0

I have this issue, after building my problem implementation, when running Optaplanner:

java.lang.IllegalStateException: The selectionList contains 2 times the same selection (Edge to: MinMax1 from: s4,s5,) and (Edge to: Sum2 from: s3,s4,).
at org.optaplanner.core.impl.heuristic.selector.common.decorator.WeightFactorySelectionSorter.sort(WeightFactorySelectionSorter.java:58)
at org.optaplanner.core.impl.heuristic.selector.entity.decorator.SortingEntitySelector.constructCache(SortingEntitySelector.java:44)

I've read the question in here but no object is null. I've modified the Entity class, which is Edge, to implement AbstractPersistable and before that I've tried to override the equals() method with same results:

@PlanningEntity(difficultyWeightFactoryClass = EdgeDifficultyWeightFactory.class)
public class Edge extends AbstractPersistable{

/**
 * 
 */
private ArrayList<Node> from;
private Node to;
private double burstTime;


private BufferSize size;

public Edge(){
    from = new ArrayList<Node>();
    BufferSize p = new BufferSize();
    p.setSize(1);
    this.size = p;
            //new Random().nextInt(1000)+1;
    this.burstTime += this.size.getSize();
}

public void setFrom(Node from){
    this.from.add(from);
    this.calculateEdgeBurstTime();
}

public ArrayList<Node> getFrom(){
    return from;
}

public void setTo(Node to){
    this.to = to;
}

public Node getTo(){
    return to;
}

@PlanningVariable(valueRangeProviderRefs = {"bufferRange"})
public BufferSize getBufferSize(){
    return size;
}

public void setBufferSize(BufferSize size){
    this.size = size;
    System.out.println("Size has been set: "+size);
    this.calculateEdgeBurstTime();
}

public void calculateEdgeBurstTime(){
    for(Node node : this.from){
        this.burstTime+=this.size.getSize()*node.getNodeTime();
    }
}

public double getEdgeTime(){
    return burstTime;
}

@Override
public String toString(){
    StringBuffer sb = new StringBuffer();
    sb.append("Edge to: "+to.getID()+" from: ");
    for(Node node : this.from){
        sb.append(node.getID()+",");
    }
    return sb.toString();
}

@Override
public boolean equals(Object other){
    if (other == null) return false;
    if (other == this) return true;
    if (!(other instanceof Edge))return false;
    Edge otherMyClass = (Edge)other;
    boolean checkFrom = true;
    if(!(otherMyClass.getTo().getID().equals(this.getTo().getID()))) return false;
    for(Node node : otherMyClass.getFrom()){
        for(Node nd : this.getFrom()){
            if(!(node.getID().equals(nd.getID()))){
                checkFrom = false;
            }
        }
    }
    System.out.println("checked "+checkFrom+this.toString());
    return checkFrom;
}

@Override    
public int hashCode(){
    HashCodeBuilder builder = new HashCodeBuilder();
    builder.append(this.to.getID());
    builder.append(this.from);
    return builder.toHashCode();
}

}

To better clarify the problem, my EdgeDifficultyWeight class is implemented this way:

public class EdgeDifficultyWeightFactory implements SelectionSorterWeightFactory<SmartNodeGraph,Edge>{

public Comparable createSorterWeight(SmartNodeGraph graph, Edge edge) {

    int difficulty = edge.getFrom().size(); //Edges with more dependencies are more difficult to plan
    if(edge.getTo() instanceof NetworkNode){
        difficulty += difficulty; //Edges to NetworkNodes are more difficult to plan.
    }
    for(Node node : edge.getFrom()){
        if(node instanceof ProcessingNode){
            difficulty +=2; //If precedes form ProcessingNode is more difficult to optimise than sensors directly
        }else if(node instanceof SensorNode){
            difficulty +=1;
        }
    }
    return new EdgeDifficultyWeight(edge,difficulty);
}

public class EdgeDifficultyWeight implements Comparable<EdgeDifficultyWeight> {

    private final Edge edge;
    private final int difficulty;

    public EdgeDifficultyWeight(Edge edge, int difficulty){
        this.edge = edge;
        this.difficulty = difficulty;
    }

    public int compareTo(EdgeDifficultyWeight arg) {
        return new CompareToBuilder().append(arg.difficulty, this.difficulty).toComparison();
    }

    }

}

The planning problem is this: I have several separated Tree structures, the Root node of each tree has a ready time, which depends on all providers nodes. I want to make all root nodes time equal, each edge of the tree have a buffer which can be modified in size to change the time of the Father node. By changing the buffers in the several edges I want to make the times at the root level of all tree's equal.

Regarding the explanation, the Edges with more nodes (among other contraints), are more difficult to assign. Using the debugger, seem's that the problem is in this line:

Comparable difficultyWeight = selectionSorterWeightFactory.createSorterWeight(solution, selection);

The Comparable object is always the same.

Can anyone help on this?

Community
  • 1
  • 1
the_grandson
  • 15
  • 10

1 Answers1

0

Add .append(arg.edge.getId(), this.edge.getId()) in the EdgeDifficultyWeight to distinguish 2 weights that have different edges but the same difficulty.

public class EdgeDifficultyWeight implements Comparable<EdgeDifficultyWeight> {

    private final Edge edge;
    private final int difficulty;

    public EdgeDifficultyWeight(Edge edge, int difficulty){
        this.edge = edge;
        this.difficulty = difficulty;
    }

    public int compareTo(EdgeDifficultyWeight arg) {
        return new CompareToBuilder()
                .append(arg.difficulty, this.difficulty)
                .append(arg.edge.getId(), this.edge.getId())
                .toComparison();
    }

}
Geoffrey De Smet
  • 26,223
  • 11
  • 73
  • 120
  • Applied your solution with the same results: `java.lang.IllegalStateException: The selectionList contains 2 times the same selection (Edge to: MinMax1 from: s4,s5,) and (Edge to: Sum2 from: s3,s4,). at org.optaplanner.core.impl.heuristic.selector.common.decorator.WeightFactorySelectionSorter.sort(WeightFactorySelectionSorter.java:58)` Do you think can be a score calculation issue? – the_grandson Jan 22 '16 at 15:43
  • Solved the problem by appending `edge.hashCode()`, now i've got: `java.lang.IllegalStateException: UndoMove corruption: the beforeMoveScore (-2hard/0medium/0soft) is not the undoScore (-2hard/-2147483648medium/-2147483647soft) which is the uncorruptedScore (-2hard/-2147483648medium/-2147483647soft) of the workingSolution.` Which I think is a score calculation problem due to clonning of the solution, can be a possibility? – the_grandson Jan 22 '16 at 16:48
  • Your id's of your edge's won't be unique. They might all be `null` even. **Using `hashCode()` is bad** because hashCodes aren't unique, they are *mostly* unique. – Geoffrey De Smet Jan 23 '16 at 07:48
  • They are because I overrided the `hashCode` function. The last problem was happening due to a score issue, I was incrementing the `this.burstTime+=this.size.getSize()*node.getNodeTime();` instead of `this.burstTime=this.size.getSize()*node.getNodeTime();`. Thank you very much for your help. :) – the_grandson Jan 23 '16 at 21:56
  • 1
    I 'd still can't help but say consider renaming your `hashCode()` function to `getId()` :) By definition, hashcodes are not guaranteed to be unique and by definition id's are guaranteed to be unique. – Geoffrey De Smet Jan 25 '16 at 09:29
  • Of course, the worth of doing any such naming refactoring all depends on if this project will be in maintenance at some point (including if the prototype might become the production implementation). – Geoffrey De Smet Jan 25 '16 at 09:30