| author | berghofe | 
| Tue, 24 Apr 2007 15:17:22 +0200 | |
| changeset 22781 | 18fbba942a80 | 
| parent 21858 | 05f57309170c | 
| child 22951 | dfafcd6223ad | 
| permissions | -rw-r--r-- | 
| 16784 | 1  | 
(* Title: HOL/Matrix/cplex/Cplex_tools.ML  | 
2  | 
ID: $Id$  | 
|
3  | 
Author: Steven Obua  | 
|
4  | 
*)  | 
|
5  | 
||
6  | 
signature CPLEX =  | 
|
7  | 
sig  | 
|
8  | 
||
9  | 
datatype cplexTerm = cplexVar of string | cplexNum of string | cplexInf  | 
|
10  | 
| cplexNeg of cplexTerm  | 
|
11  | 
| cplexProd of cplexTerm * cplexTerm  | 
|
12  | 
| cplexSum of (cplexTerm list)  | 
|
13  | 
||
14  | 
datatype cplexComp = cplexLe | cplexLeq | cplexEq | cplexGe | cplexGeq  | 
|
15  | 
||
16  | 
datatype cplexGoal = cplexMinimize of cplexTerm  | 
|
17  | 
| cplexMaximize of cplexTerm  | 
|
18  | 
||
19  | 
datatype cplexConstr = cplexConstr of cplexComp *  | 
|
20  | 
(cplexTerm * cplexTerm)  | 
|
21  | 
||
22  | 
datatype cplexBounds = cplexBounds of cplexTerm * cplexComp * cplexTerm  | 
|
23  | 
* cplexComp * cplexTerm  | 
|
24  | 
| cplexBound of cplexTerm * cplexComp * cplexTerm  | 
|
25  | 
||
26  | 
datatype cplexProg = cplexProg of string  | 
|
27  | 
* cplexGoal  | 
|
28  | 
* ((string option * cplexConstr)  | 
|
29  | 
list)  | 
|
30  | 
* cplexBounds list  | 
|
31  | 
||
32  | 
datatype cplexResult = Unbounded  | 
|
33  | 
| Infeasible  | 
|
34  | 
| Undefined  | 
|
35  | 
| Optimal of string *  | 
|
36  | 
(((* name *) string *  | 
|
37  | 
(* value *) string) list)  | 
|
| 
16966
 
37e34f315057
1. changed configuration variables for linear programming (Cplex_tools):
 
obua 
parents: 
16873 
diff
changeset
 | 
38  | 
|
| 
 
37e34f315057
1. changed configuration variables for linear programming (Cplex_tools):
 
obua 
parents: 
16873 
diff
changeset
 | 
39  | 
datatype cplexSolver = SOLVER_DEFAULT | SOLVER_CPLEX | SOLVER_GLPK  | 
| 16784 | 40  | 
|
41  | 
exception Load_cplexFile of string  | 
|
42  | 
exception Load_cplexResult of string  | 
|
43  | 
exception Save_cplexFile of string  | 
|
44  | 
exception Execute of string  | 
|
45  | 
||
46  | 
val load_cplexFile : string -> cplexProg  | 
|
47  | 
||
48  | 
val save_cplexFile : string -> cplexProg -> unit  | 
|
49  | 
||
50  | 
val elim_nonfree_bounds : cplexProg -> cplexProg  | 
|
51  | 
||
52  | 
val relax_strict_ineqs : cplexProg -> cplexProg  | 
|
53  | 
||
54  | 
val is_normed_cplexProg : cplexProg -> bool  | 
|
55  | 
||
| 
16966
 
37e34f315057
1. changed configuration variables for linear programming (Cplex_tools):
 
obua 
parents: 
16873 
diff
changeset
 | 
56  | 
val get_solver : unit -> cplexSolver  | 
| 
 
37e34f315057
1. changed configuration variables for linear programming (Cplex_tools):
 
obua 
parents: 
16873 
diff
changeset
 | 
57  | 
val set_solver : cplexSolver -> unit  | 
| 16784 | 58  | 
val solve : cplexProg -> cplexResult  | 
59  | 
end;  | 
|
60  | 
||
61  | 
structure Cplex : CPLEX =  | 
|
62  | 
struct  | 
|
63  | 
||
| 
16966
 
37e34f315057
1. changed configuration variables for linear programming (Cplex_tools):
 
obua 
parents: 
16873 
diff
changeset
 | 
64  | 
datatype cplexSolver = SOLVER_DEFAULT | SOLVER_CPLEX | SOLVER_GLPK  | 
| 
 
37e34f315057
1. changed configuration variables for linear programming (Cplex_tools):
 
obua 
parents: 
16873 
diff
changeset
 | 
65  | 
|
| 
 
37e34f315057
1. changed configuration variables for linear programming (Cplex_tools):
 
obua 
parents: 
16873 
diff
changeset
 | 
66  | 
val cplexsolver = ref SOLVER_DEFAULT;  | 
| 
 
37e34f315057
1. changed configuration variables for linear programming (Cplex_tools):
 
obua 
parents: 
16873 
diff
changeset
 | 
67  | 
fun get_solver () = !cplexsolver;  | 
| 
 
37e34f315057
1. changed configuration variables for linear programming (Cplex_tools):
 
obua 
parents: 
16873 
diff
changeset
 | 
68  | 
fun set_solver s = (cplexsolver := s);  | 
| 
 
37e34f315057
1. changed configuration variables for linear programming (Cplex_tools):
 
obua 
parents: 
16873 
diff
changeset
 | 
