def test_passing_list_of_non_ints_assumptions(self): """Test an error is raised when non-ints are passed in assumptions.""" with self.assertRaises(InvalidArgumentTypeError): sat_one([[1, -2], [-1]], assumptions=[1, 'string']) with self.assertRaises(InvalidArgumentTypeError): sat_all([[1, -2], [-1]], assumptions=[1, 'string'])
def test_passing_list_containing_zeroes_assumptions(self): """Test an error is rasied when zeroes are passed in assumptions.""" with self.assertRaises(InvalidArgumentValueError): sat_one([[1, -2], [-1]], assumptions=[1, 2, 3, 0]) with self.assertRaises(InvalidArgumentValueError): sat_all([[1, -2], [-1]], assumptions=[1, 2, 3, 0])
def test_passing_non_int_inner_list_clauses(self): """Test an error is raised when passing a non-int in clauses.""" with self.assertRaises(InvalidArgumentTypeError): sat_one([[1, 2, 3], [-2], ['string']]) with self.assertRaises(InvalidArgumentTypeError): sat_all([[1, 2, 3], [-2], ['string']])
def test_passing_zeroes_inner_list_clauses(self): """Test an error is raised when zeroes are passed in clauses.""" with self.assertRaises(InvalidArgumentValueError): sat_one([[1, 0, 3], [2], [3]]) with self.assertRaises(InvalidArgumentValueError): sat_all([[1, 0, 3], [2], [3]])
def test_passing_empty_inner_list_clauses(self): """Test an error is raised when passing an empty list of clauses.""" with self.assertRaises(InvalidArgumentValueError): sat_one([[1], [], [2, 3]]) with self.assertRaises(InvalidArgumentValueError): sat_all([[1], [], [2, 3]])
def test_passing_empty_list_assumptions(self): """Test passing an empty list as assumptions causes an error.""" with self.assertRaises(InvalidArgumentValueError): sat_one([[1, 2]], assumptions=[]) with self.assertRaises(InvalidArgumentValueError): sat_all([[1, 2]], assumptions=[])
def test_passing_empty_list_clauses(self): """Test passing an empty list as clauses causes an error.""" with self.assertRaises(InvalidArgumentValueError): sat_one([]) with self.assertRaises(InvalidArgumentValueError): sat_all([])
def test_passing_non_list_assumptions(self): """Test passing an invalid non-list as the assumptions argument.""" with self.assertRaises(InvalidArgumentTypeError): sat_one([[1, 2, 3]], assumptions=float()) with self.assertRaises(InvalidArgumentTypeError): sat_all([[1, 2, 3]], assumptions=float())
def test_passing_non_list_clauses(self): """Test passing a non-list as the clauses argument.""" with self.assertRaises(InvalidArgumentTypeError): sat_one(None) with self.assertRaises(InvalidArgumentTypeError): sat_all(None)
def test_sat_all_without_assumptions_satisfiable_one_sol(self): """Test sat_all w/o assumptions that produces a single solution.""" count = 0 expected = [-1, -2, 3, 4] for solution in sat_all([[1, 2, 3], [-1], [-2], [-3, 4]]): self.assertEqual(expected, solution) count += 1 self.assertEqual(1, count)
def test_sat_all_with_assumptions_satisfiable_multiple_sols(self): """Test sat_all w/ assumptions producing multiple solutions.""" count = 0 expected = [[1, -2, 3, -4, 5], [1, -2, 3, -4, -5]] for solution in sat_all([[1, 2, 3, 4, 5], [-1, -2], [-3, -4]], assumptions=[1, 3]): self.assertIn(solution, expected) count += 1 self.assertEqual(2, count)
def test_sat_all_with_assumptions_satisfiable_one_sol(self): """Test sat_all w/ assumptions producing a single solution.""" count = 0 expected = [-1, 2, -3, -4, 5] for solution in sat_all([[1, 2], [2, 3], [3, 4, 5]], assumptions=[-1, -3, -4]): self.assertEqual(expected, solution) count += 1 self.assertEqual(1, count)
def test_sat_all_without_assumptions_satisfiable_multiple_sols(self): """Test sat_all w/o assumptions producing multiple solutions.""" count = 0 expected = [[-1, -2, 3, -4], [-1, 2, -3, -4], [1, 2, -3, -4], [1, -2, -3, -4]] for solution in sat_all([[1, 2, 3, 4], [-2, -3], [-4], [-1, -3]]): self.assertIn(solution, expected) count += 1 self.assertEqual(4, count)
def sat_all(self): """Find all combinations of inputs that satisfy this expression. Under the hood, this method is using the functionality exposed in tt's :mod:`satisfiability.picosat <tt.satisfiability.picosat>` module. Here's a simple example of iterating through a few SAT solutions:: >>> from tt import BooleanExpression >>> b = BooleanExpression('(A xor B) and (C xor D)') >>> for solution in b.sat_all(): ... print(solution) ... A=1, B=0, C=1, D=0 A=1, B=0, C=0, D=1 A=0, B=1, C=0, D=1 A=0, B=1, C=1, D=0 We can also constrain away a few of those solutions:: >>> with b.constrain(A=1, C=0): ... for solution in b.sat_all(): ... print(solution) ... A=1, B=0, C=0, D=1 :returns: An iterator of :func:`namedtuple <python:collections.namedtuple>`-like objects representing satisfying combinations of inputs; if no satisfying solutions exist, the iterator will be empty. :rtype: Iterator[:func:`namedtuple <python:collections.namedtuple>` -like objects] :raises NoEvaluationVariationError: If this is an expression of only constants. """ if not self._symbols: raise NoEvaluationVariationError( 'Cannot attempt to satisfy an expression of only constants') if not (self._symbol_set - self._constrained_symbol_set): # shortcut if all symbols are constrained if self.evaluate_unchecked(**self._constraints): yield self._symbol_vals_factory(**self._constraints) else: # empty iterator while False: yield None return clauses, assumptions, symbol_to_index_map, index_to_symbol_map = \ self._to_picosat_clauses_assumptions_and_symbol_mappings() if not assumptions: # cannot pass empty list of assumptions to picosat assumptions = None for picosat_sol in picosat.sat_all(clauses, assumptions=assumptions): result_dict = self._picosat_result_as_dict(picosat_sol, symbol_to_index_map, index_to_symbol_map) yield self._symbol_vals_factory(**result_dict)
def test_sat_all_with_assumptions_not_satisfiable(self): """Test sat_all w/ assumptions that produces no solutions.""" count = 0 for _ in sat_all([[1, 2, 3], [2, 3], [-1]], assumptions=[-2, -3]): count += 1 self.assertEqual(0, count)
def test_sat_all_without_assumptions_not_satisfiable(self): """Test sat_all w/o assumptions that produces no solutions.""" count = 0 for _ in sat_all([[1, 2], [-1, -2], [1], [2]]): count += 1 self.assertEqual(0, count)