4 * Copyright: (c) Universitaet Karlsruhe
5 * Licence: This file protected by GPL - GNU GENERAL PUBLIC LICENSE.
19 #include <sys/types.h>
28 #include "sp_matrix.h"
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 */
36 typedef struct _name_t name_t;
37 typedef enum _value_kind_t {none=0, start, solution} value_kind_t;
39 #define DEBUG_LVL SET_LEVEL_1
40 static firm_dbg_module_t *dbg = NULL;
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;
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 */
60 /* Cst/Var to Nr mapping */
61 set *cst2nr; /**< Holds name_t's for constraints */
62 set *var2nr; /**< Holds name_t's for variables */
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 */
72 sol_state_t sol_state;
76 unsigned next_name_number;
79 #define HASH_NAME_T(n) HASH_STR((n)->name, strlen((n)->name))
81 static INLINE FILE *ffopen(const char *base, const char *ext, const char *mode) {
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);
93 static int cmp_name_t(const void *x, const void *y, size_t size) {
96 return strcmp(n->name, m->name);
99 #define INITIAL_SIZE 64
101 lpp_t *new_lpp(const char *name, opt_t opt_type) {
104 dbg = firm_dbg_register("ir.be.copyoptilp");
105 firm_dbg_set_mask(dbg, DEBUG_LVL);
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);
122 void free_lpp(lpp_t *lpp) {
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);
139 static INLINE int name2nr(set *where, char *name) {
142 found = set_find(where, &find, sizeof(find), HASH_NAME_T(&find));
143 return (found ? found->nr : -1);
146 #define cst_nr(lpp, name) name2nr(lpp->cst2nr, name)
147 #define var_nr(lpp, name) name2nr(lpp->var2nr, name)
149 static INLINE char *get_next_name(lpp_t *lpp) {
150 char *res = xmalloc(12);
151 snprintf(res, 12, "_%d", lpp->next_name_number++);
155 int lpp_add_cst(lpp_t *lpp, const char *cst_name, cst_t cst_type, double rhs) {
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;
161 n.name = xstrdup(cst_name);
163 n.name = get_next_name(lpp);
165 inner = set_insert(lpp->cst2nr, &n, sizeof(n), HASH_NAME_T(&n));
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) {
173 lpp->csts = xrealloc(lpp->csts, lpp->cst_size * sizeof(*lpp->csts));
175 lpp->csts[lpp->cst_next] = inner;
177 matrix_set(lpp->m, inner->nr, 0, rhs);
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);
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;
193 int lpp_add_var(lpp_t *lpp, const char *var_name, var_t var_type, double obj) {
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;
200 n.name = xstrdup(var_name);
202 n.name = get_next_name(lpp);
204 inner = set_insert(lpp->var2nr, &n, sizeof(n), HASH_NAME_T(&n));
207 if (inner->nr == -1) {
208 inner->nr = lpp->var_next;
209 inner->value_kind = 0;
211 inner->type.var_type = var_type;
212 if (lpp->var_next == lpp->var_size) {
214 lpp->vars = xrealloc(lpp->vars, lpp->var_size * sizeof(*lpp->vars));
216 lpp->vars[lpp->var_next] = inner;
218 matrix_set(lpp->m, 0, inner->nr, obj);
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);
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;
234 int lpp_set_factor(lpp_t *lpp, char *cst_name, char *var_name, double value) {
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)
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);
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)
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);
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;
265 /******************************************************************************
267 ******************************************************************************/
270 * Fixed-column mps where spaces are allowed in identifiers :-0
271 * Free mps where whitespace is a seperator :-)
274 typedef enum _style_t {s_mps_fixed, s_mps_free} style_t;
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;
281 static INLINE void mps_write_line(FILE *out, style_t style, mps_line_t line_type, ...) {
285 assert(style == s_mps_fixed || style == s_mps_free);
286 va_start(args, line_type);
288 if (style == s_mps_fixed) {
289 /* white spaces are important! */
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 */
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;
323 vfprintf(out, fmt, args);
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);
330 /* print end-marker for last */
332 mps_write_line(out, style, l_marker, marker_nr++, "INTEND");
334 /* print begin-marker for curr */
336 mps_write_line(out, style, l_marker, marker_nr++, "INTORG");
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");
356 static void lpp_write_mps(lpp_t *lpp, style_t style) {
358 int i, count, marker_nr = 0;
360 const matrix_elem_t *elem, *before;
362 assert(style == s_mps_fixed || style == s_mps_free);
364 out = ffopen(lpp->name, "mps", "wt");
366 mps_write_line(out, style, l_ind_name, lpp->name);
369 mps_write_line(out, style, l_ind_objs);
370 if (lpp->opt_type == maximize)
371 mps_write_line(out, style, l_raw, " MAX");
374 mps_write_line(out, style, l_ind_rows);
375 for(i=0; i<lpp->cst_next; ++i) {
377 mps_write_line(out, style, l_data_row, mps_cst_encoding[curr->type.cst_type], curr->name);
381 mps_write_line(out, style, l_ind_cols);
383 for(i=1; i<lpp->var_next; ++i) { /* column 0 is rhs */
387 marker_nr = mps_insert_markers(out, style, curr->type.var_type, last_type, marker_nr);
388 last_type = curr->type.var_type;
390 /* participation in constraints */
392 matrix_foreach_in_col(lpp->m, curr->nr, elem) {
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);
402 mps_write_line(out, style, l_data_col1, curr->name, lpp->csts[before->row]->name, (double)before->val);
404 mps_insert_markers(out, style, invalid, last_type, marker_nr); /* potential end-marker */
407 mps_write_line(out, style, l_ind_rhs);
409 matrix_foreach_in_col(lpp->m, 0, elem) {
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);
419 mps_write_line(out, style, l_data_col1, "rhs", lpp->csts[before->row]->name, (double)before->val);
422 mps_write_line(out, style, l_ind_end);
426 static void lpp_write_mst(lpp_t *lpp, style_t style) {
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);
435 mps_write_line(out, style, l_ind_end);
439 static void lpp_write_exp(lpp_t *lpp) {
443 pwfile = fopen(SSH_PASSWD_FILE, "rt");
444 fgets(passwd, sizeof(passwd), pwfile);
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);
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);
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);
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);
464 * Sets the colors of irns according to the values of variables found in the
465 * output file of the solver.
467 static void lpp_read_solution(lpp_t *lpp) {
471 int vars_section = 0;
475 if (!(in = ffopen(lpp->name, "sol", "rt"))) {
476 lpp->sol_state = no_solution_file;
481 fgets(buf, sizeof(buf), in);
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;
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;
499 /* variable values */
500 else if(!strcmp(buf, "Variable Name Solution Value")) {
503 for(i=0; i<lpp->var_next; ++i) {
504 name_t *var = lpp->vars[i];
506 var->value_kind = solution;
509 else if(!strncmp(buf, "All other var", 13))
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;
515 assert(0 && "There should be variables to read in!");
520 printf("\n%s\n", lpp->error);
526 static void lpp_delete_files(lpp_t *lpp) {
528 int end = snprintf(buf, sizeof(buf), "%s", lpp->name);
530 snprintf(buf+end, sizeof(buf)-end, ".mps");
532 snprintf(buf+end, sizeof(buf)-end, ".mst");
534 snprintf(buf+end, sizeof(buf)-end, ".cmd");
536 snprintf(buf+end, sizeof(buf)-end, ".sol");
538 remove(EXPECT_FILENAME ".exp");
542 int lpp_solve(lpp_t *lpp, int use_start_values) {
544 lpp_write_mps(lpp, s_mps_free);
545 if (use_start_values)
546 lpp_write_mst(lpp, s_mps_free);
549 /* call the expect script */
550 chmod(EXPECT_FILENAME ".exp", 0700);
551 system(EXPECT_FILENAME ".exp");
553 lpp_read_solution(lpp);
555 lpp_delete_files(lpp);
560 sol_state_t lpp_get_solution(lpp_t *lpp, double *values, int begin, int end) {
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;