Example #1
0
    def test_selection(self):
        with self.assertRaises(NoSolverAvailableError):
            QuantifierEliminator(logic=UFLIRA)

        with self.assertRaises(NoSolverAvailableError):
            QuantifierEliminator(name="nonexistent")

        # MathSAT QE does not support LIA
        with self.assertRaises(NoSolverAvailableError):
            QuantifierEliminator(name="msat", logic=LIA)
Example #2
0
    def __init__(self, environment):
        DagWalker.__init__(self, environment)
        self.backconversion = {}
        self.mgr = environment.formula_manager
        self._get_type = environment.stc.get_type

#         self.qelim = QuantifierEliminator(name="z3")
        self.qelim = QuantifierEliminator(name="scalarshannon")
        self.cache_qf = {}

        # Maps a Symbol into the corresponding internal yices instance
        self.symbol_to_decl = {}
        # Maps an internal yices instance into the corresponding symbol
        self.decl_to_symbol = {}
        self._yicesSort = {}
        self._yicesEnumSort = {}
        self._yicesEnumConsts = {}
        self._yices2pysmt = {}
Example #3
0
    def test_qe_z3(self):
        qe = QuantifierEliminator(name='z3')
        self._bool_example(qe)
        self._real_example(qe)
        self._int_example(qe)
        self._alternation_bool_example(qe)
        self._alternation_real_example(qe)
        self._alternation_int_example(qe)
        self._std_examples(qe, LRA)
        self._std_examples(qe, LIA)
        # Additional test for raising error on back conversion of
        # quantified formulae
        p, q = Symbol("p", INT), Symbol("q", INT)

        f = ForAll([p], Exists([q], Equals(ToReal(p),
                                           Plus(ToReal(q), ToReal(Int(1))))))
        with self.assertRaises(NotImplementedError):
            qe.eliminate_quantifiers(f).simplify()
Example #4
0
    def test_qe_eq(self):
        qe = QuantifierEliminator(logic=LRA)

        varA = Symbol("A", BOOL)
        varB = Symbol("B", BOOL)

        varAt = Symbol("At", REAL)
        varBt = Symbol("Bt", REAL)

        f = And(Iff(varA, GE(Minus(varAt, varBt), Real(0))),
                Iff(varB, LT(Minus(varAt, varBt), Real(1))))

        qf = Exists([varBt, varA], f)
        r1 = qe.eliminate_quantifiers(qf)

        try:
            self.assertValid(Iff(r1, qf), logic=LRA,
                             msg="The two formulas should be equivalent.")
        except SolverReturnedUnknownResultError:
            pass
Example #5
0
    def test_qe_eq(self):
        qe = QuantifierEliminator(logic=LRA)

        varA = Symbol("A", BOOL)
        varB = Symbol("B", BOOL)

        varAt = Symbol("At", REAL)
        varBt = Symbol("Bt", REAL)

        f = And(Iff(varA, GE(Minus(varAt, varBt), Real(0))),
                Iff(varB, LT(Minus(varAt, varBt), Real(1))))

        qf = Exists([varBt, varA], f)
        r1 = qe.eliminate_quantifiers(qf)

        try:
            self.assertValid(Iff(r1, qf), logic=LRA,
                             msg="The two formulas should be equivalent.")
        except SolverReturnedUnknownResultError:
            pass
Example #6
0
def check_install():
    """Checks which solvers are visible to pySMT."""

    from pysmt.shortcuts import Solver, QuantifierEliminator
    from pysmt.exceptions import NoSolverAvailableError

    required_solver = os.environ.get("PYSMT_SOLVER")
    if required_solver is None:
        required_solver = "None"
    elif required_solver == "cudd":
        # Special case for bdd
        required_solver = "bdd"

    print("Solvers:")
    for solver in ['msat', 'z3', 'cvc4', 'yices', 'bdd', 'picosat']:
        is_installed = False
        try:
            Solver(name=solver)
            is_installed = True
        except NoSolverAvailableError:
            is_installed = False
        print("  %s%s" % (solver.ljust(10), is_installed))

        if solver == required_solver and not is_installed:
            raise Exception("Was expecting to find %s installed" %
                            required_solver)

    print("\nQuantifier Eliminators:")
    for solver in ['msat_fm', 'msat_lw', 'z3', 'bdd']:
        is_installed = False
        try:
            QuantifierEliminator(name=solver)
            is_installed = True
        except NoSolverAvailableError:
            is_installed = False
        print("  %s%s" % (solver.ljust(10), is_installed))

        if solver == required_solver and not is_installed:
            raise Exception("Was expecting to find %s installed" %
                            required_solver)
Example #7
0
class YicesConverter(Converter, DagWalker):

    def __init__(self, environment):
        DagWalker.__init__(self, environment)
        self.backconversion = {}
        self.mgr = environment.formula_manager
        self._get_type = environment.stc.get_type

#         self.qelim = QuantifierEliminator(name="z3")
        self.qelim = QuantifierEliminator(name="scalarshannon")
        self.cache_qf = {}

        # Maps a Symbol into the corresponding internal yices instance
        self.symbol_to_decl = {}
        # Maps an internal yices instance into the corresponding symbol
        self.decl_to_symbol = {}
        self._yicesSort = {}
        self._yicesEnumSort = {}
        self._yicesEnumConsts = {}
        self._yices2pysmt = {}
    
    def quantifier_free(self, formula):
        if formula in self.cache_qf:
            return self.cache_qf[formula]
        formulaqf = self.qelim.eliminate_quantifiers(formula)
#         formulaqf = self.qelim.eliminate_quantifiers(formula).simplify()
        res = self.convert(formulaqf)
        self._yices2pysmt[res] = formula
        self.cache_qf[formula] = res
