/* * Kodkod -- Copyright (c) 2005-present, 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.config; import kodkod.engine.satlab.SATFactory; import kodkod.util.ints.IntRange; import kodkod.util.ints.Ints; /** * Stores information about various * user-level translation and analysis options. It can be * used to choose the SAT solver, control symmetry breaking, etc. * * @specfield solver: SATFactory // SAT solver factory to use * @specfield reporter: Reporter // reporter to use * @specfield symmetryBreaking: int // the amount of symmetry breaking to perform * @specfield sharing: int // the depth to which circuits should be checked for equivalence during translation * @specfield intEncoding: IntEncoding // encoding to use for translating int expressions * @specfield bitwidth: int // the bitwidth to use for integer representation / arithmetic * @specfield skolemDepth: int // skolemization depth * @specfield logTranslation: [0..2] // log translation events, default is 0 (no logging) * @specfield coreGranularity: [0..3] // unsat core granularity, default is 0 (only top-level conjuncts are considered) * @author Emina Torlak */ public final class Options implements Cloneable { private Reporter reporter = new AbstractReporter(){}; private SATFactory solver = SATFactory.DefaultSAT4J; private int symmetryBreaking = 20; private IntEncoding intEncoding = IntEncoding.TWOSCOMPLEMENT; private int bitwidth = 4; private int sharing = 3; private int skolemDepth = 0; private int logTranslation = 0; private int coreGranularity = 0; /** * Constructs an Options object initialized with default values. * @ensures this.solver' = SATFactory.DefaultSAT4J * this.reporter' is silent (no messages reported) * this.symmetryBreaking' = 20 * this.sharing' = 3 * this.intEncoding' = BINARY * this.bitwidth' = 4 * this.skolemDepth' = 0 * this.logTranslation' = 0 * this.coreGranularity' = 0 */ public Options() {} /** * Returns the value of the solver options. * The default is SATSolver.DefaultSAT4J. * @return this.solver */ public SATFactory solver() { return solver; } /** * Sets the solver option to the given value. * @ensures this.solver' = solver * @throws NullPointerException solver = null */ public void setSolver(SATFactory solver) { if (solver==null) throw new NullPointerException(); this.solver = solver; } /** * Returns this.reporter. * @return this.reporter */ public Reporter reporter() { return reporter; } /** * Sets this.reporter to the given reporter. * @requires reporter != null * @ensures this.reporter' = reporter * @throws NullPointerException reporter = null */ public void setReporter(Reporter reporter) { if (reporter==null) throw new NullPointerException(); this.reporter = reporter; } /** * @throws IllegalArgumentException arg !in [min..max] */ private void checkRange(int arg, int min, int max) { if (arg < min || arg > max) throw new IllegalArgumentException(arg + " !in [" + min + ".." + max + "]"); } /** * Returns the integer encoding that will be used for translating {@link kodkod.ast.IntExpression int nodes}. * The default is BINARY representation, which allows negative numbers. UNARY representation is best suited to * problems with small scopes, in which cardinalities are only compared (and possibly added to each other or * non-negative numbers). * @return this.intEncoding */ public IntEncoding intEncoding() { return intEncoding; } /** * Sets the intEncoding option to the given value. * @ensures this.intEncoding' = encoding * @throws NullPointerException encoding = null * @throws IllegalArgumentException this.bitwidth is not a valid bitwidth for the specified encoding */ public void setIntEncoding(IntEncoding encoding) { if (encoding.maxAllowedBitwidth()<bitwidth) throw new IllegalArgumentException(); this.intEncoding = encoding; } /** * Returns the size of the integer representation. For example, if this.intEncoding is * BINARY and this.bitwidth = 5 (the default), then all operations will yield * one of the five-bit numbers in the range [-16..15]. If this.intEncoding is UNARY and * this.bitwidth = 5, then all operations will yield one of the numbers in the * range [0..5]. * @return this.bitwidth */ public int bitwidth() { return bitwidth; } /** * Sets this.bitwidth to the given value. * @ensures this.bitwidth' = bitwidth * @throws IllegalArgumentException bitwidth < 1 * @throws IllegalArgumentException this.intEncoding==BINARY && bitwidth > 32 */ public void setBitwidth(int bitwidth) { checkRange(bitwidth, 1, intEncoding.maxAllowedBitwidth()); this.bitwidth = bitwidth; } /** * Returns the range of integers that can be encoded * using this.intEncoding and this.bitwidth. * @return range of integers that can be encoded * using this.intEncoding and this.bitwidth. */ public IntRange integers() { return intEncoding.range(bitwidth); } /** * Returns the 'amount' of symmetry breaking to perform. * If a non-symmetric solver is chosen for this.solver, * this value controls the maximum length of the generated * lex-leader symmetry breaking predicate. In general, * the higher this value, the more symmetries will be broken. But * setting the value too high may have the opposite effect * and slow down the solving. The default * value for this property is 20. * @return this.symmetryBreaking */ public int symmetryBreaking() { return symmetryBreaking; } /** * Sets the symmetryBreaking option to the given value. * @ensures this.symmetryBreaking' = symmetryBreaking * @throws IllegalArgumentException symmetryBreaking !in [0..Integer.MAX_VALUE] */ public void setSymmetryBreaking(int symmetryBreaking) { checkRange(symmetryBreaking, 0, Integer.MAX_VALUE); this.symmetryBreaking = symmetryBreaking; } /** * Returns the depth to which circuits are checked for equivalence during translation. * The default depth is 3, and the minimum allowed depth is 1. Increasing the sharing * may result in a smaller CNF, but at the cost of slower translation times. * @return this.sharing */ public int sharing() { return sharing; } /** * Sets the sharing option to the given value. * @ensures this.sharing' = sharing * @throws IllegalArgumentException sharing !in [1..Integer.MAX_VALUE] */ public void setSharing(int sharing) { checkRange(sharing, 1, Integer.MAX_VALUE); this.sharing = sharing; } /** * Returns the depth to which existential quantifiers are skolemized. * A negative depth means that no skolemization is performed. * The default depth of 0 means that only existentials that are not nested * within a universal quantifiers are skolemized. A depth of 1 means that * existentials nested within a single universal are also skolemized, etc. * @return this.skolemDepth */ public int skolemDepth() { return skolemDepth; } /** * Sets the skolemDepth to the given value. * @ensures this.skolemDepth' = skolemDepth */ public void setSkolemDepth(int skolemDepth) { this.skolemDepth = skolemDepth; } /** * Returns the translation logging level (0, 1, or 2), where 0 * means logging is not performed, 1 means only the translations of * top level formulas are logged, and 2 means all formula translations * are logged. This is necessary for determining which formulas occur in the unsat core of an * unsatisfiable formula. Logging is off by default, since * it incurs a non-trivial time overhead. * @return this.logTranslation */ public int logTranslation() { return logTranslation; } /** * Sets the translation logging level. * @requires logTranslation in [0..2] * @ensures this.logTranslation' = logTranslation * @throws IllegalArgumentException logTranslation !in [0..2] */ public void setLogTranslation(int logTranslation) { checkRange(logTranslation, 0, 2); this.logTranslation = logTranslation; } /** * Returns the core granularity level. The default is 0, which means that top-level * conjuncts of the input formula are used as "roots" for the purposes of core minimization and extraction. Granularity * of 1 means that the top-level conjuncts of the input formula's negation normal form (NNF) are * used as roots; granularity of 2 means that the top-level conjuncts of the formula's skolemized * NNF (SNNF) are used as roots; and, finally, a granularity of 3 means that the universal quantifiers of the formula's * SNNF are broken up and that the resulting top-level conjuncts are then used as roots for core minimization and extraction. * * <p>Note that the finer granularity (that is, a larger value of this.coreGranularity) will provide better information at * the cost of slower core extraction and, in particular, minimization.</p> * * @return this.coreGranularity */ public int coreGranularity() { return coreGranularity; } /** * Sets the core granularity level. * @requires coreGranularity in [0..3] * @ensures this.coreGranularity' = coreGranularity * @throws IllegalArgumentException coreGranularity !in [0..3] */ public void setCoreGranularity(int coreGranularity) { checkRange(coreGranularity, 0, 3); this.coreGranularity = coreGranularity; } /** * Returns a shallow copy of this Options object. In particular, * the returned options shares the same {@linkplain #reporter()} * and {@linkplain #solver()} factory objects as this Options. * @return a shallow copy of this Options object. */ public Options clone() { final Options c = new Options(); c.setSolver(solver); c.setReporter(reporter); c.setBitwidth(bitwidth); c.setIntEncoding(intEncoding); c.setSharing(sharing); c.setSharing(sharing); c.setSymmetryBreaking(symmetryBreaking); c.setSkolemDepth(skolemDepth); c.setLogTranslation(logTranslation); c.setCoreGranularity(coreGranularity); return c; } /** * Returns a string representation of this Options object. * @return a string representation of this Options object. */ public String toString() { StringBuilder b = new StringBuilder(); b.append("Options:"); b.append("\n solver: "); b.append(solver); b.append("\n reporter: "); b.append(reporter); b.append("\n intEncoding: "); b.append(intEncoding); b.append("\n bitwidth: "); b.append(bitwidth); b.append("\n sharing: "); b.append(sharing); b.append("\n symmetryBreaking: "); b.append(symmetryBreaking); b.append("\n skolemDepth: "); b.append(skolemDepth); b.append("\n logTranslation: "); b.append(logTranslation); b.append("\n coreGranularity: "); b.append(coreGranularity); return b.toString(); } /** * Integer encoding options for the translation of * {@link kodkod.ast.IntExpression int expressions}. */ public static enum IntEncoding { /** * Two's-complement encoding of integers supports * comparisons, addition, subtraction, multiplication, * division, and all low-level bit operations * (shifting, and, or, not, etc.). Maximum allowed * bitwidth for this encoding is 32 bits. */ TWOSCOMPLEMENT { @Override int maxAllowedBitwidth() { return 32; } @Override IntRange range(int bitwidth) { final int shift = bitwidth-1; return Ints.range(-1<<shift, (1<<shift)-1); } }; /** * Returns the maximum bitwidth allowed by this encoding. * @return maximum bitwidth allowed by this encoding. */ abstract int maxAllowedBitwidth(); /** * Returns the range of integers representable with * this encoding using the given number of bits. * @requires bitwidth > 0 * @return range of integers representable with * this encoding using the given number of bits. */ abstract IntRange range(int bitwidth) ; } }