code: """
  This class provide an interface for other libraries to specific modules. For example, the evolutionary operations
  can be used easily just by calling a function and providing the lower and upper bounds of the problem.
  
  """
  import copy
  import types
  
  import numpy as np
  
  from pymoo.model.algorithm import filter_optimum
  from pymoo.model.individual import Individual
  from pymoo.model.population import Population
  from pymoo.model.problem import Problem
  
  
  # =========================================================================================================
  # A global interface for some features
  # =========================================================================================================
  
  
  def get_problem_func(n_var, xl, xu, type_var):
      class P(Problem):
          def __init__(self) -> None:
              super().__init__(n_var=n_var, n_obj=1, n_constr=0, xl=xl, xu=xu, type_var=type_var)
  
      return P
  
  
  def sample(sampling, n_samples, n_var, xl=0, xu=1, **kwargs):
      problem = get_problem_func(n_var, xl, xu, None)(**kwargs)
      return sampling.do(problem, n_samples, pop=None, **kwargs)
  
  
  def crossover(crossover, a, b, c=None, xl=0, xu=1, type_var=np.double, **kwargs):
      n = a.shape[0]
      _pop = Population.merge(Population().new("X", a), Population().new("X", b))
      _P = np.column_stack([np.arange(n), np.arange(n) + n])
  
      if c is not None:
          _pop = Population.merge(_pop, Population().new("X", c))
          _P = np.column_stack([_P, np.arange(n) + 2 * n])
  
      problem = get_problem_func(a.shape[1], xl, xu, type_var)(**kwargs)
      return crossover.do(problem, _pop, _P, **kwargs).get("X")
  
  
  def mutation(mutation, X, xl=0, xu=1, type_var=np.double, **kwargs):
      problem = get_problem_func(X.shape[1], xl, xu, type_var)(**kwargs)
      return mutation.do(problem, Population().new("X", X), **kwargs).get("X")
  
  
  # =========================================================================================================
  # Ask And Tell Interface
  # =========================================================================================================
  
  
  def evaluate_to_nan(self, x, out, *args, **kwargs):
      n_points, _ = x.shape
      out["F"] = None
      if self.n_constr > 0:
          out["G"] = None
  
  
  def evaluate_to_value(F, G=None):
      def eval(self, x, out, *args, **kwargs):
          n_points, _ = x.shape
          out["F"] = F
          if G is not None:
              out["G"] = G
  
      return eval
  
  
  class AskAndTell:
  
      def __init__(self, algorithm, problem=None, **kwargs):
  
          if problem is not None:
              self.problem = copy.deepcopy(problem)
          else:
              self.problem = Problem(**kwargs)
  
          self.algorithm = copy.deepcopy(algorithm)
  
      def get_population(self):
          return self.algorithm.pop
  
      def set_population(self, pop):
          self.algorithm.pop = pop
  
      def get_offsprings(self):
          return self.algorithm.off
  
      def set_offsprings(self, off):
          self.algorithm.off = off
  
      def ask(self):
  
          # if the initial population has not been generated yet
          if self.get_population() is None:
  
              self.algorithm.initialize(self.problem)
  
              # deactivate the survival because no values have been set yet
              survival = self.algorithm.survival
              self.algorithm.survival = None
  
              self.problem._evaluate = types.MethodType(evaluate_to_nan, self.problem)
              self.algorithm._initialize()
  
              # activate the survival for the further runs
              self.algorithm.survival = survival
  
              return self.get_population().get("X")
  
          # usually the case - create the next output
          else:
  
              # if offsprings do not exist set the pop - otherwise always offsprings
              if self.get_offsprings() is not None:
                  self.set_population(Population.merge(self.get_population(), self.get_offsprings()))
  
              # execute a survival of the algorithm
              survivors = self.algorithm.survival.do(self.problem, self.get_population(),
                                                     self.algorithm.pop_size, algorithm=self.algorithm)
              self.set_population(survivors)
  
              # execute the mating using the population
              off = self.algorithm.mating.do(self.algorithm.problem, self.get_population(),
                                             n_offsprings=self.algorithm.n_offsprings, algorithm=self.algorithm)
  
              # execute the fake evaluation of the individuals
              self.problem._evaluate = types.MethodType(evaluate_to_nan, self.problem)
              self.algorithm.evaluator.eval(self.problem, off, algorithm=self.algorithm)
              self.set_offsprings(off)
  
              return off.get("X")
  
      def tell(self, F, G=None, X=None):
  
          # if offsprings do not exist set the pop - otherwise always offsprings
          pop_to_evaluate = self.get_offsprings() if self.get_offsprings() is not None else self.get_population()
  
          # if the user changed the design space values for whatever reason
          if X is not None:
              pop_to_evaluate.set("X")
  
          # do the function evaluations
          self.problem._evaluate = types.MethodType(evaluate_to_value(F.copy(), G.copy()), self.problem)
          self.algorithm.evaluator.eval(self.problem, pop_to_evaluate, algorithm=self.algorithm)
  
      def result(self, only_optimum=True, return_values_of="auto"):
  
          if return_values_of == "auto":
              return_values_of = ["X", "F"]
              if self.problem.n_constr > 0:
                  return_values_of.append("CV")
  
          if only_optimum:
              self.algorithm.finalize()
              pop, opt = self.algorithm.pop, self.algorithm.opt
              res = filter_optimum(pop.copy()) if opt is None else opt.copy()
  
              if isinstance(res, Individual):
                  res = Population.create(res)
  
          else:
              res = self.algorithm.pop
  
          return res.get(*return_values_of)
  
  
  
apis: numpy.arange