MOEAFramework / MOEAFramework

A Free and Open Source Java Framework for Multiobjective Optimization
http://moeaframework.org
Other
324 stars 128 forks source link

setting decision variable for custom operators #174

Closed Onnene closed 1 year ago

Onnene commented 6 years ago

Hello @dhadka,

Question 1 How can I set a custom decision variable in the evaluate method?

public void evaluate(Solution solution) {

    double[] vars = EncodingUtils.getReal(solution); // Works for real decision variables 

    customDecisionVariable result = new customDecisionVariable(); // will this work for custom variables?

    for (int i = 0; i < getNumberOfVariables(); i++) {
            result = (customDecisionVariable) solution.getVariable(i);
     }
   ...
   ...
   ...
}

Question 2: How do I can I print out the final set of results? I try the line of code below but I only get a representative string and not the variable itself. System.out.print(result.get(0).getVariable(0).toString());

Question 3: Is it fine to use a built-in initialization like RandomInitialization for a custom problem? e.g. Initialization initialization = new RandomInitialization(problem, x);// x = population size

Thank you for your time.

dhadka commented 6 years ago
  1. Yes, you just cast it to the type you need:

    CustomDecisionVariable var = (CustomDecisionVariable)solution.getVariable(0);
  1. You would need something that converts the variables to a string representation. For example, here's one that supports real, binary, and permutations.

    private String encode(Variable variable) throws IOException {
        StringBuilder sb = new StringBuilder();

        if (variable instanceof RealVariable) {
            RealVariable rv = (RealVariable)variable;
            sb.append(rv.getValue());
        } else if (variable instanceof BinaryVariable) {
            BinaryVariable bv = (BinaryVariable)variable;

            for (int i=0; i<bv.getNumberOfBits(); i++) {
                sb.append(bv.get(i) ? "1" : "0");
            }
        } else if (variable instanceof Permutation) {
            Permutation p = (Permutation)variable;

            for (int i=0; i<p.size(); i++) {
                if (i > 0) {
                    sb.append(',');
                }

                sb.append(p.get(i));
            }
        } else {
            throw new IOException("type not supported");
        }

        return sb.toString();
    }

Then call System.out.println(encode(result.get(0).getVariable(0)));. You would need to add in else if conditions for any other types you use.

  1. Yes, RandomInitialization is designed to work with custom problems and custom decision variables. When creating a new decision variable type, you will need to define the randomize() method. This method should assign the value of that variable randomly. Take a look at https://github.com/MOEAFramework/MOEAFramework/tree/master/src/org/moeaframework/core/variable to see how the built-in decision variables are implemented.
Onnene commented 6 years ago

@dhadka

Sorry but I have 2 more questions

1) After declaring the initialization operator for example Initialization initialization = new RandomInitialization(problem, 50);, do I need to add it to the Executor() if yes how can I do that? The other part of this question is if I use a .withProperty(populationSize = 50) in the executor is that an equivalent way of using the initialization?

2) I am using the NSGA-II on a problem with 1 decision variable and 2 objectives, also I am using an external simulation as the evaluation mechanism inside the evaluate(). For the purpose of a very quick run and observation, I set the .withMaxEvaluations to just 1 (population is set to 1 as well), my understanding is that the decision variable should be evaluated for each objective once, but my observation is that the algorithm calls the simulation many more times because the simulation is running longer than normal when in stand-alone mode. Please throw more light on how the evaluation works with NSGA-II, is there a minimum or default number of evaluations that the algorithm must run, also is there a default populationSize it works with such that if a value below the minimum is given, it uses that default value.

Thanks for the clarification, I am trying to understand the mechanisms of the framework.

dhadka commented 6 years ago
  1. The preferred way is to use .withProperty("populationSize", 50). There is currently no way to directly pass an initialization operator through the Executor...if you needed to, you would need to create the algorithm explicitly using its constructor.

  2. The evaluate(Solution solution) method in your problem class will be called once for each solution generated. If you set max evaluations to 1 and the population size to 1, then you should only have one solution evaluated. I suspect if you're seeing more evaluations taking place that it's not setting the population size correctly. See the code below for a simple test.

    There are some cases where we will exceed the max evaluations. The most common reasons are:

    • Max evaluations is not a multiple of the population size. For example, if you set the population size to 100 and max evaluations to 1, you will likely end up evaluating 100 solutions because it executes one iteration which generates 100 solutions.

    • Some algorithms may place additional restrictions on the population size, such as requiring an even sized population.


public class TestEvaluate {

    public static int callsToEvaluate = 0;

    public static class TestProblem extends AbstractProblem {
        public TestProblem() {
            super(1, 1);
        }

        @Override
        public void evaluate(Solution solution) {
            callsToEvaluate++;
        }

        @Override
        public Solution newSolution() {
            Solution solution = new Solution(1, 1);
            solution.setVariable(0, EncodingUtils.newReal(0.0, 1.0));
            return solution;
        }
    }

    public static void main(String[] args) {
        NondominatedPopulation result = new Executor()
                .withProblemClass(TestProblem.class)
                .withAlgorithm("NSGAII")
                .withMaxEvaluations(1)
                .withProperty("populationSize", 1)
                .run();

        System.out.println("Evaluate called " + callsToEvaluate + " times...");
    }

}
github-actions[bot] commented 1 year ago

This is an automated message. This issue is flagged as stale and will be closed in 7 days. If you feel this issue is still relevant, leave a comment to keep the issue open. Please also consider contributing a fix for the issue.