Exemplo n.º 1
0
dyret_enum dy_hotstart (lpprob_struct *orig_lp)

/*
  This routine is responsible for handling a hot start. The assumption is
  that all data structures are in place, and that the user is allowed to change
  the bounds on variables and any of the rhs and objective coefficients. See
  the comments at the head of the file.

  Changes to the rhs and bounds are handled first. We reinstall the rhs
  array, then scan the variables, updating bounds and status and making the
  rhs corrections required for inactive variables.  If the bounds or rhs
  change, we need new primals. After we calculate new primals, we'll need to
  scan the basic variables and make sure their final status is correct.

  If the objective or bounds change, we need to recalculate the contribution
  to the objective from inactive variables. If the objective changes, we need
  new duals.  (It's also true that if the objective changes, we need new
  reduced costs, but that's handled in commonstart.)

  The most likely situation is that we haven't pivoted since refactoring as
  part of the preoptimality sequence, so we shouldn't need to refactor here.
  Instead, we leave it to dy_duenna to pick this up with the next pivot, as
  well as any possible accuracy check.

  Once all the changes have been incorporated, calculate primals and duals to
  determine primal and dual feasibility, and select the appropriate simplex
  phase in dy_lp->simplex.next.

  Parameters:
    orig_lp:	The original lp problem structure

  Returns: dyrOK if the setup completes without error, dyrINV or dyrFATAL
	   otherwise.
*/

{ int oxkndx,xkndx,oaindx,aindx ;
  double *ogvlb,*dyvlb,*ogvub,*dyvub,*ogobj,*dyobj,*dyrhs,*ogrhs ;
  double lbj,ubj ;
  consys_struct *orig_sys ;
  flags *ogstatus,calcflgs,statk ;
  dyret_enum retval ;
  lpret_enum lpret ;
  dyphase_enum phase ;
  const char *rtnnme = "dy_hotstart" ;

  /* dy_scaling.c */
  extern void dy_refreshlclsystem(flags what) ;

  /* dy_force.c */
  extern dyphase_enum dy_forceFull(consys_struct *orig_sys) ;

/*
  It could happen that there are no changes, in which case there's no point
  in going through the motions.
*/
  if (flgoff(orig_lp->ctlopts,
	     lpctlLBNDCHG|lpctlUBNDCHG|lpctlOBJCHG|lpctlRHSCHG))
  {
#   ifndef DYLP_NDEBUG
    if (dy_opts->print.crash >= 1)
      dyio_outfmt(dy_logchn,dy_gtxecho,
		  "\n  no data structure changes at hot start.") ;
#   endif
    hot_updateMiscState(lpINV) ;
    return (dyrOK) ; }
/*
  But it's far more likely there are changes, and we need to get on with them.
*/
# ifndef DYLP_NDEBUG
  if (dy_opts->print.crash >= 1)
  { dyio_outfmt(dy_logchn,dy_gtxecho,
		"\n  updating data structures at hot start ...") ;
    if (dy_opts->print.crash >= 2)
    { dyio_outfmt(dy_logchn,dy_gtxecho,"\n    scanning changes to") ;
      if (flgon(orig_lp->ctlopts,lpctlRHSCHG))
	dyio_outfmt(dy_logchn,dy_gtxecho," rhs") ;
      if (flgon(orig_lp->ctlopts,lpctlLBNDCHG))
	dyio_outfmt(dy_logchn,dy_gtxecho," vlb") ;
      if (flgon(orig_lp->ctlopts,lpctlUBNDCHG))
	dyio_outfmt(dy_logchn,dy_gtxecho," vub") ;
      if (flgon(orig_lp->ctlopts,lpctlOBJCHG))
	dyio_outfmt(dy_logchn,dy_gtxecho," obj") ;
      dyio_outfmt(dy_logchn,dy_gtxecho," ...") ; } }
#   endif

/*
  Transfer any changes from the client's system to the scaled local copy, if
  it exists. Then set up convenient handles for the various vectors.
*/
  dy_refreshlclsystem(orig_lp->ctlopts) ;

  orig_sys = orig_lp->consys ;
  dyrhs = dy_sys->rhs ;
  ogrhs = orig_sys->rhs ;
  ogvlb = orig_sys->vlb ;
  dyvlb = dy_sys->vlb ;
  ogvub = orig_sys->vub ;
  dyvub = dy_sys->vub ;
  ogobj = orig_sys->obj ;
  dyobj = dy_sys->obj ;
  ogstatus = orig_lp->status ;
/*
  If any of the rhs or bounds have been changed, we need to reinstall the rhs
  and bounds.  Begin by scanning the orig_sys rhs array, updating the dy_sys
  entries for the active constraints. If a range constraint comes by, we also
  need to set the upper bound of the associated logical.
*/
  if (flgon(orig_lp->ctlopts,lpctlLBNDCHG|lpctlUBNDCHG|lpctlRHSCHG))
  { 
    for (aindx = 1 ; aindx <= dy_sys->concnt ; aindx++)
    { oaindx = dy_actcons[aindx] ;
      if (oaindx > 0)
      { dyrhs[aindx] = ogrhs[oaindx] ;
	if (dy_sys->ctyp[aindx] == contypRNG)
	{ dy_sys->rhslow[aindx] = orig_sys->rhslow[oaindx] ;
	  dyvub[aindx] = dyrhs[aindx]-dy_sys->rhslow[aindx] ; } } } }
/*
  We need to scan the columns no matter what changed.  Objective coefficient
  changes are just copied into the active system as needed. The real action
  is updating bounds and dealing with the side effects of bounded variables.
    * Recalculate the contribution to inactzcorr for each inactive variable.
    * Update dy_sys->vlb, dy_sys->vub, and dy_sys->obj for each active
      variable.
    * Update dy_status for each active variable.
    * Update dy_x for each nonbasic active variable.
    * Update loadable/unloadable accounting.
*/
  dy_lp->inactzcorr = 0 ;
  lpret = lpINV ;
  dy_lp->sys.vars.loadable = 0 ;
  dy_lp->sys.vars.unloadable = 0 ;
  for (oxkndx = 1 ; oxkndx <= orig_sys->varcnt ; oxkndx++)
  { xkndx = dy_origvars[oxkndx] ;
    lbj = ogvlb[oxkndx] ;
    ubj = ogvub[oxkndx] ;
    if (ogvlb[oxkndx] > ogvub[oxkndx])
    { lpret = lpINFEAS ;
#     ifndef DYLP_NDEBUG
      if (dy_opts->print.setup >= 1)
      { dyio_outfmt(dy_logchn,dy_gtxecho,
		 "\n\tTrivial infeasibility for %s (%d), lb = %g > ub = %g.",
		 consys_nme(orig_sys,'v',oxkndx,0,0),oxkndx,
		 ogvlb[oxkndx],ogvub[oxkndx]) ; }
#     endif
    }
/*
  Inactive variables: update the status in dy_origvars and calculate the
  contribution to inactzcorr. If we've reloaded rhs and rhslow, correct
  them to account for the value of the variable.

  Active variables: touch up bounds for fixed variables, update vlb, vub,
  and obj arrays for dy_sys, update dy_status, and update dy_x for nonbasic
  variables.
*/
    if (xkndx < 0)
    { if (process_inactive(orig_lp,oxkndx) == FALSE) return (dyrFATAL) ;
      statk = (flags) -dy_origvars[oxkndx] ;
      if (flgon(statk,vstatNOLOAD))
      { dy_lp->sys.vars.unloadable++ ; }
      else
      { dy_lp->sys.vars.loadable++ ; } }
    else
    { process_active(orig_lp,oxkndx) ; } }
/*
  Now, what do we need? Calculate primal values first.  If we calculate new
  primal variables, we need to reset the status of the basic variables, which
  means we need to do a quick scan of the logicals to reset their status.
  Arguably this is not necessary if only the objective changed, but overall
  it's a good investment of our time.
*/
  if (dy_calcprimals() == FALSE)
  { errmsg(316,rtnnme,dy_sys->nme) ;
    return (dyrFATAL) ; }
  for (xkndx = 1 ; xkndx <= dy_sys->concnt ; xkndx++)
  { if (dy_var2basis[xkndx] != 0)
    { if (dyvub[xkndx] == dyvlb[xkndx])
	dy_status[xkndx] = vstatBFX ;
      else
	dy_status[xkndx] = vstatB ; } }
  dy_setfinalstatus() ;
/*
  Is the phase I objective installed? If so, remove it. This hurts a bit,
  particularly if we ultimately end up targetting primal phase I as the
  starting simplex, but it's the only way to test for a dual feasible start.
  And if we have dual feasibility, it's a big win.
*/
  if (dy_lp->p1obj.installed == TRUE)
  { if (dy_swapobjs(dyPRIMAL2) == FALSE)
    { errmsg(318,rtnnme,dy_sys->nme,dy_prtlpphase(dy_lp->phase,TRUE),
	     dy_lp->tot.iters,"remove") ;
      return (dyrFATAL) ; } }
/*
  Calculate duals and reduced costs and see if we're primal or dual feasible.
  Calculate the objective just for kicks.
*/
  dy_calcduals() ;
  if (dy_calccbar() == FALSE)
  { errmsg(384,rtnnme,dy_sys->nme,
	   dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters) ;
    return (dyrFATAL) ; }
  dy_lp->z = dy_calcobj() ;

  calcflgs = ladPRIMFEAS|ladPFQUIET|ladDUALFEAS|ladDFQUIET ;
  retval = dy_accchk(&calcflgs) ;
  if (retval != dyrOK)
  { errmsg(304,rtnnme,dy_sys->nme,
	   dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters) ;
    return (retval) ; }
  if (flgoff(calcflgs,ladPRIMFEAS))
  { dy_lp->simplex.next = dyPRIMAL2 ; }
  else
  if (flgoff(calcflgs,ladDUALFEAS))
  { dy_lp->simplex.next = dyDUAL ; }
  else
  { dy_lp->simplex.next = dyPRIMAL1 ; }
/*
  Reset a few control variables and counts in dy_lp.
*/
  hot_updateMiscState(lpret) ;
/*
  And that should do it. Let's make a paranoid check or two, then we're
  off and running.
*/
# ifdef DYLP_PARANOIA
  if (dy_chkdysys(orig_sys) == FALSE) return (dyrFATAL) ;
# endif
/*
  Now, is the client forcing the full system on top of the hot start? If so,
  do it here. We're up and running at this point, so dy_forceFull can do its
  thing.

  Normally, dy_forceFull is called when we've failed at primal simplex with a
  partial system, then tried and failed to force dual feasibility. Make it
  look like this while we're working.  Reset phase to dyINIT and dy_lp->lpret
  to dyrINV when we're done so that dylp() sees the codes it expects.

  This is an exceptional activity, so I'm not going out of my way to do this
  in the most efficient manner. There really isn't a legitimate reason for
  this --- it's most likely careless coding on the part of the client, but we
  can cope without too much trouble.

  TODO (100817) I might want to rethink this, because I'm going to take the
  attitude that the OsiSimplex interface will force the full system from
  enableFactorization and enableSimplexInterface.
*/
  if (dy_opts->fullsys == TRUE &&
      (dy_lp->sys.cons.loadable > 0 || dy_lp->sys.vars.loadable > 0))
  {
#   ifndef DYLP_NDEBUG
    if (dy_opts->print.force >= 1)
    { dyio_outfmt(dy_logchn,dy_gtxecho,"\n  Forcing full system.") ; }
#   endif
    dy_lp->lpret = lpFORCEDUAL ;
    dy_lp->phase = dyFORCEFULL ;
    phase = dy_forceFull(orig_sys) ;
    if (phase == dyINV)
    { retval = dyrFATAL ; }
    else
    { dy_lp->lpret = lpINV ;
      dy_lp->phase = dyINIT ;
      retval = dyrOK ; } }
  else
  { retval = dyrOK ; }

  return (retval) ; }
