Example #1
0
 def try_property(self,prop=None):
     if prop == None:
         udc = false_properties()
         udc_text = [str(prop) for prop in udc]
         msg = "Choose a property to see counterexample:"
         cmd = lambda idx: self.try_property(udc[idx])
         self.listbox_dialog(msg,udc_text,command=cmd)
     else:
         print "type(prop) = {}".format(type(prop))
         if hasattr(prop,'lineno'):
             filename,lineno = prop.lineno
             self.browse(filename,lineno)
         dual = dual_clauses(formula_to_clauses(prop.formula))
         ag = AnalysisGraph(initializer=top_alpha)
         oag = ag.bmc(ag.states[0],dual)
         self.add(oag)
Example #2
0
 def __init__(self, model):
     self.model = model  # should be an IvyModel instance
     self.ivy_ag = model.ivy_ag
     self.ivy_interp = model.ivy_interp
     self.goal_stack = ProofGoalStack()
     self.conjectures = []
     # initialise an AnalysisGraph for the crg
     self.crg = AnalysisGraph(self.ivy_ag.domain, self.ivy_ag.pvars)
     self.crg.actions = self.ivy_ag.actions
Example #3
0
class AnalysisGraphWidget(Canvas):
    def __init__(self,tk,g,root=None):
        if root == None:
            root = tk
        menubar = Menu(root)
        filemenu = Menu(menubar, tearoff=0)
        filemenu.add_command(label="Save",command=self.save)
        filemenu.add_command(label="Save abstraction",command=self.save_abstraction)
        filemenu.add_separator()
        filemenu.add_command(label="Exit", command=root.quit)
        menubar.add_cascade(label="File", menu=filemenu)
        modemenu = Menu(menubar, tearoff=0)
        self.mode = StringVar(root,default_mode.get())
        modemenu.add("radiobutton",label="Concrete",variable=self.mode,value="concrete")
        modemenu.add("radiobutton",label="Abstract",variable=self.mode,value="abstract")
        modemenu.add("radiobutton",label="Bounded",variable=self.mode,value="bounded")
        menubar.add_cascade(label="Mode", menu=modemenu)
        actionmenu = Menu(menubar, tearoff=0)
        actionmenu.add_command(label="Recalculate all",command=self.recalculate_all)
        actionmenu.add_command(label="Show reachable states",command=self.show_reachable_states)
        menubar.add_cascade(label="Action", menu=actionmenu)
        root.config(menu=menubar)
        Canvas.__init__(self,root)
        self.g = g
        self.tk = tk
        self.root = root
        self.mark = None
        tk.eval('package require Tcldot')
        self.pack(fill=BOTH,expand=1)
        self.rebuild()
    def busy(self):
        self.tk.config(cursor="watch")
        self.tk.update()
        self.config(cursor="watch")
    def ready(self):
        self.tk.config(cursor="")
        self.tk.update()
        self.config(cursor="")
    def save(self):
        f = tkFileDialog.asksaveasfile(mode='w',filetypes = [('analysis files', '.a2g')],title='Save analysis state as...',parent=self)
        if f:
            pickle.dump(self.g,f, protocol=2)
            f.close()
    def save_abstraction(self):
        f = tkFileDialog.asksaveasfile(mode='w',filetypes = [('ivy files', '.ivy')],title='Save abstraction as...',parent=self)
        if f:
            for concept in self.g.domain.concept_spaces:
                f.write('concept ' + repr(concept[0]) + ' = ' + repr(concept[1]) + '\n')
            f.close()
    
    def node_color(self,node):
        return "green" if hasattr(node,'safe') and node.safe else "black"

    def rebuild(self):
        tk = self.tk
        g = self.g
        tk.eval('set graph [dotnew digraph forcelabels true]')
        handle_to_node = dict()
        self.node_to_handle = dict()
        for (s,i) in zip(g.states,range(len(g.states))):
                p = "%d" % i
                shape = "point" if (s.clauses != None and s.clauses.is_false()) else "circle"
                label = str(s.id) if s.clauses != None else '?'
                color = self.node_color(s) 
                handle = tk.eval('$graph addnode "' + p + '" label "' + label + '" shape ' + shape + ' color ' + color + ' fontsize 10 width 0.5 penwidth 2.0')
                handle = 'node' + str(i+1) if handle.startswith('node0x') else handle
                self.node_to_handle[i] = handle
                handle_to_node[handle] = s
        i = 0
        for transition in g.transitions:
                x,op,label,y = transition
                handle = tk.eval('$graph addedge "' + ("%d" % x.id) + '" "' + ("%d" % y.id) + '" label {' + label + '} fontsize 10 penwidth 2.0')
                handle = 'edge' + str(i+1)  if handle.startswith('edge0x') else handle
                i += 1
                if isinstance(op,AnalysisSubgraph):
                    self.tag_bind("0" + handle, "<Button-1>", lambda y, op=op: op.display(tk))
                    self.tag_bind("1" + handle, "<Button-1>", lambda y, op=op: op.display(tk))
                self.tag_bind("0" + handle, "<Button-3>", lambda ev, transition=transition: self.right_click_edge(ev,transition))
                self.tag_bind("1" + handle, "<Button-3>", lambda ev, transition=transition: self.right_click_edge(ev,transition))
        for (x,y) in g.covering:
                handle = tk.eval('$graph addedge "' + ("%d" % x.id) + '" "' + ("%d" % y.id) + '" style dashed')
        self.delete(ALL)
#        print tk.eval('$graph render ' + self._w  + ' DOT')
        tk.eval('eval [$graph render ' + self._w  + ' DOT]')
        self.config(scrollregion=self.bbox(ALL))
        for x in handle_to_node:
            n = handle_to_node[x]
