コード例 #1
0
int pushCbcOsiLogLevel (CoinParam *param)

{
    assert (param != 0) ;
    CbcOsiParam *osiParam = dynamic_cast<CbcOsiParam *>(param) ;
    assert (osiParam != 0) ;
    OsiSolverInterface *osi = osiParam->obj() ;
    assert(osi != 0) ;

    int lvl = param->intVal() ;
    /*
      Setup to return nonfatal/fatal error (1/-1) by default, so that all we need
      to do is correct to 0 (no error) if we're successful.
    */
    int retval ;
    if (CoinParamUtils::isInteractive()) {
        retval = 1 ;
    } else {
        retval = -1 ;
    }
    /*
      Now try to do the right thing with a hint. Harder to say -- assume that log
      level 1 is `normal'.
    */
    OsiHintStrength strength ;
    bool sense ;
    if (lvl < 1) {
        strength = OsiHintDo ;
        sense = true ;
    } else if (lvl == 1) {
        strength = OsiHintIgnore ;
        sense = true ;
    } else if (lvl == 2) {
        strength = OsiHintTry ;
        sense = false ;
    } else {
        strength = OsiHintDo ;
        sense = false ;
    }

    bool setOK = osi->setHintParam(OsiDoReducePrint, sense, strength) ;

    /*
      Recover the message handler and set the log level directly.
    */
    CoinMessageHandler *hndl = osi->messageHandler() ;
    assert (hndl != 0) ;
    hndl->setLogLevel(lvl) ;

    if (setOK) {
        return (0) ;
    } else {
        return (retval) ;
    }
}
コード例 #2
0
/*
  Original comments from 040916:

  This routine looks to be something of a work in progress.  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.
  l<j> = -infty is uncommon; perhaps it's not worth the effort?

  Why exclude the code protected by PRESOLVE_TIGHTEN_DUALS? Why are we
  using ekkinf instead of PRESOLVE_INF?
*/
const CoinPresolveAction
  *remove_dual_action::presolve (CoinPresolveMatrix *prob,
				 const CoinPresolveAction *next)
{
# if PRESOLVE_DEBUG > 0
  std::cout
    << "Entering remove_dual_action::presolve, " << prob->nrows_ 
    << "x" << prob->ncols_ << "." << std::endl ;
# endif
# if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0
  presolve_check_sol(prob,2,1,1) ;
  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

  // column-major representation
  const int ncols = prob->ncols_ ;
  const CoinBigIndex *const mcstrt = prob->mcstrt_ ;
  const int *const hincol = prob->hincol_ ;
  const int *const hrow = prob->hrow_ ;
  const double *const colels = prob->colels_ ;
  const double *const cost = prob->cost_ ;

  // column type, bounds, solution, and status
  const unsigned char *const integerType = prob->integerType_ ;
  const double *const clo = prob->clo_ ;
  const double *const cup = prob->cup_ ;
  double *const csol = prob->sol_ ;
  unsigned char *&colstat = prob->colstat_ ;

  // row-major representation
  const int nrows = prob->nrows_ ;
  const CoinBigIndex *const mrstrt = prob->mrstrt_ ;
  const int *const hinrow = prob->hinrow_ ;
  const int *const hcol = prob->hcol_ ;
# if REMOVE_DUAL_ACTION_REPAIR_SOLN > 0
  const double *const rowels = prob->rowels_ ;
# endif

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

  // tolerances
  const double ekkinf2 = PRESOLVE_SMALL_INF ;
  const double ekkinf = ekkinf2*1.0e8 ;
  const double ztolcbarj = prob->ztoldj_ ;
  const CoinRelFltEq relEq(prob->ztolzb_) ;

/*
  Grab one of the preallocated scratch arrays to hold min and max values of
  row duals.
*/
  double *ymin	= prob->usefulRowDouble_ ;
  double *ymax	= ymin+nrows ;
/*
  Initialise row dual min/max. The defaults are +/- infty, but if we know
  that the logical has no upper (lower) bound, it can only be nonbasic at
  the other bound. Given minimisation, we can conclude:
    * <= constraint ==> [0,infty] ==> NBLB ==> cbar<i> > 0 ==> y<i> < 0
    * >= constraint ==> [-infty,0] ==> NBUB ==> cbar<i> < 0 ==> y<i> > 0
  Range constraints are not helpful here, the dual can be either sign. There's
  no calculation because we assume the objective coefficient c<i> = 0  and the
  coefficient a<ii> = 1.0, hence cbar<i> = -y<i>.
*/
  for (int i = 0; i < nrows; i++) {
    const bool no_lb = (rup[i] >= ekkinf) ;
    const bool no_ub = (rlo[i] <= -ekkinf) ;

    ymin[i] = ((no_lb && !no_ub)?0.0:(-PRESOLVE_INF)) ;
    ymax[i] = ((no_ub && !no_lb)?0.0:PRESOLVE_INF) ;
  }
/*
  We can do a similar calculation with singleton columns where the variable
  has only one finite bound, but we have to work a bit harder, as the cost
  coefficient c<j> is not necessarily 0.0 and a<ij> is not necessarily 1.0.

  cbar<j> = c<j> - y<i>a<ij>, hence y<i> = c<j>/a<ij> at cbar<j> = 0. The
  question is whether this is an upper or lower bound on y<i>.
    * x<j> NBLB ==> cbar<j> >= 0
      a<ij> > 0 ==> increasing y<i> decreases cbar<j> ==> upper bound 
      a<ij> < 0 ==> increasing y<i> increases cbar<j> ==> lower bound 
    * x<j> NBUB ==> cbar<j> <= 0
      a<ij> > 0 ==> increasing y<i> decreases cbar<j> ==> lower bound 
      a<ij> < 0 ==> increasing y<i> increases cbar<j> ==> upper bound 
  The condition below (simple test for equality to choose the bound) looks
  a bit odd, but a bit of boolean algebra should convince you it's correct.
  We have a bound; the only question is whether it's upper or lower.

  Skip integer variables; it's far too likely that we'll tighten infinite
  bounds elsewhere in presolve.

  NOTE: If bound propagation is applied to continuous variables, the same
  	hazard will apply.  -- lh, 110611 --
*/
  for (int j = 0 ; j < ncols ; j++) {
    if (integerType[j]) continue ;
    if (hincol[j] != 1) continue ;
    const bool no_ub = (cup[j] >= ekkinf) ;
    const bool no_lb = (clo[j] <= -ekkinf) ;
    if (no_ub != no_lb) {
      const int &i = hrow[mcstrt[j]] ;
      double aij = colels[mcstrt[j]] ;
      PRESOLVEASSERT(fabs(aij) > ZTOLDP) ;
      const double yzero = cost[j]/aij ;
      if ((aij > 0.0) == no_ub) {
	if (ymax[i] > yzero)
	  ymax[i] = yzero ;
      } else {
	if (ymin[i] < yzero)
	  ymin[i] = yzero ;
      }
    }
  }
  int nfixup_cols = 0 ;
  int nfixdown_cols = ncols ;
  // Grab another work array, sized to ncols_
  int *fix_cols	= prob->usefulColumnInt_ ;
# if PRESOLVE_TIGHTEN_DUALS > 0
  double *cbarmin = new double[ncols] ;
  double *cbarmax = new double[ncols] ;
# endif
/*
  Now we have (admittedly weak) bounds on the dual for each row. We can use
  these to calculate upper and lower bounds on cbar<j>. Open loops to
  take multiple passes over all columns.
*/
  int nPass = 0 ;
  while (nPass++ < 100) {
    int tightened = 0 ;
    for (int j = 0 ; j < ncols ; j++) {
      if (hincol[j] <= 0) continue ;
/*
  Calculate min cbar<j> and max cbar<j> for the column by calculating the
  contribution to c<j>-ya<j> using ymin<i> and ymax<i> from above.
*/
      const CoinBigIndex &kcs = mcstrt[j] ;
      const 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 cbarjmin = cost[j] ;
      double cbarjmax = cbarjmin ;
      for (CoinBigIndex k = kcs ; k < kce ; k++) {
	const int &i = hrow[k] ;
	const double &aij = colels[k] ;
	const double mindelta = aij*ymin[i] ;
	const double maxdelta = aij*ymax[i] ;
	
	if (aij > 0.0) {
	  if (ymin[i] >= -ekkinf2) {
	    cbarjmax -= mindelta ;
	    nordu++ ;
	  } else {
	    nflagu++ ;
	  }
	  if (ymax[i] <= ekkinf2) {
	    cbarjmin -= maxdelta ;
	    nordl++ ;
	  } else {
	    nflagl++ ;
	  }
	} else {
	  if (ymax[i] <= ekkinf2) {
	    cbarjmax -= maxdelta ;
	    nordu++ ;
	  } else {
	    nflagu++ ;
	  }
	  if (ymin[i] >= -ekkinf2) {
	    cbarjmin -= mindelta ;
	    nordl++ ;
	  } else {
	    nflagl++ ;
	  }
	}
      }
/*
  See if we can tighten bounds on a dual y<t>. See the comments at the head of
  the file for the linear algebra.

  At this point, I don't understand the restrictions on propagation. Neither
  are necessary. The net effect is to severely restrict the circumstances
  where we'll propagate. Requiring nflagu == 1 excludes the case where all
  duals have finite bounds (unlikely?). And why cbarjmax < -ztolcbarj?

  In any event, we're looking for the row that's contributing the infinity. If
  a<tj> > 0, the contribution is due to ymin<t> and we tighten ymax<t>;
  a<tj> < 0, ymax<t> and ymin<t>, respectively.

  The requirement cbarjmax < (ymax[i]*aij-ztolcbarj) ensures we don't propagate
  tiny changes. If we make a change, it will affect cbarjmin. Make the
  adjustment immediately.

  Continuous variables only.
*/
      if (!integerType[j]) {
	if (cup[j] > ekkinf) {
	  if (nflagu == 1 && cbarjmax < -ztolcbarj) {
	    for (CoinBigIndex k = kcs ; k < kce; k++) {
	      const int i = hrow[k] ;
	      const double aij = colels[k] ;
	      if (aij > 0.0 && ymin[i] < -ekkinf2) {
		if (cbarjmax < (ymax[i]*aij-ztolcbarj)) {
		  const double newValue = cbarjmax/aij ;
		  if (ymax[i] > ekkinf2 && newValue <= ekkinf2) {
		    nflagl-- ;
		    cbarjmin -= aij*newValue ;
		  } else if (ymax[i] <= ekkinf2) {
		    cbarjmin -= aij*(newValue-ymax[i]) ;
		  }
#		  if PRESOLVE_DEBUG > 1
		  std::cout
		    << "NDUAL(infu/inf): u(" << j << ") = " << cup[j]
		    << ": max y(" << i << ") was " << ymax[i]
		    << " now " << newValue << "." << std::endl ;
#		  endif
		  ymax[i] = newValue ;
		  tightened++ ;
		}
	      } else if (aij < 0.0 && ymax[i] > ekkinf2) {
		if (cbarjmax < (ymin[i]*aij-ztolcbarj)) {
		  const double newValue = cbarjmax/aij ;
		  if (ymin[i] < -ekkinf2 && newValue >= -ekkinf2) {
		    nflagl-- ;
		    cbarjmin -= aij*newValue ;
		  } else if (ymin[i] >= -ekkinf2) {
		    cbarjmin -= aij*(newValue-ymin[i]) ;
		  }
#		  if PRESOLVE_DEBUG > 1
		  std::cout
		    << "NDUAL(infu/inf): u(" << j << ") = " << cup[j]
		    << ": min y(" << i << ") was " << ymin[i]
		    << " now " << newValue << "." << std::endl ;
#		  endif
		  ymin[i] = newValue ;
		  tightened++ ;
		  // Huh? asymmetric 
		  // cbarjmin = 0.0 ;
		}
	      }
	    }
	  } else if (nflagl == 0 && nordl == 1 && cbarjmin < -ztolcbarj) {
/*
  This is a column singleton. Why are we doing this? It's not like changes
  to other y will affect this.
*/
	    for (CoinBigIndex k = kcs; k < kce; k++) {
	      const int i = hrow[k] ;
	      const double aij = colels[k] ;
	      if (aij > 0.0) {
#		if PRESOLVE_DEBUG > 1
		std::cout
		  << "NDUAL(infu/sing): u(" << j << ") = " << cup[j]
		  << ": max y(" << i << ") was " << ymax[i]
		  << " now " << ymax[i]+cbarjmin/aij << "." << std::endl ;
#		endif
		ymax[i] += cbarjmin/aij ;
		cbarjmin = 0.0 ;
		tightened++ ;
	      } else if (aij < 0.0 ) {
#		if PRESOLVE_DEBUG > 1
		std::cout
		  << "NDUAL(infu/sing): u(" << j << ") = " << cup[j]
		  << ": min y(" << i << ") was " << ymin[i]
		  << " now " << ymin[i]+cbarjmin/aij << "." << std::endl ;
#		endif
		ymin[i] += cbarjmin/aij ;
		cbarjmin = 0.0 ;
		tightened++ ;
	      }
	    }
	  }
	}   // end u<j> = infty
#       if PROCESS_INFINITE_LB
/*
  Unclear why this section is commented out, except for the possibility that
  bounds of -infty < x < something are rare and it likely suffered fromm the
  same errors. Consider the likelihood that this whole block needs to be
  edited to sway min/max, & similar.
*/
	if (clo[j]<-ekkinf) {
	  // cbarj can not be positive
	  if (cbarjmin > ztolcbarj&&nflagl == 1) {
	    // We can make bound finite one way
	    for (CoinBigIndex k = kcs; k < kce; k++) {
	      const int i = hrow[k] ;
	      const double coeff = colels[k] ;
	      
	      if (coeff < 0.0&&ymin[i] < -ekkinf2) {
		// ymax[i] has upper bound
		if (cbarjmin>ymax[i]*coeff+ztolcbarj) {
		  const double newValue = cbarjmin/coeff ;
		  // re-compute hi
		  if (ymax[i] > ekkinf2 && newValue <= ekkinf2) {
		    nflagu-- ;
		    cbarjmax -= coeff * newValue ;
		  } else if (ymax[i] <= ekkinf2) {
		    cbarjmax -= coeff * (newValue-ymax[i]) ;
		  }
		  ymax[i] = newValue ;
		  tightened++ ;
#		  if PRESOLVE_DEBUG > 1
		  printf("Col %d, row %d max pi now %g\n",j,i,ymax[i]) ;
#		  endif
		}
	      } else if (coeff > 0.0 && ymax[i] > ekkinf2) {
		// ymin[i] has lower bound
		if (cbarjmin>ymin[i]*coeff+ztolcbarj) {
		  const double newValue = cbarjmin/coeff ;
		  // re-compute lo
		  if (ymin[i] < -ekkinf2 && newValue >= -ekkinf2) {
		    nflagu-- ;
		    cbarjmax -= coeff * newValue ;
		  } else if (ymin[i] >= -ekkinf2) {
		    cbarjmax -= coeff*(newValue-ymin[i]) ;
		  }
		  ymin[i] = newValue ;
		  tightened++ ;
#		  if PRESOLVE_DEBUG > 1
		  printf("Col %d, row %d min pi now %g\n",j,i,ymin[i]) ;
#		  endif
		}
	      }
	    }
	  } else if (nflagu == 0 && nordu == 1 && cbarjmax > ztolcbarj) {
	    // We may be able to tighten
	    for (CoinBigIndex k = kcs; k < kce; k++) {
	      const int i = hrow[k] ;
	      const double coeff = colels[k] ;
	      
	      if (coeff < 0.0) {
		ymax[i] += cbarjmax/coeff ;
		cbarjmax =0.0 ;
		tightened++ ;
#		if PRESOLVE_DEBUG > 1
		printf("Col %d, row %d max pi now %g\n",j,i,ymax[i]) ;
#		endif
	      } else if (coeff > 0.0 ) {
		ymin[i] += cbarjmax/coeff ;
		cbarjmax =0.0 ;
		tightened++ ;
#		if PRESOLVE_DEBUG > 1
		printf("Col %d, row %d min pi now %g\n",j,i,ymin[i]) ;
#		endif
	      }
	    }
	  }
	}    // end l<j> < -infty
#       endif	// PROCESS_INFINITE_LB
      }
/*
  That's the end of propagation of bounds for dual variables.
*/
#     if PRESOLVE_TIGHTEN_DUALS > 0
      cbarmin[j] = (nflagl?(-PRESOLVE_INF):cbarjmin) ;
      cbarmax[j] = (nflagu?PRESOLVE_INF:cbarjmax) ;
#     endif
/*
  If cbarmin<j> > 0 (strict inequality) then x<j> NBLB at optimality. If l<j>
  is -infinity, notify the user and set the status to unbounded.
*/
      if (cbarjmin > ztolcbarj && nflagl == 0 && !prob->colProhibited2(j)) {
	if (clo[j] <= -ekkinf) {
	  CoinMessageHandler *msghdlr = prob->messageHandler() ;
	  msghdlr->message(COIN_PRESOLVE_COLUMNBOUNDB,prob->messages())
	    << j << CoinMessageEol ;
	  prob->status_ |= 2 ;
	  break ;
	} else {
	  fix_cols[--nfixdown_cols] = j ;
#	  if PRESOLVE_DEBUG > 1
	  std::cout << "NDUAL(fix l): fix x(" << j << ")" ;
	  if (csol) std::cout << " = " << csol[j] ;
	  std::cout
	    << " at l(" << j << ") = " << clo[j] << "; cbar("
	    << j << ") > " << cbarjmin << "." << std::endl ;
#	  endif
	  if (csol) {
#           if REMOVE_DUAL_ACTION_REPAIR_SOLN > 0
/*
  Original comment: User may have given us feasible solution - move if simple

  Except it's not simple. The net result is that we end up with an excess of
  basic variables. Mark x<j> NBLB and let the client recalculate the solution
  after establishing the basis.
*/
	    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++) {
		const int k = hcol[kk] ;
		if (colstat[k] == CoinPrePostsolveMatrix::superBasic)
		  continue ;

		if (hincol[k] == 1 && k != j) {
		  const 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(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] ;
	    colstat[j] = CoinPrePostsolveMatrix::atLowerBound ;
	  }
	}
      } else if (cbarjmax < -ztolcbarj && nflagu == 0 &&
      		 !prob->colProhibited2(j)) {
/*
  If cbarmax<j> < 0 (strict inequality) then x<j> NBUB at optimality. If u<j>
  is infinity, notify the user and set the status to unbounded.
*/
	if (cup[j] >= ekkinf) {
	  CoinMessageHandler *msghdlr = prob->messageHandler() ;
	  msghdlr->message(COIN_PRESOLVE_COLUMNBOUNDA,prob->messages())
	    << j << CoinMessageEol ;
	  prob->status_ |= 2 ;
	  break ;
	} else {
	  fix_cols[nfixup_cols++] = j ;
#	  if PRESOLVE_DEBUG > 1
	  std::cout << "NDUAL(fix u): fix x(" << j << ")" ;
	  if (csol) std::cout << " = " << csol[j] ;
	  std::cout
	    << " at u(" << j << ") = " << cup[j] << "; cbar("
	    << j << ") < " << cbarjmax << "." << std::endl ;
#	  endif
	  if (csol) {
#	    if 0
	    // See comments above for 'fix at lb'.
	    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++) {
		const int k = hcol[kk] ;
		if (colstat[k] == CoinPrePostsolveMatrix::superBasic)
		  continue ;

		if (hincol[k] == 1 && k != j) {
		  const 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(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] ;
	    colstat[j] = CoinPrePostsolveMatrix::atUpperBound ;
	  }
	}
      }    // end cbar<j> < 0
    }
/*
  That's the end of this walk through the columns.
*/
    // I don't know why I stopped doing this.
#   if PRESOLVE_TIGHTEN_DUALS > 0
    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++) {
      const bool no_ub = (rup[i] >= ekkinf) ;
      const bool no_lb = (rlo[i] <= -ekkinf) ;

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

	// all row columns are non-empty
	for (CoinBigIndex k = krs ; k < kre ; k++) {
	  const double coeff = rowels[k] ;
	  const int icol = hcol[k] ;
	  const double cbarmax0 = cbarmax[icol] ;
	  const double cbarmin0 = cbarmin[icol] ;

	  if (no_ub) {
	    // cbarj must not be negative
	    if (coeff > ZTOLDP2 &&
	    	cbarjmax0 < PRESOLVE_INF && cup[icol] >= ekkinf) {
	      const double bnd = cbarjmax0 / coeff ;
	      if (rmax > bnd) {
#		if PRESOLVE_DEBUG > 1
		printf("MAX TIGHT[%d,%d]: %g --> %g\n",i,hrow[k],ymax[i],bnd) ;
#		endif
		ymax[i] = rmax = bnd ;
		tightened ++; ;
	      }
	    } else if (coeff < -ZTOLDP2 &&
	    	       cbarjmax0 <PRESOLVE_INF && cup[icol] >= ekkinf) {
	      const double bnd = cbarjmax0 / coeff ;
	      if (rmin < bnd) {
#		if PRESOLVE_DEBUG > 1
		printf("MIN TIGHT[%d,%d]: %g --> %g\n",i,hrow[k],ymin[i],bnd) ;
#		endif
		ymin[i] = rmin = bnd ;
		tightened ++; ;
	      }
	    }
	  } else {	// no_lb
	    // cbarj must not be positive
	    if (coeff > ZTOLDP2 &&
	    	cbarmin0 > -PRESOLVE_INF && clo[icol] <= -ekkinf) {
	      const double bnd = cbarmin0 / coeff ;
	      if (rmin < bnd) {
#		if PRESOLVE_DEBUG > 1
		printf("MIN1 TIGHT[%d,%d]: %g --> %g\n",i,hrow[k],ymin[i],bnd) ;
#		endif
		ymin[i] = rmin = bnd ;
		tightened ++; ;
	      }
	    } else if (coeff < -ZTOLDP2 &&
	    	       cbarmin0 > -PRESOLVE_INF && clo[icol] <= -ekkinf) {
	      const double bnd = cbarmin0 / coeff ;
	      if (rmax > bnd) {
#		if PRESOLVE_DEBUG > 1
		printf("MAX TIGHT1[%d,%d]: %g --> %g\n",i,hrow[k],ymax[i],bnd) ;
#		endif
		ymax[i] = rmax = bnd ;
		tightened ++; ;
	      }
	    }
	  }
	}
      }
    }
#   endif    // PRESOLVE_TIGHTEN_DUALS
/*
  Is it productive to continue with another pass? Essentially, we need lots of
  tightening but no fixing. If we fixed any variables, break and process them.
*/
#   if PRESOLVE_DEBUG > 1
    std::cout
      << "NDUAL: pass " << nPass << ": fixed " << (ncols-nfixdown_cols)
      << " down, " << nfixup_cols << " up, tightened " << tightened
      << " duals." << std::endl ;
#   endif
    if (tightened < 100 || nfixdown_cols < ncols || nfixup_cols)
      break ;
  }
  assert (nfixup_cols <= nfixdown_cols) ;
/*
  Process columns fixed at upper bound.
*/
  if (nfixup_cols) {
#   if PRESOLVE_DEBUG > 1
    std::cout << "NDUAL(upper):" ;
    for (int k = 0 ; k < nfixup_cols ; k++)
      std::cout << " " << fix_cols[k] ;
    std::cout << "." << std::endl ;
#   endif
    next = make_fixed_action::presolve(prob,fix_cols,nfixup_cols,false,next) ;
  }
/*
  Process columns fixed at lower bound.
*/
  if (nfixdown_cols < ncols) {
    int *fixdown_cols = fix_cols+nfixdown_cols ; 
    nfixdown_cols = ncols-nfixdown_cols ;
#   if PRESOLVE_DEBUG > 1
    std::cout << "NDUAL(lower):" ;
    for (int k = 0 ; k < nfixdown_cols ; k++)
      std::cout << " " << fixdown_cols[k] ;
    std::cout << "." << std::endl ;
#   endif
    next = make_fixed_action::presolve(prob,fixdown_cols,
    				       nfixdown_cols,true,next) ;
  }
/*
  Now look for variables that, when moved in the favourable direction
  according to reduced cost, will naturally tighten an inequality to an
  equality. We can convert that inequality to an equality. See the comments
  at the head of the routine.

  Start with logicals. Open a loop and look for suitable rows. At the
  end of this loop, rows marked with +/-1 will be forced to equality by
  their logical. Rows marked with +/-2 are inequalities that (perhaps)
  can be forced to equality using architecturals. Rows marked with 0 are
  not suitable (range or nonbinding).

  Note that usefulRowInt_ is 3*nrows_; we'll use the second partition below.
*/
  int *canFix = prob->usefulRowInt_ ;
  for (int i = 0 ; i < nrows ; i++) {
    const bool no_rlb = (rlo[i] <= -ekkinf) ;
    const bool no_rub = (rup[i] >= ekkinf) ;
    canFix[i] = 0 ;
    if (no_rub && !no_rlb ) {
      if (ymin[i] > 0.0) 
	canFix[i] = -1 ;
      else
	canFix[i] = -2 ;
    } else if (no_rlb && !no_rub ) {
      if (ymax[i] < 0.0)
	canFix[i] = 1 ;
      else
	canFix[i] = 2 ;
    }
#   if PRESOLVE_DEBUG > 1
    if (abs(canFix[i]) == 1) {
      std::cout
        << "NDUAL(eq): candidate row (" << i << ") (" << rlo[i]
	<< "," << rup[i] << "), logical must be "
	<< ((canFix[i] == -1)?"NBUB":"NBLB") << "." << std::endl ;
    }
#   endif
  }
/*
  Can we do a similar trick with architectural variables? Here, we're looking
  for x<j> such that
  (1) Exactly one of l<j> or u<j> is infinite.
  (2) Moving x<j> towards the infinite bound can tighten exactly one
      constraint i to equality.  If x<j> is entangled with other constraints,
      moving x<j> towards the infinite bound will loosen those constraints.
  (3) Moving x<j> towards the infinite bound is a good idea according to the
      cost c<j> (note we don't have to consider reduced cost here).
  If we can find a suitable x<j>, constraint i can become an equality.

  This is yet another instance of bound propagation, but we're looking for
  a very specific pattern: A variable that can be increased arbitrarily
  in all rows it's entangled with, except for one, which bounds it. And
  we're going to push the variable so as to make that row an equality.
  But note what we're *not* doing: No actual comparison of finite bound
  values to the amount necessary to force an equality. So no worries about
  accuracy, the bane of bound propagation.

  Open a loop to scan columns. bindingUp and bindingDown indicate the result
  of the analysis; -1 says `possible', -2 is ruled out. Test first for
  condition (1). Column singletons are presumably handled elsewhere. Integer
  variables need not apply. If both bounds are finite, no need to look
  further.
*/
  for (int j = 0 ; j < ncols ; j++) {
    if (hincol[j] <= 1) continue ;
    if (integerType[j]) continue ;
    int bindingUp = -1 ;
    int bindingDown = -1 ;
    if (cup[j] < ekkinf)
      bindingUp = -2 ;
    if (clo[j] > -ekkinf)
      bindingDown = -2 ;
    if (bindingUp == -2 && bindingDown == -2) continue ;
/*
  Open a loop to walk the column and check for condition (2).

  The test for |canFix[i]| != 2 is a non-interference check. We don't want to
  mess with constraints where we've already decided to use the logical to
  force equality. Nor do we want to deal with range or nonbinding constraints.
*/
    const CoinBigIndex &kcs = mcstrt[j] ;
    const CoinBigIndex kce = kcs+hincol[j] ;
    for (CoinBigIndex k = kcs; k < kce; k++) {
      const int &i = hrow[k] ;
      if (abs(canFix[i]) != 2) {
	bindingUp = -2 ;
	bindingDown = -2 ;
	break ;
      }
      double aij = colels[k] ;
/*
  For a<ij> > 0 in a <= constraint (canFix = 2), the up direction is
  binding. For a >= constraint, it'll be the down direction. If the relevant
  binding code is still -1, set it to the index of the row. Similarly for
  a<ij> < 0.

  If this is the second or subsequent binding constraint in that direction,
  set binding[Up,Down] to -2 (we don't want to get into the business of
  calculating which constraint is actually binding).
*/
      if (aij > 0.0) {
	if (canFix[i] == 2) {
	  if (bindingUp == -1)
	    bindingUp = i ;
	  else
	    bindingUp = -2 ;
	} else {
	  if (bindingDown == -1)
	    bindingDown = i ;
	  else
	    bindingDown = -2 ;
	}
      } else {
	if (canFix[i] == 2) {
	  if (bindingDown == -1)
	    bindingDown = i ;
	  else
	    bindingDown = -2 ;
	} else {
	  if (bindingUp == -1)
	    bindingUp = i ;
	  else
	    bindingUp = -2 ;
	}
      }
    }
    if (bindingUp == -2 && bindingDown == -2) continue ;
/*
  If bindingUp > -2, then either no constraint provided a bound (-1) or
  there's a single constraint (0 <= i < m) that bounds x<j>.  If we have
  just one binding constraint, check that the reduced cost is favourable
  (c<j> <= 0 for x<j> NBUB at optimum for minimisation). If so, declare
  that we will force the row to equality (canFix[i] = +/-1). Note that we
  don't adjust the primal solution value for x<j>.

  If no constraint provided a bound, we might be headed for unboundedness,
  but leave that for some other code to determine.
*/
    double cj = cost[j] ;
    if (bindingUp > -2 && cj <= 0.0) {
      if (bindingUp >= 0) {
	canFix[bindingUp] /= 2 ;
#       if PRESOLVE_DEBUG > 1
	std::cout
	  << "NDUAL(eq): candidate row (" << bindingUp << ") ("
	  << rlo[bindingUp] << "," << rup[bindingUp] << "), "
	  << " increasing x(" << j << "), cbar = " << cj << "."
	  << std::endl ;
      } else {
          std::cout
	    << "NDUAL(eq): no binding upper bound for x(" << j
	    << "), cbar = " << cj << "." << std::endl ;
#        endif
      }
    } else if (bindingDown > -2 && cj >= 0.0) {
      if (bindingDown >= 0) {
	canFix[bindingDown] /= 2 ;
#       if PRESOLVE_DEBUG > 1
	std::cout
	  << "NDUAL(eq): candidate row (" << bindingDown << ") ("
	  << rlo[bindingDown] << "," << rup[bindingDown] << "), "
	  << " decreasing x(" << j << "), cbar = " << cj << "."
	  << std::endl ;
      } else {
          std::cout
	    << "NDUAL(eq): no binding lower bound for x(" << j
	    << "), cbar = " << cj << "." << std::endl ;
#        endif
      }
    }
  }
/*
  We have candidate rows. We've avoided scanning full rows until now,
  but there's one remaining hazard: if the row contains unfixed integer
  variables then we don't want to just pin the row to a fixed rhs; that
  might prevent us from achieving integrality. Scan canFix, count and
  record suitable rows (use the second partition of usefulRowInt_).
*/
# if PRESOLVE_DEBUG > 0
  int makeEqCandCnt = 0 ;
  for (int i = 0 ; i < nrows ; i++) {
    if (abs(canFix[i]) == 1) makeEqCandCnt++ ;
  }
# endif
  int makeEqCnt = nrows ;
  for (int i = 0 ; i < nrows ; i++) {
    if (abs(canFix[i]) == 1) {
      const CoinBigIndex &krs = mrstrt[i] ;
      const CoinBigIndex kre = krs+hinrow[i] ;
      for (CoinBigIndex k = krs ; k < kre ; k++) {
	const int j = hcol[k] ;
	if (cup[j] > clo[j] && (integerType[j]||prob->colProhibited2(j))) {
	  canFix[i] = 0 ;
#	  if PRESOLVE_DEBUG > 1
	  std::cout
	    << "NDUAL(eq): cannot convert row " << i << " to equality; "
	    << "unfixed integer variable x(" << j << ")." << std::endl ;
#         endif
          break ;
	}
      }
      if (canFix[i] != 0) canFix[makeEqCnt++] = i ;
    }
  }
  makeEqCnt -= nrows ;
# if PRESOLVE_DEBUG > 0
  if ((makeEqCandCnt-makeEqCnt) > 0) {
    std::cout
      << "NDUAL(eq): rejected " << (makeEqCandCnt-makeEqCnt)
      << " rows due to unfixed integer variables." << std::endl ;
  }
# endif
/*
  If we've identified inequalities to convert, do the conversion, record
  the information needed to restore bounds in postsolve, and finally create
  the postsolve object.
*/
  if (makeEqCnt > 0) {
    action *bndRecords = new action[makeEqCnt] ;
    for (int k = 0 ; k < makeEqCnt ; k++) {
      const int &i = canFix[k+nrows] ;
#     if PRESOLVE_DEBUG > 1
      std::cout << "NDUAL(eq): forcing row " << i << " to equality;" ;
      if (canFix[i] == -1)
	std::cout << " dropping b = " << rup[i] << " to " << rlo[i] ;
      else
	std::cout << " raising blow = " << rlo[i] << " to " << rup[i] ;
      std::cout << "." << std::endl ;
#     endif
      action &bndRec = bndRecords[(k)] ;
      bndRec.rlo_ = rlo[i] ;
      bndRec.rup_ = rup[i] ;
      bndRec.ndx_ = i ;
      if (canFix[i] == 1) {
	rlo[i] = rup[i] ;
	prob->addRow(i) ;
      } else if (canFix[i] == -1) {
	rup[i] = rlo[i] ;
	prob->addRow(i) ;
      }
    }
    next = new remove_dual_action(makeEqCnt,bndRecords,next) ;
  }

# if PRESOLVE_TIGHTEN_DUALS > 0
  delete[] cbarmin ;
  delete[] cbarmax ;
# endif

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

  return (next) ;
}
コード例 #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) ;
}