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) ; }
int main (int argc, char *argv[]) { time_t timeval ; struct tm *tm ; char runtime[50] ; ioid ttyin,ttyout,outchn ; int optlett,printlvl ; bool silent,terse,swaperrs,errecho,doversion,dohelp ; const char *errmsgpath,*errlogpath,*optpath,*mpspath,*logpath ; struct timeval lptime ; const char *rtnnme = argv[0] ; /* getopt() --- we need explicit declarations under strict ANSI compliance mode. This function seems to be quite common, however. Present in GCC. */ int getopt(int argc, char*const *argv, const char *optstring) ; extern char *optarg ; extern int opterr,optind,optopt ; const char *optstring = "e:stp:E:o:m:L:O:hv"; extern struct tm *localtime(const time_t *clock) ; /* mpsio.c */ extern bool dy_mpsin(const char *mpspath, consys_struct **consys, double infinity); /* dy_basis.c */ extern void dy_initbasis(int concnt, int factor_freq, double zero_tol), dy_freebasis(void) ; /* Set up some defaults, then process the command line options. This is all very specific to Unix and SunOS. */ errmsgpath = DYLP_ERRMSGPATH ; errlogpath = NULL ; optpath = NULL ; mpspath = NULL ; outpath = NULL ; logpath = NULL ; ttyout = IOID_INV ; ttyin = IOID_INV ; dy_logchn = IOID_INV ; outchn = IOID_INV ; silent = FALSE ; terse = FALSE ; dy_gtxecho = TRUE ; dy_cmdecho = FALSE ; doversion = FALSE ; dohelp = FALSE ; printlvl = -1 ; opterr = 0 ; for (optlett = getopt(argc,argv,optstring) ; optlett != -1 ; optlett = getopt(argc,argv,optstring)) switch (optlett) { case 'e': { errmsgpath = optarg ; break ; } case 'E': { errlogpath = optarg ; break ; } case 'o': { optpath = optarg ; break ; } case 'm': { mpspath = optarg ; break ; } case 'L': { logpath = optarg ; break ; } case 'O': { outpath = optarg ; break ; } case 's': { silent = TRUE ; dy_gtxecho = FALSE ; break ; } case 't': { terse = TRUE ; dy_gtxecho = FALSE ; break ; } case 'p': { printlvl = atoi(optarg) ; break ; } case 'v': { doversion = TRUE ; break ; } case 'h': { dohelp = TRUE ; break ; } case '?': { errinit(errmsgpath,errlogpath,TRUE) ; dyio_ioinit() ; errmsg(3,rtnnme,"command line option",optopt) ; exit (1) ; } } /* If there's still a parameter left, it must be the mps file, specified without using the -m option. There should not be more than one parameter remaining. */ if (optind < argc) { mpspath = argv[optind] ; optind++ ; } if (optind < argc) { dohelp = TRUE ; } /* What's our output level? If the user has specified a print level, go with it. Otherwise, take a cue from any -s or -t flags. Default to print level 2. */ if (printlvl < 0) { if (silent == TRUE) printlvl = 0 ; else if (terse == TRUE) printlvl = 1 ; else printlvl = 2 ; } /* Output file name: if the user hasn't specified one, and we're not running silent, default to stdout. */ if (outpath == NULL && silent == FALSE) outpath = "stdout" ; /* Grab the time and format it nicely. */ if (time(&timeval) == (time_t)(-1)) { warn(19,rtnnme) ; osidylp_time = "n/a" ; } else { tm = localtime(&timeval) ; if (tm != NULL) { strftime(runtime,sizeof(runtime),"%A, %B %d, %Y, %I:%M:%S %p",tm) ; osidylp_time = runtime ; } else { osidylp_time = "n/a" ; } } /* Figure out the appropriate settings for silent and terse. silent is set to (silent || terse), and is passed to process_cmds so that it can properly handle dy_cmdecho and dy_gtxecho. terse is set for ease of controlling the output specifically mentioned in conjunction with terse mode (which is controlled from this routine). The proper value is (terse || !silent). The cryptic little if statement below accomplishes this (try a decision tree to convince yourself). */ if (terse == TRUE) { if (silent == TRUE) terse = FALSE ; else silent = TRUE ; } else { if (silent == FALSE) terse = TRUE ; } /* Execute initialization routines for the i/o and error reporting packages. */ if (silent == TRUE) errecho = FALSE ; else errecho = TRUE ; errinit(errmsgpath,errlogpath,errecho) ; if (dyio_ioinit() != TRUE) { errmsg(1,rtnnme,__LINE__) ; exit (2) ; } /* Connect ttyout to the standard output. Initialize ttyin, setting the mode to line-oriented. Serious internal confusion if we can't manage these. Set the initial command input channel to stdin. */ ttyout = dyio_openfile("stdout","w") ; if (ttyout == IOID_INV) { errmsg(1,rtnnme,__LINE__) ; exit(3) ; } ttyin = dyio_openfile("stdin","r") ; if (ttyin == IOID_INV) { errmsg(1,rtnnme,__LINE__) ; exit(4) ; } (void) dyio_setmode(ttyin,'l') ; dy_cmdchn = ttyin ; /* Initialize logging. */ if (logpath != NULL) { dy_logchn = dyio_openfile(logpath,"w") ; if (dy_logchn == IOID_INV) { warn(201,rtnnme,logpath) ; dy_logchn = IOID_NOSTRM ; } } else { dy_logchn = IOID_NOSTRM ; } /* Are we supposed to merge the error messages with the log stream? (Note that errors will be echoed to stderr unless we're running silent. If the user's turned off logging too, well, it's their business.) swaperrs just tells the code that it should reset the error logging channel if it ever resets the main logging channel. */ if (errlogpath == NULL && dy_logchn != IOID_NOSTRM) { swaperrs = TRUE ; errlogpath = logpath ; if (dyio_chgerrlog(errlogpath,errecho) == FALSE) { warn(18,rtnnme,"<null>",errlogpath) ; } } /* Ok, after all that work, check if we've been asked for the version or usage messages. If so, do it and head for the exit. Version preempts help. */ if (doversion == TRUE) { print_version(dy_logchn,dy_gtxecho,argv[0],rtnnme,osidylp_version) ; goto NOOUTFILE_CLEANUP ; } if (dohelp == TRUE) { print_help(dy_logchn,dy_gtxecho,argv[0]) ; goto NOOUTFILE_CLEANUP ; } /* We're up! Banners to the appropriate places. */ dyio_outfmt(dy_logchn,terse,"\n\t\t %s\tV %s\n",rtnnme,osidylp_version) ; dyio_outfmt(dy_logchn,terse,"\n\t\t%s",runtime) ; dyio_outfmt(dy_logchn,terse,"\n\n") ; if (outpath != NULL && strcmp(outpath,"stdout") != 0) { outchn = dyio_pathtoid(outpath,NULL) ; if (outchn == IOID_INV) outchn = dyio_openfile(outpath,"w") ; if (outchn == IOID_INV) { warn(10,rtnnme,outpath,"w") ; } else { dyio_outfmt(outchn,FALSE,"\n\t\t %s\tV %s\n",rtnnme,osidylp_version) ; dyio_outfmt(outchn,FALSE,"\n\t\t%s",runtime) ; dyio_outfmt(outchn,FALSE,"\n\n") ; } } /* Time to set up the lp options. Establish a set of defaults, then read the options file to see what the user has in mind. Because this is a standalone shell, doing a one-time solution for an LP, set up a default of cold start using the full system and a logical basis. This can be overridden in a .spc file if desired. For reasons that escape me at the moment, the parser fails on Windows. This may get fixed eventually. For now, disabled by the simple expedient of forcing optpath to NULL. */ dy_defaults(&main_lpopts,&main_lptols) ; main_lpopts->forcecold = TRUE ; main_lpopts->fullsys = TRUE ; main_lpopts->coldbasis = ibLOGICAL ; # if defined(_MSC_VER) || defined(__MSVCRT__) optpath = NULL ; # endif if (optpath != NULL) { dy_cmdchn = dyio_openfile(optpath,"r") ; if (dy_cmdchn == IOID_INV) exit (1) ; (void) dyio_setmode(dy_cmdchn,'l') ; switch (process_cmds(silent)) { case cmdOK: { break ; } case cmdHALTERROR: { exit (1) ; } case cmdHALTNOERROR: { exit (0) ; } default: { exit (1) ; } } if (dy_cmdchn != ttyin) { (void) dyio_closefile(dy_cmdchn) ; dy_cmdchn = IOID_INV ; } } /* Make an attempt to read the mps input file. */ if (dy_mpsin(mpspath,&main_sys,main_lptols->inf) == FALSE) { exit (10) ;} /* Check over the option settings, now that we know how big the constraint system will be. */ dy_checkdefaults(main_sys,main_lpopts,main_lptols) ; /* Initialise the basis maintenance package. The second parameter controls how many basis updates the basis can hold before it requires refactoring. Adding 5 to dylp's refactor interval should give a safety margin. */ dy_initbasis(2*main_sys->concnt,main_lpopts->factor+5,0.0) ; /* Run the lp. */ if (do_lp(&lptime,printlvl) == FALSE) { errmsg(443,rtnnme,main_sys->nme,dy_prtlpphase(main_lp->phase,TRUE), main_lp->iters) ; } /* Should we produce any output? Print to a file, if requested. */ if (outchn != IOID_INV && outchn != ttyout) { dy_dumpcompact(outchn,FALSE,main_lp,FALSE) ; } /* Any final terminal output we should do? */ if (printlvl >= 1) { if (printlvl >= 2) { dy_dumpcompact(dy_logchn, (dy_logchn == IOID_INV)?TRUE:FALSE,main_lp,FALSE) ; } dyio_outfmt(dy_logchn,TRUE,"\nReturn code %s",dy_prtlpret(main_lp->lpret)) ; if (main_lp->phase == dyDONE) dyio_outfmt(dy_logchn,TRUE," after %d pivots",main_lp->iters) ; if (main_lp->lpret == lpOPTIMAL) { dyio_outfmt(dy_logchn,TRUE,"; objective %.8g",main_lp->obj) ; } dyio_outfmt(dy_logchn,TRUE, " (%.2f sec.)",lptime.tv_sec+lptime.tv_usec/1e6) ; dyio_outfmt(dy_logchn,TRUE,".\n") ; dyio_flushio(dy_logchn,dy_gtxecho) ; } /* Final cleanup. Free space used by the remaining main_* structures. */ dy_freebasis() ; if (main_lp != NULL) { dy_freesoln(main_lp) ; if (main_lp->consys != NULL) consys_free(main_lp->consys) ; FREE(main_lp) ; } if (main_lpopts != NULL) FREE(main_lpopts) ; if (main_lptols != NULL) FREE(main_lptols) ; /* Leap to here for shutdown when we opened the output file but didn't solve an LP. */ if (outchn != IOID_INV && outchn != ttyout) { (void) dyio_closefile(outchn) ; } /* Leap to here for shutdown in cases where we never get as far as opening an output file. We still need to close the log file and shut down the i/o subsystem. */ NOOUTFILE_CLEANUP: if (dy_logchn != IOID_INV && dy_logchn != IOID_NOSTRM) { (void) dyio_closefile(dy_logchn) ; } dyio_ioterm() ; errterm() ; exit(0) ; /* Just to suppress the silly warning from the compiler, which isn't satisfied with the immediately preceding `exit'. */ return (0) ; }
dyret_enum dy_warmstart (lpprob_struct *orig_lp) /* This routine is responsible for recreating the active constraint system, basis, and status specified by the user in orig_lp. It will handle even the pathological case of 0 active constraints and 0 active variables. If the user has supplied an active variable vector, only those variables will be activated. Clearly, the supplied basis, status, and active variable vector should be consistent, or bad things will happen. If we're operating in fullsys mode, we need to check here for additions to the constraint system. << In the very near future, this routine should also be upgraded to cope with the possibility that constraints specified in the warm start basis have disappeared. >> Parameters: orig_lp: The original lp problem structure Returns: dyrOK if the setup completes without error, any of a number of error codes otherwise (dyrFATAL, dyrINV, or a code from dy_factor) */ { int vndx,dyvndx,bpos,cndx,dycndx,dycsze,dyvsze,nbfxcnt ; double *vlb,*vub,vlbj,vubj,obj ; consys_struct *orig_sys ; flags *orig_status,vstat,calcflgs ; dyret_enum retval ; basisel_struct *orig_basis ; bool *orig_actvars,rngseen,noactvarspec ; pkvec_struct *pkcol ; char nmebuf[50] ; flags parts = CONSYS_OBJ|CONSYS_VUB|CONSYS_VLB|CONSYS_RHS|CONSYS_RHSLOW| CONSYS_VTYP|CONSYS_CTYP, opts = CONSYS_LVARS|CONSYS_WRNATT ; const char *rtnnme = "dy_warmstart" ; extern void dy_setfinalstatus(void) ; /* dy_hotstart.c */ # if defined(DYLP_PARANOIA) || !defined(DYLP_NDEBUG) double xi ; # endif retval = dyrINV ; nbfxcnt = -1 ; /* Do a little unpacking. */ orig_sys = orig_lp->consys ; orig_status = orig_lp->status ; orig_basis = orig_lp->basis->el ; if (flgon(orig_lp->ctlopts,lpctlACTVARSIN) && dy_opts->fullsys == FALSE) { orig_actvars = orig_lp->actvars ; noactvarspec = FALSE ; } else { orig_actvars = NULL ; noactvarspec = TRUE ; } /* Initialise the statistics on loadable/unloadable variables and constraints. */ dy_lp->sys.forcedfull = FALSE ; dy_lp->sys.vars.loadable = orig_sys->varcnt ; dy_lp->sys.vars.unloadable = 0 ; dy_lp->sys.cons.loadable = orig_sys->concnt ; dy_lp->sys.cons.unloadable = 0 ; /* Create the dy_sys constraint system to match the user's basis and active variables (if specified). We'll create the system with logicals enabled. For variables, if there is an active variable vector, skim it for a count. Otherwise, skim the status array and count the number of nonbasic fixed variables (which will never become active). For constraints, we need to consider the possibility that the user has added cuts and is trusting dylp to deal with it. If we're operating in the usual dynamic mode, this will be picked up automatically, and we can size the constraint system to the active constraints of the basis. But if we're operating in fullsys mode, we need to add them here. In this case, the number of constraints is the current size of the constraint system. Take this opportunity to clean the bounds arrays, making sure that bounds within the feasibility tolerance of one another are set to be exactly equal. (This simplifies handling fixed variables.) For nonbasic variables, force the status to NBFX and cancel activation if actvars is present. Basic variables which need BFX are picked up later, after the basis is established. */ vub = orig_sys->vub ; vlb = orig_sys->vlb ; dyio_outfxd(nmebuf,-((int) (sizeof(nmebuf)-1)), 'l',"%s[actv]",orig_sys->nme) ; if (noactvarspec == FALSE) { dyvsze = 0 ; for (vndx = 1 ; vndx <= orig_sys->varcnt ; vndx++) { vlbj = vlb[vndx] ; vubj = vub[vndx] ; if (atbnd(vlbj,vubj)) { if (vlbj != vubj) { # ifndef DYLP_NDEBUG if (dy_opts->print.setup >= 3) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n\tForcing equal bound %g for %s (%d)", (vlbj+vubj)/2,consys_nme(orig_sys,'v',vndx,0,0),vndx) ; dyio_outfmt(dy_logchn,dy_gtxecho, "\n\t original lb = %g, ub = %g, diff = %g, tol = %g", vlbj,vubj,vubj-vlbj,dy_tols->pfeas) ; } # endif vlb[vndx] = (vlbj+vubj)/2 ; vub[vndx] = vlb[vndx] ; } if (((int) orig_status[vndx]) > 0) { orig_status[vndx] = vstatNBFX ; orig_actvars[vndx] = FALSE ; } } if (vlb[vndx] > vub[vndx]) { dy_lp->lpret = lpINFEAS ; # ifndef DYLP_NDEBUG if (dy_opts->print.setup >= 1) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n\tTrivial infeasibility for %s (%d), lb = %g > ub = %g.", consys_nme(orig_sys,'v',vndx,0,0),vndx,vlb[vndx],vub[vndx]) ; } # endif } if (orig_actvars[vndx] == TRUE) dyvsze++ ; } } else { nbfxcnt = 0 ; for (vndx = 1 ; vndx <= orig_sys->varcnt ; vndx++) { vlbj = vlb[vndx] ; vubj = vub[vndx] ; if (atbnd(vlbj,vubj)) { if (vlbj != vubj) { # ifndef DYLP_NDEBUG if (dy_opts->print.setup >= 3) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n\tForcing equal bound %g for %s (g)", (vlbj+vubj)/2,consys_nme(orig_sys,'v',vndx,0,0),vndx) ; dyio_outfmt(dy_logchn,dy_gtxecho, "\n\t original lb = %g, ub = %g, diff = %g, tol = %g", vlbj,vubj,vubj-vlbj,dy_tols->pfeas) ; } # endif vlb[vndx] = (vlbj+vubj)/2 ; vub[vndx] = vlb[vndx] ; } if (((int) orig_status[vndx]) > 0) { orig_status[vndx] = vstatNBFX ; } } if (vlb[vndx] > vub[vndx]) { dy_lp->lpret = lpINFEAS ; } if ((((int) orig_status[vndx]) > 0) && flgon(orig_status[vndx],vstatNBFX)) { nbfxcnt++ ; } } dyvsze = orig_sys->varcnt-nbfxcnt ; } if (dy_opts->fullsys == TRUE) dycsze = orig_sys->concnt ; else dycsze = orig_lp->basis->len ; dyvsze += dycsze ; # ifndef DYLP_NDEBUG if (dy_opts->print.setup >= 1) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n creating constraint system %s (%d x %d+%d)", nmebuf,dycsze,dyvsze-dycsze,dycsze) ; if (dy_opts->print.setup >= 3) { if (flgoff(orig_lp->ctlopts,lpctlACTVARSIN)) dyio_outfmt(dy_logchn,dy_gtxecho, "\n %d nonbasic fixed variables excluded.", nbfxcnt) ; } } # endif dy_sys = consys_create(nmebuf,parts,opts,dycsze,dyvsze,dy_tols->inf) ; if (dy_sys == NULL) { errmsg(152,rtnnme,nmebuf) ; return (dyrFATAL) ; } /* Hang a set of translation vectors onto each system: origcons and origvars on orig_sys, and actcons and actvars on dy_sys. */ if (consys_attach(dy_sys,CONSYS_ROW, sizeof(int),(void **) &dy_actvars) == FALSE) { errmsg(100,rtnnme,dy_sys->nme,"active -> original variable map") ; return (dyrFATAL) ; } if (consys_attach(dy_sys,CONSYS_COL, sizeof(int),(void **) &dy_actcons) == FALSE) { errmsg(100,rtnnme,dy_sys->nme,"active -> original constraint map") ; return (dyrFATAL) ; } if (consys_attach(orig_sys,CONSYS_ROW, sizeof(int),(void **) &dy_origvars) == FALSE) { errmsg(100,rtnnme,orig_sys->nme,"original -> active variable map") ; return (dyrFATAL) ; } if (consys_attach(orig_sys,CONSYS_COL, sizeof(int),(void **) &dy_origcons) == FALSE) { errmsg(100,rtnnme,orig_sys->nme,"original -> active constraint map") ; return (dyrFATAL) ; } /* dy_origvars is cleared to 0 as it's attached, indicating that the original variables have no predefined status. We need to correct this. If the caller's supplied an active variable vector, we can use it to activate variables prior to adding constraints. (But in any case don't activate nonbasic fixed variables.) It's illegal to declare a formerly basic variable to be inactive by the simple expedient of setting actvars[vndx] = FALSE, hence the paranoid check. Otherwise, we'll need to depend on dy_loadcon to activate the variables referenced in the active constraints. We'll still fill in origvars, with two purposes: * We can avoid activating nonbasic fixed variables. * We can use dy_origvars == 0 as a paranoid check from here on out. Inactive variables are required to be nonbasic, so in this case the proper status for formerly basic variables is SB. */ if (noactvarspec == FALSE) { # ifndef DYLP_NDEBUG if (dy_opts->print.setup >= 1) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n processing active variable list ...") ; } # endif pkcol = pkvec_new(0) ; for (vndx = 1 ; vndx <= orig_sys->varcnt ; vndx++) { if (((int) orig_status[vndx]) > 0) vstat = orig_status[vndx] ; else vstat = vstatB ; if (orig_actvars[vndx] == TRUE && flgoff(vstat,vstatNBFX)) { if (consys_getcol_pk(orig_sys,vndx,&pkcol) == FALSE) { errmsg(122,rtnnme,orig_sys->nme,"variable", consys_nme(orig_sys,'v',vndx,TRUE,NULL),vndx) ; retval = dyrFATAL ; break ; } if (consys_addcol_pk(dy_sys,vartypCON,pkcol, orig_sys->obj[vndx],vlb[vndx],vub[vndx]) == FALSE) { errmsg(156,rtnnme,"variable",dy_sys->nme,pkcol->nme) ; retval = dyrFATAL ; break ; } dyvndx = pkcol->ndx ; dy_origvars[vndx] = dyvndx ; dy_actvars[dyvndx] = vndx ; # ifndef DYLP_NDEBUG if (dy_opts->print.setup >= 3) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n\tactivating %s variable %s (%d) to index %d.", consys_prtvartyp(orig_sys->vtyp[vndx]), consys_nme(orig_sys,'v',vndx,FALSE,NULL),vndx,dyvndx) ; } # endif } else { # ifdef DYLP_PARANOIA if (flgon(vstat,vstatBASIC)) { errmsg(380,rtnnme,orig_sys->nme, consys_nme(orig_sys,'v',vndx,FALSE,NULL),vndx, dy_prtvstat(vstat),"non-basic") ; retval = dyrFATAL ; break ; } # endif dy_origvars[vndx] = -((int) vstat) ; } } pkvec_free(pkcol) ; if (retval != dyrINV) return (retval) ; } else { for (vndx = 1 ; vndx <= orig_sys->varcnt ; vndx++) { if (((int) orig_status[vndx]) > 0) vstat = orig_status[vndx] ; else vstat = vstatSB ; MARK_INACTIVE_VAR(vndx,-((int) vstat)) ; } } /* Walk the basis and install the constraints in order. When we're finished with this, the active system will be up and about. In the case where there's no active variable specification, some of the status information written into dy_origvars may have been overwritten; only variables with vstatNBFX are guaranteed to remain inactive. */ rngseen = FALSE ; for (bpos = 1 ; bpos <= orig_lp->basis->len ; bpos++) { cndx = orig_basis[bpos].cndx ; # ifndef DYLP_NDEBUG if (dy_opts->print.setup >= 2) dyio_outfmt(dy_logchn,dy_gtxecho, "\n activating %s %s (%d) in pos'n %d", consys_prtcontyp(orig_sys->ctyp[cndx]), consys_nme(orig_sys,'c',cndx,FALSE,NULL),cndx,bpos) ; # endif # ifdef DYLP_STATISTICS if (dy_stats != NULL) dy_stats->cons.init[cndx] = TRUE ; # endif if (dy_loadcon(orig_sys,cndx,noactvarspec,NULL) == FALSE) { errmsg(430,rtnnme, dy_sys->nme,dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters, "activate","constraint", consys_nme(orig_sys,'c',cndx,TRUE,NULL),cndx) ; return (dyrFATAL) ; } if (orig_sys->ctyp[cndx] == contypRNG) rngseen = TRUE ; } /* If we're in fullsys mode, repeat constraint installation actions for any cuts added after this basis was assembled. */ if (dy_opts->fullsys == TRUE) { for (cndx = orig_lp->basis->len+1 ; cndx <= orig_sys->concnt ; cndx++) { # ifndef DYLP_NDEBUG if (dy_opts->print.setup >= 2) dyio_outfmt(dy_logchn,dy_gtxecho, "\n activating %s %s (%d) in pos'n %d", consys_prtcontyp(orig_sys->ctyp[cndx]), consys_nme(orig_sys,'c',cndx,FALSE,NULL),cndx,cndx) ; # endif # ifdef DYLP_STATISTICS if (dy_stats != NULL) dy_stats->cons.init[cndx] = TRUE ; # endif if (dy_loadcon(orig_sys,cndx,noactvarspec,NULL) == FALSE) { errmsg(430,rtnnme, dy_sys->nme,dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters, "activate","constraint", consys_nme(orig_sys,'c',cndx,TRUE,NULL),cndx) ; return (dyrFATAL) ; } if (orig_sys->ctyp[cndx] == contypRNG) rngseen = TRUE ; } } # ifdef DYLP_PARANOIA /* Paranoid checks and informational print statements. */ if (dy_chkdysys(orig_sys) == FALSE) return (dyrINV) ; # endif # ifndef DYLP_NDEBUG if (dy_opts->print.setup >= 1) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n system %s has %d constraints, %d+%d variables", dy_sys->nme,dy_sys->concnt,dy_sys->archvcnt,dy_sys->logvcnt) ; dyio_outfmt(dy_logchn,dy_gtxecho, "\n %d constraints, %d variables remain inactive in system %s.", orig_sys->concnt-dy_sys->concnt,orig_sys->archvcnt-dy_sys->archvcnt, orig_sys->nme) ; if (dy_opts->print.setup >= 4) { nbfxcnt = 0 ; for (vndx = 1 ; vndx <= orig_sys->varcnt ; vndx++) { if (INACTIVE_VAR(vndx)) { vstat = (flags) (-dy_origvars[vndx]) ; switch (getflg(vstat,vstatSTATUS)) { case vstatNBUB: { xi = orig_sys->vub[vndx] ; break ; } case vstatNBLB: case vstatNBFX: { xi = orig_sys->vlb[vndx] ; break ; } case vstatNBFR: { xi = 0 ; break ; } default: { errmsg(433,rtnnme,dy_sys->nme, dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters, "inactive",consys_nme(orig_sys,'v',vndx,TRUE,NULL), vndx,dy_prtvstat(vstat)) ; return (dyrINV) ; } } if (xi != 0) { if (nbfxcnt == 0) dyio_outfmt(dy_logchn,dy_gtxecho, "\n\tinactive variables with nonzero values:") ; nbfxcnt++ ; dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t%s (%d) = %g, status %s", consys_nme(orig_sys,'v',vndx,FALSE,NULL),vndx,xi, dy_prtvstat(vstat)) ; } } } if (nbfxcnt == 0) dyio_outfmt(dy_logchn,dy_gtxecho, "\n\tall inactive variables are zero.") ; } } # endif /* Time to assemble the basis. Attach the basis and inverse basis vectors to the constraint system. consys_attach will initialise them to 0. */ if (consys_attach(dy_sys,CONSYS_COL, sizeof(int),(void **) &dy_basis) == FALSE) { errmsg(100,rtnnme,dy_sys->nme,"basis vector") ; return (dyrFATAL) ; } if (consys_attach(dy_sys,CONSYS_ROW, sizeof(int),(void **) &dy_var2basis) == FALSE) { errmsg(100,rtnnme,dy_sys->nme,"inverse basis vector") ; return (dyrFATAL) ; } # ifndef DYLP_NDEBUG if (dy_opts->print.crash >= 1) { if (dy_opts->print.setup == 0) dyio_outfmt(dy_logchn,dy_gtxecho, "\n %s: regenerating the basis ...",rtnnme) ; else dyio_outfmt(dy_logchn,dy_gtxecho, "\n regenerating the basis.",rtnnme) ; } # endif /* Load the basis. For variables, we need to translate architecturals using dy_origvars, and watch out for logicals (vndx = negative of associated constraint index). After all the paranoia, we finally update dy_basis and dy_var2basis. Because we loaded the constraints in the order they were listed in the basis, we should have that dycndx = bpos, hence dy_actcons[bpos] = cndx. If we're installing a basic variable, it should be active already. For architectural variables, the check is made in dy_origvars. For a logical, the associated constraint should be active, hence a non-zero entry in dy_origcons. For architecturals, we also check if there are any non-zero coefficients remaining in the column (who knows what the user has done to the constraint system). This rates a message if the print level is high enough, but the basis pacakge is capable of patching the basis. (Indeed, it's hard to do it correctly here.) */ # ifdef DYLP_PARANOIA pkcol = pkvec_new(0) ; retval = dyrOK ; # endif for (bpos = 1 ; bpos <= orig_lp->basis->len ; bpos++) { cndx = orig_basis[bpos].cndx ; dycndx = dy_origcons[cndx] ; vndx = orig_basis[bpos].vndx ; if (vndx < 0) { dyvndx = dy_origcons[-vndx] ; } else { dyvndx = dy_origvars[vndx] ; } # ifdef DYLP_PARANOIA if (dycndx <= 0) { errmsg(369,rtnnme,orig_sys->nme,"constraint", consys_nme(orig_sys,'c',cndx,FALSE,NULL),cndx, "cons",cndx,dycndx) ; retval = dyrINV ; break ; } if (dy_actcons[bpos] != cndx) { errmsg(370,rtnnme,dy_sys->nme, consys_nme(orig_sys,'c',cndx,FALSE,NULL),cndx,bpos, consys_nme(orig_sys,'c',dy_actcons[bpos],FALSE,NULL), dy_actcons[bpos]) ; if (dycndx != bpos) { errmsg(1,rtnnme,__LINE__) ; } retval = dyrINV ; break ; } if (vndx < 0) { if (dyvndx <= 0) { errmsg(369,rtnnme,orig_sys->nme,"constraint", consys_nme(orig_sys,'c',-vndx,FALSE,NULL),-vndx, "cons",-vndx,dyvndx) ; retval = dyrINV ; break ; } } else { if (dyvndx <= 0) { errmsg(369,rtnnme,orig_sys->nme,"variable", consys_nme(orig_sys,'v',vndx,FALSE,NULL),vndx, "vars",vndx,dyvndx) ; retval = dyrINV ; break ; } if (consys_getcol_pk(dy_sys,dyvndx,&pkcol) == FALSE) { errmsg(122,rtnnme,orig_sys->nme,"variable", consys_nme(orig_sys,'v',vndx,TRUE,NULL),vndx) ; retval = dyrFATAL ; break ; } if (pkcol->cnt == 0 && dy_opts->print.crash >= 4) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n %s (%d) has no non-zeros in active constraints.", consys_nme(dy_sys,'v',dyvndx,TRUE,NULL),dyvndx) ; } } # endif dy_basis[dycndx] = dyvndx ; dy_var2basis[dyvndx] = dycndx ; } /* If we're in fullsys mode, make the logical basic for any remaining constraints. */ if (dy_opts->fullsys == TRUE) { for ( ; bpos <= dy_sys->concnt ; bpos++) { dy_basis[bpos] = bpos ; dy_var2basis[bpos] = bpos ; } } # ifdef DYLP_PARANOIA pkvec_free(pkcol) ; if (retval != dyrOK) return (retval) ; # endif # ifndef DYLP_NDEBUG if (dy_opts->print.crash >= 4) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n\t Pos'n Variable Constraint") ; for (bpos = 1 ; bpos <= orig_lp->basis->len ; bpos++) { vndx = dy_basis[bpos] ; dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t %3d (%3d) %-15s",bpos,vndx, consys_nme(dy_sys,'v',vndx,FALSE,NULL)) ; dyio_outfmt(dy_logchn,dy_gtxecho,"%-15s", consys_nme(dy_sys,'c',bpos,FALSE,NULL)) ; } } # endif /* Factor the basis. We don't want any of the primal or dual variables calculated just yet. If this fails we're in deep trouble. Don't do this if we're dealing with a constraint system with no constraints! */ if (dy_sys->concnt > 0) { # ifndef DYLP_NDEBUG if (dy_opts->print.crash >= 2) dyio_outfmt(dy_logchn,dy_gtxecho,"\n factoring ...") ; # endif calcflgs = 0 ; retval = dy_factor(&calcflgs) ; switch (retval) { case dyrOK: case dyrPATCHED: { break ; } default: { errmsg(309,rtnnme,dy_sys->nme) ; return (retval) ; } } } /* Attach and clear the vectors which will hold the status, values of primal and dual variables, and reduced costs. */ if (consys_attach(dy_sys,CONSYS_ROW, sizeof(flags),(void **) &dy_status) == FALSE) { errmsg(100,rtnnme,dy_sys->nme,"status vector") ; return (dyrFATAL) ; } if (consys_attach(dy_sys,CONSYS_COL, sizeof(double),(void **) &dy_xbasic) == FALSE) { errmsg(100,rtnnme,dy_sys->nme,"basic variable vector") ; return (dyrFATAL) ; } if (consys_attach(dy_sys,CONSYS_ROW, sizeof(double),(void **) &dy_x) == FALSE) { errmsg(100,rtnnme,dy_sys->nme,"primal variable vector") ; return (dyrFATAL) ; } if (consys_attach(dy_sys,CONSYS_COL, sizeof(double),(void **) &dy_y) == FALSE) { errmsg(100,rtnnme,dy_sys->nme,"dual variable vector") ; return (dyrFATAL) ; } if (consys_attach(dy_sys,CONSYS_ROW, sizeof(double),(void **) &dy_cbar) == FALSE) { errmsg(100,rtnnme,dy_sys->nme,"reduced cost vector") ; return (dyrFATAL) ; } /* Calculate dual variables and reduced costs. Might as well make a try for a dual feasible start, eh? */ # ifndef DYLP_NDEBUG if (dy_opts->print.crash >= 2) dyio_outfmt(dy_logchn,dy_gtxecho,"\n calculating dual values ...") ; # endif dy_calcduals() ; if (dy_calccbar() == FALSE) { errmsg(384,rtnnme,dy_sys->nme, dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters) ; return (dyrFATAL) ; } /* Initialise dy_status for logicals, using dy_var2basis and dy_cbar as guides. We have to consider the type of constraint so that we can give artificials NBFX status (thus avoiding the issue of whether NBLB or NBUB gives dual feasibility), and so that we can check the sign of the associated reduced cost to determine the proper bound for the logical associated with a range constraint. */ vlb = dy_sys->vlb ; vub = dy_sys->vub ; # ifndef DYLP_NDEBUG if (dy_opts->print.crash >= 2) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n establishing initial status and reference frame ...") ; dyio_outfmt(dy_logchn,dy_gtxecho,"\n logicals ...") ; } # endif for (dyvndx = 1 ; dyvndx <= dy_sys->concnt ; dyvndx++) { if (dy_var2basis[dyvndx] != 0) { if (vub[dyvndx] == vlb[dyvndx]) dy_status[dyvndx] = vstatBFX ; else dy_status[dyvndx] = vstatB ; } else { switch (dy_sys->ctyp[dyvndx]) { case contypLE: case contypGE: { dy_status[dyvndx] = vstatNBLB ; dy_x[dyvndx] = 0 ; break ; } case contypEQ: { dy_status[dyvndx] = vstatNBFX ; dy_x[dyvndx] = 0 ; break ; } case contypRNG: { if (vub[dyvndx] == vlb[dyvndx]) { dy_status[dyvndx] = vstatNBFX ; dy_x[dyvndx] = vub[dyvndx] ; } else if (dy_cbar[dyvndx] < 0) { dy_status[dyvndx] = vstatNBUB ; dy_x[dyvndx] = vub[dyvndx] ; } else { dy_status[dyvndx] = vstatNBLB ; dy_x[dyvndx] = vlb[dyvndx] ; } break ; } default: { errmsg(1,rtnnme,__LINE__) ; return (dyrFATAL) ; } } } # ifndef DYLP_NDEBUG if (dy_opts->print.crash >= 4) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t %s (%d) %s", consys_nme(dy_sys,'v',dyvndx,FALSE,NULL),dyvndx, dy_prtvstat(dy_status[dyvndx])) ; if (flgon(dy_status[dyvndx],vstatNONBASIC|vstatNBFR)) dyio_outfmt(dy_logchn,dy_gtxecho," with value %g.",dy_x[dyvndx]) ; else dyio_outchr(dy_logchn,dy_gtxecho,'.') ; } # endif } /* Scan dy_origvars, with two purposes in mind: * For active architectural variables, initialise dy_status from orig_status, using the actual status for nonbasic variables, and vstatB, vstatBFX, or vstatBFR for basic variables. (We'll tune this once we have the values of the basic variables.) Initialise dy_x to the proper value for nonbasic variables. We shouldn't see NBFX here, as those variables should have been left inactive. * For inactive architectural variables, accumulate the objective function correction. Nonbasic free variables are assumed to have value 0. */ # ifndef DYLP_NDEBUG if (dy_opts->print.crash >= 2) dyio_outfmt(dy_logchn,dy_gtxecho,"\n architecturals ...") ; # endif dy_lp->inactzcorr = 0 ; for (vndx = 1 ; vndx <= orig_sys->varcnt ; vndx++) { dyvndx = dy_origvars[vndx] ; if (dyvndx < 0) { obj = orig_sys->obj[vndx] ; switch ((flags) (-dyvndx)) { case vstatNBFX: case vstatNBLB: { dy_lp->inactzcorr += obj*orig_sys->vlb[vndx] ; break ; } case vstatNBUB: { dy_lp->inactzcorr += obj*orig_sys->vub[vndx] ; break ; } # ifdef DYLP_PARANOIA case vstatNBFR: { break ; } default: { errmsg(1,rtnnme,__LINE__) ; return (dyrINV) ; } # endif } } else { if (((int) orig_status[vndx]) < 0) { if (vlb[dyvndx] == vub[dyvndx]) dy_status[dyvndx] = vstatBFX ; else if (vlb[dyvndx] <= -dy_tols->inf && vub[dyvndx] >= dy_tols->inf) dy_status[dyvndx] = vstatBFR ; else dy_status[dyvndx] = vstatB ; } else { dy_status[dyvndx] = orig_status[vndx] ; switch (dy_status[dyvndx]) { case vstatNBLB: { dy_x[dyvndx] = vlb[dyvndx] ; break ; } case vstatNBUB: { dy_x[dyvndx] = vub[dyvndx] ; break ; } case vstatNBFR: { dy_x[dyvndx] = 0 ; break ; } # ifdef DYLP_PARANOIA default: { errmsg(1,rtnnme,__LINE__) ; return (dyrINV) ; } # endif } } # ifndef DYLP_NDEBUG if (dy_opts->print.crash >= 4) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n\t %s (%d) %s", consys_nme(dy_sys,'v',dyvndx,FALSE,NULL),dyvndx, dy_prtvstat(dy_status[dyvndx])) ; if (flgon(dy_status[dyvndx],vstatNONBASIC|vstatNBFR)) dyio_outfmt(dy_logchn,dy_gtxecho," with value %g.",dy_x[dyvndx]) ; else dyio_outchr(dy_logchn,dy_gtxecho,'.') ; } # endif } } /* Did we patch the basis? If so, we need to scan the status array and correct the entries for the architectural variables that were booted out during the patch. */ if (retval == dyrPATCHED) correct_for_patch() ; /* Ok, status is set. Now it's time to calculate initial values for the primal variables and objective. Arguably we don't need the true objective for phase I, but it's cheap to calculate. Once we have the primal variables, adjust the status for any that are pinned against a bound or out of bounds, and see how it looks, in terms of primal infeasibility. */ # ifndef DYLP_NDEBUG if (dy_opts->print.crash >= 2) dyio_outfmt(dy_logchn,dy_gtxecho,"\n calculating primal values ...") ; # endif if (dy_calcprimals() == FALSE) { errmsg(316,rtnnme,dy_sys->nme) ; return (dyrFATAL) ; } dy_lp->z = dy_calcobj() ; dy_setfinalstatus() ; /* Make the check for primal and/or dual feasibility, and set the initial simplex phase accordingly. */ calcflgs = ladPRIMFEAS|ladPFQUIET|ladDUALFEAS|ladDFQUIET ; retval = dy_accchk(&calcflgs) ; if (retval != dyrOK) { errmsg(304,rtnnme,dy_sys->nme, dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters) ; return (retval) ; } if (flgoff(calcflgs,ladPRIMFEAS)) { dy_lp->simplex.next = dyPRIMAL2 ; } else if (flgoff(calcflgs,ladDUALFEAS)) { dy_lp->simplex.next = dyDUAL ; } else { dy_lp->simplex.next = dyPRIMAL1 ; } # ifndef DYLP_NDEBUG if (dy_opts->print.crash >= 2) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n phase %s, initial objective %g", dy_prtlpphase(dy_lp->simplex.next,FALSE),dy_lp->z) ; if (dy_lp->infeascnt != 0) dyio_outfmt(dy_logchn,dy_gtxecho,", %d infeasible vars, infeas = %g", dy_lp->infeascnt,dy_lp->infeas) ; dyio_outchr(dy_logchn,dy_gtxecho,'.') ; } if (dy_opts->print.crash >= 3) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n\nPos'n\tConstraint\tDual\t\tPrimal\n") ; for (bpos = 1 ; bpos <= dy_sys->concnt; bpos++) { cndx = dy_actcons[bpos] ; dyvndx = dy_basis[bpos] ; if (dyvndx <= dy_sys->concnt) vndx = orig_sys->varcnt+dyvndx ; else vndx = dy_actvars[dyvndx] ; dyio_outfmt(dy_logchn,dy_gtxecho, "\n%5d\t(%4d) %-8s\t%12.4g\t(%4d) %-8s %12.4g", bpos,cndx, consys_nme(dy_sys,'c',bpos,FALSE,NULL),dy_y[bpos],vndx, consys_nme(dy_sys,'v',dyvndx,FALSE,NULL),dy_x[dyvndx]) ; } } # endif return (dyrOK) ; }
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) ; }
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_initbasis (int concnt, int factor, double zero_tol) /* This routine calls the glpk routine inv_create to initialize the basis data structures, then sets values for the zero tolerance (eps_tol), pivot ratio (piv_tol) and number of candidates examined (piv_lim). NOTE: This routine can be (and typically is) called before any of the main dylp data structures exist. Be careful what you reference. Parameters: concnt: the number of constraints (rows) that the basis representation should be capable of handling factor: the planned refactorisation frequency; passed to glpk as the basis inverse update capacity (i.e., the limit on the number of pivots between refactorisations) zero_tol: zero tolerance; a value of 0.0 uses the glpk default (INV->LUF->eps_tol = 1.0e-15). Returns: void */ { int sva_size ; const char *rtnnme = "dy_initbasis" ; /* Create the basis. Allow for at least five constraints (also handles pathological examples with no explicit constraints). */ luf_capacity = maxx(concnt,5) ; luf_basis = inv_create(luf_capacity,factor) ; if (luf_basis == NULL) { if (dy_lp == NULL) { errmsg(302,rtnnme,"empty","pre-init",0,"create") ; } else { errmsg(302,rtnnme,dy_sys->nme,dy_prtlpphase(dy_lp->phase,TRUE), dy_lp->tot.iters,"create") ; } return ; } /* WARNING: We're going to reach inside glpluf to get it to triple the amount of space that it allocates for the sparse vector area. We're doing this by triggering the reallocation mechanism built into luf_decomp (called by inv_decomp). */ sva_size = luf_basis->luf->sv_size ; luf_basis->luf->new_sva = 3*sva_size ; # ifndef DYLP_NDEBUG if (dy_opts != NULL && dy_opts->print.basis >= 2) { dyio_outfmt(dy_logchn,dy_gtxecho, "\ninitbasis: %s(%d) basis capacity %d, piv lim %d.", dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters, luf_basis->luf->n,luf_basis->hh_max) ; } /* XX_DEBUG_XX There's no good way to control this output, given the timing of the call (before dy_opts is initialised), but it's sometimes useful when debugging. else { dyio_outfmt(dy_logchn,TRUE, "\ninitbasis: EXTERN(0) basis capacity %d, piv lim %d.", luf_basis->luf->n,luf_basis->hh_max) ; } */ # endif /* Set the initial pivot level to {.01,4}, and allow it to drop to {.01,4}. */ pivlevel = 0 ; minpivlevel = 0 ; if (zero_tol != 0.0) luf_basis->luf->eps_tol = zero_tol ; luf_basis->luf->piv_tol = pivtols[pivlevel].stable ; luf_basis->luf->piv_lim = pivtols[pivlevel].look ; luf_basis->luf->max_gro = 1.0e7 ; /* This is the smallest value that can appear on the diagonal of U after a pivot update. dylp will (in extremis) drop its pivot selection tolerance tols.pivot to 1e-9 (or thereabouts), so upd_tol had better be less or we spend a lot of time refactoring. This should probably be adjusted as needed, in response to adjustments in tols.pivot, but I need to sit down and really think about the math. In the meantime, this seems to be adequate. */ luf_basis->upd_tol = 1.0e-10 ; return ; }
dyret_enum dy_pivot (int xipos, double abarij, double maxabarj) /* This routine handles a single pivot. It first checks that the pivot element satisfies a stability test, then calls inv_update to pivot the basis. We can still run into trouble, however, if the pivot results in a singular or near-singular basis. NOTE: There is an implicit argument here that's not immediately obvious. inv_update gets the entering column from a cached result set with the most recent call to inv_ftran(*,1) (dy_ftran(*,true), if you prefer). The underlying assumption is that this is readily available from when we ftran'd the entering column to find the leaving variable. Parameters: xipos: the basis position of the entering variable abarij: the pivot element (only the absolute value is used) maxabarj: for a primal pivot, max{i} |abar<i,j>|, for a dual pivot, max{j} |abar<i,j>| Returns: dyrOK: the pivot was accomplished without incident (inv_update) dyrMADPIV: the pivot element abar<i,j> was rejected as numerically unstable (dy_chkpiv) dyrSINGULAR: the pivot attempt resulted in a structurally singular basis (i.e., some diagonal element is zero) (inv_update) dyrNUMERIC: the pivot attempt resulted in a numerically singular (unstable) basis (i.e, some diagonal element is too small compared to other elements in the associated row and column) (inv_update) dyrBSPACE: glpinv/glpluf ran out of space for the basis representation (inv_update) dyrFATAL: internal confusion */ { int retval ; double ratio ; dyret_enum retcode ; const char *rtnnme = "dy_pivot" ; /* Check that the pivot element meets the current criterion for numerical stability. Arguably this should have been checked by the caller, but that's no excuse for not doing it now. */ ratio = dy_chkpiv(abarij,maxabarj) ; if (ratio < 1.0) { # ifndef DYLP_NDEBUG if (dy_opts->print.basis >= 3) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n %s(%d) pivot aborted; est. pivot stability %g.", dy_prtlpphase(dy_lp->phase,TRUE), dy_lp->tot.iters,rtnnme,ratio) ; } # endif return (dyrMADPIV) ; } /* Make the call to inv_update, then recode the result. */ retval = inv_update(luf_basis,xipos) ; # ifndef DYLP_NDEBUG if ((retval == 0 && dy_opts->print.basis >= 5) || (retval > 0 && dy_opts->print.basis >= 3)) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n %s(%d) estimated pivot stability %g; ", dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters,ratio) ; dyio_outfmt(dy_logchn,dy_gtxecho,"measured pivot stability %g.", luf_basis->min_vrratio) ; } # endif switch (retval) { case 0: { retcode = dyrOK ; break ; } case 1: { retcode = dyrSINGULAR ; # ifndef DYLP_NDEBUG if (dy_opts->print.basis >= 2) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n %s(%d) singular basis (structural) after pivot.", dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters) ; } # endif break ; } case 2: { retcode = dyrNUMERIC ; # ifndef DYLP_NDEBUG if (dy_opts->print.basis >= 2) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n %s(%d) singular basis (numeric) after pivot.", dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters) ; } # endif break ; } case 3: case 4: { retcode = dyrBSPACE ; # ifndef DYLP_NDEBUG if (dy_opts->print.basis >= 2) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n %s(%d) out of space (%s)", dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters, (retval == 3)?"eta matrix limit":"sparse vector area") ; } # endif break ; } default: { errmsg(1,rtnnme,__LINE__) ; retcode = dyrFATAL ; break ; } } return (retcode) ; }
dyret_enum dy_factor (flags *calcflgs) /* This routine orchestrates the LU factorisation of the basis. The glpk routines do the grunt work. This routine provides the intelligence. If inv_decomp aborts the attempt to factor due to numerical instability, we tighten the pivot selection parameters one notch and try again, giving up only when no further increase is possible. The sequence of values for the pivot selection parameters are defined in a table at the top of this file. If inv_decomp aborts the attempt to factor because the basis is singular, we correct the basis with adjust_basis and take another run at factoring. In the event that the basis is successfully patched, we have serious work to do. See the comments with adjust_therest for further information. If the user has for some reason disabled basis patching, we return dyrSINGULAR. inv_decomp (actually, luf_decomp) is self-expanding --- if more space is needed to hold the factorization, the expansion is handled internally. dylp uses ladEXPAND to force basis expansion after a pivot fails due to lack of space. In glpk, inv_update will set instructions in the basis structure and luf_decomp will handle the expansion, so ladEXPAND is redundant. No action need be taken in this routine. It's also not possible to tell if the basis has been expanded, so ladEXPAND is not set on output. Parameters: calcflgs: (i) ladPRIMALS indicates the primal variables should be recalculated after factoring the basis. ladDUALS indicates the dual variables should be recalculated after factoring the basis. ladEXPAND indicates that the basis should be expanded prior to refactoring. (o) flags are set to indicate if the corresponding variables have been recalculated. Returns: dyrOK if the basis is factored without incident dyrPATCHED if the basis was singular and has been repaired dyrSINGULAR if the basis was singular and has not been repaired dyrNUMERIC if factoring failed for the strictest pivoting regimen dyrFATAL for other fatal errors NOTE: glpinv/glpluf will crash and burn if they encounter what they consider to be a fatal error, rather than returning a fatal error code. This needs to be addressed at some point. In particular, failure to expand the basis, failure to load the basis from the constraint system, and various parameter errors fall into this category. */ { int retval,patchcnt ; bool try_again,patched ; dyret_enum retcode ; patch_struct *patches ; const char *rtnnme = "dy_factor" ; #ifdef DYLP_PARANOIA if (dy_sys == NULL) { errmsg(2,rtnnme,"dy_sys") ; return (dyrFATAL) ; } if (dy_basis == NULL) { errmsg(2,rtnnme,"basis") ; return (dyrFATAL) ; } #endif # ifdef DYLP_STATISTICS if (dy_stats != NULL) { int pivcnt ; pivcnt = dy_lp->tot.pivs-dy_stats->factor.prevpiv ; dy_stats->factor.avgpivs = dy_stats->factor.avgpivs*dy_stats->factor.cnt ; dy_stats->factor.avgpivs += pivcnt ; dy_stats->factor.cnt++ ; dy_stats->factor.avgpivs /= dy_stats->factor.cnt ; if (pivcnt > dy_stats->factor.maxpivs) dy_stats->factor.maxpivs = pivcnt ; dy_stats->factor.prevpiv = dy_lp->tot.pivs ; } # endif retcode = dyrINV ; patchcnt = 0 ; patches = NULL ; /* Call luf_adjustsize to set the actual size of the basis. If the allocated capacity is too small, it will be expanded. */ luf_adjustsize() ; /* Open a loop for factorisation attempts. We'll persist in the face of numerical stability problems as long as there's room to tighten the pivot selection. At present, glpinv/glpluf will crash and burn if they encounter fatal problems. The basis load is implicit --- the routine factor_loadcol is called from luf_decomp to load up the coefficients. */ try_again = TRUE ; patched = FALSE ; while (try_again) { retval = inv_decomp(luf_basis,dy_sys,factor_loadcol) ; # ifndef DYLP_NDEBUG if ((retval == 0 && dy_opts->print.basis >= 4) || (retval > 0 && dy_opts->print.basis >= 2)) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n (%s)%d: factored with %s, basis stability %g.", dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters, dy_prtpivparms(-1),luf_basis->min_vrratio) ; } # endif /* Deal with the result. A return code of 0 means there were no difficulties; 1 says the basis was singular and had to be patched before the factorisation could be completed. Either is success, and we're done. */ switch (retval) { case 0: { try_again = FALSE ; retcode = dyrOK ; break ; } /* Alas, the failures. If the problem is a singular basis (retval = 1), fix up the basis structures as indicated in the luf_basis structure and try again to factor the basis, unless the user has forbidden it. If the problem is numerical instability (retval = 2) try to make the pivot selection more stringent, and keep trying until we can try no more, at which point we'll return numeric instability to the caller. What's left is fatal confusion; pass the buck back to the caller. */ case 1: { if (dy_opts->patch == FALSE) { errmsg(308,rtnnme,dy_sys->nme,dy_prtlpphase(dy_lp->phase,TRUE), dy_lp->tot.iters,dy_prtdyret(dyrSINGULAR)) ; clrflg(*calcflgs,ladPRIMALS|ladDUALS) ; return (dyrSINGULAR) ; } # ifndef DYLP_NDEBUG if (dy_opts->print.basis >= 2) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n (%s)%d: attempting to patch singular basis.", dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters) ; } # endif adjust_basis(&patchcnt,&patches) ; patched = TRUE ; break ; } case 2: { retcode = dyrNUMERIC ; # ifndef DYLP_NDEBUG if (dy_opts->print.basis >= 2) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n (%s)%d: factor failed at %s, numerical instability,", dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters, dy_prtpivparms(-1)) ; dyio_outfmt(dy_logchn,dy_gtxecho," max = %g, gro = %g.", luf_basis->luf->big_v,luf_basis->luf->max_gro) ; } # endif if (dy_setpivparms(+1,0) == FALSE) { errmsg(307,rtnnme,dy_sys->nme,dy_prtlpphase(dy_lp->phase,TRUE), dy_lp->tot.iters,dy_prtpivparms(-1)) ; return (retcode) ; } # ifndef DYLP_NDEBUG if (dy_opts->print.basis >= 2) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n\ttrying again with %s.", dy_prtpivparms(-1)) ; } # endif break ; } default: { errmsg(7,rtnnme,__LINE__,"inv_decomp return code",retval) ; return (dyrFATAL) ; } } } /* If we reach here, we managed to factor the basis. Reset the count of pivots since the last refactor. If the basis was patched, we have some serious cleanup to do, so call adjust_therest to deal with the details. Otherwise, turn to the requests to calculate values for the primal and/or dual variables. */ dy_lp->basis.etas = 0 ; if (patched == TRUE) { retcode = adjust_therest(patchcnt,patches) ; FREE(patches) ; if (retcode == dyrFATAL) { errmsg(306,rtnnme,dy_sys->nme,dy_prtlpphase(dy_lp->phase,TRUE), dy_lp->tot.iters) ; return (dyrFATAL) ; } # ifndef DYLP_NDEBUG if (dy_opts->print.basis >= 1) { dyio_outfmt(dy_logchn,dy_gtxecho, "\n\t[%s]: compensated for basis correction.", dy_sys->nme) ; } # endif if (!(dy_lp->phase == dyINIT)) { setflg(*calcflgs,ladPRIMALS|ladDUALS) ; if (retcode == dyrLOSTDFEAS) setflg(*calcflgs,ladDUALFEAS) ; } retcode = dyrPATCHED ; } else { if (flgon(*calcflgs,ladPRIMALS)) { if (dy_calcprimals() == FALSE) { clrflg(*calcflgs,ladPRIMALS) ; return (dyrFATAL) ; } } if (flgon(*calcflgs,ladDUALS)) dy_calcduals() ; } return (retcode) ; }
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) ; }
bool dy_clrpivrej (int *entries) /* This routine removes variables from rejected pivot list. As far as the rest of dylp is concerned, all that needs to be done is to clear the NOPIVOT qualifier from the variable's status entry. Internally, there are really two modes: clear specified entries, and clear the entire list. If the client supplies an array of indices in the entries parameter, selective removal is performed, otherwise the entire list is cleared. Parameters: entries: an array with indices of entries to be removed entries[0] is expected to contain the number of entries Returns: TRUE if the clearing operation is successful, FALSE otherwise. */ { int n,j,ndx,last,endx,elast ; flags statj ; const char *rtnnme = "dy_clrpivrej" ; # ifdef DYLP_PARANOIA flags chkflgs ; /* For dual simplex, only out-of-bound basic variables are considered for pivoting, but subsequent dual pivots could change that to pretty much any basic status. For primal simplex, any nonbasic status is ok, including the exotic ones. */ if (dy_lp->phase == dyDUAL) { chkflgs = vstatBASIC ; } else { chkflgs = vstatNONBASIC|vstatEXOTIC ; } # endif /* Are we clearing the entire list? If so, also restore the default pivot tolerance. If we're being selective about clearing, leave the tolerance unchanged and assume the client will take care of it. If there are no entries in pivrejlst, that's all we need to do. */ if (entries == NULL) { dy_tols->pivot = pivrej_ctl.savedtol ; pivrej_ctl.iter_reduced = -1 ; } if (pivrej_ctl.cnt == 0) return (TRUE) ; # ifndef DYLP_NDEBUG if (dy_opts->print.pivreject >= 1) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n %s pivot reject list ... ", (entries == NULL)?"clearing":"winnowing") ; } # endif n = dy_sys->varcnt ; last = pivrej_ctl.cnt-1 ; /* If the client hasn't supplied entries, we're clearing the entire list. */ if (entries == NULL) { for (ndx = 0 ; ndx <= last ; ndx++) { j = pivrejlst[ndx].ndx ; statj = dy_status[j] ; # ifdef DYLP_PARANOIA if (j < 1 || j > n) { errmsg(102,rtnnme,dy_sys->nme,"rejected variable",j,1,n) ; return (FALSE) ; } if (flgoff(statj,vstatNOPIVOT) || flgoff(statj,chkflgs)) { errmsg(329,rtnnme, dy_sys->nme,dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters, consys_nme(dy_sys,'v',j,FALSE,NULL),j,ndx,dy_prtvstat(statj), (dy_lp->phase == dyDUAL)?"basic":"nonbasic") ; return (FALSE) ; } # endif # ifndef DYLP_NDEBUG if (dy_opts->print.pivreject >= 2) dyio_outfmt(dy_logchn,dy_gtxecho, "\n\trestoring %s (%d) as eligible for pivoting.", consys_nme(dy_sys,'v',j,TRUE,NULL),j) ; # endif clrflg(dy_status[j],vstatNOPIVOT) ; } last = -1 ; pivrej_ctl.mad = 0 ; pivrej_ctl.sing = 0 ; } /* The more complicated case: Remove the set of entries specified by the client. The sort is necessary so that we can compress in place, moving the last entry to replace the deleted entry. */ else { elast = entries[0] ; if (elast > 1) { qsort(&entries[1],elast,sizeof(int),int_nonincreasing) ; } for (endx = 1 ; endx <= elast ; endx++) { ndx = entries[endx] ; # ifdef DYLP_PARANOIA if (ndx < 0 || ndx >= pivrej_ctl.cnt) { errmsg(102,rtnnme,dy_sys->nme,"pivrej list index",ndx, 0,pivrej_ctl.cnt-1) ; return (FALSE) ; } # endif j = pivrejlst[ndx].ndx ; statj = dy_status[j] ; # ifdef DYLP_PARANOIA if (j < 1 || j > n) { errmsg(102,rtnnme,dy_sys->nme,"rejected variable",j,1,n) ; return (FALSE) ; } if (flgoff(statj,vstatNOPIVOT) || flgoff(statj,chkflgs)) { errmsg(329,rtnnme, dy_sys->nme,dy_prtlpphase(dy_lp->phase,TRUE),dy_lp->tot.iters, consys_nme(dy_sys,'v',j,FALSE,NULL),j,ndx,dy_prtvstat(statj), (dy_lp->phase == dyDUAL)?"basic":"nonbasic") ; return (FALSE) ; } # endif clrflg(dy_status[j],vstatNOPIVOT) ; # ifndef DYLP_NDEBUG if (dy_opts->print.pivreject >= 2) dyio_outfmt(dy_logchn,dy_gtxecho, "\n\trestoring %s (%d) as eligible for pivoting.", consys_nme(dy_sys,'v',j,TRUE,NULL),j) ; # endif if (ndx < last) { pivrejlst[ndx] = pivrejlst[last] ; switch (pivrejlst[ndx].why) { case dyrSINGULAR: { pivrej_ctl.sing-- ; break ; } case dyrMADPIV: { pivrej_ctl.mad-- ; break ; } default: { errmsg(1,rtnnme,__LINE__) ; return (FALSE) ; } } } last-- ; } } last++ ; # ifndef DYLP_NDEBUG if (dy_opts->print.pivreject >= 1) { if (dy_opts->print.pivreject >= 2) { dyio_outfmt(dy_logchn,dy_gtxecho,"\n ") ; } dyio_outfmt(dy_logchn,dy_gtxecho,"restored %d variables.", pivrej_ctl.cnt-last) ; } # endif pivrej_ctl.cnt = last ; return (TRUE) ; }