#            print "x = {}".format(x)
#            print "n = {}".format(n)
            self.tag_bind("0" + x, "<Button-1>", lambda y, n=n: self.left_click_node(y,n))
            self.tag_bind("1" + x, "<Button-1>", lambda y, n=n: self.left_click_node(y,n))
            self.tag_bind("0" + x, "<Button-3>", lambda y, n=n: self.right_click_node(y,n))
            self.tag_bind("1" + x, "<Button-3>", lambda y, n=n: self.right_click_node(y,n))
        self.show_mark(True)

    def update_node_color(self,node):
        for item in self.find_withtag("1"+self.node_to_handle[node.id]):
            self.itemconfig(item,outline=self.node_color(node))

    def show_mark(self,on=True):
        if self.mark in self.node_to_handle:
            for item in self.find_withtag("1"+self.node_to_handle[self.mark]):
                self.itemconfig(item,fill=('red' if on else 'white'))
    def view_state(self,n,clauses):
        print "state: {}".format(clauses)
        if hasattr(self,'current_concept_graph') and self.current_concept_graph.winfo_exists():
            self.current_concept_graph.set_parent_state(n,clauses)
            return
        sg = self.g.concept_graph(n,clauses)
        self.current_concept_graph = ivy_graph_ui.show_graph(sg,self.tk,parent=self)
    def left_click_node(self,event,n):
        if n.clauses != None:
            self.view_state(n,n.clauses)
    def right_click_node(self,event,n):
        tk = self.tk
        g = self.g
        self.popup = Menu(tk, tearoff=0)
        self.popup.add_command(label="Execute action:")
        self.popup.add_separator()
        state_equations = g.state_actions(n)
        for a in sorted(state_equations, key=state_equation_label):
            self.popup.add_command(label=state_equation_label(a),command = lambda n=n,a=a: self.do_state_action(a))
        self.popup.add_separator()
        self.popup.add_command(label='Check safety',command = lambda n=n: self.check_safety_node(n))
        self.popup.add_command(label='Extend',command = lambda n=n: self.find_extension(n))
        self.popup.add_command(label='Mark',command = lambda n=n: self.mark_node(n))
        self.popup.add_command(label='Cover by marked',command = lambda n=n: self.cover_node(n))
        self.popup.add_command(label='Join with marked',command = lambda n=n: self.join_node(n))
        self.popup.add_command(label='Try conjecture',command = lambda n=n: self.try_conjecture(n))
        self.popup.add_command(label='Try remembered goal',command = lambda n=n: self.try_remembered_graph(n))
        self.popup.add_command(label='Delete',command = lambda n=n: self.delete_node(n))
        self.popup.tk_popup(event.x_root, event.y_root, 0)
    def mark_node(self,n):
        self.show_mark(False)
        self.mark = n.id
        self.show_mark(True)
    def get_mark(self):
        return self.g.states[self.mark]
    def cover_node(self,covered_node):
        g = self.g
        try:
            covering_node = self.get_mark()
        except:
            return
        print "Trying to cover %s by %s" % (covered_node.id,covering_node.id)
        with RunContext(self):
            if not g.cover(covered_node,covering_node):
                raise IvyError(None,"Covering failed")
        self.rebuild()
    def join_node(self,node2):
        g = self.g
        try:
            node1 = self.get_mark()
        except:
            return
        with RunContext(self):
            g.join(node1,node2,self.get_alpha())
        self.rebuild()
    def delete_node(self,deleted_node):
        g = self.g
        g.delete(deleted_node)
        self.rebuild()
    def right_click_edge(self,event,transition):
        tk = self.tk
        g = self.g
        self.popup = Menu(tk, tearoff=0)
        self.popup.add_command(label="Dismiss")
        self.popup.add_command(label="Recalculate",command = lambda transition=transition: self.recalculate_edge(transition))
        self.popup.tk_popup(event.x_root, event.y_root, 0)

    def set_state(self,state,clauses):
        state.clauses = clauses

    def get_alpha(self):
        return (None if self.mode.get() == "concrete"
                else ivy_alpha.alpha if self.mode.get() == "abstract"
                else top_alpha)

    def do_state_action(self,a):
        with RunContext(self):
            with EvalContext(check=False):
                print "action {"
                s = self.g.do_state_action(a,self.get_alpha())
                print "state = {}".format(s)
                print "} action %s" % a.args[0]
        self.rebuild()

    def execute_action(self,n,a):
        with RunContext(self):
            print "action %s {" % a
            s = self.g.execute_action(a,n,self.get_alpha())
            print "} action %s" % a
        self.rebuild()

    def show_reachable_states(self):
        ui_create(self.reachable_tree,self.tk,Toplevel(self.tk))

    def recalculate_all(self):
        done = set()
        for transition in self.g.transitions:
            if transition[-1].id not in done:
                self.recalculate_edge(transition)
                done.add(transition[-1].id)

    def recalculate_edge(self,transition):
        with RunContext(self):
            self.g.recalculate(transition,self.get_alpha())
        self.rebuild()

    def recalculate_state(self,state):
        with RunContext(self):
            self.g.recalculate_state(state,self.get_alpha())
        self.rebuild()
        
    @property
    def reachable_tree(self):
        if  not hasattr(self,'_reachable_tree'):
            self._reachable_tree = AnalysisGraph(self.g.domain,self.g.pvars)
            for s in self.g.domain.unders[0:1]:
                self._reachable_tree.add(s)
        return self._reachable_tree

    def one_step_reach(self,state,clauses):
        with RunContext(self):
            rs = reach_state_from_pred_no_abduction(state,clauses)
            if rs != None:
                self.reachable_tree.add(rs)
                t = self.g.transition_to(state)
                if t:
                    pre,action,label,post = t
                    self.reachable_tree.transitions.append((rs.pred,action,label,rs))
                f = filter_conjectures(state,rs.clauses)
                if f:
                    dlg = Toplevel(self)
                    Label(dlg, text="The following conjectures have been eliminated:").pack()
                    S = Scrollbar(dlg)
                    T = Listbox(dlg, height=8, width=50, selectmode=SINGLE)
                    S.pack(side=RIGHT, fill=Y)
                    T.pack(side=LEFT, fill=Y)
                    S.config(command=T.yview)
                    T.config(yscrollcommand=S.set)
                    for conj in f:
                        T.insert(END, repr(clauses_to_formula(conj)))
                    b = Button(dlg, text="OK", command=dlg.destroy)
                    b.pack(padx=5,side=TOP)
                    uu.center_window_on_window(dlg,self.root)
                    self.tk.wait_window(dlg)
            return rs

    def check_safety_node(self,node):
        node.safe = self.g.check_safety(node)
        self.update_node_color(node)
        if not node.safe:
            if self.mode.get() != "bounded":
                uu.ok_cancel_dialog(self.tk,self.root,"The node is not proved safe: {}. View unsafe states?".format(node.safe.msg),
                                    command=functools.partial(self.view_state,node.safe.state,node.safe.clauses))
            else:
#                print "bounded check: node.safe.clauses={}".format(node.safe.clauses)
                res = self.g.bmc(node.safe.state,node.safe.clauses)
                if res == None:
                    node.safe = True
                    self.update_node_color(node)
                else:
                    uu.ok_cancel_dialog(self.tk,self.root,"The node is unsafe: {}. View error trace?".format(node.safe.msg),
                                        command=functools.partial(self.view_ag,res))

    def view_ag(self,res):
        ui_create(res,self.tk,Toplevel(self.tk))
            
    def find_extension(self,node):
        try:
            with RunContext(self):
                a = next(self.g.state_extensions(node))
                self.do_state_action(a)
        except StopIteration:
            uu.ok_dialog(self.tk,self.root,"State {} is closed.".format(node.id))
            

    def try_conjecture(self,node):
        dlg = Toplevel(self)
        lbl = "Choose a conjecture to prove:"
        Label(dlg, text=lbl).pack()
        S = Scrollbar(dlg)
        T = Listbox(dlg, height=8, width=50, selectmode=SINGLE)
        S.pack(side=RIGHT, fill=Y)
        T.pack(side=LEFT, fill=Y)
        S.config(command=T.yview)
        T.config(yscrollcommand=S.set)
        udc = undecided_conjectures(node)
        for conj in udc:
            T.insert(END, repr(clauses_to_formula(conj)))
        b = Button(dlg, text="Prove", command=functools.partial(self.do_try_conjecture,node,T,dlg,udc))
        b.pack(padx=5,side=TOP)
        b = Button(dlg, text="Cancel", command=dlg.destroy)
        b.pack(padx=5,side=TOP)
        uu.center_window_on_window(dlg,self.root)
        self.tk.wait_window(dlg)
        

    def do_try_conjecture(self,node,T,dlg,udc):
        sel = map(int, T.curselection())
        if sel:
            conj = udc[sel[0]]
            print "type(conj) = {}".format(type(conj))
            dual = dual_clauses(conj)
            sg = self.g.concept_graph(node)
            sg.add_constraints(dual.clauses)
            ivy_graph_ui.show_graph(sg,self.tk,parent=self)
        dlg.destroy()


    def try_remembered_graph(self,node):
        dlg = Toplevel(self)
        lbl = "Choose a remembered goal:"
        Label(dlg, text=lbl).pack()
        S = Scrollbar(dlg)
        T = Listbox(dlg, height=8, width=50, selectmode=SINGLE)
        S.pack(side=RIGHT, fill=Y)
        T.pack(side=LEFT, fill=Y)
        S.config(command=T.yview)
        T.config(yscrollcommand=S.set)
        if not hasattr(self,'remembered_graphs'):
            self.remembered_graphs = {}
        names = [n for n in self.remembered_graphs]
        for name in names:
            T.insert(END, name)
        b = Button(dlg, text="Try", command=functools.partial(self.do_try_remembered_graph,node,T,dlg,names))
        b.pack(padx=5,side=TOP)
        b = Button(dlg, text="Cancel", command=dlg.destroy)
        b.pack(padx=5,side=TOP)
        uu.center_window_on_window(dlg,self.root)
        self.tk.wait_window(dlg)
        

    def do_try_remembered_graph(self,node,T,dlg,names):
        sel = map(int, T.curselection())
        dlg.destroy()
        if sel:
            sg = self.remembered_graphs[names[sel[0]]].copy()
            sg.parent_state = node
            sg.set_state(and_clauses(node.clauses,sg.constraints))
            self.g.state_graphs.append(sg)
            ivy_graph_ui.show_graph(sg,self.tk,parent=self)

    def remember_graph(self,name,graph):
        if not hasattr(self,'remembered_graphs'):
            self.remembered_graphs = {}
        self.remembered_graphs[name] = graph

    def reverse_update_concrete_clauses(self,state, clauses):
        with RunContext(self):
            try:
                if state.pred != None:
                    print "reverse from %s to %s: post_state = %s" % (state.id,state.pred.id,clauses)
                    next_state = state.pred
                    clauses = reverse_update_concrete_clauses(state,clauses)
                    return (clauses,state.pred)

                elif hasattr(state,'join_of') and state.join_of:
                    next_state = state
                    return reverse_join_concrete_clauses(state,state.join_of,clauses)

                return None

            except UnsatCoreWithInterpolant as ici:
