1 | % (c) 2009-2024 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(state_space, | |
6 | [get_state_space_stats/3, get_state_space_stats/4, | |
7 | gen_new_state_id/1, | |
8 | history/1, forward_history/1, | |
9 | get_action_trace/1, % old trace/1 | |
10 | get_action_trace_with_limit/2, | |
11 | get_action_term_trace/1, | |
12 | op_trace_ids/1, add_to_op_trace_ids/1, remove_from_op_trace_ids/1, reset_op_trace_ids/0, | |
13 | get_state_id_trace/1, | |
14 | current_state_id/1, current_expression/2, | |
15 | set_current_state_id/1, | |
16 | current_options/1, set_current_options/1, | |
17 | get_current_predecessor_state_id/1, | |
18 | ||
19 | add_id_at_front/1, add_id_at_end/1, | |
20 | add_id_random/1, /* from state_space_open_nodes_c */ | |
21 | add_id_with_weight/2, add_id_to_process/3, | |
22 | pop_id_from_front/1, pop_id_from_end/1, pop_id_oldest/1, | |
23 | retract_open_node/1, open_ids_empty/0, | |
24 | top_front_id/1, top_front_weight/1, | |
25 | ||
26 | visited_expression/3, visited_expression/2, visited_expression_id/1, | |
27 | find_hashed_packed_visited_expression/3, | |
28 | retract_visited_expression/2, | |
29 | not_all_transitions_added/1, | |
30 | not_invariant_checked/1, set_invariant_checked/1, | |
31 | invariant_not_yet_checked/1, invariant_still_to_be_checked/1, | |
32 | not_interesting/1, % nodes ignored because they do not satisfy a user-provided scope predicate | |
33 | mark_as_not_interesting/1, | |
34 | max_reached_for_node/1, | |
35 | max_reached_or_timeout_for_node/1, | |
36 | use_no_timeout/1, time_out_for_node/1, time_out_for_node/3, | |
37 | hash_to_id/2,id_to_marker/2, hash_to_nauty_id/2, | |
38 | register_back_edge/2, try_compute_depth_of_state_id/2, | |
39 | invariant_violated/1, time_out_for_invariant/1, time_out_for_assertions/1, | |
40 | set_invariant_violated/1, | |
41 | ||
42 | state_error_exists/0, state_error/3, store_state_error/3, | |
43 | set_context_state/1, set_context_state/2, | |
44 | update_context_state/1, clear_context_state/0, get_current_context_state/1, | |
45 | store_error_for_context_state/2, | |
46 | copy_current_errors_to_state/2, | |
47 | store_abort_error_for_context_state_if_possible/4, | |
48 | abort_error_exists_in_context_state/1, | |
49 | ||
50 | transition/3,transition/4, any_transition/3, | |
51 | store_transition/4, | |
52 | deadlocked_state/1, % no outgoing edge | |
53 | is_initial_state_id/1, is_concrete_constants_state_id/1, | |
54 | multiple_concrete_constants_exist/0, | |
55 | get_constants_state_for_id/2, get_constants_state_id_for_id/2, | |
56 | try_get_unique_constants_state/1, | |
57 | get_constants_id_for_state_id/2, | |
58 | get_variables_state_for_id/2, | |
59 | out_degree/2, | |
60 | operation_not_yet_covered/1, operation_name_not_yet_covered/1, | |
61 | get_operation_name_coverage_infos/4, | |
62 | mark_operation_as_covered/1, | |
63 | initialise_operation_not_yet_covered/0, | |
64 | ||
65 | transition_info/2, store_transition_infos/2, | |
66 | keep_transition_info/1, | |
67 | compute_transitions_if_necessary/1, | |
68 | ||
69 | state_space_initialise/0, state_space_initialise_with_stats/0, | |
70 | state_space_reset/0, | |
71 | state_space_add/2, state_space_packed_add/2, | |
72 | delete_node/1, | |
73 | ||
74 | current_state_corresponds_to_initialised_b_machine/0, | |
75 | current_state_corresponds_to_fully_setup_b_machine/0, | |
76 | current_state_corresponds_to_setup_constants_b_machine/0, | |
77 | visited_state_corresponds_to_initialised_b_machine/1, | |
78 | visited_state_corresponds_to_setup_constants_b_machine/1, | |
79 | ||
80 | specialized_inv/2, %reuse_operation/4, | |
81 | assert_max_reached_for_node/1, assert_time_out_for_node/3, | |
82 | assert_time_out_for_invariant/1, assert_time_out_for_assertions/1, | |
83 | ||
84 | set_max_nr_of_new_impl_trans_nodes/1, | |
85 | get_max_nr_of_new_impl_trans_nodes/1, | |
86 | impl_trans_term/3, impl_trans_term_all/2, | |
87 | impl_trans_id/4, impl_trans_not_complete/1, | |
88 | compute_transitions_if_necessary_saved/1, | |
89 | max_nr_of_new_nodes_limit_not_reached/0, | |
90 | ||
91 | find_trace_to_initial_state/2, find_initialised_states/1, | |
92 | ||
93 | tcltk_save_state_space/1, tcltk_load_state/1, | |
94 | compute_full_state_space_hash/1, | |
95 | ||
96 | execute_id_trace_from_current/3, | |
97 | set_trace_by_transition_ids/1, try_set_trace_by_transition_ids/1, | |
98 | extend_trace_by_transition_ids/1, | |
99 | extract_term_trace_from_transition_ids/2, | |
100 | ||
101 | add_counterexample_node/1, add_counterexample_op/1, | |
102 | reset_counterexample/0, set_counterexample_by_transition_ids/1, | |
103 | counterexample_node/1, counterexample_op/1 % specific predicates to register counter examples | |
104 | ]). | |
105 | ||
106 | :- use_module(library(lists)). | |
107 | ||
108 | :- use_module(self_check). | |
109 | :- use_module(error_manager). | |
110 | :- use_module(gensym). | |
111 | :- use_module(preferences). | |
112 | :- use_module(tools). | |
113 | %:- use_module(state_space_exploration_modes,[compute_hash/3]). | |
114 | ||
115 | :- use_module(extension('counter/counter'), | |
116 | [counter_init/0, new_counter/1, get_counter/2, inc_counter/1, inc_counter/2, inc_counter_by/2, reset_counter/1, set_counter/2]). | |
117 | ||
118 | :- use_module(module_information). | |
119 | :- module_info(group,state_space). | |
120 | :- module_info(description,'This module keeps track of the visited states by the animator/model checker.'). | |
121 | ||
122 | % ---------------------------------- | |
123 | ||
124 | :- load_files(library(system), [when(compile_time), imports([environ/2])]). | |
125 | :- if((environ(prob_myheap,false) ; \+ predicate_property(load_foreign_resource(_), _))). | |
126 | :- use_module(state_space_open_nodes). %% comment in to use only Prolog datastructures | |
127 | :- else. | |
128 | :- use_module(state_space_open_nodes_c). %% comment in to use C++ multimap queue; can make use of HEURISTIC_FUNCTION | |
129 | :- endif. | |
130 | ||
131 | % ---------------------------------- | |
132 | ||
133 | get_state_space_stats(NrNodes,NrTransitions,ProcessedNodes) :- | |
134 | get_counter(states,N), NrNodes is N+1, | |
135 | get_counter(transitions,NrTransitions), | |
136 | get_counter(processed_nodes,ProcessedNodes). | |
137 | ||
138 | get_state_space_stats(NrNodes,NrTransitions,ProcessedNodes,IgnoredNodes) :- | |
139 | get_state_space_stats(NrNodes,NrTransitions,ProcessedNodes), | |
140 | get_counter(not_interesting_nodes,IgnoredNodes). | |
141 | ||
142 | gen_new_state_id(Nr) :- | |
143 | inc_counter(states,N1), Nr is N1-1. % only one C call | |
144 | %get_counter(states,Nr), inc_counter(states). | |
145 | reset_state_counter :- reset_counter(states). | |
146 | reset_state_counter(Nr) :- set_counter(states,Nr). | |
147 | ||
148 | get_state_id_trace(StateIds) :- | |
149 | history(History), | |
150 | current_state_id(CurID), | |
151 | reverse([CurID|History],StateIds). | |
152 | ||
153 | get_current_predecessor_state_id(PriorID) :- | |
154 | history([PriorID|_]). | |
155 | ||
156 | :- dynamic history/1. | |
157 | history([]). | |
158 | ||
159 | ||
160 | :- dynamic forward_history/1. | |
161 | ||
162 | :- dynamic current_state_id/1. | |
163 | current_state_id(root). | |
164 | /* INITIAL STATE, third arg: clp(fd) constraints as generated | |
165 | by fd_copy_term */ | |
166 | ||
167 | current_expression(ID,State) :- current_state_id(ID), | |
168 | visited_expression(ID,State). | |
169 | current_packed_expression(ID,State) :- current_state_id(ID), | |
170 | packed_visited_expression(ID,State). | |
171 | ||
172 | :- use_module(specfile,[state_corresponds_to_initialised_b_machine/1, | |
173 | state_corresponds_to_fully_setup_b_machine/1, | |
174 | state_corresponds_to_set_up_constants/1]). | |
175 | current_state_corresponds_to_initialised_b_machine :- | |
176 | current_packed_expression(_,PS), unpack_state_top_level(PS,TLState), | |
177 | state_corresponds_to_initialised_b_machine(TLState). | |
178 | visited_state_corresponds_to_initialised_b_machine(ID) :- | |
179 | packed_visited_expression(ID,PS), unpack_state_top_level(PS,TLState), | |
180 | state_corresponds_to_initialised_b_machine(TLState). | |
181 | ||
182 | current_state_corresponds_to_fully_setup_b_machine :- | |
183 | current_packed_expression(_,PS), unpack_state_top_level(PS,TLState), | |
184 | state_corresponds_to_fully_setup_b_machine(TLState). | |
185 | ||
186 | current_state_corresponds_to_setup_constants_b_machine :- | |
187 | current_packed_expression(_,PS), unpack_state_top_level(PS,TLState), | |
188 | state_corresponds_to_set_up_constants(TLState). | |
189 | ||
190 | visited_state_corresponds_to_setup_constants_b_machine(ID) :- | |
191 | packed_visited_expression(ID,PS), unpack_state_top_level(PS,TLState), | |
192 | state_corresponds_to_set_up_constants(TLState). | |
193 | ||
194 | :- dynamic current_options/1. | |
195 | current_options([]). | |
196 | ||
197 | set_current_options(Options) :- | |
198 | retractall( current_options(_) ), | |
199 | assertz( current_options(Options) ). | |
200 | ||
201 | :- dynamic packed_visited_expression/2. | |
202 | %packed_visited_expression(v_0,true). | |
203 | ||
204 | :- use_module(state_packing). | |
205 | ||
206 | retract_visited_expression(ID,State) :- retract(packed_visited_expression(ID,PState)), | |
207 | unpack_state(PState,State). | |
208 | ||
209 | retractall_visited_expression(ID) :- retractall(packed_visited_expression(ID,_)). | |
210 | ||
211 | state_space_packed_add(Id,PackedTerm) :- assertz(packed_visited_expression(Id,PackedTerm)). | |
212 | ||
213 | state_space_add(Id,Term) :- | |
214 | (pack_state(Term,PackedTerm) -> assertz(packed_visited_expression(Id,PackedTerm)) | |
215 | ; add_internal_error('State packing failed: ',pack_state(Term,_)), | |
216 | assertz(packed_visited_expression(Id,Term))). | |
217 | ||
218 | % deprecated: | |
219 | visited_expression(ID,State,true) :- visited_expression(ID,State). | |
220 | % not call(CurBody); for the moment we always have true as last argument | |
221 | ||
222 | visited_expression(A,B) :- packed_visited_expression(A,PB), | |
223 | (unpack_state(PB,R) -> B=R ; add_internal_error('Unpacking state failed: ',unpack_state(PB,R)),R=A). | |
224 | ||
225 | %visited_expression_id(A) :- packed_visited_expression(A,_). % avoid unpacking state | |
226 | % even better: not to look up fact at all to avoid constructing state term, this is done below: | |
227 | :- use_module(library(between),[between/3]). | |
228 | visited_expression_id(ID) :- number(ID),!, ID>=0, get_counter(states,N), ID<N. | |
229 | visited_expression_id(ID) :- ID==root,!. | |
230 | visited_expression_id(root). | |
231 | visited_expression_id(Nr) :- Nr \== root, get_counter(states,N), N1 is N-1, between(0,N1,Nr). | |
232 | ||
233 | % given a hash and a packed state: find ID (fail if does not exist) | |
234 | find_hashed_packed_visited_expression(Hash,PackedState,ID) :- | |
235 | hash_to_id(Hash,ID), | |
236 | (packed_visited_expression(ID,PackedState) | |
237 | -> true /* warning: may instantiate State if not ground */ | |
238 | ; print(hash_collision(Hash,ID)),nl,fail | |
239 | ). | |
240 | ||
241 | ||
242 | :- dynamic not_invariant_checked/1. | |
243 | set_invariant_checked(ID) :- %print(inv_checked(ID)),nl, | |
244 | retract(not_invariant_checked(ID)). | |
245 | ||
246 | invariant_not_yet_checked(ID) :- | |
247 | not_all_transitions_added(ID) ; /* assumption: if not all transitions added then we haven't checked invariant yet */ | |
248 | not_invariant_checked(ID) ; | |
249 | not_interesting(ID). % assumption: if a node is marked as not interesting it will not be examined | |
250 | ||
251 | % difference with invariant_not_yet_checked: not interesting nodes not reported and cannot be backtracked | |
252 | invariant_still_to_be_checked(ID) :- | |
253 | (not_all_transitions_added(ID) -> true ; not_invariant_checked(ID) -> true). | |
254 | ||
255 | :- dynamic not_interesting/1. | |
256 | %not_interesting(v_0). | |
257 | ||
258 | :- dynamic max_reached_for_node/1. | |
259 | /* true if not all outgoing transistions were computed due to the limit | |
260 | on the number of operations/initialisations computed */ | |
261 | :- dynamic time_out_for_node/3, use_no_timeout/1, time_out_for_invariant/1, time_out_for_assertions/1. | |
262 | ||
263 | time_out_for_node(ID) :- (var(ID) -> visited_expression_id(ID) ; true), | |
264 | (time_out_for_node(ID,_,_) -> true ; fail). | |
265 | ||
266 | :- dynamic transition/4. | |
267 | %transition(v_0,a,1,v_1). | |
268 | ||
269 | store_transition(Org,Action,Dest,Id) :- | |
270 | %get_counter(transitions,Id), inc_counter(transitions), | |
271 | inc_counter(transitions,Id1), Id is Id1-1, % only one C call | |
272 | assertz(transition(Org,Action,Id,Dest)). | |
273 | ||
274 | deadlocked_state(Origin) :- \+ any_transition(Origin,_,_). | |
275 | ||
276 | is_concrete_constants_state_id(ID) :- | |
277 | transition(root,_,ID), | |
278 | packed_visited_expression(ID,concrete_constants(_)). | |
279 | ||
280 | % check if we have multiple constant setups | |
281 | multiple_concrete_constants_exist :- | |
282 | is_concrete_constants_state_id(ID), | |
283 | is_concrete_constants_state_id(ID2), ID2 \= ID,!. | |
284 | ||
285 | ||
286 | is_initial_state_id(InitialStateID) :- | |
287 | transition(root,_,State), | |
288 | packed_visited_expression(State,P), | |
289 | (P = concrete_constants(_) % also covers '$partial_setup_constants' | |
290 | -> state_space:transition(State,_,InitialStateID) | |
291 | ; InitialStateID=State). | |
292 | ||
293 | % get the constants for a state (if there are constants) | |
294 | get_constants_state_for_id(ID,ConstantsState) :- | |
295 | visited_expression(ID,State), % TO DO: use packed_visited_expression for performance | |
296 | get_constants_state_aux(State,ConstantsState). | |
297 | get_constants_state_aux(concrete_constants(ConstantsState),ConstantsState). | |
298 | get_constants_state_aux(const_and_vars(ID,_),State) :- get_constants_state_for_id(ID,State). | |
299 | ||
300 | % get the staet id for associated constants state for a state (if there are constants) | |
301 | get_constants_state_id_for_id(ID,CstID) :- | |
302 | visited_expression(ID,State), % TO DO: use packed_visited_expression for performance | |
303 | get_constants_id_for_id_aux(State,ID,CstID). | |
304 | get_constants_id_for_id_aux(concrete_constants(_),ID,ID). | |
305 | get_constants_id_for_id_aux(const_and_vars(ID,_),_,ID). | |
306 | ||
307 | get_variables_state_for_id(ID,VarState) :- | |
308 | visited_expression(ID,State), | |
309 | get_vars_aux(State,VarState). | |
310 | %get_vars_aux(concrete_constants(ConstantsState),[]). | |
311 | get_vars_aux(const_and_vars(_,Vars),State) :- !, State=Vars. | |
312 | get_vars_aux([],[]). | |
313 | get_vars_aux([H|T],[H|T]). | |
314 | ||
315 | % check if there is a unique constants state: | |
316 | try_get_unique_constants_state(ConstantsState) :- | |
317 | transition(root,_,_TransID,DestID), | |
318 | \+ (transition(root,_,_,DestID2), DestID2 \= DestID), % no other transition exists | |
319 | \+ max_reached_or_timeout_for_node(root), | |
320 | DestID \= root, % DestID=root should never happen | |
321 | get_constants_state_for_id(DestID,ConstantsState). | |
322 | ||
323 | % returns id of constants state for a state (if it exists) | |
324 | get_constants_id_for_state_id(ID,ConstID) :- | |
325 | packed_visited_expression(ID,'$cst_vars'(ConstID,_)). | |
326 | ||
327 | any_transition(Origin,TransID,Destination) :- transition(Origin,_,TransID,Destination). | |
328 | ||
329 | transition(Origin,Action,Destination) :- transition(Origin,Action,_TransID,Destination). | |
330 | ||
331 | :- dynamic transition_info/2. | |
332 | store_transition_infos([],_TransId). | |
333 | store_transition_infos([Info|Irest],TransId) :- | |
334 | store_transition_info(Info,TransId), | |
335 | store_transition_infos(Irest,TransId). | |
336 | store_transition_info(Info,TransId) :- %print(info(Info,TransId)),nl, | |
337 | (keep_transition_info(Info) | |
338 | -> assertz(transition_info(TransId,Info)) | |
339 | ; true). | |
340 | ||
341 | % Do not store path info by default | |
342 | keep_transition_info(path(_)) :- !,fail. | |
343 | keep_transition_info(eventtrace(_)) :- !,preference(eventtrace,true). | |
344 | keep_transition_info(event(_)) :- !,preference(store_event_transinfo,true). | |
345 | keep_transition_info(_). % store everything else | |
346 | ||
347 | reset_transition_store :- | |
348 | retractall(transition(_,_,_,_)), | |
349 | retractall(transition_info(_,_)), | |
350 | reset_counter(transitions), | |
351 | reset_counterexample. | |
352 | ||
353 | /* | |
354 | Version with packing of transitions: | |
355 | store_transition(Org,Action,Dest,Id) :- | |
356 | retract(transition_counter(Id)), | |
357 | NewId is Id+1, | |
358 | assertz(transition_counter(NewId)), | |
359 | Action =.. [ActionName|Parameters], | |
360 | pack_values(Parameters,PackedParameters), | |
361 | assertz(packed_transition(Org,ActionName,PackedParameters,Id,Dest)). | |
362 | ||
363 | transition(Origin,Action,TransID,Destination) :- nonvar(Action),!, | |
364 | Action =.. [ActionName|Parameters], | |
365 | packed_transition(Origin,ActionName,PackedParameters,TransID,Destination), | |
366 | unpack_values(PackedParameters,Parameters). | |
367 | transition(Origin,Action,TransID,Destination) :- | |
368 | packed_transition(Origin,ActionName,PackedParameters,TransID,Destination), | |
369 | unpack_values(PackedParameters,Parameters), | |
370 | Action =.. [ActionName|Parameters]. | |
371 | any_transition(Origin,TransID,Destination) :- packed_transition(Origin,_,_,TransID,Destination). | |
372 | */ | |
373 | ||
374 | % compute out-degree of a node | |
375 | out_degree(ID,OutDegree) :- findall(0, transition(ID,_,_,_), L), length(L,OutDegree). | |
376 | ||
377 | operation_name_not_yet_covered(OpName) :- operation_not_yet_covered(OpName). | |
378 | ||
379 | ||
380 | get_operation_name_coverage_infos(PossibleNr,FeasibleNr,UncovNr,UncoveredList) :- | |
381 | findall(ON, specfile:get_possible_event(ON), Possible), length(Possible,PossibleNr), | |
382 | findall(OF, specfile:get_feasible_event(OF), Feasible), length(Feasible,FeasibleNr), | |
383 | findall(OpName, state_space: operation_name_not_yet_covered(OpName), UncoveredList), | |
384 | length(UncoveredList,UncovNr). | |
385 | ||
386 | ||
387 | :- dynamic operation_not_yet_covered/1. | |
388 | %operation_not_yet_covered(b). | |
389 | ||
390 | :- use_module(probsrc(debug),[formatsilent/2]). | |
391 | mark_operation_as_covered(OpName) :- | |
392 | (retract(operation_not_yet_covered(OpName)) | |
393 | -> (preferences:get_preference(provide_trace_information,true) | |
394 | -> print(cover(OpName)),nl ; true), | |
395 | (operation_not_yet_covered(_) -> true ; formatsilent('~nALL OPERATIONS COVERED~n',[])) | |
396 | ; true | |
397 | ). | |
398 | ||
399 | ||
400 | :- use_module(bmachine,[b_top_level_operation/1]). | |
401 | :- use_module(debug,[debug_println/1]). | |
402 | :- use_module(probcspsrc(haskell_csp),[channel/2]). | |
403 | initialise_operation_not_yet_covered :- retractall(operation_not_yet_covered(_)), | |
404 | b_or_z_mode, | |
405 | ? | b_top_level_operation(Name), |
406 | % b_get_machine_operation(Name,_,Par,_), length(Par,Arity), functor(Op,Name,Arity), | |
407 | % Note: no '-->' added | |
408 | assertz(operation_not_yet_covered(Name)), | |
409 | debug_println(operation_not_yet_covered(Name)), | |
410 | fail. | |
411 | /* Missing: treat operations with return values */ | |
412 | initialise_operation_not_yet_covered :- csp_mode, \+ csp_with_bz_mode, | |
413 | channel(Name,_), | |
414 | assertz(operation_not_yet_covered(Name)), | |
415 | debug_println(operation_not_yet_covered(Name)), | |
416 | fail. | |
417 | initialise_operation_not_yet_covered. | |
418 | ||
419 | state_error_exists :- state_error(_,_,_),!. | |
420 | :- dynamic state_error/3. | |
421 | ||
422 | %state_error([],invariant_violated). | |
423 | ||
424 | reset_next_state_error_id_counter :- reset_counter(next_state_error_id). | |
425 | :- use_module(tools_printing, [print_error/1, format_error_with_nl/2]). | |
426 | :- use_module(error_manager,[print_error_span/1]). | |
427 | store_state_error(State,Error,Id) :- state_error(State,Id,Error),!. % do not store identical error twice | |
428 | store_state_error(State,Error,Id) :- | |
429 | %retract( next_state_error_id(Id) ), | |
430 | inc_counter(next_state_error_id,Id), | |
431 | % tools_printing:print_term_summary(Error),nl, tools_printing:nested_print_term(Error),nl, | |
432 | assertz( state_error(State,Id,Error) ). | |
433 | store_error_for_context_state(Error,Id) :- | |
434 | ( context_state(State,Errs) -> | |
435 | (Errs<25 | |
436 | -> store_state_error(State,Error,Id), E1 is Errs+1, | |
437 | %assertz(context_state(State,E1)) | |
438 | set_context_number_of_errors(E1) | |
439 | ; store_state_error(State,max_state_errors_reached(25),Id) | |
440 | ) | |
441 | ; | |
442 | add_internal_error('No known context when calling store_error_for_context_state: ',store_error_for_context_state(Error,Id)), | |
443 | fail). | |
444 | ||
445 | % check if we already have a certain type of error in the current context state | |
446 | abort_error_exists_in_context_state(ErrType) :- get_current_context_state(State), | |
447 | Error = abort_error(ErrType,_Msg,_Term,_Pos), | |
448 | state_error(State,_Id,Error). | |
449 | ||
450 | % copy current errors from error_manager to state errors | |
451 | copy_current_errors_to_state(StateID,Context) :- | |
452 | % error_manager:logged_error(Source,ErrMsg,_Context,Span), % will not retract | |
453 | error_manager:get_error_with_span(Source,ErrMsg,Span), % will retract | |
454 | store_state_error(StateID,abort_error(Source,ErrMsg,'',span_context(Span,Context)),SID), | |
455 | % TO DO: use other error class | |
456 | debug_println(stored(Source,SID,_Context,ErrMsg)), | |
457 | fail. | |
458 | copy_current_errors_to_state(_,_). | |
459 | ||
460 | store_abort_error_for_context_state_if_possible(ErrType,Msg,Term,Span) :- | |
461 | %print(store(Msg,Term,Span)),nl, | |
462 | ( get_current_context_state(State) -> | |
463 | error_manager:get_error_context(Context), | |
464 | (abort_error_for_same_location_exists(State,Id1,ErrType,Msg,Span), | |
465 | abort_error_for_same_location_exists(State,Id2,ErrType,Msg,Span), | |
466 | Id2>Id1 | |
467 | -> /* two errors of same type, for same state and same source location exists */ | |
468 | /* TO DO: maybe merge state errors */ | |
469 | simplify_span(Span,Span1), | |
470 | compress_span(Span1,Span2), | |
471 | store_state_error(State,abort_error(ErrType,'Further identical errors occurred (not stored !)',Term,span_context(Span2,Context)),_) | |
472 | ; | |
473 | format_error_with_nl('! An error occurred in state ~w: ~w !',[State,ErrType]), | |
474 | % usual errors: precondition_error, while_invariant_violation, while_variant_error, | |
475 | % assert_error, well_definedness_error, card_overflow_error | |
476 | print_error(Msg), | |
477 | print_error_term(Term,Span), | |
478 | % print_error(context_state_id(State)), % printed by print_error_context | |
479 | print_error_context, | |
480 | (debug_mode(on),visited_expression(State,S) -> translate:translate_bstate(S,O),print_error(O) ; true), | |
481 | compress_span(Span,Span2), | |
482 | print_error_span(Span2), | |
483 | store_state_error(State,abort_error(ErrType,Msg,Term,span_context(Span2,Context)),_) | |
484 | ), | |
485 | (add_new_event_in_error_scope(abort_error(ErrType)) -> true ; true), % should we use well_definedness_error? | |
486 | assert_real_error_occurred(abort_error) % Note that in this case the error manager list of errors maybe empty even though real_error_occured is true. (see ProB2 kernel test de.prob.cli.integration.rules.RulesMachineTest > testReuseStateSpace) | |
487 | ; % no current context_state exists: | |
488 | compress_span(Span,Span2), | |
489 | add_error(ErrType,Msg,Term,Span2) | |
490 | ). | |
491 | ||
492 | ||
493 | :- use_module(bsyntaxtree, [find_identifier_uses/3]). | |
494 | compress_span(span_context(Span,C),span_context(CS,C)) :- !, | |
495 | compress_span(Span,CS). | |
496 | compress_span(pos_context(Span1,C,Span2),pos_context(CS1,C,CS2)) :- !, | |
497 | compress_span(Span1,CS1), | |
498 | compress_span(Span2,CS2). | |
499 | compress_span(span_predicate(Pred,LS,S),Res) :- find_identifier_uses(Pred,[],Ids), | |
500 | sort(Ids,SIds), | |
501 | filter_state(LS,SIds,FLS), | |
502 | filter_state(S,SIds,FS), % TODO: do we need to store the global state? we can reconstruct it ? | |
503 | % format('Compressed span_predicate (~w)~n',[Ids]), | |
504 | !, | |
505 | Res = span_predicate(Pred,FLS,FS). | |
506 | compress_span(S,S). | |
507 | % avoid storing large useless values | |
508 | % TO DO: probably we should stop storing spans when a certain threshold of number of errors is reached | |
509 | ||
510 | ||
511 | :- use_module(library(ordsets),[ord_member/2]). | |
512 | filter_state([],_,[]). | |
513 | filter_state([bind(ID,L)|T],Vars,Res) :- | |
514 | (ord_member(ID,Vars) -> Res = [bind(ID,L)|RT] ; Res=RT), | |
515 | filter_state(T,Vars,RT). | |
516 | ||
517 | ||
518 | :- use_module(translate, [translate_error_term/3]). | |
519 | print_error_term(T,S) :- (var(T);var(S)),!, | |
520 | print_error('### VARIABLE error term or span:'), print_error(print_error_term(T,S)). | |
521 | print_error_term(Term,Span) :- translate_error_term(Term,Span,S), | |
522 | (S='' -> true ; print_error(S)). | |
523 | ||
524 | abort_error_for_same_location_exists(State,Id,ErrType,Msg,Span) :- | |
525 | state_error(State,Id,abort_error(ErrType,Msg,_Term2,span_context(Span2,_Ctxt2))), | |
526 | same_span_location(Span2,Span). | |
527 | % should be moved to error_manager ? | |
528 | same_span_location(span_context(Span1,C),span_context(Span2,C)) :- !, same_span_location(Span1,Span2). | |
529 | same_span_location(pos_context(Span1,C,_),pos_context(Span2,C,_)) :- !, | |
530 | same_span_location(Span1,Span2). % should we check second span? | |
531 | same_span_location(span_predicate(Pred1,_,_),span_predicate(Pred2,_,_)) :- !,Pred1=Pred2. | |
532 | same_span_location(X,X). | |
533 | ||
534 | :- dynamic saved_nested_context_state/2. | |
535 | save_nested_context_state(_S) :- | |
536 | bb_get(state_space_context_state,ID), | |
537 | bb_get(state_space_context_errors,Errs),!, | |
538 | %print(saving_context_state(_S,ID,Errs)),nl, | |
539 | asserta(saved_nested_context_state(ID,Errs)). | |
540 | save_nested_context_state(_). | |
541 | ||
542 | % actually pops context state | |
543 | clear_context_state :- | |
544 | retract(saved_nested_context_state(ID,Errs)),!, %print(restoring_nested(ID,Errs)),nl, | |
545 | bb_put(state_space_context_state,ID), | |
546 | bb_put(state_space_context_errors,Errs). | |
547 | clear_context_state :- | |
548 | (bb_delete(state_space_context_state,_) -> true ; true). | |
549 | %(retract(context_state(_,_)) -> true ; true). % retractall seems to be slowing down with use | |
550 | ||
551 | % Note: Each Prolog module maintains its own blackboard for bb_get/bb_put | |
552 | context_state(ID,Errs) :- | |
553 | bb_get(state_space_context_state,ID), bb_get(state_space_context_errors,Errs). | |
554 | ||
555 | % sets a new context state; pushing the previous one if necessary | |
556 | set_context_state(State) :- %print(set_id(State)),nl, | |
557 | save_nested_context_state(State), | |
558 | bb_put(state_space_context_state,State), | |
559 | bb_put(state_space_context_errors,0). | |
560 | ||
561 | set_context_state(State,_Context) :- % Context can be used for debugging later | |
562 | set_context_state(State). | |
563 | ||
564 | % update current context state, without storing nested states | |
565 | update_context_state(State) :- | |
566 | bb_put(state_space_context_state,State), | |
567 | bb_put(state_space_context_errors,0). | |
568 | ||
569 | get_current_context_state(ID) :- bb_get(state_space_context_state,ID). | |
570 | %get_current_context_state(ID) :- context_state(ID,_). | |
571 | ||
572 | set_context_number_of_errors(Errs) :- bb_put(state_space_context_errors,Errs). | |
573 | ||
574 | retractall_invariant_violated(State) :- | |
575 | retractall(state_error(State,_,invariant_violated)). | |
576 | invariant_violated(State) :- | |
577 | state_error(State,_,invariant_violated). | |
578 | set_invariant_violated(State) :- | |
579 | ( invariant_violated(State) -> true | |
580 | ; time_out_for_invariant(ID) -> print('Timeout for node: '), print(ID),nl, | |
581 | print('Not setting invariant violation status'),nl | |
582 | ; store_state_error(State,invariant_violated,_) | |
583 | ). | |
584 | ||
585 | %:- set_invariant_violated([]). % why is this ?? | |
586 | ||
587 | ||
588 | :- dynamic hash_to_id/2. | |
589 | :- dynamic id_to_marker/2. | |
590 | :- dynamic id_back_edge/3. % optional facts to keep track where a state was constructed from | |
591 | ||
592 | :- dynamic hash_to_nauty_id/2. % used in nauty mode to map nauty id's to hash values | |
593 | ||
594 | :- dynamic specialized_inv/2. /* stores whether for a node a specialized invariant | |
595 | version could be computed */ | |
596 | ||
597 | % :- dynamic reuse_operation/4. /* when for a state and given operation name we can reuse the operation computed for another state */ | |
598 | % used to be used for OPERATION_REUSE TRUE | |
599 | ||
600 | :- use_module(hashing). | |
601 | state_space_startup :- % call once at startup to ensure all counters exist | |
602 | counter_init, | |
603 | new_counter(states), new_counter(processed_nodes), new_counter(transitions), | |
604 | new_counter(next_state_error_id), | |
605 | new_counter(not_interesting_nodes), | |
606 | reset_open_ids. % also calls myheap init | |
607 | state_space_initialise :- counter_init, reset_gennum, reset_gensym, | |
608 | new_counter(states), new_counter(processed_nodes), new_counter(transitions), | |
609 | new_counter(next_state_error_id), new_counter(not_interesting_nodes), | |
610 | reset_state_counter, reset_processed_nodes_counter, reset_next_state_error_id_counter, | |
611 | retractall_visited_expression(_), | |
612 | reset_open_ids, | |
613 | reset_stored_values, % state_packing | |
614 | retractall(not_invariant_checked(_)), | |
615 | reset_not_interesting, | |
616 | retractall(max_reached_for_node(_)), | |
617 | retractall(time_out_for_node(_,_,_)), | |
618 | retractall(time_out_for_invariant(_)), | |
619 | retractall(time_out_for_assertions(_)), | |
620 | retractall(use_no_timeout(_)), | |
621 | retractall(state_error(_,_,_)), | |
622 | clear_context_state, | |
623 | reset_transition_store, | |
624 | retractall(operation_not_yet_covered(_)), | |
625 | retractall(hash_to_id(_,_)), | |
626 | retractall(id_back_edge(_,_,_)), | |
627 | retractall(hash_to_nauty_id(_,_)), | |
628 | retractall(id_to_marker(_,_)), | |
629 | retractall(specialized_inv(_,_)), | |
630 | %retractall(reuse_operation(_,_,_,_)), | |
631 | state_space_add(root,root), | |
632 | add_id_at_front(root), | |
633 | my_term_hash(root,RootHash), | |
634 | assertz(hash_to_id(RootHash,root)), | |
635 | %assertz(not_invariant_checked(root)), | |
636 | state_space_reset. | |
637 | ||
638 | :- use_module(eventhandling,[register_event_listener/3]). | |
639 | :- register_event_listener(startup_prob,state_space_startup, | |
640 | 'Initialise Statespace Counters.'). | |
641 | :- register_event_listener(reset_specification,state_space_initialise, | |
642 | 'Reset Statespace.'). | |
643 | :- register_event_listener(change_of_animation_mode,state_space_initialise, | |
644 | 'Reset Statespace.'). | |
645 | :- register_event_listener(specification_initialised,initialise_operation_not_yet_covered, | |
646 | 'Init coverage info.'). | |
647 | :- register_event_listener(reset_prob,state_space_initialise, | |
648 | 'Reset Statespace.'). | |
649 | ||
650 | /* A version of reset which checks how much memory is used by each fact */ | |
651 | /* state_space:init_with_stats */ | |
652 | state_space_initialise_with_stats :- | |
653 | reset_gennum, reset_gensym, reset_state_counter, reset_processed_nodes_counter, | |
654 | reset_next_state_error_id_counter, | |
655 | retract_open_ids_with_statistics, | |
656 | retract_with_statistics(state_space,[packed_visited_expression(_,_), | |
657 | not_invariant_checked(_), | |
658 | not_interesting(_), | |
659 | max_reached_for_node(_), | |
660 | time_out_for_node(_,_,_), | |
661 | time_out_for_invariant(_), | |
662 | time_out_for_assertions(_), | |
663 | use_no_timeout(_), | |
664 | state_error(_,_,_), | |
665 | transition(_,_,_,_), | |
666 | transition_info(_,_), | |
667 | operation_not_yet_covered(_), | |
668 | hash_to_id(_,_), | |
669 | hash_to_nauty_id(_,_), | |
670 | id_to_marker(_,_), | |
671 | specialized_inv(_,_), | |
672 | %reuse_operation(_,_,_,_), | |
673 | history(_), forward_history(_), op_trace_ids(_)]), | |
674 | reset_not_interesting, | |
675 | retract_stored_values_with_statistics, | |
676 | clear_context_state, | |
677 | reset_transition_store, | |
678 | state_space_add(root,root), | |
679 | add_id_at_front(root), | |
680 | %assertz(not_invariant_checked(root)), | |
681 | state_space_reset, | |
682 | initialise_operation_not_yet_covered. | |
683 | ||
684 | ||
685 | ||
686 | :- dynamic op_trace_ids/1. | |
687 | reset_trace :- retractall(op_trace_ids(_)), assertz(op_trace_ids([])). | |
688 | get_action_trace(T) :- trace(T). | |
689 | get_action_term_trace(PT) :- get_action_trace_with_limit(0,T), project_on_action_term(T,PT). | |
690 | trace(Trace) :- get_action_trace_with_limit(500,Trace). | |
691 | get_action_trace_with_limit(Limit,Trace) :- | |
692 | op_trace_ids(IDT), reverse(IDT,RIDT), | |
693 | extract_trace_from_transition_ids(RIDT,root,Limit,[],Trace). | |
694 | ||
695 | reset_op_trace_ids :- retractall(op_trace_ids(_)), assertz(op_trace_ids([])). | |
696 | add_to_op_trace_ids(OpID) :- retract(op_trace_ids(OpIDS)), assertz(op_trace_ids([OpID|OpIDS])). | |
697 | remove_from_op_trace_ids(OpID) :- retract(op_trace_ids(OpIDS)), | |
698 | (OpIDS = [R|Rest] | |
699 | -> assertz(op_trace_ids(Rest)), OpID = R | |
700 | ; assertz(op_trace_ids(OpIDS)), fail). | |
701 | ||
702 | % translate a list of transition ids (from root) into a list of operation terms | |
703 | extract_term_trace_from_transition_ids(TransIDListFromRoot,Trace) :- | |
704 | extract_trace_from_transition_ids(TransIDListFromRoot,root,0,[],ActionTrace), | |
705 | reverse_and_project_on_action_term(ActionTrace,[],Trace). | |
706 | ||
707 | reverse_and_project_on_action_term([],A,A). | |
708 | reverse_and_project_on_action_term([action(_,Term)|T],Acc,Res) :- !, | |
709 | reverse_and_project_on_action_term(T,[Term|Acc],Res). | |
710 | reverse_and_project_on_action_term([H|T],Acc,Res) :- | |
711 | add_error(reverse_and_project_on_action_term,'Illegal action: ',H), | |
712 | reverse_and_project_on_action_term(T,[H|Acc],Res). | |
713 | ||
714 | project_on_action_term([],[]). | |
715 | project_on_action_term([action(_,Term)|T],Res) :- !, Res=[Term|TR], | |
716 | project_on_action_term(T,TR). | |
717 | project_on_action_term([H|T],Res) :- | |
718 | add_error(project_on_action_term,'Illegal action: ',H), | |
719 | project_on_action_term(T,Res). | |
720 | ||
721 | extract_trace_from_transition_ids([],_CurrentState,_,Trace,Trace). | |
722 | extract_trace_from_transition_ids([TransId|Rest],CurrentState,Limit,AccTrace,Trace) :- | |
723 | compute_op_string(TransId,CurrentState,Limit,OpTerm,OpString,DestState),!, | |
724 | extract_trace_from_transition_ids(Rest,DestState,Limit, | |
725 | [action(OpString,OpTerm)|AccTrace],Trace). | |
726 | extract_trace_from_transition_ids([TransId|_],CurrentState,_,_,_Trace) :- | |
727 | add_error(state_space,'Could not execute transition id: ', TransId:from(CurrentState)),fail. | |
728 | ||
729 | :- use_module(translate,[translate_event_with_src_and_target_id/5]). | |
730 | compute_op_string(jump(TO),_CurID,_,Term,String,DestID) :- !, Term=jump,String=jump,DestID=TO. | |
731 | compute_op_string(TransId,CurID,Limit,Term,String,DestID) :- transition(CurID,Term,TransId,DestID), | |
732 | translate_event_with_src_and_target_id(Term,CurID,DestID,Limit,String). | |
733 | ||
734 | % reset history and forward history, but not state-space itself | |
735 | state_space_reset :- | |
736 | reset_trace, | |
737 | retractall(history(_)), | |
738 | retractall(forward_history(_)), | |
739 | retractall(current_state_id(_)), | |
740 | retractall(current_options(_)), | |
741 | assertz(history([])), | |
742 | assertz(current_state_id(root)). | |
743 | ||
744 | reset_not_interesting :- retractall(not_interesting(_)), reset_counter(not_interesting_nodes). | |
745 | ||
746 | mark_as_not_interesting(ID) :- assertz(not_interesting(ID)), inc_counter(not_interesting_nodes). | |
747 | ||
748 | set_current_state_id(ID) :- (retract(current_state_id(_)) -> true ; true), | |
749 | assertz(current_state_id(ID)). | |
750 | ||
751 | state_space_clean_all :- | |
752 | retractall(state_space_version_in_file(_)), | |
753 | retractall_visited_expression(_), | |
754 | reset_open_ids, | |
755 | retractall(not_invariant_checked(_)), | |
756 | reset_not_interesting, | |
757 | retractall(max_reached_for_node(_)), | |
758 | retractall(time_out_for_node(_,_,_)), | |
759 | retractall(time_out_for_invariant(_)), | |
760 | retractall(time_out_for_assertions(_)), | |
761 | retractall(use_no_timeout(_)), | |
762 | retractall(state_error(_,_,_)), | |
763 | clear_context_state, | |
764 | reset_transition_store, | |
765 | retractall(operation_not_yet_covered(_)), | |
766 | retractall(hash_to_id(_,_)), | |
767 | retractall(id_back_edge(_,_,_)), | |
768 | retractall(hash_to_nauty_id(_,_)), | |
769 | retractall(id_to_marker(_,_)), | |
770 | retractall(specialized_inv(_,_)), | |
771 | %retractall(reuse_operation(_,_,_,_)), | |
772 | retractall(history(_)), | |
773 | retractall(forward_history(_)), | |
774 | retractall(op_trace_ids(_)), | |
775 | retractall(current_state_id(_)), | |
776 | retractall(current_options(_)). | |
777 | ||
778 | % this is only used from within the Tcl/Tk animator at the moment: | |
779 | delete_node(ID) :- print(deleting(ID)),nl, | |
780 | retractall_visited_expression(ID), | |
781 | retractall_invariant_violated(ID), | |
782 | retract_open_node_and_update_processed_nodes(ID), | |
783 | retractall(not_invariant_checked(ID)), | |
784 | (retract(not_interesting(ID)) -> inc_counter_by(not_interesting_nodes,-1) ; true), | |
785 | retractall(max_reached_for_node(ID)), | |
786 | retractall(time_out_for_node(ID,_,_)), | |
787 | retractall(time_out_for_invariant(ID)), | |
788 | retractall(time_out_for_assertions(ID)), | |
789 | retractall(use_no_timeout(ID)), | |
790 | retractall(state_error(ID,_,_)), | |
791 | retractall(transition(ID,_,_,_)), | |
792 | % to do: check if operation_not_yet_covered(_) changes | |
793 | retract_hash(ID), | |
794 | retractall(id_to_marker(ID,_)). | |
795 | ||
796 | retract_hash(ID) :- retract(hash_to_id(Hash,ID)), retractall(hash_to_nauty_id(_TermHash,Hash)),fail. | |
797 | retract_hash(_). | |
798 | ||
799 | assert_max_reached_for_node(Id) :- %print_message(max_reached_for_node(Id)), | |
800 | (max_reached_for_node(Id) -> true ; assertz(max_reached_for_node(Id))). | |
801 | ||
802 | :- use_module(probsrc(debug),[debug_mode/1]). | |
803 | assert_time_out_for_node(Id,OpName,TypeOfTimeOut) :- | |
804 | (debug_mode(off),functor(TypeOfTimeOut,virtual_time_out,_) -> true % can easily happen when parameters are unbounded | |
805 | ; print_message(time_out_for_node(Id,OpName,TypeOfTimeOut))), | |
806 | (time_out_for_node(Id,OpName,_) -> true ; assertz(time_out_for_node(Id,OpName,TypeOfTimeOut))). | |
807 | assert_time_out_for_invariant(Id) :- print_message(time_out_for_invariant(Id)), | |
808 | (time_out_for_invariant(Id) -> true ; assertz(time_out_for_invariant(Id))). | |
809 | assert_time_out_for_assertions(Id) :- print_message(time_out_for_assertions(Id)), | |
810 | (time_out_for_assertions(Id) -> true ; assertz(time_out_for_assertions(Id))). | |
811 | ||
812 | max_reached_or_timeout_for_node(Id) :- | |
813 | (max_reached_for_node(Id) ; time_out_for_node(Id,_,_)). | |
814 | /* ---------------------- */ | |
815 | /* state space saving */ | |
816 | /* ---------------------- */ | |
817 | ||
818 | :- dynamic state_space_version_in_file/1. % | |
819 | state_space_version(1). | |
820 | ||
821 | check_state_space_version :- state_space_version(V), | |
822 | (state_space_version_in_file(F) -> true ; F=0), | |
823 | (V>F -> add_message(state_space,'Warning: saved state_space may be incompatible with current version: ',F:V) ; true). | |
824 | ||
825 | % save all infos of state space (transitions, evaluated invariants, ...) | |
826 | tcltk_save_state_space(File) :- | |
827 | print('% saving full state space to: '), print(File),nl, | |
828 | open(File,write,Stream,[encoding(utf8)]), | |
829 | print_state_space(Stream), | |
830 | close(Stream), | |
831 | print_message(done). | |
832 | ||
833 | ||
834 | :- use_module(tools_printing, [print_dynamic_fact/2,print_dynamic_pred/4]). | |
835 | print_state_space(Stream) :- | |
836 | state_space_version(V), | |
837 | print_dynamic_fact(Stream,state_space_version_in_file(V)), | |
838 | % TO DO: maybe also save some important preferences, and warn user and/or propose to adapt preferences ? | |
839 | print_dynamic_pred(Stream,state_space,history,1), | |
840 | print_dynamic_pred(Stream,state_space,forward_history,1), | |
841 | print_dynamic_pred(Stream,state_space,op_trace_ids,1), | |
842 | print_dynamic_pred(Stream,state_space,current_state_id,1), | |
843 | print_dynamic_pred(Stream,state_space,current_options,1), | |
844 | print_dynamic_pred(Stream,state_space,packed_visited_expression,2), | |
845 | print_dynamic_pred(Stream,state_space,not_invariant_checked,1), | |
846 | print_dynamic_pred(Stream,state_space,not_interesting,1), | |
847 | print_dynamic_pred(Stream,state_space,max_reached_for_node,1), | |
848 | print_dynamic_pred(Stream,state_space,time_out_for_node,3), | |
849 | print_dynamic_pred(Stream,state_space,use_no_timeout,1), | |
850 | print_dynamic_pred(Stream,state_space,transition,4), | |
851 | print_dynamic_pred(Stream,state_space,transition_info,2), | |
852 | print_dynamic_pred(Stream,state_space,operation_not_yet_covered,1), | |
853 | print_dynamic_pred(Stream,state_space,state_error,3), | |
854 | print_state_space_open_nodes(Stream), | |
855 | print_stored_values(Stream), | |
856 | get_counter(states,X), | |
857 | write_term(Stream,saved_gennum_count(X),[quoted(true)]),write(Stream,'.'),nl(Stream). | |
858 | ||
859 | saved_gennum_count(99999). | |
860 | ||
861 | /* ---------------------- */ | |
862 | /* state space loading */ | |
863 | /* ---------------------- */ | |
864 | ||
865 | tcltk_load_state(File) :- state_space_clean_all, | |
866 | print('Loading: '), print(File),nl, | |
867 | user_consult_without_redefine_warning(File), % this will read in bind_skeleton/2, ..., next_value_id/1 | |
868 | check_state_space_version, | |
869 | print('Generating open node info'),nl, | |
870 | transfer_open_node_info, | |
871 | print('Transfer state packing info'),nl, | |
872 | transfer_state_packing_info, | |
873 | print('Recomputing hash index'),nl, | |
874 | recompute_all_hash, | |
875 | (saved_gennum_count(X) -> reset_state_counter(X) ; true), | |
876 | reset_processed_nodes_counter, % TO DO: restore or save it | |
877 | reset_next_state_error_id_counter, % DITTO | |
878 | print('Done'),nl,!. | |
879 | tcltk_load_state(File) :- | |
880 | add_error(tcltk_load_state,'Could not load state from file: ',File), | |
881 | state_space_initialise. | |
882 | ||
883 | :- dynamic not_all_z_saved/1, not_all_transitions_added_saved/1. | |
884 | :- dynamic bind_skeleton/2, stored_value/2, stored_value_hash_to_id/2, next_value_id/1. | |
885 | ||
886 | % transfer facts read into state_space into other modules: | |
887 | transfer_open_node_info :- retract(not_all_z_saved(X)), %print(not_all_z(X)),nl, | |
888 | assert_not_all_z(X),fail. | |
889 | transfer_open_node_info :- retract(not_all_transitions_added_saved(X)), | |
890 | assert_not_all_transitions_added(X),fail. | |
891 | transfer_open_node_info. | |
892 | % now for transferring to state_packing module info generated by print_stored_values | |
893 | transfer_state_packing_info :- retract(bind_skeleton(X,Y)), %print(skel(X)),nl, | |
894 | assertz(state_packing:bind_skeleton(X,Y)),fail. | |
895 | transfer_state_packing_info :- retract(stored_value(X,Y)), | |
896 | assertz(state_packing:stored_value(X,Y)),fail. | |
897 | transfer_state_packing_info :- retract(stored_value_hash_to_id(X,Y)), | |
898 | assertz(state_packing:stored_value_hash_to_id(X,Y)),fail. | |
899 | transfer_state_packing_info :- retract(next_value_id(X)), | |
900 | state_packing:set_next_value_id(X),fail. | |
901 | transfer_state_packing_info. | |
902 | ||
903 | recompute_all_hash :- | |
904 | retractall(hash_to_id(_,_)),retractall(id_to_marker(_,_)), | |
905 | retractall(hash_to_nauty_id(_,_)), | |
906 | visited_expression(ID,StateTemplate), | |
907 | state_space_exploration_modes:compute_hash(StateTemplate,Hash,Marker), | |
908 | assertz(hash_to_id(Hash,ID)), | |
909 | assertz(id_to_marker(ID,Marker)), | |
910 | fail. | |
911 | recompute_all_hash. | |
912 | ||
913 | :- use_module(hashing,[my_term_hash/2]). | |
914 | % generates a hash for the entire state space not depending on the order in which states where added | |
915 | compute_full_state_space_hash(Hash) :- | |
916 | %listing(hash_to_id/2), listing(packed_visited_expression/2), | |
917 | findall(Hash,hash_to_id(Hash,_),ListOfHashCodes), | |
918 | sort(ListOfHashCodes,SortedList), | |
919 | my_term_hash(SortedList,Hash). | |
920 | % TO DO: also provide transition hashes | |
921 | ||
922 | :- use_module(tools_meta,[safe_on_exception/3]). | |
923 | user_consult_without_redefine_warning(File) :- | |
924 | get_set_optional_prolog_flag(redefine_warnings, Old, off), | |
925 | get_set_optional_prolog_flag(single_var_warnings, Old2, off), | |
926 | (safe_on_exception(Exc, | |
927 | %consult(File), % | |
928 | load_files([File], [load_type(source),compilation_mode(consult),encoding(utf8)]), | |
929 | (nl,print('Exception occurred:'),print(Exc),nl,fail)) | |
930 | -> OK=true ; OK=false), | |
931 | get_set_optional_prolog_flag(redefine_warnings, _, Old), | |
932 | get_set_optional_prolog_flag(single_var_warnings, _, Old2), | |
933 | OK=true. | |
934 | ||
935 | ||
936 | ||
937 | execute_id_trace_from_current(ID,OpIDL,StateIDList) :- | |
938 | current_state_id(CurID), | |
939 | reverse([CurID|StateIDList],Rev), | |
940 | Rev = [Dest|TRev], (Dest==ID -> true ; print(not_eq(Dest,ID)),nl), | |
941 | retract(history(H)), | |
942 | update_forward_history(OpIDL), % check if OpIDL conforms to forward history and keep it | |
943 | append(TRev,H,NewH), | |
944 | assertz(history(NewH)), | |
945 | retract(op_trace_ids(OldTrace)), | |
946 | reverse(OpIDL,NewTrace), | |
947 | append(NewTrace,OldTrace,Trace), | |
948 | assertz(op_trace_ids(Trace)), | |
949 | retractall(current_state_id(_)), | |
950 | assertz(current_state_id(ID)). | |
951 | %execute_trace_to_node(OpL,StateIDList). /* <----- BOTTLENECK FOR LONG SEQUENCES */ | |
952 | %generate_trace([],Acc,Acc). | |
953 | %generate_trace([OpTerm|T],Acc,Res) :- | |
954 | % translate:translate_event(OpTerm,OpString), | |
955 | % generate_trace(T,[action(OpString,OpTerm)|Acc],Res). | |
956 | ||
957 | update_forward_history(TransitionIds) :- | |
958 | (retract(forward_history(Forward)), | |
959 | prune_forward_history(TransitionIds,Forward,NewForward) | |
960 | -> assert(forward_history(NewForward)) | |
961 | ; true). | |
962 | % try and prune forward history if it matches operation trace | |
963 | prune_forward_history([],ForwardHistory,ForwardHistory). | |
964 | prune_forward_history([TransID|T],[forward(_,TransID)|TF],Res) :- | |
965 | prune_forward_history(T,TF,Res). | |
966 | ||
967 | try_set_trace_by_transition_ids(TransIds) :- | |
968 | (set_trace_by_transition_ids(TransIds) -> true | |
969 | ; add_internal_error('Call failed:',set_trace_by_transition_ids(TransIds))). | |
970 | ||
971 | set_trace_by_transition_ids(TransitionIds) :- | |
972 | extract_history_from_transition_ids(TransitionIds,root,[],[],Last,History,OpTrace), | |
973 | %visited_expression(Last,LastState,LastCond), | |
974 | retractall(history(_)), | |
975 | retractall(forward_history(_)), % TODO: we could try and recover a new forward history if transition ids match current history^forward history | |
976 | update_forward_history(TransitionIds), | |
977 | retractall(current_state_id(_)), | |
978 | retractall(op_trace_ids(_)), | |
979 | assertz(history(History)), | |
980 | assertz(op_trace_ids(OpTrace)), | |
981 | assertz(current_state_id(Last)). | |
982 | ||
983 | ||
984 | extract_history_from_transition_ids([],CurrentState,History,Trace,CurrentState,History,Trace). | |
985 | extract_history_from_transition_ids([TransId|Rest],CurrentState,AccHist,AccTrace,Last,History,Trace) :- | |
986 | transition(CurrentState,_,TransId,DestState),!, | |
987 | extract_history_from_transition_ids(Rest,DestState,[CurrentState|AccHist], | |
988 | [TransId|AccTrace],Last,History,Trace). | |
989 | extract_history_from_transition_ids([TransId|_],CurrentState,_,_,_,_,_Trace) :- | |
990 | add_error(state_space,'Could not execute transition id: ', TransId:from(CurrentState)),fail. | |
991 | ||
992 | % extend trace from current state | |
993 | extend_trace_by_transition_ids(TransitionIds) :- | |
994 | current_state_id(CurID), | |
995 | history(OldH), op_trace_ids(OldOT), | |
996 | extract_history_from_transition_ids(TransitionIds,CurID,OldH,OldOT,Last,History,OpTrace), | |
997 | retractall(history(_)), retractall(forward_history(_)), | |
998 | retractall(current_state_id(_)), | |
999 | retractall(op_trace_ids(_)), | |
1000 | assertz(history(History)), | |
1001 | assertz(op_trace_ids(OpTrace)), | |
1002 | assertz(current_state_id(Last)). | |
1003 | ||
1004 | /* --------------------------------- */ | |
1005 | :- dynamic max_nr_of_new_nodes/1. | |
1006 | ||
1007 | % negative number or non-number signifies no limit | |
1008 | set_max_nr_of_new_impl_trans_nodes(MaxNrOfNewNodes) :- | |
1009 | retractall(max_nr_of_new_nodes(_)), | |
1010 | (number(MaxNrOfNewNodes), MaxNrOfNewNodes>=0 | |
1011 | -> assertz(max_nr_of_new_nodes(MaxNrOfNewNodes)) | |
1012 | ; true). % no need to store limit; we will explore as much as needed | |
1013 | ||
1014 | get_max_nr_of_new_impl_trans_nodes(MaxNrOfNewNodes) :- | |
1015 | (max_nr_of_new_nodes(Max) -> MaxNrOfNewNodes=Max; MaxNrOfNewNodes = 0). | |
1016 | ||
1017 | % used e.g., in refinement or ltl checker | |
1018 | impl_trans_term(From,ActionAsTerm,To) :- | |
1019 | compute_transitions_if_necessary_saved(From), | |
1020 | transition(From,ActionAsTerm,_TID,To). | |
1021 | ||
1022 | % a variation also giving the transition id: | |
1023 | impl_trans_id(From,ActionAsTerm,TransitionID,To) :- | |
1024 | compute_transitions_if_necessary_saved(From), | |
1025 | transition(From,ActionAsTerm,TransitionID,To). | |
1026 | ||
1027 | impl_trans_term_all(From,Ops) :- | |
1028 | compute_transitions_if_necessary_saved(From), | |
1029 | findall(op(Id,ActionAsTerm,To), | |
1030 | transition(From,ActionAsTerm,Id,To), | |
1031 | Ops). | |
1032 | ||
1033 | % true if e.g., a time-out occurred during computation of all transitions | |
1034 | impl_trans_not_complete(From) :- max_reached_or_timeout_for_node(From). | |
1035 | ||
1036 | compute_transitions_if_necessary_saved(From) :- | |
1037 | catch( | |
1038 | compute_transitions_if_necessary(From), | |
1039 | error(forced_interrupt_error('User has interrupted the current execution'),_), | |
1040 | user_interrupts:process_interrupted_error_message). | |
1041 | ||
1042 | :- use_module(tcltk_interface,[compute_all_transitions_if_necessary/2]). | |
1043 | compute_transitions_if_necessary(From) :- | |
1044 | not_all_transitions_added(From),!, | |
1045 | decrease_max_nr_of_new_nodes(From), | |
1046 | compute_all_transitions_if_necessary(From,false). | |
1047 | compute_transitions_if_necessary(_From). | |
1048 | ||
1049 | decrease_max_nr_of_new_nodes(ID) :- | |
1050 | retract(max_nr_of_new_nodes(Max)),!, | |
1051 | ( Max>0 -> | |
1052 | NewMax is Max-1, | |
1053 | assertz(max_nr_of_new_nodes(NewMax)) | |
1054 | ; Max=0 -> NM is -1, | |
1055 | assertz(max_nr_of_new_nodes(NM)), | |
1056 | add_warning(state_space,'Maximum number of new nodes reached for CTL/LTL/refinement check, node id = ',ID), | |
1057 | fail | |
1058 | ; % negative number: re-assert and fail | |
1059 | assertz(max_nr_of_new_nodes(Max)), | |
1060 | fail). | |
1061 | decrease_max_nr_of_new_nodes(_). % no limit stored; just proceed | |
1062 | ||
1063 | % will be called from TCL/TK side | |
1064 | max_nr_of_new_nodes_limit_not_reached :- | |
1065 | max_nr_of_new_nodes(N),N>0. | |
1066 | ||
1067 | :- use_module(specfile,[b_or_z_mode/0, csp_mode/0, csp_with_bz_mode/0]). | |
1068 | retract_open_node(NodeID) :- retract_open_node_and_update_processed_nodes(NodeID), | |
1069 | (b_or_z_mode -> assertz(not_invariant_checked(NodeID)) ; true). | |
1070 | ||
1071 | reset_processed_nodes_counter :- reset_counter(processed_nodes). | |
1072 | %reset_processed_nodes_counter(Nr) :- set_counter(processed_nodes,Nr). | |
1073 | ||
1074 | retract_open_node_and_update_processed_nodes(NodeID) :- | |
1075 | retract_open_node_direct(NodeID), | |
1076 | inc_processed. | |
1077 | ||
1078 | inc_processed :- | |
1079 | inc_counter(processed_nodes). | |
1080 | ||
1081 | pop_id_from_front(ID) :- pop_id_from_front_direct(ID), inc_processed. | |
1082 | pop_id_from_end(ID) :- pop_id_from_end_direct(ID), inc_processed. | |
1083 | pop_id_oldest(ID) :- pop_id_oldest_direct(ID), inc_processed. | |
1084 | ||
1085 | ||
1086 | ||
1087 | /* --------------------------------- */ | |
1088 | ||
1089 | % find initialised states; very similar to is_initial_state_id/1 | |
1090 | % but is used by ltl/ctl/sap | |
1091 | % TO DO: merge these two variations of the same concept | |
1092 | ||
1093 | :- use_module(specfile,[animation_mode/1]). | |
1094 | ||
1095 | find_initialised_states(Init) :- | |
1096 | animation_mode(Mode), | |
1097 | ( init_states_mode_cst_init(Mode) -> | |
1098 | findall(I,find_init1(root,I,_),Init) | |
1099 | ; init_states_mode_one_step(Mode) -> | |
1100 | next_states_from_root(Init) | |
1101 | ; | |
1102 | fail). | |
1103 | ||
1104 | % find trace to some initialised state | |
1105 | find_trace_to_initial_state(Target,Trace) :- animation_mode(Mode), | |
1106 | find_aux(Mode,Target,Trace). | |
1107 | find_aux(Mode,Target,[root,Target]) :- | |
1108 | init_states_mode_one_step(Mode). | |
1109 | find_aux(Mode,Target,[root|Trace]) :- | |
1110 | init_states_mode_cst_init(Mode), | |
1111 | find_init1(root,Target,Trace). | |
1112 | ||
1113 | ||
1114 | init_states_mode_cst_init(b). | |
1115 | init_states_mode_cst_init(z). | |
1116 | init_states_mode_cst_init(csp_and_b). | |
1117 | ||
1118 | init_states_mode_one_step(csp). | |
1119 | init_states_mode_one_step(cspm). | |
1120 | init_states_mode_one_step(xtl). | |
1121 | %init_states_mode_one_step(promela). | |
1122 | ||
1123 | next_states_from_root(States) :- | |
1124 | impl_trans_term_all(root,Ops), | |
1125 | findall(S, member(op(_Id,_,S),Ops), States). | |
1126 | ||
1127 | find_init1(Start,Init,Trace) :- Start==Init,!,Trace=[]. % usually called with Start=Init=root | |
1128 | find_init1(Start,Init,[State|Rest]) :- | |
1129 | impl_trans_term(Start,O,State), | |
1130 | find_init2(O,State,Init,Rest). | |
1131 | find_init2(O,Init,Init,[]) :- | |
1132 | has_functor_and_maybe_tau(O,'$initialise_machine'). | |
1133 | find_init2(O,State,Init,Path) :- | |
1134 | has_functor_and_maybe_tau(O,'$setup_constants'), | |
1135 | find_init1(State,Init,Path). | |
1136 | find_init2(start_cspm_MAIN,State,Init,Path) :- | |
1137 | find_init1(State,Init,Path). | |
1138 | find_init2(start_cspm(_Proc),State,Init,Path) :- | |
1139 | find_init1(State,Init,Path). | |
1140 | ||
1141 | % has_functor_and_maybe_tau(Term,Functor) | |
1142 | % checks if Term has the form "Functor(...)" or "tau(Functor(...))" | |
1143 | % this is used for CSP||B specification where the initialisation is wrapped with | |
1144 | % in a tau operator | |
1145 | has_functor_and_maybe_tau(tau(Term),Functor) :- | |
1146 | has_functor_and_maybe_tau(Term,Functor),!. | |
1147 | has_functor_and_maybe_tau(Term,Functor) :- | |
1148 | functor(Term,Functor,_). | |
1149 | ||
1150 | % compute how far the state is from the root node using back_edge markers (if available) | |
1151 | try_compute_depth_of_state_id(root,R) :- !, R=0. | |
1152 | try_compute_depth_of_state_id(Node,Depth) :- id_back_edge(Node,Depth,_Back). | |
1153 | ||
1154 | % optional registering of back_edges: to quickly find trace from root and to computed depth/diameter | |
1155 | register_back_edge(ID,FromID) :- | |
1156 | try_compute_depth_of_state_id(FromID,D),!, | |
1157 | D1 is D+1, | |
1158 | assertz(id_back_edge(ID,D1,FromID)). | |
1159 | register_back_edge(ID,FromID) :- write(cannot_store_back_edge(ID,FromID)),nl. | |
1160 | ||
1161 | /* --------------------------------- */ | |
1162 | ||
1163 | % | |
1164 | % Code to compute equivalence classes | |
1165 | % using the standard DFA minimization algorithm | |
1166 | ||
1167 | :- dynamic equivalent/2. | |
1168 | % state_space:compute_equivalence_classes | |
1169 | :- public compute_equivalence_classes/0. | |
1170 | ||
1171 | compute_equivalence_classes :- init_equi, | |
1172 | split_equivalence_classes,nl, | |
1173 | print_equi. | |
1174 | ||
1175 | print_equi :- state_space:equivalent(A,B), visited_expression(A,State), | |
1176 | visited_expression(B,StateB), | |
1177 | nl, | |
1178 | print(A), print(' : '), print(State),nl, | |
1179 | print(B), print(' : '), print(StateB),nl,fail. | |
1180 | print_equi. | |
1181 | ||
1182 | init_equi :- retractall(equivalent(_,_)), | |
1183 | packed_visited_expression(ID,_State), | |
1184 | \+ not_all_transitions_added(ID), | |
1185 | findall(Action,transition(ID,Action,_,_),List), | |
1186 | packed_visited_expression(ID2,_S2), ID2 @> ID, | |
1187 | \+ not_all_transitions_added(ID2), | |
1188 | findall(Action,transition(ID2,Action,_,_),List), | |
1189 | assertz(equivalent(ID,ID2)), % they have the same signature | |
1190 | %print(equivalent(ID,ID2)),nl, | |
1191 | fail. | |
1192 | init_equi :- print(finished_initialising),nl. | |
1193 | ||
1194 | split_equivalence_classes :- retractall(echange), | |
1195 | equivalent(ID1,ID2), | |
1196 | transition(ID1,A,_,Dest1), | |
1197 | transition(ID2,A,_,Dest2), | |
1198 | \+ check_equi(Dest1,Dest2), | |
1199 | retract(equivalent(ID1,ID2)), % splitting class | |
1200 | % print(diff(ID1,ID2, A, Dest1, Dest2)),nl, | |
1201 | assert_echange, | |
1202 | fail. | |
1203 | split_equivalence_classes :- echange -> split_equivalence_classes ; true. | |
1204 | ||
1205 | :- dynamic echange/0. | |
1206 | assert_echange :- echange -> true ; assertz(echange),print('.'),flush_output. | |
1207 | ||
1208 | check_equi(A,B) :- A=B -> true ; A @<B -> equivalent(A,B) ; equivalent(B,A). | |
1209 | ||
1210 | /* | |
1211 | % benchmark how much time it takes to copy the state space state_space:bench_state_space. | |
1212 | bench_state_space :- | |
1213 | statistics(walltime,_), | |
1214 | (state_space:packed_visited_expression(ID,S), assertz(pve(ID,S)),fail ; true), | |
1215 | statistics(walltime,[_,Delta]), format('Time to copy packed_visited_expression: ~w ms~n',[Delta]), | |
1216 | (state_space:transition(A,B,C,D), assertz(tr(A,B,C,D)),fail ; true), | |
1217 | statistics(walltime,[_,Delta2]), format('Time to copy transition: ~w ms~n',[Delta2]), | |
1218 | (state_packing:stored_value(A,B), assertz(sv(A,B)),fail ; true), | |
1219 | (state_packing:stored_value_hash_to_id(A,B), assertz(svhi(A,B)),fail ; true), | |
1220 | statistics(walltime,[_,Delta3]), format('Time to copy stored_value: ~w ms~n',[Delta3]). | |
1221 | */ | |
1222 | ||
1223 | :- public portray_state_space/0. | |
1224 | portray_state_space :- packed_visited_expression(ID,S), functor(S,F,N), | |
1225 | format('State ~w : ~w/~w~n',[ID,F,N]), fail. | |
1226 | portray_state_space :- transition(ID,Action,TransID,DestID), | |
1227 | format(' ~w: ~w -- ~w --> ~w~n',[TransID,ID,Action,DestID]),fail. | |
1228 | portray_state_space. | |
1229 | ||
1230 | bench_state_space :- statistics(walltime,[W1,_]), | |
1231 | (packed_visited_expression(_,_), fail ; true), | |
1232 | statistics(walltime,[W2,_]), T1 is W2-W1, | |
1233 | format('Time to inspect all states: ~w ms walltime~n',[T1]), | |
1234 | (visited_expression(_,_), fail ; true), | |
1235 | statistics(walltime,[W3,_]), T2 is W3-W2, | |
1236 | format('Time to inspect and unpack all states: ~w ms walltime~n',[T2]), | |
1237 | (transition(_,_,_,_), fail ; true), | |
1238 | statistics(walltime,[W4,_]), T3 is W4-W3, | |
1239 | format('Time to inspect all transitions: ~w ms walltime~n',[T3]), | |
1240 | (visited_expression(_,E), my_term_hash(E,_), fail ; true), | |
1241 | statistics(walltime,[W5,_]), T4 is W5-W4, | |
1242 | format('Time to inspect, unpack and hash all states: ~w ms walltime~n',[T4]). | |
1243 | ||
1244 | % ---------------------------- | |
1245 | % COUNTER EXAMPLE MANAGEMENT | |
1246 | ||
1247 | % store counter example nodes and transition ids; used by LTL model checking for example | |
1248 | ||
1249 | ||
1250 | :- dynamic counterexample_node/1. | |
1251 | :- dynamic counterexample_op/1. | |
1252 | ||
1253 | add_counterexample_node(NodeID) :- assertz(counterexample_node(NodeID)). | |
1254 | add_counterexample_op(TransID) :- | |
1255 | (counterexample_op(TransID) -> true ; assertz(counterexample_op(TransID))). | |
1256 | ||
1257 | ||
1258 | reset_counterexample :- | |
1259 | retractall(counterexample_node(_)), | |
1260 | retractall(counterexample_op(_)). | |
1261 | ||
1262 | :- register_event_listener(play_counterexample,reset_counterexample, | |
1263 | 'Reset marked nodes from previous counterexamples.'). | |
1264 | ||
1265 | set_counterexample_by_transition_ids(TransIds) :- | |
1266 | set_trace_by_transition_ids(TransIds), | |
1267 | maplist(add_counterexample_op,TransIds), | |
1268 | extract_history_from_transition_ids(TransIds,root,[],[],_Last,History,_OpTrace), | |
1269 | maplist(add_counterexample_node,History). |