Esempio n. 1
0
/*
  This routine looks to be something of a work in progres. See the comment
  that begins with `Gack!'. And down in the bound propagation loop, why do we
  only work with variables with u_j = infty? The corresponding section of code
  for l_j = -infty is ifdef'd away. And why exclude the code protected by
  PRESOLVE_TIGHTEN_DUALS? And why are we using ekkinf instead of PRESOLVE_INF?

  There is no postsolve action because once we've identified a variable to fix
  we can invoke make_fixed_action.
*/
const CoinPresolveAction *remove_dual_action::presolve(CoinPresolveMatrix *prob,
				   const CoinPresolveAction *next)
{
  double startTime = 0.0;
  int startEmptyRows=0;
  int startEmptyColumns = 0;
  if (prob->tuning_) {
    startTime = CoinCpuTime();
    startEmptyRows = prob->countEmptyRows();
    startEmptyColumns = prob->countEmptyCols();
  }
  double *colels	= prob->colels_;
  int *hrow		= prob->hrow_;
  CoinBigIndex *mcstrt		= prob->mcstrt_;
  int *hincol		= prob->hincol_;
  int ncols		= prob->ncols_;

  double *clo	= prob->clo_;
  double *cup	= prob->cup_;
  unsigned char *colstat = prob->colstat_ ;

  // Used only in `fix if simple' section below. Remove dec'l to avoid
  // GCC compile warning.
  // double *rowels	= prob->rowels_;
  int *hcol		= prob->hcol_;
  CoinBigIndex *mrstrt		= prob->mrstrt_;
  int *hinrow		= prob->hinrow_;
  double *csol	= prob->sol_;
  int nrows		= prob->nrows_;

  double *rlo	= prob->rlo_;
  double *rup	= prob->rup_;

  double *dcost	= prob->cost_;
  const unsigned char *integerType = prob->integerType_;

  const double maxmin	= prob->maxmin_;

  const double ekkinf = 1e28;
  const double ekkinf2 = 1e20;
  const double ztoldj = prob->ztoldj_;

  CoinRelFltEq relEq(prob->ztolzb_) ;

  double *rdmin	= prob->usefulRowDouble_; //new double[nrows];
  double *rdmax	= reinterpret_cast<double *> (prob->usefulRowInt_); //new double[nrows];

# if PRESOLVE_DEBUG
  std::cout << "Entering remove_dual_action::presolve, " << nrows << " X " << ncols << "." << std::endl ;
  presolve_check_sol(prob) ;
  presolve_check_nbasic(prob) ;
# endif

  // This combines the initialization of rdmin/rdmax to extreme values
  // (PRESOLVE_INF/-PRESOLVE_INF) with a version of the next loop specialized
  // for row slacks.
  // In this case, it is always the case that dprice==0.0 and coeff==1.0.
  int i;
  for ( i = 0; i < nrows; i++) {
    double sup = -rlo[i];	// slack ub; corresponds to cup[j]
    double slo = -rup[i];	// slack lb; corresponds to clo[j]
    bool no_lb = (slo <= -ekkinf);
    bool no_ub = (sup >= ekkinf);

    // here, dj==-row dual
    // the row slack has no lower bound, but it does have an upper bound,
    // then the reduced cost must be <= 0, so the row dual must be >= 0
    rdmin[i] = (no_lb && !no_ub) ? 0.0 : -PRESOLVE_INF;

    rdmax[i] = (no_ub && !no_lb) ? 0.0 :  PRESOLVE_INF;
  }

  // Look for col singletons and update bounds on dual costs
  // Take the min of the maxes and max of the mins
  int j;
  for ( j = 0; j<ncols; j++) {
    if (integerType[j])
      continue; // even if it has infinite bound now ....
    bool no_ub = (cup[j] >= ekkinf);
    bool no_lb = (clo[j] <= -ekkinf);

    if (hincol[j] == 1 &&

	// we need singleton cols that have exactly one bound
	(no_ub ^ no_lb)) {
      int row = hrow[mcstrt[j]];
      double coeff = colels[mcstrt[j]];

      PRESOLVEASSERT(fabs(coeff) > ZTOLDP);

      // I don't think the sign of dcost[j] matters

      // this row dual would make this col's reduced cost be 0
      double dprice = maxmin * dcost[j] / coeff;

      // no_ub == !no_lb
      // no_ub ==> !(dj<0)
      // no_lb ==> !(dj>0)
      // I don't think the case where !no_ub has actually been tested
      if ((coeff > 0.0) == no_ub) {
	// coeff>0 ==> making the row dual larger would make dj *negative*
	//		==> dprice is an upper bound on dj if no *ub*
	// coeff<0 ==> making the row dual larger would make dj *positive*
	//		==> dprice is an upper bound on dj if no *lb*
	if (rdmax[row] > dprice)	// reduced cost may be positive
	  rdmax[row] = dprice;
      } else if ((coeff < 0.0) == no_lb) { // no lower bound
	if (rdmin[row] < dprice) 	// reduced cost may be negative
	  rdmin[row] = dprice;
      }
    }
  }

  int *fix_cols	= prob->usefulColumnInt_; //new int[ncols];

  //int *fixdown_cols	= new int[ncols];

#if	PRESOLVE_TIGHTEN_DUALS
  double *djmin	= new double[ncols];
  double *djmax	= new double[ncols];
#endif
  int nfixup_cols	= 0;
  int nfixdown_cols	= ncols;

  int nPass=100;
  while (nPass-->0) {
    int tightened = 0;
    /* Perform duality tests */
    for (int j = 0; j<ncols; j++) {
      if (hincol[j] > 0) {
	CoinBigIndex kcs = mcstrt[j];
	CoinBigIndex kce = kcs + hincol[j];
	// Number of infinite rows
	int nflagu = 0;
	int nflagl = 0;
	// Number of ordinary rows
	int nordu = 0;
	int nordl = 0;
	double ddjlo = maxmin * dcost[j];
	double ddjhi = ddjlo;

	for (CoinBigIndex k = kcs; k < kce; k++) {
	  int i = hrow[k];
	  double coeff = colels[k];

	  if (coeff > 0.0) {
	    if (rdmin[i] >= -ekkinf2) {
	      ddjhi -= coeff * rdmin[i];
	      nordu ++;
	    } else {
	      nflagu ++;
	    }
	    if (rdmax[i] <= ekkinf2) {
	      ddjlo -= coeff * rdmax[i];
	      nordl ++;
	    } else {
	      nflagl ++;
	    }
	  } else {
	    if (rdmax[i] <= ekkinf2) {
	      ddjhi -= coeff * rdmax[i];
	      nordu ++;
	    } else {
	      nflagu ++;
	    }
	    if (rdmin[i] >= -ekkinf2) {
	      ddjlo -= coeff * rdmin[i];
	      nordl ++;
	    } else {
	      nflagl ++;
	    }
	  }
	}
	// See if we may be able to tighten a dual
	if (!integerType[j]) {
	  if (cup[j]>ekkinf) {
	    // dj can not be negative
	    if (nflagu==1&&ddjhi<-ztoldj) {
	      // We can make bound finite one way
	      for (CoinBigIndex k = kcs; k < kce; k++) {
		int i = hrow[k];
		double coeff = colels[k];

		if (coeff > 0.0&&rdmin[i] < -ekkinf2) {
		  // rdmax[i] has upper bound
		  if (ddjhi<rdmax[i]*coeff-ztoldj) {
		    double newValue = ddjhi/coeff;
		    // re-compute lo
		    if (rdmax[i] > ekkinf2 && newValue <= ekkinf2) {
		      nflagl--;
		      ddjlo -= coeff * newValue;
		    } else if (rdmax[i] <= ekkinf2) {
		      ddjlo -= coeff * (newValue-rdmax[i]);
		    }
		    rdmax[i] = newValue;
		    tightened++;
#if	PRESOLVE_DEBUG
		    printf("Col %d, row %d max pi now %g\n",j,i,rdmax[i]);
#endif
		  }
		} else if (coeff < 0.0 && rdmax[i] > ekkinf2) {
		  // rdmin[i] has lower bound
		  if (ddjhi<rdmin[i]*coeff-ztoldj) {
		    double newValue = ddjhi/coeff;
		    // re-compute lo
		    if (rdmin[i] < -ekkinf2 && newValue >= -ekkinf2) {
		      nflagl--;
		      ddjlo -= coeff * newValue;
		    } else if (rdmax[i] >= -ekkinf2) {
		      ddjlo -= coeff * (newValue-rdmin[i]);
		    }
		    rdmin[i] = newValue;
		    tightened++;
#if	PRESOLVE_DEBUG
		    printf("Col %d, row %d min pi now %g\n",j,i,rdmin[i]);
#endif
		    ddjlo = 0.0;
		  }
		}
	      }
	    } else if (nflagl==0&&nordl==1&&ddjlo<-ztoldj) {
	      // We may be able to tighten
	      for (CoinBigIndex k = kcs; k < kce; k++) {
		int i = hrow[k];
		double coeff = colels[k];

		if (coeff > 0.0) {
		  rdmax[i] += ddjlo/coeff;
		  ddjlo =0.0;
		  tightened++;
#if	PRESOLVE_DEBUG
		  printf("Col %d, row %d max pi now %g\n",j,i,rdmax[i]);
#endif
		} else if (coeff < 0.0 ) {
		  rdmin[i] += ddjlo/coeff;
		  ddjlo =0.0;
		  tightened++;
#if	PRESOLVE_DEBUG
		  printf("Col %d, row %d min pi now %g\n",j,i,rdmin[i]);
#endif
		}
	      }
	    }
	  }
#if 0
	  if (clo[j]<-ekkinf) {
	    // dj can not be positive
	    if (ddjlo>ztoldj&&nflagl==1) {
	      // We can make bound finite one way
	      for (CoinBigIndex k = kcs; k < kce; k++) {
		int i = hrow[k];
		double coeff = colels[k];

		if (coeff < 0.0&&rdmin[i] < -ekkinf2) {
		  // rdmax[i] has upper bound
		  if (ddjlo>rdmax[i]*coeff+ztoldj) {
		    double newValue = ddjlo/coeff;
		    // re-compute hi
		    if (rdmax[i] > ekkinf2 && newValue <= ekkinf2) {
		      nflagu--;
		      ddjhi -= coeff * newValue;
		    } else if (rdmax[i] <= ekkinf2) {
		      ddjhi -= coeff * (newValue-rdmax[i]);
		    }
		    rdmax[i] = newValue;
		    tightened++;
#if	PRESOLVE_DEBUG
		    printf("Col %d, row %d max pi now %g\n",j,i,rdmax[i]);
#endif
		  }
		} else if (coeff > 0.0 && rdmax[i] > ekkinf2) {
		  // rdmin[i] has lower bound
		  if (ddjlo>rdmin[i]*coeff+ztoldj) {
		    double newValue = ddjlo/coeff;
		    // re-compute lo
		    if (rdmin[i] < -ekkinf2 && newValue >= -ekkinf2) {
		      nflagu--;
		      ddjhi -= coeff * newValue;
		    } else if (rdmax[i] >= -ekkinf2) {
		      ddjhi -= coeff * (newValue-rdmin[i]);
		    }
		    rdmin[i] = newValue;
		    tightened++;
#if	PRESOLVE_DEBUG
		    printf("Col %d, row %d min pi now %g\n",j,i,rdmin[i]);
#endif
		  }
		}
	      }
	    } else if (nflagu==0&&nordu==1&&ddjhi>ztoldj) {
	      // We may be able to tighten
	      for (CoinBigIndex k = kcs; k < kce; k++) {
		int i = hrow[k];
		double coeff = colels[k];

		if (coeff < 0.0) {
		  rdmax[i] += ddjhi/coeff;
		  ddjhi =0.0;
		  tightened++;
#if	PRESOLVE_DEBUG
		  printf("Col %d, row %d max pi now %g\n",j,i,rdmax[i]);
#endif
		} else if (coeff > 0.0 ) {
		  rdmin[i] += ddjhi/coeff;
		  ddjhi =0.0;
		  tightened++;
#if	PRESOLVE_DEBUG
		  printf("Col %d, row %d min pi now %g\n",j,i,rdmin[i]);
#endif
		}
	      }
	    }
	  }
#endif
	}

#if	PRESOLVE_TIGHTEN_DUALS
	djmin[j] = (nflagl ?  -PRESOLVE_INF : ddjlo);
	djmax[j] = (nflagu ? PRESOLVE_INF : ddjhi);
#endif

	if (ddjlo > ztoldj && nflagl == 0&&!prob->colProhibited2(j)) {
	  // dj>0 at optimality ==> must be at lower bound
	  if (clo[j] <= -ekkinf) {
	    prob->messageHandler()->message(COIN_PRESOLVE_COLUMNBOUNDB,
					    prob->messages())
					      <<j
					      <<CoinMessageEol;
	    prob->status_ |= 2;
	    break;
	  } else {
	    fix_cols[--nfixdown_cols] = j;
	    PRESOLVE_DETAIL_PRINT(printf("pre_duallo %dC E\n",j));
#	    if PRESOLVE_DEBUG
	    printf("NDUAL: fixing x<%d>",fix_cols[nfixdown_cols]) ;
	    if (csol) printf(" = %g",csol[j]) ;
	    printf(" at lb = %g.\n",clo[j]) ;
#	    endif
	    //if (csol[j]-clo[j]>1.0e-7)
	    //printf("down %d row %d nincol %d\n",j,hrow[mcstrt[j]],hincol[j]);
	    // User may have given us feasible solution - move if simple
	    if (csol) {
# if 0
	/*
	  Except it's not simple. The net result is that we end up with an
	  excess of basic variables.
	*/
	      if (csol[j]-clo[j]>1.0e-7&&hincol[j]==1) {
		double value_j = colels[mcstrt[j]];
		double distance_j = csol[j]-clo[j];
		int row=hrow[mcstrt[j]];
		// See if another column can take value
		for (CoinBigIndex kk=mrstrt[row];kk<mrstrt[row]+hinrow[row];kk++) {
		  int k = hcol[kk];
		  if (colstat[k] == CoinPrePostsolveMatrix::superBasic)
		    continue ;

		  if (hincol[k]==1&&k!=j) {
		    double value_k = rowels[kk];
		    double movement;
		    if (value_k*value_j>0.0) {
		      // k needs to increase
		      double distance_k = cup[k]-csol[k];
		      movement = CoinMin((distance_j*value_j)/value_k,distance_k);
		    } else {
		      // k needs to decrease
		      double distance_k = clo[k]-csol[k];
		      movement = CoinMax((distance_j*value_j)/value_k,distance_k);
		    }
		    if (relEq(movement,0)) continue ;

		    csol[k] += movement;
		    if (relEq(csol[k],clo[k]))
		    { colstat[k] = CoinPrePostsolveMatrix::atLowerBound ; }
		    else
		    if (relEq(csol[k],cup[k]))
		    { colstat[k] = CoinPrePostsolveMatrix::atUpperBound ; }
		    else
		    if (colstat[k] != CoinPrePostsolveMatrix::isFree)
		    { colstat[k] = CoinPrePostsolveMatrix::basic ; }
		    printf("NDUAL: x<%d> moved %g to %g; ",
			   k,movement,csol[k]) ;
		    printf("lb = %g, ub = %g, status now %s.\n",
			   clo[k],cup[k],columnStatusString(colstat[k])) ;
		    distance_j -= (movement*value_k)/value_j;
		    csol[j] -= (movement*value_k)/value_j;
		    if (distance_j<1.0e-7)
		      break;
		  }
		}
	      }
# endif		// repair solution.

	      csol[j] = clo[j] ;	// but the bottom line is we've changed x<j>
	      colstat[j] = CoinPrePostsolveMatrix::atLowerBound ;
	    }
	  }
	} else if (ddjhi < -ztoldj && nflagu == 0&&!prob->colProhibited2(j)) {
	  // dj<0 at optimality ==> must be at upper bound
	  if (cup[j] >= ekkinf) {
	    prob->messageHandler()->message(COIN_PRESOLVE_COLUMNBOUNDA,
					    prob->messages())
					      <<j
					      <<CoinMessageEol;
	    prob->status_ |= 2;
	    break;
	  } else {
	    PRESOLVE_DETAIL_PRINT(printf("pre_dualup %dC E\n",j));
	    fix_cols[nfixup_cols++] = j;
#	    if PRESOLVE_DEBUG
	    printf("NDUAL: fixing x<%d>",fix_cols[nfixup_cols-1]) ;
	    if (csol) printf(" = %g",csol[j]) ;
	    printf(" at ub = %g.\n",cup[j]) ;
#	    endif
	    // User may have given us feasible solution - move if simple
	    // See comments for `fix at lb' case above.
	    //if (cup[j]-csol[j]>1.0e-7)
	    //printf("up %d row %d nincol %d\n",j,hrow[mcstrt[j]],hincol[j]);
	    if (csol) {
# if 0
	// See comments above.
	      if (cup[j]-csol[j]>1.0e-7&&hincol[j]==1) {
		double value_j = colels[mcstrt[j]];
		double distance_j = csol[j]-cup[j];
		int row=hrow[mcstrt[j]];
		// See if another column can take value
		for (CoinBigIndex kk=mrstrt[row];kk<mrstrt[row]+hinrow[row];kk++) {
		  int k = hcol[kk];
		  if (colstat[k] == CoinPrePostsolveMatrix::superBasic)
		    continue ;

		  if (hincol[k]==1&&k!=j) {
		    double value_k = rowels[kk];
		    double movement;
		    if (value_k*value_j<0.0) {
		      // k needs to increase
		      double distance_k = cup[k]-csol[k];
		      movement = CoinMin((distance_j*value_j)/value_k,distance_k);
		    } else {
		      // k needs to decrease
		      double distance_k = clo[k]-csol[k];
		      movement = CoinMax((distance_j*value_j)/value_k,distance_k);
		    }
		    if (relEq(movement,0)) continue ;

		    csol[k] += movement;
		    if (relEq(csol[k],clo[k]))
		    { colstat[k] = CoinPrePostsolveMatrix::atLowerBound ; }
		    else
		    if (relEq(csol[k],cup[k]))
		    { colstat[k] = CoinPrePostsolveMatrix::atUpperBound ; }
		    else
		    if (colstat[k] != CoinPrePostsolveMatrix::isFree)
		    { colstat[k] = CoinPrePostsolveMatrix::basic ; }
		    printf("NDUAL: x<%d> moved %g to %g; ",
			   k,movement,csol[k]) ;
		    printf("lb = %g, ub = %g, status now %s.\n",
			   clo[k],cup[k],columnStatusString(colstat[k])) ;
		    distance_j -= (movement*value_k)/value_j;
		    csol[j] -= (movement*value_k)/value_j;
		    if (distance_j>-1.0e-7)
		      break;
		  }
		}
	      }
# endif
	      csol[j] = cup[j] ;	// but the bottom line is we've changed x<j>
	      colstat[j] = CoinPrePostsolveMatrix::atUpperBound ;
	    }
	  }
	}
      }
    }

    // I don't know why I stopped doing this.
#if	PRESOLVE_TIGHTEN_DUALS
    const double *rowels	= prob->rowels_;
    const int *hcol	= prob->hcol_;
    const CoinBigIndex *mrstrt	= prob->mrstrt_;
    int *hinrow	= prob->hinrow_;
    // tighten row dual bounds, as described on p. 229
    for (int i = 0; i < nrows; i++) {
      bool no_ub = (rup[i] >= ekkinf);
      bool no_lb = (rlo[i] <= -ekkinf);

      if ((no_ub ^ no_lb) == true) {
	CoinBigIndex krs = mrstrt[i];
	CoinBigIndex kre = krs + hinrow[i];
	double rmax  = rdmax[i];
	double rmin  = rdmin[i];

	// all row columns are non-empty
	for (CoinBigIndex k=krs; k<kre; k++) {
	  double coeff = rowels[k];
	  int icol = hcol[k];
	  double djmax0 = djmax[icol];
	  double djmin0 = djmin[icol];

	  if (no_ub) {
	    // dj must not be negative
	    if (coeff > ZTOLDP2 && djmax0 <PRESOLVE_INF && cup[icol]>=ekkinf) {
	      double bnd = djmax0 / coeff;
	      if (rmax > bnd) {
#if	PRESOLVE_DEBUG
		printf("MAX TIGHT[%d,%d]:  %g --> %g\n", i,hrow[k], rdmax[i], bnd);
#endif
		rdmax[i] = rmax = bnd;
		tightened ++;
	      }
	    } else if (coeff < -ZTOLDP2 && djmax0 <PRESOLVE_INF && cup[icol] >= ekkinf) {
	      double bnd = djmax0 / coeff ;
	      if (rmin < bnd) {
#if	PRESOLVE_DEBUG
		printf("MIN TIGHT[%d,%d]:  %g --> %g\n", i, hrow[k], rdmin[i], bnd);
#endif
		rdmin[i] = rmin = bnd;
		tightened ++;
	      }
	    }
	  } else {	// no_lb
	    // dj must not be positive
	    if (coeff > ZTOLDP2 && djmin0 > -PRESOLVE_INF && clo[icol]<=-ekkinf) {
	      double bnd = djmin0 / coeff ;
	      if (rmin < bnd) {
#if	PRESOLVE_DEBUG
		printf("MIN1 TIGHT[%d,%d]:  %g --> %g\n", i, hrow[k], rdmin[i], bnd);
#endif
		rdmin[i] = rmin = bnd;
		tightened ++;
	      }
	    } else if (coeff < -ZTOLDP2 && djmin0 > -PRESOLVE_INF && clo[icol] <= -ekkinf) {
	      double bnd = djmin0 / coeff ;
	      if (rmax > bnd) {
#if	PRESOLVE_DEBUG
		printf("MAX TIGHT1[%d,%d]:  %g --> %g\n", i,hrow[k], rdmax[i], bnd);
#endif
		rdmax[i] = rmax = bnd;
		tightened ++;
	      }
	    }
	  }
	}
      }
    }
#endif

    if (tightened<100||nfixdown_cols<ncols||nfixup_cols)
      break;
#if	PRESOLVE_TIGHTEN_DUALS
    else
      printf("DUAL TIGHTENED!  %d\n", tightened);
#endif
  }
  assert (nfixup_cols<=nfixdown_cols);
  if (nfixup_cols) {
#if	PRESOLVE_DEBUG
    printf("NDUAL: %d up", nfixup_cols);
    for (i = 0 ; i < nfixup_cols ; i++) printf(" %d",fix_cols[i]) ;
    printf(".\n") ;
#endif
    next = make_fixed_action::presolve(prob, fix_cols, nfixup_cols, false, next);
  }

  if (nfixdown_cols<ncols) {
    int * fixdown_cols = fix_cols+nfixdown_cols;
    nfixdown_cols = ncols-nfixdown_cols;
#if	PRESOLVE_DEBUG
    printf("NDUAL: %d down", nfixdown_cols);
    for (i = 0 ; i < nfixdown_cols ; i++) printf(" %d",fixdown_cols[i]) ;
    printf(".\n") ;
#endif
    next = make_fixed_action::presolve(prob, fixdown_cols, nfixdown_cols, true, next);
  }
  // If dual says so then we can make equality row
  // Also if cost is in right direction and only one binding row for variable
  // We may wish to think about giving preference to rows with 2 or 3 elements
/*
  Gack! Ok, I can appreciate the thought here, but I'm seriously skeptical
  about writing canFix[0] before reading rdmin[0]. After that, we should be out
  of the interference zone for the typical situation where sizeof(double) is
  twice sizeof(int).
*/
  int * canFix = reinterpret_cast<int *> (rdmin);
  for ( i = 0; i < nrows; i++) {
    bool no_lb = (rlo[i] <= -ekkinf);
    bool no_ub = (rup[i] >= ekkinf);
    canFix[i]=0;
    if (no_ub && !no_lb ) {
      if ( rdmin[i]>0.0)
	canFix[i]=-1;
      else
	canFix[i]=-2;
    } else if (no_lb && !no_ub ) {
      if (rdmax[i]<0.0)
	canFix[i]=1;
      else
	canFix[i]=2;
    }
  }
  for (j = 0; j<ncols; j++) {
    if (hincol[j]<=1)
      continue;
    if (integerType[j])
      continue; // even if it has infinite bound now ....
    CoinBigIndex kcs = mcstrt[j];
    CoinBigIndex kce = kcs + hincol[j];
    int bindingUp=-1;
    int bindingDown=-1;
    if (cup[j]<ekkinf)
      bindingUp=-2;
    if (clo[j]>-ekkinf)
      bindingDown=-2;
    for (CoinBigIndex k = kcs; k < kce; k++) {
      int i = hrow[k];
      if (abs(canFix[i])!=2) {
	bindingUp=-2;
	bindingDown=-2;
	break;
      }
      double coeff = colels[k];
      if (coeff>0.0) {
	if (canFix[i]==2) {
	  // binding up
	  if (bindingUp==-1)
	    bindingUp = i;
	  else
	    bindingUp = -2;
	} else {
	  // binding down
	  if (bindingDown==-1)
	    bindingDown = i;
	  else
	    bindingDown = -2;
	}
      } else {
	if (canFix[i]==2) {
	  // binding down
	  if (bindingDown==-1)
	    bindingDown = i;
	  else
	    bindingDown = -2;
	} else {
	  // binding up
	  if (bindingUp==-1)
	    bindingUp = i;
	  else
	    bindingUp = -2;
	}
      }
    }
    double cost = maxmin * dcost[j];
    if (bindingUp>-2&&cost<=0.0) {
      // might as well make equality
      if (bindingUp>=0) {
	canFix[bindingUp] /= 2; //So -2 goes to -1 etc
	//printf("fixing row %d to ub by %d\n",bindingUp,j);
      } else {
	//printf("binding up row by %d\n",j);
      }
    } else if (bindingDown>-2 &&cost>=0.0) {
      // might as well make equality
      if (bindingDown>=0) {
	canFix[bindingDown] /= 2; //So -2 goes to -1 etc
	//printf("fixing row %d to lb by %d\n",bindingDown,j);
      } else {
	//printf("binding down row by %d\n",j);
      }
    }
  }
  // can't fix if integer and non-unit coefficient
  //const double *rowels	= prob->rowels_;
  //const int *hcol	= prob->hcol_;
  //const CoinBigIndex *mrstrt	= prob->mrstrt_;
  //int *hinrow	= prob->hinrow_;
  for ( i = 0; i < nrows; i++) {
    if (abs(canFix[i])==1) {
      CoinBigIndex krs = mrstrt[i];
      CoinBigIndex kre = krs + hinrow[i];
      for (CoinBigIndex k=krs; k<kre; k++) {
	int icol = hcol[k];
	if (cup[icol]>clo[icol]&&integerType[icol]) {
	  canFix[i]=0; // not safe
#ifdef COIN_DEVELOP
	  printf("no dual something CoinPresolveDual row %d col %d\n",
		 i,icol);
#endif
	}
      }
    }
    if (canFix[i]==1) {
      rlo[i]=rup[i];
      prob->addRow(i);
    } else if (canFix[i]==-1) {
      rup[i]=rlo[i];
      prob->addRow(i);
    }
  }

  //delete[]rdmin;
  //delete[]rdmax;

  //delete[]fixup_cols;
  //delete[]fixdown_cols;

#if	PRESOLVE_TIGHTEN_DUALS
  delete[]djmin;
  delete[]djmax;
#endif

  if (prob->tuning_) {
    double thisTime=CoinCpuTime();
    int droppedRows = prob->countEmptyRows() - startEmptyRows;
    int droppedColumns =  prob->countEmptyCols() - startEmptyColumns;
    printf("CoinPresolveDual(1) - %d rows, %d columns dropped in time %g, total %g\n",
	   droppedRows,droppedColumns,thisTime-startTime,thisTime-prob->startTime_);
  }

# if PRESOLVE_DEBUG
  presolve_check_sol(prob) ;
  presolve_check_nbasic(prob) ;
  std::cout << "Leaving remove_dual_action::presolve." << std::endl ;
# endif

  return (next);
}
/*
  It is always the case that one of the variables of a doubleton is, or
  can be made, implied free, but neither will necessarily be a singleton.
  Since in the case of a doubleton the number of non-zero entries will never
  increase if one is eliminated, it makes sense to always eliminate them.

  The col rep and row rep must be consistent.
 */
