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