diff --git a/cmake/sources-python.cmake b/cmake/sources-python.cmake index b89ee37586..99a896c38d 100644 --- a/cmake/sources-python.cmake +++ b/cmake/sources-python.cmake @@ -199,6 +199,7 @@ set(highs_sources_python highs/lp_data/HighsModelUtils.cpp highs/lp_data/HighsOptions.cpp highs/lp_data/HighsRanging.cpp + highs/lp_data/HighsReformulation.cpp highs/lp_data/HighsSolution.cpp highs/lp_data/HighsSolutionDebug.cpp highs/lp_data/HighsSolve.cpp diff --git a/cmake/sources.cmake b/cmake/sources.cmake index 0167c8075d..3e92a8b7d1 100644 --- a/cmake/sources.cmake +++ b/cmake/sources.cmake @@ -352,6 +352,7 @@ set(highs_sources lp_data/HighsModelUtils.cpp lp_data/HighsOptions.cpp lp_data/HighsRanging.cpp + lp_data/HighsReformulation.cpp lp_data/HighsSolution.cpp lp_data/HighsSolutionDebug.cpp lp_data/HighsSolve.cpp diff --git a/highs/Highs.h b/highs/Highs.h index 51c59115f5..088bcd96c4 100644 --- a/highs/Highs.h +++ b/highs/Highs.h @@ -1771,8 +1771,6 @@ class Highs { HighsStatus lpKktCheck(const HighsLp& lp, const std::string& message = ""); HighsStatus invertRequirementError(std::string method_name) const; - HighsStatus handleInfCost(); - void restoreInfCost(HighsStatus& return_status); HighsStatus optionChangeAction(); HighsStatus userScale(HighsUserScaleData& data); @@ -1800,6 +1798,13 @@ class Highs { bool tryPdlpCleanup(HighsInt& pdlp_cleanup_iteration_limit, const HighsInfo& presolved_lp_info) const; + + // Reformulation methods + HighsStatus doReformulation(); + void undoReformulation(HighsStatus& optimize_status); + + HighsStatus handleInfiniteCost(); + void restoreInfiniteCost(HighsStatus& optimize_status); }; // Start of deprecated methods not in the Highs class diff --git a/highs/lp_data/HStruct.h b/highs/lp_data/HStruct.h index 5c12c017c0..cb29d1c2e6 100644 --- a/highs/lp_data/HStruct.h +++ b/highs/lp_data/HStruct.h @@ -119,6 +119,8 @@ struct HighsLpMods { std::vector save_inf_cost_variable_upper; void clear(); + void clearInfiniteCostRecord(); + void clearSemiVariableRecord(); bool isClear(); }; diff --git a/highs/lp_data/Highs.cpp b/highs/lp_data/Highs.cpp index 74403945cb..24df2dbc64 100644 --- a/highs/lp_data/Highs.cpp +++ b/highs/lp_data/Highs.cpp @@ -977,9 +977,16 @@ HighsStatus Highs::run() { HighsStatus Highs::optimizeHighs() { // Level 1 of Highs::run() // - // Move the "mods" to here - return this->multi_linear_objective_.size() ? this->multiobjectiveSolve() - : this->optimizeModel(); + HighsStatus optimize_status = doReformulation(); + if (optimize_status == HighsStatus::kError) return optimize_status; + + optimize_status = this->multi_linear_objective_.size() + ? this->multiobjectiveSolve() + : this->optimizeModel(); + + undoReformulation(optimize_status); + + return optimize_status; } HighsStatus Highs::optimizeLp() { @@ -1088,26 +1095,7 @@ HighsStatus Highs::calledOptimizeModel() { // Set undo_mods = false so that returnFromOptimizeModel() doesn't undo any // mods that must be preserved - such as when solving a MIP node bool undo_mods = false; - if (model_.lp_.has_infinite_cost_) { - // If the model has infinite costs, then try to remove them. The - // return_status indicates the success of this operation and, if - // it's unsuccessful, the model will not have been modified and - // optimizeModel() can simply return an error with model status - // HighsModelStatus::kUnknown - assert(model_.lp_.hasInfiniteCost(options_.infinite_cost)); - HighsStatus return_status = handleInfCost(); - if (return_status != HighsStatus::kOk) { - assert(return_status == HighsStatus::kError); - setHighsModelStatusAndClearSolutionAndBasis(HighsModelStatus::kUnknown); - return return_status; - } - // Modifications have been performed, so must be undone before - // this call to optimizeModel() returns - assert(!model_.lp_.has_infinite_cost_); - undo_mods = true; - } else { - assert(!model_.lp_.hasInfiniteCost(options_.infinite_cost)); - } + assert(!model_.lp_.has_infinite_cost_); // Ensure that all vectors in the model have exactly the right size exactResizeModel(); @@ -4727,12 +4715,8 @@ HighsStatus Highs::returnFromOptimizeModel(const HighsStatus run_return_status, called_return_from_optimize_model = true; if (undo_mods) { - // Restore any infinite costs - this->restoreInfCost(return_status); - // Unapply any modifications that have not yet been unapplied this->model_.lp_.unapplyMods(); - // undo_mods = false; } // Unless solved as a MIP, report on the solution diff --git a/highs/lp_data/HighsInterface.cpp b/highs/lp_data/HighsInterface.cpp index b6bf051ae2..cbe242d47c 100644 --- a/highs/lp_data/HighsInterface.cpp +++ b/highs/lp_data/HighsInterface.cpp @@ -3069,139 +3069,12 @@ HighsStatus Highs::invertRequirementError(std::string method_name) const { return HighsStatus::kError; } -HighsStatus Highs::handleInfCost() { - HighsLp& lp = this->model_.lp_; - if (!lp.has_infinite_cost_) return HighsStatus::kOk; - HighsLpMods& mods = lp.mods_; - double inf_cost = this->options_.infinite_cost; - for (HighsInt k = 0; k < 2; k++) { - // Pass twice: first checking that infinite costs can be handled, - // then handling them, so that model is unmodified if infinite - // costs cannot be handled - for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) { - double cost = lp.col_cost_[iCol]; - if (cost > -inf_cost && cost < inf_cost) continue; - double lower = lp.col_lower_[iCol]; - double upper = lp.col_upper_[iCol]; - if (lp.isMip()) { - if (lp.integrality_[iCol] == HighsVarType::kInteger) { - lower = std::ceil(lower); - upper = std::floor(upper); - } - } - if (cost <= -inf_cost) { - if (lp.sense_ == ObjSense::kMinimize) { - // Minimizing with -inf cost so try to fix at upper bound - if (upper < kHighsInf) { - if (k) lp.col_lower_[iCol] = upper; - } else { - highsLogUser(options_.log_options, HighsLogType::kError, - "Cannot minimize with a cost on variable %d of %g and " - "upper bound of %g\n", - int(iCol), cost, upper); - return HighsStatus::kError; - } - } else { - // Maximizing with -inf cost so try to fix at lower bound - if (lower > -kHighsInf) { - if (k) lp.col_upper_[iCol] = lower; - } else { - highsLogUser(options_.log_options, HighsLogType::kError, - "Cannot maximize with a cost on variable %d of %g and " - "lower bound of %g\n", - int(iCol), cost, lower); - return HighsStatus::kError; - } - } - } else { - if (lp.sense_ == ObjSense::kMinimize) { - // Minimizing with inf cost so try to fix at lower bound - if (lower > -kHighsInf) { - if (k) lp.col_upper_[iCol] = lower; - } else { - highsLogUser(options_.log_options, HighsLogType::kError, - "Cannot minimize with a cost on variable %d of %g and " - "lower bound of %g\n", - int(iCol), cost, lower); - return HighsStatus::kError; - } - } else { - // Maximizing with inf cost so try to fix at upper bound - if (upper < kHighsInf) { - if (k) lp.col_lower_[iCol] = upper; - } else { - highsLogUser(options_.log_options, HighsLogType::kError, - "Cannot maximize with a cost on variable %d of %g and " - "upper bound of %g\n", - int(iCol), cost, upper); - return HighsStatus::kError; - } - } - } - if (k) { - mods.save_inf_cost_variable_index.push_back(iCol); - mods.save_inf_cost_variable_cost.push_back(cost); - mods.save_inf_cost_variable_lower.push_back(lower); - mods.save_inf_cost_variable_upper.push_back(upper); - lp.col_cost_[iCol] = 0; - } - } - } - // Infinite costs have been removed, but their presence in the - // original model is known from mods.save_inf_cost_variable_*, so - // set lp.has_infinite_cost_ to be false to avoid assert when run() - // is called using copy of model in MIP solver (See #1446) - lp.has_infinite_cost_ = false; - - return HighsStatus::kOk; -} - HighsStatus Highs::optionChangeAction() { if (this->iis_.valid_ && options_.iis_strategy != this->iis_.strategy_) this->iis_.clear(); return HighsStatus::kOk; } -void Highs::restoreInfCost(HighsStatus& return_status) { - HighsLp& lp = this->model_.lp_; - HighsBasis& basis = this->basis_; - HighsLpMods& mods = lp.mods_; - HighsInt num_inf_cost = mods.save_inf_cost_variable_index.size(); - if (num_inf_cost <= 0) return; - assert(num_inf_cost); - for (HighsInt ix = 0; ix < num_inf_cost; ix++) { - HighsInt iCol = mods.save_inf_cost_variable_index[ix]; - double cost = mods.save_inf_cost_variable_cost[ix]; - double lower = mods.save_inf_cost_variable_lower[ix]; - double upper = mods.save_inf_cost_variable_upper[ix]; - double value = solution_.value_valid ? solution_.col_value[iCol] : 0; - if (basis.valid) { - assert(basis.col_status[iCol] != HighsBasisStatus::kBasic); - if (lp.col_lower_[iCol] == lower) { - basis.col_status[iCol] = HighsBasisStatus::kLower; - } else { - basis.col_status[iCol] = HighsBasisStatus::kUpper; - } - } - assert(lp.col_cost_[iCol] == 0); - if (value) this->info_.objective_function_value += value * cost; - lp.col_cost_[iCol] = cost; - lp.col_lower_[iCol] = lower; - lp.col_upper_[iCol] = upper; - } - // Infinite costs have been reintroduced, so reset to true the flag - // that was set false in Highs::handleInfCost() (See #1446) - lp.has_infinite_cost_ = true; - - if (this->model_status_ == HighsModelStatus::kInfeasible) { - // Model is infeasible with the infinite cost variables fixed at - // appropriate values, so model status cannot be determined - this->model_status_ = HighsModelStatus::kUnknown; - setHighsModelStatusAndClearSolutionAndBasis(this->model_status_); - return_status = highsStatusFromHighsModelStatus(model_status_); - } -} - HighsStatus Highs::userScale(HighsUserScaleData& data) { if (!options_.user_objective_scale && !options_.user_bound_scale) return HighsStatus::kOk; diff --git a/highs/lp_data/HighsLp.cpp b/highs/lp_data/HighsLp.cpp index ebcc3c2a3b..e89f5a4237 100644 --- a/highs/lp_data/HighsLp.cpp +++ b/highs/lp_data/HighsLp.cpp @@ -491,7 +491,14 @@ void HighsLp::unapplyMods() { this->mods_.clear(); } -void HighsLpMods::clear() { +void HighsLpMods::clearInfiniteCostRecord() { + this->save_inf_cost_variable_index.clear(); + this->save_inf_cost_variable_cost.clear(); + this->save_inf_cost_variable_lower.clear(); + this->save_inf_cost_variable_upper.clear(); +} + +void HighsLpMods::clearSemiVariableRecord() { this->save_non_semi_variable_index.clear(); this->save_inconsistent_semi_variable_index.clear(); this->save_inconsistent_semi_variable_lower_bound_value.clear(); @@ -501,10 +508,11 @@ void HighsLpMods::clear() { this->save_relaxed_semi_variable_lower_bound_value.clear(); this->save_tightened_semi_variable_upper_bound_index.clear(); this->save_tightened_semi_variable_upper_bound_value.clear(); - this->save_inf_cost_variable_index.clear(); - this->save_inf_cost_variable_cost.clear(); - this->save_inf_cost_variable_lower.clear(); - this->save_inf_cost_variable_upper.clear(); +} + +void HighsLpMods::clear() { + this->clearInfiniteCostRecord(); + this->clearSemiVariableRecord(); } bool HighsLpMods::isClear() { diff --git a/highs/lp_data/HighsReformulation.cpp b/highs/lp_data/HighsReformulation.cpp new file mode 100644 index 0000000000..a7681ff1f7 --- /dev/null +++ b/highs/lp_data/HighsReformulation.cpp @@ -0,0 +1,174 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* This file is part of the HiGHS linear optimization suite */ +/* */ +/* Available as open-source under the MIT License */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/**@file lp_data/HighsReformulation.cpp + * @brief + */ +// #include + +#include "Highs.h" +// #include "lp_data/HighsLpUtils.h" +#include "lp_data/HighsModelUtils.h" +// #include "mip/HighsMipSolver.h" // For getGapString +// #include "model/HighsHessianUtils.h" +// #include "simplex/HSimplex.h" +// #include "util/HighsMatrixUtils.h" +// #include "util/HighsSort.h" + +HighsStatus Highs::doReformulation() { + HighsStatus status = HighsStatus::kOk; + assert(model_.lp_.has_infinite_cost_ == + model_.lp_.hasInfiniteCost(options_.infinite_cost)); + if (model_.lp_.has_infinite_cost_) { + // If the model has infinite costs, then try to remove them. The + // status indicates the success of this operation and, if + // it's unsuccessful, the model will not have been modified. + status = handleInfiniteCost(); + if (status != HighsStatus::kOk) { + assert(status == HighsStatus::kError); + setHighsModelStatusAndClearSolutionAndBasis(HighsModelStatus::kUnknown); + return status; + } + assert(!model_.lp_.has_infinite_cost_); + } else { + assert(!model_.lp_.hasInfiniteCost(options_.infinite_cost)); + } + return status; +} + +void Highs::undoReformulation(HighsStatus& optimize_status) { + this->restoreInfiniteCost(optimize_status); + // this->model_.lp_.unapplyMods(); +} + +HighsStatus Highs::handleInfiniteCost() { + HighsLp& lp = this->model_.lp_; + if (!lp.has_infinite_cost_) return HighsStatus::kOk; + HighsLpMods& mods = lp.mods_; + double inf_cost = this->options_.infinite_cost; + for (HighsInt k = 0; k < 2; k++) { + // Pass twice: first checking that infinite costs can be handled, + // then handling them, so that model is unmodified if infinite + // costs cannot be handled + for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) { + double cost = lp.col_cost_[iCol]; + if (cost > -inf_cost && cost < inf_cost) continue; + double lower = lp.col_lower_[iCol]; + double upper = lp.col_upper_[iCol]; + if (lp.isMip()) { + if (lp.integrality_[iCol] == HighsVarType::kInteger) { + lower = std::ceil(lower); + upper = std::floor(upper); + } + } + if (cost <= -inf_cost) { + if (lp.sense_ == ObjSense::kMinimize) { + // Minimizing with -inf cost so try to fix at upper bound + if (upper < kHighsInf) { + if (k) lp.col_lower_[iCol] = upper; + } else { + highsLogUser(options_.log_options, HighsLogType::kError, + "Cannot minimize with a cost on variable %d of %g and " + "upper bound of %g\n", + int(iCol), cost, upper); + return HighsStatus::kError; + } + } else { + // Maximizing with -inf cost so try to fix at lower bound + if (lower > -kHighsInf) { + if (k) lp.col_upper_[iCol] = lower; + } else { + highsLogUser(options_.log_options, HighsLogType::kError, + "Cannot maximize with a cost on variable %d of %g and " + "lower bound of %g\n", + int(iCol), cost, lower); + return HighsStatus::kError; + } + } + } else { + if (lp.sense_ == ObjSense::kMinimize) { + // Minimizing with inf cost so try to fix at lower bound + if (lower > -kHighsInf) { + if (k) lp.col_upper_[iCol] = lower; + } else { + highsLogUser(options_.log_options, HighsLogType::kError, + "Cannot minimize with a cost on variable %d of %g and " + "lower bound of %g\n", + int(iCol), cost, lower); + return HighsStatus::kError; + } + } else { + // Maximizing with inf cost so try to fix at upper bound + if (upper < kHighsInf) { + if (k) lp.col_lower_[iCol] = upper; + } else { + highsLogUser(options_.log_options, HighsLogType::kError, + "Cannot maximize with a cost on variable %d of %g and " + "upper bound of %g\n", + int(iCol), cost, upper); + return HighsStatus::kError; + } + } + } + if (k) { + mods.save_inf_cost_variable_index.push_back(iCol); + mods.save_inf_cost_variable_cost.push_back(cost); + mods.save_inf_cost_variable_lower.push_back(lower); + mods.save_inf_cost_variable_upper.push_back(upper); + lp.col_cost_[iCol] = 0; + } + } + } + // Infinite costs have been removed, but their presence in the + // original model is known from mods.save_inf_cost_variable_*, so + // set lp.has_infinite_cost_ to be false to avoid assert when run() + // is called using copy of model in MIP solver (See #1446) + lp.has_infinite_cost_ = false; + + return HighsStatus::kOk; +} + +void Highs::restoreInfiniteCost(HighsStatus& optimize_status) { + HighsLp& lp = this->model_.lp_; + HighsBasis& basis = this->basis_; + HighsLpMods& mods = lp.mods_; + HighsInt num_inf_cost = mods.save_inf_cost_variable_index.size(); + if (num_inf_cost <= 0) return; + assert(num_inf_cost); + for (HighsInt ix = 0; ix < num_inf_cost; ix++) { + HighsInt iCol = mods.save_inf_cost_variable_index[ix]; + double cost = mods.save_inf_cost_variable_cost[ix]; + double lower = mods.save_inf_cost_variable_lower[ix]; + double upper = mods.save_inf_cost_variable_upper[ix]; + double value = solution_.value_valid ? solution_.col_value[iCol] : 0; + if (basis.valid) { + assert(basis.col_status[iCol] != HighsBasisStatus::kBasic); + if (lp.col_lower_[iCol] == lower) { + basis.col_status[iCol] = HighsBasisStatus::kLower; + } else { + basis.col_status[iCol] = HighsBasisStatus::kUpper; + } + } + assert(lp.col_cost_[iCol] == 0); + if (value) this->info_.objective_function_value += value * cost; + lp.col_cost_[iCol] = cost; + lp.col_lower_[iCol] = lower; + lp.col_upper_[iCol] = upper; + } + // Infinite costs have been reintroduced, so reset to true the flag + // that was set false in Highs::handleInfiniteCost() (See #1446) + lp.has_infinite_cost_ = true; + mods.clearInfiniteCostRecord(); + + if (this->model_status_ == HighsModelStatus::kInfeasible) { + // Model is infeasible with the infinite cost variables fixed at + // appropriate values, so model status cannot be determined + this->model_status_ = HighsModelStatus::kUnknown; + setHighsModelStatusAndClearSolutionAndBasis(this->model_status_); + optimize_status = highsStatusFromHighsModelStatus(model_status_); + } +} diff --git a/highs/meson.build b/highs/meson.build index b442310708..5390fd3c37 100644 --- a/highs/meson.build +++ b/highs/meson.build @@ -217,6 +217,7 @@ _srcs = [ 'lp_data/HighsModelUtils.cpp', 'lp_data/HighsOptions.cpp', 'lp_data/HighsRanging.cpp', + 'lp_data/HighsReformulation.cpp', 'lp_data/HighsSolution.cpp', 'lp_data/HighsSolutionDebug.cpp', 'lp_data/HighsSolve.cpp', diff --git a/highs/presolve/HPresolve.cpp b/highs/presolve/HPresolve.cpp index 5bc361f496..12ece25523 100644 --- a/highs/presolve/HPresolve.cpp +++ b/highs/presolve/HPresolve.cpp @@ -2889,35 +2889,47 @@ void HPresolve::toCSC(std::vector& Aval, std::vector& Aindex, Aval[pos] = Avalue[i]; Aindex[pos] = Arow[i]; } +#ifndef NDEBUG + checkCSC(Aval, Aindex, Astart); +#endif } -void HPresolve::toCSR(std::vector& ARval, - std::vector& ARindex, - std::vector& ARstart) { - // set up the row starts using the row size array +#ifndef NDEBUG +// Segfault #2748 occurs in highsSparseTranspose, so this method is +// used to ensure that index and value are of the correct size, that +// the entries of index are in bounds, and that there there are no +// explicit zeros in value +void HPresolve::checkCSC(const std::vector& Aval, + const std::vector& Aindex, + const std::vector& Astart) const { + size_t numcol = colsize.size(); size_t numrow = rowsize.size(); - ARstart.resize(numrow + 1); - HighsInt nnz = 0; - for (size_t i = 0; i != numrow; ++i) { - ARstart[i] = nnz; - nnz += rowsize[i]; - } - ARstart[numrow] = nnz; - - // now setup the entries of the CSC matrix - // we reuse the colsize array to count down to zero - // for determining the position of each nonzero - ARval.resize(nnz); - ARindex.resize(nnz); - for (HighsInt i = 0; i != nnz; ++i) { - if (Avalue[i] == 0.0) continue; - HighsInt pos = ARstart[Arow[i] + 1] - rowsize[Arow[i]]; - --rowsize[Arow[i]]; - assert(rowsize[Arow[i]] >= 0); - ARval[pos] = Avalue[i]; - ARindex[pos] = Acol[i]; + assert(Astart.size() == numcol + 1); + HighsInt nnz = Astart[numcol]; + bool Aindex_size_ok = Aindex.size() == static_cast(nnz); + if (!Aindex_size_ok) { + printf("HPresolve::checkCSC %d = Aindex.size() != nnz = %d\n", + int(Aindex.size()), int(nnz)); + assert(Aindex_size_ok); + } + bool Aval_size_ok = Aval.size() == static_cast(nnz); + if (!Aval_size_ok) { + printf("HPresolve::checkCSC %d = Aval.size() != nnz = %d\n", + int(Aval.size()), int(nnz)); + assert(Aval_size_ok); + } + for (size_t i = 0; i != nnz; ++i) { + if (!Aval[i]) + printf("HPresolve::checkCSC Aval[%d/%d] is explicit zero\n", int(i), + int(nnz)); + assert(Aval[i]); + bool index_ok = 0 <= Aindex[i] && Aindex[i] < static_cast(numrow); + if (!index_ok) + printf("HPresolve::checkCSC row index %d is out of bounds [0, %d]\n", + static_cast(i), static_cast(numrow)); } } +#endif HPresolve::Result HPresolve::doubletonEq(HighsPostsolveStack& postsolve_stack, HighsInt row, diff --git a/highs/presolve/HPresolve.h b/highs/presolve/HPresolve.h index d548d320d8..45ae39a191 100644 --- a/highs/presolve/HPresolve.h +++ b/highs/presolve/HPresolve.h @@ -265,8 +265,11 @@ class HPresolve { void toCSC(std::vector& Aval, std::vector& Aindex, std::vector& Astart); - void toCSR(std::vector& ARval, std::vector& ARindex, - std::vector& ARstart); +#ifndef NDEBUG + void checkCSC(const std::vector& Aval, + const std::vector& Aindex, + const std::vector& Astart) const; +#endif void getRowPositions(HighsInt row, std::vector& myrowpositions) const; diff --git a/highs/util/HighsUtils.cpp b/highs/util/HighsUtils.cpp index 9dc4fa6a20..21371617c4 100644 --- a/highs/util/HighsUtils.cpp +++ b/highs/util/HighsUtils.cpp @@ -68,6 +68,27 @@ void highsSparseTranspose(HighsInt numRow, HighsInt numCol, std::vector iwork(numRow, 0); ARstart.resize(numRow + 1, 0); HighsInt AcountX = Aindex.size(); + // Segfault #2748 occurs in highsSparseTranspose, and only oddity in + // highsSparseTranspose is that it uses AcountX = Aindex.size(); to + // determine the number of nonzeros in the matrix - and hence resize + // ARindex and ARvalue, rather than Astart[numCol] - although both + // values should be the same. So, if the size of Aindex is + // incorrect, the size of ARindex and ARvalue is incorrect. However, + // it's only conceivable that AcountX is larger than Astart[numCol] + // - otherwise a segfault would have been expected earlier - so size + // of ARindex and ARvalue would be larger than necessary, which + // wouldn't lead to a segfault. + HighsInt checkAcountX = Astart[numCol]; + if (AcountX != checkAcountX) { +#ifndef NDEBUG + printf("highsSparseTranspose: %d = AcountX != checkAcountX = %d\n", + int(AcountX), int(checkAcountX)); +#endif + assert(AcountX == checkAcountX); + } + // Use Astart[numCol] rather than Aindex.size() as number of + // nonzeros + AcountX = Astart[numCol]; ARindex.resize(AcountX); ARvalue.resize(AcountX); for (HighsInt k = 0; k < AcountX; k++) {