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