0

I've recently started working with C# and I'm currently trying to implement a version of GA to solve Schwefel’s function(See code below). The code is based on a working Processing code that I built.

The first generation(first 100 individuals) seems to work fine but after that the fitness function gets repetitive values. I'm sure I'm missing something here but does anyone know what might be the problem?

    public void button21_Click(object sender, EventArgs e)
    {
        Population p;
        // populationNum = 100;
        p = new Population();
        int gen = 0;
        while (gen < 8000)
        {
            p.evolve();
        }
        ++gen;
    }

    //Class Genotype
    public partial class Genotype
    {
        public int[] genes;

        public Genotype()
        {
            genes = new int[2];
            for (int i = 0; i < genes.Length; i++)
            {
                Random rnd = new Random(int.Parse(Guid.NewGuid().ToString().Substring(0, 8), System.Globalization.NumberStyles.HexNumber));
                //Random rnd = new Random(0);
                int random = rnd.Next(256);
                genes[i] = (int)random;
            }
        }

        public void mutate()
        {
            //5% mutation rate
            for (int i = 0; i < genes.Length; i++)
            {
                Random rnd = new Random(int.Parse(Guid.NewGuid().ToString().Substring(0, 8), System.Globalization.NumberStyles.HexNumber));
                int random = rnd.Next(100);
                if (random < 5)
                {
                    //Random genernd = new Random();
                    int generandom = rnd.Next(256);
                    genes[i] = (int)generandom;
                }
            }
        }
    }

    static Genotype crossover(Genotype a, Genotype b)
    {
        Genotype c = new Genotype();
        for (int i = 0; i < c.genes.Length; i++)
        {
            //50-50 chance of selection
            Random rnd = new Random(int.Parse(Guid.NewGuid().ToString().Substring(0, 8), System.Globalization.NumberStyles.HexNumber));

            float random = rnd.Next(0, 1);
            if (random < 0.5)
            {
                c.genes[i] = a.genes[i];
            }
            else
            {
                c.genes[i] = b.genes[i];
            }
        }
        return c;
    }


    //Class Phenotype
    public partial class Phenotype
    {
        double i_x;
        double i_y;

        public Phenotype(Genotype g)
        {
            i_x = g.genes[0] * 500 / 256;
            i_y = g.genes[1] * 500 / 256;
        }

        public double evaluate()
        {
            double fitness = 0;
            fitness -= (-1.0*i_x * Math.Sin(Math.Sqrt(Math.Abs(i_x)))) + (-1.0*i_y * Math.Sin(Math.Sqrt(Math.Abs(i_y))));
            Console.WriteLine(fitness);
            return fitness;  
        }
    }

    //Class Individual
    public partial class Individual : IComparable<Individual>
    {
        public Genotype i_genotype;
        public Phenotype i_phenotype;
        double i_fitness;

        public Individual()
        {
            this.i_genotype = new Genotype();
            this.i_phenotype = new Phenotype(i_genotype);
            this.i_fitness = 0;
        }

        public void evaluate()
        {
            i_fitness = i_phenotype.evaluate();
        }

        int IComparable<Individual>.CompareTo(Individual objI)
        {
            Individual iToCompare = (Individual)objI;
            if (i_fitness < iToCompare.i_fitness)
            {
                return -1; //if I am less fit than iCompare return -1
            }
            else if (i_fitness > iToCompare.i_fitness)
            {
                return 1; //if I am fitter than iCompare return 1
            }

            return 0; // if we are equally return 0
        }
    }

    static Individual breed(Individual a, Individual b)
    {
        Individual c = new Individual();
        c.i_genotype = crossover(a.i_genotype, b.i_genotype);
        c.i_genotype.mutate();
        c.i_phenotype = new Phenotype(c.i_genotype);
        return c;
    }

    //Class Population
    public class Population
    {
        Individual[] pop;
        int populationNum = 100;

        public Population()
        {
            pop = new Individual[populationNum];
            for (int i = 0; i < populationNum; i++)
            {
                this.pop[i] = new Individual();
                pop[i].evaluate();
            }
            Array.Sort(this.pop);
        }

        public void evolve()
        {
            Individual a = select();
            Individual b = select();
            //breed the two selected individuals
            Individual x = breed(a, b);
            //place the offspring in the lowest position in the population, thus replacing the previously weakest offspring
            pop[0] = x;
            //evaluate the new individual (grow)
            x.evaluate();
            //the fitter offspring will find its way in the population ranks
            Array.Sort(this.pop);
            //rnd = new Random(0);
        }

        Individual select()
        {
            Random rnd = new Random(int.Parse(Guid.NewGuid().ToString().Substring(0, 8), System.Globalization.NumberStyles.HexNumber));
            float random = rnd.Next(0, 1);
            //skew distribution; multiplying by 99.999999 scales a number from 0-1 to 0-99, BUT NOT 100
            //the sqrt of a number between 0-1 has bigger possibilities of giving us a smaller number
            //if we subtract that squares number from 1 the opposite is true-> we have bigger possibilities of having a larger number
            int which = (int)Math.Floor(((float)populationNum - 1e-6) * (1.0 - Math.Pow(random, random)));
            return pop[which];
        }
    }
