1 % (c) 2009-2024 Lehrstuhl fuer Softwaretechnik und Programmiersprachen,
2 % Heinrich Heine Universitaet Duesseldorf
3 % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html)
4 :- module(bvisual,
5 [
6 get_tree_from_expr/4,
7 %get_tree_from_expr2/4,
8 write_dot_graph_to_file/2,
9 %print_dot_graph/1,
10 get_any/5
11 %get_any_most_satisfier/5,
12
13 ]
14 ).
15
16 :- meta_predicate visit_tree(5,3,-,-,-).
17
18 :- use_module(probsrc(module_information),[module_info/2]).
19 :- module_info(group,dot).
20 :- module_info(description,'Generate a dot visualization of B predicates and expressions.').
21
22 :- use_module(library(lists), [maplist/3, scanlist/4]).
23 %:- use_module(library(terms)).
24
25 :- use_module(probsrc(bsyntaxtree)).
26 :- use_module(probsrc(translate), [translate_bvalue_with_limit/3,
27 translate_bexpression_with_limit/3]).
28 :- use_module(probsrc(kernel_waitflags), [
29 init_wait_flags/2,
30 ground_wait_flags/1]).
31
32 :- use_module(dotsrc(bvisual_any_maxsolver)).
33 :- use_module(probsrc(error_manager)).
34
35
36 %:- use_module(probsrc(self_check)).
37
38
39
40 % PARAMETERS
41
42 max_chars_per_line(40).
43 max_lines(10).
44 max_chars_total(400). % :- max_chars_per_line(X), max_lines(ML), T is X*ML.
45
46 %%
47 % Tree format:
48 % Trees used by write_dot_graph_to_file/2 are modelled by the bind/3
49 % predicate:
50 % bind(Children, NewLocalState, Metas).
51 % Children is a list of subexpressions, whose numbers may be greater than two.
52 % NewLocalState is a new local state for the subexpressions.
53 % Metas are informations about the expression, like value, label etc.
54 %
55 % Important: Avoid access bind directly since it's structure may
56 % be changed. Use accessors defined below.
57 % They provide a convenient way to access the meta informations.
58 %
59 % Use get_tree_from_expr(Tree, Expr, LocalStale, State) to gain
60 % a bind based tree from a predicate or expression evaluated in LocalState
61 % and State.
62 %
63 % Use get_tree_from_expr2(State, LocalState, TExpr, Tree) to inspect the
64 % predicate or expression only without walking down the children recursively.
65 %
66
67 %%
68 % accessors & shortcuts
69 %
70
71 create_long_tree_node(bind(Children, Label, Type, Value, LongDesc),
72 Children, Label, Type, Value, LongDesc).
73 get_tree_node_info(NODE, Children, NewLocalState, Metas) :-
74 (create_tree_node(NODE, C,N,M) -> Children=C, NewLocalState=N, Metas=M
75 ; add_error(bvisual,'Illegal node: ',NODE)).
76 create_tree_node(bind(Children, NewLocalState, Metas),
77 Children, NewLocalState, Metas).
78
79
80 % bind([],cv_o,nonboolean,137,b(identifier(cv_o),integer,[nodeid(pos(139677,56,192,18,192,21))]))
81
82 has_no_children(Node) :- child_nodes(Node,[]).
83
84 child_nodes(bind(ChildNodes,_,_,_,_), ChildNodes).
85 child_nodes(bind(ChildNodes,_,_), ChildNodes).
86
87 label(bind(_,Label,_,_,_), Label).
88 ?label(bind(_,_, Metas), Val) :- member(label(Val), Metas),!.
89
90 value(bind(_,_,_,Val,_), Val).
91 ?value(bind(_,_, Metas), Val) :- member(value(Val), Metas),!.
92
93 long_desc(bind(_,_,_,_,Val), Val).
94 long_desc(bind(_,_, Metas), Val):- member(long_desc(Val), Metas),!.
95
96 is_boolean(bind(_,_,boolean,_,_)).
97 ?is_boolean(bind(_,_, Metas)):- member(type(boolean), Metas),!.
98
99 %type(bind(_,_, Metas), Val):- member(type(Val), Metas),!.
100
101 is_explicit_value_node(Node) :- long_desc(Node,Val), is_explicit_value(Val).
102
103 %%
104 % Evaluate an expression and it's subexpressions and
105 % gain their values as a tree.
106 %
107 :- use_module(probsrc(tools),[catch_call/1]).
108 :- use_module(probsrc(b_global_sets),[add_prob_deferred_set_elements_to_store/3]).
109 get_tree_from_expr(Tree, Expr, LocalState, State) :-
110 (get_preference(dot_use_unicode,true) -> translate:set_unicode_mode ; true),
111 add_prob_deferred_set_elements_to_store(State,State1,visible),
112 call_cleanup(catch_call(get_tree_from_expr3(State1, LocalState, Expr, Tree)),
113 (get_preference(dot_use_unicode,true) -> translate:unset_unicode_mode ; true)).
114 % catch: as this can sometimes generate resource_error(memory) error exceptions
115
116
117 % whole tree at once
118 get_tree_from_expr3(State, LocalState, TExpr, Tree) :- rewrite_expression(TExpr,NewTExpr),!,
119 get_tree_from_expr3(State, LocalState, NewTExpr, Tree).
120 get_tree_from_expr3(State, LocalState, Expr, Tree) :-
121 get_tree_from_expr2(State, LocalState, Expr, NewTree),
122 get_tree_node_info(NewTree, SubExprs, NewLocalState, _),
123 label(NewTree, Label),
124 value(NewTree, Value),
125 create_long_tree_node(Tree, Children, Label, Type, Value, Expr),
126 % print(value(Value)),nl,
127 ( is_boolean(NewTree) -> Type = boolean ; Type = nonboolean ),
128 maplist(get_tree_from_expr3(State, NewLocalState), SubExprs, Children).
129
130
131
132
133 % only one layer
134 %get_tree_from_expr2(State, LocalState, b(B,T,_), Tree) :- functor(B,F,N), print(visit(F,N,T)),nl,fail.
135 get_tree_from_expr2(State, LocalState, TExpr, Tree) :-
136 get_texpr_expr(TExpr, Expr),
137 functor(Expr, Op, _Arity),
138 ? x_is_quantifier_like(Op),!,
139 x_get_metas(State, LocalState, TExpr, Metas),
140 ? member(value(Value), Metas),
141 (create_new_local_state(TExpr, Value, State, LocalState, NewLocalState) ->
142 x_get_sub_expressions(TExpr, State, LocalState, Children),
143 create_tree_node(Tree, Children, NewLocalState, Metas)
144 ;
145 create_tree_node(Tree, [], LocalState, Metas)
146 ),!.
147 get_tree_from_expr2(State, LocalState, TExpr, Tree):-
148 x_get_metas(State, LocalState, TExpr, Metas),
149 x_get_sub_expressions(TExpr, State, LocalState, Children),
150 create_tree_node(Tree, Children, LocalState, Metas),!.
151
152
153
154 %%
155 % Get all meta information for an expression/predicate
156 %
157 x_get_metas(State, LocalState, TExpr, Metas):-
158 findall(MetaValue, x_get_meta(State, LocalState, TExpr, MetaValue), Metas).
159
160
161 :- use_module(probsrc(eval_interface),[get_value_string_of_formula/5, b_compute_expression_no_wd/4]).
162 %%
163 % Helpers, each single meta info.
164 % There are value/1, type/1, label/1 and long_desc/1
165 %
166 % compute the various attributes of a node:
167 x_get_meta(State, LocalState, Expr, value(Value)):-
168 max_chars_total(Lim),
169 get_value_string_of_formula(State, LocalState, Expr, Lim, Value).
170 x_get_meta(_State, _LocalState, Expr, type(Type)):-
171 ( check_if_typed_predicate(Expr) ->
172 Type = boolean
173 ;
174 Type = nonboolean
175 ).
176 x_get_meta(_State, _LocalState, Expr, label(Label)):-
177 ? once(x_get_label(Expr, Label)).
178 x_get_meta(_State, _LocalState, TExpr, long_desc(Expr)):-
179 get_texpr_expr(TExpr, Expr).
180 x_get_meta(_State, _LocalState, TExpr, rodin_label(Label)):-
181 ? once(get_texpr_label(TExpr,Label)).
182
183
184
185 %%
186 % Generate a new local state for subexpressions if necessary, like a forall
187 % predicate. called for x_is_quantifier/1 predicate.
188 %
189
190 create_new_local_state(TExpr, Value, State, LocalState, NewLocalState):-
191 get_texpr_expr(TExpr, Expr),
192 ? (create_new_local_state2(Expr, Value, State, LocalState, NewLocalState)
193 -> true
194 ; %trace, create_new_local_state2(Expr, Value, State, LocalState, NewLocalState),
195 fail).
196
197 :- use_module(probsrc(bsyntaxtree),[create_typed_id/3]).
198 create_new_local_state2(forall(IDs, LHS, _), true, State, LocalState, NewLocalState) :- !,
199 (get_any(IDs, LHS, State, LocalState, NewLocalState)
200 -> true /* there is a solution for LHS */
201 ; /* Forall true because no solution for LHS */
202 get_any(IDs, b(truth,pred,[]), State, LocalState, NewLocalState)
203 ).
204 create_new_local_state2(forall(IDs, LHS, RHS), false,State, LocalState, NewLocalState) :- !,
205 safe_create_texpr(negation(RHS), pred, NotRHS),
206 conjunct_predicates([LHS, NotRHS], Condition),
207 get_any(IDs, Condition, State, LocalState, NewLocalState).
208 create_new_local_state2(let_expression(Ids,AssignmentExprs,Expr), Value, State, LocalState, NewLocalState) :- !,
209 create_typed_id('$VALUE',EType,TID), get_texpr_type(Expr,EType),
210 construct_pred([TID],[Expr],[],Preds0),
211 construct_pred(Ids,AssignmentExprs,Preds0,Preds),
212 conjunct_predicates(Preds,NewPred),
213 EXISTS = exists([TID|Ids],NewPred), %translate:print_bexpr(b(EXISTS,pred,[])),nl,
214 (Value=unknown -> ExValue = undefined ; ExValue=true),
215 create_new_local_state2(EXISTS, ExValue, State, LocalState, NewLocalState).
216 create_new_local_state2(let_predicate(Ids,AssignmentExprs,Pred), Value, State, LocalState, NewLocalState) :-
217 construct_pred(Ids,AssignmentExprs,[Pred],Preds),
218 conjunct_predicates(Preds,NewPred),
219 EXISTS = exists(Ids,NewPred),
220 create_new_local_state2(EXISTS, Value, State, LocalState, NewLocalState).
221 create_new_local_state2(exists(IDs, LHS), true, State, LocalState, NewLocalState) :- !,
222 ? get_any(IDs, LHS, State, LocalState, NewLocalState).
223 create_new_local_state2(exists(IDs, LHS), Status, State, LocalState, NewLocalState) :-
224 (Status=false ; Status=undefined),!, % for Status=undefined get_any can fail but get_any_most_satisfier should always succeed
225 get_any_most_satisfier(IDs, LHS, State, LocalState, NewLocalState).
226 create_new_local_state2(X, undefined, State, LocalState, NewLocalState) :- !,
227 create_new_local_state2(X, true, State, LocalState, NewLocalState).
228 create_new_local_state2(X, x_both_true_and_not_true, State, LocalState, NewLocalState) :-
229 %print(x_both_true_and_not_true(X)),nl, % probably means a PROB BUG !
230 (X = forall(_,_,_) -> VAL=false ; VAL=true),
231 create_new_local_state2(X, VAL, State, LocalState, NewLocalState).
232 create_new_local_state2(comprehension_set(IDs, Body), Val, State, LocalState, NewLocalState) :- !,
233 % TODO: we could extract values from the context; e.g., if we have x:{y|Pred} we could use value assigned to x for y
234 % format(user_output,'Comprehension set ~w, Val=~w~n~n',[IDs,Val]),
235 (is_empty_set_value(Val) ->
236 get_any_most_satisfier(IDs, Body, State, LocalState, NewLocalState)
237 % we could have a symbolic value; which we do not know if it is empty or not; Body could also have a WD error
238 ? ; get_any_if_possible(IDs, Body, State, LocalState, NewLocalState)).
239 create_new_local_state2(general_sum(IDs, CondPred, _Expr), _Val, State, LocalState, NewLocalState) :- !,
240 get_any_if_possible(IDs, CondPred, State, LocalState, NewLocalState).
241 create_new_local_state2(general_product(IDs, CondPred, _Expr), _Val, State, LocalState, NewLocalState) :- !,
242 get_any_if_possible(IDs, CondPred, State, LocalState, NewLocalState).
243 create_new_local_state2(quantified_union(IDs, CondPred, _Expr), _Val, State, LocalState, NewLocalState) :- !,
244 get_any_if_possible(IDs, CondPred, State, LocalState, NewLocalState).
245 create_new_local_state2(quantified_intersection(IDs, CondPred, _Expr), _Val, State, LocalState, NewLocalState) :- !,
246 get_any_if_possible(IDs, CondPred, State, LocalState, NewLocalState).
247 %create_new_local_state2(value(StoredVal), Val, State, LocalState, NewLocalState) :- nonvar(StoredVal),
248 % StoredVal = closure(P,T,Body),
249 % !,
250 % maplist(bsyntaxtree:create_typed_id,P,T,IDs),
251 % create_new_local_state2(comprehension_set(IDs, Body), Val, State, LocalState, NewLocalState).
252 create_new_local_state2(Expr, Value, _State, _LocalState, _NewLocalState) :-
253 add_error(bvisual,'Uncovered Expr in create_new_local_state: ',Expr:Value),
254 fail.
255
256 % a bit of a hack; it would be better to look at the Prolog version of the value
257 is_empty_set_value('{}').
258 is_empty_set_value('[]').
259 is_empty_set_value(X) :- translate:unicode_translation(empty_set,X).
260
261 % construct predicate from list of typed ids and expressions
262 construct_pred([],[],TailPreds,TailPreds).
263 construct_pred([ID|T],[EXPR|ET],TailPreds,[b(equal(ID,EXPR),pred,[])|TR]) :-
264 construct_pred(T,ET,TailPreds,TR).
265
266
267 :- use_module(probsrc(custom_explicit_sets),[is_interval_closure/3]).
268 % rewrite rules to transform some values into a formula tree
269 rewrite_expression(b(value(StoredVal),Type,I),b(comprehension_set(IDs, Body),Type,[compiled_closure|I])) :-
270 nonvar(StoredVal),
271 StoredVal = closure(P,T,Body),
272 \+ is_interval_closure(StoredVal,_,_),
273 maplist(bsyntaxtree:create_typed_id,P,T,IDs).
274
275
276 %%
277 % Get subexpressions, merge sub expressions with associative operators, like
278 % 'and'. For example the predicate and(A,and(B,C)) has three sub expressions:
279 % A, B and C. This behaviour is controlled by x_associative_operator/1.
280 %
281
282 x_get_sub_expressions(TExpr, Children) :-
283 x_get_sub_expressions(TExpr, [], [], Children).
284
285 x_get_sub_expressions(TExpr, _, _, Children) :- nonvar(TExpr),
286 do_not_expand(TExpr), !,
287 Children=[].
288 x_get_sub_expressions(TExpr, State, LocalState, Children) :-
289 syntaxtraversion(TExpr,Expr,_,_,Children0,_),
290 % add custom explanation rules for certain nodes:
291 x_get_custom_explanation_subexpressions(Expr,State, LocalState, Children0,Children).
292
293 :- use_module(probsrc(preferences),[get_preference/2]).
294 do_not_expand(b(equal(A,B),pred,_)) :- get_preference(pp_propositional_logic_mode,true),
295 A = b(identifier(_),boolean,_), % do not expand such nodes
296 is_explicit_value(B).
297
298 :- use_module(library(lists)).
299 % is a value which does not need to be further decomposed
300 is_explicit_value(b(Val,_,_)) :- is_explicit_value2(Val).
301 is_explicit_value2(integer(_N)).
302 is_explicit_value2(string(_S)).
303 is_explicit_value2(boolean_true).
304 is_explicit_value2(boolean_false).
305 is_explicit_value2(identifier(N)) :- b_global_sets:lookup_global_constant(N,_).
306 is_explicit_value2(couple(A,B)) :- is_explicit_value(A), is_explicit_value(B).
307 is_explicit_value2(value(_)).
308 % to do: add further cases
309
310
311 :- use_module(library(timeout),[time_out/3]).
312 x_get_custom_explanation_subexpressions(function(Fun,Value),State,LocalState,_C0,Children) :-
313 % a custom rule for function application of closures
314 % print(function(Fun)),nl, print(value(Value)),nl,print(C0),nl,
315 simple_value(Fun),
316 get_preference(formula_tree_minimal_timeout,TOVAL),
317 time_out(b_compute_expression_no_wd(Fun, LocalState, State, FunValue),TOVAL,TRes), TRes \== time_out,
318 FunValue = closure(P,T,Pred),
319 P = [X,_Res], % TO DO: extend to functions with multiple arguments
320 closures:is_lambda_closure(P,T,Pred, _OtherIDs,_OtherTypes, DomainPred, Expr),!,
321 bsyntaxtree:replace_id_by_expr(Expr,X,Value,NewExpr),
322 bsyntaxtree:replace_id_by_expr(DomainPred,X,Value,NewDomainPred),
323 Children = [Fun,Value, NewDomainPred,NewExpr].
324 % we could do similar treatment for member(Tuple,Fun)
325 x_get_custom_explanation_subexpressions(Expr,_State,_LocalState, Children0,Children) :-
326 x_get_custom_explanation_subexpressions(Expr, Children0,Children).
327
328
329
330 simple_value(b(V,_,_)) :- simple_value_aux(V).
331 simple_value_aux(identifier(_)).
332 simple_value_aux(value(_)).
333
334 :- use_module(probsrc(typing_tools),[create_maximal_type_set/2]).
335
336 % TO DO: add explanation rules, e.g., f:A --> B ==> f:TA +-> B & dom(f)=A ,...
337 x_get_custom_explanation_subexpressions(Expr,_Children0, Children):-
338 functor(Expr, Op, _),
339 x_operator_with_local_variables(Op),!,Children=[].
340 x_get_custom_explanation_subexpressions(Expr,Children0, Children):-
341 functor(Expr, Op, _), Op = set_extension,
342 length(Children0,Len), (Len>50 ; maplist(is_explicit_value,Children0)),
343 % can be a very large explicit node; no sense in expanding and showing every element
344 % print(no_child(Expr)),nl,
345 % TO DO: check if simple value
346 !,Children=[].
347 x_get_custom_explanation_subexpressions(Expr,Children0, Children):-
348 functor(Expr, Op, _),
349 x_associative_operator(Op),!,
350 maplist(x_collect_exprs_with_op(Op), Children0, Children1),
351 scanlist(x_append_reverse, Children1, [], Children).
352 x_get_custom_explanation_subexpressions(member(X,b(partial_function(Dom,Range),T,Info)),_,Children) :-
353 T=set(set(couple(DomT,RanT))),
354 %print(pf(Dom,Range)),nl,
355 (\+ bsyntaxtree:is_just_type(Dom) ; \+ bsyntaxtree:is_just_type(Range)),
356 nonmember(already_expanded,Info),
357 % otherwise we may have an infinite recursion; maybe better to add other operator
358 %print(not_type),nl,fail,
359 !,
360 Children = [A|TB],
361 create_maximal_type_set(DomT,DomType),
362 create_maximal_type_set(RanT,RanType),
363 A = b(member(X,b(partial_function(DomType,RanType),T,[already_expanded|Info])),pred,[]),
364 add_subset_check(b(domain(X),set(DomT),[]), Dom, TB,TC),
365 add_subset_check(b(range(X),set(RanT),[]), Range, TC,[]),
366 TB \= [],!.
367 x_get_custom_explanation_subexpressions(member(X,b(total_function(Dom,Range),T,Info)),_,Children) :-
368 T=set(set(couple(DomT,RanT))), !,
369 Children = [A,B|TC],
370 %print(total_fun_check(DomT,RanT)),nl,
371 create_maximal_type_set(DomT,DomType),
372 create_maximal_type_set(RanT,RanType),
373 A = b(member(X,b(partial_function(DomType,RanType),T,Info)),pred,[]),
374 B = b(equal(b(domain(X),set(DomT),[]), Dom),pred,[]),
375 add_subset_check(b(range(X),set(RanT),[]), Range, TC,[]).
376 x_get_custom_explanation_subexpressions(member(X,b(total_surjection(Dom,Range),T,Info)),_,Children) :-
377 T=set(set(couple(DomT,RanT))), !,
378 Children = [A,B,C],
379 create_maximal_type_set(DomT,DomType),
380 create_maximal_type_set(RanT,RanType),
381 A = b(member(X,b(partial_function(DomType,RanType),T,Info)),pred,[]),
382 B = b(equal(b(domain(X),set(DomT),[]), Dom),pred,[]),
383 C = b(equal(b(range(X),set(RanT),[]), Range),pred,[]).
384 x_get_custom_explanation_subexpressions(member(X,b(partial_surjection(Dom,Range),T,Info)),_,Children) :-
385 T=set(set(couple(DomT,RanT))), !,
386 Children = [A|TB],
387 create_maximal_type_set(DomT,DomType),
388 create_maximal_type_set(RanT,RanType),
389 A = b(member(X,b(partial_function(DomType,RanType),T,Info)),pred,[]),
390 add_subset_check(b(domain(X),set(DomT),[]), Dom, TB,[C]),
391 C = b(equal(b(range(X),set(RanT),[]), Range),pred,[]).
392 x_get_custom_explanation_subexpressions(Expr,Children, Res) :-
393 functor(Expr,Functor,_),
394 (filter_trivial_children_for(Functor) -> exclude(is_explicit_value,Children,Res) ; Res=Children).
395
396 filter_trivial_children_for(let_predicate). % avoid we see trivial values twice
397
398
399
400 add_subset_check(LHS,RHS,Result,EndOfList) :-
401 % add subset check, unless "obvious"
402 (bsyntaxtree:is_just_type(RHS) -> Result=EndOfList
403 ; Result = [b(subset(LHS, RHS),pred,[])|EndOfList]
404 ).
405
406 x_collect_exprs_with_op(Op, TExpr, Children):-
407 syntaxtraversion(TExpr,Expr,_,_,_,_),
408 ( functor(Expr, Op, _),
409 \+ get_texpr_label(TExpr,_) % do not expand out if there is a separate label for the sub-conjunct/disjunct
410 -> x_get_sub_expressions(TExpr, Children)
411 ; Children = [TExpr]
412 ).
413
414 x_append_reverse(X,Y,L):- append(Y,X,L).
415
416
417
418 %%
419 % Get label for an expression
420 %
421
422 x_get_label(b(Expr,_,Infos), Label) :-
423 ? x_get_label_aux(Expr,Infos,Label).
424 x_get_label_aux(comprehension_set(_,_),[compiled_closure|_],Label) :- !, % generated by rewrite_expression
425 Label= 'comprehension_set (compiled closure)'.
426 x_get_label_aux(value(V), _, Label) :- !, get_value_label(V,Label).
427 x_get_label_aux(external_function_call(Name,_),_Infos,Label) :- !,
428 Label=Name.
429 x_get_label_aux(external_pred_call(Name,_),_Infos,Label) :- !,
430 Label=Name.
431 x_get_label_aux(Expr, _, Label) :-
432 %Expr =.. [Op, Label],
433 functor(Expr,Op,1), arg(1,Expr,Label),
434 x_literal_value_operator(Op).
435 x_get_label_aux(Expr, _ , Label):-
436 functor(Expr, Fkt, _),
437 x_functor_to_label(Fkt, Label).
438
439 get_value_label(V,Label) :- var(V),!, Label='VAR-VALUE'.
440 %get_value_label(global_set(GS),Label) :- !, Label=GS. % should we pretty print the value
441 %get_value_label(V,stored_value(F)) :- functor(V,F,_N).
442 get_value_label(V,Label) :- translate_bvalue_with_limit(V,40,Label).
443 %%
444 % Helpers
445 %
446
447 %%
448 % Maps functor of prolog term to correct label
449 %
450 :- use_module(probsrc(translate),[translate_prolog_constructor_in_mode/2]).
451 x_functor_to_label(F,R) :- translate_prolog_constructor_in_mode(F,PPVersion), !, R=PPVersion.
452 %x_functor_to_label(conjunct, conjunction):-!.
453 %x_functor_to_label(disjunct, disjunction):-!.
454 x_functor_to_label(function, R):-!, R='func.applic.'.
455 x_functor_to_label(X, X).
456
457 %%
458 % List of operators which may generate a new local state,
459 % like a forall predicate.
460 %
461 x_is_quantifier_like(comprehension_set).
462 x_is_quantifier_like(general_sum).
463 x_is_quantifier_like(general_product).
464 x_is_quantifier_like(quantified_union).
465 x_is_quantifier_like(quantified_intersection).
466 x_is_quantifier_like(X) :- x_is_quantifier(X).
467
468 x_is_quantifier(forall).
469 x_is_quantifier(exists).
470 x_is_quantifier(let_predicate).
471 x_is_quantifier(let_expression).
472
473 x_is_quantifier(true_guard).
474
475 %%
476 % List of associative operators
477 %
478
479 x_associative_operator(conjunct).
480 x_associative_operator(disjunct).
481 x_associative_operator(add).
482 x_associative_operator(mul).
483
484
485 %%
486 % List of operators with local variables
487 %
488
489 % x_operator_with_local_variables(comprehension_set). is now supported in create_new_local_state2
490 % x_operator_with_local_variables(let_predicate). ditto, and for let_expression
491 %x_operator_with_local_variables(general_sum).
492 %x_operator_with_local_variables(general_product).
493 %x_operator_with_local_variables(quantified_union).
494 %x_operator_with_local_variables(quantified_intersection).
495 x_operator_with_local_variables(lambda).
496 x_operator_with_local_variables(lazy_let_expr).
497 x_operator_with_local_variables(lazy_let_pred).
498 x_operator_with_local_variables(lazy_let_subst).
499
500
501 %%
502 % List of literal values
503 %
504 x_literal_value_operator(identifier).
505 x_literal_value_operator(value).
506 x_literal_value_operator(integer).
507 x_literal_value_operator(string).
508
509
510
511
512
513
514 %%
515 % Try to execute an 'Any' statement on LocalState and State.
516 % New state with the values is returned by NewLocalState.
517 % Compare to b_interpreter:b_execute_statement2(any(Parameters,PreCond,Body),...)
518 %
519
520 :- use_module(probsrc(b_enumerate), [b_tighter_enumerate_values_in_ctxt/3]).
521 :- use_module(probsrc(b_interpreter), [set_up_typed_localstate/6]).
522
523
524 get_any(IDs, Condition, State, LocalState, NewLocalState) :-
525 ? catch(get_any1(IDs, Condition, State, LocalState, NewLocalState), E,
526 (add_warning(bvisual,'Exception in bvisual while treating quantfier predicate: ',E),fail)).
527 get_any1(IDs, C, S, LS, NewLS) :-
528 % used to be: on_enumeration_warning(bvisual:get_any2(IDs, C, S, LS, NewLS), fail).
529 % call below does not force throwing enum warnings, which is ok as we just want one solution:
530 ? catch_enumeration_warning_exceptions(bvisual:get_any2(IDs, C, S, LS, NewLS), fail, false, C).
531 get_any2(IDs, Condition, State, LocalState, NewLocalState) :-
532 set_up_typed_localstate(IDs, _FreshVars, TypedVals, LocalState, NewLocalState,positive),
533 init_wait_flags(WF,[get_any2]),
534 b_interpreter:b_test_boolean_expression(Condition,NewLocalState, State, WF), /* check WHERE */
535 b_tighter_enumerate_values_in_ctxt(TypedVals,Condition,WF),
536 ? ground_wait_flags(WF).
537
538
539 get_any_most_satisfier(IDs, Condition, State, LocalState, NewLocalState):-
540 ( any_maxsolver(IDs, Condition, State, LocalState, NewLocalState) ->
541 true
542 ; get_typing_solution(IDs, State, LocalState, NewLocalState)
543 ).
544
545 % just get a solution for truth: i.e., find any value for the types
546 get_typing_solution(IDs, State, LocalState, NewLocalState) :-
547 conjunct_predicates([], Truth),
548 get_any(IDs, Truth, State, LocalState, NewLocalState).
549
550 % get some successful solution; or if this does not work: just call get_any_most_satisfier
551 get_any_if_possible(IDs, Condition, State, LocalState, NewLocalState) :-
552 ? if(get_any(IDs, Condition, State, LocalState, NewLocalState),true,
553 get_any_most_satisfier(IDs, Condition, State, LocalState, NewLocalState)).
554
555
556
557 %%
558 % Main predicate for writing dot graph
559 % Use it to start.
560 %
561 % FileSpec is used by tell/1 to open a stream.
562 % The graph is printed by print_dot_graph_for_tree/1.
563 %
564
565 :- use_module(probsrc(tools),[catch_call/1]).
566 :- use_module(probsrc(tools_io),[safe_intelligent_open_file/3]).
567
568 write_dot_graph_to_file(Tree, FileSpec) :-
569 catch_call(write_dot_graph_to_file_aux(Tree, FileSpec)).
570 write_dot_graph_to_file_aux(Tree, FileSpec) :-
571 (get_preference(dot_use_unicode,true) -> translate:set_unicode_mode ; true),
572 safe_intelligent_open_file(FileSpec,write,Stream),
573 call_cleanup(print_dot_graph(Stream,Tree),
574 (close(Stream),(get_preference(dot_use_unicode,true) -> translate:unset_unicode_mode ; true))).
575
576 print_dot_graph(Stream,Tree):-
577 format(Stream,'digraph g {~nrankdir=RL;~n',[]),
578 visit_tree(x_print_dot_graph_visitor(Stream), x_print_dot_graph_node(Stream), Tree, [],_),
579 format(Stream,'}~n',[]).
580
581
582 %%
583 % Helpers
584 %
585 :- public x_print_dot_graph_visitor/6.
586 x_print_dot_graph_visitor(Stream,enter, Node, _Children, [], [1,root]) :-
587 ? x_print_dot_graph_node_only(Stream,Node, root),!.
588 x_print_dot_graph_visitor(Stream,enter, Node, _Children, In, [NewID|In]):-
589 x_print_dot_graph_node(Stream,Node, In, [NewID|_]).
590 x_print_dot_graph_visitor(_Stream,leave, _Node, _Children, [NewID,_ID|Out], [NewID|Out]).
591
592 /* for 3 args : */
593 x_print_dot_graph_node(Stream,Node, [], []) :-
594 x_print_dot_graph_node_only(Stream,Node, root),
595 !.
596 x_print_dot_graph_node(Stream,Node, [ID|Rest], [NewID|Rest]) :-
597 Rest = [PID|_],
598 NewID is ID + 1,
599 ? x_print_dot_graph_node_only(Stream,Node, ID),
600 format(Stream,'~8|Node~p -> Node~p;~N', [ID, PID]),
601 !.
602
603
604
605
606 shorten_atom(MaxLengthPerLine, Atom, NewAtom) :-
607 max_lines(MaxLines),
608 ? shorten_atom(MaxLengthPerLine, Atom, NewAtom,MaxLines).
609 shorten_atom(MaxLengthPerLine, Atom, NewAtom,MaxLines) :-
610 ( atom(Atom) ->
611 atom_chars(Atom, AtomChars),
612 ? shorten_atom_list(MaxLengthPerLine, AtomChars, NewAtom,MaxLines)
613 ;
614 NewAtom = Atom
615 ).
616
617 shorten_atom_list(MaxLengthPerLine, Chars, Atom,MaxLines):-
618 %length(Chars, Length),
619 ? split_chars(Chars,MaxLengthPerLine, Prefix,Suffix),
620 ( Suffix=[] ->
621 atom_chars(Atom, Chars)
622 ;
623 %lists:append_length(Prefix, Suffix, Chars, MaxLengthPerLine),
624 %print( split_chars(Chars,MaxLengthPerLine, Prefix,Suffix) ),nl,
625 (MaxLines>1 -> ML1 is MaxLines-1,
626 ? shorten_atom_list(MaxLengthPerLine, Suffix, SuffixAtom,ML1)
627 ; SuffixAtom = '...'),
628 atom_chars(PrefixAtom, Prefix),
629 atom_concat(PrefixAtom, '\\n', PrefixAtomNewLine),
630 atom_concat(PrefixAtomNewLine, SuffixAtom, Atom)
631 ).
632
633 % bvisual:split_chars([a,b,d,e,f],2,P,S), P==[a,b], S==[d,e,f]
634
635 % a predicate to split List if it is longer than MaxLen, but only outside of single and double quotes
636 % List is suppoed to be already encoded using string_escape
637 split_chars(List,MaxLen,Prefix,Suffix) :-
638 ? split_chars(List,MaxLen,Prefix,Suffix,q(outside,outside)).
639
640 split_chars(List,MaxL,R,Suffix,Q) :- MaxL < 1, Q=q(outside,outside), !,
641 % only add newline if we are outside of quotes
642 R=[], Suffix=List.
643 split_chars([],_MaxL,[],[],_).
644 split_chars(L,MaxL,LR,LeftOver,Q) :-
645 is_a_unit(L,T,LR,TR),!,
646 ? M1 is MaxL-1, split_chars(T,M1,TR,LeftOver,Q).
647 split_chars([Slash,Quote|T],MaxL,[Slash,Quote|TR],LeftOver,Q) :- Slash = ('\\'),
648 !, %(Quote=34;Quote=39), !, % \" or \'
649 toggle_quotes(Quote,Q,TQ),
650 ? M1 is MaxL-1, split_chars(T,M1,TR,LeftOver,TQ).
651 split_chars([H|T],MaxL,[H|TR],LeftOver,Q) :-
652 ? M1 is MaxL-1, split_chars(T,M1,TR,LeftOver,Q).
653
654 toggle_quotes('\'',q(outside,Q2),q(inside,Q2)) :- !.
655 toggle_quotes('\'',q(inside,Q2),q(outside,Q2)) :- !.
656 toggle_quotes('\"',q(Q1,outside),q(Q1,inside)) :- !.
657 toggle_quotes('\"',q(Q1,inside),q(Q1,outside)) :- !.
658 toggle_quotes(_,Q,Q).
659
660 % check for HTML encodings like à Ë α ...
661 % these should be counted as a single character and not be split
662 is_a_unit(['&'|T1],T,['&'|T1R],TR) :- get_lexeme(T1,10,LexUnit,T),
663 append(LexUnit,TR,T1R).
664
665 get_lexeme([';'|T],_,[';'],TRes) :- !, TRes=T.
666 get_lexeme([H|T],L,[H|LexT],TRes) :- L>1, L1 is L-1, get_lexeme(T,L1,LexT,TRes).
667
668 :- use_module(probsrc(tools_strings),[ajoin/2]).
669 get_long_label(Node,MaxPerLine,LongLabel) :-
670 long_desc(Node, LongDesc),
671 %tools:print_wtime(start_translate(ID)),
672 LongDesc = b(P,T,Infos),
673 ? (select_info_labels(Labels,Infos,I2) -> true ; Labels=[],I2=Infos),
674 translate_bexpression_with_limit(b(P,T,I2), 250, Pretty0),
675 %tools:print_wtime(translated(ID)),
676 string_escape(Pretty0, Pretty1),
677 ? shorten_atom(MaxPerLine, Pretty1, Pretty1h), % maximally 10 lines with 40 chars
678 gen_labels(Labels,[Pretty1h],Atoms), % print labels separately one per line
679 ajoin(Atoms,LongLabel).
680
681 gen_labels([],Res,Res).
682 gen_labels([Label|T],Tail,['/*@ ',Label,' */\\n' | Res]) :- gen_labels(T,Tail,Res).
683
684 :- use_module(probsrc(tools),[string_escape/2]).
685 x_print_dot_graph_node_only(Stream,Node, ID) :- %tools:print_wtime(node(ID,Node)),
686 label(Node, Label0),
687 max_chars_per_line(MaxPerLine),
688 string_escape(Label0, Label1), %tools:print_wtime(escaped(ID)),
689 ( ID \= root, is_boolean(Node)
690 ->
691 Label=Label1,
692 ? get_long_label(Node,MaxPerLine,Pretty1h),
693 value(Node, TruthValue),
694 string_escape(TruthValue, TVEsc),
695 get_predicate_field_seperator(Sep),
696 atom_concat(TVEsc, Sep, Pretty4),
697 atom_concat(Pretty4, Pretty1h, Pretty)
698 ;
699 value(Node, Val),
700 string_escape(Val, Pretty1), % TO DO: check that we also escape | in case of records!
701 (is_boolean(Node) -> get_predicate_field_seperator(Sep)
702 ? ; get_expression_field_seperator(Sep)),
703 ((Pretty1=Label1 ; is_explicit_value_node(Node))
704 -> Pretty='', Label=Pretty1 % we have a simple value node like integer(1),...
705 ; has_no_children(Node)
706 ? -> get_long_label(Node,MaxPerLine,Label),
707 ? shorten_atom(MaxPerLine, Pretty1, Pretty)
708 ; Label=Label1,
709 ? shorten_atom(MaxPerLine, Pretty1, Pretty)
710 )
711 ),
712 %tools:print_wtime(node_style(ID)),
713 x_print_dot_graph_node_style(Node, Shape, Color,Style),
714 %tools:print_wtime(printing(ID)),
715 (Pretty=''
716 -> format(Stream,'~4|Node~p [label="~p", shape="~p", fillcolor="~p", style="~p"]~N',
717 [ID, Label, Shape, Color,Style])
718 ; format(Stream,'~4|Node~p [label="~p~p~p", shape="~p", fillcolor="~p", style="~p"]~N',
719 [ID, Label, Sep, Pretty, Shape, Color,Style])). % ,tools:print_wtime(finished(ID)).
720
721
722 x_print_dot_graph_node_style(Node, Shape, 'tomato','filled,rounded') :-
723 is_boolean(Node), value(Node, false),!, boolean_shape(Shape).
724 x_print_dot_graph_node_style(Node, Shape, 'olivedrab2','filled,rounded') :-
725 is_boolean(Node), value(Node, true),!, boolean_shape(Shape).
726 x_print_dot_graph_node_style(Node, Shape, 'orange','filled,rounded') :- % styles could be dashed,dotted, rounded, ...
727 is_boolean(Node),!, boolean_shape(Shape).
728 x_print_dot_graph_node_style(Node, Shape, 'yellow','filled') :-
729 value(Node, unknown),!, get_preference(dot_expression_node_shape,Shape).
730 x_print_dot_graph_node_style(_, Shape, 'white',filled) :-
731 get_preference(dot_expression_node_shape,Shape).
732
733
734 boolean_shape(Shape) :- get_preference(dot_predicate_node_shape,Shape). % ellipse, oval, record TO DO: other form for quantifiers
735
736 % if we use a Dot record, we can use | to separate fields
737 get_predicate_field_seperator('|') :- get_preference(dot_predicate_node_shape,record).
738 get_predicate_field_seperator('\\n').
739 get_expression_field_seperator('|') :- get_preference(dot_expression_node_shape,record).
740 get_expression_field_seperator('\\n').
741
742
743
744 %%
745 % Hierarchical Visitor Pattern for "trees"
746 % used to be in gtree module
747 %
748 % For this implementation a tree is a (root) node and a functor, which
749 % maps a node to it's child nodes. For example a Prolog term can be
750 % understand as a tree by the functor:
751 % children_fkt(Term, Subterms):- Term=..[_|Subterms].
752 %
753 % Important: Do not use trees with cycles. This implementation will not
754 % detect them and will not terminate.
755 %
756 % The indented calling is:
757 % visit_tree(+VisitorFkt5, +VisitorFkt3, +Node, +EnvIn, -EnvOut)
758 %
759 % If no functor is specified, the former mentioned one is used.
760 % EnvIn and EnvOut are Variables for intermediate values or states,
761 % which you might need to maintain between calls to VisitorFkt. You
762 % might omit them, if you do not need them.
763 %
764 % During the visiting process the VisitorFkt functor is called for each
765 % node starting with the "root". Calls are made in following order:
766 % - VisitorFkt(Node, EnvIn, EnvOut), if Node is a leaf,
767 % i.e. ChildrenFkt(Node, []).
768 % - VisitorFkt(enter, Node, Children, EnvIn, EnvOut), before branching,
769 % - VisitorFkt(leave, Node, Children, EnvIn, EnvOut), after branching
770 % to Node's child nodes.
771 % !- Only if VisitorFkt(enter, ...) succeeds, child nodes will be
772 % visited and VisitorFkt(leave, ...) will be called.
773 %
774
775 :- use_module(library(lists), [scanlist/4, maplist/2]).
776
777 %:- meta_predicate visit_tree(5,3,-,-,-).
778
779 visit_tree(VisitorFkt5, VisitorFkt3, Node, EnvIn, EnvOut):-
780 child_nodes(Node, Children),
781 ( Children == [] ->
782 call(VisitorFkt3, Node, EnvIn, EnvOut)
783 ;
784 ? ( call(VisitorFkt5, enter, Node, Children, EnvIn, Env1) ->
785 scanlist(visit_tree(VisitorFkt5, VisitorFkt3), Children, Env1, Env2),
786 call(VisitorFkt5, leave, Node, Children, Env2, EnvOut)
787 ;
788 EnvOut = EnvIn
789 )
790 ),
791 !.
792
793
794
795