@Override
  public void performIteration(PSO algorithm) {
    delegate.performIteration(algorithm);
    Topology<Particle> topology = algorithm.getTopology();

    // calculate vAvg
    Vector avgV =
        Vectors.mean(
            Lists.transform(
                topology,
                new Function<Particle, Vector>() {
                  @Override
                  public Vector apply(Particle f) {
                    return (Vector) f.getVelocity();
                  }
                }));

    Vector.Builder builder = Vector.newBuilder();
    for (Numeric n : avgV) {
      if (Math.abs(n.doubleValue()) > vMax.getParameter()) {
        builder.add(vMax.getParameter());
      } else {
        builder.add(n);
      }
    }

    avgV = builder.build();

    // mutation
    Particle gBest = Topologies.getBestEntity(topology, new SocialBestFitnessComparator());
    Particle mutated = gBest.getClone();
    Vector pos = (Vector) gBest.getBestPosition();
    final Bounds bounds = pos.boundsOf(0);

    pos =
        pos.plus(
            avgV.multiply(
                new Supplier<Number>() {
                  @Override
                  public Number get() {
                    return distribution.getRandomNumber() * bounds.getRange()
                        + bounds.getLowerBound();
                  }
                }));

    mutated.setCandidateSolution(pos);
    mutated.calculateFitness();

    if (gBest.getBestFitness().compareTo(mutated.getFitness()) < 0) {
      gBest.getProperties().put(EntityType.Particle.BEST_FITNESS, mutated.getBestFitness());
      gBest.getProperties().put(EntityType.Particle.BEST_POSITION, mutated.getBestPosition());
    }
  }
  /** {@inheritDoc} */
  @Override
  public <E extends Entity> List<E> crossover(List<E> parentCollection) {
    checkState(
        parentCollection.size() >= 3,
        "There must be a minimum of three parents to perform UNDX crossover.");
    checkState(
        numberOfOffspring > 0,
        "At least one offspring must be generated. Check 'numberOfOffspring'.");

    List<Vector> solutions = Entities.<Vector>getCandidateSolutions(parentCollection);
    List<E> offspring = Lists.newArrayListWithCapacity(numberOfOffspring);
    UniformDistribution randomParent = new UniformDistribution();
    final int k = solutions.size();
    final int n = solutions.get(0).size();

    for (int os = 0; os < numberOfOffspring; os++) {
      // get index of main parent and put its solution at the end of the list
      int parent = (int) randomParent.getRandomNumber(0.0, k);
      Collections.swap(solutions, parent, k - 1);

      List<Vector> e_zeta = new ArrayList<Vector>();

      // calculate mean of parents except main parent
      Vector g = Vectors.mean(solutions.subList(0, k - 1));

      // basis vectors defined by parents
      for (int i = 0; i < k - 1; i++) {
        Vector d = solutions.get(i).subtract(g);

        if (!d.isZero()) {
          double dbar = d.length();
          Vector e = d.orthogonalize(e_zeta);

          if (!e.isZero()) {
            e_zeta.add(e.normalize().multiply(dbar));
          }
        }
      }

      final double D = solutions.get(k - 1).subtract(g).length();

      // create the remaining basis vectors
      List<Vector> e_eta = Lists.newArrayList();
      e_eta.add(solutions.get(k - 1).subtract(g));

      for (int i = 0; i < n - e_zeta.size() - 1; i++) {
        Vector d = Vector.newBuilder().copyOf(g).buildRandom();
        e_eta.add(d);
      }

      e_eta = Vectors.orthonormalize(e_eta);

      // construct the offspring
      Vector variables = Vector.copyOf(g);

      if (!useIndividualProviders) {
        for (int i = 0; i < e_zeta.size(); i++) {
          variables =
              variables.plus(
                  e_zeta.get(i).multiply(random.getRandomNumber(0.0, sigma1.getParameter())));
        }

        for (int i = 0; i < e_eta.size(); i++) {
          variables =
              variables.plus(
                  e_eta
                      .get(i)
                      .multiply(
                          D * random.getRandomNumber(0.0, sigma2.getParameter() / Math.sqrt(n))));
        }
      } else {
        for (int i = 0; i < e_zeta.size(); i++) {
          variables =
              variables.plus(
                  e_zeta
                      .get(i)
                      .multiply(
                          new Supplier<Number>() {

                            @Override
                            public Number get() {
                              return random.getRandomNumber(0.0, sigma1.getParameter());
                            }
                          }));
        }

        for (int i = 0; i < e_eta.size(); i++) {
          variables =
              variables.plus(
                  e_eta
                      .get(i)
                      .multiply(
                          new Supplier<Number>() {

                            @Override
                            public Number get() {
                              return D
                                  * random.getRandomNumber(
                                      0.0, sigma2.getParameter() / Math.sqrt(n));
                            }
                          }));
        }
      }

      E child = (E) parentCollection.get(parent).getClone();
      child.setCandidateSolution(variables);

      offspring.add(child);
    }

    return offspring;
  }