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