From 01042e6ce27f3a6efc025f1c0fdca8e156be6820 Mon Sep 17 00:00:00 2001 From: Christoph Zengler Date: Fri, 26 Jul 2019 09:59:36 +0200 Subject: [PATCH 01/16] PG on solver implementation --- pom.xml | 2 +- .../java/org/logicng/solvers/MiniSat.java | 127 +++-- .../logicng/solvers/sat/MiniSat2Solver.java | 477 ++++++++++-------- .../logicng/solvers/sat/MiniSatConfig.java | 467 +++++++++-------- ...PlaistedGreenbaumTransformationSolver.java | 192 +++++++ .../solvers/sat/ConfigurationsTest.java | 4 +- .../java/org/logicng/solvers/sat/SATTest.java | 357 ++++++------- 7 files changed, 1004 insertions(+), 622 deletions(-) create mode 100644 src/main/java/org/logicng/transformations/cnf/PlaistedGreenbaumTransformationSolver.java diff --git a/pom.xml b/pom.xml index 8b59719d..91ffa6e8 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ 4.0.0 org.logicng logicng - 1.5.1 + 1.6.0 jar LogicNG diff --git a/src/main/java/org/logicng/solvers/MiniSat.java b/src/main/java/org/logicng/solvers/MiniSat.java index fada4303..f3e5e975 100644 --- a/src/main/java/org/logicng/solvers/MiniSat.java +++ b/src/main/java/org/logicng/solvers/MiniSat.java @@ -28,6 +28,10 @@ package org.logicng.solvers; +import static org.logicng.datastructures.Tristate.FALSE; +import static org.logicng.datastructures.Tristate.TRUE; +import static org.logicng.datastructures.Tristate.UNDEF; + import org.logicng.cardinalityconstraints.CCEncoder; import org.logicng.cardinalityconstraints.CCIncrementalData; import org.logicng.collections.LNGBooleanVector; @@ -55,6 +59,7 @@ import org.logicng.solvers.sat.MiniSat2Solver; import org.logicng.solvers.sat.MiniSatConfig; import org.logicng.solvers.sat.MiniSatStyleSolver; +import org.logicng.transformations.cnf.PlaistedGreenbaumTransformationSolver; import java.util.ArrayList; import java.util.Arrays; @@ -69,13 +74,9 @@ import java.util.SortedSet; import java.util.TreeSet; -import static org.logicng.datastructures.Tristate.FALSE; -import static org.logicng.datastructures.Tristate.TRUE; -import static org.logicng.datastructures.Tristate.UNDEF; - /** * Wrapper for the MiniSAT-style SAT solvers. - * @version 1.3.1 + * @version 1.6.0 * @since 1.0 */ public final class MiniSat extends SATSolver { @@ -90,6 +91,7 @@ private enum SolverStyle {MINISAT, GLUCOSE, MINICARD} private final boolean initialPhase; private final boolean incremental; private int nextStateId; + private final PlaistedGreenbaumTransformationSolver pgTransformation; /** * Constructs a new SAT solver instance. @@ -121,6 +123,7 @@ private MiniSat(final FormulaFactory f, final SolverStyle solverStyle, final Min this.validStates = new LNGIntVector(); this.nextStateId = 0; this.ccEncoder = new CCEncoder(f); + this.pgTransformation = new PlaistedGreenbaumTransformationSolver(this); } /** @@ -184,28 +187,41 @@ public static MiniSat miniCard(final FormulaFactory f, final MiniSatConfig confi @Override public void add(final Formula formula, final Proposition proposition) { + this.result = UNDEF; if (formula.type() == FType.PBC) { final PBConstraint constraint = (PBConstraint) formula; - this.result = UNDEF; if (constraint.isCC()) { if (this.style == SolverStyle.MINICARD) { - if (constraint.comparator() == CType.LE) + if (constraint.comparator() == CType.LE) { ((MiniCard) this.solver).addAtMost(generateClauseVector(Arrays.asList(constraint.operands())), constraint.rhs()); - else if (constraint.comparator() == CType.LT && constraint.rhs() > 3) + } else if (constraint.comparator() == CType.LT && constraint.rhs() > 3) { ((MiniCard) this.solver).addAtMost(generateClauseVector(Arrays.asList(constraint.operands())), constraint.rhs() - 1); - else if (constraint.comparator() == CType.EQ && constraint.rhs() == 1) { + } else if (constraint.comparator() == CType.EQ && constraint.rhs() == 1) { ((MiniCard) this.solver).addAtMost(generateClauseVector(Arrays.asList(constraint.operands())), constraint.rhs()); this.solver.addClause(generateClauseVector(Arrays.asList(constraint.operands())), proposition); - } else - this.addClauseSet(constraint.cnf(), proposition); + } else { + addFormulaAsCNF(constraint, proposition); + } } else { final EncodingResult result = EncodingResult.resultForMiniSat(this.f, this); this.ccEncoder.encode(constraint, result); } - } else - this.addClauseSet(constraint.cnf(), proposition); - } else + } else { + addFormulaAsCNF(constraint, proposition); + } + } else { + addFormulaAsCNF(formula, proposition); + } + } + + private void addFormulaAsCNF(final Formula formula, final Proposition proposition) { + if (this.config.getCnfMethod() == MiniSatConfig.CNFMethod.FACTORY_CNF) { this.addClauseSet(formula.cnf(), proposition); + } else if (this.config.getCnfMethod() == MiniSatConfig.CNFMethod.PG_ON_SOLVER) { + this.pgTransformation.addCNFtoSolver(formula, proposition); + } else { + throw new IllegalStateException("Unknown Solver CNF method: " + this.config.getCnfMethod()); + } } @Override @@ -215,16 +231,18 @@ public void addWithoutUnknown(final Formula formula) { final Map map = this.solver.name2idx(); for (final Variable var : formula.variables()) { final Integer index = map.get(var.name()); - if (index == null || index >= nVars) + if (index == null || index >= nVars) { restriction.addLiteral(var.negate()); + } } this.add(formula.restrict(restriction)); } @Override public CCIncrementalData addIncrementalCC(final PBConstraint cc) { - if (!cc.isCC()) + if (!cc.isCC()) { throw new IllegalArgumentException("Cannot generate an incremental cardinality constraint on a pseudo-Boolean constraint"); + } final EncodingResult result = EncodingResult.resultForMiniSat(this.f, this); return this.ccEncoder.encodeIncremental(cc, result); } @@ -232,7 +250,8 @@ public CCIncrementalData addIncrementalCC(final PBConstraint cc) { @Override protected void addClause(final Formula formula, final Proposition proposition) { this.result = UNDEF; - this.solver.addClause(generateClauseVector(formula.literals()), proposition); + final LNGIntVector ps = generateClauseVector(formula.literals()); + this.solver.addClause(ps, proposition); } @Override @@ -245,8 +264,9 @@ protected void addClauseWithRelaxation(final Variable relaxationVar, final Formu @Override public Tristate sat(final SATHandler handler) { - if (this.result != UNDEF) + if (this.result != UNDEF) { return this.result; + } this.result = this.solver.solve(handler); return this.result; } @@ -285,13 +305,15 @@ public Tristate sat(final SATHandler handler, final Collection variables) { - if (this.result == UNDEF) + if (this.result == UNDEF) { throw new IllegalStateException("Cannot get a model as long as the formula is not solved. Call 'sat' first."); + } final LNGIntVector relevantIndices = variables == null ? null : new LNGIntVector(variables.size()); if (relevantIndices != null) { for (final Variable var : variables) { @@ -320,8 +342,9 @@ public List enumerateAllModels(final Collection literals, public List enumerateAllModels(final Collection variables, final Collection additionalVariables, final ModelEnumerationHandler handler) { final List models = new LinkedList<>(); SolverState stateBeforeEnumeration = null; - if (this.style == SolverStyle.MINISAT && this.incremental) + if (this.style == SolverStyle.MINISAT && this.incremental) { stateBeforeEnumeration = this.saveState(); + } boolean proceed = true; SortedSet allVariables = new TreeSet<>(); if (variables == null) { @@ -357,8 +380,9 @@ public List enumerateAllModels(final Collection variables, break; } } - if (this.style == SolverStyle.MINISAT && this.incremental) + if (this.style == SolverStyle.MINISAT && this.incremental) { this.loadState(stateBeforeEnumeration); + } return models; } @@ -399,39 +423,49 @@ public SolverState saveState() { @Override public void loadState(final SolverState state) { int index = -1; - for (int i = this.validStates.size() - 1; i >= 0 && index == -1; i--) - if (this.validStates.get(i) == state.id()) + for (int i = this.validStates.size() - 1; i >= 0 && index == -1; i--) { + if (this.validStates.get(i) == state.id()) { index = i; - if (index == -1) + } + } + if (index == -1) { throw new IllegalArgumentException("The given solver state is not valid anymore."); + } this.validStates.shrinkTo(index + 1); this.solver.loadState(state.state()); this.result = UNDEF; + this.pgTransformation.clearCache(); } @Override public SortedSet knownVariables() { final SortedSet result = new TreeSet<>(); final int nVars = this.solver.nVars(); - for (final Map.Entry entry : this.solver.name2idx().entrySet()) - if (entry.getValue() < nVars) + for (final Map.Entry entry : this.solver.name2idx().entrySet()) { + if (entry.getValue() < nVars) { result.add(this.f.variable(entry.getKey())); + } + } return result; } @Override public UNSATCore unsatCore() { - if (!this.config.proofGeneration()) + if (!this.config.proofGeneration()) { throw new IllegalStateException("Cannot generate an unsat core if proof generation is not turned on"); - if (this.result == TRUE) + } + if (this.result == TRUE) { throw new IllegalStateException("An unsat core can only be generated if the formula is solved and is UNSAT"); + } if (this.result == Tristate.UNDEF) { throw new IllegalStateException("Cannot generate an unsat core before the formula was solved."); } - if (this.underlyingSolver() instanceof MiniCard) + if (this.underlyingSolver() instanceof MiniCard) { throw new IllegalStateException("Cannot compute an unsat core with MiniCard."); - if (this.underlyingSolver() instanceof GlucoseSyrup && this.config.incremental()) + } + if (this.underlyingSolver() instanceof GlucoseSyrup && this.config.incremental()) { throw new IllegalStateException("Cannot compute an unsat core with Glucose in incremental mode."); + } final DRUPTrim trimmer = new DRUPTrim(); @@ -441,8 +475,9 @@ public UNSATCore unsatCore() { clauses.push(pi.clause()); final Formula clause = getFormulaForVector(pi.clause()); Proposition proposition = pi.proposition(); - if (proposition == null) + if (proposition == null) { proposition = new StandardProposition(clause); + } clause2proposition.put(clause, proposition); } @@ -452,11 +487,13 @@ public UNSATCore unsatCore() { } final DRUPTrim.DRUPResult result = trimmer.compute(clauses, this.underlyingSolver().pgProof()); - if (result.trivialUnsat()) + if (result.trivialUnsat()) { return handleTrivialCase(); + } final LinkedHashSet propositions = new LinkedHashSet<>(); - for (final LNGIntVector vector : result.unsatCore()) + for (final LNGIntVector vector : result.unsatCore()) { propositions.add(clause2proposition.get(getFormulaForVector(vector))); + } return new UNSATCore<>(new ArrayList<>(propositions), false); } @@ -490,19 +527,30 @@ private Assignment createAssignment(final LNGBooleanVector vec, final LNGIntVect final Assignment model = new Assignment(); if (relevantIndices == null) { for (int i = 0; i < vec.size(); i++) { - model.addLiteral(this.f.literal(this.solver.nameForIdx(i), vec.get(i))); + final String name = this.solver.nameForIdx(i); + if (isRelevantVariable(name)) { + model.addLiteral(this.f.literal(name, vec.get(i))); + } } } else { for (int i = 0; i < relevantIndices.size(); i++) { final int index = relevantIndices.get(i); if (index != -1) { - model.addLiteral(this.f.literal(this.solver.nameForIdx(index), vec.get(index))); + final String name = this.solver.nameForIdx(index); + if (isRelevantVariable(name)) { + model.addLiteral(this.f.literal(name, vec.get(index))); + } } } } return model; } + private boolean isRelevantVariable(final String name) { + return this.config.isAuxiliaryVariablesInModels() || (!name.startsWith(FormulaFactory.CNF_PREFIX) && + !name.startsWith(FormulaFactory.CC_PREFIX) && !name.startsWith(FormulaFactory.PB_PREFIX)); + } + /** * Returns the underlying core solver. *

@@ -524,7 +572,7 @@ public boolean initialPhase() { private UNSATCore handleTrivialCase() { final LNGVector clauses = this.underlyingSolver().pgOriginalClauses(); - for (int i = 0; i < clauses.size(); i++) + for (int i = 0; i < clauses.size(); i++) { for (int j = i + 1; j < clauses.size(); j++) { if (clauses.get(i).clause().size() == 1 && clauses.get(j).clause().size() == 1 && clauses.get(i).clause().get(0) + clauses.get(j).clause().get(0) == 0) { @@ -536,13 +584,16 @@ private UNSATCore handleTrivialCase() { return new UNSATCore<>(new ArrayList<>(propositions), false); } } + } throw new IllegalStateException("Should be a trivial unsat core, but did not found one."); } private boolean containsEmptyClause(final LNGVector clauses) { - for (final LNGIntVector clause : clauses) - if (clause.empty()) + for (final LNGIntVector clause : clauses) { + if (clause.empty()) { return true; + } + } return false; } diff --git a/src/main/java/org/logicng/solvers/sat/MiniSat2Solver.java b/src/main/java/org/logicng/solvers/sat/MiniSat2Solver.java index 5b7d593a..0f39cd66 100644 --- a/src/main/java/org/logicng/solvers/sat/MiniSat2Solver.java +++ b/src/main/java/org/logicng/solvers/sat/MiniSat2Solver.java @@ -95,7 +95,7 @@ public MiniSat2Solver(final MiniSatConfig config) { * Initializes the additional parameters. */ private void initializeMiniSAT() { - unitClauses = new LNGIntVector(); + this.unitClauses = new LNGIntVector(); this.learntsizeAdjustConfl = 0; this.learntsizeAdjustCnt = 0; this.learntsizeAdjustStartConfl = 100; @@ -104,13 +104,13 @@ private void initializeMiniSAT() { } @Override - public int newVar(boolean sign, boolean dvar) { - int v = vars.size(); - MSVariable newVar = new MSVariable(sign); - vars.push(newVar); - watches.push(new LNGVector()); - watches.push(new LNGVector()); - seen.push(false); + public int newVar(final boolean sign, final boolean dvar) { + final int v = this.vars.size(); + final MSVariable newVar = new MSVariable(sign); + this.vars.push(newVar); + this.watches.push(new LNGVector()); + this.watches.push(new LNGVector()); + this.seen.push(false); newVar.setDecision(dvar); insertVarOrder(v); return v; @@ -123,13 +123,15 @@ public boolean addClause(final LNGIntVector ps, final Proposition proposition) { int i; int j; if (this.config.proofGeneration) { - LNGIntVector vec = new LNGIntVector(ps.size()); - for (i = 0; i < ps.size(); i++) + final LNGIntVector vec = new LNGIntVector(ps.size()); + for (i = 0; i < ps.size(); i++) { vec.push((var(ps.get(i)) + 1) * (-2 * (sign(ps.get(i)) ? 1 : 0) + 1)); + } this.pgOriginalClauses.push(new ProofInformation(vec, proposition)); } - if (!ok) + if (!this.ok) { return false; + } ps.sort(); boolean flag = false; @@ -138,46 +140,51 @@ public boolean addClause(final LNGIntVector ps, final Proposition proposition) { oc = new LNGIntVector(); for (i = 0, p = LIT_UNDEF; i < ps.size(); i++) { oc.push(ps.get(i)); - if (value(ps.get(i)) == Tristate.TRUE || ps.get(i) == not(p) || value(ps.get(i)) == Tristate.FALSE) + if (value(ps.get(i)) == Tristate.TRUE || ps.get(i) == not(p) || value(ps.get(i)) == Tristate.FALSE) { flag = true; + } } } - for (i = 0, j = 0, p = LIT_UNDEF; i < ps.size(); i++) - if (value(ps.get(i)) == Tristate.TRUE || ps.get(i) == not(p)) + for (i = 0, j = 0, p = LIT_UNDEF; i < ps.size(); i++) { + if (value(ps.get(i)) == Tristate.TRUE || ps.get(i) == not(p)) { return true; - else if (value(ps.get(i)) != Tristate.FALSE && ps.get(i) != p) { + } else if (value(ps.get(i)) != Tristate.FALSE && ps.get(i) != p) { p = ps.get(i); ps.set(j++, p); } + } ps.removeElements(i - j); if (flag) { LNGIntVector vec = new LNGIntVector(ps.size() + 1); vec.push(1); - for (i = 0; i < ps.size(); i++) + for (i = 0; i < ps.size(); i++) { vec.push((var(ps.get(i)) + 1) * (-2 * (sign(ps.get(i)) ? 1 : 0) + 1)); + } this.pgProof.push(vec); vec = new LNGIntVector(oc.size()); vec.push(-1); - for (i = 0; i < oc.size(); i++) + for (i = 0; i < oc.size(); i++) { vec.push((var(oc.get(i)) + 1) * (-2 * (sign(oc.get(i)) ? 1 : 0) + 1)); + } this.pgProof.push(vec); } if (ps.empty()) { - ok = false; + this.ok = false; return false; } else if (ps.size() == 1) { uncheckedEnqueue(ps.get(0), null); - ok = propagate() == null; - if (incremental) - unitClauses.push(ps.get(0)); - return ok; + this.ok = propagate() == null; + if (this.incremental) { + this.unitClauses.push(ps.get(0)); + } + return this.ok; } else { final MSClause c = new MSClause(ps, false); - clauses.push(c); + this.clauses.push(c); attachClause(c); } return true; @@ -186,36 +193,42 @@ else if (value(ps.get(i)) != Tristate.FALSE && ps.get(i) != p) { @Override public Tristate solve(final SATHandler handler) { this.handler = handler; - if (this.handler != null) + if (this.handler != null) { this.handler.startedSolving(); - model.clear(); - conflict.clear(); - if (!ok) + } + this.model.clear(); + this.conflict.clear(); + if (!this.ok) { return Tristate.FALSE; - learntsizeAdjustConfl = learntsizeAdjustStartConfl; - learntsizeAdjustCnt = (int) learntsizeAdjustConfl; - maxLearnts = clauses.size() * learntsizeFactor; + } + this.learntsizeAdjustConfl = this.learntsizeAdjustStartConfl; + this.learntsizeAdjustCnt = (int) this.learntsizeAdjustConfl; + this.maxLearnts = this.clauses.size() * this.learntsizeFactor; Tristate status = Tristate.UNDEF; int currRestarts = 0; - while (status == Tristate.UNDEF && !canceledByHandler) { - double restBase = luby(restartInc, currRestarts); - status = search((int) (restBase * restartFirst)); + while (status == Tristate.UNDEF && !this.canceledByHandler) { + final double restBase = luby(this.restartInc, currRestarts); + status = search((int) (restBase * this.restartFirst)); currRestarts++; } if (this.config.proofGeneration) { - if (status == Tristate.FALSE) + if (status == Tristate.FALSE) { this.pgProof.push(new LNGIntVector(1, 0)); + } } if (status == Tristate.TRUE) { - model = new LNGBooleanVector(vars.size()); - for (final MSVariable v : this.vars) - model.push(v.assignment() == Tristate.TRUE); - } else if (status == Tristate.FALSE && conflict.empty()) - ok = false; - if (this.handler != null) + this.model = new LNGBooleanVector(this.vars.size()); + for (final MSVariable v : this.vars) { + this.model.push(v.assignment() == Tristate.TRUE); + } + } else if (status == Tristate.FALSE && this.conflict.empty()) { + this.ok = false; + } + if (this.handler != null) { this.handler.finishedSolving(); + } cancelUntil(0); this.handler = null; this.canceledByHandler = false; @@ -237,40 +250,45 @@ public void reset() { */ @Override public int[] saveState() { - if (!incremental) + if (!this.incremental) { throw new IllegalStateException("Cannot save a state when the incremental mode is deactivated"); - int[] state; + } + final int[] state; state = new int[7]; - state[0] = ok ? 1 : 0; - state[1] = vars.size(); - state[2] = clauses.size(); - state[3] = learnts.size(); - state[4] = unitClauses.size(); + state[0] = this.ok ? 1 : 0; + state[1] = this.vars.size(); + state[2] = this.clauses.size(); + state[3] = this.learnts.size(); + state[4] = this.unitClauses.size(); if (this.config.proofGeneration) { - state[5] = pgOriginalClauses.size(); - state[6] = pgProof.size(); + state[5] = this.pgOriginalClauses.size(); + state[6] = this.pgProof.size(); } return state; } @Override - public void loadState(int[] state) { - if (!incremental) + public void loadState(final int[] state) { + if (!this.incremental) { throw new IllegalStateException("Cannot load a state when the incremental mode is deactivated"); + } int i; completeBacktrack(); this.ok = state[0] == 1; - int newVarsSize = Math.min(state[1], vars.size()); - for (i = this.vars.size() - 1; i >= newVarsSize; i--) + final int newVarsSize = Math.min(state[1], this.vars.size()); + for (i = this.vars.size() - 1; i >= newVarsSize; i--) { this.orderHeap.remove(this.name2idx.remove(this.idx2name.remove(i))); - vars.shrinkTo(newVarsSize); - int newClausesSize = Math.min(state[2], this.clauses.size()); - for (i = this.clauses.size() - 1; i >= newClausesSize; i--) + } + this.vars.shrinkTo(newVarsSize); + final int newClausesSize = Math.min(state[2], this.clauses.size()); + for (i = this.clauses.size() - 1; i >= newClausesSize; i--) { simpleRemoveClause(this.clauses.get(i)); + } this.clauses.shrinkTo(newClausesSize); - int newLearntsSize = Math.min(state[3], this.learnts.size()); - for (i = this.learnts.size() - 1; i >= newLearntsSize; i--) + final int newLearntsSize = Math.min(state[3], this.learnts.size()); + for (i = this.learnts.size() - 1; i >= newLearntsSize; i--) { simpleRemoveClause(this.learnts.get(i)); + } this.learnts.shrinkTo(newLearntsSize); this.watches.shrinkTo(newVarsSize * 2); this.unitClauses.shrinkTo(state[4]); @@ -279,43 +297,45 @@ public void loadState(int[] state) { this.ok = propagate() == null; } if (this.config.proofGeneration) { - int newPgOriginalSize = Math.min(state[5], this.pgOriginalClauses.size()); + final int newPgOriginalSize = Math.min(state[5], this.pgOriginalClauses.size()); this.pgOriginalClauses.shrinkTo(newPgOriginalSize); - int newPgProofSize = Math.min(state[6], this.pgProof.size()); + final int newPgProofSize = Math.min(state[6], this.pgProof.size()); this.pgProof.shrinkTo(newPgProofSize); } } @Override - protected void uncheckedEnqueue(int lit, MSClause reason) { + protected void uncheckedEnqueue(final int lit, final MSClause reason) { assert value(lit) == Tristate.UNDEF; final MSVariable var = v(lit); var.assign(Tristate.fromBool(!sign(lit))); var.setReason(reason); var.setLevel(decisionLevel()); - trail.push(lit); + this.trail.push(lit); } @Override protected void attachClause(final MSClause c) { assert c.size() > 1; - watches.get(not(c.get(0))).push(new MSWatcher(c, c.get(1))); - watches.get(not(c.get(1))).push(new MSWatcher(c, c.get(0))); - if (c.learnt()) - learntsLiterals += c.size(); - else - clausesLiterals += c.size(); + this.watches.get(not(c.get(0))).push(new MSWatcher(c, c.get(1))); + this.watches.get(not(c.get(1))).push(new MSWatcher(c, c.get(0))); + if (c.learnt()) { + this.learntsLiterals += c.size(); + } else { + this.clausesLiterals += c.size(); + } } @Override protected void detachClause(final MSClause c) { assert c.size() > 1; - watches.get(not(c.get(0))).remove(new MSWatcher(c, c.get(1))); - watches.get(not(c.get(1))).remove(new MSWatcher(c, c.get(0))); - if (c.learnt()) - learntsLiterals -= c.size(); - else - clausesLiterals -= c.size(); + this.watches.get(not(c.get(0))).remove(new MSWatcher(c, c.get(1))); + this.watches.get(not(c.get(1))).remove(new MSWatcher(c, c.get(0))); + if (c.learnt()) { + this.learntsLiterals -= c.size(); + } else { + this.clausesLiterals -= c.size(); + } } @Override @@ -323,93 +343,99 @@ protected void removeClause(final MSClause c) { if (this.config.proofGeneration) { final LNGIntVector vec = new LNGIntVector(c.size()); vec.push(-1); - for (int i = 0; i < c.size(); i++) + for (int i = 0; i < c.size(); i++) { vec.push((var(c.get(i)) + 1) * (-2 * (sign(c.get(i)) ? 1 : 0) + 1)); + } this.pgProof.push(vec); } detachClause(c); - if (locked(c)) + if (locked(c)) { v(c.get(0)).setReason(null); + } } @Override protected MSClause propagate() { MSClause confl = null; int numProps = 0; - while (qhead < trail.size()) { - int p = trail.get(qhead++); - LNGVector ws = watches.get(p); + while (this.qhead < this.trail.size()) { + final int p = this.trail.get(this.qhead++); + final LNGVector ws = this.watches.get(p); int iInd = 0; int jInd = 0; numProps++; while (iInd < ws.size()) { - MSWatcher i = ws.get(iInd); - int blocker = i.blocker(); + final MSWatcher i = ws.get(iInd); + final int blocker = i.blocker(); if (value(blocker) == Tristate.TRUE) { ws.set(jInd++, i); iInd++; continue; } - MSClause c = i.clause(); - int falseLit = not(p); + final MSClause c = i.clause(); + final int falseLit = not(p); if (c.get(0) == falseLit) { c.set(0, c.get(1)); c.set(1, falseLit); } assert c.get(1) == falseLit; iInd++; - int first = c.get(0); - MSWatcher w = new MSWatcher(c, first); + final int first = c.get(0); + final MSWatcher w = new MSWatcher(c, first); if (first != blocker && value(first) == Tristate.TRUE) { ws.set(jInd++, w); continue; } boolean foundWatch = false; - for (int k = 2; k < c.size() && !foundWatch; k++) + for (int k = 2; k < c.size() && !foundWatch; k++) { if (value(c.get(k)) != Tristate.FALSE) { c.set(1, c.get(k)); c.set(k, falseLit); - watches.get(not(c.get(1))).push(w); + this.watches.get(not(c.get(1))).push(w); foundWatch = true; } + } if (!foundWatch) { ws.set(jInd++, w); if (value(first) == Tristate.FALSE) { confl = c; - qhead = trail.size(); - while (iInd < ws.size()) + this.qhead = this.trail.size(); + while (iInd < ws.size()) { ws.set(jInd++, ws.get(iInd++)); - } else + } + } else { uncheckedEnqueue(first, c); + } } } ws.removeElements(iInd - jInd); } - simpDBProps -= numProps; + this.simpDBProps -= numProps; return confl; } @Override - protected boolean litRedundant(int p, int abstractLevels) { - analyzeStack.clear(); - analyzeStack.push(p); - int top = analyzeToClear.size(); - while (analyzeStack.size() > 0) { - assert v(analyzeStack.back()).reason() != null; - MSClause c = v(analyzeStack.back()).reason(); - analyzeStack.pop(); + protected boolean litRedundant(final int p, final int abstractLevels) { + this.analyzeStack.clear(); + this.analyzeStack.push(p); + final int top = this.analyzeToClear.size(); + while (this.analyzeStack.size() > 0) { + assert v(this.analyzeStack.back()).reason() != null; + final MSClause c = v(this.analyzeStack.back()).reason(); + this.analyzeStack.pop(); for (int i = 1; i < c.size(); i++) { final int q = c.get(i); - if (!seen.get(var(q)) && v(q).level() > 0) { + if (!this.seen.get(var(q)) && v(q).level() > 0) { if (v(q).reason() != null && (abstractLevel(var(q)) & abstractLevels) != 0) { - seen.set(var(q), true); - analyzeStack.push(q); - analyzeToClear.push(q); + this.seen.set(var(q), true); + this.analyzeStack.push(q); + this.analyzeToClear.push(q); } else { - for (int j = top; j < analyzeToClear.size(); j++) - seen.set(var(analyzeToClear.get(j)), false); - analyzeToClear.removeElements(analyzeToClear.size() - top); + for (int j = top; j < this.analyzeToClear.size(); j++) { + this.seen.set(var(this.analyzeToClear.get(j)), false); + } + this.analyzeToClear.removeElements(this.analyzeToClear.size() - top); return false; } } @@ -419,46 +445,49 @@ protected boolean litRedundant(int p, int abstractLevels) { } @Override - protected void analyzeFinal(int p, final LNGIntVector outConflict) { + protected void analyzeFinal(final int p, final LNGIntVector outConflict) { outConflict.clear(); outConflict.push(p); - if (decisionLevel() == 0) + if (decisionLevel() == 0) { return; - seen.set(var(p), true); + } + this.seen.set(var(p), true); int x; MSVariable v; - for (int i = trail.size() - 1; i >= trailLim.get(0); i--) { - x = var(trail.get(i)); - if (seen.get(x)) { + for (int i = this.trail.size() - 1; i >= this.trailLim.get(0); i--) { + x = var(this.trail.get(i)); + if (this.seen.get(x)) { v = this.vars.get(x); if (v.reason() == null) { assert v.level() > 0; - outConflict.push(not(trail.get(i))); + outConflict.push(not(this.trail.get(i))); } else { final MSClause c = v.reason(); - for (int j = 1; j < c.size(); j++) - if (v(c.get(j)).level() > 0) - seen.set(var(c.get(j)), true); + for (int j = 1; j < c.size(); j++) { + if (v(c.get(j)).level() > 0) { + this.seen.set(var(c.get(j)), true); + } + } } - seen.set(x, false); + this.seen.set(x, false); } } - seen.set(var(p), false); + this.seen.set(var(p), false); } @Override - protected void cancelUntil(int level) { + protected void cancelUntil(final int level) { if (decisionLevel() > level) { - for (int c = trail.size() - 1; c >= trailLim.get(level); c--) { - int x = var(trail.get(c)); - MSVariable v = this.vars.get(x); + for (int c = this.trail.size() - 1; c >= this.trailLim.get(level); c--) { + final int x = var(this.trail.get(c)); + final MSVariable v = this.vars.get(x); v.assign(Tristate.UNDEF); - v.setPolarity(sign(trail.get(c))); + v.setPolarity(sign(this.trail.get(c))); insertVarOrder(x); } - qhead = trailLim.get(level); - trail.removeElements(trail.size() - trailLim.get(level)); - trailLim.removeElements(trailLim.size() - level); + this.qhead = this.trailLim.get(level); + this.trail.removeElements(this.trail.size() - this.trailLim.get(level)); + this.trailLim.removeElements(this.trailLim.size() - level); } } @@ -466,16 +495,17 @@ protected void cancelUntil(int level) { protected void reduceDB() { int i; int j; - double extraLim = claInc / learnts.size(); - learnts.manualSort(MSClause.minisatComparator); - for (i = j = 0; i < learnts.size(); i++) { - final MSClause c = learnts.get(i); - if (c.size() > 2 && !locked(c) && (i < learnts.size() / 2 || c.activity() < extraLim)) - removeClause(learnts.get(i)); - else - learnts.set(j++, learnts.get(i)); - } - learnts.removeElements(i - j); + final double extraLim = this.claInc / this.learnts.size(); + this.learnts.manualSort(MSClause.minisatComparator); + for (i = j = 0; i < this.learnts.size(); i++) { + final MSClause c = this.learnts.get(i); + if (c.size() > 2 && !locked(c) && (i < this.learnts.size() / 2 || c.activity() < extraLim)) { + removeClause(this.learnts.get(i)); + } else { + this.learnts.set(j++, this.learnts.get(i)); + } + } + this.learnts.removeElements(i - j); } @Override @@ -484,17 +514,18 @@ protected void removeSatisfied(final LNGVector cs) { int j; for (i = j = 0; i < cs.size(); i++) { final MSClause c = cs.get(i); - if (satisfied(c)) + if (satisfied(c)) { removeClause(cs.get(i)); - else { + } else { assert value(c.get(0)) == Tristate.UNDEF && value(c.get(1)) == Tristate.UNDEF; if (!this.config.proofGeneration) { // This simplification does not work with proof generation - for (int k = 2; k < c.size(); k++) + for (int k = 2; k < c.size(); k++) { if (value(c.get(k)) == Tristate.FALSE) { c.set(k--, c.get(c.size() - 1)); c.pop(); } + } } cs.set(j++, cs.get(i)); } @@ -504,27 +535,31 @@ protected void removeSatisfied(final LNGVector cs) { @Override protected boolean satisfied(final MSClause c) { - for (int i = 0; i < c.size(); i++) - if (value(c.get(i)) == Tristate.TRUE) + for (int i = 0; i < c.size(); i++) { + if (value(c.get(i)) == Tristate.TRUE) { return true; + } + } return false; } @Override protected boolean simplify() { assert decisionLevel() == 0; - if (!ok || propagate() != null) { - ok = false; + if (!this.ok || propagate() != null) { + this.ok = false; return false; } - if (nAssigns() == simpDBAssigns || (simpDBProps > 0)) + if (nAssigns() == this.simpDBAssigns || (this.simpDBProps > 0)) { return true; - removeSatisfied(learnts); - if (removeSatisfied) - removeSatisfied(clauses); + } + removeSatisfied(this.learnts); + if (this.removeSatisfied) { + removeSatisfied(this.clauses); + } rebuildOrderHeap(); - simpDBAssigns = nAssigns(); - simpDBProps = clausesLiterals + learntsLiterals; + this.simpDBAssigns = nAssigns(); + this.simpDBProps = this.clausesLiterals + this.learntsLiterals; return true; } @@ -534,29 +569,32 @@ protected boolean simplify() { * @return a {@link Tristate} representing the result. {@code FALSE} if the formula is UNSAT, {@code TRUE} if the * formula is SAT, and {@code UNDEF} if the state is not known yet (restart) or the handler canceled the computation */ - private Tristate search(int nofConflicts) { - if (!ok) + private Tristate search(final int nofConflicts) { + if (!this.ok) { return Tristate.FALSE; + } int conflictC = 0; while (true) { - MSClause confl = propagate(); + final MSClause confl = propagate(); if (confl != null) { - if (handler != null && !handler.detectedConflict()) { - canceledByHandler = true; + if (this.handler != null && !this.handler.detectedConflict()) { + this.canceledByHandler = true; return Tristate.UNDEF; } conflictC++; - if (decisionLevel() == 0) + if (decisionLevel() == 0) { return Tristate.FALSE; - LNGIntVector learntClause = new LNGIntVector(); + } + final LNGIntVector learntClause = new LNGIntVector(); analyze(confl, learntClause); - cancelUntil(analyzeBtLevel); + cancelUntil(this.analyzeBtLevel); if (this.config.proofGeneration) { final LNGIntVector vec = new LNGIntVector(learntClause.size()); vec.push(1); - for (int i = 0; i < learntClause.size(); i++) + for (int i = 0; i < learntClause.size(); i++) { vec.push((var(learntClause.get(i)) + 1) * (-2 * (sign(learntClause.get(i)) ? 1 : 0) + 1)); + } this.pgProof.push(vec); } @@ -565,38 +603,42 @@ private Tristate search(int nofConflicts) { this.unitClauses.push(learntClause.get(0)); } else { final MSClause cr = new MSClause(learntClause, true); - learnts.push(cr); + this.learnts.push(cr); attachClause(cr); - if (!incremental) + if (!this.incremental) { claBumpActivity(cr); + } uncheckedEnqueue(learntClause.get(0), cr); } varDecayActivity(); - if (!incremental) + if (!this.incremental) { claDecayActivity(); - if (--learntsizeAdjustCnt == 0) { - learntsizeAdjustConfl *= learntsizeAdjustInc; - learntsizeAdjustCnt = (int) learntsizeAdjustConfl; - maxLearnts *= learntsizeInc; + } + if (--this.learntsizeAdjustCnt == 0) { + this.learntsizeAdjustConfl *= this.learntsizeAdjustInc; + this.learntsizeAdjustCnt = (int) this.learntsizeAdjustConfl; + this.maxLearnts *= this.learntsizeInc; } } else { if (nofConflicts >= 0 && conflictC >= nofConflicts) { cancelUntil(0); return Tristate.UNDEF; } - if (!incremental) { - if (decisionLevel() == 0 && !simplify()) + if (!this.incremental) { + if (decisionLevel() == 0 && !simplify()) { return Tristate.FALSE; - if (learnts.size() - nAssigns() >= maxLearnts) + } + if (this.learnts.size() - nAssigns() >= this.maxLearnts) { reduceDB(); + } } int next = LIT_UNDEF; - while (decisionLevel() < assumptions.size()) { - int p = assumptions.get(decisionLevel()); + while (decisionLevel() < this.assumptions.size()) { + final int p = this.assumptions.get(decisionLevel()); if (value(p) == Tristate.TRUE) { - trailLim.push(trail.size()); + this.trailLim.push(this.trail.size()); } else if (value(p) == Tristate.FALSE) { - analyzeFinal(not(p), conflict); + analyzeFinal(not(p), this.conflict); return Tristate.FALSE; } else { next = p; @@ -605,10 +647,11 @@ private Tristate search(int nofConflicts) { } if (next == LIT_UNDEF) { next = pickBranchLit(); - if (next == LIT_UNDEF) + if (next == LIT_UNDEF) { return Tristate.TRUE; + } } - trailLim.push(trail.size()); + this.trailLim.push(this.trail.size()); uncheckedEnqueue(next, null); } } @@ -625,26 +668,29 @@ private void analyze(final MSClause conflictClause, final LNGIntVector outLearnt int pathC = 0; int p = LIT_UNDEF; outLearnt.push(-1); - int index = trail.size() - 1; + int index = this.trail.size() - 1; do { assert c != null; - if (!incremental && c.learnt()) + if (!this.incremental && c.learnt()) { claBumpActivity(c); + } for (int j = (p == LIT_UNDEF) ? 0 : 1; j < c.size(); j++) { - int q = c.get(j); - if (!seen.get(var(q)) && v(q).level() > 0) { + final int q = c.get(j); + if (!this.seen.get(var(q)) && v(q).level() > 0) { varBumpActivity(var(q)); - seen.set(var(q), true); - if (v(q).level() >= decisionLevel()) + this.seen.set(var(q), true); + if (v(q).level() >= decisionLevel()) { pathC++; - else + } else { outLearnt.push(q); + } } } - while (!seen.get(var(trail.get(index--)))) ; - p = trail.get(index + 1); + while (!this.seen.get(var(this.trail.get(index--)))) { + } + p = this.trail.get(index + 1); c = v(p).reason(); - seen.set(var(p), false); + this.seen.set(var(p), false); pathC--; } while (pathC > 0); outLearnt.set(0, not(p)); @@ -659,58 +705,67 @@ private void simplifyClause(final LNGIntVector outLearnt) { int i; int j; this.analyzeToClear = new LNGIntVector(outLearnt); - if (ccminMode == MiniSatConfig.ClauseMinimization.DEEP) { + if (this.ccminMode == MiniSatConfig.ClauseMinimization.DEEP) { int abstractLevel = 0; - for (i = 1; i < outLearnt.size(); i++) + for (i = 1; i < outLearnt.size(); i++) { abstractLevel |= abstractLevel(var(outLearnt.get(i))); - for (i = j = 1; i < outLearnt.size(); i++) - if (v(outLearnt.get(i)).reason() == null || !litRedundant(outLearnt.get(i), abstractLevel)) + } + for (i = j = 1; i < outLearnt.size(); i++) { + if (v(outLearnt.get(i)).reason() == null || !litRedundant(outLearnt.get(i), abstractLevel)) { outLearnt.set(j++, outLearnt.get(i)); - } else if (ccminMode == MiniSatConfig.ClauseMinimization.BASIC) { + } + } + } else if (this.ccminMode == MiniSatConfig.ClauseMinimization.BASIC) { for (i = j = 1; i < outLearnt.size(); i++) { - if (v(outLearnt.get(i)).reason() == null) + if (v(outLearnt.get(i)).reason() == null) { outLearnt.set(j++, outLearnt.get(i)); - else { - MSClause c = v(outLearnt.get(i)).reason(); - for (int k = 1; k < c.size(); k++) - if (!seen.get(var(c.get(k))) && v(c.get(k)).level() > 0) { + } else { + final MSClause c = v(outLearnt.get(i)).reason(); + for (int k = 1; k < c.size(); k++) { + if (!this.seen.get(var(c.get(k))) && v(c.get(k)).level() > 0) { outLearnt.set(j++, outLearnt.get(i)); break; } + } } } - } else + } else { i = j = outLearnt.size(); + } outLearnt.removeElements(i - j); - analyzeBtLevel = 0; + this.analyzeBtLevel = 0; if (outLearnt.size() > 1) { int max = 1; - for (int k = 2; k < outLearnt.size(); k++) - if (v(outLearnt.get(k)).level() > v(outLearnt.get(max)).level()) + for (int k = 2; k < outLearnt.size(); k++) { + if (v(outLearnt.get(k)).level() > v(outLearnt.get(max)).level()) { max = k; - int p = outLearnt.get(max); + } + } + final int p = outLearnt.get(max); outLearnt.set(max, outLearnt.get(1)); outLearnt.set(1, p); - analyzeBtLevel = v(p).level(); + this.analyzeBtLevel = v(p).level(); + } + for (int l = 0; l < this.analyzeToClear.size(); l++) { + this.seen.set(var(this.analyzeToClear.get(l)), false); } - for (int l = 0; l < analyzeToClear.size(); l++) - seen.set(var(analyzeToClear.get(l)), false); } /** * Performs an unconditional backtrack to level zero. */ private void completeBacktrack() { - for (int v = 0; v < vars.size(); v++) { - MSVariable var = vars.get(v); + for (int v = 0; v < this.vars.size(); v++) { + final MSVariable var = this.vars.get(v); var.assign(Tristate.UNDEF); var.setReason(null); - if (!orderHeap.inHeap(v) && var.decision()) - orderHeap.insert(v); + if (!this.orderHeap.inHeap(v) && var.decision()) { + this.orderHeap.insert(v); + } } - trail.clear(); - trailLim.clear(); - qhead = 0; + this.trail.clear(); + this.trailLim.clear(); + this.qhead = 0; } /** @@ -718,7 +773,7 @@ private void completeBacktrack() { * @param c the clause to remove */ private void simpleRemoveClause(final MSClause c) { - watches.get(not(c.get(0))).remove(new MSWatcher(c, c.get(1))); - watches.get(not(c.get(1))).remove(new MSWatcher(c, c.get(0))); + this.watches.get(not(c.get(0))).remove(new MSWatcher(c, c.get(1))); + this.watches.get(not(c.get(1))).remove(new MSWatcher(c, c.get(0))); } } diff --git a/src/main/java/org/logicng/solvers/sat/MiniSatConfig.java b/src/main/java/org/logicng/solvers/sat/MiniSatConfig.java index a8d419c1..0ff493db 100644 --- a/src/main/java/org/logicng/solvers/sat/MiniSatConfig.java +++ b/src/main/java/org/logicng/solvers/sat/MiniSatConfig.java @@ -28,251 +28,322 @@ package org.logicng.solvers.sat; +import static org.logicng.solvers.sat.MiniSatConfig.CNFMethod.FACTORY_CNF; +import static org.logicng.solvers.sat.MiniSatConfig.ClauseMinimization.DEEP; + import org.logicng.configurations.Configuration; import org.logicng.configurations.ConfigurationType; - -import static org.logicng.solvers.sat.MiniSatConfig.ClauseMinimization.DEEP; +import org.logicng.formulas.Formula; +import org.logicng.solvers.SATSolver; /** * The configuration object for a MiniSAT-style SAT solver. - * @version 1.3 + * @version 1.6.0 * @since 1.0 */ public final class MiniSatConfig extends Configuration { - /** - * The different methods for clause minimization. - * {@code NONE} - no minimization is performed - * {@code BASIC} - local minimization is performed - * {@code DEEP} - recursive minimization is performed - */ - public enum ClauseMinimization { - NONE, BASIC, DEEP - } - - final double varDecay; - final double varInc; - final ClauseMinimization clauseMin; - final int restartFirst; - final double restartInc; - final double clauseDecay; - final boolean removeSatisfied; - final double learntsizeFactor; - final double learntsizeInc; - final boolean incremental; - final boolean initialPhase; - final boolean proofGeneration; - - /** - * Constructs a new MiniSAT configuration from a given builder. - * @param builder the builder - */ - private MiniSatConfig(final Builder builder) { - super(ConfigurationType.MINISAT); - this.varDecay = builder.varDecay; - this.varInc = builder.varInc; - this.clauseMin = builder.clauseMin; - this.restartFirst = builder.restartFirst; - this.restartInc = builder.restartInc; - this.clauseDecay = builder.clauseDecay; - this.removeSatisfied = builder.removeSatisfied; - this.learntsizeFactor = builder.learntsizeFactor; - this.learntsizeInc = builder.learntsizeInc; - this.incremental = builder.incremental; - this.initialPhase = builder.initialPhase; - this.proofGeneration = builder.proofGeneration; - } - - /** - * Returns whether the solver is incremental or not - * @return {@code true} if the solver is incremental, {@code false} otherwise - */ - public boolean incremental() { - return this.incremental; - } - - /** - * Returns the initial phase of the solver. - * @return the initial phase of the solver - */ - public boolean initialPhase() { - return this.initialPhase; - } - - /** - * Returns whether proof generation should be performed or not. - * @return whether proof generation should be performed or not - */ - public boolean proofGeneration() { - return this.proofGeneration; - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder("MiniSatConfig{").append(System.lineSeparator()); - sb.append("varDecay=").append(this.varDecay).append(System.lineSeparator()); - sb.append("varInc=").append(this.varInc).append(System.lineSeparator()); - sb.append("clauseMin=").append(this.clauseMin).append(System.lineSeparator()); - sb.append("restartFirst=").append(this.restartFirst).append(System.lineSeparator()); - sb.append("restartInc=").append(this.restartInc).append(System.lineSeparator()); - sb.append("clauseDecay=").append(this.clauseDecay).append(System.lineSeparator()); - sb.append("removeSatisfied=").append(this.removeSatisfied).append(System.lineSeparator()); - sb.append("learntsizeFactor=").append(this.learntsizeFactor).append(System.lineSeparator()); - sb.append("learntsizeInc=").append(this.learntsizeInc).append(System.lineSeparator()); - sb.append("incremental=").append(this.incremental).append(System.lineSeparator()); - sb.append("initialPhase=").append(this.initialPhase).append(System.lineSeparator()); - sb.append("proofGeneration=").append(this.proofGeneration).append(System.lineSeparator()); - sb.append("}").append(System.lineSeparator()); - return sb.toString(); - } - - /** - * The builder for a MiniSAT configuration. - */ - public static class Builder { - private double varDecay = 0.95; - private double varInc = 1.0; - private ClauseMinimization clauseMin = DEEP; - private int restartFirst = 100; - private double restartInc = 2.0; - private double clauseDecay = 0.999; - private boolean removeSatisfied = true; - private double learntsizeFactor = 1.0 / 3.0; - private double learntsizeInc = 1.1; - private boolean incremental = true; - private boolean initialPhase = false; - private boolean proofGeneration = false; - /** - * Sets the variable activity decay factor to a given value. The default value is 0.95. - * @param varDecay the value (should be in the range 0..1) - * @return the builder + * The different methods for clause minimization. + *

    + *
  • {@code NONE} - no minimization is performed + *
  • {@code BASIC} - local minimization is performed + *
  • {@code DEEP} - recursive minimization is performed + *
*/ - public Builder varDecay(double varDecay) { - this.varDecay = varDecay; - return this; + public enum ClauseMinimization { + NONE, BASIC, DEEP } /** - * Sets the initial value to bump a variable with each time it is used in conflict resolution to a given value. - * The default value is 1.0. - * @param varInc the value - * @return the builder + * The different methods for generating a CNF for a formula to put on the solver. + *
    + *
  • {@code FACTORY_CNF} calls the {@link Formula#cnf()} method on the formula + * to convert it to CNF. Therefore the CNF including all its auxiliary variables will + * be added to the formula factory. + *
  • {@code PG_ON_SOLVER} uses a solver-internal implementation of Plaisted-Greenbaum. + * Auxiliary variables are only added on the solver, not on the factory. This usually + * leads to a reduced heap usage and often faster performance. + *
*/ - public Builder varInc(double varInc) { - this.varInc = varInc; - return this; + public enum CNFMethod { + FACTORY_CNF, PG_ON_SOLVER } - /** - * Sets the clause minimization method. The default value is {@code DEEP}. - * @param ccmin the value - * @return the builder - */ - public Builder clMinimization(final ClauseMinimization ccmin) { - this.clauseMin = ccmin; - return this; - } + final double varDecay; + final double varInc; + final ClauseMinimization clauseMin; + final int restartFirst; + final double restartInc; + final double clauseDecay; + final boolean removeSatisfied; + final double learntsizeFactor; + final double learntsizeInc; + final boolean incremental; + final boolean initialPhase; + final boolean proofGeneration; + final CNFMethod cnfMethod; + final boolean auxiliaryVariablesInModels; /** - * Sets the base restart interval to the given value. The default value is 100. - * @param restartFirst the value (should be at least 1) - * @return the builder + * Constructs a new MiniSAT configuration from a given builder. + * @param builder the builder */ - public Builder restartFirst(int restartFirst) { - this.restartFirst = restartFirst; - return this; + private MiniSatConfig(final Builder builder) { + super(ConfigurationType.MINISAT); + this.varDecay = builder.varDecay; + this.varInc = builder.varInc; + this.clauseMin = builder.clauseMin; + this.restartFirst = builder.restartFirst; + this.restartInc = builder.restartInc; + this.clauseDecay = builder.clauseDecay; + this.removeSatisfied = builder.removeSatisfied; + this.learntsizeFactor = builder.learntsizeFactor; + this.learntsizeInc = builder.learntsizeInc; + this.incremental = builder.incremental; + this.initialPhase = builder.initialPhase; + this.proofGeneration = builder.proofGeneration; + this.cnfMethod = builder.cnfMethod; + this.auxiliaryVariablesInModels = builder.auxiliaryVariablesInModels; } /** - * Sets the restart interval increase factor to the given value. The default value is 2.0. - * @param restartInc the value (should be at least 1) - * @return the builder + * Returns whether the solver is incremental or not + * @return {@code true} if the solver is incremental, {@code false} otherwise */ - public Builder restartInc(double restartInc) { - this.restartInc = restartInc; - return this; + public boolean incremental() { + return this.incremental; } /** - * Sets the clause activity decay factor to a given value. The default value is 0.999. - * @param clauseDecay the value (should be in the range 0..1) - * @return the builder + * Returns the initial phase of the solver. + * @return the initial phase of the solver */ - public Builder clauseDecay(double clauseDecay) { - this.clauseDecay = clauseDecay; - return this; + public boolean initialPhase() { + return this.initialPhase; } /** - * If turned on, the satisfied original clauses will be removed when simplifying on level 0, when turned off, - * only the satisfied learnt clauses will be removed. The default value is {@code true}. - * @param removeSatisfied {@code true} if the original clauses should be simplified, {@code false} otherwise - * @return the builder + * Returns whether proof generation should be performed or not. + * @return whether proof generation should be performed or not */ - public Builder removeSatisfied(boolean removeSatisfied) { - this.removeSatisfied = removeSatisfied; - return this; + public boolean proofGeneration() { + return this.proofGeneration; } /** - * Sets the initial limit for learnt clauses as a factor of the original clauses to the given value. The default - * value is 1/3. - * @param learntsizeFactor the value - * @return the builder + * Returns the CNF method which should be used. + * @return the CNF method */ - public Builder lsFactor(double learntsizeFactor) { - this.learntsizeFactor = learntsizeFactor; - return this; + public CNFMethod getCnfMethod() { + return this.cnfMethod; } /** - * Sets the factor by which the limit for learnt clauses is multiplied every restart to a given value. The default - * value is 1.1. - * @param learntsizeInc the value - * @return the builder + * Returns whether auxiliary Variables should be included in the model or not. + * @return whether auxiliary Variables should be included in the model or not */ - public Builder lsInc(double learntsizeInc) { - this.learntsizeInc = learntsizeInc; - return this; + public boolean isAuxiliaryVariablesInModels() { + return this.auxiliaryVariablesInModels; } - /** - * Turns the incremental mode of the solver off and on. The default value is {@code true}. - * @param incremental {@code true} if incremental mode is turned on, {@code false} otherwise - * @return the builder - */ - public Builder incremental(boolean incremental) { - this.incremental = incremental; - return this; + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("MiniSatConfig{").append(System.lineSeparator()); + sb.append("varDecay=").append(this.varDecay).append(System.lineSeparator()); + sb.append("varInc=").append(this.varInc).append(System.lineSeparator()); + sb.append("clauseMin=").append(this.clauseMin).append(System.lineSeparator()); + sb.append("restartFirst=").append(this.restartFirst).append(System.lineSeparator()); + sb.append("restartInc=").append(this.restartInc).append(System.lineSeparator()); + sb.append("clauseDecay=").append(this.clauseDecay).append(System.lineSeparator()); + sb.append("removeSatisfied=").append(this.removeSatisfied).append(System.lineSeparator()); + sb.append("learntsizeFactor=").append(this.learntsizeFactor).append(System.lineSeparator()); + sb.append("learntsizeInc=").append(this.learntsizeInc).append(System.lineSeparator()); + sb.append("incremental=").append(this.incremental).append(System.lineSeparator()); + sb.append("initialPhase=").append(this.initialPhase).append(System.lineSeparator()); + sb.append("proofGeneration=").append(this.proofGeneration).append(System.lineSeparator()); + sb.append("cnfMethod=").append(this.cnfMethod).append(System.lineSeparator()); + sb.append("auxiliaryVariablesInModels=").append(this.auxiliaryVariablesInModels).append(System.lineSeparator()); + sb.append("}").append(System.lineSeparator()); + return sb.toString(); } /** - * Sets the initial phase of the solver. The default value is {@code true}. - * @param initialPhase the initial phase - * @return the builder + * The builder for a MiniSAT configuration. */ - public Builder initialPhase(boolean initialPhase) { - this.initialPhase = initialPhase; - return this; - } + public static class Builder { + private double varDecay = 0.95; + private double varInc = 1.0; + private ClauseMinimization clauseMin = DEEP; + private int restartFirst = 100; + private double restartInc = 2.0; + private double clauseDecay = 0.999; + private boolean removeSatisfied = true; + private double learntsizeFactor = 1.0 / 3.0; + private double learntsizeInc = 1.1; + private boolean incremental = true; + private boolean initialPhase = false; + private boolean proofGeneration = false; + CNFMethod cnfMethod = FACTORY_CNF; + boolean auxiliaryVariablesInModels = true; - /** - * Sets whether the information for generating a proof with DRUP should be recorded or not. - * @param proofGeneration {@code true} if proof generating information should be recorded, {@code false} otherwise - * @return the builder - */ - public Builder proofGeneration(boolean proofGeneration) { - this.proofGeneration = proofGeneration; - return this; - } + /** + * Sets the variable activity decay factor to a given value. The default value is 0.95. + * @param varDecay the value (should be in the range 0..1) + * @return the builder + */ + public Builder varDecay(final double varDecay) { + this.varDecay = varDecay; + return this; + } - /** - * Builds the MiniSAT configuration. - * @return the configuration - */ - public MiniSatConfig build() { - return new MiniSatConfig(this); + /** + * Sets the initial value to bump a variable with each time it is used in conflict resolution to a given value. + * The default value is 1.0. + * @param varInc the value + * @return the builder + */ + public Builder varInc(final double varInc) { + this.varInc = varInc; + return this; + } + + /** + * Sets the clause minimization method. The default value is {@code DEEP}. + * @param ccmin the value + * @return the builder + */ + public Builder clMinimization(final ClauseMinimization ccmin) { + this.clauseMin = ccmin; + return this; + } + + /** + * Sets the base restart interval to the given value. The default value is 100. + * @param restartFirst the value (should be at least 1) + * @return the builder + */ + public Builder restartFirst(final int restartFirst) { + this.restartFirst = restartFirst; + return this; + } + + /** + * Sets the restart interval increase factor to the given value. The default value is 2.0. + * @param restartInc the value (should be at least 1) + * @return the builder + */ + public Builder restartInc(final double restartInc) { + this.restartInc = restartInc; + return this; + } + + /** + * Sets the clause activity decay factor to a given value. The default value is 0.999. + * @param clauseDecay the value (should be in the range 0..1) + * @return the builder + */ + public Builder clauseDecay(final double clauseDecay) { + this.clauseDecay = clauseDecay; + return this; + } + + /** + * If turned on, the satisfied original clauses will be removed when simplifying on level 0, when turned off, + * only the satisfied learnt clauses will be removed. The default value is {@code true}. + * @param removeSatisfied {@code true} if the original clauses should be simplified, {@code false} otherwise + * @return the builder + */ + public Builder removeSatisfied(final boolean removeSatisfied) { + this.removeSatisfied = removeSatisfied; + return this; + } + + /** + * Sets the initial limit for learnt clauses as a factor of the original clauses to the given value. The default + * value is 1/3. + * @param learntsizeFactor the value + * @return the builder + */ + public Builder lsFactor(final double learntsizeFactor) { + this.learntsizeFactor = learntsizeFactor; + return this; + } + + /** + * Sets the factor by which the limit for learnt clauses is multiplied every restart to a given value. The default + * value is 1.1. + * @param learntsizeInc the value + * @return the builder + */ + public Builder lsInc(final double learntsizeInc) { + this.learntsizeInc = learntsizeInc; + return this; + } + + /** + * Turns the incremental mode of the solver off and on. The default value is {@code true}. + * @param incremental {@code true} if incremental mode is turned on, {@code false} otherwise + * @return the builder + */ + public Builder incremental(final boolean incremental) { + this.incremental = incremental; + return this; + } + + /** + * Sets the initial phase of the solver. The default value is {@code true}. + * @param initialPhase the initial phase + * @return the builder + */ + public Builder initialPhase(final boolean initialPhase) { + this.initialPhase = initialPhase; + return this; + } + + /** + * Sets whether the information for generating a proof with DRUP should be recorded or not. The default + * value is {@code false}. + * @param proofGeneration {@code true} if proof generating information should be recorded, {@code false} otherwise + * @return the builder + */ + public Builder proofGeneration(final boolean proofGeneration) { + this.proofGeneration = proofGeneration; + return this; + } + + /** + * Sets the CNF method for converting formula which are not in CNF for the solver. The default value + * is {@code FACTORY_CNF}. + * @param cnfMethod the CNF method + * @return the builder + */ + public Builder cnfMethod(final CNFMethod cnfMethod) { + this.cnfMethod = cnfMethod; + return this; + } + + /** + * Sets whether auxiliary variables (CNF, cardinality constraints, pseudo-Boolean constraints) should + * be included in methods like {@link SATSolver#model()} or {@link SATSolver#enumerateAllModels()}. If + * set to {@code true}, all variables will be included in these methods, if set to {@code false}, variables + * starting with "@RESERVED_CC_", "@RESERVED_PB_", and "@RESERVED_CNF_" will be excluded from the models. + * The default value is {@code true}. + * @param auxiliaryVariablesInModels {@code true} if auxiliary variables should be included in the models, + * {@code false} otherwise + * @return the builder + */ + public Builder auxiliaryVariablesInModels(final boolean auxiliaryVariablesInModels) { + this.auxiliaryVariablesInModels = auxiliaryVariablesInModels; + return this; + } + + /** + * Builds the MiniSAT configuration. + * @return the configuration + */ + public MiniSatConfig build() { + return new MiniSatConfig(this); + } } - } } diff --git a/src/main/java/org/logicng/transformations/cnf/PlaistedGreenbaumTransformationSolver.java b/src/main/java/org/logicng/transformations/cnf/PlaistedGreenbaumTransformationSolver.java new file mode 100644 index 00000000..24152dbe --- /dev/null +++ b/src/main/java/org/logicng/transformations/cnf/PlaistedGreenbaumTransformationSolver.java @@ -0,0 +1,192 @@ +/////////////////////////////////////////////////////////////////////////// +// __ _ _ ________ // +// / / ____ ____ _(_)____/ | / / ____/ // +// / / / __ \/ __ `/ / ___/ |/ / / __ // +// / /___/ /_/ / /_/ / / /__/ /| / /_/ / // +// /_____/\____/\__, /_/\___/_/ |_/\____/ // +// /____/ // +// // +// The Next Generation Logic Library // +// // +/////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2015-20xx Christoph Zengler // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or // +// implied. See the License for the specific language governing // +// permissions and limitations under the License. // +// // +/////////////////////////////////////////////////////////////////////////// + +package org.logicng.transformations.cnf; + +import org.logicng.collections.LNGIntVector; +import org.logicng.formulas.FType; +import org.logicng.formulas.Formula; +import org.logicng.formulas.FormulaFactory; +import org.logicng.formulas.Literal; +import org.logicng.predicates.CNFPredicate; +import org.logicng.propositions.Proposition; +import org.logicng.solvers.MiniSat; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * A Plaisted-Greenbaum CNF conversion which is performed directly on the internal SAT solver, + * not on a formula factory. + * @version 1.6.0 + * @since 1.6.0 + */ +public final class PlaistedGreenbaumTransformationSolver { + + private final Map variableCache; + private final Set formulaCache; + private final MiniSat solver; + private final CNFPredicate cnfPredicate = new CNFPredicate(); + + /** + * Constructs a new transformation for a given SAT solver. + * @param solver the solver + */ + public PlaistedGreenbaumTransformationSolver(final MiniSat solver) { + this.variableCache = new HashMap<>(); + this.formulaCache = new HashSet<>(); + this.solver = solver; + } + + /** + * Adds the CNF of the given formula (and its optional proposition) to the solver, + * @param formula the formula to add to the solver + * @param proposition the optional proposition of the formula + */ + public void addCNFtoSolver(final Formula formula, final Proposition proposition) { + final Formula nnf = formula.nnf(); + if (nnf.holds(this.cnfPredicate)) { + addCNF(nnf, proposition); + } else { + computeTransformation(nnf, proposition); + final int topLevelVariable = this.variableCache.get(nnf); + this.solver.underlyingSolver().addClause(topLevelVariable, proposition); + this.variableCache.put(formula, topLevelVariable); + } + } + + /** + * Clears the cache. + */ + public void clearCache() { + this.variableCache.clear(); + this.formulaCache.clear(); + } + + private void computeTransformation(final Formula formula, final Proposition proposition) { + switch (formula.type()) { + case LITERAL: + return; + case OR: + case AND: + computePosPolarity(formula, proposition); + for (final Formula op : formula) { + this.computeTransformation(op, proposition); + } + break; + default: + throw new IllegalArgumentException("Could not process the formula type " + formula.type()); + } + } + + private void computePosPolarity(final Formula formula, final Proposition proposition) { + if (this.formulaCache.contains(formula)) { + return; + } + final int pgVar = pgVariable(formula); + switch (formula.type()) { + case AND: { + for (final Formula op : formula) { + this.solver.underlyingSolver().addClause(new LNGIntVector(new int[]{pgVar ^ 1, pgVariable(op)}), proposition); + } + this.formulaCache.add(formula); + break; + } + case OR: { + final LNGIntVector singleClause = new LNGIntVector(); + singleClause.push(pgVar ^ 1); + for (final Formula op : formula) { + singleClause.push(pgVariable(op)); + } + this.solver.underlyingSolver().addClause(singleClause, proposition); + this.formulaCache.add(formula); + break; + } + default: + throw new IllegalArgumentException("not yet implemented"); + } + } + + private void addCNF(final Formula cnf, final Proposition proposition) { + switch (cnf.type()) { + case TRUE: + break; + case FALSE: + case LITERAL: + case OR: + this.solver.underlyingSolver().addClause(generateClauseVector(cnf.literals()), proposition); + break; + case AND: + for (final Formula clause : cnf) { + this.solver.underlyingSolver().addClause(generateClauseVector(clause.literals()), proposition); + } + break; + default: + throw new IllegalArgumentException("Input formula ist not a valid CNF: " + cnf); + } + } + + private int pgVariable(final Formula formula) { + if (formula.type() == FType.LITERAL) { + return solverLiteral((Literal) formula); + } + Integer index = this.variableCache.get(formula); + if (index == null) { + index = newSolverVariable(); + this.variableCache.put(formula, index); + } + return index; + } + + private LNGIntVector generateClauseVector(final Collection literals) { + final LNGIntVector clauseVec = new LNGIntVector(literals.size()); + for (final Literal lit : literals) { + clauseVec.push(solverLiteral(lit)); + } + return clauseVec; + } + + private int solverLiteral(final Literal lit) { + int index = this.solver.underlyingSolver().idxForName(lit.name()); + if (index == -1) { + index = this.solver.underlyingSolver().newVar(!this.solver.initialPhase(), true); + this.solver.underlyingSolver().addName(lit.name(), index); + } + return lit.phase() ? index * 2 : (index * 2) ^ 1; + } + + private int newSolverVariable() { + final int index = this.solver.underlyingSolver().newVar(!this.solver.initialPhase(), true); + final String name = FormulaFactory.CNF_PREFIX + "MINISAT_" + index; + this.solver.underlyingSolver().addName(name, index); + return index * 2; + } +} diff --git a/src/test/java/org/logicng/solvers/sat/ConfigurationsTest.java b/src/test/java/org/logicng/solvers/sat/ConfigurationsTest.java index 8b599bab..d50606df 100644 --- a/src/test/java/org/logicng/solvers/sat/ConfigurationsTest.java +++ b/src/test/java/org/logicng/solvers/sat/ConfigurationsTest.java @@ -36,7 +36,7 @@ /** * Unit tests for the solver configurations. - * @version 1.3 + * @version 1.6 * @since 1.0 */ public class ConfigurationsTest { @@ -69,6 +69,8 @@ public void testMiniSatConfigToString() { "incremental=false%n" + "initialPhase=true%n" + "proofGeneration=false%n" + + "cnfMethod=FACTORY_CNF%n" + + "auxiliaryVariablesInModels=true%n" + "}%n"); Assert.assertEquals(expected, config.toString()); } diff --git a/src/test/java/org/logicng/solvers/sat/SATTest.java b/src/test/java/org/logicng/solvers/sat/SATTest.java index e3ff52eb..1db8935e 100644 --- a/src/test/java/org/logicng/solvers/sat/SATTest.java +++ b/src/test/java/org/logicng/solvers/sat/SATTest.java @@ -28,6 +28,13 @@ package org.logicng.solvers.sat; +import static org.assertj.core.api.Assertions.assertThat; +import static org.logicng.datastructures.Tristate.FALSE; +import static org.logicng.datastructures.Tristate.TRUE; +import static org.logicng.datastructures.Tristate.UNDEF; +import static org.logicng.solvers.sat.MiniSatConfig.ClauseMinimization.BASIC; +import static org.logicng.solvers.sat.MiniSatConfig.ClauseMinimization.NONE; + import org.junit.Assert; import org.junit.Test; import org.logicng.collections.ImmutableFormulaList; @@ -55,7 +62,6 @@ import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.PrintStream; @@ -68,16 +74,9 @@ import java.util.SortedSet; import java.util.TreeSet; -import static org.assertj.core.api.Assertions.assertThat; -import static org.logicng.datastructures.Tristate.FALSE; -import static org.logicng.datastructures.Tristate.TRUE; -import static org.logicng.datastructures.Tristate.UNDEF; -import static org.logicng.solvers.sat.MiniSatConfig.ClauseMinimization.BASIC; -import static org.logicng.solvers.sat.MiniSatConfig.ClauseMinimization.NONE; - /** * Unit tests for the SAT solvers. - * @version 1.3 + * @version 1.6 * @since 1.0 */ public class SATTest { @@ -90,28 +89,30 @@ public class SATTest { public SATTest() { this.f = new FormulaFactory(); - this.pg = new PigeonHoleGenerator(f); - this.parser = new PropositionalParser(f); - this.solvers = new SATSolver[8]; - this.solvers[0] = MiniSat.miniSat(f, new MiniSatConfig.Builder().incremental(true).build()); - this.solvers[1] = MiniSat.miniSat(f, new MiniSatConfig.Builder().incremental(false).build()); - this.solvers[2] = MiniSat.glucose(f, new MiniSatConfig.Builder().incremental(false).build(), + this.pg = new PigeonHoleGenerator(this.f); + this.parser = new PropositionalParser(this.f); + this.solvers = new SATSolver[9]; + this.solvers[0] = MiniSat.miniSat(this.f, new MiniSatConfig.Builder().incremental(true).build()); + this.solvers[1] = MiniSat.miniSat(this.f, new MiniSatConfig.Builder().incremental(false).build()); + this.solvers[2] = MiniSat.glucose(this.f, new MiniSatConfig.Builder().incremental(false).build(), new GlucoseConfig.Builder().build()); - this.solvers[3] = MiniSat.miniCard(f, new MiniSatConfig.Builder().incremental(true).build()); - this.solvers[4] = MiniSat.miniCard(f, new MiniSatConfig.Builder().incremental(false).build()); - this.solvers[5] = CleaneLing.minimalistic(f); - this.solvers[6] = CleaneLing.full(f, new CleaneLingConfig.Builder().plain(true).glueUpdate(true).gluered(true).build()); - this.solvers[7] = CleaneLing.full(f); - - this.testStrings = new String[8]; + this.solvers[3] = MiniSat.miniCard(this.f, new MiniSatConfig.Builder().incremental(true).build()); + this.solvers[4] = MiniSat.miniCard(this.f, new MiniSatConfig.Builder().incremental(false).build()); + this.solvers[5] = MiniSat.miniSat(this.f, new MiniSatConfig.Builder().cnfMethod(MiniSatConfig.CNFMethod.PG_ON_SOLVER).build()); + this.solvers[6] = CleaneLing.minimalistic(this.f); + this.solvers[7] = CleaneLing.full(this.f, new CleaneLingConfig.Builder().plain(true).glueUpdate(true).gluered(true).build()); + this.solvers[8] = CleaneLing.full(this.f); + + this.testStrings = new String[9]; this.testStrings[0] = "MiniSat{result=UNDEF, incremental=true}"; this.testStrings[1] = "MiniSat{result=UNDEF, incremental=false}"; this.testStrings[2] = "MiniSat{result=UNDEF, incremental=false}"; this.testStrings[3] = "MiniSat{result=UNDEF, incremental=true}"; this.testStrings[4] = "MiniSat{result=UNDEF, incremental=false}"; - this.testStrings[5] = "CleaneLing{result=UNDEF, idx2name={}}"; + this.testStrings[5] = "MiniSat{result=UNDEF, incremental=true}"; this.testStrings[6] = "CleaneLing{result=UNDEF, idx2name={}}"; this.testStrings[7] = "CleaneLing{result=UNDEF, idx2name={}}"; + this.testStrings[8] = "CleaneLing{result=UNDEF, idx2name={}}"; } @Test @@ -171,14 +172,14 @@ public void testAnd1() { @Test public void testAnd2() { for (final SATSolver s : this.solvers) { - final StandardProposition prop = new StandardProposition(f.and(f.literal("a", true), f.literal("b", false), f.literal("c", true), f.literal("d", false))); + final StandardProposition prop = new StandardProposition(this.f.and(this.f.literal("a", true), this.f.literal("b", false), this.f.literal("c", true), this.f.literal("d", false))); s.add(prop); Assert.assertEquals(TRUE, s.sat()); Assert.assertEquals(4, s.model().size()); - Assert.assertTrue(s.model().evaluateLit(f.variable("a"))); - Assert.assertFalse(s.model().evaluateLit(f.variable("b"))); - Assert.assertTrue(s.model().evaluateLit(f.variable("c"))); - Assert.assertFalse(s.model().evaluateLit(f.variable("d"))); + Assert.assertTrue(s.model().evaluateLit(this.f.variable("a"))); + Assert.assertFalse(s.model().evaluateLit(this.f.variable("b"))); + Assert.assertTrue(s.model().evaluateLit(this.f.variable("c"))); + Assert.assertFalse(s.model().evaluateLit(this.f.variable("d"))); s.reset(); } } @@ -187,10 +188,10 @@ public void testAnd2() { public void testAnd3() { for (final SATSolver s : this.solvers) { final List formulas = new ArrayList<>(3); - formulas.add(f.literal("a", true)); - formulas.add(f.literal("b", false)); - formulas.add(f.literal("a", false)); - formulas.add(f.literal("d", false)); + formulas.add(this.f.literal("a", true)); + formulas.add(this.f.literal("b", false)); + formulas.add(this.f.literal("a", false)); + formulas.add(this.f.literal("d", false)); s.add(formulas); Assert.assertEquals(FALSE, s.sat()); s.reset(); @@ -200,13 +201,13 @@ public void testAnd3() { @Test public void testFormula1() throws ParserException { for (final SATSolver s : this.solvers) { - s.add(parser.parse("(x => y) & (~x => y) & (y => z) & (z => ~x)")); + s.add(this.parser.parse("(x => y) & (~x => y) & (y => z) & (z => ~x)")); Assert.assertEquals(TRUE, s.sat()); Assert.assertEquals(3, s.model().size()); - Assert.assertFalse(s.model().evaluateLit(f.variable("x"))); - Assert.assertTrue(s.model().evaluateLit(f.variable("y"))); - Assert.assertTrue(s.model().evaluateLit(f.variable("z"))); - s.add(f.variable("x")); + Assert.assertFalse(s.model().evaluateLit(this.f.variable("x"))); + Assert.assertTrue(s.model().evaluateLit(this.f.variable("y"))); + Assert.assertTrue(s.model().evaluateLit(this.f.variable("z"))); + s.add(this.f.variable("x")); Assert.assertEquals(FALSE, s.sat()); s.reset(); } @@ -216,14 +217,14 @@ public void testFormula1() throws ParserException { public void testFormula2() throws ParserException { for (int i = 0; i < this.solvers.length - 1; i++) { final SATSolver s = this.solvers[i]; - s.add(parser.parse("(x => y) & (~x => y) & (y => z) & (z => ~x)")); + s.add(this.parser.parse("(x => y) & (~x => y) & (y => z) & (z => ~x)")); final List models = s.enumerateAllModels(); Assert.assertEquals(1, models.size()); Assert.assertEquals(3, models.get(0).size()); - Assert.assertFalse(models.get(0).evaluateLit(f.variable("x"))); - Assert.assertTrue(models.get(0).evaluateLit(f.variable("y"))); - Assert.assertTrue(models.get(0).evaluateLit(f.variable("z"))); - s.add(f.variable("x")); + Assert.assertFalse(models.get(0).evaluateLit(this.f.variable("x"))); + Assert.assertTrue(models.get(0).evaluateLit(this.f.variable("y"))); + Assert.assertTrue(models.get(0).evaluateLit(this.f.variable("z"))); + s.add(this.f.variable("x")); Assert.assertEquals(FALSE, s.sat()); s.reset(); } @@ -234,27 +235,29 @@ public void testCC1() { for (int i = 0; i < this.solvers.length - 1; i++) { final SATSolver s = this.solvers[i]; final Variable[] lits = new Variable[100]; - for (int j = 0; j < lits.length; j++) - lits[j] = f.variable("x" + j); - s.add(f.exo(lits)); + for (int j = 0; j < lits.length; j++) { + lits[j] = this.f.variable("x" + j); + } + s.add(this.f.exo(lits)); final List models = s.enumerateAllModels(lits); Assert.assertEquals(100, models.size()); - for (final Assignment m : models) + for (final Assignment m : models) { Assert.assertEquals(1, m.positiveLiterals().size()); + } s.reset(); } } @Test public void testPBC() { - for (SATSolver s : this.solvers) { - List lits = new ArrayList<>(); - List coeffs = new ArrayList<>(); + for (final SATSolver s : this.solvers) { + final List lits = new ArrayList<>(); + final List coeffs = new ArrayList<>(); for (int i = 0; i < 5; i++) { - lits.add(f.literal("x" + i, i % 2 == 0)); + lits.add(this.f.literal("x" + i, i % 2 == 0)); coeffs.add(i + 1); } - s.add(f.pbc(CType.GE, 10, lits, coeffs)); + s.add(this.f.pbc(CType.GE, 10, lits, coeffs)); Assert.assertEquals(Tristate.TRUE, s.sat()); s.reset(); } @@ -262,15 +265,15 @@ public void testPBC() { @Test public void testPartialModel() { - for (SATSolver s : this.solvers) { + for (final SATSolver s : this.solvers) { s.add(F.A); s.add(F.B); s.add(F.C); - Variable[] relevantVars = new Variable[2]; + final Variable[] relevantVars = new Variable[2]; relevantVars[0] = F.A; relevantVars[1] = F.B; Assert.assertEquals(Tristate.TRUE, s.sat()); - Assignment relModel = s.model(relevantVars); + final Assignment relModel = s.model(relevantVars); Assert.assertTrue(relModel.negativeLiterals().isEmpty()); Assert.assertFalse(relModel.literals().contains(F.C)); s.reset(); @@ -279,22 +282,22 @@ public void testPartialModel() { @Test public void testModelEnumerationHandler() { - for (SATSolver s : solvers) { + for (final SATSolver s : this.solvers) { s.add(F.IMP3); try { - List models = s.enumerateAllModels(new ModelEnumerationHandler() { + final List models = s.enumerateAllModels(new ModelEnumerationHandler() { @Override - public boolean foundModel(Assignment assignment) { + public boolean foundModel(final Assignment assignment) { return !assignment.negativeLiterals().isEmpty(); } }); Assert.assertFalse(models.isEmpty()); Assert.assertTrue(models.get(models.size() - 1).negativeLiterals().isEmpty()); models.remove(models.size() - 1); - for (Assignment model : models) { + for (final Assignment model : models) { Assert.assertFalse(model.negativeLiterals().isEmpty()); } - } catch (Exception e) { + } catch (final Exception e) { Assert.assertTrue(e instanceof UnsupportedOperationException); } @@ -304,47 +307,47 @@ public boolean foundModel(Assignment assignment) { @Test public void testWithRelaxation() throws ParserException { - PropositionalParser parser = new PropositionalParser(f); - Formula one = parser.parse("a & b & (c | ~d)"); - Formula two = parser.parse("~a | ~c"); + final PropositionalParser parser = new PropositionalParser(this.f); + final Formula one = parser.parse("a & b & (c | ~d)"); + final Formula two = parser.parse("~a | ~c"); - for (SATSolver s : this.solvers) { + for (final SATSolver s : this.solvers) { s.add(one); - s.addWithRelaxation(f.variable("d"), two); + s.addWithRelaxation(this.f.variable("d"), two); Assert.assertEquals(Tristate.TRUE, s.sat()); try { Assert.assertEquals(2, s.enumerateAllModels().size()); - } catch (Exception e) { + } catch (final Exception e) { Assert.assertTrue(e instanceof UnsupportedOperationException); } s.reset(); s.add(one); - s.addWithRelaxation(f.variable("d"), new StandardProposition(two)); + s.addWithRelaxation(this.f.variable("d"), new StandardProposition(two)); Assert.assertEquals(Tristate.TRUE, s.sat()); try { Assert.assertEquals(2, s.enumerateAllModels().size()); - } catch (Exception e) { + } catch (final Exception e) { Assert.assertTrue(e instanceof UnsupportedOperationException); } s.reset(); s.add(one); - s.addWithRelaxation(f.variable("d"), new ImmutableFormulaList(two)); + s.addWithRelaxation(this.f.variable("d"), new ImmutableFormulaList(two)); Assert.assertEquals(Tristate.TRUE, s.sat()); try { Assert.assertEquals(2, s.enumerateAllModels().size()); - } catch (Exception e) { + } catch (final Exception e) { Assert.assertTrue(e instanceof UnsupportedOperationException); } s.reset(); s.add(one); - s.addWithRelaxation(f.variable("d"), Arrays.asList(two, f.verum())); + s.addWithRelaxation(this.f.variable("d"), Arrays.asList(two, this.f.verum())); Assert.assertEquals(Tristate.TRUE, s.sat()); try { Assert.assertEquals(2, s.enumerateAllModels().size()); - } catch (Exception e) { + } catch (final Exception e) { Assert.assertTrue(e instanceof UnsupportedOperationException); } s.reset(); @@ -353,18 +356,19 @@ public void testWithRelaxation() throws ParserException { @Test(expected = UnsupportedOperationException.class) public void testIllegalEnumeration() { - final SATSolver s = this.solvers[7]; + final SATSolver s = this.solvers[8]; final Variable[] lits = new Variable[100]; - for (int j = 0; j < lits.length; j++) - lits[j] = f.variable("x" + j); - s.add(f.exo(lits)); + for (int j = 0; j < lits.length; j++) { + lits[j] = this.f.variable("x" + j); + } + s.add(this.f.exo(lits)); s.enumerateAllModels(lits); } @Test public void testPigeonHole1() { for (final SATSolver s : this.solvers) { - s.add(pg.generate(1)); + s.add(this.pg.generate(1)); Assert.assertEquals(FALSE, s.sat()); Assert.assertNull(s.model()); s.reset(); @@ -374,7 +378,7 @@ public void testPigeonHole1() { @Test public void testPigeonHole2() { for (final SATSolver s : this.solvers) { - s.add(pg.generate(2)); + s.add(this.pg.generate(2)); Assert.assertEquals(FALSE, s.sat()); Assert.assertEquals(FALSE, s.sat()); Assert.assertNull(s.model()); @@ -385,7 +389,7 @@ public void testPigeonHole2() { @Test public void testPigeonHole3() { for (final SATSolver s : this.solvers) { - s.add(pg.generate(3)); + s.add(this.pg.generate(3)); Assert.assertEquals(FALSE, s.sat()); Assert.assertNull(s.model()); s.reset(); @@ -395,7 +399,7 @@ public void testPigeonHole3() { @Test public void testPigeonHole4() { for (final SATSolver s : this.solvers) { - s.add(pg.generate(4)); + s.add(this.pg.generate(4)); Assert.assertEquals(FALSE, s.sat()); Assert.assertNull(s.model()); s.reset(); @@ -405,7 +409,7 @@ public void testPigeonHole4() { @Test public void testPigeonHole5() { for (final SATSolver s : this.solvers) { - s.add(pg.generate(5)); + s.add(this.pg.generate(5)); Assert.assertEquals(FALSE, s.sat()); Assert.assertNull(s.model()); s.reset(); @@ -415,7 +419,7 @@ public void testPigeonHole5() { @Test public void testPigeonHole6() { for (final SATSolver s : this.solvers) { - s.add(pg.generate(6)); + s.add(this.pg.generate(6)); Assert.assertEquals(FALSE, s.sat()); Assert.assertNull(s.model()); s.reset(); @@ -425,7 +429,7 @@ public void testPigeonHole6() { @Test public void testPigeonHole7() { for (final SATSolver s : this.solvers) { - s.add(pg.generate(7)); + s.add(this.pg.generate(7)); Assert.assertEquals(FALSE, s.sat()); Assert.assertNull(s.model()); s.reset(); @@ -435,14 +439,14 @@ public void testPigeonHole7() { @Test public void testDifferentClauseMinimizations() { final SATSolver[] moreSolvers = new SATSolver[6]; - moreSolvers[0] = MiniSat.miniSat(f, new MiniSatConfig.Builder().clMinimization(NONE).build()); - moreSolvers[1] = MiniSat.miniSat(f, new MiniSatConfig.Builder().clMinimization(BASIC).build()); - moreSolvers[2] = MiniSat.glucose(f, new MiniSatConfig.Builder().clMinimization(NONE).build(), new GlucoseConfig.Builder().build()); - moreSolvers[3] = MiniSat.glucose(f, new MiniSatConfig.Builder().clMinimization(BASIC).build(), new GlucoseConfig.Builder().build()); - moreSolvers[4] = MiniSat.miniCard(f, new MiniSatConfig.Builder().clMinimization(NONE).build()); - moreSolvers[5] = MiniSat.miniCard(f, new MiniSatConfig.Builder().clMinimization(BASIC).build()); + moreSolvers[0] = MiniSat.miniSat(this.f, new MiniSatConfig.Builder().clMinimization(NONE).build()); + moreSolvers[1] = MiniSat.miniSat(this.f, new MiniSatConfig.Builder().clMinimization(BASIC).build()); + moreSolvers[2] = MiniSat.glucose(this.f, new MiniSatConfig.Builder().clMinimization(NONE).build(), new GlucoseConfig.Builder().build()); + moreSolvers[3] = MiniSat.glucose(this.f, new MiniSatConfig.Builder().clMinimization(BASIC).build(), new GlucoseConfig.Builder().build()); + moreSolvers[4] = MiniSat.miniCard(this.f, new MiniSatConfig.Builder().clMinimization(NONE).build()); + moreSolvers[5] = MiniSat.miniCard(this.f, new MiniSatConfig.Builder().clMinimization(BASIC).build()); for (final SATSolver s : moreSolvers) { - s.add(pg.generate(7)); + s.add(this.pg.generate(7)); Assert.assertEquals(FALSE, s.sat()); Assert.assertNull(s.model()); } @@ -451,7 +455,7 @@ public void testDifferentClauseMinimizations() { @Test public void testTimeoutSATHandler() { for (final SATSolver s : this.solvers) { - s.add(pg.generate(10)); + s.add(this.pg.generate(10)); final Tristate result = s.sat(new TimeoutSATHandler(1000)); Assert.assertEquals(UNDEF, result); s.reset(); @@ -474,7 +478,7 @@ public void testDimacsFiles() throws IOException { final String fileName = file.getName(); if (fileName.endsWith(".cnf")) { readCNF(solver, file); - boolean res = solver.sat() == TRUE; + final boolean res = solver.sat() == TRUE; Assert.assertEquals(expectedResults.get(fileName), res); } } @@ -487,8 +491,9 @@ private void readCNF(final SATSolver solver, final File file) throws IOException final BufferedReader reader = new BufferedReader(new FileReader(file)); while (reader.ready()) { final String line = reader.readLine(); - if (line.startsWith("p cnf")) + if (line.startsWith("p cnf")) { break; + } } String[] tokens; final List literals = new ArrayList<>(); @@ -499,13 +504,14 @@ private void readCNF(final SATSolver solver, final File file) throws IOException literals.clear(); for (int i = 0; i < tokens.length - 1; i++) { if (!tokens[i].isEmpty()) { - int parsedLit = Integer.parseInt(tokens[i]); - String var = "v" + Math.abs(parsedLit); - literals.add(parsedLit > 0 ? f.literal(var, true) : f.literal(var, false)); + final int parsedLit = Integer.parseInt(tokens[i]); + final String var = "v" + Math.abs(parsedLit); + literals.add(parsedLit > 0 ? this.f.literal(var, true) : this.f.literal(var, false)); } } - if (!literals.isEmpty()) - solver.add(f.or(literals)); + if (!literals.isEmpty()) { + solver.add(this.f.or(literals)); + } } } } @@ -513,16 +519,16 @@ private void readCNF(final SATSolver solver, final File file) throws IOException @Test public void testPigeonHoleWithReset() { for (final SATSolver s : this.solvers) { - s.add(pg.generate(4)); + s.add(this.pg.generate(4)); Assert.assertEquals(FALSE, s.sat()); s.reset(); - s.add(pg.generate(5)); + s.add(this.pg.generate(5)); Assert.assertEquals(FALSE, s.sat()); s.reset(); - s.add(pg.generate(6)); + s.add(this.pg.generate(6)); Assert.assertEquals(FALSE, s.sat()); s.reset(); - s.add(pg.generate(7)); + s.add(this.pg.generate(7)); Assert.assertEquals(FALSE, s.sat()); s.reset(); } @@ -535,18 +541,18 @@ public void testModelEnumeration() { final SortedSet lits = new TreeSet<>(); final SortedSet firstFive = new TreeSet<>(); for (int j = 0; j < 20; j++) { - Variable lit = f.variable("x" + j); + final Variable lit = this.f.variable("x" + j); lits.add(lit); if (j < 5) { firstFive.add(lit); } } - s.add(f.cc(CType.GE, 1, lits)); + s.add(this.f.cc(CType.GE, 1, lits)); - List models = s.enumerateAllModels(firstFive, lits); + final List models = s.enumerateAllModels(firstFive, lits); Assert.assertEquals(32, models.size()); - for (Assignment model : models) { - for (Variable lit : lits) { + for (final Assignment model : models) { + for (final Variable lit : lits) { Assert.assertTrue(model.positiveLiterals().contains(lit) || model.negativeVariables().contains(lit)); } } @@ -561,19 +567,19 @@ public void testModelEnumerationWithHandler() { final SortedSet lits = new TreeSet<>(); final SortedSet firstFive = new TreeSet<>(); for (int j = 0; j < 20; j++) { - Variable lit = f.variable("x" + j); + final Variable lit = this.f.variable("x" + j); lits.add(lit); if (j < 5) { firstFive.add(lit); } } - s.add(f.cc(CType.GE, 1, lits)); + s.add(this.f.cc(CType.GE, 1, lits)); - NumberOfModelsHandler handler = new NumberOfModelsHandler(29); - List modelsWithHandler = s.enumerateAllModels(firstFive, lits, handler); + final NumberOfModelsHandler handler = new NumberOfModelsHandler(29); + final List modelsWithHandler = s.enumerateAllModels(firstFive, lits, handler); Assert.assertEquals(29, modelsWithHandler.size()); - for (Assignment model : modelsWithHandler) { - for (Variable lit : lits) { + for (final Assignment model : modelsWithHandler) { + for (final Variable lit : lits) { Assert.assertTrue(model.positiveLiterals().contains(lit) || model.negativeVariables().contains(lit)); } } @@ -585,8 +591,8 @@ public void testModelEnumerationWithHandler() { public void testEmptyEnumeration() { for (int i = 0; i < this.solvers.length - 1; i++) { final SATSolver s = this.solvers[i]; - s.add(f.falsum()); - List models = s.enumerateAllModels(); + s.add(this.f.falsum()); + final List models = s.enumerateAllModels(); Assert.assertTrue(models.isEmpty()); s.reset(); @@ -598,38 +604,43 @@ public void testNumberOfModelHandler() { for (int i = 0; i < this.solvers.length - 1; i++) { final SATSolver s = this.solvers[i]; final Variable[] lits = new Variable[100]; - for (int j = 0; j < lits.length; j++) - lits[j] = f.variable("x" + j); - s.add(f.exo(lits)); + for (int j = 0; j < lits.length; j++) { + lits[j] = this.f.variable("x" + j); + } + s.add(this.f.exo(lits)); NumberOfModelsHandler handler = new NumberOfModelsHandler(100); List models = s.enumerateAllModels(lits, handler); Assert.assertEquals(100, models.size()); - for (final Assignment m : models) + for (final Assignment m : models) { Assert.assertEquals(1, m.positiveLiterals().size()); + } s.reset(); - s.add(f.exo(lits)); + s.add(this.f.exo(lits)); handler = new NumberOfModelsHandler(200); models = s.enumerateAllModels(lits, handler); Assert.assertEquals(100, models.size()); - for (final Assignment m : models) + for (final Assignment m : models) { Assert.assertEquals(1, m.positiveLiterals().size()); + } s.reset(); - s.add(f.exo(lits)); + s.add(this.f.exo(lits)); handler = new NumberOfModelsHandler(50); models = s.enumerateAllModels(lits, handler); Assert.assertEquals(50, models.size()); - for (final Assignment m : models) + for (final Assignment m : models) { Assert.assertEquals(1, m.positiveLiterals().size()); + } s.reset(); - s.add(f.exo(lits)); + s.add(this.f.exo(lits)); handler = new NumberOfModelsHandler(1); models = s.enumerateAllModels(lits, handler); Assert.assertEquals(1, models.size()); - for (final Assignment m : models) + for (final Assignment m : models) { Assert.assertEquals(1, m.positiveLiterals().size()); + } s.reset(); } } @@ -641,54 +652,54 @@ public void testIllegalHandler() { @Test(expected = IllegalArgumentException.class) public void testAddNonCCAsCC() { - MiniSat solver = MiniSat.miniSat(f); + final MiniSat solver = MiniSat.miniSat(this.f); solver.addIncrementalCC((PBConstraint) F.PBC3); } @Test(expected = IllegalStateException.class) public void testModelBeforeSolving() { - MiniSat solver = MiniSat.miniSat(f); + final MiniSat solver = MiniSat.miniSat(this.f); solver.model(); } @Test(expected = IllegalArgumentException.class) public void testCLAddNonCCAsCC() { - CleaneLing solver = CleaneLing.minimalistic(f, new CleaneLingConfig.Builder().gluered(true).build()); + final CleaneLing solver = CleaneLing.minimalistic(this.f, new CleaneLingConfig.Builder().gluered(true).build()); solver.addIncrementalCC((PBConstraint) F.PBC3); } @Test(expected = IllegalStateException.class) public void testCLModelBeforeSolving() { - CleaneLing solver = CleaneLing.minimalistic(f); + final CleaneLing solver = CleaneLing.minimalistic(this.f); solver.model(); } @Test(expected = UnsupportedOperationException.class) public void testCLSatWithLit() { - CleaneLing solver = CleaneLing.minimalistic(f); + final CleaneLing solver = CleaneLing.minimalistic(this.f); solver.add(F.AND1); solver.sat(new TimeoutSATHandler(10000), F.A); } @Test(expected = UnsupportedOperationException.class) public void testCLSaveState() { - CleaneLing solver = CleaneLing.minimalistic(f); + final CleaneLing solver = CleaneLing.minimalistic(this.f); solver.add(F.AND1); solver.saveState(); } @Test(expected = UnsupportedOperationException.class) public void testCLLoadState() { - CleaneLing solver = CleaneLing.minimalistic(f); + final CleaneLing solver = CleaneLing.minimalistic(this.f); solver.add(F.AND1); solver.loadState(new SolverState(27, new int[3])); } @Test(expected = UnsupportedOperationException.class) public void testCLSatWithLitCollection() { - CleaneLing solver = CleaneLing.minimalistic(f); + final CleaneLing solver = CleaneLing.minimalistic(this.f); solver.add(F.AND1); - List lits = new ArrayList<>(); + final List lits = new ArrayList<>(); lits.add(F.A); lits.add(F.B); solver.sat(new TimeoutSATHandler(10000), lits); @@ -696,7 +707,7 @@ public void testCLSatWithLitCollection() { @Test(expected = UnsupportedOperationException.class) public void testCLEnumerateWithWrongConfig() { - CleaneLing solver = CleaneLing.full(f, new CleaneLingConfig.Builder().plain(false).build()); + final CleaneLing solver = CleaneLing.full(this.f, new CleaneLingConfig.Builder().plain(false).build()); solver.add(F.AND1); solver.enumerateAllModels(); } @@ -710,11 +721,11 @@ public void testToString() { @Test public void testPrintMinimalisticCleaneLing() { - CleaneLingMinimalisticSolver clms = new CleaneLingMinimalisticSolver(new CleaneLingConfig.Builder().build()); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - PrintStream ps = new PrintStream(baos); + final CleaneLingMinimalisticSolver clms = new CleaneLingMinimalisticSolver(new CleaneLingConfig.Builder().build()); + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final PrintStream ps = new PrintStream(baos); clms.printSolverState(ps); - String expected = String.format("level=0%n" + + final String expected = String.format("level=0%n" + "next=0%n" + "ignore=null%n" + "empty=null%n" + @@ -731,36 +742,36 @@ public void testPrintMinimalisticCleaneLing() { @Test public void testKnownVariables() throws ParserException { - final PropositionalParser parser = new PropositionalParser(f); + final PropositionalParser parser = new PropositionalParser(this.f); final Formula phi = parser.parse("x1 & x2 & x3 & (x4 | ~x5)"); - final SATSolver minisat = MiniSat.miniSat(f); - final SATSolver minicard = MiniSat.miniCard(f); - final SATSolver cleaneling = CleaneLing.minimalistic(f); + final SATSolver minisat = MiniSat.miniSat(this.f); + final SATSolver minicard = MiniSat.miniCard(this.f); + final SATSolver cleaneling = CleaneLing.minimalistic(this.f); minisat.add(phi); minicard.add(phi); cleaneling.add(phi); final SortedSet expected = new TreeSet<>(Arrays.asList( - f.variable("x1"), - f.variable("x2"), - f.variable("x3"), - f.variable("x4"), - f.variable("x5"))); + this.f.variable("x1"), + this.f.variable("x2"), + this.f.variable("x3"), + this.f.variable("x4"), + this.f.variable("x5"))); Assert.assertEquals(expected, minisat.knownVariables()); Assert.assertEquals(expected, minicard.knownVariables()); Assert.assertEquals(expected, cleaneling.knownVariables()); final SolverState state = minisat.saveState(); final SolverState stateCard = minicard.saveState(); - minisat.add(f.variable("x6")); - minicard.add(f.variable("x6")); - cleaneling.add(f.variable("x6")); + minisat.add(this.f.variable("x6")); + minicard.add(this.f.variable("x6")); + cleaneling.add(this.f.variable("x6")); final SortedSet expected2 = new TreeSet<>(Arrays.asList( - f.variable("x1"), - f.variable("x2"), - f.variable("x3"), - f.variable("x4"), - f.variable("x5"), - f.variable("x6"))); + this.f.variable("x1"), + this.f.variable("x2"), + this.f.variable("x3"), + this.f.variable("x4"), + this.f.variable("x5"), + this.f.variable("x6"))); Assert.assertEquals(expected2, minisat.knownVariables()); Assert.assertEquals(expected2, minicard.knownVariables()); Assert.assertEquals(expected2, cleaneling.knownVariables()); @@ -774,30 +785,30 @@ public void testKnownVariables() throws ParserException { @Test public void testAddWithoutUnknown() throws ParserException { - final PropositionalParser parser = new PropositionalParser(f); + final PropositionalParser parser = new PropositionalParser(this.f); final Formula phi = parser.parse("x1 & (~x2 | x3) & (x4 | ~x5)"); final SortedSet phiVars = new TreeSet<>(Arrays.asList( - f.variable("x1"), - f.variable("x2"), - f.variable("x3"), - f.variable("x4"), - f.variable("x5"))); + this.f.variable("x1"), + this.f.variable("x2"), + this.f.variable("x3"), + this.f.variable("x4"), + this.f.variable("x5"))); final Formula add1 = parser.parse("x1 | x6 | x7"); final Formula add2 = parser.parse("~x1 | ~x6 | x8"); final Formula add3 = parser.parse("x2 & ~x3 | x7"); final Formula add4 = parser.parse("x8 | x9"); - final SATSolver minisat = MiniSat.miniSat(f); - final SATSolver minicard = MiniSat.miniCard(f); - final SATSolver cleaneling = CleaneLing.minimalistic(f); + final SATSolver minisat = MiniSat.miniSat(this.f); + final SATSolver minicard = MiniSat.miniCard(this.f); + final SATSolver cleaneling = CleaneLing.minimalistic(this.f); final SATSolver[] solvers = new SATSolver[]{minisat, minicard, cleaneling}; for (final SATSolver solver : solvers) { solver.add(phi); solver.addWithoutUnknown(add1); Assert.assertEquals(TRUE, solver.sat()); - Assert.assertEquals(phiVars, solver.model().formula(f).variables()); + Assert.assertEquals(phiVars, solver.model().formula(this.f).variables()); solver.addWithoutUnknown(add2); Assert.assertEquals(TRUE, solver.sat()); - Assert.assertEquals(phiVars, solver.model().formula(f).variables()); + Assert.assertEquals(phiVars, solver.model().formula(this.f).variables()); if (solver instanceof MiniSat) { final SolverState state = solver.saveState(); solver.addWithoutUnknown(add3); @@ -805,17 +816,17 @@ public void testAddWithoutUnknown() throws ParserException { solver.loadState(state); solver.add(add1); Assert.assertEquals(TRUE, solver.sat()); - Assert.assertTrue(solver.model().formula(f).variables().containsAll(Arrays.asList(f.variable("x6"), f.variable("x7")))); + Assert.assertTrue(solver.model().formula(this.f).variables().containsAll(Arrays.asList(this.f.variable("x6"), this.f.variable("x7")))); solver.loadState(state); solver.sat(); - Assert.assertEquals(phiVars, solver.model().formula(f).variables()); + Assert.assertEquals(phiVars, solver.model().formula(this.f).variables()); } else { solver.add(add1); Assert.assertEquals(TRUE, solver.sat()); - Assert.assertTrue(solver.model().formula(f).variables().containsAll(Arrays.asList(f.variable("x6"), f.variable("x7")))); - solver.add(f.variable("x7")); + Assert.assertTrue(solver.model().formula(this.f).variables().containsAll(Arrays.asList(this.f.variable("x6"), this.f.variable("x7")))); + solver.add(this.f.variable("x7")); Assert.assertEquals(TRUE, solver.sat()); - Assert.assertTrue(solver.model().formula(f).variables().containsAll(Arrays.asList(f.variable("x6"), f.variable("x7")))); + Assert.assertTrue(solver.model().formula(this.f).variables().containsAll(Arrays.asList(this.f.variable("x6"), this.f.variable("x7")))); solver.addWithoutUnknown(add4); Assert.assertEquals(FALSE, solver.sat()); } @@ -824,7 +835,7 @@ public void testAddWithoutUnknown() throws ParserException { @Test public void testUPZeroLiteralsUNSAT() throws ParserException { - Formula formula = parser.parse("a & (a => b) & (b => c) & (c => ~a)"); + final Formula formula = this.parser.parse("a & (a => b) & (b => c) & (c => ~a)"); for (final SATSolver solver : this.solvers) { solver.reset(); solver.add(formula); From 52d7b2e669998f9feedd7cb9cff636041ef4b0ac Mon Sep 17 00:00:00 2001 From: Christoph Zengler Date: Fri, 12 Jul 2019 11:33:37 +0200 Subject: [PATCH 02/16] include changes from 1.5.2 --- README.md | 8 +- .../java/org/logicng/backbones/Backbone.java | 281 ++++---- .../logicng/backbones/MiniSatBackbone.java | 630 +++++++++--------- .../java/org/logicng/solvers/MiniSat.java | 22 +- .../transformations/BackboneSimplifier.java | 28 +- .../org/logicng/backbones/BackboneTest.java | 200 +++--- .../org/logicng/solvers/sat/MiniSatTest.java | 34 +- 7 files changed, 622 insertions(+), 581 deletions(-) diff --git a/README.md b/README.md index 2fc4eeb2..c268f157 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![wercker status](https://app.wercker.com/status/24c4765f3a0d79520ad80a1e4c20cfa2/s/master "wercker status")](https://app.wercker.com/project/bykey/24c4765f3a0d79520ad80a1e4c20cfa2) [![Coverage Status](https://coveralls.io/repos/logic-ng/LogicNG/badge.svg?branch=master&service=github)](https://coveralls.io/github/logic-ng/LogicNG?branch=master) ![License](https://img.shields.io/badge/license-Apache%202-blue.svg) ![Version](https://img.shields.io/badge/version-1.5.1-ff69b4.svg) +[![wercker status](https://app.wercker.com/status/24c4765f3a0d79520ad80a1e4c20cfa2/s/master "wercker status")](https://app.wercker.com/project/bykey/24c4765f3a0d79520ad80a1e4c20cfa2) [![Coverage Status](https://coveralls.io/repos/logic-ng/LogicNG/badge.svg?branch=master&service=github)](https://coveralls.io/github/logic-ng/LogicNG?branch=master) ![License](https://img.shields.io/badge/license-Apache%202-blue.svg) ![Version](https://img.shields.io/badge/version-1.5.2-ff69b4.svg) logo @@ -19,7 +19,7 @@ LogicNG is released in the Maven Central Repository. To include it just add org.logicng logicng - 1.5.1 + 1.5.2 ``` to your Maven POM. @@ -63,6 +63,10 @@ The library is released under the Apache License and therefore is free to use in ## Changelog +### Version 1.5.2 (Release July 2019) +* Fixed caching behaviour when using a `sat()` call without assumptions after a call with assumptions +* Clarified behaviour of the `Backbone` object + ### Version 1.5.1 (Release May 2019) * Introduced a new `FormulaHelper` class for small utility methods on formulas * Added a new NNF predicate diff --git a/src/main/java/org/logicng/backbones/Backbone.java b/src/main/java/org/logicng/backbones/Backbone.java index a95fa84c..0e83e200 100644 --- a/src/main/java/org/logicng/backbones/Backbone.java +++ b/src/main/java/org/logicng/backbones/Backbone.java @@ -53,159 +53,152 @@ *
  • Optional variables: Variables that are neither in the positive nor in the negative backbone. * Therefore these variables can be assigned to true or false. * - * If only the positive or negative backbone is computed, the optional variables are {@code null}. - * Also the variable set which was not computed is {@code null}. You can use the methods - * {@link #hasPositiveBackboneResult()}, {@link #hasNegativeBackboneResult()}, and - * {@link #hasOptionalVariablesResult()} to check if a certain result set was computed and is present - * in the backbone object. - * @version 1.5.0 + * All variable sets which were not computed are empty. + * @version 1.5.2 * @since 1.5.0 */ public class Backbone { - private final SortedSet positiveBackbone; - private final SortedSet negativeBackbone; - private final SortedSet optionalVariables; - - /** - * Constructs a new backbone that contains the given backbone variables and the given optional variables. - * @param positiveBackbone positive backbone variables - * @param negativeBackbone negative backbone variables - * @param optionalVariables optional variables - */ - public Backbone(final SortedSet positiveBackbone, final SortedSet negativeBackbone, final SortedSet optionalVariables) { - this.positiveBackbone = positiveBackbone; - this.negativeBackbone = negativeBackbone; - this.optionalVariables = optionalVariables; + private final boolean sat; + private final SortedSet positiveBackbone; + private final SortedSet negativeBackbone; + private final SortedSet optionalVariables; + + /** + * Constructs a new backbone that contains the given backbone variables and the given optional variables. + * @param sat is the original formula satisfiable or not + * @param positiveBackbone positive backbone variables + * @param negativeBackbone negative backbone variables + * @param optionalVariables optional variables + */ + private Backbone(final boolean sat, final SortedSet positiveBackbone, final SortedSet negativeBackbone, + final SortedSet optionalVariables) { + this.sat = sat; + this.positiveBackbone = positiveBackbone == null ? new TreeSet() : positiveBackbone; + this.negativeBackbone = negativeBackbone == null ? new TreeSet() : negativeBackbone; + this.optionalVariables = optionalVariables == null ? new TreeSet() : optionalVariables; + } + + /** + * Returns a new backbone for a satisfiable formula. + * @param positiveBackbone positive backbone variables + * @param negativeBackbone negative backbone variables + * @param optionalVariables optional variables + * @return the backbone + */ + public static Backbone satBackbone(final SortedSet positiveBackbone, final SortedSet negativeBackbone, + final SortedSet optionalVariables) { + return new Backbone(true, positiveBackbone, negativeBackbone, optionalVariables); + } + + /** + * Returns a new empty backbone for an unsatisfiable formula. + * @return the backbone + */ + public static Backbone unsatBackbone() { + return new Backbone(false, null, null, null); + } + + /** + * Returns whether the original formula of this backbone was satisfiable or not. + * @return whether the original formula of this backbone was satisfiable or not + */ + public boolean isSat() { + return this.sat; + } + + /** + * Returns the positive variables of the backbone. + * @return the set of positive backbone variables + */ + public SortedSet getPositiveBackbone() { + return Collections.unmodifiableSortedSet(this.positiveBackbone); + } + + /** + * Returns the negative variables of the backbone. + * @return the set of negative backbone variables + */ + public SortedSet getNegativeBackbone() { + return Collections.unmodifiableSortedSet(this.negativeBackbone); + } + + /** + * Returns the variables of the formula that are optional, i.e. not in the positive or negative backbone. + * @return the set of non-backbone variables + */ + public SortedSet getOptionalVariables() { + return Collections.unmodifiableSortedSet(this.optionalVariables); + } + + /** + * Returns all literals of the backbone. Positive backbone variables have positive polarity, negative + * backbone variables have negative polarity. + * @return the set of both positive and negative backbone variables as literals + */ + public SortedSet getCompleteBackbone() { + final SortedSet completeBackbone = new TreeSet(this.positiveBackbone); + for (final Variable var : this.negativeBackbone) { + completeBackbone.add(var.negate()); } - - /** - * Tests whether the backbone has a positive backbone result. - * @return {@code true} if the backbone has a positive backbone result, {@code false} otherwise - */ - public boolean hasPositiveBackboneResult() { - return this.positiveBackbone != null; + return Collections.unmodifiableSortedSet(completeBackbone); + } + + /** + * Returns the positive and negative backbone as a conjunction of literals. + * @param f the formula factory needed for construction the backbone formula. + * @return the backbone formula + */ + public Formula toFormula(final FormulaFactory f) { + return this.sat ? f.and(this.getCompleteBackbone()) : f.falsum(); + } + + /** + * Returns the backbone as map from variables to tri-states. A positive variable is mapped to {@code Tristate.TRUE}, + * a negative variable to {@code Tristate.FALSE} and the optional variables to {@code Tristate.UNDEF}. + * @return the mapping of the backbone + */ + public SortedMap toMap() { + final SortedMap map = new TreeMap<>(); + for (final Variable var : this.positiveBackbone) { + map.put(var, Tristate.TRUE); } - - /** - * Tests whether the backbone has a negative backbone result. - * @return {@code true} if the backbone has a negative backbone result, {@code false} otherwise - */ - public boolean hasNegativeBackboneResult() { - return this.negativeBackbone != null; + for (final Variable var : this.negativeBackbone) { + map.put(var, Tristate.FALSE); } - - /** - * Tests whether the backbone has an optional variables result. - * @return {@code true} if the backbone has an optional variables result, {@code false} otherwise - */ - public boolean hasOptionalVariablesResult() { - return this.optionalVariables != null; + for (final Variable var : this.optionalVariables) { + map.put(var, Tristate.UNDEF); } + return Collections.unmodifiableSortedMap(map); + } - /** - * Returns the positive variables of the backbone. - * @return the set of positive backbone variables - */ - public SortedSet getPositiveBackbone() { - return hasPositiveBackboneResult() ? Collections.unmodifiableSortedSet(this.positiveBackbone) : null; + @Override + public boolean equals(final Object other) { + if (other == null) { + return false; } - - /** - * Returns the negative variables of the backbone. - * @return the set of negative backbone variables - */ - public SortedSet getNegativeBackbone() { - return hasNegativeBackboneResult() ? Collections.unmodifiableSortedSet(this.negativeBackbone) : null; + if (this == other) { + return true; } - - /** - * Returns the variables of the formula that are optional, i.e. not in the positive or negative backbone. - * @return the set of non-backbone variables - */ - public SortedSet getOptionalVariables() { - return hasOptionalVariablesResult() ? Collections.unmodifiableSortedSet(this.optionalVariables) : null; - } - - /** - * Returns all literals of the backbone. Positive backbone variables have positive polarity, negative - * backbone variables have negative polarity. - * @return the set of both positive and negative backbone variables as literals - */ - public SortedSet getCompleteBackbone() { - final SortedSet completeBackbone = new TreeSet<>(); - if (hasPositiveBackboneResult()) { - completeBackbone.addAll(this.positiveBackbone); - } - if (hasNegativeBackboneResult()) { - for (final Variable var : this.negativeBackbone) { - completeBackbone.add(var.negate()); - } - } - return Collections.unmodifiableSortedSet(completeBackbone); - } - - /** - * Returns the positive and negative backbone as a conjunction of literals. - * @param f the formula factory needed for construction the backbone formula. - * @return the backbone formula - */ - public Formula toFormula(final FormulaFactory f) { - return f.and(this.getCompleteBackbone()); - } - - /** - * Returns the backbone as map from variables to tri-states. A positive variable is mapped to {@code Tristate.TRUE}, - * a negative variable to {@code Tristate.FALSE} and the optional variables to {@code Tristate.UNDEF}. - * @return the mapping of the backbone - */ - public SortedMap toMap() { - final SortedMap map = new TreeMap<>(); - if (hasPositiveBackboneResult()) { - for (final Variable var : this.positiveBackbone) { - map.put(var, Tristate.TRUE); - } - } - if (hasNegativeBackboneResult()) { - for (final Variable var : this.negativeBackbone) { - map.put(var, Tristate.FALSE); - } - } - if (hasOptionalVariablesResult()) { - for (final Variable var : this.optionalVariables) { - map.put(var, Tristate.UNDEF); - } - } - return Collections.unmodifiableSortedMap(map); - } - - @Override - public boolean equals(final Object other) { - if (other == null) { - return false; - } - if (this == other) { - return true; - } - if (getClass() != other.getClass()) { - return false; - } - final Backbone backbone = (Backbone) other; - return Objects.equals(this.positiveBackbone, backbone.positiveBackbone) && - Objects.equals(this.negativeBackbone, backbone.negativeBackbone) && - Objects.equals(this.optionalVariables, backbone.optionalVariables); - } - - @Override - public int hashCode() { - return Objects.hash(this.positiveBackbone, this.negativeBackbone, this.optionalVariables); - } - - @Override - public String toString() { - return "Backbone{" + - "positiveBackbone=" + this.positiveBackbone + - ", negativeBackbone=" + this.negativeBackbone + - ", optionalVariables=" + this.optionalVariables + - '}'; + if (getClass() != other.getClass()) { + return false; } + final Backbone backbone = (Backbone) other; + return Objects.equals(this.positiveBackbone, backbone.positiveBackbone) && + Objects.equals(this.negativeBackbone, backbone.negativeBackbone) && + Objects.equals(this.optionalVariables, backbone.optionalVariables); + } + + @Override + public int hashCode() { + return Objects.hash(this.positiveBackbone, this.negativeBackbone, this.optionalVariables); + } + + @Override + public String toString() { + return "Backbone{" + + "positiveBackbone=" + this.positiveBackbone + + ", negativeBackbone=" + this.negativeBackbone + + ", optionalVariables=" + this.optionalVariables + + '}'; + } } diff --git a/src/main/java/org/logicng/backbones/MiniSatBackbone.java b/src/main/java/org/logicng/backbones/MiniSatBackbone.java index e4d419be..69c6f85f 100644 --- a/src/main/java/org/logicng/backbones/MiniSatBackbone.java +++ b/src/main/java/org/logicng/backbones/MiniSatBackbone.java @@ -60,362 +60,362 @@ */ public class MiniSatBackbone extends MiniSat2Solver { - private BackboneConfig config; + private BackboneConfig config; - /** - * Type of the backbone computation. - */ - private BackboneType type; + /** + * Type of the backbone computation. + */ + private BackboneType type; - /** - * Candidates to test for backbone. - *

    - * The integers are solver internal literal integers. - */ - private Stack candidates; + /** + * Candidates to test for backbone. + *

    + * The integers are solver internal literal integers. + */ + private Stack candidates; - /** - * Assumptions used to call the solver with, filled by identified backbone literals. - */ - private LNGIntVector assumptions; + /** + * Assumptions used to call the solver with, filled by identified backbone literals. + */ + private LNGIntVector assumptions; - /** - * Backbone map: integer literal -> Tristate. - *

      - *
    • {@link Tristate#TRUE} is a positive backbone variable - *
    • {@link Tristate#FALSE} is a negative backbone variable - *
    • {@link Tristate#UNDEF} is an optional variable - *
    - */ - private HashMap backboneMap; + /** + * Backbone map: integer literal -> Tristate. + *
      + *
    • {@link Tristate#TRUE} is a positive backbone variable + *
    • {@link Tristate#FALSE} is a negative backbone variable + *
    • {@link Tristate#UNDEF} is an optional variable + *
    + */ + private HashMap backboneMap; - /** - * Creates a new backbone solver using the given configuration. - * @param config configuration - */ - public MiniSatBackbone(final BackboneConfig config) { - this.config = config; - } + /** + * Creates a new backbone solver using the given configuration. + * @param config configuration + */ + public MiniSatBackbone(final BackboneConfig config) { + this.config = config; + } - /** - * Creates a new backbone solver using the default configuration. - */ - public MiniSatBackbone() { - this(new BackboneConfig.Builder().build()); - } + /** + * Creates a new backbone solver using the default configuration. + */ + public MiniSatBackbone() { + this(new BackboneConfig.Builder().build()); + } - /** - * Sets a new backbone configuration. - * @param newConfig the new {@link BackboneConfig} - */ - public void setConfig(final BackboneConfig newConfig) { - this.config = newConfig; - } + /** + * Sets a new backbone configuration. + * @param newConfig the new {@link BackboneConfig} + */ + public void setConfig(final BackboneConfig newConfig) { + this.config = newConfig; + } - /** - * Adds an arbitrary formula to the solver. - * @param formula the formula - */ - public void add(final Formula formula) { - final Formula cnf = formula.cnf(); - switch (cnf.type()) { - case TRUE: - break; - case FALSE: - case LITERAL: - case OR: - this.addClause(generateClauseVector(cnf), null); - break; - case AND: - for (final Formula op : cnf) { - this.addClause(generateClauseVector(op), null); - } - break; - default: - throw new IllegalStateException("Unexpected formula type in CNF: " + cnf.type()); + /** + * Adds an arbitrary formula to the solver. + * @param formula the formula + */ + public void add(final Formula formula) { + final Formula cnf = formula.cnf(); + switch (cnf.type()) { + case TRUE: + break; + case FALSE: + case LITERAL: + case OR: + this.addClause(generateClauseVector(cnf), null); + break; + case AND: + for (final Formula op : cnf) { + this.addClause(generateClauseVector(op), null); } + break; + default: + throw new IllegalStateException("Unexpected formula type in CNF: " + cnf.type()); } + } - /** - * Adds the given arbitrary formulas to the solver. - * @param formulas the formulas - */ - public void add(final Collection formulas) { - for (final Formula formula : formulas) { - add(formula); - } + /** + * Adds the given arbitrary formulas to the solver. + * @param formulas the formulas + */ + public void add(final Collection formulas) { + for (final Formula formula : formulas) { + add(formula); } + } - /** - * Returns a list of relevant variable indices. A relevant variable is known by the solver. - * @param variables variables to convert and filter - * @return list of relevant variable indices - */ - private List getRelevantVarIndices(final Collection variables) { - final List relevantVarIndices = new ArrayList<>(variables.size()); - for (final Variable var : variables) { - final Integer idx = this.name2idx.get(var.name()); - // Note: Unknown variables are variables added to the solver yet. Thus, these are optional variables and can - // be left out for the backbone computation. - if (idx != null) { - relevantVarIndices.add(idx); - } - } - return relevantVarIndices; + /** + * Returns a list of relevant variable indices. A relevant variable is known by the solver. + * @param variables variables to convert and filter + * @return list of relevant variable indices + */ + private List getRelevantVarIndices(final Collection variables) { + final List relevantVarIndices = new ArrayList<>(variables.size()); + for (final Variable var : variables) { + final Integer idx = this.name2idx.get(var.name()); + // Note: Unknown variables are variables added to the solver yet. Thus, these are optional variables and can + // be left out for the backbone computation. + if (idx != null) { + relevantVarIndices.add(idx); + } } + return relevantVarIndices; + } - /** - * Initializes the internal solver state. - * @param type backbone type - * @param variables to test - */ - private void init(final List variables, final BackboneType type) { - this.type = type; - this.candidates = new Stack<>(); - this.assumptions = new LNGIntVector(variables.size()); - this.backboneMap = new HashMap<>(); - for (final Integer var : variables) { - this.backboneMap.put(var, Tristate.UNDEF); - } + /** + * Initializes the internal solver state. + * @param type backbone type + * @param variables to test + */ + private void init(final List variables, final BackboneType type) { + this.type = type; + this.candidates = new Stack<>(); + this.assumptions = new LNGIntVector(variables.size()); + this.backboneMap = new HashMap<>(); + for (final Integer var : variables) { + this.backboneMap.put(var, Tristate.UNDEF); } + } - /** - * Tests if positive backbone literals should be computed. - * @return {@code true} if positive backbone literals should be computed, otherwise {@code false} - */ - private boolean isBothOrPositiveType() { - return this.type == BackboneType.POSITIVE_AND_NEGATIVE || this.type == BackboneType.ONLY_POSITIVE; - } + /** + * Tests if positive backbone literals should be computed. + * @return {@code true} if positive backbone literals should be computed, otherwise {@code false} + */ + private boolean isBothOrPositiveType() { + return this.type == BackboneType.POSITIVE_AND_NEGATIVE || this.type == BackboneType.ONLY_POSITIVE; + } - /** - * Tests if negative backbone literals should be computed. - * @return {@code true} if negative backbone literals should be computed, otherwise {@code false} - */ - private boolean isBothOrNegativeType() { - return this.type == BackboneType.POSITIVE_AND_NEGATIVE || this.type == BackboneType.ONLY_NEGATIVE; - } + /** + * Tests if negative backbone literals should be computed. + * @return {@code true} if negative backbone literals should be computed, otherwise {@code false} + */ + private boolean isBothOrNegativeType() { + return this.type == BackboneType.POSITIVE_AND_NEGATIVE || this.type == BackboneType.ONLY_NEGATIVE; + } - /** - * Tests if positive and negative backbone literals should be computed. - * @return {@code true} if positive and negative backbone literals should be computed, otherwise {@code false} - */ - private boolean isBothType() { - return this.type == BackboneType.POSITIVE_AND_NEGATIVE; - } + /** + * Tests if positive and negative backbone literals should be computed. + * @return {@code true} if positive and negative backbone literals should be computed, otherwise {@code false} + */ + private boolean isBothType() { + return this.type == BackboneType.POSITIVE_AND_NEGATIVE; + } - /** - * Builds the backbone object from the computed backbone literals. - * @param variables relevant variables - * @return backbone - */ - private Backbone buildBackbone(final Collection variables) { - final SortedSet posBackboneVars = isBothOrPositiveType() ? new TreeSet() : null; - final SortedSet negBackboneVars = isBothOrNegativeType() ? new TreeSet() : null; - final SortedSet optionalVars = isBothType() ? new TreeSet() : null; - for (final Variable var : variables) { - final Integer idx = this.name2idx.get(var.name()); - if (idx == null) { - if (isBothType()) { - optionalVars.add(var); - } - } else { - switch (this.backboneMap.get(idx)) { - case TRUE: - if (isBothOrPositiveType()) { - posBackboneVars.add(var); - } - break; - case FALSE: - if (isBothOrNegativeType()) { - negBackboneVars.add(var); - } - break; - case UNDEF: - if (isBothType()) { - optionalVars.add(var); - } - break; - default: - throw new IllegalStateException("Unknown tristate: " + this.backboneMap.get(idx)); - } + /** + * Builds the backbone object from the computed backbone literals. + * @param variables relevant variables + * @return backbone + */ + private Backbone buildBackbone(final Collection variables) { + final SortedSet posBackboneVars = isBothOrPositiveType() ? new TreeSet() : null; + final SortedSet negBackboneVars = isBothOrNegativeType() ? new TreeSet() : null; + final SortedSet optionalVars = isBothType() ? new TreeSet() : null; + for (final Variable var : variables) { + final Integer idx = this.name2idx.get(var.name()); + if (idx == null) { + if (isBothType()) { + optionalVars.add(var); + } + } else { + switch (this.backboneMap.get(idx)) { + case TRUE: + if (isBothOrPositiveType()) { + posBackboneVars.add(var); + } + break; + case FALSE: + if (isBothOrNegativeType()) { + negBackboneVars.add(var); } + break; + case UNDEF: + if (isBothType()) { + optionalVars.add(var); + } + break; + default: + throw new IllegalStateException("Unknown tristate: " + this.backboneMap.get(idx)); } - return new Backbone(posBackboneVars, negBackboneVars, optionalVars); + } } + return Backbone.satBackbone(posBackboneVars, negBackboneVars, optionalVars); + } - /** - * Computes the backbone of the given variables with respect to the formulas added to the solver. - * @param variables variables to test - * @param type backbone type - * @return the backbone projected to the relevant variables or {@code null} if the formula on the solver with the restrictions are not satisfiable - */ - public Backbone compute(final Collection variables, final BackboneType type) { - final boolean sat = solve(null) == Tristate.TRUE; - Backbone backbone = null; - if (sat) { - final List relevantVarIndices = getRelevantVarIndices(variables); - init(relevantVarIndices, type); - compute(relevantVarIndices); - backbone = buildBackbone(variables); - } - return backbone; + /** + * Computes the backbone of the given variables with respect to the formulas added to the solver. + * @param variables variables to test + * @param type backbone type + * @return the backbone projected to the relevant variables or {@code null} if the formula on the solver with the restrictions are not satisfiable + */ + public Backbone compute(final Collection variables, final BackboneType type) { + final boolean sat = solve(null) == Tristate.TRUE; + if (sat) { + final List relevantVarIndices = getRelevantVarIndices(variables); + init(relevantVarIndices, type); + compute(relevantVarIndices); + return buildBackbone(variables); + } else { + return Backbone.unsatBackbone(); } + } - /** - * Tests the given variable whether it is a unit propagated literal on level 0. - *

    - * Assumption: The formula on the solver has successfully been tested to be satisfiable before. - * @param var variable index to test - * @return {@code true} if the variable is a unit propagated literal on level 0, otherwise {@code false} - */ - private boolean isUPZeroLit(final int var) { - return this.vars.get(var).level() == 0; - } + /** + * Tests the given variable whether it is a unit propagated literal on level 0. + *

    + * Assumption: The formula on the solver has successfully been tested to be satisfiable before. + * @param var variable index to test + * @return {@code true} if the variable is a unit propagated literal on level 0, otherwise {@code false} + */ + private boolean isUPZeroLit(final int var) { + return this.vars.get(var).level() == 0; + } - /** - * Tests the given literal whether it is unit in the given clause. - * @param lit literal to test - * @param clause clause containing the literal - * @return {@code true} if the literal is unit, {@code false} otherwise - */ - private boolean isUnit(final int lit, final MSClause clause) { - for (int i = 0; i < clause.size(); ++i) { - final int clauseLit = clause.get(i); - if (lit != clauseLit && this.model.get(var(clauseLit)) != sign(clauseLit)) { - return false; - } - } - return true; + /** + * Tests the given literal whether it is unit in the given clause. + * @param lit literal to test + * @param clause clause containing the literal + * @return {@code true} if the literal is unit, {@code false} otherwise + */ + private boolean isUnit(final int lit, final MSClause clause) { + for (int i = 0; i < clause.size(); ++i) { + final int clauseLit = clause.get(i); + if (lit != clauseLit && this.model.get(var(clauseLit)) != sign(clauseLit)) { + return false; + } } + return true; + } - /** - * Tests the given literal whether it is rotatable in the current model. - * @param lit literal to test - * @return {@code true} if the literal is rotatable, otherwise {@code false} - */ - private boolean isRotatable(final int lit) { - // A rotatable literal MUST NOT be a unit propagated literal - if (v(lit).reason() != null) { - return false; - } - // A rotatable literal MUST NOT be unit - for (final MSWatcher watcher : this.watches.get(not(lit))) { - if (isUnit(lit, watcher.clause())) { - return false; - } - } - return true; + /** + * Tests the given literal whether it is rotatable in the current model. + * @param lit literal to test + * @return {@code true} if the literal is rotatable, otherwise {@code false} + */ + private boolean isRotatable(final int lit) { + // A rotatable literal MUST NOT be a unit propagated literal + if (v(lit).reason() != null) { + return false; } - - /** - * Adds the given literal to the backbone result and optionally adds the literal to the solver. - * @param lit literal to add - */ - private void addBackboneLiteral(final int lit) { - this.backboneMap.put(var(lit), sign(lit) ? Tristate.FALSE : Tristate.TRUE); - this.assumptions.push(lit); + // A rotatable literal MUST NOT be unit + for (final MSWatcher watcher : this.watches.get(not(lit))) { + if (isUnit(lit, watcher.clause())) { + return false; + } } + return true; + } - /** - * Creates the initial candidate literals for the backbone computation. - * @param variables variables to test - * @return initial candidates - */ - private Stack createInitialCandidates(final List variables) { - for (final Integer var : variables) { - if (isUPZeroLit(var)) { - final int backboneLit = mkLit(var, !this.model.get(var)); - addBackboneLiteral(backboneLit); - } else { - final boolean modelPhase = this.model.get(var); - if (isBothOrNegativeType() && !modelPhase || isBothOrPositiveType() && modelPhase) { - final int lit = mkLit(var, !modelPhase); - if (!this.config.isInitialUBCheckForRotatableLiterals() || !isRotatable(lit)) { - this.candidates.add(lit); - } - } - } - } - return this.candidates; - } + /** + * Adds the given literal to the backbone result and optionally adds the literal to the solver. + * @param lit literal to add + */ + private void addBackboneLiteral(final int lit) { + this.backboneMap.put(var(lit), sign(lit) ? Tristate.FALSE : Tristate.TRUE); + this.assumptions.push(lit); + } - /** - * Refines the upper bound by optional checks (UP zero literal, complement model literal, rotatable literal). - */ - private void refineUpperBound() { - for (final Integer lit : new ArrayList<>(this.candidates)) { - final int var = var(lit); - if (isUPZeroLit(var)) { - this.candidates.remove(lit); - addBackboneLiteral(lit); - } else if (this.config.isCheckForComplementModelLiterals() && this.model.get(var) == sign(lit)) { - this.candidates.remove(lit); - } else if (this.config.isCheckForRotatableLiterals() && isRotatable(lit)) { - this.candidates.remove(lit); - } + /** + * Creates the initial candidate literals for the backbone computation. + * @param variables variables to test + * @return initial candidates + */ + private Stack createInitialCandidates(final List variables) { + for (final Integer var : variables) { + if (isUPZeroLit(var)) { + final int backboneLit = mkLit(var, !this.model.get(var)); + addBackboneLiteral(backboneLit); + } else { + final boolean modelPhase = this.model.get(var); + if (isBothOrNegativeType() && !modelPhase || isBothOrPositiveType() && modelPhase) { + final int lit = mkLit(var, !modelPhase); + if (!this.config.isInitialUBCheckForRotatableLiterals() || !isRotatable(lit)) { + this.candidates.add(lit); + } } + } } + return this.candidates; + } - /** - * Tests the given literal with the formula on the solver for satisfiability. - * @param lit literal to test - * @return {@code true} if satisfiable, otherwise {@code false} - */ - private boolean solve(final int lit) { - this.assumptions.push(not(lit)); - final boolean sat = solve(null, this.assumptions) == Tristate.TRUE; - this.assumptions.pop(); - return sat; + /** + * Refines the upper bound by optional checks (UP zero literal, complement model literal, rotatable literal). + */ + private void refineUpperBound() { + for (final Integer lit : new ArrayList<>(this.candidates)) { + final int var = var(lit); + if (isUPZeroLit(var)) { + this.candidates.remove(lit); + addBackboneLiteral(lit); + } else if (this.config.isCheckForComplementModelLiterals() && this.model.get(var) == sign(lit)) { + this.candidates.remove(lit); + } else if (this.config.isCheckForRotatableLiterals() && isRotatable(lit)) { + this.candidates.remove(lit); + } } + } - @Override - protected void cancelUntil(final int level) { - if (decisionLevel() > level) { - for (int c = this.trail.size() - 1; c >= this.trailLim.get(level); c--) { - final int x = var(this.trail.get(c)); - final MSVariable v = this.vars.get(x); - v.assign(Tristate.UNDEF); - v.setPolarity(false); - insertVarOrder(x); - } - this.qhead = this.trailLim.get(level); - this.trail.removeElements(this.trail.size() - this.trailLim.get(level)); - this.trailLim.removeElements(this.trailLim.size() - level); - } + /** + * Tests the given literal with the formula on the solver for satisfiability. + * @param lit literal to test + * @return {@code true} if satisfiable, otherwise {@code false} + */ + private boolean solve(final int lit) { + this.assumptions.push(not(lit)); + final boolean sat = solve(null, this.assumptions) == Tristate.TRUE; + this.assumptions.pop(); + return sat; + } + + @Override + protected void cancelUntil(final int level) { + if (decisionLevel() > level) { + for (int c = this.trail.size() - 1; c >= this.trailLim.get(level); c--) { + final int x = var(this.trail.get(c)); + final MSVariable v = this.vars.get(x); + v.assign(Tristate.UNDEF); + v.setPolarity(false); + insertVarOrder(x); + } + this.qhead = this.trailLim.get(level); + this.trail.removeElements(this.trail.size() - this.trailLim.get(level)); + this.trailLim.removeElements(this.trailLim.size() - level); } + } - /** - * Computes the backbone for the given variables. - * @param variables variables to test - */ - private void compute(final List variables) { - final Stack candidates = createInitialCandidates(variables); - while (candidates.size() > 0) { - final int lit = candidates.pop(); - if (solve(lit)) { - refineUpperBound(); - } else { - addBackboneLiteral(lit); - } - } + /** + * Computes the backbone for the given variables. + * @param variables variables to test + */ + private void compute(final List variables) { + final Stack candidates = createInitialCandidates(variables); + while (candidates.size() > 0) { + final int lit = candidates.pop(); + if (solve(lit)) { + refineUpperBound(); + } else { + addBackboneLiteral(lit); + } } + } - /** - * Generates a solver vector of a clause. - * @param clause the clause - * @return the solver vector - */ - private LNGIntVector generateClauseVector(final Formula clause) { - final LNGIntVector clauseVec = new LNGIntVector(clause.numberOfOperands()); - for (final Literal lit : clause.literals()) { - int index = this.idxForName(lit.name()); - if (index == -1) { - index = this.newVar(false, true); - this.addName(lit.name(), index); - } - final int litNum = lit.phase() ? index * 2 : (index * 2) ^ 1; - clauseVec.push(litNum); - } - return clauseVec; + /** + * Generates a solver vector of a clause. + * @param clause the clause + * @return the solver vector + */ + private LNGIntVector generateClauseVector(final Formula clause) { + final LNGIntVector clauseVec = new LNGIntVector(clause.numberOfOperands()); + for (final Literal lit : clause.literals()) { + int index = this.idxForName(lit.name()); + if (index == -1) { + index = this.newVar(false, true); + this.addName(lit.name(), index); + } + final int litNum = lit.phase() ? index * 2 : (index * 2) ^ 1; + clauseVec.push(litNum); } + return clauseVec; + } } diff --git a/src/main/java/org/logicng/solvers/MiniSat.java b/src/main/java/org/logicng/solvers/MiniSat.java index f3e5e975..6e1cca2f 100644 --- a/src/main/java/org/logicng/solvers/MiniSat.java +++ b/src/main/java/org/logicng/solvers/MiniSat.java @@ -28,10 +28,6 @@ package org.logicng.solvers; -import static org.logicng.datastructures.Tristate.FALSE; -import static org.logicng.datastructures.Tristate.TRUE; -import static org.logicng.datastructures.Tristate.UNDEF; - import org.logicng.cardinalityconstraints.CCEncoder; import org.logicng.cardinalityconstraints.CCIncrementalData; import org.logicng.collections.LNGBooleanVector; @@ -74,6 +70,10 @@ import java.util.SortedSet; import java.util.TreeSet; +import static org.logicng.datastructures.Tristate.FALSE; +import static org.logicng.datastructures.Tristate.TRUE; +import static org.logicng.datastructures.Tristate.UNDEF; + /** * Wrapper for the MiniSAT-style SAT solvers. * @version 1.6.0 @@ -92,6 +92,7 @@ private enum SolverStyle {MINISAT, GLUCOSE, MINICARD} private final boolean incremental; private int nextStateId; private final PlaistedGreenbaumTransformationSolver pgTransformation; + private boolean lastComputationWithAssumptions; /** * Constructs a new SAT solver instance. @@ -264,10 +265,11 @@ protected void addClauseWithRelaxation(final Variable relaxationVar, final Formu @Override public Tristate sat(final SATHandler handler) { - if (this.result != UNDEF) { + if (lastResultIsUsable()) { return this.result; } this.result = this.solver.solve(handler); + this.lastComputationWithAssumptions = false; return this.result; } @@ -282,6 +284,7 @@ public Tristate sat(final SATHandler handler, final Literal literal) { final int litNum = literal.phase() ? index * 2 : (index * 2) ^ 1; clauseVec.push(litNum); this.result = this.solver.solve(handler, clauseVec); + this.lastComputationWithAssumptions = true; return this.result; } @@ -299,12 +302,14 @@ public Tristate sat(final SATHandler handler, final Collection unsatCore() { if (this.underlyingSolver() instanceof GlucoseSyrup && this.config.incremental()) { throw new IllegalStateException("Cannot compute an unsat core with Glucose in incremental mode."); } + if (this.lastComputationWithAssumptions) { + throw new IllegalStateException("Cannot compute an unsat core for a computation with assumptions."); + } final DRUPTrim trimmer = new DRUPTrim(); @@ -631,4 +639,8 @@ public SortedSet upZeroLiterals() { } return upZeroLiterals; } + + private boolean lastResultIsUsable() { + return this.result != UNDEF && !this.lastComputationWithAssumptions; + } } diff --git a/src/main/java/org/logicng/transformations/BackboneSimplifier.java b/src/main/java/org/logicng/transformations/BackboneSimplifier.java index 9e4788f0..52eef029 100644 --- a/src/main/java/org/logicng/transformations/BackboneSimplifier.java +++ b/src/main/java/org/logicng/transformations/BackboneSimplifier.java @@ -42,19 +42,19 @@ * @since 1.5.0 */ public class BackboneSimplifier implements FormulaTransformation { - @Override - public Formula apply(final Formula formula, final boolean cache) { - final Backbone backbone = BackboneGeneration.compute(formula); - if (backbone == null) { - return formula.factory().falsum(); - } - if (!backbone.getNegativeBackbone().isEmpty() || !backbone.getPositiveBackbone().isEmpty()) { - final Formula backboneFormula = backbone.toFormula(formula.factory()); - final Assignment assignment = new Assignment(backbone.getCompleteBackbone()); - final Formula restrictedFormula = formula.restrict(assignment); - return formula.factory().and(backboneFormula, restrictedFormula); - } else { - return formula; - } + @Override + public Formula apply(final Formula formula, final boolean cache) { + final Backbone backbone = BackboneGeneration.compute(formula); + if (!backbone.isSat()) { + return formula.factory().falsum(); } + if (!backbone.getNegativeBackbone().isEmpty() || !backbone.getPositiveBackbone().isEmpty()) { + final Formula backboneFormula = backbone.toFormula(formula.factory()); + final Assignment assignment = new Assignment(backbone.getCompleteBackbone()); + final Formula restrictedFormula = formula.restrict(assignment); + return formula.factory().and(backboneFormula, restrictedFormula); + } else { + return formula; + } + } } diff --git a/src/test/java/org/logicng/backbones/BackboneTest.java b/src/test/java/org/logicng/backbones/BackboneTest.java index 9350d50c..4b3dc016 100644 --- a/src/test/java/org/logicng/backbones/BackboneTest.java +++ b/src/test/java/org/logicng/backbones/BackboneTest.java @@ -46,110 +46,120 @@ /** * Unit tests for {@link Backbone}. - * @version 1.5.0 + * @version 1.5.2 * @since 1.5.0 */ public class BackboneTest { - private final FormulaFactory f = new FormulaFactory(); - private final PropositionalParser p = new PropositionalParser(this.f); - private final Variable a1 = this.f.variable("a1"); - private final Variable a2 = this.f.variable("a2"); - private final Variable a3 = this.f.variable("a3"); - private final Variable b1 = this.f.variable("b1"); - private final Variable b2 = this.f.variable("b2"); - private final Variable b3 = this.f.variable("b3"); - private final Variable x1 = this.f.variable("x1"); - private final Variable x2 = this.f.variable("x2"); - private final Variable x3 = this.f.variable("x3"); + private final FormulaFactory f = new FormulaFactory(); + private final PropositionalParser p = new PropositionalParser(this.f); + private final Variable a1 = this.f.variable("a1"); + private final Variable a2 = this.f.variable("a2"); + private final Variable a3 = this.f.variable("a3"); + private final Variable b1 = this.f.variable("b1"); + private final Variable b2 = this.f.variable("b2"); + private final Variable b3 = this.f.variable("b3"); + private final Variable x1 = this.f.variable("x1"); + private final Variable x2 = this.f.variable("x2"); + private final Variable x3 = this.f.variable("x3"); - @Test - public void testToFormula() throws ParserException { - Backbone backbone = new Backbone(set(this.a1, this.a2, this.a3), null, null); - assertThat(backbone.toFormula(this.f)).isEqualTo(this.p.parse("a1 & a2 & a3")); - backbone = new Backbone(null, set(this.b1, this.b2, this.b3), null); - assertThat(backbone.toFormula(this.f)).isEqualTo(this.p.parse("~b1 & ~b2 & ~b3")); - backbone = new Backbone(set(this.a1, this.a2, this.a3), set(this.b1), null); - assertThat(backbone.toFormula(this.f)).isEqualTo(this.p.parse("a1 & a2 & a3 & ~b1")); - backbone = new Backbone(set(this.a1, this.a2, this.a3), null, set(this.x1, this.x2, this.x3)); - assertThat(backbone.toFormula(this.f)).isEqualTo(this.p.parse("a1 & a2 & a3")); - backbone = new Backbone(null, set(this.b1, this.b2, this.b3), set(this.x1, this.x2, this.x3)); - assertThat(backbone.toFormula(this.f)).isEqualTo(this.p.parse("~b1 & ~b2 & ~b3")); - backbone = new Backbone(set(this.a1), set(this.b1, this.b2, this.b3), set(this.x1)); - assertThat(backbone.toFormula(this.f)).isEqualTo(this.p.parse("a1 & ~b1 & ~b2 & ~b3")); - } + @Test + public void testToFormula() throws ParserException { + Backbone backbone = Backbone.satBackbone(set(this.a1, this.a2, this.a3), null, null); + assertThat(backbone.toFormula(this.f)).isEqualTo(this.p.parse("a1 & a2 & a3")); + backbone = Backbone.satBackbone(null, set(this.b1, this.b2, this.b3), null); + assertThat(backbone.toFormula(this.f)).isEqualTo(this.p.parse("~b1 & ~b2 & ~b3")); + backbone = Backbone.satBackbone(set(this.a1, this.a2, this.a3), set(this.b1), null); + assertThat(backbone.toFormula(this.f)).isEqualTo(this.p.parse("a1 & a2 & a3 & ~b1")); + backbone = Backbone.satBackbone(set(this.a1, this.a2, this.a3), null, set(this.x1, this.x2, this.x3)); + assertThat(backbone.toFormula(this.f)).isEqualTo(this.p.parse("a1 & a2 & a3")); + backbone = Backbone.satBackbone(null, set(this.b1, this.b2, this.b3), set(this.x1, this.x2, this.x3)); + assertThat(backbone.toFormula(this.f)).isEqualTo(this.p.parse("~b1 & ~b2 & ~b3")); + backbone = Backbone.satBackbone(set(this.a1), set(this.b1, this.b2, this.b3), set(this.x1)); + assertThat(backbone.toFormula(this.f)).isEqualTo(this.p.parse("a1 & ~b1 & ~b2 & ~b3")); + } - @Test - public void testToMap() { - Backbone backbone = new Backbone(set(this.a1, this.a2), null, null); - assertThat(backbone.toMap()).containsExactly( - MapEntry.entry(this.a1, TRUE), - MapEntry.entry(this.a2, TRUE) - ); - backbone = new Backbone(null, set(this.b1, this.b2, this.b3), null); - assertThat(backbone.toMap()).containsExactly( - MapEntry.entry(this.b1, FALSE), - MapEntry.entry(this.b2, FALSE), - MapEntry.entry(this.b3, FALSE) - ); - backbone = new Backbone(set(this.a1, this.a2, this.a3), set(this.b1), null); - assertThat(backbone.toMap()).containsExactly( - MapEntry.entry(this.a1, TRUE), - MapEntry.entry(this.a2, TRUE), - MapEntry.entry(this.a3, TRUE), - MapEntry.entry(this.b1, FALSE) - ); - backbone = new Backbone(set(this.a1, this.a2, this.a3), null, set(this.x1, this.x2, this.x3)); - assertThat(backbone.toMap()).containsExactly( - MapEntry.entry(this.a1, TRUE), - MapEntry.entry(this.a2, TRUE), - MapEntry.entry(this.a3, TRUE), - MapEntry.entry(this.x1, UNDEF), - MapEntry.entry(this.x2, UNDEF), - MapEntry.entry(this.x3, UNDEF) - ); - backbone = new Backbone(null, set(this.b1, this.b2, this.b3), set(this.x1, this.x2, this.x3)); - assertThat(backbone.toMap()).containsExactly( - MapEntry.entry(this.b1, FALSE), - MapEntry.entry(this.b2, FALSE), - MapEntry.entry(this.b3, FALSE), - MapEntry.entry(this.x1, UNDEF), - MapEntry.entry(this.x2, UNDEF), - MapEntry.entry(this.x3, UNDEF) - ); - backbone = new Backbone(set(this.a1), set(this.b1, this.b2), set(this.x1)); - assertThat(backbone.toMap()).containsExactly( - MapEntry.entry(this.a1, TRUE), - MapEntry.entry(this.b1, FALSE), - MapEntry.entry(this.b2, FALSE), - MapEntry.entry(this.x1, UNDEF) - ); - } + @Test + public void testToMap() { + Backbone backbone = Backbone.satBackbone(set(this.a1, this.a2), null, null); + assertThat(backbone.toMap()).containsExactly( + MapEntry.entry(this.a1, TRUE), + MapEntry.entry(this.a2, TRUE) + ); + backbone = Backbone.satBackbone(null, set(this.b1, this.b2, this.b3), null); + assertThat(backbone.toMap()).containsExactly( + MapEntry.entry(this.b1, FALSE), + MapEntry.entry(this.b2, FALSE), + MapEntry.entry(this.b3, FALSE) + ); + backbone = Backbone.satBackbone(set(this.a1, this.a2, this.a3), set(this.b1), null); + assertThat(backbone.toMap()).containsExactly( + MapEntry.entry(this.a1, TRUE), + MapEntry.entry(this.a2, TRUE), + MapEntry.entry(this.a3, TRUE), + MapEntry.entry(this.b1, FALSE) + ); + backbone = Backbone.satBackbone(set(this.a1, this.a2, this.a3), null, set(this.x1, this.x2, this.x3)); + assertThat(backbone.toMap()).containsExactly( + MapEntry.entry(this.a1, TRUE), + MapEntry.entry(this.a2, TRUE), + MapEntry.entry(this.a3, TRUE), + MapEntry.entry(this.x1, UNDEF), + MapEntry.entry(this.x2, UNDEF), + MapEntry.entry(this.x3, UNDEF) + ); + backbone = Backbone.satBackbone(null, set(this.b1, this.b2, this.b3), set(this.x1, this.x2, this.x3)); + assertThat(backbone.toMap()).containsExactly( + MapEntry.entry(this.b1, FALSE), + MapEntry.entry(this.b2, FALSE), + MapEntry.entry(this.b3, FALSE), + MapEntry.entry(this.x1, UNDEF), + MapEntry.entry(this.x2, UNDEF), + MapEntry.entry(this.x3, UNDEF) + ); + backbone = Backbone.satBackbone(set(this.a1), set(this.b1, this.b2), set(this.x1)); + assertThat(backbone.toMap()).containsExactly( + MapEntry.entry(this.a1, TRUE), + MapEntry.entry(this.b1, FALSE), + MapEntry.entry(this.b2, FALSE), + MapEntry.entry(this.x1, UNDEF) + ); + } - @Test - public void testToString() { - final Backbone backbone = new Backbone(set(this.a1, this.a2, this.a3), set(this.b1, this.b2, this.b3), set(this.x1, this.x2, this.x3)); - assertThat(backbone.toString()).isEqualTo("Backbone{positiveBackbone=[a1, a2, a3], negativeBackbone=[b1, b2, b3], optionalVariables=[x1, x2, x3]}"); + @Test + public void testUnsatBackbone() { + final Backbone backbone = Backbone.unsatBackbone(); + assertThat(backbone.getCompleteBackbone()).isEmpty(); + assertThat(backbone.getNegativeBackbone()).isEmpty(); + assertThat(backbone.getPositiveBackbone()).isEmpty(); + assertThat(backbone.getOptionalVariables()).isEmpty(); + assertThat(backbone.toMap()).isEmpty(); + assertThat(backbone.toFormula(this.f)).isEqualTo(this.f.falsum()); + } - } + @Test + public void testToString() { + final Backbone backbone = Backbone.satBackbone(set(this.a1, this.a2, this.a3), set(this.b1, this.b2, this.b3), set(this.x1, this.x2, this.x3)); + assertThat(backbone.toString()).isEqualTo("Backbone{positiveBackbone=[a1, a2, a3], negativeBackbone=[b1, b2, b3], optionalVariables=[x1, x2, x3]}"); + } - @Test - public void testEqualsAndHashCode() { - final Backbone backbone1a = new Backbone(set(this.a1, this.a2, this.a3), null, null); - final Backbone backbone1b = new Backbone(set(this.a1, this.a2, this.a3), null, null); - final Backbone backbone3 = new Backbone(set(this.a1, this.a2, this.a3), set(this.b1), null); - final Backbone backbone5 = new Backbone(null, set(this.b1, this.b2, this.b3), set(this.x1, this.x2, this.x3)); + @Test + public void testEqualsAndHashCode() { + final Backbone backbone1a = Backbone.satBackbone(set(this.a1, this.a2, this.a3), null, null); + final Backbone backbone1b = Backbone.satBackbone(set(this.a1, this.a2, this.a3), null, null); + final Backbone backbone3 = Backbone.satBackbone(set(this.a1, this.a2, this.a3), set(this.b1), null); + final Backbone backbone5 = Backbone.satBackbone(null, set(this.b1, this.b2, this.b3), set(this.x1, this.x2, this.x3)); - assertThat(backbone1a.hashCode()).isEqualTo(backbone1b.hashCode()); - assertThat(backbone1a.equals(backbone1a)).isTrue(); - assertThat(backbone1a.equals(backbone1b)).isTrue(); - assertThat(backbone1a.equals(backbone3)).isFalse(); - assertThat(backbone1a.equals(backbone5)).isFalse(); - assertThat(backbone1a.equals("String")).isFalse(); - assertThat(backbone1a.equals(null)).isFalse(); + assertThat(backbone1a.hashCode()).isEqualTo(backbone1b.hashCode()); + assertThat(backbone1a.equals(backbone1a)).isTrue(); + assertThat(backbone1a.equals(backbone1b)).isTrue(); + assertThat(backbone1a.equals(backbone3)).isFalse(); + assertThat(backbone1a.equals(backbone5)).isFalse(); + assertThat(backbone1a.equals("String")).isFalse(); + assertThat(backbone1a.equals(null)).isFalse(); - } + } - private SortedSet set(final Variable... variables) { - return new TreeSet<>(Arrays.asList(variables)); - } + private SortedSet set(final Variable... variables) { + return new TreeSet<>(Arrays.asList(variables)); + } } diff --git a/src/test/java/org/logicng/solvers/sat/MiniSatTest.java b/src/test/java/org/logicng/solvers/sat/MiniSatTest.java index 56040884..a6e63b48 100644 --- a/src/test/java/org/logicng/solvers/sat/MiniSatTest.java +++ b/src/test/java/org/logicng/solvers/sat/MiniSatTest.java @@ -28,15 +28,20 @@ package org.logicng.solvers.sat; +import static org.assertj.core.api.Assertions.assertThat; +import static org.logicng.datastructures.Tristate.FALSE; +import static org.logicng.datastructures.Tristate.TRUE; + import org.junit.Assert; import org.junit.Test; import org.logicng.collections.LNGIntVector; +import org.logicng.formulas.FormulaFactory; +import org.logicng.io.parsers.ParserException; +import org.logicng.solvers.MiniSat; +import org.logicng.solvers.SATSolver; import java.util.Arrays; -import static org.logicng.datastructures.Tristate.FALSE; -import static org.logicng.datastructures.Tristate.TRUE; - /** * Some MiniSat specific unit tests. * @version 1.3 @@ -77,14 +82,31 @@ public void testConfig() { Assert.assertTrue(Arrays.asList(MiniSatConfig.ClauseMinimization.values()).contains(MiniSatConfig.ClauseMinimization.valueOf("DEEP"))); } - private LNGIntVector clause(int... lits) { + @Test + public void testAssumptionChecking() throws ParserException { + final FormulaFactory f = new FormulaFactory(); + final SATSolver solver = MiniSat.miniSat(f); + solver.add(f.parse("A & B")); + assertThat(solver.sat()).isEqualTo(TRUE); + assertThat(solver.sat(f.literal("A", true))).isEqualTo(TRUE); + assertThat(solver.sat(f.literal("B", true))).isEqualTo(TRUE); + assertThat(solver.sat(f.literal("A", false))).isEqualTo(FALSE); + assertThat(solver.sat(f.literal("B", false))).isEqualTo(FALSE); + assertThat(solver.sat(f.literal("A", true))).isEqualTo(TRUE); + assertThat(solver.sat(f.literal("B", true))).isEqualTo(TRUE); + assertThat(solver.sat(f.literal("A", false))).isEqualTo(FALSE); + assertThat(solver.sat()).isEqualTo(TRUE); + } + + private LNGIntVector clause(final int... lits) { final LNGIntVector c = new LNGIntVector(lits.length); - for (int l : lits) + for (final int l : lits) { c.push(literal(l)); + } return c; } - private int literal(int l) { + private int literal(final int l) { return l < 0 ? (-l * 2) ^ 1 : l * 2; } } From c1b11e610e33e24b7bd3a7abb162cb2bcb45fc36 Mon Sep 17 00:00:00 2001 From: Christoph Zengler Date: Fri, 26 Jul 2019 14:41:16 +0200 Subject: [PATCH 03/16] more work on the new CNF methods --- .../logicng/backbones/MiniSatBackbone.java | 48 +++++-------------- .../java/org/logicng/solvers/MiniSat.java | 27 ++++++++--- .../solvers/sat/MiniSatStyleSolver.java | 30 ++++++++---- ...PlaistedGreenbaumTransformationSolver.java | 31 ++++++------ 4 files changed, 71 insertions(+), 65 deletions(-) diff --git a/src/main/java/org/logicng/backbones/MiniSatBackbone.java b/src/main/java/org/logicng/backbones/MiniSatBackbone.java index 69c6f85f..10e563a2 100644 --- a/src/main/java/org/logicng/backbones/MiniSatBackbone.java +++ b/src/main/java/org/logicng/backbones/MiniSatBackbone.java @@ -31,12 +31,12 @@ import org.logicng.collections.LNGIntVector; import org.logicng.datastructures.Tristate; import org.logicng.formulas.Formula; -import org.logicng.formulas.Literal; import org.logicng.formulas.Variable; import org.logicng.solvers.datastructures.MSClause; import org.logicng.solvers.datastructures.MSVariable; import org.logicng.solvers.datastructures.MSWatcher; import org.logicng.solvers.sat.MiniSat2Solver; +import org.logicng.transformations.cnf.PlaistedGreenbaumTransformationSolver; import java.util.ArrayList; import java.util.Collection; @@ -61,6 +61,7 @@ public class MiniSatBackbone extends MiniSat2Solver { private BackboneConfig config; + private final PlaistedGreenbaumTransformationSolver pgTransformation = new PlaistedGreenbaumTransformationSolver(this, true); /** * Type of the backbone computation. @@ -117,23 +118,7 @@ public void setConfig(final BackboneConfig newConfig) { * @param formula the formula */ public void add(final Formula formula) { - final Formula cnf = formula.cnf(); - switch (cnf.type()) { - case TRUE: - break; - case FALSE: - case LITERAL: - case OR: - this.addClause(generateClauseVector(cnf), null); - break; - case AND: - for (final Formula op : cnf) { - this.addClause(generateClauseVector(op), null); - } - break; - default: - throw new IllegalStateException("Unexpected formula type in CNF: " + cnf.type()); - } + this.pgTransformation.addCNFtoSolver(formula, null); } /** @@ -400,22 +385,15 @@ private void compute(final List variables) { } } - /** - * Generates a solver vector of a clause. - * @param clause the clause - * @return the solver vector - */ - private LNGIntVector generateClauseVector(final Formula clause) { - final LNGIntVector clauseVec = new LNGIntVector(clause.numberOfOperands()); - for (final Literal lit : clause.literals()) { - int index = this.idxForName(lit.name()); - if (index == -1) { - index = this.newVar(false, true); - this.addName(lit.name(), index); - } - final int litNum = lit.phase() ? index * 2 : (index * 2) ^ 1; - clauseVec.push(litNum); - } - return clauseVec; + @Override + public void reset() { + super.reset(); + this.pgTransformation.clearCache(); + } + + @Override + public void loadState(final int[] state) { + super.loadState(state); + this.pgTransformation.clearCache(); } } diff --git a/src/main/java/org/logicng/solvers/MiniSat.java b/src/main/java/org/logicng/solvers/MiniSat.java index 6e1cca2f..383be195 100644 --- a/src/main/java/org/logicng/solvers/MiniSat.java +++ b/src/main/java/org/logicng/solvers/MiniSat.java @@ -28,6 +28,10 @@ package org.logicng.solvers; +import static org.logicng.datastructures.Tristate.FALSE; +import static org.logicng.datastructures.Tristate.TRUE; +import static org.logicng.datastructures.Tristate.UNDEF; + import org.logicng.cardinalityconstraints.CCEncoder; import org.logicng.cardinalityconstraints.CCIncrementalData; import org.logicng.collections.LNGBooleanVector; @@ -70,10 +74,6 @@ import java.util.SortedSet; import java.util.TreeSet; -import static org.logicng.datastructures.Tristate.FALSE; -import static org.logicng.datastructures.Tristate.TRUE; -import static org.logicng.datastructures.Tristate.UNDEF; - /** * Wrapper for the MiniSAT-style SAT solvers. * @version 1.6.0 @@ -124,7 +124,7 @@ private MiniSat(final FormulaFactory f, final SolverStyle solverStyle, final Min this.validStates = new LNGIntVector(); this.nextStateId = 0; this.ccEncoder = new CCEncoder(f); - this.pgTransformation = new PlaistedGreenbaumTransformationSolver(this); + this.pgTransformation = new PlaistedGreenbaumTransformationSolver(this.underlyingSolver(), this.initialPhase); } /** @@ -358,7 +358,21 @@ public List enumerateAllModels(final Collection variables, allVariables.addAll(variables); allVariables.addAll(additionalVariables); } - final LNGIntVector relevantIndices = variables == null ? null : new LNGIntVector(variables.size()); + final LNGIntVector relevantIndices; + if (variables == null) { + if (!this.config.isAuxiliaryVariablesInModels()) { + relevantIndices = new LNGIntVector(); + for (final Map.Entry entry : this.solver.getName2idx().entrySet()) { + if (isRelevantVariable(entry.getKey())) { + relevantIndices.push(entry.getValue()); + } + } + } else { + relevantIndices = null; + } + } else { + relevantIndices = new LNGIntVector(variables.size()); + } LNGIntVector relevantAllIndices = null; if (relevantIndices != null) { for (final Variable var : variables) { @@ -374,7 +388,6 @@ public List enumerateAllModels(final Collection variables, while (proceed && this.sat((SATHandler) null) == TRUE) { final LNGBooleanVector modelFromSolver = this.solver.model(); final Assignment model = this.createAssignment(modelFromSolver, relevantAllIndices); - assert model != null; models.add(model); proceed = handler == null || handler.foundModel(model); if (model.size() > 0) { diff --git a/src/main/java/org/logicng/solvers/sat/MiniSatStyleSolver.java b/src/main/java/org/logicng/solvers/sat/MiniSatStyleSolver.java index 32403da6..eb49a696 100644 --- a/src/main/java/org/logicng/solvers/sat/MiniSatStyleSolver.java +++ b/src/main/java/org/logicng/solvers/sat/MiniSatStyleSolver.java @@ -129,6 +129,10 @@ protected MiniSatStyleSolver(final MiniSatConfig config) { this.initialize(); } + public Map getName2idx() { + return this.name2idx; + } + /** * Creates a literal for a given variable number and literal. * @param var the variable number @@ -441,8 +445,9 @@ protected int abstractLevel(int x) { * @param x the variable index */ protected void insertVarOrder(int x) { - if (!this.orderHeap.inHeap(x) && this.vars.get(x).decision()) + if (!this.orderHeap.inHeap(x) && this.vars.get(x).decision()) { this.orderHeap.insert(x); + } } /** @@ -451,11 +456,13 @@ protected void insertVarOrder(int x) { */ protected int pickBranchLit() { int next = -1; - while (next == -1 || this.vars.get(next).assignment() != Tristate.UNDEF || !this.vars.get(next).decision()) - if (this.orderHeap.empty()) + while (next == -1 || this.vars.get(next).assignment() != Tristate.UNDEF || !this.vars.get(next).decision()) { + if (this.orderHeap.empty()) { return -1; - else + } else { next = this.orderHeap.removeMin(); + } + } return mkLit(next, this.vars.get(next).polarity()); } @@ -483,12 +490,14 @@ protected void varBumpActivity(int v, double inc) { final MSVariable var = this.vars.get(v); var.incrementActivity(inc); if (var.activity() > 1e100) { - for (final MSVariable variable : this.vars) + for (final MSVariable variable : this.vars) { variable.rescaleActivity(); + } this.varInc *= 1e-100; } - if (this.orderHeap.inHeap(v)) + if (this.orderHeap.inHeap(v)) { this.orderHeap.decrease(v); + } } /** @@ -496,9 +505,11 @@ protected void varBumpActivity(int v, double inc) { */ protected void rebuildOrderHeap() { final LNGIntVector vs = new LNGIntVector(); - for (int v = 0; v < this.nVars(); v++) - if (this.vars.get(v).decision() && this.vars.get(v).assignment() == Tristate.UNDEF) + for (int v = 0; v < this.nVars(); v++) { + if (this.vars.get(v).decision() && this.vars.get(v).assignment() == Tristate.UNDEF) { vs.push(v); + } + } this.orderHeap.build(vs); } @@ -525,8 +536,9 @@ protected void claDecayActivity() { protected void claBumpActivity(final MSClause c) { c.incrementActivity(claInc); if (c.activity() > 1e20) { - for (final MSClause clause : learnts) + for (final MSClause clause : learnts) { clause.rescaleActivity(); + } claInc *= 1e-20; } } diff --git a/src/main/java/org/logicng/transformations/cnf/PlaistedGreenbaumTransformationSolver.java b/src/main/java/org/logicng/transformations/cnf/PlaistedGreenbaumTransformationSolver.java index 24152dbe..8b8dbd3c 100644 --- a/src/main/java/org/logicng/transformations/cnf/PlaistedGreenbaumTransformationSolver.java +++ b/src/main/java/org/logicng/transformations/cnf/PlaistedGreenbaumTransformationSolver.java @@ -35,7 +35,7 @@ import org.logicng.formulas.Literal; import org.logicng.predicates.CNFPredicate; import org.logicng.propositions.Proposition; -import org.logicng.solvers.MiniSat; +import org.logicng.solvers.sat.MiniSatStyleSolver; import java.util.Collection; import java.util.HashMap; @@ -53,17 +53,20 @@ public final class PlaistedGreenbaumTransformationSolver { private final Map variableCache; private final Set formulaCache; - private final MiniSat solver; + private final MiniSatStyleSolver solver; + private final boolean initialPhase; private final CNFPredicate cnfPredicate = new CNFPredicate(); /** * Constructs a new transformation for a given SAT solver. - * @param solver the solver + * @param solver the solver + * @param initialPhase the initial phase for new variables */ - public PlaistedGreenbaumTransformationSolver(final MiniSat solver) { + public PlaistedGreenbaumTransformationSolver(final MiniSatStyleSolver solver, final boolean initialPhase) { this.variableCache = new HashMap<>(); this.formulaCache = new HashSet<>(); this.solver = solver; + this.initialPhase = initialPhase; } /** @@ -78,7 +81,7 @@ public void addCNFtoSolver(final Formula formula, final Proposition proposition) } else { computeTransformation(nnf, proposition); final int topLevelVariable = this.variableCache.get(nnf); - this.solver.underlyingSolver().addClause(topLevelVariable, proposition); + this.solver.addClause(topLevelVariable, proposition); this.variableCache.put(formula, topLevelVariable); } } @@ -115,7 +118,7 @@ private void computePosPolarity(final Formula formula, final Proposition proposi switch (formula.type()) { case AND: { for (final Formula op : formula) { - this.solver.underlyingSolver().addClause(new LNGIntVector(new int[]{pgVar ^ 1, pgVariable(op)}), proposition); + this.solver.addClause(new LNGIntVector(new int[]{pgVar ^ 1, pgVariable(op)}), proposition); } this.formulaCache.add(formula); break; @@ -126,7 +129,7 @@ private void computePosPolarity(final Formula formula, final Proposition proposi for (final Formula op : formula) { singleClause.push(pgVariable(op)); } - this.solver.underlyingSolver().addClause(singleClause, proposition); + this.solver.addClause(singleClause, proposition); this.formulaCache.add(formula); break; } @@ -142,11 +145,11 @@ private void addCNF(final Formula cnf, final Proposition proposition) { case FALSE: case LITERAL: case OR: - this.solver.underlyingSolver().addClause(generateClauseVector(cnf.literals()), proposition); + this.solver.addClause(generateClauseVector(cnf.literals()), proposition); break; case AND: for (final Formula clause : cnf) { - this.solver.underlyingSolver().addClause(generateClauseVector(clause.literals()), proposition); + this.solver.addClause(generateClauseVector(clause.literals()), proposition); } break; default: @@ -175,18 +178,18 @@ private LNGIntVector generateClauseVector(final Collection literals) { } private int solverLiteral(final Literal lit) { - int index = this.solver.underlyingSolver().idxForName(lit.name()); + int index = this.solver.idxForName(lit.name()); if (index == -1) { - index = this.solver.underlyingSolver().newVar(!this.solver.initialPhase(), true); - this.solver.underlyingSolver().addName(lit.name(), index); + index = this.solver.newVar(!this.initialPhase, true); + this.solver.addName(lit.name(), index); } return lit.phase() ? index * 2 : (index * 2) ^ 1; } private int newSolverVariable() { - final int index = this.solver.underlyingSolver().newVar(!this.solver.initialPhase(), true); + final int index = this.solver.newVar(!this.initialPhase, true); final String name = FormulaFactory.CNF_PREFIX + "MINISAT_" + index; - this.solver.underlyingSolver().addName(name, index); + this.solver.addName(name, index); return index * 2; } } From 72311eeb1127b12b3779d645605402b51a41d725 Mon Sep 17 00:00:00 2001 From: Rouven Walter Date: Fri, 26 Jul 2019 17:20:39 +0200 Subject: [PATCH 04/16] fix: NullPointerException in enumerateAllModels --- .../java/org/logicng/solvers/MiniSat.java | 24 +++++----- .../java/org/logicng/solvers/sat/SATTest.java | 45 +++++++++++++++---- 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/logicng/solvers/MiniSat.java b/src/main/java/org/logicng/solvers/MiniSat.java index 383be195..03e995cb 100644 --- a/src/main/java/org/logicng/solvers/MiniSat.java +++ b/src/main/java/org/logicng/solvers/MiniSat.java @@ -351,13 +351,6 @@ public List enumerateAllModels(final Collection variables, stateBeforeEnumeration = this.saveState(); } boolean proceed = true; - SortedSet allVariables = new TreeSet<>(); - if (variables == null) { - allVariables = null; - } else { - allVariables.addAll(variables); - allVariables.addAll(additionalVariables); - } final LNGIntVector relevantIndices; if (variables == null) { if (!this.config.isAuxiliaryVariablesInModels()) { @@ -372,15 +365,20 @@ public List enumerateAllModels(final Collection variables, } } else { relevantIndices = new LNGIntVector(variables.size()); - } - LNGIntVector relevantAllIndices = null; - if (relevantIndices != null) { for (final Variable var : variables) { relevantIndices.push(this.solver.idxForName(var.name())); } - relevantAllIndices = additionalVariables.isEmpty() ? relevantIndices : new LNGIntVector(allVariables.size()); - if (!additionalVariables.isEmpty()) { - for (final Variable var : allVariables) { + } + LNGIntVector relevantAllIndices = null; + if (relevantIndices != null) { + if(additionalVariables.isEmpty()) { + relevantAllIndices = relevantIndices; + } else { + relevantAllIndices = new LNGIntVector(relevantIndices.size() + additionalVariables.size()); + for(int i = 0; i < relevantIndices.size(); ++i) { + relevantAllIndices.push(relevantIndices.get(i)); + } + for (final Variable var : additionalVariables) { relevantAllIndices.push(this.solver.idxForName(var.name())); } } diff --git a/src/test/java/org/logicng/solvers/sat/SATTest.java b/src/test/java/org/logicng/solvers/sat/SATTest.java index 1db8935e..7e1f7826 100644 --- a/src/test/java/org/logicng/solvers/sat/SATTest.java +++ b/src/test/java/org/logicng/solvers/sat/SATTest.java @@ -91,7 +91,7 @@ public SATTest() { this.f = new FormulaFactory(); this.pg = new PigeonHoleGenerator(this.f); this.parser = new PropositionalParser(this.f); - this.solvers = new SATSolver[9]; + this.solvers = new SATSolver[10]; this.solvers[0] = MiniSat.miniSat(this.f, new MiniSatConfig.Builder().incremental(true).build()); this.solvers[1] = MiniSat.miniSat(this.f, new MiniSatConfig.Builder().incremental(false).build()); this.solvers[2] = MiniSat.glucose(this.f, new MiniSatConfig.Builder().incremental(false).build(), @@ -99,20 +99,22 @@ public SATTest() { this.solvers[3] = MiniSat.miniCard(this.f, new MiniSatConfig.Builder().incremental(true).build()); this.solvers[4] = MiniSat.miniCard(this.f, new MiniSatConfig.Builder().incremental(false).build()); this.solvers[5] = MiniSat.miniSat(this.f, new MiniSatConfig.Builder().cnfMethod(MiniSatConfig.CNFMethod.PG_ON_SOLVER).build()); - this.solvers[6] = CleaneLing.minimalistic(this.f); - this.solvers[7] = CleaneLing.full(this.f, new CleaneLingConfig.Builder().plain(true).glueUpdate(true).gluered(true).build()); - this.solvers[8] = CleaneLing.full(this.f); + this.solvers[6] = MiniSat.miniSat(this.f, new MiniSatConfig.Builder().cnfMethod(MiniSatConfig.CNFMethod.PG_ON_SOLVER).auxiliaryVariablesInModels(false).build()); + this.solvers[7] = CleaneLing.minimalistic(this.f); + this.solvers[8] = CleaneLing.full(this.f, new CleaneLingConfig.Builder().plain(true).glueUpdate(true).gluered(true).build()); + this.solvers[9] = CleaneLing.full(this.f); - this.testStrings = new String[9]; + this.testStrings = new String[10]; this.testStrings[0] = "MiniSat{result=UNDEF, incremental=true}"; this.testStrings[1] = "MiniSat{result=UNDEF, incremental=false}"; this.testStrings[2] = "MiniSat{result=UNDEF, incremental=false}"; this.testStrings[3] = "MiniSat{result=UNDEF, incremental=true}"; this.testStrings[4] = "MiniSat{result=UNDEF, incremental=false}"; this.testStrings[5] = "MiniSat{result=UNDEF, incremental=true}"; - this.testStrings[6] = "CleaneLing{result=UNDEF, idx2name={}}"; + this.testStrings[6] = "MiniSat{result=UNDEF, incremental=true}"; this.testStrings[7] = "CleaneLing{result=UNDEF, idx2name={}}"; this.testStrings[8] = "CleaneLing{result=UNDEF, idx2name={}}"; + this.testStrings[9] = "CleaneLing{result=UNDEF, idx2name={}}"; } @Test @@ -356,7 +358,7 @@ public void testWithRelaxation() throws ParserException { @Test(expected = UnsupportedOperationException.class) public void testIllegalEnumeration() { - final SATSolver s = this.solvers[8]; + final SATSolver s = this.solvers[9]; final Variable[] lits = new Variable[100]; for (int j = 0; j < lits.length; j++) { lits[j] = this.f.variable("x" + j); @@ -561,7 +563,7 @@ public void testModelEnumeration() { } @Test - public void testModelEnumerationWithHandler() { + public void testModelEnumerationWithHandler01() { for (int i = 0; i < this.solvers.length - 1; i++) { final SATSolver s = this.solvers[i]; final SortedSet lits = new TreeSet<>(); @@ -587,6 +589,33 @@ public void testModelEnumerationWithHandler() { } } + @Test + public void testModelEnumerationWithHandler02() { + for (int i = 0; i < this.solvers.length - 1; i++) { + final SATSolver s = this.solvers[i]; + final SortedSet lits = new TreeSet<>(); + final SortedSet firstFive = new TreeSet<>(); + for (int j = 0; j < 20; j++) { + final Variable lit = this.f.variable("x" + j); + lits.add(lit); + if (j < 5) { + firstFive.add(lit); + } + } + s.add(this.f.cc(CType.GE, 1, lits)); + + final NumberOfModelsHandler handler = new NumberOfModelsHandler(29); + final List modelsWithHandler = s.enumerateAllModels(null, Collections.singletonList(firstFive.first()), handler); + Assert.assertEquals(29, modelsWithHandler.size()); + for (final Assignment model : modelsWithHandler) { + for (final Variable lit : lits) { + Assert.assertTrue(model.positiveLiterals().contains(lit) || model.negativeVariables().contains(lit)); + } + } + s.reset(); + } + } + @Test public void testEmptyEnumeration() { for (int i = 0; i < this.solvers.length - 1; i++) { From 77ba60a52278d0ddd34f6ce7c09bd81171027fc4 Mon Sep 17 00:00:00 2001 From: Christoph Zengler Date: Sat, 27 Jul 2019 15:49:48 +0200 Subject: [PATCH 05/16] Integrated backbone solver in MiniSat --- .../org/logicng/backbones/BackboneConfig.java | 140 ------ .../logicng/backbones/BackboneGeneration.java | 28 +- .../logicng/backbones/MiniSatBackbone.java | 399 ------------------ .../java/org/logicng/solvers/CleaneLing.java | 45 +- .../java/org/logicng/solvers/MiniSat.java | 20 +- .../java/org/logicng/solvers/SATSolver.java | 50 ++- .../logicng/solvers/sat/MiniSat2Solver.java | 268 +++++++++++- .../logicng/solvers/sat/MiniSatConfig.java | 70 +++ .../solvers/sat/MiniSatStyleSolver.java | 95 ++--- .../transformations/BackboneSimplifier.java | 10 +- .../backbones/BackboneGenerationTest.java | 124 +++--- .../solvers/sat/ConfigurationsTest.java | 4 + 12 files changed, 545 insertions(+), 708 deletions(-) delete mode 100644 src/main/java/org/logicng/backbones/BackboneConfig.java delete mode 100644 src/main/java/org/logicng/backbones/MiniSatBackbone.java diff --git a/src/main/java/org/logicng/backbones/BackboneConfig.java b/src/main/java/org/logicng/backbones/BackboneConfig.java deleted file mode 100644 index 08e6e67b..00000000 --- a/src/main/java/org/logicng/backbones/BackboneConfig.java +++ /dev/null @@ -1,140 +0,0 @@ -/////////////////////////////////////////////////////////////////////////// -// __ _ _ ________ // -// / / ____ ____ _(_)____/ | / / ____/ // -// / / / __ \/ __ `/ / ___/ |/ / / __ // -// / /___/ /_/ / /_/ / / /__/ /| / /_/ / // -// /_____/\____/\__, /_/\___/_/ |_/\____/ // -// /____/ // -// // -// The Next Generation Logic Library // -// // -/////////////////////////////////////////////////////////////////////////// -// // -// Copyright 2015-20xx Christoph Zengler // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or // -// implied. See the License for the specific language governing // -// permissions and limitations under the License. // -// // -/////////////////////////////////////////////////////////////////////////// - -package org.logicng.backbones; - -import org.logicng.configurations.Configuration; -import org.logicng.configurations.ConfigurationType; - -/** - * The configuration object for the Backbone generation. - * @version 1.5.0 - * @since 1.5.0 - */ -public class BackboneConfig extends Configuration { - - private final boolean initialUBCheckForRotatableLiterals; - private final boolean checkForComplementModelLiterals; - private final boolean checkForRotatableLiterals; - - /** - * Constructs a new backbone configuration from a given builder. - * @param builder the builder - */ - private BackboneConfig(final Builder builder) { - super(ConfigurationType.BACKBONE); - this.initialUBCheckForRotatableLiterals = builder.initialUBCheckForRotatableLiterals; - this.checkForComplementModelLiterals = builder.checkForComplementModelLiterals; - this.checkForRotatableLiterals = builder.checkForRotatableLiterals; - } - - /** - * Returns whether the algorithm should check for rotatable literals during initial unit propagation. - * @return whether the algorithm should check for rotatable literals during initial unit propagation - */ - public boolean isInitialUBCheckForRotatableLiterals() { - return this.initialUBCheckForRotatableLiterals; - } - - /** - * Returns whether the algorithm should check for complement model literals. - * @return whether the algorithm should check for complement model literals - */ - public boolean isCheckForComplementModelLiterals() { - return this.checkForComplementModelLiterals; - } - - /** - * Returns whether the algorithm should check for rotatable literals. - * @return whether the algorithm should check for rotatable literals - */ - public boolean isCheckForRotatableLiterals() { - return this.checkForRotatableLiterals; - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder("BackboneConfig{").append(System.lineSeparator()); - sb.append("initialUBCheckForRotatableLiterals=").append(this.initialUBCheckForRotatableLiterals).append(System.lineSeparator()); - sb.append("checkForComplementModelLiterals=").append(this.checkForComplementModelLiterals).append(System.lineSeparator()); - sb.append("checkForRotatableLiterals=").append(this.checkForRotatableLiterals).append(System.lineSeparator()); - sb.append("}").append(System.lineSeparator()); - return sb.toString(); - } - - /** - * The builder for a Backbone configuration. - */ - public static class Builder { - private boolean initialUBCheckForRotatableLiterals = true; - private boolean checkForComplementModelLiterals = true; - private boolean checkForRotatableLiterals = true; - - /** - * Sets whether the algorithm should check for rotatable literals. The default value is {@code true}. - * @param checkForRotatableLiterals the boolean value that is {@code true} if the algorithm should check for - * rotatables or {@code false} otherwise. - * @return the builder - */ - public BackboneConfig.Builder checkForRotatableLiterals(final boolean checkForRotatableLiterals) { - this.checkForRotatableLiterals = checkForRotatableLiterals; - return this; - } - - /** - * Sets whether the algorithm should check for rotatable literals during initial unit propagation. The default - * value is {@code true}. - * @param initialUBCheckForRotatableLiterals the boolean value that is {@code true} if the algorithm should - * check for rotatables or {@code false} otherwise. - * @return the builder - */ - public BackboneConfig.Builder initialUBCheckForRotatableLiterals(final boolean initialUBCheckForRotatableLiterals) { - this.initialUBCheckForRotatableLiterals = initialUBCheckForRotatableLiterals; - return this; - } - - /** - * Sets whether the algorithm should check for complement model literals. The default value is {@code true}. - * @param checkForComplementModelLiterals the boolean value that is {@code true} if the algorithm should check for - * complement literals or {@code false} otherwise. - * @return the builder - */ - public BackboneConfig.Builder checkForComplementModelLiterals(final boolean checkForComplementModelLiterals) { - this.checkForComplementModelLiterals = checkForComplementModelLiterals; - return this; - } - - /** - * Builds the configuration. - * @return the configuration. - */ - public BackboneConfig build() { - return new BackboneConfig(this); - } - } -} diff --git a/src/main/java/org/logicng/backbones/BackboneGeneration.java b/src/main/java/org/logicng/backbones/BackboneGeneration.java index c258190c..397a0689 100644 --- a/src/main/java/org/logicng/backbones/BackboneGeneration.java +++ b/src/main/java/org/logicng/backbones/BackboneGeneration.java @@ -29,7 +29,10 @@ package org.logicng.backbones; import org.logicng.formulas.Formula; +import org.logicng.formulas.FormulaFactory; import org.logicng.formulas.Variable; +import org.logicng.solvers.MiniSat; +import org.logicng.solvers.sat.MiniSatConfig; import org.logicng.util.FormulaHelper; import java.util.Collection; @@ -39,16 +42,11 @@ * Main entry point for backbone computations. *

    * This class provides convenient methods for backbone computation for many use cases. - * For more control over the backbone solver you can create an instance of - * {@link MiniSatBackbone} directly. E.g., with an instance of {@link MiniSatBackbone} - * the already loaded formulas can be re-used for multiple backbone computations. - * @version 1.5.1 + * @version 1.6.0 * @since 1.5.0 */ public class BackboneGeneration { - private static final MiniSatBackbone solver = new MiniSatBackbone(); - /** * Private constructor. */ @@ -56,14 +54,6 @@ private BackboneGeneration() { // Intentionally left empty. } - /** - * Sets a new backbone configuration. - * @param config the new backbone configuration - */ - public static void setConfig(final BackboneConfig config) { - solver.setConfig(config); - } - /** * Computes the backbone for a given collection of formulas w.r.t. a collection of variables and a backbone type. * @param formulas the given collection of formulas @@ -72,9 +62,13 @@ public static void setConfig(final BackboneConfig config) { * @return the backbone or {@code null} if the formula is UNSAT */ public static Backbone compute(final Collection formulas, final Collection variables, final BackboneType type) { - solver.reset(); - solver.add(formulas); - return solver.compute(variables, type); + if (formulas == null || formulas.isEmpty()) { + throw new IllegalArgumentException("Provide at least one formula for backbone computation"); + } + final FormulaFactory f = formulas.iterator().next().factory(); + final MiniSat miniSat = MiniSat.miniSat(f, new MiniSatConfig.Builder().fastBackboneComputation(true).build()); + miniSat.add(formulas); + return miniSat.computeBackbone(variables, type); } /** diff --git a/src/main/java/org/logicng/backbones/MiniSatBackbone.java b/src/main/java/org/logicng/backbones/MiniSatBackbone.java deleted file mode 100644 index 10e563a2..00000000 --- a/src/main/java/org/logicng/backbones/MiniSatBackbone.java +++ /dev/null @@ -1,399 +0,0 @@ -/////////////////////////////////////////////////////////////////////////// -// __ _ _ ________ // -// / / ____ ____ _(_)____/ | / / ____/ // -// / / / __ \/ __ `/ / ___/ |/ / / __ // -// / /___/ /_/ / /_/ / / /__/ /| / /_/ / // -// /_____/\____/\__, /_/\___/_/ |_/\____/ // -// /____/ // -// // -// The Next Generation Logic Library // -// // -/////////////////////////////////////////////////////////////////////////// -// // -// Copyright 2015-20xx Christoph Zengler // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or // -// implied. See the License for the specific language governing // -// permissions and limitations under the License. // -// // -/////////////////////////////////////////////////////////////////////////// - -package org.logicng.backbones; - -import org.logicng.collections.LNGIntVector; -import org.logicng.datastructures.Tristate; -import org.logicng.formulas.Formula; -import org.logicng.formulas.Variable; -import org.logicng.solvers.datastructures.MSClause; -import org.logicng.solvers.datastructures.MSVariable; -import org.logicng.solvers.datastructures.MSWatcher; -import org.logicng.solvers.sat.MiniSat2Solver; -import org.logicng.transformations.cnf.PlaistedGreenbaumTransformationSolver; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.SortedSet; -import java.util.Stack; -import java.util.TreeSet; - -/** - * An extension of MiniSat to compute the backbone of a formula. - *

    - * The algorithm iteratively checks each variable of an initial model (candidates) whether the variable - * is a backbone variable. For each check the SAT solving procedure is called. Thus, at the number of SAT calls is at - * most the number of variables. - *

    - * Reference: Algorithm 3 in M. Janota, I. Lynce, J. Marques-Silva, Algorithms for Computing Backbones of Propositional - * Formulae, AI Communications, Volume 28(2), 161-177, 2015. - * @version 1.5.1 - * @since 1.5.0 - */ -public class MiniSatBackbone extends MiniSat2Solver { - - private BackboneConfig config; - private final PlaistedGreenbaumTransformationSolver pgTransformation = new PlaistedGreenbaumTransformationSolver(this, true); - - /** - * Type of the backbone computation. - */ - private BackboneType type; - - /** - * Candidates to test for backbone. - *

    - * The integers are solver internal literal integers. - */ - private Stack candidates; - - /** - * Assumptions used to call the solver with, filled by identified backbone literals. - */ - private LNGIntVector assumptions; - - /** - * Backbone map: integer literal -> Tristate. - *

      - *
    • {@link Tristate#TRUE} is a positive backbone variable - *
    • {@link Tristate#FALSE} is a negative backbone variable - *
    • {@link Tristate#UNDEF} is an optional variable - *
    - */ - private HashMap backboneMap; - - /** - * Creates a new backbone solver using the given configuration. - * @param config configuration - */ - public MiniSatBackbone(final BackboneConfig config) { - this.config = config; - } - - /** - * Creates a new backbone solver using the default configuration. - */ - public MiniSatBackbone() { - this(new BackboneConfig.Builder().build()); - } - - /** - * Sets a new backbone configuration. - * @param newConfig the new {@link BackboneConfig} - */ - public void setConfig(final BackboneConfig newConfig) { - this.config = newConfig; - } - - /** - * Adds an arbitrary formula to the solver. - * @param formula the formula - */ - public void add(final Formula formula) { - this.pgTransformation.addCNFtoSolver(formula, null); - } - - /** - * Adds the given arbitrary formulas to the solver. - * @param formulas the formulas - */ - public void add(final Collection formulas) { - for (final Formula formula : formulas) { - add(formula); - } - } - - /** - * Returns a list of relevant variable indices. A relevant variable is known by the solver. - * @param variables variables to convert and filter - * @return list of relevant variable indices - */ - private List getRelevantVarIndices(final Collection variables) { - final List relevantVarIndices = new ArrayList<>(variables.size()); - for (final Variable var : variables) { - final Integer idx = this.name2idx.get(var.name()); - // Note: Unknown variables are variables added to the solver yet. Thus, these are optional variables and can - // be left out for the backbone computation. - if (idx != null) { - relevantVarIndices.add(idx); - } - } - return relevantVarIndices; - } - - /** - * Initializes the internal solver state. - * @param type backbone type - * @param variables to test - */ - private void init(final List variables, final BackboneType type) { - this.type = type; - this.candidates = new Stack<>(); - this.assumptions = new LNGIntVector(variables.size()); - this.backboneMap = new HashMap<>(); - for (final Integer var : variables) { - this.backboneMap.put(var, Tristate.UNDEF); - } - } - - /** - * Tests if positive backbone literals should be computed. - * @return {@code true} if positive backbone literals should be computed, otherwise {@code false} - */ - private boolean isBothOrPositiveType() { - return this.type == BackboneType.POSITIVE_AND_NEGATIVE || this.type == BackboneType.ONLY_POSITIVE; - } - - /** - * Tests if negative backbone literals should be computed. - * @return {@code true} if negative backbone literals should be computed, otherwise {@code false} - */ - private boolean isBothOrNegativeType() { - return this.type == BackboneType.POSITIVE_AND_NEGATIVE || this.type == BackboneType.ONLY_NEGATIVE; - } - - /** - * Tests if positive and negative backbone literals should be computed. - * @return {@code true} if positive and negative backbone literals should be computed, otherwise {@code false} - */ - private boolean isBothType() { - return this.type == BackboneType.POSITIVE_AND_NEGATIVE; - } - - /** - * Builds the backbone object from the computed backbone literals. - * @param variables relevant variables - * @return backbone - */ - private Backbone buildBackbone(final Collection variables) { - final SortedSet posBackboneVars = isBothOrPositiveType() ? new TreeSet() : null; - final SortedSet negBackboneVars = isBothOrNegativeType() ? new TreeSet() : null; - final SortedSet optionalVars = isBothType() ? new TreeSet() : null; - for (final Variable var : variables) { - final Integer idx = this.name2idx.get(var.name()); - if (idx == null) { - if (isBothType()) { - optionalVars.add(var); - } - } else { - switch (this.backboneMap.get(idx)) { - case TRUE: - if (isBothOrPositiveType()) { - posBackboneVars.add(var); - } - break; - case FALSE: - if (isBothOrNegativeType()) { - negBackboneVars.add(var); - } - break; - case UNDEF: - if (isBothType()) { - optionalVars.add(var); - } - break; - default: - throw new IllegalStateException("Unknown tristate: " + this.backboneMap.get(idx)); - } - } - } - return Backbone.satBackbone(posBackboneVars, negBackboneVars, optionalVars); - } - - /** - * Computes the backbone of the given variables with respect to the formulas added to the solver. - * @param variables variables to test - * @param type backbone type - * @return the backbone projected to the relevant variables or {@code null} if the formula on the solver with the restrictions are not satisfiable - */ - public Backbone compute(final Collection variables, final BackboneType type) { - final boolean sat = solve(null) == Tristate.TRUE; - if (sat) { - final List relevantVarIndices = getRelevantVarIndices(variables); - init(relevantVarIndices, type); - compute(relevantVarIndices); - return buildBackbone(variables); - } else { - return Backbone.unsatBackbone(); - } - } - - /** - * Tests the given variable whether it is a unit propagated literal on level 0. - *

    - * Assumption: The formula on the solver has successfully been tested to be satisfiable before. - * @param var variable index to test - * @return {@code true} if the variable is a unit propagated literal on level 0, otherwise {@code false} - */ - private boolean isUPZeroLit(final int var) { - return this.vars.get(var).level() == 0; - } - - /** - * Tests the given literal whether it is unit in the given clause. - * @param lit literal to test - * @param clause clause containing the literal - * @return {@code true} if the literal is unit, {@code false} otherwise - */ - private boolean isUnit(final int lit, final MSClause clause) { - for (int i = 0; i < clause.size(); ++i) { - final int clauseLit = clause.get(i); - if (lit != clauseLit && this.model.get(var(clauseLit)) != sign(clauseLit)) { - return false; - } - } - return true; - } - - /** - * Tests the given literal whether it is rotatable in the current model. - * @param lit literal to test - * @return {@code true} if the literal is rotatable, otherwise {@code false} - */ - private boolean isRotatable(final int lit) { - // A rotatable literal MUST NOT be a unit propagated literal - if (v(lit).reason() != null) { - return false; - } - // A rotatable literal MUST NOT be unit - for (final MSWatcher watcher : this.watches.get(not(lit))) { - if (isUnit(lit, watcher.clause())) { - return false; - } - } - return true; - } - - /** - * Adds the given literal to the backbone result and optionally adds the literal to the solver. - * @param lit literal to add - */ - private void addBackboneLiteral(final int lit) { - this.backboneMap.put(var(lit), sign(lit) ? Tristate.FALSE : Tristate.TRUE); - this.assumptions.push(lit); - } - - /** - * Creates the initial candidate literals for the backbone computation. - * @param variables variables to test - * @return initial candidates - */ - private Stack createInitialCandidates(final List variables) { - for (final Integer var : variables) { - if (isUPZeroLit(var)) { - final int backboneLit = mkLit(var, !this.model.get(var)); - addBackboneLiteral(backboneLit); - } else { - final boolean modelPhase = this.model.get(var); - if (isBothOrNegativeType() && !modelPhase || isBothOrPositiveType() && modelPhase) { - final int lit = mkLit(var, !modelPhase); - if (!this.config.isInitialUBCheckForRotatableLiterals() || !isRotatable(lit)) { - this.candidates.add(lit); - } - } - } - } - return this.candidates; - } - - /** - * Refines the upper bound by optional checks (UP zero literal, complement model literal, rotatable literal). - */ - private void refineUpperBound() { - for (final Integer lit : new ArrayList<>(this.candidates)) { - final int var = var(lit); - if (isUPZeroLit(var)) { - this.candidates.remove(lit); - addBackboneLiteral(lit); - } else if (this.config.isCheckForComplementModelLiterals() && this.model.get(var) == sign(lit)) { - this.candidates.remove(lit); - } else if (this.config.isCheckForRotatableLiterals() && isRotatable(lit)) { - this.candidates.remove(lit); - } - } - } - - /** - * Tests the given literal with the formula on the solver for satisfiability. - * @param lit literal to test - * @return {@code true} if satisfiable, otherwise {@code false} - */ - private boolean solve(final int lit) { - this.assumptions.push(not(lit)); - final boolean sat = solve(null, this.assumptions) == Tristate.TRUE; - this.assumptions.pop(); - return sat; - } - - @Override - protected void cancelUntil(final int level) { - if (decisionLevel() > level) { - for (int c = this.trail.size() - 1; c >= this.trailLim.get(level); c--) { - final int x = var(this.trail.get(c)); - final MSVariable v = this.vars.get(x); - v.assign(Tristate.UNDEF); - v.setPolarity(false); - insertVarOrder(x); - } - this.qhead = this.trailLim.get(level); - this.trail.removeElements(this.trail.size() - this.trailLim.get(level)); - this.trailLim.removeElements(this.trailLim.size() - level); - } - } - - /** - * Computes the backbone for the given variables. - * @param variables variables to test - */ - private void compute(final List variables) { - final Stack candidates = createInitialCandidates(variables); - while (candidates.size() > 0) { - final int lit = candidates.pop(); - if (solve(lit)) { - refineUpperBound(); - } else { - addBackboneLiteral(lit); - } - } - } - - @Override - public void reset() { - super.reset(); - this.pgTransformation.clearCache(); - } - - @Override - public void loadState(final int[] state) { - super.loadState(state); - this.pgTransformation.clearCache(); - } -} diff --git a/src/main/java/org/logicng/solvers/CleaneLing.java b/src/main/java/org/logicng/solvers/CleaneLing.java index d75da58e..ff945a43 100644 --- a/src/main/java/org/logicng/solvers/CleaneLing.java +++ b/src/main/java/org/logicng/solvers/CleaneLing.java @@ -32,6 +32,8 @@ import static org.logicng.datastructures.Tristate.TRUE; import static org.logicng.datastructures.Tristate.UNDEF; +import org.logicng.backbones.Backbone; +import org.logicng.backbones.BackboneType; import org.logicng.cardinalityconstraints.CCEncoder; import org.logicng.cardinalityconstraints.CCIncrementalData; import org.logicng.collections.LNGBooleanVector; @@ -156,20 +158,30 @@ public void add(final Formula formula, final Proposition proposition) { if (constraint.isCC()) { final EncodingResult result = EncodingResult.resultForCleaneLing(this.f, this); this.ccEncoder.encode(constraint, result); - } else { this.addClauseSet(formula.cnf(), proposition); } - } else { this.addClauseSet(formula.cnf(), proposition); } + } else { + this.addClauseSet(formula.cnf(), proposition); + } + } else { + this.addClauseSet(formula.cnf(), proposition); + } } @Override public void addWithoutUnknown(final Formula formula) { final Assignment restriction = new Assignment(true); - for (final Variable var : formula.variables()) { if (this.name2idx.get(var.name()) == null) { restriction.addLiteral(var.negate()); } } + for (final Variable var : formula.variables()) { + if (this.name2idx.get(var.name()) == null) { + restriction.addLiteral(var.negate()); + } + } this.add(formula.restrict(restriction)); } @Override public CCIncrementalData addIncrementalCC(final PBConstraint cc) { - if (!cc.isCC()) { throw new IllegalArgumentException("Cannot generate an incremental cardinality constraint on a pseudo-Boolean constraint"); } + if (!cc.isCC()) { + throw new IllegalArgumentException("Cannot generate an incremental cardinality constraint on a pseudo-Boolean constraint"); + } final EncodingResult result = EncodingResult.resultForCleaneLing(this.f, this); return this.ccEncoder.encodeIncremental(cc, result); } @@ -190,7 +202,9 @@ protected void addClauseWithRelaxation(final Variable relaxationVar, final Formu @Override public Tristate sat(final SATHandler handler) { - if (this.result != UNDEF) { return this.result; } + if (this.result != UNDEF) { + return this.result; + } this.result = this.solver.solve(handler); return this.result; } @@ -213,7 +227,9 @@ public void reset() { @Override public Assignment model(final Collection variables) { - if (this.result == UNDEF) { throw new IllegalStateException("Cannot get a model as long as the formula is not solved. Call 'sat' first."); } + if (this.result == UNDEF) { + throw new IllegalStateException("Cannot get a model as long as the formula is not solved. Call 'sat' first."); + } return this.result == TRUE ? this.createAssignment(this.solver.model(), variables) : null; } @@ -286,7 +302,9 @@ public void loadState(final SolverState state) { @Override public SortedSet knownVariables() { final SortedSet result = new TreeSet<>(); - for (final String name : this.name2idx.keySet()) { result.add(this.f.variable(name)); } + for (final String name : this.name2idx.keySet()) { + result.add(this.f.variable(name)); + } return result; } @@ -295,6 +313,11 @@ public UNSATCore unsatCore() { throw new UnsupportedOperationException("CleaneLing cannot compute unsat cores at the moment"); } + @Override + public Backbone computeBackbone(final Collection relevantVariables, final BackboneType type) { + throw new UnsupportedOperationException("CleaneLing cannot compute fast backbones at the moment"); + } + /** * Adds a collection of literals to the solver. * @param literals the literals @@ -324,8 +347,12 @@ private Assignment createAssignment(final LNGBooleanVector vec, final Collection for (int i = 1; i < vec.size(); i++) { final Variable var = this.f.variable(this.idx2name.get(i)); if (vec.get(i)) { - if (variables == null || variables.contains(var)) { model.addLiteral(var); } - } else if (variables == null || variables.contains(var)) { model.addLiteral(var.negate()); } + if (variables == null || variables.contains(var)) { + model.addLiteral(var); + } + } else if (variables == null || variables.contains(var)) { + model.addLiteral(var.negate()); + } } return model; } diff --git a/src/main/java/org/logicng/solvers/MiniSat.java b/src/main/java/org/logicng/solvers/MiniSat.java index 03e995cb..224e544c 100644 --- a/src/main/java/org/logicng/solvers/MiniSat.java +++ b/src/main/java/org/logicng/solvers/MiniSat.java @@ -32,6 +32,8 @@ import static org.logicng.datastructures.Tristate.TRUE; import static org.logicng.datastructures.Tristate.UNDEF; +import org.logicng.backbones.Backbone; +import org.logicng.backbones.BackboneType; import org.logicng.cardinalityconstraints.CCEncoder; import org.logicng.cardinalityconstraints.CCIncrementalData; import org.logicng.collections.LNGBooleanVector; @@ -371,11 +373,11 @@ public List enumerateAllModels(final Collection variables, } LNGIntVector relevantAllIndices = null; if (relevantIndices != null) { - if(additionalVariables.isEmpty()) { + if (additionalVariables.isEmpty()) { relevantAllIndices = relevantIndices; } else { relevantAllIndices = new LNGIntVector(relevantIndices.size() + additionalVariables.size()); - for(int i = 0; i < relevantIndices.size(); ++i) { + for (int i = 0; i < relevantIndices.size(); ++i) { relevantAllIndices.push(relevantIndices.get(i)); } for (final Variable var : additionalVariables) { @@ -516,6 +518,20 @@ public UNSATCore unsatCore() { return new UNSATCore<>(new ArrayList<>(propositions), false); } + @Override + public Backbone computeBackbone(final Collection relevantVariables, final BackboneType type) { + if (!this.config.isFastBackboneComputation()) { + throw new UnsupportedOperationException("Cannot compute a backbone if fast backbone computation is not turned on"); + } + if (!(this.solver instanceof MiniSat2Solver)) { + throw new UnsupportedOperationException("Only the original MiniSat can compute fast backbones"); + } + final SolverState state = saveState(); + final Backbone backbone = ((MiniSat2Solver) this.solver).computeBackbone(relevantVariables, type); + loadState(state); + return backbone; + } + /** * Generates a clause vector of a collection of literals. * @param literals the literals diff --git a/src/main/java/org/logicng/solvers/SATSolver.java b/src/main/java/org/logicng/solvers/SATSolver.java index 4eb8a56d..d4e479cc 100644 --- a/src/main/java/org/logicng/solvers/SATSolver.java +++ b/src/main/java/org/logicng/solvers/SATSolver.java @@ -28,6 +28,8 @@ package org.logicng.solvers; +import org.logicng.backbones.Backbone; +import org.logicng.backbones.BackboneType; import org.logicng.cardinalityconstraints.CCIncrementalData; import org.logicng.collections.ImmutableFormulaList; import org.logicng.datastructures.Assignment; @@ -91,7 +93,9 @@ public void add(final Formula formula) { * @param propositions the set of propositions */ public void addPropositions(final Collection propositions) { - for (final Proposition proposition : propositions) { add(proposition); } + for (final Proposition proposition : propositions) { + add(proposition); + } } /** @@ -99,7 +103,9 @@ public void addPropositions(final Collection propositions * @param propositions the set of propositions */ public void addPropositions(final Proposition... propositions) { - for (final Proposition proposition : propositions) { add(proposition); } + for (final Proposition proposition : propositions) { + add(proposition); + } } /** @@ -107,7 +113,9 @@ public void addPropositions(final Proposition... propositions) { * @param proposition the proposition */ public void add(final Proposition proposition) { - for (final Formula formula : proposition.formulas()) { this.add(formula, proposition); } + for (final Formula formula : proposition.formulas()) { + this.add(formula, proposition); + } } /** @@ -115,7 +123,9 @@ public void add(final Proposition proposition) { * @param formulas the formula list */ public void add(final ImmutableFormulaList formulas) { - for (final Formula formula : formulas) { this.add(formula); } + for (final Formula formula : formulas) { + this.add(formula); + } } /** @@ -123,7 +133,9 @@ public void add(final ImmutableFormulaList formulas) { * @param formulas the collection of formulas */ public void add(final Collection formulas) { - for (final Formula formula : formulas) { this.add(formula); } + for (final Formula formula : formulas) { + this.add(formula); + } } /** @@ -141,7 +153,9 @@ public void addWithRelaxation(final Variable relaxationVar, final Formula formul * @param proposition the proposition */ public void addWithRelaxation(final Variable relaxationVar, final Proposition proposition) { - for (final Formula formula : proposition.formulas()) { this.addWithRelaxation(relaxationVar, formula); } + for (final Formula formula : proposition.formulas()) { + this.addWithRelaxation(relaxationVar, formula); + } } /** @@ -150,7 +164,9 @@ public void addWithRelaxation(final Variable relaxationVar, final Proposition pr * @param formulas the formula list */ public void addWithRelaxation(final Variable relaxationVar, final ImmutableFormulaList formulas) { - for (final Formula formula : formulas) { this.addWithRelaxation(relaxationVar, formula); } + for (final Formula formula : formulas) { + this.addWithRelaxation(relaxationVar, formula); + } } /** @@ -159,7 +175,9 @@ public void addWithRelaxation(final Variable relaxationVar, final ImmutableFormu * @param formulas the collection of formulas */ public void addWithRelaxation(final Variable relaxationVar, final Collection formulas) { - for (final Formula formula : formulas) { this.addWithRelaxation(relaxationVar, formula); } + for (final Formula formula : formulas) { + this.addWithRelaxation(relaxationVar, formula); + } } /** @@ -190,7 +208,9 @@ void addClauseSet(final Formula formula, final Proposition proposition) { this.addClause(formula, proposition); break; case AND: - for (final Formula op : formula) { this.addClause(op, proposition); } + for (final Formula op : formula) { + this.addClause(op, proposition); + } break; default: throw new IllegalArgumentException("Input formula ist not a valid CNF: " + formula); @@ -212,7 +232,9 @@ private void addClauseSetWithRelaxation(final Variable relaxationVar, final Form this.addClauseWithRelaxation(relaxationVar, formula); break; case AND: - for (final Formula op : formula) { this.addClauseWithRelaxation(relaxationVar, op); } + for (final Formula op : formula) { + this.addClauseWithRelaxation(relaxationVar, op); + } break; default: throw new IllegalArgumentException("Input formula ist not a valid CNF: " + formula); @@ -442,6 +464,14 @@ public void setSolverToUndef() { */ public abstract UNSATCore unsatCore(); + /** + * Computes a backbone of the current formula on the solver. + * @param relevantVariables the variables which should be considered for the backbone + * @param type the type of backbone which should be computed + * @return the backbone + */ + public abstract Backbone computeBackbone(final Collection relevantVariables, final BackboneType type); + /** * Returns all unit propagated literals on level 0 of the current formula on the solver. * If the formula is UNSAT, {@code null} will be returned. diff --git a/src/main/java/org/logicng/solvers/sat/MiniSat2Solver.java b/src/main/java/org/logicng/solvers/sat/MiniSat2Solver.java index 0f39cd66..9191a117 100644 --- a/src/main/java/org/logicng/solvers/sat/MiniSat2Solver.java +++ b/src/main/java/org/logicng/solvers/sat/MiniSat2Solver.java @@ -44,16 +44,27 @@ package org.logicng.solvers.sat; +import org.logicng.backbones.Backbone; +import org.logicng.backbones.BackboneType; import org.logicng.collections.LNGBooleanVector; import org.logicng.collections.LNGIntVector; import org.logicng.collections.LNGVector; import org.logicng.datastructures.Tristate; +import org.logicng.formulas.Variable; import org.logicng.handlers.SATHandler; import org.logicng.propositions.Proposition; import org.logicng.solvers.datastructures.MSClause; import org.logicng.solvers.datastructures.MSVariable; import org.logicng.solvers.datastructures.MSWatcher; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.SortedSet; +import java.util.Stack; +import java.util.TreeSet; + /** * A solver based on MiniSAT 2.2.0. If the incremental mode is deactivated, this version should behave exactly * like the C++ version. @@ -74,6 +85,11 @@ public class MiniSat2Solver extends MiniSatStyleSolver { private double learntsizeAdjustInc; private double maxLearnts; + private Stack backboneCandidates; + private LNGIntVector backboneAssumptions; + private HashMap backboneMap; + private boolean computingBackbone; + /** * Constructs a new MiniSAT 2 solver with the default values for solver configuration. By default, incremental mode * is activated. @@ -101,6 +117,7 @@ private void initializeMiniSAT() { this.learntsizeAdjustStartConfl = 100; this.learntsizeAdjustInc = 1.5; this.maxLearnts = 0; + this.computingBackbone = false; } @Override @@ -478,12 +495,22 @@ protected void analyzeFinal(final int p, final LNGIntVector outConflict) { @Override protected void cancelUntil(final int level) { if (decisionLevel() > level) { - for (int c = this.trail.size() - 1; c >= this.trailLim.get(level); c--) { - final int x = var(this.trail.get(c)); - final MSVariable v = this.vars.get(x); - v.assign(Tristate.UNDEF); - v.setPolarity(sign(this.trail.get(c))); - insertVarOrder(x); + if (!this.config.fastBackboneComputation) { + for (int c = this.trail.size() - 1; c >= this.trailLim.get(level); c--) { + final int x = var(this.trail.get(c)); + final MSVariable v = this.vars.get(x); + v.assign(Tristate.UNDEF); + v.setPolarity(sign(this.trail.get(c))); + insertVarOrder(x); + } + } else { + for (int c = this.trail.size() - 1; c >= this.trailLim.get(level); c--) { + final int x = var(this.trail.get(c)); + final MSVariable v = this.vars.get(x); + v.assign(Tristate.UNDEF); + v.setPolarity(!this.computingBackbone && sign(this.trail.get(c))); + insertVarOrder(x); + } } this.qhead = this.trailLim.get(level); this.trail.removeElements(this.trail.size() - this.trailLim.get(level)); @@ -776,4 +803,233 @@ private void simpleRemoveClause(final MSClause c) { this.watches.get(not(c.get(0))).remove(new MSWatcher(c, c.get(1))); this.watches.get(not(c.get(1))).remove(new MSWatcher(c, c.get(0))); } + + ///// Backbone Stuff ///// + + /** + * Computes the backbone of the given variables with respect to the formulas added to the solver. + * @param variables variables to test + * @param type backbone type + * @return the backbone projected to the relevant variables or {@code null} if the formula on the solver with the restrictions are not satisfiable + */ + public Backbone computeBackbone(final Collection variables, final BackboneType type) { + final boolean sat = solve(null) == Tristate.TRUE; + if (sat) { + this.computingBackbone = true; + final List relevantVarIndices = getRelevantVarIndices(variables); + initBackboneDS(relevantVarIndices); + computeBackbone(relevantVarIndices, type); + final Backbone backbone = buildBackbone(variables, type); + this.computingBackbone = false; + return backbone; + } else { + return Backbone.unsatBackbone(); + } + } + + /** + * Returns a list of relevant variable indices. A relevant variable is known by the solver. + * @param variables variables to convert and filter + * @return list of relevant variable indices + */ + private List getRelevantVarIndices(final Collection variables) { + final List relevantVarIndices = new ArrayList<>(variables.size()); + for (final Variable var : variables) { + final Integer idx = this.name2idx.get(var.name()); + // Note: Unknown variables are variables added to the solver yet. Thus, these are optional variables and can + // be left out for the backbone computation. + if (idx != null) { + relevantVarIndices.add(idx); + } + } + return relevantVarIndices; + } + + /** + * Initializes the internal solver state for backbones. + * @param variables to test + */ + private void initBackboneDS(final List variables) { + this.backboneCandidates = new Stack<>(); + this.backboneAssumptions = new LNGIntVector(variables.size()); + this.backboneMap = new HashMap<>(); + for (final Integer var : variables) { + this.backboneMap.put(var, Tristate.UNDEF); + } + } + + /** + * Computes the backbone for the given variables. + * @param variables variables to test + */ + private void computeBackbone(final List variables, final BackboneType type) { + final Stack candidates = createInitialCandidates(variables, type); + while (candidates.size() > 0) { + final int lit = candidates.pop(); + if (solveWithLit(lit)) { + refineUpperBound(); + } else { + addBackboneLiteral(lit); + } + } + } + + /** + * Creates the initial candidate literals for the backbone computation. + * @param variables variables to test + * @return initial candidates + */ + private Stack createInitialCandidates(final List variables, final BackboneType type) { + for (final Integer var : variables) { + if (isUPZeroLit(var)) { + final int backboneLit = mkLit(var, !this.model.get(var)); + addBackboneLiteral(backboneLit); + } else { + final boolean modelPhase = this.model.get(var); + if (isBothOrNegativeType(type) && !modelPhase || isBothOrPositiveType(type) && modelPhase) { + final int lit = mkLit(var, !modelPhase); + if (!this.config.bbInitialUBCheckForRotatableLiterals || !isRotatable(lit)) { + this.backboneCandidates.add(lit); + } + } + } + } + return this.backboneCandidates; + } + + /** + * Refines the upper bound by optional checks (UP zero literal, complement model literal, rotatable literal). + */ + private void refineUpperBound() { + for (final Integer lit : new ArrayList<>(this.backboneCandidates)) { + final int var = var(lit); + if (isUPZeroLit(var)) { + this.backboneCandidates.remove(lit); + addBackboneLiteral(lit); + } else if (this.config.bbCheckForComplementModelLiterals && this.model.get(var) == sign(lit)) { + this.backboneCandidates.remove(lit); + } else if (this.config.bbCheckForRotatableLiterals && isRotatable(lit)) { + this.backboneCandidates.remove(lit); + } + } + } + + /** + * Tests the given literal with the formula on the solver for satisfiability. + * @param lit literal to test + * @return {@code true} if satisfiable, otherwise {@code false} + */ + private boolean solveWithLit(final int lit) { + this.backboneAssumptions.push(not(lit)); + final boolean sat = solve(null, this.backboneAssumptions) == Tristate.TRUE; + this.backboneAssumptions.pop(); + return sat; + } + + /** + * Builds the backbone object from the computed backbone literals. + * @param variables relevant variables + * @return backbone + */ + private Backbone buildBackbone(final Collection variables, final BackboneType type) { + final SortedSet posBackboneVars = isBothOrPositiveType(type) ? new TreeSet() : null; + final SortedSet negBackboneVars = isBothOrNegativeType(type) ? new TreeSet() : null; + final SortedSet optionalVars = isBothType(type) ? new TreeSet() : null; + for (final Variable var : variables) { + final Integer idx = this.name2idx.get(var.name()); + if (idx == null) { + if (isBothType(type)) { + optionalVars.add(var); + } + } else { + switch (this.backboneMap.get(idx)) { + case TRUE: + if (isBothOrPositiveType(type)) { + posBackboneVars.add(var); + } + break; + case FALSE: + if (isBothOrNegativeType(type)) { + negBackboneVars.add(var); + } + break; + case UNDEF: + if (isBothType(type)) { + optionalVars.add(var); + } + break; + default: + throw new IllegalStateException("Unknown tristate: " + this.backboneMap.get(idx)); + } + } + } + return Backbone.satBackbone(posBackboneVars, negBackboneVars, optionalVars); + } + + /** + * Tests the given variable whether it is a unit propagated literal on level 0. + *

    + * Assumption: The formula on the solver has successfully been tested to be satisfiable before. + * @param var variable index to test + * @return {@code true} if the variable is a unit propagated literal on level 0, otherwise {@code false} + */ + private boolean isUPZeroLit(final int var) { + return this.vars.get(var).level() == 0; + } + + /** + * Tests the given literal whether it is unit in the given clause. + * @param lit literal to test + * @param clause clause containing the literal + * @return {@code true} if the literal is unit, {@code false} otherwise + */ + private boolean isUnit(final int lit, final MSClause clause) { + for (int i = 0; i < clause.size(); ++i) { + final int clauseLit = clause.get(i); + if (lit != clauseLit && this.model.get(var(clauseLit)) != sign(clauseLit)) { + return false; + } + } + return true; + } + + /** + * Tests the given literal whether it is rotatable in the current model. + * @param lit literal to test + * @return {@code true} if the literal is rotatable, otherwise {@code false} + */ + private boolean isRotatable(final int lit) { + // A rotatable literal MUST NOT be a unit propagated literal + if (v(lit).reason() != null) { + return false; + } + // A rotatable literal MUST NOT be unit + for (final MSWatcher watcher : this.watches.get(not(lit))) { + if (isUnit(lit, watcher.clause())) { + return false; + } + } + return true; + } + + /** + * Adds the given literal to the backbone result and optionally adds the literal to the solver. + * @param lit literal to add + */ + private void addBackboneLiteral(final int lit) { + this.backboneMap.put(var(lit), sign(lit) ? Tristate.FALSE : Tristate.TRUE); + this.backboneAssumptions.push(lit); + } + + private boolean isBothOrPositiveType(final BackboneType type) { + return type == BackboneType.POSITIVE_AND_NEGATIVE || type == BackboneType.ONLY_POSITIVE; + } + + private boolean isBothOrNegativeType(final BackboneType type) { + return type == BackboneType.POSITIVE_AND_NEGATIVE || type == BackboneType.ONLY_NEGATIVE; + } + + private boolean isBothType(final BackboneType type) { + return type == BackboneType.POSITIVE_AND_NEGATIVE; + } } diff --git a/src/main/java/org/logicng/solvers/sat/MiniSatConfig.java b/src/main/java/org/logicng/solvers/sat/MiniSatConfig.java index 0ff493db..330567ee 100644 --- a/src/main/java/org/logicng/solvers/sat/MiniSatConfig.java +++ b/src/main/java/org/logicng/solvers/sat/MiniSatConfig.java @@ -84,6 +84,10 @@ public enum CNFMethod { final boolean proofGeneration; final CNFMethod cnfMethod; final boolean auxiliaryVariablesInModels; + final boolean fastBackboneComputation; + final boolean bbInitialUBCheckForRotatableLiterals; + final boolean bbCheckForComplementModelLiterals; + final boolean bbCheckForRotatableLiterals; /** * Constructs a new MiniSAT configuration from a given builder. @@ -105,6 +109,10 @@ private MiniSatConfig(final Builder builder) { this.proofGeneration = builder.proofGeneration; this.cnfMethod = builder.cnfMethod; this.auxiliaryVariablesInModels = builder.auxiliaryVariablesInModels; + this.fastBackboneComputation = builder.fastBackboneComputation; + this.bbInitialUBCheckForRotatableLiterals = builder.bbInitialUBCheckForRotatableLiterals; + this.bbCheckForComplementModelLiterals = builder.bbCheckForComplementModelLiterals; + this.bbCheckForRotatableLiterals = builder.bbCheckForRotatableLiterals; } /** @@ -147,6 +155,14 @@ public boolean isAuxiliaryVariablesInModels() { return this.auxiliaryVariablesInModels; } + /** + * Returns whether fast backbone computation is activated or not. + * @return whether fast backbone computation is activated or not + */ + public boolean isFastBackboneComputation() { + return this.fastBackboneComputation; + } + @Override public String toString() { final StringBuilder sb = new StringBuilder("MiniSatConfig{").append(System.lineSeparator()); @@ -164,6 +180,10 @@ public String toString() { sb.append("proofGeneration=").append(this.proofGeneration).append(System.lineSeparator()); sb.append("cnfMethod=").append(this.cnfMethod).append(System.lineSeparator()); sb.append("auxiliaryVariablesInModels=").append(this.auxiliaryVariablesInModels).append(System.lineSeparator()); + sb.append("fastBackboneComputation=").append(this.fastBackboneComputation).append(System.lineSeparator()); + sb.append("bbInitialUBCheckForRotatableLiterals=").append(this.bbInitialUBCheckForRotatableLiterals).append(System.lineSeparator()); + sb.append("bbCheckForComplementModelLiterals=").append(this.bbCheckForComplementModelLiterals).append(System.lineSeparator()); + sb.append("bbCheckForRotatableLiterals=").append(this.bbCheckForRotatableLiterals).append(System.lineSeparator()); sb.append("}").append(System.lineSeparator()); return sb.toString(); } @@ -186,6 +206,10 @@ public static class Builder { private boolean proofGeneration = false; CNFMethod cnfMethod = FACTORY_CNF; boolean auxiliaryVariablesInModels = true; + private boolean fastBackboneComputation = false; + private boolean bbInitialUBCheckForRotatableLiterals = true; + private boolean bbCheckForComplementModelLiterals = true; + private boolean bbCheckForRotatableLiterals = true; /** * Sets the variable activity decay factor to a given value. The default value is 0.95. @@ -338,6 +362,52 @@ public Builder auxiliaryVariablesInModels(final boolean auxiliaryVariablesInMode return this; } + /** + * Sets whether fast backbone computation is turned on or off. The default value is {@code false}. + * @param fastBackboneComputation {@code true} if fast backbone generation is on, {@code false} otherwise + * @return the builder + */ + public Builder fastBackboneComputation(final boolean fastBackboneComputation) { + this.fastBackboneComputation = fastBackboneComputation; + return this; + } + + /** + * Sets whether the backbone algorithm should check for rotatable literals. + * The default value is {@code true}. + * @param checkForRotatableLiterals the boolean value that is {@code true} if the algorithm should check for + * rotatables or {@code false} otherwise. + * @return the builder + */ + public Builder bbCheckForRotatableLiterals(final boolean checkForRotatableLiterals) { + this.bbCheckForRotatableLiterals = checkForRotatableLiterals; + return this; + } + + /** + * Sets whether the backbone algorithm should check for rotatable literals during initial unit propagation. + * The default value is {@code true}. + * @param initialUBCheckForRotatableLiterals the boolean value that is {@code true} if the algorithm should + * check for rotatables or {@code false} otherwise. + * @return the builder + */ + public Builder bbInitialUBCheckForRotatableLiterals(final boolean initialUBCheckForRotatableLiterals) { + this.bbInitialUBCheckForRotatableLiterals = initialUBCheckForRotatableLiterals; + return this; + } + + /** + * Sets whether the backbone algorithm should check for complement model literals. + * The default value is {@code true}. + * @param checkForComplementModelLiterals the boolean value that is {@code true} if the algorithm should check for + * complement literals or {@code false} otherwise. + * @return the builder + */ + public Builder bbCheckForComplementModelLiterals(final boolean checkForComplementModelLiterals) { + this.bbCheckForComplementModelLiterals = checkForComplementModelLiterals; + return this; + } + /** * Builds the MiniSAT configuration. * @return the configuration diff --git a/src/main/java/org/logicng/solvers/sat/MiniSatStyleSolver.java b/src/main/java/org/logicng/solvers/sat/MiniSatStyleSolver.java index eb49a696..5cf7863d 100644 --- a/src/main/java/org/logicng/solvers/sat/MiniSatStyleSolver.java +++ b/src/main/java/org/logicng/solvers/sat/MiniSatStyleSolver.java @@ -139,7 +139,7 @@ public Map getName2idx() { * @param sign {@code true} if the literal is negative, {@code false} otherwise * @return the literal (as integer value) */ - public static int mkLit(int var, boolean sign) { + public static int mkLit(final int var, final boolean sign) { return var + var + (sign ? 1 : 0); } @@ -148,7 +148,7 @@ public static int mkLit(int var, boolean sign) { * @param lit the literal * @return the negated literal */ - public static int not(int lit) { + public static int not(final int lit) { return lit ^ 1; } @@ -157,7 +157,7 @@ public static int not(int lit) { * @param lit the literal * @return {@code true} if the literal is negated */ - public static boolean sign(int lit) { + public static boolean sign(final int lit) { return (lit & 1) == 1; } @@ -166,7 +166,7 @@ public static boolean sign(int lit) { * @param lit the literal * @return the variable index of the literal */ - public static int var(int lit) { + public static int var(final int lit) { return lit >> 1; } @@ -176,7 +176,7 @@ public static int var(int lit) { * @param x the current number of restarts * @return the next number in the Luby sequence */ - protected static double luby(double y, int x) { + protected static double luby(final double y, final int x) { int intX = x; int size = 1; int seq = 0; @@ -248,7 +248,7 @@ private void initializeConfig() { * @param lit the literal * @return the variable of the literal */ - protected MSVariable v(int lit) { + protected MSVariable v(final int lit) { return this.vars.get(lit >> 1); } @@ -257,7 +257,7 @@ protected MSVariable v(int lit) { * @param lit the literal * @return the assigned value of the literal */ - protected Tristate value(int lit) { + protected Tristate value(final int lit) { return sign(lit) ? Tristate.negate(this.v(lit).assignment()) : this.v(lit).assignment(); } @@ -267,7 +267,7 @@ protected Tristate value(int lit) { * @param y the second variable * @return {@code true} if the first variable's activity is larger then the second one's */ - public boolean lt(int x, int y) { + public boolean lt(final int x, final int y) { return this.vars.get(x).activity() > this.vars.get(y).activity(); } @@ -286,7 +286,7 @@ public int idxForName(final String name) { * @param var the variable index * @return the name for the index */ - public String nameForIdx(int var) { + public String nameForIdx(final int var) { return this.idx2name.get(var); } @@ -295,7 +295,7 @@ public String nameForIdx(int var) { * @param name the variable name * @param id the variable index */ - public void addName(final String name, int id) { + public void addName(final String name, final int id) { this.name2idx.put(name, id); this.idx2name.put(id, name); } @@ -315,7 +315,7 @@ public void addName(final String name, int id) { * @param proposition a proposition (if required for proof tracing) * @return {@code true} if the clause was added successfully, {@code false} otherwise */ - public boolean addClause(int lit, final Proposition proposition) { + public boolean addClause(final int lit, final Proposition proposition) { final LNGIntVector unit = new LNGIntVector(1); unit.push(lit); return this.addClause(unit, proposition); @@ -412,7 +412,7 @@ public int nVars() { * @return the mapping from variable names to internal solver indices */ public Map name2idx() { - return name2idx; + return this.name2idx; } /** @@ -436,7 +436,7 @@ protected int decisionLevel() { * @param x a variable index * @return the abstraction of levels */ - protected int abstractLevel(int x) { + protected int abstractLevel(final int x) { return 1 << (this.vars.get(x).level() & 31); } @@ -444,7 +444,7 @@ protected int abstractLevel(int x) { * Inserts a variable (given by its index) into the heap of decision variables. * @param x the variable index */ - protected void insertVarOrder(int x) { + protected void insertVarOrder(final int x) { if (!this.orderHeap.inHeap(x) && this.vars.get(x).decision()) { this.orderHeap.insert(x); } @@ -477,7 +477,7 @@ protected void varDecayActivity() { * Bumps the activity of the variable at a given index. * @param v the variable index */ - protected void varBumpActivity(int v) { + protected void varBumpActivity(final int v) { this.varBumpActivity(v, this.varInc); } @@ -486,7 +486,7 @@ protected void varBumpActivity(int v) { * @param v the variable index * @param inc the increment value */ - protected void varBumpActivity(int v, double inc) { + protected void varBumpActivity(final int v, final double inc) { final MSVariable var = this.vars.get(v); var.incrementActivity(inc); if (var.activity() > 1e100) { @@ -526,7 +526,7 @@ protected boolean locked(final MSClause c) { * Decays the clause activity increment by the clause decay factor. */ protected void claDecayActivity() { - claInc *= (1 / clauseDecay); + this.claInc *= (1 / this.clauseDecay); } /** @@ -534,12 +534,12 @@ protected void claDecayActivity() { * @param c the clause */ protected void claBumpActivity(final MSClause c) { - c.incrementActivity(claInc); + c.incrementActivity(this.claInc); if (c.activity() > 1e20) { - for (final MSClause clause : learnts) { + for (final MSClause clause : this.learnts) { clause.rescaleActivity(); } - claInc *= 1e-20; + this.claInc *= 1e-20; } } @@ -643,28 +643,28 @@ public LNGVector pgProof() { @Override public String toString() { final StringBuilder sb = new StringBuilder(); - sb.append("ok ").append(ok).append(System.lineSeparator()); - sb.append("qhead ").append(qhead).append(System.lineSeparator()); - sb.append("#clauses ").append(clauses.size()).append(System.lineSeparator()); - sb.append("#learnts ").append(learnts.size()).append(System.lineSeparator()); - sb.append("#watches ").append(watches.size()).append(System.lineSeparator()); - sb.append("#vars ").append(vars.size()).append(System.lineSeparator()); - sb.append("#orderheap ").append(orderHeap.size()).append(System.lineSeparator()); - sb.append("#trail ").append(trail.size()).append(System.lineSeparator()); - sb.append("#trailLim ").append(trailLim.size()).append(System.lineSeparator()); - - sb.append("model ").append(model).append(System.lineSeparator()); - sb.append("conflict ").append(conflict).append(System.lineSeparator()); - sb.append("assumptions ").append(assumptions).append(System.lineSeparator()); - sb.append("#seen ").append(seen.size()).append(System.lineSeparator()); - sb.append("#stack ").append(analyzeStack.size()).append(System.lineSeparator()); - sb.append("#toclear ").append(analyzeToClear.size()).append(System.lineSeparator()); - - sb.append("claInc ").append(claInc).append(System.lineSeparator()); - sb.append("simpDBAssigns ").append(simpDBAssigns).append(System.lineSeparator()); - sb.append("simpDBProps ").append(simpDBProps).append(System.lineSeparator()); - sb.append("#clause lits ").append(clausesLiterals).append(System.lineSeparator()); - sb.append("#learnts lits ").append(learntsLiterals).append(System.lineSeparator()); + sb.append("ok ").append(this.ok).append(System.lineSeparator()); + sb.append("qhead ").append(this.qhead).append(System.lineSeparator()); + sb.append("#clauses ").append(this.clauses.size()).append(System.lineSeparator()); + sb.append("#learnts ").append(this.learnts.size()).append(System.lineSeparator()); + sb.append("#watches ").append(this.watches.size()).append(System.lineSeparator()); + sb.append("#vars ").append(this.vars.size()).append(System.lineSeparator()); + sb.append("#orderheap ").append(this.orderHeap.size()).append(System.lineSeparator()); + sb.append("#trail ").append(this.trail.size()).append(System.lineSeparator()); + sb.append("#trailLim ").append(this.trailLim.size()).append(System.lineSeparator()); + + sb.append("model ").append(this.model).append(System.lineSeparator()); + sb.append("conflict ").append(this.conflict).append(System.lineSeparator()); + sb.append("assumptions ").append(this.assumptions).append(System.lineSeparator()); + sb.append("#seen ").append(this.seen.size()).append(System.lineSeparator()); + sb.append("#stack ").append(this.analyzeStack.size()).append(System.lineSeparator()); + sb.append("#toclear ").append(this.analyzeToClear.size()).append(System.lineSeparator()); + + sb.append("claInc ").append(this.claInc).append(System.lineSeparator()); + sb.append("simpDBAssigns ").append(this.simpDBAssigns).append(System.lineSeparator()); + sb.append("simpDBProps ").append(this.simpDBProps).append(System.lineSeparator()); + sb.append("#clause lits ").append(this.clausesLiterals).append(System.lineSeparator()); + sb.append("#learnts lits ").append(this.learntsLiterals).append(System.lineSeparator()); return sb.toString(); } @@ -680,7 +680,7 @@ public static class ProofInformation { * @param clause the clause * @param proposition the proposition */ - public ProofInformation(LNGIntVector clause, Proposition proposition) { + public ProofInformation(final LNGIntVector clause, final Proposition proposition) { this.clause = clause; this.proposition = proposition; } @@ -690,7 +690,7 @@ public ProofInformation(LNGIntVector clause, Proposition proposition) { * @return the clause */ public LNGIntVector clause() { - return clause; + return this.clause; } /** @@ -698,14 +698,14 @@ public LNGIntVector clause() { * @return the proposition */ public Proposition proposition() { - return proposition; + return this.proposition; } @Override public String toString() { return "ProofInformation{" + - "clause=" + clause + - ", proposition=" + proposition + + "clause=" + this.clause + + ", proposition=" + this.proposition + '}'; } } @@ -726,4 +726,5 @@ public LNGIntVector upZeroLiterals() { } return upZeroLiterals; } + } diff --git a/src/main/java/org/logicng/transformations/BackboneSimplifier.java b/src/main/java/org/logicng/transformations/BackboneSimplifier.java index 52eef029..8ab97e34 100644 --- a/src/main/java/org/logicng/transformations/BackboneSimplifier.java +++ b/src/main/java/org/logicng/transformations/BackboneSimplifier.java @@ -28,10 +28,13 @@ package org.logicng.transformations; import org.logicng.backbones.Backbone; -import org.logicng.backbones.BackboneGeneration; +import org.logicng.backbones.BackboneType; import org.logicng.datastructures.Assignment; import org.logicng.formulas.Formula; import org.logicng.formulas.FormulaTransformation; +import org.logicng.solvers.MiniSat; +import org.logicng.solvers.SATSolver; +import org.logicng.solvers.sat.MiniSatConfig; /** * This class simplifies a formula by computing its backbone and propagating @@ -44,7 +47,10 @@ public class BackboneSimplifier implements FormulaTransformation { @Override public Formula apply(final Formula formula, final boolean cache) { - final Backbone backbone = BackboneGeneration.compute(formula); + final SATSolver solver = MiniSat.miniSat(formula.factory(), new MiniSatConfig.Builder() + .fastBackboneComputation(true).build()); + solver.add(formula); + final Backbone backbone = solver.computeBackbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); if (!backbone.isSat()) { return formula.factory().falsum(); } diff --git a/src/test/java/org/logicng/backbones/BackboneGenerationTest.java b/src/test/java/org/logicng/backbones/BackboneGenerationTest.java index 61f0bcac..2177f906 100644 --- a/src/test/java/org/logicng/backbones/BackboneGenerationTest.java +++ b/src/test/java/org/logicng/backbones/BackboneGenerationTest.java @@ -40,6 +40,8 @@ import org.logicng.io.readers.FormulaReader; import org.logicng.solvers.MiniSat; import org.logicng.solvers.SATSolver; +import org.logicng.solvers.SolverState; +import org.logicng.solvers.sat.MiniSatConfig; import java.io.IOException; import java.util.ArrayList; @@ -57,33 +59,6 @@ */ public class BackboneGenerationTest { - @Test - public void testBackboneConfig() { - BackboneConfig config = new BackboneConfig.Builder().build(); - assertThat(config.toString()).isEqualTo(String.format("BackboneConfig{%n" + - "initialUBCheckForRotatableLiterals=true%n" + - "checkForComplementModelLiterals=true%n" + - "checkForRotatableLiterals=true%n" + - "}%n")); - - config = new BackboneConfig.Builder() - .checkForComplementModelLiterals(false) - .checkForRotatableLiterals(false) - .initialUBCheckForRotatableLiterals(false).build(); - assertThat(config.toString()).isEqualTo(String.format("BackboneConfig{%n" + - "initialUBCheckForRotatableLiterals=false%n" + - "checkForComplementModelLiterals=false%n" + - "checkForRotatableLiterals=false%n" + - "}%n")); - - config = new BackboneConfig.Builder().checkForComplementModelLiterals(true).build(); - assertThat(config.toString()).isEqualTo(String.format("BackboneConfig{%n" + - "initialUBCheckForRotatableLiterals=true%n" + - "checkForComplementModelLiterals=true%n" + - "checkForRotatableLiterals=true%n" + - "}%n")); - } - @Test public void testBackboneGeneration() { final FormulaFactory f = new FormulaFactory(); @@ -114,16 +89,12 @@ public void testBackboneGeneration() { assertThat(BackboneGeneration.computeNegative(formula1, new ArrayList<>(Arrays.asList(x, z))).getCompleteBackbone()).containsExactly(x.negate()); assertThat(BackboneGeneration.computeNegative(collection).getCompleteBackbone()).containsExactly(x.negate(), z.negate()); assertThat(BackboneGeneration.computeNegative(collection, new ArrayList<>(Arrays.asList(x, y))).getCompleteBackbone()).containsExactly(x.negate()); - - final BackboneConfig config = new BackboneConfig.Builder().checkForRotatableLiterals(false).build(); - BackboneGeneration.setConfig(config); - assertThat(BackboneGeneration.compute(formula1).getCompleteBackbone()).containsExactly(x.negate(), y); } @Test public void testSimpleBackbones() { final FormulaFactory f = new FormulaFactory(); - final MiniSatBackbone solver = new MiniSatBackbone(); + final MiniSat solver = MiniSat.miniSat(f, new MiniSatConfig.Builder().fastBackboneComputation(true).build()); final Literal x = f.literal("x", true); final Literal y = f.literal("y", true); @@ -135,9 +106,10 @@ public void testSimpleBackbones() { f.variable("z"), f.variable("u"), f.variable("v"))); Formula formula = f.verum(); - int[] before = solver.saveState(); + + SolverState before = solver.saveState(); solver.add(formula); - assertThat(solver.compute(Collections.emptyList(), BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.computeBackbone(Collections.emptyList(), BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>() ); solver.loadState(before); @@ -145,7 +117,7 @@ public void testSimpleBackbones() { formula = x; before = solver.saveState(); solver.add(formula); - assertThat(solver.compute(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.computeBackbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>(Collections.singletonList(x)) ); solver.loadState(before); @@ -153,7 +125,7 @@ public void testSimpleBackbones() { formula = f.and(x, y); before = solver.saveState(); solver.add(formula); - assertThat(solver.compute(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.computeBackbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>(Arrays.asList(x, y)) ); solver.loadState(before); @@ -161,7 +133,7 @@ public void testSimpleBackbones() { formula = f.or(x, y); before = solver.saveState(); solver.add(formula); - assertThat(solver.compute(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.computeBackbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>() ); solver.loadState(before); @@ -169,7 +141,7 @@ public void testSimpleBackbones() { formula = x.negate(); before = solver.saveState(); solver.add(formula); - assertThat(solver.compute(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.computeBackbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>(Collections.singleton(x.negate())) ); solver.loadState(before); @@ -177,7 +149,7 @@ public void testSimpleBackbones() { formula = f.or(f.and(x, y, z), f.and(x, y, u), f.and(x, u, z)); before = solver.saveState(); solver.add(formula); - assertThat(solver.compute(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.computeBackbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>(Collections.singleton(x)) ); solver.loadState(before); @@ -185,7 +157,7 @@ public void testSimpleBackbones() { formula = f.and(f.or(x, y, z), f.or(x, y, u), f.or(x, u, z)); before = solver.saveState(); solver.add(formula); - assertThat(solver.compute(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.computeBackbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>() ); solver.loadState(before); @@ -193,7 +165,7 @@ public void testSimpleBackbones() { formula = f.and(f.or(x.negate(), y), x); before = solver.saveState(); solver.add(formula); - assertThat(solver.compute(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.computeBackbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>(Arrays.asList(x, y)) ); solver.loadState(before); @@ -201,7 +173,7 @@ public void testSimpleBackbones() { formula = f.and(f.or(x, y), f.or(x.negate(), y)); before = solver.saveState(); solver.add(formula); - assertThat(solver.compute(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.computeBackbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>(Collections.singleton(y)) ); solver.loadState(before); @@ -209,14 +181,14 @@ public void testSimpleBackbones() { formula = f.and(f.and(f.or(x.negate(), y), x.negate()), f.and(z, f.or(x, y))); before = solver.saveState(); solver.add(formula); - assertThat(solver.compute(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.computeBackbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>(Arrays.asList(x.negate(), y, z)) ); solver.loadState(before); formula = f.and(f.or(x, y), f.or(u, v), z); solver.add(formula); - assertThat(solver.compute(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.computeBackbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>(Collections.singleton(z)) ); } @@ -225,9 +197,9 @@ public void testSimpleBackbones() { public void testSmallFormulas() throws IOException, ParserException { final FormulaFactory f = new FormulaFactory(); final Formula formula = FormulaReader.readPseudoBooleanFormula("src/test/resources/formulas/small_formulas.txt", f); - final MiniSatBackbone backboneSolver = new MiniSatBackbone(); - backboneSolver.add(formula); - final Backbone backbone = backboneSolver.compute(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); + final MiniSat solver = MiniSat.miniSat(f, new MiniSatConfig.Builder().fastBackboneComputation(true).build()); + solver.add(formula); + final Backbone backbone = solver.computeBackbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); assertThat(verifyBackbone(backbone, formula, formula.variables())).isTrue(); } @@ -235,9 +207,9 @@ public void testSmallFormulas() throws IOException, ParserException { public void testLargeFormula() throws IOException, ParserException { final FormulaFactory f = new FormulaFactory(); final Formula formula = FormulaReader.readPseudoBooleanFormula("src/test/resources/formulas/large_formula.txt", f); - final MiniSatBackbone backboneSolver = new MiniSatBackbone(); - backboneSolver.add(formula); - final Backbone backbone = backboneSolver.compute(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); + final MiniSat solver = MiniSat.miniSat(f, new MiniSatConfig.Builder().fastBackboneComputation(true).build()); + solver.add(formula); + final Backbone backbone = solver.computeBackbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); assertThat(verifyBackbone(backbone, formula, formula.variables())).isTrue(); } @@ -270,18 +242,18 @@ private boolean verifyBackbone(final Backbone backbone, final Formula formula, f @Test public void testBackboneType() { final FormulaFactory f = new FormulaFactory(); - final MiniSatBackbone solver = new MiniSatBackbone(); + final MiniSat solver = MiniSat.miniSat(f, new MiniSatConfig.Builder().fastBackboneComputation(true).build()); final Literal x = f.literal("x", true); final Literal y = f.literal("y", true); final Literal z = f.literal("z", true); Formula formula = f.not(x); - int[] before = solver.saveState(); + SolverState before = solver.saveState(); solver.add(formula); - Backbone backbone = solver.compute(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); - Backbone backbonePositive = solver.compute(formula.variables(), BackboneType.ONLY_POSITIVE); - Backbone backboneNegative = solver.compute(formula.variables(), BackboneType.ONLY_NEGATIVE); + Backbone backbone = solver.computeBackbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); + Backbone backbonePositive = solver.computeBackbone(formula.variables(), BackboneType.ONLY_POSITIVE); + Backbone backboneNegative = solver.computeBackbone(formula.variables(), BackboneType.ONLY_NEGATIVE); assertThat(backbone.getCompleteBackbone()).containsExactly(x.negate()); assertThat(backbonePositive.getCompleteBackbone()).isEmpty(); assertThat(backboneNegative.getCompleteBackbone()).containsExactly(x.negate()); @@ -293,9 +265,9 @@ public void testBackboneType() { formula = f.and(f.or(x, y.negate()), x.negate()); before = solver.saveState(); solver.add(formula); - backbone = solver.compute(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); - backbonePositive = solver.compute(formula.variables(), BackboneType.ONLY_POSITIVE); - backboneNegative = solver.compute(formula.variables(), BackboneType.ONLY_NEGATIVE); + backbone = solver.computeBackbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); + backbonePositive = solver.computeBackbone(formula.variables(), BackboneType.ONLY_POSITIVE); + backboneNegative = solver.computeBackbone(formula.variables(), BackboneType.ONLY_NEGATIVE); assertThat(backbone.getCompleteBackbone()).containsExactly(x.negate(), y.negate()); assertThat(backbonePositive.getCompleteBackbone()).isEmpty(); assertThat(backboneNegative.getCompleteBackbone()).containsExactly(x.negate(), y.negate()); @@ -307,9 +279,9 @@ public void testBackboneType() { formula = f.and(f.or(x, y), f.or(x.negate(), y)); before = solver.saveState(); solver.add(formula); - backbone = solver.compute(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); - backbonePositive = solver.compute(formula.variables(), BackboneType.ONLY_POSITIVE); - backboneNegative = solver.compute(formula.variables(), BackboneType.ONLY_NEGATIVE); + backbone = solver.computeBackbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); + backbonePositive = solver.computeBackbone(formula.variables(), BackboneType.ONLY_POSITIVE); + backboneNegative = solver.computeBackbone(formula.variables(), BackboneType.ONLY_NEGATIVE); assertThat(backbone.getCompleteBackbone()).containsExactly(y); assertThat(backbonePositive.getCompleteBackbone()).containsExactly(y); assertThat(backboneNegative.getCompleteBackbone()).isEmpty(); @@ -321,9 +293,9 @@ public void testBackboneType() { formula = f.and(f.and(f.or(x.negate(), y), x.negate()), f.and(z, f.or(x, y))); before = solver.saveState(); solver.add(formula); - backbone = solver.compute(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); - backbonePositive = solver.compute(formula.variables(), BackboneType.ONLY_POSITIVE); - backboneNegative = solver.compute(formula.variables(), BackboneType.ONLY_NEGATIVE); + backbone = solver.computeBackbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); + backbonePositive = solver.computeBackbone(formula.variables(), BackboneType.ONLY_POSITIVE); + backboneNegative = solver.computeBackbone(formula.variables(), BackboneType.ONLY_NEGATIVE); assertThat(backbone.getCompleteBackbone()).containsExactly(x.negate(), y, z); assertThat(backbone.getOptionalVariables()).containsExactly(); assertThat(backbonePositive.getCompleteBackbone()).containsExactly(y, z); @@ -336,21 +308,21 @@ public void testBackboneType() { @Test public void testDifferentConfigurations() throws IOException, ParserException { - final List configs = new ArrayList<>(); - configs.add(new BackboneConfig.Builder().checkForComplementModelLiterals(false).build()); - configs.add(new BackboneConfig.Builder().checkForRotatableLiterals(false).build()); - configs.add(new BackboneConfig.Builder().initialUBCheckForRotatableLiterals(false).build()); + final List configs = new ArrayList<>(); + configs.add(new MiniSatConfig.Builder().fastBackboneComputation(true).bbCheckForComplementModelLiterals(false).build()); + configs.add(new MiniSatConfig.Builder().fastBackboneComputation(true).bbCheckForRotatableLiterals(false).build()); + configs.add(new MiniSatConfig.Builder().fastBackboneComputation(true).bbInitialUBCheckForRotatableLiterals(false).build()); final FormulaFactory f = new FormulaFactory(); final Formula formula = FormulaReader.readPseudoBooleanFormula("src/test/resources/formulas/large_formula.txt", f); - MiniSatBackbone backboneSolver = new MiniSatBackbone(new BackboneConfig.Builder().build()); - backboneSolver.add(formula); - final Backbone backbone = backboneSolver.compute(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); - - for (final BackboneConfig config : configs) { - backboneSolver = new MiniSatBackbone(config); - backboneSolver.add(formula); - assertThat(backboneSolver.compute(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE)).isEqualTo(backbone); + MiniSat solver = MiniSat.miniSat(f, new MiniSatConfig.Builder().fastBackboneComputation(true).build()); + solver.add(formula); + final Backbone backbone = solver.computeBackbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); + + for (final MiniSatConfig config : configs) { + solver = MiniSat.miniSat(f, config); + solver.add(formula); + assertThat(solver.computeBackbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE)).isEqualTo(backbone); } } } diff --git a/src/test/java/org/logicng/solvers/sat/ConfigurationsTest.java b/src/test/java/org/logicng/solvers/sat/ConfigurationsTest.java index d50606df..f6642f94 100644 --- a/src/test/java/org/logicng/solvers/sat/ConfigurationsTest.java +++ b/src/test/java/org/logicng/solvers/sat/ConfigurationsTest.java @@ -71,6 +71,10 @@ public void testMiniSatConfigToString() { "proofGeneration=false%n" + "cnfMethod=FACTORY_CNF%n" + "auxiliaryVariablesInModels=true%n" + + "fastBackboneComputation=false%n" + + "bbInitialUBCheckForRotatableLiterals=true%n" + + "bbCheckForComplementModelLiterals=true%n" + + "bbCheckForRotatableLiterals=true%n" + "}%n"); Assert.assertEquals(expected, config.toString()); } From 2c516267f724dd39846db7f7b2ef494bd3c0aebe Mon Sep 17 00:00:00 2001 From: Christoph Zengler Date: Sat, 27 Jul 2019 23:41:34 +0200 Subject: [PATCH 06/16] Extensive unit tests for model generation --- .../java/org/logicng/solvers/MiniSat.java | 6 +- .../java/org/logicng/solvers/ModelTest.java | 211 ++++++++++++++++++ 2 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/logicng/solvers/ModelTest.java diff --git a/src/main/java/org/logicng/solvers/MiniSat.java b/src/main/java/org/logicng/solvers/MiniSat.java index 224e544c..c774d5cb 100644 --- a/src/main/java/org/logicng/solvers/MiniSat.java +++ b/src/main/java/org/logicng/solvers/MiniSat.java @@ -644,7 +644,7 @@ private Formula getFormulaForVector(final LNGIntVector vector) { @Override public String toString() { - return String.format("MiniSat{result=%s, incremental=%s}", this.result, this.incremental); + return String.format(this.solver.getClass().getSimpleName()); } private Literal getLiteralFromIntLiteral(final int lit) { @@ -670,4 +670,8 @@ public SortedSet upZeroLiterals() { private boolean lastResultIsUsable() { return this.result != UNDEF && !this.lastComputationWithAssumptions; } + + public MiniSatConfig getConfig() { + return this.config; + } } diff --git a/src/test/java/org/logicng/solvers/ModelTest.java b/src/test/java/org/logicng/solvers/ModelTest.java new file mode 100644 index 00000000..e5e16c7d --- /dev/null +++ b/src/test/java/org/logicng/solvers/ModelTest.java @@ -0,0 +1,211 @@ +package org.logicng.solvers; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.logicng.datastructures.Assignment; +import org.logicng.datastructures.Tristate; +import org.logicng.formulas.Formula; +import org.logicng.formulas.FormulaFactory; +import org.logicng.formulas.Variable; +import org.logicng.io.parsers.ParserException; +import org.logicng.solvers.sat.GlucoseConfig; +import org.logicng.solvers.sat.MiniSatConfig; +import org.logicng.util.Pair; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; + +/** + * Test model generation and model enumeration on solvers. + * @version 1.6.0 + * @since 1.6.0 + */ +@RunWith(Parameterized.class) +public class ModelTest { + + private static final FormulaFactory f = new FormulaFactory(); + + private final MiniSat solver; + + @Parameterized.Parameters(name = "{1}") + public static Collection solvers() { + final MiniSatConfig configNoPGAux = new MiniSatConfig.Builder().cnfMethod(MiniSatConfig.CNFMethod.FACTORY_CNF).auxiliaryVariablesInModels(true).build(); + final MiniSatConfig configNoPGNoAux = new MiniSatConfig.Builder().cnfMethod(MiniSatConfig.CNFMethod.FACTORY_CNF).auxiliaryVariablesInModels(false).build(); + final MiniSatConfig configPGAux = new MiniSatConfig.Builder().cnfMethod(MiniSatConfig.CNFMethod.PG_ON_SOLVER).auxiliaryVariablesInModels(true).build(); + final MiniSatConfig configPGNoAux = new MiniSatConfig.Builder().cnfMethod(MiniSatConfig.CNFMethod.PG_ON_SOLVER).auxiliaryVariablesInModels(false).build(); + final List> configs = Arrays.asList( + new Pair<>(configNoPGAux, "FF CNF, +AUX"), + new Pair<>(configNoPGNoAux, "FF CNF, -AUX"), + new Pair<>(configPGAux, "PG CNF, +AUX"), + new Pair<>(configPGNoAux, "PG CNF, -AUX") + ); + final List solvers = new ArrayList<>(); + for (final Pair config : configs) { + solvers.add(new Object[]{MiniSat.miniSat(f, config.first()), "MiniSat (" + config.second() + ")"}); + solvers.add(new Object[]{MiniSat.miniCard(f, config.first()), "MiniCard (" + config.second() + ")"}); + solvers.add(new Object[]{MiniSat.glucose(f, config.first(), new GlucoseConfig.Builder().build()), "Glucose (" + config.second() + ")"}); + } + return solvers; + } + + public ModelTest(final MiniSat solver, final String description) { + this.solver = solver; + } + + @Test + public void testNoModel() throws ParserException { + this.solver.reset(); + this.solver.add(f.falsum()); + this.solver.sat(); + assertThat(this.solver.model()).isNull(); + this.solver.reset(); + this.solver.add(f.parse("A & ~A")); + this.solver.sat(); + assertThat(this.solver.model()).isNull(); + this.solver.reset(); + this.solver.add(f.parse("(A => (B & C)) & A & C & (C <=> ~B)")); + this.solver.sat(); + assertThat(this.solver.model()).isNull(); + } + + @Test + public void testEmptyModel() { + this.solver.reset(); + this.solver.add(f.verum()); + this.solver.sat(); + final Assignment model = this.solver.model(); + assertThat(model.literals()).isEmpty(); + assertThat(model.blockingClause(f)).isEqualTo(f.falsum()); + assertThat(this.solver.enumerateAllModels()).hasSize(1); + } + + @Test + public void testSimpleModel() { + this.solver.reset(); + this.solver.add(f.literal("A", true)); + this.solver.sat(); + Assignment model = this.solver.model(); + assertThat(model.literals()).containsExactly(f.literal("A", true)); + assertThat(this.solver.enumerateAllModels()).hasSize(1); + this.solver.reset(); + this.solver.add(f.literal("A", false)); + this.solver.sat(); + model = this.solver.model(); + assertThat(model.literals()).containsExactly(f.literal("A", false)); + assertThat(this.solver.enumerateAllModels()).hasSize(1); + } + + @Test + public void testCNFFormula() throws ParserException { + this.solver.reset(); + final Formula formula = f.parse("(A|B|C) & (~A|~B|~C) & (A|~B|~C) & (~A|~B|C)"); + this.solver.add(formula); + this.solver.sat(); + final Assignment model = this.solver.model(); + assertThat(formula.evaluate(model)).isTrue(); + assertThat(this.solver.enumerateAllModels()).hasSize(4); + for (final Assignment assignment : this.solver.enumerateAllModels()) { + assertThat(formula.evaluate(assignment)).isTrue(); + } + } + + @Test + public void testNonCNFAllVars() throws ParserException { + this.solver.reset(); + final Formula formula = f.parse("(A => B & C) & (~A => C & ~D) & (C => (D & E | ~E & B)) & ~F"); + this.solver.add(formula); + this.solver.sat(); + final Assignment model = this.solver.model(); + assertThat(formula.evaluate(model)).isTrue(); + final List allModels = this.solver.enumerateAllModels(); + if (!this.solver.getConfig().isAuxiliaryVariablesInModels() || this.solver.getConfig().getCnfMethod() == MiniSatConfig.CNFMethod.FACTORY_CNF) { + assertThat(allModels).hasSize(4); + for (final Assignment assignment : allModels) { + assertThat(formula.evaluate(assignment)).isTrue(); + assertThat(assignment.formula(f).variables()).isEqualTo(formula.variables()); + } + } else { + assertThat(allModels).hasSize(6); + for (final Assignment assignment : allModels) { + System.out.println(assignment); + assertThat(formula.evaluate(assignment)).isTrue(); + assertThat(formula.variables()).isSubsetOf(assignment.formula(f).variables()); + } + } + } + + @Test + public void testNonCNFOnlyFormulaVars() throws ParserException { + this.solver.reset(); + final Formula formula = f.parse("(A => B & C) & (~A => C & ~D) & (C => (D & E | ~E & B)) & ~F"); + this.solver.add(formula); + this.solver.sat(); + final Assignment model = this.solver.model(formula.variables()); + assertThat(formula.evaluate(model)).isTrue(); + assertThat(model.formula(f).variables()).isEqualTo(formula.variables()); + final List allModels = this.solver.enumerateAllModels(formula.variables()); + assertThat(allModels).hasSize(4); + for (final Assignment assignment : allModels) { + assertThat(formula.evaluate(assignment)).isTrue(); + assertThat(assignment.formula(f).variables()).isEqualTo(formula.variables()); + } + } + + @Test + public void testNonCNFRestrictedVars() throws ParserException { + this.solver.reset(); + final Formula formula = f.parse("(A => B & C) & (~A => C & ~D) & (C => (D & E | ~E & B)) & ~F"); + final MiniSat miniSat = MiniSat.miniSat(f); + miniSat.add(formula); + this.solver.add(formula); + this.solver.sat(); + final SortedSet relevantVariables = new TreeSet<>(Arrays.asList(f.variable("A"), f.variable("B"), f.variable("C"))); + final Assignment model = this.solver.model(relevantVariables); + assertThat(miniSat.sat(model.literals())).isEqualTo(Tristate.TRUE); + assertThat(model.formula(f).variables()).isEqualTo(relevantVariables); + final List allModels = this.solver.enumerateAllModels(relevantVariables); + assertThat(allModels).hasSize(2); + for (final Assignment assignment : allModels) { + assertThat(miniSat.sat(assignment.literals())).isEqualTo(Tristate.TRUE); + assertThat(assignment.formula(f).variables()).isEqualTo(relevantVariables); + } + } + + @Test + public void testNonCNFRestrictedAndAdditionalVars() throws ParserException { + this.solver.reset(); + final Formula formula = f.parse("(A => B & C) & (~A => C & ~D) & (C => (D & E | ~E & B)) & ~F"); + final MiniSat miniSat = MiniSat.miniSat(f); + miniSat.add(formula); + this.solver.add(formula); + this.solver.sat(); + final SortedSet relevantVariables = new TreeSet<>(Arrays.asList(f.variable("A"), f.variable("B"), f.variable("C"))); + final SortedSet additionalVariables = new TreeSet<>(Arrays.asList(f.variable("D"), f.variable("X"), f.variable("Y"))); + final SortedSet allVariables = new TreeSet<>(relevantVariables); + allVariables.add(f.variable("D")); + final Assignment model = this.solver.model(additionalVariables); + assertThat(miniSat.sat(model.literals())).isEqualTo(Tristate.TRUE); + assertThat(model.formula(f).variables()).containsExactly(f.variable("D")); + final List allModels = this.solver.enumerateAllModels(relevantVariables, additionalVariables); + assertThat(allModels).hasSize(2); + for (final Assignment assignment : allModels) { + assertThat(miniSat.sat(assignment.literals())).isEqualTo(Tristate.TRUE); + assertThat(assignment.formula(f).variables()).isEqualTo(allVariables); + } + } + + @Test(expected = IllegalStateException.class) + public void testUnsolvedFormula() throws ParserException { + this.solver.reset(); + final Formula formula = f.parse("(A => B & C) & (~A => C & ~D) & (C => (D & E | ~E & B)) & ~F"); + this.solver.add(formula); + this.solver.model(); + } +} From 1ddf527464ec2f4760e85136c31adcca5d4bf64a Mon Sep 17 00:00:00 2001 From: Christoph Zengler Date: Sat, 27 Jul 2019 23:54:28 +0200 Subject: [PATCH 07/16] more unit testing --- .../java/org/logicng/solvers/ModelTest.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/test/java/org/logicng/solvers/ModelTest.java b/src/test/java/org/logicng/solvers/ModelTest.java index e5e16c7d..429ec158 100644 --- a/src/test/java/org/logicng/solvers/ModelTest.java +++ b/src/test/java/org/logicng/solvers/ModelTest.java @@ -13,6 +13,7 @@ import org.logicng.io.parsers.ParserException; import org.logicng.solvers.sat.GlucoseConfig; import org.logicng.solvers.sat.MiniSatConfig; +import org.logicng.transformations.cnf.TseitinTransformation; import org.logicng.util.Pair; import java.util.ArrayList; @@ -116,6 +117,50 @@ public void testCNFFormula() throws ParserException { } } + @Test + public void testCNFWithAuxiliaryVars() throws ParserException { + this.solver.reset(); + final Formula formula = f.parse("(A => B & C) & (~A => C & ~D) & (C => (D & E | ~E & B)) & ~F"); + final Formula cnf = formula.transform(new TseitinTransformation(0)); + this.solver.add(cnf); + this.solver.sat(); + final Assignment model = this.solver.model(); + assertThat(formula.evaluate(model)).isTrue(); + final List allModels = this.solver.enumerateAllModels(); + assertThat(allModels).hasSize(4); + if (this.solver.getConfig().isAuxiliaryVariablesInModels()) { + assertThat(model.formula(f).variables()).isEqualTo(cnf.variables()); + for (final Assignment assignment : allModels) { + assertThat(formula.evaluate(assignment)).isTrue(); + assertThat(assignment.formula(f).variables()).isEqualTo(cnf.variables()); + } + } else { + assertThat(model.formula(f).variables()).isEqualTo(formula.variables()); + for (final Assignment assignment : allModels) { + assertThat(formula.evaluate(assignment)).isTrue(); + assertThat(assignment.formula(f).variables()).isEqualTo(formula.variables()); + } + } + } + + @Test + public void testCNFWithAuxiliaryVarsRestrictedToOriginal() throws ParserException { + this.solver.reset(); + final Formula formula = f.parse("(A => B & C) & (~A => C & ~D) & (C => (D & E | ~E & B)) & ~F"); + final Formula cnf = formula.transform(new TseitinTransformation(0)); + this.solver.add(cnf); + this.solver.sat(); + final Assignment model = this.solver.model(formula.variables()); + assertThat(formula.evaluate(model)).isTrue(); + final List allModels = this.solver.enumerateAllModels(formula.variables()); + assertThat(allModels).hasSize(4); + assertThat(model.formula(f).variables()).isEqualTo(formula.variables()); + for (final Assignment assignment : allModels) { + assertThat(formula.evaluate(assignment)).isTrue(); + assertThat(assignment.formula(f).variables()).isEqualTo(formula.variables()); + } + } + @Test public void testNonCNFAllVars() throws ParserException { this.solver.reset(); From ef6a00d305e86cc3ee8e115a3cace3aed6ac98b0 Mon Sep 17 00:00:00 2001 From: Christoph Zengler Date: Sun, 28 Jul 2019 00:01:49 +0200 Subject: [PATCH 08/16] Fixed unit tests --- src/main/java/org/logicng/solvers/MiniSat.java | 2 +- .../java/org/logicng/solvers/sat/SATTest.java | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/logicng/solvers/MiniSat.java b/src/main/java/org/logicng/solvers/MiniSat.java index c774d5cb..3215045f 100644 --- a/src/main/java/org/logicng/solvers/MiniSat.java +++ b/src/main/java/org/logicng/solvers/MiniSat.java @@ -644,7 +644,7 @@ private Formula getFormulaForVector(final LNGIntVector vector) { @Override public String toString() { - return String.format(this.solver.getClass().getSimpleName()); + return String.format("%s{result=%s, incremental=%s}", this.solver.getClass().getSimpleName(), this.result, this.incremental); } private Literal getLiteralFromIntLiteral(final int lit) { diff --git a/src/test/java/org/logicng/solvers/sat/SATTest.java b/src/test/java/org/logicng/solvers/sat/SATTest.java index 7e1f7826..e4c0a3f9 100644 --- a/src/test/java/org/logicng/solvers/sat/SATTest.java +++ b/src/test/java/org/logicng/solvers/sat/SATTest.java @@ -105,13 +105,13 @@ public SATTest() { this.solvers[9] = CleaneLing.full(this.f); this.testStrings = new String[10]; - this.testStrings[0] = "MiniSat{result=UNDEF, incremental=true}"; - this.testStrings[1] = "MiniSat{result=UNDEF, incremental=false}"; - this.testStrings[2] = "MiniSat{result=UNDEF, incremental=false}"; - this.testStrings[3] = "MiniSat{result=UNDEF, incremental=true}"; - this.testStrings[4] = "MiniSat{result=UNDEF, incremental=false}"; - this.testStrings[5] = "MiniSat{result=UNDEF, incremental=true}"; - this.testStrings[6] = "MiniSat{result=UNDEF, incremental=true}"; + this.testStrings[0] = "MiniSat2Solver{result=UNDEF, incremental=true}"; + this.testStrings[1] = "MiniSat2Solver{result=UNDEF, incremental=false}"; + this.testStrings[2] = "GlucoseSyrup{result=UNDEF, incremental=false}"; + this.testStrings[3] = "MiniCard{result=UNDEF, incremental=true}"; + this.testStrings[4] = "MiniCard{result=UNDEF, incremental=false}"; + this.testStrings[5] = "MiniSat2Solver{result=UNDEF, incremental=true}"; + this.testStrings[6] = "MiniSat2Solver{result=UNDEF, incremental=true}"; this.testStrings[7] = "CleaneLing{result=UNDEF, idx2name={}}"; this.testStrings[8] = "CleaneLing{result=UNDEF, idx2name={}}"; this.testStrings[9] = "CleaneLing{result=UNDEF, idx2name={}}"; @@ -123,7 +123,6 @@ public void testTrue() { s.add(F.TRUE); Assert.assertEquals(TRUE, s.sat()); Assert.assertEquals(0, s.model().size()); - Assert.assertTrue(s.toString().contains("MiniSat{result=TRUE, incremental=") || s.toString().equals("CleaneLing{result=TRUE, idx2name={}}")); s.reset(); } } From e461656e9ba6ce7009ba354ee6292a1d01bcd92b Mon Sep 17 00:00:00 2001 From: Christoph Zengler Date: Mon, 29 Jul 2019 13:35:33 +0200 Subject: [PATCH 09/16] Integrated backbone computation in top-level SAT solver --- .../logicng/backbones/BackboneGeneration.java | 5 +- .../java/org/logicng/solvers/CleaneLing.java | 2 +- .../java/org/logicng/solvers/MiniSat.java | 15 +- .../java/org/logicng/solvers/SATSolver.java | 2 +- .../org/logicng/solvers/sat/GlucoseSyrup.java | 657 ++++++++++-------- .../org/logicng/solvers/sat/MiniCard.java | 576 ++++++++------- .../logicng/solvers/sat/MiniSat2Solver.java | 271 -------- .../logicng/solvers/sat/MiniSatConfig.java | 22 - .../solvers/sat/MiniSatStyleSolver.java | 280 +++++++- .../transformations/BackboneSimplifier.java | 6 +- .../backbones/BackboneGenerationTest.java | 70 +- .../solvers/sat/ConfigurationsTest.java | 1 - 12 files changed, 992 insertions(+), 915 deletions(-) diff --git a/src/main/java/org/logicng/backbones/BackboneGeneration.java b/src/main/java/org/logicng/backbones/BackboneGeneration.java index 397a0689..f23fb4a6 100644 --- a/src/main/java/org/logicng/backbones/BackboneGeneration.java +++ b/src/main/java/org/logicng/backbones/BackboneGeneration.java @@ -32,7 +32,6 @@ import org.logicng.formulas.FormulaFactory; import org.logicng.formulas.Variable; import org.logicng.solvers.MiniSat; -import org.logicng.solvers.sat.MiniSatConfig; import org.logicng.util.FormulaHelper; import java.util.Collection; @@ -66,9 +65,9 @@ public static Backbone compute(final Collection formulas, final Collect throw new IllegalArgumentException("Provide at least one formula for backbone computation"); } final FormulaFactory f = formulas.iterator().next().factory(); - final MiniSat miniSat = MiniSat.miniSat(f, new MiniSatConfig.Builder().fastBackboneComputation(true).build()); + final MiniSat miniSat = MiniSat.miniSat(f); miniSat.add(formulas); - return miniSat.computeBackbone(variables, type); + return miniSat.backbone(variables, type); } /** diff --git a/src/main/java/org/logicng/solvers/CleaneLing.java b/src/main/java/org/logicng/solvers/CleaneLing.java index ff945a43..5691bf5e 100644 --- a/src/main/java/org/logicng/solvers/CleaneLing.java +++ b/src/main/java/org/logicng/solvers/CleaneLing.java @@ -314,7 +314,7 @@ public UNSATCore unsatCore() { } @Override - public Backbone computeBackbone(final Collection relevantVariables, final BackboneType type) { + public Backbone backbone(final Collection relevantVariables, final BackboneType type) { throw new UnsupportedOperationException("CleaneLing cannot compute fast backbones at the moment"); } diff --git a/src/main/java/org/logicng/solvers/MiniSat.java b/src/main/java/org/logicng/solvers/MiniSat.java index 3215045f..1b9628cc 100644 --- a/src/main/java/org/logicng/solvers/MiniSat.java +++ b/src/main/java/org/logicng/solvers/MiniSat.java @@ -519,16 +519,15 @@ public UNSATCore unsatCore() { } @Override - public Backbone computeBackbone(final Collection relevantVariables, final BackboneType type) { - if (!this.config.isFastBackboneComputation()) { - throw new UnsupportedOperationException("Cannot compute a backbone if fast backbone computation is not turned on"); + public Backbone backbone(final Collection relevantVariables, final BackboneType type) { + SolverState stateBeforeBackbone = null; + if (this.style == SolverStyle.MINISAT && this.incremental) { + stateBeforeBackbone = this.saveState(); } - if (!(this.solver instanceof MiniSat2Solver)) { - throw new UnsupportedOperationException("Only the original MiniSat can compute fast backbones"); + final Backbone backbone = this.solver.computeBackbone(relevantVariables, type); + if (this.style == SolverStyle.MINISAT && this.incremental) { + loadState(stateBeforeBackbone); } - final SolverState state = saveState(); - final Backbone backbone = ((MiniSat2Solver) this.solver).computeBackbone(relevantVariables, type); - loadState(state); return backbone; } diff --git a/src/main/java/org/logicng/solvers/SATSolver.java b/src/main/java/org/logicng/solvers/SATSolver.java index d4e479cc..4c24728b 100644 --- a/src/main/java/org/logicng/solvers/SATSolver.java +++ b/src/main/java/org/logicng/solvers/SATSolver.java @@ -470,7 +470,7 @@ public void setSolverToUndef() { * @param type the type of backbone which should be computed * @return the backbone */ - public abstract Backbone computeBackbone(final Collection relevantVariables, final BackboneType type); + public abstract Backbone backbone(final Collection relevantVariables, final BackboneType type); /** * Returns all unit propagated literals on level 0 of the current formula on the solver. diff --git a/src/main/java/org/logicng/solvers/sat/GlucoseSyrup.java b/src/main/java/org/logicng/solvers/sat/GlucoseSyrup.java index 906c7393..39dc80e7 100644 --- a/src/main/java/org/logicng/solvers/sat/GlucoseSyrup.java +++ b/src/main/java/org/logicng/solvers/sat/GlucoseSyrup.java @@ -170,13 +170,13 @@ private void initializeGlucose() { this.lbdQueue = new LNGBoundedLongQueue(); this.trailQueue = new LNGBoundedIntQueue(); this.assump = new LNGBooleanVector(); - this.lbdQueue.initSize(sizeLBDQueue); - this.trailQueue.initSize(sizeTrailQueue); + this.lbdQueue.initSize(this.sizeLBDQueue); + this.trailQueue.initSize(this.sizeTrailQueue); this.myflag = 0; this.analyzeBtLevel = 0; this.analyzeLBD = 0; this.analyzeSzWithoutSelectors = 0; - this.nbclausesbeforereduce = firstReduceDB; + this.nbclausesbeforereduce = this.firstReduceDB; this.conflicts = 0; this.conflictsRestarts = 0; this.sumLBD = 0; @@ -187,33 +187,33 @@ private void initializeGlucose() { * Initializes the glucose configuration. */ private void initializeGlucoseConfig() { - this.lbLBDMinimizingClause = glucoseConfig.lbLBDMinimizingClause; - this.lbLBDFrozenClause = glucoseConfig.lbLBDFrozenClause; - this.lbSizeMinimizingClause = glucoseConfig.lbSizeMinimizingClause; - this.firstReduceDB = glucoseConfig.firstReduceDB; - this.specialIncReduceDB = glucoseConfig.specialIncReduceDB; - this.incReduceDB = glucoseConfig.incReduceDB; - this.factorK = glucoseConfig.factorK; - this.factorR = glucoseConfig.factorR; - this.sizeLBDQueue = glucoseConfig.sizeLBDQueue; - this.sizeTrailQueue = glucoseConfig.sizeTrailQueue; - this.reduceOnSize = glucoseConfig.reduceOnSize; - this.reduceOnSizeSize = glucoseConfig.reduceOnSizeSize; - this.maxVarDecay = glucoseConfig.maxVarDecay; + this.lbLBDMinimizingClause = this.glucoseConfig.lbLBDMinimizingClause; + this.lbLBDFrozenClause = this.glucoseConfig.lbLBDFrozenClause; + this.lbSizeMinimizingClause = this.glucoseConfig.lbSizeMinimizingClause; + this.firstReduceDB = this.glucoseConfig.firstReduceDB; + this.specialIncReduceDB = this.glucoseConfig.specialIncReduceDB; + this.incReduceDB = this.glucoseConfig.incReduceDB; + this.factorK = this.glucoseConfig.factorK; + this.factorR = this.glucoseConfig.factorR; + this.sizeLBDQueue = this.glucoseConfig.sizeLBDQueue; + this.sizeTrailQueue = this.glucoseConfig.sizeTrailQueue; + this.reduceOnSize = this.glucoseConfig.reduceOnSize; + this.reduceOnSizeSize = this.glucoseConfig.reduceOnSizeSize; + this.maxVarDecay = this.glucoseConfig.maxVarDecay; } @Override - public int newVar(boolean sign, boolean dvar) { - int v = nVars(); - MSVariable newVar = new MSVariable(sign); - watches.push(new LNGVector()); - watches.push(new LNGVector()); - watchesBin.push(new LNGVector()); - watchesBin.push(new LNGVector()); - vars.push(newVar); - seen.push(false); - permDiff.push(0); - assump.push(false); + public int newVar(final boolean sign, final boolean dvar) { + final int v = nVars(); + final MSVariable newVar = new MSVariable(sign); + this.watches.push(new LNGVector()); + this.watches.push(new LNGVector()); + this.watchesBin.push(new LNGVector()); + this.watchesBin.push(new LNGVector()); + this.vars.push(newVar); + this.seen.push(false); + this.permDiff.push(0); + this.assump.push(false); newVar.setDecision(dvar); insertVarOrder(v); return v; @@ -226,13 +226,15 @@ public boolean addClause(final LNGIntVector ps, final Proposition proposition) { int i; int j; if (this.config.proofGeneration) { - LNGIntVector vec = new LNGIntVector(ps.size()); - for (i = 0; i < ps.size(); i++) + final LNGIntVector vec = new LNGIntVector(ps.size()); + for (i = 0; i < ps.size(); i++) { vec.push((var(ps.get(i)) + 1) * (-2 * (sign(ps.get(i)) ? 1 : 0) + 1)); + } this.pgOriginalClauses.push(new ProofInformation(vec, proposition)); } - if (!ok) + if (!this.ok) { return false; + } ps.sort(); boolean flag = false; @@ -241,44 +243,48 @@ public boolean addClause(final LNGIntVector ps, final Proposition proposition) { oc = new LNGIntVector(); for (i = 0, p = LIT_UNDEF; i < ps.size(); i++) { oc.push(ps.get(i)); - if (value(ps.get(i)) == Tristate.TRUE || ps.get(i) == not(p) || value(ps.get(i)) == Tristate.FALSE) + if (value(ps.get(i)) == Tristate.TRUE || ps.get(i) == not(p) || value(ps.get(i)) == Tristate.FALSE) { flag = true; + } } } - for (i = 0, j = 0, p = LIT_UNDEF; i < ps.size(); i++) - if (value(ps.get(i)) == Tristate.TRUE || ps.get(i) == not(p)) + for (i = 0, j = 0, p = LIT_UNDEF; i < ps.size(); i++) { + if (value(ps.get(i)) == Tristate.TRUE || ps.get(i) == not(p)) { return true; - else if (value(ps.get(i)) != Tristate.FALSE && ps.get(i) != p) { + } else if (value(ps.get(i)) != Tristate.FALSE && ps.get(i) != p) { p = ps.get(i); ps.set(j++, p); } + } ps.removeElements(i - j); if (flag) { LNGIntVector vec = new LNGIntVector(ps.size() + 1); vec.push(1); - for (i = 0; i < ps.size(); i++) + for (i = 0; i < ps.size(); i++) { vec.push((var(ps.get(i)) + 1) * (-2 * (sign(ps.get(i)) ? 1 : 0) + 1)); + } this.pgProof.push(vec); vec = new LNGIntVector(oc.size()); vec.push(-1); - for (i = 0; i < oc.size(); i++) + for (i = 0; i < oc.size(); i++) { vec.push((var(oc.get(i)) + 1) * (-2 * (sign(oc.get(i)) ? 1 : 0) + 1)); + } this.pgProof.push(vec); } if (ps.size() == 0) { - ok = false; + this.ok = false; return false; } else if (ps.size() == 1) { uncheckedEnqueue(ps.get(0), null); - ok = propagate() == null; - return ok; + this.ok = propagate() == null; + return this.ok; } else { final MSClause c = new MSClause(ps, false); - clauses.push(c); + this.clauses.push(c); attachClause(c); } return true; @@ -286,40 +292,50 @@ else if (value(ps.get(i)) != Tristate.FALSE && ps.get(i) != p) { @Override public Tristate solve(final SATHandler handler) { - if (this.config.incremental && this.config.proofGeneration) + if (this.config.incremental && this.config.proofGeneration) { throw new IllegalStateException("Cannot use incremental and proof generation at the same time"); + } this.handler = handler; - if (this.handler != null) + if (this.handler != null) { this.handler.startedSolving(); - model.clear(); - conflict.clear(); - if (!ok) + } + this.model.clear(); + this.conflict.clear(); + if (!this.ok) { return Tristate.FALSE; - for (int i = 0; i < assumptions.size(); i++) + } + for (int i = 0; i < this.assumptions.size(); i++) { this.assump.set(var(this.assumptions.get(i)), !sign(this.assumptions.get(i))); + } Tristate status = Tristate.UNDEF; - while (status == Tristate.UNDEF && !canceledByHandler) + while (status == Tristate.UNDEF && !this.canceledByHandler) { status = search(); + } if (this.config.proofGeneration) { - if (status == Tristate.FALSE) + if (status == Tristate.FALSE) { this.pgProof.push(new LNGIntVector(1, 0)); + } } if (status == Tristate.TRUE) { - model = new LNGBooleanVector(vars.size()); - for (final MSVariable v : this.vars) - model.push(v.assignment() == Tristate.TRUE); - } else if (status == Tristate.FALSE && conflict.size() == 0) - ok = false; - if (this.handler != null) + this.model = new LNGBooleanVector(this.vars.size()); + for (final MSVariable v : this.vars) { + this.model.push(v.assignment() == Tristate.TRUE); + } + } else if (status == Tristate.FALSE && this.conflict.size() == 0) { + this.ok = false; + } + if (this.handler != null) { this.handler.finishedSolving(); + } cancelUntil(0); this.handler = null; this.canceledByHandler = false; - for (int i = 0; i < assumptions.size(); i++) - assump.set(var(assumptions.get(i)), false); + for (int i = 0; i < this.assumptions.size(); i++) { + this.assump.set(var(this.assumptions.get(i)), false); + } return status; } @@ -335,50 +351,52 @@ public int[] saveState() { } @Override - public void loadState(int[] state) { + public void loadState(final int[] state) { throw new UnsupportedOperationException("The Glucose solver does not support state loading/saving"); } @Override - protected void uncheckedEnqueue(int lit, MSClause reason) { + protected void uncheckedEnqueue(final int lit, final MSClause reason) { assert value(lit) == Tristate.UNDEF; final MSVariable var = v(lit); var.assign(Tristate.fromBool(!sign(lit))); var.setReason(reason); var.setLevel(decisionLevel()); - trail.push(lit); + this.trail.push(lit); } @Override protected void attachClause(final MSClause c) { assert c.size() > 1; if (c.size() == 2) { - watchesBin.get(not(c.get(0))).push(new MSWatcher(c, c.get(1))); - watchesBin.get(not(c.get(1))).push(new MSWatcher(c, c.get(0))); + this.watchesBin.get(not(c.get(0))).push(new MSWatcher(c, c.get(1))); + this.watchesBin.get(not(c.get(1))).push(new MSWatcher(c, c.get(0))); + } else { + this.watches.get(not(c.get(0))).push(new MSWatcher(c, c.get(1))); + this.watches.get(not(c.get(1))).push(new MSWatcher(c, c.get(0))); + } + if (c.learnt()) { + this.learntsLiterals += c.size(); } else { - watches.get(not(c.get(0))).push(new MSWatcher(c, c.get(1))); - watches.get(not(c.get(1))).push(new MSWatcher(c, c.get(0))); + this.clausesLiterals += c.size(); } - if (c.learnt()) - learntsLiterals += c.size(); - else - clausesLiterals += c.size(); } @Override protected void detachClause(final MSClause c) { assert c.size() > 1; if (c.size() == 2) { - watchesBin.get(not(c.get(0))).remove(new MSWatcher(c, c.get(1))); - watchesBin.get(not(c.get(1))).remove(new MSWatcher(c, c.get(0))); + this.watchesBin.get(not(c.get(0))).remove(new MSWatcher(c, c.get(1))); + this.watchesBin.get(not(c.get(1))).remove(new MSWatcher(c, c.get(0))); } else { - watches.get(not(c.get(0))).remove(new MSWatcher(c, c.get(1))); - watches.get(not(c.get(1))).remove(new MSWatcher(c, c.get(0))); + this.watches.get(not(c.get(0))).remove(new MSWatcher(c, c.get(1))); + this.watches.get(not(c.get(1))).remove(new MSWatcher(c, c.get(0))); + } + if (c.learnt()) { + this.learntsLiterals -= c.size(); + } else { + this.clausesLiterals -= c.size(); } - if (c.learnt()) - learntsLiterals -= c.size(); - else - clausesLiterals -= c.size(); } @Override @@ -386,29 +404,31 @@ protected void removeClause(final MSClause c) { if (this.config.proofGeneration) { final LNGIntVector vec = new LNGIntVector(c.size()); vec.push(-1); - for (int i = 0; i < c.size(); i++) + for (int i = 0; i < c.size(); i++) { vec.push((var(c.get(i)) + 1) * (-2 * (sign(c.get(i)) ? 1 : 0) + 1)); + } this.pgProof.push(vec); } detachClause(c); - if (locked(c)) + if (locked(c)) { v(c.get(0)).setReason(null); + } } @Override protected MSClause propagate() { MSClause confl = null; int numProps = 0; - while (qhead < trail.size()) { - int p = trail.get(qhead++); - LNGVector ws = watches.get(p); + while (this.qhead < this.trail.size()) { + final int p = this.trail.get(this.qhead++); + final LNGVector ws = this.watches.get(p); int iInd = 0; int jInd = 0; numProps++; - LNGVector wbin = watchesBin.get(p); + final LNGVector wbin = this.watchesBin.get(p); for (int k = 0; k < wbin.size(); k++) { - int imp = wbin.get(k).blocker(); + final int imp = wbin.get(k).blocker(); if (value(imp) == Tristate.FALSE) { return wbin.get(k).clause(); } @@ -417,101 +437,106 @@ protected MSClause propagate() { } } while (iInd < ws.size()) { - MSWatcher i = ws.get(iInd); - int blocker = i.blocker(); + final MSWatcher i = ws.get(iInd); + final int blocker = i.blocker(); if (value(blocker) == Tristate.TRUE) { ws.set(jInd++, i); iInd++; continue; } - MSClause c = i.clause(); + final MSClause c = i.clause(); assert !c.oneWatched(); - int falseLit = not(p); + final int falseLit = not(p); if (c.get(0) == falseLit) { c.set(0, c.get(1)); c.set(1, falseLit); } assert c.get(1) == falseLit; iInd++; - int first = c.get(0); - MSWatcher w = new MSWatcher(c, first); + final int first = c.get(0); + final MSWatcher w = new MSWatcher(c, first); if (first != blocker && value(first) == Tristate.TRUE) { ws.set(jInd++, w); continue; } boolean foundWatch = false; - if (incremental) { + if (this.incremental) { int choosenPos = -1; for (int k = 2; k < c.size(); k++) { if (value(c.get(k)) != Tristate.FALSE) { - if (decisionLevel() > assumptions.size()) { + if (decisionLevel() > this.assumptions.size()) { choosenPos = k; break; } else { choosenPos = k; - if (value(c.get(k)) == Tristate.TRUE || !isSelector(var(c.get(k)))) + if (value(c.get(k)) == Tristate.TRUE || !isSelector(var(c.get(k)))) { break; + } } } } if (choosenPos != -1) { c.set(1, c.get(choosenPos)); c.set(choosenPos, falseLit); - watches.get(not(c.get(1))).push(w); + this.watches.get(not(c.get(1))).push(w); foundWatch = true; } } else { - for (int k = 2; k < c.size() && !foundWatch; k++) + for (int k = 2; k < c.size() && !foundWatch; k++) { if (value(c.get(k)) != Tristate.FALSE) { c.set(1, c.get(k)); c.set(k, falseLit); - watches.get(not(c.get(1))).push(w); + this.watches.get(not(c.get(1))).push(w); foundWatch = true; } + } } if (!foundWatch) { ws.set(jInd++, w); if (value(first) == Tristate.FALSE) { confl = c; - qhead = trail.size(); - while (iInd < ws.size()) + this.qhead = this.trail.size(); + while (iInd < ws.size()) { ws.set(jInd++, ws.get(iInd++)); - } else + } + } else { uncheckedEnqueue(first, c); + } } } ws.removeElements(iInd - jInd); } - simpDBProps -= numProps; + this.simpDBProps -= numProps; return confl; } @Override - protected boolean litRedundant(int p, int abstractLevels) { - analyzeStack.clear(); - analyzeStack.push(p); - int top = analyzeToClear.size(); - while (analyzeStack.size() > 0) { - assert v(analyzeStack.back()).reason() != null; - MSClause c = v(analyzeStack.back()).reason(); - analyzeStack.pop(); + protected boolean litRedundant(final int p, final int abstractLevels) { + this.analyzeStack.clear(); + this.analyzeStack.push(p); + final int top = this.analyzeToClear.size(); + while (this.analyzeStack.size() > 0) { + assert v(this.analyzeStack.back()).reason() != null; + final MSClause c = v(this.analyzeStack.back()).reason(); + this.analyzeStack.pop(); if (c.size() == 2 && value(c.get(0)) == Tristate.FALSE) { assert value(c.get(1)) == Tristate.TRUE; - int tmp = c.get(0); + final int tmp = c.get(0); c.set(0, c.get(1)); c.set(1, tmp); } for (int i = 1; i < c.size(); i++) { - int q = c.get(i); - if (!seen.get(var(q)) && v(q).level() > 0) { + final int q = c.get(i); + if (!this.seen.get(var(q)) && v(q).level() > 0) { if (v(q).reason() != null && (abstractLevel(var(q)) & abstractLevels) != 0) { - seen.set(var(q), true); - analyzeStack.push(q); - analyzeToClear.push(q); + this.seen.set(var(q), true); + this.analyzeStack.push(q); + this.analyzeToClear.push(q); } else { - for (int j = top; j < analyzeToClear.size(); j++) - seen.set(var(analyzeToClear.get(j)), false); - analyzeToClear.removeElements(analyzeToClear.size() - top); + for (int j = top; j < this.analyzeToClear.size(); j++) { + this.seen.set(var(this.analyzeToClear.get(j)), false); + } + this.analyzeToClear.removeElements(this.analyzeToClear.size() - top); return false; } } @@ -521,71 +546,61 @@ protected boolean litRedundant(int p, int abstractLevels) { } @Override - protected void analyzeFinal(int p, final LNGIntVector outConflict) { + protected void analyzeFinal(final int p, final LNGIntVector outConflict) { outConflict.clear(); outConflict.push(p); - if (decisionLevel() == 0) + if (decisionLevel() == 0) { return; - seen.set(var(p), true); + } + this.seen.set(var(p), true); int x; MSVariable v; - for (int i = trail.size() - 1; i >= trailLim.get(0); i--) { - x = var(trail.get(i)); - if (seen.get(x)) { + for (int i = this.trail.size() - 1; i >= this.trailLim.get(0); i--) { + x = var(this.trail.get(i)); + if (this.seen.get(x)) { v = this.vars.get(x); if (v.reason() == null) { assert v.level() > 0; - outConflict.push(not(trail.get(i))); + outConflict.push(not(this.trail.get(i))); } else { final MSClause c = v.reason(); - for (int j = c.size() == 2 ? 0 : 1; j < c.size(); j++) - if (v(c.get(j)).level() > 0) - seen.set(var(c.get(j)), true); + for (int j = c.size() == 2 ? 0 : 1; j < c.size(); j++) { + if (v(c.get(j)).level() > 0) { + this.seen.set(var(c.get(j)), true); + } + } } - seen.set(x, false); + this.seen.set(x, false); } } - seen.set(var(p), false); - } - - @Override - protected void cancelUntil(int level) { - if (decisionLevel() > level) { - for (int c = trail.size() - 1; c >= trailLim.get(level); c--) { - int x = var(trail.get(c)); - MSVariable v = this.vars.get(x); - v.assign(Tristate.UNDEF); - v.setPolarity(sign(trail.get(c))); - insertVarOrder(x); - } - qhead = trailLim.get(level); - trail.removeElements(trail.size() - trailLim.get(level)); - trailLim.removeElements(trailLim.size() - level); - } + this.seen.set(var(p), false); } @Override protected void reduceDB() { int i; int j; - learnts.manualSort(MSClause.glucoseComparator); - if (learnts.get(learnts.size() / RATIO_REMOVE_CLAUSES).lbd() <= 3) - nbclausesbeforereduce += specialIncReduceDB; - if (learnts.back().lbd() <= 5) - nbclausesbeforereduce += specialIncReduceDB; - int limit = learnts.size() / 2; - for (i = j = 0; i < learnts.size(); i++) { - final MSClause c = learnts.get(i); - if (c.lbd() > 2 && c.size() > 2 && c.canBeDel() && !locked(c) && (i < limit)) - removeClause(learnts.get(i)); - else { - if (!c.canBeDel()) + this.learnts.manualSort(MSClause.glucoseComparator); + if (this.learnts.get(this.learnts.size() / RATIO_REMOVE_CLAUSES).lbd() <= 3) { + this.nbclausesbeforereduce += this.specialIncReduceDB; + } + if (this.learnts.back().lbd() <= 5) { + this.nbclausesbeforereduce += this.specialIncReduceDB; + } + int limit = this.learnts.size() / 2; + for (i = j = 0; i < this.learnts.size(); i++) { + final MSClause c = this.learnts.get(i); + if (c.lbd() > 2 && c.size() > 2 && c.canBeDel() && !locked(c) && (i < limit)) { + removeClause(this.learnts.get(i)); + } else { + if (!c.canBeDel()) { limit++; + } c.setCanBeDel(true); - learnts.set(j++, learnts.get(i)); + this.learnts.set(j++, this.learnts.get(i)); } } - learnts.removeElements(i - j); + this.learnts.removeElements(i - j); } @Override @@ -594,43 +609,49 @@ protected void removeSatisfied(final LNGVector cs) { int j; for (i = j = 0; i < cs.size(); i++) { final MSClause c = cs.get(i); - if (satisfied(c)) + if (satisfied(c)) { removeClause(cs.get(i)); - else + } else { cs.set(j++, cs.get(i)); + } } cs.removeElements(i - j); } @Override protected boolean satisfied(final MSClause c) { - if (incremental) + if (this.incremental) { return (value(c.get(0)) == Tristate.TRUE) || (value(c.get(1)) == Tristate.TRUE); - for (int i = 0; i < c.size(); i++) - if (value(c.get(i)) == Tristate.TRUE) + } + for (int i = 0; i < c.size(); i++) { + if (value(c.get(i)) == Tristate.TRUE) { return true; + } + } return false; } @Override protected boolean simplify() { assert decisionLevel() == 0; - if (!ok) - return ok = false; - else { + if (!this.ok) { + return this.ok = false; + } else { final MSClause cr = propagate(); if (cr != null) { - return ok = false; + return this.ok = false; } } - if (nAssigns() == simpDBAssigns || (simpDBProps > 0)) + if (nAssigns() == this.simpDBAssigns || (this.simpDBProps > 0)) { return true; - removeSatisfied(learnts); - if (removeSatisfied) - removeSatisfied(clauses); + } + removeSatisfied(this.learnts); + if (this.removeSatisfied) { + removeSatisfied(this.clauses); + } rebuildOrderHeap(); - simpDBAssigns = nAssigns(); - simpDBProps = clausesLiterals + learntsLiterals; + this.simpDBAssigns = nAssigns(); + this.simpDBProps = this.clausesLiterals + this.learntsLiterals; return true; } @@ -640,39 +661,44 @@ protected boolean simplify() { * @param e parameter for incremental mode * @return the LBD */ - private long computeLBD(final LNGIntVector lits, int e) { + private long computeLBD(final LNGIntVector lits, final int e) { int end = e; long nblevels = 0; - myflag++; - if (incremental) { - if (end == -1) + this.myflag++; + if (this.incremental) { + if (end == -1) { end = lits.size(); + } long nbDone = 0; for (int i = 0; i < lits.size(); i++) { - if (nbDone >= end) + if (nbDone >= end) { break; - if (isSelector(var(lits.get(i)))) + } + if (isSelector(var(lits.get(i)))) { continue; + } nbDone++; - int l = v(lits.get(i)).level(); - if (permDiff.get(l) != myflag) { - permDiff.set(l, myflag); + final int l = v(lits.get(i)).level(); + if (this.permDiff.get(l) != this.myflag) { + this.permDiff.set(l, this.myflag); nblevels++; } } } else { for (int i = 0; i < lits.size(); i++) { - int l = v(lits.get(i)).level(); - if (permDiff.get(l) != myflag) { - permDiff.set(l, myflag); + final int l = v(lits.get(i)).level(); + if (this.permDiff.get(l) != this.myflag) { + this.permDiff.set(l, this.myflag); nblevels++; } } } - if (!reduceOnSize) + if (!this.reduceOnSize) { return nblevels; - if (lits.size() < reduceOnSizeSize) + } + if (lits.size() < this.reduceOnSizeSize) { return lits.size(); + } return lits.size() + nblevels; } @@ -683,34 +709,38 @@ private long computeLBD(final LNGIntVector lits, int e) { */ private long computeLBD(final MSClause c) { long nblevels = 0; - myflag++; - if (incremental) { + this.myflag++; + if (this.incremental) { long nbDone = 0; for (int i = 0; i < c.size(); i++) { - if (nbDone >= c.sizeWithoutSelectors()) + if (nbDone >= c.sizeWithoutSelectors()) { break; - if (isSelector(var(c.get(i)))) + } + if (isSelector(var(c.get(i)))) { continue; + } nbDone++; - int l = v(c.get(i)).level(); - if (permDiff.get(l) != myflag) { - permDiff.set(l, myflag); + final int l = v(c.get(i)).level(); + if (this.permDiff.get(l) != this.myflag) { + this.permDiff.set(l, this.myflag); nblevels++; } } } else { for (int i = 0; i < c.size(); i++) { - int l = v(c.get(i)).level(); - if (permDiff.get(l) != myflag) { - permDiff.set(l, myflag); + final int l = v(c.get(i)).level(); + if (this.permDiff.get(l) != this.myflag) { + this.permDiff.set(l, this.myflag); nblevels++; } } } - if (!reduceOnSize) + if (!this.reduceOnSize) { return nblevels; - if (c.size() < reduceOnSizeSize) + } + if (c.size() < this.reduceOnSizeSize) { return c.size(); + } return c.size() + nblevels; } @@ -719,8 +749,8 @@ private long computeLBD(final MSClause c) { * @param v the variable * @return {@code true} if the given variable is a selector variable */ - private boolean isSelector(int v) { - return incremental && assump.get(v); + private boolean isSelector(final int v) { + return this.incremental && this.assump.get(v); } /** @@ -728,24 +758,25 @@ private boolean isSelector(int v) { * @param outLearnt the vector where the new learnt 1-UIP clause is stored */ private void minimisationWithBinaryResolution(final LNGIntVector outLearnt) { - long lbd = computeLBD(outLearnt, -1); + final long lbd = computeLBD(outLearnt, -1); int p = not(outLearnt.get(0)); - if (lbd <= lbLBDMinimizingClause) { - myflag++; - for (int i = 1; i < outLearnt.size(); i++) - permDiff.set(var(outLearnt.get(i)), myflag); + if (lbd <= this.lbLBDMinimizingClause) { + this.myflag++; + for (int i = 1; i < outLearnt.size(); i++) { + this.permDiff.set(var(outLearnt.get(i)), this.myflag); + } int nb = 0; - for (final MSWatcher wbin : watchesBin.get(p)) { - int imp = wbin.blocker(); - if (permDiff.get(var(imp)) == myflag && value(imp) == Tristate.TRUE) { + for (final MSWatcher wbin : this.watchesBin.get(p)) { + final int imp = wbin.blocker(); + if (this.permDiff.get(var(imp)) == this.myflag && value(imp) == Tristate.TRUE) { nb++; - permDiff.set(var(imp), myflag - 1); + this.permDiff.set(var(imp), this.myflag - 1); } } int l = outLearnt.size() - 1; if (nb > 0) { for (int i = 1; i < outLearnt.size() - nb; i++) { - if (permDiff.get(var(outLearnt.get(i))) != myflag) { + if (this.permDiff.get(var(outLearnt.get(i))) != this.myflag) { p = outLearnt.get(l); outLearnt.set(l, outLearnt.get(i)); outLearnt.set(i, p); @@ -764,41 +795,45 @@ private void minimisationWithBinaryResolution(final LNGIntVector outLearnt) { * formula is SAT, and {@code UNKNOWN} if the state is not known yet (restart) */ private Tristate search() { - assert ok; - LNGIntVector learntClause = new LNGIntVector(); - LNGIntVector selectors = new LNGIntVector(); + assert this.ok; + final LNGIntVector learntClause = new LNGIntVector(); + final LNGIntVector selectors = new LNGIntVector(); boolean blocked = false; while (true) { final MSClause confl = propagate(); if (confl != null) { - if (handler != null && !handler.detectedConflict()) { - canceledByHandler = true; + if (this.handler != null && !this.handler.detectedConflict()) { + this.canceledByHandler = true; return Tristate.UNDEF; } - conflicts++; - conflictsRestarts++; - if (conflicts % 5000 == 0 && varDecay < maxVarDecay) - varDecay += 0.01; - if (decisionLevel() == 0) + this.conflicts++; + this.conflictsRestarts++; + if (this.conflicts % 5000 == 0 && this.varDecay < this.maxVarDecay) { + this.varDecay += 0.01; + } + if (decisionLevel() == 0) { return Tristate.FALSE; - trailQueue.push(trail.size()); - if (conflictsRestarts > LB_BLOCKING_RESTART && lbdQueue.valid() && trail.size() > factorR * trailQueue.avg()) { - lbdQueue.fastClear(); - if (!blocked) + } + this.trailQueue.push(this.trail.size()); + if (this.conflictsRestarts > LB_BLOCKING_RESTART && this.lbdQueue.valid() && this.trail.size() > this.factorR * this.trailQueue.avg()) { + this.lbdQueue.fastClear(); + if (!blocked) { blocked = true; + } } learntClause.clear(); selectors.clear(); analyze(confl, learntClause, selectors); - lbdQueue.push(analyzeLBD); - sumLBD += analyzeLBD; - cancelUntil(analyzeBtLevel); + this.lbdQueue.push(this.analyzeLBD); + this.sumLBD += this.analyzeLBD; + cancelUntil(this.analyzeBtLevel); if (this.config.proofGeneration) { final LNGIntVector vec = new LNGIntVector(learntClause.size()); vec.push(1); - for (int i = 0; i < learntClause.size(); i++) + for (int i = 0; i < learntClause.size(); i++) { vec.push((var(learntClause.get(i)) + 1) * (-2 * (sign(learntClause.get(i)) ? 1 : 0) + 1)); + } this.pgProof.push(vec); } @@ -806,10 +841,10 @@ private Tristate search() { uncheckedEnqueue(learntClause.get(0), null); } else { final MSClause cr = new MSClause(learntClause, true); - cr.setLBD(analyzeLBD); + cr.setLBD(this.analyzeLBD); cr.setOneWatched(false); - cr.setSizeWithoutSelectors(analyzeSzWithoutSelectors); - learnts.push(cr); + cr.setSizeWithoutSelectors(this.analyzeSzWithoutSelectors); + this.learnts.push(cr); attachClause(cr); claBumpActivity(cr); uncheckedEnqueue(learntClause.get(0), cr); @@ -817,28 +852,30 @@ private Tristate search() { varDecayActivity(); claDecayActivity(); } else { - if (lbdQueue.valid() && (lbdQueue.avg() * factorK) > (sumLBD / conflictsRestarts)) { - lbdQueue.fastClear(); + if (this.lbdQueue.valid() && (this.lbdQueue.avg() * this.factorK) > (this.sumLBD / this.conflictsRestarts)) { + this.lbdQueue.fastClear(); int bt = 0; - if (incremental) - bt = (decisionLevel() < assumptions.size()) ? decisionLevel() : assumptions.size(); + if (this.incremental) { + bt = (decisionLevel() < this.assumptions.size()) ? decisionLevel() : this.assumptions.size(); + } cancelUntil(bt); return Tristate.UNDEF; } - if (decisionLevel() == 0 && !simplify()) + if (decisionLevel() == 0 && !simplify()) { return Tristate.FALSE; - if (conflicts >= (curRestart * nbclausesbeforereduce) && learnts.size() > 0) { - curRestart = (conflicts / nbclausesbeforereduce) + 1; + } + if (this.conflicts >= (this.curRestart * this.nbclausesbeforereduce) && this.learnts.size() > 0) { + this.curRestart = (this.conflicts / this.nbclausesbeforereduce) + 1; reduceDB(); - nbclausesbeforereduce += incReduceDB; + this.nbclausesbeforereduce += this.incReduceDB; } int next = LIT_UNDEF; - while (decisionLevel() < assumptions.size()) { - int p = assumptions.get(decisionLevel()); - if (value(p) == Tristate.TRUE) - trailLim.push(trail.size()); - else if (value(p) == Tristate.FALSE) { - analyzeFinal(not(p), conflict); + while (decisionLevel() < this.assumptions.size()) { + final int p = this.assumptions.get(decisionLevel()); + if (value(p) == Tristate.TRUE) { + this.trailLim.push(this.trail.size()); + } else if (value(p) == Tristate.FALSE) { + analyzeFinal(not(p), this.conflict); return Tristate.FALSE; } else { next = p; @@ -847,10 +884,11 @@ else if (value(p) == Tristate.FALSE) { } if (next == LIT_UNDEF) { next = pickBranchLit(); - if (next == LIT_UNDEF) + if (next == LIT_UNDEF) { return Tristate.TRUE; + } } - trailLim.push(trail.size()); + this.trailLim.push(this.trail.size()); uncheckedEnqueue(next, null); } } @@ -869,53 +907,58 @@ private void analyze(final MSClause conflictClause, final LNGIntVector outLearnt int pathC = 0; int p = LIT_UNDEF; outLearnt.push(-1); - int index = trail.size() - 1; + int index = this.trail.size() - 1; do { assert c != null; if (p != LIT_UNDEF && c.size() == 2 && value(c.get(0)) == Tristate.FALSE) { assert value(c.get(1)) == Tristate.TRUE; - int tmp = c.get(0); + final int tmp = c.get(0); c.set(0, c.get(1)); c.set(1, tmp); } - if (c.learnt()) + if (c.learnt()) { claBumpActivity(c); - else { - if (!c.seen()) + } else { + if (!c.seen()) { c.setSeen(true); + } } if (c.learnt() && c.lbd() > 2) { - long nblevels = computeLBD(c); + final long nblevels = computeLBD(c); if (nblevels + 1 < c.lbd()) { - if (c.lbd() <= lbLBDFrozenClause) { + if (c.lbd() <= this.lbLBDFrozenClause) { c.setCanBeDel(false); } c.setLBD(nblevels); } } for (int j = (p == LIT_UNDEF) ? 0 : 1; j < c.size(); j++) { - int q = c.get(j); - if (!seen.get(var(q)) && v(q).level() != 0) { - if (!isSelector(var(q))) + final int q = c.get(j); + if (!this.seen.get(var(q)) && v(q).level() != 0) { + if (!isSelector(var(q))) { varBumpActivity(var(q)); - seen.set(var(q), true); + } + this.seen.set(var(q), true); if (v(q).level() >= decisionLevel()) { pathC++; - if (!isSelector(var(q)) && (v(q).reason() != null) && v(q).reason().learnt()) - lastDecisionLevel.push(q); + if (!isSelector(var(q)) && (v(q).reason() != null) && v(q).reason().learnt()) { + this.lastDecisionLevel.push(q); + } } else { if (isSelector(var(q))) { assert value(q) == Tristate.FALSE; selectors.push(q); - } else + } else { outLearnt.push(q); + } } } } - while (!seen.get(var(trail.get(index--)))) ; - p = trail.get(index + 1); + while (!this.seen.get(var(this.trail.get(index--)))) { + } + p = this.trail.get(index + 1); c = v(p).reason(); - seen.set(var(p), false); + this.seen.set(var(p), false); pathC--; } while (pathC > 0); outLearnt.set(0, not(p)); @@ -930,68 +973,82 @@ private void analyze(final MSClause conflictClause, final LNGIntVector outLearnt private void simplifyClause(final LNGIntVector outLearnt, final LNGIntVector selectors) { int i; int j; - for (i = 0; i < selectors.size(); i++) + for (i = 0; i < selectors.size(); i++) { outLearnt.push(selectors.get(i)); + } this.analyzeToClear = new LNGIntVector(outLearnt); - if (ccminMode == MiniSatConfig.ClauseMinimization.DEEP) { + if (this.ccminMode == MiniSatConfig.ClauseMinimization.DEEP) { int abstractLevel = 0; - for (i = 1; i < outLearnt.size(); i++) + for (i = 1; i < outLearnt.size(); i++) { abstractLevel |= abstractLevel(var(outLearnt.get(i))); - for (i = j = 1; i < outLearnt.size(); i++) - if (v(outLearnt.get(i)).reason() == null || !litRedundant(outLearnt.get(i), abstractLevel)) + } + for (i = j = 1; i < outLearnt.size(); i++) { + if (v(outLearnt.get(i)).reason() == null || !litRedundant(outLearnt.get(i), abstractLevel)) { outLearnt.set(j++, outLearnt.get(i)); - } else if (ccminMode == MiniSatConfig.ClauseMinimization.BASIC) { + } + } + } else if (this.ccminMode == MiniSatConfig.ClauseMinimization.BASIC) { for (i = j = 1; i < outLearnt.size(); i++) { - MSVariable v = v(outLearnt.get(i)); - if (v.reason() == null) + final MSVariable v = v(outLearnt.get(i)); + if (v.reason() == null) { outLearnt.set(j++, outLearnt.get(i)); - else { - MSClause c = v(outLearnt.get(i)).reason(); - for (int k = c.size() == 2 ? 0 : 1; k < c.size(); k++) - if (!seen.get(var(c.get(k))) && v(c.get(k)).level() > 0) { + } else { + final MSClause c = v(outLearnt.get(i)).reason(); + for (int k = c.size() == 2 ? 0 : 1; k < c.size(); k++) { + if (!this.seen.get(var(c.get(k))) && v(c.get(k)).level() > 0) { outLearnt.set(j++, outLearnt.get(i)); break; } + } } } - } else + } else { i = j = outLearnt.size(); + } outLearnt.removeElements(i - j); - if (!incremental && outLearnt.size() <= lbSizeMinimizingClause) + if (!this.incremental && outLearnt.size() <= this.lbSizeMinimizingClause) { minimisationWithBinaryResolution(outLearnt); - analyzeBtLevel = 0; + } + this.analyzeBtLevel = 0; if (outLearnt.size() > 1) { int max = 1; - for (int k = 2; k < outLearnt.size(); k++) - if (v(outLearnt.get(k)).level() > v(outLearnt.get(max)).level()) + for (int k = 2; k < outLearnt.size(); k++) { + if (v(outLearnt.get(k)).level() > v(outLearnt.get(max)).level()) { max = k; - int p = outLearnt.get(max); + } + } + final int p = outLearnt.get(max); outLearnt.set(max, outLearnt.get(1)); outLearnt.set(1, p); - analyzeBtLevel = v(p).level(); + this.analyzeBtLevel = v(p).level(); } - analyzeSzWithoutSelectors = 0; - if (incremental) { + this.analyzeSzWithoutSelectors = 0; + if (this.incremental) { for (int k = 0; k < outLearnt.size(); k++) { - if (!isSelector(var(outLearnt.get(k)))) - analyzeSzWithoutSelectors++; - else if (k > 0) + if (!isSelector(var(outLearnt.get(k)))) { + this.analyzeSzWithoutSelectors++; + } else if (k > 0) { break; + } } - } else - analyzeSzWithoutSelectors = outLearnt.size(); - analyzeLBD = computeLBD(outLearnt, outLearnt.size() - selectors.size()); - if (lastDecisionLevel.size() > 0) { - for (int k = 0; k < lastDecisionLevel.size(); k++) { - if ((v(lastDecisionLevel.get(k)).reason()).lbd() < analyzeLBD) - varBumpActivity(var(lastDecisionLevel.get(k))); + } else { + this.analyzeSzWithoutSelectors = outLearnt.size(); + } + this.analyzeLBD = computeLBD(outLearnt, outLearnt.size() - selectors.size()); + if (this.lastDecisionLevel.size() > 0) { + for (int k = 0; k < this.lastDecisionLevel.size(); k++) { + if ((v(this.lastDecisionLevel.get(k)).reason()).lbd() < this.analyzeLBD) { + varBumpActivity(var(this.lastDecisionLevel.get(k))); + } } - lastDecisionLevel.clear(); + this.lastDecisionLevel.clear(); + } + for (int m = 0; m < this.analyzeToClear.size(); m++) { + this.seen.set(var(this.analyzeToClear.get(m)), false); + } + for (int m = 0; m < selectors.size(); m++) { + this.seen.set(var(selectors.get(m)), false); } - for (int m = 0; m < analyzeToClear.size(); m++) - seen.set(var(analyzeToClear.get(m)), false); - for (int m = 0; m < selectors.size(); m++) - seen.set(var(selectors.get(m)), false); } } diff --git a/src/main/java/org/logicng/solvers/sat/MiniCard.java b/src/main/java/org/logicng/solvers/sat/MiniCard.java index 8b8d5d10..9700738d 100644 --- a/src/main/java/org/logicng/solvers/sat/MiniCard.java +++ b/src/main/java/org/logicng/solvers/sat/MiniCard.java @@ -97,7 +97,7 @@ public MiniCard(final MiniSatConfig config) { * Initializes the additional parameters. */ private void initializeMiniSAT() { - unitClauses = new LNGIntVector(); + this.unitClauses = new LNGIntVector(); this.learntsizeAdjustConfl = 0; this.learntsizeAdjustCnt = 0; this.learntsizeAdjustStartConfl = 100; @@ -106,13 +106,13 @@ private void initializeMiniSAT() { } @Override - public int newVar(boolean sign, boolean dvar) { - int v = vars.size(); - MSVariable newVar = new MSVariable(sign); - vars.push(newVar); - watches.push(new LNGVector()); - watches.push(new LNGVector()); - seen.push(false); + public int newVar(final boolean sign, final boolean dvar) { + final int v = this.vars.size(); + final MSVariable newVar = new MSVariable(sign); + this.vars.push(newVar); + this.watches.push(new LNGVector()); + this.watches.push(new LNGVector()); + this.seen.push(false); newVar.setDecision(dvar); insertVarOrder(v); return v; @@ -121,32 +121,35 @@ public int newVar(boolean sign, boolean dvar) { @Override public boolean addClause(final LNGIntVector ps, final Proposition proposition) { assert decisionLevel() == 0; - if (!ok) + if (!this.ok) { return false; + } ps.sort(); int p; int i; int j; - for (i = 0, j = 0, p = LIT_UNDEF; i < ps.size(); i++) - if (value(ps.get(i)) == Tristate.TRUE || ps.get(i) == not(p)) + for (i = 0, j = 0, p = LIT_UNDEF; i < ps.size(); i++) { + if (value(ps.get(i)) == Tristate.TRUE || ps.get(i) == not(p)) { return true; - else if (value(ps.get(i)) != Tristate.FALSE && ps.get(i) != p) { + } else if (value(ps.get(i)) != Tristate.FALSE && ps.get(i) != p) { p = ps.get(i); ps.set(j++, p); } + } ps.removeElements(i - j); if (ps.empty()) { - ok = false; + this.ok = false; return false; } else if (ps.size() == 1) { uncheckedEnqueue(ps.get(0), null); - ok = propagate() == null; - if (incremental) - unitClauses.push(ps.get(0)); - return ok; + this.ok = propagate() == null; + if (this.incremental) { + this.unitClauses.push(ps.get(0)); + } + return this.ok; } else { final MSClause c = new MSClause(ps, false); - clauses.push(c); + this.clauses.push(c); attachClause(c); } return true; @@ -155,30 +158,35 @@ else if (value(ps.get(i)) != Tristate.FALSE && ps.get(i) != p) { @Override public Tristate solve(final SATHandler handler) { this.handler = handler; - if (this.handler != null) + if (this.handler != null) { this.handler.startedSolving(); - model.clear(); - conflict.clear(); - if (!ok) + } + this.model.clear(); + this.conflict.clear(); + if (!this.ok) { return Tristate.FALSE; - learntsizeAdjustConfl = learntsizeAdjustStartConfl; - learntsizeAdjustCnt = (int) learntsizeAdjustConfl; - maxLearnts = clauses.size() * learntsizeFactor; + } + this.learntsizeAdjustConfl = this.learntsizeAdjustStartConfl; + this.learntsizeAdjustCnt = (int) this.learntsizeAdjustConfl; + this.maxLearnts = this.clauses.size() * this.learntsizeFactor; Tristate status = Tristate.UNDEF; int currRestarts = 0; - while (status == Tristate.UNDEF && !canceledByHandler) { - double restBase = luby(restartInc, currRestarts); - status = search((int) (restBase * restartFirst)); + while (status == Tristate.UNDEF && !this.canceledByHandler) { + final double restBase = luby(this.restartInc, currRestarts); + status = search((int) (restBase * this.restartFirst)); currRestarts++; } if (status == Tristate.TRUE) { - model = new LNGBooleanVector(vars.size()); - for (final MSVariable v : this.vars) - model.push(v.assignment() == Tristate.TRUE); - } else if (status == Tristate.FALSE && conflict.empty()) - ok = false; - if (this.handler != null) + this.model = new LNGBooleanVector(this.vars.size()); + for (final MSVariable v : this.vars) { + this.model.push(v.assignment() == Tristate.TRUE); + } + } else if (status == Tristate.FALSE && this.conflict.empty()) { + this.ok = false; + } + if (this.handler != null) { this.handler.finishedSolving(); + } cancelUntil(0); this.handler = null; this.canceledByHandler = false; @@ -200,36 +208,41 @@ public void reset() { */ @Override public int[] saveState() { - if (!incremental) + if (!this.incremental) { throw new IllegalStateException("Cannot save a state when the incremental mode is deactivated"); - int[] state; + } + final int[] state; state = new int[5]; - state[0] = ok ? 1 : 0; - state[1] = vars.size(); - state[2] = clauses.size(); - state[3] = learnts.size(); - state[4] = unitClauses.size(); + state[0] = this.ok ? 1 : 0; + state[1] = this.vars.size(); + state[2] = this.clauses.size(); + state[3] = this.learnts.size(); + state[4] = this.unitClauses.size(); return state; } @Override - public void loadState(int[] state) { - if (!incremental) + public void loadState(final int[] state) { + if (!this.incremental) { throw new IllegalStateException("Cannot save a state when the incremental mode is deactivated"); + } int i; completeBacktrack(); this.ok = state[0] == 1; - int newVarsSize = Math.min(state[1], vars.size()); - for (i = this.vars.size() - 1; i >= newVarsSize; i--) + final int newVarsSize = Math.min(state[1], this.vars.size()); + for (i = this.vars.size() - 1; i >= newVarsSize; i--) { this.orderHeap.remove(this.name2idx.remove(this.idx2name.remove(i))); - vars.shrinkTo(newVarsSize); - int newClausesSize = Math.min(state[2], this.clauses.size()); - for (i = this.clauses.size() - 1; i >= newClausesSize; i--) + } + this.vars.shrinkTo(newVarsSize); + final int newClausesSize = Math.min(state[2], this.clauses.size()); + for (i = this.clauses.size() - 1; i >= newClausesSize; i--) { simpleRemoveClause(this.clauses.get(i)); + } this.clauses.shrinkTo(newClausesSize); - int newLearntsSize = Math.min(state[3], this.learnts.size()); - for (i = this.learnts.size() - 1; i >= newLearntsSize; i--) + final int newLearntsSize = Math.min(state[3], this.learnts.size()); + for (i = this.learnts.size() - 1; i >= newLearntsSize; i--) { simpleRemoveClause(this.learnts.get(i)); + } this.learnts.shrinkTo(newLearntsSize); this.watches.shrinkTo(newVarsSize * 2); this.unitClauses.shrinkTo(state[4]); @@ -240,13 +253,13 @@ public void loadState(int[] state) { } @Override - protected void uncheckedEnqueue(int lit, MSClause reason) { + protected void uncheckedEnqueue(final int lit, final MSClause reason) { assert value(lit) == Tristate.UNDEF; final MSVariable var = v(lit); var.assign(Tristate.fromBool(!sign(lit))); var.setReason(reason); var.setLevel(decisionLevel()); - trail.push(lit); + this.trail.push(lit); } @Override @@ -254,17 +267,18 @@ protected void attachClause(final MSClause c) { if (c.isAtMost()) { for (int i = 0; i < c.atMostWatchers(); i++) { final int l = c.get(i); - watches.get(l).push(new MSWatcher(c, LIT_UNDEF)); + this.watches.get(l).push(new MSWatcher(c, LIT_UNDEF)); } - clausesLiterals += c.size(); + this.clausesLiterals += c.size(); } else { assert c.size() > 1; - watches.get(not(c.get(0))).push(new MSWatcher(c, c.get(1))); - watches.get(not(c.get(1))).push(new MSWatcher(c, c.get(0))); - if (c.learnt()) - learntsLiterals += c.size(); - else - clausesLiterals += c.size(); + this.watches.get(not(c.get(0))).push(new MSWatcher(c, c.get(1))); + this.watches.get(not(c.get(1))).push(new MSWatcher(c, c.get(0))); + if (c.learnt()) { + this.learntsLiterals += c.size(); + } else { + this.clausesLiterals += c.size(); + } } } @@ -272,25 +286,29 @@ protected void attachClause(final MSClause c) { protected void detachClause(final MSClause c) { assert !c.isAtMost(); assert c.size() > 1; - watches.get(not(c.get(0))).remove(new MSWatcher(c, c.get(1))); - watches.get(not(c.get(1))).remove(new MSWatcher(c, c.get(0))); - if (c.learnt()) - learntsLiterals -= c.size(); - else - clausesLiterals -= c.size(); + this.watches.get(not(c.get(0))).remove(new MSWatcher(c, c.get(1))); + this.watches.get(not(c.get(1))).remove(new MSWatcher(c, c.get(0))); + if (c.learnt()) { + this.learntsLiterals -= c.size(); + } else { + this.clausesLiterals -= c.size(); + } } @Override protected void removeClause(final MSClause c) { if (c.isAtMost()) { detachAtMost(c); - for (int i = 0; i < c.atMostWatchers(); i++) - if (value(c.get(i)) == Tristate.FALSE && v(c.get(i)).reason() != null && v(c.get(i)).reason() == c) + for (int i = 0; i < c.atMostWatchers(); i++) { + if (value(c.get(i)) == Tristate.FALSE && v(c.get(i)).reason() != null && v(c.get(i)).reason() == c) { v(c.get(i)).setReason(null); + } + } } else { detachClause(c); - if (locked(c)) + if (locked(c)) { v(c.get(0)).setReason(null); + } } } @@ -298,106 +316,113 @@ protected void removeClause(final MSClause c) { protected MSClause propagate() { MSClause confl = null; int numProps = 0; - while (qhead < trail.size()) { - int p = trail.get(qhead++); - LNGVector ws = watches.get(p); + while (this.qhead < this.trail.size()) { + final int p = this.trail.get(this.qhead++); + final LNGVector ws = this.watches.get(p); int iInd = 0; int jInd = 0; numProps++; while (iInd < ws.size()) { - MSWatcher i = ws.get(iInd); - int blocker = i.blocker(); + final MSWatcher i = ws.get(iInd); + final int blocker = i.blocker(); if (blocker != LIT_UNDEF && value(blocker) == Tristate.TRUE) { ws.set(jInd++, i); iInd++; continue; } - MSClause c = i.clause(); + final MSClause c = i.clause(); if (c.isAtMost()) { - int newWatch = findNewWatch(c, p); + final int newWatch = findNewWatch(c, p); if (newWatch == LIT_UNDEF) { - for (int k = 0; k < c.atMostWatchers(); k++) + for (int k = 0; k < c.atMostWatchers(); k++) { if (c.get(k) != p && value(c.get(k)) != Tristate.FALSE) { assert value(c.get(k)) == Tristate.UNDEF || value(c.get(k)) == Tristate.FALSE; uncheckedEnqueue(not(c.get(k)), c); } + } ws.set(jInd++, ws.get(iInd++)); } else if (newWatch == LIT_ERROR) { confl = c; - qhead = trail.size(); - while (iInd < ws.size()) + this.qhead = this.trail.size(); + while (iInd < ws.size()) { ws.set(jInd++, ws.get(iInd++)); - } else if (newWatch == p) + } + } else if (newWatch == p) { ws.set(jInd++, ws.get(iInd++)); - else { + } else { iInd++; - MSWatcher w = new MSWatcher(c, LIT_UNDEF); - watches.get(newWatch).push(w); + final MSWatcher w = new MSWatcher(c, LIT_UNDEF); + this.watches.get(newWatch).push(w); } } else { - int falseLit = not(p); + final int falseLit = not(p); if (c.get(0) == falseLit) { c.set(0, c.get(1)); c.set(1, falseLit); } assert c.get(1) == falseLit; iInd++; - int first = c.get(0); - MSWatcher w = new MSWatcher(c, first); + final int first = c.get(0); + final MSWatcher w = new MSWatcher(c, first); if (first != blocker && value(first) == Tristate.TRUE) { ws.set(jInd++, w); continue; } boolean foundWatch = false; - for (int k = 2; k < c.size() && !foundWatch; k++) + for (int k = 2; k < c.size() && !foundWatch; k++) { if (value(c.get(k)) != Tristate.FALSE) { c.set(1, c.get(k)); c.set(k, falseLit); - watches.get(not(c.get(1))).push(w); + this.watches.get(not(c.get(1))).push(w); foundWatch = true; } + } if (!foundWatch) { ws.set(jInd++, w); if (value(first) == Tristate.FALSE) { confl = c; - qhead = trail.size(); - while (iInd < ws.size()) + this.qhead = this.trail.size(); + while (iInd < ws.size()) { ws.set(jInd++, ws.get(iInd++)); - } else + } + } else { uncheckedEnqueue(first, c); + } } } } ws.removeElements(iInd - jInd); } - simpDBProps -= numProps; + this.simpDBProps -= numProps; return confl; } @Override - protected boolean litRedundant(int p, int abstractLevels) { - analyzeStack.clear(); - analyzeStack.push(p); - int top = analyzeToClear.size(); - while (analyzeStack.size() > 0) { - assert v(analyzeStack.back()).reason() != null; - MSClause c = v(analyzeStack.back()).reason(); - analyzeStack.pop(); + protected boolean litRedundant(final int p, final int abstractLevels) { + this.analyzeStack.clear(); + this.analyzeStack.push(p); + final int top = this.analyzeToClear.size(); + while (this.analyzeStack.size() > 0) { + assert v(this.analyzeStack.back()).reason() != null; + final MSClause c = v(this.analyzeStack.back()).reason(); + this.analyzeStack.pop(); if (c.isAtMost()) { for (int i = 0; i < c.size(); i++) { - if (value(c.get(i)) != Tristate.TRUE) + if (value(c.get(i)) != Tristate.TRUE) { continue; + } final int q = not(c.get(i)); - if (!seen.get(var(q)) && v(q).level() > 0) { + if (!this.seen.get(var(q)) && v(q).level() > 0) { if (v(q).reason() != null && (abstractLevel(var(q)) & abstractLevels) != 0) { - seen.set(var(q), true); - analyzeStack.push(q); - analyzeToClear.push(q); + this.seen.set(var(q), true); + this.analyzeStack.push(q); + this.analyzeToClear.push(q); } else { - for (int j = top; j < analyzeToClear.size(); j++) - seen.set(var(analyzeToClear.get(j)), false); - analyzeToClear.removeElements(analyzeToClear.size() - top); + for (int j = top; j < this.analyzeToClear.size(); j++) { + this.seen.set(var(this.analyzeToClear.get(j)), false); + } + this.analyzeToClear.removeElements(this.analyzeToClear.size() - top); return false; } } @@ -405,15 +430,16 @@ protected boolean litRedundant(int p, int abstractLevels) { } else { for (int i = 1; i < c.size(); i++) { final int q = c.get(i); - if (!seen.get(var(q)) && v(q).level() > 0) { + if (!this.seen.get(var(q)) && v(q).level() > 0) { if (v(q).reason() != null && (abstractLevel(var(q)) & abstractLevels) != 0) { - seen.set(var(q), true); - analyzeStack.push(q); - analyzeToClear.push(q); + this.seen.set(var(q), true); + this.analyzeStack.push(q); + this.analyzeToClear.push(q); } else { - for (int j = top; j < analyzeToClear.size(); j++) - seen.set(var(analyzeToClear.get(j)), false); - analyzeToClear.removeElements(analyzeToClear.size() - top); + for (int j = top; j < this.analyzeToClear.size(); j++) { + this.seen.set(var(this.analyzeToClear.get(j)), false); + } + this.analyzeToClear.removeElements(this.analyzeToClear.size() - top); return false; } } @@ -424,70 +450,60 @@ protected boolean litRedundant(int p, int abstractLevels) { } @Override - protected void analyzeFinal(int p, final LNGIntVector outConflict) { + protected void analyzeFinal(final int p, final LNGIntVector outConflict) { outConflict.clear(); outConflict.push(p); - if (decisionLevel() == 0) + if (decisionLevel() == 0) { return; - seen.set(var(p), true); + } + this.seen.set(var(p), true); int x; MSVariable v; - for (int i = trail.size() - 1; i >= trailLim.get(0); i--) { - x = var(trail.get(i)); - if (seen.get(x)) { + for (int i = this.trail.size() - 1; i >= this.trailLim.get(0); i--) { + x = var(this.trail.get(i)); + if (this.seen.get(x)) { v = this.vars.get(x); if (v.reason() == null) { assert v.level() > 0; - outConflict.push(not(trail.get(i))); + outConflict.push(not(this.trail.get(i))); } else { final MSClause c = v.reason(); if (!c.isAtMost()) { - for (int j = 1; j < c.size(); j++) - if (v(c.get(j)).level() > 0) - seen.set(var(c.get(j)), true); + for (int j = 1; j < c.size(); j++) { + if (v(c.get(j)).level() > 0) { + this.seen.set(var(c.get(j)), true); + } + } } else { - for (int j = 0; j < c.size(); j++) - if (value(c.get(j)) == Tristate.TRUE && v(c.get(j)).level() > 0) - seen.set(var(c.get(j)), true); + for (int j = 0; j < c.size(); j++) { + if (value(c.get(j)) == Tristate.TRUE && v(c.get(j)).level() > 0) { + this.seen.set(var(c.get(j)), true); + } + } } } - seen.set(x, false); + this.seen.set(x, false); } } - seen.set(var(p), false); - } - - @Override - protected void cancelUntil(int level) { - if (decisionLevel() > level) { - for (int c = trail.size() - 1; c >= trailLim.get(level); c--) { - int x = var(trail.get(c)); - MSVariable v = this.vars.get(x); - v.assign(Tristate.UNDEF); - v.setPolarity(sign(trail.get(c))); - insertVarOrder(x); - } - qhead = trailLim.get(level); - trail.removeElements(trail.size() - trailLim.get(level)); - trailLim.removeElements(trailLim.size() - level); - } + this.seen.set(var(p), false); } @Override protected void reduceDB() { int i; int j; - double extraLim = claInc / learnts.size(); - learnts.manualSort(MSClause.minisatComparator); - for (i = j = 0; i < learnts.size(); i++) { - final MSClause c = learnts.get(i); + final double extraLim = this.claInc / this.learnts.size(); + this.learnts.manualSort(MSClause.minisatComparator); + for (i = j = 0; i < this.learnts.size(); i++) { + final MSClause c = this.learnts.get(i); assert !c.isAtMost(); - if (c.size() > 2 && !locked(c) && (i < learnts.size() / 2 || c.activity() < extraLim)) - removeClause(learnts.get(i)); - else - learnts.set(j++, learnts.get(i)); + if (c.size() > 2 && !locked(c) && (i < this.learnts.size() / 2 || c.activity() < extraLim)) { + removeClause(this.learnts.get(i)); + } else { + this.learnts.set(j++, this.learnts.get(i)); + } } - learnts.removeElements(i - j); + this.learnts.removeElements(i - j); } @Override @@ -496,10 +512,11 @@ protected void removeSatisfied(final LNGVector cs) { int j; for (i = j = 0; i < cs.size(); i++) { final MSClause c = cs.get(i); - if (satisfied(c)) + if (satisfied(c)) { removeClause(cs.get(i)); - else + } else { cs.set(j++, cs.get(i)); + } } cs.removeElements(i - j); } @@ -511,14 +528,17 @@ protected boolean satisfied(final MSClause c) { for (int i = 0; i < c.size(); i++) { if (value(c.get(i)) == Tristate.FALSE) { numFalse++; - if (numFalse >= c.atMostWatchers() - 1) + if (numFalse >= c.atMostWatchers() - 1) { return true; + } } } } else { - for (int i = 0; i < c.size(); i++) - if (value(c.get(i)) == Tristate.TRUE) + for (int i = 0; i < c.size(); i++) { + if (value(c.get(i)) == Tristate.TRUE) { return true; + } + } } return false; } @@ -526,18 +546,20 @@ protected boolean satisfied(final MSClause c) { @Override protected boolean simplify() { assert decisionLevel() == 0; - if (!ok || propagate() != null) { - ok = false; + if (!this.ok || propagate() != null) { + this.ok = false; return false; } - if (nAssigns() == simpDBAssigns || (simpDBProps > 0)) + if (nAssigns() == this.simpDBAssigns || (this.simpDBProps > 0)) { return true; - removeSatisfied(learnts); - if (removeSatisfied) - removeSatisfied(clauses); + } + removeSatisfied(this.learnts); + if (this.removeSatisfied) { + removeSatisfied(this.clauses); + } rebuildOrderHeap(); - simpDBAssigns = nAssigns(); - simpDBProps = clausesLiterals + learntsLiterals; + this.simpDBAssigns = nAssigns(); + this.simpDBProps = this.clausesLiterals + this.learntsLiterals; return true; } @@ -547,19 +569,20 @@ protected boolean simplify() { * @param rhs the right hand side of the constraint * @return {@code true} if the constraint was added, {@code false} otherwise */ - public boolean addAtMost(final LNGIntVector ps, int rhs) { + public boolean addAtMost(final LNGIntVector ps, final int rhs) { int k = rhs; assert decisionLevel() == 0; - if (!ok) + if (!this.ok) { return false; + } ps.sort(); int p; int i; int j; for (i = j = 0, p = LIT_UNDEF; i < ps.size(); i++) { - if (value(ps.get(i)) == Tristate.TRUE) + if (value(ps.get(i)) == Tristate.TRUE) { k--; - else if (ps.get(i) == not(p)) { + } else if (ps.get(i) == not(p)) { p = ps.get(i); j--; k--; @@ -569,24 +592,26 @@ else if (ps.get(i) == not(p)) { } } ps.removeElements(i - j); - if (k >= ps.size()) + if (k >= ps.size()) { return true; + } if (k < 0) { - ok = false; + this.ok = false; return false; } if (k == 0) { for (i = 0; i < ps.size(); i++) { uncheckedEnqueue(not(ps.get(i)), null); - if (incremental) - unitClauses.push(not(ps.get(i))); + if (this.incremental) { + this.unitClauses.push(not(ps.get(i))); + } } - ok = propagate() == null; - return ok; + this.ok = propagate() == null; + return this.ok; } final MSClause cr = new MSClause(ps, false, true); cr.setAtMostWatchers(ps.size() - k + 1); - clauses.push(cr); + this.clauses.push(cr); attachClause(cr); return true; } @@ -596,9 +621,10 @@ else if (ps.get(i) == not(p)) { * @param c the at-most clause. */ private void detachAtMost(final MSClause c) { - for (int i = 0; i < c.atMostWatchers(); i++) - watches.get(c.get(i)).remove(new MSWatcher(c, c.get(i))); - clausesLiterals -= c.size(); + for (int i = 0; i < c.atMostWatchers(); i++) { + this.watches.get(c.get(i)).remove(new MSWatcher(c, c.get(i))); + } + this.clausesLiterals -= c.size(); } /** @@ -607,60 +633,66 @@ private void detachAtMost(final MSClause c) { * @return a {@link Tristate} representing the result. {@code FALSE} if the formula is UNSAT, {@code TRUE} if the * formula is SAT, and {@code UNKNOWN} if the state is not known yet (restart) */ - private Tristate search(int nofConflicts) { - if (!ok) + private Tristate search(final int nofConflicts) { + if (!this.ok) { return Tristate.FALSE; + } int conflictC = 0; while (true) { - MSClause confl = propagate(); + final MSClause confl = propagate(); if (confl != null) { - if (handler != null && !handler.detectedConflict()) { - canceledByHandler = true; + if (this.handler != null && !this.handler.detectedConflict()) { + this.canceledByHandler = true; return Tristate.UNDEF; } conflictC++; - if (decisionLevel() == 0) + if (decisionLevel() == 0) { return Tristate.FALSE; - LNGIntVector learntClause = new LNGIntVector(); + } + final LNGIntVector learntClause = new LNGIntVector(); analyze(confl, learntClause); - cancelUntil(analyzeBtLevel); + cancelUntil(this.analyzeBtLevel); if (learntClause.size() == 1) { uncheckedEnqueue(learntClause.get(0), null); this.unitClauses.push(learntClause.get(0)); } else { final MSClause cr = new MSClause(learntClause, true); - learnts.push(cr); + this.learnts.push(cr); attachClause(cr); - if (!incremental) + if (!this.incremental) { claBumpActivity(cr); + } uncheckedEnqueue(learntClause.get(0), cr); } varDecayActivity(); - if (!incremental) + if (!this.incremental) { claDecayActivity(); - if (--learntsizeAdjustCnt == 0) { - learntsizeAdjustConfl *= learntsizeAdjustInc; - learntsizeAdjustCnt = (int) learntsizeAdjustConfl; - maxLearnts *= learntsizeInc; + } + if (--this.learntsizeAdjustCnt == 0) { + this.learntsizeAdjustConfl *= this.learntsizeAdjustInc; + this.learntsizeAdjustCnt = (int) this.learntsizeAdjustConfl; + this.maxLearnts *= this.learntsizeInc; } } else { if (nofConflicts >= 0 && conflictC >= nofConflicts) { cancelUntil(0); return Tristate.UNDEF; } - if (!incremental) { - if (decisionLevel() == 0 && !simplify()) + if (!this.incremental) { + if (decisionLevel() == 0 && !simplify()) { return Tristate.FALSE; - if (learnts.size() - nAssigns() >= maxLearnts) + } + if (this.learnts.size() - nAssigns() >= this.maxLearnts) { reduceDB(); + } } int next = LIT_UNDEF; - while (decisionLevel() < assumptions.size()) { - int p = assumptions.get(decisionLevel()); + while (decisionLevel() < this.assumptions.size()) { + final int p = this.assumptions.get(decisionLevel()); if (value(p) == Tristate.TRUE) { - trailLim.push(trail.size()); + this.trailLim.push(this.trail.size()); } else if (value(p) == Tristate.FALSE) { - analyzeFinal(not(p), conflict); + analyzeFinal(not(p), this.conflict); return Tristate.FALSE; } else { next = p; @@ -669,52 +701,57 @@ private Tristate search(int nofConflicts) { } if (next == LIT_UNDEF) { next = pickBranchLit(); - if (next == LIT_UNDEF) + if (next == LIT_UNDEF) { return Tristate.TRUE; + } } - trailLim.push(trail.size()); + this.trailLim.push(this.trail.size()); uncheckedEnqueue(next, null); } } } - private int findNewWatch(final MSClause c, int p) { + private int findNewWatch(final MSClause c, final int p) { assert c.isAtMost(); int newWatch = LIT_ERROR; int numFalse = 0; int numTrue = 0; - int maxTrue = c.size() - c.atMostWatchers() + 1; + final int maxTrue = c.size() - c.atMostWatchers() + 1; for (int q = 0; q < c.atMostWatchers(); q++) { - Tristate val = value(c.get(q)); - if (val == Tristate.UNDEF) + final Tristate val = value(c.get(q)); + if (val == Tristate.UNDEF) { continue; - else if (val == Tristate.FALSE) { + } else if (val == Tristate.FALSE) { numFalse++; - if (numFalse >= c.atMostWatchers() - 1) + if (numFalse >= c.atMostWatchers() - 1) { return p; + } continue; } assert val == Tristate.TRUE; numTrue++; - if (numTrue > maxTrue) + if (numTrue > maxTrue) { return LIT_ERROR; + } if (c.get(q) == p) { assert newWatch == LIT_ERROR; - for (int next = c.atMostWatchers(); next < c.size(); next++) + for (int next = c.atMostWatchers(); next < c.size(); next++) { if (value(c.get(next)) != Tristate.TRUE) { newWatch = c.get(next); c.set(next, c.get(q)); c.set(q, newWatch); return newWatch; } + } newWatch = LIT_UNDEF; } } assert newWatch == LIT_UNDEF; - if (numTrue > 1) + if (numTrue > 1) { return LIT_ERROR; - else + } else { return LIT_UNDEF; + } } /** @@ -728,42 +765,47 @@ private void analyze(final MSClause conflictClause, final LNGIntVector outLearnt int pathC = 0; int p = LIT_UNDEF; outLearnt.push(-1); - int index = trail.size() - 1; + int index = this.trail.size() - 1; do { assert c != null; if (c.isAtMost()) { for (int j = 0; j < c.size(); j++) { - if (value(c.get(j)) != Tristate.TRUE) + if (value(c.get(j)) != Tristate.TRUE) { continue; + } final int q = not(c.get(j)); - if (!seen.get(var(q)) && v(q).level() > 0) { + if (!this.seen.get(var(q)) && v(q).level() > 0) { varBumpActivity(var(q)); - seen.set(var(q), true); - if (v(q).level() >= decisionLevel()) + this.seen.set(var(q), true); + if (v(q).level() >= decisionLevel()) { pathC++; - else + } else { outLearnt.push(q); + } } } } else { - if (!incremental && c.learnt()) + if (!this.incremental && c.learnt()) { claBumpActivity(c); + } for (int j = (p == LIT_UNDEF) ? 0 : 1; j < c.size(); j++) { - int q = c.get(j); - if (!seen.get(var(q)) && v(q).level() > 0) { + final int q = c.get(j); + if (!this.seen.get(var(q)) && v(q).level() > 0) { varBumpActivity(var(q)); - seen.set(var(q), true); - if (v(q).level() >= decisionLevel()) + this.seen.set(var(q), true); + if (v(q).level() >= decisionLevel()) { pathC++; - else + } else { outLearnt.push(q); + } } } } - while (!seen.get(var(trail.get(index--)))) ; - p = trail.get(index + 1); + while (!this.seen.get(var(this.trail.get(index--)))) { + } + p = this.trail.get(index + 1); c = v(p).reason(); - seen.set(var(p), false); + this.seen.set(var(p), false); pathC--; } while (pathC > 0); outLearnt.set(0, not(p)); @@ -778,59 +820,68 @@ private void simplifyClause(final LNGIntVector outLearnt) { int i; int j; this.analyzeToClear = new LNGIntVector(outLearnt); - if (ccminMode == MiniSatConfig.ClauseMinimization.DEEP) { + if (this.ccminMode == MiniSatConfig.ClauseMinimization.DEEP) { int abstractLevel = 0; - for (i = 1; i < outLearnt.size(); i++) + for (i = 1; i < outLearnt.size(); i++) { abstractLevel |= abstractLevel(var(outLearnt.get(i))); - for (i = j = 1; i < outLearnt.size(); i++) - if (v(outLearnt.get(i)).reason() == null || !litRedundant(outLearnt.get(i), abstractLevel)) + } + for (i = j = 1; i < outLearnt.size(); i++) { + if (v(outLearnt.get(i)).reason() == null || !litRedundant(outLearnt.get(i), abstractLevel)) { outLearnt.set(j++, outLearnt.get(i)); - } else if (ccminMode == MiniSatConfig.ClauseMinimization.BASIC) { + } + } + } else if (this.ccminMode == MiniSatConfig.ClauseMinimization.BASIC) { for (i = j = 1; i < outLearnt.size(); i++) { - if (v(outLearnt.get(i)).reason() == null) + if (v(outLearnt.get(i)).reason() == null) { outLearnt.set(j++, outLearnt.get(i)); - else { - MSClause c = v(outLearnt.get(i)).reason(); + } else { + final MSClause c = v(outLearnt.get(i)).reason(); assert !c.isAtMost(); - for (int k = 1; k < c.size(); k++) - if (!seen.get(var(c.get(k))) && v(c.get(k)).level() > 0) { + for (int k = 1; k < c.size(); k++) { + if (!this.seen.get(var(c.get(k))) && v(c.get(k)).level() > 0) { outLearnt.set(j++, outLearnt.get(i)); break; } + } } } - } else + } else { i = j = outLearnt.size(); + } outLearnt.removeElements(i - j); - analyzeBtLevel = 0; + this.analyzeBtLevel = 0; if (outLearnt.size() > 1) { int max = 1; - for (int k = 2; k < outLearnt.size(); k++) - if (v(outLearnt.get(k)).level() > v(outLearnt.get(max)).level()) + for (int k = 2; k < outLearnt.size(); k++) { + if (v(outLearnt.get(k)).level() > v(outLearnt.get(max)).level()) { max = k; - int p = outLearnt.get(max); + } + } + final int p = outLearnt.get(max); outLearnt.set(max, outLearnt.get(1)); outLearnt.set(1, p); - analyzeBtLevel = v(p).level(); + this.analyzeBtLevel = v(p).level(); + } + for (int l = 0; l < this.analyzeToClear.size(); l++) { + this.seen.set(var(this.analyzeToClear.get(l)), false); } - for (int l = 0; l < analyzeToClear.size(); l++) - seen.set(var(analyzeToClear.get(l)), false); } /** * Performs an unconditional backtrack to level zero. */ private void completeBacktrack() { - for (int v = 0; v < vars.size(); v++) { - MSVariable var = vars.get(v); + for (int v = 0; v < this.vars.size(); v++) { + final MSVariable var = this.vars.get(v); var.assign(Tristate.UNDEF); var.setReason(null); - if (!orderHeap.inHeap(v) && var.decision()) - orderHeap.insert(v); + if (!this.orderHeap.inHeap(v) && var.decision()) { + this.orderHeap.insert(v); + } } - trail.clear(); - trailLim.clear(); - qhead = 0; + this.trail.clear(); + this.trailLim.clear(); + this.qhead = 0; } /** @@ -838,12 +889,13 @@ private void completeBacktrack() { * @param c the clause to remove */ private void simpleRemoveClause(final MSClause c) { - if (c.isAtMost()) - for (int i = 0; i < c.atMostWatchers(); i++) - watches.get(c.get(i)).remove(new MSWatcher(c, c.get(i))); - else { - watches.get(not(c.get(0))).remove(new MSWatcher(c, c.get(1))); - watches.get(not(c.get(1))).remove(new MSWatcher(c, c.get(0))); + if (c.isAtMost()) { + for (int i = 0; i < c.atMostWatchers(); i++) { + this.watches.get(c.get(i)).remove(new MSWatcher(c, c.get(i))); + } + } else { + this.watches.get(not(c.get(0))).remove(new MSWatcher(c, c.get(1))); + this.watches.get(not(c.get(1))).remove(new MSWatcher(c, c.get(0))); } } } diff --git a/src/main/java/org/logicng/solvers/sat/MiniSat2Solver.java b/src/main/java/org/logicng/solvers/sat/MiniSat2Solver.java index 9191a117..e2a2132c 100644 --- a/src/main/java/org/logicng/solvers/sat/MiniSat2Solver.java +++ b/src/main/java/org/logicng/solvers/sat/MiniSat2Solver.java @@ -44,27 +44,16 @@ package org.logicng.solvers.sat; -import org.logicng.backbones.Backbone; -import org.logicng.backbones.BackboneType; import org.logicng.collections.LNGBooleanVector; import org.logicng.collections.LNGIntVector; import org.logicng.collections.LNGVector; import org.logicng.datastructures.Tristate; -import org.logicng.formulas.Variable; import org.logicng.handlers.SATHandler; import org.logicng.propositions.Proposition; import org.logicng.solvers.datastructures.MSClause; import org.logicng.solvers.datastructures.MSVariable; import org.logicng.solvers.datastructures.MSWatcher; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.SortedSet; -import java.util.Stack; -import java.util.TreeSet; - /** * A solver based on MiniSAT 2.2.0. If the incremental mode is deactivated, this version should behave exactly * like the C++ version. @@ -85,11 +74,6 @@ public class MiniSat2Solver extends MiniSatStyleSolver { private double learntsizeAdjustInc; private double maxLearnts; - private Stack backboneCandidates; - private LNGIntVector backboneAssumptions; - private HashMap backboneMap; - private boolean computingBackbone; - /** * Constructs a new MiniSAT 2 solver with the default values for solver configuration. By default, incremental mode * is activated. @@ -117,7 +101,6 @@ private void initializeMiniSAT() { this.learntsizeAdjustStartConfl = 100; this.learntsizeAdjustInc = 1.5; this.maxLearnts = 0; - this.computingBackbone = false; } @Override @@ -492,32 +475,6 @@ protected void analyzeFinal(final int p, final LNGIntVector outConflict) { this.seen.set(var(p), false); } - @Override - protected void cancelUntil(final int level) { - if (decisionLevel() > level) { - if (!this.config.fastBackboneComputation) { - for (int c = this.trail.size() - 1; c >= this.trailLim.get(level); c--) { - final int x = var(this.trail.get(c)); - final MSVariable v = this.vars.get(x); - v.assign(Tristate.UNDEF); - v.setPolarity(sign(this.trail.get(c))); - insertVarOrder(x); - } - } else { - for (int c = this.trail.size() - 1; c >= this.trailLim.get(level); c--) { - final int x = var(this.trail.get(c)); - final MSVariable v = this.vars.get(x); - v.assign(Tristate.UNDEF); - v.setPolarity(!this.computingBackbone && sign(this.trail.get(c))); - insertVarOrder(x); - } - } - this.qhead = this.trailLim.get(level); - this.trail.removeElements(this.trail.size() - this.trailLim.get(level)); - this.trailLim.removeElements(this.trailLim.size() - level); - } - } - @Override protected void reduceDB() { int i; @@ -804,232 +761,4 @@ private void simpleRemoveClause(final MSClause c) { this.watches.get(not(c.get(1))).remove(new MSWatcher(c, c.get(0))); } - ///// Backbone Stuff ///// - - /** - * Computes the backbone of the given variables with respect to the formulas added to the solver. - * @param variables variables to test - * @param type backbone type - * @return the backbone projected to the relevant variables or {@code null} if the formula on the solver with the restrictions are not satisfiable - */ - public Backbone computeBackbone(final Collection variables, final BackboneType type) { - final boolean sat = solve(null) == Tristate.TRUE; - if (sat) { - this.computingBackbone = true; - final List relevantVarIndices = getRelevantVarIndices(variables); - initBackboneDS(relevantVarIndices); - computeBackbone(relevantVarIndices, type); - final Backbone backbone = buildBackbone(variables, type); - this.computingBackbone = false; - return backbone; - } else { - return Backbone.unsatBackbone(); - } - } - - /** - * Returns a list of relevant variable indices. A relevant variable is known by the solver. - * @param variables variables to convert and filter - * @return list of relevant variable indices - */ - private List getRelevantVarIndices(final Collection variables) { - final List relevantVarIndices = new ArrayList<>(variables.size()); - for (final Variable var : variables) { - final Integer idx = this.name2idx.get(var.name()); - // Note: Unknown variables are variables added to the solver yet. Thus, these are optional variables and can - // be left out for the backbone computation. - if (idx != null) { - relevantVarIndices.add(idx); - } - } - return relevantVarIndices; - } - - /** - * Initializes the internal solver state for backbones. - * @param variables to test - */ - private void initBackboneDS(final List variables) { - this.backboneCandidates = new Stack<>(); - this.backboneAssumptions = new LNGIntVector(variables.size()); - this.backboneMap = new HashMap<>(); - for (final Integer var : variables) { - this.backboneMap.put(var, Tristate.UNDEF); - } - } - - /** - * Computes the backbone for the given variables. - * @param variables variables to test - */ - private void computeBackbone(final List variables, final BackboneType type) { - final Stack candidates = createInitialCandidates(variables, type); - while (candidates.size() > 0) { - final int lit = candidates.pop(); - if (solveWithLit(lit)) { - refineUpperBound(); - } else { - addBackboneLiteral(lit); - } - } - } - - /** - * Creates the initial candidate literals for the backbone computation. - * @param variables variables to test - * @return initial candidates - */ - private Stack createInitialCandidates(final List variables, final BackboneType type) { - for (final Integer var : variables) { - if (isUPZeroLit(var)) { - final int backboneLit = mkLit(var, !this.model.get(var)); - addBackboneLiteral(backboneLit); - } else { - final boolean modelPhase = this.model.get(var); - if (isBothOrNegativeType(type) && !modelPhase || isBothOrPositiveType(type) && modelPhase) { - final int lit = mkLit(var, !modelPhase); - if (!this.config.bbInitialUBCheckForRotatableLiterals || !isRotatable(lit)) { - this.backboneCandidates.add(lit); - } - } - } - } - return this.backboneCandidates; - } - - /** - * Refines the upper bound by optional checks (UP zero literal, complement model literal, rotatable literal). - */ - private void refineUpperBound() { - for (final Integer lit : new ArrayList<>(this.backboneCandidates)) { - final int var = var(lit); - if (isUPZeroLit(var)) { - this.backboneCandidates.remove(lit); - addBackboneLiteral(lit); - } else if (this.config.bbCheckForComplementModelLiterals && this.model.get(var) == sign(lit)) { - this.backboneCandidates.remove(lit); - } else if (this.config.bbCheckForRotatableLiterals && isRotatable(lit)) { - this.backboneCandidates.remove(lit); - } - } - } - - /** - * Tests the given literal with the formula on the solver for satisfiability. - * @param lit literal to test - * @return {@code true} if satisfiable, otherwise {@code false} - */ - private boolean solveWithLit(final int lit) { - this.backboneAssumptions.push(not(lit)); - final boolean sat = solve(null, this.backboneAssumptions) == Tristate.TRUE; - this.backboneAssumptions.pop(); - return sat; - } - - /** - * Builds the backbone object from the computed backbone literals. - * @param variables relevant variables - * @return backbone - */ - private Backbone buildBackbone(final Collection variables, final BackboneType type) { - final SortedSet posBackboneVars = isBothOrPositiveType(type) ? new TreeSet() : null; - final SortedSet negBackboneVars = isBothOrNegativeType(type) ? new TreeSet() : null; - final SortedSet optionalVars = isBothType(type) ? new TreeSet() : null; - for (final Variable var : variables) { - final Integer idx = this.name2idx.get(var.name()); - if (idx == null) { - if (isBothType(type)) { - optionalVars.add(var); - } - } else { - switch (this.backboneMap.get(idx)) { - case TRUE: - if (isBothOrPositiveType(type)) { - posBackboneVars.add(var); - } - break; - case FALSE: - if (isBothOrNegativeType(type)) { - negBackboneVars.add(var); - } - break; - case UNDEF: - if (isBothType(type)) { - optionalVars.add(var); - } - break; - default: - throw new IllegalStateException("Unknown tristate: " + this.backboneMap.get(idx)); - } - } - } - return Backbone.satBackbone(posBackboneVars, negBackboneVars, optionalVars); - } - - /** - * Tests the given variable whether it is a unit propagated literal on level 0. - *

    - * Assumption: The formula on the solver has successfully been tested to be satisfiable before. - * @param var variable index to test - * @return {@code true} if the variable is a unit propagated literal on level 0, otherwise {@code false} - */ - private boolean isUPZeroLit(final int var) { - return this.vars.get(var).level() == 0; - } - - /** - * Tests the given literal whether it is unit in the given clause. - * @param lit literal to test - * @param clause clause containing the literal - * @return {@code true} if the literal is unit, {@code false} otherwise - */ - private boolean isUnit(final int lit, final MSClause clause) { - for (int i = 0; i < clause.size(); ++i) { - final int clauseLit = clause.get(i); - if (lit != clauseLit && this.model.get(var(clauseLit)) != sign(clauseLit)) { - return false; - } - } - return true; - } - - /** - * Tests the given literal whether it is rotatable in the current model. - * @param lit literal to test - * @return {@code true} if the literal is rotatable, otherwise {@code false} - */ - private boolean isRotatable(final int lit) { - // A rotatable literal MUST NOT be a unit propagated literal - if (v(lit).reason() != null) { - return false; - } - // A rotatable literal MUST NOT be unit - for (final MSWatcher watcher : this.watches.get(not(lit))) { - if (isUnit(lit, watcher.clause())) { - return false; - } - } - return true; - } - - /** - * Adds the given literal to the backbone result and optionally adds the literal to the solver. - * @param lit literal to add - */ - private void addBackboneLiteral(final int lit) { - this.backboneMap.put(var(lit), sign(lit) ? Tristate.FALSE : Tristate.TRUE); - this.backboneAssumptions.push(lit); - } - - private boolean isBothOrPositiveType(final BackboneType type) { - return type == BackboneType.POSITIVE_AND_NEGATIVE || type == BackboneType.ONLY_POSITIVE; - } - - private boolean isBothOrNegativeType(final BackboneType type) { - return type == BackboneType.POSITIVE_AND_NEGATIVE || type == BackboneType.ONLY_NEGATIVE; - } - - private boolean isBothType(final BackboneType type) { - return type == BackboneType.POSITIVE_AND_NEGATIVE; - } } diff --git a/src/main/java/org/logicng/solvers/sat/MiniSatConfig.java b/src/main/java/org/logicng/solvers/sat/MiniSatConfig.java index 330567ee..b8cd8366 100644 --- a/src/main/java/org/logicng/solvers/sat/MiniSatConfig.java +++ b/src/main/java/org/logicng/solvers/sat/MiniSatConfig.java @@ -84,7 +84,6 @@ public enum CNFMethod { final boolean proofGeneration; final CNFMethod cnfMethod; final boolean auxiliaryVariablesInModels; - final boolean fastBackboneComputation; final boolean bbInitialUBCheckForRotatableLiterals; final boolean bbCheckForComplementModelLiterals; final boolean bbCheckForRotatableLiterals; @@ -109,7 +108,6 @@ private MiniSatConfig(final Builder builder) { this.proofGeneration = builder.proofGeneration; this.cnfMethod = builder.cnfMethod; this.auxiliaryVariablesInModels = builder.auxiliaryVariablesInModels; - this.fastBackboneComputation = builder.fastBackboneComputation; this.bbInitialUBCheckForRotatableLiterals = builder.bbInitialUBCheckForRotatableLiterals; this.bbCheckForComplementModelLiterals = builder.bbCheckForComplementModelLiterals; this.bbCheckForRotatableLiterals = builder.bbCheckForRotatableLiterals; @@ -155,14 +153,6 @@ public boolean isAuxiliaryVariablesInModels() { return this.auxiliaryVariablesInModels; } - /** - * Returns whether fast backbone computation is activated or not. - * @return whether fast backbone computation is activated or not - */ - public boolean isFastBackboneComputation() { - return this.fastBackboneComputation; - } - @Override public String toString() { final StringBuilder sb = new StringBuilder("MiniSatConfig{").append(System.lineSeparator()); @@ -180,7 +170,6 @@ public String toString() { sb.append("proofGeneration=").append(this.proofGeneration).append(System.lineSeparator()); sb.append("cnfMethod=").append(this.cnfMethod).append(System.lineSeparator()); sb.append("auxiliaryVariablesInModels=").append(this.auxiliaryVariablesInModels).append(System.lineSeparator()); - sb.append("fastBackboneComputation=").append(this.fastBackboneComputation).append(System.lineSeparator()); sb.append("bbInitialUBCheckForRotatableLiterals=").append(this.bbInitialUBCheckForRotatableLiterals).append(System.lineSeparator()); sb.append("bbCheckForComplementModelLiterals=").append(this.bbCheckForComplementModelLiterals).append(System.lineSeparator()); sb.append("bbCheckForRotatableLiterals=").append(this.bbCheckForRotatableLiterals).append(System.lineSeparator()); @@ -206,7 +195,6 @@ public static class Builder { private boolean proofGeneration = false; CNFMethod cnfMethod = FACTORY_CNF; boolean auxiliaryVariablesInModels = true; - private boolean fastBackboneComputation = false; private boolean bbInitialUBCheckForRotatableLiterals = true; private boolean bbCheckForComplementModelLiterals = true; private boolean bbCheckForRotatableLiterals = true; @@ -362,16 +350,6 @@ public Builder auxiliaryVariablesInModels(final boolean auxiliaryVariablesInMode return this; } - /** - * Sets whether fast backbone computation is turned on or off. The default value is {@code false}. - * @param fastBackboneComputation {@code true} if fast backbone generation is on, {@code false} otherwise - * @return the builder - */ - public Builder fastBackboneComputation(final boolean fastBackboneComputation) { - this.fastBackboneComputation = fastBackboneComputation; - return this; - } - /** * Sets whether the backbone algorithm should check for rotatable literals. * The default value is {@code true}. diff --git a/src/main/java/org/logicng/solvers/sat/MiniSatStyleSolver.java b/src/main/java/org/logicng/solvers/sat/MiniSatStyleSolver.java index 5cf7863d..dd587bd4 100644 --- a/src/main/java/org/logicng/solvers/sat/MiniSatStyleSolver.java +++ b/src/main/java/org/logicng/solvers/sat/MiniSatStyleSolver.java @@ -44,10 +44,15 @@ package org.logicng.solvers.sat; +import static org.logicng.datastructures.Tristate.UNDEF; + +import org.logicng.backbones.Backbone; +import org.logicng.backbones.BackboneType; import org.logicng.collections.LNGBooleanVector; import org.logicng.collections.LNGIntVector; import org.logicng.collections.LNGVector; import org.logicng.datastructures.Tristate; +import org.logicng.formulas.Variable; import org.logicng.handlers.SATHandler; import org.logicng.propositions.Proposition; import org.logicng.solvers.datastructures.LNGHeap; @@ -55,8 +60,15 @@ import org.logicng.solvers.datastructures.MSVariable; import org.logicng.solvers.datastructures.MSWatcher; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.SortedSet; +import java.util.Stack; import java.util.TreeMap; +import java.util.TreeSet; /** * The super class for all MiniSAT-style solvers. @@ -120,6 +132,12 @@ public abstract class MiniSatStyleSolver { protected LNGVector pgOriginalClauses; protected LNGVector pgProof; + // backbone computation + private Stack backboneCandidates; + private LNGIntVector backboneAssumptions; + private HashMap backboneMap; + private boolean computingBackbone; + /** * Constructs a new MiniSAT-style solver with a given configuration. * @param config the configuration @@ -225,6 +243,7 @@ protected void initialize() { this.pgOriginalClauses = new LNGVector<>(); this.pgProof = new LNGVector<>(); } + this.computingBackbone = false; } /** @@ -456,7 +475,7 @@ protected void insertVarOrder(final int x) { */ protected int pickBranchLit() { int next = -1; - while (next == -1 || this.vars.get(next).assignment() != Tristate.UNDEF || !this.vars.get(next).decision()) { + while (next == -1 || this.vars.get(next).assignment() != UNDEF || !this.vars.get(next).decision()) { if (this.orderHeap.empty()) { return -1; } else { @@ -506,7 +525,7 @@ protected void varBumpActivity(final int v, final double inc) { protected void rebuildOrderHeap() { final LNGIntVector vs = new LNGIntVector(); for (int v = 0; v < this.nVars(); v++) { - if (this.vars.get(v).decision() && this.vars.get(v).assignment() == Tristate.UNDEF) { + if (this.vars.get(v).decision() && this.vars.get(v).assignment() == UNDEF) { vs.push(v); } } @@ -589,11 +608,30 @@ protected void claBumpActivity(final MSClause c) { */ protected abstract void analyzeFinal(int p, final LNGIntVector outConflict); - /** - * Performs backtracking up to a given level. - * @param level the level - */ - protected abstract void cancelUntil(int level); + protected void cancelUntil(final int level) { + if (decisionLevel() > level) { + if (!this.computingBackbone) { + for (int c = this.trail.size() - 1; c >= this.trailLim.get(level); c--) { + final int x = var(this.trail.get(c)); + final MSVariable v = this.vars.get(x); + v.assign(Tristate.UNDEF); + v.setPolarity(sign(this.trail.get(c))); + insertVarOrder(x); + } + } else { + for (int c = this.trail.size() - 1; c >= this.trailLim.get(level); c--) { + final int x = var(this.trail.get(c)); + final MSVariable v = this.vars.get(x); + v.assign(Tristate.UNDEF); + v.setPolarity(!this.computingBackbone && sign(this.trail.get(c))); + insertVarOrder(x); + } + } + this.qhead = this.trailLim.get(level); + this.trail.removeElements(this.trail.size() - this.trailLim.get(level)); + this.trailLim.removeElements(this.trailLim.size() - level); + } + } /** * Reduces the database of learnt clauses. Only clauses of the first half of the clauses with the most activity @@ -727,4 +765,232 @@ public LNGIntVector upZeroLiterals() { return upZeroLiterals; } + ///// Backbone Stuff ///// + + /** + * Computes the backbone of the given variables with respect to the formulas added to the solver. + * @param variables variables to test + * @param type backbone type + * @return the backbone projected to the relevant variables or {@code null} if the formula on the solver with the restrictions are not satisfiable + */ + public Backbone computeBackbone(final Collection variables, final BackboneType type) { + final boolean sat = solve(null) == Tristate.TRUE; + if (sat) { + this.computingBackbone = true; + final List relevantVarIndices = getRelevantVarIndices(variables); + initBackboneDS(relevantVarIndices); + computeBackbone(relevantVarIndices, type); + final Backbone backbone = buildBackbone(variables, type); + this.computingBackbone = false; + return backbone; + } else { + return Backbone.unsatBackbone(); + } + } + + /** + * Returns a list of relevant variable indices. A relevant variable is known by the solver. + * @param variables variables to convert and filter + * @return list of relevant variable indices + */ + private List getRelevantVarIndices(final Collection variables) { + final List relevantVarIndices = new ArrayList<>(variables.size()); + for (final Variable var : variables) { + final Integer idx = this.name2idx.get(var.name()); + // Note: Unknown variables are variables added to the solver yet. Thus, these are optional variables and can + // be left out for the backbone computation. + if (idx != null) { + relevantVarIndices.add(idx); + } + } + return relevantVarIndices; + } + + /** + * Initializes the internal solver state for backbones. + * @param variables to test + */ + private void initBackboneDS(final List variables) { + this.backboneCandidates = new Stack<>(); + this.backboneAssumptions = new LNGIntVector(variables.size()); + this.backboneMap = new HashMap<>(); + for (final Integer var : variables) { + this.backboneMap.put(var, UNDEF); + } + } + + /** + * Computes the backbone for the given variables. + * @param variables variables to test + */ + private void computeBackbone(final List variables, final BackboneType type) { + final Stack candidates = createInitialCandidates(variables, type); + while (candidates.size() > 0) { + final int lit = candidates.pop(); + if (solveWithLit(lit)) { + refineUpperBound(); + } else { + addBackboneLiteral(lit); + } + } + } + + /** + * Creates the initial candidate literals for the backbone computation. + * @param variables variables to test + * @return initial candidates + */ + private Stack createInitialCandidates(final List variables, final BackboneType type) { + for (final Integer var : variables) { + if (isUPZeroLit(var)) { + final int backboneLit = mkLit(var, !this.model.get(var)); + addBackboneLiteral(backboneLit); + } else { + final boolean modelPhase = this.model.get(var); + if (isBothOrNegativeType(type) && !modelPhase || isBothOrPositiveType(type) && modelPhase) { + final int lit = mkLit(var, !modelPhase); + if (!this.config.bbInitialUBCheckForRotatableLiterals || !isRotatable(lit)) { + this.backboneCandidates.add(lit); + } + } + } + } + return this.backboneCandidates; + } + + /** + * Refines the upper bound by optional checks (UP zero literal, complement model literal, rotatable literal). + */ + private void refineUpperBound() { + for (final Integer lit : new ArrayList<>(this.backboneCandidates)) { + final int var = var(lit); + if (isUPZeroLit(var)) { + this.backboneCandidates.remove(lit); + addBackboneLiteral(lit); + } else if (this.config.bbCheckForComplementModelLiterals && this.model.get(var) == sign(lit)) { + this.backboneCandidates.remove(lit); + } else if (this.config.bbCheckForRotatableLiterals && isRotatable(lit)) { + this.backboneCandidates.remove(lit); + } + } + } + + /** + * Tests the given literal with the formula on the solver for satisfiability. + * @param lit literal to test + * @return {@code true} if satisfiable, otherwise {@code false} + */ + private boolean solveWithLit(final int lit) { + this.backboneAssumptions.push(not(lit)); + final boolean sat = solve(null, this.backboneAssumptions) == Tristate.TRUE; + this.backboneAssumptions.pop(); + return sat; + } + + /** + * Builds the backbone object from the computed backbone literals. + * @param variables relevant variables + * @return backbone + */ + private Backbone buildBackbone(final Collection variables, final BackboneType type) { + final SortedSet posBackboneVars = isBothOrPositiveType(type) ? new TreeSet() : null; + final SortedSet negBackboneVars = isBothOrNegativeType(type) ? new TreeSet() : null; + final SortedSet optionalVars = isBothType(type) ? new TreeSet() : null; + for (final Variable var : variables) { + final Integer idx = this.name2idx.get(var.name()); + if (idx == null) { + if (isBothType(type)) { + optionalVars.add(var); + } + } else { + switch (this.backboneMap.get(idx)) { + case TRUE: + if (isBothOrPositiveType(type)) { + posBackboneVars.add(var); + } + break; + case FALSE: + if (isBothOrNegativeType(type)) { + negBackboneVars.add(var); + } + break; + case UNDEF: + if (isBothType(type)) { + optionalVars.add(var); + } + break; + default: + throw new IllegalStateException("Unknown tristate: " + this.backboneMap.get(idx)); + } + } + } + return Backbone.satBackbone(posBackboneVars, negBackboneVars, optionalVars); + } + + /** + * Tests the given variable whether it is a unit propagated literal on level 0. + *

    + * Assumption: The formula on the solver has successfully been tested to be satisfiable before. + * @param var variable index to test + * @return {@code true} if the variable is a unit propagated literal on level 0, otherwise {@code false} + */ + private boolean isUPZeroLit(final int var) { + return this.vars.get(var).level() == 0; + } + + /** + * Tests the given literal whether it is unit in the given clause. + * @param lit literal to test + * @param clause clause containing the literal + * @return {@code true} if the literal is unit, {@code false} otherwise + */ + private boolean isUnit(final int lit, final MSClause clause) { + for (int i = 0; i < clause.size(); ++i) { + final int clauseLit = clause.get(i); + if (lit != clauseLit && this.model.get(var(clauseLit)) != sign(clauseLit)) { + return false; + } + } + return true; + } + + /** + * Tests the given literal whether it is rotatable in the current model. + * @param lit literal to test + * @return {@code true} if the literal is rotatable, otherwise {@code false} + */ + private boolean isRotatable(final int lit) { + // A rotatable literal MUST NOT be a unit propagated literal + if (v(lit).reason() != null) { + return false; + } + // A rotatable literal MUST NOT be unit + for (final MSWatcher watcher : this.watches.get(not(lit))) { + if (isUnit(lit, watcher.clause())) { + return false; + } + } + return true; + } + + /** + * Adds the given literal to the backbone result and optionally adds the literal to the solver. + * @param lit literal to add + */ + private void addBackboneLiteral(final int lit) { + this.backboneMap.put(var(lit), sign(lit) ? Tristate.FALSE : Tristate.TRUE); + this.backboneAssumptions.push(lit); + } + + private boolean isBothOrPositiveType(final BackboneType type) { + return type == BackboneType.POSITIVE_AND_NEGATIVE || type == BackboneType.ONLY_POSITIVE; + } + + private boolean isBothOrNegativeType(final BackboneType type) { + return type == BackboneType.POSITIVE_AND_NEGATIVE || type == BackboneType.ONLY_NEGATIVE; + } + + private boolean isBothType(final BackboneType type) { + return type == BackboneType.POSITIVE_AND_NEGATIVE; + } } diff --git a/src/main/java/org/logicng/transformations/BackboneSimplifier.java b/src/main/java/org/logicng/transformations/BackboneSimplifier.java index 8ab97e34..10586cc4 100644 --- a/src/main/java/org/logicng/transformations/BackboneSimplifier.java +++ b/src/main/java/org/logicng/transformations/BackboneSimplifier.java @@ -34,7 +34,6 @@ import org.logicng.formulas.FormulaTransformation; import org.logicng.solvers.MiniSat; import org.logicng.solvers.SATSolver; -import org.logicng.solvers.sat.MiniSatConfig; /** * This class simplifies a formula by computing its backbone and propagating @@ -47,10 +46,9 @@ public class BackboneSimplifier implements FormulaTransformation { @Override public Formula apply(final Formula formula, final boolean cache) { - final SATSolver solver = MiniSat.miniSat(formula.factory(), new MiniSatConfig.Builder() - .fastBackboneComputation(true).build()); + final SATSolver solver = MiniSat.miniSat(formula.factory()); solver.add(formula); - final Backbone backbone = solver.computeBackbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); + final Backbone backbone = solver.backbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); if (!backbone.isSat()) { return formula.factory().falsum(); } diff --git a/src/test/java/org/logicng/backbones/BackboneGenerationTest.java b/src/test/java/org/logicng/backbones/BackboneGenerationTest.java index 2177f906..8319c9dc 100644 --- a/src/test/java/org/logicng/backbones/BackboneGenerationTest.java +++ b/src/test/java/org/logicng/backbones/BackboneGenerationTest.java @@ -94,7 +94,7 @@ public void testBackboneGeneration() { @Test public void testSimpleBackbones() { final FormulaFactory f = new FormulaFactory(); - final MiniSat solver = MiniSat.miniSat(f, new MiniSatConfig.Builder().fastBackboneComputation(true).build()); + final MiniSat solver = MiniSat.miniSat(f); final Literal x = f.literal("x", true); final Literal y = f.literal("y", true); @@ -109,7 +109,7 @@ public void testSimpleBackbones() { SolverState before = solver.saveState(); solver.add(formula); - assertThat(solver.computeBackbone(Collections.emptyList(), BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.backbone(Collections.emptyList(), BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>() ); solver.loadState(before); @@ -117,7 +117,7 @@ public void testSimpleBackbones() { formula = x; before = solver.saveState(); solver.add(formula); - assertThat(solver.computeBackbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.backbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>(Collections.singletonList(x)) ); solver.loadState(before); @@ -125,7 +125,7 @@ public void testSimpleBackbones() { formula = f.and(x, y); before = solver.saveState(); solver.add(formula); - assertThat(solver.computeBackbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.backbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>(Arrays.asList(x, y)) ); solver.loadState(before); @@ -133,7 +133,7 @@ public void testSimpleBackbones() { formula = f.or(x, y); before = solver.saveState(); solver.add(formula); - assertThat(solver.computeBackbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.backbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>() ); solver.loadState(before); @@ -141,7 +141,7 @@ public void testSimpleBackbones() { formula = x.negate(); before = solver.saveState(); solver.add(formula); - assertThat(solver.computeBackbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.backbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>(Collections.singleton(x.negate())) ); solver.loadState(before); @@ -149,7 +149,7 @@ public void testSimpleBackbones() { formula = f.or(f.and(x, y, z), f.and(x, y, u), f.and(x, u, z)); before = solver.saveState(); solver.add(formula); - assertThat(solver.computeBackbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.backbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>(Collections.singleton(x)) ); solver.loadState(before); @@ -157,7 +157,7 @@ public void testSimpleBackbones() { formula = f.and(f.or(x, y, z), f.or(x, y, u), f.or(x, u, z)); before = solver.saveState(); solver.add(formula); - assertThat(solver.computeBackbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.backbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>() ); solver.loadState(before); @@ -165,7 +165,7 @@ public void testSimpleBackbones() { formula = f.and(f.or(x.negate(), y), x); before = solver.saveState(); solver.add(formula); - assertThat(solver.computeBackbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.backbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>(Arrays.asList(x, y)) ); solver.loadState(before); @@ -173,7 +173,7 @@ public void testSimpleBackbones() { formula = f.and(f.or(x, y), f.or(x.negate(), y)); before = solver.saveState(); solver.add(formula); - assertThat(solver.computeBackbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.backbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>(Collections.singleton(y)) ); solver.loadState(before); @@ -181,14 +181,14 @@ public void testSimpleBackbones() { formula = f.and(f.and(f.or(x.negate(), y), x.negate()), f.and(z, f.or(x, y))); before = solver.saveState(); solver.add(formula); - assertThat(solver.computeBackbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.backbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>(Arrays.asList(x.negate(), y, z)) ); solver.loadState(before); formula = f.and(f.or(x, y), f.or(u, v), z); solver.add(formula); - assertThat(solver.computeBackbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( + assertThat(solver.backbone(variables, BackboneType.POSITIVE_AND_NEGATIVE).getCompleteBackbone()).isEqualTo( new TreeSet<>(Collections.singleton(z)) ); } @@ -197,9 +197,9 @@ public void testSimpleBackbones() { public void testSmallFormulas() throws IOException, ParserException { final FormulaFactory f = new FormulaFactory(); final Formula formula = FormulaReader.readPseudoBooleanFormula("src/test/resources/formulas/small_formulas.txt", f); - final MiniSat solver = MiniSat.miniSat(f, new MiniSatConfig.Builder().fastBackboneComputation(true).build()); + final MiniSat solver = MiniSat.miniSat(f); solver.add(formula); - final Backbone backbone = solver.computeBackbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); + final Backbone backbone = solver.backbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); assertThat(verifyBackbone(backbone, formula, formula.variables())).isTrue(); } @@ -207,9 +207,9 @@ public void testSmallFormulas() throws IOException, ParserException { public void testLargeFormula() throws IOException, ParserException { final FormulaFactory f = new FormulaFactory(); final Formula formula = FormulaReader.readPseudoBooleanFormula("src/test/resources/formulas/large_formula.txt", f); - final MiniSat solver = MiniSat.miniSat(f, new MiniSatConfig.Builder().fastBackboneComputation(true).build()); + final MiniSat solver = MiniSat.miniSat(f); solver.add(formula); - final Backbone backbone = solver.computeBackbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); + final Backbone backbone = solver.backbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); assertThat(verifyBackbone(backbone, formula, formula.variables())).isTrue(); } @@ -242,7 +242,7 @@ private boolean verifyBackbone(final Backbone backbone, final Formula formula, f @Test public void testBackboneType() { final FormulaFactory f = new FormulaFactory(); - final MiniSat solver = MiniSat.miniSat(f, new MiniSatConfig.Builder().fastBackboneComputation(true).build()); + final MiniSat solver = MiniSat.miniSat(f); final Literal x = f.literal("x", true); final Literal y = f.literal("y", true); @@ -251,9 +251,9 @@ public void testBackboneType() { Formula formula = f.not(x); SolverState before = solver.saveState(); solver.add(formula); - Backbone backbone = solver.computeBackbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); - Backbone backbonePositive = solver.computeBackbone(formula.variables(), BackboneType.ONLY_POSITIVE); - Backbone backboneNegative = solver.computeBackbone(formula.variables(), BackboneType.ONLY_NEGATIVE); + Backbone backbone = solver.backbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); + Backbone backbonePositive = solver.backbone(formula.variables(), BackboneType.ONLY_POSITIVE); + Backbone backboneNegative = solver.backbone(formula.variables(), BackboneType.ONLY_NEGATIVE); assertThat(backbone.getCompleteBackbone()).containsExactly(x.negate()); assertThat(backbonePositive.getCompleteBackbone()).isEmpty(); assertThat(backboneNegative.getCompleteBackbone()).containsExactly(x.negate()); @@ -265,9 +265,9 @@ public void testBackboneType() { formula = f.and(f.or(x, y.negate()), x.negate()); before = solver.saveState(); solver.add(formula); - backbone = solver.computeBackbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); - backbonePositive = solver.computeBackbone(formula.variables(), BackboneType.ONLY_POSITIVE); - backboneNegative = solver.computeBackbone(formula.variables(), BackboneType.ONLY_NEGATIVE); + backbone = solver.backbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); + backbonePositive = solver.backbone(formula.variables(), BackboneType.ONLY_POSITIVE); + backboneNegative = solver.backbone(formula.variables(), BackboneType.ONLY_NEGATIVE); assertThat(backbone.getCompleteBackbone()).containsExactly(x.negate(), y.negate()); assertThat(backbonePositive.getCompleteBackbone()).isEmpty(); assertThat(backboneNegative.getCompleteBackbone()).containsExactly(x.negate(), y.negate()); @@ -279,9 +279,9 @@ public void testBackboneType() { formula = f.and(f.or(x, y), f.or(x.negate(), y)); before = solver.saveState(); solver.add(formula); - backbone = solver.computeBackbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); - backbonePositive = solver.computeBackbone(formula.variables(), BackboneType.ONLY_POSITIVE); - backboneNegative = solver.computeBackbone(formula.variables(), BackboneType.ONLY_NEGATIVE); + backbone = solver.backbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); + backbonePositive = solver.backbone(formula.variables(), BackboneType.ONLY_POSITIVE); + backboneNegative = solver.backbone(formula.variables(), BackboneType.ONLY_NEGATIVE); assertThat(backbone.getCompleteBackbone()).containsExactly(y); assertThat(backbonePositive.getCompleteBackbone()).containsExactly(y); assertThat(backboneNegative.getCompleteBackbone()).isEmpty(); @@ -293,9 +293,9 @@ public void testBackboneType() { formula = f.and(f.and(f.or(x.negate(), y), x.negate()), f.and(z, f.or(x, y))); before = solver.saveState(); solver.add(formula); - backbone = solver.computeBackbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); - backbonePositive = solver.computeBackbone(formula.variables(), BackboneType.ONLY_POSITIVE); - backboneNegative = solver.computeBackbone(formula.variables(), BackboneType.ONLY_NEGATIVE); + backbone = solver.backbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); + backbonePositive = solver.backbone(formula.variables(), BackboneType.ONLY_POSITIVE); + backboneNegative = solver.backbone(formula.variables(), BackboneType.ONLY_NEGATIVE); assertThat(backbone.getCompleteBackbone()).containsExactly(x.negate(), y, z); assertThat(backbone.getOptionalVariables()).containsExactly(); assertThat(backbonePositive.getCompleteBackbone()).containsExactly(y, z); @@ -309,20 +309,20 @@ public void testBackboneType() { @Test public void testDifferentConfigurations() throws IOException, ParserException { final List configs = new ArrayList<>(); - configs.add(new MiniSatConfig.Builder().fastBackboneComputation(true).bbCheckForComplementModelLiterals(false).build()); - configs.add(new MiniSatConfig.Builder().fastBackboneComputation(true).bbCheckForRotatableLiterals(false).build()); - configs.add(new MiniSatConfig.Builder().fastBackboneComputation(true).bbInitialUBCheckForRotatableLiterals(false).build()); + configs.add(new MiniSatConfig.Builder().bbCheckForComplementModelLiterals(false).build()); + configs.add(new MiniSatConfig.Builder().bbCheckForRotatableLiterals(false).build()); + configs.add(new MiniSatConfig.Builder().bbInitialUBCheckForRotatableLiterals(false).build()); final FormulaFactory f = new FormulaFactory(); final Formula formula = FormulaReader.readPseudoBooleanFormula("src/test/resources/formulas/large_formula.txt", f); - MiniSat solver = MiniSat.miniSat(f, new MiniSatConfig.Builder().fastBackboneComputation(true).build()); + MiniSat solver = MiniSat.miniSat(f); solver.add(formula); - final Backbone backbone = solver.computeBackbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); + final Backbone backbone = solver.backbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE); for (final MiniSatConfig config : configs) { solver = MiniSat.miniSat(f, config); solver.add(formula); - assertThat(solver.computeBackbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE)).isEqualTo(backbone); + assertThat(solver.backbone(formula.variables(), BackboneType.POSITIVE_AND_NEGATIVE)).isEqualTo(backbone); } } } diff --git a/src/test/java/org/logicng/solvers/sat/ConfigurationsTest.java b/src/test/java/org/logicng/solvers/sat/ConfigurationsTest.java index f6642f94..91ad5925 100644 --- a/src/test/java/org/logicng/solvers/sat/ConfigurationsTest.java +++ b/src/test/java/org/logicng/solvers/sat/ConfigurationsTest.java @@ -71,7 +71,6 @@ public void testMiniSatConfigToString() { "proofGeneration=false%n" + "cnfMethod=FACTORY_CNF%n" + "auxiliaryVariablesInModels=true%n" + - "fastBackboneComputation=false%n" + "bbInitialUBCheckForRotatableLiterals=true%n" + "bbCheckForComplementModelLiterals=true%n" + "bbCheckForRotatableLiterals=true%n" + From 7e03a253a65d7de8d5c318136a8fd073a9d5482e Mon Sep 17 00:00:00 2001 From: Christoph Zengler Date: Mon, 29 Jul 2019 20:30:52 +0200 Subject: [PATCH 10/16] Large backbone unit tests --- .../java/org/logicng/solvers/SATSolver.java | 9 + .../org/logicng/solvers/sat/GlucoseSyrup.java | 15 +- .../solvers/sat/MiniSatStyleSolver.java | 4 +- .../logicng/solvers/BackboneOnSolverTest.java | 340 ++++++++++++++++++ .../backbones/backbone_large_formula.txt | 6 + .../backbones/backbone_small_formulas.txt | 6 + 6 files changed, 377 insertions(+), 3 deletions(-) create mode 100644 src/test/java/org/logicng/solvers/BackboneOnSolverTest.java create mode 100644 src/test/resources/backbones/backbone_large_formula.txt create mode 100644 src/test/resources/backbones/backbone_small_formulas.txt diff --git a/src/main/java/org/logicng/solvers/SATSolver.java b/src/main/java/org/logicng/solvers/SATSolver.java index 4c24728b..3aeab790 100644 --- a/src/main/java/org/logicng/solvers/SATSolver.java +++ b/src/main/java/org/logicng/solvers/SATSolver.java @@ -464,6 +464,15 @@ public void setSolverToUndef() { */ public abstract UNSATCore unsatCore(); + /** + * Computes a backbone with both positive and negative variables of the current formula on the solver. + * @param relevantVariables the variables which should be considered for the backbone + * @return the backbone + */ + public Backbone backbone(final Collection relevantVariables) { + return backbone(relevantVariables, BackboneType.POSITIVE_AND_NEGATIVE); + } + /** * Computes a backbone of the current formula on the solver. * @param relevantVariables the variables which should be considered for the backbone diff --git a/src/main/java/org/logicng/solvers/sat/GlucoseSyrup.java b/src/main/java/org/logicng/solvers/sat/GlucoseSyrup.java index 39dc80e7..e8c8e010 100644 --- a/src/main/java/org/logicng/solvers/sat/GlucoseSyrup.java +++ b/src/main/java/org/logicng/solvers/sat/GlucoseSyrup.java @@ -856,7 +856,7 @@ private Tristate search() { this.lbdQueue.fastClear(); int bt = 0; if (this.incremental) { - bt = (decisionLevel() < this.assumptions.size()) ? decisionLevel() : this.assumptions.size(); + bt = Math.min(decisionLevel(), this.assumptions.size()); } cancelUntil(bt); return Tristate.UNDEF; @@ -1050,5 +1050,18 @@ private void simplifyClause(final LNGIntVector outLearnt, final LNGIntVector sel this.seen.set(var(selectors.get(m)), false); } } + + @Override + protected boolean isRotatable(final int lit) { + if (!super.isRotatable(lit)) { + return false; + } + for (final MSWatcher watcher : this.watchesBin.get(not(lit))) { + if (isUnit(lit, watcher.clause())) { + return false; + } + } + return true; + } } diff --git a/src/main/java/org/logicng/solvers/sat/MiniSatStyleSolver.java b/src/main/java/org/logicng/solvers/sat/MiniSatStyleSolver.java index dd587bd4..719a5a85 100644 --- a/src/main/java/org/logicng/solvers/sat/MiniSatStyleSolver.java +++ b/src/main/java/org/logicng/solvers/sat/MiniSatStyleSolver.java @@ -944,7 +944,7 @@ private boolean isUPZeroLit(final int var) { * @param clause clause containing the literal * @return {@code true} if the literal is unit, {@code false} otherwise */ - private boolean isUnit(final int lit, final MSClause clause) { + protected boolean isUnit(final int lit, final MSClause clause) { for (int i = 0; i < clause.size(); ++i) { final int clauseLit = clause.get(i); if (lit != clauseLit && this.model.get(var(clauseLit)) != sign(clauseLit)) { @@ -959,7 +959,7 @@ private boolean isUnit(final int lit, final MSClause clause) { * @param lit literal to test * @return {@code true} if the literal is rotatable, otherwise {@code false} */ - private boolean isRotatable(final int lit) { + protected boolean isRotatable(final int lit) { // A rotatable literal MUST NOT be a unit propagated literal if (v(lit).reason() != null) { return false; diff --git a/src/test/java/org/logicng/solvers/BackboneOnSolverTest.java b/src/test/java/org/logicng/solvers/BackboneOnSolverTest.java new file mode 100644 index 00000000..62ef3278 --- /dev/null +++ b/src/test/java/org/logicng/solvers/BackboneOnSolverTest.java @@ -0,0 +1,340 @@ +package org.logicng.solvers; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.logicng.backbones.Backbone; +import org.logicng.backbones.BackboneType; +import org.logicng.formulas.Formula; +import org.logicng.formulas.FormulaFactory; +import org.logicng.formulas.Literal; +import org.logicng.formulas.Variable; +import org.logicng.io.parsers.ParserException; +import org.logicng.io.readers.FormulaReader; +import org.logicng.solvers.sat.GlucoseConfig; +import org.logicng.solvers.sat.MiniSat2Solver; +import org.logicng.solvers.sat.MiniSatConfig; +import org.logicng.util.Pair; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; + +/** + * Tests for generating backbones on solvers. + * @version 1.6.0 + * @since 1.6.0 + */ +@RunWith(Parameterized.class) +public class BackboneOnSolverTest { + + private static final FormulaFactory f = new FormulaFactory(); + + private final MiniSat solver; + + @Parameterized.Parameters(name = "{1}") + public static Collection solvers() { + final MiniSatConfig configNoPG1 = new MiniSatConfig.Builder().cnfMethod(MiniSatConfig.CNFMethod.FACTORY_CNF).bbCheckForRotatableLiterals(false).bbCheckForComplementModelLiterals(false).bbInitialUBCheckForRotatableLiterals(false).build(); + final MiniSatConfig configNoPG2 = new MiniSatConfig.Builder().cnfMethod(MiniSatConfig.CNFMethod.FACTORY_CNF).bbCheckForRotatableLiterals(true).bbCheckForComplementModelLiterals(false).bbInitialUBCheckForRotatableLiterals(false).build(); + final MiniSatConfig configNoPG3 = new MiniSatConfig.Builder().cnfMethod(MiniSatConfig.CNFMethod.FACTORY_CNF).bbCheckForRotatableLiterals(false).bbCheckForComplementModelLiterals(true).bbInitialUBCheckForRotatableLiterals(false).build(); + final MiniSatConfig configNoPG4 = new MiniSatConfig.Builder().cnfMethod(MiniSatConfig.CNFMethod.FACTORY_CNF).bbCheckForRotatableLiterals(false).bbCheckForComplementModelLiterals(false).bbInitialUBCheckForRotatableLiterals(true).build(); + final MiniSatConfig configNoPG5 = new MiniSatConfig.Builder().cnfMethod(MiniSatConfig.CNFMethod.FACTORY_CNF).bbCheckForRotatableLiterals(true).bbCheckForComplementModelLiterals(true).bbInitialUBCheckForRotatableLiterals(true).build(); + final MiniSatConfig configPG1 = new MiniSatConfig.Builder().cnfMethod(MiniSatConfig.CNFMethod.PG_ON_SOLVER).bbCheckForRotatableLiterals(false).bbCheckForComplementModelLiterals(false).bbInitialUBCheckForRotatableLiterals(false).build(); + final MiniSatConfig configPG2 = new MiniSatConfig.Builder().cnfMethod(MiniSatConfig.CNFMethod.PG_ON_SOLVER).bbCheckForRotatableLiterals(true).bbCheckForComplementModelLiterals(false).bbInitialUBCheckForRotatableLiterals(false).build(); + final MiniSatConfig configPG3 = new MiniSatConfig.Builder().cnfMethod(MiniSatConfig.CNFMethod.PG_ON_SOLVER).bbCheckForRotatableLiterals(false).bbCheckForComplementModelLiterals(true).bbInitialUBCheckForRotatableLiterals(false).build(); + final MiniSatConfig configPG4 = new MiniSatConfig.Builder().cnfMethod(MiniSatConfig.CNFMethod.PG_ON_SOLVER).bbCheckForRotatableLiterals(false).bbCheckForComplementModelLiterals(false).bbInitialUBCheckForRotatableLiterals(true).build(); + final MiniSatConfig configPG5 = new MiniSatConfig.Builder().cnfMethod(MiniSatConfig.CNFMethod.PG_ON_SOLVER).bbCheckForRotatableLiterals(true).bbCheckForComplementModelLiterals(true).bbInitialUBCheckForRotatableLiterals(true).build(); + final List> configs = Arrays.asList( + new Pair<>(configNoPG1, "FF CNF -ROT -COMP -UB"), + new Pair<>(configNoPG2, "FF CNF +ROT -COMP -UB"), + new Pair<>(configNoPG3, "FF CNF -ROT +COMP -UB"), + new Pair<>(configNoPG4, "FF CNF -ROT -COMP +UB"), + new Pair<>(configNoPG5, "FF CNF +ROT +COMP +UB"), + new Pair<>(configPG1, "PG CNF -ROT -COMP -UB"), + new Pair<>(configPG2, "PG CNF +ROT -COMP -UB"), + new Pair<>(configPG3, "PG CNF -ROT +COMP -UB"), + new Pair<>(configPG4, "PG CNF -ROT -COMP +UB"), + new Pair<>(configPG5, "PG CNF +ROT +COMP +UB") + ); + final List solvers = new ArrayList<>(); + for (final Pair config : configs) { + solvers.add(new Object[]{MiniSat.miniSat(f, config.first()), "MiniSat (" + config.second() + ")"}); + solvers.add(new Object[]{MiniSat.miniCard(f, config.first()), "MiniCard (" + config.second() + ")"}); + solvers.add(new Object[]{MiniSat.glucose(f, config.first(), new GlucoseConfig.Builder().build()), "Glucose (" + config.second() + ")"}); + } + return solvers; + } + + public BackboneOnSolverTest(final MiniSat solver, final String description) { + this.solver = solver; + } + + @Test + public void testConstants() { + this.solver.reset(); + SolverState state = null; + if (this.solver.underlyingSolver() instanceof MiniSat2Solver) { + state = this.solver.saveState(); + } + this.solver.add(f.falsum()); + Backbone backbone = this.solver.backbone(v("a b c"), BackboneType.POSITIVE_AND_NEGATIVE); + assertThat(backbone.isSat()).isFalse(); + assertThat(backbone.getCompleteBackbone()).isEmpty(); + if (this.solver.underlyingSolver() instanceof MiniSat2Solver) { + this.solver.loadState(state); + } else { + this.solver.reset(); + } + this.solver.add(f.verum()); + backbone = this.solver.backbone(v("a b c"), BackboneType.POSITIVE_AND_NEGATIVE); + assertThat(backbone.isSat()).isTrue(); + assertThat(backbone.getCompleteBackbone()).isEmpty(); + } + + @Test + public void testSimpleBackbones() throws ParserException { + this.solver.reset(); + SolverState state = null; + if (this.solver.underlyingSolver() instanceof MiniSat2Solver) { + state = this.solver.saveState(); + } + this.solver.add(f.parse("a & b & ~c")); + Backbone backbone = this.solver.backbone(v("a b c"), BackboneType.POSITIVE_AND_NEGATIVE); + assertThat(backbone.isSat()).isTrue(); + assertThat(backbone.getCompleteBackbone()).containsExactly(f.variable("a"), f.variable("b"), f.literal("c", false)); + if (this.solver.underlyingSolver() instanceof MiniSat2Solver) { + this.solver.loadState(state); + } else { + this.solver.reset(); + } + this.solver.add(f.parse("~a & ~b & c")); + backbone = this.solver.backbone(v("a c"), BackboneType.POSITIVE_AND_NEGATIVE); + assertThat(backbone.isSat()).isTrue(); + assertThat(backbone.getCompleteBackbone()).containsExactly(f.literal("a", false), f.variable("c")); + } + + @Test + public void testSimpleFormulas() throws ParserException { + this.solver.reset(); + this.solver.add(f.parse("(a => c | d) & (b => d | ~e) & (a | b)")); + Backbone backbone = this.solver.backbone(v("a b c d e f"), BackboneType.POSITIVE_AND_NEGATIVE); + assertThat(backbone.isSat()).isTrue(); + assertThat(backbone.getCompleteBackbone()).isEmpty(); + this.solver.add(f.parse("a => b")); + this.solver.add(f.parse("b => a")); + this.solver.add(f.parse("~d")); + backbone = this.solver.backbone(v("a b c d e f g h"), BackboneType.POSITIVE_AND_NEGATIVE); + assertThat(backbone.isSat()).isTrue(); + assertThat(backbone.getCompleteBackbone()).containsExactly(f.variable("a"), f.variable("b"), f.variable("c"), + f.literal("d", false), f.literal("e", false)); + } + + @Test + public void testRealFormulaIncremental1() throws IOException, ParserException { + this.solver.reset(); + final Formula formula = FormulaReader.readPseudoBooleanFormula("src/test/resources/formulas/large_formula.txt", f); + this.solver.add(formula); + final List expectedBackbones = new ArrayList<>(); + final BufferedReader reader = new BufferedReader(new FileReader("src/test/resources/backbones/backbone_large_formula.txt")); + while (reader.ready()) { + expectedBackbones.add(reader.readLine()); + } + reader.close(); + Backbone backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(0))); + this.solver.add(f.variable("v411")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(1))); + assertThat(backbone.isSat()).isTrue(); + this.solver.add(f.variable("v385")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(2))); + assertThat(backbone.isSat()).isTrue(); + this.solver.add(f.variable("v275")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(3))); + assertThat(backbone.isSat()).isTrue(); + this.solver.add(f.variable("v188")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(4))); + assertThat(backbone.isSat()).isTrue(); + this.solver.add(f.variable("v103")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(5))); + assertThat(backbone.isSat()).isTrue(); + this.solver.add(f.variable("v404")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEmpty(); + assertThat(backbone.isSat()).isFalse(); + } + + @Ignore("Long Running") + @Test + public void testRealFormulaIncremental2() throws IOException, ParserException { + this.solver.reset(); + final Formula formula = FormulaReader.readPseudoBooleanFormula("src/test/resources/formulas/small_formulas.txt", f); + this.solver.add(formula); + final List expectedBackbones = new ArrayList<>(); + final BufferedReader reader = new BufferedReader(new FileReader("src/test/resources/backbones/backbone_small_formulas.txt")); + while (reader.ready()) { + expectedBackbones.add(reader.readLine()); + } + reader.close(); + Backbone backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(0))); + this.solver.add(f.variable("v2609")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(1))); + assertThat(backbone.isSat()).isTrue(); + this.solver.add(f.variable("v2416")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(2))); + assertThat(backbone.isSat()).isTrue(); + this.solver.add(f.variable("v2048")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(3))); + assertThat(backbone.isSat()).isTrue(); + this.solver.add(f.variable("v39")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(4))); + assertThat(backbone.isSat()).isTrue(); + this.solver.add(f.variable("v1663")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(5))); + assertThat(backbone.isSat()).isTrue(); + this.solver.add(f.variable("v2238")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEmpty(); + assertThat(backbone.isSat()).isFalse(); + } + + @Test + public void testRealFormulaIncrementalDecremental1() throws IOException, ParserException { + if (this.solver.underlyingSolver() instanceof MiniSat2Solver) { + this.solver.reset(); + final Formula formula = FormulaReader.readPseudoBooleanFormula("src/test/resources/formulas/large_formula.txt", f); + this.solver.add(formula); + final SolverState state = this.solver.saveState(); + final List expectedBackbones = new ArrayList<>(); + final BufferedReader reader = new BufferedReader(new FileReader("src/test/resources/backbones/backbone_large_formula.txt")); + while (reader.ready()) { + expectedBackbones.add(reader.readLine()); + } + reader.close(); + Backbone backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(0))); + this.solver.add(f.variable("v411")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(1))); + assertThat(backbone.isSat()).isTrue(); + + this.solver.loadState(state); + this.solver.add(f.parse("v411 & v385")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(2))); + assertThat(backbone.isSat()).isTrue(); + + this.solver.loadState(state); + this.solver.add(f.parse("v411 & v385 & v275")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(3))); + assertThat(backbone.isSat()).isTrue(); + + this.solver.loadState(state); + this.solver.add(f.parse("v411 & v385 & v275 & v188")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(4))); + assertThat(backbone.isSat()).isTrue(); + + this.solver.loadState(state); + this.solver.add(f.parse("v411 & v385 & v275 & v188 & v103")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(5))); + assertThat(backbone.isSat()).isTrue(); + + this.solver.loadState(state); + this.solver.add(f.parse("v411 & v385 & v275 & v188 & v103 & v404")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEmpty(); + assertThat(backbone.isSat()).isFalse(); + } + } + + @Ignore("Long Running") + @Test + public void testRealFormulaIncrementalDecremental2() throws IOException, ParserException { + if (this.solver.underlyingSolver() instanceof MiniSat2Solver) { + this.solver.reset(); + final Formula formula = FormulaReader.readPseudoBooleanFormula("src/test/resources/formulas/small_formulas.txt", f); + this.solver.add(formula); + final SolverState state = this.solver.saveState(); + final List expectedBackbones = new ArrayList<>(); + final BufferedReader reader = new BufferedReader(new FileReader("src/test/resources/backbones/backbone_small_formulas.txt")); + while (reader.ready()) { + expectedBackbones.add(reader.readLine()); + } + reader.close(); + Backbone backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(0))); + this.solver.add(f.variable("v2609")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(1))); + assertThat(backbone.isSat()).isTrue(); + + this.solver.loadState(state); + this.solver.add(f.parse("v2609 & v2416")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(2))); + assertThat(backbone.isSat()).isTrue(); + + this.solver.loadState(state); + this.solver.add(f.parse("v2609 & v2416 & v2048")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(3))); + assertThat(backbone.isSat()).isTrue(); + + this.solver.loadState(state); + this.solver.add(f.parse("v2609 & v2416 & v2048 & v39")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(4))); + assertThat(backbone.isSat()).isTrue(); + + this.solver.loadState(state); + this.solver.add(f.parse("v2609 & v2416 & v2048 & v39 & v1663")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEqualTo(parseBackbone(expectedBackbones.get(5))); + assertThat(backbone.isSat()).isTrue(); + + this.solver.loadState(state); + this.solver.add(f.parse("v2609 & v2416 & v2048 & v39 & v1663 & v2238")); + backbone = this.solver.backbone(formula.variables()); + assertThat(backbone.getCompleteBackbone()).isEmpty(); + assertThat(backbone.isSat()).isFalse(); + } + } + + private SortedSet v(final String s) { + final SortedSet vars = new TreeSet<>(); + for (final String name : s.split(" ")) { + vars.add(f.variable(name)); + } + return vars; + } + + private SortedSet parseBackbone(final String string) throws ParserException { + final SortedSet literals = new TreeSet<>(); + for (final String lit : string.split(" ")) { + literals.add((Literal) f.parse(lit)); + } + return literals; + } +} diff --git a/src/test/resources/backbones/backbone_large_formula.txt b/src/test/resources/backbones/backbone_large_formula.txt new file mode 100644 index 00000000..fdd24bda --- /dev/null +++ b/src/test/resources/backbones/backbone_large_formula.txt @@ -0,0 +1,6 @@ +~v101 v11 ~v121 v126 ~v137 ~v144 ~v146 ~v147 ~v150 v159 ~v160 ~v173 ~v178 ~v189 v2 v22 v221 v227 ~v248 v26 v33 ~v345 ~v349 v40 ~v415 ~v460 ~v51 ~v54 ~v55 ~v56 ~v57 v6 ~v60 ~v69 ~v70 ~v73 ~v74 ~v78 ~v91 ~v92 ~v95 ~v96 ~v97 +~v101 v11 ~v121 v126 ~v137 ~v144 ~v146 ~v147 ~v150 v159 ~v160 v17 ~v173 ~v178 ~v189 v2 v22 v221 v227 ~v248 v26 v33 ~v345 ~v349 v40 v411 ~v415 ~v460 ~v51 ~v54 ~v55 ~v56 ~v57 v6 ~v60 ~v69 ~v70 ~v72 ~v73 ~v74 ~v78 ~v91 ~v92 ~v95 ~v96 ~v97 +v0 ~v101 v11 ~v112 ~v121 v126 ~v137 ~v144 ~v146 ~v147 ~v150 v159 ~v160 v17 ~v173 ~v178 ~v189 v2 v22 v221 v227 ~v248 v26 v33 ~v345 ~v349 ~v360 ~v361 ~v362 ~v363 ~v364 ~v365 ~v366 ~v368 ~v369 ~v370 ~v371 ~v381 ~v382 ~v383 ~v384 v385 ~v386 ~v387 ~v388 ~v389 ~v390 ~v391 ~v392 ~v393 ~v394 v40 v411 ~v415 ~v460 ~v51 ~v54 ~v55 ~v56 ~v57 v6 ~v60 ~v69 ~v70 ~v72 ~v73 ~v74 ~v78 ~v87 ~v91 ~v92 ~v93 ~v95 ~v96 ~v97 ~v98 +v0 ~v101 v11 ~v112 ~v121 v126 ~v137 ~v144 ~v146 ~v147 ~v150 v159 ~v160 v17 ~v173 ~v178 ~v189 v2 v22 v221 v227 ~v248 v26 ~v265 ~v269 ~v270 ~v271 ~v272 ~v273 ~v274 v275 ~v276 ~v277 ~v278 ~v279 ~v280 ~v281 ~v282 ~v283 ~v284 ~v286 ~v287 ~v288 ~v289 ~v290 ~v291 ~v292 ~v294 ~v295 ~v296 ~v297 ~v298 ~v299 ~v300 ~v301 ~v302 ~v303 ~v304 ~v306 ~v307 ~v308 ~v309 ~v310 ~v311 ~v312 ~v313 ~v314 ~v315 ~v316 ~v317 ~v318 ~v319 ~v320 ~v321 ~v322 ~v323 ~v324 ~v325 ~v326 ~v327 ~v328 ~v329 v33 ~v331 ~v332 ~v333 ~v334 ~v335 ~v336 ~v337 ~v338 ~v339 ~v340 ~v341 ~v345 ~v349 ~v360 ~v361 ~v362 ~v363 ~v364 ~v365 ~v366 ~v368 ~v369 ~v370 ~v371 ~v381 ~v382 ~v383 ~v384 v385 ~v386 ~v387 ~v388 ~v389 ~v390 ~v391 ~v392 ~v393 ~v394 v40 v411 ~v415 ~v460 v48 ~v51 ~v54 ~v55 ~v56 ~v57 v6 ~v60 ~v69 ~v70 ~v72 ~v73 ~v74 ~v78 ~v87 ~v91 ~v92 ~v93 ~v95 ~v96 ~v97 ~v98 +v0 ~v101 v11 ~v112 ~v121 v126 ~v137 ~v144 ~v146 ~v147 ~v150 v159 ~v160 v17 ~v173 ~v178 v188 ~v189 v2 v22 v221 v227 ~v248 v26 ~v265 ~v269 ~v270 ~v271 ~v272 ~v273 ~v274 v275 ~v276 ~v277 ~v278 ~v279 ~v280 ~v281 ~v282 ~v283 ~v284 ~v286 ~v287 ~v288 ~v289 ~v290 ~v291 ~v292 ~v294 ~v295 ~v296 ~v297 ~v298 ~v299 ~v300 ~v301 ~v302 ~v303 ~v304 ~v306 ~v307 ~v308 ~v309 ~v310 ~v311 ~v312 ~v313 ~v314 ~v315 ~v316 ~v317 ~v318 ~v319 ~v320 ~v321 ~v322 ~v323 ~v324 ~v325 ~v326 ~v327 ~v328 ~v329 v33 ~v331 ~v332 ~v333 ~v334 ~v335 ~v336 ~v337 ~v338 ~v339 ~v340 ~v341 ~v345 ~v349 v36 ~v360 ~v361 ~v362 ~v363 ~v364 ~v365 ~v366 ~v368 ~v369 ~v370 ~v371 ~v381 ~v382 ~v383 ~v384 v385 ~v386 ~v387 ~v388 ~v389 ~v390 ~v391 ~v392 ~v393 ~v394 v40 v411 ~v415 ~v460 v48 ~v51 ~v54 ~v55 ~v56 ~v57 v6 ~v60 ~v69 ~v70 ~v72 ~v73 ~v74 ~v78 ~v87 ~v91 ~v92 ~v93 ~v95 ~v96 ~v97 ~v98 +v0 ~v100 ~v101 ~v102 v103 ~v104 ~v105 ~v106 ~v107 ~v108 ~v109 v11 ~v110 ~v111 ~v112 ~v113 ~v114 ~v115 ~v116 ~v117 ~v118 ~v119 ~v120 ~v121 ~v122 v124 v126 v129 ~v137 ~v144 ~v146 ~v147 ~v150 v151 v159 ~v160 v17 ~v173 ~v178 v188 ~v189 ~v19 v2 v209 v22 v221 v227 ~v233 ~v242 ~v248 v26 ~v265 ~v269 ~v270 ~v271 ~v272 ~v273 ~v274 v275 ~v276 ~v277 ~v278 ~v279 ~v280 ~v281 ~v282 ~v283 ~v284 ~v286 ~v287 ~v288 ~v289 ~v290 ~v291 ~v292 ~v294 ~v295 ~v296 ~v297 ~v298 ~v299 ~v300 ~v301 ~v302 ~v303 ~v304 ~v306 ~v307 ~v308 ~v309 ~v310 ~v311 ~v312 ~v313 ~v314 ~v315 ~v316 ~v317 ~v318 ~v319 ~v320 ~v321 ~v322 ~v323 ~v324 ~v325 ~v326 ~v327 ~v328 ~v329 v33 ~v331 ~v332 ~v333 ~v334 ~v335 ~v336 ~v337 ~v338 ~v339 ~v340 ~v341 ~v342 ~v343 ~v345 ~v347 ~v349 ~v354 ~v355 ~v356 ~v357 v36 ~v360 ~v361 ~v362 ~v363 ~v364 ~v365 ~v366 ~v368 ~v369 ~v370 ~v371 ~v381 ~v382 ~v383 ~v384 v385 ~v386 ~v387 ~v388 ~v389 ~v390 ~v391 ~v392 ~v393 ~v394 v396 ~v397 ~v398 ~v399 v40 ~v400 ~v401 ~v402 ~v403 ~v404 ~v405 ~v406 ~v407 ~v408 ~v409 ~v410 v411 v413 ~v415 ~v416 v418 ~v419 ~v420 ~v421 ~v432 ~v435 ~v437 ~v446 ~v460 v48 ~v51 ~v54 ~v55 ~v56 ~v57 v6 ~v60 ~v64 ~v65 ~v69 ~v70 ~v71 ~v72 ~v73 ~v74 ~v78 ~v86 ~v87 ~v91 ~v92 ~v93 ~v94 ~v95 ~v96 ~v97 ~v98 ~v99 diff --git a/src/test/resources/backbones/backbone_small_formulas.txt b/src/test/resources/backbones/backbone_small_formulas.txt new file mode 100644 index 00000000..6ebf5314 --- /dev/null +++ b/src/test/resources/backbones/backbone_small_formulas.txt @@ -0,0 +1,6 @@ +~v0 ~v10 ~v100 ~v1000 ~v1001 ~v1002 ~v1003 ~v1004 ~v1005 ~v1006 ~v1007 ~v1008 ~v1009 ~v101 ~v1010 ~v1011 ~v1012 ~v1013 ~v1014 ~v1015 ~v1016 ~v1017 ~v1018 ~v1019 ~v102 ~v1020 ~v1021 ~v1022 ~v1023 ~v1024 ~v1025 ~v1026 ~v1027 ~v1028 ~v1029 ~v103 ~v1030 ~v1031 ~v1032 ~v1033 ~v1034 ~v1035 ~v1036 ~v1037 ~v1038 ~v1039 ~v104 ~v1040 ~v1041 ~v1042 ~v1043 ~v1044 ~v1045 ~v1046 ~v1047 ~v1048 ~v1049 ~v105 ~v1050 ~v1051 ~v1052 ~v1053 ~v1054 ~v1055 ~v1056 ~v1057 ~v1058 ~v1059 ~v106 ~v1060 ~v1061 ~v1062 ~v1063 ~v1064 ~v1065 ~v1066 ~v1067 ~v1068 ~v1069 ~v107 ~v1070 ~v1071 ~v1072 ~v1073 ~v1074 ~v1075 ~v1076 ~v1077 ~v1079 ~v108 ~v1080 ~v1081 ~v1082 ~v1083 ~v1084 ~v1085 ~v1086 ~v1087 ~v1088 ~v1089 ~v109 ~v1090 ~v1091 ~v1092 ~v1093 ~v1094 ~v1095 ~v1096 ~v1097 ~v1098 ~v1099 ~v110 ~v1100 ~v1101 ~v1102 ~v1103 ~v1104 ~v1105 ~v1106 ~v1107 ~v1108 ~v1109 ~v111 ~v1110 ~v1111 ~v1112 ~v1113 ~v1114 ~v1115 ~v1116 ~v1117 ~v1118 ~v1119 ~v112 ~v1120 ~v1121 ~v1122 ~v1123 ~v1124 ~v1125 ~v1126 ~v1127 ~v1128 ~v1129 ~v113 ~v1130 ~v1131 ~v1132 ~v1133 ~v1134 ~v1135 ~v1136 ~v1137 ~v1138 ~v1139 ~v114 ~v1140 ~v1141 ~v1142 ~v1143 ~v1144 ~v1145 ~v1146 ~v1147 ~v1148 ~v1149 ~v115 ~v1150 ~v1151 ~v1152 ~v1153 ~v1154 ~v1155 ~v1156 ~v1157 ~v1158 ~v1159 ~v116 ~v1160 ~v1161 ~v1162 ~v1163 ~v1164 ~v1165 ~v1166 ~v1167 ~v1169 ~v117 ~v1170 ~v1171 ~v1172 ~v1173 ~v1174 ~v1175 ~v1176 ~v1177 ~v1178 ~v1179 ~v118 ~v1180 ~v1181 ~v1182 ~v1183 ~v1184 ~v1185 ~v1186 ~v1187 ~v1188 ~v1189 ~v119 ~v1190 ~v1191 ~v1192 ~v1193 ~v1194 ~v1195 ~v1196 ~v1197 ~v1198 ~v1199 ~v120 ~v1201 ~v1202 ~v1204 ~v1205 ~v1206 ~v1207 ~v1208 ~v121 ~v1210 ~v1211 ~v1212 ~v1213 ~v1214 ~v1215 ~v1216 ~v1217 ~v1218 ~v1219 ~v122 ~v1220 ~v1221 ~v1222 ~v1223 ~v1224 ~v1225 ~v1226 ~v1227 ~v1228 ~v1229 ~v123 ~v1231 ~v1233 ~v1234 ~v1235 ~v1236 ~v1237 ~v1238 ~v1239 ~v124 ~v1240 ~v1241 ~v1242 ~v1243 ~v1244 ~v1245 ~v1246 ~v1247 ~v1248 ~v1249 ~v125 ~v1250 ~v1251 ~v1252 ~v1253 ~v1254 ~v1255 ~v1256 ~v1257 ~v1258 ~v1259 ~v126 ~v1260 ~v1261 ~v1262 ~v1263 ~v1264 ~v1265 ~v1266 ~v1267 ~v1268 ~v1269 ~v127 ~v1270 ~v1271 ~v1272 ~v1273 ~v1274 ~v1275 ~v1276 ~v1277 ~v1278 ~v1279 ~v128 ~v1280 ~v1281 ~v1282 ~v1283 ~v1284 ~v1285 ~v1286 ~v1287 ~v1288 ~v1289 ~v129 ~v1290 ~v1291 ~v1292 ~v1293 ~v1294 ~v1295 ~v1296 ~v1297 ~v1298 ~v1299 ~v1300 ~v1301 ~v1302 ~v1303 ~v1304 ~v1305 ~v1306 ~v1307 ~v1308 ~v1309 ~v1310 ~v1311 ~v1312 ~v1313 ~v1314 ~v1315 ~v1316 ~v1317 ~v1318 ~v1319 ~v1320 ~v1321 ~v1322 ~v1323 ~v1324 ~v1325 ~v1326 ~v1327 ~v1328 ~v1329 ~v133 ~v1330 ~v1331 ~v1332 ~v1333 ~v1334 ~v1335 ~v1336 ~v1337 ~v1338 ~v1339 ~v1340 ~v1341 ~v1342 ~v1343 ~v1344 ~v1345 ~v1346 ~v1347 ~v1348 ~v1349 ~v135 ~v1351 ~v1352 ~v1353 ~v1357 ~v1358 ~v1359 ~v136 ~v1360 ~v1367 ~v1368 ~v137 ~v138 ~v1382 ~v1383 ~v139 ~v1395 ~v1397 ~v140 ~v1403 ~v1406 ~v1407 ~v1410 ~v1411 ~v1412 ~v1413 v1414 ~v1415 ~v1416 ~v1417 ~v1418 ~v1419 ~v142 ~v1420 ~v1422 ~v1425 ~v1426 ~v1427 ~v1428 ~v1429 ~v143 ~v1430 ~v1431 ~v1432 ~v1433 ~v1434 ~v1435 ~v1436 ~v1437 ~v1438 ~v1439 ~v144 ~v1440 ~v1441 ~v1442 ~v1443 ~v1444 ~v1445 ~v1446 ~v145 ~v1456 ~v146 ~v1462 ~v1463 ~v1465 ~v147 ~v1478 ~v1479 ~v148 ~v1480 ~v1488 ~v1489 ~v149 ~v1490 ~v1491 ~v1492 ~v1497 ~v1498 ~v1499 ~v150 ~v1502 ~v1503 ~v1504 ~v1505 ~v1506 ~v1508 ~v151 ~v1513 ~v1514 ~v1516 ~v1517 ~v1518 ~v1519 ~v152 ~v1520 ~v1521 ~v1522 ~v1523 ~v1524 ~v1525 ~v1526 ~v1527 ~v1528 ~v1529 ~v153 ~v1530 ~v1531 ~v1532 ~v1534 ~v1535 ~v1536 ~v1537 ~v1538 ~v154 ~v1542 ~v1543 ~v1544 ~v1545 ~v1548 ~v155 ~v1552 ~v1557 ~v1558 ~v156 ~v1561 ~v1564 ~v1566 ~v1569 ~v157 ~v1573 ~v1574 ~v1575 ~v1576 ~v1577 ~v1578 ~v1579 ~v158 ~v1580 ~v1581 ~v1582 ~v1583 ~v1584 ~v1585 ~v1586 ~v1587 ~v1588 ~v1589 ~v159 ~v1590 ~v1591 ~v1592 ~v1594 ~v1595 ~v1596 ~v1597 ~v1598 ~v1599 ~v160 ~v1600 ~v1601 ~v1602 ~v1603 ~v1608 ~v1609 ~v161 ~v1610 ~v1611 ~v1612 ~v1616 ~v1617 ~v1618 ~v1619 ~v162 ~v1621 ~v1623 ~v1627 ~v163 ~v1630 ~v1631 ~v1632 ~v1633 ~v1634 ~v1635 ~v1637 ~v1638 ~v164 ~v1641 ~v1642 ~v1643 ~v1646 ~v165 ~v1651 ~v1652 ~v1653 ~v166 ~v1661 ~v1667 ~v167 ~v1670 ~v1671 ~v1672 ~v1673 ~v1674 ~v1675 ~v1676 ~v1677 ~v1678 ~v1679 ~v168 ~v1680 ~v1681 ~v1682 ~v1683 ~v1684 ~v1685 ~v1686 ~v1687 ~v1688 ~v1689 ~v169 ~v1690 ~v1691 ~v1692 ~v1693 ~v1694 ~v1695 ~v1696 ~v1697 ~v1698 ~v1699 ~v170 ~v1700 ~v1701 ~v1702 ~v1703 ~v1704 ~v1705 ~v1706 ~v1707 ~v1708 ~v1709 ~v171 ~v1710 ~v1711 ~v1712 ~v1713 ~v1714 ~v1715 ~v1716 ~v1717 ~v1718 ~v1719 ~v172 ~v1720 ~v1721 ~v1722 ~v1723 ~v1724 ~v1725 ~v1726 ~v1727 ~v1728 ~v1729 ~v173 ~v1730 ~v1731 ~v1732 ~v1733 ~v1734 ~v1735 ~v1736 ~v1737 ~v1738 ~v1739 ~v174 ~v1740 ~v1741 ~v1742 ~v1743 ~v1744 ~v1745 ~v1746 ~v1747 ~v1748 ~v1749 ~v175 ~v1750 ~v1751 ~v1752 ~v1753 ~v1754 ~v1755 ~v1756 ~v1757 ~v1758 ~v1759 ~v176 ~v1760 ~v1761 ~v1762 ~v1763 ~v1764 ~v1765 ~v1766 ~v1767 ~v1768 ~v1769 ~v177 ~v1770 ~v1771 ~v1772 ~v1773 ~v1774 ~v1775 ~v1776 ~v1777 ~v1778 ~v1779 ~v1780 ~v1781 ~v1782 ~v1783 ~v1784 ~v1785 ~v1786 ~v1787 ~v1788 ~v1789 ~v1790 ~v1791 ~v1792 ~v1793 ~v1794 ~v1795 ~v1796 ~v1797 ~v1798 ~v1799 ~v180 ~v1800 ~v1801 ~v1802 ~v1803 ~v1804 ~v1805 ~v1806 ~v1807 ~v1808 ~v1809 ~v181 ~v1810 ~v1811 ~v1812 ~v1813 ~v1814 ~v1815 ~v1816 ~v1817 ~v1818 ~v1819 ~v182 ~v1820 ~v1821 ~v1822 ~v1823 ~v1824 ~v1825 ~v1826 ~v1827 ~v1828 ~v1829 ~v183 ~v1830 ~v1831 ~v1832 ~v1833 ~v1834 ~v1835 ~v1836 ~v1837 ~v1838 ~v1839 ~v184 ~v1840 ~v1841 ~v1842 ~v1843 ~v1844 ~v1845 ~v1846 ~v1847 ~v1848 ~v1849 ~v185 ~v1850 ~v1851 ~v1852 ~v1853 ~v1854 ~v1855 ~v1856 ~v1857 ~v1858 ~v1859 ~v186 ~v1860 ~v1861 ~v1862 ~v1863 ~v1864 ~v1865 ~v1866 ~v1867 ~v1868 ~v1869 ~v187 ~v1870 ~v1871 ~v1872 ~v1873 ~v1874 ~v1875 ~v1876 ~v1877 ~v1878 ~v1879 ~v188 ~v1880 ~v1881 ~v1882 ~v1883 ~v1884 ~v1885 ~v1886 ~v189 ~v190 ~v1901 ~v1902 ~v1903 ~v1904 ~v1905 ~v1906 ~v1908 ~v1909 ~v191 ~v1910 ~v1911 ~v1912 ~v1913 ~v1914 ~v1915 ~v1916 ~v1917 ~v1918 ~v1919 ~v192 ~v1920 ~v1921 ~v1922 ~v1923 ~v1924 ~v1925 ~v1926 ~v1927 ~v1928 ~v1929 ~v193 ~v1930 ~v1931 ~v1932 ~v1934 ~v1935 ~v1936 ~v1938 ~v1939 ~v194 ~v1940 ~v1941 ~v1942 ~v1943 ~v1944 ~v1945 ~v1946 ~v1948 ~v1949 ~v195 ~v1950 ~v1951 ~v1952 ~v1953 ~v1954 ~v1955 ~v1956 ~v1957 ~v1958 ~v1959 ~v196 ~v1960 ~v1961 ~v1962 ~v1963 ~v1964 ~v1965 ~v1966 ~v1967 ~v1968 ~v1969 ~v197 ~v1970 ~v1971 ~v1972 ~v1973 ~v1974 ~v1975 ~v1976 ~v1977 ~v1978 ~v1979 ~v198 ~v1980 ~v1981 ~v1982 ~v1983 ~v1984 ~v1985 ~v1986 ~v1987 ~v1988 ~v1989 ~v199 ~v1990 ~v1991 ~v1992 ~v1993 ~v1994 ~v1995 ~v1996 ~v1997 ~v1998 ~v1999 ~v200 ~v2000 ~v2001 ~v2002 ~v2003 ~v2005 ~v2006 ~v2007 ~v2008 ~v2009 ~v201 ~v2010 ~v2011 ~v2012 ~v2013 ~v2014 ~v2015 ~v2016 ~v2017 ~v2019 ~v202 ~v2020 ~v2021 ~v2022 ~v2023 ~v2024 ~v2026 ~v2027 ~v2028 ~v203 ~v2030 ~v2031 ~v2032 ~v2033 ~v2034 ~v2035 ~v2036 ~v2037 ~v2038 ~v2039 ~v204 ~v2040 ~v2041 ~v2042 ~v2043 ~v2044 ~v2046 ~v2047 ~v2049 ~v205 ~v2050 ~v2051 ~v2053 ~v2054 ~v206 ~v2061 ~v2065 ~v2066 ~v207 ~v2074 ~v2076 ~v2079 ~v208 ~v2080 ~v2082 ~v2083 ~v2084 ~v2085 ~v2086 ~v2087 ~v2089 ~v209 ~v2090 ~v2094 ~v2097 ~v2098 ~v2099 ~v210 ~v2100 ~v2101 ~v2102 ~v2103 ~v2104 ~v2105 ~v2106 ~v2107 ~v2108 ~v2109 ~v211 ~v2110 ~v2112 ~v2119 ~v212 ~v2123 ~v2124 ~v2128 ~v2129 ~v213 ~v2130 ~v2131 ~v2138 ~v2139 ~v214 ~v2140 ~v2141 ~v2142 ~v2143 ~v2144 ~v2145 ~v2146 ~v2147 ~v2148 ~v2149 ~v215 ~v2151 ~v2153 ~v2155 ~v2156 ~v2157 ~v2158 ~v2159 ~v216 ~v2160 ~v2162 ~v2163 ~v2164 ~v2165 ~v2166 ~v2167 ~v2168 ~v2169 ~v217 ~v2170 ~v2171 ~v2174 ~v2175 ~v2176 ~v2177 ~v2178 ~v2179 ~v218 ~v2180 ~v2181 ~v2182 ~v2183 ~v2184 ~v2185 ~v2186 ~v2187 ~v2188 ~v2189 ~v219 ~v2190 ~v2191 ~v2192 ~v2193 ~v2194 ~v2195 ~v2196 ~v2197 ~v2198 ~v2199 ~v220 ~v2200 ~v2201 ~v2202 ~v2203 ~v2204 ~v2205 ~v2206 ~v2207 ~v2208 ~v2209 ~v221 ~v2210 ~v2211 ~v2212 ~v2213 ~v2214 ~v2215 ~v2216 ~v2217 ~v2218 ~v2219 ~v222 ~v2220 ~v2221 ~v2222 ~v2223 ~v2224 ~v2225 ~v2226 ~v2227 ~v2228 ~v223 ~v2231 ~v2232 ~v2233 ~v2234 ~v2235 ~v2236 v2237 ~v2238 ~v2239 ~v224 v2240 ~v2241 ~v2242 ~v2243 ~v2244 ~v2245 ~v2246 ~v2248 ~v225 ~v2250 ~v2251 ~v2252 ~v2253 ~v2256 ~v2257 ~v2258 ~v2259 ~v226 ~v2260 ~v2261 ~v2262 ~v2263 ~v2264 ~v2265 ~v2266 ~v2267 ~v2268 ~v227 ~v2270 ~v2271 ~v2272 ~v2273 ~v2278 ~v2279 ~v228 ~v2280 ~v2281 ~v2282 ~v2284 ~v2288 ~v229 ~v2292 ~v2293 ~v2298 ~v230 ~v2305 ~v2306 ~v2307 ~v2308 ~v2309 ~v231 ~v2310 ~v2311 ~v2312 ~v2313 ~v2315 ~v2317 ~v2318 ~v232 ~v2321 ~v2322 ~v2323 ~v2324 ~v2325 ~v2326 ~v2327 ~v2328 ~v233 ~v2330 ~v2331 ~v2334 ~v2335 ~v2336 ~v2337 ~v2338 ~v2339 ~v234 ~v2340 ~v2341 ~v2342 ~v2343 ~v2344 ~v2345 ~v2346 ~v2347 ~v2348 ~v2349 ~v235 ~v2350 ~v236 ~v2362 ~v2364 ~v2367 ~v2368 ~v2369 ~v237 ~v2371 ~v2372 ~v2373 ~v2374 ~v2375 ~v2376 ~v2377 ~v2378 ~v2379 ~v238 ~v2380 ~v2381 ~v2382 ~v2383 ~v2384 ~v2385 ~v2386 ~v2387 ~v2388 ~v2389 ~v239 ~v2390 ~v2391 ~v2392 ~v2394 ~v2395 ~v24 ~v240 ~v241 ~v242 ~v243 ~v2433 ~v2434 ~v244 ~v2444 ~v2445 ~v2446 ~v2447 ~v2448 ~v2449 ~v245 ~v2450 ~v2451 ~v2452 ~v2453 ~v2454 ~v2455 ~v2456 ~v2457 ~v2458 ~v2459 ~v246 ~v2460 ~v2461 ~v2462 ~v2463 ~v2464 ~v2465 ~v2466 ~v2467 ~v2468 ~v2469 ~v247 ~v2470 ~v2471 ~v2472 ~v2473 ~v2474 ~v2475 ~v2476 ~v2477 ~v2478 ~v2479 ~v248 ~v2480 ~v2481 ~v2482 ~v2483 ~v2484 ~v2485 ~v2486 ~v2487 ~v2488 ~v2489 ~v249 ~v2490 ~v2491 ~v2492 ~v2493 ~v2494 ~v2495 ~v2496 ~v2497 v2498 ~v2499 ~v25 ~v250 v2500 ~v2504 ~v2505 v2506 ~v2507 ~v2508 ~v2509 ~v251 ~v2510 ~v2511 ~v2512 ~v2513 ~v2514 ~v2515 ~v2516 ~v2517 ~v2518 ~v2519 ~v252 ~v2520 ~v2521 ~v2522 ~v2523 ~v2524 ~v2525 ~v2526 ~v2527 ~v2528 ~v2529 ~v253 ~v2530 ~v2531 ~v2532 ~v2533 ~v2534 ~v2535 ~v2536 ~v2537 ~v2538 ~v2539 ~v254 ~v2540 ~v2541 ~v2542 ~v2543 ~v2544 ~v2545 ~v2546 ~v2547 ~v2548 ~v2549 ~v255 ~v2550 ~v2551 ~v2552 ~v2553 ~v2554 ~v2555 ~v2556 ~v2557 ~v2558 ~v2559 ~v256 ~v2560 ~v2561 ~v2562 ~v2563 ~v2564 ~v2565 ~v2566 ~v2567 ~v2568 ~v2569 ~v257 ~v2570 ~v2571 ~v2572 ~v2573 ~v2574 ~v2575 ~v2576 ~v2577 ~v2578 ~v2579 ~v258 ~v2580 ~v2581 ~v2582 ~v2583 ~v2584 ~v2585 ~v2586 ~v2587 ~v2588 ~v2589 ~v259 ~v2590 ~v2591 ~v2592 ~v2593 ~v2594 ~v2596 v2597 ~v2598 ~v2599 ~v26 ~v260 ~v261 ~v2610 ~v2611 ~v2612 ~v2617 ~v2619 ~v262 ~v2622 ~v2627 ~v2628 ~v2629 ~v263 ~v2630 ~v265 ~v266 ~v2677 ~v2684 ~v2686 ~v269 ~v27 ~v270 ~v271 ~v272 ~v273 ~v274 ~v275 ~v276 ~v277 ~v278 ~v279 ~v28 ~v280 ~v281 ~v282 ~v283 ~v284 ~v285 ~v286 ~v287 ~v288 ~v289 ~v29 ~v290 ~v291 ~v295 ~v296 ~v297 ~v298 ~v299 ~v30 ~v300 ~v301 ~v302 ~v303 ~v304 ~v305 ~v306 ~v307 ~v308 ~v309 ~v31 ~v310 ~v311 ~v312 ~v313 ~v317 ~v32 ~v320 ~v321 ~v322 ~v323 ~v324 ~v325 ~v326 ~v327 ~v328 ~v329 ~v33 ~v330 ~v331 ~v332 ~v333 ~v334 ~v335 ~v336 ~v337 ~v338 ~v339 ~v34 ~v340 ~v341 ~v343 ~v346 ~v347 ~v348 ~v349 ~v35 ~v350 ~v351 ~v352 ~v353 ~v354 ~v355 ~v36 ~v362 ~v365 ~v37 ~v370 ~v371 ~v38 ~v40 ~v406 ~v41 ~v413 ~v417 ~v42 ~v420 ~v422 ~v423 ~v43 ~v431 ~v433 ~v435 ~v44 ~v45 ~v46 ~v461 ~v462 ~v466 ~v467 ~v468 ~v469 ~v47 ~v470 ~v472 ~v473 ~v474 ~v475 ~v476 ~v477 ~v48 ~v481 ~v483 ~v484 ~v485 ~v487 ~v488 ~v489 ~v49 ~v490 ~v491 ~v495 ~v50 ~v509 ~v51 ~v510 ~v52 ~v53 ~v54 ~v55 ~v56 ~v57 ~v577 ~v578 ~v579 ~v58 ~v580 ~v581 ~v582 ~v583 ~v584 ~v585 ~v586 ~v587 ~v588 ~v589 ~v59 ~v590 ~v591 ~v592 ~v595 ~v597 ~v6 ~v60 ~v600 ~v61 ~v610 ~v62 ~v624 ~v629 ~v64 ~v65 ~v66 ~v67 ~v677 ~v68 ~v680 ~v685 ~v689 ~v69 v697 ~v70 ~v71 ~v72 ~v726 ~v727 ~v73 ~v74 ~v75 ~v76 ~v760 ~v761 ~v762 ~v764 ~v766 ~v767 ~v768 ~v77 ~v773 ~v774 ~v775 ~v777 ~v778 ~v779 ~v78 ~v780 ~v783 ~v784 ~v785 ~v786 ~v787 ~v788 ~v79 ~v792 v798 ~v80 ~v804 ~v805 ~v806 ~v81 ~v813 ~v814 ~v815 ~v816 v817 ~v818 ~v819 ~v82 ~v820 ~v83 ~v831 ~v833 ~v834 ~v838 ~v839 ~v84 ~v840 ~v841 ~v842 ~v843 ~v844 ~v845 ~v846 ~v847 ~v848 ~v849 ~v85 v853 ~v857 ~v859 ~v86 ~v863 ~v867 ~v868 ~v869 ~v87 ~v870 ~v871 ~v872 ~v873 ~v874 ~v875 ~v877 ~v878 ~v879 ~v88 ~v880 ~v881 ~v882 ~v883 ~v884 ~v885 ~v887 ~v888 ~v889 ~v89 ~v890 ~v892 ~v893 ~v894 ~v895 ~v899 ~v90 ~v900 ~v901 ~v902 ~v903 ~v904 ~v905 ~v906 ~v907 ~v908 ~v909 ~v91 ~v910 ~v911 ~v912 ~v913 ~v914 ~v915 ~v916 ~v917 ~v918 ~v919 ~v92 ~v920 ~v921 ~v922 ~v923 ~v924 ~v925 ~v926 ~v927 ~v928 ~v929 ~v93 ~v930 ~v931 ~v932 ~v933 ~v934 ~v935 ~v936 ~v937 ~v938 ~v939 ~v94 ~v940 ~v941 ~v942 ~v943 ~v944 ~v945 ~v946 ~v947 ~v948 ~v949 ~v950 ~v951 ~v952 ~v953 ~v954 ~v955 ~v956 ~v957 ~v958 ~v959 ~v96 ~v960 ~v961 ~v962 ~v963 ~v964 ~v97 ~v975 ~v98 ~v983 ~v985 ~v986 ~v988 ~v989 ~v99 ~v991 ~v992 ~v993 ~v994 ~v995 ~v996 ~v997 ~v998 ~v999 +~v0 ~v10 ~v100 ~v1000 ~v1001 ~v1002 ~v1003 ~v1004 ~v1005 ~v1006 ~v1007 ~v1008 ~v1009 ~v101 ~v1010 ~v1011 ~v1012 ~v1013 ~v1014 ~v1015 ~v1016 ~v1017 ~v1018 ~v1019 ~v102 ~v1020 ~v1021 ~v1022 ~v1023 ~v1024 ~v1025 ~v1026 ~v1027 ~v1028 ~v1029 ~v103 ~v1030 ~v1031 ~v1032 ~v1033 ~v1034 ~v1035 ~v1036 ~v1037 ~v1038 ~v1039 ~v104 ~v1040 ~v1041 ~v1042 ~v1043 ~v1044 ~v1045 ~v1046 ~v1047 ~v1048 ~v1049 ~v105 ~v1050 ~v1051 ~v1052 ~v1053 ~v1054 ~v1055 ~v1056 ~v1057 ~v1058 ~v1059 ~v106 ~v1060 ~v1061 ~v1062 ~v1063 ~v1064 ~v1065 ~v1066 ~v1067 ~v1068 ~v1069 ~v107 ~v1070 ~v1071 ~v1072 ~v1073 ~v1074 ~v1075 ~v1076 ~v1077 ~v1079 ~v108 ~v1080 ~v1081 ~v1082 ~v1083 ~v1084 ~v1085 ~v1086 ~v1087 ~v1088 ~v1089 ~v109 ~v1090 ~v1091 ~v1092 ~v1093 ~v1094 ~v1095 ~v1096 ~v1097 ~v1098 ~v1099 ~v110 ~v1100 ~v1101 ~v1102 ~v1103 ~v1104 ~v1105 ~v1106 ~v1107 ~v1108 ~v1109 ~v111 ~v1110 ~v1111 ~v1112 ~v1113 ~v1114 ~v1115 ~v1116 ~v1117 ~v1118 ~v1119 ~v112 ~v1120 ~v1121 ~v1122 ~v1123 ~v1124 ~v1125 ~v1126 ~v1127 ~v1128 ~v1129 ~v113 ~v1130 ~v1131 ~v1132 ~v1133 ~v1134 ~v1135 ~v1136 ~v1137 ~v1138 ~v1139 ~v114 ~v1140 ~v1141 ~v1142 ~v1143 ~v1144 ~v1145 ~v1146 ~v1147 ~v1148 ~v1149 ~v115 ~v1150 ~v1151 ~v1152 ~v1153 ~v1154 ~v1155 ~v1156 ~v1157 ~v1158 ~v1159 ~v116 ~v1160 ~v1161 ~v1162 ~v1163 ~v1164 ~v1165 ~v1166 ~v1167 ~v1169 ~v117 ~v1170 ~v1171 ~v1172 ~v1173 ~v1174 ~v1175 ~v1176 ~v1177 ~v1178 ~v1179 ~v118 ~v1180 ~v1181 ~v1182 ~v1183 ~v1184 ~v1185 ~v1186 ~v1187 ~v1188 ~v1189 ~v119 ~v1190 ~v1191 ~v1192 ~v1193 ~v1194 ~v1195 ~v1196 ~v1197 ~v1198 ~v1199 ~v120 ~v1201 ~v1202 ~v1204 ~v1205 ~v1206 ~v1207 ~v1208 ~v121 ~v1210 ~v1211 ~v1212 ~v1213 ~v1214 ~v1215 ~v1216 ~v1217 ~v1218 ~v1219 ~v122 ~v1220 ~v1221 ~v1222 ~v1223 ~v1224 ~v1225 ~v1226 ~v1227 ~v1228 ~v1229 ~v123 ~v1231 ~v1233 ~v1234 ~v1235 ~v1236 ~v1237 ~v1238 ~v1239 ~v124 ~v1240 ~v1241 ~v1242 ~v1243 ~v1244 ~v1245 ~v1246 ~v1247 ~v1248 ~v1249 ~v125 ~v1250 ~v1251 ~v1252 ~v1253 ~v1254 ~v1255 ~v1256 ~v1257 ~v1258 ~v1259 ~v126 ~v1260 ~v1261 ~v1262 ~v1263 ~v1264 ~v1265 ~v1266 ~v1267 ~v1268 ~v1269 ~v127 ~v1270 ~v1271 ~v1272 ~v1273 ~v1274 ~v1275 ~v1276 ~v1277 ~v1278 ~v1279 ~v128 ~v1280 ~v1281 ~v1282 ~v1283 ~v1284 ~v1285 ~v1286 ~v1287 ~v1288 ~v1289 ~v129 ~v1290 ~v1291 ~v1292 ~v1293 ~v1294 ~v1295 ~v1296 ~v1297 ~v1298 ~v1299 ~v1300 ~v1301 ~v1302 ~v1303 ~v1304 ~v1305 ~v1306 ~v1307 ~v1308 ~v1309 ~v1310 ~v1311 ~v1312 ~v1313 ~v1314 ~v1315 ~v1316 ~v1317 ~v1318 ~v1319 ~v1320 ~v1321 ~v1322 ~v1323 ~v1324 ~v1325 ~v1326 ~v1327 ~v1328 ~v1329 ~v133 ~v1330 ~v1331 ~v1332 ~v1333 ~v1334 ~v1335 ~v1336 ~v1337 ~v1338 ~v1339 ~v1340 ~v1341 ~v1342 ~v1343 ~v1344 ~v1345 ~v1346 ~v1347 ~v1348 ~v1349 ~v135 ~v1351 ~v1352 ~v1353 ~v1357 ~v1358 ~v1359 ~v136 ~v1360 ~v1367 ~v1368 ~v137 ~v138 ~v1382 ~v1383 ~v139 ~v1395 ~v1397 ~v1398 ~v140 ~v1403 ~v1406 ~v1407 ~v1410 ~v1411 ~v1412 ~v1413 v1414 ~v1415 ~v1416 ~v1417 ~v1418 ~v1419 ~v142 ~v1420 ~v1422 ~v1425 ~v1426 ~v1427 ~v1428 ~v1429 ~v143 ~v1430 ~v1431 ~v1432 ~v1433 ~v1434 ~v1435 ~v1436 ~v1437 ~v1438 ~v1439 ~v144 ~v1440 ~v1441 ~v1442 ~v1443 ~v1444 ~v1445 ~v1446 ~v145 ~v1456 ~v146 ~v1462 ~v1463 ~v1465 ~v147 ~v1478 ~v1479 ~v148 ~v1480 ~v1488 ~v1489 ~v149 ~v1490 ~v1491 ~v1492 ~v1497 ~v1498 ~v1499 ~v150 ~v1500 ~v1501 ~v1502 ~v1503 ~v1504 ~v1505 ~v1506 ~v1508 ~v151 ~v1513 ~v1514 ~v1516 ~v1517 ~v1518 ~v1519 ~v152 ~v1520 ~v1521 ~v1522 ~v1523 ~v1524 ~v1525 ~v1526 ~v1527 ~v1528 ~v1529 ~v153 ~v1530 ~v1531 ~v1532 ~v1534 ~v1535 ~v1536 ~v1537 ~v1538 ~v154 ~v1542 ~v1543 ~v1544 ~v1545 ~v1548 ~v155 ~v1552 ~v1557 ~v1558 ~v156 ~v1561 ~v1564 ~v1566 ~v1569 ~v157 ~v1573 ~v1574 ~v1575 ~v1576 ~v1577 ~v1578 ~v1579 ~v158 ~v1580 ~v1581 ~v1582 ~v1583 ~v1584 ~v1585 ~v1586 ~v1587 ~v1588 ~v1589 ~v159 ~v1590 ~v1591 ~v1592 ~v1594 ~v1595 ~v1596 ~v1597 ~v1598 ~v1599 ~v160 ~v1600 ~v1601 ~v1602 ~v1603 ~v1608 ~v1609 ~v161 ~v1610 ~v1611 ~v1612 ~v1616 ~v1617 ~v1618 ~v1619 ~v162 ~v1621 ~v1623 ~v1626 ~v1627 ~v163 ~v1630 ~v1631 ~v1632 ~v1633 ~v1634 ~v1635 ~v1637 ~v1638 ~v164 ~v1641 ~v1642 ~v1643 ~v1646 ~v165 ~v1651 ~v1652 ~v1653 ~v166 ~v1661 ~v1667 ~v167 ~v1670 ~v1671 ~v1672 ~v1673 ~v1674 ~v1675 ~v1676 ~v1677 ~v1678 ~v1679 ~v168 ~v1680 ~v1681 ~v1682 ~v1683 ~v1684 ~v1685 ~v1686 ~v1687 ~v1688 ~v1689 ~v169 ~v1690 ~v1691 ~v1692 ~v1693 ~v1694 ~v1695 ~v1696 ~v1697 ~v1698 ~v1699 ~v170 ~v1700 ~v1701 ~v1702 ~v1703 ~v1704 ~v1705 ~v1706 ~v1707 ~v1708 ~v1709 ~v171 ~v1710 ~v1711 ~v1712 ~v1713 ~v1714 ~v1715 ~v1716 ~v1717 ~v1718 ~v1719 ~v172 ~v1720 ~v1721 ~v1722 ~v1723 ~v1724 ~v1725 ~v1726 ~v1727 ~v1728 ~v1729 ~v173 ~v1730 ~v1731 ~v1732 ~v1733 ~v1734 ~v1735 ~v1736 ~v1737 ~v1738 ~v1739 ~v174 ~v1740 ~v1741 ~v1742 ~v1743 ~v1744 ~v1745 ~v1746 ~v1747 ~v1748 ~v1749 ~v175 ~v1750 ~v1751 ~v1752 ~v1753 ~v1754 ~v1755 ~v1756 ~v1757 ~v1758 ~v1759 ~v176 ~v1760 ~v1761 ~v1762 ~v1763 ~v1764 ~v1765 ~v1766 ~v1767 ~v1768 ~v1769 ~v177 ~v1770 ~v1771 ~v1772 ~v1773 ~v1774 ~v1775 ~v1776 ~v1777 ~v1778 ~v1779 ~v1780 ~v1781 ~v1782 ~v1783 ~v1784 ~v1785 ~v1786 ~v1787 ~v1788 ~v1789 ~v1790 ~v1791 ~v1792 ~v1793 ~v1794 ~v1795 ~v1796 ~v1797 ~v1798 ~v1799 ~v180 ~v1800 ~v1801 ~v1802 ~v1803 ~v1804 ~v1805 ~v1806 ~v1807 ~v1808 ~v1809 ~v181 ~v1810 ~v1811 ~v1812 ~v1813 ~v1814 ~v1815 ~v1816 ~v1817 ~v1818 ~v1819 ~v182 ~v1820 ~v1821 ~v1822 ~v1823 ~v1824 ~v1825 ~v1826 ~v1827 ~v1828 ~v1829 ~v183 ~v1830 ~v1831 ~v1832 ~v1833 ~v1834 ~v1835 ~v1836 ~v1837 ~v1838 ~v1839 ~v184 ~v1840 ~v1841 ~v1842 ~v1843 ~v1844 ~v1845 ~v1846 ~v1847 ~v1848 ~v1849 ~v185 ~v1850 ~v1851 ~v1852 ~v1853 ~v1854 ~v1855 ~v1856 ~v1857 ~v1858 ~v1859 ~v186 ~v1860 ~v1861 ~v1862 ~v1863 ~v1864 ~v1865 ~v1866 ~v1867 ~v1868 ~v1869 ~v187 ~v1870 ~v1871 ~v1872 ~v1873 ~v1874 ~v1875 ~v1876 ~v1877 ~v1878 ~v1879 ~v188 ~v1880 ~v1881 ~v1882 ~v1883 ~v1884 ~v1885 ~v1886 ~v189 ~v190 ~v1901 ~v1902 ~v1903 ~v1904 ~v1905 ~v1906 ~v1908 ~v1909 ~v191 ~v1910 ~v1911 ~v1912 ~v1913 ~v1914 ~v1915 ~v1916 ~v1917 ~v1918 ~v1919 ~v192 ~v1920 ~v1921 ~v1922 ~v1923 ~v1924 ~v1925 ~v1926 ~v1927 ~v1928 ~v1929 ~v193 ~v1930 ~v1931 ~v1932 ~v1934 ~v1935 ~v1936 ~v1938 ~v1939 ~v194 ~v1940 ~v1941 ~v1942 ~v1943 ~v1944 ~v1945 ~v1946 ~v1948 ~v1949 ~v195 ~v1950 ~v1951 ~v1952 ~v1953 ~v1954 ~v1955 ~v1956 ~v1957 ~v1958 ~v1959 ~v196 ~v1960 ~v1961 ~v1962 ~v1963 ~v1964 ~v1965 ~v1966 ~v1967 ~v1968 ~v1969 ~v197 ~v1970 ~v1971 ~v1972 ~v1973 ~v1974 ~v1975 ~v1976 ~v1977 ~v1978 ~v1979 ~v198 ~v1980 ~v1981 ~v1982 ~v1983 ~v1984 ~v1985 ~v1986 ~v1987 ~v1988 ~v1989 ~v199 ~v1990 ~v1991 ~v1992 ~v1993 ~v1994 ~v1995 ~v1996 ~v1997 ~v1998 ~v1999 ~v200 ~v2000 ~v2001 ~v2002 ~v2003 ~v2005 ~v2006 ~v2007 ~v2008 ~v2009 ~v201 ~v2010 ~v2011 ~v2012 ~v2013 ~v2014 ~v2015 ~v2016 ~v2017 ~v2019 ~v202 ~v2020 ~v2021 ~v2022 ~v2023 ~v2024 ~v2026 ~v2027 ~v2028 ~v203 ~v2030 ~v2031 ~v2032 ~v2033 ~v2034 ~v2035 ~v2036 ~v2037 ~v2038 ~v2039 ~v204 ~v2040 ~v2041 ~v2042 ~v2043 ~v2044 ~v2046 ~v2047 ~v2049 ~v205 ~v2050 ~v2051 ~v2053 ~v2054 ~v206 ~v2061 ~v2065 ~v2066 ~v207 ~v2074 ~v2076 ~v2079 ~v208 ~v2080 ~v2082 ~v2083 ~v2084 ~v2085 ~v2086 ~v2087 ~v2089 ~v209 ~v2090 ~v2094 ~v2097 ~v2098 ~v2099 ~v210 ~v2100 ~v2101 ~v2102 ~v2103 ~v2104 ~v2105 ~v2106 ~v2107 ~v2108 ~v2109 ~v211 ~v2110 ~v2112 ~v2119 ~v212 ~v2123 ~v2124 ~v2128 ~v2129 ~v213 ~v2130 ~v2131 ~v2138 ~v2139 ~v214 ~v2140 ~v2141 ~v2142 ~v2143 ~v2144 ~v2145 ~v2146 ~v2147 ~v2148 ~v2149 ~v215 ~v2151 ~v2153 ~v2155 ~v2156 ~v2157 ~v2158 ~v2159 ~v216 ~v2160 ~v2162 ~v2163 ~v2164 ~v2165 ~v2166 ~v2167 ~v2168 ~v2169 ~v217 ~v2170 ~v2171 ~v2174 ~v2175 ~v2176 ~v2177 ~v2178 ~v2179 ~v218 ~v2180 ~v2181 ~v2182 ~v2183 ~v2184 ~v2185 ~v2186 ~v2187 ~v2188 ~v2189 ~v219 ~v2190 ~v2191 ~v2192 ~v2193 ~v2194 ~v2195 ~v2196 ~v2197 ~v2198 ~v2199 ~v220 ~v2200 ~v2201 ~v2202 ~v2203 ~v2204 ~v2205 ~v2206 ~v2207 ~v2208 ~v2209 ~v221 ~v2210 ~v2211 ~v2212 ~v2213 ~v2214 ~v2215 ~v2216 ~v2217 ~v2218 ~v2219 ~v222 ~v2220 ~v2221 ~v2222 ~v2223 ~v2224 ~v2225 ~v2226 ~v2227 ~v2228 ~v223 ~v2231 ~v2232 ~v2233 ~v2234 ~v2235 ~v2236 v2237 ~v2238 ~v2239 ~v224 v2240 ~v2241 ~v2242 ~v2243 ~v2244 ~v2245 ~v2246 ~v2248 ~v225 ~v2250 ~v2251 ~v2252 ~v2253 ~v2256 ~v2257 ~v2258 ~v2259 ~v226 ~v2260 ~v2261 ~v2262 ~v2263 ~v2264 ~v2265 ~v2266 ~v2267 ~v2268 ~v227 ~v2270 ~v2271 ~v2272 ~v2273 ~v2278 ~v2279 ~v228 ~v2280 ~v2281 ~v2282 ~v2284 ~v2288 ~v229 ~v2292 ~v2293 ~v2298 ~v230 ~v2305 ~v2306 ~v2307 ~v2308 ~v2309 ~v231 ~v2310 ~v2311 ~v2312 ~v2313 ~v2315 ~v2317 ~v2318 ~v232 ~v2321 ~v2322 ~v2323 ~v2324 ~v2325 ~v2326 ~v2327 ~v2328 ~v233 ~v2330 ~v2331 ~v2334 ~v2335 ~v2336 ~v2337 ~v2338 ~v2339 ~v234 ~v2340 ~v2341 ~v2342 ~v2343 ~v2344 ~v2345 ~v2346 ~v2347 ~v2348 ~v2349 ~v235 ~v2350 ~v236 ~v2362 ~v2364 ~v2367 ~v2368 ~v2369 ~v237 ~v2371 ~v2372 ~v2373 ~v2374 ~v2375 ~v2376 ~v2377 ~v2378 ~v2379 ~v238 ~v2380 ~v2381 ~v2382 ~v2383 ~v2384 ~v2385 ~v2386 ~v2387 ~v2388 ~v2389 ~v239 ~v2390 ~v2391 ~v2392 ~v2394 ~v2395 ~v24 ~v240 ~v241 ~v242 ~v243 ~v2433 ~v2434 ~v244 ~v2444 ~v2445 ~v2446 ~v2447 ~v2448 ~v2449 ~v245 ~v2450 ~v2451 ~v2452 ~v2453 ~v2454 ~v2455 ~v2456 ~v2457 ~v2458 ~v2459 ~v246 ~v2460 ~v2461 ~v2462 ~v2463 ~v2464 ~v2465 ~v2466 ~v2467 ~v2468 ~v2469 ~v247 ~v2470 ~v2471 ~v2472 ~v2473 ~v2474 ~v2475 ~v2476 ~v2477 ~v2478 ~v2479 ~v248 ~v2480 ~v2481 ~v2482 ~v2483 ~v2484 ~v2485 ~v2486 ~v2487 ~v2488 ~v2489 ~v249 ~v2490 ~v2491 ~v2492 ~v2493 ~v2494 ~v2495 ~v2496 ~v2497 v2498 ~v2499 ~v25 ~v250 v2500 ~v2504 ~v2505 v2506 ~v2507 ~v2508 ~v2509 ~v251 ~v2510 ~v2511 ~v2512 ~v2513 ~v2514 ~v2515 ~v2516 ~v2517 ~v2518 ~v2519 ~v252 ~v2520 ~v2521 ~v2522 ~v2523 ~v2524 ~v2525 ~v2526 ~v2527 ~v2528 ~v2529 ~v253 ~v2530 ~v2531 ~v2532 ~v2533 ~v2534 ~v2535 ~v2536 ~v2537 ~v2538 ~v2539 ~v254 ~v2540 ~v2541 ~v2542 ~v2543 ~v2544 ~v2545 ~v2546 ~v2547 ~v2548 ~v2549 ~v255 ~v2550 ~v2551 ~v2552 ~v2553 ~v2554 ~v2555 ~v2556 ~v2557 ~v2558 ~v2559 ~v256 ~v2560 ~v2561 ~v2562 ~v2563 ~v2564 ~v2565 ~v2566 ~v2567 ~v2568 ~v2569 ~v257 ~v2570 ~v2571 ~v2572 ~v2573 ~v2574 ~v2575 ~v2576 ~v2577 ~v2578 ~v2579 ~v258 ~v2580 ~v2581 ~v2582 ~v2583 ~v2584 ~v2585 ~v2586 ~v2587 ~v2588 ~v2589 ~v259 ~v2590 ~v2591 ~v2592 ~v2593 ~v2594 ~v2596 v2597 ~v2598 ~v2599 ~v26 ~v260 ~v2600 ~v2601 ~v2602 ~v2603 ~v2604 ~v2605 ~v2606 ~v2607 ~v2608 v2609 ~v261 ~v2610 ~v2611 ~v2612 ~v2617 ~v2619 ~v262 ~v2622 ~v2627 ~v2628 ~v2629 ~v263 ~v2630 ~v265 ~v266 ~v2677 ~v2684 ~v2686 ~v269 ~v27 ~v270 ~v271 ~v272 ~v273 ~v274 ~v275 ~v276 ~v277 ~v278 ~v279 ~v28 ~v280 ~v281 ~v282 ~v283 ~v284 ~v285 ~v286 ~v287 ~v288 ~v289 ~v29 ~v290 ~v291 ~v295 ~v296 ~v297 ~v298 ~v299 ~v30 ~v300 ~v301 ~v302 ~v303 ~v304 ~v305 ~v306 ~v307 ~v308 ~v309 ~v31 ~v310 ~v311 ~v312 ~v313 ~v314 ~v317 ~v32 ~v320 ~v321 ~v322 ~v323 ~v324 ~v325 ~v326 ~v327 ~v328 ~v329 ~v33 ~v330 ~v331 ~v332 ~v333 ~v334 ~v335 ~v336 ~v337 ~v338 ~v339 ~v34 ~v340 ~v341 ~v343 ~v346 ~v347 ~v348 ~v349 ~v35 ~v350 ~v351 ~v352 ~v353 ~v354 ~v355 ~v36 ~v362 ~v365 ~v37 ~v370 ~v371 ~v378 ~v38 ~v386 ~v387 ~v390 ~v392 ~v393 ~v395 ~v396 ~v397 ~v398 ~v40 ~v406 ~v41 ~v413 ~v417 ~v42 ~v420 ~v422 ~v423 ~v43 ~v431 ~v433 ~v435 ~v44 ~v45 ~v46 ~v461 ~v462 ~v464 ~v466 ~v467 ~v468 ~v469 ~v47 ~v470 ~v471 ~v472 ~v473 ~v474 ~v475 ~v476 ~v477 ~v478 ~v48 ~v481 ~v483 ~v484 ~v485 ~v487 ~v488 ~v489 ~v49 ~v490 ~v491 ~v495 ~v50 ~v509 ~v51 ~v510 ~v515 ~v517 ~v52 ~v53 ~v54 ~v55 ~v56 ~v562 ~v564 ~v57 ~v570 ~v577 ~v578 ~v579 ~v58 ~v580 ~v581 ~v582 ~v583 ~v584 ~v585 ~v586 ~v587 ~v588 ~v589 ~v59 ~v590 ~v591 ~v592 ~v595 ~v597 ~v6 ~v60 ~v600 ~v61 ~v610 ~v62 ~v624 ~v629 ~v64 ~v65 ~v656 ~v658 ~v66 ~v660 ~v67 ~v677 ~v68 ~v680 ~v685 ~v689 ~v69 v697 ~v70 ~v71 ~v72 ~v726 ~v727 ~v73 ~v74 ~v75 ~v753 ~v755 ~v756 ~v76 ~v760 ~v761 ~v762 ~v764 ~v765 ~v766 ~v767 ~v768 ~v77 ~v773 ~v774 ~v775 ~v777 ~v778 ~v779 ~v78 ~v780 ~v783 ~v784 ~v785 ~v786 ~v787 ~v788 ~v79 ~v792 v798 ~v80 ~v804 ~v805 ~v806 ~v81 ~v813 ~v814 ~v815 ~v816 v817 ~v818 ~v819 ~v82 ~v820 ~v826 ~v83 ~v831 ~v833 ~v834 ~v838 ~v839 ~v84 ~v840 ~v841 ~v842 ~v843 ~v844 ~v845 ~v846 ~v847 ~v848 ~v849 ~v85 v853 ~v857 ~v859 ~v86 ~v863 ~v867 ~v868 ~v869 ~v87 ~v870 ~v871 ~v872 ~v873 ~v874 ~v875 ~v877 ~v878 ~v879 ~v88 ~v880 ~v881 ~v882 ~v883 ~v884 ~v885 ~v887 ~v888 ~v889 ~v89 ~v890 ~v892 ~v893 ~v894 ~v895 ~v899 ~v90 ~v900 ~v901 ~v902 ~v903 ~v904 ~v905 ~v906 ~v907 ~v908 ~v909 ~v91 ~v910 ~v911 ~v912 ~v913 ~v914 ~v915 ~v916 ~v917 ~v918 ~v919 ~v92 ~v920 ~v921 ~v922 ~v923 ~v924 ~v925 ~v926 ~v927 ~v928 ~v929 ~v93 ~v930 ~v931 ~v932 ~v933 ~v934 ~v935 ~v936 ~v937 ~v938 ~v939 ~v94 ~v940 ~v941 ~v942 ~v943 ~v944 ~v945 ~v946 ~v947 ~v948 ~v949 ~v950 ~v951 ~v952 ~v953 ~v954 ~v955 ~v956 ~v957 ~v958 ~v959 ~v96 ~v960 ~v961 ~v962 ~v963 ~v964 ~v97 ~v975 ~v98 ~v983 ~v985 ~v986 ~v988 ~v989 ~v99 ~v991 ~v992 ~v993 ~v994 ~v995 ~v996 ~v997 ~v998 ~v999 +~v0 ~v10 ~v100 ~v1000 ~v1001 ~v1002 ~v1003 ~v1004 ~v1005 ~v1006 ~v1007 ~v1008 ~v1009 ~v101 ~v1010 ~v1011 ~v1012 ~v1013 ~v1014 ~v1015 ~v1016 ~v1017 ~v1018 ~v1019 ~v102 ~v1020 ~v1021 ~v1022 ~v1023 ~v1024 ~v1025 ~v1026 ~v1027 ~v1028 ~v1029 ~v103 ~v1030 ~v1031 ~v1032 ~v1033 ~v1034 ~v1035 ~v1036 ~v1037 ~v1038 ~v1039 ~v104 ~v1040 ~v1041 ~v1042 ~v1043 ~v1044 ~v1045 ~v1046 ~v1047 ~v1048 ~v1049 ~v105 ~v1050 ~v1051 ~v1052 ~v1053 ~v1054 ~v1055 ~v1056 ~v1057 ~v1058 ~v1059 ~v106 ~v1060 ~v1061 ~v1062 ~v1063 ~v1064 ~v1065 ~v1066 ~v1067 ~v1068 ~v1069 ~v107 ~v1070 ~v1071 ~v1072 ~v1073 ~v1074 ~v1075 ~v1076 ~v1077 ~v1079 ~v108 ~v1080 ~v1081 ~v1082 ~v1083 ~v1084 ~v1085 ~v1086 ~v1087 ~v1088 ~v1089 ~v109 ~v1090 ~v1091 ~v1092 ~v1093 ~v1094 ~v1095 ~v1096 ~v1097 ~v1098 ~v1099 ~v110 ~v1100 ~v1101 ~v1102 ~v1103 ~v1104 ~v1105 ~v1106 ~v1107 ~v1108 ~v1109 ~v111 ~v1110 ~v1111 ~v1112 ~v1113 ~v1114 ~v1115 ~v1116 ~v1117 ~v1118 ~v1119 ~v112 ~v1120 ~v1121 ~v1122 ~v1123 ~v1124 ~v1125 ~v1126 ~v1127 ~v1128 ~v1129 ~v113 ~v1130 ~v1131 ~v1132 ~v1133 ~v1134 ~v1135 ~v1136 ~v1137 ~v1138 ~v1139 ~v114 ~v1140 ~v1141 ~v1142 ~v1143 ~v1144 ~v1145 ~v1146 ~v1147 ~v1148 ~v1149 ~v115 ~v1150 ~v1151 ~v1152 ~v1153 ~v1154 ~v1155 ~v1156 ~v1157 ~v1158 ~v1159 ~v116 ~v1160 ~v1161 ~v1162 ~v1163 ~v1164 ~v1165 ~v1166 ~v1167 ~v1169 ~v117 ~v1170 ~v1171 ~v1172 ~v1173 ~v1174 ~v1175 ~v1176 ~v1177 ~v1178 ~v1179 ~v118 ~v1180 ~v1181 ~v1182 ~v1183 ~v1184 ~v1185 ~v1186 ~v1187 ~v1188 ~v1189 ~v119 ~v1190 ~v1191 ~v1192 ~v1193 ~v1194 ~v1195 ~v1196 ~v1197 ~v1198 ~v1199 ~v120 ~v1201 ~v1202 ~v1204 ~v1205 ~v1206 ~v1207 ~v1208 ~v121 ~v1210 ~v1211 ~v1212 ~v1213 ~v1214 ~v1215 ~v1216 ~v1217 ~v1218 ~v1219 ~v122 ~v1220 ~v1221 ~v1222 ~v1223 ~v1224 ~v1225 ~v1226 ~v1227 ~v1228 ~v1229 ~v123 ~v1231 ~v1233 ~v1234 ~v1235 ~v1236 ~v1237 ~v1238 ~v1239 ~v124 ~v1240 ~v1241 ~v1242 ~v1243 ~v1244 ~v1245 ~v1246 ~v1247 ~v1248 ~v1249 ~v125 ~v1250 ~v1251 ~v1252 ~v1253 ~v1254 ~v1255 ~v1256 ~v1257 ~v1258 ~v1259 ~v126 ~v1260 ~v1261 ~v1262 ~v1263 ~v1264 ~v1265 ~v1266 ~v1267 ~v1268 ~v1269 ~v127 ~v1270 ~v1271 ~v1272 ~v1273 ~v1274 ~v1275 ~v1276 ~v1277 ~v1278 ~v1279 ~v128 ~v1280 ~v1281 ~v1282 ~v1283 ~v1284 ~v1285 ~v1286 ~v1287 ~v1288 ~v1289 ~v129 ~v1290 ~v1291 ~v1292 ~v1293 ~v1294 ~v1295 ~v1296 ~v1297 ~v1298 ~v1299 ~v1300 ~v1301 ~v1302 ~v1303 ~v1304 ~v1305 ~v1306 ~v1307 ~v1308 ~v1309 ~v1310 ~v1311 ~v1312 ~v1313 ~v1314 ~v1315 ~v1316 ~v1317 ~v1318 ~v1319 ~v1320 ~v1321 ~v1322 ~v1323 ~v1324 ~v1325 ~v1326 ~v1327 ~v1328 ~v1329 ~v133 ~v1330 ~v1331 ~v1332 ~v1333 ~v1334 ~v1335 ~v1336 ~v1337 ~v1338 ~v1339 ~v1340 ~v1341 ~v1342 ~v1343 ~v1344 ~v1345 ~v1346 ~v1347 ~v1348 ~v1349 ~v135 ~v1351 ~v1352 ~v1353 ~v1357 ~v1358 ~v1359 ~v136 ~v1360 ~v1367 ~v1368 ~v137 ~v138 ~v1382 ~v1383 ~v139 ~v1395 ~v1397 ~v1398 ~v140 ~v1403 ~v1406 ~v1407 ~v1410 ~v1411 ~v1412 ~v1413 v1414 ~v1415 ~v1416 ~v1417 ~v1418 ~v1419 ~v142 ~v1420 ~v1422 ~v1425 ~v1426 ~v1427 ~v1428 ~v1429 ~v143 ~v1430 ~v1431 ~v1432 ~v1433 ~v1434 ~v1435 ~v1436 ~v1437 ~v1438 ~v1439 ~v144 ~v1440 ~v1441 ~v1442 ~v1443 ~v1444 ~v1445 ~v1446 ~v145 ~v1456 ~v146 ~v1462 ~v1463 ~v1465 ~v147 ~v1478 ~v1479 ~v148 ~v1480 ~v1488 ~v1489 ~v149 ~v1490 ~v1491 ~v1492 ~v1497 ~v1498 ~v1499 ~v150 ~v1500 ~v1501 ~v1502 ~v1503 ~v1504 ~v1505 ~v1506 ~v1508 ~v151 ~v1513 ~v1514 ~v1516 ~v1517 ~v1518 ~v1519 ~v152 ~v1520 ~v1521 ~v1522 ~v1523 ~v1524 ~v1525 ~v1526 ~v1527 ~v1528 ~v1529 ~v153 ~v1530 ~v1531 ~v1532 ~v1534 ~v1535 ~v1536 ~v1537 ~v1538 ~v154 ~v1542 ~v1543 ~v1544 ~v1545 ~v1548 ~v155 ~v1552 ~v1557 ~v1558 ~v156 ~v1561 ~v1564 ~v1566 ~v1569 ~v157 ~v1573 ~v1574 ~v1575 ~v1576 ~v1577 ~v1578 ~v1579 ~v158 ~v1580 ~v1581 ~v1582 ~v1583 ~v1584 ~v1585 ~v1586 ~v1587 ~v1588 ~v1589 ~v159 ~v1590 ~v1591 ~v1592 ~v1594 ~v1595 ~v1596 ~v1597 ~v1598 ~v1599 ~v160 ~v1600 ~v1601 ~v1602 ~v1603 ~v1608 ~v1609 ~v161 ~v1610 ~v1611 ~v1612 ~v1616 ~v1617 ~v1618 ~v1619 ~v162 ~v1621 ~v1623 ~v1626 ~v1627 ~v163 ~v1630 ~v1631 ~v1632 ~v1633 ~v1634 ~v1635 ~v1637 ~v1638 ~v164 ~v1641 ~v1642 ~v1643 ~v1646 ~v165 ~v1651 ~v1652 ~v1653 ~v166 ~v1661 ~v1667 ~v167 ~v1670 ~v1671 ~v1672 ~v1673 ~v1674 ~v1675 ~v1676 ~v1677 ~v1678 ~v1679 ~v168 ~v1680 ~v1681 ~v1682 ~v1683 ~v1684 ~v1685 ~v1686 ~v1687 ~v1688 ~v1689 ~v169 ~v1690 ~v1691 ~v1692 ~v1693 ~v1694 ~v1695 ~v1696 ~v1697 ~v1698 ~v1699 ~v170 ~v1700 ~v1701 ~v1702 ~v1703 ~v1704 ~v1705 ~v1706 ~v1707 ~v1708 ~v1709 ~v171 ~v1710 ~v1711 ~v1712 ~v1713 ~v1714 ~v1715 ~v1716 ~v1717 ~v1718 ~v1719 ~v172 ~v1720 ~v1721 ~v1722 ~v1723 ~v1724 ~v1725 ~v1726 ~v1727 ~v1728 ~v1729 ~v173 ~v1730 ~v1731 ~v1732 ~v1733 ~v1734 ~v1735 ~v1736 ~v1737 ~v1738 ~v1739 ~v174 ~v1740 ~v1741 ~v1742 ~v1743 ~v1744 ~v1745 ~v1746 ~v1747 ~v1748 ~v1749 ~v175 ~v1750 ~v1751 ~v1752 ~v1753 ~v1754 ~v1755 ~v1756 ~v1757 ~v1758 ~v1759 ~v176 ~v1760 ~v1761 ~v1762 ~v1763 ~v1764 ~v1765 ~v1766 ~v1767 ~v1768 ~v1769 ~v177 ~v1770 ~v1771 ~v1772 ~v1773 ~v1774 ~v1775 ~v1776 ~v1777 ~v1778 ~v1779 ~v1780 ~v1781 ~v1782 ~v1783 ~v1784 ~v1785 ~v1786 ~v1787 ~v1788 ~v1789 ~v1790 ~v1791 ~v1792 ~v1793 ~v1794 ~v1795 ~v1796 ~v1797 ~v1798 ~v1799 ~v180 ~v1800 ~v1801 ~v1802 ~v1803 ~v1804 ~v1805 ~v1806 ~v1807 ~v1808 ~v1809 ~v181 ~v1810 ~v1811 ~v1812 ~v1813 ~v1814 ~v1815 ~v1816 ~v1817 ~v1818 ~v1819 ~v182 ~v1820 ~v1821 ~v1822 ~v1823 ~v1824 ~v1825 ~v1826 ~v1827 ~v1828 ~v1829 ~v183 ~v1830 ~v1831 ~v1832 ~v1833 ~v1834 ~v1835 ~v1836 ~v1837 ~v1838 ~v1839 ~v184 ~v1840 ~v1841 ~v1842 ~v1843 ~v1844 ~v1845 ~v1846 ~v1847 ~v1848 ~v1849 ~v185 ~v1850 ~v1851 ~v1852 ~v1853 ~v1854 ~v1855 ~v1856 ~v1857 ~v1858 ~v1859 ~v186 ~v1860 ~v1861 ~v1862 ~v1863 ~v1864 ~v1865 ~v1866 ~v1867 ~v1868 ~v1869 ~v187 ~v1870 ~v1871 ~v1872 ~v1873 ~v1874 ~v1875 ~v1876 ~v1877 ~v1878 ~v1879 ~v188 ~v1880 ~v1881 ~v1882 ~v1883 ~v1884 ~v1885 ~v1886 ~v189 ~v190 ~v1901 ~v1902 ~v1903 ~v1904 ~v1905 ~v1906 ~v1908 ~v1909 ~v191 ~v1910 ~v1911 ~v1912 ~v1913 ~v1914 ~v1915 ~v1916 ~v1917 ~v1918 ~v1919 ~v192 ~v1920 ~v1921 ~v1922 ~v1923 ~v1924 ~v1925 ~v1926 ~v1927 ~v1928 ~v1929 ~v193 ~v1930 ~v1931 ~v1932 ~v1934 ~v1935 ~v1936 ~v1938 ~v1939 ~v194 ~v1940 ~v1941 ~v1942 ~v1943 ~v1944 ~v1945 ~v1946 ~v1948 ~v1949 ~v195 ~v1950 ~v1951 ~v1952 ~v1953 ~v1954 ~v1955 ~v1956 ~v1957 ~v1958 ~v1959 ~v196 ~v1960 ~v1961 ~v1962 ~v1963 ~v1964 ~v1965 ~v1966 ~v1967 ~v1968 ~v1969 ~v197 ~v1970 ~v1971 ~v1972 ~v1973 ~v1974 ~v1975 ~v1976 ~v1977 ~v1978 ~v1979 ~v198 ~v1980 ~v1981 ~v1982 ~v1983 ~v1984 ~v1985 ~v1986 ~v1987 ~v1988 ~v1989 ~v199 ~v1990 ~v1991 ~v1992 ~v1993 ~v1994 ~v1995 ~v1996 ~v1997 ~v1998 ~v1999 ~v200 ~v2000 ~v2001 ~v2002 ~v2003 ~v2005 ~v2006 ~v2007 ~v2008 ~v2009 ~v201 ~v2010 ~v2011 ~v2012 ~v2013 ~v2014 ~v2015 ~v2016 ~v2017 ~v2019 ~v202 ~v2020 ~v2021 ~v2022 ~v2023 ~v2024 ~v2026 ~v2027 ~v2028 ~v203 ~v2030 ~v2031 ~v2032 ~v2033 ~v2034 ~v2035 ~v2036 ~v2037 ~v2038 ~v2039 ~v204 ~v2040 ~v2041 ~v2042 ~v2043 ~v2044 ~v2046 ~v2047 ~v2049 ~v205 ~v2050 ~v2051 ~v2053 ~v2054 ~v206 ~v2061 ~v2065 ~v2066 ~v207 ~v2074 ~v2076 ~v2079 ~v208 ~v2080 ~v2082 ~v2083 ~v2084 ~v2085 ~v2086 ~v2087 ~v2089 ~v209 ~v2090 ~v2094 ~v2097 ~v2098 ~v2099 ~v210 ~v2100 ~v2101 ~v2102 ~v2103 ~v2104 ~v2105 ~v2106 ~v2107 ~v2108 ~v2109 ~v211 ~v2110 ~v2112 ~v2119 ~v212 ~v2123 ~v2124 ~v2128 ~v2129 ~v213 ~v2130 ~v2131 ~v2138 ~v2139 ~v214 ~v2140 ~v2141 ~v2142 ~v2143 ~v2144 ~v2145 ~v2146 ~v2147 ~v2148 ~v2149 ~v215 ~v2151 ~v2153 ~v2155 ~v2156 ~v2157 ~v2158 ~v2159 ~v216 ~v2160 ~v2162 ~v2163 ~v2164 ~v2165 ~v2166 ~v2167 ~v2168 ~v2169 ~v217 ~v2170 ~v2171 ~v2174 ~v2175 ~v2176 ~v2177 ~v2178 ~v2179 ~v218 ~v2180 ~v2181 ~v2182 ~v2183 ~v2184 ~v2185 ~v2186 ~v2187 ~v2188 ~v2189 ~v219 ~v2190 ~v2191 ~v2192 ~v2193 ~v2194 ~v2195 ~v2196 ~v2197 ~v2198 ~v2199 ~v220 ~v2200 ~v2201 ~v2202 ~v2203 ~v2204 ~v2205 ~v2206 ~v2207 ~v2208 ~v2209 ~v221 ~v2210 ~v2211 ~v2212 ~v2213 ~v2214 ~v2215 ~v2216 ~v2217 ~v2218 ~v2219 ~v222 ~v2220 ~v2221 ~v2222 ~v2223 ~v2224 ~v2225 ~v2226 ~v2227 ~v2228 ~v223 ~v2231 ~v2232 ~v2233 ~v2234 ~v2235 ~v2236 v2237 ~v2238 ~v2239 ~v224 v2240 ~v2241 ~v2242 ~v2243 ~v2244 ~v2245 ~v2246 ~v2248 ~v225 ~v2250 ~v2251 ~v2252 ~v2253 ~v2256 ~v2257 ~v2258 ~v2259 ~v226 ~v2260 ~v2261 ~v2262 ~v2263 ~v2264 ~v2265 ~v2266 ~v2267 ~v2268 ~v227 ~v2270 ~v2271 ~v2272 ~v2273 ~v2278 ~v2279 ~v228 ~v2280 ~v2281 ~v2282 ~v2284 ~v2288 ~v229 ~v2292 ~v2293 ~v2298 ~v230 ~v2305 ~v2306 ~v2307 ~v2308 ~v2309 ~v231 ~v2310 ~v2311 ~v2312 ~v2313 ~v2315 ~v2317 ~v2318 ~v232 ~v2321 ~v2322 ~v2323 ~v2324 ~v2325 ~v2326 ~v2327 ~v2328 ~v233 ~v2330 ~v2331 ~v2334 ~v2335 ~v2336 ~v2337 ~v2338 ~v2339 ~v234 ~v2340 ~v2341 ~v2342 ~v2343 ~v2344 ~v2345 ~v2346 ~v2347 ~v2348 ~v2349 ~v235 ~v2350 ~v236 ~v2362 ~v2364 ~v2367 ~v2368 ~v2369 ~v237 ~v2371 ~v2372 ~v2373 ~v2374 ~v2375 ~v2376 ~v2377 ~v2378 ~v2379 ~v238 ~v2380 ~v2381 ~v2382 ~v2383 ~v2384 ~v2385 ~v2386 ~v2387 ~v2388 ~v2389 ~v239 ~v2390 ~v2391 ~v2392 ~v2394 ~v2395 ~v2396 ~v2397 ~v2398 ~v2399 ~v24 ~v240 ~v2400 ~v2401 ~v2402 ~v2403 ~v2404 ~v2405 ~v2406 ~v2407 ~v2408 ~v2409 ~v241 ~v2410 ~v2411 ~v2412 ~v2413 ~v2414 ~v2415 v2416 ~v2417 ~v2418 ~v2419 ~v242 ~v2420 ~v2421 ~v2422 ~v2423 ~v2424 ~v2425 ~v2426 ~v2427 ~v2428 ~v2429 ~v243 ~v2430 ~v2433 ~v2434 ~v244 ~v2444 ~v2445 ~v2446 ~v2447 ~v2448 ~v2449 ~v245 ~v2450 ~v2451 ~v2452 ~v2453 ~v2454 ~v2455 ~v2456 ~v2457 ~v2458 ~v2459 ~v246 ~v2460 ~v2461 ~v2462 ~v2463 ~v2464 ~v2465 ~v2466 ~v2467 ~v2468 ~v2469 ~v247 ~v2470 ~v2471 ~v2472 ~v2473 ~v2474 ~v2475 ~v2476 ~v2477 ~v2478 ~v2479 ~v248 ~v2480 ~v2481 ~v2482 ~v2483 ~v2484 ~v2485 ~v2486 ~v2487 ~v2488 ~v2489 ~v249 ~v2490 ~v2491 ~v2492 ~v2493 ~v2494 ~v2495 ~v2496 ~v2497 v2498 ~v2499 ~v25 ~v250 v2500 ~v2504 ~v2505 v2506 ~v2507 ~v2508 ~v2509 ~v251 ~v2510 ~v2511 ~v2512 ~v2513 ~v2514 ~v2515 ~v2516 ~v2517 ~v2518 ~v2519 ~v252 ~v2520 ~v2521 ~v2522 ~v2523 ~v2524 ~v2525 ~v2526 ~v2527 ~v2528 ~v2529 ~v253 ~v2530 ~v2531 ~v2532 ~v2533 ~v2534 ~v2535 ~v2536 ~v2537 ~v2538 ~v2539 ~v254 ~v2540 ~v2541 ~v2542 ~v2543 ~v2544 ~v2545 ~v2546 ~v2547 ~v2548 ~v2549 ~v255 ~v2550 ~v2551 ~v2552 ~v2553 ~v2554 ~v2555 ~v2556 ~v2557 ~v2558 ~v2559 ~v256 ~v2560 ~v2561 ~v2562 ~v2563 ~v2564 ~v2565 ~v2566 ~v2567 ~v2568 ~v2569 ~v257 ~v2570 ~v2571 ~v2572 ~v2573 ~v2574 ~v2575 ~v2576 ~v2577 ~v2578 ~v2579 ~v258 ~v2580 ~v2581 ~v2582 ~v2583 ~v2584 ~v2585 ~v2586 ~v2587 ~v2588 ~v2589 ~v259 ~v2590 ~v2591 ~v2592 ~v2593 ~v2594 ~v2596 v2597 ~v2598 ~v2599 ~v26 ~v260 ~v2600 ~v2601 ~v2602 ~v2603 ~v2604 ~v2605 ~v2606 ~v2607 ~v2608 v2609 ~v261 ~v2610 ~v2611 ~v2612 ~v2617 ~v2619 ~v262 ~v2622 ~v2627 ~v2628 ~v2629 ~v263 ~v2630 ~v265 ~v266 ~v2677 ~v2684 ~v2686 ~v269 ~v27 ~v270 ~v271 ~v272 ~v273 ~v274 ~v275 ~v276 ~v277 ~v278 ~v279 ~v28 ~v280 ~v281 ~v282 ~v283 ~v284 ~v285 ~v286 ~v287 ~v288 ~v289 ~v29 ~v290 ~v291 ~v295 ~v296 ~v297 ~v298 ~v299 ~v30 ~v300 ~v301 ~v302 ~v303 ~v304 ~v305 ~v306 ~v307 ~v308 ~v309 ~v31 ~v310 ~v311 ~v312 ~v313 ~v314 ~v317 ~v32 ~v320 ~v321 ~v322 ~v323 ~v324 ~v325 ~v326 ~v327 ~v328 ~v329 ~v33 ~v330 ~v331 ~v332 ~v333 ~v334 ~v335 ~v336 ~v337 ~v338 ~v339 ~v34 ~v340 ~v341 ~v343 ~v346 ~v347 ~v348 ~v349 ~v35 ~v350 ~v351 ~v352 ~v353 ~v354 ~v355 ~v36 ~v362 ~v365 ~v37 ~v370 ~v371 ~v378 ~v38 ~v386 ~v387 ~v390 ~v392 ~v393 ~v395 ~v396 ~v397 ~v398 ~v40 ~v406 ~v41 ~v413 ~v417 ~v42 ~v420 ~v422 ~v423 ~v43 ~v431 ~v433 ~v435 ~v44 ~v45 ~v46 ~v461 ~v462 ~v464 ~v466 ~v467 ~v468 ~v469 ~v47 ~v470 ~v471 ~v472 ~v473 ~v474 ~v475 ~v476 ~v477 ~v478 ~v48 ~v481 ~v483 ~v484 ~v485 ~v487 ~v488 ~v489 ~v49 ~v490 ~v491 ~v495 ~v50 ~v509 ~v51 ~v510 ~v515 ~v517 ~v52 ~v53 ~v54 ~v55 ~v56 ~v562 ~v564 ~v57 ~v570 ~v577 ~v578 ~v579 ~v58 ~v580 ~v581 ~v582 ~v583 ~v584 ~v585 ~v586 ~v587 ~v588 ~v589 ~v59 ~v590 ~v591 ~v592 ~v595 ~v597 ~v6 ~v60 ~v600 ~v61 ~v610 ~v62 ~v624 ~v629 ~v64 ~v65 ~v656 ~v658 ~v66 ~v660 ~v67 ~v677 ~v68 ~v680 ~v685 ~v689 ~v69 v697 ~v70 ~v71 ~v72 ~v726 ~v727 ~v73 ~v74 ~v75 ~v753 ~v755 ~v756 ~v76 ~v760 ~v761 ~v762 ~v764 ~v765 ~v766 ~v767 ~v768 ~v77 ~v773 ~v774 ~v775 ~v777 ~v778 ~v779 ~v78 ~v780 ~v783 ~v784 ~v785 ~v786 ~v787 ~v788 ~v79 ~v792 v798 ~v80 ~v804 ~v805 ~v806 ~v81 ~v813 ~v814 ~v815 ~v816 v817 ~v818 ~v819 ~v82 ~v820 ~v826 ~v83 ~v831 ~v833 ~v834 ~v838 ~v839 ~v84 ~v840 ~v841 ~v842 ~v843 ~v844 ~v845 ~v846 ~v847 ~v848 ~v849 ~v85 v853 ~v857 ~v859 ~v86 ~v863 ~v867 ~v868 ~v869 ~v87 ~v870 ~v871 ~v872 ~v873 ~v874 ~v875 ~v877 ~v878 ~v879 ~v88 ~v880 ~v881 ~v882 ~v883 ~v884 ~v885 ~v887 ~v888 ~v889 ~v89 ~v890 ~v892 ~v893 ~v894 ~v895 ~v899 ~v90 ~v900 ~v901 ~v902 ~v903 ~v904 ~v905 ~v906 ~v907 ~v908 ~v909 ~v91 ~v910 ~v911 ~v912 ~v913 ~v914 ~v915 ~v916 ~v917 ~v918 ~v919 ~v92 ~v920 ~v921 ~v922 ~v923 ~v924 ~v925 ~v926 ~v927 ~v928 ~v929 ~v93 ~v930 ~v931 ~v932 ~v933 ~v934 ~v935 ~v936 ~v937 ~v938 ~v939 ~v94 ~v940 ~v941 ~v942 ~v943 ~v944 ~v945 ~v946 ~v947 ~v948 ~v949 ~v950 ~v951 ~v952 ~v953 ~v954 ~v955 ~v956 ~v957 ~v958 ~v959 ~v96 ~v960 ~v961 ~v962 ~v963 ~v964 ~v97 ~v975 ~v98 ~v983 ~v985 ~v986 ~v988 ~v989 ~v99 ~v991 ~v992 ~v993 ~v994 ~v995 ~v996 ~v997 ~v998 ~v999 +~v0 ~v10 ~v100 ~v1000 ~v1001 ~v1002 ~v1003 ~v1004 ~v1005 ~v1006 ~v1007 ~v1008 ~v1009 ~v101 ~v1010 ~v1011 ~v1012 ~v1013 ~v1014 ~v1015 ~v1016 ~v1017 ~v1018 ~v1019 ~v102 ~v1020 ~v1021 ~v1022 ~v1023 ~v1024 ~v1025 ~v1026 ~v1027 ~v1028 ~v1029 ~v103 ~v1030 ~v1031 ~v1032 ~v1033 ~v1034 ~v1035 ~v1036 ~v1037 ~v1038 ~v1039 ~v104 ~v1040 ~v1041 ~v1042 ~v1043 ~v1044 ~v1045 ~v1046 ~v1047 ~v1048 ~v1049 ~v105 ~v1050 ~v1051 ~v1052 ~v1053 ~v1054 ~v1055 ~v1056 ~v1057 ~v1058 ~v1059 ~v106 ~v1060 ~v1061 ~v1062 ~v1063 ~v1064 ~v1065 ~v1066 ~v1067 ~v1068 ~v1069 ~v107 ~v1070 ~v1071 ~v1072 ~v1073 ~v1074 ~v1075 ~v1076 ~v1077 ~v1079 ~v108 ~v1080 ~v1081 ~v1082 ~v1083 ~v1084 ~v1085 ~v1086 ~v1087 ~v1088 ~v1089 ~v109 ~v1090 ~v1091 ~v1092 ~v1093 ~v1094 ~v1095 ~v1096 ~v1097 ~v1098 ~v1099 ~v110 ~v1100 ~v1101 ~v1102 ~v1103 ~v1104 ~v1105 ~v1106 ~v1107 ~v1108 ~v1109 ~v111 ~v1110 ~v1111 ~v1112 ~v1113 ~v1114 ~v1115 ~v1116 ~v1117 ~v1118 ~v1119 ~v112 ~v1120 ~v1121 ~v1122 ~v1123 ~v1124 ~v1125 ~v1126 ~v1127 ~v1128 ~v1129 ~v113 ~v1130 ~v1131 ~v1132 ~v1133 ~v1134 ~v1135 ~v1136 ~v1137 ~v1138 ~v1139 ~v114 ~v1140 ~v1141 ~v1142 ~v1143 ~v1144 ~v1145 ~v1146 ~v1147 ~v1148 ~v1149 ~v115 ~v1150 ~v1151 ~v1152 ~v1153 ~v1154 ~v1155 ~v1156 ~v1157 ~v1158 ~v1159 ~v116 ~v1160 ~v1161 ~v1162 ~v1163 ~v1164 ~v1165 ~v1166 ~v1167 ~v1169 ~v117 ~v1170 ~v1171 ~v1172 ~v1173 ~v1174 ~v1175 ~v1176 ~v1177 ~v1178 ~v1179 ~v118 ~v1180 ~v1181 ~v1182 ~v1183 ~v1184 ~v1185 ~v1186 ~v1187 ~v1188 ~v1189 ~v119 ~v1190 ~v1191 ~v1192 ~v1193 ~v1194 ~v1195 ~v1196 ~v1197 ~v1198 ~v1199 ~v120 ~v1201 ~v1202 ~v1204 ~v1205 ~v1206 ~v1207 ~v1208 ~v121 ~v1210 ~v1211 ~v1212 ~v1213 ~v1214 ~v1215 ~v1216 ~v1217 ~v1218 ~v1219 ~v122 ~v1220 ~v1221 ~v1222 ~v1223 ~v1224 ~v1225 ~v1226 ~v1227 ~v1228 ~v1229 ~v123 ~v1231 ~v1233 ~v1234 ~v1235 ~v1236 ~v1237 ~v1238 ~v1239 ~v124 ~v1240 ~v1241 ~v1242 ~v1243 ~v1244 ~v1245 ~v1246 ~v1247 ~v1248 ~v1249 ~v125 ~v1250 ~v1251 ~v1252 ~v1253 ~v1254 ~v1255 ~v1256 ~v1257 ~v1258 ~v1259 ~v126 ~v1260 ~v1261 ~v1262 ~v1263 ~v1264 ~v1265 ~v1266 ~v1267 ~v1268 ~v1269 ~v127 ~v1270 ~v1271 ~v1272 ~v1273 ~v1274 ~v1275 ~v1276 ~v1277 ~v1278 ~v1279 ~v128 ~v1280 ~v1281 ~v1282 ~v1283 ~v1284 ~v1285 ~v1286 ~v1287 ~v1288 ~v1289 ~v129 ~v1290 ~v1291 ~v1292 ~v1293 ~v1294 ~v1295 ~v1296 ~v1297 ~v1298 ~v1299 ~v1300 ~v1301 ~v1302 ~v1303 ~v1304 ~v1305 ~v1306 ~v1307 ~v1308 ~v1309 ~v1310 ~v1311 ~v1312 ~v1313 ~v1314 ~v1315 ~v1316 ~v1317 ~v1318 ~v1319 ~v1320 ~v1321 ~v1322 ~v1323 ~v1324 ~v1325 ~v1326 ~v1327 ~v1328 ~v1329 ~v133 ~v1330 ~v1331 ~v1332 ~v1333 ~v1334 ~v1335 ~v1336 ~v1337 ~v1338 ~v1339 ~v1340 ~v1341 ~v1342 ~v1343 ~v1344 ~v1345 ~v1346 ~v1347 ~v1348 ~v1349 ~v135 ~v1351 ~v1352 ~v1353 ~v1357 ~v1358 ~v1359 ~v136 ~v1360 ~v1367 ~v1368 ~v137 ~v1375 ~v1376 ~v138 ~v1382 ~v1383 ~v139 ~v1395 ~v1397 ~v1398 ~v140 ~v1403 ~v1406 ~v1407 ~v1410 ~v1411 ~v1412 ~v1413 v1414 ~v1415 ~v1416 ~v1417 ~v1418 ~v1419 ~v142 ~v1420 ~v1422 ~v1425 ~v1426 ~v1427 ~v1428 ~v1429 ~v143 ~v1430 ~v1431 ~v1432 ~v1433 ~v1434 ~v1435 ~v1436 ~v1437 ~v1438 ~v1439 ~v144 ~v1440 ~v1441 ~v1442 ~v1443 ~v1444 ~v1445 ~v1446 ~v145 ~v1456 ~v146 ~v1462 ~v1463 ~v1465 ~v147 ~v1478 ~v1479 ~v148 ~v1480 ~v1488 ~v1489 ~v149 ~v1490 ~v1491 ~v1492 ~v1497 ~v1498 ~v1499 ~v150 ~v1500 ~v1501 ~v1502 ~v1503 ~v1504 ~v1505 ~v1506 ~v1508 ~v151 ~v1513 ~v1514 ~v1516 ~v1517 ~v1518 ~v1519 ~v152 ~v1520 ~v1521 ~v1522 ~v1523 ~v1524 ~v1525 ~v1526 ~v1527 ~v1528 ~v1529 ~v153 ~v1530 ~v1531 ~v1532 ~v1534 ~v1535 ~v1536 ~v1537 ~v1538 ~v154 ~v1542 ~v1543 ~v1544 ~v1545 ~v1548 ~v155 ~v1552 ~v1557 ~v1558 ~v156 ~v1561 ~v1564 ~v1566 ~v1569 ~v157 ~v1573 ~v1574 ~v1575 ~v1576 ~v1577 ~v1578 ~v1579 ~v158 ~v1580 ~v1581 ~v1582 ~v1583 ~v1584 ~v1585 ~v1586 ~v1587 ~v1588 ~v1589 ~v159 ~v1590 ~v1591 ~v1592 ~v1594 ~v1595 ~v1596 ~v1597 ~v1598 ~v1599 ~v160 ~v1600 ~v1601 ~v1602 ~v1603 ~v1608 ~v1609 ~v161 ~v1610 ~v1611 ~v1612 ~v1616 ~v1617 ~v1618 ~v1619 ~v162 ~v1621 ~v1623 ~v1626 ~v1627 ~v163 ~v1630 ~v1631 ~v1632 ~v1633 ~v1634 ~v1635 ~v1637 ~v1638 ~v164 ~v1641 ~v1642 ~v1643 ~v1646 ~v165 ~v1651 ~v1652 ~v1653 ~v166 ~v1661 ~v1667 ~v167 ~v1670 ~v1671 ~v1672 ~v1673 ~v1674 ~v1675 ~v1676 ~v1677 ~v1678 ~v1679 ~v168 ~v1680 ~v1681 ~v1682 ~v1683 ~v1684 ~v1685 ~v1686 ~v1687 ~v1688 ~v1689 ~v169 ~v1690 ~v1691 ~v1692 ~v1693 ~v1694 ~v1695 ~v1696 ~v1697 ~v1698 ~v1699 ~v170 ~v1700 ~v1701 ~v1702 ~v1703 ~v1704 ~v1705 ~v1706 ~v1707 ~v1708 ~v1709 ~v171 ~v1710 ~v1711 ~v1712 ~v1713 ~v1714 ~v1715 ~v1716 ~v1717 ~v1718 ~v1719 ~v172 ~v1720 ~v1721 ~v1722 ~v1723 ~v1724 ~v1725 ~v1726 ~v1727 ~v1728 ~v1729 ~v173 ~v1730 ~v1731 ~v1732 ~v1733 ~v1734 ~v1735 ~v1736 ~v1737 ~v1738 ~v1739 ~v174 ~v1740 ~v1741 ~v1742 ~v1743 ~v1744 ~v1745 ~v1746 ~v1747 ~v1748 ~v1749 ~v175 ~v1750 ~v1751 ~v1752 ~v1753 ~v1754 ~v1755 ~v1756 ~v1757 ~v1758 ~v1759 ~v176 ~v1760 ~v1761 ~v1762 ~v1763 ~v1764 ~v1765 ~v1766 ~v1767 ~v1768 ~v1769 ~v177 ~v1770 ~v1771 ~v1772 ~v1773 ~v1774 ~v1775 ~v1776 ~v1777 ~v1778 ~v1779 ~v1780 ~v1781 ~v1782 ~v1783 ~v1784 ~v1785 ~v1786 ~v1787 ~v1788 ~v1789 ~v1790 ~v1791 ~v1792 ~v1793 ~v1794 ~v1795 ~v1796 ~v1797 ~v1798 ~v1799 ~v180 ~v1800 ~v1801 ~v1802 ~v1803 ~v1804 ~v1805 ~v1806 ~v1807 ~v1808 ~v1809 ~v181 ~v1810 ~v1811 ~v1812 ~v1813 ~v1814 ~v1815 ~v1816 ~v1817 ~v1818 ~v1819 ~v182 ~v1820 ~v1821 ~v1822 ~v1823 ~v1824 ~v1825 ~v1826 ~v1827 ~v1828 ~v1829 ~v183 ~v1830 ~v1831 ~v1832 ~v1833 ~v1834 ~v1835 ~v1836 ~v1837 ~v1838 ~v1839 ~v184 ~v1840 ~v1841 ~v1842 ~v1843 ~v1844 ~v1845 ~v1846 ~v1847 ~v1848 ~v1849 ~v185 ~v1850 ~v1851 ~v1852 ~v1853 ~v1854 ~v1855 ~v1856 ~v1857 ~v1858 ~v1859 ~v186 ~v1860 ~v1861 ~v1862 ~v1863 ~v1864 ~v1865 ~v1866 ~v1867 ~v1868 ~v1869 ~v187 ~v1870 ~v1871 ~v1872 ~v1873 ~v1874 ~v1875 ~v1876 ~v1877 ~v1878 ~v1879 ~v188 ~v1880 ~v1881 ~v1882 ~v1883 ~v1884 ~v1885 ~v1886 ~v189 ~v190 ~v1900 ~v1901 ~v1902 ~v1903 ~v1904 ~v1905 ~v1906 ~v1907 ~v1908 ~v1909 ~v191 ~v1910 ~v1911 ~v1912 ~v1913 ~v1914 ~v1915 ~v1916 ~v1917 ~v1918 ~v1919 ~v192 ~v1920 ~v1921 ~v1922 ~v1923 ~v1924 ~v1925 ~v1926 ~v1927 ~v1928 ~v1929 ~v193 ~v1930 ~v1931 ~v1932 ~v1933 ~v1934 ~v1935 ~v1936 ~v1937 ~v1938 ~v1939 ~v194 ~v1940 ~v1941 ~v1942 ~v1943 ~v1944 ~v1945 ~v1946 ~v1947 ~v1948 ~v1949 ~v195 ~v1950 ~v1951 ~v1952 ~v1953 ~v1954 ~v1955 ~v1956 ~v1957 ~v1958 ~v1959 ~v196 ~v1960 ~v1961 ~v1962 ~v1963 ~v1964 ~v1965 ~v1966 ~v1967 ~v1968 ~v1969 ~v197 ~v1970 ~v1971 ~v1972 ~v1973 ~v1974 ~v1975 ~v1976 ~v1977 ~v1978 ~v1979 ~v198 ~v1980 ~v1981 ~v1982 ~v1983 ~v1984 ~v1985 ~v1986 ~v1987 ~v1988 ~v1989 ~v199 ~v1990 ~v1991 ~v1992 ~v1993 ~v1994 ~v1995 ~v1996 ~v1997 ~v1998 ~v1999 ~v200 ~v2000 ~v2001 ~v2002 ~v2003 ~v2004 ~v2005 ~v2006 ~v2007 ~v2008 ~v2009 ~v201 ~v2010 ~v2011 ~v2012 ~v2013 ~v2014 ~v2015 ~v2016 ~v2017 ~v2018 ~v2019 ~v202 ~v2020 ~v2021 ~v2022 ~v2023 ~v2024 ~v2025 ~v2026 ~v2027 ~v2028 ~v2029 ~v203 ~v2030 ~v2031 ~v2032 ~v2033 ~v2034 ~v2035 ~v2036 ~v2037 ~v2038 ~v2039 ~v204 ~v2040 ~v2041 ~v2042 ~v2043 ~v2044 ~v2045 ~v2046 ~v2047 v2048 ~v2049 ~v205 ~v2050 ~v2051 ~v2052 ~v2053 ~v2054 ~v206 ~v2061 ~v2065 ~v2066 ~v207 ~v2074 ~v2076 ~v2079 ~v208 ~v2080 ~v2082 ~v2083 ~v2084 ~v2085 ~v2086 ~v2087 ~v2089 ~v209 ~v2090 ~v2094 ~v2097 ~v2098 ~v2099 ~v210 ~v2100 ~v2101 ~v2102 ~v2103 ~v2104 ~v2105 ~v2106 ~v2107 ~v2108 ~v2109 ~v211 ~v2110 ~v2112 ~v2119 ~v212 ~v2123 ~v2124 ~v2128 ~v2129 ~v213 ~v2130 ~v2131 ~v2138 ~v2139 ~v214 ~v2140 ~v2141 ~v2142 ~v2143 ~v2144 ~v2145 ~v2146 ~v2147 ~v2148 ~v2149 ~v215 ~v2151 ~v2153 ~v2155 ~v2156 ~v2157 ~v2158 ~v2159 ~v216 ~v2160 ~v2162 ~v2163 ~v2164 ~v2165 ~v2166 ~v2167 ~v2168 ~v2169 ~v217 ~v2170 ~v2171 ~v2174 ~v2175 ~v2176 ~v2177 ~v2178 ~v2179 ~v218 ~v2180 ~v2181 ~v2182 ~v2183 ~v2184 ~v2185 ~v2186 ~v2187 ~v2188 ~v2189 ~v219 ~v2190 ~v2191 ~v2192 ~v2193 ~v2194 ~v2195 ~v2196 ~v2197 ~v2198 ~v2199 ~v220 ~v2200 ~v2201 ~v2202 ~v2203 ~v2204 ~v2205 ~v2206 ~v2207 ~v2208 ~v2209 ~v221 ~v2210 ~v2211 ~v2212 ~v2213 ~v2214 ~v2215 ~v2216 ~v2217 ~v2218 ~v2219 ~v222 ~v2220 ~v2221 ~v2222 ~v2223 ~v2224 ~v2225 ~v2226 ~v2227 ~v2228 ~v223 ~v2231 ~v2232 ~v2233 ~v2234 ~v2235 ~v2236 v2237 ~v2238 ~v2239 ~v224 v2240 ~v2241 ~v2242 ~v2243 ~v2244 ~v2245 ~v2246 ~v2248 ~v225 ~v2250 ~v2251 ~v2252 ~v2253 ~v2256 ~v2257 ~v2258 ~v2259 ~v226 ~v2260 ~v2261 ~v2262 ~v2263 ~v2264 ~v2265 ~v2266 ~v2267 ~v2268 ~v227 ~v2270 ~v2271 ~v2272 ~v2273 ~v2278 ~v2279 ~v228 ~v2280 ~v2281 ~v2282 ~v2284 ~v2288 ~v229 ~v2292 ~v2293 ~v2298 ~v230 ~v2305 ~v2306 ~v2307 ~v2308 ~v2309 ~v231 ~v2310 ~v2311 ~v2312 ~v2313 ~v2315 ~v2317 ~v2318 ~v232 ~v2321 ~v2322 ~v2323 ~v2324 ~v2325 ~v2326 ~v2327 ~v2328 ~v233 ~v2330 ~v2331 ~v2334 ~v2335 ~v2336 ~v2337 ~v2338 ~v2339 ~v234 ~v2340 ~v2341 ~v2342 ~v2343 ~v2344 ~v2345 ~v2346 ~v2347 ~v2348 ~v2349 ~v235 ~v2350 ~v236 ~v2362 ~v2364 ~v2367 ~v2368 ~v2369 ~v237 ~v2371 ~v2372 ~v2373 ~v2374 ~v2375 ~v2376 ~v2377 ~v2378 ~v2379 ~v238 ~v2380 ~v2381 ~v2382 ~v2383 ~v2384 ~v2385 ~v2386 ~v2387 ~v2388 ~v2389 ~v239 ~v2390 ~v2391 ~v2392 ~v2394 ~v2395 ~v2396 ~v2397 ~v2398 ~v2399 ~v24 ~v240 ~v2400 ~v2401 ~v2402 ~v2403 ~v2404 ~v2405 ~v2406 ~v2407 ~v2408 ~v2409 ~v241 ~v2410 ~v2411 ~v2412 ~v2413 ~v2414 ~v2415 v2416 ~v2417 ~v2418 ~v2419 ~v242 ~v2420 ~v2421 ~v2422 ~v2423 ~v2424 ~v2425 ~v2426 ~v2427 ~v2428 ~v2429 ~v243 ~v2430 ~v2433 ~v2434 ~v244 ~v2444 ~v2445 ~v2446 ~v2447 ~v2448 ~v2449 ~v245 ~v2450 ~v2451 ~v2452 ~v2453 ~v2454 ~v2455 ~v2456 ~v2457 ~v2458 ~v2459 ~v246 ~v2460 ~v2461 ~v2462 ~v2463 ~v2464 ~v2465 ~v2466 ~v2467 ~v2468 ~v2469 ~v247 ~v2470 ~v2471 ~v2472 ~v2473 ~v2474 ~v2475 ~v2476 ~v2477 ~v2478 ~v2479 ~v248 ~v2480 ~v2481 ~v2482 ~v2483 ~v2484 ~v2485 ~v2486 ~v2487 ~v2488 ~v2489 ~v249 ~v2490 ~v2491 ~v2492 ~v2493 ~v2494 ~v2495 ~v2496 ~v2497 v2498 ~v2499 ~v25 ~v250 v2500 ~v2504 ~v2505 v2506 ~v2507 ~v2508 ~v2509 ~v251 ~v2510 ~v2511 ~v2512 ~v2513 ~v2514 ~v2515 ~v2516 ~v2517 ~v2518 ~v2519 ~v252 ~v2520 ~v2521 ~v2522 ~v2523 ~v2524 ~v2525 ~v2526 ~v2527 ~v2528 ~v2529 ~v253 ~v2530 ~v2531 ~v2532 ~v2533 ~v2534 ~v2535 ~v2536 ~v2537 ~v2538 ~v2539 ~v254 ~v2540 ~v2541 ~v2542 ~v2543 ~v2544 ~v2545 ~v2546 ~v2547 ~v2548 ~v2549 ~v255 ~v2550 ~v2551 ~v2552 ~v2553 ~v2554 ~v2555 ~v2556 ~v2557 ~v2558 ~v2559 ~v256 ~v2560 ~v2561 ~v2562 ~v2563 ~v2564 ~v2565 ~v2566 ~v2567 ~v2568 ~v2569 ~v257 ~v2570 ~v2571 ~v2572 ~v2573 ~v2574 ~v2575 ~v2576 ~v2577 ~v2578 ~v2579 ~v258 ~v2580 ~v2581 ~v2582 ~v2583 ~v2584 ~v2585 ~v2586 ~v2587 ~v2588 ~v2589 ~v259 ~v2590 ~v2591 ~v2592 ~v2593 ~v2594 ~v2596 v2597 ~v2598 ~v2599 ~v26 ~v260 ~v2600 ~v2601 ~v2602 ~v2603 ~v2604 ~v2605 ~v2606 ~v2607 ~v2608 v2609 ~v261 ~v2610 ~v2611 ~v2612 ~v2617 ~v2619 ~v262 ~v2622 ~v2627 ~v2628 ~v2629 ~v263 ~v2630 ~v265 ~v266 ~v2677 ~v2684 ~v2686 ~v269 ~v27 ~v270 ~v271 ~v272 ~v273 ~v274 ~v275 ~v276 ~v277 ~v278 ~v279 ~v28 ~v280 ~v281 ~v282 ~v283 ~v284 ~v285 ~v286 ~v287 ~v288 ~v289 ~v29 ~v290 ~v291 ~v295 ~v296 ~v297 ~v298 ~v299 ~v30 ~v300 ~v301 ~v302 ~v303 ~v304 ~v305 ~v306 ~v307 ~v308 ~v309 ~v31 ~v310 ~v311 ~v312 ~v313 ~v314 ~v317 ~v32 ~v320 ~v321 ~v322 ~v323 ~v324 ~v325 ~v326 ~v327 ~v328 ~v329 ~v33 ~v330 ~v331 ~v332 ~v333 ~v334 ~v335 ~v336 ~v337 ~v338 ~v339 ~v34 ~v340 ~v341 ~v343 ~v346 ~v347 ~v348 ~v349 ~v35 ~v350 ~v351 ~v352 ~v353 ~v354 ~v355 ~v36 ~v362 ~v365 ~v37 ~v370 ~v371 ~v378 ~v38 ~v386 ~v387 ~v390 ~v392 ~v393 ~v395 ~v396 ~v397 ~v398 ~v40 ~v406 ~v41 ~v413 ~v417 ~v42 ~v420 ~v422 ~v423 ~v43 ~v431 ~v433 ~v435 ~v44 ~v45 ~v46 ~v461 ~v462 ~v464 ~v466 ~v467 ~v468 ~v469 ~v47 ~v470 ~v471 ~v472 ~v473 ~v474 ~v475 ~v476 ~v477 ~v478 ~v48 ~v481 ~v483 ~v484 ~v485 ~v487 ~v488 ~v489 ~v49 ~v490 ~v491 ~v495 ~v50 ~v509 ~v51 ~v510 ~v515 ~v517 ~v52 ~v53 ~v54 ~v55 ~v56 ~v562 ~v564 ~v57 ~v570 ~v577 ~v578 ~v579 ~v58 ~v580 ~v581 ~v582 ~v583 ~v584 ~v585 ~v586 ~v587 ~v588 ~v589 ~v59 ~v590 ~v591 ~v592 ~v595 ~v597 ~v6 ~v60 ~v600 ~v61 ~v610 ~v62 ~v624 ~v629 ~v64 ~v65 ~v656 ~v658 ~v66 ~v660 ~v67 ~v677 ~v68 ~v680 ~v685 ~v689 ~v69 v697 ~v70 ~v71 ~v72 ~v726 ~v727 ~v73 ~v74 ~v75 ~v753 ~v755 ~v756 ~v76 ~v760 ~v761 ~v762 ~v764 ~v765 ~v766 ~v767 ~v768 ~v77 ~v773 ~v774 ~v775 ~v777 ~v778 ~v779 ~v78 ~v780 ~v783 ~v784 ~v785 ~v786 ~v787 ~v788 ~v79 ~v792 v798 ~v80 ~v804 ~v805 ~v806 ~v81 ~v813 ~v814 ~v815 ~v816 v817 ~v818 ~v819 ~v82 ~v820 ~v826 ~v83 ~v831 ~v833 ~v834 ~v838 ~v839 ~v84 ~v840 ~v841 ~v842 ~v843 ~v844 ~v845 ~v846 ~v847 ~v848 ~v849 ~v85 v853 ~v857 ~v859 ~v86 ~v863 ~v867 ~v868 ~v869 ~v87 ~v870 ~v871 ~v872 ~v873 ~v874 ~v875 ~v877 ~v878 ~v879 ~v88 ~v880 ~v881 ~v882 ~v883 ~v884 ~v885 ~v887 ~v888 ~v889 ~v89 ~v890 ~v892 ~v893 ~v894 ~v895 ~v899 ~v90 ~v900 ~v901 ~v902 ~v903 ~v904 ~v905 ~v906 ~v907 ~v908 ~v909 ~v91 ~v910 ~v911 ~v912 ~v913 ~v914 ~v915 ~v916 ~v917 ~v918 ~v919 ~v92 ~v920 ~v921 ~v922 ~v923 ~v924 ~v925 ~v926 ~v927 ~v928 ~v929 ~v93 ~v930 ~v931 ~v932 ~v933 ~v934 ~v935 ~v936 ~v937 ~v938 ~v939 ~v94 ~v940 ~v941 ~v942 ~v943 ~v944 ~v945 ~v946 ~v947 ~v948 ~v949 ~v950 ~v951 ~v952 ~v953 ~v954 ~v955 ~v956 ~v957 ~v958 ~v959 ~v96 ~v960 ~v961 ~v962 ~v963 ~v964 ~v97 ~v975 ~v98 ~v983 ~v985 ~v986 ~v988 ~v989 ~v99 ~v991 ~v992 ~v993 ~v994 ~v995 ~v996 ~v997 ~v998 ~v999 +~v0 ~v1 ~v10 ~v100 ~v1000 ~v1001 ~v1002 ~v1003 ~v1004 ~v1005 ~v1006 ~v1007 ~v1008 ~v1009 ~v101 ~v1010 ~v1011 ~v1012 ~v1013 ~v1014 ~v1015 ~v1016 ~v1017 ~v1018 ~v1019 ~v102 ~v1020 ~v1021 ~v1022 ~v1023 ~v1024 ~v1025 ~v1026 ~v1027 ~v1028 ~v1029 ~v103 ~v1030 ~v1031 ~v1032 ~v1033 ~v1034 ~v1035 ~v1036 ~v1037 ~v1038 ~v1039 ~v104 ~v1040 ~v1041 ~v1042 ~v1043 ~v1044 ~v1045 ~v1046 ~v1047 ~v1048 ~v1049 ~v105 ~v1050 ~v1051 ~v1052 ~v1053 ~v1054 ~v1055 ~v1056 ~v1057 ~v1058 ~v1059 ~v106 ~v1060 ~v1061 ~v1062 ~v1063 ~v1064 ~v1065 ~v1066 ~v1067 ~v1068 ~v1069 ~v107 ~v1070 ~v1071 ~v1072 ~v1073 ~v1074 ~v1075 ~v1076 ~v1077 ~v1078 ~v1079 ~v108 ~v1080 ~v1081 ~v1082 ~v1083 ~v1084 ~v1085 ~v1086 ~v1087 ~v1088 ~v1089 ~v109 ~v1090 ~v1091 ~v1092 ~v1093 ~v1094 ~v1095 ~v1096 ~v1097 ~v1098 ~v1099 ~v110 ~v1100 ~v1101 ~v1102 ~v1103 ~v1104 ~v1105 ~v1106 ~v1107 ~v1108 ~v1109 ~v111 ~v1110 ~v1111 ~v1112 ~v1113 ~v1114 ~v1115 ~v1116 ~v1117 ~v1118 ~v1119 ~v112 ~v1120 ~v1121 ~v1122 ~v1123 ~v1124 ~v1125 ~v1126 ~v1127 ~v1128 ~v1129 ~v113 ~v1130 ~v1131 ~v1132 ~v1133 ~v1134 ~v1135 ~v1136 ~v1137 ~v1138 ~v1139 ~v114 ~v1140 ~v1141 ~v1142 ~v1143 ~v1144 ~v1145 ~v1146 ~v1147 ~v1148 ~v1149 ~v115 ~v1150 ~v1151 ~v1152 ~v1153 ~v1154 ~v1155 ~v1156 ~v1157 ~v1158 ~v1159 ~v116 ~v1160 ~v1161 ~v1162 ~v1163 ~v1164 ~v1165 ~v1166 ~v1167 ~v1168 ~v1169 ~v117 ~v1170 ~v1171 ~v1172 ~v1173 ~v1174 ~v1175 ~v1176 ~v1177 ~v1178 ~v1179 ~v118 ~v1180 ~v1181 ~v1182 ~v1183 ~v1184 ~v1185 ~v1186 ~v1187 ~v1188 ~v1189 ~v119 ~v1190 ~v1191 ~v1192 ~v1193 ~v1194 ~v1195 ~v1196 ~v1197 ~v1198 ~v1199 ~v120 ~v1200 ~v1201 ~v1202 ~v1203 ~v1204 ~v1205 ~v1206 ~v1207 ~v1208 ~v1209 ~v121 ~v1210 ~v1211 ~v1212 ~v1213 ~v1214 ~v1215 ~v1216 ~v1217 ~v1218 ~v1219 ~v122 ~v1220 ~v1221 ~v1222 ~v1223 ~v1224 ~v1225 ~v1226 ~v1227 ~v1228 ~v1229 ~v123 ~v1230 ~v1231 ~v1232 ~v1233 ~v1234 ~v1235 ~v1236 ~v1237 ~v1238 ~v1239 ~v124 ~v1240 ~v1241 ~v1242 ~v1243 ~v1244 ~v1245 ~v1246 ~v1247 ~v1248 ~v1249 ~v125 ~v1250 ~v1251 ~v1252 ~v1253 ~v1254 ~v1255 ~v1256 ~v1257 ~v1258 ~v1259 ~v126 ~v1260 ~v1261 ~v1262 ~v1263 ~v1264 ~v1265 ~v1266 ~v1267 ~v1268 ~v1269 ~v127 ~v1270 ~v1271 ~v1272 ~v1273 ~v1274 ~v1275 ~v1276 ~v1277 ~v1278 ~v1279 ~v128 ~v1280 ~v1281 ~v1282 ~v1283 ~v1284 ~v1285 ~v1286 ~v1287 ~v1288 ~v1289 ~v129 ~v1290 ~v1291 ~v1292 ~v1293 ~v1294 ~v1295 ~v1296 ~v1297 ~v1298 ~v1299 ~v13 ~v130 ~v1300 ~v1301 ~v1302 ~v1303 ~v1304 ~v1305 ~v1306 ~v1307 ~v1308 ~v1309 ~v131 ~v1310 ~v1311 ~v1312 ~v1313 ~v1314 ~v1315 ~v1316 ~v1317 ~v1318 ~v1319 ~v132 ~v1320 ~v1321 ~v1322 ~v1323 ~v1324 ~v1325 ~v1326 ~v1327 ~v1328 ~v1329 ~v133 ~v1330 ~v1331 ~v1332 ~v1333 ~v1334 ~v1335 ~v1336 ~v1337 ~v1338 ~v1339 ~v134 ~v1340 ~v1341 ~v1342 ~v1343 ~v1344 ~v1345 ~v1346 ~v1347 ~v1348 ~v1349 ~v135 ~v1351 ~v1352 ~v1353 ~v1357 ~v1358 ~v1359 ~v136 ~v1360 ~v1367 ~v1368 ~v137 ~v1370 ~v1371 ~v1375 ~v1376 ~v138 ~v1382 ~v1383 ~v1384 v1385 ~v1386 ~v1387 ~v139 ~v1394 ~v1395 ~v1396 ~v1397 ~v1398 ~v14 ~v140 ~v1403 ~v1406 ~v1407 ~v1409 ~v1410 ~v1411 ~v1412 ~v1413 v1414 ~v1415 ~v1416 ~v1417 ~v1418 ~v1419 ~v142 ~v1420 ~v1422 ~v1425 ~v1426 ~v1427 ~v1428 ~v1429 ~v143 ~v1430 ~v1431 ~v1432 ~v1433 ~v1434 ~v1435 ~v1436 ~v1437 ~v1438 ~v1439 ~v144 ~v1440 ~v1441 ~v1442 ~v1443 ~v1444 ~v1445 ~v1446 ~v1448 ~v145 ~v1451 ~v1456 ~v1457 ~v146 v1461 ~v1462 ~v1463 v1464 ~v1465 ~v147 ~v1470 v1471 ~v1472 ~v1476 ~v1478 ~v1479 ~v148 ~v1480 ~v1482 ~v1486 ~v1487 ~v1488 ~v1489 ~v149 ~v1490 ~v1491 ~v1492 v1493 ~v1497 ~v1498 ~v1499 ~v15 ~v150 ~v1500 ~v1501 ~v1502 ~v1503 ~v1504 ~v1505 ~v1506 v1507 ~v1508 ~v151 ~v1513 ~v1514 ~v1516 ~v1517 ~v1518 ~v1519 ~v152 ~v1520 ~v1521 ~v1522 ~v1523 ~v1524 ~v1525 ~v1526 ~v1527 ~v1528 ~v1529 ~v153 ~v1530 ~v1531 ~v1532 ~v1534 ~v1535 ~v1536 ~v1537 ~v1538 ~v154 ~v1541 ~v1542 ~v1543 ~v1544 ~v1545 ~v1546 ~v1548 ~v155 ~v1552 ~v1553 ~v1554 ~v1557 ~v1558 v1559 ~v156 ~v1561 ~v1564 ~v1566 ~v1568 ~v1569 ~v157 ~v1572 ~v1573 ~v1574 ~v1575 ~v1576 ~v1577 ~v1578 ~v1579 ~v158 ~v1580 ~v1581 ~v1582 ~v1583 ~v1584 ~v1585 ~v1586 ~v1587 ~v1588 ~v1589 ~v159 ~v1590 ~v1591 ~v1592 ~v1594 ~v1595 ~v1596 ~v1597 ~v1598 ~v1599 ~v16 ~v160 ~v1600 ~v1601 ~v1602 ~v1603 ~v1606 ~v1608 ~v1609 ~v161 ~v1610 ~v1611 ~v1612 ~v1613 ~v1614 ~v1615 ~v1616 ~v1617 ~v1618 ~v1619 ~v162 ~v1620 ~v1621 ~v1623 ~v1625 ~v1626 ~v1627 ~v163 ~v1630 ~v1631 ~v1632 ~v1633 ~v1634 ~v1635 ~v1636 ~v1637 ~v1638 ~v1639 ~v164 ~v1640 ~v1641 ~v1642 ~v1643 ~v1645 ~v1646 ~v165 ~v1651 ~v1652 ~v1653 ~v1654 v1655 ~v166 ~v1661 v1666 ~v1667 ~v1668 v1669 ~v167 ~v1670 ~v1671 ~v1672 ~v1673 ~v1674 ~v1675 ~v1676 ~v1677 ~v1678 ~v1679 ~v168 ~v1680 ~v1681 ~v1682 ~v1683 ~v1684 ~v1685 ~v1686 ~v1687 ~v1688 ~v1689 ~v169 ~v1690 ~v1691 ~v1692 ~v1693 ~v1694 ~v1695 ~v1696 ~v1697 ~v1698 ~v1699 ~v170 ~v1700 ~v1701 ~v1702 ~v1703 ~v1704 ~v1705 ~v1706 ~v1707 ~v1708 ~v1709 ~v171 ~v1710 ~v1711 ~v1712 ~v1713 ~v1714 ~v1715 ~v1716 ~v1717 ~v1718 ~v1719 ~v172 ~v1720 ~v1721 ~v1722 ~v1723 ~v1724 ~v1725 ~v1726 ~v1727 ~v1728 ~v1729 ~v173 ~v1730 ~v1731 ~v1732 ~v1733 ~v1734 ~v1735 ~v1736 ~v1737 ~v1738 ~v1739 ~v174 ~v1740 ~v1741 ~v1742 ~v1743 ~v1744 ~v1745 ~v1746 ~v1747 ~v1748 ~v1749 ~v175 ~v1750 ~v1751 ~v1752 ~v1753 ~v1754 ~v1755 ~v1756 ~v1757 ~v1758 ~v1759 ~v176 ~v1760 ~v1761 ~v1762 ~v1763 ~v1764 ~v1765 ~v1766 ~v1767 ~v1768 ~v1769 ~v177 ~v1770 ~v1771 ~v1772 ~v1773 ~v1774 ~v1775 ~v1776 ~v1777 ~v1778 ~v1779 ~v1780 ~v1781 ~v1782 ~v1783 ~v1784 ~v1785 ~v1786 ~v1787 ~v1788 ~v1789 ~v1790 ~v1791 ~v1792 ~v1793 ~v1794 ~v1795 ~v1796 ~v1797 ~v1798 ~v1799 ~v180 ~v1800 ~v1801 ~v1802 ~v1803 ~v1804 ~v1805 ~v1806 ~v1807 ~v1808 ~v1809 ~v181 ~v1810 ~v1811 ~v1812 ~v1813 ~v1814 ~v1815 ~v1816 ~v1817 ~v1818 ~v1819 ~v182 ~v1820 ~v1821 ~v1822 ~v1823 ~v1824 ~v1825 ~v1826 ~v1827 ~v1828 ~v1829 ~v183 ~v1830 ~v1831 ~v1832 ~v1833 ~v1834 ~v1835 ~v1836 ~v1837 ~v1838 ~v1839 ~v184 ~v1840 ~v1841 ~v1842 ~v1843 ~v1844 ~v1845 ~v1846 ~v1847 ~v1848 ~v1849 ~v185 ~v1850 ~v1851 ~v1852 ~v1853 ~v1854 ~v1855 ~v1856 ~v1857 ~v1858 ~v1859 ~v186 ~v1860 ~v1861 ~v1862 ~v1863 ~v1864 ~v1865 ~v1866 ~v1867 ~v1868 ~v1869 ~v187 ~v1870 ~v1871 ~v1872 ~v1873 ~v1874 ~v1875 ~v1876 ~v1877 ~v1878 ~v1879 ~v188 ~v1880 ~v1881 ~v1882 ~v1883 ~v1884 ~v1885 ~v1886 ~v189 ~v1896 ~v1899 ~v19 ~v190 ~v1900 ~v1901 ~v1902 ~v1903 ~v1904 ~v1905 ~v1906 ~v1907 ~v1908 ~v1909 ~v191 ~v1910 ~v1911 ~v1912 ~v1913 ~v1914 ~v1915 ~v1916 ~v1917 ~v1918 ~v1919 ~v192 ~v1920 ~v1921 ~v1922 ~v1923 ~v1924 ~v1925 ~v1926 ~v1927 ~v1928 ~v1929 ~v193 ~v1930 ~v1931 ~v1932 ~v1933 ~v1934 ~v1935 ~v1936 ~v1937 ~v1938 ~v1939 ~v194 ~v1940 ~v1941 ~v1942 ~v1943 ~v1944 ~v1945 ~v1946 ~v1947 ~v1948 ~v1949 ~v195 ~v1950 ~v1951 ~v1952 ~v1953 ~v1954 ~v1955 ~v1956 ~v1957 ~v1958 ~v1959 ~v196 ~v1960 ~v1961 ~v1962 ~v1963 ~v1964 ~v1965 ~v1966 ~v1967 ~v1968 ~v1969 ~v197 ~v1970 ~v1971 ~v1972 ~v1973 ~v1974 ~v1975 ~v1976 ~v1977 ~v1978 ~v1979 ~v198 ~v1980 ~v1981 ~v1982 ~v1983 ~v1984 ~v1985 ~v1986 ~v1987 ~v1988 ~v1989 ~v199 ~v1990 ~v1991 ~v1992 ~v1993 ~v1994 ~v1995 ~v1996 ~v1997 ~v1998 ~v1999 ~v2 ~v20 ~v200 ~v2000 ~v2001 ~v2002 ~v2003 ~v2004 ~v2005 ~v2006 ~v2007 ~v2008 ~v2009 ~v201 ~v2010 ~v2011 ~v2012 ~v2013 ~v2014 ~v2015 ~v2016 ~v2017 ~v2018 ~v2019 ~v202 ~v2020 ~v2021 ~v2022 ~v2023 ~v2024 ~v2025 ~v2026 ~v2027 ~v2028 ~v2029 ~v203 ~v2030 ~v2031 ~v2032 ~v2033 ~v2034 ~v2035 ~v2036 ~v2037 ~v2038 ~v2039 ~v204 ~v2040 ~v2041 ~v2042 ~v2043 ~v2044 ~v2045 ~v2046 ~v2047 v2048 ~v2049 ~v205 ~v2050 ~v2051 ~v2052 ~v2053 ~v2054 ~v206 ~v2060 ~v2061 ~v2065 ~v2066 ~v2067 ~v207 ~v2074 ~v2076 v2077 ~v2079 ~v208 ~v2080 ~v2082 ~v2083 ~v2084 ~v2085 ~v2086 ~v2087 ~v2089 ~v209 ~v2090 ~v2091 ~v2093 ~v2094 ~v2097 ~v2098 ~v2099 ~v21 ~v210 ~v2100 ~v2101 ~v2102 ~v2103 ~v2104 ~v2105 ~v2106 ~v2107 ~v2108 ~v2109 ~v211 ~v2110 ~v2111 ~v2112 ~v2119 ~v212 ~v2120 ~v2121 ~v2122 ~v2123 ~v2124 ~v2125 ~v2128 ~v2129 ~v213 ~v2130 ~v2131 ~v2138 ~v2139 ~v214 ~v2140 ~v2141 ~v2142 ~v2143 ~v2144 ~v2145 ~v2146 ~v2147 ~v2148 ~v2149 ~v215 ~v2151 ~v2153 ~v2155 ~v2156 ~v2157 ~v2158 ~v2159 ~v216 ~v2160 ~v2162 ~v2163 ~v2164 ~v2165 ~v2166 ~v2167 ~v2168 ~v2169 ~v217 ~v2170 ~v2171 ~v2172 ~v2174 ~v2175 ~v2176 ~v2177 ~v2178 ~v2179 ~v218 ~v2180 ~v2181 ~v2182 ~v2183 ~v2184 ~v2185 ~v2186 ~v2187 ~v2188 ~v2189 ~v219 ~v2190 ~v2191 ~v2192 ~v2193 ~v2194 ~v2195 ~v2196 ~v2197 ~v2198 ~v2199 ~v22 ~v220 ~v2200 ~v2201 ~v2202 ~v2203 ~v2204 ~v2205 ~v2206 ~v2207 ~v2208 ~v2209 ~v221 ~v2210 ~v2211 ~v2212 ~v2213 ~v2214 ~v2215 ~v2216 ~v2217 ~v2218 ~v2219 ~v222 ~v2220 ~v2221 ~v2222 ~v2223 ~v2224 ~v2225 ~v2226 ~v2227 ~v2228 ~v2229 ~v223 ~v2230 ~v2231 ~v2232 ~v2233 ~v2234 ~v2235 ~v2236 v2237 ~v2238 ~v2239 ~v224 v2240 ~v2241 ~v2242 ~v2243 ~v2244 ~v2245 ~v2246 ~v2248 ~v225 ~v2250 ~v2251 ~v2252 ~v2253 ~v2256 ~v2257 ~v2258 ~v2259 ~v226 ~v2260 ~v2261 ~v2262 ~v2263 ~v2264 ~v2265 ~v2266 ~v2267 ~v2268 ~v227 ~v2270 ~v2271 ~v2272 ~v2273 ~v2275 ~v2278 ~v2279 ~v228 ~v2280 ~v2281 ~v2282 ~v2284 ~v2288 ~v229 ~v2292 ~v2293 ~v2296 ~v2298 ~v23 ~v230 ~v2305 ~v2306 ~v2307 ~v2308 ~v2309 ~v231 ~v2310 ~v2311 ~v2312 ~v2313 ~v2315 ~v2317 ~v2318 ~v232 ~v2321 ~v2322 ~v2323 ~v2324 ~v2325 ~v2326 ~v2327 ~v2328 ~v233 ~v2330 ~v2331 ~v2332 ~v2334 ~v2335 ~v2336 ~v2337 ~v2338 ~v2339 ~v234 ~v2340 ~v2341 ~v2342 ~v2343 ~v2344 ~v2345 ~v2346 ~v2347 ~v2348 ~v2349 ~v235 ~v2350 ~v236 ~v2362 ~v2364 ~v2367 ~v2368 ~v2369 ~v237 ~v2371 ~v2372 ~v2373 ~v2374 ~v2375 ~v2376 ~v2377 ~v2378 ~v2379 ~v238 ~v2380 ~v2381 ~v2382 ~v2383 ~v2384 ~v2385 ~v2386 ~v2387 ~v2388 ~v2389 ~v239 ~v2390 ~v2391 ~v2392 v2393 ~v2394 ~v2395 ~v2396 ~v2397 ~v2398 ~v2399 ~v24 ~v240 ~v2400 ~v2401 ~v2402 ~v2403 ~v2404 ~v2405 ~v2406 ~v2407 ~v2408 ~v2409 ~v241 ~v2410 ~v2411 ~v2412 ~v2413 ~v2414 ~v2415 v2416 ~v2417 ~v2418 ~v2419 ~v242 ~v2420 ~v2421 ~v2422 ~v2423 ~v2424 ~v2425 ~v2426 ~v2427 ~v2428 ~v2429 ~v243 ~v2430 ~v2433 ~v2434 ~v2437 ~v2438 ~v2439 ~v244 ~v2440 ~v2441 ~v2442 ~v2443 ~v2444 ~v2445 ~v2446 ~v2447 ~v2448 ~v2449 ~v245 ~v2450 ~v2451 ~v2452 ~v2453 ~v2454 ~v2455 ~v2456 ~v2457 ~v2458 ~v2459 ~v246 ~v2460 ~v2461 ~v2462 ~v2463 ~v2464 ~v2465 ~v2466 ~v2467 ~v2468 ~v2469 ~v247 ~v2470 ~v2471 ~v2472 ~v2473 ~v2474 ~v2475 ~v2476 ~v2477 ~v2478 ~v2479 ~v248 ~v2480 ~v2481 ~v2482 ~v2483 ~v2484 ~v2485 ~v2486 ~v2487 ~v2488 ~v2489 ~v249 ~v2490 ~v2491 ~v2492 ~v2493 ~v2494 ~v2495 ~v2496 ~v2497 v2498 ~v2499 ~v25 ~v250 v2500 v2501 ~v2504 ~v2505 v2506 ~v2507 ~v2508 ~v2509 ~v251 ~v2510 ~v2511 ~v2512 ~v2513 ~v2514 ~v2515 ~v2516 ~v2517 ~v2518 ~v2519 ~v252 ~v2520 ~v2521 ~v2522 ~v2523 ~v2524 ~v2525 ~v2526 ~v2527 ~v2528 ~v2529 ~v253 ~v2530 ~v2531 ~v2532 ~v2533 ~v2534 ~v2535 ~v2536 ~v2537 ~v2538 ~v2539 ~v254 ~v2540 ~v2541 ~v2542 ~v2543 ~v2544 ~v2545 ~v2546 ~v2547 ~v2548 ~v2549 ~v255 ~v2550 ~v2551 ~v2552 ~v2553 ~v2554 ~v2555 ~v2556 ~v2557 ~v2558 ~v2559 ~v256 ~v2560 ~v2561 ~v2562 ~v2563 ~v2564 ~v2565 ~v2566 ~v2567 ~v2568 ~v2569 ~v257 ~v2570 ~v2571 ~v2572 ~v2573 ~v2574 ~v2575 ~v2576 ~v2577 ~v2578 ~v2579 ~v258 ~v2580 ~v2581 ~v2582 ~v2583 ~v2584 ~v2585 ~v2586 ~v2587 ~v2588 ~v2589 ~v259 ~v2590 ~v2591 ~v2592 ~v2593 ~v2594 v2595 ~v2596 v2597 ~v2598 ~v2599 ~v26 ~v260 ~v2600 ~v2601 ~v2602 ~v2603 ~v2604 ~v2605 ~v2606 ~v2607 ~v2608 v2609 ~v261 ~v2610 ~v2611 ~v2612 ~v2617 ~v2619 ~v262 ~v2622 ~v2627 ~v2628 ~v2629 ~v263 ~v2630 v2631 ~v2632 ~v2633 ~v264 ~v2640 ~v2642 ~v265 ~v266 ~v267 ~v2677 ~v268 v2681 v2682 v2683 ~v2684 ~v2685 ~v2686 ~v269 ~v27 ~v270 ~v271 ~v272 ~v273 ~v274 ~v275 ~v276 ~v277 ~v278 ~v279 ~v28 ~v280 ~v281 ~v282 ~v283 ~v284 ~v285 ~v286 ~v287 ~v288 ~v289 ~v29 ~v290 ~v291 ~v292 ~v293 ~v294 ~v295 ~v296 ~v297 ~v298 ~v299 ~v3 ~v30 ~v300 ~v301 ~v302 ~v303 ~v304 ~v305 ~v306 ~v307 ~v308 ~v309 ~v31 ~v310 ~v311 ~v312 ~v313 ~v314 v315 ~v316 ~v317 ~v32 ~v320 ~v321 ~v322 ~v323 ~v324 ~v325 ~v326 ~v327 ~v328 ~v329 ~v33 ~v330 ~v331 ~v332 ~v333 ~v334 ~v335 ~v336 ~v337 ~v338 ~v339 ~v34 ~v340 ~v341 ~v342 ~v343 ~v344 ~v345 ~v346 ~v347 ~v348 ~v349 ~v35 ~v350 ~v351 ~v352 ~v353 ~v354 ~v355 ~v356 ~v358 ~v36 ~v362 ~v363 ~v364 ~v365 ~v366 ~v367 ~v368 ~v369 ~v37 ~v370 ~v371 ~v378 ~v38 ~v380 ~v381 ~v386 ~v387 v39 ~v390 ~v391 ~v392 ~v393 ~v395 ~v396 ~v397 ~v398 ~v399 ~v4 ~v40 ~v406 ~v41 ~v413 ~v414 ~v417 ~v419 ~v42 ~v420 ~v422 ~v423 ~v43 ~v431 ~v433 ~v435 ~v437 ~v44 ~v45 ~v46 ~v461 ~v462 ~v464 ~v466 ~v467 ~v468 ~v469 ~v47 ~v470 ~v471 ~v472 ~v473 ~v474 ~v475 ~v476 ~v477 ~v478 ~v48 ~v481 ~v482 ~v483 ~v484 ~v485 ~v487 ~v488 ~v489 ~v49 ~v490 ~v491 ~v495 ~v5 ~v50 ~v500 ~v505 ~v509 ~v51 ~v510 v511 ~v512 ~v515 ~v517 ~v52 ~v53 ~v54 ~v55 ~v56 ~v562 ~v564 ~v57 ~v570 ~v577 ~v578 ~v579 ~v58 ~v580 ~v581 ~v582 ~v583 ~v584 ~v585 ~v586 ~v587 ~v588 ~v589 ~v59 ~v590 ~v591 ~v592 ~v593 ~v594 ~v595 ~v597 ~v599 ~v6 ~v60 ~v600 ~v61 ~v610 ~v62 ~v624 ~v629 ~v64 ~v646 ~v65 ~v656 ~v657 ~v658 ~v659 ~v66 ~v660 ~v67 v675 v676 ~v677 ~v678 ~v68 ~v680 v681 v682 ~v683 ~v685 ~v689 ~v69 v697 ~v7 ~v70 ~v708 ~v71 ~v72 ~v726 ~v727 ~v73 ~v74 ~v75 ~v753 ~v755 ~v756 ~v757 ~v758 ~v759 ~v76 ~v760 ~v761 ~v762 ~v763 ~v764 ~v765 ~v766 ~v767 ~v768 ~v769 ~v77 ~v770 ~v771 ~v772 ~v773 ~v774 ~v775 ~v776 ~v777 ~v778 ~v779 ~v78 ~v780 ~v783 ~v784 ~v785 ~v786 ~v787 ~v788 ~v789 ~v79 ~v790 ~v792 v797 v798 v799 ~v8 ~v80 ~v801 ~v802 ~v803 ~v804 ~v805 ~v806 ~v809 ~v81 ~v811 ~v812 ~v813 ~v814 ~v815 ~v816 v817 ~v818 ~v819 ~v82 ~v820 ~v821 ~v822 ~v826 ~v827 ~v828 ~v829 ~v83 ~v830 ~v831 ~v832 ~v833 ~v834 ~v836 ~v837 ~v838 ~v839 ~v84 ~v840 ~v841 ~v842 ~v843 ~v844 ~v845 ~v846 ~v847 ~v848 ~v849 ~v85 v853 ~v855 ~v856 ~v857 ~v858 ~v859 ~v86 ~v863 ~v867 ~v868 ~v869 ~v87 ~v870 ~v871 ~v872 ~v873 ~v874 ~v875 ~v876 ~v877 ~v878 ~v879 ~v88 ~v880 ~v881 ~v882 ~v883 ~v884 ~v885 ~v886 ~v887 ~v888 ~v889 ~v89 ~v890 ~v891 ~v892 ~v893 ~v894 ~v895 ~v899 ~v90 ~v900 ~v901 ~v902 ~v903 ~v904 ~v905 ~v906 ~v907 ~v908 ~v909 ~v91 ~v910 ~v911 ~v912 ~v913 ~v914 ~v915 ~v916 ~v917 ~v918 ~v919 ~v92 ~v920 ~v921 ~v922 ~v923 ~v924 ~v925 ~v926 ~v927 ~v928 ~v929 ~v93 ~v930 ~v931 ~v932 ~v933 ~v934 ~v935 ~v936 ~v937 ~v938 ~v939 ~v94 ~v940 ~v941 ~v942 ~v943 ~v944 ~v945 ~v946 ~v947 ~v948 ~v949 v95 ~v950 ~v951 ~v952 ~v953 ~v954 ~v955 ~v956 ~v957 ~v958 ~v959 ~v96 ~v960 ~v961 ~v962 ~v963 ~v964 ~v966 ~v967 ~v968 ~v969 ~v97 ~v970 ~v971 ~v972 ~v975 ~v976 ~v977 ~v98 ~v983 ~v984 ~v985 ~v986 ~v987 ~v988 ~v989 ~v99 ~v990 ~v991 ~v992 ~v993 ~v994 ~v995 ~v996 ~v997 ~v998 ~v999 +~v0 ~v1 ~v10 ~v100 ~v1000 ~v1001 ~v1002 ~v1003 ~v1004 ~v1005 ~v1006 ~v1007 ~v1008 ~v1009 ~v101 ~v1010 ~v1011 ~v1012 ~v1013 ~v1014 ~v1015 ~v1016 ~v1017 ~v1018 ~v1019 ~v102 ~v1020 ~v1021 ~v1022 ~v1023 ~v1024 ~v1025 ~v1026 ~v1027 ~v1028 ~v1029 ~v103 ~v1030 ~v1031 ~v1032 ~v1033 ~v1034 ~v1035 ~v1036 ~v1037 ~v1038 ~v1039 ~v104 ~v1040 ~v1041 ~v1042 ~v1043 ~v1044 ~v1045 ~v1046 ~v1047 ~v1048 ~v1049 ~v105 ~v1050 ~v1051 ~v1052 ~v1053 ~v1054 ~v1055 ~v1056 ~v1057 ~v1058 ~v1059 ~v106 ~v1060 ~v1061 ~v1062 ~v1063 ~v1064 ~v1065 ~v1066 ~v1067 ~v1068 ~v1069 ~v107 ~v1070 ~v1071 ~v1072 ~v1073 ~v1074 ~v1075 ~v1076 ~v1077 ~v1078 ~v1079 ~v108 ~v1080 ~v1081 ~v1082 ~v1083 ~v1084 ~v1085 ~v1086 ~v1087 ~v1088 ~v1089 ~v109 ~v1090 ~v1091 ~v1092 ~v1093 ~v1094 ~v1095 ~v1096 ~v1097 ~v1098 ~v1099 ~v110 ~v1100 ~v1101 ~v1102 ~v1103 ~v1104 ~v1105 ~v1106 ~v1107 ~v1108 ~v1109 ~v111 ~v1110 ~v1111 ~v1112 ~v1113 ~v1114 ~v1115 ~v1116 ~v1117 ~v1118 ~v1119 ~v112 ~v1120 ~v1121 ~v1122 ~v1123 ~v1124 ~v1125 ~v1126 ~v1127 ~v1128 ~v1129 ~v113 ~v1130 ~v1131 ~v1132 ~v1133 ~v1134 ~v1135 ~v1136 ~v1137 ~v1138 ~v1139 ~v114 ~v1140 ~v1141 ~v1142 ~v1143 ~v1144 ~v1145 ~v1146 ~v1147 ~v1148 ~v1149 ~v115 ~v1150 ~v1151 ~v1152 ~v1153 ~v1154 ~v1155 ~v1156 ~v1157 ~v1158 ~v1159 ~v116 ~v1160 ~v1161 ~v1162 ~v1163 ~v1164 ~v1165 ~v1166 ~v1167 ~v1168 ~v1169 ~v117 ~v1170 ~v1171 ~v1172 ~v1173 ~v1174 ~v1175 ~v1176 ~v1177 ~v1178 ~v1179 ~v118 ~v1180 ~v1181 ~v1182 ~v1183 ~v1184 ~v1185 ~v1186 ~v1187 ~v1188 ~v1189 ~v119 ~v1190 ~v1191 ~v1192 ~v1193 ~v1194 ~v1195 ~v1196 ~v1197 ~v1198 ~v1199 ~v120 ~v1200 ~v1201 ~v1202 ~v1203 ~v1204 ~v1205 ~v1206 ~v1207 ~v1208 ~v1209 ~v121 ~v1210 ~v1211 ~v1212 ~v1213 ~v1214 ~v1215 ~v1216 ~v1217 ~v1218 ~v1219 ~v122 ~v1220 ~v1221 ~v1222 ~v1223 ~v1224 ~v1225 ~v1226 ~v1227 ~v1228 ~v1229 ~v123 ~v1230 ~v1231 ~v1232 ~v1233 ~v1234 ~v1235 ~v1236 ~v1237 ~v1238 ~v1239 ~v124 ~v1240 ~v1241 ~v1242 ~v1243 ~v1244 ~v1245 ~v1246 ~v1247 ~v1248 ~v1249 ~v125 ~v1250 ~v1251 ~v1252 ~v1253 ~v1254 ~v1255 ~v1256 ~v1257 ~v1258 ~v1259 ~v126 ~v1260 ~v1261 ~v1262 ~v1263 ~v1264 ~v1265 ~v1266 ~v1267 ~v1268 ~v1269 ~v127 ~v1270 ~v1271 ~v1272 ~v1273 ~v1274 ~v1275 ~v1276 ~v1277 ~v1278 ~v1279 ~v128 ~v1280 ~v1281 ~v1282 ~v1283 ~v1284 ~v1285 ~v1286 ~v1287 ~v1288 ~v1289 ~v129 ~v1290 ~v1291 ~v1292 ~v1293 ~v1294 ~v1295 ~v1296 ~v1297 ~v1298 ~v1299 ~v13 ~v130 ~v1300 ~v1301 ~v1302 ~v1303 ~v1304 ~v1305 ~v1306 ~v1307 ~v1308 ~v1309 ~v131 ~v1310 ~v1311 ~v1312 ~v1313 ~v1314 ~v1315 ~v1316 ~v1317 ~v1318 ~v1319 ~v132 ~v1320 ~v1321 ~v1322 ~v1323 ~v1324 ~v1325 ~v1326 ~v1327 ~v1328 ~v1329 ~v133 ~v1330 ~v1331 ~v1332 ~v1333 ~v1334 ~v1335 ~v1336 ~v1337 ~v1338 ~v1339 ~v134 ~v1340 ~v1341 ~v1342 ~v1343 ~v1344 ~v1345 ~v1346 ~v1347 ~v1348 ~v1349 ~v135 ~v1351 ~v1352 ~v1353 ~v1357 ~v1358 ~v1359 ~v136 ~v1360 ~v1367 ~v1368 ~v137 ~v1370 ~v1371 ~v1375 ~v1376 ~v138 ~v1382 ~v1383 ~v1384 v1385 ~v1386 ~v1387 ~v139 ~v1394 ~v1395 ~v1396 ~v1397 ~v1398 ~v14 ~v140 ~v1403 ~v1406 ~v1407 ~v1409 ~v1410 ~v1411 ~v1412 ~v1413 v1414 ~v1415 ~v1416 ~v1417 ~v1418 ~v1419 ~v142 ~v1420 ~v1422 ~v1425 ~v1426 ~v1427 ~v1428 ~v1429 ~v143 ~v1430 ~v1431 ~v1432 ~v1433 ~v1434 ~v1435 ~v1436 ~v1437 ~v1438 ~v1439 ~v144 ~v1440 ~v1441 ~v1442 ~v1443 ~v1444 ~v1445 ~v1446 ~v1448 ~v145 ~v1451 ~v1456 ~v1457 ~v146 v1461 ~v1462 ~v1463 v1464 ~v1465 ~v147 ~v1470 v1471 ~v1472 ~v1476 ~v1478 ~v1479 ~v148 ~v1480 ~v1482 ~v1486 ~v1487 ~v1488 ~v1489 ~v149 ~v1490 ~v1491 ~v1492 v1493 ~v1497 ~v1498 ~v1499 ~v15 ~v150 ~v1500 ~v1501 ~v1502 ~v1503 ~v1504 ~v1505 ~v1506 v1507 ~v1508 ~v151 ~v1513 ~v1514 ~v1516 ~v1517 ~v1518 ~v1519 ~v152 ~v1520 ~v1521 ~v1522 ~v1523 ~v1524 ~v1525 ~v1526 ~v1527 ~v1528 ~v1529 ~v153 ~v1530 ~v1531 ~v1532 ~v1534 ~v1535 ~v1536 ~v1537 ~v1538 ~v154 ~v1541 ~v1542 ~v1543 ~v1544 ~v1545 ~v1546 ~v1548 ~v155 ~v1552 ~v1553 ~v1554 ~v1557 ~v1558 v1559 ~v156 ~v1561 ~v1564 ~v1566 ~v1568 ~v1569 ~v157 ~v1572 ~v1573 ~v1574 ~v1575 ~v1576 ~v1577 ~v1578 ~v1579 ~v158 ~v1580 ~v1581 ~v1582 ~v1583 ~v1584 ~v1585 ~v1586 ~v1587 ~v1588 ~v1589 ~v159 ~v1590 ~v1591 ~v1592 ~v1594 ~v1595 ~v1596 ~v1597 ~v1598 ~v1599 ~v16 ~v160 ~v1600 ~v1601 ~v1602 ~v1603 ~v1606 ~v1608 ~v1609 ~v161 ~v1610 ~v1611 ~v1612 ~v1613 ~v1614 ~v1615 ~v1616 ~v1617 ~v1618 ~v1619 ~v162 ~v1620 ~v1621 ~v1623 ~v1625 ~v1626 ~v1627 ~v163 ~v1630 ~v1631 ~v1632 ~v1633 ~v1634 ~v1635 ~v1636 ~v1637 ~v1638 ~v1639 ~v164 ~v1640 ~v1641 ~v1642 ~v1643 ~v1645 ~v1646 ~v165 ~v1651 ~v1652 ~v1653 ~v1654 v1655 ~v166 ~v1661 v1663 v1666 ~v1667 ~v1668 v1669 ~v167 ~v1670 ~v1671 ~v1672 ~v1673 ~v1674 ~v1675 ~v1676 ~v1677 ~v1678 ~v1679 ~v168 ~v1680 ~v1681 ~v1682 ~v1683 ~v1684 ~v1685 ~v1686 ~v1687 ~v1688 ~v1689 ~v169 ~v1690 ~v1691 ~v1692 ~v1693 ~v1694 ~v1695 ~v1696 ~v1697 ~v1698 ~v1699 ~v170 ~v1700 ~v1701 ~v1702 ~v1703 ~v1704 ~v1705 ~v1706 ~v1707 ~v1708 ~v1709 ~v171 ~v1710 ~v1711 ~v1712 ~v1713 ~v1714 ~v1715 ~v1716 ~v1717 ~v1718 ~v1719 ~v172 ~v1720 ~v1721 ~v1722 ~v1723 ~v1724 ~v1725 ~v1726 ~v1727 ~v1728 ~v1729 ~v173 ~v1730 ~v1731 ~v1732 ~v1733 ~v1734 ~v1735 ~v1736 ~v1737 ~v1738 ~v1739 ~v174 ~v1740 ~v1741 ~v1742 ~v1743 ~v1744 ~v1745 ~v1746 ~v1747 ~v1748 ~v1749 ~v175 ~v1750 ~v1751 ~v1752 ~v1753 ~v1754 ~v1755 ~v1756 ~v1757 ~v1758 ~v1759 ~v176 ~v1760 ~v1761 ~v1762 ~v1763 ~v1764 ~v1765 ~v1766 ~v1767 ~v1768 ~v1769 ~v177 ~v1770 ~v1771 ~v1772 ~v1773 ~v1774 ~v1775 ~v1776 ~v1777 ~v1778 ~v1779 ~v1780 ~v1781 ~v1782 ~v1783 ~v1784 ~v1785 ~v1786 ~v1787 ~v1788 ~v1789 ~v1790 ~v1791 ~v1792 ~v1793 ~v1794 ~v1795 ~v1796 ~v1797 ~v1798 ~v1799 ~v180 ~v1800 ~v1801 ~v1802 ~v1803 ~v1804 ~v1805 ~v1806 ~v1807 ~v1808 ~v1809 ~v181 ~v1810 ~v1811 ~v1812 ~v1813 ~v1814 ~v1815 ~v1816 ~v1817 ~v1818 ~v1819 ~v182 ~v1820 ~v1821 ~v1822 ~v1823 ~v1824 ~v1825 ~v1826 ~v1827 ~v1828 ~v1829 ~v183 ~v1830 ~v1831 ~v1832 ~v1833 ~v1834 ~v1835 ~v1836 ~v1837 ~v1838 ~v1839 ~v184 ~v1840 ~v1841 ~v1842 ~v1843 ~v1844 ~v1845 ~v1846 ~v1847 ~v1848 ~v1849 ~v185 ~v1850 ~v1851 ~v1852 ~v1853 ~v1854 ~v1855 ~v1856 ~v1857 ~v1858 ~v1859 ~v186 ~v1860 ~v1861 ~v1862 ~v1863 ~v1864 ~v1865 ~v1866 ~v1867 ~v1868 ~v1869 ~v187 ~v1870 ~v1871 ~v1872 ~v1873 ~v1874 ~v1875 ~v1876 ~v1877 ~v1878 ~v1879 ~v188 ~v1880 ~v1881 ~v1882 ~v1883 ~v1884 ~v1885 ~v1886 ~v189 ~v1896 ~v1899 ~v19 ~v190 ~v1900 ~v1901 ~v1902 ~v1903 ~v1904 ~v1905 ~v1906 ~v1907 ~v1908 ~v1909 ~v191 ~v1910 ~v1911 ~v1912 ~v1913 ~v1914 ~v1915 ~v1916 ~v1917 ~v1918 ~v1919 ~v192 ~v1920 ~v1921 ~v1922 ~v1923 ~v1924 ~v1925 ~v1926 ~v1927 ~v1928 ~v1929 ~v193 ~v1930 ~v1931 ~v1932 ~v1933 ~v1934 ~v1935 ~v1936 ~v1937 ~v1938 ~v1939 ~v194 ~v1940 ~v1941 ~v1942 ~v1943 ~v1944 ~v1945 ~v1946 ~v1947 ~v1948 ~v1949 ~v195 ~v1950 ~v1951 ~v1952 ~v1953 ~v1954 ~v1955 ~v1956 ~v1957 ~v1958 ~v1959 ~v196 ~v1960 ~v1961 ~v1962 ~v1963 ~v1964 ~v1965 ~v1966 ~v1967 ~v1968 ~v1969 ~v197 ~v1970 ~v1971 ~v1972 ~v1973 ~v1974 ~v1975 ~v1976 ~v1977 ~v1978 ~v1979 ~v198 ~v1980 ~v1981 ~v1982 ~v1983 ~v1984 ~v1985 ~v1986 ~v1987 ~v1988 ~v1989 ~v199 ~v1990 ~v1991 ~v1992 ~v1993 ~v1994 ~v1995 ~v1996 ~v1997 ~v1998 ~v1999 ~v2 ~v20 ~v200 ~v2000 ~v2001 ~v2002 ~v2003 ~v2004 ~v2005 ~v2006 ~v2007 ~v2008 ~v2009 ~v201 ~v2010 ~v2011 ~v2012 ~v2013 ~v2014 ~v2015 ~v2016 ~v2017 ~v2018 ~v2019 ~v202 ~v2020 ~v2021 ~v2022 ~v2023 ~v2024 ~v2025 ~v2026 ~v2027 ~v2028 ~v2029 ~v203 ~v2030 ~v2031 ~v2032 ~v2033 ~v2034 ~v2035 ~v2036 ~v2037 ~v2038 ~v2039 ~v204 ~v2040 ~v2041 ~v2042 ~v2043 ~v2044 ~v2045 ~v2046 ~v2047 v2048 ~v2049 ~v205 ~v2050 ~v2051 ~v2052 ~v2053 ~v2054 ~v206 ~v2060 ~v2061 ~v2065 ~v2066 ~v2067 ~v207 ~v2074 ~v2076 v2077 ~v2079 ~v208 ~v2080 ~v2082 ~v2083 ~v2084 ~v2085 ~v2086 ~v2087 ~v2089 ~v209 ~v2090 ~v2091 ~v2093 ~v2094 ~v2097 ~v2098 ~v2099 ~v21 ~v210 ~v2100 ~v2101 ~v2102 ~v2103 ~v2104 ~v2105 ~v2106 ~v2107 ~v2108 ~v2109 ~v211 ~v2110 ~v2111 ~v2112 ~v2119 ~v212 ~v2120 ~v2121 ~v2122 ~v2123 ~v2124 ~v2125 ~v2128 ~v2129 ~v213 ~v2130 ~v2131 ~v2138 ~v2139 ~v214 ~v2140 ~v2141 ~v2142 ~v2143 ~v2144 ~v2145 ~v2146 ~v2147 ~v2148 ~v2149 ~v215 ~v2151 ~v2153 ~v2155 ~v2156 ~v2157 ~v2158 ~v2159 ~v216 ~v2160 ~v2162 ~v2163 ~v2164 ~v2165 ~v2166 ~v2167 ~v2168 ~v2169 ~v217 ~v2170 ~v2171 ~v2172 ~v2174 ~v2175 ~v2176 ~v2177 ~v2178 ~v2179 ~v218 ~v2180 ~v2181 ~v2182 ~v2183 ~v2184 ~v2185 ~v2186 ~v2187 ~v2188 ~v2189 ~v219 ~v2190 ~v2191 ~v2192 ~v2193 ~v2194 ~v2195 ~v2196 ~v2197 ~v2198 ~v2199 ~v22 ~v220 ~v2200 ~v2201 ~v2202 ~v2203 ~v2204 ~v2205 ~v2206 ~v2207 ~v2208 ~v2209 ~v221 ~v2210 ~v2211 ~v2212 ~v2213 ~v2214 ~v2215 ~v2216 ~v2217 ~v2218 ~v2219 ~v222 ~v2220 ~v2221 ~v2222 ~v2223 ~v2224 ~v2225 ~v2226 ~v2227 ~v2228 ~v2229 ~v223 ~v2230 ~v2231 ~v2232 ~v2233 ~v2234 ~v2235 ~v2236 v2237 ~v2238 ~v2239 ~v224 v2240 ~v2241 ~v2242 ~v2243 ~v2244 ~v2245 ~v2246 ~v2248 ~v225 ~v2250 ~v2251 ~v2252 ~v2253 ~v2256 ~v2257 ~v2258 ~v2259 ~v226 ~v2260 ~v2261 ~v2262 ~v2263 ~v2264 ~v2265 ~v2266 ~v2267 ~v2268 ~v227 ~v2270 ~v2271 ~v2272 ~v2273 ~v2275 ~v2278 ~v2279 ~v228 ~v2280 ~v2281 ~v2282 ~v2284 ~v2288 ~v229 ~v2292 ~v2293 ~v2296 ~v2298 ~v23 ~v230 ~v2305 ~v2306 ~v2307 ~v2308 ~v2309 ~v231 ~v2310 ~v2311 ~v2312 ~v2313 ~v2315 ~v2317 ~v2318 ~v232 ~v2321 ~v2322 ~v2323 ~v2324 ~v2325 ~v2326 ~v2327 ~v2328 ~v233 ~v2330 ~v2331 ~v2332 ~v2334 ~v2335 ~v2336 ~v2337 ~v2338 ~v2339 ~v234 ~v2340 ~v2341 ~v2342 ~v2343 ~v2344 ~v2345 ~v2346 ~v2347 ~v2348 ~v2349 ~v235 ~v2350 ~v236 ~v2362 ~v2364 ~v2367 ~v2368 ~v2369 ~v237 ~v2371 ~v2372 ~v2373 ~v2374 ~v2375 ~v2376 ~v2377 ~v2378 ~v2379 ~v238 ~v2380 ~v2381 ~v2382 ~v2383 ~v2384 ~v2385 ~v2386 ~v2387 ~v2388 ~v2389 ~v239 ~v2390 ~v2391 ~v2392 v2393 ~v2394 ~v2395 ~v2396 ~v2397 ~v2398 ~v2399 ~v24 ~v240 ~v2400 ~v2401 ~v2402 ~v2403 ~v2404 ~v2405 ~v2406 ~v2407 ~v2408 ~v2409 ~v241 ~v2410 ~v2411 ~v2412 ~v2413 ~v2414 ~v2415 v2416 ~v2417 ~v2418 ~v2419 ~v242 ~v2420 ~v2421 ~v2422 ~v2423 ~v2424 ~v2425 ~v2426 ~v2427 ~v2428 ~v2429 ~v243 ~v2430 ~v2433 ~v2434 ~v2437 ~v2438 ~v2439 ~v244 ~v2440 ~v2441 ~v2442 ~v2443 ~v2444 ~v2445 ~v2446 ~v2447 ~v2448 ~v2449 ~v245 ~v2450 ~v2451 ~v2452 ~v2453 ~v2454 ~v2455 ~v2456 ~v2457 ~v2458 ~v2459 ~v246 ~v2460 ~v2461 ~v2462 ~v2463 ~v2464 ~v2465 ~v2466 ~v2467 ~v2468 ~v2469 ~v247 ~v2470 ~v2471 ~v2472 ~v2473 ~v2474 ~v2475 ~v2476 ~v2477 ~v2478 ~v2479 ~v248 ~v2480 ~v2481 ~v2482 ~v2483 ~v2484 ~v2485 ~v2486 ~v2487 ~v2488 ~v2489 ~v249 ~v2490 ~v2491 ~v2492 ~v2493 ~v2494 ~v2495 ~v2496 ~v2497 v2498 ~v2499 ~v25 ~v250 v2500 v2501 ~v2504 ~v2505 v2506 ~v2507 ~v2508 ~v2509 ~v251 ~v2510 ~v2511 ~v2512 ~v2513 ~v2514 ~v2515 ~v2516 ~v2517 ~v2518 ~v2519 ~v252 ~v2520 ~v2521 ~v2522 ~v2523 ~v2524 ~v2525 ~v2526 ~v2527 ~v2528 ~v2529 ~v253 ~v2530 ~v2531 ~v2532 ~v2533 ~v2534 ~v2535 ~v2536 ~v2537 ~v2538 ~v2539 ~v254 ~v2540 ~v2541 ~v2542 ~v2543 ~v2544 ~v2545 ~v2546 ~v2547 ~v2548 ~v2549 ~v255 ~v2550 ~v2551 ~v2552 ~v2553 ~v2554 ~v2555 ~v2556 ~v2557 ~v2558 ~v2559 ~v256 ~v2560 ~v2561 ~v2562 ~v2563 ~v2564 ~v2565 ~v2566 ~v2567 ~v2568 ~v2569 ~v257 ~v2570 ~v2571 ~v2572 ~v2573 ~v2574 ~v2575 ~v2576 ~v2577 ~v2578 ~v2579 ~v258 ~v2580 ~v2581 ~v2582 ~v2583 ~v2584 ~v2585 ~v2586 ~v2587 ~v2588 ~v2589 ~v259 ~v2590 ~v2591 ~v2592 ~v2593 ~v2594 v2595 ~v2596 v2597 ~v2598 ~v2599 ~v26 ~v260 ~v2600 ~v2601 ~v2602 ~v2603 ~v2604 ~v2605 ~v2606 ~v2607 ~v2608 v2609 ~v261 ~v2610 ~v2611 ~v2612 ~v2617 ~v2619 ~v262 ~v2622 ~v2627 ~v2628 ~v2629 ~v263 ~v2630 v2631 ~v2632 ~v2633 ~v264 ~v2640 ~v2642 ~v265 ~v266 ~v267 ~v2677 ~v268 v2681 v2682 v2683 ~v2684 ~v2685 ~v2686 ~v269 ~v27 ~v270 ~v271 ~v272 ~v273 ~v274 ~v275 ~v276 ~v277 ~v278 ~v279 ~v28 ~v280 ~v281 ~v282 ~v283 ~v284 ~v285 ~v286 ~v287 ~v288 ~v289 ~v29 ~v290 ~v291 ~v292 ~v293 ~v294 ~v295 ~v296 ~v297 ~v298 ~v299 ~v3 ~v30 ~v300 ~v301 ~v302 ~v303 ~v304 ~v305 ~v306 ~v307 ~v308 ~v309 ~v31 ~v310 ~v311 ~v312 ~v313 ~v314 v315 ~v316 ~v317 ~v32 ~v320 ~v321 ~v322 ~v323 ~v324 ~v325 ~v326 ~v327 ~v328 ~v329 ~v33 ~v330 ~v331 ~v332 ~v333 ~v334 ~v335 ~v336 ~v337 ~v338 ~v339 ~v34 ~v340 ~v341 ~v342 ~v343 ~v344 ~v345 ~v346 ~v347 ~v348 ~v349 ~v35 ~v350 ~v351 ~v352 ~v353 ~v354 ~v355 ~v356 ~v358 ~v36 ~v362 ~v363 ~v364 ~v365 ~v366 ~v367 ~v368 ~v369 ~v37 ~v370 ~v371 ~v378 ~v38 ~v380 ~v381 ~v386 ~v387 v39 ~v390 ~v391 ~v392 ~v393 ~v395 ~v396 ~v397 ~v398 ~v399 ~v4 ~v40 ~v406 ~v41 ~v413 ~v414 ~v417 ~v419 ~v42 ~v420 ~v422 ~v423 ~v43 ~v431 ~v433 ~v435 ~v437 ~v44 ~v45 ~v46 ~v461 ~v462 ~v464 ~v466 ~v467 ~v468 ~v469 ~v47 ~v470 ~v471 ~v472 ~v473 ~v474 ~v475 ~v476 ~v477 ~v478 ~v48 ~v481 ~v482 ~v483 ~v484 ~v485 ~v487 ~v488 ~v489 ~v49 ~v490 ~v491 ~v495 ~v5 ~v50 ~v500 ~v505 ~v509 ~v51 ~v510 v511 ~v512 ~v515 ~v517 ~v52 ~v53 ~v54 ~v55 ~v56 ~v562 ~v564 ~v57 ~v570 ~v577 ~v578 ~v579 ~v58 ~v580 ~v581 ~v582 ~v583 ~v584 ~v585 ~v586 ~v587 ~v588 ~v589 ~v59 ~v590 ~v591 ~v592 ~v593 ~v594 ~v595 ~v597 ~v599 ~v6 ~v60 ~v600 ~v61 ~v610 ~v62 ~v624 ~v629 ~v64 ~v646 ~v65 ~v656 ~v657 ~v658 ~v659 ~v66 ~v660 ~v67 v675 v676 ~v677 ~v678 ~v68 ~v680 v681 v682 ~v683 ~v685 ~v689 ~v69 v697 ~v7 ~v70 ~v708 ~v71 ~v72 ~v726 ~v727 ~v73 ~v74 ~v75 ~v753 ~v755 ~v756 ~v757 ~v758 ~v759 ~v76 ~v760 ~v761 ~v762 ~v763 ~v764 ~v765 ~v766 ~v767 ~v768 ~v769 ~v77 ~v770 ~v771 ~v772 ~v773 ~v774 ~v775 ~v776 ~v777 ~v778 ~v779 ~v78 ~v780 ~v783 ~v784 ~v785 ~v786 ~v787 ~v788 ~v789 ~v79 ~v790 ~v792 v797 v798 v799 ~v8 ~v80 ~v801 ~v802 ~v803 ~v804 ~v805 ~v806 ~v809 ~v81 ~v811 ~v812 ~v813 ~v814 ~v815 ~v816 v817 ~v818 ~v819 ~v82 ~v820 ~v821 ~v822 ~v826 ~v827 ~v828 ~v829 ~v83 ~v830 ~v831 ~v832 ~v833 ~v834 ~v836 ~v837 ~v838 ~v839 ~v84 ~v840 ~v841 ~v842 ~v843 ~v844 ~v845 ~v846 ~v847 ~v848 ~v849 ~v85 v853 ~v855 ~v856 ~v857 ~v858 ~v859 ~v86 ~v863 ~v867 ~v868 ~v869 ~v87 ~v870 ~v871 ~v872 ~v873 ~v874 ~v875 ~v876 ~v877 ~v878 ~v879 ~v88 ~v880 ~v881 ~v882 ~v883 ~v884 ~v885 ~v886 ~v887 ~v888 ~v889 ~v89 ~v890 ~v891 ~v892 ~v893 ~v894 ~v895 ~v899 ~v90 ~v900 ~v901 ~v902 ~v903 ~v904 ~v905 ~v906 ~v907 ~v908 ~v909 ~v91 ~v910 ~v911 ~v912 ~v913 ~v914 ~v915 ~v916 ~v917 ~v918 ~v919 ~v92 ~v920 ~v921 ~v922 ~v923 ~v924 ~v925 ~v926 ~v927 ~v928 ~v929 ~v93 ~v930 ~v931 ~v932 ~v933 ~v934 ~v935 ~v936 ~v937 ~v938 ~v939 ~v94 ~v940 ~v941 ~v942 ~v943 ~v944 ~v945 ~v946 ~v947 ~v948 ~v949 v95 ~v950 ~v951 ~v952 ~v953 ~v954 ~v955 ~v956 ~v957 ~v958 ~v959 ~v96 ~v960 ~v961 ~v962 ~v963 ~v964 ~v966 ~v967 ~v968 ~v969 ~v97 ~v970 ~v971 ~v972 ~v975 ~v976 ~v977 ~v98 ~v983 ~v984 ~v985 ~v986 ~v987 ~v988 ~v989 ~v99 ~v990 ~v991 ~v992 ~v993 ~v994 ~v995 ~v996 ~v997 ~v998 ~v999 From b0426fe249334ee90c320ee083fd9647618f30ce Mon Sep 17 00:00:00 2001 From: Christoph Zengler Date: Mon, 12 Aug 2019 11:07:58 +0200 Subject: [PATCH 11/16] first implementation of a new feature to get the current formula on the solver --- .../java/org/logicng/solvers/CleaneLing.java | 44 +- .../java/org/logicng/solvers/MiniSat.java | 118 ++++-- .../java/org/logicng/solvers/SATSolver.java | 47 ++- .../solvers/sat/MiniSatStyleSolver.java | 126 +++--- .../java/org/logicng/solvers/sat/SATTest.java | 385 ++++++++++-------- 5 files changed, 448 insertions(+), 272 deletions(-) diff --git a/src/main/java/org/logicng/solvers/CleaneLing.java b/src/main/java/org/logicng/solvers/CleaneLing.java index d75da58e..7e321386 100644 --- a/src/main/java/org/logicng/solvers/CleaneLing.java +++ b/src/main/java/org/logicng/solvers/CleaneLing.java @@ -58,6 +58,7 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; @@ -156,20 +157,30 @@ public void add(final Formula formula, final Proposition proposition) { if (constraint.isCC()) { final EncodingResult result = EncodingResult.resultForCleaneLing(this.f, this); this.ccEncoder.encode(constraint, result); - } else { this.addClauseSet(formula.cnf(), proposition); } - } else { this.addClauseSet(formula.cnf(), proposition); } + } else { + this.addClauseSet(formula.cnf(), proposition); + } + } else { + this.addClauseSet(formula.cnf(), proposition); + } } @Override public void addWithoutUnknown(final Formula formula) { final Assignment restriction = new Assignment(true); - for (final Variable var : formula.variables()) { if (this.name2idx.get(var.name()) == null) { restriction.addLiteral(var.negate()); } } + for (final Variable var : formula.variables()) { + if (this.name2idx.get(var.name()) == null) { + restriction.addLiteral(var.negate()); + } + } this.add(formula.restrict(restriction)); } @Override public CCIncrementalData addIncrementalCC(final PBConstraint cc) { - if (!cc.isCC()) { throw new IllegalArgumentException("Cannot generate an incremental cardinality constraint on a pseudo-Boolean constraint"); } + if (!cc.isCC()) { + throw new IllegalArgumentException("Cannot generate an incremental cardinality constraint on a pseudo-Boolean constraint"); + } final EncodingResult result = EncodingResult.resultForCleaneLing(this.f, this); return this.ccEncoder.encodeIncremental(cc, result); } @@ -190,7 +201,9 @@ protected void addClauseWithRelaxation(final Variable relaxationVar, final Formu @Override public Tristate sat(final SATHandler handler) { - if (this.result != UNDEF) { return this.result; } + if (this.result != UNDEF) { + return this.result; + } this.result = this.solver.solve(handler); return this.result; } @@ -213,7 +226,9 @@ public void reset() { @Override public Assignment model(final Collection variables) { - if (this.result == UNDEF) { throw new IllegalStateException("Cannot get a model as long as the formula is not solved. Call 'sat' first."); } + if (this.result == UNDEF) { + throw new IllegalStateException("Cannot get a model as long as the formula is not solved. Call 'sat' first."); + } return this.result == TRUE ? this.createAssignment(this.solver.model(), variables) : null; } @@ -286,7 +301,9 @@ public void loadState(final SolverState state) { @Override public SortedSet knownVariables() { final SortedSet result = new TreeSet<>(); - for (final String name : this.name2idx.keySet()) { result.add(this.f.variable(name)); } + for (final String name : this.name2idx.keySet()) { + result.add(this.f.variable(name)); + } return result; } @@ -324,8 +341,12 @@ private Assignment createAssignment(final LNGBooleanVector vec, final Collection for (int i = 1; i < vec.size(); i++) { final Variable var = this.f.variable(this.idx2name.get(i)); if (vec.get(i)) { - if (variables == null || variables.contains(var)) { model.addLiteral(var); } - } else if (variables == null || variables.contains(var)) { model.addLiteral(var.negate()); } + if (variables == null || variables.contains(var)) { + model.addLiteral(var); + } + } else if (variables == null || variables.contains(var)) { + model.addLiteral(var.negate()); + } } return model; } @@ -393,4 +414,9 @@ public SortedSet upZeroLiterals() { } return upZeroLiterals; } + + @Override + public Set formulaOnSolver() { + throw new UnsupportedOperationException("The CleaneLing solver does not support returning the formula on the solver"); + } } diff --git a/src/main/java/org/logicng/solvers/MiniSat.java b/src/main/java/org/logicng/solvers/MiniSat.java index fada4303..1b32ba3f 100644 --- a/src/main/java/org/logicng/solvers/MiniSat.java +++ b/src/main/java/org/logicng/solvers/MiniSat.java @@ -28,6 +28,10 @@ package org.logicng.solvers; +import static org.logicng.datastructures.Tristate.FALSE; +import static org.logicng.datastructures.Tristate.TRUE; +import static org.logicng.datastructures.Tristate.UNDEF; + import org.logicng.cardinalityconstraints.CCEncoder; import org.logicng.cardinalityconstraints.CCIncrementalData; import org.logicng.collections.LNGBooleanVector; @@ -49,6 +53,8 @@ import org.logicng.handlers.SATHandler; import org.logicng.propositions.Proposition; import org.logicng.propositions.StandardProposition; +import org.logicng.solvers.datastructures.MSClause; +import org.logicng.solvers.datastructures.MSVariable; import org.logicng.solvers.sat.GlucoseConfig; import org.logicng.solvers.sat.GlucoseSyrup; import org.logicng.solvers.sat.MiniCard; @@ -69,10 +75,6 @@ import java.util.SortedSet; import java.util.TreeSet; -import static org.logicng.datastructures.Tristate.FALSE; -import static org.logicng.datastructures.Tristate.TRUE; -import static org.logicng.datastructures.Tristate.UNDEF; - /** * Wrapper for the MiniSAT-style SAT solvers. * @version 1.3.1 @@ -189,23 +191,26 @@ public void add(final Formula formula, final Proposition proposition) { this.result = UNDEF; if (constraint.isCC()) { if (this.style == SolverStyle.MINICARD) { - if (constraint.comparator() == CType.LE) + if (constraint.comparator() == CType.LE) { ((MiniCard) this.solver).addAtMost(generateClauseVector(Arrays.asList(constraint.operands())), constraint.rhs()); - else if (constraint.comparator() == CType.LT && constraint.rhs() > 3) + } else if (constraint.comparator() == CType.LT && constraint.rhs() > 3) { ((MiniCard) this.solver).addAtMost(generateClauseVector(Arrays.asList(constraint.operands())), constraint.rhs() - 1); - else if (constraint.comparator() == CType.EQ && constraint.rhs() == 1) { + } else if (constraint.comparator() == CType.EQ && constraint.rhs() == 1) { ((MiniCard) this.solver).addAtMost(generateClauseVector(Arrays.asList(constraint.operands())), constraint.rhs()); this.solver.addClause(generateClauseVector(Arrays.asList(constraint.operands())), proposition); - } else + } else { this.addClauseSet(constraint.cnf(), proposition); + } } else { final EncodingResult result = EncodingResult.resultForMiniSat(this.f, this); this.ccEncoder.encode(constraint, result); } - } else + } else { this.addClauseSet(constraint.cnf(), proposition); - } else + } + } else { this.addClauseSet(formula.cnf(), proposition); + } } @Override @@ -215,16 +220,18 @@ public void addWithoutUnknown(final Formula formula) { final Map map = this.solver.name2idx(); for (final Variable var : formula.variables()) { final Integer index = map.get(var.name()); - if (index == null || index >= nVars) + if (index == null || index >= nVars) { restriction.addLiteral(var.negate()); + } } this.add(formula.restrict(restriction)); } @Override public CCIncrementalData addIncrementalCC(final PBConstraint cc) { - if (!cc.isCC()) + if (!cc.isCC()) { throw new IllegalArgumentException("Cannot generate an incremental cardinality constraint on a pseudo-Boolean constraint"); + } final EncodingResult result = EncodingResult.resultForMiniSat(this.f, this); return this.ccEncoder.encodeIncremental(cc, result); } @@ -245,8 +252,9 @@ protected void addClauseWithRelaxation(final Variable relaxationVar, final Formu @Override public Tristate sat(final SATHandler handler) { - if (this.result != UNDEF) + if (this.result != UNDEF) { return this.result; + } this.result = this.solver.solve(handler); return this.result; } @@ -290,8 +298,9 @@ public void reset() { @Override public Assignment model(final Collection variables) { - if (this.result == UNDEF) + if (this.result == UNDEF) { throw new IllegalStateException("Cannot get a model as long as the formula is not solved. Call 'sat' first."); + } final LNGIntVector relevantIndices = variables == null ? null : new LNGIntVector(variables.size()); if (relevantIndices != null) { for (final Variable var : variables) { @@ -320,8 +329,9 @@ public List enumerateAllModels(final Collection literals, public List enumerateAllModels(final Collection variables, final Collection additionalVariables, final ModelEnumerationHandler handler) { final List models = new LinkedList<>(); SolverState stateBeforeEnumeration = null; - if (this.style == SolverStyle.MINISAT && this.incremental) + if (this.style == SolverStyle.MINISAT && this.incremental) { stateBeforeEnumeration = this.saveState(); + } boolean proceed = true; SortedSet allVariables = new TreeSet<>(); if (variables == null) { @@ -357,8 +367,9 @@ public List enumerateAllModels(final Collection variables, break; } } - if (this.style == SolverStyle.MINISAT && this.incremental) + if (this.style == SolverStyle.MINISAT && this.incremental) { this.loadState(stateBeforeEnumeration); + } return models; } @@ -399,11 +410,14 @@ public SolverState saveState() { @Override public void loadState(final SolverState state) { int index = -1; - for (int i = this.validStates.size() - 1; i >= 0 && index == -1; i--) - if (this.validStates.get(i) == state.id()) + for (int i = this.validStates.size() - 1; i >= 0 && index == -1; i--) { + if (this.validStates.get(i) == state.id()) { index = i; - if (index == -1) + } + } + if (index == -1) { throw new IllegalArgumentException("The given solver state is not valid anymore."); + } this.validStates.shrinkTo(index + 1); this.solver.loadState(state.state()); this.result = UNDEF; @@ -413,25 +427,31 @@ public void loadState(final SolverState state) { public SortedSet knownVariables() { final SortedSet result = new TreeSet<>(); final int nVars = this.solver.nVars(); - for (final Map.Entry entry : this.solver.name2idx().entrySet()) - if (entry.getValue() < nVars) + for (final Map.Entry entry : this.solver.name2idx().entrySet()) { + if (entry.getValue() < nVars) { result.add(this.f.variable(entry.getKey())); + } + } return result; } @Override public UNSATCore unsatCore() { - if (!this.config.proofGeneration()) + if (!this.config.proofGeneration()) { throw new IllegalStateException("Cannot generate an unsat core if proof generation is not turned on"); - if (this.result == TRUE) + } + if (this.result == TRUE) { throw new IllegalStateException("An unsat core can only be generated if the formula is solved and is UNSAT"); + } if (this.result == Tristate.UNDEF) { throw new IllegalStateException("Cannot generate an unsat core before the formula was solved."); } - if (this.underlyingSolver() instanceof MiniCard) + if (this.underlyingSolver() instanceof MiniCard) { throw new IllegalStateException("Cannot compute an unsat core with MiniCard."); - if (this.underlyingSolver() instanceof GlucoseSyrup && this.config.incremental()) + } + if (this.underlyingSolver() instanceof GlucoseSyrup && this.config.incremental()) { throw new IllegalStateException("Cannot compute an unsat core with Glucose in incremental mode."); + } final DRUPTrim trimmer = new DRUPTrim(); @@ -441,8 +461,9 @@ public UNSATCore unsatCore() { clauses.push(pi.clause()); final Formula clause = getFormulaForVector(pi.clause()); Proposition proposition = pi.proposition(); - if (proposition == null) + if (proposition == null) { proposition = new StandardProposition(clause); + } clause2proposition.put(clause, proposition); } @@ -452,11 +473,13 @@ public UNSATCore unsatCore() { } final DRUPTrim.DRUPResult result = trimmer.compute(clauses, this.underlyingSolver().pgProof()); - if (result.trivialUnsat()) + if (result.trivialUnsat()) { return handleTrivialCase(); + } final LinkedHashSet propositions = new LinkedHashSet<>(); - for (final LNGIntVector vector : result.unsatCore()) + for (final LNGIntVector vector : result.unsatCore()) { propositions.add(clause2proposition.get(getFormulaForVector(vector))); + } return new UNSATCore<>(new ArrayList<>(propositions), false); } @@ -524,7 +547,7 @@ public boolean initialPhase() { private UNSATCore handleTrivialCase() { final LNGVector clauses = this.underlyingSolver().pgOriginalClauses(); - for (int i = 0; i < clauses.size(); i++) + for (int i = 0; i < clauses.size(); i++) { for (int j = i + 1; j < clauses.size(); j++) { if (clauses.get(i).clause().size() == 1 && clauses.get(j).clause().size() == 1 && clauses.get(i).clause().get(0) + clauses.get(j).clause().get(0) == 0) { @@ -536,13 +559,16 @@ private UNSATCore handleTrivialCase() { return new UNSATCore<>(new ArrayList<>(propositions), false); } } + } throw new IllegalStateException("Should be a trivial unsat core, but did not found one."); } private boolean containsEmptyClause(final LNGVector clauses) { - for (final LNGIntVector clause : clauses) - if (clause.empty()) + for (final LNGIntVector clause : clauses) { + if (clause.empty()) { return true; + } + } return false; } @@ -580,4 +606,34 @@ public SortedSet upZeroLiterals() { } return upZeroLiterals; } + + @Override + public Set formulaOnSolver() { + final Set formulas = new LinkedHashSet<>(); + for (final MSClause clause : this.underlyingSolver().clauses()) { + final List lits = new ArrayList<>(); + for (int i = 0; i < clause.size(); i++) { + final int litInt = clause.get(i); + lits.add(this.f.literal(this.solver.nameForIdx(litInt >> 1), (litInt & 1) != 1)); + } + if (!clause.isAtMost()) { + formulas.add(this.f.clause(lits)); + } else { + final int rhs = clause.size() + 1 - clause.atMostWatchers(); + final List vars = new ArrayList<>(); + for (final Literal lit : lits) { + vars.add(lit.variable()); + } + formulas.add(this.f.cc(CType.LE, rhs, vars)); + } + } + final LNGVector variables = this.solver.variables(); + for (int i = 0; i < variables.size(); i++) { + final MSVariable var = variables.get(i); + if (var.level() == 0) { + formulas.add(this.f.literal(this.solver.nameForIdx(i), var.assignment() == TRUE)); + } + } + return formulas; + } } diff --git a/src/main/java/org/logicng/solvers/SATSolver.java b/src/main/java/org/logicng/solvers/SATSolver.java index 4eb8a56d..0d877543 100644 --- a/src/main/java/org/logicng/solvers/SATSolver.java +++ b/src/main/java/org/logicng/solvers/SATSolver.java @@ -45,6 +45,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Set; import java.util.SortedSet; /** @@ -91,7 +92,9 @@ public void add(final Formula formula) { * @param propositions the set of propositions */ public void addPropositions(final Collection propositions) { - for (final Proposition proposition : propositions) { add(proposition); } + for (final Proposition proposition : propositions) { + add(proposition); + } } /** @@ -99,7 +102,9 @@ public void addPropositions(final Collection propositions * @param propositions the set of propositions */ public void addPropositions(final Proposition... propositions) { - for (final Proposition proposition : propositions) { add(proposition); } + for (final Proposition proposition : propositions) { + add(proposition); + } } /** @@ -107,7 +112,9 @@ public void addPropositions(final Proposition... propositions) { * @param proposition the proposition */ public void add(final Proposition proposition) { - for (final Formula formula : proposition.formulas()) { this.add(formula, proposition); } + for (final Formula formula : proposition.formulas()) { + this.add(formula, proposition); + } } /** @@ -115,7 +122,9 @@ public void add(final Proposition proposition) { * @param formulas the formula list */ public void add(final ImmutableFormulaList formulas) { - for (final Formula formula : formulas) { this.add(formula); } + for (final Formula formula : formulas) { + this.add(formula); + } } /** @@ -123,7 +132,9 @@ public void add(final ImmutableFormulaList formulas) { * @param formulas the collection of formulas */ public void add(final Collection formulas) { - for (final Formula formula : formulas) { this.add(formula); } + for (final Formula formula : formulas) { + this.add(formula); + } } /** @@ -141,7 +152,9 @@ public void addWithRelaxation(final Variable relaxationVar, final Formula formul * @param proposition the proposition */ public void addWithRelaxation(final Variable relaxationVar, final Proposition proposition) { - for (final Formula formula : proposition.formulas()) { this.addWithRelaxation(relaxationVar, formula); } + for (final Formula formula : proposition.formulas()) { + this.addWithRelaxation(relaxationVar, formula); + } } /** @@ -150,7 +163,9 @@ public void addWithRelaxation(final Variable relaxationVar, final Proposition pr * @param formulas the formula list */ public void addWithRelaxation(final Variable relaxationVar, final ImmutableFormulaList formulas) { - for (final Formula formula : formulas) { this.addWithRelaxation(relaxationVar, formula); } + for (final Formula formula : formulas) { + this.addWithRelaxation(relaxationVar, formula); + } } /** @@ -159,7 +174,9 @@ public void addWithRelaxation(final Variable relaxationVar, final ImmutableFormu * @param formulas the collection of formulas */ public void addWithRelaxation(final Variable relaxationVar, final Collection formulas) { - for (final Formula formula : formulas) { this.addWithRelaxation(relaxationVar, formula); } + for (final Formula formula : formulas) { + this.addWithRelaxation(relaxationVar, formula); + } } /** @@ -190,7 +207,9 @@ void addClauseSet(final Formula formula, final Proposition proposition) { this.addClause(formula, proposition); break; case AND: - for (final Formula op : formula) { this.addClause(op, proposition); } + for (final Formula op : formula) { + this.addClause(op, proposition); + } break; default: throw new IllegalArgumentException("Input formula ist not a valid CNF: " + formula); @@ -212,7 +231,9 @@ private void addClauseSetWithRelaxation(final Variable relaxationVar, final Form this.addClauseWithRelaxation(relaxationVar, formula); break; case AND: - for (final Formula op : formula) { this.addClauseWithRelaxation(relaxationVar, op); } + for (final Formula op : formula) { + this.addClauseWithRelaxation(relaxationVar, op); + } break; default: throw new IllegalArgumentException("Input formula ist not a valid CNF: " + formula); @@ -456,4 +477,10 @@ public void setSolverToUndef() { public FormulaFactory factory() { return this.f; } + + /** + * Returns the formula which is currently stored on the solver. + * @return the formula on the solver + */ + public abstract Set formulaOnSolver(); } diff --git a/src/main/java/org/logicng/solvers/sat/MiniSatStyleSolver.java b/src/main/java/org/logicng/solvers/sat/MiniSatStyleSolver.java index 32403da6..1d9baf21 100644 --- a/src/main/java/org/logicng/solvers/sat/MiniSatStyleSolver.java +++ b/src/main/java/org/logicng/solvers/sat/MiniSatStyleSolver.java @@ -135,7 +135,7 @@ protected MiniSatStyleSolver(final MiniSatConfig config) { * @param sign {@code true} if the literal is negative, {@code false} otherwise * @return the literal (as integer value) */ - public static int mkLit(int var, boolean sign) { + public static int mkLit(final int var, final boolean sign) { return var + var + (sign ? 1 : 0); } @@ -144,7 +144,7 @@ public static int mkLit(int var, boolean sign) { * @param lit the literal * @return the negated literal */ - public static int not(int lit) { + public static int not(final int lit) { return lit ^ 1; } @@ -153,7 +153,7 @@ public static int not(int lit) { * @param lit the literal * @return {@code true} if the literal is negated */ - public static boolean sign(int lit) { + public static boolean sign(final int lit) { return (lit & 1) == 1; } @@ -162,7 +162,7 @@ public static boolean sign(int lit) { * @param lit the literal * @return the variable index of the literal */ - public static int var(int lit) { + public static int var(final int lit) { return lit >> 1; } @@ -172,7 +172,7 @@ public static int var(int lit) { * @param x the current number of restarts * @return the next number in the Luby sequence */ - protected static double luby(double y, int x) { + protected static double luby(final double y, final int x) { int intX = x; int size = 1; int seq = 0; @@ -244,7 +244,7 @@ private void initializeConfig() { * @param lit the literal * @return the variable of the literal */ - protected MSVariable v(int lit) { + protected MSVariable v(final int lit) { return this.vars.get(lit >> 1); } @@ -253,7 +253,7 @@ protected MSVariable v(int lit) { * @param lit the literal * @return the assigned value of the literal */ - protected Tristate value(int lit) { + protected Tristate value(final int lit) { return sign(lit) ? Tristate.negate(this.v(lit).assignment()) : this.v(lit).assignment(); } @@ -263,7 +263,7 @@ protected Tristate value(int lit) { * @param y the second variable * @return {@code true} if the first variable's activity is larger then the second one's */ - public boolean lt(int x, int y) { + public boolean lt(final int x, final int y) { return this.vars.get(x).activity() > this.vars.get(y).activity(); } @@ -282,7 +282,7 @@ public int idxForName(final String name) { * @param var the variable index * @return the name for the index */ - public String nameForIdx(int var) { + public String nameForIdx(final int var) { return this.idx2name.get(var); } @@ -291,7 +291,7 @@ public String nameForIdx(int var) { * @param name the variable name * @param id the variable index */ - public void addName(final String name, int id) { + public void addName(final String name, final int id) { this.name2idx.put(name, id); this.idx2name.put(id, name); } @@ -311,7 +311,7 @@ public void addName(final String name, int id) { * @param proposition a proposition (if required for proof tracing) * @return {@code true} if the clause was added successfully, {@code false} otherwise */ - public boolean addClause(int lit, final Proposition proposition) { + public boolean addClause(final int lit, final Proposition proposition) { final LNGIntVector unit = new LNGIntVector(1); unit.push(lit); return this.addClause(unit, proposition); @@ -408,7 +408,7 @@ public int nVars() { * @return the mapping from variable names to internal solver indices */ public Map name2idx() { - return name2idx; + return this.name2idx; } /** @@ -432,7 +432,7 @@ protected int decisionLevel() { * @param x a variable index * @return the abstraction of levels */ - protected int abstractLevel(int x) { + protected int abstractLevel(final int x) { return 1 << (this.vars.get(x).level() & 31); } @@ -440,9 +440,10 @@ protected int abstractLevel(int x) { * Inserts a variable (given by its index) into the heap of decision variables. * @param x the variable index */ - protected void insertVarOrder(int x) { - if (!this.orderHeap.inHeap(x) && this.vars.get(x).decision()) + protected void insertVarOrder(final int x) { + if (!this.orderHeap.inHeap(x) && this.vars.get(x).decision()) { this.orderHeap.insert(x); + } } /** @@ -451,11 +452,13 @@ protected void insertVarOrder(int x) { */ protected int pickBranchLit() { int next = -1; - while (next == -1 || this.vars.get(next).assignment() != Tristate.UNDEF || !this.vars.get(next).decision()) - if (this.orderHeap.empty()) + while (next == -1 || this.vars.get(next).assignment() != Tristate.UNDEF || !this.vars.get(next).decision()) { + if (this.orderHeap.empty()) { return -1; - else + } else { next = this.orderHeap.removeMin(); + } + } return mkLit(next, this.vars.get(next).polarity()); } @@ -470,7 +473,7 @@ protected void varDecayActivity() { * Bumps the activity of the variable at a given index. * @param v the variable index */ - protected void varBumpActivity(int v) { + protected void varBumpActivity(final int v) { this.varBumpActivity(v, this.varInc); } @@ -479,16 +482,18 @@ protected void varBumpActivity(int v) { * @param v the variable index * @param inc the increment value */ - protected void varBumpActivity(int v, double inc) { + protected void varBumpActivity(final int v, final double inc) { final MSVariable var = this.vars.get(v); var.incrementActivity(inc); if (var.activity() > 1e100) { - for (final MSVariable variable : this.vars) + for (final MSVariable variable : this.vars) { variable.rescaleActivity(); + } this.varInc *= 1e-100; } - if (this.orderHeap.inHeap(v)) + if (this.orderHeap.inHeap(v)) { this.orderHeap.decrease(v); + } } /** @@ -496,9 +501,11 @@ protected void varBumpActivity(int v, double inc) { */ protected void rebuildOrderHeap() { final LNGIntVector vs = new LNGIntVector(); - for (int v = 0; v < this.nVars(); v++) - if (this.vars.get(v).decision() && this.vars.get(v).assignment() == Tristate.UNDEF) + for (int v = 0; v < this.nVars(); v++) { + if (this.vars.get(v).decision() && this.vars.get(v).assignment() == Tristate.UNDEF) { vs.push(v); + } + } this.orderHeap.build(vs); } @@ -515,7 +522,7 @@ protected boolean locked(final MSClause c) { * Decays the clause activity increment by the clause decay factor. */ protected void claDecayActivity() { - claInc *= (1 / clauseDecay); + this.claInc *= (1 / this.clauseDecay); } /** @@ -523,11 +530,12 @@ protected void claDecayActivity() { * @param c the clause */ protected void claBumpActivity(final MSClause c) { - c.incrementActivity(claInc); + c.incrementActivity(this.claInc); if (c.activity() > 1e20) { - for (final MSClause clause : learnts) + for (final MSClause clause : this.learnts) { clause.rescaleActivity(); - claInc *= 1e-20; + } + this.claInc *= 1e-20; } } @@ -631,28 +639,28 @@ public LNGVector pgProof() { @Override public String toString() { final StringBuilder sb = new StringBuilder(); - sb.append("ok ").append(ok).append(System.lineSeparator()); - sb.append("qhead ").append(qhead).append(System.lineSeparator()); - sb.append("#clauses ").append(clauses.size()).append(System.lineSeparator()); - sb.append("#learnts ").append(learnts.size()).append(System.lineSeparator()); - sb.append("#watches ").append(watches.size()).append(System.lineSeparator()); - sb.append("#vars ").append(vars.size()).append(System.lineSeparator()); - sb.append("#orderheap ").append(orderHeap.size()).append(System.lineSeparator()); - sb.append("#trail ").append(trail.size()).append(System.lineSeparator()); - sb.append("#trailLim ").append(trailLim.size()).append(System.lineSeparator()); - - sb.append("model ").append(model).append(System.lineSeparator()); - sb.append("conflict ").append(conflict).append(System.lineSeparator()); - sb.append("assumptions ").append(assumptions).append(System.lineSeparator()); - sb.append("#seen ").append(seen.size()).append(System.lineSeparator()); - sb.append("#stack ").append(analyzeStack.size()).append(System.lineSeparator()); - sb.append("#toclear ").append(analyzeToClear.size()).append(System.lineSeparator()); - - sb.append("claInc ").append(claInc).append(System.lineSeparator()); - sb.append("simpDBAssigns ").append(simpDBAssigns).append(System.lineSeparator()); - sb.append("simpDBProps ").append(simpDBProps).append(System.lineSeparator()); - sb.append("#clause lits ").append(clausesLiterals).append(System.lineSeparator()); - sb.append("#learnts lits ").append(learntsLiterals).append(System.lineSeparator()); + sb.append("ok ").append(this.ok).append(System.lineSeparator()); + sb.append("qhead ").append(this.qhead).append(System.lineSeparator()); + sb.append("#clauses ").append(this.clauses.size()).append(System.lineSeparator()); + sb.append("#learnts ").append(this.learnts.size()).append(System.lineSeparator()); + sb.append("#watches ").append(this.watches.size()).append(System.lineSeparator()); + sb.append("#vars ").append(this.vars.size()).append(System.lineSeparator()); + sb.append("#orderheap ").append(this.orderHeap.size()).append(System.lineSeparator()); + sb.append("#trail ").append(this.trail.size()).append(System.lineSeparator()); + sb.append("#trailLim ").append(this.trailLim.size()).append(System.lineSeparator()); + + sb.append("model ").append(this.model).append(System.lineSeparator()); + sb.append("conflict ").append(this.conflict).append(System.lineSeparator()); + sb.append("assumptions ").append(this.assumptions).append(System.lineSeparator()); + sb.append("#seen ").append(this.seen.size()).append(System.lineSeparator()); + sb.append("#stack ").append(this.analyzeStack.size()).append(System.lineSeparator()); + sb.append("#toclear ").append(this.analyzeToClear.size()).append(System.lineSeparator()); + + sb.append("claInc ").append(this.claInc).append(System.lineSeparator()); + sb.append("simpDBAssigns ").append(this.simpDBAssigns).append(System.lineSeparator()); + sb.append("simpDBProps ").append(this.simpDBProps).append(System.lineSeparator()); + sb.append("#clause lits ").append(this.clausesLiterals).append(System.lineSeparator()); + sb.append("#learnts lits ").append(this.learntsLiterals).append(System.lineSeparator()); return sb.toString(); } @@ -668,7 +676,7 @@ public static class ProofInformation { * @param clause the clause * @param proposition the proposition */ - public ProofInformation(LNGIntVector clause, Proposition proposition) { + public ProofInformation(final LNGIntVector clause, final Proposition proposition) { this.clause = clause; this.proposition = proposition; } @@ -678,7 +686,7 @@ public ProofInformation(LNGIntVector clause, Proposition proposition) { * @return the clause */ public LNGIntVector clause() { - return clause; + return this.clause; } /** @@ -686,14 +694,14 @@ public LNGIntVector clause() { * @return the proposition */ public Proposition proposition() { - return proposition; + return this.proposition; } @Override public String toString() { return "ProofInformation{" + - "clause=" + clause + - ", proposition=" + proposition + + "clause=" + this.clause + + ", proposition=" + this.proposition + '}'; } } @@ -714,4 +722,12 @@ public LNGIntVector upZeroLiterals() { } return upZeroLiterals; } + + public LNGVector clauses() { + return this.clauses; + } + + public LNGVector variables() { + return this.vars; + } } diff --git a/src/test/java/org/logicng/solvers/sat/SATTest.java b/src/test/java/org/logicng/solvers/sat/SATTest.java index e3ff52eb..9e1bfb1d 100644 --- a/src/test/java/org/logicng/solvers/sat/SATTest.java +++ b/src/test/java/org/logicng/solvers/sat/SATTest.java @@ -28,6 +28,13 @@ package org.logicng.solvers.sat; +import static org.assertj.core.api.Assertions.assertThat; +import static org.logicng.datastructures.Tristate.FALSE; +import static org.logicng.datastructures.Tristate.TRUE; +import static org.logicng.datastructures.Tristate.UNDEF; +import static org.logicng.solvers.sat.MiniSatConfig.ClauseMinimization.BASIC; +import static org.logicng.solvers.sat.MiniSatConfig.ClauseMinimization.NONE; + import org.junit.Assert; import org.junit.Test; import org.logicng.collections.ImmutableFormulaList; @@ -45,6 +52,7 @@ import org.logicng.handlers.TimeoutSATHandler; import org.logicng.io.parsers.ParserException; import org.logicng.io.parsers.PropositionalParser; +import org.logicng.io.parsers.PseudoBooleanParser; import org.logicng.propositions.StandardProposition; import org.logicng.solvers.CleaneLing; import org.logicng.solvers.MiniSat; @@ -55,26 +63,21 @@ import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; -import static org.assertj.core.api.Assertions.assertThat; -import static org.logicng.datastructures.Tristate.FALSE; -import static org.logicng.datastructures.Tristate.TRUE; -import static org.logicng.datastructures.Tristate.UNDEF; -import static org.logicng.solvers.sat.MiniSatConfig.ClauseMinimization.BASIC; -import static org.logicng.solvers.sat.MiniSatConfig.ClauseMinimization.NONE; - /** * Unit tests for the SAT solvers. * @version 1.3 @@ -90,18 +93,18 @@ public class SATTest { public SATTest() { this.f = new FormulaFactory(); - this.pg = new PigeonHoleGenerator(f); - this.parser = new PropositionalParser(f); + this.pg = new PigeonHoleGenerator(this.f); + this.parser = new PropositionalParser(this.f); this.solvers = new SATSolver[8]; - this.solvers[0] = MiniSat.miniSat(f, new MiniSatConfig.Builder().incremental(true).build()); - this.solvers[1] = MiniSat.miniSat(f, new MiniSatConfig.Builder().incremental(false).build()); - this.solvers[2] = MiniSat.glucose(f, new MiniSatConfig.Builder().incremental(false).build(), + this.solvers[0] = MiniSat.miniSat(this.f, new MiniSatConfig.Builder().incremental(true).build()); + this.solvers[1] = MiniSat.miniSat(this.f, new MiniSatConfig.Builder().incremental(false).build()); + this.solvers[2] = MiniSat.glucose(this.f, new MiniSatConfig.Builder().incremental(false).build(), new GlucoseConfig.Builder().build()); - this.solvers[3] = MiniSat.miniCard(f, new MiniSatConfig.Builder().incremental(true).build()); - this.solvers[4] = MiniSat.miniCard(f, new MiniSatConfig.Builder().incremental(false).build()); - this.solvers[5] = CleaneLing.minimalistic(f); - this.solvers[6] = CleaneLing.full(f, new CleaneLingConfig.Builder().plain(true).glueUpdate(true).gluered(true).build()); - this.solvers[7] = CleaneLing.full(f); + this.solvers[3] = MiniSat.miniCard(this.f, new MiniSatConfig.Builder().incremental(true).build()); + this.solvers[4] = MiniSat.miniCard(this.f, new MiniSatConfig.Builder().incremental(false).build()); + this.solvers[5] = CleaneLing.minimalistic(this.f); + this.solvers[6] = CleaneLing.full(this.f, new CleaneLingConfig.Builder().plain(true).glueUpdate(true).gluered(true).build()); + this.solvers[7] = CleaneLing.full(this.f); this.testStrings = new String[8]; this.testStrings[0] = "MiniSat{result=UNDEF, incremental=true}"; @@ -171,14 +174,14 @@ public void testAnd1() { @Test public void testAnd2() { for (final SATSolver s : this.solvers) { - final StandardProposition prop = new StandardProposition(f.and(f.literal("a", true), f.literal("b", false), f.literal("c", true), f.literal("d", false))); + final StandardProposition prop = new StandardProposition(this.f.and(this.f.literal("a", true), this.f.literal("b", false), this.f.literal("c", true), this.f.literal("d", false))); s.add(prop); Assert.assertEquals(TRUE, s.sat()); Assert.assertEquals(4, s.model().size()); - Assert.assertTrue(s.model().evaluateLit(f.variable("a"))); - Assert.assertFalse(s.model().evaluateLit(f.variable("b"))); - Assert.assertTrue(s.model().evaluateLit(f.variable("c"))); - Assert.assertFalse(s.model().evaluateLit(f.variable("d"))); + Assert.assertTrue(s.model().evaluateLit(this.f.variable("a"))); + Assert.assertFalse(s.model().evaluateLit(this.f.variable("b"))); + Assert.assertTrue(s.model().evaluateLit(this.f.variable("c"))); + Assert.assertFalse(s.model().evaluateLit(this.f.variable("d"))); s.reset(); } } @@ -187,10 +190,10 @@ public void testAnd2() { public void testAnd3() { for (final SATSolver s : this.solvers) { final List formulas = new ArrayList<>(3); - formulas.add(f.literal("a", true)); - formulas.add(f.literal("b", false)); - formulas.add(f.literal("a", false)); - formulas.add(f.literal("d", false)); + formulas.add(this.f.literal("a", true)); + formulas.add(this.f.literal("b", false)); + formulas.add(this.f.literal("a", false)); + formulas.add(this.f.literal("d", false)); s.add(formulas); Assert.assertEquals(FALSE, s.sat()); s.reset(); @@ -200,13 +203,13 @@ public void testAnd3() { @Test public void testFormula1() throws ParserException { for (final SATSolver s : this.solvers) { - s.add(parser.parse("(x => y) & (~x => y) & (y => z) & (z => ~x)")); + s.add(this.parser.parse("(x => y) & (~x => y) & (y => z) & (z => ~x)")); Assert.assertEquals(TRUE, s.sat()); Assert.assertEquals(3, s.model().size()); - Assert.assertFalse(s.model().evaluateLit(f.variable("x"))); - Assert.assertTrue(s.model().evaluateLit(f.variable("y"))); - Assert.assertTrue(s.model().evaluateLit(f.variable("z"))); - s.add(f.variable("x")); + Assert.assertFalse(s.model().evaluateLit(this.f.variable("x"))); + Assert.assertTrue(s.model().evaluateLit(this.f.variable("y"))); + Assert.assertTrue(s.model().evaluateLit(this.f.variable("z"))); + s.add(this.f.variable("x")); Assert.assertEquals(FALSE, s.sat()); s.reset(); } @@ -216,14 +219,14 @@ public void testFormula1() throws ParserException { public void testFormula2() throws ParserException { for (int i = 0; i < this.solvers.length - 1; i++) { final SATSolver s = this.solvers[i]; - s.add(parser.parse("(x => y) & (~x => y) & (y => z) & (z => ~x)")); + s.add(this.parser.parse("(x => y) & (~x => y) & (y => z) & (z => ~x)")); final List models = s.enumerateAllModels(); Assert.assertEquals(1, models.size()); Assert.assertEquals(3, models.get(0).size()); - Assert.assertFalse(models.get(0).evaluateLit(f.variable("x"))); - Assert.assertTrue(models.get(0).evaluateLit(f.variable("y"))); - Assert.assertTrue(models.get(0).evaluateLit(f.variable("z"))); - s.add(f.variable("x")); + Assert.assertFalse(models.get(0).evaluateLit(this.f.variable("x"))); + Assert.assertTrue(models.get(0).evaluateLit(this.f.variable("y"))); + Assert.assertTrue(models.get(0).evaluateLit(this.f.variable("z"))); + s.add(this.f.variable("x")); Assert.assertEquals(FALSE, s.sat()); s.reset(); } @@ -234,27 +237,29 @@ public void testCC1() { for (int i = 0; i < this.solvers.length - 1; i++) { final SATSolver s = this.solvers[i]; final Variable[] lits = new Variable[100]; - for (int j = 0; j < lits.length; j++) - lits[j] = f.variable("x" + j); - s.add(f.exo(lits)); + for (int j = 0; j < lits.length; j++) { + lits[j] = this.f.variable("x" + j); + } + s.add(this.f.exo(lits)); final List models = s.enumerateAllModels(lits); Assert.assertEquals(100, models.size()); - for (final Assignment m : models) + for (final Assignment m : models) { Assert.assertEquals(1, m.positiveLiterals().size()); + } s.reset(); } } @Test public void testPBC() { - for (SATSolver s : this.solvers) { - List lits = new ArrayList<>(); - List coeffs = new ArrayList<>(); + for (final SATSolver s : this.solvers) { + final List lits = new ArrayList<>(); + final List coeffs = new ArrayList<>(); for (int i = 0; i < 5; i++) { - lits.add(f.literal("x" + i, i % 2 == 0)); + lits.add(this.f.literal("x" + i, i % 2 == 0)); coeffs.add(i + 1); } - s.add(f.pbc(CType.GE, 10, lits, coeffs)); + s.add(this.f.pbc(CType.GE, 10, lits, coeffs)); Assert.assertEquals(Tristate.TRUE, s.sat()); s.reset(); } @@ -262,15 +267,15 @@ public void testPBC() { @Test public void testPartialModel() { - for (SATSolver s : this.solvers) { + for (final SATSolver s : this.solvers) { s.add(F.A); s.add(F.B); s.add(F.C); - Variable[] relevantVars = new Variable[2]; + final Variable[] relevantVars = new Variable[2]; relevantVars[0] = F.A; relevantVars[1] = F.B; Assert.assertEquals(Tristate.TRUE, s.sat()); - Assignment relModel = s.model(relevantVars); + final Assignment relModel = s.model(relevantVars); Assert.assertTrue(relModel.negativeLiterals().isEmpty()); Assert.assertFalse(relModel.literals().contains(F.C)); s.reset(); @@ -279,22 +284,22 @@ public void testPartialModel() { @Test public void testModelEnumerationHandler() { - for (SATSolver s : solvers) { + for (final SATSolver s : this.solvers) { s.add(F.IMP3); try { - List models = s.enumerateAllModels(new ModelEnumerationHandler() { + final List models = s.enumerateAllModels(new ModelEnumerationHandler() { @Override - public boolean foundModel(Assignment assignment) { + public boolean foundModel(final Assignment assignment) { return !assignment.negativeLiterals().isEmpty(); } }); Assert.assertFalse(models.isEmpty()); Assert.assertTrue(models.get(models.size() - 1).negativeLiterals().isEmpty()); models.remove(models.size() - 1); - for (Assignment model : models) { + for (final Assignment model : models) { Assert.assertFalse(model.negativeLiterals().isEmpty()); } - } catch (Exception e) { + } catch (final Exception e) { Assert.assertTrue(e instanceof UnsupportedOperationException); } @@ -304,47 +309,47 @@ public boolean foundModel(Assignment assignment) { @Test public void testWithRelaxation() throws ParserException { - PropositionalParser parser = new PropositionalParser(f); - Formula one = parser.parse("a & b & (c | ~d)"); - Formula two = parser.parse("~a | ~c"); + final PropositionalParser parser = new PropositionalParser(this.f); + final Formula one = parser.parse("a & b & (c | ~d)"); + final Formula two = parser.parse("~a | ~c"); - for (SATSolver s : this.solvers) { + for (final SATSolver s : this.solvers) { s.add(one); - s.addWithRelaxation(f.variable("d"), two); + s.addWithRelaxation(this.f.variable("d"), two); Assert.assertEquals(Tristate.TRUE, s.sat()); try { Assert.assertEquals(2, s.enumerateAllModels().size()); - } catch (Exception e) { + } catch (final Exception e) { Assert.assertTrue(e instanceof UnsupportedOperationException); } s.reset(); s.add(one); - s.addWithRelaxation(f.variable("d"), new StandardProposition(two)); + s.addWithRelaxation(this.f.variable("d"), new StandardProposition(two)); Assert.assertEquals(Tristate.TRUE, s.sat()); try { Assert.assertEquals(2, s.enumerateAllModels().size()); - } catch (Exception e) { + } catch (final Exception e) { Assert.assertTrue(e instanceof UnsupportedOperationException); } s.reset(); s.add(one); - s.addWithRelaxation(f.variable("d"), new ImmutableFormulaList(two)); + s.addWithRelaxation(this.f.variable("d"), new ImmutableFormulaList(two)); Assert.assertEquals(Tristate.TRUE, s.sat()); try { Assert.assertEquals(2, s.enumerateAllModels().size()); - } catch (Exception e) { + } catch (final Exception e) { Assert.assertTrue(e instanceof UnsupportedOperationException); } s.reset(); s.add(one); - s.addWithRelaxation(f.variable("d"), Arrays.asList(two, f.verum())); + s.addWithRelaxation(this.f.variable("d"), Arrays.asList(two, this.f.verum())); Assert.assertEquals(Tristate.TRUE, s.sat()); try { Assert.assertEquals(2, s.enumerateAllModels().size()); - } catch (Exception e) { + } catch (final Exception e) { Assert.assertTrue(e instanceof UnsupportedOperationException); } s.reset(); @@ -355,16 +360,17 @@ public void testWithRelaxation() throws ParserException { public void testIllegalEnumeration() { final SATSolver s = this.solvers[7]; final Variable[] lits = new Variable[100]; - for (int j = 0; j < lits.length; j++) - lits[j] = f.variable("x" + j); - s.add(f.exo(lits)); + for (int j = 0; j < lits.length; j++) { + lits[j] = this.f.variable("x" + j); + } + s.add(this.f.exo(lits)); s.enumerateAllModels(lits); } @Test public void testPigeonHole1() { for (final SATSolver s : this.solvers) { - s.add(pg.generate(1)); + s.add(this.pg.generate(1)); Assert.assertEquals(FALSE, s.sat()); Assert.assertNull(s.model()); s.reset(); @@ -374,7 +380,7 @@ public void testPigeonHole1() { @Test public void testPigeonHole2() { for (final SATSolver s : this.solvers) { - s.add(pg.generate(2)); + s.add(this.pg.generate(2)); Assert.assertEquals(FALSE, s.sat()); Assert.assertEquals(FALSE, s.sat()); Assert.assertNull(s.model()); @@ -385,7 +391,7 @@ public void testPigeonHole2() { @Test public void testPigeonHole3() { for (final SATSolver s : this.solvers) { - s.add(pg.generate(3)); + s.add(this.pg.generate(3)); Assert.assertEquals(FALSE, s.sat()); Assert.assertNull(s.model()); s.reset(); @@ -395,7 +401,7 @@ public void testPigeonHole3() { @Test public void testPigeonHole4() { for (final SATSolver s : this.solvers) { - s.add(pg.generate(4)); + s.add(this.pg.generate(4)); Assert.assertEquals(FALSE, s.sat()); Assert.assertNull(s.model()); s.reset(); @@ -405,7 +411,7 @@ public void testPigeonHole4() { @Test public void testPigeonHole5() { for (final SATSolver s : this.solvers) { - s.add(pg.generate(5)); + s.add(this.pg.generate(5)); Assert.assertEquals(FALSE, s.sat()); Assert.assertNull(s.model()); s.reset(); @@ -415,7 +421,7 @@ public void testPigeonHole5() { @Test public void testPigeonHole6() { for (final SATSolver s : this.solvers) { - s.add(pg.generate(6)); + s.add(this.pg.generate(6)); Assert.assertEquals(FALSE, s.sat()); Assert.assertNull(s.model()); s.reset(); @@ -425,7 +431,7 @@ public void testPigeonHole6() { @Test public void testPigeonHole7() { for (final SATSolver s : this.solvers) { - s.add(pg.generate(7)); + s.add(this.pg.generate(7)); Assert.assertEquals(FALSE, s.sat()); Assert.assertNull(s.model()); s.reset(); @@ -435,14 +441,14 @@ public void testPigeonHole7() { @Test public void testDifferentClauseMinimizations() { final SATSolver[] moreSolvers = new SATSolver[6]; - moreSolvers[0] = MiniSat.miniSat(f, new MiniSatConfig.Builder().clMinimization(NONE).build()); - moreSolvers[1] = MiniSat.miniSat(f, new MiniSatConfig.Builder().clMinimization(BASIC).build()); - moreSolvers[2] = MiniSat.glucose(f, new MiniSatConfig.Builder().clMinimization(NONE).build(), new GlucoseConfig.Builder().build()); - moreSolvers[3] = MiniSat.glucose(f, new MiniSatConfig.Builder().clMinimization(BASIC).build(), new GlucoseConfig.Builder().build()); - moreSolvers[4] = MiniSat.miniCard(f, new MiniSatConfig.Builder().clMinimization(NONE).build()); - moreSolvers[5] = MiniSat.miniCard(f, new MiniSatConfig.Builder().clMinimization(BASIC).build()); + moreSolvers[0] = MiniSat.miniSat(this.f, new MiniSatConfig.Builder().clMinimization(NONE).build()); + moreSolvers[1] = MiniSat.miniSat(this.f, new MiniSatConfig.Builder().clMinimization(BASIC).build()); + moreSolvers[2] = MiniSat.glucose(this.f, new MiniSatConfig.Builder().clMinimization(NONE).build(), new GlucoseConfig.Builder().build()); + moreSolvers[3] = MiniSat.glucose(this.f, new MiniSatConfig.Builder().clMinimization(BASIC).build(), new GlucoseConfig.Builder().build()); + moreSolvers[4] = MiniSat.miniCard(this.f, new MiniSatConfig.Builder().clMinimization(NONE).build()); + moreSolvers[5] = MiniSat.miniCard(this.f, new MiniSatConfig.Builder().clMinimization(BASIC).build()); for (final SATSolver s : moreSolvers) { - s.add(pg.generate(7)); + s.add(this.pg.generate(7)); Assert.assertEquals(FALSE, s.sat()); Assert.assertNull(s.model()); } @@ -451,7 +457,7 @@ public void testDifferentClauseMinimizations() { @Test public void testTimeoutSATHandler() { for (final SATSolver s : this.solvers) { - s.add(pg.generate(10)); + s.add(this.pg.generate(10)); final Tristate result = s.sat(new TimeoutSATHandler(1000)); Assert.assertEquals(UNDEF, result); s.reset(); @@ -474,7 +480,7 @@ public void testDimacsFiles() throws IOException { final String fileName = file.getName(); if (fileName.endsWith(".cnf")) { readCNF(solver, file); - boolean res = solver.sat() == TRUE; + final boolean res = solver.sat() == TRUE; Assert.assertEquals(expectedResults.get(fileName), res); } } @@ -487,8 +493,9 @@ private void readCNF(final SATSolver solver, final File file) throws IOException final BufferedReader reader = new BufferedReader(new FileReader(file)); while (reader.ready()) { final String line = reader.readLine(); - if (line.startsWith("p cnf")) + if (line.startsWith("p cnf")) { break; + } } String[] tokens; final List literals = new ArrayList<>(); @@ -499,13 +506,14 @@ private void readCNF(final SATSolver solver, final File file) throws IOException literals.clear(); for (int i = 0; i < tokens.length - 1; i++) { if (!tokens[i].isEmpty()) { - int parsedLit = Integer.parseInt(tokens[i]); - String var = "v" + Math.abs(parsedLit); - literals.add(parsedLit > 0 ? f.literal(var, true) : f.literal(var, false)); + final int parsedLit = Integer.parseInt(tokens[i]); + final String var = "v" + Math.abs(parsedLit); + literals.add(parsedLit > 0 ? this.f.literal(var, true) : this.f.literal(var, false)); } } - if (!literals.isEmpty()) - solver.add(f.or(literals)); + if (!literals.isEmpty()) { + solver.add(this.f.or(literals)); + } } } } @@ -513,16 +521,16 @@ private void readCNF(final SATSolver solver, final File file) throws IOException @Test public void testPigeonHoleWithReset() { for (final SATSolver s : this.solvers) { - s.add(pg.generate(4)); + s.add(this.pg.generate(4)); Assert.assertEquals(FALSE, s.sat()); s.reset(); - s.add(pg.generate(5)); + s.add(this.pg.generate(5)); Assert.assertEquals(FALSE, s.sat()); s.reset(); - s.add(pg.generate(6)); + s.add(this.pg.generate(6)); Assert.assertEquals(FALSE, s.sat()); s.reset(); - s.add(pg.generate(7)); + s.add(this.pg.generate(7)); Assert.assertEquals(FALSE, s.sat()); s.reset(); } @@ -535,18 +543,18 @@ public void testModelEnumeration() { final SortedSet lits = new TreeSet<>(); final SortedSet firstFive = new TreeSet<>(); for (int j = 0; j < 20; j++) { - Variable lit = f.variable("x" + j); + final Variable lit = this.f.variable("x" + j); lits.add(lit); if (j < 5) { firstFive.add(lit); } } - s.add(f.cc(CType.GE, 1, lits)); + s.add(this.f.cc(CType.GE, 1, lits)); - List models = s.enumerateAllModels(firstFive, lits); + final List models = s.enumerateAllModels(firstFive, lits); Assert.assertEquals(32, models.size()); - for (Assignment model : models) { - for (Variable lit : lits) { + for (final Assignment model : models) { + for (final Variable lit : lits) { Assert.assertTrue(model.positiveLiterals().contains(lit) || model.negativeVariables().contains(lit)); } } @@ -561,19 +569,19 @@ public void testModelEnumerationWithHandler() { final SortedSet lits = new TreeSet<>(); final SortedSet firstFive = new TreeSet<>(); for (int j = 0; j < 20; j++) { - Variable lit = f.variable("x" + j); + final Variable lit = this.f.variable("x" + j); lits.add(lit); if (j < 5) { firstFive.add(lit); } } - s.add(f.cc(CType.GE, 1, lits)); + s.add(this.f.cc(CType.GE, 1, lits)); - NumberOfModelsHandler handler = new NumberOfModelsHandler(29); - List modelsWithHandler = s.enumerateAllModels(firstFive, lits, handler); + final NumberOfModelsHandler handler = new NumberOfModelsHandler(29); + final List modelsWithHandler = s.enumerateAllModels(firstFive, lits, handler); Assert.assertEquals(29, modelsWithHandler.size()); - for (Assignment model : modelsWithHandler) { - for (Variable lit : lits) { + for (final Assignment model : modelsWithHandler) { + for (final Variable lit : lits) { Assert.assertTrue(model.positiveLiterals().contains(lit) || model.negativeVariables().contains(lit)); } } @@ -585,8 +593,8 @@ public void testModelEnumerationWithHandler() { public void testEmptyEnumeration() { for (int i = 0; i < this.solvers.length - 1; i++) { final SATSolver s = this.solvers[i]; - s.add(f.falsum()); - List models = s.enumerateAllModels(); + s.add(this.f.falsum()); + final List models = s.enumerateAllModels(); Assert.assertTrue(models.isEmpty()); s.reset(); @@ -598,38 +606,43 @@ public void testNumberOfModelHandler() { for (int i = 0; i < this.solvers.length - 1; i++) { final SATSolver s = this.solvers[i]; final Variable[] lits = new Variable[100]; - for (int j = 0; j < lits.length; j++) - lits[j] = f.variable("x" + j); - s.add(f.exo(lits)); + for (int j = 0; j < lits.length; j++) { + lits[j] = this.f.variable("x" + j); + } + s.add(this.f.exo(lits)); NumberOfModelsHandler handler = new NumberOfModelsHandler(100); List models = s.enumerateAllModels(lits, handler); Assert.assertEquals(100, models.size()); - for (final Assignment m : models) + for (final Assignment m : models) { Assert.assertEquals(1, m.positiveLiterals().size()); + } s.reset(); - s.add(f.exo(lits)); + s.add(this.f.exo(lits)); handler = new NumberOfModelsHandler(200); models = s.enumerateAllModels(lits, handler); Assert.assertEquals(100, models.size()); - for (final Assignment m : models) + for (final Assignment m : models) { Assert.assertEquals(1, m.positiveLiterals().size()); + } s.reset(); - s.add(f.exo(lits)); + s.add(this.f.exo(lits)); handler = new NumberOfModelsHandler(50); models = s.enumerateAllModels(lits, handler); Assert.assertEquals(50, models.size()); - for (final Assignment m : models) + for (final Assignment m : models) { Assert.assertEquals(1, m.positiveLiterals().size()); + } s.reset(); - s.add(f.exo(lits)); + s.add(this.f.exo(lits)); handler = new NumberOfModelsHandler(1); models = s.enumerateAllModels(lits, handler); Assert.assertEquals(1, models.size()); - for (final Assignment m : models) + for (final Assignment m : models) { Assert.assertEquals(1, m.positiveLiterals().size()); + } s.reset(); } } @@ -641,54 +654,54 @@ public void testIllegalHandler() { @Test(expected = IllegalArgumentException.class) public void testAddNonCCAsCC() { - MiniSat solver = MiniSat.miniSat(f); + final MiniSat solver = MiniSat.miniSat(this.f); solver.addIncrementalCC((PBConstraint) F.PBC3); } @Test(expected = IllegalStateException.class) public void testModelBeforeSolving() { - MiniSat solver = MiniSat.miniSat(f); + final MiniSat solver = MiniSat.miniSat(this.f); solver.model(); } @Test(expected = IllegalArgumentException.class) public void testCLAddNonCCAsCC() { - CleaneLing solver = CleaneLing.minimalistic(f, new CleaneLingConfig.Builder().gluered(true).build()); + final CleaneLing solver = CleaneLing.minimalistic(this.f, new CleaneLingConfig.Builder().gluered(true).build()); solver.addIncrementalCC((PBConstraint) F.PBC3); } @Test(expected = IllegalStateException.class) public void testCLModelBeforeSolving() { - CleaneLing solver = CleaneLing.minimalistic(f); + final CleaneLing solver = CleaneLing.minimalistic(this.f); solver.model(); } @Test(expected = UnsupportedOperationException.class) public void testCLSatWithLit() { - CleaneLing solver = CleaneLing.minimalistic(f); + final CleaneLing solver = CleaneLing.minimalistic(this.f); solver.add(F.AND1); solver.sat(new TimeoutSATHandler(10000), F.A); } @Test(expected = UnsupportedOperationException.class) public void testCLSaveState() { - CleaneLing solver = CleaneLing.minimalistic(f); + final CleaneLing solver = CleaneLing.minimalistic(this.f); solver.add(F.AND1); solver.saveState(); } @Test(expected = UnsupportedOperationException.class) public void testCLLoadState() { - CleaneLing solver = CleaneLing.minimalistic(f); + final CleaneLing solver = CleaneLing.minimalistic(this.f); solver.add(F.AND1); solver.loadState(new SolverState(27, new int[3])); } @Test(expected = UnsupportedOperationException.class) public void testCLSatWithLitCollection() { - CleaneLing solver = CleaneLing.minimalistic(f); + final CleaneLing solver = CleaneLing.minimalistic(this.f); solver.add(F.AND1); - List lits = new ArrayList<>(); + final List lits = new ArrayList<>(); lits.add(F.A); lits.add(F.B); solver.sat(new TimeoutSATHandler(10000), lits); @@ -696,7 +709,7 @@ public void testCLSatWithLitCollection() { @Test(expected = UnsupportedOperationException.class) public void testCLEnumerateWithWrongConfig() { - CleaneLing solver = CleaneLing.full(f, new CleaneLingConfig.Builder().plain(false).build()); + final CleaneLing solver = CleaneLing.full(this.f, new CleaneLingConfig.Builder().plain(false).build()); solver.add(F.AND1); solver.enumerateAllModels(); } @@ -710,11 +723,11 @@ public void testToString() { @Test public void testPrintMinimalisticCleaneLing() { - CleaneLingMinimalisticSolver clms = new CleaneLingMinimalisticSolver(new CleaneLingConfig.Builder().build()); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - PrintStream ps = new PrintStream(baos); + final CleaneLingMinimalisticSolver clms = new CleaneLingMinimalisticSolver(new CleaneLingConfig.Builder().build()); + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final PrintStream ps = new PrintStream(baos); clms.printSolverState(ps); - String expected = String.format("level=0%n" + + final String expected = String.format("level=0%n" + "next=0%n" + "ignore=null%n" + "empty=null%n" + @@ -731,36 +744,36 @@ public void testPrintMinimalisticCleaneLing() { @Test public void testKnownVariables() throws ParserException { - final PropositionalParser parser = new PropositionalParser(f); + final PropositionalParser parser = new PropositionalParser(this.f); final Formula phi = parser.parse("x1 & x2 & x3 & (x4 | ~x5)"); - final SATSolver minisat = MiniSat.miniSat(f); - final SATSolver minicard = MiniSat.miniCard(f); - final SATSolver cleaneling = CleaneLing.minimalistic(f); + final SATSolver minisat = MiniSat.miniSat(this.f); + final SATSolver minicard = MiniSat.miniCard(this.f); + final SATSolver cleaneling = CleaneLing.minimalistic(this.f); minisat.add(phi); minicard.add(phi); cleaneling.add(phi); final SortedSet expected = new TreeSet<>(Arrays.asList( - f.variable("x1"), - f.variable("x2"), - f.variable("x3"), - f.variable("x4"), - f.variable("x5"))); + this.f.variable("x1"), + this.f.variable("x2"), + this.f.variable("x3"), + this.f.variable("x4"), + this.f.variable("x5"))); Assert.assertEquals(expected, minisat.knownVariables()); Assert.assertEquals(expected, minicard.knownVariables()); Assert.assertEquals(expected, cleaneling.knownVariables()); final SolverState state = minisat.saveState(); final SolverState stateCard = minicard.saveState(); - minisat.add(f.variable("x6")); - minicard.add(f.variable("x6")); - cleaneling.add(f.variable("x6")); + minisat.add(this.f.variable("x6")); + minicard.add(this.f.variable("x6")); + cleaneling.add(this.f.variable("x6")); final SortedSet expected2 = new TreeSet<>(Arrays.asList( - f.variable("x1"), - f.variable("x2"), - f.variable("x3"), - f.variable("x4"), - f.variable("x5"), - f.variable("x6"))); + this.f.variable("x1"), + this.f.variable("x2"), + this.f.variable("x3"), + this.f.variable("x4"), + this.f.variable("x5"), + this.f.variable("x6"))); Assert.assertEquals(expected2, minisat.knownVariables()); Assert.assertEquals(expected2, minicard.knownVariables()); Assert.assertEquals(expected2, cleaneling.knownVariables()); @@ -774,30 +787,30 @@ public void testKnownVariables() throws ParserException { @Test public void testAddWithoutUnknown() throws ParserException { - final PropositionalParser parser = new PropositionalParser(f); + final PropositionalParser parser = new PropositionalParser(this.f); final Formula phi = parser.parse("x1 & (~x2 | x3) & (x4 | ~x5)"); final SortedSet phiVars = new TreeSet<>(Arrays.asList( - f.variable("x1"), - f.variable("x2"), - f.variable("x3"), - f.variable("x4"), - f.variable("x5"))); + this.f.variable("x1"), + this.f.variable("x2"), + this.f.variable("x3"), + this.f.variable("x4"), + this.f.variable("x5"))); final Formula add1 = parser.parse("x1 | x6 | x7"); final Formula add2 = parser.parse("~x1 | ~x6 | x8"); final Formula add3 = parser.parse("x2 & ~x3 | x7"); final Formula add4 = parser.parse("x8 | x9"); - final SATSolver minisat = MiniSat.miniSat(f); - final SATSolver minicard = MiniSat.miniCard(f); - final SATSolver cleaneling = CleaneLing.minimalistic(f); + final SATSolver minisat = MiniSat.miniSat(this.f); + final SATSolver minicard = MiniSat.miniCard(this.f); + final SATSolver cleaneling = CleaneLing.minimalistic(this.f); final SATSolver[] solvers = new SATSolver[]{minisat, minicard, cleaneling}; for (final SATSolver solver : solvers) { solver.add(phi); solver.addWithoutUnknown(add1); Assert.assertEquals(TRUE, solver.sat()); - Assert.assertEquals(phiVars, solver.model().formula(f).variables()); + Assert.assertEquals(phiVars, solver.model().formula(this.f).variables()); solver.addWithoutUnknown(add2); Assert.assertEquals(TRUE, solver.sat()); - Assert.assertEquals(phiVars, solver.model().formula(f).variables()); + Assert.assertEquals(phiVars, solver.model().formula(this.f).variables()); if (solver instanceof MiniSat) { final SolverState state = solver.saveState(); solver.addWithoutUnknown(add3); @@ -805,17 +818,17 @@ public void testAddWithoutUnknown() throws ParserException { solver.loadState(state); solver.add(add1); Assert.assertEquals(TRUE, solver.sat()); - Assert.assertTrue(solver.model().formula(f).variables().containsAll(Arrays.asList(f.variable("x6"), f.variable("x7")))); + Assert.assertTrue(solver.model().formula(this.f).variables().containsAll(Arrays.asList(this.f.variable("x6"), this.f.variable("x7")))); solver.loadState(state); solver.sat(); - Assert.assertEquals(phiVars, solver.model().formula(f).variables()); + Assert.assertEquals(phiVars, solver.model().formula(this.f).variables()); } else { solver.add(add1); Assert.assertEquals(TRUE, solver.sat()); - Assert.assertTrue(solver.model().formula(f).variables().containsAll(Arrays.asList(f.variable("x6"), f.variable("x7")))); - solver.add(f.variable("x7")); + Assert.assertTrue(solver.model().formula(this.f).variables().containsAll(Arrays.asList(this.f.variable("x6"), this.f.variable("x7")))); + solver.add(this.f.variable("x7")); Assert.assertEquals(TRUE, solver.sat()); - Assert.assertTrue(solver.model().formula(f).variables().containsAll(Arrays.asList(f.variable("x6"), f.variable("x7")))); + Assert.assertTrue(solver.model().formula(this.f).variables().containsAll(Arrays.asList(this.f.variable("x6"), this.f.variable("x7")))); solver.addWithoutUnknown(add4); Assert.assertEquals(FALSE, solver.sat()); } @@ -824,7 +837,7 @@ public void testAddWithoutUnknown() throws ParserException { @Test public void testUPZeroLiteralsUNSAT() throws ParserException { - Formula formula = parser.parse("a & (a => b) & (b => c) & (c => ~a)"); + final Formula formula = this.parser.parse("a & (a => b) & (b => c) & (c => ~a)"); for (final SATSolver solver : this.solvers) { solver.reset(); solver.add(formula); @@ -884,4 +897,42 @@ public void testUPZeroLiteralsDimacsFiles() throws IOException { solver.reset(); } } + + @Test + public void testFormulaOnSolver() throws ParserException { + for (final SATSolver solver : this.solvers) { + if (solver instanceof MiniSat) { + final PseudoBooleanParser p = new PseudoBooleanParser(this.f); + final Set formulas = new LinkedHashSet<>(); + formulas.add(p.parse("A | B | C")); + formulas.add(p.parse("~A | ~B | ~C")); + formulas.add(p.parse("A | ~B")); + formulas.add(p.parse("A")); + solver.add(formulas); + compareFormulas(formulas, solver.formulaOnSolver()); + formulas.add(p.parse("~A | C")); + solver.reset(); + solver.add(formulas); + compareFormulas(formulas, solver.formulaOnSolver()); + final Formula formula = p.parse("C + D + E <= 2"); + formulas.add(formula); + solver.add(formula); + compareFormulas(formulas, solver.formulaOnSolver()); + } + } + } + + private void compareFormulas(final Collection original, final Collection solver) { + final SortedSet vars = new TreeSet<>(); + for (final Formula formula : original) { + vars.addAll(formula.variables()); + } + final MiniSat miniSat = MiniSat.miniSat(this.f); + miniSat.add(original); + final List models1 = miniSat.enumerateAllModels(vars); + miniSat.reset(); + miniSat.add(solver); + final List models2 = miniSat.enumerateAllModels(vars); + assertThat(models1).containsOnlyElementsOf(models2); + } } From c8f1eddc7c0a706c9112b796a5d1e4632fcbc0d9 Mon Sep 17 00:00:00 2001 From: Christoph Zengler Date: Fri, 23 Aug 2019 10:22:08 +0200 Subject: [PATCH 12/16] Do not store proof generation info for unsatisfiable solver states when solving with assumptions --- src/main/java/org/logicng/solvers/sat/GlucoseSyrup.java | 2 +- src/main/java/org/logicng/solvers/sat/MiniSat2Solver.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/logicng/solvers/sat/GlucoseSyrup.java b/src/main/java/org/logicng/solvers/sat/GlucoseSyrup.java index 906c7393..98e8b43a 100644 --- a/src/main/java/org/logicng/solvers/sat/GlucoseSyrup.java +++ b/src/main/java/org/logicng/solvers/sat/GlucoseSyrup.java @@ -302,7 +302,7 @@ public Tristate solve(final SATHandler handler) { while (status == Tristate.UNDEF && !canceledByHandler) status = search(); - if (this.config.proofGeneration) { + if (this.config.proofGeneration && this.assumptions.empty()) { if (status == Tristate.FALSE) this.pgProof.push(new LNGIntVector(1, 0)); } diff --git a/src/main/java/org/logicng/solvers/sat/MiniSat2Solver.java b/src/main/java/org/logicng/solvers/sat/MiniSat2Solver.java index 5b7d593a..e6fb3e05 100644 --- a/src/main/java/org/logicng/solvers/sat/MiniSat2Solver.java +++ b/src/main/java/org/logicng/solvers/sat/MiniSat2Solver.java @@ -203,7 +203,7 @@ public Tristate solve(final SATHandler handler) { currRestarts++; } - if (this.config.proofGeneration) { + if (this.config.proofGeneration && this.assumptions.empty()) { if (status == Tristate.FALSE) this.pgProof.push(new LNGIntVector(1, 0)); } From c7f7972e90344e3176b5bead99db235a732fe886 Mon Sep 17 00:00:00 2001 From: Christoph Zengler Date: Fri, 23 Aug 2019 10:31:31 +0200 Subject: [PATCH 13/16] next version in POM --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8b59719d..91ffa6e8 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ 4.0.0 org.logicng logicng - 1.5.1 + 1.6.0 jar LogicNG From 7aaa1f1a469ba0c9ff2a68dc5b4e4227b9a859f3 Mon Sep 17 00:00:00 2001 From: Steffen Hildebrandt Date: Sun, 1 Sep 2019 09:43:07 +0200 Subject: [PATCH 14/16] make BDDFactory and BDDKernel extensible --- .../java/org/logicng/bdds/BDDFactory.java | 25 +++++++++++++------ .../org/logicng/bdds/jbuddy/BDDKernel.java | 18 ++++++------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/logicng/bdds/BDDFactory.java b/src/main/java/org/logicng/bdds/BDDFactory.java index fbdcafbb..0ffd3308 100644 --- a/src/main/java/org/logicng/bdds/BDDFactory.java +++ b/src/main/java/org/logicng/bdds/BDDFactory.java @@ -57,6 +57,8 @@ package org.logicng.bdds; +import static org.logicng.formulas.FType.AND; + import org.logicng.bdds.datastructures.BDD; import org.logicng.bdds.datastructures.BDDConstant; import org.logicng.bdds.datastructures.BDDInnerNode; @@ -87,20 +89,18 @@ import java.util.TreeMap; import java.util.TreeSet; -import static org.logicng.formulas.FType.AND; - /** * The factory for the jBuddy implementation. * @version 1.4.0 * @since 1.4.0 */ -public final class BDDFactory { +public class BDDFactory { - private final BDDKernel kernel; + protected final BDDKernel kernel; - private final FormulaFactory f; - private final SortedMap var2idx; - private final SortedMap idx2var; + protected final FormulaFactory f; + protected final SortedMap var2idx; + protected final SortedMap idx2var; /** @@ -110,8 +110,17 @@ public final class BDDFactory { * @param f the formula factory */ public BDDFactory(final int numNodes, final int cacheSize, final FormulaFactory f) { + this(new BDDKernel(numNodes, cacheSize), f); + } + + /** + * Constructs a new jBuddy BDD factory. + * @param kernel the BDD kernel + * @param f the formula factory + */ + protected BDDFactory(final BDDKernel kernel, final FormulaFactory f) { this.f = f; - this.kernel = new BDDKernel(numNodes, cacheSize); + this.kernel = kernel; this.var2idx = new TreeMap<>(); this.idx2var = new TreeMap<>(); } diff --git a/src/main/java/org/logicng/bdds/jbuddy/BDDKernel.java b/src/main/java/org/logicng/bdds/jbuddy/BDDKernel.java index 3180db8f..66cf64d5 100644 --- a/src/main/java/org/logicng/bdds/jbuddy/BDDKernel.java +++ b/src/main/java/org/logicng/bdds/jbuddy/BDDKernel.java @@ -68,7 +68,7 @@ * @version 1.4.0 * @since 1.4.0 */ -public final class BDDKernel { +public class BDDKernel { public static final int BDD_TRUE = 1; public static final int BDD_FALSE = 0; @@ -112,10 +112,10 @@ private enum Operand { private int freepos; // First free node private int freenum; // Number of free nodes private long produced; // Number of new nodes ever produced - private int varnum; // Number of defined BDD variables + protected int varnum; // Number of defined BDD variables private int[] refstack; // Internal node reference stack private int refstacktop; // Internal node reference stack top - private int[] level2var; // Level -> variable table + protected int[] level2var; // Level -> variable table private int[] quantvarset; // Current variable set for quant. private int quantvarsetID; // Current id used in quantvarset @@ -596,7 +596,7 @@ private void nodeResize() { gbcRehash(); } - private void initRef() { + protected void initRef() { this.refstacktop = 0; } @@ -621,23 +621,23 @@ private boolean isConst(final int a) { return a < 2; } - private boolean isOne(final int a) { + protected boolean isOne(final int a) { return a == 1; } - private boolean isZero(final int a) { + protected boolean isZero(final int a) { return a == 0; } - private int level(final int a) { + protected int level(final int a) { return this.nodes[a].level; } - private int low(final int a) { + protected int low(final int a) { return this.nodes[a].low; } - private int high(final int a) { + protected int high(final int a) { return this.nodes[a].high; } From ac7cc540ce4ae2cb7fd08c768a770a1dffa9d5c3 Mon Sep 17 00:00:00 2001 From: Christoph Zengler Date: Tue, 3 Sep 2019 12:48:02 +0200 Subject: [PATCH 15/16] fixed a small bug in the BDD helper method for constants --- src/main/java/org/logicng/formulas/Formula.java | 2 +- src/test/java/org/logicng/bdds/FormulaBDDTest.java | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/logicng/formulas/Formula.java b/src/main/java/org/logicng/formulas/Formula.java index f006383d..375117c9 100644 --- a/src/main/java/org/logicng/formulas/Formula.java +++ b/src/main/java/org/logicng/formulas/Formula.java @@ -243,7 +243,7 @@ public Formula cnf() { * @return the BDD for this formula with the given ordering */ public BDD bdd(final VariableOrdering variableOrdering) { - final int varNum = this.variables().size(); + final int varNum = this.variables().size() == 0 ? 1 : this.variables.size(); final BDDFactory factory = new BDDFactory(varNum * 30, varNum * 20, this.factory()); if (variableOrdering == null) { factory.setNumberOfVars(varNum); diff --git a/src/test/java/org/logicng/bdds/FormulaBDDTest.java b/src/test/java/org/logicng/bdds/FormulaBDDTest.java index 7d89a5b9..c0e87b8b 100644 --- a/src/test/java/org/logicng/bdds/FormulaBDDTest.java +++ b/src/test/java/org/logicng/bdds/FormulaBDDTest.java @@ -31,6 +31,7 @@ import org.junit.Test; import org.logicng.bdds.datastructures.BDD; import org.logicng.bdds.orderings.VariableOrdering; +import org.logicng.datastructures.Assignment; import org.logicng.formulas.Formula; import org.logicng.formulas.FormulaFactory; import org.logicng.io.parsers.ParserException; @@ -46,6 +47,17 @@ */ public class FormulaBDDTest { + @Test + public void testSimpleCases() { + final FormulaFactory f = new FormulaFactory(); + BDD bdd = f.verum().bdd(); + assertThat(bdd.isTautology()).isTrue(); + bdd = f.falsum().bdd(); + assertThat(bdd.isContradiction()).isTrue(); + bdd = f.variable("A").bdd(); + assertThat(bdd.enumerateAllModels()).containsExactly(new Assignment(f.variable("A"))); + } + @Test public void testBDDGeneration() throws ParserException { final FormulaFactory f = new FormulaFactory(); From f018ebaf76b632168d5618126699923d1b6829b5 Mon Sep 17 00:00:00 2001 From: Christoph Zengler Date: Tue, 3 Sep 2019 12:53:42 +0200 Subject: [PATCH 16/16] adjusted readme --- README.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c268f157..70328409 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![wercker status](https://app.wercker.com/status/24c4765f3a0d79520ad80a1e4c20cfa2/s/master "wercker status")](https://app.wercker.com/project/bykey/24c4765f3a0d79520ad80a1e4c20cfa2) [![Coverage Status](https://coveralls.io/repos/logic-ng/LogicNG/badge.svg?branch=master&service=github)](https://coveralls.io/github/logic-ng/LogicNG?branch=master) ![License](https://img.shields.io/badge/license-Apache%202-blue.svg) ![Version](https://img.shields.io/badge/version-1.5.2-ff69b4.svg) +[![wercker status](https://app.wercker.com/status/24c4765f3a0d79520ad80a1e4c20cfa2/s/master "wercker status")](https://app.wercker.com/project/bykey/24c4765f3a0d79520ad80a1e4c20cfa2) [![Coverage Status](https://coveralls.io/repos/logic-ng/LogicNG/badge.svg?branch=master&service=github)](https://coveralls.io/github/logic-ng/LogicNG?branch=master) ![License](https://img.shields.io/badge/license-Apache%202-blue.svg) ![Version](https://img.shields.io/badge/version-1.6.0-ff69b4.svg) logo @@ -19,7 +19,7 @@ LogicNG is released in the Maven Central Repository. To include it just add org.logicng logicng - 1.5.2 + 1.6.0 ``` to your Maven POM. @@ -63,6 +63,15 @@ The library is released under the Apache License and therefore is free to use in ## Changelog +### Version 1.6.0 (Release September 2019) +* A new method for generating CNFs directly on the solver instead of using the formula factory. + This often leads to a faster generation and reduced Heap consumption but with the loss of + caching +* The standard MiniSat-based solvers can now directly efficiently compute a backone. No extra solver + is required anymore +* The current formula on a MiniSat-based solver can be extracted +* BDD factory can now be extended externally + ### Version 1.5.2 (Release July 2019) * Fixed caching behaviour when using a `sat()` call without assumptions after a call with assumptions * Clarified behaviour of the `Backbone` object