#         print("q : %s" % term.serialize())
#         print("qf: %s" % res.serialize())
#         assert(0)
        return res

    def get_term(self, formula, qf):
        if qf:
            return self.quantifier_free(formula)
        else:
            return self.convert(formula)

    @catch_conversion_error
    def convert(self, formula):
        res = self.walk(formula)
        self._yices2pysmt[res] = formula
        return res

    def back(self, expr, model=None):
        """Convert a Yices expression back into a pySMT expression.
        """
        if expr in self._yices2pysmt:
            return self._yices2pysmt[expr]
        raise NotImplementedError

    def _check_term_result(self, res):
        if res == -1:
            err = yicespy.yices_error_string()
            raise InternalSolverError("Yices returned an error: " + err)

    def walk_and(self, formula, args, **kwargs):
        res = yicespy.yices_and(len(args), yicespy.make_term_array(args))
        self._check_term_result(res)
        return res

    def walk_or(self, formula, args, **kwargs):
        res = yicespy.yices_or(len(args), yicespy.make_term_array(args))
        self._check_term_result(res)
        return res

    def walk_not(self, formula, args, **kwargs):
        res = yicespy.yices_not(args[0])
        self._check_term_result(res)
        return res

    def walk_symbol(self, formula, **kwargs):
        symbol_type = formula.symbol_type()
        var_type = self._type_to_yices(symbol_type)
        term = yicespy.yices_new_uninterpreted_term(var_type)
        yicespy.yices_set_term_name(term, formula.symbol_name())
        self._check_term_result(term)
        return term

    def _bound_symbol(self, var):
        symbol_type = var.symbol_type()
        var_type = self._type_to_yices(symbol_type)
        term = yicespy.yices_new_variable(var_type)
        yicespy.yices_set_term_name(term, var.symbol_name())
        return term

    def walk_iff(self, formula, args, **kwargs):
        res = yicespy.yices_iff(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_implies(self, formula, args, **kwargs):
        res = yicespy.yices_implies(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_le(self, formula, args, **kwargs):
        res = yicespy.yices_arith_leq_atom(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_lt(self, formula, args, **kwargs):
        res = yicespy.yices_arith_lt_atom(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_ite(self, formula, args, **kwargs):
        i, t, e = args
        res = yicespy.yices_ite(i, t, e)
        self._check_term_result(res)
        return res

    def walk_real_constant(self, formula, **kwargs):
        frac = formula.constant_value()
        n,d = frac.numerator, frac.denominator
        rep = str(n) + "/" + str(d)
        res = yicespy.yices_parse_rational(rep)
        self._check_term_result(res)
        return res

    def walk_int_constant(self, formula, **kwargs):
        assert is_pysmt_integer(formula.constant_value())
        rep = str(formula.constant_value())
        res = yicespy.yices_parse_rational(rep)
        self._check_term_result(res)
        return res

    def walk_bool_constant(self, formula, **kwargs):
        if formula.constant_value():
            return yicespy.yices_true()
        else:
            return yicespy.yices_false()

    def walk_exists(self, formula, args, **kwargs):
        (bound_formula, var_list) = \
                 self._rename_bound_variables(args[0], formula.quantifier_vars())
        res = yicespy.yices_exists(len(var_list), yicespy.make_term_array(var_list), bound_formula)
        self._check_term_result(res)
        return res

    def walk_forall(self, formula, args, **kwargs):
        (bound_formula, var_list) = \
                 self._rename_bound_variables(args[0], formula.quantifier_vars())
        res = yicespy.yices_forall(len(var_list), yicespy.make_term_array(var_list), bound_formula)
        self._check_term_result(res)
        return res

    def _rename_bound_variables(self, formula, variables):
        """Bounds the variables in formula.

        Returns a tuple (new_formula, new_var_list) in which the old
        variables have been replaced by the new variables in the list.
        """
        new_vars = [self._bound_symbol(x) for x in variables]
        old_vars = [self.walk_symbol(x) for x in variables]
        new_formula = yicespy.yices_subst_term(len(variables), yicespy.make_term_array(new_vars),
                                                yicespy.make_term_array(old_vars), formula)
        return (new_formula, new_vars)


    def walk_plus(self, formula, args, **kwargs):
        res = yicespy.yices_sum(len(args), yicespy.make_term_array(args))
        self._check_term_result(res)
        return res

    def walk_minus(self, formula, args, **kwargs):
        res = yicespy.yices_sub(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_equals(self, formula, args, **kwargs):
        tp = self._get_type(formula.arg(0))
        res = None
        if tp.is_bv_type():
            res = yicespy.yices_bveq_atom(args[0], args[1])
        elif tp.is_int_type() or tp.is_real_type():
            res = yicespy.yices_arith_eq_atom(args[0], args[1])
        else:
            assert tp.is_custom_type() or tp.is_enum_type()
            res = yicespy.yices_eq(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_times(self, formula, args, **kwargs):
        res = args[0]
        for x in args[1:]:
            res = yicespy.yices_mul(res, x)
            self._check_term_result(res)
        return res

    def walk_toreal(self, formula, args, **kwargs):
        return args[0]

    def walk_function(self, formula, args, **kwargs):
        name = formula.function_name()
        if name not in self.symbol_to_decl:
            self.declare_variable(name)
        decl = self.symbol_to_decl[name]
        res = yicespy.yices_application(decl, len(args), yicespy.make_term_array(args))
        self._check_term_result(res)
        return res


    def walk_bv_constant(self, formula, **kwargs):
        width = formula.bv_width()
        res = None
        value = formula.constant_value()
        if value <= ((2**63) - 1):
            # we can use the numerical representation
            # Note: yicespy uses *signed* longs in the API, so the maximal
            # representable number is 2^63 - 1
            res = yicespy.yices_bvconst_uint64(width, value)
        else:
            # we must resort to strings to communicate the result to yices
            res = yicespy.yices_parse_bvbin(formula.bv_bin_str())
        self._check_term_result(res)
        return res

    def walk_enum_constant(self, formula, **kwargs):
        sname = str(formula.constant_value())
        tp = formula.constant_type()
        sort_ast = self._type_to_yices(tp)
        assert(sname in self._yicesEnumConsts)
        res = self._yicesEnumConsts[sname]
        return res

    def walk_bv_ult(self, formula, args, **kwargs):
        res = yicespy.yices_bvlt_atom(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_bv_ule(self, formula, args, **kwargs):
        res = yicespy.yices_bvle_atom(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_bv_concat(self, formula, args, **kwargs):
        res = yicespy.yices_bvconcat2(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_bv_extract(self, formula, args, **kwargs):
        res = yicespy.yices_bvextract(args[0],
                                       formula.bv_extract_start(),
                                       formula.bv_extract_end())
        self._check_term_result(res)
        return res

    def walk_bv_or(self, formula, args, **kwargs):
        res = yicespy.yices_bvor2(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_bv_not(self, formula, args, **kwargs):
        res = yicespy.yices_bvnot(args[0])
        self._check_term_result(res)
        return res

    def walk_bv_and(self, formula, args, **kwargs):
        res = yicespy.yices_bvand2(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_bv_xor(self, formula, args, **kwargs):
        res = yicespy.yices_bvxor2(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_bv_add(self, formula, args, **kwargs):
        res = yicespy.yices_bvadd(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_bv_sub(self, formula, args, **kwargs):
        res = yicespy.yices_bvsub(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_bv_neg(self, formula, args, **kwargs):
        res = yicespy.yices_bvneg(args[0])
        self._check_term_result(res)
        return res

    def walk_bv_mul(self, formula, args, **kwargs):
        res = yicespy.yices_bvmul(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_bv_udiv(self, formula, args, **kwargs):
        res = yicespy.yices_bvdiv(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_bv_urem(self, formula, args, **kwargs):
        res = yicespy.yices_bvrem(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_bv_lshl(self, formula, args, **kwargs):
        res = yicespy.yices_bvshl(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_bv_lshr(self, formula, args, **kwargs):
        res = yicespy.yices_bvlshr(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_bv_rol(self, formula, args, **kwargs):
        res = yicespy.yices_rotate_left(args[0], formula.bv_rotation_step())
        self._check_term_result(res)
        return res

    def walk_bv_ror(self, formula, args, **kwargs):
        res = yicespy.yices_rotate_right(args[0], formula.bv_rotation_step())
        self._check_term_result(res)
        return res

    def walk_bv_zext(self, formula, args, **kwargs):
        res = yicespy.yices_zero_extend(args[0], formula.bv_extend_step())
        self._check_term_result(res)
        return res

    def walk_bv_sext (self, formula, args, **kwargs):
        res = yicespy.yices_sign_extend(args[0], formula.bv_extend_step())
        self._check_term_result(res)
        return res

    def walk_bv_slt(self, formula, args, **kwargs):
        res = yicespy.yices_bvslt_atom(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_bv_sle (self, formula, args, **kwargs):
        res = yicespy.yices_bvsle_atom(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_bv_comp (self, formula, args, **kwargs):
        a,b = args
        eq = yicespy.yices_bveq_atom(a, b)
        self._check_term_result(eq)
        one = yicespy.yices_bvconst_int32(1, 1)
        zero = yicespy.yices_bvconst_int32(1, 0)
        res = yicespy.yices_ite(eq, one, zero)
        self._check_term_result(res)
        return res

    def walk_bv_sdiv (self, formula, args, **kwargs):
        res = yicespy.yices_bvsdiv(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_bv_srem (self, formula, args, **kwargs):
        res = yicespy.yices_bvsrem(args[0], args[1])
        self._check_term_result(res)
        return res

    def walk_bv_ashr (self, formula, args, **kwargs):
        res = yicespy.yices_bvashr(args[0], args[1])
        self._check_term_result(res)
        return res

    def yicesSort(self, name):
        """Return the yices Sort for the given name."""
        name = str(name)
        try:
            return self._yicesSort[name]
        except KeyError:
            sort = yicespy.yices_new_uninterpreted_type()
            self._yicesSort[name] = sort
        return sort

    def yicesEnumSort(self, name, value):
        """Return the yices EnumSort for the given name."""
        key = str(name)
        try:
            return self._yicesEnumSort[key][0]
        except KeyError:
            sort = yicespy.yices_new_scalar_type(len(value))
            sortvalues = []
            for i in range(len(value)):
                lhs = str(value[i])
                rhs = yicespy.yices_constant(sort, i)
                yicespy.yices_set_term_name(rhs, lhs)
                sortvalues.append(rhs)
                self._yicesEnumConsts[lhs] = rhs
            self._yicesEnumSort[key] = (sort, sortvalues)
        return sort

    def _type_to_yices(self, tp):
        if tp.is_bool_type():
            return yicespy.yices_bool_type()
        elif tp.is_real_type():
            return yicespy.yices_real_type()
        elif tp.is_int_type():
            return yicespy.yices_int_type()
        elif tp.is_function_type():
            stps = [self._type_to_yices(x) for x in tp.param_types]
            rtp = self._type_to_yices(tp.return_type)
            #arr = (yicespy.type_t * len(stps))(*stps)
            return yicespy.yices_function_type(len(stps),
                                              yicespy.make_type_array(stps),
                                              rtp)
        elif tp.is_bv_type():
            return yicespy.yices_bv_type(tp.width)
        elif tp.is_enum_type():
            return self.yicesEnumSort(tp.name, tp.domain)
        elif tp.is_custom_type():
            return self.yicesSort(str(tp))
        else:
            raise NotImplementedError(tp)

    def declare_variable(self, var):
        if not var.is_symbol():
            raise PysmtTypeError("Trying to declare as a variable something "
                                 "that is not a symbol: %s" % var)
        if var.symbol_name() not in self.symbol_to_decl:
            tp = self._type_to_yices(var.symbol_type())
            decl = yicespy.yices_new_uninterpreted_term(tp)
            yicespy.yices_set_term_name(decl, var.symbol_name())
            self.symbol_to_decl[var] = decl
            self.decl_to_symbol[decl] = var
Example #8
0
 def test_selection_lra(self):
     QuantifierEliminator(logic=LRA)
Example #9
0
 def reset(self):
     self.qesolver = QuantifierEliminator(name='z3')
     self._faxioms = []
     self._axiom_formula = TRUE()
     get_env().fsubstituter.freset()
     self.inferences = []
Example #10
0
class FR(object):
    def __init__(self, system):
        self.system = system
        self.reset()
        self.debug = False

    def init_solver(self):
        solver = Solver(name="bdd", logic=BOOL)
        return solver

    def reset(self):
        self.qesolver = QuantifierEliminator(name='z3')
        self._faxioms = []
        self._axiom_formula = TRUE()
        get_env().fsubstituter.freset()
        self.inferences = []

    def new_solver(self):
        s = self.init_solver()
        formulae = []
        formulae.append(axiom_formula(self))
        formulae.append(trel_formula(self))
        #         formulae = self.get_formulae(formulae, True)
        formulae = self.get_formulae(formulae)
        assert_permanent(s, formulae)
        return s

    def get_qf_form(self, f):
        qf = self.qesolver.eliminate_quantifiers(f).simplify()
        #         print("quantified: \n%s", f.serialize())
        #         print("quantifier-free: \n%s", qf.serialize())
        return qf

    def get_formulae(self, formula, qf=True):
        formulae = formula
        if not isinstance(formulae, list):
            formulae = [formula]
        if qf:
            push_time()
            q_formula = And(formulae)
            qf = self.get_qf_form(q_formula)
            qf_formulae = flatten_cube(qf)
            return qf_formulae
        return formulae

    def check_query(self, solver, formulae=None, timeout=None):
        print("Formulae #%d:" % len(formulae))
        for f in formulae:
            print("\t%s" % f.serialize())
        print()

        res = solver.solve() if formulae == None else solver.solve(formulae)
        return res

    def solve_formula(self, solver, formula, quiet=False):
        """Check whether formula is satisfiable or not"""
        #         print("Formula: %s" % formula.serialize())
        formulae = self.get_formulae(formula)
        push_time()
        res = self.check_query(solver, formulae)
        if res:
            if (not quiet):
                print("-> SAT")
            return True
        else:
            if (not quiet):
                print("-> UNSAT")
            return False

    def get_bdd(self, node):
        bdd_expr = self.converter.convert(node)
        return bdd_expr

    def get_symmetric(self, cl, pol=True):
        cube = cl
        if pol:
            cube = Not(cube)
        cubesOut = symmetry_cube(self, cube, 0, False)
        if pol:
            cubesOutNew = set()
            for cubeSym, complex in cubesOut:
                cubeSymNew = Not(cubeSym)
                cubesOutNew.add((cubeSymNew, complex))
            cubesOut = cubesOutNew
        return cubesOut

    def build_actions(self):
        self.actions = {}
        for f in self.system.curr._actions:
            action = f[0]
            name = f[1]
            if self.system.curr.is_noop(name):
                continue
            eprint(time_str(), "(building bdd for %s)" % name)
            print(time_str(), "(building bdds for %s)" % name)
            if name not in self.actions:
                self.actions[name] = []
            if action.is_exists():
                instances = self.converter._get_children(action)
                queue = []
                for i in instances:
                    bddi = self.converter.convert(i)
                    queue.append(bddi)

                all_vars = set(self.converter.var2node.keys())
                pnabstract = all_vars.difference(self.pvars)
                pnabstract = pnabstract.difference(self.nvars)
                projAll = self.converter.cube_from_var_list(pnabstract)
                for bddq in queue:
                    #                     bddq = self.ddmanager.ExistAbstract(bddq, projAll)
                    self.actions[name].append(bddq)
                print("\t\tfound #%d bdd instances for %s)" %
                      (len(queue), name))
#                 if name == "ext:grant":
#                     for bdd in self.actions[name]:
#                         self.dump_dot(bdd)
#                         assert(0)
            else:
                q = self.converter.convert(action)
                self.actions[name] = [q]

    def build_axioms(self):
        self.axiom = self.converter.typeok
        if axiom_formula(self) != TRUE():
            bddA = self.formula2bdd(axiom_formula(self))
            self.axiom = self.ddmanager.And(self.axiom, bddA)

    def formula2bdd(self, formula, quiet=True):
        f = And(self.get_formulae(formula, False))
        return self.get_bdd(f)

    def set_atoms(self):
        self.patoms = {}
        self.natoms = {}
        self.p2natoms = {}
        formulae = []
        formulae.append(init_formula(self))
        formulae.append(trel_formula(self))
        formulae.append(axiom_formula(self))
        formulae.append(prop_formula(self))

        formula = And(formulae)
        formula = And(self.get_formulae(formula))
        #         atoms = formula.get_atoms()
        atoms = self.converter.atom2var.keys()
        for p in atoms:
            vars = p.get_free_variables()
            ovars = vars.difference(self.system.curr._states)
            if len(ovars) == 0:
                #             nvars = vars.intersection(self.system.curr._nex2pre.keys())
                #             if len(nvars) == 0:
                bddp = self.get_bdd(p)
                self.patoms[p] = bddp
                print("adding pre: %s with bdd %s" % (p, bddp.NodeReadIndex()))
                n = pre2nex(self, p)
                bddn = bddp
                if n != p:
                    bddn = self.get_bdd(n)
                self.natoms[n] = bddn
#                 print("adding nex: %s with bdd %s" % (n, bddn))

    def add_bddSupport(self, bdd, support):
        ps = self.ddmanager.Support(bdd)
        psA = repycudd.IntArray(self.converter.numvars)
        self.ddmanager.BddToCubeArray(ps, psA)
        for i in range(len(psA)):
            if psA[i] == 0 or psA[i] == 1:
                var = self.converter.idx2var[i]
                support.add(var)

    def set_bddvars(self):
        self.pvars = set()
        self.nvars = set()
        eprint("\t(#%d variables)" % self.converter.numvars)
        print("\t(#%d variables)" % self.converter.numvars)
        for p in self.patoms:
            self.pvars.add(self.converter.atom2var[p])
#             bdd = self.patoms[p]
#             self.add_bddSupport(bdd, self.pvars)
        for n in self.natoms:
            self.nvars.add(self.converter.atom2var[n])
#             bdd = self.natoms[n]
#             self.add_bddSupport(bdd, self.nvars)
#         print("pvars: %s" % self.pvars)
#         print("nvars: %s" % self.nvars)

    def set_p2nVars(self):
        self.p2nvars = {}
        for p in self.patoms:
            n = pre2nex(self, p)
            if n != p:
                pvar = self.converter.atom2var[p]
                nvar = self.converter.atom2var[n]
                self.p2nvars[pvar] = nvar
        pv = set(self.p2nvars.keys())
        nv = set(self.p2nvars.values())
        diff = pv.intersection(nv)
        if len(diff) != 0:
            print(diff)
            assert (0)

    def header_espresso(self):
        self.espresso_in = []
        res = ""
        for idx, var in self.converter.idx2var.items():
            if var in self.converter.var2atom:
                atom = self.converter.var2atom[var]
                self.espresso_in.append(atom)
                res += str(atom).replace(" ", "") + " "
            else:
                atom = Symbol("v%d" % idx)
                self.espresso_in.append(atom)
                res += str(atom) + " "
                print("Found ")
                assert (0)
        return res

    def print_espresso(self, bdd, restricted, name):
        global outF
        outF = open(name + ".pla", "w")

        str_head = ".ilb "
        self.espresso_head = []
        abvars = set(self.converter.var2atom.keys())
        atomList = []
        for idx in range(self.numvars):
            var = self.converter.idx2var[idx]
            atom = self.converter.var2atom[var]
            atomList.append(atom)
            if atom in restricted:
                self.espresso_head.append(atom)
                abvars.remove(var)

        for atom in self.espresso_head:
            str_head += pretty_serialize(atom).replace(" ", "") + " "
        fprint(".i %d" % len(self.espresso_head))
        fprint(".o 1")
        fprint(str_head)
        fprint(".ob out")
        fprint(".phase 0")
        abcube = self.converter.cube_from_var_list(list(abvars))
        #         bddnew = bdd
        bddnew = self.ddmanager.ExistAbstract(bdd, abcube)
        print("\t(printing pla)")
        for cube_tup in repycudd.ForeachCubeIterator(self.ddmanager, bddnew):
            str_cube = ""
            for idx, char in enumerate(cube_tup):
                if idx >= self.numvars:
                    break
                atom = atomList[idx]
                #                 var = self.converter.idx2var[idx]
                #                 atom = self.converter.var2atom[var]
                if atom in restricted:
                    if char == 2:
                        str_cube += '-'
                    else:
                        str_cube += str(char)
            str_cube += " 1"
            fprint(str_cube)
        fprint(".e")
        outF.close()

    def execute_espresso(self, bdd, restricted, mode="exact"):
        global outF
        name = "%s/espresso_in" % (common.gopts.out)
        self.print_espresso(bdd, restricted, name)
        cmd = "exec ./utils/espresso/espresso.linux"
        if mode == "exact":
            cmd += " -D exact -o kiss %s.pla" % name
            eprint("\t(running espresso in exact mode)")
            print("\t(running espresso in exact mode)")
        elif mode == "primes":
            cmd += " -D primes -o kiss %s.pla" % name
            eprint("\t(running espresso in primes mode)")
            print("\t(running espresso in primes mode)")
        else:
            cmd += " -e fast -o kiss %s.pla" % name
            eprint("\t(running espresso in fast mode)")
            print("\t(running espresso in fast mode)")
        print(cmd)
        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
        (out, err) = proc.communicate()

        if err != None:
            print("espresso err: %s" % err)
            assert (0)
        output = out.split(b'\n')
        newCubes = []
        outName = "%s/" % (common.gopts.out)
        if mode == "exact":
            outName += "espresso_out_exact.pla"
        elif mode == "primes":
            outName += "espresso_out_primes.pla"
        else:
            outName += "espresso_out_fast.pla"
        outF = open(outName, "w")
        str_head = ".ilb "
        for atom in self.espresso_head:
            str_head += pretty_serialize(atom).replace(" ", "") + " "
        fprint(".i %d" % len(self.espresso_head))
        fprint(".o 1")
        fprint(str_head)
        fprint(".ob out")
        for l in output:
            if l != "":
                fprint(l)
                cube = []
                cubeMap = {}
                for i in range(len(self.espresso_head)):
                    atom = self.espresso_head[i]
                    val = l[i]
                    if val == "0":
                        cubeMap[atom] = 0
                        cube.append(Not(atom))
                    elif val == "1":
                        cubeMap[atom] = 1
                        cube.append(atom)
                newCubes.append((And(cube), cubeMap, l))
#                 print("%s -> %s" % (l, And(cube)))
        fprint(".e")
        outF.close()
        if len(newCubes) == 0:
            print("No cube in espresso output.")
            print("Something went wrong with espresso (probably memout).")
            assert (0)

        return newCubes

    def bdd2atoms(self, bdd, atomset=None):
        psA = repycudd.IntArray(self.converter.numvars)
        self.ddmanager.BddToCubeArray(bdd, psA)
        atoms = []
        atomval = {}
        for i in range(len(psA)):
            if psA[i] == 0 or psA[i] == 1:
                var = self.converter.idx2var[i]
                if var in self.converter.var2atom:
                    atom = self.converter.var2atom[var]
                    #                     print("%d -> %s" % (i, atom))
                    if atomset != None and atom not in atomset:
                        continue
                    if psA[i] == 0:
                        atomval[atom] = 0
                        atom = Not(atom)
                    else:
                        atomval[atom] = 1
                    atoms.append(atom)
        return (And(atoms), atomval)

    def extract_pcubes(self, bdd, prefix="Cube"):
        cubes = self.extract_cubes(bdd, self.patoms)
        print("%s: #%d" % (prefix, len(cubes)))
        if len(cubes) < 500:
            for cube, cubeMap in cubes:
                print(cube)
        return cubes

    def extract_ncubes(self, bdd, prefix="Cube"):
        cubes = self.extract_cubes(bdd, self.natoms)
        print("%s: #%d" % (prefix, len(cubes)))
        if len(cubes) < 100:
            for cube, cubeMap in cubes:
                print(cube)
        return cubes

    def extract_cubes(self, bdd, allowed):
        cubes = []
        for cube in repycudd.ForeachCubeIterator(self.ddmanager, bdd):
            atoms = []
            atomval = {}
            for i in range(self.numvars):
                lit = cube[i]
                if lit == 0 or lit == 1:
                    var = self.converter.idx2var[i]
                    if var in self.converter.var2atom:
                        atom = self.converter.var2atom[var]
                        #                     print("%d -> %s" % (i, atom))
                        if atom not in allowed:
                            continue
                        if lit == 0:
                            atomval[atom] = 0
                            atom = Not(atom)
                        else:
                            atomval[atom] = 1
                        atoms.append(atom)
            cubeNew = (And(atoms), atomval)
            cubes.append(cubeNew)
        return cubes

    def dump_dot(self, bdd):
        add = self.ddmanager.BddToAdd(bdd)
        self.ddmanager.DumpDot(add)

        idx2atom = {}
        for idx in range(self.numvars):
            var = self.converter.idx2var[idx]
            atom = self.converter.var2atom[var]
            k = "\" %d \"" % idx
            idx2atom[k] = "\" %s \"" % pretty_serialize(atom)
        inF = open('out.dot', 'r')
        str_dot = inF.read()
        inF.close()
        global outF
        outF = open("out.dot", "w")
        for k, v in idx2atom.items():
            str_dot = str_dot.replace(k, v)
        fprint(str_dot)

    def dump_blif(self, bdd):
        add = self.ddmanager.BddToAdd(bdd)
        self.ddmanager.DumpBlif(add)

    def func2inst(self, f, ft):
        if ft.is_function_type():
            args = []
            i = 0
            for paramt in ft.param_types:
                i += 1
                paramt_name = str(i) + ":" + str(paramt)
                args.append(Symbol(paramt_name, paramt))
            fi = Function(f, args)
            return fi
        else:
            return f

    def instantiate_function(self, f, ft, fi):
        #         print("processing: %s" % f)
        instantiated = True
        if ft.is_function_type():
            #             for idx, paramt in enumerate(ft.param_types):
            for idx in range(len(ft.param_types) - 1, -1, -1):
                paramt = ft.param_types[idx]
                assert paramt.is_enum_type()
                arg = f.arg(idx)
                if not arg.is_enum_constant():
                    instantiated = False
                    dom = [Enum(d, paramt) for d in paramt.domain]
                    for d in reversed(dom):
                        subs = {}
                        subs[arg] = d
                        fn = f.substitute(subs)
                        #                         print("%s to %s" % (f, fn))
                        self.instantiate_function(fn, ft, fi)

        if instantiated:
            rt = ft
            if ft.is_function_type():
                rt = ft.return_type
            if rt.is_enum_type():
                dom = [Enum(d, rt) for d in rt.domain]
                for d in dom:
                    eq = EqualsOrIff(f, d)
                    if eq not in fi:
                        fi.append(eq)
            elif rt == BOOL:
                if f not in fi:
                    fi.append(f)
            else:
                assert (0)

    def initialize_atoms(self):
        self.atoms = []
        self.patoms = {}
        self.natoms = {}
        self.gatoms = {}
        self.p2natoms = {}
        self.pvars = set()
        self.nvars = set()
        self.converter.pre2nex = self.system.curr._pre2nex

        states_ordered = {}
        for s in self.system.curr._states:
            currSign = 0
            prefix = "2_"
            st = s.symbol_type()
            if st.is_function_type():
                currSign += len(st.param_types)
                st = st.return_type
            if st.is_enum_type():
                prefix = "1_"
            if s in self.system.curr._globals:
                prefix = "0_"
            states_ordered[prefix + str(currSign) + str(s)] = s

        states = sorted(states_ordered.items(), key=lambda v: v, reverse=True)
        #         print(states)
        for nf, f in states:
            print("processing %s" % pretty_serialize(f))
            if self.converter.zero != None and str(f).startswith("zero"):
                print("skipping %s" % pretty_serialize(f))
                continue
            ft = f.symbol_type()
            fi = []
            self.instantiate_function(self.func2inst(f, ft), ft, fi)
            #             print(fi)

            for atom in fi:
                self.atoms.append(atom)
                natom = pre2nex(self, atom)
                self.p2natoms[atom] = natom
                if atom != natom:
                    self.atoms.append(natom)

                bdd = self.get_bdd(atom)
                self.patoms[atom] = bdd
                var = self.converter.atom2var[atom]
                self.pvars.add(var)
                #                 self.add_bddSupport(bdd, self.pvars)
                print("adding pre: %s <-> %s := %d" %
                      (pretty_serialize(atom), var, bdd.NodeReadIndex()))

                if atom == natom:
                    self.gatoms[atom] = bdd

                bdd = self.get_bdd(natom)
                self.natoms[natom] = bdd
                var = self.converter.atom2var[natom]
                self.nvars.add(var)
#                 print("adding nex: %s <-> %s := %d" % (natom, var, bdd.NodeReadIndex()))

#                 self.add_bddSupport(bdd, self.nvars)
        eprint("\t(#%d atoms with #%d variables)" %
               (len(self.atoms), self.converter.numvars))
        print("\t(#%d atoms with #%d variables)" %
              (len(self.atoms), self.converter.numvars))
        #         print(self.atoms)
        #         print(self.patoms)
        #         print(self.natoms)
        #         assert(0)
        self.set_p2nVars()
        #         self.check_typeok()
        self.print_atoms()
        self.converter.pnset = True
        self.numvars = self.converter.numvars

    def print_atoms(self):
        for i in range(self.converter.numvars):
            var = self.converter.idx2var[i]
            if var in self.converter.var2atom:
                atom = self.converter.var2atom[var]
                print("%d -> %s" % (i, pretty_serialize(atom)))
            else:
                print("%d has no var" % (i))

    def check_typeok(self):
        print("#typeok = %d" % len(self.converter.typeconst))
#         self.print_atoms()
#         for idx, t in enumerate(self.converter.typeconst):
#             if idx == 2:
#                 bdd = self.converter.typeok
#                 self.dump_dot(bdd)
#                 self.ddmanager.PrintMinterm(bdd)
#                 assert(0)

    def set_abstract(self):
        all_vars = set(self.converter.var2node.keys())
        pabstract = all_vars.difference(self.pvars)
        nabstract = all_vars.difference(self.nvars)
        self.projPre = self.converter.cube_from_var_list(pabstract)
        self.projNex = self.converter.cube_from_var_list(nabstract)

        pnabstract = all_vars.difference(self.pvars)
        pnabstract = pnabstract.difference(self.nvars)
        self.projAll = self.converter.cube_from_var_list(pnabstract)

        self.N = len(self.p2nvars)
        self.preV = repycudd.DdArray(self.ddmanager, self.N)
        self.nexV = repycudd.DdArray(self.ddmanager, self.N)
        count = 0
        for pv, nv in self.p2nvars.items():
            self.preV[count] = self.converter.var2node[pv]
            self.nexV[count] = self.converter.var2node[nv]
            assert (self.preV[count] != self.nexV[count])
            count += 1

    def print_pla(self, bddI, bddT):
        self.print_espresso(bddI, self.patoms, gopts.out + "/init")
        #         bddT = self.axiom
        #         for action, actionBdds in self.actions.items():
        #             for actionBdd in actionBdds:
        #                 bddT = self.ddmanager.Or(bddT, actionBdd)
        allowed = self.patoms
        for k, v in self.natoms.items():
            allowed[k] = v
        self.dump_dot(bddT)
        self.print_espresso(bddT, allowed, gopts.out + "/trel_formula")
#         allowed = self.patoms
#         for lhs, rhs in self.natoms.items():
#             if lhs not in allowed:
#                 allowed[lhs] = rhs
#                 name = str(lhs)
#                 self.print_espresso(bddT, allowed, name)
#                 del allowed[lhs]

    def experiment(self, bdd):
        ab_vars = set(self.converter.var2node.keys())
        allowed_atoms = set()
        allowed_atoms.add("__semaphore(s0)")
        #         allowed_atoms.add("__semaphore(s1)")
        #         allowed_atoms.add("__semaphore(s2)")
        allowed_atoms.add("__link(c0, s0)")
        #         allowed_atoms.add("__link(c1, s0)")
        for atom in self.patoms.keys():
            if str(atom) in allowed_atoms:
                var = self.converter.atom2var[atom]
                ab_vars.discard(var)
        proj = self.converter.cube_from_var_list(ab_vars)

        bddNew = self.ddmanager.ExistAbstract(bdd, proj)
        self.dump_dot(bddNew)
        assert (0)

    def check_safe(self, bdd):
        bddC = self.ddmanager.And(bdd, self.bddnotP)
        if bddC != self.ddmanager.ReadLogicZero():
            print("-- Finite check: violated --")
            #             bddC = self.ddmanager.ExistAbstract(bddC, self.projPre)
            self.dump_dot(bddC)
            assert (0)
        else:
            print("-- Finite check: safe --")

    def execute(self):
        """Forward Reachability using BDDs."""
        prop = prop_formula(self)
        print("\nChecking property %s...\n" % prop)

        self.ddmanager = repycudd.DdManager()
        self.converter = BddConverter(environment=get_env(),
                                      ddmanager=self.ddmanager)
        #         self.ddmanager.AutodynDisable()

        for k, v in self.system._enumsorts.items():
            if str(k).startswith("epoch"):
                self.converter.zero = v[0]
                break

        self.initialize_atoms()

        eprint(time_str(), "(building bdds)")
        bddI = self.formula2bdd(init_formula(self))
        #         pathCount = bddI.CountPathsToNonZero()
        #         print("Found %d paths in init" % pathCount)
        #         self.dump_dot(bddI)
        #         assert(0)

        self.build_actions()
        self.build_axioms()

        #         bddT = self.formula2bdd(trel_formula(self))
        #         eprint(time_str(), "(building bdd for T done)")
        # #         self.dump_dot(bddT)
        # #         assert(0)
        #
        #         if axiom_formula(self) != TRUE():
        #             bddA = self.formula2bdd(axiom_formula(self))
        # #             self.dump_dot(bddA)
        # #             assert(0)
        #             bddT = self.ddmanager.And(bddT, bddA)
        # #             pathCount = bddT.CountPathsToNonZero()
        # #             print("Found %d paths in trel /\ axioms" % pathCount)
        #
        #         self.print_pla(bddI, bddT)
        #         assert(0)

        #         bddP = self.formula2bdd(prop_formula(self))
        #         pathCount = bddP.CountPathsToNonZero()
        #         print("Found %d paths in prop" % pathCount)

        #         self.extract_pcubes(bddI, "Init")
        #         self.extract_pcubes(bddT, "Trel")
        #         self.extract_pcubes(bddA, "Axiom")
        #         self.extract_pcubes(bddP, "Property")

        #         self.set_atoms()
        #         self.set_bddvars()
        #         self.set_p2nVars()

        self.bddP = self.formula2bdd(prop_formula(self))
        self.bddnotP = self.ddmanager.Not(self.bddP)
        #         self.dump_dot(self.bddP)
        #         self.execute_espresso(self.bddP, self.patoms, True)
        #         assert(0)

        self.set_abstract()

        sources = list()
        initSrc = self.ddmanager.AndAbstract(bddI, self.axiom, self.projPre)
        totalR = initSrc
        sources.append((initSrc, "init"))
        iteration = 0
        eprint("\t(running forward reachability)")
        while (len(sources) != 0):
            #             print("#sources = %d" % len(sources))
            src, comment = sources.pop()
            iteration += 1

            if src == self.ddmanager.ReadLogicZero():
                print("#%d Found no new states" % iteration)
                continue
            else:
                print("#%d Found #%d new states: %s" %
                      (iteration, len(sources) + 1, comment))
            self.check_safe(src)

            #                 src = self.ddmanager.And(src, self.axiom)
            notTotalR = self.ddmanager.Not(totalR)

            destinations = []
            for action, actionBdds in self.actions.items():
                nex = self.ddmanager.Zero()
                done = False
                for actionBdd in actionBdds:
                    image = self.ddmanager.AndAbstract(src, actionBdd,
                                                       self.projNex)
                    if image == self.ddmanager.ReadLogicZero(): continue
                    image = self.ddmanager.SwapVariables(
                        image, self.preV, self.nexV, self.N)
                    image = self.ddmanager.AndAbstract(image, self.axiom,
                                                       self.projPre)
                    if image == self.ddmanager.ReadLogicZero(): continue
                    image = self.ddmanager.And(image, notTotalR)
                    if image == self.ddmanager.ReadLogicZero(): continue
                    nex = self.ddmanager.Or(nex, image)
                    done = True
#                     print("found a state in %s" % action)
#                         break
                if done:
                    destinations.append((nex, action))

            for dest, comment in destinations:
                sources.append((dest, comment))
                totalR = self.ddmanager.Or(totalR, dest)

#         eprint("\t(found total #%d paths)" % totalPathCount)
#         print("\t(found total #%d paths)" % totalPathCount)

#         print("Reachable states:")
#         self.ddmanager.PrintMinterm(totalR)

        totalR = self.ddmanager.ExistAbstract(totalR, self.projPre)

        if self.converter.zero != None:
            proj_vars = set(self.converter.var2node.keys())
            proj_vars = proj_vars.difference(self.pvars)
            for atom in self.gatoms.keys():
                enumc = atom.get_enum_constants()
                if self.converter.zero in enumc:
                    var = self.converter.atom2var[atom]
                    proj_vars.add(var)
                    self.patoms.pop(atom)
            projCustom = self.converter.cube_from_var_list(proj_vars)
            totalR = self.ddmanager.ExistAbstract(totalR, projCustom)

        self.dump_dot(totalR)

        #         self.experiment(totalR)

        #         assert(0)
        eprint("\t(forward reachability done)")
        print("\t(forward reachability done)")
        self.check_safe(totalR)

        notCubes_fast = self.execute_espresso(totalR, self.patoms, "fast")
        notCubes = notCubes_fast
        #         notCubes_primes = self.execute_espresso(totalR, self.patoms, "primes")
        #         notCubes = notCubes_primes
        #         notCubes_exact = self.execute_espresso(totalR, self.patoms, "exact")
        #         notCubes = notCubes_exact
        symCubes = set()
        eprint("\t(invoking symmetry on #%d)" % len(notCubes))
        print("\t(invoking symmetry on #%d)" % len(notCubes))
        for cube, cubeMap, l in notCubes:
            print("%s i.e. " % l, end='')
            pretty_print(cube)
            cubesOut = symmetry_cube(self, cube, 0, False)
            assert (len(cubesOut) == 1)
            for cubeSym, _ in cubesOut:
                symCubes.add(cubeSym)
                print("\t", end="")
                pretty_print(Not(cubeSym))


#         print("Symmetric notR: #%d" % len(symCubes))
        for idx, cubeSym in enumerate(symCubes):
            label = "frpo%d" % str(len(self.inferences) + 1)
            clause = Not(cubeSym)
            self.inferences.append((label, clause))
        pretty_print_inv(self.inferences, "Forward inferences")
        return self.inferences