docs: def ask_and_eval(self, func, args=(), gradf=None, number=None, xmean=None, sigma_fac=1,
                       evaluations=1, aggregation=np.median, kappa=1):
          """samples `number` solutions and evaluates them on `func`, where
          each solution `s` is resampled until ``self.is_feasible(s, func(s)) is True``.
  
          Arguments
          ---------
              `func`
                  objective function, ``func(x)`` returns a scalar
              `args`
                  additional parameters for `func`
              `gradf`
                  gradient of objective function, ``g = gradf(x, *args)``
                  must satisfy ``len(g) == len(x)``
              `number`
                  number of solutions to be sampled, by default
                  population size ``popsize`` (AKA lambda)
              `xmean`
                  mean for sampling the solutions, by default ``self.mean``.
              `sigma_fac`
                  multiplier for sampling width, standard deviation, for example
                  to get a small perturbation of solution `xmean`
              `evaluations`
                  number of evaluations for each sampled solution
              `aggregation`
                  function that aggregates `evaluations` values to
                  as single value.
              `kappa`
                  multiplier used for the evaluation of the solutions, in
                  that ``func(m + kappa*(x - m))`` is the f-value for x.
  
          Return
          ------
          ``(X, fit)``, where
              X -- list of solutions
              fit -- list of respective function values
  
          Details
          -------
          While ``not self.is_feasible(x, func(x))``new solutions are sampled. By
          default ``self.is_feasible == cma.feasible == lambda x, f: f not in (None, np.NaN)``.
          The argument to `func` can be freely modified within `func`.
  
          Depending on the ``CMA_mirrors`` option, some solutions are not sampled
          independently but as mirrors of other bad solutions. This is a simple
          derandomization that can save 10-30% of the evaluations in particular
          with small populations, for example on the cigar function.
  
          Example
          -------
          >>> import cma
          >>> x0, sigma0 = 8*[10], 1  # 8-D
          >>> es = cma.CMAEvolutionStrategy(x0, sigma0)
          >>> while not es.stop():
          ...     X, fit = es.ask_and_eval(cma.fcts.elli)  # handles NaN with resampling
          ...     es.tell(X, fit)  # pass on fitness values
          ...     es.disp(20) # print every 20-th iteration
          >>> print('terminated on ' + str(es.stop()))
          <output omitted>
  
          A single iteration step can be expressed in one line, such that
          an entire optimization after initialization becomes
          ::
  
              while not es.stop():
                  es.tell(*es.ask_and_eval(cma.fcts.elli))
  
          """
          # initialize
          popsize = self.sp.popsize
          if number is not None:
              popsize = number
  
          selective_mirroring = self.opts['CMA_mirrormethod'] > 0
          nmirrors = self.sp.lam_mirr
          if popsize != self.sp.popsize:
              nmirrors = Mh.sround(popsize * self.sp.lam_mirr / self.sp.popsize)
              # TODO: now selective mirroring might be impaired
          assert new_injections or self.opts['CMA_mirrormethod'] < 2 
          if new_injections and self.opts['CMA_mirrormethod'] != 1: # otherwise mirrors are done elsewhere
              nmirrors = 0
          assert nmirrors <= popsize // 2
          self.mirrors_idx = np.arange(nmirrors)  # might never be used
          self.mirrors_rejected_idx = []  # might never be used
          is_feasible = self.opts['is_feasible']
  
          # do the work
          fit = []  # or np.NaN * np.empty(number)
          X_first = self.ask(popsize, xmean=xmean, gradf=gradf, args=args)
          if xmean is None:
              xmean = self.mean  # might have changed in self.ask
          X = []
          for k in xrange(int(popsize)):
              x, f = X_first.pop(0), None
              rejected = -1
              while rejected < 0 or not is_feasible(x, f):  # rejection sampling
                  rejected += 1
                  if rejected:  # resample
                      x = self.ask(1, xmean, sigma_fac)[0]
                  elif k >= popsize - nmirrors:  # mirrored sample
                      if k == popsize - nmirrors and selective_mirroring:
                          self.mirrors_idx = np.argsort(fit)[-1:-1 - nmirrors:-1]
                      x = self.get_mirror(X[self.mirrors_idx[popsize - 1 - k]])
                  if rejected == 1 and k >= popsize - nmirrors:
                      self.mirrors_rejected_idx.append(k)
  
                  # contraints handling test hardwired ccccccccccc
                  length_normalizer = 1
                  # zzzzzzzzzzzzzzzzzzzzzzzzz
                  f = func(x, *args) if kappa == 1 else \
                      func(xmean + kappa * length_normalizer * (x - xmean),
                           *args)
                  if is_feasible(x, f) and evaluations > 1:
                      f = aggregation([f] + [(func(x, *args) if kappa == 1 else
                                              func(xmean + kappa * length_normalizer * (x - xmean), *args))
                                             for _i in xrange(int(evaluations - 1))])
                  if rejected + 1 % 1000 == 0:
                      print('  %d solutions rejected (f-value NaN or None) at iteration %d' %
                            (rejected, self.countiter))
              fit.append(f)
              X.append(x)
          self.evaluations_per_f_value = int(evaluations)
          return X, fit