/**
   * Returns a new {@link CMAES} instance.
   *
   * @param properties the properties for customizing the new {@code CMAES} instance
   * @param problem the problem
   * @return a new {@code CMAES} instance
   */
  private Algorithm newCMAES(TypedProperties properties, Problem problem) {
    if (!checkType(RealVariable.class, problem)) {
      throw new FrameworkException("unsupported decision variable type");
    }

    int lambda = (int) properties.getDouble("lambda", 100);
    double cc = properties.getDouble("cc", -1.0);
    double cs = properties.getDouble("cs", -1.0);
    double damps = properties.getDouble("damps", -1.0);
    double ccov = properties.getDouble("ccov", -1.0);
    double ccovsep = properties.getDouble("ccovsep", -1.0);
    double sigma = properties.getDouble("sigma", -1.0);
    int diagonalIterations = (int) properties.getDouble("diagonalIterations", 0);
    String indicator = properties.getString("indicator", "crowding");
    double[] initialSearchPoint = properties.getDoubleArray("initialSearchPoint", null);
    NondominatedPopulation archive = null;
    FitnessEvaluator fitnessEvaluator = null;

    if (problem.getNumberOfObjectives() == 1) {
      archive = new NondominatedPopulation();
    } else {
      archive =
          new EpsilonBoxDominanceArchive(
              properties.getDoubleArray(
                  "epsilon", new double[] {EpsilonHelper.getEpsilon(problem)}));
    }

    if ("hypervolume".equals(indicator)) {
      fitnessEvaluator = new HypervolumeFitnessEvaluator(problem);
    } else if ("epsilon".equals(indicator)) {
      fitnessEvaluator = new AdditiveEpsilonIndicatorFitnessEvaluator(problem);
    }

    CMAES cmaes =
        new CMAES(
            problem,
            lambda,
            fitnessEvaluator,
            archive,
            initialSearchPoint,
            false,
            cc,
            cs,
            damps,
            ccov,
            ccovsep,
            sigma,
            diagonalIterations);

    return cmaes;
  }
  /**
   * Returns a new {@link eNSGAII} instance.
   *
   * @param properties the properties for customizing the new {@code eNSGAII} instance
   * @param problem the problem
   * @return a new {@code eNSGAII} instance
   */
  private Algorithm neweNSGAII(TypedProperties properties, Problem problem) {
    int populationSize = (int) properties.getDouble("populationSize", 100);

    Initialization initialization = new RandomInitialization(problem, populationSize);

    NondominatedSortingPopulation population =
        new NondominatedSortingPopulation(new ParetoDominanceComparator());

    EpsilonBoxDominanceArchive archive =
        new EpsilonBoxDominanceArchive(
            properties.getDoubleArray("epsilon", new double[] {EpsilonHelper.getEpsilon(problem)}));

    TournamentSelection selection =
        new TournamentSelection(
            2, new ChainedComparator(new ParetoDominanceComparator(), new CrowdingComparator()));

    Variation variation = OperatorFactory.getInstance().getVariation(null, properties, problem);

    NSGAII nsgaii = new NSGAII(problem, population, archive, selection, variation, initialization);

    AdaptiveTimeContinuation algorithm =
        new AdaptiveTimeContinuation(
            nsgaii,
            properties.getInt("windowSize", 100),
            Math.max(properties.getInt("windowSize", 100), properties.getInt("maxWindowSize", 100)),
            1.0 / properties.getDouble("injectionRate", 0.25),
            properties.getInt("minimumPopulationSize", 100),
            properties.getInt("maximumPopulationSize", 10000),
            new UniformSelection(),
            new UM(1.0));

    return algorithm;
  }
  /**
   * Returns a new single-objective {@link DE/rand/1/bin} instance.
   *
   * @param properties the properties for customizing the new {@code DE/rand/1/bin} instance
   * @param problem the problem
   * @return a new {@code DE/rand/1/bin} instance
   */
  private Algorithm newDifferentialEvolution(TypedProperties properties, Problem problem) {
    if (!checkType(RealVariable.class, problem)) {
      throw new FrameworkException("unsupported decision variable type");
    }

    int populationSize = (int) properties.getDouble("populationSize", 100);
    double[] weights = properties.getDoubleArray("weights", new double[] {1.0});
    String method = properties.getString("method", "linear");

    AggregateObjectiveComparator comparator = null;

    if (method.equalsIgnoreCase("linear")) {
      comparator = new LinearDominanceComparator(weights);
    } else if (method.equalsIgnoreCase("min-max")) {
      comparator = new MinMaxDominanceComparator(weights);
    } else {
      throw new FrameworkException("unrecognized weighting method: " + method);
    }

    Initialization initialization = new RandomInitialization(problem, populationSize);

    DifferentialEvolutionSelection selection = new DifferentialEvolutionSelection();

    DifferentialEvolutionVariation variation =
        (DifferentialEvolutionVariation)
            OperatorFactory.getInstance().getVariation("de", properties, problem);

    return new DifferentialEvolution(problem, comparator, initialization, selection, variation);
  }
  /**
   * Returns a new {@link RandomSearch} instance.
   *
   * @param properties the properties for customizing the new {@code RandomSearch} instance
   * @param problem the problem
   * @return a new {@code RandomSearch} instance
   */
  private Algorithm newRandomSearch(TypedProperties properties, Problem problem) {
    int populationSize = (int) properties.getDouble("populationSize", 100);

    Initialization generator = new RandomInitialization(problem, populationSize);

    NondominatedPopulation archive = null;

    if (properties.contains("epsilon")) {
      archive =
          new EpsilonBoxDominanceArchive(
              properties.getDoubleArray(
                  "epsilon", new double[] {EpsilonHelper.getEpsilon(problem)}));
    } else {
      archive = new NondominatedPopulation();
    }

    return new RandomSearch(problem, generator, archive);
  }
  /**
   * Returns a new {@link OMOPSO} instance.
   *
   * @param properties the properties for customizing the new {@code OMOPSO} instance
   * @param problem the problem
   * @return a new {@code OMOPSO} instance
   */
  private Algorithm newOMOPSO(TypedProperties properties, Problem problem) {
    if (!checkType(RealVariable.class, problem)) {
      throw new FrameworkException("unsupported decision variable type");
    }

    int populationSize = (int) properties.getDouble("populationSize", 100);
    int archiveSize = (int) properties.getDouble("archiveSize", 100);
    int maxIterations = (int) properties.getDouble("maxEvaluations", 25000) / populationSize;
    double mutationProbability =
        properties.getDouble("mutationProbability", 1.0 / problem.getNumberOfVariables());
    double perturbationIndex = properties.getDouble("perturbationIndex", 0.5);
    double[] epsilon =
        properties.getDoubleArray("epsilon", new double[] {EpsilonHelper.getEpsilon(problem)});

    return new OMOPSO(
        problem,
        populationSize,
        archiveSize,
        epsilon,
        mutationProbability,
        perturbationIndex,
        maxIterations);
  }
  /**
   * Returns a new {@link eMOEA} instance.
   *
   * @param properties the properties for customizing the new {@code eMOEA} instance
   * @param problem the problem
   * @return a new {@code eMOEA} instance
   */
  private Algorithm neweMOEA(TypedProperties properties, Problem problem) {
    int populationSize = (int) properties.getDouble("populationSize", 100);

    Initialization initialization = new RandomInitialization(problem, populationSize);

    Population population = new Population();

    DominanceComparator comparator = new ParetoDominanceComparator();

    EpsilonBoxDominanceArchive archive =
        new EpsilonBoxDominanceArchive(
            properties.getDoubleArray("epsilon", new double[] {EpsilonHelper.getEpsilon(problem)}));

    final TournamentSelection selection = new TournamentSelection(2, comparator);

    Variation variation = OperatorFactory.getInstance().getVariation(null, properties, problem);

    EpsilonMOEA emoea =
        new EpsilonMOEA(
            problem, population, archive, selection, variation, initialization, comparator);

    return emoea;
  }
  /**
   * Returns a new single-objective {@link GeneticAlgorithm} instance.
   *
   * @param properties the properties for customizing the new {@code GeneticAlgorithm} instance
   * @param problem the problem
   * @return a new {@code GeneticAlgorithm} instance
   */
  private Algorithm newGeneticAlgorithm(TypedProperties properties, Problem problem) {
    int populationSize = (int) properties.getDouble("populationSize", 100);
    double[] weights = properties.getDoubleArray("weights", new double[] {1.0});
    String method = properties.getString("method", "linear");

    AggregateObjectiveComparator comparator = null;

    if (method.equalsIgnoreCase("linear")) {
      comparator = new LinearDominanceComparator(weights);
    } else if (method.equalsIgnoreCase("min-max")) {
      comparator = new MinMaxDominanceComparator(weights);
    } else {
      throw new FrameworkException("unrecognized weighting method: " + method);
    }

    Initialization initialization = new RandomInitialization(problem, populationSize);

    Selection selection = new TournamentSelection(2, comparator);

    Variation variation = OperatorFactory.getInstance().getVariation(null, properties, problem);

    return new GeneticAlgorithm(problem, comparator, initialization, selection, variation);
  }