text: import string
  
  import numpy as np
  
  from pymoo.algorithms.nsga2 import NSGA2
  from pymoo.model.crossover import Crossover
  from pymoo.model.duplicate import ElementwiseDuplicateElimination
  from pymoo.model.mutation import Mutation
  from pymoo.model.problem import Problem
  from pymoo.model.sampling import Sampling
  from pymoo.optimize import minimize
  from pymoo.visualization.scatter import Scatter
  
  
  class MyProblem(Problem):
      def __init__(self, n_characters=10):
          super().__init__(n_var=1, n_obj=2, n_constr=0, elementwise_evaluation=True)
          self.n_characters = n_characters
          self.ALPHABET = [c for c in string.ascii_lowercase]
  
      def _evaluate(self, x, out, *args, **kwargs):
          n_a, n_b = 0, 0
          for c in x[0]:
              if c == 'a':
                  n_a += 1
              elif c == 'b':
                  n_b += 1
  
          out["F"] = np.array([- n_a, - n_b], dtype=float)
         
  
  
  class MySampling(Sampling):
  
      def _do(self, problem, n_samples, **kwargs):
          X = np.full((n_samples, 1), None, dtype=np.object)
  
          for i in range(n_samples):
              X[i, 0] = "".join([np.random.choice(problem.ALPHABET) for _ in range(problem.n_characters)])
          print("population:",X)
          return X
  
  
  class MyCrossover(Crossover):
      def __init__(self):
  
          # define the crossover: number of parents and number of offsprings
          super().__init__(2, 2)
  
      def _do(self, problem, X, **kwargs):
  
          # The input of has the following shape (n_parents, n_matings, n_var)
          _, n_matings, n_var = X.shape
  
          # The output owith the shape (n_offsprings, n_matings, n_var)
          # Because there the number of parents and offsprings are equal it keeps the shape of X
          Y = np.full_like(X, None, dtype=np.object)
  
          # for each mating provided
          for k in range(n_matings):
  
              # get the first and the second parent
              a, b = X[0, k, 0], X[1, k, 0]
  
              # prepare the offsprings
              off_a = ["_"] * problem.n_characters
              off_b = ["_"] * problem.n_characters
  
              for i in range(problem.n_characters):
                  if np.random.random() < 0.5:
                      off_a[i] = a[i]
                      off_b[i] = b[i]
                  else:
                      off_a[i] = b[i]
                      off_b[i] = a[i]
  
              # join the character list and set the output
              Y[0, k, 0], Y[1, k, 0] = "".join(off_a), "".join(off_b)
          print("crossover:",Y)
          return Y
  
  
  class MyMutation(Mutation):
      def __init__(self):
          super().__init__()
  
      def _do(self, problem, X, **kwargs):
          for i in range(len(X)):
              if np.random.random() < 0.5:
                  X[i, 0] = "".join(np.array([e for e in X[i, 0]])[np.random.permutation(problem.n_characters)])
          print(X)
          return X
  
  
  class MyDuplicateElimination(ElementwiseDuplicateElimination):
  
      def is_equal(self, a, b):
          return a.X[0] == b.X[0]
  
  
  algorithm = NSGA2(pop_size=20,
                    sampling=MySampling(),
                    crossover=MyCrossover(),
                    mutation=MyMutation(),
                    eliminate_duplicates=MyDuplicateElimination()
                    )
  
  res = minimize(MyProblem(),
                 algorithm,
                 seed=1,
                 verbose=True)
  
  Scatter().add(res.F).show()
  print(res.X[np.argsort(res.F[:, 0])])
  