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 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
|
(***********************************************************************)
(* *)
(* 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_nonlinear.ml,v 1.8 2004/08/12 15:22:07 barnier Exp $ *)
open Fcl_var
open Fcl_misc.Operators
open Printf
module C = Fcl_cstr
module Linear = Fcl_linear
(*** Bounds evaluation ***)
(* signs differ with x <= y *)
let diffsign x y = assert (x <= y); x < 0 && y > 0
let diffeqsign x y = assert (x <= y); x <= 0 && y >= 0
(* unsorted diffsign *)
let udiffsign x y = if x <= y then diffsign x y else diffsign y x
(* min_of_absmod for min_max_of_expr when bounds are already known
AND positive *)
let min_of_absmod_inter a b c d =
assert (a >= 0 && c >= 0);
if b < c then a
else (* 0 *)
(* on peut faire mieux quand c=d (i.e. x2 connu) *)
if c = d then
if c = 0 then
Fcl_debug.fatal_error "Arith.min_of_absmod_inter: division_by_zero"
else
let amodc = a mod c in
if b - a + amodc < c then amodc else 0
else 0
(* max_of_absmod for min_max_of_expr when bounds are already known
AND positive *)
let max_of_absmod_inter a b c d =
assert (a >= 0 && c >= 0);
if b < d then b
else (* d - 1 *)
(* we could do more when c=d (i.e. x2 known) *)
if c = d then
if c = 0 then
Fcl_debug.fatal_error "Arith.max_of_absmod_inter: division_by_zero"
else
let bmodc = b mod c in
if bmodc - (b - a) > 0 then bmodc else d - 1
else d - 1
(* Implementation of non-linear constraints (z = x*y, x^n, x/y, |x|, x%y) *)
(*** monome constraint: z = x * y ***)
let min_max_mult_inter a b c d =
if diffeqsign a b || diffeqsign c d then
(* one of the two domains contains 0 *)
(min (a*d) (b*c), max (a*c) (b*d))
else if udiffsign a c then (* domains not on the same side of 0 *)
if a > 0 then (b*c, a*d) else (a*d, b*c)
else (* same side *)
if a > 0 then (a*c, b*d) else (b*d, a*c)
(* y = z[a,b] / x[c,d] *)
(* compute bounds to refine y *)
let min_max_of_div_for_mult z x =
(* if 0 belongs to dom(x), no conclusion on y *)
if Fd.member x 0 then (min_int, max_int) else
let (a, b) = Fd.min_max z and (c, d) = Fd.min_max x in
(* is this useful? only waken on min or max modif... *)
if diffsign c d then (* c < 0 < d*)
match Fd.value x with
Unk xa ->
let (c', d') = Fcl_domain.largest_hole_around (Attr.dom xa) 0 in
(min (a /+ d') (b /+ c'), max (a /- c') (b /- d'))
(* if x was ground, c*d >= 0 *)
| _ -> Fcl_debug.internal_error "min_max_of_div_for_mult: x ground"
(* 0 < c || d < 0 *)
else if diffsign a b then (* a < 0 < b *)
if c > 0 then (a /+ c, b /- c) else (b /+ d, a /- d)
(* 0 does not belong to dom(z) or dom(x) *)
else
if a >= 0 then (* z >= 0 *)
if c > 0 then (a /+ d, b /- c) else (b /+ d, a /- c) (* x pos or neg *)
else (* z <= 0 *)
if c < 0 then (b /+ c, a /- d) else (a /+ c, b /- d) (* x neg or pos *)
(* z = x*y *)
let monome z x y =
let name = "monome" in
let zero_removed = Fcl_stak.ref false in
let update_val_unk a y =
match Fd.value z with
Val c -> (* a <> 0 *)
if c mod a = 0 then (Fd.unify y (c / a); true)
else Fcl_stak.fail (name ^ ": Val a, Unk _, Val c")
| Unk _ ->
C.post (Linear.cstr [(1, z); (-a, y)] Linear.Equal 0); true in
let compute_bounds () = (Fd.min_max z, Fd.min_max x, Fd.min_max y) in
let delay c =
delay [Fd.on_min; Fd.on_max] z c;
delay [Fd.on_min; Fd.on_max] x c;
delay [Fd.on_min; Fd.on_max] y c
and fprint c =
Printf.fprintf c "%a = %a * %a" Fd.fprint z Fd.fprint x Fd.fprint y;
flush c
and update _ =
Fcl_debug.call 'a' (fun s -> fprintf s "%s - before update : %a = %a * %a\n" name Fd.fprint z Fd.fprint x Fd.fprint y);
let rec loop () =
match Fd.value x, Fd.value y with
Val a, Val b -> Fd.unify z (a * b); true
| (Val 0, _ | _, Val 0) -> Fd.unify z 0; true
| Val a, Unk _ -> update_val_unk a y
| Unk _, Val b -> update_val_unk b x
(* On pourrait aussi traiter le cas z = Val 1 (idem pour expn) *)
| Unk xa, Unk ya ->
match Fd.value z with
Val 0 ->
let xa_with_0 = Attr.member xa 0
and ya_with_0 = Attr.member ya 0 in
if xa_with_0 then begin
if not ya_with_0 then begin
Fd.unify x 0; true end
else false end
else if ya_with_0 then begin
Fd.subst y 0; true end
else Fcl_stak.fail (name ^ ": Unk xa, Unk ya, Val c")
| _z_val ->
(* On essaie d'enlever d'abord 0 dans x et y sinon pas de
propagation *)
if not (Fcl_stak.get zero_removed) && not (Fd.member z 0) then begin
Fcl_stak.set zero_removed true;
Fd.refine x (Fcl_domain.remove 0 (Attr.dom xa));
Fd.refine y (Fcl_domain.remove 0 (Attr.dom ya));
(* x et y peuvent etre instancies *)
loop () end
else begin
let bounds = compute_bounds () in
let (a, b) = Fd.min_max x and (c, d) = Fd.min_max y in
let (z_min, z_max) = min_max_mult_inter a b c d in
Fcl_debug.call 'a' (fun s -> fprintf s "%s - Unk xa, Unk ya, z : z_min=%d z_max=%d " name z_min z_max);
Fd.refine_low_up z z_min z_max;
let (y_min, y_max) = min_max_of_div_for_mult z x in
Fcl_debug.call 'a' (fun s -> fprintf s "y_min=%d y_max=%d " y_min y_max);
Fd.refine_low_up y y_min y_max;
let (x_min, x_max) = min_max_of_div_for_mult z y in
Fcl_debug.call 'a' (fun s -> fprintf s "x_min=%d x_max=%d\n" x_min x_max);
Fd.refine_low_up x x_min x_max;
(* On rappelle update pour atteindre le point fixe *)
if bounds <> compute_bounds () then loop () else false end in
let r = loop () in
Fcl_debug.call 'a' (fun s -> fprintf s "%s - after update : %a = %a * %a\n" name Fd.fprint z Fd.fprint x Fd.fprint y);
r in
C.create ~name ~fprint update delay
let min_max_abs_for_abs x =
match Fd.value x with
Val a -> let absa = abs a in (absa, absa)
| Unk xa ->
let a = Attr.min xa and b = Attr.max xa in
if a >= 0 then (a, b) else if b <= 0 then (-b, -a) else
(* x cheval sur 0 *)
let domx = Attr.dom xa in
let a' = Fcl_domain.greatest_leq domx 0
and b' = Fcl_domain.smallest_geq domx 0 in
(min (-a') b', max (-a) b)
let absolute z x =
let name = "absolute" in
let delay c = (* We could delay on_refine for x *)
delay [Fd.on_min; Fd.on_max] z c;
delay [Fd.on_min; Fd.on_max] x c
and fprint c =
Printf.fprintf c "%a = |%a|" Fd.fprint z Fd.fprint x; flush c
and update _ =
Fcl_debug.call 'a' (fun s -> fprintf s "%s - before update : %a=|%a|\n" name Fd.fprint z Fd.fprint x);
let r = match Fd.value x with
Val a -> Fd.unify z (abs a); true
| Unk xa ->
if Attr.min xa >= 0 then begin
C.post (Linear.cstr [(1, z); (-1, x)] Linear.Equal 0);
true end
else if Attr.max xa <= 0 then begin
C.post (Linear.cstr [(1, z); (1, x)] Linear.Equal 0);
true end
else
match Fd.value z with
Val c -> begin
Fd.refine x
(Fcl_domain.intersection
(Fcl_domain.create [-c; c]) (Attr.dom xa));
true end
| Unk _ -> begin
let (z_min, z_max) = min_max_abs_for_abs x in
Fd.refine_low_up z z_min z_max;
let z_min, z_max = Fd.min_max z in
assert (z_min >= 0);
let d =
Fcl_domain.remove_closed_inter (-z_min+1) (z_min-1)
(Fcl_domain.remove_low_up (-z_max) z_max (Attr.dom xa)) in
Fd.refine x d;
false end in
Fcl_debug.call 'a' (fun s -> fprintf s "%s - after update : %a=|%a|\n" name Fd.fprint z Fd.fprint x);
r in
C.create ~name ~fprint update delay
(* z = x1 / x2 *)
let min_max_div_inter a b c d =
if c = d && c = 0 then
Fcl_stak.fail "Arith.min_max_div_inter: division_by_zero"
else (* Otherwise, we suppose that x2 won't be instantiated to 0 *)
let c = if c = 0 then 1 else c and d = if d = 0 then -1 else d in
if diffsign c d then
(min a (0 - b), max (0 - a) b)
else if diffsign a b then
if c > 0 then (a/c, b/c) else (b/d, a/d)
else
if a >= 0 then (* x1 positive *)
if c > 0 then (a/d, b/c) else (b/d, a/c) (* x2 positive ou negative *)
else (* x1 negative *)
if c < 0 then (b/c, a/d) else (a/c, b/d) (* x2 negative ou positive *)
(* y = xr[a,b] / z[c,d] *)
(* a = min (x-r) , b = max (x-r) *)
let min_max_of_div_for_div a b z =
(* if 0 belongs to dom(x), no conclusion on y *)
if Fd.member z 0 then (min_int, max_int)
else
let (c, d) = Fd.min_max z in
(* is this useful? only waken on min or max modif... *)
if sign c * sign d < 0 then (* c < 0 < d *)
match Fd.value z with
Unk za ->
let domz = Attr.dom za in
let (c', d') = Fcl_domain.largest_hole_around domz 0 in
(min (a /+ d') (b /+ c'), max (a /- c') (b /- d'))
(* if z was ground, c*d >= 0 *)
| _ -> Fcl_debug.internal_error "min_max_of_div_for_mult : z ground"
(* 0 < c || d < 0 *)
else if sign a * sign b < 0 then (* a < 0 < b *)
if c > 0 then (a /+ c, b /- c) else (b /+ d, a /- d)
else
if a >= 0 then (* xr positive *)
if c > 0 then (a /+ d, b /- c) else (b /+ d, a /- c) (* z positive or negative *)
else (* xr negative *)
if c < 0 then (b /+ c, a /- d) else (a /+ c, b /- d) (* z negative or positive *)
(* z=x/y, x=y*z+r *)
(* if x >= 0 then r >= 0 else r <= 0 *)
let min_max_of_remainder x y =
let r_abs_max =
let min_y, max_y = Fd.min_max y in
max (Stdlib.abs min_y) (Stdlib.abs max_y) - 1 in
if Fd.min x >= 0 then (0, r_abs_max)
else if Fd.max x <= 0 then ((0 - r_abs_max), 0)
else ((0 - r_abs_max), r_abs_max)
(* z = x / y *)
let division z x y =
let zero_removed = Fcl_stak.ref false in
let min_max_r () = min_max_of_remainder x y in
let compute_bounds () =
(Fd.min_max z, Fd.min_max x, Fd.min_max y) in
let name = "division" in
let delay c =
delay [Fd.on_min; Fd.on_max] z c;
delay [Fd.on_min; Fd.on_max] x c;
delay [Fd.on_min; Fd.on_max] y c
and fprint c =
Printf.fprintf c "%a = %a / %a" Fd.fprint z Fd.fprint x Fd.fprint y;
flush c
and update _ =
Fcl_debug.call 'a' (fun s -> fprintf s "%s - before update : %a = %a / %a\n" name Fd.fprint z Fd.fprint x Fd.fprint y);
if not (Fcl_stak.get zero_removed) then begin
begin match Fd.value y with
Unk ya -> Fd.refine y (Fcl_domain.remove 0 (Attr.dom ya))
| Val 0 -> Fcl_stak.fail (name ^ ": division by zero")
| _ -> () end;
Fcl_stak.set zero_removed true end;
(* 0 does not belong to dom(y) *)
let rec loop bounds =
match Fd.value x, Fd.value y with
Val a, Val b -> Fd.unify z (a / b); true
| Val 0, _ -> Fd.unify z 0; true
| _x_val, _y_val ->
let (a, b) = Fd.min_max x and (c, d) = Fd.min_max y in
let (z_min, z_max) = min_max_div_inter a b c d in
Fcl_debug.call 'a' (fun s -> fprintf s "z_min=%d z_max=%d " z_min z_max);
Fd.refine_low_up z z_min z_max;
(* x = y*z + r *)
let (a, b) = Fd.min_max y and (c, d) = Fd.min_max z in
let (yz_min, yz_max) = min_max_mult_inter a b c d
and (r_min, r_max) = min_max_r () in
let (x_min, x_max) =
Linear.min_max_plus_inter yz_min yz_max r_min r_max in
Fcl_debug.call 'a' (fun s -> fprintf s "x_min=%d x_max=%d " x_min x_max);
Fd.refine_low_up x x_min x_max;
(* y = (x-r) / z *)
let (r_min, r_max) = min_max_r () in
let xr_min = Fd.min x - r_max and xr_max = Fd.max x - r_min in
let (y_min, y_max) = min_max_of_div_for_div xr_min xr_max z in
Fcl_debug.call 'a' (fun s -> fprintf s "y_min=%d y_max=%d\n" y_min y_max);
Fd.refine_low_up y y_min y_max;
let new_bounds = compute_bounds () in
if bounds <> new_bounds then loop new_bounds else false in
let r = loop (compute_bounds ()) in
Fcl_debug.call 'a' (fun s -> fprintf s "%s - after update : %a = %a / %a\n" name Fd.fprint z Fd.fprint x Fd.fprint y);
r in
C.create ~name ~fprint update delay
let min_max_abs_inter a b =
if a >= 0 then (a, b)
else if b <= 0 then (0 - b, 0 - a)
else (0, max (0 - a) b)
let min_max_mod_inter a b c d =
let (c, d) = min_max_abs_inter c d in
if a >= 0 then (* x1 >= 0 *)
(min_of_absmod_inter a b c d, max_of_absmod_inter a b c d)
else if b <= 0 then (* x1 <= 0 *)
let (a, b) = min_max_abs_inter a b in
(0 - max_of_absmod_inter a b c d, 0 - min_of_absmod_inter a b c d)
else (0 - max_of_absmod_inter 0 (-a) c d, max_of_absmod_inter 0 b c d)
(* y = (x-z) / (x/y) *)
let min_max_of_div_for_mod a b c d =
(* if 0 belongs to domain of x/y, no conclusion on y *)
if c <= 0 && d >= 0 then
(min_int, max_int)
else (* x/y positive ou negative *)
if sign a * sign b < 0 then (* xz cheval sur 0 *)
if c > 0 then (a /+ c, b /- c) else (b /+ d, a /- d)
(* ni xz ni x/y cheval sur 0 *)
else if a >= 0 then (* xz positive *)
if c > 0 then (a /+ d, b /- c) else (b /+ d, a /- c) (* x/y positive ou negative *)
else (* xz negative *)
if c < 0 then (b /+ c, a /- d) else (a /+ c, b /- d) (* x/y negative ou positive *)
(* z = x % y *)
let modulo z x y =
let zero_removed = Fcl_stak.ref false in
let min_max_of_xexp () =
let (xa, xb) = Fd.min_max x and (yc, yd) = Fd.min_max y in
let (xyc, xyd) = min_max_div_inter xa xb yc yd in
let (yxya, yxyb) = min_max_mult_inter yc yd xyc xyd in
let (zc, zd) = Fd.min_max z in
let xmin, xmax = Linear.min_max_plus_inter yxya yxyb zc zd in
let xmin = if zc >= 0 then max 0 xmin else xmin
and xmax = if zd <= 0 then min 0 xmax else xmax in
(xmin, xmax) in
let min_max_of_yexp () =
let (xa, xb) = Fd.min_max x and (zc, zd) = Fd.min_max z in
let (xza, xzb) = Linear.min_max_minus_inter xa xb zc zd in
let (yc, yd) = Fd.min_max y in
let (xyc, xyd) = min_max_div_inter xa xb yc yd in
min_max_of_div_for_mod xza xzb xyc xyd in
(* when y is known and b-a < y, c actually is |c| *)
let hole a b c =
assert (c >= 0);
if a >= 0 then (* x >= 0 *)
let amodc = a mod c and bmodc = b mod c in
if amodc <= bmodc then
match Fd.value z with
Val v -> (* a/c = b/c *)
let xv = c * (a / c) + v in
Fd.subst x xv; raise Exit
| _ -> Fd.refine_low_up z amodc bmodc
else
match Fd.value z with
Unk attrz ->
let newdom = Fcl_domain.union
(Fcl_domain.interval 0 bmodc) (Fcl_domain.interval amodc (c-1)) in
Fd.refine z (Fcl_domain.intersection newdom (Attr.dom attrz))
| Val v ->
let ab = (* a/c = b/c - 1 *)
if v >= amodc && v < c then a
else if v <= bmodc && v >= 0 then b
else Fcl_stak.fail "Nonlinear.modulo" in
let xv = c * (ab / c) + v in
Fd.subst x xv; raise Exit
else if b <= 0 then (* x <= 0 *)
let amodc = a mod c and bmodc = b mod c in
if amodc <= bmodc then
match Fd.value z with
Val v -> (* a/c = b/c *)
let xv = c * (a / c) + v in
Fd.subst x xv; raise Exit
| _ -> Fd.refine_low_up z amodc bmodc
else
match Fd.value z with
Unk attrz ->
let newdom = Fcl_domain.union (* c >= 0 *)
(Fcl_domain.interval amodc 0) (Fcl_domain.interval (1-c) bmodc) in
Fd.refine z (Fcl_domain.intersection newdom (Attr.dom attrz))
| Val v ->
let ab = (* b/c = a/c - 1 *)
if v <= bmodc && v > -c then b
else if v >= amodc && v <= 0 then a
else Fcl_stak.fail "Nonlinear.modulo" in
let xv = c * (ab / c) + v in
Fd.subst x xv; raise Exit
(* 0 strictly belongs to [a,b] so -|c| < a < 0 < b < |c| *)
else Fd.refine_low_up z a b in
let compute_bounds () = (Fd.min_max z, Fd.min_max x, Fd.min_max y) in
let name = "modulo" in
let delay c =
delay [Fd.on_min; Fd.on_max] z c;
delay [Fd.on_min; Fd.on_max] x c;
delay [Fd.on_min; Fd.on_max] y c
and fprint c =
Printf.fprintf c "%a = %a %% %a" Fd.fprint z Fd.fprint x Fd.fprint y;
flush c
and update _ =
Fcl_debug.call 'a' (fun s -> fprintf s "%s - before update : %a = %a %% %a\n" name Fd.fprint z Fd.fprint x Fd.fprint y);
if not (Fcl_stak.get zero_removed) then begin
begin match Fd.value y with
Unk ya -> Fd.refine y (Fcl_domain.remove 0 (Attr.dom ya))
| Val 0 -> Fcl_stak.fail (name ^ ": division by zero")
| _ -> () end;
Fcl_stak.set zero_removed true end;
(* 0 does not belong to dom(y) *)
let rec loop bounds =
match Fd.value x, Fd.value y with
Val a, Val b -> Fd.unify z (a mod b); true
| Val 0, _ -> Fd.unify z 0; true
| _x_val, _y_val ->
let (a, b) = Fd.min_max x and (c, d) = Fd.min_max y in
if c = d && b - a < abs c then (* y known *)
hole a b (abs c)
else begin
let (z_min, z_max) = min_max_mod_inter a b c d in
Fcl_debug.call 'a' (fun s -> fprintf s "z_min=%d z_max=%d " z_min z_max);
Fd.refine_low_up z z_min z_max end;
(* x = y*(x/y) + z *)
let (x_min, x_max) = min_max_of_xexp () in
Fcl_debug.call 'a' (fun s -> fprintf s "x_min=%d x_max=%d " x_min x_max);
Fd.refine_low_up x x_min x_max;
(* y = (x-z) / (x/y) *)
let (y_min, y_max) = min_max_of_yexp () in
Fcl_debug.call 'a' (fun s -> fprintf s "y_min=%d y_max=%d\n" y_min y_max);
Fd.refine_low_up y y_min y_max;
let new_bounds = compute_bounds () in
if bounds <> new_bounds then loop new_bounds else false in
(* function hole raises Exit when the constraint is satisfied *)
let r = try loop (compute_bounds ()) with Exit -> true in
Fcl_debug.call 'a' (fun s -> fprintf s "%s - after update : %a = %a %% %a\n" name Fd.fprint z Fd.fprint x Fd.fprint y);
r in
C.create ~name ~fprint update delay
let expn_int x n =
if n < 0 then Fcl_debug.fatal_error "Arith.expn_int: negative exponent" else
let rec loop = function
0 -> 1
| n ->
let n2 = n / 2 in
let xn2 = loop n2 in
let xn = xn2 * xn2 in
if n mod 2 = 0 then xn else xn * x in
loop n
let min_max_expn_inter a b n =
assert (a <= b);
if n < 0 then
Fcl_debug.fatal_error "Nonlinear.min_max_expn_inter: negative exponent"
else if n = 0 then (1, 1) else
let an = expn_int a n and bn = expn_int b n in
if n mod 2 = 0 then
if a >= 0 then (an, bn)
else if b <= 0 then (bn, an)
else if an <= bn then (0, bn) else (0, an)
else (an , bn)
let min_max_of_expn x n =
assert (n > 1);
match Fd.value x with
Unk xa ->
let min_xa = Attr.min xa and max_xa = Attr.max xa in
let min_xan = expn_int min_xa n and max_xan = expn_int max_xa n in
if n mod 2 = 0 then (* even exponent *)
if min_xa >= 0 then (min_xan, max_xan) (* positive domain *)
else if max_xa <= 0 then (max_xan, min_xan) (* negative domain *)
else
let domx = Attr.dom xa in
let min_neg = Fcl_domain.greatest_leq domx 0
and min_pos = Fcl_domain.smallest_geq domx 0 in
let minn =
if -min_neg < min_pos then expn_int min_neg n
else expn_int min_pos n
and maxn = max min_xan max_xan in
(minn, maxn)
(* L'exposant est impair *)
else (min_xan, max_xan)
| Val c -> let cn = expn_int c n in (cn, cn)
let nth_root upper z n =
(* Root of a negative number returns nan, so computation is done
with the absolute value *)
let az = abs z and sz = sign z in
let znth = truncate (float az ** (1. /. float n)) in
if expn_int znth n = az then sz * znth else
(* float computation may return z^(1/n)-e, rounded by truncate to the
preceding integer of the actual root *)
let znmore = znth + 1 in
if expn_int znmore n = az then sz * znmore else
if upper then
if z >= 0 then znmore else (0 - znth)
else
if z >= 0 then znth else (0 - znmore)
let ( **/+) = nth_root true
let ( **/-) = nth_root false
let min_max_of_nth_root z n =
(Fd.min z **/+ n, Fd.max z **/- n)
let int_root z n =
let az = abs z and sz = sign z in
let znth = truncate (float az ** (1. /. float n)) in
if expn_int znth n = az then sz * znth else
let znmore = znth + 1 in
if expn_int znmore n = az then sz * znmore else raise Not_found
(* z = x^n *)
let expn z x n =
let even = n mod 2 = 0
and compute_bounds () = (Fd.min_max z, Fd.min_max x) in
let name = "expn" in
let delay c =
delay [Fd.on_min; Fd.on_max] z c;
delay [Fd.on_min; Fd.on_max] x c
and fprint c =
Printf.fprintf c "%a = %a ^ %d" Fd.fprint z Fd.fprint x n;
flush c
and update _ =
Fcl_debug.call 'a' (fun s -> fprintf s "%s - before update : %a = %a ^ %d\n" name Fd.fprint z Fd.fprint x n);
let rec loop bounds =
match Fd.value x, Fd.value z with
Val a, _ -> Fd.unify z (expn_int a n); true
| Unk xa, Val c -> begin
try
let root = int_root c n in
if even then
Fd.refine x
(Fcl_domain.intersection
(Fcl_domain.create [-root; root]) (Attr.dom xa))
else Fd.subst x root;
true
with Not_found -> Fcl_stak.fail name end
| Unk xa, Unk za ->
let (z_min, z_max) = min_max_of_expn x n in
Fcl_debug.call 'a' (fun s -> fprintf s "%s - Unk xa, Unk za : z_min=%d z_max=%d " name z_min z_max);
Fd.refine z
(Fcl_domain.remove_low_up z_min z_max (Attr.dom za));
let (x_min, x_max) = min_max_of_nth_root z n in
Fcl_debug.call 'a' (fun s -> if even then fprintf s "%s (even) - Unk xa, za : x_neg=[%d,%d] x_pos=[%d,%d]\n" name (-x_max) (-x_min) x_min x_max else fprintf s "%s (odd) - Unk xa, Unk za : x_min=%d x_max=%d\n" name x_min x_max);
if even then
(* [-x_max_pos, -x_min_pos] U [x_min_pos, x_max_pos] *)
let d =
Fcl_domain.remove_closed_inter (-x_min+1) (x_min-1)
(Fcl_domain.remove_low_up (-x_max) x_max (Attr.dom xa)) in
Fd.refine x d
else
(* [x_min, x_max] *)
Fd.refine x
(Fcl_domain.remove_low_up x_min x_max (Attr.dom xa));
let new_bounds = compute_bounds () in
if bounds <> new_bounds then loop new_bounds else false in
let r = loop (compute_bounds ()) in
Fcl_debug.call 'a' (fun s -> fprintf s "expn - after update : %a = %a ^ %d\n" Fd.fprint z Fd.fprint x n);
r in
C.create ~name ~fprint update delay
let aux2 bounds x y =
let (a, b) = Fd.min_max x and (c, d) = Fd.min_max y in
let (z_min, z_max) = bounds a b c d in
Fd.interval z_min z_max
let monome_aux = aux2 min_max_mult_inter
let division_aux = aux2 min_max_div_inter
let modulo_aux = aux2 min_max_mod_inter
let absolute_aux x =
let (z_min, z_max) = min_max_abs_for_abs x in
Fd.interval z_min z_max
let expn_aux x n =
assert (n > 0);
if n = 1 then x else
let (mini, maxi) = min_max_of_expn x n in
Fd.interval mini maxi
|