| 1 | % (c) 2014-2022 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, | |
| 2 | % Heinrich Heine Universitaet Duesseldorf | |
| 3 | % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) | |
| 4 | ||
| 5 | :- module(enable_graph, [ | |
| 6 | print_enable_graph_dot/2 % predicate called from Tcl/Tk interface | |
| 7 | ,add_vertex/3, add_neighbour/5, edge_labels/2 % should be deleted (exported just for testing) | |
| 8 | ,path_set/8 | |
| 9 | ]). | |
| 10 | ||
| 11 | /* Modules and Infos for the code coverage analysis */ | |
| 12 | :- use_module(probsrc(module_information)). | |
| 13 | :- module_info(group,model_checker). | |
| 14 | :- module_info(description,'This module provides operations of directed enable graphs.'). | |
| 15 | ||
| 16 | % Standard SICSTUS prolog libraries | |
| 17 | :- use_module(library(lists)). | |
| 18 | ||
| 19 | % Classical B prolog modules | |
| 20 | :- use_module(probsrc(bmachine),[b_get_machine_operation/4,b_top_level_operation/1]). | |
| 21 | :- use_module(probsrc(b_interpreter),[b_test_boolean_expression_cs/5]). | |
| 22 | :- use_module(probsrc(bsyntaxtree), [get_texpr_expr/2]). | |
| 23 | ||
| 24 | % Event-B prolog modules | |
| 25 | %:- use_module(probsrc(b_interpreter_eventb),[b_event/6, b_event_with_change_set/7]). | |
| 26 | ||
| 27 | % Prolog modules handling the state space | |
| 28 | :- use_module(probsrc(store), [empty_state/1,store_updates_and_normalise/3]). | |
| 29 | ||
| 30 | % Prolog modules declaring predicates relevant for partial order reduction optimisation | |
| 31 | %% :- use_module(probporsrc(static_analysis),[dependent_act/2]). | |
| 32 | :- use_module(cbcsrc(enabling_analysis),[translate_enable_res/6, eop_node_predicate/6]). | |
| 33 | ||
| 34 | % Other modules (used mostly for debugging) | |
| 35 | :- use_module(dotsrc(dot_graph_generator), [gen_dot_graph/3]). | |
| 36 | :- use_module(probsrc(translate),[translate_bexpression/2]). | |
| 37 | %:- use_module(probsrc(preferences),[get_preference/2]). | |
| 38 | ||
| 39 | % Importing unit tests predicates | |
| 40 | :- use_module(probsrc(self_check)). | |
| 41 | ||
| 42 | /* | |
| 43 | An enable graph is represented as a list of pairs (vertex-edgelist), the edge list is a list of pairs (neighbor-enabling_predicate). | |
| 44 | The module follows the definition of enable graph from 'Automatic Flow Analysis for Event-B'. An enable graph can be used, for example, | |
| 45 | for the analysis of the event relations in a Event-B machine that is necessary for optimisations like partial order reduction and | |
| 46 | partial guard evaluation. | |
| 47 | */ | |
| 48 | ||
| 49 | % Unit tests | |
| 50 | :- assert_must_succeed((enable_graph: vertices_edges_predicates_to_egraph([a,b,d,e,q],[a-1-d,a-2-b,d-4-q,e-1-a,e-4-b,q-3-c],R), R = [a-[b-2,d-1],b-[],d-[q-4],e-[a-1,b-4],q-[c-3]])). | |
| 51 | :- assert_must_succeed((enable_graph: vertices_edges_predicates_to_egraph([],[a-1-d,a-2-b,d-4-q,e-1-a,e-4-b,q-3-c],R), R = [a-[b-2,d-1],d-[q-4],e-[a-1,b-4],q-[c-3]])). | |
| 52 | :- assert_must_succeed((enable_graph: vertices_edges_predicates_to_egraph([],[],R), R = [])). | |
| 53 | :- assert_must_succeed((enable_graph: add_neighbour([2-[3-v],4-[1-w,4-p],5-[]],2,q,3,R), R = [2-[3-q,3-v],4-[1-w,4-p],5-[]])). | |
| 54 | :- assert_must_succeed((enable_graph: add_neighbour([2-[3-v],4-[1-w,4-p],5-[]],1,q,3,R), R = [1-[3-q],2-[3-v],4-[1-w,4-p],5-[]])). | |
| 55 | :- assert_must_succeed((enable_graph: add_neighbour([2-[3-v],4-[1-w,4-p],5-[]],6,q,3,R), R = [2-[3-v],4-[1-w,4-p],5-[],6-[3-q]])). | |
| 56 | :- assert_must_succeed((enable_graph: add_vertex([a-2,b-3,d-4],aa,R), R = [a-2,aa-[],b-3,d-4])). | |
| 57 | :- assert_must_succeed((enable_graph: add_vertex([a-2,b-3,d-4],'A',R), R = ['A'-[],a-2,b-3,d-4])). | |
| 58 | :- assert_must_succeed((enable_graph: add_vertex([],'A',R), R = ['A'-[]])). | |
| 59 | ||
| 60 | vertices_edges_predicates_to_egraph(Vertices,Edges,EnableGraph) :- | |
| 61 | add_vertices([],Vertices,EnableGraph1), | |
| 62 | add_edges(EnableGraph1,Edges,EnableGraph). | |
| 63 | ||
| 64 | add_vertices(EnableGraph,[],EnableGraph). | |
| 65 | add_vertices(EnableGraph,[V|Vs],NewEnableGraph) :- | |
| 66 | add_vertex(EnableGraph,V,EnableGraph1), | |
| 67 | add_vertices(EnableGraph1,Vs,NewEnableGraph). | |
| 68 | ||
| 69 | %% Using difference lists for constructing the enable graph | |
| 70 | add_vertex([],V,[V-[]]). | |
| 71 | add_vertex([V-NB|Rest],V,[V-NB|Rest]) :- !. | |
| 72 | add_vertex([W-NB|Rest],V,[V-[],W-NB|Rest]) :- | |
| 73 | W @> V,!. | |
| 74 | add_vertex([W-NB|Rest],V,[W-NB|S]) :- | |
| 75 | add_vertex(Rest,V,S). | |
| 76 | ||
| 77 | add_edges(EnableGraph,[],EnableGraph). | |
| 78 | add_edges(EnableGraph,[From-Predicate-To|Edges],NewEnableGraph) :- | |
| 79 | add_neighbour(EnableGraph,From,Predicate,To,EnableGraph1), | |
| 80 | add_edges(EnableGraph1,Edges,NewEnableGraph). | |
| 81 | ||
| 82 | add_neighbour([],From,Predicate,To,[From-[To-Predicate]]). | |
| 83 | add_neighbour([V-Neighbours|Rest],From,Predicate,To,[V-NewNeighbours|Rest]) :- | |
| 84 | From==V,!, | |
| 85 | (member(To-Predicate,Neighbours) -> | |
| 86 | NewNeighbours = Neighbours | |
| 87 | ; sort([To-Predicate|Neighbours],NewNeighbours) | |
| 88 | ). | |
| 89 | add_neighbour([V-Neighbours|Rest],From,Predicate,To,[From-[To-Predicate],V-Neighbours|Rest]) :- | |
| 90 | V @> From,!. | |
| 91 | add_neighbour([V-Neighbours|Rest],From,Predicate,To,[V-Neighbours|S]) :- | |
| 92 | add_neighbour(Rest,From,Predicate,To,S). | |
| 93 | ||
| 94 | edge_labels(EnableGraph,Labels) :- | |
| 95 | edge_labels(EnableGraph,[],Labels). | |
| 96 | ||
| 97 | edge_labels([],Labels,Labels). | |
| 98 | edge_labels([_V-NB|Rest],LabelsSoFar,Labels) :- | |
| 99 | maplist(get_vertex_label,NB,Ls), | |
| 100 | append(LabelsSoFar,Ls,LabelsSoFar1), | |
| 101 | edge_labels(Rest,LabelsSoFar1,Labels). | |
| 102 | ||
| 103 | get_vertex_label(_Dst-P,P). | |
| 104 | ||
| 105 | path_set(EnableGraph,Src,Goals,Module,Predicate,EvaluationDepth,[Src|Path],Length) :- | |
| 106 | path_set1(EnableGraph,Src,Goals,Module,Predicate,EvaluationDepth,[],Path,0,Length). | |
| 107 | ||
| 108 | path_set1(_EnableGraph,Goal,Goals,_Module,_Predicate,_EvaluationDepth,_CurPath,[],Length,Length) :- | |
| 109 | member(Goal,Goals). | |
| 110 | path_set1(EnableGraph,Src,Goals,Module,Predicate,EvaluationDepth,CurPath,Path,CurLength,Length) :- | |
| 111 | nonmember(Src,Goals), | |
| 112 | (EvaluationDepth==0 -> EvaluatePredicate=fail; EvaluatePredicate=true), | |
| 113 | predicate_edge(EnableGraph,Src,Next,CurPath,Module,Predicate,EvaluatePredicate,NewPredicate), | |
| 114 | NewLength is CurLength +1, | |
| 115 | decrease_evaluation_depth(EvaluationDepth,NewEvaluationDepth), | |
| 116 | Path = [Next|PathTail], | |
| 117 | path_set1(EnableGraph,Next,Goals,Module,NewPredicate,NewEvaluationDepth,[Next|CurPath],PathTail,NewLength,Length). | |
| 118 | ||
| 119 | decrease_evaluation_depth(EvaluationDepth,NewEvaluationDepth) :- | |
| 120 | (EvaluationDepth>0 -> | |
| 121 | NewEvaluationDepth is EvaluationDepth -1 | |
| 122 | ; NewEvaluationDepth=EvaluationDepth | |
| 123 | ). | |
| 124 | ||
| 125 | % Checks if there is an edge between V1 and V2 and additionally tests whether the | |
| 126 | % Label (e.g. the enabling predicate) evaluates to true. If one of both is not | |
| 127 | % fullfilled then the call should fail. | |
| 128 | predicate_edge(EnableGraph,V1,V2,CurPath,Module,Predicate,EvaluatePredicate,NewPredicate) :- | |
| 129 | edge(EnableGraph,V1,V2,P), | |
| 130 | nonmember(V2,CurPath), | |
| 131 | call(Module:Predicate,P,EvaluatePredicate,V1,V2,NewPredicate). | |
| 132 | ||
| 133 | edge(EnableGraph,V1,V2,Predicate) :- | |
| 134 | member(V1-NB,EnableGraph), | |
| 135 | member(V2-Predicate,NB). | |
| 136 | ||
| 137 | :- public evaluate_enabling_predicate_for_por/7. % used as argument to path_set | |
| 138 | % TODO: The implementation should be removed to another module | |
| 139 | % In the context of POR the DoNotVisit set is the set of the currently computed ample set. | |
| 140 | % predicate(State,DoNotVisit,++EvaluatePredicate,) | |
| 141 | evaluate_enabling_predicate_for_por(State,DoNotVisit,P,EvaluatePredicate,OpName1,OpName2,NewPredicate) :- | |
| 142 | nonmember(OpName2,DoNotVisit), | |
| 143 | empty_state(LocalState), | |
| 144 | (EvaluatePredicate,State\=[] -> | |
| 145 | % V2 is enabled after V1, we need to execute V1 in oder to generate the next state | |
| 146 | % TODO: maybe we should set a sort of time-out to decrease the overhead in some cases | |
| 147 | !, | |
| 148 | b_test_boolean_expression_cs(P,LocalState,State,'POR enabling',OpName1:OpName2), | |
| 149 | % the success of the predicate should guarantee that no well-definedness-errors occur | |
| 150 | % when OpName1 is executed | |
| 151 | execute_operation(OpName1,LocalState,State,NextState) % TODO: handle case for non-deterministic operations | |
| 152 | ; % just checking whether there is an edge between V1 and V2 | |
| 153 | NextState=[] % TODO: what should we give as next state | |
| 154 | ), | |
| 155 | NewPredicate = evaluate_enabling_predicate_for_por(NextState,DoNotVisit). | |
| 156 | ||
| 157 | execute_operation(OpName,LocalState,State,NewState) :- | |
| 158 | %% (OpName = 'T3writebus' -> trace; true), | |
| 159 | b_get_machine_operation(OpName,_Res,Par,Body), | |
| 160 | %% print(expr(Body)),nl, | |
| 161 | get_texpr_expr(Body,Expr), | |
| 162 | (Par = [] -> | |
| 163 | execute_expression(OpName,Expr,LocalState,State,Updates) | |
| 164 | ; b_interpreter: b_execute_top_level_operation_update(OpName,_FullOp,State,Updates,_Path) | |
| 165 | %b_interpreter: b_execute_operation_with_parameters(OpName,State,Par,Updates,Res,_Path,_WF,output_required) | |
| 166 | ), | |
| 167 | %print(executed_operation(OpName)),nl, | |
| 168 | % guarantee that Updates is not a variable here | |
| 169 | %% print(states(Updates,State)),nl, | |
| 170 | store_updates_and_normalise(Updates,State,NewState). | |
| 171 | ||
| 172 | :- use_module(probsrc(preferences),[temporary_set_preference/3,reset_temporary_preference/2]). | |
| 173 | % do not evaluate the precondition | |
| 174 | execute_expression(_OpName,precondition(_Condition,Body),LocalState,State,Updates) :- !, | |
| 175 | b_interpreter: b_execute_inner_statement(Body,LocalState,State,Updates,_WF,_IPath,_OR). | |
| 176 | execute_expression(OpName,Stmt,_LocalState,State,Updates) :- | |
| 177 | functor(Stmt,rlevent,11),!, | |
| 178 | % Stmt = rlevent(Name,Section,_Status,_Params,Guard,_Theorems,_Actions,_VWitnesses,_PWitnesses,_Unmod,_AbstractEvents) | |
| 179 | temporary_set_preference(ltsmin_do_not_evaluate_top_level_guards,full,CHNG), | |
| 180 | %bsyntaxtree:safe_create_texpr(Stmt,event,Event), | |
| 181 | %b_interpreter_eventb:b_execute_event(OpName,Event,[],[],State,OutputState,_Path), | |
| 182 | %b_interpreter_eventb:b_event_with_change_set(Event,[],[],State,_ChangeSet,Updates,_Path), | |
| 183 | b_interpreter:b_execute_top_level_operation_update(OpName,_Operation,State,Updates,_PathInfo), | |
| 184 | reset_temporary_preference(ltsmin_do_not_evaluate_top_level_guards,CHNG). | |
| 185 | execute_expression(_OpName,Stmt,LocalState,State,Updates) :- | |
| 186 | b_interpreter: b_execute_statement(Stmt,LocalState,State,Updates,_WF,_Path). | |
| 187 | % TODO: export b_execute_inner_statement/7 and b_execute_statement/6 in b_interpreter.pl module | |
| 188 | ||
| 189 | :- dynamic asserted_enable_graph/1. | |
| 190 | ||
| 191 | print_enable_graph_dot(EnableGraph,File) :- | |
| 192 | assertz(asserted_enable_graph(EnableGraph)), | |
| 193 | gen_dot_graph(File,dot_eop_node_predicate,enable_trans_predicate), | |
| 194 | retractall(asserted_enable_graph(_)). | |
| 195 | ||
| 196 | dot_eop_node_predicate(NodeID,SubGraph,NodeDesc,Shape,Style,Color):- | |
| 197 | eop_node_predicate(NodeID,SubGraph,NodeDesc,Shape,Style,Color). | |
| 198 | ||
| 199 | enable_trans_predicate(NodeID,Label,SuccID,Color,Style) :- | |
| 200 | b_top_level_operation(OpName1), | |
| 201 | b_top_level_operation(OpName2), | |
| 202 | asserted_enable_graph(EnableGraph), | |
| 203 | edge(EnableGraph,OpName1,OpName2,Predicate), | |
| 204 | translate_bexpression(Predicate,Res), | |
| 205 | translate_enable_res(Res,NodeID,SuccID,Label,Color,Style). |