Example #1
0
static bool process_inactive (lpprob_struct *orig_lp, int oxkndx)

/*
  This routine handles the data structure updates for an inactive variable
  x<k>.  We need to have a look at the bounds l<k> and u<k>, and perhaps
  update the status kept in dy_origvars. We need to add the contribution
  c<k>l<k> or c<k>u<k> to the objective function. Finally, if we've reloaded
  b & blow due to a bound or rhs change, we need to walk the column a<k>
  and adjust b<i> (and perhaps blow<i>) for each nonzero a<ik> in the active
  system.

  Parameters:
    orig_lp:	the original lp problem
    oxkndx:	index of x<k> in orig_sys
  
  Returns: TRUE if the update is made without incident, FALSE otherwise.
*/

{ int oaindx,aindx,ndx ;
  double xk,lk,uk,ck ;
  pkvec_struct *ak ;
  pkcoeff_struct *aik ;
  consys_struct *orig_sys ;
  flags xkstatus ;
  const char *rtnnme = "process_inactive" ;

  orig_sys = orig_lp->consys ;

  xkstatus = getflg(orig_lp->status[oxkndx],vstatSTATUS) ;

# ifdef DYLP_PARANOIA
/*
  Any inactive variable should be nonbasic, and the paranoid check is looking
  to make sure of this.
*/
  if (!VALID_STATUS(xkstatus))
  { errmsg(300,rtnnme,(int) xkstatus,
	   consys_nme(orig_sys,'v',oxkndx,FALSE,NULL),oxkndx) ;
    return (FALSE) ; }
  if (flgoff(xkstatus,vstatNONBASIC|vstatNBFR))
  { errmsg(433,rtnnme,
	   dy_sys->nme,dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters,
	   "inactive",consys_nme(orig_sys,'v',oxkndx,TRUE,NULL),oxkndx,
	   dy_prtvstat(xkstatus)) ;
    return (FALSE) ; }
# endif
/*
  The bounds can change arbitrarily, and the client may not be maintaining
  the status vector, but we're limited in what we can do --- bounds and status
  are our only clues to the value of an inactive variable. (Contrast with the
  equivalent section in process_active.)
*/
  lk = orig_sys->vlb[oxkndx] ;
  uk = orig_sys->vub[oxkndx] ;
  ck = orig_sys->obj[oxkndx] ;
/*
  Start with the case that both bounds are finite. Use a previous status of
  NBLB or NBUB. Otherwise, guess from the sign of the objective coefficient.
  `Dirty' fixed variables are marked as unloadable.
*/
  if (lk > -dy_tols->inf && uk < dy_tols->inf)
  { if (atbnd(lk,uk) && lk != uk)
    { if (flgon(xkstatus,vstatNBLB|vstatNBUB))
      { setflg(xkstatus,vstatNOLOAD) ; }
      else
      { if (ck < 0)
	{ xkstatus = vstatNBUB|vstatNOLOAD ; }
	else
	{ xkstatus = vstatNBLB|vstatNOLOAD ; } }
#     ifndef DYLP_NDEBUG
      if (dy_opts->print.setup >= 3)
      { dyio_outfmt(dy_logchn,dy_gtxecho,"\n\tDirty fixed variable %s (%d)",
		    consys_nme(orig_sys,'v',oxkndx,0,0),oxkndx) ;
	dyio_outfmt(dy_logchn,dy_gtxecho,
		    " assigned status %s.",dy_prtvstat(xkstatus)) ;
	dyio_outfmt(dy_logchn,dy_gtxecho,
		    "\n\t  original lb = %g, ub = %g, diff = %g, tol = %g",
		    lk,uk,uk-lk,dy_tols->pfeas) ; }
#     endif
    }
    else
    if (lk == uk)
    { xkstatus = vstatNBFX|vstatNOLOAD ; }
    else
    if (flgon(xkstatus,vstatNBLB|vstatNBUB))
    { xkstatus = orig_lp->status[oxkndx] ; }
    else
    { if (ck < 0)
      { xkstatus = vstatNBUB ; }
      else
      { xkstatus = vstatNBLB ; } } }
/*
  Variables with one bound, or no bounds. No choices here.
*/
  else
  if (lk > -dy_tols->inf)
  { xkstatus = vstatNBLB ; }
  else
  if (uk < dy_tols->inf)
  { xkstatus = vstatNBUB ; }
  else
  { xkstatus = vstatNBFR ; }
/*
  Determine the variable's value and set up the status entries.

  The default case in the switch below should never execute, but it serves
  for paranoia and lets gcc conclude xk will always have a value.

  Consider whether it's really a good idea to change orig_lp->status.
*/
  switch (getflg(xkstatus,vstatSTATUS))
  { case vstatNBLB:
    case vstatNBFX:
    { xk = lk ;
      break ; }
    case vstatNBUB:
    { xk = uk ;
      break ; }
    case vstatNBFR:
    { xk = 0 ;
      break ; }
    default:
    { xk = 0 ;
      errmsg(1,rtnnme,__LINE__) ;
      return (FALSE) ; } }
  orig_lp->status[oxkndx] = xkstatus ;
  dy_origvars[oxkndx] = -((int) xkstatus) ;
/*
  Note any contribution to the objective and constraint rhs & rhslow values.
*/
  dy_lp->inactzcorr += xk*orig_sys->obj[oxkndx] ;
  if (flgon(orig_lp->ctlopts,lpctlRHSCHG|lpctlLBNDCHG|lpctlUBNDCHG))
  { ak = NULL ;
    if (consys_getcol_pk(orig_sys,oxkndx,&ak) == FALSE)
    { errmsg(122,rtnnme,orig_sys->nme,"variable",
	     consys_nme(orig_sys,'v',oxkndx,TRUE,NULL),oxkndx) ;
      if (ak != NULL) pkvec_free(ak) ;
      return (FALSE) ; }
    for (ndx = 0, aik = &ak->coeffs[0] ; ndx < ak->cnt ; ndx++, aik++)
    { oaindx = aik->ndx ;
      if (ACTIVE_CON(oaindx))
      { aindx = dy_origcons[oaindx] ;
        dy_sys->rhs[aindx] -= aik->val*xk ;
	if (dy_sys->ctyp[aindx] == contypRNG)
	  dy_sys->rhslow[aindx] -= aik->val*xk ; } }
    pkvec_free(ak) ; }
/*
  And we're done. Print some information and return.
*/

# ifndef DYLP_NDEBUG
  if (dy_opts->print.crash >= 4)
  { dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t  %s (%d) %s inactive with value ",
	        consys_nme(orig_sys,'v',oxkndx,FALSE,NULL),oxkndx,
	        dy_prtvstat(xkstatus)) ;
    switch (getflg(xkstatus,vstatSTATUS))
    { case vstatNBFX:
      case vstatNBLB:
      case vstatNBUB:
      case vstatNBFR:
      { dyio_outfmt(dy_logchn,dy_gtxecho,"%g.",xk) ;
	break ; }
      default:
      { dyio_outfmt(dy_logchn,dy_gtxecho,"??.") ;
	break ; } } }
