/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* */ /* This file is part of the HiGHS linear optimization suite */ /* */ /* Written and engineered 2008-2022 at the University of Edinburgh */ /* */ /* Available as open-source under the MIT License */ /* */ /* Authors: Julian Hall, Ivet Galabova, Leona Gottwald and Michael */ /* Feldmeier */ /* */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /**@file lp_data/HighsRanging.cpp * @brief Compute LP ranging data for HiGHS */ #include "lp_data/HighsRanging.h" #include #include #include // for negate #include #include "lp_data/HighsModelUtils.h" using std::min; void HighsRanging::invalidate() { valid = false; } void HighsRanging::clear() { this->invalidate(); this->col_cost_up.value_.clear(); this->col_cost_up.objective_.clear(); this->col_cost_up.in_var_.clear(); this->col_cost_up.ou_var_.clear(); this->col_cost_dn.value_.clear(); this->col_cost_dn.objective_.clear(); this->col_cost_dn.in_var_.clear(); this->col_cost_dn.ou_var_.clear(); this->col_bound_up.value_.clear(); this->col_bound_up.objective_.clear(); this->col_bound_up.in_var_.clear(); this->col_bound_up.ou_var_.clear(); this->col_bound_dn.value_.clear(); this->col_bound_dn.objective_.clear(); this->col_bound_dn.in_var_.clear(); this->col_bound_dn.ou_var_.clear(); this->row_bound_up.value_.clear(); this->row_bound_up.objective_.clear(); this->row_bound_up.in_var_.clear(); this->row_bound_up.ou_var_.clear(); this->row_bound_dn.value_.clear(); this->row_bound_dn.objective_.clear(); this->row_bound_dn.in_var_.clear(); this->row_bound_dn.ou_var_.clear(); } static double infProduct(double value) { // Multiplying value and kHighsInf if (value == 0) { return 0; } else { return value * kHighsInf; } } static double possInfProduct(double poss_inf, double value) { // Multiplying something that could be infinite and value if (value == 0) { return 0; } else { return poss_inf * value; } } HighsStatus getRangingData(HighsRanging& ranging, HighsLpSolverObject& solver_object) { ranging.clear(); if (solver_object.model_status_ != HighsModelStatus::kOptimal) { highsLogUser(solver_object.options_.log_options, HighsLogType::kError, "Cannot get ranging without an optimal solution\n"); return HighsStatus::kError; } HEkk& ekk_instance = solver_object.ekk_instance_; if (!ekk_instance.status_.initialised_for_solve) { highsLogUser(solver_object.options_.log_options, HighsLogType::kError, "Cannot get ranging without a valid Simplex instance\n"); return HighsStatus::kError; } const HighsLp& use_lp = solver_object.lp_; // Unscale the simplex data if the LP has been solved in the scaled space ekk_instance.unscaleSimplex(use_lp); // Aliases const HighsSimplexInfo& simplex_info = ekk_instance.info_; const SimplexBasis& simplex_basis = ekk_instance.basis_; const vector& col_scale = use_lp.scale_.col; const vector& row_scale = use_lp.scale_.row; const vector& value_ = simplex_info.workValue_; const vector& dual_ = simplex_info.workDual_; const vector& cost_ = simplex_info.workCost_; const vector& lower_ = simplex_info.workLower_; const vector& upper_ = simplex_info.workUpper_; const vector& Bvalue_ = simplex_info.baseValue_; const vector& Blower_ = simplex_info.baseLower_; const vector& Bupper_ = simplex_info.baseUpper_; const vector& Nflag_ = simplex_basis.nonbasicFlag_; const vector& Nmove_ = simplex_basis.nonbasicMove_; const vector& Bindex_ = simplex_basis.basicIndex_; const HighsSparseMatrix& matrix = use_lp.a_matrix_; // Local copies of scalars const HighsInt numRow = use_lp.num_row_; const HighsInt numCol = use_lp.num_col_; const HighsInt numTotal = numCol + numRow; const double H_TT = 1e-13; const double H_INF = kHighsInf; const double objective = solver_object.highs_info_.objective_function_value; // Code written for minimization problems. Maximization problems are // solved by using negated costs in the simplex solver and // minimizing. Thus dual information in the simplex solver is // negated for maximization problems. The objective has the right // sign, though. Maximization problems are, thus, accommodated by // applying the sign multiplier to dual information. HighsInt sense = 1; if (use_lp.sense_ == ObjSense::kMaximize) sense = -1; vector iWork_(numTotal); vector dWork_(numTotal); HVector column; column.setup(numRow); vector xi = Bvalue_; for (HighsInt i = 0; i < numRow; i++) { xi[i] = max(xi[i], Blower_[i]); xi[i] = min(xi[i], Bupper_[i]); } vector dj = dual_; for (HighsInt j = 0; j < numTotal; j++) { if (Nflag_[j] && (lower_[j] != upper_[j])) { if (value_[j] == lower_[j]) dj[j] = max(dj[j], 0.0); if (value_[j] == upper_[j]) dj[j] = min(dj[j], 0.0); if (lower_[j] == -H_INF && upper_[j] == H_INF) dj[j] = 0; } } // // Ranging 1.2. prepare "delta" space // vector dxi_inc(numRow); vector dxi_dec(numRow); for (HighsInt i = 0; i < numRow; i++) { dxi_inc[i] = Bupper_[i] - xi[i]; dxi_dec[i] = Blower_[i] - xi[i]; } vector ddj_inc(numTotal); vector ddj_dec(numTotal); for (HighsInt j = 0; j < numTotal; j++) { if (Nflag_[j]) { ddj_inc[j] = (value_[j] == lower_[j]) ? +H_INF : -dj[j]; ddj_dec[j] = (value_[j] == upper_[j]) ? -H_INF : -dj[j]; } } // // Ranging 1.3. prepare "theta" space // const double tol_a = 1e-9; const double THETA_INF = H_INF / 1e40; vector txj_inc(numTotal, +THETA_INF); // theta vector axj_inc(numTotal, 0); // alpha vector ixj_inc(numTotal, -1); // i-out vector wxj_inc(numTotal, 0); // which bound is limiting vector jxj_inc(numTotal, -1); // j = n(i), (with bound flip) vector txj_dec(numTotal, -THETA_INF); vector axj_dec(numTotal, 0); vector ixj_dec(numTotal, -1); vector wxj_dec(numTotal, 0); vector jxj_dec(numTotal, -1); vector tci_inc(numRow, +THETA_INF); // theta vector aci_inc(numRow, 0); // alpha vector jci_inc(numRow, -1); // column index vector tci_dec(numRow, -THETA_INF); vector aci_dec(numRow, 0); vector jci_dec(numRow, -1); // Major "theta" loop for (HighsInt j = 0; j < numTotal; j++) { // Skip basic column if (!Nflag_[j]) continue; // Form updated column column.clear(); matrix.collectAj(column, j, 1); const double expected_density = ekk_instance.info_.col_aq_density; ekk_instance.ftran(column, expected_density); HighsInt nWork = 0; for (HighsInt k = 0; k < column.count; k++) { HighsInt iRow = column.index[k]; double alpha = column.array[iRow]; if (fabs(alpha) > tol_a) { iWork_[nWork] = iRow; dWork_[nWork] = alpha; nWork++; } } // Standard primal ratio test double myt_inc = +THETA_INF; double myt_dec = -THETA_INF; HighsInt myk_inc = -1; HighsInt myk_dec = -1; for (HighsInt k = 0; k < nWork; k++) { HighsInt i = iWork_[k]; double alpha = dWork_[k]; double theta_inc = (alpha < 0 ? dxi_inc[i] : dxi_dec[i]) / -alpha; double theta_dec = (alpha > 0 ? dxi_inc[i] : dxi_dec[i]) / -alpha; if (myt_inc > theta_inc) myt_inc = theta_inc, myk_inc = k; if (myt_dec < theta_dec) myt_dec = theta_dec, myk_dec = k; } if (myk_inc != -1) { HighsInt i = iWork_[myk_inc]; double alpha = dWork_[myk_inc]; ixj_inc[j] = i; axj_inc[j] = alpha; const double numerator = (alpha < 0 ? dxi_inc[i] : dxi_dec[i]); txj_inc[j] = (alpha < 0 ? dxi_inc[i] : dxi_dec[i]) / -alpha; wxj_inc[j] = (alpha < 0 ? +1 : -1); } if (myk_dec != -1) { HighsInt i = iWork_[myk_dec]; double alpha = dWork_[myk_dec]; ixj_dec[j] = i; axj_dec[j] = alpha; txj_dec[j] = (alpha > 0 ? dxi_inc[i] : dxi_dec[i]) / -alpha; wxj_dec[j] = (alpha > 0 ? +1 : -1); } // Accumulated dual ratio test double myd_inc = ddj_inc[j]; double myd_dec = ddj_dec[j]; for (HighsInt k = 0; k < nWork; k++) { HighsInt i = iWork_[k]; double alpha = dWork_[k]; double theta_inc = (alpha < 0 ? myd_inc : myd_dec) / -alpha; double theta_dec = (alpha > 0 ? myd_inc : myd_dec) / -alpha; if (tci_inc[i] > theta_inc) tci_inc[i] = theta_inc, aci_inc[i] = alpha, jci_inc[i] = j; if (tci_dec[i] < theta_dec) tci_dec[i] = theta_dec, aci_dec[i] = alpha, jci_dec[i] = j; } } // Additional j-out for primal ratio test (considering bound flip) for (HighsInt j = 0; j < numTotal; j++) { if (Nflag_[j]) { // J-out for x_j = l_j if (Nmove_[j] == +1) { double value = value_[j] + txj_inc[j]; if (ixj_inc[j] != -1 && value <= upper_[j]) { jxj_inc[j] = Bindex_[ixj_inc[j]]; } else if (value > upper_[j]) { jxj_inc[j] = j; } } // J-out for x_j = u_j if (Nmove_[j] == -1) { double value = value_[j] + txj_dec[j]; if (ixj_dec[j] != -1 && value >= lower_[j]) { jxj_dec[j] = Bindex_[ixj_dec[j]]; } else if (value < lower_[j]) { jxj_dec[j] = j; } } // J-out for free variable if (lower_[j] == -H_INF && upper_[j] == H_INF) { if (ixj_inc[j] != -1) jxj_inc[j] = jxj_dec[j] = Bindex_[ixj_inc[j]]; if (ixj_dec[j] != -1) jxj_inc[j] = jxj_dec[j] = Bindex_[ixj_dec[j]]; } } } // // Ranging 2. cost ranging // vector c_up_c(numTotal), c_dn_c(numTotal); vector c_up_f(numTotal), c_dn_f(numTotal); vector c_up_e(numTotal), c_dn_e(numTotal); vector c_up_l(numTotal), c_dn_l(numTotal); // // Ranging 2.1. non-basic cost ranging // // const HighsInt check_col = 2951; for (HighsInt j = 0; j < numCol; j++) { if (Nflag_[j]) { // Primal value and its sign double value = value_[j]; double vsign = (value > 0) ? 1 : (value < 0 ? -1 : 0); // Increase c_j if (ddj_inc[j] != H_INF) { c_up_c[j] = cost_[j] + ddj_inc[j]; c_up_f[j] = objective + sense * possInfProduct(ddj_inc[j], value); // value * ddj_inc[j]; c_up_e[j] = j; c_up_l[j] = jxj_dec[j]; } else { c_up_c[j] = H_INF; c_up_f[j] = objective + sense * infProduct(vsign); // vsign * H_INF; c_up_e[j] = -1; c_up_l[j] = -1; } // Decrease c_j if (ddj_dec[j] != H_INF) { c_dn_c[j] = cost_[j] + ddj_dec[j]; c_dn_f[j] = objective + sense * possInfProduct(ddj_dec[j], value); // value * ddj_dec[j]; c_dn_e[j] = j; c_dn_l[j] = jxj_inc[j]; } else { c_up_c[j] = -H_INF; c_up_f[j] = objective - sense * infProduct(vsign); // vsign * H_INF; c_up_e[j] = -1; c_up_l[j] = -1; } } } // // Ranging 2.2. basic cost ranging // for (HighsInt i = 0; i < numRow; i++) { if (Bindex_[i] < numCol) { // Primal variable and its sign HighsInt j = Bindex_[i], je; double value = xi[i]; double vsign = (value > 0) ? 1 : (value < 0 ? -1 : 0); // Increase c_i if (jci_inc[i] != -1) { c_up_c[j] = cost_[j] + tci_inc[i]; c_up_f[j] = objective + sense * possInfProduct(tci_inc[i], value); // value * tci_inc[i]; c_up_e[j] = je = jci_inc[i]; c_up_l[j] = Nmove_[je] > 0 ? jxj_inc[je] : jxj_dec[je]; } else { c_up_c[j] = H_INF; c_up_f[j] = objective + sense * infProduct(vsign); // vsign * H_INF; c_up_e[j] = -1; c_up_l[j] = -1; } // Decrease c_i if (jci_dec[i] != -1) { c_dn_c[j] = cost_[j] + tci_dec[i]; c_dn_f[j] = objective + sense * possInfProduct(tci_dec[i], value); // value * tci_dec[i]; c_dn_e[j] = je = jci_dec[i]; c_dn_l[j] = Nmove_[je] > 0 ? jxj_inc[je] : jxj_dec[je]; } else { c_dn_c[j] = -H_INF; c_dn_f[j] = objective - sense * infProduct(vsign); // H_INF * vsign; c_dn_e[j] = -1; c_dn_l[j] = -1; } } } // // Ranging 3. bounds ranging // vector b_up_b(numTotal), b_dn_b(numTotal); vector b_up_f(numTotal), b_dn_f(numTotal); vector b_up_e(numTotal), b_dn_e(numTotal); vector b_up_l(numTotal), b_dn_l(numTotal); // // Ranging 3.1. non-basic bounds ranging // for (HighsInt j = 0; j < numTotal; j++) { if (Nflag_[j]) { // FREE variable if (lower_[j] == -H_INF && upper_[j] == H_INF) { b_up_b[j] = H_INF; b_up_f[j] = objective; b_up_e[j] = -1; b_up_l[j] = -1; b_dn_b[j] = -H_INF; b_dn_f[j] = objective; b_dn_e[j] = -1; b_dn_l[j] = -1; continue; } // Dual value and its sign double dualv = dj[j]; double dsign = (dualv > 0) ? 1 : (dualv < 0 ? -1 : 0); // Increase x_j if (ixj_inc[j] != -1) { HighsInt i = ixj_inc[j]; b_up_b[j] = value_[j] + txj_inc[j]; b_up_f[j] = objective + sense * possInfProduct(txj_inc[j], dualv); // txj_inc[j] * dualv; b_up_e[j] = wxj_inc[j] > 0 ? jci_inc[i] : jci_dec[i]; b_up_l[j] = Bindex_[i]; } else { b_up_b[j] = H_INF; b_up_f[j] = objective + sense * infProduct(dsign); // H_INF * dsign; b_up_e[j] = -1; b_up_l[j] = -1; } // Check if b_up_b > upper if (value_[j] != upper_[j] && b_up_b[j] > upper_[j]) { b_up_b[j] = upper_[j]; assert(lower_[j] > -kHighsInf); b_up_f[j] = objective + sense * (upper_[j] - lower_[j]) * dualv; b_up_e[j] = j; b_up_l[j] = j; } // Decrease x_j if (ixj_dec[j] != -1) { HighsInt i = ixj_dec[j]; b_dn_b[j] = value_[j] + txj_dec[j]; b_dn_f[j] = objective + sense * possInfProduct(txj_dec[j], dualv); // txj_dec[j] * dualv; b_dn_e[j] = wxj_dec[j] > 0 ? jci_inc[i] : jci_dec[i]; b_dn_l[j] = Bindex_[i]; } else { b_dn_b[j] = -H_INF; b_dn_f[j] = objective - sense * infProduct(dsign); // H_INF * dsign; b_dn_e[j] = -1; b_dn_l[j] = -1; } // Check if b_dn_b < lower if (value_[j] != lower_[j] && b_dn_b[j] < lower_[j]) { b_dn_b[j] = lower_[j]; assert(upper_[j] < kHighsInf); b_dn_f[j] = objective + sense * (lower_[j] - upper_[j]) * dualv; b_dn_e[j] = j; b_dn_l[j] = j; } } } // // Ranging 3.2. basic bounds ranging // for (HighsInt i = 0; i < numRow; i++) { for (HighsInt dir = -1; dir <= 1; dir += 2) { HighsInt j = Bindex_[i]; double& newx = dir == -1 ? b_dn_b[j] : b_up_b[j]; double& newf = dir == -1 ? b_dn_f[j] : b_up_f[j]; HighsInt& j_enter = dir == -1 ? b_dn_e[j] : b_up_e[j]; HighsInt& j_leave = dir == -1 ? b_dn_l[j] : b_up_l[j]; HighsInt j_in = dir == -1 ? jci_inc[i] : jci_dec[i]; double a_in = dir == -1 ? aci_inc[i] : aci_dec[i]; if (j_in != -1) { HighsInt jmove = Nmove_[j_in]; HighsInt i_out = jmove > 0 ? ixj_inc[j_in] : ixj_dec[j_in]; HighsInt j_out = jmove > 0 ? jxj_inc[j_in] : jxj_dec[j_in]; HighsInt w_out = jmove > 0 ? wxj_inc[j_in] : wxj_dec[j_in]; double tt = jmove > 0 ? txj_inc[j_in] : txj_dec[j_in]; if (j_out == j_in) { // Bound flip double delta = jmove * (upper_[j_in] - lower_[j_in]); newx = xi[i] - delta * a_in; newf = objective + sense * delta * dual_[j_in]; j_enter = j_in; j_leave = j_out; } else if (j_out != -1) { // Regular double delta = w_out > 0 ? dxi_inc[i_out] : dxi_dec[i_out]; double a_out = jmove > 0 ? axj_inc[j_in] : axj_dec[j_in]; newx = xi[i] + delta * a_in / a_out; newf = objective + sense * tt * dual_[j_in]; j_enter = j_in; j_leave = j_out; } else { // Primal ratio test failed - change unlimitedly // // While still limited by its own bounds // // Its own bounds could just be inf newx = dir == -1 ? lower_[j] : upper_[j]; newf = objective; j_enter = -1; j_leave = -1; } } else { // Dual ratio test failed - just stay newx = xi[i]; newf = objective; j_enter = -1; j_leave = -1; } } } // // Ranging 4.1.1 Trim small value to zero // for (HighsInt j = 0; j < numCol; j++) { if (fabs(c_up_c[j]) < H_TT) c_up_c[j] = 0; if (fabs(c_dn_c[j]) < H_TT) c_dn_c[j] = 0; if (fabs(b_up_b[j]) < H_TT) b_up_b[j] = 0; if (fabs(b_dn_b[j]) < H_TT) b_dn_b[j] = 0; } for (HighsInt i = 0, j = numCol; i < numRow; i++, j++) { if (fabs(b_up_b[j]) < H_TT) b_up_b[j] = 0; if (fabs(b_dn_b[j]) < H_TT) b_dn_b[j] = 0; } // // Ranging 4.2. Put to output buffer // if (sense > 0) { ranging.col_cost_up.value_ = c_up_c; ranging.col_cost_dn.value_ = c_dn_c; ranging.col_cost_up.objective_ = c_up_f; ranging.col_cost_dn.objective_ = c_dn_f; ranging.col_cost_up.in_var_ = c_up_e; ranging.col_cost_dn.in_var_ = c_dn_e; ranging.col_cost_up.ou_var_ = c_up_l; ranging.col_cost_dn.ou_var_ = c_dn_l; } else { // For maximization problems, flip data and negate the cost values ranging.col_cost_up.value_ = c_dn_c; ranging.col_cost_dn.value_ = c_up_c; ranging.col_cost_up.objective_ = c_dn_f; ranging.col_cost_dn.objective_ = c_up_f; ranging.col_cost_up.in_var_ = c_dn_e; ranging.col_cost_dn.in_var_ = c_up_e; ranging.col_cost_up.ou_var_ = c_dn_l; ranging.col_cost_dn.ou_var_ = c_up_l; std::transform(ranging.col_cost_up.value_.cbegin(), ranging.col_cost_up.value_.cend(), ranging.col_cost_up.value_.begin(), std::negate()); std::transform(ranging.col_cost_dn.value_.cbegin(), ranging.col_cost_dn.value_.cend(), ranging.col_cost_dn.value_.begin(), std::negate()); } ranging.col_bound_up.value_ = {b_up_b.begin(), b_up_b.begin() + numCol}; ranging.col_bound_dn.value_ = {b_dn_b.begin(), b_dn_b.begin() + numCol}; ranging.col_bound_up.objective_ = {b_up_f.begin(), b_up_f.begin() + numCol}; ranging.col_bound_dn.objective_ = {b_dn_f.begin(), b_dn_f.begin() + numCol}; ranging.col_bound_up.in_var_ = {b_up_e.begin(), b_up_e.begin() + numCol}; ranging.col_bound_dn.in_var_ = {b_dn_e.begin(), b_dn_e.begin() + numCol}; ranging.col_bound_up.ou_var_ = {b_up_l.begin(), b_up_l.begin() + numCol}; ranging.col_bound_dn.ou_var_ = {b_dn_l.begin(), b_dn_l.begin() + numCol}; // Flip all data and negate the row bound values ranging.row_bound_up.value_ = {b_dn_b.begin() + numCol, b_dn_b.begin() + numTotal}; std::transform(ranging.row_bound_up.value_.cbegin(), ranging.row_bound_up.value_.cend(), ranging.row_bound_up.value_.begin(), std::negate()); ranging.row_bound_dn.value_ = {b_up_b.begin() + numCol, b_up_b.begin() + numTotal}; std::transform(ranging.row_bound_dn.value_.cbegin(), ranging.row_bound_dn.value_.cend(), ranging.row_bound_dn.value_.begin(), std::negate()); ranging.row_bound_up.objective_ = {b_dn_f.begin() + numCol, b_dn_f.begin() + numTotal}; ranging.row_bound_dn.objective_ = {b_up_f.begin() + numCol, b_up_f.begin() + numTotal}; ranging.row_bound_up.in_var_ = {b_dn_e.begin() + numCol, b_dn_e.begin() + numTotal}; ranging.row_bound_dn.in_var_ = {b_up_e.begin() + numCol, b_up_e.begin() + numTotal}; ranging.row_bound_up.ou_var_ = {b_dn_l.begin() + numCol, b_dn_l.begin() + numTotal}; ranging.row_bound_dn.ou_var_ = {b_up_l.begin() + numCol, b_up_l.begin() + numTotal}; ranging.valid = true; if (solver_object.options_.log_dev_level) writeRangingFile(stdout, use_lp, solver_object.highs_info_.objective_function_value, solver_object.basis_, solver_object.solution_, ranging, kSolutionStylePretty); return HighsStatus::kOk; } void writeRangingFile(FILE* file, const HighsLp& lp, const double objective_function_value, const HighsBasis& basis, const HighsSolution& solution, const HighsRanging& ranging, const HighsInt style) { if (!ranging.valid) { fprintf(file, "None\n"); return; } fprintf(file, "Valid\n"); const double double_tolerance = 1e-13; std::stringstream ss; const bool have_col_names = lp.col_names_.size() > 0; const bool have_row_names = lp.row_names_.size() > 0; const bool pretty = style == kSolutionStylePretty; const char* pretty_cost_format = "%6d %4s %-10.4g %-10.4g %-10.4g %-10.4g " "%-10.4g %-s\n"; const char* raw_cost_format = "%-s %s %s %s %s\n"; const char* pretty_bound_format = "%6d %4s %-10.4g %-10.4g %-10.4g %-10.4g %-10.4g %-10.4g %-10.4g " "%-s\n"; const char* raw_bound_format = "%-s %s %s %s %s\n"; std::array dn_objective; std::array up_objective; std::array dn_value; std::array up_value; std::array objective = highsDoubleToString(objective_function_value, double_tolerance); fprintf(file, "Objective %s\n", objective.data()); if (pretty) { fprintf(file, "\n Cost ranging\n" "Column Status DownObj Down Value " " Up UpObj Name\n"); } else { fprintf(file, "\n# Cost ranging\n"); } for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) { // Create a column name ss.str(std::string()); ss << "C" << iCol; const std::string name = have_col_names ? lp.col_names_[iCol] : ss.str(); if (pretty) { fprintf(file, pretty_cost_format, (int)iCol, statusToString(basis.col_status[iCol], lp.col_lower_[iCol], lp.col_upper_[iCol]) .c_str(), ranging.col_cost_dn.objective_[iCol], ranging.col_cost_dn.value_[iCol], lp.col_cost_[iCol], ranging.col_cost_up.value_[iCol], ranging.col_cost_up.objective_[iCol], name.c_str()); } else { dn_objective = highsDoubleToString(ranging.col_cost_dn.objective_[iCol], double_tolerance); up_objective = highsDoubleToString(ranging.col_cost_up.objective_[iCol], double_tolerance); dn_value = highsDoubleToString(ranging.col_cost_dn.value_[iCol], double_tolerance); up_value = highsDoubleToString(ranging.col_cost_up.value_[iCol], double_tolerance); fprintf(file, raw_cost_format, name.c_str(), dn_objective.data(), dn_value.data(), up_value.data(), up_objective.data()); } } if (pretty) { fprintf(file, "\n Bound ranging\n" "Column Status DownObj Down Lower Value Upper " " Up UpObj Name\n"); } else { fprintf(file, "\n# Bound ranging\n# Columns\n"); } for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) { // Create a column name ss.str(std::string()); ss << "C" << iCol; const std::string name = have_col_names ? lp.col_names_[iCol] : ss.str(); if (pretty) { fprintf(file, pretty_bound_format, (int)iCol, statusToString(basis.col_status[iCol], lp.col_lower_[iCol], lp.col_upper_[iCol]) .c_str(), ranging.col_bound_dn.objective_[iCol], ranging.col_bound_dn.value_[iCol], lp.col_lower_[iCol], solution.col_value[iCol], lp.col_upper_[iCol], ranging.col_bound_up.value_[iCol], ranging.col_bound_up.objective_[iCol], name.c_str()); } else { dn_objective = highsDoubleToString(ranging.col_bound_dn.objective_[iCol], double_tolerance); up_objective = highsDoubleToString(ranging.col_bound_up.objective_[iCol], double_tolerance); dn_value = highsDoubleToString(ranging.col_bound_dn.value_[iCol], double_tolerance); up_value = highsDoubleToString(ranging.col_bound_up.value_[iCol], double_tolerance); fprintf(file, raw_bound_format, name.c_str(), dn_objective.data(), dn_value.data(), up_value.data(), up_objective.data()); } } if (pretty) { fprintf(file, " Bound ranging\n" " Row Status DownObj Down Lower Value Upper " " Up UpObj Name\n"); } else { fprintf(file, "# Rows\n"); } for (HighsInt iRow = 0; iRow < lp.num_row_; iRow++) { // Create a row name ss.str(std::string()); ss << "R" << iRow; const std::string name = have_row_names ? lp.row_names_[iRow] : ss.str(); if (pretty) { fprintf(file, pretty_bound_format, (int)iRow, statusToString(basis.row_status[iRow], lp.row_lower_[iRow], lp.row_upper_[iRow]) .c_str(), ranging.row_bound_dn.objective_[iRow], ranging.row_bound_dn.value_[iRow], lp.row_lower_[iRow], solution.row_value[iRow], lp.row_upper_[iRow], ranging.row_bound_up.value_[iRow], ranging.row_bound_up.objective_[iRow], name.c_str()); } else { dn_objective = highsDoubleToString(ranging.row_bound_dn.objective_[iRow], double_tolerance); up_objective = highsDoubleToString(ranging.row_bound_up.objective_[iRow], double_tolerance); dn_value = highsDoubleToString(ranging.row_bound_dn.value_[iRow], double_tolerance); up_value = highsDoubleToString(ranging.row_bound_up.value_[iRow], double_tolerance); fprintf(file, raw_bound_format, name.c_str(), dn_objective.data(), dn_value.data(), up_value.data(), up_objective.data()); } } }