Esempio n. 1
0
def cyclic_core(f, care, fol):
    """Shallow minimal cover, only up to cyclic core."""
    log.info('cyclic core computation')
    t0 = time.time()
    # assert
    assert f in fol.bdd, f
    assert care in fol.bdd, care
    assert care != fol.false, 'empty care set'
    assert f != fol.false, 'nothing to cover'
    assert f != fol.true or care != fol.true, (
        'no variables involved in problem')
    prm = lat.setup_aux_vars(f, care, fol)
    lat.setup_lattice(prm, fol)
    fcare = ~care | f
    bab = _BranchAndBound(prm, fol)
    # covering problem
    x = lat.embed_as_implicants(f, prm, fol)
    y = lat.prime_implicants(fcare, prm, fol)
    # assert problem is feasible
    assert x != fol.false
    assert y != fol.false
    assert _covers(y, f, prm, fol)
    xcore, ycore, essential = _cyclic_core_fixpoint(x, y, bab, fol)
    if xcore == fol.false:
        assert _covers(essential, f, prm, fol)
    _print_cyclic_core(x, y, xcore, ycore, essential, t0, bab.prm, fol)
    return xcore, ycore, essential
Esempio n. 2
0
def test_cyclic_core_recursion():
    """One cyclic core."""
    fol = _fol.Context()
    fol.declare(x=(0, 1), y=(0, 1), z=(0, 1))
    s = r'''
        (
            \/ (z = 1  /\  y = 0)
            \/ (x = 0  /\  z = 1)
            \/ (y = 1  /\  x = 0)
            \/ (y = 1  /\  z = 0)
            \/ (x = 1  /\  z = 0)
            \/ (x = 1  /\  y = 0)
        )
        '''
    f = fol.add_expr(s)
    care = fol.true
    # setup variables and lattice
    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)
    # enumerative check
    enumerated_covers(x, y, prm, fol)
    # symbolically minimize
    mincovers = cov_enum.minimize(f, care, fol)
    n = len(mincovers)
    assert n == 2, (n, mincovers)
    for cover in mincovers:
        n = fol.count(cover)
        primes = list(fol.pick_iter(cover))
        assert n == 3, (n, primes)
Esempio n. 3
0
def cyclic_core(f, care, fol):
    """Shallow minimal cover, only up to cyclic core."""
    log.info('cyclic core computation')
    t0 = time.time()
    # assert
    assert f in fol.bdd, f
    assert care in fol.bdd, care
    assert care != fol.false, 'empty care set'
    assert f != fol.false, 'nothing to cover'
    assert f != fol.true or care != fol.true, (
        'no variables involved in problem')
    prm = lat.setup_aux_vars(f, care, fol)
    lat.setup_lattice(prm, fol)
    fcare = ~ care | f
    bab = _BranchAndBound(prm, fol)
    # covering problem
    x = lat.embed_as_implicants(f, prm, fol)
    y = lat.prime_implicants(fcare, prm, fol)
    # assert problem is feasible
    assert x != fol.false
    assert y != fol.false
    assert _covers(y, f, prm, fol)
    xcore, ycore, essential = _cyclic_core_fixpoint(
        x, y, bab, fol)
    if xcore == fol.false:
        assert _covers(essential, f, prm, fol)
    _print_cyclic_core(
        x, y, xcore, ycore, essential,
        t0, bab.prm, fol)
    return xcore, ycore, essential
