0

In C# I have written a genetic algorithm that creates geometry for a CAD program (Rhino). This algorithm is contained in a library and I have written a small windows forms plugin for the CAD program. A call to the GA.run() function will return a List which populates a ListBox and selecting an Item will generate the geometry in the CAD program.

In the windows forms application I create an instance of the GA class and by clicking the start button the various GA settings are gathered from the form and the GA.run() function is called. Now, as this is a genetic algorithm, it will run a number of generations before returning. However, I would like to have the option of stopping the function before it runs all generations. How can I stop the GA.run() function from within my plugin? When I hit the run button, the windows forms application "pauses" until GA.run() returns its List...

I guess there is a better way of calling GA.run()? ..

stop button code:

private void runBtn_Click(object sender, EventArgs e)
{
    // get rule settings
    SettingsHolder gaSettings = new SettingsHolder();
    gaSettings.GenMaxrun = (int)gensNUD.Value;
    ..

    gaAlgo = new GA(algoSettings);

    try
    {
        currentPopulation = paretoAlgo.run();
    }
    catch(Exception e)
    {
        MessageBox.Show("exc while trying run: " + e);
    }

    // iterate list and list in form
    popLB.Items.Clear();
    foreach (Chromosome c in currentPopulation)
    {
        popLB.Items.Add(new KeyValuePair<String, int> (c.present(), id));
    }
}

GA.run():

   public List<Chromosome> run()
    {
        try
        {
            initPop();

            // loop until genMaxrun or no new achieved
            do
            {
                // create geometries from chromosomes
                foreach (Chromosome chromo in population)
                {
                    chromo.embryogeny(Settings);
                }

                // step 2:Fitness

                // union the population and archive
                unionPop = new List<Chromosome>(population);
                unionPop.AddRange(archive);

                calculateDomination(unionPop);

                foreach(Chromosome chromo in unionPop)
                {
                    chromo.calculateRawFitness(unionPop);
                    chromo.calculateDensity(unionPop);
                    chromo.setFitness();
                }

                environmentalSelection();

                // increase Generation +1
                generation += 1;

                // create new population...
                population = reproduction(generation);                    
            }
            while (generation < Settings.GenMaxrun);

        }
        catch (Exception e)
        {
           throw new Exception("Error in SPEA run", e);
        }

        return archive;
    }

Is there a way to add a check within the while-loop of run()? Can a stop button click event in the forms application change a parameter of GA (as it is executing the run() function) and the run() function will abort? Or can I make a check from the GA.run() to a parameter in the caller? Or do I need to use threads?

Let me know if I need to explain something more accurately.

Cheers, Eirik

Eirik
  • 649
  • 2
  • 5
  • 13

2 Answers2

1

Although you can do something with Application.DoEvents in a loop, you're better off having that code run in a Task or a BackgroundWorker. Either of those will start the genetic algorithm running on a separate thread, leaving the Form active. You can then have a Cancel button that stops the background processing.

The BackgroundWorker fully supports cancellation and periodically reporting progress to the form. Check out the examples on that page. I think it'll do what you want.

There are those who say that Task is a better solution because it uses the new threading model. In general, Task is what I'd use for a production program because it has some other options that I find useful, but BackgroundWorker works just fine.

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
  • Yep Application.DoEvents, shudder every time I see it. – Tony Hopkinson Mar 02 '13 at 14:58
  • thanks, it works great. BUT ... as my GA.run() is a loop that refines a population for each iteration, a cancel should also return a value. Inside GA.run() I maintain a List population. On success, the GA.run() returns this list. When I try to access the e.result it doesn't (and shouldn't) work. http://stackoverflow.com/questions/856313/c-sharp-background-worker-setting-e-result-in-dowork-and-getting-value-back-in-w ... How can I return the population List at the point where cancelled? – Eirik Mar 02 '13 at 20:03
  • Could I use the ReportProgress function and report the current population in each loop iteration? – Eirik Mar 02 '13 at 20:13
  • 2
    More complex every minute. Make Population a separate class. You main form can read it when it needs to you GA.Run thread writes to it when it needs to. So "Cancel" becomes stop changing it. – Tony Hopkinson Mar 02 '13 at 20:39
  • 1
    @Eirik: You could report progress every iteration, but you're probably better off doing as @Tony H. said. Just be careful not to access the `Population` from the UI thread while it's still being calculated. Unless you protect it with a lock. – Jim Mischel Mar 02 '13 at 22:30
  • thanks Tony and Jim. Works like a charm! A question on threads: My GA.run() is now running as a BackgroundWorker. It can create about 100 generations of geometry with a population of 100 in 2 mins. As the process runs, the CPU is at about 1% load. As a Backgroundworker, can I start off new threads? Say one thread per individual in my population, in order to speed up the algorithm? Is it feasible, does it add any performance or is there overhead by starting of new threads? – Eirik Mar 03 '13 at 19:21
1

There is a way of doing that Your CA is in a tight loop. The windows message pump isn't executing, so to get say user just clicked on Cancel button, you'd have to call DoEvents inside your GA loop, and then poll for a flag that Cancel had been clicked. Trust me you do not want to do that, it's inefficient, fragile, messy and a total PIA to unit test.

What you need is a thread. You also need to decide whether your form can be used while the GA is doing it's work. If not just throw up a "working... " style form modally and put a cacle button on it. That's in your UI thread, so it will get the click and then you stop the GA thread.

There's a lot more to threading, but .Net has some nice stuff for just getting going on this stuff. BackgroundWorker would be a good one to look up.

Tony Hopkinson
  • 20,172
  • 3
  • 31
  • 39