double consys_dotrow (consys_struct *consys, int rowndx, double *vec) /* This routine computes the dot product of the specified row with the expanded vector passed in vec. Parameters: consys: constraint system rowndx: row vec: vector Returns: dot product, or NaN if the calculation goes awry. */ { double dotprod ; rowhdr_struct *rowhdr ; coeff_struct *coeff ; # if defined(DYLP_PARANOIA) || !defined(DYLP_NDEBUG) const char *rtnnme = "consys_dotrow" ; # endif # ifdef DYLP_PARANOIA if (consys == NULL) { errmsg(2,rtnnme,"consys") ; return (quiet_nan(0)) ; } if (consys->mtx.rows == NULL) { errmsg(101,rtnnme,consys->nme,"row header") ; return (quiet_nan(0)) ; } # endif # ifndef DYLP_NDEBUG if (rowndx <= 0 || rowndx > consys->concnt) { errmsg(102,rtnnme,consys->nme,"row",rowndx,1,consys->concnt) ; return (quiet_nan(0)) ; } # endif rowhdr = consys->mtx.rows[rowndx] ; # ifdef DYLP_PARANOIA if (rowhdr == NULL) { errmsg(103,rtnnme,consys->nme,"row",rowndx) ; return (quiet_nan(0)) ; } if (rowndx != rowhdr->ndx) { errmsg(126,rtnnme,consys->nme,"row",rowhdr,rowhdr->ndx,rowndx,rowhdr) ; return (quiet_nan(0)) ; } if (vec == NULL) { errmsg(2,rtnnme,"vec") ; return (quiet_nan(0)) ; } # endif dotprod = 0 ; for (coeff = rowhdr->coeffs ; coeff != NULL ; coeff = coeff->rownxt) { # ifdef DYLP_PARANOIA if (coeff->colhdr == NULL) { errmsg(125,rtnnme,consys->nme,"colhdr",coeff,"row", consys_nme(consys,'c',rowndx,FALSE,NULL),rowndx) ; return (quiet_nan(0)) ; } if (coeff->colhdr->ndx <= 0 || coeff->colhdr->ndx > consys->varcnt) { errmsg(102,rtnnme,consys->nme,"column",coeff->colhdr->ndx, 1,consys->varcnt) ; return (quiet_nan(0)) ; } if (coeff->colhdr != consys->mtx.cols[coeff->colhdr->ndx]) { errmsg(126,rtnnme,consys->nme,"column",coeff->colhdr,coeff->colhdr->ndx, coeff->colhdr->ndx,consys->mtx.cols[coeff->colhdr->ndx]) ; return (quiet_nan(0)) ; } # endif dotprod += coeff->val*vec[coeff->colhdr->ndx] ; } return (dotprod) ; }
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) ; }
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) ; }
void dy_rowPrimals (lpprob_struct *orig_lp, double **p_xB, int **p_indB) /* This routine returns the values of the primal basic variables, unscaled, in row (basis) order in the frame of reference of the original system. Unscaling is straightforward: sc_x<B> = sc_inv(B)sc_b = inv(S<B>)inv(B)inv(R)Rb = inv(S<B>)(inv(B)b) so all that's needed to recover x<B> = inv(B)b is to multiply by S<B>. For logicals, recall that S<i> = 1/R<i>. By construction, the basic variable for inactive constraints is the logical for the constraint. Generating beta<i> = [ -a<B,i>inv(B) 1 ] for an inactive row, correcting b<i> for nonbasic, nonzero variables (active and inactive), and calculating dot(beta<i>,b) is a lot of work. Much easier to call colPrimals for the complete solution vector and calculate b<i> - dot(a<i>,x) in the original system. Parameters: orig_lp: the original lp problem p_xB: (i) vector to hold the values of the primal basic variables; if NULL, a vector of appropriate size will be allocated (o) values of the primal basic variables, unscaled, in the original system frame of reference p_indB: (i) vector to hold the indices of the primal basic variables; if NULL, a vector of appropriate size will be allocated (o) indices of the primal basic variables, unscaled, in the original system frame of reference; indices of logical variables are encoded as the negative of the constraint index Returns: undefined */ { int i,j,m,i_orig,j_orig,m_orig,n_orig ; double xj,lhs ; consys_struct *orig_sys ; double *x,*xB ; int *indB ; bool scaled ; const double *rscale,*cscale ; # ifndef DYLP_NDEBUG int v ; # endif # ifdef DYLP_PARANOIA char *rtnnme = "dy_rowPrimals" ; if (dy_std_paranoia(orig_lp,rtnnme) == FALSE) { return ; } if (p_xB == NULL) { errmsg(2,rtnnme,"x") ; return ; } if (p_indB == NULL) { errmsg(2,rtnnme,"x") ; return ; } # endif /* Is unscaling required? Acquire the scaling vectors. If there are inactive constraints, we'll need the primal architecturals in order to calculate the value of the associated (basic) logical. */ scaled = dy_isscaled() ; if (scaled == TRUE) { dy_scaling_vectors(&rscale,&cscale) ; } orig_sys = orig_lp->consys ; n_orig = orig_sys->varcnt ; m_orig = orig_sys->concnt ; m = dy_sys->concnt ; x = NULL ; if (m < m_orig) { dy_colPrimals(orig_lp,&x) ; } /* Do we need vectors? Do the necessary setup. */ if (*p_xB != NULL) { xB = *p_xB ; memset(xB,0,(m_orig+1)*sizeof(double)) ; } else { xB = (double *) CALLOC((m_orig+1),sizeof(double)) ; } if (*p_indB != NULL) { indB = *p_indB ; memset(indB,0,(m_orig+1)*sizeof(int)) ; } else { indB = (int *) CALLOC((m_orig+1),sizeof(int)) ; } /* Walk the constraints of the original system. For each constraint that's active, we can obtain the value from dy_xbasic. For each inactive constraint, we need to calculate the value of the logical. Indices of logicals are recorded in indB as the negative of the constraint index. */ for (i_orig = 1 ; i_orig <= m_orig ; i_orig++) { if (ACTIVE_CON(i_orig)) { i = dy_origcons[i_orig] ; j = dy_basis[i] ; if (j <= m) { j_orig = dy_actcons[j] ; } else { j_orig = dy_actvars[j] ; } if (scaled == TRUE) { if (j <= m) { xj = (1/rscale[j_orig])*dy_xbasic[i] ; } else { xj = cscale[j_orig]*dy_xbasic[i] ; } } else { xj = dy_xbasic[i] ; } if (j <= m) { indB[i_orig] = -j_orig ; } else { indB[i_orig] = j_orig ; } } else { lhs = consys_dotrow(orig_sys,i_orig,x) ; xj = orig_sys->rhs[i_orig]-lhs ; indB[i_orig] = -i_orig ; } setcleanzero(xj,dy_tols->zero) ; xB[i_orig] = xj ; } if (x != NULL) FREE(x) ; # ifndef DYLP_NDEBUG if (dy_opts->print.soln >= 3) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n\txB =") ; v = 0 ; for (i_orig = 1 ; i_orig <= m_orig ; i_orig++) { if ((++v)%3 == 0) { v = 0 ; dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t ") ; } j_orig = indB[i_orig] ; if (j_orig < 0) { j = n_orig-j_orig ; } else { j = j_orig ; } dyio_outfmt(dy_logchn,dy_gtxecho," (%d %g %s %d)", i_orig,xB[i_orig], consys_nme(orig_sys,'v',j,FALSE,NULL),j_orig) ; } } # endif /* That's it. Return the vectors. */ *p_xB = xB ; *p_indB = indB ; return ; }
void dy_colStatus (lpprob_struct *orig_lp, flags **p_colstat) /* This routine returns the status of the primal architectural variables, in column order for the original system. The routine reports out the full set of dylp status codes. Parameters: orig_lp: the original lp problem p_colstat: (i) vector to hold the status of the primal architectural variables; if NULL, a vector of appropriate size will be allocated (o) status of the primal architectural variables, in the original system frame of reference Returns: undefined */ { int j,j_orig,n_orig ; flags statj ; consys_struct *orig_sys ; flags *colstat ; # ifndef DYLP_NDEBUG int v ; # endif # ifdef DYLP_PARANOIA char *rtnnme = "dy_colStatus" ; if (dy_std_paranoia(orig_lp,rtnnme) == FALSE) { return ; } if (p_colstat == NULL) { errmsg(2,rtnnme,"colstat") ; return ; } # endif orig_sys = orig_lp->consys ; n_orig = orig_sys->varcnt ; /* Do we need a vector? */ if (*p_colstat != NULL) { colstat = *p_colstat ; memset(colstat,0,(n_orig+1)*sizeof(flags)) ; } else { colstat = (flags *) CALLOC((n_orig+1),sizeof(flags)) ; } /* Walk the columns of the original system. For active variables, copy the status from dy_status. For inactive variables, we acquire it from dy_origvars. */ for (j_orig = 1 ; j_orig <= n_orig ; j_orig++) { if (ACTIVE_VAR(j_orig)) { j = dy_origvars[j_orig] ; statj = dy_status[j] ; } else { statj = (flags)(-dy_origvars[j_orig]) ; } colstat[j_orig] = statj ; } # ifndef DYLP_NDEBUG if (dy_opts->print.soln >= 3) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n\tcolstat =") ; v = 0 ; for (j_orig = 1 ; j_orig <= n_orig ; j_orig++) { if ((++v)%3 == 0) { v = 0 ; dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t ") ; } dyio_outfmt(dy_logchn,dy_gtxecho," (%s %d %s)", consys_nme(orig_sys,'v',j_orig,FALSE,NULL),j_orig, dy_prtvstat(colstat[j_orig])) ; } } # endif /* That's it. Return the vector. */ *p_colstat = colstat ; return ; }
static void correct_for_patch (void) /* This routine scans dy_status looking for architectural variables that are recorded as basic but have been booted out of the basis by a patch operation. It's a very special-purpose routine, separated out so it doesn't clutter up the code in dy_warmstart. Parameters: none Returns: undefined */ { int j,cnt ; flags statj ; double *vlb,*vub ; vlb = dy_sys->vlb ; vub = dy_sys->vub ; /* Open a loop to scan the status array, checking that variables recorded as basic are really basic. dy_patch clears the var2basis entry when it makes the patch, so we're looking for basic status with a 0 in var2basis. When we find a variable that needs to be corrected, decide an appropriate nonbasic status based on the sign of the objective coefficient and the presence/absence of finite bounds. */ # ifndef DYLP_NDEBUG if (dy_opts->print.crash >= 3) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n\tcorrecting status due to basis patch ...") ; } # endif cnt = 0 ; for (j = dy_sys->concnt+1 ; j <= dy_sys->varcnt ; j++) { statj = dy_status[j] ; if (flgon(statj,vstatBASIC) && dy_var2basis[j] == 0) { if (vlb[j] > -dy_tols->inf && vub[j] < dy_tols->inf) { if (vub[j] == vlb[j]) { dy_status[j] = vstatNBFX ; dy_x[j] = vub[j] ; } else if (dy_sys->obj[j] >= 0) { dy_status[j] = vstatNBLB ; dy_x[j] = vlb[j] ; } else { dy_status[j] = vstatNBUB ; dy_x[j] = vub[j] ; } } else if (vlb[j] > -dy_tols->inf) { dy_status[j] = vstatNBLB ; dy_x[j] = vlb[j] ; } else if (vub[j] < dy_tols->inf) { dy_status[j] = vstatNBUB ; dy_x[j] = vub[j] ; } else { dy_status[j] = vstatNBFR ; dy_x[j] = 0 ; } # ifndef DYLP_NDEBUG if (dy_opts->print.crash >= 4) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n\t changing status for %s (%d) to %s,", consys_nme(dy_sys,'v',j,FALSE,NULL),j, dy_prtvstat(dy_status[j])) ; dyio_outfmt(dy_logchn,dy_gtxecho," value %g.",dy_x[j]) ; } # endif cnt++ ; } } # ifndef DYLP_NDEBUG /* Given that this routine has been called, there should be corrections to be made, but it's possible that the patch involved only logicals. If so, dy_warmstart has already dealt with the problem and we simply can't tell. (The necessary data structure is not exported from dy_basis.c) The least we can do is print a message. */ if (cnt == 0 && dy_opts->print.crash >= 4) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t no architecturals corrected.") ; } # endif return ; }
void dy_rowDuals (lpprob_struct *orig_lp, double **p_y, bool trueDuals) /* This routine returns the unscaled vector of row duals, commonly referred to as the dual variables, c<B>inv(B). The values are unscaled and returned in a vector matching the original system frame of reference. Duals associated with inactive rows are always zero. In dylp's min primal <=> min dual pairing, the duals have the wrong sign for the true dual variables used by the min dual problem. If you'd prefer that the duals have a sign convention appropriate for the min dual problem, specify trueDuals = false. The relevant bit of unscaling is: sc_y<i> = sc_c<B>sc_inv(B) = c<B>S<B>inv(S<B>)inv(B)inv(R) = c<B>inv(B)inv(R) So, to recover y<i> we need to postmultiply by inv(R). The appropriate row factor is the one associated with the original row. Parameters: orig_lp: the original lp problem p_y: (i) vector to hold the dual variables; if NULL, a vector of appropriate size will be allocated (o) values of the dual variables, unscaled, in the original system frame of reference Returns: undefined */ { int i,m,n,i_orig,m_orig,n_orig ; double yi ; double *y ; consys_struct *orig_sys ; contyp_enum *ctyp ; bool scaled ; const double *rscale,*cscale ; # ifndef DYLP_NDEBUG int j,v ; # endif # ifdef DYLP_PARANOIA char *rtnnme = "dy_rowDuals" ; if (dy_std_paranoia(orig_lp,rtnnme) == FALSE) { return ; } if (p_y == NULL) { errmsg(2,rtnnme,"y") ; return ; } # endif /* Is unscaling required? Acquire the scaling vectors. accordingly. */ scaled = dy_isscaled() ; if (scaled == TRUE) { dy_scaling_vectors(&rscale,&cscale) ; } orig_sys = orig_lp->consys ; n_orig = orig_sys->varcnt ; m_orig = orig_sys->concnt ; n = dy_sys->varcnt ; m = dy_sys->concnt ; ctyp = orig_sys->ctyp ; /* Do we need a vector? */ if (*p_y != NULL) { y = *p_y ; memset(y,0,(m_orig+1)*sizeof(double)) ; } else { y = (double *) CALLOC((m_orig+1),sizeof(double)) ; } /* Step through the constraints of the original system. For active constraints, acquire and unscale the dual value. */ for (i_orig = 1 ; i_orig <= m_orig ; i_orig++) { if (ACTIVE_CON(i_orig)) { i = dy_origcons[i_orig] ; yi = dy_y[i] ; if (scaled == TRUE) { yi *= rscale[i_orig] ; } setcleanzero(yi,dy_tols->cost) ; } else { yi = 0.0 ; } /* The true duals are the negative of the minimisation duals here. */ if (trueDuals == TRUE) y[i_orig] = -yi ; else y[i_orig] = yi ; } # ifndef DYLP_NDEBUG if (dy_opts->print.soln >= 3) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n\ty =") ; v = 0 ; for (i_orig = 1 ; i_orig <= m_orig ; i_orig++) { if (y[i_orig] != 0) { if ((++v)%3 == 0) { v = 0 ; dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t ") ; } i = dy_origcons[i_orig] ; j = dy_basis[i] ; dyio_outfmt(dy_logchn,dy_gtxecho," (%d %g %s %d)", i_orig,y[i_orig], consys_nme(dy_sys,'v',j,FALSE,NULL),j) ; } } } # endif /* That's it. Return the vector. */ *p_y = y ; return ; }
int consys_gcdrow (consys_struct *consys, int rowndx) /* This routine calculates the gcd of the coefficients of the specified row using the euclidean algorithm. Note that explicit zeros should not appear in the coefficient matrix. Obviously, the coefficients should be integer. If they're not, the routine returns 0. If the row is empty, the routine returns 0, on the theory that whatever you were trying to do with this row, it's probably not suitable. The code uses the following statement of the euclidean algorithm, courtesy of Martin, Large Scale Linear and Integer Optimization, p. 106. Assume a<1> and a<2> positive integer, a<1> > a<2>. while (a<1> > 0 && a<2> > 0) { q = floor(a<1>/a<2>) ; r = a<1> - q*a<2> ; a<1> = a<2> ; a<2> = r ; } Parameters: consys: constraint system rowndx: row to be evaluated Returns: gcd(a<1>, ..., a<n>), 0 if the coefficients aren't integer, -1 if anything else goes wrong. */ { double gcd,a1,a2,q,r ; rowhdr_struct *rowhdr ; coeff_struct *coeff ; # if defined(DYLP_PARANOIA) || !defined(DYLP_NDEBUG) const char *rtnnme = "consys_gcdrow" ; # endif /* The usual paranoia, plus an honest index check. */ # ifdef DYLP_PARANOIA if (consys == NULL) { errmsg(2,rtnnme,"consys") ; return (-1) ; } if (consys->mtx.rows == NULL) { errmsg(101,rtnnme,consys->nme,"row header") ; return (-1) ; } # endif # ifndef DYLP_NDEBUG if (rowndx <= 0 || rowndx > consys->concnt) { errmsg(102,rtnnme,consys->nme,"row",rowndx,1,consys->concnt) ; return (-1) ; } # endif rowhdr = consys->mtx.rows[rowndx] ; # ifdef DYLP_PARANOIA if (rowhdr == NULL) { errmsg(103,rtnnme,consys->nme,"row",rowndx) ; return (-1) ; } if (rowndx != rowhdr->ndx) { errmsg(126,rtnnme,consys->nme,"row",rowhdr,rowhdr->ndx,rowndx,rowhdr) ; return (-1) ; } # endif /* Trivial cases: 0 or 1 coefficients. */ if (rowhdr->len == 0) return (1) ; coeff = rowhdr->coeffs ; # ifdef DYLP_PARANOIA if (coeff == NULL) { errmsg(116,rtnnme,consys->nme,rowhdr->nme,rowhdr->ndx,rowhdr->len,0) ; return (-1) ; } if (coeff->colhdr == NULL) { errmsg(125,rtnnme,consys->nme,"colhdr",coeff,"row", consys_nme(consys,'c',rowndx,FALSE,NULL),rowndx) ; return (-1) ; } if (coeff->colhdr->ndx <= 0 || coeff->colhdr->ndx > consys->varcnt) { errmsg(102,rtnnme,consys->nme,"column",coeff->colhdr->ndx, 1,consys->varcnt) ; return (-1) ; } if (coeff->colhdr != consys->mtx.cols[coeff->colhdr->ndx]) { errmsg(126,rtnnme,consys->nme,"column",coeff->colhdr,coeff->colhdr->ndx, coeff->colhdr->ndx,consys->mtx.cols[coeff->colhdr->ndx]) ; return (-1) ; } # endif a1 = coeff->val ; if (a1 < 0) a1 = -a1 ; if (floor(a1) != a1) return (0) ; if (rowhdr->len == 1) return ((int) a1) ; /* Two or more coefficients. We work through them, calculating gcd(gcd,a<i>). We first do a quick test for a<i>/gcd integer (in which case we can keep gcd and move on to the next coefficient). When the gcd drops to 1, we bail out. */ gcd = a1 ; for (coeff = coeff->rownxt ; gcd > 1 && coeff != NULL ; coeff = coeff->rownxt) { # ifdef DYLP_PARANOIA if (coeff->colhdr == NULL) { errmsg(125,rtnnme,consys->nme,"colhdr",coeff,"row", consys_nme(consys,'c',rowndx,FALSE,NULL),rowndx) ; return (-1) ; } if (coeff->colhdr->ndx <= 0 || coeff->colhdr->ndx > consys->varcnt) { errmsg(102,rtnnme,consys->nme,"column",coeff->colhdr->ndx, 1,consys->varcnt) ; return (-1) ; } if (coeff->colhdr != consys->mtx.cols[coeff->colhdr->ndx]) { errmsg(126,rtnnme,consys->nme,"column",coeff->colhdr,coeff->colhdr->ndx, coeff->colhdr->ndx,consys->mtx.cols[coeff->colhdr->ndx]) ; return (-1) ; } # endif a1 = coeff->val ; if (a1 < 0) a1 = -a1 ; if (floor(a1) != a1) return (0) ; if (a1 > gcd) { if (floor(a1/gcd) == a1/gcd) continue ; a2 = gcd ; } else { a2 = a1 ; a1 = gcd ; } /* We need to do a gcd calculation. */ while (a1 > 0 && a2 > 0) { q = floor(a1/a2) ; r = a1 - q*a2 ; a1 = a2 ; a2 = r ; } gcd = a1 ; } return ((int) gcd) ; }
static void adjust_basis (int *p_patchcnt, patch_struct **p_patches) /* This routine corrects the dylp basis arrays when glpinv/glpluf declares the current basis to be singular. glpluf doesn't actually salvage the basis --- it just reports the linearly dependent (hence unpivoted) columns and corresponding unpivoted rows. Once we've adjusted the basis accordingly, we can make another attempt to factor. The convention is as follows: luf_basis.rank gives the rank of the basis. qq_col[rank+1 .. m] contain the indices of the basic columns that must be removed from the basis. pp_row[rank+1 .. m] contain the indices of the basic rows that remain unpivoted; we'll put the logicals for these rows into the basis. Both of the above are expressed in terms of basis positions. For the rows, basis position i is equivalent to constraint i; for the columns, we need to look up the variable j in basis position i. Recognise that in general this is the easiest part of salvaging the situation. We record the changes for adjust_therest, which will do the remainder of the work after the basis is successfully factored. Parameters: p_patchcnt: (o) the number of basis corrections p_patches: (o) patch array recording the basis corrections Returns: undefined */ { int *qq_col, *pp_row ; int rank,pqndx,i,j,k,pndx ; patch_struct *patches ; #if defined(DYLP_PARANOIA) || MALLOC_DEBUG == 2 const char *rtnnme = "adjust_basis" ; if (dy_sys == NULL) { errmsg(2,rtnnme,"dy_sys") ; return ; } if (dy_basis == NULL) { errmsg(2,rtnnme,"basis") ; return ; } if (dy_var2basis == NULL) { errmsg(2,rtnnme,"var2basis") ; return ; } if (luf_basis == NULL) { errmsg(2,rtnnme,"LUF basis") ; return ; } if (p_patches == NULL) { errmsg(2,rtnnme,"p_patches") ; return ; } #endif qq_col = luf_basis->luf->qq_col ; pp_row = luf_basis->luf->pp_row ; rank = luf_basis->luf->rank ; patches = (patch_struct *) MALLOC((dy_sys->concnt-rank)*sizeof(patch_struct)) ; /* Walk qq_col, retrieving the basis position that must be corrected. Remove the corresponding variable from the basis, and put in its place the slack for the basis row. */ for (pqndx = rank+1, pndx = 0 ; pqndx <= dy_sys->concnt ; pqndx++, pndx++) { k = qq_col[pqndx] ; j = dy_basis[k] ; i = pp_row[pqndx] ; dy_basis[k] = i ; dy_var2basis[j] = 0 ; dy_var2basis[i] = k ; patches[pndx].pos = k ; patches[pndx].out = j ; patches[pndx].in = i ; # ifndef DYLP_NDEBUG if (dy_opts->print.basis >= 3) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n pos'n %d (%s (%d)) replacing %s (%d) with %s (%d).", k,consys_nme(dy_sys,'c',k,FALSE,NULL),k, consys_nme(dy_sys,'v',j,FALSE,NULL),j, consys_nme(dy_sys,'v',i,FALSE,NULL),i) ; } # endif } *p_patchcnt = pndx ; *p_patches = patches ; return ; }
bool consys_mulrow (consys_struct *consys, int rowndx, double scalar) /* This routine multiplies a row i by a scalar q. It deals with the coefficients a<i>, and also with b<i>, blow<i>, cub<i>, and clb<i>, if they exist. If q < 0, the type of constraint is changed accordingly (>= swapped with <=) and clb<i> is swapped with cub<i>. Note that range constraints always take the form blow <= ax <= b, so if we multiply a range constraint by q < 0, the resulting constraint is qblow >= (qa)x >= qb => qb <= (qa)x <= qblow. Attempting to multiply a constraint by 0 gets you a warning if the CONSYS_WRNZERO flag is set in consys->opts. The routine will work with clb<i> and cub<i> only if both are present. It's difficult to define consistent changes otherwise. Parameters: consys: constraint system rowndx: row to be modified scalar: the multiplicative scalar Returns: TRUE if no problems are encountered, FALSE otherwise. */ { double tmprhs ; rowhdr_struct *rowhdr ; coeff_struct *coeff ; conbnd_struct tmpbnd ; bool do_conbnds ; const char *rtnnme = "consys_mulrow" ; /* The usual paranoia, plus an honest index check. */ # ifdef DYLP_PARANOIA if (consys == NULL) { errmsg(2,rtnnme,"consys") ; return (FALSE) ; } if (consys->mtx.rows == NULL) { errmsg(101,rtnnme,consys->nme,"row header") ; return (FALSE) ; } # endif # ifndef DYLP_NDEBUG if (rowndx <= 0 || rowndx > consys->concnt) { errmsg(102,rtnnme,consys->nme,"row",rowndx,1,consys->concnt) ; return (FALSE) ; } # endif rowhdr = consys->mtx.rows[rowndx] ; # ifdef DYLP_PARANOIA if (rowhdr == NULL) { errmsg(103,rtnnme,consys->nme,"row",rowndx) ; return (FALSE) ; } if (rowndx != rowhdr->ndx) { errmsg(126,rtnnme,consys->nme,"row",rowhdr,rowhdr->ndx,rowndx,rowhdr) ; return (FALSE) ; } # endif # ifndef DYLP_NDEBUG if (scalar == 0 && flgon(consys->opts,CONSYS_WRNZERO)) { dywarn(132,rtnnme,consys->nme,"row",rowhdr->nme,rowndx) ; } # endif if (consys->cub != NULL && consys->clb != NULL) do_conbnds = TRUE ; else do_conbnds = FALSE ; /* The straightforward part. Multiply the coefficients by the scalar. */ for (coeff = rowhdr->coeffs ; coeff != NULL ; coeff = coeff->rownxt) { # ifdef DYLP_PARANOIA if (coeff->colhdr == NULL) { errmsg(125,rtnnme,consys->nme,"colhdr",coeff,"row", consys_nme(consys,'c',rowndx,FALSE,NULL),rowndx) ; return (FALSE) ; } if (coeff->colhdr->ndx <= 0 || coeff->colhdr->ndx > consys->varcnt) { errmsg(102,rtnnme,consys->nme,"column",coeff->colhdr->ndx, 1,consys->varcnt) ; return (FALSE) ; } if (coeff->colhdr != consys->mtx.cols[coeff->colhdr->ndx]) { errmsg(126,rtnnme,consys->nme,"column",coeff->colhdr,coeff->colhdr->ndx, coeff->colhdr->ndx,consys->mtx.cols[coeff->colhdr->ndx]) ; return (FALSE) ; } # endif coeff->val *= scalar ; } /* If we did get a 0 for the scalar, we can be done in no time. */ if (scalar == 0) { if (consys->rhs != NULL) consys->rhs[rowndx] = 0 ; if (consys->rhslow != NULL) consys->rhslow[rowndx] = 0 ; if (do_conbnds == TRUE) { tmpbnd.revs = 0 ; tmpbnd.inf = 0 ; tmpbnd.bnd = 0 ; consys->cub[rowndx] = tmpbnd ; consys->clb[rowndx] = tmpbnd ; } return (TRUE) ; } /* For q != 0, it's a little more work. Correct b<i>, blow<i>, cub<i>, and clb<i>, if they exist. */ if (consys->rhs != NULL) consys->rhs[rowndx] *= scalar ; if (consys->rhslow != NULL) consys->rhslow[rowndx] *= scalar ; if (do_conbnds == TRUE) { consys->cub[rowndx].bnd *= scalar ; consys->clb[rowndx].bnd *= scalar ; } /* And now the complicated bit. If q < 0, swap the constraint bounds, then take additional action as needed, depending on the constraint type. */ if (scalar < 0) { if (do_conbnds == TRUE) { tmpbnd = consys->cub[rowndx] ; consys->cub[rowndx] = consys->clb[rowndx] ; consys->clb[rowndx] = tmpbnd ; } switch (consys->ctyp[rowndx]) { case contypLE: { consys->ctyp[rowndx] = contypGE ; break ; } case contypGE: { consys->ctyp[rowndx] = contypLE ; break ; } case contypRNG: { tmprhs = consys->rhs[rowndx] ; consys->rhs[rowndx] = consys->rhslow[rowndx] ; consys->rhslow[rowndx] = tmprhs ; break ; } case contypEQ: case contypNB: { break ; } default: { errmsg(1,rtnnme,__LINE__) ; return (FALSE) ; } } } return (TRUE) ; }
bool consys_divrow (consys_struct *consys, int rowndx, double scalar) /* This routine divides a row i by a scalar q. It deals with the coefficients a<i>, and also with b<i>, blow<i>, cub<i>, and clb<i>, if they exist. If q < 0, the type of constraint is changed accordingly (>= swapped with <=) and clb<i> is swapped with cub<i>. It's a separate routine (rather than using consys_mulrow to multiply by 1/scalar) to try and retain accuracy. Note that range constraints always take the form blow <= ax <= b, so if we divide a range constraint by q < 0, the resulting constraint is qblow >= (qa)x >= qb => qb <= (qa)x <= qblow. Attempting to divide a constraint by 0 is an error. The routine will work with clb<i> and cub<i> only if both are present. It's difficult to define consistent changes otherwise. Parameters: consys: constraint system rowndx: row to be divided scalar: the dividing scalar Returns: TRUE if no problems are encountered, FALSE otherwise. */ { double tmprhs ; rowhdr_struct *rowhdr ; coeff_struct *coeff ; conbnd_struct tmpbnd ; bool do_conbnds ; const char *rtnnme = "consys_divrow" ; /* The usual paranoia, plus an honest index check. */ # ifdef DYLP_PARANOIA if (consys == NULL) { errmsg(2,rtnnme,"consys") ; return (FALSE) ; } if (consys->mtx.rows == NULL) { errmsg(101,rtnnme,consys->nme,consys_assocnme(NULL,CONSYS_ROWHDR)) ; return (FALSE) ; } # endif # ifndef DYLP_NDEBUG if (rowndx <= 0 || rowndx > consys->concnt) { errmsg(102,rtnnme,consys->nme,"row",rowndx,1,consys->concnt) ; return (FALSE) ; } # endif rowhdr = consys->mtx.rows[rowndx] ; # ifdef DYLP_PARANOIA if (rowhdr == NULL) { errmsg(103,rtnnme,consys->nme,"row",rowndx) ; return (FALSE) ; } if (rowndx != rowhdr->ndx) { errmsg(126,rtnnme,consys->nme,"row",rowhdr,rowhdr->ndx,rowndx,rowhdr) ; return (FALSE) ; } if (scalar == 0) { errmsg(5,rtnnme,"scalar",(int) scalar) ; return (FALSE) ; } # endif if (consys->cub != NULL && consys->clb != NULL) do_conbnds = TRUE ; else do_conbnds = FALSE ; /* The straightforward part. Divide the coefficients by the scalar. */ for (coeff = rowhdr->coeffs ; coeff != NULL ; coeff = coeff->rownxt) { # ifdef DYLP_PARANOIA if (coeff->colhdr == NULL) { errmsg(125,rtnnme,consys->nme,"colhdr",coeff,"row", consys_nme(consys,'c',rowndx,FALSE,NULL),rowndx) ; return (FALSE) ; } if (coeff->colhdr->ndx <= 0 || coeff->colhdr->ndx > consys->varcnt) { errmsg(102,rtnnme,consys->nme,"column",coeff->colhdr->ndx, 1,consys->varcnt) ; return (FALSE) ; } if (coeff->colhdr != consys->mtx.cols[coeff->colhdr->ndx]) { errmsg(126,rtnnme,consys->nme,"column",coeff->colhdr,coeff->colhdr->ndx, coeff->colhdr->ndx,consys->mtx.cols[coeff->colhdr->ndx]) ; return (FALSE) ; } # endif coeff->val /= scalar ; } /* Correct b<i>, blow<i>, cub<i>, and clb<i>, if they exist. */ if (consys->rhs != NULL) consys->rhs[rowndx] /= scalar ; if (consys->rhslow != NULL) consys->rhslow[rowndx] /= scalar ; if (do_conbnds == TRUE) { consys->cub[rowndx].bnd /= scalar ; consys->clb[rowndx].bnd /= scalar ; } /* And now the complicated bit. If q < 0, swap the constraint bounds, then take additional action as needed, depending on the constraint type. */ if (scalar < 0) { if (do_conbnds == TRUE) { tmpbnd = consys->cub[rowndx] ; consys->cub[rowndx] = consys->clb[rowndx] ; consys->clb[rowndx] = tmpbnd ; } switch (consys->ctyp[rowndx]) { case contypLE: { consys->ctyp[rowndx] = contypGE ; break ; } case contypGE: { consys->ctyp[rowndx] = contypLE ; break ; } case contypRNG: { tmprhs = consys->rhs[rowndx] ; consys->rhs[rowndx] = consys->rhslow[rowndx] ; consys->rhslow[rowndx] = tmprhs ; break ; } case contypEQ: case contypNB: { break ; } default: { errmsg(1,rtnnme,__LINE__) ; return (FALSE) ; } } } return (TRUE) ; }
double consys_infnormcol (consys_struct *consys, int colndx) /* This routine computes the infinity-norm of a column: MAX{i} |a<i,j>|. Parameters: consys: constraint system colndx: column Returns: value of the norm, or NaN if the calculation goes awry */ { double norm ; colhdr_struct *colhdr ; coeff_struct *coeff ; # if defined(DYLP_PARANOIA) || !defined(DYLP_NDEBUG) const char *rtnnme = "consys_infnormcol" ; # endif # ifdef DYLP_PARANOIA if (consys == NULL) { errmsg(2,rtnnme,"consys") ; return (quiet_nan(0)) ; } if (consys->mtx.cols == NULL) { errmsg(101,rtnnme,consys->nme,"column header") ; return (quiet_nan(0)) ; } # endif # ifndef DYLP_NDEBUG if (colndx <= 0 || colndx > consys->varcnt) { errmsg(102,rtnnme,consys->nme,"column",colndx,1,consys->varcnt) ; return (quiet_nan(0)) ; } # endif colhdr = consys->mtx.cols[colndx] ; # ifdef DYLP_PARANOIA if (colhdr == NULL) { errmsg(103,rtnnme,consys->nme,"column",colndx) ; return (quiet_nan(0)) ; } if (colndx != colhdr->ndx) { errmsg(126,rtnnme,consys->nme,"column",colhdr,colhdr->ndx,colndx,colhdr) ; return (quiet_nan(0)) ; } # endif norm = 0 ; for (coeff = colhdr->coeffs ; coeff != NULL ; coeff = coeff->colnxt) { # ifdef DYLP_PARANOIA if (coeff->rowhdr == NULL) { errmsg(125,rtnnme,consys->nme,"rowhdr",coeff,"column", consys_nme(consys,'v',colndx,FALSE,NULL),colndx) ; return (quiet_nan(0)) ; } if (coeff->rowhdr->ndx <= 0 || coeff->rowhdr->ndx > consys->varcnt) { errmsg(102,rtnnme,consys->nme,"row",coeff->rowhdr->ndx, 1,consys->varcnt) ; return (quiet_nan(0)) ; } if (coeff->rowhdr != consys->mtx.rows[coeff->rowhdr->ndx]) { errmsg(126,rtnnme,consys->nme,"row",coeff->rowhdr,coeff->rowhdr->ndx, coeff->rowhdr->ndx,consys->mtx.rows[coeff->rowhdr->ndx]) ; return (quiet_nan(0)) ; } # endif norm = maxx(fabs(coeff->val),norm) ; } return (norm) ; }
double consys_ssqcol (consys_struct *consys, int colndx) /* This routine computes the sum of squares of a column: SUM{i} a<i,j>**2. It's sometimes more useful to have this than the actual 2-norm. Parameters: consys: constraint system colndx: column Returns: value of the norm, or NaN if the calculation goes awry */ { double norm ; colhdr_struct *colhdr ; coeff_struct *coeff ; # if defined(DYLP_PARANOIA) || !defined(DYLP_NDEBUG) const char *rtnnme = "consys_ssqcol" ; # endif # ifdef DYLP_PARANOIA if (consys == NULL) { errmsg(2,rtnnme,"consys") ; return (quiet_nan(0)) ; } if (consys->mtx.cols == NULL) { errmsg(101,rtnnme,consys->nme,"column header") ; return (quiet_nan(0)) ; } # endif # ifndef DYLP_NDEBUG if (colndx <= 0 || colndx > consys->varcnt) { errmsg(102,rtnnme,consys->nme,"column",colndx,1,consys->varcnt) ; return (quiet_nan(0)) ; } # endif colhdr = consys->mtx.cols[colndx] ; # ifdef DYLP_PARANOIA if (colhdr == NULL) { errmsg(103,rtnnme,consys->nme,"column",colndx) ; return (quiet_nan(0)) ; } if (colndx != colhdr->ndx) { errmsg(126,rtnnme,consys->nme,"column",colhdr,colhdr->ndx,colndx,colhdr) ; return (quiet_nan(0)) ; } # endif norm = 0 ; for (coeff = colhdr->coeffs ; coeff != NULL ; coeff = coeff->colnxt) { # ifdef DYLP_PARANOIA if (coeff->rowhdr == NULL) { errmsg(125,rtnnme,consys->nme,"rowhdr",coeff,"column", consys_nme(consys,'v',colndx,FALSE,NULL),colndx) ; return (quiet_nan(0)) ; } if (coeff->rowhdr->ndx <= 0 || coeff->rowhdr->ndx > consys->varcnt) { errmsg(102,rtnnme,consys->nme,"row",coeff->rowhdr->ndx, 1,consys->varcnt) ; return (quiet_nan(0)) ; } if (coeff->rowhdr != consys->mtx.rows[coeff->rowhdr->ndx]) { errmsg(126,rtnnme,consys->nme,"row",coeff->rowhdr,coeff->rowhdr->ndx, coeff->rowhdr->ndx,consys->mtx.rows[coeff->rowhdr->ndx]) ; return (quiet_nan(0)) ; } # endif norm += coeff->val*coeff->val ; } return (norm) ; }
double consys_infnormrow (consys_struct *consys, int rowndx) /* This routine computes the infinity-norm of a row: MAX{j} |a<i,j>| Parameters: consys: constraint system rowndx: row Returns: value of the norm, or NaN if the calculation goes awry */ { double norm ; rowhdr_struct *rowhdr ; coeff_struct *coeff ; # if defined(DYLP_PARANOIA) || !defined(DYLP_NDEBUG) const char *rtnnme = "consys_infnormrow" ; # endif /* The usual paranoia, plus an honest index check. */ # ifdef DYLP_PARANOIA if (consys == NULL) { errmsg(2,rtnnme,"consys") ; return (quiet_nan(0)) ; } if (consys->mtx.rows == NULL) { errmsg(101,rtnnme,consys->nme,"row header") ; return (quiet_nan(0)) ; } # endif # ifndef DYLP_NDEBUG if (rowndx <= 0 || rowndx > consys->concnt) { errmsg(102,rtnnme,consys->nme,"row",rowndx,1,consys->concnt) ; return (quiet_nan(0)) ; } # endif rowhdr = consys->mtx.rows[rowndx] ; # ifdef DYLP_PARANOIA if (rowhdr == NULL) { errmsg(103,rtnnme,consys->nme,"row",rowndx) ; return (quiet_nan(0)) ; } if (rowndx != rowhdr->ndx) { errmsg(126,rtnnme,consys->nme,"row",rowhdr,rowhdr->ndx,rowndx,rowhdr) ; return (quiet_nan(0)) ; } # endif norm = 0 ; for (coeff = rowhdr->coeffs ; coeff != NULL ; coeff = coeff->rownxt) { # ifdef DYLP_PARANOIA if (coeff->colhdr == NULL) { errmsg(125,rtnnme,consys->nme,"colhdr",coeff,"row", consys_nme(consys,'c',rowndx,FALSE,NULL),rowndx) ; return (quiet_nan(0)) ; } if (coeff->colhdr->ndx <= 0 || coeff->colhdr->ndx > consys->varcnt) { errmsg(102,rtnnme,consys->nme,"column",coeff->colhdr->ndx, 1,consys->varcnt) ; return (quiet_nan(0)) ; } if (coeff->colhdr != consys->mtx.cols[coeff->colhdr->ndx]) { errmsg(126,rtnnme,consys->nme,"column",coeff->colhdr,coeff->colhdr->ndx, coeff->colhdr->ndx,consys->mtx.cols[coeff->colhdr->ndx]) ; return (quiet_nan(0)) ; } # endif norm = maxx(fabs(coeff->val),norm) ; } return (norm) ; }
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) ; }
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) ; }
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) ; }
static int factor_loadcol (void *p_consys, int i, int *rndx, double *coeff) /* This routine is used by luf_decomp to load columns of the basis into its internal data structure. The requirements are that it load rndx[] and coeff[] with the row indices and coefficients, respectively, of column i of the basis, returning the number of coefficients. Here, we need to look up the index j of the variable that actually occupies basis position i, retrieve the column, and load it into rndx and coeff. Parameters: p_consys: a consys_struct i: basis index rndx: (o) row indices i of coefficients a<ij> coeff: (o) coefficients a<ij> Returns: number of coefficients, or -1 in the event of an error. */ { int j,vecndx,pkndx ; double aij ; pkvec_struct *aj ; consys_struct *consys ; const char *rtnnme = "factor_loadcol" ; # ifdef DYLP_PARANOIA if (p_consys == NULL) { errmsg(2,rtnnme,"consys") ; return (-1) ; } if (rndx == NULL) { errmsg(2,rtnnme,"row index vector") ; return (-1) ; } if (coeff == NULL) { errmsg(2,rtnnme,"coefficient") ; return (-1) ; } # endif consys = (consys_struct *) p_consys ; aj = pkvec_new(consys->maxcollen) ; /* Retrieve the column ... */ j = dy_basis[i] ; if (consys_getcol_pk(consys,j,&aj) == FALSE) { errmsg(112,rtnnme,dy_sys->nme,"retrieve","column", consys_nme(dy_sys,'v',j,FALSE,NULL),j) ; if (aj != NULL) pkvec_free(aj) ; return (-1) ; } /* ... and load it into the return vectors rndx and coeff. */ vecndx = 1 ; for (pkndx = 0 ; pkndx < aj->cnt ; pkndx++) { aij = aj->coeffs[pkndx].val ; if (aij != 0.0) { rndx[vecndx] = aj->coeffs[pkndx].ndx ; coeff[vecndx] = aij ; vecndx++ ; } } /* Clean up and return. */ pkvec_free(aj) ; return (vecndx-1) ; }
bool dy_expandxopt (lpprob_struct *lp, double **p_xopt) /* This is a utility routine to load an expanded vector with the optimal solution to an lp relaxation. If the client supplies the vector, it's assumed it's large enough to hold the result. Note that unscaling is not required here. lp->x should have been unscaled when it was generated, and the client's constraint system (lp->consys) is not touched when dylp scales. Parameters: lp: lpprob_struct with optimal solution attached p_xopt: (i) vector to be filled in (created if null) (o) vector filled with optimal solution from lp Returns: TRUE if there's no problem translating the solution, FALSE otherwise. */ { int j,jpos ; consys_struct *consys ; flags *status,jstat ; double *xopt ; const char *rtnnme = "dy_expandxopt" ; # ifdef DYLP_PARANOIA if (p_xopt == NULL) { errmsg(2,rtnnme,"&x<opt>") ; return (FALSE) ; } if (lp == NULL) { errmsg(2,rtnnme,"lp problem") ; return (FALSE) ; } if (lp->lpret != lpOPTIMAL) { errmsg(4,rtnnme,"lp return code",dy_prtlpret(lp->lpret)) ; return (FALSE) ; } if (lp->consys == NULL) { errmsg(2,rtnnme,"lp constraint system") ; return (FALSE) ; } if (lp->basis == NULL) { errmsg(2,rtnnme,"lp basis") ; return (FALSE) ; } if (lp->basis->el == NULL) { errmsg(2,rtnnme,"lp basis vector") ; return (FALSE) ; } if (lp->status == NULL) { errmsg(2,rtnnme,"lp status") ; return (FALSE) ; } # endif consys = lp->consys ; status = lp->status ; /* If the user didn't supply a solution vector, allocate one now. */ if (*p_xopt == NULL) { xopt = (double *) MALLOC((consys->varcnt+1)*sizeof(double)) ; } else { xopt = *p_xopt ; } for (j = 1 ; j <= consys->varcnt ; j++) { if (((int ) status[j]) < 0) { jstat = vstatB ; jpos = -((int) status[j]) ; xopt[j] = lp->x[jpos] ; } else { jstat = status[j] ; switch (jstat) { case vstatNBFX: case vstatNBLB: { xopt[j] = consys->vlb[j] ; break ; } case vstatNBUB: { xopt[j] = consys->vub[j] ; break ; } case vstatNBFR: { xopt[j] = 0 ; break ; } default: { errmsg(359,rtnnme,consys->nme, consys_nme(consys,'v',j,FALSE,NULL),j,dy_prtvstat(jstat)) ; if (*p_xopt == NULL) FREE(xopt) ; return (FALSE) ; } } } } *p_xopt = xopt ; return (TRUE) ; }
static void stats_lp (const char *outpath, bool echo, lpprob_struct *lp, struct timeval *lptime, lpstats_struct *lpstats) /* A little shell routine to handle writing detailed statistics on an LP to the output file. Parameters: outpath: the output file path name. echo: TRUE to echo to stdout, FALSE otherwise lp: lp problem structure lptime: elapsed time for call to do_lp lpstats: lp statistics structure Returns : undefined */ { ioid chn ; int vndx,bpos ; const char *rtnnme = "stats_lp" ; /* Set up the output. Don't echo this to stdout twice. */ if (outpath == NULL) { warn(2,rtnnme,"file name") ; chn = IOID_NOSTRM ; } else { chn = dyio_pathtoid(outpath,NULL) ; if (chn == IOID_INV) chn = dyio_openfile(outpath,"w") ; if (chn == IOID_INV) { warn(10,rtnnme,outpath,"w") ; chn = IOID_NOSTRM ; } if (strcmp(outpath,"stdout") == 0) echo = FALSE ; } /* Print a few items from the lp structure --- name, status, pivot count, and lp return code. */ if (lp == NULL) { dyio_outfmt(chn,echo, "\n\n<< %s: LP problem structure is NULL! >>\n", rtnnme) ; } else { dyio_outfmt(chn,echo, "\n\nSystem: %s\t\t\tfinal status: %s after %d iterations.", lp->consys->nme,dy_prtlpphase(lp->phase,FALSE),lp->iters) ; if (lp->phase == dyDONE) { dyio_outfmt(chn,echo,"\n lp status: %s",dy_prtlpret(lp->lpret)) ; switch (lp->lpret) { case lpOPTIMAL: { dyio_outfmt(chn,echo,"\t\tobjective: %.9g",lp->obj) ; break ; } case lpINFEAS: { dyio_outfmt(chn,echo,"\t\tinfeasibility: %.9g",lp->obj) ; break ; } case lpUNBOUNDED: { if (lp->obj != 0) { if (lp->obj < 0) { vndx = abs((int) lp->obj) ; bpos = -1 ; } else { vndx = (int) lp->obj ; bpos = 1 ; } dyio_outfmt(chn,echo,"\t\tunbounded variable %s (%d) (%s)", consys_nme(lp->consys,'v',vndx,FALSE,NULL),vndx, (bpos < 0)?"decreasing":"increasing") ; } break ; } default: { break ; } } } if (lptime != NULL) { dyio_outfmt(chn,echo,"\n lp time: ") ; prt_timeval(chn,echo,lptime) ; dyio_outfmt(chn,echo," (%.2f)",lptime->tv_sec+lptime->tv_usec/1e6) ; } } # ifdef DYLP_STATISTICS if (lpstats != NULL) dy_dumpstats(chn,echo,lpstats,lp->consys) ; # endif dyio_outfmt(chn,echo,"\n") ; dyio_flushio(chn,echo) ; return ; }
void dy_colPrimals (lpprob_struct *orig_lp, double **p_x) /* This routine returns the values of the primal architectural variables (basic and nonbasic), unscaled, in the frame of reference of the original system. Unscaling is straightforward. For basic variables, we have sc_x<B> = sc_inv(B)sc_b = inv(S<B>)inv(B)inv(R)Rb = inv(S<B>)(inv(B)b) so all that's needed to recover x<B> = inv(B)b is to multiply by S<B>. Upper and lower bounds on variables have the same scaling (inv(S)). Parameters: orig_lp: the original lp problem p_x: (i) vector to hold the primal architectural variables; if NULL, a vector of appropriate size will be allocated (o) values of the primal architectural variables, unscaled, in the original system frame of reference Returns: undefined */ { int j,j_orig,n_orig ; double xj ; flags statj ; consys_struct *orig_sys ; double *x ; bool scaled ; const double *rscale,*cscale ; char *rtnnme = "dy_colPrimals" ; # ifndef DYLP_NDEBUG int v ; # endif # ifdef DYLP_PARANOIA if (dy_std_paranoia(orig_lp,rtnnme) == FALSE) { return ; } if (p_x == NULL) { errmsg(2,rtnnme,"x") ; return ; } # endif /* Is unscaling required? Acquire the scaling vectors. accordingly. */ scaled = dy_isscaled() ; if (scaled == TRUE) { dy_scaling_vectors(&rscale,&cscale) ; } orig_sys = orig_lp->consys ; n_orig = orig_sys->varcnt ; /* Do we need a vector? */ if (*p_x != NULL) { x = *p_x ; memset(x,0,(n_orig+1)*sizeof(double)) ; } else { x = (double *) CALLOC((n_orig+1),sizeof(double)) ; } /* Walk the columns of the original system. For each variable that's active (basic or nonbasic), we can obtain the value from dy_x and unscale. For each variable that's inactive, we have to do a bit of work to decode the status and look up the appropriate bound value. */ for (j_orig = 1 ; j_orig <= n_orig ; j_orig++) { if (ACTIVE_VAR(j_orig)) { j = dy_origvars[j_orig] ; if (scaled == TRUE) { xj = cscale[j_orig]*dy_x[j] ; } else { xj = dy_x[j] ; } } else { statj = (flags)(-dy_origvars[j_orig]) ; switch (statj) { case vstatNBFX: case vstatNBLB: { xj = orig_sys->vlb[j_orig] ; break ; } case vstatNBUB: { xj = orig_sys->vub[j_orig] ; break ; } case vstatNBFR: { xj = 0 ; break ; } default: { warn(359,rtnnme,orig_sys->nme, consys_nme(orig_sys,'v',j_orig,FALSE,NULL),j_orig, dy_prtvstat(statj)) ; xj = 0.0 ; break ; } } } setcleanzero(xj,dy_tols->zero) ; x[j_orig] = xj ; } # ifndef DYLP_NDEBUG if (dy_opts->print.soln >= 3) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n\tx =") ; v = 0 ; for (j_orig = 1 ; j_orig <= n_orig ; j_orig++) { if (x[j_orig] != 0) { if ((++v)%3 == 0) { v = 0 ; dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t ") ; } dyio_outfmt(dy_logchn,dy_gtxecho," (%d %g %s)", j_orig,x[j_orig], consys_nme(orig_sys,'v',j_orig,FALSE,NULL)) ; } } } # endif /* That's it. Return the vector. */ *p_x = x ; return ; }
int dytest_colDuals (lpprob_struct *main_lp, lptols_struct *main_lptols, lpopts_struct *main_lpopts) /* This routine checks the dual variables returned by dy_colDuals (more usually called the reduced costs of the architectural variables). It checks that cbar<N> = c<N> - yN, where y is the vector of row duals returned by dy_rowDuals and N is the set of nonbasic architectural columns of A (or the matching index set, as appropriate). It also checks that the reduced cost is in agreement with the status. Parameters: main_lp: the lp problem structure main_lptols: the lp tolerance structure main_lpopts: the lp options structure Returns: 0 if cbar<N> = c<N> - yN, error count otherwise. */ { int j,m,n ; flags statj ; consys_struct *sys ; double *obj ; double *y ; flags *status ; double *cbarN ; double cbarj ; int errcnt ; bool staterr ; char *errstring ; char *rtnnme = "dytest_colDuals" ; /* 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 cbar<N> = c<N> - yN using %s (%d x %d).", rtnnme,sys->nme,m,n) ; } # endif /* Acquire the row duals, column duals, status vector, and objective. We want both the (row) duals and reduced costs (column duals) to come through with sign unchanged, appropriate for a minimisation primal. */ y = NULL ; dy_rowDuals(main_lp,&y,FALSE) ; cbarN = NULL ; dy_colDuals(main_lp,&cbarN,FALSE) ; status = NULL ; dy_colStatus(main_lp,&status) ; obj = sys->obj ; /* Now step through the columns checking that cbar<j> = c<j> - dot(y,a<j>). Also check to see that the sign is correct for the status of the variable in a minimisation problem. For status values not listed (vstatSB and any of the basic status codes), there's no `correct' sign. */ errcnt = 0 ; for (j = 1 ; j <= n ; j++) { cbarj = obj[j] - consys_dotcol(sys,j,y) ; statj = status[j] ; if (fabs(cbarj-cbarN[j]) > main_lptols->cost) { errcnt++ ; dyio_outfmt(dy_logchn,dy_gtxecho, "\nERROR: col %s (%d) %s cbar<%d> = %g; expected %g;", consys_nme(sys,'v',j,FALSE,NULL),j,dy_prtvstat(statj), j,cbarj,cbarN[j]) ; dyio_outfmt(dy_logchn,dy_gtxecho," error %g, tol %g.", fabs(cbarj),main_lptols->cost) ; } staterr = FALSE ; switch (statj) { case vstatNBLB: { if (cbarj < -main_lptols->zero) { staterr = TRUE ; errstring = "positive" ; } break ; } case vstatNBUB: { if (cbarj > main_lptols->zero) { staterr = TRUE ; errstring = "negative" ; } break ; } case vstatNBFR: { if (fabs(cbarj) > main_lptols->zero) { staterr = TRUE ; errstring = "zero" ; } break ; } default: { break ; } } if (staterr == TRUE) { errcnt++ ; dyio_outfmt(dy_logchn,dy_gtxecho, "\nERROR: col %s (%d) %s cbar<%d> = %g; should be %s.", consys_nme(sys,'v',j,FALSE,NULL),j,dy_prtvstat(statj), j,cbarj,errstring) ; } } /* Free up space and report the result. */ if (y != NULL) FREE(y) ; if (cbarN != NULL) FREE(cbarN) ; if (status != NULL) FREE(status) ; if (errcnt != 0) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n%s: found %d errors testing cbar<N> = c<N> - yN.\n", rtnnme,errcnt) ; } else { dyio_outfmt(dy_logchn,dy_gtxecho, "\n%s: pass cbar<N> = c<N> - yN.\n",rtnnme) ; } return (errcnt) ; }
void dy_logPrimals (lpprob_struct *orig_lp, double **p_logx) /* This routine returns the values of the primal logical variables, unscaled, in the frame of reference of the original system (i.e., the value of the logical for constraint i is in position i of the vector). Unscaling is straightforward: sc_x<B> = sc_inv(B)sc_b = inv(S<B>)inv(B)inv(R)Rb = inv(S<B>)(inv(B)b) so all that's needed to recover x<B> = inv(B)b is to multiply by S<B>. We just have to remember that for a logical, S<i> = 1/R<i>. It's more work to get the value of the logical for an inactive constraint --- we have to actually calculate b - dot(a<i>,x). Parameters: orig_lp: the original lp problem p_logx: (i) vector to hold the primal logical variables; if NULL, a vector of appropriate size will be allocated (o) values of the primal logical variables, unscaled, in the original system frame of reference Returns: undefined */ { int j,m,i_orig,m_orig ; double xj,lhs ; consys_struct *orig_sys ; double *logx,*x ; bool scaled ; const double *rscale,*cscale ; # ifndef DYLP_NDEBUG int v,n_orig ; # endif # ifdef DYLP_PARANOIA char *rtnnme = "dy_logPrimals" ; if (dy_std_paranoia(orig_lp,rtnnme) == FALSE) { return ; } if (p_logx == NULL) { errmsg(2,rtnnme,"logx") ; return ; } # endif /* Is unscaling required? Acquire the scaling vectors. If we have inactive constraints, we'll need the values of the architecturals in order to calculate the value of the associated logical. */ scaled = dy_isscaled() ; if (scaled == TRUE) { dy_scaling_vectors(&rscale,&cscale) ; } orig_sys = orig_lp->consys ; m_orig = orig_sys->concnt ; m = dy_sys->concnt ; x = NULL ; if (m < m_orig) { dy_colPrimals(orig_lp,&x) ; } /* Do we need a vector? */ if (*p_logx != NULL) { logx = *p_logx ; memset(logx,0,(m_orig+1)*sizeof(double)) ; } else { logx = (double *) CALLOC((m_orig+1),sizeof(double)) ; } /* Walk the rows of the original system. For each constraint that's active, we can obtain the value of the associated logical from dy_x. For each constraint that's inactive, we have to actually calculate the row activity dot(x,a<i>) and do the arithmetic. */ for (i_orig = 1 ; i_orig <= m_orig ; i_orig++) { if (ACTIVE_CON(i_orig)) { j = dy_origcons[i_orig] ; if (scaled == TRUE) { xj = (1/rscale[i_orig])*dy_x[j] ; } else { xj = dy_x[j] ; } } else { lhs = consys_dotrow(orig_sys,i_orig,x) ; xj = orig_sys->rhs[i_orig]-lhs ; } setcleanzero(xj,dy_tols->zero) ; logx[i_orig] = xj ; } if (x != NULL) FREE(x) ; # ifndef DYLP_NDEBUG if (dy_opts->print.soln >= 3) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n\tlogx =") ; n_orig = orig_sys->varcnt ; v = 0 ; for (i_orig = 1 ; i_orig <= m_orig ; i_orig++) { if (logx[i_orig] != 0) { if ((++v)%3 == 0) { v = 0 ; dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t ") ; } dyio_outfmt(dy_logchn,dy_gtxecho," (%d %g %s)", i_orig,logx[i_orig], consys_nme(orig_sys,'v',n_orig+i_orig,FALSE,NULL)) ; } } } # endif /* That's it. Return the vector. */ *p_logx = logx ; return ; }
int dytest_rowDuals (lpprob_struct *main_lp, lptols_struct *main_lptols, lpopts_struct *main_lpopts) /* This routine checks the dual variables returned by dy_rowDuals. It checks that y<i> = c<B>(inv(B))<i>. Columns of the basis inverse are obtained from the routine dy_betaj. Parameters: main_lp: the lp problem structure main_lptols: the lp tolerance structure main_lpopts: the lp options structure Returns: 0 if y = c<B>inv(B), error count otherwise. */ { int i,j,k,m,n ; consys_struct *sys ; flags *status ; double *y ; double *cB ; int *basis2sys ; basisel_struct *basisVec ; int basisLen ; double *betai ; double cBdotbetai ; int errcnt ; char *rtnnme = "dytest_rowDuals" ; /* 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 y = c<B>inv(B) using %s (%d x %d).", rtnnme,sys->nme,m,n) ; if (main_lpopts->print.soln >= 2) { dyio_outfmt(dy_logchn,dy_gtxecho, " basis contains %d entries.\n",main_lp->basis->len) ; } } # endif /* Acquire the row duals and the status vector. For this check, we can use the min primal sign convention. */ y = NULL ; dy_rowDuals(main_lp,&y,FALSE) ; status = main_lp->status ; /* Make a vector c<B> of objective coefficients in basis order. This is considerably easier than creating a basis matrix (as is done for tableau testing). By construction, the basic variables for inactive constraints are the logicals, which have an objective coefficient of zero, and this is how cB and basis2sys are initialised. All that need be done for c<B> is to change the entries that are associated with architecturals. For the basis, we need to set all entries (logicals can be basic out of natural position). Recall that basic logicals are represented by negative indices. */ cB = (double *) MALLOC((m+1)*sizeof(double)) ; basis2sys = (int *) MALLOC((m+1)*sizeof(int)) ; for (i = 1 ; i <= m ; i++) { cB[i] = 0.0 ; basis2sys[i] = -i ; } basisLen = main_lp->basis->len ; basisVec = main_lp->basis->el ; for (k = 1 ; k <= basisLen ; k++) { i = basisVec[k].cndx ; j = basisVec[k].vndx ; if (j > 0) { cB[i] = sys->obj[j] ; basis2sys[i] = j ; } else { basis2sys[i] = j ; } } # ifndef DYLP_NDEBUG if (main_lpopts->print.soln >= 5) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n\tc<B> =") ; k = 0 ; for (i = 1 ; i <= m ; i++) { if (cB[i] != 0) { if ((++k)%4 == 0) { k = 0 ; dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t ") ; } j = basis2sys[i] ; dyio_outfmt(dy_logchn,dy_gtxecho," (%d %g %s %d)", i,cB[i],consys_nme(sys,'v',j,FALSE,NULL),j) ; } } } # endif /* Now step through the rows (equivalently, walk the basis) and see if y<i> = c<B>beta<j>, where beta<j> is the column of inv(B) such that x<j> is basic in pos'n i. */ errcnt = 0 ; betai = NULL ; for (i = 1 ; i <= m ; i++) { j = basis2sys[i] ; if (dy_betaj(main_lp,j,&betai) == FALSE) { errcnt++ ; if (j < 0) { j = n-j ; } errmsg(952,rtnnme,sys->nme,"column",i,"variable", consys_nme(sys,'v',j,FALSE,NULL),j-n) ; continue ; } cBdotbetai = 0 ; for (k = 1 ; k <= m ; k++) { /* dyio_outfmt(dy_logchn,dy_gtxecho, "\n %s (%d) %g * %g", consys_nme(sys,'c',k,FALSE,NULL),k,cB[k],betai[k]) ; */ cBdotbetai += cB[k]*betai[k] ; } if (fabs(cBdotbetai-y[i]) > main_lptols->cost) { errcnt++ ; if (j < 0) { j = n-j ; } dyio_outfmt(dy_logchn,dy_gtxecho, "\n ERROR: pos'n %d %s (%d) c<B> dot beta<j> = %g; ", i,consys_nme(sys,'v',j,FALSE,NULL),j-n,cBdotbetai) ; dyio_outfmt(dy_logchn,dy_gtxecho,"expected %g; err %g, tol %g.", y[i],(cBdotbetai-y[i]),main_lptols->zero) ; } } /* Free up space and report the result. */ if (cB != NULL) FREE(cB) ; if (basis2sys != NULL) FREE(basis2sys) ; if (betai != NULL) FREE(betai) ; if (y != NULL) FREE(y) ; if (errcnt != 0) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n%s: found %d errors testing y = c<B>inv(B).\n", rtnnme,errcnt) ; } else { dyio_outfmt(dy_logchn,dy_gtxecho,"\n%s: pass y = c<B>inv(B).\n",rtnnme) ; } return (errcnt) ; }
void dy_logStatus (lpprob_struct *orig_lp, flags **p_logstat) /* This routine returns the status of the primal logical variables, in row order for the original system. The routine reports out the full set of dylp status codes. It's actually a fair bit of work to get the status right for inactive constraints. Because we're reporting the full set of dylp status codes, and the client might be calling in a situation where the outcome was infeasible or unbounded, we need to calculate the value and assign the appropriate status code. Parameters: orig_lp: the original lp problem p_logstat: (i) vector to hold the status of the primal logical variables; if NULL, a vector of appropriate size will be allocated (o) status of the primal logical variables, in the original system frame of reference Returns: undefined */ { int i,m,i_orig,m_orig ; flags stati ; double rhsi,rhslowi,lhsi,xi,lbi,ubi ; consys_struct *orig_sys ; flags *logstat ; double *x ; char *rtnnme = "dy_logStatus" ; # ifndef DYLP_NDEBUG int v,n_orig ; # endif # ifdef DYLP_PARANOIA if (dy_std_paranoia(orig_lp,rtnnme) == FALSE) { return ; } if (p_logstat == NULL) { errmsg(2,rtnnme,"logstat") ; return ; } # endif orig_sys = orig_lp->consys ; m_orig = orig_sys->concnt ; m = dy_sys->concnt ; /* If we're not playing with a full deck, we'll need the values of the architecturals to determine the appropriate status for the logical. */ x = NULL ; if (m < m_orig) { dy_colPrimals(orig_lp,&x) ; } /* Do we need a vector? */ if (*p_logstat != NULL) { logstat = *p_logstat ; memset(logstat,0,(m_orig+1)*sizeof(flags)) ; } else { logstat = (flags *) CALLOC((m_orig+1),sizeof(flags)) ; } /* Walk the rows of the original system. For active constraints, copy the status of the logical from dy_status. For inactive constraints, we need to actually calculate the value of the logical and assign the appropriate status. This is more work than you'd think, because we need to determine the appropriate bounds for the logical based on the constraint type, and we need to allow for the possibility that the problem was infeasible or unbounded and the logical is not within bounds. We also need to allow for the possibility that dylp deactivated a tight constraint with y<i> = 0. The convention for logicals in the original system is that all have a coefficient of 1.0. Thus we have bounds of (0,infty) for a slack (contypLE), (0,0) for an artificial (contypEQ), (-infty,0) for a surplus (contypGE), and (0,rhs-rhslow) for a bounded slack (contypRNG). */ for (i_orig = 1 ; i_orig <= m_orig ; i_orig++) { if (ACTIVE_CON(i_orig)) { i = dy_origcons[i_orig] ; stati = dy_status[i] ; } else { lhsi = consys_dotrow(orig_sys,i_orig,x) ; rhsi = orig_sys->rhs[i_orig] ; xi = rhsi-lhsi ; setcleanzero(xi,dy_tols->zero) ; lbi = -dy_tols->inf ; ubi = dy_tols->inf ; switch (orig_sys->ctyp[i_orig]) { case contypLE: { lbi = 0.0 ; break ; } case contypEQ: { lbi = 0.0 ; ubi = 0.0 ; break ; } case contypGE: { ubi = 0.0 ; break ; } case contypRNG: { rhslowi = orig_sys->rhslow[i_orig] ; lbi = 0 ; ubi = rhsi-rhslowi ; break ; } case contypNB: { continue ; } default: { errmsg(1,rtnnme,__LINE__) ; break ; } } if (belowbnd(xi,lbi)) { stati = vstatBLLB ; } else if (atbnd(xi,lbi)) { stati = vstatBLB ; } else if (atbnd(xi,ubi)) { stati = vstatBUB ; } else if (abovebnd(xi,ubi)) { stati = vstatBUUB ; } else { stati = vstatB ; } } logstat[i_orig] = stati ; } if (x != NULL) FREE(x) ; # ifndef DYLP_NDEBUG if (dy_opts->print.soln >= 3) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n\trowstat =") ; n_orig = orig_sys->varcnt ; v = 0 ; for (i_orig = 1 ; i_orig <= m_orig ; i_orig++) { if ((++v)%3 == 0) { v = 0 ; dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t ") ; } dyio_outfmt(dy_logchn,dy_gtxecho," (%s %d %s)", consys_nme(orig_sys,'v',i_orig+n_orig,FALSE,NULL),i_orig, dy_prtvstat(logstat[i_orig])) ; } } # endif /* That's it. Return the vector. */ *p_logstat = logstat ; return ; }
int dytest_allDuals (lpprob_struct *main_lp, lptols_struct *main_lptols, lpopts_struct *main_lpopts) /* This routine uses the dual variables returned by dy_rowDuals and dy_colDuals and checks that yA >= (-c) (row duals only) and y'A' = (-c), where y' is both row and column duals and A' is A, expanded as needed with coefficients to add explicit bound constraints for nonbasic architecturals. As with so many things involving faking dual simplex on the primal constraint system with implicit bounds, we have to be a bit careful when working with the duals corresponding to nonbasic primal variables. Consider a primal variable x<j> NBUB. The reduced cost cbar<j> will be negative at optimality in dylp's min primal world. This is not correct for the sign convention of the true dual problem, where all duals are positive, so it's negated when we ask for the true dual sign convention. But then only a little thought reveals that we're considering yA + y<j> = (-c), and if y<j> >= 0 it's clear that yA <= (-c). So we have to invert the sense of that test when processing a column with an NBUB primal. Since the sign of the reduced cost for an NBFX variable can go either way, no test is possible using only the row duals. Parameters: main_lp: the lp problem structure main_lptols: the lp tolerance structure main_lpopts: the lp options structure Returns: 0 if yA = c, error count otherwise. */ { int i,j,k,m,n ; consys_struct *sys ; double *obj ; double *y,*cbar ; double ydotaj,cj,cbarj ; flags *status ; flags statj ; int errcnt ; char *rtnnme = "dytest_allDuals" ; /* Do a little initialisation. Mention that we've started. */ sys = main_lp->consys ; m = sys->concnt ; n = sys->varcnt ; obj = sys->obj ; # ifndef DYLP_NDEBUG if (main_lpopts->print.soln >= 1) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n%s: checking yA = c using %s (%d x %d).", rtnnme,sys->nme,m,n) ; } # endif /* Acquire the row and column duals and column status. Go with the sign convention that matches the true dual problem. */ y = NULL ; dy_rowDuals(main_lp,&y,TRUE) ; cbar = NULL ; dy_colDuals(main_lp,&cbar,TRUE) ; status = NULL ; dy_colStatus(main_lp,&status) ; /* Open a loop to walk the columns. First check that yA >= (-c) for a column with an NBLB primal variable, yA <= (-c) for a column with an NBUB primal variable. For an NBFX variable, the dual could go either way, so we can't check. */ errcnt = 0 ; for (j = 1 ; j <= n ; j++) { statj = status[j] ; cj = -obj[j] ; ydotaj = consys_dotcol(sys,j,y) ; if ((flgon(statj,vstatNBLB) && ydotaj-cj < -main_lptols->cost) || (flgon(statj,vstatNBUB) && ydotaj-cj > main_lptols->cost)) { errcnt++ ; dyio_outfmt(dy_logchn,dy_gtxecho, "\n ERROR: %s (%d) y dot a<j> = %g; ", consys_nme(sys,'v',j,FALSE,NULL),j,ydotaj) ; dyio_outfmt(dy_logchn,dy_gtxecho,"expected %s %g; err %g, tol %g.", (flgon(statj,vstatNBUB)?"<=":">="), cj,ydotaj-cj,main_lptols->cost) ; } /* Now add any contribution due to an architectural at bound. After this we should have equality. For an upper bound, we have x<j> <= u<j>. For a lower bound, it's -x<j> <= -l<j>. For a fixed variable, it's an equality x<j> = u<j>, so lump NBFX with NBUB. */ if (flgon(statj,vstatNONBASIC)) { cbarj = cbar[j] ; switch (statj) { case vstatNBLB: { ydotaj -= cbarj ; break ; } case vstatNBUB: case vstatNBFX: { ydotaj += cbarj ; break ; } default: { errmsg(1,rtnnme,__LINE__) ; errcnt += 10000 ; ydotaj = quiet_nan(42.0L) ; break ; } } } if (fabs(ydotaj-cj) > main_lptols->cost) { errcnt++ ; dyio_outfmt(dy_logchn,dy_gtxecho, "\n ERROR: %s (%d) y dot a<j> = %g; ", consys_nme(sys,'v',j,FALSE,NULL),j,ydotaj) ; dyio_outfmt(dy_logchn,dy_gtxecho,"expected %g; err %g, tol %g.", cj,fabs(ydotaj-cj),main_lptols->cost) ; } } /* Free up space and report the result. */ if (y != NULL) FREE(y) ; if (cbar != NULL) FREE(cbar) ; if (status != NULL) FREE(status) ; if (errcnt != 0) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n%s: found %d errors testing yA = c.\n", rtnnme,errcnt) ; } else { dyio_outfmt(dy_logchn,dy_gtxecho,"\n%s: pass yA = c.\n",rtnnme) ; } return (errcnt) ; }
static void process_active (lpprob_struct *orig_lp, int oxkndx) /* This routine handles the data structure updates for an active variable x<k>. We need to copy the new values for l<k>, u<k>, and c<k> into the active system. For nonbasic variables, we need to choose a status based on the bounds. For basic variables, the status vector encodes the basis index, so we need to decide on an initial status --- either B, BFX, or BFR. The routine expects that bounds have been groomed (i.e., if the difference between l<k> and u<k> is less than the feasibility tolerance, they have been forced to exact equality). Parameters: orig_lp: the original lp problem oxkndx: index of x<k> in orig_sys Returns: undefined (the only possible error is a paranoid check) */ { int xkndx ; double lk,uk,xk ; flags xkstatus ; consys_struct *orig_sys ; # ifdef DYLP_PARANOIA const char *rtnnme = "process_active" ; # endif orig_sys = orig_lp->consys ; /* Get the index of the variable in the active system, and the status. The paranoid check is that we're not attempting to convert between basic and nonbasic status. */ xkndx = dy_origvars[oxkndx] ; xkstatus = dy_status[xkndx] ; # ifdef DYLP_PARANOIA if ((flgon(xkstatus,vstatBASIC) && ((int) orig_lp->status[oxkndx]) > 0) || (flgon(xkstatus,vstatNONBASIC|vstatNBFR) && ((int) orig_lp->status[oxkndx]) < 0)) { char buf[30] ; if (((int) orig_lp->status[oxkndx]) > 0) strcpy(buf,dy_prtvstat(orig_lp->status[oxkndx])) ; else strcpy(buf,"unspecified basic") ; errmsg(398,rtnnme,dy_sys->nme,consys_nme(dy_sys,'v',xkndx,FALSE,NULL), xkndx,dy_prtvstat(xkstatus),buf) ; return ; } # endif /* Update the bounds and objective coefficient. */ lk = orig_sys->vlb[oxkndx] ; dy_sys->vlb[xkndx] = lk ; uk = orig_sys->vub[oxkndx] ; dy_sys->vub[xkndx] = uk ; dy_sys->obj[xkndx] = orig_sys->obj[oxkndx] ; /* For nonbasic variables, set the proper status based on the bounds and put the proper value in dy_x. Because the bounds can change arbitrarily and the client may not be maintaining the status vector, it's easiest to start from scratch, using the value from dy_x to decide the best new status. For basic variables, just decide between strictly basic (B), basic fixed (BFX), and basic free (BFR). This will be correct, in the absence of bound changes, and the values held in dy_x and dy_xbasic are unchanged. If bounds have changed, we'll recalculate the primal variables and then decide on the final status of basic variables (which could be BLLB or BUUB). */ if (flgon(dy_status[xkndx],vstatNONBASIC|vstatNBFR)) { if (lk > -dy_tols->inf && uk < dy_tols->inf) { if (lk == uk) { xkstatus = vstatNBFX ; xk = lk ; } else if ((dy_x[xkndx] - lk) < (uk-dy_x[xkndx])) { xkstatus = vstatNBLB ; xk = lk ; } else { xkstatus = vstatNBUB ; xk = uk ; } } else if (lk > -dy_tols->inf) { xkstatus = vstatNBLB ; xk = lk ; } else if (uk < dy_tols->inf) { xkstatus = vstatNBUB ; xk = uk ; } else { xkstatus = vstatNBFR ; xk = 0 ; } dy_x[xkndx] = xk ; } else { if (lk == uk) xkstatus = vstatBFX ; else if (lk <= -dy_tols->inf && uk >= dy_tols->inf) xkstatus = vstatBFR ; else xkstatus = vstatB ; } dy_status[xkndx] = xkstatus ; /* 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 active", consys_nme(dy_sys,'v',xkndx,FALSE,NULL),xkndx, dy_prtvstat(dy_status[xkndx])) ; if (flgon(xkstatus,vstatNONBASIC|vstatNBFR)) dyio_outfmt(dy_logchn,dy_gtxecho," with value %g.",dy_x[xkndx]) ; else dyio_outchr(dy_logchn,dy_gtxecho,'.') ; } # endif return ; }
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) ; }
void dy_setfinalstatus (void) /* This code is common to all three start routines (coldstart, warmstart, hotstart). It scans the newly calculated basic variables and assigns them their final status. In the process, it calculates the number of infeasible variables, and the total infeasibility. Parameters: none Returns: undefined */ { int aindx, xkndx ; double xk,lbk,ubk ; # ifdef DYLP_PARANOIA const char *rtnnme = "dy_setfinalstatus" ; # endif # ifndef DYLP_NDEBUG if (dy_opts->print.crash >= 2) dyio_outfmt(dy_logchn,dy_gtxecho,"\n\testablishing final status ...") ; # endif dy_lp->infeas = 0.0 ; dy_lp->infeascnt = 0 ; /* Step through the constraints, and have a look at the basic variable in each position. The paranoid check will complain if the basis is corrupt, but since nothing can go wrong if we're not paranoid, it just complains and moves to the next entry. */ for (aindx = 1 ; aindx <= dy_sys->concnt ; aindx++) { xkndx = dy_basis[aindx] ; xk = dy_xbasic[aindx] ; lbk = dy_sys->vlb[xkndx] ; ubk = dy_sys->vub[xkndx] ; # ifdef DYLP_PARANOIA if (xkndx <= 0 || xkndx > dy_sys->varcnt) { errmsg(303,rtnnme,dy_sys->nme,aindx,1,xkndx,dy_sys->varcnt) ; continue ; } # endif switch (dy_status[xkndx]) { case vstatB: { if (atbnd(xk,lbk)) { dy_status[xkndx] = vstatBLB ; } else if (belowbnd(xk,lbk)) { dy_lp->infeascnt++ ; dy_lp->infeas += lbk-xk ; dy_status[xkndx] = vstatBLLB ; } else if (atbnd(xk,ubk)) { dy_status[xkndx] = vstatBUB ; } else if (abovebnd(xk,ubk)) { dy_lp->infeascnt++ ; dy_lp->infeas += xk-ubk ; dy_status[xkndx] = vstatBUUB ; } break ; } case vstatBFX: { if (!atbnd(xk,lbk)) { if (belowbnd(xk,lbk)) { dy_lp->infeascnt++ ; dy_lp->infeas += lbk-xk ; dy_status[xkndx] = vstatBLLB ; } else { dy_lp->infeascnt++ ; dy_lp->infeas += xk-ubk ; dy_status[xkndx] = vstatBUUB ; } } break ; } } # 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',xkndx,FALSE,NULL),xkndx, dy_prtvstat(dy_status[xkndx])) ; if (lbk > -dy_tols->inf) dyio_outfmt(dy_logchn,dy_gtxecho,", lb = %g",lbk) ; dyio_outfmt(dy_logchn,dy_gtxecho,", val = %g",xk) ; if (ubk < dy_tols->inf) dyio_outfmt(dy_logchn,dy_gtxecho,", ub = %g",ubk) ; if (flgon(dy_status[xkndx],vstatBLLB|vstatBUUB)) { dyio_outfmt(dy_logchn,dy_gtxecho,", infeasibility = ") ; if (flgon(dy_status[xkndx],vstatBLLB)) dyio_outfmt(dy_logchn,dy_gtxecho,"%g",lbk-xk) ; else dyio_outfmt(dy_logchn,dy_gtxecho,"%g",xk-ubk) ; } dyio_outchr(dy_logchn,dy_gtxecho,'.') ; } # endif } setcleanzero(dy_lp->infeas,dy_tols->zero) ; return ; }
bool consys_mulaccumcol (consys_struct *consys, int colndx, double scalar, double *vec) /* This routine multiplies the column specified by colndx by scalar and then adds it to the expanded vector passed in vec. Identical to consys_accumcol, except for the multiplication. Parameters: consys: constraint system colndx: column scalar: scalar multiplier for column vec: vector Returns: TRUE if there are no problems, FALSE otherwise. */ { colhdr_struct *colhdr ; coeff_struct *coeff ; # if defined(DYLP_PARANOIA) || !defined(DYLP_NDEBUG) const char *rtnnme = "consys_accumcol" ; # endif # ifdef DYLP_PARANOIA if (consys == NULL) { errmsg(2,rtnnme,"consys") ; return (FALSE) ; } if (consys->mtx.cols == NULL) { errmsg(101,rtnnme,consys->nme,"column header") ; return (FALSE) ; } # endif # ifndef DYLP_NDEBUG if (colndx <= 0 || colndx > consys->varcnt) { errmsg(102,rtnnme,consys->nme,"column",colndx,1,consys->varcnt) ; return (FALSE) ; } # endif colhdr = consys->mtx.cols[colndx] ; # ifdef DYLP_PARANOIA if (colhdr == NULL) { errmsg(103,rtnnme,consys->nme,"column",colndx) ; return (FALSE) ; } if (colndx != colhdr->ndx) { errmsg(126,rtnnme,consys->nme,"column",colhdr,colhdr->ndx,colndx,colhdr) ; return (FALSE) ; } if (vec == NULL) { errmsg(2,rtnnme,"vec") ; return (FALSE) ; } # endif for (coeff = colhdr->coeffs ; coeff != NULL ; coeff = coeff->colnxt) { # ifdef DYLP_PARANOIA if (coeff->rowhdr == NULL) { errmsg(125,rtnnme,consys->nme,"rowhdr",coeff,"column", consys_nme(consys,'v',colndx,FALSE,NULL),colndx) ; return (FALSE) ; } if (coeff->rowhdr->ndx <= 0 || coeff->rowhdr->ndx > consys->concnt) { errmsg(102,rtnnme,consys->nme,"row",coeff->rowhdr->ndx, 1,consys->concnt) ; return (FALSE) ; } if (coeff->rowhdr != consys->mtx.rows[coeff->rowhdr->ndx]) { errmsg(126,rtnnme,consys->nme,"row",coeff->rowhdr,coeff->rowhdr->ndx, coeff->rowhdr->ndx,consys->mtx.rows[coeff->rowhdr->ndx]) ; return (FALSE) ; } # endif vec[coeff->rowhdr->ndx] += scalar*coeff->val ; } return (TRUE) ; }