1
2 /* -------------------------------------------------------------------------
3 Presolve routines for lp_solve v5.0+
4 -------------------------------------------------------------------------
5 Author: Kjell Eikland
6 Contact: kjell.eikland@broadpark.no
7 License terms: LGPL.
8
9 Requires: lp_lib.h, lp_presolve, lp_crash.h, lp_scale.h, lp_report.h
10
11 Release notes:
12 v1.0.0 1 January 2003 Initial crude version used with lp_solve v4.
13 v5.0.0 1 January 2004 Significantly expanded and repackaged
14 presolve routines for lp_solve v5 release.
15 v5.0.1 1 April 2004 Added reference to new crash module
16 v5.1.0 20 August 2004 Reworked infeasibility detection.
17 Added encapsulation of presolve undo logic.
18 v5.1.1 10 September 2004 Added variable bound tightening based on
19 full-constraint information, as well as
20 variable fixing by duality.
21 v5.2.0 1 January 2005 Fixes to bound fixing handling.
22 Added fast batch compression after presolve.
23 Restructured calls by adding presolve wrapper.
24 Major optimization of identification logic
25 along with bug fixes.
26 Enabled storage of eliminated matrix data.
27 Added function to report on constraint classes.
28 v5.5.0 1 June 2005 Added implied slack presolve, restructured
29 looping logic to be more modular, and made
30 active row/column selection logic faster.
31 v5.5.1 18 June 2005 Finished sparsity-enhancing logic and added
32 initial version of column aggregation code.
33 ------------------------------------------------------------------------- */
34
35 #include <string.h>
36 #include "commonlib.h"
37 #include "lp_lib.h"
38 #include "lp_presolve.h"
39 #include "lp_crash.h"
40 #include "lp_scale.h"
41 #include "lp_report.h"
42
43 #ifdef FORTIFY
44 # include "lp_fortify.h"
45 #endif
46
47
48 #define presolve_setstatus(one, two) presolve_setstatusex(one, two, __LINE__, __FILE__)
presolve_setstatusex(presolverec * psdata,int status,int lineno,char * filename)49 STATIC int presolve_setstatusex(presolverec *psdata, int status, int lineno, char *filename)
50 {
51 if((status == INFEASIBLE) || (status == UNBOUNDED)) {
52 report(psdata->lp,
53 #ifdef Paranoia
54 NORMAL,
55 #else
56 DETAILED,
57 #endif
58 "presolve_setstatus: Status set to '%s' on code line %d, file '%s'\n",
59 (status == INFEASIBLE ? "INFEASIBLE" : "UNBOUNDED"), lineno, (filename == NULL ? "Unknown" : filename));
60 }
61 return( status );
62 }
63
presolve_statuscheck(presolverec * psdata,int * status)64 STATIC MYBOOL presolve_statuscheck(presolverec *psdata, int *status)
65 {
66 if(*status == RUNNING) {
67 lprec *lp = psdata->lp;
68 if(!mat_validate(lp->matA))
69 *status = MATRIXERROR;
70 else if(userabort(lp, -1))
71 *status = lp->spx_status;
72 }
73 return( (MYBOOL) (*status == RUNNING) );
74 }
75
presolve_createUndo(lprec * lp)76 STATIC MYBOOL presolve_createUndo(lprec *lp)
77 {
78 if(lp->presolve_undo != NULL)
79 presolve_freeUndo(lp);
80 lp->presolve_undo = (presolveundorec *) calloc(1, sizeof(presolveundorec));
81 lp->presolve_undo->lp = lp;
82 if(lp->presolve_undo == NULL)
83 return( FALSE );
84 return( TRUE );
85 }
inc_presolve_space(lprec * lp,int delta,MYBOOL isrows)86 STATIC MYBOOL inc_presolve_space(lprec *lp, int delta, MYBOOL isrows)
87 {
88 int i, ii,
89 oldrowcolalloc, rowcolsum, oldrowalloc, oldcolalloc;
90 presolveundorec *psundo = lp->presolve_undo;
91
92 if(psundo == NULL) {
93 presolve_createUndo(lp);
94 psundo = lp->presolve_undo;
95 }
96
97 /* Set constants */
98 oldrowalloc = lp->rows_alloc-delta;
99 oldcolalloc = lp->columns_alloc-delta;
100 oldrowcolalloc = lp->sum_alloc-delta;
101 rowcolsum = lp->sum_alloc + 1;
102
103 /* Reallocate lp memory */
104 if(isrows)
105 allocREAL(lp, &psundo->fixed_rhs, lp->rows_alloc+1, AUTOMATIC);
106 else
107 allocREAL(lp, &psundo->fixed_obj, lp->columns_alloc+1, AUTOMATIC);
108 allocINT(lp, &psundo->var_to_orig, rowcolsum, AUTOMATIC);
109 allocINT(lp, &psundo->orig_to_var, rowcolsum, AUTOMATIC);
110
111 /* Fill in default values, where appropriate */
112 if(isrows)
113 ii = oldrowalloc+1;
114 else
115 ii = oldcolalloc+1;
116 for(i = oldrowcolalloc+1; i < rowcolsum; i++, ii++) {
117 psundo->var_to_orig[i] = 0;
118 psundo->orig_to_var[i] = 0;
119 if(isrows)
120 psundo->fixed_rhs[ii] = 0;
121 else
122 psundo->fixed_obj[ii] = 0;
123 }
124
125 return(TRUE);
126 }
presolve_setOrig(lprec * lp,int orig_rows,int orig_cols)127 STATIC MYBOOL presolve_setOrig(lprec *lp, int orig_rows, int orig_cols)
128 {
129 presolveundorec *psundo = lp->presolve_undo;
130
131 if(psundo == NULL)
132 return( FALSE );
133 psundo->orig_rows = orig_rows;
134 psundo->orig_columns = orig_cols;
135 psundo->orig_sum = orig_rows + orig_cols;
136 if(lp->wasPresolved)
137 presolve_fillUndo(lp, orig_rows, orig_cols, FALSE);
138 return( TRUE );
139 }
presolve_fillUndo(lprec * lp,int orig_rows,int orig_cols,MYBOOL setOrig)140 STATIC MYBOOL presolve_fillUndo(lprec *lp, int orig_rows, int orig_cols, MYBOOL setOrig)
141 {
142 int i;
143 presolveundorec *psundo = lp->presolve_undo;
144
145 for(i = 0; i <= orig_rows; i++) {
146 psundo->var_to_orig[i] = i;
147 psundo->orig_to_var[i] = i;
148 psundo->fixed_rhs[i] = 0;
149 }
150 for(i = 1; i <= orig_cols; i++) {
151 psundo->var_to_orig[orig_rows + i] = i;
152 psundo->orig_to_var[orig_rows + i] = i;
153 psundo->fixed_obj[i] = 0;
154 }
155 if(setOrig)
156 presolve_setOrig(lp, orig_rows, orig_cols);
157
158 return( TRUE );
159 }
presolve_rebuildUndo(lprec * lp,MYBOOL isprimal)160 STATIC MYBOOL presolve_rebuildUndo(lprec *lp, MYBOOL isprimal)
161 {
162 int ik, ie, ix, j, k, *colnrDep;
163 REAL hold, *value, *solution, *slacks;
164 presolveundorec *psdata = lp->presolve_undo;
165 MATrec *mat = NULL;
166
167 /* Point to and initialize undo structure at first call */
168 if(isprimal) {
169 if(psdata->primalundo != NULL)
170 mat = psdata->primalundo->tracker;
171 solution = lp->full_solution + lp->presolve_undo->orig_rows;
172 slacks = lp->full_solution;
173 }
174 else {
175 if(psdata->dualundo != NULL)
176 mat = psdata->dualundo->tracker;
177 solution = lp->full_duals;
178 slacks = lp->full_duals + lp->presolve_undo->orig_rows;
179 }
180 if(mat == NULL)
181 return( FALSE );
182
183 /* Loop backward over the undo chain */
184 for(j = mat->col_tag[0]; j > 0; j--) {
185 ix = mat->col_tag[j];
186 ik = mat->col_end[j-1];
187 ie = mat->col_end[j];
188 colnrDep = &COL_MAT_ROWNR(ik);
189 value = &COL_MAT_VALUE(ik);
190 hold = 0;
191 k = 0;
192 for(; ik < ie; ik++, colnrDep += matRowColStep, value += matValueStep) {
193
194 /* Constant term */
195 if(*colnrDep == 0)
196 hold += *value;
197
198 /* Special case with dependence on a slack variable */
199 else if(isprimal && (*colnrDep > lp->presolve_undo->orig_columns)) {
200 k = (*colnrDep) - lp->presolve_undo->orig_columns;
201 hold -= (*value) * slacks[k];
202 slacks[k] = 0;
203 }
204 else if(!isprimal && (*colnrDep > lp->presolve_undo->orig_rows)) {
205 k = (*colnrDep) - lp->presolve_undo->orig_rows;
206 hold -= (*value) * slacks[k];
207 slacks[k] = 0;
208 }
209
210 /* Dependence on other user variable */
211 else
212 hold -= (*value) * solution[*colnrDep];
213
214 *value = 0;
215 }
216 if(fabs(hold) > lp->epsvalue)
217 solution[ix] = hold;
218 }
219
220 return( TRUE );
221 }
presolve_freeUndo(lprec * lp)222 STATIC MYBOOL presolve_freeUndo(lprec *lp)
223 {
224 presolveundorec *psundo = lp->presolve_undo;
225
226 if(psundo == NULL)
227 return( FALSE );
228 FREE(psundo->orig_to_var);
229 FREE(psundo->var_to_orig);
230 FREE(psundo->fixed_rhs);
231 FREE(psundo->fixed_obj);
232 if(psundo->deletedA != NULL)
233 freeUndoLadder(&(psundo->deletedA));
234 if(psundo->primalundo != NULL)
235 freeUndoLadder(&(psundo->primalundo));
236 if(psundo->dualundo != NULL)
237 freeUndoLadder(&(psundo->dualundo));
238 FREE(lp->presolve_undo);
239 return( TRUE );
240 }
241
presolve_storeDualUndo(presolverec * psdata,int rownr,int colnr)242 STATIC void presolve_storeDualUndo(presolverec *psdata, int rownr, int colnr)
243 {
244 lprec *lp = psdata->lp;
245 MYBOOL firstdone = FALSE;
246 int ix, iix, item;
247 REAL Aij = get_mat(lp, rownr, colnr);
248 MATrec *mat = lp->matA;
249
250 if(presolve_collength(psdata, colnr) == 0)
251 return;
252
253 /* Add undo information for the dual of the deleted constraint */
254 item = 0;
255 for(ix = presolve_nextrow(psdata, colnr, &item); ix >= 0;
256 ix = presolve_nextrow(psdata, colnr, &item)) {
257 iix = COL_MAT_ROWNR(ix);
258 if(iix == rownr)
259 continue;
260 if(!firstdone)
261 firstdone = addUndoPresolve(lp, FALSE, rownr, get_mat(lp, 0, colnr)/Aij,
262 get_mat_byindex(lp, ix, FALSE, TRUE)/Aij, iix);
263 else
264 appendUndoPresolve(lp, FALSE, get_mat_byindex(lp, ix, FALSE, TRUE)/Aij, iix);
265 }
266 }
267
268 /* ----------------------------------------------------------------------------- */
269 /* Presolve debugging routines */
270 /* ----------------------------------------------------------------------------- */
presolve_SOScheck(presolverec * psdata)271 STATIC MYBOOL presolve_SOScheck(presolverec *psdata)
272 {
273 MYBOOL status = TRUE;
274 lprec *lp = psdata->lp;
275 int *list, i, j, n, k, nk, colnr, nSOS = SOS_count(lp), nerr = 0;
276 SOSrec *SOS;
277
278 if(nSOS == 0)
279 return( status );
280
281 /* For each SOS and each member check validity */
282 for(i = 1; i<= nSOS; i++) {
283 SOS = lp->SOS->sos_list[i-1];
284 list = SOS->members;
285 n = list[0];
286 for(j = 1; j<= n; j++) {
287 colnr = list[j];
288 /* Check valid range */
289 if((colnr < 1) || (colnr > lp->columns)) {
290 nerr++;
291 report(lp, IMPORTANT, "presolve_SOScheck: A - Column index %d is outside of valid range\n",
292 colnr);
293 }
294 /* Check for deletion */
295 if(!isActiveLink(psdata->cols->varmap, colnr)) {
296 nerr++;
297 report(lp, IMPORTANT, "presolve_SOScheck: B - Column index %d has been marked for deletion\n",
298 colnr);
299 }
300 /* Check if sorted member array is Ok */
301 if(SOS_member_index(lp->SOS, i, colnr) != j) {
302 nerr++;
303 report(lp, IMPORTANT, "presolve_SOScheck: C - Column index %d not found in fast search array\n",
304 colnr);
305 }
306 /* Check for variable membership in this SOS record of the sparse storage */
307 k = lp->SOS->memberpos[colnr-1];
308 nk = lp->SOS->memberpos[colnr];
309 while((k < nk) && (lp->SOS->membership[k] != i))
310 k++;
311 if(k >= nk) {
312 nerr++;
313 report(lp, IMPORTANT, "presolve_SOScheck: D - Column index %d was not found in sparse array\n",
314 colnr);
315 }
316 }
317 }
318
319 /* Check that all members in the sparse array can be validated as SOS members */
320 for(colnr = 1; colnr <= lp->columns; colnr++) {
321 k = lp->SOS->memberpos[colnr-1];
322 nk = lp->SOS->memberpos[colnr];
323 for(; k < nk; k++) {
324 if(!SOS_is_member(lp->SOS, lp->SOS->membership[k], colnr)) {
325 nerr++;
326 report(lp, IMPORTANT, "presolve_SOScheck: E - Sparse array did not indicate column index %d as member of SOS %d\n",
327 colnr, lp->SOS->membership[k]);
328 }
329 }
330 }
331 status = (MYBOOL) (nerr == 0);
332 if(!status)
333 report(lp, IMPORTANT, "presolve_SOScheck: There were %d errors\n",
334 nerr);
335
336
337 return( status );
338 }
339
340 /* ----------------------------------------------------------------------------- */
341 /* Presolve routines for tightening the model */
342 /* ----------------------------------------------------------------------------- */
343
presolve_roundrhs(lprec * lp,REAL value,MYBOOL isGE)344 INLINE REAL presolve_roundrhs(lprec *lp, REAL value, MYBOOL isGE)
345 {
346 #ifdef DoPresolveRounding
347 REAL eps = PRESOLVE_EPSVALUE*1000,
348 /* REAL eps = PRESOLVE_EPSVALUE*pow(10.0,MAX(0,log10(1+fabs(value)))), */
349 testout = my_precision(value, eps);
350 #if 1
351 if(my_chsign(isGE, value-testout) < 0)
352 value = testout;
353 #elif 0
354 if(my_chsign(isGE, value-testout) < 0)
355 value = testout;
356 else if(value != testout)
357 value += my_chsign(isGE, (value-testout)/2);
358 /* value = testout + my_chsign(isGE, (value-testout)/2); */
359 #else
360 if(testout != value)
361 value += my_chsign(isGE, eps*1000); /* BASE OPTION */
362 #endif
363
364 #endif
365 return( value );
366 }
367
presolve_roundval(lprec * lp,REAL value)368 INLINE REAL presolve_roundval(lprec *lp, REAL value)
369 {
370 #ifdef DoPresolveRounding
371 /* value = my_precision(value, PRESOLVE_EPSVALUE*MAX(1,log10(1+fabs(value)))); */
372 value = my_precision(value, PRESOLVE_EPSVALUE); /* BASE OPTION */
373 #endif
374 return( value );
375 }
376
presolve_mustupdate(lprec * lp,int colnr)377 INLINE MYBOOL presolve_mustupdate(lprec *lp, int colnr)
378 {
379 #if 0
380 return( my_infinite(lp, get_lowbo(lp, colnr)) ||
381 my_infinite(lp, get_upbo(lp, colnr)) );
382 #else
383 return( my_infinite(lp, lp->orig_lowbo[lp->rows+colnr]) ||
384 my_infinite(lp, lp->orig_upbo[lp->rows+colnr]) );
385 #endif
386 }
387
presolve_sumplumin(lprec * lp,int item,psrec * ps,MYBOOL doUpper)388 INLINE REAL presolve_sumplumin(lprec *lp, int item, psrec *ps, MYBOOL doUpper)
389 {
390 REAL *plu = (doUpper ? ps->pluupper : ps->plulower),
391 *neg = (doUpper ? ps->negupper : ps->neglower);
392
393 if(fabs(plu[item]) >= lp->infinite)
394 return( plu[item] );
395 else if(fabs(neg[item]) >= lp->infinite)
396 return( neg[item] );
397 else
398 return( plu[item]+neg[item] );
399 }
400
presolve_range(lprec * lp,int rownr,psrec * ps,REAL * loValue,REAL * hiValue)401 INLINE void presolve_range(lprec *lp, int rownr, psrec *ps, REAL *loValue, REAL *hiValue)
402 {
403 *loValue = presolve_sumplumin(lp, rownr, ps, FALSE);
404 *hiValue = presolve_sumplumin(lp, rownr, ps, TRUE);
405 }
406
presolve_rangeorig(lprec * lp,int rownr,psrec * ps,REAL * loValue,REAL * hiValue,REAL delta)407 STATIC void presolve_rangeorig(lprec *lp, int rownr, psrec *ps, REAL *loValue, REAL *hiValue, REAL delta)
408 {
409 delta = my_chsign(is_chsign(lp, rownr), lp->presolve_undo->fixed_rhs[rownr] + delta);
410 *loValue = presolve_sumplumin(lp, rownr, ps, FALSE) + delta;
411 *hiValue = presolve_sumplumin(lp, rownr, ps, TRUE) + delta;
412 }
413
presolve_rowfeasible(presolverec * psdata,int rownr,MYBOOL userowmap)414 STATIC MYBOOL presolve_rowfeasible(presolverec *psdata, int rownr, MYBOOL userowmap)
415 {
416 lprec *lp = psdata->lp;
417 MYBOOL status = TRUE;
418 int contype, origrownr = rownr;
419 REAL LHS, RHS, value;
420
421 /* Optionally loop across all active rows in the provided map (debugging) */
422 if(userowmap)
423 rownr = firstActiveLink(psdata->rows->varmap);
424
425 /* Now do once for ingoing rownr or loop across rowmap */
426 while((status == TRUE) && (rownr != 0)) {
427
428 /* Check the lower bound */
429 value = presolve_sumplumin(lp, rownr, psdata->rows, TRUE);
430 LHS = get_rh_lower(lp, rownr);
431 if(value < LHS-lp->epssolution) {
432 contype = get_constr_type(lp, rownr);
433 report(lp, NORMAL, "presolve_rowfeasible: Lower bound infeasibility in %s row %s (%g << %g)\n",
434 get_str_constr_type(lp, contype), get_row_name(lp, rownr), value, LHS);
435 if(rownr != origrownr)
436 report(lp, NORMAL, " ... Input row base used for testing was %s\n",
437 get_row_name(lp, origrownr));
438 status = FALSE;
439 }
440
441 /* Check the upper bound */
442 value = presolve_sumplumin(lp, rownr, psdata->rows, FALSE);
443 RHS = get_rh_upper(lp, rownr);
444 if(value > RHS+lp->epssolution) {
445 contype = get_constr_type(lp, rownr);
446 report(lp, NORMAL, "presolve_rowfeasible: Upper bound infeasibility in %s row %s (%g >> %g)\n",
447 get_str_constr_type(lp, contype), get_row_name(lp, rownr), value, RHS);
448 status = FALSE;
449 }
450 if(userowmap)
451 rownr = nextActiveLink(psdata->rows->varmap, rownr);
452 else
453 rownr = 0;
454 }
455 return( status );
456 }
457
presolve_debugmap(presolverec * psdata,char * caption)458 STATIC MYBOOL presolve_debugmap(presolverec *psdata, char *caption)
459 {
460 lprec *lp = psdata->lp;
461 MATrec *mat = lp->matA;
462 int colnr, ix, ie, nx, jx, je, *cols, *rows, n;
463 int nz = mat->col_end[lp->columns] - 1;
464 MYBOOL status = FALSE;
465
466 for(colnr = 1; colnr <= lp->columns; colnr++) {
467 rows = psdata->cols->next[colnr];
468 if(!isActiveLink(psdata->cols->varmap, colnr)) {
469 if(rows != NULL) {
470 report(lp, SEVERE, "presolve_debugmap: Inactive column %d is non-empty\n",
471 colnr);
472 goto Done;
473 }
474 else
475 continue;
476 }
477 if(rows == NULL)
478 report(lp, SEVERE, "presolve_debugmap: Active column %d is empty\n",
479 colnr);
480 je = *rows;
481 rows++;
482 for(jx = 1; jx <= je; jx++, rows++) {
483 if((*rows < 0) || (*rows > nz)) {
484 report(lp, SEVERE, "presolve_debugmap: NZ index %d for column %d out of range (index %d<=%d)\n",
485 *rows, colnr, jx, je);
486 goto Done;
487 }
488 cols = psdata->rows->next[COL_MAT_ROWNR(*rows)];
489 ie = cols[0];
490 n = 0;
491 for(ix = 1; ix <= ie; ix++) {
492 nx = cols[ix];
493 if((nx < 0) || (nx > nz)) {
494 report(lp, SEVERE, "presolve_debugmap: NZ index %d for column %d to row %d out of range\n",
495 nx, colnr, jx);
496 goto Done;
497 }
498 }
499 }
500 }
501 status = TRUE;
502 Done:
503 if(!status && (caption != NULL))
504 report(lp, SEVERE, "...caller was '%s'\n", caption);
505 return( status );
506 }
507
presolve_validate(presolverec * psdata,MYBOOL forceupdate)508 STATIC MYBOOL presolve_validate(presolverec *psdata, MYBOOL forceupdate)
509 {
510 int i, ie, j, je, k, rownr, *items;
511 REAL upbound, lobound, value;
512 lprec *lp = psdata->lp;
513 MATrec *mat = lp->matA;
514 MYBOOL status = mat->row_end_valid && !forceupdate;
515
516 if(status)
517 return( status );
518 else if(!mat->row_end_valid)
519 status = mat_validate(mat);
520 else
521 status = forceupdate;
522 if(status) {
523
524 /* First update rows... */
525 for(i = 1; i <= lp->rows; i++) {
526
527 psdata->rows->plucount[i] = 0;
528 psdata->rows->negcount[i] = 0;
529 psdata->rows->pluneg[i] = 0;
530
531 if(!isActiveLink(psdata->rows->varmap, i)) {
532 FREE(psdata->rows->next[i]);
533 }
534 else {
535 /* Create next column pointers by row */
536 k = mat_rowlength(mat, i);
537 allocINT(lp, &(psdata->rows->next[i]), k+1, AUTOMATIC);
538 items = psdata->rows->next[i];
539 je = mat->row_end[i];
540 k = 0;
541 for(j = mat->row_end[i-1]; j < je; j++)
542 if(isActiveLink(psdata->cols->varmap, ROW_MAT_COLNR(j))) {
543 k++;
544 items[k] = j;
545 }
546 items[0] = k;
547 }
548 }
549
550 /* ...then update columns */
551 for(j = 1; j <= lp->columns; j++) {
552
553 psdata->cols->plucount[j] = 0;
554 psdata->cols->negcount[j] = 0;
555 psdata->cols->pluneg[j] = 0;
556
557 if(!isActiveLink(psdata->cols->varmap, j)) {
558 FREE(psdata->cols->next[j]);
559 }
560 else {
561 upbound = get_upbo(lp, j);
562 lobound = get_lowbo(lp, j);
563 if(is_semicont(lp, j) && (upbound > lobound)) {
564 if(lobound > 0)
565 lobound = 0;
566 else if(upbound < 0)
567 upbound = 0;
568 }
569
570 /* Create next row pointers by column */
571 k = mat_collength(mat, j);
572 allocINT(lp, &(psdata->cols->next[j]), k+1, AUTOMATIC);
573 items = psdata->cols->next[j];
574 ie = mat->col_end[j];
575 k = 0;
576 for(i = mat->col_end[j-1]; i < ie; i++) {
577 rownr = COL_MAT_ROWNR(i);
578 if(isActiveLink(psdata->rows->varmap, rownr)) {
579 k++;
580 items[k] = i;
581
582 /* Cumulate counts */
583 value = COL_MAT_VALUE(i);
584 if(my_chsign(is_chsign(lp, rownr), value) > 0) {
585 psdata->rows->plucount[rownr]++;
586 psdata->cols->plucount[j]++;
587 }
588 else {
589 psdata->rows->negcount[rownr]++;
590 psdata->cols->negcount[j]++;
591 }
592 if((lobound < 0) && (upbound >= 0)) {
593 psdata->rows->pluneg[rownr]++;
594 psdata->cols->pluneg[j]++;
595 }
596 }
597 }
598 items[0] = k;
599 }
600 }
601 #ifdef Paranoia
602 presolve_debugmap(psdata, "presolve_validate");
603 #endif
604 }
605 return( status );
606 }
607
presolve_rowtallies(presolverec * psdata,int rownr,int * plu,int * neg,int * pluneg)608 STATIC MYBOOL presolve_rowtallies(presolverec *psdata, int rownr, int *plu, int *neg, int *pluneg)
609 {
610 REAL value;
611 lprec *lp = psdata->lp;
612 MATrec *mat = lp->matA;
613 int ix, jx, ib = 0;
614 MYBOOL chsign = is_chsign(lp, rownr);
615
616 /* Initialize */
617 *plu = 0;
618 *neg = 0;
619 *pluneg = 0;
620
621 /* Loop over still active row members */
622 for(ix = presolve_nextcol(psdata, rownr, &ib); ix >= 0; ix = presolve_nextcol(psdata, rownr, &ib)) {
623
624 /* Get matrix column and value */
625 jx = ROW_MAT_COLNR(ix);
626 value = ROW_MAT_VALUE(ix);
627
628 /* Cumulate counts */
629 if(my_chsign(chsign, value) > 0)
630 (*plu)++;
631 else
632 (*neg)++;
633 if((get_lowbo(lp, jx) < 0) && (get_upbo(lp, jx) >= 0))
634 (*pluneg)++;
635 }
636 return( TRUE );
637 }
presolve_debugrowtallies(presolverec * psdata)638 STATIC MYBOOL presolve_debugrowtallies(presolverec *psdata)
639 {
640 lprec *lp = psdata->lp;
641 int i, plu, neg, pluneg, nerr = 0;
642
643 for(i = 1; i <= lp->rows; i++)
644 if(isActiveLink(psdata->rows->varmap, i) &&
645 presolve_rowtallies(psdata, i, &plu, &neg, &pluneg)) {
646 if((psdata->rows->plucount[i] != plu) ||
647 (psdata->rows->negcount[i] != neg) ||
648 (psdata->rows->pluneg[i] != pluneg)) {
649 nerr++;
650 report(lp, SEVERE, "presolve_debugrowtallies: Detected inconsistent count for row %d\n", i);
651 }
652 }
653 return( (MYBOOL) (nerr == 0) );
654 }
655
presolve_debugcheck(lprec * lp,LLrec * rowmap,LLrec * colmap)656 STATIC int presolve_debugcheck(lprec *lp, LLrec *rowmap, LLrec *colmap)
657 {
658 int i, j, errc = 0;
659
660 /* Validate constraint bounds */
661 for(i = 1; i < lp->rows; i++) {
662 if((rowmap != NULL) && !isActiveLink(rowmap, i))
663 continue;
664 /* Check if we have a negative range */
665 if(lp->orig_upbo[i] < 0) {
666 errc++;
667 report(lp, SEVERE, "presolve_debugcheck: Detected negative range %g for row %d\n",
668 lp->orig_upbo[i], i);
669 }
670 }
671 /* Validate variables */
672 for(j = 1; j < lp->columns; j++) {
673 if((colmap != NULL) && !isActiveLink(colmap, j))
674 continue;
675 i = lp->rows+j;
676 /* Check if we have infeasible bounds */
677 if(lp->orig_lowbo[i] > lp->orig_upbo[i]) {
678 errc++;
679 report(lp, SEVERE, "presolve_debugcheck: Detected UB < LB for column %d\n",
680 j);
681 }
682 }
683 /* Return total number of errors */
684 return( errc );
685 }
686
presolve_candeletevar(presolverec * psdata,int colnr)687 STATIC MYBOOL presolve_candeletevar(presolverec *psdata, int colnr)
688 {
689 lprec *lp = psdata->lp;
690 int usecount = SOS_memberships(lp->SOS, colnr);
691
692 return( (MYBOOL) ((lp->SOS == NULL) || (usecount == 0) ||
693 (/*is_presolve(lp, PRESOLVE_SOS) &&*/
694 (((lp->SOS->sos1_count == lp->SOS->sos_count)) ||
695 (usecount == SOS_is_member_of_type(lp->SOS, colnr, SOS1))))) );
696 }
697
presolve_rowlengthex(presolverec * psdata,int rownr)698 STATIC int presolve_rowlengthex(presolverec *psdata, int rownr)
699 {
700 int j1 = psdata->rows->plucount[rownr] + psdata->rows->negcount[rownr];
701 #ifdef Paranoia
702 int j2 = presolve_rowlength(psdata, rownr);
703
704 if(j1 != j2) {
705 report(psdata->lp, SEVERE, "presolve_rowlengthex: Expected row length %d, but found %d in row %s\n",
706 j2, j1, get_row_name(psdata->lp, rownr));
707 j1 = -j1;
708 }
709 #endif
710
711 return( j1 );
712 }
presolve_rowlengthdebug(presolverec * psdata)713 STATIC int presolve_rowlengthdebug(presolverec *psdata)
714 {
715 int rownr, n = 0;
716
717 for(rownr = firstActiveLink(psdata->rows->varmap); rownr != 0;
718 rownr = nextActiveLink(psdata->rows->varmap, rownr))
719 n += presolve_rowlengthex(psdata, rownr);
720 return( n );
721 }
722
presolve_nextrecord(psrec * ps,int recnr,int * previtem)723 INLINE int presolve_nextrecord(psrec *ps, int recnr, int *previtem)
724 {
725 int *nzlist = ps->next[recnr], nzcount = nzlist[0], status = -1;
726
727 /* Check if we simply wish the last active column */
728 if(previtem == NULL) {
729 if(nzlist != NULL)
730 status = nzlist[*nzlist];
731 return( status );
732 }
733
734 /* Step to next */
735 #ifdef Paranoia
736 else if((*previtem < 0) || (*previtem > nzcount))
737 return( status );
738 #endif
739 (*previtem)++;
740
741 /* Set the return values */
742 if(*previtem > nzcount)
743 (*previtem) = 0;
744 else
745 status = nzlist[*previtem];
746
747 return( status );
748 }
presolve_nextcol(presolverec * psdata,int rownr,int * previtem)749 INLINE int presolve_nextcol(presolverec *psdata, int rownr, int *previtem)
750 /* Find the first active (non-eliminated) nonzero column in rownr after prevcol */
751 {
752 return( presolve_nextrecord(psdata->rows, rownr, previtem) );
753 }
presolve_lastcol(presolverec * psdata,int rownr)754 INLINE int presolve_lastcol(presolverec *psdata, int rownr)
755 {
756 return( presolve_nextrecord(psdata->rows, rownr, NULL) );
757 }
presolve_nextrow(presolverec * psdata,int colnr,int * previtem)758 INLINE int presolve_nextrow(presolverec *psdata, int colnr, int *previtem)
759 /* Find the first active (non-eliminated) nonzero row in colnr after prevrow */
760 {
761 return( presolve_nextrecord(psdata->cols, colnr, previtem) );
762 }
presolve_lastrow(presolverec * psdata,int colnr)763 INLINE int presolve_lastrow(presolverec *psdata, int colnr)
764 {
765 return( presolve_nextrecord(psdata->cols, colnr, NULL) );
766 }
767
presolve_adjustrhs(presolverec * psdata,int rownr,REAL fixdelta,REAL epsvalue)768 INLINE void presolve_adjustrhs(presolverec *psdata, int rownr, REAL fixdelta, REAL epsvalue)
769 {
770 lprec *lp = psdata->lp;
771
772 lp->orig_rhs[rownr] -= fixdelta;
773 if(epsvalue > 0)
774 #if 1
775 my_roundzero(lp->orig_rhs[rownr], epsvalue);
776 #else
777 lp->orig_rhs[rownr] = presolve_roundrhs(lp, lp->orig_rhs[rownr], FALSE);
778 #endif
779 lp->presolve_undo->fixed_rhs[rownr] += fixdelta;
780 }
781
presolve_shrink(presolverec * psdata,int * nConRemove,int * nVarRemove)782 STATIC int presolve_shrink(presolverec *psdata, int *nConRemove, int *nVarRemove)
783 {
784 SOSgroup *SOS = psdata->lp->SOS;
785 int status = RUNNING, countR = 0, countC = 0,
786 i, ix, n, *list;
787 REAL fixValue;
788
789 /* Remove empty rows */
790 list = psdata->rows->empty;
791 if(list != NULL) {
792 n = list[0];
793 for(i = 1; i <= n; i++)
794 if(isActiveLink(psdata->rows->varmap, list[i])) {
795 presolve_rowremove(psdata, list[i], FALSE);
796 countR++;
797 }
798 if(nConRemove != NULL)
799 (*nConRemove) += countR;
800 list[0] = 0;
801 }
802
803 /* Fix and remove empty columns (unless they are in a SOS) */
804 list = psdata->cols->empty;
805 if(list != NULL) {
806 n = list[0];
807 for(i = 1; i <= n; i++) {
808 ix = list[i];
809 if(isActiveLink(psdata->cols->varmap, ix)) {
810 if(presolve_colfixdual(psdata, ix, &fixValue, &status)) {
811 if(!presolve_colfix(psdata, ix, fixValue, TRUE, nVarRemove)) {
812 status = presolve_setstatus(psdata, INFEASIBLE);
813 break;
814 }
815 presolve_colremove(psdata, ix, FALSE);
816 countC++;
817 }
818 else if(SOS_is_member(SOS, 0, ix))
819 report(psdata->lp, DETAILED, "presolve_shrink: Empty column %d is member of a SOS\n", ix);
820 }
821 }
822 list[0] = 0;
823 }
824
825 return( status );
826 }
827
presolve_rowremove(presolverec * psdata,int rownr,MYBOOL allowcoldelete)828 STATIC void presolve_rowremove(presolverec *psdata, int rownr, MYBOOL allowcoldelete)
829 {
830 lprec *lp = psdata->lp;
831 MATrec *mat = lp->matA;
832 int ix, ie, nx, jx, je, *cols, *rows, n, colnr;
833
834 #ifdef Paranoia
835 if((rownr < 1) || (rownr > lp->rows))
836 report(lp, SEVERE, "presolve_rowremove: Row %d out of range\n", rownr);
837 #endif
838
839 /* Remove this row for each column that is active in the row */
840 cols = psdata->rows->next[rownr];
841 ie = *cols;
842 cols++;
843 for(ix = 1; ix <= ie; ix++, cols++) {
844 n = 0;
845 colnr = ROW_MAT_COLNR(*cols);
846 rows = psdata->cols->next[colnr];
847 je = rows[0];
848 /* See if we can narrow the search window */
849 jx = je / 2;
850 if((jx > 5) && (rownr >= COL_MAT_ROWNR(rows[jx])))
851 n = jx-1;
852 else
853 jx = 1;
854 /* Do the compression loop */
855 for(; jx <= je; jx++) {
856 nx = rows[jx];
857 if(COL_MAT_ROWNR(nx) != rownr) {
858 n++;
859 rows[n] = nx;
860 }
861 }
862 rows[0] = n;
863
864 /* Make sure we delete columns that have become empty */
865 #if 1
866 if((n == 0) && allowcoldelete) {
867 int *list = psdata->cols->empty;
868 n = ++list[0];
869 list[n] = colnr;
870 }
871 #endif
872
873 }
874 FREE(psdata->rows->next[rownr]);
875
876 removeLink(psdata->rows->varmap, rownr);
877 switch(get_constr_type(lp, rownr)) {
878 case LE: removeLink(psdata->LTmap, rownr);
879 break;
880 case EQ: removeLink(psdata->EQmap, rownr);
881 break;
882 }
883 if(isActiveLink(psdata->INTmap, rownr))
884 removeLink(psdata->INTmap, rownr);
885 }
886
presolve_colremove(presolverec * psdata,int colnr,MYBOOL allowrowdelete)887 STATIC int presolve_colremove(presolverec *psdata, int colnr, MYBOOL allowrowdelete)
888 {
889 lprec *lp = psdata->lp;
890
891 #ifdef Paranoia
892 if((colnr < 1) || (colnr > lp->columns))
893 report(lp, SEVERE, "presolve_colremove: Column %d out of range\n", colnr);
894 if(!isActiveLink(psdata->cols->varmap, colnr) || !presolve_candeletevar(psdata, colnr))
895 colnr = -1;
896 else
897 #endif
898 {
899 MATrec *mat = lp->matA;
900 int ix, ie, nx, jx, je, *cols, *rows, n, rownr;
901
902 /* Remove this column for each row that is active in the column */
903 rows = psdata->cols->next[colnr];
904 je = *rows;
905 rows++;
906 for(jx = 1; jx <= je; jx++, rows++) {
907 n = 0;
908 rownr = COL_MAT_ROWNR(*rows);
909 cols = psdata->rows->next[rownr];
910 ie = cols[0];
911 /* See if we can narrow the search window */
912 ix = ie / 2;
913 if((ix > 5) && (colnr >= ROW_MAT_COLNR(cols[ix])))
914 n = ix-1;
915 else
916 ix = 1;
917 /* Do the compression loop */
918 for(; ix <= ie; ix++) {
919 nx = cols[ix];
920 if(ROW_MAT_COLNR(nx) != colnr) {
921 n++;
922 cols[n] = nx;
923 }
924 }
925 cols[0] = n;
926
927 /* Make sure we delete rows that become empty */
928 #if 1
929 if((n == 0) && allowrowdelete) {
930 int *list = psdata->rows->empty;
931 n = ++list[0];
932 list[n] = rownr;
933 }
934 #endif
935
936 }
937 FREE(psdata->cols->next[colnr]);
938
939 /* Update other counts */
940 if(SOS_is_member(lp->SOS, 0, colnr)) {
941 if(lp->sos_priority != NULL) {
942 lp->sos_vars--;
943 if(is_int(lp, colnr))
944 lp->sos_ints--;
945 }
946 SOS_member_delete(lp->SOS, 0, colnr);
947 clean_SOSgroup(lp->SOS, TRUE);
948 if(SOS_count(lp) == 0)
949 free_SOSgroup(&(lp->SOS));
950 }
951
952 /* Finally remove the column from the active column list */
953 colnr = removeLink(psdata->cols->varmap, colnr);
954 }
955 return( colnr );
956 }
957
presolve_redundantSOS(presolverec * psdata,int * nb,int * nSum)958 STATIC int presolve_redundantSOS(presolverec *psdata, int *nb, int *nSum)
959 {
960 lprec *lp = psdata->lp;
961 int i, ii, k, kk, j, nrows = lp->rows, *fixed = NULL,
962 iBoundTighten = 0, status = RUNNING;
963 SOSrec *SOS;
964
965 /* Is there anything to do? */
966 i = ii = SOS_count(lp);
967 if(ii == 0)
968 return( status );
969
970 /* Allocate working member list */
971 if(!allocINT(lp, &fixed, lp->columns+1, FALSE) )
972 return( lp->spx_status );
973
974 /* Check if we have SOS'es that are already satisfied or fixable/satisfiable */
975 while(i > 0) {
976 SOS = lp->SOS->sos_list[i-1];
977 kk = SOS->members[0];
978 fixed[0] = 0;
979 for(k = 1; k <= kk; k++) {
980 j = SOS->members[k];
981 if((get_lowbo(lp, j) > 0) && !is_semicont(lp, j)) {
982 fixed[++fixed[0]] = k;
983 /* Abort if we have identified SOS infeasibility */
984 if(fixed[0] > SOS->type) {
985 status = presolve_setstatus(psdata, INFEASIBLE);
986 goto Done;
987 }
988 }
989 }
990 /* If there were an exact number of non-zero SOS members, check their sequentiality */
991 if(fixed[0] == SOS->type) {
992 /* Check sequentiality of members with non-zero lower bounds */
993 for(k = 2; k <= fixed[0]; k++) {
994 if(fixed[k] != fixed[k-1]+1) {
995 status = presolve_setstatus(psdata, INFEASIBLE);
996 goto Done;
997 }
998 }
999 /* Fix other member variables to zero, if necessary */
1000 for(k = kk; k > 0; k--) {
1001 j = SOS->members[k];
1002 if((get_lowbo(lp, j) > 0) && !is_semicont(lp, j))
1003 continue;
1004 if(!presolve_colfix(psdata, j, 0.0, AUTOMATIC, &iBoundTighten)) {
1005 status = presolve_setstatus(psdata, INFEASIBLE);
1006 goto Done;
1007 }
1008 }
1009 /* Remove the SOS */
1010 delete_SOSrec(lp->SOS, i /* , FALSE */);
1011 }
1012 /* Otherwise, try to fix variables outside the SOS type window */
1013 else if(fixed[0] > 0) {
1014 for(k = kk; k > 0; k--) {
1015 if((k > fixed[fixed[0]]-SOS->type) && /* After leading entries */
1016 (k < fixed[1]+SOS->type)) /* Before trailing entries */
1017 continue;
1018 j = SOS->members[k];
1019 SOS_member_delete(lp->SOS, i, j);
1020 /* if(get_upbo(lp, j) - get_lowbo(lp, j) < lp->epsprimal) */
1021 if(is_fixedvar(lp, nrows+j))
1022 continue;
1023 if(!presolve_colfix(psdata, j, 0.0, AUTOMATIC, &iBoundTighten)) {
1024 status = presolve_setstatus(psdata, INFEASIBLE);
1025 goto Done;
1026 }
1027 }
1028 }
1029 i--;
1030 }
1031
1032 /* Update the sparse member map if there were SOS deletions;
1033 Remember that delete_SOSrec() above specified deferred updating! */
1034 i = SOS_count(lp);
1035 if((i < ii) || (iBoundTighten > 0)) {
1036 SOS_member_updatemap(lp->SOS);
1037 }
1038
1039 /* Update tag orders */
1040 for(; i > 0; i--)
1041 lp->SOS->sos_list[i-1]->tagorder = i;
1042
1043 Done:
1044 FREE(fixed);
1045 (*nb) += iBoundTighten;
1046 (*nSum) += iBoundTighten;
1047
1048 return( status );
1049 }
1050
presolve_fixSOS1(presolverec * psdata,int colnr,REAL fixvalue,int * nr,int * nv)1051 STATIC MYBOOL presolve_fixSOS1(presolverec *psdata, int colnr, REAL fixvalue, int *nr, int *nv)
1052 {
1053 lprec *lp = psdata->lp;
1054 int i, k, j;
1055 SOSrec *SOS;
1056 REAL newvalue;
1057 MYBOOL *fixed = NULL, status = FALSE;
1058
1059 /* Allocate working member list */
1060 if(!allocMYBOOL(lp, &fixed, lp->columns+1, TRUE) )
1061 return(FALSE);
1062
1063 /* Fix variables in SOS's where colnr is a member */
1064 i = SOS_count(lp);
1065 while(i > 0) {
1066 /* Set next SOS target (note that colnr has been tested earlier as not being a member of a higher order SOS) */
1067 SOS = lp->SOS->sos_list[i-1];
1068 if(SOS_is_member(lp->SOS, i, colnr)) {
1069 for(k = SOS->members[0]; k > 0; k--) {
1070 j = SOS->members[k];
1071 if(fixed[j])
1072 continue;
1073 if(j == colnr) {
1074 fixed[j] = TRUE;
1075 newvalue = fixvalue;
1076 }
1077 else {
1078 fixed[j] = AUTOMATIC;
1079 newvalue = 0.0;
1080 }
1081 /* If it is a member of a higher order SOS then just change bounds */
1082 if(!presolve_candeletevar(psdata, j)) {
1083 set_bounds(lp, j, newvalue, newvalue);
1084 fixed[j] = TRUE | AUTOMATIC;
1085 psdata->forceupdate = TRUE;
1086 }
1087 /* Otherwise fix it in preparation for removal */
1088 else if(!presolve_colfix(psdata, j, newvalue, TRUE, nv))
1089 goto Done;
1090 }
1091 }
1092 i--;
1093 }
1094
1095 /* Delete SOS'es or SOS member variables where we can */
1096 k = i = SOS_count(lp);
1097 while(i > 0) {
1098 /* Set next SOS target */
1099 SOS = lp->SOS->sos_list[i-1];
1100 if(SOS_is_member(lp->SOS, i, colnr)) {
1101 /* Always delete SOS1's */
1102 if(SOS->type == SOS1)
1103 delete_SOSrec(lp->SOS, i /* , FALSE */);
1104 /* Only delete leading or trailing SOS members in higher-order SOS'es that are fixed at 0;
1105 (note that this section of the code will never be called in the current setup) */
1106 else {
1107 /* First the leading entries... */
1108 for(j = 1; j <= SOS->members[0]; j++) {
1109 if(fixed[SOS->members[j]] == AUTOMATIC)
1110 SOS_member_delete(lp->SOS, i, SOS->members[j]);
1111 }
1112 /* ...then trailing entries */
1113 for(j = SOS->members[0]; j > 0; j--) {
1114 if(fixed[SOS->members[j]] == AUTOMATIC)
1115 SOS_member_delete(lp->SOS, i, SOS->members[j]);
1116 }
1117 }
1118 }
1119 i--;
1120 }
1121
1122 /* Update the sparse member map if there were SOS deletions; delete_SOSrec() above
1123 specified deferred updating */
1124 i = SOS_count(lp);
1125 if(i < k)
1126 SOS_member_updatemap(lp->SOS);
1127
1128 /* Delete the variables that have been fixed */
1129 k = 0;
1130 for(j = lp->columns; j > 0; j--) {
1131 if((fixed[j] == TRUE) || (fixed[j] == AUTOMATIC)) {
1132 presolve_colremove(psdata, j, TRUE);
1133 k++;
1134 }
1135 }
1136
1137 /* Update tag orders */
1138 i = SOS_count(lp);
1139 for(; i > 0; i--)
1140 lp->SOS->sos_list[i-1]->tagorder = i;
1141
1142 status = TRUE;
1143
1144 Done:
1145 FREE(fixed);
1146 return( status );
1147 }
1148
presolve_setEQ(presolverec * psdata,int rownr)1149 STATIC void presolve_setEQ(presolverec *psdata, int rownr)
1150 {
1151 lprec *lp = psdata->lp;
1152
1153 if(is_constr_type(lp, rownr, LE))
1154 removeLink(psdata->LTmap, rownr);
1155 setLink(psdata->EQmap, rownr);
1156 set_constr_type(lp, rownr, EQ);
1157 psdata->dv_lobo[rownr] = -lp->infinite;
1158 psdata->dv_upbo[rownr] = lp->infinite;
1159 }
1160
presolve_singletonbounds(presolverec * psdata,int rownr,int colnr,REAL * lobound,REAL * upbound,REAL * aval)1161 STATIC MYBOOL presolve_singletonbounds(presolverec *psdata, int rownr, int colnr, REAL *lobound, REAL *upbound, REAL *aval)
1162 {
1163 lprec *lp = psdata->lp;
1164 REAL coeff_a, epsvalue = psdata->epsvalue;
1165 MYBOOL isneg;
1166
1167 /* Compute row singleton variable range */
1168 if(is_constr_type(lp, rownr, EQ) && (fabs(*lobound) < epsvalue))
1169 *lobound = *upbound = 0;
1170 else {
1171 if(aval == NULL)
1172 coeff_a = get_mat(lp, rownr, colnr);
1173 else
1174 coeff_a = *aval;
1175 isneg = (MYBOOL) (coeff_a < 0);
1176 if(*lobound > -lp->infinite)
1177 *lobound /= coeff_a;
1178 else if(isneg)
1179 *lobound = -(*lobound);
1180 if(*upbound < lp->infinite)
1181 *upbound /= coeff_a;
1182 else if(isneg)
1183 *upbound = -(*upbound);
1184 if(isneg)
1185 swapREAL(lobound, upbound);
1186 }
1187
1188 /* Check against bound - handle SC variables specially */
1189 if(is_semicont(lp, colnr)) {
1190 coeff_a = get_lowbo(lp, colnr);
1191 if(coeff_a > 0) {
1192 SETMAX(*lobound, 0.0);
1193 SETMIN(*upbound, get_upbo(lp, colnr));
1194 }
1195 else {
1196 coeff_a = get_upbo(lp, colnr);
1197 if(coeff_a > 0) {
1198 SETMAX(*lobound, get_lowbo(lp, colnr));
1199 SETMIN(*upbound, 0.0);
1200 }
1201 }
1202 }
1203 else {
1204 SETMAX(*lobound, get_lowbo(lp, colnr));
1205 SETMIN(*upbound, get_upbo(lp, colnr));
1206 }
1207
1208 /* Return with consistency status */
1209 #ifdef DoPresolveRelativeTest
1210 isneg = (MYBOOL) (my_reldiff(*upbound, *lobound) >= - epsvalue);
1211 #else
1212 isneg = (MYBOOL) (*upbound >= *lobound - epsvalue);
1213 #endif
1214 if(!isneg) {
1215 /* Attempt bound-related error correction */
1216 if(fabs(my_reldiff(*lobound, get_upbo(lp, colnr))) < PRESOLVE_BOUNDSLACK*epsvalue)
1217 *lobound = get_upbo(lp, colnr);
1218 else if(fabs(my_reldiff(*upbound, get_lowbo(lp, colnr))) < PRESOLVE_BOUNDSLACK*epsvalue)
1219 *upbound = get_lowbo(lp, colnr);
1220 #ifdef DoPresolveRelativeTest
1221 isneg = (MYBOOL) (my_reldiff(*upbound, *lobound) >= - epsvalue);
1222 #else
1223 isneg = (MYBOOL) (*upbound >= *lobound - epsvalue);
1224 #endif
1225 if(!isneg)
1226 report(lp, NORMAL, "presolve_singletonbounds: Singleton variable %s in row %s infeasibility (%g << %g)\n",
1227 get_col_name(lp, colnr), get_row_name(lp, rownr), *lobound, *upbound);
1228 }
1229 return( isneg );
1230 }
1231
presolve_altsingletonvalid(presolverec * psdata,int rownr,int colnr,REAL reflotest,REAL refuptest)1232 STATIC MYBOOL presolve_altsingletonvalid(presolverec *psdata, int rownr, int colnr, REAL reflotest, REAL refuptest)
1233 {
1234 lprec *lp = psdata->lp;
1235 REAL coeff_bl, coeff_bu, epsvalue = psdata->epsvalue;
1236
1237 coeff_bl = get_rh_lower(lp, rownr);
1238 coeff_bu = get_rh_upper(lp, rownr);
1239
1240 /* Check base data validity */
1241 #ifdef DoPresolveRelativeTest
1242 if((my_reldiff(refuptest, reflotest) < -epsvalue) ||
1243 #else
1244 if((reflotest > refuptest + epsvalue) ||
1245 #endif
1246 !presolve_singletonbounds(psdata, rownr, colnr, &coeff_bl, &coeff_bu, NULL))
1247 return( FALSE );
1248
1249 /* Base data is Ok, now check against against each other */
1250 epsvalue = MAX(reflotest-coeff_bu, coeff_bl-refuptest) / epsvalue;
1251 if(epsvalue > PRESOLVE_BOUNDSLACK) {
1252 report(lp, NORMAL, "presolve_altsingletonvalid: Singleton variable %s in row %s infeasible (%g)\n",
1253 get_col_name(lp, colnr), get_row_name(lp, rownr), MAX(reflotest-coeff_bu, coeff_bl-refuptest));
1254 return( FALSE );
1255 }
1256 else
1257 return( TRUE );
1258 }
1259
presolve_multibounds(presolverec * psdata,int rownr,int colnr,REAL * lobound,REAL * upbound,REAL * aval,MYBOOL * rowbinds)1260 STATIC MYBOOL presolve_multibounds(presolverec *psdata, int rownr, int colnr,
1261 REAL *lobound, REAL *upbound, REAL *aval, MYBOOL *rowbinds)
1262 {
1263 lprec *lp = psdata->lp;
1264 MYBOOL rowbindsvar = FALSE, status = FALSE;
1265 REAL coeff_a, LHS, RHS, netX, Xupper, Xlower, epsvalue = psdata->epsvalue;
1266
1267 /* Get variable bounds for netting */
1268 LHS = *lobound;
1269 RHS = *upbound;
1270 Xlower = get_lowbo(lp, colnr);
1271 Xupper = get_upbo(lp, colnr);
1272
1273 /* Identify opportunity for bound tightening */
1274 if(aval == NULL)
1275 coeff_a = get_mat(lp, rownr, colnr);
1276 else
1277 coeff_a = *aval;
1278
1279 netX = presolve_sumplumin(lp, rownr, psdata->rows, TRUE);
1280 if(!my_infinite(lp, LHS) && !my_infinite(lp, netX)) {
1281 if(coeff_a > 0) {
1282 LHS -= netX-coeff_a*Xupper;
1283 LHS /= coeff_a;
1284 if(LHS > Xlower + epsvalue) {
1285 Xlower = presolve_roundrhs(lp, LHS, TRUE);
1286 status = TRUE;
1287 }
1288 else if(LHS > Xlower - epsvalue)
1289 rowbindsvar = TRUE;
1290 }
1291 else {
1292 LHS -= netX-coeff_a*Xlower;
1293 LHS /= coeff_a;
1294 if(LHS < Xupper - epsvalue) {
1295 Xupper = presolve_roundrhs(lp, LHS, FALSE);
1296 status = AUTOMATIC;
1297 }
1298 else if(LHS < Xupper + epsvalue)
1299 rowbindsvar = AUTOMATIC;
1300 }
1301 }
1302
1303 netX = presolve_sumplumin(lp, rownr, psdata->rows, FALSE);
1304 if(!my_infinite(lp, RHS) && !my_infinite(lp, netX)) {
1305 if(coeff_a < 0) {
1306 if(!my_infinite(lp, Xupper)) {
1307 RHS -= netX-coeff_a*Xupper;
1308 RHS /= coeff_a;
1309 if(RHS > Xlower + epsvalue) {
1310 Xlower = presolve_roundrhs(lp, RHS, TRUE);
1311 status |= TRUE;
1312 }
1313 else if(RHS > Xlower - epsvalue)
1314 rowbindsvar |= TRUE;
1315 }
1316 }
1317 else if(!my_infinite(lp, Xlower)) {
1318 RHS -= netX-coeff_a*Xlower;
1319 RHS /= coeff_a;
1320 if(RHS < Xupper - epsvalue) {
1321 Xupper = presolve_roundrhs(lp, RHS, FALSE);
1322 status |= AUTOMATIC;
1323 }
1324 else if(RHS < Xupper + epsvalue)
1325 rowbindsvar |= AUTOMATIC;
1326 }
1327 }
1328
1329 *lobound = Xlower;
1330 *upbound = Xupper;
1331 if(rowbinds != NULL)
1332 *rowbinds = rowbindsvar;
1333
1334 return(status);
1335 }
1336
isnz_origobj(lprec * lp,int colnr)1337 STATIC MYBOOL isnz_origobj(lprec *lp, int colnr)
1338 {
1339 return( (MYBOOL) (lp->orig_obj[colnr] != 0) );
1340 }
1341
presolve_testrow(presolverec * psdata,int lastrow)1342 STATIC MYBOOL presolve_testrow(presolverec *psdata, int lastrow)
1343 {
1344 if(psdata->forceupdate) {
1345 presolve_updatesums(psdata);
1346 psdata->forceupdate = FALSE;
1347 }
1348 if(!presolve_rowfeasible(psdata, 0, TRUE))
1349 return( FALSE );
1350 else
1351 return( TRUE );
1352 }
1353
presolve_coltighten(presolverec * psdata,int colnr,REAL LOnew,REAL UPnew,int * count)1354 STATIC MYBOOL presolve_coltighten(presolverec *psdata, int colnr, REAL LOnew, REAL UPnew, int *count)
1355 {
1356 lprec *lp = psdata->lp;
1357 int elmnr, elmend, k, oldcount = 0, newcount = 0, deltainf;
1358 REAL LOold, UPold, Value, margin = psdata->epsvalue;
1359 MATrec *mat = lp->matA;
1360 REAL *value;
1361 int *rownr;
1362
1363 /* Attempt correction of marginally equal, but inconsistent input values */
1364 Value = UPnew - LOnew;
1365 if((Value <= -margin) && (Value > -lp->epsprimal)) {
1366 if(fabs(fmod(UPnew, 1.0)) < margin)
1367 LOnew = UPnew;
1368 else
1369 UPnew = LOnew;
1370 }
1371
1372 /* Check if there is anything to do */
1373 LOold = get_lowbo(lp, colnr);
1374 UPold = get_upbo(lp, colnr);
1375 #ifdef Paranoia
1376 if(((LOold > LOnew) && !is_semicont(lp, colnr)) || (UPold < UPnew)) {
1377 report(lp, SEVERE, "presolve_coltighten: Inconsistent new bounds requested for column %d\n", colnr);
1378 return( FALSE );
1379 }
1380 #endif
1381 if(count != NULL)
1382 newcount = *count;
1383 oldcount = newcount;
1384
1385 /* Modify inf-count */
1386 deltainf = 0;
1387 if((UPold < lp->infinite) || (LOold > -lp->infinite))
1388 deltainf -= 1;
1389 if((UPnew < lp->infinite) || (LOnew > -lp->infinite))
1390 deltainf += 1;
1391 if(isnz_origobj(lp, colnr))
1392 psdata->rows->infcount[0] += deltainf;
1393 elmnr = mat->col_end[colnr-1];
1394 elmend = mat->col_end[colnr];
1395 rownr = &COL_MAT_ROWNR(elmnr);
1396 for(; elmnr < elmend; elmnr++, rownr += matRowColStep) {
1397 k = *rownr;
1398 if(isActiveLink(psdata->rows->varmap, k))
1399 psdata->rows->infcount[k] += deltainf;
1400 }
1401
1402 /* Look for opportunity to tighten upper variable bound */
1403 if((UPnew < lp->infinite) && (UPnew+margin < UPold)) {
1404 if(is_int(lp, colnr))
1405 UPnew = floor(UPnew+margin);
1406 if(UPold < lp->infinite) {
1407 /* First do OF */
1408 k = 0;
1409 Value = my_chsign(is_chsign(lp, k), lp->orig_obj[colnr]);
1410 if((Value > 0) && (psdata->rows->pluupper[k] < lp->infinite))
1411 psdata->rows->pluupper[k] += (UPnew-UPold)*Value;
1412 else if((Value < 0) && (psdata->rows->negupper[k] < lp->infinite))
1413 psdata->rows->negupper[k] += (LOnew-LOold)*Value;
1414 psdata->rows->infcount[k] += deltainf;
1415
1416 /* Then scan the constraint rows */
1417 elmnr = mat->col_end[colnr-1];
1418 elmend = mat->col_end[colnr];
1419 rownr = &COL_MAT_ROWNR(elmnr);
1420 value = &COL_MAT_VALUE(elmnr);
1421 for(; elmnr < elmend;
1422 elmnr++, rownr += matRowColStep, value += matValueStep) {
1423 k = *rownr;
1424 if(!isActiveLink(psdata->rows->varmap, k))
1425 continue;
1426 Value = my_chsign(is_chsign(lp, k), *value);
1427 if((Value > 0) && (psdata->rows->pluupper[k] < lp->infinite))
1428 psdata->rows->pluupper[k] += (UPnew-UPold)*Value;
1429 else if((Value < 0) && (psdata->rows->negupper[k] < lp->infinite))
1430 psdata->rows->negupper[k] += (LOnew-LOold)*Value;
1431 }
1432 }
1433 else
1434 psdata->forceupdate = TRUE;
1435 if(UPnew < UPold) {
1436 UPold = UPnew;
1437 newcount++;
1438 }
1439 }
1440
1441 /* Look for opportunity to tighten lower variable bound */
1442 if((LOnew > -lp->infinite) && (LOnew-margin > LOold)) {
1443 if(is_int(lp, colnr))
1444 LOnew = ceil(LOnew-margin);
1445 if(LOold > -lp->infinite) {
1446 /* First do OF */
1447 k = 0;
1448 Value = my_chsign(is_chsign(lp, k), lp->orig_obj[colnr]);
1449 if((Value > 0) && (psdata->rows->plulower[k] > -lp->infinite))
1450 psdata->rows->plulower[k] += (LOnew-LOold)*Value;
1451 else if((Value < 0) && (psdata->rows->neglower[k] > -lp->infinite))
1452 psdata->rows->neglower[k] += (UPnew-UPold)*Value;
1453
1454 /* Then scan the constraint rows */
1455 elmnr = mat->col_end[colnr-1];
1456 elmend = mat->col_end[colnr];
1457 rownr = &COL_MAT_ROWNR(elmnr);
1458 value = &COL_MAT_VALUE(elmnr);
1459 for(; elmnr < elmend;
1460 elmnr++, rownr += matRowColStep, value += matValueStep) {
1461 k = *rownr;
1462 if(!isActiveLink(psdata->rows->varmap, k))
1463 continue;
1464 Value = my_chsign(is_chsign(lp, k), *value);
1465 if((Value > 0) && (psdata->rows->plulower[k] > -lp->infinite))
1466 psdata->rows->plulower[k] += (LOnew-LOold)*Value;
1467 else if((Value < 0) && (psdata->rows->neglower[k] > -lp->infinite))
1468 psdata->rows->neglower[k] += (UPnew-UPold)*Value;
1469 }
1470 }
1471 else
1472 psdata->forceupdate = TRUE;
1473 if(LOnew > LOold) {
1474 LOold = LOnew;
1475 newcount++;
1476 }
1477 }
1478
1479 /* Now set the new variable bounds, if they are tighter */
1480 if(newcount > oldcount) {
1481 UPnew = presolve_roundval(lp, UPnew);
1482 LOnew = presolve_roundval(lp, LOnew);
1483 if(LOnew > UPnew) {
1484 if(LOnew-UPnew < margin) {
1485 LOnew = UPnew;
1486 }
1487 else {
1488 report(lp, NORMAL, "presolve_coltighten: Found column %s with LB %g > UB %g\n",
1489 get_col_name(lp, colnr), LOnew, UPnew);
1490 return( FALSE );
1491 }
1492 }
1493 if(lp->spx_trace || (lp->verbose > DETAILED))
1494 report(lp, NORMAL, "presolve_coltighten: Replaced bounds on column %s to [%g ... %g]\n",
1495 get_col_name(lp, colnr), LOnew, UPnew);
1496 set_bounds(lp, colnr, LOnew, UPnew);
1497 }
1498 if(count != NULL)
1499 *count = newcount;
1500
1501 return( TRUE );
1502 }
1503
presolve_rowtighten(presolverec * psdata,int rownr,int * tally,MYBOOL intsonly)1504 STATIC int presolve_rowtighten(presolverec *psdata, int rownr, int *tally, MYBOOL intsonly)
1505 {
1506 lprec *lp = psdata->lp;
1507 MYBOOL rowbinds;
1508 int item = 0, jx, jjx, ix, idxn = 0, *idxbound = NULL, status = RUNNING;
1509 REAL *newbound = NULL, RHlo = get_rh_lower(lp, rownr), RHup = get_rh_upper(lp, rownr),
1510 VARlo, VARup, Aval;
1511 MATrec *mat = lp->matA;
1512
1513 jx = presolve_rowlength(psdata, rownr);
1514 allocREAL(lp, &newbound, 2*jx, TRUE);
1515 allocINT (lp, &idxbound, 2*jx, TRUE);
1516
1517 /* Identify bound tightening for each active variable in the constraint */
1518 for(jx = presolve_nextcol(psdata, rownr, &item); jx >= 0;
1519 jx = presolve_nextcol(psdata, rownr, &item)) {
1520 jjx = ROW_MAT_COLNR(jx);
1521 Aval = ROW_MAT_VALUE(jx);
1522 Aval = my_chsign(rownr, Aval);
1523
1524 VARlo = RHlo;
1525 VARup = RHup;
1526 presolve_multibounds(psdata, rownr,jjx, &VARlo, &VARup, &Aval, &rowbinds);
1527 if(rowbinds & TRUE) {
1528 idxbound[idxn] = -jjx;
1529 newbound[idxn] = VARlo;
1530 idxn++;
1531 }
1532 if(rowbinds & AUTOMATIC) {
1533 idxbound[idxn] = jjx;
1534 newbound[idxn] = VARup;
1535 idxn++;
1536 }
1537 }
1538
1539 /* Loop over the bounds identified for tightening and perform update */
1540 ix = 0;
1541 while(ix < idxn) {
1542 jjx = idxbound[ix];
1543 jx = abs(jjx);
1544
1545 /* Skip free variables and non-ints, if specified */
1546 if(is_unbounded(lp, jx) ||
1547 (intsonly && !is_int(lp, jx)))
1548 continue;
1549
1550 VARlo = get_lowbo(lp, jx);
1551 VARup = get_upbo(lp, jx);
1552 /* while((ix < idxn) && (jx == abs(jjx))) { */
1553 while((ix < idxn) && (jx == abs((jjx = idxbound[ix])))) {
1554 if(jjx < 0)
1555 VARlo = newbound[ix];
1556 else
1557 VARup = newbound[ix];
1558 ix++;
1559 }
1560 if(!presolve_coltighten(psdata, jx, VARlo, VARup, tally)) {
1561 status = presolve_setstatus(psdata, INFEASIBLE);
1562 break;
1563 }
1564 }
1565
1566 FREE(newbound);
1567 FREE(idxbound);
1568
1569 return(status);
1570 }
1571
set_dv_bounds(presolverec * psdata,int rownr,REAL lowbo,REAL upbo)1572 STATIC void set_dv_bounds(presolverec *psdata, int rownr, REAL lowbo, REAL upbo)
1573 {
1574 psdata->dv_lobo[rownr] = lowbo;
1575 psdata->dv_upbo[rownr] = upbo;
1576 }
get_dv_lower(presolverec * psdata,int rownr)1577 STATIC REAL get_dv_lower(presolverec *psdata, int rownr)
1578 {
1579 return( psdata->dv_lobo[rownr] );
1580 }
1581
get_dv_upper(presolverec * psdata,int rownr)1582 STATIC REAL get_dv_upper(presolverec *psdata, int rownr)
1583 {
1584 return( psdata->dv_upbo[rownr] );
1585 }
1586
presolve_rowfix(presolverec * psdata,int rownr,REAL newvalue,MYBOOL remove,int * tally)1587 STATIC MYBOOL presolve_rowfix(presolverec *psdata, int rownr, REAL newvalue, MYBOOL remove, int *tally)
1588 {
1589 lprec *lp = psdata->lp;
1590 int i, ix, ie;
1591 MYBOOL isneg, lofinite, upfinite, doupdate = FALSE, chsign = is_chsign(lp, rownr);
1592 REAL lobound, upbound, lovalue, upvalue,
1593 Value, fixvalue, fixprod, mult;
1594 MATrec *mat = lp->matA;
1595 psrec *ps = psdata->cols;
1596
1597 /* Set "fixed" value in case we are deleting a variable */
1598 upbound = get_dv_upper(psdata, rownr);
1599 lobound = get_dv_lower(psdata, rownr);
1600 if(remove) {
1601 if(upbound-lobound < psdata->epsvalue) {
1602 if((newvalue > lobound) && (newvalue < upbound))
1603 fixvalue = newvalue;
1604 else
1605 fixvalue = lobound;
1606 }
1607 else {
1608 if(my_infinite(lp, newvalue) && (get_rh(lp, rownr) == 0))
1609 fixvalue = ((lobound <= 0) && (upbound >= 0) ? 0 : MIN(upbound, lobound));
1610 else
1611 fixvalue = newvalue;
1612 }
1613 set_dv_bounds(psdata, rownr, fixvalue, fixvalue);
1614 if(fixvalue != 0)
1615 addUndoPresolve(lp, FALSE, rownr, fixvalue, 0, 0);
1616 mult = -1;
1617 }
1618 else {
1619 mult = 1;
1620 fixvalue = 0;
1621 }
1622
1623 /* Loop over rows to update statistics */
1624 ix = mat->row_end[rownr - 1];
1625 ie = mat->row_end[rownr];
1626 for(; ix < ie; ix++) {
1627
1628 /* Retrieve row data and adjust RHS if we are deleting a variable */
1629 i = ROW_MAT_COLNR(ix);
1630 Value = ROW_MAT_VALUE(ix);
1631 if(Value == 0)
1632 continue;
1633
1634 if(remove && (fixvalue != 0)) {
1635 fixprod = Value*fixvalue;
1636 lp->orig_obj[i] -= fixprod;
1637 my_roundzero(lp->orig_obj[i], psdata->epsvalue);
1638 lp->presolve_undo->fixed_obj[i] += fixprod;
1639 }
1640
1641 /* Prepare for further processing */
1642 Value = my_chsign(chsign, Value);
1643 isneg = (MYBOOL) (Value < 0);
1644
1645 /* Reduce row variable counts if we are removing the variable */
1646 if(!isActiveLink(ps->varmap, i))
1647 continue;
1648 if(remove) {
1649 if(isneg) {
1650 ps->negcount[i]--;
1651 }
1652 else {
1653 ps->plucount[i]--;
1654 }
1655 if((lobound < 0) && (upbound >= 0)) {
1656 ps->pluneg[i]--;
1657 }
1658 }
1659
1660 /* Compute associated constraint contribution values */
1661 upfinite = (MYBOOL) (upbound < lp->infinite);
1662 lofinite = (MYBOOL) (lobound > -lp->infinite);
1663 if(upfinite || lofinite) {
1664 if(remove)
1665 ps->infcount[i]--;
1666 else
1667 ps->infcount[i]++;
1668 }
1669 upvalue = my_if(upfinite, Value*upbound, my_chsign(isneg, lp->infinite));
1670 lovalue = my_if(lofinite, Value*lobound, my_chsign(isneg, -lp->infinite));
1671
1672 /* Cumulate effective upper column bound (only bother with non-finite bound) */
1673 if(isneg) {
1674 if((ps->negupper[i] < lp->infinite) && lofinite) {
1675 ps->negupper[i] += mult*lovalue;
1676 ps->negupper[i] = presolve_roundrhs(lp, ps->negupper[i], FALSE);
1677 }
1678 else if(remove && !lofinite)
1679 doupdate = TRUE;
1680 else
1681 ps->negupper[i] = lp->infinite;
1682 }
1683 else {
1684 if((ps->pluupper[i] < lp->infinite) && upfinite) {
1685 ps->pluupper[i] += mult*upvalue;
1686 ps->pluupper[i] = presolve_roundrhs(lp, ps->pluupper[i], FALSE);
1687 }
1688 else if(remove && !upfinite)
1689 doupdate = TRUE;
1690 else
1691 ps->pluupper[i] = lp->infinite;
1692 }
1693
1694 /* Cumulate effective lower column bound (only bother with non-finite bound) */
1695 if(isneg) {
1696 if((ps->neglower[i] > -lp->infinite) && upfinite) {
1697 ps->neglower[i] += mult*upvalue;
1698 ps->neglower[i] = presolve_roundrhs(lp, ps->neglower[i], TRUE);
1699 }
1700 else if(remove && !upfinite)
1701 doupdate = TRUE;
1702 else
1703 ps->neglower[i] = -lp->infinite;
1704 }
1705 else {
1706 if((ps->plulower[i] > -lp->infinite) && lofinite) {
1707 ps->plulower[i] += mult*lovalue;
1708 ps->plulower[i] = presolve_roundrhs(lp, ps->plulower[i], TRUE);
1709 }
1710 else if(remove && !lofinite)
1711 doupdate = TRUE;
1712 else
1713 ps->plulower[i] = -lp->infinite;
1714 }
1715
1716 /* Validate consistency of eliminated singleton */
1717 if(remove && ((i == 0) || (ps->next[i][0] == 1)) && !psdata->forceupdate) {
1718 presolve_range(lp, i, ps, &lovalue, &upvalue);
1719 Value = get_mat(lp, 0, i);
1720 if((upvalue < Value) ||
1721 (lovalue > Value)) {
1722 report(lp, IMPORTANT, "presolve: Row %s (%g << %g) infeasibility in column %s (OF=%g)\n",
1723 get_row_name(lp, rownr), lovalue, upvalue, get_col_name(lp, i), Value);
1724 return( FALSE );
1725 }
1726 }
1727 }
1728 if(remove) {
1729 psdata->forceupdate |= doupdate;
1730 if(tally != NULL)
1731 (*tally)++;
1732 }
1733 return( TRUE );
1734 }
1735
1736
presolve_colsingleton(presolverec * psdata,int i,int j,int * count)1737 STATIC int presolve_colsingleton(presolverec *psdata, int i, int j, int *count)
1738 {
1739 lprec *lp = psdata->lp;
1740 REAL RHlow, RHup, LObound, UPbound, Value;
1741
1742 #ifdef Paranoia
1743 if(!isActiveLink(psdata->cols->varmap, j))
1744 report(lp, SEVERE, "presolve_colsingleton: Nothing to do, column %d was eliminated earlier\n",
1745 j);
1746 #endif
1747
1748 Value = get_mat(lp,i,j);
1749 if(Value == 0)
1750 return( RUNNING );
1751
1752 /* Initialize and identify semicontinuous variable */
1753 LObound = get_lowbo(lp, j);
1754 UPbound = get_upbo(lp, j);
1755 if(is_semicont(lp, j) && (UPbound > LObound)) {
1756 if(LObound > 0)
1757 LObound = 0;
1758 else if(UPbound < 0)
1759 UPbound = 0;
1760 }
1761
1762 /* Get singleton variable bounds */
1763 RHlow = get_rh_lower(lp, i);
1764 RHup = get_rh_upper(lp, i);
1765 if(!presolve_singletonbounds(psdata, i,j, &RHlow, &RHup, &Value))
1766 return( presolve_setstatus(psdata, INFEASIBLE) );
1767
1768 if(presolve_coltighten(psdata, j, RHlow, RHup, count))
1769 return( RUNNING );
1770 else
1771 return( presolve_setstatus(psdata, INFEASIBLE) );
1772 }
1773
presolve_colfix(presolverec * psdata,int colnr,REAL newvalue,MYBOOL remove,int * tally)1774 STATIC MYBOOL presolve_colfix(presolverec *psdata, int colnr, REAL newvalue, MYBOOL remove, int *tally)
1775 {
1776 lprec *lp = psdata->lp;
1777 int i, ix, ie;
1778 MYBOOL isneg, lofinite, upfinite, doupdate = FALSE, doOF = TRUE;
1779 REAL lobound, upbound, lovalue, upvalue,
1780 Value, fixvalue, mult;
1781 MATrec *mat = lp->matA;
1782 psrec *ps = psdata->rows;
1783 REAL *value;
1784 int *rownr;
1785
1786 /* Set "fixed" value in case we are deleting a variable */
1787 upbound = get_upbo(lp, colnr);
1788 lobound = get_lowbo(lp, colnr);
1789 if(remove) {
1790 if(upbound-lobound < psdata->epsvalue) {
1791 if((newvalue > lobound) && (newvalue < upbound))
1792 fixvalue = newvalue;
1793 else
1794 fixvalue = lobound;
1795 }
1796 else {
1797 if(my_infinite(lp, newvalue) && (get_mat(lp, 0, colnr) == 0))
1798 fixvalue = ((lobound <= 0) && (upbound >= 0) ? 0 : MIN(upbound, lobound));
1799 else
1800 fixvalue = newvalue;
1801 }
1802 #if 1 /* Fast normal version */
1803 set_bounds(lp, colnr, fixvalue, fixvalue);
1804 #else /* Slower version that can be used for debugging/control purposes */
1805 presolve_coltighten(psdata, colnr, fixvalue, fixvalue, NULL);
1806 lobound = fixvalue;
1807 upbound = fixvalue;
1808 #endif
1809 if(fixvalue != 0)
1810 addUndoPresolve(lp, TRUE, colnr, fixvalue, 0, 0);
1811 mult = -1;
1812 }
1813 else {
1814 mult = 1;
1815 fixvalue = 0;
1816 }
1817
1818 /* Adjust semi-continuous variable bounds to zero-base */
1819 if(is_semicont(lp, colnr) && (upbound > lobound)) {
1820 if(lobound > 0)
1821 lobound = 0;
1822 else if(upbound < 0)
1823 upbound = 0;
1824 }
1825
1826 /* Loop over rows to update statistics */
1827 ix = mat->col_end[colnr - 1];
1828 ie = mat->col_end[colnr];
1829 rownr = &COL_MAT_ROWNR(ix);
1830 value = &COL_MAT_VALUE(ix);
1831 for(; doOF || (ix < ie);
1832 ix++, rownr += matRowColStep, value += matValueStep) {
1833
1834 /* Retrieve row data and adjust RHS if we are deleting a variable */
1835 Restart:
1836 if(doOF) {
1837 i = 0;
1838 Value = lp->orig_obj[colnr];
1839 }
1840 else {
1841 i = *rownr;
1842 Value = *value;
1843 if(!isActiveLink(ps->varmap, i))
1844 continue;
1845 }
1846 if(Value == 0)
1847 goto BlockEnd;
1848
1849 if(remove && (fixvalue != 0))
1850 presolve_adjustrhs(psdata, i, Value*fixvalue, psdata->epsvalue);
1851
1852 /* Prepare for further processing */
1853 Value = my_chsign(is_chsign(lp, i), Value);
1854 isneg = (MYBOOL) (Value < 0);
1855
1856 /* Reduce row variable counts if we are removing the variable */
1857 if(remove == TRUE) {
1858 if(isneg) {
1859 ps->negcount[i]--;
1860 }
1861 else {
1862 ps->plucount[i]--;
1863 }
1864 if((lobound < 0) && (upbound >= 0)) {
1865 ps->pluneg[i]--;
1866 }
1867 }
1868
1869 /* Compute associated constraint contribution values */
1870 upfinite = (MYBOOL) (upbound < lp->infinite);
1871 lofinite = (MYBOOL) (lobound > -lp->infinite);
1872 if(upfinite || lofinite) {
1873 if(remove)
1874 ps->infcount[i]--;
1875 else
1876 ps->infcount[i]++;
1877 }
1878 upvalue = my_if(upfinite, Value*upbound, my_chsign(isneg, lp->infinite));
1879 lovalue = my_if(lofinite, Value*lobound, my_chsign(isneg, -lp->infinite));
1880
1881 /* Cumulate effective upper row bound (only bother with non-finite bound) */
1882 if(isneg) {
1883 if((ps->negupper[i] < lp->infinite) && lofinite) {
1884 ps->negupper[i] += mult*lovalue;
1885 ps->negupper[i] = presolve_roundrhs(lp, ps->negupper[i], FALSE);
1886 }
1887 else if(remove && !lofinite)
1888 doupdate = TRUE;
1889 else
1890 ps->negupper[i] = lp->infinite;
1891 }
1892 else {
1893 if((ps->pluupper[i] < lp->infinite) && upfinite) {
1894 ps->pluupper[i] += mult*upvalue;
1895 ps->pluupper[i] = presolve_roundrhs(lp, ps->pluupper[i], FALSE);
1896 }
1897 else if(remove && !upfinite)
1898 doupdate = TRUE;
1899 else
1900 ps->pluupper[i] = lp->infinite;
1901 }
1902
1903 /* Cumulate effective lower row bound (only bother with non-finite bound) */
1904 if(isneg) {
1905 if((ps->neglower[i] > -lp->infinite) && upfinite) {
1906 ps->neglower[i] += mult*upvalue;
1907 ps->neglower[i] = presolve_roundrhs(lp, ps->neglower[i], TRUE);
1908 }
1909 else if(remove && !upfinite)
1910 doupdate = TRUE;
1911 else
1912 ps->neglower[i] = -lp->infinite;
1913 }
1914 else {
1915 if((ps->plulower[i] > -lp->infinite) && lofinite) {
1916 ps->plulower[i] += mult*lovalue;
1917 ps->plulower[i] = presolve_roundrhs(lp, ps->plulower[i], TRUE);
1918 }
1919 else if(remove && !lofinite)
1920 doupdate = TRUE;
1921 else
1922 ps->plulower[i] = -lp->infinite;
1923 }
1924
1925 /* Validate consistency of eliminated singleton */
1926 if(remove && ((i == 0) || (ps->next[i][0] == 1)) && !psdata->forceupdate) {
1927 if(i == 0) {
1928 lovalue = get_rh_lower(lp, i);
1929 upvalue = get_rh_upper(lp, i);
1930 report(lp, DETAILED, "presolve_colfix: Objective determined by presolve as %18g\n",
1931 (is_maxim(lp) ? upvalue : lovalue));
1932 }
1933 else {
1934 presolve_range(lp, i, ps, &lovalue, &upvalue);
1935 #if 1
1936 Value = 0;
1937 #else
1938 Value = MAX(fabs(upvalue), fabs(lovalue));
1939 Value = psdata->epsvalue * MAX(1, Value);
1940 #endif
1941 if((upvalue < get_rh_lower(lp, i)-Value) ||
1942 (lovalue > get_rh_upper(lp, i)+Value)) {
1943 report(lp, NORMAL, "presolve_colfix: Variable %s (%g << %g) infeasibility in row %s (%g << %g)\n",
1944 get_col_name(lp, colnr), lovalue, upvalue,
1945 get_row_name(lp, i), get_rh_lower(lp,i), get_rh_upper(lp, i));
1946 return( FALSE );
1947 }
1948 }
1949 }
1950 BlockEnd:
1951 if(doOF) {
1952 doOF = FALSE;
1953 if(ix < ie)
1954 goto Restart;
1955 }
1956
1957 }
1958 if(remove) {
1959 psdata->forceupdate |= doupdate;
1960 if(tally != NULL)
1961 (*tally)++;
1962 }
1963 return( TRUE );
1964 }
1965
1966 /* Delete the columns of the specified row, but make sure we don't delete SOS variables.
1967 Note that we cannot use presolve_nextcol() here, since the variables are deleted. */
presolve_rowfixzero(presolverec * psdata,int rownr,int * nv)1968 STATIC int presolve_rowfixzero(presolverec *psdata, int rownr, int *nv)
1969 {
1970 lprec *lp = psdata->lp;
1971 MATrec *mat = lp->matA;
1972 int ix, jx, ib = mat->row_end[rownr-1];
1973 for(ix = mat->row_end[rownr]-1; ix >= ib; ix--) {
1974 jx = ROW_MAT_COLNR(ix);
1975 if(isActiveLink(psdata->cols->varmap, jx)) {
1976 if(!presolve_colfix(psdata, jx, 0.0, TRUE, nv))
1977 return( presolve_setstatus(psdata, INFEASIBLE) );
1978 if(presolve_candeletevar(psdata, jx))
1979 presolve_colremove(psdata, jx, TRUE);
1980 }
1981 }
1982 #ifdef xxParanoia
1983 if(!presolve_debugrowtallies(psdata))
1984 return( INFEASIBLE );
1985 #endif
1986 return( RUNNING );
1987 }
1988
1989 /* Function to find if a variable can be fixed based on considering the dual */
presolve_colfixdual(presolverec * psdata,int colnr,REAL * fixValue,int * status)1990 STATIC MYBOOL presolve_colfixdual(presolverec *psdata, int colnr, REAL *fixValue, int *status)
1991 {
1992 lprec *lp = psdata->lp;
1993 MYBOOL hasOF, isMI, isDualFREE = TRUE;
1994 int i, ix, ie, *rownr, signOF;
1995 REAL *value, loX, upX, eps = psdata->epsvalue;
1996 MATrec *mat = lp->matA;
1997
1998 /* First check basic variable range */
1999 loX = get_lowbo(lp, colnr);
2000 upX = get_upbo(lp, colnr);
2001 if(((loX < 0) && (upX > 0)) ||
2002 (fabs(upX-loX) < lp->epsvalue) ||
2003 SOS_is_member_of_type(lp->SOS, colnr, SOSn))
2004 return( FALSE );
2005 isMI = (MYBOOL) (upX <= 0);
2006
2007 /* Retrieve OF (standard form assuming maximization) */
2008 ix = mat->col_end[colnr - 1];
2009 ie = mat->col_end[colnr];
2010 rownr = &COL_MAT_ROWNR(ix);
2011 value = &COL_MAT_VALUE(ix);
2012 hasOF = isnz_origobj(lp, colnr);
2013 if(hasOF)
2014 signOF = my_sign(lp->orig_obj[colnr]);
2015 else
2016 signOF = 0;
2017
2018 /* Loop over all constraints involving active variable (standard form with LE constraints)*/
2019 for(; (ix < ie) && isDualFREE;
2020 ix++, rownr += matRowColStep, value += matValueStep) {
2021 i = *rownr;
2022 if(!isActiveLink(psdata->rows->varmap, i))
2023 continue;
2024 if(presolve_rowlength(psdata, i) == 1) {
2025 REAL val = my_chsign(is_chsign(lp, i), *value),
2026 loR = get_rh_lower(lp, i),
2027 upR = get_rh_upper(lp, i);
2028 if(!presolve_singletonbounds(psdata, i, colnr, &loR, &upR, &val)) {
2029 *status = presolve_setstatus(psdata, INFEASIBLE);
2030 return( FALSE );
2031 }
2032 if(loR > loX + psdata->epsvalue)
2033 loX = presolve_roundrhs(lp, loR, TRUE);
2034 if(upR < upX - psdata->epsvalue)
2035 upX = presolve_roundrhs(lp, upR, FALSE);
2036 continue;
2037 }
2038 else
2039 isDualFREE = my_infinite(lp, get_rh_range(lp, i)) || /* Explicitly free */
2040 ((presolve_sumplumin(lp, i, psdata->rows, TRUE)-eps <= get_rh_upper(lp, i)) && /* Implicitly free */
2041 (presolve_sumplumin(lp, i, psdata->rows, FALSE)+eps >= get_rh_lower(lp, i)));
2042 if(isDualFREE) {
2043 if(signOF == 0) /* Test on the basis of identical signs in the constraints */
2044 signOF = my_sign(*value);
2045 else /* Test on the basis of constraint sign equal to OF sign */
2046 isDualFREE = (MYBOOL) (signOF == my_sign(*value));
2047 }
2048 }
2049
2050 /* Set fixing value if we were successful */
2051 if(isDualFREE) {
2052 if(signOF == 0) {
2053 SETMAX(loX, 0);
2054 *fixValue = MIN(loX, upX);
2055 }
2056 else if(signOF > 0) {
2057 if(my_infinite(lp, loX))
2058 isDualFREE = FALSE;
2059 else {
2060 if(is_int(lp, colnr))
2061 *fixValue = ceil(loX-PRESOLVE_EPSVALUE);
2062 else
2063 *fixValue = loX;
2064 }
2065 }
2066 else {
2067 if(my_infinite(lp, upX))
2068 isDualFREE = FALSE;
2069 else {
2070 if(is_int(lp, colnr) && (upX != 0))
2071 *fixValue = floor(upX+PRESOLVE_EPSVALUE);
2072 else
2073 *fixValue = upX;
2074 }
2075 }
2076 if((*fixValue != 0) && SOS_is_member(lp->SOS, 0, colnr))
2077 return( FALSE );
2078
2079 }
2080
2081 return( isDualFREE );
2082 }
2083
2084 #if 0
2085 STATIC MYBOOL presolve_probefix01(presolverec *psdata, int colnr, REAL *fixvalue)
2086 {
2087 lprec *lp = psdata->lp;
2088 int i, ix, item;
2089 REAL loLim, absvalue, epsvalue = psdata->epsvalue;
2090 MATrec *mat = lp->matA;
2091 MYBOOL chsign, canfix = FALSE;
2092
2093 if(!is_binary(lp, colnr))
2094 return( canfix );
2095
2096 /* Loop over all active rows to search for fixing opportunity */
2097 item = 0;
2098 for(ix = presolve_nextrow(psdata, colnr, &item);
2099 (ix >= 0) && !canfix;
2100 ix = presolve_nextrow(psdata, colnr, &item)) {
2101 i = COL_MAT_ROWNR(ix);
2102 *fixvalue = COL_MAT_VALUE(ix);
2103 chsign = is_chsign(lp, i);
2104
2105 /* First check the lower bound of the normalized constraint */
2106 loLim = presolve_sumplumin(lp, i, psdata->rows, chsign);
2107 loLim = my_chsign(chsign, loLim);
2108 absvalue = fabs(*fixvalue);
2109 canfix = (MYBOOL) ((loLim + absvalue > lp->orig_rhs[i]+epsvalue*MAX(1, absvalue)));
2110
2111 /* If we were unsuccessful in fixing above, try the upper bound
2112 of the normalized constraint - if it is finite */
2113 if(!canfix && !my_infinite(lp, get_rh_range(lp, i))) {
2114 loLim = presolve_sumplumin(lp, i, psdata->rows, (MYBOOL) !chsign);
2115 loLim = my_chsign(!chsign, loLim);
2116 *fixvalue = -(*fixvalue);
2117 canfix = (MYBOOL) ((loLim + absvalue > get_rh_range(lp, i)-lp->orig_rhs[i]+epsvalue*MAX(1, absvalue)));
2118 }
2119 }
2120
2121 /* Check if we were successful in identifying fixing opportunity */
2122 if(canfix) {
2123 if(*fixvalue < 0)
2124 *fixvalue = 1;
2125 else
2126 *fixvalue = 0;
2127 }
2128 return( canfix );
2129 }
2130 #else
presolve_probefix01(presolverec * psdata,int colnr,REAL * fixvalue)2131 STATIC MYBOOL presolve_probefix01(presolverec *psdata, int colnr, REAL *fixvalue)
2132 {
2133 lprec *lp = psdata->lp;
2134 int i, ix, item;
2135 REAL loLim, upLim, range, absvalue, epsvalue = psdata->epsvalue, tolgap;
2136 MATrec *mat = lp->matA;
2137 MYBOOL chsign, status = FALSE;
2138
2139 if(!is_binary(lp, colnr))
2140 return( status );
2141
2142 /* Loop over all active rows to search for fixing opportunity. The logic is that if a
2143 constraint gets violated by setting a variable at one of its bounds, then it can be
2144 fixed at its opposite bound. */
2145 item = 0;
2146
2147 for(ix = presolve_nextrow(psdata, colnr, &item); (ix >= 0); ix = presolve_nextrow(psdata, colnr, &item)) {
2148 i = COL_MAT_ROWNR(ix);
2149 *fixvalue = COL_MAT_VALUE(ix);
2150 absvalue = fabs(*fixvalue);
2151 SETMIN(absvalue, 100);
2152 tolgap = epsvalue*MAX(1, absvalue);
2153 chsign = is_chsign(lp, i);
2154
2155 /* Get the constraint value limits based on variable bounds, normalized to LE constraint */
2156 loLim = presolve_sumplumin(lp, i, psdata->rows, FALSE);
2157 upLim = presolve_sumplumin(lp, i, psdata->rows, TRUE);
2158 if(chsign) {
2159 loLim = my_chsign(chsign, loLim);
2160 upLim = my_chsign(chsign, upLim);
2161 swapREAL(&loLim, &upLim);
2162 }
2163
2164 /* Check the upper constraint bound for possible violation if the value were to be fixed at 1 */
2165 if(loLim + *fixvalue > lp->orig_rhs[i]+tolgap) {
2166 if(*fixvalue < 0)
2167 presolve_setstatus(psdata, INFEASIBLE);
2168 *fixvalue = 0;
2169 break;
2170 }
2171
2172 /* Check the lower constraint bound for possible violation if the value were to be fixed at 1 */
2173 range = get_rh_range(lp, i);
2174 if(!my_infinite(lp, range) &&
2175 (upLim + *fixvalue < lp->orig_rhs[i]-range-tolgap)) {
2176 if(*fixvalue > 0)
2177 presolve_setstatus(psdata, INFEASIBLE);
2178 *fixvalue = 0;
2179 break;
2180 }
2181
2182 /* Check if we have to fix the value at 1 to avoid constraint infeasibility */
2183 if(psdata->rows->infcount[i] >= 1)
2184 continue;
2185 if(((*fixvalue < 0) && (upLim + *fixvalue >= loLim-tolgap) && (upLim > lp->orig_rhs[i]+tolgap)) ||
2186 ((*fixvalue > 0) && (loLim + *fixvalue <= upLim+tolgap) && (loLim < lp->orig_rhs[i]-range-tolgap) && !my_infinite(lp, range))) {
2187 *fixvalue = 1;
2188 break;
2189 }
2190 }
2191 status = (MYBOOL) (ix >= 0);
2192
2193 /* Returns TRUE if fixing opportunity was identified */
2194 return( status );
2195 }
2196 #endif
2197
presolve_probetighten01(presolverec * psdata,int colnr)2198 STATIC int presolve_probetighten01(presolverec *psdata, int colnr)
2199 {
2200 lprec *lp = psdata->lp;
2201 MYBOOL chsign;
2202 int i, ix, item, n = 0;
2203 REAL upLim, value, absvalue, epsvalue = psdata->epsvalue;
2204 MATrec *mat = lp->matA;
2205
2206 #if 0 /* Handled in calling routine */
2207 if(!is_binary(lp, colnr))
2208 return( n );
2209 #endif
2210
2211 /* Loop over all active rows and do coefficient tightening for qualifying constraints */
2212 item = 0;
2213 for(ix = presolve_nextrow(psdata, colnr, &item); ix >= 0;
2214 ix = presolve_nextrow(psdata, colnr, &item)) {
2215 i = COL_MAT_ROWNR(ix);
2216 value = COL_MAT_VALUE(ix);
2217 chsign = is_chsign(lp, i);
2218 upLim = presolve_sumplumin(lp, i, psdata->rows, (MYBOOL) !chsign);
2219 upLim = my_chsign(chsign, upLim);
2220
2221 /* Does this constraint qualify for coefficient tightening? */
2222 absvalue = fabs(value);
2223 if(upLim - absvalue < lp->orig_rhs[i]-epsvalue*MAX(1, absvalue)) {
2224 REAL delta = lp->orig_rhs[i] - upLim;
2225 lp->orig_rhs[i] = upLim;
2226 upLim = value - my_chsign(value < 0, delta);
2227 COL_MAT_VALUE(ix) = upLim;
2228 if(my_sign(value) != my_sign(upLim)) {
2229 if(chsign) {
2230 psdata->rows->negcount[i]--;
2231 psdata->rows->plucount[i]++;
2232 }
2233 else {
2234 psdata->rows->negcount[i]++;
2235 psdata->rows->plucount[i]--;
2236 }
2237 }
2238 n++;
2239 }
2240 }
2241 return( n );
2242 }
2243
presolve_mergerows(presolverec * psdata,int * nRows,int * nSum)2244 STATIC int presolve_mergerows(presolverec *psdata, int *nRows, int *nSum)
2245 {
2246 lprec *lp = psdata->lp;
2247 MYBOOL candelete;
2248 int status = RUNNING, item1, item2,
2249 firstix, RT1, RT2, i, ix, iix, j, jjx, n = 0;
2250 REAL Value1, Value2, bound;
2251 MATrec *mat = lp->matA;
2252
2253 for(i = lastActiveLink(psdata->rows->varmap); (i > 0) && (status == RUNNING); ) {
2254
2255 /* First scan for rows with identical row lengths */
2256 ix = prevActiveLink(psdata->rows->varmap, i);
2257 if(ix == 0)
2258 break;
2259
2260 /* Don't bother about empty rows or row singletons, since they are
2261 handled by PRESOLVE_ROWS */
2262 j = presolve_rowlength(psdata, i);
2263 if(j <= 1) {
2264 i = ix;
2265 continue;
2266 }
2267
2268 #if 0
2269 /* Enable this to scan all rows back */
2270 RT2 = lp->rows;
2271
2272 /* Check abort since this section can be pretty "expensive" */
2273 if(!presolve_statuscheck(psdata, &status))
2274 return( status );
2275 #else
2276 RT2 = 2+1;
2277 #endif
2278 firstix = ix;
2279 for(RT1 = 0; (ix > 0) && (RT1 < RT2) && (status == RUNNING);
2280 ix = prevActiveLink(psdata->rows->varmap, ix), RT1++) {
2281 candelete = FALSE;
2282 if(presolve_rowlength(psdata, ix) != j)
2283 continue;
2284
2285 /* Check if the beginning columns are identical; if not, continue */
2286 item1 = 0;
2287 iix = presolve_nextcol(psdata, ix, &item1);
2288 item2 = 0;
2289 jjx = presolve_nextcol(psdata, i, &item2);
2290
2291 if(ROW_MAT_COLNR(iix) != ROW_MAT_COLNR(jjx))
2292 continue;
2293
2294 /* We have a candidate row; check if the entries have a fixed non-zero ratio */
2295 Value1 = get_mat_byindex(lp, iix, TRUE, FALSE);
2296 Value2 = get_mat_byindex(lp, jjx, TRUE, FALSE);
2297 bound = Value1 / Value2;
2298 Value1 = bound;
2299
2300 /* Loop over remaining entries */
2301 jjx = presolve_nextcol(psdata, i, &item2);
2302 for(; (jjx >= 0) && (Value1 == bound);
2303 jjx = presolve_nextcol(psdata, i, &item2)) {
2304 iix = presolve_nextcol(psdata, ix, &item1);
2305 if(ROW_MAT_COLNR(iix) != ROW_MAT_COLNR(jjx))
2306 break;
2307 Value1 = get_mat_byindex(lp, iix, TRUE, FALSE);
2308 Value2 = get_mat_byindex(lp, jjx, TRUE, FALSE);
2309
2310 /* If the ratio is different from the reference value we have a mismatch */
2311 Value1 = Value1 / Value2;
2312 if(bound == lp->infinite)
2313 bound = Value1;
2314 else if(fabs(Value1 - bound) > psdata->epsvalue)
2315 break;
2316 }
2317
2318 /* Check if we found a match (we traversed all active columns without a break) */
2319 if(jjx < 0) {
2320
2321 /* Get main reference values */
2322 Value1 = lp->orig_rhs[ix];
2323 Value2 = lp->orig_rhs[i] * bound;
2324
2325 /* First check for inconsistent equalities */
2326 if((fabs(Value1 - Value2) > psdata->epsvalue) &&
2327 ((get_constr_type(lp, ix) == EQ) && (get_constr_type(lp, i) == EQ))) {
2328 report(lp, NORMAL, "presolve_mergerows: Inconsistent equalities %d and %d found\n",
2329 ix, i);
2330 status = presolve_setstatus(psdata, INFEASIBLE);
2331 }
2332
2333 else {
2334
2335 /* Update lower and upper bounds */
2336 if(is_chsign(lp, i) != is_chsign(lp, ix))
2337 bound = -bound;
2338
2339 Value1 = get_rh_lower(lp, i);
2340 if(Value1 <= -lp->infinite)
2341 Value1 *= my_sign(bound);
2342 else
2343 Value1 *= bound;
2344 my_roundzero(Value1, lp->epsdual); /* Extra rounding tolerance *** */
2345
2346 Value2 = get_rh_upper(lp, i);
2347 if(Value2 >= lp->infinite)
2348 Value2 *= my_sign(bound);
2349 else
2350 Value2 *= bound;
2351 my_roundzero(Value2, lp->epsdual); /* Extra rounding tolerance *** */
2352
2353 if((bound < 0))
2354 swapREAL(&Value1, &Value2);
2355
2356 bound = get_rh_lower(lp, ix);
2357 if(Value1 > bound + psdata->epsvalue)
2358 set_rh_lower(lp, ix, Value1);
2359 else
2360 Value1 = bound;
2361 bound = get_rh_upper(lp, ix);
2362 if(Value2 < bound - psdata->epsvalue)
2363 set_rh_upper(lp, ix, Value2);
2364 else
2365 Value2 = bound;
2366
2367 /* Check results and make equality if appropriate */
2368 if(fabs(Value2-Value1) < psdata->epsvalue)
2369 presolve_setEQ(psdata, ix);
2370 else if(Value2 < Value1) {
2371 status = presolve_setstatus(psdata, INFEASIBLE);
2372 }
2373
2374 /* Verify if we can continue */
2375 candelete = (MYBOOL) (status == RUNNING);
2376 if(!candelete) {
2377 report(lp, NORMAL, "presolve: Range infeasibility found involving rows %s and %s\n",
2378 get_row_name(lp, ix), get_row_name(lp, i));
2379 }
2380 }
2381 }
2382 /* Perform i-row deletion if authorized */
2383 if(candelete) {
2384 presolve_rowremove(psdata, i, TRUE);
2385 n++;
2386 break;
2387 }
2388 }
2389 i = firstix;
2390 }
2391 (*nRows) += n;
2392 (*nSum) += n;
2393
2394 return( status );
2395 }
2396
presolve_reduceGCD(presolverec * psdata,int * nn,int * nb,int * nsum)2397 STATIC MYBOOL presolve_reduceGCD(presolverec *psdata, int *nn, int *nb, int *nsum)
2398 {
2399 lprec *lp = psdata->lp;
2400 MYBOOL status = TRUE;
2401 int i, jx, je, in = 0, ib = 0;
2402 LLONG GCDvalue;
2403 REAL *Avalue, Rvalue, epsvalue = psdata->epsvalue;
2404 MATrec *mat = lp->matA;
2405
2406 for(i = firstActiveLink(psdata->INTmap); i != 0; i = nextActiveLink(psdata->INTmap, i)) {
2407
2408 /* Obtain the row GCD */
2409 jx = mat->row_end[i - 1];
2410 je = mat->row_end[i];
2411 Rvalue = ROW_MAT_VALUE(jx);
2412 GCDvalue = abs((int) Rvalue);
2413 jx++;
2414 if(jx < je)
2415 for(; (jx < je) && (GCDvalue > 1); jx++) {
2416 Rvalue = fabs(ROW_MAT_VALUE(jx));
2417 GCDvalue = gcd((LLONG) Rvalue, GCDvalue, NULL, NULL);
2418 }
2419
2420 /* Reduce the coefficients, if possible */
2421 if(GCDvalue > 1) {
2422 jx = mat->row_end[i - 1];
2423 je = mat->row_end[i];
2424 for(; jx < je; jx++) {
2425 Avalue = &ROW_MAT_VALUE(jx);
2426 *Avalue /= GCDvalue;
2427 in++;
2428 }
2429 Rvalue = (lp->orig_rhs[i] / GCDvalue) + epsvalue;
2430 lp->orig_rhs[i] = floor(Rvalue);
2431 Rvalue = fabs(lp->orig_rhs[i]-Rvalue);
2432 if(is_constr_type(lp, i, EQ) && (Rvalue > epsvalue)) {
2433 report(lp, NORMAL, "presolve_reduceGCD: Infeasible equality constraint %d\n", i);
2434 status = FALSE;
2435 break;
2436 }
2437 if(!my_infinite(lp, lp->orig_upbo[i]))
2438 lp->orig_upbo[i] = floor(lp->orig_upbo[i] / GCDvalue);
2439 ib++;
2440 }
2441 }
2442 if(status && (in > 0))
2443 report(lp, DETAILED, "presolve_reduceGCD: Did %d constraint coefficient reductions.\n", in);
2444
2445 (*nn) += in;
2446 (*nb) += ib;
2447 (*nsum) += in + ib;
2448
2449 return( status );
2450 }
2451
presolve_knapsack(presolverec * psdata,int * nn)2452 STATIC int presolve_knapsack(presolverec *psdata, int *nn)
2453 {
2454 lprec *lp = psdata->lp;
2455 int m, n, i, ix, j, jx, colnr, *rownr = NULL,
2456 status = RUNNING;
2457 REAL *colOF = lp->orig_obj, value, *ratio = NULL;
2458 LLrec *map = psdata->EQmap;
2459 MATrec *mat = lp->matA;
2460
2461 /* Check if it is worth trying */
2462 m = mat->row_end[0];
2463 if((map->count == 0) || (m < 2))
2464 return( status );
2465
2466 /* Get the OF row */
2467 allocINT(lp, &rownr, map->count+1, FALSE);
2468 allocREAL(lp, &ratio, map->count+1, FALSE);
2469
2470 /* Loop over each row trying to find equal entries in the OF */
2471 rownr[0] = 0;
2472 for(i = firstActiveLink(map); i != 0; i = nextActiveLink(map, i)) {
2473 if(get_rh(lp, i) <= 0)
2474 continue;
2475 jx = mat->row_end[i];
2476 n = 0;
2477 for(j = mat->row_end[i-1]; j < jx; j++, n++) {
2478 colnr = ROW_MAT_COLNR(j);
2479 value = ROW_MAT_VALUE(j);
2480 if(colOF[colnr] == 0)
2481 break;
2482 if(n == 0) {
2483 ratio[0] = colOF[colnr] / value;
2484 }
2485 else if(fabs(value * ratio[0] - colOF[colnr]) > psdata->epsvalue) {
2486 n = -1;
2487 break;
2488 }
2489 }
2490 /* Register row if we were successful (and row long enough) */
2491 if(n >= 2) {
2492 ix = ++rownr[0];
2493 rownr[ix] = i;
2494 ratio[ix] = ratio[0];
2495 }
2496 }
2497 n = rownr[0];
2498 if(n == 0)
2499 goto Finish;
2500
2501 /* Process the identified rows, eliminating the OF value */
2502 for(ix = 1; ix <= n; ix++) {
2503 i = rownr[ix];
2504 jx = mat->row_end[i];
2505 for(j = mat->row_end[i-1]; j < jx; j++) {
2506 colnr = ROW_MAT_COLNR(j);
2507 colOF[colnr] = 0;
2508 }
2509 }
2510
2511 /* Update key mapper structures */
2512 j = lp->columns;
2513 psdata->cols->varmap = cloneLink(psdata->cols->varmap, j+n, TRUE);
2514 psdata->forceupdate = TRUE;
2515
2516 /* Finally, add helper columns */
2517 for(ix = 1; ix <= n; ix++) {
2518 i = rownr[ix];
2519 rownr[0] = 0;
2520 colOF[0] = my_chsign(is_maxim(lp), ratio[ix]);
2521 rownr[1] = i;
2522 colOF[1] = -1;
2523 value = get_rh(lp, i);
2524 /* j = get_constr_type(lp, i); */
2525 add_columnex(lp, 2, colOF, rownr);
2526 set_bounds(lp, lp->columns, value, value);
2527 /* presolve_setEQ(psdata, i); */
2528 set_rh(lp, i, 0);
2529 appendLink(psdata->cols->varmap, j+ix);
2530 }
2531 presolve_validate(psdata, TRUE);
2532
2533 /* Clean up before returning */
2534 Finish:
2535 FREE(rownr);
2536 FREE(ratio);
2537 (*nn) += n;
2538
2539 return( status );
2540 }
2541
presolve_invalideq2(lprec * lp,presolverec * psdata)2542 STATIC MYBOOL presolve_invalideq2(lprec *lp, presolverec *psdata)
2543 {
2544 int jx, jjx, i = 0, item;
2545 MATrec *mat = lp->matA;
2546 MYBOOL error = FALSE;
2547
2548 do {
2549
2550 if(i == 0)
2551 i = firstActiveLink(psdata->EQmap);
2552 else
2553 i = nextActiveLink(psdata->EQmap, i);
2554 if(i == 0)
2555 return( error );
2556
2557 /* Get the row index of the first 2-element equality */
2558 for(; i > 0; i = nextActiveLink(psdata->EQmap, i))
2559 if(presolve_rowlength(psdata, i) == 2)
2560 break;
2561 if(i == 0)
2562 return( error );
2563
2564 /* Get the first column */
2565 item = 0;
2566 jx = presolve_nextcol(psdata, i, &item);
2567 if(jx < 0)
2568 error = TRUE;
2569 jx = ROW_MAT_COLNR(jx);
2570
2571 /* Get the second column */
2572 jjx = presolve_nextcol(psdata, i, &item);
2573 if(jjx < 0)
2574 error = AUTOMATIC;
2575 } while(!error);
2576
2577 return( error );
2578 }
2579
2580 /* Callback to obtain the non-zero rows of equality constraints */
presolve_getcolumnEQ(lprec * lp,int colnr,REAL nzvalues[],int nzrows[],int mapin[])2581 int BFP_CALLMODEL presolve_getcolumnEQ(lprec *lp, int colnr, REAL nzvalues[], int nzrows[], int mapin[])
2582 {
2583 int i, ib, ie, nn = 0;
2584 MATrec *mat = lp->matA;
2585
2586 ib = mat->col_end[colnr-1];
2587 ie = mat->col_end[colnr];
2588 for(; ib < ie; ib++) {
2589 i = COL_MAT_ROWNR(ib);
2590 if(!is_constr_type(lp, i, EQ) || /* It has to be an equality constraint */
2591 (mapin[i] == 0)) /* And it should not already have been deleted */
2592 continue;
2593 if(nzvalues != NULL) {
2594 nzrows[nn] = mapin[i];
2595 nzvalues[nn] = COL_MAT_VALUE(ib);
2596 }
2597 nn++;
2598 }
2599 return( nn );
2600 }
presolve_singularities(presolverec * psdata,int * nn,int * nr,int * nv,int * nSum)2601 STATIC int presolve_singularities(presolverec *psdata, int *nn, int *nr, int *nv, int *nSum)
2602 {
2603 lprec *lp = psdata->lp;
2604 int i, j, n, *rmapin = NULL, *rmapout = NULL, *cmapout = NULL;
2605
2606 if(lp->bfp_findredundant(lp, 0, NULL, NULL, NULL) == 0)
2607 return( 0 );
2608
2609 /* Create condensed row map */
2610 allocINT(lp, &rmapin, lp->rows+1, TRUE);
2611 allocINT(lp, &rmapout, psdata->EQmap->count+1, FALSE);
2612 allocINT(lp, &cmapout, lp->columns+1, FALSE);
2613 n = 0;
2614 for(i = firstActiveLink(psdata->EQmap); i != 0; i = nextActiveLink(psdata->EQmap, i)) {
2615 n++;
2616 rmapout[n] = i;
2617 rmapin[i] = n;
2618 }
2619 rmapout[0] = n;
2620 n = 0;
2621 for(i = firstActiveLink(psdata->cols->varmap); i != 0; i = nextActiveLink(psdata->cols->varmap, i)) {
2622 n++;
2623 cmapout[n] = i;
2624 }
2625 cmapout[0] = n;
2626
2627 /* Do the rank-revealing factorization */
2628 n = lp->bfp_findredundant(lp, psdata->EQmap->count, presolve_getcolumnEQ, rmapin, cmapout);
2629
2630 /* Delete the redundant rows */
2631 for(i = 1; i <= n; i++) {
2632 j = rmapin[i];
2633 j = rmapout[j];
2634 presolve_rowremove(psdata, j, TRUE);
2635 }
2636 (*nn) += n;
2637 (*nr) += n;
2638 (*nSum) += n;
2639
2640 /* Clean up */
2641 FREE(rmapout);
2642 FREE(rmapin);
2643 FREE(cmapout);
2644
2645 return( n );
2646 }
2647
presolve_elimeq2(presolverec * psdata,int * nn,int * nr,int * nc,int * nSum)2648 STATIC int presolve_elimeq2(presolverec *psdata, int *nn, int *nr, int *nc, int *nSum)
2649 {
2650 lprec *lp = psdata->lp;
2651 int n, i, jx, jjx, k, item, *plucount, *negcount, colplu, colneg,
2652 iCoeffChanged = 0, iRowsRemoved = 0, iVarsFixed = 0, nrows = lp->rows,
2653 status = RUNNING, *colindex = NULL;
2654 MYBOOL freshupdate;
2655 REAL Coeff1, Coeff2, Value1, Value2, lobound, upbound, bound, test, product,
2656 *colvalue = NULL, *delvalue = NULL, *colitem;
2657 MATrec *mat = lp->matA, *rev = NULL;
2658 DeltaVrec *DV = NULL;
2659 LLrec *EQ2 = NULL;
2660
2661 /* See if there is anything to do */
2662 if(psdata->EQmap->count == 0) {
2663 (*nSum) = 0;
2664 return( status );
2665 }
2666
2667 /* Tally counts */
2668 createLink(lp->rows, &EQ2, NULL);
2669 if((EQ2 == NULL) || !allocREAL(lp, &colvalue, nrows+1, FALSE) ||
2670 !allocREAL(lp, &delvalue, nrows+1, FALSE))
2671 goto Finish;
2672 for(i = firstActiveLink(psdata->EQmap); i > 0; i = nextActiveLink(psdata->EQmap, i)) {
2673 if(presolve_rowlength(psdata, i) == 2)
2674 appendLink(EQ2, i);
2675 }
2676 if(EQ2->count == 0)
2677 goto Finish;
2678 n = 0;
2679
2680 /* Do the elimination loop for all identified 2-element equalities */
2681 for(i = firstActiveLink(EQ2); i > 0; i = nextActiveLink(EQ2, i)) {
2682
2683 /* Check if the constraint has been modified by a previous elimination */
2684 if(presolve_rowlength(psdata, i) != 2)
2685 continue;
2686
2687 /* Get the column indeces of NZ-values of the "pivot" row */
2688 item = 0;
2689 jx = presolve_nextcol(psdata, i, &item); /* Eliminated variable coefficient b */
2690 #ifdef Paranoia
2691 if(jx < 0)
2692 report(lp, SEVERE, "presolve_elimeq2: No qualifying %dst column was found in row %s (ostensible length %d)\n",
2693 1, get_row_name(lp, i), presolve_rowlength(psdata, i));
2694 #endif
2695 Coeff2 = ROW_MAT_VALUE(jx);
2696 jx = ROW_MAT_COLNR(jx);
2697 jjx = presolve_nextcol(psdata, i, &item); /* Non-eliminated variable coefficient a */
2698 #ifdef Paranoia
2699 if(jjx < 0)
2700 report(lp, SEVERE, "presolve_elimeq2: No qualifying %dnd column was found in row %s (ostensible length %d)\n",
2701 2, get_row_name(lp, i), presolve_rowlength(psdata, i));
2702 #endif
2703 Coeff1 = ROW_MAT_VALUE(jjx);
2704 jjx = ROW_MAT_COLNR(jjx);
2705
2706 /* Check if at least one of the coefficients is large enough to preserve stability;
2707 use opposing maximum column values for stability testing. */
2708 if((fabs(Coeff1) < psdata->epspivot*mat->colmax[jx]) &&
2709 ((fabs(Coeff1) != 1) && (fabs(Coeff2) != 1)) &&
2710 (fabs(Coeff2) < psdata->epspivot*mat->colmax[jjx]))
2711 continue;
2712
2713 /* Cannot eliminate a variable if both are SOS members or SC variables */
2714 if((is_semicont(lp, jx) && is_semicont(lp, jjx)) ||
2715 (SOS_is_member(lp->SOS, 0, jx) && SOS_is_member(lp->SOS, 0, jjx)))
2716 continue;
2717
2718 /* First check if we are allowed to swap; set swap "blockers" */
2719 k = 0;
2720 if(!is_int(lp, jx) && is_int(lp, jjx))
2721 k += 1;
2722 else if(!is_semicont(lp, jx) && is_semicont(lp, jjx))
2723 k += 2;
2724 else if(!SOS_is_member(lp->SOS, 0, jx) && SOS_is_member(lp->SOS, 0, jjx))
2725 k += 4;
2726
2727 /* If there were no blockers, determine if we MUST swap the variable to be eliminated */
2728 if(k == 0) {
2729 if(is_int(lp, jx) && !is_int(lp, jjx))
2730 k += 8;
2731 else if(is_semicont(lp, jx) && !is_semicont(lp, jjx))
2732 k += 16;
2733 else if(SOS_is_member(lp->SOS, 0, jx) && !SOS_is_member(lp->SOS, 0, jjx))
2734 k += 32;
2735
2736 /* If we are not forced to swap, decide if it otherwise makes sense - high order */
2737 if(k == 0) {
2738 if((fabs(Coeff2) < psdata->epspivot*mat->colmax[jjx]) &&
2739 (fabs(Coeff1) > psdata->epspivot*mat->colmax[jx]))
2740 k += 64;
2741 else if(presolve_collength(psdata, jx) > presolve_collength(psdata, jjx))
2742 k += 128;
2743 }
2744
2745 /* If we are not forced to swap, decide if it otherwise makes sense - low order */
2746 if(k == 0) {
2747 Value2 = Coeff1/Coeff2;
2748 #ifdef DualFeasibilityLogicEQ2
2749 if((Value2*lp->orig_obj[jx] < 0) &&
2750 (Value2*lp->orig_obj[jjx] > 0)) /* Seek increased dual feasibility */
2751 k += 256;
2752 #endif
2753 #ifdef DivisorIntegralityLogicEQ2
2754 if((fabs(modf(Coeff2, &Value2)) >= lp->epsvalue) && /* Seek integrality of result */
2755 (fabs(modf(Coeff1, &Value2)) < lp->epsvalue))
2756 k += 512;
2757 else if((fabs(fabs(Coeff2)-1) >= lp->epsvalue) && /* Seek integrality of divisor */
2758 (fabs(fabs(Coeff1)-1) < lp->epsvalue))
2759 k += 1024;
2760 #endif
2761 }
2762
2763 }
2764 else
2765 k = 0;
2766
2767 /* Perform variable index swap if indicated */
2768 if(k != 0) {
2769 swapINT(&jx, &jjx);
2770 swapREAL(&Coeff1, &Coeff2);
2771 }
2772
2773 Value1 = lp->orig_rhs[i]/Coeff2; /* Delta constant term */
2774 Value2 = Coeff1/Coeff2; /* Delta variable term */
2775 upbound = lp->orig_upbo[lp->rows+jx];
2776 lobound = lp->orig_lowbo[lp->rows+jx];
2777 if(lp->spx_trace) {
2778 report(lp, DETAILED, "Row %3d : Elim %g %s - %d\n", i, Coeff2, get_col_name(lp, jx), jx);
2779 report(lp, DETAILED, " Keep %g %s - %d\n", Coeff1, get_col_name(lp, jjx), jjx);
2780 }
2781
2782 /* Get the coefficient vectors of the independent (jjx) and dependent (jx) columns;
2783 the dependent column will be deleted and reconstructed during postsolve. */
2784 freshupdate = (MYBOOL) ((colindex == NULL) || (colindex[jjx] == 0));
2785 if(freshupdate)
2786 mat_expandcolumn(mat, jjx, colvalue, NULL, TRUE);
2787 else
2788 mat_expandcolumn(rev, colindex[jjx], colvalue, NULL, FALSE);
2789 if((colindex == NULL) || (colindex[jx] == 0))
2790 mat_expandcolumn(mat, jx, delvalue, NULL, TRUE);
2791 else
2792 mat_expandcolumn(rev, colindex[jx], delvalue, NULL, FALSE);
2793
2794 /* Add variable reconstruction information */
2795 addUndoPresolve(lp, TRUE, jx, Value1, Value2, jjx);
2796
2797 /* If possible, tighten the bounds of the uneliminated variable based
2798 on the bounds of the eliminated variable. Also handle roundings
2799 and attempt precision management. */
2800 bound = lobound;
2801 k = nrows+jjx;
2802 if(bound > -lp->infinite) {
2803 bound = (lp->orig_rhs[i] - Coeff2*bound) / Coeff1;
2804 if(Value2 > 0) {
2805 test = lp->orig_upbo[k];
2806 if(bound < test - psdata->epsvalue) {
2807 if(is_int(lp, jjx))
2808 lp->orig_upbo[k] = floor(bound + lp->epsint);
2809 else
2810 lp->orig_upbo[k] = presolve_roundrhs(lp, bound, FALSE);
2811 }
2812 }
2813 else {
2814 test = lp->orig_lowbo[k];
2815 if(bound > test + psdata->epsvalue) {
2816 if(is_int(lp, jjx))
2817 lp->orig_lowbo[k] = ceil(bound - lp->epsint);
2818 else
2819 lp->orig_lowbo[k] = presolve_roundrhs(lp, bound, TRUE);
2820 }
2821 }
2822 }
2823 bound = upbound;
2824 if(bound < lp->infinite) {
2825 bound = (lp->orig_rhs[i] - Coeff2*bound) / Coeff1;
2826 if(Value2 < 0) {
2827 test = lp->orig_upbo[k];
2828 if(bound < test - psdata->epsvalue) {
2829 if(is_int(lp, jjx))
2830 lp->orig_upbo[k] = floor(bound + lp->epsint);
2831 else
2832 lp->orig_upbo[k] = presolve_roundrhs(lp, bound, FALSE);
2833 }
2834 }
2835 else {
2836 test = lp->orig_lowbo[k];
2837 if(bound > test + psdata->epsvalue) {
2838 if(is_int(lp, jjx))
2839 lp->orig_lowbo[k] = ceil(bound - lp->epsint);
2840 else
2841 lp->orig_lowbo[k] = presolve_roundrhs(lp, bound, TRUE);
2842 }
2843 }
2844 }
2845
2846 #ifdef Eq2Reldiff
2847 test = 2*lp->epsvalue;
2848 #else
2849 test = psdata->epsvalue;
2850 #endif
2851 if(/*(lp->orig_upbo[k] < lp->orig_lowbo[k]) ||*/
2852 #ifdef Eq2Reldiff
2853 (fabs(my_reldiff(lp->orig_upbo[k],lp->orig_lowbo[k])) < test)) {
2854 #else
2855 (fabs(lp->orig_upbo[k] - lp->orig_lowbo[k]) < test)) {
2856 #endif
2857 my_roundzero(lp->orig_lowbo[k], test);
2858 lp->orig_upbo[k] = lp->orig_lowbo[k];
2859 }
2860 else {
2861 my_roundzero(lp->orig_upbo[k], test);
2862 my_roundzero(lp->orig_lowbo[k], test);
2863 }
2864
2865 if(/*(upbound < lobound) ||*/
2866 #ifdef Eq2Reldiff
2867 (fabs(my_reldiff(upbound, lobound)) < test)) {
2868 #else
2869 (fabs(upbound - lobound) < test)) {
2870 #endif
2871 my_roundzero(lobound, test);
2872 lp->orig_upbo[nrows+jx] = lobound;
2873 upbound = lobound;
2874 }
2875
2876 /* Loop over the non-zero rows of the column (jx) to be eliminated;
2877 substitute jx-variable by updating rhs and jjx coefficients */
2878 colitem = colvalue;
2879 plucount = psdata->rows->plucount;
2880 negcount = psdata->rows->negcount;
2881 colplu = 0;
2882 colneg = 0;
2883 /* Count of non-zeros in the independent column jjx */
2884 item = presolve_collength(psdata, jjx) - 1;
2885 if(isnz_origobj(lp, jjx))
2886 item++;
2887 for(k = 0; k <= nrows; k++, colitem++) {
2888
2889 bound = delvalue[k];
2890 if((k == i) || (bound == 0) ||
2891 ((k > 0) && !isActiveLink(psdata->rows->varmap, k)))
2892 continue;
2893
2894 /* Do constraint and nz-count updates for the substituted variable */
2895 product = bound*Value1;
2896
2897 /* "Raw"/unsigned data */
2898 presolve_adjustrhs(psdata, k, my_chsign(is_chsign(lp, k), product), test);
2899
2900 /* Change back to signed part */
2901 if(*colitem != 0) {
2902 if(*colitem > 0) {
2903 colplu--;
2904 plucount[k]--;
2905 }
2906 else {
2907 colneg--;
2908 negcount[k]--;
2909 }
2910 if((lobound < 0) && (upbound >= 0)) {
2911 psdata->cols->pluneg[jjx]--;
2912 psdata->rows->pluneg[k]--;
2913 }
2914 item--;
2915 }
2916 (*colitem) -= bound*Value2;
2917 iCoeffChanged++;
2918
2919 /* Update counts */
2920 if(fabs(*colitem) >= mat->epsvalue) {
2921 if(*colitem > 0) {
2922 colplu++;
2923 plucount[k]++;
2924 }
2925 else {
2926 colneg++;
2927 negcount[k]++;
2928 }
2929 if((lobound < 0) && (upbound >= 0)) {
2930 psdata->cols->pluneg[jjx]++;
2931 psdata->rows->pluneg[k]++;
2932 }
2933 item++;
2934 }
2935 else {
2936 *colitem = 0;
2937 }
2938
2939 /* Also reduce count if the row contains the deleted variable */
2940 if(bound > 0)
2941 plucount[k]--;
2942 else
2943 negcount[k]--;
2944 }
2945 psdata->cols->plucount[jjx] += colplu;
2946 psdata->cols->negcount[jjx] += colneg;
2947
2948 /* Save the new column */
2949 if(rev == NULL) {
2950 DV = createUndoLadder(lp, nrows, lp->columns / RESIZEFACTOR);
2951 rev = DV->tracker;
2952 rev->epsvalue = mat->epsvalue;
2953 allocINT(lp, &(rev->col_tag), mat->columns_alloc+1, FALSE);
2954 allocINT(lp, &colindex, lp->columns+1, TRUE);
2955 rev->col_tag[0] = 0;
2956 }
2957 n = rev->col_tag[0] = incrementUndoLadder(DV);
2958 mat_setcol(rev, n, 0, colvalue, NULL, FALSE, FALSE);
2959 rev->col_tag[n] = jjx;
2960
2961 /* Save index to updated vector, but specially handle case where we have
2962 the same independent variable for multiple equations! */
2963 if(!freshupdate)
2964 rev->col_tag[colindex[jjx]] *= -1;
2965 colindex[jjx] = n;
2966
2967 /* Delete the column dependent variable */
2968 jx = presolve_colremove(psdata, jx, FALSE);
2969 iVarsFixed++;
2970
2971 /* Check if we have been lucky enough to have eliminated the independent
2972 variable via substitution of the dependent variable */
2973 if(item == 0) {
2974 #ifdef Paranoia
2975 report(lp, DETAILED, "presolve_elimeq2: Was able to remove variables %d and %d in row %s\n",
2976 jx, jjx, get_row_name(lp, i));
2977 #endif
2978 if(presolve_colfix(psdata, jjx, 0.0, TRUE, nc))
2979 jjx = presolve_colremove(psdata, jjx, FALSE);
2980 }
2981
2982 /* Delete the row */
2983 presolve_rowremove(psdata, i, FALSE);
2984 iRowsRemoved++;
2985 }
2986
2987 /* Perform the column updates collected above */
2988 if(n > 0) {
2989 mat_mapreplace(mat, psdata->rows->varmap, psdata->cols->varmap, rev);
2990 presolve_validate(psdata, TRUE);
2991 #ifdef PresolveForceUpdateMax
2992 mat_computemax(mat /* , FALSE */);
2993 #endif
2994 psdata->forceupdate = TRUE;
2995 }
2996
2997 /* Free work arrays */
2998 Finish:
2999 if(DV != NULL)
3000 freeUndoLadder(&DV);
3001 freeLink(&EQ2);
3002 FREE(colvalue);
3003 FREE(delvalue);
3004 FREE(colindex);
3005
3006 /* Update counters */
3007 (*nn) += iCoeffChanged;
3008 (*nr) += iRowsRemoved;
3009 (*nc) += iVarsFixed;
3010 (*nSum) += iCoeffChanged + iRowsRemoved + iVarsFixed;
3011
3012 return( status );
3013 }
3014
3015 STATIC MYBOOL presolve_impliedfree(lprec *lp, presolverec *psdata, int colnr)
3016 {
3017 int i, ix, ie;
3018 REAL Tlower, Tupper;
3019 MYBOOL status, rowbinds, isfree = FALSE;
3020 MATrec *mat = lp->matA;
3021
3022 if(my_infinite(lp, get_lowbo(lp, colnr)) && my_infinite(lp, get_upbo(lp, colnr)))
3023 return( TRUE );
3024
3025 ie = mat->col_end[colnr];
3026 for(ix = mat->col_end[colnr-1]; (isfree != (TRUE | AUTOMATIC)) && (ix < ie); ix++) {
3027 i = COL_MAT_ROWNR(ix);
3028 if(!isActiveLink(psdata->rows->varmap, i))
3029 continue;
3030 Tlower = get_rh_lower(lp, i);
3031 Tupper = get_rh_upper(lp, i);
3032 status = presolve_multibounds(psdata, i, colnr, &Tlower, &Tupper, NULL, &rowbinds);
3033 isfree = isfree | status | rowbinds;
3034 }
3035
3036 return( (MYBOOL) (isfree == (TRUE | AUTOMATIC)) );
3037 }
3038
3039 STATIC MYBOOL presolve_impliedcolfix(presolverec *psdata, int rownr, int colnr, MYBOOL isfree)
3040 {
3041 lprec *lp = psdata->lp;
3042 MYBOOL signflip, undoadded = FALSE;
3043 MATrec *mat = lp->matA;
3044 int jx, i, ib, ie = mat->row_end[rownr];
3045 REAL varLo = 0, varHi = 0, varRange, conRange = 0, matValue = 0, dual, RHS = lp->orig_rhs[rownr],
3046 pivot, matAij = mat_getitem(mat, rownr, colnr), *vecOF = lp->orig_obj;
3047
3048 /* We cannot have semi-continuous or non-qualifying integers */
3049 if(is_semicont(lp, colnr) || is_SOS_var(lp, colnr))
3050 return( FALSE );
3051 if(is_int(lp, colnr)) {
3052 if(!isActiveLink(psdata->INTmap, rownr) || !is_presolve(lp, PRESOLVE_KNAPSACK))
3053 return( FALSE );
3054 /* colnr must have a coefficient equal to the smallest in the row */
3055 varRange = lp->infinite;
3056 i = 0;
3057 pivot = 0;
3058 for(ib = presolve_nextcol(psdata, rownr, &i); i != 0; ib = presolve_nextcol(psdata, rownr, &i)) {
3059 jx = ROW_MAT_COLNR(ib);
3060 dual = fabs(ROW_MAT_VALUE(ib));
3061 /* Check if we have the target column and save the pivot value */
3062 if(jx == colnr) {
3063 /* Always accept unit coefficient */
3064 if(fabs(dual - 1) < psdata->epsvalue)
3065 break;
3066 pivot = dual;
3067 /* Otherwise continue scan */
3068 }
3069 /* Cannot accept case where result can be fractional */
3070 else if((pivot > dual + psdata->epsvalue) ||
3071 ((pivot > 0) && (fabs(fmod(dual, pivot)) > psdata->epsvalue)))
3072 return( FALSE );
3073 }
3074 }
3075
3076 /* Ascertain that the pivot value is large enough to preserve stability */
3077 pivot = matAij;
3078 if(fabs(pivot) < psdata->epspivot*mat->colmax[colnr])
3079 return( FALSE );
3080
3081 /* Must ascertain that the row variables are not SOS'es; this is because
3082 the eliminated variable will be a function of another variable. */
3083 if(SOS_count(lp) > 0) {
3084 for(ib = mat->row_end[rownr-1]; ib < ie; ib++)
3085 if(SOS_is_member(lp->SOS, 0, ROW_MAT_COLNR(ib)))
3086 return( FALSE );
3087 }
3088
3089 /* Calculate the dual value */
3090 dual = vecOF[colnr]/pivot;
3091
3092 /* Here we have free variable in an equality constraint; this means we can
3093 can adjust the OF for the deleted variable and also delete the constraint. */
3094 if(isfree && is_constr_type(lp, rownr, EQ)) {
3095 matValue = RHS/pivot;
3096 if(matValue != 0)
3097 undoadded = addUndoPresolve(lp, TRUE, colnr, matValue, 0.0, 0);
3098 }
3099
3100 else {
3101
3102 /* IMPLIEDFREE: For simplicity, ensure that we can keep the slack based at 0,
3103 and not its upper bound. Effectively, we consider the constraint
3104 an equality, using the information of the sign of the dual.
3105 IMPLIEDSLK: Since we already have an equality constraint, we wish to make sure
3106 that the ensuing inequality constraint will have an RHS that is
3107 non-infinite. */
3108 if(isfree) {
3109 SETMIN(RHS, presolve_sumplumin(lp, rownr, psdata->rows, TRUE));
3110 matValue = presolve_sumplumin(lp, rownr, psdata->rows, FALSE);
3111 conRange = get_rh_lower(lp, rownr);
3112 conRange = RHS - MAX(matValue, conRange);
3113 signflip = (MYBOOL) ((dual > 0) &&
3114 !my_infinite(lp, conRange));
3115 }
3116 else {
3117 varLo = get_lowbo(lp, colnr);
3118 varLo *= (my_infinite(lp, varLo) ? my_sign(pivot) : pivot);
3119 varHi = get_upbo(lp, colnr);
3120 varHi *= (my_infinite(lp, varHi) ? my_sign(pivot) : pivot);
3121 if(pivot < 0)
3122 swapREAL(&varHi, &varLo);
3123 signflip = my_infinite(lp, varLo);
3124 }
3125 if(signflip) {
3126 mat_multrow(mat, rownr, -1);
3127 RHS -= conRange;
3128 RHS = -RHS;
3129 lp->orig_rhs[rownr] = RHS;
3130 pivot = -pivot;
3131 dual = -dual;
3132 if(!isfree) {
3133 varLo = -varLo;
3134 varHi = -varHi;
3135 swapREAL(&varHi, &varLo);
3136 }
3137 }
3138 matValue = RHS/pivot;
3139
3140 /* Prepare for deleting free or implied free variable in inequality constraint.
3141 Different strategies need to be used:
3142
3143 ACTUAL: Find the proper constraint bound and store undo information for
3144 recovering the value of the implied free variable. The constraint
3145 is then deleted. We have to adjust the objective function if the
3146 OF coefficient for the implied free variable is non-zero.
3147 IMPLIED: Convert the constraint to an inequality at the proper bound.
3148 For given models, the new equality constraint can later provide
3149 an implied slack, which means that a further variable is eliminated,
3150 and the constraint again becomes an inequality constraint.
3151
3152 Note that this version only implements the ACTUAL mode */
3153 if(isfree) {
3154 /* Add undo information connecting the deleted variable to the RHS */
3155 if(matValue != 0)
3156 undoadded = addUndoPresolve(lp, TRUE, colnr, matValue, 0.0, 0);
3157 /* Add undo information for the dual of the deleted constraint */
3158 if(dual != 0)
3159 addUndoPresolve(lp, FALSE, rownr, dual, 0.0, 0);
3160 }
3161
3162 /* Prepare for deleting implied slack variable. The following two cases are
3163 handled:
3164
3165 1. Equality constraint: Convert the constraint to an inequality constraint
3166 that is possibly ranged
3167 2. Other constraints: Expand existing slack variable / constraint
3168 range, if required. */
3169 else {
3170 if(my_infinite(lp, varHi))
3171 varRange = lp->infinite;
3172 #ifdef Paranoia
3173 else if(my_infinite(lp, varLo)) {
3174 report(lp, SEVERE, "presolve_impliedcolfix: Negative infinite limit for variable %d\n", colnr);
3175 varRange = lp->infinite;
3176 }
3177 #endif
3178 else
3179 varRange = my_precision(fabs(varHi - varLo) + lp->epsvalue, psdata->epsvalue);
3180 presolve_adjustrhs(psdata, rownr, varLo, psdata->epsvalue);
3181
3182 /* Handle case 1 of an equality constraint */
3183 if(is_constr_type(lp, rownr, EQ)) {
3184 /* Make sure we actually have a ranged constraint */
3185 if(varRange > 0) {
3186 set_constr_type(lp, rownr, LE);
3187 if(!my_infinite(lp, varRange))
3188 lp->orig_upbo[rownr] = varRange;
3189 setLink(psdata->LTmap, rownr);
3190 removeLink(psdata->EQmap, rownr);
3191 }
3192 }
3193 /* Handle case 2 of an inequality constraint (UNDER CONSTRUCTION!)*/
3194 else {
3195 if(!my_infinite(lp, lp->orig_upbo[rownr])) {
3196 if(my_infinite(lp, varRange))
3197 lp->orig_upbo[rownr] = lp->infinite;
3198 else
3199 lp->orig_upbo[rownr] += varHi - varLo;
3200 }
3201 }
3202 /* Update counts */
3203 if(matAij > 0)
3204 psdata->rows->plucount[rownr]--;
3205 else
3206 psdata->rows->negcount[rownr]--;
3207 if(my_sign(varLo) != my_sign(varHi))
3208 psdata->rows->pluneg[rownr]--;
3209
3210 /* Add undo information for the deleted variable; note that we cannot link the
3211 deleted variable to the slack, since it may not be available during undo.
3212 We really should have a mini LP to compute this allocation ex-post. */
3213 if(RHS != 0)
3214 undoadded = addUndoPresolve(lp, TRUE, colnr, RHS/pivot, 0.0, 0);
3215 }
3216 }
3217
3218 /* Update the OF constant */
3219 if(dual != 0) {
3220 presolve_adjustrhs(psdata, 0, dual * RHS, 0);
3221 /* lp->orig_rhs[0] -= dual * RHS; */
3222 vecOF[colnr] = 0;
3223 }
3224
3225 /* Do affine transformation with the constraint row */
3226 i = 0;
3227 for(ib = presolve_nextcol(psdata, rownr, &i); ib >= 0;
3228 ib = presolve_nextcol(psdata, rownr, &i)) {
3229
3230 /* Get the constraint element */
3231 jx = ROW_MAT_COLNR(ib);
3232 if(jx == colnr)
3233 continue;
3234 matValue = ROW_MAT_VALUE(ib);
3235
3236 /* Adjust OF for the variable to be deleted */
3237 if(dual != 0)
3238 vecOF[jx] -= dual * matValue;
3239
3240 /* Add reconstruction/undo parameters for the deleted variable */
3241 if(!undoadded)
3242 undoadded = addUndoPresolve(lp, TRUE, colnr, 0.0, matValue/pivot, jx);
3243 else
3244 appendUndoPresolve(lp, TRUE, matValue/pivot, jx);
3245 }
3246
3247 return( TRUE );
3248 }
3249
3250 STATIC psrec *presolve_initpsrec(lprec *lp, int size)
3251 {
3252 psrec *ps = (psrec *) calloc(1, sizeof(*ps));
3253
3254 createLink(size, &ps->varmap, NULL);
3255 fillLink(ps->varmap);
3256
3257 size++;
3258
3259 allocINT(lp, &ps->empty, size, FALSE);
3260 ps->empty[0] = 0;
3261
3262 allocREAL(lp, &ps->pluupper, size, FALSE);
3263 allocREAL(lp, &ps->negupper, size, FALSE);
3264 allocREAL(lp, &ps->plulower, size, FALSE);
3265 allocREAL(lp, &ps->neglower, size, FALSE);
3266 allocINT(lp, &ps->infcount, size, FALSE);
3267
3268 ps->next = (int **) calloc(size, sizeof(*(ps->next)));
3269
3270 allocINT(lp, &ps->plucount, size, TRUE);
3271 allocINT(lp, &ps->negcount, size, TRUE);
3272 allocINT(lp, &ps->pluneg, size, TRUE);
3273
3274 ps->allocsize = size;
3275
3276 return( ps );
3277 }
3278 STATIC void presolve_freepsrec(psrec **ps)
3279 {
3280 FREE((*ps)->plucount);
3281 FREE((*ps)->negcount);
3282 FREE((*ps)->pluneg);
3283 FREE((*ps)->infcount);
3284
3285 if((*ps)->next != NULL) {
3286 int i, n = (*ps)->allocsize;
3287 for(i = 0; i < n; i++)
3288 FREE((*ps)->next[i]);
3289 FREE((*ps)->next);
3290 }
3291
3292 FREE((*ps)->plulower);
3293 FREE((*ps)->neglower);
3294 FREE((*ps)->pluupper);
3295 FREE((*ps)->negupper);
3296
3297 FREE((*ps)->empty);
3298
3299 freeLink(&(*ps)->varmap);
3300
3301 FREE(*ps);
3302 }
3303
3304 STATIC presolverec *presolve_init(lprec *lp)
3305 {
3306 int k, i, ix, ixx, colnr,
3307 ncols = lp->columns,
3308 nrows = lp->rows;
3309 REAL hold;
3310 MATrec *mat = lp->matA;
3311 presolverec *psdata = NULL;
3312
3313 /* Optimize memory usage if we have a very large model;
3314 this is to reduce the risk of out-of-memory situations. */
3315 ix = get_nonzeros(lp);
3316 ixx = lp->matA->mat_alloc;
3317 if((ixx - ix > MAT_START_SIZE) && ((ixx - ix) * 20 > ixx))
3318 mat_memopt(lp->matA, nrows / 20, ncols / 20, ix / 20);
3319
3320 psdata = (presolverec *) calloc(1, sizeof(*psdata));
3321
3322 psdata->lp = lp;
3323 psdata->rows = presolve_initpsrec(lp, nrows);
3324 psdata->cols = presolve_initpsrec(lp, ncols);
3325
3326 psdata->epsvalue = PRESOLVE_EPSVALUE;
3327 psdata->epspivot = PRESOLVE_EPSPIVOT;
3328 psdata->forceupdate = TRUE;
3329
3330 /* Save incoming primal bounds */
3331 k = lp->sum + 1;
3332 allocREAL(lp, &psdata->pv_lobo, k, FALSE);
3333 MEMCOPY(psdata->pv_lobo, lp->orig_lowbo, k);
3334 allocREAL(lp, &psdata->pv_upbo, k, FALSE);
3335 MEMCOPY(psdata->pv_upbo, lp->orig_upbo, k);
3336
3337 /* Create and initialize dual value (Langrangean and slack) limits */
3338 allocREAL(lp, &psdata->dv_lobo, k, FALSE);
3339 allocREAL(lp, &psdata->dv_upbo, k, FALSE);
3340 for(i = 0; i <= nrows; i++) {
3341 psdata->dv_lobo[i] = (is_constr_type(lp, i, EQ) ? -lp->infinite : 0);
3342 psdata->dv_upbo[i] = lp->infinite;
3343 }
3344 k--;
3345 for(; i <= k; i++) {
3346 psdata->dv_lobo[i] = 0;
3347 psdata->dv_upbo[i] = lp->infinite;
3348 }
3349
3350 /* Create NZ count and sign arrays, and do general initialization of row bounds */
3351 createLink(nrows, &psdata->EQmap, NULL);
3352 createLink(nrows, &psdata->LTmap, NULL);
3353 createLink(nrows, &psdata->INTmap, NULL);
3354 for(i = 1; i <= nrows; i++) {
3355 switch (get_constr_type(lp, i)) {
3356 case LE: appendLink(psdata->LTmap, i);
3357 break;
3358 case EQ: appendLink(psdata->EQmap, i);
3359 break;
3360 }
3361 k = mat_rowlength(mat, i);
3362 if((lp->int_vars > 0) && (k > 0))
3363 appendLink(psdata->INTmap, i);
3364 }
3365
3366 /* Seek to reduce set of sum(INT*INT) rows (mainly for GCD coefficient reductions) */
3367 if(psdata->INTmap->count > 0)
3368 for(i = 1; i <= nrows; i++) {
3369 if(!isActiveLink(psdata->INTmap, i))
3370 continue;
3371 /* Disqualify if there is a non-int variable, otherwise find smallest absolute fractional row value */
3372 ix = mat->row_end[i - 1];
3373 ixx = mat->row_end[i];
3374 colnr = 0;
3375 for(; ix < ixx; ix++) {
3376 if(!is_int(lp, ROW_MAT_COLNR(ix))) {
3377 removeLink(psdata->INTmap, i);
3378 break;
3379 }
3380 hold = fabs(ROW_MAT_VALUE(ix));
3381 hold = fmod(hold, 1);
3382 /* Adjust colnr to be a decimal scalar */
3383 for(k = 0; (k <= MAX_FRACSCALE) && (hold+psdata->epsvalue < 1); k++)
3384 hold *= 10;
3385 if(k > MAX_FRACSCALE) {
3386 removeLink(psdata->INTmap, i);
3387 break;
3388 }
3389 SETMAX(colnr, k);
3390 }
3391 if(!isActiveLink(psdata->INTmap, i))
3392 continue;
3393 hold = pow(10.0, colnr);
3394 /* Also disqualify if the RHS is fractional after scaling */
3395 if(fabs(fmod(lp->orig_rhs[i] * hold, 1)) > psdata->epsvalue) {
3396 removeLink(psdata->INTmap, i);
3397 continue;
3398 }
3399 /* We have an all-int constraint, see if we should scale it up */
3400 if(k > 0) {
3401 ix = mat->row_end[i - 1];
3402 for(; ix < ixx; ix++) {
3403 ROW_MAT_VALUE(ix) *= hold;
3404 }
3405 lp->orig_rhs[i] *= hold;
3406 if(!my_infinite(lp, lp->orig_upbo[i]))
3407 lp->orig_upbo[i] *= hold; /* KE: Fix due to Andy Loto - 20070619 */
3408 }
3409 }
3410
3411 /* Do the real tallying and ordering work */
3412 presolve_validate(psdata, TRUE);
3413
3414 return( psdata );
3415 }
3416
3417 STATIC void presolve_free(presolverec **psdata)
3418 {
3419 presolve_freepsrec(&(*psdata)->rows);
3420 presolve_freepsrec(&(*psdata)->cols);
3421 FREE((*psdata)->dv_lobo);
3422 FREE((*psdata)->dv_upbo);
3423 FREE((*psdata)->pv_lobo);
3424 FREE((*psdata)->pv_upbo);
3425 freeLink(&(*psdata)->EQmap);
3426 freeLink(&(*psdata)->LTmap);
3427 freeLink(&(*psdata)->INTmap);
3428 FREE(*psdata);
3429 }
3430
3431 STATIC int presolve_makefree(presolverec *psdata)
3432 {
3433 lprec *lp = psdata->lp;
3434 int i, ix, j, nn = 0;
3435 REAL Xlower, Xupper, losum, upsum, lorhs, uprhs, freeinf = lp->infinite / 10;
3436 MATrec *mat = lp->matA;
3437 LLrec *colLL = NULL;
3438
3439 /* First see if we can relax ranged constraints */
3440 for(i = firstActiveLink(psdata->rows->varmap); i != 0; i = nextActiveLink(psdata->rows->varmap, i)) {
3441 if(is_constr_type(lp, i, EQ))
3442 continue;
3443 presolve_range(lp, i, psdata->rows, &losum, &upsum);
3444 lorhs = get_rh_lower(lp, i);
3445 uprhs = get_rh_upper(lp, i);
3446
3447 /* Look for opportunity to relax constraint bounds */
3448 if(presolve_rowlength(psdata, i) > 1) {
3449 if((is_constr_type(lp, i, GE) && (upsum <= uprhs)) ||
3450 (is_constr_type(lp, i, LE) && (losum >= lorhs)))
3451 set_rh_range(lp, i, lp->infinite);
3452 }
3453 }
3454
3455 /* Collect columns available for bound relaxation (find implied free variables)
3456 (consider sorting the list in decending order of column lengths or do call to
3457 COLAMD to maximize impact) */
3458 createLink(lp->columns, &colLL, NULL);
3459 for(j = firstActiveLink(psdata->cols->varmap); j != 0; j = nextActiveLink(psdata->cols->varmap, j))
3460 if(presolve_impliedfree(lp, psdata, j))
3461 appendLink(colLL, j);
3462
3463 /* Find what columns to relax (ideally one per row) */
3464 if(colLL->count > 0) {
3465 LLrec *rowLL = NULL;
3466 MYBOOL canfree;
3467
3468 /* Create row tracker */
3469 createLink(lp->rows, &rowLL, NULL);
3470 fillLink(rowLL);
3471
3472 /* Loop over all column candidates */
3473 for(j = firstActiveLink(colLL); (j > 0) && (rowLL->count > 0); j = nextActiveLink(colLL, j)) {
3474
3475 /* Verify that the variable is applicable */
3476 canfree = TRUE;
3477 for(ix = mat->col_end[j-1]; canfree && (ix < mat->col_end[j]); ix++)
3478 canfree = isActiveLink(rowLL, COL_MAT_ROWNR(ix));
3479
3480 /* If so, then open the bounds and update the row availability mapper */
3481 if(canfree) {
3482 nn++;
3483 Xlower = get_lowbo(lp, j);
3484 Xupper = get_upbo(lp, j);
3485 if(Xlower >= 0)
3486 set_bounds(lp, j, 0, freeinf);
3487 else if(Xupper <= 0)
3488 set_bounds(lp, j, -freeinf, 0);
3489 else
3490 /* set_bounds(lo, j, -freeinf, freeinf); */
3491 set_unbounded(lp, j);
3492 for(ix = mat->col_end[j-1]; ix < mat->col_end[j]; ix++)
3493 removeLink(rowLL, COL_MAT_ROWNR(ix));
3494 }
3495 }
3496 freeLink(&rowLL);
3497 }
3498
3499 /* Free list and return */
3500 freeLink(&colLL);
3501 return( nn );
3502 }
3503
3504 STATIC MYBOOL presolve_updatesums(presolverec *psdata)
3505 {
3506 lprec *lp = psdata->lp;
3507 int j;
3508
3509 /* Initialize row accumulation arrays */
3510 MEMCLEAR(psdata->rows->pluupper, lp->rows + 1);
3511 MEMCLEAR(psdata->rows->negupper, lp->rows + 1);
3512 MEMCLEAR(psdata->rows->plulower, lp->rows + 1);
3513 MEMCLEAR(psdata->rows->neglower, lp->rows + 1);
3514 MEMCLEAR(psdata->rows->infcount, lp->rows + 1);
3515
3516 /* Loop over active columns */
3517 for(j = firstActiveLink(psdata->cols->varmap); j != 0;
3518 j = nextActiveLink(psdata->cols->varmap, j)) {
3519 presolve_colfix(psdata, j, lp->infinite, FALSE, NULL);
3520 }
3521
3522 #ifdef UseDualPresolve
3523 /* Initialize column accumulation arrays */
3524 MEMCLEAR(psdata->cols->pluupper, lp->columns + 1);
3525 MEMCLEAR(psdata->cols->negupper, lp->columns + 1);
3526 MEMCLEAR(psdata->cols->plulower, lp->columns + 1);
3527 MEMCLEAR(psdata->cols->neglower, lp->columns + 1);
3528 MEMCLEAR(psdata->cols->infcount, lp->columns + 1);
3529
3530 /* Loop over active rows */
3531 for(j = firstActiveLink(psdata->rows->varmap); j != 0;
3532 j = nextActiveLink(psdata->rows->varmap, j)) {
3533 presolve_rowfix(psdata, j, lp->infinite, FALSE, NULL);
3534 }
3535 #endif
3536
3537 return( TRUE );
3538 }
3539
3540 STATIC MYBOOL presolve_finalize(presolverec *psdata)
3541 {
3542 lprec *lp = psdata->lp;
3543 MYBOOL compactvars = FALSE;
3544 int ke, n;
3545
3546 /* Save eliminated rows and columns for restoration purposes */
3547 #ifdef SavePresolveEliminated
3548 psdata->deletedA = mat_extractmat(lp->matA, rowmap, colmap, TRUE);
3549 if(!mat_validate(psdata->deletedA))
3550 report(lp, SEVERE, "presolve_finalize: Could not validate matrix with undo data\n");
3551 #endif
3552
3553 /* Check if OF columns are to be deleted */
3554 lp->presolve_undo->OFcolsdeleted = FALSE;
3555 for(n = firstInactiveLink(psdata->cols->varmap); (n != 0) && !lp->presolve_undo->OFcolsdeleted;
3556 n = nextInactiveLink(psdata->cols->varmap, n))
3557 lp->presolve_undo->OFcolsdeleted = (MYBOOL) (lp->orig_obj[n] != 0);
3558
3559 /* Delete eliminated columns */
3560 ke = lastInactiveLink(psdata->cols->varmap);
3561 n = countInactiveLink(psdata->cols->varmap);
3562 if((n > 0) && (ke > 0)) {
3563 del_columnex(lp, psdata->cols->varmap);
3564 mat_colcompact(lp->matA, lp->presolve_undo->orig_rows,
3565 lp->presolve_undo->orig_columns);
3566 compactvars = TRUE;
3567 }
3568
3569 /* Delete eliminated rows */
3570 ke = lastInactiveLink(psdata->rows->varmap);
3571 n = countInactiveLink(psdata->rows->varmap);
3572 if((n > 0) && (ke > 0)) {
3573 del_constraintex(lp, psdata->rows->varmap);
3574 mat_rowcompact(lp->matA, TRUE);
3575 compactvars = TRUE;
3576 }
3577 else if(psdata->nzdeleted > 0)
3578 mat_zerocompact(lp->matA);
3579
3580 /* Do compacting and updating of variable maps */
3581 if(compactvars)
3582 varmap_compact(lp, lp->presolve_undo->orig_rows,
3583 lp->presolve_undo->orig_columns);
3584
3585 /* Reduce memory usage of postsolve matrices */
3586 if(lp->presolve_undo->primalundo != NULL)
3587 mat_memopt(lp->presolve_undo->primalundo->tracker, 0, 0, 0);
3588 if(lp->presolve_undo->dualundo != NULL)
3589 mat_memopt(lp->presolve_undo->dualundo->tracker, 0, 0, 0);
3590
3591 /* Round near-zero objective function coefficients and RHS values */
3592 ke = lp->columns;
3593 for(n = 1; n <= ke; n++)
3594 my_roundzero(lp->orig_obj[n], lp->epsvalue);
3595 ke = lp->rows;
3596 for(n = 1; n <= ke; n++)
3597 my_roundzero(lp->orig_rhs[n], lp->epsvalue);
3598
3599 /* Update the SOS sparse mapping */
3600 if(SOS_count(lp) > 0)
3601 SOS_member_updatemap(lp->SOS);
3602
3603 /* Validate matrix and reconstruct row indexation */
3604 return(mat_validate(lp->matA));
3605 }
3606
3607 STATIC MYBOOL presolve_debugdump(lprec *lp, presolverec *psdata, char *filename, MYBOOL doappend)
3608 {
3609 FILE *output = stdout;
3610 int size;
3611 MYBOOL ok;
3612
3613 ok = (MYBOOL) ((filename == NULL) || ((output = fopen(filename, my_if(doappend, "a", "w"))) != NULL));
3614 if(!ok)
3615 return(ok);
3616 if((filename == NULL) && (lp->outstream != NULL))
3617 output = lp->outstream;
3618
3619 fprintf(output, "\nPRESOLVE - Status at loop %d:%d:%d\n",
3620 psdata->outerloops, psdata->middleloops, psdata->innerloops);
3621 fprintf(output, "Model size: %d rows (%d equalities, %d less than), %d columns\n",
3622 psdata->rows->varmap->count, psdata->EQmap->count, psdata->LTmap->count, psdata->cols->varmap->count);
3623
3624 fprintf(output, "\nMAPPERS\n-------\n\n");
3625 size = 1;
3626 blockWriteINT(output, "colmap", psdata->cols->varmap->map, 0, size*psdata->cols->varmap->size);
3627 blockWriteINT(output, "rowmap", psdata->rows->varmap->map, 0, size*psdata->rows->varmap->size);
3628 blockWriteINT(output, "EQmap", psdata->EQmap->map, 0, size*psdata->EQmap->size);
3629 blockWriteINT(output, "LTmap", psdata->LTmap->map, 0, size*psdata->LTmap->size);
3630
3631 fprintf(output, "\nCOUNTS\n------\n\n");
3632 blockWriteINT(output, "plucount", psdata->rows->plucount, 0, lp->rows);
3633 blockWriteINT(output, "negcount", psdata->rows->negcount, 0, lp->rows);
3634 blockWriteINT(output, "pluneg", psdata->rows->pluneg, 0, lp->rows);
3635
3636 fprintf(output, "\nSUMS\n----\n\n");
3637 blockWriteREAL(output, "pluupper", psdata->rows->pluupper, 0, lp->rows);
3638 blockWriteREAL(output, "negupper", psdata->rows->negupper, 0, lp->rows);
3639 blockWriteREAL(output, "plulower", psdata->rows->pluupper, 0, lp->rows);
3640 blockWriteREAL(output, "neglower", psdata->rows->negupper, 0, lp->rows);
3641
3642 if(filename != NULL)
3643 fclose(output);
3644 return(ok);
3645 }
3646
3647 int CMP_CALLMODEL compRedundant(const UNIONTYPE QSORTrec *current, const UNIONTYPE QSORTrec *candidate)
3648 {
3649 int start1 = (int) (current->int4.intpar1),
3650 start2 = (int) (candidate->int4.intpar1),
3651 result = CMP_COMPARE(start1, start2);
3652
3653 if(result == 0) {
3654 start1 = (int) (current->int4.intpar2);
3655 start2 = (int) (candidate->int4.intpar2);
3656 result = -CMP_COMPARE(start1, start2);
3657 }
3658 return( result );
3659 }
3660 int CMP_CALLMODEL compSparsity(const UNIONTYPE QSORTrec *current, const UNIONTYPE QSORTrec *candidate)
3661 {
3662 int start1 = (int) (current->int4.intpar1),
3663 start2 = (int) (candidate->int4.intpar1),
3664 result = CMP_COMPARE(start1, start2);
3665
3666 if(result == 0) {
3667 start1 = (int) (current->int4.intpar2);
3668 start2 = (int) (candidate->int4.intpar2);
3669 result = -CMP_COMPARE(start1, start2);
3670 }
3671
3672 if(result == 0) {
3673 start1 = (int) (current->int4.intval);
3674 start2 = (int) (candidate->int4.intval);
3675 result = CMP_COMPARE(start1, start2);
3676 }
3677 return( result );
3678 }
3679 int CMP_CALLMODEL compAggregate(const UNIONTYPE QSORTrec *current, const UNIONTYPE QSORTrec *candidate)
3680 {
3681 int index1 = (int) (current->pvoidint2.intval),
3682 index2 = (int) (candidate->pvoidint2.intval);
3683 lprec *lp = (lprec *) current->pvoidint2.ptr;
3684 REAL value1 = lp->orig_obj[index1],
3685 value2 = lp->orig_obj[index2];
3686
3687 /* Smallest objective coefficient (largest contribution to OF) */
3688 int result = CMP_COMPARE(value1, value2);
3689
3690 /* Smallest lower variable bound */
3691 if(result == 0) {
3692 index1 += lp->rows;
3693 index2 += lp->rows;
3694 value1 = lp->orig_lowbo[index1];
3695 value2 = lp->orig_lowbo[index2];
3696 result = CMP_COMPARE(value1, value2);
3697 }
3698
3699 /* Largest upper variable bound */
3700 if(result == 0) {
3701 value1 = lp->orig_upbo[index1];
3702 value2 = lp->orig_upbo[index2];
3703 result = -CMP_COMPARE(value1, value2);
3704 }
3705 return( result );
3706 }
3707
3708 STATIC int presolve_rowdominance(presolverec *psdata, int *nCoeffChanged, int *nRowsRemoved, int *nVarsFixed, int *nSum)
3709 {
3710 lprec *lp = psdata->lp;
3711 MATrec *mat = lp->matA;
3712 int i, ii, ib, ie, n, jb, je, jx, *coldel = NULL, status = RUNNING, item,
3713 iCoeffChanged = 0, iRowRemoved = 0, iVarFixed = 0;
3714 REAL ratio, *rowvalues = NULL;
3715 UNIONTYPE QSORTrec *QS = (UNIONTYPE QSORTrec *) calloc(lp->rows+1, sizeof(*QS));
3716
3717 /* Check if we were able to obtain working memory */
3718 if(QS == NULL)
3719 return( status);
3720
3721 /* A dominating row of variables always satisfy the following criteria:
3722 1) The starting column position is never lower, but could be the same
3723 2) The non-zero row count is always lower */
3724 n = 0;
3725 for(i = firstActiveLink(psdata->EQmap); i != 0; i = nextActiveLink(psdata->EQmap, i)) {
3726 /* Make sure we have no SOS or semi-continuous variables */
3727 jb = je = 0;
3728 if((SOS_count(lp) > 0) || (lp->sc_vars > 0)) {
3729 item = 0;
3730 for(jb = presolve_nextcol(psdata, i, &item); jb >= 0;
3731 jb = presolve_nextcol(psdata, i, &item)) {
3732 jx = ROW_MAT_COLNR(jb);
3733 if(SOS_is_member(lp->SOS, 0, jx) || is_semicont(lp, jx))
3734 break;
3735 }
3736 }
3737
3738 /* Add to list if we are Ok */
3739 if(jb < 0) {
3740 QS[n].int4.intval = i;
3741 item = 0;
3742 ii = presolve_nextcol(psdata, i, &item);
3743 QS[n].int4.intpar1 = ROW_MAT_COLNR(ii);
3744 QS[n].int4.intpar2 = presolve_rowlength(psdata, i);
3745 n++;
3746 }
3747 }
3748 if(n <= 1)
3749 goto Finish;
3750 QS_execute(QS, n, (findCompare_func *) compRedundant, NULL);
3751
3752 /* Let us start from the top of the list, going forward and looking
3753 for the longest possible dominating row */
3754 if(!allocREAL(lp, &rowvalues, lp->columns + 1, TRUE) ||
3755 !allocINT(lp, &coldel, lp->columns + 1, FALSE))
3756 goto Finish;
3757
3758 for(ib = 0; ib < n; ib++) {
3759
3760 /* Get row and check if it was previously eliminated */
3761 i = QS[ib].int4.intval;
3762 if(i < 0)
3763 continue;
3764
3765 /* Load the non-zero row values */
3766 item = 0;
3767 for(jb = presolve_nextcol(psdata, i, &item); jb >= 0;
3768 jb = presolve_nextcol(psdata, i, &item)) {
3769 jx = ROW_MAT_COLNR(jb);
3770 rowvalues[jx] = ROW_MAT_VALUE(jb);
3771 }
3772
3773 for(ie = ib+1; ie < n; ie++) {
3774
3775 /* Get row and check if it was previously eliminated */
3776 ii = QS[ie].int4.intval;
3777 if(ii < 0)
3778 continue;
3779
3780 #ifdef Paranoia
3781 if((QS[ib].int4.intpar1 > QS[ie].int4.intpar1) ||
3782 ((QS[ib].int4.intpar1 == QS[ie].int4.intpar1) && (QS[ib].int4.intpar2 < QS[ie].int4.intpar2)))
3783 report(lp, SEVERE, "presolve_rowdominance: Invalid sorted row order\n");
3784 #endif
3785
3786 /* Loop over every row member to confirm that the candidate
3787 actually dominates in every position */
3788 if((lp->orig_rhs[i] == 0) && (lp->orig_rhs[ii] == 0))
3789 ratio = 0;
3790 else if((lp->orig_rhs[i] != 0) && (lp->orig_rhs[ii] != 0))
3791 ratio = lp->orig_rhs[i] / lp->orig_rhs[ii];
3792 else
3793 continue;
3794 item = 0;
3795 for(jb = presolve_nextcol(psdata, ii, &item); jb >= 0;
3796 jb = presolve_nextcol(psdata, ii, &item)) {
3797 jx = ROW_MAT_COLNR(jb);
3798 if(rowvalues[jx] == 0)
3799 break;
3800 if(ratio == 0)
3801 ratio = rowvalues[jx] / ROW_MAT_VALUE(jb);
3802 else if(fabs(rowvalues[jx] - ratio*ROW_MAT_VALUE(jb)) > psdata->epsvalue)
3803 break;
3804 }
3805
3806 /* "We have contact" */
3807 if(jb < 0) {
3808 int sign_1 = 0, sign_j = 0;
3809
3810 /* Need to fix any superset columns, but require that they have equal signs */
3811 coldel[0] = 0;
3812 item = 0;
3813 for(jb = presolve_nextcol(psdata, i, &item); jb >= 0;
3814 jb = presolve_nextcol(psdata, i, &item)) {
3815 jx = ROW_MAT_COLNR(jb);
3816 if(mat_findelm(mat, ii, jx) <= 0) {
3817
3818 /* Cancel if we detect a free or "quasi-free" variable */
3819 if((lp->orig_lowbo[lp->rows + jx] < 0) &&
3820 (lp->orig_upbo[lp->rows + jx] > 0)) {
3821 coldel[0] = -1;
3822 break;
3823 }
3824
3825 /* Ensure that we are feasible */
3826 else if((lp->orig_lowbo[lp->rows + jx] > 0) ||
3827 (lp->orig_upbo[lp->rows + jx] < 0)) {
3828 report(lp, DETAILED, "presolve_rowdominate: Column %s is infeasible due to conflict in rows %s and %s\n",
3829 get_col_name(lp, jx), get_row_name(lp, i), get_row_name(lp, ii));
3830 coldel[0] = -1;
3831 break;
3832 }
3833
3834 /* Check consistency / uniformity of signs */
3835 sign_j = my_sign(ROW_MAT_VALUE(jb));
3836 sign_j = my_chsign(is_negative(lp, jx), sign_j);
3837 if(coldel[0] == 0) {
3838 sign_1 = sign_j;
3839 coldel[++coldel[0]] = jx;
3840 }
3841 else if(sign_j == sign_1) {
3842 coldel[++coldel[0]] = jx;
3843 }
3844 else {
3845 coldel[0] = -1;
3846 break;
3847 }
3848 }
3849 }
3850
3851 /* Force break / continuation if the superset columns were incompatible */
3852 if(coldel[0] < 0)
3853 continue;
3854
3855 /* Do the column fixing and deletion (check for infeasibility in the process) */
3856 for(jb = 1; jb <= coldel[0]; jb++) {
3857 jx = coldel[jb];
3858 if(!presolve_colfix(psdata, jx, 0, TRUE, &iVarFixed)) {
3859 status = presolve_setstatus(psdata, INFEASIBLE);
3860 goto Finish;
3861 }
3862 presolve_colremove(psdata, jx, TRUE);
3863 rowvalues[jx] = 0;
3864 }
3865
3866 /* Then delete the row */
3867 presolve_rowremove(psdata, ii, TRUE);
3868 iRowRemoved++;
3869 QS[ie].int4.intval = -ii;
3870 }
3871 }
3872
3873 /* Clear the non-zero row values ahead of the next row candidate */
3874 ie = mat->row_end[i-1];
3875 ii = mat->row_end[i];
3876 for(; ie < ii; ie++)
3877 rowvalues[ROW_MAT_COLNR(ie)] = 0;
3878
3879 }
3880 Finish:
3881 FREE(QS);
3882 FREE(rowvalues);
3883 FREE(coldel);
3884
3885 (*nCoeffChanged) += iCoeffChanged;
3886 (*nRowsRemoved) += iRowRemoved;
3887 (*nVarsFixed) += iVarFixed;
3888 (*nSum) += iCoeffChanged + iRowRemoved + iVarFixed;
3889
3890 return( status );
3891 }
3892
3893 #if 0
3894 STATIC int presolve_coldominance01(presolverec *psdata, int *nConRemoved, int *nVarsFixed, int *nSum)
3895 /* The current version of this routine eliminates binary variables
3896 that are dominated via set coverage or unit knapsack constraints */
3897 {
3898 lprec *lp = psdata->lp;
3899 MATrec *mat = lp->matA;
3900 MYBOOL first;
3901 int i, ii, ib, ie, n, jb, je, jx, jj, item, item2,
3902 *coldel = NULL, status = RUNNING, iVarFixed = 0;
3903 REAL scale, rhsval, *colvalues = NULL;
3904 UNIONTYPE QSORTrec *QS = (UNIONTYPE QSORTrec *) calloc(lp->columns+1, sizeof(*QS));
3905
3906 /* Check if we were able to obtain working memory */
3907 if(QS == NULL)
3908 return( status);
3909 if(lp->int_vars == 0)
3910 goto Finish;
3911
3912 /* A column dominates another binary variable column with the following criteria:
3913 1) The relative matrix non-zero entries are identical
3914 2) The relative objective coefficient is worse than the other;
3915 if the OF coefficients are identical, we can delete an arbitrary variable */
3916 n = 0;
3917 for(i = firstActiveLink(psdata->cols->varmap); i != 0; i = nextActiveLink(psdata->cols->varmap, i))
3918 if(is_binary(lp, i) && !SOS_is_member(lp->SOS, 0, i)) {
3919 /* Make sure we have an all-binary, unit-coefficient row */
3920 je = mat->col_end[i];
3921 item = 0;
3922 for(jb = presolve_nextrow(psdata, i, &item); jb >= 0;
3923 jb = presolve_nextrow(psdata, i, &item)) {
3924 jx = COL_MAT_ROWNR(jb);
3925 if(COL_MAT_VALUE(jb) != 1)
3926 break;
3927 }
3928
3929 /* Add to list if we are Ok */
3930 if(jb < 0) {
3931 QS[n].int4.intval = i;
3932 item = 0;
3933 ii = presolve_nextrow(psdata, i, &item);
3934 QS[n].int4.intpar1 = COL_MAT_ROWNR(ii);
3935 ii = presolve_collength(psdata, i);
3936 QS[n].int4.intpar2 = ii;
3937 n++;
3938 }
3939 }
3940 if(n <= 1) {
3941 FREE(QS);
3942 return( status );
3943 }
3944 QS_execute(QS, n, (findCompare_func *) compRedundant, NULL);
3945
3946 /* Let us start from the top of the list, going forward and looking
3947 for the longest possible dominated column */
3948 if(!allocREAL(lp, &colvalues, lp->rows + 1, TRUE) ||
3949 !allocINT(lp, &coldel, lp->columns + 1, FALSE))
3950 goto Finish;
3951
3952 for(ib = 0; ib < n; ib++) {
3953
3954 /* Get column and check if it was previously eliminated */
3955 i = QS[ib].int4.intval;
3956 if(i < 0)
3957 continue;
3958
3959 /* Load the non-zero column values */
3960 item = 0;
3961 for(jb = presolve_nextrow(psdata, i, &item); jb >= 0;
3962 jb = presolve_nextrow(psdata, i, &item)) {
3963 jx = COL_MAT_ROWNR(jb);
3964 colvalues[jx] = COL_MAT_VALUE(jb);
3965 }
3966
3967 coldel[0] = 0;
3968 for(ie = ib+1; ie < n; ie++) {
3969
3970 /* Insist on identical column lengths (sort is decending in column lengths) */
3971 ii = QS[ib].int4.intpar2 - QS[ie].int4.intpar2;
3972 if(ii != 0)
3973 break;
3974
3975 /* Also insist on identical starting positions */
3976 ii = QS[ib].int4.intpar1 - QS[ie].int4.intpar1;
3977 if(ii != 0)
3978 break;
3979
3980 /* Get column and check if it was previously eliminated */
3981 ii = QS[ie].int4.intval;
3982 if(ii < 0)
3983 continue;
3984
3985 /* Also make sure that the variables have "compatible" bounds */
3986 #if 1
3987 if((fabs(my_reldiff(lp->orig_lowbo[lp->rows + i], lp->orig_lowbo[lp->rows + ii])) > psdata->epsvalue) ||
3988 (fabs(my_reldiff(lp->orig_upbo[lp->rows + i], lp->orig_upbo[lp->rows + ii] )) > psdata->epsvalue))
3989 continue;
3990 #endif
3991
3992 #ifdef Paranoia
3993 if((QS[ib].int4.intpar1 > QS[ie].int4.intpar1) ||
3994 ((QS[ib].int4.intpar1 == QS[ie].int4.intpar1) && (QS[ib].int4.intpar2 < QS[ie].int4.intpar2)))
3995 report(lp, SEVERE, "presolve_coldominance01: Invalid sorted column order\n");
3996 #endif
3997
3998 /* Loop over every column member to confirm that the candidate is
3999 relatively identical in every position */
4000 first = TRUE;
4001 item = 0;
4002 item2 = 0;
4003 scale = 1;
4004 for(jb = presolve_nextrow(psdata, ii, &item),
4005 jj = presolve_nextrow(psdata, i, &item2); jb >= 0;
4006 jb = presolve_nextrow(psdata, ii, &item),
4007 jj = presolve_nextrow(psdata, i, &item2)) {
4008 jx = COL_MAT_ROWNR(jb);
4009 if(jx != COL_MAT_ROWNR(jj))
4010 break;
4011 if(first) {
4012 first = !first;
4013 scale = colvalues[jx] / COL_MAT_VALUE(jb);
4014 }
4015 else {
4016 if(fabs(colvalues[jx] - scale * COL_MAT_VALUE(jb)) > psdata->epsvalue)
4017 break;
4018 }
4019 /* Also make sure we have a compatible RHS (since this version of the
4020 dominance logic only applies to "sets") */
4021 rhsval = scale*lp->orig_rhs[jx] - 1.0;
4022 /* if((rhsval < 0) || (rhsval > 1 + psdata->epsvalue)) */
4023 if(fabs(rhsval) > psdata->epsvalue)
4024 break;
4025 }
4026
4027 /* "We have contact" */
4028 if(jb < 0) {
4029 coldel[++coldel[0]] = ii;
4030 QS[ie].int4.intval = -ii;
4031 }
4032 }
4033
4034 /* Find the dominant column and delete / fix the others;
4035 if there is a tie, simply delete the second candidate */
4036 ii = i;
4037 for(jb = 1; jb <= coldel[0]; jb++) {
4038 jx = coldel[jb];
4039 if(lp->orig_obj[jx] < lp->orig_obj[ii])
4040 swapINT(&ii, &coldel[jb]);
4041 }
4042 for(jb = 1; jb <= coldel[0]; jb++) {
4043 jx = coldel[jb];
4044 if(!presolve_colfix(psdata, jx, lp->orig_lowbo[lp->rows+jx], TRUE, &iVarFixed)) {
4045 status = presolve_setstatus(psdata, INFEASIBLE);
4046 goto Finish;
4047 }
4048 presolve_colremove(psdata, jx, TRUE);
4049 }
4050
4051 /* Clear the non-zero row values ahead of the next row candidate */
4052 if(ib + 1 < n) {
4053 ie = mat->col_end[i-1];
4054 ii = mat->col_end[i];
4055 for(; ie < ii; ie++)
4056 colvalues[COL_MAT_ROWNR(ie)] = 0;
4057 }
4058 }
4059 Finish:
4060 FREE(QS);
4061 FREE(colvalues);
4062 FREE(coldel);
4063
4064 (*nVarsFixed) += iVarFixed;
4065 (*nSum) += iVarFixed;
4066
4067 return( status );
4068 }
4069 #else
4070
4071 /* DEVELOPMENT/TEST CODE FOR POSSIBLE REPLACEMENT OF SIMILAR FUNCTION IN lp_presolve.c */
4072
4073 #define NATURAL int
4074
4075 STATIC int presolve_coldominance01(presolverec *psdata, NATURAL *nConRemoved, NATURAL *nVarsFixed, NATURAL *nSum)
4076 /* The current version of this routine eliminates binary variables
4077 that are dominated via set coverage or unit knapsack constraints */
4078 {
4079 lprec *lp = psdata->lp;
4080 MATrec *mat = lp->matA;
4081 NATURAL i, ib, ie, jx, item, item2,
4082 n = lp->int_vars, iVarFixed = 0, nrows = lp->rows,
4083 *coldel = NULL;
4084 int jb, jj, ii,
4085 status = RUNNING;
4086 REAL rhsval = 0.0,
4087 *colvalues = NULL, *colobj = NULL;
4088 LLrec *sets = NULL;
4089 UNIONTYPE QSORTrec *QS = (UNIONTYPE QSORTrec *) calloc(n+1, sizeof(*QS));
4090
4091 /* Check if we were able to obtain working memory */
4092 if(QS == NULL)
4093 return( status);
4094 if(n == 0)
4095 goto Finish;
4096
4097 /* Create list of set coverage and knapsack constraints */
4098 createLink(nrows, &sets, NULL);
4099 for(i = firstActiveLink(psdata->rows->varmap); i != 0; i = nextActiveLink(psdata->rows->varmap, i)) {
4100 if((lp->orig_rhs[i] < 0) || (psdata->rows->negcount[i] > 0))
4101 continue;
4102 item = 0;
4103 for(jb = presolve_nextcol(psdata, i, &item); jb >= 0;
4104 jb = presolve_nextcol(psdata, i, &item)) {
4105 jx = ROW_MAT_COLNR(jb);
4106 if(!is_binary(lp, jx))
4107 break;
4108 rhsval = ROW_MAT_VALUE(jb) - 1;
4109 if(fabs(rhsval) > lp->epsvalue)
4110 break;
4111 }
4112 if(jb < 0)
4113 setLink(sets, i);
4114 }
4115 if(countActiveLink(sets) == 0)
4116 goto Finish;
4117
4118 /* A column dominates another binary variable column with the following criteria:
4119 1) The relative matrix non-zero entries are identical
4120 2) The relative objective coefficient is worse than the other;
4121 if the OF coefficients are identical, we can delete an arbitrary variable */
4122 n = 0;
4123 for(i = firstActiveLink(psdata->cols->varmap); i != 0; i = nextActiveLink(psdata->cols->varmap, i))
4124 if(is_binary(lp, i) && !SOS_is_member(lp->SOS, 0, i)) {
4125 /* Make sure the column is member of at least one set */
4126 item = 0;
4127 for(jb = presolve_nextrow(psdata, i, &item); jb >= 0;
4128 jb = presolve_nextrow(psdata, i, &item)) {
4129 jx = COL_MAT_ROWNR(jb);
4130 if(isActiveLink(sets, jx))
4131 break;
4132 }
4133
4134 /* Add to list if set membership test is Ok */
4135 if(jb >= 0) {
4136 QS[n].int4.intval = i;
4137 item = 0;
4138 ii = presolve_nextrow(psdata, i, &item);
4139 QS[n].int4.intpar1 = COL_MAT_ROWNR(ii);
4140 ii = presolve_collength(psdata, i);
4141 QS[n].int4.intpar2 = ii;
4142 n++;
4143 }
4144 }
4145 if(n <= 1) {
4146 FREE(QS);
4147 return( status );
4148 }
4149 QS_execute(QS, n, (findCompare_func *) compRedundant, NULL);
4150
4151 /* Let us start from the top of the list, going forward and looking
4152 for the longest possible dominated column */
4153 if(!allocREAL(lp, &colvalues, nrows + 1, TRUE) ||
4154 !allocREAL(lp, &colobj, n + 1, FALSE) ||
4155 !allocINT(lp, &coldel, n + 1, FALSE))
4156 goto Finish;
4157
4158 for(ib = 0; ib < n; ib++) {
4159
4160 /* Get column and check if it was previously eliminated */
4161 i = QS[ib].int4.intval;
4162 if(!isActiveLink(psdata->cols->varmap, i))
4163 continue;
4164
4165 /* Load the non-zero column values */
4166 item = 0;
4167 for(jb = presolve_nextrow(psdata, i, &item); jb >= 0;
4168 jb = presolve_nextrow(psdata, i, &item)) {
4169 jx = COL_MAT_ROWNR(jb);
4170 colvalues[jx] = COL_MAT_VALUE(jb);
4171 }
4172
4173 /* Store data for current column */
4174 coldel[0] = 1;
4175 coldel[1] = i;
4176 colobj[1] = lp->orig_obj[i];
4177
4178 /* Loop over all other columns to see if they have equal constraint coefficients */
4179 for(ie = ib+1; ie < n; ie++) {
4180
4181 /* Check if this column was previously eliminated */
4182 ii = QS[ie].int4.intval;
4183 if(!isActiveLink(psdata->cols->varmap, ii))
4184 continue;
4185
4186 /* Insist on identical column lengths (sort is decending in column lengths) */
4187 ii = QS[ib].int4.intpar2 - QS[ie].int4.intpar2;
4188 if(ii != 0)
4189 break;
4190
4191 /* Also insist on identical starting positions */
4192 ii = QS[ib].int4.intpar1 - QS[ie].int4.intpar1;
4193 if(ii != 0)
4194 break;
4195
4196 /* Get column and check if it was previously eliminated */
4197 ii = QS[ie].int4.intval;
4198
4199 #ifdef Paranoia
4200 if((QS[ib].int4.intpar1 > QS[ie].int4.intpar1) ||
4201 ((QS[ib].int4.intpar1 == QS[ie].int4.intpar1) && (QS[ib].int4.intpar2 < QS[ie].int4.intpar2)))
4202 report(lp, SEVERE, "presolve_coldominance01: Invalid sorted column order\n");
4203 #endif
4204
4205 /* Loop over every column member to confirm that the candidate is identical in every row;
4206 we also compute the minimal set order */
4207 rhsval = lp->infinite;
4208 item = 0;
4209 item2 = 0;
4210 for(jb = presolve_nextrow(psdata, ii, &item),
4211 jj = presolve_nextrow(psdata, i, &item2); jb >= 0;
4212 jb = presolve_nextrow(psdata, ii, &item),
4213 jj = presolve_nextrow(psdata, i, &item2)) {
4214 jx = COL_MAT_ROWNR(jb);
4215 if(jx != COL_MAT_ROWNR(jj))
4216 break;
4217 if(isActiveLink(sets, jx))
4218 SETMIN(rhsval, lp->orig_rhs[jx]);
4219 }
4220
4221 /* "We have contact" */
4222 if(jb < 0) {
4223 coldel[++coldel[0]] = ii;
4224 colobj[coldel[0]] = lp->orig_obj[ii];
4225 }
4226 }
4227
4228 /* Find the dominant columns, fix and delete the others */
4229 if(coldel[0] > 1) {
4230 qsortex(colobj+1, coldel[0], 0, sizeof(*colobj), FALSE, compareREAL, coldel+1, sizeof(*coldel));
4231 /* if(rhsval+lp->epsvalue < lp->infinite) { */
4232 jb = (NATURAL) (rhsval+lp->epsvalue);
4233 /* printf("%f / %d\n", rhsval, jb); */
4234 for(jb++; jb <= coldel[0]; jb++) {
4235 jx = coldel[jb];
4236 if(!presolve_colfix(psdata, jx, lp->orig_lowbo[nrows+jx], TRUE, &iVarFixed)) {
4237 status = presolve_setstatus(psdata, INFEASIBLE);
4238 goto Finish;
4239 }
4240 presolve_colremove(psdata, jx, TRUE);
4241 }
4242 /*} */
4243 }
4244
4245 /* Clear the non-zero row values ahead of the next row candidate */
4246 if(ib + 1 < n) {
4247 ie = mat->col_end[i-1];
4248 ii = mat->col_end[i];
4249 for(; ie < ii; ie++)
4250 colvalues[COL_MAT_ROWNR(ie)] = 0;
4251 }
4252 }
4253 Finish:
4254 freeLink(&sets);
4255 FREE(QS);
4256 FREE(colvalues);
4257 FREE(coldel);
4258 FREE(colobj);
4259
4260 (*nVarsFixed) += iVarFixed;
4261 (*nSum) += iVarFixed;
4262
4263 return( status );
4264 }
4265
4266 #endif
4267
4268 STATIC int presolve_aggregate(presolverec *psdata, int *nConRemoved, int *nVarsFixed, int *nSum)
4269 /* This routine combines compatible or identical columns */
4270 {
4271 lprec *lp = psdata->lp;
4272 MATrec *mat = lp->matA;
4273 MYBOOL first;
4274 int i, ii, ib, ie, ix, n, jb, je, jx, jj, item, item2,
4275 *coldel = NULL, status = RUNNING, iVarFixed = 0;
4276 REAL scale, *colvalues = NULL;
4277 UNIONTYPE QSORTrec *QScand = (UNIONTYPE QSORTrec *) calloc(lp->columns+1, sizeof(*QScand));
4278
4279 /* Check if we were able to obtain working memory */
4280 if(QScand == NULL)
4281 return( status);
4282
4283 /* Obtain the list of qualifying columns to be sorted */
4284 n = 0;
4285 for(i = firstActiveLink(psdata->cols->varmap); i != 0; i = nextActiveLink(psdata->cols->varmap, i))
4286 if(!is_semicont(lp, i) && !SOS_is_member(lp->SOS, 0, i)) {
4287 QScand[n].int4.intval = i;
4288 item = 0;
4289 ii = presolve_nextrow(psdata, i, &item);
4290 QScand[n].int4.intpar1 = COL_MAT_ROWNR(ii);
4291 ii = presolve_collength(psdata, i);
4292 QScand[n].int4.intpar2 = ii;
4293 n++;
4294 }
4295 if(n <= 1) {
4296 FREE(QScand);
4297 return( status );
4298 }
4299 QS_execute(QScand, n, (findCompare_func *) compRedundant, NULL);
4300
4301 /* Let us start from the top of the list, going forward and looking
4302 for the longest possible identical column */
4303 if(!allocREAL(lp, &colvalues, lp->rows + 1, TRUE) ||
4304 !allocINT(lp, &coldel, lp->columns + 1, FALSE))
4305 goto Finish;
4306
4307 for(ib = 0; ib < n; ib++) {
4308
4309 /* Get column and check if it was previously eliminated */
4310 i = QScand[ib].int4.intval;
4311 if(i < 0)
4312 continue;
4313
4314 /* Load the non-zero column values of this active/reference column */
4315 item = 0;
4316 for(jb = presolve_nextrow(psdata, i, &item); jb >= 0;
4317 jb = presolve_nextrow(psdata, i, &item)) {
4318 jx = COL_MAT_ROWNR(jb);
4319 colvalues[jx] = COL_MAT_VALUE(jb);
4320 }
4321
4322 coldel[0] = 0;
4323 for(ie = ib+1; ie < n; ie++) {
4324
4325 /* Insist on identical column lengths (sort is decending in column lengths) */
4326 ii = QScand[ib].int4.intpar2 - QScand[ie].int4.intpar2;
4327 if(ii != 0)
4328 break;
4329
4330 /* Also insist on identical starting positions */
4331 ii = QScand[ib].int4.intpar1 - QScand[ie].int4.intpar1;
4332 if(ii != 0)
4333 break;
4334
4335 /* Get column and check if it was previously eliminated */
4336 ii = QScand[ie].int4.intval;
4337 if(ii < 0)
4338 continue;
4339
4340 /* Loop over every column member to confirm that the candidate is
4341 relatively identical in every position */
4342 first = TRUE;
4343 item = 0;
4344 item2 = 0;
4345 scale = 1;
4346 for(jb = presolve_nextrow(psdata, ii, &item),
4347 jj = presolve_nextrow(psdata, i, &item2); jb >= 0;
4348 jb = presolve_nextrow(psdata, ii, &item),
4349 jj = presolve_nextrow(psdata, i, &item2)) {
4350 jx = COL_MAT_ROWNR(jb);
4351 if(jx != COL_MAT_ROWNR(jj))
4352 break;
4353 if(first) {
4354 first = !first;
4355 scale = colvalues[jx] / COL_MAT_VALUE(jb);
4356 }
4357 else {
4358 if(fabs(colvalues[jx] - scale * COL_MAT_VALUE(jb)) > psdata->epsvalue)
4359 break;
4360 }
4361 }
4362
4363 /* "We have contact", store the column in the aggregation list */
4364 if(jb < 0) {
4365 coldel[++coldel[0]] = ii;
4366 QScand[ie].int4.intval = -ii;
4367 }
4368 }
4369
4370 /* Sort the aggregation list if we have aggregation candidates */
4371 if(coldel[0] > 1) {
4372 REAL of, ofelim, fixvalue;
4373 MYBOOL isint;
4374 UNIONTYPE QSORTrec *QSagg = (UNIONTYPE QSORTrec *) calloc(coldel[0], sizeof(*QSagg));
4375
4376 for(jb = 1; jb <= coldel[0]; jb++) {
4377 ii = jb - 1;
4378 QSagg[ii].pvoidint2.intval = coldel[jb];
4379 QSagg[ii].pvoidint2.ptr = (void *) lp;
4380 }
4381 QS_execute(QSagg, coldel[0], (findCompare_func *) compAggregate, NULL);
4382
4383 /* Process columns with identical OF coefficients */
4384 jb = 0;
4385 while((status == RUNNING) && (jb < coldel[0])) {
4386 ii = QSagg[jb].pvoidint2.intval;
4387 of = lp->orig_obj[ii];
4388 isint = is_int(lp, ii);
4389 je = jb + 1;
4390 while((status == RUNNING) && (je < coldel[0]) &&
4391 (fabs(lp->orig_obj[ix = QSagg[je].pvoidint2.intval] - of) < psdata->epsvalue)) {
4392 /* We now have two columns with equal OFs; the following cases are possible:
4393
4394 1) The first column has Inf upper bound, which means that it can
4395 "absorb" compatible columns, which are then fixed at the appropriate
4396 bounds (or zero in case of free variables).
4397 2) The first column has a -Inf lower bound, and further columns are
4398 Inf upper bounds, which means steps towards forming a free variable
4399 can be made.
4400 3) The first column is a non-Inf upper bound, in which case the bounds
4401 are summed into a helper variable and the variable simply deleted.
4402 The deleted variables' value are allocated/distributed via a simple
4403 linear programming routine at postsolve.
4404
4405 In the current version of this code, we only handle case 1. */
4406 if(is_int(lp, ix) == isint) {
4407 ofelim = lp->orig_obj[ix];
4408 if(of == 0)
4409 scale = 1;
4410 else
4411 scale = ofelim / of;
4412
4413 if(my_infinite(lp, lp->orig_upbo[lp->rows+ii])) { /* Case 1 (recipe.mps) */
4414 if(is_unbounded(lp, ix))
4415 fixvalue = 0;
4416 else if(ofelim < 0)
4417 fixvalue = lp->orig_upbo[lp->rows+ix];
4418 else
4419 fixvalue = lp->orig_lowbo[lp->rows+ix];
4420 if(my_infinite(lp, fixvalue))
4421 status = presolve_setstatus(psdata, UNBOUNDED);
4422 else if(!presolve_colfix(psdata, ix, fixvalue, TRUE, &iVarFixed))
4423 status = presolve_setstatus(psdata, INFEASIBLE);
4424 else
4425 presolve_colremove(psdata, ix, TRUE);
4426 }
4427
4428 else if(my_infinite(lp, lp->orig_lowbo[lp->rows+ii])) { /* Case 2 */
4429 /* Do nothing */
4430 }
4431
4432 else { /* Case 3 */
4433 #if 0
4434 /* Do nothing */
4435 #else
4436 if(ofelim >= 0) {
4437 fixvalue = lp->orig_lowbo[lp->rows+ix];
4438 lp->orig_upbo[lp->rows+ii] += scale * (lp->orig_upbo[lp->rows+ix] - fixvalue);
4439 }
4440 else {
4441 fixvalue = lp->orig_upbo[lp->rows+ix];
4442 lp->orig_upbo[lp->rows+ii] -= scale * (fixvalue - lp->orig_lowbo[lp->rows+ix]);
4443 }
4444 if(my_infinite(lp, fixvalue))
4445 status = presolve_setstatus(psdata, UNBOUNDED);
4446 else if(!presolve_colfix(psdata, ix, fixvalue, TRUE, &iVarFixed))
4447 status = presolve_setstatus(psdata, INFEASIBLE);
4448 else
4449 presolve_colremove(psdata, ix, TRUE);
4450 #ifdef xxParanoia
4451 if(presolve_rowlengthdebug(psdata) > 0)
4452 report(lp, SEVERE, "presolve_aggregate: Invalid row count\n");
4453 #endif
4454 psdata->forceupdate = TRUE;
4455 #endif
4456 }
4457 }
4458 je++;
4459 }
4460 jb = je;
4461 }
4462 FREE(QSagg);
4463 }
4464
4465 /* Clear the non-zero row values ahead of the next row candidate */
4466 if(ib + 1 < n) {
4467 ie = mat->col_end[i-1];
4468 ii = mat->col_end[i];
4469 for(; ie < ii; ie++)
4470 colvalues[COL_MAT_ROWNR(ie)] = 0;
4471 }
4472 }
4473 Finish:
4474 FREE(QScand);
4475 FREE(colvalues);
4476 FREE(coldel);
4477
4478 (*nVarsFixed) += iVarFixed;
4479 (*nSum) += iVarFixed;
4480
4481 return( status );
4482 }
4483
4484 STATIC int presolve_makesparser(presolverec *psdata, int *nCoeffChanged, int *nConRemove, int *nVarFixed, int *nSum)
4485 {
4486 lprec *lp = psdata->lp;
4487 MATrec *mat = lp->matA;
4488 MYBOOL chsign;
4489 int i, ii, ib, ix, k, n, jb, je, jl, jjb, jje, jjl, jx, jjx, item, itemEQ,
4490 *nzidx = NULL, status = RUNNING, iObjChanged = 0, iCoeffChanged = 0, iConRemove = 0;
4491 REAL test, ratio, value, valueEQ, *valptr;
4492 LLrec *EQlist = NULL;
4493 UNIONTYPE QSORTrec *QS = (UNIONTYPE QSORTrec *) calloc(lp->rows, sizeof(*QS));
4494
4495 /* Check if we were able to obtain working memory */
4496 if((QS == NULL) || (psdata->rows->varmap->count == 0) || (psdata->EQmap->count == 0))
4497 return( status);
4498
4499 /* Sort rows in 1) increasing order of start index, 2) decreasing length, and
4500 3) non-equalities (i.e. equalities last) */
4501 n = 0;
4502 for(i = firstActiveLink(psdata->rows->varmap); i != 0; i = nextActiveLink(psdata->rows->varmap, i)) {
4503 k = presolve_rowlength(psdata, i);
4504 if(k >= 2) {
4505 item = 0;
4506 ii = presolve_nextcol(psdata, i, &item);
4507 #ifdef Paranoia
4508 if((ii < 0) || (item == 0)) {
4509 report(lp, SEVERE, "presolve_makesparser: Unexpected zero-length row %d\n", i);
4510 continue;
4511 }
4512 #endif
4513 QS[n].int4.intval = my_chsign(is_constr_type(lp, i, EQ), i);
4514 QS[n].int4.intpar1 = ROW_MAT_COLNR(ii);
4515 QS[n].int4.intpar2 = k;
4516 n++;
4517 }
4518 }
4519 if(n <= 1) {
4520 FREE(QS);
4521 return( status );
4522 }
4523 QS_execute(QS, n, (findCompare_func *) compSparsity, NULL);
4524
4525 /* Create associated sorted map of indeces to equality constraints;
4526 note that we need to have a unit offset for compatibility. */
4527 allocINT(lp, &nzidx, lp->columns + 1, FALSE);
4528 createLink(lp->rows, &EQlist, NULL);
4529 for(ib = 0; ib < n; ib++) {
4530 i = QS[ib].int4.intval;
4531 if(i < 0)
4532 appendLink(EQlist, ib + 1);
4533 }
4534
4535 /* Loop over all equality masks */
4536 for(ix = firstActiveLink(EQlist); ix != 0; ) {
4537
4538 /* Get row starting and ending positions of the mask */
4539 ii = abs(QS[ix-1].int4.intval);
4540 jjb = QS[ix-1].int4.intpar1;
4541 jje = presolve_lastcol(psdata, ii);
4542 jje = ROW_MAT_COLNR(jje);
4543 jjl = QS[ix-1].int4.intpar2;
4544
4545 /* Scan the OF */
4546 i = 0;
4547 chsign = is_chsign(lp, i);
4548 test = ratio = 0.0;
4549 itemEQ = 0;
4550 nzidx[0] = 0;
4551 while(((jjx = presolve_nextcol(psdata, ii, &itemEQ)) >= 0) && /*(itemEQ > 0) && */
4552 (fabs(test-ratio) < psdata->epsvalue)) {
4553 valueEQ = ROW_MAT_VALUE(jjx);
4554 if(valueEQ == 0)
4555 continue;
4556 k = ROW_MAT_COLNR(jjx);
4557 value = lp->orig_obj[k];
4558 if(fabs(value) < psdata->epsvalue)
4559 break;
4560 if(ratio == 0.0) {
4561 test = ratio = value / valueEQ;
4562 }
4563 else
4564 test = value / valueEQ;
4565 /* Store nz index */
4566 nzidx[++nzidx[0]] = k;
4567 }
4568
4569 /* We were successful if the equality was completely traversed; we will
4570 then zero-out the OF coefficients and update the constant term. */
4571 if((itemEQ == 0) && (nzidx[0] > 0) && (fabs(test-ratio) < psdata->epsvalue)) {
4572 for(k = 1; k <= nzidx[0]; k++) {
4573 /* We should add recovery data for the zero'ed coefficient here */
4574 jx = nzidx[k];
4575 value = lp->orig_obj[jx];
4576 lp->orig_obj[jx] = 0.0;
4577 /* Update counts */
4578 value = my_chsign(chsign, value);
4579 if(value < 0) {
4580 psdata->rows->negcount[i]--;
4581 psdata->cols->negcount[jx]--;
4582 }
4583 else {
4584 psdata->rows->plucount[i]--;
4585 psdata->cols->plucount[jx]--;
4586 }
4587 iObjChanged++;
4588 }
4589 value = ratio * lp->orig_rhs[ii];
4590 presolve_adjustrhs(psdata, i, value, psdata->epsvalue);
4591 }
4592
4593 /* Scan for compatible constraints that can be masked for sparsity elimination */
4594 for(ib = 1; ib < ix; ib++) {
4595
4596 /* Get row starting and ending positions of the target constraint */
4597 i = abs(QS[ib-1].int4.intval);
4598 jb = QS[ib-1].int4.intpar1;
4599 je = presolve_lastcol(psdata, i);
4600 je = ROW_MAT_COLNR(je);
4601 jl = QS[ib-1].int4.intpar2;
4602
4603 /* Check if there is a window mismatch */
4604 if((jjb < jb) || (jje > je) || (jjl > jl))
4605 goto NextEQ;
4606
4607 /* We have a window match; now check if there is a (scalar) member-by-member
4608 match as well. We approach this in the following manner:
4609 1) Get first (or next) member of active equality
4610 2) Loop to matching member in the target constraint, but abandon if no match
4611 3) Set ratio if this is the first match, otherwise compare ratio and abandon
4612 on mismatch
4613 4) Go to 1) of there are more elements in the active equality
4614 5) Proceed to do sparsity elimination if we were successful. */
4615 chsign = is_chsign(lp, i);
4616 test = ratio = 0.0;
4617 itemEQ = 0;
4618 item = 0;
4619 nzidx[0] = 0;
4620 while(((jjx = presolve_nextcol(psdata, ii, &itemEQ)) >= 0) && /*(itemEQ > 0) &&*/
4621 (fabs(test-ratio) < psdata->epsvalue)) {
4622 valueEQ = ROW_MAT_VALUE(jjx);
4623 if(valueEQ == 0)
4624 continue;
4625 jx = 0;
4626 jjx = ROW_MAT_COLNR(jjx);
4627 for(k = presolve_nextcol(psdata, i, &item);
4628 (jx < jjx) && (item > 0);
4629 k = presolve_nextcol(psdata, i, &item)) {
4630 jx = ROW_MAT_COLNR(k);
4631 /* Do we have a column index match? */
4632 if(jx == jjx) {
4633 value = ROW_MAT_VALUE(k);
4634 /* Abandon if we have a zero value */
4635 if(value == 0)
4636 goto NextEQ;
4637 if(ratio == 0.0) {
4638 test = ratio = value / valueEQ;
4639 }
4640 else
4641 test = value / valueEQ;
4642 /* Store nz index */
4643 nzidx[++nzidx[0]] = k;
4644 break;
4645 }
4646 /* Give up matching if there is overshooting */
4647 else if(jx > jjx)
4648 goto NextEQ;
4649 }
4650 }
4651
4652 /* We were successful if the equality was completely traversed */
4653 if((itemEQ == 0) && (nzidx[0] > 0) && (fabs(test-ratio) < psdata->epsvalue)) {
4654
4655 /* Check if we have found parametrically indentical constraints */
4656 if(presolve_rowlength(psdata, i) == presolve_rowlength(psdata,ii)) {
4657
4658 value = lp->orig_rhs[i];
4659 valueEQ = lp->orig_rhs[ii];
4660
4661 /* Are they both equalities? */
4662 if(is_constr_type(lp, i, EQ)) {
4663 /* Determine applicable ratio for the RHS */
4664 if(fabs(valueEQ) < psdata->epsvalue) {
4665 if(fabs(value) < psdata->epsvalue)
4666 test = ratio;
4667 else
4668 test = lp->infinite;
4669 }
4670 else
4671 test = value / valueEQ;
4672 /* Check for infeasibility */
4673 if(fabs(test-ratio) > psdata->epsvalue) {
4674 report(lp, NORMAL, "presolve_sparser: Infeasibility of relatively equal constraints %d and %d\n",
4675 i, ii);
4676 status = presolve_setstatus(psdata, INFEASIBLE);
4677 goto Finish;
4678 }
4679 /* Otherwise we can delete a redundant constraint */
4680 else {
4681 removeLink(EQlist, i);
4682 presolve_rowremove(psdata, i, TRUE);
4683 MEMCOPY(&QS[ib-1], &QS[ib], n-ib);
4684 n--;
4685 iConRemove++;
4686 }
4687 }
4688 /* ... if not, then delete the inequality, since the equality dominates */
4689 else {
4690 /* First verify feasibility of the RHS */
4691 if((value+psdata->epsvalue < valueEQ) ||
4692 (value-get_rh_range(lp, i)-psdata->epsvalue > valueEQ)) {
4693 report(lp, NORMAL, "presolve_sparser: Infeasibility of relatively equal RHS values for %d and %d\n",
4694 i, ii);
4695 status = presolve_setstatus(psdata, INFEASIBLE);
4696 goto Finish;
4697 }
4698 presolve_rowremove(psdata, i, TRUE);
4699 MEMCOPY(&QS[ib-1], &QS[ib], n-ib);
4700 n--;
4701 iConRemove++;
4702 }
4703 }
4704
4705 /* Otherwise zero-out the target constraint coefficients and update the RHS */
4706 else {
4707 for(k = 1; k <= nzidx[0]; k++) {
4708 /* We should add recovery data for the zero'ed coefficient here */
4709 jjx = nzidx[k];
4710 jx = ROW_MAT_COLNR(jjx);
4711 valptr = &ROW_MAT_VALUE(jjx);
4712 value = *valptr;
4713 *valptr = 0.0;
4714 /* Update counts */
4715 value = my_chsign(chsign, value);
4716 if(value < 0) {
4717 psdata->rows->negcount[i]--;
4718 psdata->cols->negcount[jx]--;
4719 }
4720 else {
4721 psdata->rows->plucount[i]--;
4722 psdata->cols->plucount[jx]--;
4723 }
4724 iCoeffChanged++;
4725 }
4726 value = ratio * lp->orig_rhs[ii];
4727 presolve_adjustrhs(psdata, i, value, psdata->epsvalue);
4728 }
4729 }
4730
4731 }
4732 /* Get next equality index */
4733 NextEQ:
4734 ix = nextActiveLink(EQlist, ix);
4735 }
4736
4737 Finish:
4738 FREE(QS);
4739 freeLink(&EQlist);
4740 FREE(nzidx);
4741
4742 /* Let us condense the matrix if we modified the constraint matrix */
4743 if(iCoeffChanged > 0) {
4744 mat->row_end_valid = FALSE;
4745 mat_zerocompact(mat);
4746 presolve_validate(psdata, TRUE);
4747 #ifdef PresolveForceUpdateMax
4748 mat_computemax(mat /* , FALSE */);
4749 #endif
4750 psdata->forceupdate = TRUE;
4751 }
4752
4753 (*nConRemove) += iConRemove;
4754 (*nCoeffChanged) += iCoeffChanged + iObjChanged;
4755 (*nSum) += iCoeffChanged + iObjChanged + iConRemove;
4756
4757 return( status );
4758 }
4759
4760 STATIC int presolve_SOS1(presolverec *psdata, int *nCoeffChanged, int *nConRemove, int *nVarFixed, int *nSOS, int *nSum)
4761 {
4762 lprec *lp = psdata->lp;
4763 MYBOOL candelete, SOS_GUBactive = FALSE;
4764 int iCoeffChanged = 0, iConRemove = 0, iSOS = 0,
4765 i,ix,iix, j,jx,jjx, status = RUNNING;
4766 REAL Value1;
4767 MATrec *mat = lp->matA;
4768
4769 for(i = lastActiveLink(psdata->rows->varmap); i > 0; ) {
4770 candelete = FALSE;
4771 Value1 = get_rh(lp, i);
4772 jx = get_constr_type(lp, i);
4773 if((Value1 == 1) && (presolve_rowlength(psdata, i) >= MIN_SOS1LENGTH) &&
4774 ((SOS_GUBactive && (jx != GE)) || (!SOS_GUBactive && (jx == LE)))) {
4775 jjx = mat->row_end[i-1];
4776 iix = mat->row_end[i];
4777 for(; jjx < iix; jjx++) {
4778 j = ROW_MAT_COLNR(jjx);
4779 if(!isActiveLink(psdata->cols->varmap, j))
4780 continue;
4781 if(!is_binary(lp, j) || (ROW_MAT_VALUE(jjx) != 1))
4782 break;
4783 }
4784 if(jjx >= iix) {
4785 char SOSname[16];
4786
4787 /* Define a new SOS instance */
4788 ix = SOS_count(lp) + 1;
4789 sprintf(SOSname, "SOS_%d", ix);
4790 ix = add_SOS(lp, SOSname, 1, ix, 0, NULL, NULL);
4791 if(jx == EQ)
4792 SOS_set_GUB(lp->SOS, ix, TRUE);
4793 Value1 = 0;
4794 jjx = mat->row_end[i-1];
4795 for(; jjx < iix; jjx++) {
4796 j = ROW_MAT_COLNR(jjx);
4797 if(!isActiveLink(psdata->cols->varmap, j))
4798 continue;
4799 Value1 += 1;
4800 append_SOSrec(lp->SOS->sos_list[ix-1], 1, &j, &Value1);
4801 }
4802 candelete = TRUE;
4803 iSOS++;
4804 }
4805 }
4806
4807 /* Get next row and do the deletion of the previous, if indicated */
4808 ix = i;
4809 i = prevActiveLink(psdata->rows->varmap, i);
4810 if(candelete) {
4811 presolve_rowremove(psdata, ix, TRUE);
4812 iConRemove++;
4813 }
4814 }
4815 if(iSOS)
4816 report(lp, DETAILED, "presolve_SOS1: Converted %5d constraints to SOS1.\n", iSOS);
4817 clean_SOSgroup(lp->SOS, (MYBOOL) (iSOS > 0));
4818
4819 (*nCoeffChanged) += iCoeffChanged;
4820 (*nConRemove) += iConRemove;
4821 (*nSOS) += iSOS;
4822 (*nSum) += iCoeffChanged+iConRemove+iSOS;
4823
4824 return( status );
4825 }
4826
4827 STATIC int presolve_boundconflict(presolverec *psdata, int baserowno, int colno)
4828 {
4829 REAL Value1, Value2;
4830 lprec *lp = psdata->lp;
4831 MATrec *mat = lp->matA;
4832 int ix, item = 0,
4833 status = RUNNING;
4834
4835 if(baserowno <= 0) do {
4836 ix = presolve_nextrow(psdata, colno, &item);
4837 if(ix < 0)
4838 return( status );
4839 baserowno = COL_MAT_ROWNR(ix);
4840 } while(presolve_rowlength(psdata, baserowno) != 1);
4841 Value1 = get_rh_upper(lp, baserowno),
4842 Value2 = get_rh_lower(lp, baserowno);
4843
4844 if(presolve_singletonbounds(psdata, baserowno, colno, &Value2, &Value1, NULL)) {
4845 int iix;
4846 item = 0;
4847 for(ix = presolve_nextrow(psdata, colno, &item);
4848 ix >= 0; ix = presolve_nextrow(psdata, colno, &item)) {
4849 iix = COL_MAT_ROWNR(ix);
4850 if((iix != baserowno) &&
4851 (presolve_rowlength(psdata, iix) == 1) &&
4852 !presolve_altsingletonvalid(psdata, iix, colno, Value2, Value1)) {
4853 status = presolve_setstatus(psdata, INFEASIBLE);
4854 break;
4855 }
4856 }
4857 }
4858 else
4859 status = presolve_setstatus(psdata, INFEASIBLE);
4860 return( status );
4861 }
4862
4863 STATIC int presolve_columns(presolverec *psdata, int *nCoeffChanged, int *nConRemove, int *nVarFixed, int *nBoundTighten, int *nSum)
4864 {
4865 lprec *lp = psdata->lp;
4866 MYBOOL candelete, isOFNZ, unbounded,
4867 probefix = is_presolve(lp, PRESOLVE_PROBEFIX),
4868 #if 0
4869 probereduce = is_presolve(lp, PRESOLVE_PROBEREDUCE),
4870 #endif
4871 colfixdual = is_presolve(lp, PRESOLVE_COLFIXDUAL);
4872 int iCoeffChanged = 0, iConRemove = 0, iVarFixed = 0, iBoundTighten = 0,
4873 status = RUNNING, ix, j, countNZ, item;
4874 REAL Value1;
4875
4876 for(j = firstActiveLink(psdata->cols->varmap); (j != 0) && (status == RUNNING); ) {
4877
4878 /* Don't presolve members SOS'es */
4879 if(SOS_is_member(lp->SOS, 0, j)) {
4880 j = nextActiveLink(psdata->cols->varmap, j);
4881 continue;
4882 }
4883
4884 /* Initialize */
4885 countNZ = presolve_collength(psdata, j);
4886 isOFNZ = isnz_origobj(lp, j);
4887 Value1 = get_lowbo(lp, j);
4888 unbounded = is_unbounded(lp, j);
4889
4890 /* Clear unnecessary semicont-definitions */
4891 if((lp->sc_vars > 0) && (Value1 == 0) && is_semicont(lp, j))
4892 set_semicont(lp, j, FALSE);
4893
4894 candelete = FALSE;
4895 item = 0;
4896 ix = lp->rows + j;
4897
4898 /* Check if the variable is unused */
4899 if((countNZ == 0) && !isOFNZ) {
4900 if(Value1 != 0)
4901 report(lp, DETAILED, "presolve_columns: Eliminated unused variable %s\n",
4902 get_col_name(lp,j));
4903 candelete = TRUE;
4904 }
4905
4906 /* Check if the variable has a cost, but is not limited by constraints */
4907 else if((countNZ == 0) && isOFNZ) {
4908 if(lp->orig_obj[j] < 0)
4909 Value1 = get_upbo(lp, j);
4910 if(fabs(Value1) >= lp->infinite) {
4911 report(lp, DETAILED, "presolve_columns: Unbounded variable %s\n",
4912 get_col_name(lp,j));
4913 status = presolve_setstatus(psdata, UNBOUNDED);
4914 }
4915 else {
4916 /* Fix the value at its best bound */
4917 report(lp, DETAILED, "presolve_columns: Eliminated trivial variable %s fixed at %g\n",
4918 get_col_name(lp,j), Value1);
4919 candelete = TRUE;
4920 }
4921 }
4922
4923 /* Check if the variable can be eliminated because it is fixed */
4924 else if(isOrigFixed(lp, ix)) {
4925 if(countNZ > 0) {
4926 status = presolve_boundconflict(psdata, -1, j);
4927 if(status != RUNNING)
4928 break;
4929 }
4930 report(lp, DETAILED, "presolve_columns: Eliminated variable %s fixed at %g\n",
4931 get_col_name(lp,j), Value1);
4932 candelete = TRUE;
4933 }
4934
4935 #if 0
4936 /* Merge OF-constraint column doubleton in equality constraint (if it has
4937 not been captured by the singleton free variable rule above) */
4938 else if((countNZ == 1) && isOFNZ &&
4939 ((i = presolve_nextrow(psdata, j, &item)) >= 0) &&
4940 is_constr_type(lp, i = COL_MAT_ROWNR(i), EQ)) {
4941 MATrec *mat = lp->matA;
4942
4943 /* Merge the constraint into the OF */
4944 Value1 = lp->orig_obj[j] / get_mat(lp, i, j);
4945 for(jx = mat->row_end[i-1]; jx < mat->row_end[i]; jx++) {
4946 jjx = ROW_MAT_COLNR(jx);
4947 lp->orig_obj[jjx] -= Value1 * ROW_MAT_VALUE(jx);
4948 }
4949 Value2 = lp->orig_rhs[i];
4950 presolve_adjustrhs(psdata, 0, Value1 * Value2, 0.0);
4951
4952 /* Verify feasibility */
4953 Value2 /= get_mat(lp, i, j);
4954 if((Value2 < get_lowbo(lp, j)) || (Value2 > get_upbo(lp, j))) {
4955 status = presolve_setstatus(psdata, INFEASIBLE);
4956 break;
4957 }
4958
4959 /* Do column (and flag row) deletion */
4960 presolve_rowremove(psdata, i, TRUE);
4961 psdata->forceupdate = TRUE;
4962 iConRemove++;
4963 candelete = TRUE;
4964 }
4965 #endif
4966 /* Look for opportunity to fix column based on the dual */
4967 else if(colfixdual && presolve_colfixdual(psdata, j, &Value1, &status)) {
4968 if(my_infinite(lp, Value1)) {
4969 report(lp, DETAILED, "presolve_columns: Unbounded variable %s\n",
4970 get_col_name(lp,j));
4971 status = presolve_setstatus(psdata, UNBOUNDED);
4972 }
4973 else {
4974 /* Fix the value at its best bound */
4975 report(lp, DETAILED, "presolve_columns: Eliminated dual-zero variable %s fixed at %g\n",
4976 get_col_name(lp,j), Value1);
4977 candelete = TRUE;
4978 }
4979 }
4980
4981 /* Do probing of binary variables to see if we can fix them */
4982 else if(probefix && is_binary(lp, j) &&
4983 presolve_probefix01(psdata, j, &Value1)) {
4984 report(lp, DETAILED, "presolve_columns: Fixed binary variable %s at %g\n",
4985 get_col_name(lp,j), Value1);
4986 candelete = TRUE;
4987 }
4988 #if 0
4989 /* Do probing of binary variables to see if we can tighten their coefficients */
4990 else if(probereduce && is_binary(lp, j) &&
4991 (ix = presolve_probetighten01(psdata, j) > 0)) {
4992 report(lp, DETAILED, "presolve_columns: Tightened coefficients for binary variable %s in %d rows\n",
4993 get_col_name(lp,j), ix);
4994 iCoeffChanged += ix;
4995 psdata->forceupdate = TRUE;
4996 }
4997 #endif
4998
4999 /* Perform fixing and deletion, if indicated */
5000 if(candelete) {
5001
5002 /* If we have a SOS1 member variable fixed at a non-zero value, then we
5003 must fix the other member variables at zero and delete the SOS(es) */
5004 if((Value1 != 0) && SOS_is_member(lp->SOS, 0, j)) {
5005 ix = iVarFixed;
5006 if(!presolve_fixSOS1(psdata, j, Value1, &iConRemove, &iVarFixed))
5007 status = presolve_setstatus(psdata, INFEASIBLE);
5008 if(iVarFixed > ix)
5009 psdata->forceupdate = TRUE;
5010 break;
5011 }
5012 else {
5013 if(!presolve_colfix(psdata, j, Value1, TRUE, &iVarFixed)) {
5014 status = presolve_setstatus(psdata, INFEASIBLE);
5015 break;
5016 }
5017 j = presolve_colremove(psdata, j, TRUE);
5018 }
5019 }
5020 else
5021 j = nextActiveLink(psdata->cols->varmap, j);
5022 }
5023
5024 /* Remove any "hanging" empty row and columns */
5025 if(status == RUNNING)
5026 status = presolve_shrink(psdata, &iConRemove, &iVarFixed);
5027
5028 (*nCoeffChanged) += iCoeffChanged;
5029 (*nConRemove) += iConRemove;
5030 (*nVarFixed) += iVarFixed;
5031 (*nBoundTighten) += iBoundTighten;
5032 (*nSum) += iCoeffChanged+iConRemove+iVarFixed+iBoundTighten;
5033
5034 return( status );
5035 }
5036
5037 STATIC int presolve_freeandslacks(presolverec *psdata, int *nCoeffChanged, int *nConRemove, int *nVarFixed, int *nSum)
5038 {
5039 lprec *lp = psdata->lp;
5040 MYBOOL isOFNZ, unbounded,
5041 impliedfree = is_presolve(lp, PRESOLVE_IMPLIEDFREE),
5042 impliedslack = is_presolve(lp, PRESOLVE_IMPLIEDSLK);
5043 int iCoeffChanged = 0, iConRemove = 0, iVarFixed = 0,
5044 status = RUNNING, i, ix, j, countNZ;
5045 REAL coeff_bl, coeff_bu;
5046 MATrec *mat = lp->matA;
5047
5048 if(impliedfree || impliedslack)
5049 for(j = firstActiveLink(psdata->cols->varmap); j != 0; ) {
5050
5051 /* Check and initialize */
5052 if((presolve_collength(psdata, j) != 1) ||
5053 is_int(lp, j) || is_semicont(lp, j) ||
5054 !presolve_candeletevar(psdata, j)) {
5055 j = nextActiveLink(psdata->cols->varmap, j);
5056 continue;
5057 }
5058 ix = 0;
5059 i = COL_MAT_ROWNR(presolve_nextrow(psdata, j, &ix));
5060 isOFNZ = isnz_origobj(lp, j);
5061 countNZ = presolve_rowlength(psdata, i);
5062 coeff_bu = get_upbo(lp, j);
5063 coeff_bl = get_lowbo(lp, j);
5064 unbounded = my_infinite(lp, coeff_bl) && my_infinite(lp, coeff_bu);
5065 ix = lp->rows + j;
5066
5067 /* Eliminate singleton free variable and its associated constraint */
5068 if(impliedfree && unbounded &&
5069 presolve_impliedcolfix(psdata, i, j, TRUE)) {
5070 report(lp, DETAILED, "presolve_freeandslacks: Eliminated free variable %s and row %s\n",
5071 get_col_name(lp, j), get_row_name(lp, i));
5072 presolve_rowremove(psdata, i, TRUE);
5073 iConRemove++;
5074 j = presolve_colremove(psdata, j, TRUE);
5075 iVarFixed++;
5076 }
5077
5078 /* Check for implied slack variable in equality constraint */
5079 else if(impliedslack &&
5080 (countNZ > 1) &&
5081 is_constr_type(lp, i, EQ) &&
5082 presolve_impliedcolfix(psdata, i, j, FALSE)) {
5083 report(lp, DETAILED, "presolve_freeandslacks: Eliminated implied slack variable %s via row %s\n",
5084 get_col_name(lp, j), get_row_name(lp, i));
5085 psdata->forceupdate = TRUE;
5086 j = presolve_colremove(psdata, j, TRUE);
5087 iVarFixed++;
5088 }
5089
5090 /* Check for implied (generalized) slack variable in inequality constraint */
5091 else if(impliedslack && !isOFNZ &&
5092 my_infinite(lp, coeff_bu) && /* Consider removing this test */
5093 #if 0 /* Force zero-bounded implicit slack */
5094 (coeff_bl == 0)) &&
5095 #else
5096 !my_infinite(lp, coeff_bl) &&
5097 #endif
5098 (countNZ > 1) &&
5099 !is_constr_type(lp, i, EQ)) {
5100 REAL *target,
5101 ValueA = COL_MAT_VALUE(presolve_lastrow(psdata, j));
5102 #if 0
5103 coeff_bu = get_rh_upper(lp, i);
5104 coeff_bl = get_rh_lower(lp, i);
5105 if(!presolve_singletonbounds(psdata, i, j, &coeff_bl, &coeff_bu, &ValueA)) {
5106 status = presolve_setstatus(psdata, INFEASIBLE);
5107 break;
5108 }
5109 #endif
5110 if((coeff_bl != 0) && !my_infinite(lp, coeff_bl) && !my_infinite(lp, coeff_bu))
5111 coeff_bu -= coeff_bl;
5112
5113 /* If the coefficient is negative, reduce the lower bound / increase range */
5114 if(ValueA > 0) {
5115 target = &lp->orig_upbo[i];
5116 if(!my_infinite(lp, *target)) {
5117 if(my_infinite(lp, coeff_bu)) {
5118 *target = lp->infinite;
5119 psdata->forceupdate = TRUE;
5120 }
5121 else {
5122 *target += ValueA * coeff_bu;
5123 *target = presolve_roundrhs(lp, *target, FALSE);
5124 }
5125 }
5126 }
5127 /* Otherwise see if the upper bound should be changed */
5128 else {
5129 target = &lp->orig_rhs[i];
5130 if(my_infinite(lp, coeff_bu) || my_infinite(lp, *target)) {
5131 /* Do we suddenly find that the constraint becomes redundant? (e226.mps) */
5132 if(my_infinite(lp, lp->orig_upbo[i])) {
5133 presolve_rowremove(psdata, i, TRUE);
5134 iConRemove++;
5135 }
5136 /* Or does the upper bound of a ranged constraint become Inf? */
5137 else {
5138 *target -= lp->orig_upbo[i];
5139 *target = -(*target);
5140 mat_multrow(mat, i, -1);
5141 lp->orig_upbo[i] = lp->infinite;
5142 psdata->forceupdate = TRUE;
5143 }
5144 }
5145 else {
5146 *target -= ValueA * coeff_bu;
5147 *target = presolve_roundrhs(lp, *target, FALSE);
5148 }
5149 }
5150 presolve_colfix(psdata, j, coeff_bl, TRUE, &iVarFixed);
5151 report(lp, DETAILED, "presolve_freeandslacks: Eliminated duplicate slack variable %s via row %s\n",
5152 get_col_name(lp, j), get_row_name(lp, i));
5153 j = presolve_colremove(psdata, j, TRUE);
5154 }
5155
5156 /* Go to next column */
5157 else
5158 j = nextActiveLink(psdata->cols->varmap, j);
5159 }
5160
5161 (*nCoeffChanged) += iCoeffChanged;
5162 (*nConRemove) += iConRemove;
5163 (*nVarFixed) += iVarFixed;
5164 (*nSum) += iCoeffChanged+iConRemove+iVarFixed;
5165
5166 return( status );
5167 }
5168
5169 STATIC int presolve_preparerows(presolverec *psdata, int *nBoundTighten, int *nSum)
5170 {
5171 lprec *lp = psdata->lp;
5172 MYBOOL impliedfree = is_presolve(lp, PRESOLVE_IMPLIEDFREE),
5173 tightenbounds = is_presolve(lp, PRESOLVE_BOUNDS);
5174 int iRangeTighten = 0, iBoundTighten = 0, status = RUNNING, i, j;
5175 REAL losum, upsum, lorhs, uprhs, epsvalue = psdata->epsvalue;
5176 MATrec *mat = lp->matA;
5177
5178 for(i = lastActiveLink(psdata->rows->varmap); i > 0; i = prevActiveLink(psdata->rows->varmap, i)) {
5179
5180 /* First identify any full row infeasibilities */
5181 j = presolve_rowlengthex(psdata, i);
5182 #ifdef Paranoia
5183 if(!presolve_testrow(psdata, nextActiveLink(psdata->rows->varmap, i))) {
5184 #else
5185 if((j > 1) && !psdata->forceupdate && !presolve_rowfeasible(psdata, i, FALSE)) {
5186 #endif
5187 status = presolve_setstatus(psdata, INFEASIBLE);
5188 break;
5189 }
5190
5191 /* Do bound (LHS) or constraint range (RHS) tightening if we will later identify
5192 implied free variables (tends to produce degeneracy otherwise) */
5193 if(impliedfree && (j > 1) && mat_validate(mat)){
5194
5195 /* Look for opportunity to tighten constraint bounds (and check for feasibility again) */
5196 presolve_range(lp, i, psdata->rows, &losum, &upsum);
5197 lorhs = get_rh_lower(lp, i);
5198 uprhs = get_rh_upper(lp, i);
5199 if((losum > MIN(upsum, uprhs)+epsvalue) ||
5200 (upsum < MAX(losum, lorhs)-epsvalue)) {
5201 report(lp, NORMAL, "presolve_preparerows: Variable bound / constraint value infeasibility in row %s.\n",
5202 get_row_name(lp, i));
5203 status = presolve_setstatus(psdata, INFEASIBLE);
5204 break;
5205 }
5206
5207 if(losum > lorhs+epsvalue) {
5208 set_rh_lower(lp, i, presolve_roundrhs(lp, losum, TRUE));
5209 iRangeTighten++;
5210 }
5211 if(upsum < uprhs-epsvalue) {
5212 set_rh_upper(lp, i, presolve_roundrhs(lp, upsum, FALSE));
5213 iRangeTighten++;
5214 }
5215 }
5216
5217 /* Seek to tighten bounds on individual variables */
5218 if(tightenbounds && mat_validate(mat)) {
5219 #if 1
5220 if(j > 1)
5221 status = presolve_rowtighten(psdata, i, &iBoundTighten, FALSE);
5222 #else
5223 if((MIP_count(lp) > 0) && (j > 1))
5224 status = presolve_rowtighten(psdata, i, &iBoundTighten, TRUE);
5225 #endif
5226 }
5227
5228 /* Look for opportunity to convert ranged constraint to equality-type */
5229 if(!is_constr_type(lp, i, EQ) && (get_rh_range(lp, i) < epsvalue)) {
5230 presolve_setEQ(psdata, i);
5231 iRangeTighten++;
5232 }
5233 }
5234
5235 psdata->forceupdate |= (MYBOOL) (iBoundTighten > 0);
5236 (*nBoundTighten) += iBoundTighten+iRangeTighten;
5237 (*nSum) += iBoundTighten+iRangeTighten;
5238
5239 return( status );
5240 }
5241
5242 STATIC int presolve_rows(presolverec *psdata, int *nCoeffChanged, int *nConRemove, int *nVarFixed, int *nBoundTighten, int *nSum)
5243 {
5244 lprec *lp = psdata->lp;
5245 MYBOOL candelete;
5246 int iCoeffChanged = 0, iConRemove = 0, iVarFixed = 0, iBoundTighten = 0,
5247 status = RUNNING, i,ix, j,jx, item;
5248 REAL Value1, Value2, losum, upsum, lorhs, uprhs, epsvalue = psdata->epsvalue;
5249 MATrec *mat = lp->matA;
5250
5251 for(i = lastActiveLink(psdata->rows->varmap); (i > 0) && (status == RUNNING); ) {
5252
5253 candelete = FALSE;
5254
5255 /* First identify any full row infeasibilities
5256 Note: Handle singletons below to ensure that conflicting multiple singleton
5257 rows with this variable do not provoke notice of infeasibility */
5258 j = presolve_rowlengthex(psdata, i);
5259 if((j > 1) &&
5260 !psdata->forceupdate && !presolve_rowfeasible(psdata, i, FALSE)) {
5261 status = presolve_setstatus(psdata, INFEASIBLE);
5262 break;
5263 }
5264 presolve_range(lp, i, psdata->rows, &losum, &upsum);
5265 lorhs = get_rh_lower(lp, i);
5266 uprhs = get_rh_upper(lp, i);
5267 #ifdef Paranoia
5268 if((losum>uprhs+epsvalue) || (upsum<lorhs-epsvalue)) {
5269 status = presolve_setstatus(psdata, INFEASIBLE);
5270 break;
5271 }
5272 #endif
5273
5274 /* Delete empty rows */
5275 if(j == 0)
5276 candelete = TRUE;
5277 else
5278
5279 /* Convert non-fixed row singletons to bounds */
5280 #if 0 /* Version that deletes bound-fixed columns in presolve_columns() */
5281 if((j == 1) && (upsum-losum >= -epsvalue)) {
5282 #else /* Version that deletes bound-fixed columns here */
5283 if((j == 1) && (uprhs-lorhs >= -epsvalue)) {
5284 #endif
5285 item = 0;
5286 jx = presolve_nextcol(psdata, i, &item);
5287 j = ROW_MAT_COLNR(jx);
5288
5289 /* Make sure we don't have conflicting other singleton rows with this variable */
5290 Value1 = lp->infinite;
5291 Value2 = -Value1;
5292 if(presolve_collength(psdata, j) > 1)
5293 status = presolve_boundconflict(psdata, i, j);
5294 else if(is_constr_type(lp, i, EQ)) {
5295 Value2 = ROW_MAT_VALUE(jx);
5296 Value1 = lp->orig_rhs[i] / Value2;
5297 if(Value2 < 0)
5298 swapREAL(&losum, &upsum);
5299 if((Value1 < losum / my_if(my_infinite(lp, losum), my_sign(Value2), Value2) - epsvalue) ||
5300 (Value1 > upsum / my_if(my_infinite(lp, upsum), my_sign(Value2), Value2) + epsvalue))
5301 status = presolve_setstatus(psdata, INFEASIBLE);
5302 Value2 = Value1;
5303 }
5304
5305 /* Proceed to fix and remove variable (if it is not a SOS member) */
5306 if(status == RUNNING) {
5307 if((fabs(Value2-Value1) < epsvalue) && (fabs(Value2) > epsvalue)) {
5308 MYBOOL isSOS = (MYBOOL) (SOS_is_member(lp->SOS, 0, j) != FALSE),
5309 deleteSOS = isSOS && presolve_candeletevar(psdata, j);
5310 if((Value1 != 0) && deleteSOS) {
5311 if(!presolve_fixSOS1(psdata, j, Value1, &iConRemove, &iVarFixed))
5312 status = presolve_setstatus(psdata, INFEASIBLE);
5313 psdata->forceupdate = TRUE;
5314 }
5315 else {
5316 if(!presolve_colfix(psdata, j, Value1, (MYBOOL) !isSOS, NULL))
5317 status = presolve_setstatus(psdata, INFEASIBLE);
5318 else if(isSOS && !deleteSOS)
5319 iBoundTighten++;
5320 else {
5321 presolve_colremove(psdata, j, TRUE);
5322 iVarFixed++;
5323 }
5324 }
5325 }
5326 else
5327 status = presolve_colsingleton(psdata, i, j, &iBoundTighten);
5328 }
5329 if(status == INFEASIBLE) {
5330 break;
5331 }
5332 if(psdata->forceupdate != AUTOMATIC) {
5333 /* Store dual recovery information and code for deletion */
5334 presolve_storeDualUndo(psdata, i, j);
5335 candelete = TRUE;
5336 }
5337 }
5338
5339 /* Delete non-empty rows and variables that are completely determined at zero */
5340 else if((j > 0) /* Only examine non-empty rows, */
5341 && (fabs(lp->orig_rhs[i]) < epsvalue) /* .. and the current RHS is zero, */
5342 && ((psdata->rows->plucount[i] == 0) ||
5343 (psdata->rows->negcount[i] == 0)) /* .. and the parameter signs are all equal, */
5344 && (psdata->rows->pluneg[i] == 0) /* .. and no (quasi) free variables, */
5345 && (is_constr_type(lp, i, EQ)
5346 #ifdef FindImpliedEqualities
5347 || (fabs(lorhs-upsum) < epsvalue) /* Convert to equalities */
5348 || (fabs(uprhs-losum) < epsvalue) /* Convert to equalities */
5349 #endif
5350 )
5351 ) {
5352
5353 /* Delete the columns we can delete */
5354 status = presolve_rowfixzero(psdata, i, &iVarFixed);
5355
5356 /* Then delete the row, which is redundant */
5357 if(status == RUNNING)
5358 candelete = TRUE;
5359 }
5360
5361
5362 /* Check if we have a constraint made redundant through bounds on individual
5363 variables; such constraints are often referred to as "forcing constraints" */
5364 else if((losum >= lorhs-epsvalue) &&
5365 (upsum <= uprhs+epsvalue)) {
5366
5367 /* Check if we can also fix all the variables */
5368 if(fabs(losum-upsum) < epsvalue) {
5369 item = 0;
5370 jx = presolve_nextcol(psdata, i, &item);
5371 while((status == RUNNING) && (jx >= 0)) {
5372 j = ROW_MAT_COLNR(jx);
5373 Value1 = get_lowbo(lp, j);
5374 if(presolve_colfix(psdata, j, Value1, TRUE, &iVarFixed)) {
5375 presolve_colremove(psdata, j, TRUE);
5376 iVarFixed++;
5377 jx = presolve_nextcol(psdata, i, &item);
5378 }
5379 else
5380 status = presolve_setstatus(psdata, INFEASIBLE);
5381 }
5382 }
5383 candelete = TRUE;
5384 }
5385
5386 /* Get next row and do the deletion of the previous, if indicated */
5387 ix = i;
5388 i = prevActiveLink(psdata->rows->varmap, i);
5389 if(candelete) {
5390 presolve_rowremove(psdata, ix, TRUE);
5391 iConRemove++;
5392 }
5393 }
5394
5395 /* Remove any "hanging" empty row and columns */
5396 if(status == RUNNING)
5397 status = presolve_shrink(psdata, &iConRemove, &iVarFixed);
5398
5399 (*nCoeffChanged) += iCoeffChanged;
5400 (*nConRemove) += iConRemove;
5401 (*nVarFixed) += iVarFixed;
5402 (*nBoundTighten) += iBoundTighten;
5403 (*nSum) += iCoeffChanged+iConRemove+iVarFixed+iBoundTighten;
5404
5405 return( status );
5406 }
5407
5408 /* Top level presolve routine */
5409 STATIC int presolve(lprec *lp)
5410 {
5411 int status = RUNNING,
5412 i, j = 0, jx = 0, jjx = 0, k, oSum,
5413 iCoeffChanged = 0, iConRemove = 0, iVarFixed = 0, iBoundTighten = 0, iSOS = 0, iSum = 0,
5414 nCoeffChanged = 0, nConRemove = 0, nVarFixed = 0, nBoundTighten = 0, nSOS = 0, nSum = 0;
5415 REAL Value1, Value2, initrhs0 = lp->orig_rhs[0];
5416 presolverec *psdata = NULL;
5417 MATrec *mat = lp->matA;
5418
5419 #if 0
5420 lp->do_presolve = PRESOLVE_ROWS;
5421 report(lp, IMPORTANT, "presolve: Debug override of presolve setting to %d\n", lp->do_presolve);
5422 #endif
5423
5424 /* Lock the variable mapping arrays and counts ahead of any row/column
5425 deletion or creation in the course of presolve, solvelp or postsolve */
5426 if(!lp->varmap_locked)
5427 varmap_lock(lp);
5428
5429 /* Check if we have already done presolve */
5430 mat_validate(mat);
5431 if(lp->wasPresolved) {
5432 if(SOS_count(lp) > 0) {
5433 SOS_member_updatemap(lp->SOS);
5434 make_SOSchain(lp, (MYBOOL) ((lp->do_presolve & PRESOLVE_LASTMASKMODE) != PRESOLVE_NONE));
5435 }
5436 if((lp->solvecount > 1) && (lp->bb_level < 1) &&
5437 ((lp->scalemode & SCALE_DYNUPDATE) != 0))
5438 auto_scale(lp);
5439 if(!lp->basis_valid) {
5440 crash_basis(lp);
5441 report(lp, DETAILED, "presolve: Had to repair broken basis.\n");
5442 }
5443 lp->timepresolved = timeNow();
5444 return(status);
5445 }
5446
5447 /* Produce original model statistics (do hoops to produce correct stats if we have SOS'es) */
5448 i = SOS_count(lp);
5449 if(i > 0) {
5450 SOS_member_updatemap(lp->SOS);
5451 lp->sos_vars = SOS_memberships(lp->SOS, 0);
5452 }
5453 REPORT_modelinfo(lp, TRUE, "SUBMITTED");
5454 report(lp, NORMAL, " \n");
5455 if(i > 0)
5456 lp->sos_vars = 0;
5457
5458 /* Finalize basis indicators; if no basis was created earlier via
5459 set_basis or crash_basis then simply set the default basis. */
5460 if(!lp->basis_valid)
5461 lp->var_basic[0] = AUTOMATIC; /* Flag that we are presolving */
5462
5463 #if 0
5464 write_lp(lp, "test_in.lp"); /* Write to lp-formatted file for debugging */
5465 /*write_mps(lp, "test_in.mps");*/ /* Write to lp-formatted file for debugging */
5466 #endif
5467
5468 /* Update inf norms and check for potential factorization trouble */
5469 mat_computemax(mat /*, FALSE */);
5470 #if 0
5471 Value1 = fabs(lp->negrange);
5472 if(is_obj_in_basis(lp) && (mat->dynrange < Value1) && vec_computeext(lp->orig_obj, 1, lp->columns, TRUE, &i, &j)) {
5473
5474 /* Compute relative scale metric */
5475 Value2 = fabs(lp->orig_obj[j]/lp->orig_obj[i]) / mat->dynrange;
5476 if(Value2 < 1.0)
5477 Value2 = 1.0 / Value2;
5478
5479 /* Determine if we should alert modeler and possibly move the OF out of the coefficient matrix */
5480 if((Value2 > Value1) /* Case with extreme scale difference */
5481 #if 1
5482 || (mat->dynrange == 1.0) /* Case where we have an all-unit coefficient matrix, possibly totally unimodular */
5483 #endif
5484 )
5485 if((lp->simplex_strategy & SIMPLEX_DYNAMIC) > 0) {
5486 clear_action(&lp->algopt, ALGOPT_OBJINBASIS);
5487 report(lp, NORMAL, "Moved objective function out of the basis matrix to enhance factorization accuracy.\n");
5488 }
5489 else if(mat->dynrange > 1.0)
5490 report(lp, IMPORTANT, "Warning: Objective/matrix coefficient magnitude differences will cause inaccuracy!\n");
5491 }
5492 #endif
5493
5494 /* Do traditional simple presolve */
5495 yieldformessages(lp);
5496 if((lp->do_presolve & PRESOLVE_LASTMASKMODE) == PRESOLVE_NONE) {
5497 mat_checkcounts(mat, NULL, NULL, TRUE);
5498 i = 0;
5499 }
5500 else {
5501
5502 if(lp->full_solution == NULL)
5503 allocREAL(lp, &lp->full_solution, lp->sum_alloc+1, TRUE);
5504
5505 /* Identify infeasible SOS'es prior to any pruning */
5506 j = 0;
5507 for(i = 1; i <= SOS_count(lp); i++) {
5508 k = SOS_infeasible(lp->SOS, i);
5509 if(k > 0) {
5510 presolverec psdata;
5511
5512 psdata.lp = lp;
5513 report(lp, NORMAL, "presolve: Found SOS %d (type %d) to be range-infeasible on variable %d\n",
5514 i, SOS_get_type(lp->SOS, i), k);
5515 status = presolve_setstatus(&psdata, INFEASIBLE);
5516 j++;
5517 }
5518 }
5519 if(j > 0)
5520 goto Finish;
5521
5522 /* Create and initialize the presolve data structures */
5523 psdata = presolve_init(lp);
5524
5525 /* Reentry point for the outermost, computationally expensive presolve loop */
5526 psdata->outerloops = 0;
5527 do {
5528 psdata->outerloops++;
5529 iCoeffChanged = 0;
5530 iConRemove = 0;
5531 iVarFixed = 0;
5532 iBoundTighten = 0;
5533 iSOS = 0;
5534 oSum = nSum;
5535
5536 /* Do the middle elimination loop */
5537 do {
5538 psdata->middleloops++;
5539 nSum += iSum;
5540 iSum = 0;
5541
5542 /* Accumulate constraint bounds based on bounds on individual variables. */
5543 j = 0;
5544 while(presolve_statuscheck(psdata, &status) && psdata->forceupdate) {
5545 psdata->forceupdate = FALSE;
5546 /* Update sums, but limit iteration count to avoid possible
5547 "endless" loops with only marginal bound improvements */
5548 if(presolve_updatesums(psdata) && (j < MAX_PSBOUNDTIGHTENLOOPS)) {
5549 /* Do row preparation useful for subsequent column and row presolve operations */
5550 if((psdata->outerloops == 1) && (psdata->middleloops == 1))
5551 status = presolve_preparerows(psdata, &iBoundTighten, &iSum);
5552 nBoundTighten += iBoundTighten;
5553 iBoundTighten = 0;
5554 nSum += iSum;
5555 iSum = 0;
5556 j++;
5557 if(status != RUNNING)
5558 report(lp, NORMAL, "presolve: Break after bound tightening iteration %d.\n", j);
5559 }
5560 }
5561 if(status != RUNNING)
5562 break;
5563
5564 /* Do the relatively cheap innermost elimination loop */
5565 do {
5566
5567 psdata->innerloops++;
5568 nSum += iSum;
5569 iSum = 0;
5570
5571 /* Eliminate empty rows, convert row singletons to bounds,
5572 tighten bounds, and remove always satisfied rows */
5573 if(presolve_statuscheck(psdata, &status) &&
5574 is_presolve(lp, PRESOLVE_ROWS))
5575 status = presolve_rows(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iBoundTighten, &iSum);
5576
5577 /* Eliminate empty or fixed columns (including trivial OF column singletons) */
5578 if(presolve_statuscheck(psdata, &status) &&
5579 is_presolve(lp, PRESOLVE_COLS))
5580 status = presolve_columns(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iBoundTighten, &iSum);
5581
5582 /* Presolve SOS'es if possible (always do this) */
5583 if(presolve_statuscheck(psdata, &status))
5584 status = presolve_redundantSOS(psdata, &iBoundTighten, &iSum);
5585
5586 } while((status == RUNNING) && (iSum > 0));
5587 if(status != RUNNING)
5588 break;
5589
5590 /* Merge compatible similar rows; loop backwards over every row */
5591 if(presolve_statuscheck(psdata, &status) &&
5592 (psdata->outerloops == 1) && (psdata->middleloops <= MAX_PSMERGELOOPS) &&
5593 is_presolve(lp, PRESOLVE_MERGEROWS))
5594 status = presolve_mergerows(psdata, &iConRemove, &iSum);
5595
5596 /* Eliminate dominated rows */
5597 if(presolve_statuscheck(psdata, &status) &&
5598 is_presolve(lp, PRESOLVE_ROWDOMINATE))
5599 presolve_rowdominance(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iSum);
5600
5601 /* See if we can convert some constraints to SOSes (only SOS1 handled) */
5602 if(presolve_statuscheck(psdata, &status) && (MIP_count(lp) > 0) &&
5603 is_presolve(lp, PRESOLVE_SOS))
5604 status = presolve_SOS1(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iSOS, &iSum);
5605
5606 /* Eliminate dominated columns in set coverage models */
5607 if(presolve_statuscheck(psdata, &status) && (lp->int_vars > 1) &&
5608 is_presolve(lp, PRESOLVE_COLDOMINATE))
5609 presolve_coldominance01(psdata, &iConRemove, &iVarFixed, &iSum);
5610
5611 /* Aggregate compatible columns */
5612 if(presolve_statuscheck(psdata, &status) && /*TRUE ||*/
5613 is_presolve(lp, PRESOLVE_AGGREGATE))
5614 presolve_aggregate(psdata, &iConRemove, &iVarFixed, &iSum);
5615
5616 /* Eliminate free variables and implied slacks */
5617 if(presolve_statuscheck(psdata, &status) &&
5618 /* !is_presolve(lp, PRESOLVE_ELIMEQ2) && */
5619 is_presolve(lp, PRESOLVE_IMPLIEDSLK | PRESOLVE_IMPLIEDFREE))
5620 status = presolve_freeandslacks(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iSum);
5621
5622 } while((status == RUNNING) && (iSum > 0));
5623 if(status != RUNNING)
5624 break;
5625
5626 /* Check if we can do elimination of rank-deficient equality constraints */
5627 if(presolve_statuscheck(psdata, &status) && (psdata->EQmap->count > 1) &&
5628 is_presolve(lp, PRESOLVE_LINDEP)) {
5629 #if 0
5630 REPORT_mat_mmsave(lp, "A.mtx", NULL, FALSE, "Constraint matrix A");
5631 #endif
5632 presolve_singularities(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iSum);
5633 }
5634
5635 /* Eliminate variable and tighten bounds using 2-element EQs;
5636 note that this involves modifying the coefficients of A and
5637 can therefore be a slow operation. */
5638 if(presolve_statuscheck(psdata, &status) &&
5639 is_presolve(lp, PRESOLVE_ELIMEQ2)) {
5640 jjx = 0;
5641 do {
5642 jjx += iSum;
5643 status = presolve_elimeq2(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iSum);
5644 } while((status == RUNNING) && (iSum > jjx));
5645 iSum = jjx;
5646
5647 #if 0
5648 /* Eliminate free variables and implied slacks */
5649 if(presolve_statuscheck(psdata, &status) &&
5650 is_presolve(lp, PRESOLVE_IMPLIEDSLK | PRESOLVE_IMPLIEDFREE))
5651 status = presolve_freeandslacks(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iSum);
5652 #endif
5653 }
5654
5655 /* Increase A matrix sparsity by discovering common subsets using EQs */
5656 if(presolve_statuscheck(psdata, &status) && (psdata->EQmap->count > 0) &&
5657 is_presolve(lp, PRESOLVE_SPARSER))
5658 status = presolve_makesparser(psdata, &iCoeffChanged, &iConRemove, &iVarFixed, &iSum);
5659
5660 /* Do GCD-based coefficient reductions (also does row scaling,
5661 even if no rhs INT truncations are possible) */
5662 if(presolve_statuscheck(psdata, &status) && (psdata->INTmap->count > 0) &&
5663 is_presolve(lp, PRESOLVE_REDUCEGCD))
5664 if(!presolve_reduceGCD(psdata, &iCoeffChanged, &iBoundTighten, &iSum))
5665 status = presolve_setstatus(psdata, INFEASIBLE);
5666
5667 /* Simplify knapsack or set coverage models where OF coefficients are
5668 duplicated in the constraints. At the cost of adding helper columns, this
5669 increases sparsity and facilitates identification of lower and upper bounds. */
5670 if(presolve_statuscheck(psdata, &status) &&
5671 is_presolve(lp, PRESOLVE_KNAPSACK)) {
5672 i = iCoeffChanged;
5673 status = presolve_knapsack(psdata, &iCoeffChanged);
5674 }
5675
5676 /* Remove any "hanging" empty row and columns */
5677 if(status == RUNNING)
5678 status = presolve_shrink(psdata, &iConRemove, &iVarFixed);
5679
5680 nCoeffChanged += iCoeffChanged;
5681 nConRemove += iConRemove;
5682 nVarFixed += iVarFixed;
5683 nBoundTighten += iBoundTighten;
5684 nSOS += iSOS;
5685 nSum += iSum;
5686
5687 iSum = iConRemove + iVarFixed + iBoundTighten + iCoeffChanged;
5688 if(iSum > 0)
5689 report(lp, NORMAL, "Presolve O:%d -> Reduced rows:%5d, cols:%5d --- changed bnds:%5d, Ab:%5d.\n",
5690 psdata->outerloops, iConRemove, iVarFixed, iBoundTighten, iCoeffChanged);
5691
5692 /* Do the outermost loop again if we were successful in this presolve sequences */
5693 } while(presolve_statuscheck(psdata, &status) &&
5694 (psdata->forceupdate || (oSum < nSum)) &&
5695 (psdata->outerloops < get_presolveloops(lp)) &&
5696 (psdata->rows->varmap->count+psdata->cols->varmap->count > 0));
5697
5698 /* Finalize presolve */
5699 #ifdef Paranoia
5700 i = presolve_debugcheck(lp, psdata->rows->varmap, psdata->cols->varmap);
5701 if(i > 0)
5702 report(lp, SEVERE, "presolve: %d internal consistency failure%s\n", i, my_plural_std(i));
5703 if((SOS_count(lp) > 0) && !presolve_SOScheck(psdata))
5704 report(lp, SEVERE, "presolve: SOS sparse member mapping problem - part 1\n");
5705 #endif
5706 /* Perform bound relaxation to reduce chance of degeneracy. */
5707 if((status == RUNNING) && !is_presolve(lp, PRESOLVE_IMPLIEDFREE))
5708 jjx = presolve_makefree(psdata);
5709 else
5710 jjx = 0;
5711
5712
5713 /* Finalize the presolve */
5714 if(!presolve_finalize(psdata))
5715 report(lp, SEVERE, "presolve: Unable to construct internal data representation\n");
5716
5717 /* Report summary information */
5718 i = NORMAL;
5719 iVarFixed = lp->presolve_undo->orig_columns - psdata->cols->varmap->count;
5720 iConRemove = lp->presolve_undo->orig_rows - psdata->rows->varmap->count;
5721 if(nSum > 0)
5722 report(lp, i, "PRESOLVE Elimination loops performed.......... O%d:M%d:I%d\n",
5723 psdata->outerloops, psdata->middleloops, psdata->innerloops);
5724 if(nVarFixed)
5725 report(lp, i, " %8d empty or fixed variables............. %s.\n", nVarFixed, "REMOVED");
5726 if(nConRemove)
5727 report(lp, i, " %8d empty or redundant constraints....... %s.\n", nConRemove, "REMOVED");
5728 if(nBoundTighten)
5729 report(lp, i, " %8d bounds............................... %s.\n", nBoundTighten, "TIGHTENED");
5730 if(nCoeffChanged)
5731 report(lp, i, " %8d matrix coefficients.................. %s.\n", nCoeffChanged, "CHANGED");
5732 if(jjx > 0)
5733 report(lp, i, " %8d variables' final bounds.............. %s.\n", jjx, "RELAXED");
5734 if(nSOS)
5735 report(lp, i, " %8d constraints detected as SOS1......... %s.\n", nSOS, "CONVERTED");
5736
5737 /* Report optimality or infeasibility */
5738 if(status == UNBOUNDED)
5739 report(lp, NORMAL, "%20s Solution status detected............. %s.\n", "", "UNBOUNDED");
5740 else if(status == INFEASIBLE)
5741 report(lp, NORMAL, "%20s Solution status detected............. %s.\n", "", "INFEASIBLE");
5742 else {
5743 if(psdata->cols->varmap->count == 0)
5744 Value1 = Value2 = lp->presolve_undo->fixed_rhs[0] -initrhs0;
5745 else
5746 presolve_rangeorig(lp, 0, psdata->rows, &Value1, &Value2, -initrhs0);
5747 if((fabs(Value1 - Value2) < psdata->epsvalue) || (fabs(my_reldiff(Value1, Value2)) < psdata->epsvalue)) {
5748 if((lp->rows == 0) && (lp->columns == 0)) {
5749 status = PRESOLVED;
5750 Value1 = my_chsign(is_maxim(lp), Value1);
5751 lp->solution[0] = Value1;
5752 lp->best_solution[0] = Value1;
5753 lp->full_solution[0] = Value1;
5754 }
5755 report(lp, NORMAL, "%20s OPTIMAL solution found............... %-g", "", Value1);
5756 }
5757 else if((status == RUNNING) && (i >= NORMAL)) {
5758 char lonum[20], upnum[20];
5759 if(my_infinite(lp, Value1))
5760 sprintf(lonum, "%13s", "-Inf");
5761 else
5762 sprintf(lonum, "%+12g", Value1);
5763 if(my_infinite(lp, Value2))
5764 sprintf(upnum, "%-13s", "Inf");
5765 else
5766 sprintf(upnum, "%+-12g", Value2);
5767 report(lp, i, "%20s [ %s < Z < %s ]\n", "", lonum, upnum);
5768 }
5769
5770 /* Update values for dual limit and best heuristic values */
5771 if((MIP_count(lp) > 0) || (get_Lrows(lp) > 0)) {
5772 if(is_maxim(lp)) {
5773 SETMAX(lp->bb_heuristicOF, Value1);
5774 SETMIN(lp->bb_limitOF, Value2);
5775 }
5776 else {
5777 SETMIN(lp->bb_heuristicOF, Value2);
5778 SETMAX(lp->bb_limitOF, Value1);
5779 }
5780 }
5781 }
5782 report(lp, NORMAL, " \n");
5783
5784 /* Clean up (but save counts of constraint types for display later) */
5785 j = psdata->LTmap->count;
5786 jx = psdata->EQmap->count;
5787 jjx = lp->rows - j - jx;
5788 presolve_free(&psdata);
5789
5790 }
5791
5792 /* Signal that we are done presolving */
5793 if((lp->usermessage != NULL) &&
5794 ((lp->do_presolve & PRESOLVE_LASTMASKMODE) != 0) && (lp->msgmask & MSG_PRESOLVE))
5795 lp->usermessage(lp, lp->msghandle, MSG_PRESOLVE);
5796
5797 /* Create master SOS variable list */
5798 if(SOS_count(lp) > 0) {
5799 /*SOS_member_updatemap(lp->SOS); */
5800 make_SOSchain(lp, (MYBOOL) ((lp->do_presolve & PRESOLVE_LASTMASKMODE) != PRESOLVE_NONE));
5801 }
5802
5803 /* Finalize model not identified as infeasible or unbounded */
5804 if(status == RUNNING) {
5805
5806 /* Resolve GUBs */
5807 if(is_bb_mode(lp, NODE_GUBMODE))
5808 identify_GUB(lp, TRUE);
5809
5810 #if 0
5811 /* Mark rows containing hidden identity matrices so that supporting factorization
5812 engines can use this structural information to boost efficiency */
5813 if(is_algopt(lp, ALGOPT_COMPACTBPF))
5814 lp->bfpoptimize = (MYBOOL) (assist_factorization(lp, ROWTYPE_LOGICAL,
5815 &lp->rowset1, &lp->rowno1) > 0);
5816 #endif
5817
5818 /* Scale the model based on current settings */
5819 auto_scale(lp);
5820
5821 /* Crash the basis, if specified */
5822 crash_basis(lp);
5823
5824 /* Produce presolved model statistics */
5825 if(nConRemove+nVarFixed+nBoundTighten+nVarFixed+nCoeffChanged > 0) {
5826 REPORT_modelinfo(lp, FALSE, "REDUCED");
5827 if(nSum > 0) {
5828 report(lp, NORMAL, "Row-types: %7d LE, %7d GE, %7d EQ.\n",
5829 j, jjx, jx);
5830 report(lp, NORMAL, " \n");
5831 }
5832 }
5833 }
5834
5835 /* Optionally produce data on constraint classes */
5836 if(lp->verbose > NORMAL) {
5837 report(lp, NORMAL, " \n");
5838 REPORT_constraintinfo(lp, "CONSTRAINT CLASSES");
5839 report(lp, NORMAL, " \n");
5840 }
5841
5842 Finish:
5843 lp->wasPresolved = TRUE;
5844 lp->timepresolved = timeNow();
5845
5846 #if 0
5847 /* write_mps(lp, "test_out.mps"); */ /* Must put here due to variable name mapping */
5848 write_lp(lp, "test_out.lp"); /* Must put here due to variable name mapping */
5849 #endif
5850 #if 0
5851 REPORT_debugdump(lp, "testint2.txt", FALSE);
5852 #endif
5853
5854 return( status );
5855
5856 }
5857
5858 STATIC MYBOOL postsolve(lprec *lp, int status)
5859 {
5860 /* Verify solution */
5861 if(lp->lag_status != RUNNING) {
5862 int itemp;
5863
5864 if(status == PRESOLVED)
5865 status = OPTIMAL;
5866
5867 if((status == OPTIMAL) || (status == SUBOPTIMAL)) {
5868 itemp = check_solution(lp, lp->columns, lp->best_solution,
5869 lp->orig_upbo, lp->orig_lowbo, lp->epssolution);
5870 if((itemp != OPTIMAL) && (lp->spx_status == OPTIMAL))
5871 lp->spx_status = itemp;
5872 else if((itemp == OPTIMAL) && ((status == SUBOPTIMAL) || (lp->spx_status == PRESOLVED)))
5873 lp->spx_status = status;
5874 }
5875 else if(status != PRESOLVED) {
5876 report(lp, NORMAL, "lp_solve unsuccessful after %.0f iter and a last best value of %g\n",
5877 (double) get_total_iter(lp), lp->best_solution[0]);
5878 if(lp->bb_totalnodes > 0)
5879 report(lp, NORMAL, "lp_solve explored %.0f nodes before termination\n",
5880 (double) get_total_nodes(lp));
5881 }
5882 else
5883 lp->spx_status = OPTIMAL;
5884
5885 /* Only rebuild primal solution here, since the dual is only computed on request */
5886 presolve_rebuildUndo(lp, TRUE);
5887 }
5888
5889 /* Check if we can clear the variable map */
5890 if(varmap_canunlock(lp))
5891 lp->varmap_locked = FALSE;
5892 #if 0
5893 REPORT_mat_mmsave(lp, "basis.mtx", NULL, FALSE); /* Write the current basis matrix (no OF) */
5894 #endif
5895
5896 return( TRUE );
5897 }
5898