Exemplo n.º 2
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.º 3
0
dyret_enum dy_warmstart (lpprob_struct *orig_lp)

/*
  This routine is responsible for recreating the active constraint system,
  basis, and status specified by the user in orig_lp. It will handle even the
  pathological case of 0 active constraints and 0 active variables. If the
  user has supplied an active variable vector, only those variables will be
  activated. Clearly, the supplied basis, status, and active variable vector
  should be consistent, or bad things will happen.

  If we're operating in fullsys mode, we need to check here for additions to
  the constraint system.

  << In the very near future, this routine should also be upgraded to cope
     with the possibility that constraints specified in the warm start basis
     have disappeared. >>

  Parameters:
    orig_lp:	The original lp problem structure

  Returns: dyrOK if the setup completes without error, any of a number of
	   error codes otherwise (dyrFATAL, dyrINV, or a code from dy_factor)
*/

{ int vndx,dyvndx,bpos,cndx,dycndx,dycsze,dyvsze,nbfxcnt ;
  double *vlb,*vub,vlbj,vubj,obj ;
  consys_struct *orig_sys ;
  flags *orig_status,vstat,calcflgs ;
  dyret_enum retval ;
  basisel_struct *orig_basis ;
  bool *orig_actvars,rngseen,noactvarspec ;
  pkvec_struct *pkcol ;
  char nmebuf[50] ;

  flags parts = CONSYS_OBJ|CONSYS_VUB|CONSYS_VLB|CONSYS_RHS|CONSYS_RHSLOW|
		CONSYS_VTYP|CONSYS_CTYP,
	opts = CONSYS_LVARS|CONSYS_WRNATT ;
  
  const char *rtnnme = "dy_warmstart" ;

  extern void dy_setfinalstatus(void) ;		/* dy_hotstart.c */

# if defined(DYLP_PARANOIA) || !defined(DYLP_NDEBUG)
  double xi ;
# endif

  retval = dyrINV ;
  nbfxcnt = -1 ;

/*
  Do a little unpacking.
*/
  orig_sys = orig_lp->consys ;
  orig_status = orig_lp->status ;
  orig_basis = orig_lp->basis->el ;
  if (flgon(orig_lp->ctlopts,lpctlACTVARSIN) && dy_opts->fullsys == FALSE)
  { orig_actvars = orig_lp->actvars ;
    noactvarspec = FALSE ; }
  else
  { orig_actvars = NULL ;
    noactvarspec = TRUE ; }
/*
  Initialise the statistics on loadable/unloadable variables and constraints.
*/
  dy_lp->sys.forcedfull = FALSE ;
  dy_lp->sys.vars.loadable = orig_sys->varcnt ;
  dy_lp->sys.vars.unloadable = 0 ;
  dy_lp->sys.cons.loadable = orig_sys->concnt ;
  dy_lp->sys.cons.unloadable = 0 ;
/*
  Create the dy_sys constraint system to match the user's basis and active
  variables (if specified). We'll create the system with logicals enabled.
  
  For variables, if there is an active variable vector, skim it for a count.
  Otherwise, skim the status array and count the number of nonbasic fixed
  variables (which will never become active).

  For constraints, we need to consider the possibility that the user has
  added cuts and is trusting dylp to deal with it. If we're operating in the
  usual dynamic mode, this will be picked up automatically, and we can size
  the constraint system to the active constraints of the basis. But if we're
  operating in fullsys mode, we need to add them here. In this case, the
  number of constraints is the current size of the constraint system.

  Take this opportunity to clean the bounds arrays, making sure that bounds
  within the feasibility tolerance of one another are set to be exactly
  equal.  (This simplifies handling fixed variables.) For nonbasic variables,
  force the status to NBFX and cancel activation if actvars is present. Basic
  variables which need BFX are picked up later, after the basis is
  established.
*/
  vub = orig_sys->vub ;
  vlb = orig_sys->vlb ;
  dyio_outfxd(nmebuf,-((int) (sizeof(nmebuf)-1)),
	      'l',"%s[actv]",orig_sys->nme) ;
  if (noactvarspec == FALSE)
  { dyvsze = 0 ;
    for (vndx = 1 ; vndx <= orig_sys->varcnt ; vndx++)
    { vlbj = vlb[vndx] ;
      vubj = vub[vndx] ;
      if (atbnd(vlbj,vubj))
      { if (vlbj != vubj)
	{ 
#	  ifndef DYLP_NDEBUG
	  if (dy_opts->print.setup >= 3)
	  { dyio_outfmt(dy_logchn,dy_gtxecho,
		        "\n\tForcing equal bound %g for %s (%d)",
		        (vlbj+vubj)/2,consys_nme(orig_sys,'v',vndx,0,0),vndx) ;
	    dyio_outfmt(dy_logchn,dy_gtxecho,
		        "\n\t  original lb = %g, ub = %g, diff = %g, tol = %g",
		        vlbj,vubj,vubj-vlbj,dy_tols->pfeas) ; }
#	  endif
	  vlb[vndx] = (vlbj+vubj)/2 ;
	  vub[vndx] = vlb[vndx] ; }
	if (((int) orig_status[vndx]) > 0)
	{ orig_status[vndx] = vstatNBFX ;
	  orig_actvars[vndx] = FALSE ; } }
      if (vlb[vndx] > vub[vndx])
      { dy_lp->lpret = lpINFEAS ;
#     ifndef DYLP_NDEBUG
      if (dy_opts->print.setup >= 1)
      { dyio_outfmt(dy_logchn,dy_gtxecho,
	       "\n\tTrivial infeasibility for %s (%d), lb = %g > ub = %g.",
	       consys_nme(orig_sys,'v',vndx,0,0),vndx,vlb[vndx],vub[vndx]) ; }
#     endif
      }
      if (orig_actvars[vndx] == TRUE) dyvsze++ ; } }
  else
  { nbfxcnt = 0 ;
    for (vndx = 1 ; vndx <= orig_sys->varcnt ; vndx++)
    { vlbj = vlb[vndx] ;
      vubj = vub[vndx] ;
      if (atbnd(vlbj,vubj))
      { if (vlbj != vubj)
	{ 
#	  ifndef DYLP_NDEBUG
	  if (dy_opts->print.setup >= 3)
	  { dyio_outfmt(dy_logchn,dy_gtxecho,
			"\n\tForcing equal bound %g for %s (g)",
		        (vlbj+vubj)/2,consys_nme(orig_sys,'v',vndx,0,0),vndx) ;
	    dyio_outfmt(dy_logchn,dy_gtxecho,
		   "\n\t  original lb = %g, ub = %g, diff = %g, tol = %g",
		   vlbj,vubj,vubj-vlbj,dy_tols->pfeas) ; }
#	  endif
	  vlb[vndx] = (vlbj+vubj)/2 ;
	  vub[vndx] = vlb[vndx] ; }
	if (((int) orig_status[vndx]) > 0)
	{ orig_status[vndx] = vstatNBFX ; } }
      if (vlb[vndx] > vub[vndx])
      { dy_lp->lpret = lpINFEAS ; }
      if ((((int) orig_status[vndx]) > 0) &&
	  flgon(orig_status[vndx],vstatNBFX))
      { nbfxcnt++ ; } }
    dyvsze = orig_sys->varcnt-nbfxcnt ; }
  if (dy_opts->fullsys == TRUE)
    dycsze = orig_sys->concnt ;
  else
    dycsze = orig_lp->basis->len ;
  dyvsze += dycsze ;
# ifndef DYLP_NDEBUG
  if (dy_opts->print.setup >= 1)
  { dyio_outfmt(dy_logchn,dy_gtxecho,
		"\n  creating constraint system %s (%d x %d+%d)",
		nmebuf,dycsze,dyvsze-dycsze,dycsze) ;
    if (dy_opts->print.setup >= 3)
    { if (flgoff(orig_lp->ctlopts,lpctlACTVARSIN))
        dyio_outfmt(dy_logchn,dy_gtxecho,
		    "\n      %d nonbasic fixed variables excluded.",
		    nbfxcnt) ; } }
# endif
  dy_sys = consys_create(nmebuf,parts,opts,dycsze,dyvsze,dy_tols->inf) ;
  if (dy_sys == NULL)
  { errmsg(152,rtnnme,nmebuf) ;
    return (dyrFATAL) ; }
/*
  Hang a set of translation vectors onto each system: origcons and origvars
  on orig_sys, and actcons and actvars on dy_sys.
*/
  if (consys_attach(dy_sys,CONSYS_ROW,
		    sizeof(int),(void **) &dy_actvars) == FALSE)
  { errmsg(100,rtnnme,dy_sys->nme,"active -> original variable map") ;
    return (dyrFATAL) ; }
  if (consys_attach(dy_sys,CONSYS_COL,
		    sizeof(int),(void **) &dy_actcons) == FALSE)
  { errmsg(100,rtnnme,dy_sys->nme,"active -> original constraint map") ;
    return (dyrFATAL) ; }
  if (consys_attach(orig_sys,CONSYS_ROW,
		    sizeof(int),(void **) &dy_origvars) == FALSE)
  { errmsg(100,rtnnme,orig_sys->nme,"original -> active variable map") ;
    return (dyrFATAL) ; }
  if (consys_attach(orig_sys,CONSYS_COL,
		    sizeof(int),(void **) &dy_origcons) == FALSE)
  { errmsg(100,rtnnme,orig_sys->nme,"original -> active constraint map") ;
    return (dyrFATAL) ; }
/*
  dy_origvars is cleared to 0 as it's attached, indicating that the original
  variables have no predefined status. We need to correct this.

  If the caller's supplied an active variable vector, we can use it to
  activate variables prior to adding constraints. (But in any case don't
  activate nonbasic fixed variables.) It's illegal to declare a formerly
  basic variable to be inactive by the simple expedient of setting
  actvars[vndx] = FALSE, hence the paranoid check.

  Otherwise, we'll need to depend on dy_loadcon to activate the variables
  referenced in the active constraints. We'll still fill in origvars, with
  two purposes:
    * We can avoid activating nonbasic fixed variables.
    * We can use dy_origvars == 0 as a paranoid check from here on out.
  Inactive variables are required to be nonbasic, so in this case the proper
  status for formerly basic variables is SB.
*/
  if (noactvarspec == FALSE)
  { 
#   ifndef DYLP_NDEBUG
    if (dy_opts->print.setup >= 1)
    { dyio_outfmt(dy_logchn,dy_gtxecho,
		  "\n  processing active variable list ...") ; }
#   endif
    pkcol = pkvec_new(0) ;
    for (vndx = 1 ; vndx <= orig_sys->varcnt ; vndx++)
    { if (((int) orig_status[vndx]) > 0)
	vstat = orig_status[vndx] ;
      else
	vstat = vstatB ;
      if (orig_actvars[vndx] == TRUE && flgoff(vstat,vstatNBFX))
      { if (consys_getcol_pk(orig_sys,vndx,&pkcol) == FALSE)
	{ errmsg(122,rtnnme,orig_sys->nme,"variable",
		 consys_nme(orig_sys,'v',vndx,TRUE,NULL),vndx) ;
	  retval = dyrFATAL ;
	  break ; }
	if (consys_addcol_pk(dy_sys,vartypCON,pkcol,
			     orig_sys->obj[vndx],vlb[vndx],vub[vndx]) == FALSE)
	{ errmsg(156,rtnnme,"variable",dy_sys->nme,pkcol->nme) ;
	  retval = dyrFATAL ;
	  break ; }
	dyvndx = pkcol->ndx ;
	dy_origvars[vndx] = dyvndx ;
	dy_actvars[dyvndx] = vndx ;
#       ifndef DYLP_NDEBUG
	if (dy_opts->print.setup >= 3)
	{ dyio_outfmt(dy_logchn,dy_gtxecho,
		      "\n\tactivating %s variable %s (%d) to index %d.",
		      consys_prtvartyp(orig_sys->vtyp[vndx]),
		      consys_nme(orig_sys,'v',vndx,FALSE,NULL),vndx,dyvndx) ; }
#       endif
      }
      else
      {
#       ifdef DYLP_PARANOIA
	if (flgon(vstat,vstatBASIC))
	{ errmsg(380,rtnnme,orig_sys->nme,
		 consys_nme(orig_sys,'v',vndx,FALSE,NULL),vndx,
		 dy_prtvstat(vstat),"non-basic") ;
	  retval = dyrFATAL ;
	  break ; }
#	endif
	dy_origvars[vndx] = -((int) vstat) ; } }
    pkvec_free(pkcol) ;
    if (retval != dyrINV) return (retval) ; }
  else
  { for (vndx = 1 ; vndx <= orig_sys->varcnt ; vndx++)
    { if (((int) orig_status[vndx]) > 0)
	vstat = orig_status[vndx] ;
      else
	vstat = vstatSB ;
      MARK_INACTIVE_VAR(vndx,-((int) vstat)) ; } }
/*
  Walk the basis and install the constraints in order. When we're finished
  with this, the active system will be up and about. In the case where
  there's no active variable specification, some of the status information
  written into dy_origvars may have been overwritten; only variables with
  vstatNBFX are guaranteed to remain inactive.
*/
  rngseen = FALSE ;
  for (bpos = 1 ; bpos <= orig_lp->basis->len ; bpos++)
  { cndx = orig_basis[bpos].cndx ;
#   ifndef DYLP_NDEBUG
    if (dy_opts->print.setup >= 2)
      dyio_outfmt(dy_logchn,dy_gtxecho,
		  "\n    activating %s %s (%d) in pos'n %d",
		  consys_prtcontyp(orig_sys->ctyp[cndx]),
		  consys_nme(orig_sys,'c',cndx,FALSE,NULL),cndx,bpos) ;
#   endif
#   ifdef DYLP_STATISTICS
    if (dy_stats != NULL) dy_stats->cons.init[cndx] = TRUE ;
#   endif
    if (dy_loadcon(orig_sys,cndx,noactvarspec,NULL) == FALSE)
    { errmsg(430,rtnnme,
	     dy_sys->nme,dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters,
	     "activate","constraint",
	     consys_nme(orig_sys,'c',cndx,TRUE,NULL),cndx) ;
      return (dyrFATAL) ; }
    if (orig_sys->ctyp[cndx] == contypRNG) rngseen = TRUE ; }
/*
  If we're in fullsys mode, repeat constraint installation actions for any
  cuts added after this basis was assembled.
*/
  if (dy_opts->fullsys == TRUE)
  { for (cndx = orig_lp->basis->len+1 ; cndx <= orig_sys->concnt ; cndx++)
    { 
#     ifndef DYLP_NDEBUG
      if (dy_opts->print.setup >= 2)
	dyio_outfmt(dy_logchn,dy_gtxecho,
		    "\n    activating %s %s (%d) in pos'n %d",
		    consys_prtcontyp(orig_sys->ctyp[cndx]),
		    consys_nme(orig_sys,'c',cndx,FALSE,NULL),cndx,cndx) ;
#     endif
#     ifdef DYLP_STATISTICS
      if (dy_stats != NULL) dy_stats->cons.init[cndx] = TRUE ;
#     endif
      if (dy_loadcon(orig_sys,cndx,noactvarspec,NULL) == FALSE)
      { errmsg(430,rtnnme,
	       dy_sys->nme,dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters,
	       "activate","constraint",
	       consys_nme(orig_sys,'c',cndx,TRUE,NULL),cndx) ;
	return (dyrFATAL) ; }
      if (orig_sys->ctyp[cndx] == contypRNG) rngseen = TRUE ; } }
# ifdef DYLP_PARANOIA
/*
  Paranoid checks and informational print statements.
*/
  if (dy_chkdysys(orig_sys) == FALSE) return (dyrINV) ;
# endif
# ifndef DYLP_NDEBUG
  if (dy_opts->print.setup >= 1)
  { dyio_outfmt(dy_logchn,dy_gtxecho,
		"\n    system %s has %d constraints, %d+%d variables",
	        dy_sys->nme,dy_sys->concnt,dy_sys->archvcnt,dy_sys->logvcnt) ;
    dyio_outfmt(dy_logchn,dy_gtxecho,
	  "\n    %d constraints, %d variables remain inactive in system %s.",
	  orig_sys->concnt-dy_sys->concnt,orig_sys->archvcnt-dy_sys->archvcnt,
	  orig_sys->nme) ;
    if (dy_opts->print.setup >= 4)
    { nbfxcnt = 0 ;
      for (vndx = 1 ; vndx <= orig_sys->varcnt ; vndx++)
      { if (INACTIVE_VAR(vndx))
	{ vstat = (flags) (-dy_origvars[vndx]) ;
	  switch (getflg(vstat,vstatSTATUS))
	  { case vstatNBUB:
	    { xi = orig_sys->vub[vndx] ;
	      break ; }
	    case vstatNBLB:
	    case vstatNBFX:
	    { xi = orig_sys->vlb[vndx] ;
	      break ; }
	    case vstatNBFR:
	    { xi = 0 ;
	      break ; }
	    default:
	    { errmsg(433,rtnnme,dy_sys->nme,
		     dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters,
		     "inactive",consys_nme(orig_sys,'v',vndx,TRUE,NULL),
		     vndx,dy_prtvstat(vstat)) ;
	      return (dyrINV) ; } }
	  if (xi != 0)
	  { if (nbfxcnt == 0)
	      dyio_outfmt(dy_logchn,dy_gtxecho,
			  "\n\tinactive variables with nonzero values:") ;
	    nbfxcnt++ ;
	    dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t%s (%d) = %g, status %s",
		        consys_nme(orig_sys,'v',vndx,FALSE,NULL),vndx,xi,
		        dy_prtvstat(vstat)) ; } } }
      if (nbfxcnt == 0)
	dyio_outfmt(dy_logchn,dy_gtxecho,
		    "\n\tall inactive variables are zero.") ; } }
