seaklab / mopAOS

Source code used in "A Classification and Comparison of Credit Assignment Strategies in Multiobjective Adaptive Operator Selection" submitted to IEEE Transactions in Evolutionary Computation.
0 stars 5 forks source link

How can I write individuals of all generations? #1

Closed ugokedebu closed 7 years ago

ugokedebu commented 7 years ago

Hello,

I'd like to write all the individuals of each generation to a file when using AOS. There was a problem also when trying to combine with code which Mr. Hadka imitated what published on Github.If you know how to write individuals of all generations please let us know.

Thanks.

nh295 commented 7 years ago

To write individuals from each generation to a file, first obtain the instance of the class Population that you want to save. You can do this by using the same instance you pass into the algorithm's constructor or use the ".getResult()" method, which in most cases gets the current non-dominated solutions. Then, use the MOEAFramework class called PopulationIO to save the population to disk. There are methods to write to a plain text file or to a binary file. To save all generations to disk, you simply have to get the population at each iteration you want to save and then use the PopulationIO to save it. The algorithms iterate with a method called ".step()" so put in your save method calls after that.

When we created this code, we replaced many of the PRNG (pseudo random number generator) static methods with a new class called ParallelPRNG to support concurrent calls. Calls to PRNG within the operators and algorithms were modified, which is probably why you are seeing some incompatibilities with MOEAFramework. Recent commits to the "develop" branch have reverted back to the original PRNG calls, so it should work as expected.

Hope that helps.

ugokedebu commented 7 years ago

Thank you for your reply

I confirmed the latest development branch and updated my source code. By the way, you used to run AOSCreditTest.java around TestRun.java, but how do you implement the execution code in the current specification? Will you tell me the Test code for my reference?

nh295 commented 7 years ago

The code on the develop branch is supposed to be more modular to allow more flexible development of AOS. There is no main class and is not setup to run the same problems (i.e. UF, WFG, DTLZ) as before. However that can be easily set up if you set up the problems yourself and run the algorithm (with or without AOS). Refer to the MOEAFramework documentation on how to set up and run a problem with an evolutionary algorithm.

ugokedebu commented 7 years ago

Currently, the source code I am using is the following, but if I incorporate AOS into this code I do not know what to add to what part. I am thinking of adding something to Executor (), but please tell me where you declare the type of crossover and where {creditassignment, operatorselectors} is declared.

ugokedebu commented 7 years ago

` import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.io.FileWriter; import java.io.BufferedWriter; import java.io.PrintWriter; import java.io.IOException;

import org.moeaframework.Executor;
import org.moeaframework.Instrumenter;
import org.moeaframework.analysis.collector.Accumulator;
import org.moeaframework.analysis.collector.AttachPoint;
import org.moeaframework.analysis.collector.Collector;
import org.moeaframework.core.EvolutionaryAlgorithm;
import org.moeaframework.core.Population;
import org.moeaframework.core.Solution;

public class Test5 {

    /**
     * Collects the population from an {@link EvolutionaryAlgorithm}.
     */
    public static class PopulationCollector implements Collector {

        /**
         * The algorithm instance used by this collector; or {@code null} if this 
         * collector has not yet been attached.
         */
        private final EvolutionaryAlgorithm algorithm;

        /**
         * Constructs an unattached collector for recording the population used by
         * an {@code EvolutionaryAlgorithm}.
         */
        public PopulationCollector() {
            this(null);
        }

        /**
         * Constructs a collector for recording the population used by the specified
         * {@code EvolutionaryAlgorithm}.
         * 
         * @param algorithm the algorithm this collector records data from
         */
        public PopulationCollector(EvolutionaryAlgorithm algorithm) {
            super();
            this.algorithm = algorithm;
        }

        @Override
        public AttachPoint getAttachPoint() {
            return AttachPoint.isSubclass(EvolutionaryAlgorithm.class).and(
                    AttachPoint.not(AttachPoint.isNestedIn(
                            EvolutionaryAlgorithm.class)));
        }

        @Override
        public Collector attach(Object object) {
            return new PopulationCollector(
                    (EvolutionaryAlgorithm)object);
        }

        @Override
        public void collect(Accumulator accumulator) {
            ArrayList<Solution> list = new ArrayList<Solution>();
            Population population = algorithm.getPopulation();

            for (Solution solution : population) {
                list.add(solution);
            }

            accumulator.add("Population", list);
        }

    }

    public static void main(String[] args) throws IOException {
        // setup the instrumenter to record the population
        Instrumenter instrumenter = new Instrumenter()
                .withFrequency(100)
                .attach(new PopulationCollector());

        String[] problem = {"DTLZ2_3","DTLZ3_3","DTLZ4_3","WFG1_3","WFG2_3","WFG6_3","WFG8_3","WFG9_3","ZDT1","ZDT4"};
        String[] algorithm = {"NSGAII"/*,"eMOEA","IBEA"*/};
        for(int s=0; s < problem.length;s++){
            for(int k=1; k<= 10;k++){
                for(int alg=0;alg<algorithm.length;alg++){
        // use the executor to run the algorithm with the instrumenter
                new Executor()
                .withProblem(problem[s])
                .withAlgorithm(algorithm[alg])
                .withMaxEvaluations(10000)
                .withProperty("populationSize", 100)
                .withInstrumenter(instrumenter)
                .run();
                Accumulator accumulator = instrumenter.getLastAccumulator();

        try{

            FileWriter fw = new FileWriter("./output3/"+ problem[s] +"/"+ algorithm[alg] +"-a-"+k+".csv",false);
            PrintWriter pw = new PrintWriter(new BufferedWriter(fw));

            // print the population at each generation
            for (int i = 0; i < accumulator.size("NFE"); i++) {
                System.out.println("NFE: " + accumulator.get("NFE", i));

                for (Solution solution : (List<Solution>)accumulator.get("Population", i)) {

                    //pw.print("  O:");

                    for (int j = 0; j < solution.getNumberOfObjectives(); j++) {
                        pw.print(" ");
                        pw.print(solution.getObjective(j));
                    }
                    //pw.print("  V:");

                    for (int j = 0; j < solution.getNumberOfVariables(); j++) {
                        pw.print(" ");
                        pw.print(solution.getVariable(j));
                    }

                    //pw.print("  C:");

                    for (int j = 0; j < solution.getNumberOfConstraints(); j++) {
                        pw.print(" ");
                        pw.print(solution.getConstraint(j));
                    }

                    pw.println("");
                }

            }
            pw.close(); 
        }catch(IOException ex){
            ex.printStackTrace();
         }
        }
       }    
      }
    }
}

`