Esempio n. 4
0
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 cover as BDD over parameters
    @rtype: BDD node
    """
    # 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 _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 _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('---- branching ----')
    path_cost = 0.0
    prm = lat.setup_aux_vars(f, care, fol)
    lat.setup_lattice(prm, fol)
    # covering problem
    fcare = f | ~ care
    # the slack is introduced by having more primes
    # (those for `fcare`) to cover the same minterms (`f`)
    x = lat.embed_as_implicants(f, prm, fol)
    y = lat.prime_implicants(fcare, prm, fol)
    bab = _BranchAndBound(prm, fol)
    # initialize upper bound
    bab.upper_bound = _upper_bound(
        x, y, prm.p_leq_q, prm.p_to_q, fol)
    # assert covers(bab.best_cover, f, prm, fol)
    cover, _ = _traverse(x, y, path_cost, bab, fol)
    if cover is None:
        cover, _ = _some_cover(x, y, prm.p_leq_q, p_to_q, fol)
    assert cover is not None
    cover = unfloors(cover, y, fol, bab)
    assert_is_a_cover_from_y(
        cover, y, f, prm, fol)
    low = care & ~ f
    assert _none_covered(cover, low, prm, fol)
    log.info('==== branching ==== ')
    return cover
Esempio n. 5
0
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 cover as BDD over parameters
    @rtype: BDD node
    """
    # 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 _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 _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('---- branching ----')
    path_cost = 0.0
    prm = lat.setup_aux_vars(f, care, fol)
    lat.setup_lattice(prm, fol)
    # covering problem
    fcare = f | ~ care
    # the slack is introduced by having more primes
    # (those for `fcare`) to cover the same minterms (`f`)
    x = lat.embed_as_implicants(f, prm, fol)
    y = lat.prime_implicants(fcare, prm, fol)
    bab = _BranchAndBound(prm, fol)
    # initialize upper bound
    bab.upper_bound = _upper_bound(
        x, y, prm.p_leq_q, prm.p_to_q, fol)
    # assert covers(bab.best_cover, f, prm, fol)
    cover, _ = _traverse(x, y, path_cost, bab, fol)
    if cover is None:
        cover, _ = _some_cover(x, y, prm.p_leq_q, p_to_q, fol)
    assert cover is not None
    cover = unfloors(cover, y, fol, bab)
    assert_is_a_cover_from_y(
        cover, y, f, prm, fol)
    low = care & ~ f
    assert _none_covered(cover, low, prm, fol)
    log.info('==== branching ==== ')
    return cover
Esempio n. 6
0
def test_scaling_equality():
    aut = _fol.Context()
    x_vars = dict(x=(0, 10), y=(0, 15), z=(0, 15))
    aut.declare(**x_vars)
    params = dict(pa='a', pb='b', qa='u', qb='v')
    p_dom = lat._parameter_table(x_vars,
                                 aut.vars,
                                 a_name=params['pa'],
                                 b_name=params['pb'])
    q_dom = lat._parameter_table(x_vars,
                                 aut.vars,
                                 a_name=params['qa'],
                                 b_name=params['qb'])
    aut.declare(**p_dom)
    aut.declare(**q_dom)
    px = lat._map_vars_to_parameters(x_vars,
                                     a_name=params['pa'],
                                     b_name=params['pb'])
    qx = lat._map_vars_to_parameters(x_vars,
                                     a_name=params['qa'],
                                     b_name=params['qb'])
    p_to_q = lat._renaming_between_parameters(px, qx)
    x_as_x = {xj: dict(a=xj, b=xj) for xj in px}
    varmap = lat.parameter_varmap(px, x_as_x)
    log.info('Number of variables: {n}'.format(n=len(varmap)))
    u = lat.subseteq(varmap, aut)
    #
    s = ('( '
         '(z = 1  /\  y <= 0)  \/ '
         '(x = 0  /\  z = 1)  \/ '
         '(y >= 1  /\  x <= 0)  \/ '
         '(y >= 1  /\  z <= 0)  \/ '
         '(x >= 1  /\  z <= 0)  \/ '
         '(x >= 1  /\  y <= 0) '
         ') ')
    f = aut.add_expr(s)
    prm = lat.Parameters()
    prm._px = px
    lat.embed_as_implicants(f, prm, aut)
Esempio n. 7
0
def _none_covered(cover_p, f, prm, fol):
    """Return `True` if `cover_p` covers no minterm in `f`.

    Arguments similar to `covers`.
    """
    fp = lat.embed_as_implicants(f, prm, fol)
    fq = fol.let(prm.p_to_q, fp)
    # \A p:  \/ ~ cover(p)
    #        \/ ~ \E q:  /\ f(q)
    #                    /\ Intersect(p, q)
    r = fq & lat.implicants_intersect(prm, fol)
    r = ~fol.exist(prm.q_vars, r)
    r |= ~cover_p
    r = fol.forall(prm.p_vars, r)
    return r == fol.true
