Source file dualize.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
module type S = sig
module Scalar : Scalar.S
type vector = (int * Scalar.t) list
type 'a obj_ext = vector * 'a Sdp.block_diag
type 'a constr_ext = vector * 'a Sdp.block_diag * Scalar.t * Scalar.t
val solve_ext_sparse : ?options:Sdp.options -> ?solver:Sdp.solver ->
Sdp.sparse_matrix obj_ext ->
Sdp.sparse_matrix constr_ext list -> Sdp.bounds ->
SdpRet.t * (float * float)
* (vector * Sdp.matrix Sdp.block_diag)
module ScalarLinExpr : LinExpr.S with module Coeff = Scalar
type 'a details_val = DV of 'a | DVexpr of ScalarLinExpr.t
type details =
(int * Ident.t) list * Ident.t array array Sdp.block_diag
* float details_val Ident.Map.t
val solve_ext_sparse_details :
?options:Sdp.options -> ?solver:Sdp.solver ->
Sdp.sparse_matrix obj_ext ->
Sdp.sparse_matrix constr_ext list -> Sdp.bounds ->
SdpRet.t * (float * float)
* (vector * Sdp.matrix Sdp.block_diag)
* details
val pp_obj_ext : (Format.formatter -> 'a -> unit) ->
Format.formatter -> 'a obj_ext -> unit
val pp_constr_ext : (Format.formatter -> 'a -> unit) ->
Format.formatter -> 'a constr_ext -> unit
val pp_ext_sparse : Format.formatter ->
(Sdp.sparse_matrix obj_ext *
Sdp.sparse_matrix constr_ext list * Sdp.bounds) -> unit
end
module Make (S : Scalar.S) : S with module Scalar = S = struct
module Scalar = S
type vector = (int * Scalar.t) list
type 'a obj_ext = vector * 'a Sdp.block_diag
type 'a constr_ext = vector * 'a Sdp.block_diag * Scalar.t * Scalar.t
module ScalarLinExpr = LinExpr.Make (Scalar)
type 'a details_val = DV of 'a | DVexpr of ScalarLinExpr.t
type details =
(int * Ident.t) list * Ident.t array array Sdp.block_diag
* float details_val Ident.Map.t
module IMap = Map.Make (struct type t = int let compare = compare end)
module IIIMap =
Map.Make (struct type t = int * int * int let compare = compare end)
let solve_ext_sparse_details ?options ?solver obj constraints bounds =
if bounds <> [] then assert false;
let constraints =
List.map
(fun (v, m, lb, ub) ->
if not (Scalar.equal lb ub) then assert false;
v, m, lb)
constraints in
let vids =
List.fold_left
(fun m (i, _) ->
if IMap.mem i m then m
else IMap.add i (Ident.create (Format.sprintf "_v%d_" i)) m)
IMap.empty
(List.flatten (fst obj :: List.map (fun (x, _, _) -> x) constraints)) in
let mids =
List.fold_left
(fun m (bi, b) ->
List.fold_left
(fun m (i, j, _) ->
if IIIMap.mem (bi, i, j) m then m else
let s = Format.sprintf "_m%d_%d_%d_" bi i j in
IIIMap.add (bi, i, j) (Ident.create s) m)
m b)
IIIMap.empty
(List.flatten (snd obj :: List.map (fun (_, x, _) -> x) constraints)) in
let module LE = ScalarLinExpr in
let l_of_vec v =
List.fold_left (fun l (i, c) -> (IMap.find i vids, c) :: l) [] v in
let l_of_mat m =
List.fold_left
(fun l (bi, b) ->
List.fold_left
(fun l (i, j, c) ->
let c = if i = j then S.of_float c else S.of_float (2. *. c) in
(IIIMap.find (bi, i, j) mids, c) :: l)
l b)
[] m in
let eqs =
List.map
(fun (v, m, b) -> LE.of_list (l_of_vec v @ l_of_mat m) S.(~- b))
constraints in
let assigs =
let rec eliminate assigs eqs =
match eqs with
| [] -> assigs
| eq :: eqs ->
match LE.choose eq with
| None ->
begin match LE.is_const eq with
| None -> assert false
| Some c ->
if S.sign c <> 0 then raise Exit else eliminate assigs eqs end
| Some (id, c) ->
let eq = LE.mult_scalar S.(neg (inv c)) (LE.remove eq id) in
let repl l = LE.replace l [id, eq] in
let assigs = Ident.Map.map repl assigs in
let eqs = List.map repl eqs in
eliminate (Ident.Map.add id eq assigs) eqs in
try Some (eliminate Ident.Map.empty eqs) with Exit -> None in
let assigs = match assigs with
| None -> None
| Some assigs ->
let check (_, i, j) id =
i <> j ||
try
let e = Ident.Map.find id assigs in
match LE.is_const e with None -> true | Some c -> S.sign c >= 0
with Not_found -> true in
if IIIMap.for_all check mids then Some assigs else None in
match assigs with
| None ->
SdpRet.PrimalInfeasible, (0., 0.), ([], []), ([], [], Ident.Map.empty)
| Some assigs ->
let mAi, mC =
IIIMap.fold
(fun bij id (mAi, mC) ->
let e = try Ident.Map.find id assigs with Not_found -> LE.var id in
let l, c = LE.to_list e in
let mAi =
List.fold_left
(fun mAi (i, c) ->
let mat =
try Ident.Map.find i mAi with Not_found -> IIIMap.empty in
Ident.Map.add i (IIIMap.add bij c mat) mAi)
mAi l in
let mC = if S.sign c = 0 then mC else IIIMap.add bij S.(~- c) mC in
mAi, mC)
mids (Ident.Map.empty, IIIMap.empty) in
let obj, obj_c =
let le = LE.of_list (l_of_vec (fst obj) @ l_of_mat (snd obj)) S.zero in
let le = LE.mult_scalar S.(~- one) le in
let le = LE.replace le (Ident.Map.bindings assigs) in
let l, c = LE.to_list le in
List.fold_left
(fun m (id, c) -> Ident.Map.add id c m)
Ident.Map.empty l,
S.to_float c in
let dual_vars =
Ident.Set.empty |> Ident.Map.fold (fun id _ -> Ident.Set.add id) mAi
|> Ident.Map.fold (fun id _ -> Ident.Set.add id) obj in
let tr_mx m =
IIIMap.fold
(fun (bi, i, j) c m ->
let b = try IMap.find bi m with Not_found -> [] in
IMap.add bi ((i, j, S.to_float c) :: b) m)
m IMap.empty
|> IMap.bindings in
let dual_constraints =
Ident.Set.fold
(fun id l ->
let mA = try tr_mx (Ident.Map.find id mAi) with Not_found -> [] in
let cb = try Ident.Map.find id obj with Not_found -> S.zero in
Sdp.Eq (mA, S.to_float cb) :: l)
dual_vars [] in
let dual_obj = tr_mx mC in
let ret, (pobj, dobj), (_, y, _) =
Sdp.solve_sparse ?options ?solver dual_obj dual_constraints in
let ret = match ret with
| SdpRet.Success | SdpRet.PartialSuccess | SdpRet.Unknown -> ret
| SdpRet.PrimalInfeasible -> SdpRet.DualInfeasible
| SdpRet.DualInfeasible -> SdpRet.PrimalInfeasible
| SdpRet.NearPrimalInfeasible -> SdpRet.NearDualInfeasible
| SdpRet.NearDualInfeasible -> SdpRet.NearPrimalInfeasible in
let obj = -. (dobj +. obj_c), -. (pobj +. obj_c) in
if Array.length y <> Ident.Set.cardinal dual_vars then
ret, obj, ([], []), ([], [], Ident.Map.empty)
else
let dual_sol =
List.fold_left2
(fun m id f -> Ident.Map.add id f m)
Ident.Map.empty
(Ident.Set.elements dual_vars)
(List.rev (Array.to_list y)) in
let details_values =
dual_sol |> Ident.Map.map (fun f -> DV f)
|> Ident.Map.fold
(fun id le dv -> Ident.Map.add id (DVexpr le) dv)
assigs
|> IMap.fold
(fun _ id dv ->
if Ident.Map.mem id dv then dv else Ident.Map.add id (DV 0.) dv)
vids in
let blocksizes =
IIIMap.fold
(fun (bi, i, j) _ blocksizes ->
let sz = try IMap.find bi blocksizes with Not_found -> 0 in
IMap.add bi (max sz (max i j + 1)) blocksizes)
mids IMap.empty in
let details =
let bids =
let dummy = Ident.create "_dummy_" in
IMap.map (fun sz -> Array.make_matrix sz sz dummy) blocksizes in
IIIMap.iter
(fun (bi, i, j) id -> let b = IMap.find bi bids in b.(i).(j) <- id)
mids;
IMap.bindings vids, IMap.bindings bids, details_values in
let getf id = match Ident.Map.find id details_values with
| DV f -> f | DVexpr _ -> raise Not_found in
let v =
let get id =
try
match Ident.Map.find id details_values with
| DV f -> S.of_float f
| DVexpr le ->
let l, c = LE.to_list le in
List.fold_left
(fun r (id, c) -> S.(r + c * of_float (getf id)))
c l
with Not_found -> S.zero in
List.map (fun (i, id) -> i, get id) ((fun (x, _, _) -> x) details) in
let m =
let get b =
let getc id =
try
match Ident.Map.find id details_values with
| DV f -> f
| DVexpr le ->
let l, c = LE.to_list le in
List.fold_left
(fun r (id, c) -> r +. S.to_float c *. getf id)
(S.to_float c) l
with Not_found -> 0. in
let sz = Array.length b in
let b' = Array.make_matrix sz sz 0. in
for i = 0 to sz - 1 do
for j = 0 to i do
let c = getc b.(i).(j) in
b'.(i).(j) <- c;
b'.(j).(i) <- c;
done
done;
b' in
List.map (fun (i, b) -> i, get b) ((fun (_, x, _) -> x) details) in
ret, obj, (v, m), details
let solve_ext_sparse ?options ?solver obj constraints bounds =
let ret, obj, primsol, _ =
solve_ext_sparse_details ?options ?solver obj constraints bounds in
ret, obj, primsol
let pp_obj_ext f fmt (v, m) =
let pp_e_v fmt (i, s) = Format.fprintf fmt "%a x_%d" Scalar.pp s i in
let pp_e_m fmt (i, m) = Format.fprintf fmt "tr(%a X_%d)" f m i in
match v, m with
| [], [] -> Format.printf "0"
| [], _ ->
Format.fprintf fmt "@[%a@]" (Utils.pp_list ~sep:"@ + " pp_e_m) m
| _, [] ->
Format.fprintf fmt "@[%a@]" (Utils.pp_list ~sep:"@ + " pp_e_v) v
| _ ->
Format.fprintf
fmt "@[%a@ + %a@]"
(Utils.pp_list ~sep:"@ + " pp_e_v) v
(Utils.pp_list ~sep:"@ + " pp_e_m) m
let pp_constr_ext f fmt (v, m, lb, ub) =
if Scalar.equal lb ub then
Format.fprintf fmt "%a = %a" (pp_obj_ext f) (v, m) Scalar.pp lb
else if Scalar.to_float lb = neg_infinity then
Format.fprintf fmt "%a <= %a" (pp_obj_ext f) (v, m) Scalar.pp ub
else if Scalar.to_float ub = infinity then
Format.fprintf fmt "%a >= %a" (pp_obj_ext f) (v, m) Scalar.pp lb
else
Format.fprintf
fmt "%a <= %a <= %a" Scalar.pp lb (pp_obj_ext f) (v, m) Scalar.pp ub
let pp_ext f fmt (obj, cstrs, bounds) =
match bounds with
| [] ->
Format.fprintf
fmt "@[<v>maximize %a@ subject to @[<v>%a@],@ X psd@]"
(pp_obj_ext f) obj
(Utils.pp_list ~sep:",@ " (pp_constr_ext f)) cstrs
| _ ->
Format.fprintf
fmt "@[<v>maximize %a@ \
subject to @[<v>%a@],@ %a,@ X psd@]"
(pp_obj_ext f) obj
(Utils.pp_list ~sep:",@ " (pp_constr_ext f)) cstrs
Sdp.pp_bounds bounds
let pp_ext_sparse = pp_ext Sdp.pp_sparse_matrix
end
module Q = Make (Scalar.Q)
module Float = Make (Scalar.Float)