From 6e9e877ed669c47898dffddc99ae9d505339fb31 Mon Sep 17 00:00:00 2001 From: Simon Girardin Date: Thu, 11 Jun 2020 21:39:31 +0200 Subject: [PATCH] Development (#2) * Initial Commit * Adapted the readme.md and moved the main class to be executable with exec-maven plugin * Adapted project information * Update README.md * Title correction * Deleted previous versions * Added reference to the data sets --- .gitignore | 3 +- README.md | 85 +-- pom.xml | 85 +-- src/main/java/NSGA2Test.java | 40 ++ .../nsgaii/datastructure/IntegerAllele.java | 27 + .../MaximizeEmployeeSatisfaction.java | 149 +++++ .../MaximizePersonalCostOptmization.java | 50 ++ .../aci/nsgaii/plugin/OnePointCrossover.java | 60 ++ .../plugin/SchedulingObjectiveProvider.java | 20 + .../plugin/SchedulingPluginProvider.java | 90 +++ .../aci/nsgaii/population/TrainCrewSet.java | 89 +++ .../nsgaii/population/TrainServiceSet.java | 110 ++++ .../sets/models/DistanceBetweenPlaces.java | 85 +++ .../aci/nsgaii/sets/models/TrainCrew.java | 74 +++ .../aci/nsgaii/sets/models/TrainService.java | 81 +++ .../aci/nsgaii/sets/models/enums/Place.java | 5 + .../aci/nsgaii/sets/models/enums/Shift.java | 5 + .../models/enums/TrainCrewPopulationSize.java | 5 + .../nsgaii/sets/models/enums/TrainType.java | 5 + .../com/debacharya/nsgaii/Configuration.java | 198 ++---- .../java/com/debacharya/nsgaii/NSGA2.java | 16 +- .../java/com/debacharya/nsgaii/Reporter.java | 260 ++++---- .../java/com/debacharya/nsgaii/Service.java | 21 + .../nsgaii/datastructure/AbstractAllele.java | 8 +- .../nsgaii/datastructure/BooleanAllele.java | 8 +- .../nsgaii/datastructure/Chromosome.java | 35 +- .../nsgaii/plugin/AbstractMutation.java | 4 +- .../plugin/FitnessCalculatorProvider.java | 4 +- .../nsgaii/plugin/SinglePointMutation.java | 51 +- .../nsgaii/plugin/UniformCrossover.java | 4 +- src/test/java/NSGA2Test.java | 11 - v1/README.md | 74 --- v1/README.v1.md | 75 --- v1/pom.xml | 25 - .../nsga/ii/Interface/IObjectiveFunction.java | 24 - .../onclave/nsga/ii/algorithm/Algorithm.java | 179 ------ .../io/onclave/nsga/ii/api/GraphPlot.java | 131 ---- .../java/io/onclave/nsga/ii/api/Reporter.java | 96 --- .../java/io/onclave/nsga/ii/api/Service.java | 570 ------------------ .../io/onclave/nsga/ii/api/Synthesis.java | 187 ------ .../nsga/ii/configuration/Configuration.java | 87 --- .../onclave/nsga/ii/datastructure/Allele.java | 37 -- .../nsga/ii/datastructure/Chromosome.java | 103 ---- .../nsga/ii/datastructure/ParetoObject.java | 57 -- .../nsga/ii/datastructure/Population.java | 34 -- .../nsga/ii/objectivefunction/SCH_1.java | 40 -- .../nsga/ii/objectivefunction/SCH_2.java | 41 -- .../nsga/ii/objectivefunction/ZDT1_1.java | 41 -- .../nsga/ii/objectivefunction/ZDT1_2.java | 46 -- v1/src/test/java/TestService.java | 100 --- v1/src/test/java/TestSynthesis.java | 94 --- v2/README.md | 98 --- v2/pom.xml | 25 - .../onclave/nsga/ii/algorithm/Algorithm.java | 60 -- .../io/onclave/nsga/ii/api/Configuration.java | 146 ----- .../io/onclave/nsga/ii/api/GraphPlot.java | 114 ---- .../java/io/onclave/nsga/ii/api/NSGAII.java | 145 ----- .../java/io/onclave/nsga/ii/api/Reporter.java | 108 ---- .../java/io/onclave/nsga/ii/api/Service.java | 234 ------- .../io/onclave/nsga/ii/api/Synthesis.java | 157 ----- .../onclave/nsga/ii/datastructure/Allele.java | 30 - .../nsga/ii/datastructure/Chromosome.java | 103 ---- .../nsga/ii/datastructure/Population.java | 27 - .../ii/interfaces/IObjectiveFunction.java | 18 - .../nsga/ii/objectivefunction/SCH_1.java | 30 - .../nsga/ii/objectivefunction/SCH_2.java | 30 - 66 files changed, 1254 insertions(+), 3800 deletions(-) create mode 100644 src/main/java/NSGA2Test.java create mode 100644 src/main/java/ch/fhnw/mbis/aci/nsgaii/datastructure/IntegerAllele.java create mode 100644 src/main/java/ch/fhnw/mbis/aci/nsgaii/objectivefunction/MaximizeEmployeeSatisfaction.java create mode 100644 src/main/java/ch/fhnw/mbis/aci/nsgaii/objectivefunction/MaximizePersonalCostOptmization.java create mode 100644 src/main/java/ch/fhnw/mbis/aci/nsgaii/plugin/OnePointCrossover.java create mode 100644 src/main/java/ch/fhnw/mbis/aci/nsgaii/plugin/SchedulingObjectiveProvider.java create mode 100644 src/main/java/ch/fhnw/mbis/aci/nsgaii/plugin/SchedulingPluginProvider.java create mode 100644 src/main/java/ch/fhnw/mbis/aci/nsgaii/population/TrainCrewSet.java create mode 100644 src/main/java/ch/fhnw/mbis/aci/nsgaii/population/TrainServiceSet.java create mode 100644 src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/DistanceBetweenPlaces.java create mode 100644 src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/TrainCrew.java create mode 100644 src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/TrainService.java create mode 100644 src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/enums/Place.java create mode 100644 src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/enums/Shift.java create mode 100644 src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/enums/TrainCrewPopulationSize.java create mode 100644 src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/enums/TrainType.java delete mode 100644 src/test/java/NSGA2Test.java delete mode 100644 v1/README.md delete mode 100644 v1/README.v1.md delete mode 100644 v1/pom.xml delete mode 100644 v1/src/main/java/io/onclave/nsga/ii/Interface/IObjectiveFunction.java delete mode 100644 v1/src/main/java/io/onclave/nsga/ii/algorithm/Algorithm.java delete mode 100644 v1/src/main/java/io/onclave/nsga/ii/api/GraphPlot.java delete mode 100644 v1/src/main/java/io/onclave/nsga/ii/api/Reporter.java delete mode 100644 v1/src/main/java/io/onclave/nsga/ii/api/Service.java delete mode 100644 v1/src/main/java/io/onclave/nsga/ii/api/Synthesis.java delete mode 100644 v1/src/main/java/io/onclave/nsga/ii/configuration/Configuration.java delete mode 100644 v1/src/main/java/io/onclave/nsga/ii/datastructure/Allele.java delete mode 100644 v1/src/main/java/io/onclave/nsga/ii/datastructure/Chromosome.java delete mode 100644 v1/src/main/java/io/onclave/nsga/ii/datastructure/ParetoObject.java delete mode 100644 v1/src/main/java/io/onclave/nsga/ii/datastructure/Population.java delete mode 100644 v1/src/main/java/io/onclave/nsga/ii/objectivefunction/SCH_1.java delete mode 100644 v1/src/main/java/io/onclave/nsga/ii/objectivefunction/SCH_2.java delete mode 100644 v1/src/main/java/io/onclave/nsga/ii/objectivefunction/ZDT1_1.java delete mode 100644 v1/src/main/java/io/onclave/nsga/ii/objectivefunction/ZDT1_2.java delete mode 100644 v1/src/test/java/TestService.java delete mode 100644 v1/src/test/java/TestSynthesis.java delete mode 100644 v2/README.md delete mode 100644 v2/pom.xml delete mode 100644 v2/src/main/java/io/onclave/nsga/ii/algorithm/Algorithm.java delete mode 100644 v2/src/main/java/io/onclave/nsga/ii/api/Configuration.java delete mode 100644 v2/src/main/java/io/onclave/nsga/ii/api/GraphPlot.java delete mode 100644 v2/src/main/java/io/onclave/nsga/ii/api/NSGAII.java delete mode 100644 v2/src/main/java/io/onclave/nsga/ii/api/Reporter.java delete mode 100644 v2/src/main/java/io/onclave/nsga/ii/api/Service.java delete mode 100644 v2/src/main/java/io/onclave/nsga/ii/api/Synthesis.java delete mode 100644 v2/src/main/java/io/onclave/nsga/ii/datastructure/Allele.java delete mode 100644 v2/src/main/java/io/onclave/nsga/ii/datastructure/Chromosome.java delete mode 100644 v2/src/main/java/io/onclave/nsga/ii/datastructure/Population.java delete mode 100644 v2/src/main/java/io/onclave/nsga/ii/interfaces/IObjectiveFunction.java delete mode 100644 v2/src/main/java/io/onclave/nsga/ii/objectivefunction/SCH_1.java delete mode 100644 v2/src/main/java/io/onclave/nsga/ii/objectivefunction/SCH_2.java diff --git a/.gitignore b/.gitignore index 203b996..150c072 100644 --- a/.gitignore +++ b/.gitignore @@ -82,4 +82,5 @@ nb-configuration.xml ############################## ## OS X ############################## -.DS_Store \ No newline at end of file +.DS_Store +/NSGA-II-report-*.txt diff --git a/README.md b/README.md index 6a1e8eb..5aaf454 100644 --- a/README.md +++ b/README.md @@ -1,53 +1,32 @@ -# NSGA-II -**an NSGA-II implementation using Java** - -**_Original Authors of the Paper_: [Kalyanmoy Deb](http://www.egr.msu.edu/~kdeb/), [Amrit Pratap](https://scholar.google.com/citations?user=E8wJ7G8AAAAJ&hl=en), [Sameer Agarwal](http://ieeexplore.ieee.org/search/searchresult.jsp?searchWithin=%22Authors%22:.QT.S.%20Agarwal.QT.&newsearch=true), [T. Meyarivan](http://ieeexplore.ieee.org/search/searchresult.jsp?searchWithin=%22Authors%22:.QT.T.%20Meyarivan.QT.&newsearch=true)** - -_links to original contents:_ - -* [NSGA-II paper: PDF](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.542.385&rep=rep1&type=pdf) -* [NSGA-II code implementation by original authors at **KanGAL**](https://www.iitk.ac.in/kangal/codes.shtml) - -_**note**: this implementation of NSGA-II algorithm is in pure reference to the original published paper. This is not an effort to convert the originally implemented C code in Java. The original C code by the authors has not be referred to while writing this implementation._ - -_**Dependency: Java( >= 13), JFreeChart(1.5.0), JCommon(1.0.24)**_ - -### Please Note: - -This is **v3** of the algorithm implementation. This reference implementation has been updated to be: - -* Used as package. -* Much more generic, customizable and hence more powerful. -* More efficient than the previous version. - -The reference **v2** implementation can be found [here](https://github.com/onclave/NSGA-II/tree/master/v2). The _README_ of that implementation can be found [here](https://github.com/onclave/NSGA-II/blob/master/v2/README.md). - -The reference **v1** implementation can be found [here](https://github.com/onclave/NSGA-II/tree/master/v1). The _README_ of that implementation can be found [here](https://github.com/onclave/NSGA-II/blob/master/v1/README.md). - -**note:** Code commenting of **v3** is under progress and not all code is commented properly. This shall be done shortly. In the mean time, if you are unable to understand any part of the code, feel free to open an _issue_ about it and I shall try to - resolve it. - -### Documentation - -This is a fully customizable implementation of the NSGA-II algorithm, made as generic as possible. This documentation assumes you have basic understanding of the NSGA-II algorithm. Apart from the core concepts of the algorithm, everything else in this - package can be implemented as per the user's choice and plugged into the algorithm dynamically. - - By default, the package provides a default implementation of every plugin and hence the package can be run with just one line of code as a PoC. - - ```java -(new NSGA2()).run(); - ``` - -For more information, visit the [Wiki](https://github.com/onclave/NSGA-II/wiki) - -For full documentation, visit the [Documentation Wiki](https://github.com/onclave/NSGA-II/wiki/Documentation). - -### Using it in your project - -This package shall be published to maven shortly. Till then you can use the source package directly in your project. - -### [Getting Started](https://github.com/onclave/NSGA-II/wiki/Getting-Started) - -### Contributing - -This project is open to pull requests and encourages new features through contribution. The contribution guidelines shall be updated shortly. \ No newline at end of file +# Train rostering using NSGA-II +**an NSGA-II implementation for assigning randomly Train Services to train crews** + +**Original implementation of the algorithm: [here](https://github.com/onclave/NSGA-II) v3.0.1** +**Original readme.md [here](https://github.com/sgirardin/NSGA-II/wiki/Original-readme.md)** + +## Installation +You will need: +1. Have Java >= 13 +1. Maven 3+ +1. Then clone the repository locally + +## How to run +### With your favorite IDE (prefered way) +1. Run the main class ```NSGA2Test.java``` + +### Command line +1. Go in project root folder with a terminal +2. Run ```mvn install -Dgpg.skip exec:java``` +3. Do modification in the configuration and do the command at point above. + +## Configuration +In the ```NSGA2Test:main``` method you can change the following parameters: +1. Population size +2. Number of generations +3. Mutation probability +4. Which crossover operator (for the moment we have UniformCrossover or OnePointMutation) +5. Size of Train Crew against which 63 Services will be distributed + +## Data sets +* ```ch.fhnw.mbis.aci.nsgaii.sets.models.TrainService.java``` for the train services +* ```ch.fhnw.mbis.aci.nsgaii.sets.models.TrainCrew.java ```for the train drivers \ No newline at end of file diff --git a/pom.xml b/pom.xml index bc7c967..af54401 100644 --- a/pom.xml +++ b/pom.xml @@ -6,63 +6,11 @@ NSGA-II - - oss-parent - org.sonatype.oss - 9 - - - com.debacharya + ch.fhnw.aci nsgaii - 3.0.1 + 1.0.0 jar - 2019 - https://debacharya.com/nsgaii - - - A NSGA-II implementation using Java. This implementation of NSGA-II algorithm is in pure reference to the - original published paper. This is not an effort to convert the originally implemented C code in Java. - The original C code by the authors has not be referred to while writing this implementation. - This is a fully customizable implementation of the NSGA-II algorithm, made as generic as possible. - This documentation assumes you have basic understanding of the NSGA-II algorithm. Apart from the core concepts - of the algorithm, everything else in this package can be implemented as per the user's choice and plugged - into the algorithm dynamically. Since NSGA-II is more like a set of protocols to follow as an algorithm rather - than a concrete implementation of every aspect, this package has been re-written from scratch keeping complete - customizability in mind. Apart from the core concepts of the algorithm, everything is considered to be a plugin - external to the algorithm that can be implemented by the user and dynamically plugged into the algorithm during - runtime as needed. This opens up the possibility of the package to be used simply as a PoC or be converted into - something much more complex according to the users needs. - - - - https://github.com/onclave/NSGA-II/issues - GitHub Issues - - - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2 - - - - - scm:git:git:https://github.com/onclave/NSGA-II.git - https://github.com/onclave/NSGA-II - - - - - Debabrata Acharya - debabrata@bloomscorp.com - - - MIT License @@ -81,6 +29,19 @@ jcommon 1.0.24 + + + com.github.javafaker + javafaker + 1.0.2 + + + junit + junit + 4.12 + test + + @@ -148,6 +109,22 @@ + + org.apache.maven.plugins + maven-compiler-plugin + + 13 + 13 + + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + NSGA2Test + + diff --git a/src/main/java/NSGA2Test.java b/src/main/java/NSGA2Test.java new file mode 100644 index 0000000..667e5e1 --- /dev/null +++ b/src/main/java/NSGA2Test.java @@ -0,0 +1,40 @@ +import ch.fhnw.mbis.aci.nsgaii.plugin.OnePointCrossover; +import ch.fhnw.mbis.aci.nsgaii.plugin.SchedulingPluginProvider; +import ch.fhnw.mbis.aci.nsgaii.population.TrainCrewSet; +import ch.fhnw.mbis.aci.nsgaii.population.TrainServiceSet; +import ch.fhnw.mbis.aci.nsgaii.sets.models.enums.TrainCrewPopulationSize; +import com.debacharya.nsgaii.Configuration; +import com.debacharya.nsgaii.NSGA2; +import com.debacharya.nsgaii.datastructure.Population; +import com.debacharya.nsgaii.plugin.CrossoverParticipantCreatorProvider; +import com.debacharya.nsgaii.plugin.PopulationProducer; +import com.debacharya.nsgaii.plugin.SinglePointMutation; +import com.debacharya.nsgaii.plugin.UniformCrossover; + +public class NSGA2Test { + + public static void main(String[] args) { + // Set parameters + int populationSize = 3; + int nbGenerations = 1; + float mutationProbability = 0.4f; + TrainCrewPopulationSize crewPopulationSize = TrainCrewPopulationSize.TWENTY; + + //Implemented Mutations and crossovers + UniformCrossover uniformCrossover = new UniformCrossover(CrossoverParticipantCreatorProvider.selectByBinaryTournamentSelection()); + OnePointCrossover onePointCrossover = new OnePointCrossover(CrossoverParticipantCreatorProvider.selectByBinaryTournamentSelection()); + SinglePointMutation singlePointMutation = new SinglePointMutation(mutationProbability); + + + //Initialize Population sets + TrainServiceSet.setupServiceSet(); + TrainCrewSet.setupPopulation(crewPopulationSize); + + PopulationProducer populationProducer = SchedulingPluginProvider.defaultPopulationProducer(); + + Configuration configuration = new Configuration(populationSize,nbGenerations, populationProducer, uniformCrossover, singlePointMutation); + + NSGA2 nsga2 = new NSGA2(configuration); + Population paretoFront = nsga2.run(); + } +} diff --git a/src/main/java/ch/fhnw/mbis/aci/nsgaii/datastructure/IntegerAllele.java b/src/main/java/ch/fhnw/mbis/aci/nsgaii/datastructure/IntegerAllele.java new file mode 100644 index 0000000..2335eed --- /dev/null +++ b/src/main/java/ch/fhnw/mbis/aci/nsgaii/datastructure/IntegerAllele.java @@ -0,0 +1,27 @@ +package ch.fhnw.mbis.aci.nsgaii.datastructure; + +import com.debacharya.nsgaii.datastructure.AbstractAllele; + +public class IntegerAllele extends AbstractAllele { + + public IntegerAllele(Integer gene) { + super(gene); + } + + @Override + public Integer getAllele() { + return (int) this.allele; + } + + @Override + public IntegerAllele getCopy() { + return new IntegerAllele((Integer)this.allele); + } + + @Override + public String toString() { + return "IntegerAllele{" + + "gene=" + allele + + '}'; + } +} diff --git a/src/main/java/ch/fhnw/mbis/aci/nsgaii/objectivefunction/MaximizeEmployeeSatisfaction.java b/src/main/java/ch/fhnw/mbis/aci/nsgaii/objectivefunction/MaximizeEmployeeSatisfaction.java new file mode 100644 index 0000000..98166b9 --- /dev/null +++ b/src/main/java/ch/fhnw/mbis/aci/nsgaii/objectivefunction/MaximizeEmployeeSatisfaction.java @@ -0,0 +1,149 @@ +package ch.fhnw.mbis.aci.nsgaii.objectivefunction; + +import ch.fhnw.mbis.aci.nsgaii.population.TrainCrewSet; +import ch.fhnw.mbis.aci.nsgaii.population.TrainServiceSet; +import ch.fhnw.mbis.aci.nsgaii.sets.models.TrainCrew; +import ch.fhnw.mbis.aci.nsgaii.sets.models.TrainService; +import ch.fhnw.mbis.aci.nsgaii.sets.models.enums.Shift; +import com.debacharya.nsgaii.datastructure.Chromosome; +import com.debacharya.nsgaii.objectivefunction.AbstractObjectiveFunction; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.debacharya.nsgaii.Configuration.HARD_CONSTRAINT_PENALTY; + +public class MaximizeEmployeeSatisfaction extends AbstractObjectiveFunction { + + private int penaltyConstraintSameTrain = - 2; + private int penaltyConstraintShiftPreference = - 3; + private int penaltyConstraintSameShiftDuringWeek = - 5; + + public MaximizeEmployeeSatisfaction() { + this.objectiveFunctionTitle = "Maximize Employee Satisfaction"; + } + + @Override + public double getValue(Chromosome chromosome) { + List geneList = chromosome.retrieveChromosomeGenes(); + + Map> trainCrewServices = new HashMap<>(); + + double chromosomeFitness = 0d; + int locus = 0; + + for(TrainService currentTrainService: + TrainServiceSet.getPopulation().values()){ + + String gene = geneList.get(locus); + + TrainCrew trainCrew = TrainCrewSet.getPopulation().get(Integer.parseInt(gene,2)); + if (trainCrew != null ){ + List previousTrainServicesCrew = retrieveTrainService(trainCrew, trainCrewServices); + + chromosomeFitness = chromosomeFitness + calculatePenaltyShiftFollowing(previousTrainServicesCrew, currentTrainService); + chromosomeFitness = chromosomeFitness + calculatePenaltyShiftSameDay(previousTrainServicesCrew, currentTrainService); + chromosomeFitness = chromosomeFitness + calculatePenaltySoftConstraintSameShiftDuringWeek(previousTrainServicesCrew, currentTrainService); + chromosomeFitness = chromosomeFitness + calculatePenaltyConstraintShiftPreference(trainCrew, currentTrainService); + chromosomeFitness = chromosomeFitness + calculatePenaltySoftConstraintSameTrainType(trainCrew, currentTrainService); + + previousTrainServicesCrew.add(currentTrainService); + trainCrewServices.put(trainCrew, previousTrainServicesCrew); + } else { + chromosomeFitness = chromosomeFitness + HARD_CONSTRAINT_PENALTY; + } + locus++; + } + + return chromosomeFitness; + } + + private double calculatePenaltyShiftFollowing(List previousTrainServices, TrainService currentTrainService){ + int penalty = 0; + + for(TrainService previousTrainService + :previousTrainServices){ + if(isPreviousShiftDirectlyAfterNewShift(previousTrainService,currentTrainService)){ + penalty = penalty + HARD_CONSTRAINT_PENALTY; + + } + } + return penalty; + } + + private double calculatePenaltyShiftSameDay(List previousTrainServices, TrainService currentTrainService){ + int penalty = 0; + + for(TrainService previousTrainService + :previousTrainServices){ + if(areServicesSameDay(previousTrainService,currentTrainService)){ + penalty = penalty + HARD_CONSTRAINT_PENALTY; + + } + } + return penalty; + } + + private double calculatePenaltySoftConstraintSameShiftDuringWeek(List previousTrainServices, TrainService currentTrainService){ + int penalty = 0; + + for(TrainService previousTrainService + :previousTrainServices){ + if(!previousTrainService.getServiceShift().equals(currentTrainService.getServiceShift())){ + penalty = penalty + penaltyConstraintSameShiftDuringWeek; + } + } + return penalty; + } + + private double calculatePenaltySoftConstraintSameTrainType(TrainCrew trainCrew, TrainService trainService){ + double penalty = 0d; + if(trainCrew.isFavroriseSameTrainPreference()){ + if(trainCrew.getFavoriteTrainType() != trainService.getTrainType()){ + penalty = penaltyConstraintSameTrain; + } + } + return penalty; + } + + private double calculatePenaltyConstraintShiftPreference(TrainCrew trainCrew, TrainService trainService){ + double penalty = 0d; + if(trainCrew.isFavoriseSameShiftPreference()){ + if(trainCrew.getShiftPreference() != trainService.getServiceShift()){ + penalty = penaltyConstraintShiftPreference; + } + } + return penalty; + } + + private List retrieveTrainService(TrainCrew trainCrew, Map> trainCrewServices){ + if (!trainCrewServices.containsKey(trainCrew)){ + trainCrewServices.put(trainCrew, new ArrayList<>()); + } + return trainCrewServices.get(trainCrew); + } + + private boolean isPreviousShiftDirectlyAfterNewShift(TrainService previousTrainService,TrainService currentTrainService){ + boolean isShiftPrevious = false; + + if (areServicesOneDayPriorDay(previousTrainService, currentTrainService) + && (currentTrainService.getServiceShift().equals(Shift.MORNING_SHIFT) + && previousTrainService.getServiceShift().equals(Shift.NIGHT_SHIFT))){ + isShiftPrevious = true; + } else { + //Already handeld in the calculatePenaltyShiftSameDay + } + + return isShiftPrevious; + } + + private boolean areServicesSameDay(TrainService previousTrainService,TrainService currentTrainService){ + return previousTrainService.getStartTime().getDayOfWeek().equals(currentTrainService.getStartTime().getDayOfWeek()); + } + private boolean areServicesOneDayPriorDay(TrainService previousTrainService,TrainService currentTrainService){ + //TODO handle new year + return currentTrainService.getStartTime().getDayOfYear() - previousTrainService.getStartTime().getDayOfYear() == 1; + } +} diff --git a/src/main/java/ch/fhnw/mbis/aci/nsgaii/objectivefunction/MaximizePersonalCostOptmization.java b/src/main/java/ch/fhnw/mbis/aci/nsgaii/objectivefunction/MaximizePersonalCostOptmization.java new file mode 100644 index 0000000..deaba2e --- /dev/null +++ b/src/main/java/ch/fhnw/mbis/aci/nsgaii/objectivefunction/MaximizePersonalCostOptmization.java @@ -0,0 +1,50 @@ +package ch.fhnw.mbis.aci.nsgaii.objectivefunction; + +import ch.fhnw.mbis.aci.nsgaii.population.TrainCrewSet; +import ch.fhnw.mbis.aci.nsgaii.population.TrainServiceSet; +import ch.fhnw.mbis.aci.nsgaii.sets.models.DistanceBetweenPlaces; +import ch.fhnw.mbis.aci.nsgaii.sets.models.TrainCrew; +import ch.fhnw.mbis.aci.nsgaii.sets.models.TrainService; +import com.debacharya.nsgaii.datastructure.Chromosome; +import com.debacharya.nsgaii.objectivefunction.AbstractObjectiveFunction; + +import java.util.List; + +import static com.debacharya.nsgaii.Configuration.HARD_CONSTRAINT_PENALTY; + +public class MaximizePersonalCostOptmization extends AbstractObjectiveFunction { + + public MaximizePersonalCostOptmization() { + this.objectiveFunctionTitle = "Maximize Personal Cost optmization"; + } + + @Override + public double getValue(Chromosome chromosome) { + List geneList = chromosome.retrieveChromosomeGenes(); + + double chromosomeFitness = 0.0; + int locus = 0; + + for(TrainService trainService: + TrainServiceSet.getPopulation().values()){ + String gene = geneList.get(locus); + + TrainCrew trainCrew = TrainCrewSet.getPopulation().get(Integer.parseInt(gene,2)); + if(trainCrew != null && trainService != null){ + chromosomeFitness = chromosomeFitness - calculateGeneCost(trainCrew, trainService); + } else { + chromosomeFitness = chromosomeFitness + HARD_CONSTRAINT_PENALTY; + } + + locus++; + } + + return chromosomeFitness; + } + + private double calculateGeneCost(TrainCrew trainCrew, TrainService trainService){ + double totalDistance = DistanceBetweenPlaces.caculateDistance(trainCrew.getPlaceOfResidence(),trainService.getStartPlace()); + totalDistance = totalDistance + DistanceBetweenPlaces.caculateDistance(trainService.getEndPlace(),trainCrew.getPlaceOfResidence()); + return totalDistance; + } +} diff --git a/src/main/java/ch/fhnw/mbis/aci/nsgaii/plugin/OnePointCrossover.java b/src/main/java/ch/fhnw/mbis/aci/nsgaii/plugin/OnePointCrossover.java new file mode 100644 index 0000000..0122963 --- /dev/null +++ b/src/main/java/ch/fhnw/mbis/aci/nsgaii/plugin/OnePointCrossover.java @@ -0,0 +1,60 @@ +package ch.fhnw.mbis.aci.nsgaii.plugin; + +import com.debacharya.nsgaii.Configuration; +import com.debacharya.nsgaii.datastructure.AbstractAllele; +import com.debacharya.nsgaii.datastructure.Chromosome; +import com.debacharya.nsgaii.datastructure.Population; +import com.debacharya.nsgaii.plugin.AbstractCrossover; +import com.debacharya.nsgaii.plugin.CrossoverParticipantCreator; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +public class OnePointCrossover extends AbstractCrossover { + + public OnePointCrossover(CrossoverParticipantCreator crossoverParticipantCreator) { + super(crossoverParticipantCreator); + } + + @Override + public List perform(Population population) { + + List result = new ArrayList<>(); + List selected = this.crossoverParticipantCreator.create(population); + + if(this.shouldPerformCrossover()) + for(int i = 0; i < 2; i++) + result.add( + this.prepareChildChromosome( + selected.get(0), + selected.get(1) + ) + ); + else { + result.add(selected.get(0).getCopy()); + result.add(selected.get(1).getCopy()); + } + + return result; + } + + private Chromosome prepareChildChromosome(Chromosome chromosome1, Chromosome chromosome2) { + + List geneticCode = new ArrayList<>(); + + int crossOverLocus = findCrossoverAllele(chromosome1.getChromosomeAlleles().size()); + + geneticCode.addAll(chromosome1.getChromosomeAlleles().subList(0, crossOverLocus)); + geneticCode.addAll(chromosome2.getChromosomeAlleles().subList(crossOverLocus, chromosome2.getChromosomeAlleles().size())); + + + return new Chromosome(geneticCode); + } + + private int findCrossoverAllele(int chromosomeSize){ + int geneSize = Configuration.calculateGeneSize(); + int randomLocus = ThreadLocalRandom.current().nextInt(0, chromosomeSize/geneSize); + return randomLocus * geneSize; + } +} diff --git a/src/main/java/ch/fhnw/mbis/aci/nsgaii/plugin/SchedulingObjectiveProvider.java b/src/main/java/ch/fhnw/mbis/aci/nsgaii/plugin/SchedulingObjectiveProvider.java new file mode 100644 index 0000000..a97933e --- /dev/null +++ b/src/main/java/ch/fhnw/mbis/aci/nsgaii/plugin/SchedulingObjectiveProvider.java @@ -0,0 +1,20 @@ +package ch.fhnw.mbis.aci.nsgaii.plugin; + +import ch.fhnw.mbis.aci.nsgaii.objectivefunction.MaximizeEmployeeSatisfaction; +import ch.fhnw.mbis.aci.nsgaii.objectivefunction.MaximizePersonalCostOptmization; +import com.debacharya.nsgaii.objectivefunction.AbstractObjectiveFunction; + +import java.util.ArrayList; +import java.util.List; + +public class SchedulingObjectiveProvider { + + public static List provideSCHObjectives() { + + List objectives = new ArrayList<>(); + objectives.add(new MaximizeEmployeeSatisfaction()); + objectives.add(new MaximizePersonalCostOptmization()); + + return objectives; + } +} diff --git a/src/main/java/ch/fhnw/mbis/aci/nsgaii/plugin/SchedulingPluginProvider.java b/src/main/java/ch/fhnw/mbis/aci/nsgaii/plugin/SchedulingPluginProvider.java new file mode 100644 index 0000000..2c75b89 --- /dev/null +++ b/src/main/java/ch/fhnw/mbis/aci/nsgaii/plugin/SchedulingPluginProvider.java @@ -0,0 +1,90 @@ +package ch.fhnw.mbis.aci.nsgaii.plugin; + +import ch.fhnw.mbis.aci.nsgaii.population.TrainCrewSet; +import ch.fhnw.mbis.aci.nsgaii.sets.models.TrainCrew; +import com.debacharya.nsgaii.Configuration; +import com.debacharya.nsgaii.Service; +import com.debacharya.nsgaii.datastructure.BooleanAllele; +import com.debacharya.nsgaii.datastructure.Chromosome; +import com.debacharya.nsgaii.datastructure.Population; +import com.debacharya.nsgaii.plugin.ChildPopulationProducer; +import com.debacharya.nsgaii.plugin.GeneticCodeProducer; +import com.debacharya.nsgaii.plugin.PopulationProducer; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +public class SchedulingPluginProvider { + + private static String geneFormat = "%0"+ Configuration.calculateGeneSize() +"d"; + + public static GeneticCodeProducer defaultGeneticCodeProducer() { + return (length) -> { + List chromosomeCode = new ArrayList<>(); + + while(chromosomeCode.size()< length){ + int trainCrewID = ThreadLocalRandom.current().nextInt(1, TrainCrewSet.getPopulation().size()); + TrainCrew trainCrew = TrainCrewSet.getPopulation().get(trainCrewID); + + String gene = String.format(geneFormat,returnBinaryValueFromInt(trainCrew.getID())); + + for(char alleleChar : gene.toCharArray()){ + chromosomeCode.add(new BooleanAllele(returnBooleanValueFromChar(alleleChar))); + } + } + + return chromosomeCode; + }; + } + + public static PopulationProducer defaultPopulationProducer() { + return (populationSize, chromosomeLength, geneticCodeProducer, fitnessCalculator) -> { + + List populace = new ArrayList<>(); + + for(int i = 0; i < populationSize; i++) + populace.add( + new Chromosome( + geneticCodeProducer.produce(chromosomeLength) + ) + ); + + return new Population(populace); + }; + } + + public static ChildPopulationProducer defaultChildPopulationProducer() { + return (parentPopulation, crossover, mutation, populationSize) -> { + + List populace = new ArrayList<>(); + + while(populace.size() < populationSize) + if((populationSize - populace.size()) == 1) + populace.add( + mutation.perform( + Service.crowdedBinaryTournamentSelection(parentPopulation) + ) + ); + else + for(Chromosome chromosome : crossover.perform(parentPopulation)) + populace.add(mutation.perform(chromosome)); + + return new Population(populace); + }; + } + + private static boolean returnBooleanValueFromChar(char charValue){ + if('1' == charValue){ + return true; + } else if ('0' == charValue){ + return false; + } else { + throw new IllegalArgumentException(); + } + } + + private static int returnBinaryValueFromInt(int intToTransform){ + return Integer.parseInt(Integer.toBinaryString(intToTransform)); + } +} diff --git a/src/main/java/ch/fhnw/mbis/aci/nsgaii/population/TrainCrewSet.java b/src/main/java/ch/fhnw/mbis/aci/nsgaii/population/TrainCrewSet.java new file mode 100644 index 0000000..b5b7d35 --- /dev/null +++ b/src/main/java/ch/fhnw/mbis/aci/nsgaii/population/TrainCrewSet.java @@ -0,0 +1,89 @@ +package ch.fhnw.mbis.aci.nsgaii.population; + +import ch.fhnw.mbis.aci.nsgaii.sets.models.TrainCrew; +import ch.fhnw.mbis.aci.nsgaii.sets.models.enums.Place; +import ch.fhnw.mbis.aci.nsgaii.sets.models.enums.Shift; +import ch.fhnw.mbis.aci.nsgaii.sets.models.enums.TrainCrewPopulationSize; +import ch.fhnw.mbis.aci.nsgaii.sets.models.enums.TrainType; +import com.github.javafaker.Faker; + +import java.util.*; + +public class TrainCrewSet { + + private static int trainCrewID = 0; + private static Map population = new HashMap<>(); + private static Faker faker = new Faker(new Locale("de-CH")); + + public static void setupPopulation(TrainCrewPopulationSize populationSize){ + List serviceList = null; + switch (populationSize){ + case TEN: + serviceList = generateTrain10Crew(); + break; + case FIFTEEN: + serviceList = generateTrain15Crew(); + break; + case TWENTY: + serviceList = generateTrain20Crew(); + break; + default: + serviceList = generateTrain10Crew(); + } + serviceList.stream().parallel().forEach(trainCrew -> population.put(trainCrew.getID(), trainCrew)); + } + + private static List generateTrain10Crew(){ + + List trainCrewList = new ArrayList<>(); + + trainCrewList.add(createTrainCrew(faker.name().firstName(), Place.D,Shift.DAY_SHIFT,false,true,TrainType.Z)); + trainCrewList.add(createTrainCrew(faker.name().firstName(), Place.C,Shift.MORNING_SHIFT,true,true,TrainType.Y)); + trainCrewList.add(createTrainCrew(faker.name().firstName(), Place.D,Shift.MORNING_SHIFT,false,true,TrainType.X)); + trainCrewList.add(createTrainCrew(faker.name().firstName(), Place.C,Shift.MORNING_SHIFT,false,false,TrainType.Z)); + trainCrewList.add(createTrainCrew(faker.name().firstName(), Place.B,Shift.NIGHT_SHIFT,true,true,TrainType.X)); + trainCrewList.add(createTrainCrew(faker.name().firstName(), Place.B,Shift.NIGHT_SHIFT,false,true,TrainType.X)); + trainCrewList.add(createTrainCrew(faker.name().firstName(), Place.D,Shift.DAY_SHIFT,true,true,TrainType.X)); + trainCrewList.add(createTrainCrew(faker.name().firstName(), Place.B,Shift.MORNING_SHIFT,true,false,TrainType.Z)); + trainCrewList.add(createTrainCrew(faker.name().firstName(), Place.C,Shift.MORNING_SHIFT,true,false,TrainType.Z)); + trainCrewList.add(createTrainCrew(faker.name().firstName(), Place.A,Shift.DAY_SHIFT,true,false,TrainType.Y)); + + return trainCrewList; + } + + private static List generateTrain15Crew(){ + + List trainCrewList = generateTrain10Crew(); + + trainCrewList.add(createTrainCrew(faker.name().firstName(), Place.D,Shift.DAY_SHIFT,false,true,TrainType.Z)); + trainCrewList.add(createTrainCrew(faker.name().firstName(), Place.C,Shift.MORNING_SHIFT,true,true,TrainType.Y)); + trainCrewList.add(createTrainCrew(faker.name().firstName(), Place.D,Shift.MORNING_SHIFT,false,true,TrainType.X)); + trainCrewList.add(createTrainCrew(faker.name().firstName(), Place.C,Shift.MORNING_SHIFT,false,false,TrainType.Z)); + trainCrewList.add(createTrainCrew(faker.name().firstName(), Place.B,Shift.NIGHT_SHIFT,true,true,TrainType.X)); + + return trainCrewList; + } + + private static List generateTrain20Crew(){ + + List trainCrewList = generateTrain15Crew(); + + trainCrewList.add(createTrainCrew(faker.name().firstName(), Place.B,Shift.NIGHT_SHIFT,false,true,TrainType.X)); + trainCrewList.add(createTrainCrew(faker.name().firstName(), Place.D,Shift.DAY_SHIFT,true,true,TrainType.X)); + trainCrewList.add(createTrainCrew(faker.name().firstName(), Place.B,Shift.MORNING_SHIFT,true,false,TrainType.Z)); + trainCrewList.add(createTrainCrew(faker.name().firstName(), Place.C,Shift.MORNING_SHIFT,true,false,TrainType.Z)); + trainCrewList.add(createTrainCrew(faker.name().firstName(), Place.A,Shift.DAY_SHIFT,true,false,TrainType.Y)); + + return trainCrewList; + } + + private static TrainCrew createTrainCrew(String name, Place placeOfResidence, Shift shiftPreference, boolean favoriseSameShiftPreference, boolean favroriseSameTrainPreference, TrainType favoriteTrainType){ + trainCrewID++; + int id = trainCrewID; + return new TrainCrew(id, name, placeOfResidence,shiftPreference,favoriseSameShiftPreference,favroriseSameTrainPreference, favoriteTrainType); + } + + public static Map getPopulation() { + return population; + } +} diff --git a/src/main/java/ch/fhnw/mbis/aci/nsgaii/population/TrainServiceSet.java b/src/main/java/ch/fhnw/mbis/aci/nsgaii/population/TrainServiceSet.java new file mode 100644 index 0000000..7f6c71c --- /dev/null +++ b/src/main/java/ch/fhnw/mbis/aci/nsgaii/population/TrainServiceSet.java @@ -0,0 +1,110 @@ +package ch.fhnw.mbis.aci.nsgaii.population; + + +import ch.fhnw.mbis.aci.nsgaii.sets.models.TrainService; +import ch.fhnw.mbis.aci.nsgaii.sets.models.enums.Place; +import ch.fhnw.mbis.aci.nsgaii.sets.models.enums.TrainType; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TrainServiceSet { + + private static int serviceID = 0; + + private static Map population = new HashMap<>(); + + public static void setupServiceSet(){ + List trainServiceList = generate63Services(); + trainServiceList.stream().forEach(trainService -> population.put(trainService.getID(), trainService)); + } + + + private static List generate63Services(){ + List trainServiceList = new ArrayList<>(); + trainServiceList.add(createService(Place.A,Place.B,1,4,11,TrainType.W)); + trainServiceList.add(createService(Place.B,Place.C,1,4,11,TrainType.X)); + trainServiceList.add(createService(Place.C,Place.A,1,4,11,TrainType.Y)); + trainServiceList.add(createService(Place.B,Place.D,1,11,18,TrainType.W)); + trainServiceList.add(createService(Place.C,Place.A,1,11,18,TrainType.X)); + trainServiceList.add(createService(Place.A,Place.B,1,11,18,TrainType.Y)); + trainServiceList.add(createService(Place.A,Place.C,1,18,0,TrainType.X)); + trainServiceList.add(createService(Place.B,Place.A,1,18,0,TrainType.Y)); + trainServiceList.add(createService(Place.D,Place.B,1,18,0,TrainType.Z)); + trainServiceList.add(createService(Place.D,Place.C,2,4,11,TrainType.W)); + trainServiceList.add(createService(Place.A,Place.B,2,4,11,TrainType.X)); + trainServiceList.add(createService(Place.B,Place.D,2,4,11,TrainType.Y)); + trainServiceList.add(createService(Place.C,Place.A,2,11,18,TrainType.W)); + trainServiceList.add(createService(Place.B,Place.D,2,11,18,TrainType.X)); + trainServiceList.add(createService(Place.D,Place.C,2,11,18,TrainType.Y)); + trainServiceList.add(createService(Place.D,Place.B,2,18,0,TrainType.X)); + trainServiceList.add(createService(Place.C,Place.D,2,18,0,TrainType.Y)); + trainServiceList.add(createService(Place.B,Place.A,2,18,0,TrainType.Z)); + trainServiceList.add(createService(Place.A,Place.C,3,4,11,TrainType.W)); + trainServiceList.add(createService(Place.D,Place.B,3,4,11,TrainType.X)); + trainServiceList.add(createService(Place.C,Place.A,3,4,11,TrainType.Y)); + trainServiceList.add(createService(Place.C,Place.D,3,11,18,TrainType.W)); + trainServiceList.add(createService(Place.B,Place.A,3,11,18,TrainType.X)); + trainServiceList.add(createService(Place.A,Place.C,3,11,18,TrainType.Y)); + trainServiceList.add(createService(Place.A,Place.B,3,18,0,TrainType.X)); + trainServiceList.add(createService(Place.C,Place.D,3,18,0,TrainType.Y)); + trainServiceList.add(createService(Place.A,Place.C,3,18,0,TrainType.Z)); + trainServiceList.add(createService(Place.D,Place.C,4,4,11,TrainType.W)); + trainServiceList.add(createService(Place.B,Place.A,4,4,11,TrainType.X)); + trainServiceList.add(createService(Place.D,Place.B,4,4,11,TrainType.Y)); + trainServiceList.add(createService(Place.C,Place.D,4,11,18,TrainType.W)); + trainServiceList.add(createService(Place.A,Place.B,4,11,18,TrainType.X)); + trainServiceList.add(createService(Place.B,Place.C,4,11,18,TrainType.Y)); + trainServiceList.add(createService(Place.B,Place.D,4,18,0,TrainType.X)); + trainServiceList.add(createService(Place.C,Place.A,4,18,0,TrainType.Y)); + trainServiceList.add(createService(Place.C,Place.B,4,18,0,TrainType.Z)); + trainServiceList.add(createService(Place.D,Place.B,5,4,11,TrainType.W)); + trainServiceList.add(createService(Place.B,Place.C,5,4,11,TrainType.X)); + trainServiceList.add(createService(Place.A,Place.D,5,4,11,TrainType.Y)); + trainServiceList.add(createService(Place.B,Place.A,5,11,18,TrainType.W)); + trainServiceList.add(createService(Place.C,Place.D,5,11,18,TrainType.X)); + trainServiceList.add(createService(Place.D,Place.C,5,11,18,TrainType.Y)); + trainServiceList.add(createService(Place.D,Place.A,5,18,0,TrainType.X)); + trainServiceList.add(createService(Place.C,Place.B,5,18,0,TrainType.Y)); + trainServiceList.add(createService(Place.B,Place.C,5,18,0,TrainType.Z)); + trainServiceList.add(createService(Place.A,Place.D,6,4,11,TrainType.W)); + trainServiceList.add(createService(Place.A,Place.C,6,4,11,TrainType.X)); + trainServiceList.add(createService(Place.B,Place.A,6,4,11,TrainType.Y)); + trainServiceList.add(createService(Place.D,Place.C,6,11,18,TrainType.W)); + trainServiceList.add(createService(Place.C,Place.D,6,11,18,TrainType.X)); + trainServiceList.add(createService(Place.A,Place.A,6,11,18,TrainType.Y)); + trainServiceList.add(createService(Place.D,Place.B,6,18,0,TrainType.X)); + trainServiceList.add(createService(Place.A,Place.C,6,18,0,TrainType.Y)); + trainServiceList.add(createService(Place.C,Place.D,6,18,0,TrainType.Z)); + trainServiceList.add(createService(Place.C,Place.A,7,4,11,TrainType.W)); + trainServiceList.add(createService(Place.B,Place.B,7,4,11,TrainType.X)); + trainServiceList.add(createService(Place.C,Place.C,7,4,11,TrainType.Y)); + trainServiceList.add(createService(Place.A,Place.D,7,11,18,TrainType.W)); + trainServiceList.add(createService(Place.B,Place.A,7,11,18,TrainType.X)); + trainServiceList.add(createService(Place.C,Place.B,7,11,18,TrainType.Y)); + trainServiceList.add(createService(Place.A,Place.C,7,18,0,TrainType.X)); + trainServiceList.add(createService(Place.B,Place.D,7,18,0,TrainType.Y)); + trainServiceList.add(createService(Place.D,Place.A,7,18,0,TrainType.Z)); + return trainServiceList; + } + + private static TrainService createService(Place startPlace, Place endPlace, int dayInWeek, int starthour, int endhour, TrainType trainType){ + serviceID = serviceID +1; + int id = serviceID; + + int serviceDayInMonth = 24 + dayInWeek; + + LocalDateTime startTime = LocalDateTime.of(LocalDate.of(2020,05,serviceDayInMonth), LocalTime.of(starthour,0)); + LocalDateTime endTime = LocalDateTime.of(LocalDate.of(2020,05,serviceDayInMonth), LocalTime.of(endhour,0)); + return new TrainService(id, startPlace, endPlace, startTime, endTime, trainType); + } + + public static Map getPopulation() { + return population; + } +} diff --git a/src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/DistanceBetweenPlaces.java b/src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/DistanceBetweenPlaces.java new file mode 100644 index 0000000..52815be --- /dev/null +++ b/src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/DistanceBetweenPlaces.java @@ -0,0 +1,85 @@ +package ch.fhnw.mbis.aci.nsgaii.sets.models; + + +import ch.fhnw.mbis.aci.nsgaii.sets.models.enums.Place; + +import java.security.InvalidParameterException; +import java.util.HashMap; +import java.util.Map; + +public class DistanceBetweenPlaces { + + private static Map distanceFromA = setupMapDistanceFromA(); + private static Map distanceFromB = setupMapDistanceFromB(); + private static Map distanceFromC = setupMapDistanceFromC(); + private static Map distanceFromD = setupMapDistanceFromD(); + + public static Integer caculateDistance(Place startPlace, Place endPlace){ + int distance = 0; + Map distanceFromStartPlace; + switch (startPlace){ + case A: + distanceFromStartPlace = distanceFromA; + break; + case B: + distanceFromStartPlace = distanceFromB; + break; + case C: + distanceFromStartPlace = distanceFromC; + break; + case D: + distanceFromStartPlace = distanceFromD; + break; + default: throw new InvalidParameterException(startPlace.toString()); + } + + return distanceFromStartPlace.get(endPlace); + } + + //TODO + private static Map setupMapDistance(Place startPlace, int x, int y, int z){ + Map distanceFromPlace = new HashMap<>(); + for (Place place : + Place.values()) { + if(place != startPlace){ + } + } + return distanceFromPlace; + } + + private static Map setupMapDistanceFromA(){ + Map distanceFromA = new HashMap<>(); + distanceFromA.put(Place.A,2); + distanceFromA.put(Place.B,2); + distanceFromA.put(Place.C,5); + distanceFromA.put(Place.D,10); + return distanceFromA; + } + + private static Map setupMapDistanceFromB(){ + Map distanceFromB = new HashMap<>(); + distanceFromB.put(Place.A,2); + distanceFromB.put(Place.B,0); + distanceFromB.put(Place.C,3); + distanceFromB.put(Place.D,7); + return distanceFromB; + } + + private static Map setupMapDistanceFromC(){ + Map distanceFromC = new HashMap<>(); + distanceFromC.put(Place.A,5); + distanceFromC.put(Place.B,3); + distanceFromC.put(Place.C,0); + distanceFromC.put(Place.D,5); + return distanceFromC; + } + + private static Map setupMapDistanceFromD(){ + Map distanceFromD = new HashMap<>(); + distanceFromD.put(Place.A,10); + distanceFromD.put(Place.B,7); + distanceFromD.put(Place.C,5); + distanceFromD.put(Place.D,0); + return distanceFromD; + } +} diff --git a/src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/TrainCrew.java b/src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/TrainCrew.java new file mode 100644 index 0000000..68c3725 --- /dev/null +++ b/src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/TrainCrew.java @@ -0,0 +1,74 @@ +package ch.fhnw.mbis.aci.nsgaii.sets.models; + +import ch.fhnw.mbis.aci.nsgaii.sets.models.enums.Place; +import ch.fhnw.mbis.aci.nsgaii.sets.models.enums.Shift; +import ch.fhnw.mbis.aci.nsgaii.sets.models.enums.TrainType; + +public class TrainCrew { + + private int ID; + private String name; + private Place placeOfResidence; + private Shift shiftPreference; + private boolean favoriseSameShiftPreference; + private boolean favroriseSameTrainPreference; + private TrainType favoriteTrainType; + + /** + * + * @param name + * @param placeOfResidence + * @param shiftPreference + * @param favoriseSameShiftPreference + * @param favroriseSameTrainPreference + */ + public TrainCrew(int ID, String name, Place placeOfResidence, Shift shiftPreference, boolean favoriseSameShiftPreference, boolean favroriseSameTrainPreference, TrainType favoriteTrainType) { + this.ID = ID; + this.name = name; + this.placeOfResidence = placeOfResidence; + this.shiftPreference = shiftPreference; + this.favoriseSameShiftPreference = favoriseSameShiftPreference; + this.favroriseSameTrainPreference = favroriseSameTrainPreference; + this.favoriteTrainType = favoriteTrainType; + } + + public int getID() { + return ID; + } + + public String getName() { + return name; + } + + public Place getPlaceOfResidence() { + return placeOfResidence; + } + + public Shift getShiftPreference() { + return shiftPreference; + } + + public boolean isFavoriseSameShiftPreference() { + return favoriseSameShiftPreference; + } + + public boolean isFavroriseSameTrainPreference() { + return favroriseSameTrainPreference; + } + + public TrainType getFavoriteTrainType() { + return favoriteTrainType; + } + + @Override + public String toString() { + return "TrainCrew{" + + "ID=" + ID + + ", name='" + name + '\'' + + ", placeOfResidence='" + placeOfResidence + '\'' + + ", shiftPreference=" + shiftPreference + + ", favoriseSameShiftPreference=" + favoriseSameShiftPreference + + ", favroriseSameTrainPreference=" + favroriseSameTrainPreference + + '}'; + } +} diff --git a/src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/TrainService.java b/src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/TrainService.java new file mode 100644 index 0000000..b085766 --- /dev/null +++ b/src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/TrainService.java @@ -0,0 +1,81 @@ +package ch.fhnw.mbis.aci.nsgaii.sets.models; + + +import ch.fhnw.mbis.aci.nsgaii.sets.models.enums.Place; +import ch.fhnw.mbis.aci.nsgaii.sets.models.enums.Shift; +import ch.fhnw.mbis.aci.nsgaii.sets.models.enums.TrainType; + +import java.time.LocalDateTime; + +public class TrainService { + + private static final int MORNING_SHIFT_START = 4; + private static final int DAY_SHIFT_START = 10; + private static final int NIGHT_SHIFT_START = 16; + + private int ID; + private Place startPlace; + private Place endPlace; + private LocalDateTime startTime; + private LocalDateTime endTime; + private TrainType trainType; + private Shift serviceShift; + + + public TrainService(int ID, Place startPlace, Place endPlace, LocalDateTime startTime, LocalDateTime endTime, TrainType trainType) { + this.ID = ID; + this.startPlace = startPlace; + this.endPlace = endPlace; + this.startTime = startTime; + this.endTime = endTime; + this.trainType = trainType; + + if (startTime.getHour() >= NIGHT_SHIFT_START){ + serviceShift = Shift.NIGHT_SHIFT; + } else if (startTime.getHour() >= DAY_SHIFT_START){ + serviceShift = Shift.DAY_SHIFT; + } else if (startTime.getHour() >= MORNING_SHIFT_START){ + serviceShift = Shift.MORNING_SHIFT; + } + } + + public Place getStartPlace() { + return startPlace; + } + + public Place getEndPlace() { + return endPlace; + } + + public LocalDateTime getStartTime() { + return startTime; + } + + public LocalDateTime getEndTime() { + return endTime; + } + + public TrainType getTrainType() { + return trainType; + } + + public Shift getServiceShift() { + return serviceShift; + } + + public int getID() { + return ID; + } + + @Override + public String toString() { + return "Service{" + + "startPlace=" + startPlace + + ", endPlace=" + endPlace + + ", startTime=" + startTime + + ", endTime=" + endTime + + ", train=" + trainType + + ", serviceShift=" + serviceShift + + '}'; + } +} diff --git a/src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/enums/Place.java b/src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/enums/Place.java new file mode 100644 index 0000000..8b1bd95 --- /dev/null +++ b/src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/enums/Place.java @@ -0,0 +1,5 @@ +package ch.fhnw.mbis.aci.nsgaii.sets.models.enums; + +public enum Place { + A,B,C,D; +} diff --git a/src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/enums/Shift.java b/src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/enums/Shift.java new file mode 100644 index 0000000..afeda97 --- /dev/null +++ b/src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/enums/Shift.java @@ -0,0 +1,5 @@ +package ch.fhnw.mbis.aci.nsgaii.sets.models.enums; + +public enum Shift { + MORNING_SHIFT,DAY_SHIFT, NIGHT_SHIFT; +} diff --git a/src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/enums/TrainCrewPopulationSize.java b/src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/enums/TrainCrewPopulationSize.java new file mode 100644 index 0000000..807d1ca --- /dev/null +++ b/src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/enums/TrainCrewPopulationSize.java @@ -0,0 +1,5 @@ +package ch.fhnw.mbis.aci.nsgaii.sets.models.enums; + +public enum TrainCrewPopulationSize { + TEN, FIFTEEN, TWENTY; +} diff --git a/src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/enums/TrainType.java b/src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/enums/TrainType.java new file mode 100644 index 0000000..e8b0128 --- /dev/null +++ b/src/main/java/ch/fhnw/mbis/aci/nsgaii/sets/models/enums/TrainType.java @@ -0,0 +1,5 @@ +package ch.fhnw.mbis.aci.nsgaii.sets.models.enums; + +public enum TrainType { + W, X, Y, Z; +} diff --git a/src/main/java/com/debacharya/nsgaii/Configuration.java b/src/main/java/com/debacharya/nsgaii/Configuration.java index ae41b63..78ac6a5 100644 --- a/src/main/java/com/debacharya/nsgaii/Configuration.java +++ b/src/main/java/com/debacharya/nsgaii/Configuration.java @@ -24,6 +24,10 @@ package com.debacharya.nsgaii; +import ch.fhnw.mbis.aci.nsgaii.plugin.SchedulingObjectiveProvider; +import ch.fhnw.mbis.aci.nsgaii.plugin.SchedulingPluginProvider; +import ch.fhnw.mbis.aci.nsgaii.population.TrainCrewSet; +import ch.fhnw.mbis.aci.nsgaii.population.TrainServiceSet; import com.debacharya.nsgaii.objectivefunction.AbstractObjectiveFunction; import com.debacharya.nsgaii.plugin.*; @@ -32,139 +36,43 @@ public class Configuration { public static final String CONFIGURATION_NOT_SETUP = "The NSGA-II configuration object is not setup properly!"; - public static final int DEFAULT_POPULATION_SIZE = 100; - public static final int DEFAULT_GENERATIONS = 25; - public static final int DEFAULT_CHROMOSOME_LENGTH = 20; + + public static final int HARD_CONSTRAINT_PENALTY = - 1000; + public static final float CHROMOSOME_MUTATION_PROBABILITY = 0.04f; public static List objectives; - public static String FITNESS_CALCULATOR_NULL = "The fitness calculation operation has not been setup. " + - "You need to set the AbstractObjectiveFunction#fitnessCalculator " + - "with an instance of FitnessCalculator!"; private int populationSize; private int generations; private int chromosomeLength; + private int geneLength; private PopulationProducer populationProducer; private ChildPopulationProducer childPopulationProducer; private GeneticCodeProducer geneticCodeProducer; private AbstractCrossover crossover; private AbstractMutation mutation; - private FitnessCalculator fitnessCalculator; - - public Configuration() { - this( - Configuration.DEFAULT_POPULATION_SIZE, - Configuration.DEFAULT_GENERATIONS, - Configuration.DEFAULT_CHROMOSOME_LENGTH - ); - } - public Configuration(int populationSize, int generations, int chromosomeLength) { - this( - populationSize, - generations, - chromosomeLength, - DefaultPluginProvider.defaultPopulationProducer(), - DefaultPluginProvider.defaultChildPopulationProducer(), - DefaultPluginProvider.defaultGeneticCodeProducer(), - ObjectiveProvider.provideSCHObjectives(chromosomeLength), - new UniformCrossover(CrossoverParticipantCreatorProvider.selectByBinaryTournamentSelection()), - new SinglePointMutation(), - false, - true, - true - ); - } - public Configuration(PopulationProducer populationProducer) { - this( - Configuration.DEFAULT_POPULATION_SIZE, - Configuration.DEFAULT_GENERATIONS, - Configuration.DEFAULT_CHROMOSOME_LENGTH, + public Configuration(int populationSize, + int nbGenerations, + PopulationProducer populationProducer, + AbstractCrossover crossover, + AbstractMutation mutation) { + this(populationSize, nbGenerations, populationProducer, - DefaultPluginProvider.defaultChildPopulationProducer(), - DefaultPluginProvider.defaultGeneticCodeProducer(), - ObjectiveProvider.provideSCHObjectives(Configuration.DEFAULT_CHROMOSOME_LENGTH), - new UniformCrossover(CrossoverParticipantCreatorProvider.selectByBinaryTournamentSelection()), - new SinglePointMutation(), - false, - true, - true - ); - } - - public Configuration(ChildPopulationProducer childPopulationProducer) { - this( - Configuration.DEFAULT_POPULATION_SIZE, - Configuration.DEFAULT_GENERATIONS, - Configuration.DEFAULT_CHROMOSOME_LENGTH, - DefaultPluginProvider.defaultPopulationProducer(), - childPopulationProducer, - DefaultPluginProvider.defaultGeneticCodeProducer(), - ObjectiveProvider.provideSCHObjectives(Configuration.DEFAULT_CHROMOSOME_LENGTH), - new UniformCrossover(CrossoverParticipantCreatorProvider.selectByBinaryTournamentSelection()), - new SinglePointMutation(), - false, - true, - true - ); - } - - public Configuration(GeneticCodeProducer geneticCodeProducer) { - this( - Configuration.DEFAULT_POPULATION_SIZE, - Configuration.DEFAULT_GENERATIONS, - Configuration.DEFAULT_CHROMOSOME_LENGTH, - DefaultPluginProvider.defaultPopulationProducer(), - DefaultPluginProvider.defaultChildPopulationProducer(), - geneticCodeProducer, - ObjectiveProvider.provideSCHObjectives(Configuration.DEFAULT_CHROMOSOME_LENGTH), - new UniformCrossover(CrossoverParticipantCreatorProvider.selectByBinaryTournamentSelection()), - new SinglePointMutation(), + SchedulingPluginProvider.defaultChildPopulationProducer(), + SchedulingPluginProvider.defaultGeneticCodeProducer(), + SchedulingObjectiveProvider.provideSCHObjectives(), + crossover, + mutation, false, true, true ); } - public Configuration(List objectives) { - this( - Configuration.DEFAULT_POPULATION_SIZE, - Configuration.DEFAULT_GENERATIONS, - Configuration.DEFAULT_CHROMOSOME_LENGTH, - DefaultPluginProvider.defaultPopulationProducer(), - DefaultPluginProvider.defaultChildPopulationProducer(), - DefaultPluginProvider.defaultGeneticCodeProducer(), - objectives, - new UniformCrossover(CrossoverParticipantCreatorProvider.selectByBinaryTournamentSelection()), - new SinglePointMutation(), - false, - true, - true - ); - } - - public Configuration(FitnessCalculator fitnessCalculator) { - this( - Configuration.DEFAULT_POPULATION_SIZE, - Configuration.DEFAULT_GENERATIONS, - Configuration.DEFAULT_CHROMOSOME_LENGTH, - DefaultPluginProvider.defaultPopulationProducer(), - DefaultPluginProvider.defaultChildPopulationProducer(), - DefaultPluginProvider.defaultGeneticCodeProducer(), - ObjectiveProvider.provideSCHObjectives(Configuration.DEFAULT_CHROMOSOME_LENGTH), - new UniformCrossover(CrossoverParticipantCreatorProvider.selectByBinaryTournamentSelection()), - new SinglePointMutation(), - false, - true, - true, - fitnessCalculator - ); - } - public Configuration(int populationSize, int generations, - int chromosomeLength, PopulationProducer populationProducer, ChildPopulationProducer childPopulationProducer, GeneticCodeProducer geneticCodeProducer, @@ -177,7 +85,8 @@ public Configuration(int populationSize, this.populationSize = populationSize; this.generations = generations; - this.chromosomeLength = chromosomeLength; + this.geneLength = calculateGeneSize() * TrainServiceSet.getPopulation().size(); + this.chromosomeLength = this.geneLength; this.populationProducer = populationProducer; this.childPopulationProducer = childPopulationProducer; this.geneticCodeProducer = geneticCodeProducer; @@ -190,35 +99,6 @@ public Configuration(int populationSize, Reporter.writeToDisk = writeToDisk; } - public Configuration(int populationSize, - int generations, - int chromosomeLength, - PopulationProducer populationProducer, - ChildPopulationProducer childPopulationProducer, - GeneticCodeProducer geneticCodeProducer, - List objectives, - AbstractCrossover crossover, - AbstractMutation mutation, - boolean silent, - boolean plotGraph, - boolean writeToDisk, - FitnessCalculator fitnessCalculator) { - - this(populationSize, - generations, - chromosomeLength, - populationProducer, - childPopulationProducer, - geneticCodeProducer, - objectives, - crossover, - mutation, - silent, - plotGraph, - writeToDisk); - this.fitnessCalculator = fitnessCalculator; - } - public int getPopulationSize() { return populationSize; } @@ -289,23 +169,6 @@ public void setMutation(AbstractMutation mutation) { this.mutation = mutation; } - public FitnessCalculator getFitnessCalculator() { - - if(this.fitnessCalculator == null) - this.fitnessCalculator = FitnessCalculatorProvider.normalizedGeneticCodeValue( - 0, - Math.pow(2, this.chromosomeLength) - 1, - 0, - 2 - ); - - return this.fitnessCalculator; - } - - public void setFitnessCalculator(FitnessCalculator fitnessCalculator) { - this.fitnessCalculator = fitnessCalculator; - } - public boolean isSetup() { return ( this.populationSize != 0 && @@ -347,7 +210,12 @@ public String toString() { "\nGenerations: " + this.generations + " [" + - (this.generations > 0 ? "valid" : "invalid") + + (this.generations > 0 ? "valid" : "invalid") + + "]" + + "\nGene Length: " + + this.geneLength + + " [" + + (this.geneLength > 0 ? "valid" : "invalid") + "]" + "\nChromosome Length: " + this.chromosomeLength + @@ -377,10 +245,14 @@ public String toString() { "\nMutation Operator: " + "[" + (this.mutation != null ? "provided" : "not provided") + - "]" + - "\nFitness Calculator: " + - "[" + - (this.fitnessCalculator != null ? "provided" : "not provided") + "]"; } + + public static int calculateGeneSize(){ + int geneSizeInBinary = 0; + geneSizeInBinary = Integer.toBinaryString(TrainCrewSet.getPopulation().size()).length(); + + return geneSizeInBinary; + } + } diff --git a/src/main/java/com/debacharya/nsgaii/NSGA2.java b/src/main/java/com/debacharya/nsgaii/NSGA2.java index b793195..8154264 100644 --- a/src/main/java/com/debacharya/nsgaii/NSGA2.java +++ b/src/main/java/com/debacharya/nsgaii/NSGA2.java @@ -44,14 +44,8 @@ public class NSGA2 { private final Configuration configuration; - /** - * creates an instance of `NSGA2` with a default configuration object that provides a default implementation of every plugin - * needed by the algorithm to run. While this constructor is not of much use to the user, but this helps run a proof-of-concept - * or the algorithm itself with all the default plugins filled in. - */ - public NSGA2() { - this.configuration = new Configuration(); - } + private List allParentGenerations = new ArrayList<>(); + /** * creates an instance of `NSGA2` by taking a configuration object as parameter. @@ -87,6 +81,8 @@ public Population run() { ) ); + allParentGenerations.add(parent); + Population child = this.preparePopulation( this.configuration.getChildPopulationProducer().produce( parent, @@ -108,6 +104,7 @@ public Population run() { ) ) ); + allParentGenerations.add(parent); child = this.preparePopulation( this.configuration.getChildPopulationProducer().produce( @@ -120,8 +117,7 @@ public Population run() { Reporter.reportGeneration(parent, child, generation); } - - Reporter.terminate(child); + Reporter.terminate(parent, allParentGenerations); return child; } diff --git a/src/main/java/com/debacharya/nsgaii/Reporter.java b/src/main/java/com/debacharya/nsgaii/Reporter.java index 99b72b7..126ff60 100644 --- a/src/main/java/com/debacharya/nsgaii/Reporter.java +++ b/src/main/java/com/debacharya/nsgaii/Reporter.java @@ -30,160 +30,204 @@ import com.debacharya.nsgaii.plugin.GraphPlot; import java.io.FileWriter; -import java.util.List; +import java.util.*; import java.util.concurrent.ThreadLocalRandom; public class Reporter { - private static final int fileHash = ThreadLocalRandom.current().nextInt(10000, 100000); - private static final StringBuilder writeContent = new StringBuilder(); - private static final GraphPlot allGenerationGraph = new GraphPlot("ALL GENERATIONS"); + private static final int fileHash = ThreadLocalRandom.current().nextInt(10000, 100000); + private static final StringBuilder writeContent = new StringBuilder(); + private static final GraphPlot allGenerationGraph = new GraphPlot("ALL GENERATIONS"); - public static boolean silent = false; - public static boolean plotGraph = true; - public static boolean plotCompiledGraphForEveryGeneration = true; - public static boolean plotGraphForEveryGeneration = false; - public static boolean writeToDisk = true; - public static String filename = "NSGA-II-report-" + Reporter.fileHash + ".txt"; + public static boolean silent = false; + public static boolean plotGraph = true; + public static boolean plotCompiledGraphForEveryGeneration = true; + public static boolean plotGraphForEveryGeneration = false; + public static boolean writeToDisk = true; + public static String filename = "NSGA-II-report-" + Reporter.fileHash + ".txt"; - public static void init(Configuration configuration) { + public static void init(Configuration configuration) { - if(silent && !writeToDisk) return; + if (silent && !writeToDisk) return; - p("\n[ " + java.time.LocalDateTime.now() + " ]"); - p("\n** To stop reporter from printing to console, change Reporter.silent to true or call Configuration.beSilent()"); - p( - "** Reporter is" + - (Reporter.writeToDisk ? "" : " not") + - " writing to disk" + - (Reporter.writeToDisk ? (" at " + Reporter.filename) : "") + - "." - ); + p("\n[ " + java.time.LocalDateTime.now() + " ]"); + p("\n** To stop reporter from printing to console, change Reporter.silent to true or call Configuration.beSilent()"); + p( + "** Reporter is" + + (Reporter.writeToDisk ? "" : " not") + + " writing to disk" + + (Reporter.writeToDisk ? (" at " + Reporter.filename) : "") + + "." + ); - if(plotGraph) - p("** Plotting pareto front."); + if (plotGraph) + p("** Plotting pareto front."); - if(plotCompiledGraphForEveryGeneration) - p("** Plotting compiled graph for all generations"); + if (plotCompiledGraphForEveryGeneration) + p("** Plotting compiled graph for all generations"); - if(plotGraphForEveryGeneration) - p("** Plotting separate graph for every generation. !! Note that this might cause performance issues!"); + if (plotGraphForEveryGeneration) + p("** Plotting separate graph for every generation. !! Note that this might cause performance issues!"); - p("** To change this behavior, change Reporter.writeToDisk or call Cofiguration.writeToDisk(boolean)."); - p("** To change location and file name of where to save the file, change Reporter.filename.\n"); - p("------------------------------------------------"); - p(" NON-DOMINATED SORTING GENETIC ALGORITHM-II "); - p("------------------------------------------------"); - p(configuration.toString()); - } + p("** To change this behavior, change Reporter.writeToDisk or call Cofiguration.writeToDisk(boolean)."); + p("** To change location and file name of where to save the file, change Reporter.filename.\n"); + p("------------------------------------------------"); + p(" NON-DOMINATED SORTING GENETIC ALGORITHM-II "); + p("------------------------------------------------"); + p(configuration.toString()); + } - public static void reportGeneration(Population parent, Population child, int generation) { + public static void reportGeneration(Population parent, Population child, int generation) { - if(plotGraph && plotCompiledGraphForEveryGeneration) - Reporter.allGenerationGraph.addData(child, "gen. " + generation); + if (plotGraph && plotCompiledGraphForEveryGeneration) + Reporter.allGenerationGraph.addData(child, "gen. " + generation); - if(plotGraph && plotGraphForEveryGeneration) - Reporter.plot2DPopulation(child, "GENERATION " + generation); + if (plotGraph && plotGraphForEveryGeneration) + Reporter.plot2DPopulation(child, "GENERATION " + generation); - if(silent && !writeToDisk) return; + if (silent && !writeToDisk) return; - p("\n++++++++++++++++ GENERATION: " + generation + " ++++++++++++++++\n"); - p("[ START ]\n"); - p("Parent Population: " + parent.size()); - p("Child Population: " + child.size()); - p("\n======== PARENT ========\n"); - Reporter.reportPopulation(parent); - p("\n======== CHILD ========\n"); - Reporter.reportPopulation(child); - p("\n[ END ]"); - } + p("\n++++++++++++++++ GENERATION: " + generation + " ++++++++++++++++\n"); + p("[ START ]\n"); + p("Parent Population: " + parent.size()); + p("Child Population: " + child.size()); + p("\n======== PARENT ========\n"); + Reporter.reportPopulation(parent); + p("\n======== CHILD ========\n"); + Reporter.reportPopulation(child); + p("\n[ END ]"); + } - public static void reportPopulation(Population population) { + public static void reportPopulation(Population population) { - if(silent && !writeToDisk) return; + if (silent && !writeToDisk) return; - for(Chromosome chromosome : population.getPopulace()) - Reporter.reportChromosome(chromosome); - } + for (Chromosome chromosome : population.getPopulace()) + Reporter.reportChromosome(chromosome); + } - public static void reportChromosome(Chromosome chromosome) { + public static void reportChromosome(Chromosome chromosome) { - if(silent && !writeToDisk) return; + if (silent && !writeToDisk) return; - Reporter.reportGeneticCode(chromosome.getGeneticCode()); + Reporter.reportGeneticCode(chromosome.getChromosomeAlleles()); - p(">> " + chromosome.toString()); - } + p(">> " + chromosome.toString()); + } - public static void reportGeneticCode(List geneticCode) { + public static void reportGeneticCode(List geneticCode) { - if(silent && !writeToDisk) return; + if (silent && !writeToDisk) return; - StringBuilder code = new StringBuilder("# [ "); + StringBuilder code = new StringBuilder("# [ "); - for(AbstractAllele allele : geneticCode) - code.append(allele.toString()).append(" "); + for (AbstractAllele allele : geneticCode) + code.append(allele.toString()).append(" "); - p(code.append("]").toString()); - } + p(code.append("]").toString()); + } - public static void plot2DPopulation(Population population, String key) { + public static void plot2DPopulation(Population population, String key) { - if(!GraphPlot.isCompatible()) return; + if (!GraphPlot.isCompatible()) return; - GraphPlot graph = new GraphPlot(key); + GraphPlot graph = new GraphPlot(key); - graph.addData(population); - graph.plot(); - } + graph.addData(population); + graph.plot(); + } - public static void plot2DParetoFront(Population population) { - Reporter.plot2DPopulation(population, "PARETO FRONT"); - } + public static void plot2DParetoFront(Population finalParent) { + Population paretoParent = sortParetoFrontPopulation(finalParent); + p("---PARETO FRONT---"); + reportPopulation(paretoParent); + Reporter.plot2DPopulation(paretoParent, "PARETO FRONT"); + } - public static void plot2DGraphForAllGenerations() { + public static void plot2DGraphForAllGenerations() { - if(!GraphPlot.isCompatible()) return; + if (!GraphPlot.isCompatible()) return; - Reporter.allGenerationGraph.plot(); - } + Reporter.allGenerationGraph.plot(); + } - public static void plotGraphs(Population finalChild) { + public static void plotGraphs(Population finalParent, List allGenerations) { - if(!GraphPlot.isCompatible()) return; - if(plotGraph) Reporter.plot2DParetoFront(finalChild); - if(plotCompiledGraphForEveryGeneration) Reporter.plot2DGraphForAllGenerations(); - } + if (!GraphPlot.isCompatible()) return; + if (plotGraph) Reporter.plot2DParetoFront(finalParent); + if (plotCompiledGraphForEveryGeneration) Reporter.plot2DGraphForAllGenerations(); + } - public static void terminate(Population finalChild) { + public static void terminate(Population finalChild, List allParentGenerations) { - Reporter.plotGraphs(finalChild); + Reporter.plotGraphs(finalChild, allParentGenerations); - if(silent && !writeToDisk) return; + if (silent && !writeToDisk) return; - p("------------------------------------------------"); - p("NSGA-II ENDED SUCCESSFULLY\n"); + p("------------------------------------------------"); + p("NSGA-II ENDED SUCCESSFULLY\n"); - if(writeToDisk) { - Reporter.writeToFile(); - p("** Output saved at " + filename + "\n"); - } - } + if (writeToDisk) { + Reporter.writeToFile(); + p("** Output saved at " + filename + "\n"); + } + } - private static void writeToFile() { + private static void writeToFile() { - try { - FileWriter writer = new FileWriter(Reporter.filename); + try { + FileWriter writer = new FileWriter(Reporter.filename); - writer.write(Reporter.writeContent.toString()); - writer.close(); - } catch (Exception e) { - System.out.println("\n!!! COULD NOT WRITE FILE TO DISK!\n\n"); - } - } + writer.write(Reporter.writeContent.toString()); + writer.close(); + } catch (Exception e) { + System.out.println("\n!!! COULD NOT WRITE FILE TO DISK!\n\n"); + } + } - private static void p(String s) { - if(writeToDisk) Reporter.writeContent.append(s).append(System.lineSeparator()); - if(!silent) System.out.println(s); - } + private static void p(String s) { + if (writeToDisk) Reporter.writeContent.append(s).append(System.lineSeparator()); + if (!silent) System.out.println(s); + } + + + private static Population sortParetoFrontPopulation(Population finalParent) { + SortedMap generationChromosomes = Service.returnSortedXAxisChromosomes(finalParent.getPopulace()); + SortedMap paretoChromosomes = new TreeMap<>(); + + Chromosome worstSolution = generationChromosomes.get(generationChromosomes.lastKey()); + paretoChromosomes.put(worstSolution.getObjectiveValues().get(0), worstSolution); + + List generationChromosomeList = new ArrayList(generationChromosomes.values()); + + for (int i = generationChromosomes.size()-1 ; i >= 0 ; i--) { + Chromosome chromosome = generationChromosomeList.get(i); + if (dominatesInAll2Dimensions(chromosome, paretoChromosomes)) { + List objectiveValues = chromosome.getObjectiveValues(); + double xValueChromosome = objectiveValues.get(0); + paretoChromosomes.put(xValueChromosome, chromosome); + + } + } + + return new Population(new ArrayList<>(paretoChromosomes.values())); + } + + + private static boolean dominatesInAll2Dimensions(Chromosome chromosomeToCheck, SortedMap paretoChromosomes) { + boolean isDominatingInAllDimensions = false; + List objectiveValues = chromosomeToCheck.getObjectiveValues(); + double xValueChromosomeToCheck = objectiveValues.get(0); + double yValueChromosomeToCheck = objectiveValues.get(1); + + double highestXValueInCurrentParetoFront = paretoChromosomes.firstKey(); + + Chromosome dominatedChromosome = paretoChromosomes.get(highestXValueInCurrentParetoFront); + double yValueDominatedChromosome = dominatedChromosome.getObjectiveValues().get(1); + if (yValueChromosomeToCheck > yValueDominatedChromosome) { + isDominatingInAllDimensions = true; + } + + return isDominatingInAllDimensions; + } } diff --git a/src/main/java/com/debacharya/nsgaii/Service.java b/src/main/java/com/debacharya/nsgaii/Service.java index 8411075..9e5c556 100644 --- a/src/main/java/com/debacharya/nsgaii/Service.java +++ b/src/main/java/com/debacharya/nsgaii/Service.java @@ -29,6 +29,8 @@ import com.debacharya.nsgaii.datastructure.Population; import java.util.List; +import java.util.SortedMap; +import java.util.TreeMap; import java.util.concurrent.ThreadLocalRandom; public class Service { @@ -199,6 +201,25 @@ public static double roundOff(double value, double decimalPlace) { return (Math.round(value * decimalPlace) / decimalPlace); } + public static SortedMap returnSortedXAxisChromosomes(List chromosomes){ + SortedMap sortedChromosomes = new TreeMap<>(); + for (Chromosome newChromosome : chromosomes) { + double xAxisValueNewChromosome = newChromosome.getObjectiveValues().get(0); + if(sortedChromosomes.containsKey(xAxisValueNewChromosome)){ + double yAxisValueNewChromosome = newChromosome.getObjectiveValues().get(1); + double yAxisValueExistingChromosome = sortedChromosomes.get(xAxisValueNewChromosome).getObjectiveValues().get(1); + if(yAxisValueNewChromosome > yAxisValueExistingChromosome){ + sortedChromosomes.put(xAxisValueNewChromosome, newChromosome); + } + } else { + sortedChromosomes.put(xAxisValueNewChromosome, newChromosome); + } + + } + + return sortedChromosomes; + } + private static void swapForRank(List populace, int firstIndex, int secondIndex) { Chromosome temporary = populace.get(firstIndex); diff --git a/src/main/java/com/debacharya/nsgaii/datastructure/AbstractAllele.java b/src/main/java/com/debacharya/nsgaii/datastructure/AbstractAllele.java index 7587d65..c3062fb 100644 --- a/src/main/java/com/debacharya/nsgaii/datastructure/AbstractAllele.java +++ b/src/main/java/com/debacharya/nsgaii/datastructure/AbstractAllele.java @@ -26,12 +26,12 @@ public abstract class AbstractAllele { - protected final Object gene; + protected final Object allele; - public AbstractAllele(Object gene) { - this.gene = gene; + public AbstractAllele(Object allele) { + this.allele = allele; } - public abstract Object getGene(); + public abstract Object getAllele(); public abstract AbstractAllele getCopy(); } diff --git a/src/main/java/com/debacharya/nsgaii/datastructure/BooleanAllele.java b/src/main/java/com/debacharya/nsgaii/datastructure/BooleanAllele.java index 174654f..35da7d2 100644 --- a/src/main/java/com/debacharya/nsgaii/datastructure/BooleanAllele.java +++ b/src/main/java/com/debacharya/nsgaii/datastructure/BooleanAllele.java @@ -31,17 +31,17 @@ public BooleanAllele(boolean gene) { } @Override - public Boolean getGene() { - return (Boolean) this.gene; + public Boolean getAllele() { + return (Boolean) this.allele; } @Override public AbstractAllele getCopy() { - return new BooleanAllele((Boolean) this.gene); + return new BooleanAllele((Boolean) this.allele); } @Override public String toString() { - return ((Boolean) this.gene ? "1" : "0"); + return ((Boolean) this.allele ? "1" : "0"); } } diff --git a/src/main/java/com/debacharya/nsgaii/datastructure/Chromosome.java b/src/main/java/com/debacharya/nsgaii/datastructure/Chromosome.java index 715fbc3..8988dfe 100644 --- a/src/main/java/com/debacharya/nsgaii/datastructure/Chromosome.java +++ b/src/main/java/com/debacharya/nsgaii/datastructure/Chromosome.java @@ -24,6 +24,7 @@ package com.debacharya.nsgaii.datastructure; +import com.debacharya.nsgaii.Configuration; import com.debacharya.nsgaii.Service; import java.util.ArrayList; @@ -33,27 +34,27 @@ public class Chromosome { private final List objectiveValues; private final List normalizedObjectiveValues; - private final List geneticCode; + private final List chromosomeAlleles; private List dominatedChromosomes; private double crowdingDistance = 0; private int dominatedCount = 0; private double fitness = Double.MIN_VALUE; private int rank = -1; - public Chromosome(List geneticCode) { + public Chromosome(List chromosomeAlleles) { - this.geneticCode = new ArrayList<>(); + this.chromosomeAlleles = new ArrayList<>(); this.objectiveValues = new ArrayList<>(); this.normalizedObjectiveValues = new ArrayList<>(); this.dominatedChromosomes = new ArrayList<>(); - for(AbstractAllele allele : geneticCode) - this.geneticCode.add(allele.getCopy()); + for(AbstractAllele allele : chromosomeAlleles) + this.chromosomeAlleles.add(allele.getCopy()); } public Chromosome(Chromosome chromosome) { - this(chromosome.geneticCode); + this(chromosome.chromosomeAlleles); for(int i = 0; i < chromosome.objectiveValues.size(); i++) this.objectiveValues.add(i, chromosome.objectiveValues.get(i)); @@ -102,8 +103,8 @@ public void setNormalizedObjectiveValue(int index, double value) { else this.normalizedObjectiveValues.set(index, value); } - public List getGeneticCode() { - return geneticCode; + public List getChromosomeAlleles() { + return chromosomeAlleles; } public double getCrowdingDistance() { @@ -139,7 +140,7 @@ public void setRank(int rank) { } public int getLength() { - return this.geneticCode.size(); + return this.chromosomeAlleles.size(); } public Chromosome getCopy() { @@ -152,6 +153,22 @@ public void reset() { this.dominatedChromosomes = new ArrayList<>(); } + public List retrieveChromosomeGenes(){ + List genes = new ArrayList<>(); + + StringBuilder gene = new StringBuilder(); + for (int i = 1; i <= this.chromosomeAlleles.size(); i++) { + AbstractAllele allele = this.chromosomeAlleles.get(i-1); + gene.append(allele.toString()); + + if((i % Configuration.calculateGeneSize()) == 0){ + genes.add(gene.toString()); + gene.delete(0, gene.length()); + } + } + return genes; + } + @Override public String toString() { diff --git a/src/main/java/com/debacharya/nsgaii/plugin/AbstractMutation.java b/src/main/java/com/debacharya/nsgaii/plugin/AbstractMutation.java index 08ccafb..960347d 100644 --- a/src/main/java/com/debacharya/nsgaii/plugin/AbstractMutation.java +++ b/src/main/java/com/debacharya/nsgaii/plugin/AbstractMutation.java @@ -28,9 +28,11 @@ import java.util.concurrent.ThreadLocalRandom; +import static com.debacharya.nsgaii.Configuration.CHROMOSOME_MUTATION_PROBABILITY; + public abstract class AbstractMutation { - protected float mutationProbability = 0.03f; + protected float mutationProbability = CHROMOSOME_MUTATION_PROBABILITY; public AbstractMutation() {} diff --git a/src/main/java/com/debacharya/nsgaii/plugin/FitnessCalculatorProvider.java b/src/main/java/com/debacharya/nsgaii/plugin/FitnessCalculatorProvider.java index 33e842a..be283a5 100644 --- a/src/main/java/com/debacharya/nsgaii/plugin/FitnessCalculatorProvider.java +++ b/src/main/java/com/debacharya/nsgaii/plugin/FitnessCalculatorProvider.java @@ -38,11 +38,11 @@ public class FitnessCalculatorProvider { public static FitnessCalculator normalizedGeneticCodeValue(double actualMin, double actualMax, double normalizedMin, double normalizedMax) { return chromosome -> { - if(!(chromosome.getGeneticCode().get(0) instanceof BooleanAllele)) + if(!(chromosome.getChromosomeAlleles().get(0) instanceof BooleanAllele)) throw new UnsupportedOperationException(FitnessCalculatorProvider.NON_BOOLEAN_ALLELE_UNSUPPORTED); return Service.getNormalizedGeneticCodeValue( - chromosome.getGeneticCode().stream().map(e -> (BooleanAllele) e).collect(Collectors.toList()), + chromosome.getChromosomeAlleles().stream().map(e -> (BooleanAllele) e).collect(Collectors.toList()), actualMin, actualMax, normalizedMin, diff --git a/src/main/java/com/debacharya/nsgaii/plugin/SinglePointMutation.java b/src/main/java/com/debacharya/nsgaii/plugin/SinglePointMutation.java index c5099ef..6f41f07 100644 --- a/src/main/java/com/debacharya/nsgaii/plugin/SinglePointMutation.java +++ b/src/main/java/com/debacharya/nsgaii/plugin/SinglePointMutation.java @@ -24,6 +24,8 @@ package com.debacharya.nsgaii.plugin; +import ch.fhnw.mbis.aci.nsgaii.population.TrainCrewSet; +import com.debacharya.nsgaii.Configuration; import com.debacharya.nsgaii.datastructure.AbstractAllele; import com.debacharya.nsgaii.datastructure.BooleanAllele; import com.debacharya.nsgaii.datastructure.Chromosome; @@ -48,21 +50,46 @@ public SinglePointMutation(float mutationProbability) { @Override public Chromosome perform(Chromosome chromosome) { - for(AbstractAllele allele : chromosome.getGeneticCode()) - if(!(allele instanceof BooleanAllele)) + for(AbstractAllele allele : chromosome.getChromosomeAlleles()){ + if(!(allele instanceof BooleanAllele)){ throw new UnsupportedOperationException(SinglePointMutation.BOOLEAN_ALLELE_INSTANCE_ERROR); + } + } - List booleanGeneticCode = new ArrayList<>(); + List newValidGeneList = new ArrayList<>(); + List tempGeneList = new ArrayList<>(); + int geneSize = Configuration.calculateGeneSize(); + int i = 0; + while(newValidGeneList.size() < chromosome.getChromosomeAlleles().size()){ + if (tempGeneList.size() >= geneSize){ + if(checkIfIdValid(tempGeneList)){ + newValidGeneList.addAll(tempGeneList); + tempGeneList.clear(); + } else { + //Restart gene + i = i - geneSize; + tempGeneList.clear(); + } + } else{ + AbstractAllele booleanAllele = chromosome.getChromosomeAlleles().get(i); + tempGeneList.add(new BooleanAllele( + this.shouldPerformMutation() ? + !((BooleanAllele) booleanAllele).getAllele() : + ((BooleanAllele) booleanAllele).getAllele() + )); + i++; + } + } - for(int i = 0; i < chromosome.getLength(); i++) - booleanGeneticCode.add( - i, new BooleanAllele( - this.shouldPerformMutation() ? - !((BooleanAllele) chromosome.getGeneticCode().get(i)).getGene() : - ((BooleanAllele) chromosome.getGeneticCode().get(i)).getGene() - ) - ); + return new Chromosome(new ArrayList<>(newValidGeneList)); + } - return new Chromosome(new ArrayList<>(booleanGeneticCode)); + private boolean checkIfIdValid(List abstractAlleleList){ + String gene = ""; + for (AbstractAllele abstractAllele + : abstractAlleleList) { + gene = gene.concat(abstractAllele.toString()); + } + return TrainCrewSet.getPopulation().get(Integer.parseInt(gene,2)) != null; } } diff --git a/src/main/java/com/debacharya/nsgaii/plugin/UniformCrossover.java b/src/main/java/com/debacharya/nsgaii/plugin/UniformCrossover.java index ef47fcd..6ee3e61 100644 --- a/src/main/java/com/debacharya/nsgaii/plugin/UniformCrossover.java +++ b/src/main/java/com/debacharya/nsgaii/plugin/UniformCrossover.java @@ -65,8 +65,8 @@ private Chromosome prepareChildChromosome(Chromosome chromosome1, Chromosome chr for(int i = 0; i < chromosome1.getLength(); i++) switch (Math.random() <= 0.5 ? 1 : 2) { - case 1: geneticCode.add(i, chromosome1.getGeneticCode().get(i).getCopy()); break; - case 2: geneticCode.add(i, chromosome2.getGeneticCode().get(i).getCopy()); break; + case 1: geneticCode.add(i, chromosome1.getChromosomeAlleles().get(i).getCopy()); break; + case 2: geneticCode.add(i, chromosome2.getChromosomeAlleles().get(i).getCopy()); break; } return new Chromosome(geneticCode); diff --git a/src/test/java/NSGA2Test.java b/src/test/java/NSGA2Test.java deleted file mode 100644 index 4a2a052..0000000 --- a/src/test/java/NSGA2Test.java +++ /dev/null @@ -1,11 +0,0 @@ -import com.debacharya.nsgaii.NSGA2; -import com.debacharya.nsgaii.datastructure.Population; - -public class NSGA2Test { - - public static void main(String[] args) { - - NSGA2 nsga2 = new NSGA2(); - Population paretoFront = nsga2.run(); - } -} diff --git a/v1/README.md b/v1/README.md deleted file mode 100644 index 13e21b2..0000000 --- a/v1/README.md +++ /dev/null @@ -1,74 +0,0 @@ -# NSGA-II -**an NSGA-II implementation using Java** -**This is v1 implementation. For latest v2 reference implementation, visit [here](https://github.com/onclave/NSGA-II)** - -**_Original Authors of the Paper_: [Kalyanmoy Deb](http://www.egr.msu.edu/~kdeb/), [Amrit Pratap](https://scholar.google.com/citations?user=E8wJ7G8AAAAJ&hl=en), [Sameer Agarwal](http://ieeexplore.ieee.org/search/searchresult.jsp?searchWithin=%22Authors%22:.QT.S.%20Agarwal.QT.&newsearch=true), [T. Meyarivan](http://ieeexplore.ieee.org/search/searchresult.jsp?searchWithin=%22Authors%22:.QT.T.%20Meyarivan.QT.&newsearch=true)** - -_links to original contents:_ - -* [NSGA-II paper: PDF](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.542.385&rep=rep1&type=pdf) -* [NSGA-II code implementation by original authors at **KanGAL**](https://www.iitk.ac.in/kangal/codes.shtml) - -_**note**: this implementation of NSGA-II algorithm is in pure reference to the original published paper. This is not an effort to convert the originally implemented C code in Java. The original C code by the authors has not be referred to while writing this implementation._ - -_**Dependency: Java(>=1.8), JFreeChart(1.0.13), JCommon(1.0.24)**_ - -### How It Works - -This code has 6 packages: - -* _io.onclave.nsga.ii.Interface_ : This package has the **IObjectiveFunction** interface that must be implemented by any Objective Function class in package _io.onclave.nsga.ii.objectivefunction_ -* _io.onclave.nsga.ii.algorithm_ : This package contains the main algorithm class, **Algorithm.java** that must be run during execution. -* _io.onclave.nsga.ii.api_ : This package contains - * **GraphPlot.java** which currently supports 2D graph plotting using [JFreeChart](http://www.jfree.org/jfreechart/) library, - * the **Reporter.java** that is used to print to console various states of the dataset during runtime, - * the **Service.java** that contains most of the essential functions specific to NSGA-II and - * **Synthesis.java** that is essential for genetic algorithm specific functions. -* _io.onclave.nsga.ii.configuration_ : This package contains the **Configuration** class which prepares and configures various modules before the algorithm is executed. -* _io.onclave.nsga.ii.datastructure_ : This package contains the datastructures required for NSGA-II. An Object Oriented approached has been used. - * **Allele.java** - * **Chromosome.java** - * **ParetoObject.java** - * **Population.java** -* _io.onclave.nsga.ii.objectivefunction_ : contains various objective function specific classes that can be used with NSGA-II. All objective function classes must implement **IObjectiveInterface** - -This implementation of the algorithm works in accordance to the original algorithm proposed in the paper. - -Right now, the algorithm does not implement the method **Service#nonDominatedPopulationSort** but calls the method in the actual algorithm, so that if required, this method may be implemented as required. - -The sorting algorithm used during **crowding distance assignment** in **Service#crowdingDistanceAssignment** is **randomized quick sort** implemented in **Service#RandomizedQuickSort**. This gives the optimal average case time complexity. If this is required to be changed, change the code at **Service#sort** to call your own sorting algorithm as required. - -### Configurations - -To change various general configurations within the code, please refer to **Configuration** class and change the required values. - -* **POPULATION_SIZE** : population size at each generation. -* **GENERATIONS** : number of generations the algorithm should run. -* **CHROMOSOME_LENGTH** : chromosome length in bits. Default is 8. Please note that, changing this value may throw a _NumberFormatException_ since the 8 bit binary string is directly converted to a corresponding double value. A relatively large binary string may throw exceptions during conversions. The 8 bit binary string keeps the value within 0 to 255. -* **CROSSOVER_PROBABILITY** -* **MUTATION_PROBABILITY** -* The binary strings generated as genetic code for each chromosome is converted to the corresponding double value which is the fitness of the chromosomes. The default fitness value is always between 0 and 255 since the default chromosome length is 8. This value is then normalized using min-max normalisation between 0 to 2. Change the variables **ACTUAL_MIN**, **ACTUAL_MAX**, **NORMALIZED_MIN**, **NORMALIZED_MAX** for other values. -* To change the default way the fitness value is calculated for each chromosome, implement **Service#calculateFitness** method according to requirement. This method would be automatically called during call on **Chromosome#setGeneticCode** for every chromosome. To change this behaviour, tweak the **Chromosome#setGeneticCode** method. -* The **Configuration#buildObjectives** method is called at the very beginning of the algorithm. This method should configure the **Configuration#objectives** property before the algorithm can execute anything. All the objectives must be configured and added to this list at the very beginning. Refer to **Configuration#buildObjectives** method for basic implementation. - -To change various synthesis configurations within the code, please refer to **Synthesis** class and change the required methods. - -* a binary tournament selection procedure is used to select a chromosome from parent to child at **Synthesis#binaryTournamentSelection** -* a uniform crossover is used where the chromosomes are broken from the middle. To change this behavious, tweak **Synthesis#crossover** -* a bit flip is used for mutation -* **Synthesis#synthesizePopulation** method is used to generate a new population -* **Synthesis#synthesizeChild** method generates a new child population from parent population -* **Synthesis#synthesizeGeneticCode** method generates the genetic code of each chromosome - -To change various plotting configurations within the code, please refer to **GraphPlot** class and change the required values. - -* **GRAPH_TITLE** -* **KEY** -* **DIMENSION_X** : x-axis dimension of generated graph -* **DIMENSION_Y** : y-axis dimension of generated graph -* **COLOR** -* **STROKE_THICKNESS** - -To set these values using setters, call the setter methods prior to calling the **GraphPlot#configurePlotter** - -The **Reporter#render2DGraph** works only if number of objectives is 2. It uses the **Configuration#getObjectives** method to check this condition. Implement your own renderer method to render various other graphs. \ No newline at end of file diff --git a/v1/README.v1.md b/v1/README.v1.md deleted file mode 100644 index 25cd290..0000000 --- a/v1/README.v1.md +++ /dev/null @@ -1,75 +0,0 @@ -# NSGA-II -**an NSGA-II implementation using Java.** - -**This is v1 implementation. For latest v2 reference implementation, visit [here](https://github.com/onclave/NSGA-II)** - -**_Original Authors of the Paper_: [Kalyanmoy Deb](http://www.egr.msu.edu/~kdeb/), [Amrit Pratap](https://scholar.google.com/citations?user=E8wJ7G8AAAAJ&hl=en), [Sameer Agarwal](http://ieeexplore.ieee.org/search/searchresult.jsp?searchWithin=%22Authors%22:.QT.S.%20Agarwal.QT.&newsearch=true), [T. Meyarivan](http://ieeexplore.ieee.org/search/searchresult.jsp?searchWithin=%22Authors%22:.QT.T.%20Meyarivan.QT.&newsearch=true)** - -_links to original contents:_ - -* [NSGA-II paper: PDF](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.542.385&rep=rep1&type=pdf) -* [NSGA-II code implementation by original authors at **KanGAL**](https://www.iitk.ac.in/kangal/codes.shtml) - -_**note**: this implementation of NSGA-II algorithm is in pure reference to the original published paper. This is not an effort to convert the originally implemented C code in Java. The original C code by the authors has not be referred to while writing this implementation._ - -_**Dependency: Java(>=1.8), JFreeChart(1.0.13), JCommon(1.0.24)**_ - -### How It Works - -This code has 6 packages: - -* _io.onclave.nsga.ii.Interface_ : This package has the **IObjectiveFunction** interface that must be implemented by any Objective Function class in package _io.onclave.nsga.ii.objectivefunction_ -* _io.onclave.nsga.ii.algorithm_ : This package contains the main algorithm class, **Algorithm.java** that must be run during execution. -* _io.onclave.nsga.ii.api_ : This package contains - * **GraphPlot.java** which currently supports 2D graph plotting using [JFreeChart](http://www.jfree.org/jfreechart/) library, - * the **Reporter.java** that is used to print to console various states of the dataset during runtime, - * the **Service.java** that contains most of the essential functions specific to NSGA-II and - * **Synthesis.java** that is essential for genetic algorithm specific functions. -* _io.onclave.nsga.ii.configuration_ : This package contains the **Configuration** class which prepares and configures various modules before the algorithm is executed. -* _io.onclave.nsga.ii.datastructure_ : This package contains the datastructures required for NSGA-II. An Object Oriented approached has been used. - * **Allele.java** - * **Chromosome.java** - * **ParetoObject.java** - * **Population.java** -* _io.onclave.nsga.ii.objectivefunction_ : contains various objective function specific classes that can be used with NSGA-II. All objective function classes must implement **IObjectiveInterface** - -This implementation of the algorithm works in accordance to the original algorithm proposed in the paper. - -Right now, the algorithm does not implement the method **Service#nonDominatedPopulationSort** but calls the method in the actual algorithm, so that if required, this method may be implemented as required. - -The sorting algorithm used during **crowding distance assignment** in **Service#crowdingDistanceAssignment** is **randomized quick sort** implemented in **Service#RandomizedQuickSort**. This gives the optimal average case time complexity. If this is required to be changed, change the code at **Service#sort** to call your own sorting algorithm as required. - -### Configurations - -To change various general configurations within the code, please refer to **Configuration** class and change the required values. - -* **POPULATION_SIZE** : population size at each generation. -* **GENERATIONS** : number of generations the algorithm should run. -* **CHROMOSOME_LENGTH** : chromosome length in bits. Default is 8. Please note that, changing this value may throw a _NumberFormatException_ since the 8 bit binary string is directly converted to a corresponding double value. A relatively large binary string may throw exceptions during conversions. The 8 bit binary string keeps the value within 0 to 255. -* **CROSSOVER_PROBABILITY** -* **MUTATION_PROBABILITY** -* The binary strings generated as genetic code for each chromosome is converted to the corresponding double value which is the fitness of the chromosomes. The default fitness value is always between 0 and 255 since the default chromosome length is 8. This value is then normalized using min-max normalisation between 0 to 2. Change the variables **ACTUAL_MIN**, **ACTUAL_MAX**, **NORMALIZED_MIN**, **NORMALIZED_MAX** for other values. -* To change the default way the fitness value is calculated for each chromosome, implement **Service#calculateFitness** method according to requirement. This method would be automatically called during call on **Chromosome#setGeneticCode** for every chromosome. To change this behaviour, tweak the **Chromosome#setGeneticCode** method. -* The **Configuration#buildObjectives** method is called at the very beginning of the algorithm. This method should configure the **Configuration#objectives** property before the algorithm can execute anything. All the objectives must be configured and added to this list at the very beginning. Refer to **Configuration#buildObjectives** method for basic implementation. - -To change various synthesis configurations within the code, please refer to **Synthesis** class and change the required methods. - -* a binary tournament selection procedure is used to select a chromosome from parent to child at **Synthesis#binaryTournamentSelection** -* a uniform crossover is used where the chromosomes are broken from the middle. To change this behavious, tweak **Synthesis#crossover** -* a bit flip is used for mutation -* **Synthesis#synthesizePopulation** method is used to generate a new population -* **Synthesis#synthesizeChild** method generates a new child population from parent population -* **Synthesis#synthesizeGeneticCode** method generates the genetic code of each chromosome - -To change various plotting configurations within the code, please refer to **GraphPlot** class and change the required values. - -* **GRAPH_TITLE** -* **KEY** -* **DIMENSION_X** : x-axis dimension of generated graph -* **DIMENSION_Y** : y-axis dimension of generated graph -* **COLOR** -* **STROKE_THICKNESS** - -To set these values using setters, call the setter methods prior to calling the **GraphPlot#configurePlotter** - -The **Reporter#render2DGraph** works only if number of objectives is 2. It uses the **Configuration#getObjectives** method to check this condition. Implement your own renderer method to render various other graphs. \ No newline at end of file diff --git a/v1/pom.xml b/v1/pom.xml deleted file mode 100644 index 4e299d0..0000000 --- a/v1/pom.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - 4.0.0 - io.onclave - NSGA-II - 1.0-SNAPSHOT - jar - - - org.jfree - jfreechart - 1.0.13 - - - org.jfree - jcommon - 1.0.24 - - - - UTF-8 - 1.8 - 1.8 - - \ No newline at end of file diff --git a/v1/src/main/java/io/onclave/nsga/ii/Interface/IObjectiveFunction.java b/v1/src/main/java/io/onclave/nsga/ii/Interface/IObjectiveFunction.java deleted file mode 100644 index 11d3454..0000000 --- a/v1/src/main/java/io/onclave/nsga/ii/Interface/IObjectiveFunction.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * This repository / codebase is Open Source and free for use and rewrite. - */ -package io.onclave.nsga.ii.Interface; - -import io.onclave.nsga.ii.datastructure.Chromosome; -import io.onclave.nsga.ii.datastructure.ParetoObject; - -/** - * this is an interface that each objective function object that is created must implement. - * all the methods must be overridden. - * without implementing this interface, no objective function object can be plugged into the - * algorithm as the algorithm will not understand the objective function from a generic level. - * - * @author Debabrata Acharya - * @version 1.0 - * @since 0.1 - */ -public interface IObjectiveFunction { - public double objectiveFunction(double geneVaue); - public double objectiveFunction(Chromosome chromosome); - public double objectiveFunction(ParetoObject paretoObject); - public String getAxisTitle(); -} diff --git a/v1/src/main/java/io/onclave/nsga/ii/algorithm/Algorithm.java b/v1/src/main/java/io/onclave/nsga/ii/algorithm/Algorithm.java deleted file mode 100644 index 60b1015..0000000 --- a/v1/src/main/java/io/onclave/nsga/ii/algorithm/Algorithm.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * This repository / codebase is Open Source and free for use and rewrite. - */ -package io.onclave.nsga.ii.algorithm; - -import io.onclave.nsga.ii.api.GraphPlot; -import io.onclave.nsga.ii.api.Reporter; -import io.onclave.nsga.ii.api.Service; -import io.onclave.nsga.ii.api.Synthesis; -import io.onclave.nsga.ii.configuration.Configuration; -import io.onclave.nsga.ii.datastructure.Chromosome; -import io.onclave.nsga.ii.datastructure.ParetoObject; -import io.onclave.nsga.ii.datastructure.Population; -import org.jfree.ui.RefineryUtilities; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -/** - * This is the starting point of the main NSGA-II algorithm. - * Run this class to get the desired output. - * - * @author Debabrata Acharya - * @version 1.1 - * @since 0.1 - */ -public class Algorithm { - - /** - * This method first prepares the multi-objectives that it is going to work with. In this case, - * it works with 2 objectives. At generation 0, a random initial population is generated and then - * sorted using non-dominated population sorting. Using this initial parent population, a child - * population is generated. As the initial parent and child population are created, new generations - * are simulated and at each generation, the following actions are carried out: - * 1: the present parent and child are combined to create a new population containing all - * chromosomes from both parent and child. This ensures elitism. - * 2: this new combined population is then sorted using the fast non-dominated sorting algorithm - * to get a list of chromosomes that are grouped according to their rank. The higher the rank, - * more desirable they are to be carried forward into the next generation. - * 3: an iteration is carried over all the ranks as follows: - * i: the list of chromosomes from the current iterated rank is taken into account. - * ii: the amount of free remaining space in the new child population is calculated. - * iii: a crowd comparison sort is done after assigning crowding distance to the chromosomes. - * iv: if the number of chromosomes in this rank is less than or equal to the amount of free - * space available in the new child population, then the whole chromosome cluster is added - * to the new population, else only the available number of chromosomes is added to the - * child population according to their crowding distance. This is done for diversity - * preservation. - * v: end. - * 4: the new synthesized populace is added to the new child population. - * 5: if this is the last generation, then the present child is shown as the Pareto Front, otherwise, - * the present child is labeled as the new parent for the next generation, and a new child - * population is generated from this newly labeled parent population. This combination now becomes - * the new parent/child for the next generation. - * 6: all the child from all the generations are added to the Graph Rendering engine to show all the - * child data as fronts for that generation. - * 7: end. - * the plotted graphs are viewed. - * - * @param args pass command line arguments. Not required to run this code. - * @see Plotted graphs of all fronts as well as the Pareto Front as output. - */ - - public static void main(String[] args) { - - /* prepares the objectives [See Configuration.java file for more information.] */ - Configuration.buildObjectives(); - GraphPlot multiPlotGraph = new GraphPlot(); - - /** - * a new random population is synthesized and sorted using non-dominated population sort to get - * a sorted list of parent chromosomes at generation 0. - * child population generated from parent population. - */ - Population parent = Service.nonDominatedPopulationSort(Synthesis.syntesizePopulation()); - Population child = Synthesis.synthesizeChild(parent); - - /** - * a loop is run that iterates as new generations are created (new child population is created from previous parent - * population. - * the number of generations to be simulated are defined in the Configuration.java file. - */ - for(int i = 2; i <= Configuration.getGENERATIONS(); i++) { - - System.out.println("GENERATION : " + i); - - /** - * a combined population of both latest parent and child is created to ensure elitism. - * the combined population created is then sorted using fast non-dominated sorting algorithm, - * to create rank wise divisions [chromosomes with rank 1 (non-dominated), - * chromosomes with rank 2 (dominated by 1 chromosome), etc.] - * this information is stored in a HashMap data-structure that maps one integer value - * to one list of chromosomes. The integer refers to the rank number while the list refers - * to the chromosomes that belong to that rank. - */ - HashMap> rankedFronts = Service.fastNonDominatedSort(Service.createCombinedPopulation(parent, child)); - - Population nextChildPopulation = new Population(); - List childPopulace = new ArrayList<>(); - - /** - * an iteration is carried over the HashMap to go through each rank of chromosomes, and the - * most desired chromosomes (higher ranks) are included into the child population of the - * next generation. - */ - for(int j = 1; j <= rankedFronts.size(); j++) { - - /** - * during iteration, the current ranked list of chromosomes is chosen and the amount of - * free space (to accommodate chromosomes) of the current child population is calculated - * to check whether chromosomes from this rank can be fit into the new child population. - */ - List singularFront = rankedFronts.get(j); - int usableSpace = Configuration.getPOPULATION_SIZE() - childPopulace.size(); - - /** - * if the new list of chromosomes is not null and if the child population has free usable space, - * then an attempt to include some or all of the chromosomes is made otherwise, there is no more - * space in the child population and hence no more rank/chromosome checks are done and the program - * breaks out from the inner for-loop. - */ - if(singularFront != null && !singularFront.isEmpty() && usableSpace > 0) { - - /** - * if the amount of usable space is more than or equal to the number of chromosomes in the clot, - * the whole clot of chromosomes is added to the child population/populace, otherwise, only the - * number of chromosomes that can be fit within the usable space is chosen according to the - * crowding distance of the chromosomes. - */ - if(usableSpace >= singularFront.size()) childPopulace.addAll(singularFront); - else { - - /** - * a crowd comparison sort is carried over the present clot of chromosomes after assigning them a - * crowding distance (to preserve diversity) and hence a list of ParetoObjects are prepared. - * [refer ParetoObject.java for more information] - */ - List latestFront = Service.crowdComparisonSort(Service.crowdingDistanceAssignment(singularFront)); - - for(int k = 0; k < usableSpace; k++) childPopulace.add(latestFront.get(k).getChromosome()); - } - } else break; - } - - /** - * the new populace is added to the new child population - */ - nextChildPopulation.setPopulace(childPopulace); - - /** - * if this iteration is not the last generation, the new child created is made the parent for the next - * generation, and a new child is synthesized from this new parent for the next generation. - * this is the new parent and child for the next generation. - * if this is the last generation, no new parent/child combination is created, instead the Pareto Front - * is plotted and rendered as the latest created child is the actual Pareto Front. - */ - if(i < Configuration.getGENERATIONS()) { - parent = child; - child = Synthesis.synthesizeChild(nextChildPopulation); - } else Reporter.render2DGraph(child); - - /** - * this adds the child of each generation to the plotting to render the front of all the generations. - */ - multiPlotGraph.prepareMultipleDataset(child, i, "generation " + i); - } - - System.out.println("\n\n----CHECK PARETO FRONT OUTPUT----\n\n"); - - /** - * the plotted and rendered chart/graph is viewed to the user. - */ - multiPlotGraph.configureMultiplePlotter(Configuration.getXaxisTitle(), Configuration.getYaxisTitle(), "All Pareto"); - multiPlotGraph.pack(); - RefineryUtilities.centerFrameOnScreen(multiPlotGraph); - multiPlotGraph.setVisible(true); - } -} diff --git a/v1/src/main/java/io/onclave/nsga/ii/api/GraphPlot.java b/v1/src/main/java/io/onclave/nsga/ii/api/GraphPlot.java deleted file mode 100644 index ef0bc57..0000000 --- a/v1/src/main/java/io/onclave/nsga/ii/api/GraphPlot.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * This repository / codebase is Open Source and free for use and rewrite. - */ -package io.onclave.nsga.ii.api; - -import io.onclave.nsga.ii.configuration.Configuration; -import io.onclave.nsga.ii.datastructure.Population; -import org.jfree.chart.ChartFactory; -import org.jfree.chart.ChartPanel; -import org.jfree.chart.JFreeChart; -import org.jfree.chart.plot.PlotOrientation; -import org.jfree.chart.plot.XYPlot; -import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; -import org.jfree.data.xy.XYSeries; -import org.jfree.data.xy.XYSeriesCollection; -import org.jfree.ui.ApplicationFrame; - -import java.awt.*; -import java.util.Random; - -/** - * this class is the under-the-hood service layer for generating the graphs using jFreeCharts library. - * - * @author Debabrata Acharya - * @version 1.1 - * @since 1.0 - */ -public class GraphPlot extends ApplicationFrame { - - private final static XYSeriesCollection DATASET = new XYSeriesCollection(); - private final static XYSeriesCollection MULTIPLE_DATASET = new XYSeriesCollection(); - private final static XYLineAndShapeRenderer MULTIPLE_RENDERER = new XYLineAndShapeRenderer(); - private final static String APPLICATION_TITLE = "NSGA-II"; - private static String GRAPH_TITLE = "PARETO FRONT"; - private static String KEY = "Pareto Front"; - private static int DIMENSION_X = 800; - private static int DIMENSION_Y = 600; - private static Paint COLOR = Color.RED; - private static float STROKE_THICKNESS = 2.0f; - private static final Random RANDOM = new Random(); - - public GraphPlot() { - super(APPLICATION_TITLE); - } - - public GraphPlot(Population population) { - - super(APPLICATION_TITLE); - createDataset(population); - } - - public void prepareMultipleDataset(final Population population, final int datasetIndex, final String key) { - - createDataset(population, key, MULTIPLE_DATASET); - - MULTIPLE_RENDERER.setSeriesPaint(datasetIndex, new Color(RANDOM.nextFloat(), RANDOM.nextFloat(), RANDOM.nextFloat())); - MULTIPLE_RENDERER.setSeriesStroke(datasetIndex, new BasicStroke(STROKE_THICKNESS)); - } - - public void configureMultiplePlotter(final String x_axis, final String y_axis, final String graphTitle) { - - JFreeChart xyLineChart = ChartFactory.createXYLineChart(graphTitle, x_axis, y_axis, MULTIPLE_DATASET, PlotOrientation.VERTICAL, true, true, false); - ChartPanel chartPanel = new ChartPanel(xyLineChart); - - chartPanel.setPreferredSize(new java.awt.Dimension(DIMENSION_X, DIMENSION_Y)); - - final XYPlot plot = xyLineChart.getXYPlot(); - - plot.setRenderer(MULTIPLE_RENDERER); - setContentPane(chartPanel); - } - - private void createDataset(final Population population) { - createDataset(population, KEY); - } - - private void createDataset(final Population population, String key) { - createDataset(population, key, DATASET); - } - - private void createDataset(final Population population, String key, XYSeriesCollection dataset) { - - final XYSeries paretoFront = new XYSeries(key); - - population.getPopulace().stream().forEach((c) -> { paretoFront.add(Configuration.getObjectives().get(0).objectiveFunction(c), Configuration.getObjectives().get(1).objectiveFunction(c)); }); - - dataset.addSeries(paretoFront); - } - - public void configurePlotter(final String x_axis, final String y_axis) { - - JFreeChart xyLineChart = ChartFactory.createXYLineChart(GRAPH_TITLE, x_axis, y_axis, DATASET, PlotOrientation.VERTICAL, true, true, false); - ChartPanel chartPanel = new ChartPanel(xyLineChart); - - chartPanel.setPreferredSize(new java.awt.Dimension(DIMENSION_X, DIMENSION_Y)); - - final XYPlot plot = xyLineChart.getXYPlot(); - - XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); - - renderer.setSeriesPaint(0, COLOR); - renderer.setSeriesStroke(0, new BasicStroke(STROKE_THICKNESS)); - - plot.setRenderer(renderer); - setContentPane(chartPanel); - } - - public static void setGRAPH_TITLE(String GRAPH_TITLE) { - GraphPlot.GRAPH_TITLE = GRAPH_TITLE; - } - - public static void setKEY(String KEY) { - GraphPlot.KEY = KEY; - } - - public static void setDIMENSION_X(int DIMENSION_X) { - GraphPlot.DIMENSION_X = DIMENSION_X; - } - - public static void setDIMENSION_Y(int DIMENSION_Y) { - GraphPlot.DIMENSION_Y = DIMENSION_Y; - } - - public static void setCOLOR(Paint COLOR) { - GraphPlot.COLOR = COLOR; - } - - public static void setSTROKE_THICKNESS(float STROKE_THICKNESS) { - GraphPlot.STROKE_THICKNESS = STROKE_THICKNESS; - } -} diff --git a/v1/src/main/java/io/onclave/nsga/ii/api/Reporter.java b/v1/src/main/java/io/onclave/nsga/ii/api/Reporter.java deleted file mode 100644 index 4f20d21..0000000 --- a/v1/src/main/java/io/onclave/nsga/ii/api/Reporter.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * This repository / codebase is Open Source and free for use and rewrite. - */ -package io.onclave.nsga.ii.api; - -import io.onclave.nsga.ii.configuration.Configuration; -import io.onclave.nsga.ii.datastructure.Allele; -import io.onclave.nsga.ii.datastructure.Chromosome; -import io.onclave.nsga.ii.datastructure.Population; -import org.jfree.ui.RefineryUtilities; - -import java.util.HashMap; -import java.util.List; - -/** - * this is the add-on class that communicates with the console and prints appropriate object - * details as necessary. - * - * @author Debabrata Acharya - * @version 1.1 - * @since 1.0 - */ -public class Reporter { - - public static void render2DGraph(final Population population) { - - if(Configuration.getObjectives().size() > 2) { - p("\n\nThis Implementation has more than 2 objectives and cannot be plotted on a 2D graph. Either minimize objectives to 2, or use other plotting implemenataions.\n\n"); return; - } - - GraphPlot graph = new GraphPlot(population); - - graph.configurePlotter(Configuration.getXaxisTitle(), Configuration.getYaxisTitle()); - graph.pack(); - - RefineryUtilities.centerFrameOnScreen(graph); - - graph.setVisible(true); - } - - public static void reportPopulation(Population population) { - - int i = 1; - - for(Chromosome chromosome : population.getPopulace()) - p("Chromosome " + i++ + " : " + chromosome.getUniqueID() + " | " + chromosome.getFitness()); - } - - public static void reportGeneticCode(Allele[] geneticCode) { - - String geneticString = ""; - - for(Allele allele : geneticCode) geneticString += allele.getGene() ? "1" : "0"; - - p("GENETIC CODE : " + geneticString); - } - - public static void reportSingularFront(List singularFront, int i) { - - p("\n\nFRONT : " + i); - - for(Chromosome c : singularFront) { - int j = 0; - p("\tmaster : " + c.getUniqueID() + " | " + Double.toString(c.getFitness())); - for(Chromosome dc : c.getDominatedChromosomes()) p("\t\tslave : " + ++j + " | " + dc.getUniqueID() + " | " + Integer.toString(dc.getDominationRank())); - } - } - - public static void reportParetoFront(HashMap> paretoFront) { - - int j = 1; - int max = paretoFront.size(); - - for(int i = 1; i <= max; i++) { - - p("\n\nFRONT : " + i); - - int d = 0; - List population = paretoFront.get(i); - - if(population != null && !population.isEmpty()) { - - for(Chromosome c : population) { - - p("\tmaster : " + c.getUniqueID() + " | " + Double.toString(c.getFitness())); - - for(Chromosome dc : c.getDominatedChromosomes()) p("\t\tslave : " + ++d + " | " + dc.getUniqueID() + " | " + Integer.toString(dc.getDominationRank())); - } - } - } - } - - public static void p(String string) { - System.out.println(string); - } -} diff --git a/v1/src/main/java/io/onclave/nsga/ii/api/Service.java b/v1/src/main/java/io/onclave/nsga/ii/api/Service.java deleted file mode 100644 index 2090741..0000000 --- a/v1/src/main/java/io/onclave/nsga/ii/api/Service.java +++ /dev/null @@ -1,570 +0,0 @@ -/* - * This repository / codebase is Open Source and free for use and rewrite. - */ -package io.onclave.nsga.ii.api; - -import io.onclave.nsga.ii.Interface.IObjectiveFunction; -import io.onclave.nsga.ii.configuration.Configuration; -import io.onclave.nsga.ii.datastructure.Allele; -import io.onclave.nsga.ii.datastructure.Chromosome; -import io.onclave.nsga.ii.datastructure.ParetoObject; -import io.onclave.nsga.ii.datastructure.Population; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.concurrent.ThreadLocalRandom; - -/** - * This is the service class that does most of the under-the-hood work that is abstracted/encapsulated - * by other classes at the business/controller layer. - * - * @author Debabrata Acharya - * @version 1.1 - * @since 0.1 - */ -public class Service { - - /** - * this is an implementation of the fast non-dominated sorting algorithm as defined in the - * NSGA-II paper [DOI: 10.1109/4235.996017] Section III Part A. - * - * @param population the population object that needs to undergo fast non-dominated sorting algorithm - * @return a HashMap with an integer key that labels the ranks and a list of chromosomes as values that clot chromosomes of same rank - */ - public static HashMap> fastNonDominatedSort(Population population) { - - HashMap> paretoFront = new HashMap<>(); - List singularFront = new ArrayList<>(); - List populace = population.getPopulace(); - - /** - * iterating over each chromosome of the population - */ - for(Chromosome chromosome : populace) { - - /** - * an initial domination rank of 0 is set for each chromosome and a blank list is set for the number of - * chromosomes that the present chromosome dominates. - */ - chromosome.setDominationRank(0); - chromosome.setDominatedChromosomes(new ArrayList<>()); - - /** - * for each chromosome, the program iterates over all the other remaining chromosomes to find which other - * chromosomes are dominated by this chromosome and vice versa. - */ - for (Chromosome competitor : populace) if(!competitor.equals(chromosome)) { - - /** - * if the present chromosome dominates the competitor, then: - * i: check if the competitor already exists in the list of dominated chromosomes of the present chromosome. - * ii: if the competitor does not exist within the list, then add it to the list of dominated chromosomes - * of the present chromosome. - * else, if the competitor dominates the present chromosome, then increment the domination rank of the present - * chromosome by one. - */ - if(dominates(chromosome, competitor)) { - if(!chromosome.getDominatedChromosomes().contains(competitor)) chromosome.getDominatedChromosomes().add(competitor); - } else if(dominates(competitor, chromosome)) chromosome.setDominationRank(chromosome.getDominationRank() + 1); - } - - /** - * if the domination rank of the present chromosome is 0, it means that this chromosome is a non-dominated chromosome - * and hence it is added to the clot of chromosomes that are also non-dominated. - */ - if(chromosome.getDominationRank() == 0) singularFront.add(chromosome); - } - - /** - * the first clot of non-dominated chromosomes is added to the HashMap with rank label 1. - */ - paretoFront.put(1, singularFront); - - int i = 1; - List previousFront = paretoFront.get(i); - List nextFront = new ArrayList<>(); - - /** - * the current/previous ranked clot of chromosomes with rank i is iterated over to find the next clot of chromosomes - * with rank (i+1) - */ - while(previousFront != null && !previousFront.isEmpty()) { - - /** - * iterating over each chromosome from the previous clot of chromosomes ranked i. - */ - for(Chromosome chromosome : previousFront) { - - /** - * iterating over each of the dominated chromosomes from the present chromosome of rank i. - */ - for(Chromosome recessive : chromosome.getDominatedChromosomes()) { - - /** - * if the domination rank of the current recessive chromosome in consideration is not 0, then - * decrement it's rank by 1. - * if the domination rank of the current recessive chromosome in consideration is 0, then add - * it to the next front [clot of chromosomes that belong to rank (i+1)]. - */ - if(recessive.getDominationRank() != 0) recessive.setDominationRank(recessive.getDominationRank() - 1); - if(recessive.getDominationRank() == 0) if(!nextFront.contains(recessive)) nextFront.add(recessive); - } - } - - /** - * this code snippet ensures "rank jumps" to create all the possible rank lists from the parent - * population. - * new ranks are created only when there are recessive chromosomes with domination rank = 1 which are - * decremented to domination rank 0 and then added to the next front. - * but, due to the randomness of the algorithm, situation may occur such that even after decrementing all recessive - * chromosome domination ranks by 1, none have domination rank 0 and hence the next front remains empty. - * to ensure that all recessive chromosomes are added to some rank list, the program jumps domination ranks - * of each recessive chromosome by decrementing domination rank by 1 until at least one of them reaches a - * domination rank count of 0 and then that recessive chromosome is added to the next front. - * - * if the next front is empty and the previous front has at least one dominated chromosome: - * i: find the minimum rank among all the recessive chromosomes available: - * 1: iterate over all the chromosomes of the previous front - * 2: while the chromosomes have no dominated chromosomes with rank 0: - * a: iterate over all the recessive chromosomes of the current chromosome - * b: if the minimum rank is greater than the dominated rank of the present recessive, - * mark this as the minimum rank recorded among all recessive chromosomes available. - * 3: end while - * ii: iterate over all the chromosomes of the previous front - * 1: while the chromosomes have no dominated chromosomes with rank 0: - * a: iterate over all the dominated chromosomes of the current chromosome - * b: if the domination rank of the recessive chromosome is not 0, then decrement the - * domination count by value of minimum rank. - * c: if the domination rank is 0, then add it to the next front. - * 2: end while - */ - if(nextFront.isEmpty() && !isDominatedChromosomesEmpty(previousFront)) { - - int minimumRank = -1; - - for(Chromosome chromosome : previousFront) - while(hasRecessiveRankGreaterThanZero(chromosome)) - for(Chromosome recessive : chromosome.getDominatedChromosomes()) - if((minimumRank == -1) || minimumRank > recessive.getDominationRank()) minimumRank = recessive.getDominationRank(); - - if(minimumRank != -1) for(Chromosome chromosome : previousFront) - while(hasRecessiveRankGreaterThanZero(chromosome)) for(Chromosome recessive : chromosome.getDominatedChromosomes()) { - if(recessive.getDominationRank() != 0) recessive.setDominationRank(recessive.getDominationRank() - minimumRank); - if(recessive.getDominationRank() == 0) if(!nextFront.contains(recessive)) nextFront.add(recessive); - } - } - - /** - * if the next front calculated is not empty, then it is added to the ranked HashMap data-structure - * with the rank (i+1), else all chromosomes are sorted into some rank or the other and the program - * breaks out of the loop. - */ - if(!nextFront.isEmpty()) paretoFront.put(++i, nextFront); else break; - - /** - * the next front (i) calculated is marked as the previous front for the next iteration (i+1) and - * an empty next front is created. - */ - previousFront = nextFront; - nextFront = new ArrayList<>(); - } - - return paretoFront; - } - - /** - * this is the implementation of the crowding distance assignment algorithm as defined in the - * NSGA-II paper [DOI: 10.1109/4235.996017] Section III Part B. - * this ensures diversity preservation. - * - * @param singularFront a list of chromosomes whose crowding distances are to be calculated - * @return a list of ParetoObjects with assigned crowding distances. [Refer ParetoObject.java for more information] - */ - public static List crowdingDistanceAssignment(List singularFront) { - - int i = 0; - int end = singularFront.size() - 1; - Double maxObjectiveValue; - Double minObjectiveValue; - List objectives = Configuration.getObjectives(); - List singlePareto = new ArrayList<>(); - - /** - * for each chromosome in the input list, a new ParetoObject with an initial crowding distance of 0 - * is created and added to the list of ParetoObjects that are to be returned. - */ - for(Chromosome chromosome : singularFront) singlePareto.add(i++, new ParetoObject(chromosome, 0f)); - - /** - * iterating over each of the objective functions set [refer Configuration.java for more information], - * the ParetoObject list is sorted according to the objective functions and the first and last ParetoObjects - * are set a crowding distance of infinity. - */ - for(IObjectiveFunction objective : objectives) { - - maxObjectiveValue = null; - minObjectiveValue = null; - singlePareto = sort(singlePareto, objective); - - singlePareto.get(0).setCrowdingDistance(Double.MAX_VALUE); - singlePareto.get(end).setCrowdingDistance(Double.MAX_VALUE); - - /** - * the max and min objective values are calculated according to the present objective function - */ - for(ParetoObject paretoObject : singlePareto) { - - if((maxObjectiveValue == null) || (maxObjectiveValue < objective.objectiveFunction(paretoObject))) maxObjectiveValue = objective.objectiveFunction(paretoObject); - if((minObjectiveValue == null) || (minObjectiveValue > objective.objectiveFunction(paretoObject))) minObjectiveValue = objective.objectiveFunction(paretoObject); - } - - /** - * the crowding distance of all ParetoObjects are calculated and assigned except the first and last ParetoObjects - * that have infinite crowding distance - */ - for(i = 2; i < end; i++) singlePareto.get(i).setCrowdingDistance(calculateCrowdingDistance(singlePareto, - i, - objective, - maxObjectiveValue, - minObjectiveValue)); - } - - return singlePareto; - } - - /** - * this method sorts a list of ParetoObjects based on the Crowd-Comparison Operator using the domination - * rank and crowding distance as discussed in the NSGA-II paper [DOI: 10.1109/4235.996017] Section III Part B. - * - * @param singleFront a list of ParetoObjects that are to be sorted according to their crowding distance - * @return a list of sorted ParetoObjects - */ - public static List crowdComparisonSort(List singleFront) { - - int index = -1; - List sortedFront = new ArrayList<>(); - ParetoObject presentParetoObject; - ParetoObject competitor; - - /** - * all the ParetoObjects are, at first, marked as false for crowding distance sorted. - */ - singleFront.stream().forEach((paretoObject) -> { paretoObject.setCrowdingDistanceSorted(false); }); - - /** - * iterating over each ParetoObject in the singular front input: - * i: the i-th ParetoObject is marked as presentParetoObject - * ii: if the presentParetoObject is not already sorted by crowding distance: - * 1: iterate over the rest of the ParetoObjects in the input list as competitors that are - * not already sorted using crowding distance - * 2: compare the i-th and the j-th chromosome using the crowd comparison operator: - * a: for different ranks, choose the one with the lower (better) rank. - * b: for same rank, choose the one which has lower crowding distance. - * 3: if competitor dominates the i-th chromosome, then mark competitor as presentParetoObject - * 4: continue until i-th chromosome is compared to all competitors. - * 5: mark the presentParetoObject as already sorted by crowding distance - * 6: add presentParetoObject into list of sorted front with an incremented index - */ - for(int i = 0; i < singleFront.size(); i++) { - - presentParetoObject = singleFront.get(i); - - if(!presentParetoObject.isCrowdingDistanceSorted()) { - - for(int j = 0; j < singleFront.size(); j++) { - - competitor = singleFront.get(j); - - if(!competitor.isCrowdingDistanceSorted()) { - - double dominationRank = presentParetoObject.getChromosome().getDominationRank(); - double competingDominationRank = competitor.getChromosome().getDominationRank(); - double crowdingDistance = presentParetoObject.getCrowdingDistance(); - double competingCrowdingDistance = competitor.getCrowdingDistance(); - - if(i != j) if((dominationRank > competingDominationRank) || ((dominationRank == competingDominationRank) && (crowdingDistance < competingCrowdingDistance))) presentParetoObject = competitor; - } - } - - presentParetoObject.setCrowdingDistanceSorted(true); - sortedFront.add(++index, presentParetoObject); - } - } - - return sortedFront; - } - - /** - * this method is not implemented, as it is not absolutely necessary for this algorithm to work. - * is kept if implementation is needed in future. - * returns the same unsorted parent population as of now. - * - * @param population the population that is to be sorted - * @return a sorted population - */ - public static Population nonDominatedPopulationSort(Population population) { - - //--TO-DO-- - - return population; - } - - /** - * this method checks whether competitor1 dominates competitor2. - * requires that none of the values of the objective functions using competitor1 is smaller - * than the values of the objective functions using competitor2. - * at least one of the values of the objective functions using competitor1 is greater than - * the corresponding value of the objective functions using competitor2. - * - * @param competitor1 the chromosome that may dominate - * @param competitor2 the chromosome that may be dominated - * @return boolean logic whether competitor1 dominates competitor2. - */ - public static boolean dominates(final Chromosome competitor1, final Chromosome competitor2) { - - /** - * getting the list of configured objectives from Configuration.java - */ - List objectives = Configuration.getObjectives(); - - /** - * checks the negation of the predicate [none of the values of objective functions using competitor1 - * is less than values of objective functions using competitor2] meaning that at least one of the values - * of the objective functions using competitor1 is less than the values of the objective functions using - * competitor2, hence returning false as competitor1 does not dominate competitor2 - */ - if (!objectives.stream().noneMatch((objective) -> (objective.objectiveFunction(competitor1) < objective.objectiveFunction(competitor2)))) return false; - - /** - * returns the value of the predicate [at least one of the values of the objective functions using - * competitor1 is greater than the corresponding value of the objective function using competitor2] - */ - return objectives.stream().anyMatch((objective) -> (objective.objectiveFunction(competitor1) > objective.objectiveFunction(competitor2))); - } - - /** - * the list is first converted to an array data-structure and then a randomized quick sort - * algorithm is followed. - * the resulting sorted array is again converted to a List data-structure before returning. - * - * @param singlePareto the list of ParetoObjects that are to be sorted. - * @param objective the objective function using which the ParetoObjects are sorted. - * @return sorted list of ParetoObjects. - */ - private static List sort(List singlePareto, IObjectiveFunction objective) { - - ParetoObject[] paretoArray = new ParetoObject[singlePareto.size()]; - singlePareto.toArray(paretoArray); - - randomizedQuickSort(paretoArray, 0, paretoArray.length - 1, objective); - - return (new ArrayList<>(Arrays.asList(paretoArray))); - } - - /** - * refer [https://jordanspencerwu.github.io/randomized-quick-sort/] for more details on randomized - * quick sort algorithm. - * - * @param paretoArray the array to be sorted - * @param head the pointer/position of the head element - * @param tail the pointer/position of the tail element - * @param objective the objective function depending on which the sort is to take place - * @return the pivot index. - */ - private static int partition(ParetoObject[] paretoArray, int head, int tail, IObjectiveFunction objective) { - - ParetoObject pivot = paretoArray[tail]; - int i = head - 1; - - for(int j = head; j <= (tail - 1); j++) { - - if(objective.objectiveFunction(paretoArray[j]) <= objective.objectiveFunction(pivot)) { - - i++; - ParetoObject temporary = paretoArray[i]; - paretoArray[i] = paretoArray[j]; - paretoArray[j] = temporary; - } - } - - ParetoObject temporary = paretoArray[i + 1]; - paretoArray[i + 1] = paretoArray[tail]; - paretoArray[tail] = temporary; - - return (i + 1); - } - - /** - * refer [https://jordanspencerwu.github.io/randomized-quick-sort/] for more details on randomized - * quick sort algorithm. - * - * @param paretoArray the array to be sorted - * @param head the pointer/position of the head element - * @param tail the pointer/position of the tail element - * @param objective the objective function depending on which the sort is to take place - * @return the random partition position index. - */ - private static int randomizedPartition(ParetoObject[] paretoArray, int head, int tail, IObjectiveFunction objective) { - - int random = ThreadLocalRandom.current().nextInt(head, tail + 1); - - ParetoObject temporary = paretoArray[head]; - paretoArray[head] = paretoArray[random]; - paretoArray[random] = temporary; - - return partition(paretoArray, head, tail, objective); - } - - /** - * refer [https://jordanspencerwu.github.io/randomized-quick-sort/] for more details on randomized - * quick sort algorithm. - * - * @param paretoArray the array to be sorted - * @param head the pointer/position of the head element - * @param tail the pointer/position of the tail element - * @param objective the objective function depending on which the sort is to take place - */ - private static void randomizedQuickSort(ParetoObject[] paretoArray, int head, int tail, IObjectiveFunction objective) { - - if(head < tail) { - - int pivot = randomizedPartition(paretoArray, head, tail, objective); - - randomizedQuickSort(paretoArray, head, pivot - 1, objective); - randomizedQuickSort(paretoArray, pivot + 1, tail, objective); - } - } - - /** - * implementation of crowding distance calculation as defined in NSGA-II paper - * [DOI: 10.1109/4235.996017] Section III Part B. - * - * I[i]distance = I[i]distance + (I[i+1].m - I[i-1].m)/(f-max - f-min) - * - * I[i]distance = crowding distance of the i-th individual - * I[i+1].m = m-th objective function value of the (i+1)-th individual - * I[i-1].m = m-th objective function value of the (i-1)-th individual - * f-max, f-min = maximum and minimum values of the m-th objective function - * - * @param singlePareto the list of ParetoObjects - * @param presentIndex the present index of ParetoObject whose crowding distance is to be calculated - * @param objective the objective function over which the value of i-th individual is to be calculated - * @param maxObjectiveValue the maximum value for this objective function - * @param minObjectiveValue the minimum value for this objective function - * @return the crowding distance - */ - private static double calculateCrowdingDistance(List singlePareto, - final int presentIndex, - final IObjectiveFunction objective, - final double maxObjectiveValue, - final double minObjectiveValue) { - - return ( - singlePareto.get(presentIndex).getCrowdingDistance() - + ((objective.objectiveFunction(singlePareto.get(presentIndex + 1)) - - objective.objectiveFunction(singlePareto.get(presentIndex - 1))) / (maxObjectiveValue - minObjectiveValue)) - ); - } - - /** - * checks whether any of the dominated chromosome list of the given front is empty, - * returns true if at least one set of dominated chromosomes is not non-empty. - * - * @param front list of chromosomes whose dominated chromosomes are to be checked - * @return boolean logic whether the dominated chromosomes are empty - */ - private static boolean isDominatedChromosomesEmpty(List front) { - return front.stream().anyMatch((chromosome) -> (!chromosome.getDominatedChromosomes().isEmpty())); - } - - /** - * checks if any of the dominated chromosomes of the input chromosome has a domination rank of 0, - * returns true if at least one dominated chromosome contains domination rank 0. - * - * @param chromosome chromosome to check whether it contains any dominated chromosome with rank 0 - * @return boolean logic whether dominated chromosomes contain rank 0. - */ - private static boolean hasRecessiveRankGreaterThanZero(Chromosome chromosome) { - - if(chromosome.getDominatedChromosomes().isEmpty()) return false; - - return chromosome.getDominatedChromosomes().stream().noneMatch((recessive) -> (recessive.getDominationRank() == 0)); - } - - /** - * the child and parent population is combined to create a larger population pool - * - * @param parent parent population - * @param child child population - * @return combined parent + child population - */ - public static Population createCombinedPopulation(Population parent, Population child) { - - List combinedPopulace = new ArrayList<>(); - Population combinedPopulation = new Population(); - - combinedPopulace.addAll(parent.getPopulace()); - combinedPopulace.addAll(child.getPopulace()); - combinedPopulation.setPopulace(combinedPopulace); - - return combinedPopulation; - } - - /** - * this method decodes the genetic code that is represented as a string of binary values, converted into - * decimal value. - * - * @param geneticCode the genetic code as an array of Allele. Refer Allele.java for more information - * @return the decimal value of the corresponding binary string. - */ - public static double decodeGeneticCode(final Allele[] geneticCode) { - - double value = 0; - String binaryString = ""; - - for(Allele bit : geneticCode) binaryString += bit.getGene() ? "1" : "0"; - for(int i = 0; i < binaryString.length(); i++) if(binaryString.charAt(i) == '1') value += Math.pow(2, binaryString.length() - 1 - i); - - return value; - } - - /** - * fitness is calculated using min-max normalization - * - * @param geneticCode the genetic code whose fitness is to be calculated - * @return the corresponding calculated fitness - */ - public static double calculateFitness(Allele[] geneticCode) { - return minMaxNormalization(decodeGeneticCode(geneticCode)); - } - - /** - * an implementation of min-max normalization - * - * @param value the value that is to be normalized - * @return the normalized value - */ - private static double minMaxNormalization(final double value) { - return (((value - Configuration.ACTUAL_MIN) / (Configuration.ACTUAL_MAX - Configuration.ACTUAL_MIN)) * (Configuration.NORMALIZED_MAX - Configuration.NORMALIZED_MIN)) + Configuration.NORMALIZED_MIN; - } - - /** - * used to generate a random integer value - * - * @return a random integer value - */ - public static int generateRandomInt() { - return ThreadLocalRandom.current().nextInt(); - } - - /** - * a short hand for System.out.println(). - * - * @param string the string to print to console. - */ - public static void p(String string) { - System.out.println(string); - } -} diff --git a/v1/src/main/java/io/onclave/nsga/ii/api/Synthesis.java b/v1/src/main/java/io/onclave/nsga/ii/api/Synthesis.java deleted file mode 100644 index ee74f1c..0000000 --- a/v1/src/main/java/io/onclave/nsga/ii/api/Synthesis.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * This repository / codebase is Open Source and free for use and rewrite. - */ -package io.onclave.nsga.ii.api; - -import io.onclave.nsga.ii.configuration.Configuration; -import io.onclave.nsga.ii.datastructure.Allele; -import io.onclave.nsga.ii.datastructure.Chromosome; -import io.onclave.nsga.ii.datastructure.Population; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -/** - * This is the synthesis class that does many of the under-the-hood work (biological simulation) that is abstracted/encapsulated - * by other classes at the business/controller layer. - * - * @author Debabrata Acharya - * @version 1.1 - * @since 0.2 - */ -public class Synthesis { - - private static final Random LOCAL_RANDOM = new Random(); - - /** - * depending on the settings available in the Configuration.java file, this method synthesizes a - * random population of chromosomes with pseudo-randomly generated genetic code for each chromosome. - * - * @return a randomly generated population - */ - public static Population syntesizePopulation() { - - List populace = new ArrayList<>(); - - /** - * the number of chromosomes in the population is received from the Configuration.java file - */ - for(int i = 0; i < Configuration.getPOPULATION_SIZE(); i++) { - - Chromosome chromosome = new Chromosome(); - chromosome.setGeneticCode(synthesizeGeneticCode(Configuration.getCHROMOSOME_LENGTH())); - populace.add(chromosome); - } - - return new Population(populace); - } - - /** - * a child population of the same size as the parent is synthesized from the parent population - * - * @param parent the parent population object - * @return a child population synthesized from the parent population - */ - public static Population synthesizeChild(Population parent) { - - Population child = new Population(); - List populace = new ArrayList<>(); - - /** - * child chromosomes undergo crossover and mutation. - * the child chromosomes are selected using binary tournament selection. - * crossover returns an array of exactly two child chromosomes synthesized from two parent - * chromosomes. - */ - while(populace.size() < Configuration.getPOPULATION_SIZE()) - if((Configuration.getPOPULATION_SIZE() - populace.size()) == 1) populace.add(mutation(binaryTournamentSelection(parent))); - else for(Chromosome chromosome : crossover(binaryTournamentSelection(parent), binaryTournamentSelection(parent))) populace.add(mutation(chromosome)); - - child.setPopulace(populace); - - return child; - } - - /** - * this is an implementation of basic binary tournament selection. - * for a tournament of size t, select t individuals (randomly) from population and determine winner of - * tournament with the highest fitness value. - * in case of binary tournament selection, t = 2. - * - * refer [https://stackoverflow.com/questions/36989783/binary-tournament-selection] for more information. - * - * @param population the population from which a child chromosome is to be selected - * @return the selected child chromosome - */ - private static Chromosome binaryTournamentSelection(Population population) { - - Chromosome individual1 = population.getPopulace().get(LOCAL_RANDOM.nextInt(population.getPopulace().size())); - Chromosome individual2 = population.getPopulace().get(LOCAL_RANDOM.nextInt(population.getPopulace().size())); - - if(individual1.getFitness() > individual2.getFitness()) return individual1; else return individual2; - } - - /** - * this is a basic implementation of uniform crossover where the crossover/break point is the middle - * of the chromosomes. The genetic code of both the parent chromosomes are broken from the middle - * and crossover is done to create two child chromosomes. - * crossover probability is considered. - * - * @param chromosome1 the first parent chromosome taking part in crossover - * @param chromosome2 the second parent chromosome taking part in crossover - * @return an array of exactly two child chromosomes synthesized from two parent chromosomes. - */ - public static Chromosome[] crossover(Chromosome chromosome1, Chromosome chromosome2) { - - Allele[] geneticCode1 = new Allele[Configuration.getCHROMOSOME_LENGTH()]; - Allele[] geneticCode2 = new Allele[Configuration.getCHROMOSOME_LENGTH()]; - Allele[] chromosome1geneCode = chromosome1.getGeneticCode(); - Allele[] chromosome2geneCode = chromosome2.getGeneticCode(); - Chromosome[] childChromosomes = {new Chromosome(), new Chromosome()}; - int breakPoint = Configuration.getCHROMOSOME_LENGTH() / 2; - - /** - * generating a new random float value and if this value is less than equal to the - * crossover probability mentioned in the Configuration file, then crossover occurs, - * otherwise the parents themselves are copied as child chromosomes. - */ - if(LOCAL_RANDOM.nextFloat() <= Configuration.getCROSSOVER_PROBABILITY()) { - - for(int i = 0; i < Configuration.getCHROMOSOME_LENGTH(); i++) { - - if(i <= breakPoint) { - geneticCode1[i] = chromosome1geneCode[i]; - geneticCode2[i] = chromosome2geneCode[i]; - } else { - geneticCode1[i] = chromosome2geneCode[i]; - geneticCode2[i] = chromosome1geneCode[i]; - } - } - - childChromosomes[0].setGeneticCode(geneticCode1); - childChromosomes[1].setGeneticCode(geneticCode2); - } else { - childChromosomes[0] = chromosome1; - childChromosomes[1] = chromosome2; - } - - return childChromosomes; - } - - /** - * in this mutation operation implementation, a random bit-flip takes place. - * a random float value is generated and if this value is less than equal to the mutation - * probability defined in Configuration, then mutation takes place, otherwise the original - * chromosome is returned. - * - * @param chromosome the chromosome over which the mutation takes place - * @return the mutated chromosome - */ - private static Chromosome mutation(Chromosome chromosome) { - - if(LOCAL_RANDOM.nextFloat() <= Configuration.getMUTATION_PROBABILITY()) { - - Allele[] geneticCode = chromosome.getGeneticCode(); - geneticCode[LOCAL_RANDOM.nextInt(geneticCode.length)].bitFlip(); - chromosome.setGeneticCode(geneticCode); - } - - return chromosome; - } - - /** - * a genetic code as an array of Alleles is synthesized. - * refer Allele.java for more information. - * - * @param length the required length of the genetic code. - * @return the synthesized genetic code. - */ - public static Allele[] synthesizeGeneticCode(final int length) { - - Allele[] geneticCode = new Allele[length]; - - for(int i = 0; i < length; i++) geneticCode[i] = synthesizeAllele(); - - return geneticCode; - } - - /** - * an allele object with a randomly selected boolean gene value is synthesized. - * - * @return a randomly generated Allele object - */ - public static Allele synthesizeAllele() { - return new Allele(LOCAL_RANDOM.nextBoolean()); - } -} diff --git a/v1/src/main/java/io/onclave/nsga/ii/configuration/Configuration.java b/v1/src/main/java/io/onclave/nsga/ii/configuration/Configuration.java deleted file mode 100644 index 1424afb..0000000 --- a/v1/src/main/java/io/onclave/nsga/ii/configuration/Configuration.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * This repository / codebase is Open Source and free for use and rewrite. - */ -package io.onclave.nsga.ii.configuration; - -import io.onclave.nsga.ii.Interface.IObjectiveFunction; -import io.onclave.nsga.ii.objectivefunction.SCH_1; -import io.onclave.nsga.ii.objectivefunction.SCH_2; - -import java.util.ArrayList; -import java.util.List; - -/** - * this is the Configuration file for the algorithm, where all the values are set and the initial - * configurations are set and run. - * to change any aspect of the algorithm, this file may be tweaked. - * - * @author Debabrata Acharya - * @version 1.0 - * @since 0.1 - */ -public class Configuration { - - private static final int POPULATION_SIZE = 800; - private static final int GENERATIONS = 50; - private static final int CHROMOSOME_LENGTH = 20; - private static final float CROSSOVER_PROBABILITY = 0.7f; - private static final float MUTATION_PROBABILITY = 0.03f; - private static List objectives = null; - - public static final double ACTUAL_MIN = 0; - public static final double ACTUAL_MAX = Math.pow(2, CHROMOSOME_LENGTH) - 1; - public static final double NORMALIZED_MIN = 0; - public static final double NORMALIZED_MAX = 2; - public static final String DEFAULT_X_AXIS_TITLE = "x-axis"; - public static final String DEFAULT_Y_AXIS_TITLE = "y-axis"; - - public static int getPOPULATION_SIZE() { - return POPULATION_SIZE; - } - - public static int getGENERATIONS() { - return GENERATIONS; - } - - public static int getCHROMOSOME_LENGTH() { - return CHROMOSOME_LENGTH; - } - - /** - * this method sets the objective functions over which the algorithm is to operate. - * it is a list of IObjectionFunction objects. - */ - public static void buildObjectives() { - - List newObjectives = new ArrayList<>(); - - newObjectives.add(new SCH_1()); - newObjectives.add(new SCH_2()); - - setObjectives(newObjectives); - } - - public static List getObjectives() { - return objectives; - } - - public static void setObjectives(List objectives) { - Configuration.objectives = objectives; - } - - public static float getMUTATION_PROBABILITY() { - return MUTATION_PROBABILITY; - } - - public static float getCROSSOVER_PROBABILITY() { - return CROSSOVER_PROBABILITY; - } - - public static String getXaxisTitle() { - return getObjectives().size() > 2 ? DEFAULT_X_AXIS_TITLE : getObjectives().get(0).getAxisTitle(); - } - - public static String getYaxisTitle() { - return getObjectives().size() > 2 ? DEFAULT_Y_AXIS_TITLE : getObjectives().get(1).getAxisTitle(); - } -} diff --git a/v1/src/main/java/io/onclave/nsga/ii/datastructure/Allele.java b/v1/src/main/java/io/onclave/nsga/ii/datastructure/Allele.java deleted file mode 100644 index abc04ff..0000000 --- a/v1/src/main/java/io/onclave/nsga/ii/datastructure/Allele.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This repository / codebase is Open Source and free for use and rewrite. - */ -package io.onclave.nsga.ii.datastructure; - -/** - * this is a simulation of an allele in a biological chromosome that contains a gene value. - * an array of alleles create the genetic code for the chromosome. - * - * @author Debabrata Acharya - * @version 1.0 - * @since 0.1 - */ -public class Allele { - - public Allele() { - this(false); - } - - public Allele(final boolean gene) { - this.gene = gene; - } - - private boolean gene; - - public boolean getGene() { - return gene; - } - - public void setGene(boolean gene) { - this.gene = gene; - } - - public void bitFlip() { - this.gene = !this.gene; - } -} diff --git a/v1/src/main/java/io/onclave/nsga/ii/datastructure/Chromosome.java b/v1/src/main/java/io/onclave/nsga/ii/datastructure/Chromosome.java deleted file mode 100644 index 92f3ca7..0000000 --- a/v1/src/main/java/io/onclave/nsga/ii/datastructure/Chromosome.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * This repository / codebase is Open Source and free for use and rewrite. - */ -package io.onclave.nsga.ii.datastructure; - -import io.onclave.nsga.ii.api.Service; - -import java.util.ArrayList; -import java.util.List; - -/** - * this is a simulation of a biological chromosome that contains a genetic code, a fitness value, - * a domination rank, an unique ID, and a list of dominated chromosomes. - * - * @author Debabrata Acharya - * @version 1.1 - * @since 0.1 - */ -public class Chromosome { - - public Chromosome() { - this(-Double.MIN_VALUE); - } - - public Chromosome(final double fitness) { - this(null, fitness, ""); - } - - public Chromosome(final Allele[] geneticCode, final double fitness, final String extraInfo) { - this(geneticCode, fitness, extraInfo, 0); - - } - - public Chromosome(final Allele[] geneticCode, final double fitness, final String extraInfo, final int rank) { - - this.geneticCode = geneticCode; - this.fitness = fitness; - this.extraInfo = extraInfo; - this.dominationRank = rank; - this.uniqueID = Long.toString(System.currentTimeMillis()) + "-" + Integer.toString(Service.generateRandomInt()); - this.dominatedChromosomes = new ArrayList<>(); - } - - private Allele[] geneticCode; - private double fitness; - private String extraInfo; - private int dominationRank = 0; - private String uniqueID; - private List dominatedChromosomes; - - public List getDominatedChromosomes() { - return dominatedChromosomes; - } - - public void setDominatedChromosomes(List dominatedChromosomes) { - this.dominatedChromosomes = dominatedChromosomes; - } - - public String getUniqueID() { - return uniqueID; - } - - public void setUniqueID(String uniqueID) { - this.uniqueID = uniqueID; - } - - public int getDominationRank() { - return dominationRank; - } - - public void setDominationRank(int dominationRank) { - this.dominationRank = dominationRank; - } - - public String getExtraInfo() { - return extraInfo; - } - - public void setExtraInfo(String extraInfo) { - this.extraInfo = extraInfo; - } - - public Allele[] getGeneticCode() { - return geneticCode; - } - - /** - * the new fitness value is set as soon as a new genetic code is set for a chromosome. - * @param geneticCode the genetic code that the chromosome carries. - */ - public void setGeneticCode(Allele[] geneticCode) { - this.geneticCode = geneticCode; - this.setFitness(Service.calculateFitness(geneticCode)); - } - - public double getFitness() { - return fitness; - } - - public void setFitness(double fitness) { - this.fitness = fitness; - } -} diff --git a/v1/src/main/java/io/onclave/nsga/ii/datastructure/ParetoObject.java b/v1/src/main/java/io/onclave/nsga/ii/datastructure/ParetoObject.java deleted file mode 100644 index 127a1b0..0000000 --- a/v1/src/main/java/io/onclave/nsga/ii/datastructure/ParetoObject.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This repository / codebase is Open Source and free for use and rewrite. - */ -package io.onclave.nsga.ii.datastructure; - -/** - * The ParetoObject class is used to define a single pareto object where each object has a chromosome, - * along with the corresponding assigned crowding distance. - * this is an IoC extension of the Chromosome class where each chromosomes are stored along with its - * corresponding crowding distance assigned to it. - * - * property crowdingDistanceSorted is a marker variable that keeps record whether the ParetoObject has - * already been considered during crowd comparison sorting. - * - * @author Debabrata Acharya - * @version 1.0 - * @since 0.1 - */ -public class ParetoObject { - - private Chromosome chromosome = null; - private double crowdingDistance = -1f; - private boolean crowdingDistanceSorted = false; - - public ParetoObject(Chromosome chromosome) { - this(chromosome, -1f); - } - - public ParetoObject(Chromosome chromosome, float crowdingDistance) { - this.chromosome = chromosome; - this.crowdingDistance = crowdingDistance; - } - - public Chromosome getChromosome() { - return chromosome; - } - - public void setChromosome(Chromosome chromosome) { - this.chromosome = chromosome; - } - - public double getCrowdingDistance() { - return crowdingDistance; - } - - public void setCrowdingDistance(double crowdingDistance) { - this.crowdingDistance = crowdingDistance; - } - - public boolean isCrowdingDistanceSorted() { - return crowdingDistanceSorted; - } - - public void setCrowdingDistanceSorted(boolean crowdingDistanceSorted) { - this.crowdingDistanceSorted = crowdingDistanceSorted; - } -} diff --git a/v1/src/main/java/io/onclave/nsga/ii/datastructure/Population.java b/v1/src/main/java/io/onclave/nsga/ii/datastructure/Population.java deleted file mode 100644 index 503cf24..0000000 --- a/v1/src/main/java/io/onclave/nsga/ii/datastructure/Population.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This repository / codebase is Open Source and free for use and rewrite. - */ -package io.onclave.nsga.ii.datastructure; - -import java.util.List; - -/** - * this is a simulation of a population of chromosome known as a populace. - * - * @author Debabrata Acharya - * @version 1.0 - * @since 0.1 - */ -public class Population { - - public Population() { - this(null); - } - - public Population(final List populace) { - this.populace = populace; - } - - private List populace; - - public List getPopulace() { - return populace; - } - - public void setPopulace(List populace) { - this.populace = populace; - } -} diff --git a/v1/src/main/java/io/onclave/nsga/ii/objectivefunction/SCH_1.java b/v1/src/main/java/io/onclave/nsga/ii/objectivefunction/SCH_1.java deleted file mode 100644 index f2967b5..0000000 --- a/v1/src/main/java/io/onclave/nsga/ii/objectivefunction/SCH_1.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This repository / codebase is Open Source and free for use and rewrite. - */ -package io.onclave.nsga.ii.objectivefunction; - -import io.onclave.nsga.ii.Interface.IObjectiveFunction; -import io.onclave.nsga.ii.datastructure.Chromosome; -import io.onclave.nsga.ii.datastructure.ParetoObject; - -/** - * the SCH objective function [f(x) = x^2] - * - * @author Debabrata Acharya - * @version 1.0 - * @since 0.1 - */ -public class SCH_1 implements IObjectiveFunction { - - private static final String AXIS_TITLE = "pow(x, 2)"; - - @Override - public double objectiveFunction(final ParetoObject paretoObject) { - return objectiveFunction(paretoObject.getChromosome()); - } - - @Override - public double objectiveFunction(final Chromosome chromosome) { - return objectiveFunction(chromosome.getFitness()); - } - - @Override - public double objectiveFunction(double geneVaue) { - return Math.pow(geneVaue, 2); - } - - @Override - public String getAxisTitle() { - return AXIS_TITLE; - } -} diff --git a/v1/src/main/java/io/onclave/nsga/ii/objectivefunction/SCH_2.java b/v1/src/main/java/io/onclave/nsga/ii/objectivefunction/SCH_2.java deleted file mode 100644 index 536f94c..0000000 --- a/v1/src/main/java/io/onclave/nsga/ii/objectivefunction/SCH_2.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This repository / codebase is Open Source and free for use and rewrite. - */ -package io.onclave.nsga.ii.objectivefunction; - -import io.onclave.nsga.ii.Interface.IObjectiveFunction; -import io.onclave.nsga.ii.datastructure.Chromosome; -import io.onclave.nsga.ii.datastructure.ParetoObject; - -/** - * the SCH objective function [f(x) = (x - 2)^2] - * - * @author Debabrata Acharya - * @version 1.0 - * @since 0.1 - */ -public class SCH_2 implements IObjectiveFunction { - - private static final String AXIS_TITLE = "pow(x - 2, 2)"; - - @Override - public double objectiveFunction(Chromosome chromosome) { - return objectiveFunction(chromosome.getFitness()); - } - - @Override - public double objectiveFunction(ParetoObject paretoObject) { - return objectiveFunction(paretoObject.getChromosome()); - } - - @Override - public double objectiveFunction(double geneVaue) { - return Math.pow(geneVaue - 2, 2); - } - - @Override - public String getAxisTitle() { - return AXIS_TITLE; - } - -} diff --git a/v1/src/main/java/io/onclave/nsga/ii/objectivefunction/ZDT1_1.java b/v1/src/main/java/io/onclave/nsga/ii/objectivefunction/ZDT1_1.java deleted file mode 100644 index 106e3c0..0000000 --- a/v1/src/main/java/io/onclave/nsga/ii/objectivefunction/ZDT1_1.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This code file and the codebase/software containing it is - * explicitely licensed to Mr. Debabrata Acharya (@onclave) - * unauthorized use and access of the codebase, parts of the - * codebase, software or parts of this software is not allowed. - */ -package io.onclave.nsga.ii.objectivefunction; - -import io.onclave.nsga.ii.Interface.IObjectiveFunction; -import io.onclave.nsga.ii.datastructure.Chromosome; -import io.onclave.nsga.ii.datastructure.ParetoObject; - -/** - * - * @author sajib - */ -public class ZDT1_1 implements IObjectiveFunction { - - private static final String AXIS_TITLE = "x1"; - - @Override - public double objectiveFunction(double geneVaue) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public double objectiveFunction(Chromosome chromosome) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public double objectiveFunction(ParetoObject paretoObject) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public String getAxisTitle() { - return AXIS_TITLE; - } - -} diff --git a/v1/src/main/java/io/onclave/nsga/ii/objectivefunction/ZDT1_2.java b/v1/src/main/java/io/onclave/nsga/ii/objectivefunction/ZDT1_2.java deleted file mode 100644 index 3c87385..0000000 --- a/v1/src/main/java/io/onclave/nsga/ii/objectivefunction/ZDT1_2.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This code file and the codebase/software containing it is - * explicitely licensed to Mr. Debabrata Acharya (@onclave) - * unauthorized use and access of the codebase, parts of the - * codebase, software or parts of this software is not allowed. - */ -package io.onclave.nsga.ii.objectivefunction; - -import io.onclave.nsga.ii.Interface.IObjectiveFunction; -import io.onclave.nsga.ii.datastructure.Chromosome; -import io.onclave.nsga.ii.datastructure.ParetoObject; - -/** - * - * @author sajib - */ -public class ZDT1_2 implements IObjectiveFunction { - - private static final String AXIS_TITLE = "g(x)[1 - sqrt(x1/g(x))]"; - private static final int N = 30; - - @Override - public double objectiveFunction(double geneVaue) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public double objectiveFunction(Chromosome chromosome) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public double objectiveFunction(ParetoObject paretoObject) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - private double g_x() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public String getAxisTitle() { - return AXIS_TITLE; - } - -} diff --git a/v1/src/test/java/TestService.java b/v1/src/test/java/TestService.java deleted file mode 100644 index 472919c..0000000 --- a/v1/src/test/java/TestService.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * This repository / codebase is Open Source and free for use and rewrite. - */ - -import io.onclave.nsga.ii.api.Reporter; -import io.onclave.nsga.ii.api.Service; -import io.onclave.nsga.ii.api.Synthesis; -import io.onclave.nsga.ii.configuration.Configuration; -import io.onclave.nsga.ii.datastructure.Population; - -/** - * - * @author sajib - */ -public class TestService { - - public static void main(String[] args) { - - Configuration.buildObjectives(); - -// decodeGeneticCodeTest(); -// calculateFitnessTest(); -// createCombinedPopulationTest(); - fastNonDominatedSortTest(); - } - - public static void decodeGeneticCodeTest() { - - p("\ndecodeGeneticCodeTest : START\n"); - - for(int i = 0; i < 10; i++) p("Fitness : " + Service.decodeGeneticCode(Synthesis.synthesizeGeneticCode(10))); - - p("\ndecodeGeneticCodeTest : END\n"); - } - - public static void calculateFitnessTest() { - - p("\ncalculateFitnessTest : START\n"); - - for(int i = 0; i < 10; i++) p("Fitness : " + Service.calculateFitness(Synthesis.synthesizeGeneticCode(10))); - - p("\ncalculateFitnessTest : END\n"); - } - - public static void createCombinedPopulationTest() { - - p("\ncreateCombinedPopulationTest : START\n"); - - Population parent = Synthesis.syntesizePopulation(); - Population child = Synthesis.synthesizeChild(parent); - Population combined = Service.createCombinedPopulation(parent, child); - - p("\nPARENT : "); - - Reporter.reportPopulation(parent); - - p("\n\nCHILD : "); - - Reporter.reportPopulation(child); - - p("\nCOMBINED"); - - Reporter.reportPopulation(combined); - - p("\ncreateCombinedPopulationTest : END\n"); - } - - public static void fastNonDominatedSortTest() { - - p("\nfastNonDominatedSortTest : START\n"); - - Population parent = Synthesis.syntesizePopulation(); - Population child = Synthesis.synthesizeChild(parent); - Population combined = Service.createCombinedPopulation(parent, child); - - p("\nPARENT : "); - - Reporter.reportPopulation(parent); - - p("\n\nCHILD : "); - - Reporter.reportPopulation(child); - - p("\nCOMBINED"); - - Reporter.reportPopulation(combined); - - p("\nPARETO FRONT"); - -// Reporter.reportParetoFront(Service.fastNonDominatedSort(combined)); - - Service.fastNonDominatedSort(combined); - - p("\nfastNonDominatedSortTest : END\n"); - } - - public static void p(String string) { - System.out.println(string); - } -} diff --git a/v1/src/test/java/TestSynthesis.java b/v1/src/test/java/TestSynthesis.java deleted file mode 100644 index 90515d6..0000000 --- a/v1/src/test/java/TestSynthesis.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * This repository / codebase is Open Source and free for use and rewrite. - */ -import io.onclave.nsga.ii.api.Reporter; -import io.onclave.nsga.ii.api.Synthesis; -import io.onclave.nsga.ii.configuration.Configuration; -import io.onclave.nsga.ii.datastructure.Chromosome; -import io.onclave.nsga.ii.datastructure.Population; - -/** - * - * @author sajib - */ -public class TestSynthesis { - - public static void main(String[] args) { - - Configuration.buildObjectives(); - - synthesizeAlleleTest(); - synthesizeGeneticCodeTest(); - syntesizePopulationTest(); - crossoverTest(); - synthesizeChildTest(); - } - - public static void synthesizeAlleleTest() { - - p("\nsynthesizeAlleleTest : START\n"); - - for(int i = 0; i < 10; i++) p("Random Allele : " + Synthesis.synthesizeAllele().getGene()); - - p("\nsynthesizeAlleleTest : END\n"); - } - - public static void synthesizeGeneticCodeTest() { - - p("\nsynthesizeGeneticCodeTest : START\n"); - - Reporter.reportGeneticCode(Synthesis.synthesizeGeneticCode(10)); - - p("\nsynthesizeGeneticCodeTest : END\n"); - } - - public static void syntesizePopulationTest() { - - p("\nsynthesizePopulationTest : START\n"); - - Reporter.reportPopulation(Synthesis.syntesizePopulation()); - - p("\nsynthesizePopulationTest : END"); - } - - public static void crossoverTest() { - - p("\ncrossoverTest : START\n"); - - Chromosome chromosome1 = new Chromosome(); - Chromosome chromosome2 = new Chromosome(); - - chromosome1.setGeneticCode(Synthesis.synthesizeGeneticCode(Configuration.getCHROMOSOME_LENGTH())); - chromosome2.setGeneticCode(Synthesis.synthesizeGeneticCode(Configuration.getCHROMOSOME_LENGTH())); - - Reporter.reportGeneticCode(chromosome1.getGeneticCode()); - Reporter.reportGeneticCode(chromosome2.getGeneticCode()); - - Chromosome[] chromosomes = Synthesis.crossover(chromosome1, chromosome2); - - for(Chromosome chromosome : chromosomes) Reporter.reportGeneticCode(chromosome.getGeneticCode()); - - p("\ncrossoverTest : END"); - } - - public static void synthesizeChildTest() { - - p("\nsynthesizeChildTest : START\n"); - - Population parent = Synthesis.syntesizePopulation(); - - p("\nPARENT : "); - - Reporter.reportPopulation(parent); - - p("\n\nCHILD : "); - - Reporter.reportPopulation(Synthesis.synthesizeChild(parent)); - - p("\nsynthesizeChildTest : END"); - } - - public static void p(String string) { - System.out.println(string); - } -} diff --git a/v2/README.md b/v2/README.md deleted file mode 100644 index 53f0d23..0000000 --- a/v2/README.md +++ /dev/null @@ -1,98 +0,0 @@ -# NSGA-II -**an NSGA-II implementation using Java** - -**_Original Authors of the Paper_: [Kalyanmoy Deb](http://www.egr.msu.edu/~kdeb/), [Amrit Pratap](https://scholar.google.com/citations?user=E8wJ7G8AAAAJ&hl=en), [Sameer Agarwal](http://ieeexplore.ieee.org/search/searchresult.jsp?searchWithin=%22Authors%22:.QT.S.%20Agarwal.QT.&newsearch=true), [T. Meyarivan](http://ieeexplore.ieee.org/search/searchresult.jsp?searchWithin=%22Authors%22:.QT.T.%20Meyarivan.QT.&newsearch=true)** - -_links to original contents:_ - -* [NSGA-II paper: PDF](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.542.385&rep=rep1&type=pdf) -* [NSGA-II code implementation by original authors at **KanGAL**](https://www.iitk.ac.in/kangal/codes.shtml) - -_**note**: this implementation of NSGA-II algorithm is in pure reference to the original published paper. This is not an effort to convert the originally implemented C code in Java. The original C code by the authors has not be referred to while writing this implementation._ - -_**Dependency: Java(>=1.8), JFreeChart(1.5.0), JCommon(1.0.24)**_ - -### Please Note: - -This is **v2** of the algorithm implementation. This reference implementation has been updated to be: - -* Much simpler than the previous version. -* More efficient than the previous version. -* Resolves an issue with the Binary Tournament Selection procedure. -* Provides a simple interactive console while running the algorithm. -* Updated dependencies. - -The reference **v1** implemention can be found [here](https://github.com/onclave/NSGA-II/tree/master/v1). The _README_ of that implementation can be found [here](https://github.com/onclave/NSGA-II/blob/master/v1/README.md). - -**note:** Code commenting of **v2** is under progress and not all code is commented properly. This shall be done shortly. In the mean time, if you are unable to understand any part of the code, feel free to open an _issue_ about it and I shall try to resolve it. - -### How It Works - -This code has 5 packages: - -* _io.onclave.nsga.ii.interfaces_ : This package has the **IObjectiveFunction** interface that must be implemented by any Objective Function class in package _io.onclave.nsga.ii.objectivefunction_ -* _io.onclave.nsga.ii.algorithm_ : This package contains the main algorithm class, **Algorithm.java** that must be run during execution. -* _io.onclave.nsga.ii.api_ : This package contains - * the **Configuration.java** class which prepares and configures various modules before the algorithm is executed. - * the **GraphPlot.java** class which currently supports 2D graph plotting using [JFreeChart](http://www.jfree.org/jfreechart/) library, - * the **NSGAII.java** class which has all the NSGA-II specific methods and procedures such as **NSGAII#fastNonDominatedSort** and **NSGAII#crowdingDistanceAssignment** among others. The implementation of this class is completely new in contrast to the [v1](https://github.com/onclave/NSGA-II/tree/master/v1) reference implementation of this algoithm, - * the **Reporter.java** that is used to print to console various states of the dataset and other information during runtime, - * the **Service.java** that contains most of the essential functions required to run NSGA-II. Much of the implementation of this class has changed since v1, and - * **Synthesis.java** that is essential for genetic algorithm specific functions. -* _io.onclave.nsga.ii.datastructure_ : This package contains the datastructures required for NSGA-II. An Object Oriented approached has been used. - * **Allele.java** - * **Chromosome.java** - This class has a new implementation from v1. - * **Population.java** -* _io.onclave.nsga.ii.objectivefunction_ : contains various objective function specific classes that can be used with NSGA-II. All objective function classes must implement **IObjectiveFunction** - -This implementation of the algorithm works in accordance to the original algorithm proposed in the paper. - -### Procedure - -* The algorithm can be run by executing **Algorithm.java**. This class just shows how the actual NSGA-II algorithm works. This class is not absolutely necessary to run this algorithm and can be used as just an example to study how the actual implementation works. -* For the initial generation, a population of size **_N_** = **Configuration#POPULATION_SIZE** is created using **Synthesis.syntesizePopulation** after which the NSGA-II specific procedures of fast non-dominated sort and crowding distance assignment are called on this initial population at **NSGAII#preparePopulation**. - * Within **NSGAII#preparePopulation**, the **NSGAII#fastNonDominatedSort** and **NSGAII#crowdingDistanceAssignment** are called on the population followed by **Service#randomizedQuickSortForRank** to sort them based on their rank. -* This creates the first parent population. -* The first child population is created from the parent population using **Synthesis#synthesizeChild**. -* Then, for each consecutive generation, - * The parent and child population are combined to create a combined population of size **_2N_** using **Synthesis#createCombinedPopulation**. - * The **NSGAII#fastNonDominatedSort** and **NSGAII#crowdingDistanceAssignment** are called on the combined population via **NSGAII#preparePopulation** followed by **Service#randomizedQuickSortForRank**. - * the child population is obtained from the combined population from **NSGAII#getChildFromCombinedPopulation** based on their _crowding distance_. This becomes the new parent population. - * The new child population is then synthesized from this parent population using **Synthesis#synthesizeChild**. - -### Configurations - -To change various general configurations within the code, please refer to **Configuration** class and change the required values. - -* **POPULATION_SIZE** : population size at each generation. Default is 100. -* **GENERATIONS** : number of generations the algorithm should run. Default is 25. -* **CHROMOSOME_LENGTH** : chromosome length _(n)_ in bits. Default is 20. Please note that, changing this to a very large value may throw a _NumberFormatException_ since the _n_ bit binary string is directly converted to a corresponding double value **to calculate the fitness value of each chromosome**. A relatively large binary string may throw exceptions during conversions. For example, _n = 8_ bit binary string keeps the value within 0 to 255. **This may not be a problem if you decide to change how you might want to calculate your fitness value for each chromosome**. By default, the fitness value is calculated for each chromosome as soon as a new chromosome is synthesized within the **Chromosome#Chromosome(Allele[])** constructor. Change this to use your own implementation. -* **CROSSOVER_PROBABILITY** : Default is 0.7. -* **MUTATION_PROBABILITY** : Default is 0.03. -* The binary strings generated as genetic code for each chromosome is converted to the corresponding double value which is the fitness of the chromosomes. This value is then normalized using min-max normalisation between 0 to 2. Change the variables **ACTUAL_MIN**, **ACTUAL_MAX**, **NORMALIZED_MIN**, **NORMALIZED_MAX** for other values. -* To change the default way the fitness value is calculated for each chromosome, implement **Service#calculateFitness** method according to requirement. This method would be automatically called during creation of every new instance of a chromosome. To change this behaviour, tweak the **Chromosome(Allele[])** constructor within the **Chromosome** class. -* The **Configuration#buildObjectives** method is called at the very beginning of the algorithm during call to **Configuration#configure**. This method should configure the **Configuration#objectives** property before the algorithm can execute anything. All the objectives must be configured and added to this list at the very beginning. Refer to **Configuration#buildObjectives** method for basic implementation. -* The x-axis and y-axis titles are retrieved from the objective functions by default using the **IObjectiveFunction#objectiveFunctionTitle** method. This can be changed to provide other values in the interactive console during runtime. -* The interactive console behaviour can be tweaked and changed according to need in the **Configuration#configure** method. - -To change various synthesis configurations within the code, please refer to **Synthesis** class and change the required methods. - -* a crowded binary tournament selection procedure is used to select a chromosome from parent to child at **Synthesis#crowdedBinaryTournamentSelection** -* a uniform crossover is used where the chromosomes are broken from the middle. To change this behavious, tweak **Synthesis#crossover** -* a bit flip is used for mutation -* **Synthesis#synthesizePopulation** method is used to generate a new population -* **Synthesis#synthesizeChild** method generates a new child population from parent population -* **Synthesis#synthesizeGeneticCode** method generates the genetic code of each chromosome - -To change various plotting configurations within the code, please refer to **GraphPlot** class and change the required values. - -* **GRAPH_TITLE** -* **KEY** -* **DIMENSION_X** : x-axis dimension of generated graph -* **DIMENSION_Y** : y-axis dimension of generated graph -* **COLOR** -* **STROKE_THICKNESS** - -To set these values using setters, call the setter methods prior to calling the **GraphPlot#configurePlotter** - -The **Reporter#render2DGraph** works only if number of objectives is 2. It uses the **Configuration#getObjectives** method to check this condition. Implement your own renderer method to render various other graphs. \ No newline at end of file diff --git a/v2/pom.xml b/v2/pom.xml deleted file mode 100644 index ab68db1..0000000 --- a/v2/pom.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - 4.0.0 - io.onclave - NSGA-II - 2.0-SNAPSHOT - jar - - - org.jfree - jfreechart - 1.5.0 - - - org.jfree - jcommon - 1.0.24 - - - - UTF-8 - 1.8 - 1.8 - - \ No newline at end of file diff --git a/v2/src/main/java/io/onclave/nsga/ii/algorithm/Algorithm.java b/v2/src/main/java/io/onclave/nsga/ii/algorithm/Algorithm.java deleted file mode 100644 index e3ff92e..0000000 --- a/v2/src/main/java/io/onclave/nsga/ii/algorithm/Algorithm.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This code / file / algorithm is completely free to use and modify as necessary. - * Any attribution is welcome and highly appriciated. - */ -package io.onclave.nsga.ii.algorithm; - -import io.onclave.nsga.ii.api.*; -import io.onclave.nsga.ii.datastructure.Population; -import org.jfree.ui.RefineryUtilities; - -import java.io.IOException; - -/** - * This is the starting point of the main NSGA-II algorithm. - * Run this class to get the desired output. - * - * @author Debabrata Acharya - * @version 2.0 - * @since 2.0 - */ -public class Algorithm { - - public static void main(String[] args) throws IOException { - - Configuration.configure(); - - GraphPlot multiPlotGraph = new GraphPlot(); - - Reporter.reportInitialParentPopulationGeneration(); - Reporter.reportGeneration(1); - - Population parent = NSGAII.preparePopulation(Synthesis.syntesizePopulation()); - Population child = Synthesis.synthesizeChild(parent); - Population combinedPopulation; - - for(int generation = 1; generation <= Configuration.GENERATIONS; generation++) { - - Reporter.reportGeneration(generation + 1); - - combinedPopulation = NSGAII.preparePopulation(Synthesis.createCombinedPopulation(parent, child)); - parent = NSGAII.getChildFromCombinedPopulation(combinedPopulation); - child = Synthesis.synthesizeChild(parent); - - multiPlotGraph.prepareMultipleDataset(child, generation, "gen. " + generation); - } - - Reporter.reportGraphPlotAlert(); - Reporter.render2DGraph(child); - - /** - * the plotted and rendered chart/graph is viewed to the user. - */ - multiPlotGraph.configureMultiplePlotter(Configuration.getXaxisTitle(), Configuration.getYaxisTitle(), "All Pareto"); - multiPlotGraph.pack(); - RefineryUtilities.centerFrameOnScreen(multiPlotGraph); - multiPlotGraph.setVisible(true); - - Reporter.reportAlgorithmEnd(); - } -} diff --git a/v2/src/main/java/io/onclave/nsga/ii/api/Configuration.java b/v2/src/main/java/io/onclave/nsga/ii/api/Configuration.java deleted file mode 100644 index d6d480b..0000000 --- a/v2/src/main/java/io/onclave/nsga/ii/api/Configuration.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * This code / file / algorithm is completely free to use and modify as necessary. - * Any attribution is welcome and highly appriciated. - */ -package io.onclave.nsga.ii.api; - -import io.onclave.nsga.ii.interfaces.IObjectiveFunction; -import io.onclave.nsga.ii.objectivefunction.SCH_1; -import io.onclave.nsga.ii.objectivefunction.SCH_2; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.List; - -/** - * - * - * @author Debabrata Acharya - * @version 2.0 - * @since 2.0 - */ -public class Configuration { - - /** - * this class is never supposed to be instantiated - */ - private Configuration() {} - - public static int POPULATION_SIZE = 100; - public static int GENERATIONS = 25; - public static int CHROMOSOME_LENGTH = 20; - - public static String X_AXIS_TITLE = "X-AXIS"; - public static String Y_AXIS_TITLE = "Y-AXIS"; - - public static final double ACTUAL_MIN = 0; - public static double ACTUAL_MAX = 0; - public static final double NORMALIZED_MIN = 0; - public static final double NORMALIZED_MAX = 2; - public static final float CROSSOVER_PROBABILITY = 0.7f; - public static final float MUTATION_PROBABILITY = 0.03f; - - public static List objectives = null; - - public static void configure() { - - Configuration.buildObjectives(); - - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); - - Reporter.reportAlgorithmStart(); - - try { - - switch(bufferedReader.readLine()) { - - case "1": - - Configuration.setDefaultAxisTitles(); - Reporter.reportDefaultConfiguration(); - break; - case "2": - - Reporter.reportCustomConfigurationStartInput(); - - Configuration.setCustomConfiguration(bufferedReader); - Configuration.setCustomAxisTitles(bufferedReader); - - Reporter.reportCustomConfiguration(); - - break; - default: Reporter.reportWrongInput(); break; - } - - Configuration.ACTUAL_MAX = Math.pow(2, CHROMOSOME_LENGTH) - 1; - } catch(IOException e) { - Reporter.reportIOException(); - } - } - - public static void setObjectives(List objectives) { - Configuration.objectives = objectives; - } - - public static String getXaxisTitle() { - return Configuration.X_AXIS_TITLE; - } - - public static String getYaxisTitle() { - return Configuration.Y_AXIS_TITLE; - } - - private static void setDefaultAxisTitles() { - - Configuration.X_AXIS_TITLE = Configuration.objectives.get(0).objectiveFunctionTitle(); - Configuration.Y_AXIS_TITLE = Configuration.objectives.get(1).objectiveFunctionTitle(); - } - - private static void setCustomAxisTitles(final BufferedReader bufferedReader) throws IOException { - - System.out.print("\nDo you want to provide axis titles? (y/n): "); - - switch(bufferedReader.readLine()) { - - case "y": - - System.out.print("\nEnter X-Axis Title: "); - Configuration.X_AXIS_TITLE = bufferedReader.readLine(); - - System.out.print("Enter Y-Axis Title: "); - Configuration.Y_AXIS_TITLE = bufferedReader.readLine(); - - break; - case "n": break; - default: Reporter.reportWrongInput(); break; - } - } - - private static void setCustomConfiguration(final BufferedReader bufferedReader) throws IOException { - - System.out.print("Enter the chromosome length to work with: "); - Configuration.CHROMOSOME_LENGTH = Integer.parseInt(bufferedReader.readLine()); - - System.out.print("Enter population size: "); - Configuration.POPULATION_SIZE = Integer.parseInt(bufferedReader.readLine()); - - System.out.print("Enter number of generations: "); - Configuration.GENERATIONS = Integer.parseInt(bufferedReader.readLine()); - } - - /** - * this method sets the objective functions over which the algorithm is to operate. - * it is a list of IObjectionFunction objects. - */ - private static void buildObjectives() { - - List newObjectives = new ArrayList<>(); - - newObjectives.add(0, new SCH_1()); - newObjectives.add(1, new SCH_2()); - - Configuration.setObjectives(newObjectives); - } -} diff --git a/v2/src/main/java/io/onclave/nsga/ii/api/GraphPlot.java b/v2/src/main/java/io/onclave/nsga/ii/api/GraphPlot.java deleted file mode 100644 index 462da96..0000000 --- a/v2/src/main/java/io/onclave/nsga/ii/api/GraphPlot.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * This code / file / algorithm is completely free to use and modify as necessary. - * Any attribution is welcome and highly appriciated. - */ -package io.onclave.nsga.ii.api; - -import io.onclave.nsga.ii.datastructure.Population; -import org.jfree.chart.ChartFactory; -import org.jfree.chart.ChartPanel; -import org.jfree.chart.JFreeChart; -import org.jfree.chart.plot.PlotOrientation; -import org.jfree.chart.plot.XYPlot; -import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; -import org.jfree.data.xy.XYSeries; -import org.jfree.data.xy.XYSeriesCollection; -import org.jfree.ui.ApplicationFrame; - -import java.awt.*; -import java.util.Random; - -/** - * this class is the under-the-hood service layer for generating the graphs using jFreeCharts library. - * - * @author Debabrata Acharya - * @version 2.0 - * @since 2.0 - */ -public class GraphPlot extends ApplicationFrame { - - private final static XYSeriesCollection DATASET = new XYSeriesCollection(); - private final static XYSeriesCollection MULTIPLE_DATASET = new XYSeriesCollection(); - private final static XYLineAndShapeRenderer MULTIPLE_RENDERER = new XYLineAndShapeRenderer(); - - private final static String APPLICATION_TITLE = "NSGA-II"; - private static String KEY = "Pareto Front"; - private static String GRAPH_TITLE = "PARETO FRONT"; - private static float STROKE_THICKNESS = 2.0f; - private static int DIMENSION_X = 800; - private static int DIMENSION_Y = 600; - private static Paint COLOR = Color.RED; - - private static final Random RANDOM = new Random(); - - public GraphPlot() { - super(GraphPlot.APPLICATION_TITLE); - } - - public GraphPlot(Population population) { - - super(GraphPlot.APPLICATION_TITLE); - this.createDataset(population); - } - - public void prepareMultipleDataset(final Population population, final int datasetIndex, final String key) { - - this.createDataset(population, key, GraphPlot.MULTIPLE_DATASET); - - GraphPlot.MULTIPLE_RENDERER.setSeriesPaint(datasetIndex, new Color(GraphPlot.RANDOM.nextFloat(), GraphPlot.RANDOM.nextFloat(), GraphPlot.RANDOM.nextFloat())); - GraphPlot.MULTIPLE_RENDERER.setSeriesStroke(datasetIndex, new BasicStroke(GraphPlot.STROKE_THICKNESS)); - } - - public void configureMultiplePlotter(final String x_axis, final String y_axis, final String graphTitle) { - - JFreeChart xyLineChart = ChartFactory.createXYLineChart(graphTitle, x_axis, y_axis, GraphPlot.MULTIPLE_DATASET, PlotOrientation.VERTICAL, true, true, false); - ChartPanel chartPanel = new ChartPanel(xyLineChart); - - chartPanel.setPreferredSize(new java.awt.Dimension(GraphPlot.DIMENSION_X, GraphPlot.DIMENSION_Y)); - - final XYPlot plot = xyLineChart.getXYPlot(); - - plot.setRenderer(GraphPlot.MULTIPLE_RENDERER); - setContentPane(chartPanel); - } - - public void configurePlotter(final String x_axis, final String y_axis) { - - JFreeChart xyLineChart = ChartFactory.createXYLineChart(GraphPlot.GRAPH_TITLE, x_axis, y_axis, GraphPlot.DATASET, PlotOrientation.VERTICAL, true, true, false); - ChartPanel chartPanel = new ChartPanel(xyLineChart); - - chartPanel.setPreferredSize(new java.awt.Dimension(GraphPlot.DIMENSION_X, GraphPlot.DIMENSION_Y)); - - final XYPlot plot = xyLineChart.getXYPlot(); - - XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); - - renderer.setSeriesPaint(0, GraphPlot.COLOR); - renderer.setSeriesStroke(0, new BasicStroke(GraphPlot.STROKE_THICKNESS)); - - plot.setRenderer(renderer); - setContentPane(chartPanel); - } - - private void createDataset(final Population population, String key, XYSeriesCollection dataset) { - - final XYSeries paretoFront = new XYSeries(key); - - population.getPopulace().stream().forEach((chromosome) -> { - System.out.println(Configuration.objectives.get(0).objectiveFunctionTitle() + " : " + chromosome.getObjectiveValues().get(0) + " | " - + Configuration.objectives.get(1).objectiveFunctionTitle() + " : " + chromosome.getObjectiveValues().get(1)); - }); - - population.getPopulace().stream().forEach((chromosome) -> { paretoFront.add(chromosome.getObjectiveValues().get(0), chromosome.getObjectiveValues().get(1)); }); - - dataset.addSeries(paretoFront); - } - - private void createDataset(final Population population) { - this.createDataset(population, KEY); - } - - private void createDataset(final Population population, String key) { - this.createDataset(population, key, DATASET); - } -} diff --git a/v2/src/main/java/io/onclave/nsga/ii/api/NSGAII.java b/v2/src/main/java/io/onclave/nsga/ii/api/NSGAII.java deleted file mode 100644 index bc2b321..0000000 --- a/v2/src/main/java/io/onclave/nsga/ii/api/NSGAII.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * This code / file / algorithm is completely free to use and modify as necessary. - * Any attribution is welcome and highly appriciated. - */ -package io.onclave.nsga.ii.api; - -import io.onclave.nsga.ii.datastructure.Chromosome; -import io.onclave.nsga.ii.datastructure.Population; - -import java.util.ArrayList; -import java.util.List; - -/** - * - * - * @author Debabrata Acharya - * @version 2.0 - * @since 2.0 - */ -public class NSGAII { - - /** - * this class is never supposed to be instantiated - */ - private NSGAII() {} - - private static final int DOMINANT = 1; - private static final int INFERIOR = 2; - private static final int NON_DOMINATED = 3; - - public static Population preparePopulation(final Population population) { - - Chromosome[] populace = population.getPopulace().toArray(new Chromosome[population.getPopulace().size()]); - - NSGAII.fastNonDominatedSort(populace); - NSGAII.crowdingDistanceAssignment(populace); - - Service.randomizedQuickSortForRank(population.getPopulace(), 0, populace.length - 1); - - return population; - } - - public static Population getChildFromCombinedPopulation(final Population combinedPopulation) { - - int lastNonDominatedSetRank = combinedPopulation.getPopulace().get(Configuration.POPULATION_SIZE - 1).getRank(); - List populace = new ArrayList<>(); - - Service.sortForCrowdingDistance(combinedPopulation.getPopulace(), lastNonDominatedSetRank); - - for(int i = 0; i < Configuration.POPULATION_SIZE; i++) populace.add(combinedPopulation.getPopulace().get(i)); - - return new Population(populace); - } - - private static void fastNonDominatedSort(final Chromosome[] populace) { - - for(Chromosome chromosome : populace) chromosome.reset(); - - for(int i = 0; i < populace.length - 1; i++) { - - for(int j = i + 1; j < populace.length; j++) { - - switch(NSGAII.dominates(populace[i], populace[j])) { - - case NSGAII.DOMINANT: - - populace[i].setDominatedChromosome(populace[j]); - populace[j].incrementDominationCount(1); - - break; - - case NSGAII.INFERIOR: - - populace[i].incrementDominationCount(1); - populace[j].setDominatedChromosome(populace[i]); - - break; - - case NSGAII.NON_DOMINATED: break; - } - } - - if(populace[i].getDominationCount() == 0) populace[i].setRank(1); - } - - if(populace[populace.length - 1].getDominationCount() == 0) populace[populace.length - 1].setRank(1); - - for(int i = 0; i < populace.length; i++) { - - for(Chromosome chromosome : populace[i].getDominatedChromosomes()) { - - chromosome.incrementDominationCount(-1); - - if(chromosome.getDominationCount() == 0) chromosome.setRank(populace[i].getRank() + 1); - } - } - } - - private static void crowdingDistanceAssignment(final Chromosome[] nondominatedChromosomes) { - - int size = nondominatedChromosomes.length; - - for(int i = 0; i < Configuration.objectives.size(); i++) { - - Service.sortAgainstObjective(nondominatedChromosomes, i); - - nondominatedChromosomes[0].setCrowdingDistance(Double.MAX_VALUE); - nondominatedChromosomes[size - 1].setCrowdingDistance(Double.MAX_VALUE); - - double maxObjectiveValue = Service.selectMaximumObjectiveValue(nondominatedChromosomes, i); - double minObjectiveValue = Service.selectMinimumObjectiveValue(nondominatedChromosomes, i); - - for(int j = 1; j < size - 1; j++) if(nondominatedChromosomes[j].getCrowdingDistance() < Double.MAX_VALUE) nondominatedChromosomes[j].setCrowdingDistance( - nondominatedChromosomes[j].getCrowdingDistance() + ( - (nondominatedChromosomes[j + 1].getObjectiveValues().get(i) - nondominatedChromosomes[j - 1].getObjectiveValues().get(i)) / (maxObjectiveValue - minObjectiveValue) - ) - ); - - } - } - - private static int dominates(final Chromosome chromosome1, final Chromosome chromosome2) { - - if(NSGAII.isDominant(chromosome1, chromosome2)) return NSGAII.DOMINANT; - else if(NSGAII.isDominant(chromosome2, chromosome1)) return NSGAII.INFERIOR; - else return NSGAII.NON_DOMINATED; - } - - private static boolean isDominant(final Chromosome chromosome1, final Chromosome chromosome2) { - - boolean isDominant = true; - boolean atleastOneIsLarger = false; - - for(int i = 0; i < Configuration.objectives.size(); i++) { - - if(chromosome1.getObjectiveValues().get(i) < chromosome2.getObjectiveValues().get(i)) { - - isDominant = false; - break; - } else if(!atleastOneIsLarger && (chromosome1.getObjectiveValues().get(i) > chromosome2.getObjectiveValues().get(i))) atleastOneIsLarger = true; - } - - return isDominant && atleastOneIsLarger; - } -} diff --git a/v2/src/main/java/io/onclave/nsga/ii/api/Reporter.java b/v2/src/main/java/io/onclave/nsga/ii/api/Reporter.java deleted file mode 100644 index 7c7a37b..0000000 --- a/v2/src/main/java/io/onclave/nsga/ii/api/Reporter.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * This code / file / algorithm is completely free to use and modify as necessary. - * Any attribution is welcome and highly appriciated. - */ -package io.onclave.nsga.ii.api; - -import io.onclave.nsga.ii.datastructure.Population; -import org.jfree.ui.RefineryUtilities; - -/** - * - * - * @author Debabrata Acharya - * @version 2.0 - * @since 2.0 - */ -public class Reporter { - - /** - * this class is never supposed to be instantiated - */ - private Reporter() {} - - public static void reportInitialParentPopulationGeneration() { - - System.out.println("\n\n============================================================="); - System.out.println("CREATING INITIAL PARENT POPULATION"); - System.out.println("=============================================================\n\n"); - } - - public static void reportGeneration(int generation) { - - System.out.println("\n\n============================================================="); - System.out.println("GENERATION : " + generation); - System.out.println("=============================================================\n\n"); - } - - public static void reportAlgorithmStart() { - - System.out.println("\n\n============================================================="); - System.out.println("RUNNING ALGORITHM"); - System.out.println("=============================================================\n\nPlease provide configuration(s) / settings - Press:"); - System.out.println("1 - To use default configuration(s).\n2 - To provide custom configuration(s)."); - System.out.print("\n: "); - } - - public static void reportDefaultConfiguration() { - - System.out.println("\n\nUsing default configuration(s) . . .\n"); - Reporter.reportConfiguration(); - } - - public static void reportCustomConfigurationStartInput() { - System.out.println("\n\nPlease provide your required configuration(s) . . .\n"); - } - - public static void reportWrongInput() { - System.out.println("\n\nWrong input provided. Program will now exit!\n\n"); - } - - public static void reportCustomConfiguration() { - - System.out.println("\nUsing custom configuration(s) . . .\n"); - Reporter.reportConfiguration(); - } - - public static void reportConfiguration() { - - System.out.println("CHROMOSOME LENGTH TO WORK WITH : " + Configuration.CHROMOSOME_LENGTH); - System.out.println("POPULATION SIZE : " + Configuration.POPULATION_SIZE); - System.out.println("NUMBER OF GENERATIONS : " + Configuration.GENERATIONS); - System.out.println("X-AXIS : " + Configuration.X_AXIS_TITLE); - System.out.println("Y-AXIS : " + Configuration.Y_AXIS_TITLE); - } - - public static void reportIOException() { - System.out.println("\n\nInput / Output Exception caught. Program will now exit!\n\n"); - } - - public static void reportAlgorithmEnd() { - - System.out.println("\n\n============================================================="); - System.out.println("ALGORITHM ENDED SUCCESSFULLY"); - System.out.println("=============================================================\n\n"); - } - - public static void reportGraphPlotAlert() { - System.out.println("\n\n============================================================="); - System.out.println("CHECK PARETO FRONT OUTPUT"); - System.out.println("=============================================================\n\n"); - } - - public static void render2DGraph(final Population population) { - - if(Configuration.objectives.size() > 2) { - System.out.println("\n\nThis Implementation has more than 2 objectives and cannot be plotted on a 2D graph. Either minimize objectives to 2, or use other plotting implemenataions.\n\n"); return; - } - - GraphPlot graph = new GraphPlot(population); - - graph.configurePlotter(Configuration.getXaxisTitle(), Configuration.getYaxisTitle()); - graph.pack(); - - RefineryUtilities.centerFrameOnScreen(graph); - - graph.setVisible(true); - } -} diff --git a/v2/src/main/java/io/onclave/nsga/ii/api/Service.java b/v2/src/main/java/io/onclave/nsga/ii/api/Service.java deleted file mode 100644 index a455154..0000000 --- a/v2/src/main/java/io/onclave/nsga/ii/api/Service.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * This code / file / algorithm is completely free to use and modify as necessary. - * Any attribution is welcome and highly appriciated. - */ -package io.onclave.nsga.ii.api; - -import io.onclave.nsga.ii.datastructure.Allele; -import io.onclave.nsga.ii.datastructure.Chromosome; - -import java.util.List; -import java.util.concurrent.ThreadLocalRandom; - -/** - * - * - * @author Debabrata Acharya - * @version 2.0 - * @since 2.0 - */ -public class Service { - - /** - * this class is never supposed to be instantiated - */ - private Service() {} - - /** - * fitness is calculated using min-max normalization - * - * @param geneticCode the genetic code whose fitness is to be calculated - * @return the corresponding calculated fitness - */ - public static double calculateFitness(Allele[] geneticCode) { - return minMaxNormalization(decodeGeneticCode(geneticCode)); - } - - public static void randomizedQuickSortForRank(final List populace, final int head, final int tail) { - - if(head < tail) { - - int pivot = Service.randomizedPartitionForRank(populace, head, tail); - - Service.randomizedQuickSortForRank(populace, head, pivot - 1); - Service.randomizedQuickSortForRank(populace, pivot + 1, tail); - } - } - - public static void sortForCrowdingDistance(final List populace, final int lastNonDominatedSetRank) { - - int rankStartIndex = -1; - int rankEndIndex = -1; - - for(int i = 0; i < populace.size(); i++) - if((rankStartIndex < 0) && (populace.get(i).getRank() == lastNonDominatedSetRank)) rankStartIndex = i; - else if((rankStartIndex >= 0) && (populace.get(i).getRank() == lastNonDominatedSetRank)) rankEndIndex = i; - - Service.randomizedQuickSortForCrowdingDistance(populace, rankStartIndex, rankEndIndex); - } - - public static void sortAgainstObjective(final Chromosome[] populace, int objectiveIndex) { - Service.randomizedQuickSortAgainstObjective(populace, 0, populace.length - 1, objectiveIndex); - } - - public static double selectMaximumObjectiveValue(final Chromosome[] populace, int objectiveIndex) { - - double result = populace[0].getObjectiveValues().get(objectiveIndex); - - for(Chromosome chromosome : populace) if(chromosome.getObjectiveValues().get(objectiveIndex) > result) result = chromosome.getObjectiveValues().get(objectiveIndex); - - return result; - } - - public static double selectMinimumObjectiveValue(final Chromosome[] populace, int objectiveIndex) { - - double result = populace[0].getObjectiveValues().get(objectiveIndex); - - for(Chromosome chromosome : populace) if(chromosome.getObjectiveValues().get(objectiveIndex) < result) result = chromosome.getObjectiveValues().get(objectiveIndex); - - return result; - } - - /** - * this method decodes the genetic code that is represented as a string of binary values, converted into - * decimal value. - * - * @param geneticCode the genetic code as an array of Allele. Refer Allele.java for more information - * @return the decimal value of the corresponding binary string. - */ - private static double decodeGeneticCode(final Allele[] geneticCode) { - - double value = 0; - String binaryString = ""; - - for(Allele bit : geneticCode) binaryString += bit.getGene() ? "1" : "0"; - for(int i = 0; i < binaryString.length(); i++) - if(binaryString.charAt(i) == '1') - value += Math.pow(2, binaryString.length() - 1 - i); - - return value; - } - - /** - * an implementation of min-max normalization - * - * @param value the value that is to be normalized - * @return the normalized value - */ - private static double minMaxNormalization(final double value) { - return (((value - Configuration.ACTUAL_MIN) / (Configuration.ACTUAL_MAX - Configuration.ACTUAL_MIN)) * (Configuration.NORMALIZED_MAX - Configuration.NORMALIZED_MIN)) + Configuration.NORMALIZED_MIN; - } - - private static int randomizedPartitionForRank(final List populace, final int head, final int tail) { - - Service.swapForRank(populace, head, ThreadLocalRandom.current().nextInt(head, tail + 1)); - - return Service.partitionForRank(populace, head, tail); - } - - private static void swapForRank(final List populace, final int firstIndex, final int secondIndex) { - - Chromosome temporary = populace.get(firstIndex); - - populace.set(firstIndex, populace.get(secondIndex)); - populace.set(secondIndex, temporary); - } - - private static int partitionForRank(final List populace, final int head, final int tail) { - - int pivot = populace.get(tail).getRank(); - int pivotIndex = head; - - for(int j = head; j < tail; j++) { - - if(populace.get(j).getRank() <= pivot) { - - Service.swapForRank(populace, pivotIndex, j); - ++pivotIndex; - } - } - - Service.swapForRank(populace, pivotIndex, tail); - - return pivotIndex; - } - - private static void randomizedQuickSortForCrowdingDistance(final List populace, final int head, final int tail) { - - if(head < tail) { - - int pivot = Service.randomizedPartitionForCrowdingDistance(populace, head, tail); - - Service.randomizedQuickSortForCrowdingDistance(populace, head, pivot - 1); - Service.randomizedQuickSortForCrowdingDistance(populace, pivot + 1, tail); - } - } - - private static int randomizedPartitionForCrowdingDistance(final List populace, final int head, final int tail) { - - Service.swapForCrowdingDistance(populace, head, ThreadLocalRandom.current().nextInt(head, tail + 1)); - - return Service.partitionForCrowdingDistance(populace, head, tail); - } - - private static void swapForCrowdingDistance(final List populace, final int firstIndex, final int secondIndex) { - - Chromosome temporary = populace.get(firstIndex); - - populace.set(firstIndex, populace.get(secondIndex)); - populace.set(secondIndex, temporary); - } - - private static int partitionForCrowdingDistance(final List populace, final int head, final int tail) { - - double pivot = populace.get(tail).getCrowdingDistance(); - int pivotIndex = head; - - for(int j = head; j < tail; j++) { - - if(populace.get(j).getCrowdingDistance() >= pivot) { - - Service.swapForCrowdingDistance(populace, pivotIndex, j); - ++pivotIndex; - } - } - - Service.swapForCrowdingDistance(populace, pivotIndex, tail); - - return pivotIndex; - } - - private static void randomizedQuickSortAgainstObjective(final Chromosome[] populace, final int head, final int tail, final int objectiveIndex) { - - if(head < tail) { - - int pivot = Service.randomizedPartitionAgainstObjective(populace, head, tail, objectiveIndex); - - Service.randomizedQuickSortAgainstObjective(populace, head, pivot - 1, objectiveIndex); - Service.randomizedQuickSortAgainstObjective(populace, pivot + 1, tail, objectiveIndex); - } - } - - private static int randomizedPartitionAgainstObjective(final Chromosome[] populace, final int head, final int tail, final int objectiveIndex) { - - Service.swapAgainstObjective(populace, head, ThreadLocalRandom.current().nextInt(head, tail + 1)); - - return Service.partitionAgainstObjective(populace, head, tail, objectiveIndex); - } - - private static void swapAgainstObjective(final Chromosome[] populace, final int firstIndex, final int secondIndex) { - - Chromosome temporary = populace[firstIndex]; - populace[firstIndex] = populace[secondIndex]; - populace[secondIndex] = temporary; - } - - private static int partitionAgainstObjective(final Chromosome[] populace, final int head, final int tail, final int objectiveIndex) { - - double pivot = populace[tail].getObjectiveValues().get(objectiveIndex); - int pivotIndex = head; - - for(int j = head; j < tail; j++) { - - if(populace[j].getObjectiveValues().get(objectiveIndex) <= pivot) { - - Service.swapAgainstObjective(populace, pivotIndex, j); - ++pivotIndex; - } - } - - Service.swapAgainstObjective(populace, pivotIndex, tail); - - return pivotIndex; - } -} diff --git a/v2/src/main/java/io/onclave/nsga/ii/api/Synthesis.java b/v2/src/main/java/io/onclave/nsga/ii/api/Synthesis.java deleted file mode 100644 index 5179e3d..0000000 --- a/v2/src/main/java/io/onclave/nsga/ii/api/Synthesis.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * This code / file / algorithm is completely free to use and modify as necessary. - * Any attribution is welcome and highly appriciated. - */ -package io.onclave.nsga.ii.api; - -import io.onclave.nsga.ii.datastructure.Allele; -import io.onclave.nsga.ii.datastructure.Chromosome; -import io.onclave.nsga.ii.datastructure.Population; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; -import java.util.concurrent.ThreadLocalRandom; - -/** - * - * - * @author Debabrata Acharya - * @version 2.0 - * @since 2.0 - */ -public class Synthesis { - - /** - * this class is never supposed to be instantiated - */ - private Synthesis() {} - - private static final Random LOCAL_RANDOM = new Random(); - - /** - * depending on the settings available in the Configuration.java file, this method synthesizes a - * random population of chromosomes with pseudo-randomly generated genetic code for each chromosome. - * - * @return a randomly generated population - */ - public static Population syntesizePopulation() { - - List populace = new ArrayList<>(); - - /** - * the number of chromosomes in the population is received from the Configuration.java file - */ - for(int i = 0; i < Configuration.POPULATION_SIZE; i++) { - - Chromosome chromosome = new Chromosome(Synthesis.synthesizeGeneticCode(Configuration.CHROMOSOME_LENGTH)); - populace.add(chromosome); - } - - return new Population(populace); - } - - /** - * a genetic code as an array of Alleles is synthesized. - * refer Allele.java for more information. - * - * @param length the required length of the genetic code. - * @return the synthesized genetic code. - */ - public static Allele[] synthesizeGeneticCode(final int length) { - - Allele[] geneticCode = new Allele[length]; - - for(int i = 0; i < length; i++) geneticCode[i] = Synthesis.synthesizeAllele(); - - return geneticCode; - } - - /** - * an allele object with a randomly selected boolean gene value is synthesized. - * - * @return a randomly generated Allele object - */ - public static Allele synthesizeAllele() { - return new Allele(LOCAL_RANDOM.nextBoolean()); - } - - /** - * a child population of the same size as the parent is synthesized from the parent population - * - * @param parent the parent population object - * @return a child population synthesized from the parent population - */ - public static Population synthesizeChild(Population parent) { - - List populace = new ArrayList<>(); - - while(populace.size() < Configuration.POPULATION_SIZE) - if((Configuration.POPULATION_SIZE - populace.size()) == 1) populace.add(Synthesis.mutation(Synthesis.crowdedBinaryTournamentSelection(parent))); - else for(Chromosome chromosome : Synthesis.crossover(Synthesis.crowdedBinaryTournamentSelection(parent), Synthesis.crowdedBinaryTournamentSelection(parent))) populace.add(Synthesis.mutation(chromosome)); - - return new Population(populace); - } - - public static Population createCombinedPopulation(final Population parent, final Population child) { - - List populace = parent.getPopulace(); - - for(Chromosome chromosome : child.getPopulace()) populace.add(chromosome); - - return new Population(populace); - } - - private static Chromosome crowdedBinaryTournamentSelection(final Population population) { - - Chromosome participant1 = population.getPopulace().get(ThreadLocalRandom.current().nextInt(0, Configuration.POPULATION_SIZE)); - Chromosome participant2 = population.getPopulace().get(ThreadLocalRandom.current().nextInt(0, Configuration.POPULATION_SIZE)); - - if(participant1.getRank() < participant2.getRank()) return participant1; - else if(participant1.getRank() == participant2.getRank()) { - if(participant1.getCrowdingDistance() > participant2.getCrowdingDistance()) return participant1; - else if(participant1.getCrowdingDistance() < participant2.getCrowdingDistance()) return participant2; - else return Synthesis.LOCAL_RANDOM.nextBoolean() ? participant1 : participant2; - } else return participant2; - } - - private static Chromosome mutation(final Chromosome chromosome) { - return ((Synthesis.LOCAL_RANDOM.nextFloat() <= Configuration.MUTATION_PROBABILITY) ? Synthesis.singlePointMutation(chromosome) : chromosome); - } - - private static Chromosome singlePointMutation(final Chromosome chromosome) { - - Allele[] geneticCode = new Allele[Configuration.CHROMOSOME_LENGTH]; - - for(int i = 0; i < Configuration.CHROMOSOME_LENGTH; i++) - geneticCode[i] = new Allele((Synthesis.LOCAL_RANDOM.nextFloat() <= Configuration.MUTATION_PROBABILITY) ? !chromosome.getGeneticCode()[i].getGene() : chromosome.getGeneticCode()[i].getGene()); - - return (Synthesis.isGeneticCodeSimilar(geneticCode, chromosome.getGeneticCode()) ? chromosome.getCopy() : new Chromosome(geneticCode)); - } - - private static Chromosome[] crossover(final Chromosome chromosome1, final Chromosome chromosome2) { - - if(Synthesis.LOCAL_RANDOM.nextFloat() <= Configuration.CROSSOVER_PROBABILITY) return new Chromosome[] { Synthesis.uniformCrossover(chromosome1, chromosome2), Synthesis.uniformCrossover(chromosome1, chromosome2) }; - else return new Chromosome[] { chromosome1.getCopy(), chromosome2.getCopy() }; - } - - private static Chromosome uniformCrossover(final Chromosome chromosome1, final Chromosome chromosome2) { - - Allele[] geneticCode = new Allele[Configuration.CHROMOSOME_LENGTH]; - - for(int i = 0; i < Configuration.CHROMOSOME_LENGTH; i++) { - - switch(Synthesis.LOCAL_RANDOM.nextInt(2)) { - - case 0: geneticCode[i] = new Allele(chromosome1.getGeneticCode()[i].getGene()); break; - case 1: geneticCode[i] = new Allele(chromosome2.getGeneticCode()[i].getGene()); break; - } - } - - return new Chromosome(geneticCode); - } - - private static boolean isGeneticCodeSimilar(final Allele[] geneticCode1, final Allele[] geneticCode2) { - for(int i = 0; i < Configuration.CHROMOSOME_LENGTH; i++) if(geneticCode1[i].getGene() != geneticCode2[i].getGene()) return false; return true; - } -} diff --git a/v2/src/main/java/io/onclave/nsga/ii/datastructure/Allele.java b/v2/src/main/java/io/onclave/nsga/ii/datastructure/Allele.java deleted file mode 100644 index f886544..0000000 --- a/v2/src/main/java/io/onclave/nsga/ii/datastructure/Allele.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This code / file / algorithm is completely free to use and modify as necessary. - * Any attribution is welcome and highly appriciated. - */ -package io.onclave.nsga.ii.datastructure; - -/** - * - * - * @author Debabrata Acharya - * @version 2.0 - * @since 2.0 - */ -public class Allele { - - private final boolean gene; - - public Allele(final boolean gene) { - this.gene = gene; - } - - public boolean getGene() { - return gene; - } - - @Override - public String toString() { - return (this.gene ? "1" : "0"); - } -} diff --git a/v2/src/main/java/io/onclave/nsga/ii/datastructure/Chromosome.java b/v2/src/main/java/io/onclave/nsga/ii/datastructure/Chromosome.java deleted file mode 100644 index 6af1d36..0000000 --- a/v2/src/main/java/io/onclave/nsga/ii/datastructure/Chromosome.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * This code / file / algorithm is completely free to use and modify as necessary. - * Any attribution is welcome and highly appriciated. - */ -package io.onclave.nsga.ii.datastructure; - -import io.onclave.nsga.ii.api.Configuration; -import io.onclave.nsga.ii.api.Service; - -import java.util.ArrayList; -import java.util.List; - -/** - * - * - * @author Debabrata Acharya - * @version 2.0 - * @since 2.0 - */ -public class Chromosome { - - private List dominatedChromosomes = new ArrayList<>(); - private final List objectiveValues = new ArrayList<>(); - private double crowdingDistance = 0; - private final Allele[] geneticCode; - private int dominationCount = 0; - private double fitness; - private int rank = 1; - - private Chromosome(final Chromosome chromosome) { - - this.geneticCode = new Allele[Configuration.CHROMOSOME_LENGTH]; - - for(int i = 0; i < Configuration.CHROMOSOME_LENGTH; i++) this.geneticCode[i] = new Allele(chromosome.getGeneticCode()[i].getGene()); - for(int i = 0; i < Configuration.objectives.size(); i++) this.objectiveValues.add(chromosome.getObjectiveValues().get(i)); - } - - public Chromosome(final Allele[] geneticCode) { - - this.geneticCode = geneticCode; - this.fitness = Service.calculateFitness(this.geneticCode); - - for(int index = 0; index < Configuration.objectives.size(); index++) { - this.objectiveValues.add(index, Configuration.objectives.get(index).getObjectiveValue(this.fitness)); - } - } - - public void reset() { - - this.dominationCount = 0; - this.rank = Integer.MAX_VALUE; - this.dominatedChromosomes = new ArrayList<>(); - } - - public Chromosome getCopy() { - return new Chromosome(this); - } - - public void setDominatedChromosome(final Chromosome chromosome) { - this.dominatedChromosomes.add(chromosome); - } - - public void incrementDominationCount(int incrementValue) { - this.dominationCount += incrementValue; - } - - public Allele[] getGeneticCode() { - return geneticCode; - } - - public List getObjectiveValues() { - return objectiveValues; - } - - public int getRank() { - return rank; - } - - public void setRank(int rank) { - this.rank = rank; - } - - public double getCrowdingDistance() { - return crowdingDistance; - } - - public void setCrowdingDistance(double crowdingDistance) { - this.crowdingDistance = crowdingDistance; - } - - public int getDominationCount() { - return dominationCount; - } - - public List getDominatedChromosomes() { - return dominatedChromosomes; - } - - @Override - public String toString() { - return null; - } -} diff --git a/v2/src/main/java/io/onclave/nsga/ii/datastructure/Population.java b/v2/src/main/java/io/onclave/nsga/ii/datastructure/Population.java deleted file mode 100644 index 7dd6a35..0000000 --- a/v2/src/main/java/io/onclave/nsga/ii/datastructure/Population.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * This code / file / algorithm is completely free to use and modify as necessary. - * Any attribution is welcome and highly appriciated. - */ -package io.onclave.nsga.ii.datastructure; - -import java.util.List; - -/** - * - * - * @author Debabrata Acharya - * @version 2.0 - * @since 2.0 - */ -public class Population { - - private final List populace; - - public Population(final List populace) { - this.populace = populace; - } - - public List getPopulace() { - return populace; - } -} diff --git a/v2/src/main/java/io/onclave/nsga/ii/interfaces/IObjectiveFunction.java b/v2/src/main/java/io/onclave/nsga/ii/interfaces/IObjectiveFunction.java deleted file mode 100644 index 87569c8..0000000 --- a/v2/src/main/java/io/onclave/nsga/ii/interfaces/IObjectiveFunction.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * This code / file / algorithm is completely free to use and modify as necessary. - * Any attribution is welcome and highly appriciated. - */ -package io.onclave.nsga.ii.interfaces; - -/** - * - * - * @author Debabrata Acharya - * @version 2.0 - * @since 2.0 - */ -public interface IObjectiveFunction { - - public String objectiveFunctionTitle(); - public double getObjectiveValue(double fitness); -} diff --git a/v2/src/main/java/io/onclave/nsga/ii/objectivefunction/SCH_1.java b/v2/src/main/java/io/onclave/nsga/ii/objectivefunction/SCH_1.java deleted file mode 100644 index 00907ae..0000000 --- a/v2/src/main/java/io/onclave/nsga/ii/objectivefunction/SCH_1.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This code / file / algorithm is completely free to use and modify as necessary. - * Any attribution is welcome and highly appriciated. - */ -package io.onclave.nsga.ii.objectivefunction; - -import io.onclave.nsga.ii.interfaces.IObjectiveFunction; - -/** - * the SCH objective function [f(x) = x^2] - * - * @author Debabrata Acharya - * @version 2.0 - * @since 0.1 - */ -public class SCH_1 implements IObjectiveFunction { - - private static final String OBJECTIVE_TITLE = "pow(x, 2)"; - - @Override - public String objectiveFunctionTitle() { - return SCH_1.OBJECTIVE_TITLE; - } - - @Override - public double getObjectiveValue(final double fitness) { - return Math.pow(fitness, 2); - } - -} diff --git a/v2/src/main/java/io/onclave/nsga/ii/objectivefunction/SCH_2.java b/v2/src/main/java/io/onclave/nsga/ii/objectivefunction/SCH_2.java deleted file mode 100644 index b11f10d..0000000 --- a/v2/src/main/java/io/onclave/nsga/ii/objectivefunction/SCH_2.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This code / file / algorithm is completely free to use and modify as necessary. - * Any attribution is welcome and highly appriciated. - */ -package io.onclave.nsga.ii.objectivefunction; - -import io.onclave.nsga.ii.interfaces.IObjectiveFunction; - -/** - * the SCH objective function [f(x) = (x - 2)^2] - * - * @author Debabrata Acharya - * @version 2.0 - * @since 0.1 - */ -public class SCH_2 implements IObjectiveFunction { - - private static final String OBJECTIVE_TITLE = "pow(x - 2, 2)"; - - @Override - public String objectiveFunctionTitle() { - return SCH_2.OBJECTIVE_TITLE; - } - - @Override - public double getObjectiveValue(final double fitness) { - return Math.pow(fitness - 2d, 2); - } - -}