/*
* Kodkod -- Copyright (c) 2005-2012, Emina Torlak
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package kodkod.engine.fol2sat;
import java.util.Map;
import java.util.Set;
import kodkod.ast.Relation;
import kodkod.engine.bool.BooleanConstant;
import kodkod.engine.config.Options;
import kodkod.engine.satlab.SATSolver;
import kodkod.instance.Bounds;
import kodkod.instance.Instance;
import kodkod.instance.TupleFactory;
import kodkod.instance.TupleSet;
import kodkod.util.ints.IndexedEntry;
import kodkod.util.ints.IntIterator;
import kodkod.util.ints.IntSet;
import kodkod.util.ints.Ints;
/**
* Stores the translation of a Kodkod problem to CNF. A problem consists of
* a {@linkplain kodkod.ast.Formula formula}, {@linkplain Bounds bounds} and
* {@linkplain Options}. A translation can be {@linkplain Whole basic} or
* {@linkplain Incremental incremental}.
*
* @specfield originalFormula: Formula // the original formula, as constructed by client
* @specfield originalBounds: Bounds // the original bounds, as constructed by client
* @specfield formula: Formula // optimization of this.originalFormula that was used for translation
* @specfield bounds: Bounds // optimization of this.originalBounds that was used for translation
* @specfield options: Options // the options object used to control translation
* @specfield vars: this.bounds.relations -> int // mapping from relations to variables that encode their contents
* @specfield solver: SATSolver // a SATSolver containing the CNF representation of the formula
*
* @invariant solver.solve() iff SAT(formula, bounds, options)
* @invariant SAT(formula, bounds, options) iff SAT(originalFormula, originalBounds, options)
* @invariant this.originalBounds.relations in this.bounds.relations
* @invariant this.originalBounds.ints().equals(this.bounds.ints())
* @invariant all r: this.bounds.relations | some vars[r] => #vars[r] = this.bounds.upperBound(r).size() - this.bounds.lowerBound(r).size()
* @invariant all r: this.bounds.relations, i: vars[r] | min(vars[r]) <= i <= max(vars[r])
* @invariant vars[Relation] in this.solver.variables
*
* @see Whole
* @see Incremental
*
* @author Emina Torlak
*/
public abstract class Translation {
private final Bounds bounds;
private final Options options;
/**
* Creates a translation using the given bounds and options.
* @ensures this.bounds' = bounds && this.options' = options
*/
Translation(Bounds bounds, Options options) {
this.bounds = bounds;
this.options = options;
}
/**
* Returns the optimized bounds. Modification of the returned object
* may cause violation of {@link Translation} invariants.
* @return this.bounds
*/
public final Bounds bounds() { return bounds; }
/**
* Returns the translation options. Modification of the returned object
* may cause violation of {@link Translation} invariants.
* @return this.options
*/
public final Options options() { return options; }
/**
* Returns the set of primary variables that represent
* the tuples in the given relation. If no variables were allocated
* to the given relation, empty set is returned. This set
* contains exactly {@code this.bounds.upperBound(r).size() - this.bounds.lowerBound(r).size()}
* variable identifiers.
* @return this.vars[relation]
*/
public abstract IntSet primaryVariables(Relation relation);
/**
* Returns the number of primary variables allocated
* during translation. Primary variables represent
* the tuples of relations in this.bounds that have different
* lower and upper bounds (i.e. {@code some this.bounds.upperBound[r].tuples - this.bounds.lowerBound[r].tuples}).
* @return #this.vars[Relation]
*/
public abstract int numPrimaryVariables() ;
/**
* Returns the SATSolver object containing the CNF encoding of this.formula. Satisfiability
* of the formula can be checked by calling {@link kodkod.engine.satlab.SATSolver#solve()}.
* Modification of the returned object may cause violation of {@link Translation} invariants.
* @return {s: SATSolver | SAT(s.variables, s.clauses) iff SAT(this.formula, this.bounds, this.options) }
*/
public abstract SATSolver cnf();
/**
* Returns true iff this translation is trivially true or trivially false. We
* consider a problem defined by {@code this.formula} and {@code this.bounds} to be
* trivially true or false if the {@linkplain Translator} simplifies it to a {@linkplain BooleanConstant}.
* The {@linkplain BooleanConstant#TRUE TRUE} value is represented as a {@linkplain #cnf() CNF}
* with no variables and no clauses. The {@linkplain BooleanConstant#FALSE FALSE} value
* is represented as a {@linkplain #cnf() CNF} with no variables and a single, empty
* clause. Note that in the case of a trivially satisfiable problem, the {@link #interpret()}
* method returns the minimal trivial instance for that problem, which consists of
* the lower bounds specified by {@code this.bounds}.
* @return this.cnf.numberOfVariables() = 0
* @see #interpret()
*/
public final boolean trivial() { return cnf().numberOfVariables()==0; }
/**
* If {@code this.solver.solve()} is true, returns an interpretation of the CNF solution as a
* mapping from Relations to sets of Tuples. The returned instance maps all relations in
* {@code this.bounds} and, therefore, all relations in {@code this.originalBounds}. The additional
* relations in {@code this.bounds}, if any, consist of generated skolem constants.
*
* <p>
* The returned {@code instance} assigns the value {@code instance.tuples(r)} to each relation {@code r} in
* {@code this.bounds.relations} as follows: (1) {@code instance.tuples(r)} includes all tuples in
* {@code this.bound.lowerBound(r)}, and (2) it includes the ith tuple from
* {@code this.bounds.upperBound(r) - this.bounds.lowerBound(r)} iff the model obtain from {@code this.solver}
* binds the ith variable in {@code this.{@linkplain #primaryVariables(Relation) primaryVariables}(r)} to true.
* In other words, if no primary variables were allocated to {@code r}, then the returned instance simply binds {@code r}
* to its lower bound. Note that in the case of a trivially satisfiable problem {@code (this.formula, this.bounds, this.options)},
* the returned {@code instance} is the smallest possible instance for that problem, consisting solely of
* the lower bounds specified by {@code this.bounds}.
* </p>
*
* @return a new instance of the problem {@code (this.formula, this.bounds, this.options)}, as described above
*
* @throws IllegalStateException this.solver.solve() has not been called or the outcome of the last call
* was not <code>true</code>.
*/
public final Instance interpret() {
final SATSolver solver = cnf();
final Instance instance = new Instance(bounds.universe());
final TupleFactory f = bounds.universe().factory();
for(IndexedEntry<TupleSet> entry : bounds.intBounds()) {
instance.add(entry.index(), entry.value());
}
for(Relation r : bounds.relations()) {
TupleSet lower = bounds.lowerBound(r);
IntSet indices = Ints.bestSet(lower.capacity());
indices.addAll(lower.indexView());
IntSet vars = primaryVariables(r);
if (!vars.isEmpty()) {
//System.out.println(r + ": [" + vars.min() + ", " + vars.max() + "]");
int lit = vars.min();
for(IntIterator iter = bounds.upperBound(r).indexView().iterator(); iter.hasNext();) {
final int index = iter.next();
if (!indices.contains(index) && solver.valueOf(lit++))
indices.add(index);
}
}
instance.add(r, f.setOf(r.arity(), indices));
}
return instance;
}
/**
* A {@linkplain Whole whole} translation stores the complete CNF of encoding
* of a given problem. Unlike an {@link Incremental incremental} translation,
* a whole translation cannot be augmented with additional constraints. The
* tradeoff is that a whole translation is cheaper to store; the encoding
* may be more efficient than if the same problem were translated incrementally;
* and, whole translations support logging and core extraction
*
* @specfield log: lone {@link TranslationLog} // a translation log mapping nodes in this.formula to their corresponding literals in this.solver
* @invariant some log => log.originalFormula = originalFormula && log.originalBounds = originalBounds &&
* log.formula = formula && log.bounds = bounds
* @invariant some log iff this.options.logTranslation > 0
* @invariant vars[this.bounds.relations] = { i: int | 1 <= i <= #vars }
*
* @see Translator#translate(kodkod.ast.Formula, Bounds, Options)
*
* @author Emina Torlak
*/
public static final class Whole extends Translation {
private final SATSolver solver;
private final Map<Relation, IntSet> primaryVarUsage;
private final TranslationLog log;
private final int maxPrimaryVar;
/**
* Creates a whole translation using the given bounds, options, solver, var map, and log.
*
* @requires primaryVarUsage.keySet() in { r: bounds.relations | bounds.lower[r] != bounds.upper[r] }
* @requires maxPrimaryVar = max(varUsage[Relation].max)
* @requires all i: varUsage.map[Relation].ints | 1 <= i <= maxPrimaryVar
* @requires varUsage.map[Relation].ints in solver.variables
* @ensures this.solver' = solver && this.bounds' = bounds &&
* this.options' = options && this.log' = log && this.vars' = varUsage
*/
Whole(Bounds bounds, Options options, SATSolver solver, Map<Relation, IntSet> varUsage, int maxPrimaryVar, TranslationLog log) {
super(bounds, options);
this.solver = solver;
this.log = log;
this.maxPrimaryVar = maxPrimaryVar;
this.primaryVarUsage = varUsage;
}
/**
* {@inheritDoc}
* @see kodkod.engine.fol2sat.Translation#cnf()
*/
public final SATSolver cnf() { return solver; }
/**
* {@inheritDoc}
* @see kodkod.engine.fol2sat.Translation#primaryVariables(kodkod.ast.Relation)
*/
@Override
public IntSet primaryVariables(Relation relation) {
final IntSet vars = primaryVarUsage.get(relation);
return vars==null ? Ints.EMPTY_SET : vars;
}
/**
* {@inheritDoc}
* @see kodkod.engine.fol2sat.Translation#numPrimaryVariables()
*/
@Override
public int numPrimaryVariables() { return maxPrimaryVar; }
/**
* If translation logging was enabled (by setting {@code this.options.logTranslation > 0}),
* returns the {@linkplain TranslationLog log} of {@linkplain TranslationRecord records}
* generated for this translation. Otherwise returns null.
* @return translation log for this translation, if one was generated, or null otherwise
*/
public TranslationLog log() { return log; }
}
/**
* An {@linkplain Incremental incremental} translation preserves the internal data structures used during
* translation. This enables the translation to be updated with CNF encodings of
* additional formulas and bounds using the
* {@linkplain Translator#translateIncremental(kodkod.ast.Formula, Bounds, Translation.Incremental)} method.
*
* <p>
* Incremental translations are more expensive to store than {@link Whole whole} translations, and they
* place some restrictions on the form of problems that are translated. In particular, logging
* must be disabled during translation; the translation must use an incremental SAT solver; and the
* addition of new clauses must not violate the invariants guaranteed by the {@link Translation} class.
* All three restrictions are enforced by the {@code translateIncremental(...)} methods of the
* {@link Translator} class.
* </p>
*
* @specfield symmetries: set IntSet // partition of the universe into equivalence classes induced this.originalBounds
*
* @invariant this.options.logTranslation = 0 && this.options.solver.incremental()
* @invariant this.symmetries = {@linkplain SymmetryDetector#partition(Bounds) partition}(this.originalBounds)
*
* @see Translator#translateIncremental(kodkod.ast.Formula, Bounds, Options)
* @see Translator#translateIncremental(kodkod.ast.Formula, Bounds, Translation.Incremental)
*
* @author Emina Torlak
*/
public static final class Incremental extends Translation {
/**
* @invariant this.interpreter.universe = this.bounds.universe &&
* this.interpreter.relations in this.bounds.relations &&
* this.interpreter.intBound = this.bounds.ibounds &&
* this.interpreter.lowerBound in this.bounds.lbounds &&
* this.interpreter.upperBound in this.bounds.ubounds &&
* this.interpreter.vars = this.vars.label
**/
private final LeafInterpreter interpreter;
/**
* @invariant this.incrementer.solver = this.solver
* @invariant this.incrementer.factory = this.interpreter.factory
*/
private final Bool2CNFTranslator incrementer;
private final Set<IntSet> symmetries;
/**
* Creates an Incremental translation using the given bounds, options, symmetries of the original bounds,
* translator and interpreter. This constructor assumes that the symmetries induced by {@code bounds} refine
* the {@code originalSymmetries}, which were obtained from the original problem bounds.
* @requires options.logTranslation = 0 && options.solver.incremental()
* @requires translator.solver was constructed by calling options.solver.instance()
* @requires all s : SymmetryDetector.partition(bounds) | some p : originalSymmetries | s.ints in p.ints
* @ensures this.bounds' = bounds && this.options' = options && this.symmetries' = originalSymmetries &&
* this.incrementer' = incrementer && this.interpreter' = interpreter
*/
Incremental(Bounds bounds, Options options, Set<IntSet> originalSymmetries, LeafInterpreter interpreter, Bool2CNFTranslator translator) {
super(bounds, options);
this.interpreter = interpreter;
this.incrementer = translator;
this.symmetries = originalSymmetries;
}
/**
* Returns the symmetries induced by the original bounds.
* @return this.symmetries
*/
Set<IntSet> symmetries() { return symmetries; }
/**
* Returns this.interpreter.
* @return this.interpreter
*/
LeafInterpreter interpreter() { return interpreter; }
/**
* Returns this.incrementer.
* @return this.incrementer.
*/
Bool2CNFTranslator incrementer() { return incrementer; }
/**
* {@inheritDoc}
* @see kodkod.engine.fol2sat.Translation#cnf()
*/
public final SATSolver cnf() { return incrementer.solver(); }
/**
* {@inheritDoc}
* @see kodkod.engine.fol2sat.Translation#primaryVariables(kodkod.ast.Relation)
*/
@Override
public IntSet primaryVariables(Relation relation) {
return interpreter.vars(relation);
}
/**
* {@inheritDoc}
* @see kodkod.engine.fol2sat.Translation#numPrimaryVariables()
*/
@Override
public int numPrimaryVariables() { return interpreter.factory().numberOfVariables(); }
}
}