View Javadoc
1   package net.bmahe.genetics4j.samples;
2   
3   import java.io.File;
4   import java.io.IOException;
5   import java.util.BitSet;
6   import java.util.List;
7   import java.util.Objects;
8   
9   import org.apache.commons.cli.CommandLine;
10  import org.apache.commons.cli.CommandLineParser;
11  import org.apache.commons.cli.DefaultParser;
12  import org.apache.commons.cli.HelpFormatter;
13  import org.apache.commons.cli.Options;
14  import org.apache.commons.cli.ParseException;
15  import org.apache.commons.io.FileUtils;
16  import org.apache.logging.log4j.LogManager;
17  import org.apache.logging.log4j.Logger;
18  
19  import net.bmahe.genetics4j.core.EASystem;
20  import net.bmahe.genetics4j.core.EASystemFactory;
21  import net.bmahe.genetics4j.core.Genotype;
22  import net.bmahe.genetics4j.core.chromosomes.BitChromosome;
23  import net.bmahe.genetics4j.core.evolutionlisteners.EvolutionListeners;
24  import net.bmahe.genetics4j.core.postevaluationprocess.FitnessSharing;
25  import net.bmahe.genetics4j.core.spec.EAConfiguration;
26  import net.bmahe.genetics4j.core.spec.EAConfiguration.Builder;
27  import net.bmahe.genetics4j.core.spec.EAExecutionContext;
28  import net.bmahe.genetics4j.core.spec.EAExecutionContexts;
29  import net.bmahe.genetics4j.core.spec.EvolutionResult;
30  import net.bmahe.genetics4j.core.spec.chromosome.BitChromosomeSpec;
31  import net.bmahe.genetics4j.core.spec.combination.MultiPointCrossover;
32  import net.bmahe.genetics4j.core.spec.mutation.RandomMutation;
33  import net.bmahe.genetics4j.core.spec.selection.Tournament;
34  import net.bmahe.genetics4j.core.termination.Terminations;
35  import net.bmahe.genetics4j.core.util.BitChromosomeUtils;
36  import net.bmahe.genetics4j.extras.evolutionlisteners.CSVEvolutionListener;
37  import net.bmahe.genetics4j.extras.evolutionlisteners.ColumnExtractor;
38  
39  public class FitnessSharingExample {
40  
41  	final static public Logger logger = LogManager.getLogger(FitnessSharingExample.class);
42  
43  	final static public String PARAM_DEST_CSV_WITHOUT_SHARING = "w";
44  	final static public String LONG_PARAM_DEST_CSV_WITHOUT_SHARING = "without-sharing-dest";
45  
46  	final static public String PARAM_DEST_CSV_WITH_SHARING = "s";
47  	final static public String LONG_PARAM_DEST_CSV_WITH_SHARING = "with-sharing-dest";
48  
49  	final static public String PARAM_POPULATION_SIZE = "p";
50  	final static public String LONG_PARAM_POPULATION_SIZE = "population-size";
51  
52  	final static public String DEFAULT_DEST_CSV_WITHOUT_SHARING = "withoutFitnessSharing.csv";
53  	final static public String DEFAULT_DEST_CSV_WITH_SHARING = "withFitnessSharing.csv";
54  	final static public int DEFAULT_POPULATION_SIZE = 50;
55  
56  	public static void cliError(final Options options, final String errorMessage) {
57  		final HelpFormatter formatter = new HelpFormatter();
58  		logger.error(errorMessage);
59  		formatter.printHelp(FitnessSharingExample.class.getSimpleName(), options);
60  		System.exit(-1);
61  	}
62  
63  	public static int toPhenotype(final Genotype genotype) {
64  		Objects.requireNonNull(genotype);
65  
66  		final BitChromosome bitChromosome = genotype.getChromosome(0, BitChromosome.class);
67  		final BitSet individualBitSet = bitChromosome.getBitSet();
68  
69  		final long[] longArray = individualBitSet.toLongArray();
70  
71  		return longArray.length > 0 ? (int) longArray[0] : 0;
72  	}
73  
74  	public static void main(String[] args) throws IOException {
75  
76  		/**
77  		 * Parse CLI
78  		 */
79  
80  		final CommandLineParser parser = new DefaultParser();
81  
82  		final Options options = new Options();
83  		options.addOption(
84  				PARAM_DEST_CSV_WITHOUT_SHARING,
85  					LONG_PARAM_DEST_CSV_WITHOUT_SHARING,
86  					true,
87  					"destination csv file for the case without fitness sharing");
88  
89  		options.addOption(
90  				PARAM_DEST_CSV_WITH_SHARING,
91  					LONG_PARAM_DEST_CSV_WITH_SHARING,
92  					true,
93  					"destination csv file for the case with fitness sharing");
94  
95  		options.addOption(PARAM_POPULATION_SIZE, LONG_PARAM_POPULATION_SIZE, true, "Population size");
96  
97  		String csvFilenameWithoutSharing = DEFAULT_DEST_CSV_WITHOUT_SHARING;
98  		String csvFilenameWithSharing = DEFAULT_DEST_CSV_WITH_SHARING;
99  		int populationSize = DEFAULT_POPULATION_SIZE;
100 		try {
101 			final CommandLine line = parser.parse(options, args);
102 
103 			if (line.hasOption(PARAM_DEST_CSV_WITHOUT_SHARING)) {
104 				csvFilenameWithoutSharing = line.getOptionValue(PARAM_DEST_CSV_WITHOUT_SHARING);
105 			}
106 
107 			if (line.hasOption(PARAM_DEST_CSV_WITH_SHARING)) {
108 				csvFilenameWithSharing = line.getOptionValue(PARAM_DEST_CSV_WITH_SHARING);
109 			}
110 
111 			if (line.hasOption(PARAM_POPULATION_SIZE)) {
112 				populationSize = Integer.parseInt(line.getOptionValue(PARAM_POPULATION_SIZE));
113 			}
114 
115 		} catch (ParseException exp) {
116 			cliError(options, "Unexpected exception:" + exp.getMessage());
117 		}
118 
119 		logger.info("Population size: {}", populationSize);
120 
121 		logger.info("Evolution without fitness sharing. CSV output located at {}", csvFilenameWithoutSharing);
122 		FileUtils.forceMkdirParent(new File(csvFilenameWithoutSharing));
123 
124 		// tag::eaConfigurationBuilder[]
125 		final Builder<Double> eaConfigurationBuilder = new EAConfiguration.Builder<>();
126 		eaConfigurationBuilder.chromosomeSpecs(BitChromosomeSpec.of(7))
127 				.parentSelectionPolicy(Tournament.of(2))
128 				.combinationPolicy(MultiPointCrossover.of(2))
129 				.mutationPolicies(RandomMutation.of(0.05))
130 				.fitness((genotype) -> {
131 					final int x = toPhenotype(genotype);
132 					return x < 0 || x > 100 ? 0.0 : Math.abs(30 * Math.sin(x / 10));
133 				})
134 				.termination(Terminations.ofMaxGeneration(5));
135 		final EAConfiguration<Double> eaConfiguration = eaConfigurationBuilder.build();
136 		// end::eaConfigurationBuilder[]
137 
138 		// tag::eaExecutionContext[]
139 		final var csvEvolutionListener = CSVEvolutionListener.<Double, Void>of(
140 				csvFilenameWithoutSharing,
141 					List.of(
142 							ColumnExtractor.of("generation", es -> es.generation()),
143 								ColumnExtractor.of("fitness", es -> es.fitness()),
144 								ColumnExtractor.of("x", es -> toPhenotype(es.individual()))));
145 
146 		final var logTop5EvolutionListener = EvolutionListeners
147 				.<Double>ofLogTopN(logger, 5, genotype -> Integer.toString(toPhenotype(genotype)));
148 
149 		final EAExecutionContext<Double> eaExecutionContext = EAExecutionContexts.<Double>forScalarFitness()
150 				.populationSize(populationSize)
151 				.addEvolutionListeners(logTop5EvolutionListener, csvEvolutionListener)
152 				.build();
153 		// end::eaExecutionContext[]
154 
155 		// tag::eaSystem[]
156 		final EASystem<Double> eaSystem = EASystemFactory.from(eaConfiguration, eaExecutionContext);
157 
158 		final EvolutionResult<Double> evolutionResult = eaSystem.evolve();
159 		logger.info("Best genotype: {}", evolutionResult.bestGenotype());
160 		logger.info("  with fitness: {}", evolutionResult.bestFitness());
161 		logger.info("  at generation: {}", evolutionResult.generation());
162 		// end::eaSystem[]
163 
164 		/////// With Fitness Sharing
165 
166 		logger.info("Evolution with fitness sharing. CSV output located at {}", csvFilenameWithSharing);
167 		FileUtils.forceMkdirParent(new File(csvFilenameWithSharing));
168 
169 		// tag::eaConfigurationWithFS[]
170 		var eaConfigurationWithFitnessSharing = new EAConfiguration.Builder<Double>().from(eaConfiguration)
171 				.postEvaluationProcessor(FitnessSharing.ofStandard((i1, i2) -> {
172 
173 					final BitChromosome bitChromosome1 = i1.getChromosome(0, BitChromosome.class);
174 					final BitChromosome bitChromosome2 = i2.getChromosome(0, BitChromosome.class);
175 
176 					return (double) BitChromosomeUtils.hammingDistance(bitChromosome1, bitChromosome2);
177 				}, 5.0))
178 				.build();
179 		// end::eaConfigurationWithFS[]
180 
181 		// tag::eaExecutionContextWithFS[]
182 		final var csvEvolutionListenerWithFitnessSharing = new CSVEvolutionListener.Builder<Double, Void>()
183 				.from(csvEvolutionListener)
184 				.filename(csvFilenameWithSharing)
185 				.build();
186 
187 		final var eaExecutionContextWithFitnessSharing = EAExecutionContext.<Double>builder()
188 				.from(eaExecutionContext)
189 				.evolutionListeners(List.of(logTop5EvolutionListener, csvEvolutionListenerWithFitnessSharing))
190 				.build();
191 		// end::eaExecutionContextWithFS[]
192 
193 		// tag::eaSystemWithFS[]
194 		final EASystem<Double> eaSystemWithFitnessSharing = EASystemFactory
195 				.from(eaConfigurationWithFitnessSharing, eaExecutionContextWithFitnessSharing);
196 
197 		final EvolutionResult<Double> evolutionResultWithFitnessSharing = eaSystemWithFitnessSharing.evolve();
198 		logger.info("Best genotype: {}", evolutionResultWithFitnessSharing.bestGenotype());
199 		logger.info("  with fitness: {}", evolutionResultWithFitnessSharing.bestFitness());
200 		logger.info("  at generation: {}", evolutionResultWithFitnessSharing.generation());
201 		// end::eaSystemWithFS[]
202 	}
203 }