#                print "core: %s" % ici.core
#                print "interp: %s" % ici.interp
                if ici.interp != None:
                    used_names = used_symbols_clauses(Clauses([[Literal(0,a)] for a,d in self.g.domain.concept_spaces]))
                    name = unused_name_with_base('itp',set(s.name for s in used_names))
                    concept = clauses_to_concept(name,ici.interp)
                    dlg = Toplevel(self)
                    Label(dlg, text="The pre-state is vacuous. The following concept can be used to prove your goal in the post-state:").pack()
                    S = Scrollbar(dlg)
                    T = Text(dlg, height=4, width=100)
                    S.pack(side=RIGHT, fill=Y)
                    T.pack(side=LEFT, fill=Y)
                    S.config(command=T.yview)
                    T.config(yscrollcommand=S.set)
                    T.insert(END, 'concept ' + repr(concept[0]) + ' = ' + repr(concept[1]))
                    b = Button(dlg, text="OK", command=dlg.destroy)
                    b.pack(padx=5,side=TOP)
                    b = Button(dlg, text="Refine", command=functools.partial(self.refine,concept,dlg))
                    b.pack(padx=5,side=TOP)
                    uu.center_window_on_window(dlg,self.root)
                    self.tk.wait_window(dlg)
                    return (false_clauses(),next_state)
                raise IvyError(None,"UNSAT, but interpolant could not be computed")
        return ([[]],next_state)

    def conjecture(self,state,clauses):
        with RunContext(self):
            return case_conjecture(state,clauses)

    def refine(self,concept,dlg):
        print "concept: {}".format(concept)
        self.g.domain.concept_spaces.append((concept[0],concept[1]))
#        print "current concepts: {}".format(self.g.domain.concept_spaces)
        dlg.destroy()
    def state_label(self,state):
        return str(state.id)
    def add_state_graph(self,sg):
        self.g.state_graphs.append(sg)
    def remove_state_graph(self,sg):
        self.g.state_graphs.remove(sg)

    def bmc(self,state,err_cond):
        res = self.g.bmc(state,err_cond)
        if res == None:
            dlg = Toplevel(self)
            Label(dlg, text="The condition is unreachable along the given path").pack()
            b = Button(dlg, text="OK", command=dlg.destroy)
            b.pack(padx=5,side=TOP)
            uu.center_window_on_window(dlg,self.root)
            self.tk.wait_window(dlg)
            return
        ui_create(res,self.tk,Toplevel(self.tk))
Example #4
0
 def reachable_tree(self):
     if  not hasattr(self,'_reachable_tree'):
         self._reachable_tree = AnalysisGraph(self.g.domain,self.g.pvars)
         for s in self.g.domain.unders[0:1]:
             self._reachable_tree.add(s)
     return self._reachable_tree
Example #5
0
def ui_main_loop(art, tk = None, frame = None):
    if tk == None:
        tk = Tk()
        tk.tk_setPalette(background='white')
        tk.wm_title("ivy")
        frame = tk
    elif frame == None:
        frame = Toplevel(tk)
    ui_create(art,tk,frame)
    tk.mainloop()


if __name__ == '__main__':
    d = ShapeDomain(["x", "y", "t"], True)
    ag = AnalysisGraph(d)
    s = State(d, to_clauses("[[~n(V1,V2)],[~=(x,y)],[~r_x(y)],[~r_y(x)]]")) # start with empty next relation
    ag.add(s)
    ag.execute("alloc t")
    ag.execute("t.n := x")
    s1 = ag.execute("x := t")
    ag.execute("alloc t")
    ag.execute("t.n := x")
    ag.execute("x := t")
    ag.join(s1)
    ag.execute("alloc t")
    ag.execute("t.n := y")
    ag.execute("y := t")
    ag.display()

Example #6
0
 def reachable_tree(self):
     if  not hasattr(self,'_reachable_tree'):
         self._reachable_tree = AnalysisGraph(self.g.domain,self.g.pvars)
         for s in self.g.domain.unders[0:1]:
             self._reachable_tree.add(s)
     return self._reachable_tree
Example #7
0
class AnalysisGraphUI(object):

    # This defines the menus items we provide. The actual menus might be
    # tool buttons or other such things.

    def menus(self):
        return [("menu","File",
                 [("button","Save",self.save),
                  ("button","Save abstraction",self.save_abstraction),
                  ("separator",),
                  ("button","Remove tab",lambda self=self: self.ui_parent.remove(self)),
                  ("button","Exit", lambda self=self: self.ui_parent.exit()),]),
                ("menu","Mode",
                 [("radiobuttons","mode",default_mode.get(),
                   [("Concrete","concrete"),
                    ("Abstract","abstract"),
                    ("Bounded","bounded"),
                    ("Induction","induction")])]),
                 ("menu","Action",
                  [("button","Recalculate all",self.recalculate_all),
                   ("button","Show reachable states",self.show_reachable_states),])]

    # get the mode variable

    @property 
    def mode(self):
        return self.radiobutton('mode')

    # This is called on startup of the ui. We initialize by creating an intitial
    # node in the ART. Whether we abstract or not depends on the mode. For historical
    # reasons, abstract mode uses a concrete initial state.

    def start(self):
        if len(self.g.states) == 0:
            self.g.initialize(self.init_alpha())
        self.rebuild()

    def init_alpha(self):
        if 'mode' in self.radios:
            return (None if (self.mode.get() == "concrete" or self.mode.get() == "abstract")
                    else ivy_alpha.alpha if self.mode.get() == "induction"
                    else top_alpha)
        return top_alpha
        

    # Save the current arg and maybe concept graph in a file

    def save(self):
        f = self.ui_parent.saveas_dialog('Save analysis state as...',[('analysis files', '.a2g')])
        if f:
            pickle.dump(self.g,f, protocol=2)
            f.close()

    # Save the current abstract domain in a file

    def save_abstraction(self):
        f = self.ui_parent.saveas_dialog('Save abstraction as...',[('ivy files', '.ivy')])
        if f:
            for concept in self.g.domain.concept_spaces:
                f.write('concept ' + repr(concept[0]) + ' = ' + repr(concept[1]) + '\n')
            f.close()
    
    # Get the display color of an ARG node. Used by UI.

    def node_color(self,node):
        return "green" if hasattr(node,'safe') and node.safe else "black"

    # Actions to perform on nodes

    def get_node_actions(self,node,click='left'):
        if click == 'left':
            return [("<>",self.view_state)]
        else:
            return ([("Execute action:",None),
                     ("---",None),] +
                    self.node_execute_commands(node) +
                    [("---",None),] +
                    self.node_commands())

    # When left-clicking a node, we view it

    def get_edge_actions(self,transition,click='right'):
        if click == 'left':
            return [] # nothing on right click yet
        else:
            return ([("Dismiss",lambda t: None),
                     ("Recalculate",self.recalculate_edge),
                     ("Decompose",self.decompose_edge),
                     ("View Source",self.view_source_edge),])


    # Show a state in the current concept graph, or create a concept graph

    def view_state(self,n,clauses = None, reset=False):
        clauses = clauses or n.clauses