# endif
/*
  Time to assemble the basis. Attach the basis and inverse basis vectors to
  the constraint system. consys_attach will initialise them to 0.
*/
  if (consys_attach(dy_sys,CONSYS_COL,
		    sizeof(int),(void **) &dy_basis) == FALSE)
  { errmsg(100,rtnnme,dy_sys->nme,"basis vector") ;
    return (dyrFATAL) ; }
  if (consys_attach(dy_sys,CONSYS_ROW,
		    sizeof(int),(void **) &dy_var2basis) == FALSE)
  { errmsg(100,rtnnme,dy_sys->nme,"inverse basis vector") ;
    return (dyrFATAL) ; }
# ifndef DYLP_NDEBUG
  if (dy_opts->print.crash >= 1)
  { if (dy_opts->print.setup == 0)
      dyio_outfmt(dy_logchn,dy_gtxecho,
		  "\n %s: regenerating the basis ...",rtnnme) ;
    else
      dyio_outfmt(dy_logchn,dy_gtxecho,
		  "\n  regenerating the basis.",rtnnme) ; }
# endif
/*
  Load the basis. For variables, we need to translate architecturals using
  dy_origvars, and watch out for logicals (vndx = negative of associated
  constraint index). After all the paranoia, we finally update dy_basis and
  dy_var2basis.

  Because we loaded the constraints in the order they were listed in the
  basis, we should have that dycndx = bpos, hence dy_actcons[bpos] = cndx.

  If we're installing a basic variable, it should be active already.  For
  architectural variables, the check is made in dy_origvars.  For a logical,
  the associated constraint should be active, hence a non-zero entry in
  dy_origcons.  For architecturals, we also check if there are any non-zero
  coefficients remaining in the column (who knows what the user has done to
  the constraint system).  This rates a message if the print level is high
  enough, but the basis pacakge is capable of patching the basis. (Indeed,
  it's hard to do it correctly here.) 
*/
# ifdef DYLP_PARANOIA
  pkcol = pkvec_new(0) ;
  retval = dyrOK ;
