package facile

  1. Overview
  2. Docs

Source file fcl_conjunto.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
(***********************************************************************)
(*                                                                     *)
(*                           FaCiLe                                    *)
(*                 A Functional Constraint Library                     *)
(*                                                                     *)
(*            Nicolas Barnier, Pascal Brisset, LOG, CENA               *)
(*                                                                     *)
(* Copyright 2004 CENA. All rights reserved. This file is distributed  *)
(* under the terms of the GNU Lesser General Public License.           *)
(***********************************************************************)
(* $Id: fcl_conjunto.ml,v 1.18 2004/08/12 15:22:07 barnier Exp $ *)

open Fcl_var
module S = Fcl_setDomain.S

(* Gervet C., Interval Propagation to Reason about Sets: Definition
and Implementation of a Practical Language, in CONSTRAINTS journal,
ed. E.C. Freuder,1(3), pp 191-244, 1997
   http://www.icparc.ic.ac.uk/~cg6/ *)

let safe_refine ?(mess = "Conjunto.refine") d glb lub = 
  if not (S.subset glb lub) then Fcl_stak.fail mess;
  SetFd.refine d (Fcl_setDomain.unsafe_interval glb lub)

let refine_low_up x inf sup =
  match Fd.value x with
    Val v -> if v < inf || v > sup then Fcl_stak.fail "Conjunto.refine_low_up"
  | Unk ax -> Fd.refine x (Fcl_domain.remove_low_up inf sup (Attr.dom ax))

let size_cstr d cardinal =
  let update _x =
    let (m,n) = Fd.min_max cardinal
    and glb, lub = SetFd.min_max d in
    let glb_size = S.cardinal glb
    and lub_size = S.cardinal lub in
    (* Rule T12 *)
    let m' = max m glb_size
    and n' = min n lub_size in
    if n' < m' then Fcl_stak.fail "cardinal";
    if m' <> m || n' <> n then refine_low_up cardinal m' n';
    if glb_size = n' then begin     (* Rule T13 *)
      SetFd.unify d glb;
      Fd.unify cardinal n'; (* + T12 *)
      true
    end else if lub_size = m' then begin    (* Rule T14 *)
      SetFd.unify d lub;
      Fd.unify cardinal m'; (* + T12 *)
      true
    end else false
  and delay ct =
    delay [Fd.on_min;Fd.on_max] cardinal ct;
    SetFd.delay [SetFd.on_min;SetFd.on_max] d ct in
  Fcl_cstr.create ~name:"Conjunto.cardinal" update delay;;


let cardinal d =
  let glb, lub = SetFd.min_max d in
  let glb_size = S.cardinal glb
  and lub_size = S.cardinal lub in
  let name = Printf.sprintf "card(%s)" (SetFd.name d) in
  let c = Fd.interval ~name glb_size lub_size in
  Fcl_cstr.post (size_cstr d c);
  c

let inter_cstr d1 d2 d =
  let update _waking =
    Fcl_debug.call 's' (fun c -> Printf.fprintf c ">inter_cstr %a %a %a\n" SetFd.fprint d1  SetFd.fprint d2  SetFd.fprint d);
    let glb1, lub1 = SetFd.min_max d1
    and glb2, lub2 = SetFd.min_max d2
    and glb, lub = SetFd.min_max d in
    
    (* T7 *)
    let glb1' = S.union glb1 glb
    and lub1' = S.diff lub1 (S.diff glb2 lub) in
    safe_refine ~mess:"Conjunto.inter T7 1" d1 glb1' lub1';

    let glb2' = S.union glb2 glb
    and lub2' = S.diff lub2 (S.diff glb1 lub) in
    safe_refine ~mess:"Conjunto.inter T7 2" d2 glb2' lub2';

    (* T8 *)
    let glb' = S.inter glb1' glb2' (* glb is alredy in glb1' and glb2' *)
    and lub' = S.inter lub (S.inter lub1' lub2') in
    assert(S.subset glb' lub');
    SetFd.refine d (Fcl_setDomain.interval glb' lub');
    (* Fixpoint ? *)

    Fcl_debug.call 's' (fun c -> Printf.fprintf c "<inter_cstr %a %a %a\n" SetFd.fprint d1  SetFd.fprint d2  SetFd.fprint d);

    not (SetFd.is_var d || SetFd.is_var d1 || SetFd.is_var d2) (* better ? *)

  and delay ct =
    SetFd.delay [SetFd.on_min;SetFd.on_max] d ct;
    SetFd.delay [SetFd.on_min;SetFd.on_max] d1 ct;
    SetFd.delay [SetFd.on_min;SetFd.on_max] d2 ct in

  Fcl_cstr.create ~name:"Conjunto.inter" update delay;;

let inter d1 d2 =
  let glb1, lub1 = SetFd.min_max d1
  and glb2, lub2 = SetFd.min_max d2 in
  let d = SetFd.interval (S.inter glb1 glb2) (S.inter lub1 lub2) in
  Fcl_cstr.post (inter_cstr d1 d2 d);
  d;;

let union_cstr d1 d2 d =
  let update _ =
    let glb1, lub1 = SetFd.min_max d1
    and glb2, lub2 = SetFd.min_max d2
    and glb, lub = SetFd.min_max d in
    
    (* T5 *)
    let glb1' = S.union glb1 (S.diff glb lub2)
    and lub1' = S.inter lub1 lub in
    safe_refine ~mess:"Conjunto.union T5 1" d1 glb1' lub1';

    let glb2' = S.union glb2 (S.diff glb lub1)
    and lub2' = S.inter lub2 lub in
    safe_refine ~mess:"Conjunto.union T5 2" d2 glb2' lub2';

    (* T6 *)
    let glb' = S.union glb (S.union glb1' glb2')
    and lub' = S.union lub1' lub2' in (* lub is already in lub1' and lub2' *)
    assert(S.subset glb' lub');
    SetFd.refine d (Fcl_setDomain.interval glb' lub');

    not (SetFd.is_var d || SetFd.is_var d1 || SetFd.is_var d2) (* better ? *)

  and delay ct =
    SetFd.delay [SetFd.on_min;SetFd.on_max] d ct;
    SetFd.delay [SetFd.on_min;SetFd.on_max] d1 ct;
    SetFd.delay [SetFd.on_min;SetFd.on_max] d2 ct in

  Fcl_cstr.create ~name:"Conjunto.union" update delay;;

let union d1 d2 =
  let glb1, lub1 = SetFd.min_max d1
  and glb2, lub2 = SetFd.min_max d2 in
  let d = SetFd.interval (S.union glb1 glb2) (S.union lub1 lub2) in
  Fcl_cstr.post (union_cstr d1 d2 d);
  d;;

let ith_diff_from_others ds i =
  let update _ =
    let glbi = SetFd.min ds.(i) in
    for j = 0 to Array.length ds -1 do
      if j <> i then
	let glbj, lubj = SetFd.min_max ds.(j) in
	(* Rule T3 (T4) *)
	let lubj' = S.diff lubj glbi in
	if not (S.subset glbj lubj') then Fcl_stak.fail "Conjunto.disjoint";
	SetFd.refine ds.(j) (Fcl_setDomain.interval glbj lubj')
    done;
    not (SetFd.is_var ds.(i))
  and delay ct =
    SetFd.delay [SetFd.on_min] ds.(i) ct in
  Fcl_cstr.create ~name:"Conjunto.ith_diff_from_others" update delay;;

let all_disjoint ds =
  Fcl_cstr.conjunction
    (Fcl_misc.goedel
       (fun i r -> ith_diff_from_others ds i :: r) (Array.length ds) [])

let inside x d =
  let glb,lub = SetFd.min_max d in
  if not (S.mem x lub) then Fcl_stak.fail "inside";
  if not (S.mem x glb) then
    SetFd.refine d (Fcl_setDomain.interval (S.add x glb) lub);;

let outside x d =
  let glb,lub = SetFd.min_max d in
  if S.mem x glb then Fcl_stak.fail "outside";
  if S.mem x lub then
    SetFd.refine d (Fcl_setDomain.interval glb (S.remove x lub));;

let smallest_cstr d x =
  let update _ =
    let glb, lub = SetFd.min_max d in
    if S.is_empty lub then
      Fcl_debug.fatal_error "Conjunto.smallest: empty set";
    if S.is_empty glb then
      refine_low_up x (max (Fd.min x) (S.min_elt lub)) (S.max_elt lub)
    else
      refine_low_up x (S.min_elt lub) (S.min_elt glb);


    let mi = Fd.min x in
    let lub' = S.remove_low mi lub in
    if lub <> lub' then
      SetFd.refine d (Fcl_setDomain.interval glb lub');

    not (Fd.is_var x)

  and delay ct = 
    SetFd.delay [SetFd.on_min; SetFd.on_max] d ct;
    Fd.delay [Fd.on_min] x ct

  in
  
  Fcl_cstr.create ~name:"Conjunto.smallest" update delay;;

let smallest d =
  let lub = SetFd.max d in
  let x = Fd.interval (S.min_elt lub) (S.max_elt lub) in
  Fcl_cstr.post (smallest_cstr d x);
  x;;

let disjoint d1 d2 = all_disjoint [|d1; d2|];;

let subset d1 d2 =
  let update _ =
    let glb1, lub1 = SetFd.min_max d1
    and glb2, lub2 = SetFd.min_max d2 in

     (* Rule T1: *)
    let lub1' = S.inter lub1 lub2 in
    safe_refine ~mess:"Conjunto.subset T1" d1 glb1 lub1'; 

     (* Rule T2 *)
    let glb2' = S.union glb2 glb1 in
    safe_refine ~mess:"Conjunto.subset T2" d2 glb2' lub2;

    not (SetFd.is_var d1) || not (SetFd.is_var d2)

  and delay ct =
    SetFd.delay [SetFd.on_max] d1 ct;
    SetFd.delay [SetFd.on_min] d2 ct in

  Fcl_cstr.create ~name:"Conjunto.subset" update delay;;

let mem_check x d () =
  let glb, lub = SetFd.min_max d in
  match Fd.value x with
    Val x ->
      if S.mem x glb then
 	true
      else if not (S.mem x lub) then
	false
      else
	raise Fcl_cstr.DontKnow
  | Unk ax ->
      let domx = Attr.dom ax in
      if S.subset domx glb then
	true
      else if S.is_empty (S.inter domx lub) then
	false
      else
      	raise Fcl_cstr.DontKnow

let rec mem x d =
  let update _ =
    match Fd.value x with
      Val x ->
	inside x d;
	true
    | Unk ax ->
	Fd.refine x (S.inter (SetFd.max d) (Attr.dom ax));
	not (Fd.is_var x) || not (SetFd.is_var d)

  and delay ct =
    SetFd.delay [SetFd.on_max] d ct;
    Fd.delay [Fd.on_refine] x ct

  and not () = not_mem x d in

  Fcl_cstr.create ~name:"Conjunto.mem" ~check:(mem_check x d) ~not:not update delay

and not_mem x d =
  let update _ =
    match Fd.value x with
      Val x ->
	outside x d;
	true
    | Unk ax ->
	Fd.refine x (Fcl_domain.diff (Attr.dom ax) (SetFd.min d));
	not (Fd.is_var x) || not (SetFd.is_var d)

  and delay ct =
    SetFd.delay [SetFd.on_min] d ct;
    Fd.delay [Fd.on_refine] x ct

  and check () = not (mem_check x d ())

  and not () = mem x d in

  Fcl_cstr.create ~name:"Conjunto.not_mem" ~check:check ~not:not update delay

(* Order based on the minimum element. The empty set is the smallest element. *)
let inf_min d1 d2 =
  
  let update _ =
    let glb1, lub1 = SetFd.min_max d1
    and glb2, lub2 = SetFd.min_max d2 in
    if S.is_empty lub1 then
      true
    else if S.is_empty lub2 && not (S.is_empty glb1) then
      Fcl_stak.fail "Conjunto.inf_min: d2 empty"
    else if not (S.is_empty glb1) && S.min_elt glb1 <= S.min_elt lub2 then
      true
    else if not (S.is_empty glb2) && S.min_elt lub1 > S.min_elt glb2 then
      Fcl_stak.fail "Conjunto.inf_min: d1 > d2"
    else if not (S.is_empty lub2) && S.min_elt lub2 < S.min_elt lub1 then begin
      let lub2' = S.remove_low (S.min_elt lub1) lub2 in
      SetFd.refine d2 (Fcl_setDomain.interval glb2 lub2');
      false end
    else false

  and delay ct =
    SetFd.delay [SetFd.on_min; SetFd.on_max] d1 ct;
    SetFd.delay [SetFd.on_min; SetFd.on_max] d2 ct in

  Fcl_cstr.create ~name:"Conjunto.inf_min" update delay;;

(* Order like Domain.compare *)
let order_with_card d1 card1 d2 card2 =
  Fcl_cstr.post (Fcl_arith.(<=~) (Fcl_arith.fd2e card1) (Fcl_arith.fd2e card2));

  let update _ =
    if Fd.max card1 < Fd.min card2 then true else
    
    match SetFd.value d1, SetFd.value d2 with
      Val v1, Val v2 -> begin
        (* equal cards :
	   if d1 and d2 are ground, their cards too (priority later)
	   and with the preceding test they should be equal (otherwise
	   true or failure) *)
	assert (not (Fd.is_var card1 || Fd.is_var card2)
		  && (Fd.elt_value card1 = Fd.elt_value card2));
	S.compare v1 v2 <= 0 || Fcl_stak.fail "Conjunto.order" end
    | _, _ -> false

  and delay ct =
    SetFd.delay [SetFd.on_subst] d1 ct;
    SetFd.delay [SetFd.on_subst] d2 ct in
	
  Fcl_cstr.create ~name:"Conjunto.order" ~priority:Fcl_cstr.later update delay;;

let order d1 d2 =
  let card1 = cardinal d1 and card2 = cardinal d2 in
  order_with_card d1 card1 d2 card2

(* Member *)

let unicise l =
  let sl = List.sort S.compare l in
  let rec loop = function
      [] -> []
    | [_] as le -> le
    | e1 :: ((e2 :: _) as e2r) ->
	if S.compare e1 e2 = 0 then loop e2r else e1 :: loop e2r in
  loop sl

let filter glb lub sets =
  List.filter (fun set -> S.subset glb set && S.subset set lub) sets

let member s sets =
  (* On pourrait faire l'intersection des sets pour rajouter des éléments
     dans glb et leur union pour en enlever dans lub, mais c'est coûteux - il
     faudrait la recalculer à chaque fois et on en a pas besoin pour la
     sectorisation *)
  let sets = unicise sets in
  let sets = Fcl_stak.ref sets in

  let update _ =
    match SetFd.value s with
      Val sv ->
	List.exists (fun set -> S.compare set sv = 0) (Fcl_stak.get sets) || Fcl_stak.fail "Conjunto.member"
    | Unk attr ->
	let glb = SetAttr.min attr and lub = SetAttr.max attr in
	let new_sets = filter glb lub (Fcl_stak.get sets) in
	match new_sets with
	  [] -> Fcl_stak.fail "Conjunto.member"
	| [set] -> SetFd.unify s set; true
	| _ -> Fcl_stak.set sets new_sets; false

  and delay ct =
    SetFd.delay [SetFd.on_min; SetFd.on_max] s ct in

  Fcl_cstr.create ~name:"Conjunto.member" update delay


(* sum_weight *)

(* Un seul binding par clé et toutes les données positives *)
let check_hash h =
  try
    Hashtbl.iter
      (fun k d ->
	match Hashtbl.find_all h k with
	  [_] -> if d < 0 then raise Exit
	| _ -> raise Exit)
      h;
    true
  with Exit -> false

let check_dom lub h =
  try
    S.iter (fun k -> ignore (Hashtbl.find h k)) lub;
    true
  with Not_found -> false

let sum_weight_cstr s weights_list sum =
  let weights = Hashtbl.create (List.length weights_list) in
  List.iter (fun (k, d) -> Hashtbl.add weights k d) weights_list;

  (* s ne doit pas adresser d'éléments en dehors de weights et tous
     les poids doivent être positifs. De plus, les bindings doivent être
     unique dans weights. *)
  assert
    (let lub = SetFd.max s in check_dom lub weights && check_hash weights);

  let f d = 
    (* S.fold pas encore implanté *)
    (* S.fold (fun i r -> Hashtbl.find weights i + r) d 0 in *)
    let fd = ref 0 in
    S.iter (fun i -> fd := !fd + Hashtbl.find weights i) d;
    !fd in

  let update _ =
    let m, n = Fd.min_max sum
    and a, b = SetFd.min_max s in
    let fa = f a and fb = f b in
    (* Rule I3 *)
    let m' = max m fa and n' = min n fb in
    if n' < m' then Fcl_stak.fail "Conjunto.sum_weight";
    if m' <> m || n' <> n then refine_low_up sum m' n';
    (* Rule I4 *)
    if n' = fa then begin SetFd.unify s a; Fd.unify sum fa; true end
    (* Rule I5 *)
    else if m' = fb then begin SetFd.unify s b; Fd.unify sum fb; true end
    else false

  and delay ct =
    SetFd.delay [SetFd.on_min; SetFd.on_max] s ct;
    Fd.delay [Fd.on_min; Fd.on_max] sum ct in

  Fcl_cstr.create ~name:"Conjunto.sum_weight" update delay

let sum_weight s weights =
  let sum = Fd.create (Fcl_domain.int) in
  Fcl_cstr.post (sum_weight_cstr s weights sum);
  sum


(* [sets] must have cardinality [c] must intersect pairwise in atmost one
 element *)
let atmost1 sets c =
  let n = Array.length sets in
  let values =
    Array.fold_right
      (fun x r -> S.union (SetFd.max x) r) sets S.empty in
  let lubs = Array.map Fcl_invariant.SetFd.max sets
  and glbs = Array.map Fcl_invariant.SetFd.min sets in


  S.iter
    (fun a ->
      let cardS_a =
    	Fcl_invariant.sum (Array.map (Fcl_invariant.unary (fun glbsi -> if S.mem a glbsi then 1 else 0)) glbs)

      and bigTa =
	Array.fold_right
	  (Fcl_invariant.binary
	      (fun lubsi r ->
	    	if S.mem a lubsi
	    	then S.union lubsi r
	    	else r))
	  lubs
	  (Fcl_invariant.constant S.empty)
      in
      let maxa = Fcl_invariant.unary (fun x -> (S.cardinal x-1)/(c-1)) bigTa in
      ignore(Fcl_invariant.binary
	       (fun maxa cardS_a ->
		 Fcl_debug.call 's' (fun c -> Printf.fprintf c "atmost1 max%d=%d cardS_%d=%d\n" a maxa a cardS_a);
		 if maxa < cardS_a then Fcl_stak.fail "atmost1";
		 if maxa = cardS_a then begin
		   for i = 0 to n - 1 do
		     let glbi = Fcl_invariant.get glbs.(i) in
		     if not (S.mem a glbi) then begin
		       Fcl_debug.call 's' (fun c -> Printf.fprintf c "atmost1 remove %d from %a\n" a SetFd.fprint sets.(i));
		       outside a sets.(i)
		     end
		   done
		 end)
	       maxa cardS_a))
    values;;