2014-07-02 74 views
2

我一直在處理幾天沒有明顯原因的分數腐敗錯誤。該錯誤僅在FULL_ASSERT模式下出現,並且與drools文件上定義的約束無關。增量分數計算錯誤?

以下是錯誤:

014-07-02 14:51:49,037 [SwingWorker-pool-1-thread-4] TRACE   Move index (0), score (-4/-2450/-240/-170), accepted (false) for move ([email protected] => EMP2). 
     java.util.concurrent.ExecutionException: java.lang.IllegalStateException: Score corruption: the workingScore (-3/-1890/-640/-170) is not the uncorruptedScore (-3/-1890/-640/-250) after completedAction ([email protected] => EMP4): 
     The corrupted scoreDirector has 1 ConstraintMatch(s) which are in excess (and should not be there): 
     com.abcdl.be.solver/MinimizeTotalTime/level3/[[email protected]]=-170 
     The corrupted scoreDirector has 1 ConstraintMatch(s) which are missing: 
     com.abcdl.be.solver/MinimizeTotalTime/level3/[[email protected]]=-250 
     Check your score constraints. 

錯誤會在每次完成無明顯原因的幾個步驟後的時間。

我正在開發一個軟件來安排考慮時間和資源約束的幾項任務。 整個過程由一個有向樹圖表示,使得圖的節點表示任務和邊,以及任務之間的依賴關係。

爲此,規劃師更改每個節點的父節點,直到他找到最佳解決方案。

節點是規劃實體及其父規劃變量:

@PlanningEntity(difficultyComparatorClass = NodeDifficultyComparator.class) 
public class Node extends ProcessChain { 

    private Node parent; // Planning variable: changes during planning, between score calculations. 

    private String delay; // Used to display the delay for nodes of type "And" 

    private int id; // Used as an identifier for each node. Different nodes cannot have the same id 

    public Node(String name, String type, int time, int resources, String md, int id) 
    { 
     super(name, "", time, resources, type, md); 
     this.id = id; 
    } 

    public Node() 
    { 
     super(); 
     this.delay = ""; 
    } 

    public String getDelay() { 
     return delay; 
    } 

    public void setDelay(String delay) { 
     this.delay = delay; 
    } 


    @PlanningVariable(valueRangeProviderRefs = {"parentRange"}, strengthComparatorClass = ParentStrengthComparator.class, nullable = false) 
    public Node getParent() { 
     return parent; 
    } 

    public void setParent(Node parent) { 
     this.parent = parent; 
    } 

    public int getId() { 
     return id; 
    } 

    public void setId(int id) { 
     this.id = id; 
    } 

    /*public String toString() 
    { 
     if(this.type.equals("AND")) 
      return delay; 
     if(!this.md.isEmpty()) 
      return Tools.excerpt(name+" : "+this.md); 

     return Tools.excerpt(name); 
    }*/ 


    public String toString() 
    { 
     if(parent!= null) 
      return Tools.excerpt(name) +"@"+parent; 
     else 
      return Tools.excerpt(name); 
    } 
    public boolean equals(Object o) { 
     if (this == o) { 
      return true; 
     } else if (o instanceof Node) { 
      Node other = (Node) o; 
      return new EqualsBuilder() 
        .append(name, other.name) 
        .append(id, other.id) 
        .isEquals(); 
     } else { 
      return false; 
     } 
    } 

    public int hashCode() { 
     return new HashCodeBuilder() 
       .append(name) 
       .append(id) 
       .toHashCode(); 
    } 


    // ************************************************************************ 
    // Complex methods 
    // ************************************************************************ 

    public int getStartTime() 
    { 
     try{ 
      return Graph.getInstance().getNode2times().get(this).getFirst(); 
      } 
     catch(NullPointerException e) 
     { 
      System.out.println("getStartTime() is null for " + this); 
     } 
     return 10; 
    } 

    public int getEndTime() 
    { try{ 
     return Graph.getInstance().getNode2times().get(this).getSecond(); 
     } 
    catch(NullPointerException e) 
    { 
     System.out.println("getEndTime() is null for " + this); 
    } 
    return 10; 
    } 