# endif
  for (bpos = 1 ; bpos <= orig_lp->basis->len ; bpos++)
  { cndx = orig_basis[bpos].cndx ;
    dycndx = dy_origcons[cndx] ;
    vndx = orig_basis[bpos].vndx ;
    if (vndx < 0)
    { dyvndx = dy_origcons[-vndx] ; }
    else
    { dyvndx = dy_origvars[vndx] ; }

#   ifdef DYLP_PARANOIA
    if (dycndx <= 0)
    { errmsg(369,rtnnme,orig_sys->nme,"constraint",
	     consys_nme(orig_sys,'c',cndx,FALSE,NULL),cndx,
	     "cons",cndx,dycndx) ;
      retval = dyrINV ;
      break ; }
    if (dy_actcons[bpos] != cndx)
    { errmsg(370,rtnnme,dy_sys->nme,
	     consys_nme(orig_sys,'c',cndx,FALSE,NULL),cndx,bpos,
	     consys_nme(orig_sys,'c',dy_actcons[bpos],FALSE,NULL),
	     dy_actcons[bpos]) ;
      if (dycndx != bpos) { errmsg(1,rtnnme,__LINE__) ; }
      retval = dyrINV ;
      break ; }

    if (vndx < 0)
    { if (dyvndx <= 0)
      { errmsg(369,rtnnme,orig_sys->nme,"constraint",
	       consys_nme(orig_sys,'c',-vndx,FALSE,NULL),-vndx,
	       "cons",-vndx,dyvndx) ;
	retval = dyrINV ;
	break ; } }
    else
    { if (dyvndx <= 0)
      { errmsg(369,rtnnme,orig_sys->nme,"variable",
	       consys_nme(orig_sys,'v',vndx,FALSE,NULL),vndx,
	       "vars",vndx,dyvndx) ;
	retval = dyrINV ;
	break ; }
      if (consys_getcol_pk(dy_sys,dyvndx,&pkcol) == FALSE)
      { errmsg(122,rtnnme,orig_sys->nme,"variable",
	       consys_nme(orig_sys,'v',vndx,TRUE,NULL),vndx) ;
	retval = dyrFATAL ;
	break ; }
      if (pkcol->cnt == 0 && dy_opts->print.crash >= 4)
      { dyio_outfmt(dy_logchn,dy_gtxecho,
		    "\n      %s (%d) has no non-zeros in active constraints.",
		    consys_nme(dy_sys,'v',dyvndx,TRUE,NULL),dyvndx) ; } }
#   endif

    dy_basis[dycndx] = dyvndx ;
    dy_var2basis[dyvndx] = dycndx ; }
