-
Notifications
You must be signed in to change notification settings - Fork 9
/
opt_z3.ml
161 lines (142 loc) · 5.91 KB
/
opt_z3.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
(* ========================================================================== *)
(* FPTaylor: A Tool for Rigorous Estimation of Round-off Errors *)
(* *)
(* Author: Alexey Solovyev, University of Utah *)
(* *)
(* This file is distributed under the terms of the MIT license *)
(* ========================================================================== *)
(* -------------------------------------------------------------------------- *)
(* Optimization with the Z3 SMT solver (see z3opt.py) *)
(* -------------------------------------------------------------------------- *)
open Num
open Expr
open Interval
open Opt_common
module Out = ExprOut.Make(ExprOut.Z3PythonPrinter)
let gen_z3py_opt_code (pars : Opt_common.opt_pars) max_only bounds fmt =
let z3_seed = Config.get_int_option "z3-seed" in
let nl = Format.pp_print_newline fmt in
let p str = Format.pp_print_string fmt str; nl() in
let p' str =
Format.pp_print_string fmt str;
Format.pp_print_flush fmt () in
let head () =
p "from z3opt import *";
p "from z3 import *";
p "" in
let tail () =
let max_str = if max_only then "True" else "False" in
let bounds_str =
if Config.get_bool_option "z3-interval-bounds" && More_num.check_interval bounds = "" then
Format.sprintf "(%.20e, %.20e)" bounds.low bounds.high
else
"None" in
p "";
p (Format.sprintf
"l, u = find_bounds(f, var_constraints + constraints, \
f_abs_tol=%e, f_rel_tol=%e, \
timeout=%d, seed=%d, max_only=%s, bounds=%s)"
pars.f_abs_tol pars.f_rel_tol
pars.timeout z3_seed max_str bounds_str);
p "print('min = {0:.20e}'.format(l))";
p "print('max = {0:.20e}'.format(u))" in
let num_to_z3 n =
let s = Big_int.string_of_big_int in
let ns = s (More_num.numerator n) and
ds = s (More_num.denominator n) in
(* "\"" ^ ns ^ "/" ^ ds ^ "\"" in*)
"Q(" ^ ns ^ "," ^ ds ^ ")" in
let const_to_z3 c =
if Const.is_rat c then num_to_z3 (Const.to_num c)
else failwith "gen_z3py_opt_code: interval constant" in
let print_constraint c =
match c with
| Le (e, Const c) ->
Out.print_fmt fmt e;
p' " < ";
p' (const_to_z3 c)
| _ -> failwith "z3opt: print_constraint(): unsupported constraint" in
let constraint_vars c =
match c with
| Le (e, Const _) -> vars_in_expr e
| _ -> failwith "z3opt: constraint_vars(): unsupported constraint" in
let constraints cs =
if cs = [] then
p "constraints = []"
else
let _ = p' "constraints = [" in
let _ = List.map (fun c -> print_constraint c; p' ", ") cs in
p "]" in
let vars var_names var_bounds =
if var_names = [] then
p "var_constraints = []"
else
let eq_vars, neq_vars = List.partition
(fun (name, (low, high)) -> low =/ high)
(List.combine var_names var_bounds) in
let eqs = List.map
(fun (name, (low, high)) ->
Format.sprintf "%s = %s" name (num_to_z3 low))
eq_vars in
let constraints = List.map
(fun (name, (low, high)) ->
if low =/ high then
Format.sprintf "%s >= %s, %s <= %s" name (num_to_z3 low) name (num_to_z3 high)
else
Format.sprintf "%s >= %s, %s <= %s" name (num_to_z3 low) name (num_to_z3 high))
neq_vars in
let names = String.concat ", " var_names in
p (Format.sprintf "[%s] = Reals('%s')" names names);
p (String.concat "\n" eqs);
p (Format.sprintf "var_constraints = [%s]" (String.concat ", " constraints)) in
(*
let low, high = unzip var_bounds in
let low_str = String.concat ", " (map2 (Format.sprintf "%s > %s")
var_names (map num_to_z3 low)) in
let high_str = String.concat ", " (map2 (Format.sprintf "%s < %s")
var_names (map num_to_z3 high)) in
let names = String.concat ", " var_names in
p (Format.sprintf "[%s] = Reals('%s')" names names);
p (Format.sprintf "var_constraints = [%s, %s]" low_str high_str) in
*)
let expr e =
p' "f = ";
Out.print_fmt fmt e in
fun (cs, e) ->
let vars_cs = List.map constraint_vars cs.constraints in
let var_names = Lib.unions (vars_in_expr e :: vars_cs) in
let var_bounds = List.map cs.var_rat_bounds var_names in
head ();
vars (List.map (fun name -> "var_" ^ (ExprOut.fix_name name)) var_names) var_bounds;
constraints cs.constraints;
expr e;
tail ()
let name_counter = ref 0;;
let min_max_expr (pars : Opt_common.opt_pars) max_only (cs : constraints) bounds e =
if vars_in_expr e = [] then
let n = Eval.eval_num_const_expr e in
let t = More_num.interval_of_num n in
(t.low, t.high)
else
let tmp = Lib.get_tmp_dir () in
incr name_counter;
let py_name = Filename.concat tmp
(Format.sprintf "min_max_%d.py" !name_counter) in
let gen = gen_z3py_opt_code pars max_only bounds in
let _ = Lib.write_to_file py_name gen (cs, e) in
let python_path =
let path = try Unix.getenv "PYTHONPATH" with Not_found -> "" in
let base = Filename.concat (Config.base_dir ()) "z3opt" in
let z3path = Config.get_string_option "z3-python-lib" in
Lib.concat_env_paths [path; base; z3path] in
let lib_path =
let path = try Unix.getenv "LD_LIBRARY_PATH" with Not_found -> "" in
let z3path = Config.get_string_option "z3-bin" in
Lib.concat_env_paths [path; z3path] in
let z3python = Config.get_string_option "z3-python-cmd" in
let cmd = Format.sprintf "LD_LIBRARY_PATH=\"%s\" PYTHONPATH=\"%s\" %s \"%s\""
lib_path python_path z3python py_name in
let out = Lib.run_cmd cmd in
let fmin = Opt_common.get_float out "min = " and
fmax = Opt_common.get_float out "max = " in
(fmin, fmax)