Esempio n. 8
0
def test_scaling_equality():
    aut = _fol.Context()
    x_vars = dict(x=(0, 10), y=(0, 15), z=(0, 15))
    aut.declare(**x_vars)
    params = dict(pa='a', pb='b', qa='u', qb='v')
    p_dom = lat._parameter_table(
        x_vars, aut.vars,
        a_name=params['pa'], b_name=params['pb'])
    q_dom = lat._parameter_table(
        x_vars, aut.vars,
        a_name=params['qa'], b_name=params['qb'])
    aut.declare(**p_dom)
    aut.declare(**q_dom)
    px = lat._map_vars_to_parameters(
        x_vars, a_name=params['pa'], b_name=params['pb'])
    qx = lat._map_vars_to_parameters(
        x_vars, a_name=params['qa'], b_name=params['qb'])
    p_to_q = lat._renaming_between_parameters(px, qx)
    x_as_x = {xj: dict(a=xj, b=xj) for xj in px}
    varmap = lat.parameter_varmap(px, x_as_x)
    log.info('Number of variables: {n}'.format(n=len(varmap)))
    u = lat.subseteq(varmap, aut)
    #
    s = (
        '( '
        '(z = 1  /\  y <= 0)  \/ '
        '(x = 0  /\  z = 1)  \/ '
        '(y >= 1  /\  x <= 0)  \/ '
        '(y >= 1  /\  z <= 0)  \/ '
        '(x >= 1  /\  z <= 0)  \/ '
        '(x >= 1  /\  y <= 0) '
        ') ')
    f = aut.add_expr(s)
    prm = lat.Parameters()
    prm._px = px
    lat.embed_as_implicants(f, prm, aut)
Esempio n. 9
0
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
Esempio n. 10
0
def _none_covered(
        cover_p, f, prm, fol):
    """Return `True` if `cover_p` covers no minterm in `f`.

    Arguments similar to `covers`.
    """
    fp = lat.embed_as_implicants(f, prm, fol)
    fq = fol.let(prm.p_to_q, fp)
    # \A p:  \/ ~ cover(p)
    #        \/ ~ \E q:  /\ f(q)
    #                    /\ Intersect(p, q)
    r = fq & lat.implicants_intersect(prm, fol)
    r = ~ fol.exist(prm.q_vars, r)
    r |= ~ cover_p
    r = fol.forall(prm.p_vars, r)
    return r == fol.true
Esempio n. 11
0
def test_embed_as_implicants():
    fol, prm = setup_aut()
    prm._px = dict(x=prm._px['x'])  # restrict keys
    u = fol.add_expr('2 <= x  /\  x <= 9')
    r = lat.embed_as_implicants(u, prm, fol)
    r_ = lat._embed_as_implicants_naive(u, prm._px, fol)
    assert r == r_, (r, r_)
    v = fol.add_expr('(a_x = 2  /\  b_x = 2) \/'
                     '(a_x = 3  /\  b_x = 3) \/'
                     '(a_x = 4  /\  b_x = 4) \/'
                     '(a_x = 5  /\  b_x = 5) \/'
                     '(a_x = 6  /\  b_x = 6) \/'
                     '(a_x = 7  /\  b_x = 7) \/'
                     '(a_x = 8  /\  b_x = 8) \/'
                     '(a_x = 9  /\  b_x = 9)')
    assert r == v
Esempio n. 12
0
def test_embed_as_implicants():
    fol, prm = setup_aut()
    prm._px = dict(x=prm._px['x'])  # restrict keys
    u = fol.add_expr('2 <= x  /\  x <= 9')
    r = lat.embed_as_implicants(u, prm, fol)
    r_ = lat._embed_as_implicants_naive(u, prm._px, fol)
    assert r == r_, (r, r_)
    v = fol.add_expr(
        '(a_x = 2  /\  b_x = 2) \/'
        '(a_x = 3  /\  b_x = 3) \/'
        '(a_x = 4  /\  b_x = 4) \/'
        '(a_x = 5  /\  b_x = 5) \/'
        '(a_x = 6  /\  b_x = 6) \/'
        '(a_x = 7  /\  b_x = 7) \/'
        '(a_x = 8  /\  b_x = 8) \/'
        '(a_x = 9  /\  b_x = 9)')
    assert r == v
Esempio n. 13
0
def _covers(cover_p, f, prm, fol):
    """Return `True` if `cover_p` covers `f`.

    This is the operator `IsACover` defined in the
    module `spec/MinCover.tla`.

    @param cover_p: primes, repr as `p`
    @param f: elements to cover, repr as `x`
    """
    fp = lat.embed_as_implicants(f, prm, fol)
    cover_q = fol.let(prm.p_to_q, cover_p)
    # \A p:  \/ ~ f(p)
    #        \/ \E q:  cover(q) /\ (p <= q)
    r = cover_q & prm.p_leq_q
    r = fol.exist(prm.q_vars, r)
    r |= ~fp
    r = fol.forall(prm.p_vars, r)
    return r == fol.true
