STATIC MYBOOL scaleCR(lprec *lp, REAL *scaledelta) { REAL *scalechange = NULL; int Result; if(!lp->scaling_used) { allocREAL(lp, &lp->scalars, lp->sum_alloc + 1, FALSE); for(Result = 0; Result <= lp->sum; Result++) lp->scalars[Result] = 1; lp->scaling_used = TRUE; } if(scaledelta == NULL) allocREAL(lp, &scalechange, lp->sum + 1, FALSE); else scalechange = scaledelta; Result=CurtisReidScales(lp, FALSE, scalechange, &scalechange[lp->rows]); if(Result>0) { /* Do the scaling*/ if(scale_updaterows(lp, scalechange, TRUE) || scale_updatecolumns(lp, &scalechange[lp->rows], TRUE)) lp->scalemode |= SCALE_CURTISREID; set_action(&lp->spx_action, ACTION_REBASE | ACTION_REINVERT | ACTION_RECOMPUTE); } if(scaledelta == NULL) FREE(scalechange); return((MYBOOL) (Result > 0)); }
STATIC void formWeights(lprec *lp, int colnr, REAL *pcol, REAL **w) /* This computes Bw = a, where B is the basis and a is a column of A */ { allocREAL(lp, w, lp->rows+1, FALSE); if(pcol == NULL) fsolve(lp, colnr, *w, NULL, 0.0, 0.0, FALSE); else { MEMCOPY(*w, pcol, lp->rows+1); /* *w[0] = 0; */ /* Test */ } /* if(pcol != NULL) { REAL cEdge, hold; int i; cEdge = 0; for(i = 1; i <= m; i++) { hold = *w[i]-pcol[i]; cEdge += hold*hold; } cEdge /= m; cEdge = sqrt(cEdge); if(cEdge > lp->epspivot) report(lp, SEVERE, "updatePricer: MRS error is %g\n", cEdge); } */ }
STATIC int make_SOSchain(lprec *lp, MYBOOL forceresort) { int i, j, k, n; MYBOOL *hold = NULL; REAL *order, sum, weight; SOSgroup *group = lp->SOS; /* PART A: Resort individual SOS member lists, if specified */ if(forceresort) SOS_member_sortlist(group, 0); /* PART B: Tally SOS variables and create master SOS variable list */ n = 0; for(i = 0; i < group->sos_count; i++) n += group->sos_list[i]->size; lp->sos_vars = n; if(lp->sos_vars > 0) /* Prevent memory loss in case of multiple solves */ FREE(lp->sos_priority); allocINT(lp, &lp->sos_priority, n, FALSE); allocREAL(lp, &order, n, FALSE); /* Move variable data to the master SOS list and sort by ascending weight */ n = 0; sum = 0; for(i = 0; i < group->sos_count; i++) { for(j = 1; j <= group->sos_list[i]->size; j++) { lp->sos_priority[n] = group->sos_list[i]->members[j]; weight = group->sos_list[i]->weights[j]; sum += weight; order[n] = sum; n++; } } hpsortex(order, n, 0, sizeof(*order), FALSE, compareREAL, lp->sos_priority); FREE(order); /* Remove duplicate SOS variables */ allocMYBOOL(lp, &hold, lp->columns+1, TRUE); k = 0; for(i = 0; i < n; i++) { j = lp->sos_priority[i]; if(!hold[j]) { hold[j] = TRUE; if(k < i) lp->sos_priority[k] = j; k++; } } FREE(hold); /* Adjust the size of the master variable list, if necessary */ if(k < lp->sos_vars) { allocINT(lp, &lp->sos_priority, k, AUTOMATIC); lp->sos_vars = k; } return( k ); }
STATIC MYBOOL resizePricer(lprec *lp) { if(!applyPricer(lp)) return( TRUE ); /* Reallocate vector for new size */ if(!allocREAL(lp, &(lp->edgeVector), lp->sum_alloc+1, AUTOMATIC)) return( FALSE ); /* Signal that we have not yet initialized the price vector */ MEMCLEAR(lp->edgeVector, lp->sum_alloc+1); lp->edgeVector[0] = -1; return( TRUE ); }
STATIC REAL auto_scale(lprec *lp) { int n = 1; REAL scalingmetric = 0, *scalenew = NULL; if(lp->scaling_used && ((((lp->scalemode & SCALE_DYNUPDATE) == 0)) || (lp->bb_level > 0))) return( scalingmetric); if(lp->scalemode != SCALE_NONE) { /* Allocate array for incremental scaling if appropriate */ if((lp->solvecount > 1) && (lp->bb_level < 1) && ((lp->scalemode & SCALE_DYNUPDATE) != 0)) allocREAL(lp, &scalenew, lp->sum + 1, FALSE); if(is_scaletype(lp, SCALE_CURTISREID)) { scalingmetric = scaleCR(lp, scalenew); } else { REAL scalinglimit, scalingdelta; int count; /* Integer value of scalelimit holds the maximum number of iterations; default to 1 */ count = (int) floor(lp->scalelimit); scalinglimit = lp->scalelimit; if((count == 0) || (scalinglimit == 0)) { if(scalinglimit > 0) count = DEF_SCALINGLIMIT; /* A non-zero convergence has been given, default to max 5 iterations */ else count = 1; } else scalinglimit -= count; /* Scale to desired relative convergence or iteration limit */ n = 0; scalingdelta = 1.0; scalingmetric = 1.0; while((n < count) && (fabs(scalingdelta) > scalinglimit)) { n++; scalingdelta = scale(lp, scalenew); scalingmetric = scalingmetric*(1+scalingdelta); } scalingmetric -= 1; } } /* Update the inf norm of the elements of the matrix (excluding the OF) */ mat_computemax(lp->matA); /* Check if we really have to do scaling */ if(lp->scaling_used && (fabs(scalingmetric) >= lp->epsprimal)) /* Ok, do it */ finalize_scaling(lp, scalenew); else { /* Otherwise reset scaling variables */ if(lp->scalars != NULL) { FREE(lp->scalars); } lp->scaling_used = FALSE; lp->columns_scaled = FALSE; } if(scalenew != NULL) FREE(scalenew); return(scalingmetric); }
STATIC int append_SOSrec(SOSrec *SOS, int size, int *variables, REAL *weights) { int i, oldsize, newsize, nn; lprec *lp = SOS->parent->lp; oldsize = SOS->size; newsize = oldsize + size; nn = abs(SOS->type); /* Shift existing active data right (normally zero) */ if(SOS->members == NULL) allocINT(lp, &SOS->members, 1+newsize+1+nn, TRUE); else { allocINT(lp, &SOS->members, 1+newsize+1+nn, AUTOMATIC); for(i = newsize+1+nn; i > newsize+1; i--) SOS->members[i] = SOS->members[i-size]; } SOS->members[0] = newsize; SOS->members[newsize+1] = nn; /* Copy the new data into the arrays */ if(SOS->weights == NULL) allocREAL(lp, &SOS->weights, 1+newsize, TRUE); else allocREAL(lp, &SOS->weights, 1+newsize, AUTOMATIC); for(i = oldsize+1; i <= newsize; i++) { SOS->members[i] = variables[i-oldsize-1]; if((SOS->members[i] < 1) || (SOS->members[i] > lp->columns)) report(lp, IMPORTANT, "append_SOS_rec: Invalid SOS variable definition for index %d\n", SOS->members[i]); else { if(SOS->isGUB) lp->var_type[SOS->members[i]] |= ISGUB; else lp->var_type[SOS->members[i]] |= ISSOS; } if(weights == NULL) SOS->weights[i] = i; /* Follow standard, which is sorted ascending */ else SOS->weights[i] = weights[i-oldsize-1]; SOS->weights[0] += SOS->weights[i]; } /* Sort the new paired lists ascending by weight (simple bubble sort) */ i = sortByREAL(SOS->members, SOS->weights, newsize, 1, TRUE); if(i > 0) report(lp, DETAILED, "append_SOS_rec: Non-unique SOS variable weight for index %d\n", i); /* Define mapping arrays to search large SOS's faster */ allocINT(lp, &SOS->membersSorted, newsize, AUTOMATIC); allocINT(lp, &SOS->membersMapped, newsize, AUTOMATIC); for(i = oldsize+1; i <= newsize; i++) { SOS->membersSorted[i - 1] = SOS->members[i]; SOS->membersMapped[i - 1] = i; } sortByINT(SOS->membersMapped, SOS->membersSorted, newsize, 0, TRUE); /* Confirm the new size */ SOS->size = newsize; return(newsize); }
MYBOOL __WINAPI guess_basis(lprec *lp, REAL *guessvector, int *basisvector) { MYBOOL *isnz = NULL, status = FALSE; REAL *values = NULL, *violation = NULL, eps = lp->epsprimal, *value, error, upB, loB, sortorder = -1.0; int i, j, jj, n, *rownr, *colnr, *slkpos = NULL, nrows = lp->rows, ncols = lp->columns, nsum = lp->sum; int *basisnr; MATrec *mat = lp->matA; if(!mat_validate(mat)) return( status ); /* Create helper arrays, providing for multiple use of the violation array */ if(!allocREAL(lp, &values, nsum+1, TRUE) || !allocREAL(lp, &violation, nsum+1, TRUE)) goto Finish; /* Compute the values of the constraints for the given guess vector */ i = 0; n = get_nonzeros(lp); rownr = &COL_MAT_ROWNR(i); colnr = &COL_MAT_COLNR(i); value = &COL_MAT_VALUE(i); for(; i < n; i++, rownr += matRowColStep, colnr += matRowColStep, value += matValueStep) values[*rownr] += unscaled_mat(lp, my_chsign(is_chsign(lp, *rownr), *value), *rownr, *colnr) * guessvector[*colnr]; MEMMOVE(values+nrows+1, guessvector+1, ncols); /* Initialize bound "violation" or primal non-degeneracy measures, expressed as the absolute value of the differences from the closest bound. */ for(i = 1; i <= nsum; i++) { if(i <= nrows) { loB = get_rh_lower(lp, i); upB = get_rh_upper(lp, i); } else { loB = get_lowbo(lp, i-nrows); upB = get_upbo(lp, i-nrows); } /* Free constraints/variables */ if(my_infinite(lp, loB) && my_infinite(lp, upB)) error = 0; /* Violated constraints/variable bounds */ else if(values[i]+eps < loB) error = loB-values[i]; else if(values[i]-eps > upB) error = values[i]-upB; /* Non-violated constraints/variables bounds */ else if(my_infinite(lp, upB)) error = MAX(0, values[i]-loB); else if(my_infinite(lp, loB)) error = MAX(0, upB-values[i]); else error = MIN(upB-values[i], values[i]-loB); /* MAX(upB-values[i], values[i]-loB); */ if(error != 0) violation[i] = sortorder*error; basisvector[i] = i; } /* Sort decending , meaning that variables with the largest "violations" will be designated basic. Effectively, we are performing a greedy type algorithm, but start at the "least interesting" end. */ sortByREAL(basisvector, violation, nsum, 1, FALSE); error = violation[1]; /* Used for setting the return value */ /* Let us check for obvious row singularities and try to fix these. Note that we reuse the memory allocated to the violation array. First assemble necessary basis statistics... */ slkpos = (int *) violation; n = nrows+1; MEMCLEAR(slkpos, n); isnz = (MYBOOL *) (slkpos+n+1); MEMCLEAR(isnz, n); for(i = 1; i <= nrows; i++) { j = abs(basisvector[i]); if(j <= nrows) { isnz[j] = TRUE; slkpos[j] = i; } else { j-= nrows; jj = mat->col_end[j-1]; jj = COL_MAT_ROWNR(jj); isnz[jj] = TRUE; } } for(; i <= nsum; i++) { j = abs(basisvector[i]); if(j <= nrows) slkpos[j] = i; } /* ...then set the corresponding slacks basic for row rank deficient positions */ for(j = 1; j <= nrows; j++) { if(slkpos[j] == 0) report(lp, SEVERE, "guess_basis: Internal error"); if(!isnz[j]) { isnz[j] = TRUE; i = slkpos[j]; swapINT(&basisvector[i], &basisvector[j]); basisvector[j] = abs(basisvector[j]); } } /* Adjust the non-basic indeces for the (proximal) bound state */ for(i = nrows+1, basisnr = basisvector+i; i <= nsum; i++, basisnr++) { n = *basisnr; if(n <= nrows) { values[n] -= get_rh_lower(lp, n); if(values[n] <= eps) *basisnr = -(*basisnr); } else if(values[n]-eps <= get_lowbo(lp, n-nrows)) *basisnr = -(*basisnr); } /* Lastly normalize all basic variables to be coded as lower-bounded, or effectively zero-based in the case of free variables. */ for(i = 1; i <= nrows; i++) basisvector[i] = -abs(basisvector[i]); /* Clean up and return status */ status = (MYBOOL) (error <= eps); Finish: FREE(values); FREE(violation); return( status ); }
MYBOOL __WINAPI guess_basis(lprec *lp, REAL *guessvector, int *basisvector) { MYBOOL *isnz, status = FALSE; REAL *values = NULL, *violation = NULL, eps = lp->epsprimal, *value, error, upB, loB, sortorder = 1.0; int i, j, jj, n, *rownr, *colnr, *slkpos, nrows = lp->rows, ncols = lp->columns; MATrec *mat = lp->matA; if(!mat_validate(mat)) return( status ); /* Create helper arrays */ if(!allocREAL(lp, &values, lp->sum+1, TRUE) || !allocREAL(lp, &violation, lp->sum+1, TRUE)) goto Finish; /* Compute values of slack variables for given guess vector */ i = 0; n = get_nonzeros(lp); rownr = &COL_MAT_ROWNR(i); colnr = &COL_MAT_COLNR(i); value = &COL_MAT_VALUE(i); for(; i < n; i++, rownr += matRowColStep, colnr += matRowColStep, value += matValueStep) values[*rownr] += unscaled_mat(lp, my_chsign(is_chsign(lp, *rownr), *value), *rownr, *colnr) * guessvector[*colnr]; MEMMOVE(values+nrows+1, guessvector+1, ncols); /* Initialize constraint bound violation measures (expressed as positive values) */ for(i = 1; i <= nrows; i++) { upB = get_rh_upper(lp, i); loB = get_rh_lower(lp, i); error = values[i] - upB; if(error > -eps) violation[i] = sortorder*MAX(0,error); else { error = loB - values[i]; if(error > -eps) violation[i] = sortorder*MAX(0,error); else if(my_infinite(lp, loB) && my_infinite(lp, upB)) ; else if(my_infinite(lp, upB)) violation[i] = sortorder*(loB - values[i]); else if(my_infinite(lp, loB)) violation[i] = sortorder*(values[i] - upB); else violation[i] = -sortorder*MAX(upB - values[i], values[i] - loB); } basisvector[i] = i; } /* Initialize user variable bound violation measures (expressed as positive values) */ for(i = 1; i <= ncols; i++) { n = nrows+i; upB = get_upbo(lp, i); loB = get_lowbo(lp, i); error = guessvector[i] - upB; if(error > -eps) violation[n] = sortorder*MAX(0,error); else { error = loB - values[n]; if(error > -eps) violation[n] = sortorder*MAX(0,error); else if(my_infinite(lp, loB) && my_infinite(lp, upB)) ; else if(my_infinite(lp, upB)) violation[n] = sortorder*(loB - values[n]); else if(my_infinite(lp, loB)) violation[n] = sortorder*(values[n] - upB); else violation[n] = -sortorder*MAX(upB - values[n], values[n] - loB); } basisvector[n] = n; } /* Sort decending by violation; this means that variables with the largest violations will be designated as basic */ sortByREAL(basisvector, violation, lp->sum, 1, FALSE); error = violation[1]; /* Adjust the non-basic indeces for the (proximal) bound state */ for(i = nrows+1, rownr = basisvector+i; i <= lp->sum; i++, rownr++) { if(*rownr <= nrows) { values[*rownr] -= lp->orig_rhs[*rownr]; if(values[*rownr] <= eps) *rownr = -(*rownr); } else if(values[i] <= get_lowbo(lp, (*rownr)-nrows)+eps) *rownr = -(*rownr); } /* Let us check for obvious row singularities and try to fix these; First assemble necessary basis statistics... */ isnz = (MYBOOL *) values; MEMCLEAR(isnz, nrows+1); slkpos = (int *) violation; MEMCLEAR(slkpos, nrows+1); for(i = 1; i <= nrows; i++) { j = abs(basisvector[i]); if(j <= nrows) { isnz[j] = TRUE; slkpos[j] = i; } else { j-= nrows; jj = mat->col_end[j-1]; isnz[COL_MAT_ROWNR(jj)] = TRUE; /* if(++jj < mat->col_end[j]) isnz[COL_MAT_ROWNR(jj)] = TRUE; */ } } for(; i <= lp->sum; i++) { j = abs(basisvector[i]); if(j <= nrows) slkpos[j] = i; } /* ...then set the corresponding slacks basic for row rank deficient positions */ for(j = 1; j <= nrows; j++) { #ifdef Paranoia if(slkpos[j] == 0) report(lp, SEVERE, "guess_basis: Internal error"); #endif if(!isnz[j]) { isnz[j] = TRUE; i = slkpos[j]; swapINT(&basisvector[i], &basisvector[j]); basisvector[j] = abs(basisvector[j]); } } /* Lastly normalize all basic variables to be coded as lower-bounded */ for(i = 1; i <= nrows; i++) basisvector[i] = -abs(basisvector[i]); /* Clean up and return status */ status = (MYBOOL) (error <= eps); Finish: FREE(values); FREE(violation); return( status ); }
MYBOOL __WINAPI guess_basis(lprec *lp, REAL *guessvector, int *basisvector) { MYBOOL status = FALSE; REAL *values = NULL, *violation = NULL, *value, error, upB, loB, sortorder = 1.0; int i, n, *rownr, *colnr; MATrec *mat = lp->matA; if(!mat_validate(lp->matA)) return( status ); /* Create helper arrays */ if(!allocREAL(lp, &values, lp->sum+1, TRUE) || !allocREAL(lp, &violation, lp->sum+1, TRUE)) goto Finish; /* Compute values of slack variables for given guess vector */ i = 0; n = get_nonzeros(lp); rownr = &COL_MAT_ROWNR(i); colnr = &COL_MAT_COLNR(i); value = &COL_MAT_VALUE(i); for(; i < n; i++, rownr += matRowColStep, colnr += matRowColStep, value += matValueStep) values[*rownr] += unscaled_mat(lp, my_chsign(is_chsign(lp, *rownr), *value), *rownr, *colnr) * guessvector[*colnr]; MEMMOVE(values+lp->rows+1, guessvector+1, lp->columns); /* Initialize constraint bound violation measures */ for(i = 1; i <= lp->rows; i++) { upB = get_rh_upper(lp, i); loB = get_rh_lower(lp, i); error = values[i] - upB; if(error > lp->epsprimal) violation[i] = sortorder*error; else { error = loB - values[i]; if(error > lp->epsprimal) violation[i] = sortorder*error; else if(is_infinite(lp, loB) && is_infinite(lp, upB)) ; else if(is_infinite(lp, upB)) violation[i] = sortorder*(loB - values[i]); else if(is_infinite(lp, loB)) violation[i] = sortorder*(values[i] - upB); else violation[i] = - sortorder*MAX(upB - values[i], values[i] - loB); } basisvector[i] = i; } /* Initialize user variable bound violation measures */ for(i = 1; i <= lp->columns; i++) { n = lp->rows+i; upB = get_upbo(lp, i); loB = get_lowbo(lp, i); error = guessvector[i] - upB; if(error > lp->epsprimal) violation[n] = sortorder*error; else { error = loB - values[n]; if(error > lp->epsprimal) violation[n] = sortorder*error; else if(is_infinite(lp, loB) && is_infinite(lp, upB)) ; else if(is_infinite(lp, upB)) violation[n] = sortorder*(loB - values[n]); else if(is_infinite(lp, loB)) violation[n] = sortorder*(values[n] - upB); else violation[n] = - sortorder*MAX(upB - values[n], values[n] - loB); } basisvector[n] = n; } /* Sort decending by violation; this means that variables with the largest violations will be designated as basic */ sortByREAL(basisvector, violation, lp->sum, 1, FALSE); /* Adjust the non-basic indeces for the (proximal) bound state */ error = lp->epsprimal; for(i = lp->rows+1, rownr = basisvector+i; i <= lp->sum; i++, rownr++) { if(*rownr <= lp->rows) { if(values[*rownr] <= get_rh_lower(lp, *rownr)+error) *rownr = -(*rownr); } else if(values[i] <= get_lowbo(lp, (*rownr)-lp->rows)+error) *rownr = -(*rownr); } /* Clean up and return status */ status = (MYBOOL) (violation[1] == 0); Finish: FREE(values); FREE(violation); return( status ); }
MYBOOL crash_basis(lprec *lp) { int i; MATrec *mat = lp->matA; MYBOOL ok = TRUE; /* Initialize basis indicators */ if(lp->basis_valid) lp->var_basic[0] = FALSE; else default_basis(lp); /* Set initial partial pricing blocks */ if(lp->rowblocks != NULL) lp->rowblocks->blocknow = 1; if(lp->colblocks != NULL) lp->colblocks->blocknow = ((lp->crashmode == CRASH_NONE) || (lp->colblocks->blockcount == 1) ? 1 : 2); /* Construct a basis that is in some measure the "most feasible" */ if((lp->crashmode == CRASH_MOSTFEASIBLE) && mat_validate(mat)) { /* The logic here follows Maros */ LLrec *rowLL = NULL, *colLL = NULL; int ii, rx, cx, ix, nz; REAL wx, tx, *rowMAX = NULL, *colMAX = NULL; int *rowNZ = NULL, *colNZ = NULL, *rowWT = NULL, *colWT = NULL; REAL *value; int *rownr, *colnr; report(lp, NORMAL, "crash_basis: 'Most feasible' basis crashing selected\n"); /* Tally row and column non-zero counts */ ok = allocINT(lp, &rowNZ, lp->rows+1, TRUE) && allocINT(lp, &colNZ, lp->columns+1, TRUE) && allocREAL(lp, &rowMAX, lp->rows+1, FALSE) && allocREAL(lp, &colMAX, lp->columns+1, FALSE); if(!ok) goto Finish; nz = mat_nonzeros(mat); rownr = &COL_MAT_ROWNR(0); colnr = &COL_MAT_COLNR(0); value = &COL_MAT_VALUE(0); for(i = 0; i < nz; i++, rownr += matRowColStep, colnr += matRowColStep, value += matValueStep) { rx = *rownr; cx = *colnr; wx = fabs(*value); rowNZ[rx]++; colNZ[cx]++; if(i == 0) { rowMAX[rx] = wx; colMAX[cx] = wx; colMAX[0] = wx; } else { SETMAX(rowMAX[rx], wx); SETMAX(colMAX[cx], wx); SETMAX(colMAX[0], wx); } } /* Reduce counts for small magnitude to preserve stability */ rownr = &COL_MAT_ROWNR(0); colnr = &COL_MAT_COLNR(0); value = &COL_MAT_VALUE(0); for(i = 0; i < nz; i++, rownr += matRowColStep, colnr += matRowColStep, value += matValueStep) { rx = *rownr; cx = *colnr; wx = fabs(*value); #ifdef CRASH_SIMPLESCALE if(wx < CRASH_THRESHOLD * colMAX[0]) { rowNZ[rx]--; colNZ[cx]--; } #else if(wx < CRASH_THRESHOLD * rowMAX[rx]) rowNZ[rx]--; if(wx < CRASH_THRESHOLD * colMAX[cx]) colNZ[cx]--; #endif } /* Set up priority tables */ ok = allocINT(lp, &rowWT, lp->rows+1, TRUE); createLink(lp->rows, &rowLL, NULL); ok &= (rowLL != NULL); if(!ok) goto Finish; for(i = 1; i <= lp->rows; i++) { if(get_constr_type(lp, i)==EQ) ii = 3; else if(lp->upbo[i] < lp->infinite) ii = 2; else if(fabs(lp->rhs[i]) < lp->infinite) ii = 1; else ii = 0; rowWT[i] = ii; if(ii > 0) appendLink(rowLL, i); } ok = allocINT(lp, &colWT, lp->columns+1, TRUE); createLink(lp->columns, &colLL, NULL); ok &= (colLL != NULL); if(!ok) goto Finish; for(i = 1; i <= lp->columns; i++) { ix = lp->rows+i; if(is_unbounded(lp, i)) ii = 3; else if(lp->upbo[ix] >= lp->infinite) ii = 2; else if(fabs(lp->upbo[ix]-lp->lowbo[ix]) > lp->epsmachine) ii = 1; else ii = 0; colWT[i] = ii; if(ii > 0) appendLink(colLL, i); } /* Loop over all basis variables */ for(i = 1; i <= lp->rows; i++) { /* Select row */ rx = 0; wx = -lp->infinite; for(ii = firstActiveLink(rowLL); ii > 0; ii = nextActiveLink(rowLL, ii)) { tx = rowWT[ii] - CRASH_SPACER*rowNZ[ii]; if(tx > wx) { rx = ii; wx = tx; } } if(rx == 0) break; removeLink(rowLL, rx); /* Select column */ cx = 0; wx = -lp->infinite; for(ii = mat->row_end[rx-1]; ii < mat->row_end[rx]; ii++) { /* Update NZ column counts for row selected above */ tx = fabs(ROW_MAT_VALUE(ii)); ix = ROW_MAT_COLNR(ii); #ifdef CRASH_SIMPLESCALE if(tx >= CRASH_THRESHOLD * colMAX[0]) #else if(tx >= CRASH_THRESHOLD * colMAX[ix]) #endif colNZ[ix]--; if(!isActiveLink(colLL, ix) || (tx < CRASH_THRESHOLD * rowMAX[rx])) continue; /* Now do the test for best pivot */ tx = my_sign(lp->orig_obj[ix]) - my_sign(ROW_MAT_VALUE(ii)); tx = colWT[ix] + CRASH_WEIGHT*tx - CRASH_SPACER*colNZ[ix]; if(tx > wx) { cx = ix; wx = tx; } } if(cx == 0) break; removeLink(colLL, cx); /* Update row NZ counts */ ii = mat->col_end[cx-1]; rownr = &COL_MAT_ROWNR(ii); value = &COL_MAT_VALUE(ii); for(; ii < mat->col_end[cx]; ii++, rownr += matRowColStep, value += matValueStep) { wx = fabs(*value); ix = *rownr; #ifdef CRASH_SIMPLESCALE if(wx >= CRASH_THRESHOLD * colMAX[0]) #else if(wx >= CRASH_THRESHOLD * rowMAX[ix]) #endif rowNZ[ix]--; } /* Set new basis variable */ set_basisvar(lp, rx, lp->rows+cx); } /* Clean up */ Finish: FREE(rowNZ); FREE(colNZ); FREE(rowMAX); FREE(colMAX); FREE(rowWT); FREE(colWT); freeLink(&rowLL); freeLink(&colLL); } /* Construct a basis that is in some measure the "least degenerate" */ else if((lp->crashmode == CRASH_LEASTDEGENERATE) && mat_validate(mat)) { /* The logic here follows Maros */ LLrec *rowLL = NULL, *colLL = NULL; int ii, rx, cx, ix, nz, *merit = NULL; REAL *value, wx, hold, *rhs = NULL, *eta = NULL; int *rownr, *colnr; report(lp, NORMAL, "crash_basis: 'Least degenerate' basis crashing selected\n"); /* Create temporary arrays */ ok = allocINT(lp, &merit, lp->columns + 1, FALSE) && allocREAL(lp, &eta, lp->rows + 1, FALSE) && allocREAL(lp, &rhs, lp->rows + 1, FALSE); createLink(lp->columns, &colLL, NULL); createLink(lp->rows, &rowLL, NULL); ok &= (colLL != NULL) && (rowLL != NULL); if(!ok) goto FinishLD; MEMCOPY(rhs, lp->orig_rhs, lp->rows + 1); for(i = 1; i <= lp->columns; i++) appendLink(colLL, i); for(i = 1; i <= lp->rows; i++) appendLink(rowLL, i); /* Loop until we have found enough new bases */ while(colLL->count > 0) { /* Tally non-zeros matching in RHS and each active column */ nz = mat_nonzeros(mat); rownr = &COL_MAT_ROWNR(0); colnr = &COL_MAT_COLNR(0); ii = 0; MEMCLEAR(merit, lp->columns + 1); for(i = 0; i < nz; i++, rownr += matRowColStep, colnr += matRowColStep) { rx = *rownr; cx = *colnr; if(isActiveLink(colLL, cx) && (rhs[rx] != 0)) { merit[cx]++; ii++; } } if(ii == 0) break; /* Find maximal match; break ties with column length */ i = firstActiveLink(colLL); cx = i; for(i = nextActiveLink(colLL, i); i != 0; i = nextActiveLink(colLL, i)) { if(merit[i] >= merit[cx]) { if((merit[i] > merit[cx]) || (mat_collength(mat, i) > mat_collength(mat, cx))) cx = i; } } /* Determine the best pivot row */ i = mat->col_end[cx-1]; nz = mat->col_end[cx]; rownr = &COL_MAT_ROWNR(i); value = &COL_MAT_VALUE(i); rx = 0; wx = 0; MEMCLEAR(eta, lp->rows + 1); for(; i < nz; i++, rownr += matRowColStep, value += matValueStep) { ix = *rownr; hold = *value; eta[ix] = rhs[ix] / hold; hold = fabs(hold); if(isActiveLink(rowLL, ix) && (hold > wx)) { wx = hold; rx = ix; } } /* Set new basis variable */ if(rx > 0) { /* We have to update the rhs vector for the implied transformation in order to be able to find the new RHS non-zero pattern */ for(i = 1; i <= lp->rows; i++) rhs[i] -= wx * eta[i]; rhs[rx] = wx; /* Do the exchange */ set_basisvar(lp, rx, lp->rows+cx); removeLink(rowLL, rx); } removeLink(colLL, cx); } /* Clean up */ FinishLD: FREE(merit); FREE(rhs); freeLink(&rowLL); freeLink(&colLL); } return( ok ); }
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 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 {
/* Report the traditional tableau corresponding to the current basis */ MYBOOL REPORT_tableau(lprec *lp) { int j, row_nr, *coltarget; LPSREAL *prow = NULL; FILE *stream = lp->outstream; if(lp->outstream == NULL) return(FALSE); if(!lp->model_is_valid || !has_BFP(lp) || (get_total_iter(lp) == 0) || (lp->spx_status == NOTRUN)) { lp->spx_status = NOTRUN; return(FALSE); } if(!allocREAL(lp, &prow,lp->sum + 1, TRUE)) { lp->spx_status = NOMEMORY; return(FALSE); } fprintf(stream, "\n"); fprintf(stream, "Tableau at iter %.0f:\n", (double) get_total_iter(lp)); for(j = 1; j <= lp->sum; j++) if (!lp->is_basic[j]) fprintf(stream, "%15d", (j <= lp->rows ? (j + lp->columns) * ((lp->orig_upbo[j] == 0) || (is_chsign(lp, j)) ? 1 : -1) : j - lp->rows) * (lp->is_lower[j] ? 1 : -1)); fprintf(stream, "\n"); coltarget = (int *) mempool_obtainVector(lp->workarrays, lp->columns+1, sizeof(*coltarget)); if(!get_colIndexA(lp, SCAN_USERVARS+USE_NONBASICVARS, coltarget, FALSE)) { mempool_releaseVector(lp->workarrays, (char *) coltarget, FALSE); return(FALSE); } for(row_nr = 1; (row_nr <= lp->rows + 1); row_nr++) { if (row_nr <= lp->rows) fprintf(stream, "%3d", (lp->var_basic[row_nr] <= lp->rows ? (lp->var_basic[row_nr] + lp->columns) * ((lp->orig_upbo[lp->var_basic [row_nr]] == 0) || (is_chsign(lp, lp->var_basic[row_nr])) ? 1 : -1) : lp->var_basic[row_nr] - lp->rows) * (lp->is_lower[lp->var_basic [row_nr]] ? 1 : -1)); else fprintf(stream, " "); bsolve(lp, row_nr <= lp->rows ? row_nr : 0, prow, NULL, lp->epsmachine*DOUBLEROUND, 1.0); prod_xA(lp, coltarget, prow, NULL, lp->epsmachine, 1.0, prow, NULL, MAT_ROUNDDEFAULT); for(j = 1; j <= lp->rows + lp->columns; j++) if (!lp->is_basic[j]) fprintf(stream, "%15.7f", prow[j] * (lp->is_lower[j] ? 1 : -1) * (row_nr <= lp->rows ? 1 : -1)); fprintf(stream, "%15.7f", lp->rhs[row_nr <= lp->rows ? row_nr : 0] * (double) ((row_nr <= lp->rows) || (is_maxim(lp)) ? 1 : -1)); fprintf(stream, "\n"); } fflush(stream); mempool_releaseVector(lp->workarrays, (char *) coltarget, FALSE); FREE(prow); return(TRUE); }
STATIC REAL scale(lprec *lp, REAL *scaledelta) { int i, j, nz, row_count, nzOF = 0; REAL *row_max, *row_min, *scalechange = NULL, absval; REAL col_max, col_min; MYBOOL rowscaled, colscaled; MATrec *mat = lp->matA; REAL *value; int *rownr; if(is_scaletype(lp, SCALE_NONE)) return(0.0); if(!lp->scaling_used) { allocREAL(lp, &lp->scalars, lp->sum_alloc + 1, FALSE); for(i = 0; i <= lp->sum; i++) { lp->scalars[i] = 1; } lp->scaling_used = TRUE; } #ifdef Paranoia else for(i = 0; i <= lp->sum; i++) { if(lp->scalars[i] == 0) report(lp, SEVERE, "scale: Zero-valued scalar found at index %d\n", i); } #endif if(scaledelta == NULL) allocREAL(lp, &scalechange, lp->sum + 1, FALSE); else scalechange = scaledelta; /* Must initialize due to computation of scaling statistic - KE */ for(i = 0; i <= lp->sum; i++) scalechange[i] = 1; row_count = lp->rows; allocREAL(lp, &row_max, row_count + 1, TRUE); allocREAL(lp, &row_min, row_count + 1, FALSE); /* Initialise min and max values of rows */ for(i = 0; i <= row_count; i++) { if(is_scaletype(lp, SCALE_MEAN)) row_min[i] = 0; /* Carries the count of elements */ else row_min[i] = lp->infinite; /* Carries the minimum element */ } /* Calculate row scaling data */ for(j = 1; j <= lp->columns; j++) { absval = lp->orig_obj[j]; if(absval != 0) { absval = scaled_mat(lp, absval, 0, j); accumulate_for_scale(lp, &row_min[0], &row_max[0], absval); nzOF++; } i = mat->col_end[j - 1]; value = &(COL_MAT_VALUE(i)); rownr = &(COL_MAT_ROWNR(i)); nz = mat->col_end[j]; for(; i < nz; i++, value += matValueStep, rownr += matRowColStep) { absval = scaled_mat(lp, *value, *rownr, j); accumulate_for_scale(lp, &row_min[*rownr], &row_max[*rownr], absval); } } /* Calculate scale factors for rows */ i = 0; for(; i <= lp->rows; i++) { if(i == 0) nz = nzOF; else nz = mat_rowlength(lp->matA, i); absval = minmax_to_scale(lp, row_min[i], row_max[i], nz); /* nz instead of nzOF KJEI 20/05/2010 */ if(absval == 0) absval = 1; scalechange[i] = absval; } FREE(row_max); FREE(row_min); /* Row-scale the matrix (including the objective function and Lagrangean constraints) */ rowscaled = scale_updaterows(lp, scalechange, TRUE); /* Calculate column scales */ i = 1; for(j = 1; j <= lp->columns; j++) { if(is_int(lp,j) && !is_integerscaling(lp)) { /* do not scale integer columns */ scalechange[lp->rows + j] = 1; } else { col_max = 0; if(is_scaletype(lp, SCALE_MEAN)) col_min = 0; else col_min = lp->infinite; absval = lp->orig_obj[j]; if(absval != 0) { absval = scaled_mat(lp, absval, 0, j); accumulate_for_scale(lp, &col_min, &col_max, absval); } i = mat->col_end[j - 1]; value = &(COL_MAT_VALUE(i)); rownr = &(COL_MAT_ROWNR(i)); nz = mat->col_end[j]; for(; i < nz; i++, value += matValueStep, rownr += matRowColStep) { absval = scaled_mat(lp, *value, *rownr, j); accumulate_for_scale(lp, &col_min, &col_max, absval); } nz = mat_collength(lp->matA, j); if(fabs(lp->orig_obj[j]) > 0) nz++; scalechange[lp->rows + j] = minmax_to_scale(lp, col_min, col_max, nz); } } /* ... and then column-scale the already row-scaled matrix */ colscaled = scale_updatecolumns(lp, &scalechange[lp->rows], TRUE); /* Create a geometric mean-type measure of the extent of scaling performed; */ /* ideally, upon successive calls to scale() the value should converge to 0 */ if(rowscaled || colscaled) { col_max = 0; for(j = 1; j <= lp->columns; j++) col_max += log(scalechange[lp->rows + j]); col_max = exp(col_max/lp->columns); i = 0; col_min = 0; for(; i <= lp->rows; i++) col_min += log(scalechange[i]); col_min = exp(col_min/row_count); } else { col_max = 1; col_min = 1; } if(scaledelta == NULL) FREE(scalechange); return(1 - sqrt(col_max*col_min)); }
/* MUST MODIFY */ MYBOOL BFP_CALLMODEL bfp_resize(lprec *lp, int newsize) { INVrec *lu; lu = lp->invB; /* Increment dimensionality since we put the objective row at the top */ newsize = newsize + bfp_rowoffset(lp); lu->dimalloc = newsize; /* Allocate index tracker arrays, LU matrices and various work vectors */ if(!allocREAL(lp, &(lu->value), newsize+MATINDEXBASE, AUTOMATIC)) return( FALSE ); /* Data specific to the factorization engine */ if(lu->LUSOL != NULL) { if(newsize > 0 || 1) LUSOL_sizeto(lu->LUSOL, newsize, newsize, 0); else { LUSOL_free(lu->LUSOL); lu->LUSOL = NULL; } } else if(newsize > 0 || 1) { int asize; REAL bsize; lu->LUSOL = LUSOL_create(NULL, 0, LUSOL_PIVMOD_TPP, bfp_pivotmax(lp)*0); #if 1 lu->LUSOL->luparm[LUSOL_IP_ACCELERATION] = LUSOL_AUTOORDER; lu->LUSOL->parmlu[LUSOL_RP_SMARTRATIO] = 0.50; #endif #if 0 lu->timed_refact = DEF_TIMEDREFACT; #else lu->timed_refact = FALSE; #endif /* The following adjustments seem necessary to make the really tough NETLIB models perform reliably and still performant (e.g. cycle.mps) */ #if 0 lu->LUSOL->parmlu[LUSOL_RP_SMALLDIAG_U] = lu->LUSOL->parmlu[LUSOL_RP_EPSDIAG_U] = lp->epsprimal; #endif #if 0 lu->LUSOL->parmlu[LUSOL_RP_ZEROTOLERANCE] = lp->epsvalue; #endif #if 1 LUSOL_setpivotmodel(lu->LUSOL, LUSOL_PIVMOD_NOCHANGE, LUSOL_PIVTOL_SLIM); #else LUSOL_setpivotmodel(lu->LUSOL, LUSOL_PIVMOD_NOCHANGE, LUSOL_PIVTOL_TIGHT); #endif #ifdef LUSOL_UseBLAS /* if(fileSearchPath("PATH", "myBLAS.DLL", NULL) && load_BLAS("myBLAS")) */ if(is_nativeBLAS() && load_BLAS(libnameBLAS)) lp->report(lp, NORMAL, "Optimized BLAS was successfully loaded for bfp_LUSOL.\n"); #endif /* Try to minimize memory allocation if we have a large number of unit columns */ bsize = (REAL) lp->get_nonzeros(lp); if(newsize > lp->columns) bsize += newsize; else bsize = bsize/lp->columns*newsize; /* Add a "reasonable" delta to allow for B and associated factorizations that are denser than average; this makes reallocations less frequent. Values between 1.2 and 1.5 appear to be reasonable. */ asize = (int) (bsize*MAX_DELTAFILLIN*1.3333); if(!LUSOL_sizeto(lu->LUSOL, newsize, newsize, asize)) return( FALSE ); } lu->dimcount = newsize; return( TRUE ); }
int CurtisReidScales(lprec *lp, MYBOOL _Advanced, REAL *FRowScale, REAL *FColScale) { int i, row, col, ent, nz; REAL *RowScalem2, *ColScalem2, *RowSum, *ColSum, *residual_even, *residual_odd; REAL sk, qk, ek, skm1, qkm1, ekm1, qkm2, qkqkm1, ekm2, ekekm1, absvalue, logvalue, StopTolerance; int *RowCount, *ColCount, colMax; int Result; MATrec *mat = lp->matA; REAL *value; int *rownr, *colnr; if(CurtisReidMeasure(lp, _Advanced, FRowScale, FColScale)<0.1*get_nonzeros(lp)) return(0); /* Allocate temporary memory and find RowSum and ColSum measures */ nz = get_nonzeros(lp); colMax = lp->columns; allocREAL(lp, &RowSum, lp->rows+1, TRUE); allocINT(lp, &RowCount, lp->rows+1, TRUE); allocREAL(lp, &residual_odd, lp->rows+1, TRUE); allocREAL(lp, &ColSum, colMax+1, TRUE); allocINT(lp, &ColCount, colMax+1, TRUE); allocREAL(lp, &residual_even, colMax+1, TRUE); allocREAL(lp, &RowScalem2, lp->rows+1, FALSE); allocREAL(lp, &ColScalem2, colMax+1, FALSE); /* Set origin for row scaling */ for(i = 1; i <= colMax; i++) { absvalue=fabs(lp->orig_obj[i]); if(absvalue>0) { logvalue = log(absvalue); ColSum[i] += logvalue; RowSum[0] += logvalue; ColCount[i]++; RowCount[0]++; } } value = &(COL_MAT_VALUE(0)); rownr = &(COL_MAT_ROWNR(0)); colnr = &(COL_MAT_COLNR(0)); for(i = 0; i < nz; i++, value += matValueStep, rownr += matRowColStep, colnr += matRowColStep) { absvalue=fabs(*value); if(absvalue>0) { logvalue = log(absvalue); ColSum[*colnr] += logvalue; RowSum[*rownr] += logvalue; ColCount[*colnr]++; RowCount[*rownr]++; } } /* Make sure we dont't have division by zero errors */ for(row = 0; row <= lp->rows; row++) if(RowCount[row] == 0) RowCount[row] = 1; for(col = 1; col <= colMax; col++) if(ColCount[col] == 0) ColCount[col] = 1; /* Initialize to RowScale = RowCount-1 RowSum ColScale = 0.0 residual = ColSum - ET RowCount-1 RowSum */ StopTolerance= MAX(lp->scalelimit-floor(lp->scalelimit), DEF_SCALINGEPS); StopTolerance *= (REAL) nz; for(row = 0; row <= lp->rows; row++) { FRowScale[row] = RowSum[row] / (REAL) RowCount[row]; RowScalem2[row] = FRowScale[row]; } /* Compute initial residual */ for(col = 1; col <= colMax; col++) { FColScale[col] = 0; ColScalem2[col] = 0; residual_even[col] = ColSum[col]; if(lp->orig_obj[col] != 0) residual_even[col] -= RowSum[0] / (REAL) RowCount[0]; i = mat->col_end[col-1]; rownr = &(COL_MAT_ROWNR(i)); ent = mat->col_end[col]; for(; i < ent; i++, rownr += matRowColStep) { residual_even[col] -= RowSum[*rownr] / (REAL) RowCount[*rownr]; } } /* Compute sk */ sk = 0; skm1 = 0; for(col = 1; col <= colMax; col++) sk += (residual_even[col]*residual_even[col]) / (REAL) ColCount[col]; Result = 0; qk=1; qkm1=0; qkm2=0; ek=0; ekm1=0; ekm2=0; while(sk>StopTolerance) { /* Given the values of residual and sk, construct ColScale (when pass is even) RowScale (when pass is odd) */ qkqkm1 = qk * qkm1; ekekm1 = ek * ekm1; if((Result % 2) == 0) { /* pass is even; construct RowScale[pass+1] */ if(Result != 0) { for(row = 0; row <= lp->rows; row++) RowScalem2[row] = FRowScale[row]; if(qkqkm1 != 0) { for(row = 0; row <= lp->rows; row++) FRowScale[row]*=(1 + ekekm1 / qkqkm1); for(row = 0; row<=lp->rows; row++) FRowScale[row]+=(residual_odd[row] / (qkqkm1 * (REAL) RowCount[row]) - RowScalem2[row] * ekekm1 / qkqkm1); } } } else { /* pass is odd; construct ColScale[pass+1] */ for(col = 1; col <= colMax; col++) ColScalem2[col] = FColScale[col]; if(qkqkm1 != 0) { for(col = 1; col <= colMax; col++) FColScale[col] *= (1 + ekekm1 / qkqkm1); for(col = 1; col <= colMax; col++) FColScale[col] += (residual_even[col] / ((REAL) ColCount[col] * qkqkm1) - ColScalem2[col] * ekekm1 / qkqkm1); } } /* update residual and sk (pass + 1) */ if((Result % 2) == 0) { /* even */ /* residual */ for(row = 0; row <= lp->rows; row++) residual_odd[row] *= ek; for(i = 1; i <= colMax; i++) if(lp->orig_obj[i] != 0) residual_odd[0] += (residual_even[i] / (REAL) ColCount[i]); rownr = &(COL_MAT_ROWNR(0)); colnr = &(COL_MAT_COLNR(0)); for(i = 0; i < nz; i++, rownr += matRowColStep, colnr += matRowColStep) { residual_odd[*rownr] += (residual_even[*colnr] / (REAL) ColCount[*colnr]); } for(row = 0; row <= lp->rows; row++) residual_odd[row] *= (-1 / qk); /* sk */ skm1 = sk; sk = 0; for(row = 0; row <= lp->rows; row++) sk += (residual_odd[row]*residual_odd[row]) / (REAL) RowCount[row]; } else { /* odd */ /* residual */ for(col = 1; col <= colMax; col++) residual_even[col] *= ek; for(i = 1; i <= colMax; i++) if(lp->orig_obj[i] != 0) residual_even[i] += (residual_odd[0] / (REAL) RowCount[0]); rownr = &(COL_MAT_ROWNR(0)); colnr = &(COL_MAT_COLNR(0)); for(i = 0; i < nz; i++, rownr += matRowColStep, colnr += matRowColStep) { residual_even[*colnr] += (residual_odd[*rownr] / (REAL) RowCount[*rownr]); } for(col = 1; col <= colMax; col++) residual_even[col] *= (-1 / qk); /* sk */ skm1 = sk; sk = 0; for(col = 1; col <= colMax; col++) sk += (residual_even[col]*residual_even[col]) / (REAL) ColCount[col]; } /* Compute ek and qk */ ekm2=ekm1; ekm1=ek; ek=qk * sk / skm1; qkm2=qkm1; qkm1=qk; qk=1-ek; Result++; } /* Synchronize the RowScale and ColScale vectors */ ekekm1 = ek * ekm1; if(qkm1 != 0) { if((Result % 2) == 0) { /* pass is even, compute RowScale */ for(row = 0; row<=lp->rows; row++) FRowScale[row]*=(1.0 + ekekm1 / qkm1); for(row = 0; row<=lp->rows; row++) FRowScale[row]+=(residual_odd[row] / (qkm1 * (REAL) RowCount[row]) - RowScalem2[row] * ekekm1 / qkm1); } else { /* pass is odd, compute ColScale */ for(col=1; col<=colMax; col++) FColScale[col]*=(1 + ekekm1 / qkm1); for(col=1; col<=colMax; col++) FColScale[col]+=(residual_even[col] / ((REAL) ColCount[col] * qkm1) - ColScalem2[col] * ekekm1 / qkm1); } } /* Do validation, if indicated */ if(FALSE && mat_validate(mat)){ double check, error; /* CHECK: M RowScale + E ColScale = RowSum */ error = 0; for(row = 0; row <= lp->rows; row++) { check = (REAL) RowCount[row] * FRowScale[row]; if(row == 0) { for(i = 1; i <= colMax; i++) { if(lp->orig_obj[i] != 0) check += FColScale[i]; } } else { i = mat->row_end[row-1]; ent = mat->row_end[row]; for(; i < ent; i++) { col = ROW_MAT_COLNR(i); check += FColScale[col]; } } check -= RowSum[row]; error += check*check; } /* CHECK: E^T RowScale + N ColScale = ColSum */ error = 0; for(col = 1; col <= colMax; col++) { check = (REAL) ColCount[col] * FColScale[col]; if(lp->orig_obj[col] != 0) check += FRowScale[0]; i = mat->col_end[col-1]; ent = mat->col_end[col]; rownr = &(COL_MAT_ROWNR(i)); for(; i < ent; i++, rownr += matRowColStep) { check += FRowScale[*rownr]; } check -= ColSum[col]; error += check*check; } } /* Convert to scaling factors (rounding to nearest power of 2 can optionally be done as a separate step later) */ for(col = 1; col <= colMax; col++) { absvalue = exp(-FColScale[col]); if(absvalue < MIN_SCALAR) absvalue = MIN_SCALAR; if(absvalue > MAX_SCALAR) absvalue = MAX_SCALAR; if(!is_int(lp,col) || is_integerscaling(lp)) FColScale[col] = absvalue; else FColScale[col] = 1; } for(row = 0; row <= lp->rows; row++) { absvalue = exp(-FRowScale[row]); if(absvalue < MIN_SCALAR) absvalue = MIN_SCALAR; if(absvalue > MAX_SCALAR) absvalue = MAX_SCALAR; FRowScale[row] = absvalue; } /* free temporary memory */ FREE(RowSum); FREE(ColSum); FREE(RowCount); FREE(ColCount); FREE(residual_even); FREE(residual_odd); FREE(RowScalem2); FREE(ColScalem2); return(Result); }
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 ); }
/* Save a matrix column subset to a MatrixMarket formatted file, say to export the basis matrix for further numerical analysis. If colndx is NULL, then the full constraint matrix is assumed. */ MYBOOL REPORT_mat_mmsave(lprec *lp, char *filename, int *colndx, MYBOOL includeOF, char *infotext) { int n, m, nz, i, j, k, kk; MATrec *mat = lp->matA; MM_typecode matcode; FILE *output = stdout; MYBOOL ok; LPSREAL *acol = NULL; int *nzlist = NULL; /* Open file */ ok = (MYBOOL) ((filename == NULL) || ((output = fopen(filename,"w")) != NULL)); if(!ok) return(ok); if((filename == NULL) && (lp->outstream != NULL)) output = lp->outstream; /* Compute column and non-zero counts */ if(colndx == lp->var_basic) { if(!lp->basis_valid) return( FALSE ); m = lp->rows; } else if(colndx != NULL) m = colndx[0]; else m = lp->columns; n = lp->rows; nz = 0; for(j = 1; j <= m; j++) { k = (colndx == NULL ? n + j : colndx[j]); if(k > n) { k -= lp->rows; nz += mat_collength(mat, k); if(includeOF && is_OF_nz(lp, k)) nz++; } else nz++; } kk = 0; if(includeOF) { n++; /* Row count */ kk++; /* Row index offset */ } /* Initialize */ mm_initialize_typecode(&matcode); mm_set_matrix(&matcode); mm_set_coordinate(&matcode); mm_set_real(&matcode); mm_write_banner(output, matcode); mm_write_mtx_crd_size(output, n+kk, m, nz+(colndx == lp->var_basic ? 1 : 0)); /* Allocate working arrays for sparse column storage */ allocREAL(lp, &acol, n+2, FALSE); allocINT(lp, &nzlist, n+2, FALSE); /* Write the matrix non-zero values column-by-column. NOTE: matrixMarket files use 1-based indeces, i.e. first row of a vector has index 1, not 0. */ if(infotext != NULL) { fprintf(output, "%%\n"); fprintf(output, "%% %s\n", infotext); fprintf(output, "%%\n"); } if(includeOF && (colndx == lp->var_basic)) fprintf(output, "%d %d %g\n", 1, 1, 1.0); for(j = 1; j <= m; j++) { k = (colndx == NULL ? lp->rows + j : colndx[j]); if(k == 0) continue; nz = obtain_column(lp, k, acol, nzlist, NULL); for(i = 1; i <= nz; i++) { if(!includeOF && (nzlist[i] == 0)) continue; fprintf(output, "%d %d %g\n", nzlist[i]+kk, j+kk, acol[i]); } } fprintf(output, "%% End of MatrixMarket file\n"); /* Finish */ FREE(acol); FREE(nzlist); fclose(output); 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 */