def test_long(self) : for l in range(10,30) : solver = Solver() toadd = [] toassume = [] solution_expected = [None] for i in range(1,l) : toadd.append(i) solution_expected.append(False) if i != l-1 : toassume.append(i*-1) solver.add_xor_clause(toadd, False) res, solution = solver.solve(toassume) self.assertEqual(res, True) self.assertEqual(solution, tuple(solution_expected))
def is_system_uniquely_satisfiable(self, system, n): """ Tests unique satisfiable by banning all zero solution :param system: :param n: :return: """ if not system: return False # Prep solver solver = Solver() for clause in system: solver.add_xor_clause(clause, False) # Ban all zero solver.add_clause(range(1, n + 1)) sat, sol = solver.solve() # print "Found system is {0}".format(sat) return not sat
class TestXor(unittest.TestCase) : def setUp(self) : self.solver = Solver(threads = 2); def test_wrong_args(self) : self.assertRaises(TypeError, self.solver.add_xor_clause, [1, 2]) self.assertRaises(ValueError, self.solver.add_xor_clause, [1, 0], True) self.assertRaises(ValueError, self.solver.add_xor_clause, [-1, 2], True) def test_binary(self) : self.solver.add_xor_clause([1,2], False) res, solution = self.solver.solve([1]) self.assertEqual(res, True) self.assertEqual(solution, (None, True, True)) def test_unit(self) : self.solver.add_xor_clause([1], False) res, solution = self.solver.solve() self.assertEqual(res, True) self.assertEqual(solution, (None, False)) def test_unit2(self) : self.solver.add_xor_clause([1], True) res, solution = self.solver.solve() self.assertEqual(res, True) self.assertEqual(solution, (None, True)) def test_3_long(self) : self.solver.add_xor_clause([1, 2, 3], False) res, solution = self.solver.solve([1, 2]) self.assertEqual(res, True) #self.assertEqual(solution, (None, True, True, False)) def test_3_long2(self) : self.solver.add_xor_clause([1, 2, 3], True) res, solution = self.solver.solve([1, -2]) self.assertEqual(res, True) self.assertEqual(solution, (None, True, False, False)) def test_long(self) : for l in range(10,30) : self.setUp() toadd = [] toassume = [] solution_expected = [None] for i in range(1,l) : toadd.append(i) solution_expected.append(False) if i != l-1 : toassume.append(i*-1) self.solver.add_xor_clause(toadd, False) res, solution = self.solver.solve(toassume) self.assertEqual(res, True) self.assertEqual(solution, tuple(solution_expected))
class TestXor(unittest.TestCase): def setUp(self): self.solver = Solver(threads=2) def test_wrong_args(self): self.assertRaises(TypeError, self.solver.add_xor_clause, [1, 2]) self.assertRaises(ValueError, self.solver.add_xor_clause, [1, 0], True) self.assertRaises(ValueError, self.solver.add_xor_clause, [-1, 2], True) def test_binary(self): self.solver.add_xor_clause([1, 2], False) res, solution = self.solver.solve([1]) self.assertEqual(res, True) self.assertEqual(solution, (None, True, True)) def test_unit(self): self.solver.add_xor_clause([1], False) res, solution = self.solver.solve() self.assertEqual(res, True) self.assertEqual(solution, (None, False)) def test_unit2(self): self.solver.add_xor_clause([1], True) res, solution = self.solver.solve() self.assertEqual(res, True) self.assertEqual(solution, (None, True)) def test_3_long(self): self.solver.add_xor_clause([1, 2, 3], False) res, solution = self.solver.solve([1, 2]) self.assertEqual(res, True) # self.assertEqual(solution, (None, True, True, False)) def test_3_long2(self): self.solver.add_xor_clause([1, 2, 3], True) res, solution = self.solver.solve([1, -2]) self.assertEqual(res, True) self.assertEqual(solution, (None, True, False, False)) def test_long(self): for l in range(10, 30): self.setUp() toadd = [] toassume = [] solution_expected = [None] for i in range(1, l): toadd.append(i) solution_expected.append(False) if i != l - 1: toassume.append(i * -1) self.solver.add_xor_clause(toadd, False) res, solution = self.solver.solve(toassume) self.assertEqual(res, True) self.assertEqual(solution, tuple(solution_expected))
class ProgramSolver(): def __init__(self,filename): self.s = Solver(threads = 3) self.tt = 0 h2v = {} # hole 2 variable self.maximum_variable = -1 with open(filename,'r') as f: for l in f: if len(l) > len('c hole ') and l[:len('c hole ')] == 'c hole ': ms = re.findall(r'(\d+) \- (\d+)', l)[0] assert int(ms[0]) == int(ms[1]) ms = int(ms[0]) n = int(re.findall(r'H__\S+_(\S+)\s',l)[0]) h2v[n] = ms elif len(l) > 0 and not 'c' in l and not 'p' in l: vs = re.findall(r'(\-?\d+)',l) assert vs[-1] == '0' clause = [int(v) for v in vs[:-1] ] self.maximum_variable = max([self.maximum_variable] + [abs(v) for v in clause ]) self.s.add_clause(clause) print "Loaded",filename," with",len(h2v),"holes" # convert the tape index into a sat variable self.tape2variable = [ v for h,v in sorted(h2v.items()) ] # converts a sat variable to a tape index self.variable2tape = dict([ (v,h) for h,v in h2v.items() ]) def generate_variable(self): self.maximum_variable += 1 return self.maximum_variable def random_projection(self): self.s.add_xor_clause([v for v in self.variable2tape if random.random() > 0.5 ],random.random() > 0.5) def try_solving(self,assumptions = None): print "About to run solver == == == > " start_time = time.time() if assumptions != None: result = self.s.solve(assumptions) else: result = self.s.solve() dt = (time.time() - start_time) self.tt += dt print "Ran solver in time",dt if result[0]: bindings = {} for v in range(len(result[1])): if v in self.variable2tape: bindings[v] = result[1][v] print "Satisfiable." return bindings else: print "Unsatisfiable." return False def uniqueness_clause(self,tape): p,bit_mask = parse_tape(tape) clause = [] for j in range(len(tape)): if bit_mask[j] == 1: # jth tape position v = self.tape2variable[j] if tape[j] == 1: v = -v clause += [v] return clause def is_solution_unique(self,tape): d = self.generate_variable() clause = [d] + self.uniqueness_clause(tape) print "uniqueness clause",clause self.s.add_clause(clause) result = self.try_solving([-d]) self.s.add_clause([d]) # make the clause documents they satisfied if result: tp = self.holes2tape(result) print "alternative:",parse_tape(tp) print "alternative tape:",tp return False else: return True def holes2tape(self,result): return [ (1 if result[v] else 0) for v in self.tape2variable ] def try_sampling(self,subspace_dimension): for j in range(subspace_dimension): self.random_projection() result = self.try_solving() if result: print "Random projection satisfied" tp = self.holes2tape(result) print parse_tape(tp)[0] if self.is_solution_unique(tp): print "Unique. Accepted." else: print "Sample rejected" def adaptive_sample(self): subspace_dimension = 1 result = self.try_solving() if result: print "Formula satisfied" for j in range(subspace_dimension): self.random_projection() while True: print "\n\niterating:" result = self.try_solving() if result: print "Satisfied %d constraints" % subspace_dimension tp = self.holes2tape(result) print parse_tape(tp) print "tape = ",tp if self.is_solution_unique(tp): print "UNIQUE" print "<<< == == == >>>" self.random_projection() subspace_dimension += 1 else: print "Rejected %d projections" % subspace_dimension print "total time = ",self.tt break def enumerate_solutions(self): solutions = [] result = self.try_solving() d = self.generate_variable() logZ = float('-inf') while result: tp = self.holes2tape(result) program,mask = parse_tape(tp) solutions = solutions + [program] specified = sum(mask) logZ = lse(logZ, -specified * 0.693) print "Enumerated program", program, "with", specified, "specified bits." self.s.add_clause([d] + self.uniqueness_clause(tp)) result = self.try_solving([-d]) print "log(z) = ",logZ, "\t1/p = ", math.exp(-logZ) return solutions
class CryptoMiniSat(SatSolver): r""" CryptoMiniSat Solver. INPUT: - ``verbosity`` -- an integer between 0 and 15 (default: 0). Verbosity. - ``confl_limit`` -- an integer (default: ``None``). Abort after this many conflicts. If set to ``None``, never aborts. - ``threads`` -- an integer (default: None). The number of thread to use. If set to ``None``, the number of threads used corresponds to the number of cpus. EXAMPLES:: sage: from sage.sat.solvers.cryptominisat import CryptoMiniSat sage: solver = CryptoMiniSat() # optional - cryptominisat """ def __init__(self, verbosity=0, confl_limit=None, threads=None): r""" Constuct a new CryptoMiniSat instance. See the documentation class for the description of inputs. EXAMPLES:: sage: from sage.sat.solvers.cryptominisat import CryptoMiniSat sage: solver = CryptoMiniSat(threads=1) # optional - cryptominisat """ if threads is None: from sage.parallel.ncpus import ncpus threads = ncpus() if confl_limit is None: from sys import maxint confl_limit = maxint try: from pycryptosat import Solver except ImportError: from sage.misc.package import PackageNotFoundError raise PackageNotFoundError("cryptominisat") self._solver = Solver(verbose=int(verbosity), confl_limit=int(confl_limit), threads=int(threads)) self._nvars = 0 self._clauses = [] def var(self, decision=None): r""" Return a *new* variable. INPUT: - ``decision`` -- accepted for compatibility with other solvers, ignored. EXAMPLES:: sage: from sage.sat.solvers.cryptominisat import CryptoMiniSat sage: solver = CryptoMiniSat() # optional - cryptominisat sage: solver.var() # optional - cryptominisat 1 sage: solver.add_clause((-1,2,-4)) # optional - cryptominisat sage: solver.var() # optional - cryptominisat 5 """ return self._nvars + 1 def nvars(self): r""" Return the number of variables. Note that for compatibility with DIMACS convention, the number of variables corresponds to the maximal index of the variables used. EXAMPLES:: sage: from sage.sat.solvers.cryptominisat import CryptoMiniSat sage: solver = CryptoMiniSat() # optional - cryptominisat sage: solver.nvars() # optional - cryptominisat 0 If a variable with intermediate index is not used, it is still considered as a variable:: sage: solver.add_clause((1,-2,4)) # optional - cryptominisat sage: solver.nvars() # optional - cryptominisat 4 """ return self._nvars def add_clause(self, lits): r""" Add a new clause to set of clauses. INPUT: - ``lits`` -- a tuple of nonzero integers. .. note:: If any element ``e`` in ``lits`` has ``abs(e)`` greater than the number of variables generated so far, then new variables are created automatically. EXAMPLES:: sage: from sage.sat.solvers.cryptominisat import CryptoMiniSat sage: solver = CryptoMiniSat() # optional - cryptominisat sage: solver.add_clause((1, -2 , 3)) # optional - cryptominisat """ if 0 in lits: raise ValueError("0 should not appear in the clause: {}".format(lits)) # cryptominisat does not handle Sage integers lits = tuple(int(i) for i in lits) self._nvars = max(self._nvars, max(abs(i) for i in lits)) self._solver.add_clause(lits) self._clauses.append((lits, False, None)) def add_xor_clause(self, lits, rhs=True): r""" Add a new XOR clause to set of clauses. INPUT: - ``lits`` -- a tuple of positive integers. - ``rhs`` -- boolean (default: ``True``). Whether this XOR clause should be evaluated to ``True`` or ``False``. EXAMPLES:: sage: from sage.sat.solvers.cryptominisat import CryptoMiniSat sage: solver = CryptoMiniSat() # optional - cryptominisat sage: solver.add_xor_clause((1, 2 , 3), False) # optional - cryptominisat """ if 0 in lits: raise ValueError("0 should not appear in the clause: {}".format(lits)) # cryptominisat does not handle Sage integers lits = tuple(int(i) for i in lits) self._nvars = max(self._nvars, max(abs(i) for i in lits)) self._solver.add_xor_clause(lits, rhs) self._clauses.append((lits, True, rhs)) def __call__(self, assumptions=None): r""" Solve this instance. OUTPUT: - If this instance is SAT: A tuple of length ``nvars()+1`` where the ``i``-th entry holds an assignment for the ``i``-th variables (the ``0``-th entry is always ``None``). - If this instance is UNSAT: ``False``. EXAMPLES:: sage: from sage.sat.solvers.cryptominisat import CryptoMiniSat sage: solver = CryptoMiniSat() # optional - cryptominisat sage: solver.add_clause((1,2)) # optional - cryptominisat sage: solver.add_clause((-1,2)) # optional - cryptominisat sage: solver.add_clause((-1,-2)) # optional - cryptominisat sage: solver() # optional - cryptominisat (None, False, True) sage: solver.add_clause((1,-2)) # optional - cryptominisat sage: solver() # optional - cryptominisat False """ satisfiable, assignments = self._solver.solve() if satisfiable: return assignments else: return False def __repr__(self): r""" TESTS:: sage: from sage.sat.solvers.cryptominisat import CryptoMiniSat sage: solver = CryptoMiniSat() # optional - cryptominisat sage: solver # optional - cryptominisat CryptoMiniSat solver: 0 variables, 0 clauses. """ return "CryptoMiniSat solver: {} variables, {} clauses.".format(self.nvars(), len(self.clauses())) def clauses(self, filename=None): r""" Return original clauses. INPUT: - ``filename`` -- if not ``None`` clauses are written to ``filename`` in DIMACS format (default: ``None``) OUTPUT: If ``filename`` is ``None`` then a list of ``lits, is_xor, rhs`` tuples is returned, where ``lits`` is a tuple of literals, ``is_xor`` is always ``False`` and ``rhs`` is always ``None``. If ``filename`` points to a writable file, then the list of original clauses is written to that file in DIMACS format. EXAMPLES:: sage: from sage.sat.solvers import CryptoMiniSat sage: solver = CryptoMiniSat() # optional - cryptominisat sage: solver.add_clause((1,2,3,4,5,6,7,8,-9)) # optional - cryptominisat sage: solver.add_xor_clause((1,2,3,4,5,6,7,8,9), rhs=True) # optional - cryptominisat sage: solver.clauses() # optional - cryptominisat [((1, 2, 3, 4, 5, 6, 7, 8, -9), False, None), ((1, 2, 3, 4, 5, 6, 7, 8, 9), True, True)] DIMACS format output:: sage: from sage.sat.solvers import CryptoMiniSat sage: solver = CryptoMiniSat() # optional - cryptominisat sage: solver.add_clause((1, 2, 4)) # optional - cryptominisat sage: solver.add_clause((1, 2, -4)) # optional - cryptominisat sage: fn = tmp_filename() # optional - cryptominisat sage: solver.clauses(fn) # optional - cryptominisat sage: print(open(fn).read()) # optional - cryptominisat p cnf 4 2 1 2 4 0 1 2 -4 0 <BLANKLINE> Note that in cryptominisat, the DIMACS standard format is augmented with the following extension: having an ``x`` in front of a line makes that line an XOR clause:: sage: solver.add_xor_clause((1,2,3), rhs=True) # optional - cryptominisat sage: solver.clauses(fn) # optional - cryptominisat sage: print(open(fn).read()) # optional - cryptominisat p cnf 4 3 1 2 4 0 1 2 -4 0 x1 2 3 0 <BLANKLINE> Note that inverting an xor-clause is equivalent to inverting one of the variables:: sage: solver.add_xor_clause((1,2,5),rhs=False) # optional - cryptominisat sage: solver.clauses(fn) # optional - cryptominisat sage: print(open(fn).read()) # optional - cryptominisat p cnf 5 4 1 2 4 0 1 2 -4 0 x1 2 3 0 x1 2 -5 0 <BLANKLINE> """ if filename is None: return self._clauses else: from sage.sat.solvers.dimacs import DIMACS DIMACS.render_dimacs(self._clauses, filename, self.nvars())
class ProgramSolver(): def __init__(self, filename): self.s = Solver(threads=3) self.tt = 0 h2v = {} # hole 2 variable self.maximum_variable = -1 with open(filename, 'r') as f: for l in f: if len(l) > len('c hole ') and l[:len('c hole ')] == 'c hole ': ms = re.findall(r'(\d+) \- (\d+)', l)[0] assert int(ms[0]) == int(ms[1]) ms = int(ms[0]) n = int(re.findall(r'H__\S+_(\S+)\s', l)[0]) h2v[n] = ms elif len(l) > 0 and not 'c' in l and not 'p' in l: vs = re.findall(r'(\-?\d+)', l) assert vs[-1] == '0' clause = [int(v) for v in vs[:-1]] self.maximum_variable = max([self.maximum_variable] + [abs(v) for v in clause]) self.s.add_clause(clause) print "Loaded", filename, " with", len(h2v), "holes" # convert the tape index into a sat variable self.tape2variable = [v for h, v in sorted(h2v.items())] # converts a sat variable to a tape index self.variable2tape = dict([(v, h) for h, v in h2v.items()]) def generate_variable(self): self.maximum_variable += 1 return self.maximum_variable def random_projection(self): self.s.add_xor_clause( [v for v in self.variable2tape if random.random() > 0.5], random.random() > 0.5) def try_solving(self, assumptions=None): print "About to run solver == == == > " start_time = time.time() if assumptions != None: result = self.s.solve(assumptions) else: result = self.s.solve() dt = (time.time() - start_time) self.tt += dt print "Ran solver in time", dt if result[0]: bindings = {} for v in range(len(result[1])): if v in self.variable2tape: bindings[v] = result[1][v] print "Satisfiable." return bindings else: print "Unsatisfiable." return False def uniqueness_clause(self, tape): p, bit_mask = parse_tape(tape) clause = [] for j in range(len(tape)): if bit_mask[j] == 1: # jth tape position v = self.tape2variable[j] if tape[j] == 1: v = -v clause += [v] return clause def is_solution_unique(self, tape): d = self.generate_variable() clause = [d] + self.uniqueness_clause(tape) print "uniqueness clause", clause self.s.add_clause(clause) result = self.try_solving([-d]) self.s.add_clause([d]) # make the clause documents they satisfied if result: tp = self.holes2tape(result) print "alternative:", parse_tape(tp) print "alternative tape:", tp return False else: return True def holes2tape(self, result): return [(1 if result[v] else 0) for v in self.tape2variable] def try_sampling(self, subspace_dimension): for j in range(subspace_dimension): self.random_projection() result = self.try_solving() if result: print "Random projection satisfied" tp = self.holes2tape(result) print parse_tape(tp)[0] if self.is_solution_unique(tp): print "Unique. Accepted." else: print "Sample rejected" def adaptive_sample(self): subspace_dimension = 1 result = self.try_solving() if result: print "Formula satisfied" for j in range(subspace_dimension): self.random_projection() while True: print "\n\niterating:" result = self.try_solving() if result: print "Satisfied %d constraints" % subspace_dimension tp = self.holes2tape(result) print parse_tape(tp) print "tape = ", tp if self.is_solution_unique(tp): print "UNIQUE" print "<<< == == == >>>" self.random_projection() subspace_dimension += 1 else: print "Rejected %d projections" % subspace_dimension print "total time = ", self.tt break def enumerate_solutions(self): solutions = [] result = self.try_solving() d = self.generate_variable() logZ = float('-inf') while result: tp = self.holes2tape(result) program, mask = parse_tape(tp) solutions = solutions + [program] specified = sum(mask) logZ = lse(logZ, -specified * 0.693) print "Enumerated program", program, "with", specified, "specified bits." self.s.add_clause([d] + self.uniqueness_clause(tp)) result = self.try_solving([-d]) print "log(z) = ", logZ, "\t1/p = ", math.exp(-logZ) return solutions
def solve_xor(model): s = Solver() for clause in model.clauses: s.add_xor_clause(_only_positive(clause), rhs=_get_clause_parity(clause)) return s.solve()
def test_binary(self) : solver = Solver() solver.add_xor_clause([1,2], False) res, solution = solver.solve([1]) self.assertEqual(res, True) self.assertEqual(solution, (None, True, True))
def test_3_long2(self) : solver = Solver() solver.add_xor_clause([1, 2, 3], True) res, solution = solver.solve([1, -2]) self.assertEqual(res, True) self.assertEqual(solution, (None, True, False, False))
def test_unit2(self) : solver = Solver() solver.add_xor_clause([1], True) res, solution = solver.solve() self.assertEqual(res, True) self.assertEqual(solution, (None, True))