All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.chocosolver.solver.learn.ExplanationForSignedClause Maven / Gradle / Ivy

There is a newer version: 4.10.17
Show newest version
/*
 * This file is part of choco-solver, http://choco-solver.org/
 *
 * Copyright (c) 2019, IMT Atlantique. All rights reserved.
 *
 * Licensed under the BSD 4-clause license.
 *
 * See LICENSE file in the project root for full license information.
 */
package org.chocosolver.solver.learn;

import org.chocosolver.solver.ICause;
import org.chocosolver.solver.Model;
import org.chocosolver.solver.constraints.Propagator;
import org.chocosolver.solver.constraints.nary.clauses.ClauseBuilder;
import org.chocosolver.solver.constraints.nary.clauses.ClauseStore;
import org.chocosolver.solver.constraints.nary.clauses.PropSignedClause;
import org.chocosolver.solver.exception.ContradictionException;
import org.chocosolver.solver.exception.SolverException;
import org.chocosolver.solver.search.strategy.assignments.DecisionOperatorFactory;
import org.chocosolver.solver.search.strategy.decision.DecisionPath;
import org.chocosolver.solver.search.strategy.decision.IntDecision;
import org.chocosolver.solver.variables.BoolVar;
import org.chocosolver.solver.variables.IntVar;
import org.chocosolver.solver.variables.events.IntEventType;
import org.chocosolver.util.PoolManager;
import org.chocosolver.util.objects.ValueSortedMap;
import org.chocosolver.util.objects.setDataStructures.iterable.IntIterableRangeSet;
import org.chocosolver.util.objects.setDataStructures.iterable.IntIterableSetUtils;

import java.util.HashMap;

