Search code examples
optaplanner

Optaplanner Shadow Variables


Recently learning Optaplanner constraint framework, but I can't understand the shadow variables, the documentation only has some simple descriptions, hope someone can help me, here is my code,thanks.

@Data
@PlanningEntity
public class CloudProcess {

private int requireCpuPower;

private int requireMemory;

private int requireNetworkBandwidth;

@PlanningVariable(valueRangeProviderRefs = {"computerRange"})
private CloudComputer cloudComputer;

}

@Data
@PlanningEntity
public class CloudComputer {
private int cpuPower;

private int memory;

private int networkBandwidth;

private int cost;

@InverseRelationShadowVariable(sourceVariableName = "cloudComputer")
private List<CloudProcess> processList = new ArrayList<>();
}

@PlanningSolution
@Data
public class CloudBalance {

@PlanningEntityCollectionProperty
private List<CloudProcess> processList;


@ProblemFactCollectionProperty
@ValueRangeProvider(id="computerRange")
private List<CloudComputer> computerList;

@PlanningScore
private HardSoftScore score;
}

java.lang.StackOverflowError: null at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na]


Solution

  • Explanation of the problem

    The java.lang.StackOverflowError is caused by combination of two facts:

    1. By introducing @InverseRelationShadowVariable processList on the CloudComputer you are creating a bi-directional relationship between CloudComputer and CloudProcess.
    2. You are using Lombok @Data, which generates equals(), hashCode() and toString() implementations that use all the class fields by default.

    That leads to an infinite loop because, for example, to compute Process 1's hash code, Computer A's hash code is needed and that includes its processList hashCode, which contains Computer A and so on. It's impossible to compute the hash code and the method will run until the current thread's stack overflows with local variables holding the partial results of the computation.

    Solution 1

    If you don't need Lombok-generated hashCode(), equals() and toString(), then replace @Data with @Getter and @Setter.

    Solution 2

    If you want all the code generated by @Data including hashCode(), equals() and toString(), then you must replace it with more annotations and exclude the computer and processList fields to break the loop:

    @PlanningEntity
    @Getter
    @Setter
    @ToString
    @EqualsAndHashCode(callSuper = true, exclude = "computer")
    public class CloudProcess {
    
        private int requiredCpuPower;
        private int requiredMemory;
        private int requiredNetworkBandwidth;
    
        @PlanningVariable(valueRangeProviderRefs = {"computerRange"})
        private CloudComputer computer;
    }
    
    @PlanningEntity
    @Getter
    @Setter
    @ToString(exclude = "processList")
    @EqualsAndHashCode(callSuper = true, exclude = "processList")
    public class CloudComputer {
    
        private int cpuPower;
        private int memory;
        private int networkBandwidth;
        private int cost;
    
    
        @InverseRelationShadowVariable(sourceVariableName = "computer")
        private List<CloudProcess> processList = new ArrayList<>();
    }