#        print "state: {}".format(clauses)
        if hasattr(self,'current_concept_graph'):
           #  and self.current_concept_graph.winfo_exists():
#            print "current cg: {}".format(type(self.current_concept_graph))
            self.current_concept_graph.set_parent_state(n,clauses,reset=reset)
            return
        sg = self.g.concept_graph(n,clauses)
        self.current_concept_graph = self.show_graph(sg)
        return self.current_concept_graph

    # TODO: unsure what this does

    def view_concrete_trace(self,n,conc):
        pass
#        self.ui_parent.add(self.g.make_concrete_trace(n,conc))


    # Get the commands for the node context menu

    def node_commands(self):
        return [
          ('Check safety',self.check_safety_node),
          ('Extend',self.find_extension),
          ('Mark',self.mark_node),
          ('Cover by marked',self.cover_node),
          ('Join with marked',self.join_node),
          ('Try conjecture',self.try_conjecture),
          ('Try remembered goal',self.try_remembered_graph),
          ('Delete',self.delete_node),
        ]

    # Get the commands for the node execute menu

    def node_execute_commands(self,n):
        state_equations = self.g.state_actions(n)
        return [(state_equation_label(a), functools.partial(self.do_state_action,a))
                for a in sorted(state_equations, key=state_equation_label)]

    # Set the marked node

    def mark_node(self,n):
        self.show_mark(False)
        self.mark = n
        self.show_mark(True)

    # Get the marked node

    def get_mark(self):
        return self.mark

    # Try covering a node by the marked node

    def cover_node(self,covered_node):
        g = self.g
        covering_node = self.get_mark()
        if covering_node is not None:
            print "Trying to cover %s by %s" % (covered_node.id,covering_node.id)
            with self.ui_parent.run_context():
                if not g.cover(covered_node,covering_node):
                    raise IvyError(None,"Covering failed")
            self.rebuild()

    # Join a node with the marked node

    def join_node(self,node2):
        g = self.g
        try:
            node1 = self.get_mark()
        except:
            return
        with self.ui_parent.run_context():
            g.join(node1,node2,self.get_alpha())
        self.rebuild()

    # Delete a node from the ARG

    def delete_node(self,deleted_node):
        g = self.g
        g.delete(deleted_node)
        self.rebuild()

    # Set the abstract value of a state

    def set_state(self,state,clauses):
        state.clauses = clauses

    # Get the abstraction operator

    def get_alpha(self):
        return (None if self.mode.get() == "concrete"
                else ivy_alpha.alpha if (self.mode.get() == "abstract" or self.mode.get() == "induction")
                else top_alpha)

    # Get the node with given id

    def node(self,id):
        return self.g.states[id]

    # Evaluate a state equation to generate a new node

    def do_state_action(self,a,node=None):
        with self.ui_parent.run_context():
            with EvalContext(check=False):
                print "action {"
                s = self.g.do_state_action(a,self.get_alpha())
                print "state = {}".format(s)
                print "} action %s" % a.args[0]
        self.rebuild()

    # Evaluate an action at a node

    def execute_action(self,n,a):
        with self.ui_parent.run_context():
            print "action %s {" % a
            s = self.g.execute_action(a,n,self.get_alpha())
            print "} action %s" % a
        self.rebuild()

    # Display the reached states tree

    def show_reachable_states(self):
        self.ui_parent.add(self.reachable_tree)

    # Reevaluate all the nodes in the ARG

    def recalculate_all(self):
        done = set()
        for transition in self.g.transitions:
            if transition[-1].id not in done:
                self.recalculate_edge(transition)
                done.add(transition[-1].id)

    # Reevaluate just one edges of the ARG
    # DEPRECATED: use recalculate_state

    def recalculate_edge(self,transition):
        with self.ui_parent.run_context():
            self.g.recalculate(transition,self.get_alpha())
        self.rebuild()

    # Decompose an edge into smaller actions

    def decompose_edge(self,transition):
        with self.ui_parent.run_context():
            art = self.g.decompose_edge(transition)
            if art == None:
                raise IvyError(None,'Cannot decompose action')
            return self.ui_parent.add(art,ui_class=AnalysisGraphUI)

    # Decompose incoming edge of a node into smaller actions

    def decompose_node(self,node):
        with self.ui_parent.run_context():
            art = self.g.decompose_state(node)
            if art == None:
                raise IvyError(None,'Cannot decompose action')
            return self.ui_parent.add(art,ui_class=AnalysisGraphUI)

    # Browse the source of an edge

    def view_source_edge(self,transition):
        with self.ui_parent.run_context():
            act = transition[1]
            assert isinstance(act,Action)
            if hasattr(act,'lineno'):
                filename,lineno = act.lineno.filename,act.lineno.line
                self.ui_parent.browse(filename,lineno)

    # Recalculate a state based on its equation

    def recalculate_state(self,state):
        with self.ui_parent.run_context():
            self.g.recalculate_state(state,self.get_alpha())
        self.rebuild()
        
    # Return a concrete reachability graph with all the known
    # reachable states

    @property
    def reachable_tree(self):
        if  not hasattr(self,'_reachable_tree'):
            self._reachable_tree = AnalysisGraph(self.g.domain,self.g.pvars)
            for s in self.g.domain.unders[0:1]:
                self._reachable_tree.add(s)
        return self._reachable_tree

    # Try to reach a state in one step from known reached states under
    # a constraint. TODO: the computation part should be moved to
    # AnalysisGraph

    def one_step_reach(self,state,clauses):
        with self.ui_parent.run_context():
            rs = reach_state_from_pred_no_abduction(state,clauses)
            if rs != None:
                self.reachable_tree.add(rs)
                t = self.g.transition_to(state)
                if t:
                    pre,action,label,post = t
                    self.reachable_tree.transitions.append((rs.pred,action,label,rs))
                f = filter_conjectures(state,rs.clauses)
                if f:
                    msg = "The following conjectures have been eliminated:"
                    items = [repr(clauses_to_formula(conj)) for conj in f]
                    self.ui_parent.listbox_dialog(msg,items,on_cancel=None)
            return rs

    # Check the safety of a state on a path from the initial
    # state. This is not really BMC checking since only the last state
    # is checked. To be marked safe, the node must satsify any
    # specified safety condition, and the incoming transition must not
    # have any failures.

    def check_bounded_safety(self,node):
        res = self.g.check_bounded_safety(node)
        if res == None:
            node.safe = True
            self.update_node_color(node)
        else:
            node.safe = False
            msg = "The node is unsafe: View error trace?"
            cmd = functools.partial(self.view_ag,res)
            self.ui_parent.ok_cancel_dialog(msg,cmd)

    # Check local safety of a node. A node is locally safe if its
    # abstract state implies any safety conditions and if if its
    # predecessor's abstrafct state implies the weakest precondition
    # of its incoming action.

    def check_local_safety(self,node):
        node.safe = self.g.check_safety(node)
        self.update_node_color(node)
        if not node.safe:
            bcs = []
            if node.safe.clauses != None:
                bcs.append(("View unsafe states",functools.partial(self.view_state,node.safe.state,node.safe.clauses)))
            if node.safe.conc != None:
                bcs.append(("View concrete trace",functools.partial(self.view_concrete_trace,node.safe.state,node.safe.conc)))
            msg = "The node is not proved safe: {}".format(node.safe.msg)
            self.ui_parent.buttons_dialog_cancel(msg,bcs)
        
    # Check safety of a node using the current mode.

    def check_safety_node(self,node):
        if self.mode.get() != "bounded" and self.mode.get() != "induction":
            self.check_local_safety(node)
        else:
            self.check_bounded_safety(node)

    # Add an ARG to the UI

    def view_ag(self,res):
        self.ui_parent.add(res)
            
    # Find an action that can extend the ARG at the given node,
    # without being covered. This is a useful operation for lazy
    # abstraction.

    def find_extension(self,node):
        try:
            with self.ui_parent.run_context():
                a = next(self.g.state_extensions(node))
                self.do_state_action(a)
        except StopIteration:
            self.ui_parent.ok_dialog("State {} is closed.".format(node.id))
            

    # Set up to prove a conjecture at the given node. If no conjecture
    # is given, display a list of not-yet-proven conjectures for the
    # user to choose from. Also browses the source code of the
    # conjecture. The proof method depends on the current mode.

    def try_conjecture(self,node,conj=None,msg=None,bound=None):
        if conj == None:
            udc = undecided_conjectures(node)
            udc_text = [repr(clauses_to_formula(conj)) for conj in udc]
            msg = msg or "Choose a conjecture to prove:"
            cmd = lambda idx: self.try_conjecture(node,udc[idx],bound=bound)
            self.ui_parent.listbox_dialog(msg,udc_text,command=cmd)
        else:
            if hasattr(conj,'lineno'):
                filename,lineno = conj.lineno
                self.ui_parent.browse(filename,lineno)
            dual = dual_clauses(conj)
            if self.mode.get() == "induction":
                iu.dbg('node.clauses')
                iu.dbg('dual')
                self.bmc(node,dual,bound=bound)
            else:
                sg = self.g.concept_graph(node)
                sg.current.add_constraints(dual.conjuncts)
                self.show_graph(sg)

    # Set up to prove a remembered subgoal. If no goal is given, display a list
    # of remembered subgoals for the user. 

    def try_remembered_graph(self,node, goal=None):
        if not hasattr(self,'remembered_graphs'):
            self.remembered_graphs = {}
        if goal == None:
            msg = "Choose a remembered goal:"
            names = [n for n in self.remembered_graphs]
            cmd = lambda idx: self.try_remembered_graph(node,names[idx])
            self.ui_parent.listbox_dialog(msg,names,command=cmd)
        else:
            sg = self.remembered_graphs[goal].copy()
            sg.parent_state = node
            sg.set_state(and_clauses(node.clauses,sg.constraints))
            self.g.state_graphs.append(sg)
            self.show_graph(sg)

    # Save a proof goal under a given name.

    def remember_graph(self,name,graph):
        if not hasattr(self,'remembered_graphs'):
            self.remembered_graphs = {}
        self.remembered_graphs[name] = graph

    # This is the "reverse" step from lazy annotation or IC3, with
    # refinement.  It tries to push back the proof goal "clauses" at
    # node "state" to the predecessor state. If this feasible, the new
    # proof goal is returned as a pair (clauses,state). If infeasible,
    # an interpolant is computed that can be used as a refinement for
    # the current node. In this case, the returned goal is "false". If
    # here is no predecessor node, None is returned.

    def reverse_update_concrete_clauses(self,state, clauses):
        with self.ui_parent.run_context():
            try:
                return self.reverse_goal(state, clauses)
            except UnsatCoreWithInterpolant as ici:
#                print "core: %s" % ici.core
#                print "interp: %s" % ici.interp
                if ici.interp != None:
                    self.refine_with_interpolant(ici.interp)
                    return (false_clauses(),state.pred)
                raise IvyError(None,"UNSAT, but interpolant could not be computed")

    # This is the "reverse" step from lazy annotation or IC3.
    # It tries to push back the proof goal "clauses" at node "state"
    # to the predecessor state. If this feasible, the new proof goal
    # is returned as a pair (clauses,state). If infeasible, an
    # UnsatCoreWithInterpolant exception is raised. If
    # here is no predecessor node, None is returned.

    # TODO: this should really move to ARG, since there are not UI actions

    def reverse_goal(self, state, clauses):
        if state.pred != None:
            print "reverse from %s to %s: post_state = %s" % (state.id,state.pred.id,clauses)
            next_state = state.pred
            clauses = reverse_update_concrete_clauses(state,clauses)
            return (clauses,state.pred)
        elif hasattr(state,'join_of') and state.join_of:
            next_state = state
            return reverse_join_concrete_clauses(state,state.join_of,clauses)
        return None

    # Refine the abstract domain with an interpolant (as a Clauses)
    # Displays the refinement and gives the user the option to apply
    # it.

    def refine_with_interpolant(self,interp):
        concept = self.interp_to_refinement(interp)
        msg = "The pre-state is vacuous. The following concept can be used to prove your goal in the post-state:"
        text = 'concept ' + repr(concept[0]) + ' = ' + repr(concept[1])
        cmd = functools.partial(self.refine,concept)
        self.ui_parent.text_dialog(msg,text,command=cmd,command_label="Refine")

    # Refine the abstract domain with an interpolant (as a Clauses)
    # Displays the refinement and gives the user the option to apply
    # it. TODO: move to ARG.

    def interp_to_refinement(self,interp):
        used_names = used_symbols_clauses(Clauses([[Literal(0,a)] for a,d in self.g.domain.concept_spaces]))
        name = unused_name_with_base('itp',set(s.name for s in used_names))
        return clauses_to_concept(name,interp)

    # Conjecture a separator between state underapprox and clauses.
    # The separator must be true of all models of the underapprox and
    # false in at least one model of clauses.

    def conjecture(self,state,clauses):
        with self.ui_parent.run_context():
            return case_conjecture(state,clauses)

    # Refines the abstract domain by adding a new concept space.

    def refine(self,concept):
        self.g.domain.concept_spaces.append((concept[0],concept[1]))

    # Get a label for an ARG node

    def state_label(self,state):
        return str(state.id)

    # DEPRECATED: add a concept graph to the ARG's list

    def add_state_graph(self,sg):
        pass

    # DEPRECATED: remove a concept graph to the ARG's list

    def remove_state_graph(self,sg):
        pass

    # Bounded reachability: find a concrete path from initial node to
    # a given state satisfying err_cond in state.

    def bmc(self,state,err_cond,bound=None):
        res = self.g.bmc(state,err_cond,bound=bound)
        if res == None:
            msg = "The condition is unreachable along the given path"
            self.ui_parent.ok_dialog(msg)
            return None
        return self.ui_parent.add(res)

    def CGUI(self):
        # This returns the default Concept Graph UI class
        return GraphWidget
