-
Notifications
You must be signed in to change notification settings - Fork 23
Getting Started
The most basic way to start using this package as a proof-of-concept is by directly creating an instance of the NSGA2
class which contains all the default configuration and calling the run()
method on it.
NSGA2 nsga2 = new NSGA2();
nsga2.run()
This instantly runs with all the default configuration, and the SCH_1
and SCH_2
objective functions. It generates two graphs, one showing the final pareto front, and the other showing all the generations compiled into one. Since, by default, it generates a lot of output, the output is written to disk with random hash appended to the file name. A successful run means that the algorithm is working correctly.
To change or provide your objective function, simply override the AbstractObjectiveFunction
class.
class MyAwesomeObjective1 extends AbstractObjectiveFunction {
@Override
public double getValue(Chromosome chromosome) {
// implementation
}
}
class MyAwesomeObjective2 extends AbstractObjectiveFunction {
@Override
public double getValue(Chromosome chromosome) {
// implementation
}
}
finally, create a Configuration
object and set your list of custom objectives with that configuration instance and pass it to your NSGA2
instance.
List<AbstractObjectiveFunction> objectives = new ArrayList<>();
// adding your custom objective
// you can add as many objectives as you want
objectives.add(new MyAwesomeObjective1());
objectives.add(new MyAwesomeObjective2());
// creating your configuration with the new objectives
Configuration configuration = new Configuration(objectives);
NSGA2 nsga2 = new NSGA2(configuration);
//run() returns the final child population or the pareto front
Population paretoFront = nsga2.run();
While the default values for population size, generation count and chromosome length is 100, 25 and 20 respectively, this can be changed as per requirement.
int populationSize = 500;
int generationCount = 50;
int chromosomeLength = 100;
Configuration configuration = new Configuration(
populationSize,
generationCount,
chromosomeLength
);
// you may or may not also add your list of objectives.
configuration.objectives = objectives;
NSGA2 nsga2 = new NSGA2(configuration);
nsga2.run();
To create our own crossover operator, we need to do two things.
First, we need a CrossoverParticipantCreator
. This is used to select the participants of the crossover. We can use the default one provided by the package, which uses crowded binary tournament selection to select the participants for crossover or we can implement our own by implementing the CrossoverParticipantCreator
interface.
Finally, we need to extend the AbstractCrossover
class and implement our own crossover operator.
Let us create a custom CrossoverParticipantCreator
first. Since this interface behaves as a Functional Interface, we can use lambda expression to define this or you may create a concrete implementation of this interface in the traditional way.
public static CrossoverParticipantCreator insaneParticipantSelector() {
return population -> {
// implementation
}
}
Once this is done, we can now write our own crossover operator.
public class MyAwesomeCrossover extends AbstractCrossover {
public MyAwesomeCrossover(CrossoverParticipantCreator cpc) {
super(cpc);
}
@Override
public List<Chromosome> perform(Population population) {
// implementation
}
}
Now we can use this custom crossover operator in our algorithm by passing it into the configuration object.
CrossoverParticipantCreator cpc = insaneParticipantSelector();
Configuration configuration = new Configuration(
new MyAwesomeCrossover(cpc)
);
// you may or may not also add your list of objectives.
configuration.objectives = objectives;
// maybe we even change the number of generations it shall run
configuration.setGenerations(100);
NSGA2 nsga2 = new NSGA2(configuration);
nsga2.run();
more demo in progress
No matter what data you are working with, for this library to work with the dataset, it has to be represented in terms of Alleles, Chromosomes and Population under the datastructure package.
First, decide on how you would like to represent your dataset for the algorithm to work with, specifically the type of chromosome encoding you would like to use. This shall help you figure out the type of Allele to use. For example, for binary encoding, use BooleanAllele, for permutation encoding, use IntegerAllele and for value encoding, use ValueAllele. These are the three types of encoding provided by default by the library. If you would like to use some other form of encoding, you must implement your own Allele by extending the AbstractAllele class. If you are implementing a new type of encoding, consider providing a pull request for the same to make it a part of the library itself.
For most cases, if you want the chromosomes to directly represent the data points from any dataset, the initial parent population at generation 0 is used to feed from the dataset. For example, if you have a total of 10 data points, and you want to create 4 chromosomes, each with 6 alleles, randomly chosen from the set of 10 data points, this has to be done using the GeneticCodeProducer
to read the dataset and PopulationProducer
to create the chromosomes. However, if you use binary encoding, you might want to keep a reference to your dataset instead of creating the chromosomes using the dataset. This is implementation dependant.
To create chromosomes that directly depend on the dataset, you may have to write your own implementation that implements the GeneticCodeProducer interface. You may want to take a look at the library's GeneticCodeProducerProvider class to refer as examples on how to implement your own GeneticCodeProducer. Alternatively, you may even want to use any of the default implementations of GeneticCodeProducer
from the GeneticCodeProducerProvider
class of the library instead of implementing your own. The library supports binary encoding, value encoding and permutation encoding out of the box.
Refer to the GeneticCodeProducer Documentation to understand what it does and how it works. Basically, you need a genetic code for each of your chromosomes to work with. The genetic code, in this case, is a list of AbstractAllele
s of your choice. For more information regarding this ideology of mimicking these biological entities in this library, refer the Introduction section. The chromosomes can all have fixed length genetic code or variable length genetic code. By default, all chromosomes use the same GeneticCodeProducer
implementation for producing their genetic code. This is used to produce the genetic code for the chromosomes of the initial population at generation 0.
Every generation requires you to provide a Population
of chromosomes. For this, you need an implementation of the PopulationProducer and ChildPopulationProducer interfaces. Alternatively, you can use the default implementations provided by the library out of the box from the DefaultPluginProvider class.
The PopulationProducer
is used to produce the initial parent population for the algorithm to work with, while the ChildPopulationProducer
is used to generate the consecutive child populations required at each generation. To know more about these interfaces, refer to their documentation here and here. Basically, the initial parent population is produced using the GeneticCodeProducer
, where you can have your own implementation of GeneticCodeProducer
to read your dataset, while the ChildPopulationProducer
is used to generate all consecutive child populations at each generation.
The good thing about this library is that, if you only care to fit in your dataset while using all other default implementation for the algorithm, all you need to do is provide your own implementation of GeneticCodeProducer
and inject it within the Configuration object. The library will automatically integrate your code with the rest of the implementation. Hence, the steps are as follows:
- Implement your own
GeneticCodeProducer
if required. - Implement your own
PopulationProducer
if required. - Implement your own
ChildPopulationProducer
if required. - Set your custom implementation(s) to a
Configuration
object. - Provide this configuration object to your instance of
NSGA2()
. TheNSGA2()
constructor should setup everything accordingly. - Run
NSGA2()
.
You might also want to take a look at this issue, and specifically this comment to have a quick example on how you can use your own dataset with this library.