dwdyer / watchmaker

The Watchmaker Framework for Evolutionary Computation
https://watchmaker.uncommons.org
Apache License 2.0
206 stars 79 forks source link

EvolutionMonitor causes Swing to hang #6

Closed cowwoc closed 13 years ago

cowwoc commented 13 years ago

The following program causes Swing to hang for me:

Main.java

import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import org.uncommons.maths.random.MersenneTwisterRNG;
import org.uncommons.maths.random.Probability;
import org.uncommons.watchmaker.framework.*;
import org.uncommons.watchmaker.framework.factories.StringFactory;
import org.uncommons.watchmaker.framework.interactive.Renderer;
import org.uncommons.watchmaker.framework.operators.EvolutionPipeline;
import org.uncommons.watchmaker.framework.operators.StringCrossover;
import org.uncommons.watchmaker.framework.operators.StringMutation;
import org.uncommons.watchmaker.framework.selection.RouletteWheelSelection;
import org.uncommons.watchmaker.framework.termination.TargetFitness;
import org.uncommons.watchmaker.swing.evolutionmonitor.EvolutionMonitor;

public class Main
{
    public static void main(String[] args)
    {
        // Create a factory to generate random 11-character Strings.
        char[] chars = new char[27];
        for (char c = 'A'; c <= 'Z'; c++)
            chars[c - 'A'] = c;
        chars[26] = ' ';
        CandidateFactory<String> factory = new StringFactory(chars, 11);

        // Create a pipeline that applies cross-over then mutation.
        List<EvolutionaryOperator<String>> operators = new LinkedList<EvolutionaryOperator<String>>();
        operators.add(new StringMutation(chars, new Probability(0.02)));
        operators.add(new StringCrossover());
        EvolutionaryOperator<String> pipeline = new EvolutionPipeline<String>(operators);

        FitnessEvaluator<String> fitnessEvaluator = new StringEvaluator();
        SelectionStrategy<Object> selection = new RouletteWheelSelection();
        Random rng = new MersenneTwisterRNG();

        EvolutionEngine<String> engine = new GenerationalEvolutionEngine<String>(factory,
            pipeline,
            fitnessEvaluator,
            selection,
            rng);
        EvolutionMonitor monitor = new EvolutionMonitor<String>(new Renderer<String, JComponent>()
        {
            private final JLabel result = new JLabel();

            @Override
            public JComponent render(String entity)
            {
                result.setText(entity);
                return result;
            }
        }, false);
        engine.addEvolutionObserver(monitor);
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.getContentPane().add(monitor.getGUIComponent());
        frame.setSize(800, 600);
        frame.setVisible(true);
        String result = engine.evolve(10, 0, new TargetFitness(11, true));
        System.out.println(result);
    }
}

StringEvaluator.java

import java.util.List;
import org.uncommons.watchmaker.framework.FitnessEvaluator;

public class StringEvaluator implements FitnessEvaluator<String>
{
    private final String targetString = "HELLO WORLD";

    /**
     * Assigns one "fitness point" for every character in the
     * candidate String that matches the corresponding position in
     * the target string.
     */
    public double getFitness(String candidate,
                                                     List<? extends String> population)
    {
        int matches = 0;
        for (int i = 0; i < candidate.length(); i++)
        {
            if (candidate.charAt(i) == targetString.charAt(i))
                ++matches;
        }
        return matches;
    }

    public boolean isNatural()
    {
        return true;
    }
}

If you modify the EvolutionMonitor renderer to return null Swing does not hang. I believe that the WatchMaker framework is "starving" the Swing thread somehow. If you add other EvolutionObservers that print to stdout the hang will disappear, again because the framework is spending less time inside the EDT.

cowwoc commented 13 years ago

I'm fairly sure the problem is that EvolutionMonitor,populationUpdate() is queuing events faster than Swing can execute them. A simple fix is for EvolutionMonitor,populationUpdate() to only update Swing once every 300ms.

  1. When populationUpdate() gets invoked, compare the current time to lastUpdate.
  2. If 300ms elapsed, set lastUpdate to the current time and update Swing.
  3. Else, do nothing.
cowwoc commented 13 years ago

Dan,

The above commit fixes the problem for me.

Do-min-ik commented 11 years ago

Did you fix the code or just the examples? It still hangs for me.

(watchmaker-swing-0.7.1)