Fehler bei der Berechnung der inkrementellen Punktzahl?

Ich habe seit einigen Tagen ohne ersichtlichen Grund mit einem Score-Korruptionsfehler zu tun. Der Fehler wird nur im FULL_ASSERT-Modus angezeigt und bezieht sich nicht auf die in der Drools-Datei definierten Einschränkungen.

Folgendes ist der Fehler:

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.

Der Fehler tritt jedes Mal auf, nachdem mehrere Schritte ohne ersichtlichen Grund ausgeführt wurden.

Ich entwickle eine Software, um mehrere Aufgaben unter Berücksichtigung von Zeit- und Ressourcenbeschränkungen zu planen. Der gesamte Prozess wird durch ein gerichtetes Baumdiagramm dargestellt, so dass die Knoten des Diagramms die Aufgaben und die Kanten darstellen, die Abhängigkeiten zwischen den Aufgaben.

Dazu ändert der Planer den übergeordneten Knoten jedes Knotens, bis er die beste Lösung findet.

Der Knoten ist die Planungsentität und sein übergeordnetes Element die Planungsvariable:

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


}

Bei jeder Bewegung muss das Diagramm aktualisiert werden, indem die vorherige Kante entfernt und die neue Kante vom Knoten zum übergeordneten Knoten hinzugefügt wird. Daher verwende ich eine benutzerdefinierte Änderungsbewegung:

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

}

Das Diagramm definiert mehrere Methoden, die von den Einschränkungen verwendet werden, um die Bewertung für jede Lösung wie folgt zu berechnen:

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

In anderen Umgebungsmodi wird der Fehler nicht angezeigt, aber die beste berechnete Punktzahl entspricht nicht der tatsächlichen Punktzahl.

Ich habe keine Ahnung, wo das Problem herkommen könnte. Beachten Sie, dass ich bereits alle meine Gleichheits- und Hashcode-Methoden überprüft habe.

BEARBEITEN: Nach dem Vorschlag von ge0ffrey habe ich in der Regel "MinimizeTotalTime" die Funktion "collect CE" verwendet, um zu überprüfen, ob der Fehler erneut auftritt:

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

Zu diesem Zeitpunkt wird kein Fehler angezeigt und alles scheint in Ordnung zu sein. Wenn ich jedoch "Vorzeitig beenden" verwende, wird der folgende Fehler angezeigt:

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

Außerdem habe ich eine Regel, die keine Methode aus der Graph-Klasse verwendet und die inkrementelle Punktzahlberechnung zu respektieren scheint, aber einen weiteren Punktzahlkorruptionsfehler zurückgibt.

Mit der Regel soll sichergestellt werden, dass nicht mehr Ressourcen verwendet werden, als verfügbar sind:

   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

Folgendes ist der Fehler:

    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.

Antworten auf die Frage(1)

Ihre Antwort auf die Frage