aboutsummaryrefslogtreecommitdiffstats
path: root/src/jcgp/backend/statistics/StatisticsLogger.java
blob: 7165a11cc42177fef3d7d51f04d69b5d4ca6755d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
package jcgp.backend.statistics;

import java.util.ArrayList;

/**
 * This is a utility class for logging experiment statistics when doing multiple runs.
 * <br><br>
 * Information about each run is added via the {@code logRun()} method. The many getters
 * can be used to obtain statistics about the logged runs, such as success rate and average
 * fitness.
 * <br><br>
 * {@code JCGP} uses this class to perform its logging and print out experiment data at the end.
 * 
 * 
 * @author Eduardo Pedroni
 *
 */
public class StatisticsLogger {

	// this list holds the logged entries
	private ArrayList<RunEntry> runEntries;
	
	/**
	 * Create a new statistics logger, use this when resetting is necessary.
	 */
	public StatisticsLogger() {
		runEntries = new ArrayList<RunEntry>();
	}
	
	/**
	 * Log a new run. Calling any of the statistics getters will
	 * now take this logged run into account as well as all previously
	 * logged runs.
	 * 
	 * @param generation the last generation when improvement occurred.
	 * @param fitness the best fitness achieved in the run.
	 * @param active the number of active nodes in the best chromosome found.
	 * @param successful true if a perfect solution was found, false if otherwise.
	 */
	public void logRun(int generation, double fitness, int active, boolean successful) {
		runEntries.add(new RunEntry(generation, fitness, active, successful));
	}
	
	/**
	 * Averages the best fitness obtained in each run.
	 * 
	 * @return the average fitness.
	 */
	public double getAverageFitness() {
		double average = 0;
		for (RunEntry runEntry : runEntries) {
			average += runEntry.getFitness() / runEntries.size();
		}
		return average;
	}
	
	/**
	 * Calculates the standard deviation of
	 * the best fitness obtained in each run.
	 * 
	 * @return the standard deviation of average fitnesses.
	 */
	public double getAverageFitnessStdDev() {
		double average = getAverageFitness();
		double temp, stdDev = 0;
		for (RunEntry runEntry : runEntries) {
			temp = runEntry.getFitness() - average;
			temp = temp * temp;
			stdDev += temp;
		}
		return stdDev;
	}
	
	/**
	 * Averages the number of active nodes in the 
	 * best chromosomes obtained across all runs.
	 * 
	 * @return the average number of active nodes.
	 */
	public double getAverageActiveNodes() {
		double average = 0;
		for (RunEntry runEntry : runEntries) {
			average += runEntry.getActiveNodes() / runEntries.size();
		}
		return average;
	}
	
	/**
	 * Calculates the standard deviation of
	 * the number of active nodes in the best solution
	 * in each run.
	 * 
	 * @return the standard deviation of active node counts.
	 */
	public double getAverageActiveNodesStdDev() {
		double average = getAverageActiveNodes();
		double temp, stdDev = 0;
		for (RunEntry runEntry : runEntries) {
			temp = runEntry.getActiveNodes() - average;
			temp = temp * temp;
			stdDev += temp;
		}
		return stdDev;
	}
	
	/**
	 * Calculates the average generation out of all runs. 
	 * The generation value in each run corresponds to the
	 * last generation in which improvement happened. 
	 * <br><br>
	 * Note that this method includes runs where no perfect
	 * solution was found. For the average number of generations
	 * for perfect solutions only, use {@code getAverageSuccessfulGenerations}.
	 * 
	 * @return the average number of generations.
	 */
	public double getAverageGenerations() {
		double average = 0;
		for (RunEntry runEntry : runEntries) {
			average += runEntry.getGeneration() / runEntries.size();
		}
		return average;
	}
	
	/**
	 * Calculates the standard deviation of
	 * the average number of generations in
	 * each run.
	 * 
	 * @return the standard deviation of the number of generations.
	 */
	public double getAverageGenerationsStdDev() {
		double average = getAverageGenerations();
		double temp, stdDev = 0;
		for (RunEntry runEntry : runEntries) {
			temp = runEntry.getGeneration() - average;
			temp = temp * temp;
			stdDev += temp;
		}
		return stdDev;
	}
	
	/**
	 * @return the highest fitness across all runs.
	 */
	public double getHighestFitness() {
		double highest = 0;
		for (RunEntry runEntry : runEntries) {
			if (runEntry.getFitness() > highest) {
				highest = runEntry.getFitness();
			}
		}
		return highest;
	}
	
	/**
	 * @return the lowest fitness across all runs.
	 */
	public double getLowestFitness() {
		double lowest = Double.MAX_VALUE;
		for (RunEntry runEntry : runEntries) {
			if (runEntry.getFitness() < lowest) {
				lowest = runEntry.getFitness();
			}
		}
		return lowest;
	}
	
	/**
	 * 
	 * @return the number of runs in which a perfect solution was found.
	 */
	public int getSuccessfulRuns() {
		int count = 0;
		for (RunEntry runEntry : runEntries) {
			// only increment if solution was perfect
			if (runEntry.isSuccessful()) {
				count++;
			}
		}
		return count;
	}
	
	/**
	 * Calculates the ratio of successful runs (runs where
	 * a perfect solution was found) to total number of runs.
	 * A double-precision value between 0 and 1 is returned, 
	 * where 0 means 0% success rate and 1 means 100% success rate.
	 * 
	 * @return the success rate across all runs.
	 */
	public double getSuccessRate() {
		return getSuccessfulRuns() / (double) runEntries.size();
	}
	
	/**
	 * Calculates the average generation out of successful runs only. 
	 * The generation value in each successful run corresponds to the
	 * generation in which the perfect solution was found. 
	 * 
	 * @return the average number of generations for perfect solutions.
	 */
	public double getAverageSuccessfulGenerations() {
		double average = 0;
		int successfulRuns = getSuccessfulRuns();
		for (RunEntry runEntry : runEntries) {
			// only if solution was perfect
			if (runEntry.isSuccessful()) {
				average += runEntry.getGeneration() / successfulRuns;
			}
		}
		return average;
	}
	
	/**
	 * Calculates the standard deviation of
	 * the average number of generations in
	 * each run where a perfect solution was found.
	 * 
	 * @return the standard deviation of the number of generations in successful runs.
	 */
	public double getAverageSuccessfulGenerationsStdDev() {
		double average = getAverageSuccessfulGenerations();
		double temp, stdDev = 0;
		for (RunEntry runEntry : runEntries) {
			// only if solution was perfect
			if (runEntry.isSuccessful()) {
				temp = runEntry.getGeneration() - average;
				temp = temp * temp;
				stdDev += temp;
			}
		}
		return stdDev;
	}
	
}