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   
8   import org.apache.commons.cli.CommandLine;
9   import org.apache.commons.cli.CommandLineParser;
10  import org.apache.commons.cli.DefaultParser;
11  import org.apache.commons.cli.HelpFormatter;
12  import org.apache.commons.cli.Options;
13  import org.apache.commons.cli.ParseException;
14  import org.apache.commons.io.FileUtils;
15  import org.apache.commons.lang3.Validate;
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  		Validate.notNull(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(PARAM_DEST_CSV_WITHOUT_SHARING,
84  				LONG_PARAM_DEST_CSV_WITHOUT_SHARING,
85  				true,
86  				"destination csv file for the case without fitness sharing");
87  
88  		options.addOption(PARAM_DEST_CSV_WITH_SHARING,
89  				LONG_PARAM_DEST_CSV_WITH_SHARING,
90  				true,
91  				"destination csv file for the case with fitness sharing");
92  
93  		options.addOption(PARAM_POPULATION_SIZE, LONG_PARAM_POPULATION_SIZE, true, "Population size");
94  
95  		String csvFilenameWithoutSharing = DEFAULT_DEST_CSV_WITHOUT_SHARING;
96  		String csvFilenameWithSharing = DEFAULT_DEST_CSV_WITH_SHARING;
97  		int populationSize = DEFAULT_POPULATION_SIZE;
98  		try {
99  			final CommandLine line = parser.parse(options, args);
100 
101 			if (line.hasOption(PARAM_DEST_CSV_WITHOUT_SHARING)) {
102 				csvFilenameWithoutSharing = line.getOptionValue(PARAM_DEST_CSV_WITHOUT_SHARING);
103 			}
104 
105 			if (line.hasOption(PARAM_DEST_CSV_WITH_SHARING)) {
106 				csvFilenameWithSharing = line.getOptionValue(PARAM_DEST_CSV_WITH_SHARING);
107 			}
108 
109 			if (line.hasOption(PARAM_POPULATION_SIZE)) {
110 				populationSize = Integer.parseInt(line.getOptionValue(PARAM_POPULATION_SIZE));
111 			}
112 
113 		} catch (ParseException exp) {
114 			cliError(options, "Unexpected exception:" + exp.getMessage());
115 		}
116 
117 		logger.info("Population size: {}", populationSize);
118 
119 		logger.info("Evolution without fitness sharing. CSV output located at {}", csvFilenameWithoutSharing);
120 		FileUtils.forceMkdirParent(new File(csvFilenameWithoutSharing));
121 
122 		// tag::eaConfigurationBuilder[]
123 		final Builder<Double> eaConfigurationBuilder = new EAConfiguration.Builder<>();
124 		eaConfigurationBuilder.chromosomeSpecs(BitChromosomeSpec.of(7))
125 				.parentSelectionPolicy(Tournament.of(2))
126 				.combinationPolicy(MultiPointCrossover.of(2))
127 				.mutationPolicies(RandomMutation.of(0.05))
128 				.fitness((genotype) -> {
129 					final int x = toPhenotype(genotype);
130 					return x < 0 || x > 100 ? 0.0 : Math.abs(30 * Math.sin(x / 10));
131 				})
132 				.termination(Terminations.ofMaxGeneration(5));
133 		final EAConfiguration<Double> eaConfiguration = eaConfigurationBuilder.build();
134 		// end::eaConfigurationBuilder[]
135 
136 		// tag::eaExecutionContext[]
137 		final var csvEvolutionListener = CSVEvolutionListener.<Double, Void>of(csvFilenameWithoutSharing,
138 				List.of(ColumnExtractor.of("generation", es -> es.generation()),
139 						ColumnExtractor.of("fitness", es -> es.fitness()),
140 						ColumnExtractor.of("x", es -> toPhenotype(es.individual()))));
141 
142 		final var logTop5EvolutionListener = EvolutionListeners
143 				.<Double>ofLogTopN(logger, 5, genotype -> Integer.toString(toPhenotype(genotype)));
144 
145 		final EAExecutionContext<Double> eaExecutionContext = EAExecutionContexts.<Double>forScalarFitness()
146 				.populationSize(populationSize)
147 				.addEvolutionListeners(logTop5EvolutionListener, csvEvolutionListener)
148 				.build();
149 		// end::eaExecutionContext[]
150 
151 		// tag::eaSystem[]
152 		final EASystem<Double> eaSystem = EASystemFactory.from(eaConfiguration, eaExecutionContext);
153 
154 		final EvolutionResult<Double> evolutionResult = eaSystem.evolve();
155 		logger.info("Best genotype: {}", evolutionResult.bestGenotype());
156 		logger.info("  with fitness: {}", evolutionResult.bestFitness());
157 		logger.info("  at generation: {}", evolutionResult.generation());
158 		// end::eaSystem[]
159 
160 		/////// With Fitness Sharing
161 
162 		logger.info("Evolution with fitness sharing. CSV output located at {}", csvFilenameWithSharing);
163 		FileUtils.forceMkdirParent(new File(csvFilenameWithSharing));
164 
165 		// tag::eaConfigurationWithFS[]
166 		var eaConfigurationWithFitnessSharing = new EAConfiguration.Builder<Double>().from(eaConfiguration)
167 				.postEvaluationProcessor(FitnessSharing.ofStandard((i1, i2) -> {
168 
169 					final BitChromosome bitChromosome1 = i1.getChromosome(0, BitChromosome.class);
170 					final BitChromosome bitChromosome2 = i2.getChromosome(0, BitChromosome.class);
171 
172 					return (double) BitChromosomeUtils.hammingDistance(bitChromosome1, bitChromosome2);
173 				}, 5.0))
174 				.build();
175 		// end::eaConfigurationWithFS[]
176 
177 		// tag::eaExecutionContextWithFS[]
178 		final var csvEvolutionListenerWithFitnessSharing = new CSVEvolutionListener.Builder<Double, Void>()
179 				.from(csvEvolutionListener)
180 				.filename(csvFilenameWithSharing)
181 				.build();
182 
183 		final var eaExecutionContextWithFitnessSharing = EAExecutionContext.<Double>builder()
184 				.from(eaExecutionContext)
185 				.evolutionListeners(List.of(logTop5EvolutionListener, csvEvolutionListenerWithFitnessSharing))
186 				.build();
187 		// end::eaExecutionContextWithFS[]
188 
189 		// tag::eaSystemWithFS[]
190 		final EASystem<Double> eaSystemWithFitnessSharing = EASystemFactory.from(eaConfigurationWithFitnessSharing,
191 				eaExecutionContextWithFitnessSharing);
192 
193 		final EvolutionResult<Double> evolutionResultWithFitnessSharing = eaSystemWithFitnessSharing.evolve();
194 		logger.info("Best genotype: {}", evolutionResultWithFitnessSharing.bestGenotype());
195 		logger.info("  with fitness: {}", evolutionResultWithFitnessSharing.bestFitness());
196 		logger.info("  at generation: {}", evolutionResultWithFitnessSharing.generation());
197 		// end::eaSystemWithFS[]
198 	}
199 }