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
type t = Term.t list
type classified = {const: t; linear: t; quad: t}
type decomposed =
{ const: float
; lcs: float list
; lvs: Var.t list
; qcs: float list
; qv0s: Var.t list
; qv1s: Var.t list }
let c x = [Term.c x]
let var ?(integer = false) ?(lb = Float.zero) ?(ub = Float.infinity) name =
[Term.var ~integer ~lb ~ub name]
let of_var v = [Term.of_var v]
let of_term t = [t]
let binary name = [Term.var ~integer:true ~lb:Float.zero ~ub:Float.one name]
let range ?(integer = false) ?(lb = Float.zero) ?(ub = Float.infinity)
?(start = 0) stop name =
Array.init (stop - start) (fun i ->
var ~integer ~lb ~ub (String.concat "_" [name; string_of_int (start + i)]))
let range2 ?(integer = false) ?(lb = Float.zero) ?(ub = Float.infinity)
?(start0 = 0) ?(start1 = 0) stop0 stop1 name =
Array.init (stop0 - start0) (fun i ->
range ~integer ~lb ~ub ~start:start1 stop1
(String.concat "_" [name; string_of_int (start0 + i)]))
let range3 ?(integer = false) ?(lb = Float.zero) ?(ub = Float.infinity)
?(start0 = 0) ?(start1 = 0) ?(start2 = 0) stop0 stop1 stop2 name =
Array.init (stop0 - start0) (fun i ->
range2 ~integer ~lb ~ub ~start0:start1 ~start1:start2 stop1 stop2
(String.concat "_" [name; string_of_int (start0 + i)]))
let rangeb = range ~integer:true ~lb:Float.zero ~ub:Float.one
let range2b = range2 ~integer:true ~lb:Float.zero ~ub:Float.one
let range3b = range3 ~integer:true ~lb:Float.zero ~ub:Float.one
let rangev ?(integer = false) ?(lb = [||]) ?(ub = [||]) ?(start = 0) stop name =
Array.init (stop - start) (fun i ->
let l = if Array.length lb > 0 then lb.(i) else Float.zero in
let u = if Array.length ub > 0 then ub.(i) else Float.infinity in
var ~integer ~lb:l ~ub:u
(String.concat "_" [name; string_of_int (start + i)]))
let range2v ?(integer = false) ?(lb = [||]) ?(ub = [||]) ?(start0 = 0)
?(start1 = 0) stop0 stop1 name =
Array.init (stop0 - start0) (fun i ->
let l = if Array.length lb > 0 then lb.(i) else [||] in
let u = if Array.length ub > 0 then ub.(i) else [||] in
rangev ~integer ~lb:l ~ub:u ~start:start1 stop1
(String.concat "_" [name; string_of_int (start0 + i)]))
let range3v ?(integer = false) ?(lb = [||]) ?(ub = [||]) ?(start0 = 0)
?(start1 = 0) ?(start2 = 0) stop0 stop1 stop2 name =
Array.init (stop0 - start0) (fun i ->
let l = if Array.length lb > 0 then lb.(i) else [||] in
let u = if Array.length ub > 0 then ub.(i) else [||] in
range2v ~integer ~lb:l ~ub:u ~start0:start1 ~start1:start2 stop1 stop2
(String.concat "_" [name; string_of_int (start0 + i)]))
let concat a = List.concat (Array.to_list a)
let concat_list = List.concat
let of_float_array fa = concat (Array.map c fa)
let of_term_list = Fun.id
let to_float = function
| [] ->
0.0
| [Term.Const c] ->
c
| _ ->
failwith "cannot convert to float as this is not const monomial"
let zero = []
let one = [Term.one]
let sort p = List.sort Term.compare (List.map Term.sort p)
let rec compare p0 p1 =
match (p0, p1) with
| [], [] ->
0
| _, [] ->
1
| [], _ ->
-1
| t0 :: rest0, t1 :: rest1 ->
let c = Term.compare t0 t1 in
if c <> 0 then c else compare rest0 rest1
let partition poly =
List.partition
(fun t -> match t with Term.Const _ -> false | _ -> true)
poly
let classify poly =
let rec classify_ cs ls qs = function
| [] ->
{const= List.rev cs; linear= List.rev ls; quad= List.rev qs}
| (Term.Const _ as c) :: rest ->
classify_ (c :: cs) ls qs rest
| (Term.Linear _ as l) :: rest ->
classify_ cs (l :: ls) qs rest
| (Term.Quad _ as q) :: rest ->
classify_ cs ls (q :: qs) rest
in
classify_ [] [] [] poly
let classify_by var poly =
let rec classify_ cs ls qs = function
| [] ->
{const= List.rev cs; linear= List.rev ls; quad= List.rev qs}
| (Term.Const _ as c) :: rest ->
classify_ (c :: cs) ls qs rest
| (Term.Linear (_, v) as l) :: rest when v = var ->
classify_ cs (l :: ls) qs rest
| (Term.Linear _ as c) :: rest ->
classify_ (c :: cs) ls qs rest
| (Term.Quad (_, v0, v1) as q) :: rest when v0 = var && v1 = var ->
classify_ cs ls (q :: qs) rest
| (Term.Quad (_, v0, v1) as l) :: rest when v0 = var || v1 = var ->
classify_ cs (l :: ls) qs rest
| (Term.Quad _ as c) :: rest ->
classify_ (c :: cs) ls qs rest
in
classify_ [] [] [] poly
let decompose poly =
let rec decompose_ const lcs lvs qcs qv0s qv1s = function
| [] ->
{const; lcs; lvs; qcs; qv0s; qv1s}
| Term.Const c :: rest ->
decompose_ (c +. const) lcs lvs qcs qv0s qv1s rest
| Term.Linear (c, v) :: rest ->
decompose_ const (c :: lcs) (v :: lvs) qcs qv0s qv1s rest
| Term.Quad (c, v0, v1) :: rest ->
decompose_ const lcs lvs (c :: qcs) (v0 :: qv0s) (v1 :: qv1s) rest
in
decompose_ Float.zero [] [] [] [] [] poly
let degree p = List.fold_left max 0 (List.map Term.degree p)
let take_vars poly =
let rec take vars = function
| [] ->
vars
| Term.Const _ :: rest ->
take vars rest
| Term.Linear (_, v) :: rest ->
take (v :: vars) rest
| Term.Quad (_, v0, v1) :: rest ->
take (v0 :: v1 :: vars) rest
in
take [] poly
let uniq_vars poly =
let vars = take_vars poly in
List.sort_uniq Var.compare_name vars
let linear_coeff poly var =
let accum coeff = function
| Term.Linear (c, v) when v = var ->
c +. coeff
| _ ->
coeff
in
List.fold_left accum Float.zero poly
let quad_coeff poly v0 v1 =
let accum coeff = function
| Term.Quad (c, vv0, vv1)
when (v0 = vv0 && v1 = vv1) || (v1 = vv0 && v0 = vv1) ->
c +. coeff
| _ ->
coeff
in
List.fold_left accum Float.zero poly
let simplify ?(eps = 10. *. epsilon_float) poly =
let rec simplify_ const lins quads = function
| [] ->
(Term.Const const :: List.rev lins) @ List.rev quads
| Term.Const c :: rest ->
simplify_ (c +. const) lins quads rest
| (Term.Linear (newc, _) as newl) :: rest ->
let simpl_l =
match lins with
| [] ->
[newl]
| (Term.Linear (c, v) as l) :: restr ->
if Term.common_var newl l then Term.Linear (newc +. c, v) :: restr
else newl :: l :: restr
| _ ->
failwith "simplify_: unexpected pattern"
in
simplify_ const simpl_l quads rest
| (Term.Quad (newc, _, _) as newq) :: rest ->
let simpl_q =
match quads with
| [] ->
[newq]
| (Term.Quad (c, v0, v1) as q) :: restr ->
if Term.common_var newq q then
Term.Quad (newc +. c, v0, v1) :: restr
else newq :: q :: restr
| _ ->
failwith "simplify_: unexpected pattern"
in
simplify_ const lins simpl_q rest
in
poly |> sort |> simplify_ Float.zero [] []
|> List.filter (fun t -> not (Term.near_zero ~eps t))
let collision p =
let sorted = sort p in
let res =
List.fold_left
(fun coll_term t ->
(fst coll_term || Term.collision (snd coll_term) t, t))
(false, Term.zero) sorted
in
fst res
let to_string ?(short = false) p =
let ts_string = List.map (Term.to_string ~short) in
String.concat " "
(let cp = classify p in
match cp with
| {const= []; linear= []; quad= []} ->
["0.0"]
| {const= _; linear= _; quad= []} ->
ts_string (cp.const @ cp.linear)
| {const= []; linear= []; quad= _} ->
["["] @ ts_string cp.quad @ ["]"]
| _ ->
ts_string (cp.const @ cp.linear)
@ ["+"; "["] @ ts_string cp.quad @ ["]"])
let neg p = List.map Term.neg p
let ( ~-- ) = neg
let ( ++ ) = List.append
let ( -- ) pl pr = pl @ neg pr
let expand pl pr =
List.concat (List.map (fun tl -> List.map (fun tr -> Term.mul tl tr) pr) pl)
let ( *~ ) = expand
let dot = List.map2 Term.mul
let ( *@ ) = dot
let equiv ?(eps = 10. *. epsilon_float) pl pr =
match simplify ~eps (pl -- pr) with [] -> true | _ -> false
let divt poly term = List.map (fun t -> Term.div t term) poly
let long_div var n d =
let deg p =
match classify_by var p with
| {quad= []; linear= []; const= _} ->
0
| {quad= []; linear= _; _} ->
1
| _ ->
2
in
let lead p =
match classify_by var p with
| {quad= []; linear= []; const= c} ->
c
| {quad= []; linear= l; _} ->
l
| {quad= q; _} ->
q
in
let leadt p =
match classify_by var p with
| {quad= []; linear= []; const= [c]} ->
c
| {quad= []; linear= [l]; _} ->
l
| {quad= [q]; _} ->
q
| _ ->
failwith "multi-variate polynomial is passed to leadt"
in
let rec loop q r =
if equiv r zero || deg r < deg d then (q, r)
else
let t = divt (lead r) (leadt d) in
loop (q ++ t) (simplify (r -- (t *~ d)))
in
loop zero n
let div n d =
match simplify d with
| [] ->
raise Division_by_zero
| [t] ->
divt n t
| sd -> (
match uniq_vars sd with
| [v] -> (
match long_div v (simplify n) sd with
| q, [] ->
q
| _ ->
failwith "Failed to long-divide" )
| _ ->
failwith "Cannot divide by multi-variate polynomial" )
let ( /~ ) = div
let with_bound name lb ub p = List.map (Term.with_bound name lb ub) p
let to_binary name p = List.map (Term.to_binary name) p
let to_integer name p = List.map (Term.to_integer name) p
let double_quad p = List.map Term.double_quad p
let half_quad p = List.map Term.half_quad p
let map = List.map
let map_linear f =
List.map (fun t ->
match t with
| Term.Linear (c, v) ->
f c v
| _ ->
failwith "non-linear term encountered")
let mapi = List.mapi
let iter = List.iter
let iter_linear f =
List.iter (fun t -> match t with Term.Linear (c, v) -> f c v | _ -> ())
let iter_linear_exn f =
List.iter (fun t ->
match t with
| Term.Linear (c, v) ->
f c v
| _ ->
failwith "non-linear term encountered")
let iteri = List.iteri
let length = List.length
let take_linear_coeffs = map_linear (fun c _ -> c)