const CoinPresolveAction
  *doubleton_action::presolve (CoinPresolveMatrix *prob,
			      const CoinPresolveAction *next)

{
# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
# if PRESOLVE_DEBUG > 0
  std::cout
    << "Entering doubleton_action::presolve; considering "
    << prob->numberRowsToDo_ << " rows." << std::endl ;
# endif
  presolve_consistent(prob) ;
  presolve_links_ok(prob) ;
  presolve_check_sol(prob) ;
  presolve_check_nbasic(prob) ;
# endif
# if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING > 0
  int startEmptyRows = 0 ;
  int startEmptyColumns = 0 ;
  startEmptyRows = prob->countEmptyRows() ;
  startEmptyColumns = prob->countEmptyCols() ;
# if COIN_PRESOLVE_TUNING > 0
  double startTime = 0.0 ;
  if (prob->tuning_) startTime = CoinCpuTime() ;
# endif
# endif

  const int n = prob->ncols_ ;
  const int m = prob->nrows_ ;

/*
  Unpack column-major and row-major representations, along with rim vectors.
*/
  CoinBigIndex *colStarts = prob->mcstrt_ ;
  int *colLengths = prob->hincol_ ;
  double *colCoeffs = prob->colels_ ;
  int *rowIndices = prob->hrow_ ;
  presolvehlink *clink = prob->clink_ ;

  double *clo = prob->clo_ ;
  double *cup = prob->cup_ ;

  CoinBigIndex *rowStarts = prob->mrstrt_ ;
  int *rowLengths = prob->hinrow_ ;
  double *rowCoeffs = prob->rowels_ ;
  int *colIndices = prob->hcol_ ;
  presolvehlink *rlink = prob->rlink_ ;

  double *rlo = prob->rlo_ ;
  double *rup = prob->rup_ ;

  const unsigned char *integerType = prob->integerType_ ;

  double *cost = prob->cost_ ;

  int numberLook = prob->numberRowsToDo_ ;
  int *look = prob->rowsToDo_ ;
  const double ztolzb	= prob->ztolzb_ ;
  const double ztolzero = 1.0e-12 ;

  action *actions = new action [m] ;
  int nactions = 0 ;

/*
  zeros will hold columns that should be groomed to remove explicit zeros when
  we're finished.

  fixed will hold columns that have ended up as fixed variables.
*/
  int *zeros = prob->usefulColumnInt_ ;
  int nzeros = 0 ;

  int *fixed = zeros+n ;
  int nfixed = 0 ;

  unsigned char *rowstat = prob->rowstat_ ;
  double *acts	= prob->acts_ ;
  double *sol = prob->sol_ ;
/*
  More like `ignore infeasibility'.
*/
  bool fixInfeasibility = ((prob->presolveOptions_&0x4000) != 0) ;

/*
  Open the main loop to scan for doubleton candidates.
*/
  for (int iLook = 0 ; iLook < numberLook ; iLook++) {
    const int tgtrow = look[iLook] ;
/*
  We need an equality with two coefficients. Avoid isolated constraints, lest
  both variables vanish.

  Failure of the assert indicates that the row- and column-major
  representations are out of sync.
*/
    if ((rowLengths[tgtrow] != 2) ||
        (fabs(rup[tgtrow]-rlo[tgtrow]) > ZTOLDP)) continue ;

    const CoinBigIndex krs = rowStarts[tgtrow] ;
    int tgtcolx = colIndices[krs] ;
    int tgtcoly = colIndices[krs+1] ;

    PRESOLVEASSERT(colLengths[tgtcolx] > 0 || colLengths[tgtcoly] > 0) ;
    if (colLengths[tgtcolx] == 1 && colLengths[tgtcoly] == 1) continue ;
/*
  Avoid prohibited columns and fixed columns. Make sure the coefficients are
  nonzero.
  JJF - test should allow one to be prohibited as long as you leave that
  one.  I modified earlier code but hope I have got this right.
*/
    if (prob->colProhibited(tgtcolx) && prob->colProhibited(tgtcoly))
      continue ;
    if (fabs(rowCoeffs[krs]) < ZTOLDP2 || fabs(rowCoeffs[krs+1]) < ZTOLDP2)
      continue ;
    if ((fabs(cup[tgtcolx]-clo[tgtcolx]) < ZTOLDP) ||
	(fabs(cup[tgtcoly]-clo[tgtcoly]) < ZTOLDP)) continue ;

#   if PRESOLVE_DEBUG > 2
    std::cout
      << "  row " << tgtrow << " colx " << tgtcolx << " coly " << tgtcoly
      << " passes preliminary eval." << std::endl ;
#   endif

/*
  Find this row in each column. The indices are not const because we may flip
  them below, once we decide which column will be eliminated.
*/
    CoinBigIndex krowx =
        presolve_find_row(tgtrow,colStarts[tgtcolx],
			  colStarts[tgtcolx]+colLengths[tgtcolx],rowIndices) ;
    double coeffx = colCoeffs[krowx] ;
    CoinBigIndex krowy =
        presolve_find_row(tgtrow,colStarts[tgtcoly],
			  colStarts[tgtcoly]+colLengths[tgtcoly],rowIndices) ;
    double coeffy = colCoeffs[krowy] ;
    const double rhs = rlo[tgtrow] ;
/*
  Avoid obscuring a requirement for integrality.

  If only one variable is integer, keep it and substitute for the continuous
  variable.

  If both are integer, substitute only for the forms x = k*y (k integral
  and non-empty intersection on bounds on x) or x = 1-y, where both x and
  y are binary.

  flag bits for integerStatus: 0x01: x integer;  0x02: y integer

  This bit of code works because 0 is continuous, 1 is integer. Make sure
  that's true.
*/
    assert((integerType[tgtcolx] == 0) || (integerType[tgtcolx] == 1)) ;
    assert((integerType[tgtcoly] == 0) || (integerType[tgtcoly] == 1)) ;

    int integerX = integerType[tgtcolx];
    int integerY = integerType[tgtcoly];
    /* if one prohibited then treat that as integer. This
       may be pessimistic - but will catch SOS etc */
    if (prob->colProhibited2(tgtcolx))
      integerX=1;
    if (prob->colProhibited2(tgtcoly))
      integerY=1;
    int integerStatus = (integerY<<1)|integerX ;

    if (integerStatus == 3) {
      int good = 0 ;
      double rhs2 = rhs ;
      if (coeffx < 0.0) {
	coeffx = -coeffx ;
	rhs2 += 1 ;
      }
      if ((cup[tgtcolx] == 1.0) && (clo[tgtcolx] == 0.0) &&
	  (fabs(coeffx-1.0) < 1.0e-7) && !prob->colProhibited2(tgtcoly))
	good = 1 ;
      if (coeffy < 0.0) {
	coeffy = -coeffy ;
	rhs2 += 1 ;
      }
      if ((cup[tgtcoly] == 1.0) && (clo[tgtcoly] == 0.0) &&
	  (fabs(coeffy-1.0) < 1.0e-7) && !prob->colProhibited2(tgtcolx))
	good |= 2 ;
      if (!(good == 3 && fabs(rhs2-1.0) < 1.0e-7))
	integerStatus = -1 ;
/*
  Not x+y = 1. Try for ax+by = 0
*/
      if (integerStatus < 0 && rhs == 0.0) {
	coeffx = colCoeffs[krowx] ;
	coeffy = colCoeffs[krowy] ;
	double ratio ;
	bool swap = false ;
	if (fabs(coeffx) > fabs(coeffy)) {
	  ratio = coeffx/coeffy ;
	} else {
	  ratio = coeffy/coeffx ;
	  swap = true ;
	}
	ratio = fabs(ratio) ;
	if (fabs(ratio-floor(ratio+0.5)) < ztolzero) {
	  integerStatus = swap ? 2 : 1 ;
	}
      }
/*
  One last try --- just require an integral substitution formula.

  But ax+by = 0 above is a subset of ax+by = c below and should pass the
  test below. For that matter, so will x+y = 1. Why separate special cases
  above?  -- lh, 121106 --
*/
      if (integerStatus < 0) {
	bool canDo = false ;
	coeffx = colCoeffs[krowx] ;
	coeffy = colCoeffs[krowy] ;
	double ratio ;
	bool swap = false ;
	double rhsRatio ;
	if (fabs(coeffx) > fabs(coeffy)) {
	  ratio = coeffx/coeffy ;
	  rhsRatio = rhs/coeffx ;
	} else {
	  ratio = coeffy/coeffx ;
	  rhsRatio = rhs/coeffy ;
	  swap = true ;
	}
	ratio = fabs(ratio) ;
	if (fabs(ratio-floor(ratio+0.5)) < ztolzero) {
	  // possible
	  integerStatus = swap ? 2 : 1 ;
	  // but check rhs
	  if (rhsRatio==floor(rhsRatio+0.5))
	    canDo=true ;
	}
#       ifdef COIN_DEVELOP2
	if (canDo)
	  printf("Good CoinPresolveDoubleton tgtcolx %d (%g and bounds %g %g) tgtcoly %d (%g and bound %g %g) - rhs %g\n",
		 tgtcolx,colCoeffs[krowx],clo[tgtcolx],cup[tgtcolx],
		 tgtcoly,colCoeffs[krowy],clo[tgtcoly],cup[tgtcoly],rhs) ;
	else
	printf("Bad CoinPresolveDoubleton tgtcolx %d (%g) tgtcoly %d (%g) - rhs %g\n",
	       tgtcolx,colCoeffs[krowx],tgtcoly,colCoeffs[krowy],rhs) ;
#       endif
	if (!canDo)
	  continue ;
      }
    }
/*
  We've resolved integrality concerns. If we concluded that we need to
  switch the roles of x and y because of integrality, do that now. If both
  variables are continuous, we may still want to swap for numeric stability.
  Eliminate the variable with the larger coefficient.
*/
    if (integerStatus == 2) {
      CoinSwap(tgtcoly,tgtcolx) ;
      CoinSwap(krowy,krowx) ;
    } else if (integerStatus == 0) {
      if (fabs(colCoeffs[krowy]) < fabs(colCoeffs[krowx])) {
	CoinSwap(tgtcoly,tgtcolx) ;
	CoinSwap(krowy,krowx) ;
      }
    }
/*
  Don't eliminate y just yet if it's entangled in a singleton row (we want to
  capture that explicit bound in a column bound).
*/
    const CoinBigIndex kcsy = colStarts[tgtcoly] ;
    const CoinBigIndex kcey = kcsy+colLengths[tgtcoly] ;
    bool singletonRow = false ;
    for (CoinBigIndex kcol = kcsy ; kcol < kcey ; kcol++) {
      if (rowLengths[rowIndices[kcol]] == 1) {
        singletonRow = true ;
	break ;
      }
    }
    // skip if y prohibited
    if (singletonRow || prob->colProhibited2(tgtcoly)) continue ;

    coeffx = colCoeffs[krowx] ;
    coeffy = colCoeffs[krowy] ;
#   if PRESOLVE_DEBUG > 2
    std::cout
      << "  doubleton row " << tgtrow << ", keep x(" << tgtcolx
      << ") elim x(" << tgtcoly << ")." << std::endl ;
#   endif
    PRESOLVE_DETAIL_PRINT(printf("pre_doubleton %dC %dC %dR E\n",
				 tgtcoly,tgtcolx,tgtrow)) ;
/*
  Capture the existing columns and other information before we start to modify
  the constraint system. Save the shorter column.
*/
    action *s = &actions[nactions] ;
    nactions++ ;
    s->row = tgtrow ;
    s->icolx = tgtcolx ;
    s->clox = clo[tgtcolx] ;
    s->cupx = cup[tgtcolx] ;
    s->costx = cost[tgtcolx] ;
    s->icoly = tgtcoly ;
    s->costy = cost[tgtcoly] ;
    s->rlo = rlo[tgtrow] ;
    s->coeffx = coeffx ;
    s->coeffy = coeffy ;
    s->ncolx = colLengths[tgtcolx] ;
    s->ncoly = colLengths[tgtcoly] ;
    if (s->ncoly < s->ncolx) {
      s->colel	= presolve_dupmajor(colCoeffs,rowIndices,colLengths[tgtcoly],
				    colStarts[tgtcoly],tgtrow) ;
      s->ncolx = 0 ;
    } else {
      s->colel = presolve_dupmajor(colCoeffs,rowIndices,colLengths[tgtcolx],
				   colStarts[tgtcolx],tgtrow) ;
      s->ncoly = 0 ;
    }
/*
  Move finite bound information from y to x, so that y is implied free.
    a x + b y = c
    l1 <= x <= u1
    l2 <= y <= u2
   
    l2 <= (c - a x) / b <= u2
    b/-a > 0 ==> (b l2 - c) / -a <= x <= (b u2 - c) / -a
    b/-a < 0 ==> (b u2 - c) / -a <= x <= (b l2 - c) / -a
*/
    {
      double lo1 = -PRESOLVE_INF ;
      double up1 = PRESOLVE_INF ;
      
      if (-PRESOLVE_INF < clo[tgtcoly]) {
	if (coeffx*coeffy < 0)
	  lo1 = (coeffy*clo[tgtcoly]-rhs)/-coeffx ;
	else 
	  up1 = (coeffy*clo[tgtcoly]-rhs)/-coeffx ;
      }
      
      if (cup[tgtcoly] < PRESOLVE_INF) {
	if (coeffx*coeffy < 0)
	  up1 = (coeffy*cup[tgtcoly]-rhs)/-coeffx ;
	else 
	  lo1 = (coeffy*cup[tgtcoly]-rhs)/-coeffx ;
      }
/*
  Don't forget the objective coefficient.
    costy y = costy ((c - a x) / b) = (costy c)/b + x (costy -a)/b
*/
      cost[tgtcolx] += (cost[tgtcoly]*-coeffx)/coeffy ;
      prob->change_bias((cost[tgtcoly]*rhs)/coeffy) ;
/*
  The transfer of bounds could make x infeasible. Patch it up if the problem
  is minor or if the user was so incautious as to instruct us to ignore it.
  Prefer an integer value if there's one nearby. If there's nothing to be
  done, break out of the main loop.
*/
      {
	double lo2 = CoinMax(clo[tgtcolx],lo1) ;
	double up2 = CoinMin(cup[tgtcolx],up1) ;
	if (lo2 > up2) {
	  if (lo2 <= up2+prob->feasibilityTolerance_ || fixInfeasibility) {
	    double nearest = floor(lo2+0.5) ;
	    if (fabs(nearest-lo2) < 2.0*prob->feasibilityTolerance_) {
	      lo2 = nearest ;
	      up2 = nearest ;
	    } else {
	      lo2 = up2 ;
	    }
	  } else {
	    prob->status_ |= 1 ;
	    prob->messageHandler()->message(COIN_PRESOLVE_COLINFEAS,
	    				    prob->messages())
		 << tgtcolx << lo2 << up2 << CoinMessageEol ;
	    break ;
	  }
	}
#       if PRESOLVE_DEBUG > 2
	std::cout
	  << "  x(" << tgtcolx << ") lb " << clo[tgtcolx] << " --> " << lo2
	  << ", ub " << cup[tgtcolx] << " --> " << up2 << std::endl ;
#       endif
	clo[tgtcolx] = lo2 ;
	cup[tgtcolx] = up2 ;
/*
  Do we have a solution to maintain? If so, take a stab at it. If x ends up at
  bound, prefer to set it nonbasic, but if we're short of basic variables
  after eliminating y and the logical for the row, make it basic.

  This code will snap the value of x to bound if it's within the primal
  feasibility tolerance.
*/
	if (rowstat && sol) {
	  int numberBasic = 0 ;
	  double movement = 0 ;
	  if (prob->columnIsBasic(tgtcolx))
	    numberBasic++ ;
	  if (prob->columnIsBasic(tgtcoly))
	    numberBasic++ ;
	  if (prob->rowIsBasic(tgtrow))
	    numberBasic++ ;
	  if (sol[tgtcolx] <= lo2+ztolzb) {
	    movement = lo2-sol[tgtcolx] ;
	    sol[tgtcolx] = lo2 ;
	    prob->setColumnStatus(tgtcolx,
	    			  CoinPrePostsolveMatrix::atLowerBound) ;
	  } else if (sol[tgtcolx] >= up2-ztolzb) {
	    movement = up2-sol[tgtcolx] ;
	    sol[tgtcolx] = up2 ;
	    prob->setColumnStatus(tgtcolx,
	    			  CoinPrePostsolveMatrix::atUpperBound) ;
	  }
	  if (numberBasic > 1)
	    prob->setColumnStatus(tgtcolx,CoinPrePostsolveMatrix::basic) ;
/*
  We need to compensate if x was forced to move. Beyond that, even if x
  didn't move, we've forced y = (c-ax)/b, and that might not have been
  true before. So even if x didn't move, y may have moved. Note that the
  constant term c/b is subtracted out as the constraints are modified,
  so we don't include it when calculating movement for y.
*/
	  if (movement) { 
	    const CoinBigIndex kkcsx = colStarts[tgtcolx] ;
	    const CoinBigIndex kkcex = kkcsx+colLengths[tgtcolx] ;
	    for (CoinBigIndex kcol = kkcsx ; kcol < kkcex ; kcol++) {
	      int row = rowIndices[kcol] ;
	      if (rowLengths[row])
		acts[row] += movement*colCoeffs[kcol] ;
	    }
	  }
	  movement = ((-coeffx*sol[tgtcolx])/coeffy)-sol[tgtcoly] ;
	  if (movement) {
	    const CoinBigIndex kkcsy = colStarts[tgtcoly] ;
	    const CoinBigIndex kkcey = kkcsy+colLengths[tgtcoly] ;
	    for (CoinBigIndex kcol = kkcsy ; kcol < kkcey ; kcol++) {
	      int row = rowIndices[kcol] ;
	      if (rowLengths[row])
		acts[row] += movement*colCoeffs[kcol] ;
	    }
	  }
	}
	if (lo2 == up2)
	  fixed[nfixed++] = tgtcolx ;
      }
    }
/*
  We're done transferring bounds from y to x, and we've patched up the
  solution if one existed to patch. One last thing to do before we eliminate
  column y and the doubleton row: put column x and the entangled rows on
  the lists of columns and rows to look at in the next round of transforms.
*/
    {
      prob->addCol(tgtcolx) ;
      const CoinBigIndex kkcsy = colStarts[tgtcoly] ;
      const CoinBigIndex kkcey = kkcsy+colLengths[tgtcoly] ;
      for (CoinBigIndex kcol = kkcsy ; kcol < kkcey ; kcol++) {
	int row = rowIndices[kcol] ;
	prob->addRow(row) ;
      }
      const CoinBigIndex kkcsx = colStarts[tgtcolx] ;
      const CoinBigIndex kkcex = kkcsx+colLengths[tgtcolx] ;
      for (CoinBigIndex kcol = kkcsx ; kcol < kkcex ; kcol++) {
	int row = rowIndices[kcol] ;
	prob->addRow(row) ;
      }
    }

/*
  Empty tgtrow in the column-major matrix.  Deleting the coefficient for
  (tgtrow,tgtcoly) is a bit costly (given that we're about to drop the whole
  column), but saves the trouble of checking for it in elim_doubleton.
*/
    presolve_delete_from_col(tgtrow,tgtcolx,
    			     colStarts,colLengths,rowIndices,colCoeffs) ;
    presolve_delete_from_col(tgtrow,tgtcoly,
    			     colStarts,colLengths,rowIndices,colCoeffs) ;
/*
  Drop tgtrow in the row-major representation: set the length to 0
  and reclaim the major vector space in bulk storage.
*/
    rowLengths[tgtrow] = 0 ;
    PRESOLVE_REMOVE_LINK(rlink,tgtrow) ;

/*
  Transfer the colx factors to coly. This modifies coefficients in column x
  as it removes coefficients in column y.
*/
    bool no_mem = elim_doubleton("ELIMD",
				 colStarts,rlo,rup,colCoeffs,
				 rowIndices,colIndices,rowLengths,colLengths,
				 clink,n, 
				 rowStarts,rowCoeffs,
				 -coeffx/coeffy,
				 rhs/coeffy,
				 tgtrow,tgtcolx,tgtcoly) ;
    if (no_mem) 
      throwCoinError("out of memory","doubleton_action::presolve") ;

/*
  Eliminate coly entirely from the col rep. We'll want to groom colx to remove
  explicit zeros.
*/
    colLengths[tgtcoly] = 0 ;
    PRESOLVE_REMOVE_LINK(clink, tgtcoly) ;
    cost[tgtcoly] = 0.0 ;

    rlo[tgtrow] = 0.0 ;
    rup[tgtrow] = 0.0 ;

    zeros[nzeros++] = tgtcolx ;

#   if PRESOLVE_CONSISTENCY > 0
    presolve_consistent(prob) ;
    presolve_links_ok(prob) ;
#   endif
  }
/*
  Tidy up the collected actions and clean up explicit zeros and fixed
  variables. Don't bother unless we're feasible (status of 0).
*/
  if (nactions && !prob->status_) {
#   if PRESOLVE_SUMMARY > 0
    printf("NDOUBLETONS:  %d\n", nactions) ;
#   endif
    action *actions1 = new action[nactions] ;
    CoinMemcpyN(actions, nactions, actions1) ;

    next = new doubleton_action(nactions, actions1, next) ;

    if (nzeros)
      next = drop_zero_coefficients_action::presolve(prob, zeros, nzeros, next) ;
    if (nfixed)
      next = remove_fixed_action::presolve(prob, fixed, nfixed, next) ;
  }

  deleteAction(actions,action*) ;

# if COIN_PRESOLVE_TUNING > 0
  if (prob->tuning_) double thisTime = CoinCpuTime() ;
# endif
# if PRESOLVE_CONSISTENCY > 0 || PRESOLVE_DEBUG > 0
  presolve_check_sol(prob) ;
# endif
# if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING > 0
  int droppedRows = prob->countEmptyRows()-startEmptyRows ;
  int droppedColumns = prob->countEmptyCols()-startEmptyColumns ;
  std::cout
    << "Leaving doubleton_action::presolve, " << droppedRows << " rows, "
    << droppedColumns << " columns dropped" ;
# if COIN_PRESOLVE_TUNING > 0
  std::cout << " in " << thisTime-startTime << "s" ;
# endif
  std::cout << "." << std::endl ;
# endif

  return (next) ;
}
Esempio n. 3
0
/*
  It may be the case that the bounds on the variables in a constraint are
  such that no matter what feasible value the variables take, the constraint
  cannot be violated. In this case we can drop the constraint as useless.

  On the other hand, it may be that the only way to satisfy a constraint
  is to jam all the variables in the constraint to one of their bounds, fixing
  the variables. This is a forcing constraint, the primary target of this
  transform.

  Detection of both useless and forcing constraints requires calculation of
  bounds on the row activity (often referred to as lhs bounds, from the common
  form ax <= b). This routine will remember useless constraints as it finds
  them and invoke useless_constraint_action to deal with them.
  
  The transform applied here simply tightens the bounds on the variables.
  Other transforms will remove the fixed variables, leaving an empty row which
  is ultimately dropped.

  A reasonable question to ask is ``If a variable is already fixed, why do
  we need a record in the postsolve object?'' The answer is that in postsolve
  we'll be dealing with a column-major representation and we may need to scan
  the row (see postsolve comments). So it's useful to record all variables in
  the constraint.
  
  On the other hand, it's definitely harmful to ask remove_fixed_action
  to process a variable more than once (causes problems in
  remove_fixed_action::postsolve).

  Original comments:

  It looks like these checks could be performed in parallel, that is,
  the tests could be carried out for all rows in parallel, and then the
  rows deleted and columns tightened afterward.  Obviously, this is true
  for useless rows.  By doing it in parallel rather than sequentially,
  we may miss transformations due to variables that were fixed by forcing
  constraints, though.

  Note that both of these operations will cause problems if the variables
  in question really need to exceed their bounds in order to make the
  problem feasible.
*/
const CoinPresolveAction*
  forcing_constraint_action::presolve (CoinPresolveMatrix *prob,
  				       const CoinPresolveAction *next)
{
# if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING
  int startEmptyRows = 0 ;
  int startEmptyColumns = 0 ;
  startEmptyRows = prob->countEmptyRows() ;
  startEmptyColumns = prob->countEmptyCols() ;
# endif
# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
# if PRESOLVE_DEBUG > 0
  std::cout
    << "Entering forcing_constraint_action::presolve, considering "
    << prob->numberRowsToDo_ << " rows." << std::endl ;
# endif
  presolve_check_sol(prob) ;
  presolve_check_nbasic(prob) ;
# endif
# if COIN_PRESOLVE_TUNING
  double startTime = 0.0 ;
  if (prob->tuning_) {
    startTime = CoinCpuTime() ;
  }
# endif

  // Column solution and bounds
  double *clo = prob->clo_ ;
  double *cup = prob->cup_ ;
  double *csol = prob->sol_ ;

  // Row-major representation
  const CoinBigIndex *mrstrt = prob->mrstrt_ ;
  const double *rowels = prob->rowels_ ;
  const int *hcol = prob->hcol_ ;
  const int *hinrow = prob->hinrow_ ;
  const int nrows = prob->nrows_ ;

  const double *rlo = prob->rlo_ ;
  const double *rup = prob->rup_ ;

  const double tol = ZTOLDP ;
  const double inftol = prob->feasibilityTolerance_ ;
  // for redundant rows be safe
  const double inftol2 = 0.01*prob->feasibilityTolerance_ ;
  const int ncols = prob->ncols_ ;

  int *fixed_cols = new int[ncols] ;
  int nfixed_cols = 0 ;

  action *actions = new action [nrows] ;
  int nactions = 0 ;

  int *useless_rows = new int[nrows] ;
  int nuseless_rows = 0 ;

  const int numberLook = prob->numberRowsToDo_ ;
  int *look = prob->rowsToDo_ ;

  bool fixInfeasibility = ((prob->presolveOptions_&0x4000) != 0) ;
/*
  Open a loop to scan the constraints of interest. There must be variables
  left in the row.
*/
  for (int iLook = 0 ; iLook < numberLook ; iLook++) {
    int irow = look[iLook] ;
    if (hinrow[irow] <= 0) continue ;

    const CoinBigIndex krs = mrstrt[irow] ;
    const CoinBigIndex kre = krs+hinrow[irow] ;
/*
  Calculate upper and lower bounds on the row activity based on upper and lower
  bounds on the variables. If these are finite and incompatible with the given
  row bounds, we have infeasibility.
*/
    double maxup, maxdown ;
    implied_row_bounds(rowels,clo,cup,hcol,krs,kre,maxup,maxdown) ;
#   if PRESOLVE_DEBUG > 2
    std::cout
      << "  considering row " << irow << ", rlo " << rlo[irow]
      << " LB " << maxdown << " UB " << maxup << " rup " << rup[irow] ;
#   endif
/*
  If the maximum lhs value is less than L(i) or the minimum lhs value is
  greater than U(i), we're infeasible.
*/
    if (maxup < PRESOLVE_INF &&
        maxup+inftol < rlo[irow] && !fixInfeasibility) {
      CoinMessageHandler *hdlr = prob->messageHandler() ;
      prob->status_|= 1 ;
      hdlr->message(COIN_PRESOLVE_ROWINFEAS,prob->messages())
	 << irow << rlo[irow] << rup[irow] << CoinMessageEol ;
#     if PRESOLVE_DEBUG > 2
      std::cout << "; infeasible." << std::endl ;
#     endif
      break ;
    }
    if (-PRESOLVE_INF < maxdown &&
        rup[irow] < maxdown-inftol && !fixInfeasibility) {
      CoinMessageHandler *hdlr = prob->messageHandler() ;
      prob->status_|= 1 ;
      hdlr->message(COIN_PRESOLVE_ROWINFEAS,prob->messages())
	 << irow << rlo[irow] << rup[irow] << CoinMessageEol ;
#     if PRESOLVE_DEBUG > 2
      std::cout << "; infeasible." << std::endl ;
#     endif
      break ;
    }
/*
  We've dealt with prima facie infeasibility. Now check if the constraint
  is trivially satisfied. If so, add it to the list of useless rows and move
  on.

  The reason we require maxdown and maxup to be finite if the row bound is
  finite is to guard against some subsequent transform changing a column
  bound from infinite to finite. Once finite, bounds continue to tighten,
  so we're safe.
*/
    /* Test changed to use +small tolerance rather than -tolerance
       as test fails often */
    if (((rlo[irow] <= -PRESOLVE_INF) ||
	 (-PRESOLVE_INF < maxdown && rlo[irow] <= maxdown+inftol2)) &&
	((rup[irow] >= PRESOLVE_INF) ||
	 (maxup < PRESOLVE_INF && rup[irow] >= maxup-inftol2))) {
      // check none prohibited
      if (prob->anyProhibited_) {
	bool anyProhibited=false;
	for (int k=krs; k<kre; k++) {
	  int jcol = hcol[k];
	  if (prob->colProhibited(jcol)) {
	    anyProhibited=true;
	    break;
	  }
	}
	if (anyProhibited)
	  continue; // skip row
      }
      useless_rows[nuseless_rows++] = irow ;
#     if PRESOLVE_DEBUG > 2
      std::cout << "; useless." << std::endl ;
#     endif
      continue ;
    }
/*
  Is it the case that we can just barely attain L(i) or U(i)? If so, we have a
  forcing constraint. As explained above, we need maxup and maxdown to be
  finite in order for the test to be valid.
*/
    const bool tightAtLower = ((maxup < PRESOLVE_INF) &&
    			       (fabs(rlo[irow]-maxup) < tol)) ;
    const bool tightAtUpper = ((-PRESOLVE_INF < maxdown) &&
			       (fabs(rup[irow]-maxdown) < tol)) ;
#   if PRESOLVE_DEBUG > 2
    if (tightAtLower || tightAtUpper) std::cout << "; forcing." ;
    std::cout << std::endl ;
#   endif
    if (!(tightAtLower || tightAtUpper)) continue ;
    // check none prohibited
    if (prob->anyProhibited_) {
      bool anyProhibited=false;
      for (int k=krs; k<kre; k++) {
	int jcol = hcol[k];
	if (prob->colProhibited(jcol)) {
	  anyProhibited=true;
	  break;
	}
      }
      if (anyProhibited)
	continue; // skip row
    }
/*
  We have a forcing constraint.
  Get down to the business of fixing the variables at the appropriate bound.
  We need to remember the original value of the bound we're tightening.
  Allocate a pair of arrays the size of the row. Load variables fixed at l<j>
  from the start, variables fixed at u<j> from the end. Add the column to
  the list of columns to be processed further.
*/
    double *bounds = new double[hinrow[irow]] ;
    int *rowcols = new int[hinrow[irow]] ;
    CoinBigIndex lk = krs ;
    CoinBigIndex uk = kre ;
    for (CoinBigIndex k = krs ; k < kre ; k++) {
      const int j = hcol[k] ;
      const double lj = clo[j] ;
      const double uj = cup[j] ;
      const double coeff = rowels[k] ;
      PRESOLVEASSERT(fabs(coeff) > ZTOLDP) ;
/*
  If maxup is tight at L(i), then we want to force variables x<j> to the bound
  that produced maxup: u<j> if a<ij> > 0, l<j> if a<ij> < 0. If maxdown is
  tight at U(i), it'll be just the opposite.
*/
      if (tightAtLower == (coeff > 0.0)) {
	--uk ;
	bounds[uk-krs] = lj ;
	rowcols[uk-krs] = j ;
	if (csol != 0) {
	  csol[j] = uj ;
	}
	clo[j] = uj ;
      } else {
	bounds[lk-krs] = uj ;
	rowcols[lk-krs] = j ;
	++lk ;
	if (csol != 0) {
	  csol[j] = lj ;
	}
	cup[j] = lj ;
      }
/*
  Only add a column to the list of fixed columns the first time it's fixed.
*/
      if (lj != uj) {
	fixed_cols[nfixed_cols++] = j ;
	prob->addCol(j) ;
      }
    }
    PRESOLVEASSERT(uk == lk) ;
    PRESOLVE_DETAIL_PRINT(printf("pre_forcing %dR E\n",irow)) ;
#   if PRESOLVE_DEBUG > 1
    std::cout
      << "FORCING: row(" << irow << "), " << (kre-krs) << " variables."
      << std::endl ;
#   endif
/*
  Done with this row. Remember the changes in a postsolve action.
*/
    action *f = &actions[nactions] ;
    nactions++ ;
    f->row = irow ;
    f->nlo = lk-krs ;
    f->nup = kre-uk ;
    f->rowcols = rowcols ;
    f->bounds = bounds ;
  }

/*
  Done processing the rows of interest.  No sense doing any additional work
  unless we're feasible.
*/
  if (prob->status_ == 0) {
#   if PRESOLVE_DEBUG > 0
    std::cout
      << "FORCING: " << nactions << " forcing, " << nuseless_rows << " useless."
      << std::endl ;
#   endif
/*
  Trim the actions array to size and create a postsolve object.
*/
    if (nactions) {
      next = new forcing_constraint_action(nactions, 
				 CoinCopyOfArray(actions,nactions),next) ;
    }
/*
  Hand off the job of dealing with the useless rows to a specialist.
*/
    if (nuseless_rows) {
      next = useless_constraint_action::presolve(prob,
      					useless_rows,nuseless_rows,next) ;
    }
/*
  Hand off the job of dealing with the fixed columns to a specialist.

  Note that there *cannot* be duplicates in this list or we'll get in trouble
  `unfixing' a column multiple times. The code above now adds a variable
  to fixed_cols only if it's not already fixed. If that ever changes,
  the disabled code (sort, unique) will need to be reenabled.
*/
    if (nfixed_cols) {
      if (false && nfixed_cols > 1) {
	std::sort(fixed_cols,fixed_cols+nfixed_cols) ;
	int *end = std::unique(fixed_cols,fixed_cols+nfixed_cols) ;
	nfixed_cols = static_cast<int>(end-fixed_cols) ;
      }
      next = remove_fixed_action::presolve(prob,fixed_cols,nfixed_cols,next) ;
    }
  } else {
    // delete arrays
    for (int i=0;i<nactions;i++) {
      deleteAction(actions[i].rowcols,int *) ;
      deleteAction(actions[i].bounds,double *) ;
    }
  }

  deleteAction(actions,action*) ;
  delete [] useless_rows ;
  delete [] fixed_cols ;

# if COIN_PRESOLVE_TUNING
  if (prob->tuning_) double thisTime = CoinCpuTime() ;
# endif
# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
  presolve_check_sol(prob) ;
  presolve_check_nbasic(prob) ;
# endif
# if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING
  int droppedRows = prob->countEmptyRows()-startEmptyRows ;
  int droppedColumns =  prob->countEmptyCols()-startEmptyColumns ;
  std::cout
    << "Leaving forcing_constraint_action::presolve: removed " << droppedRows
    << " rows, " << droppedColumns << " columns" ;
# if COIN_PRESOLVE_TUNING > 0
  if (prob->tuning_)
    std::cout << " in " << (thisTime-prob->startTime_) << "s" ;
# endif
  std::cout << "." << std::endl ;
# endif

  return (next) ;
}
/*
 *
 * The col rep and row rep must be consistent.
 */