/*
  If we're in fullsys mode, make the logical basic for any remaining
  constraints.
*/
  if (dy_opts->fullsys == TRUE)
  { for ( ; bpos <= dy_sys->concnt ; bpos++)
    { dy_basis[bpos] = bpos ;
      dy_var2basis[bpos] = bpos ; } }

# ifdef DYLP_PARANOIA
  pkvec_free(pkcol) ;
  if (retval != dyrOK) return (retval) ;
# endif

# ifndef DYLP_NDEBUG
  if (dy_opts->print.crash >= 4)
  { dyio_outfmt(dy_logchn,dy_gtxecho,
		"\n\t    Pos'n Variable           Constraint") ;
    for (bpos = 1 ; bpos <= orig_lp->basis->len ; bpos++)
    { vndx = dy_basis[bpos] ;
      dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t     %3d  (%3d) %-15s",bpos,vndx,
		  consys_nme(dy_sys,'v',vndx,FALSE,NULL)) ;
      dyio_outfmt(dy_logchn,dy_gtxecho,"%-15s",
		  consys_nme(dy_sys,'c',bpos,FALSE,NULL)) ; } }
# endif

/*
  Factor the basis. We don't want any of the primal or dual variables
  calculated just yet. If this fails we're in deep trouble. Don't do this
  if we're dealing with a constraint system with no constraints!
*/
  if (dy_sys->concnt > 0)
  {
#   ifndef DYLP_NDEBUG
    if (dy_opts->print.crash >= 2)
      dyio_outfmt(dy_logchn,dy_gtxecho,"\n    factoring ...") ;
#   endif
    calcflgs = 0 ;
    retval = dy_factor(&calcflgs) ;
    switch (retval)
    { case dyrOK:
      case dyrPATCHED:
      { break ; }
      default:
      { errmsg(309,rtnnme,dy_sys->nme) ;
	return (retval) ; } } }
