1 % (c) 2009-2026 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(value_persistance, [set_storage_directory/1,set_storage_directory/2,
6 unset_storage_directory/0,
7 save_constants/1,
8 load_constants/2,
9 cache_is_activated/0, clear_applicable_flag/0,
10 load_partial_constants/3, % load saved constants for sub-machines
11 lookup_cached_transitions/4,
12 add_new_transitions_to_cache/1,
13 add_new_transition_to_cache_from_expanded_state/4, % mainly for execute_modus
14 start_cache_execute_modus/1, stop_cache_execute_modus/0,
15 tcltk_load_constants/1,
16 delete_cache_files/0,
17 delete_cache_files_for_machine/1,
18 show_cache_file_contents/1, % portray contents of cache files
19 print_value_persistance_stats/0,
20 get_value_persistance_stats/1
21 ]).
22
23
24 :- use_module(library(lists)).
25 :- use_module(library(file_systems)).
26 :- use_module(library(fastrw)). % fast_read/2
27
28 :- use_module(debug).
29 :- use_module(tools).
30 :- use_module(error_manager, [add_error/2,add_error/3]).
31 :- use_module(state_space, [transition/3,visited_expression/2,
32 time_out_for_node/1,not_all_transitions_added/1,
33 time_out_for_node/3,transition/4,
34 transition_info/2 %,max_reached_for_node/1
35 ]).
36 :- use_module(succeed_max, [max_reached/1]).
37 :- use_module(preferences).
38 :- use_module(b_machine_hierarchy).
39 :- use_module(specfile, [animation_mode/1, classical_b_mode/0,
40 state_corresponds_to_initialised_b_machine/2]).
41 :- use_module(store, [lookup_value/3]).
42 :- use_module(kernel_objects, [equal_object/2]).
43 :- use_module(bmachine, [b_get_machine_refinement_hierarchy/1,
44 b_top_level_operation/1, get_operation_info/2,
45 b_get_machine_constants/1,
46 get_nr_of_machine_constants/1]).
47 :- use_module(bmachine_construction, [used_machine_not_included_anywhere/3]).
48 %:- use_module(bmachine_structure, [get_section/3]).
49 :- use_module(b_global_sets, [is_b_global_constant/3,b_global_set/1]).
50 :- use_module(bsyntaxtree).
51 :- use_module(extension('probhash/probhash'),[raw_sha_hash/2]).
52 :- use_module(tools_printing,[format_with_colour_nl/4]).
53 :- use_module(runtime_profiler,[start_profile/2, stop_profile/4]).
54 :- use_module(memoization,[export_memo_state/1, import_memo_state/1]).
55 :- use_module(state_packing).
56
57 :- use_module(module_information).
58 :- module_info(group,interpreter).
59 :- module_info(description,'This module is responsible for caching constants and operations on disk').
60
61 :- dynamic storage_directory/1.
62 % storage_directory('/home/plagge/svn/alstomprob/examples/cache/').
63 :- volatile storage_directory/1.
64 :- dynamic is_applicable_internal/1.
65 :- volatile is_applicable_internal/1.
66
67 /**********************************************************************/
68 /* common for loading and saving: */
69 /**********************************************************************/
70
71 cache_is_activated :-
72 storage_directory(_).
73
74 cache_is_applicable_for_constants :-
75 cache_is_applicable, get_preference(cache_constants,true).
76 cache_is_applicable_for_transitions :-
77 cache_is_applicable, get_preference(cache_operations,true).
78
79 cache_is_applicable :-
80 is_applicable_internal(Applicable),!,Applicable=true.
81 cache_is_applicable :-
82 cache_is_activated,
83 % b_mode, % only for B models
84 classical_b_mode,
85 b_get_machine_refinement_hierarchy([_]), % only if there is no refinement - we cache no refinements
86 get_preference(symmetry_mode,off), % with symmetry on: you cannot simply patch operation updates
87 formatinfo('value caching: cache IS applicable',[]),
88 assert_once( is_applicable_internal(true) ),!.
89 cache_is_applicable :-
90 debug_format(19,'value persistance, cache NOT applicable~n',[]),
91 assert_once( is_applicable_internal(false) ),fail.
92
93 :- use_module(tools,[get_parent_directory_of_directory/2]).
94 set_storage_directory(Dir) :- set_storage_directory(Dir,strict).
95 set_storage_directory(Dir,Create) :-
96 unset_storage_directory,
97 (absolute_file_name(Dir,AbsDir,[file_type(directory),access(exist),fileerrors(fail)])
98 -> assertz(storage_directory(AbsDir))
99 ; Create=create_if_needed,
100 get_parent_directory_of_directory(Dir,ParentDir),
101 directory_exists(ParentDir),
102 % parent directory exists; try to create the directory itself
103 absolute_file_name(Dir,AbsDir,[file_type(directory),access(none),fileerrors(fail)]),
104 formatinfo('Trying to create cache directory: ~w',[AbsDir]),
105 make_directory(AbsDir)
106 -> assertz(storage_directory(AbsDir))
107 ; add_error(cache,'Could not set cache directory, directory does not exist: ',Dir)).
108
109 reset_value_persistance :-
110 unset_storage_directory,
111 reset_counters.
112 unset_storage_directory :-
113 retractall(storage_directory(_)),
114 clear_applicable_flag.
115
116 % should be called if e.g. animation mode information changes (csp_and_b mode)
117 clear_applicable_flag :-
118 retractall(is_applicable_internal(_)).
119 :- use_module(eventhandling,[register_event_listener/3]).
120 :- register_event_listener(change_of_animation_mode,clear_applicable_flag, 'Clear Cache Info.').
121 :- register_event_listener(reset_prob,reset_value_persistance, 'Reset Cache').
122
123 %:- use_module(version, [revision/1]).
124 :- use_module(version,[version/4]).
125 get_revision_info(info(V1,V2,V3,V4,ValuePersistanceRevision)) :-
126 % revision(Revision), % revision not used: it differs between probcli and ProB Tcl/Tk
127 version(V1,V2,V3,V4),
128 ValuePersistanceRevision = '$Rev$'.
129
130 collect_computation_parameters(Name,[machine_name(Name)
131 ,prob_revision(Revision)
132 %,machine_hash(Hash)
133 ,constants_signature(ConstSig)
134 |Prefs]) :-
135 find_relevant_preferences(Prefs),
136 get_revision_info(Revision),
137 %machine_hash(Name,Hash),
138 compute_constants_signature(Name,ConstSig).
139
140 /* returns a list of Pref/Value pairs for each preference that is regarded
141 a relevant when computing values for constants. See is_relevant_preference/2
142 below */
143 find_relevant_preferences(Prefs) :-
144 findall(preference(Pref,Value), relevant_preference(Pref,Value), Unsorted),
145 sort(Unsorted,Prefs).
146
147 % value_persistance:find_relevant_preferences(L), member(preference(P,_),L), print(P),nl,fail.
148
149 relevant_preference(Pref,Value) :-
150 preference_category(Pref,Category),
151 ( is_relevant_preference(Pref,Category) -> true; fail), % just to introduce a local cut
152 get_preference(Pref,Value).
153
154 /* is_relevant_preference(+Preference,+Category)
155 is used to specify those preferencees whose values are stored in the constants file.
156 If such a preference changes, the constants have to be recomputed */
157
158 is_relevant_preference(Pref,_) :- nonvar(Pref),
159 irrelevant_preference(Pref),!,fail.
160 is_relevant_preference(_Pref,animation).
161 is_relevant_preference(_Pref,hidden).
162 is_relevant_preference(_Pref,advanced).
163
164 % number_animated_abstractions : only relevant for Event-B + if refinement
165 irrelevant_preference(cache_operations_runtime_limit). % only influences what gets stored; re-use always safe
166 irrelevant_preference(cache_constants). % ditto
167 irrelevant_preference(cache_operations). % ditto
168 irrelevant_preference(clash_strict_checks). % no influence on values computed
169 irrelevant_preference(strict_raise_warnings). % no influence on values computed
170 irrelevant_preference(warn_if_definition_hides_variable). % no influence on values computed
171 %irrelevant_preference(strict_raise_enum_warnings). % no sure if it can affect outcome in meaningful; could result in symbolic instead values??
172 irrelevant_preference(jvm_parser_heap_size_mb). % no influence on values computed
173 irrelevant_preference(jvm_parser_additional_args). % ditto
174 irrelevant_preference(use_safety_ltl_model_checker). % ditto
175 irrelevant_preference(store_only_one_incoming_transition). % ditto
176 irrelevant_preference(forget_state_space). % ditto
177 irrelevant_preference(type_check_definitions). % ditto
178 irrelevant_preference(expand_avl_upto). % only affects pretty-printing
179 irrelevant_preference(bool_expression_as_predicate). % only used inside REPL/eval
180 irrelevant_preference(use_large_jvm_for_parser).
181 irrelevant_preference(expand_forall_upto). % only relevant in predicate analysis
182 irrelevant_preference(time_out). % if time_out occurs: no re-use anyway
183 irrelevant_preference(performance_monitoring_on). % just prints messages
184 irrelevant_preference(performance_monitoring_expansion_limit).
185 irrelevant_preference(kodkod_for_components) :- \+ preference(use_solver_on_load,kodkod).
186 irrelevant_preference(do_neginvariant_checking). % just indicates whether invariant evaluated doubly
187 irrelevant_preference(provide_trace_information). % just provides feedback; should not change values
188 irrelevant_preference(prob_profiling_on). % ditto
189 irrelevant_preference(prob_source_profiling_on). % ditto
190 irrelevant_preference(use_closure_expansion_memoization). % only influences performance
191 irrelevant_preference(warn_when_expanding_infinite_closures). % only generates extra warnings
192 irrelevant_preference(use_small_window).
193 irrelevant_preference(tk_show_source_pane).
194 irrelevant_preference(check_prolog_b_ast).
195 irrelevant_preference(use_font_size_for_columns).
196 irrelevant_preference(user_is_an_expert_with_accessto_source_distribution).
197 irrelevant_preference(default_to_runtime_type_checking_on_startup_for_expert).
198 irrelevant_preference(number_of_recent_documents).
199 irrelevant_preference(number_of_searched_patterns).
200 irrelevant_preference(number_of_replaced_patterns).
201 irrelevant_preference(number_of_eval_history_elements).
202 irrelevant_preference(number_of_eval_csp_history_elements).
203 irrelevant_preference(number_of_checked_ltl_formulas).
204 irrelevant_preference(machines_path).
205 irrelevant_preference(tlc_number_of_workers).
206 irrelevant_preference(tlc_use_prob_constant_setup).
207 irrelevant_preference(unsat_core_algorithm).
208
209 irrelevant_preference(deterministic_trace_replay).
210 irrelevant_preference(dot_use_ps_viewer).
211 %irrelevant_preference(double_evaluation_when_analysing).% not used when computing constants or events
212 irrelevant_preference(generate_minimal_nr_of_testcases).
213 irrelevant_preference(ltl_to_ba_tool).
214 irrelevant_preference(path_to_atb_krt).
215 irrelevant_preference(path_to_bcomp).
216 irrelevant_preference(path_to_clingo).
217 irrelevant_preference(path_to_cspm).
218 irrelevant_preference(path_to_csp_typechecker).
219 irrelevant_preference(path_to_dot).
220 irrelevant_preference(path_to_dotty).
221 irrelevant_preference(path_to_fdr).
222 irrelevant_preference(path_to_fuzz). %% ??
223 irrelevant_preference(path_to_latex).
224 irrelevant_preference(path_to_ltsmin).
225 irrelevant_preference(path_to_probe).
226 irrelevant_preference(path_to_ps_viewer).
227 irrelevant_preference(path_to_spin).
228 irrelevant_preference(path_to_text_editor).
229 irrelevant_preference(path_to_text_editor_launch).
230 irrelevant_preference(repl_cache_parsing).
231 irrelevant_preference(show_bvisual_formula_explanations).
232 irrelevant_preference(show_bvisual_formula_functor_from).
233 irrelevant_preference(show_bvisual_proof_info_icons).
234 irrelevant_preference(show_function_tuples_in_property).
235 irrelevant_preference(symbolic_mc_try_other_solvers).
236 irrelevant_preference(use_scope_predicate).
237
238 irrelevant_preference(path_to_java).
239 irrelevant_preference(translate_print_all_sequences).
240 irrelevant_preference(translate_print_cs_style_sequences).
241 irrelevant_preference(view_probcli_errors_using_bbresults).
242 irrelevant_preference(trace_upon_error).
243 irrelevant_preference(error_log_file).
244 irrelevant_preference(tk_custom_state_viewer_padding).
245 irrelevant_preference(tk_custom_state_viewer_str_padding).
246 irrelevant_preference(tk_custom_state_viewer_font_name).
247 irrelevant_preference(tk_custom_state_viewer_font_size).
248 irrelevant_preference(use_tk_custom_state_viewer).
249 irrelevant_preference(translate_suppress_rodin_positions_flag).
250 irrelevant_preference(translate_ids_to_parseable_format).
251 irrelevant_preference(translate_print_frozen_infos).
252 irrelevant_preference(translate_print_typing_infos).
253 irrelevant_preference(translate_force_all_typing_infos).
254 irrelevant_preference(prob_lastchangedate_info).
255 irrelevant_preference(prob_revision_info). % comment in for development mode; to avoid re-computing cache after minor changes
256 irrelevant_preference(X) :- preference_category(X,Cat), irrelevant_category(Cat).
257
258 irrelevant_category(alloy2b).
259 irrelevant_category(cbc_commands). %cbc_provide_explanations
260 irrelevant_category(distb).
261 irrelevant_category(dot).
262 irrelevant_category(dot_definitions).
263 irrelevant_category(dot_event_hierarchy).
264 irrelevant_category(dot_formula_tree).
265 irrelevant_category(dot_machine_hierarchy).
266 irrelevant_category(dot_graph_generator).
267 irrelevant_category(dot_projection).
268 irrelevant_category(dot_state_graph).
269 irrelevant_category(dot_state_space).
270 irrelevant_category(dot_variable_modification).
271 irrelevant_category(gui_prefs).
272 irrelevant_category(latex).
273 irrelevant_category(mc_dc_commands).
274 irrelevant_category(smtlib2b).
275 irrelevant_category(state_as_graph).
276 irrelevant_category(syntax_highlighting).
277 irrelevant_category(table_commands).
278 irrelevant_category(trace_generator).
279 irrelevant_category(visb).
280 irrelevant_category(wd_commands).
281 % alternate categories already covered by dot category:
282 %irrelevant_category(state_as_graph).
283 %irrelevant_category(dot_graph_generator).
284 %irrelevant_category(dot_state_space).
285
286 % compute signature of PROPERTIES and of PROPERTIES of all referenced machines
287 compute_constants_signature(Name,constsig(Name,PropHash,Sublist)) :-
288 properties_hash(Name,PropHash),
289 findall(Sig,
290 ( referenced_machine_with_constants_or_properties(Name,RefName),
291 compute_constants_signature(RefName,Sig)),
292 SubSigs),
293 sort(SubSigs,Sublist).
294
295 referenced_machine_with_constants_or_properties(OrigName,RefName) :-
296 referenced_machine(OrigName,RefName,_,_),
297 machine_has_constants_or_properties_trans(RefName).
298
299 machine_has_constants_or_properties_trans(Name) :-
300 machine_has_constants_or_properties(Name),!.
301 machine_has_constants_or_properties_trans(Name) :-
302 referenced_machine(Name,Ref,_,_),
303 machine_has_constants_or_properties_trans(Ref),!.
304 machine_has_constants_or_properties(Name) :-
305 machine_identifiers(Name,_Params,Sets,_AVars,_CVars,AConsts,CConsts),
306 ( Sets=[_|_] ; AConsts==[_|_] ; CConsts=[_|_]),!.
307
308 referenced_machine(SrcName,DstName,Type,Prefix) :-
309 machine_references(SrcName,Refs),
310 member(ref(Type,DstName,Prefix),Refs).
311
312 /**********************************************************************/
313 /* saving values of constants */
314 /**********************************************************************/
315
316 save_constants(MaxReached) :-
317 cache_is_applicable,
318 main_machine_name(Name), % this fails e.g. in Z mode
319 \+ constants_loaded_from_file(Name), % we have just loaded the constants from file; no need to re-write them
320 !,
321 save_constants1(Name,MaxReached).
322 %(machine_name(MName), save_constants1(MName,MaxReached), fail ; true). % this would save all constants; not just the one's from the main machine; but we should probably only save the constants whose value is already deterministic in the original included machine
323 save_constants(_MaxReached).
324
325 save_constants1(Name,MaxReached) :-
326 (save_constants2(Name,MaxReached) -> true ; add_error(value_persistance,'Storing constants failed for:',Name)).
327
328 save_constants2(Name,MaxReached) :-
329 statistics(runtime,[Start,_]),
330 statistics(walltime,[WStart,_]),
331 save_constants_for_machine(Name,MaxReached),
332 statistics(runtime,[Stop,_]), Time is Stop - Start,
333 statistics(walltime,[WStop,_]), WTime is WStop - WStart,
334 %% tools:print_memory_used_wo_gc,
335 formatinfo('value caching: storing constants for ~w (max_reached: ~w) (~w [~w] ms).',
336 [Name,MaxReached,Time,WTime]).
337
338 save_constants_for_machine(Name,MaxReached) :-
339 machine_has_constants_no_params(Name), % at least one constant exists, check before computing findall !
340 find_constants_stores(Stores), % find all concrete_constants(_) nodes in state_space
341 is_usable_data(Name,Stores),!,
342 save_constants_stores(Name,MaxReached,Stores).
343 save_constants_for_machine(Name,_MaxReached) :-
344 formatinfo('value caching: no constants stored for ~w',[Name]).
345
346 % called by add_new_transition_to_cache_from_expanded_state:
347 save_constants_from_expanded_state(SingleState) :-
348 cache_is_applicable_for_constants,
349 main_machine_name(Name),
350 \+ constants_loaded_from_file(Name), % we have just loaded the constants from file; no need to re-write them
351 SingleState = concrete_constants(Store),
352 length(Store,Len), formatinfo('value caching: saving ~w constants from expanded state',[Len]),
353 !,
354 MaxReached=true, % we do not know if there are more constants states
355 save_constants_stores(Name,MaxReached,[Store]).
356 save_constants_from_expanded_state(_) :-
357 (cache_is_applicable,main_machine_name(Name)
358 -> formatinfo('value caching: no constants stored for ~w',[Name]) ; true).
359
360 % TODO: at the moment memoisation closures will not work and Memo id info also need to be stored and re-loaded
361 save_constants_stores(Name,MaxReached,Stores) :-
362 debug_println(9,saving_constants(Name)),
363 Stores=ConstantBindings, %find_constants(Name,Constants),maplist(generate_bindings(Constants),Stores,ConstantBindings), % first arg. (Constants) currently not used
364 collect_computation_parameters(Name,CompParams),
365 length(ConstantBindings,Len),formatinfo('value caching: saving ~w constant solutions of machine ~w to file.',[Len,Name]),
366 export_memo_state(MemoState),
367 bb_inc_by(value_persistance_stored_constants,Len),
368 save_constants_values_into_file(Name,CompParams,ConstantBindings,MemoState,MaxReached).
369
370 % find_main_constants(Constants) :- b_get_machine_constants(C), sort(C,Constants).
371 %find_constants(Name,Constants) :-
372 % get_machine_constants(Name,AbstractConstants,ConcreteConstants),
373 % append(AbstractConstants,ConcreteConstants,RawConstants),
374 % maplist(get_raw_identifier,RawConstants,Unsorted),
375 % sort(Unsorted,Constants).
376
377 get_raw_identifier(identifier(_Pos,Id),Id).
378 get_raw_identifier(description(_Pos,_Desc,Raw),Id) :- get_raw_identifier(Raw,Id).
379
380 get_machine_constants(Name,AbstractConstants,ConcreteConstants) :-
381 machine_identifiers(Name,Params,_Sets,_AVars,_CVars,AbstractConstants,ConcreteConstants),
382 ( Params=[_|_]
383 -> add_warning(value_persistance,'Parameters not yet supported.'), fail
384 % ok to just issue warning ?? or should we generate error ?
385 ; true).
386
387 machine_has_constants_no_params(Name) :-
388 get_machine_constants(Name,AbstractConstants,ConcreteConstants),
389 (AbstractConstants = [_|_] -> true ; ConcreteConstants = [_|_]).
390
391 /* Returns the stores of values in the state space that contain values for contants */
392 find_constants_stores(Stores) :-
393 findall(S,
394 ( transition(root,Trans,I),
395 functor(Trans,'$setup_constants',_),
396 visited_expression(I,concrete_constants(S))
397 ),
398 Stores).
399
400 /* is_usable_data/2 succeeds if there are constants that are ready to store */
401 is_usable_data(MachName,Stores) :-
402 % at least one state with computed constants was generated
403 Stores = [_|_],
404 % all constants have been computed
405 (not_all_transitions_added(root)
406 -> formatwarn('value caching: not storing constants for ~w as not all transitions computed.',[MachName]), fail
407 ; true),
408 % no time-out in the root node
409 (time_out_for_node(root)
410 -> formatwarn('value caching: not storing constants for ~w as TIME_OUT occured.',[MachName]), fail
411 ; true).
412
413 % TODO: review if needs to be used
414 %generate_bindings(Constants,Store,ConstantsBinding) :-
415 % maplist(generate_binding2(Store),Constants,ConstantsBinding).
416 %generate_binding2(Store,Constant,bind(Constant,Value)) :-
417 % lookup_value(Constant,Store,Value).
418
419
420 save_constants_values_into_file(Name,CompParams,ConstantBindings,MemoState,MaxReached) :-
421 storage_file_name(constants,Name,Filename),
422 open(Filename,write,S,[type(binary)]),
423 call_cleanup(save_values_into_file2(S,CompParams,ConstantBindings,MemoState,MaxReached),
424 my_close(S)).
425 save_values_into_file2(S,CompParams,ConstantBindings,MemoState,MaxReached) :-
426 fast_write(S,comp_parameters(CompParams)),
427 fast_write(S,maximum_reached(MaxReached)),
428 fast_write(S,memo_state(MemoState)),
429 maplist(write_bind(S,constants),ConstantBindings),
430 fast_write(S,end_of_file).
431
432 write_bind(S,Type,Store) :-
433 temporary_set_preference(use_state_packing,false,Change),
434 pack_values(Store,PS), % does not seem to bring major performance benefit; but reduces file size
435 % print(packed(Store,PS)),nl,
436 reset_temporary_preference(use_state_packing,Change),
437 fast_write(S,values(Type,PS)).
438 % fast_write(S,values(Type)), fast_write_list(PS,S). % should we do something like this to reduce memory consumption ?
439
440 %fast_write_list([],S) :- fast_write(S,end_of_list).
441 %fast_write_list('$bind_var'(Var,Val,T),S) :- fast_write(S,bind(Var,Val)), fast_write_list(T,S).
442
443 /* compute the full file name for the stored constants for the machine
444 the predicate uses the specified directory where constants values
445 should be stored */
446 storage_file_name(Type,MachineName,FullName) :-
447 cache_file_suffix(Type,Suffix),!,
448 storage_directory(Directory),
449 atom_concat(MachineName,Suffix,Name),
450 atom_concat(Directory,Name,FullName).
451 storage_file_name(Type,MachineName,_) :-
452 add_internal_error('Illegal cache file type: ',storage_file_name(Type,MachineName,_)),fail.
453
454 cache_file_suffix(constants,'.probcst').
455 cache_file_suffix(operations_index,'.probops').
456 cache_file_suffix(operations_data,'.opdata').
457
458 cache_file_type(constants).
459 cache_file_type(operations_index).
460
461 /**********************************************************************/
462 /* load constants */
463 /**********************************************************************/
464
465 % a predicate to see which constant values have been saved
466 tcltk_load_constants(list(CS)) :-
467 load_constants(ConstantsStores,_),
468 maplist(translate:translate_bstate,ConstantsStores,CS).
469
470
471 /* load_constants/2 returns a list of stores if the constants have been
472 saved before for the same computation parameters.
473 The predicate fails if no constants can be loaded.
474 MaxReached is 'true' if the maximum number of computed solutions was reached
475 or 'false' otherwise.
476 */
477
478 :- dynamic constants_loaded_from_file/1.
479 :- volatile constants_loaded_from_file/1.
480 % load constants for main machine
481 load_constants(ConstantStores,MaxReached) :-
482 cache_is_applicable_for_constants, % check if storing/re-using constants is activated
483 main_machine_name(Name),
484 machine_constants_cache_file_exists(Name,FullName),
485 open(FullName,read,S,[type(binary)]),
486 call_cleanup(load_constants2(S,Name,ConstantStores,MaxReached),
487 my_close(S)),!.
488 load_constants2(S,Name,ConstantStores,MaxReached) :-
489 start_profile(Name,T1),
490 load_parameters(S,StoredParameters), % if the relevant computation parameters
491 collect_computation_parameters(Name,CurrentParameters), % are different, we cannot re-use the
492 (compare_computation_parameters(CurrentParameters,StoredParameters,Name) % values stored in the file
493 -> true
494 ; formatwarn('value caching: parameters changed; not reusing previously computed constants for main machine ~w.',[Name]),
495 stop_profile(Name,cache_load_constants_failed,unknown,T1),
496 fail
497 ),
498 load_max_reached(S,MaxReached),
499 load_constant_values(S,ConstantStores),
500 bb_inc(value_persistance_constants_loaded),
501 (debug_mode(off)
502 -> formatgood('value caching: re-using stored solutions for constants for main machine ~w.', [Name])
503 ; length(ConstantStores,Len),
504 get_ids(ConstantStores,CstNames),
505 length(CstNames,CLen),
506 formatgood('value caching: re-using ~w stored solutions for ~w constants ~w for main machine ~w.',
507 [Len,CLen,CstNames,Name])
508 ),
509 assert_once( constants_loaded_from_file(Name) ),
510 stop_profile(Name,cache_load_constants,unknown,T1).
511
512
513 machine_constants_cache_file_exists(Name,FullName) :-
514 cache_file_exists(constants,Name,FullName).
515 cache_file_exists(Type,Name,FullName) :-
516 storage_file_name(Type,Name,FullName),
517 (file_exists(FullName)
518 -> debug_format(19,'value caching: ~w cache file ~w exists for ~w.~n',[Type, FullName, Name])
519 ; get_tail_filename(FullName,Tail),
520 formatinfo('value caching: ~w cache file ~w does not yet exist for ~w.',[Type, Tail, Name]),
521 fail
522 ).
523
524 load_parameters(S,StoredParameters) :-
525 safe_constants_read(S,comp_parameters(StoredParameters)).
526
527 load_max_reached(S,MR) :-
528 safe_constants_read(S,maximum_reached(MR)).
529
530
531 load_constant_values(S,Stores) :-
532 safe_constants_read(S,memo_state(MS)),
533 import_memo_state(MS),
534 load_constant_values2(S,Stores).
535 load_constant_values2(S,Stores) :-
536 safe_constants_read(S,Term),
537 ( Term = end_of_file ->
538 Stores = []
539 ; Term = values(constants,Store) ->
540 unpack_constants(Store,UPS),
541 Stores = [UPS|Rest],
542 load_constant_values2(S,Rest)).
543
544 unpack_constants(Store,UPS) :-
545 temporary_set_preference(use_state_packing,false,Change),
546 unpack_values(Store,UPS),
547 % print(unpacked(Store,UPS)),nl,
548 reset_temporary_preference(use_state_packing,Change).
549
550 safe_constants_read(S,Term) :-
551 read_syntax_safe(S,T,Error),
552 ( Error==true ->
553 write('Invalid cache file content. File skipped.\n'),
554 fail
555 ;
556 Term = T).
557
558 % load constant solutions from subsidiary referenced machines
559 load_partial_constants(ConstantsBindings,InProperties,OutProperties) :-
560 % TODO: - check if parameters / constraints have been used (what are the implications?)
561 cache_is_applicable_for_constants, % check if storing/re-using constants is activated
562 main_machine_name(Name),
563 start_profile(Name,T1),
564 findall( partial(MName,NewBindings),
565 try_to_reuse_referenced_machine(Name,MName,NewBindings),
566 PSol),
567 (PSol = [_|_] -> true
568 ; stop_profile(Name,cache_load_constants_failed,unknown,T1),fail),
569 !,
570 remove_evaluated_properties(PSol,InProperties,OutProperties),
571 length(PSol,Len),
572 get_number_of_loaded_partial_constants(PSol,0,PLoaded),get_nr_of_machine_constants(Tot),
573 formatgood('value caching: partially re-using stored solutions for ~w/~w constants of ~w referenced machines of ~w.',
574 [PLoaded,Tot,Len,Name]),
575 stop_profile(Name,cache_load_constants,unknown,T1),
576 select_and_apply_binding(PSol,ConstantsBindings).
577 load_partial_constants(_ConstantsBindings,Properties,Properties).
578
579 % compute number of constants that are partially loaded across all different referenced machines
580 get_number_of_loaded_partial_constants([],A,A).
581 get_number_of_loaded_partial_constants([partial(_RefMachine,Bindings)|T],Acc,Res) :-
582 (Bindings = [Store1|_], length(Store1,Len) -> A1 is Acc+Len ; A1 = Acc),
583 get_number_of_loaded_partial_constants(T,A1,Res).
584
585 /**********************************************************************/
586
587 :- meta_predicate open_cache_file(+,+,-,0).
588 open_cache_file(Type,Name,Stream,Call) :-
589 cache_file_exists(Type,Name,FullName), %print_cache_file_props(Type,Name),
590 my_open(FullName,read,Stream,[type(binary)]),
591 formatdetails('Opened cache file ~w~n',[FullName]),
592 call_cleanup( (call(Call),!),my_close(Stream)).
593
594 print_cache_file_props(Type,Name) :-
595 cache_file_exists(Type,Name,FullName),
596 file_property(FullName, modify_timestamp, Time),
597 file_property(FullName,size_in_bytes,Size),
598 formatinfo('Modified at: ~w, size: ~w Bytes',[Time,Size]).
599 /**********************************************************************/
600
601 :- dynamic referenced_machine/1.
602
603 :- mode try_to_reuse_referenced_machine(+,-,-).
604 try_to_reuse_referenced_machine(OrigName,Name,Bindings) :-
605 retractall(referenced_machine(_)),
606 try_to_reuse_referenced_machine1(OrigName,Name,Bindings).
607 try_to_reuse_referenced_machine1(OrigName,Name,Bindings) :-
608 referenced_machine_with_constants_or_properties(OrigName,RefName),
609 (referenced_machine(RefName) -> fail ; true), % we have already processed it
610 assert(referenced_machine(RefName)),
611 try_to_reuse_referenced_machine2(OrigName,RefName,Name,Bindings).
612 try_to_reuse_referenced_machine2(OrigName,RefName,Name,Bindings) :-
613 % try to re-use values of a machine directly referenced by OrigName that
614 % contains constants
615 open_cache_file(constants,RefName,S,try_load_ref_values2(RefName,OrigName,S,Bindings1)),
616 !,
617 RefName = Name,
618 Bindings1 = Bindings.
619 try_to_reuse_referenced_machine2(_,RefName,Name,Bindings) :-
620 % using the directly referenced machine failed, using a machine
621 % that is referenced by the referenced machine
622 try_to_reuse_referenced_machine1(RefName,Name,Bindings).
623
624 try_load_ref_values2(RefName,OrigName,S,Bindings) :-
625 start_profile(RefName,T1),
626 check_if_machine_has_no_parameters(RefName),
627 load_parameters(S,StoredParameters),
628 check_if_all_constants_computed(RefName,S),
629 collect_computation_parameters(RefName,CurrentParameters),
630 (compare_computation_parameters(CurrentParameters,StoredParameters,RefName)
631 -> true
632 ; formatwarn('value caching: not re-using stored solutions for constants for referenced machine ~w.', [RefName]),
633 stop_profile(RefName,cache_load_constants_failed,unknown,T1),
634 fail),
635 load_constant_values(S,Bindings),
636 bb_inc(value_persistance_ref_constants_loaded),
637 (debug_mode(off)
638 -> formatgood('value caching: re-using stored solutions for constants for referenced machine ~w.', [RefName])
639 ; length(Bindings,Len),
640 get_ids(Bindings,CstNames),
641 length(CstNames,CLen),
642 formatgood('value caching: re-using ~w previously computed solutions for ~w constants ~w of referenced machine ~w within ~w.',[Len,CLen,CstNames,RefName,OrigName])
643 % TODO: show names of constants ...
644 ),
645 stop_profile(RefName,cache_load_constants,unknown,T1).
646
647 get_name(bind(ID,_),ID) :- !.
648 get_name(X,X).
649
650 get_ids([Binding|_],Names) :- maplist(get_name,Binding,Names).
651
652 check_if_machine_has_no_parameters(RefName) :-
653 machine_identifiers(RefName,Params,_Sets,_AVars,_CVars,_AConsts,_CConsts),
654 ( Params=[] -> true
655 ;
656 formatwarn('value caching: constants of referenced machine ~w are not used because it uses parameters',[Params]),
657 fail).
658
659 compare_computation_parameters([],[],_) :- !.
660 compare_computation_parameters([CH|CT],[SH|ST],Context) :-
661 !,
662 (SH = preference(Pref,_),
663 irrelevant_preference(Pref) % can happen preference has now become irrelevant and we ignore prob_revision_info
664 -> compare_computation_parameters([CH|CT],ST,Context)
665 ; compare_computation_parameters(CH,SH,Context)
666 -> compare_computation_parameters(CT,ST,Context)
667 ).
668 compare_computation_parameters(op_comp_parameters(C),op_comp_parameters(S),Context) :- !,
669 compare_computation_parameters(C,S,Context).
670 compare_computation_parameters(C,C,_) :- !.
671 compare_computation_parameters(C,S,Context) :-
672 formatwarn('value caching: parameter difference:~ncurrent is ~w,~n stored is ~w (context: ~w).',[C,S,Context]),
673 fail.
674
675 check_if_all_constants_computed(_RefName,S) :-
676 % make sure that all solutions have been generated
677 load_max_reached(S,false),!.
678 check_if_all_constants_computed(RefName,_S) :-
679 formatwarn('value caching: constants for referenced machine ~w not used because not all solutions were computed.',[RefName]),
680 fail.
681
682 select_and_apply_binding(Solutions,Store) :-
683 maplist(select_solutions,Solutions,Bindings),
684 maplist(select_and_apply_binding2(Store),Bindings).
685 select_and_apply_binding2(Store,Binding) :-
686 maplist(apply_binding(Store),Binding).
687 apply_binding(Store,bind(Id,Value)) :-
688 lookup_value(Id,Store,SValue),
689 equal_object(SValue,Value).
690 select_solutions(partial(_Name,Bindings),Binding) :-
691 member(Binding,Bindings). % select one of the stored solutions for referenced machine _Name
692
693 :- use_module(bmachine,[get_machine_file_number/4]).
694 machine_file_number(Name,Nr) :-
695 get_machine_file_number(Name,Ext,Nr,_),
696 Ext \= 'def',!.
697 machine_file_number(Name,Nr) :-
698 add_error(value_persistance,'Could not find machine name:',Name),
699 bmachine:portray_filenumbers,
700 Nr = -99.
701
702 :- use_module(tools_positions, [get_position_filenumber/2]).
703 % remove the properties from those machines whose constants have already been set from file
704 remove_evaluated_properties(PSol,InProperties,OutProperties) :-
705 findall(Name,member(partial(Name,_),PSol),UnsortedNames),
706 sort(UnsortedNames,Names),
707 findall(FileNr,(member(NN,Names), get_subsidiary_loaded_filenumber(NN,FileNr)), FileNumbers),
708 sort(FileNumbers,SFileNumbers),
709 debug_println(9,filtering_properties(Names,SFileNumbers)),nl,
710 conjunction_to_list(InProperties,InAsList),
711 % exclude all properties who are in one of the files for which we have restored (all) constant values
712 exclude(belongs_to_file(SFileNumbers),InAsList,OutAsList), % this is exclude/3 from library(lists)
713 length(InAsList,InLen), length(OutAsList,OutLen), length(SFileNumbers,FLen),
714 formatgood('value caching: filtered PROPERTIES, keeping ~w out of ~w (#files=~w filtered).',[OutLen,InLen,FLen]),
715 conjunct_predicates(OutAsList,OutProperties).
716 belongs_to_file(SFileNumbers,TPred) :-
717 get_texpr_info(TPred,Info),
718 memberchk(nodeid(Pos),Info),
719 get_position_filenumber(Pos,FilePos),
720 ord_member(FilePos,SFileNumbers).
721 % TODO: check if this is a property coming from a machine included by a file in FileNumbers, without prefix for variables
722
723 %TODO: we could cache the computation or do a topological sorting to avoid re-computing filenumbers
724 get_subsidiary_loaded_filenumber(MachName,FileNr) :-
725 machine_file_number(MachName,FileNr).
726 get_subsidiary_loaded_filenumber(MachName,FileNr) :-
727 machine_references(MachName,References),
728 Prefix='', % we only consider machines included without prefix as loaded;
729 % TODO: for Constants it would be ok with Prefix
730 member(ref(_Type,IncludedMachName,Prefix),References),
731 get_subsidiary_loaded_filenumber(IncludedMachName,FileNr).
732
733
734 /**********************************************************************/
735
736 :- dynamic loadable_operation/5, loadable_short_operation/3,
737 storable_operation/5, stored_transition/4, operation_has_stored_transition/1,
738 operations_index_file_opened/2.
739 :- volatile loadable_operation/5, loadable_short_operation/3,
740 storable_operation/5, stored_transition/4, operation_has_stored_transition/1,
741 operations_index_file_opened/2.
742
743 initialise_value_persistance :-
744 clear_applicable_flag,
745 cache_is_applicable_for_transitions,
746 main_machine_name(MachName), % if we have no machine name, fail silently (for default machine)
747 !,
748 % check wich operations are cachable and prepare information for them
749 formatdetails('value caching: initialise_value_persistance for main machine ~w',[MachName]),
750 retract_all_op_cache_data,
751 precompute_machine_has_operations,
752 %initialise_op_cache_for_machine(MachName),
753 find_loadable_machines(LoadableMachines),
754 formatdetails('value caching: loadable machines: ~w',[LoadableMachines]),
755 maplist(initialise_op_cache_for_machine,LoadableMachines),
756 formatdetails('value caching: initialise_op_cache_for_machine for main machine ~w',[MachName]),
757 maplist(try_to_load_operations_cache,LoadableMachines),
758 maplist(refresh_op_cache_for_machine,LoadableMachines).
759 initialise_value_persistance :-
760 retract_all_op_cache_data.
761
762 initialise_op_cache_for_machine(loadable(_,MachName)) :-
763 bb_inc(value_persistance_op_cache_machines),
764 get_operation_names(MachName,OperationNames),
765 maplist(init_operation_cache(MachName),OperationNames).
766
767 refresh_op_cache_for_machine(loadable(_,MachName)) :-
768 (operations_index_file_fully_loaded(MachName)
769 -> formatgood('value caching: operations_index file needs no refresh for ~w',[MachName])
770 ; get_operation_names(MachName,OperationNames),
771 save_operations_index_cache(MachName,OperationNames)
772 ),
773 open_data_file(MachName).
774
775 :- use_module(eventhandling,[register_event_listener/3]).
776 :- register_event_listener(specification_initialised,initialise_value_persistance,
777 'Initialise operation and constants persistent caching info.').
778
779
780 retract_all_op_cache_data :-
781 retractall(machine_has_operations(_)),
782 retractall(constants_loaded_from_file(_)),
783 retractall(loadable_operation(_,_,_,_,_)),
784 retractall(loadable_short_operation(_,_,_)),
785 retractall(storable_operation(_,_,_,_,_)),
786 retractall(stored_transition(_,_,_,_)),
787 retractall(operation_has_stored_transition(_)),
788 retractall(cst_id_to_hash(_,_)),
789 retractall(constant_pre_hash_cache_active),
790 retractall(operations_index_file_fully_loaded(_)),
791 reset_operations_index_file_opened.
792
793 % operations_index_file_opened(Stream) contains a possibly open stream for the operations (probops) file
794 reset_operations_index_file_opened :-
795 ( operations_index_file_opened(M,S) ->
796 my_close(S),retractall(operations_index_file_opened(M,_)),
797 reset_operations_index_file_opened
798 ; true).
799
800 get_operation_names(MachName,OperationNames) :-
801 if(machine_operations(MachName,RawOperationNames),
802 maplist(get_raw_identifier,RawOperationNames,OperationNames),
803 add_error_fail(value_persistance,'Cannot find machine:',MachName)).
804
805
806 :- dynamic operations_index_file_fully_loaded/1.
807 :- volatile operations_index_file_fully_loaded/1.
808
809 try_to_load_operations_cache(loadable(Type,MachName)) :- \+ machine_has_operations(MachName),
810 \+ main_machine_name(MachName), !,
811 formatdetails('value caching: machine has no relevant operation ~w (~w)',[MachName,Type]).
812 try_to_load_operations_cache(loadable(Type,MachName)) :- % Type is sub or main
813 formatdetails('value caching: try loading cached operations for machine ~w (~w)',[MachName,Type]),
814 start_profile(MachName,T1),
815 get_operations_computation_parameters(MachName,OpParams),
816 (open_cache_file(operations_index,MachName,S,read_cached_operations(S,MachName,OpParams,Error,RequiresRefresh))
817 -> true
818 ; stop_profile(MachName,cache_load_operations_index_failed,unknown,T1), fail
819 ),
820 !,
821 ( Error==true ->
822 formatwarn('value caching: corrupted index file for machine ~w, deleting file.',[MachName]),
823 delete_operations_data_cache_file(MachName)
824 ; var(RequiresRefresh) -> assertz(operations_index_file_fully_loaded(MachName))
825 ; true
826 ),
827 stop_profile(MachName,cache_load_operations_index,unknown,T1).
828 try_to_load_operations_cache(loadable(main,MachName)) :-
829 % read_cached_operations has failed, due to general computations parameter change
830 % delete the data file if one exists -- do only for main machine
831 delete_operations_data_cache_file(MachName),!.
832 try_to_load_operations_cache(_Loadable).
833
834 delete_operations_data_cache_file(MachName) :-
835 cache_file_exists(operations_data,MachName,Filename),!,
836 formatinfo('value caching: deleting opdata cache file of machine ~w: ~w',[MachName,Filename]),
837 delete_file(Filename).
838
839 read_cached_operations(S,MachName,CurrentOpParams,SyntaxError,RequiresRefresh) :-
840 read_syntax_safe(S,StoredOpParams,SyntaxError),
841 ( SyntaxError==true -> true
842 ; compare_computation_parameters(CurrentOpParams,StoredOpParams,MachName) ->
843 formatgood('value caching: parameters unchanged, re-use of stored operations possible for ~w',[MachName]),
844 read_cached_operations2(S,MachName,SyntaxError,RequiresRefresh)
845 ;
846 formatwarn('value caching: general computations parameters have changed, no re-use of stored operations for ~w',[MachName]),
847 debug_params(CurrentOpParams,StoredOpParams),
848 fail).
849
850 read_cached_operations2(S,MachName,SyntaxError,RequiresRefresh) :-
851 read_syntax_safe(S,Term,SyntaxError),
852 ( SyntaxError==true -> true
853 ; Term == end_of_file -> true
854 ; read_cached_operations3(Term,MachName,RequiresRefresh) ->
855 read_cached_operations2(S,MachName,SyntaxError,RequiresRefresh)
856 ;
857 functor(Term,F,A),
858 formatwarn('value caching: unrecognised entry in cache file for ~w: ~w/~w~n',[MachName,F,A]),
859 fail
860 ).
861 debug_params(op_comp_parameters(A),op_comp_parameters(B)) :- !,
862 debug_params(A,B).
863 debug_params(preference(P,A),preference(P,B)) :- !,
864 (A=B -> true ; print(P), print(':'), print(A/B), print(' ')).
865 debug_params([],[]) :- !,nl.
866 debug_params([preference(P1,A)|CT],[preference(P2,B)|ST]) :- !,
867 (P1=P2 -> (A=B -> true ; format('Difference in preference ~w : current = ~w, stored = ~w~n',[P1,A,B])),
868 debug_params(CT,ST)
869 ; P1 @< P2 -> format('New preference ~w = ~w (not stored)~n',[P1,A]), debug_params(CT,[preference(P2,B)|ST])
870 ; format('Stored preference removed ~w = ~w~n',[P2,B]), debug_params([preference(P1,A)|CT],ST)
871 ).
872 debug_params([Cur|CT],[Stored|ST]) :- !,
873 debug_params(Cur,Stored),
874 debug_params(CT,ST).
875 debug_params(A,B) :- (A=B -> true ; print(A/B),nl).
876
877 % -----------------
878
879 % a way to clean/delete all cache files for current machine; useful for tests
880 delete_cache_files :-
881 reset_operations_index_file_opened, % to ensure we can delete the files
882 find_loadable_machines(LoadableMachines),
883 maplist(delete_cache_files_for_machine(_),LoadableMachines).
884
885 % delete a single cache file only
886 delete_cache_files_for_machine(Machine) :-
887 reset_operations_index_file_opened, % to ensure we can delete the files
888 delete_cache_files_for_machine(_,loadable(main,Machine)).
889
890 delete_cache_files_for_machine(ExpectedType,loadable(_MType,MachName0)) :-
891 (get_machine_file_number(MachName0,_Ext,_Nr,_) -> MachName=MachName0
892 ; get_modulename_filename(MachName0,MachName),
893 get_machine_file_number(MachName,_Ext,_Nr,_)
894 -> formatinfo('Trying to delete cache for machine: ~w',[MachName]) % TODO: check if file matches?
895 ; formatwarn('Cannot clear cache. Machine is unknown: ~w',[MachName0]),fail
896 ),
897 cache_file_suffix(Type,_), % backtracks and also matches operations_data
898 (var(ExpectedType) -> true ; ExpectedType=Type),
899 (cache_file_exists(Type,MachName,FullName)
900 -> get_tail_filename(FullName,FF),
901 format_with_colour_nl(user_output,[blue],'Deleting ~w cache file for machine ~w: ~w',[Type,MachName,FF]),
902 delete_file(FullName)
903 ; formatinfo('No need to clear cache, no file of type ~w exists for machine ~w',[Type,MachName])
904 ),fail.
905 delete_cache_files_for_machine(_,_).
906
907 :- meta_predicate show_cache_file_contents_for_machine(-,0,-,-).
908 % a small utility to print out the contents of the cache files:
909
910 % called by -show_cache and -show_cache_verbose commands of probcli
911 show_cache_file_contents(Verbose) :-
912 (storage_directory(Dir) -> true ; Dir='?'),
913 format_with_colour_nl(user_output,[blue],'Cache contents (directory: ~w)',[Dir]),
914 reset_operations_index_file_opened, % otherwise on Windows we are not able to open the probobs file as it is already open
915 show_cache_file_operations(Verbose),
916 (Verbose=verbose -> potray_loadable_operations ; true).
917
918
919 show_cache_file_operations(Verbose) :-
920 reset_operations_index_file_opened, % otherwise on Windows we are not able to open the probobs file as it is already open
921 find_loadable_machines(LoadableMachines),
922 maplist(show_cache_file_contents_for_machine(_,show_facts(S,Verbose),S),LoadableMachines).
923
924 cache_applicable(operations) :- !, cache_is_applicable_for_transitions.
925 cache_applicable(constants) :- !, cache_is_applicable_for_constants.
926 cache_applicable(_) :- cache_is_applicable.
927
928 :- use_module(tools,[get_tail_filename/2, get_modulename_filename/2]).
929 show_cache_file_contents_for_machine(ExpectedType,Call,Stream,loadable(_MType,MachName)) :-
930 cache_file_type(Type), % backtracks
931 (var(ExpectedType) -> true ; ExpectedType=Type),
932 (cache_file_exists(Type,MachName,FullName) ->
933 get_tail_filename(FullName,FF),
934 format_with_colour_nl(user_output,[blue],'Contents of ~w cache file for machine ~w: ~w',[Type,MachName,FF]),
935 (cache_applicable(Type) -> true
936 ; format_with_colour_nl(user_output,[blue],' (Cache not applicable for ~w)',[Type])),
937 (open_cache_file(Type,MachName,Stream,Call)
938 -> format_with_colour_nl(user_output,[blue],'End of contents (~w)',[FF]),
939 print_cache_file_props(Type,MachName)
940 ; format_with_colour_nl(user_error,[red],'Failed to show cache contents of ~w.',[FF])
941 )
942 ; \+ cache_applicable(Type) -> true
943 ; Type=operations_index, machine_operations(MachName,_)
944 -> format_with_colour_nl(user_output,[blue],'No cache file of type ~w for machine with operations ~w.~n',[Type,MachName])
945 ; Type=constants, machine_has_constants(MachName)
946 -> format_with_colour_nl(user_output,[blue],'No cache file of type ~w for machine with constants ~w.~n',[Type,MachName])
947 ; debug_format(19,'No cache file of type ~w for machine ~w.~n',[Type,MachName]) % TODO: check if MachName has constants,...
948 ),
949 fail.
950 show_cache_file_contents_for_machine(_,_,_,_).
951
952 show_facts(S,Verbose) :- read_syntax_safe(S,Term,Error),
953 (nonvar(Error)
954 -> add_error(value_persistance,'Error occured reading cache stream: ',S)
955 ; Term = end_of_file -> reset_and_show_stats % will print # of maximum_reached ...
956 ; portray_content_fact(Term,Verbose),
957 show_facts(S,Verbose)).
958
959 :- use_module(tools_strings,[get_hex_bytes/2]).
960 :- use_module(translate,[print_bstate/1]).
961 % operations:
962 portray_content_fact(op_comp_parameters(OpParams),Verbose) :- OpParams=[H,H2|_], !,
963 length(OpParams,Len),
964 format_verbose(Verbose,' op_comp_parameters([~w,~w,...]). % ~w parameters~n',[H,H2,Len]).
965 portray_content_fact(operation(OpName,Hash),_) :- get_hex_bytes(Hash,Hex), !,
966 format(' operation(~w,~s).~n',[OpName,Hex]).
967 portray_content_fact(trans_index(OpName,Hash,Index),Verbose) :- get_hex_bytes(Hash,Hex), !,
968 compute_short_hash(Hash,Short),
969 format_verbose(Verbose,' transition(~w,~s,~w). % short: ~w~n',[OpName,Hex,Index,Short]),
970 inc_counter(OpName).
971 % constants:
972 portray_content_fact(comp_parameters(OpParams),Verbose) :- OpParams=[H,H2|_], !,
973 length(OpParams,Len),
974 format_verbose(Verbose,' comp_parameters([~w,~w,...]). % ~w parameters~n~n',[H,H2,Len]).
975 portray_content_fact(maximum_reached(MaxReached),Verbose) :- !,
976 format_verbose(Verbose,' maximum_reached(~w).~n',[MaxReached]),
977 (MaxReached=true -> inc_counter(maximum_reached) ; true).
978 portray_content_fact(values(constants,Store),_Verbose) :- !,
979 unpack_constants(Store,UPS),
980 print_bstate(UPS),nl.
981 portray_content_fact(Term,_) :- write(' '),portray_clause(Term).
982
983 format_verbose(verbose,Str,A) :- !, format(Str,A).
984 format_verbose(_,_,_).
985
986
987 :- dynamic counter/2.
988 reset_and_show_stats :- retract(counter(C,Nr)), format(' # of ~w : ~w~n',[C,Nr]),fail.
989 reset_and_show_stats.
990
991 inc_counter(C) :- (retract(counter(C,Nr)) -> N1 is Nr+1 ; N1 = 1),
992 assertz(counter(C,N1)).
993
994 :- use_module(error_manager).
995 % variation of tools_fastread:fastrw_read/3
996 read_syntax_safe(S,Term,Error) :-
997 catch( fast_read(S,Term1), % from library fastrw
998 error(E,_),
999 ( E=syntax_error(_) -> Error = true
1000 ; E=permission_error(_,_,_) -> Term1 = end_of_file
1001 ; E=consistency_error(_,_,_)
1002 -> add_error(value_persistance,'Consistency error when reading from stream: ',E),
1003 Error = true
1004 ;
1005 add_error(value_persistance,'Unknown error when reading from stream: ',E),
1006 throw(E))
1007 ), %functor(Term1,F,N),write(read_syntax_safe(F/N,E)),nl,
1008 Term1 = Term.
1009
1010
1011 read_cached_operations3(operation(OpName,StoredHash),MachName,RequiresRefresh) :-
1012 ( operation_hash(MachName,OpName,CurrentHash) ->
1013 ( CurrentHash = StoredHash % we can reuse operation; the hash includes DEFINITIONS and called operations
1014 ->
1015 formatgood('value caching: operation ~w unchanged for ~w.',[OpName,MachName]),
1016 %TODO: what if the machine has parameters? Can we reuse the transitions from the generic machine?
1017 find_matching_operations(OpName,MachName,PrefixedOpNames), % find includes with renaming, ...
1018 ( PrefixedOpNames = [] ->
1019 formatwarn('value caching: no operation found for ~w in ~w', [OpName,MachName]),
1020 RequiresRefresh=refresh
1021 ;
1022 (PrefixedOpNames = [OpName] -> true
1023 ; formatinfo('value caching: (composed/renamed) operations ~w found for ~w in ~w',
1024 [PrefixedOpNames,OpName,MachName])
1025 ),
1026 maplist(store_loadable_operation(OpName,MachName),PrefixedOpNames) )
1027 ;
1028 get_hex_bytes(CurrentHash,CH), get_hex_bytes(StoredHash,SH),
1029 formatwarn('value caching: operation ~w has changed (~s, was ~s).',[OpName, CH, SH]),
1030 RequiresRefresh=refresh
1031 )
1032 ;
1033 formatwarn('value caching: unrecognised operation ~w.',[OpName]),
1034 RequiresRefresh=refresh
1035 ).
1036 read_cached_operations3(trans_index(OpName,Hash,Index),MachName,RequiresRefresh) :-
1037 ( loadable_short_operation(OpName,MachName,_FullOpNameInCurMachine) ->
1038 compute_short_hash(Hash,Short),
1039 debuggood('value caching: loading stored_transition ~w (index ~w) : hash ~w',[OpName,Index,Short]),
1040 assert_stored_transition(Short,Hash,OpName,Index)
1041 ;
1042 % ignore
1043 % potray_loadable_operations,
1044 formatwarn('value caching: ignoring stored_transition ~w (index ~w) for ~w',[OpName,Index,MachName]),
1045 RequiresRefresh=refresh
1046 ).
1047
1048 store_loadable_operation(_OpName,_MachName,PrefixedOpName) :-
1049 loadable_operation(PrefixedOpName,_,_,_,_),!.
1050 store_loadable_operation(OpName,MachName,PrefixedOpName) :-
1051 (find_operation_input_output(PrefixedOpName,Input,Output) ->
1052 create_output_pattern(Output,OutputPattern),
1053 check_output_pattern_order(OpName,PrefixedOpName,OutputPattern),
1054 compute_lookup_pattern(OpName,PrefixedOpName,Input,InputPattern)),
1055 !,
1056 bb_inc(value_persistance_loadable_operations),
1057 formatinfo('value caching: register loadable_operation from cache: ~w in ~w',[OpName,MachName]),
1058 assertz( loadable_operation(PrefixedOpName,OpName,MachName,InputPattern,OutputPattern)),
1059 assertz( loadable_short_operation(OpName,MachName,PrefixedOpName)). % assert with short name as index
1060 store_loadable_operation(_OpName,_MachName,PrefixedOpName) :-
1061 formatwarn('value caching: unable to generate input/output pattern for operation ~w.',
1062 [PrefixedOpName]).
1063
1064 find_matching_operations(Operation,MachName,FullNames) :-
1065 findall(O, is_matching_operation(Operation,MachName,O), FullNames).
1066
1067 is_matching_operation(Operation,MachName,Operation) :-
1068 % there is an operation with exactly the same name
1069 operation_with_machine(Operation,MachName).
1070 is_matching_operation(Operation,MachName,FullName) :-
1071 atom_concat('.',Operation,DottedOp),
1072 operation_with_machine(FullName,MachName),
1073 atom_concat(_,DottedOp,FullName).
1074
1075 operation_with_machine(OpName,MachName) :-
1076 get_operation_info(OpName,Infos),
1077 ( memberchk(origin(Origin),Infos), % for c1.Inc the origin will be the included machine
1078 last(Origin,_/MachName) -> true
1079 ; main_machine_name(MachName)
1080 ).
1081
1082 :- dynamic machine_has_operations/1.
1083 :- volatile machine_has_operations/1.
1084 precompute_machine_has_operations :-
1085 operation_with_machine(_Op,MachName), % this will be used to check if an operation from MachName should be loaded
1086 \+ machine_has_operations(MachName),
1087 assert(machine_has_operations(MachName)), % register that it is useful to load cached operations from MachName
1088 fail.
1089 precompute_machine_has_operations.
1090
1091 create_output_pattern(Ids,Values/Bindings) :-
1092 maplist(create_output_pattern2,Ids,Values,Bindings).
1093 create_output_pattern2(Id,Value,bind(Id,Value)).
1094
1095 init_operation_cache(MachName,FullOpName) :-
1096 storable_operation(FullOpName,MachName,_,_InputPattern,_Output),!. % ignore, already registered
1097 init_operation_cache(MachName,OpName) :-
1098 b_top_level_operation(OpName),!,
1099 init_operation_cache2(MachName,OpName,OpName).
1100 init_operation_cache(MachName,OpName) :-
1101 used_machine_not_included_anywhere(MachName,'',_Kind), % we do not support prefixes other than '' at the moment
1102 ajoin([MachName,'.',OpName],FullOpName), % we create_dummy_machine has created a main machine which INCLUDES MachName with prefix MachName
1103 b_top_level_operation(FullOpName),!,
1104 init_operation_cache2(MachName,OpName,FullOpName).
1105 init_operation_cache(MachName,OpName) :-
1106 % only store top-level operations for which we compute all solutions at the moment
1107 % for subsidiary operations we do a call by predicate; much more difficult to cache/store
1108 (used_machine_not_included_anywhere(MachName,Prefix,Kind)
1109 -> formatinfo('value caching: Not storing operation ~w from ~w machine (prefix ~w) ~w which is not INCLUDED',[OpName,Kind,Prefix,MachName])
1110 ; formatinfo('value caching: Not storing subsidiary operation ~w in machine ~w',[OpName,MachName])
1111 ).
1112
1113 init_operation_cache2(MachName,OpName,FullOpName) :-
1114 ( find_operation_input_output(FullOpName,Input,Output) ->
1115 compute_lookup_pattern(OpName,FullOpName, Input,InputPattern),
1116 debug_println(9,find_operation_input_output(FullOpName,Input,Output)),
1117 formatdetails('value caching: storable operation ~w in machine ~w',[FullOpName,MachName]),
1118 bb_inc(value_persistance_storeable_operations),
1119 assertz( storable_operation(FullOpName,MachName,OpName,InputPattern,Output) ),
1120 % Operations of the main machine are never prefixed
1121 create_output_pattern(Output,OutputPattern),
1122 check_output_pattern_order(OpName,FullOpName,OutputPattern),
1123 (loadable_operation(FullOpName,_,_,_,_) -> true
1124 ; bb_inc(value_persistance_loadable_operations), % see also store_loadable_operation
1125 assertz( loadable_operation(FullOpName,OpName,MachName,InputPattern,OutputPattern) ),
1126 assertz( loadable_short_operation(OpName,MachName,FullOpName))
1127 )
1128 ;
1129 formatwarn('value caching: unable to generate input/output pattern for operation ~w.~n',[FullOpName])
1130 ).
1131
1132
1133 get_operations_computation_parameters(Name,op_comp_parameters(OpParams)) :-
1134 find_relevant_preferences(Prefs),
1135 get_revision_info(Revision),
1136 OpParams = [machine_name(Name),prob_revision(Revision)|Prefs].
1137
1138 % called from b_interpreter inside b_execute_operation2
1139 lookup_cached_transitions(PrefixedOpName,InState,Info,OutputBindings) :- %write(lookup(PrefixedOpName)),nl,
1140 cache_is_applicable_for_transitions,
1141 (loadable_operation(PrefixedOpName,OpName,MachName,InputPattern,OutputPattern)
1142 -> true
1143 % PrefixedOpName can be something like MachName.OpName, and is a local name
1144 % in the cache file we store the short OpName without prefix
1145 ; debugwarn('value caching: no loadable_operation found for: ~w',[PrefixedOpName]),
1146 fail
1147 ),
1148 (operation_has_stored_transition(OpName)
1149 -> true
1150 ; % otherwise no use in computing hash here
1151 debugwarn('value caching: no transition stored for: ~w',[OpName]),
1152 fail
1153 ),
1154 sort(InState,SortedInState),
1155 % tools:start_ms_timer(T1),
1156 hash_input_values(OpName,MachName,InputPattern,SortedInState,ShortHash,InputHash),
1157 % tools:stop_ms_walltimer_with_msg(T1,hash_input(OpName)),
1158 formatdetails('value caching: computed hash for looking up ~w : ~w',[PrefixedOpName,ShortHash]),
1159 %portray_stored_transitions,
1160 (stored_transition(ShortHash,InputHash,OpName,Index) -> true
1161 ; debugwarn('value caching: no cached operation transition found ~w (~w -> ...).',[PrefixedOpName,ShortHash]),
1162 fail
1163 ), !,
1164 formatgood('value caching: re-using values for operation ~w (~w -> ~w).',[OpName,ShortHash,Index]),
1165 %formatdetails('value caching: state=~w',[SortedInState]),
1166 start_profile(OpName,T1),
1167 ( load_solutions(MachName,Index,Info,Solutions) -> true
1168 %length(Solutions,Sn),
1169 %format('value caching: re-using ~w solutions for operation ~w of machine ~w.~n',
1170 % [Sn,OpName,MachName])
1171 ;
1172 formatwarn('value caching: re-using values for operation ~w failed.',[OpName]),
1173 fail),
1174 %print(solution(Solutions)),nl, print(pattern(OutputPattern)),nl,
1175 % OutputPattern is of the form [V1,...]/[bind(v1,V1),...]
1176 bb_inc(value_persistance_reused_transitions),
1177 maplist(create_output_binding(OutputPattern),Solutions,OutputBindings),
1178 %tools:stop_ms_walltimer_with_msg(T1,time_caching(OpName)),
1179 stop_profile(OpName,cache_load_transitions,unknown,T1).
1180 load_solutions(MachName,Index,Info,Solutions) :-
1181 storage_file_name(operations_data,MachName,FilenameData),
1182 catch(load_sol_aux(FilenameData,Index,ReadSolutions),
1183 Exc,
1184 (ajoin(['Corrupt opdata cache file for ',MachName,':'],Msg),
1185 add_error_fail(value_persistance,Msg,Exc))
1186 ),
1187 (ReadSolutions = trans_info(II,S) -> Solutions=S, Info=II ; Solutions=ReadSolutions, Info=[]).
1188 load_sol_aux(FilenameData,Index,Solutions) :-
1189 open(FilenameData,read,S,[type(binary),reposition(true)]),
1190 call_cleanup((seek(S,Index,bof,_),
1191 fast_read(S,Solutions)), % read list of transition terms
1192 my_close(S)).
1193
1194 create_output_binding(Pattern,trans_cached(Param,Result,New,Info),
1195 trans_cached(Param,Result,Update,Info)) :-
1196 copy_term(Pattern,New/Update).
1197
1198 % check that it is ok not to remove machine prefix in output pattern
1199 % in the cache file we only store the updated values, not the names of the updated variables
1200 check_output_pattern_order(OpName,PrefixOpName,_/OutputBinds) :-
1201 strip_machine_prefix(OpName,PrefixOpName,OutputBinds,StrippedOutput),
1202 sort(StrippedOutput,SIds),
1203 (SIds = StrippedOutput -> true
1204 ; add_internal_error('Output order influenced by machine prefix:',OpName-(OutputBinds,SIds))).
1205
1206 % select updated variables for a cached operation:
1207 select_output_values(Ids,State,Values) :- sort(State,SState),
1208 select_values_sorted(Ids,SState,Values).
1209 % Note: as Identifiers are sorted then we can avoid quadratic complexity here and scan State only once for all Ids
1210 % Note: we do not need to strip_machine_prefix and sort according to local order, as all ids should be prefixed with the same prefix, see check_output_order
1211 select_values_sorted(Ids,SState,Values) :-
1212 (select_values_sorted_aux(Ids,SState,Values) -> true
1213 ; print('Lookup: '),print(Ids), nl,
1214 print('State: '), maplist(get_bind_id,SState,SIds), print(SIds),nl,fail).
1215 select_values_sorted_aux([],_,[]).
1216 select_values_sorted_aux([Id|Irest],State,[Value|Vrest]) :-
1217 ( ordered_select(State,Id,StoredValue,RestState) ->
1218 Value=StoredValue,
1219 select_values_sorted_aux(Irest,RestState,Vrest)
1220 ;
1221 ajoin(['Looking up "', Id, '" failed.'], Msg),
1222 (maplist(get_bind_id,State,Ids) -> true ; Ids = '<<cannot print state ids>>'),
1223 add_error(value_persistance,Msg,Ids),
1224 fail
1225 ).
1226 ordered_select([bind(Id,Value)|T],Id,Value,T).
1227 ordered_select([_|T],Id,Value,R) :- ordered_select(T,Id,Value,R).
1228 get_bind_id(bind(Id,_),Id).
1229
1230 % compute a hash of all the values a given operation OpName depends upon
1231 % we have a Short hash used for indexing and the full SHA hash for checking if the value is really identical
1232 % we could perform a separate ShortHash based on term_hash with depth-bound; to check if it makes sense to compute the full hash to lookup a transition (which can be expensive)
1233 hash_input_values(OpName,MachName,InputPattern,SortedInState,Short,Long) :-
1234 % tools:start_ms_timer(T1),
1235 ( hash_input_values2(OpName,MachName,InputPattern,SortedInState,Short,Long) ->
1236 true %,write(hash(Short,OpName,MachName,InputPattern,SortedInState)),nl
1237 %,tools:stop_ms_walltimer_with_msg(T1,time_hashing(OpName,MachName))
1238 % tools:print_memory_used_wo_gc,
1239 ; add_failed_call_error(hash_input_values2(OpName,MachName,InputPattern,SortedInState,Short,Long)),
1240 fail).
1241
1242 hash_input_values2(OpName,MachName,InputPattern,SortedInState,Short,Long) :-
1243 start_profile(OpName,T1),
1244 copy_term(InputPattern,lookup_pattern(SortedVars,SortedInputValues,InputVarsAndCsts,InputValues)),
1245 select_values_sorted(SortedVars,SortedInState,SortedInputValues),
1246 l_pre_hash(InputVarsAndCsts,InputValues,PreHashedInputValues),
1247 % terms:term_size(PreHashedInputValues,Sz), write(hash(OpName,SortedVars,Sz)),nl,
1248 % tools_printing:print_term_summary(raw_sha_hash(OpName,PreHashedInputValues)),nl,
1249 raw_sha_hash(OpName/MachName/PreHashedInputValues,Long),
1250 compute_short_hash(Long,Short),
1251 stop_profile(OpName,cache_hashing_input_values,unknown,T1).
1252 compute_short_hash([A,B,C,D|_],Short) :-
1253 Short is A<<24 + B<<16 + C<<8 + D.
1254
1255 % pre-hash certain potentially large values, so that we do not have to send them to raw_sha_hash every time
1256 l_pre_hash([],[],[]).
1257 l_pre_hash([ID|T],[Val|TV],[PreHashVal|PT]) :-
1258 pre_hash(Val,ID,PreHashVal),
1259 l_pre_hash(T,TV,PT).
1260
1261 :- dynamic constant_pre_hash_cache_active/0.
1262
1263 :- dynamic cst_id_to_hash/2.
1264 :- use_module(probsrc(memoization),[is_memoization_closure/2]).
1265 %pre_hash(Val,_ID,PreHashVal) :-
1266 % is_memoization_closure(Val,MemoID),!, %write(memo(ID,MemoID)),nl,
1267 % PreHashVal = '$MEMO'(MemoID).
1268 pre_hash(Val,CstId,PreHashVal) :-
1269 bmachine:b_is_constant(CstId), % TODO: also use for variables, whose values stay constant
1270 value_can_be_large(Val),!,
1271 (constant_pre_hash_cache_active
1272 -> (cst_id_to_hash(CstId,StoredHash)
1273 -> PreHashVal = StoredHash % used stored value
1274 ; pack_and_compute_sha_hash(Val,Hash),
1275 assert(cst_id_to_hash(CstId,Hash)),
1276 debug_println(4,storing_cache_hash_for_constant(CstId)),
1277 PreHashVal = Hash
1278 )
1279 ; pack_and_compute_sha_hash(Val,PreHashVal) % there can be multiple constant values active
1280 ).
1281 pre_hash(V,_,V).
1282
1283 pack_and_compute_sha_hash(Val,PreHashVal) :-
1284 temporary_set_preference(use_state_packing,false,Change),
1285 pack_value(Val,PackedVal), % to be consistent with write_bind, which packs/unpacks values (and changes interval rep)
1286 reset_temporary_preference(use_state_packing,Change),
1287 raw_sha_hash(PackedVal,PreHashVal).
1288
1289 :- use_module(probsrc(avl_tools),[avl_height_less_than/2]).
1290 value_can_be_large(avl_set(A)) :- \+ avl_height_less_than(A,7).
1291 value_can_be_large(closure(_,_,_)).
1292 value_can_be_large((A,B)) :- value_can_be_large(A) -> true ; value_can_be_large(B).
1293 value_can_be_large(rec(Fields)) :- (member(field(_,V),Fields), value_can_be_large(V) -> true).
1294
1295 % TODO: separate constants from variables? or do full incremental hashing; by hashing all large sets first
1296 % we also could detect memoization closures and just store their id? or pre-compute their hash and store it?
1297
1298 % save operations_index (.probops) file for machine
1299 % contains operation/2 facts and trans_index/3 facts
1300 save_operations_index_cache(MachName,OperationNames) :-
1301 get_operations_computation_parameters(MachName,OpParams),
1302 storage_file_name(operations_index,MachName,FileName),
1303 formatdetails('Saving operations index cache: ~w',MachName),
1304 my_open(FileName,write,S,[type(binary)]),
1305 fast_write(S,OpParams),
1306 get_tail_filename(FileName,TailFileName),
1307 save_operation_info(S,TailFileName,MachName,OperationNames),
1308 my_close(S).
1309
1310 % is this really required: it seems to re-write already loaded information to the file; at least in some cases
1311 save_operation_info(S,FileName,MachName,OperationNames) :-
1312 member(FullOpName,OperationNames),
1313 storable_operation(FullOpName,MachName,OpName,_InputPattern,_Output),
1314 operation_hash(MachName,OpName,OpHash),
1315 fast_write(S,operation(OpName,OpHash)),
1316 stored_transition(Short,InputHash,FullOpName,Index),
1317 formatinfo('value caching: storing/refreshing transition for ~w (index ~w, hash ~w) from ~w to ~w',[OpName,Index,Short,MachName,FileName]),
1318 fast_write(S,trans_index(OpName,InputHash,Index)),
1319 bb_inc(value_persistance_refresh_transitions),
1320 fail.
1321 save_operation_info(_S,_F,_MachName,_OperationNames).
1322
1323 find_operation_input_output(OpName,Input,Output) :-
1324 get_operation_info(OpName,Info),
1325 memberchk(reads(Input1),Info),
1326 % remove references to global sets and their elements
1327 get_all_sets_and_enum_ids(SEIds),
1328 remove_all(Input1,SEIds,Input),
1329 memberchk(modifies(Output),Info).
1330
1331 get_all_sets_and_enum_ids(SEIds) :-
1332 findall(Id,
1333 ( is_b_global_constant(_,_,Id)
1334 ; b_global_set(Id)),
1335 SEIds).
1336
1337
1338
1339 % add new transitions from the state space
1340 add_new_transitions_to_cache(InStateId) :-
1341 cache_is_applicable_for_transitions,
1342 visited_expression(InStateId,InState1),
1343 state_corresponds_to_initialised_b_machine(InState1,InState),
1344 !,
1345 add_new_transitions_to_cache1(InStateId,InState).
1346 add_new_transitions_to_cache(_InStateId).
1347
1348 get_all_max_reached_operations(MaxReachedList) :-
1349 findall(OpName, max_reached(OpName), MaxReached),
1350 sort(MaxReached,MaxReachedList).
1351
1352 :- use_module(library(ordsets),[ord_member/2]).
1353
1354 add_new_transitions_to_cache1(InStateId,InState) :-
1355 get_all_max_reached_operations(MaxReachedList),
1356 %main_machine_name(MachName),
1357 storable_operation(FullOpName,MachName,OpName,InputPattern,Output),
1358 \+ time_out_for_node(InStateId,FullOpName,_),
1359 (ord_member(FullOpName,MaxReachedList) -> MaxReached=true ; MaxReached=false),
1360 sort(InState,SortedInState),
1361 add_new_transitions_to_cache2(InStateId,SortedInState,OpName,InputPattern,Output,MaxReached,MachName),
1362 fail.
1363 add_new_transitions_to_cache1(_InStateId,_) :-
1364 flush_data.
1365
1366 add_new_transitions_to_cache2(InStateId,SortedInState,OpName,InputPattern,Output,MaxReached,MachName) :-
1367 hash_input_values(OpName,MachName,InputPattern,SortedInState,ShortHash,InputHash),
1368 \+ stored_transition(ShortHash,InputHash,_,_), % Is there already a cache entry?
1369 find_all_transitions(InStateId,OpName,Output,Transitions),
1370 length(Transitions,Length),
1371 %format('value caching: storing ~w transitions for operation ~w in state ~w.~n',
1372 % [Length,OpName,InStateId]),
1373 %print(Transitions),nl,
1374 statistics(runtime,[Start,_]),
1375 statistics(walltime,[WStart,_]),
1376 store_transition_into_cache(OpName,ShortHash,InputHash,Transitions,MaxReached,MachName),
1377 statistics(runtime,[Stop,_]), Time is Stop - Start,
1378 statistics(walltime,[WStop,_]), WTime is WStop - WStart,
1379 %%tools:print_memory_used_wo_gc,
1380 bb_inc_by(value_persistance_stored_transitions,Length),
1381 formatdetails('value caching: storing ~w transitions for operation ~w in state ~w (~w, state ~w, [~w] ms).',
1382 [Length,OpName,InStateId,ShortHash,Time,WTime]).
1383
1384 store_transition_into_cache(OpName,ShortHash,Hash,Transitions,MaxReached,MachName) :-
1385 start_profile(OpName,T1),
1386 storage_file_name(operations_data,MachName,FilenameData),
1387 my_open(FilenameData,read,SR,[type(binary),reposition(true)]),
1388 call_cleanup(seek(SR,0,eof,Index), % gets the end of file
1389 my_close(SR)),
1390 formatdetails('storing ~w (~w), EOF index is ~w for file ~w',[OpName,ShortHash,Index,FilenameData]),
1391 my_open(FilenameData,append,SW,[type(binary)]),
1392 (MaxReached = true -> Term = trans_info([max_reached],Transitions) ; Term=Transitions),
1393 call_cleanup(fast_write(SW,Term),
1394 my_close(SW)),
1395 assert_stored_transition(ShortHash,Hash,OpName,Index),
1396 get_operations_index_file(MachName,OI),
1397 formatdetails('storing ~w index ~w in file ~w',[OpName,Index,OI]),
1398 fast_write(OI,trans_index(OpName,Hash,Index)),
1399 stop_profile(OpName,cache_store_transitions,unknown,T1).
1400
1401 assert_stored_transition(ShortHash,Hash,OpName,Index) :-
1402 store_operation_has_stored_transition(OpName),
1403 assertz( stored_transition(ShortHash,Hash,OpName,Index) ).
1404
1405 portray_stored_transitions :-
1406 format(' - ~w : ~w (~w) --> ~w~n',['Operation','ShortHash','Hash','Index']),
1407 stored_transition(ShortHash,Hash,OpName,Index),
1408 format(' - ~w : ~w (~w) --> ~w~n',[OpName,ShortHash,Hash,Index]),
1409 fail.
1410 portray_stored_transitions.
1411
1412 store_operation_has_stored_transition(X) :- operation_has_stored_transition(X),!.
1413 store_operation_has_stored_transition(X) :- formatdetails('Operation now has a stored transition: ~w',[X]),
1414 assertz( operation_has_stored_transition(X) ).
1415
1416 find_all_transitions(InStateId,OpName,Output,Transitions) :-
1417 findall( trans_cached(ParaValues,ResultValues,Updates,TransInfo),
1418 find_transition(InStateId,OpName,Output,ParaValues,ResultValues,Updates,TransInfo),
1419 Transitions).
1420 find_transition(InStateId,OpName,Output,ParaValues,ResultValues,Updates,TransInfo) :-
1421 transition(InStateId,Operation,TransId,OutStateId),
1422 operation_name(Operation,OpName,ParaValues,ResultValues),
1423 findall(TI,transition_info(TransId,TI),TransInfo),
1424 visited_expression(OutStateId,OutState1), % we could use packed_visited_expression here ?
1425 state_corresponds_to_initialised_b_machine(OutState1,OutState),
1426 select_output_values(Output,OutState,Updates).
1427 operation_name('-->'(Operation,ResultValues),OpName,ParaValues,ResultValues) :-
1428 !,operation_name(Operation,OpName,ParaValues,_).
1429 operation_name(Operation,OpName,ParaValues,[]) :-
1430 Operation =.. [OpName|ParaValues].
1431
1432
1433 % add a single new transition, found by execute by predicate or -execute
1434 add_new_transition_to_cache_from_expanded_state(root,_OpTerm,OutState,_CacheInfo) :-
1435 OutState = concrete_constants(_),!,
1436 save_constants_from_expanded_state(OutState).
1437 add_new_transition_to_cache_from_expanded_state(InState,OpTerm,OutState,CacheInfo) :-
1438 cache_is_applicable_for_transitions,
1439 %main_machine_name(MachName), % removed check: we also want to store operations for INCLUDED/EXTENDED machines
1440 operation_name(OpTerm,FullOpName,ParaValues,ResultValues),
1441 (storable_operation(FullOpName,MachName,OpName,InputPattern,Output)
1442 -> true
1443 ; bb_inc(value_persistance_not_storeable_transitions),
1444 formatwarn('value caching: cannot store (subsidiary or SEEN?) operation ~w',[FullOpName]),
1445 fail),
1446 \+ new_transition_not_worthwhile(CacheInfo,FullOpName),
1447 sort(InState,SortedInState),
1448 hash_input_values(OpName,MachName,InputPattern,SortedInState,ShortHash,InputHash), % can be expensive
1449 formatdetails('value caching: computed hash for storing ~w : ~w',[FullOpName,ShortHash]),
1450 (stored_transition(ShortHash,InputHash,_,_) % Is there already a cache entry?
1451 -> debugwarn('value caching: transition already exists for ~w : ~w',[FullOpName,ShortHash]),
1452 % should normally not happen: if so we would have used the cache!
1453 fail
1454 ; true),
1455 !,
1456 MaxReached=true, % TODO: should we check that MAX_OPERATIONS is set to exactly 1
1457 TransInfo = [], % TODO: preference(eventtrace,true), preference(store_event_transinfo,true)
1458 Transitions = [trans_cached(ParaValues,ResultValues,Updates,TransInfo)],
1459 select_output_values(Output,OutState,Updates),
1460 bb_inc(value_persistance_stored_transitions),
1461 %formatinfo('value caching: storing ~w (~w) --> ~w and updates ~w for ~w', [OpName,ParaValues,ResultValues,Updates,Output]),
1462 store_transition_into_cache(OpName,ShortHash,InputHash,Transitions,MaxReached,MachName),
1463 formatinfo('value caching: storing single transition for operation ~w in execute mode (~w).', [FullOpName,ShortHash]),
1464 flush_data.
1465 add_new_transition_to_cache_from_expanded_state(_,_,_,_).
1466
1467 % add_deadlock_to_cache_from_expanded_state(InState,OpName) % TODO: worthwhile to store failure info
1468
1469 start_cache_execute_modus(cache_info(StartRuntime,Counter)) :-
1470 statistics(runtime,[StartRuntime,_]),
1471 bb_safe_get(value_persistance_reused_transitions,Counter),
1472 (constant_pre_hash_cache_active -> true
1473 ; assert(constant_pre_hash_cache_active)).
1474
1475 stop_cache_execute_modus :-
1476 retractall(constant_pre_hash_cache_active).
1477
1478 new_transition_not_worthwhile(cache_info(_,OldCounter),OpName) :-
1479 bb_safe_get(value_persistance_reused_transitions,Counter),
1480 Counter > OldCounter, % we cannot reuse subsidiary operation calls, so this must be the main operation itself
1481 formatdetails('value caching: not storing transition for ~w; it has been extracted from cache itself',[OpName]).
1482 new_transition_not_worthwhile(cache_info(StartRuntime,_),OpName) :-
1483 get_preference(cache_operations_runtime_limit,Limit), Limit>0,
1484 statistics(runtime,[CurrentTime,_]), Delta is CurrentTime-StartRuntime,
1485 Delta < Limit,
1486 bb_inc(value_persistance_not_worth_transitions),
1487 formatinfo('value caching: not storing transition for ~w, runtime (ms) ~w smaller than limit ~w',
1488 [OpName,Delta,Limit]).
1489
1490
1491
1492 % --------------------
1493
1494 open_data_file(Name) :-
1495 storage_file_name(operations_index,Name,FilenameIndex),
1496 my_open(FilenameIndex,append,FI,[type(binary)]),
1497 assertz(operations_index_file_opened(Name,FI)),
1498 storage_file_name(operations_data,Name,FilenameData),
1499 ( file_exists(FilenameData)
1500 -> true
1501 ; formatinfo('value caching: creating data file ~w',[FilenameData]),
1502 my_open(FilenameData,write,SD,[type(binary)]),
1503 my_close(SD)).
1504
1505 flush_data :-
1506 findall(M,(operations_index_file_opened(M,S),flush_output(S)),L),
1507 (L=[] -> add_internal_error('No index file opened',flush_data) ; true).
1508
1509 get_operations_index_file(Machine,OI) :-
1510 operations_index_file_opened(Machine,F),!,OI=F.
1511 get_operations_index_file(Machine,OI) :-
1512 add_internal_error('No operations_index file opened',get_operations_index_file(Machine,OI)),fail.
1513
1514 find_loadable_machines(Solutions) :-
1515 findall(loadable(Type,Machine),
1516 find_loadable_machines2(Type,Machine),
1517 Solutions).
1518 find_loadable_machines2(Type,Machine) :-
1519 main_machine_name(Main),
1520 find_loadable_machines_sees_includes(Main,Type,Machine).
1521 find_loadable_machines_sees_includes(Main,main,Main).
1522 find_loadable_machines_sees_includes(Main,sub,Seen) :-
1523 machine_sees(Main,_Prefix,Seen).
1524 find_loadable_machines_sees_includes(Start,sub,Included) :-
1525 find_loadable_machines_includes(Start,Included).
1526 find_loadable_machines_includes(Start,Included) :-
1527 machine_includes(Start,_Prefix1,M),
1528 ( Included=M
1529 ; find_loadable_machines_includes(M,Included)).
1530
1531 machine_sees(MachA,Prefix,MachB) :-
1532 machine_references(MachA,References),
1533 member(ref(Type,MachB,Prefix),References), % sees without a prefix
1534 memberchk(Type,[sees,uses]).
1535 machine_includes(MachA,Prefix,MachB) :-
1536 machine_references(MachA,References),
1537 member(ref(Type,MachB,Prefix),References),
1538 memberchk(Type,[includes,extends]).
1539
1540 % lookup_pattern/2:
1541 % To enable a fast lookup of values in a state, we have to sort the variables.
1542 % In the past we did not sort the variables when storing them,
1543 % Now we also sort the variables when storing, but according to the local machine order (to enable to re-use SEEN/USED machines with different prefixes)
1544 % This predicate generates a "lookup_pattern" that sorts the variables and their
1545 % corresponding values and enables to map the sorted values to the original order
1546 % by a simple unification.
1547 compute_lookup_pattern(OpName,PrefixOpName,
1548 Variables,lookup_pattern(SortedVariables,SortedValues,VariablesToHash,ValuesToHash)) :-
1549 maplist(variable_value_pair,Variables,VarValues,_),
1550 sort(VarValues,SortedVarValues),
1551 maplist(variable_value_pair,SortedVariables,SortedVarValues,SortedValues),
1552 strip_machine_prefix(OpName,PrefixOpName,VarValues,StrippedVarValues),
1553 sort(StrippedVarValues,SVV),
1554 maplist(variable_value_pair,VariablesToHash,SVV,ValuesToHash),
1555 (append(_,[V,V|_],VariablesToHash) -> add_internal_error('Duplicate id to hash:',OpName:V) ; true).
1556
1557 variable_value_pair(I,I/V,V).
1558
1559 strip_machine_prefix(OpName,OpName,Vars,Res) :- !, Res=Vars.
1560 strip_machine_prefix(OpName,PrefixOpName,VarValues,Res) :-
1561 atom_concat(Prefix,OpName,PrefixOpName),!,
1562 % strip MachName. prefix from variables, so that we sort and store the correct local names
1563 % this is relevant if another main machine uses the cache for this same subsidiary machine
1564 maplist(strip_machine_prefix_aux(Prefix),VarValues,Res).
1565 strip_machine_prefix(OpName,PrefixOpName,Vars,Vars) :-
1566 formatwarn('value caching: cannot strip prefix for operation ~w (to ~w), leading to sub-optimal cache re-use',[PrefixOpName,OpName]).
1567
1568 strip_machine_prefix_aux(Prefix,Bind,NewBind) :-
1569 get_bind(Bind,NewBind,VarName,NewVarName),
1570 (atom_concat(Prefix,Suffix,VarName) -> NewVarName = Suffix ; NewVarName = VarName).
1571 get_bind(VarName/V,NewVarName/V,VarName,NewVarName).
1572 get_bind(bind(VarName,V),bind(NewVarName,V),VarName,NewVarName).
1573
1574 % utility for statistics:
1575
1576 reset_counters :-
1577 ? (is_counter(C), bb_reset(C), fail ; true).
1578
1579 print_value_persistance_stats :-
1580 format_with_colour_nl(user_output,[blue],'ProB Value Persistance Caching Statistics',[]),
1581 (storage_directory(Dir)
1582 -> format_with_colour_nl(user_output,[blue],'* Cache storage directory: ~w',[Dir])
1583 ; format_with_colour_nl(user_output,[orange],'* No cache storage directory set!',[])
1584 ),
1585 format_with_colour_nl(user_output,[blue],'ProB Value Persistance Caching Statistics',[]),
1586 bb_safe_get(value_persistance_constants_loaded,C),
1587 format_with_colour_nl(user_output,[blue],' * Nr. of main machines for which stored constants were reused: ~w',[C]),
1588 bb_safe_get(value_persistance_ref_constants_loaded,RC),
1589 format_with_colour_nl(user_output,[blue],' * Nr. of referenced machines for which stored constants were reused: ~w',[RC]),
1590 bb_safe_get(value_persistance_stored_constants,StCs),
1591 format_with_colour_nl(user_output,[blue],' * Nr. of solutions for constants stored: ~w',[StCs]),
1592 bb_safe_get(value_persistance_op_cache_machines,OCM),
1593 format_with_colour_nl(user_output,[blue],' * Nr. of machines for which operations can be reused/stored: ~w',[OCM]),
1594 bb_safe_get(value_persistance_loadable_operations,LO),
1595 format_with_colour_nl(user_output,[blue],' * Nr. of stored operations which can be re-used: ~w',[LO]),
1596 bb_safe_get(value_persistance_storeable_operations,SO),
1597 format_with_colour_nl(user_output,[blue],' * Nr. of operations which can be stored: ~w',[SO]),
1598 bb_safe_get(value_persistance_reused_transitions,R),
1599 format_with_colour_nl(user_output,[blue],' * Nr. of stored operation calls reused: ~w',[R]),
1600 bb_safe_get(value_persistance_stored_transitions,S),
1601 format_with_colour_nl(user_output,[blue],' * Nr. of operation calls *not* reused and stored: ~w',[S]),
1602 bb_safe_get(value_persistance_not_worth_transitions,WW),
1603 format_with_colour_nl(user_output,[blue],' * Nr. of operation calls *not* reused and *not* stored due to CACHE_OPERATIONS_RUNTIME_LIMIT: ~w',[WW]),
1604 bb_safe_get(value_persistance_not_storeable_transitions,WW2),
1605 format_with_colour_nl(user_output,[blue],' * Nr. of operation calls *not* reused and *not* storeable: ~w',[WW2]),
1606 bb_safe_get(value_persistance_refresh_transitions,RF),
1607 format_with_colour_nl(user_output,[blue],' * Nr. of operation calls refreshed: ~w',[RF]).
1608
1609 get_value_persistance_stats(List) :-
1610 findall(Counter-Value,(is_counter(Counter),bb_safe_get(Counter,Value)),List).
1611
1612 is_counter(value_persistance_constants_loaded).
1613 is_counter(value_persistance_ref_constants_loaded).
1614 is_counter(value_persistance_loadable_operations).
1615 is_counter(value_persistance_op_cache_machines).
1616 is_counter(value_persistance_storeable_operations).
1617 is_counter(value_persistance_reused_transitions).
1618 is_counter(value_persistance_stored_transitions).
1619 is_counter(value_persistance_not_worth_transitions).
1620 is_counter(value_persistance_not_storeable_transitions).
1621 is_counter(value_persistance_refresh_transitions).
1622
1623 bb_reset(Counter) :- bb_put(Counter,0).
1624
1625
1626 % utility for debugging:
1627
1628 debugwarn(_Format,_Args) :- debug_mode(off),!.
1629 debugwarn(Format,Args) :-
1630 format_with_colour_nl(user_output,[magenta],Format,Args).
1631
1632 debuggood(_,_) :- debug_mode(off),!.
1633 debuggood(Format,Args) :-
1634 format_with_colour_nl(user_output,[cyan],Format,Args).
1635
1636 formatwarn(Format,Args) :-
1637 format_with_colour_nl(user_output,[orange],Format,Args).
1638
1639 formatinfo(_Format,_Args) :- silent_mode(on),!.
1640 formatinfo(Format,Args) :-
1641 format_with_colour_nl(user_output,[grey],Format,Args).
1642
1643 formatdetails(_Format,_Args) :- (silent_mode(on) ; debug_mode(off)), % or TRACE_INFO ?
1644 !.
1645 formatdetails(Format,Args) :-
1646 format_with_colour_nl(user_output,[grey],Format,Args).
1647
1648 formatgood(_Format,_Args) :- silent_mode(on),!.
1649 formatgood(Format,Args) :-
1650 format_with_colour_nl(user_output,[green],Format,Args).
1651
1652 :- use_module(tools_io,[safe_open_file/4]).
1653 my_open(File,Mode,St,Opt) :- debug_mode(off), !, safe_open_file(File,Mode,St,Opt).
1654 my_open(File,Mode,St,Opt) :-
1655 safe_open_file(File,Mode,St,Opt),
1656 formatinfo('Opened file (~w) ~w -> ~w',[Mode,File,St]).
1657
1658 my_close(Stream) :- debug_mode(off), !, close(Stream).
1659 my_close(Stream) :- stream_property(Stream,file_name(F)),
1660 stream_property(Stream,mode(M)),!,
1661 formatinfo('Closing ~w (~w) -> ~w',[F, M, Stream]),
1662 close(Stream).
1663 my_close(Stream) :-
1664 formatinfo('Closing -> ~w',[Stream]),
1665 close(Stream).
1666
1667 % -------------------
1668
1669 potray_loadable_operations :-
1670 format_with_colour_nl(user_output,[blue],'loadable_operations:',[]),
1671 loadable_operation(PrefixedOpName,OpName,MachName,InputPattern,_OutputPattern),
1672 format_with_colour_nl(user_output,[blue],'* ~w : (~w in ~w)',[PrefixedOpName,OpName,MachName]),
1673 InputPattern=lookup_pattern(SortedVars,_,_,_),
1674 format_with_colour_nl(user_output,[gray],' input: ~w',[SortedVars]),
1675 fail.
1676 potray_loadable_operations.