1 % (c) 2009-2019 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, gen_new_state_id/1,
7 history/1, forward_history/1,
8 get_action_trace/1, % old trace/1
9 get_action_term_trace/1,
10 op_trace_ids/1, add_to_op_trace_ids/1, remove_from_op_trace_ids/1, reset_op_trace_ids/0,
11 get_state_id_trace/1,
12 current_state_id/1, current_expression/2,
13 current_options/1, set_current_options/1,
14
15 add_id_at_front/1, add_id_at_end/1,
16 add_id_random/1, /* from state_space_open_nodes_c */
17 add_id_with_weight/2, add_id_to_process/3,
18 pop_id_from_front/1, pop_id_from_end/1, pop_id_oldest/1,
19 retract_open_node/1, open_ids_empty/0,
20
21 visited_expression/3, visited_expression/2, visited_expression_id/1,
22 find_hashed_packed_visited_expression/3,
23 retract_visited_expression/2,
24 not_all_transitions_added/1,
25 not_invariant_checked/1, set_invariant_checked/1,
26 invariant_not_yet_checked/1,
27 not_interesting/1,max_reached_for_node/1,
28 max_reached_or_timeout_for_node/1,
29 use_no_timeout/1, time_out_for_node/1, time_out_for_node/3,
30 hash_to_id/2,id_to_marker/2, hash_to_nauty_id/2,
31 invariant_violated/1, time_out_for_invariant/1, time_out_for_assertions/1,
32 set_invariant_violated/1,
33
34 state_error_exists/0, state_error/3, store_state_error/3,
35 set_context_state/1, clear_context_state/0, get_current_context_state/1,
36 store_error_for_context_state/2,
37 store_abort_error_for_context_state_if_possible/4,
38
39 transition/3,transition/4, any_transition/3,
40 store_transition/4,
41 deadlocked_state/1, % no outgoing edge
42 is_initial_state_id/1, is_concrete_constants_state_id/1,
43 multiple_concrete_constants_exist/0,
44 out_degree/2,
45 operation_not_yet_covered/1, operation_name_not_yet_covered/1,
46
47 transition_info/2, store_transition_infos/2,
48 compute_transitions_if_necessary/1,
49
50 state_space_initialise/0, state_space_initialise_with_stats/0,
51 state_space_reset/0,
52 state_space_add/2, state_space_packed_add/2,
53 delete_node/1,
54
55 current_state_corresponds_to_initialised_b_machine/0,
56 current_state_corresponds_to_fully_setup_b_machine/0,
57 current_state_corresponds_to_setup_constants_b_machine/0,
58
59 specialized_inv/2, reuse_operation/4,
60 assert_max_reached_for_node/1, assert_time_out_for_node/3,
61 assert_time_out_for_invariant/1, assert_time_out_for_assertions/1,
62
63 set_max_nr_of_new_impl_trans_nodes/1,
64 get_max_nr_of_new_impl_trans_nodes/1,
65 impl_trans_term/3, impl_trans_term_all/2,
66 compute_transitions_if_necessary_saved/1,
67 max_nr_of_new_nodes_limit_not_reached/0,
68
69 tcltk_save_state/1, tcltk_load_state/1,
70 compute_full_state_space_hash/1,
71
72 execute_id_trace_from_current/3,
73 set_trace_by_transition_ids/1,
74 extract_term_trace_from_transition_ids/2]).
75
76 :- use_module(library(lists)).
77
78 :- use_module(self_check).
79 :- use_module(error_manager).
80 :- use_module(gensym).
81 :- use_module(preferences).
82 :- use_module(tools).
83 %:- use_module(state_space_exploration_modes,[compute_hash/3]).
84
85 :- use_module(extension('counter/counter')).
86
87 :- use_module(module_information).
88 :- module_info(group,animator).
89 :- module_info(description,'This module keeps track of the visited states by the animator/model checker.').
90
91 % ----------------------------------
92
93 :- load_files(library(system), [when(compile_time), imports([environ/2])]).
94 :- if(environ(prob_myheap,false)).
95 :- use_module(state_space_open_nodes). %% comment in to use only Prolog datastructures
96 :- else.
97 :- use_module(state_space_open_nodes_c). %% comment in to use C++ multimap queue; can make use of HEURISTIC_FUNCTION
98 :- endif.
99
100 % ----------------------------------
101
102 get_state_space_stats(NrNodes,NrTransitions,ProcessedNodes) :-
103 get_counter(states,N), NrNodes is N+1,
104 get_counter(transitions,NrTransitions),
105 get_counter(processed_nodes,ProcessedNodes).
106
107 gen_new_state_id(Nr) :-
108 inc_counter(states,N1), Nr is N1-1. % only one C call
109 %get_counter(states,Nr), inc_counter(states).
110 reset_state_counter :- reset_counter(states).
111 reset_state_counter(Nr) :- set_counter(states,Nr).
112
113 get_state_id_trace(StateIds) :-
114 history(History),
115 current_state_id(CurID),
116 reverse([CurID|History],StateIds).
117
118 :- dynamic history/1.
119 history([]).
120
121
122 :- dynamic forward_history/1.
123
124 :- dynamic current_state_id/1.
125 current_state_id(root).
126 /* INITIAL STATE, third arg: clp(fd) constraints as generated
127 by fd_copy_term */
128
129 current_expression(ID,State) :- current_state_id(ID),
130 visited_expression(ID,State).
131
132 :- use_module(specfile,[state_corresponds_to_initialised_b_machine/1,
133 state_corresponds_to_fully_setup_b_machine/1,
134 state_corresponds_to_set_up_constants/1]).
135 current_state_corresponds_to_initialised_b_machine:-
136 current_expression(_,State),
137 state_corresponds_to_initialised_b_machine(State).
138
139 current_state_corresponds_to_fully_setup_b_machine :-
140 current_expression(_,State),
141 state_corresponds_to_fully_setup_b_machine(State).
142
143 current_state_corresponds_to_setup_constants_b_machine :-
144 current_expression(_,State),
145 state_corresponds_to_set_up_constants(State).
146
147
148 :- dynamic current_options/1.
149 current_options([]).
150
151 set_current_options(Options) :-
152 retractall( current_options(_) ),
153 assert( current_options(Options) ).
154
155 :- dynamic packed_visited_expression/2.
156 %packed_visited_expression(v_0,true).
157
158 :- use_module(state_packing).
159
160 retract_visited_expression(ID,State) :- retract(packed_visited_expression(ID,PState)),
161 unpack_state(PState,State).
162
163 retractall_visited_expression(ID) :- retractall(packed_visited_expression(ID,_)).
164
165 state_space_packed_add(Id,PackedTerm) :- assert(packed_visited_expression(Id,PackedTerm)).
166
167 state_space_add(Id,Term) :-
168 (pack_state(Term,PackedTerm) -> assert(packed_visited_expression(Id,PackedTerm))
169 ; add_internal_error('State packing failed: ',pack_state(Term,_)),
170 assert(packed_visited_expression(Id,Term))).
171
172 % deprecated:
173 visited_expression(ID,State,true) :- visited_expression(ID,State).
174 % not call(CurBody); for the moment we always have true as last argument
175
176 ?visited_expression(A,B) :- packed_visited_expression(A,PB),
177 (unpack_state(PB,R) -> B=R ; add_internal_error('Unpacking state failed: ',unpack_state(PB,R)),R=A).
178
179 visited_expression_id(A) :- packed_visited_expression(A,_). % avoid unpacking state
180
181
182
183 % given a hash and a packed state: find ID (fail if does not exist)
184 find_hashed_packed_visited_expression(Hash,PackedState,ID) :-
185 hash_to_id(Hash,ID),
186 (packed_visited_expression(ID,PackedState)
187 -> true /* warning: may instantiate State if not ground */
188 ; print(hash_collision(Hash,ID)),nl,fail
189 ).
190
191
192 :- dynamic not_invariant_checked/1.
193 set_invariant_checked(ID) :- %print(inv_checked(ID)),nl,
194 retract(not_invariant_checked(ID)).
195
196 invariant_not_yet_checked(ID) :-
197 not_all_transitions_added(ID) ; /* assumption: if not all transitions added then we haven't checked invariant yet */
198 not_invariant_checked(ID).
199
200 :- dynamic not_interesting/1.
201 %not_interesting(v_0).
202
203 :- dynamic max_reached_for_node/1.
204 /* true if not all outgoing transistions were computed due to the limit
205 on the number of operations/initialisations computed */
206 :- dynamic time_out_for_node/3, use_no_timeout/1, time_out_for_invariant/1, time_out_for_assertions/1.
207
208 time_out_for_node(ID) :- (var(ID) -> packed_visited_expression(ID,_) ; true),
209 (time_out_for_node(ID,_,_) -> true ; fail).
210
211 :- dynamic transition/4.
212 %transition(v_0,a,1,v_1).
213
214 store_transition(Org,Action,Dest,Id) :-
215 %get_counter(transitions,Id), inc_counter(transitions),
216 inc_counter(transitions,Id1), Id is Id1-1, % only one C call
217 assert(transition(Org,Action,Id,Dest)).
218
219 deadlocked_state(Origin) :- \+ any_transition(Origin,_,_).
220
221 is_concrete_constants_state_id(ID) :-
222 transition(root,_,ID),
223 packed_visited_expression(ID,concrete_constants(_)).
224
225 % check if we have multiple constant setups
226 multiple_concrete_constants_exist :-
227 is_concrete_constants_state_id(ID),
228 is_concrete_constants_state_id(ID2), ID2 \= ID,!.
229
230
231 is_initial_state_id(InitialStateID) :-
232 transition(root,_,State),
233 packed_visited_expression(State,P),
234 (P = concrete_constants(_)
235 -> state_space:transition(State,_,InitialStateID)
236 ; InitialStateID=State).
237
238 any_transition(Origin,TransID,Destination) :- transition(Origin,_,TransID,Destination).
239
240 transition(Origin,Action,Destination) :- transition(Origin,Action,_TransID,Destination).
241
242 :- dynamic transition_info/2.
243 store_transition_infos([],_TransId).
244 store_transition_infos([Info|Irest],TransId) :-
245 store_transition_info(Info,TransId),
246 store_transition_infos(Irest,TransId).
247 store_transition_info(Info,TransId) :- %print(info(Info,TransId)),nl,
248 (keep_transition_info(Info)
249 -> assert(transition_info(TransId,Info))
250 ; true).
251
252 % Do not store path info by default
253 keep_transition_info(path(_)) :- !,fail.
254 keep_transition_info(eventtrace(_)) :- !,preference(eventtrace,true).
255 keep_transition_info(event(_)) :- !,preference(store_event_transinfo,true).
256 keep_transition_info(_). % store everything else
257
258 reset_transition_store :-
259 retractall(transition(_,_,_,_)),
260 retractall(transition_info(_,_)),
261 reset_counter(transitions).
262
263 /*
264 Version with packing of transitions:
265 store_transition(Org,Action,Dest,Id) :-
266 retract(transition_counter(Id)),
267 NewId is Id+1,
268 assert(transition_counter(NewId)),
269 Action =.. [ActionName|Parameters],
270 pack_values(Parameters,PackedParameters),
271 assert(packed_transition(Org,ActionName,PackedParameters,Id,Dest)).
272
273 transition(Origin,Action,TransID,Destination) :- nonvar(Action),!,
274 Action =.. [ActionName|Parameters],
275 packed_transition(Origin,ActionName,PackedParameters,TransID,Destination),
276 unpack_values(PackedParameters,Parameters).
277 transition(Origin,Action,TransID,Destination) :-
278 packed_transition(Origin,ActionName,PackedParameters,TransID,Destination),
279 unpack_values(PackedParameters,Parameters),
280 Action =.. [ActionName|Parameters].
281 any_transition(Origin,TransID,Destination) :- packed_transition(Origin,_,_,TransID,Destination).
282 */
283
284 % compute out-degree of a node
285 out_degree(ID,OutDegree) :- findall(0, transition(ID,_,_,_), L), length(L,OutDegree).
286
287 operation_name_not_yet_covered(OpName) :- operation_not_yet_covered(OpName).
288
289 :- dynamic operation_not_yet_covered/1.
290 %operation_not_yet_covered(b).
291
292 state_error_exists :- state_error(_,_,_),!.
293 :- dynamic state_error/3, context_state/2.
294
295 %state_error([],invariant_violated).
296
297 reset_next_state_error_id_counter :- reset_counter(next_state_error_id).
298 :- use_module(tools_printing, [print_error/1]).
299 :- use_module(error_manager,[print_error_span/1]).
300 store_state_error(State,Error,Id) :- state_error(State,Id,Error),!. % do not store identical error twice
301 store_state_error(State,Error,Id) :-
302 %retract( next_state_error_id(Id) ),
303 inc_counter(next_state_error_id,Id),
304 assert( state_error(State,Id,Error) ).
305 store_error_for_context_state(Error,Id) :-
306 ( retract(context_state(State,Errs)) ->
307 (Errs<25 -> store_state_error(State,Error,Id), E1 is Errs+1,assert(context_state(State,E1))
308 ; store_state_error(State,max_state_errors_reached(25),Id),assert(context_state(State,25))
309 )
310 ; otherwise ->
311 add_internal_error('No known context when calling store_error_for_context_state: ',store_error_for_context_state(Error,Id)),
312 fail).
313 store_abort_error_for_context_state_if_possible(ErrType,Msg,Term,Span) :-
314 %print(store(Msg,Term,Span)),nl,
315 ( context_state(State,_Errs) ->
316 error_manager:get_error_context(Context),
317 (abort_error_for_same_location_exists(State,Id1,ErrType,Msg,Span),
318 abort_error_for_same_location_exists(State,Id2,ErrType,Msg,Span),
319 Id2>Id1
320 -> /* two errors of same type, for same state and same source location exists */
321 /* TO DO: maybe merge state errors */
322 simplify_span(Span,Span2),
323 store_state_error(State,abort_error(ErrType,'Further identical errors occurred (not stored !)',Term,span_context(Span2,Context)),_)
324 ;
325 print_error('An abort error occurred !'), print_error(ErrType),
326 print_error(Msg),
327 print_error_term(Term),
328 print_error(context_state_id(State)),
329 print_error_span(Span),
330 store_state_error(State,abort_error(ErrType,Msg,Term,span_context(Span,Context)),_)
331 ),
332 (add_new_event_in_error_scope(abort_error(ErrType)) -> true ; true),
333 assert_real_error_occurred
334 ; otherwise ->
335 add_error(ErrType,Msg,Term,Span)).
336
337 :- use_module(translate, [translate_error_term/2]).
338 print_error_term(Term) :- translate_error_term(Term,S), print_error(S).
339
340 abort_error_for_same_location_exists(State,Id,ErrType,Msg,Span) :-
341 state_error(State,Id,abort_error(ErrType,Msg,_Term2,span_context(Span2,_Ctxt2))),
342 same_span_location(Span2,Span).
343 % should be moved to error_manager ?
344 same_span_location(span_predicate(Pred,_,_),span_predicate(Pred,_,_)) :- !.
345 same_span_location(X,X).
346
347 % remove state information; quite often the same WD error occurs with slightly different stores
348 simplify_span(span_predicate(Pred,_,_),R) :- !, R=Pred.
349 simplify_span(X,X).
350
351 clear_context_state :-
352 retractall(context_state(_,_)).
353 set_context_state(State) :-
354 %print(setting_ctxt_state(State,Event)),nl,
355 retractall(context_state(_,_)), assert(context_state(State,0)).
356 get_current_context_state(ID) :- context_state(ID,_).
357
358 retractall_invariant_violated(State) :-
359 retractall(state_error(State,_,invariant_violated)).
360 invariant_violated(State) :-
361 ? state_error(State,_,invariant_violated).
362 set_invariant_violated(State) :-
363 ( invariant_violated(State) -> true
364 ; time_out_for_invariant(ID) -> print('Timeout for node: '), print(ID),nl,
365 print('Not setting invariant violation status'),nl
366 ; store_state_error(State,invariant_violated,_)
367 ).
368
369 %:- set_invariant_violated([]). % why is this ??
370
371
372 :- dynamic hash_to_id/2.
373 :- dynamic id_to_marker/2.
374
375 :- dynamic hash_to_nauty_id/2. % used in nauty mode to map nauty id's to hash values
376
377 :- dynamic specialized_inv/2. /* stores whether for a node a specialized invariant
378 version could be computed */
379
380 :- dynamic reuse_operation/4. /* when for a state and given operation name we can reuse the operation computed for another state */
381
382 :- use_module(hashing).
383 state_space_startup :- % call once at startup to ensure all counters exist
384 counter_init,
385 new_counter(states), new_counter(processed_nodes), new_counter(transitions),
386 new_counter(next_state_error_id),
387 reset_open_ids. % also calls myheap init
388 state_space_initialise :- counter_init, reset_gennum, reset_gensym,
389 new_counter(states), new_counter(processed_nodes), new_counter(transitions),
390 new_counter(next_state_error_id),
391 reset_state_counter, reset_processed_nodes_counter, reset_next_state_error_id_counter,
392 retractall_visited_expression(_),
393 reset_open_ids, reset_stored_values,
394 retractall(not_invariant_checked(_)),
395 retractall(not_interesting(_)),
396 retractall(max_reached_for_node(_)),
397 retractall(time_out_for_node(_,_,_)),
398 retractall(time_out_for_invariant(_)),
399 retractall(time_out_for_assertions(_)),
400 retractall(use_no_timeout(_)),
401 retractall(state_error(_,_,_)),
402 clear_context_state,
403 reset_transition_store,
404 retractall(operation_not_yet_covered(_)),
405 retractall(hash_to_id(_,_)),
406 retractall(hash_to_nauty_id(_,_)),
407 retractall(id_to_marker(_,_)),
408 retractall(specialized_inv(_,_)),
409 retractall(reuse_operation(_,_,_,_)),
410 state_space_add(root,root),
411 add_id_at_front(root),
412 hashing:my_term_hash(root,RootHash),
413 assert(hash_to_id(RootHash,root)),
414 %assert(not_invariant_checked(root)),
415 state_space_reset.
416
417 :- use_module(eventhandling,[register_event_listener/3]).
418 :- register_event_listener(startup_prob,state_space_startup,
419 'Initialise Statespace Counters.').
420 :- register_event_listener(reset_specification,state_space_initialise,
421 'Reset Statespace.').
422 :- register_event_listener(change_of_animation_mode,state_space_initialise,
423 'Reset Statespace.').
424
425 /* A version of reset which checks how much memory is used by each fact */
426 /* state_space:init_with_stats */
427 state_space_initialise_with_stats :-
428 reset_gennum, reset_gensym, reset_state_counter, reset_processed_nodes_counter,
429 reset_next_state_error_id_counter,
430 retract_open_ids_with_statistics,
431 retract_with_statistics(state_space,[packed_visited_expression(_,_),
432 not_invariant_checked(_),
433 not_interesting(_),
434 max_reached_for_node(_),
435 time_out_for_node(_,_,_),
436 time_out_for_invariant(_),
437 time_out_for_assertions(_),
438 use_no_timeout(_),
439 state_error(_,_,_),
440 transition(_,_,_,_),
441 transition_info(_,_),
442 operation_not_yet_covered(_),
443 hash_to_id(_,_),
444 hash_to_nauty_id(_,_),
445 id_to_marker(_,_),
446 specialized_inv(_,_),
447 reuse_operation(_,_,_,_),
448 history(_), forward_history(_), op_trace_ids(_)]),
449 retract_stored_values_with_statistics,
450 clear_context_state,
451 reset_transition_store,
452 state_space_add(root,root),
453 add_id_at_front(root),
454 %assert(not_invariant_checked(root)),
455 state_space_reset.
456
457
458
459 :- dynamic op_trace_ids/1.
460 reset_trace :- retractall(op_trace_ids(_)), assert(op_trace_ids([])).
461 get_action_trace(T) :- trace(T).
462 get_action_term_trace(PT) :- trace_with_limit(0,T), project_on_action_term(T,PT).
463 trace(Trace) :- trace_with_limit(500,Trace).
464 trace_with_limit(Limit,Trace) :-
465 op_trace_ids(IDT), reverse(IDT,RIDT),
466 extract_trace_from_transition_ids(RIDT,root,Limit,[],Trace).
467
468 reset_op_trace_ids :- retractall(op_trace_ids(_)), assert(op_trace_ids([])).
469 add_to_op_trace_ids(OpID) :- retract(op_trace_ids(OpIDS)), assert(op_trace_ids([OpID|OpIDS])).
470 remove_from_op_trace_ids(OpID) :- retract(op_trace_ids(OpIDS)),
471 (OpIDS = [R|Rest] -> assert(op_trace_ids(Rest)), OpID = R
472 ; assert(op_trace_ids(OpIDS)),fail).
473
474 % translate a list of transition ids (from root) into a list of operation terms
475 extract_term_trace_from_transition_ids(TransIDListFromRoot,Trace) :-
476 extract_trace_from_transition_ids(TransIDListFromRoot,root,0,[],ActionTrace),
477 reverse_and_project_on_action_term(ActionTrace,[],Trace).
478
479 reverse_and_project_on_action_term([],A,A).
480 reverse_and_project_on_action_term([action(_,Term)|T],Acc,Res) :- !,
481 reverse_and_project_on_action_term(T,[Term|Acc],Res).
482 reverse_and_project_on_action_term([H|T],Acc,Res) :-
483 add_error(reverse_and_project_on_action_term,'Illegal action: ',H),
484 reverse_and_project_on_action_term(T,[H|Acc],Res).
485
486 project_on_action_term([],[]).
487 project_on_action_term([action(_,Term)|T],Res) :- !, Res=[Term|TR],
488 project_on_action_term(T,TR).
489 project_on_action_term([H|T],Res) :-
490 add_error(project_on_action_term,'Illegal action: ',H),
491 project_on_action_term(T,Res).
492
493 extract_trace_from_transition_ids([],_CurrentState,_,Trace,Trace).
494 extract_trace_from_transition_ids([TransId|Rest],CurrentState,Limit,AccTrace,Trace) :-
495 compute_op_string(TransId,CurrentState,Limit,OpTerm,OpString,DestState),!,
496 extract_trace_from_transition_ids(Rest,DestState,Limit,
497 [action(OpString,OpTerm)|AccTrace],Trace).
498 extract_trace_from_transition_ids([TransId|_],CurrentState,_,_,_Trace) :-
499 add_error(state_space,'Could not execute transition id: ', TransId:from(CurrentState)),fail.
500
501 :- use_module(translate,[translate_event_with_src_and_target_id/5]).
502 compute_op_string(jump(TO),_CurID,_,Term,String,DestID) :- !, Term=jump,String=jump,DestID=TO.
503 compute_op_string(TransId,CurID,Limit,Term,String,DestID) :- transition(CurID,Term,TransId,DestID),
504 translate_event_with_src_and_target_id(Term,CurID,DestID,Limit,String).
505
506 state_space_reset :-
507 reset_trace,
508 retractall(history(_)),
509 retractall(forward_history(_)),
510 retractall(current_state_id(_)),
511 retractall(current_options(_)),
512 assert(history([])),
513 assert(current_state_id(root)).
514
515
516 state_space_clean_all :-
517 retractall(state_space_version_in_file(_)),
518 retractall_visited_expression(_),
519 reset_open_ids,
520 retractall(not_invariant_checked(_)),
521 retractall(not_interesting(_)),
522 retractall(max_reached_for_node(_)),
523 retractall(time_out_for_node(_,_,_)),
524 retractall(time_out_for_invariant(_)),
525 retractall(time_out_for_assertions(_)),
526 retractall(use_no_timeout(_)),
527 retractall(state_error(_,_,_)),
528 clear_context_state,
529 reset_transition_store,
530 retractall(operation_not_yet_covered(_)),
531 retractall(hash_to_id(_,_)),
532 retractall(hash_to_nauty_id(_,_)),
533 retractall(id_to_marker(_,_)),
534 retractall(specialized_inv(_,_)),
535 retractall(reuse_operation(_,_,_,_)),
536 retractall(history(_)),
537 retractall(forward_history(_)),
538 retractall(op_trace_ids(_)),
539 retractall(current_state_id(_)),
540 retractall(current_options(_)).
541
542 % this is only used from within the Tcl/Tk animator at the moment:
543 delete_node(ID) :- print(deleting(ID)),nl,
544 retractall_visited_expression(ID),
545 retractall_invariant_violated(ID),
546 retract_open_node_and_update_processed_nodes(ID),
547 retractall(not_invariant_checked(ID)),
548 retractall(not_interesting(ID)),
549 retractall(max_reached_for_node(ID)),
550 retractall(time_out_for_node(ID,_,_)),
551 retractall(time_out_for_invariant(ID)),
552 retractall(time_out_for_assertions(ID)),
553 retractall(use_no_timeout(ID)),
554 retractall(state_error(ID,_,_)),
555 retractall(transition(ID,_,_,_)),
556 % to do: check if operation_not_yet_covered(_) changes
557 retract_hash(ID),
558 retractall(id_to_marker(ID,_)).
559
560 retract_hash(ID) :- retract(hash_to_id(Hash,ID)), retractall(hash_to_nauty_id(_TermHash,Hash)),fail.
561 retract_hash(_).
562
563 assert_max_reached_for_node(Id) :- %print_message(max_reached_for_node(Id)),
564 (max_reached_for_node(Id) -> true ; assert(max_reached_for_node(Id))).
565
566 assert_time_out_for_node(Id,OpName,TypeOfTimeOut) :- print_message(time_out_for_node(Id,OpName,TypeOfTimeOut)),
567 (time_out_for_node(Id,OpName,_) -> true ; assert(time_out_for_node(Id,OpName,TypeOfTimeOut))).
568 assert_time_out_for_invariant(Id) :- print_message(time_out_for_invariant(Id)),
569 (time_out_for_invariant(Id) -> true ; assert(time_out_for_invariant(Id))).
570 assert_time_out_for_assertions(Id) :- print_message(time_out_for_assertions(Id)),
571 (time_out_for_assertions(Id) -> true ; assert(time_out_for_assertions(Id))).
572
573 max_reached_or_timeout_for_node(Id) :-
574 (max_reached_for_node(Id) ; time_out_for_node(Id,_,_)).
575 /* ---------------------- */
576 /* state space saving */
577 /* ---------------------- */
578
579 :- dynamic state_space_version_in_file/1. %
580 state_space_version(1).
581
582 check_state_space_version :- state_space_version(V),
583 (state_space_version_in_file(F) -> true ; F=0),
584 (V>F -> add_message(state_space,'Warning: saved state_space may be incompatible with current version: ',F:V) ; true).
585
586 tcltk_save_state(File) :-
587 print('% saving state to: '), print(File),nl,
588 tell(File),
589 print_state_space,
590 told,
591 print_message(done).
592
593
594 :- use_module(tools_printing, [print_dynamic_fact/1,print_dynamic_pred/3]).
595 print_state_space :-
596 state_space_version(V),
597 print_dynamic_fact(state_space_version_in_file(V)),
598 % TO DO: maybe also save some important preferences, and warn user and/or propose to adapt preferences ?
599 print_dynamic_pred(state_space,history,1),
600 print_dynamic_pred(state_space,forward_history,1),
601 print_dynamic_pred(state_space,op_trace_ids,1),
602 print_dynamic_pred(state_space,current_state_id,1),
603 print_dynamic_pred(state_space,current_options,1),
604 print_dynamic_pred(state_space,packed_visited_expression,2),
605 print_dynamic_pred(state_space,not_invariant_checked,1),
606 print_dynamic_pred(state_space,not_interesting,1),
607 print_dynamic_pred(state_space,max_reached_for_node,1),
608 print_dynamic_pred(state_space,time_out_for_node,3),
609 print_dynamic_pred(state_space,use_no_timeout,1),
610 print_dynamic_pred(state_space,transition,4),
611 print_dynamic_pred(state_space,transition_info,2),
612 print_dynamic_pred(state_space,operation_not_yet_covered,1),
613 print_dynamic_pred(state_space,state_error,3),
614 print_state_space_open_nodes,
615 print_stored_values,
616 get_counter(states,X),
617 write_term(saved_gennum_count(X),[quoted(true)]),print('.'),nl.
618
619 saved_gennum_count(99999).
620
621 /* ---------------------- */
622 /* state space loading */
623 /* ---------------------- */
624
625 tcltk_load_state(File) :- state_space_clean_all,
626 print('Loading: '), print(File),nl,
627 user_consult_without_redefine_warning(File), % this will read in bind_skeleton/2, ..., next_value_id/1
628 check_state_space_version,
629 print('Generating open node info'),nl,
630 transfer_open_node_info,
631 print('Transfer state packing info'),nl,
632 transfer_state_packing_info,
633 print('Recomputing hash index'),nl,
634 recompute_all_hash,
635 (saved_gennum_count(X) -> reset_state_counter(X) ; true),
636 reset_processed_nodes_counter, % TO DO: restore or save it
637 reset_next_state_error_id_counter, % DITTO
638 print('Done'),nl,!.
639 tcltk_load_state(File) :-
640 add_error(tcltk_load_state,'Could not load state from file: ',File),
641 state_space_initialise.
642
643 :- dynamic not_all_z_saved/1, not_all_transitions_added_saved/1.
644 :- dynamic bind_skeleton/2, stored_value/2, stored_value_hash_to_id/2, next_value_id/1.
645
646 % transfer facts read into state_space into other modules:
647 transfer_open_node_info :- retract(not_all_z_saved(X)), %print(not_all_z(X)),nl,
648 assert_not_all_z(X),fail.
649 transfer_open_node_info :- retract(not_all_transitions_added_saved(X)),
650 assert_not_all_transitions_added(X),fail.
651 transfer_open_node_info.
652 % now for transferring to state_packing module info generated by print_stored_values
653 transfer_state_packing_info :- retract(bind_skeleton(X,Y)), %print(skel(X)),nl,
654 assert(state_packing:bind_skeleton(X,Y)),fail.
655 transfer_state_packing_info :- retract(stored_value(X,Y)),
656 assert(state_packing:stored_value(X,Y)),fail.
657 transfer_state_packing_info :- retract(stored_value_hash_to_id(X,Y)),
658 assert(state_packing:stored_value_hash_to_id(X,Y)),fail.
659 transfer_state_packing_info :- retract(next_value_id(X)),
660 assert(state_packing:next_value_id(X)),fail.
661 transfer_state_packing_info.
662
663 recompute_all_hash :-
664 retractall(hash_to_id(_,_)),retractall(id_to_marker(_,_)),
665 retractall(hash_to_nauty_id(_,_)),
666 visited_expression(ID,StateTemplate),
667 state_space_exploration_modes:compute_hash(StateTemplate,Hash,Marker),
668 assert(hash_to_id(Hash,ID)),
669 assert(id_to_marker(ID,Marker)),
670 fail.
671 recompute_all_hash.
672
673 :- use_module(hashing,[my_term_hash/2]).
674 % generates a hash for the entire state space not depending on the order in which states where added
675 compute_full_state_space_hash(Hash) :-
676 findall(Hash,hash_to_id(Hash,_),ListOfHashCodes),
677 sort(ListOfHashCodes,SortedList),
678 my_term_hash(SortedList,Hash).
679 % TO DO: also provide transition hashes
680
681 :- use_module(tools_meta,[safe_on_exception/3]).
682 user_consult_without_redefine_warning(File) :-
683 prolog_flag(redefine_warnings, Old, off),
684 prolog_flag(single_var_warnings, Old2, off),
685 (safe_on_exception(Exc,consult(File),(nl,print('Exception occurred:'),print(Exc),nl,fail))
686 -> OK=true ; OK=false),
687 prolog_flag(redefine_warnings, _, Old),
688 prolog_flag(single_var_warnings, _, Old2),
689 OK=true.
690
691
692
693 execute_id_trace_from_current(ID,OpIDL,StateIDList) :-
694 current_state_id(CurID),
695 reverse([CurID|StateIDList],Rev),
696 Rev = [Dest|TRev], (Dest==ID -> true ; (print(not_eq(Dest,ID)),nl)),
697 retract(history(H)), retractall(forward_history(_)),
698 append(TRev,H,NewH),
699 assert(history(NewH)),
700 retract(op_trace_ids(OldTrace)),
701 reverse(OpIDL,NewTrace),
702 append(NewTrace,OldTrace,Trace),
703 assert(op_trace_ids(Trace)),
704 retractall(current_state_id(_)),
705 assert(current_state_id(ID)).
706 %execute_trace_to_node(OpL,StateIDList). /* <----- BOTTLENECK FOR LONG SEQUENCES */
707 %generate_trace([],Acc,Acc).
708 %generate_trace([OpTerm|T],Acc,Res) :-
709 % translate:translate_event(OpTerm,OpString),
710 % generate_trace(T,[action(OpString,OpTerm)|Acc],Res).
711
712 set_trace_by_transition_ids(TransitionIds) :-
713 extract_history_from_transition_ids(TransitionIds,root,[],[],Last,History,OpTrace),
714 %visited_expression(Last,LastState,LastCond),
715 retractall(history(_)), retractall(forward_history(_)),
716 retractall(current_state_id(_)),
717 retractall(op_trace_ids(_)),
718 assert(history(History)), assert(op_trace_ids(OpTrace)),
719 assert(current_state_id(Last)).
720
721 extract_history_from_transition_ids([],CurrentState,History,Trace,CurrentState,History,Trace).
722 extract_history_from_transition_ids([TransId|Rest],CurrentState,AccHist,AccTrace,Last,History,Trace) :-
723 transition(CurrentState,_,TransId,DestState),!,
724 extract_history_from_transition_ids(Rest,DestState,[CurrentState|AccHist],
725 [TransId|AccTrace],Last,History,Trace).
726 extract_history_from_transition_ids([TransId|_],CurrentState,_,_,_,_,_Trace) :-
727 add_error(state_space,'Could not execute transition id: ', TransId:from(CurrentState)),fail.
728
729
730
731 /* --------------------------------- */
732 :- dynamic max_nr_of_new_nodes/1.
733
734 % negative number or non-number signifies no limit
735 set_max_nr_of_new_impl_trans_nodes(MaxNrOfNewNodes) :-
736 retractall(max_nr_of_new_nodes(_)),
737 (number(MaxNrOfNewNodes), MaxNrOfNewNodes>=0
738 -> assert(max_nr_of_new_nodes(MaxNrOfNewNodes))
739 ; true). % no need to store limit
740
741 get_max_nr_of_new_impl_trans_nodes(MaxNrOfNewNodes) :-
742 (max_nr_of_new_nodes(Max) -> MaxNrOfNewNodes=Max; MaxNrOfNewNodes = 0).
743
744 impl_trans_term(From,ActionAsTerm,To) :-
745 compute_transitions_if_necessary_saved(From),
746 transition(From,ActionAsTerm,_TID,To).
747
748 impl_trans_term_all(From,Ops) :-
749 compute_transitions_if_necessary_saved(From),
750 findall(op(Id,ActionAsTerm,To),
751 transition(From,ActionAsTerm,Id,To),
752 Ops).
753
754 compute_transitions_if_necessary_saved(From) :-
755 on_exception(error(forced_interrupt_error('User has interrupted the current execution'),_),
756 compute_transitions_if_necessary(From),
757 user:process_interrupted_error_message).
758
759 compute_transitions_if_necessary(From) :-
760 not_all_transitions_added(From),!,
761 decrease_max_nr_of_new_nodes(From),
762 %user:do_one_gui_event,
763 user:compute_all_transitions_if_necessary(From,false).
764 compute_transitions_if_necessary(_From).
765
766 decrease_max_nr_of_new_nodes(ID) :-
767 retract(max_nr_of_new_nodes(Max)),!,
768 ( Max>0 ->
769 NewMax is Max-1,
770 assert(max_nr_of_new_nodes(NewMax))
771 ; Max=0 -> NM is -1,
772 assert(max_nr_of_new_nodes(NM)),
773 add_warning(state_space,'Maximum number of new nodes reached for CTL/LTL/refinement check, node id = ',ID),
774 %trace,
775 fail
776 ; otherwise -> % negative number: re-assert and fail
777 assert(max_nr_of_new_nodes(Max)),
778 fail).
779 decrease_max_nr_of_new_nodes(_). % no limit stored; just proceed
780
781 % will be called from TCL/TK side
782 max_nr_of_new_nodes_limit_not_reached :-
783 max_nr_of_new_nodes(N),N>0.
784
785 :- use_module(specfile,[b_or_z_mode/0]).
786 retract_open_node(NodeID) :- retract_open_node_and_update_processed_nodes(NodeID),
787 (b_or_z_mode -> assert(not_invariant_checked(NodeID)) ; true).
788
789 reset_processed_nodes_counter :- reset_counter(processed_nodes).
790 %reset_processed_nodes_counter(Nr) :- set_counter(processed_nodes,Nr).
791
792 retract_open_node_and_update_processed_nodes(NodeID) :-
793 retract_open_node_direct(NodeID),
794 inc_processed.
795
796 inc_processed :-
797 inc_counter(processed_nodes).
798
799 pop_id_from_front(ID) :- pop_id_from_front_direct(ID), inc_processed.
800 pop_id_from_end(ID) :- pop_id_from_end_direct(ID), inc_processed.
801 pop_id_oldest(ID) :- pop_id_oldest_direct(ID), inc_processed.
802
803 /* --------------------------------- */
804
805 %
806 % Code to compute equivalence classes
807 % using the standard DFA minimization algorithm
808
809 :- dynamic equivalent/2.
810 % state_space:compute_equivalence_classes
811 :- public compute_equivalence_classes/0.
812
813 compute_equivalence_classes :- init_equi,
814 split_equivalence_classes,nl,
815 print_equi.
816
817 print_equi :- state_space:equivalent(A,B), visited_expression(A,State),
818 visited_expression(B,StateB),
819 nl,
820 print(A), print(' : '), print(State),nl,
821 print(B), print(' : '), print(StateB),nl,fail.
822 print_equi.
823
824 init_equi :- retractall(equivalent(_,_)),
825 packed_visited_expression(ID,_State),
826 \+ not_all_transitions_added(ID),
827 findall(Action,transition(ID,Action,_,_),List),
828 packed_visited_expression(ID2,_S2), ID2 @> ID,
829 \+ not_all_transitions_added(ID2),
830 findall(Action,transition(ID2,Action,_,_),List),
831 assert(equivalent(ID,ID2)), % they have the same signature
832 %print(equivalent(ID,ID2)),nl,
833 fail.
834 init_equi :- print(finished_initialising),nl.
835
836 split_equivalence_classes :- retractall(echange),
837 equivalent(ID1,ID2),
838 transition(ID1,A,_,Dest1),
839 transition(ID2,A,_,Dest2),
840 \+ check_equi(Dest1,Dest2),
841 retract(equivalent(ID1,ID2)), % splitting class
842 % print(diff(ID1,ID2, A, Dest1, Dest2)),nl,
843 assert_echange,
844 fail.
845 split_equivalence_classes :- echange -> split_equivalence_classes ; true.
846
847 :- dynamic echange/0.
848 assert_echange :- echange -> true ; assert(echange),print('.'),flush_output.
849
850 check_equi(A,B) :- A=B -> true ; A @<B -> equivalent(A,B) ; equivalent(B,A).
851
852 /*
853 % benchmark how much time it takes to copy the state space state_space:bench_state_space.
854 bench_state_space :-
855 statistics(walltime,_),
856 (state_space:packed_visited_expression(ID,S), assert(pve(ID,S)),fail ; true),
857 statistics(walltime,[_,Delta]), format('Time to copy packed_visited_expression: ~w ms~n',[Delta]),
858 (state_space:transition(A,B,C,D), assert(tr(A,B,C,D)),fail ; true),
859 statistics(walltime,[_,Delta2]), format('Time to copy transition: ~w ms~n',[Delta2]),
860 (state_packing:stored_value(A,B), assert(sv(A,B)),fail ; true),
861 (state_packing:stored_value_hash_to_id(A,B), assert(svhi(A,B)),fail ; true),
862 statistics(walltime,[_,Delta3]), format('Time to copy stored_value: ~w ms~n',[Delta3]).
863 */
864