STATIC void updatePricer(lprec *lp, int rownr, int colnr, REAL *pcol, REAL *prow, int *nzprow) { REAL *vEdge = NULL, cEdge, hold, *newEdge, *w = NULL; int i, m, n, exitcol, errlevel = DETAILED; MYBOOL forceRefresh = FALSE, isDual, isDEVEX; if(!applyPricer(lp)) return; /* Make sure we have something to update */ hold = lp->edgeVector[0]; if(hold < 0) return; isDual = (MYBOOL) (hold > 0); /* Do common initializations and computations */ m = lp->rows; n = lp->sum; isDEVEX = is_piv_rule(lp, PRICER_DEVEX); exitcol = lp->var_basic[rownr]; /* Solve/copy Bw = a */ /* formWeights(lp, colnr, NULL, &w); Experimental */ formWeights(lp, colnr, pcol, &w); /* Price norms for the dual simplex - the basic columns */ if(isDual) { REAL rw; int targetcol; /* Don't need to compute cross-products with DEVEX */ if(!isDEVEX) { allocREAL(lp, &vEdge, m+1, FALSE); /* Extract the row of the inverse containing the leaving variable and then form the dot products against the other variables, i.e. "Tau" */ #if 0 /* Extract row explicitly */ bsolve(lp, rownr, vEdge, 0, 0.0); #else /* Reuse previously extracted row data */ MEMCOPY(vEdge, prow, m+1); vEdge[0] = 0; #endif lp->bfp_ftran_normal(lp, vEdge, NULL); } /* Deal with the variable entering the basis to become a new leaving candidate */ cEdge = lp->edgeVector[exitcol]; rw = w[rownr]; hold = 1 / rw; lp->edgeVector[colnr] = (hold*hold) * cEdge; /* Possibly adjust initial value in case of Devex */ if(isDEVEX && !DEVEX_ENHANCED && (lp->edgeVector[colnr] < DEVEX_MINVALUE)) lp->edgeVector[colnr] = DEVEX_MINVALUE; #ifdef Paranoia if(lp->edgeVector[colnr] <= lp->epsmachine) report(lp, errlevel, "updatePricer: Invalid dual norm %g at entering index %d - iteration %d\n", lp->edgeVector[colnr], rownr, lp->total_iter+lp->current_iter); #endif /* Then loop over all basic variables, but skip the leaving row */ for(i = 1; i <= m; i++) { if(i == rownr) continue; targetcol = lp->var_basic[i]; hold = w[i]; if(hold == 0) continue; hold /= rw; if(fabs(hold) < lp->epsmachine) continue; newEdge = &(lp->edgeVector[targetcol]); *newEdge += (hold*hold) * cEdge; if(isDEVEX) { if((*newEdge) > DEVEX_RESTARTLIMIT) { forceRefresh = TRUE; break; } } else { *newEdge -= 2*hold*vEdge[i]; #ifdef xxApplySteepestEdgeMinimum *newEdge = my_max(*newEdge, hold*hold+1); /* Kludge; use the primal lower bound */ #else if(*newEdge <= 0) { report(lp, errlevel, "updatePricer: Invalid dual norm %g at index %d - iteration %d\n", *newEdge, i, lp->total_iter+lp->current_iter); forceRefresh = TRUE; break; } #endif } } } /* Price norms for the primal simplex - the non-basic columns */ else { REAL *vTemp, *vAlpha, cAlpha; int *coltarget; allocREAL(lp, &vTemp, m+1, TRUE); allocREAL(lp, &vAlpha, n+1, TRUE); /* Check if we have strategy fallback for the primal */ if(!isDEVEX) isDEVEX = is_piv_mode(lp, PRICE_PRIMALFALLBACK); /* Initialize column target array */ coltarget = (int *) mempool_obtainVector(lp->workarrays, lp->sum+1, sizeof(*coltarget)); if(!get_colIndexA(lp, SCAN_ALLVARS+USE_NONBASICVARS, coltarget, FALSE)) { mempool_releaseVector(lp->workarrays, (char *) coltarget, FALSE); return; } /* Don't need to compute cross-products with DEVEX */ if(!isDEVEX) { vEdge = (REAL *) calloc((n + 1), sizeof(*vEdge)); /* Compute v and then N'v */ MEMCOPY(vTemp, w, m+1); bsolve(lp, -1, vTemp, NULL, lp->epsmachine*DOUBLEROUND, 0.0); vTemp[0] = 0; prod_xA(lp, coltarget, vTemp, NULL, XRESULT_FREE, lp->epsmachine, 0.0, vEdge, NULL); } /* Compute Sigma and then Alpha */ bsolve(lp, rownr, vTemp, NULL, 0*DOUBLEROUND, 0.0); vTemp[0] = 0; prod_xA(lp, coltarget, vTemp, NULL, XRESULT_FREE, lp->epsmachine, 0.0, vAlpha, NULL); mempool_releaseVector(lp->workarrays, (char *) coltarget, FALSE); /* Update the squared steepest edge norms; first store some constants */ cEdge = lp->edgeVector[colnr]; cAlpha = vAlpha[colnr]; /* Deal with the variable leaving the basis to become a new entry candidate */ hold = 1 / cAlpha; lp->edgeVector[exitcol] = (hold*hold) * cEdge; /* Possibly adjust initial value in case of Devex */ if(isDEVEX && !DEVEX_ENHANCED && (lp->edgeVector[exitcol] < DEVEX_MINVALUE)) lp->edgeVector[exitcol] = DEVEX_MINVALUE; #ifdef Paranoia if(lp->edgeVector[exitcol] <= lp->epsmachine) report(lp, errlevel, "updatePricer: Invalid primal norm %g at leaving index %d - iteration %d\n", lp->edgeVector[exitcol], exitcol, lp->total_iter+lp->current_iter); #endif /* Then loop over all non-basic variables, but skip the entering column */ for(i = 1; i <= lp->sum; i++) { if(lp->is_basic[i] || (i == colnr)) continue; hold = vAlpha[i]; if(hold == 0) continue; hold /= cAlpha; if(fabs(hold) < lp->epsmachine) continue; newEdge = &(lp->edgeVector[i]); *newEdge += (hold*hold) * cEdge; if(isDEVEX) { if((*newEdge) > DEVEX_RESTARTLIMIT) { forceRefresh = TRUE; break; } } else { *newEdge -= 2*hold*vEdge[i]; #ifdef ApplySteepestEdgeMinimum *newEdge = my_max(*newEdge, hold*hold+1); #else if(*newEdge < 0) { report(lp, errlevel, "updatePricer: Invalid primal norm %g at index %d - iteration %d\n", *newEdge, i, lp->total_iter+lp->current_iter); if(lp->spx_trace) report(lp, errlevel, "Error detail: (RelAlpha=%g, vEdge=%g, cEdge=%g)\n", hold, vEdge[i], cEdge); forceRefresh = TRUE; break; } #endif } } FREE(vAlpha); FREE(vTemp); } if(vEdge != NULL) FREE(vEdge); freeWeights(w); if(forceRefresh) restartPricer(lp, AUTOMATIC); }
STATIC void restartPricer(lprec *lp, MYBOOL isdual) { REAL *sEdge, seNorm, hold; int i, j, m; MYBOOL isDEVEX; if(!applyPricer(lp)) return; /* Store the active/current pricing type */ if(isdual == AUTOMATIC) isdual = (MYBOOL) lp->edgeVector[0]; else lp->edgeVector[0] = isdual; m = lp->rows; /* Determine strategy and check if we have strategy fallback for the primal */ isDEVEX = is_piv_rule(lp, PRICER_DEVEX); if(!isDEVEX && !isdual) isDEVEX = is_piv_mode(lp, PRICE_PRIMALFALLBACK); /* Check if we only need to do the simple DEVEX initialization */ if(isDEVEX && !DEVEX_ENHANCED) { if(isdual) { for(i = 1; i <= m; i++) lp->edgeVector[lp->var_basic[i]] = 1.0; } else { for(i = 1; i <= lp->sum; i++) if(!lp->is_basic[i]) lp->edgeVector[i] = 1.0; } return; } /* Otherwise do the full Steepest Edge norm initialization */ sEdge = (REAL *) malloc((m + 1) * sizeof(*sEdge)); if(isdual) { /* Extract the rows of the basis inverse and compute their squared norms */ for(i = 1; i <= m; i++) { bsolve(lp, i, sEdge, NULL, 0, 0.0); /* Compute the edge norm */ seNorm = 0; for(j = 1; j <= m; j++) { hold = sEdge[j]; seNorm += hold*hold; } j = lp->var_basic[i]; lp->edgeVector[j] = seNorm; } } else { /* Solve a=Bb for b over all non-basic variables and compute their squared norms */ for(i = 1; i <= lp->sum; i++) { if(lp->is_basic[i]) continue; fsolve(lp, i, sEdge, NULL, 0, 0.0, FALSE); /* Compute the edge norm */ seNorm = 1; for(j = 1; j <= m; j++) { hold = sEdge[j]; seNorm += hold*hold; } lp->edgeVector[i] = seNorm; } } free(sEdge); }
STATIC int dualloop(lprec *lp, MYBOOL feasible) { int i, ok = TRUE; LREAL theta = 0.0; REAL *drow = NULL, *prow = NULL, *pcol = NULL, prevobj, epsvalue; MYBOOL primal = FALSE, forceoutEQ = FALSE; MYBOOL minit, pivdynamic, bfpfinal = FALSE; int oldpivrule, oldpivmode, pivrule, Blandswitches, colnr, rownr, lastnr, minitcount = 0; int Rcycle = 0, Ccycle = 0, Ncycle = 0, changedphase = TRUE; #ifdef FixInaccurateDualMinit int minitcolnr = 0; #endif int *nzprow = NULL, *workINT = NULL; if(lp->spx_trace) report(lp, DETAILED, "Entering dual simplex algorithm\n"); /* Set Extrad value to force dual feasibility; reset when "local optimality" has been achieved or a dual non-feasibility has been encountered (no candidate for a first leaving variable) */ if(feasible) lp->Extrad = 0; else lp->Extrad = feasibilityOffset(lp, (MYBOOL)!primal); if(lp->spx_trace) report(lp, DETAILED, "Extrad = %g\n", (double)lp->Extrad); /* Allocate work arrays */ allocREAL(lp, &drow, lp->sum + 1, TRUE); #ifdef UseSparseReducedCost allocINT(lp, &nzprow, lp->sum + 1, FALSE); #endif allocREAL(lp, &prow, lp->sum + 1, TRUE); allocREAL(lp, &pcol, lp->rows + 1, TRUE); /* Refactorize the basis and set status variables */ i = my_if(is_bb_action(lp, ACTION_REBASE), INITSOL_SHIFTZERO, INITSOL_USEZERO); if(((lp->spx_status == SWITCH_TO_DUAL) && !lp->justInverted) || (lp->Extrad != 0) || is_bb_action(lp, ACTION_REINVERT)) { simplexPricer(lp, (MYBOOL)!primal); /* Do basis crashing before refactorization, if specified */ invert(lp, (MYBOOL) i, TRUE); } else { if(is_bb_action(lp, ACTION_RECOMPUTE)) recompute_solution(lp, (MYBOOL) i); restartPricer(lp, (MYBOOL)!primal); } lp->bb_action = ACTION_NONE; lp->spx_status = RUNNING; lp->doIterate = FALSE; minit = ITERATE_MAJORMAJOR; prevobj = lp->rhs[0]; oldpivmode = lp->piv_strategy; oldpivrule = get_piv_rule(lp); pivdynamic = ANTICYCLEBLAND && is_piv_mode(lp, PRICE_ADAPTIVE); epsvalue = lp->epspivot; Blandswitches = 0; rownr = 0; colnr = -1; /* Used to detect infeasibility at the beginning of the dual loop */ lastnr = 0; lp->rejectpivot[0] = 0; if(feasible) lp->simplex_mode = SIMPLEX_Phase2_DUAL; else lp->simplex_mode = SIMPLEX_Phase1_DUAL; /* Check if we have equality slacks in the basis and we should try to drive them out in order to reduce chance of degeneracy in Phase 1 */ if(!feasible && (lp->fixedvars > 0) && is_anti_degen(lp, ANTIDEGEN_FIXEDVARS)) { forceoutEQ = AUTOMATIC; } while(lp->spx_status == RUNNING) { if(lp->spx_trace) if(lastnr > 0) report(lp, NORMAL, "dualloop: Objective at iteration %8d is " RESULTVALUEMASK " (%4d: %4d %s- %4d)\n", get_total_iter(lp), lp->rhs[0], rownr, lastnr, my_if(minit == ITERATE_MAJORMAJOR, "<","|"), colnr); pivrule = get_piv_rule(lp); if(pivdynamic && ((pivrule != PRICER_FIRSTINDEX) || (pivrule != oldpivrule)) #if DualPivotStickiness==2 /* Stays with pricing rule as long as possible (also preserves accuracy) */ && (lp->fixedvars == 0) #elif DualPivotStickiness==1 /* Stays with pricing rule only if the model is infeasible */ && feasible #endif ) { /* Check if we have a stationary solution */ if((minit == ITERATE_MAJORMAJOR) && !lp->justInverted && (fabs(my_reldiff(lp->rhs[0], prevobj)) < epsvalue)) { Ncycle++; /* Start to monitor for variable cycling if this is the initial stationarity */ if(Ncycle <= 1) { Ccycle = colnr; Rcycle = rownr; } /* Check if we should change pivoting strategy due to stationary variable cycling */ else if((pivrule == oldpivrule) && (((MAX_STALLCOUNT > 1) && (Ncycle > MAX_STALLCOUNT)) || (Ccycle == rownr) || (Rcycle == colnr))) { /* First check if we should give up on Bland's rule and try perturbed bound relaxation instead */ #ifdef EnableStallAntiDegen if((MAX_BLANDSWITCH >= 0) && (Blandswitches >= MAX_BLANDSWITCH)) { lp->spx_status = DEGENERATE; break; } #endif Blandswitches++; lp->piv_strategy = PRICER_FIRSTINDEX; /* There is no advanced normalization for Bland's rule, restart at end */ Ccycle = 0; Rcycle = 0; Ncycle = 0; if(lp->spx_trace) report(lp, DETAILED, "dualloop: Detected cycling at iteration %d; changed to FIRST INDEX rule!\n", get_total_iter(lp)); } } /* Handle cycling or stationary situations by switching to the primal simplex */ else if((pivrule == oldpivrule) && feasible && (lp->simplex_strategy & SIMPLEX_DYNAMIC)) { lp->spx_status = SWITCH_TO_PRIMAL; break; } /* Change back to original selection strategy as soon as possible */ else if((minit == ITERATE_MAJORMAJOR) && (pivrule != oldpivrule)) { lp->piv_strategy = oldpivmode; restartPricer(lp, AUTOMATIC); /* Pricer restart following Bland's rule */ Ccycle = 0; Rcycle = 0; Ncycle = 0; if(lp->spx_trace) report(lp, DETAILED, "...returned to original pivot selection rule at iteration %d.\n", get_total_iter(lp)); } } /* Store current LP value for reference at next iteration */ changedphase = FALSE; prevobj = lp->rhs[0]; lastnr = lp->var_basic[rownr]; lp->doInvert = FALSE; /* Do minor iterations (non-basic variable bound switches) for as long as possible since this is a cheap way of iterating */ #ifdef Phase1DualPriceEqualities RetryRow: #endif if(minit != ITERATE_MINORRETRY) { /* forceoutEQ FALSE : Only eliminate assured "good" violated equality constraint slacks AUTOMATIC: Seek more elimination of equality constraint slacks (but not as aggressive as the rule used in lp_solve v4.0 and earlier) TRUE: Force remaining equality slacks out of the basis */ i = 0; do { if(partial_countBlocks(lp, (MYBOOL) !primal) > 1) partial_blockStep(lp, (MYBOOL) !primal); rownr = rowdual(lp, forceoutEQ); i++; } while ((rownr == 0) && (i < partial_countBlocks(lp, (MYBOOL) !primal))); lastnr = lp->var_basic[rownr]; } if(rownr > 0) { #ifdef UseRejectionList RetryCol: #endif lp->doIterate = FALSE; colnr = coldual(lp, rownr, (MYBOOL)(minit == ITERATE_MINORRETRY), prow, nzprow, drow, NULL); if(colnr > 0) { lp->doIterate = TRUE; fsolve(lp, colnr, pcol, workINT, lp->epsmachine, 1.0, TRUE); #ifdef FixInaccurateDualMinit /* Prevent bound flip-flops during minor iterations; used to detect infeasibility after triggering of minor iteration accuracy management */ if(colnr != minitcolnr) minitcolnr = 0; #endif /* Getting division by zero here; catch it and try to recover */ if(pcol[rownr] == 0) { if(lp->spx_trace) report(lp, DETAILED, "dualloop: Attempt to divide by zero (pcol[%d])\n", rownr); lp->doIterate = FALSE; if(!lp->justInverted) { report(lp, DETAILED, "...trying to recover by reinverting!\n"); lp->doInvert = TRUE; bfpfinal = FALSE; } #ifdef UseRejectionList else if(lp->rejectpivot[0] < DEF_MAXPIVOTRETRY) { lp->rejectpivot[0]++; lp->rejectpivot[lp->rejectpivot[0]] = colnr; if(lp->bb_totalnodes == 0) report(lp, DETAILED, "...trying to recover via another pivot column!\n"); goto RetryCol; } #endif else { if(lp->bb_totalnodes == 0) report(lp, DETAILED, "...cannot recover by reinverting.\n"); lp->spx_status = NUMFAILURE; ok = FALSE; } } else { lp->rejectpivot[0] = 0; theta = lp->bfp_prepareupdate(lp, rownr, colnr, pcol); /* Verify numeric accuracy of the inverse and change to the "theoretically" correct version of the theta */ if((lp->improve & IMPROVE_INVERSE) && (my_reldiff(fabs(theta),fabs(prow[colnr])) > lp->epspivot*10.0*log(2.0+50.0*lp->rows))) { lp->doInvert = TRUE; bfpfinal = TRUE; #ifdef IncreasePivotOnReducedAccuracy if(lp->bfp_pivotcount(lp) < 2*DEF_MAXPIVOTRETRY) lp->epspivot *= 2.0; #endif report(lp, DETAILED, "dualloop: Refactorizing at iteration %d due to loss of accuracy.\n", get_total_iter(lp)); } theta = prow[colnr]; compute_theta(lp, rownr, &theta, !lp->is_lower[colnr], 0, primal); } } #ifdef FixInaccurateDualMinit /* Reinvert and try another row if we did not find a bound-violated leaving column */ else if((minit != ITERATE_MAJORMAJOR) && (colnr != minitcolnr)) { minitcolnr = colnr; lp->doInvert = TRUE; i = invert(lp, INITSOL_USEZERO, TRUE); if((lp->spx_status == USERABORT) || (lp->spx_status == TIMEOUT)) break; else if(!i) { lp->spx_status = SINGULAR_BASIS; break; } minit = ITERATE_MAJORMAJOR; continue; } #endif else { if(lp->justInverted && (lp->simplex_mode == SIMPLEX_Phase2_DUAL)) lp->spx_status = LOSTFEAS; #if 1 else if(!lp->justInverted && (lp->bb_level <= 1)) { #else else if(!lp->justInverted) { #endif lp->doIterate = FALSE; lp->doInvert = TRUE; bfpfinal = TRUE; } else { if((lp->spx_trace && (lp->bb_totalnodes == 0)) || (lp->bb_trace && (lp->bb_totalnodes > 0))) report(lp, DETAILED, "Model lost dual feasibility.\n"); lp->spx_status = INFEASIBLE; ok = FALSE; break; } } } else { /* New code to solve to optimality using the dual, but only if the user has specified a preference for the dual simplex - KE added 20030731 */ lp->doIterate = FALSE; bfpfinal = TRUE; if((lp->Extrad != 0) && (colnr < 0) && !isPrimalFeasible(lp, lp->epsprimal)) { if(feasible) { if(lp->bb_totalnodes == 0) report(lp, DETAILED, "Model is dual infeasible and primal feasible\n"); lp->spx_status = SWITCH_TO_PRIMAL; lp->doInvert = (MYBOOL) (lp->Extrad != 0); lp->Extrad = 0; } else { if(lp->bb_totalnodes == 0) report(lp, NORMAL, "Model is primal and dual infeasible\n"); lp->spx_status = INFEASIBLE; ok = FALSE; } break; } else if(lp->Extrad == 0) { /* We are feasible (and possibly also optimal) */ feasible = TRUE; lp->simplex_mode = SIMPLEX_Phase2_DUAL; /* Check if we still have equality slacks stuck in the basis; drive them out? */ if((lp->fixedvars > 0) && lp->bb_totalnodes == 0) #ifdef Paranoia report(lp, NORMAL, #else report(lp, DETAILED, #endif "Found dual solution with %d fixed slack variables left basic\n", lp->fixedvars); #ifdef Phase1DualPriceEqualities if(forceoutEQ != TRUE) { forceoutEQ = TRUE; goto RetryRow; } colnr = 0; #else #if 1 /* Problem: Check if we are dual degenerate and need to switch to the primal simplex (there is a flaw in the dual simplex code) */ colnr = colprim(lp, FALSE, drow, nzprow); #else colnr = 0; #endif #endif if(colnr == 0) lp->spx_status = OPTIMAL; else { lp->spx_status = SWITCH_TO_PRIMAL; if(lp->total_iter == 0) report(lp, NORMAL, "Use primal simplex for finalization at iteration %8d\n", get_total_iter(lp)); } if((lp->total_iter == 0) && (lp->spx_status == OPTIMAL)) report(lp, NORMAL, "Optimal solution with dual simplex at iteration %8d\n", get_total_iter(lp)); break; } else {
STATIC MYBOOL restartPricer(lprec *lp, MYBOOL isdual) { REAL *sEdge = NULL, seNorm, hold; int i, j, m; MYBOOL isDEVEX, ok = applyPricer(lp); /* Correction from V6, apparently, via Kjell Eikland and the ** lpSolve mailing list 2014-06-18 2:57 p.m. */ if (ok && (lp->edgeVector[0] < 0) && (isdual == AUTOMATIC)) ok = FALSE; if(!ok) return( ok ); /* Store the active/current pricing type */ if(isdual == AUTOMATIC) isdual = (MYBOOL) lp->edgeVector[0]; else lp->edgeVector[0] = isdual; m = lp->rows; /* Determine strategy and check if we have strategy fallback for the primal */ isDEVEX = is_piv_rule(lp, PRICER_DEVEX); if(!isDEVEX && !isdual) isDEVEX = is_piv_mode(lp, PRICE_PRIMALFALLBACK); /* Check if we only need to do the simple DEVEX initialization */ if(!is_piv_mode(lp, PRICE_TRUENORMINIT)) { if(isdual) { for(i = 1; i <= m; i++) lp->edgeVector[lp->var_basic[i]] = 1.0; } else { for(i = 1; i <= lp->sum; i++) if(!lp->is_basic[i]) lp->edgeVector[i] = 1.0; } return( ok ); } /* Otherwise do the full Steepest Edge norm initialization */ ok = allocREAL(lp, &sEdge, m+1, FALSE); if(!ok) return( ok ); if(isdual) { /* Extract the rows of the basis inverse and compute their squared norms */ for(i = 1; i <= m; i++) { bsolve(lp, i, sEdge, NULL, 0, 0.0); /* Compute the edge norm */ seNorm = 0; for(j = 1; j <= m; j++) { hold = sEdge[j]; seNorm += hold*hold; } j = lp->var_basic[i]; lp->edgeVector[j] = seNorm; } } else { /* Solve a=Bb for b over all non-basic variables and compute their squared norms */ for(i = 1; i <= lp->sum; i++) { if(lp->is_basic[i]) continue; fsolve(lp, i, sEdge, NULL, 0, 0.0, FALSE); /* Compute the edge norm */ seNorm = 1; for(j = 1; j <= m; j++) { hold = sEdge[j]; seNorm += hold*hold; } lp->edgeVector[i] = seNorm; } } FREE(sEdge); return( ok ); }
STATIC int primloop(lprec *lp, MYBOOL feasible) { int i, j, k, ok = TRUE; LREAL theta = 0.0; REAL *prow = NULL, *drow = NULL, *pcol = NULL, prevobj, epsvalue; MYBOOL primal = TRUE, minit; MYBOOL pivdynamic, bfpfinal = FALSE; int oldpivrule, oldpivmode, pivrule, Blandswitches, colnr, rownr, lastnr, minitcount = 0; int Rcycle = 0, Ccycle = 0, Ncycle = 0, changedphase = FALSE; int *nzdrow = NULL, *workINT = NULL; /* Add sufficent number of artificial variables to make the problem feasible through the first phase; delete when primal feasibility has been achieved */ lp->Extrap = 0; #ifdef EnablePrimalPhase1 if(!feasible) { #ifdef Paranoia if(!verifyBasis(lp)) report(lp, SEVERE, "primloop: No valid basis for artificial variables\n"); #endif #if 0 /* First check if we can get away with a single artificial variable */ if(lp->equalities == 0) { i = (int) feasibilityOffset(lp, !primal); add_artificial(lp, i); } else #endif /* Otherwise add as many as is necessary to force basic feasibility */ for(i = 1; i <= lp->rows; i++) add_artificial(lp, i); } if(lp->spx_trace) report(lp, DETAILED, "Extrap count = %d\n", lp->Extrap); #endif if(lp->spx_trace) report(lp, DETAILED, "Entered primal simplex algorithm with feasibility %s\n", my_boolstr(feasible)); /* Create work arrays */ #ifdef UseSparseReducedCost allocINT(lp, &nzdrow, lp->sum + 1, FALSE); #endif allocREAL(lp, &prow, lp->sum + 1, TRUE); allocREAL(lp, &drow, lp->sum + 1, TRUE); allocREAL(lp, &pcol, lp->rows + 1, TRUE); /* Refactorize the basis and set status variables */ i = my_if(is_bb_action(lp, ACTION_REBASE), INITSOL_SHIFTZERO, INITSOL_USEZERO); if(((lp->spx_status == SWITCH_TO_PRIMAL) && !lp->justInverted) || (lp->Extrap != 0) || is_bb_action(lp, ACTION_REINVERT)) { simplexPricer(lp, (MYBOOL)!primal); /* Do basis crashing before refactorization, if specified */ invert(lp, (MYBOOL) i, TRUE); } else { if(is_bb_action(lp, ACTION_RECOMPUTE)) recompute_solution(lp, (MYBOOL) i); restartPricer(lp, (MYBOOL)!primal); } lp->bb_action = ACTION_NONE; lp->spx_status = RUNNING; lp->doIterate = FALSE; minit = ITERATE_MAJORMAJOR; prevobj = lp->rhs[0]; oldpivmode = lp->piv_strategy; oldpivrule = get_piv_rule(lp); pivdynamic = ANTICYCLEBLAND && is_piv_mode(lp, PRICE_ADAPTIVE); epsvalue = lp->epspivot; Blandswitches = 0; rownr = 0; colnr = 0; lastnr = 0; lp->rejectpivot[0] = 0; if(feasible) lp->simplex_mode = SIMPLEX_Phase2_PRIMAL; else lp->simplex_mode = SIMPLEX_Phase1_PRIMAL; /* Iterate while we are successful; exit when the model is infeasible/unbounded, or we must terminate due to numeric instability or user-determined reasons */ while(lp->spx_status == RUNNING) { if(lp->spx_trace) if(lastnr > 0) report(lp, NORMAL, "primloop: Objective at iteration %8d is " RESULTVALUEMASK " (%4d: %4d %s- %4d)\n", get_total_iter(lp), lp->rhs[0], rownr, lastnr, my_if(minit == ITERATE_MAJORMAJOR, "<","|"), colnr); pivrule = get_piv_rule(lp); if(pivdynamic && ((pivrule != PRICER_FIRSTINDEX) || (pivrule != oldpivrule)) #if PrimalPivotStickiness==2 && (lp->fixedvars == 0) #elif PrimalPivotStickiness==1 && feasible #endif ) { /* Check if we have a stationary solution */ if((minit == ITERATE_MAJORMAJOR) && !lp->justInverted && (fabs(my_reldiff(lp->rhs[0], prevobj)) < epsvalue)) { Ncycle++; /* Start to monitor for variable cycling if this is the initial stationarity */ if(Ncycle <= 1) { Ccycle = colnr; Rcycle = rownr; } /* Check if we should change pivoting strategy due to stationary variable cycling */ else if((pivrule == oldpivrule) && (((MAX_STALLCOUNT > 1) && (Ncycle > MAX_STALLCOUNT)) || (Ccycle == rownr) || (Rcycle == colnr))) { /* First check if we should give up on Bland's rule and try perturbed bound relaxation instead */ #ifdef EnableStallAntiDegen if((MAX_BLANDSWITCH >= 0) && (Blandswitches >= MAX_BLANDSWITCH)) { lp->spx_status = DEGENERATE; break; } #endif Blandswitches++; lp->piv_strategy = PRICER_FIRSTINDEX; /* There is no advanced normalization for Bland's rule, restart at end */ Ccycle = 0; Rcycle = 0; Ncycle = 0; if(lp->spx_trace) report(lp, DETAILED, "primloop: Detected cycling at iteration %d; changed to FIRST INDEX rule!\n", get_total_iter(lp)); } } #if 0 /* Handle cycling or stationary situations by switching to the dual simplex */ else if((pivrule == oldpivrule) && feasible && (lp->simplex_strategy & SIMPLEX_DYNAMIC)) { lp->spx_status = SWITCH_TO_DUAL; if(lp->total_iter == 0) report(lp, NORMAL, "Start dual simplex for finalization at iteration %8d\n", get_total_iter(lp)); break; } #endif /* Change back to original selection strategy as soon as possible */ else if((minit == ITERATE_MAJORMAJOR) && (pivrule != oldpivrule)) { lp->piv_strategy = oldpivmode; restartPricer(lp, AUTOMATIC); /* Pricer restart following Bland's rule */ Ccycle = 0; Rcycle = 0; Ncycle = 0; if(lp->spx_trace) report(lp, DETAILED, "...returned to original pivot selection rule at iteration %d.\n", get_total_iter(lp)); } } /* Store current LP value for reference at next iteration */ prevobj = lp->rhs[0]; lp->doIterate = FALSE; lp->doInvert = FALSE; /* Find best column to enter the basis */ RetryCol: if(!changedphase) { i = 0; do { if(partial_countBlocks(lp, (MYBOOL) !primal) > 1) partial_blockStep(lp, (MYBOOL) !primal); colnr = colprim(lp, (MYBOOL) (minit == ITERATE_MINORRETRY), drow, nzdrow); i++; } while ((colnr == 0) && (i < partial_countBlocks(lp, (MYBOOL) !primal))); #ifdef FinalOptimalErrorLimitation /* Do additional checking that we have a correct identification of optimality */ if((colnr == 0) && !lp->justInverted) { lp->doInvert = TRUE; i = invert(lp, INITSOL_USEZERO, TRUE); colnr = colprim(lp, FALSE, drow, nzdrow); } #endif } if(colnr > 0) { changedphase = FALSE; fsolve(lp, colnr, pcol, workINT, lp->epsmachine, 1.0, TRUE); /* Solve entering column for Pi */ #ifdef UseRejectionList if(is_anti_degen(lp, ANTIDEGEN_COLUMNCHECK) && !check_degeneracy(lp, pcol, NULL)) { if(lp->rejectpivot[0] < DEF_MAXPIVOTRETRY/3) { i = ++lp->rejectpivot[0]; lp->rejectpivot[i] = colnr; report(lp, DETAILED, "Entering column %d found to be non-improving due to degeneracy!\n", colnr); goto RetryCol; } else { lp->rejectpivot[0] = 0; report(lp, DETAILED, "Gave up trying to find a strictly improving entering column!\n"); } } #endif /* Find the leaving variable that gives the most stringent bound on the entering variable */ theta = drow[colnr]; rownr = rowprim(lp, colnr, &theta, pcol); #if 0 report(lp, NORMAL, "Iteration %d: Enter %d, Leave %d\n", lp->current_iter, colnr, rownr); #endif /* See if we can do a straight artificial<->slack replacement (when "colnr" is a slack) */ if((lp->Extrap != 0) && (rownr == 0) && (colnr <= lp->rows)) rownr = findAnti_artificial(lp, colnr); if(rownr > 0) { lp->rejectpivot[0] = 0; lp->bfp_prepareupdate(lp, rownr, colnr, pcol); } else if(lp->spx_status == UNBOUNDED) { report(lp, DETAILED, "primloop: The model is primal unbounded.\n"); break; } #ifdef UseRejectionList else if(lp->rejectpivot[0] < DEF_MAXPIVOTRETRY) { lp->spx_status = RUNNING; if(lp->justInverted) { lp->rejectpivot[0]++; lp->rejectpivot[lp->rejectpivot[0]] = colnr; report(lp, DETAILED, "...trying to recover via another pivot column!\n"); } else { lp->doInvert = TRUE; invert(lp, INITSOL_USEZERO, TRUE); } goto RetryCol; } #endif else { /* Assume failure if we are still unsuccessful and the model is not unbounded */ if((rownr == 0) && (lp->spx_status == RUNNING)) { report(lp, IMPORTANT, "primloop: Could not find a leaving variable for entering %d (iteration %d)\n", colnr, get_total_iter(lp)); lp->spx_status = NUMFAILURE; } } } #ifdef EnablePrimalPhase1 else if(!feasible || isPhase1(lp)) { if(feasiblePhase1(lp, epsvalue)) { lp->spx_status = RUNNING; if(lp->bb_totalnodes == 0) { report(lp, NORMAL, "Found feasibility by primal simplex at iteration %8d\n", get_total_iter(lp)); if((lp->usermessage != NULL) && (lp->msgmask & MSG_LPFEASIBLE)) lp->usermessage(lp, lp->msghandle, MSG_LPFEASIBLE); } changedphase = FALSE; feasible = TRUE; lp->simplex_mode = SIMPLEX_Phase2_PRIMAL; /* We can do two things now; 1) delete the rows belonging to those variables, since they are redundant, OR 2) drive out the existing artificial variables via pivoting. */ if(lp->Extrap > 0) { #ifdef Phase1EliminateRedundant /* If it is not a MIP model we can try to delete redundant rows */ if((lp->bb_totalnodes == 0) && (MIP_count(lp) == 0)) { while(lp->Extrap > 0) { i = lp->rows; while((i > 0) && (lp->var_basic[i] <= lp->sum-lp->Extrap)) i--; #ifdef Paranoia if(i <= 0) { report(lp, SEVERE, "primloop: Could not find redundant artificial.\n"); break; } #endif /* Obtain column and row indeces */ j = lp->var_basic[i]-lp->rows; k = get_artificialRow(lp, j); /* Delete row before column due to basis "compensation logic" */ if(lp->is_basic[k]) { lp->is_basic[lp->rows+j] = FALSE; del_constraint(lp, k); } else setBasisVar(lp, i, k); del_column(lp, j); lp->Extrap--; } lp->basis_valid = TRUE; } /* Otherwise we drive out the artificials by elimination pivoting */ else { eliminate_artificials(lp, prow); lp->doIterate = FALSE; } #else lp->Extrap = my_flipsign(lp->Extrap); #endif } lp->doInvert = TRUE; prevobj = lp->infinite; } else { lp->spx_status = INFEASIBLE; minit = ITERATE_MAJORMAJOR; if(lp->spx_trace) report(lp, NORMAL, "Model infeasible by primal simplex at iteration %8d\n", get_total_iter(lp)); } } #endif /* Pivot row/col and update the inverse */ if(lp->doIterate) { lastnr = lp->var_basic[rownr]; if(lp->justInverted) minitcount = 0; else if(minitcount > MAX_MINITUPDATES) { recompute_solution(lp, INITSOL_USEZERO); minitcount = 0; } minit = performiteration(lp, rownr, colnr, theta, primal, NULL, NULL, pcol, NULL); if(minit != ITERATE_MAJORMAJOR) minitcount++; if((lp->spx_status == USERABORT) || (lp->spx_status == TIMEOUT)) break; else if(minit == ITERATE_MINORMAJOR) continue; #ifdef UsePrimalReducedCostUpdate /* Do a fast update of the reduced costs in preparation for the next iteration */ if(minit == ITERATE_MAJORMAJOR) update_reducedcosts(lp, primal, lastnr, colnr, pcol, drow); #endif #ifdef EnablePrimalPhase1 /* Detect if an auxiliary variable has left the basis and delete it; if the non-basic variable only changed bound (a "minor iteration"), the basic artificial variable did not leave and there is nothing to do */ if((minit == ITERATE_MAJORMAJOR) && (lastnr > lp->sum - abs(lp->Extrap))) { #ifdef Paranoia if(lp->is_basic[lastnr] || !lp->is_basic[colnr]) report(lp, SEVERE, "primloop: Invalid basis indicator for variable %d at iteration %d\n", lastnr, get_total_iter(lp)); #endif del_column(lp, lastnr-lp->rows); if(lp->Extrap > 0) lp->Extrap--; else lp->Extrap++; if(lp->Extrap == 0) { colnr = 0; prevobj = lp->infinite; changedphase = TRUE; } } #endif } if(lp->spx_status == SWITCH_TO_DUAL) ; else if(!changedphase && lp->bfp_mustrefactorize(lp)) { i = invert(lp, INITSOL_USEZERO, FALSE); #ifdef ResetMinitOnReinvert minit = ITERATE_MAJORMAJOR; #endif if((lp->spx_status == USERABORT) || (lp->spx_status == TIMEOUT)) break; else if(!i) { lp->spx_status = SINGULAR_BASIS; break; } /* Check whether we are still feasible or not... */ if(!isPrimalFeasible(lp, lp->epspivot)) { lp->spx_status = LOSTFEAS; } } userabort(lp, -1); } if (lp->piv_strategy != oldpivmode) lp->piv_strategy = oldpivmode; #ifdef EnablePrimalPhase1 /* Remove any remaining artificial variables (feasible or infeasible model) */ lp->Extrap = abs(lp->Extrap); if(lp->Extrap > 0) { clear_artificials(lp); if(lp->spx_status != OPTIMAL) restore_basis(lp); i = invert(lp, INITSOL_USEZERO, TRUE); } #ifdef Paranoia if(!verifyBasis(lp)) report(lp, SEVERE, "primloop: Invalid basis detected due to internal error\n"); #endif /* Switch to dual phase 1 simplex for MIP models during B&B phases */ if((lp->bb_totalnodes == 0) && (MIP_count(lp) > 0) && ((lp->simplex_strategy & SIMPLEX_Phase1_DUAL) == 0)) { lp->simplex_strategy &= !SIMPLEX_Phase1_PRIMAL; lp->simplex_strategy += SIMPLEX_Phase1_DUAL; } #endif FREE(nzdrow); FREE(drow); FREE(prow); FREE(pcol); return(ok); } /* primloop */