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_dealWithPunt (void) /* This routine decides on the appropriate action(s) when a simplex decides to punt. The algorithm is this: 1) Sort the entries in pivrejlst into two sets: iter == basis.pivs (current) and iter != basis.pivs (old). In the current set, count the number of mad and singular entries. 2) If there are any entries in old, remove them from pivrejlst and return with an indication to resume pivoting (dyrRESELECT). 3) If all entries in current are of type singular, return with an indication to abort this simplex phase (dyrPUNT) and hope that we can alter the constraint system. 4) For each permissible reduction in pivot tolerance, check for entries of type MADPIV that might become acceptable. If there are any, remove them from pivrejlst and return dyrRESELECT. 5) If 4) failed to identify pivots, return dyrPUNT. Parameters: none Returns: dyrRESELECT if pivoting can resume dyrPUNT to abort this simplex phase dyrFATAL if something goes wrong */ { int j,ndx,last,oldcnt,curcnt,curmad,brk ; double maxratio,pivmul ; bool clr_retval ; dyret_enum retval ; int *old,*current ; pivrej_struct *pivrej ; # ifndef DYLP_NDEBUG const char *rtnnme = "dy_dealWithPunt" ; # endif # ifdef DYLP_STATISTICS if (dy_stats != NULL) dy_stats->pivrej.puntcall++ ; # endif retval = dyrINV ; /* If there are no rejected pivots, the punt stands. */ if (pivrej_ctl.cnt == 0) { # ifdef DYLP_STATISTICS if (dy_stats != NULL) dy_stats->pivrej.puntret++ ; # endif return (dyrPUNT) ; } /* Setup and scan pivrejlst as indicated above. */ last = pivrej_ctl.cnt ; brk = dy_lp->basis.pivs ; old = (int *) MALLOC((last+1)*sizeof(int)) ; current = (int *) MALLOC((last+1)*sizeof(int)) ; oldcnt = 0 ; curcnt = 0 ; curmad = 0 ; maxratio = 0 ; for (ndx = 0 ; ndx < last ; ndx++) { pivrej = &pivrejlst[ndx] ; if (pivrej->iter != brk) { old[++oldcnt] = ndx ; } else { current[++curcnt] = ndx ; if (pivrej->why == dyrMADPIV) { curmad++ ; if (maxratio < pivrej->ratio) maxratio = pivrej->ratio ; } } } /* If there are old entries, we can always hope the intervening pivots have cured the problem. It happens. */ if (oldcnt > 0) { old[0] = oldcnt ; clr_retval = dy_clrpivrej(old) ; if (clr_retval == TRUE) { retval = dyrRESELECT ; } else { retval = dyrFATAL ; } # ifndef DYLP_NDEBUG if (dy_opts->print.pivreject >= 1) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n restored %d entries queued before iter = %d.", old[0],brk) ; } # endif } /* Are there any mad pivots that we can press into service by reducing the pivot tolerance? */ else if (curmad > 0 && maxratio > dy_tols->zero) { pivmul = 1/dy_tols->pivot ; while (maxratio*pivmul < 1.0) pivmul *= pivrej_ctl.pivmul ; if (1/pivmul >= dy_tols->zero*100) { # ifndef DYLP_NDEBUG if (dy_opts->print.pivreject >= 1) { warn(376,rtnnme, dy_sys->nme,dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters, dy_tols->pivot,1/pivmul) ; } # endif dy_tols->pivot = 1/pivmul ; # ifdef DYLP_STATISTICS if (dy_stats != NULL) { dy_stats->pivrej.pivtol_red++ ; if (dy_tols->pivot < dy_stats->pivrej.min_pivtol) { dy_stats->pivrej.min_pivtol = dy_tols->pivot ; } } # endif j = 0 ; for (ndx = 1 ; ndx <= curcnt ; ndx++) { pivrej = &pivrejlst[current[ndx]] ; if (pivrej->ratio*pivmul > 1.0) { current[++j] = current[ndx] ; } } current[0] = j ; clr_retval = dy_clrpivrej(current) ; if (clr_retval == TRUE) { retval = dyrRESELECT ; } else { retval = dyrFATAL ; } # ifndef DYLP_NDEBUG if (dy_opts->print.pivreject >= 1) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n restored %d entries queued at iter = %d at piv. tol = %g", current[0],brk,dy_tols->pivot) ; } # endif } else { # ifndef DYLP_NDEBUG if (dy_opts->print.pivreject >= 1) { warn(383,rtnnme,dy_sys->nme, dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters, dy_tols->zero,dy_prtdyret(dyrPUNT)) ; } # endif retval = dyrPUNT ; } } else { retval = dyrPUNT ; } /* That's it, we've done our best. Free the old and current arrays and return. */ FREE(old) ; FREE(current) ; # ifndef DYLP_NDEBUG if (retval == dyrPUNT && dy_opts->print.pivreject >= 1) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n PUNT! mad = %d, singular = %d.", pivrej_ctl.mad,pivrej_ctl.sing) ; } # endif # ifdef DYLP_STATISTICS if (dy_stats != NULL && retval == dyrPUNT) dy_stats->pivrej.puntret++ ; # endif return (retval) ; }