# endif

return (TRUE) ; }
Example #2
0
void dy_colDuals (lpprob_struct *orig_lp, double **p_cbar, bool trueDuals)

/*
  Returns the unscaled vector of duals associated with architectural columns
  (aka reduced costs), in the original system frame of reference.

  These are the duals associated with implicit bound constraints. See
  dy_rowDuals for the duals associated with explicit (architectural)
  constraints. (These latter are the usual notion of dual variables, and
  also correspond to the reduced costs of logical variables.)

  In dylp's min primal <=> min dual pairing, the reduced costs have the
  correct sign for the true dual variables used by the min dual problem,
  except that the values associated with NBUB variables need to be negated.
  If you'd prefer that the duals have a sign convention appropriate for a min
  primal, specify trueDuals = false.

  The algorithm is to walk the columns of orig_sys, copying over the reduced
  cost from dy_cbar when the variable is active, otherwise calculting cbar<j>
  on the spot.

  For active variables, we have

  sc_cbar<j> = sc_c<j> - sc_c<B>sc_inv(B)sc_a<j>
	     = c<j>S<j> - c<B>S<B>inv(S<B>)inv(B)inv(R)Ra<j>S<j>
	     = c<j>S<j> - c<B>inv(B)a<j>S<j>
	     = cbar<j>S<j>

  To unscale sc_cbar<j>, we simply multiply by 1/S<j>, keeping in mind that
  if x<j> is a logical for row i, the appropriate factor is R<i>.

  For inactive variables, we calculate dot(y,a<j>) using the scaled version
  of the original system, which leaves us with the same sc_abar<j>.

  Why not use the client's original system and the vector of unscaled duals
  returned by dy_rowDuals?  That would certainly be an option. One argument
  against it is the additional work involved to get the unscaled duals. The
  other argument is that maximising the independence of the two calculations
  means that the test routine (which confirms cbar<j> = c<j> - dot(y,a<j>)
  in the external frame) is marginally more convincing.

  Parameters:
    orig_lp:	the original lp problem
    p_cbar:	(i) pointer to vector; if NULL, a vector of the appropriate
		    size will be allocated
		(o) vector of reduced costs
    trueDuals:	true to return values with a sign convention appropriate
		for the min dual problem, false to use a sign convention that
		matches the min primal.

  Returns: undefined
*/

