Made lpp stuff modular.
[libfirm] / ir / be / lpp_rzcpx.c
1 /**
2  * Author:      Daniel Grund
3  * Date:                Fri 13.05.2005
4  * Copyright:   (c) Universitaet Karlsruhe
5  * Licence:     This file protected by GPL -  GNU GENERAL PUBLIC LICENSE.
6  */
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
10
11 #ifdef HAVE_IO_H
12 #include <io.h>
13 #endif
14
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <stdarg.h>
18 #include <string.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21
22 #include "debug.h"
23 #include "lpp.h"
24 #include "xmalloc.h"
25 #include "assert.h"
26 #include "hashptr.h"
27 #include "set.h"
28 #include "sp_matrix.h"
29
30 /* CPLEX-account related stuff */
31 #undef DELETE_FILES             /**< deletes all dumped files after use. Files on server are always deleted. */
32 #define SSH_USER_HOST "kb61@sp-smp.rz.uni-karlsruhe.de"
33 #define SSH_PASSWD_FILE "/ben/daniel/.smppw"
34 #define EXPECT_FILENAME "runme" /* name of the expect-script */
35
36 typedef struct _name_t name_t;
37 typedef enum _value_kind_t {none=0, start, solution} value_kind_t;
38
39 #define DEBUG_LVL SET_LEVEL_1
40 static firm_dbg_module_t *dbg = NULL;
41
42
43 struct _name_t {
44         char *name;                                     /**< the name of the var/constraint supplied by user */
45         int nr;                                         /**< the col/row number in the matrix */
46         value_kind_t value_kind;
47         double value;
48         union _type {
49                 var_t var_type;
50                 cst_t cst_type;
51         } type;
52 };
53
54 struct _lpp_t {
55         /* The problem data */
56         char *name;                                             /**< A textual name for this problem */
57         opt_t opt_type;                                 /**< Optimization direction */
58         sp_matrix_t *m;                                 /**< The matrix holding objective, constraints and rhs */
59
60         /* Cst/Var to Nr mapping */
61         set *cst2nr;                                    /**< Holds name_t's for constraints */
62         set *var2nr;                                    /**< Holds name_t's for variables */
63
64         /* Nr to Cst/Var mapping */
65         int cst_size, var_size;                 /**< Size of the csts/vars-arrays below */
66         int cst_next, var_next;                 /**< Next free position in arrays below */
67         name_t **csts;                                  /**< Pointers to the elements in the cst2nr set */
68         name_t **vars;                                  /**< Pointers to the elements in the var2nr set */
69
70         /* Solution stuff */
71         char *error;
72         sol_state_t sol_state;
73         double sol_time;
74         unsigned iterations;
75
76         unsigned next_name_number;
77 };
78
79 #define HASH_NAME_T(n) HASH_STR((n)->name, strlen((n)->name))
80
81 static INLINE FILE *ffopen(const char *base, const char *ext, const char *mode) {
82         FILE *out;
83         char buf[1024];
84
85         snprintf(buf, sizeof(buf), "%s.%s", base, ext);
86         if (! (out = fopen(buf, mode))) {
87                 fprintf(stderr, "Cannot open file %s in mode %s\n", buf, mode);
88                 return NULL;
89         }
90         return out;
91 }
92
93 static int cmp_name_t(const void *x, const void *y, size_t size) {
94         const name_t *n = x;
95         const name_t *m = y;
96         return strcmp(n->name, m->name);
97 }
98
99 #define INITIAL_SIZE 64
100
101 lpp_t *new_lpp(const char *name, opt_t opt_type) {
102         lpp_t *lpp;
103
104         dbg = firm_dbg_register("ir.be.copyoptilp");
105         firm_dbg_set_mask(dbg, DEBUG_LVL);
106
107         lpp = xcalloc(1, sizeof(*lpp));
108         lpp->name = xstrdup(name);
109         lpp->opt_type = opt_type;
110         lpp->cst2nr = new_set(cmp_name_t, INITIAL_SIZE);
111         lpp->var2nr = new_set(cmp_name_t, INITIAL_SIZE);
112         lpp->cst_size = INITIAL_SIZE;
113         lpp->var_size = INITIAL_SIZE;
114         lpp->csts = xcalloc(INITIAL_SIZE, sizeof(*lpp->csts));
115         lpp->vars = xcalloc(INITIAL_SIZE, sizeof(*lpp->vars));
116         lpp->m = new_matrix(INITIAL_SIZE, INITIAL_SIZE);
117         lpp_add_cst(lpp, "obj", objective, 0);
118         lpp_add_var(lpp, "rhs", rhs, 0);
119         return lpp;
120 }
121
122 void free_lpp(lpp_t *lpp) {
123         int i;
124         for(i=0;i<lpp->cst_next;++i)
125                 free(lpp->csts[i]->name);
126         for(i=0;i<lpp->var_next;++i)
127                 free(lpp->vars[i]->name);
128         del_set(lpp->cst2nr);
129         del_set(lpp->var2nr);
130         del_matrix(lpp->m);
131         free(lpp->name);
132         free(lpp->csts);
133         free(lpp->vars);
134         if (lpp->error)
135                 free(lpp->error);
136         free(lpp);
137 }
138
139 static INLINE int name2nr(set *where, char *name) {
140         name_t find, *found;
141         find.name = name;
142         found = set_find(where, &find, sizeof(find), HASH_NAME_T(&find));
143         return (found ? found->nr : -1);
144 }
145
146 #define cst_nr(lpp, name) name2nr(lpp->cst2nr, name)
147 #define var_nr(lpp, name) name2nr(lpp->var2nr, name)
148
149 static INLINE char *get_next_name(lpp_t *lpp) {
150         char *res = xmalloc(12);
151         snprintf(res, 12, "_%d", lpp->next_name_number++);
152         return res;
153 }
154
155 int lpp_add_cst(lpp_t *lpp, const char *cst_name, cst_t cst_type, double rhs) {
156         name_t n, *inner;
157         DBG((dbg, LEVEL_2, "%s %d %g\n", cst_name, cst_type, rhs));
158         if (cst_name && cst_name[0] == '_')
159                 return ERR_NAME_NOT_ALLOWED;
160         if (cst_name)
161                 n.name = xstrdup(cst_name);
162         else
163                 n.name = get_next_name(lpp);
164         n.nr = -1;
165         inner = set_insert(lpp->cst2nr, &n, sizeof(n), HASH_NAME_T(&n));
166         assert(inner);
167
168         if (inner->nr == -1) {
169                 inner->nr = lpp->cst_next;
170                 inner->type.cst_type = cst_type;
171                 if (lpp->cst_next == lpp->cst_size) {
172                         lpp->cst_size *= 2;
173                         lpp->csts = xrealloc(lpp->csts, lpp->cst_size * sizeof(*lpp->csts));
174                 }
175                 lpp->csts[lpp->cst_next] = inner;
176                 lpp->cst_next++;
177                 matrix_set(lpp->m, inner->nr, 0, rhs);
178         }
179
180         return inner->nr;
181 }
182
183 int lpp_get_cst_idx(lpp_t *lpp, char *cst_name) {
184         DBG((dbg, LEVEL_2, "%s --> %d\n", cst_name, cst_nr(lpp, cst_name)));
185         return cst_nr(lpp, cst_name);
186 }
187
188 const char *lpp_get_cst_name(lpp_t *lpp, int index) {
189         DBG((dbg, LEVEL_2, "%d --> %s\n", index, lpp->csts[index]->name));
190         return lpp->csts[index]->name;
191 }
192
193 int lpp_add_var(lpp_t *lpp, const char *var_name, var_t var_type, double obj) {
194         name_t n, *inner;
195         DBG((dbg, LEVEL_2, "%s %d %g\n", var_name, var_type, obj));
196         assert(var_type != invalid && "invalid is for internal use only");
197         if (var_name && var_name[0] == '_')
198                 return ERR_NAME_NOT_ALLOWED;
199         if (var_name)
200                 n.name = xstrdup(var_name);
201         else
202                 n.name = get_next_name(lpp);
203         n.nr = -1;
204         inner = set_insert(lpp->var2nr, &n, sizeof(n), HASH_NAME_T(&n));
205         assert(inner);
206
207         if (inner->nr == -1) {
208                 inner->nr = lpp->var_next;
209                 inner->value_kind = 0;
210                 inner->value = 0;
211                 inner->type.var_type = var_type;
212                 if (lpp->var_next == lpp->var_size) {
213                         lpp->var_size *= 2;
214                         lpp->vars = xrealloc(lpp->vars, lpp->var_size * sizeof(*lpp->vars));
215                 }
216                 lpp->vars[lpp->var_next] = inner;
217                 lpp->var_next++;
218                 matrix_set(lpp->m, 0, inner->nr, obj);
219         }
220
221         return inner->nr;
222 }
223
224 int lpp_get_var_idx(lpp_t *lpp, char *var_name) {
225         DBG((dbg, LEVEL_2, "%s --> %d\n", var_name, var_nr(lpp, var_name)));
226         return var_nr(lpp, var_name);
227 }
228
229 const char *lpp_get_var_name(lpp_t *lpp, int index) {
230         DBG((dbg, LEVEL_2, "%d --> %s\n", index, lpp->vars[index]->name));
231         return lpp->vars[index]->name;
232 }
233
234 int lpp_set_factor(lpp_t *lpp, char *cst_name, char *var_name, double value) {
235         int cst, var;
236
237         cst = cst_nr(lpp, cst_name);
238         var = var_nr(lpp, var_name);
239         assert(cst != -1 && var != -1);
240         if (cst == -1 || var == -1)
241                 return -1;
242         DBG((dbg, LEVEL_2, "%s[%d] %s[%d] %g\n", cst_name, cst, var_name, var, value));
243         matrix_set(lpp->m, cst, var, value);
244         return 0;
245 }
246
247 int lpp_set_factor_fast(lpp_t *lpp, int cst_idx, int var_idx, double value) {
248         assert(cst_idx >= 0 && var_idx >= 0);
249         assert(cst_idx < lpp->cst_next && var_idx < lpp->var_next);
250         if (cst_idx >= lpp->cst_next || var_idx >= lpp->var_next || cst_idx < 0 || var_idx < 0)
251                 return -1;
252         DBG((dbg, LEVEL_2, "%s[%d] %s[%d] %g\n", lpp->csts[cst_idx]->name, cst_idx, lpp->vars[var_idx]->name, var_idx, value));
253         matrix_set(lpp->m, cst_idx, var_idx, value);
254         return 0;
255 }
256
257 void lpp_set_start_value(lpp_t *lpp, int var_idx, double value) {
258         assert(var_idx >= 0 && var_idx < lpp->var_next);
259         DBG((dbg, LEVEL_2, "%d %s %g\n", var_idx, lpp->vars[var_idx]->name, value));
260         lpp->vars[var_idx]->value = value;
261         lpp->vars[var_idx]->value_kind = start;
262 }
263
264
265 /******************************************************************************
266        MPS-STUFF
267 ******************************************************************************/
268
269 /**
270  * Fixed-column mps where spaces are allowed in identifiers :-0
271  * Free mps where whitespace is a seperator :-)
272  * LP format
273  */
274 typedef enum _style_t {s_mps_fixed, s_mps_free} style_t;
275
276 static char *mps_cst_encoding[4] = {"N", "E", "L", "G"};
277 typedef enum _mps_line_t {l_raw,
278                                                   l_ind_name, l_ind_objs, l_ind_rows, l_ind_cols, l_ind_rhs, l_ind_end,
279                                                   l_data_row, l_data_col1, l_data_col2, l_data_mst, l_marker} mps_line_t;
280
281 static INLINE void mps_write_line(FILE *out, style_t style, mps_line_t line_type, ...) {
282         va_list args;
283         char *fmt = "";
284
285         assert(style == s_mps_fixed || style == s_mps_free);
286         va_start(args, line_type);
287
288         if (style == s_mps_fixed) {
289                 /* white spaces are important! */
290                 switch (line_type) {
291                         case l_raw:                     fmt = "%s\n"; break;
292                         case l_ind_name:        fmt = "NAME          %s\n"; break;
293                         case l_ind_objs:        fmt = "OBJSENSE\n"; break;
294                         case l_ind_rows:        fmt = "ROWS\n"; break;
295                         case l_ind_cols:        fmt = "COLUMNS\n"; break;
296                         case l_ind_rhs:         fmt = "RHS\n"; break;
297                         case l_ind_end:         fmt = "ENDATA\n"; break;
298                         case l_data_row:        fmt = " %-2s %-8s\n"; break; /* Field 1-2 */
299                         case l_data_col1:       fmt = "    %-8s  %-8s  %12g\n"; break; /* Field 2-4 */
300                         case l_data_col2:       fmt = "    %-8s  %-8s  %12g   %-8s  %12g\n"; break; /* Field 2-6 */
301                         case l_data_mst:        fmt = "    %-8s            %12g\n"; break; /* Field 3-4 */
302                         case l_marker:          fmt = "    M%-7d  'MARKER'                 '%s'\n"; break; /* Field 2,3,5 */
303                         default: assert(0);
304                 }
305         } else {
306                 switch (line_type) {
307                         case l_raw:                     fmt = "%s\n"; break;
308                         case l_ind_name:        fmt = "NAME %s\n"; break;
309                         case l_ind_objs:        fmt = "OBJSENSE\n"; break;
310                         case l_ind_rows:        fmt = "ROWS\n"; break;
311                         case l_ind_cols:        fmt = "COLUMNS\n"; break;
312                         case l_ind_rhs:         fmt = "RHS\n"; break;
313                         case l_ind_end:         fmt = "ENDATA\n"; break;
314                         case l_data_row:        fmt = " %s\t%s\n"; break;
315                         case l_data_col1:       fmt = " %s\t%s\t%g\n"; break;
316                         case l_data_col2:       fmt = " %s\t%s\t%g\t%s\t%g\n"; break;
317                         case l_data_mst:        fmt = " %s\t%g\n"; break;
318                         case l_marker:          fmt = " M%d\t'MARKER'\t'%s'\n"; break;
319                         default: assert(0);
320                 }
321         }
322
323         vfprintf(out, fmt, args);
324         va_end(args);
325 }
326
327 static INLINE int mps_insert_markers(FILE *out, style_t style, var_t curr, var_t last, int marker_nr) {
328         assert(style == s_mps_fixed || style == s_mps_free);
329         if (last != curr) {
330                 /* print end-marker for last */
331                 if (last == binary)
332                         mps_write_line(out, style, l_marker, marker_nr++, "INTEND");
333
334                 /* print begin-marker for curr */
335                 if (curr == binary)
336                         mps_write_line(out, style, l_marker, marker_nr++, "INTORG");
337         }
338         return marker_nr;
339 }
340
341 static void lpp_write_cmd(lpp_t *lpp) {
342         FILE *out = ffopen(lpp->name, "cmd", "wt");
343         fprintf(out, "set logfile %s.sol\n", lpp->name);
344         fprintf(out, "set mip strategy mipstart 1\n");
345         fprintf(out, "set mip emphasis 3\n"); /* moving best bound */
346         fprintf(out, "set mip strategy variableselect 3\n"); /* strong branching */
347 //      fprintf(out, "set mip strategy branch 1\n"); /* branch up first */
348         fprintf(out, "read %s.mps\n", lpp->name);
349         fprintf(out, "read %s.mst\n", lpp->name);
350         fprintf(out, "optimize\n");
351         fprintf(out, "display solution variables -\n");
352         fprintf(out, "quit\n");
353         fclose(out);
354 }
355
356 static void lpp_write_mps(lpp_t *lpp, style_t style) {
357         FILE *out;
358         int i, count, marker_nr = 0;
359         const name_t *curr;
360         const matrix_elem_t *elem, *before;
361         var_t last_type;
362         assert(style == s_mps_fixed || style == s_mps_free);
363
364         out = ffopen(lpp->name, "mps", "wt");
365         /* NAME */
366         mps_write_line(out, style, l_ind_name, lpp->name);
367
368         /* OBJSENSE */
369         mps_write_line(out, style, l_ind_objs);
370         if (lpp->opt_type == maximize)
371                 mps_write_line(out, style, l_raw, " MAX");
372
373         /* ROWS */
374         mps_write_line(out, style, l_ind_rows);
375         for(i=0; i<lpp->cst_next; ++i) {
376                 curr = lpp->csts[i];
377                 mps_write_line(out, style, l_data_row, mps_cst_encoding[curr->type.cst_type], curr->name);
378         }
379
380         /* COLUMNS */
381         mps_write_line(out, style, l_ind_cols);
382         last_type = invalid;
383         for(i=1; i<lpp->var_next; ++i) { /* column 0 is rhs */
384                 curr = lpp->vars[i];
385
386                 /* markers */
387                 marker_nr = mps_insert_markers(out, style, curr->type.var_type, last_type, marker_nr);
388                 last_type = curr->type.var_type;
389
390                 /* participation in constraints */
391                 count = 0;
392                 matrix_foreach_in_col(lpp->m, curr->nr, elem) {
393                         if (count == 0) {
394                                 before = elem;
395                                 count = 1;
396                         } else {
397                                 mps_write_line(out, style, l_data_col2, curr->name, lpp->csts[before->row]->name, (double)before->val, lpp->csts[elem->row]->name, (double)elem->val);
398                                 count = 0;
399                         }
400                 }
401                 if (count == 1)
402                         mps_write_line(out, style, l_data_col1, curr->name, lpp->csts[before->row]->name, (double)before->val);
403         }
404         mps_insert_markers(out, style, invalid, last_type, marker_nr); /* potential end-marker */
405
406         /* RHS */
407         mps_write_line(out, style, l_ind_rhs);
408         count = 0;
409         matrix_foreach_in_col(lpp->m, 0, elem) {
410                 if (count == 0) {
411                         before = elem;
412                         count = 1;
413                 } else {
414                         mps_write_line(out, style, l_data_col2, "rhs", lpp->csts[before->row]->name, (double)before->val, lpp->csts[elem->row]->name, (double)elem->val);
415                         count = 0;
416                 }
417         }
418         if (count == 1)
419                 mps_write_line(out, style, l_data_col1, "rhs", lpp->csts[before->row]->name, (double)before->val);
420
421         /* ENDATA */
422         mps_write_line(out, style, l_ind_end);
423         fclose(out);
424 }
425
426 static void lpp_write_mst(lpp_t *lpp, style_t style) {
427         int i;
428         FILE *out = ffopen(lpp->name, "mst", "wt");
429         mps_write_line(out, style, l_ind_name, "");
430         for (i=0; i<lpp->var_next; ++i) {
431                 const name_t *var = lpp->vars[i];
432                 if (var->value_kind == start)
433                         mps_write_line(out, style, l_data_mst, var->name, (double)var->value);
434         }
435         mps_write_line(out, style, l_ind_end);
436         fclose(out);
437 }
438
439 static void lpp_write_exp(lpp_t *lpp) {
440         FILE *pwfile, *out;
441         char passwd[128];
442
443         pwfile = fopen(SSH_PASSWD_FILE, "rt");
444         fgets(passwd, sizeof(passwd), pwfile);
445         fclose(pwfile);
446
447         out = ffopen(EXPECT_FILENAME, "exp", "wt");
448         fprintf(out, "#! /usr/bin/expect\n");
449         fprintf(out, "spawn scp %s.mps %s.mst %s.cmd %s:\n", lpp->name, lpp->name, lpp->name, SSH_USER_HOST); /* copy problem files */
450         fprintf(out, "expect \"word:\"\nsend \"%s\\n\"\ninteract\n", passwd);
451
452         fprintf(out, "spawn ssh %s \"./cplex90 < %s.cmd\"\n", SSH_USER_HOST, lpp->name); /* solve */
453         fprintf(out, "expect \"word:\"\nsend \"%s\\n\"\ninteract\n", passwd);
454
455         fprintf(out, "spawn scp %s:%s.sol .\n", SSH_USER_HOST, lpp->name); /*copy back solution */
456         fprintf(out, "expect \"word:\"\nsend \"%s\\n\"\ninteract\n", passwd);
457
458         fprintf(out, "spawn ssh %s ./dell\n", SSH_USER_HOST); /* clean files on server */
459         fprintf(out, "expect \"word:\"\nsend \"%s\\n\"\ninteract\n", passwd);
460         fclose(out);
461 }
462
463 /**
464  * Sets the colors of irns according to the values of variables found in the
465  * output file of the solver.
466  */
467 static void lpp_read_solution(lpp_t *lpp) {
468         FILE *in;
469         double sol_time;
470         unsigned iter;
471         int vars_section = 0;
472         char var_name[128];
473         double var_value;
474
475         if (!(in = ffopen(lpp->name, "sol", "rt"))) {
476                 lpp->sol_state = no_solution_file;
477                 return;
478         }
479         while (!feof(in)) {
480                 char buf[1024];
481                 fgets(buf, sizeof(buf), in);
482
483                 /* error and solution state */
484                 if (!strncmp(buf, "CPLEX Error", 11))
485                         lpp->error = xstrdup(buf);
486                 else if (!strncmp(buf, "Warning:", 8))
487                         lpp->error = xstrdup(buf);
488                 else if (!strncmp(buf, "Integer optimal solution:", 25))
489                         lpp->sol_state = optimal;
490                 else if (!strcmp(buf, "No integer feasible solution exists."))
491                         lpp->sol_state = infeasible;
492                 else if (!strcmp(buf, "Error termination, integer feasible:"))
493                         lpp->sol_state = feasible;
494                 /* stats */
495                 else if (sscanf(buf, "Solution time = %lg sec. Iterations = %u", &sol_time, &iter) == 2) {
496                         lpp->sol_time = sol_time;
497                         lpp->iterations = iter;
498                 }
499                 /* variable values */
500                 else if(!strcmp(buf, "Variable Name           Solution Value")) {
501                         int i;
502                         vars_section = 1;
503                         for(i=0; i<lpp->var_next; ++i) {
504                                 name_t *var = lpp->vars[i];
505                                 var->value = 0;
506                                 var->value_kind = solution;
507                         }
508                 }
509                 else if(!strncmp(buf, "All other var", 13))
510                         vars_section = 0;
511                 else if (vars_section) {
512                         if (sscanf(buf, "%s %lg", var_name, &var_value) == 2)
513                                 lpp->vars[var_nr(lpp, var_name)]->value = var_value;
514                         else
515                                 assert(0 && "There should be variables to read in!");
516                 }
517         }
518         fclose(in);
519         if (lpp->error) {
520                 printf("\n%s\n", lpp->error);
521                 assert(0);
522         }
523 }
524
525 #ifdef DELETE_FILES
526 static void lpp_delete_files(lpp_t *lpp) {
527         char buf[1024];
528         int end = snprintf(buf, sizeof(buf), "%s", lpp->name);
529
530         snprintf(buf+end, sizeof(buf)-end, ".mps");
531         remove(buf);
532         snprintf(buf+end, sizeof(buf)-end, ".mst");
533         remove(buf);
534         snprintf(buf+end, sizeof(buf)-end, ".cmd");
535         remove(buf);
536         snprintf(buf+end, sizeof(buf)-end, ".sol");
537         remove(buf);
538         remove(EXPECT_FILENAME ".exp");
539 }
540 #endif
541
542 int lpp_solve(lpp_t *lpp, int use_start_values) {
543         lpp_write_cmd(lpp);
544         lpp_write_mps(lpp, s_mps_free);
545         if (use_start_values)
546                 lpp_write_mst(lpp, s_mps_free);
547         lpp_write_exp(lpp);
548
549         /* call the expect script */
550         chmod(EXPECT_FILENAME ".exp", 0700);
551         system(EXPECT_FILENAME ".exp");
552
553         lpp_read_solution(lpp);
554 #ifdef DELETE_FILES
555         lpp_delete_files(lpp);
556 #endif
557         return 0;
558 }
559
560 sol_state_t lpp_get_solution(lpp_t *lpp, double *values, int begin, int end) {
561         int i;
562         if (lpp->sol_state < 4)
563                 return lpp->sol_state;
564         /* here we are feasible or optimal */
565         for (i=0; i<end-begin+1; ++i)
566                 values[i] = lpp->vars[begin+i]->value;
567         return lpp->sol_state;
568 }