def test_derivation_with_general_window():
    block = fully_cross_block([color, text, congruent_bookend], [color, text],
                              [])
    # congruent bookend - yes
    d = Derivation(16, [[0, 2], [1, 3]], congruent_bookend)
    backend_request = BackendRequest(19)
    d.apply(block, backend_request)

    (expected_cnf, expected_fresh) = to_cnf_tseitin(
        And([
            Iff(17, Or([And([1, 3]), And([2, 4])])),
            Iff(19, Or([And([13, 15]), And([14, 16])]))
        ]), 19)

    assert backend_request.fresh == expected_fresh
    assert backend_request.cnfs == [expected_cnf]

    # congruent bookend - no
    d = Derivation(17, [[0, 3], [1, 2]], congruent_bookend)
    backend_request = BackendRequest(19)
    d.apply(block, backend_request)

    (expected_cnf, expected_fresh) = to_cnf_tseitin(
        And([
            Iff(18, Or([And([1, 4]), And([2, 3])])),
            Iff(20, Or([And([13, 16]), And([14, 15])]))
        ]), 19)

    assert backend_request.fresh == expected_fresh
    assert backend_request.cnfs == [expected_cnf]
Exemple #2
0
def test_tseitin_rep_and():
    from sweetpea.logic import __tseitin_rep, _Cache

    clauses = []
    cache = _Cache(4)

    # Make sure return is correct, and value was cached.
    assert __tseitin_rep(And([1, 2, 3]), clauses, cache) == 4
    assert cache.get(str(And([1, 2, 3]))) == 4

    # Make sure equivalence clauses were added.
    assert Or([1, Not(4)]) in clauses
    assert Or([2, Not(4)]) in clauses
    assert Or([3, Not(4)]) in clauses
    assert Or([Not(1), Not(2), Not(3), 4]) in clauses

    # Don't duplicate clauses when the cache was already populated
    clauses = []
    cache = _Cache(4)

    # Prewarm the cache
    assert cache.get(str(And([1, 2, 3]))) == 4
    assert __tseitin_rep(And([1, 2, 3]), clauses, cache) == 4

    # Make sure no clauses were added
    assert clauses == []
Exemple #3
0
def test_tseitin_rep_iff():
    from sweetpea.logic import __tseitin_rep, _Cache

    clauses = []
    cache = _Cache(3)

    # Make sure return is correct and value was cached.
    assert __tseitin_rep(Iff(1, 2), clauses, cache) == 3
    assert cache.get(str(Iff(1, 2))) == 3

    # Make sure equivalence clauses were added.
    assert Or([    1,      2,      3 ]) in clauses
    assert Or([Not(1), Not(2),     3 ]) in clauses
    assert Or([    1,  Not(2), Not(3)]) in clauses
    assert Or([Not(1),     2,  Not(3)]) in clauses

    # Don't duplicate clauses when the cache was already populated.
    clauses = []
    cache = _Cache(3)

    # Prewarm the cache.
    assert cache.get(str(Iff(1, 2))) == 3
    assert __tseitin_rep(Iff(1, 2), clauses, cache) == 3

    # Make sure no clauses were added.
    assert clauses == []
Exemple #4
0
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_derivation():
    # Congruent derivation
    d = Derivation(4, [[0, 2], [1, 3]], con_factor)
    backend_request = BackendRequest(24)
    d.apply(block, backend_request)

    (expected_cnf, expected_fresh) = to_cnf_tseitin(
        And([
            Iff(5, Or([And([1, 3]), And([2, 4])])),
            Iff(11, Or([And([7, 9]), And([8, 10])])),
            Iff(17, Or([And([13, 15]), And([14, 16])])),
            Iff(23, Or([And([19, 21]), And([20, 22])]))
        ]), 24)

    assert backend_request.fresh == expected_fresh
    assert backend_request.cnfs == [expected_cnf]

    # Incongruent derivation
    d = Derivation(5, [[0, 3], [1, 2]], con_factor)
    backend_request = BackendRequest(24)
    d.apply(block, backend_request)

    (expected_cnf, expected_fresh) = to_cnf_tseitin(
        And([
            Iff(6, Or([And([1, 4]), And([2, 3])])),
            Iff(12, Or([And([7, 10]), And([8, 9])])),
            Iff(18, Or([And([13, 16]), And([14, 15])])),
            Iff(24, Or([And([19, 22]), And([20, 21])]))
        ]), 24)

    assert backend_request.fresh == expected_fresh
    assert backend_request.cnfs == [expected_cnf]