{ int i,j,m,n,i_orig,j_orig,m_orig,n_orig ;
  flags statj ;
  consys_struct *orig_sys ;

  double *orig_y ;
  consys_struct *scaled_orig_sys ;
  bool scaled ;
  const double *rscale,*cscale ;

  double cbarj ;
  double *cbar ;

# ifdef DYLP_PARANOIA
  char *rtnnme = "dy_colDuals" ;

  if (dy_std_paranoia(orig_lp,rtnnme) == FALSE)
  { return ; }
  if (p_cbar == NULL)
  { errmsg(2,rtnnme,"cbar") ;
    return ; }
# endif
/*
  Is unscaling required? Acquire the scaling vectors and set up scaled_orig_sys
  accordingly. We'll also need the constraint type vector so that we don't
  overcompensate for >= constraints when returning true duals.
*/
  scaled = dy_isscaled() ;
  if (scaled == TRUE)
  { dy_scaling_vectors(&rscale,&cscale) ;
    scaled_orig_sys = dy_scaled_origsys() ; }
  else
  { scaled_orig_sys = NULL ; }

  orig_sys = orig_lp->consys ;
  n_orig = orig_sys->varcnt ;
  m_orig = orig_sys->concnt ;
  n = dy_sys->varcnt ;
  m = dy_sys->concnt ;
/*
  Do we need a vector?
*/
  if (*p_cbar != NULL)
  { cbar = *p_cbar ;
    memset(cbar,0,(n_orig+1)*sizeof(double)) ; }
  else
  { cbar = (double *) CALLOC((n_orig+1),sizeof(double)) ; }
/*
  Make a vector of duals that matches orig_sys, for efficient pricing of
  inactive columns.
*/
  orig_y = (double *) CALLOC((m_orig+1),sizeof(double)) ;
  for (i = 1 ; i <= m ; i++)
  { i_orig = dy_actcons[i] ;
    orig_y[i_orig] = dy_y[i] ; }
/*
  Get on with the calculation. For an active variable, we can pull the value
  from dy_cbar. For an inactive variable, we need to calculate dot(y,a<j>).
  Then we unscale and drop the result into the proper place in the result
  vector.  Since we're starting from orig_sys, we'll never reference a column
  for a logical variable.
*/
  for (j_orig = 1 ; j_orig <= n_orig ; j_orig++)
  { if (ACTIVE_VAR(j_orig))
    { j = dy_origvars[j_orig] ;
      statj = getflg(dy_status[j],vstatSTATUS) ;
      if (flgon(statj,vstatBASIC))
      { cbarj = 0.0 ; }
      else
      { if (scaled == TRUE)
	{ cbarj = dy_cbar[j]/cscale[j_orig] ; }
	else
	{ cbarj = dy_cbar[j] ; } } }
    else
    { statj = (flags) -dy_origvars[j_orig] ;
      if (scaled == TRUE)
      { cbarj = scaled_orig_sys->obj[j_orig] ; 
	cbarj -= consys_dotcol(scaled_orig_sys,j_orig,orig_y) ;
	cbarj /= cscale[j_orig] ; }
      else
      { cbarj = orig_sys->obj[j_orig] ;
	cbarj -= consys_dotcol(orig_sys,j_orig,orig_y) ; } }
    setcleanzero(cbarj,dy_tols->cost) ;
/*
  What's our sign convention? If these values are to work with the imaginary
  true dual problem, we need to flip the sign on variables that are NBUB. If
  we're just going for the min primal convention, they're already correct.
*/
    if (trueDuals == TRUE)
    { if (flgon(statj,vstatNBUB))
	cbar[j_orig] = -cbarj ;
      else
	cbar[j_orig] = cbarj ; }
    else
      cbar[j_orig] = cbarj ; }
/*
  Clean up a bit and we're done.
*/
  if (orig_y != NULL) FREE(orig_y) ;
  *p_cbar = cbar ;

  return ; }