    @ValueRangeProvider(id = "parentRange") 
    public Collection<Node> getPossibleParents() 
    { 
     Collection<Node> nodes = new ArrayList<Node>(Graph.getInstance().getNodes()); 

     nodes.remove(this); // We remove this node from the list 

     if(Graph.getInstance().getParentsCount(this) > 0) 
      nodes.remove(Graph.getInstance().getParents(this)); // We remove its parents from the list 

     if(Graph.getInstance().getChildCount(this) > 0) 
      nodes.remove(Graph.getInstance().getChildren(this)); // We remove its children from the list 

     if(!nodes.contains(Graph.getInstance().getNt())) 
      nodes.add(Graph.getInstance().getNt()); 

     return nodes; 
    } 

    /** 
    * The normal methods {@link #equals(Object)} and {@link #hashCode()} cannot be used because the rule engine already 
    * requires them (for performance in their original state). 
    * @see #solutionHashCode() 
    */ 
    public boolean solutionEquals(Object o) { 
     if (this == o) { 
      return true; 
     } else if (o instanceof Node) { 
      Node other = (Node) o; 
      return new EqualsBuilder() 
        .append(name, other.name) 
        .append(id, other.id) 
        .isEquals(); 
     } else { 
      return false; 
     } 
    } 

    /** 
    * The normal methods {@link #equals(Object)} and {@link #hashCode()} cannot be used because the rule engine already 
    * requires them (for performance in their original state). 
    * @see #solutionEquals(Object) 
    */ 
    public int solutionHashCode() { 
     return new HashCodeBuilder() 
       .append(name) 
       .append(id) 
       .toHashCode(); 
    } 


} 

每個移動必須通過除去前邊緣,並從該節點到它的父添加新邊緣更新圖表,所以我中號使用自定義變化的舉動:

public class ParentChangeMove implements Move{ 

    private Node node; 
    private Node parent; 

    private Graph g = Graph.getInstance(); 

    public ParentChangeMove(Node node, Node parent) { 
     this.node = node; 
     this.parent = parent; 
    } 

    public boolean isMoveDoable(ScoreDirector scoreDirector) { 
     List<Dependency> dep = new ArrayList<Dependency>(g.getDependencies()); 
     dep.add(new Dependency(parent.getName(), node.getName())); 

     return !ObjectUtils.equals(node.getParent(), parent) && !g.detectCycles(dep) && !g.getParents(node).contains(parent); 
    } 

    public Move createUndoMove(ScoreDirector scoreDirector) { 
     return new ParentChangeMove(node, node.getParent()); 
    } 


    public void doMove(ScoreDirector scoreDirector) { 

     scoreDirector.beforeVariableChanged(node, "parent"); // before changes are made 

     //The previous edge is removed from the graph 
     if(node.getParent() != null) 
     { 
      Dependency d = new Dependency(node.getParent().getName(), node.getName()); 
      g.removeEdge(g.getDep2link().get(d)); 
      g.getDependencies().remove(d); 
      g.getDep2link().remove(d); 
     } 

     node.setParent(parent); // the move 

     //The new edge is added on the graph (parent ==> node) 
     Link link = new Link(); 
     Dependency d = new Dependency(parent.getName(), node.getName()); 
     g.addEdge(link, parent, node); 
     g.getDependencies().add(d); 
     g.getDep2link().put(d, link); 

     g.setStepTimes(); 

     scoreDirector.afterVariableChanged(node, "parent"); // after changes are made 
    } 


    public Collection<? extends Object> getPlanningEntities() { 
     return Collections.singletonList(node); 
    } 


    public Collection<? extends Object> getPlanningValues() { 
     return Collections.singletonList(parent); 
    } 

    public boolean equals(Object o) { 
      if (this == o) { 
       return true; 
      } else if (o instanceof ParentChangeMove) { 
       ParentChangeMove other = (ParentChangeMove) o; 
       return new EqualsBuilder() 
         .append(node, other.node) 
         .append(parent, other.parent) 
         .isEquals(); 
      } else { 
       return false; 
      } 
     } 

     public int hashCode() { 
      return new HashCodeBuilder() 
        .append(node) 
        .append(parent) 
        .toHashCode(); 
     } 

     public String toString() { 
      return node + " => " + parent; 
     } 

} 

的圖並定義用於由約束來計算分數像下面的每個解決方案的多種方法:

rule "MinimizeTotalTime" // Minimize the total process time 
    when 
     eval(true) 
    then 
     scoreHolder.addSoftConstraintMatch(kcontext, 1, -Graph.getInstance().totalTime()); 
end 

在其他環境模式下,不會出現錯誤,但計算的最高分數不等於實際分數。

我對這個問題來自哪裏並無任何線索。請注意,我已經檢查了所有的equals和hashcode方法。


