def test_transpose(): fol = _fol.Context() dvars = {'p': (0, 4), 'q': (0, 4), "p_cp": (0, 4)} fol.declare(**dvars) s = '(p = 1) \/ (p = 2) \/ (p = 4)' p_is_prime = fol.add_expr(s) s = '(p = 1) \/ (p = 3)' p_is_signature = fol.add_expr(s) p_to_q = {'p': 'q'} p_leq_q = fol.add_expr("p <= q") u_leq_p = fol.add_expr("p_cp <= p") p_leq_u = fol.add_expr("p <= p_cp") prm = lat.Parameters() prm.u_leq_p = u_leq_p prm.p_leq_u = p_leq_u prm.p_leq_q = p_leq_q prm.p_to_q = p_to_q prm.p_vars = {'p'} prm.q_vars = {'q'} prm.u_vars = {'p_cp'} prm.p_to_u = {'p': 'p_cp'} bab = cov._BranchAndBound(prm, fol) tau = cov._floor( p_is_signature, p_is_prime, bab, fol) s = 'p = 1 \/ p = 3' tau_ = fol.add_expr(s) assert tau == tau_
def test_max_transpose(): fol = _fol.Context() # `p'` serves as `u` dvars = {'p': (0, 4), 'q': (0, 4), "p_cp": (0, 4)} fol.declare(**dvars) s = '(p = 2) \/ (p = 4)' p_is_prime = fol.add_expr(s) s = '(p = 1) \/ (p = 3)' p_is_signature = fol.add_expr(s) p_to_q = {'p': 'q'} # we use intervals `0..p` as literals px = dict(p=dict(a='0', b='p')) qx = dict(p=dict(a='0', b='q')) u_leq_p = fol.add_expr("p_cp <= p") p_leq_u = fol.add_expr("p <= p_cp") p_leq_q = fol.add_expr("p <= q") p_eq_q = fol.add_expr("p = q") # /\ (0 = 0) prm = lat.Parameters() prm._px = px prm._qx = qx prm.u_leq_p = u_leq_p prm.p_leq_u = p_leq_u prm.p_leq_q = p_leq_q prm.p_eq_q = p_eq_q prm.p_to_q = p_to_q prm.p_vars = {'p'} prm.q_vars = {'q'} prm.u_vars = {'p_cp'} prm.p_to_u = {'p': 'p_cp'} bab = cov._BranchAndBound(prm, fol) max_tau_x = cov._max_transpose( p_is_signature, p_is_prime, bab, fol) s = 'p = 3' max_tau_x_ = fol.add_expr(s) assert max_tau_x == max_tau_x_
def test_cyclic_core_fixpoint_recursive(): fol = _fol.Context() p_vars = dict(p=(0, 3)) q_vars = dict(q=(0, 3)) u_vars = dict(p_cp=(0, 3)) fol.declare(**p_vars) fol.declare(**q_vars) fol.declare(**u_vars) p_eq_q = fol.to_bdd(r'p \in 0..3 /\ p = q') # 3 # | | # 1 2 # | | # 0 leq = r''' # (* layer 1 *) \/ (p = 0 /\ q = 1) \/ (p = 0 /\ q = 2) # (* layer 2 *) \/ (p = 1 /\ q = 3) \/ (p = 2 /\ q = 3) # transitivity \/ (p = 0 /\ q = 3) # equality \/ (p = q /\ p \in 0..3) ''' p_leq_q = fol.to_bdd(leq) u_leq_p = fol.to_bdd(leq.replace('p', 'p_cp').replace('q', 'p')) p_leq_u = fol.to_bdd(leq.replace('q', 'p_cp')) # bundle prm = Parameters() prm.p_vars = set(p_vars) prm.q_vars = set(q_vars) prm.u_vars = set(u_vars) # prm.p_to_q = dict(p='q') prm.q_to_p = dict(q='p') prm.p_to_u = dict(p='p_cp') # prm.u_leq_p = u_leq_p prm.p_leq_u = p_leq_u prm.p_leq_q = p_leq_q prm.p_eq_q = p_eq_q # x = fol.add_expr('p = 0') y = fol.add_expr(r'p \in 1..2') # path_cost = 0.0 bab = cov._BranchAndBound(prm, fol) bab.upper_bound = cov._upper_bound(x, y, prm.p_leq_q, prm.p_to_q, fol) path_cost = 0.0 mincovers = cov_enum._cyclic_core_fixpoint_recursive( x, y, path_cost, bab, fol) assert len(mincovers) == 2, mincovers mincovers_ = {fol.add_expr('p = 1'), fol.add_expr('p = 2')} assert mincovers == mincovers_, list(fol.pick_iter(mincovers))
def test_branch_and_bound_class(): fol = _fol.Context() prm = lat.Parameters() bab = cov._BranchAndBound(prm, fol) bab.lower_bound = 15 with assert_raises(AssertionError): bab.upper_bound = 10 bab.upper_bound = 20 # only init for lower bound with assert_raises(AssertionError): bab.lower_bound = 17 with assert_raises(AssertionError): bab.upper_bound = 10 bab.upper_bound = 18
def minimize(f, care, fol): """Compute minimal DNF of predicate `f` over integers. @param f: predicate over integer-valued variables @param care: care set as predicate over same variables @type f, care: BDD node @type fol: `omega.symbolic.fol.Context` @return: minimal covers as BDDs over parameters @rtype: set of BDD nodes """ # reasons for permisiveness here: # # - enable inspecting env violations of assumption # - make errors visible # - use entire instantiation domain # - permit computing DNF for care set using same `fol.vars` # - tests if not cov._care_implies_type_hints(f, care, fol): log.warning('care set should imply type hints') # could let # f &= care # but explicit is better. # Also, this permits working outside type hints. if not cov._f_implies_care(f, care, fol): log.warning('f should imply care set') if (f | ~care) == fol.true: log.warning('f covers care set, so trivial cover') log.info('---- branch and bound search ----') prm = lat.setup_aux_vars(f, care, fol) lat.setup_lattice(prm, fol) # covering problem fcare = f | ~care x = lat.embed_as_implicants(f, prm, fol) y = lat.prime_implicants(fcare, prm, fol) bab = cov._BranchAndBound(prm, fol) # initialize upper bound bab.upper_bound = cov._upper_bound(x, y, prm.p_leq_q, prm.p_to_q, fol) path_cost = 0.0 mincovers = _cyclic_core_fixpoint_recursive(x, y, path_cost, bab, fol) # assert assert mincovers for cover in mincovers: cov.assert_is_a_cover_from_y(cover, y, f, prm, fol) low = care & ~f assert cov._none_covered(cover, low, prm, fol) log.info('==== branch and bound search ==== ') return mincovers
def test_example_of_strong_reduction(): """Computing all minimal covers for the counterexample.""" # For now `cov.minimize` creates the primes etc in # a specific way, for a specific basis (orthotopes). # This example doesn't fit in that recipe, # so we rewrite some of the code in `minimize`. fol = _fol.Context() fol.to_bdd = fol.add_expr x_vars = dict(x=(0, 8)) p_vars = dict(p=(0, 8)) q_vars = dict(q=(0, 8)) u_vars = dict(p_cp=(0, 8)) fol.declare(**x_vars) fol.declare(**p_vars) fol.declare(**q_vars) fol.declare(**u_vars) p_eq_q = fol.to_bdd(r'p \in 0..8 /\ q \in 0..8 /\ p = q') leq = r''' # (* layer 1 *) \/ (p = 0 /\ q = 6) \/ (p = 0 /\ q = 7) \/ (p = 0 /\ q = 8) # (* layer 2 *) \/ (p = 6 /\ q = 2) \/ (p = 6 /\ q = 3) \/ (p = 7 /\ q = 3) \/ (p = 7 /\ q = 4) \/ (p = 8 /\ q = 4) \/ (p = 8 /\ q = 5) # (* layer 3 *) \/ (p = 2 /\ q = 1) \/ (p = 3 /\ q = 1) \/ (p = 4 /\ q = 1) \/ (p = 5 /\ q = 1) # transitivity \/ (p = 0 /\ q = 2) \/ (p = 0 /\ q = 3) \/ (p = 0 /\ q = 4) \/ (p = 0 /\ q = 5) \/ (p = 0 /\ q = 1) \/ (p = 6 /\ q = 1) \/ (p = 7 /\ q = 1) \/ (p = 8 /\ q = 1) # equality \/ (p = q /\ p \in 0..8 /\ q \in 0..8) ''' p_leq_q = fol.to_bdd(leq) u_leq_p = fol.to_bdd(leq.replace('p', 'p_cp').replace('q', 'p')) p_leq_u = fol.to_bdd(leq.replace('q', 'p_cp')) # bundle prm = Parameters() prm.x_vars = set(x_vars) prm.p_vars = set(p_vars) prm.q_vars = set(q_vars) prm.u_vars = set(u_vars) # prm.p_to_q = dict(p='q') prm.q_to_p = dict(q='p') prm.p_to_u = dict(p='p_cp') # prm.u_leq_p = u_leq_p prm.p_leq_u = p_leq_u prm.p_leq_q = p_leq_q prm.p_eq_q = p_eq_q # x = fol.add_expr(r'p \in 6..8') y = fol.add_expr(r'p \in 2..5') # path_cost = 0.0 bab = cov._BranchAndBound(prm, fol) bab.upper_bound = cov._upper_bound(x, y, prm.p_leq_q, prm.p_to_q, fol) path_cost = 0.0 mincovers = cov_enum._cyclic_core_fixpoint_recursive( x, y, path_cost, bab, fol) # enumerative check enumerated_covers(x, y, prm, fol) print('all minimal covers:') cov_enum._print_mincovers(mincovers, fol) # assert set of minimal covers is as expected assert len(mincovers) == 3, mincovers mincovers_t = set() for cover in mincovers: c = set() for d in fol.pick_iter(cover): r, = d.values() c.add(r) c = tuple(sorted(c)) mincovers_t.add(c) mincovers_t_ = {(2, 4), (3, 4), (3, 5)} assert mincovers_t == mincovers_t_, (mincovers_t, mincovers_t_)
def test_counterexample_to_strong_reduction(): """Not all minimal solutions can be recovered. This counterexample shows that the minimal covering algorithm does not ensure strong reduction. The reason is that the minimal cover before unfloors has max floors elements that are incomparable to some elements from which we could build alternative minimal covers. """ # For now `cov.minimize` creates the primes etc in # a specific way, for a specific basis. # This example doesn't fit in that recipe, # so we rewrite some of the code in `minimize`. fol = _fol.Context() fol.to_bdd = fol.add_expr x_vars = dict(x=(0, 8)) p_vars = dict(p=(0, 8)) q_vars = dict(q=(0, 8)) u_vars = dict(p_cp=(0, 8)) fol.declare(**x_vars, **p_vars, **q_vars, **u_vars) p_eq_q = fol.to_bdd(r'p \in 0..8 /\ q \in 0..8 /\ p = q') leq = r''' # (* layer 1 *) \/ (p = 0 /\ q = 6) \/ (p = 0 /\ q = 7) \/ (p = 0 /\ q = 8) # (* layer 2 *) \/ (p = 6 /\ q = 2) \/ (p = 6 /\ q = 3) \/ (p = 7 /\ q = 3) \/ (p = 7 /\ q = 4) \/ (p = 8 /\ q = 4) \/ (p = 8 /\ q = 5) # (* layer 3 *) \/ (p = 2 /\ q = 1) \/ (p = 3 /\ q = 1) \/ (p = 4 /\ q = 1) \/ (p = 5 /\ q = 1) # transitivity \/ (p = 0 /\ q = 2) \/ (p = 0 /\ q = 3) \/ (p = 0 /\ q = 4) \/ (p = 0 /\ q = 5) \/ (p = 0 /\ q = 1) \/ (p = 6 /\ q = 1) \/ (p = 7 /\ q = 1) \/ (p = 8 /\ q = 1) # equality \/ (p = q /\ p \in 0..8 /\ q \in 0..8) ''' p_leq_q = fol.to_bdd(leq) u_leq_p = fol.to_bdd(leq.replace('p', 'p_cp').replace('q', 'p')) p_leq_u = fol.to_bdd(leq.replace('q', 'p_cp')) # bundle prm = Parameters() prm.x_vars = set(x_vars) prm.p_vars = set(p_vars) prm.q_vars = set(q_vars) prm.u_vars = set(u_vars) # prm.p_to_q = dict(p='q') prm.q_to_p = dict(q='p') prm.p_to_u = dict(p='p_cp') # prm.u_leq_p = u_leq_p prm.p_leq_u = p_leq_u prm.p_leq_q = p_leq_q prm.p_eq_q = p_eq_q # x = fol.add_expr(r'p \in 6..8') y = fol.add_expr(r'p \in 2..5') # path_cost = 0.0 bab = cov._BranchAndBound(prm, fol) bab.upper_bound = cov._upper_bound(x, y, prm.p_leq_q, prm.p_to_q, fol) cover, _ = cov._traverse(x, y, path_cost, bab, fol) # assert that the result is indeed a cover f = x assert _covers(cover, f, prm, fol) # assert the cover is as expected primes = list(fol.pick_iter(cover)) print(primes) primes_ = [dict(p=3), dict(p=4)] assert len(primes) == 2, primes for p in primes_: assert p in primes, (p, primes)
def test_counterexample_to_strong_reduction(): """Not all minimal solutions can be recovered. This counterexample shows that the minimal covering algorithm does not ensure strong reduction. The reason is that the minimal cover before unfloors has max floors elements that are incomparable to some elements from which we could build alternative minimal covers. """ # For now `cov.minimize` creates the primes etc in # a specific way, for a specific basis. # This example doesn't fit in that recipe, # so we rewrite some of the code in `minimize`. fol = _fol.Context() fol.to_bdd = fol.add_expr x_vars = dict(x=(0, 8)) p_vars = dict(p=(0, 8)) q_vars = dict(q=(0, 8)) u_vars = dict(p_cp=(0, 8)) fol.declare(**x_vars, **p_vars, **q_vars, **u_vars) p_eq_q = fol.to_bdd('p \in 0..8 /\ q \in 0..8 /\ p = q') leq = ''' # (* layer 1 *) \/ (p = 0 /\ q = 6) \/ (p = 0 /\ q = 7) \/ (p = 0 /\ q = 8) # (* layer 2 *) \/ (p = 6 /\ q = 2) \/ (p = 6 /\ q = 3) \/ (p = 7 /\ q = 3) \/ (p = 7 /\ q = 4) \/ (p = 8 /\ q = 4) \/ (p = 8 /\ q = 5) # (* layer 3 *) \/ (p = 2 /\ q = 1) \/ (p = 3 /\ q = 1) \/ (p = 4 /\ q = 1) \/ (p = 5 /\ q = 1) \/ (p = q /\ p \in 0..8 /\ q \in 0..8) ''' p_leq_q = fol.to_bdd(leq) u_leq_p = fol.to_bdd(leq.replace('p', 'p_cp').replace('q', 'p')) p_leq_u = fol.to_bdd(leq.replace('q', 'p_cp')) # bundle prm = Parameters() prm.x_vars = set(x_vars) prm.p_vars = set(p_vars) prm.q_vars = set(q_vars) prm.u_vars = set(u_vars) # prm.p_to_q = dict(p='q') prm.q_to_p = dict(q='p') prm.p_to_u = dict(p='p_cp') # prm.u_leq_p = u_leq_p prm.p_leq_u = p_leq_u prm.p_leq_q = p_leq_q prm.p_eq_q = p_eq_q # x = fol.add_expr('p \in 6..8') y = fol.add_expr('p \in 2..5') # path_cost = 0.0 bab = cov._BranchAndBound(prm, fol) bab.upper_bound = cov._upper_bound( x, y, prm.p_leq_q, prm.p_to_q, fol) cover, _ = cov._traverse(x, y, path_cost, bab, fol) # assert that the result is indeed a cover f = x assert _covers(cover, f, prm, fol) # assert the cover is as expected primes = list(fol.pick_iter(cover)) print(primes) primes_ = [dict(p=3), dict(p=4)] assert len(primes) == 2, primes for p in primes_: assert p in primes, (p, primes)