1 % (c) 2016-2025 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 :- module(ltsmin,[ltsmin_init/2,ltsmin_init/3,ltsmin_loop/1,
5 ltsmin_teardown/2, start_ltsmin/4,
6 ltsmin_generate_ltlfile/2, generate_bindings/3]).
7
8 :- use_module(library(fastrw),[ fast_buf_read/2]).
9 :- use_module(library(codesio), [write_to_codes/2]).
10 :- use_module(library(lists)).
11
12 :- use_module(extension('ltsmin/ltsmin_c_interface')).
13 :- use_module(extension('ltsmin/msg_interop')).
14
15
16 :- use_module('../../src/module_information.pl').
17
18 :- use_module(extension('plspec/plspec/plspec_core')).
19 :- enable_spec_check(write_matrix/3).
20
21 :- use_module(probsrc(specfile)).
22 :- use_module(probsrc(bmachine)).
23 :- use_module(probsrc(b_read_write_info), [tcltk_read_write_matrix/1]).
24 :- use_module(probsrc(debug), [debug_println/2, debug_mode/1]).
25
26 :- use_module(probsrc(typing_tools)).
27 %:- enable_all_spec_checks.
28
29 :- module_info(group,experimental).
30 :- module_info(description,'This is the interface to C code for LTSmin integration.').
31 :- use_module(probsrc(b_state_model_check), [get_guard/2]).
32 :- use_module(probsrc(bsyntaxtree), [conjunction_to_list/2, same_texpr/2,
33 conjunct_predicates/2, is_truth/1, create_negation/2]).
34 %:- use_module(probsrc(system_call), [system_call_keep_open/7]).
35 :- use_module(library(process), [process_wait/2]).
36
37 :- dynamic label_to_reads/2.
38
39 :- spec_pre(msg_interop:write_matrix/3, [c_pointer,
40 one_of([ltsmin_matrix(ltsmin_state_label, ltsmin_variable), % state label / guard / LTL matrices
41 ltsmin_matrix(ltsmin_transition_group, ltsmin_variable), % read/write matrices
42 ltsmin_matrix(ltsmin_state_label, ltsmin_transition_group), % NES / NDS
43 ltsmin_matrix(ltsmin_transition_group, ltsmin_state_label), % guard info
44 ltsmin_matrix(ltsmin_state_label, ltsmin_state_label), % may be co-enabled
45 ltsmin_matrix(ltsmin_transition_group, ltsmin_transition_group)]), % DNA
46 any]).
47
48 :- defspec(ltsmin_flag, one_of([atom(por),
49 atom(g),
50 atom(nocache),
51 compound(ltl_formula(any))])).
52 :- defspec(ltsmin_backend, one_of([atom(sequential), atom(symbolic)])).
53 :- defspec(atom_bool, one_of([atom(true), atom(false)])).
54 :- defspec(c_pointer, int).
55 :- defspec(ltsmin_matrix(X, Y), [tuple([X, list(Y)])]).
56 :- defspec(b_pred_ish, compound(bpred(any))).
57
58 ltsmin_variable(X) :-
59 varname_list(Variables), member(X, Variables).
60 :- defspec_pred(ltsmin_variable, ltsmin_variable).
61
62 ltsmin_transition_group(X) :-
63 get_transition_groups(TGs), member(X, TGs).
64 :- defspec_pred(ltsmin_transition_group, ltsmin_transition_group).
65
66 ltsmin_state_label(X) :- state_label(X, _, _).
67 :- defspec_pred(ltsmin_state_label, ltsmin_state_label).
68
69 :- spec_pre(ltsmin_loop/1, [c_pointer]).
70 ltsmin_loop(Zocket) :-
71 h_zocket2(Zocket, Ret), % this is a C function in ltsmin_c_interface, which does the call-backs to Prolog
72 Ret == 0, !,
73 ltsmin_loop(Zocket).
74 ltsmin_loop(_).
75
76 interpose([], _, []).
77 interpose([X], _, [X]) :- !.
78 interpose([H|T], I, [H,I|IT]) :-
79 interpose(T, I, IT).
80
81 atom_concat([X], X) :- !.
82 atom_concat([H1, H2|T], R) :-
83 atom_concat(H1, H2, H),
84 atom_concat([H|T], R).
85
86 :- spec_pre(get_ltsmin_invariant/2, [var, atom_bool]).
87 :- spec_invariant(get_ltsmin_invariant/2, ['return value: an atom containing the flags for invariant checking for ltsmin':atom,
88 'input: true or false describing whether POR is disabled or enabled':atom_bool]).
89 get_ltsmin_invariant(LTSminInv, Por) :-
90 (Por == false -> Type = invariant ; Type = por_invariant),
91 findall(InvLabel, state_label(InvLabel, Type, _AST), InvariantNames),
92 (InvariantNames == [] -> Invariant = ['true']; interpose(InvariantNames, ' && ', Invariant)),
93 atom_concat(Invariant, Invariant2),
94 atom_concat(['--invariant=!is_init || (', Invariant2, ')'], LTSminInv).
95
96
97 backend_specific_flags(symbolic, ['--lace-workers=1'], _).
98 backend_specific_flags(sequential, FlagsSeq, MoreFlags) :-
99 (member(nocache, MoreFlags) -> FlagsSeq = [] ; FlagsSeq = ['-c']).
100
101 :- spec_pre(property_flags/4, [atom_bool, atom_bool, atom_bool, var]).
102 :- spec_invariant(property_flags/4, ['no deadlock checking':atom_bool,
103 'no invariant checking':atom_bool,
104 'POR active':atom_bool,
105 'resulting flags':[atomic]]).
106 property_flags(true, true, _, []).
107 property_flags(false, true, _, ['-d']).
108 property_flags(true, false, POR, [InvariantFlags]) :-
109 get_ltsmin_invariant(InvariantFlags, POR).
110
111
112 % TODO: maybe the path should be chosen in another way
113 get_path_to_ltl_file('/tmp/prob-ltsmin-formula.ltl').
114 :- use_module(probsrc(system_call),[get_temporary_filename/2]).
115 %get_path_to_trace_file('/tmp/prob-ltsmin-trace.gcf').
116 get_path_to_fresh_trace_file(File) :- get_temporary_filename('prob-ltsmin-trace.gcf',File).
117 %get_path_to_csv_trace('/tmp/prob-ltsmin-trace.csv').
118 get_path_to_fresh_csv_trace_file(File) :- get_temporary_filename('prob-ltsmin-trace.csv',File).
119 get_path_to_fresh_ltsmin_endpoint(File) :- get_temporary_filename('ltsmin.probz',File).
120
121 :- spec_pre(ltsmin_flag/2, [ltsmin_flag, var]).
122 :- spec_invariant(ltsmin_flag/2, [ltsmin_flag, atom]).
123 ltsmin_flag(por, '--por').
124 ltsmin_flag(g, '-g').
125 ltsmin_flag(ltl_formula(_), Flag) :-
126 get_path_to_ltl_file(Path),
127 atom_concat('--ltl=', Path, Flag).
128
129
130 % MoreFlags can contain: por, nocache, g (for guard splitting), ltl_formula(String)
131 :- spec_pre(get_ltsmin_flags/6, [ltsmin_backend, atom_bool, atom_bool, [ltsmin_flag], atom, var]).
132 :- spec_invariant(get_ltsmin_flags/6, ['symbolic_or_sequential':ltsmin_backend,
133 % at least one of the checks has to be false
134 'no deadlock checking':atom_bool,
135 'no invariant checking':atom_bool,
136 'more flags that should be converted to LTSmin flags':[ltsmin_flag],
137 'trace_file':atom,
138 'resulting flags for LTSmin':[atom]]).
139 get_ltsmin_flags(Backend, NoDead, NoInv, MoreFlags, TraceFile, ResFlags) :-
140 backend_specific_flags(Backend, BackendFlags, MoreFlags),
141 (member(por, MoreFlags) -> POR = true ; POR = false),
142 property_flags(NoDead, NoInv, POR, PropertyFlags),
143 convlist(ltsmin_flag, MoreFlags, EvenMoreFlags),
144 TraceFlags = ['--trace', TraceFile],
145 append([TraceFlags, BackendFlags, PropertyFlags, EvenMoreFlags], ResFlags).
146
147
148
149 :- use_module(probsrc(tools_printing),[print_error/1]).
150 :- use_module(probsrc(preferences), [get_preference/2]).
151 :- use_module(probsrc(error_manager), [add_warning/3, add_internal_error/2]).
152 :- use_module(probsrc(pathes),[runtime_application_path/1]).
153 :- use_module(probsrc(pathes_lib), [lib_component_full_path/4]).
154
155 :- use_module(library(file_systems),[file_exists/1,delete_file/1]).
156 del_temp_file(F) :- (file_exists(F) -> delete_file(F) ; true).
157
158 get_tool_name(symbolic, 'prob2lts-sym').
159 get_tool_name(sequential, 'prob2lts-seq').
160 get_tool_name(printtrace, 'ltsmin-printtrace').
161
162 :- spec_pre(start_ltsmin/4, [ltsmin_backend, tuple([atom_bool, atom_bool]), [ltsmin_flag],var]).
163 :- spec_invariant(start_ltsmin/4, ['which backend should be used (symbolic or sequential)':ltsmin_backend,
164 'tuple of length 2: no deadlock checking, no invariant checking':tuple([atom_bool, atom_bool]),
165 'more ltsmin flags':[ltsmin_flag],
166 'resulting of LTSmin model-checking':any ]).
167 start_ltsmin(Backend, [NoDead, NoInv], MoreFlags,Result) :-
168 (NoDead = false, NoInv = false ->
169 add_warning(start_ltsmin,'Turning off deadlock checking for LTSmin command: ',Backend),
170 NoDead2 = true
171 ; NoDead2 = NoDead),
172 get_path_to_fresh_ltsmin_endpoint(Endpoint),
173 ltsmin:ltsmin_init(Endpoint, Zocket),
174 get_path_to_fresh_trace_file(TraceFile),
175 get_ltsmin_flags(Backend, NoDead2, NoInv, MoreFlags, TraceFile, Flags),
176 (member(ltl_formula(Formula), MoreFlags)
177 -> get_path_to_ltl_file(LTLFile),
178 ltsmin_generate_ltlfile(Formula, LTLFile)
179 ; true),
180 preferences:get_preference(path_to_ltsmin, Path),
181 runtime_application_path(PROBPATH),
182 get_tool_name(Backend, Tool),
183 lib_component_full_path(executable, Tool, ltsmin_extension, LTSminCmd),
184 catch(
185 process:process_create(LTSminCmd, [Endpoint, '--stats', '--when'|Flags], [process(PID)]),
186 % we could add stdout(pipe(STDOut)) option to process_create and try and parse output
187 error(existence_error(_,_),E),
188 (format(user_error,'process_create exception: ~w~n',[E]),
189 format('LTSMIN preference is set to: "~w"~n',[Path]),
190 format(' relative to ProB path: "~w"~n',[PROBPATH]),
191 format(' using tool: "~w"~n',[Tool]),
192 add_error_fail(ltsmin,'Path to LTSmin incorrect (LTSMIN preference) or LTSmin command not installed (copy prob2lts-sym, prob2lts-seq, ltsmin-printtrace from https://github.com/utwente-fmt/ltsmin/releases to ProB\'s lib folder), cannot execute: ',LTSminCmd))),
193 ltsmin_loop(Zocket),
194 process_wait(PID, ExitStatus),
195 (ExitStatus == exit(0)
196 -> Result = ltsmin_model_checking_ok
197 ; ExitStatus = killed(_)
198 -> Result = ltsmin_model_checking_aborted
199 ; ExitStatus == exit(1)
200 -> get_tool_name(printtrace, PrintTrace),
201 lib_component_full_path(executable, PrintTrace, ltsmin_extension, PrintTraceCmd),
202 get_path_to_fresh_csv_trace_file(CsvFile),
203 format('writing trace to ~w~n', CsvFile),
204 catch(
205 process:process_create(PrintTraceCmd, [TraceFile, CsvFile, '-s,,'], [process(PID2)]),
206 error(existence_error(_,_),E),
207 (format(user_error,'process_create exception: ~w~n',[E]),
208 add_error_fail(ltsmin,'Path to LTSmin incorrect or ltsmin-printtrace command not installed (copy command from https://github.com/utwente-fmt/ltsmin/releases to ProB\'s lib folder), cannot execute: ',PrintTraceCmd))),
209 process_wait(PID2, _ExitStatus2),
210 Result = ltsmin_counter_example_found(CsvFile)
211 ; add_internal_error('Unexpected LTSmin exit status:',ExitStatus),
212 Result = ltsmin_model_checking_aborted
213 ),
214 del_temp_file(TraceFile),
215 del_temp_file(Endpoint),
216 format('~w with ~w exited with status: ~w~n',[Tool, Flags, ExitStatus]),
217 ltsmin:ltsmin_teardown(Zocket, Endpoint).
218
219
220 :- use_module(probsrc(state_space),[get_operation_name_coverage_infos/4]).
221 :- public print_ltsmin_stats/0.
222 % can be called from ltsmin.c
223 print_ltsmin_stats :-
224 get_operation_name_coverage_infos(PossibleNr,_FeasibleNr,UncovNr,UncoveredList),
225 Cov is PossibleNr - UncovNr,
226 format('~w/~w operations covered, uncovered = ~w~n~n',[Cov,PossibleNr,UncoveredList]).
227
228 :- dynamic varname_list/1.
229
230 :- use_module(probltlsrc(ltl_tools), [temporal_parser/3]).
231
232 :- dynamic(inv_state_label_matrix/1).
233 :- dynamic(inv_state_label_matrix_for_por/1).
234
235 write_to_ltl_file(Formula, Path) :-
236 open(Path, write, Stream),
237 tell(Stream),
238 print(Formula),
239 told.
240
241 ltsmin_generate_ltlfile(Formula, OutputFile) :-
242 %% firstly, wrap the Formula into X(Formula)
243 %% this avoids that a property is already considered true
244 %% because it is in the artificial initial state
245 atom_concat('X(', Formula, XFormula),
246 atom_concat(XFormula, ')', FinalFormula),
247 format('parsing LTL formula ~a~N', [FinalFormula]),
248 setup_ltl_formula(FinalFormula, ResAtom),
249 format('done. writing to: ~a~N', [OutputFile]),
250 write_to_ltl_file(ResAtom, OutputFile).
251
252
253 setup_ltl_formula(Formula, ResAtom) :-
254 temporal_parser(Formula, ltl, ParsedFormula),
255 ltl_safety:pp_formula_to_ltl2ba_syntax(ParsedFormula, ResAtom).
256
257
258 :- spec_pre(ltsmin_init/3, [atom, var, atom]).
259 :- spec_pre(ltsmin_init/3, [atom, var, var]).
260 :- spec_invariant(ltsmin_init/3, ['the path to the ZMQ endpoint':atom,
261 'return value: a C pointer to a ZMQ zocket':c_pointer,
262 'an LTL formula':atom]).
263 ltsmin_init(Endpoint,Zocket, LtlFormula) :-
264 (var(LtlFormula) -> true ; setup_ltl_formula(LtlFormula, _)),
265 ltsmin_init(Endpoint, Zocket).
266
267 :- spec_pre(ltsmin_init/2, [atom, var]).
268 :- spec_pre(ltsmin_init/2, [atom, var]).
269 :- spec_invariant(ltsmin_init/2, ['the path to the ZMQ endpoint':atom,
270 'return value: a C pointer to a ZMQ zocket':c_pointer]).
271 ltsmin_init(Endpoint,Zocket) :-
272 loadfr,
273 reset_ltsmin_extension,
274 split_invariant(ListOfInvariantConjuncts, InvNames, ListOfInvariantConjunctsForPOR, InvNamesForPOR),
275 get_state_labels(ListOfInvariantConjuncts, InvNames, invariant, InvStateLabelMatrix),
276 get_state_labels(ListOfInvariantConjunctsForPOR, InvNamesForPOR, por_invariant, InvStateLabelMatrixForPOR),
277 assertz(inv_state_label_matrix(InvStateLabelMatrix)),
278 assertz(inv_state_label_matrix_for_por(InvStateLabelMatrixForPOR)),
279 ltsmin_initialize(Endpoint, Zocket).
280
281 %assert_dir :- (user:library_directory('.') -> true ;
282 %assertz(user:library_directory('.'))).
283
284
285 :- use_module(probsrc(runtime_profiler),[profile_single_call/2]).
286 :- use_module(probsrc(state_space),[mark_operation_as_covered/1]).
287 :- public get_next_states/4. % not sure how this is called
288 get_next_states(LongOrPattern, Msg, State,Op) :- % State is a list of bindings
289 % TO DO: set_context_state(SomeID) and look if state_error(SomeID,Id,Error) were added
290 profile_single_call(Op,ltsmin:compute_successors(Op, State, Succs)),
291 length(Succs,NrStates),
292 % format(" ~w successor states for ~w~n ~w~n",[NrStates,Op,Succs]),nl,
293 (NrStates>0 -> mark_operation_as_covered(Op) ; true), % TO DO: we should probably use another Prolog fact for this; to avoid confusion which operations are covered in ProB's internal statespace
294 put_number(Msg, NrStates),
295 put_successors(LongOrPattern, Succs, Msg).
296
297
298 read_state([],[]).
299 read_state([Addr|T], [Arg|R]) :- read_state(T,R), fast_buf_read(Arg,Addr).
300
301 :- meta_predicate mnf1(0).
302 mnf1(Call) :- %format('Calling ~w~n',[Call]), flush_output,
303 if( catch(Call, E,
304 (add_internal_error('LTSMin Extension Exception: ',[E]),flush_output,fail)
305 ),
306 true,
307 (add_internal_error('LTSMin Extension Call Failed: ',[Call]),flush_output,fail)
308 ).
309
310 :- use_module(probsrc(specfile),[csp_with_bz_mode/0]).
311 % convert a list of values into a ProB environment [bind(x,Vx),...]
312 generate_bound_long_state([CSP|BState],Res) :- csp_with_bz_mode,!,
313 Res=csp_and_b(CSP,BoundState),
314 varname_list(L),
315 %print(gen_bind(BState,L)),nl,
316 generate_bindings(BState, L, BoundState).
317 /* this needs to be fixed for short states ;
318 * it does not get the entire varname list */
319 generate_bound_long_state(BState,BoundState) :-
320 varname_list(L),
321 generate_bindings(BState, L, BoundState).
322
323 generate_bound_state_short_label(BState,Label,BoundState) :-
324 label_to_reads(Label, L),
325 generate_bindings(BState, L, BoundState).
326
327 :- spec_pre(generate_bindings/3, [list(any), list(atom), list(compound(bind(atom, any)))]).
328 :- spec_pre(generate_bindings/3, [list(any), list(atom), var]).
329 :- spec_pre(generate_bindings/3, [var, var, list(compound(bind(atom, any)))]).
330 :- spec_invariant(generate_bindings/3, ['a list of values':list(any),
331 'a list of identifiers':list(atom),
332 'list of identifiers and values, wrapped in functor bind/2':list(compound(bind(atom, any)))]).
333
334 :- load_files(library(system), [when(compile_time), imports([environ/2])]).
335 :- if(environ(prob_safe_mode,true)).
336 :- use_module(probsrc(bmachine),[b_is_variable/2]).
337 :- use_module(probsrc(btypechecker), [unify_types_strict/2]).
338 :- use_module(probsrc(error_manager),[add_internal_error/2]).
339 :- debug_println(19,'TYPE CHECKING LTSMIN BINDINGS:').
340 generate_bindings([], [], []) :- !.
341 generate_bindings([H|T], [Name|TN], [bind(Name, H)|R]) :-
342 (get_type(Name,Type)
343 -> kernel_objects:infer_value_type(H,HType),
344 (unify_types_strict(HType,Type) -> true
345 ; add_internal_error('Unexpected type for:',id(Name,HType,Type,H))
346 )
347 ; add_internal_error('Unknown identifier:',Name)
348 ),
349 generate_bindings(T, TN, R).
350
351 get_type(Name,Type) :- b_is_variable(Name,Type).
352 get_type(Name,Type) :- b_get_machine_constants(Csts),
353 member(b(identifier(Name),Type,_),Csts).
354 :- else.
355 generate_bindings([], [], []) :- !.
356 generate_bindings([H|T], [Name|TN], [bind(Name, H)|R]) :-
357 generate_bindings(T, TN, R).
358 :- endif.
359
360 :- public get_successors_short/3. % called from C code
361 % implements R2W
362 get_successors_short(Msg, OperationA, StateA) :- %print(prolog_get_succs),nl,flush_output,
363 mnf1(get_successors_aux(short, Msg, OperationA, StateA)).
364
365 :- public get_successors/3. % called from C code
366 get_successors(Msg, OperationA, StateA) :- %print(prolog_get_succs),nl,flush_output,
367 mnf1(get_successors_aux(long, Msg, OperationA, StateA)).
368
369 get_successors_aux(Type, Msg, OperationA, StateA) :-
370 read_state(StateA,State),
371 fast_buf_read(Operation,OperationA),
372 %print(operation(Operation)),nl,
373 % print(successors(Operation, State)),nl,
374 (Type == short -> black_magic_short_transformation(Operation, BoundState, State, LongPattern, ShortSucc, Defaults, _, _, _, _, _), ShortOrLong = LongPattern-ShortSucc-Defaults
375 ; generate_bound_long_state(State, BoundState), ShortOrLong = long),
376 %print(generate(BoundState)),nl,
377 (debug:debug_mode(on)
378 -> format('~n ** get_successors for ~w (~w)~n',[Operation,ShortOrLong]),
379 translate:print_state(BoundState),nl
380 ; true),
381 get_next_states(ShortOrLong, Msg, BoundState,Operation).
382
383 :- public get_state_label/3. % called by C
384 get_state_label(Msg, LabelA, StateA) :- mnf1(get_state_label_aux(long, Msg, LabelA, StateA)).
385
386 :- public get_state_label_short/3. % called by C
387 get_state_label_short(Msg, LabelA, StateA) :- mnf1(get_state_label_aux(short, Msg, LabelA, StateA)).
388
389 get_state_label_aux(Type, Msg, LabelA, StateA) :-
390 read_state(StateA, State),
391 fast_buf_read(Label,LabelA), %print(get_state_label(Label)),nl,
392 (Type == long -> generate_bound_long_state(State,BoundState)
393 ; generate_bound_state_short_label(State, Label, BoundState)),
394 %print(get_state_label_state(Label, BoundState)),nl,
395 eval_label(Msg, BoundState,Label).
396
397 % NOTE: LTSMin assumes the first transition group is the initialisation action, it is called only once
398 % all other events are not called in the initial state
399 get_transition_groups(['$init_state'|L]) :-
400 (specfile:b_or_z_mode
401 -> findall(OperationName, bmachine:b_top_level_feasible_operation(OperationName), BL),
402 (csp_with_bz_mode -> L = ['$CSP'|BL] ; L=BL)
403 ; L=[]). % FIXME: provide list for CSP-M
404
405 extract_name_and_type([],[], []).
406 extract_name_and_type([b(identifier(HT),TypeTerm,_)|T], [HT|RT], [TypeTerm|RType]) :-
407 extract_name_and_type(T,RT,RType).
408
409 /* is that already defined somewhere? */
410 get_ltsmin_variable_names(Names, Types) :-
411 bmachine:b_get_machine_variables(V),
412 % bmachine:b_get_machine_all_constants(C), % this includes constants which have been moved to deferred sets; causes issue with hanoi model
413 bmachine:b_get_machine_constants(C), % this does not include global set constants
414 append(V,C,CV), % compatible with b_initialise_machine2 and expand_const_and_vars order
415 extract_name_and_type(CV,Names,Types).
416 % b_initialise_machine2 puts constants at end to make sharing of complete tail-list easier for successor states
417
418
419
420 extract_tg_and_nth_entry([], _, R, R).
421 extract_tg_and_nth_entry([list(H)|T], Arg, Acc, R) :-
422 nth1(1, H, TransitionGroup),
423 nth1(Arg, H, X),
424 extract_tg_and_nth_entry(T, Arg, [[TransitionGroup, X]|Acc], R).
425
426 % patch matrix for CSP||B
427 put_patched_matrix(Msg, Type,M) :-
428 patch_matrix(M,PM),
429 (debug:debug_mode(on) -> print(put_matrix(Type,PM)),nl ; true),
430 write_matrix(Msg, PM, Type).
431
432 % add $CSP as read/write dependency to matrixes
433 % [ [Odd,[]], [Even,[]], [Inc,[count]], [Reset,[count]]]
434 patch_matrix(M,Res) :- csp_with_bz_mode, !,
435 Res = [ ['$CSP',['$CSP_CHUNK']] | PM], % add virtual CSP transition for CSP only events; TO DO: also allow/treat other pure CSP events; the CSP transition can only modify the $CSP chunk
436 maplist(patch_row,M,PM).
437 patch_matrix(M,M).
438
439 patch_row([Op,Vars], [Op,['$CSP_CHUNK'|Vars]]).
440
441
442 build_initial_state(_, VType, VVal) :-
443 any_value_for_type(VType, VVal).
444 % do not use term undefined in order to avoid guard errors in the initial state
445 %VVal = term(undefined).
446
447
448 term_to_atom(T, A) :-
449 write_to_codes(T, Codes),
450 atom_codes(A, Codes).
451
452
453 :- use_module(probsrc(b_operation_guards), [get_operation_propositional_guards/3]).
454
455
456 guard_seen(G, [H|_]) :-
457 same_texpr(G, H), !.
458 guard_seen(G, [_|LofG]) :-
459 guard_seen(G, LofG).
460
461
462 setify_guards(L, R) :-
463 setify_guards(L, [], R).
464 setify_guards([], Seen, Seen).
465 setify_guards([G|RGuards], Seen, Res) :-
466 (guard_seen(G, Seen)
467 -> setify_guards(RGuards, Seen, Res)
468 ; setify_guards(RGuards, [G|Seen], Res)).
469
470
471 guard_vector(ListOfListOfGuards, Res) :-
472 append(ListOfListOfGuards, ListOfGuards),
473 setify_guards(ListOfGuards, Res).
474
475
476 get_name([H|_], [Name|_], G, Name) :-
477 same_texpr(G, H), !.
478 get_name([_|GV], [_|Names], G, R) :-
479 get_name(GV, Names, G, R).
480
481
482 guards_to_indices(GuardVector, Names, ListOfGuards, ListOfIndices) :-
483 maplist(get_name(GuardVector, Names), ListOfGuards, ListOfIndices).
484
485 split_guards(Por, ['$init_state'|TransitionGroups], GuardVector, Names, [[]|IndexVectors]) :-
486 % if we use POR, the transition group has to be enabled iff all its guards are true;
487 % thus, for POR we need to use get_guard that also provides
488 % existential quantifications for parameters as additional guards
489 (Por == false
490 -> maplist(get_ltsmin_guards, TransitionGroups, RGuards, _Bodies)
491 ; maplist(get_guard, TransitionGroups, Guards),
492 maplist(conjunction_to_list, Guards, RGuards)),
493 %% TODO: normalise guards
494 guard_vector(RGuards, GuardVector),
495 generate_names(GuardVector, 'guard_', Names), % get list [guardN, ..., guard1]
496 maplist(guards_to_indices(GuardVector, Names), RGuards, IndexVectors).
497
498 :- use_module(probsrc(bsyntaxtree),[always_well_defined/1]).
499 get_ltsmin_guards(OpName,Guards,RestBody) :-
500 get_preference(ltsmin_guard_splitting,true),!,
501 get_operation_propositional_guards(OpName,Guards,RestBody).
502 get_ltsmin_guards(OpName,ResGuards,RestBody) :-
503 get_operation_propositional_guards(OpName,Guards,RestBody),
504 conjunct_predicates(Guards,BigGuard),
505 (always_well_defined(BigGuard)
506 -> ResGuards = Guards
507 ; ResGuards = [BigGuard] % avoid WD errors when splitting guard
508 ).
509
510 create_matrix([], [], Res, Res) :- !.
511 create_matrix([H1|T1], [H2|T2], Acc, Res) :-
512 create_matrix(T1, T2, [[H1,H2]|Acc], Res).
513 create_matrix(Headings, Entries, Res) :-
514 create_matrix(Headings, Entries, [], Res).
515
516
517 generate_names(Terms, PrefixAtom, Names) :-
518 atom_codes(PrefixAtom, PrefixChars),
519 generate_names_aux(Terms, PrefixChars, 1, [], Names).
520 generate_names_aux([], _, _, Acc, Acc) :- !.
521 generate_names_aux([_|T], Prefix, N, Acc, Res) :-
522 number_codes(N, NC),
523 append(Prefix, NC, NamedC),
524 atom_codes(Named, NamedC),
525 N1 is N+1,
526 generate_names_aux(T, Prefix, N1, [Named|Acc], Res).
527
528 wrap_named_inv(Type, Name, Invariant, state_label(Name, Type, Invariant)).
529 :- dynamic state_label/3.
530
531
532 assert_all([]).
533 assert_all([H|T]) :-
534 assertz(H), assert_all(T).
535
536 /* need to extract this from the atomic props of the formula,
537 we can also use the prolog term of a formula as its state label */
538 get_state_labels(ListOfConjuncts, Names, Type, StateLabelMatrix) :-
539 %% format of StateLabelMatrix is
540 % [[label1, [id1, id2, ...]]
541 % [label2, [id2, id4, ...]]
542 % ...]
543 maplist(wrap_named_inv(Type), Names, ListOfConjuncts, Facts),
544 assert_all(Facts),
545 maplist(bsyntaxtree:predicate_identifiers, ListOfConjuncts, SLSVars),
546 create_matrix(Names, SLSVars, StateLabelMatrix).
547
548
549 :- use_module(probsrc(bmachine),[get_unproven_invariants/1]).
550 split_invariant(ListOfInvariantConjuncts, InvNames, ListOfInvariantConjunctsForPOR, InvNamesForPOR) :-
551 get_unproven_invariants(ListOfInvariantConjuncts),!, % warning: does not check for INITIALISATION
552 shorten_inv_list(ListOfInvariantConjuncts,ListOfInvariantConjunctsForPOR),
553 generate_names(ListOfInvariantConjunctsForPOR, 'porinv', InvNamesForPOR),
554 generate_names(ListOfInvariantConjuncts, 'inv', InvNames),
555 (debug_mode(on) -> maplist(portray_invariant,InvNames,ListOfInvariantConjuncts) ; true).
556
557 % shorten to maximal 8 invariants, as otherwise ltsmin segfaults
558 shorten_inv_list([I1,I2,I3,I4,I5,I6,I7,I8|T],[I1,I2,I3,I4,I5,I6,I7,I8New]) :- T \= [], !,
559 length([I8|T],Len), NrInvs is Len+7,
560 format('Merging last ~w of ~w invariants (LTSMin POR supports max 8 invariants)~n',[Len,NrInvs]),
561 conjunct_predicates([I8|T],I8New).
562 shorten_inv_list([],R) :- !, format('No invariants to check~n',[]), R=[].
563 shorten_inv_list(I,I).
564
565 :- use_module(probsrc(translate),[translate_bexpression_with_limit/3]).
566 portray_invariant(Label,Inv) :-
567 translate_bexpression_with_limit(Inv,100,IS),
568 format(' ~w : ~w~n',[Label,IS]).
569
570
571 :- use_module(probporsrc(static_analysis), [test_path/6, dependent_actions/5]).
572
573 does_not_accord_with_aux([], _, []).
574 does_not_accord_with_aux([H|T], TransitionGroup, [H|R]) :-
575 debug_println(9,check_dependency(H,TransitionGroup)),
576 get_preference(timeout_cbc_analysis,TO),
577 dependent_actions(H, TransitionGroup, 0, TO, _), !, % TODO: handle interrupts?
578 does_not_accord_with_aux(T, TransitionGroup, R).
579 does_not_accord_with_aux([_|T], TransitionGroup, R) :-
580 does_not_accord_with_aux(T, TransitionGroup, R).
581
582 do_not_accord(['$init_state'|TransitionGroups], [[]|DoNotAccordMatrix]) :-
583 maplist(does_not_accord_with_aux(TransitionGroups), TransitionGroups, DoNotAccordMatrix).
584
585 can_enable(TransitionGroup, Guard, NGuard) :-
586 ltsmin_test_path_possible(NGuard, [TransitionGroup], Guard).
587
588 ltsmin_test_path_possible(Conjunction,Path,Guard) :-
589 get_preference(use_cbc_analysis,true),!,
590 get_preference(timeout_cbc_analysis,TO),
591 temporary_set_preference(use_chr_solver,true,CHNG1),
592 test_path(Conjunction, Path, Guard, 0, TO, Result),
593 reset_temporary_preference(use_chr_solver,CHNG1),
594 (Result = ok ; Result == timeout ; Result == unknown).
595 ltsmin_test_path_possible(_,_,_). % assume possible
596
597
598 calculate_enabling_set_aux([], _Guard, _NGuard, []) :- !.
599 calculate_enabling_set_aux([Transition|Rest], Guard, NGuard, [Transition|ResList]) :-
600 can_enable(Transition, Guard, NGuard), !,
601 calculate_enabling_set_aux(Rest, Guard, NGuard, ResList).
602 calculate_enabling_set_aux([_|Rest], Guard, NGuard, ResList) :-
603 calculate_enabling_set_aux(Rest, Guard, NGuard, ResList).
604
605 calculate_enabling_set(TransitionGroupList, Guard, Res) :-
606 create_negation(Guard, NGuard),
607 calculate_enabling_set_aux(TransitionGroupList, Guard, NGuard, Res).
608
609 necessary_enabling_set(GuardVector, ['$init_state'|TransitionGroups], R) :-
610 maplist(calculate_enabling_set(TransitionGroups), GuardVector, R).
611
612 necessary_disabling_set(GuardVector, TransitionGroups, R) :-
613 maplist(create_negation, GuardVector, NGuardVector),
614 necessary_enabling_set(NGuardVector, TransitionGroups, R).
615
616
617 may_be_coenabled_guards(G1, _G2Name, G2) :-
618 conjunct_predicates([G1, G2], Conjunction),
619 is_truth(Truth),
620 ltsmin_test_path_possible(Conjunction, [], Truth).
621 % fail on interrupt
622
623 may_be_coenabled2([], [], []).
624 may_be_coenabled2([_], [_], []).
625 may_be_coenabled2([H|T], [Name|NameRest], [[Name,R]|RT]) :-
626 include(may_be_coenabled_guards(H), NameRest, T, R),
627 may_be_coenabled2(T, NameRest, RT).
628 may_be_coenabled(GuardVector, Names, MayBeCoEnabledMatrix) :-
629 % calculates an upper triangle matrix without the elements on the diagonal
630 may_be_coenabled2(GuardVector, Names, MayBeCoEnabledMatrix).
631
632 :- dynamic(guard_labels/1).
633
634 get_label_group_guards(Msg, State) :-
635 guard_labels(Labels),
636 maplist(eval_label(Msg, State), Labels).
637
638 :- public get_state_label_group/3. % this is called from ltmsin.c
639 get_state_label_group(Msg, OperationA, StateA) :- %print(prolog_get_succs),nl,flush_output,
640 mnf1(get_state_label_group_aux(Msg, OperationA, StateA)).
641 get_state_label_group_aux(Msg, TypeA, StateA) :-
642 read_state(StateA,State),
643 %print(generate(State)),nl,
644 generate_bound_long_state(State,BoundState),
645 fast_buf_read(Type,TypeA),
646 (debug:debug_mode(on)
647 -> format('~n ** get_state_label_group for ~w~n',[Type]),
648 translate:print_state(BoundState),nl
649 ; true),
650 (Type == '1' -> get_label_group_guards(Msg, BoundState)
651 ; print(unhandled_group_type(Type)),nl,fail).
652
653 get_ltl_labels(ASTs, Names) :-
654 findall(ap_representation(Hash, AST), ltl_safety:ap_representation(Hash, AST), APReprs),
655 maplist(arg(2), APReprs, ASTs),
656 maplist(arg(1), APReprs, Names).
657
658
659 :- use_module(probsrc(error_manager), [add_error_fail/3]).
660
661 op_to_reads_guard(Op, ReadSet) :-
662 get_opname(Op, OpName),
663 op_guards(OpName, GuardList),
664 maplist(guard_reads, GuardList, ReadSets),
665 append(ReadSets, ReadSet).
666
667 oplist_to_reads_guards(OpList, ReadSet) :-
668 maplist(op_to_reads_guard, OpList, ReadSets),
669 append(ReadSets, ReadSet).
670 % identifiers might be contained multiple times due to append
671 get_ltl_readsets(bpred(X), ReadSet) :-
672 bsyntaxtree:predicate_identifiers(X, ReadSet).
673 get_ltl_readsets(enabled(Op), ReadSet) :-
674 op_to_reads_guard(Op, ReadSet). % get readset of guards of op
675 get_ltl_readsets(det(OpList), ReadSet) :-
676 oplist_to_reads_guards(OpList, ReadSet).
677 get_ltl_readsets(dlk(OpList), ReadSet) :-
678 oplist_to_reads_guards(OpList, ReadSet).
679 get_ltl_readsets(ctrl(OpList), ReadSet) :- !,
680 oplist_to_reads_guards(OpList, ReadSet).
681 get_ltl_readsets(deadlock, ReadSet) :-
682 get_transition_groups(OpList),
683 oplist_to_reads_guards(OpList, ReadSet).
684 get_ltl_readsets(AP,_) :- !,
685 add_error_fail(ltsmin, 'operation is not supported for LTSmin', AP).
686
687 :- dynamic(guard_reads/2).
688
689 assert_guard_readsets([]).
690 assert_guard_readsets([[GuardName, ReadSet]|R]) :-
691 assertz(guard_reads(GuardName, ReadSet)),
692 assert_guard_readsets(R).
693
694 :- dynamic(op_guards/2).
695
696 assert_op_guards([], []).
697 assert_op_guards([Op|T], [GuardList|T2]) :-
698 assertz(op_guards(Op, GuardList)),
699 assert_op_guards(T, T2).
700
701 :- spec_pre(ltl_to_state_label_matrix/3, [[b_pred_ish], [atom], var]).
702 :- spec_invariant(ltl_to_state_label_matrix/3, [[b_pred_ish], [atom], ltsmin_matrix(ltsmin_state_label, ltsmin_variable)]).
703 :- spec_post(ltl_to_state_label_matrix/3, [[b_pred_ish], [atom], var], [[b_pred_ish], [atom], ltsmin_matrix(ltsmin_state_label, ltsmin_variable)]).
704 ltl_to_state_label_matrix(ListOfAPs, LtlApNames, LtlStateLabelMatrix) :-
705 %% format of StateLabelMatrix is
706 % [[label1, [id1, id2, ...]]
707 % [label2, [id2, id4, ...]]
708 % ...]
709 maplist(wrap_named_inv(ltl_ap), LtlApNames, ListOfAPs, Facts),
710 assert_all(Facts),
711 maplist(get_ltl_readsets, ListOfAPs, SLSVars),
712 create_matrix(LtlApNames, SLSVars, LtlStateLabelMatrix).
713
714
715 calc_and_append_por_info(false, _Msg, _, _, _) :- !.
716 calc_and_append_por_info(_Por, Msg, GuardVector, GuardNames, TransitionGroups) :-
717 may_be_coenabled(GuardVector, GuardNames, MayBeCoEnabledMatrix),
718 write_matrix(Msg, MayBeCoEnabledMatrix, may_be_coenabled_matrix),
719
720 necessary_enabling_set(GuardVector, TransitionGroups, NES),
721 create_matrix(GuardNames, NES, NesMatrix),
722 write_matrix(Msg, NesMatrix, necessary_enabling_set),
723
724 necessary_disabling_set(GuardVector, TransitionGroups, NDS),
725 create_matrix(GuardNames, NDS, NdsMatrix),
726 write_matrix(Msg, NdsMatrix, necessary_disabling_set),
727
728 do_not_accord(TransitionGroups, DNA),
729 create_matrix(TransitionGroups, DNA, DnaMatrix),
730 write_matrix(Msg, DnaMatrix, do_not_accord_matrix).
731
732
733 sort_by_and_distinct(SortByList, ToBeSortedMayContainDups, SortedUniqs) :-
734 findall(X, (member(X, SortByList), member(X, ToBeSortedMayContainDups)), SortedWithDups),
735 distinct(SortedWithDups, SortedUniqs).
736
737
738
739 :- dynamic black_magic_short_transformation/11.
740 % arg 1: Name of Operation
741 % arg 2: a pattern in order to create a bound state
742 % arg 3: a list of variables that unify the second argument to a bound state
743 % arg 4: a long state pattern that can be unified in order to get a short state in arg 5
744 % arg 5: the corresponding short state
745 % arg 6: default values in case variables are not written
746 % arg 7-11: see arg 2-6, but for next_action
747
748
749 % the last argument should be unified with the LTSmin short state values;
750 % this way, we acquire a bound state in the second to last argument.
751 prepare_binding([], [], [], []).
752 prepare_binding([H|AccessedTail], [H|ReadTail], [bind(H, V)|BindingTail], [V|ValTail]) :- !,
753 prepare_binding(AccessedTail, ReadTail, BindingTail, ValTail).
754 prepare_binding([H|AccessedTail], Reads, [bind(H, term(undefined))|BindingTail], Vals) :-
755 prepare_binding(AccessedTail, Reads, BindingTail, Vals).
756
757 % TODO: maybe refactor this (pk, 07.03.2018)
758 back_to_short([], [], [], [], [], _InitialState).
759 back_to_short([bind(H, _)|T], [H|WriteTail], [bind(H, V)|LongStateTail], [bind(H, V)|ShortTail], [bind(H, Default)|DefaultBindingsTail], InitialState) :- !,
760 % variable is written to
761 memberchk(bind(H, Default), InitialState),
762 back_to_short(T, WriteTail, LongStateTail, ShortTail, DefaultBindingsTail, InitialState).
763 back_to_short([bind(H, _)|T], Writes, [bind(H, _)|LongStateTail], Short, DefaultBindings, InitialState) :-
764 back_to_short(T, Writes, LongStateTail, Short, DefaultBindings, InitialState).
765
766 assert_short_state_transformations([], InitialState) :-
767 varname_list(L),
768 prepare_binding(L, [], PreparedBinding, PreparedVarListForUnification),
769 back_to_short(PreparedBinding, L, LongPattern, PreparedShort, InitialState, InitialState),
770 assertz(black_magic_short_transformation('$init_state', PreparedBinding, PreparedVarListForUnification, LongPattern, PreparedShort, InitialState,
771 PreparedBinding, PreparedVarListForUnification, LongPattern, PreparedShort, InitialState)).
772 assert_short_state_transformations([list([Op, ReadsGuard, ReadsAction, MustWrite, MayWrite|_])|T], InitialState) :-
773 varname_list(L),
774 sort_by_and_distinct(L, ReadsAction, SortedReadsAction),
775 append([ReadsGuard, ReadsAction, MustWrite, MayWrite], AllAccessedVarsWithDups),
776 append([ReadsAction, MustWrite, MayWrite], AllAccessedVarsForActionWithDups),
777 append([ReadsGuard, ReadsAction], AllReadsWithDups),
778 append([MustWrite, MayWrite], AllWritesWithDups),
779 sort_by_and_distinct(L, AllAccessedVarsWithDups, AllAccessedVars),
780 sort_by_and_distinct(L, AllAccessedVarsForActionWithDups, AllAccessedVarsForAction),
781 sort_by_and_distinct(L, AllReadsWithDups, AllReads),
782 sort_by_and_distinct(L, AllWritesWithDups, AllWrites),
783 prepare_binding(AllAccessedVars, AllReads, PreparedBinding, PreparedVarListForUnification),
784 prepare_binding(AllAccessedVarsForAction, SortedReadsAction, PreparedBindingForNextAction, PreparedVarListForUnificationForNextActionFactoryProviderSingleton),
785 back_to_short(PreparedBinding, AllWrites, LongPattern, PreparedShort, Defaults, InitialState),
786 back_to_short(PreparedBindingForNextAction, AllWrites, LongPatternForAction, PreparedShortForNextAction, DefaultsForAction, InitialState),
787 assertz(black_magic_short_transformation(Op, PreparedBinding, PreparedVarListForUnification, LongPattern, PreparedShort, Defaults,
788 PreparedBindingForNextAction, PreparedVarListForUnificationForNextActionFactoryProviderSingleton, LongPatternForAction, PreparedShortForNextAction, DefaultsForAction)),
789 assert_short_state_transformations(T, InitialState).
790
791
792 assert_label_to_accessed_vars([]).
793 assert_label_to_accessed_vars([[Label,ListOfVars]|T]) :-
794 varname_list(L),
795 findall(AccessedOrderedVars, (member(AccessedOrderedVars, L),
796 member(AccessedOrderedVars, ListOfVars)),
797 AccessedVarSet), % poor man's sort-by
798 assertz(label_to_reads(Label, AccessedVarSet)),
799 assert_label_to_accessed_vars(T).
800
801
802
803
804
805
806 distinct(X, Y) :-
807 distinct(X, [], Y).
808 distinct([], _, []).
809 distinct([H|T], Seen, Res) :-
810 member(H, Seen), !,
811 distinct(T, Seen, Res).
812 distinct([H|T], Seen, [H|Res]) :-
813 distinct(T, [H|Seen], Res).
814
815
816
817 put_read_write_matrices(Msg, MR) :-
818 %% read write matrices
819 tcltk_read_write_matrix(M),
820 (debug:debug_mode(on) -> print(M),nl,flush_output ; true),
821 M = list([_Headings|MR]),
822 (debug:debug_mode(on) -> print(MR),nl ; true),
823 extract_tg_and_nth_entry(MR, 5, [], MayWriteMatrix),
824 extract_tg_and_nth_entry(MR, 4, [], MustWriteMatrix),
825 extract_tg_and_nth_entry(MR, 3, [], ReadsActionMatrix),
826 extract_tg_and_nth_entry(MR, 2, [], ReadsGuardMatrix),
827 put_may_write_matrix(Msg, MayWriteMatrix),
828 put_patched_matrix(Msg, must_write_matrix,MustWriteMatrix),
829 put_patched_matrix(Msg, reads_action_matrix,ReadsActionMatrix),
830 put_patched_matrix(Msg, reads_guard_matrix,ReadsGuardMatrix).
831
832 assert_short_state_info(MR, InitialState) :-
833 varname_list(L),
834 generate_bindings(InitialState, L, BoundInitialState),
835 assert_short_state_transformations(MR, BoundInitialState).
836
837 reset_ltsmin_extension :-
838 reset_c_counters,
839 retractall(black_magic_short_transformation(_,_,_,_,_,_,_,_,_,_,_)),
840 retractall(label_to_reads(_,_)),
841 retractall(inv_state_label_matrix(_)),
842 retractall(inv_state_label_matrix_for_por(_)),
843 retractall(varname_list(_)),
844 retractall(op_guards(_, _)),
845 retractall(state_label(_, _, _)),
846 retractall(guard_labels(_)),
847 retractall(guard_reads(_, _)).
848
849 :- use_module(probsrc(debug),[formatsilent/2]).
850 :- public get_init/2.
851 get_init(Msg, PorInt) :-
852 (PorInt == 0 -> Por = false ; Por = true),
853 get_ltsmin_variable_names(VS, VTypes),
854 assertz(varname_list(VS)),
855 formatsilent('List of variables for LTSmin (por=~w): ~w~n',[Por,VS]),
856 maplist(build_initial_state, VS, VTypes, BInitialState),
857 %maplist(any_value_for_type,VTypes,InitialState),
858 maplist(term_to_atom, VTypes, VTypeAtoms),
859 get_transition_groups(TransitionGroups),
860 (csp_with_bz_mode
861 -> InitialState = [stop(no_loc_info_available) | BInitialState], % DUMMY Values
862 Vars = ['$CSP_CHUNK'|VS], Types = ['csp_process'|VTypeAtoms]
863 ; InitialState = BInitialState,
864 Vars = VS, Types = VTypeAtoms
865 ),
866 debug_println(9,initial(InitialState)),
867 write_chunklist(Msg, InitialState, initial_state),
868 write_chunklist(Msg, TransitionGroups, transition_group_list),
869 write_chunklist(Msg, Vars, variables_name_list), % we use $CSP_CHUNK as name for the CSP chunk
870 write_chunklist(Msg, Types, variables_type_list),
871
872
873 put_read_write_matrices(Msg, MR),
874 assert_short_state_info(MR, InitialState),
875
876 (Por == false -> inv_state_label_matrix(InvStateLabelMatrix)
877 ; inv_state_label_matrix_for_por(InvStateLabelMatrix)),
878
879 %print(slm(InvStateLabelMatrix)),nl,
880 %% split guards to state labels
881 split_guards(Por, TransitionGroups, GuardVector, GuardNames, IndexVectors),
882 get_state_labels(GuardVector, GuardNames, guard, GuardStateLabelMatrix),
883
884 % memoize mapping OpName -> GuardNames
885 assert_op_guards(TransitionGroups, IndexVectors),
886 % memoize mapping GuardName -> ReadSet(Guard)
887 assert_guard_readsets(GuardStateLabelMatrix),
888
889 reverse(GuardNames, GuardNamesInOrder),
890 assertz(guard_labels(GuardNamesInOrder)),
891
892 get_ltl_labels(ListOfLabelASTs, LtlApNames),
893 ltl_to_state_label_matrix(ListOfLabelASTs, LtlApNames, LtlStateLabelMatrix),
894
895 % accessed variable names in order (for short states)
896 assert_label_to_accessed_vars(InvStateLabelMatrix),
897 assert_label_to_accessed_vars(GuardStateLabelMatrix),
898 assert_label_to_accessed_vars(LtlStateLabelMatrix),
899
900 %% write all state labels (invariant conjuncts and guard conjuncts)
901 write_matrix(Msg, InvStateLabelMatrix, state_label_matrix),
902 write_matrix(Msg, GuardStateLabelMatrix, guard_label_matrix),
903 write_matrix(Msg, LtlStateLabelMatrix, ltl_label_matrix),
904
905 %% create guard info matrix, i.e. which transition depends on which guards
906 create_matrix(TransitionGroups, IndexVectors, GuardInfoMatrix),
907 write_matrix(Msg, GuardInfoMatrix, guard_info_matrix),
908 calc_and_append_por_info(Por, Msg, GuardVector, GuardNames, TransitionGroups).
909
910
911
912 compute_successors('$init_state',_,Result) :- !,
913 findall(S,init_state(S),Result),
914 debug:debug_println(9,res_init_state(Result)).
915 compute_successors(OpName,L,Result) :-
916 temporary_set_preference(try_operation_reuse,false,CHNG2), % operation reuse done by LTSmin; we would only get no_packed_state_info messages and no benefit
917 (debug:debug_mode(on) -> nl,print(compute_successors(OpName)),nl,flush_output ; true),
918 findall(S,specfile:specfile_trans(L,OpName,_,S,_,_),Result),
919 reset_temporary_preference(try_operation_reuse,CHNG2).
920 % this in turn will call b_execute_top_level_operation_update for normal operations
921
922 :- public next_action/3. % called from C (ltsmin.c, see init_predicate)
923 next_action(Msg, OperationA, StateA) :- %print(prolog_get_succs),nl,flush_output,
924 mnf1(next_action_aux(long, Msg, OperationA, StateA)).
925
926 :- public next_action_short/3. % called from C (ltsmin.c, see init_predicate)
927 next_action_short(Msg, OperationA, StateA) :- %print(prolog_get_succs),nl,flush_output,
928 mnf1(next_action_aux(short, Msg, OperationA, StateA)).
929 next_action_aux(Type, Msg, OperationA, StateA) :-
930 read_state(StateA,State),
931 fast_buf_read(Operation,OperationA),
932 %print(generate(Type, State, Operation)),nl,
933 (Type == short -> black_magic_short_transformation(Operation, _, _, _, _, _, BoundState, State, LongPattern, ShortSucc, Defaults), LongOrPattern = LongPattern-ShortSucc-Defaults
934 ; generate_bound_long_state(State, BoundState), LongOrPattern = long),
935 (debug:debug_mode(on)
936 -> format('~n ** get_successors for ~w~n',[Operation]),
937 translate:print_state(BoundState),nl
938 ; true),
939 get_next_action(LongOrPattern, Msg, BoundState,Operation).
940
941 :- use_module(probsrc(preferences),[get_preference/2,temporary_set_preference/3,reset_temporary_preference/2]).
942 get_next_action(Type, Msg, State,Op) :- % State is a list of bindings
943 temporary_set_preference(ltsmin_do_not_evaluate_top_level_guards,true,CHNG),
944 get_next_states(Type, Msg, State,Op),
945 reset_temporary_preference(ltsmin_do_not_evaluate_top_level_guards,CHNG).
946
947 init_state(Res) :- specfile:specfile_trans(root,_,_,S,_,_),
948 init_state2(S,Res). % , nl,print(init(Res)),nl.
949
950 init_state2(csp_and_b_root,Res) :- !,
951 specfile:specfile_trans(csp_and_b_root,_,_,S,_,_),
952 init_state2(S,Res).
953 init_state2(concrete_constants(L),FullRes) :- !,
954 specfile:specfile_trans(concrete_constants(L),_,_,Res,_,_),
955 expand_const_and_vars_to_full_store(Res,FullRes).
956 init_state2(R,R).
957
958 :- use_module(probsrc(b_interpreter),[b_test_boolean_expression_for_ground_state/5]).
959 eval_label(Msg, State,invariant) :-
960 bmachine:b_get_invariant_from_machine(Invariant),!, % Should we use: get_unproven_invariants
961 (profile_single_call(check_invariant,
962 b_interpreter:b_test_boolean_expression_for_ground_state(Invariant,[],State,'LTSMin invariant label',0)) ->
963 put_number(Msg, 1); put_number(Msg, 0)).
964 eval_label(Msg, State,Label) :-
965 (debug:debug_mode(on) -> print(eval(Label)),nl,flush_output ; true),
966 state_label(Label, Type, AST),
967 profile_single_call(Label,ltsmin:eval_label2(Type, Msg, State, AST)).
968
969
970 %:- use_module(probltlsrc(ltl_prepositions), [check_ap/2]).
971 eval_label2(ltl_ap, Msg, State, AST) :-
972 (check_ap_bound_state(AST,State) ->
973 put_number(Msg, 1); put_number(Msg, 0)).
974 eval_label2(_, Msg, State, AST) :-
975 (b_test_boolean_expression_for_ground_state(AST,[],State,'LTSMin label',0) ->
976 put_number(Msg, 1) ; put_number(Msg, 0)).
977 % foreign predicate put_number(Msg, X) :: 0 = false, 1=true
978
979
980 get_opname(bop(Name, _, _, _, _), Name).
981 get_opname(btrans(event(Name)), Name).
982 get_opname(btrans(event(Name, _, _)), Name).
983
984 op_enabled(Op, State) :-
985 get_opname(Op, OpName),
986 get_guard(OpName, Guard),
987 b_test_boolean_expression_for_ground_state(Guard, [], State,'LTSMin op_enabled',0).
988
989 % check an atomic proposition in a given node (i.e. state)
990 check_ap_bound_state(bpred(Property),Node) :-
991 b_test_boolean_expression_for_ground_state(Property, [], Node,'LTSMin atomic proposition',0).
992 check_ap_bound_state(enabled(Op),Node) :- !,
993 op_enabled(Op, Node).
994 check_ap_bound_state(det(OpList),Node) :- !,
995 max_enabled(OpList, Node, 1, 0, R),
996 R < 2.
997 check_ap_bound_state(dlk(OpList),Node) :- !,
998 max_enabled(OpList, Node, 0, 0, 0).
999 check_ap_bound_state(ctrl(OpList),Node) :- !,
1000 max_enabled(OpList, Node, 1, 0, 1).
1001 check_ap_bound_state(deadlock,Node) :- !,
1002 get_transition_groups(OpList),
1003 check_ap_bound_state(dlk(OpList), Node).
1004
1005 %max_enabled(OpList, Max, EnabledSoFar, ResEnabled)
1006 max_enabled([], _State, _Max, R, R).
1007 max_enabled([Op|T], State, Max, EnabledSoFar, R) :-
1008 (op_enabled(Op, State)
1009 -> Enabled is EnabledSoFar + 1,
1010 Max >= Enabled,
1011 max_enabled(T, State, Max, Enabled, R)
1012 ; max_enabled(T, State, Max, EnabledSoFar, R)).
1013
1014