Exemple #6
0
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)])
Exemple #7
0
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_derivation_with_multiple_transitions():
    block = fully_cross_block(
        [color, text, color_repeats_factor, text_repeats_factor],
        [color, text], [])

    # Text repeats derivation
    d = Derivation(22, [[2, 6], [3, 7]], text_repeats_factor)
    backend_request = BackendRequest(29)
    d.apply(block, backend_request)

    (expected_cnf, expected_fresh) = to_cnf_tseitin(
        And([
            Iff(23, Or([And([3, 7]), And([4, 8])])),
            Iff(25, Or([And([7, 11]), And([8, 12])])),
            Iff(27, Or([And([11, 15]), And([12, 16])]))
        ]), 29)

    assert backend_request.fresh == expected_fresh
    assert backend_request.cnfs == [expected_cnf]

    # Text does not repeat derivation
    d = Derivation(23, [[2, 7], [3, 6]], text_repeats_factor)
    backend_request = BackendRequest(29)
    d.apply(block, backend_request)

    (expected_cnf, expected_fresh) = to_cnf_tseitin(
        And([
            Iff(24, Or([And([3, 8]), And([4, 7])])),
            Iff(26, Or([And([7, 12]), And([8, 11])])),
            Iff(28, Or([And([11, 16]), And([12, 15])]))
        ]), 29)

    assert backend_request.fresh == expected_fresh
    assert backend_request.cnfs == [expected_cnf]
def test_derivation_with_transition():
    block = fully_cross_block([color, text, color_repeats_factor],
                              [color, text], [])

    # Color repeats derivation
    d = Derivation(16, [[0, 4], [1, 5]], color_repeats_factor)
    backend_request = BackendRequest(23)
    d.apply(block, backend_request)

    (expected_cnf, expected_fresh) = to_cnf_tseitin(
        And([
            Iff(17, Or([And([1, 5]), And([2, 6])])),
            Iff(19, Or([And([5, 9]), And([6, 10])])),
            Iff(21, Or([And([9, 13]), And([10, 14])]))
        ]), 23)

    assert backend_request.fresh == expected_fresh
    assert backend_request.cnfs == [expected_cnf]

    # Color does not repeat derivation
    d = Derivation(17, [[0, 5], [1, 4]], color_repeats_factor)
    backend_request = BackendRequest(23)
    d.apply(block, backend_request)

    (expected_cnf, expected_fresh) = to_cnf_tseitin(
        And([
            Iff(18, Or([And([1, 6]), And([2, 5])])),
            Iff(20, Or([And([5, 10]), And([6, 9])])),
            Iff(22, Or([And([9, 14]), And([10, 13])]))
        ]), 23)

    assert backend_request.fresh == expected_fresh
    assert backend_request.cnfs == [expected_cnf]
Exemple #10
0
    def __apply_derivation(self, block: Block, backend_request: BackendRequest) -> None:
        trial_size = block.variables_per_trial()
        cross_size = block.trials_per_sample()

        iffs = []
        for n in range(cross_size):
            or_clause = Or(list(And(list(map(lambda x: x + (n * trial_size) + 1, l))) for l in self.dependent_idxs))
            iffs.append(Iff(self.derived_idx + (n * trial_size) + 1, or_clause))

        (cnf, new_fresh) = block.cnf_fn(And(iffs), backend_request.fresh)

        backend_request.cnfs.append(cnf)
        backend_request.fresh = new_fresh
