View Javadoc
1   package net.bmahe.genetics4j.samples.neat;
2   
3   import java.io.File;
4   import java.io.PrintWriter;
5   import java.util.List;
6   import java.util.Set;
7   import java.util.concurrent.Callable;
8   import java.util.stream.Collectors;
9   import java.util.stream.Stream;
10  
11  import org.apache.logging.log4j.LogManager;
12  import org.apache.logging.log4j.Logger;
13  import picocli.CommandLine;
14  import picocli.CommandLine.Command;
15  import picocli.CommandLine.Option;
16  
17  import net.bmahe.genetics4j.core.Genotype;
18  import net.bmahe.genetics4j.core.spec.EvolutionResult;
19  import net.bmahe.genetics4j.neat.Connection;
20  import net.bmahe.genetics4j.neat.chromosomes.NeatChromosome;
21  
22  @Command(name = "RecurrentParityBenchmarkCLI",
23  		mixinStandardHelpOptions = true,
24  		version = "0.1",
25  		description = "Run benchmark for different hyperparameter values")
26  public class RecurrentParityBenchmarkCLI implements Callable<Integer> {
27  	private static final Logger logger = LogManager.getLogger(RecurrentParityBenchmarkCLI.class);
28  
29  	private double randomWeightsProb = 0.05;
30  	private double switchMutationProb = 0.005;
31  	private double addNodeProb = 0.01;
32  	private double deleteNodeProb = 0.005;
33  	private double addConnectionProb = 0.01;
34  
35  	// Node
36  	@Option(names = "--start-add-node", description = "Start of the range for addition of nodes")
37  	private double startAddNodeProb = 0.005;
38  
39  	@Option(names = "--end-add-node", description = "End of the range for addition of nodes")
40  	private double endAddNodeProb = 0.05;
41  
42  	@Option(names = "--count-add-node", description = "How many steps to try")
43  	private int countAddNodeProb = 1;
44  
45  	@Option(names = "--start-delete-node", description = "Start of the range for deletion of nodes")
46  	private double startDeleteNodeProb = 0.005;
47  
48  	@Option(names = "--end-delete-node", description = "End of the range for deletion of nodes")
49  	private double endDeleteNodeProb = 0.05;
50  
51  	@Option(names = "--count-delete-node", description = "How many steps to try")
52  	private int countDeleteNodeProb = 1;
53  
54  	// Connection
55  	@Option(names = "--start-add-connection", description = "Start of the range for addition of connections")
56  	private double startAddConnectionProb = 0.005;
57  
58  	@Option(names = "--end-add-connection", description = "End of the range for addition of connections")
59  	private double endAddConnectionProb = 0.05;
60  
61  	@Option(names = "--count-add-connection", description = "How many steps to try")
62  	private int countAddConnectionProb = 1;
63  
64  	@Option(names = "--start-delete-connection", description = "Start of the range for deletion of connections")
65  	private double startDeleteConnectionProb = 0.005;
66  
67  	@Option(names = "--end-delete-connection", description = "End of the range for deletion of connections")
68  	private double endDeleteConnectionProb = 0.05;
69  
70  	@Option(names = "--count-delete-connection", description = "How many steps to try")
71  	private int countDeleteConnectionProb = 1;
72  
73  	// Global
74  	@Option(names = "--count", description = "How many steps to try")
75  	private int countExecs = 1;
76  
77  	@Option(names = { "-f", "--file" },
78  			paramLabel = "FILE",
79  			description = "Destination filename for generated csv data",
80  			defaultValue = "recurrent_parity_benchmark.csv")
81  	private String destinationFilename;
82  
83  	public static void main(String... args) {
84  		int exitCode = new CommandLine(new RecurrentParityBenchmarkCLI()).execute(args);
85  		System.exit(exitCode);
86  	}
87  
88  	@Override
89  	public Integer call() throws Exception {
90  		final RecurrentParityExample recurrentParityExample = new RecurrentParityExample();
91  
92  		final File csvOutputFile = new File(destinationFilename);
93  		try (PrintWriter printWriter = new PrintWriter(csvOutputFile)) {
94  
95  			printWriter.println(
96  					String.join(
97  							",",
98  							"delete_connection",
99  							"add_connection",
100 							"delete_node",
101 							"add_node",
102 							"duration_ns",
103 							"best_fitness",
104 							"generation",
105 							"num_connections",
106 							"num_connections_enabled",
107 							"num_connections_disabled",
108 							"num_nodes"));
109 
110 			final double deleteConnectionStep = (endDeleteConnectionProb - startDeleteConnectionProb)
111 					/ countDeleteConnectionProb;
112 			for (double deleteConnectionProb = startDeleteConnectionProb; deleteConnectionProb < endDeleteConnectionProb; deleteConnectionProb += deleteConnectionStep) {
113 				logger.info("delete connection probability: {}", deleteConnectionProb);
114 
115 				final double addConnectionStep = (endAddConnectionProb - startAddConnectionProb) / countAddConnectionProb;
116 				for (double addConnectionProb = startAddConnectionProb; addConnectionProb < endAddConnectionProb; addConnectionProb += addConnectionStep) {
117 					logger.info("\t add connection probability: {}", addConnectionProb);
118 
119 					final double deleteNodeStep = (endDeleteNodeProb - startDeleteNodeProb) / countDeleteNodeProb;
120 					for (double deleteNodeProb = startDeleteNodeProb; deleteNodeProb < endDeleteNodeProb; deleteNodeProb += deleteNodeStep) {
121 						logger.info("\t\t delete Node probability: {}", deleteNodeProb);
122 
123 						final double addNodeStep = (endAddNodeProb - startAddNodeProb) / countAddNodeProb;
124 						for (double addNodeProb = startAddNodeProb; addNodeProb < endAddNodeProb; addNodeProb += addNodeStep) {
125 							logger.info("\t\t\t add Node probability: {}", addNodeProb);
126 
127 							for (int i = 0; i < countExecs; i++) {
128 								final long startTimeNs = System.nanoTime();
129 								final EvolutionResult<Float> evolutionResult = recurrentParityExample.run(
130 										randomWeightsProb,
131 										switchMutationProb,
132 										addNodeProb,
133 										deleteNodeProb,
134 										addConnectionProb,
135 										deleteConnectionProb);
136 								final long endTimeNs = System.nanoTime();
137 								final long durationNs = endTimeNs - startTimeNs;
138 
139 								final Float bestFitness = evolutionResult.bestFitness();
140 								final long generation = evolutionResult.generation();
141 
142 								final Genotype genotype = evolutionResult.bestGenotype();
143 								final NeatChromosome chromosome = genotype.getChromosome(0, NeatChromosome.class);
144 								final List<Connection> connections = chromosome.getConnections();
145 
146 								final long numEnabledConnections = connections.stream().filter(Connection::isEnabled).count();
147 								final long numDisabledConnections = connections.size() - numEnabledConnections;
148 
149 								final Set<Integer> uniqueNodes = connections.stream()
150 										.flatMap(connection -> Stream.of(connection.fromNodeIndex(), connection.toNodeIndex()))
151 										.collect(Collectors.toSet());
152 
153 								final String csvRow = String.format(
154 										"%f,%f,%f,%f,%d,%f,%d,%d,%d,%d,%d",
155 										deleteConnectionProb,
156 										addConnectionProb,
157 										deleteNodeProb,
158 										addNodeProb,
159 										durationNs,
160 										bestFitness,
161 										generation,
162 										connections.size(),
163 										numEnabledConnections,
164 										numDisabledConnections,
165 										uniqueNodes.size());
166 								printWriter.println(csvRow);
167 								printWriter.flush();
168 							}
169 						}
170 					}
171 
172 				}
173 			}
174 
175 		}
176 		return 0;
177 	}
178 
179 }