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