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 | | |
5 | | |
6 | | :- module(predicate_analysis,[predicate_analysis/5 |
7 | | ,predicate_analysis_with_global_sets/5 |
8 | | ,info_conjunct/3 |
9 | | ,info_disjunct/3 |
10 | | ,test_predicate_analysis/0 |
11 | | ,max_type_cardinality/2 |
12 | | ]). |
13 | | |
14 | | :- use_module(library(lists)). |
15 | | :- use_module(library(avl)). |
16 | | :- use_module(library(ordsets)). |
17 | | :- use_module(library(terms)). |
18 | | |
19 | | :- use_module(probsrc(bsyntaxtree)). |
20 | | :- use_module(probsrc(btypechecker)). |
21 | | :- use_module(probsrc(preferences)). |
22 | | :- use_module(probsrc(tools)). |
23 | | :- use_module(probsrc(translate)). |
24 | | :- use_module(probsrc(bmachine)). |
25 | | :- use_module(probsrc(b_global_sets), [is_b_global_constant/3,b_global_set_cardinality/2]). |
26 | | |
27 | | :- use_module(probsrc(error_manager)). |
28 | | :- use_module(probsrc(self_check)). |
29 | | :- use_module(probsrc(debug),[debug_println/2, formatsilent/2, debug_format/3]). |
30 | | |
31 | | :- use_module(probsrc(inf_arith)). |
32 | | :- use_module(kodkodsrc(interval_calc)). |
33 | | |
34 | | % for interpreting value(...) components |
35 | | :- use_module(probsrc(store), [empty_state/1]). |
36 | | :- use_module(probsrc(b_interpreter), [b_compute_expression_nowf/6]). |
37 | | |
38 | | :- use_module(probsrc(module_information),[module_info/2]). |
39 | | :- module_info(group,kodkod). |
40 | | :- module_info(description,'This is a module and plugin to extract information about expressions (e.g. integer bounds (aka intervals), cardinality) by static analysis. Used by Kodkod.'). |
41 | | |
42 | | :- dynamic debugging_enabled/0. |
43 | | :- load_files(library(system), [when(compile_time), imports([environ/2])]). |
44 | | % avl_apply |
45 | | % similar to avl_fetch_pair: but checks whether we have a function |
46 | | % and whether the function is defined for the key |
47 | | :- if(environ(debug_kodkod,true)). |
48 | | debugging_enabled. % commment in to have dot files written to debug-constraint-anim folder (interval analysis for kodkod) |
49 | | :- endif. |
50 | | |
51 | | % the following is called by kodkod: |
52 | | predicate_analysis_with_global_sets(Tree,ROIdentifiers,Identifiers,NewIdentifiers,NewTree) :- |
53 | | findall(X, |
54 | | (bmachine_is_precompiled, |
55 | | b_get_machine_set(Set), |
56 | | b_global_set_cardinality(Set,Card), |
57 | | create_texpr(identifier(Set),set(global(Set)),[analysis([card:irange(Card,Card)])],X)), |
58 | | Sets), |
59 | | findall(X, |
60 | | (bmachine_is_precompiled, |
61 | | is_b_global_constant(Set,_Index,Elem), |
62 | | create_texpr(identifier(Elem),global(Set),[],X)), |
63 | | Elements), |
64 | | append([ROIdentifiers,Sets,Elements],ROIds2), |
65 | ? | predicate_analysis(Tree,ROIds2,Identifiers,NewIdentifiers,NewTree). |
66 | | |
67 | | |
68 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
69 | | % static analysis of integers |
70 | | |
71 | | :- volatile nodecounter/1, debug_node/2, debug_constraints/1. |
72 | | :- dynamic nodecounter/1, debug_node/2, debug_constraints/1. |
73 | | |
74 | | predicate_analysis(Tree,ROIdentifiers,Identifiers,NewIdentifiers,NewTree) :- |
75 | | reset_nodecounter, |
76 | | debug_println(15,starting_predicate_analysis), |
77 | | start_ms_timer(Timer1), |
78 | | create_node_environment(ROIdentifiers,Identifiers,NewIdentifiers,NodeEnv,Infos1), |
79 | | (debug:debug_mode(on) -> stop_ms_timer_with_msg(Timer1,create_node_environment) ; true), |
80 | | start_ms_timer(Timer2), |
81 | | extract_nodes(Tree,NodeEnv,_Node,_Mode,NewTree,(Constraints,Infos),Infos1), |
82 | | (debug:debug_mode(on) -> stop_ms_timer_with_msg(Timer2,extract_nodes) ; true), |
83 | | start_ms_timer(Timer3), |
84 | | create_constraint_tree(Constraints,Nodes,CTree), |
85 | | (debug:debug_mode(on) -> stop_ms_timer_with_msg(Timer3,create_constraint_tree) ; true), |
86 | | retractall(debug_constraints(_)),assertz(debug_constraints(Constraints)), |
87 | | create_initial_information(Nodes,Empty), |
88 | | apply_start_constraints(Constraints,ChangedNodes,Empty,Initial), |
89 | | compute_maximum_iterations(Nodes,MaxIter), |
90 | | start_ms_timer(Timer4), |
91 | ? | do_analysis(ChangedNodes,2,MaxIter,CTree,Initial,Values), |
92 | | (debug:debug_mode(on) -> stop_ms_timer_with_msg(Timer4,do_analysis(MaxIter)) ; true), |
93 | | start_ms_timer(Timer5), |
94 | | insert_info(Infos,Values), |
95 | | (debug:debug_mode(on) -> stop_ms_timer_with_msg(Timer5,insert_info) ; true). |
96 | | |
97 | | create_node_environment(ROIdentifiers,Identifiers,NewIdentifiers,NodeEnv,Infos) :- |
98 | | initial_node_env(InitNodeEnv), |
99 | | create_identifier_nodes2(ROIdentifiers,_,InitNodeEnv,RONodeEnv1,Infos1,([],[])), |
100 | | identifier_nodes_readonly(RONodeEnv1,RONodeEnv), |
101 | | create_identifier_nodes2(Identifiers,NewIdentifiers,RONodeEnv,NodeEnv,Infos2,Infos1), |
102 | | foldl(add_id_basic_constraint(NodeEnv),Identifiers,Infos,Infos2). |
103 | | add_id_basic_constraint(NodeEnv,Identifier,In,Out) :- |
104 | | get_texpr_id(Identifier,Id), |
105 | | identifier_node_lookup(Id,NodeEnv,_NodeId,Nodes,_Mode), |
106 | | add_type_constraints(Identifier,Nodes,In,Out). |
107 | | |
108 | | % just a heuristic to avoid a non-terminating run |
109 | | compute_maximum_iterations(Nodes,MaxIter) :- |
110 | | length(Nodes,N), MaxIter is N*10. |
111 | | |
112 | | reset_nodecounter :- |
113 | | retractall(nodecounter(_)),assertz(nodecounter(1)), |
114 | | retractall(debug_node(_,_)), |
115 | | retractall(debug_constraints(_)). |
116 | | |
117 | | % create a numbered node for each identifier |
118 | | initial_node_env(NodeEnv) :- empty_avl(NodeEnv). |
119 | | |
120 | | %create_identifier_nodes(Ids,NIds,Env,IIn,IOut) :- |
121 | | % empty_avl(Start), create_identifier_nodes2(Ids,NIds,Start,Env,IIn,IOut). |
122 | | |
123 | | create_identifier_nodes2([],[],Env,Env,Info,Info). |
124 | | create_identifier_nodes2([TId|Rest],[NTId|NRest],In,Out,IIn,IOut) :- |
125 | | create_identifier_nodes3(TId,NTId,In,Inter,IIn,IInter), |
126 | | create_identifier_nodes2(Rest,NRest,Inter,Out,IInter,IOut). |
127 | | create_identifier_nodes3(TId,NTId,In,Out,IIn,IOut) :- |
128 | | get_texpr_id(TId,Id), |
129 | | get_texpr_type(TId,Type), |
130 | | new_node_scope(Type,NodeId,Nodes), |
131 | | avl_store(Id,In,nodes(NodeId,Nodes,rw),Out), |
132 | | texpr_add_node_infos(TId,NodeId,NTId,IIn,IOut). |
133 | | |
134 | | % look up the node of an identifier |
135 | | identifier_node_lookup(Id,Env,NodeId,Nodes,Mode) :- |
136 | | (avl_fetch(Id,Env,nodes(NodeId,Nodes,Mode)) -> true |
137 | | ; add_warning(kodkod,'Could not lookup identifier: ',Id),fail). |
138 | | |
139 | | % set the complete environment to read-only, |
140 | | % read-only nodes are not used for output of constraint nodes |
141 | | identifier_nodes_readonly(OldEnv,NewEnv) :- |
142 | | avl_map(set_readonly,OldEnv,NewEnv). |
143 | | set_readonly(nodes(Id,Nodes,_Mode),nodes(Id,Nodes,ro)). |
144 | | |
145 | | compute_integer_set('INTEGER',-inf,inf). |
146 | | compute_integer_set('NATURAL',0,inf). |
147 | | compute_integer_set('NATURAL1',1,inf). |
148 | | compute_integer_set('INT',Min,Max) :- |
149 | | get_preference(minint,Min), |
150 | | get_preference(maxint,Max). |
151 | | compute_integer_set('NAT',0,Max) :- |
152 | | get_preference(maxint,Max). |
153 | | compute_integer_set('NAT1',1,Max) :- |
154 | | get_preference(maxint,Max). |
155 | | |
156 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
157 | | % computation on information |
158 | | |
159 | | info_conjunct(irange(X,Y),irange(A,B),Result) :- |
160 | | Max infis max(X,A), Min infis min(Y,B), |
161 | | normalise_irange(irange(Max,Min),Result). |
162 | | info_disjunct(irange(X,Y),irange(A,B),Result) :- |
163 | | ( is_bottom(irange(X,Y)) -> Result = irange(A,B) |
164 | | ; is_bottom(irange(A,B)) -> Result = irange(X,Y) |
165 | | ; |
166 | | Min infis min(X,A), Max infis max(Y,B), |
167 | | normalise_irange(irange(Min,Max),Result)). |
168 | | |
169 | | normalise_irange(In,Result) :- |
170 | | (is_bottom(In) -> Result=irange(0,-1) ; Result=In). |
171 | | is_bottom(irange(A,B)) :- infless(B,A). |
172 | | |
173 | | |
174 | | has_more_information(irange(X,_Y),irange(A,_B)) :- infless(A,X),!. |
175 | | has_more_information(irange(_X,Y),irange(_A,B)) :- infless(Y,B),!. |
176 | | has_more_information(set(A),elem(B)) :- has_more_information(A,B). |
177 | | has_more_information(left(A),left(B)) :- has_more_information(A,B). |
178 | | has_more_information(right(A),right(B)) :- has_more_information(A,B). |
179 | | |
180 | | % what kind of information may be stored in a node of a certain data type |
181 | | get_type_scopes(Type,Scopes) :- |
182 | | findall(S,get_type_scope(Type,S),Scopes1), |
183 | | sort([possible_values|Scopes1],Scopes). |
184 | | get_type_scope(integer,interval). |
185 | | get_type_scope(set(T),elem(S)) :- |
186 | ? | get_type_scope(T,S). |
187 | | get_type_scope(set(_),card). |
188 | | get_type_scope(seq(S),T) :- |
189 | | get_type_scope(set(couple(integer,S)),T). |
190 | | get_type_scope(couple(A,_),left(S)) :- |
191 | ? | get_type_scope(A,S). |
192 | | get_type_scope(couple(_,B),right(S)) :- |
193 | | get_type_scope(B,S). |
194 | | |
195 | | % get the top element of a scope |
196 | | get_scope_top(interval,irange(-inf,inf)). |
197 | | get_scope_top(possible_values,irange(0,inf)). |
198 | | get_scope_top(elem(S),T) :- get_scope_top(S,T). |
199 | | get_scope_top(left(S),T) :- get_scope_top(S,T). |
200 | | get_scope_top(right(S),T) :- get_scope_top(S,T). |
201 | | get_scope_top(card,irange(0,inf)). |
202 | | |
203 | | get_scope_bottom(interval,irange(0,-1)). |
204 | | get_scope_bottom(possible_values,T) :- get_scope_bottom(interval,T). |
205 | | get_scope_bottom(elem(S),T) :- get_scope_bottom(S,T). |
206 | | get_scope_bottom(left(S),T) :- get_scope_bottom(S,T). |
207 | | get_scope_bottom(right(S),T) :- get_scope_bottom(S,T). |
208 | | get_scope_bottom(card,T) :- get_scope_bottom(interval,T). |
209 | | |
210 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
211 | | % operations on possible infinite numbers |
212 | | %irange_union(irange(Am,AM2),irange(Bm,BM2),irange(Cm,CM2)) :- |
213 | | % Cm infis min(Am,Bm), CM2 infis max(AM2,BM2). |
214 | | |
215 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
216 | | % new extract |
217 | | |
218 | | new_node_scope(Type,Id,Nodes) :- |
219 | | new_node2(Id),get_type_scopes(Type,Scopes), |
220 | | findall(S:Id,member(S,Scopes),Nodes). |
221 | | |
222 | | new_node2(Id):- |
223 | | retract(nodecounter(Id)), |
224 | | Id2 is Id+1, assertz(nodecounter(Id2)). |
225 | | |
226 | | extract_nodes(TExpr,NodeEnv,Nodes,Mode,NTExpr,In,Out) :- |
227 | | get_texpr_expr(TExpr,Expr), |
228 | | extract_nodes2(Expr,TExpr,NodeEnv,Nodes,Mode,NTExpr,In,Inter), |
229 | | add_type_constraints(TExpr,Nodes,Inter,Out), |
230 | | store_debug_info(Nodes,TExpr). |
231 | | extract_nodes2(identifier(Id),TExpr,NodeEnv,Nodes,Mode,NTExpr,In,Out) :- !, |
232 | | identifier_node_lookup(Id,NodeEnv,NodeId,Nodes,Mode),!, |
233 | | texpr_add_node_infos(TExpr,NodeId,NTExpr,In,Out). |
234 | | extract_nodes2(conjunct(A,B),TExpr,NodeEnv,[],rw,NTExpr,In,Out) :- !, |
235 | | get_texpr_info(TExpr,Infos), |
236 | | create_texpr(conjunct(NA,NB),pred,Infos,NTExpr), |
237 | | extract_nodes(A,NodeEnv,_NodesA,_ModeA,NA,In,Inter), |
238 | | extract_nodes(B,NodeEnv,_NodesB,_ModeB,NB,Inter,Out). |
239 | | extract_nodes2(forall(Ids,Pre,Cond),TExpr,NodeEnv,[],rw,NTExpr,In,Out) :- !, |
240 | | get_texpr_info(TExpr,Info), |
241 | | create_texpr(forall(NIds,NPre,NCond),pred,Info,NTExpr), |
242 | | identifier_nodes_readonly(NodeEnv,SubEnv1), |
243 | | create_identifier_nodes2(Ids,NIds,SubEnv1,SubEnv,In,Inter1), |
244 | | extract_nodes(Pre,SubEnv,_NodesPre,_ModePre,NPre,Inter1,Inter2), |
245 | | identifier_nodes_readonly(SubEnv,ROEnv), |
246 | | extract_nodes(Cond,ROEnv,_NodesCond,_ModeCond,NCond,Inter2,Out). |
247 | | extract_nodes2(exists(Ids,Cond),TExpr,NodeEnv,[],rw,NTExpr,In,Out) :- !, |
248 | | get_texpr_info(TExpr,Info), |
249 | | create_texpr(exists(NIds,NCond),pred,Info,NTExpr), |
250 | | create_identifier_nodes2(Ids,NIds,NodeEnv,SubEnv,In,Inter1), |
251 | | extract_nodes(Cond,SubEnv,_NodesCond,_ModeCond,NCond,Inter1,Out). |
252 | | extract_nodes2(general_sum([TId],TPred,TExpr),TSigma,NodeEnv,Nodes,rw,NTSigma,In,Out) :- !, |
253 | | nodes_for_general_sum_or_mult(mult,TId,TPred,TExpr,TSigma,NodeEnv,Nodes,NTSigma,In,Out). |
254 | | extract_nodes2(general_product([TId],TPred,TExpr),TSigma,NodeEnv,Nodes,rw,NTSigma,In,Out) :- !, |
255 | | nodes_for_general_sum_or_mult(power_rule,TId,TPred,TExpr,TSigma,NodeEnv,Nodes,NTSigma,In,Out). |
256 | | extract_nodes2(comprehension_set(Ids,Cond),TExpr,NodeEnv,Nodes,rw,NTExpr,In,Out) :- !, |
257 | | get_texpr_info(TExpr,Info), |
258 | | get_texpr_type(TExpr,Type), |
259 | | identifier_nodes_readonly(NodeEnv,SubEnv1), |
260 | | create_identifier_nodes2(Ids,NIds,SubEnv1,SubEnv,In,Inter1), |
261 | | extract_nodes(Cond,SubEnv,_NodesCond,_ModeCond,NCond,Inter1,Inter2), |
262 | | new_node_scope(Type,NodeId,Nodes), |
263 | | compset_constraints(Nodes,Ids,SubEnv,Constraints), |
264 | | add_all(Constraints,Inter2,Inter3), |
265 | | add_node_infos(Info,NodeId,NInfo,Inter3,Out), |
266 | | create_texpr(comprehension_set(NIds,NCond),Type,NInfo,NTExpr). |
267 | | extract_nodes2(set_extension(L),TExpr,NodeEnv,LocalNodes,rw,NTExpr,In,Out) :- !, |
268 | | get_texpr_info(TExpr,Info), |
269 | | get_texpr_type(TExpr,Type), |
270 | | new_node_scope(Type,NodeId,LocalNodes), |
271 | | extract_set_extension(L,NodeEnv,LocalNodes,NTSubs,In,Inter1), |
272 | | CardNode = card:_, memberchk(CardNode,LocalNodes), |
273 | | length(L,MaxCard), |
274 | | add_all([constraint([],[CardNode],constant(irange(1,MaxCard)))], |
275 | | Inter1,Inter2), |
276 | | add_node_infos(Info,NodeId,NewInfo,Inter2,Out), |
277 | | create_texpr(set_extension(NTSubs),Type,NewInfo,NTExpr). |
278 | | extract_nodes2(value(Value),TExpr,NodeEnv,LocalNodes,RW,NTExpr,In,Out) :- |
279 | | get_texpr_info(TExpr,Infos), member(was_identifier(ID),Infos), |
280 | | \+ ground(Value),!, |
281 | | %print(treating_value_as_id(Value,ID)),nl, |
282 | | extract_nodes2(identifier(ID),TExpr,NodeEnv,LocalNodes,RW,NTExpr,In,Out). |
283 | | extract_nodes2(value(Value),TExpr,_NodeEnv,LocalNodes,rw,NTExpr,In,Out) :- nonvar(Value), |
284 | | !, |
285 | | get_texpr_info(TExpr,Info), |
286 | | get_texpr_type(TExpr,Type), |
287 | | new_node_scope(Type,NodeId,LocalNodes), |
288 | | findall(C,extract_constraint_for_value(Type,TExpr,LocalNodes,C),Constraints), |
289 | | add_all(Constraints,In,Inter), |
290 | | add_node_infos(Info,NodeId,NewInfo,Inter,Out), |
291 | | create_texpr(value(Value),Type,NewInfo,NTExpr). |
292 | | extract_nodes2(partition(S,Es),TExpr,NodeEnv,[],rw,NTExpr,In,Out) :- !, |
293 | | extract_nodes_l([S|Es],NodeEnv,[SNodes|EsNodeLists],_Modes,[NS|NEs],In,Inter1), |
294 | | % subset constraints |
295 | | findall(constraint(CI,CO,CC), |
296 | | (member(Scope:SId,SNodes), |
297 | | member(NodeList,EsNodeLists),member(Scope:Elem,NodeList), |
298 | | is_subset_extract(Scope:Elem,Scope:SId,CI,CO,CC)), |
299 | | SubsetConstraints), |
300 | | add_all(SubsetConstraints,Inter1,Inter2), |
301 | | % cardinality constraints |
302 | | memberchk(card:SNodeId,SNodes), |
303 | | findall(card:Elem2,(member(NodeList2,EsNodeLists),member(card:Elem2,NodeList2)), |
304 | | CardNodes), |
305 | | create_add_list_constraints(CardNodes,card:SNodeId,CardConstraints), |
306 | | add_all(CardConstraints,Inter2,Inter3), |
307 | | % union constraints |
308 | | findall(constraint(ElemNodes,[elem(Scope3):SId3],union), |
309 | | ( member(elem(Scope3):SId3,SNodes), |
310 | | findall(elem(Scope3):Elem3, |
311 | | (member(NodeList3,EsNodeLists),member(elem(Scope3):Elem3,NodeList3)), |
312 | | ElemNodes)), |
313 | | UnionConstraints), |
314 | | add_all(UnionConstraints,Inter3,Out), |
315 | | get_texpr_info(TExpr,Info), |
316 | | create_texpr(partition(NS,NEs),pred,Info,NTExpr). |
317 | | extract_nodes2(sequence_extension(L),TExpr,NodeEnv,LocalNodes,rw,NTExpr,In,Out) :- !, |
318 | | get_texpr_info(TExpr,Info), |
319 | | get_texpr_type(TExpr,Type), |
320 | | new_node_scope(Type,NodeId,LocalNodes), |
321 | | extract_seq_extension(L,NodeEnv,LocalNodes,NTSubs,In,Inter1), |
322 | | CardNode = card:_, memberchk(CardNode,LocalNodes), |
323 | | DomNode = elem(left(interval)):_, memberchk(DomNode,LocalNodes), |
324 | | length(L,Card), |
325 | | add_all([constraint([],[CardNode],constant(irange(Card,Card))), |
326 | | constraint([],[DomNode], constant(irange(1, Card)))], |
327 | | Inter1,Inter2), |
328 | | add_node_infos(Info,NodeId,NewInfo,Inter2,Out), |
329 | | create_texpr(sequence_extension(NTSubs),Type,NewInfo,NTExpr). |
330 | | extract_nodes2(empty_set,TExpr,_NodeEnv,Nodes,rw,NTExpr,In,Out) :- !, |
331 | | get_texpr_type(TExpr,Type), new_node_scope(Type,NodeId,Nodes), |
332 | | texpr_add_node_infos(TExpr,NodeId,NTExpr,In,Inter), |
333 | | get_type_scopes(Type,Scopes), |
334 | | findall(constraint([],[elem(S):NodeId],constant(Bottom)), |
335 | | (member(elem(S),Scopes),get_scope_bottom(S,Bottom)), |
336 | | Bottoms), |
337 | | add_all([constraint([],[card:NodeId],constant(irange(0,0)))|Bottoms],Inter,Out). |
338 | | extract_nodes2(Expr,TExpr,NodeEnv,Nodes,rw,NTExpr,In,Out) :- |
339 | | same_functor(Expr,Tmp), |
340 | ? | extract(Tmp,_,_,_,_),!, |
341 | | syntaxtransformation(Expr,Subs,[],NSubs,NExpr), |
342 | | copy_term((NSubs,NExpr),(NSubsRO,NExprRO)), |
343 | | copy_term((NSubs,NExpr),(NewSubs,NewExpr)), |
344 | | extract_nodes_l(Subs,NodeEnv,SubNodeLists,Modes,NewSubs,In,Inter), |
345 | | ignore_readonly_nodes(Modes,SubNodeLists,SubNodeListsRO), |
346 | | get_texpr_info(TExpr,Info), |
347 | | get_texpr_type(TExpr,Type), |
348 | | new_node_scope(Type,NodeId,Nodes), |
349 | | add_node_infos(Info,NodeId,NewInfos,Inter,Inter2), |
350 | | create_texpr(NewExpr,Type,NewInfos,NTExpr), |
351 | | findall(constraint(InputNodes,OutputNodes,Transition), |
352 | | ( extract(NExpr,NodeId,InputNodes,OrigOutputNodes,Transition), |
353 | | copy_term(extract(NExpr,NodeId,InputNodes,OrigOutputNodes,Transition), |
354 | | extract(NExprRO,NodeId,_,OutputNodes,Transition)), |
355 | | choose_one(SubNodeLists,SubNodeListsRO,NSubs,NSubsRO)), |
356 | | Constraints), |
357 | | %error_manager:print_error_span(TExpr),nl,print(Constraints),nl, |
358 | | add_all(Constraints,Inter2,Out). |
359 | | extract_nodes2(Expr,TExpr,NodeEnv,[],rw,NTExpr,In,Out) :- |
360 | | remove_bt(TExpr,_,NExpr,NTExpr), |
361 | | syntaxtransformation(Expr,Subs,Names,NSubs,NExpr), |
362 | | identifier_nodes_readonly(NodeEnv,SubEnv1), |
363 | | create_identifier_nodes2(Names,_NIds,SubEnv1,SubEnv,In,Inter1),!, |
364 | | extract_nodes_l(Subs,SubEnv,_SubNodeLists,_Modes,NSubs,Inter1,Out). |
365 | | extract_nodes2(_,TExpr,_,_,_,_,_,_) :- |
366 | | throw(failed_analysis(TExpr)). |
367 | | |
368 | | add_type_constraints(TExpr,Nodes,In,Out) :- |
369 | | get_texpr_type(TExpr,Type), |
370 | | findall(C, type_constraint(Type,Nodes,C), Constraints), |
371 | | add_all(Constraints,In,Out). |
372 | | |
373 | | type_constraint(Type,Nodes,constraint([],[possible_values:N],constant(irange(1,Card)))) :- |
374 | ? | member(possible_values:N,Nodes), |
375 | | max_type_cardinality(Type,Card). |
376 | | type_constraint(set(T),Nodes,constraint([],[card:N],constant(irange(0,Card)))) :- |
377 | ? | member(card:N,Nodes), |
378 | | max_type_cardinality(T,Card). |
379 | | type_constraint(integer,Nodes,constraint([interval:N],[possible_values:N],interval_possibilities)) :- |
380 | ? | member(interval:N,Nodes),member(possible_values:N,Nodes). |
381 | | |
382 | | max_type_cardinality(global(G),Card) :- |
383 | | % TODO: This should be switched off if we try to determine the cardinality of the deferred sets |
384 | | b_global_set_cardinality(G,Card). |
385 | | max_type_cardinality(boolean,2). |
386 | | max_type_cardinality(set(T),Card) :- |
387 | | max_type_cardinality(T,C1), |
388 | | % catch/3 to avoid overflow errors |
389 | | catch( Card is ceiling(2 ** C1), |
390 | | error(_,_), |
391 | | fail). |
392 | | max_type_cardinality(couple(A,B),Card) :- |
393 | | max_type_cardinality(A,CardA), |
394 | | max_type_cardinality(B,CardB), |
395 | | Card is CardA * CardB. |
396 | | |
397 | | add_existing_constraints(Info,NodeId,In,Out) :- |
398 | | memberchk(analysis(Analysis),Info),!, |
399 | | findall( constraint([],[Scope:NodeId],constant(Value)), |
400 | | member(Scope:Value,Analysis), |
401 | | Constraints), |
402 | | add_all(Constraints,In,Out). |
403 | | add_existing_constraints(_Info,_NodeId,In,In). |
404 | | |
405 | | extract_constraint_for_value(Type,Value,LocalNodes,constraint([],[NodeType:NodeId],Constant)) :- |
406 | ? | extract_constraint_for_value2(Type,Value,NodeType,Constant), |
407 | | memberchk(NodeType:NodeId,LocalNodes). |
408 | | extract_constraint_for_value2(set(_Type),Value,card,Constant) :- |
409 | | compute_integer(card(Value),Card), |
410 | | Constant = constant(irange(Card,Card)). |
411 | | extract_constraint_for_value2(set(integer),Value,elem(interval),constant(irange(Min,Max))) :- |
412 | | compute_integer(min(Value),Min), |
413 | | compute_integer(max(Value),Max). |
414 | | extract_constraint_for_value2(set(couple(TA,_)),Value,elem(left(DN)),Constant) :- |
415 | | compute_expression(domain(Value),set(TA),Dom), |
416 | | create_texpr(value(Dom),set(TA),[],BDom), |
417 | | extract_constraint_for_value2(set(TA),BDom,elem(DN),Constant). |
418 | | extract_constraint_for_value2(set(couple(_,TB)),Value,elem(right(RN)),Constant) :- |
419 | | compute_expression(range(Value),set(TB),Ran), |
420 | | create_texpr(value(Ran),set(TB),[],BRan), |
421 | | extract_constraint_for_value2(set(TB),BRan,elem(RN),Constant). |
422 | | |
423 | | compute_integer(Expr,Result) :- |
424 | | compute_expression(Expr,integer,int(Result)). |
425 | | compute_expression(Expr,Type,Result) :- |
426 | | empty_state(Empty), |
427 | | create_texpr(Expr,Type,[],BExpr), |
428 | | b_compute_expression_nowf(BExpr,Empty,Empty,Result,'predicate analysis',0). |
429 | | |
430 | | compset_constraints(SetNodes,Ids,NodeEnv,Constraints) :- |
431 | | lookup_identifier_nodes(Ids,NodeEnv,NodeLists), |
432 | | findall( C, |
433 | | (SetNode=elem(Scope):_SetNodeId, member(SetNode,SetNodes), |
434 | | compset_constraint(Scope,NodeLists,ElemNode), |
435 | | member(C, [constraint([SetNode],[ElemNode],id), |
436 | | constraint([ElemNode],[SetNode],id)])), |
437 | | Constraints). |
438 | | compset_constraint(left(Scope),NodeLists,Node) :- !, |
439 | | last(Begin,_,NodeLists), |
440 | | compset_constraint(Scope,Begin,Node). |
441 | | compset_constraint(right(Scope),NodeLists,Node) :- !, |
442 | | last(_,Last,NodeLists), |
443 | | compset_constraint(Scope,[Last],Node). |
444 | | compset_constraint(Scope,[Nodes],Scope:NodeId) :- |
445 | | memberchk(Scope:NodeId,Nodes). |
446 | | |
447 | | lookup_identifier_nodes([],_Env,[]). |
448 | | lookup_identifier_nodes([TId|Irest],Env,[Nodes|Nrest]) :- |
449 | | get_texpr_id(TId,Id), |
450 | | identifier_node_lookup(Id,Env,_NodeId,Nodes,_Mode), |
451 | | lookup_identifier_nodes(Irest,Env,Nrest). |
452 | | |
453 | | /* |
454 | | extract_set_extension([L],NodeEnv,Nodes,[NTExpr],In,Out) :- !, |
455 | | extract_nodes(L,NodeEnv,Nodes,_Mode,NTExpr,In,Out). |
456 | | extract_set_extension([Elem|Rest],NodeEnv,TmpNodes,[NTExpr|NTrest],In,Out) :- |
457 | | extract_nodes(Elem,NodeEnv,ElemNodes,_Mode,NTExpr,In,Inter1), |
458 | | extract_set_extension(Rest,NodeEnv,RestNodes,NTrest,Inter1,Inter2), |
459 | | %append([TmpNodes,ElemNodes,RestNodes],Nodes), |
460 | | get_texpr_type(Elem,Type), |
461 | | new_node_scope(Type,NodeId,TmpNodes), |
462 | | add_node_infos([],NodeId,_,Inter2,Inter3), |
463 | | findall(C,( EN = Scope:_, |
464 | | RN = Scope:_, |
465 | | ON = Scope:_, |
466 | | member(EN,ElemNodes), |
467 | | memberchk(RN,RestNodes), |
468 | | memberchk(ON,TmpNodes), |
469 | | member(C,[constraint([EN,RN],[ON],union), |
470 | | constraint([ON],[EN],id), |
471 | | constraint([ON],[RN],id)])), |
472 | | Constraints), |
473 | | add_all(Constraints,Inter3,Out). |
474 | | */ |
475 | | extract_set_extension(Elements,NodeEnv,SeqNodes,NElements,In,Out) :- |
476 | | extract_extension_nodes(Elements,NodeEnv,ElemNodes,NElements,In,Inter), |
477 | | findall(n(Scope,elem(Scope):N),member(elem(Scope):N,SeqNodes),Scopes), |
478 | | extract_ext_nodes2(Scopes,ElemNodes,Constraints), |
479 | | add_all(Constraints,Inter,Out). |
480 | | |
481 | | extract_seq_extension(Elements,NodeEnv,SeqNodes,NElements,In,Out) :- |
482 | | extract_extension_nodes(Elements,NodeEnv,ElemNodes,NElements,In,Inter), |
483 | | findall(n(Scope,elem(right(Scope)):N),member(elem(right(Scope)):N,SeqNodes),Scopes), |
484 | | extract_ext_nodes2(Scopes,ElemNodes,Constraints), |
485 | | add_all(Constraints,Inter,Out). |
486 | | |
487 | | extract_ext_nodes2([],_ElemNodes,[]). |
488 | | extract_ext_nodes2([Scope|Srest],ElemNodes,Constraints) :- |
489 | | extract_ext_nodes3(Scope,ElemNodes,C1), |
490 | | append(C1,C2,Constraints), |
491 | | extract_ext_nodes2(Srest,ElemNodes,C2). |
492 | | extract_ext_nodes3(n(Scope,SeqNode),ElemNodes,[Union|Constraints]) :- |
493 | | findall(Scope:E, |
494 | | ( member(NodeList,ElemNodes), memberchk(Scope:E,NodeList)), |
495 | | AllElemNodes), |
496 | | Union = constraint(AllElemNodes,[SeqNode],union), |
497 | | % for set extensions like { min(0..7) } this generates an empty list AllElemenNodes; TO DO: fix |
498 | | findall(constraint([SeqNode],[E],id), member(E,AllElemNodes), Constraints). |
499 | | |
500 | | extract_extension_nodes([],_NodeEnv,[],[],X,X). |
501 | | extract_extension_nodes([Elem|Rest],NodeEnv,[ElemNodes|RestNodes],[NElem|NRest],In,Out) :- |
502 | | extract_nodes(Elem,NodeEnv,ElemNodes,_Mode,NElem,In,Inter), |
503 | | extract_extension_nodes(Rest,NodeEnv,RestNodes,NRest,Inter,Out). |
504 | | |
505 | | |
506 | | texpr_add_node_infos(TExpr,NodeId,NTExpr,In,Out) :- |
507 | | get_texpr_expr(TExpr,Expr), |
508 | | get_texpr_type(TExpr,Type), |
509 | | get_texpr_info(TExpr,Info), |
510 | | add_node_infos(Info,NodeId,NewInfo,In,Inter), |
511 | | create_texpr(Expr,Type,NewInfo,NTExpr), |
512 | | add_existing_constraints(Info,NodeId,Inter,Out). |
513 | | add_node_infos(OldInfo,NodeId,NewInfos, |
514 | | (Constraints,[infonode(NodeId,NewInfos,OldInfo)|Out]), |
515 | | (Constraints,Out)). |
516 | | |
517 | | add_all([],In,In). |
518 | | add_all([E|Rest],([E|In],Infonodes),Out) :- |
519 | | add_all(Rest,(In,Infonodes),Out). |
520 | | extract_nodes_l([],_,[],[],[],In,In). |
521 | | extract_nodes_l([TExpr|ERest],NodeEnv,[Node|NRest],[Mode|MRest],[NTExpr|NTRest],In,Out) :- |
522 | | extract_nodes(TExpr,NodeEnv,Node,Mode,NTExpr,In,Inter), |
523 | | extract_nodes_l(ERest,NodeEnv,NRest,MRest,NTRest,Inter,Out). |
524 | | choose_one([],[],[],[]). |
525 | | choose_one([List1|L1Rest],[List2|L2Rest],[Elem1|E1Rest],[Elem2|E2Rest]) :- |
526 | ? | choose_one2(List1,List2,Elem1,Elem2), |
527 | ? | choose_one(L1Rest,L2Rest,E1Rest,E2Rest). |
528 | | choose_one2(NodeList,NodeListRO,Elem,ElemRO) :- |
529 | ? | (Elem == (*) -> true ; nth1(N,NodeList,Elem), nth1(N,NodeListRO,ElemRO)). |
530 | | ignore_readonly_nodes([],[],[]). |
531 | | ignore_readonly_nodes([Mode|MRest],[InNodes|InRest],[OutNodes|OutRest]) :- |
532 | | ignore_readonly_nodes2(Mode,InNodes,OutNodes), |
533 | | ignore_readonly_nodes(MRest,InRest,OutRest). |
534 | | ignore_readonly_nodes2(ro,Nodes,Ignored) :- ignore_readonly_nodes3(Nodes,Ignored). |
535 | | ignore_readonly_nodes2(rw,Nodes,Nodes). |
536 | | ignore_readonly_nodes3([],[]). |
537 | | ignore_readonly_nodes3([Scope:_Node|NRest],[Scope:ignored|IRest]) :- |
538 | | ignore_readonly_nodes3(NRest,IRest). |
539 | | |
540 | | nodes_for_general_sum_or_mult(Rule,TId,TPred,TExpr,TOrig,NodeEnv,Nodes,TNew,In,Out) :- |
541 | | identifier_nodes_readonly(NodeEnv,SubEnv1), |
542 | | create_identifier_nodes2([TId],[NTId],SubEnv1,SubEnv,In,Inter1), |
543 | | extract_nodes_l([TId,TPred,TExpr],SubEnv,[IdNodes,_PredNodes,ExprNodes],_Modes, |
544 | | [NTId,NTPred,NTExpr],Inter1,Inter2), |
545 | | get_texpr_info(TOrig,Info), |
546 | | memberchk(interval:ExprNodeId,ExprNodes), |
547 | | memberchk(possible_values:IdId,IdNodes), |
548 | | new_node_scope(integer,NodeId,Nodes), |
549 | | add_node_infos(Info,NodeId,NInfo,Inter2,Inter3), |
550 | | add_all([constraint([possible_values:IdId,interval:ExprNodeId], |
551 | | [interval:NodeId],Rule)], |
552 | | Inter3,Out), |
553 | | get_texpr_expr(TOrig,OrigExpr),functor(OrigExpr,Functor,_), |
554 | | NExpr =.. [Functor,[NTId],NTPred,NTExpr], |
555 | | create_texpr(NExpr,integer,NInfo,TNew). |
556 | | |
557 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
558 | | % extract constraints from syntax elements |
559 | | % extract(NExpr,NodeId,InputNodes,OrigOutputNodes,Transition) |
560 | | |
561 | | extract(Expr,R,X,Y,Op) :- |
562 | ? | extract_symmetric(Expr,R,X1,Y1,Op1), |
563 | ? | symmetric_constraint(Op1,Op2), |
564 | | ( Op=Op1,X=X1,Y=Y1 |
565 | | ; Op=Op2,X=Y1,Y=X1). |
566 | | |
567 | | extract(integer(I),R,[],[interval:R],constant(irange(I,I))). |
568 | | extract(value(IV),R,[],[interval:R],constant(irange(I,I))) :- nonvar(IV), IV = int(I). % TO DO: other vals |
569 | | extract(interval(interval:A,interval:B),R,[interval:A,interval:B],[card:R],interval_card). |
570 | | extract(member(S:A,elem(S):B),_,[elem(S):B],[S:A],id). |
571 | | extract(subset(elem(S):A,elem(S):B),_,[elem(S):B],[elem(S):A],id). |
572 | | extract(subset_strict(elem(S):A,elem(S):B),_,[elem(S):B],[elem(S):A],id). |
573 | ? | extract(add(interval:A,interval:B),R,I,O,C) :- addition_constraint(interval:A,interval:B,interval:R,I,O,C). |
574 | ? | extract(minus(interval:A,interval:B),R,I,O,C) :- addition_constraint(interval:B,interval:R,interval:A,I,O,C). |
575 | | extract(minus(interval:A,interval:B),R,[interval:R,interval:B],[interval:A],add). |
576 | | extract(minus(interval:A,interval:B),R,[interval:A,interval:R],[interval:B],sub). |
577 | | extract(multiplication(interval:A,interval:B),R,[interval:A,interval:B],[interval:R],mult). |
578 | | extract(div(interval:A,interval:B),R,[interval:A,interval:B],[interval:R],divrange). |
579 | | extract(floored_div(interval:A,interval:B),R,[interval:A,interval:B],[interval:R],divrange). % TO DO: provide proper treatment of floored_div |
580 | | extract(modulo(*,interval:B),R,[interval:B],[interval:R],lt). |
581 | | extract(modulo(*,*),R,[],[interval:R],constant(irange(0,inf))). |
582 | | extract(power_of(interval:Base,interval:Exp),R,[interval:Base,interval:Exp],[interval:R],power). |
583 | | extract(integer_set(Set),R,[],[elem(interval):R],constant(irange(Min,Max))) :- |
584 | | compute_integer_set(Set,Min,Max). |
585 | | extract(bool_set,R,[],[card:R],constant(irange(2,2))). |
586 | | extract(pow1_subset(*),R,[],[elem(card):R],constant(irange(1,inf))). |
587 | | extract(fin_subset(A),R,AL,RL,Op) :- extract(pow_subset(A),R,AL,RL,Op). |
588 | | extract(fin1_subset(A),R,AL,RL,Op) :- extract(pow1_subset(A),R,AL,RL,Op). |
589 | ? | extract(partial_function(A,B),R,AL,RL,Op) :- extract(relations(A,B),R,AL,RL,Op). |
590 | ? | extract(total_function(A,B),R,AL,RL,Op) :- extract(relations(A,B),R,AL,RL,Op). |
591 | ? | extract(partial_injection(A,B),R,AL,RL,Op) :- extract(partial_function(A,B),R,AL,RL,Op). |
592 | ? | extract(total_injection(A,B),R,AL,RL,Op) :- extract(total_function(A,B),R,AL,RL,Op). |
593 | ? | extract(partial_surjection(A,B),R,AL,RL,Op) :- extract(partial_function(A,B),R,AL,RL,Op). |
594 | ? | extract(total_surjection(A,B),R,AL,RL,Op) :- extract(total_function(A,B),R,AL,RL,Op). |
595 | ? | extract(total_bijection(A,B),R,AL,RL,Op) :- extract(total_function(A,B),R,AL,RL,Op). |
596 | | extract(partial_bijection(A,B),R,AL,RL,Op) :- extract(partial_function(A,B),R,AL,RL,Op). |
597 | | extract(cartesian_product(card:A,card:B),R,[card:A,card:B],[card:R],mult). |
598 | | extract(cartesian_product(card:A,card:B),R,[card:R,card:A],[card:B],cart_div). |
599 | | extract(cartesian_product(card:A,card:B),R,[card:R,card:B],[card:A],cart_div). |
600 | | extract(set_subtraction(elem(S):A,*),R,[elem(S):A],[elem(S):R],id). |
601 | | extract(image(elem(right(S)):A,*),R,[elem(right(S)):A],[elem(S):R],id). |
602 | ? | extract(intersection(S:A,*),R,I,O,C) :- is_subset_extract(S:R,S:A,I,O,C). |
603 | ? | extract(intersection(*,S:B),R,I,O,C) :- is_subset_extract(S:R,S:B,I,O,C). |
604 | | extract(union(elem(S):A,elem(S):B),R,[elem(S):A,elem(S):B],[elem(S):R],union). |
605 | ? | extract(union(S:A,*),R,I,O,C) :- is_subset_extract(S:A,S:R,I,O,C). |
606 | ? | extract(union(*,S:B),R,I,O,C) :- is_subset_extract(S:B,S:R,I,O,C). |
607 | | extract(domain_restriction(S:A,*),R,I,O,C) :- is_subset_extract(left(S):R,S:A,I,O,C). % ALWAYS FAILS !!!!! SOMETHING IS WRONG HERE; TODO: fix |
608 | ? | extract(domain_restriction(*,S:B),R,I,O,C) :- is_subset_extract(S:R,S:B,I,O,C). |
609 | ? | extract(domain_subtraction(*,S:B),R,I,O,C) :- is_subset_extract(S:R,S:B,I,O,C). |
610 | | extract(range_restriction(*,S:B),R,I,O,C) :- is_subset_extract(right(S):R,S:B,I,O,C). % ALWAYS FAILS !!!!! |
611 | ? | extract(range_restriction(S:A,*),R,I,O,C) :- is_subset_extract(S:R,S:A,I,O,C). |
612 | ? | extract(range_subtraction(S:A,*),R,I,O,C) :- is_subset_extract(S:R,S:A,I,O,C). |
613 | | extract(closure(elem(S):A),R,[elem(S):A],[elem(S):R],id). |
614 | | extract(composition(elem(left(S)):A,*),R,I,O,C) :- is_subset_extract(elem(left(S)):R,elem(left(S)):A,I,O,C). |
615 | | extract(composition(*,elem(right(S)):B),R,I,O,C) :- is_subset_extract(elem(right(S)):R,elem(right(S)):B,I,O,C). |
616 | | extract(function(elem(right(S)):A,*),R,[elem(right(S)):A],[S:R],id). |
617 | | extract(concat(card:A,card:B),R,I,O,C) :- addition_constraint(card:A,card:B,card:R,I,O,C). |
618 | | extract(concat(Right:A,Right:B),R,[Right:A,Right:B],[Right:R],union) :- Right=elem(right(_)). |
619 | | extract(concat(S:A,*),R,I,O,C) :- S=elem(right(_)),is_subset_extract(S:A,S:R,I,O,C). |
620 | | extract(concat(*,S:B),R,I,O,C) :- S=elem(right(_)),is_subset_extract(S:B,S:R,I,O,C). |
621 | | extract(concat(LI:A,LI:B),R,[LI:A,LI:B],[LI:R],concat_index) :- LI=elem(left(interval)). % TODO: More rules like addition |
622 | | extract(seq(*),R,[],[elem(elem(left(interval))):R],constant(irange(1,inf))). |
623 | | extract(seq1(A),R,I,O,C) :- extract(seq(A),R,I,O,C). |
624 | | extract(seq1(*),R,[],[elem(card):R],constant(irange(1,inf))). |
625 | | extract(size(A),R,I,O,C) :- extract(card(A),R,I,O,C). |
626 | | |
627 | | addition_constraint(A,B,R,[A,B],[R],add). |
628 | | addition_constraint(A,B,R,[R,B],[A],sub). |
629 | | addition_constraint(A,B,R,[R,A],[B],sub). |
630 | | |
631 | ? | is_subset_extract(A,B,I,O,C) :- extract(subset(A,B),_,I,O,C). |
632 | | |
633 | | extract_symmetric(equal(S:A,S:B),_,[S:A],[S:B],id). |
634 | | extract_symmetric(subset(card:A,card:B),_,[card:A],[card:B],gte). |
635 | | extract_symmetric(subset_strict(card:A,card:B),_,[card:A],[card:B],gt). |
636 | | extract_symmetric(less(interval:A,interval:B),_,[interval:B],[interval:A],lt). |
637 | | extract_symmetric(less_equal(interval:A,interval:B),_,[interval:B],[interval:A],lte). |
638 | | extract_symmetric(greater(interval:A,interval:B),_,[interval:B],[interval:A],gt). |
639 | | extract_symmetric(greater_equal(interval:A,interval:B),_,[interval:B],[interval:A],gte). |
640 | | extract_symmetric(unary_minus(interval:X),R,[interval:X],[interval:R],negate). |
641 | | extract_symmetric(card(card:X),R,[card:X],[interval:R],id). |
642 | | extract_symmetric(cartesian_product(elem(S):A,*),R,[elem(S):A],[elem(left(S)):R],id). |
643 | | extract_symmetric(cartesian_product(*,elem(S):B),R,[elem(S):B],[elem(right(S)):R],id). |
644 | | extract_symmetric(couple(S:A,*),R,[S:A],[left(S):R],id). |
645 | | extract_symmetric(couple(*,S:B),R,[S:B],[right(S):R],id). |
646 | | extract_symmetric(pow_subset(elem(S):A),R,[elem(S):A],[elem(elem(S)):R],id). |
647 | | extract_symmetric(pow_subset(card:A),R,[card:A],[elem(card):R],lte). |
648 | | extract_symmetric(pow1_subset(S),R,[A],[B],C) :- extract_symmetric(pow_subset(S),R,[A],[B],C). |
649 | | extract_symmetric(seq(A),R,[elem(S):A],[elem(elem(right(S))):R],id). |
650 | | extract_symmetric(relations(elem(S):A,*),R,[elem(S):A],[elem(elem(left(S))):R],id). |
651 | | extract_symmetric(relations(*,elem(S):B),R,[elem(S):B],[elem(elem(right(S))):R],id). |
652 | | extract_symmetric(interval(interval:A,interval:_),R,[interval:A],[elem(interval):R],gte). |
653 | | extract_symmetric(interval(interval:_,interval:B),R,[interval:B],[elem(interval):R],lte). |
654 | | extract_symmetric(partial_function(card:A,card:_),R,[card:A],[elem(card):R],lte). |
655 | | extract_symmetric(total_function(card:A,card:_),R,[card:A],[elem(card):R],id). |
656 | | extract_symmetric(partial_injection(card:_,card:B),R,[card:B],[elem(card):R],lte). |
657 | | extract_symmetric(total_injection(card:_,card:B),R,[card:B],[elem(card):R],lte). |
658 | | extract_symmetric(total_surjection(card:_,card:B),R,[card:B],[elem(card):R],gte). |
659 | | extract_symmetric(total_bijection(card:A,card:_),R,[card:A],[elem(card):R],id). |
660 | | extract_symmetric(total_bijection(card:_,card:B),R,[card:B],[elem(card):R],id). |
661 | | extract_symmetric(domain(elem(left(S)):A),R,[elem(left(S)):A],[elem(S):R],id). |
662 | | extract_symmetric(range(elem(right(S)):A),R,[elem(right(S)):A],[elem(S):R],id). |
663 | | extract_symmetric(reverse(elem(left(S)):A),R,[elem(left(S)):A],[elem(right(S)):R],id). |
664 | | extract_symmetric(reverse(elem(right(S)):A),R,[elem(right(S)):A],[elem(left(S)):R],id). |
665 | | extract_symmetric(reverse(card:A),R,[card:A],[card:R],id). |
666 | | extract_symmetric(set_subtraction(card:A,*),R,[card:A],[card:R],lte). % was erroneously gte, see test 2410 |
667 | | extract_symmetric(closure(elem(left(S)):A),R,[elem(left(S)):A],[elem(left(S)):R],id). |
668 | | extract_symmetric(closure(elem(right(S)):A),R,[elem(right(S)):A],[elem(right(S)):R],id). |
669 | | extract_symmetric(closure(card:A),R,[card:A],[card:R],gte). |
670 | | extract_symmetric(image(card:A,*),R,[card:R],[card:A],gte). |
671 | | extract_symmetric(first_of_pair(left(S):A),R,[left(S):A],[S:R],id). |
672 | | extract_symmetric(second_of_pair(right(S):A),R,[right(S):A],[S:R],id). |
673 | | |
674 | | symmetric_constraint(A,B) :- symmetric_constraint2(A,B),!. |
675 | ? | symmetric_constraint(A,B) :- symmetric_constraint2(B,A). |
676 | | symmetric_constraint2(id,id). |
677 | | symmetric_constraint2(lt,gt). |
678 | | symmetric_constraint2(lte,gte). |
679 | | symmetric_constraint2(negate,negate). |
680 | | |
681 | | |
682 | | % this seems to take up most of the time: |
683 | | create_constraint_tree(Constraints,Nodes,Tree) :- |
684 | | empty_avl(EmptyTree), |
685 | | sort(Constraints,SConstraints), % for SAT_Generator sorting reduces runtime from 22 sec to 18 sec for n=8; TO DO: investigate |
686 | | (debug:debug_mode(on) -> length(Constraints,Len), format('Processing ~w constraints~n',[Len]) ; true), |
687 | | create_constraint_tree1(SConstraints,[],UnsortedNodes,EmptyTree,Tree), |
688 | | (debug:debug_mode(on) -> length(UnsortedNodes,Len2), format('Sorting ~w nodes~n',[Len2]) ; true), |
689 | | sort(UnsortedNodes,Nodes), |
690 | | (debug:debug_mode(on) -> length(Nodes,Len3), format('Done sorting ~w nodes~n',[Len3]) ; true). |
691 | | %,set_prolog_flag(profiling,off),print_profile. |
692 | | |
693 | | create_constraint_tree1([],Nodes,Nodes,Tree,Tree). |
694 | | create_constraint_tree1([C|CRest],InNodes,OutNodes,InTree,OutTree) :- |
695 | | create_constraint_tree2(C,InNodes,InterNodes,InTree,InterTree), |
696 | | create_constraint_tree1(CRest,InterNodes,OutNodes,InterTree,OutTree). |
697 | | |
698 | | create_constraint_tree2(Constraint,InNodes,OutNodes,InTree,OutTree) :- |
699 | | Constraint = constraint(Input,Output,_Rule), |
700 | | ( all_nodes_ignored(Output) -> |
701 | | InTree = OutTree, InNodes=OutNodes |
702 | | ; |
703 | | add_all_to_constraint_tree(Input,Constraint,InTree,OutTree), |
704 | | %ord_union(Input,InNodes,Nodes),ord_union(Output,Nodes,OutNodes) % this can be very expensive ! |
705 | | append(Input,InNodes,Nodes), append(Output,Nodes,OutNodes) % just append the nodes and sort them later |
706 | | ). |
707 | | |
708 | | add_all_to_constraint_tree([],_,Tree,Tree). |
709 | | add_all_to_constraint_tree([Node|NRest],Constraint,InTree,OutTree) :- |
710 | | ( avl_change(Node,InTree,Old,InterTree,[Constraint|Old]) -> |
711 | | true |
712 | | ; |
713 | | avl_store(Node,InTree,[Constraint],InterTree)), |
714 | | add_all_to_constraint_tree(NRest,Constraint,InterTree,OutTree). |
715 | | |
716 | | all_nodes_ignored([]). |
717 | | all_nodes_ignored([_:ignored|Rest]) :- |
718 | | all_nodes_ignored(Rest). |
719 | | |
720 | | |
721 | | create_initial_information(Nodes,InitEnv) :- |
722 | | empty_avl(Empty), |
723 | | foldl(create_initial_information2,Nodes,Empty,InitEnv). |
724 | | create_initial_information2(Scope:Node,In,Out) :- |
725 | | get_scope_top(Scope,Top), |
726 | | avl_store(Scope:Node,In,Top,Out). |
727 | | |
728 | | apply_start_constraints(Constraints,ChangedNodes,In,Out) :- |
729 | | find_start_constraints(Constraints,Start), |
730 | | writetodot(1,[],Start,In), |
731 | | compute_constraints(Start,ChangedNodes1,[],In,Out), |
732 | | list_to_ord_set(ChangedNodes1,ChangedNodes). |
733 | | find_start_constraints(Constraints,Start) :- |
734 | | findall(constraint([],Output,Rule), |
735 | | member(constraint([],Output,Rule),Constraints), |
736 | | Start). |
737 | | |
738 | | do_analysis([],Counter,_MaxIter,_CTree,Values,Values) :- |
739 | | !,writetodot(Counter,[],[],Values). |
740 | | do_analysis([ChangedNode|CNRest],Counter,MaxIter,CTree,In,Out) :- |
741 | | Counter < MaxIter,!, |
742 | | (avl_fetch(ChangedNode,CTree,Constraints) -> true ; Constraints = []), |
743 | | writetodot(Counter,[ChangedNode|CNRest],Constraints,In), |
744 | ? | compute_constraints(Constraints,ChangedNodes1,[],In,Inter), |
745 | | list_to_ord_set(ChangedNodes1,ChangedNodes), |
746 | | ord_union(CNRest,ChangedNodes,NewChangedNodes), |
747 | | Counter2 is Counter+1, |
748 | ? | do_analysis(NewChangedNodes,Counter2,MaxIter,CTree,Inter,Out). |
749 | | do_analysis(_ChangedNodes,Counter,MaxIter,_CTree,Values,Values) :- |
750 | | Counter >= MaxIter. |
751 | | compute_constraints([],Changed,Changed,Values,Values). |
752 | | compute_constraints([Constraint|CRest],Cin,Cout,Vin,Vout) :- |
753 | ? | compute_constraint(Constraint,Cin,Cinter,Vin,Vinter), |
754 | ? | compute_constraints(CRest,Cinter,Cout,Vinter,Vout). |
755 | | compute_constraint(constraint(Input,Output,Rule),Cin,Cout,Vin,Vout) :- |
756 | | get_input_values(Input,Vin,InValues), |
757 | | same_length(Output,OutValues), |
758 | ? | call_rule(Rule,InValues,OutValues), |
759 | | apply_output(Output,OutValues,Cin,Cout,Vin,Vout). |
760 | | get_input_values([],_,[]). |
761 | | get_input_values([Node|NRest],Values,[Value|VRest]) :- |
762 | | avl_fetch(Node,Values,Value), |
763 | | get_input_values(NRest,Values,VRest). |
764 | | call_rule(Rule,InValues,OutValues) :- |
765 | | append_arguments(Rule,InValues,CallRule), |
766 | ? | if(rule(CallRule,OutValues), true, |
767 | | (add_warning(predicate_analysis,'No rule for: ',CallRule),fail)). |
768 | | apply_output([],[],Changed,Changed,Values,Values). |
769 | | apply_output([Node|NRest],[Value|VRest],Cin,Cout,Vin,Vout) :- |
770 | | ( Node = _:ignored -> |
771 | | Vin = Vinter, Cin=Cinter |
772 | | ; |
773 | | avl_change(Node,Vin,OldValue,Vinter,NewValue), |
774 | | info_conjunct(OldValue,Value,NewValue), |
775 | | ( has_more_information(NewValue,OldValue) -> |
776 | | Cin = [Node|Cinter] |
777 | | ; |
778 | | Cin = Cinter)), |
779 | | apply_output(NRest,VRest,Cinter,Cout,Vinter,Vout). |
780 | | |
781 | | append_arguments(Orig,Args,Result) :- |
782 | | Orig =.. FA, |
783 | | append_arguments2(FA,Args,AllArgs), |
784 | | Result =.. AllArgs. |
785 | | append_arguments2([F|FA],Args,AllArgs) :- |
786 | | multi_rule(F),!, |
787 | | append([F|FA],[Args],AllArgs). |
788 | | append_arguments2(FA,Args,AllArgs) :- |
789 | | append(FA,Args,AllArgs). |
790 | | |
791 | | |
792 | | create_add_list_constraints([],_Result,Constraints) :- !,Constraints=[]. |
793 | | create_add_list_constraints([Node],Result,Constraints) :- !, |
794 | | Constraints = [constraint([Node],[Result],id),constraint([Result],[Node],id)]. |
795 | | create_add_list_constraints(Nodes,Result,Constraints) :- !, |
796 | | create_add_list_constraints2(Nodes,Result,Constraints,[]). |
797 | | create_add_list_constraints2([A,B],Result) --> !, |
798 | | findall(constraint(I,O,C),addition_constraint(A,B,Result,I,O,C)). |
799 | | create_add_list_constraints2([A|Nodes],Result) --> |
800 | | {new_node_scope(integer,NodeId,_Nodes)}, |
801 | | findall(constraint(I,O,C),addition_constraint(A,interval:NodeId,Result,I,O,C)), |
802 | | create_add_list_constraints2(Nodes,interval:NodeId). |
803 | | |
804 | | |
805 | | |
806 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
807 | | % compute a single constraint |
808 | | |
809 | | rule(id(Info),[Info]). |
810 | | rule(constant(Info),[Info]). |
811 | | rule(add(A,B),[C]) :- interval_addition(A,B,C). |
812 | | rule(sub(A,B),[C]) :- interval_subtraction(A,B,C). |
813 | | rule(mult(A,B),[C]) :- interval_multiplication(A,B,C). |
814 | | rule(power(Base,Exp),[Result]) :- |
815 | | (power_rule(Base,Exp,Result) -> true ; Result = irange(-inf,inf)). |
816 | | rule(cart_div(irange(Cm,CM2),irange(Am,AM2)),[irange(Bm,BM2)]) :- % propagates C = A * B |
817 | | ( CM2 = inf -> % C can be infinite |
818 | | BM2 = inf |
819 | | ; Am = 0, % the set A can be empty |
820 | | number(Cm),Cm<1 -> % set C can be empty |
821 | | BM2 = inf % B can be arbitrarily larger and still yield an empty A*B |
822 | | ; Am = 0 -> % case: set C cannot be empty, actually Am is 1 |
823 | | BM2 = CM2 % B can be as large as the maximum size of C |
824 | | ; Am = inf -> % A is infinite |
825 | | BM2 = 0 % as CM2 is not inf, C cannot be infinite, only solution is B to be empty |
826 | | ; |
827 | | BM2 infis (CM2+Am-1) // Am % e.g., for CM2=3 and Am=2 we obtain 2 ?? Shouldnt we use CM2 // Am ?? |
828 | | ), |
829 | | ( number(Cm),Cm<1 -> % set C can be empty |
830 | | Bm = 0 % B can also be empty |
831 | | ; AM2 = 0 -> % A has to be empty; this means C is empty and we have a contradiction |
832 | | Bm is -1 % try force contradiction |
833 | | ; AM2 = inf -> % A can be infinite, but C is definitely not empty |
834 | | Bm = 1 % B must have at least one element |
835 | | ; Cm = inf -> Bm = inf % C has to be infinite, but A is finite |
836 | | ; AM2 > Cm -> Bm = 1 % B must have at least one element, as C is not empty |
837 | | ; |
838 | | Bm infis Cm // AM2). % minimal size of B is C divided by maximal size of A |
839 | | % TO DO: can be improved, e.g., for :kodkod A = {1,2} & avl = {1|->2, 1|->3, 2|->2, 2|->3, 4|->2} & avl = A*B |
840 | | % print(rule_result(cart_div(irange(Cm,CM2),irange(Am,AM2)),[irange(Bm,BM2)])),nl. |
841 | | rule(negate(A),[C]) :- interval_negation(A,C). |
842 | | rule(lt(irange(_,AM2)),[irange(-inf,CM2)]) :- CM2 infis AM2-1. |
843 | | rule(lte(irange(_,AM2)),[irange(-inf,AM2)]). |
844 | | rule(gt(irange(Am,_)),[irange(Cm,inf)]) :- Cm infis Am+1. |
845 | | rule(gte(irange(Am,_)),[irange(Am,inf)]). |
846 | | rule(interval_card(irange(Am,AM2),irange(Bm,BM2)),[irange(Cm,CM2)]) :- |
847 | | Cm infis max(Bm-AM2+1,0), CM2 infis max(BM2-Am+1,0). |
848 | | rule(interval_possibilities(irange(Am,AM2)),[irange(0,M)]) :- |
849 | | M infis max(AM2-Am+1,0). |
850 | | rule(union([A|Rest]),[C]) :- union2(Rest,A,C). |
851 | | rule(union([]),[irange(-inf,inf)]) :- print(empty_kodkod_union_constraint),nl. % TO DO: check if ok !! |
852 | | % happens e.g., for set extensions like { min(0..7) } ; should not occur |
853 | | rule(divrange(irange(Am,AM2),irange(Bm,BM2)),[C]) :- C = irange(MinC,MaxC), |
854 | | div_rule(Am,AM2,Bm,BM2,MinC,MaxC), |
855 | | format('Division propagation: ~w..~w / ~w..~w ---> ~w~n',[Am,AM2,Bm,BM2,C]). |
856 | | rule(concat_index(irange(_,AM2),irange(_,BM2)),[irange(1,CM2)]) :- |
857 | | (number(AM2),number(BM2) -> CM2 is AM2+BM2 ; CM2 = inf). |
858 | | |
859 | | |
860 | | :- assert_must_succeed((div_rule(1,100,1,2,MinC,MaxC), MinC == 0, MaxC == 100)). |
861 | | :- assert_must_succeed((div_rule(100,200,1,50,MinC,MaxC), MinC == 2, MaxC == 200)). |
862 | | :- assert_must_succeed((div_rule(-2,100,1,50,MinC,MaxC), MinC == -2, MaxC == 100)). |
863 | | :- assert_must_succeed((div_rule(-2,100,0,50,MinC,MaxC), MinC == -2, MaxC == 100)). % with try_find_abort we would get: |
864 | | % :- assert_must_succeed((div_rule(-2,100,0,50,MinC,MaxC), MinC == -inf, MaxC == inf)). % above with try_find_abort |
865 | | |
866 | | % propagate division Am..AM2 / Bm..BM2 --> C |
867 | | div_rule(Am,AM2,Bm,BM2,MinC,MaxC) :- number(Bm), Bm >= 0,!, % division by positive number |
868 | | (Am = -inf -> MinC = -inf |
869 | | ; Am < 0, Bm=0 -> MinC = Am % if try_find_abort is true we could set MinC to -inf; here we suppose division by 0 will not occur or not produce a value |
870 | | ; Am < 0 -> MinC is Am // Bm % a possible lowest value is obtained by the largest absolute (neg) value |
871 | | ; BM2=inf -> MinC is 0 % Am is positive ; we can divide Am by an arbitrarily large number to reach 0 |
872 | | ; MinC is Am // BM2 % minimum is dividing min of A by maximum of B |
873 | | ), |
874 | | (AM2 = inf -> MaxC = inf |
875 | | ; BM2=inf, AM2<0 -> MinC is 0 % Am is positive ; we can divide Am by an arbitrarily large number to reach 0 |
876 | | ; AM2 < 0 -> MaxC is AM2 // BM2 % a possible max value is obtained by the smallest absolute (neg) value |
877 | | ; Bm=0 -> MaxC = AM2 % if try_find_abort is true we could set MaxC to inf |
878 | | ; MaxC is AM2 // Bm % maximum is dividing max of A by min of B |
879 | | ). |
880 | | div_rule(Am,AM2,_Bm,_BM2,MinC,MaxC) :- number(Am),number(AM2), !, % TODO: improve and check |
881 | | M is max(abs(Am),abs(AM2)), % if try_find_abort is true we could divide by 0 and set to -inf .. inf |
882 | | MinC is -M, MaxC = M. |
883 | | div_rule(_,_,_,_,-inf,inf). |
884 | | |
885 | | union2([],Acc,R) :- !,Acc=R. |
886 | | union2([H|T],Acc,R) :- info_disjunct(H,Acc,I),union2(T,I,R). |
887 | | |
888 | | power_rule(irange(Basem,BaseM2),irange(Expm,ExpM2),irange(Rm,RM2)) :- |
889 | | number(Basem),Basem > 0, |
890 | | number(Expm),Expm >= 0, |
891 | | Rm is floor( Basem ** Expm), |
892 | | ((BaseM2=inf; ExpM2=inf) -> RM2=inf ; RM2 is floor(BaseM2 ** ExpM2)). |
893 | | |
894 | | multi_rule(union). |
895 | | |
896 | | |
897 | | insert_info(Infonodes,Values) :- |
898 | | create_info_lookup_table(Values,ValueTable), |
899 | | insert_info2(Infonodes,ValueTable). |
900 | | insert_info2([],_). |
901 | | insert_info2([infonode(Node,New,Old)|Rest],Values) :- |
902 | | find_all_node_information(Node,Values,Info), |
903 | | add_info(Info,Old,New), |
904 | | insert_info2(Rest,Values). |
905 | | |
906 | | :- load_files(library(system), [when(compile_time), imports([environ/2])]). |
907 | | :- if(environ(prob_safe_mode,true)). |
908 | | find_all_node_information(Node,_,_) :- \+ ground(Node), add_internal_error('Non-ground node: ',Node),fail. |
909 | | :- endif. |
910 | | find_all_node_information(Node,Values,Info) :- |
911 | | ( avl_fetch(Node,Values,RawInfos) -> filter_tops(RawInfos,Info) % used to call avl_member; but Node is ground |
912 | | ; Info = []). |
913 | | |
914 | | filter_tops([],[]). |
915 | | filter_tops([Scope:RI|RRest],Result) :- |
916 | | get_scope_top(Scope,Top), |
917 | | ( has_more_information(RI,Top) -> Result = [Scope:RI|RestResult] |
918 | | ; Result = RestResult), |
919 | | filter_tops(RRest,RestResult). |
920 | | |
921 | | add_info([],Old,Old) :- !. |
922 | | add_info(Info,Old,[analysis(Info)|Old]). |
923 | | |
924 | | create_info_lookup_table(Values,Nodes) :- |
925 | | avl_to_list(Values,ValueList), |
926 | | empty_avl(Empty), |
927 | | create_info_lookup_table2(ValueList,Empty,Nodes). |
928 | | create_info_lookup_table2([],Nodes,Nodes). |
929 | | create_info_lookup_table2([(Scope:Node)-I|Rest],In,Out) :- |
930 | | (avl_fetch(Node,In,Old) -> true ; Old = []), |
931 | | avl_store(Node,In,[Scope:I|Old],Inter), |
932 | | create_info_lookup_table2(Rest,Inter,Out). |
933 | | |
934 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
935 | | % dot output |
936 | | |
937 | | writetodot(Counter,ChangedNodes,ActiveConstraints,Values) :- |
938 | | debugging_enabled,!, |
939 | | debug_constraints(Constraints), |
940 | | writetodot(Counter,Constraints,ChangedNodes,ActiveConstraints,Values). |
941 | | writetodot(_Counter,_ChangedNodes,_ActiveConstraints,_Values). |
942 | | |
943 | | writetodot(Counter,Constraints,ChangedNodes,ActiveConstraints,Values) :- |
944 | | printnulls(3,Counter,CStr), |
945 | | ajoin(['debug-constraint-anim/constraints-',CStr,'.dot'],Filename), |
946 | | debug_format(19,'Writing Kodkod constraints to dot file: ~w~n',[Filename]), |
947 | | open(Filename,write,S), |
948 | | write(S,'digraph constraints {\n'), |
949 | | get_nodes(Constraints,INodes,CNodes), |
950 | | write_inodes(INodes,ChangedNodes,Values,S), |
951 | | write_cnodes(CNodes,INodes,ActiveConstraints,S), |
952 | | write(S,'}\n'), |
953 | | close(S). |
954 | | get_nodes(Constraints,INodes,RNodes) :- |
955 | | findall(CN, |
956 | | (member(constraint(I,O,_),Constraints), |
957 | | (member(CN,I);member(CN,O))), |
958 | | Nodes1), |
959 | | sort(Nodes1,Nodes2), |
960 | | number_nodes(Nodes2,1,INodes), |
961 | | number_nodes(Constraints,1,RNodes). |
962 | | number_nodes([],_,[]). |
963 | | number_nodes([N|Rest],Nr,[node(Nr,N)|RRest]) :- |
964 | | Nr2 is Nr+1, |
965 | | number_nodes(Rest,Nr2,RRest). |
966 | | write_inodes([],_,_,_). |
967 | | write_inodes([node(Nr,N)|Rest],ChangedNodes,Values,S) :- |
968 | | write(S,' i'),write(S,Nr),write(S,'[shape=box,'), |
969 | | ( ChangedNodes = [N|_] -> |
970 | | !,write(S,'fillcolor=green,style=filled,') |
971 | | ; member(N,ChangedNodes) -> |
972 | | !,write(S,'fillcolor=yellow,style=filled,') |
973 | | ; true), |
974 | | write(S,'label=\"'), |
975 | | write(S,N),write(S,'\\n'), |
976 | | ( debug_node(N,TExpr) -> |
977 | | translate_bexpression(TExpr,Text) |
978 | | ; |
979 | | Text = ('?')), |
980 | | write(S,Text),write(S,'\\n'), |
981 | | ( avl_fetch(N,Values,Value) -> |
982 | | write(S,Value) |
983 | | ; write(S,'?')), |
984 | | write(S,'\"];\n'), |
985 | | write_inodes(Rest,ChangedNodes,Values,S). |
986 | | write_cnodes([],_,_,_). |
987 | | write_cnodes([node(Nr,constraint(In,Out,R))|Rest],INodes,ActiveConstraints,S) :- |
988 | | write(S,' c'),write(S,Nr),write(S,'[shape=diamond,'), |
989 | | ( member(constraint(In,Out,R),ActiveConstraints) -> |
990 | | !,write(S,'fillcolor=lightblue,style=filled,') |
991 | | ; true), |
992 | | write(S,'label=\"'), |
993 | | write(S,R),write(S,'\"];\n'), |
994 | | write_inputs(In,INodes,Nr,S), |
995 | | write_outputs(Out,INodes,Nr,S), |
996 | | write_cnodes(Rest,INodes,ActiveConstraints,S). |
997 | | write_inputs([],_,_,_). |
998 | | write_inputs([I|Rest],INodes,RNr,S) :- |
999 | | member(node(INr,I),INodes),!, |
1000 | | write(S,' i'),write(S,INr),write(S,' -> '), |
1001 | | write(S,' c'),write(S,RNr),write(S,';\n'), |
1002 | | write_inputs(Rest,INodes,RNr,S). |
1003 | | write_outputs([],_,_,_). |
1004 | | write_outputs([O|Rest],INodes,RNr,S) :- |
1005 | | member(node(ONr,O),INodes),!, |
1006 | | write(S,' c'),write(S,RNr),write(S,' -> '), |
1007 | | write(S,' i'),write(S,ONr),write(S,';\n'), |
1008 | | write_outputs(Rest,INodes,RNr,S). |
1009 | | |
1010 | | printnulls(N,Number,Str) :- |
1011 | | number_codes(Number,NCodes), |
1012 | | length(NCodes,L), |
1013 | | R is N-L, |
1014 | | fillnulls(R,NCodes,Codes), |
1015 | | atom_codes(Str,Codes). |
1016 | | fillnulls(R,Codes,Codes) :- R =< 0,!. |
1017 | | fillnulls(R,NCodes,[48|Codes]) :- |
1018 | | R2 is R-1, |
1019 | | fillnulls(R2,NCodes,Codes). |
1020 | | |
1021 | | |
1022 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1023 | | |
1024 | | :- dynamic debug_predicate_analysis/0. |
1025 | | % assert this fact to enable dot visualization using writetodot below |
1026 | | |
1027 | | store_debug_info(I,T) :- debug_predicate_analysis,!,store_debug_info2(I,T). |
1028 | | store_debug_info(_,_). |
1029 | | |
1030 | | store_debug_info2([],_). |
1031 | | store_debug_info2([Node|Rest],TExpr) :- |
1032 | | assertz(debug_node(Node,TExpr)), |
1033 | | store_debug_info2(Rest,TExpr). |
1034 | | |
1035 | | |
1036 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1037 | | % test case specification for predicate analysis |
1038 | | |
1039 | | :- use_module(probsrc(junit_tests)). |
1040 | | |
1041 | | test_predicate_analysis :- |
1042 | | b_machine_has_constants, |
1043 | | b_get_machine_constants(Constants), |
1044 | | b_get_properties_from_machine(Properties), |
1045 | | b_get_static_assertions_from_machine(Assertions), |
1046 | | Assertions = [_|_],!, |
1047 | | test_predicate_analysis2(Constants,Properties,Assertions,Junits), |
1048 | | b_machine_name(MachineName), |
1049 | | print_junit([predicate_analysis,MachineName], Junits). |
1050 | | test_predicate_analysis :- |
1051 | | add_error(predicate_analysis, |
1052 | | 'Machine must have constants and assertions to be a valid predicate analysis test case.'), |
1053 | | fail. |
1054 | | test_predicate_analysis2(Constants,Properties,Assertions,Junits) :- |
1055 | | predicate_analysis_with_global_sets(Properties,[],Constants,TaggedConstants,_TaggedProperties),!, |
1056 | | test_predicate_analysis3(Assertions,TaggedConstants,Junits). |
1057 | | test_predicate_analysis2(_,_,_,[Junit]) :- |
1058 | | in_junit_mode,!, |
1059 | | pa_junit(setup,error('Predicate analysis failed'),Junit). |
1060 | | test_predicate_analysis2(_,_,_,_) :- |
1061 | | add_error(predicate_analysis, 'Predicate analysis of properties failed'), |
1062 | | fail. |
1063 | | test_predicate_analysis3([],_Constants,[]). |
1064 | | test_predicate_analysis3([Assertion|Arest],Constants,[Junit|Jrest]) :- |
1065 | | test_predicate_analysis_testcase(Assertion,Constants,Junit), |
1066 | | test_predicate_analysis3(Arest,Constants,Jrest). |
1067 | | |
1068 | | test_predicate_analysis_testcase(TAssertion,Constants,Junit) :- |
1069 | | translate_bexpression(TAssertion,Name), |
1070 | | ( catch( ( test_predicate_analysis_testcase1(TAssertion,Constants), |
1071 | | pa_junit(Name,pass,Junit)), |
1072 | | junit(Msg), |
1073 | | handle_error(Name,Msg,Junit)) -> true |
1074 | | ; |
1075 | | pa_junit(Name,error('Test failed.'),Junit)). |
1076 | | handle_error(Name,Msg,Junit) :- |
1077 | | in_junit_mode,!, |
1078 | | pa_junit(Name,error(Msg),Junit). |
1079 | | handle_error(Name,Msg,_Junit) :- |
1080 | | add_error(predicate_analysis, Name, Msg), fail. |
1081 | | |
1082 | | test_predicate_analysis_testcase1(TAssertion,Constants) :- |
1083 | | extract_predicate_analysis_testcases(TAssertion,[],Testcases), |
1084 | | maplist(run_testcase(Constants),Testcases). |
1085 | | run_testcase(Constants,pa_testcase(Id,Scope,ExpectedValue)) :- |
1086 | | formatsilent('Predicate analysis testcase: check if "~w" of "~w" is ~w~N', [Scope,Id,ExpectedValue]), |
1087 | | expected_value_is_top(ExpectedValue,Scope,IsTop), |
1088 | | extract_analysis(Id,Constants,Scope,FoundValue,IsTop), |
1089 | | ( FoundValue == ExpectedValue -> true |
1090 | | ; |
1091 | | ajoin(['unexpected information for constant ',Id,': expected ', ExpectedValue, |
1092 | | ', but was ', FoundValue],Msg), |
1093 | | throw(junit(Msg))). |
1094 | | expected_value_is_top(ExpectedValue,Scope,true) :- |
1095 | | ground(ExpectedValue),get_scope_top(Scope,ExpectedValue),!. |
1096 | | expected_value_is_top(_ExpectedValue,_Scope,false). |
1097 | | |
1098 | | extract_predicate_analysis_testcases(TExpr,QRefs,Testcases) :- |
1099 | | get_texpr_expr(TExpr,Expr), |
1100 | | extract_predicate_analysis_testcases2(Expr,QRefs,Testcases). |
1101 | | extract_predicate_analysis_testcases2(forall(Ids,TPre,Cond),QRefs,Testcases) :- |
1102 | | get_texpr_expr(TPre,Pre),introduce_qrefs(Pre,Ids,QRefs,SubQRefs), |
1103 | | extract_predicate_analysis_testcases(Cond,SubQRefs,Testcases). |
1104 | | extract_predicate_analysis_testcases2(equal(Ref,Spec),QRefs,[pa_testcase(Constant,Scope,irange(Value,Value))]) :- !, |
1105 | | extract_test_reference(Ref,QRefs,Constant,Scope), |
1106 | | extract_integer(Spec,Value). |
1107 | | extract_predicate_analysis_testcases2(less_equal(Ref,Spec),QRefs,[pa_testcase(Constant,Scope,irange(-inf,Value))]) :- !, |
1108 | | extract_test_reference(Ref,QRefs,Constant,Scope), |
1109 | | extract_integer(Spec,Value). |
1110 | | extract_predicate_analysis_testcases2(greater_equal(Ref,Spec),QRefs,[pa_testcase(Constant,Scope,irange(Value,inf))]) :- !, |
1111 | | extract_test_reference(Ref,QRefs,Constant,Scope), |
1112 | | extract_integer(Spec,Value). |
1113 | | extract_predicate_analysis_testcases2(member(Ref,TSpec),QRefs,[pa_testcase(Constant,Scope,Info)]) :- |
1114 | | get_texpr_expr(TSpec,Spec), |
1115 | | extract_test_reference(Ref,QRefs,Constant,Scope), |
1116 | | integer_interval_membership(Spec,Scope,Info),!. |
1117 | | extract_predicate_analysis_testcases2(not_member(Ref,TSpec),QRefs,[pa_testcase(Constant,Scope,Top)]) :- |
1118 | | get_texpr_expr(TSpec,empty_set), |
1119 | | extract_test_reference(Ref,QRefs,Constant,Scope), |
1120 | | get_scope_top(Scope,Top). |
1121 | | extract_predicate_analysis_testcases2(conjunct(A,B),QRefs,Testcases) :- !, |
1122 | | extract_predicate_analysis_testcases(A,QRefs,TestcasesA), |
1123 | | extract_predicate_analysis_testcases(B,QRefs,TestcasesB), |
1124 | | append(TestcasesA,TestcasesB,Testcases). |
1125 | | extract_predicate_analysis_testcases2(_Expr,_QRefs,_Testcases) :- |
1126 | | throw(junit('Unsupported test case pattern')). |
1127 | | |
1128 | | integer_interval_membership(empty_set,Scope,Bottom) :- get_scope_bottom(Scope,Bottom). |
1129 | | integer_interval_membership(interval(Min,Max),_Scope,irange(VMin,VMax)) :- |
1130 | | extract_integer(Min,VMin),extract_integer(Max,VMax). |
1131 | | integer_interval_membership(integer_set(I),_Scope,irange(Min,Max)) :- |
1132 | | compute_integer_set(I,Min,Max). |
1133 | | |
1134 | | introduce_qrefs(member(E2,Ref),Ids,QRefs,NewQRefs) :- |
1135 | | lookup_ref(Ref,QRefs,Path,Constant),append(Path,[elem],Path2), |
1136 | | couple_pathes(E2,Path2,Constant,Ids,NewQRefs,QRefs). |
1137 | | introduce_qrefs(equal(E2,Ref),Ids,QRefs,NewQRefs) :- |
1138 | | lookup_ref(Ref,QRefs,Path,Constant), |
1139 | | couple_pathes(E2,Path,Constant,Ids,NewQRefs,QRefs). |
1140 | | |
1141 | | couple_pathes(TExpr,Path,Constant,TIdentifiers) --> |
1142 | | {get_texpr_ids(TIdentifiers,Identifiers)}, |
1143 | | couple_pathes2(TExpr,Path,Constant,Identifiers). |
1144 | | couple_pathes2(TExpr,Path,Constant,Identifiers) --> |
1145 | | {get_texpr_expr(TExpr,Expr)},couple_pathes3(Expr,TExpr,Path,Constant,Identifiers). |
1146 | | couple_pathes3(identifier(Id),_,Path,Constant,Identifiers) --> |
1147 | | {memberchk(Id,Identifiers),!},[Id-ref(Path,Constant)]. |
1148 | | couple_pathes3(couple(A,B),_,Path,Constant,Identifiers) --> !, |
1149 | | {append(Path,[left],PathA),append(Path,[right],PathB)}, |
1150 | | couple_pathes2(A,PathA,Constant,Identifiers), |
1151 | | couple_pathes2(B,PathB,Constant,Identifiers). |
1152 | | couple_pathes3(_,TExpr,_Path,_Constant,_Identifiers,_,_) :- |
1153 | | translate_bexpression(TExpr,String), |
1154 | | atom_concat('Expected quantified identifier or couple in test specification, but was: ', |
1155 | | String,Msg), |
1156 | | throw(junit(Msg)). |
1157 | | |
1158 | | |
1159 | | lookup_ref(Expr,QRefs,Path,Constant) :- |
1160 | | get_texpr_id(Expr,Id), |
1161 | | memberchk(Id-ref(Path1,Constant1),QRefs),!, |
1162 | | Path1=Path,Constant1=Constant. |
1163 | | lookup_ref(Expr,_QRefs,[],Id) :- |
1164 | | get_texpr_id(Expr,Id). |
1165 | | |
1166 | | extract_analysis(Id,Constants,Scope,FoundValue,IsTop) :- |
1167 | | get_texpr_id(Constant,Id), |
1168 | | ( memberchk(Constant,Constants) -> |
1169 | | get_texpr_info(Constant,Info), |
1170 | | (memberchk(analysis(Analysis),Info) -> true ; Analysis=[]), |
1171 | | ( memberchk(Scope:FoundValue,Analysis) -> true |
1172 | | ; IsTop == true -> get_scope_top(Scope,FoundValue) /* no information expected */ |
1173 | | ; |
1174 | | ajoin(['No analysis information found for constant ',Id,', scope ',Scope],Msg), |
1175 | | print(extract_analysis(Id,Constants,Scope,FoundValue,IsTop)),nl, |
1176 | | throw(junit(Msg))) |
1177 | | ; |
1178 | | ajoin(['Constant ',Id,' not found'],Msg), |
1179 | | throw(junit(Msg))). |
1180 | | |
1181 | | extract_test_reference(TRef,QRefs,Id,Scope) :- |
1182 | | get_texpr_expr(TRef,Ref), |
1183 | | extract_test_reference2(Ref,QRefs,BaseScope,Id,ScopeList), |
1184 | | construct_scope(ScopeList,BaseScope,Scope),!. |
1185 | | extract_test_reference(TRef,_QRefs,_Id,_Scope) :- |
1186 | | translate_bexpression(TRef,String), |
1187 | | atom_concat('Unsupported reference on left side: ', String,Msg), |
1188 | | throw(junit(Msg)). |
1189 | | extract_test_reference2(card(TRef),QRefs,card,Id,Scope) :- |
1190 | | !,get_texpr_expr(TRef,Ref),Ref=identifier(_), |
1191 | | extract_test_reference2(Ref,QRefs,_,Id,Scope). |
1192 | | extract_test_reference2(identifier(Id),QRefs,interval,Constant,Scope) :- |
1193 | | memberchk(Id-ref(Scope,Constant),QRefs),!. |
1194 | | extract_test_reference2(identifier(Id),_QRefs,interval,Id,[]) :- !. |
1195 | | |
1196 | | |
1197 | | :- assert_must_succeed(( construct_scope([elem,right],interval,S), |
1198 | | S==elem(right(interval)) )). |
1199 | | construct_scope([],Scope,Scope). |
1200 | | construct_scope([F|Rest],Base,Scope) :- |
1201 | | functor(Scope,F,1),arg(1,Scope,Subscope), |
1202 | | construct_scope(Rest,Base,Subscope). |
1203 | | |
1204 | | extract_integer(Spec,Value) :- |
1205 | | ( get_texpr_expr(Spec,integer(Value)) -> true |
1206 | | ; get_texpr_expr(Spec,unary_minus(Spec2)) -> |
1207 | | extract_integer(Spec2,Value2),Value is -Value2 |
1208 | | ; |
1209 | | throw(junit('Integer value expected in right side of assertion'))). |
1210 | | |
1211 | | in_junit_mode :- |
1212 | | junit_mode(_),!. |
1213 | | |
1214 | | pa_junit(Name,Verdict,Junit) :- |
1215 | | create_junit_result(Name,0,Verdict,Junit). |