def test_tseitin_rep_if(): from sweetpea.logic import __tseitin_rep, _Cache clauses = [] cache = _Cache(3) # Make sure return is correct and value was cached. assert __tseitin_rep(If(1, 2), clauses, cache) == 3 assert cache.get(str(If(1, 2))) == 3 # Make sure equivalence clauses were added. assert Or([Not(1), 2, Not(3)]) in clauses assert Or([1, 3]) in clauses assert Or([Not(2), 3]) in clauses # Don't duplicate clauses when the cache was already populated. clauses = [] cache = _Cache(3) # Prewarm the cache. assert cache.get(str(If(1, 2))) == 3 assert __tseitin_rep(If(1, 2), clauses, cache) == 3 # Make sure no clauses were added. assert clauses == []
def test_cnf_to_json(): assert cnf_to_json([And([1])]) == [[1]] assert cnf_to_json([And([Or([1])])]) == [[1]] assert cnf_to_json([And([Or([Not(5)])])]) == [[-5]] assert cnf_to_json([And([Or([1, 2, Not(4)])])]) == [[1, 2, -4]] assert cnf_to_json([And([Or([1, 4]), Or([5, -4, 2]), Or([-1, -5])])]) == [ [1, 4], [5, -4, 2], [-1, -5]] assert cnf_to_json([And([ Or([1, 3, Not(2)]), Or([1, 3, 5]), Or([1, 4, Not(2)]), Or([1, 4, 5]), Or([2, 3, 1, 5]), Or([2, 4, 1, 5])])]) == [ [1, 3, -2], [1, 3, 5], [1, 4, -2], [1, 4, 5], [2, 3, 1, 5], [2, 4, 1, 5]]
def test_tseitin_rep_or(): from sweetpea.logic import __tseitin_rep, _Cache clauses = [] cache = _Cache(4) # Make sure return is correct, and value was cached. assert __tseitin_rep(Or([1, 2, 3]), clauses, cache) == 4 assert cache.get(str(Or([1, 2, 3]))) == 4 # Make sure equivalence clauses were added. assert Or([Not(1), 4]) in clauses assert Or([Not(2), 4]) in clauses assert Or([Not(3), 4]) in clauses assert Or([1, 2, 3, Not(4)]) in clauses # Don't duplicate clauses when the cache was already populated clauses = [] cache = _Cache(4) # Prewarm the cache assert cache.get(str(Or([1, 2, 3]))) == 4 assert __tseitin_rep(Or([1, 2, 3]), clauses, cache) == 4 # Make sure no clauses were added assert clauses == []
def test_distribute_ors_switching(): from sweetpea.logic import __distribute_ors_switching # When lhs or rhs is a single variable, just distribute it. assert __distribute_ors_switching(Or([1, And([2, 3])]), 4) == (And([Or([1, 2]), Or([1, 3])]), 4) assert __distribute_ors_switching(Or([And([1, 2]), 3]), 4) == (And([Or([1, 3]), Or([2, 3])]), 4) # Should distribute over multiple individual variables assert __distribute_ors_switching(Or([1, 2, And([3, 4])]), 5) == (And( [Or([1, 2, 3]), Or([1, 2, 4])]), 5) # When both the lhs and rhs are more than just a single variable, # then introduce a switching variable to limit the formula growth. assert __distribute_ors_switching(Or([And([1, 2]), And([3, 4])]), 5) == (And([ Or([1, Not(5)]), Or([2, Not(5)]), Or([3, 5]), Or([4, 5]) ]), 6)
def apply_to_backend_request(self, block: Block, level: Tuple[Factor, Union[SimpleLevel, DerivedLevel]], backend_request: BackendRequest ) -> None: sublists = self._build_variable_sublists(block, level, self.k) implications = [] # Handle the regular cases (1 => 2 ^ ... ^ n ^ ~n+1) trim = len(sublists) if self.k > 1 else len(sublists) - 1 for idx, l in enumerate(sublists[:trim]): if idx > 0: p_list = [Not(sublists[idx-1][0]), l[0]] p = And(p_list) if len(p_list) > 1 else p_list[0] else: p = l[0] if idx < len(sublists) - 1: q_list = cast(List[Any], l[1:]) + [Not(sublists[idx+1][-1])] q = And(q_list) if len(q_list) > 1 else q_list[0] else: q = And(l[1:]) if len(l[1:]) > 1 else l[self.k - 1] implications.append(If(p, q)) # Handle the tail if len(sublists[-1]) > 1: tail = sublists[-1] tail.reverse() for idx in range(len(tail) - 1): implications.append(If(l[idx], l[idx + 1])) (cnf, new_fresh) = block.cnf_fn(And(implications), backend_request.fresh) backend_request.cnfs.append(cnf) backend_request.fresh = new_fresh
def test_eliminate_iff(): from sweetpea.logic import __eliminate_iff # P <-> Q ==> (P v ~Q) ^ (~P v Q) assert __eliminate_iff(Iff(1, 2)) == And([Or([1, Not(2)]), Or([Not(1), 2])]) assert __eliminate_iff(Iff(1, And([2, 3]))) == And([ Or([1, Not(And([2, 3]))]), Or([Not(1), And([2, 3])]) ])
def test_distribute_ors_naive(): from sweetpea.logic import __distribute_ors_naive # When f is int or Not, return it. (Not can only contain int, as we've # already applied DeMorgan's laws) assert __distribute_ors_naive(1) == 1 assert __distribute_ors_naive(Not(1)) == Not(1) # When f in an Or, distribute the Or over the contained clauses. assert __distribute_ors_naive(Or([1, 2])) == And([Or([1, 2])]) assert __distribute_ors_naive(Or([1, And([2, 3])])) == And([Or([1, 2]), Or([1, 3])]) assert __distribute_ors_naive(Or([And([1, 2]), And([3, 4])])) == And([ Or([1, 3]), Or([1, 4]), Or([2, 3]), Or([2, 4]) ]) # When f is an And, disitribute Ors over the contained clauses. assert __distribute_ors_naive(And([1, Not(2)])) == And([1, Not(2)]) assert __distribute_ors_naive(And([1, Or([2, And([3, 4])])])) == And([ Or([2, 3]), Or([2, 4]), 1 ])
def test_to_cnf_tseitin(): assert to_cnf_tseitin(Or([1, And([2, 3])]), 4) == (And([ # 4 <=> (2 ^ 3) Or([Not(2), Not(3), 4]), Or([2, Not(4)]), Or([3, Not(4)]), # 5 <=> (1 v 4) Or([1, 4, Not(5)]), Or([Not(1), 5]), Or([Not(4), 5]), # Final clause 5 ]), 6)
def test_to_cnf_naive(): assert to_cnf_naive(1, 2) == (And([1]), 2) assert to_cnf_naive(And([1]), 2) == (And([1]), 2) assert to_cnf_naive(And([1, 2]), 3) == (And([1, 2]), 3) assert to_cnf_naive(Or([1, 2]), 3) == (And([Or([1, 2])]), 3) assert to_cnf_naive(Or([1, And([2, 3])]), 4) == (And([Or([1, 2]), Or([1, 3])]), 4) formula = And([Iff(1, And([2, 3])), Iff(4, And([5, 6]))]) expected_cnf = And([ Or([1, Not(2), Not(3)]), Or([Not(1), 2]), Or([Not(1), 3]), Or([4, Not(5), Not(6)]), Or([Not(4), 5]), Or([Not(4), 6]) ]) assert to_cnf_switching(formula, 7) == (expected_cnf, 7)
def apply_to_backend_request(self, block: Block, level: Tuple[Factor, Union[SimpleLevel, DerivedLevel]], backend_request: BackendRequest) -> None: # Request sublists for k+1 to allow us to determine the transition sublists = self._build_variable_sublists(block, level, self.k + 1) implications = [] if sublists: # Starting corner case implications.append(If(sublists[0][0], And(sublists[0][1:-1]))) for sublist in sublists: implications.append(If(And([Not(sublist[0]), sublist[1]]), And(sublist[2:]))) # Ending corner case implications.append(If(sublists[-1][-1], And(sublists[-1][1:-1]))) (cnf, new_fresh) = block.cnf_fn(And(implications), backend_request.fresh) backend_request.cnfs.append(cnf) backend_request.fresh = new_fresh
def test_tseitin_rep_not(): from sweetpea.logic import __tseitin_rep, _Cache # Should replace Not(var) with another variable clauses = [] cache = _Cache(2) assert __tseitin_rep(Not(1), clauses, cache) == 2 # Make sure that Not(1) was cached. assert cache.get(str(Not(1))) == 2 # Make sure that the correct implication clauses were added. assert Or([ 1, 2 ]) in clauses assert Or([Not(1), Not(2)]) in clauses # No clauses should be added if the value was already cached. clauses = [] cache = _Cache(2) assert cache.get(str(Not(1))) == 2 # Prewarm the cache. assert __tseitin_rep(Not(1), clauses, cache) == 2 assert clauses == []
def test_to_cnf_switching(): formula = Or([ And([3, 4]), And([Not(2), Or([1, 5])]) ]) expected_cnf = And([ Or([3, Not(6)]), Or([4, Not(6)]), Or([1, 5, 6]), Or([Not(2), 6]) ]) assert to_cnf_switching(formula, 6) == (expected_cnf, 7) formula = And([ Iff(1, And([2, 3])), Iff(4, And([5, 6])) ]) expected_cnf = And([ Or([1, Not(2), Not(3)]), Or([Not(1), 2]), Or([Not(1), 3]), Or([4, Not(5), Not(6)]), Or([Not(4), 5]), Or([Not(4), 6]) ]) assert to_cnf_switching(formula, 7) == (expected_cnf, 7)
def test_apply_demorgan(): from sweetpea.logic import __apply_demorgan # P ==> P, ~P ==> ~P assert __apply_demorgan(4) == 4 assert __apply_demorgan(Not(4)) == Not(4) # ~~P ==> P assert __apply_demorgan(Not(Not(4))) == 4 # ~(P v Q) ==> ~P ^ ~Q assert __apply_demorgan(Not(Or([1, 2]))) == And([Not(1), Not(2)]) # ~(P ^ Q) ==> ~P v ~Q assert __apply_demorgan(Not(And([1, 2]))) == Or([Not(1), Not(2)]) assert __apply_demorgan(Or([1, Not(And([2, 3]))])) == Or([1, Not(2), Not(3)])
def test_exactlykinarow(): backend_request = __run_kinarow( ExactlyKInARow(1, (color, get_level_from_name(color, "red")))) (expected_cnf, expected_fresh) = to_cnf_tseitin( And([ If(1, Not(7)), If(And([Not(1), 7]), Not(13)), If(And([Not(7), 13]), Not(19)) ]), 25) assert backend_request.fresh == expected_fresh assert backend_request.cnfs == [expected_cnf] backend_request = __run_kinarow( ExactlyKInARow(2, (color, get_level_from_name(color, "red")))) (expected_cnf, expected_fresh) = to_cnf_tseitin( And([ If(1, And([7, Not(13)])), If(And([Not(1), 7]), And([13, Not(19)])), If(And([Not(7), 13]), 19), If(19, 13) ]), 25) assert backend_request.fresh == expected_fresh assert backend_request.cnfs == [expected_cnf] backend_request = __run_kinarow( ExactlyKInARow(3, (color, get_level_from_name(color, "red")))) (expected_cnf, expected_fresh) = to_cnf_tseitin( And([ If(1, And([7, 13, Not(19)])), If(And([Not(1), 7]), And([13, 19])), If(19, 13), If(13, 7) ]), 25) assert backend_request.fresh == expected_fresh assert backend_request.cnfs == [expected_cnf]