const CoinPresolveAction *tripleton_action::presolve(CoinPresolveMatrix *prob,
						  const CoinPresolveAction *next)
{
  double startTime = 0.0;
  int startEmptyRows=0;
  int startEmptyColumns = 0;
  if (prob->tuning_) {
    startTime = CoinCpuTime();
    startEmptyRows = prob->countEmptyRows();
    startEmptyColumns = prob->countEmptyCols();
  }
  double *colels	= prob->colels_;
  int *hrow		= prob->hrow_;
  CoinBigIndex *mcstrt		= prob->mcstrt_;
  int *hincol		= prob->hincol_;
  int ncols		= prob->ncols_;

  double *clo	= prob->clo_;
  double *cup	= prob->cup_;

  double *rowels	= prob->rowels_;
  int *hcol		= prob->hcol_;
  CoinBigIndex *mrstrt		= prob->mrstrt_;
  int *hinrow		= prob->hinrow_;
  int nrows		= prob->nrows_;

  double *rlo	= prob->rlo_;
  double *rup	= prob->rup_;

  presolvehlink *clink = prob->clink_;
  presolvehlink *rlink = prob->rlink_;

  const unsigned char *integerType = prob->integerType_;

  double *cost	= prob->cost_;

  int numberLook = prob->numberRowsToDo_;
  int iLook;
  int * look = prob->rowsToDo_;
  const double ztolzb	= prob->ztolzb_;

  action * actions = new action [nrows];
# ifdef ZEROFAULT
  // initialise alignment padding bytes
  memset(actions,0,nrows*sizeof(action)) ;
# endif
  int nactions = 0;

  int *zeros	= prob->usefulColumnInt_; //new int[ncols];
  char * mark = reinterpret_cast<char *>(zeros+ncols);
  memset(mark,0,ncols);
  int nzeros	= 0;

  // If rowstat exists then all do
  unsigned char *rowstat	= prob->rowstat_;
  double *acts	= prob->acts_;
  //  unsigned char * colstat = prob->colstat_;


# if PRESOLVE_CONSISTENCY
  presolve_links_ok(prob) ;
# endif

  // wasfor (int irow=0; irow<nrows; irow++)
  for (iLook=0;iLook<numberLook;iLook++) {
    int irow = look[iLook];
    if (hinrow[irow] == 3 &&
	fabs(rup[irow] - rlo[irow]) <= ZTOLDP) {
      double rhs = rlo[irow];
      CoinBigIndex krs = mrstrt[irow];
      CoinBigIndex kre = krs + hinrow[irow];
      int icolx, icoly, icolz;
      double coeffx, coeffy, coeffz;
      CoinBigIndex k;
      
      /* locate first column */
      for (k=krs; k<kre; k++) {
	if (hincol[hcol[k]] > 0) {
	  break;
	}
      }
      PRESOLVEASSERT(k<kre);
      coeffx = rowels[k];
      if (fabs(coeffx) < ZTOLDP2)
	continue;
      icolx = hcol[k];
      
      
      /* locate second column */
      for (k++; k<kre; k++) {
	if (hincol[hcol[k]] > 0) {
	  break;
	}
      }
      PRESOLVEASSERT(k<kre);
      coeffy = rowels[k];
      if (fabs(coeffy) < ZTOLDP2)
	continue;
      icoly = hcol[k];
      
      /* locate third column */
      for (k++; k<kre; k++) {
	if (hincol[hcol[k]] > 0) {
	  break;
	}
      }
      PRESOLVEASSERT(k<kre);
      coeffz = rowels[k];
      if (fabs(coeffz) < ZTOLDP2)
	continue;
      icolz = hcol[k];
      
      // For now let's do obvious one
      if (coeffx*coeffz>0.0) {
	if(coeffx*coeffy>0.0) 
	  continue;
      } else if (coeffx*coeffy>0.0) {
	int iTemp = icoly;
	icoly=icolz;
	icolz=iTemp;
	double dTemp = coeffy;
	coeffy=coeffz;
	coeffz=dTemp;
      } else {
	int iTemp = icoly;
	icoly=icolx;
	icolx=iTemp;
	double dTemp = coeffy;
	coeffy=coeffx;
	coeffx=dTemp;
      }
      // Not all same sign and y is odd one out
      // don't bother with fixed variables
      if (!(fabs(cup[icolx] - clo[icolx]) < ZTOLDP) &&
	  !(fabs(cup[icoly] - clo[icolx]) < ZTOLDP) &&
	  !(fabs(cup[icolz] - clo[icoly]) < ZTOLDP)) {
	assert (coeffx*coeffz>0.0&&coeffx*coeffy<0.0);
	// Only do if does not give implicit bounds on x and z
	double cx = - coeffx/coeffy;
	double cz = - coeffz/coeffy;
	/* don't do if y integer for now */
	if (integerType[icoly]) {
#define PRESOLVE_DANGEROUS
#ifndef PRESOLVE_DANGEROUS
	  continue;
#else
	  if (!integerType[icolx]||!integerType[icolz])
	    continue;
	  if (cx!=floor(cx+0.5)||cz!=floor(cz+0.5))
	    continue;
#endif
	}
	double rhsRatio = rhs/coeffy;
	if (clo[icoly]>-1.0e30) {
	  if (clo[icolx]<-1.0e30||clo[icolz]<-1.0e30)
	    continue;
	  if (cx*clo[icolx]+cz*clo[icolz]+rhsRatio<clo[icoly]-ztolzb)
	    continue;
	}
	if (cup[icoly]<1.0e30) {
	  if (cup[icolx]>1.0e30||cup[icolz]>1.0e30)
	    continue;
	  if (cx*cup[icolx]+cz*cup[icolz]+rhsRatio>cup[icoly]+ztolzb)
	    continue;
	}
	CoinBigIndex krowx,krowy,krowz;
	/* find this row in each of the columns and do counts */
	bool singleton=false;
	for (k=mcstrt[icoly]; k<mcstrt[icoly]+hincol[icoly]; k++) {
	  int jrow=hrow[k];
	  if (hinrow[jrow]==1)
	    singleton=true;
	  if (jrow == irow) 
	    krowy=k;
	  else
	    prob->setRowUsed(jrow);
	}
	int nDuplicate=0;
	for (k=mcstrt[icolx]; k<mcstrt[icolx]+hincol[icolx]; k++) {
	  int jrow=hrow[k];
	  if (jrow == irow) 
	    krowx=k;
	  else if (prob->rowUsed(jrow))
	    nDuplicate++;;
	}
	for (k=mcstrt[icolz]; k<mcstrt[icolz]+hincol[icolz]; k++) {
	  int jrow=hrow[k];
	  if (jrow == irow) 
	    krowz=k;
	  else if (prob->rowUsed(jrow))
	    nDuplicate++;;
	}
	int nAdded=hincol[icoly]-3-nDuplicate;
	for (k=mcstrt[icoly]; k<mcstrt[icoly]+hincol[icoly]; k++) {
	  int jrow=hrow[k];
	  prob->unsetRowUsed(jrow);
	}
	// let singleton rows be taken care of first
	if (singleton)
	  continue;
	//if (nAdded<=1) 
	//printf("%d elements added, hincol %d , dups %d\n",nAdded,hincol[icoly],nDuplicate);
	if (nAdded>2)
	  continue;

	// it is possible that both x/z and y are singleton columns
	// that can cause problems
	if ((hincol[icolx] == 1 ||hincol[icolz] == 1) && hincol[icoly] == 1)
	  continue;

	// common equations are of the form ax + by = 0, or x + y >= lo
	{
	  action *s = &actions[nactions];	  
	  nactions++;
	  PRESOLVE_DETAIL_PRINT(printf("pre_tripleton %dR %dC %dC %dC E\n",
				       irow,icoly,icolx,icolz));
	  
	  s->row = irow;
	  s->icolx = icolx;
	  s->icolz = icolz;
	  
	  s->icoly = icoly;
	  s->cloy = clo[icoly];
	  s->cupy = cup[icoly];
	  s->costy = cost[icoly];
	  
	  s->rlo = rlo[irow];
	  s->rup = rup[irow];
	  
	  s->coeffx = coeffx;
	  s->coeffy = coeffy;
	  s->coeffz = coeffz;
	  
	  s->ncoly	= hincol[icoly];
	  s->colel	= presolve_dupmajor(colels, hrow, hincol[icoly],
					    mcstrt[icoly]);
	}

	// costs
	// the effect of maxmin cancels out
	cost[icolx] += cost[icoly] * cx;
	cost[icolz] += cost[icoly] * cz;

	prob->change_bias(cost[icoly] * rhs / coeffy);
	//if (cost[icoly]*rhs)
	//printf("change %g col %d cost %g rhs %g coeff %g\n",cost[icoly]*rhs/coeffy,
	// icoly,cost[icoly],rhs,coeffy);

	if (rowstat) {
	  // update solution and basis
	  int numberBasic=0;
	  if (prob->columnIsBasic(icoly))
	    numberBasic++;
	  if (prob->rowIsBasic(irow))
	    numberBasic++;
	  if (numberBasic>1) {
	    if (!prob->columnIsBasic(icolx))
	      prob->setColumnStatus(icolx,CoinPrePostsolveMatrix::basic);
	    else
	      prob->setColumnStatus(icolz,CoinPrePostsolveMatrix::basic);
	  }
	}
	  
	// Update next set of actions
	{
	  prob->addCol(icolx);
	  int i,kcs,kce;
	  kcs = mcstrt[icoly];
	  kce = kcs + hincol[icoly];
	  for (i=kcs;i<kce;i++) {
	    int row = hrow[i];
	    prob->addRow(row);
	  }
	  kcs = mcstrt[icolx];
	  kce = kcs + hincol[icolx];
	  for (i=kcs;i<kce;i++) {
	    int row = hrow[i];
	    prob->addRow(row);
	  }
	  prob->addCol(icolz);
	  kcs = mcstrt[icolz];
	  kce = kcs + hincol[icolz];
	  for (i=kcs;i<kce;i++) {
	    int row = hrow[i];
	    prob->addRow(row);
	  }
	}

	/* transfer the colx factors to coly */
	bool no_mem = elim_tripleton("ELIMT",
				     mcstrt, rlo, acts, rup, colels,
				     hrow, hcol, hinrow, hincol,
				     clink, ncols, rlink, nrows,
				     mrstrt, rowels,
				     cx,
				     cz,
				     rhs / coeffy,
				     irow, icolx, icoly,icolz);
	if (no_mem) 
	  throwCoinError("out of memory",
			 "tripleton_action::presolve");

	// now remove irow from icolx and icolz in the col rep
	// better if this were first.
	presolve_delete_from_col(irow,icolx,mcstrt,hincol,hrow,colels) ;
	presolve_delete_from_col(irow,icolz,mcstrt,hincol,hrow,colels) ;

	// eliminate irow entirely from the row rep
	hinrow[irow] = 0;

	// eliminate irow entirely from the row rep
	PRESOLVE_REMOVE_LINK(rlink, irow);

	// eliminate coly entirely from the col rep
	PRESOLVE_REMOVE_LINK(clink, icoly);
	cost[icoly] = 0.0;

	rlo[irow] = 0.0;
	rup[irow] = 0.0;

	if (!mark[icolx]) {
	  mark[icolx]=1;
	  zeros[nzeros++]=icolx;
	}
	if (!mark[icolz]) {
	  mark[icolz]=1;
	  zeros[nzeros++]=icolz;
	}
      }
      
#     if PRESOLVE_CONSISTENCY
      presolve_links_ok(prob) ;
      presolve_consistent(prob);
#     endif
    }
  }
  if (nactions) {
#   if PRESOLVE_SUMMARY
    printf("NTRIPLETONS:  %d\n", nactions);
#   endif
    action *actions1 = new action[nactions];
    CoinMemcpyN(actions, nactions, actions1);

    next = new tripleton_action(nactions, actions1, next);

    if (nzeros) {
      next = drop_zero_coefficients_action::presolve(prob, zeros, nzeros, next);
    }
  }

  //delete[]zeros;
  deleteAction(actions,action*);

  if (prob->tuning_) {
    double thisTime=CoinCpuTime();
    int droppedRows = prob->countEmptyRows() - startEmptyRows ;
    int droppedColumns =  prob->countEmptyCols() - startEmptyColumns;
    printf("CoinPresolveTripleton(8) - %d rows, %d columns dropped in time %g, total %g\n",
	   droppedRows,droppedColumns,thisTime-startTime,thisTime-prob->startTime_);
  }
  return (next);
}
Esempio n. 5
0
const CoinPresolveAction *do_tighten_action::presolve(CoinPresolveMatrix *prob,
					       const CoinPresolveAction *next)
{
  double *colels	= prob->colels_;
  int *hrow		= prob->hrow_;
  CoinBigIndex *mcstrt		= prob->mcstrt_;
  int *hincol		= prob->hincol_;
  int ncols		= prob->ncols_;

  double *clo	= prob->clo_;
  double *cup	= prob->cup_;

  double *rlo	= prob->rlo_;
  double *rup	= prob->rup_;

  double *dcost	= prob->cost_;

  const unsigned char *integerType = prob->integerType_;

  int *fix_cols	= prob->usefulColumnInt_;
  int nfixup_cols	= 0;

  int nfixdown_cols	= ncols;

  int *useless_rows	= prob->usefulRowInt_;
  int nuseless_rows	= 0;
  
  action *actions	= new action [ncols];
  int nactions		= 0;

  int numberLook = prob->numberColsToDo_;
  int iLook;
  int * look = prob->colsToDo_;
  bool fixInfeasibility = ((prob->presolveOptions_&0x4000) != 0) ;

# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
# if PRESOLVE_DEBUG > 0
  std::cout
    << "Entering do_tighten_action::presolve; considering " << numberLook
    << " rows." << std::endl ;
# endif
  presolve_consistent(prob) ;
  presolve_links_ok(prob) ;
  presolve_check_sol(prob) ;
  presolve_check_nbasic(prob) ;
# endif

# if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING > 0
  int startEmptyRows = 0 ;
  int startEmptyColumns = 0 ;
  startEmptyRows = prob->countEmptyRows() ;
  startEmptyColumns = prob->countEmptyCols() ;
# if COIN_PRESOLVE_TUNING > 0
  double startTime = 0.0;
  if (prob->tuning_) startTime = CoinCpuTime() ;
# endif
# endif


  // singleton columns are especially likely to be caught here
  for (iLook=0;iLook<numberLook;iLook++) {
    int j = look[iLook];
    // modify bounds if integer
    if (integerType[j]) {
      clo[j] = ceil(clo[j]-1.0e-12);
      cup[j] = floor(cup[j]+1.0e-12);
      if (clo[j]>cup[j]&&!fixInfeasibility) {
        // infeasible
	prob->status_|= 1;
	prob->messageHandler()->message(COIN_PRESOLVE_COLINFEAS,
					     prob->messages())
				 	       <<j
					       <<clo[j]
					       <<cup[j]
					       <<CoinMessageEol;
      }
    }
    if (dcost[j]==0.0) {
      int iflag=0; /* 1 - up is towards feasibility, -1 down is towards */
      int nonFree=0; // Number of non-free rows

      CoinBigIndex kcs = mcstrt[j];
      CoinBigIndex kce = kcs + hincol[j];

      // check constraints
      for (CoinBigIndex k=kcs; k<kce; ++k) {
	int i = hrow[k];
	double coeff = colels[k];
	double rlb = rlo[i];
	double rub = rup[i];

	if (-1.0e28 < rlb && rub < 1.0e28) {
	  // bounded - we lose
	  iflag=0;
	  break;
	} else if (-1.0e28 < rlb || rub < 1.0e28) {
	  nonFree++;
	}

	PRESOLVEASSERT(fabs(coeff) > ZTOLDP);

	// see what this particular row says
	// jflag == 1 ==> up is towards feasibility
	int jflag = (coeff > 0.0
		     ? (rub >  1.0e28 ? 1 : -1)
		     : (rlb < -1.0e28 ? 1 : -1));

	if (iflag) {
	  // check that it agrees with iflag.
	  if (iflag!=jflag) {
	    iflag=0;
	    break;
	  }
	} else {
	  // first row -- initialize iflag
	  iflag=jflag;
	}
      }
      // done checking constraints
      if (!nonFree)
	iflag=0; // all free anyway
      if (iflag) {
	if (iflag==1 && cup[j]<1.0e10) {
#if	PRESOLVE_DEBUG > 1
	  printf("TIGHTEN UP:  %d\n", j);
#endif
	  fix_cols[nfixup_cols++] = j;

	} else if (iflag==-1&&clo[j]>-1.0e10) {
	  // symmetric case
	  //mpre[j] = PRESOLVE_XUP;

#if	PRESOLVE_DEBUG > 1
	  printf("TIGHTEN DOWN:  %d\n", j);
#endif

	  fix_cols[--nfixdown_cols] = j;

	} else {
#if 0
	  static int limit;
	  static int which = atoi(getenv("WZ"));
	  if (which == -1)
	    ;
	  else if (limit != which) {
	    limit++;
	    continue;
	  } else
	    limit++;

	  printf("TIGHTEN STATS %d %g %g %d:  \n", j, clo[j], cup[j], integerType[j]); 
  double *rowels	= prob->rowels_;
  int *hcol		= prob->hcol_;
  int *mrstrt		= prob->mrstrt_;
  int *hinrow		= prob->hinrow_;
	  for (CoinBigIndex k=kcs; k<kce; ++k) {
	    int irow = hrow[k];
	    CoinBigIndex krs = mrstrt[irow];
	    CoinBigIndex kre = krs + hinrow[irow];
	    printf("%d  %g %g %g:  ",
		   irow, rlo[irow], rup[irow], colels[irow]);
	    for (CoinBigIndex kk=krs; kk<kre; ++kk)
	      printf("%d(%g) ", hcol[kk], rowels[kk]);
	    printf("\n");
	  }
#endif

	  {
	    action *s = &actions[nactions];	  
	    nactions++;
	    s->col = j;
	    PRESOLVE_DETAIL_PRINT(printf("pre_tighten %dC E\n",j));
	    if (integerType[j]) {
	      assert (iflag==-1||iflag==1);
	      iflag *= 2; // say integer
	    }
	    s->direction = iflag;

	    s->rows =   new int[hincol[j]];
	    s->lbound = new double[hincol[j]];
	    s->ubound = new double[hincol[j]];
#if         PRESOLVE_DEBUG > 1
	    printf("TIGHTEN FREE:  %d   ", j);
#endif
	    int nr = 0;
            prob->addCol(j);
	    for (CoinBigIndex k=kcs; k<kce; ++k) {
	      int irow = hrow[k];
	      // ignore this if we've already made it useless
	      if (! (rlo[irow] == -PRESOLVE_INF && rup[irow] == PRESOLVE_INF)) {
		prob->addRow(irow);
		s->rows  [nr] = irow;
		s->lbound[nr] = rlo[irow];
		s->ubound[nr] = rup[irow];
		nr++;

		useless_rows[nuseless_rows++] = irow;

		rlo[irow] = -PRESOLVE_INF;
		rup[irow] = PRESOLVE_INF;

#if             PRESOLVE_DEBUG > 1
		printf("%d ", irow);
#endif
	      }
	    }
	    s->nrows = nr;

#if         PRESOLVE_DEBUG > 1
	    printf("\n");
#endif
	  }
	}
      }
    }
  }


#if	PRESOLVE_SUMMARY > 0
  if (nfixdown_cols<ncols || nfixup_cols || nuseless_rows) {
    printf("NTIGHTENED:  %d %d %d\n", ncols-nfixdown_cols, nfixup_cols, nuseless_rows);
  }
#endif

  if (nuseless_rows) {
    next = new do_tighten_action(nactions, CoinCopyOfArray(actions,nactions), next);

    next = useless_constraint_action::presolve(prob,
					       useless_rows, nuseless_rows,
					       next);
  }
  deleteAction(actions, action*);
  //delete[]useless_rows;

  if (nfixdown_cols<ncols) {
    int * fixdown_cols = fix_cols+nfixdown_cols; 
    nfixdown_cols = ncols-nfixdown_cols;
    next = make_fixed_action::presolve(prob, fixdown_cols, nfixdown_cols,
				       true,
				       next);
  }
  //delete[]fixdown_cols;

  if (nfixup_cols) {
    next = make_fixed_action::presolve(prob, fix_cols, nfixup_cols,
				       false,
				       next);
  }
  //delete[]fixup_cols;

# if COIN_PRESOLVE_TUNING > 0
  if (prob->tuning_) double thisTime = CoinCpuTime() ;
# endif
# if PRESOLVE_CONSISTENCY > 0 || PRESOLVE_DEBUG > 0
  presolve_check_sol(prob) ;
# endif
# if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING > 0
  int droppedRows = prob->countEmptyRows()-startEmptyRows ;
  int droppedColumns = prob->countEmptyCols()-startEmptyColumns ;
  std::cout
    << "Leaving do_tighten_action::presolve, " << droppedRows << " rows, "
    << droppedColumns << " columns dropped" ;
# if COIN_PRESOLVE_TUNING > 0
  std::cout << " in " << thisTime-startTime << "s" ;
# endif
  std::cout << "." << std::endl ;
# endif

  return (next);
}
Esempio n. 6
0
const CoinPresolveAction *subst_constraint_action::presolve (
    CoinPresolveMatrix *prob,
    const int *implied_free, const int *whichFree, int numberFree,
    const CoinPresolveAction *next, int maxLook)
{

# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
# if PRESOLVE_DEBUG > 0
  std::cout
    << "Entering subst_constraint_action::presolve, fill level "
    << maxLook << ", " << numberFree << " candidates." << std::endl ;
# endif
  presolve_consistent(prob) ;
  presolve_links_ok(prob) ;
  presolve_check_sol(prob) ;
  presolve_check_nbasic(prob) ;
# endif

# if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING > 0
  int startEmptyRows = 0 ;
  int startEmptyColumns = 0 ;
  startEmptyRows = prob->countEmptyRows() ;
  startEmptyColumns = prob->countEmptyCols() ;
# if COIN_PRESOLVE_TUNING > 0
  double startTime = 0.0 ;
  if (prob->tuning_) startTime = CoinCpuTime() ;
# endif
# endif

/*
  Unpack the row- and column-major representations.
*/
  const int ncols = prob->ncols_ ;
  const int nrows = prob->nrows_ ;

  CoinBigIndex *rowStarts = prob->mrstrt_ ;
  int *rowLengths = prob->hinrow_ ;
  double *rowCoeffs = prob->rowels_ ;
  int *colIndices = prob->hcol_ ;
  presolvehlink *rlink = prob->rlink_ ;

  CoinBigIndex *colStarts = prob->mcstrt_ ;
  int *colLengths = prob->hincol_ ;
  double *colCoeffs = prob->colels_ ;
  int *rowIndices = prob->hrow_ ;
  presolvehlink *clink = prob->clink_ ;

/*
  Row bounds and activity, objective.
*/
  double *rlo = prob->rlo_ ;
  double *rup = prob->rup_ ;
  double *acts = prob->acts_ ;
  double *cost = prob->cost_ ;


  const double tol = prob->feasibilityTolerance_ ;

  action *actions = new action [ncols] ;
# ifdef ZEROFAULT
  CoinZeroN(reinterpret_cast<char *>(actions),ncols*sizeof(action)) ;
# endif
  int nactions = 0 ;
/*
  This array is used to hold the indices of columns involved in substitutions,
  where we have the potential for cancellation. At the end they'll be
  checked to eliminate any actual zeros that may result. At the end of
  processing of each target row, the column indices of the target row are
  copied into zerocols.

  NOTE that usefulColumnInt_ is already in use for parameters implied_free and
  whichFree when this routine is called from implied_free.
*/
  int *zerocols = new int[ncols] ;
  int nzerocols = 0 ;

  int *x_to_y = new int[ncols] ;

  int *rowsUsed = &prob->usefulRowInt_[0] ;
  int nRowsUsed = 0 ;
/*
  Open a loop to process the (equality r, implied free variable t) pairs
  in whichFree and implied_free.

  It can happen that removal of (row, natural singleton) pairs back in
  implied_free will reduce the length of column t. It can also happen
  that previous processing here has resulted in fillin or cancellation. So
  check again for column length and exclude natural singletons and overly
  dense columns.
*/
  for (int iLook = 0 ; iLook < numberFree ; iLook++) {
    const int tgtcol = whichFree[iLook] ;
    const int tgtcol_len = colLengths[tgtcol] ;
    const int tgtrow = implied_free[iLook] ;
    const int tgtrow_len = rowLengths[tgtrow] ;

    assert(fabs(rlo[tgtrow]-rup[tgtrow]) < tol) ;

    if (colLengths[tgtcol] < 2 || colLengths[tgtcol] > maxLook) {
#     if PRESOLVE_DEBUG > 3
      std::cout
        << "    skipping eqn " << tgtrow << " x(" << tgtcol
	<< "); length now " << colLengths[tgtcol] << "." << std::endl ;
#     endif
      continue ;
    }

    CoinBigIndex tgtcs = colStarts[tgtcol] ;
    CoinBigIndex tgtce = tgtcs+colLengths[tgtcol] ;
/*
  A few checks to make sure that the candidate pair is still suitable.
  Processing candidates earlier in the list can eliminate coefficients.
    * Don't use this pair if any involved row i has become a row singleton
      or empty.
    * Don't use this pair if any involved row has been modified as part of
      the processing for a previous candidate pair on this call.
    * Don't use this pair if a(i,tgtcol) has become zero.

  The checks on a(i,tgtcol) seem superfluous but it's possible that
  implied_free identified two candidate pairs to eliminate the same column. If
  we've already processed one of them, we could be in trouble.
*/
    double tgtcoeff = 0.0 ;
    bool dealBreaker = false ;
    for (CoinBigIndex kcol = tgtcs ; kcol < tgtce ; ++kcol) {
      const int i = rowIndices[kcol] ;
      if (rowLengths[i] < 2 || prob->rowUsed(i)) {
        dealBreaker = true ;
	break ;
      }
      const double aij = colCoeffs[kcol] ;
      if (fabs(aij) <= ZTOLDP2) {
	dealBreaker = true ;
	break ;
      }
      if (i == tgtrow) tgtcoeff = aij ;
    }

    if (dealBreaker == true) {
#     if PRESOLVE_DEBUG > 3
      std::cout
        << "    skipping eqn " << tgtrow << " x(" << tgtcol
	<< "); deal breaker (1)." << std::endl ;
#     endif
      continue ;
    }
/*
  Check for numerical stability.A large coeff_factor will inflate the
  coefficients in the substitution formula.
*/
    dealBreaker = false ;
    for (CoinBigIndex kcol = tgtcs ; kcol < tgtce ; ++kcol) {
      const double coeff_factor = fabs(colCoeffs[kcol]/tgtcoeff) ;
      if (coeff_factor > 10.0)
	dealBreaker = true ;
    }
/*
  Given enough target rows with sufficient overlap, there's an outside chance
  we could overflow zerocols. Unlikely to ever happen.
*/
    if (!dealBreaker && nzerocols+rowLengths[tgtrow] >= ncols)
      dealBreaker = true ;
    if (dealBreaker == true) {
#     if PRESOLVE_DEBUG > 3
      std::cout
        << "    skipping eqn " << tgtrow << " x(" << tgtcol
	<< "); deal breaker (2)." << std::endl ;
#     endif
      continue ;
    }
/*
  If c(t) != 0, we will need to modify the objective coefficients and remember
  the original objective.
*/
    const bool nonzero_cost = (fabs(cost[tgtcol]) > tol) ;
    double *costsx = (nonzero_cost?new double[rowLengths[tgtrow]]:0) ;

#   if PRESOLVE_DEBUG > 1
    std::cout << "  Eliminating row " << tgtrow << ", col " << tgtcol ;
    if (nonzero_cost) std::cout << ", cost " << cost[tgtcol] ;
    std::cout << "." << std::endl ;
#   endif

/*
  Count up the total number of coefficients in entangled rows and mark them as
  contaminated.
*/
    int ntotels = 0 ;
    for (CoinBigIndex kcol = tgtcs ; kcol < tgtce ; ++kcol) {
      const int i = rowIndices[kcol] ;
      ntotels += rowLengths[i] ;
      PRESOLVEASSERT(!prob->rowUsed(i)) ;
      prob->setRowUsed(i) ;
      rowsUsed[nRowsUsed++] = i ;
    }
/*
  Create the postsolve object. Copy in all the affected rows. Take the
  opportunity to mark the entangled rows as changed and put them on the list
  of rows to process in the next round.

  coeffxs in particular holds the coefficients of the target column.
*/
    action *ap = &actions[nactions++] ;

    ap->col = tgtcol ;
    ap->rowy = tgtrow ;
    PRESOLVE_DETAIL_PRINT(printf("pre_subst %dC %dR E\n",tgtcol,tgtrow)) ;

    ap->nincol = tgtcol_len ;
    ap->rows = new int[tgtcol_len] ;
    ap->rlos = new double[tgtcol_len] ;
    ap->rups = new double[tgtcol_len] ;

    ap->costsx = costsx ;
    ap->coeffxs = new double[tgtcol_len] ;

    ap->ninrowxs = new int[tgtcol_len] ;
    ap->rowcolsxs = new int[ntotels] ;
    ap->rowelsxs = new double[ntotels] ;

    ntotels = 0 ;
    for (CoinBigIndex kcol = tgtcs ; kcol < tgtce ; ++kcol) {
      const int ndx = kcol-tgtcs ;
      const int i = rowIndices[kcol] ;
      const CoinBigIndex krs = rowStarts[i] ;
      prob->addRow(i) ;
      ap->rows[ndx] = i ;
      ap->ninrowxs[ndx] = rowLengths[i] ;
      ap->rlos[ndx] = rlo[i] ;
      ap->rups[ndx] = rup[i] ;
      ap->coeffxs[ndx] = colCoeffs[kcol] ;

      CoinMemcpyN(&colIndices[krs],rowLengths[i],&ap->rowcolsxs[ntotels]) ;
      CoinMemcpyN(&rowCoeffs[krs],rowLengths[i],&ap->rowelsxs[ntotels]) ;

      ntotels += rowLengths[i] ;
    }

    CoinBigIndex tgtrs = rowStarts[tgtrow] ;
    CoinBigIndex tgtre = tgtrs+rowLengths[tgtrow] ;

/*
  Adjust the objective coefficients based on the substitution formula
    c'(j) = c(j) - a(rj)c(t)/a(rt)
*/
    if (nonzero_cost) {
      const double tgtcost = cost[tgtcol] ;
      for (CoinBigIndex krow = tgtrs ; krow < tgtre ; krow ++) {
	const int j = colIndices[krow] ;
	prob->addCol(j) ;
	costsx[krow-tgtrs] = cost[j] ;
	double coeff = rowCoeffs[krow] ;
	cost[j] -= (tgtcost*coeff)/tgtcoeff ;
      }
      prob->change_bias(tgtcost*rlo[tgtrow]/tgtcoeff) ;
      cost[tgtcol] = 0.0 ;
    }

#   if PRESOLVE_DEBUG > 1
    std::cout << "  tgt (" << tgtrow << ") (" << tgtrow_len << "): " ;
    for (CoinBigIndex krow = tgtrs ; krow < tgtre ; ++krow) {
      const int j = colIndices[krow] ;
      const double arj = rowCoeffs[krow] ;
      std::cout
	<< "x(" << j << ") = " << arj << " (" << colLengths[j] << ") " ;
    }
    std::cout << std::endl ;
#   endif
    // kill small if wanted
    int relax= (prob->presolveOptions()&0x60000)>>17;
    double tolerance = 1.0e-12;
    for (int i=0;i<relax;i++)
      tolerance *= 10.0;

/*
  Sort the target row for efficiency when doing elimination.
*/
      CoinSort_2(colIndices+tgtrs,colIndices+tgtre,rowCoeffs+tgtrs) ;
/*
  Get down to the business of substituting for tgtcol in the entangled rows.
  Open a loop to walk the target column. We walk the saved column because the
  bulk store can change as we work. We don't want to repeat or miss a row.
*/
    for (int colndx = 0 ; colndx < tgtcol_len ; ++colndx) {
      int i = ap->rows[colndx] ;
      if (i == tgtrow) continue ;

      double ait = ap->coeffxs[colndx] ;
      double coeff_factor = -ait/tgtcoeff ;
      
      CoinBigIndex krs = rowStarts[i] ;
      CoinBigIndex kre = krs+rowLengths[i] ;

#     if PRESOLVE_DEBUG > 1
      std::cout
	<< "  subst pre (" << i << ") (" << rowLengths[i] << "): " ;
      for (CoinBigIndex krow = krs ; krow < kre ; ++krow) {
	const int j = colIndices[krow] ;
	const double aij = rowCoeffs[krow] ;
	std::cout
	  << "x(" << j << ") = " << aij << " (" << colLengths[j] << ") " ;
      }
      std::cout << std::endl ;
#     endif

/*
  Sort the row for efficiency and call add_row to do the actual business of
  changing coefficients due to substitution. This has the potential to trigger
  compaction of the row-major bulk store, so update bulk store indices.
*/
      CoinSort_2(colIndices+krs,colIndices+kre,rowCoeffs+krs) ;
      
      bool outOfSpace = add_row(rowStarts,rlo,acts,rup,rowCoeffs,colIndices,
				rowLengths,rlink,nrows,coeff_factor,tolerance,i,tgtrow,
				x_to_y) ;
      if (outOfSpace)
	throwCoinError("out of memory","CoinImpliedFree::presolve") ;

      krs = rowStarts[i] ;
      kre = krs+rowLengths[i] ;
      tgtrs = rowStarts[tgtrow] ;
      tgtre = tgtrs+rowLengths[tgtrow] ;

#     if PRESOLVE_DEBUG > 1
      std::cout
	<< "  subst aft (" << i << ") (" << rowLengths[i] << "): " ;
      for (CoinBigIndex krow = krs ; krow < kre ; ++krow) {
	const int j = colIndices[krow] ;
	const double aij = rowCoeffs[krow] ;
	std::cout
	  << "x(" << j << ") = " << aij << " (" << colLengths[j] << ") " ;
      }
      std::cout << std::endl ;
#     endif

/*
  Now update the column-major representation from the row-major
  representation. This is easy if the coefficient already exists, but
  painful if there's fillin. presolve_find_row1 will return the index of
  the row in the column vector, or one past the end if it's missing. If the
  coefficient is fill, presolve_expand_col will make sure that there's room in
  the column for one more coefficient. This may require that the column be
  moved in the bulk store, so we need to update kcs and kce.

  Once we're done, a(it) = 0 (i.e., we've eliminated x(t) from row i).
  Physically remove the explicit zero from the row-major representation
  with presolve_delete_from_row.
*/
      for (CoinBigIndex rowndx = 0 ; rowndx < tgtrow_len ; ++rowndx) {
	const CoinBigIndex ktgt = tgtrs+rowndx ;
	const int j = colIndices[ktgt] ;
	CoinBigIndex kcs = colStarts[j] ;
	CoinBigIndex kce = kcs+colLengths[j] ;

	assert(colIndices[krs+x_to_y[rowndx]] == j) ;

	const double coeff = rowCoeffs[krs+x_to_y[rowndx]] ;

	CoinBigIndex kcol = presolve_find_row1(i,kcs,kce,rowIndices) ;
	
	if (kcol < kce) {
	  colCoeffs[kcol] = coeff ;
	} else {
	  outOfSpace = presolve_expand_col(colStarts,colCoeffs,rowIndices,
	  				   colLengths,clink,ncols,j) ;
	  if (outOfSpace)
	    throwCoinError("out of memory","CoinImpliedFree::presolve") ;
	  kcs = colStarts[j] ;
	  kce = kcs+colLengths[j] ;
	  
	  rowIndices[kce] = i ;
	  colCoeffs[kce] = coeff ;
	  colLengths[j]++ ;
	}
      }
      presolve_delete_from_row(i,tgtcol,
      			       rowStarts,rowLengths,colIndices,rowCoeffs) ;
#     if PRESOLVE_DEBUG > 1
      kre-- ;
      std::cout
	<< "  subst fin (" << i << ") (" << rowLengths[i] << "): " ;
      for (CoinBigIndex krow = krs ; krow < kre ; ++krow) {
	const int j = colIndices[krow] ;
	const double aij = rowCoeffs[krow] ;
	std::cout
	  << "x(" << j << ") = " << aij << " (" << colLengths[j] << ") " ;
      }
      std::cout << std::endl ;
#     endif
      
    }
/*
  End of the substitution loop.

  Record the column indices of the target row so we can groom these columns
  later to remove possible explicit zeros.
*/
    CoinMemcpyN(&colIndices[rowStarts[tgtrow]],rowLengths[tgtrow],
    		&zerocols[nzerocols]) ;
    nzerocols += rowLengths[tgtrow] ;
/*
  Remove the target equality from the column- and row-major representations
  Somewhat painful in the colum-major representation.  We have to walk the
  target row in the row-major representation and look up each coefficient
  in the column-major representation.
*/
    for (CoinBigIndex krow = tgtrs ; krow < tgtre ; ++krow) {
      const int j = colIndices[krow] ;
#     if PRESOLVE_DEBUG > 1
      std::cout
        << "  removing row " << tgtrow << " from col " << j << std::endl ;
#     endif
      presolve_delete_from_col(tgtrow,j,
      			       colStarts,colLengths,rowIndices,colCoeffs) ;
      if (colLengths[j] == 0) {
        PRESOLVE_REMOVE_LINK(clink,j) ;
      }
    }
/*
  Finally, physically remove the column from the column-major representation
  and the row from the row-major representation.
*/
    PRESOLVE_REMOVE_LINK(clink, tgtcol) ;
    colLengths[tgtcol] = 0 ;

    PRESOLVE_REMOVE_LINK(rlink, tgtrow) ;
    rowLengths[tgtrow] = 0 ;

    rlo[tgtrow] = 0.0 ;
    rup[tgtrow] = 0.0 ;

#   if PRESOLVE_CONSISTENCY > 0
    presolve_links_ok(prob) ;
    presolve_consistent(prob) ;
#   endif

  }
/*
  That's it, we've processed all the candidate pairs.

  Clear the row used flags.
*/
  for (int i = 0 ; i < nRowsUsed ; i++) prob->unsetRowUsed(rowsUsed[i]) ;
/*
  Trim the array of substitution transforms and queue up objects for postsolve.
  Also groom the problem representation to remove explicit zeros.
*/
  if (nactions) {
#   if PRESOLVE_SUMMARY > 0
    std::cout << "NSUBSTS: " << nactions << std::endl ;
#   endif
    next = new subst_constraint_action(nactions,
				   CoinCopyOfArray(actions,nactions),next) ;
    next = drop_zero_coefficients_action::presolve(prob,zerocols,
    						   nzerocols, next) ;
#   if PRESOLVE_CONSISTENCY > 0
    presolve_links_ok(prob) ;
    presolve_consistent(prob) ;
#   endif
  }

  deleteAction(actions,action*) ;
  delete [] x_to_y ;
  delete [] zerocols ;

# if COIN_PRESOLVE_TUNING > 0
  if (prob->tuning_) double thisTime = CoinCpuTime() ;
# endif
# if PRESOLVE_CONSISTENCY > 0 || PRESOLVE_DEBUG > 0
  presolve_check_sol(prob) ;
# endif
# if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING > 0
  int droppedRows = prob->countEmptyRows()-startEmptyRows ;
  int droppedColumns = prob->countEmptyCols()-startEmptyColumns ;
  std::cout
    << "Leaving subst_constraint_action::presolve, "
    << droppedRows << " rows, " << droppedColumns << " columns dropped" ;
# if COIN_PRESOLVE_TUNING > 0
  std::cout << " in " << thisTime-startTime << "s" ;
# endif
  std::cout << "." << std::endl ;
# endif

  return (next) ;
}
/*
 * It is always the case that one of the variables of a doubleton
 * will be (implied) free, but neither will necessarily be a singleton.
 * Since in the case of a doubleton the number of non-zero entries
 * will never increase, though, it makes sense to always eliminate them.
 *
 * The col rep and row rep must be consistent.
 */
