1

I have a List that I have populated in the main method of a console project. I pass this population to a method which is meant to take two members of the population and decompose and recombine them in a way to create two new unique members which will later be added to the population.

However when I manipulate the two original members to create the two new unique members the two original members change with in the initial population (hence altering the initial population).This means that when I go to add the new members I get duplication of entries into the List.

I'm not doing anything overly complicated I think I am just doing something stupid.

Does any one have any insight as to why this is happening ?

Here is the method that gets called to choose to initial two members of the population:

public static List<Chromosome<Gene>> runEpoch(Random rand, List<Chromosome<Gene>> population, SelectionMethod selectionMethod)
    {
        int populationSize = population.Count;
        int selectionCount = (int)Math.Truncate((population.Count * 0.75));

        if (selectionMethod == SelectionMethod.Tournament)
        {
            for (int i = 0; i < selectionCount; i++)
            {
                Chromosome<Gene> parent = selection.runTournament(rand, population);
                Chromosome<Gene> parentTwo = selection.runTournament(rand, population);

                //Checks for the presence of incestuous mating. In some cases incestuous mating causes a stack overflow to occur that the program can not recover from 
                if (parent != parentTwo)
                {
                    //where runGeneOperators calls the crossOver method directly
                    offSpring = runGeneOperators(rand, parent, parentTwo);
                }
                else
                {
                    i--;
                }
            }
        }
        else
        {
            //NSGAII
        }
        //fixPopulation is meant to sort and remove any excess members
        return fixPopulation(rand, population, selectionMethod, populationSize); ;
    }

And here is the code that is creating the two new unique members :

public List<Chromosome<Gene>> crossOver(Random rand, Chromosome<Gene> parentOne, Chromosome<Gene> parentTwo)
    {
        List<Chromosome<Gene>> offSpring = new List<Chromosome<Gene>>();

        int crossPtOne = rand.Next(0, parentOne.Length);
        int crossPtTwo = rand.Next(0, parentTwo.Length);

        if ((crossPtOne == 0) && (crossPtTwo == 0))
        {
            offSpring.Add(parentOne);
            offSpring.Add(parentTwo);

            return offSpring;
        }
        else
        {
            GeneNode<Gene> fragOne = parentOne.Children[crossPtOne];
            GeneNode<Gene> fragTwo = parentTwo.Children[crossPtTwo];

            crossOverPoint = crossPtOne;
            GeneNode<Gene> genotype = performCrossOver(parentOne.Genotype, fragTwo);
            success = false;

            parentOne.repair(genotype);

            offSpring.Add(parentOne);

            crossOverPoint = crossPtTwo;

            GeneNode<Gene> genotype2 = performCrossOver(parentTwo.Genotype, fragOne);
            success = false;

            parentTwo.repair(genotype2);

            offSpring.Add(parentTwo);
        }

        return offSpring;
    }

    private GeneNode<Gene> performCrossOver(GeneNode<Gene> tree, GeneNode<Gene> frag)
    {
        if (tree != null)
        {
            if (crossOverPoint > 0)
            {
                if (!success && tree.Left != null)
                {
                    crossOverPoint--;
                    tree.Children[0] = performCrossOver(tree.Left, frag);
                }
            }

            if (crossOverPoint > 0)
            {
                if (!success && tree.Right != null)
                {
                    crossOverPoint--;
                    tree.Children[1] = performCrossOver(tree.Right, frag);
                }
            }
        }

        if (!success)
        {
            if (crossOverPoint == 0)
            {
                success = true;
                return frag;
            }
        }

        return tree;
    }
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
user648132
  • 115
  • 3
  • 7
  • Nice job paring this down to a small repro. Create a new list with the next generation. – Hans Passant Apr 10 '11 at 20:38
  • 1
    Where is `offSpring` declared? Is it a global variable? – Lasse V. Karlsen Apr 10 '11 at 20:38
  • @Lasse yeah its declared globally. @Hans sorry im rather new to stackoverflow what do you mean by paring this down to a small repro. – user648132 Apr 10 '11 at 20:52
  • He meant that you should've tried to reduce the code down to something we could compile and run ourselves, that way you increase the speed and likelihood of getting an answer tenfold. Also, sometimes you end up figuring out what the problem is before you get as far as actually posting it. Keep it in mind for future questions. – Lasse V. Karlsen Apr 10 '11 at 21:22

3 Answers3

5

