/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* */ /* This file is part of the class library */ /* SoPlex --- the Sequential object-oriented simPlex. */ /* */ /* Copyright 1996-2022 Zuse Institute Berlin */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ /* */ /* http://www.apache.org/licenses/LICENSE-2.0 */ /* */ /* Unless required by applicable law or agreed to in writing, software */ /* distributed under the License is distributed on an "AS IS" BASIS, */ /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ /* See the License for the specific language governing permissions and */ /* limitations under the License. */ /* */ /* You should have received a copy of the Apache-2.0 license */ /* along with SoPlex; see the file LICENSE. If not email to soplex@zib.de. */ /* */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /**@file spxleastsqsc.hpp * @brief LP least squares scaling. */ #include #include #include "soplex/spxout.h" #include "soplex/basevectors.h" #include "soplex/svsetbase.h" #include "soplex/svectorbase.h" #include "soplex/ssvectorbase.h" #include namespace soplex { /* update scaling VectorBase */ template static void updateScale( const SSVectorBase vecnnzeroes, const SSVectorBase resnvec, SSVectorBase& tmpvec, SSVectorBase*& psccurr, SSVectorBase*& pscprev, R qcurr, R qprev, R eprev1, R eprev2) { assert(psccurr != NULL); assert(pscprev != NULL); assert(qcurr * qprev != 0.0); R fac = -(eprev1 * eprev2); SSVectorBase* pssv; *pscprev -= *psccurr; if(isZero(fac)) (*pscprev).clear(); else *pscprev *= fac; *pscprev += tmpvec.assignPWproduct4setup(resnvec, vecnnzeroes); *pscprev *= 1.0 / (qcurr * qprev); *pscprev += *psccurr; /* swap pointers */ pssv = psccurr; psccurr = pscprev; pscprev = pssv; } /* update scaling VectorBase after main loop */ template static void updateScaleFinal( const SSVectorBase vecnnzeroes, const SSVectorBase resnvec, SSVectorBase& tmpvec, SSVectorBase*& psccurr, SSVectorBase*& pscprev, R q, R eprev1, R eprev2) { assert(q != 0); assert(psccurr != NULL); assert(pscprev != NULL); R fac = -(eprev1 * eprev2); *pscprev -= *psccurr; if(isZero(fac)) (*pscprev).clear(); else *pscprev *= fac; *pscprev += tmpvec.assignPWproduct4setup(resnvec, vecnnzeroes); *pscprev *= 1.0 / q; *pscprev += *psccurr; psccurr = pscprev; } /* update residual VectorBase */ template static inline void updateRes( const SVSetBase facset, const SSVectorBase resvecprev, SSVectorBase& resvec, SSVectorBase& tmpvec, R eprev, R qcurr) { assert(qcurr != 0.0); if(isZero(eprev)) resvec.clear(); else resvec *= eprev; int dummy1 = 0; int dummy2 = 0; tmpvec.assign2product4setup(facset, resvecprev, 0, 0, dummy1, dummy2); tmpvec.setup(); resvec += tmpvec; resvec *= (-1.0 / qcurr); resvec.setup(); } /* initialize constant vectors and matrices */ template static void initConstVecs( const SVSetBase* vecset, SVSetBase& facset, SSVectorBase& veclogs, SSVectorBase& vecnnzinv) { assert(vecset != NULL); const int nvec = vecset->num(); for(int k = 0; k < nvec; ++k) { R logsum = 0.0; int nnz = 0; // get kth row or column of LP const SVectorBase& lpvec = (*vecset)[k]; const int size = lpvec.size(); for(int i = 0; i < size; ++i) { const R a = lpvec.value(i); if(!isZero(a)) { logsum += log2(double(spxAbs(a))); // todo spxLog2? nnz++; } } R nnzinv; if(nnz > 0) { nnzinv = 1.0 / nnz; } else { /* all-0 entries */ logsum = 1.0; nnzinv = 1.0; } veclogs.add(k, logsum); vecnnzinv.add(k, nnzinv); /* create new VectorBase for facset */ SVectorBase& vecnew = (*(facset.create(nnz))); for(int i = 0; i < size; ++i) { if(!isZero(lpvec.value(i))) vecnew.add(lpvec.index(i), nnzinv); } vecnew.sort(); } assert(veclogs.isSetup()); assert(vecnnzinv.isSetup()); } /* return name of scaler */ static inline const char* makename() { return "Least squares"; } template SPxLeastSqSC::SPxLeastSqSC() : SPxScaler(makename(), false, false) {} template SPxLeastSqSC::SPxLeastSqSC(const SPxLeastSqSC& old) : SPxScaler(old), acrcydivisor(old.acrcydivisor), maxrounds(old.maxrounds) {} template SPxLeastSqSC& SPxLeastSqSC::operator=(const SPxLeastSqSC& rhs) { if(this != &rhs) { SPxScaler::operator=(rhs); } return *this; } template void SPxLeastSqSC::setRealParam(R param, const char* name) { assert(param >= 1.0); acrcydivisor = param; } template void SPxLeastSqSC::setIntParam(int param, const char* name) { assert(param >= 0); maxrounds = param; } // todo refactor this method. Has no abstraction and is too long template void SPxLeastSqSC::scale(SPxLPBase& lp, bool persistent) { MSG_INFO1((*this->spxout), (*this->spxout) << "Least squares LP scaling" << (persistent ? " (persistent)" : "") << std::endl;) this->setup(lp); const int nrows = lp.nRows(); const int ncols = lp.nCols(); const int lpnnz = lp.nNzos(); // is contraints matrix empty? //todo don't create the scaler in this case! if(lpnnz == 0) { // to keep the invariants, we still need to call this method this->applyScaling(lp); return; } assert(nrows > 0 && ncols > 0 && lpnnz > 0); /* constant factor matrices; * in Curtis-Reid article * facnrows equals E^T M^(-1) * facncols equals E N^(-1) * */ SVSetBase facnrows(nrows, nrows, 1.1, 1.2); SVSetBase facncols(ncols, ncols, 1.1, 1.2); /* column scaling factor vectors */ SSVectorBase colscale1(ncols); SSVectorBase colscale2(ncols); /* row scaling factor vectors */ SSVectorBase rowscale1(nrows); SSVectorBase rowscale2(nrows); /* residual vectors */ SSVectorBase resnrows(nrows); SSVectorBase resncols(ncols); /* vectors to store temporary values */ SSVectorBase tmprows(nrows); SSVectorBase tmpcols(ncols); /* vectors storing the row and column sums (respectively) of logarithms of *(absolute values of) non-zero elements of left hand matrix of LP */ SSVectorBase rowlogs(nrows); SSVectorBase collogs(ncols); /* vectors storing the inverted number of non-zeros in each row and column *(respectively) of left hand matrix of LP */ SSVectorBase rownnzinv(nrows); SSVectorBase colnnzinv(ncols); /* VectorBase pointers */ SSVectorBase* csccurr = &colscale1; SSVectorBase* cscprev = &colscale2; SSVectorBase* rsccurr = &rowscale1; SSVectorBase* rscprev = &rowscale2; MSG_INFO2((*this->spxout), (*this->spxout) << "before scaling:" << " min= " << lp.minAbsNzo() << " max= " << lp.maxAbsNzo() << " col-ratio= " << this->maxColRatio(lp) << " row-ratio= " << this->maxRowRatio(lp) << std::endl;) /* initialize scalars, vectors and matrices */ assert(acrcydivisor > 0.0); const R smax = lpnnz / acrcydivisor; R qcurr = 1.0; R qprev = 0.0; std::array eprev; eprev.fill(0.0); initConstVecs(lp.rowSet(), facnrows, rowlogs, rownnzinv); initConstVecs(lp.colSet(), facncols, collogs, colnnzinv); assert(tmprows.isSetup() && tmpcols.isSetup()); assert(rowscale1.isSetup() && rowscale2.isSetup()); assert(colscale1.isSetup() && colscale2.isSetup()); // compute first residual vector r0 int dummy1 = 0; int dummy2 = 0; resncols = collogs - tmpcols.assign2product4setup(facnrows, rowlogs, 0, 0, dummy1, dummy2); resncols.setup(); resnrows.setup(); rowscale1.assignPWproduct4setup(rownnzinv, rowlogs); rowscale2 = rowscale1; R scurr = resncols * tmpcols.assignPWproduct4setup(colnnzinv, resncols); int k; /* conjugate gradient loop */ for(k = 0; k < maxrounds; ++k) { const R sprev = scurr; // termination criterion met? if(scurr < smax) break; // is k even? if((k % 2) == 0) { // not in first iteration? if(k != 0) // true, then update row scaling factor vector updateScale(rownnzinv, resnrows, tmprows, rsccurr, rscprev, qcurr, qprev, eprev[1], eprev[2]); updateRes(facncols, resncols, resnrows, tmprows, eprev[0], qcurr); scurr = resnrows * tmprows.assignPWproduct4setup(resnrows, rownnzinv); } else // k is odd { // update column scaling factor vector updateScale(colnnzinv, resncols, tmpcols, csccurr, cscprev, qcurr, qprev, eprev[1], eprev[2]); updateRes(facnrows, resnrows, resncols, tmpcols, eprev[0], qcurr); scurr = resncols * tmpcols.assignPWproduct4setup(resncols, colnnzinv); } // shift eprev entries one to the right for(unsigned l = 2; l > 0; --l) eprev[l] = eprev[l - 1]; assert(isNotZero(sprev)); eprev[0] = (qcurr * scurr) / sprev; const R tmp = qcurr; qcurr = 1.0 - eprev[0]; qprev = tmp; } if(k > 0 && (k % 2) == 0) { // update column scaling factor vector updateScaleFinal(colnnzinv, resncols, tmpcols, csccurr, cscprev, qprev, eprev[1], eprev[2]); } else if(k > 0) { // update row scaling factor vector updateScaleFinal(rownnzinv, resnrows, tmprows, rsccurr, rscprev, qprev, eprev[1], eprev[2]); } /* compute actual scaling factors */ const SSVectorBase& rowscale = *rsccurr; const SSVectorBase& colscale = *csccurr; DataArray& colscaleExp = *this->m_activeColscaleExp; DataArray& rowscaleExp = *this->m_activeRowscaleExp; for(k = 0; k < nrows; ++k) rowscaleExp[k] = -int(rowscale[k] + ((rowscale[k] >= 0.0) ? (+0.5) : (-0.5))); for(k = 0; k < ncols; ++k) colscaleExp[k] = -int(colscale[k] + ((colscale[k] >= 0.0) ? (+0.5) : (-0.5))); // scale this->applyScaling(lp); MSG_INFO3((*this->spxout), (*this->spxout) << "Row scaling min= " << this->minAbsRowscale() << " max= " << this->maxAbsRowscale() << std::endl << "Col scaling min= " << this->minAbsColscale() << " max= " << this->maxAbsColscale() << std::endl;) MSG_INFO2((*this->spxout), (*this->spxout) << "after scaling: " << " min= " << lp.minAbsNzo(false) << " max= " << lp.maxAbsNzo(false) << " col-ratio= " << this->maxColRatio(lp) << " row-ratio= " << this->maxRowRatio(lp) << std::endl;) } } // namespace soplex