/*******************************************************************************
* SAT4J: a SATisfiability library for Java Copyright (C) 2004, 2012 Artois University and CNRS
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU Lesser General Public License Version 2.1 or later (the
* "LGPL"), in which case the provisions of the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of the LGPL, and not to allow others to use your version of
* this file under the terms of the EPL, indicate your decision by deleting
* the provisions above and replace them with the notice and other provisions
* required by the LGPL. If you do not delete the provisions above, a recipient
* may use your version of this file under the terms of the EPL or the LGPL.
*
* Based on the original MiniSat specification from:
*
* An extensible SAT solver. Niklas Een and Niklas Sorensson. Proceedings of the
* Sixth International Conference on Theory and Applications of Satisfiability
* Testing, LNCS 2919, pp 502-518, 2003.
*
* See www.minisat.se for the original solver in C++.
*
* Contributors:
* CRIL - initial API and implementation
*******************************************************************************/
package org.sat4j.minisat.core;
import static org.sat4j.core.LiteralsUtils.neg;
import static org.sat4j.core.LiteralsUtils.toDimacs;
import static org.sat4j.core.LiteralsUtils.toInternal;
import static org.sat4j.core.LiteralsUtils.var;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import org.sat4j.core.ConstrGroup;
import org.sat4j.core.LiteralsUtils;
import org.sat4j.core.Vec;
import org.sat4j.core.VecInt;
import org.sat4j.specs.ContradictionException;
import org.sat4j.specs.IConstr;
import org.sat4j.specs.ILogAble;
import org.sat4j.specs.ISolver;
import org.sat4j.specs.ISolverService;
import org.sat4j.specs.IVec;
import org.sat4j.specs.IVecInt;
import org.sat4j.specs.IteratorInt;
import org.sat4j.specs.Lbool;
import org.sat4j.specs.SearchListener;
import org.sat4j.specs.TimeoutException;
import org.sat4j.specs.UnitClauseProvider;
/**
* The backbone of the library providing the modular implementation of a MiniSAT
* (Chaff) like solver.
*
* @author leberre
*/
public class Solver<D extends DataStructureFactory> implements ISolverService,
ICDCL<D> {
private static final long serialVersionUID = 1L;
private static final double CLAUSE_RESCALE_FACTOR = 1e-20;
private static final double CLAUSE_RESCALE_BOUND = 1 / CLAUSE_RESCALE_FACTOR;
protected ILogAble out;
/**
* Set of original constraints.
*/
protected final IVec<Constr> constrs = new Vec<Constr>();
/**
* Set of learned constraints.
*/
protected final IVec<Constr> learnts = new Vec<Constr>();
/**
* Increment for clause activity.
*/
private double claInc = 1.0;
/**
* decay factor pour l'activit? des clauses.
*/
private double claDecay = 1.0;
/**
* propagation queue
*/
// head of the queue in trail ... (taken from MiniSAT 1.14)
private int qhead = 0;
/**
* variable assignments (literals) in chronological order.
*/
protected final IVecInt trail = new VecInt();
/**
* position of the decision levels on the trail.
*/
protected final IVecInt trailLim = new VecInt();
/**
* position of assumptions before starting the search.
*/
protected int rootLevel;
private int[] model = null;
protected ILits voc;
private IOrder order;
private final ActivityComparator comparator = new ActivityComparator();
private SolverStats stats = new SolverStats();
private LearningStrategy<D> learner;
protected volatile boolean undertimeout;
private long timeout = Integer.MAX_VALUE;
private boolean timeBasedTimeout = true;
protected D dsfactory;
private SearchParams params;
private final IVecInt __dimacs_out = new VecInt();
protected SearchListener slistener = new VoidTracing();
private RestartStrategy restarter;
private final Map<String, Counter> constrTypes = new HashMap<String, Counter>();
private boolean isDBSimplificationAllowed = false;
private final IVecInt learnedLiterals = new VecInt();
private boolean verbose = false;
private boolean keepHot = false;
private String prefix = "c ";
private int declaredMaxVarId = 0;
private UnitClauseProvider unitClauseProvider = UnitClauseProvider.VOID;
protected IVecInt dimacs2internal(IVecInt in) {
this.__dimacs_out.clear();
this.__dimacs_out.ensure(in.size());
int p;
for (int i = 0; i < in.size(); i++) {
p = in.get(i);
if (p == 0) {
throw new IllegalArgumentException(
"0 is not a valid variable identifier");
}
this.__dimacs_out.unsafePush(this.voc.getFromPool(p));
}
return this.__dimacs_out;
}
/*
* @since 2.3.1
*/
public void registerLiteral(int p) {
this.voc.getFromPool(p);
}
/**
* creates a Solver without LearningListener. A learningListener must be
* added to the solver, else it won't backtrack!!! A data structure factory
* must be provided, else it won't work either.
*/
public Solver(LearningStrategy<D> learner, D dsf, IOrder order,
RestartStrategy restarter) {
this(learner, dsf, new SearchParams(), order, restarter);
}
public Solver(LearningStrategy<D> learner, D dsf, SearchParams params,
IOrder order, RestartStrategy restarter) {
this(learner, dsf, params, order, restarter, ILogAble.CONSOLE);
}
public Solver(LearningStrategy<D> learner, D dsf, SearchParams params,
IOrder order, RestartStrategy restarter, ILogAble logger) {
this.order = order;
this.params = params;
this.restarter = restarter;
this.out = logger;
setDataStructureFactory(dsf);
// should be called after dsf has been set up
setLearningStrategy(learner);
}
/*
* (non-Javadoc)
*
* @see org.sat4j.minisat.core.ICDCL#setDataStructureFactory(D)
*/
public final void setDataStructureFactory(D dsf) {
this.dsfactory = dsf;
this.dsfactory.setUnitPropagationListener(this);
this.dsfactory.setLearner(this);
this.voc = dsf.getVocabulary();
this.order.setLits(this.voc);
}
/**
* @since 2.2
*/
public boolean isVerbose() {
return this.verbose;
}
/**
* @param value
* @since 2.2
*/
public void setVerbose(boolean value) {
this.verbose = value;
}
/*
* (non-Javadoc)
*
* @see
* org.sat4j.minisat.core.ICDCL#setSearchListener(org.sat4j.specs.SearchListener
* )
*/
public <S extends ISolverService> void setSearchListener(
SearchListener<S> sl) {
this.slistener = sl;
}
/*
* (non-Javadoc)
*
* @see org.sat4j.minisat.core.ICDCL#getSearchListener()
*/
public <S extends ISolverService> SearchListener<S> getSearchListener() {
return this.slistener;
}
/*
* (non-Javadoc)
*
* @see org.sat4j.minisat.core.ICDCL#setLearner(org.sat4j.minisat.core.
* LearningStrategy)
*/
public void setLearner(LearningStrategy<D> strategy) {
setLearningStrategy(strategy);
}
/*
* (non-Javadoc)
*
* @see
* org.sat4j.minisat.core.ICDCL#setLearningStrategy(org.sat4j.minisat.core.
* LearningStrategy)
*/
public void setLearningStrategy(LearningStrategy<D> strategy) {
if (this.learner != null) {
this.learner.setSolver(null);
}
this.learner = strategy;
strategy.setSolver(this);
}
public void setTimeout(int t) {
this.timeout = t * 1000L;
this.timeBasedTimeout = true;
}
public void setTimeoutMs(long t) {
this.timeout = t;
this.timeBasedTimeout = true;
}
public void setTimeoutOnConflicts(int count) {
this.timeout = count;
this.timeBasedTimeout = false;
}
/*
* (non-Javadoc)
*
* @see org.sat4j.minisat.core.ICDCL#setSearchParams(org.sat4j.minisat.core.
* SearchParams)
*/
public void setSearchParams(SearchParams sp) {
this.params = sp;
}
public SearchParams getSearchParams() {
return this.params;
}
/*
* (non-Javadoc)
*
* @see
* org.sat4j.minisat.core.ICDCL#setRestartStrategy(org.sat4j.minisat.core
* .RestartStrategy)
*/
public void setRestartStrategy(RestartStrategy restarter) {
this.restarter = restarter;
}
/*
* (non-Javadoc)
*
* @see org.sat4j.minisat.core.ICDCL#getRestartStrategy()
*/
public RestartStrategy getRestartStrategy() {
return this.restarter;
}
public void expireTimeout() {
this.undertimeout = false;
if (this.timeBasedTimeout) {
if (this.timer != null) {
this.timer.cancel();
this.timer = null;
}
} else {
if (this.conflictCount != null) {
this.conflictCount = null;
}
}
}
protected int nAssigns() {
return this.trail.size();
}
public int nConstraints() {
return this.constrs.size();
}
public void learn(Constr c) {
this.slistener.learn(c);
this.learnts.push(c);
c.setLearnt();
c.register();
this.stats.learnedclauses++;
switch (c.size()) {
case 2:
this.stats.learnedbinaryclauses++;
break;
case 3:
this.stats.learnedternaryclauses++;
break;
default:
// do nothing
}
}
public final int decisionLevel() {
return this.trailLim.size();
}
@Deprecated
public int newVar() {
int index = this.voc.nVars() + 1;
this.voc.ensurePool(index);
return index;
}
public int newVar(int howmany) {
this.voc.ensurePool(howmany);
this.declaredMaxVarId = howmany;
return howmany;
}
public IConstr addClause(IVecInt literals) throws ContradictionException {
IVecInt vlits = dimacs2internal(literals);
return addConstr(this.dsfactory.createClause(vlits));
}
public boolean removeConstr(IConstr co) {
if (co == null) {
throw new IllegalArgumentException(
"Reference to the constraint to remove needed!"); //$NON-NLS-1$
}
Constr c = (Constr) co;
c.remove(this);
this.constrs.remove(c);
clearLearntClauses();
String type = c.getClass().getName();
this.constrTypes.get(type).dec();
return true;
}
/**
* @since 2.1
*/
public boolean removeSubsumedConstr(IConstr co) {
if (co == null) {
throw new IllegalArgumentException(
"Reference to the constraint to remove needed!"); //$NON-NLS-1$
}
if (this.constrs.last() != co) {
throw new IllegalArgumentException(
"Can only remove latest added constraint!!!"); //$NON-NLS-1$
}
Constr c = (Constr) co;
c.remove(this);
this.constrs.pop();
String type = c.getClass().getName();
this.constrTypes.get(type).dec();
return true;
}
public void addAllClauses(IVec<IVecInt> clauses)
throws ContradictionException {
for (Iterator<IVecInt> iterator = clauses.iterator(); iterator
.hasNext();) {
addClause(iterator.next());
}
}
public IConstr addAtMost(IVecInt literals, int degree)
throws ContradictionException {
int n = literals.size();
IVecInt opliterals = new VecInt(n);
for (IteratorInt iterator = literals.iterator(); iterator.hasNext();) {
opliterals.push(-iterator.next());
}
return addAtLeast(opliterals, n - degree);
}
public IConstr addAtLeast(IVecInt literals, int degree)
throws ContradictionException {
IVecInt vlits = dimacs2internal(literals);
return addConstr(this.dsfactory.createCardinalityConstraint(vlits,
degree));
}
public IConstr addExactly(IVecInt literals, int n)
throws ContradictionException {
ConstrGroup group = new ConstrGroup(false);
group.add(addAtMost(literals, n));
group.add(addAtLeast(literals, n));
return group;
}
@SuppressWarnings("unchecked")
public boolean simplifyDB() {
// Simplifie la base de clauses apres la premiere propagation des
// clauses unitaires
IVec<Constr>[] cs = new IVec[] { this.constrs, this.learnts };
for (int type = 0; type < 2; type++) {
int j = 0;
for (int i = 0; i < cs[type].size(); i++) {
if (cs[type].get(i).simplify()) {
// enleve les contraintes satisfaites de la base
cs[type].get(i).remove(this);
} else {
cs[type].moveTo(j++, i);
}
}
cs[type].shrinkTo(j);
}
return true;
}
/**
* Si un mod?le est trouv?, ce vecteur contient le mod?le.
*
* @return un mod?le de la formule.
*/
public int[] model() {
if (this.model == null) {
throw new UnsupportedOperationException(
"Call the solve method first!!!"); //$NON-NLS-1$
}
int[] nmodel = new int[this.model.length];
System.arraycopy(this.model, 0, nmodel, 0, this.model.length);
return nmodel;
}
/*
* (non-Javadoc)
*
* @see org.sat4j.minisat.core.ICDCL#enqueue(int)
*/
public boolean enqueue(int p) {
return enqueue(p, null);
}
/*
* (non-Javadoc)
*
* @see org.sat4j.minisat.core.ICDCL#enqueue(int,
* org.sat4j.minisat.core.Constr)
*/
public boolean enqueue(int p, Constr from) {
assert p > 1;
if (this.voc.isSatisfied(p)) {
// literal is already satisfied. Skipping.
return true;
}
if (this.voc.isFalsified(p)) {
// conflicting enqueued assignment
return false;
}
// new fact, store it
this.voc.satisfies(p);
this.voc.setLevel(p, decisionLevel());
this.voc.setReason(p, from);
this.trail.push(p);
if (from != null && from.learnt()) {
this.learnedConstraintsDeletionStrategy.onPropagation(from);
}
return true;
}
private boolean[] mseen = new boolean[0];
private final IVecInt mpreason = new VecInt();
private final IVecInt moutLearnt = new VecInt();
/**
* @throws TimeoutException
* if the timeout is reached during conflict analysis.
*/
public void analyze(Constr confl, Pair results) throws TimeoutException {
assert confl != null;
final boolean[] seen = this.mseen;
final IVecInt outLearnt = this.moutLearnt;
final IVecInt preason = this.mpreason;
outLearnt.clear();
assert outLearnt.size() == 0;
for (int i = 0; i < seen.length; i++) {
seen[i] = false;
}
int counter = 0;
int p = ILits.UNDEFINED;
outLearnt.push(ILits.UNDEFINED);
// reserve de la place pour le litteral falsifie
int outBtlevel = 0;
IConstr prevConfl = null;
do {
preason.clear();
assert confl != null;
if (prevConfl != confl) {
confl.calcReason(p, preason);
this.learnedConstraintsDeletionStrategy
.onConflictAnalysis(confl);
// Trace reason for p
for (int j = 0; j < preason.size(); j++) {
int q = preason.get(j);
this.order.updateVar(q);
if (!seen[q >> 1]) {
seen[q >> 1] = true;
if (this.voc.getLevel(q) == decisionLevel()) {
counter++;
this.order.updateVarAtDecisionLevel(q);
} else if (this.voc.getLevel(q) > 0) {
// only literals assigned after decision level 0
// part of
// the explanation
outLearnt.push(q ^ 1);
outBtlevel = Math.max(outBtlevel,
this.voc.getLevel(q));
}
}
}
}
prevConfl = confl;
// select next reason to look at
do {
p = this.trail.last();
confl = this.voc.getReason(p);
undoOne();
} while (!seen[p >> 1]);
// seen[p.var] indique que p se trouve dans outLearnt ou dans
// le dernier niveau de d?cision
} while (--counter > 0);
outLearnt.set(0, p ^ 1);
this.simplifier.simplify(outLearnt);
Constr c = this.dsfactory.createUnregisteredClause(outLearnt);
// slistener.learn(c);
this.learnedConstraintsDeletionStrategy.onClauseLearning(c);
results.reason = c;
assert outBtlevel > -1;
results.backtrackLevel = outBtlevel;
}
/**
* Derive a subset of the assumptions causing the inconistency.
*
* @param confl
* the last conflict of the search, occuring at root level.
* @param assumps
* the set of assumption literals
* @param conflictingLiteral
* the literal detected conflicting while propagating
* assumptions.
* @return a subset of assumps causing the inconsistency.
* @since 2.2
*/
public IVecInt analyzeFinalConflictInTermsOfAssumptions(Constr confl,
IVecInt assumps, int conflictingLiteral) {
if (assumps.size() == 0) {
return null;
}
while (!this.trailLim.isEmpty()
&& this.trailLim.last() == this.trail.size()) {
// conflict detected when assuming a value
this.trailLim.pop();
}
final boolean[] seen = this.mseen;
final IVecInt outLearnt = this.moutLearnt;
final IVecInt preason = this.mpreason;
outLearnt.clear();
if (this.trailLim.size() == 0) {
// conflict detected on unit clauses
return outLearnt;
}
assert outLearnt.size() == 0;
for (int i = 0; i < seen.length; i++) {
seen[i] = false;
}
if (confl == null) {
seen[conflictingLiteral >> 1] = true;
}
int p = ILits.UNDEFINED;
while (confl == null && this.trail.size() > 0
&& this.trailLim.size() > 0) {
p = this.trail.last();
confl = this.voc.getReason(p);
undoOne();
if (confl == null && p == (conflictingLiteral ^ 1)) {
outLearnt.push(toDimacs(p));
}
if (this.trail.size() <= this.trailLim.last()) {
this.trailLim.pop();
}
}
if (confl == null) {
return outLearnt;
}
do {
preason.clear();
confl.calcReason(p, preason);
// Trace reason for p
for (int j = 0; j < preason.size(); j++) {
int q = preason.get(j);
if (!seen[q >> 1]) {
seen[q >> 1] = true;
if (this.voc.getReason(q) == null
&& this.voc.getLevel(q) > 0) {
assert assumps.contains(toDimacs(q));
outLearnt.push(toDimacs(q));
}
}
}
// select next reason to look at
do {
p = this.trail.last();
confl = this.voc.getReason(p);
undoOne();
if (decisionLevel() > 0
&& this.trail.size() <= this.trailLim.last()) {
this.trailLim.pop();
}
} while (this.trail.size() > 0 && decisionLevel() > 0
&& (!seen[p >> 1] || confl == null));
} while (decisionLevel() > 0);
return outLearnt;
}
public static final ISimplifier NO_SIMPLIFICATION = new ISimplifier() {
/**
*
*/
private static final long serialVersionUID = 1L;
public void simplify(IVecInt outLearnt) {
}
@Override
public String toString() {
return "No reason simplification"; //$NON-NLS-1$
}
};
public final ISimplifier SIMPLE_SIMPLIFICATION = new ISimplifier() {
/**
*
*/
private static final long serialVersionUID = 1L;
public void simplify(IVecInt conflictToReduce) {
simpleSimplification(conflictToReduce);
}
@Override
public String toString() {
return "Simple reason simplification"; //$NON-NLS-1$
}
};
public final ISimplifier EXPENSIVE_SIMPLIFICATION = new ISimplifier() {
/**
*
*/
private static final long serialVersionUID = 1L;
public void simplify(IVecInt conflictToReduce) {
expensiveSimplification(conflictToReduce);
}
@Override
public String toString() {
return "Expensive reason simplification"; //$NON-NLS-1$
}
};
public final ISimplifier EXPENSIVE_SIMPLIFICATION_WLONLY = new ISimplifier() {
/**
*
*/
private static final long serialVersionUID = 1L;
public void simplify(IVecInt conflictToReduce) {
expensiveSimplificationWLOnly(conflictToReduce);
}
@Override
public String toString() {
return "Expensive reason simplification specific for WL data structure"; //$NON-NLS-1$
}
};
private ISimplifier simplifier = NO_SIMPLIFICATION;
/*
* (non-Javadoc)
*
* @see org.sat4j.minisat.core.ICDCL#setSimplifier(java.lang.String)
*/
public void setSimplifier(SimplificationType simp) {
Field f;
try {
f = Solver.class.getDeclaredField(simp.toString());
this.simplifier = (ISimplifier) f.get(this);
} catch (Exception e) {
e.printStackTrace();
this.simplifier = NO_SIMPLIFICATION;
}
}
/*
* (non-Javadoc)
*
* @see
* org.sat4j.minisat.core.ICDCL#setSimplifier(org.sat4j.minisat.core.Solver
* .ISimplifier)
*/
public void setSimplifier(ISimplifier simp) {
this.simplifier = simp;
}
/*
* (non-Javadoc)
*
* @see org.sat4j.minisat.core.ICDCL#getSimplifier()
*/
public ISimplifier getSimplifier() {
return this.simplifier;
}
// MiniSat -- Copyright (c) 2003-2005, Niklas Een, Niklas Sorensson
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Taken from MiniSAT 1.14: Simplify conflict clause (a little):
private void simpleSimplification(IVecInt conflictToReduce) {
int i, j, p;
final boolean[] seen = this.mseen;
IConstr r;
for (i = j = 1; i < conflictToReduce.size(); i++) {
r = this.voc.getReason(conflictToReduce.get(i));
if (r == null || r.canBePropagatedMultipleTimes()) {
conflictToReduce.moveTo(j++, i);
} else {
for (int k = 0; k < r.size(); k++) {
p = r.get(k);
if (!seen[p >> 1] && this.voc.isFalsified(p)
&& this.voc.getLevel(p) != 0) {
conflictToReduce.moveTo(j++, i);
break;
}
}
}
}
conflictToReduce.shrink(i - j);
this.stats.reducedliterals += i - j;
}
private final IVecInt analyzetoclear = new VecInt();
private final IVecInt analyzestack = new VecInt();
// Taken from MiniSAT 1.14
private void expensiveSimplification(IVecInt conflictToReduce) {
// Simplify conflict clause (a lot):
//
int i, j;
// (maintain an abstraction of levels involved in conflict)
this.analyzetoclear.clear();
conflictToReduce.copyTo(this.analyzetoclear);
for (i = 1, j = 1; i < conflictToReduce.size(); i++) {
if (this.voc.getReason(conflictToReduce.get(i)) == null
|| !analyzeRemovable(conflictToReduce.get(i))) {
conflictToReduce.moveTo(j++, i);
}
}
conflictToReduce.shrink(i - j);
this.stats.reducedliterals += i - j;
}
// Check if 'p' can be removed.' min_level' is used to abort early if
// visiting literals at a level that cannot be removed.
//
private boolean analyzeRemovable(int p) {
assert this.voc.getReason(p) != null;
ILits lvoc = this.voc;
IVecInt lanalyzestack = this.analyzestack;
IVecInt lanalyzetoclear = this.analyzetoclear;
lanalyzestack.clear();
lanalyzestack.push(p);
final boolean[] seen = this.mseen;
int top = lanalyzetoclear.size();
while (lanalyzestack.size() > 0) {
int q = lanalyzestack.last();
assert lvoc.getReason(q) != null;
Constr c = lvoc.getReason(q);
lanalyzestack.pop();
if (c.canBePropagatedMultipleTimes()) {
for (int j = top; j < lanalyzetoclear.size(); j++) {
seen[lanalyzetoclear.get(j) >> 1] = false;
}
lanalyzetoclear.shrink(lanalyzetoclear.size() - top);
return false;
}
for (int i = 0; i < c.size(); i++) {
int l = c.get(i);
if (!seen[var(l)] && lvoc.isFalsified(l)
&& lvoc.getLevel(l) != 0) {
if (lvoc.getReason(l) == null) {
for (int j = top; j < lanalyzetoclear.size(); j++) {
seen[lanalyzetoclear.get(j) >> 1] = false;
}
lanalyzetoclear.shrink(lanalyzetoclear.size() - top);
return false;
}
seen[l >> 1] = true;
lanalyzestack.push(l);
lanalyzetoclear.push(l);
}
}
}
return true;
}
// Taken from MiniSAT 1.14
private void expensiveSimplificationWLOnly(IVecInt conflictToReduce) {
// Simplify conflict clause (a lot):
//
int i, j;
// (maintain an abstraction of levels involved in conflict)
this.analyzetoclear.clear();
conflictToReduce.copyTo(this.analyzetoclear);
for (i = 1, j = 1; i < conflictToReduce.size(); i++) {
if (this.voc.getReason(conflictToReduce.get(i)) == null
|| !analyzeRemovableWLOnly(conflictToReduce.get(i))) {
conflictToReduce.moveTo(j++, i);
}
}
conflictToReduce.shrink(i - j);
this.stats.reducedliterals += i - j;
}
// Check if 'p' can be removed.' min_level' is used to abort early if
// visiting literals at a level that cannot be removed.
//
private boolean analyzeRemovableWLOnly(int p) {
assert this.voc.getReason(p) != null;
this.analyzestack.clear();
this.analyzestack.push(p);
final boolean[] seen = this.mseen;
int top = this.analyzetoclear.size();
while (this.analyzestack.size() > 0) {
int q = this.analyzestack.last();
assert this.voc.getReason(q) != null;
Constr c = this.voc.getReason(q);
this.analyzestack.pop();
for (int i = 1; i < c.size(); i++) {
int l = c.get(i);
if (!seen[var(l)] && this.voc.getLevel(l) != 0) {
if (this.voc.getReason(l) == null) {
for (int j = top; j < this.analyzetoclear.size(); j++) {
seen[this.analyzetoclear.get(j) >> 1] = false;
}
this.analyzetoclear.shrink(this.analyzetoclear.size()
- top);
return false;
}
seen[l >> 1] = true;
this.analyzestack.push(l);
this.analyzetoclear.push(l);
}
}
}
return true;
}
// END Minisat 1.14 cut and paste
/**
*
*/
protected void undoOne() {
// gather last assigned literal
int p = this.trail.last();
assert p > 1;
assert this.voc.getLevel(p) >= 0;
int x = p >> 1;
// unassign variable
this.voc.unassign(p);
this.voc.setReason(p, null);
this.voc.setLevel(p, -1);
// update heuristics value
this.order.undo(x);
// remove literal from the trail
this.trail.pop();
// update constraints on backtrack.
// not used if the solver uses watched literals.
IVec<Undoable> undos = this.voc.undos(p);
assert undos != null;
for (int size = undos.size(); size > 0; size--) {
undos.last().undo(p);
undos.pop();
}
}
/**
* Propagate activity to a constraint
*
* @param confl
* a constraint
*/
public void claBumpActivity(Constr confl) {
confl.incActivity(this.claInc);
if (confl.getActivity() > CLAUSE_RESCALE_BOUND) {
claRescalActivity();
// for (int i = 0; i < confl.size(); i++) {
// varBumpActivity(confl.get(i));
// }
}
}
public void varBumpActivity(int p) {
this.order.updateVar(p);
}
private void claRescalActivity() {
for (int i = 0; i < this.learnts.size(); i++) {
this.learnts.get(i).rescaleBy(CLAUSE_RESCALE_FACTOR);
}
this.claInc *= CLAUSE_RESCALE_FACTOR;
}
private final IVec<Propagatable> watched = new Vec<Propagatable>();
/**
* @return null if not conflict is found, else a conflicting constraint.
*/
public final Constr propagate() {
IVecInt ltrail = this.trail;
SolverStats lstats = this.stats;
IOrder lorder = this.order;
SearchListener lslistener = this.slistener;
// ltrail.size() changes due to propagation
// cannot cache that value.
while (this.qhead < ltrail.size()) {
lstats.propagations++;
int p = ltrail.get(this.qhead++);
lslistener.propagating(toDimacs(p), null);
lorder.assignLiteral(p);
Constr confl = reduceClausesForFalsifiedLiteral(p);
if (confl != null) {
return confl;
}
}
return null;
}
private Constr reduceClausesForFalsifiedLiteral(int p) {
// p is the literal to propagate
// Moved original MiniSAT code to dsfactory to avoid
// watches manipulation in counter Based clauses for instance.
assert p > 1;
IVec<Propagatable> lwatched = this.watched;
lwatched.clear();
this.voc.watches(p).moveTo(lwatched);
final int size = lwatched.size();
for (int i = 0; i < size; i++) {
this.stats.inspects++;
// try shortcut
// shortcut = shortcuts.get(i);
// if (shortcut != ILits.UNDEFINED && voc.isSatisfied(shortcut))
// {
// voc.watch(p, watched.get(i), shortcut);
// stats.shortcuts++;
// continue;
// }
if (!lwatched.get(i).propagate(this, p)) {
// Constraint is conflicting: copy remaining watches to
// watches[p]
// and return constraint
final int sizew = lwatched.size();
for (int j = i + 1; j < sizew; j++) {
this.voc.watch(p, lwatched.get(j));
}
this.qhead = this.trail.size(); // propQ.clear();
return lwatched.get(i).toConstraint();
}
}
return null;
}
void record(Constr constr) {
constr.assertConstraint(this);
int p = toDimacs(constr.get(0));
this.slistener.adding(p);
if (constr.size() == 1) {
this.stats.learnedliterals++;
this.slistener.learnUnit(p);
} else {
this.learner.learns(constr);
}
}
/**
* @return false ssi conflit imm?diat.
*/
public boolean assume(int p) {
// Precondition: assume propagation queue is empty
assert this.trail.size() == this.qhead;
assert !this.trailLim.contains(this.trail.size());
this.trailLim.push(this.trail.size());
return enqueue(p);
}
/**
* Revert to the state before the last assume()
*/
private void cancel() {
// assert trail.size() == qhead || !undertimeout;
int decisionvar = this.trail.unsafeGet(this.trailLim.last());
this.slistener.backtracking(toDimacs(decisionvar));
for (int c = this.trail.size() - this.trailLim.last(); c > 0; c--) {
undoOne();
}
this.trailLim.pop();
this.qhead = this.trail.size();
}
/**
* Restore literals
*/
private void cancelLearntLiterals(int learnedLiteralsLimit) {
this.learnedLiterals.clear();
// assert trail.size() == qhead || !undertimeout;
while (this.trail.size() > learnedLiteralsLimit) {
this.learnedLiterals.push(this.trail.last());
undoOne();
}
// qhead = 0;
// learnedLiterals = 0;
}
/**
* Cancel several levels of assumptions
*
* @param level
*/
protected void cancelUntil(int level) {
while (decisionLevel() > level) {
cancel();
}
}
private final Pair analysisResult = new Pair();
private boolean[] userbooleanmodel;
private IVecInt unsatExplanationInTermsOfAssumptions;
Lbool search(IVecInt assumps) {
assert this.rootLevel == decisionLevel();
this.stats.starts++;
int backjumpLevel;
// varDecay = 1 / params.varDecay;
this.order.setVarDecay(1 / this.params.getVarDecay());
this.claDecay = 1 / this.params.getClaDecay();
do {
this.slistener.beginLoop();
// propagate unit clauses and other constraints
Constr confl = propagate();
assert this.trail.size() == this.qhead;
if (confl == null) {
// No conflict found
if (decisionLevel() == 0 && this.isDBSimplificationAllowed) {
this.stats.rootSimplifications++;
boolean ret = simplifyDB();
assert ret;
}
assert nAssigns() <= this.voc.realnVars();
if (nAssigns() == this.voc.realnVars()) {
modelFound();
this.slistener.solutionFound(
(this.fullmodel != null) ? this.fullmodel
: this.model, this);
if (this.sharedConflict == null) {
cancelUntil(this.rootLevel);
return Lbool.TRUE;
} else {
confl = this.sharedConflict;
}
} else {
if (this.restarter.shouldRestart()) {
cancelUntil(this.rootLevel);
return Lbool.UNDEFINED;
}
if (this.needToReduceDB) {
reduceDB();
this.needToReduceDB = false;
}
if (this.sharedConflict == null) {
// New variable decision
this.stats.decisions++;
int p = this.order.select();
if (p == ILits.UNDEFINED) {
confl = preventTheSameDecisionsToBeMade();
this.lastConflictMeansUnsat = false;
} else {
assert p > 1;
this.slistener.assuming(toDimacs(p));
boolean ret = assume(p);
assert ret;
}
} else {
confl = this.sharedConflict;
}
}
}
if (confl != null) {
// conflict found
this.stats.conflicts++;
this.slistener.conflictFound(confl, decisionLevel(),
this.trail.size());
this.conflictCount.newConflict();
if (decisionLevel() == this.rootLevel) {
if (this.lastConflictMeansUnsat) {
// conflict at root level, the formula is inconsistent
this.unsatExplanationInTermsOfAssumptions = analyzeFinalConflictInTermsOfAssumptions(
confl, assumps, ILits.UNDEFINED);
return Lbool.FALSE;
}
return Lbool.UNDEFINED;
}
int conflictTrailLevel = this.trail.size();
// analyze conflict
try {
analyze(confl, this.analysisResult);
} catch (TimeoutException e) {
return Lbool.UNDEFINED;
}
assert this.analysisResult.backtrackLevel < decisionLevel();
backjumpLevel = Math.max(this.analysisResult.backtrackLevel,
this.rootLevel);
this.slistener.backjump(backjumpLevel);
cancelUntil(backjumpLevel);
if (backjumpLevel == this.rootLevel) {
this.restarter.onBackjumpToRootLevel();
}
if (confl == this.sharedConflict) {
this.sharedConflict.assertConstraintIfNeeded(this);
this.sharedConflict = null;
}
assert decisionLevel() >= this.rootLevel
&& decisionLevel() >= this.analysisResult.backtrackLevel;
if (this.analysisResult.reason == null) {
return Lbool.FALSE;
}
record(this.analysisResult.reason);
this.restarter.newLearnedClause(this.analysisResult.reason,
conflictTrailLevel);
this.analysisResult.reason = null;
decayActivities();
}
} while (this.undertimeout);
return Lbool.UNDEFINED; // timeout occured
}
private Constr preventTheSameDecisionsToBeMade() {
IVecInt clause = new VecInt(nVars());
int p;
for (int i = this.trail.size() - 1; i >= this.rootLevel; i--) {
p = this.trail.get(i);
if (this.voc.getReason(p) == null) {
clause.push(p ^ 1);
}
}
return this.dsfactory.createUnregisteredClause(clause);
}
protected void analyzeAtRootLevel(Constr conflict) {
}
private final IVecInt implied = new VecInt();
private final IVecInt decisions = new VecInt();
private int[] fullmodel;
/**
*
*/
void modelFound() {
IVecInt tempmodel = new VecInt(nVars());
this.userbooleanmodel = new boolean[realNumberOfVariables()];
this.fullmodel = null;
for (int i = 1; i <= nVars(); i++) {
if (this.voc.belongsToPool(i)) {
int p = this.voc.getFromPool(i);
if (!this.voc.isUnassigned(p)) {
tempmodel.push(this.voc.isSatisfied(p) ? i : -i);
this.userbooleanmodel[i - 1] = this.voc.isSatisfied(p);
if (this.voc.getReason(p) == null && voc.getLevel(p) > 0) {
this.decisions.push(tempmodel.last());
} else {
this.implied.push(tempmodel.last());
}
}
}
}
this.model = new int[tempmodel.size()];
tempmodel.copyTo(this.model);
if (realNumberOfVariables() > nVars()) {
for (int i = nVars() + 1; i <= realNumberOfVariables(); i++) {
if (this.voc.belongsToPool(i)) {
int p = this.voc.getFromPool(i);
if (!this.voc.isUnassigned(p)) {
tempmodel.push(this.voc.isSatisfied(p) ? i : -i);
this.userbooleanmodel[i - 1] = this.voc.isSatisfied(p);
if (this.voc.getReason(p) == null) {
this.decisions.push(tempmodel.last());
} else {
this.implied.push(tempmodel.last());
}
}
}
}
this.fullmodel = new int[tempmodel.size()];
tempmodel.moveTo(this.fullmodel);
} else {
this.fullmodel = this.model;
}
}
/**
* Forget a variable in the formula by falsifying both its positive and
* negative literals.
*
* @param var
* a variable
* @return a conflicting constraint resulting from the disparition of those
* literals.
*/
private Constr forget(int var) {
boolean satisfied = this.voc.isSatisfied(toInternal(var));
this.voc.forgets(var);
Constr confl;
if (satisfied) {
confl = reduceClausesForFalsifiedLiteral(LiteralsUtils
.toInternal(-var));
} else {
confl = reduceClausesForFalsifiedLiteral(LiteralsUtils
.toInternal(var));
}
return confl;
}
/**
* Assume literal p and perform unit propagation
*
* @param p
* a literal
* @return true if no conflict is reached, false if a conflict is found.
*/
private boolean setAndPropagate(int p) {
if (voc.isUnassigned(p)) {
assert !trail.contains(p);
assert !trail.contains(neg(p));
return assume(p) && propagate() == null;
}
return voc.isSatisfied(p);
}
private int[] prime;
public int[] primeImplicant() {
assert this.qhead == this.trail.size() + this.learnedLiterals.size();
if (this.learnedLiterals.size() > 0) {
this.qhead = trail.size();
}
if (isVerbose()) {
System.out.printf("%s implied: %d, decision: %d %n",
getLogPrefix(), implied.size(), decisions.size());
}
this.prime = new int[realNumberOfVariables() + 1];
int p, d;
for (int i = 0; i < this.prime.length; i++) {
this.prime[i] = 0;
}
boolean noproblem;
for (IteratorInt it = this.implied.iterator(); it.hasNext();) {
d = it.next();
p = toInternal(d);
this.prime[Math.abs(d)] = d;
noproblem = setAndPropagate(p);
assert noproblem;
}
boolean canBeRemoved;
int rightlevel;
int removed = 0;
int propagated = 0;
int tested = 0;
int l2propagation = 0;
for (int i = 0; i < this.decisions.size(); i++) {
d = this.decisions.get(i);
assert !this.voc.isFalsified(toInternal(d));
if (this.voc.isSatisfied(toInternal(d))) {
// d has been propagated
this.prime[Math.abs(d)] = d;
propagated++;
} else if (setAndPropagate(toInternal(-d))) {
canBeRemoved = true;
tested++;
rightlevel = currentDecisionLevel();
for (int j = i + 1; j < this.decisions.size(); j++) {
l2propagation++;
if (!setAndPropagate(toInternal(this.decisions.get(j)))) {
canBeRemoved = false;
break;
}
}
cancelUntil(rightlevel);
if (canBeRemoved) {
// it is not a necessary literal
forget(Math.abs(d));
IConstr confl = propagate();
assert confl == null;
removed++;
} else {
this.prime[Math.abs(d)] = d;
cancel();
assert voc.isUnassigned(toInternal(d));
noproblem = setAndPropagate(toInternal(d));
assert noproblem;
}
} else {
// conflict, literal is necessary
this.prime[Math.abs(d)] = d;
cancel();
noproblem = setAndPropagate(toInternal(d));
assert noproblem;
}
}
cancelUntil(0);
int[] implicant = new int[this.prime.length - removed - 1];
int index = 0;
for (int i : this.prime) {
if (i != 0) {
implicant[index++] = i;
}
}
if (isVerbose()) {
System.out.printf("%s prime implicant computation statistics%n",
getLogPrefix());
System.out
.printf("%s implied: %d, decision: %d (removed %d, tested %d, propagated %d), l2 propagation:%d%n",
getLogPrefix(), implied.size(), decisions.size(),
removed, tested, propagated, l2propagation);
}
return implicant;
}
public boolean primeImplicant(int p) {
if (p == 0 || Math.abs(p) > realNumberOfVariables()) {
throw new IllegalArgumentException(
"Use a valid Dimacs var id as argument!"); //$NON-NLS-1$
}
if (this.prime == null) {
throw new UnsupportedOperationException(
"Call the primeImplicant method first!!!"); //$NON-NLS-1$
}
return this.prime[Math.abs(p)] == p;
}
public boolean model(int var) {
if (var <= 0 || var > realNumberOfVariables()) {
throw new IllegalArgumentException(
"Use a valid Dimacs var id as argument!"); //$NON-NLS-1$
}
if (this.userbooleanmodel == null) {
throw new UnsupportedOperationException(
"Call the solve method first!!!"); //$NON-NLS-1$
}
return this.userbooleanmodel[var - 1];
}
public void clearLearntClauses() {
for (Iterator<Constr> iterator = this.learnts.iterator(); iterator
.hasNext();) {
iterator.next().remove(this);
}
this.learnts.clear();
this.learnedLiterals.clear();
}
protected final void reduceDB() {
this.stats.reduceddb++;
this.slistener.cleaning();
this.learnedConstraintsDeletionStrategy.reduce(this.learnts);
System.gc();
}
/**
* @param learnts
*/
protected void sortOnActivity() {
this.learnts.sort(this.comparator);
}
/**
*
*/
protected void decayActivities() {
this.order.varDecayActivity();
claDecayActivity();
}
/**
*
*/
private void claDecayActivity() {
this.claInc *= this.claDecay;
}
/**
* @return true iff the set of constraints is satisfiable, else false.
*/
public boolean isSatisfiable() throws TimeoutException {
return isSatisfiable(VecInt.EMPTY);
}
/**
* @return true iff the set of constraints is satisfiable, else false.
*/
public boolean isSatisfiable(boolean global) throws TimeoutException {
return isSatisfiable(VecInt.EMPTY, global);
}
private double timebegin = 0;
private boolean needToReduceDB;
private ConflictTimerContainer conflictCount;
private transient Timer timer;
public boolean isSatisfiable(IVecInt assumps) throws TimeoutException {
return isSatisfiable(assumps, false);
}
public final LearnedConstraintsDeletionStrategy fixedSize(final int maxsize) {
return new LearnedConstraintsDeletionStrategy() {
private static final long serialVersionUID = 1L;
private final ConflictTimer aTimer = new ConflictTimerAdapter(
maxsize) {
private static final long serialVersionUID = 1L;
@Override
public void run() {
Solver.this.needToReduceDB = true;
}
};
public void reduce(IVec<Constr> learnedConstrs) {
int i, j, k;
for (i = j = k = 0; i < Solver.this.learnts.size()
&& Solver.this.learnts.size() - k > maxsize; i++) {
Constr c = Solver.this.learnts.get(i);
if (c.locked() || c.size() == 2) {
Solver.this.learnts
.set(j++, Solver.this.learnts.get(i));
} else {
c.remove(Solver.this);
k++;
}
}
for (; i < Solver.this.learnts.size(); i++) {
Solver.this.learnts.set(j++, Solver.this.learnts.get(i));
}
if (Solver.this.verbose) {
Solver.this.out.log(getLogPrefix()
+ "cleaning " + (Solver.this.learnts.size() - j) //$NON-NLS-1$
+ " clauses out of " + Solver.this.learnts.size()); //$NON-NLS-1$
// out.flush();
}
Solver.this.learnts.shrinkTo(j);
}
public void onConflictAnalysis(Constr reason) {
// TODO Auto-generated method stub
}
public void onClauseLearning(Constr outLearnt) {
// TODO Auto-generated method stub
}
@Override
public String toString() {
return "Fixed size (" + maxsize
+ ") learned constraints deletion strategy";
}
public void init() {
}
public ConflictTimer getTimer() {
return this.aTimer;
}
public void onPropagation(Constr from) {
// TODO Auto-generated method stub
}
};
}
private LearnedConstraintsDeletionStrategy activityBased(
final ConflictTimer timer) {
return new LearnedConstraintsDeletionStrategy() {
private static final long serialVersionUID = 1L;
private final ConflictTimer freeMem = timer;
public void reduce(IVec<Constr> learnedConstrs) {
sortOnActivity();
int i, j;
for (i = j = 0; i < Solver.this.learnts.size() / 2; i++) {
Constr c = Solver.this.learnts.get(i);
if (c.locked() || c.size() == 2) {
Solver.this.learnts
.set(j++, Solver.this.learnts.get(i));
} else {
c.remove(Solver.this);
}
}
for (; i < Solver.this.learnts.size(); i++) {
Solver.this.learnts.set(j++, Solver.this.learnts.get(i));
}
if (Solver.this.verbose) {
Solver.this.out.log(getLogPrefix()
+ "cleaning " + (Solver.this.learnts.size() - j) //$NON-NLS-1$
+ " clauses out of " + Solver.this.learnts.size()); //$NON-NLS-1$
// out.flush();
}
Solver.this.learnts.shrinkTo(j);
}
public ConflictTimer getTimer() {
return this.freeMem;
}
@Override
public String toString() {
return "Memory based learned constraints deletion strategy";
}
public void init() {
// do nothing
}
public void onClauseLearning(Constr constr) {
// do nothing
}
public void onConflictAnalysis(Constr reason) {
if (reason.learnt()) {
claBumpActivity(reason);
}
}
public void onPropagation(Constr from) {
// do nothing
}
};
}
private final ConflictTimer memoryTimer = new ConflictTimerAdapter(500) {
private static final long serialVersionUID = 1L;
final long memorybound = Runtime.getRuntime().freeMemory() / 10;
@Override
public void run() {
long freemem = Runtime.getRuntime().freeMemory();
// System.out.println("c Free memory "+freemem);
if (freemem < this.memorybound) {
// Reduce the set of learnt clauses
Solver.this.needToReduceDB = true;
}
}
};
/**
* @since 2.1
*/
public final LearnedConstraintsDeletionStrategy memory_based = activityBased(this.memoryTimer);
private class GlucoseLCDS implements LearnedConstraintsDeletionStrategy {
private static final long serialVersionUID = 1L;
private int[] flags = new int[0];
private int flag = 0;
// private int wall = 0;
private final ConflictTimer clauseManagement;
GlucoseLCDS(ConflictTimer timer) {
this.clauseManagement = timer;
}
public void reduce(IVec<Constr> learnedConstrs) {
sortOnActivity();
int i, j;
for (i = j = learnedConstrs.size() / 2; i < learnedConstrs.size(); i++) {
Constr c = learnedConstrs.get(i);
if (c.locked() || c.getActivity() <= 2.0) {
learnedConstrs.set(j++, Solver.this.learnts.get(i));
} else {
c.remove(Solver.this);
}
}
if (Solver.this.verbose) {
Solver.this.out
.log(getLogPrefix()
+ "cleaning " + (learnedConstrs.size() - j) //$NON-NLS-1$
+ " clauses out of " + learnedConstrs.size() + " with flag " + this.flag + "/" + Solver.this.stats.conflicts); //$NON-NLS-1$ //$NON-NLS-2$
// out.flush();
}
Solver.this.learnts.shrinkTo(j);
}
public ConflictTimer getTimer() {
return this.clauseManagement;
}
@Override
public String toString() {
return "Glucose learned constraints deletion strategy";
}
public void init() {
final int howmany = Solver.this.voc.nVars();
// wall = constrs.size() > 10000 ? constrs.size() : 10000;
if (this.flags.length <= howmany) {
this.flags = new int[howmany + 1];
}
this.flag = 0;
this.clauseManagement.reset();
}
public void onClauseLearning(Constr constr) {
int nblevel = computeLBD(constr);
constr.incActivity(nblevel);
}
protected int computeLBD(Constr constr) {
int nblevel = 1;
this.flag++;
int currentLevel;
for (int i = 1; i < constr.size(); i++) {
currentLevel = Solver.this.voc.getLevel(constr.get(i));
if (this.flags[currentLevel] != this.flag) {
this.flags[currentLevel] = this.flag;
nblevel++;
}
}
return nblevel;
}
public void onConflictAnalysis(Constr reason) {
}
public void onPropagation(Constr from) {
}
}
private class Glucose2LCDS extends GlucoseLCDS {
/**
*
*/
private static final long serialVersionUID = 1L;
Glucose2LCDS(ConflictTimer timer) {
super(timer);
}
@Override
public String toString() {
return "Glucose 2 learned constraints deletion strategy";
}
@Override
public void onPropagation(Constr from) {
if (from.getActivity() > 2.0) {
int nblevel = computeLBD(from);
if (nblevel < from.getActivity()) {
Solver.this.stats.updateLBD++;
from.setActivity(nblevel);
}
}
}
}
private final ConflictTimer lbdTimer = new ConflictTimerAdapter(1000) {
private static final long serialVersionUID = 1L;
private int nbconflict = 0;
private static final int MAX_CLAUSE = 5000;
private static final int INC_CLAUSE = 1000;
private int nextbound = MAX_CLAUSE;
@Override
public void run() {
this.nbconflict += bound();
if (this.nbconflict >= this.nextbound) {
this.nextbound += INC_CLAUSE;
// if (nextbound > wall) {
// nextbound = wall;
// }
this.nbconflict = 0;
Solver.this.needToReduceDB = true;
}
}
@Override
public void reset() {
super.reset();
this.nextbound = MAX_CLAUSE;
if (this.nbconflict >= this.nextbound) {
this.nbconflict = 0;
Solver.this.needToReduceDB = true;
}
}
};
/**
* @since 2.1
*/
public final LearnedConstraintsDeletionStrategy glucose = new Glucose2LCDS(
this.lbdTimer);
protected LearnedConstraintsDeletionStrategy learnedConstraintsDeletionStrategy = this.glucose;
/*
* (non-Javadoc)
*
* @see
* org.sat4j.minisat.core.ICDCL#setLearnedConstraintsDeletionStrategy(org
* .sat4j.minisat.core.Solver.LearnedConstraintsDeletionStrategy)
*/
public void setLearnedConstraintsDeletionStrategy(
LearnedConstraintsDeletionStrategy lcds) {
if (this.conflictCount != null) {
this.conflictCount.add(lcds.getTimer());
assert this.learnedConstraintsDeletionStrategy != null;
this.conflictCount.remove(this.learnedConstraintsDeletionStrategy
.getTimer());
}
this.learnedConstraintsDeletionStrategy = lcds;
}
private boolean lastConflictMeansUnsat;
public boolean isSatisfiable(IVecInt assumps, boolean global)
throws TimeoutException {
Lbool status = Lbool.UNDEFINED;
boolean alreadylaunched = this.conflictCount != null;
final int howmany = this.voc.nVars();
if (this.mseen.length <= howmany) {
this.mseen = new boolean[howmany + 1];
}
this.trail.ensure(howmany);
this.trailLim.ensure(howmany);
this.learnedLiterals.ensure(howmany);
this.decisions.clear();
this.implied.clear();
this.slistener.init(this);
this.slistener.start();
this.model = null; // forget about previous model
this.userbooleanmodel = null;
this.prime = null;
this.unsatExplanationInTermsOfAssumptions = null;
if (!alreadylaunched || !this.keepHot) {
this.order.init();
}
this.learnedConstraintsDeletionStrategy.init();
int learnedLiteralsLimit = this.trail.size();
// Fix for Bug SAT37
this.qhead = 0;
// Apply undos on unit literals because they are getting propagated
// again now that qhead is 0.
for (int i = learnedLiteralsLimit - 1; i >= 0; i--) {
int p = this.trail.get(i);
IVec<Undoable> undos = this.voc.undos(p);
assert undos != null;
for (int size = undos.size(); size > 0; size--) {
undos.last().undo(p);
undos.pop();
}
}
// push previously learned literals
for (IteratorInt iterator = this.learnedLiterals.iterator(); iterator
.hasNext();) {
enqueue(iterator.next());
}
// propagate constraints
Constr confl = propagate();
if (confl != null) {
analyzeAtRootLevel(confl);
this.slistener.conflictFound(confl, 0, 0);
this.slistener.end(Lbool.FALSE);
cancelUntil(0);
cancelLearntLiterals(learnedLiteralsLimit);
return false;
}
// push incremental assumptions
for (IteratorInt iterator = assumps.iterator(); iterator.hasNext();) {
int assump = iterator.next();
int p = this.voc.getFromPool(assump);
if (!this.voc.isSatisfied(p) && !assume(p)
|| (confl = propagate()) != null) {
if (confl == null) {
this.slistener.conflictFound(p);
this.unsatExplanationInTermsOfAssumptions = analyzeFinalConflictInTermsOfAssumptions(
null, assumps, p);
this.unsatExplanationInTermsOfAssumptions.push(assump);
} else {
this.slistener.conflictFound(confl, decisionLevel(),
this.trail.size());
this.unsatExplanationInTermsOfAssumptions = analyzeFinalConflictInTermsOfAssumptions(
confl, assumps, ILits.UNDEFINED);
}
this.slistener.end(Lbool.FALSE);
cancelUntil(0);
cancelLearntLiterals(learnedLiteralsLimit);
return false;
}
}
this.rootLevel = decisionLevel();
// moved initialization here if new literals are added in the
// assumptions.
if (!alreadylaunched || !this.keepHot) {
this.order.init(); // duplicated on purpose
}
this.learner.init();
if (!alreadylaunched) {
this.conflictCount = new ConflictTimerContainer();
this.conflictCount.add(this.restarter);
this.conflictCount.add(this.learnedConstraintsDeletionStrategy
.getTimer());
}
boolean firstTimeGlobal = false;
if (this.timeBasedTimeout) {
if (!global || this.timer == null) {
firstTimeGlobal = true;
this.undertimeout = true;
TimerTask stopMe = new TimerTask() {
@Override
public void run() {
Solver.this.undertimeout = false;
}
};
this.timer = new Timer(true);
this.timer.schedule(stopMe, this.timeout);
}
} else {
if (!global || !alreadylaunched) {
firstTimeGlobal = true;
this.undertimeout = true;
ConflictTimer conflictTimeout = new ConflictTimerAdapter(
(int) this.timeout) {
private static final long serialVersionUID = 1L;
@Override
public void run() {
Solver.this.undertimeout = false;
}
};
this.conflictCount.add(conflictTimeout);
}
}
if (!global || firstTimeGlobal) {
this.restarter.init(this.params, this.stats);
this.timebegin = System.currentTimeMillis();
}
this.needToReduceDB = false;
// this is used to allow the solver to be incomplete,
// when using a heuristics limited to a subset of variables
this.lastConflictMeansUnsat = true;
// Solve
while (status == Lbool.UNDEFINED && this.undertimeout
&& this.lastConflictMeansUnsat) {
int before = this.trail.size();
unitClauseProvider.provideUnitClauses(this);
this.stats.importedUnits += this.trail.size() - before;
status = search(assumps);
if (status == Lbool.UNDEFINED) {
this.restarter.onRestart();
this.slistener.restarting();
}
}
cancelUntil(0);
cancelLearntLiterals(learnedLiteralsLimit);
if (!global && this.timeBasedTimeout && this.timer != null) {
this.timer.cancel();
this.timer = null;
}
this.slistener.end(status);
if (!this.undertimeout) {
String message = " Timeout (" + this.timeout
+ (this.timeBasedTimeout ? "s" : " conflicts")
+ ") exceeded";
throw new TimeoutException(message);
}
if (status == Lbool.UNDEFINED && !this.lastConflictMeansUnsat) {
throw new TimeoutException("Cannot decide the satisfiability");
}
// When using a search enumerator (to compute all models)
// the final answer is FALSE, however we are aware of at least one model
// (the last one)
return model != null;
}
public void printInfos(PrintWriter out) {
printInfos(out, prefix);
}
public void printInfos(PrintWriter out, String prefix) {
out.print(prefix);
out.println("constraints type ");
long total = 0;
for (Map.Entry<String, Counter> entry : this.constrTypes.entrySet()) {
out.println(prefix + entry.getKey() + " => " + entry.getValue());
total += entry.getValue().getValue();
}
out.print(prefix);
out.print(total);
out.println(" constraints processed.");
}
/**
* @since 2.1
*/
public void printLearntClausesInfos(PrintWriter out, String prefix) {
Map<String, Counter> learntTypes = new HashMap<String, Counter>();
for (Iterator<Constr> it = this.learnts.iterator(); it.hasNext();) {
String type = it.next().getClass().getName();
Counter count = learntTypes.get(type);
if (count == null) {
learntTypes.put(type, new Counter());
} else {
count.inc();
}
}
out.print(prefix);
out.println("learnt constraints type ");
for (Map.Entry<String, Counter> entry : learntTypes.entrySet()) {
out.println(prefix + entry.getKey() + " => " + entry.getValue());
}
}
public SolverStats getStats() {
return this.stats;
}
/**
*
* @param myStats
* @since 2.2
*/
protected void initStats(SolverStats myStats) {
this.stats = myStats;
}
/*
* (non-Javadoc)
*
* @see org.sat4j.minisat.core.ICDCL#getOrder()
*/
public IOrder getOrder() {
return this.order;
}
/*
* (non-Javadoc)
*
* @see org.sat4j.minisat.core.ICDCL#setOrder(org.sat4j.minisat.core.IOrder)
*/
public void setOrder(IOrder h) {
this.order = h;
this.order.setLits(this.voc);
}
public ILits getVocabulary() {
return this.voc;
}
public void reset() {
if (this.timer != null) {
this.timer.cancel();
this.timer = null;
}
this.trail.clear();
this.trailLim.clear();
this.qhead = 0;
for (Iterator<Constr> iterator = this.constrs.iterator(); iterator
.hasNext();) {
iterator.next().remove(this);
}
this.constrs.clear();
clearLearntClauses();
this.voc.resetPool();
this.dsfactory.reset();
this.stats.reset();
this.constrTypes.clear();
}
public int nVars() {
if (this.declaredMaxVarId == 0) {
return this.voc.nVars();
}
return this.declaredMaxVarId;
}
/**
* @param constr
* a constraint implementing the Constr interface.
* @return a reference to the constraint for external use.
*/
protected IConstr addConstr(Constr constr) {
if (constr == null) {
Counter count = this.constrTypes
.get("ignored satisfied constraints");
if (count == null) {
this.constrTypes.put("ignored satisfied constraints",
new Counter());
} else {
count.inc();
}
} else {
this.constrs.push(constr);
String type = constr.getClass().getName();
Counter count = this.constrTypes.get(type);
if (count == null) {
this.constrTypes.put(type, new Counter());
} else {
count.inc();
}
}
return constr;
}
public DataStructureFactory getDSFactory() {
return this.dsfactory;
}
public IVecInt getOutLearnt() {
return this.moutLearnt;
}
/**
* returns the ith constraint in the solver.
*
* @param i
* the constraint number (begins at 0)
* @return the ith constraint
*/
public IConstr getIthConstr(int i) {
return this.constrs.get(i);
}
/*
* (non-Javadoc)
*
* @see org.sat4j.specs.ISolver#printStat(java.io.PrintStream,
* java.lang.String)
*/
public void printStat(PrintStream out, String prefix) {
printStat(new PrintWriter(out, true), prefix);
}
public void printStat(PrintWriter out) {
printStat(out, prefix);
}
public void printStat(PrintWriter out, String prefix) {
this.stats.printStat(out, prefix);
double cputime = (System.currentTimeMillis() - this.timebegin) / 1000;
out.println(prefix
+ "speed (assignments/second)\t: " + this.stats.propagations //$NON-NLS-1$
/ cputime);
this.order.printStat(out, prefix);
printLearntClausesInfos(out, prefix);
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
public String toString(String prefix) {
StringBuffer stb = new StringBuffer();
Object[] objs = { this.dsfactory, this.learner, this.params,
this.order, this.simplifier, this.restarter,
this.learnedConstraintsDeletionStrategy };
stb.append(prefix);
stb.append("--- Begin Solver configuration ---"); //$NON-NLS-1$
stb.append("\n"); //$NON-NLS-1$
for (Object o : objs) {
stb.append(prefix);
stb.append(o.toString());
stb.append("\n"); //$NON-NLS-1$
}
stb.append(prefix);
stb.append("timeout=");
if (this.timeBasedTimeout) {
stb.append(this.timeout / 1000);
stb.append("s\n");
} else {
stb.append(this.timeout);
stb.append(" conflicts\n");
}
stb.append(prefix);
stb.append("DB Simplification allowed=");
stb.append(this.isDBSimplificationAllowed);
stb.append("\n");
stb.append(prefix);
if (isSolverKeptHot()) {
stb.append("Heuristics kept accross calls (keep the solver \"hot\")\n");
stb.append(prefix);
}
stb.append("Listener: ");
stb.append(slistener);
stb.append("\n");
stb.append(prefix);
stb.append("--- End Solver configuration ---"); //$NON-NLS-1$
return stb.toString();
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return toString(""); //$NON-NLS-1$
}
public int getTimeout() {
return (int) (this.timeBasedTimeout ? this.timeout / 1000
: this.timeout);
}
/**
* @since 2.1
*/
public long getTimeoutMs() {
if (!this.timeBasedTimeout) {
throw new UnsupportedOperationException(
"The timeout is given in number of conflicts!");
}
return this.timeout;
}
public void setExpectedNumberOfClauses(int nb) {
this.constrs.ensure(nb);
}
public Map<String, Number> getStat() {
return this.stats.toMap();
}
public int[] findModel() throws TimeoutException {
if (isSatisfiable()) {
return model();
}
// DLB findbugs ok
// A zero length array would mean that the formula is a tautology.
return null;
}
public int[] findModel(IVecInt assumps) throws TimeoutException {
if (isSatisfiable(assumps)) {
return model();
}
// DLB findbugs ok
// A zero length array would mean that the formula is a tautology.
return null;
}
public boolean isDBSimplificationAllowed() {
return this.isDBSimplificationAllowed;
}
public void setDBSimplificationAllowed(boolean status) {
this.isDBSimplificationAllowed = status;
}
/**
* @since 2.1
*/
public int nextFreeVarId(boolean reserve) {
return this.voc.nextFreeVarId(reserve);
}
/**
* @since 2.1
*/
public IConstr addBlockingClause(IVecInt literals)
throws ContradictionException {
return addClause(literals);
}
/**
* @since 2.1
*/
public void unset(int p) {
// the literal might already have been
// removed from the trail.
if (this.voc.isUnassigned(p) || this.trail.isEmpty()) {
return;
}
int current = this.trail.last();
while (current != p) {
undoOne();
if (this.trail.isEmpty()) {
return;
}
current = this.trail.last();
}
undoOne();
this.qhead = this.trail.size();
}
/**
* @since 2.2
*/
public void setLogPrefix(String prefix) {
this.prefix = prefix;
}
/**
* @since 2.2
*/
public String getLogPrefix() {
return this.prefix;
}
/**
* @since 2.2
*/
public IVecInt unsatExplanation() {
IVecInt copy = new VecInt(
this.unsatExplanationInTermsOfAssumptions.size());
this.unsatExplanationInTermsOfAssumptions.copyTo(copy);
return copy;
}
/**
* @since 2.3.1
*/
public int[] modelWithInternalVariables() {
if (this.model == null) {
throw new UnsupportedOperationException(
"Call the solve method first!!!"); //$NON-NLS-1$
}
int[] nmodel;
if (nVars() == realNumberOfVariables()) {
nmodel = new int[this.model.length];
System.arraycopy(this.model, 0, nmodel, 0, nmodel.length);
} else {
nmodel = new int[this.fullmodel.length];
System.arraycopy(this.fullmodel, 0, nmodel, 0, nmodel.length);
}
return nmodel;
}
/**
* @since 2.3.1
*/
public int realNumberOfVariables() {
return this.voc.nVars();
}
/**
* @since 2.3.2
*/
public void stop() {
expireTimeout();
}
protected Constr sharedConflict;
/**
* @since 2.3.2
*/
public void backtrack(int[] reason) {
IVecInt clause = new VecInt(reason.length);
for (int d : reason) {
clause.push(LiteralsUtils.toInternal(d));
}
this.sharedConflict = this.dsfactory.createUnregisteredClause(clause);
learn(this.sharedConflict);
}
/**
* @since 2.3.2
*/
public Lbool truthValue(int literal) {
int p = LiteralsUtils.toInternal(literal);
if (this.voc.isFalsified(p)) {
return Lbool.FALSE;
}
if (this.voc.isSatisfied(p)) {
return Lbool.TRUE;
}
return Lbool.UNDEFINED;
}
/**
* @since 2.3.2
*/
public int currentDecisionLevel() {
return decisionLevel();
}
/**
* @since 2.3.2
*/
public int[] getLiteralsPropagatedAt(int decisionLevel) {
throw new UnsupportedOperationException("Not implemented yet!");
}
/**
* @since 2.3.2
*/
public void suggestNextLiteralToBranchOn(int l) {
throw new UnsupportedOperationException("Not implemented yet!");
}
protected boolean isNeedToReduceDB() {
return this.needToReduceDB;
}
public void setNeedToReduceDB(boolean needToReduceDB) {
this.needToReduceDB = needToReduceDB;
}
public void setLogger(ILogAble out) {
this.out = out;
}
public ILogAble getLogger() {
return this.out;
}
public double[] getVariableHeuristics() {
return this.order.getVariableHeuristics();
}
public IVec<Constr> getLearnedConstraints() {
return this.learnts;
}
/**
* @since 2.3.2
*/
public void setLearnedConstraintsDeletionStrategy(ConflictTimer timer,
LearnedConstraintsEvaluationType evaluation) {
if (this.conflictCount != null) {
this.conflictCount.add(timer);
this.conflictCount.remove(this.learnedConstraintsDeletionStrategy
.getTimer());
}
switch (evaluation) {
case ACTIVITY:
this.learnedConstraintsDeletionStrategy = activityBased(timer);
break;
case LBD:
this.learnedConstraintsDeletionStrategy = new GlucoseLCDS(timer);
break;
case LBD2:
this.learnedConstraintsDeletionStrategy = new Glucose2LCDS(timer);
break;
}
if (this.conflictCount != null) {
this.learnedConstraintsDeletionStrategy.init();
}
}
/**
* @since 2.3.2
*/
public void setLearnedConstraintsDeletionStrategy(
LearnedConstraintsEvaluationType evaluation) {
ConflictTimer aTimer = this.learnedConstraintsDeletionStrategy
.getTimer();
switch (evaluation) {
case ACTIVITY:
this.learnedConstraintsDeletionStrategy = activityBased(aTimer);
break;
case LBD:
this.learnedConstraintsDeletionStrategy = new GlucoseLCDS(aTimer);
break;
case LBD2:
this.learnedConstraintsDeletionStrategy = new Glucose2LCDS(aTimer);
break;
}
if (this.conflictCount != null) {
this.learnedConstraintsDeletionStrategy.init();
}
}
public boolean isSolverKeptHot() {
return this.keepHot;
}
public void setKeepSolverHot(boolean keepHot) {
this.keepHot = keepHot;
}
private final Comparator<Integer> dimacsLevel = new Comparator<Integer>() {
public int compare(Integer i1, Integer i2) {
return voc.getLevel(Math.abs(i2)) - voc.getLevel(Math.abs(i1));
}
};
public IConstr addClauseOnTheFly(int[] literals) {
List<Integer> lliterals = new ArrayList<Integer>();
for (Integer d : literals) {
lliterals.add(d);
}
Collections.sort(lliterals, dimacsLevel);
IVecInt clause = new VecInt(literals.length);
for (int d : lliterals) {
clause.push(LiteralsUtils.toInternal(d));
}
this.sharedConflict = this.dsfactory.createUnregisteredClause(clause);
this.sharedConflict.register();
addConstr(this.sharedConflict);
IVecInt reason = new VecInt();
this.sharedConflict.calcReasonOnTheFly(ILits.UNDEFINED, trail, reason);
Set<Integer> subset = fromLastDecisionLevel(reason);
while (!trail.isEmpty() && !subset.contains(trail.last())) {
undoOne();
if (!trailLim.isEmpty() && trailLim.last() == trail.size()) {
trailLim.pop();
}
}
return this.sharedConflict;
}
public ISolver getSolvingEngine() {
return this;
}
/**
*
* @param literals
*/
public IConstr addAtMostOnTheFly(int[] literals, int degree) {
IVecInt clause = new VecInt(literals.length);
for (int d : literals) {
clause.push(LiteralsUtils.toInternal(-d));
}
IVecInt copy = new VecInt(clause.size());
clause.copyTo(copy);
this.sharedConflict = this.dsfactory
.createUnregisteredCardinalityConstraint(copy, literals.length
- degree);
this.sharedConflict.register();
addConstr(this.sharedConflict);
// backtrack to the first decision level with a reason
// for falsifying that constraint
IVecInt reason = new VecInt();
this.sharedConflict.calcReasonOnTheFly(ILits.UNDEFINED, trail, reason);
Set<Integer> subset = fromLastDecisionLevel(reason);
while (!trail.isEmpty() && !subset.contains(trail.last())) {
undoOne();
if (!trailLim.isEmpty() && trailLim.last() == trail.size()) {
trailLim.pop();
}
}
return this.sharedConflict;
}
protected Set<Integer> fromLastDecisionLevel(IVecInt lits) {
Set<Integer> subset = new HashSet<Integer>();
int max = -1, q, level;
for (int i = 0; i < lits.size(); i++) {
q = lits.get(i);
level = voc.getLevel(q);
if (level > max) {
subset.clear();
subset.add(q);
max = level;
} else if (level == max) {
subset.add(q);
}
}
return subset;
}
public void setUnitClauseProvider(UnitClauseProvider ucp) {
this.unitClauseProvider = ucp;
}
}