增量分数计算错误? [英] Incremental score calculation bug?

查看:74
本文介绍了增量分数计算错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

几天来我一直在处理分数损坏错误,没有明显的原因.该错误仅在FULL_ASSERT模式下出现,并且与drools文件中定义的约束无关.

I've been dealing with a score corruption error for few days with no apparent reason. The error appears only on FULL_ASSERT mode and it is not related to the constraints defined on the drools file.

以下是错误:

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 (EMP4@START => 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 (EMP3@EMP4 => EMP4):
      The corrupted scoreDirector has 1 ConstraintMatch(s) which are in excess (and should not be there):
        com.abcdl.be.solver/MinimizeTotalTime/level3/[org.drools.core.reteoo.InitialFactImpl@4dde85f0]=-170
      The corrupted scoreDirector has 1 ConstraintMatch(s) which are missing:
        com.abcdl.be.solver/MinimizeTotalTime/level3/[org.drools.core.reteoo.InitialFactImpl@4dde85f0]=-250
      Check your score constraints.

在没有明显原因的情况下,每次完成几个步骤后,都会出现该错误.

The error appears every time after several steps are completed for no apparent reason.

我正在开发一种软​​件,以在考虑时间和资源限制的情况下安排多个任务. 整个过程由有向树图表示,因此图的节点表示任务以及任务之间的依存关系和边缘.

I'm developing a software to schedule several tasks considering time and resources constraints. The whole process is represented by a directed tree diagram such that the nodes of the graph represent the tasks and the edges, the dependencies between the tasks.

为此,计划者更改每个节点的父节点,直到找到最佳解决方案为止.

To do this, the planner change the parent node of each node until he finds the best solution.

节点是计划实体,其父节点是计划变量:

The node is the planning entity and its parent the planning variable :

    @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();
    }


}

每次移动都必须通过删除前一条边并将新边从节点添加到其父节点来更新图形,所以我使用的是自定义更改移动:

Each move must update the graph by removing the previous edge and adding the new edge from the node to its parent, so i'm using a custom change move :

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;
        }

}

图形确实定义了多种方法,约束使用这些方法来计算每个解决方案的得分,如下所示:

The graph does define multiple methods that are used by the constraints to calculate the score for each solution like the following :

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

在其他环境模式下,不会出现错误,但计算出的最佳分数不等于实际分数.

On other environment modes, the error does not appear but the best score calculated is not equal to the actual score.

我不知道问题可能来自何处.请注意,我已经检查了所有的equals和hashcode方法.

I don't have any clue as to where the problem could come from. Note that i already checked all my equals and hashcode methods.

遵循ge0ffrey的主张,我在"MinimizeTotalTime"规则中使用了收集CE,以检查错误是否再次出现:

EDIT : Following ge0ffrey's proposition, I used collect CE in "MinimizeTotalTime" rule to check if the error comes again :

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

在这一点上,没有错误出现,并且一切正常.但是,当我使用尽早终止"时,出现以下错误:

At this point, no error appears and everything seems ok. But when I use "terminate early", I get the following error :

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

此外,我有一条规则,该规则不使用Graph类中的任何方法,并且似乎尊重增量分数计算,但是会返回另一个分数破坏错误.

Also, I have a rule that doesn't use any method from the Graph class and seems to respect the incremental score calculation but returns another score corruption error.

该规则的目的是确保我们不使用更多可用资源:

The purpose of the rule is to make sure that we don't use more resources that available:

   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 (DWL_CM_XX_101@DWL_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.

推荐答案

LHS仅为"eval(true)"的得分规则固有地被破坏.对于完全相同的权重,该约束总是被打破,并且确实没有理由对其进行评估.或者有时它被打破(或者总是被打破,但是权重不同),然后该规则需要相应地重新触发.

A score rule that has a LHS of just "eval(true)" is inherently broken. Either that constraint is always broken, for the exact same weight, and there really is no reason to evaluate it. Or it is sometimes broken (or always broken but for different weights) and then the rule needs to refire accordingly.

问题:Graph.getInstance().totalTime()的返回值随计划变量的更改而变化.但是Drools只是随着计划变量的变化而查看LHS,并且发现LHS中的任何内容都没有变化,因此,当计划变量发生变化时,无需重新评估该评分规则.注意:这称为增量分数计算(请参阅文档),这是极大的性能提升.

Problem: the return value of Graph.getInstance().totalTime() changes as the planning variables change value. But Drools just looks at the LHS as planning variables change and it sees that nothing in the LHS has changed so there's no need to re-evaluate that score rule, when the planning variables change. Note: this is called incremental score calculation (see docs), which is a huge performance speedup.

子问题:方法Graph.getInstance().totalTime()本质上不是增量的.

Subproblem: The method Graph.getInstance().totalTime() is inherently not incremental.

修复:将基于Node选择的totalTime()函数转换为DRL函数.您可能需要使用accumulate.如果这太难了(因为它是对关键路径的复杂计算),则无论如何都要尝试(为了计算增量分数)或尝试对所有Node执行collect的LHS(就像,但每次都会重新启用.

Fix: translate that totalTime() function into a DRL function based on Node selections. You 'll probably need to use accumulate. If that's too hard (because it's a complex calculation of the critical path or so), try it anyway (for incremental score calculation's sake) or try a LHS that does a collect over all Nodes (which is like eval(true) but it will be refired every time.

这篇关于增量分数计算错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