1 | | % (c) 2009-2013 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, |
2 | | % Heinrich Heine Universitaet Duesseldorf |
3 | | % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) |
4 | | |
5 | | :- module(units_domain, [ same_units/1, |
6 | | same_units/2, |
7 | | lub/3, |
8 | | units_domain_multiplication/3, |
9 | | units_domain_multiplication_conversion/3, |
10 | | units_domain_division/3, |
11 | | units_domain_division_conversion/3, |
12 | | units_domain_subtraction_conversion/3, |
13 | | units_domain_addition_conversion/3, |
14 | | units_domain_power_of/3, |
15 | | units_domain_member/2, |
16 | | units_domain_relation/3, |
17 | | involved_in_constraint/1, |
18 | | power_of_multiply_units/3 |
19 | | ]). |
20 | | |
21 | | :- use_module(library(lists)). |
22 | | :- use_module(library(clpfd)). |
23 | | :- use_module(library(sets)). |
24 | | :- use_module(library(atts)). |
25 | | |
26 | | |
27 | | :- use_module(probsrc(self_check)). |
28 | | |
29 | | :- use_module(units_conversions). |
30 | | |
31 | | :- use_module(probsrc(module_information)). |
32 | | :- module_info(group,plugin_units). |
33 | | :- module_info(description,'Units Plugin: The abstract Domain.'). |
34 | | |
35 | | :- attribute mfactors/2, dfactors/2, pfactors/2, constraint/0. |
36 | | |
37 | | involved_in_constraint(X) :- var(X), !, get_atts(X, constraint). |
38 | | involved_in_constraint([H|T]) :- |
39 | | !, (involved_in_constraint(H) ; involved_in_constraint(T)). |
40 | | involved_in_constraint(Pred) :- |
41 | | !, Pred =.. [Head|Tail], |
42 | | Tail = [_|_], |
43 | | (involved_in_constraint(Head) ; involved_in_constraint(Tail)). |
44 | | |
45 | | add_constraint_marker(A,B) :- |
46 | | (var(A) -> put_atts(A, constraint) ; true), |
47 | | (var(B) -> put_atts(B, constraint) ; true). |
48 | | |
49 | | verify_attributes(Var, Value, Goal) :- |
50 | | % the variable might be the result of a multiplication |
51 | | % -> propagate inwards |
52 | | get_atts(Var, mfactors(A,B)), !, |
53 | | (var(Value) -> Goal = [] ; |
54 | | (A==B, var(A)) -> Goal=[power_of_multiply_units(2,A,Value)] ; |
55 | | (var(A), nonvar(B)) -> Goal=[units_domain_division(Value,B,A)] ; |
56 | | (var(B), nonvar(A)) -> Goal=[units_domain_division(Value,A,B)] ; |
57 | | otherwise -> Goal=[]). |
58 | | verify_attributes(Var, Value, Goal) :- |
59 | | % the variable might be the result of a division |
60 | | % -> propagate inwards |
61 | | get_atts(Var, dfactors(A,B)), !, |
62 | | (var(Value) -> Goal=[] ; |
63 | | (var(A), nonvar(B)) -> Goal=[units_domain_multiplication(Value,B,A)] ; |
64 | | (var(B), nonvar(A)) -> Goal=[units_domain_division(A,Value,B)] ; |
65 | | otherwise -> Goal=[]). |
66 | | verify_attributes(Var, Value, Goal) :- |
67 | | % the variable might be the result of a exponentiation |
68 | | % -> propagate inwards |
69 | | get_atts(Var, pfactors(A,B)), !, |
70 | | (var(Value) -> Goal=[] ; |
71 | | (var(A), nonvar(B)) -> Goal=[power_of_multiply_units(B,A,Value)] ; |
72 | | otherwise -> Goal=[]). |
73 | | % rescue constraint attribute when unifying |
74 | | verify_attributes(Var, Value, []) :- |
75 | | get_atts(Var, constraint), !, |
76 | | (var(Value) -> put_atts(Value, constraint) ; true). |
77 | | %verify_attributes(Var, Value, []) :- |
78 | | % not a result of multiplication or division |
79 | | % true. |
80 | | |
81 | ? | one_is_var(A,B) :- var(A) ; var(B). |
82 | | |
83 | | % special case "power_of": |
84 | | % A is the unit part of an integer. This might be a variable. |
85 | | units_domain_power_of(A,b(integer(B,_,_)),C) :- var(A), put_atts(A,constraint), put_atts(C, pfactors(A,B)), !. |
86 | | % B is a raw syntax element. if it is a known integer literal, compute exponent |
87 | | units_domain_power_of(A, b(integer(X),_,_),C) :- |
88 | | power_of_multiply_units(X,A,C), !. |
89 | | % A might be top containing a list of known units |
90 | | units_domain_power_of(top(A), b(integer(X),_,_),top(C)) :- |
91 | | maplist(power_of_multiply_units(X),A,C), !. |
92 | | % otherwise, we can not infer the unit anymore |
93 | | units_domain_power_of(_A,_B,top([])) :- !. |
94 | | |
95 | | power_of_multiply_units(_,[],[]). |
96 | | power_of_multiply_units(X,[[Factor1,Unit,Pow1]|T1],[[Factor2,Unit,Pow2]|T2]) :- |
97 | | Factor2 #= Factor1 * X, Pow2 #= Pow1 * X, |
98 | | power_of_multiply_units(X,T1,T2). |
99 | | |
100 | | % Conversions between units. Conversion_factor and conversion_add is supplied in its own module. |
101 | | |
102 | | units_domain_addition_conversion(_, X, _) :- var(X), !. |
103 | | units_domain_addition_conversion(b(integer(X),_,_), [Unit|Units], [Unit2|Units]) :- |
104 | | repeat_conversion_add(X,Unit2,Unit). |
105 | | units_domain_addition_conversion(BInt,[Unit|Units],[Unit|Units2]) :- |
106 | | units_domain_addition_conversion(BInt,Units,Units2). |
107 | | |
108 | | units_domain_subtraction_conversion(_, X, _) :- var(X), !. |
109 | | units_domain_subtraction_conversion(b(integer(X),_,_), [Unit|Units], [Unit2|Units]) :- |
110 | | repeat_conversion_add(X,Unit,Unit2). |
111 | | units_domain_subtraction_conversion(BInt,[Unit|Units],[Unit|Units2]) :- |
112 | | units_domain_subtraction_conversion(BInt,Units,Units2). |
113 | | |
114 | | :- assert_must_succeed((units_domain_multiplication_conversion(b(integer(10),_,_), |
115 | | _,X),var(X))). |
116 | | :- assert_must_succeed((units_domain_multiplication_conversion(b(integer(10),_,_), |
117 | | [[1,m,1]],X),X=[[0,m,1]])). |
118 | | :- assert_must_succeed((units_domain_multiplication_conversion(b(integer(60),_,_), |
119 | | [[1,mins,1]],X),X=[[1,s,1]])). |
120 | | :- assert_must_succeed((units_domain_multiplication_conversion(b(integer(10),_,_), |
121 | | [[1,m,1],[1,s,1]],X),X=[[0,m,1],[1,s,1]])). |
122 | | :- assert_must_succeed((units_domain_multiplication_conversion(b(integer(10),_,_), |
123 | | [[1,s,1],[1,m,1]],X),X=[[1,s,1],[0,m,1]])). |
124 | | :- assert_must_succeed((units_domain_multiplication_conversion(b(integer(60),_,_), |
125 | | [[1,m,1],[1,mins,1]],X),X=[[1,m,1],[1,s,1]])). |
126 | | :- assert_must_succeed((units_domain_multiplication_conversion(b(integer(60),_,_), |
127 | | [[1,mins,1],[1,m,1]],X),X=[[1,s,1],[1,m,1]])). |
128 | | units_domain_multiplication_conversion(_, X, _) :- var(X), !. |
129 | | units_domain_multiplication_conversion(b(integer(X),_,_), [Unit|Units], [Unit2|Units]) :- |
130 | ? | repeat_conversion_factor(X,Unit,Unit2). |
131 | | units_domain_multiplication_conversion(BInt,[Unit|Units],[Unit|Units2]) :- |
132 | ? | units_domain_multiplication_conversion(BInt,Units,Units2). |
133 | | |
134 | | :- assert_must_succeed((units_domain_division_conversion(b(integer(10),_,_), |
135 | | _,X),var(X))). |
136 | | :- assert_must_succeed((units_domain_division_conversion(b(integer(10),_,_), |
137 | | [[1,m,1]],X),X=[[2,m,1]])). |
138 | | units_domain_division_conversion(_, X, _) :- var(X), !. |
139 | | units_domain_division_conversion(b(integer(X),_,_), [Unit|Units], [Unit2|Units]) :- |
140 | ? | repeat_conversion_factor(X,Unit2,Unit). |
141 | | units_domain_division_conversion(BInt,[Unit|Units],[Unit|Units2]) :- |
142 | | units_domain_division_conversion(BInt,Units,Units2). |
143 | | |
144 | | repeat_conversion_factor(X,_,_) :- X < 1, !, fail. |
145 | | repeat_conversion_factor(1.0,Unit,Unit). |
146 | | repeat_conversion_factor(Factor,Unit,UnitOut) :- |
147 | ? | var(UnitOut) |
148 | ? | -> conversion_factor(Unit2,Factor2,Unit), |
149 | ? | FactorOut is Factor / Factor2, |
150 | ? | repeat_conversion_factor(FactorOut,Unit2,UnitOut) |
151 | ? | ; conversion_factor(UnitOut,Factor2,Unit2), |
152 | ? | FactorOut is Factor / Factor2, |
153 | ? | repeat_conversion_factor(FactorOut, Unit, Unit2). |
154 | | |
155 | | repeat_conversion_add(X,_,_) :- X < 0, !, fail. |
156 | | repeat_conversion_add(0,Unit,Unit). |
157 | | repeat_conversion_add(Factor,Unit,UnitOut) :- |
158 | | var(UnitOut) |
159 | | -> conversion_add(Unit2,Factor2,Unit), |
160 | | FactorOut is Factor - Factor2, |
161 | | repeat_conversion_add(FactorOut,Unit2,UnitOut) |
162 | | ; conversion_add(UnitOut,Factor2,Unit2), |
163 | | FactorOut is Factor - Factor2, |
164 | | repeat_conversion_add(FactorOut, Unit, Unit2). |
165 | | |
166 | | |
167 | | % Operations on Integers |
168 | | :- assert_must_succeed(units_domain_multiplication(top([]),[[1,m,1]],top([]))). |
169 | | :- assert_must_succeed(units_domain_multiplication(top([[[1,m,1]],[[2,m,1]]]),[[1,m,1]],top([[[2,m,2]],[[3,m,2]]]))). |
170 | | :- assert_must_succeed(units_domain_multiplication(top([[[1,m,1]],[[2,m,1]]]),top([[[1,m,1]],[[2,m,1]]]), |
171 | | top([[[2,m,2]],[[3,m,2]],[[3,m,2]],[[4,m,2]]]))). |
172 | | :- assert_must_succeed(units_domain_multiplication([[1,m,1]],[[1,m,1]],[[2,m,2]])). |
173 | | :- assert_must_succeed((units_domain_multiplication(A,B,C),var(A),var(B),var(C))). |
174 | | :- assert_must_succeed((units_domain_multiplication(_,[[1,m,1]],Y), var(Y))). |
175 | | :- assert_must_succeed((units_domain_multiplication([[1,m,1]],_,Y), var(Y))). |
176 | | :- assert_must_succeed(units_domain_multiplication([[1,m,1],[1,a,2]],[[1,m,1]],[[2,m,2],[1,a,2]])). |
177 | | :- assert_must_succeed((units_domain_multiplication([[0,m,1],[0,s,-1]],[[0,s,1]],Res), Res = [[0,m,1]])). |
178 | ? | units_domain_multiplication(A,B,C) :- one_is_var(A,B), !, add_constraint_marker(A,B), put_atts(C, mfactors(A,B)). |
179 | | units_domain_multiplication(top(A),top(B),top(C)) :- !, |
180 | | map_product(units_domain_multiplication,A,B,C). |
181 | | units_domain_multiplication(top(A),B,top(C)) :- !, |
182 | | maplist(units_domain_multiplication(B),A,C). |
183 | | units_domain_multiplication(A,top(B),top(C)) :- !, |
184 | | maplist(units_domain_multiplication(A),B,C). |
185 | | units_domain_multiplication(Op1,[],Op1) :- !. |
186 | | units_domain_multiplication([],Op2,Op2) :- !. |
187 | | units_domain_multiplication([[Factor1,Unit1,Pow1]|Units], UnitsOp2, Res2) :- !, |
188 | | (selectchk([Factor2,Unit1,Pow2],UnitsOp2,UnitsOp22) |
189 | | -> Factor3 is Factor1+Factor2, Pow3 is Pow1+Pow2, |
190 | | Res1 = [Factor3,Unit1,Pow3], units_domain_multiplication(Units,UnitsOp22,UnitsResult) |
191 | | ; Res1 = [Factor1,Unit1,Pow1], units_domain_multiplication(Units,UnitsOp2,UnitsResult) |
192 | | ), |
193 | | (Res1 = [0,_,0] -> Res2 = UnitsResult ; Res2 = [Res1|UnitsResult]). |
194 | | |
195 | | :- assert_must_succeed(units_domain_division(top([]),[[1,m,1]],top([]))). |
196 | | :- assert_must_succeed(units_domain_division([[1,m,1]],top([]),top([]))). |
197 | | :- assert_must_succeed(units_domain_division(top([[[1,m,1],[1,a,2]],[[1,m,1],[2,a,2]]]),[[1,m,1]],top([[[1,a,2]],[[2,a,2]]]))). |
198 | | :- assert_must_succeed(units_domain_division([[1,m,1]],top([[[1,m,2]],[[1,m,3]]]),top([[[0,m,-1]],[[0,m,-2]]]))). |
199 | | :- assert_must_succeed(units_domain_division(top([[[1,m,1]],[[2,m,1]]]),top([[[1,m,2]],[[1,m,3]]]), |
200 | | top([[[0,m,-1]],[[0,m,-2]],[[1,m,-1]],[[1,m,-2]]]))). |
201 | | :- assert_must_succeed(units_domain_division([[1,m,1]],[[1,m,1]],[])). |
202 | | :- assert_must_succeed(units_domain_division([[1,m,1],[1,a,2]],[[1,m,1]],[[1,a,2]])). |
203 | | :- assert_must_succeed(units_domain_division([[1,m,1]],[[1,m,1],[1,a,2]],[[-1,a,-2]])). |
204 | | :- assert_must_succeed((units_domain_division(A,B,C),var(A),var(B),var(C))). |
205 | | :- assert_must_succeed((units_domain_division(A,A,C),var(A),C==[])). |
206 | | :- assert_must_succeed((units_domain_division(_,[[1,m,1]],Y), var(Y))). |
207 | | :- assert_must_succeed((units_domain_division([[1,m,1]],_,Y), var(Y))). |
208 | | :- assert_must_succeed((units_domain_division([[0,m,1],[0,s,1]],[[0,s,1]],Res), Res = [[0,m,1]])). |
209 | | units_domain_division(A,B,C) :- var(A), A==B, !, C=[]. |
210 | ? | units_domain_division(A,B,C) :- one_is_var(A,B), !, add_constraint_marker(A,B), put_atts(C, dfactors(A,B)). |
211 | | units_domain_division(top(A),top(B),top(C)) :- |
212 | | !, map_product(units_domain_division,A,B,C). |
213 | | units_domain_division(top(A),B,top(C)) :- |
214 | | !, maplist(units_domain_division_reversed_arguments(B),A,C). |
215 | | units_domain_division(A,top(B),top(C)) :- |
216 | | !, maplist(units_domain_division(A),B,C). |
217 | | units_domain_division(Op1,[],Op1) :- !. |
218 | | units_domain_division([],Op2,Op3) :- !, |
219 | | division_reverse_signs(Op2,Op3). |
220 | | units_domain_division([[Factor1,Unit1,Pow1]|Units], UnitsOp2, Res2) :- !, |
221 | | (selectchk([Factor2,Unit1,Pow2],UnitsOp2,UnitsOp22) |
222 | | -> Factor3 is Factor1-Factor2, Pow3 is Pow1-Pow2, |
223 | | Res1 = [Factor3,Unit1,Pow3], units_domain_division(Units,UnitsOp22,UnitsResult) |
224 | | ; Res1 = [Factor1,Unit1,Pow1], units_domain_division(Units,UnitsOp2,UnitsResult) |
225 | | ), |
226 | | (Res1 = [0,_,0] -> Res2 = UnitsResult ; Res2 = [Res1|UnitsResult]). |
227 | | |
228 | | division_reverse_signs([],[]). |
229 | | division_reverse_signs([[Factor,Unit,Pow]|T],[[Factor2,Unit,Pow2]|T2]) :- |
230 | | Factor2 is -Factor, Pow2 is -Pow, |
231 | | division_reverse_signs(T,T2). |
232 | | |
233 | | % reverse argument order - for maplist |
234 | | units_domain_division_reversed_arguments(A,B,C) :- |
235 | | units_domain_division(B,A,C). |
236 | | |
237 | | % Operations on Sets |
238 | | :- assert_must_succeed(units_domain_member(type,set(type))). |
239 | | :- assert_must_fail(units_domain_member(integer([[1,m,1]]), set(integer([[2,m,1]])))). |
240 | | units_domain_member(A,B) :- set(A) = B, !. |
241 | | units_domain_member(rec(FieldsA),set(rec(FieldsB))) :- !, |
242 | | maplist(units_domain_member,FieldsA,FieldsB). |
243 | | units_domain_member(field(Name,A),field(Name,B)) :- !, |
244 | | units_domain_member(A,B). |
245 | | |
246 | | :- assert_must_succeed(units_domain_relation(set(type),set(type2),set(set(couple(type,type2))))). |
247 | | units_domain_relation(set(A),set(B),set(set(couple(A,B)))). |
248 | | |
249 | | |
250 | | |
251 | | % COMPARISON OF UNITS |
252 | | % same_units/1 checks if a list of units are equal |
253 | | % same_units/2 checks if two units are equal |
254 | | % lub/3 infers least upper bound - a common unit higher in the lattice than the first ones |
255 | | |
256 | | % same_units/2 - test cases |
257 | | :- assert_must_fail(same_units(type(_), other_type)). |
258 | | :- assert_must_fail(same_units(type(type2(_)), type(type3(_)))). |
259 | | :- assert_must_succeed(same_units(type([[1,m,1],[1,s,2]]), type([[1,s,2],[1,m,1]]))). |
260 | | :- assert_must_succeed(same_units(type([[-1,m,1],[1,s,2]]), type([[-1,s,2],[1,m,1]]))). |
261 | | :- assert_must_succeed(same_units(type([[2,m,1],[1,s,2]]), type([[2,s,2],[1,m,1]]))). |
262 | | :- assert_must_succeed(same_units(type([[-1,m,1],[0,s,2]]), type([[-2,s,2],[1,m,1]]))). |
263 | | :- assert_must_succeed(same_units(type(top([])), type(top([])))). |
264 | | :- assert_must_succeed(same_units(type(X), type(X))). |
265 | | |
266 | | % lub/3 - test cases |
267 | | :- assert_must_succeed(lub(type([[1,m,1],[1,s,2]]), type([[1,s,2],[1,m,1]]), type([[1,m,1],[1,s,2]]))). |
268 | | :- assert_must_succeed(lub(type([[-1,m,1],[1,s,2]]), type([[-1,s,2],[1,m,1]]), type([[-1,m,1],[1,s,2]]))). |
269 | | :- assert_must_succeed(lub(type(top([])), type([[1,m,1]]), type(top([[[1,m,1]]])))). |
270 | | :- assert_must_succeed(lub(type(top([])), type([[1,s,2],[1,m,1]]), type(top([[[1,s,2],[1,m,1]]])))). |
271 | | :- assert_must_succeed(lub(type([[1,m,1],[5,s,2]]), type(top([[[1,m,1]]])), type(top([[[1,m,1],[5,s,2]],[[1,m,1]]])))). |
272 | | :- assert_must_succeed(lub(type(X), type(X), type(X))). |
273 | | :- assert_must_succeed(lub([[3,m,1]],[[3,m,1],[0,h,-1]],top([[[3,m,1]],[[3,m,1],[0,h,-1]]]))). |
274 | | :- assert_must_succeed(lub(integer(_),integer(top([[[0,m,1]],[[0,m,1],[0,s,-1]],[[0,m,1]]])), |
275 | | integer(top([[[0,m,1]],[[0,m,1],[0,s,-1]],[[0,m,1]]])))). |
276 | | :- assert_must_succeed(lub(integer(top([[[0,m,1]],[[0,m,1],[0,s,-1]],[[0,m,1]]])),integer(_), |
277 | | integer(top([[[0,m,1]],[[0,m,1],[0,s,-1]],[[0,m,1]]])))). |
278 | | :- assert_must_succeed(lub(top([[[0,m,1]],[[0,m,1],[0,s,-1]]]),[[0,m,1],[0,s,-1]], |
279 | | top([[[0,m,1]],[[0,m,1],[0,s,-1]]]))). |
280 | | :- assert_must_succeed(lub([[0,m,1],[0,s,-1]],top([[[0,m,1]],[[0,m,1],[0,s,-1]]]), |
281 | | top([[[0,m,1]],[[0,m,1],[0,s,-1]]]))). |
282 | | :- assert_must_succeed(lub(top([[[0,m,1]],[[0,m,1],[0,s,-1]]]),top([[[0,m,1],[0,s,-1]],[[0,m,1]]]), |
283 | | top([[[0,m,1],[0,s,-1]],[[0,m,1]]]))). |
284 | | |
285 | | same_units([]). |
286 | | same_units([_]). |
287 | | same_units([A,B|T]) :- same_units(A,B), same_units([B|T]). |
288 | | % two units are identical, if the lub is again one of the two units |
289 | ? | same_units(A,B) :- lub(A,B,C), !, (A=C ; B=C). |
290 | | |
291 | | % lub/3 - implementation |
292 | | lub(A,B,C) :- A=B, !, B=C. |
293 | | lub(A,B,C) :- lub_domain_level(A,B,C), !. |
294 | | lub(top(A),top(B),top(C)) :- |
295 | | !, union(A,B,C). |
296 | | lub(top(A),B,top(C)) :- |
297 | | !, union(A,[B],C). |
298 | | lub(A,top(B),top(C)) :- |
299 | | !, union([A],B,C). |
300 | | lub(A,B,C) :- |
301 | | \+ is_list(A), \+ is_list(B), \+ is_list(C), |
302 | | A =.. [Type|ArgsA], |
303 | | B =.. [Type|ArgsB], |
304 | | lub_list(ArgsA,ArgsB,ArgsC), !, |
305 | | C =.. [Type|ArgsC]. |
306 | | lub(A,B,top([A,B])) :- !. |
307 | | |
308 | | lub_list([],[],[]). |
309 | | lub_list([H1|T1],[H2|T2],[H3|T3]) :- |
310 | | lub(H1,H2,H3), !, |
311 | | lub_list(T1,T2,T3). |
312 | | |
313 | | lub_domain_level(U1,U2,U3) :- |
314 | | check_factor(U1,U2,0,0), |
315 | | lub_domain_level2(U1,U2,U3). |
316 | | |
317 | | lub_domain_level2([],[],[]) :- !. |
318 | | lub_domain_level2([[Factor1,Unit1,Pow1]|Units], UnitsOp2,[[Factor1,Unit1,Pow1]|ResUnits]) :- |
319 | | selectchk([_Factor1,Unit1,Pow1],UnitsOp2,UnitsOp22), !, |
320 | | lub_domain_level2(Units,UnitsOp22,ResUnits). |
321 | | |
322 | | check_factor([],[],X,X). |
323 | | check_factor([[Factor1,_,_]|T1], [[Factor2,_,_]|T2],X,Y) :- |
324 | | !, X2 is X + Factor1, Y2 is Y + Factor2, check_factor(T1,T2,X2,Y2). |