001 package org.jaga.hooks;
002
003 import java.io.PrintStream;
004 import java.util.ArrayList;
005 import java.util.HashMap;
006 import org.jaga.masterAlgorithm.SimpleGA;
007 import org.jaga.util.FittestIndividualResult;
008 import org.jaga.selection.AbsoluteFitness;
009 import org.jaga.definitions.*;
010
011
012 /**
013 * TODO: Complete these comments.
014 *
015 * <p><u>Project:</u> JAGA - Java API for Genetic Algorithms.</p>
016 *
017 * <p><u>Company:</u> University College London and JAGA.Org
018 * (<a href="http://www.jaga.org" target="_blank">http://www.jaga.org</a>).
019 * </p>
020 *
021 * <p><u>Copyright:</u> (c) 2004 by G. Paperin.<br/>
022 * This program is free software; you can redistribute it and/or modify
023 * it under the terms of the GNU General Public License as published by
024 * the Free Software Foundation, ONLY if you include a note of the original
025 * author(s) in any redistributed/modified copy.<br/>
026 * This program is distributed in the hope that it will be useful,
027 * but WITHOUT ANY WARRANTY; without even the implied warranty of
028 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
029 * GNU General Public License for more details.<br/>
030 * You should have received a copy of the GNU General Public License
031 * along with this program; if not, write to the Free Software
032 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
033 * or see http://www.gnu.org/licenses/gpl.html</p>
034 *
035 * @author Greg Paperin (greg@jaga.org)
036 *
037 * @version JAGA public release 1.0 beta
038 */
039
040 public class AnalysisHook extends SimpleGAHook {
041
042 /**
043 * Statistical data entry.
044 */
045 class Entry {
046 private int generation = 0;
047 private double value = 0;
048 private Entry() {
049 throw new UnsupportedOperationException("Use Entry(int gen, double val)");
050 }
051 private Entry(int gen, double val) {
052 generation = gen;
053 value = val;
054 }
055 public StringBuffer toStrBuf() {
056 return (new StringBuffer()).append(value)
057 .append(" (gen. ").append(generation).append(")");
058 }
059 public int getGeneration() { return generation; }
060 public double getValue() { return value; }
061 public String toString() { return toStrBuf().toString(); }
062 }
063
064 // Control switches:
065 private boolean analyseBestFitness = true;
066 private boolean analyseTotalFitEvals = true;
067
068 private boolean analyseGenAge = true;
069 private boolean analyseGenMinFit = true;
070 private boolean analyseGenMaxFit = true;
071 private boolean analyseGenAverageFit = true;
072 private boolean analyseGenFitStdDeviation = true;
073 private boolean analyseGenDump = false;
074
075 private boolean analyseRunTime = true;
076
077 private PrintStream logStream = null;
078 private boolean plotGraph = true;
079 private long updateDelay = -1;
080
081 // View rendering info:
082 private GAAnalysisFrame frame = null;
083 private boolean viewUpdated = false;
084 private double minPlotVal = Double.MAX_VALUE;
085 private double maxPlotVal = -Double.MAX_VALUE;
086 private int maxPlotCount = Integer.MIN_VALUE;
087 private String plotFrameTitle = "Genetic Algorithm Analysis";
088
089 // Statistical data:
090 private FittestIndividualResult bestResult = null;
091 private ArrayList bestFitnessValues = new ArrayList();
092
093 private int generationNum = -1;
094 private ArrayList genAverageFitnesses = new ArrayList();
095 private HashMap genFitnessStdDeviations = new HashMap();
096 private ArrayList genMinFitnesses = new ArrayList();
097 private ArrayList genMaxFitnesses = new ArrayList();
098
099 private long fitnessCalculations = 0;
100 private long startedTime = 0;
101 private String runTime = "00:00.0";
102
103
104 public AnalysisHook() {
105 super();
106 reset();
107 }
108
109 public AnalysisHook(PrintStream logStream) {
110 super();
111 this.logStream = logStream;
112 reset();
113 }
114
115 public AnalysisHook(boolean plotGraph) {
116 super();
117 this.plotGraph = plotGraph;
118 reset();
119 }
120
121 public AnalysisHook(PrintStream logStream, boolean plotGraph) {
122 super();
123 this.logStream = logStream;
124 this.plotGraph = plotGraph;
125 reset();
126 }
127
128 public void reset() {
129 viewUpdated = false;
130 minPlotVal = Double.MAX_VALUE;
131 maxPlotVal = -Double.MAX_VALUE;
132 maxPlotCount = Integer.MIN_VALUE;
133
134 bestResult = null;
135 bestFitnessValues.clear();
136
137 generationNum = -1;
138 genAverageFitnesses.clear();
139 genFitnessStdDeviations.clear();
140 genMinFitnesses.clear();
141 genMaxFitnesses.clear();
142
143 fitnessCalculations = 0;
144 runTime = "00:00.0";
145
146 if (isPlotGraph()) {
147 if (null != frame)
148 frame.dispose();
149 frame = createAnalysisFrame();
150 frame.show();
151 }
152
153 startedTime = System.currentTimeMillis();
154 }
155
156 private GAAnalysisFrame createAnalysisFrame() {
157 GAAnalysisFrame frame = new GAAnalysisFrame(this);
158 frame.pack();
159 return frame;
160 }
161
162 public synchronized void frameDisposed() {
163 frame = null;
164 }
165
166 private synchronized void updateResult(GAResult result, int age) {
167
168 checkTime();
169
170 if (null == result)
171 return;
172
173 FittestIndividualResult res = (FittestIndividualResult) result;
174 AbsoluteFitness fit = (AbsoluteFitness) res.getBestFitness();
175
176 if (null == fit)
177 return;
178
179 if (null != bestResult && !bestResult.getBestFitness().isWorse(fit))
180 return;
181
182 bestResult = new FittestIndividualResult();
183 bestResult.setFittestIndividual(res.getFittestIndividual());
184
185 if (!isAnalyseBestFitness())
186 return;
187
188 bestFitnessValues.add(new Entry(age, fit.getValue()));
189 updateGraphSize(fit.getValue(), bestFitnessValues.size());
190 log("\n");
191 log(" *** New best result in generation " + age + ":");
192 log(" Individual: " + res.getFittestIndividual());
193 log(" Fitness: " + res.getBestFitness());
194 updateView();
195 }
196
197 private synchronized void updatePopulation(Population pop, int age, GAResult result) {
198
199 if (generationNum == age)
200 return;
201
202 generationNum = age;
203
204 checkTime();
205
206 int popSize = pop.getSize();
207 double average = 0;
208 double stddev = 0;
209 double minfit = Double.MAX_VALUE;
210 double maxfit = -Double.MAX_VALUE;
211
212 if (isAnalyseGenMinFit() || isAnalyseGenMaxFit()
213 || isAnalyseGenAverageFit() || isAnalyseGenFitStdDeviation()) {
214 // Av. fitness:
215 for (int i = 0; i < popSize; i++) {
216 Fitness f = pop.getMember(i).getFitness();
217 AbsoluteFitness fit = (AbsoluteFitness) f;
218 final double fval = fit.getValue();
219 if (fval < minfit) minfit = fval;
220 if (fval > maxfit) maxfit = fval;
221 average += fval;
222 }
223 average /= (double) popSize;
224 if (isAnalyseGenAverageFit() || isAnalyseGenFitStdDeviation()) {
225 genAverageFitnesses.add(new Entry(age, average));
226 updateGraphSize(average, genAverageFitnesses.size());
227 }
228 if (isAnalyseGenMinFit()) {
229 genMinFitnesses.add(new Entry(age, minfit));
230 updateGraphSize(minfit, genMinFitnesses.size());
231 }
232 if (isAnalyseGenMaxFit()) {
233 genMaxFitnesses.add(new Entry(age, maxfit));
234 updateGraphSize(maxfit, genMaxFitnesses.size());
235 }
236
237 if (isAnalyseGenFitStdDeviation()) {
238 //Std. deviation:
239 for (int i = 0; i < popSize; i++) {
240 Fitness f = pop.getMember(i).getFitness();
241 AbsoluteFitness fit = (AbsoluteFitness) f;
242 final double d = fit.getValue() - average;
243 stddev += d * d;
244 }
245 stddev /= (double) (popSize - 1);
246 stddev = Math.sqrt(stddev);
247 updateGraphSize(average + stddev, genAverageFitnesses.size());
248 updateGraphSize(average - stddev, genAverageFitnesses.size());
249 genFitnessStdDeviations.put(new Integer(age), new Entry(age, stddev));
250 }
251 }
252
253 if (isAnalyseGenDump() || isAnalyseGenAge() || isAnalyseGenMinFit()
254 || isAnalyseGenMaxFit() || isAnalyseGenAverageFit()
255 || isAnalyseGenFitStdDeviation()) {
256 log("\n");
257 }
258 if (isAnalyseGenDump()) {
259 log("\n *** List of generation " + age + " (" + popSize + " induviduals):");
260 for (int i = 0; i < popSize; i++) {
261 log(" " + i + ") " + pop.getMember(i));
262 }
263 log(" *** List of the " + popSize + " individuals in generation " + age + " complete.");
264 }
265 if (isAnalyseGenAge()) {
266 Runtime rt = Runtime.getRuntime();
267 log(" *** New generation " + age + " created (" + popSize + " individuals).");
268 log(" Memory free: " + rt.freeMemory() + " KByte.");
269 log(" Memory available: " + rt.totalMemory() + " KByte.");
270 log(" Memory on system: " + rt.maxMemory() + " KByte.");
271 }
272 if (isAnalyseGenMinFit()) {
273 log(" *** Minimum fitness in generation " + age + " is " + minfit + ".");
274 }
275 if (isAnalyseGenMaxFit()) {
276 log(" *** Maximum fitness in generation " + age + " is " + maxfit + ".");
277 }
278 if (isAnalyseGenAverageFit()) {
279 log(" *** Average fitness in generation " + age + " is " + average + ".");
280 }
281 if (isAnalyseGenFitStdDeviation()) {
282 log(" *** Fitness standard deviation in generation " + age + " is " + stddev + ".");
283 }
284 if (isAnalyseGenAge() || isAnalyseGenAverageFit() || isAnalyseGenFitStdDeviation()) {
285 updateView();
286 }
287 }
288
289 private synchronized void gaTerminated() {
290
291 log("\n *** Computation completed.");
292 log(" Generations: " + generationNum);
293 log(" Fitness evaluations: " + fitnessCalculations);
294 log(" Run time: " + getRunTimeString());
295 log(" Best result: " + bestResult);
296
297 if (isAnalyseBestFitness() && bestFitnessValues.size() > 0) {
298 Entry e = (Entry) bestFitnessValues.get(bestFitnessValues.size() - 1);
299 log(" *** Best fitness (" + e.getValue()
300 + ") was discovered in generation " + e.getGeneration() + ".");
301 }
302
303 updateView();
304 }
305
306 private synchronized void fitnessCalculated() {
307 ++fitnessCalculations;
308 if (!isAnalyseTotalFitEvals())
309 return;
310 updateView();
311 }
312
313 private void updateGraphSize(double val, int count) {
314 if (val < minPlotVal)
315 minPlotVal = val;
316 if (val > maxPlotVal)
317 maxPlotVal = val;
318 if (count > maxPlotCount)
319 maxPlotCount = count;
320 }
321
322 private void updateView() {
323 if (viewUpdated)
324 return;
325 viewUpdated = true;
326 if (updateDelay < 1 && null != frame)
327 frame.updateView();
328 }
329
330 private void log(String msg) {
331 if (null == logStream)
332 return;
333 logStream.println(msg);
334 }
335
336 private void checkTime() {
337 if (!isAnalyseRunTime())
338 return;
339 runTime = getRunTimeString();
340 log("\n *** Run time: " + runTime);
341 if (isAnalyseTotalFitEvals())
342 log(" *** Total fitness evaluations: " + fitnessCalculations);
343 updateView();
344 }
345
346 private String getRunTimeString() {
347 long millis = System.currentTimeMillis();
348 millis -= startedTime;
349 StringBuffer mins = new StringBuffer();
350 mins.append(millis / 60000L);
351 while (2 > mins.length())
352 mins.insert(0, "0");
353 StringBuffer secs = new StringBuffer();
354 secs.append((millis % 60000L) / 1000L);
355 while (2 > secs.length())
356 secs.insert(0, "0");
357 StringBuffer msecs = new StringBuffer();
358 msecs.append(millis % 60000000L);
359 return mins.append(":").append(secs).append(".").append(msecs).toString();
360 }
361
362 public void initialisationDone(SimpleGA caller, Population pop, int age,
363 GAResult result, GAParameterSet params) {
364 updateResult(result, age);
365 updatePopulation(pop, age, result);
366 }
367
368 public void populationReinitialised(Population pop, int age,
369 GAResult result, GAParameterSet params) {
370 this.bestResult = null;
371 updateResult(result, age);
372 updatePopulation(pop, age, result);
373 }
374
375 public void foundNewResult(SimpleGA caller, Population pop, int age,
376 GAResult result, GAParameterSet params) {
377 updateResult(result, age);
378 }
379
380 public void generationChanged(SimpleGA caller, Population pop, int age,
381 GAResult result, GAParameterSet paramss) {
382 updatePopulation(pop, age, result);
383 }
384
385 public void terminationConditionApplies(SimpleGA caller, Population pop, int age,
386 GAResult result, GAParameterSet params) {
387 gaTerminated();
388 }
389
390 public void selectedForReproduction(SimpleGA caller, Individual [] selectedParents,
391 Population pop, int age, GAResult result,
392 GAParameterSet params) {
393 ;
394 }
395
396 public void reproduced(SimpleGA caller, Individual [] children, Individual [] parents,
397 Population pop, int age, GAResult result, GAParameterSet params) {
398 ;
399 }
400
401 public void fitnessCalculated(SimpleGA caller, Individual updatedIndividual,
402 Population pop, int age, GAParameterSet params) {
403 fitnessCalculated();
404 }
405
406 public boolean isAnalyseBestFitness() {
407 return analyseBestFitness;
408 }
409
410 public void setAnalyseBestFitness(boolean analyseBestFitness) {
411 this.analyseBestFitness = analyseBestFitness;
412 }
413
414
415
416 public boolean isAnalyseGenAge() {
417 return analyseGenAge;
418 }
419
420 public void setAnalyseGenAge(boolean analyseGenAge) {
421 this.analyseGenAge = analyseGenAge;
422 }
423
424 public boolean isAnalyseGenAverageFit() {
425 return analyseGenAverageFit;
426 }
427
428 public void setAnalyseGenAverageFit(boolean analyseGenAverageFit) {
429 this.analyseGenAverageFit = analyseGenAverageFit;
430 }
431
432 public boolean isAnalyseGenFitStdDeviation() {
433 return analyseGenFitStdDeviation;
434 }
435
436 public void setAnalyseGenFitStdDeviation(boolean analyseGenFitStdDeviation) {
437 this.analyseGenFitStdDeviation = analyseGenFitStdDeviation;
438 }
439
440 public boolean isAnalyseGenDump() {
441 return analyseGenDump;
442 }
443
444 public void setAnalyseGenDump(boolean analyseGenDump) {
445 this.analyseGenDump = analyseGenDump;
446 }
447
448 public boolean isAnalyseRunTime() {
449 return analyseRunTime;
450 }
451
452 public void setAnalyseRunTime(boolean analyseRunTime) {
453 this.analyseRunTime = analyseRunTime;
454 }
455
456 public boolean isAnalyseTotalFitEvals() {
457 return analyseTotalFitEvals;
458 }
459
460 public void setAnalyseTotalFitEvals(boolean analyseTotalFitEvals) {
461 this.analyseTotalFitEvals = analyseTotalFitEvals;
462 }
463
464 public PrintStream getLogStream() {
465 return logStream;
466 }
467
468 public void setLogStream(PrintStream logStream) {
469 this.logStream = logStream;
470 }
471
472 public void stopLogStream() {
473 this.logStream = null;
474 }
475
476 public boolean isAnalyseGenMaxFit() {
477 return analyseGenMaxFit;
478 }
479
480 public void setAnalyseGenMaxFit(boolean analyseGenMaxFit) {
481 this.analyseGenMaxFit = analyseGenMaxFit;
482 }
483
484 public boolean isAnalyseGenMinFit() {
485 return analyseGenMinFit;
486 }
487
488 public void setAnalyseGenMinFit(boolean analyseGenMinFit) {
489 this.analyseGenMinFit = analyseGenMinFit;
490 }
491
492 public boolean isPlotGraph() {
493 return plotGraph;
494 }
495
496 public void setPlotGraph(boolean plotGraph) {
497 this.plotGraph = plotGraph;
498 }
499
500 public String getPlotFrameTitle() {
501 return plotFrameTitle;
502 }
503
504 public void setPlotFrameTitle(String plotFrameTitle) {
505 this.plotFrameTitle = plotFrameTitle;
506 }
507
508 public long getUpdateDelay() {
509 return updateDelay;
510 }
511
512 public void setUpdateDelay(long updateDelay) {
513 this.updateDelay = updateDelay;
514 }
515
516 public synchronized boolean isViewUpdated() {
517 return viewUpdated;
518 }
519
520 public synchronized int getGenerationNum() {
521 return generationNum;
522 }
523
524 public synchronized long getFitnessCalculations() {
525 return fitnessCalculations;
526 }
527
528 public synchronized String getRunTime() {
529 return runTime;
530 }
531
532 public synchronized Entry getBestFitnessValue(int index) {
533 if (index < 0 || bestFitnessValues.size() <= index)
534 return new Entry(-1, 0);
535 return (Entry) bestFitnessValues.get(index);
536 }
537
538 public synchronized int getBestFitnessValueCount() {
539 return bestFitnessValues.size();
540 }
541
542 public synchronized Entry getMinFitness(int index) {
543 if (index < 0 || genMinFitnesses.size() <= index)
544 return new Entry(-1, 0);
545 return (Entry) genMinFitnesses.get(index);
546 }
547
548 public synchronized int getMinFitnessCount() {
549 return genMinFitnesses.size();
550 }
551
552 public synchronized Entry getMaxFitness(int index) {
553 if (index < 0 || genMaxFitnesses.size() <= index)
554 return new Entry(-1, 0);
555 return (Entry) genMaxFitnesses.get(index);
556 }
557
558 public synchronized int getMaxFitnessCount() {
559 return genMaxFitnesses.size();
560 }
561
562 public synchronized Entry getAverageFitness(int index) {
563 if (index < 0 || genAverageFitnesses.size() <= index)
564 return new Entry(-1, 0);
565 return (Entry) genAverageFitnesses.get(index);
566 }
567
568 public synchronized int getAverageFitnessCount() {
569 return genAverageFitnesses.size();
570 }
571
572 public synchronized int getFitnessStdDeviationCount() {
573 return genFitnessStdDeviations.size();
574 }
575
576 public synchronized double getMaxPlotVal() {
577 return maxPlotVal;
578 }
579
580 public synchronized double getMinPlotVal() {
581 return minPlotVal;
582 }
583
584 public synchronized int getMaxPlotCount() {
585 return maxPlotCount;
586 }
587
588 public synchronized void viewUpdateComplete() {
589 viewUpdated = false;
590 }
591
592 public ArrayList getBestFitnessValues() {
593 return bestFitnessValues;
594 }
595
596 public ArrayList getGenAverageFitnesses() {
597 return genAverageFitnesses;
598 }
599
600 public ArrayList getGenMaxFitnesses() {
601 return genMaxFitnesses;
602 }
603
604 public ArrayList getGenMinFitnesses() {
605 return genMinFitnesses;
606 }
607
608 public FittestIndividualResult getBestResult() {
609 return bestResult;
610 }
611
612 public Entry getStdDeviation(int genNum) {
613 Integer key = new Integer(genNum);
614 Object o = genFitnessStdDeviations.get(key);
615 if (null == key)
616 return new Entry(-1, 0);
617 else
618 return (Entry) o;
619 }
620 }