Example #8
0
        return sys.modules[__name__]
    return __import__('ivy.ivy_ui_' + defui).__dict__['ivy_ui_' + defui]
    
def get_default_ui_class():
    mod = get_default_ui_module()
    return mod.IvyUI

def get_default_ui_compile_kwargs():
    mod = get_default_ui_module()
    return mod.compile_kwargs
    


if __name__ == '__main__':
    d = ShapeDomain(["x", "y", "t"], True)
    ag = AnalysisGraph(d)
    s = State(d, to_clauses("[[~n(V1,V2)],[~=(x,y)],[~r_x(y)],[~r_y(x)]]")) # start with empty next relation
    ag.add(s)
    ag.execute("alloc t")
    ag.execute("t.n := x")
    s1 = ag.execute("x := t")
    ag.execute("alloc t")
    ag.execute("t.n := x")
    ag.execute("x := t")
    ag.join(s1)
    ag.execute("alloc t")
    ag.execute("t.n := y")
    ag.execute("y := t")
    ag.display()

 def new_ag(self):
     from ivy_art import AnalysisGraph
     ag = AnalysisGraph()
     return ag
Example #10
0
File: ivy_ui.py Project: asyaf/ivy
class AnalysisGraphWidget(Canvas):
    def __init__(self, tk, g, root=None, toplevel=None):
        if root == None:
            root = tk
        if toplevel == None:
            toplevel = root
#        menubar = Menu(toplevel)
        menubar = uu.MenuBar(root)
        menubar.pack(side=TOP, fill=X)
        #        filemenu = Menu(menubar, tearoff=0)
        filemenu = menubar.add("File")
        filemenu.add_command(label="Save", command=self.save)
        filemenu.add_command(label="Save abstraction",
                             command=self.save_abstraction)
        filemenu.add_separator()
        filemenu.add_command(
            label="Remove tab",
            command=lambda self=self: self.ui_parent.remove(self))
        filemenu.add_command(label="Exit", command=tk.quit)
        #        menubar.add_cascade(label="File", menu=filemenu)
        #        modemenu = Menu(menubar, tearoff=0)
        modemenu = menubar.add("Mode")
        self.mode = StringVar(root, default_mode.get())
        modemenu.add("radiobutton",
                     label="Concrete",
                     variable=self.mode,
                     value="concrete")
        modemenu.add("radiobutton",
                     label="Abstract",
                     variable=self.mode,
                     value="abstract")
        modemenu.add("radiobutton",
                     label="Bounded",
                     variable=self.mode,
                     value="bounded")
        #        menubar.add_cascade(label="Mode", menu=modemenu)
        #        actionmenu = Menu(menubar, tearoff=0)
        actionmenu = menubar.add("Action")
        actionmenu.add_command(label="Recalculate all",
                               command=self.recalculate_all)
        actionmenu.add_command(label="Show reachable states",
                               command=self.show_reachable_states)
        #        menubar.add_cascade(label="Action", menu=actionmenu)
        #        toplevel.config(menu=menubar)

        #        sw= Tix.ScrolledWindow(root, scrollbar=BOTH) # just the vertical scrollbar
        #        sw.pack(fill=BOTH, expand=1)

        Canvas.__init__(self, root)
        self.g = g
        self.tk = tk
        self.root = root
        self.mark = None
        tk.eval('package require Tcldot')
        self.pack(fill=BOTH, expand=1)
        self.rebuild()

    def busy(self):
        self.tk.config(cursor="watch")
        self.tk.update()
        self.config(cursor="watch")

    def ready(self):
        self.tk.config(cursor="")
        self.tk.update()
        self.config(cursor="")

    def save(self):
        f = tkFileDialog.asksaveasfile(mode='w',
                                       filetypes=[('analysis files', '.a2g')],
                                       title='Save analysis state as...',
                                       parent=self)
        if f:
            pickle.dump(self.g, f, protocol=2)
            f.close()

    def save_abstraction(self):
        f = tkFileDialog.asksaveasfile(mode='w',
                                       filetypes=[('ivy files', '.ivy')],
                                       title='Save abstraction as...',
                                       parent=self)
        if f:
            for concept in self.g.domain.concept_spaces:
                f.write('concept ' + repr(concept[0]) + ' = ' +
                        repr(concept[1]) + '\n')
            f.close()

    def node_color(self, node):
        return "green" if hasattr(node, 'safe') and node.safe else "black"

    def rebuild(self):
        tk = self.tk
        g = self.g
        tk.eval('set graph [dotnew digraph forcelabels true]')
        handle_to_node = dict()
        self.node_to_handle = dict()
        for (s, i) in zip(g.states, range(len(g.states))):
            p = "%d" % i
            shape = "point" if (s.clauses != None
                                and s.clauses.is_false()) else "circle"
            label = str(s.id) if s.clauses != None else '?'
            color = self.node_color(s)
            handle = tk.eval('$graph addnode "' + p + '" label "' + label +
                             '" shape ' + shape + ' color ' + color +
                             ' fontsize 10 width 0.5 penwidth 2.0')
            handle = 'node' + str(i +
                                  1) if handle.startswith('node0x') else handle
            self.node_to_handle[i] = handle
            handle_to_node[handle] = s
        i = 0
        edge_handles = []
        for transition in g.transitions:
            x, op, label, y = transition
            # Curly braces don't survive tcldot (even if they balance). We work around this by replacing them with digraphs
            # and then fixing it below by modding the text of the canvas items. Also, we want our code labels to be left
            # justified, so we end the lines with \l, which dot recognizes. Note the backslashes are escaped, since this
            # is *not* a special character, it is a two-character sequence.
            label = label.replace('}', ']-').replace('{', '-[')
            label = label.replace('\n', '\\l') + '\\l'
            handle = tk.eval('$graph addedge "' + ("%d" % x.id) + '" "' +
                             ("%d" % y.id) + '" label {' + label +
                             '} fontsize 10 penwidth 2.0')
            handle = 'edge' + str(i +
                                  1) if handle.startswith('edge0x') else handle
            edge_handles.append(handle)
            i += 1
            if isinstance(op, AnalysisSubgraph):
                self.tag_bind("0" + handle,
                              "<Button-1>",
                              lambda y, op=op: op.display(tk))
                self.tag_bind("1" + handle,
                              "<Button-1>",
                              lambda y, op=op: op.display(tk))
            self.tag_bind("0" + handle,
                          "<Button-3>",
                          lambda ev, transition=transition: self.
                          right_click_edge(ev, transition))
            self.tag_bind("1" + handle,
                          "<Button-3>",
                          lambda ev, transition=transition: self.
                          right_click_edge(ev, transition))
        for (x, y) in g.covering:
            handle = tk.eval('$graph addedge "' + ("%d" % x.id) + '" "' +
                             ("%d" % y.id) + '" style dashed')
        self.delete(ALL)
        #        print tk.eval('$graph render ' + self._w  + ' DOT')
        tk.eval('eval [$graph render ' + self._w + ' DOT]')
        self.config(scrollregion=self.bbox(ALL))
        for x in handle_to_node:
            n = handle_to_node[x]
            #            print "x = {}".format(x)
            #            print "n = {}".format(n)
            self.tag_bind("0" + x,
                          "<Button-1>",
                          lambda y, n=n: self.left_click_node(y, n))
            self.tag_bind("1" + x,
                          "<Button-1>",
                          lambda y, n=n: self.left_click_node(y, n))
            self.tag_bind("0" + x,
                          "<Button-3>",
                          lambda y, n=n: self.right_click_node(y, n))
            self.tag_bind("1" + x,
                          "<Button-3>",
                          lambda y, n=n: self.right_click_node(y, n))
        for x in edge_handles:
            items = self.find_withtag("0" + x)
            #            print 'items:{}'.format(items)
            for item in items:
                text = self.itemcget(item, 'text')
                text = text.replace('-[', '{').replace(']-', '}')
                self.itemconfig(item, text=text)
        self.show_mark(True)

    def update_node_color(self, node):
        for item in self.find_withtag("1" + self.node_to_handle[node.id]):
            self.itemconfig(item, outline=self.node_color(node))

    def show_mark(self, on=True):
        if self.mark in self.node_to_handle:
            for item in self.find_withtag("1" +
                                          self.node_to_handle[self.mark]):
                self.itemconfig(item, fill=('red' if on else 'white'))

    def view_state(self, n, clauses):
        print "state: {}".format(clauses)
        if hasattr(self, 'current_concept_graph'
                   ) and self.current_concept_graph.winfo_exists():
            self.current_concept_graph.set_parent_state(n, clauses)
            return
        sg = self.g.concept_graph(n, clauses)
        self.current_concept_graph = ivy_graph_ui.show_graph(
            sg, self.tk, parent=self, frame=self.state_frame)

    def view_concrete_trace(self, n, conc):
        pass


