-1

Trying to work on a genetic algorithm here and I can't figure out why the same Array returns different results a few lines later.

This is the code:

Population(int PopulationSize, ArrayList<Vessel> vessels) {
    this.PopulationSize = PopulationSize;
    solutions = new ArrayList(PopulationSize);

    for (int i = 0; i < PopulationSize; i++) {
            Solution s = new Solution(vessels);   
            solutions.add(s);
            System.out.println(i+" : "+solutions.get(i).getFitness());              
    }

    for (int i = 0; i < solutions.size(); i++) {
        System.out.println(solutions.get(i).getFitness());

    }
}

Here's the output:

0 : 4432.4956
1 : 2673.922
2 : 5166.998
3 : 4396.5454
4 : 5687.8555
5 : 2907.695
6 : 5005.9937
7 : 3289.161
8 : 3302.1948
9 : 5193.338
5193.338
5193.338
5193.338
5193.338
5193.338
5193.338
5193.338
5193.338
5193.338
5193.338

More source code.`

Solution.java

package geneticalgorithm;

import static java.lang.Math.abs;
import java.util.ArrayList;

public class Solution {

    ArrayList<Vessel> vessels;

    public Solution(ArrayList<Vessel> vessels) {
        this.vessels = new ArrayList<>(vessels);
        generateSolution();
    }

    public void generateSolution() {    
        do {
            for (Vessel vessel : vessels) {
                vessel.generateParams();
            }
        } while(!isValid());
    }

    /* Getters */
    public Vessel getIndividual(int index) {
        return vessels.get(index);
    }

    // Save individual
    public void saveIndividual(int index, Vessel v) {
        vessels.set(index, v);
    }

    public float getFitness() {
        float fitness = 0, BPCost = 0, CHDelay=0, CBDelay=0;

        for (Vessel v : vessels) {
            BPCost += v.DistancePenaltyCost*abs(v.X-v.LowestCostBerthing);
            CHDelay +=  v.TimePenaltyCost*(abs(2*(v.Y + v.HandlingTime - v.SailingTime))/2);
            CBDelay += v.BunkeringDelayCost*v.BunkeringNeeded*(abs(2*(v.BA+v.BunkeringProcessing-v.SailingTime))/2);
        }
        fitness = BPCost + CHDelay + CBDelay;
        return fitness;
    }

    public boolean isValid() {
        boolean XOverlaps, YOverlaps;

        for (Vessel vTemp : vessels) {
            for (Vessel v : vessels) {
                // Initialization 
                    XOverlaps = YOverlaps = false;

                // Checking for X overlaps
                if(v.X > vTemp.X && v.X < (vTemp.X + vTemp.Length))
                    XOverlaps = true;

                // Checking for Y overlaps
                if(v.Y >= vTemp.Y && v.Y <= (vTemp.Y + vTemp.HandlingTime))
                    YOverlaps = true;   

                // Breaking the loop
                if(XOverlaps && YOverlaps)
                    return false;
            }
        }
        return true;
    }


}

Vessel.java

package geneticalgorithm;

import java.util.Random;

public class Vessel {

    Float X,Y,BA;
    Byte Z;

    Float ETA;
    Float Length;
    Float LowestCostBerthing;
    Float HandlingTime;
    Float SailingTime;
    Float DistancePenaltyCost;
    Float TimePenaltyCost;
    Float BunkeringDelayCost;
    Byte BunkeringNeeded;
    Float BunkeringProcessing;  

    void generateParams() {
        Random rand = new Random();
        Float MinBerth = 0.0f, MaxBerth = LowestCostBerthing + Length;

        if(LowestCostBerthing - Length > 0)
            MinBerth = LowestCostBerthing - Length;

        if(MaxBerth > DataLoader.Quay)
            MaxBerth = DataLoader.Quay - Length;

        X = rand.nextFloat() * (MaxBerth - MinBerth) + MinBerth;
        Y = rand.nextFloat() * ((ETA+24) - (ETA+1)) + (ETA+1);

        if(BunkeringNeeded==1)
        {
            Z = (byte) Math.round(Math.random()); // 0 OR 1
            if(Z!=0) {
                BA = Y;
            } else if(Y - ETA < BunkeringProcessing) {
                BA = SailingTime;
            } else if(Y - ETA > BunkeringProcessing) {
                BA = ETA;
            }

        } else {
            Z = 0;
            BA = 0.0f;
        }
    }

}

Any ideas? Thanks!

Gray
  • 115,027
  • 24
  • 293
  • 354
logicbloke
  • 139
  • 3
  • 13
  • 1
    Presuming `vessels` is an object, you're putting the *same* object into different `Solution` instances. – Makoto May 17 '17 at 16:02
  • 4
    You did not post the relevant code. Please post the `Solution` class. But by the behaviour, I would say you store the fitness in some static field. And please respect the Java naming conventions: fields (unless `static final`) should always start with a lowercase letter. – Turing85 May 17 '17 at 16:02
  • 2
    Solution uses a static field. Make it non-static and each Solution will have it's own value. – matt May 17 '17 at 16:03
  • 2
    These comments are insightful guesses, but they're just guesses. Please bolster this into a MVCE https://stackoverflow.com/help/mcve -- what we have so far isn't complete or verifiable. In making it *minimal* you might discover the problem. If so, be polite, supply the MVCE and the answer here. – slim May 17 '17 at 16:05
  • The vessels object is the same but there's an element of randomness added in the constructor and stored in a few different fields. None of them is static. – logicbloke May 17 '17 at 16:07
  • "The vessels object is the same" the same as what? You showed us barely anything... – Turing85 May 17 '17 at 16:08
  • 2
    @PhoenixNoor then show the class definition. You create a new Solution each time, and after you do, calling getFintness changes. Try doing `Solution a = new Soluction(vessels); System.out.println(a.getFitness()); Solution b = new Solution(vessels); System.out.println(a.getFitness() + ", " + b.getFitness());` So either you have static field *or* you are keeping a reference to "vessels" and modifying that. – matt May 17 '17 at 16:10
  • Just updated the post. I appreciate your help with this. – logicbloke May 17 '17 at 16:12
  • 3
    You're modifying your vessels. Each of your solutions reference the same vessels. Then you modify them, so each Solution reflects the changes. – matt May 17 '17 at 16:13
  • 1
    @matt is spot on as it looks. You give the same `Vessel`s to all your `Solution`s and you keep modifying those `Vessel`s. Therefore, the fitness at the end is the same for all `Solution`s. – Turing85 May 17 '17 at 16:14
  • This is now *verifiable* and *complete*, but next time please work on *minimal*. – slim May 17 '17 at 16:23
  • I was suspecting it's working on the same reference, but how do we create copies of objects that are unrelated? Thanks! – logicbloke May 17 '17 at 16:52

3 Answers3

2

Trying to work on a genetic algorithm here and I can't figure out why the same Array returns different results a few lines later.

Each Solution is passed in a list of Vessel objects. This list is used to create another list of vessels inside each Solution but it's important to note that the new list still contains the original Vessel objects.

this.vessels = new ArrayList<>(vessels);

This code doesn't create new Vessel objects – it just creates a new list of the same objects. This means that even though you are creating a number of different Solutions, each one is dealing with the same Vessels. Whenever generateSolution() is called, it is calling vessel.generateParams() on the the same shared Vessels and updating the values in the previous Solution objects. That's why the value generated by the last Solution is the value printed out when you go back through the list.

I'm not sure what the intention is but if you are surprised by this then you probably want to add new Vessel objects to each of your solutions and not reuse the same list.

Edit:

In looking more at your Vessel object, you have some fields which really are constants:

Float Length;
Float LowestCostBerthing;
Float HandlingTime;

And you have some other fields which are calculations:

Float X,Y,BA;
Byte Z;

You really should consider separating the constants about the Vessel from the calculations generated by your solutions. Also, as an aside, fields should start with lowercase letters, be primitives (float, byte) instead of objects most likely, and should be final if they are immutable.

One idea would be to have a VesselCalculation object which would hold the calculations and also have a Vessel field:

public class VesselCalculation {
    private final Vessel vessel;
    private float x, y, ba;
    private byte z;

So then the Solution would generate a list of VesselCalculation which would be modified. The Vessel objects would be shared with the different VesselCalculation objects but would stay immutable.

private final List<VesselCalculation> vesselCalculations = new ArrayList<>();
public Solution(ArrayList<Vessel> vessels) {
    for (Vessel vessel : vessels) {
         vesselCalculations.add(new VesselCalculation(vessel));
    }
    generateSolution();
}

generateSolution() would then make changes only to the VesselCalculation objects that are local to this particular Solution.

Gray
  • 115,027
  • 24
  • 293
  • 354
  • 1
    Good spot, on the `Vessel`s themselves also being modified. I guess I'm so used to *not* modifying things like this, I skim-read the code assuming they wouldn't be. – slim May 17 '17 at 16:31
  • Yeah mutability is so important @slim and we get tripped up when someone has a pattern like this that isn't. – Gray May 17 '17 at 16:51
  • The intention is to have separate unrelated copies, how do we achieve that? Thank you for all your help. – logicbloke May 17 '17 at 16:56
1

getFitness() appears to use only values extracted from vessels.

Since every Solution contains the same instance of vessels, every solution will return the same response for getFitness(), given a particular state of vessels.

The constructor for Solution calls generateSolution() which modifies the contents of vessels. That's the reason your getFitness() result changes every time you create a new Solution.

I don't know the details of your desired algorithm, but it seems likely that each Solution should have its own copy of the list. It's up to you whether it should make that copy itself in its constructor, or whether whatever calls the constructor is responsible for creating that copy.

Because the Vessel objects themselves are mutable (and the mutability is used) you also need to copy them, rather than making another list containing references to the same ones.

In this kind of situation, using immutable collections and immutable objects can save you lots of head scratching.

slim
  • 40,215
  • 13
  • 94
  • 127
0

If you're having the same problem, I was able to solve this by using this solution. The idea is to loop over the array and copy each and every element one by one while you're cloning them. Calling the clone() function over the ArrayList has no effect but cloning the ArrayList while keeping the same references.

You're going to have to implement the cloneable interface and to override the clone method while cloning each and every field manually.

Community
  • 1
  • 1
logicbloke
  • 139
  • 3
  • 13
  • See my answer @PheonixNoor. A better solution would be to separate your constants from the calculation fields. – Gray May 17 '17 at 17:21