const CoinPresolveAction
  *doubleton_action::presolve (CoinPresolveMatrix *prob,
			      const CoinPresolveAction *next)

{
  double startTime = 0.0;
  int startEmptyRows=0;
  int startEmptyColumns = 0;
  if (prob->tuning_) {
    startTime = CoinCpuTime();
    startEmptyRows = prob->countEmptyRows();
    startEmptyColumns = prob->countEmptyCols();
  }
  double *colels	= prob->colels_;
  int *hrow		= prob->hrow_;
  CoinBigIndex *mcstrt	= prob->mcstrt_;
  int *hincol		= prob->hincol_;
  int ncols		= prob->ncols_;

  double *clo	= prob->clo_;
  double *cup	= prob->cup_;

  double *rowels	= prob->rowels_;
  int *hcol		= prob->hcol_;
  CoinBigIndex *mrstrt	= prob->mrstrt_;
  int *hinrow		= prob->hinrow_;
  int nrows		= prob->nrows_;

  double *rlo	= prob->rlo_;
  double *rup	= prob->rup_;

  presolvehlink *clink = prob->clink_;
  presolvehlink *rlink = prob->rlink_;

  const unsigned char *integerType = prob->integerType_;

  double *cost	= prob->cost_;

  int numberLook = prob->numberRowsToDo_;
  int iLook;
  int * look = prob->rowsToDo_;
  const double ztolzb	= prob->ztolzb_;

  action * actions = new action [nrows];
  int nactions = 0;

  int *zeros	= prob->usefulColumnInt_; //new int[ncols];
  int nzeros	= 0;

  int *fixed	= zeros+ncols; //new int[ncols];
  int nfixed	= 0;

  unsigned char *rowstat = prob->rowstat_;
  double *acts	= prob->acts_;
  double * sol = prob->sol_;

  bool fixInfeasibility = (prob->presolveOptions_&16384)!=0;
# if PRESOLVE_CONSISTENCY
  presolve_consistent(prob) ;
  presolve_links_ok(prob) ;
# endif

  // wasfor (int irow=0; irow<nrows; irow++)
  for (iLook=0;iLook<numberLook;iLook++) {
    int irow = look[iLook];
    if (hinrow[irow] == 2 &&
	fabs(rup[irow] - rlo[irow]) <= ZTOLDP) {
      double rhs = rlo[irow];
      CoinBigIndex krs = mrstrt[irow];
      int icolx, icoly;
      CoinBigIndex k;
      
      icolx = hcol[krs];
      icoly = hcol[krs+1];
      if (hincol[icolx]<=0||hincol[icoly]<=0) {
        // should never happen ?
        //printf("JJF - doubleton column %d has %d entries and %d has %d\n",
        //     icolx,hincol[icolx],icoly,hincol[icoly]);
        continue;
      }
      // check size
      if (fabs(rowels[krs]) < ZTOLDP2 || fabs(rowels[krs+1]) < ZTOLDP2)
	continue;
      // See if prohibited for any reason
      if (prob->colProhibited(icolx) || prob->colProhibited(icolx))
	continue;
      
      // don't bother with fixed variables
      if (!(fabs(cup[icolx] - clo[icolx]) < ZTOLDP) &&
	  !(fabs(cup[icoly] - clo[icoly]) < ZTOLDP)) {
	double coeffx, coeffy;
	/* find this row in each of the columns */
	CoinBigIndex krowx = presolve_find_row(irow, mcstrt[icolx], mcstrt[icolx] + hincol[icolx], hrow);
	CoinBigIndex krowy = presolve_find_row(irow, mcstrt[icoly], mcstrt[icoly] + hincol[icoly], hrow);

/*
  Check for integrality: If one variable is integer, keep it and substitute
  for the continuous variable. If both are integer, substitute only for the
  forms x = k * y (k integral and non-empty intersection on bounds on x)
  or x = 1-y, where both x and y are binary.

  flag bits for integerStatus: 1>>0	x integer
			       1>>1	y integer
*/
	int integerStatus=0;
	if (integerType[icolx]) {
	  if (integerType[icoly]) {
	    // both integer
	    int good = 0;
	    double rhs2 = rhs;
	    double value;
	    value=colels[krowx];
	    if (value<0.0) {
	      value = - value;
	      rhs2 += 1;
	    }
	    if (cup[icolx]==1.0&&clo[icolx]==0.0&&fabs(value-1.0)<1.0e-7)
	      good =1;
	    value=colels[krowy];
	    if (value<0.0) {
	      value = - value;
	      rhs2 += 1;
	    }
	    if (cup[icoly]==1.0&&clo[icoly]==0.0&&fabs(value-1.0)<1.0e-7)
	      good  |= 2;
	    if (good==3&&fabs(rhs2-1.0)<1.0e-7)
	      integerStatus = 3;
	    else
	      integerStatus=-1;
	    if (integerStatus==-1&&!rhs) {
	      // maybe x = k * y;
	      double value1 = colels[krowx];
	      double value2 = colels[krowy];
	      double ratio;
	      bool swap=false;
	      if (fabs(value1)>fabs(value2)) {
		ratio = value1/value2;
	      } else {
		ratio = value2/value1;
		swap=true;
	      }
	      ratio=fabs(ratio);
	      if (fabs(ratio-floor(ratio+0.5))<1.0e-12) {
		// possible
		integerStatus = swap ? 2 : 1;
		//printf("poss type %d\n",integerStatus);
	      }
	    }
	  } else {
	    integerStatus = 1;
	  }
	} else if (integerType[icoly]) {
	  integerStatus = 2;
	}
	if (integerStatus<0) {
	  // can still take in some cases
	  bool canDo=false;
	  double value1 = colels[krowx];
	  double value2 = colels[krowy];
	  double ratio;
	  bool swap=false;
	  double rhsRatio;
	  if (fabs(value1)>fabs(value2)) {
	    ratio = value1/value2;
	    rhsRatio = rhs/value1;
	  } else {
	    ratio = value2/value1;
	    rhsRatio = rhs/value2;
	    swap=true;
	  }
	  ratio=fabs(ratio);
	  if (fabs(ratio-floor(ratio+0.5))<1.0e-12) {
	    // possible
	    integerStatus = swap ? 2 : 1;
	    // but check rhs
	    if (rhsRatio==floor(rhsRatio+0.5))
	      canDo=true;
	  }
#ifdef COIN_DEVELOP2
	  if (canDo)
	    printf("Good CoinPresolveDoubleton icolx %d (%g and bounds %g %g) icoly %d (%g and bound %g %g) - rhs %g\n",
		   icolx,colels[krowx],clo[icolx],cup[icolx],
		   icoly,colels[krowy],clo[icoly],cup[icoly],rhs);
	  else
	  printf("Bad CoinPresolveDoubleton icolx %d (%g) icoly %d (%g) - rhs %g\n",
		 icolx,colels[krowx],icoly,colels[krowy],rhs);
#endif
	  if (!canDo)
	  continue;
	}
	if (integerStatus == 2) {
	  CoinSwap(icoly,icolx);
	  CoinSwap(krowy,krowx);
	}

	// HAVE TO JIB WITH ABOVE swapS
	// if x's coefficient is something like 1000, but y's only something like -1,
	// then when we postsolve, if x's is close to being out of tolerance,
	// then y is very likely to be (because y==1000x) . (55)
	// It it interesting that the number of doubletons found may depend
	// on which column is substituted away (this is true of baxter.mps).
	if (!integerStatus) {
	  if (fabs(colels[krowy]) < fabs(colels[krowx])) {
	    CoinSwap(icoly,icolx);
	    CoinSwap(krowy,krowx);
	  }
	}

#if 0
	//?????
	if (integerType[icolx] &&
	    clo[icoly] != -PRESOLVE_INF &&
	    cup[icoly] != PRESOLVE_INF) {
	  continue;
	}
#endif

	{
	  CoinBigIndex kcs = mcstrt[icoly];
	  CoinBigIndex kce = kcs + hincol[icoly];
	  for (k=kcs; k<kce; k++) {
	    if (hinrow[hrow[k]] == 1) {
	      break;
	    }
	  }
	  // let singleton rows be taken care of first
	  if (k<kce)
	    continue;
	}

	coeffx = colels[krowx];
	coeffy = colels[krowy];

	// it is possible that both x and y are singleton columns
	// that can cause problems
	if (hincol[icolx] == 1 && hincol[icoly] == 1)
	  continue;

	// BE CAUTIOUS and avoid very large relative differences
	// if this is not done in baxter, then the computed solution isn't optimal,
	// but gets it in 11995 iterations; the postsolve goes to iteration 16181.
	// with this, the solution is optimal, but takes 18825 iters; postsolve 18871.
#if 0
	if (fabs(coeffx) * max_coeff_factor <= fabs(coeffy))
	  continue;
#endif

#if 0
	if (only_zero_rhs && rhs != 0)
	  continue;

	if (reject_doubleton(mcstrt, colels, hrow, hincol,
			     -coeffx / coeffy,
			     max_coeff_ratio,
			     irow, icolx, icoly))
	  continue;
#endif

	// common equations are of the form ax + by = 0, or x + y >= lo
	{
	  PRESOLVE_DETAIL_PRINT(printf("pre_doubleton %dC %dC %dR E\n",
				       icoly,icolx,irow));
	  action *s = &actions[nactions];	  
	  nactions++;
	  
	  s->row = irow;
	  s->icolx = icolx;
	  
	  s->clox = clo[icolx];
	  s->cupx = cup[icolx];
	  s->costx = cost[icolx];
	  
	  s->icoly = icoly;
	  s->costy = cost[icoly];
	  
	  s->rlo = rlo[irow];
	  
	  s->coeffx = coeffx;
	  
	  s->coeffy = coeffy;
	  
	  s->ncolx	= hincol[icolx];
	  
	  s->ncoly	= hincol[icoly];
	  if (s->ncoly<s->ncolx) {
	    // Take out row 
	    s->colel	= presolve_dupmajor(colels,hrow,hincol[icoly],
					    mcstrt[icoly],irow) ;
	    s->ncolx=0;
	  } else {
	    s->colel	= presolve_dupmajor(colels,hrow,hincol[icolx],
					    mcstrt[icolx],irow) ;
	    s->ncoly=0;
	  }
	}

	/*
	 * This moves the bounds information for y onto x,
	 * making y free and allowing us to substitute it away.
	 *
	 * a x + b y = c
	 * l1 <= x <= u1
	 * l2 <= y <= u2	==>
	 *
	 * l2 <= (c - a x) / b <= u2
	 * b/-a > 0 ==> (b l2 - c) / -a <= x <= (b u2 - c) / -a
	 * b/-a < 0 ==> (b u2 - c) / -a <= x <= (b l2 - c) / -a
	 */
	{
	  double lo1 = -PRESOLVE_INF;
	  double up1 = PRESOLVE_INF;
	  
	  //PRESOLVEASSERT((coeffx < 0) == (coeffy/-coeffx < 0));
	  // (coeffy/-coeffx < 0) == (coeffy<0 == coeffx<0) 
	  if (-PRESOLVE_INF < clo[icoly]) {
	    if (coeffx * coeffy < 0)
	      lo1 = (coeffy * clo[icoly] - rhs) / -coeffx;
	    else 
	      up1 = (coeffy * clo[icoly] - rhs) / -coeffx;
	  }
	  
	  if (cup[icoly] < PRESOLVE_INF) {
	    if (coeffx * coeffy < 0)
	      up1 = (coeffy * cup[icoly] - rhs) / -coeffx;
	    else 
	      lo1 = (coeffy * cup[icoly] - rhs) / -coeffx;
	  }
	  
	  // costy y = costy ((c - a x) / b) = (costy c)/b + x (costy -a)/b
	  // the effect of maxmin cancels out
	  cost[icolx] += cost[icoly] * (-coeffx / coeffy);
	  
	  prob->change_bias(cost[icoly] * rhs / coeffy);
	  
	  if (0    /*integerType[icolx]*/) {
	    abort();
	    /* no change possible for now */
#if 0
	    lo1 = trunc(lo1);
	    up1 = trunc(up1);
	    
	    /* trunc(3.5) == 3.0 */
	    /* trunc(-3.5) == -3.0 */
	    
	    /* I think this is ok */
	    if (lo1 > clo[icolx]) {
	      (clo[icolx] <= 0.0)
		clo[icolx] =  ? ilo

		clo[icolx] = ilo;
	      cup[icolx] = iup;
	    }
#endif
	  } else {
Esempio n. 8
0
const CoinPresolveAction
    *forcing_constraint_action::presolve(CoinPresolveMatrix *prob,
					 const CoinPresolveAction *next)
{
  double startTime = 0.0;
  int startEmptyRows=0;
  int startEmptyColumns = 0;
  if (prob->tuning_) {
    startTime = CoinCpuTime();
    startEmptyRows = prob->countEmptyRows();
    startEmptyColumns = prob->countEmptyCols();
  }
  double *clo	= prob->clo_;
  double *cup	= prob->cup_;
  double *csol  = prob->sol_ ;

  const double *rowels	= prob->rowels_;
  const int *hcol	= prob->hcol_;
  const CoinBigIndex *mrstrt	= prob->mrstrt_;
  const int *hinrow	= prob->hinrow_;
  const int nrows	= prob->nrows_;

  const double *rlo	= prob->rlo_;
  const double *rup	= prob->rup_;

  //  const char *integerType = prob->integerType_;

  const double tol	= ZTOLDP;
  const double inftol	= prob->feasibilityTolerance_;
  const int ncols	= prob->ncols_;

  int *fixed_cols	= new int[ncols];
  int nfixed_cols	= 0;

  action *actions	= new action [nrows];
  int nactions = 0;

  int *useless_rows	= new int[nrows];
  int nuseless_rows	= 0;

  int numberLook = prob->numberRowsToDo_;
  int iLook;
  int * look = prob->rowsToDo_;
  bool fixInfeasibility = (prob->presolveOptions_&16384)!=0;

# if PRESOLVE_DEBUG
  std::cout << "Entering forcing_constraint_action::presolve." << std::endl ;
  presolve_check_sol(prob) ;
# endif
/*
  Open a loop to scan the constraints of interest. There must be variables
  left in the row.
*/
  for (iLook=0;iLook<numberLook;iLook++) {
    int irow = look[iLook];
    if (hinrow[irow] > 0) {
      CoinBigIndex krs = mrstrt[irow];
      CoinBigIndex kre = krs + hinrow[irow];
/*
  Calculate upper and lower bounds on the row activity based on upper and lower
  bounds on the variables. If these are finite and incompatible with the given
  row bounds, we have infeasibility.
*/
      double maxup, maxdown;
      implied_row_bounds(rowels, clo, cup, hcol, krs, kre,
			 &maxup, &maxdown);

      if (maxup < PRESOLVE_INF && maxup + inftol < rlo[irow]&&!fixInfeasibility) {
	/* max row activity below the row lower bound */
	prob->status_|= 1;
	prob->messageHandler()->message(COIN_PRESOLVE_ROWINFEAS,
					     prob->messages())
				 	       <<irow
					       <<rlo[irow]
					       <<rup[irow]
					       <<CoinMessageEol;
	break;
      } else if (-PRESOLVE_INF < maxdown && rup[irow] < maxdown - inftol&&!fixInfeasibility) {
	/* min row activity above the row upper bound */
	prob->status_|= 1;
	prob->messageHandler()->message(COIN_PRESOLVE_ROWINFEAS,
					     prob->messages())
					       <<irow
					       <<rlo[irow]
					       <<rup[irow]
					       <<CoinMessageEol;
	break;
      }
      // ADD TOLERANCE TO THESE TESTS
      else if ((rlo[irow] <= -PRESOLVE_INF ||
		(-PRESOLVE_INF < maxdown && rlo[irow] <= maxdown)) &&
	       (rup[irow] >= PRESOLVE_INF ||
		(maxup < PRESOLVE_INF && rup[irow] >= maxup))) {

/*
  Original comment: I'm not sure that these transforms don't intefere with
		    each other. We can get it next time.

  Well, I'll argue that bounds are never really loosened (at worst, they're
  transferred onto some other variable, or inferred to be unnecessary.
  Once useless, always useless. Leaving this hook in place allows for a sort
  of infinite loop where this routine keeps queuing the same constraints over
  and over.  -- lh, 040901 --

	 if (some_col_was_fixed(hcol, krs, kre, clo, cup)) {
	   prob->addRow(irow);
	   continue;
	 }
*/

	// this constraint must always be satisfied - drop it
	useless_rows[nuseless_rows++] = irow;

      } else if ((maxup < PRESOLVE_INF && fabs(rlo[irow] - maxup) < tol) ||
		 (-PRESOLVE_INF < maxdown && fabs(rup[irow] - maxdown) < tol)) {

	// the lower bound can just be reached, or
	// the upper bound can just be reached;
	// called a "forcing constraint" in the paper (p. 226)
	const int lbound_tight = (maxup < PRESOLVE_INF &&
				  fabs(rlo[irow] - maxup) < tol);

/*
  Original comment and rebuttal as above.
	if (some_col_was_fixed(hcol, krs, kre, clo, cup)) {
	  // make sure on next time
	  prob->addRow(irow);
	  continue;
	}
*/
	// out of space - this probably never happens (but this routine will
	// often put duplicates in the fixed column list)
	if (nfixed_cols + (kre-krs) >= ncols)
	  break;

	double *bounds = new double[hinrow[irow]];
	int *rowcols = new int[hinrow[irow]];
	int lk = krs;	// load fix-to-down in front
	int uk = kre;	// load fix-to-up in back
        CoinBigIndex k;
	for ( k=krs; k<kre; k++) {
	  int jcol = hcol[k];
	  prob->addCol(jcol);
	  double coeff = rowels[k];

	  PRESOLVEASSERT(fabs(coeff) > ZTOLDP);

	  // one of the two contributed to maxup - set the other to that
	  if (lbound_tight == (coeff > 0.0)) {
	    --uk;
	    bounds[uk-krs] = clo[jcol];
	    rowcols[uk-krs] = jcol;
	    if (csol != 0) {
	      csol[jcol] = cup[jcol] ;
	    }
	    clo[jcol] = cup[jcol];
	  } else {
	    bounds[lk-krs] = cup[jcol];
	    rowcols[lk-krs] = jcol;
	    ++lk;
	    if (csol != 0) {
	      csol[jcol] = clo[jcol] ;
	    }
	    cup[jcol] = clo[jcol];
	  }

	  fixed_cols[nfixed_cols++] = jcol;
	}
	PRESOLVEASSERT(uk == lk);

	action *f = &actions[nactions];
	nactions++;
	PRESOLVE_DETAIL_PRINT(printf("pre_forcing %dR E\n",irow));

	f->row = irow;
	f->nlo = lk-krs;
	f->nup = kre-uk;
	f->rowcols = rowcols;
	f->bounds = bounds;
      }
    }
  }


  if (nactions) {
#if	PRESOLVE_SUMMARY
    printf("NFORCED:  %d\n", nactions);
#endif
    next = new forcing_constraint_action(nactions, 
					 CoinCopyOfArray(actions,nactions), next);
  }
  deleteAction(actions,action*);
  if (nuseless_rows) {
    next = useless_constraint_action::presolve(prob,
					       useless_rows, nuseless_rows,
					       next);
  }
  delete[]useless_rows;
/*
  We need to remove duplicates here, or we get into trouble in
  remove_fixed_action::postsolve when we try to reinstate a column multiple
  times.
*/
  if (nfixed_cols)
  { if (nfixed_cols > 1)
    { std::sort(fixed_cols,fixed_cols+nfixed_cols) ;
      int *end = std::unique(fixed_cols,fixed_cols+nfixed_cols) ;
      nfixed_cols = static_cast<int>(end-fixed_cols) ; }
    next = remove_fixed_action::presolve(prob,fixed_cols,nfixed_cols,next) ; }
  delete[]fixed_cols ;

  if (prob->tuning_) {
    double thisTime=CoinCpuTime();
    int droppedRows = prob->countEmptyRows() - startEmptyRows ;
    int droppedColumns =  prob->countEmptyCols() - startEmptyColumns;
    printf("CoinPresolveForcing(32) - %d rows, %d columns dropped in time %g, total %g\n",
	   droppedRows,droppedColumns,thisTime-startTime,thisTime-prob->startTime_);
  }

# if PRESOLVE_DEBUG
  presolve_check_sol(prob) ;
  std::cout << "Leaving forcing_constraint_action::presolve." << std::endl ;
# endif

  return (next);
}