#        ui_create(self.g.make_concrete_trace(n,conc))

    def left_click_node(self, event, n):
        if n.clauses != None:
            self.view_state(n, n.clauses)

    def right_click_node(self, event, n):
        tk = self.tk
        g = self.g
        self.popup = Menu(tk, tearoff=0)
        self.popup.add_command(label="Execute action:")
        self.popup.add_separator()
        state_equations = g.state_actions(n)
        for a in sorted(state_equations, key=state_equation_label):
            self.popup.add_command(
                label=state_equation_label(a),
                command=lambda n=n, a=a: self.do_state_action(a))
        self.popup.add_separator()
        self.popup.add_command(label='Check safety',
                               command=lambda n=n: self.check_safety_node(n))
        self.popup.add_command(label='Extend',
                               command=lambda n=n: self.find_extension(n))
        self.popup.add_command(label='Mark',
                               command=lambda n=n: self.mark_node(n))
        self.popup.add_command(label='Cover by marked',
                               command=lambda n=n: self.cover_node(n))
        self.popup.add_command(label='Join with marked',
                               command=lambda n=n: self.join_node(n))
        self.popup.add_command(label='Try conjecture',
                               command=lambda n=n: self.try_conjecture(n))
        self.popup.add_command(
            label='Try remembered goal',
            command=lambda n=n: self.try_remembered_graph(n))
        self.popup.add_command(label='Delete',
                               command=lambda n=n: self.delete_node(n))
        self.popup.tk_popup(event.x_root, event.y_root, 0)

    def mark_node(self, n):
        self.show_mark(False)
        self.mark = n.id
        self.show_mark(True)

    def get_mark(self):
        return self.g.states[self.mark]

    def cover_node(self, covered_node):
        g = self.g
        try:
            covering_node = self.get_mark()
        except:
            return
        print "Trying to cover %s by %s" % (covered_node.id, covering_node.id)
        with RunContext(self):
            if not g.cover(covered_node, covering_node):
                raise IvyError(None, "Covering failed")
        self.rebuild()

    def join_node(self, node2):
        g = self.g
        try:
            node1 = self.get_mark()
        except:
            return
        with RunContext(self):
            g.join(node1, node2, self.get_alpha())
        self.rebuild()

    def delete_node(self, deleted_node):
        g = self.g
        g.delete(deleted_node)
        self.rebuild()

    def right_click_edge(self, event, transition):
        tk = self.tk
        g = self.g
        self.popup = Menu(tk, tearoff=0)
        self.popup.add_command(label="Dismiss")
        self.popup.add_command(label="Recalculate",
                               command=lambda transition=transition: self.
                               recalculate_edge(transition))
        self.popup.add_command(label="Decompose",
                               command=lambda transition=transition: self.
                               decompose_edge(transition))
        self.popup.add_command(label="View Source",
                               command=lambda transition=transition: self.
                               view_source_edge(transition))
        self.popup.tk_popup(event.x_root, event.y_root, 0)

    def set_state(self, state, clauses):
        state.clauses = clauses

    def get_alpha(self):
        return (None if self.mode.get() == "concrete" else ivy_alpha.alpha
                if self.mode.get() == "abstract" else top_alpha)

    def do_state_action(self, a):
        with RunContext(self):
            with EvalContext(check=False):
                print "action {"
                s = self.g.do_state_action(a, self.get_alpha())
                print "state = {}".format(s)
                print "} action %s" % a.args[0]
        self.rebuild()

    def execute_action(self, n, a):
        with RunContext(self):
            print "action %s {" % a
            s = self.g.execute_action(a, n, self.get_alpha())
            print "} action %s" % a
        self.rebuild()

    def show_reachable_states(self):
        ui_create(self.reachable_tree)

    def recalculate_all(self):
        done = set()
        for transition in self.g.transitions:
            if transition[-1].id not in done:
                self.recalculate_edge(transition)
                done.add(transition[-1].id)

    def recalculate_edge(self, transition):
        with RunContext(self):
            self.g.recalculate(transition, self.get_alpha())
        self.rebuild()

    def decompose_edge(self, transition):
        with RunContext(self):
            art = self.g.decompose_edge(transition)
            if art == None:
                raise IvyError(None, 'Cannot decompose action')
            ui_create(art)

    def view_source_edge(self, transition):
        with RunContext(self):
            act = transition[1]
            assert isinstance(act, Action)
            if hasattr(act, 'lineno'):
                filename, lineno = act.lineno
                self.ui_parent.browse(filename, lineno)

    def recalculate_state(self, state):
        with RunContext(self):
            self.g.recalculate_state(state, self.get_alpha())
        self.rebuild()

    @property
    def reachable_tree(self):
        if not hasattr(self, '_reachable_tree'):
            self._reachable_tree = AnalysisGraph(self.g.domain, self.g.pvars)
            for s in self.g.domain.unders[0:1]:
                self._reachable_tree.add(s)
        return self._reachable_tree

    def one_step_reach(self, state, clauses):
        with RunContext(self):
            rs = reach_state_from_pred_no_abduction(state, clauses)
            if rs != None:
                self.reachable_tree.add(rs)
                t = self.g.transition_to(state)
                if t:
                    pre, action, label, post = t
                    self.reachable_tree.transitions.append(
                        (rs.pred, action, label, rs))
                f = filter_conjectures(state, rs.clauses)
                if f:
                    dlg = Toplevel(self)
                    Label(
                        dlg,
                        text="The following conjectures have been eliminated:"
                    ).pack()
                    S = Scrollbar(dlg)
                    T = Listbox(dlg, height=8, width=50, selectmode=SINGLE)
                    S.pack(side=RIGHT, fill=Y)
                    T.pack(side=LEFT, fill=Y)
                    S.config(command=T.yview)
                    T.config(yscrollcommand=S.set)
                    for conj in f:
                        T.insert(END, repr(clauses_to_formula(conj)))
                    b = Button(dlg, text="OK", command=dlg.destroy)
                    b.pack(padx=5, side=TOP)
                    uu.center_window_on_window(dlg, self.root)
                    self.tk.wait_window(dlg)
            return rs

    def check_safety_node(self, node):
        if self.mode.get() != "bounded":
            node.safe = self.g.check_safety(node)
            self.update_node_color(node)
            if not node.safe:
                bcs = []
                if node.safe.clauses != None:
                    bcs.append(
                        ("View unsafe states",
                         functools.partial(self.view_state, node.safe.state,
                                           node.safe.clauses)))
                if node.safe.conc != None:
                    bcs.append(
                        ("View concrete trace",
                         functools.partial(self.view_concrete_trace,
                                           node.safe.state, node.safe.conc)))
                uu.buttons_dialog_cancel(
                    self.tk, self.root,
                    "The node is not proved safe: {}".format(node.safe.msg),
                    bcs)
        else:
            #                print "bounded check: node.safe.clauses={}".format(node.safe.clauses)
            res = self.g.check_bounded_safety(node)
            if res == None:
                node.safe = True
                self.update_node_color(node)
            else:
                node.safe = False
                uu.ok_cancel_dialog(self.tk,
                                    self.root,
                                    "The node is unsafe: View error trace?",
                                    command=functools.partial(
                                        self.view_ag, res))

    def view_ag(self, res):
        ui_create(res)

    def find_extension(self, node):
        try:
            with RunContext(self):
                a = next(self.g.state_extensions(node))
                self.do_state_action(a)
        except StopIteration:
            uu.ok_dialog(self.tk, self.root,
                         "State {} is closed.".format(node.id))

    def try_conjecture(self, node):
        dlg = Toplevel(self)
        lbl = "Choose a conjecture to prove:"
        Label(dlg, text=lbl).pack()
        S = Scrollbar(dlg)
        T = Listbox(dlg, height=8, width=50, selectmode=SINGLE)
        S.pack(side=RIGHT, fill=Y)
        T.pack(side=LEFT, fill=Y)
        S.config(command=T.yview)
        T.config(yscrollcommand=S.set)
        udc = undecided_conjectures(node)
        for conj in udc:
            T.insert(END, repr(clauses_to_formula(conj)))
        b = Button(dlg,
                   text="Prove",
                   command=functools.partial(self.do_try_conjecture, node, T,
                                             dlg, udc))
        b.pack(padx=5, side=TOP)
        b = Button(dlg, text="Cancel", command=dlg.destroy)
        b.pack(padx=5, side=TOP)
        uu.center_window_on_window(dlg, self.root)
        self.tk.wait_window(dlg)

    def do_try_conjecture(self, node, T, dlg, udc):
        sel = map(int, T.curselection())
        if sel:
            conj = udc[sel[0]]
            print "type(conj) = {}".format(type(conj))
            dual = dual_clauses(conj)
            sg = self.g.concept_graph(node)
            sg.add_constraints(dual.clauses)
            ivy_graph_ui.show_graph(sg, self.tk, parent=self)
        dlg.destroy()

    def try_remembered_graph(self, node):
        dlg = Toplevel(self)
        lbl = "Choose a remembered goal:"
        Label(dlg, text=lbl).pack()
        S = Scrollbar(dlg)
        T = Listbox(dlg, height=8, width=50, selectmode=SINGLE)
        S.pack(side=RIGHT, fill=Y)
        T.pack(side=LEFT, fill=Y)
        S.config(command=T.yview)
        T.config(yscrollcommand=S.set)
        if not hasattr(self, 'remembered_graphs'):
            self.remembered_graphs = {}
        names = [n for n in self.remembered_graphs]
        for name in names:
            T.insert(END, name)
        b = Button(dlg,
                   text="Try",
                   command=functools.partial(self.do_try_remembered_graph,
                                             node, T, dlg, names))
        b.pack(padx=5, side=TOP)
        b = Button(dlg, text="Cancel", command=dlg.destroy)
        b.pack(padx=5, side=TOP)
        uu.center_window_on_window(dlg, self.root)
        self.tk.wait_window(dlg)

    def do_try_remembered_graph(self, node, T, dlg, names):
        sel = map(int, T.curselection())
        dlg.destroy()
        if sel:
            sg = self.remembered_graphs[names[sel[0]]].copy()
            sg.parent_state = node
            sg.set_state(and_clauses(node.clauses, sg.constraints))
            self.g.state_graphs.append(sg)
            ivy_graph_ui.show_graph(sg, self.tk, parent=self)

    def remember_graph(self, name, graph):
        if not hasattr(self, 'remembered_graphs'):
            self.remembered_graphs = {}
        self.remembered_graphs[name] = graph

    def reverse_update_concrete_clauses(self, state, clauses):
        with RunContext(self):
            try:
                if state.pred != None:
                    print "reverse from %s to %s: post_state = %s" % (
                        state.id, state.pred.id, clauses)
                    next_state = state.pred
                    clauses = reverse_update_concrete_clauses(state, clauses)
                    return (clauses, state.pred)

                elif hasattr(state, 'join_of') and state.join_of:
                    next_state = state
                    return reverse_join_concrete_clauses(
                        state, state.join_of, clauses)

                return None

            except UnsatCoreWithInterpolant as ici:
                #                print "core: %s" % ici.core
                #                print "interp: %s" % ici.interp
                if ici.interp != None:
                    used_names = used_symbols_clauses(
                        Clauses([[Literal(0, a)]
                                 for a, d in self.g.domain.concept_spaces]))
                    name = unused_name_with_base(
                        'itp', set(s.name for s in used_names))
                    concept = clauses_to_concept(name, ici.interp)
                    dlg = Toplevel(self)
                    Label(
                        dlg,
                        text=
                        "The pre-state is vacuous. The following concept can be used to prove your goal in the post-state:"
                    ).pack()
                    S = Scrollbar(dlg)
                    T = Text(dlg, height=4, width=100)
                    S.pack(side=RIGHT, fill=Y)
                    T.pack(side=LEFT, fill=Y)
                    S.config(command=T.yview)
                    T.config(yscrollcommand=S.set)
                    T.insert(
                        END, 'concept ' + repr(concept[0]) + ' = ' +
                        repr(concept[1]))
                    b = Button(dlg, text="OK", command=dlg.destroy)
                    b.pack(padx=5, side=TOP)
                    b = Button(dlg,
                               text="Refine",
                               command=functools.partial(
                                   self.refine, concept, dlg))
                    b.pack(padx=5, side=TOP)
                    uu.center_window_on_window(dlg, self.root)
                    self.tk.wait_window(dlg)
                    return (false_clauses(), next_state)
                raise IvyError(None,
                               "UNSAT, but interpolant could not be computed")
        return ([[]], next_state)

    def conjecture(self, state, clauses):
        with RunContext(self):
            return case_conjecture(state, clauses)

    def refine(self, concept, dlg):
        print "concept: {}".format(concept)
        self.g.domain.concept_spaces.append((concept[0], concept[1]))
        #        print "current concepts: {}".format(self.g.domain.concept_spaces)
        dlg.destroy()

    def state_label(self, state):
        return str(state.id)

    def add_state_graph(self, sg):
        self.g.state_graphs.append(sg)

    def remove_state_graph(self, sg):
        self.g.state_graphs.remove(sg)

    def bmc(self, state, err_cond):
        res = self.g.bmc(state, err_cond)
        if res == None:
            dlg = Toplevel(self)
            Label(dlg,
                  text="The condition is unreachable along the given path"
                  ).pack()
            b = Button(dlg, text="OK", command=dlg.destroy)
            b.pack(padx=5, side=TOP)
            uu.center_window_on_window(dlg, self.root)
            self.tk.wait_window(dlg)
            return
        ui_create(res)