/*
  Attach and clear the vectors which will hold the status, values of primal and
  dual variables, and reduced costs.
*/
  if (consys_attach(dy_sys,CONSYS_ROW,
		    sizeof(flags),(void **) &dy_status) == FALSE)
  { errmsg(100,rtnnme,dy_sys->nme,"status vector") ;
    return (dyrFATAL) ; }
  if (consys_attach(dy_sys,CONSYS_COL,
		    sizeof(double),(void **) &dy_xbasic) == FALSE)
  { errmsg(100,rtnnme,dy_sys->nme,"basic variable vector") ;
    return (dyrFATAL) ; }
  if (consys_attach(dy_sys,CONSYS_ROW,
		    sizeof(double),(void **) &dy_x) == FALSE)
  { errmsg(100,rtnnme,dy_sys->nme,"primal variable vector") ;
    return (dyrFATAL) ; }
  if (consys_attach(dy_sys,CONSYS_COL,
		    sizeof(double),(void **) &dy_y) == FALSE)
  { errmsg(100,rtnnme,dy_sys->nme,"dual variable vector") ;
    return (dyrFATAL) ; }
  if (consys_attach(dy_sys,CONSYS_ROW,
		    sizeof(double),(void **) &dy_cbar) == FALSE)
  { errmsg(100,rtnnme,dy_sys->nme,"reduced cost vector") ;
    return (dyrFATAL) ; }
/*
  Calculate dual variables and reduced costs. Might as well make a try for a
  dual feasible start, eh?
*/
# ifndef DYLP_NDEBUG
  if (dy_opts->print.crash >= 2)
    dyio_outfmt(dy_logchn,dy_gtxecho,"\n    calculating dual values ...") ;
# endif
  dy_calcduals() ;
  if (dy_calccbar() == FALSE)
  { errmsg(384,rtnnme,dy_sys->nme,
	   dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters) ;
    return (dyrFATAL) ; }
/*
  Initialise dy_status for logicals, using dy_var2basis and dy_cbar as guides.

  We have to consider the type of constraint so that we can give artificials
  NBFX status (thus avoiding the issue of whether NBLB or NBUB gives dual
  feasibility), and so that we can check the sign of the associated reduced
  cost to determine the proper bound for the logical associated with a range
  constraint.
*/
  vlb = dy_sys->vlb ;
  vub = dy_sys->vub ;
# ifndef DYLP_NDEBUG
  if (dy_opts->print.crash >= 2)
  { dyio_outfmt(dy_logchn,dy_gtxecho,
	        "\n    establishing initial status and reference frame ...") ;
    dyio_outfmt(dy_logchn,dy_gtxecho,"\n      logicals ...") ; }
# endif
  for (dyvndx = 1 ; dyvndx <= dy_sys->concnt ; dyvndx++)
  { if (dy_var2basis[dyvndx] != 0)
    { if (vub[dyvndx] == vlb[dyvndx])
	dy_status[dyvndx] = vstatBFX ;
      else
	dy_status[dyvndx] = vstatB ; }
    else
    { switch (dy_sys->ctyp[dyvndx])
      { case contypLE:
	case contypGE:
	{ dy_status[dyvndx] = vstatNBLB ;
	  dy_x[dyvndx] = 0 ;
	  break ; }
        case contypEQ:
	{ dy_status[dyvndx] = vstatNBFX ;
	  dy_x[dyvndx] = 0 ;
	  break ; }
        case contypRNG:
	{ if (vub[dyvndx] == vlb[dyvndx])
	  { dy_status[dyvndx] = vstatNBFX ;
	    dy_x[dyvndx] = vub[dyvndx] ; }
	  else
	  if (dy_cbar[dyvndx] < 0)
	  { dy_status[dyvndx] = vstatNBUB ;
	    dy_x[dyvndx] = vub[dyvndx] ; }
	  else
	  { dy_status[dyvndx] = vstatNBLB ;
	    dy_x[dyvndx] = vlb[dyvndx] ; }
	  break ; }
	default:
	{ errmsg(1,rtnnme,__LINE__) ;
	  return (dyrFATAL) ; } } }
#   ifndef DYLP_NDEBUG
    if (dy_opts->print.crash >= 4)
    { dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t  %s (%d) %s",
		  consys_nme(dy_sys,'v',dyvndx,FALSE,NULL),dyvndx,
		  dy_prtvstat(dy_status[dyvndx])) ;
      if (flgon(dy_status[dyvndx],vstatNONBASIC|vstatNBFR))
	dyio_outfmt(dy_logchn,dy_gtxecho," with value %g.",dy_x[dyvndx]) ;
      else
	dyio_outchr(dy_logchn,dy_gtxecho,'.') ; }
#   endif
  }
/*
  Scan dy_origvars, with two purposes in mind:
    * For active architectural variables, initialise dy_status from
      orig_status, using the actual status for nonbasic variables, and
      vstatB, vstatBFX, or vstatBFR for basic variables. (We'll tune this
      once we have the values of the basic variables.) Initialise dy_x to the
      proper value for nonbasic variables. We shouldn't see NBFX here, as
      those variables should have been left inactive.
    * For inactive architectural variables, accumulate the objective function
      correction. Nonbasic free variables are assumed to have value 0.
*/
# ifndef DYLP_NDEBUG
  if (dy_opts->print.crash >= 2)
    dyio_outfmt(dy_logchn,dy_gtxecho,"\n      architecturals ...") ;