Example #3
0
int dytest_rowPrimals (lpprob_struct *main_lp, lptols_struct *main_lptols,
		       lpopts_struct *main_lpopts)
/*
  This routine checks the ind<B> and x<B> vectors returned by dy_rowPrimals.
  It first cross-checks the basis, status and indB arrays, bailing out if the
  cross-checks fail.
  
  Next it checks the values of the basic variables, architectural and logical.

  For basic variables x<B>, the routine checks x<B> =  inv(B)b - inv(B)Nx<N>
    To do this, it first walks the rows of the constraint system and
    initialises x<B> with dot(beta<i>,b). Then it walks the columns and
    accumulates the contributions abar<j>x<j> from nonzero nonbasic
    variables.  Finally, it walks the rows again and subtracts the
    contributions from nonbasic bounded logicals (due to range constraints
    tight at the lower bound).

  Parameters:
    main_lp:     the lp problem structure
    main_lptols: the lp tolerance structure
    main_lpopts: the lp options structure

  Returns: 0 if the basic variables validate, error count otherwise.
*/

{ int i,j,k,m,n,i_basis ;
  flags statj,stati ;
  double xj,betaidotb,tol ;

  consys_struct *sys ;
  flags *status,*logstatus ;
  double *rhs,*rhslow,*vlb,*vub,*betai,*xBaccum,*abarj ;
  contyp_enum *ctyp ;
  basisel_struct *basis ;
  int basisLen ;

  double *xB ;
  int *indB ;

  int berrs,nberrs,inderrs ;

  char *rtnnme = "dytest_rowPrimals" ;

/*
  Do a little initialisation. Mention that we've started.
*/
  sys = main_lp->consys ;
  m = sys->concnt ;
  n = sys->varcnt ;

# ifndef DYLP_NDEBUG
  if (main_lpopts->print.soln >= 1)
  { dyio_outfmt(dy_logchn,dy_gtxecho,
	  "\n%s: checking primal basic variables using %s (%d x %d).",
	  rtnnme,sys->nme,m,n) ; }
# endif
/*
  Acquire the variable bound and status vectors, the constraint type, rhs,
  and rhslow vectors, and the basis vector.
*/
  basisLen = main_lp->basis->len ;
  basis = main_lp->basis->el ;
  status = main_lp->status ;
  ctyp = sys->ctyp ;
  rhs = sys->rhs ;
  rhslow = sys->rhslow ;
  vlb = sys->vlb ;
  vub = sys->vub ;
/*
  Call dy_rowPrimals to acquire x<B> (values of basic variables) and ind<B>
  (indices of basic variables).
*/
  xB = NULL ;
  indB = NULL ;
  dy_rowPrimals(main_lp,&xB,&indB) ;
/*
  Validate ind<B>, status, and basis against each other, within the limits of
  each.

  IndB specifies basic variables in row order. Logicals are specified as the
  negative of the row. IndB contains an entry for every constraint. By
  construction, the basic variable for an inactive constraint should be the
  logical for the constraint.

  Basis has one entry for each active constraint. Each entry in basis
  specifies a constraint and a basic variable. Basic logicals are specified
  by the negative of the constraint index. Then for an active constraint i
  and a basis entry k such that basis[k].cndx == i, indB[i] == basis[k].vndx.

  Status only contains information on architecturals. A basic architectural
  is specified as the negative of its entry in the basis vector. Thus
  basis[-status[j]].vndx == j. 
*/
  inderrs = 0 ;
  for (i = 1 ; i <= m ; i++)
  { 
/*
  Scan the basis vector for an entry for this constraint. If it's not present,
  assume the constraint is inactive.
*/
    i_basis = -1 ;
    for (k = 1 ; k <= basisLen ; k++)
    { if (basis[k].cndx == i)
      { i_basis = k ;
	break ; } }
    j = indB[i] ;
/*
  Inactive constraints should specify the associated logical as the basic
  variable.
*/
    if (i_basis < 0)
    { if (j > 0)
      { inderrs++ ;
	dyio_outfmt(dy_logchn,dy_gtxecho,"\nERROR: constraint %s (%d)",
		    consys_nme(sys,'c',i,FALSE,NULL),i) ;
	dyio_outfmt(dy_logchn,dy_gtxecho,
		    "; basis entry = %d; should specify a logical.",j) ; }
      else
      if (-j != i)
      { inderrs++ ;
	dyio_outfmt(dy_logchn,dy_gtxecho,"\nERROR: basis[%d] (%s)",
		    i,consys_nme(sys,'c',i,FALSE,NULL)) ;
	dyio_outfmt(dy_logchn,dy_gtxecho," is %s (%d);",
		    consys_nme(sys,'c',n-j,FALSE,NULL),-j) ;
	dyio_outfmt(dy_logchn,dy_gtxecho," expected %s (%d).",
		    consys_nme(sys,'c',n+i,FALSE,NULL),i) ; } }
/*
  The constraint is active. We should have indB[i] = basis[i_basis].vndx. It
  takes way more work than it should to construct the error message.
*/
    else
    { k = basis[i_basis].vndx ;
      if (j != k)
      { inderrs++ ;
	dyio_outfmt(dy_logchn,dy_gtxecho,"\nERROR: constraint %s (%d)",
		    consys_nme(sys,'c',i,FALSE,NULL),i) ;
	statj = (k < 0)?(n-k):(k) ;
	dyio_outfmt(dy_logchn,dy_gtxecho,
		    "; basis[%d] specifies %s (%d)",
		    i_basis,consys_nme(sys,'v',statj,FALSE,NULL),k) ;
	statj = (j < 0)?(n-j):(j) ;
	dyio_outfmt(dy_logchn,dy_gtxecho,
		    "; indB[%d] specifies %s (%d); they should agree.",
		    i,consys_nme(sys,'v',statj,FALSE,NULL),j) ; }
/*
  If the basic variable k is an architectural, status[k] should agree that it's
  basic and point to the basis vector entry.
*/
      if (k > 0)
      { statj = -((int) status[k]) ;
	if (i_basis != statj)
	{ inderrs++ ;
	  dyio_outfmt(dy_logchn,dy_gtxecho,"\nERROR: constraint %s (%d)",
		      consys_nme(sys,'c',i,FALSE,NULL),i) ;
	  dyio_outfmt(dy_logchn,dy_gtxecho,
		      "; status[%d] = %d but basis[%d].vndx = %d",
		      k,statj,i_basis,k) ;
	  dyio_outfmt(dy_logchn,dy_gtxecho,
		      "; they should point to each other.") ; } } } }
  if (inderrs > 0)
  { dyio_outfmt(dy_logchn,dy_gtxecho,
		"\n%s: found %d errors cross-checking basis index vectors.\n",
		rtnnme,inderrs) ;
    dyio_outfmt(dy_logchn,dy_gtxecho,
		"\tTests of basic variable values not performed.\n") ;
    if (xB != NULL) FREE(xB) ;
    if (indB != NULL) FREE(indB) ;
    return (inderrs) ; }
/*
  Now we know the index arrays are correct and we can use them with
  confidence.  Step through the rows, placing the initial component
  dot(beta<i>,b) into each position.
*/
  xBaccum = (double *) CALLOC((m+1),sizeof(double)) ;
  berrs = 0 ;
  betai = NULL ;
  for (i = 1 ; i <= m ; i++)
  { if (dy_betai(main_lp,i,&betai) == FALSE)
    { berrs++ ;
      j = indB[i] ;
      if (j < 0)
      { statj = n-j ; }
      else
      { statj = j ; }
      errmsg(952,rtnnme,sys->nme,"row",i,"basic variable",
	     consys_nme(sys,'v',statj,FALSE,NULL),j) ;
      continue ; }
    betaidotb = 0 ;
    for (k = 1 ; k <= m ; k++)
    { betaidotb += betai[k]*rhs[k] ; }
    xBaccum[i] += betaidotb ; }
/*
  Now step through the columns. Subtract abar<j>x<j> from x<B> if x<j> is at
  a nonzero bound. Anything other than the enumerated status codes is
  extraordinary. vstatSB might be correct if dylp declared unboundedness
  immediately after refactoring in primal phase II, but that's such an unlikely
  coincidence it deserves attention. Anything else is outright wrong.
*/
  nberrs = 0 ;
  abarj = NULL ;
  for (j = 1 ; j <= n ; j++)
  { statj = status[j] ;
    if (((int) statj) < 0) continue ;
    statj = getflg(statj,vstatSTATUS) ;
    switch (statj)
    { case vstatNBLB:
      case vstatNBFX:
      { xj = vlb[j] ;
	break ; }
      case vstatNBUB:
      { xj = vub[j] ;
	break ; }
      case vstatNBFR:
      { xj = 0.0 ;
	break ; }
      default:
      { nberrs++ ;
	dyio_outfmt(dy_logchn,dy_gtxecho,"\nERROR: constraint %s (%d)",
		    consys_nme(sys,'c',i,FALSE,NULL),i) ;
	dyio_outfmt(dy_logchn,dy_gtxecho,"; status of %s (%d) is %s.",
		    consys_nme(sys,'v',j,FALSE,NULL),j,dy_prtvstat(statj)) ;
	xj = 0.0 ;
	break ; } }
    if (xj == 0.0) continue ;
    if (dy_abarj(main_lp,j,&abarj) == FALSE)
    { nberrs++ ;
      errmsg(953,rtnnme,sys->nme,"ftran'd","column",
	     consys_nme(sys,'v',j,FALSE,NULL),j) ;
      continue ; }
    for (k = 1 ; k <= m ; k++)
    { xBaccum[k] -= abarj[k]*xj ; } }
/*
  We're not quite done. We need to account for bounded slacks
  associated with range constraints. If the constraint is tight at its lower
  bound, the slack is nonbasic at its upper bound.
*/
  logstatus = NULL ;
  dy_logStatus(main_lp,&logstatus) ;
  for (i = 1 ; i <= m ; i++)
  { stati = getflg(logstatus[i],vstatSTATUS) ;
    if (ctyp[i] == contypRNG && stati == vstatNBUB)
    { xj = rhs[i]-rhslow[i] ;
      if (dy_abarj(main_lp,-i,&abarj) == FALSE)
      { nberrs++ ;
	errmsg(953,rtnnme,sys->nme,"ftran'd","column",
	       consys_nme(sys,'v',n+i,FALSE,NULL),i) ;
	continue ; }
      for (k = 1 ; k <= m ; k++)
      { xBaccum[k] -= abarj[k]*xj ; } } }
/*
  Scan the rows one more time and check the values of the basic variables.
  Scale this test just a bit so we don't get spurious indications due to
  roundoff. The average of the two values seems safest as a scaling factor.
*/
  for (i = 1 ; i <= m ; i++)
  { tol = ((fabs(xBaccum[i])+fabs(xB[i]))/2)+1 ;
    if (fabs(xBaccum[i]-xB[i]) > tol*main_lptols->zero)
    { berrs++ ;
      j = indB[i] ;
      if (j < 0)
      { statj = n-j ; }
      else
      { statj = j ; }
      dyio_outfmt(dy_logchn,dy_gtxecho,
		  "\nERROR: basis pos'n %d %s (%d) = %g; expected %g;",
		  i,consys_nme(sys,'v',statj,FALSE,NULL),j,xB[i],xBaccum[i]) ;
      dyio_outfmt(dy_logchn,dy_gtxecho," error %g, tol %g.",
		  fabs(xB[i]-xBaccum[i]),main_lptols->zero) ; } }
/*
  Free up space and report the result.
*/
  if (logstatus != NULL) FREE(logstatus) ;
  if (abarj != NULL) FREE(abarj) ;
  if (xB != NULL) FREE(xB) ;
  if (indB != NULL) FREE(indB) ;
  if (xBaccum != NULL) FREE(xBaccum) ;
  if (betai != NULL) FREE(betai) ;

  if ((berrs+nberrs) != 0)
  { if (berrs != 0)
    { dyio_outfmt(dy_logchn,dy_gtxecho,
		"\n%s: found %d errors testing x<B> = inv(B)b.\n",
		rtnnme,berrs) ; }
    if (nberrs != 0)
    { dyio_outfmt(dy_logchn,dy_gtxecho,
	      "\n%s: found %d errors attempting to use nonbasic variables.\n",
	      rtnnme,nberrs) ; } }
  else
  { dyio_outfmt(dy_logchn,dy_gtxecho,
		"\n%s: pass test of primal basic variable values.\n",
		rtnnme) ; }

  return (berrs+nberrs) ; }
