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
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
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
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 }