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

org.chocosolver.solver.constraints.ISatFactory Maven / Gradle / Ivy

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

import gnu.trove.list.TIntList;
import gnu.trove.list.array.TIntArrayList;
import org.chocosolver.sat.Literalizer;
import org.chocosolver.sat.MiniSat;
import org.chocosolver.sat.SatDecorator;
import org.chocosolver.solver.ISelf;
import org.chocosolver.solver.Model;
import org.chocosolver.solver.constraints.extension.Tuples;
import org.chocosolver.solver.constraints.extension.hybrid.HybridTuples;
import org.chocosolver.solver.constraints.nary.cnf.ILogical;
import org.chocosolver.solver.constraints.nary.cnf.LogOp;
import org.chocosolver.solver.constraints.nary.cnf.LogicTreeToolBox;
import org.chocosolver.solver.constraints.nary.sat.PropSat;
import org.chocosolver.solver.constraints.reification.LocalConstructiveDisjunction;
import org.chocosolver.solver.variables.BoolVar;
import org.chocosolver.solver.variables.IntVar;
import org.chocosolver.solver.variables.Variable;

import java.util.Arrays;

import static org.chocosolver.solver.variables.IntVar.LR_NE;

/**
 * A factory dedicated to SAT.
 * 

*
* * @author Charles Prud'homme * @since 15/07/13 */ public interface ISatFactory extends ISelf { /** * Creates, or returns if already existing, the SAT variable corresponding to this CP relationship. * * @param var a boolean variable * @return its SAT twin */ default int satVar(Variable var, Literalizer ltz) { PropSat psat = ref().getMinisat().getPropSat(); SatDecorator msat = (SatDecorator) psat.getMiniSat(); return msat.bind(var, ltz, psat::lazyAddVar); } default MiniSat sat() { if (ref().getSolver().isLCG()) { return ref().getSolver().getSat(); } PropSat psat = ref().getMinisat().getPropSat(); return psat.getMiniSat(); } /** * Return the positive literal of a SAT variable * * @param svar a sat variable * @return its positive literal */ default int lit(int svar) { return MiniSat.makeLiteral(svar, true); } /** * Return the negative literal of a SAT variable * * @param svar a sat variable * @return its negative literal */ default int neg(int svar) { return MiniSat.makeLiteral(svar, false); } /** * Add a clause based on SAT variable * * @param lits a sat variable * @return {@code true} if the clause has been added correctly. */ default boolean addClause(int... lits) { TIntList mlits = new TIntArrayList(lits); return sat().addClause(mlits); } /** * Ensures that the clauses defined in the Boolean logic formula TREE are satisfied. * * @param TREE the syntactic tree * @return true if the clause has been added to the clause store */ default boolean addClauses(LogOp TREE) { ILogical tree = LogicTreeToolBox.toCNF(TREE, ref()); boolean ret = true; if (ref().boolVar(true).equals(tree)) { ret = addClauseTrue(ref().boolVar(true)); } else if (ref().boolVar(false).equals(tree)) { ret = addClauseTrue(ref().boolVar(false)); } else { ILogical[] clauses; if (!tree.isLit() && ((LogOp) tree).is(LogOp.Operator.AND)) { clauses = ((LogOp) tree).getChildren(); } else { clauses = new ILogical[]{tree}; } for (int i = 0; i < clauses.length; i++) { ILogical clause = clauses[i]; if (clause.isLit()) { BoolVar bv = (BoolVar) clause; ret &= addClauseTrue(bv); } else { LogOp n = (LogOp) clause; BoolVar[] bvars = n.flattenBoolVar(); if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { TIntList lits = new TIntArrayList(bvars.length); MiniSat sat = sat(); // init internal structures sat.beforeAddingClauses(); for (int j = 0; j < bvars.length; j++) { lits.add(MiniSat.makeLiteral(bvars[j].satVar(), true)); } // TODO: pass by satsolver directly ret &= sat.addClause(lits); sat.afterAddingClauses(); } else { ref().sum(bvars, ">", 0).post(); } } } } return ret; } /** * Ensures that the clause defined by POSLITS and NEGLITS is satisfied. * * @param POSLITS positive literals * @param NEGLITS negative literals * @return true if the clause has been added to the clause store */ default boolean addClauses(BoolVar[] POSLITS, BoolVar[] NEGLITS) { if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { MiniSat sat = sat(); sat.beforeAddingClauses(); int[] pos = new int[POSLITS.length]; for (int i = 0; i < POSLITS.length; i++) { pos[i] = POSLITS[i].satVar(); } int[] neg = new int[NEGLITS.length]; for (int i = 0; i < NEGLITS.length; i++) { neg[i] = NEGLITS[i].satVar(); } boolean add = sat.addClause(pos, neg); sat.afterAddingClauses(); return add; } else { int PL = POSLITS.length; int NL = NEGLITS.length; BoolVar[] LITS = new BoolVar[PL + NL]; System.arraycopy(POSLITS, 0, LITS, 0, PL); for (int i = 0; i < NL; i++) { LITS[i + PL] = NEGLITS[i].not(); } ref().sum(LITS, ">", 0).post(); return true; } } /** * Add a unit clause stating that BOOLVAR must be true * * @param BOOLVAR a boolean variable * @return true if the clause has been added to the clause store */ default boolean addClauseTrue(BoolVar BOOLVAR) { if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { MiniSat sat = sat(); sat.beforeAddingClauses(); boolean add = sat.addTrue(BOOLVAR.satVar()); sat.afterAddingClauses(); return add; } else { ref().arithm(BOOLVAR, "=", 1).post(); return true; } } /** * Add a unit clause stating that BOOLVAR must be false * * @param BOOLVAR a boolean variable * @return true if the clause has been added to the clause store */ default boolean addClauseFalse(BoolVar BOOLVAR) { if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { MiniSat sat = sat(); sat.beforeAddingClauses(); boolean add = sat.addFalse(BOOLVAR.satVar()); sat.afterAddingClauses(); return add; } else { ref().arithm(BOOLVAR, "=", 0).post(); return true; } } /** * Add a clause stating that: LEFT == RIGHT * * @param LEFT a boolean variable * @param RIGHT another boolean variable * @return true if the clause has been added to the clause store */ default boolean addClausesBoolEq(BoolVar LEFT, BoolVar RIGHT) { if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { MiniSat sat = sat(); sat.beforeAddingClauses(); boolean add = sat.addBoolEq(LEFT.satVar(), RIGHT.satVar()); sat.afterAddingClauses(); return add; } else { ref().arithm(LEFT, "=", RIGHT).post(); return true; } } /** * Add a clause stating that: LEFT ≤ RIGHT * * @param LEFT a boolean variable * @param RIGHT another boolean variable * @return true if the clause has been added to the clause store */ default boolean addClausesBoolLe(BoolVar LEFT, BoolVar RIGHT) { if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { MiniSat sat = sat(); sat.beforeAddingClauses(); boolean add = sat.addBoolLe(LEFT.satVar(), RIGHT.satVar()); sat.afterAddingClauses(); return add; } else { ref().arithm(LEFT, "<=", RIGHT).post(); return true; } } /** * Add a clause stating that: LEFT < RIGHT * * @param LEFT a boolean variable * @param RIGHT another boolean variable * @return true if the clause has been added to the clause store */ default boolean addClausesBoolLt(BoolVar LEFT, BoolVar RIGHT) { if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { MiniSat sat = sat(); sat.beforeAddingClauses(); boolean add = sat.addBoolLt(LEFT.satVar(), RIGHT.satVar()); sat.afterAddingClauses(); return add; } else { ref().arithm(LEFT, "<", RIGHT).post(); return true; } } /** * Add a clause stating that: LEFT != RIGHT * * @param LEFT a boolean variable * @param RIGHT another boolean variable * @return true if the clause has been added to the clause store */ default boolean addClausesBoolNot(BoolVar LEFT, BoolVar RIGHT) { if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { MiniSat sat = sat(); sat.beforeAddingClauses(); boolean add = sat.addBoolNot(LEFT.satVar(), RIGHT.satVar()); sat.afterAddingClauses(); return add; } else { ref().arithm(LEFT, "!=", RIGHT).post(); return true; } } /** * Add a clause stating that: (BOOLVARS1∨BOOLVARS2∨...∨BOOLVARSn) ⇔ TARGET * * @param BOOLVARS a list of boolean variables * @param TARGET the reified boolean variable * @return true if the clause has been added to the clause store */ default boolean addClausesBoolOrArrayEqVar(BoolVar[] BOOLVARS, BoolVar TARGET) { if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { MiniSat sat = sat(); sat.beforeAddingClauses(); int[] vars = new int[BOOLVARS.length]; for (int i = 0; i < BOOLVARS.length; i++) { vars[i] = BOOLVARS[i].satVar(); } boolean add = sat.addBoolOrArrayEqVar(vars, TARGET.satVar()); sat.afterAddingClauses(); return add; } else { ref().max(TARGET, BOOLVARS).post(); return true; } } /** * Add a clause stating that: (BOOLVARS1∧BOOLVARS2∧...∧BOOLVARSn) ⇔ TARGET * * @param BOOLVARS a list of boolean variables * @param TARGET the reified boolean variable * @return true if the clause has been added to the clause store */ default boolean addClausesBoolAndArrayEqVar(BoolVar[] BOOLVARS, BoolVar TARGET) { if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { MiniSat sat = sat(); sat.beforeAddingClauses(); int[] vars = new int[BOOLVARS.length]; for (int i = 0; i < BOOLVARS.length; i++) { vars[i] = BOOLVARS[i].satVar(); } boolean add = sat.addBoolAndArrayEqVar(vars, TARGET.satVar()); sat.afterAddingClauses(); return add; } else { ref().min(TARGET, BOOLVARS).post(); return true; } } /** * Add a clause stating that: (LEFT ∨ RIGHT) ⇔ TARGET * * @param LEFT a boolean variable * @param RIGHT another boolean variable * @param TARGET the reified boolean variable * @return true if the clause has been added to the clause store */ default boolean addClausesBoolOrEqVar(BoolVar LEFT, BoolVar RIGHT, BoolVar TARGET) { if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { MiniSat sat = sat(); sat.beforeAddingClauses(); boolean add = sat.addBoolOrEqVar(LEFT.satVar(), RIGHT.satVar(), TARGET.satVar()); sat.afterAddingClauses(); return add; } else { ref().arithm(LEFT, "+", RIGHT, ">", 0).reifyWith(TARGET); return true; } } /** * Add a clause stating that: (LEFT ∧ RIGHT) ⇔ TARGET * * @param LEFT a boolean variable * @param RIGHT another boolean variable * @param TARGET the reified boolean variable * @return true if the clause has been added to the clause store */ default boolean addClausesBoolAndEqVar(BoolVar LEFT, BoolVar RIGHT, BoolVar TARGET) { if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { MiniSat sat = sat(); sat.beforeAddingClauses(); boolean add = sat.addBoolAndEqVar(LEFT.satVar(), RIGHT.satVar(), TARGET.satVar()); sat.afterAddingClauses(); return add; } else { ref().arithm(LEFT, "+", RIGHT, "=", 2).reifyWith(TARGET); return true; } } /** * Add a clause stating that: (LEFT ⊕ RIGHT) ⇔ TARGET * * @param LEFT a boolean variable * @param RIGHT another boolean variable * @param TARGET the reified boolean variable * @return true if the clause has been added to the clause store */ default boolean addClausesBoolXorEqVar(BoolVar LEFT, BoolVar RIGHT, BoolVar TARGET) { return addClausesBoolIsNeqVar(LEFT, RIGHT, TARGET); } /** * Add a clause stating that: (LEFT == RIGHT) ⇔ TARGET * * @param LEFT a boolean variable * @param RIGHT another boolean variable * @param TARGET the reified boolean variable * @return true if the clause has been added to the clause store */ default boolean addClausesBoolIsEqVar(BoolVar LEFT, BoolVar RIGHT, BoolVar TARGET) { if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { MiniSat sat = sat(); sat.beforeAddingClauses(); boolean add = sat.addBoolIsEqVar(LEFT.satVar(), RIGHT.satVar(), TARGET.satVar()); sat.afterAddingClauses(); return add; } else { ref().reifyXeqY(LEFT, RIGHT, TARGET); return true; } } /** * Add a clause stating that: (LEFT ≠ RIGHT) ⇔ TARGET * * @param LEFT a boolean variable * @param RIGHT another boolean variable * @param TARGET the reified boolean variable * @return true if the clause has been added to the clause store */ default boolean addClausesBoolIsNeqVar(BoolVar LEFT, BoolVar RIGHT, BoolVar TARGET) { if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { MiniSat sat = sat(); sat.beforeAddingClauses(); boolean add = sat.addBoolIsNeqVar(LEFT.satVar(), RIGHT.satVar(), TARGET.satVar()); sat.afterAddingClauses(); return add; } else { ref().reifyXneY(LEFT, RIGHT, TARGET); return true; } } /** * Add a clause stating that: (LEFT ≤ RIGHT) ⇔ TARGET * * @param LEFT a boolean variable * @param RIGHT another boolean variable * @param TARGET the reified boolean variable * @return true if the clause has been added to the clause store */ default boolean addClausesBoolIsLeVar(BoolVar LEFT, BoolVar RIGHT, BoolVar TARGET) { if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { MiniSat sat = sat(); sat.beforeAddingClauses(); boolean add = sat.addBoolIsLeVar(LEFT.satVar(), RIGHT.satVar(), TARGET.satVar()); sat.afterAddingClauses(); return add; } else { ref().reifyXleY(LEFT, RIGHT, TARGET); return true; } } /** * Add a clause stating that: (LEFT < RIGHT) ⇔ TARGET * * @param LEFT a boolean variable * @param RIGHT another boolean variable * @param TARGET the reified boolean variable * @return true if the clause has been added to the clause store */ default boolean addClausesBoolIsLtVar(BoolVar LEFT, BoolVar RIGHT, BoolVar TARGET) { if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { MiniSat sat = sat(); sat.beforeAddingClauses(); boolean add = sat.addBoolIsLtVar(LEFT.satVar(), RIGHT.satVar(), TARGET.satVar()); sat.afterAddingClauses(); return add; } else { ref().reifyXltY(LEFT, RIGHT, TARGET); return true; } } /** * Add a clause stating that: BOOLVARS1∨BOOLVARS2∨...∨BOOLVARSn * * @param BOOLVARS a list of boolean variables * @return true if the clause has been added to the clause store */ default boolean addClausesBoolOrArrayEqualTrue(BoolVar[] BOOLVARS) { if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { MiniSat sat = sat(); sat.beforeAddingClauses(); int[] vars = new int[BOOLVARS.length]; for (int i = 0; i < BOOLVARS.length; i++) { vars[i] = BOOLVARS[i].satVar(); } boolean add = sat.addBoolOrArrayEqualTrue(vars); sat.afterAddingClauses(); return add; } else { ref().sum(BOOLVARS, ">", 0).post(); return true; } } /** * Add a clause stating that: BOOLVARS1∧BOOLVARS2∧...∧BOOLVARSn * * @param BOOLVARS a list of boolean variables * @return true if the clause has been added to the clause store */ default boolean addClausesBoolAndArrayEqualFalse(BoolVar[] BOOLVARS) { return addClausesAtMostNMinusOne(BOOLVARS); } /** * Add a clause stating that: ∑ BOOLVARSi ≤ 1 * * @param BOOLVARS a list of boolean variables * @return true if the clause has been added to the clause store */ default boolean addClausesAtMostOne(BoolVar[] BOOLVARS) { if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { MiniSat sat = sat(); sat.beforeAddingClauses(); int[] vars = new int[BOOLVARS.length]; for (int i = 0; i < BOOLVARS.length; i++) { vars[i] = BOOLVARS[i].satVar(); } boolean add = sat.addAtMostOne(vars); sat.afterAddingClauses(); return add; } else { ref().sum(BOOLVARS, "<", 2).post(); return true; } } /** * Add a clause stating that: ∑ BOOLVARSi ≤ n-1 * * @param BOOLVARS a list of boolean variables * @return true if the clause has been added to the clause store */ default boolean addClausesAtMostNMinusOne(BoolVar[] BOOLVARS) { if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { MiniSat sat = sat(); sat.beforeAddingClauses(); int[] vars = new int[BOOLVARS.length]; for (int i = 0; i < BOOLVARS.length; i++) { vars[i] = BOOLVARS[i].satVar(); } boolean add = sat.addAtMostNMinusOne(vars); sat.afterAddingClauses(); return add; } else { ref().sum(BOOLVARS, "<", BOOLVARS.length).post(); return true; } } /** * Add a clause stating that: sum(BOOLVARSi) ≥ TARGET * * @param BOOLVARS a list of boolean variables * @param TARGET a boolean variable * @return true if the clause has been added to the clause store */ default boolean addClausesSumBoolArrayGreaterEqVar(BoolVar[] BOOLVARS, BoolVar TARGET) { if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { MiniSat sat = sat(); sat.beforeAddingClauses(); int[] vars = new int[BOOLVARS.length]; for (int i = 0; i < BOOLVARS.length; i++) { vars[i] = BOOLVARS[i].satVar(); } boolean add = sat.addSumBoolArrayGreaterEqVar(vars, TARGET.satVar()); sat.afterAddingClauses(); return add; } else { ref().sum(BOOLVARS, ">=", TARGET).post(); return true; } } /** * Add a clause stating that: max(BOOLVARSi) = TARGET * * @param BOOLVARS a list of boolean variables * @param TARGET a boolean variable * @return true if the clause has been added to the clause store */ default boolean addClausesMaxBoolArrayLessEqVar(BoolVar[] BOOLVARS, BoolVar TARGET) { if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { MiniSat sat = sat(); sat.beforeAddingClauses(); int[] vars = new int[BOOLVARS.length]; for (int i = 0; i < BOOLVARS.length; i++) { vars[i] = BOOLVARS[i].satVar(); } boolean add = sat.addMaxBoolArrayLessEqVar(vars, TARGET.satVar()); sat.afterAddingClauses(); return add; } else { BoolVar max = ref().boolVar(ref().generateName("bool_max")); ref().max(max, BOOLVARS).post(); max.le(TARGET).post(); return true; } } /** * Add a clause stating that: sum(BOOLVARSi) ≤ TARGET * |BOOLVARS| * * @param BOOLVARS a list of boolean variables * @param TARGET a boolean variable * @return true if the clause has been added to the clause store */ default boolean addClausesSumBoolArrayLessEqKVar(BoolVar[] BOOLVARS, BoolVar TARGET) { if (ref().getSolver().isLCG() || ref().getSettings().enableSAT()) { MiniSat sat = sat(); sat.beforeAddingClauses(); boolean add = false; if (BOOLVARS.length == 1) { add = addClausesBoolLe(BOOLVARS[0], TARGET); } int[] vars = new int[BOOLVARS.length]; for (int i = 0; i < BOOLVARS.length; i++) { vars[i] = BOOLVARS[i].satVar(); } add |= sat.addSumBoolArrayLessEqKVar(vars, TARGET.satVar()); sat.afterAddingClauses(); return add; } else { int[] coeffs = new int[BOOLVARS.length + 1]; Arrays.fill(coeffs, 1); coeffs[BOOLVARS.length] = -BOOLVARS.length; BoolVar[] nBOOLVARS = new BoolVar[BOOLVARS.length + 1]; System.arraycopy(BOOLVARS, 0, nBOOLVARS, 0, BOOLVARS.length); nBOOLVARS[BOOLVARS.length] = TARGET; ref().scalar(nBOOLVARS, coeffs, "<=", 0).post(); return true; } } /** * Make a constructive disjunction constraint * * @param cstrs constraint in disjunction * @return true if the disjunction has been added to the constructive disjunction store. */ default boolean addConstructiveDisjunction(Constraint... cstrs) { new LocalConstructiveDisjunction(cstrs).post(); return true; } default boolean addElement(IntVar VALUE, int[] TABLE, IntVar INDEX, int OFFSET) { assert (ref().getSolver().isLCG() || ref().getSettings().enableSAT()); Tuples t = new Tuples(true); for (int i = 0; i < TABLE.length; i++) { // for(int i : INDEX){ t.add(i, TABLE[i]); } addTable(new IntVar[]{ref().intView(1, INDEX, -OFFSET), VALUE}, t); return true; } default boolean addTable(IntVar[] vars, Tuples tuples) { assert vars.length >= 2; assert (ref().getSolver().isLCG() || ref().getSettings().enableSAT()); MiniSat sat = sat(); sat.beforeAddingClauses(); if (!tuples.isFeasible()) { TIntList c = new TIntArrayList(); for (int i = 0; i < tuples.nbTuples(); i++) { int[] t = tuples.get(i); c.clear(); for (int j = 0; j < vars.length; j++) { c.add(vars[j].getLit(t[j], IntVar.LR_NE)); } sat.addClause(c); } sat.afterAddingClauses(); return true; } int star = tuples.allowUniversalValue() ? tuples.getStarValue() : Integer.MAX_VALUE; int base_lit = 2 * sat.nVars(); if (vars.length > 2) { for (int i = 0; i < tuples.nbTuples(); i++) { int[] t = tuples.get(i); sat.newVariable(); for (int j = 0; j < vars.length; j++) { if (t[j] != star) { sat.addClause(base_lit + 2 * i, vars[j].getLit(t[j], IntVar.LR_EQ)); } } } } for (int w = 0; w < vars.length; w++) { int lb = vars[w].getLB(); TIntList[] sup = new TIntList[vars[w].getRange()]; for (int i = lb; i <= vars[w].getUB(); i = vars[w].nextValue(i)) { sup[i - lb] = new TIntArrayList(); } for (int i = 0; i < tuples.nbTuples(); i++) { int[] t = tuples.get(i); int p = vars.length == 2 ? t[1 - w] == star ? 1 : vars[1 - w].getLit(t[1 - w], IntVar.LR_EQ) : base_lit + 2 * i + 1; int k = t[w] - lb; if (k >= 0 && k < sup.length && sup[k] != null) { sup[k].add(p); } else if (t[w] == star) { for (TIntList l : sup) { if (l != null) { l.add(p); } } } } for (int i = 0; i < sup.length; i++) { if (sup[i] == null) continue; if (sup[i].isEmpty()) { sat.addClause(vars[w].getLit(i + lb, LR_NE)); // Can lead to a bug in the SAT solver // vars[w].ne(i + lb).post(); } else { sup[i].add(vars[w].getLit(i + lb, LR_NE)); int p = sup[i].get(0); int last = sup[i].size() - 1; sup[i].set(0, sup[i].get(last)); sup[i].set(last, p); sat.addClause(sup[i]); } } } sat.afterAddingClauses(); return true; } default boolean addTable(IntVar[] vars, HybridTuples tuples) { throw new UnsupportedOperationException(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy