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    }