/** * Add a copy of the current decision to path * * @param dec a decision of the current decision path */ private void addToPath(Decision dec) { Decision clone = dec.duplicate(); path.add(clone); int pos = path.size() - 1; if (!dec.hasNext()) { refuted.set(pos); } /*boolean forceNext = !dec.hasNext(); if (forceNext) { clone.buildNext(); // force to set up the decision in the very state it was clone.buildNext(); // that's why we call it twice }*/ }
/** Compute the initial fragment, ie set of decisions to keep. */ private void clonePath() { Decision dec = mSolver.getSearchLoop().getLastDecision(); while ((dec != RootDecision.ROOT)) { addToPath(dec); dec = dec.getPrevious(); } Collections.reverse(path); int size = path.size(); for (int i = 0, mid = size >> 1, j = size - 1; i < mid; i++, j--) { boolean bi = refuted.get(i); refuted.set(i, refuted.get(j)); refuted.set(j, bi); } }
/** * Copy the current decision path in _unkopen, for comparison with copen. Then, it compares each * decision, from the top to the bottom, to find the first difference. This is required to avoid * adding the same decision sub-path more than once * * @param searchLoop the search loop * @return the index of the decision, in _unkopen, that stops the loop */ private int compareSubpath(SearchLoop searchLoop) { _unkopen.clear(); Decision decision = searchLoop.decision; while (decision != topDecision) { _unkopen.add(decision); decision = decision.getPrevious(); } Collections.reverse(_unkopen); // int i = 0; int I = Math.min(_unkopen.size(), copen.length); while (i < I && copen[i].isEquivalentTo(_unkopen.get(i))) { i++; } return i; }
@Override public void afterUpBranch() { // we need to catch up that case when the sub tree is closed and this imposes a fragment if (last != null && mSolver.getSearchLoop().getLastDecision().getId() == last.getId()) { mSolver.getSearchLoop().restart(); } }
/** * Extract the open right branches from the current path until it reaches the i^th decision of * _unkopen * * @param searchLoop the search loop * @param i the index of the decision, in _unkopen, that stops the loop */ private void extractOB(SearchLoop searchLoop, int i) { Decision stopAt = _unkopen.get(i).getPrevious(); // then, goes up in the search tree, and detect open nodes searchLoop.mSolver.getEnvironment().worldPop(); Decision decision = searchLoop.decision; int bound; while (decision != stopAt) { bound = isMinimization ? objectiveManager.getObjective().getLB() : objectiveManager.getObjective().getUB(); if (decision.hasNext() && isValid(bound)) { opens.add(new Open(decision, bound, isMinimization)); } searchLoop.decision = searchLoop.decision.getPrevious(); decision.free(); decision = searchLoop.decision; searchLoop.mSolver.getEnvironment().worldPop(); } }
@Override public void fixSomeVariables(ICause cause) throws ContradictionException { // this is called after restart // if required, force the cut and explain the cut if (forceCft) { explainCut(); nbFixedVariables = related2cut.cardinality(); nbCall = 0; increaseLimit(); } // then fix variables // this part is specific: a fake decision path has to be created nbCall++; restrictLess(); notFrozen.clear(); notFrozen.or(related2cut); for (; !notFrozen.isEmpty() && notFrozen.cardinality() > nbFixedVariables; ) { int idx = selectVariable(); notFrozen.clear(idx); } assert mSolver.getSearchLoop().getLastDecision() == RootDecision.ROOT; // add the first refuted decisions int first = notFrozen.nextSetBit(0); for (int i = (first > -1 ? refuted.nextSetBit(first) : first); i > -1; i = refuted.nextSetBit(i + 1)) { notFrozen.clear(i); } // add unrelated notFrozen.or(unrelated); // then build the fake decision path last = null; // LOGGER.debug("relax cut {}", notFrozen.cardinality()); for (int id = notFrozen.nextSetBit(0); id >= 0 && id < path.size(); id = notFrozen.nextSetBit(id + 1)) { // last = ExplanationToolbox.mimic(path.get(id)); // required because some // unrelated decisions can be refuted if (path.get(id).hasNext()) { last = path.get(id).duplicate(); if (refuted.get(id)) last.buildNext(); ExplanationToolbox.imposeDecisionPath(mSolver, last); } } }
/** * Create an open right branch for HBFS * * @param decision an open decision * @param currentBound current lower (resp. upper) bound of the objective value for mimimization * (resp. maximization) * @param minimization set to <tt>true</tt> for minimization */ public Open(Decision<IntVar> decision, int currentBound, boolean minimization) { this.path = new ArrayList<>(); while (decision != topDecision) { Decision d = decision.duplicate(); while (decision.triesLeft() != d.triesLeft() - 1) { d.buildNext(); } path.add(d); decision = decision.getPrevious(); } this.currentBound = currentBound; this.minimization = (byte) (minimization ? 1 : -1); }
/** Force the failure, apply decisions to the last solution + cut => failure! */ private void explainCut() { // Goal: force the failure to get the set of decisions related to the cut forceCft = false; // 1. make a backup mSolver.getEnvironment().worldPush(); Decision d; try { Decision previous = mSolver.getSearchLoop().getLastDecision(); assert previous == RootDecision.ROOT; // 2. apply the decisions mExplanationEngine.getSolver().getObjectiveManager().postDynamicCut(); for (int i = 0; i < path.size(); i++) { d = path.get(i); d.setPrevious(previous); d.buildNext(); if (refuted.get(i)) d.buildNext(); d.apply(); mSolver.propagate(); previous = d; } // mSolver.propagate(); assert false : "SHOULD FAIL!"; } catch (ContradictionException cex) { if ((cex.v != null) || (cex.c != null)) { // contradiction on domain wipe out tmpDeductions.clear(); tmpValueDeductions.clear(); related2cut.clear(); unrelated.clear(); // 3. explain the failure Explanation expl = new Explanation(); if (cex.v != null) { cex.v.explain(mExplanationEngine, VariableState.DOM, expl); } else { cex.c.explain(mExplanationEngine, null, expl); } Explanation complete = mExplanationEngine.flatten(expl); ExplanationToolbox.extractDecision(complete, tmpValueDeductions); tmpDeductions.addAll(tmpValueDeductions); if (tmpDeductions.isEmpty()) { // if (LOGGER.isErrorEnabled()) { // LOGGER.error("2 cases: (a) optimality proven or (b) bug in // explanation"); // } // throw new SolverException("2 cases: (a) optimality proven or (b) bug // in explanation"); isTerminated = true; } for (int i = 0; i < tmpDeductions.size(); i++) { int idx = path.indexOf(((BranchingDecision) tmpDeductions.get(i)).getDecision()); related2cut.set(idx); } // 4. need to replace the duplicated decision with the correct one for (int i = 0; i < path.size(); i++) { Decision dec = path.get(i); boolean forceNext = !dec.hasNext(); dec.rewind(); if (forceNext) dec.buildNext(); dec.setPrevious(null); // useless .. but ... you know } } else { throw new UnsupportedOperationException( this.getClass().getName() + ".onContradiction incoherent state"); } } mSolver.getEnvironment().worldPop(); mSolver.getEngine().flush(); unrelated.andNot(related2cut); unrelated.andNot(refuted); }