/******************************************************************************* * SAT4J: a SATisfiability library for Java Copyright (C) 2004-2008 Daniel Le Berre * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Alternatively, the contents of this file may be used under the terms of * either the GNU Lesser General Public License Version 2.1 or later (the * "LGPL"), in which case the provisions of the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of the LGPL, and not to allow others to use your version of * this file under the terms of the EPL, indicate your decision by deleting * the provisions above and replace them with the notice and other provisions * required by the LGPL. If you do not delete the provisions above, a recipient * may use your version of this file under the terms of the EPL or the LGPL. * * Based on the original MiniSat specification from: * * An extensible SAT solver. Niklas Een and Niklas Sorensson. Proceedings of the * Sixth International Conference on Theory and Applications of Satisfiability * Testing, LNCS 2919, pp 502-518, 2003. * * See www.minisat.se for the original solver in C++. * *******************************************************************************/ package org.sat4j.minisat.constraints.card; import java.io.Serializable; import org.sat4j.minisat.constraints.cnf.Lits; import org.sat4j.minisat.core.Constr; import org.sat4j.minisat.core.ILits; import org.sat4j.minisat.core.Undoable; import org.sat4j.minisat.core.UnitPropagationListener; import org.sat4j.specs.ContradictionException; import org.sat4j.specs.IVecInt; public class MinWatchCard implements Constr, Undoable, Serializable { private static final long serialVersionUID = 1L; public static final boolean ATLEAST = true; public static final boolean ATMOST = false; /** * degree of the cardinality constraint */ protected int degree; /** * literals involved in the constraint */ private int[] lits; /** * contains the sign of the constraint : ATLEAT or ATMOST */ private boolean moreThan; /** * contains the sum of the coefficients of the watched literals */ protected int watchCumul; /** * Vocabulary of the constraint */ private final ILits voc; /** * Constructs and normalizes a cardinality constraint. used by * minWatchCardNew in the non-normalized case. * * @param voc * vocabulary used by the constraint * @param ps * literals involved in the constraint * @param moreThan * should be ATLEAST or ATMOST; * @param degree * degree of the constraint */ public MinWatchCard(ILits voc, IVecInt ps, boolean moreThan, int degree) { // On met en place les valeurs this.voc = voc; this.degree = degree; this.moreThan = moreThan; // On simplifie ps int[] index = new int[voc.nVars() * 2 + 2]; // Fresh array should have all elements set to 0 // On repertorie les litt?raux utiles for (int i = 0; i < ps.size(); i++) { int p = ps.get(i); if (index[p ^ 1] == 0) { index[p]++; } else { index[p ^ 1]--; } } // On supprime les litt?raux inutiles int ind = 0; while (ind < ps.size()) { if (index[ps.get(ind)] > 0) { index[ps.get(ind)]--; ind++; } else { // ?? if ((ps.get(ind) & 1) != 0) this.degree--; ps.delete(ind); } } // On copie les litt?raux de la contrainte lits = new int[ps.size()]; ps.moveTo(lits); // On normalise la contrainte au sens de Barth normalize(); } /** * Constructs and normalizes a cardinality constraint. used by * MinWatchCardPB.normalizedMinWatchCardNew() in the normalized case. <br /> * <strong>Should not be used if parameters are not already normalized</strong><br /> * This constraint is always an ATLEAST constraint. * * @param voc * vocabulary used by the constraint * @param ps * literals involved in the constraint * @param degree * degree of the constraint */ protected MinWatchCard(ILits voc, IVecInt ps, int degree) { // On met en place les valeurs this.voc = voc; this.degree = degree; this.moreThan = ATLEAST; // On copie les litt?raux de la contrainte lits = new int[ps.size()]; ps.moveTo(lits); } /** * computes the reason for a literal * * @param p * falsified literal (or Lit.UNDEFINED) * @param outReason * the reason to be computed. Vector of literals. * @see Constr#calcReason(int p, IVecInt outReason) */ public void calcReason(int p, IVecInt outReason) { // TODO calcReason: v?rifier par rapport ? l'article // Pour chaque litt?ral for (int i = 0; i < lits.length; i++) { // Si il est falsifi? if (voc.isFalsified(lits[i])) { // On ajoute sa n?gation au vecteur outReason.push(lits[i] ^ 1); } } } /** * Returns the activity of the constraint * * @return activity value of the constraint * @see Constr#getActivity() */ public double getActivity() { // TODO getActivity return 0; } /** * Increments activity of the constraint * * @param claInc * value to be added to the activity of the constraint * @see Constr#incActivity(double claInc) */ public void incActivity(double claInc) { // TODO incActivity } /** * Returns wether the constraint is learnt or not. * * @return false : a MinWatchCard cannot be learnt. * @see Constr#learnt() */ public boolean learnt() { return false; } /** * Simplifies the constraint w.r.t. the assignments of the literals * * @param voc * vocabulary used * @param ps * literals involved * @return value to be added to the degree. This value is less than or equal * to 0. */ protected static int linearisation(ILits voc, IVecInt ps) { // Stockage de l'influence des modifications int modif = 0; for (int i = 0; i < ps.size();) { // on verifie si le litteral est affecte if (voc.isUnassigned(ps.get(i))) { i++; } else { // Si le litteral est satisfait, // ?a revient ? baisser le degr? if (voc.isSatisfied(ps.get(i))) { modif--; } // dans tous les cas, s'il est assign?, // on enleve le ieme litteral ps.set(i, ps.last()); ps.pop(); } } // DLB: inutile? // ps.shrinkTo(nbElement); assert modif <= 0; return modif; } /** * Returns if the constraint is the reason for a unit propagation. * * @return true * @see Constr#locked() */ public boolean locked() { // TODO locked return true; } /** * Constructs a cardinality constraint with a minimal set of watched * literals Permet la cr?ation de contrainte de cardinalit? ? observation * minimale * * @param s * tool for propagation * @param voc * vocalulary used by the constraint * @param ps * literals involved in the constraint * @param moreThan * sign of the constraint. Should be ATLEAST or ATMOST. * @param degree * degree of the constraint * @return a new cardinality constraint, null if it is a tautology * @throws ContradictionException */ public static MinWatchCard minWatchCardNew(UnitPropagationListener s, ILits voc, IVecInt ps, boolean moreThan, int degree) throws ContradictionException { int mydegree = degree + linearisation(voc, ps); if (ps.size() == 0 && mydegree > 0) { throw new ContradictionException(); } else if (ps.size() == mydegree || ps.size() <= 0) { for (int i = 0; i < ps.size(); i++) if (!s.enqueue(ps.get(i))) { throw new ContradictionException(); } return null; } // La contrainte est maintenant cr??e MinWatchCard retour = new MinWatchCard(voc, ps, moreThan, mydegree); if (retour.degree <= 0) return null; retour.computeWatches(); retour.computePropagation(s); return retour; } /** * normalize the constraint (cf. P.Barth normalization) */ public final void normalize() { // Gestion du signe if (!moreThan) { // On multiplie le degr? par -1 this.degree = 0 - this.degree; // On r?vise chaque litt?ral for (int indLit = 0; indLit < lits.length; indLit++) { lits[indLit] = lits[indLit] ^ 1; this.degree++; } this.moreThan = true; } } /** * propagates the value of a falsified literal * * @param s * tool for literal propagation * @param p * falsified literal * @return false if an inconistency is detected, else true */ public boolean propagate(UnitPropagationListener s, int p) { // Si la contrainte est responsable de propagation unitaire if (watchCumul == degree) { voc.attach(p, this); return false; } // Recherche du litt?ral falsifi? int indFalsified = 0; while ((lits[indFalsified] ^ 1) != p) indFalsified++; assert watchCumul > degree; // Recherche du litt?ral swap int indSwap = degree + 1; while (indSwap < lits.length && voc.isFalsified(lits[indSwap])) indSwap++; // Mise ? jour de la contrainte if (indSwap == lits.length) { // Si aucun litt?ral n'a ?t? trouv? voc.attach(p, this); // La limite est atteinte watchCumul--; assert watchCumul == degree; voc.undos(p).push(this); // On met en queue les litt?raux impliqu?s for (int i = 0; i <= degree; i++) if ((p != (lits[i] ^ 1)) && !s.enqueue(lits[i], this)) return false; return true; } // Si un litt?ral a ?t? trouv? on les ?change int tmpInt = lits[indSwap]; lits[indSwap] = lits[indFalsified]; lits[indFalsified] = tmpInt; // On observe le nouveau litt?ral voc.attach(tmpInt ^ 1, this); return true; } /** * Removes a constraint from the solver */ public void remove() { for (int i = 0; i <= degree; i++) { voc.attaches(lits[i] ^ 1).remove(this); } } /** * Rescales the activity value of the constraint * * @param d * rescale factor */ public void rescaleBy(double d) { // TODO rescaleBy } /** * simplifies the constraint * * @return true if the constraint is satisfied, else false */ public boolean simplify() { // Calcul de la valeur actuelle for (int i = 0, count = 0; i < lits.length; i++) if (voc.isSatisfied(lits[i]) && (++count == degree)) return true; return false; } /** * Returns a string representation of the constraint. * * @return representation of the constraint. */ @Override public String toString() { StringBuffer stb = new StringBuffer(); stb.append("Card (" + lits.length + ") : "); if (lits.length > 0) { // if (voc.isUnassigned(lits[0])) { stb.append(Lits.toString(this.lits[0])); stb.append("["); stb.append(voc.valueToString(lits[0])); stb.append("@"); stb.append(voc.getLevel(lits[0])); stb.append("]"); stb.append(" "); //$NON-NLS-1$ // } for (int i = 1; i < lits.length; i++) { // if (voc.isUnassigned(lits[i])) { stb.append(" + "); //$NON-NLS-1$ stb.append(Lits.toString(this.lits[i])); stb.append("["); stb.append(voc.valueToString(lits[i])); stb.append("@"); stb.append(voc.getLevel(lits[i])); stb.append("]"); stb.append(" "); //$NON-NLS-1$ // } } stb.append(">= "); //$NON-NLS-1$ stb.append(this.degree); } return stb.toString(); } /** * Updates information on the constraint in case of a backtrack * * @param p * unassigned literal */ public void undo(int p) { // Le litt?ral observ? et falsifi? devient non assign? watchCumul++; } public void setLearnt() { throw new UnsupportedOperationException(); } public void register() { throw new UnsupportedOperationException(); } public int size() { return lits.length; } public int get(int i) { return lits[i]; } public void assertConstraint(UnitPropagationListener s) { throw new UnsupportedOperationException(); } protected void computeWatches() { int indSwap = lits.length; int tmpInt; for (int i = 0; i <= degree && i < indSwap; i++) { while (voc.isFalsified(lits[i]) && --indSwap > i) { tmpInt = lits[i]; lits[i] = lits[indSwap]; lits[indSwap] = tmpInt; } // Si le litteral est observable if (!voc.isFalsified(lits[i])) { watchCumul++; voc.attach(lits[i] ^ 1, this); } } if (learnt()) { // chercher tous les litteraux a regarder // par ordre de niveau decroissant int free = 1; while ((watchCumul <= degree) && (free > 0)) { free = 0; // regarder le litteral falsifie au plus bas niveau int maxlevel = -1, maxi = -1; for (int i = watchCumul; i < lits.length; i++) { if (voc.isFalsified(lits[i])) { free++; int level = voc.getLevel(lits[i]); if (level > maxlevel) { maxi = i; maxlevel = level; } } } if (free > 0) { assert maxi >= 0; voc.attach(lits[maxi] ^ 1, this); tmpInt = lits[maxi]; lits[maxi] = lits[watchCumul]; lits[watchCumul] = tmpInt; watchCumul++; free--; assert free >= 0; } } assert lits.length == 1 || watchCumul > 1; } } protected MinWatchCard computePropagation(UnitPropagationListener s) throws ContradictionException { // Si on a des litteraux impliques if (watchCumul == degree) { for (int i = 0; i < lits.length; i++) if (!s.enqueue(lits[i])) { throw new ContradictionException(); } return null; } // Si on n'observe pas suffisamment if (watchCumul < degree) { throw new ContradictionException(); } return this; } public int[] getLits() { int[] tmp = new int[size()]; System.arraycopy(lits, 0, tmp, 0, size()); return tmp; } public ILits getVocabulary() { return voc; } @Override public boolean equals(Object card){ if (card==null) { return false; } try { MinWatchCard mcard = (MinWatchCard) card; if (mcard.degree != degree) return false; if (lits.length != mcard.lits.length) return false; boolean ok; for (int lit : lits){ ok = false; for(int lit2 : mcard.lits) if (lit==lit2){ ok = true; break; } if (!ok) return false; } return true; } catch (ClassCastException e) { return false; } } @Override public int hashCode() { long sum = 0; for (int p : lits) { sum += p; } sum += degree; return (int) sum / (lits.length+1); } }