Esempio n. 14
0
def _covers(
        cover_p, f, prm, fol):
    """Return `True` if `cover_p` covers `f`.

    This is the operator `IsACover` defined in the
    module `spec/MinCover.tla`.

    @param cover_p: primes, repr as `p`
    @param f: elements to cover, repr as `x`
    """
    fp = lat.embed_as_implicants(f, prm, fol)
    cover_q = fol.let(prm.p_to_q, cover_p)
    # \A p:  \/ ~ f(p)
    #        \/ \E q:  cover(q) /\ (p <= q)
    r = cover_q & prm.p_leq_q
    r = fol.exist(prm.q_vars, r)
    r |= ~ fp
    r = fol.forall(prm.p_vars, r)
    return r == fol.true
Esempio n. 15
0
def _minimize_two_managers(f, care, fol):
    """Optimized version of `minimize` for large problems."""
    if not _care_implies_type_hints(f, care, fol):
        log.warning('care set should imply type hints')
    if not _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('---- branching ----')
    path_cost = 0.0
    # x_vars, px, qx, p_to_q
    prm = lat.setup_aux_vars(f, care, fol)
    # manager where optimization happens
    fol_2 = type(fol)()
    fol_2.add_vars(fol.vars)
    # x (to be covered)
    log.info('embed implicants')
    x = lat.embed_as_implicants(f, prm, fol)
    x = fol.copy(x, fol_2)
    # covering problem
    fcare = f | ~ care
    lat.setup_lattice(prm, fol_2)
    # y (to use in cover)
    log.info('primes')
    fcare_2 = fol.copy(fcare, fol_2)
    y = lat.prime_implicants(fcare_2, prm, fol_2)
    del fcare_2
    bab = _BranchAndBound(prm, fol_2)
    # initialize upper bound
    bab.upper_bound = _upper_bound(
        x, y, prm.p_leq_q, prm.p_to_q, fol_2)
    # assert _covers(bab.best_cover, f, prm, fol_2)
    log.info('traverse')
    cover, _ = _traverse(x, y, path_cost, bab, fol_2)
    if cover is None:
        cover, _ = _some_cover(x, y, prm.p_leq_q, prm.p_to_q, fol_2)
    assert cover is not None
    cover = unfloors(cover, y, fol_2, bab)
    log.info('==== branching ==== ')
    del fcare, prm, bab
    cover = fol_2.copy(cover, fol)
    return cover
Esempio n. 16
0
def _minimize_two_managers(f, care, fol):
    """Optimized version of `minimize` for large problems."""
    if not _care_implies_type_hints(f, care, fol):
        log.warning('care set should imply type hints')
    if not _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('---- branching ----')
    path_cost = 0.0
    # x_vars, px, qx, p_to_q
    prm = lat.setup_aux_vars(f, care, fol)
    # manager where optimization happens
    fol_2 = type(fol)()
    fol_2.add_vars(fol.vars)
    # x (to be covered)
    log.info('embed implicants')
    x = lat.embed_as_implicants(f, prm, fol)
    x = fol.copy(x, fol_2)
    # covering problem
    fcare = f | ~ care
    lat.setup_lattice(prm, fol_2)
    # y (to use in cover)
    log.info('primes')
    fcare_2 = fol.copy(fcare, fol_2)
    y = lat.prime_implicants(fcare_2, prm, fol_2)
    del fcare_2
    bab = _BranchAndBound(prm, fol_2)
    # initialize upper bound
    bab.upper_bound = _upper_bound(
        x, y, prm.p_leq_q, prm.p_to_q, fol_2)
    # assert _covers(bab.best_cover, f, prm, fol_2)
    log.info('traverse')
    cover, _ = _traverse(x, y, path_cost, bab, fol_2)
    if cover is None:
        cover, _ = _some_cover(x, y, prm.p_leq_q, prm.p_to_q, fol_2)
    assert cover is not None
    cover = unfloors(cover, y, fol_2, bab)
    log.info('==== branching ==== ')
    del fcare, prm, bab
    cover = fol_2.copy(cover, fol)
    return cover