Exemplo n.º 1
0
static dyret_enum adjust_therest (int patchcnt, patch_struct *patches)

/*
  We're here because we've successfully patched a singular basis. The patches
  array contains entries of the form <basis pos'n, x<j>, x<i>>, where x<j>
  has just been kicked out of the basis and replaced by x<i>.  The basis and
  var2basis vectors are already corrected (we needed them to complete the
  factorization).  Now we need to adjust other dylp data structures to
  reflect the unexpected change.  The amount of additional work to be done
  depends on the phase of the simplex algorithm.

    dyINIT: We're done. We've just factored the initial basis and none of the
	    other data structures have been initialised. We didn't really
	    need this call, but the code is cleaner this way.

  If we're farther along, we might be in the middle of simplex (dyPRIMAL1,
  dyPRIMAL2, or dyDUAL), or we might be manipulating the constraint system.

  If we're running simplex, the first actions are cleanup: clear the pivot
  reject list and back out any antidegeneracy activity.
    
  Next, set the status of the newly nonbasic variables, consistent with their
  previous status. The general rule is to perturb the solution as little as
  possible. If we're in a primal or dual simplex phase, try to make decisions
  that are compatible with primal or dual feasibility. Two specific points:
    * Superbasic (SB) variables are only created in dyPRIMAL2.
    * Nonbasic free (NBFR) variables imply loss of dual feasibility.

  Once we have nonbasic status set, we can calculate new primals, duals, and
  reduced costs and fine-tune the status of the newly basic variables. If
  we've arrived here from one of the constraint system manipulation phases,
  there will almost certainly be duplication of effort once we return. But
  hey, how often does a basis patch happen, anyway?
  
  If we're in a simplex phase, there's still some work to do to make the
  patch as transparent as possible.

  For dual simplex, we'll check the status of the nonbasic variables and
  try to maintain dual feasibility. This may not be possible. If we do
  maintain dual feasibility, reset the DSE norms.

  For primal simplex, we need to reset the PSE norms.

  Parameters:
    patchcnt:	the number of basis changes
    patches:	array of basis changes

  Returns: dyrOK if the repair proceeds without error, dyrLOSTDFEAS if
	   feasibility is lost in dual phase II, and dyrFATAL if anything
	   else goes wrong.
*/

{ int i,j,pndx ;
  pkvec_struct *aj ;
  flags statj ;
  dyret_enum retval ;
  dyphase_enum phase ;
  double valj,cbarj,*vub,*vlb,*obj ;

  const char *rtnnme = "adjust_therest" ;

# ifndef DYLP_NDEBUG
  flags stati ;
  double vali ;
# endif

# ifdef DYLP_PARANOIA
  if (dy_sys == NULL)
  { errmsg(2,rtnnme,"dy_sys") ;
    return (dyrFATAL) ; }
  if (dy_basis == NULL)
  { errmsg(2,rtnnme,"basis") ;
    return (dyrFATAL) ; }
  if (dy_var2basis == NULL)
  { errmsg(2,rtnnme,"var2basis") ;
    return (dyrFATAL) ; }
  if (patches == NULL)
  { errmsg(2,rtnnme,"patch") ;
    return (dyrFATAL) ; }
# endif

  phase = dy_lp->phase ;

# ifdef DYLP_PARANOIA
  if (!(phase == dyINIT || phase == dyADDVAR || phase == dyADDCON ||
	phase == dyPRIMAL1 || phase == dyPRIMAL2 || phase == dyDUAL ||
	phase == dyFORCEPRIMAL || phase == dyFORCEDUAL))
  { errmsg(1,rtnnme,__LINE__) ;
    return (dyrFATAL) ; }
  if (!(phase == dyINIT))
  { if (dy_status == NULL)
    { errmsg(2,rtnnme,"status") ;
      return (dyrFATAL) ; }
    if (dy_x == NULL)
    { errmsg(2,rtnnme,"x") ;
      return (dyrFATAL) ; }
    if (dy_xbasic == NULL)
    { errmsg(2,rtnnme,"x<B>") ;
      return (dyrFATAL) ; } }
#endif

  if (phase == dyINIT) return (dyrOK) ;

  vlb = dy_sys->vlb ;
  vub = dy_sys->vub ;
  obj = dy_sys->obj ;
  aj = NULL ;
  retval = dyrOK ;

/*
  If we're in one of the simplex phases, back out any antidegeneracy activity
  and clear the pivot rejection list.  It's easiest to clear the pivot reject
  list ahead of the status modifications so that we don't have to worry about
  the NOPIVOT qualifier when checking status values.
*/
  if (phase == dyPRIMAL1 || phase == dyPRIMAL2 || phase == dyDUAL)
  { if (dy_clrpivrej(NULL) != TRUE) return (dyrFATAL) ;
    if (dy_lp->degen > 0)
    { if (phase == dyDUAL)
      { (void) dy_dualdegenout(0) ; }
      else
      { (void) dy_degenout(0) ; } } }
/*
  Now correct the status for newly nonbasic variables. We need to correct
  dy_x if the status change forces a change in value.  If we end up with a
  NBFR variable, we've lost dual feasibility.

  While we're walking the patches, set the status for x<i> (the newly basic
  variable) to vstatB. No need to be more precise at this point.
*/

  for (pndx = 0 ; pndx < patchcnt ; pndx++)
  { i = patches[pndx].in ;
#   ifndef DYLP_NDEBUG
    stati = dy_status[i] ;
    vali = dy_x[i] ;
#   endif
    dy_status[i] = vstatB ;
    j = patches[pndx].out ;
    statj = dy_status[j] ;
    valj = dy_x[j] ;
    switch (statj)
    { case vstatBLLB:
      { dy_status[j] = vstatNBLB ;
	dy_x[j] = vlb[j] ;
	break ; }
      case vstatBLB:
      { dy_status[j] = vstatNBLB ;
	break ; }
      case vstatB:
      { if (phase == dyPRIMAL2)
	  dy_status[j] = vstatSB ;
	else
	if (valj-vlb[j] < vub[j]-valj)
	{ dy_status[j] = vstatNBLB ;
	  dy_x[j] = vlb[j] ; }
	else
	{ dy_status[j] = vstatNBUB ;
	  dy_x[j] = vub[j] ; }
	break ; }
      case vstatBUB:
      { dy_status[j] = vstatNBUB ;
	break ; }
      case vstatBUUB:
      { dy_status[j] = vstatNBUB ;
	dy_x[j] = vub[j] ;
	break ; }
      case vstatBFX:
      { dy_status[j] = vstatNBFX ;
	break ; }
      case vstatBFR:
      { dy_status[j] = vstatNBFR ;
	if (phase == dyDUAL)
	{ 
#	  ifndef DYLP_NDEBUG
	  if (dy_opts->print.dual >= 1)
	  { dywarn(346,rtnnme,
		 dy_sys->nme,dy_prtlpphase(phase,TRUE),dy_lp->tot.iters+1,
		 dy_prtvstat(statj),consys_nme(dy_sys,'v',j,FALSE,NULL),j) ; }
#	  endif
	  retval = dyrLOSTDFEAS ; }
	break ; }
      default:
      { errmsg(380,rtnnme,dy_sys->nme,consys_nme(dy_sys,'v',j,FALSE,NULL),j,
	       dy_prtvstat(statj),"basic") ;
	return (dyrFATAL) ; } }
#   ifndef DYLP_NDEBUG
    if (dy_opts->print.basis >= 3)
    { dyio_outfmt(dy_logchn,dy_gtxecho,
		  "\n\t%s (%d) had status %s, value %g, ",
		  consys_nme(dy_sys,'v',i,FALSE,NULL),i,
		  dy_prtvstat(stati),vali) ;
      dyio_outfmt(dy_logchn,dy_gtxecho,"now status %s.",
		  dy_prtvstat(dy_status[i])) ;
      dyio_outfmt(dy_logchn,dy_gtxecho,
		  "\n\t%s (%d) had status %s, value %g, ",
		  consys_nme(dy_sys,'v',j,FALSE,NULL),j,
		  dy_prtvstat(statj),valj) ;
      dyio_outfmt(dy_logchn,dy_gtxecho,"now status %s, value %g.",
		  dy_prtvstat(dy_status[j]),dy_x[j]) ; }
#   endif
  }

# ifdef DYLP_PARANOIA
/*
  If paranoid checks are in place, we need agreement between dy_status, dy_x,
  and dy_xbasic, lest dy_calccbar fail. Call dy_calcprimals and
  dy_setbasicstatus to get the basic status right. This is restricted to
  paranoid mode because the proper place to do this is after making
  corrections to nonbasic status for dual feasibility.
*/
  if (dy_calcprimals() == FALSE) return (dyrFATAL) ;
  dy_setbasicstatus() ;
# endif

/*
  Calculate the duals and reduced costs.
*/
  dy_calcduals() ;
  if (dy_calccbar() == FALSE)
  { errmsg(384,rtnnme,
	   dy_sys->nme,dy_prtlpphase(phase,TRUE),dy_lp->tot.iters) ;
    return (dyrFATAL) ; }

/*
  If we're in phase dyDUAL, it's worth a scan to check dual feasibility and
  make adjustments to maintain it, if possible. (retval = dyrLOSTDFEAS says
  we introduced a NBFR variable, in which case we have no hope).

  Open a loop to scan the nonbasic variables. NBFX variables are always dual
  feasible, NBFR variables are never dual feasible.  We're minimising, so
  dual feasibility (primal optimality) is cbarj < 0 && x<j> at upper bound,
  or cbarj > 0 && x<j> at lower bound.  It's important that the zero
  tolerance for cbar<j> here be the same as the one used in dy_dualin when it
  checks for loss of dual feasibility.
*/
  if (phase == dyDUAL && retval != dyrLOSTDFEAS)
  { for (j = 1 ; j <= dy_sys->varcnt ; j++)
    { statj = dy_status[j] ;
      if (flgon(statj,vstatBASIC|vstatNBFX)) continue ;
      if (flgon(statj,vstatNBFR))
      { retval = dyrLOSTDFEAS ;
#	ifndef DYLP_NDEBUG
	cbarj = dy_cbar[j] ;
	if (dy_opts->print.dual >= 1)
	{ dywarn(347,rtnnme,
	       dy_sys->nme,dy_prtlpphase(phase,TRUE),dy_lp->tot.iters+1,
	       consys_nme(dy_sys,'v',j,FALSE,NULL),j,
	       dy_prtvstat(statj),j,cbarj,dy_tols->dfeas) ; }
#	endif
	break ; }
      cbarj = dy_cbar[j] ;
      if (cbarj < -dy_tols->dfeas && flgoff(statj,vstatNBUB))
      { if (vub[j] >= dy_tols->inf)
	{
#	  ifndef DYLP_NDEBUG
	  if (dy_opts->print.dual >= 1)
	  { dywarn(347,rtnnme,
		 dy_sys->nme,dy_prtlpphase(phase,TRUE),dy_lp->tot.iters+1,
		 consys_nme(dy_sys,'v',j,FALSE,NULL),j,
		 dy_prtvstat(statj),j,cbarj,dy_tols->dfeas) ; }
#	  endif
	  retval = dyrLOSTDFEAS ;
	  break ; }
	else
	{ dy_status[j] = vstatNBUB ;
	  dy_x[j] = vub[j] ; } }
      else
      if (cbarj > dy_tols->dfeas && flgoff(statj,vstatNBLB))
      { if (vlb[j] >= dy_tols->inf)
	{
#	  ifndef DYLP_NDEBUG
	  if (dy_opts->print.dual >= 1)
	  { dywarn(347,rtnnme,
		 dy_sys->nme,dy_prtlpphase(phase,TRUE),dy_lp->tot.iters+1,
		 consys_nme(dy_sys,'v',j,FALSE,NULL),j,
		 dy_prtvstat(statj),j,cbarj,dy_tols->dfeas) ; }
#	  endif
	  retval = dyrLOSTDFEAS ;
	  break ; }
	else
	{ dy_status[j] = vstatNBLB ;
	  dy_x[j] = vlb[j] ; } }
#     ifndef DYLP_NDEBUG
      if (dy_opts->print.basis >= 3 && dy_status[j] != statj)
      { dyio_outfmt(dy_logchn,dy_gtxecho,
		    "\n\tchanged status of %s (%d) from %s to",
		    consys_nme(dy_sys,'v',j,FALSE,NULL),j,dy_prtvstat(statj)) ;
	dyio_outfmt(dy_logchn,dy_gtxecho,
		    " %s to maintain dual feasibility; cbar = %g.",
		    dy_prtvstat(dy_status[j]),cbarj) ; }
#     endif
    } }

/*
  The dual variables and reduced costs have been recalculated, and we have
  the final status for all nonbasic variables.  Recalculate the primal
  variables and set the status of the basic variables.
*/
  if (dy_calcprimals() == FALSE) return (dyrFATAL) ;
  dy_setbasicstatus() ;
/*
  If we're running primal simplex, reset the PSE reference frame. If we're
  running dual simplex and haven't lost dual feasibility, recalculate the
  basis inverse row norms.
*/
  if (phase == dyPRIMAL1 || phase == dyPRIMAL2)
  { dy_pseinit() ; }
  else
  if (phase == dyDUAL && retval != dyrLOSTDFEAS)
  { dy_dseinit() ; }

  return (retval) ; }
Exemplo n.º 2
0
dyret_enum dy_dealWithPunt (void)

/*
  This routine decides on the appropriate action(s) when a simplex decides to
  punt. The algorithm is this:
    1) Sort the entries in pivrejlst into two sets: iter == basis.pivs
       (current) and iter != basis.pivs (old). In the current set, count
       the number of mad and singular entries.
    2) If there are any entries in old, remove them from pivrejlst and
       return with an indication to resume pivoting (dyrRESELECT).
    3) If all entries in current are of type singular, return with an
       indication to abort this simplex phase (dyrPUNT) and hope that we can
       alter the constraint system.
    4) For each permissible reduction in pivot tolerance, check for entries
       of type MADPIV that might become acceptable. If there are any, remove
       them from pivrejlst and return dyrRESELECT.
    5) If 4) failed to identify pivots, return dyrPUNT.

  Parameters: none

  Returns: dyrRESELECT if pivoting can resume
	   dyrPUNT to abort this simplex phase
	   dyrFATAL if something goes wrong
*/

{ int j,ndx,last,oldcnt,curcnt,curmad,brk ;
  double maxratio,pivmul ;
  bool clr_retval ;
  dyret_enum retval ;

  int *old,*current ;
  pivrej_struct *pivrej ;

# ifndef DYLP_NDEBUG
  const char *rtnnme = "dy_dealWithPunt" ;
# endif

# ifdef DYLP_STATISTICS
  if (dy_stats != NULL) dy_stats->pivrej.puntcall++ ;
# endif

  retval = dyrINV ;
/*
  If there are no rejected pivots, the punt stands.
*/
  if (pivrej_ctl.cnt == 0)
  {
#   ifdef DYLP_STATISTICS
    if (dy_stats != NULL) dy_stats->pivrej.puntret++ ;
#   endif
    return (dyrPUNT) ; }
/*
  Setup and scan pivrejlst as indicated above.
*/
  last = pivrej_ctl.cnt ;
  brk = dy_lp->basis.pivs ;
  old = (int *) MALLOC((last+1)*sizeof(int)) ;
  current = (int *) MALLOC((last+1)*sizeof(int)) ;
  oldcnt = 0 ;
  curcnt = 0 ;
  curmad = 0 ;
  maxratio = 0 ;

  for (ndx = 0 ; ndx < last ; ndx++)
  { pivrej = &pivrejlst[ndx] ;
    if (pivrej->iter != brk)
    { old[++oldcnt] = ndx ; }
    else
    { current[++curcnt] = ndx ;
      if (pivrej->why == dyrMADPIV)
      { curmad++ ;
	if (maxratio < pivrej->ratio) maxratio = pivrej->ratio ; } } }
/*
  If there are old entries, we can always hope the intervening pivots have
  cured the problem. It happens.
*/
  if (oldcnt > 0)
  { old[0] = oldcnt ;
    clr_retval = dy_clrpivrej(old) ;
    if (clr_retval == TRUE)
    { retval = dyrRESELECT ; }
    else
    { retval = dyrFATAL ; }
#   ifndef DYLP_NDEBUG
    if (dy_opts->print.pivreject >= 1)
    { dyio_outfmt(dy_logchn,dy_gtxecho,
		  "\n  restored %d entries queued before iter = %d.",
		  old[0],brk) ; }
#   endif
  }
/*
  Are there any mad pivots that we can press into service by reducing the pivot
  tolerance?
*/
  else
  if (curmad > 0 && maxratio > dy_tols->zero)
  { pivmul = 1/dy_tols->pivot ;
    while (maxratio*pivmul < 1.0) pivmul *= pivrej_ctl.pivmul ;
    if (1/pivmul >= dy_tols->zero*100)
    { 
#     ifndef DYLP_NDEBUG
      if (dy_opts->print.pivreject >= 1)
      { warn(376,rtnnme,
	     dy_sys->nme,dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters,
	     dy_tols->pivot,1/pivmul) ; }
#     endif
      dy_tols->pivot = 1/pivmul ;
#     ifdef DYLP_STATISTICS
      if (dy_stats != NULL)
      { dy_stats->pivrej.pivtol_red++ ;
	if (dy_tols->pivot < dy_stats->pivrej.min_pivtol)
	{ dy_stats->pivrej.min_pivtol = dy_tols->pivot ; } }
#     endif
      j = 0 ;
      for (ndx = 1 ; ndx <= curcnt ; ndx++)
      { pivrej = &pivrejlst[current[ndx]] ;
	if (pivrej->ratio*pivmul > 1.0)
	{ current[++j] = current[ndx] ; } }
      current[0] = j ;
      clr_retval = dy_clrpivrej(current) ;
      if (clr_retval == TRUE)
      { retval = dyrRESELECT ; }
      else
      { retval = dyrFATAL ; }
#   ifndef DYLP_NDEBUG
    if (dy_opts->print.pivreject >= 1)
    { dyio_outfmt(dy_logchn,dy_gtxecho,
	      "\n  restored %d entries queued at iter = %d at piv. tol = %g",
	      current[0],brk,dy_tols->pivot) ; }
#   endif
    }
    else
    { 
#     ifndef DYLP_NDEBUG
      if (dy_opts->print.pivreject >= 1)
      { warn(383,rtnnme,dy_sys->nme,
	     dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters,
	     dy_tols->zero,dy_prtdyret(dyrPUNT)) ; }
#     endif
      retval = dyrPUNT ; } }
  else
  { retval = dyrPUNT ; }
/*
  That's it, we've done our best. Free the old and current arrays and return.
*/
  FREE(old) ;
  FREE(current) ;

# ifndef DYLP_NDEBUG
  if (retval == dyrPUNT && dy_opts->print.pivreject >= 1)
  { dyio_outfmt(dy_logchn,dy_gtxecho,"\n  PUNT! mad = %d, singular = %d.",
	        pivrej_ctl.mad,pivrej_ctl.sing) ; }
# endif
# ifdef DYLP_STATISTICS
  if (dy_stats != NULL && retval == dyrPUNT) dy_stats->pivrej.puntret++ ;
# endif

  return (retval) ; }