Example #4
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) ; }
Example #5
0
int dytest_colPrimals (lpprob_struct *main_lp, lptols_struct *main_lptols,
		       lpopts_struct *main_lpopts)
/*
  This routine checks the values of the primal architectural variables
  returned by dy_colPrimals.
  
  For basic variables x<B>, the routine checks
    x<B> =  inv(B)b - inv(B)Nx<N>
  To do this, the routine accumulates the values of the basic variables
  during the column scan. When the current column is basic in pos'n i, the
  routine calculates dot(beta<i>,b) and adds it to the total. When the current
  column is nonbasic, the routine calculates abar<j>x<j> and subtracts it
  from the total. Just to make things really annoying, we have to account for
  nonbasic bounded slacks due to range constraints tight at their lower bound
  (which makes the slack nonbasic at its upper bound).

  For a nonbasic variable, the routine checks the value of x<j> against the
  bound specified by the status of x<j>.

  Parameters:
    main_lp:     the lp problem structure
    main_lptols: the lp tolerance structure
    main_lpopts: the lp options structure

  Returns: 0 if the values check out, error count otherwise.
*/

{ int i,i_bpos,j,k,m,n ;
  flags statj,stati ;
  double xj,lbj,ubj,betaidotb ;

  consys_struct *sys ;
  flags *status,*logstatus ;
  double *rhs,*rhslow,*vlb,*vub,*betai,*xB,*abarj ;
  contyp_enum *ctyp ;
  basisel_struct *basis ;

  double *x ;

  int berrs,nberrs ;
  bool staterr ;

  char *rtnnme = "dytest_colPrimals" ;

/*
  Do a little initialisation. Mention that we've started.
*/
  sys = main_lp->consys ;
  m = sys->concnt ;
  n = sys->varcnt ;

# ifndef DYLP_NDEBUG
  if (main_lpopts->print.soln >= 1)
  { dyio_outfmt(dy_logchn,dy_gtxecho,
	  "\n%s: checking primal architectural variables using %s (%d x %d).",
	  rtnnme,sys->nme,m,n) ; }
# endif
/*
  Acquire the variable bound and status vectors, the constraint type, rhs,
  and rhslow vectors, the basis vector, and the values of the primal
  architectural variables. Allocate a vector to accumulate x<B>
*/
  x = NULL ;
  dy_colPrimals(main_lp,&x) ;
  basis = main_lp->basis->el ;
  status = main_lp->status ;
  ctyp = sys->ctyp ;
  rhs = sys->rhs ;
  rhslow = sys->rhslow ;
  vlb = sys->vlb ;
  vub = sys->vub ;
  xB = (double *) CALLOC((m+1),sizeof(double)) ;
/*
  Now step through the columns checking the values in x. For a variable basic
  in pos'n i, add dot(beta<i>,b) to the running total for the basic variable.
  
  For a nonbasic variable, confirm that the value, bound, and status agree.
  Then subtract abar<j>x<j> from x<B> if x<j> is at a nonzero bound.

  The only nonbasic status code not explicitly listed is SB (superbasic). This
  really should never appear. The only legitimate reason is that dylp patched
  the basis in primal phase II and then discovered the problem to be unbounded
  before the SB variable could be pivoted back into the basis. This is
  sufficiently exotic to deserve a message.
*/
  berrs = 0 ;
  nberrs = 0 ;
  betai = NULL ;
  abarj = NULL ;
  for (j = 1 ; j <= n ; j++)
  { statj = status[j] ;
    xj = x[j] ;
    if (((int) statj) < 0)
    { k = -((int) statj) ;
      i_bpos = basis[k].cndx ;
      if (dy_betai(main_lp,i_bpos,&betai) == FALSE)
      { berrs++ ;
	errmsg(952,rtnnme,sys->nme,"row",i_bpos,"variable",
	       consys_nme(sys,'v',j,FALSE,NULL),j) ;
	continue ; }
      betaidotb = 0 ;
      for (i = 1 ; i <= m ; i++)
      { betaidotb += betai[i]*rhs[i] ; }
      xB[i_bpos] += betaidotb ; }
    else
    { staterr = FALSE ;
      lbj = vlb[j] ;
      ubj = vub[j] ;
      statj = getflg(statj,vstatSTATUS) ;
      switch (statj)
      { case vstatNBLB:
	{ if (fabs(xj-lbj) > main_lptols->zero)
	  { staterr = TRUE ;
	    betaidotb = lbj ; }
	  break ; }
	case vstatNBUB:
	{ if (fabs(xj-ubj) > main_lptols->zero)
	  { staterr = TRUE ;
	    betaidotb = ubj ; }
	  break ; }
	case vstatNBFX:
	{ if (fabs(xj-lbj) > main_lptols->zero)
	  { staterr = TRUE ;
	    betaidotb = lbj ; }
	  break ; }
	case vstatNBFR:
	{ if (fabs(xj) > main_lptols->zero)
	  { staterr = TRUE ;
	    betaidotb = 0.0 ; }
	  break ; }
	default:
	{ staterr = TRUE ;
	  betaidotb = quiet_nan(42.0L) ;
	  break ; } }
    if (staterr == TRUE)
    { nberrs++ ;
      dyio_outfmt(dy_logchn,dy_gtxecho,
		  "\nERROR: %s col %s (%d) = %g; expected %g;",
		  dy_prtvstat(statj),consys_nme(sys,'v',j,FALSE,NULL),j,
		  xj,betaidotb) ;
      dyio_outfmt(dy_logchn,dy_gtxecho," error %g, tol %g.",
		  fabs(xj-betaidotb),main_lptols->zero) ;
      continue ; }
    if (xj == 0.0) continue ;
    if (dy_abarj(main_lp,j,&abarj) == FALSE)
    { nberrs++ ;
      errmsg(953,rtnnme,sys->nme,"ftran'd","column",
	     consys_nme(sys,'v',j,FALSE,NULL),j) ;
      continue ; }
    for (k = 1 ; k <= m ; k++)
    { xB[k] -= abarj[k]*xj ; } } }
/*
  But wait! We're not quite done. We need to account for bounded slacks
  associated with range constraints. If the constraint is tight at its lower
  bound, the slack is nonbasic at its upper bound.
*/
  logstatus = NULL ;
  dy_logStatus(main_lp,&logstatus) ;
  for (i = 1 ; i <= m ; i++)
  { stati = getflg(logstatus[i],vstatSTATUS) ;
    if (ctyp[i] == contypRNG && stati == vstatNBUB)
    { xj = rhs[i]-rhslow[i] ;
      if (dy_abarj(main_lp,-i,&abarj) == FALSE)
      { nberrs++ ;
	errmsg(953,rtnnme,sys->nme,"ftran'd","column",
	       consys_nme(sys,'v',n+i,FALSE,NULL),i) ;
	continue ; }
      for (k = 1 ; k <= m ; k++)
      { xB[k] -= abarj[k]*xj ; } } }
/*
  Scan the variables one more time and check the values of the basic variables.
*/
  for (j = 1 ; j <= n ; j++)
  { statj = status[j] ;
    xj = x[j] ;
    if (((int) statj) < 0)
    { k = -((int) statj) ;
      i_bpos = basis[k].cndx ;
      if (fabs(xj-xB[i_bpos]) > main_lptols->zero)
      { berrs++ ;
	dyio_outfmt(dy_logchn,dy_gtxecho,
		    "\nERROR: basis pos'n %d %s (%d) = %g; expected %g;",
		    i_bpos,consys_nme(sys,'v',j,FALSE,NULL),j,xj,xB[i_bpos]) ;
	dyio_outfmt(dy_logchn,dy_gtxecho," error %g, tol %g.",
		    fabs(xj-xB[i_bpos]),main_lptols->zero) ; } } }
/*
  Free up space and report the result.
*/
  if (logstatus != NULL) FREE(logstatus) ;
  if (abarj != NULL) FREE(abarj) ;
  if (xB != NULL) FREE(xB) ;
  if (betai != NULL) FREE(betai) ;
  if (x != NULL) FREE(x) ;

  if ((berrs+nberrs) != 0)
  { if (berrs != 0)
    { dyio_outfmt(dy_logchn,dy_gtxecho,
		  "\n%s: found %d errors testing x<B> = inv(B)b.\n",
		  rtnnme,berrs) ; }
    if (nberrs != 0)
    { dyio_outfmt(dy_logchn,dy_gtxecho,
	      "\n%s: found %d errors testing x<N> against bounds & status.\n",
	      rtnnme,nberrs) ; } }
  else
  { dyio_outfmt(dy_logchn,dy_gtxecho,
		"\n%s: pass test of primal architectural variable values.\n",
		rtnnme) ; }

  return (berrs+nberrs) ; }