Exemple #1
0
    def admit_definition(self, defn, proof=None):
        """ Admits a definition if it is non-recursive or match a definition schema. 
            If a proof is given it is used to match the definition to a schema, else
            default heuristic matching is used.
        
        - defn is an ivy_ast.LabeledFormula
        """

        sym = defn.formula.defines()
        if sym in self.definitions:
            raise Redefinition(defn, "redefinition of {}".format(sym))
        if sym in self.deps:
            raise Circular(defn,
                           "symbol {} defined after reference".format(sym))
        deps = list(lu.symbols_ast(defn.formula.rhs()))
        self.deps.update(deps)
        if sym in deps:
            # Recursive definitions must match a schema
            if proof is None:
                raise NoMatch(defn, "no proof given for recursive definition")
            subgoals = self.match_schema(defn.formula, proof)
            if subgoals is None:
                raise NoMatch(
                    defn,
                    "recursive definition does not match the given schema")
        else:
            subgoals = []
        self.definitions[sym] = defn
Exemple #2
0
def get_assumes_and_asserts():    
    assumes = []
    asserts = []
    macros = []
    for name,action in im.module.actions.iteritems():
        for sa in action.iter_subactions():
            if isinstance(sa,ia.AssumeAction):
                assumes.append((sa.args[0],sa))
            if isinstance(sa,ia.AssertAction):
                asserts.append((sa.args[0],sa))
            if isinstance(sa,ia.IfAction):
                asserts.append((sa.get_cond(),sa))

    for ldf in im.module.definitions:
        if ldf.formula.defines() not in ilu.symbols_ast(ldf.formula.rhs()):
            macros.append((ldf.formula.to_constraint(),ldf))
        else: # can't treat recursive definition as macro
            assumes.append((ldf.formula.to_constraint(),ldf))

    for ldf in im.module.labeled_axioms:
        assumes.append((ldf.formula,ldf))

    for ldf in im.module.labeled_props:
        asserts.append((ldf.formula,ldf))

    # TODO: check axioms, inits, conjectures

    return assumes,asserts,macros
Exemple #3
0
    def admit_definition(self,defn,proof=None):
        """ Admits a definition if it is non-recursive or match a definition schema. 
            If a proof is given it is used to match the definition to a schema, else
            default heuristic matching is used.
        
        - defn is an ivy_ast.LabeledFormula
        """

        sym = defn.formula.defines()
        if sym in self.definitions:
            raise Redefinition(defn,"redefinition of {}".format(sym))
        if sym in self.deps:
            raise Circular(defn,"symbol {} defined after reference".format(sym))
        deps = list(lu.symbols_ast(defn.formula.rhs()))
        self.deps.update(deps)
        if sym in deps:
            # Recursive definitions must match a schema
            if proof is None:
                raise NoMatch(defn,"no proof given for recursive definition")
            subgoals = self.match_schema(defn.formula,proof)
            if subgoals is None:
                raise NoMatch(defn,"recursive definition does not match the given schema")
        else:
            subgoals = []
        self.definitions[sym] = defn
Exemple #4
0
def relevant_definitions(symbols):
    dfn_map = dict((ldf.formula.defines(), ldf.formula.args[1])
                   for ldf in module.definitions)
    rch = set(
        iu.reachable(
            symbols, lambda sym: lu.symbols_ast(dfn_map[sym])
            if sym in dfn_map else []))
    return [ldf for ldf in module.definitions if ldf.formula.defines() in rch]
def prev_expr(stvarset,expr,sort_constants):
    if any(sym in stvarset or tr.is_skolem(sym) and not sym in sort_constants[sym.sort]
           for sym in ilu.symbols_ast(expr)):
        return None
    news = [sym for sym in ilu.used_symbols_ast(expr) if tr.is_new(sym)]
    if news:
        rn = dict((sym,tr.new_of(sym)) for sym in news)
        return ilu.rename_ast(expr,rn)
    return None        
Exemple #6
0
def get_assumes_and_asserts(preconds_only):
    assumes = []
    asserts = []
    macros = []
    #    for name,action in im.module.actions.iteritems():
    # for sa in action.iter_subactions():
    #     if isinstance(sa,ia.AssumeAction):
    #         assumes.append((sa.args[0],sa))
    #     if isinstance(sa,ia.AssertAction):
    #         asserts.append((sa.args[0],sa))
    #     if isinstance(sa,ia.IfAction):
    #         asserts.append((sa.get_cond(),sa))
    if preconds_only:
        for name in im.module.before_export:
            action = im.module.before_export[name]
            triple = action.update(im.module, [])
            foo = ilu.close_epr(ilu.clauses_to_formula(triple[1]))
            assumes.append((foo, action))
    else:
        for name in im.module.public_actions:
            action = im.module.actions[name]
            triple = action.update(im.module, [])
            #        print 'ivy_theory.py: triple[1]: {}'.format(triple[1])
            foo = ilu.close_epr(ilu.clauses_to_formula(triple[1]))
            #       print 'ivy_theory.py: foo (1): {}'.format(foo)
            assumes.append((foo, action))
            #        print 'ivy_theory.py: triple[2]: {}'.format(triple[2])
            foo = ilu.close_epr(ilu.clauses_to_formula(triple[2]))
            #        print 'ivy_theory.py: foo (2): {}'.format(foo)
            assumes.append((foo, action))

    for ldf in im.module.definitions:
        if ldf.formula.defines() not in ilu.symbols_ast(ldf.formula.rhs()):
            macros.append((ldf.formula.to_constraint(), ldf))
        else:  # can't treat recursive definition as macro
            assumes.append((ldf.formula.to_constraint(), ldf))

    for ldf in im.module.labeled_axioms:
        if not ldf.temporal:
            assumes.append((ldf.formula, ldf))

    for ldf in im.module.labeled_props:
        if not ldf.temporal:
            asserts.append((ldf.formula, ldf))

    for ldf in im.module.labeled_conjs:
        asserts.append((ldf.formula, ldf))
        assumes.append((ldf.formula, ldf))
    # TODO: check axioms, inits, conjectures

    return assumes, asserts, macros
Exemple #7
0
def get_assumes_and_asserts(preconds_only):    
    assumes = []
    asserts = []
    macros = []
#    for name,action in im.module.actions.iteritems():
        # for sa in action.iter_subactions():
        #     if isinstance(sa,ia.AssumeAction):
        #         assumes.append((sa.args[0],sa))
        #     if isinstance(sa,ia.AssertAction):
        #         asserts.append((sa.args[0],sa))
        #     if isinstance(sa,ia.IfAction):
        #         asserts.append((sa.get_cond(),sa))
    if preconds_only:
        for name in im.module.before_export:
            action = im.module.before_export[name]
            triple = action.update(im.module,[])
            foo = ilu.close_epr(ilu.clauses_to_formula(triple[1]))
            assumes.append((foo,action))
    else:
        for name in im.module.public_actions:
            action = im.module.actions[name]
            triple = action.update(im.module,[])
            #        print 'ivy_theory.py: triple[1]: {}'.format(triple[1])
            foo = ilu.close_epr(ilu.clauses_to_formula(triple[1]))
            #       print 'ivy_theory.py: foo (1): {}'.format(foo)
            assumes.append((foo,action))
            #        print 'ivy_theory.py: triple[2]: {}'.format(triple[2])
            foo = ilu.close_epr(ilu.clauses_to_formula(triple[2]))
            #        print 'ivy_theory.py: foo (2): {}'.format(foo)
            assumes.append((foo,action))
        
    for ldf in im.module.definitions:
        if ldf.formula.defines() not in ilu.symbols_ast(ldf.formula.rhs()):
            macros.append((ldf.formula.to_constraint(),ldf))
        else: # can't treat recursive definition as macro
            assumes.append((ldf.formula.to_constraint(),ldf))

    for ldf in im.module.labeled_axioms:
        if not ldf.temporal:
            assumes.append((ldf.formula,ldf))

    for ldf in im.module.labeled_props:
        if not ldf.temporal:
            asserts.append((ldf.formula,ldf))

    for ldf in im.module.labeled_conjs:
        asserts.append((ldf.formula,ldf))
        assumes.append((ldf.formula,ldf))
    # TODO: check axioms, inits, conjectures

    return assumes,asserts,macros
Exemple #8
0
def get_assumes_and_asserts(preconds_only):
    assumes = []
    asserts = []
    macros = []
    #    for name,action in im.module.actions.iteritems():
    # for sa in action.iter_subactions():
    #     if isinstance(sa,ia.AssumeAction):
    #         assumes.append((sa.args[0],sa))
    #     if isinstance(sa,ia.AssertAction):
    #         asserts.append((sa.args[0],sa))
    #     if isinstance(sa,ia.IfAction):
    #         asserts.append((sa.get_cond(),sa))
    if preconds_only:
        for name in im.module.before_export:
            action = im.module.before_export[name]
            triple = action.update(im.module, [])
            foo = ilu.close_epr(ilu.clauses_to_formula(triple[1]))
            assumes.append((foo, action))
    else:
        for name in im.module.public_actions:
            action = im.module.actions[name]
            triple = action.update(im.module, [])
            #        print 'ivy_theory.py: triple[1]: {}'.format(triple[1])
            foo = ilu.close_epr(ilu.clauses_to_formula(triple[1]))
            #       print 'ivy_theory.py: foo (1): {}'.format(foo)
            assumes.append((foo, action))
            #        print 'ivy_theory.py: triple[2]: {}'.format(triple[2])
            foo = ilu.close_epr(ilu.clauses_to_formula(triple[2]))
            #        print 'ivy_theory.py: foo (2): {}'.format(foo)
            assumes.append((foo, action))

    for ldf in im.module.definitions:
        if not isinstance(ldf.formula, il.DefinitionSchema):
            if (ldf.formula.defines() not in ilu.symbols_ast(ldf.formula.rhs())
                    and not isinstance(ldf.formula.rhs(), il.Some)):
                # print 'macro : {}'.format(ldf.formula)
                macros.append((ldf.formula, ldf))
            else:  # can't treat recursive definition as macro
                # print 'axiom : {}'.format(ldf.formula)
                assumes.append((ldf.formula.to_constraint(), ldf))

    for ldf in im.module.labeled_axioms:
        if not ldf.temporal:
            # print 'axiom : {}'.format(ldf.formula)
            assumes.append((ldf.formula, ldf))

    pfs = set(lf.id for lf, p in im.module.proofs)
    sgs = set(x.id for x, y in im.module.subgoals)
    for ldf in im.module.labeled_props:
        if not ldf.temporal:
            # print 'prop : {}{} {}'.format(ldf.lineno,ldf.label,ldf.formula)
            if ldf.id not in pfs:
                asserts.append((ldf.formula, ldf))
            elif ldf.id in sgs and not ldf.explicit:
                assumes.append((ldf.formula, ldf))

    for ldf in im.module.labeled_conjs:
        asserts.append((ldf.formula, ldf))
        assumes.append((ldf.formula, ldf))

    for ldf in im.module.assumed_invariants:
        if not ldf.explicit:
            assumes.append((ldf.formula, ldf))

    # TODO: check axioms, inits, conjectures

    # for x in assumes:
    #     print 'assume: {}'.format(x[0])
    # for x in asserts:
    #     print 'assert: {}'.format(x[0])
    # for x in macros:
    #     print 'macro: {}'.format(x[0])
    return assumes, asserts, macros
def l2s_tactic(prover, goals, proof):
    mod = im.module
    goal = goals[0]  # pick up the first proof goal
    lineno = goal.lineno
    conc = ipr.goal_conc(goal)  # get its conclusion
    if not isinstance(conc, itm.TemporalModels):
        raise iu.IvyError(proof, 'proof goal is not temporal')
    model = conc.model.clone([])
    fmla = conc.fmla

    # Get all the temporal properties from the prover environment as assumptions

    # Add all the assumed invariants to the model

    assumed_gprops = [
        x for x in prover.axioms if not x.explicit and x.temporal
    ]
    model.asms.extend(
        [p.clone([p.label, p.formula.args[0]]) for p in assumed_gprops])

    # TRICKY: We postpone compiling formulas in the tactic until now, so
    # that tactics can introduce their own symbols. But, this means that the
    # tactic has to be given an appropriate environment label for any temporal
    # operators. Here, we compile the invariants in the tactic, using the given
    # label.

    assert hasattr(proof, 'labels') and len(proof.labels) == 1
    proof_label = proof.labels[0]
    #    print 'proof label: {}'.format(proof_label)
    invars = [
        ilg.label_temporal(inv.compile(), proof_label)
        for inv in proof.tactic_decls
    ]

    l2s_waiting = lg.Const('l2s_waiting', lg.Boolean)
    l2s_frozen = lg.Const('l2s_frozen', lg.Boolean)
    l2s_saved = lg.Const('l2s_saved', lg.Boolean)
    l2s_d = lambda sort: lg.Const('l2s_d', lg.FunctionSort(sort, lg.Boolean))
    l2s_a = lambda sort: lg.Const('l2s_a', lg.FunctionSort(sort, lg.Boolean))
    l2s_w = lambda vs, t: lg.NamedBinder('l2s_w', vs, proof_label, t)
    l2s_s = lambda vs, t: lg.NamedBinder('l2s_s', vs, proof_label, t)
    l2s_g = lambda vs, t, environ: lg.NamedBinder('l2s_g', vs, environ, t)
    old_l2s_g = lambda vs, t, environ: lg.NamedBinder('_old_l2s_g', vs,
                                                      environ, t)

    # Desugar the invariants.
    #
    # $was. phi(V)  -->   l2s_saved & ($l2s_s V.phi(V))(V)
    # $happened. phi --> l2s_saved & ~($l2s_w V.phi(V))(V)
    #
    # We push $l2s_s inside propositional connectives, so that the saved
    # values correspond to atoms. Otherwise, we would have redundant
    # saved values, for example p(X) and ~p(X).

    def desugar(expr):
        def apply_was(expr):
            if isinstance(expr, (lg.And, lg.Or, lg.Not, lg.Implies, lg.Iff)):
                return expr.clone([apply_was(a) for a in expr.args])
            vs = list(iu.unique(ilu.variables_ast(expr)))
            return l2s_s(vs, expr)(*vs)

        def apply_happened(expr):
            vs = list(iu.unique(ilu.variables_ast(expr)))
            return lg.Not(l2s_w(vs, expr)(*vs))

        if ilg.is_named_binder(expr):
            if expr.name == 'was':
                if len(expr.variables) > 0:
                    raise iu.IvyError(
                        expr, "operator 'was' does not take parameters")
                return lg.And(l2s_saved, apply_was(expr.body))
            elif expr.name == 'happened':
                if len(expr.variables) > 0:
                    raise iu.IvyError(
                        expr, "operator 'happened' does not take parameters")
                return lg.And(l2s_saved, apply_happened(expr.body))
        return expr.clone([desugar(a) for a in expr.args])

    invars = map(desugar, invars)

    # Add the invariant phi to the list. TODO: maybe, if it is a G prop
    # invars.append(ipr.clone_goal(goal,[],invar))

    # Add the invariant list to the model
    model.invars = model.invars + invars

    # for inv in invars:
    #     print inv
    #     for b in ilu.named_binders_ast(inv):
    #         print 'orig binder: {} {} {}'.format(b.name,b.environ,b.body)

    # model pass helper funciton
    def mod_pass(transform):
        model.invars = [transform(x) for x in model.invars]
        model.asms = [transform(x) for x in model.asms]
        # TODO: what about axioms and properties?
        newb = []
        model.bindings = [
            b.clone([transform(b.action)]) for b in model.bindings
        ]
        model.init = transform(model.init)

    # We first convert all temporal operators to named binders, so
    # it's possible to normalize them. Otherwise we won't have the
    # connection betweel (globally p(X)) and (globally p(Y)). Note
    # that we replace them even inside named binders.
    l2s_gs = set()

    def _l2s_g(vs, t, env):
        vs = tuple(vs)
        res = l2s_g(vs, t, env)
        #        print 'l2s_gs: {} {} {}'.format(vs,t,env)
        l2s_gs.add((vs, t, env))
        return res

    replace_temporals_by_l2s_g = lambda ast: ilu.replace_temporals_by_named_binder_g_ast(
        ast, _l2s_g)
    mod_pass(replace_temporals_by_l2s_g)

    not_lf = replace_temporals_by_l2s_g(lg.Not(fmla))
    if debug.get():
        print "=" * 80 + "\nafter replace_temporals_by_named_binder_g_ast" + "\n" * 3
        print "=" * 80 + "\nl2s_gs:"
        for vs, t, env in sorted(l2s_gs):
            print vs, t, env
        print "=" * 80 + "\n" * 3
        print model
        print "=" * 80 + "\n" * 3

    # now we normalize all named binders
    mod_pass(ilu.normalize_named_binders)
    if debug.get():
        print "=" * 80 + "\nafter normalize_named_binders" + "\n" * 3
        print model
        print "=" * 80 + "\n" * 3

    # construct the monitor related building blocks

    uninterpreted_sorts = [
        s for s in ilg.sig.sorts.values()
        if type(s) is lg.UninterpretedSort and s.name not in mod.finite_sorts
    ]
    reset_a = [
        AssignAction(l2s_a(s)(v),
                     l2s_d(s)(v)).set_lineno(lineno)
        for s in uninterpreted_sorts for v in [lg.Var('X', s)]
    ]
    add_consts_to_d = [
        AssignAction(l2s_d(s)(c), lg.true).set_lineno(lineno)
        for s in uninterpreted_sorts for c in ilg.sig.symbols.values()
        if c.sort == s
    ]
    # TODO: maybe add all ground terms, not just consts (if stratified)
    # TODO: add conjectures that constants are in d and a

    # figure out which l2s_w and l2s_s are used in conjectures
    named_binders_conjs = defaultdict(
        list)  # dict mapping names to lists of (vars, body)
    for b in ilu.named_binders_asts(model.invars):
        #        print 'binder: {} {} {}'.format(b.name,b.environ,b.body)
        named_binders_conjs[b.name].append((b.variables, b.body))
    named_binders_conjs = defaultdict(
        list, ((k, list(set(v))) for k, v in named_binders_conjs.iteritems()))
    to_wait = [
    ]  # list of (variables, term) corresponding to l2s_w in conjectures
    to_wait += named_binders_conjs['l2s_w']
    to_save = [
    ]  # list of (variables, term) corresponding to l2s_s in conjectures
    to_save += named_binders_conjs['l2s_s']

    if debug.get():
        print "=" * 40 + "\nto_wait:\n"
        for vs, t in to_wait:
            print vs, t
            print list(ilu.variables_ast(t)) == list(vs)
            print
        print "=" * 40

    save_state = [
        AssignAction(l2s_s(vs, t)(*vs), t).set_lineno(lineno)
        for vs, t in to_save
    ]
    done_waiting = [forall(vs, lg.Not(l2s_w(vs, t)(*vs))) for vs, t in to_wait]
    reset_w = [
        AssignAction(
            l2s_w(vs, t)(*vs),
            lg.And(*([
                l2s_d(v.sort)(v)
                for v in vs if v.sort.name not in mod.finite_sorts
            ] + [
                lg.Not(t),
                replace_temporals_by_l2s_g(
                    lg.Not(lg.Globally(proof_label, ilu.negate(t))))
            ]))).set_lineno(lineno) for vs, t in to_wait
    ]

    fair_cycle = [l2s_saved]
    fair_cycle += done_waiting
    # projection of relations
    fair_cycle += [
        lg.ForAll(
            vs,
            lg.Implies(
                lg.And(*(l2s_a(v.sort)(v) for v in vs
                         if v.sort.name not in mod.finite_sorts)),
                lg.Iff(l2s_s(vs, t)(
                    *vs), t))) if len(vs) > 0 else lg.Iff(l2s_s(vs, t), t)
        for vs, t in to_save
        if (t.sort == lg.Boolean or isinstance(t.sort, lg.FunctionSort)
            and t.sort.range == lg.Boolean)
    ]
    # projection of functions and constants
    fair_cycle += [
        forall(
            vs,
            lg.Implies(
                lg.And(*([
                    l2s_a(v.sort)(v)
                    for v in vs if v.sort.name not in mod.finite_sorts
                ] + ([
                    lg.Or(l2s_a(t.sort)(l2s_s(vs, t)(*vs)),
                          l2s_a(t.sort)(t))
                ] if t.sort.name not in mod.finite_sorts else []))),
                lg.Eq(l2s_s(vs, t)(*vs), t))) for vs, t in to_save
        if (isinstance(t.sort, lg.UninterpretedSort)
            or isinstance(t.sort, lg.FunctionSort)
            and isinstance(t.sort.range, lg.UninterpretedSort))
    ]
    assert_no_fair_cycle = AssertAction(lg.Not(
        lg.And(*fair_cycle))).set_lineno(lineno)
    assert_no_fair_cycle.lineno = goal.lineno

    monitor_edge = lambda s1, s2: [
        AssumeAction(s1).set_lineno(lineno),
        AssignAction(s1, lg.false).set_lineno(lineno),
        AssignAction(s2, lg.true).set_lineno(lineno),
    ]
    change_monitor_state = [
        ChoiceAction(
            # waiting -> frozen
            Sequence(
                *(monitor_edge(l2s_waiting, l2s_frozen) +
                  [AssumeAction(x).set_lineno(lineno)
                   for x in done_waiting] + reset_a)).set_lineno(lineno),
            # frozen -> saved
            Sequence(*(monitor_edge(l2s_frozen, l2s_saved) + save_state +
                       reset_w)),
            # stay in same state (self edge)
            Sequence().set_lineno(lineno),
        ).set_lineno(lineno)
    ]

    # tableau construction (sort of)

    # Note that we first transformed globally and eventually to named
    # binders, in order to normalize. Without this, we would get
    # multiple redundant axioms like:
    # forall X. (globally phi(X)) -> phi(X)
    # forall Y. (globally phi(Y)) -> phi(Y)
    # and the same redundancy will happen for transition updates.

    # temporals = []
    # temporals += list(ilu.temporals_asts(
    #     # TODO: these should be handled by mod_pass instead (and come via l2s_gs):
    #     # mod.labeled_axioms +
    #     # mod.labeled_props +
    #     [lf]
    # ))
    # temporals += [lg.Globally(lg.Not(t)) for vs, t in to_wait]
    # temporals += [lg.Globally(t) for vs, t in l2s_gs]
    # # TODO get from temporal axioms and temporal properties as well
    # print '='*40 + "\ntemporals:"
    # for t in temporals:
    #     print t, '\n'
    # print '='*40
    # to_g = [ # list of (variables, formula)
    #     (tuple(sorted(ilu.variables_ast(tt))), tt) # TODO what about variable normalization??
    #     for t in temporals
    #     for tt in [t.body if type(t) is lg.Globally else
    #                lg.Not(t.body) if type(t) is lg.Eventually else 1/0]
    # ]
    # TODO: get rid of the above, after properly combining it
    to_g = []  # list of (variables, formula)
    to_g += list(l2s_gs)
    to_g = list(set(to_g))
    if debug.get():
        print '=' * 40 + "\nto_g:\n"
        for vs, t, env in sorted(to_g):
            print vs, t, '\n'
        print '=' * 40

    assume_g_axioms = [
        AssumeAction(forall(vs, lg.Implies(l2s_g(vs, t, env)(*vs),
                                           t))).set_lineno(lineno)
        for vs, t, env in to_g
    ]

    # now patch the module actions with monitor and tableau

    if debug.get():
        print "public_actions:", model.calls

    # Tableau construction
    #
    # Each temporal operator has an 'environment'. The operator
    # applies to states *not* in actions labeled with this
    # environment. This has several consequences:
    #
    # 1) The operator's semantic constraint is an assumed invariant (i.e.,
    # it holds outside of any action)
    #
    # 2) An 'event' for the temporal operator occurs when (a) we return
    # from an execution context inside its environment to one outside,
    # or (b) we are outside the environment of the operator and some symbol
    # occurring in it's body is mutated.
    #
    # 3) At any event for the operator, we update its truth value and
    # and re-establish its semantic constraint.
    #

    # This procedure generates code for an event corresponding to a
    # list of operators. The tableau state is updated and the
    # semantics applied.

    def prop_events(gprops):
        res = []
        for gprop in gprops:
            vs, t, env = gprop.variables, gprop.body, gprop.environ
            res.append(
                AssignAction(
                    old_l2s_g(vs, t, env)(*vs),
                    l2s_g(vs, t, env)(*vs)).set_lineno(lineno))
            res.append(HavocAction(l2s_g(vs, t, env)(*vs)).set_lineno(lineno))
        for gprop in gprops:
            vs, t, env = gprop.variables, gprop.body, gprop.environ
            res.append(
                AssumeAction(
                    forall(
                        vs,
                        lg.Implies(
                            old_l2s_g(vs, t, env)(*vs),
                            l2s_g(vs, t, env)(*vs)))).set_lineno(lineno))
            res.append(
                AssumeAction(
                    forall(
                        vs,
                        lg.Implies(
                            lg.And(lg.Not(old_l2s_g(vs, t, env)(*vs)), t),
                            lg.Not(l2s_g(vs, t,
                                         env)(*vs))))).set_lineno(lineno))
            res.append(
                AssumeAction(forall(vs, lg.Implies(l2s_g(vs, t, env)(*vs),
                                                   t))).set_lineno(lineno))

        return res

    # This procedure generates code for an event corresponding to a
    # list of eventualites to be waited on. The tableau state is updated and the
    # semantics applied.

    def wait_events(waits):
        res = []
        for wait in waits:
            vs = wait.variables
            t = wait.body

            # (l2s_w V. phi)(V) := (l2s_w V. phi)(V) & ~phi & ~(l2s_g V. ~phi)(V)

            res.append(
                AssignAction(
                    wait(*vs),
                    lg.And(
                        wait(*vs), lg.Not(t),
                        replace_temporals_by_l2s_g(
                            lg.Not(lg.Globally(proof_label, ilu.negate(t)))))
                    # TODO check this and make sure its correct
                    # note this adds to l2s_gs
                ).set_lineno(lineno))
        return res

    # The following procedure instruments a statement with operator
    # events for all of the temporal operators.  This depends on the
    # statement's environment, that is, current set of environment
    # labels.
    #
    # Currently, the environment labels of a statement have to be
    # statically determined, but this could change, i.e., the labels
    # could be represented by boolean variables.
    #

    # First, make some memo tables

    envprops = defaultdict(list)
    symprops = defaultdict(list)
    symwaits = defaultdict(list)
    for vs, t, env in l2s_gs:
        prop = l2s_g(vs, t, env)
        envprops[env].append(prop)
        for sym in ilu.symbols_ast(t):
            symprops[sym].append(prop)
    for vs, t in to_wait:
        wait = l2s_w(vs, t)
        for sym in ilu.symbols_ast(t):
            symwaits[sym].append(wait)
    actions = dict((b.name, b.action) for b in model.bindings)

    # lines = dict(zip(gprops,gproplines))

    def instr_stmt(stmt, labels):

        # first, recur on the sub-statements
        args = [
            instr_stmt(a, labels) if isinstance(a, Action) else a
            for a in stmt.args
        ]
        res = stmt.clone(args)

        # now add any needed temporal events after this statement
        event_props = set()
        event_waits = set()

        # first, if it is a call, we must consider any events associated with
        # the return

        # if isinstance(stmt,CallAction):
        #     callee = actions[stmt.callee()]  # get the called action
        #     exiting = [l for l in callee.labels if l not in labels] # environments we exit on return
        #     for label in exiting:
        #         for prop in envprops[label]:
        #             event_props.add(prop)

        # Second, if a symbol is modified, we must add events for every property that
        # depends on the symbol, but only if we are not in the environment of that property.

        for sym in stmt.modifies():
            for prop in symprops[sym]:
                #                if prop.environ not in labels:
                event_props.add(prop)
            for wait in symwaits[sym]:
                event_waits.add(wait)

        # Now, for every property event, we update the property state (none in this case)
        # and also assert the property semantic constraint.

        events = prop_events(event_props)
        events += wait_events(event_waits)
        res = iact.postfix_action(res, events)
        stmt.copy_formals(res)  # HACK: This shouldn't be needed
        return res

    # Instrument all the actions

    model.bindings = [
        b.clone([b.action.clone([instr_stmt(b.action.stmt, b.action.labels)])])
        for b in model.bindings
    ]

    # Now, for every exported action, we add the l2s construction. On
    # exit of each external procedure, we add a tableau event for all
    # the operators whose scope is being exited.
    #
    # TODO: This is wrong in the case of an exported procedure that is
    # also internally called.  We do *not* want to update the tableau
    # in the case of an internal call, since the scope of the
    # operators os not exited. One solution to this is to create to
    # duplicate the actions so there is one version for internal
    # callers and one for external callers. It is possible that this
    # is already done by ivy_isolate, but this needs to be verified.

    calls = set(model.calls)  # the exports
    for b in model.bindings:
        if b.name in calls:
            add_params_to_d = [
                AssignAction(l2s_d(p.sort)(p), lg.true)
                for p in b.action.inputs if p.sort.name not in mod.finite_sorts
            ]
            # tableau updates for exit to environment
            # event_props = set()
            # for label in b.action.labels:
            #     for prop in envprops[label]:
            #         event_props.add(prop)
            # events = prop_events(event_props)
            stmt = concat_actions(*(
                add_params_to_d +
                assume_g_axioms +  # could be added to model.asms
                [b.action.stmt] + add_consts_to_d)).set_lineno(lineno)
            b.action.stmt.copy_formals(stmt)  # HACK: This shouldn't be needed
            b.action = b.action.clone([stmt])

    # The idle action handles automaton state update and cycle checking

    idle_action = concat_actions(*(
        change_monitor_state +
        assume_g_axioms +  # could be added to model.asms
        add_consts_to_d + [assert_no_fair_cycle])).set_lineno(lineno)
    idle_action.formal_params = []
    idle_action.formal_returns = []
    model.bindings.append(
        itm.ActionTermBinding('idle', itm.ActionTerm([], [], [], idle_action)))
    model.calls.append('idle')

    l2s_init = [
        AssignAction(l2s_waiting, lg.true).set_lineno(lineno),
        AssignAction(l2s_frozen, lg.false).set_lineno(lineno),
        AssignAction(l2s_saved, lg.false).set_lineno(lineno),
    ]
    l2s_init += add_consts_to_d
    l2s_init += reset_w
    l2s_init += assume_g_axioms
    l2s_init += [AssumeAction(not_lf).set_lineno(lineno)]
    if not hasattr(model.init, 'lineno'):
        model.init.lineno = None  # Hack: fix this
    model.init = iact.postfix_action(model.init, l2s_init)

    if debug.get():
        print "=" * 80 + "\nafter patching actions" + "\n" * 3
        print model
        print "=" * 80 + "\n" * 3

    # now replace all named binders by fresh relations

    named_binders = defaultdict(
        list)  # dict mapping names to lists of (vars, body)
    for b in ilu.named_binders_asts(
            chain(
                model.invars,
                model.asms,
                [model.init],
                [b.action for b in model.bindings],
            )):
        named_binders[b.name].append(b)
    named_binders = defaultdict(list, ((k, list(sorted(set(v))))
                                       for k, v in named_binders.iteritems()))
    # make sure old_l2s_g is consistent with l2s_g
    #    assert len(named_binders['l2s_g']) == len(named_binders['_old_l2s_g'])
    named_binders['_old_l2s_g'] = [
        lg.NamedBinder('_old_l2s_g', b.variables, b.environ, b.body)
        for b in named_binders['l2s_g']
    ]
    subs = dict((b, lg.Const('{}_{}'.format(k, i), b.sort))
                for k, v in named_binders.iteritems() for i, b in enumerate(v))
    if debug.get():
        print "=" * 80 + "\nsubs:" + "\n" * 3
        for k, v in subs.items():
            print k, ' : ', v, '\n'
        print "=" * 80 + "\n" * 3
    mod_pass(lambda ast: ilu.replace_named_binders_ast(ast, subs))

    if debug.get():
        print "=" * 80 + "\nafter replace_named_binders" + "\n" * 3
        print model
        print "=" * 80 + "\n" * 3

    # if len(gprops) > 0:
    #     assumes = [gprop_to_assume(x) for x in gprops]
    #     model.bindings = [b.clone([prefix_action(b.action,assumes)]) for b in model.bindings]

    # HACK: reestablish invariant that shouldn't be needed

    for b in model.bindings:
        b.action.stmt.formal_params = b.action.inputs
        b.action.stmt.formal_returns = b.action.outputs

    # Change the conclusion formula to M |= true
    conc = itm.TemporalModels(model, lg.And())

    # Build the new goal
    goal = ipr.clone_goal(goal, ipr.goal_prems(goal), conc)

    # Return the new goal stack

    goals = [goal] + goals[1:]
    return goals
def to_aiger(mod,ext_act):

    erf = il.Symbol('err_flag',il.find_sort('bool'))
    errconds = []
    add_err_flag_mod(mod,erf,errconds)

    # we use a special state variable __init to indicate the initial state

    ext_acts = [mod.actions[x] for x in sorted(mod.public_actions)]
    ext_act = ia.EnvAction(*ext_acts)

    init_var = il.Symbol('__init',il.find_sort('bool')) 
    init = add_err_flag(ia.Sequence(*([a for n,a in mod.initializers]+[ia.AssignAction(init_var,il.And())])),erf,errconds)
    action = ia.Sequence(ia.AssignAction(erf,il.Or()),ia.IfAction(init_var,ext_act,init))
    
    # get the invariant to be proved, replacing free variables with
    # skolems. First, we apply any proof tactics.

    pc = ivy_proof.ProofChecker(mod.axioms,mod.definitions,mod.schemata)
    pmap = dict((lf.id,p) for lf,p in mod.proofs)
    conjs = []
    for lf in mod.labeled_conjs:
        if lf.id in pmap:
            proof = pmap[lf.id]
            subgoals = pc.admit_proposition(lf,proof)
            conjs.extend(subgoals)
        else:
            conjs.append(lf)

    invariant = il.And(*[il.drop_universals(lf.formula) for lf in conjs])
#    iu.dbg('invariant')
    skolemizer = lambda v: ilu.var_to_skolem('__',il.Variable(v.rep,v.sort))
    vs = ilu.used_variables_in_order_ast(invariant)
    sksubs = dict((v.rep,skolemizer(v)) for v in vs)
    invariant = ilu.substitute_ast(invariant,sksubs)
    invar_syms = ilu.used_symbols_ast(invariant)
    
    # compute the transition relation

    stvars,trans,error = action.update(mod,None)
    

#    print 'action : {}'.format(action)
#    print 'annotation: {}'.format(trans.annot)
    annot = trans.annot
#    match_annotation(action,annot,MatchHandler())
    
    indhyps = [il.close_formula(il.Implies(init_var,lf.formula)) for lf in mod.labeled_conjs]
#    trans = ilu.and_clauses(trans,indhyps)

    # save the original symbols for trace
    orig_syms = ilu.used_symbols_clauses(trans)
    orig_syms.update(ilu.used_symbols_ast(invariant))
                     
    # TODO: get the axioms (or maybe only the ground ones?)

    # axioms = mod.background_theory()

    # rn = dict((sym,tr.new(sym)) for sym in stvars)
    # next_axioms = ilu.rename_clauses(axioms,rn)
    # return ilu.and_clauses(axioms,next_axioms)

    funs = set()
    for df in trans.defs:
        funs.update(ilu.used_symbols_ast(df.args[1]))
    for fmla in trans.fmlas:
        funs.update(ilu.used_symbols_ast(fmla))
#   funs = ilu.used_symbols_clauses(trans)
    funs.update(ilu.used_symbols_ast(invariant))
    funs = set(sym for sym in funs if  il.is_function_sort(sym.sort))
    iu.dbg('[str(fun) for fun in funs]')

    # Propositionally abstract

    # step 1: get rid of definitions of non-finite symbols by turning
    # them into constraints

    new_defs = []
    new_fmlas = []
    for df in trans.defs:
        if len(df.args[0].args) == 0 and is_finite_sort(df.args[0].sort):
            new_defs.append(df)
        else:
            fmla = df.to_constraint()
            new_fmlas.append(fmla)
    trans = ilu.Clauses(new_fmlas+trans.fmlas,new_defs)

    # step 2: get rid of ite's over non-finite sorts, by introducing constraints

    cnsts = []
    new_defs = [elim_ite(df,cnsts) for df in trans.defs]
    new_fmlas = [elim_ite(fmla,cnsts) for fmla in trans.fmlas]
    trans = ilu.Clauses(new_fmlas+cnsts,new_defs)
    
    # step 3: eliminate quantfiers using finite instantiations

    from_asserts = il.And(*[il.Equals(x,x) for x in ilu.used_symbols_ast(il.And(*errconds)) if
                            tr.is_skolem(x) and not il.is_function_sort(x.sort)])
    iu.dbg('from_asserts')
    invar_syms.update(ilu.used_symbols_ast(from_asserts))
    sort_constants = mine_constants(mod,trans,il.And(invariant,from_asserts))
    sort_constants2 = mine_constants2(mod,trans,invariant)
    print '\ninstantiations:'
    trans,invariant = Qelim(sort_constants,sort_constants2)(trans,invariant,indhyps)
    
    
#    print 'after qe:'
#    print 'trans: {}'.format(trans)
#    print 'invariant: {}'.format(invariant)

    # step 4: instantiate the axioms using patterns

    # We have to condition both the transition relation and the
    # invariant on the axioms, so we define a boolean symbol '__axioms'
    # to represent the axioms.

    axs = instantiate_axioms(mod,stvars,trans,invariant,sort_constants,funs)
    ax_conj = il.And(*axs)
    ax_var = il.Symbol('__axioms',ax_conj.sort)
    ax_def = il.Definition(ax_var,ax_conj)
    invariant = il.Implies(ax_var,invariant)
    trans = ilu.Clauses(trans.fmlas+[ax_var],trans.defs+[ax_def])
    
    # step 5: eliminate all non-propositional atoms by replacing with fresh booleans
    # An atom with next-state symbols is converted to a next-state symbol if possible

    stvarset = set(stvars)
    prop_abs = dict()  # map from atoms to proposition variables
    global prop_abs_ctr  # sigh -- python lameness
    prop_abs_ctr = 0   # counter for fresh symbols
    new_stvars = []    # list of fresh symbols

    # get the propositional abstraction of an atom
    def new_prop(expr):
        res = prop_abs.get(expr,None)
        if res is None:
            prev = prev_expr(stvarset,expr,sort_constants)
            if prev is not None:
#                print 'stvar: old: {} new: {}'.format(prev,expr)
                pva = new_prop(prev)
                res = tr.new(pva)
                new_stvars.append(pva)
                prop_abs[expr] = res  # prevent adding this again to new_stvars
            else:
                global prop_abs_ctr
                res = il.Symbol('__abs[{}]'.format(prop_abs_ctr),expr.sort)
#                print '{} = {}'.format(res,expr)
                prop_abs[expr] = res
                prop_abs_ctr += 1
        return res

    # propositionally abstract an expression
    global mk_prop_fmlas
    mk_prop_fmlas = []
    def mk_prop_abs(expr):
        if il.is_quantifier(expr) or len(expr.args) > 0 and any(not is_finite_sort(a.sort) for a in expr.args):
            return new_prop(expr)
        return expr.clone(map(mk_prop_abs,expr.args))

    
    # apply propositional abstraction to the transition relation
    new_defs = map(mk_prop_abs,trans.defs)
    new_fmlas = [mk_prop_abs(il.close_formula(fmla)) for fmla in trans.fmlas]

    # find any immutable abstract variables, and give them a next definition

    def my_is_skolem(x):
        res = tr.is_skolem(x) and x not in invar_syms
        return res    
    def is_immutable_expr(expr):
        res = not any(my_is_skolem(sym) or tr.is_new(sym) or sym in stvarset for sym in ilu.used_symbols_ast(expr))
        return res
    for expr,v in prop_abs.iteritems():
        if is_immutable_expr(expr):
            new_stvars.append(v)
            print 'new state: {}'.format(expr)
            new_defs.append(il.Definition(tr.new(v),v))

    trans = ilu.Clauses(new_fmlas+mk_prop_fmlas,new_defs)

    # apply propositional abstraction to the invariant
    invariant = mk_prop_abs(invariant)

    # create next-state symbols for atoms in the invariant (is this needed?)
    rn = dict((sym,tr.new(sym)) for sym in stvars)
    mk_prop_abs(ilu.rename_ast(invariant,rn))  # this is to pick up state variables from invariant

    # update the state variables by removing the non-finite ones and adding the fresh state booleans
    stvars = [sym for sym in stvars if is_finite_sort(sym.sort)] + new_stvars

#    iu.dbg('trans')
#    iu.dbg('stvars')
#    iu.dbg('invariant')
#    exit(0)

    # For each state var, create a variable that corresponds to the input of its latch
    # Also, havoc all the state bits except the init flag at the initial time. This
    # is needed because in aiger, all latches start at 0!

    def fix(v):
        return v.prefix('nondet')
    def curval(v):
        return v.prefix('curval')
    def initchoice(v):
        return v.prefix('initchoice')
    stvars_fix_map = dict((tr.new(v),fix(v)) for v in stvars)
    stvars_fix_map.update((v,curval(v)) for v in stvars if v != init_var)
    trans = ilu.rename_clauses(trans,stvars_fix_map)
#    iu.dbg('trans')
    new_defs = trans.defs + [il.Definition(ilu.sym_inst(tr.new(v)),ilu.sym_inst(fix(v))) for v in stvars]
    new_defs.extend(il.Definition(curval(v),il.Ite(init_var,v,initchoice(v))) for v in stvars if  v != init_var)
    trans = ilu.Clauses(trans.fmlas,new_defs)
    
    # Turn the transition constraint into a definition
    
    cnst_var = il.Symbol('__cnst',il.find_sort('bool'))
    new_defs = list(trans.defs)
    new_defs.append(il.Definition(tr.new(cnst_var),fix(cnst_var)))
    new_defs.append(il.Definition(fix(cnst_var),il.Or(cnst_var,il.Not(il.And(*trans.fmlas)))))
    stvars.append(cnst_var)
    trans = ilu.Clauses([],new_defs)
    
    # Input are all the non-defined symbols. Output indicates invariant is false.

#    iu.dbg('trans')
    def_set = set(df.defines() for df in trans.defs)
    def_set.update(stvars)
#    iu.dbg('def_set')
    used = ilu.used_symbols_clauses(trans)
    used.update(ilu.symbols_ast(invariant))
    inputs = [sym for sym in used if
              sym not in def_set and not il.is_interpreted_symbol(sym)]
    fail = il.Symbol('__fail',il.find_sort('bool'))
    outputs = [fail]
    

#    iu.dbg('trans')
    
    # make an aiger

    aiger = Encoder(inputs,stvars,outputs)
    comb_defs = [df for df in trans.defs if not tr.is_new(df.defines())]

    invar_fail = il.Symbol('invar__fail',il.find_sort('bool'))  # make a name for invariant fail cond
    comb_defs.append(il.Definition(invar_fail,il.Not(invariant)))

    aiger.deflist(comb_defs)
    for df in trans.defs:
        if tr.is_new(df.defines()):
            aiger.set(tr.new_of(df.defines()),aiger.eval(df.args[1]))
    miter = il.And(init_var,il.Not(cnst_var),il.Or(invar_fail,il.And(fix(erf),il.Not(fix(cnst_var)))))
    aiger.set(fail,aiger.eval(miter))

#    aiger.sub.debug()

    # make a decoder for the abstract propositions

    decoder = dict((y,x) for x,y in prop_abs.iteritems())
    for sym in aiger.inputs + aiger.latches:
        if sym not in decoder and sym in orig_syms:
            decoder[sym] = sym

    cnsts = set(sym for syms in sort_constants.values() for sym in syms)
    return aiger,decoder,annot,cnsts,action,stvarset
Exemple #11
0
def relevant_definitions(symbols):
    dfn_map = dict((ldf.formula.defines(),ldf.formula.args[1]) for ldf in module.definitions)
    rch = set(iu.reachable(symbols,lambda sym: lu.symbols_ast(dfn_map[sym]) if sym in dfn_map else []))
    return [ldf for ldf in module.definitions if ldf.formula.defines() in rch]
Exemple #12
0
def invariance_tactic(prover,goals,proof):
    goal = goals[0]                  # pick up the first proof goal
    conc = ipr.goal_conc(goal)       # get its conclusion
    if not isinstance(conc,TemporalModels):
        raise iu.IvyError(proof,'proof goal is not temporal')
    model = conc.model
    fmla = conc.fmla
    if not isinstance(fmla,il.Globally):
        raise iu.IvyError(proof,'invariance tactic applies only to globally formulas')
    invar = fmla.args[0]
    if il.is_temporal(invar):
        raise iu.IvyError(proof,'invariance tactic applies only formulas "globally p"' +
                       ' where p is non-temporal')

    # Collect the auxiliary invariants
    invars = [inv.compile() for inv in proof.tactic_decls]

    # Add the invariant phi to the list
    invars.append(ipr.clone_goal(goal,[],invar))

    # Add the invariant list to the model
    model = model.clone([])
    model.invars = model.invars + invars
    
    # Get all the implicit globally properties from the proof
    # environment. Each temporal operator has an 'environment'. The
    # operator applies to states *not* in actions labeled with this
    # environment. This has several consequences:
    #
    # 1) The operator's semantic constraint is an assumed invariant (i.e.,
    # it holds outside of any action)
    #
    # 2) An 'event' for the temporal operator occurs when (a) we return
    # from an execution context inside its environment to one outside,
    # or (b) we are outside the environment of the operator and some symbol
    # occurring in it's body is mutated.
    #
    # 3) At any event for the operator, we update its truth value and
    # and re-establish its senatic constraint.
    #
    #
    # The following procedure instruments a statement with operator events for
    # both the property to be proved and the invariant assumptions (all G properties here).
    # This depends on the statement's environment, that is, current set of environment
    # labels.
    #
    # Currently, the environment labels of a statement have to be
    # statically determined, but this could change, i.e., the labels
    # could be represented by boolean variables. 
    #
    # TODO: there is something a bit inelegant here, because when we return from
    # an exported action to the external environment, we need to update the operator
    # states, however, we do *not* want to do this when returning to in internal caller.
    # The best solution currently for this is to duplication the actions so there
    # is one version for internal callers and one for external callers. This issue doesn't
    # affect this simple invariance tactic, because it doesn't need to update the truth
    # values of the operators.
    
    # Get all the G properties from the prover environment as assumptions
    
    assumed_gprops = [x for x in prover.axioms if not x.explicit and x.temporal and il.is_gprop(x.formula)]
    gprops = [x.formula for x in assumed_gprops]
    gproplines = [x.lineno for x in assumed_gprops]
    
    # We represent the property G phi to be proved by its negation F ~phi.
    gprops.append(il.Eventually(fmla.environ,il.Not(invar)))
    gproplines.append(goal.lineno)

    # Make some memo tables

    envprops = defaultdict(list)
    symprops = defaultdict(list)
    for prop in gprops:
        envprops[prop.environ].append(prop)
        for sym in ilu.symbols_ast(prop):
            symprops[sym].append(prop)
    actions = dict((b.name,b.action) for b in model.bindings)
    lines = dict(zip(gprops,gproplines))
            
    def instr_stmt(stmt,labels):

        # first, recur on the sub-statements
        args = [instr_stmt(a,labels) if isinstance(a,iact.Action) else a for a in stmt.args]
        res = stmt.clone(args)

        # now add any needed temporal events after this statement
        event_props = set()

        # first, if it is a call, we must consider any events associated with
        # the return
        
        if isinstance(stmt,iact.CallAction):
            callee = actions[stmt.callee()]  # get the called action
            exiting = [l for l in callee.labels if l not in labels] # environments we exit on return
            for label in exiting:
                for prop in envprops[label]:
                    event_props.add(prop)

        # Second, if a symbol is modified, we must add events for every property that
        # depends on the symbol, but only if we are not in the environment of that property. 
                    
        for sym in stmt.modifies():
            for prop in symprops[sym]:
                if prop.environ not in labels:
                    event_props.add(prop)
                    
        # Now, for every property event, we update the property state (none in this case)
        # and also assert the property semantic constraint. 

        events = [prop_event(prop,lines[prop]) for prop in event_props]
        res =  iact.postfix_action(res,events)
        stmt.copy_formals(res) # HACK: This shouldn't be needed
        return res

    # Add property events to all of the actions:

    model.bindings = [b.clone([b.action.clone([instr_stmt(b.action.stmt,b.action.labels)])])
                      for b in model.bindings]
    
    # Add all the assumed invariants to the model

    model.asms.extend([p.clone([p.label,p.formula.args[0]]) for p in assumed_gprops])
    
    # if len(gprops) > 0:
    #     assumes = [gprop_to_assume(x) for x in gprops]
    #     model.bindings = [b.clone([prefix_action(b.action,assumes)]) for b in model.bindings]

    # Change the conclusion formula to M |= true
    conc = TemporalModels(model,il.And())

    # Build the new goal
    goal = ipr.clone_goal(goal,ipr.goal_prems(goal),conc)

    # Return the new goal stack

    goals = [goal] + goals[1:]
    return goals