1 % (c) 2020-2026 Lehrstuhl fuer Softwaretechnik und Programmiersprachen,
2 % Heinrich Heine Universitaet Duesseldorf
3 % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html)
4
5 :- module(kernel_reals, [construct_real/2, construct_negative_real/2,
6 construct_real_number/2,
7 is_real/1, is_real/2, is_real_wf/2,
8 is_float/1, is_float_wf/2,
9 is_not_real/1, is_not_float/1,
10 is_ground_real/1,
11 is_largest_positive_float/1, is_smallest_positive_float/1,
12 is_next_larger_float/2, is_next_smaller_float/2,
13 convert_int_to_real/2,
14 real_floor/2, real_ceiling/2, real_truncate/2,
15 real_addition_wf/4, real_subtraction_wf/4,
16 real_multiplication_wf/4, real_division_wf/5,
17 real_unary_minus_wf/3,
18 real_absolute_value_wf/3,
19 real_sign_wf/3,
20 real_square_root_wf/4,
21 real_round_wf/3,
22 real_unop_wf/4, real_unop_wf/5,
23 real_binop_wf/6,
24 real_power_of_wf/5,
25 real_less_than_wf/3, real_less_than_equal_wf/3,
26 real_comp_wf/5,
27 real_maximum_of_set/4, real_minimum_of_set/4,
28 enumerate_real_wf/3,
29 use_clpfd_real_solver/0, do_not_double_check_solution/0,
30 post_real_equal_expr_wf/4,
31 post_real_neq/2,
32 post_real_equality/3, % reified equality
33 is_interval_closure_or_realset/3,
34 is_real_interval_closure_with_known_card/4
35 ]).
36
37 % Reals/Floats in ProB are represented by terms of the form term(floating(Number))
38 % in future a proper wrapper such as real(Number) might be created
39
40 :- meta_predicate real_comp_wf(2,-,-,-,-).
41 :- meta_predicate real_comp_float_blocking(2,-,-,-).
42
43 :- use_module(module_information,[module_info/2]).
44 :- module_info(group,kernel).
45 :- module_info(description,'This module provides (external) functions to manipulate B reals and floats.').
46
47 :- use_module(error_manager).
48 :- use_module(self_check).
49 :- use_module(library(lists)).
50 :- use_module(debug).
51
52 :- use_module(kernel_objects,[exhaustive_kernel_check_wf/2,exhaustive_kernel_check_wf/3]).
53 :- use_module(extension('counter/counter'),
54 [next_smaller_abs_float/2, next_smaller_float/2,next_larger_float/2, next_float_twoards/3,
55 smallest_float/1, largest_float/1, smallest_abs_float/1]).
56
57
58 :- if((current_prolog_flag(dialect, sicstus),
59 current_prolog_flag(version_data, sicstus(4,VN,_,_,_)), VN>=10)). % 4.10 or higher
60 can_use_clpfd_real_solver.
61 use_clpfd_real_solver :- get_preference(solver_for_reals,S), (S=precise_float_solver ; S=real_solver).
62 use_clpfd_real_solver(S) :- get_preference(solver_for_reals,S), (S=precise_float_solver ; S=real_solver).
63 :- use_module(library(clpfd),[fd_min/2, fd_max/2, fd_batch/1, maximum/2, minimum/2,
64 '#<=>'/2, '$>='/2, '$=<'/2, '$='/2, '#='/2, labeling/2]).
65
66 post_real_equal_expr_wf(X,Y,Span,WF) :-
67 catch('$='(X,Y),
68 error(evaluation_error(ERR),_),
69 process_evaluation_error(ERR,'='(X,Y),Span,WF)).
70 :- else.
71 can_use_clpfd_real_solver :- fail.
72 use_clpfd_real_solver :- fail.
73 use_clpfd_real_solver(_) :- fail.
74 post_real_equal_expr_wf(X,Y,Span,WF) :- add_internal_error('Not available:',post_real_equal_expr_wf(X,Y,Span,WF)),fail.
75 :- endif.
76
77 % there is currently no $\= in CLP(FD) for reals; use regular dif instead
78 % this call only works for variables or numbers as arguments, no expressions
79 post_real_neq(E1,E2) :- dif(E1,E2).
80
81
82 :- use_module(kernel_dif,[frozen_dif/2]).
83 % reified version of equality between X and Y
84 post_real_equality(X,Y,Res) :- nonvar(X),nonvar(Y),!,
85 (X=Y -> Res=pred_true ; Res=pred_false).
86 post_real_equality(X,Y,Res) :- % check for attached dif co-routines; later we should probably use CHR
87 frozen_dif(X,Y),!, Res=pred_false.
88 post_real_equality(X,Y,R) :- use_clpfd_real_solver,!,
89 '#<=>'('$='(X,Y),R01),
90 % Note: '#<=>'( '$='(X,1.0), R01), X = 1.0. does not instantiate R01, and allows R01=0 (see SPRM-21561)
91 % Hence, if X=Y we force R01 to be 1:
92 when(?=(X,Y),(X=Y -> R01=1; R01=0)),
93 b_interpreter_check:prop_pred_01(R,R01). % link pred_false/pred_true to 0/1
94 post_real_equality(X,Y,Res) :- when((?=(X,Y);nonvar(Res)),eq_real2(X,Y,Res)).
95
96 eq_real2(X,Y,Res) :- nonvar(Res),!,
97 (Res=pred_true -> X=Y ; post_real_neq(X,Y)).
98 eq_real2(X,Y,Res) :-
99 (X=Y -> Res=pred_true ; Res=pred_false).
100
101 % used to construct a Value from real(Atom) AST node
102 construct_real(Atom,term(floating(Float))) :-
103 atom_codes(Atom,C),
104 number_codes(Nr,C),
105 Float is float(Nr). % make sure we store a float; not required for AST literals
106
107 construct_real_number(Atom,Nr) :-
108 atom_codes(Atom,C),
109 number_codes(Nr,C).
110
111 construct_negative_real(Atom,term(floating(Float))) :-
112 atom_codes(Atom,C),
113 number_codes(Nr,C),
114 Float is -float(Nr).
115
116 is_real_wf(X,_WF) :- is_real(X,_).
117 is_real(X) :- is_real(X,_).
118
119 is_real(term(floating(Nr)),Nr).
120 % TO DO: other formats
121
122 is_float_wf(X,_WF) :- is_float(X).
123
124 is_float(term(floating(_))).
125
126 is_not_real(_) :- fail.
127 is_not_float(_) :- fail.
128
129 is_ground_real(term(floating(Nr))) :- number(Nr).
130
131 is_largest_positive_float(term(floating(Nr))) :- largest_float(Nr).
132 is_smallest_positive_float(term(floating(Nr))) :- smallest_abs_float(Nr).
133
134 % corresponds to RNEXT external function
135 is_next_larger_float(term(floating(Nr)),term(floating(Next))) :-
136 block_next_larger_float(Nr,Next).
137 :- block block_next_larger_float(-,-).
138 block_next_larger_float(Nr,Next) :- nonvar(Nr),!,next_larger_float(Nr,Next).
139 block_next_larger_float(Nr,Next) :- next_smaller_float(Next,Nr).
140
141 % corresponds to RPREV external function
142 is_next_smaller_float(term(floating(Nr)),term(floating(Next))) :-
143 block_next_larger_float(Next,Nr).
144
145
146 % convert an integer to a real, corresponds to the real(.) function in Atelier-B; convert_real as AST
147 convert_int_to_real(int(X),term(floating(R))) :-
148 convert_int_to_real_aux(X,R).
149
150 convert_int_to_real_aux(Int,Real) :- number(Int),!,
151 Real is float(Int).
152 convert_int_to_real_aux(Int,Real) :-
153 use_clpfd_real_solver,!,
154 '$='(Real,float(Int)).
155 convert_int_to_real_aux(Int,Real) :- block_convert_int_to_real_aux(Int,Real).
156
157 :- block block_convert_int_to_real_aux(-,-).
158 block_convert_int_to_real_aux(Int,Real) :- number(Int),!,
159 Real is float(Int).
160 block_convert_int_to_real_aux(Int,Real) :- Int1 is floor(Real),
161 Real is float(Int1),
162 Int1=Int.
163
164
165 % convert_int_floor as AST, Atelier-B floor(.)
166 % The value is the greatest integer less or equal to X
167 real_floor(term(floating(R)),int(X)) :- real_floor_aux(R,X).
168
169 real_floor_aux(Real,Int) :-
170 use_clpfd_real_solver,!, % + check if we use CLP(FD) for integers?
171 '#='(Int,floor(Real)).
172 real_floor_aux(Real,Int) :- block_real_floor_aux(Real,Int).
173
174 :- block block_real_floor_aux(-,?).
175 block_real_floor_aux(Real,Int) :- Int is floor(Real).
176
177
178 % convert_int_ceiling as AST, Atelier-B ceiling(.)
179 % The value is the least integer greater or equal to X.
180 real_ceiling(term(floating(R)),int(X)) :-
181 real_ceiling_aux(R,X).
182 real_ceiling_aux(Real,Int) :-
183 use_clpfd_real_solver,!, % + check if we use CLP(FD) for integers?
184 '#='(Int,ceiling(Real)).
185 real_ceiling_aux(Real,Int) :- block_real_ceiling_aux(Real,Int).
186
187 :- block block_real_ceiling_aux(-,?).
188 block_real_ceiling_aux(Real,Int) :- Int is ceiling(Real).
189
190
191 % The value is the closest integer between X and 0
192 real_truncate(term(floating(R)),int(X)) :-
193 real_truncate_aux(R,X).
194 real_truncate_aux(Real,Int) :-
195 use_clpfd_real_solver,!, % + check if we use CLP(FD) for integers?
196 '#='(Int,truncate(Real)).
197 real_truncate_aux(Real,Int) :- block_real_truncate_aux(Real,Int).
198
199 :- block block_real_truncate_aux(-,?).
200 block_real_truncate_aux(Real,Int) :- Int is truncate(Real).
201
202
203
204 :- assert_must_succeed(exhaustive_kernel_check_wf([commutative],kernel_reals:real_addition_wf(term(floating(1.0)),term(floating(2.0)),term(floating(3.0)),WF),WF)).
205 :- assert_must_succeed(exhaustive_kernel_check_wf([commutative],kernel_reals:real_addition_wf(term(floating(0.0)),term(floating(2.0)),term(floating(2.0)),WF),WF)).
206 :- assert_must_succeed(exhaustive_kernel_check_wf([commutative],kernel_reals:real_addition_wf(term(floating(-1.0)),term(floating(1.0)),term(floating(0.0)),WF),WF)).
207
208 real_addition_wf(term(floating(X)),term(floating(Y)),term(floating(R)),WF) :-
209 real_add_wf_aux(X,Y,R,WF).
210
211 %real_add_wf_aux(X,Y,R,WF) :- X==Y,!, '$='(R,2.0*X), % TODO: check if this works reliably, probably not
212 % (do_not_double_check_solution -> true ; real_add_wf_aux2(X,Y,R,WF)).
213 real_add_wf_aux(X,Y,R,WF) :-
214 use_clpfd_real_solver,!,
215 '$='(R,X+Y),
216 (do_not_double_check_solution -> true ; real_add_wf_aux2(X,Y,R,WF)).
217 real_add_wf_aux(X,Y,R,WF) :- block_real_add_wf_aux(X,Y,R,WF).
218
219 :- block block_real_add_wf_aux(-,-,?,?), block_real_add_wf_aux(?,-,-,?), block_real_add_wf_aux(-,?,-,?).
220 block_real_add_wf_aux(X,Y,R,WF) :-
221 (nonvar(X)
222 -> (nonvar(Y)
223 -> safe_is(R,X+Y,WF)
224 ; allow_constraint_propagation(Kind),
225 safe_is(YY,R-X,WF),
226 confirm_solution_for_expr(YY,X+SOL,SOL,R,Kind,Solutions,WF)
227 -> set_solution_for_expr(Y,Solutions,WF)
228 ; real_add_wf_aux2(X,Y,R,WF) % delay until Y is known
229 )
230 ; allow_constraint_propagation(Kind),
231 safe_is(XX,R-Y,WF),
232 confirm_solution_for_expr(XX,SOL+Y,SOL,R,Kind,Solutions,WF)
233 -> set_solution_for_expr(X,Solutions,WF)
234 ; real_add_wf_aux2(X,Y,R,WF) % delay until X is known
235 ).
236 :- block real_add_wf_aux2(-,?,?,?), real_add_wf_aux2(?,-,?,?).
237 real_add_wf_aux2(X,Y,R,WF) :- safe_is(R,X+Y,WF).
238
239
240 % confirm_solution_for_expr(XX,ExprX,X,R : check if XX=X is a solution for R = ExprX, where ExprX contains variable X
241 % depending on solver setting : compute a list of candidate solutions around XX
242 confirm_solution_for_expr(XX,_,_,_,no_checking,Solutions,_) :- !, Solutions=XX.
243 confirm_solution_for_expr(XX,ExprX,X,R,simple_checking,Solutions,WF) :- !,
244 check_sol_direct(XX,ExprX,X,R,WF), Solutions=XX. % single solution
245 confirm_solution_for_expr(XX,ExprX,X,R,check_unique_solution,Solutions,WF) :- !,
246 check_sol_direct(XX,ExprX,X,R,WF), Solutions=XX, % single solution
247 % Now check if there are other solutions:
248 next_larger_float(XX,XN),
249 (X=XN,safe_is(R,ExprX,WF)
250 -> debug_format(19,'Reject ambiguous larger solution {~w,~w} for ~w = ~w~n',[XX,XN,ExprX,R]),fail
251 ; true
252 ),
253 next_smaller_float(XX,XP),
254 (X=XP,safe_is(R,ExprX,WF)
255 -> debug_format(19,'Reject ambiguous smaller solution {~w,~w} for ~w = ~w~n',[XX,XP,ExprX,R]),fail
256 ; true).
257 confirm_solution_for_expr(XX,ExprX,X,R,check_small_interval_solution,Interval,WF) :-
258 get_solutions_around(XX,ExprX,X,R,Interval,WF).
259
260 :- use_module(kernel_waitflags,[get_wait_flag/4]).
261 % set the solution based on result of confirm_solution_for_expr
262 set_solution_for_expr(X,XX,_WF) :- number(XX),!,X=XX. % deterministic constraint propagation found precise solution
263 set_solution_for_expr(X,[XX],_WF) :- !,X=XX. % ditto
264 set_solution_for_expr(_,[],_WF) :- !, fail. % no solutions
265 set_solution_for_expr(X,Interval,WF) :-
266 length(Interval,Len), % we found an interval list of possible solutions
267 get_wait_flag(Len,kernel_reals,WF,LWF),
268 set_sol_aux(X,Interval,LWF).
269 :- block set_sol_aux(-,?,-).
270 set_sol_aux(X,Interval,_) :-
271 ? member(X,Interval).
272
273
274 % get a sorted list of solutions around the candidate XX
275 get_solutions_around(XX,ExprX,X,R,Interval,WF) :-
276 (check_sol_direct(XX,ExprX,X,R,WF) % XX is a solution
277 -> get_preference(solver_strength,SS),
278 Limit is 10+SS, % number of solutions we are willing to find
279 next_larger_float(XX,XN),
280 find_larger_bounds(XN,Limit,ExprX,X,R,WF,LargerSols,[]),
281 next_smaller_float(XX,XP),
282 find_lower_bounds(XP,Limit,ExprX,X,R,WF,Interval,[XX|LargerSols])
283 ; (X='X',debug_format(19,'Reject non-solution ~w for ~w = ~w~n',[XX,ExprX,R]),fail ; true),
284 %next_smaller_float(XX,XP), (check_sol_direct(XP,ExprX,X,R,WF) -> write(xp(XP)),nl ; write(nxp(XP)),nl),
285 %next_larger_float(XX,XN), (check_sol_direct(XN,ExprX,X,R,WF) -> write(xn(XN)),nl ; write(nxp(XN)),nl),
286 Interval = [] % no floating point solutions; TODO: do we need to check smaller/larger values??
287 ).
288
289 % e.g. for x+1.0 = 3.0 we have 2.0 as solution and the previous float 1.9999999999999998, nothing else
290 % e.b. for x+2.0 = 3.0 we have four solutions {0.9999999999999998,0.9999999999999999,1.0,1.0000000000000002}
291 % card({x|x + 2.999 = 3.0}) = 2049
292 % card({x|x + 2.9999 = 3.0}) = 32769
293
294 % increment floats until we hit the first non-solution or we reach the limit
295 % TODO: try and compute this bound, rather then single-stepping
296 % TODO: enumerate the first solution and then generate an enum warning if we reach limit
297 find_larger_bounds(XP,Lim,ExprX,X,R,WF,[XP|Sols1],Sols2) :-
298 check_sol_direct(XP,ExprX,X,R,WF),!, Lim>0, L1 is Lim-1,
299 next_larger_float(XP,XP2),
300 find_larger_bounds(XP2,L1,ExprX,X,R,WF,Sols1,Sols2).
301 find_larger_bounds(_Bound,_Lim,_ExprX,_X,_R,_WF,Sols,Sols).
302
303 % decrement floats until we hit the first non-solution or we reach the limit
304 find_lower_bounds(XP,Lim,ExprX,X,R,WF,[XP|Sols1],Sols2) :-
305 check_sol_direct(XP,ExprX,X,R,WF),!, Lim>0, L1 is Lim-1,
306 next_smaller_float(XP,XP2),
307 find_lower_bounds(XP2,L1,ExprX,X,R,WF,Sols1,Sols2).
308 find_lower_bounds(_Bound,_Lim,_ExprX,_X,_R,_WF,Sols,Sols).
309
310 check_sol_direct(XX,ExprX,X,R,WF) :- !,
311 % (x + y = z <=> x = z - y) does not hold for all floats, e.g., when x very small and y very large (x+y)-y = 0
312 (\+((X=XX,safe_is(R,ExprX,WF))) -> fail ; true).
313
314
315 :- use_module(probsrc(preferences), [get_preference/2]).
316
317 allow_constraint_propagation(Kind) :-
318 get_preference(solver_for_reals,Solver),
319 get_solver_kind(Solver,Kind).
320
321 get_solver_kind(real_solver,no_checking). % like CLP(R): do not check propagated solution
322 get_solver_kind(float_solver,simple_checking). % check propagated solution, but do not check uniqueness
323 %get_solver_kind(precise_float_solver,check_unique_solution).
324 get_solver_kind(precise_float_solver,check_small_interval_solution). % perform limited constraint propagation, fail directly if no solution exists
325
326 do_not_double_check_solution :- get_preference(solver_for_reals,real_solver).
327
328 precise_float_solver_active :- get_preference(solver_for_reals,precise_float_solver).
329
330 % we could have various other options:
331 % aggressive: do it like CLP(R), which can generate non-solutions
332 % | ?- {X+10.0e10 = 1.0e-9}, write(sol(X)),nl, {X+10.0e10 = 1.0e-9}.
333 % prints sol(-1.0E+11) but then fails
334 % conservative: do the precision check and also check that there are no other solutions (not done yet)
335 % e.g., X+10000000000.0 = 10000000000.0 & (X=0.000000000001 or X=2.0) is FALSE with the float_solver, but TRUE with none
336
337
338 :- assert_must_succeed(exhaustive_kernel_check_wf(kernel_reals:real_subtraction_wf(term(floating(3.0)),term(floating(2.0)),term(floating(1.0)),WF),WF)).
339 real_subtraction_wf(term(floating(X)),term(floating(Y)),term(floating(R)),WF) :-
340 real_sub_wf_aux(X,Y,R,WF).
341
342 real_sub_wf_aux(X,Y,R,WF) :-
343 use_clpfd_real_solver,!,
344 '$='(R,X-Y),
345 (do_not_double_check_solution -> true ; real_sub_wf_aux2(X,Y,R,WF)).
346 real_sub_wf_aux(X,Y,R,WF) :- block_real_sub_wf_aux(X,Y,R,WF).
347
348 :- block block_real_sub_wf_aux(-,-,?,?), block_real_sub_wf_aux(?,-,-,?), block_real_sub_wf_aux(-,?,-,?).
349 block_real_sub_wf_aux(X,Y,R,WF) :-
350 (nonvar(X)
351 -> (nonvar(Y)
352 -> safe_is(R,X-Y,WF)
353 ; allow_constraint_propagation(Kind),
354 safe_is(YY,X-R,WF),
355 confirm_solution_for_expr(YY,X-SOL,SOL,R,Kind,Solutions,WF)
356 -> set_solution_for_expr(Y,Solutions,WF) % deterministic constraint propagation found precise solution
357 ; real_sub_wf_aux2(X,Y,R,WF) % delay until Y is known
358 )
359 ; allow_constraint_propagation(Kind),
360 safe_is(XX,R+Y,WF),
361 confirm_solution_for_expr(XX,SOL-Y,SOL,R,Kind,Solutions,WF)
362 -> set_solution_for_expr(X,Solutions,WF) % deterministic constraint propagation found precise solution
363 ; real_sub_wf_aux2(X,Y,R,WF) % delay until X is known
364 ).
365
366 :- block real_sub_wf_aux2(-,?,?,?), real_sub_wf_aux2(?,-,?,?).
367 real_sub_wf_aux2(X,Y,R,WF) :- safe_is(R,X-Y,WF).
368
369
370 :- assert_must_succeed(exhaustive_kernel_check_wf([commutative],kernel_reals:real_multiplication_wf(term(floating(3.0)),term(floating(2.0)),term(floating(6.0)),WF),WF)).
371 real_multiplication_wf(term(floating(X)),term(floating(Y)),term(floating(R)),WF) :-
372 real_mul_wf_aux0(X,Y,R,WF).
373
374 real_mul_wf_aux0(X,Y,R,WF) :-
375 use_clpfd_real_solver,!,
376 (X==Y -> '$='(R,X*X) ; % causes segmentation faults up to SICS 4.10.2-beta2
377 '$='(R,X*Y),propagate_square_inverse(X,Y,R,WF)), %tools_printing:print_term_summary(mul(R,X,Y)),nl,
378 (do_not_double_check_solution -> true ; real_mul_wf_aux2(X,Y,R,WF)).
379 real_mul_wf_aux0(X,Y,R,WF) :- block_real_mul_wf_aux0(X,Y,R,WF).
380
381 % | ?- 2.0 $= X*0.0. -> clpfd:(X in fsup..fsup) ? i.e., CLPFD does not detect multiplication by 0.0
382 % CLPFD also does not take square root
383 :- block propagate_square_inverse(-,-,-,?).
384 propagate_square_inverse(X,Y,R,_WF) :- (X==0.0 ; Y==0.0), !, R=0.0.
385 propagate_square_inverse(X,Y,R,WF) :- var(X),X==Y, !, % X*X = R -> compute sqrt
386 %'$='(R,X^2.0). % makes test 2430 fail for c=card({y|y*y=x}) & x = 2.0E-309 & c=14 -> c=1
387 %'$='(R,X*X). % x * x = 1.0 -> segmentation fault
388 kernel_square_inverse(X,R,WF).
389 propagate_square_inverse(X,Y,R,_WF) :- nonvar(R), R \= 0.0, !,
390 dif(X,0.0), dif(Y,0.0).
391 propagate_square_inverse(_,_,_,_).
392
393 :- block block_real_mul_wf_aux0(-,-,-,?).
394 block_real_mul_wf_aux0(X,Y,R,_WF) :- (X==0.0 ; Y==0.0), !, R=0.0.
395 block_real_mul_wf_aux0(X,Y,R,WF) :- var(X),X==Y, !, % X*X = R -> compute sqrt
396 kernel_square_inverse(X,R,WF).
397 block_real_mul_wf_aux0(X,Y,R,WF) :- real_mul_wf_aux(X,Y,R,WF).
398
399
400 :- block real_mul_wf_aux(-,-,?,?), real_mul_wf_aux(?,-,-,?), real_mul_wf_aux(-,?,-,?).
401 real_mul_wf_aux(X,Y,R,_WF) :- (X==0.0 ; Y==0.0), !, R=0.0.
402 real_mul_wf_aux(X,Y,R,WF) :-
403 (nonvar(X)
404 -> (nonvar(Y)
405 -> safe_is(R,X*Y,WF)
406 ; allow_constraint_propagation(Kind),
407 safe_is(YY,R/X,WF),
408 confirm_solution_for_expr(YY,X*SOL,SOL,R,Kind,Solutions,WF)
409 -> set_solution_for_expr(Y,Solutions,WF)
410 ; real_mul_wf_aux2(X,Y,R,WF) % delay until Y is known
411 )
412 ; allow_constraint_propagation(Kind),
413 safe_is(XX,R/Y,WF),
414 confirm_solution_for_expr(XX,SOL*Y,SOL,R,Kind,Solutions,WF)
415 -> set_solution_for_expr(X,Solutions,WF)
416 ; real_mul_wf_aux2(X,Y,R,WF) % delay until X is known
417 ).
418
419 :- block real_mul_wf_aux2(-,?,?,?), real_mul_wf_aux2(?,-,?,?).
420 real_mul_wf_aux2(X,Y,R,WF) :- safe_is(R,X*Y,WF).
421
422 % compute sqrt, R is known
423 kernel_square_inverse(X,R,WF) :-
424 (R < 0.0 -> fail
425 ; allow_constraint_propagation(Kind),
426 safe_is(XX,sqrt(R),WF),
427 confirm_solution_for_expr(XX,SOL*SOL,SOL,R,Kind,SolutionsPos,WF),
428 (XX=0.0 -> Solutions = SolutionsPos
429 ; NX is -XX,
430 confirm_solution_for_expr(NX,SOL*SOL,SOL,R,Kind,SolutionsNeg,WF),
431 append(SolutionsPos,SolutionsNeg,Solutions)
432 )
433 -> set_solution_for_expr(X,Solutions,WF)
434 ; real_mul_wf_aux(X,X,R,WF)).
435
436
437 % used for 'RSQRT' external function
438 :- block 'real_square_root_wf'(-,-,?,?).
439 real_square_root_wf(term(floating(X)),term(floating(R)),Span,WF) :-
440 real_sqrt_wf_aux(X,R,Span,WF).
441
442 real_sqrt_wf_aux(X,R,Span,WF) :-
443 use_clpfd_real_solver,!,
444 '$='(R,sqrt(X)),
445 (do_not_double_check_solution -> true ; real_unop_wf_aux('sqrt',X,R,Span,WF)).
446 real_sqrt_wf_aux(X,R,Span,WF) :- block_real_sqrt_wf_aux(X,R,Span,WF).
447
448 :- block block_real_sqrt_wf_aux(-,-,?,?).
449 block_real_sqrt_wf_aux(X,R,_Span,WF) :- var(X),
450 allow_constraint_propagation(Kind),
451 get_preference(find_abort_values,false),
452 (R >= 0.0
453 -> safe_is(XX,R*R,WF),
454 confirm_solution_for_expr(XX,sqrt(SOL),SOL,R,Kind,Solutions,WF)
455 ; Solutions = []), % RSQRT always returns a positive number
456 !,
457 set_solution_for_expr(X,Solutions,WF).
458 block_real_sqrt_wf_aux(X,R,Span,WF) :-
459 real_unop_wf_aux('sqrt',X,R,Span,WF).
460
461
462 :- assert_must_succeed(exhaustive_kernel_check_wf(kernel_reals:real_division_wf(term(floating(6.0)),term(floating(2.0)),term(floating(3.0)),unknown,WF),WF)).
463 real_division_wf(term(floating(X)),term(floating(Y)),term(floating(R)),Span,WF) :-
464 real_div_wf_aux(X,Y,R,Span,WF).
465
466 real_div_wf_aux(X,Y,R,Span,WF) :-
467 use_clpfd_real_solver,!,
468 '$='(R,X / Y),
469 (do_not_double_check_solution -> true ; check_div(X,Y,R,Span,WF)).
470 real_div_wf_aux(X,Y,R,Span,WF) :- block_real_div_wf_aux(X,Y,R,Span,WF).
471
472 :- block block_real_div_wf_aux(-,?,-,?,?), block_real_div_wf_aux(?,-,?,?,?).
473 block_real_div_wf_aux(X,Y,R,Span,WF) :- nonvar(X),!,safe_is(R,X/Y,Span,WF).
474 block_real_div_wf_aux(X,Y,R,Span,WF) :- Y \= 0.0, allow_constraint_propagation(Kind),
475 safe_is(XX,R*Y,Span,WF),
476 confirm_solution_for_expr(XX,SOL/Y,SOL,R,Kind,Solutions,WF),
477 !,
478 set_solution_for_expr(X,Solutions,WF).
479 block_real_div_wf_aux(X,Y,R,Span,WF) :- check_div(X,Y,R,Span,WF).
480
481 :- block check_div(-,?,?,?,?), check_div(?,-,?,?,?).
482 check_div(X,Y,R,Span,WF) :- safe_is(R,X/Y,Span,WF).
483
484 :- block 'real_unary_minus_wf'(-,-,?).
485 real_unary_minus_wf(term(floating(X)),term(floating(R)),WF) :-
486 real_um_wf_aux(X,R,WF).
487
488 real_um_wf_aux(X,R,WF) :-
489 use_clpfd_real_solver,!,
490 '$='(R,-X),
491 (do_not_double_check_solution -> true ; block_real_um_wf_aux2(X,R,WF)).
492 real_um_wf_aux(X,R,WF) :- block_real_um_wf_aux2(X,R,WF).
493
494
495 :- block block_real_um_wf_aux2(-,-,?).
496 block_real_um_wf_aux2(X,R,_) :- nonvar(X),!,R is -X.
497 block_real_um_wf_aux2(X,R,_) :- X is -R.
498
499 % used for 'RABS' external function
500 :- block 'real_absolute_value_wf'(-,-,?).
501 real_absolute_value_wf(term(floating(X)),term(floating(R)),WF) :-
502 real_abs_wf_aux(X,R,WF).
503 real_abs_wf_aux(X,R,WF) :-
504 use_clpfd_real_solver,!,
505 '$='(R,abs(X)),
506 (do_not_double_check_solution -> true ; when(nonvar(X), safe_is(R,abs(X),WF))).
507 real_abs_wf_aux(X,R,WF) :- block_real_abs_wf_aux(X,R,WF).
508
509 :- block block_real_abs_wf_aux(-,-,?).
510 block_real_abs_wf_aux(X,R,_) :- nonvar(X),!, R is abs(X).
511 block_real_abs_wf_aux(X,R,_) :- R = 0.0, !, X=0.0.
512 block_real_abs_wf_aux(_,R,_) :- R < 0.0, !, fail.
513 block_real_abs_wf_aux(X,R,WF) :- MR is -R,
514 Solutions = [R,MR],
515 set_solution_for_expr(X,Solutions,WF).
516
517
518 % used for 'RSIGN' external function
519 :- block 'real_sign_wf'(-,-,?).
520 real_sign_wf(term(floating(X)),term(floating(R)),WF) :-
521 real_sign_wf_aux(X,R,WF).
522
523 real_sign_wf_aux(X,R,WF) :-
524 use_clpfd_real_solver,!,
525 '#='(IR,sign(X)), % Note: it is important to use sign(.) directly here with #=,
526 % otherwise we get an expected 'numeric argument' error
527 convert_int_to_real_aux(IR,R),
528 (do_not_double_check_solution -> true ; block_real_unop_wf(sign,X,R,unknown,WF)).
529 real_sign_wf_aux(X,R,WF) :- block_real_unop_wf(sign,X,R,unknown,WF).
530
531
532 % used for 'ROUND' external function
533 real_round_wf(term(floating(X)),int(R),_WF) :- real_round_aux(X,R).
534
535 real_round_aux(Real,Int) :-
536 use_clpfd_real_solver,!,
537 '#='(Int,round(Real)).
538 real_round_aux(Real,Int) :- block_real_round_aux(Real,Int).
539
540 :- block block_real_round_aux(-,?).
541 block_real_round_aux(Real,Int) :- Int is round(Real).
542
543
544 :- use_module(kernel_waitflags,[add_wd_error/3, add_wd_error_span/4]).
545 % a version of is/2 which catches overflows
546 safe_is(R,Expr,WF) :-
547 safe_is(R,Expr,unknown,WF).
548 safe_is(R,Expr,Span,WF) :-
549 catch(R is Expr,
550 error(evaluation_error(ERR),_),
551 process_evaluation_error(ERR,Expr,Span,WF)).
552
553 process_evaluation_error(float_overflow,Expr,Span,WF) :- !,
554 add_wd_error_span('Float Overflow while computing:',Expr,Span,WF).
555 process_evaluation_error(zero_divisor,Expr,Span,WF) :- !,
556 add_wd_error_span('Division by zero while computing:',Expr,Span,WF).
557 process_evaluation_error(undefined,Expr,Span,WF) :- !,
558 add_wd_error_span('Arithmetic operator undefined while computing:',Expr,Span,WF).
559 process_evaluation_error(_,Expr,Span,WF) :-
560 add_wd_error_span('Unknown evaluation error while computing:',Expr,Span,WF).
561
562
563 % call a Prolog unary artihmetic operator
564 real_unop_wf(OP,X,R,WF) :-
565 real_unop_wf(OP,X,R,unknown,WF).
566
567 real_unop_wf(OP,RX,RR,Span,WF) :-
568 get_real(RX,X,OP,Span,WF), get_real(RR,R,OP,Span,WF),
569 real_unop_wf_aux(OP,X,R,Span,WF).
570
571 unsupported_unary_operator(float_fractional_part). % RFRACTION
572
573 real_unop_wf_aux(OP,X,R,Span,WF) :-
574 use_clpfd_real_solver,
575 \+ unsupported_unary_operator(OP),!,
576 Expr =.. [OP,X],
577 post_real_equal_expr_wf(R,Expr,Span,WF), % TODO: catch WD issues (RASIN(>1.0), ...)
578 (do_not_double_check_solution -> true
579 ; block_real_unop_wf(OP,X,R,Span,WF)). % relevant e.g. for {y|x = RSQRT(y)}=r & x=4.0
580 real_unop_wf_aux(OP,X,R,Span,WF) :- block_real_unop_wf(OP,X,R,Span,WF).
581
582 :- block block_real_unop_wf(-,?,?,?,?), block_real_unop_wf(?,-,?,?,?).
583 block_real_unop_wf(OP,X,R,Span,WF) :-
584 Expr =.. [OP,X],
585 safe_is(R,Expr,Span,WF).
586
587
588 :- use_module(probsrc(tools_strings),[ajoin/2]).
589 :- use_module(kernel_waitflags,[add_error_wf/5]).
590 get_real(Var,Real,_,_,_) :- var(Var),!, Var=term(floating(Real)).
591 get_real(term(F),Real,_,_,_) :- !, F=floating(Real).
592 get_real(Other,_,OP,Span,WF) :-
593 ajoin(['Argument for ',OP,' is not a real number:'],Msg),
594 add_error_wf(kernel_reals,Msg,Other,Span,WF).
595
596 % when called for ** operator (power_of_real) the exponent Y is a natural number represented as a float;
597 % for RPOW Y can be any float
598 real_power_of_wf(RX,RY,RR,Span,WF) :-
599 get_real(RX,X,'RPOW',Span,WF), get_real(RY,Y,'RPOW',Span,WF), get_real(RR,R,'RPOW',Span,WF),
600 (Y==2.0 % often we use built-in ** with integer 2; maybe we should distinguish ** and RPOW
601 -> real_mul_wf_aux0(X,X,R,WF)
602 ; real_power_of_aux(X,Y,R,Span,WF)
603 ).
604
605 :- if(fail). %can_use_clpfd_real_solver). % % 4.10 or higher
606 % code still deactivated as i:10..20 & 10.0**i + 1.0 = 10.0**i from test 2430 crashes SICStus
607 real_power_of_aux(X,Y,R,Span,WF) :- % write(rpow(X,Y,R)),nl,flush_output,
608 real_binop_wf_aux('^',X,Y,R,Span,WF).
609 :- else.
610 :- block real_power_of_aux(-,-,?,?,?), real_power_of_aux(?,-,-,?,?), real_power_of_aux(-,?,-,?,?).
611 % we currently require to know at least the exponent, TODO: in future we could also compute the log
612 real_power_of_aux(X,Y,R,Span,WF) :-
613 var(X),!,
614 real_power_inverse(X,Y,R,Span,WF).
615 real_power_of_aux(X,Y,Res,Span,WF) :-
616 (Y==2.0 % typically we use built-in ** with integer 2; maybe we should distinguish ** and RPOW
617 -> real_mul_wf_aux0(X,X,Res,WF)
618 ; real_power_direct(X,Y,Res,Span,WF)).
619
620 % X is variable, Y and R are known
621 real_power_inverse(X,Y,R,_Span,WF) :- Y=2.0,!,
622 kernel_square_inverse(X,R,WF).
623 real_power_inverse(X,Y,R,_Span,_WF) :- Y=1.0,!, X=R.
624 real_power_inverse(X,Y,R,Span,WF) :-
625 real_power_direct(X,Y,R,Span,WF).
626 % TODO: add more exponents and compute root
627 :- endif.
628
629 :- block real_power_direct(-,?,?,?,?), real_power_direct(?,-,?,?,?).
630 real_power_direct(X,Y,R,Span,WF) :- write(safe_is(R,expr(X,Y))),nl,
631 safe_is(R,'**'(X,Y),Span,WF). % is the same as exp(X,Y) in SICStus, but SWI does not support exp/2
632
633 % call a Prolog binary artihmetic operator: min, max, atan2, log
634 real_binop_wf(OP,RX,RY,RR,Span,WF) :-
635 get_real(RX,X,OP,Span,WF), get_real(RY,Y,OP,Span,WF), get_real(RR,R,OP,Span,WF),
636 real_binop_wf_aux(OP,X,Y,R,Span,WF).
637
638 % atan2, log are not supported by CLP(FD)
639 supported_binary_operator(min).
640 supported_binary_operator(max).
641 supported_binary_operator('^').
642
643 real_binop_wf_aux(OP,X,Y,R,Span,WF) :- supported_binary_operator(OP),
644 use_clpfd_real_solver,!,
645 Expr =.. [OP,X,Y],
646 post_real_equal_expr_wf(R,Expr,Span,WF), % TODO: double check necessary??
647 (do_not_double_check_solution -> true
648 ; block_real_binop_wf(OP,X,Y,R,Span,WF)). % necessary for min/max ?
649 real_binop_wf_aux(OP,X,Y,R,Span,WF) :- block_real_binop_wf(OP,X,Y,R,Span,WF).
650
651 :- block block_real_binop_wf(-,?,?,?,?,?), block_real_binop_wf(?,-,?,?,?,?), block_real_binop_wf(?,?,-,?,?,?).
652 block_real_binop_wf(OP,X,Y,R,Span,WF) :-
653 Expr =.. [OP,X,Y],
654 safe_is(R,Expr,Span,WF).
655
656 real_less_than_wf(term(floating(X)),term(floating(Y)),_WF) :-
657 clpfd_less_real(X,Y).
658 real_less_than_equal_wf(term(floating(X)),term(floating(Y)),_WF) :-
659 clpfd_leq_real(X,Y).
660
661 % call a Prolog binary artihmetic operator, reified version.
662 real_comp_wf(OP,term(floating(X)),term(floating(Y)),R,_WF) :-
663 real_comp_float(OP,X,Y,R).
664
665 clpfd_leq_real(X,Y) :- use_clpfd_real_solver,!,'$=<'(X,Y).
666 clpfd_leq_real(X,Y) :- real_comp_float_blocking('=<',X,Y,pred_true).
667
668 clpfd_less_real(X,Y) :- use_clpfd_real_solver(S),!,
669 ( number(Y), S=precise_float_solver, next_smaller_float(Y,Y1) -> '$=<'(X,Y1)
670 ; number(X), S=precise_float_solver, next_larger_float(X,X1) -> '$=<'(X1,Y)
671 ; '$=<'(X,Y), dif(X,Y)
672 ).
673 clpfd_less_real(X,Y) :- real_comp_float_blocking('<',X,Y,pred_true).
674
675 real_comp_float('=<',X,Y,R) :- use_clpfd_real_solver, !,
676 '#<=>'('$=<'(X,Y),R01),
677 when(?=(X,Y),(X=Y -> R01=1; true)), % overcome limitation of CLP(FD) reification SPRM-21561
678 b_interpreter_check:prop_pred_01(R,R01).
679 real_comp_float('<',X,Y,R) :- use_clpfd_real_solver, !,
680 '#<=>'( '#/\\'('$=<'(X,Y), '#\\'('$='(Y,X))), R01), % does not work due to SPRM-21561
681 when(?=(X,Y),(X=Y -> R01=0; true)), % overcome limitation of CLP(FD) reification SPRM-21561
682 b_interpreter_check:prop_pred_01(R,R01).
683 % TODO: reify =:=, =\\= using post_real_equality
684 real_comp_float(OP,X,Y,R) :- real_comp_float2(OP,X,Y,R).
685
686 :- block real_comp_float2(?,-,?,-), real_comp_float2(?,?,-,-).
687 real_comp_float2('<',X,Y,R) :- R == pred_true,!, clpfd_less_real(X,Y).
688 real_comp_float2('<',X,Y,R) :- R == pred_false,!, clpfd_leq_real(Y,X).
689 %real_comp_float2('=<',X,Y,R) :- R == pred_true,!, clpfd_leq_real(X,Y). % not necessary, as reified
690 %real_comp_float2('=<',X,Y,R) :- R == pred_false,!, clpfd_less_real(Y,X).
691 real_comp_float2(OP,X,Y,R) :- real_comp_float_blocking(OP,X,Y,R).
692
693 :- block real_comp_float_blocking(-,?,?,?), real_comp_float_blocking(?,-,?,?), real_comp_float_blocking(?,?,-,?).
694 real_comp_float_blocking(OP,X,Y,R) :-
695 (call(OP,X,Y) -> R=pred_true ; R=pred_false).
696
697 % -----------------
698
699 is_real_interval_closure_with_known_card([Par],[real],b(Body,_,_),Card) :-
700 get_geq_leq_real_bounds(Body,Par,Low,Up),
701 known_card(Low,Up,Card).
702
703
704 % a version of is_interval_closure_or_integerset that only accepts real intervals
705 is_interval_closure_or_realset(Var,_,_) :- var(Var),!,fail.
706 is_interval_closure_or_realset(global_set('REAL'),minus_inf,inf).
707 is_interval_closure_or_realset(closure([Par],[real],b(Body,_,_)),Low,Up) :-
708 get_geq_leq_real_bounds(Body,Par,Low,Up).
709
710 known_card(Low,_Up,Card) :- Low == minus_inf,!,Card=inf.
711 known_card(_Low,Up,Card) :- Up == inf,!,Card=inf.
712 known_card(Low,Up,Card) :- Low==Up,!,Card=1.
713
714
715
716 :- use_module(custom_explicit_sets,[intersect_intervals_with_inf/6]).
717 % TODO: member
718 get_geq_leq_real_bounds(conjunct(b(LEFT,pred,_),b(RIGHT,pred,_)), Par,Low,Up) :-
719 get_geq_leq_real_bounds(LEFT,Par,From1,To1),
720 get_geq_leq_real_bounds(RIGHT,Par,From2,To2),
721 intersect_intervals_with_inf(From1,To1,From2,To2,Low,Up).
722 get_geq_leq_real_bounds(less_equal_real(b(A,_,_),b(B,_,_)),Par,Low,Up) :- get_rbounds2_leq(A,B,Par,Low,Up).
723 get_geq_leq_real_bounds( less_real(b(A,_,_),b(B,_,_)),Par,Low,Up) :-
724 precise_float_solver_active, %TODO: process strict bounds also in real_solver mode
725 % Note in reals min/max may not be defined for such strict intervals !
726 get_rbounds2_lt(A,B,Par,Low,Up).
727
728 get_rbounds2_leq(identifier(Par),V,Par,minus_inf,X) :- real_value(V,X).
729 get_rbounds2_leq(V,identifier(Par),Par,X,inf) :- real_value(V,X).
730
731 get_rbounds2_lt(identifier(Par),V,Par,minus_inf,VXMinus1) :- real_value(V,VX),block_next_larger_float(VXMinus1,VX).
732 get_rbounds2_lt(V,identifier(Par),Par,VXPlus1,inf) :- real_value(V,VX),block_next_larger_float(VX,VXPlus1).
733
734
735 % try and get a real value from an AST node
736 % see integer_value/2 in custom_explicit_sets
737 real_value(V,_) :- var(V),!, print(var_real_value(V)),nl,fail.
738 real_value(real(X),R) :- !, construct_real_number(X,R).
739 % TODO: unary_minus, ...
740 real_value(value(V),R) :- is_real(V,R).
741 % -----------------
742
743
744 :- use_module(probsrc(kernel_tools),[ground_value_check/2]).
745 :- use_module(probsrc(custom_explicit_sets),[expand_and_convert_to_avl_set/4, expand_custom_set_to_list_wf/5,
746 max_of_explicit_set_wf/3, min_of_explicit_set_wf/3]).
747
748 :- block real_maximum_of_set(-,?,?,?).
749 real_maximum_of_set([],_Res,Span,WF) :- !,
750 add_wd_error_span('max applied to empty set of reals:',[],Span,WF).
751 real_maximum_of_set(avl_set(A),Res,_Span,WF) :- !, max_of_explicit_set_wf(avl_set(A),Res,WF).
752 real_maximum_of_set([RX|T],RRes,Span,WF) :- use_clpfd_real_solver, !,
753 get_real(RX,X,maximum,Span,WF),
754 get_real(RRes,Res,maximum,Span,WF),
755 clpfd_leq_real(X,Res),
756 expand_custom_set_to_list_wf(T,ET,_,real_maximum_of_set,WF),
757 real_max_list(ET,[X],Res,Span,WF).
758 real_maximum_of_set(Set,Res,Span,WF) :-
759 is_interval_closure_or_realset(Set,Low,Up),!,
760 (Up==inf
761 -> add_wd_error_span('maximum of unbounded infinite real set not defined:',Set,Span,WF)
762 ; inf_greater_than_equal_real(Up,Low), % TODO: reify comparison and generate WD error if not? ditto for min and integers
763 is_real(Res,Up)).
764 real_maximum_of_set(Set,Res,Span,WF) :-
765 ground_value_check(Set,Gr),
766 rmax_ground_set(Gr,Set,Res,Span,WF).
767
768 % a comparison also taking inf/minus_inf values into account:
769 inf_greater_than_equal_real(X,Y) :-
770 ((X==inf;Y==minus_inf) -> true ; clpfd_leq_real(Y,X)).
771
772 :- block real_max_list(-,?,?,?,?).
773 real_max_list([],List,Res,_Span,_WF) :- clpfd:maximum(Res,List). % requires new CLP(FD) solver in 4.10
774 real_max_list([RX|T],Acc,Res,Span,WF) :-
775 get_real(RX,X,maximum,Span,WF),
776 clpfd_leq_real(X,Res), % not useful if list skeleton known
777 real_max_list(T,[X|Acc],Res,Span,WF).
778
779 :- block rmax_ground_set(-,?,?,?,?).
780 rmax_ground_set(_,Set,Res,Span,WF) :-
781 expand_and_convert_to_avl_set(Set,ESet,'RMAXIMUM','RMAXIMUM'),
782 (ESet=empty
783 -> add_wd_error_span('max applied to empty set of reals:',Set,Span,WF)
784 ; max_of_explicit_set_wf(avl_set(ESet),Res,WF)
785 ).
786
787 :- block real_minimum_of_set(-,?,?,?).
788 real_minimum_of_set([],_Res,Span,WF) :- !,
789 add_wd_error_span('min applied to empty set of reals:',[],Span,WF).
790 real_minimum_of_set(avl_set(A),Res,_Span,WF) :- !, min_of_explicit_set_wf(avl_set(A),Res,WF).
791 real_minimum_of_set([RX|T],RRes,Span,WF) :- use_clpfd_real_solver, !,
792 get_real(RX,X,minumum,Span,WF),
793 get_real(RRes,Res,minumum,Span,WF),
794 clpfd_leq_real(Res,X),
795 expand_custom_set_to_list_wf(T,ET,_,real_minimum_of_set,WF),
796 real_min_list(ET,[X],Res,Span,WF).
797 real_minimum_of_set(Set,Res,Span,WF) :-
798 is_interval_closure_or_realset(Set,Low,Up),!,
799 (Low == minus_inf
800 -> add_wd_error_span('minimum of unbounded infinite real set not defined:',Set,Span,WF)
801 ; inf_greater_than_equal_real(Up,Low),
802 is_real(Res,Low)).
803 real_minimum_of_set(Set,Res,Span,WF) :-
804 ground_value_check(Set,Gr),
805 rmin_ground_set(Gr,Set,Res,Span,WF).
806
807 :- block real_min_list(-,?,?,?,?).
808 real_min_list([],List,Res,_Span,_WF) :- clpfd:minimum(Res,List). % requires new CLP(FD) solver in 4.10
809 real_min_list([RX|T],Acc,Res,Span,WF) :-
810 get_real(RX,X,minimum,Span,WF),
811 clpfd_leq_real(Res,X), % not useful if list skeleton known
812 real_min_list(T,[X|Acc],Res,Span,WF).
813
814 :- block rmin_ground_set(-,?,?,?,?).
815 rmin_ground_set(_,Set,Res,Span,WF) :-
816 expand_and_convert_to_avl_set(Set,ESet,'RMINIMUM','RMINIMUM'),
817 (ESet=empty
818 -> add_wd_error_span('min applied to empty set of reals:',Set,Span,WF)
819 ; min_of_explicit_set_wf(avl_set(ESet),Res,WF)
820 ).
821
822
823 % not used yet: useful for kernel_strings ?
824 %:- block real_to_string(-,?).
825 %real_to_string(term(floating(I)),S) :- real_to_string2(I,S).
826 %
827 %:- block real_to_string2(-,?).
828 %real_to_string2(Num,Res) :-
829 % number_codes(Num,C),
830 % atom_codes(S,C), Res=string(S).
831
832 % -------------------------
833
834 :- use_module(kernel_objects,[gen_enum_warning_wf/6]).
835 :- use_module(library(random),[random/3]).
836
837
838 enumerate_real_wf(term(floating(F)),EnumWarning,WF) :-
839 enum_real(F,EnumWarning,WF).
840
841 enum_real(F,_,_) :- number(F),!.
842 enum_real(F,EnumWarning,WF) :- use_clpfd_real_solver,!,
843 clpfd_enum_real(F,EnumWarning,WF).
844 enum_real(F,EnumWarning,WF) :-
845 gen_enum_warning_wf('REAL',inf,'"0.0","1.0",...',EnumWarning,unknown,WF),
846 ( F = 0.0 ; F = 1.0 ; F = -1.0
847 ; random(0.0,1.0,F)
848 ; random(-1.0,0.0,F)
849 ; preferences:preference(maxint,MaxInt), random(1.0,MaxInt,F)
850 ; preferences:preference(minint,MinInt), random(MinInt,-1.0,F)
851 ).
852
853 :- if(can_use_clpfd_real_solver). % 4.10 or higher
854 clpfd_enum_real(F,EnumWarning,WF) :-
855 fd_min(F,Min), fd_max(F,Max), % finf .. fsup
856 (number(Min),number(Max) ->
857 (preferences:preference(real_solver_precision,Prec), Prec > 0.0
858 -> gen_enum_warning_wf('REAL',Min..Max,precision(Prec),EnumWarning,unknown,WF),
859 labeling([random, precision(Prec)],[F])
860 ; Min \= Max, preferences:preference(solver_for_reals,real_solver)
861 -> gen_enum_warning_wf('REAL',Min..Max,precision(0),EnumWarning,unknown,WF),
862 labeling([random, precision(5.0E-324)],[F]) % there are infinitely many real numbers
863 ; labeling([random],[F])
864 )
865 ; number(Min), preferences:preference(maxint,MaxInt),
866 MaxR is max(float(MaxInt),Min+1.0) % should we use next float instead of + 1.0? or precision(.) preference?
867 -> gen_enum_warning_wf('REAL',Min..fsup,Min..MaxR,EnumWarning,unknown,WF),
868 '$=<'(F,MaxR), labeling([random, precision(0.5)],[F]) % no need to do exhaustive enumeration; use precision
869 ; number(Max), preferences:preference(minint,MinInt),
870 MinR is min(float(MinInt),Max-1.0) % maybe use previous float or use precision(.) preference
871 -> gen_enum_warning_wf('REAL',finf..Max,MinR..Max,EnumWarning,unknown,WF),
872 '$>='(F,MinR), labeling([random, precision(0.5)],[F]) % no need to do exhaustive enumeration; use precision
873 ; preferences:preference(minint,MinInt), MinR is float(MinInt),
874 preferences:preference(maxint,MaxInt), MaxR is float(MaxInt)
875 -> gen_enum_warning_wf('REAL',finf..fsup,MinR..MaxR,EnumWarning,unknown,WF),
876 fd_batch(['$>='(F,MinR), '$=<'(F,MaxR)]),
877 labeling([random, precision(0.5)],[F]) % no need to do exhaustive enumeration; enum warning anyway
878 ).
879 :- else.
880 clpfd_enum_real(F,_,_) :- add_internal_error('Requires SICStus 4.10:',clpfd_enum_real(F,_,_)).
881 :- endif.
882
883 /*
884
885 A few reference tests with SICS 4.10 real support in CLP(FD)
886
887 | ?- X $>= 1.0, fd_dom(X,D), fd_min(X,Min), fd_max(X,Max).
888 D = 1.0 .. fsup,
889 Min = 1.0,
890 Max = fsup,
891 clpfd:(X in1.0 .. fsup) ?
892
893 | ?- X in 1.0 .. 1.000000000000001, labeling([random],[X]).
894 X = 1.0000000000000004 ? ;
895 X = 1.0000000000000009 ? ;
896 X = 1.000000000000001 ? ;
897 X = 1.0000000000000007 ? ;
898 X = 1.0 ? ;
899 X = 1.0000000000000002 ? ;
900 no
901
902 | ?- X in 1.0 .. 2.0, X + 0.5 $= 2.0, labeling([precision(0.0)],[X]), Delta is (X + 0.5) - 2.0, X + 0.5 =:= 2.0.
903 X = 1.5,
904 Delta = 0.0 ? ;
905 X = 1.5000000000000002,
906 Delta = 0.0 ? ;
907 no
908
909 % enumeration always starts with 1.0 here and not very random:
910 | ?- X $>= -1.0, X $=< 3.0, labeling([random],[X]).
911 X = 1.0 ? ;
912 X = -5.551115123125783E-17 ? ;
913 X = 0.49999999999999994 ? ;
914 X = 0.24999999999999992 ? ;
915 X = 0.3749999999999999 ? ;
916 X = 0.4374999999999999 ? ;
917 X = 0.4062499999999999 ? ;
918 X = 0.3906249999999999 ? ;
919 X = 0.3984374999999999 ? ;
920 X = 0.4023437499999999 ? ;
921 X = 0.4042968749999999 ? ;
922 X = 0.4033203124999999 ? ;
923 X = 0.4038085937499999 ? ;
924 X = 0.4035644531249999 ? ;
925 X = 0.4034423828124999 ? ;
926 X = 0.4035034179687499 ? ;
927 X = 0.4035339355468749 ? ;
928 X = 0.4035186767578124 ? ;
929 X = 0.40351104736328114 ? ;
930
931 | ?- X $= 1.0 + Y, X = 2.0.
932 X = 2.0,
933 clpfd:(Y in0.9999999999999996 .. 1.0000000000000004) ?
934 yes
935
936 | ?- X $= 1.0 + Y, X = 2.0, labeling([random],[Y]), write(Y),nl, (X is 1.0 + Y -> true ; write(ko),nl,fail).
937 1.0
938 X = 2.0,
939 Y = 1.0 ? ;
940 0.9999999999999998
941 ko
942 0.9999999999999997
943 ko
944 0.9999999999999996
945 ko
946 0.9999999999999999
947 X = 2.0,
948 Y = 0.9999999999999999 ? ;
949 1.0000000000000004
950 ko
951 1.0000000000000002
952 X = 2.0,
953 Y = 1.0000000000000002 ? ;
954 no
955 | ?- (X $>= 1.0) #<=> R, X $>= 1.1.
956 R = 1,
957 clpfd:(X in1.1 .. fsup) ?
958 yes
959
960 | ?- X in 11..12, Y $>= float(X).
961 clpfd:(Y in 11.0..fsup),
962 X in 11..12 ?
963 yes
964 | ?- Y in 11.1 .. 12.0, X #>= floor(Y).
965 clpfd:(Y in 11.1..12.0),
966 X in 11..sup ?
967 yes
968 | ?- Y in 11.1 .. 12.0, X #>= ceiling(Y).
969 clpfd:(Y in 11.1..12.0),
970 X in 12..sup ?
971 | ?- X in 1.0 .. 2.5, Y $= sin(X).
972 clpfd:(X in 1.0..2.5),
973 clpfd:(Y in 0.5984721441039559..1.0) ?
974 yes
975 | ?- X in 1.0 .. 2.5, Y $= cos(X).
976 clpfd:(X in 1.0..2.5),
977 clpfd:(Y in-0.8011436155469343..0.54030230586814) ?
978 yes
979 | ?- X in 1.0 .. 2.5, Y $= sinh(X).
980 clpfd:(X in 1.0..2.5),
981 clpfd:(Y in 1.1752011936438012..6.050204481039793) ?
982 yes
983 | ?- X in 1.0 .. 2.5, Y $= tanh(X).
984 clpfd:(X in 1.0..2.5),
985 clpfd:(Y in 0.7615941559557647..0.9866142981514303) ?
986 yes
987
988 | ?- I in 1.0 .. 2.0, J in 1.5 .. 2.1, maximum(Max,[I,J]).
989 clpfd:(I in 1.0..2.0),
990 clpfd:(J in 1.5..2.1),
991 clpfd:(Max in 1.5..2.1) ?
992 yes
993 */