nh295 commented 7 years ago

The Executor class seems to only load in standard algorithms like NSGAII, IBEA, epsilon-MOEA so you won't be able to attach the credit assignment or operator selection strategies. The only thing you can manipulate through the built-in algorithm factories are the parameters like population size and mutation rate. Instead what you can do is create the algorithm instance and pass it into the constructor for AOSMOEA, which also takes in an AOSStrategy that defines the credit assignment or operator selection strategies.

From there you can use an Instrument object to attach the collectors to the algorithm.


 Instrumenter instrumenter = new Instrumenter().withFrequency(5)
                .withReferenceSet(referencePopulation)
                .attach(Collector foo)
                .attachElapsedTimeCollector();

InstrumentedAlgorithm instAlgorithm = instrumenter.instrument(algorithm);

When you run instAlgorithm with:

while (!instAlgorithm.isTerminated() && (instAlgorithm.getNumberOfEvaluations() < maxEvaluations)) {
instAlgorithm.step();
}

As the algorithm steps through iterations, the collectors will record the desired values.

ugokedebu commented 7 years ago

Concretely '.attach (Collector foo)' What will be substituted in? AOSMOEA? OparotrSelection? CreditAssignment? Or all? It is too abstract to know how to actually run MOEA, please tell me.

nh295 commented 7 years ago

I pushed an example java class called TestCase.java (shown below) where it shows how to configure your AOS and how to attach the collectors, such as hypervolume collectors. The way I have saved population results is to use the PopulationIO at the end of the search, but in the example, I put it within the iterations since you seemed interested in saving the intermediate results.

`/*

import aos.IO.IOCreditHistory; import aos.IO.IOQualityHistory; import aos.IO.IOSelectionHistory; import aos.aos.AOSMOEA; import aos.aos.AOSStrategy; import aos.creditassigment.ICreditAssignment; import aos.creditassignment.offspringparent.ParentDomination; import aos.creditassignment.setcontribution.ParetoFrontContribution; import aos.nextoperator.IOperatorSelector; import aos.operator.AOSVariation; import aos.operatorselectors.ProbabilityMatching; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; import org.moeaframework.Instrumenter; import org.moeaframework.algorithm.NSGAII; import org.moeaframework.analysis.collector.InstrumentedAlgorithm; import org.moeaframework.core.EpsilonBoxDominanceArchive; import org.moeaframework.core.NondominatedSortingPopulation; import org.moeaframework.core.PopulationIO; import org.moeaframework.core.Variation; import org.moeaframework.core.operator.RandomInitialization; import org.moeaframework.core.operator.TournamentSelection; import org.moeaframework.core.operator.real.PM; import org.moeaframework.core.operator.real.SBX; import org.moeaframework.problem.CEC2009.UF1;

/*

} `

ugokedebu commented 7 years ago

Thank you for making samples. By the way, if the declaration of the operator to use is separately specified for SBX and PM if it is the current specification, if you are going to like {"sbx + pm", "de + pm"} as before How do I add the add function?

ugokedebu commented 7 years ago

Sorry again. In TestCase.java, I think that 100 individuals are doing calculations of 10 generations. Originally 1000 kinds of data should be output, but only 100 types of data are output to the output file. Why does this look like this?

In my opinion, we do not allow overwriting of files, so I think whether they are constantly being updated. Please teach me.

nh295 commented 7 years ago

In order to get multiple operators like "sbx+pm" you can use the CompoundVariation class.

import org.moeaframework.core.Variation;
import org.moeaframework.core.operator.real.SBX;
import org.moeaframework.core.operator.real.PM;
Variation sbx_pm = new CompoundVariation(new SBX(0.8, 20), new PM(0.1,20));

As for your other question, I think you are asking how to write to different file paths to save each generation as a different file name. The example in the TestCase.java is only saving the current population at the end of a generation. So you can save all individuals from different generations in different files by using a variable file name.

int i=0;
while (!instAlgorithm.isTerminated() && (instAlgorithm.getNumberOfEvaluations() < maxEvaluations)) {instAlgorithm.step();
            try {
                //one way to save current population
                PopulationIO.writeObjectives(new File("results" + i +".txt"), aos.getPopulation());
            } catch (IOException ex) {
                Logger.getLogger(TestCase.class.getName()).log(Level.SEVERE, null, ex);
            }
            i++;
        }
ugokedebu commented 7 years ago

Thank you very much. Surely, I got what I wanted. I will ask again if things that I do not understand match in the future.