69  | 
|
| 16784 | 70  | 
exception Load_cplexFile of string;  | 
71  | 
exception Load_cplexResult of string;  | 
|
72  | 
exception Save_cplexFile of string;  | 
|
73  | 
||
74  | 
datatype cplexTerm = cplexVar of string  | 
|
75  | 
| cplexNum of string  | 
|
76  | 
| cplexInf  | 
|
77  | 
| cplexNeg of cplexTerm  | 
|
78  | 
| cplexProd of cplexTerm * cplexTerm  | 
|
79  | 
| cplexSum of (cplexTerm list)  | 
|
80  | 
datatype cplexComp = cplexLe | cplexLeq | cplexEq | cplexGe | cplexGeq  | 
|
81  | 
datatype cplexGoal = cplexMinimize of cplexTerm | cplexMaximize of cplexTerm  | 
|
82  | 
datatype cplexConstr = cplexConstr of cplexComp * (cplexTerm * cplexTerm)  | 
|
83  | 
datatype cplexBounds = cplexBounds of cplexTerm * cplexComp * cplexTerm  | 
|
84  | 
* cplexComp * cplexTerm  | 
|
85  | 
| cplexBound of cplexTerm * cplexComp * cplexTerm  | 
|
86  | 
datatype cplexProg = cplexProg of string  | 
|
87  | 
* cplexGoal  | 
|
88  | 
* ((string option * cplexConstr) list)  | 
|
89  | 
* cplexBounds list  | 
|
90  | 
||
91  | 
fun rev_cmp cplexLe = cplexGe  | 
|
92  | 
| rev_cmp cplexLeq = cplexGeq  | 
|
93  | 
| rev_cmp cplexGe = cplexLe  | 
|
94  | 
| rev_cmp cplexGeq = cplexLeq  | 
|
95  | 
| rev_cmp cplexEq = cplexEq  | 
|
96  | 
||
97  | 
fun the NONE = raise (Load_cplexFile "SOME expected")  | 
|
98  | 
| the (SOME x) = x;  | 
|
99  | 
||
100  | 
fun modulo_signed is_something (cplexNeg u) = is_something u  | 
|
101  | 
| modulo_signed is_something u = is_something u  | 
|
102  | 
||
103  | 
fun is_Num (cplexNum _) = true  | 
|
104  | 
| is_Num _ = false  | 
|
105  | 
||
106  | 
fun is_Inf cplexInf = true  | 
|
107  | 
| is_Inf _ = false  | 
|
108  | 
||
109  | 
fun is_Var (cplexVar _) = true  | 
|
110  | 
| is_Var _ = false  | 
|
111  | 
||
112  | 
fun is_Neg (cplexNeg x ) = true  | 
|
113  | 
| is_Neg _ = false  | 
|
114  | 
||
115  | 
fun is_normed_Prod (cplexProd (t1, t2)) =  | 
|
116  | 
(is_Num t1) andalso (is_Var t2)  | 
|
117  | 
| is_normed_Prod x = is_Var x  | 
|
118  | 
||
119  | 
fun is_normed_Sum (cplexSum ts) =  | 
|
120  | 
(ts <> []) andalso forall (modulo_signed is_normed_Prod) ts  | 
|
121  | 
| is_normed_Sum x = modulo_signed is_normed_Prod x  | 
|
122  | 
||
123  | 
fun is_normed_Constr (cplexConstr (c, (t1, t2))) =  | 
|
124  | 
(is_normed_Sum t1) andalso (modulo_signed is_Num t2)  | 
|
125  | 
||
126  | 
fun is_Num_or_Inf x = is_Inf x orelse is_Num x  | 
|
127  | 
||
128  | 
fun is_normed_Bounds (cplexBounds (t1, c1, t2, c2, t3)) =  | 
|
129  | 
(c1 = cplexLe orelse c1 = cplexLeq) andalso  | 
|
130  | 
(c2 = cplexLe orelse c2 = cplexLeq) andalso  | 
|
131  | 
is_Var t2 andalso  | 
|
132  | 
modulo_signed is_Num_or_Inf t1 andalso  | 
|
133  | 
modulo_signed is_Num_or_Inf t3  | 
|
134  | 
| is_normed_Bounds (cplexBound (t1, c, t2)) =  | 
|
135  | 
(is_Var t1 andalso (modulo_signed is_Num_or_Inf t2))  | 
|
136  | 
orelse  | 
|
137  | 
(c <> cplexEq andalso  | 
|
138  | 
is_Var t2 andalso (modulo_signed is_Num_or_Inf t1))  | 
|
139  | 
||
140  | 
fun term_of_goal (cplexMinimize x) = x  | 
|
141  | 
| term_of_goal (cplexMaximize x) = x  | 
|
142  | 
||
143  | 
fun is_normed_cplexProg (cplexProg (name, goal, constraints, bounds)) =  | 
|
144  | 
is_normed_Sum (term_of_goal goal) andalso  | 
|
145  | 
forall (fn (_,x) => is_normed_Constr x) constraints andalso  | 
|
146  | 
forall is_normed_Bounds bounds  | 
|
147  | 
||
148  | 
fun is_NL s = s = "\n"  | 
|
149  | 
||
150  | 
fun is_blank s = forall (fn c => c <> #"\n" andalso Char.isSpace c) (String.explode s)  | 
|
151  | 
||
152  | 
fun is_num a =  | 
|
153  | 
let  | 
|
154  | 
val b = String.explode a  | 
|
155  | 
fun num4 cs = forall Char.isDigit cs  | 
|
156  | 
fun num3 [] = true  | 
|
157  | 
| num3 (ds as (c::cs)) =  | 
|
158  | 
if c = #"+" orelse c = #"-" then  | 
|
159  | 
num4 cs  | 
|
160  | 
else  | 
|
161  | 
num4 ds  | 
|
162  | 
fun num2 [] = true  | 
|
163  | 
| num2 (c::cs) =  | 
|
164  | 
if c = #"e" orelse c = #"E" then num3 cs  | 
|
165  | 
else (Char.isDigit c) andalso num2 cs  | 
|
166  | 
fun num1 [] = true  | 
|
167  | 
| num1 (c::cs) =  | 
|
168  | 
if c = #"." then num2 cs  | 
|
169  | 
else if c = #"e" orelse c = #"E" then num3 cs  | 
|
170  | 
else (Char.isDigit c) andalso num1 cs  | 
|
171  | 
fun num [] = true  | 
|
172  | 
| num (c::cs) =  | 
|
173  | 
if c = #"." then num2 cs  | 
|
174  | 
else (Char.isDigit c) andalso num1 cs  | 
|
175  | 
in  | 
|
176  | 
num b  | 
|
177  | 
end  | 
|
178  | 
||
179  | 
fun is_delimiter s = s = "+" orelse s = "-" orelse s = ":"  | 
|
180  | 
||
181  | 
fun is_cmp s = s = "<" orelse s = ">" orelse s = "<="  | 
|
182  | 
orelse s = ">=" orelse s = "="  | 
|
183  | 
||
184  | 
fun is_symbol a =  | 
|
185  | 
let  | 
|
186  | 
	val symbol_char = String.explode "!\"#$%&()/,.;?@_`'{}|~"
 | 
|
187  | 
fun is_symbol_char c = Char.isAlphaNum c orelse  | 
|
188  | 
exists (fn d => d=c) symbol_char  | 
|
189  | 
fun is_symbol_start c = is_symbol_char c andalso  | 
|
190  | 
not (Char.isDigit c) andalso  | 
|
191  | 
not (c= #".")  | 
|
192  | 
val b = String.explode a  | 
|
193  | 
in  | 
|
194  | 
b <> [] andalso is_symbol_start (hd b) andalso  | 
|
195  | 
forall is_symbol_char b  | 
|
196  | 
end  | 
|
197  | 
||
198  | 
fun to_upper s = String.implode (map Char.toUpper (String.explode s))  | 
|
199  | 
||
200  | 
fun keyword x =  | 
|
201  | 
let  | 
|
202  | 
val a = to_upper x  | 
|
203  | 
in  | 
|
204  | 
if a = "BOUNDS" orelse a = "BOUND" then  | 
|
205  | 
SOME "BOUNDS"  | 
|
206  | 
else if a = "MINIMIZE" orelse a = "MINIMUM" orelse a = "MIN" then  | 
|
207  | 
SOME "MINIMIZE"  | 
|
208  | 
else if a = "MAXIMIZE" orelse a = "MAXIMUM" orelse a = "MAX" then  | 
|
209  | 
SOME "MAXIMIZE"  | 
|
210  | 
else if a = "ST" orelse a = "S.T." orelse a = "ST." then  | 
|
211  | 
SOME "ST"  | 
|
212  | 
else if a = "FREE" orelse a = "END" then  | 
|
213  | 
SOME a  | 
|
214  | 
else if a = "GENERAL" orelse a = "GENERALS" orelse a = "GEN" then  | 
|
215  | 
SOME "GENERAL"  | 
|
216  | 
else if a = "INTEGER" orelse a = "INTEGERS" orelse a = "INT" then  | 
|
217  | 
SOME "INTEGER"  | 
|
218  | 
else if a = "BINARY" orelse a = "BINARIES" orelse a = "BIN" then  | 
|
219  | 
SOME "BINARY"  | 
|
220  | 
else if a = "INF" orelse a = "INFINITY" then  | 
|
221  | 
SOME "INF"  | 
|
222  | 
else  | 
|
223  | 
NONE  | 
|
224  | 
end  | 
|
225  | 
||
226  | 
val TOKEN_ERROR = ~1  | 
|
227  | 
val TOKEN_BLANK = 0  | 
|
228  | 
val TOKEN_NUM = 1  | 
|
229  | 
val TOKEN_DELIMITER = 2  | 
|
230  | 
val TOKEN_SYMBOL = 3  | 
|
231  | 
val TOKEN_LABEL = 4  | 
|
232  | 
val TOKEN_CMP = 5  | 
|
233  | 
val TOKEN_KEYWORD = 6  | 
|
234  | 
val TOKEN_NL = 7  | 
|
235  | 
||
236  | 
(* tokenize takes a list of chars as argument and returns a list of  | 
|
237  | 
int * string pairs, each string representing a "cplex token",  | 
|
238  | 
and each int being one of TOKEN_NUM, TOKEN_DELIMITER, TOKEN_CMP  | 
|
239  | 
or TOKEN_SYMBOL *)  | 
|
240  | 
fun tokenize s =  | 
|
241  | 
let  | 
|
242  | 
val flist = [(is_NL, TOKEN_NL),  | 
|
243  | 
(is_blank, TOKEN_BLANK),  | 
|
244  | 
(is_num, TOKEN_NUM),  | 
|
245  | 
(is_delimiter, TOKEN_DELIMITER),  | 
|
246  | 
(is_cmp, TOKEN_CMP),  | 
|
247  | 
(is_symbol, TOKEN_SYMBOL)]  | 
|
248  | 
fun match_helper [] s = (fn x => false, TOKEN_ERROR)  | 
|
249  | 
| match_helper (f::fs) s =  | 
|
250  | 
if ((fst f) s) then f else match_helper fs s  | 
|
251  | 
fun match s = match_helper flist s  | 
|
252  | 
fun tok s =  | 
|
253  | 
if s = "" then [] else  | 
|
254  | 
let  | 
|
255  | 
val h = String.substring (s,0,1)  | 
|
256  | 
val (f, j) = match h  | 
|
257  | 
fun len i =  | 
|
258  | 
if size s = i then i  | 
|
259  | 
else if f (String.substring (s,0,i+1)) then  | 
|
260  | 
len (i+1)  | 
|
261  | 
else i  | 
|
262  | 
in  | 
|
263  | 
if j < 0 then  | 
|
264  | 
(if h = "\\" then []  | 
|
265  | 
		     else raise (Load_cplexFile ("token expected, found: "
 | 
|
266  | 
^s)))  | 
|
267  | 
else  | 
|
268  | 
let  | 
|
269  | 
val l = len 1  | 
|
270  | 
val u = String.substring (s,0,l)  | 
|
271  | 
val v = String.extract (s,l,NONE)  | 
|
272  | 
in  | 
|
273  | 
if j = 0 then tok v else (j, u) :: tok v  | 
|
274  | 
end  | 
|
275  | 
end  | 
|
276  | 
in  | 
|
277  | 
tok s  | 
|
278  | 
end  | 
|
279  | 
||
280  | 
exception Tokenize of string;  | 
|
281  | 
||
282  | 
fun tokenize_general flist s =  | 
|
283  | 
let  | 
|
284  | 
fun match_helper [] s = raise (Tokenize s)  | 
|
285  | 
| match_helper (f::fs) s =  | 
|
286  | 
if ((fst f) s) then f else match_helper fs s  | 
|
287  | 
fun match s = match_helper flist s  | 
|
288  | 
fun tok s =  | 
|
289  | 
if s = "" then [] else  | 
|
290  | 
let  | 
|
291  | 
val h = String.substring (s,0,1)  | 
|
292  | 
val (f, j) = match h  | 
|
293  | 
fun len i =  | 
|
294  | 
if size s = i then i  | 
|
295  | 
else if f (String.substring (s,0,i+1)) then  | 
|
296  | 
len (i+1)  | 
|
297  | 
else i  | 
|
298  | 
val l = len 1  | 
|
299  | 
in  | 
|
300  | 
(j, String.substring (s,0,l)) :: tok (String.extract (s,l,NONE))  | 
|
301  | 
end  | 
|
302  | 
in  | 
|
303  | 
tok s  | 
|
304  | 
end  | 
|
305  | 
||
306  | 
fun load_cplexFile name =  | 
|
307  | 
let  | 
|
308  | 
val f = TextIO.openIn name  | 
|
309  | 
val ignore_NL = ref true  | 
|
310  | 
val rest = ref []  | 
|
311  | 
||
312  | 
fun is_symbol s c = (fst c) = TOKEN_SYMBOL andalso (to_upper (snd c)) = s  | 
|
313  | 
||
314  | 
fun readToken_helper () =  | 
|
315  | 
if length (!rest) > 0 then  | 
|
316  | 
let val u = hd (!rest) in  | 
|
317  | 
(  | 
|
318  | 
rest := tl (!rest);  | 
|
319  | 
SOME u  | 
|
320  | 
)  | 
|
321  | 
end  | 
|
322  | 
else  | 
|
323  | 
let val s = TextIO.inputLine f in  | 
|
324  | 
if s = "" then NONE else  | 
|
325  | 
let val t = tokenize s in  | 
|
326  | 
if (length t >= 2 andalso  | 
|
327  | 
snd(hd (tl t)) = ":")  | 
|
328  | 
then  | 
|
329  | 
rest := (TOKEN_LABEL, snd (hd t)) :: (tl (tl t))  | 
|
330  | 
else if (length t >= 2) andalso is_symbol "SUBJECT" (hd (t))  | 
|
331  | 
andalso is_symbol "TO" (hd (tl t))  | 
|
332  | 
then  | 
|
333  | 
rest := (TOKEN_SYMBOL, "ST") :: (tl (tl t))  | 
|
334  | 
else  | 
|
335  | 
rest := t;  | 
|
336  | 
readToken_helper ()  | 
|
337  | 
end  | 
|
338  | 
end  | 
|
339  | 
||
340  | 
fun readToken_helper2 () =  | 
|
341  | 
let val c = readToken_helper () in  | 
|
342  | 
if c = NONE then NONE  | 
|
343  | 
else if !ignore_NL andalso fst (the c) = TOKEN_NL then  | 
|
344  | 
readToken_helper2 ()  | 
|
345  | 
else if fst (the c) = TOKEN_SYMBOL  | 
|
346  | 
andalso keyword (snd (the c)) <> NONE  | 
|
347  | 
then SOME (TOKEN_KEYWORD, the (keyword (snd (the c))))  | 
|
348  | 
else c  | 
|
349  | 
end  | 
|
350  | 
||
351  | 
fun readToken () = readToken_helper2 ()  | 
|
352  | 
||
353  | 
fun pushToken a = rest := (a::(!rest))  | 
|
354  | 
||
355  | 
fun is_value token =  | 
|
356  | 
fst token = TOKEN_NUM orelse (fst token = TOKEN_KEYWORD  | 
|
357  | 
andalso snd token = "INF")  | 
|
358  | 
||
359  | 
fun get_value token =  | 
|
360  | 
if fst token = TOKEN_NUM then  | 
|
361  | 
cplexNum (snd token)  | 
|
362  | 
else if fst token = TOKEN_KEYWORD andalso snd token = "INF"  | 
|
363  | 
then  | 
|
364  | 
cplexInf  | 
|
365  | 
else  | 
|
366  | 
raise (Load_cplexFile "num expected")  | 
|
367  | 
||
368  | 
fun readTerm_Product only_num =  | 
|
369  | 
let val c = readToken () in  | 
|
370  | 
if c = NONE then NONE  | 
|
371  | 
else if fst (the c) = TOKEN_SYMBOL  | 
|
372  | 
then (  | 
|
373  | 
if only_num then (pushToken (the c); NONE)  | 
|
374  | 
else SOME (cplexVar (snd (the c)))  | 
|
375  | 
)  | 
|
376  | 
else if only_num andalso is_value (the c) then  | 
|
377  | 
SOME (get_value (the c))  | 
|
378  | 
else if is_value (the c) then  | 
|
379  | 
let val t1 = get_value (the c)  | 
|
380  | 
val d = readToken ()  | 
|
381  | 
in  | 
|
382  | 
if d = NONE then SOME t1  | 
|
383  | 
else if fst (the d) = TOKEN_SYMBOL then  | 
|
384  | 
SOME (cplexProd (t1, cplexVar (snd (the d))))  | 
|
385  | 
else  | 
|
386  | 
(pushToken (the d); SOME t1)  | 
|
387  | 
end  | 
|
388  | 
else (pushToken (the c); NONE)  | 
|
389  | 
end  | 
|
390  | 
||
391  | 
fun readTerm_Signed only_signed only_num =  | 
|
392  | 
let  | 
|
393  | 
val c = readToken ()  | 
|
394  | 
in  | 
|
395  | 
if c = NONE then NONE  | 
|
396  | 
else  | 
|
397  | 
let val d = the c in  | 
|
398  | 
if d = (TOKEN_DELIMITER, "+") then  | 
|
399  | 
readTerm_Product only_num  | 
|
400  | 
else if d = (TOKEN_DELIMITER, "-") then  | 
|
401  | 
SOME (cplexNeg (the (readTerm_Product  | 
|
402  | 
only_num)))  | 
|
403  | 
else (pushToken d;  | 
|
404  | 
if only_signed then NONE  | 
|
405  | 
else readTerm_Product only_num)  | 
|
406  | 
end  | 
|
407  | 
end  | 
|
408  | 
||
409  | 
fun readTerm_Sum first_signed =  | 
|
410  | 
let val c = readTerm_Signed first_signed false in  | 
|
411  | 
if c = NONE then [] else (the c)::(readTerm_Sum true)  | 
|
412  | 
end  | 
|
413  | 
||
414  | 
fun readTerm () =  | 
|
415  | 
let val c = readTerm_Sum false in  | 
|
416  | 
if c = [] then NONE  | 
|
417  | 
else if tl c = [] then SOME (hd c)  | 
|
418  | 
else SOME (cplexSum c)  | 
|
419  | 
end  | 
|
420  | 
||
421  | 
fun readLabeledTerm () =  | 
|
422  | 
let val c = readToken () in  | 
|
423  | 
if c = NONE then (NONE, NONE)  | 
|
424  | 
else if fst (the c) = TOKEN_LABEL then  | 
|
425  | 
let val t = readTerm () in  | 
|
426  | 
if t = NONE then  | 
|
427  | 
			    raise (Load_cplexFile ("term after label "^
 | 
|
428  | 
(snd (the c))^  | 
|
429  | 
" expected"))  | 
|
430  | 
else (SOME (snd (the c)), t)  | 
|
431  | 
end  | 
|
432  | 
else (pushToken (the c); (NONE, readTerm ()))  | 
|
433  | 
end  | 
|
434  | 
||
435  | 
fun readGoal () =  | 
|
436  | 
let  | 
|
437  | 
val g = readToken ()  | 
|
438  | 
in  | 
|
439  | 
if g = SOME (TOKEN_KEYWORD, "MAXIMIZE") then  | 
|
440  | 
cplexMaximize (the (snd (readLabeledTerm ())))  | 
|
441  | 
else if g = SOME (TOKEN_KEYWORD, "MINIMIZE") then  | 
|
442  | 
cplexMinimize (the (snd (readLabeledTerm ())))  | 
|
443  | 
else raise (Load_cplexFile "MAXIMIZE or MINIMIZE expected")  | 
|
444  | 
end  | 
|
445  | 
||
446  | 
fun str2cmp b =  | 
|
447  | 
(case b of  | 
|
448  | 
"<" => cplexLe  | 
|
449  | 
| "<=" => cplexLeq  | 
|
450  | 
| ">" => cplexGe  | 
|
451  | 
| ">=" => cplexGeq  | 
|
452  | 
| "=" => cplexEq  | 
|
453  | 
| _ => raise (Load_cplexFile (b^" is no TOKEN_CMP")))  | 
|
454  | 
||
455  | 
fun readConstraint () =  | 
|
456  | 
let  | 
|
457  | 
val t = readLabeledTerm ()  | 
|
458  | 
fun make_constraint b t1 t2 =  | 
|
459  | 
cplexConstr  | 
|
460  | 
(str2cmp b,  | 
|
461  | 
(t1, t2))  | 
|
462  | 
in  | 
|
463  | 
if snd t = NONE then NONE  | 
|
464  | 
else  | 
|
465  | 
let val c = readToken () in  | 
|
466  | 
if c = NONE orelse fst (the c) <> TOKEN_CMP  | 
|
467  | 
then raise (Load_cplexFile "TOKEN_CMP expected")  | 
|
468  | 
else  | 
|
469  | 
let val n = readTerm_Signed false true in  | 
|
470  | 
if n = NONE then  | 
|
471  | 
raise (Load_cplexFile "num expected")  | 
|
472  | 
else  | 
|
473  | 
SOME (fst t,  | 
|
474  | 
make_constraint (snd (the c))  | 
|
475  | 
(the (snd t))  | 
|
476  | 
(the n))  | 
|
477  | 
end  | 
|
478  | 
end  | 
|
479  | 
end  | 
|
480  | 
||
481  | 
fun readST () =  | 
|
482  | 
let  | 
|
483  | 
fun readbody () =  | 
|
484  | 
let val t = readConstraint () in  | 
|
485  | 
if t = NONE then []  | 
|
486  | 
else if (is_normed_Constr (snd (the t))) then  | 
|
487  | 
(the t)::(readbody ())  | 
|
488  | 
else if (fst (the t) <> NONE) then  | 
|
489  | 
raise (Load_cplexFile  | 
|
490  | 
				       ("constraint '"^(the (fst (the t)))^ 
 | 
|
491  | 
"'is not normed"))  | 
|
492  | 
else  | 
|
493  | 
raise (Load_cplexFile  | 
|
494  | 
"constraint is not normed")  | 
|
495  | 
end  | 
|
496  | 
in  | 
|
497  | 
if readToken () = SOME (TOKEN_KEYWORD, "ST")  | 
|
498  | 
then  | 
|
499  | 
readbody ()  | 
|
500  | 
else  | 
|
501  | 
raise (Load_cplexFile "ST expected")  | 
|
502  | 
end  | 
|
503  | 
||
504  | 
fun readCmp () =  | 
|
505  | 
let val c = readToken () in  | 
|
506  | 
if c = NONE then NONE  | 
|
507  | 
else if fst (the c) = TOKEN_CMP then  | 
|
508  | 
SOME (str2cmp (snd (the c)))  | 
|
509  | 
else (pushToken (the c); NONE)  | 
|
510  | 
end  | 
|
511  | 
||
512  | 
fun skip_NL () =  | 
|
513  | 
let val c = readToken () in  | 
|
514  | 
if c <> NONE andalso fst (the c) = TOKEN_NL then  | 
|
515  | 
skip_NL ()  | 
|
516  | 
else  | 
|
517  | 
(pushToken (the c); ())  | 
|
518  | 
end  | 
|
519  | 
||
520  | 
fun is_var (cplexVar _) = true  | 
|
521  | 
| is_var _ = false  | 
|
522  | 
||
523  | 
fun make_bounds c t1 t2 =  | 
|
524  | 
cplexBound (t1, c, t2)  | 
|
525  | 
||
526  | 
fun readBound () =  | 
|
527  | 
let  | 
|
528  | 
val _ = skip_NL ()  | 
|
529  | 
val t1 = readTerm ()  | 
|
530  | 
in  | 
|
531  | 
if t1 = NONE then NONE  | 
|
532  | 
else  | 
|
533  | 
let  | 
|
534  | 
val c1 = readCmp ()  | 
|
535  | 
in  | 
|
536  | 
if c1 = NONE then  | 
|
537  | 
let  | 
|
538  | 
val c = readToken ()  | 
|
539  | 
in  | 
|
540  | 
if c = SOME (TOKEN_KEYWORD, "FREE") then  | 
|
541  | 
SOME (  | 
|
542  | 
cplexBounds (cplexNeg cplexInf,  | 
|
543  | 
cplexLeq,  | 
|
544  | 
the t1,  | 
|
545  | 
cplexLeq,  | 
|
546  | 
cplexInf))  | 
|
547  | 
else  | 
|
548  | 
raise (Load_cplexFile "FREE expected")  | 
|
549  | 
end  | 
|
550  | 
else  | 
|
551  | 
let  | 
|
552  | 
val t2 = readTerm ()  | 
|
553  | 
in  | 
|
554  | 
if t2 = NONE then  | 
|
555  | 
raise (Load_cplexFile "term expected")  | 
|
556  | 
else  | 
|
557  | 
let val c2 = readCmp () in  | 
|
558  | 
if c2 = NONE then  | 
|
559  | 
SOME (make_bounds (the c1)  | 
|
560  | 
(the t1)  | 
|
561  | 
(the t2))  | 
|
562  | 
else  | 
|
563  | 
SOME (  | 
|
564  | 
cplexBounds (the t1,  | 
|
565  | 
the c1,  | 
|
566  | 
the t2,  | 
|
567  | 
the c2,  | 
|
568  | 
the (readTerm())))  | 
|
569  | 
end  | 
|
570  | 
end  | 
|
571  | 
end  | 
|
572  | 
end  | 
|
573  | 
||
574  | 
fun readBounds () =  | 
|
575  | 
let  | 
|
576  | 
fun makestring b = "?"  | 
|
577  | 
fun readbody () =  | 
|
578  | 
let  | 
|
579  | 
val b = readBound ()  | 
|
580  | 
in  | 
|
581  | 
if b = NONE then []  | 
|
582  | 
else if (is_normed_Bounds (the b)) then  | 
|
583  | 
(the b)::(readbody())  | 
|
584  | 
else (  | 
|
585  | 
raise (Load_cplexFile  | 
|
586  | 
				       ("bounds are not normed in: "^
 | 
|
587  | 
(makestring (the b)))))  | 
|
588  | 
end  | 
|
589  | 
in  | 
|
590  | 
if readToken () = SOME (TOKEN_KEYWORD, "BOUNDS") then  | 
|
591  | 
readbody ()  | 
|
592  | 
else raise (Load_cplexFile "BOUNDS expected")  | 
|
593  | 
end  | 
|
594  | 
||
595  | 
fun readEnd () =  | 
|
596  | 
if readToken () = SOME (TOKEN_KEYWORD, "END") then ()  | 
|
597  | 
else raise (Load_cplexFile "END expected")  | 
|
598  | 
||
599  | 
val result_Goal = readGoal ()  | 
|
600  | 
val result_ST = readST ()  | 
|
601  | 
val _ = ignore_NL := false  | 
|
602  | 
val result_Bounds = readBounds ()  | 
|
603  | 
val _ = ignore_NL := true  | 
|
604  | 
val _ = readEnd ()  | 
|
605  | 
val _ = TextIO.closeIn f  | 
|
606  | 
in  | 
|
607  | 
cplexProg (name, result_Goal, result_ST, result_Bounds)  | 
|
608  | 
end  | 
|
609  | 
||
610  | 
fun save_cplexFile filename (cplexProg (name, goal, constraints, bounds)) =  | 
|
611  | 
let  | 
|
612  | 
val f = TextIO.openOut filename  | 
|
613  | 
||
614  | 
fun basic_write s = TextIO.output(f, s)  | 
|
615  | 
||
616  | 
val linebuf = ref ""  | 
|
617  | 
fun buf_flushline s =  | 
|
618  | 
(basic_write (!linebuf);  | 
|
619  | 
basic_write "\n";  | 
|
620  | 
linebuf := s)  | 
|
621  | 
fun buf_add s = linebuf := (!linebuf) ^ s  | 
|
622  | 
||
623  | 
fun write s =  | 
|
624  | 
if (String.size s) + (String.size (!linebuf)) >= 250 then  | 
|
625  | 
		buf_flushline ("    "^s)
 | 
|
626  | 
else  | 
|
627  | 
buf_add s  | 
|
628  | 
||
629  | 
fun writeln s = (buf_add s; buf_flushline "")  | 
|
630  | 
||
631  | 
fun write_term (cplexVar x) = write x  | 
|
632  | 
| write_term (cplexNum x) = write x  | 
|
633  | 
| write_term cplexInf = write "inf"  | 
|
634  | 
| write_term (cplexProd (cplexNum "1", b)) = write_term b  | 
|
635  | 
| write_term (cplexProd (a, b)) =  | 
|
636  | 
(write_term a; write " "; write_term b)  | 
|
637  | 
| write_term (cplexNeg x) = (write " - "; write_term x)  | 
|
638  | 
| write_term (cplexSum ts) = write_terms ts  | 
|
639  | 
and write_terms [] = ()  | 
|
640  | 
| write_terms (t::ts) =  | 
|
641  | 
(if (not (is_Neg t)) then write " + " else ();  | 
|
642  | 
write_term t; write_terms ts)  | 
|
643  | 
||
644  | 
fun write_goal (cplexMaximize term) =  | 
|
645  | 
(writeln "MAXIMIZE"; write_term term; writeln "")  | 
|
646  | 
| write_goal (cplexMinimize term) =  | 
|
647  | 
(writeln "MINIMIZE"; write_term term; writeln "")  | 
|
648  | 
||
649  | 
fun write_cmp cplexLe = write "<"  | 
|
650  | 
| write_cmp cplexLeq = write "<="  | 
|
651  | 
| write_cmp cplexEq = write "="  | 
|
652  | 
| write_cmp cplexGe = write ">"  | 
|
653  | 
| write_cmp cplexGeq = write ">="  | 
|
654  | 
||
655  | 
fun write_constr (cplexConstr (cmp, (a,b))) =  | 
|
656  | 
(write_term a;  | 
|
657  | 
write " ";  | 
|
658  | 
write_cmp cmp;  | 
|
659  | 
write " ";  | 
|
660  | 
write_term b)  | 
|
661  | 
||
662  | 
fun write_constraints [] = ()  | 
|
663  | 
| write_constraints (c::cs) =  | 
|
664  | 
(if (fst c <> NONE)  | 
|
665  | 
then  | 
|
666  | 
(write (the (fst c)); write ": ")  | 
|
667  | 
else  | 
|
668  | 
();  | 
|
669  | 
write_constr (snd c);  | 
|
670  | 
writeln "";  | 
|
671  | 
write_constraints cs)  | 
|
672  | 
||
673  | 
fun write_bounds [] = ()  | 
|
674  | 
| write_bounds ((cplexBounds (t1,c1,t2,c2,t3))::bs) =  | 
|
675  | 
((if t1 = cplexNeg cplexInf andalso t3 = cplexInf  | 
|
676  | 
andalso (c1 = cplexLeq orelse c1 = cplexLe)  | 
|
677  | 
andalso (c2 = cplexLeq orelse c2 = cplexLe)  | 
|
678  | 
then  | 
|
679  | 
(write_term t2; write " free")  | 
|
680  | 
else  | 
|
681  | 
(write_term t1; write " "; write_cmp c1; write " ";  | 
|
682  | 
write_term t2; write " "; write_cmp c2; write " ";  | 
|
683  | 
write_term t3)  | 
|
684  | 
); writeln ""; write_bounds bs)  | 
|
685  | 
| write_bounds ((cplexBound (t1, c, t2)) :: bs) =  | 
|
686  | 
(write_term t1; write " ";  | 
|
687  | 
write_cmp c; write " ";  | 
|
688  | 
write_term t2; writeln ""; write_bounds bs)  | 
|
689  | 
||
690  | 
val _ = write_goal goal  | 
|
691  | 
val _ = (writeln ""; writeln "ST")  | 
|
692  | 
val _ = write_constraints constraints  | 
|
693  | 
val _ = (writeln ""; writeln "BOUNDS")  | 
|
694  | 
val _ = write_bounds bounds  | 
|
695  | 
val _ = (writeln ""; writeln "END")  | 
|
696  | 
val _ = TextIO.closeOut f  | 
|
697  | 
in  | 
|
698  | 
()  | 
|
699  | 
end  | 
|
700  | 
||
701  | 
fun norm_Constr (constr as cplexConstr (c, (t1, t2))) =  | 
|
702  | 
if not (modulo_signed is_Num t2) andalso  | 
|
703  | 
modulo_signed is_Num t1  | 
|
704  | 
then  | 
|
705  | 
[cplexConstr (rev_cmp c, (t2, t1))]  | 
|
706  | 
else if (c = cplexLe orelse c = cplexLeq) andalso  | 
|
707  | 
(t1 = (cplexNeg cplexInf) orelse t2 = cplexInf)  | 
|
708  | 
then  | 
|
709  | 
[]  | 
|
710  | 
else if (c = cplexGe orelse c = cplexGeq) andalso  | 
|
711  | 
(t1 = cplexInf orelse t2 = cplexNeg cplexInf)  | 
|
712  | 
then  | 
|
713  | 
[]  | 
|
714  | 
else  | 
|
715  | 
[constr]  | 
|
716  | 
||
717  | 
fun bound2constr (cplexBounds (t1,c1,t2,c2,t3)) =  | 
|
718  | 
(norm_Constr(cplexConstr (c1, (t1, t2))))  | 
|
719  | 
@ (norm_Constr(cplexConstr (c2, (t2, t3))))  | 
|
720  | 
| bound2constr (cplexBound (t1, cplexEq, t2)) =  | 
|
721  | 
(norm_Constr(cplexConstr (cplexLeq, (t1, t2))))  | 
|
722  | 
@ (norm_Constr(cplexConstr (cplexLeq, (t2, t1))))  | 
|
723  | 
| bound2constr (cplexBound (t1, c1, t2)) =  | 
|
724  | 
norm_Constr(cplexConstr (c1, (t1,t2)))  | 
|
725  | 
||
726  | 
val emptyset = Symtab.empty  | 
|
727  | 
||
| 17412 | 728  | 
fun singleton v = Symtab.update (v, ()) emptyset  | 
| 16784 | 729  | 
|
730  | 
fun merge a b = Symtab.merge (op =) (a, b)  | 
|
731  | 
||
| 21056 | 732  | 
fun mergemap f ts = fold (fn x => fn table => merge table (f x)) ts Symtab.empty  | 
| 16784 | 733  | 
|
| 21056 | 734  | 
fun diff a b = Symtab.fold (fn (k, v) => fn a =>  | 
| 16784 | 735  | 
(Symtab.delete k a) handle UNDEF => a)  | 
| 21056 | 736  | 
b a  | 
| 16784 | 737  | 
|
738  | 
fun collect_vars (cplexVar v) = singleton v  | 
|
739  | 
| collect_vars (cplexNeg t) = collect_vars t  | 
|
740  | 
| collect_vars (cplexProd (t1, t2)) =  | 
|
741  | 
merge (collect_vars t1) (collect_vars t2)  | 
|
742  | 
| collect_vars (cplexSum ts) = mergemap collect_vars ts  | 
|
743  | 
| collect_vars _ = emptyset  | 
|
744  | 
||
745  | 
(* Eliminates all nonfree bounds from the linear program and produces an  | 
|
746  | 
equivalent program with only free bounds  | 
|
747  | 
IF for the input program P holds: is_normed_cplexProg P *)  | 
|
748  | 
fun elim_nonfree_bounds (cplexProg (name, goal, constraints, bounds)) =  | 
|
749  | 
let  | 
|
750  | 
fun collect_constr_vars (_, cplexConstr (c, (t1,_))) =  | 
|
751  | 
(collect_vars t1)  | 
|
752  | 
||
753  | 
val cvars = merge (collect_vars (term_of_goal goal))  | 
|
754  | 
(mergemap collect_constr_vars constraints)  | 
|
755  | 
||
756  | 
fun collect_lower_bounded_vars  | 
|
757  | 
(cplexBounds (t1, c1, cplexVar v, c2, t3)) =  | 
|
758  | 
singleton v  | 
|
759  | 
| collect_lower_bounded_vars  | 
|
760  | 
(cplexBound (_, cplexLe, cplexVar v)) =  | 
|
761  | 
singleton v  | 
|
762  | 
| collect_lower_bounded_vars  | 
|
763  | 
(cplexBound (_, cplexLeq, cplexVar v)) =  | 
|
764  | 
singleton v  | 
|
765  | 
| collect_lower_bounded_vars  | 
|
766  | 
(cplexBound (cplexVar v, cplexGe,_)) =  | 
|
767  | 
singleton v  | 
|
768  | 
| collect_lower_bounded_vars  | 
|
769  | 
(cplexBound (cplexVar v, cplexGeq, _)) =  | 
|
770  | 
singleton v  | 
|
771  | 
| collect_lower_bounded_vars  | 
|
772  | 
(cplexBound (cplexVar v, cplexEq, _)) =  | 
|
773  | 
singleton v  | 
|
774  | 
| collect_lower_bounded_vars _ = emptyset  | 
|
775  | 
||
776  | 
val lvars = mergemap collect_lower_bounded_vars bounds  | 
|
777  | 
val positive_vars = diff cvars lvars  | 
|
778  | 
val zero = cplexNum "0"  | 
|
779  | 
||
780  | 
fun make_pos_constr v =  | 
|
781  | 
(NONE, cplexConstr (cplexGeq, ((cplexVar v), zero)))  | 
|
782  | 
||
783  | 
fun make_free_bound v =  | 
|
784  | 
cplexBounds (cplexNeg cplexInf, cplexLeq,  | 
|
785  | 
cplexVar v, cplexLeq,  | 
|
786  | 
cplexInf)  | 
|
787  | 
||
| 21056 | 788  | 
val pos_constrs = rev (Symtab.fold  | 
789  | 
(fn (k, v) => cons (make_pos_constr k))  | 
|
790  | 
positive_vars [])  | 
|
791  | 
val bound_constrs = map (pair NONE)  | 
|
792  | 
(maps bound2constr bounds)  | 
|
793  | 
val constraints' = constraints @ pos_constrs @ bound_constrs  | 
|
794  | 
val bounds' = rev (Symtab.fold (fn (v, _) => cons (make_free_bound v)) cvars []);  | 
|
| 16784 | 795  | 
in  | 
796  | 
cplexProg (name, goal, constraints', bounds')  | 
|
797  | 
end  | 
|
798  | 
||
799  | 
fun relax_strict_ineqs (cplexProg (name, goals, constrs, bounds)) =  | 
|
800  | 
let  | 
|
801  | 
fun relax cplexLe = cplexLeq  | 
|
802  | 
| relax cplexGe = cplexGeq  | 
|
803  | 
| relax x = x  | 
|
804  | 
||
805  | 
fun relax_constr (n, cplexConstr(c, (t1, t2))) =  | 
|
806  | 
(n, cplexConstr(relax c, (t1, t2)))  | 
|
807  | 
||
808  | 
fun relax_bounds (cplexBounds (t1, c1, t2, c2, t3)) =  | 
|
809  | 
cplexBounds (t1, relax c1, t2, relax c2, t3)  | 
|
810  | 
| relax_bounds (cplexBound (t1, c, t2)) =  | 
|
811  | 
cplexBound (t1, relax c, t2)  | 
|
812  | 
in  | 
|
813  | 
cplexProg (name,  | 
|
814  | 
goals,  | 
|
815  | 
map relax_constr constrs,  | 
|
816  | 
map relax_bounds bounds)  | 
|
817  | 
end  | 
|
818  | 
||
819  | 
datatype cplexResult = Unbounded  | 
|
820  | 
| Infeasible  | 
|
821  | 
| Undefined  | 
|
822  | 
| Optimal of string * ((string * string) list)  | 
|
823  | 
||
824  | 
fun is_separator x = forall (fn c => c = #"-") (String.explode x)  | 
|
825  | 
||
826  | 
fun is_sign x = (x = "+" orelse x = "-")  | 
|
827  | 
||
828  | 
fun is_colon x = (x = ":")  | 
|
829  | 
||
830  | 
fun is_resultsymbol a =  | 
|
831  | 
let  | 
|
832  | 
	val symbol_char = String.explode "!\"#$%&()/,.;?@_`'{}|~-"
 | 
|
833  | 
fun is_symbol_char c = Char.isAlphaNum c orelse  | 
|
834  | 
exists (fn d => d=c) symbol_char  | 
|
835  | 
fun is_symbol_start c = is_symbol_char c andalso  | 
|
836  | 
not (Char.isDigit c) andalso  | 
|
837  | 
not (c= #".") andalso  | 
|
838  | 
not (c= #"-")  | 
|
839  | 
val b = String.explode a  | 
|
840  | 
in  | 
|
841  | 
b <> [] andalso is_symbol_start (hd b) andalso  | 
|
842  | 
forall is_symbol_char b  | 
|
843  | 
end  | 
|
844  | 
||
845  | 
val TOKEN_SIGN = 100  | 
|
846  | 
val TOKEN_COLON = 101  | 
|
847  | 
val TOKEN_SEPARATOR = 102  | 
|
848  | 
||
849  | 
fun load_glpkResult name =  | 
|
850  | 
let  | 
|
851  | 
val flist = [(is_NL, TOKEN_NL),  | 
|
852  | 
(is_blank, TOKEN_BLANK),  | 
|
853  | 
(is_num, TOKEN_NUM),  | 
|
854  | 
(is_sign, TOKEN_SIGN),  | 
|
855  | 
(is_colon, TOKEN_COLON),  | 
|
856  | 
(is_cmp, TOKEN_CMP),  | 
|
857  | 
(is_resultsymbol, TOKEN_SYMBOL),  | 
|
858  | 
(is_separator, TOKEN_SEPARATOR)]  | 
|
859  | 
||
860  | 
val tokenize = tokenize_general flist  | 
|
861  | 
||
862  | 
val f = TextIO.openIn name  | 
|
863  | 
||
864  | 
val rest = ref []  | 
|
865  | 
||
866  | 
fun readToken_helper () =  | 
|
867  | 
if length (!rest) > 0 then  | 
|
868  | 
let val u = hd (!rest) in  | 
|
869  | 
(  | 
|
870  | 
rest := tl (!rest);  | 
|
871  | 
SOME u  | 
|
872  | 
)  | 
|
873  | 
end  | 
|
874  | 
else  | 
|
875  | 
let val s = TextIO.inputLine f in  | 
|
876  | 
if s = "" then NONE else (rest := tokenize s; readToken_helper())  | 
|
877  | 
end  | 
|
878  | 
||
879  | 
fun is_tt tok ty = (tok <> NONE andalso (fst (the tok)) = ty)  | 
|
880  | 
||
881  | 
fun pushToken a = if a = NONE then () else (rest := ((the a)::(!rest)))  | 
|
882  | 
||
883  | 
fun readToken () =  | 
|
884  | 
let val t = readToken_helper () in  | 
|
885  | 
if is_tt t TOKEN_BLANK then  | 
|
886  | 
readToken ()  | 
|
887  | 
else if is_tt t TOKEN_NL then  | 
|
888  | 
let val t2 = readToken_helper () in  | 
|
889  | 
if is_tt t2 TOKEN_SIGN then  | 
|
890  | 
(pushToken (SOME (TOKEN_SEPARATOR, "-")); t)  | 
|
891  | 
else  | 
|
892  | 
(pushToken t2; t)  | 
|
893  | 
end  | 
|
894  | 
else if is_tt t TOKEN_SIGN then  | 
|
895  | 
let val t2 = readToken_helper () in  | 
|
896  | 
if is_tt t2 TOKEN_NUM then  | 
|
897  | 
(SOME (TOKEN_NUM, (snd (the t))^(snd (the t2))))  | 
|
898  | 
else  | 
|
899  | 
(pushToken t2; t)  | 
|
900  | 
end  | 
|
901  | 
else  | 
|
902  | 
t  | 
|
903  | 
end  | 
|
904  | 
||
905  | 
fun readRestOfLine P =  | 
|
906  | 
let  | 
|
907  | 
val t = readToken ()  | 
|
908  | 
in  | 
|
909  | 
if is_tt t TOKEN_NL orelse t = NONE  | 
|
910  | 
then P  | 
|
911  | 
else readRestOfLine P  | 
|
912  | 
end  | 
|
913  | 
||
914  | 
fun readHeader () =  | 
|
915  | 
let  | 
|
916  | 
		fun readStatus () = readRestOfLine ("STATUS", snd (the (readToken ())))
 | 
|
917  | 
		fun readObjective () = readRestOfLine ("OBJECTIVE", snd (the (readToken (); readToken (); readToken ())))
 | 
|
918  | 
val t1 = readToken ()  | 
|
919  | 
val t2 = readToken ()  | 
|
920  | 
in  | 
|
921  | 
if is_tt t1 TOKEN_SYMBOL andalso is_tt t2 TOKEN_COLON  | 
|
922  | 
then  | 
|
923  | 
case to_upper (snd (the t1)) of  | 
|
924  | 
"STATUS" => (readStatus ())::(readHeader ())  | 
|
925  | 
| "OBJECTIVE" => (readObjective())::(readHeader ())  | 
|
926  | 
| _ => (readRestOfLine (); readHeader ())  | 
|
927  | 
else  | 
|
928  | 
(pushToken t2; pushToken t1; [])  | 
|
929  | 
end  | 
|
930  | 
||
931  | 
fun skip_until_sep () =  | 
|
932  | 
let val x = readToken () in  | 
|
933  | 
if is_tt x TOKEN_SEPARATOR then  | 
|
934  | 
readRestOfLine ()  | 
|
935  | 
else  | 
|
936  | 
skip_until_sep ()  | 
|
937  | 
end  | 
|
938  | 
||
939  | 
fun load_value () =  | 
|
940  | 
let  | 
|
941  | 
val t1 = readToken ()  | 
|
942  | 
val t2 = readToken ()  | 
|
943  | 
in  | 
|
944  | 
if is_tt t1 TOKEN_NUM andalso is_tt t2 TOKEN_SYMBOL then  | 
|
945  | 
let  | 
|
946  | 
val t = readToken ()  | 
|
947  | 
val state = if is_tt t TOKEN_NL then readToken () else t  | 
|
948  | 
val _ = if is_tt state TOKEN_SYMBOL then () else raise (Load_cplexResult "state expected")  | 
|
949  | 
val k = readToken ()  | 
|
950  | 
in  | 
|
951  | 
if is_tt k TOKEN_NUM then  | 
|
952  | 
readRestOfLine (SOME (snd (the t2), snd (the k)))  | 
|
953  | 
else  | 
|
954  | 
raise (Load_cplexResult "number expected")  | 
|
955  | 
end  | 
|
956  | 
else  | 
|
957  | 
(pushToken t2; pushToken t1; NONE)  | 
|
958  | 
end  | 
|
959  | 
||
960  | 
fun load_values () =  | 
|
961  | 
let val v = load_value () in  | 
|
962  | 
if v = NONE then [] else (the v)::(load_values ())  | 
|
963  | 
end  | 
|
964  | 
||
965  | 
val header = readHeader ()  | 
|
966  | 
||
967  | 
val result =  | 
|
| 17521 | 968  | 
case AList.lookup (op =) header "STATUS" of  | 
| 16784 | 969  | 
SOME "INFEASIBLE" => Infeasible  | 
970  | 
| SOME "UNBOUNDED" => Unbounded  | 
|
| 17521 | 971  | 
| SOME "OPTIMAL" => Optimal (the (AList.lookup (op =) header "OBJECTIVE"),  | 
| 16784 | 972  | 
(skip_until_sep ();  | 
973  | 
skip_until_sep ();  | 
|
974  | 
load_values ()))  | 
|
975  | 
| _ => Undefined  | 
|
976  | 
||
977  | 
val _ = TextIO.closeIn f  | 
|
978  | 
in  | 
|
979  | 
result  | 
|
980  | 
end  | 
|
981  | 
    handle (Tokenize s) => raise (Load_cplexResult ("Tokenize: "^s))
 | 
|
982  | 
| Option => raise (Load_cplexResult "Option")  | 
|
983  | 
| x => raise x  | 
|
984  | 
||
985  | 
fun load_cplexResult name =  | 
|
986  | 
let  | 
|
987  | 
val flist = [(is_NL, TOKEN_NL),  | 
|
988  | 
(is_blank, TOKEN_BLANK),  | 
|
989  | 
(is_num, TOKEN_NUM),  | 
|
990  | 
(is_sign, TOKEN_SIGN),  | 
|
991  | 
(is_colon, TOKEN_COLON),  | 
|
992  | 
(is_cmp, TOKEN_CMP),  | 
|
993  | 
(is_resultsymbol, TOKEN_SYMBOL)]  | 
|
994  | 
||
995  | 
val tokenize = tokenize_general flist  | 
|
996  | 
||
997  | 
val f = TextIO.openIn name  | 
|
998  | 
||
999  | 
val rest = ref []  | 
|
1000  | 
||
1001  | 
fun readToken_helper () =  | 
|
1002  | 
if length (!rest) > 0 then  | 
|
1003  | 
let val u = hd (!rest) in  | 
|
1004  | 
(  | 
|
1005  | 
rest := tl (!rest);  | 
|
1006  | 
SOME u  | 
|
1007  | 
)  | 
|
1008  | 
end  | 
|
1009  | 
else  | 
|
1010  | 
let  | 
|
1011  | 
val s = TextIO.inputLine f  | 
|
1012  | 
in  | 
|
1013  | 
if s = "" then NONE else (rest := tokenize s; readToken_helper())  | 
|
1014  | 
end  | 
|
1015  | 
||
1016  | 
fun is_tt tok ty = (tok <> NONE andalso (fst (the tok)) = ty)  | 
|
1017  | 
||
1018  | 
fun pushToken a = if a = NONE then () else (rest := ((the a)::(!rest)))  | 
|
1019  | 
||
1020  | 
fun readToken () =  | 
|
1021  | 
let val t = readToken_helper () in  | 
|
1022  | 
if is_tt t TOKEN_BLANK then  | 
|
1023  | 
readToken ()  | 
|
1024  | 
else if is_tt t TOKEN_SIGN then  | 
|
1025  | 
let val t2 = readToken_helper () in  | 
|
1026  | 
if is_tt t2 TOKEN_NUM then  | 
|
1027  | 
(SOME (TOKEN_NUM, (snd (the t))^(snd (the t2))))  | 
|
1028  | 
else  | 
|
1029  | 
(pushToken t2; t)  | 
|
1030  | 
end  | 
|
1031  | 
else  | 
|
1032  | 
t  | 
|
1033  | 
end  | 
|
1034  | 
||
1035  | 
fun readRestOfLine P =  | 
|
1036  | 
let  | 
|
1037  | 
val t = readToken ()  | 
|
1038  | 
in  | 
|
1039  | 
if is_tt t TOKEN_NL orelse t = NONE  | 
|
1040  | 
then P  | 
|
1041  | 
else readRestOfLine P  | 
|
1042  | 
end  | 
|
1043  | 
||
1044  | 
fun readHeader () =  | 
|
1045  | 
let  | 
|
1046  | 
		fun readStatus () = readRestOfLine ("STATUS", snd (the (readToken ())))
 | 
|
1047  | 
fun readObjective () =  | 
|
1048  | 
let  | 
|
1049  | 
val t = readToken ()  | 
|
1050  | 
in  | 
|
1051  | 
if is_tt t TOKEN_SYMBOL andalso to_upper (snd (the t)) = "VALUE" then  | 
|
1052  | 
			    readRestOfLine ("OBJECTIVE", snd (the (readToken())))
 | 
|
1053  | 
else  | 
|
1054  | 
			    readRestOfLine ("OBJECTIVE_NAME", snd (the t))
 | 
|
1055  | 
end  | 
|
1056  | 
||
1057  | 
val t = readToken ()  | 
|
1058  | 
in  | 
|
1059  | 
if is_tt t TOKEN_SYMBOL then  | 
|
1060  | 
case to_upper (snd (the t)) of  | 
|
1061  | 
"STATUS" => (readStatus ())::(readHeader ())  | 
|
1062  | 
| "OBJECTIVE" => (readObjective ())::(readHeader ())  | 
|
1063  | 
| "SECTION" => (pushToken t; [])  | 
|
1064  | 
| _ => (readRestOfLine (); readHeader ())  | 
|
1065  | 
else  | 
|
1066  | 
(readRestOfLine (); readHeader ())  | 
|
1067  | 
end  | 
|
1068  | 
||
1069  | 
fun skip_nls () =  | 
|
1070  | 
let val x = readToken () in  | 
|
1071  | 
if is_tt x TOKEN_NL then  | 
|
1072  | 
skip_nls ()  | 
|
1073  | 
else  | 
|
1074  | 
(pushToken x; ())  | 
|
1075  | 
end  | 
|
1076  | 
||
1077  | 
fun skip_paragraph () =  | 
|
1078  | 
if is_tt (readToken ()) TOKEN_NL then  | 
|
1079  | 
(if is_tt (readToken ()) TOKEN_NL then  | 
|
1080  | 
skip_nls ()  | 
|
1081  | 
else  | 
|
1082  | 
skip_paragraph ())  | 
|
1083  | 
else  | 
|
1084  | 
skip_paragraph ()  | 
|
1085  | 
||
1086  | 
fun load_value () =  | 
|
1087  | 
let  | 
|
1088  | 
val t1 = readToken ()  | 
|
1089  | 
val t1 = if is_tt t1 TOKEN_SYMBOL andalso snd (the t1) = "A" then readToken () else t1  | 
|
1090  | 
in  | 
|
1091  | 
if is_tt t1 TOKEN_NUM then  | 
|
1092  | 
let  | 
|
1093  | 
val name = readToken ()  | 
|
1094  | 
val status = readToken ()  | 
|
1095  | 
val value = readToken ()  | 
|
1096  | 
in  | 
|
1097  | 
if is_tt name TOKEN_SYMBOL andalso  | 
|
1098  | 
is_tt status TOKEN_SYMBOL andalso  | 
|
1099  | 
is_tt value TOKEN_NUM  | 
|
1100  | 
then  | 
|
1101  | 
readRestOfLine (SOME (snd (the name), snd (the value)))  | 
|
1102  | 
else  | 
|
1103  | 
raise (Load_cplexResult "column line expected")  | 
|
1104  | 
end  | 
|
1105  | 
else  | 
|
1106  | 
(pushToken t1; NONE)  | 
|
1107  | 
end  | 
|
1108  | 
||
1109  | 
fun load_values () =  | 
|
1110  | 
let val v = load_value () in  | 
|
1111  | 
if v = NONE then [] else (the v)::(load_values ())  | 
|
1112  | 
end  | 
|
1113  | 
||
1114  | 
val header = readHeader ()  | 
|
1115  | 
||
1116  | 
val result =  | 
|
| 17521 | 1117  | 
case AList.lookup (op =) header "STATUS" of  | 
| 16784 | 1118  | 
SOME "INFEASIBLE" => Infeasible  | 
1119  | 
| SOME "NONOPTIMAL" => Unbounded  | 
|
| 17521 | 1120  | 
| SOME "OPTIMAL" => Optimal (the (AList.lookup (op =) header "OBJECTIVE"),  | 
| 16784 | 1121  | 
(skip_paragraph ();  | 
1122  | 
skip_paragraph ();  | 
|
1123  | 
skip_paragraph ();  | 
|
1124  | 
skip_paragraph ();  | 
|
1125  | 
skip_paragraph ();  | 
|
1126  | 
load_values ()))  | 
|
1127  | 
| _ => Undefined  | 
|
1128  | 
||
1129  | 
val _ = TextIO.closeIn f  | 
|
1130  | 
in  | 
|
1131  | 
result  | 
|
1132  | 
end  | 
|
1133  | 
    handle (Tokenize s) => raise (Load_cplexResult ("Tokenize: "^s))
 | 
|
1134  | 
| Option => raise (Load_cplexResult "Option")  | 
|
1135  | 
| x => raise x  | 
|
1136  | 
||
1137  | 
exception Execute of string;  | 
|
1138  | 
||
| 
21858
 
05f57309170c
avoid conflict with Alice keywords: renamed pack -> implode, unpack -> explode, any -> many, avoided assert;
 
wenzelm 
parents: 
21056 
diff
changeset
 | 
1139  | 
fun tmp_file s = Path.implode (Path.expand (File.tmp_path (Path.make [s])));  | 
| 16784 | 1140  | 
fun wrap s = "\""^s^"\"";  | 
1141  | 
||
1142  | 
fun solve_glpk prog =  | 
|
1143  | 
let  | 
|
1144  | 
val name = LargeInt.toString (Time.toMicroseconds (Time.now ()))  | 
|
1145  | 
val lpname = tmp_file (name^".lp")  | 
|
1146  | 
val resultname = tmp_file (name^".txt")  | 
|
1147  | 
val _ = save_cplexFile lpname prog  | 
|
| 
16966
 
37e34f315057
1. changed configuration variables for linear programming (Cplex_tools):
 
obua 
parents: 
16873 
diff
changeset
 | 
1148  | 
val cplex_path = getenv "GLPK_PATH"  | 
| 16784 | 1149  | 
val cplex = if cplex_path = "" then "glpsol" else cplex_path  | 
1150  | 
val command = (wrap cplex)^" --lpt "^(wrap lpname)^" --output "^(wrap resultname)  | 
|
1151  | 
val answer = execute command  | 
|
1152  | 
in  | 
|
1153  | 
let  | 
|
1154  | 
val result = load_glpkResult resultname  | 
|
1155  | 
val _ = OS.FileSys.remove lpname  | 
|
1156  | 
val _ = OS.FileSys.remove resultname  | 
|
1157  | 
in  | 
|
1158  | 
result  | 
|
1159  | 
end  | 
|
1160  | 
	handle (Load_cplexResult s) => raise (Execute ("Load_cplexResult: "^s^"\nExecute: "^answer))
 | 
|
1161  | 
| _ => raise (Execute answer)  | 
|
1162  | 
end  | 
|
1163  | 
||
1164  | 
fun solve_cplex prog =  | 
|
1165  | 
let  | 
|
1166  | 
fun write_script s lp r =  | 
|
1167  | 
let  | 
|
1168  | 
val f = TextIO.openOut s  | 
|
1169  | 
val _ = TextIO.output (f, "read\n"^lp^"\noptimize\nwrite\n"^r^"\nquit")  | 
|
1170  | 
val _ = TextIO.closeOut f  | 
|
1171  | 
in  | 
|
1172  | 
()  | 
|
1173  | 
end  | 
|
1174  | 
||
1175  | 
val name = LargeInt.toString (Time.toMicroseconds (Time.now ()))  | 
|
1176  | 
val lpname = tmp_file (name^".lp")  | 
|
1177  | 
val resultname = tmp_file (name^".txt")  | 
|
1178  | 
val scriptname = tmp_file (name^".script")  | 
|
1179  | 
val _ = save_cplexFile lpname prog  | 
|
| 
16966
 
37e34f315057
1. changed configuration variables for linear programming (Cplex_tools):
 
obua 
parents: 
16873 
diff
changeset
 | 
1180  | 
val cplex_path = getenv "CPLEX_PATH"  | 
| 16784 | 1181  | 
val cplex = if cplex_path = "" then "cplex" else cplex_path  | 
1182  | 
val _ = write_script scriptname lpname resultname  | 
|
1183  | 
val command = (wrap cplex)^" < "^(wrap scriptname)^" > /dev/null"  | 
|
1184  | 
val answer = "return code "^(Int.toString (system command))  | 
|
1185  | 
in  | 
|
1186  | 
let  | 
|
1187  | 
val result = load_cplexResult resultname  | 
|
1188  | 
val _ = OS.FileSys.remove lpname  | 
|
1189  | 
val _ = OS.FileSys.remove resultname  | 
|
1190  | 
val _ = OS.FileSys.remove scriptname  | 
|
1191  | 
in  | 
|
1192  | 
result  | 
|
1193  | 
end  | 
|
1194  | 
end  | 
|
1195  | 
||
1196  | 
fun solve prog =  | 
|
| 
16966
 
37e34f315057
1. changed configuration variables for linear programming (Cplex_tools):
 
obua 
parents: 
16873 
diff
changeset
 | 
1197  | 
case get_solver () of  | 
| 
 
37e34f315057
1. changed configuration variables for linear programming (Cplex_tools):
 
obua 
parents: 
16873 
diff
changeset
 | 
1198  | 
SOLVER_DEFAULT =>  | 
| 
 
37e34f315057
1. changed configuration variables for linear programming (Cplex_tools):
 
obua 
parents: 
16873 
diff
changeset
 | 
1199  | 
(case getenv "LP_SOLVER" of  | 
| 
 
37e34f315057
1. changed configuration variables for linear programming (Cplex_tools):
 
obua 
parents: 
16873 
diff
changeset
 | 
1200  | 
"CPLEX" => solve_cplex prog  | 
| 
 
37e34f315057
1. changed configuration variables for linear programming (Cplex_tools):
 
obua 
parents: 
16873 
diff
changeset
 | 
1201  | 
| "GLPK" => solve_glpk prog  | 
| 
 
37e34f315057
1. changed configuration variables for linear programming (Cplex_tools):
 
obua 
parents: 
16873 
diff
changeset
 | 
1202  | 
         | _ => raise (Execute ("LP_SOLVER must be set to CPLEX or to GLPK")))
 | 
| 
 
37e34f315057
1. changed configuration variables for linear programming (Cplex_tools):
 
obua 
parents: 
16873 
diff
changeset
 | 
1203  | 
| SOLVER_CPLEX => solve_cplex prog  | 
| 
 
37e34f315057
1. changed configuration variables for linear programming (Cplex_tools):
 
obua 
parents: 
16873 
diff
changeset
 | 
1204  | 
| SOLVER_GLPK => solve_glpk prog  | 
| 16784 | 1205  | 
|
1206  | 
end;  | 
|
1207  | 
||
1208  | 
(*  | 
|
1209  | 
val demofile = "/home/obua/flyspeck/kepler/LP/cplexPent2.lp45"  | 
|
1210  | 
val demoout = "/home/obua/flyspeck/kepler/LP/test.out"  | 
|
1211  | 
val demoresult = "/home/obua/flyspeck/kepler/LP/try/test2.sol"  | 
|
1212  | 
||
1213  | 
fun loadcplex () = Cplex.relax_strict_ineqs  | 
|
1214  | 
(Cplex.load_cplexFile demofile)  | 
|
1215  | 
||
1216  | 
fun writecplex lp = Cplex.save_cplexFile demoout lp  | 
|
1217  | 
||
1218  | 
fun test () =  | 
|
1219  | 
let  | 
|
1220  | 
val lp = loadcplex ()  | 
|
1221  | 
val lp2 = Cplex.elim_nonfree_bounds lp  | 
|
1222  | 
in  | 
|
1223  | 
writecplex lp2  | 
|
1224  | 
end  | 
|
1225  | 
||
1226  | 
fun loadresult () = Cplex.load_cplexResult demoresult;  | 
|
1227  | 
*)  | 
|
1228  | 
||
1229  | 
(*val prog = Cplex.load_cplexFile "/home/obua/tmp/pent/graph_0.lpt";  | 
|
1230  | 
val _ = Cplex.solve prog;*)  |