# endif
  dy_lp->inactzcorr = 0 ;
  for (vndx = 1 ; vndx <= orig_sys->varcnt ; vndx++)
  { dyvndx = dy_origvars[vndx] ;
    if (dyvndx < 0)
    { obj = orig_sys->obj[vndx] ;
      switch ((flags) (-dyvndx))
      { case vstatNBFX:
        case vstatNBLB:
	{ dy_lp->inactzcorr += obj*orig_sys->vlb[vndx] ;
	  break ; }
        case vstatNBUB:
	{ dy_lp->inactzcorr += obj*orig_sys->vub[vndx] ;
	  break ; }
#       ifdef DYLP_PARANOIA
	case vstatNBFR:
	{ break ; }
	default:
	{ errmsg(1,rtnnme,__LINE__) ;
	  return (dyrINV) ; }
#	endif
      } }
    else
    { if (((int) orig_status[vndx]) < 0)
      { if (vlb[dyvndx] == vub[dyvndx])
	  dy_status[dyvndx] = vstatBFX ;
	else
	if (vlb[dyvndx] <= -dy_tols->inf && vub[dyvndx] >= dy_tols->inf)
	  dy_status[dyvndx] = vstatBFR ;
	else
	  dy_status[dyvndx] = vstatB ; }
      else
      { dy_status[dyvndx] = orig_status[vndx] ;
	switch (dy_status[dyvndx])
	{ case vstatNBLB:
	  { dy_x[dyvndx] = vlb[dyvndx] ;
	    break ; }
	  case vstatNBUB:
	  { dy_x[dyvndx] = vub[dyvndx] ;
	    break ; }
	  case vstatNBFR:
	  { dy_x[dyvndx] = 0 ;
	    break ; }
#	  ifdef DYLP_PARANOIA
	  default:
	  { errmsg(1,rtnnme,__LINE__) ;
	    return (dyrINV) ; }
#	  endif
	} }
#     ifndef DYLP_NDEBUG
      if (dy_opts->print.crash >= 4)
      { dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t  %s (%d) %s",
		    consys_nme(dy_sys,'v',dyvndx,FALSE,NULL),dyvndx,
		    dy_prtvstat(dy_status[dyvndx])) ;
	if (flgon(dy_status[dyvndx],vstatNONBASIC|vstatNBFR))
	  dyio_outfmt(dy_logchn,dy_gtxecho," with value %g.",dy_x[dyvndx]) ;
	else
	  dyio_outchr(dy_logchn,dy_gtxecho,'.') ; }
#     endif
    } }
/*
  Did we patch the basis? If so, we need to scan the status array and correct
  the entries for the architectural variables that were booted out during the
  patch.
*/
  if (retval == dyrPATCHED) correct_for_patch() ;
/*
  Ok, status is set. Now it's time to calculate initial values for the primal
  variables and objective.  Arguably we don't need the true objective for
  phase I, but it's cheap to calculate.  Once we have the primal variables,
  adjust the status for any that are pinned against a bound or out of bounds,
  and see how it looks, in terms of primal infeasibility.
*/
# ifndef DYLP_NDEBUG
  if (dy_opts->print.crash >= 2)
    dyio_outfmt(dy_logchn,dy_gtxecho,"\n    calculating primal values ...") ;
# endif
  if (dy_calcprimals() == FALSE)
  { errmsg(316,rtnnme,dy_sys->nme) ;
    return (dyrFATAL) ; }
  dy_lp->z = dy_calcobj() ;
  dy_setfinalstatus() ;
/*
  Make the check for primal and/or dual feasibility, and set the initial
  simplex phase accordingly.
*/
  calcflgs = ladPRIMFEAS|ladPFQUIET|ladDUALFEAS|ladDFQUIET ;
  retval = dy_accchk(&calcflgs) ;
  if (retval != dyrOK)
  { errmsg(304,rtnnme,dy_sys->nme,
	   dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters) ;
    return (retval) ; }
  if (flgoff(calcflgs,ladPRIMFEAS))
  { dy_lp->simplex.next = dyPRIMAL2 ; }
  else
  if (flgoff(calcflgs,ladDUALFEAS))
  { dy_lp->simplex.next = dyDUAL ; }
  else
  { dy_lp->simplex.next = dyPRIMAL1 ; }

# ifndef DYLP_NDEBUG
  if (dy_opts->print.crash >= 2)
  { dyio_outfmt(dy_logchn,dy_gtxecho,"\n    phase %s, initial objective %g",
	        dy_prtlpphase(dy_lp->simplex.next,FALSE),dy_lp->z) ;
    if (dy_lp->infeascnt != 0)
      dyio_outfmt(dy_logchn,dy_gtxecho,", %d infeasible vars, infeas = %g",
		  dy_lp->infeascnt,dy_lp->infeas) ;
    dyio_outchr(dy_logchn,dy_gtxecho,'.') ; }
  if (dy_opts->print.crash >= 3)
  { dyio_outfmt(dy_logchn,dy_gtxecho,
		"\n\nPos'n\tConstraint\tDual\t\tPrimal\n") ;
    for (bpos = 1 ; bpos <= dy_sys->concnt; bpos++)
    { cndx = dy_actcons[bpos] ;
      dyvndx = dy_basis[bpos] ;
      if (dyvndx <= dy_sys->concnt)
	vndx = orig_sys->varcnt+dyvndx ;
      else
	vndx = dy_actvars[dyvndx] ;
      dyio_outfmt(dy_logchn,dy_gtxecho,
		  "\n%5d\t(%4d) %-8s\t%12.4g\t(%4d) %-8s %12.4g",
		  bpos,cndx,
		  consys_nme(dy_sys,'c',bpos,FALSE,NULL),dy_y[bpos],vndx,
		  consys_nme(dy_sys,'v',dyvndx,FALSE,NULL),dy_x[dyvndx]) ; } }
# endif

  return (dyrOK) ; }
Exemplo n.º 4
0
dyret_enum dy_factor (flags *calcflgs)

/*
  This routine orchestrates the LU factorisation of the basis. The glpk
  routines do the grunt work. This routine provides the intelligence.

  If inv_decomp aborts the attempt to factor due to numerical instability, we
  tighten the pivot selection parameters one notch and try again, giving up
  only when no further increase is possible.  The sequence of values for the
  pivot selection parameters are defined in a table at the top of this file.

  If inv_decomp aborts the attempt to factor because the basis is singular,
  we correct the basis with adjust_basis and take another run at factoring.
  In the event that the basis is successfully patched, we have serious work
  to do.  See the comments with adjust_therest for further information. If
  the user has for some reason disabled basis patching, we return
  dyrSINGULAR.

  inv_decomp (actually, luf_decomp) is self-expanding --- if more space is
  needed to hold the factorization, the expansion is handled internally.
  dylp uses ladEXPAND to force basis expansion after a pivot fails due to lack
  of space. In glpk, inv_update will set instructions in the basis structure
  and luf_decomp will handle the expansion, so ladEXPAND is redundant. No
  action need be taken in this routine. It's also not possible to tell if the
  basis has been expanded, so ladEXPAND is not set on output.


  Parameters:
    calcflgs:   (i) ladPRIMALS indicates the primal variables should be
		    recalculated after factoring the basis.
		    ladDUALS indicates the dual variables should be
		    recalculated after factoring the basis.
		    ladEXPAND indicates that the basis should be expanded prior
		    to refactoring.
		(o) flags are set to indicate if the corresponding variables
		    have been recalculated.

  Returns: dyrOK if the basis is factored without incident
	   dyrPATCHED if the basis was singular and has been repaired
	   dyrSINGULAR if the basis was singular and has not been repaired
	   dyrNUMERIC if factoring failed for the strictest pivoting regimen
	   dyrFATAL for other fatal errors

  NOTE: glpinv/glpluf will crash and burn if they encounter what they consider
	to be a fatal error, rather than returning a fatal error code. This
	needs to be addressed at some point. In particular, failure to expand
	the basis, failure to load the basis from the constraint system, and
	various parameter errors fall into this category.
*/

