Source file fcl_linear.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
open Fcl_var
open Fcl_misc.Operators
open Printf
module C = Fcl_cstr
type operator = LessThan | Equal | Diff
let string_of_op = function Equal -> "=" | LessThan -> "<=" | Diff -> "<>"
let min_max_plus_inter a b c d = (a + c, b + d)
let min_max_minus_inter a b c d = (a - d, b - c)
let rec less_than x1 x2 d =
let name = "less_than" in
let update _ =
match Fd.value x1, Fd.value x2 with
Unk a1, Unk a2 ->
let max1 = Attr.max a1
and max2d = Attr.max a2 + d in
if max1 > max2d then
Fd.refine x1 (Fcl_domain.remove_up max2d (Attr.dom a1));
let min2 = Attr.min a2
and min1d = Attr.min a1 - d in
if min1d > min2 then
Fd.refine x2 (Fcl_domain.remove_low min1d (Attr.dom a2));
Fd.max x1 <= Fd.min x2 + d
| Val v1, Unk a2 ->
let min2 = Attr.min a2 in
if min2 < v1 - d then
Fd.refine x2 (Fcl_domain.remove_low (v1-d) (Attr.dom a2));
true
| Unk a1, Val v2 ->
let max1 = Attr.max a1 in
if max1 > v2+d then
Fd.refine x1 (Fcl_domain.remove_up (v2+d) (Attr.dom a1));
true
| Val v1, Val v2 ->
v1 <= v2+d || Fcl_stak.fail name
and check () =
let min1, max1 = Fd.min_max x1
and min2, max2 = Fd.min_max x2 in
max1 <= min2 + d || (if min1 > max2 + d then false else raise C.DontKnow)
and not () = less_than x2 x1 (-1-d)
and delay ct =
delay [Fd.on_min] x1 ct;
delay [Fd.on_max] x2 ct
and fprint f =
Printf.fprintf f "%a <= %a + %d" Fd.fprint x1 Fd.fprint x2 d
in
C.create ~not ~check ~name ~fprint update delay
let rec equalc x1 k x2 =
let bounds () = (Fd.min_max x1, Fd.min_max x2) in
let name = "equalc" in
let update _ =
Fcl_debug.call 'a' (fun s -> fprintf s "equalc - before update: %a = %d * %a\n" Fd.fprint x1 k Fd.fprint x2);
if k = 0 then begin Fd.unify x1 0; true end else
let rec loop () =
match Fd.value x1, Fd.value x2 with
Unk _, Unk _ ->
let cbounds = bounds () in
let (c, d) = Fd.min_max x2 in
let (kc, kd) = if k > 0 then (k*c, k*d) else (k*d, k*c) in
Fd.refine_low_up x1 kc kd;
let (a, b) = Fd.min_max x1 in
let (ak, bk) = if k > 0 then (a /+ k, b /- k) else (b /+ k, a /- k) in
Fd.refine_low_up x2 ak bk;
if cbounds <> bounds () then loop () else false
| Val v1, Unk _a2 -> Fd.unify x2 (v1/k); true
| Unk _a1, Val v2 -> Fd.unify x1 (k*v2); true
| Val v1, Val v2 -> v1 = k*v2 || Fcl_stak.fail name in
loop ()
and check () =
match Fd.value x1, Fd.value x2 with
Unk _, Unk _ ->
let (a, b) = Fd.min_max x1 in
let (c, d) = Fd.min_max x2 in
let (kc, kd) = if k > 0 then (k*c, k*d) else (k*d, k*c) in
if a > kd || b < kc then false else raise C.DontKnow
| Val v1, Unk a2 ->
if not (Fcl_domain.member (v1/k) (Attr.dom a2)) then
false
else raise C.DontKnow
| Unk a1, Val v2 ->
if not (Fcl_domain.member (k*v2) (Attr.dom a1)) then
false
else raise C.DontKnow
| Val v1, Val v2 -> v1 = k*v2
and not () = diffc x1 k x2
and delay ct =
delay [Fd.on_min; Fd.on_max] x1 ct;
delay [Fd.on_min; Fd.on_max] x2 ct
and fprint f =
Printf.fprintf f "%a = %d * %a" Fd.fprint x1 k Fd.fprint x2 in
C.create ~name ~fprint ~not ~check update delay
and diffc x1 k x2 =
let name = "diffc" in
let update _ =
if k = 0 then
match Fd.value x1 with
Unk a1 -> Fd.refine x1 (Fcl_domain.remove 0 (Attr.dom a1)); true
| Val v1 -> v1 <> 0 || Fcl_stak.fail name
else
match Fd.value x1, Fd.value x2 with
Unk _, Unk _ ->
let (c, d) = Fd.min_max x2 in
let (kc, kd) = if k > 0 then (k*c, k*d) else (k*d, k*c) in
let (a, b) = Fd.min_max x1 in
(a > kd || b < kc)
| Val v1, Unk a2 ->
v1 mod k <> 0 || not (Fcl_domain.member (v1/k) (Attr.dom a2))
| Unk a1, Val v2 -> not (Fcl_domain.member (k*v2) (Attr.dom a1))
| Val v1, Val v2 -> v1 <> k*v2 || Fcl_stak.fail name
and check () =
match Fd.value x1, Fd.value x2 with
Unk _, Unk _ ->
let (a, b) = Fd.min_max x1 in
let (c, d) = Fd.min_max x2 in
let (kc, kd) = if k > 0 then (k*c, k*d) else (k*d, k*c) in
(a > kd || b < kc) || raise C.DontKnow
| Val v1, Unk a2 ->
not (Fcl_domain.member (v1/k) (Attr.dom a2)) || raise C.DontKnow
| Unk a1, Val v2 ->
not (Fcl_domain.member (k*v2) (Attr.dom a1)) || raise C.DontKnow
| Val v1, Val v2 -> v1 <> k*v2
and not () = equalc x1 k x2
and delay ct =
delay [Fd.on_min; Fd.on_max] x1 ct;
delay [Fd.on_min; Fd.on_max] x2 ct
and fprint f =
Printf.fprintf f "%a <> %d * %a" Fd.fprint x1 k Fd.fprint x2 in
C.create ~name ~fprint ~not ~check update delay
let remove_constants terms =
let modif = ref false in
let r =
List.fold_left
(fun (cst, vars) ((a, x) as ax) ->
match Fd.value x with
Unk _ -> (cst, ax::vars)
| Val v -> modif := true; (cst+a*v, vars))
(0, [])
terms in
if !modif then r else raise Not_found
let compute_inf_sup pos_terms neg_terms =
let neg_inf_sum, neg_sup_sum =
List.fold_left
(fun (inf,sup) (c,x) ->
let mi, ma = Fd.min_max x in (inf+c*ma, sup+c*mi))
(0, 0)
neg_terms in
List.fold_left
(fun (inf,sup) (c,x) ->
let mi,ma = Fd.min_max x in (inf+c*mi, sup+c*ma))
(neg_inf_sum, neg_sup_sum)
pos_terms
let part_pos_neg l =
let rec loop pos neg = function
[] -> (pos, neg)
| (0, _) :: axs -> loop pos neg axs
| (a, _x) as ax :: axs ->
if a > 0 then loop (ax :: pos) neg axs
else loop pos (ax :: neg) axs in
loop [] [] l
let linear_aux terms c =
let (pos, neg) = part_pos_neg terms in
let (a, b) = compute_inf_sup pos neg in
let (ac, bc) = (a - c, b - c) in
Fd.interval ac bc
let basic_refinements pos_terms neg_terms d op =
if op <> Diff then
if List.for_all (fun (_a, x) -> Fd.min x >= 0 ) pos_terms &&
List.for_all (fun (_a, x) -> Fd.max x <= 0 ) neg_terms then begin
List.iter (fun (a, x) -> Fd.refine_up x (d/a)) pos_terms;
List.iter (fun (a, x) -> Fd.refine_low x (d/a)) neg_terms end
else if op = Equal &&
List.for_all (fun (_a, x) -> Fd.max x <= 0 ) pos_terms &&
List.for_all (fun (_a, x) -> Fd.min x >= 0 ) neg_terms then begin
List.iter (fun (a, x) -> Fd.refine_low x (d/a)) pos_terms;
List.iter (fun (a, x) -> Fd.refine_up x (d/a)) neg_terms end
let rec linear_cstr terms pos_terms neg_terms op d =
let pos_terms = Fcl_stak.ref pos_terms
and neg_terms = Fcl_stak.ref neg_terms
and d = Fcl_stak.ref d in
let name = "linear" in
let delay c =
List.iter
(fun (a, x) ->
match op with
Diff -> delay [Fd.on_subst] x c
| Equal -> delay [Fd.on_refine] x c
| LessThan -> delay [if a > 0 then Fd.on_min else Fd.on_max] x c)
terms in
let fprint c =
List.iter (fun (a,x) -> Printf.fprintf c " +%d.%a" a Fd.fprint x)
(Fcl_stak.get pos_terms);
List.iter (fun (a,x) -> Printf.fprintf c " %d.%a" a Fd.fprint x)
(Fcl_stak.get neg_terms);
Printf.fprintf c " %s %d" (string_of_op op) (Fcl_stak.get d);
flush c in
let not () =
let terms = Fcl_stak.get pos_terms @ Fcl_stak.get neg_terms
and d = Fcl_stak.get d in
match op with
Equal -> cstr terms Diff d
| Diff -> cstr terms Equal d
| LessThan ->
cstr (List.map (fun (a,x) -> (-a, x)) terms) LessThan (-1 - d)
and check () =
let inf_sum, sup_sum =
compute_inf_sup (Fcl_stak.get pos_terms) (Fcl_stak.get neg_terms) in
let d = Fcl_stak.get d in
match op with
Equal ->
if inf_sum = sup_sum then inf_sum = d else
if sup_sum < d || inf_sum > d then false else
raise C.DontKnow
| Diff ->
if inf_sum = sup_sum then inf_sum <> d else
sup_sum < d || inf_sum > d || raise C.DontKnow
| LessThan ->
sup_sum <= d ||
(if inf_sum > d then false else begin
Fcl_debug.call 'a' (fun s -> fprintf s "Dont know\n");
raise C.DontKnow end)
and update i =
Fcl_debug.call 'a' (fun s -> fprintf s "linear - before update:"; fprint s; Printf.fprintf s "\n");
assert(i = 0);
begin try
let (cst, new_pos_terms) = remove_constants (Fcl_stak.get pos_terms) in
if cst <> 0 then Fcl_stak.set d (Fcl_stak.get d - cst);
Fcl_stak.set pos_terms new_pos_terms
with Not_found -> () end;
begin try
let (cst, new_neg_terms) = remove_constants (Fcl_stak.get neg_terms) in
if cst <> 0 then Fcl_stak.set d (Fcl_stak.get d - cst);
Fcl_stak.set neg_terms new_neg_terms
with Not_found -> () end;
let d = Fcl_stak.get d in
match Fcl_stak.get pos_terms, Fcl_stak.get neg_terms with
([], []) -> begin
match op with
Diff when d <> 0 -> true
| Equal when d = 0 -> true
| LessThan when d >= 0 -> true
| _ -> Fcl_stak.fail "linear[]" end
| ([(a, x)],[]) when op = Equal ->
if d mod a = 0 then
begin Fd.subst x (d/a); true end
else Fcl_stak.fail "linear[(a,x)]"
| ([(a, x)],[]) when op = Diff ->
if d mod a <> 0 then true else
begin match Fd.value x with
Unk attr ->
Fd.refine x (Fcl_domain.remove (d/a) (Attr.dom attr)); true
| Val _ ->
Fcl_debug.internal_error
"linear#update ([(a, x)],[]) when op = Diff" end
| ([],[(a, x)]) when op = Equal ->
if d mod (-a) = 0 then
begin Fd.subst x (d/a); true end
else Fcl_stak.fail "linear[(a,x)]"
| ([(1, x1)], [(-1,x2)]) when op = Equal ->
begin match Fd.value x1, Fd.value x2 with
Unk a1, Unk a2 ->
let d1 = Attr.dom a1 and d2 = Attr.dom a2 in
Fd.refine x1 (Fcl_domain.intersection d1 (Fcl_domain.plus d2 d));
Fd.refine x2 (Fcl_domain.intersection d2 (Fcl_domain.plus d1 (-d)))
| _ -> Fcl_debug.internal_error "Arith 1 -1" end;
false
| ([(1, x1)], [(-1,x2)]) when op = LessThan ->
begin match Fd.value x1, Fd.value x2 with
Unk a1, Unk a2 ->
let max1 = Attr.max a1 and min2 = Attr.min a2
and max2 = Attr.max a2 and min1 = Attr.min a1 in
if max1 > max2 + d then
Fd.refine x1 (Fcl_domain.remove_up (max2+d) (Attr.dom a1));
if min1 > min2 + d then
Fd.refine x2 (Fcl_domain.remove_low (min1-d) (Attr.dom a2));
(Fd.max x1 <= Fd.min x2 + d)
| _ -> Fcl_debug.internal_error "Arith 1 -1" end
| ([], [(a, x)]) when op = Diff ->
if d mod (-a) <> 0 then true else begin
match Fd.value x with
Unk attr ->
Fd.refine x (Fcl_domain.remove (d/a) (Attr.dom attr)); true
| Val _ ->
Fcl_debug.internal_error
"linear#update ([], [(a, x)]) when op = Diff" end
| _ ->
if op = Diff then false else begin
let modif = ref true
and instantiated = ref true
and solved = ref false in
while !modif || !instantiated do
let last_modif = !modif in
modif := false;
instantiated := false;
let inf_sum, sup_sum =
compute_inf_sup
(Fcl_stak.get pos_terms) (Fcl_stak.get neg_terms) in
let d_inf_sum = d - inf_sum and d_sup_sum = d - sup_sum in
if (op = LessThan && 0 <= d_sup_sum) ||
(d_sup_sum = 0 && d_inf_sum = 0) then solved := true
else if 0 > d_inf_sum || (op = Equal && 0 < d_sup_sum) then
Fcl_stak.fail "linear d_inf_sum"
else if last_modif then begin
let update_pos (a, x) =
match Fd.value x with
Unk ax ->
let domx = Attr.dom ax in
let mi = Fcl_domain.min domx and
ma = Fcl_domain.max domx in
let new_sup = min ma ((d_inf_sum + a*mi)/a) in
if op = Equal then begin
let new_inf = max mi ((d_sup_sum + a*ma) /+ a) in
if new_sup < ma || new_inf > mi then begin
modif:=true;
Fd.refine_low_up x new_inf new_sup end end
else begin
if new_sup < ma then begin
Fd.refine x (Fcl_domain.remove_up new_sup domx);
if not (Fd.is_var x) then instantiated := true end
end
| Val vx ->
let new_sup = ((d_inf_sum + a*vx)/a) in
if new_sup < vx then Fcl_stak.fail "Arith.update_pos sup";
if op = Equal then
let new_inf = (d_sup_sum + a*vx) /+ a in
if new_inf > vx then
Fcl_stak.fail "Arith.update_pos inf" in
List.iter update_pos (Fcl_stak.get pos_terms);
let update_neg (a, x) =
match Fd.value x with
Unk ax ->
let domx = Attr.dom ax in
let mi = Fcl_domain.min domx
and ma = Fcl_domain.max domx in
let new_inf = max mi ((d_inf_sum + a*ma) /+ a) in
if op = Equal then begin
let new_sup = min ma ((d_sup_sum + a*mi)/a) in
if new_sup < ma || new_inf > mi then begin
modif:=true;
Fd.refine_low_up x new_inf new_sup end end
else begin
if new_inf > mi then begin
Fcl_debug.call 'a'
(fun s -> fprintf s
"linear#update, refine_low %d\n" new_inf);
Fd.refine x (Fcl_domain.remove_low new_inf domx);
if not (Fd.is_var x) then instantiated := true end end
| Val vx ->
let new_inf = (d_inf_sum + a*vx) /+ a in
if new_inf > vx then Fcl_stak.fail "Arith.update_neg inf";
if op = Equal then
let new_sup = ((d_sup_sum + a*vx)/a) in
if new_sup < vx then
Fcl_stak.fail "Arith.update_neg sup" in
List.iter update_neg (Fcl_stak.get neg_terms)
end
done;
!solved
end in
let init () =
basic_refinements
(Fcl_stak.get pos_terms) (Fcl_stak.get neg_terms) (Fcl_stak.get d) op;
ignore (update 0) in
C.create ~init ~name ~fprint ~check ~not update delay
and cstr (terms : (int*Fd.t) list) op dd =
let pos_terms, neg_terms = part_pos_neg terms in
match pos_terms, neg_terms, op, dd with
[1, x1], [-1, x2], LessThan, d ->
less_than x1 x2 d
| [1, x1], [k, x2], Equal, 0 when k <> -1 ->
equalc x1 (0 - k) x2
| [k, x1], [-1, x2], Equal, 0 when k <> 1 ->
equalc x2 k x1
| [1, x1], [k, x2], Diff, 0 when k <> -1 ->
diffc x1 (0 - k) x2
| [k, x1], [-1, x2], Diff, 0 when k <> 1 ->
diffc x2 k x1
| _ -> linear_cstr terms pos_terms neg_terms op dd
let boolsum_threshold = ref 5
let get_boolsum_threshold () = !boolsum_threshold
let set_boolsum_threshold x = boolsum_threshold := x
let is_boolean x =
let min_x, max_x = Fcl_var.Fd.min_max x in min_x = 0 && max_x = 1
let rec flatten rest = function
[] -> rest
| (a, l) :: ls ->
List.fold_left (fun r x -> (a,x)::r) (flatten rest ls) l
let cstr ?(boolsum = !boolsum_threshold) terms op d =
let (bools, others) = List.partition (fun (_a, x) -> is_boolean x) terms in
let h = Hashtbl.create 17 in
let add (a, x) =
try
let refxs = Hashtbl.find h a in refxs := x :: !refxs
with Not_found -> Hashtbl.add h a (ref [x]) in
List.iter add bools;
let bool_sums = ref [] and short_bools = ref [] in
Hashtbl.iter
(fun a refxs ->
if List.length !refxs >= boolsum then begin
let bools = Array.of_list !refxs in
Fcl_debug.call 'a' (fun c -> Printf.fprintf c "boolean sum (size %d) optimized\n" (Array.length bools));
let sumxs = Fcl_boolean.sum bools in
bool_sums := (a, sumxs) :: !bool_sums end
else short_bools := (a, !refxs) :: !short_bools)
h;
let short_bools = flatten [] !short_bools in
cstr (!bool_sums @ short_bools @ others) op d
let shift_cstr x1 x2 d =
let update _ =
match Fd.value x1, Fd.value x2 with
Unk a1, Unk a2 ->
let max1 = Attr.max a1
and max2d = Attr.max a2 + d in
if max1 > max2d then
Fd.refine x1 (Fcl_domain.remove_up max2d (Attr.dom a1))
else if max1 < max2d then
Fd.refine x2 (Fcl_domain.remove_up (max1-d) (Attr.dom a2));
let min2 = Attr.min a2
and min1d = Attr.min a1 - d in
if min1d < min2 then
Fd.refine x1 (Fcl_domain.remove_low (min2+d) (Attr.dom a1))
else if min1d > min2 then
Fd.refine x2 (Fcl_domain.remove_low min1d (Attr.dom a2));
max1 <= min2 + d
| Val v1, Unk _ ->
Fd.unify x2 (v1-d);
true
| Unk _, Val v2 ->
Fd.unify x1 (v2+d);
true
| Val v1, Val v2 ->
if v1 <> v2+d then Fcl_stak.fail "shift";
true
and delay ct =
delay [Fd.on_min; Fd.on_max] x1 ct;
delay [Fd.on_max; Fd.on_min] x2 ct
and fprint f =
Printf.fprintf f "%a = %a + %d" Fd.fprint x1 Fd.fprint x2 d in
C.create ~name:"shift" ~fprint update delay