def test_derivation_with_three_level_transition():
    f = Factor("f", ["a", "b", "c"])
    f_transition = Factor("transition", [
        DerivedLevel("aa",
                     Transition(lambda c: c[0] == "a" and c[1] == "a", [f])),
        DerivedLevel("ab",
                     Transition(lambda c: c[0] == "a" and c[1] == "b", [f])),
        DerivedLevel("ac",
                     Transition(lambda c: c[0] == "a" and c[1] == "c", [f])),
        DerivedLevel("ba",
                     Transition(lambda c: c[0] == "b" and c[1] == "a", [f])),
        DerivedLevel("bb",
                     Transition(lambda c: c[0] == "b" and c[1] == "b", [f])),
        DerivedLevel("bc",
                     Transition(lambda c: c[0] == "b" and c[1] == "c", [f])),
        DerivedLevel("ca",
                     Transition(lambda c: c[0] == "c" and c[1] == "a", [f])),
        DerivedLevel("cb",
                     Transition(lambda c: c[0] == "c" and c[1] == "b", [f])),
        DerivedLevel("cc",
                     Transition(lambda c: c[0] == "c" and c[1] == "c", [f])),
    ])

    block = fully_cross_block([f, f_transition], [f], [])

    # a-a derivation
    d = Derivation(9, [[0, 3]], f_transition)
    backend_request = BackendRequest(28)
    d.apply(block, backend_request)

    (expected_cnf, expected_fresh) = to_cnf_tseitin(
        And([Iff(10, Or([And([1, 4])])),
             Iff(19, Or([And([4, 7])]))]), 28)

    assert backend_request.fresh == expected_fresh
    assert backend_request.cnfs == [expected_cnf]
Exemple #12
0
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_derivation_with_unusual_order():
    d = Derivation(0, [[4, 2], [5, 3]], congruency)
    backend_request = BackendRequest(64)
    d.apply(block, backend_request)

    (expected_cnf, expected_fresh) = to_cnf_tseitin(
        And([
            Iff(1, Or([And([5, 3]), And([6, 4])])),
            Iff(9, Or([And([13, 11]), And([14, 12])])),
            Iff(17, Or([And([21, 19]), And([22, 20])])),
            Iff(25, Or([And([29, 27]), And([30, 28])])),
            Iff(33, Or([And([37, 35]), And([38, 36])])),
            Iff(41, Or([And([45, 43]), And([46, 44])])),
            Iff(49, Or([And([53, 51]), And([54, 52])])),
            Iff(57, Or([And([61, 59]), And([62, 60])])),
        ]), 64)

    assert backend_request.fresh == expected_fresh
    assert backend_request.cnfs == [expected_cnf]
Exemple #14
0
    def __apply_derivation_with_complex_window(self, block: Block, backend_request: BackendRequest) -> None:
        trial_size = block.variables_per_trial()
        trial_count = block.trials_per_sample()
        iffs = []
        f = self.factor
        window = f.levels[0].window
        t = 0
        for n in range(trial_count):
            if not f.applies_to_trial(n + 1):
                continue

            num_levels = len(f.levels)
            get_trial_size = lambda x: trial_size if x < block.grid_variables() else len(block.decode_variable(x+1)[0].levels)
            or_clause = Or(list(And(list(map(lambda x: x + (t * window.stride * get_trial_size(x) + 1), l))) for l in self.dependent_idxs))
            iffs.append(Iff(self.derived_idx + (t * num_levels) + 1, or_clause))
            t += 1
        (cnf, new_fresh) = block.cnf_fn(And(iffs), backend_request.fresh)

        backend_request.cnfs.append(cnf)
        backend_request.fresh = new_fresh
Exemple #15
0
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
    ])
Exemple #16
0
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]]
Exemple #17
0
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)
Exemple #18
0
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)
Exemple #19
0
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
    )