{ int retval,patchcnt ;
  bool try_again,patched ;
  dyret_enum retcode ;
  patch_struct *patches ;

  const char *rtnnme = "dy_factor" ;

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

# ifdef DYLP_STATISTICS
  if (dy_stats != NULL)
  { int pivcnt ;
    pivcnt = dy_lp->tot.pivs-dy_stats->factor.prevpiv ;
    dy_stats->factor.avgpivs = dy_stats->factor.avgpivs*dy_stats->factor.cnt ;
    dy_stats->factor.avgpivs += pivcnt ;
    dy_stats->factor.cnt++ ;
    dy_stats->factor.avgpivs /= dy_stats->factor.cnt ;
    if (pivcnt > dy_stats->factor.maxpivs) dy_stats->factor.maxpivs = pivcnt ;
    dy_stats->factor.prevpiv = dy_lp->tot.pivs ; }
# endif

  retcode = dyrINV ;
  patchcnt = 0 ;
  patches = NULL ;

/*
  Call luf_adjustsize to set the actual size of the basis. If the allocated
  capacity is too small, it will be expanded.
*/
  luf_adjustsize() ;
/*
  Open a loop for factorisation attempts. We'll persist in the face of
  numerical stability problems as long as there's room to tighten the pivot
  selection.

  At present, glpinv/glpluf will crash and burn if they encounter fatal
  problems. The basis load is implicit --- the routine factor_loadcol is
  called from luf_decomp to load up the coefficients.
*/
  try_again = TRUE ;
  patched = FALSE ;
  while (try_again)
  { retval = inv_decomp(luf_basis,dy_sys,factor_loadcol) ;
#   ifndef DYLP_NDEBUG
    if ((retval == 0 && dy_opts->print.basis >= 4) ||
	(retval > 0 && dy_opts->print.basis >= 2))
    { dyio_outfmt(dy_logchn,dy_gtxecho,
		  "\n    (%s)%d: factored with %s, basis stability %g.",
		  dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters,
		  dy_prtpivparms(-1),luf_basis->min_vrratio) ; }
#   endif
/*
  Deal with the result. A return code of 0 means there were no difficulties;
  1 says the basis was singular and had to be patched before the
  factorisation could be completed. Either is success, and we're done.
*/
    switch (retval)
    { case 0:
      { try_again = FALSE ;
	retcode = dyrOK ;
	break ; }
/*
  Alas, the failures.

  If the problem is a singular basis (retval = 1), fix up the basis structures
  as indicated in the luf_basis structure and try again to factor the basis,
  unless the user has forbidden it.

  If the problem is numerical instability (retval = 2) try to make the pivot
  selection more stringent, and keep trying until we can try no more, at
  which point we'll return numeric instability to the caller.

  What's left is fatal confusion; pass the buck back to the caller.
*/
      case 1:
      { if (dy_opts->patch == FALSE)
	{ errmsg(308,rtnnme,dy_sys->nme,dy_prtlpphase(dy_lp->phase,TRUE),
		 dy_lp->tot.iters,dy_prtdyret(dyrSINGULAR)) ;
	  clrflg(*calcflgs,ladPRIMALS|ladDUALS) ;
	  return (dyrSINGULAR) ; }
#	ifndef DYLP_NDEBUG
	if (dy_opts->print.basis >= 2)
	{ dyio_outfmt(dy_logchn,dy_gtxecho,
		      "\n    (%s)%d: attempting to patch singular basis.",
		      dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters) ; }
#	endif
	adjust_basis(&patchcnt,&patches) ;
	patched = TRUE ;
	break ; }
      case 2:
      { retcode = dyrNUMERIC ;
#	ifndef DYLP_NDEBUG
	if (dy_opts->print.basis >= 2)
	{ dyio_outfmt(dy_logchn,dy_gtxecho,
		  "\n    (%s)%d: factor failed at %s, numerical instability,",
		  dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters,
		  dy_prtpivparms(-1)) ;
	  dyio_outfmt(dy_logchn,dy_gtxecho," max = %g, gro = %g.",
		      luf_basis->luf->big_v,luf_basis->luf->max_gro) ; }
# 	endif
	if (dy_setpivparms(+1,0) == FALSE)
	{ errmsg(307,rtnnme,dy_sys->nme,dy_prtlpphase(dy_lp->phase,TRUE),
		 dy_lp->tot.iters,dy_prtpivparms(-1)) ;
	  return (retcode) ; }
#	ifndef DYLP_NDEBUG
	if (dy_opts->print.basis >= 2)
	{ dyio_outfmt(dy_logchn,dy_gtxecho,"\n\ttrying again with %s.",
		      dy_prtpivparms(-1)) ; }
#	endif
	break ; }
      default:
      { errmsg(7,rtnnme,__LINE__,"inv_decomp return code",retval) ;
	return (dyrFATAL) ; } }
  }
/*
  If we reach here, we managed to factor the basis.  Reset the count of
  pivots since the last refactor.  If the basis was patched, we have some
  serious cleanup to do, so call adjust_therest to deal with the details.
  Otherwise, turn to the requests to calculate values for the primal and/or
  dual variables.
*/
  dy_lp->basis.etas = 0 ;
  if (patched == TRUE)
  { retcode = adjust_therest(patchcnt,patches) ;
    FREE(patches) ;
    if (retcode == dyrFATAL)
    { errmsg(306,rtnnme,dy_sys->nme,dy_prtlpphase(dy_lp->phase,TRUE),
	     dy_lp->tot.iters) ;
      return (dyrFATAL) ; }
#   ifndef DYLP_NDEBUG
    if (dy_opts->print.basis >= 1)
    { dyio_outfmt(dy_logchn,dy_gtxecho,
		  "\n\t[%s]: compensated for basis correction.",
		  dy_sys->nme) ; }
#   endif
    if (!(dy_lp->phase == dyINIT))
    { setflg(*calcflgs,ladPRIMALS|ladDUALS) ;
      if (retcode == dyrLOSTDFEAS) setflg(*calcflgs,ladDUALFEAS) ; }
    retcode = dyrPATCHED ; }
  else
  { if (flgon(*calcflgs,ladPRIMALS))
    { if (dy_calcprimals() == FALSE)
      { clrflg(*calcflgs,ladPRIMALS) ;
	return (dyrFATAL) ; } }
    if (flgon(*calcflgs,ladDUALS)) dy_calcduals() ; }

  return (retcode) ; }