John Saunders
  • 160,644
  • 26
  • 247
  • 397
sles
  • 41
  • 1
  • 2
  • 4
    What is "GA", for us old folks, do you mean "Genetic Algorithm"? – John Saunders Aug 08 '14 at 19:41
  • Like John Saunders mentioned, definitions would be good. What would better is a link to the function, or an explanation of it's logic. The problem is probably with your logic and maybe not a code mistake? If we don't know the logic we cannot help weasel out an issue if that is the cause. @John yes GA is Genetic Algorithm. – TWhite Aug 08 '14 at 19:45
  • 1
    GA is largely based on extensively researching the problem itself. You might experience convergence because your fitness function isn't good enough, you have problems with crossovers, or your selection method is not ideal for this specific problem. – WSBT Aug 08 '14 at 19:49
  • You might also want to try your question over at http://biology.stackexchange.com/. – TWhite Aug 08 '14 at 20:05
  • I was pretty sure he must mean "Genetic Algorithm" considering I started off life as a BIO major. My real point is that "GA" is two letters, and might have meant something else. It's not quite as popular a term as "AI", for instance. – John Saunders Aug 08 '14 at 20:13
  • 1
    Side note: your usage of `Random` is quite random, please consider more regular practice of having [single static generator](http://stackoverflow.com/questions/767999/random-number-generator-only-generating-one-random-number) when posting public samples (unless there are important reasons to go some other particular route). – Alexei Levenkov Aug 08 '14 at 20:24
  • Thank you all and apologies for the GA. For some reason I cannot get the algorithm to work and therefore for the purposes of my project I tweaked this algorithm http://msdn.microsoft.com/en-us/magazine/jj133825.aspx which works fine. Cheers. – sles Aug 12 '14 at 08:38
  • `static Random rnd = new Random();` <-- use it – John Alexiou Aug 13 '14 at 21:07
  • Also, do not use an array (`Individual[]`) here, rather a `List`, `Stack` or a `Queue` depending on how you want to add/remove items. – John Alexiou Aug 13 '14 at 21:10
  • only init Random once. Otherwise you will always get the very same return value – BlueWizard Jun 02 '15 at 16:23

4 Answers4

2

This an updated code that I think it performs well:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Threading;

    namespace ConsoleApplication8
   {
    class Program
     {
       static Random random = new Random();

        static void Main(string[] args)
        {
        Population p;
        System.IO.StreamWriter file = new System.IO.StreamWriter("c:\\test.txt");
        int population = 100;
        p = new Population(file, population);

        int gen = 0;
        while (gen <= 1000)
        {
            p.evolve(file);
            ++gen;
        }
        file.Close();
    }

    public static double GetRandomNumber(double min, double max)
    {
        return (random.NextDouble() * (max - min)) + min;
        //return random.NextDouble() *random.Next(min,max);
    }

    //Class Genotype
    public class Genotype
    {
        public int[] genes;

        public Genotype()
        {
            this.genes = new int[2];
            for (int i = 0; i < genes.Length; i++)
            {
                this.genes[i] = (int)GetRandomNumber(-500.0, 500.0);
            }
        }

        public void mutate()
        {
            //5% mutation rate
            for (int i = 0; i < genes.Length; i++)
            {
                 if (GetRandomNumber(0.0, 100) < 5)
                 {
                    //Random genernd = new Random();
                    this.genes[i] = (int)GetRandomNumber(0.0, 256.0);
                 }
            }
        }
    }

    static Genotype crossover(Genotype a, Genotype b)
    {

        Genotype c = new Genotype();
        for (int i = 0; i < c.genes.Length; i++)
        {
            //50-50 chance of selection
            if (GetRandomNumber(0.0, 1) < 0.5)
            {
                c.genes[i] = a.genes[i];
            }
            else
            {
                c.genes[i] = b.genes[i];
            }
        }
        return c;
    }

    //Class Phenotype
    public class Phenotype
    {
        double i_x;
        double i_y;

        public Phenotype(Genotype g)
        {
            this.i_x = g.genes[0];
            this.i_y = g.genes[1];
        }

        public double evaluate(System.IO.StreamWriter file)
        {
            double fitness = 0;
            //fitness -= i_x + i_y;
            fitness -= (i_x*Math.Sin(Math.Sqrt(Math.Abs(i_x)))) + i_y*(Math.Sin(Math.Sqrt(Math.Abs(i_y))));
            file.WriteLine(fitness);
            return fitness;  
        }
    }

    //Class Individual
    public class Individual : IComparable<Individual>
    {
        public Genotype i_genotype;
        public Phenotype i_phenotype;
        double i_fitness;

        public Individual()
        {
            this.i_genotype = new Genotype();
            this.i_phenotype = new Phenotype(i_genotype);
            this.i_fitness = 0.0;
        }

        public void evaluate(System.IO.StreamWriter file)
        {
            this.i_fitness = i_phenotype.evaluate(file);
        }

        int IComparable<Individual>.CompareTo(Individual objI)
        {
            Individual iToCompare = (Individual)objI;
            if (i_fitness < iToCompare.i_fitness)
            {
                return -1; //if I am less fit than iCompare return -1
            }
            else if (i_fitness > iToCompare.i_fitness)
            {
                return 1; //if I am fitter than iCompare return 1
            }
            return 0; // if we are equally return 0
        }
    }

    public static Individual breed(Individual a, Individual b)
    {
        Individual c = new Individual();
        c.i_genotype = crossover(a.i_genotype, b.i_genotype);
        c.i_genotype.mutate();
        c.i_phenotype = new Phenotype(c.i_genotype);
        return c;
    }

    //Class Population
    public class Population
    {
        Individual[] pop;
        //int populationNum = 100;

        public Population(System.IO.StreamWriter file, int populationNum)
        {
            this.pop = new Individual[populationNum];

            for (int i = 0; i < populationNum; i++)
            {
                this.pop[i] = new Individual();
                this.pop[i].evaluate(file);
            }
            Array.Sort(pop);
        }

        public void evolve(System.IO.StreamWriter file)
        {
            Individual a = select(100);
            Individual b = select(100);
            //breed the two selected individuals
            Individual x = breed(a, b);
            //place the offspring in the lowest position in the population, thus  replacing the previously weakest offspring
            this.pop[0] = x;
            //evaluate the new individual (grow)
            x.evaluate(file);
            //the fitter offspring will find its way in the population ranks
            Array.Sort(pop);
        }

        Individual select(int popNum)
        {
            //skew distribution; multiplying by 99.999999 scales a number from 0-1 to  0-99, BUT NOT 100
            //the sqrt of a number between 0-1 has bigger possibilities of giving us a smaller number
            //if we subtract that squares number from 1 the opposite is true-> we have bigger possibilities of having a larger number
           int which = (int)Math.Floor(((float)popNum - 1E-6) * (1.0 - Math.Pow(GetRandomNumber(0.0, 1.0), 2)));
           return pop[which];
        }
    }
}

}

sles
  • 41
  • 1
  • 2
1

This is a problem:

float random = rnd.Next(0, 1);   // returns an integer from 0 to 0 as a float
// Documentation states the second argument is exclusive

Try

float random = (float)rnd.NextDouble(); // rnd should be static, init'd once.

and replace all instances of Individual[] with List<Individual> which wraps an array and allows for easy Add(), InsertAt() and RemoveAt() methods.

PS. Also common convention has it to use PascalCasing for all methods and properties.

John Alexiou
  • 28,472
  • 11
  • 77
  • 133
0

I think the biggest issue is with your select function.

The success of GA's depends a lot on picking the right Mutation, Evaluation and Selection techniques, although at first glance your selection function seems elegant to skew distribution, you're only skewing it based on relative position (i.e. Pop[0] < Pop[1]) but you're not taking into account how different they are from each other.

In GA's there's a HUGE difference between having the best individual have 100.0 Fitness and the Second have 99.9 than the best have 100.0 and the second have 75.0 and your selection function completely ignores this fact.

What is happening, why you see the repetitive fitness values, is because you're picking roughly the same individuals over and over, making your genetic pool stagnant and stalling in a local minimum (or maximum whatever you're looking for).

If you look for a method like Roullette (http://en.wikipedia.org/wiki/Fitness_proportionate_selection) they pick the probability as a function of the individual fitness divided over the total fitness, sharing the 'chance' of being picked among more individuals depending on how they behave, although this method can also get trapped in locals, it far less prone to than what you currently have, this should give you a very good boost on exploring the search space.

TL;DR - The selection function is not good enough as it is skewing the distribution too harshly and is only taking into account relative comparisons.

Sisnett
  • 101
  • 5
0

Random.next(int min,int max), will generate only integers between the min and max values. try the (rnd.NextDouble) to generate a random number between 0 and 1. this what i can help right now :)