/**
 * An implementation of {@link IExplanation} dedicated to learn signed clauses
 * 

* *

* Project: choco-solver. * * @author Charles Prud'homme * @since 27/01/2017. */ public class ExplanationForSignedClause extends IExplanation { /** * Set to false when skip assertion that * no left branch are backtracked to */ public static boolean ASSERT_NO_LEFT_BRANCH = true; /** * Set to true to force all cause to use default explanations */ public static boolean DEFAULT_X = false; /** * FOR DEBUGGING PURPOSE ONLY. * Set to true to output proofs */ public static boolean PROOF = false; /** * FOR DEBUGGING PURPOSE ONLY. * Set to true to output proofs with details */ public static boolean FINE_PROOF = PROOF; /** * Conflicting nodes */ private ValueSortedMap front; /** * Literals that explains the conflict */ private HashMap literals; /** * The decision to refute (ie, point to jump to wrt the current decision path). * @implSpec 0 represents the ROOT node, * any value greater than the decision path is ignored, * otherwise it represents the decision to refute in the decision path. */ private int assertLevel = 0; /** * The implication graph */ private final Implications mIG; private PoolManager manager; public ExplanationForSignedClause(Implications ig) { front = new ValueSortedMap<>(); literals = new HashMap<>(); manager = new PoolManager<>(); mIG = ig; } /** * @implSpec The */ @Override public void extractConstraint(Model mModel, ClauseStore ngstore) { ClauseBuilder ngb = mModel.getClauseBuilder(); literals.forEach(ngb::put); ngb.buildNogood(mModel); } @Override public void recycle() { front.clear(); literals.forEach((v, r) -> returnSet(r)); literals.clear(); assertLevel = Integer.MAX_VALUE; } public void learnSolution(DecisionPath path) { recycle(); if (path.size() > 1) { // skip solution at ROOT node int i = path.size() - 1; IntDecision dec = (IntDecision) path.getDecision(i); // skip refuted bottom decisions while (i > 1 /*0 is ROOT */ && !dec.hasNext() && dec.getArity() > 1) { dec = (IntDecision) path.getDecision(--i); } // build a 'fake' explanation that is able to refute the right decision for (; i > 0 /*0 is ROOT */ ; i--) { dec = (IntDecision) path.getDecision(i); IntIterableRangeSet dom = null; IntVar var = dec.getDecisionVariable(); literals.get(var); if (dec.getDecOp().equals(DecisionOperatorFactory.makeIntEq())) { if (dec.hasNext() || dec.getArity() == 1) { dom = getRootSet(var); dom.remove(dec.getDecisionValue()); } else { dom = getFreeSet(dec.getDecisionValue()); } } else if (dec.getDecOp().equals(DecisionOperatorFactory.makeIntNeq())) { if (dec.hasNext() || dec.getArity() == 1) { dom = getFreeSet(dec.getDecisionValue()); } else { dom = getRootSet(var); dom.remove(dec.getDecisionValue()); } } else if (dec.getDecOp().equals(DecisionOperatorFactory.makeIntSplit())) { // <= dom = getRootSet(var); if (dec.hasNext() || dec.getArity() == 1) { dom.retainBetween(dec.getDecisionValue() + 1, IntIterableRangeSet.MAX); } else { dom.retainBetween(IntIterableRangeSet.MIN, dec.getDecisionValue()); } } else if (dec.getDecOp().equals(DecisionOperatorFactory.makeIntReverseSplit())) { // >= dom = getRootSet(var); if (dec.hasNext() || dec.getArity() == 1) { dom.retainBetween(IntIterableRangeSet.MIN, dec.getDecisionValue() - 1); } else { dom.retainBetween(dec.getDecisionValue(), IntIterableRangeSet.MAX); } } addLiteral(var, dom, false); } } } /** * From a given conflict, defined by cex and the current implication graph mIG, * this method will compute the signed clause inferred from the conflict. * A call to {@link #extractConstraint(Model, ClauseStore)} will return the computed result. * @param cex the conflict */ public void learnSignedClause(ContradictionException cex) { recycle(); if (PROOF) System.out.print("<-----"); initFront(cex); loop(); if (PROOF) System.out.print(">\n"); } private void initFront(ContradictionException cex) { mIG.collectNodesFromConflict(cex, front); // deal with global conflict if (cex.v == null) { if (Propagator.class.isAssignableFrom(cex.c.getClass())) { if (PROOF) { System.out.printf("\nCstr: %s\n", cex.c); System.out.print("Pivot: none\n"); } explain(cex.c, -1); } else { throw new UnsupportedOperationException(); } } } private void loop() { int current; while (!stop()) { current = front.pollLastValue(); mIG.predecessorsOf(current, front); if (PROOF) { System.out.printf("\nCstr: %s\n", mIG.getCauseAt(current)); System.out.printf("Pivot: %s = %s\n", mIG.getIntVarAt(current).getName(), mIG.getDomainAt(current)); } explain(mIG.getCauseAt(current), current); if (PROOF) { System.out.printf("Expl: %s\n-----", literals); } // filter irrelevant nodes relax(); } } private void explain(ICause cause, int p) { if (p == -1 || DEFAULT_X && Propagator.class.isAssignableFrom(cause.getClass()) && !PropSignedClause.class.isAssignableFrom(cause.getClass()) && !ClauseStore.SignedClause.class.isAssignableFrom(cause.getClass()) ) { Propagator propagator = (Propagator) cause; Propagator.defaultExplain(propagator, this, front, mIG, p); } else { cause.explain(this, front, mIG, p); } // check reification checkReification(cause, p); } private void checkReification(ICause cause, int p) { if (Propagator.class.isAssignableFrom(cause.getClass())) { Propagator propagator = (Propagator) cause; if (propagator.isReified()) { BoolVar b = propagator.reifiedWith(); assert !propagator.isReifiedAndSilent(); mIG.findPredecessor(front, b, p == -1 ? mIG.size() : p); if (b.isInstantiated()) { IntIterableRangeSet set = getFreeSet(); set.add(1 - b.getValue()); if (FINE_PROOF) System.out.print("Reif: "); addLiteral(b, set, false); } else { throw new UnsupportedOperationException("Oh nooo!"); } } } } private void relax() { int l, k = -1; while (!front.isEmpty() && (l = front.getLastValue()) != k) { // remove variable in 'front' but not in literals // achieved lazily by only evaluating the right-most one if (!literals.containsKey(mIG.getIntVarAt(l))) { front.pollLastValue(); } else { int p = mIG.getPredecessorOf(l); // todo improve // go left as long as the right-most variable in 'front' contradicts 'literals' if (p < l /* to avoid going "before" root */ && !IntIterableSetUtils.intersect( literals.get(mIG.getIntVarAt(l)), mIG.getDomainAt(p))) { front.replace(mIG.getIntVarAt(l), p); } } k = l; } } /** * Estimate if conflict analysis can stop: *

    *
  • the rightmost node in conflict is a decision
  • *
  • or it is above the first decision
  • *
* @return true if the conflict analysis can stop */ private boolean stop() { int max; if (front.isEmpty() || IntEventType.VOID.getMask() == mIG.getEventMaskAt(max = front.getLastValue())) { if (PROOF) System.out.print("\nbacktrack to ROOT\n-----"); assertLevel = mIG.getIntVarAt(0) .getModel() .getSolver() .getDecisionPath() .getDecision(0) .getPosition(); } else if (IntDecision.class.isAssignableFrom(mIG.getCauseAt(max).getClass())) { if (PROOF) System.out.printf("\nbacktrack to %s\n-----", mIG.getCauseAt(max)); if (ASSERT_NO_LEFT_BRANCH && !((IntDecision) mIG.getCauseAt(max)).hasNext()) { throw new SolverException("Weak explanation found. Try to backjump to :" + mIG.getCauseAt(max) + "\n" + literals); } assertLevel = ((IntDecision) mIG.getCauseAt(max)).getPosition(); } return assertLevel != Integer.MAX_VALUE; } /** * Add a signed literal (vardom) to this explanation. * This is achieved in three steps: *
    *
  1. signed binary resolution (where 'v' is the pivot variable): *
         *         (v ∈ A ∨ X), (v ∈ B ∨ Y) : (v ∈ (A∩B) ∨ X ∨ Y)
         *     
    *
  2. * simplification: *
         *             (v ∈ ∅ ∨ Z) : (Z)
         *         
    *
  3. *
  4. * join literals: *
         *             ((∀i v ∈ Ai) ∨ Z) : (v ∈ (∪i Ai) ∨ Z)
         *         
    *
  5. * * * *
* @param var signed literal variable * @param dom signed literal domain * @param pivot true if var is the pivot variable */ public void addLiteral(IntVar var, IntIterableRangeSet dom, boolean pivot) { assert literals.values().stream().noneMatch(d -> d.equals(dom)) : "try to add a dom already declare"; /*if(VariableUtils.isConstant(var) && !dom.contains(var.getValue())){ if(FINE_PROOF.getAsBoolean())System.out.printf("%s: %s -- skip\n", var.getName(), dom); returnSet(dom); return; }*/ if (var.isBool()) { dom.retainBetween(0, 1); if (!dom.contains(0) && !dom.contains(1)) { if (FINE_PROOF) System.out.printf("%s: %s -- skip\n", var.getName(), dom); if (pivot) { literals.remove(var); front.remove(var); } returnSet(dom); return; } } addLiteralInternal(var, dom, pivot); } private void addLiteralInternal(IntVar var, IntIterableRangeSet dom, boolean pivot) { IntIterableRangeSet rset = literals.get(var); if (rset == null) { if (dom.size() > 0) { if (FINE_PROOF) System.out.printf("%s: %s\n", var.getName(), dom); literals.put(var, dom); } else { if (FINE_PROOF) System.out.printf("%s: %s -- skip\n", var.getName(), dom); returnSet(dom); } } else { if (pivot) { if (FINE_PROOF) System.out.printf("%s: %s ∩ %s", var.getName(), rset, dom); IntIterableSetUtils.intersectionOf(rset, dom); if (FINE_PROOF) System.out.printf(" = %s", rset); } else { if (FINE_PROOF) System.out.printf("%s: %s ∪ %s", var.getName(), rset, dom); IntIterableSetUtils.unionOf(rset, dom); if (FINE_PROOF) System.out.printf(" = %s", rset); } if (rset.size() == 0) { assert !var.isBool() || rset.contains(0) || !rset.contains(1); if (FINE_PROOF) System.out.print(" -- remove"); literals.remove(var); front.remove(var); returnSet(rset); } if (FINE_PROOF) System.out.print("\n"); returnSet(dom); } } /** * @return the number of literals in this explanation */ public int getCardinality() { return literals.size(); } /** * @return the decision to refute (ie, point to jump to wrt the current decision path). */ public int getAssertingLevel() { return assertLevel; } /** * Return an empty set available (created and returned) or create a new one * @return a free set */ public IntIterableRangeSet getFreeSet() { IntIterableRangeSet set = manager.getE(); if (set == null) { return new IntIterableRangeSet(); } return set; } /** * Return an available set (created and returned) or create a new one * then add 'val' to it. * @return a free set */ public IntIterableRangeSet getFreeSet(int val) { IntIterableRangeSet set = manager.getE(); if (set == null) { set = new IntIterableRangeSet(); } set.add(val); return set; } /** * Return an available set (created and returned) or create a new one * then add range ['a','b'] to it. * @return a free set */ public IntIterableRangeSet getFreeSet(int a, int b) { IntIterableRangeSet set = manager.getE(); if (set == null) { set = new IntIterableRangeSet(); } set.addBetween(a, b); return set; } public void returnSet(IntIterableRangeSet set) { set.clear(); manager.returnE(set); } /** * @param p position * @return a set which contains a copy of the domain of the var at position p */ public IntIterableRangeSet getSet(int p) { IntIterableRangeSet set = getFreeSet(); set.copyFrom(mIG.getDomainAt(p)); return set; } /** * @param var a variable * @return a set which contains a copy of the domain of var at its front position */ public IntIterableRangeSet getSet(IntVar var) { return getSet(front.getValue(var)); } /** * @param var a variable * @return a set which contains a copy of the complement domain of var at its front position * wrt to its root domain */ public IntIterableRangeSet getComplementSet(IntVar var) { IntIterableRangeSet set = getFreeSet(); set.copyFrom(mIG.getRootDomain(var)); set.removeAll(mIG.getDomainAt(front.getValue(var))); return set; } /** * @param var a variable * @return a set which contains a copy of the root domain of var */ public IntIterableRangeSet getRootSet(IntVar var) { IntIterableRangeSet set = getFreeSet(); set.copyFrom(mIG.getRootDomain(var)); return set; } public ValueSortedMap getFront() { return front; } public HashMap getLiterals() { return literals; } @Override public String toString() { StringBuilder st = new StringBuilder(); st.append('{'); for (IntVar v : literals.keySet()) { st.append(v.getName()).append('\u2208').append(literals.get(v)).append(','); } st.append('}'); return st.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy