Пример #1
0
 def if_tactic(self,decls,proof):
     cond = proof.args[0]
     true_goal = ia.LabeledFormula(decls[0].label,il.Implies(cond,decls[0].formula))
     true_goal.lineno = decls[0].lineno
     false_goal = ia.LabeledFormula(decls[0].label,il.Implies(il.Not(cond),decls[0].formula))
     false_goal.lineno = decls[0].lineno
     return (attrib_goals(proof.args[1],self.apply_proof([true_goal],proof.args[1])) +
             attrib_goals(proof.args[2],self.apply_proof([false_goal],proof.args[2])) +
             decls[1:])
Пример #2
0
def create_strat_map(assumes, asserts, macros):
    """ Given a list of assumptions, assertions and macros, compute
    the stratification graph. The difference between assumes and
    asserts is that the free variables in assumes are treated as
    universally quantified, while the asserts are treated as
    negated.

    Each argument is a list of pairs `(fmla,ast)` where `fmla` is the
    formula and `ast` an ast giving the origin of the formula.

    """

    global universally_quantified_variables
    global strat_map
    global arcs

    # Gather all the formulas in the VC.

    all_fmlas = [(il.close_formula(x), y) for x, y in assumes]
    all_fmlas.extend((il.Not(x), y) for x, y in asserts)
    all_fmlas.extend(macros)

    # Get the universally quantified variables. The free variables of
    # asserts and macros won't count as universal. We keep track of the
    # line numbers of these variables for error messages.

    universally_quantified_variables = dict()
    for fmla, lf in all_fmlas:
        for v in il.universal_variables([fmla]):
            if (il.is_uninterpreted_sort(v.sort)
                    or il.has_infinite_interpretation(v.sort)):
                universally_quantified_variables[v] = lf

    # print 'universally_quantified_variables : {}'.format([str(v) for v in universally_quantified_variables])

    # Create an empty graph.

    strat_map = defaultdict(UFNode)
    arcs = []

    # Handle macros, as described above.

    create_macro_maps(assumes, asserts, macros)

    # Simulate the Skolem functions that would be generated by AE alternations.

    for fmla, ast in all_fmlas:
        make_skolems(fmla, ast, True, [])

    # Construct the stratification graph by calling `map_fmla` on all
    # of the formulas in the VC. We don't include the macro definitions
    # here, because these are 'inlined' by `map_fmla`.

    for pair in assumes + asserts:
        map_fmla(pair[1].lineno, pair[0], 0)
Пример #3
0
def create_strat_map(assumes,asserts,macros):
    global symbols_over_universals
    global universally_quantified_variables
    all_fmlas = [il.close_formula(pair[0]) for pair in assumes]
    all_fmlas.extend(il.Not(pair[0]) for pair in asserts)
    all_fmlas.extend(pair[0] for pair in macros)
#    for f in all_fmlas:
#        print f
    symbols_over_universals = il.symbols_over_universals(all_fmlas)
    universally_quantified_variables = il.universal_variables(all_fmlas)
    
    strat_map = defaultdict(UFNode)
    for pair in assumes+asserts+macros:
        map_fmla(pair[0],strat_map)

#    show_strat_map(strat_map)
#    print 'universally_quantified_variables:{}'.format(universally_quantified_variables)
    return strat_map
Пример #4
0
def check_can_assert(logic, fmla, ast):
    check_can_assume(logic, fmla, ast)
    if not il.is_in_logic(il.Not(fmla), logic):
        report_error(logic, " when negated", ast)
Пример #5
0
if __name__ == "__main__":
    ag = ivy.ivy_init()
    ac = AC(ag.assertions, ag.actions, ag.domain)
    with utl.ErrorPrinter():
        for ass in ag.assertions:
            lhs = ass.args[0].rep
            rhs = ass.args[1]
            rhs_state = eval_assert_rhs(rhs, ag.domain)
            try:
                if lhs in ag.predicates:
                    with ac:
                        lhs_state = itp.eval_state(ag.predicates[lhs], ac)
                elif lhs in ag.actions:
                    with ac:
                        lhs_state = itp.eval_state(
                            act.entry(lg.Not(rhs_state.precond)), ac)
                        lhs_state = itp.apply_action(ass, lhs, ag.actions[lhs],
                                                     lhs_state)
                else:
                    raise utl.IvyError(ass, "{} undefined".format(lhs))
                print "lhs_state: %s" % lhs_state.clauses
                print "rhs_fmla: %s" % rhs_state.clauses
                cex = ag.domain.order(lhs_state, rhs_state)
                if not cex:
                    print repr(utl.IvyError(ass, "assertion does not hold"))
                    analyze_state(ag, lhs_state, cex.clauses)
            except itp.IvyActionFailedError as err:
                print repr(err)
                print err.error_state.clauses
                analyze_state(ag, err.error_state, [])
Пример #6
0
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
Пример #7
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
Пример #8
0
def vc_to_goal(lineno,name,vc,action):
    return pr.make_goal(lineno,name,[],lg.Not(lu.clauses_to_formula(vc)),
                        annot=(action,vc.annot))