Source file inequalities.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
open Format
open Options
module Z = Numbers.Z
module Q = Numbers.Q
module type S = sig
module P : Polynome.T with type r = Shostak.Combine.r
module MP : Map.S with type key = P.t
type t = {
ple0 : P.t;
is_le : bool;
dep : (Q.t * P.t * bool) Util.MI.t;
expl : Explanation.t;
age : Z.t;
}
module MINEQS : sig
type mp = (t * Q.t) MP.t
val empty : mp
val is_empty : mp -> bool
val younger : t -> t -> bool
val insert : t -> mp -> mp
val ineqs_of : mp -> t list
val add_to_map : mp -> t list -> mp
val iter : (P.t -> (t * Q.t) -> unit) -> mp -> unit
val fold : (P.t -> (t * Q.t) -> 'a -> 'a) -> mp -> 'a -> 'a
end
val current_age : unit -> Numbers.Z.t
val incr_age : unit -> unit
val create_ineq :
P.t -> P.t -> bool -> Expr.t option -> Explanation.t -> t
val print_inequation : Format.formatter -> t -> unit
val fourierMotzkin :
('are_eq -> 'acc -> P.r option -> t list -> 'acc) -> 'are_eq -> 'acc ->
MINEQS.mp -> 'acc
val fmSimplex :
('are_eq -> 'acc -> P.r option -> t list -> 'acc) -> 'are_eq -> 'acc ->
MINEQS.mp -> 'acc
val available :
('are_eq -> 'acc -> P.r option -> t list -> 'acc) -> 'are_eq -> 'acc ->
MINEQS.mp -> 'acc
end
module type Container_SIG = sig
module Make
(P : Polynome.T with type r = Shostak.Combine.r)
: S with module P = P
end
module Container : Container_SIG = struct
module Make
(P : Polynome.T with type r = Shostak.Combine.r)
: S with module P = P = struct
module X = Shostak.Combine
module P = P
module MP = Map.Make(P)
module MX = Map.Make(struct type t = X.r let compare = X.hash_cmp end)
let age_cpt = ref Z.zero
let current_age () = !age_cpt
let incr_age () = age_cpt := Z.add !age_cpt Z.one;
type t = {
ple0 : P.t;
is_le : bool;
dep : (Q.t * P.t * bool) Util.MI.t;
expl : Explanation.t;
age : Z.t;
}
let print_inequation fmt ineq =
fprintf fmt "%a %s 0 %a" P.print ineq.ple0
(if ineq.is_le then "<=" else "<") Explanation.print ineq.expl
let create_ineq p1 p2 is_le a expl =
let ple0 = P.sub p1 p2 in
match P.to_list ple0 with
| ([], ctt) when is_le && Q.sign ctt > 0->
raise (Intervals.NotConsistent expl)
| ([], ctt) when not is_le && Q.sign ctt >= 0 ->
raise (Intervals.NotConsistent expl)
| _ ->
let p,c,d = P.normal_form ple0 in
assert (Q.compare d Q.zero > 0);
let c = if P.type_info p == Ty.Treal then c else (Q.ceiling c) in
let p = P.add_const c p in
let dep = match a with
| Some a -> Util.MI.singleton (Expr.uid a) (Q.one, p, is_le)
| None -> Util.MI.empty
in
{ ple0 = p; is_le = is_le;
dep = dep;
expl = expl; age = !age_cpt }
let find_coefficient x ineq = P.find x ineq.ple0
let split_pos_neg _ ({ ple0 = p ; age; _ }, _) (mx, nb_max) =
let mx = List.fold_left (fun m (c,x) ->
let cmp = Q.sign c in
if cmp = 0 then m
else
let (pos, neg) = try MX.find x m with Not_found -> (0,0) in
if cmp > 0 then MX.add x (pos+1, neg) m
else MX.add x (pos, neg+1) m ) mx (fst (P.to_list p))
in
mx, if Z.equal age !age_cpt then nb_max + 1 else nb_max
module MINEQS = struct
type mp = (t * Q.t) MP.t
let empty = MP.empty
let is_empty mp = MP.is_empty mp
let younger ineq' ineq =
Z.compare ineq'.age ineq.age <= 0
let insert ineq mp =
let p0, ctt = P.separate_constant ineq.ple0 in
try
let ineq', ctt' = MP.find p0 mp in
let cmp = Q.compare ctt' ctt in
if cmp = 0 then
if ineq.is_le == ineq'.is_le then
if younger ineq ineq' then mp
else MP.add p0 (ineq, ctt) mp
else
if ineq.is_le then mp
else MP.add p0 (ineq, ctt) mp
else
if cmp > 0 then
mp
else
MP.add p0 (ineq, ctt) mp
with Not_found -> MP.add p0 (ineq, ctt) mp
let ineqs_of mp = MP.fold (fun _ (ineq, _) acc -> ineq :: acc) mp []
let add_to_map mp l = List.fold_left (fun mp v -> insert v mp) mp l
let iter = MP.iter
let fold = MP.fold
end
module Debug = struct
let list_of_ineqs fmt =
List.iter (fprintf fmt "%a " print_inequation)
let map_of_ineqs fmt =
MINEQS.iter (fun _ (i , _) -> fprintf fmt "%a " print_inequation i)
let cross x vars cpos cneg others =
if Options.debug_fm () then begin
fprintf Options.fmt "[fm] We cross on %a (%d vars remaining)@."
X.print x (MX.cardinal vars);
fprintf Options.fmt "with:@. cpos = %a@. cneg = %a@. others = %a@."
list_of_ineqs cpos list_of_ineqs cneg map_of_ineqs others
end
let cross_result x ninqs =
if Options.debug_fm () then
fprintf Options.fmt
"result of eliminating %a: at most %d new ineqs (not printed)@."
X.print x ninqs
end
let mult_list c dep =
if Q.equal c Q.one then dep
else
Util.MI.fold
(fun a (coef,p,is_le) dep ->
Util.MI.add a (Q.mult coef c, p, is_le) dep
)dep Util.MI.empty
let merge_deps d1 d2 =
Util.MI.merge
(fun _ op1 op2 ->
match op1, op2 with
| None, None -> None
| Some _, None -> op1
| None, Some _ -> op2
| Some(c1,p1, is_le1), Some(c2,p2, is_le2) ->
assert (P.equal p1 p2 && is_le1 == is_le2);
Some (Q.add c1 c2, p1, is_le1)
)d1 d2
let cross x cpos cneg mp =
let nb_inqs = ref 0 in
let rec cross_rec acc l =
Options.exec_thread_yield ();
match l with
| [] -> acc
| { ple0=p1; is_le=k1; dep=d1; expl=ex1; age=a1 }::l ->
let n1 = Q.abs (P.find x p1) in
let acc =
List.fold_left
(fun acc
{ple0=p2; is_le=k2; dep=d2; expl=ex2; age=a2} ->
Options.exec_thread_yield ();
let n2 = Q.abs (P.find x p2) in
let n1, n2 =
if Q.equal n1 n2 then Q.one, Q.one
else n1, n2
in
let p = P.add (P.mult_const n2 p1) (P.mult_const n1 p2) in
let p, c, d = P.normal_form p in
let p = P.add_const c p in
assert (Q.sign d > 0);
let d1 = mult_list (Q.div n2 d) d1 in
let d2 = mult_list (Q.div n1 d) d2 in
let ni =
{ ple0 = p; is_le = k1&&k2;
dep = merge_deps d1 d2;
age = Z.max a1 a2;
expl = Explanation.union ex1 ex2 }
in
incr nb_inqs;
MINEQS.insert ni acc
) acc cpos
in
cross_rec acc l
in
cross_rec mp cneg, !nb_inqs
let split x mp =
let split_rec _ (ineq, _) (cp, cn, co, nb_pos, nb_neg) =
try
let a = find_coefficient x ineq in
if Q.sign a > 0 then ineq::cp, cn, co, nb_pos+1, nb_neg
else cp, ineq::cn, co, nb_pos, nb_neg+1
with Not_found -> cp, cn, MINEQS.insert ineq co, nb_pos, nb_neg
in
MINEQS.fold split_rec mp ([], [], MINEQS.empty, 0, 0)
let choose_var mp =
let pos_neg, nb_max = MINEQS.fold split_pos_neg mp (MX.empty, 0) in
if nb_max = 0 then raise Not_found;
let xopt = MX.fold (fun x (pos, neg) acc ->
match acc with
| None -> Some (x, pos * neg)
| Some (_, c') ->
let c = pos * neg in
if c < c' then Some (x, c) else acc
) pos_neg None in
match xopt with
| Some (x, _) -> x, pos_neg
| None -> raise Not_found
let monome_ineq ineq = P.is_monomial ineq.ple0 != None
let fourierMotzkin add_ineqs are_eq acc mp =
let rec fourier acc mp =
Options.exec_thread_yield ();
if MINEQS.is_empty mp then acc
else
try
let x, vars = choose_var mp in
let cpos, cneg, others, nb_pos, nb_neg = split x mp in
Debug.cross x vars cpos cneg others;
let s_x = Some x in
let acc = add_ineqs are_eq acc s_x cpos in
let acc = add_ineqs are_eq acc s_x cneg in
let size_res = Q.from_int (nb_pos * nb_neg) in
let mp', nb_inqs =
if Q.compare size_res (fm_cross_limit ()) >= 0 &&
Q.sign (fm_cross_limit()) >= 0 then
let u_cpos = List.filter monome_ineq cpos in
let u_cneg = List.filter monome_ineq cneg in
let mp', nb_inq1 = match u_cpos with
| [] -> others, 0
| [_] -> cross x cneg u_cpos others
| _ -> assert false
in
let mp', nb_inq2 = match u_cneg with
| [] -> mp', 0
| [_] -> cross x cpos u_cneg mp'
| _ -> assert false
in
mp', nb_inq1 + nb_inq2
else
cross x cpos cneg others
in
Debug.cross_result x nb_inqs;
fourier acc mp'
with Not_found -> add_ineqs are_eq acc None (MINEQS.ineqs_of mp)
in
fourier acc mp
let fmSimplex _add_ineqs _are_eq _acc _mp =
let msg =
"Not implemented in the default version!"^
"Use the FmSimplex plugin instead" in
failwith msg
let available = fourierMotzkin
end
end
module FM = Container.Make
let current = ref (module Container : Container_SIG)
let initialized = ref false
let set_current mdl = current := mdl
let load_current_inequalities_reasoner () =
match Options.inequalities_plugin () with
| "" ->
if Options.debug_fm () then
eprintf "[Dynlink] Using the 'FM module' for arithmetic inequalities@."
| path ->
if Options.debug_fm () then
eprintf "[Dynlink] Loading the 'inequalities' reasoner in %s ...@." path;
try
MyDynlink.loadfile path;
if Options.debug_fm () then eprintf "Success !@.@."
with
| MyDynlink.Error m1 ->
if Options.debug_fm() then begin
eprintf
"[Dynlink] Loading the 'inequalities' reasoner in \"%s\" failed!@."
path;
Format.eprintf ">> Failure message: %s@.@."
(MyDynlink.error_message m1);
end;
let prefixed_path = sprintf "%s/%s" Config.pluginsdir path in
if Options.debug_fm () then
eprintf
"[Dynlink] Loading the 'inequalities' reasoner in %s with prefix %s@."
path Config.pluginsdir;
try
MyDynlink.loadfile prefixed_path;
if Options.debug_fm () then eprintf "Success !@.@."
with
| MyDynlink.Error m2 ->
if not (Options.debug_fm()) then begin
eprintf
"[Dynlink] Loading the 'inequalities' reasoner in \"%s\" failed!@."
path;
Format.eprintf ">> Failure message: %s@.@."
(MyDynlink.error_message m1);
end;
eprintf
"[Dynlink] Trying to load the plugin from \"%s\" failed too!@."
prefixed_path;
Format.eprintf ">> Failure message: %s@.@."
(MyDynlink.error_message m2);
exit 1
let get_current () =
if not !initialized then
begin
load_current_inequalities_reasoner ();
initialized := true;
end;
!current