In C#, objects are reference types, meaning adding something to a collection only adds a reference. If you manipulate a variable with the same reference (in your case, the "original" objects), all references pointing to that object will be changed as well. You need to copy the object somehow to have a different object and manipulate that.

Femaref
  • 60,705
  • 7
  • 138
  • 176
  • So copy population to say a new population object ? Something like this : List newPop = oldPop; As this is what I thought, for exactly the same reason. I have tried this and the same happens but to newPop as well as oldPop. Or do you mean something like copy all member from oldPop to newPop – user648132 Apr 10 '11 at 20:40
  • If you want to have a totally seperate object, you will need to recreate all involved objects (also the content of the list). `List newPop = oldPop;` acts the same way, as `List` is a reference type as well, you are just assigning references and will be accessing the same object. – Femaref Apr 10 '11 at 20:43
  • This is what I feared. Do you know of a quick way to do so ? – user648132 Apr 10 '11 at 20:45
0

To manipulate your collection and objects within it without altering it, you need to do a deep clone of the collection and it's containing objects, so you can alter list A without altering clone B. If you just wish to change some object inside the list you need to make a deep clone of the object inside the list and then alter the deep clone. This will make sure that your original is not altered.

The IClonable interface should be implemented on your objects to make them clonable.

EDIT: Per Henk's Comments, you should just implement your own copy method without implementing the IClonable interface, as per this article.

Cloning of objects comes in two forms, deep and shallow.

Shallow: This form of cloning creates a new object and then copyies the nonstatic fields of the current object to the new object. If a field is a value type, a bit-by-bit copy of the field is performed. If a field is a reference type, the reference is copied but the referred object is not; therefore, the original object and its clone refer to the same object.

Deep:This creates a new object and clones all data members and fields of the clonable object. This object references X2 where the original will reference X.

Here a post from SO that might assist you in cloning.

Community
  • 1
  • 1
Tony The Lion
  • 61,704
  • 67
  • 242
  • 415
  • @Henk: What do you mean? I don't see any sign of abandonment on the MSDN site? – Tony The Lion Apr 10 '11 at 20:51
  • 1
    A quick search found this (the MSDN ref is dead alas): http://blogs.msdn.com/b/brada/archive/2004/05/03/125427.aspx – H H Apr 10 '11 at 20:59
  • @user64: just don't think this is a simple solution. Look into immutable types first. – H H Apr 10 '11 at 21:04
0

Item 1 - Keep your list references private

From a API perspective, an object should never give out the reference to its internal list. This is due to the issue you have just found, whereby some other consumer code can go ahead and modify that list as it sees fit. Every reference of that list, as shared around, including the 'owning' object, has their list updated. This is usually not what was intended.

Further, the responsibility is not on the consumer to make its own copy. The responsibility is on the owner of the list to keep its version private.

It's easy enough to create a new List

private List<Gene> initialGeneList;
public List<Gene> InitialGenes 
{ 
    get { return new List<Gene>(initialGeneList); } // return a new list
}

This advice also includes passing your own list to another class/method.

Item 2 - Consider using an immutable object

Without going too much into reference vs value types, what this means is treat the object like a value type, and whenever you have an operation, return a new object instead of mutating the original.

Real value types in .net, like integers and DateTimes have the semantic of copy on assignment. e.g. When you assign, a copy is created.

Reference types can also behave like value types, and strings are good example. Many people make the following mistake, because they fail to realize the value-type semantics of System.String.

string s = "Hello World";
s.Substring(0,5);     // s unchanged. still 'Hello World'
s = s.Substring(0,5); // s changed. now 'Hello'

The string method Substring() doesn't mutate the original object, but instead returns a new one containing the result of the operation.

Where appropriate, the use of immutable objects can make the program a lot easier to understand and can reduce defects considerably.

When implementing immutable objects, one caveat is the likely need to implement value equality for comparing two objects (by overriding Equals). Objects are reference types, and by default two objects are equal if their references are equal.

In this snippet, the values of the objects are equal, but they are different objects.

string s1 = "Hello";
string s2 = string.Concat("He","l", "lo");
bool equal = s1.Equals(s2);
bool referenceEqual = object.ReferenceEquals(s1, s2);

// s1 == s2 => True, Reference Equal => False
Console.Write("s1 == s2 => {0}, Reference Equal => {1}", equal, referenceEqual);
Community
  • 1
  • 1
Robert Paulson
  • 17,603
  • 5
  • 34
  • 53