編輯:繼ge0ffrey的命題,我以前在「MinimizeTotalTime」規定徵收CE檢查,如果錯誤再次出現:

rule "MinimizeTotalTime" // Minimize the total process time 
    when 
     ArrayList() from collect(Node()) 
    then 
     scoreHolder.addSoftConstraintMatch(kcontext, 0, -Graph.getInstance().totalTime()); 
end 

在這一點上,沒有出現錯誤,一切似乎確定。但是當我使用「提前終止」,我得到以下錯誤:

java.util.concurrent.ExecutionException: java.lang.IllegalStateException: Score corruption: the solution's score (-9133) is not the uncorruptedScore (-9765). 

另外,我有一個不使用從該圖類的任何方法,似乎要尊重增量分數計算,但返回另一個規則評分腐敗錯誤。

規則的目的是確保我們不使用更多的資源可供選擇:

rule "addMarks" //insert a Mark each time a task starts or ends 

    when 
     Node($startTime : getStartTime(), $endTime : getEndTime()) 

    then 
     insertLogical(new Mark($startTime)); 
     insertLogical(new Mark($endTime)); 

end 

rule "resourcesLimit" // At any time, The number of resources used must not exceed the total number of resources available 

    when 
     Mark($startTime: time) 
     Mark(time > $startTime, $endTime : time) 
     not Mark(time > $startTime, time < $endTime) 
     $total : Number(intValue > Global.getInstance().getAvailableResources()) from 
      accumulate(Node(getEndTime() >=$endTime, getStartTime()<= $startTime, $res : resources), sum($res)) 
    then 
      scoreHolder.addHardConstraintMatch(kcontext, 0, (Global.getInstance().getAvailableResources() - $total.intValue()) * ($endTime - $startTime));    
end 

以下是錯誤:

java.util.concurrent.ExecutionException: java.lang.IllegalStateException: Score corruption: the workingScore (-193595) is not the uncorruptedScore (-193574) after completedAction ([email protected]_PA_XX_180 => DWL_PA_XX_180): 
    The corrupted scoreDirector has 4 ConstraintMatch(s) which are in excess (and should not be there): 
    com.abcdl.be.solver/resourcesLimit/level0/[43.0, 2012, 1891]=-2783 
    com.abcdl.be.solver/resourcesLimit/level0/[45.0, 1870, 1805]=-1625 
    com.abcdl.be.solver/resourcesLimit/level0/[46.0, 1805, 1774]=-806 
    com.abcdl.be.solver/resourcesLimit/level0/[45.0, 1774, 1762]=-300 
    The corrupted scoreDirector has 3 ConstraintMatch(s) which are missing: 
    com.abcdl.be.solver/resourcesLimit/level0/[43.0, 2012, 1901]=-2553 
    com.abcdl.be.solver/resourcesLimit/level0/[45.0, 1870, 1762]=-2700 
    com.abcdl.be.solver/resourcesLimit/level0/[44.0, 1901, 1891]=-240 
    Check your score constraints. 

回答

2

一個計分規則,有一個LHS只是「eval(true)」的內容已被破壞。要麼這個約束總是被打破,要麼完全相同的權重,而且真的沒有理由去評估它。或者它有時會被打破(或者總是被打破,但是對於不同的權重),然後規則需要相應地進行思考。

問題:Graph.getInstance().totalTime()的返回值隨着計劃變量值的變化而變化。但是Drools只是看着LHS,因爲規劃變量發生了變化,並且它看到LHS中沒有任何變化,所以當規劃變量發生變化時,不需要重新評估該得分規則。注意:這被稱爲增量分數計算(參見文檔),這是一個巨大的性能加速。

子問題:方法Graph.getInstance().totalTime()本質上不是增量式的。

修復:將totalTime()函數轉換爲基於Node選擇的DRL函數。您可能需要使用accumulate。如果這太難了(因爲它是關鍵路徑的複雜計算),請試試它(用於增量計算計算)或嘗試在所有Node s之間執行collect的LHS(這與eval(true)類似,但它會被重新利用

+0

我試着制定一個評分規則,選擇1個節點及其時間作爲一個軟約束,如果有時候2個節點的總時間少於或多於他們的時間總和,我讓LHS檢測那些相應的調整爲了看看這些規則是否運行良好,請使用EasyScoreCalculator並將其置於assertionScoreDirectionFactory(請參閱文檔) –

+0

感謝您的回答,我在編輯部分的命題後添加了更多的細節我的問題。 –