diff --git a/main/sccomp/source/solver/solver.cxx b/main/sccomp/source/solver/solver.cxx index a607b9e751..f7fee96ec5 100644 --- a/main/sccomp/source/solver/solver.cxx +++ b/main/sccomp/source/solver/solver.cxx @@ -40,6 +40,8 @@ #include #include #include +#include +#include #include @@ -332,14 +334,16 @@ void SAL_CALL SolverComponent::solve() aCellsHash[aCellAddr].reserve( nVariables + 1 ); // constraints: right hand side } - // set all variables to zero - //! store old values? - //! use old values as initial values? - std::vector::const_iterator aVarIter; - for ( aVarIter = aVariableCells.begin(); aVarIter != aVariableCells.end(); ++aVarIter ) + // Save the current values of the changing cells for use as a possible + // initial solution, then reset the cells to zero for model construction. + std::vector aInitValues(nVariables); + for (nVar = 0; nVar < nVariables; ++nVar) { - lcl_SetValue( mxDoc, *aVarIter, 0.0 ); + // read current value of the variable cell and store as initial value + aInitValues[nVar] = lcl_GetValue( mxDoc, aVariableCells[nVar] ); + lcl_SetValue( mxDoc, aVariableCells[nVar], 0.0 ); } + std::vector::const_iterator aVarIter; // read initial values from all dependent cells ScSolverCellHashMap::iterator aCellsIter; @@ -468,6 +472,117 @@ void SAL_CALL SolverComponent::solve() } } + // Try to combine complementary <= and >= rows with identical coefficients + // into a single ranged row 'R' with RANGE = upper - lower. + // We do this before building the column-wise matrix. When a pair is + // merged we zero out the coefficients of the removed row so it is + // ignored when building the sparse column representation. + // Allocate a row-indexed range array up-front (one entry per original row). + double* pRangeValues = new double[nRows]; + for (size_t i = 0; i < nRows; ++i) pRangeValues[i] = 0.0; + + // Two-pass approach: first collect best lower/upper per coefficient + // signature (exact bitwise signature). Second, convert compatibles to + // ranged rows. This avoids online erase/replace semantics and ensures + // decisions are based on the original model. + struct RowPair { size_t lowerIdx; double lowerVal; size_t upperIdx; double upperVal; }; + const size_t npos = static_cast(-1); + std::unordered_map< std::string, RowPair > rowMap; + rowMap.reserve(nRows * 2); + + // Pass 1: populate rowMap with tightest lower (max G) and tightest + // upper (min L) for each coefficient signature. + for (size_t i = 0; i < nRows; ++i) + { + char ti = pRowType[i]; + if ( ti != 'L' && ti != 'G' ) + continue; + + const char* data = reinterpret_cast(&pCompMatrix[i * nVariables]); + size_t len = (size_t)nVariables * sizeof(double); + std::string sig; + sig.assign(data, len); + + auto it = rowMap.find(sig); + if ( it == rowMap.end() ) + { + RowPair rp; rp.lowerIdx = npos; rp.upperIdx = npos; rp.lowerVal = 0.0; rp.upperVal = 0.0; + std::pair::iterator, bool> res = rowMap.insert(std::make_pair(sig, rp)); + it = res.first; + } + + RowPair &rp = it->second; + if ( ti == 'L' ) + { + double v = pRHS[i]; + if ( rp.upperIdx == npos || v < rp.upperVal ) + { + rp.upperIdx = i; + rp.upperVal = v; + } + } + else // 'G' + { + double v = pRHS[i]; + if ( rp.lowerIdx == npos || v > rp.lowerVal ) + { + rp.lowerIdx = i; + rp.lowerVal = v; + } + } + } + + // Pass 2: perform conversions for entries that have both bounds. + size_t nMergedRows = 0; + for (auto &kv : rowMap) + { + RowPair &rp = kv.second; + if ( rp.lowerIdx == npos || rp.upperIdx == npos ) + continue; + + size_t idxLower = rp.lowerIdx; + size_t idxUpper = rp.upperIdx; + double lower = rp.lowerVal; + double upper = rp.upperVal; + + if ( lower <= upper ) + { + // make upper the ranged row and mark lower as removed + pRowType[idxUpper] = 'R'; + pRHS[idxUpper] = upper; + pRangeValues[idxUpper] = upper - lower; + + pRowType[idxLower] = 'N'; + pRHS[idxLower] = 0.0; + + ++nMergedRows; + OSL_TRACE("Solver: merging rows %lu (G %.17g) and %lu (L %.17g) into ranged row %lu [%.17g, %.17g]\n", + static_cast(idxLower), lower, + static_cast(idxUpper), upper, + static_cast(idxUpper), lower, upper); + } + else + { + // invalid (contradictory) bounds: leave rows unchanged + OSL_TRACE("Solver: contradictory bounds for coeff-signature - lowerRow=%lu (%.17g) upperRow=%lu (%.17g); leaving rows unchanged\n", + static_cast(idxLower), lower, + static_cast(idxUpper), upper); + } + } + + // After Pass 2 produce a summary trace (rows merged, ranged rows created, + // rows eliminated). This is the default diagnostic; per-row traces are + // emitted above and can be enabled/disabled by adjusting trace levels. + int nRangeCount_tmp = 0; + int nEliminated = 0; + for (size_t i = 0; i < nRows; ++i) + { + if ( pRowType[i] == 'R' ) ++nRangeCount_tmp; + if ( pRowType[i] == 'N' ) ++nEliminated; + } + OSL_TRACE("Solver: %lu input rows, %d ranged rows created, %d rows eliminated, %lu merged pairs\n", + static_cast(nRows), nRangeCount_tmp, nEliminated, static_cast(nMergedRows)); + // Find non-zero coefficients, column-wise int* pMatrixBegin = new int[nVariables+1]; @@ -480,6 +595,8 @@ void SAL_CALL SolverComponent::solve() int nBegin = nMatrixPos; for (size_t nRow=0; nRow