Skip to content

Commit 591aebd

Browse files
committed
Adding code for testing scaling and condition estimates
1 parent 75cc162 commit 591aebd

File tree

10 files changed

+145
-94
lines changed

10 files changed

+145
-94
lines changed

check/TestBasisSolves.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,3 +615,20 @@ TEST_CASE("Kappa", "[highs_basis_solves]") {
615615

616616
highs.resetGlobalScheduler(true);
617617
}
618+
619+
TEST_CASE("scaling-kappa", "[highs_basis_solves]") {
620+
std::string model;
621+
// model = "chip";
622+
// model = "avgas";
623+
model = "afiro";
624+
std::string filename =
625+
std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps";
626+
627+
Highs highs;
628+
// highs.setOptionValue("output_flag", dev_run);
629+
630+
// Read the LP given by filename
631+
highs.readModel(filename);
632+
633+
highs.run();
634+
}

highs/Highs.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -690,11 +690,9 @@ class Highs {
690690

691691
/**
692692
* @brief Get the condition number of the current basis matrix,
693-
* possibly computing it exactly and reporting the error in the
694-
* approximate condition number
693+
* possibly computing it exactly
695694
*/
696-
HighsStatus getKappa(double& kappa, const bool exact = false,
697-
const bool report = false) const;
695+
HighsStatus getKappa(double& kappa, const bool exact = false) const;
698696

699697
/**
700698
* @brief Get the number of columns in the incumbent model

highs/interfaces/highs_c_api.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -784,7 +784,8 @@ HighsInt Highs_getDoubleOptionValue(const void* highs, const char* option,
784784
* @param highs A pointer to the Highs instance.
785785
* @param option The name of the option.
786786
* @param value A pointer to allocated memory to store the current value of
787-
* the option. This must have length `kHighsMaximumStringLength`.
787+
* the option. This must have length
788+
* `kHighsMaximumStringLength`.
788789
*
789790
* @returns A `kHighsStatus` constant indicating whether the call succeeded.
790791
*/

highs/lp_data/HConst.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
#include "util/HighsInt.h"
1919

20+
const bool kSimplexScaleDev = true;
21+
2022
const std::string kHighsCopyrightStatement =
2123
"Copyright (c) 2025 HiGHS under MIT licence terms";
2224

highs/lp_data/HStruct.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ struct HighsScale {
100100
double cost;
101101
std::vector<double> col;
102102
std::vector<double> row;
103+
void print(const std::string& prefix = "",
104+
const std::string& message = "") const;
103105
};
104106

105107
struct HighsLpMods {

highs/lp_data/Highs.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2338,11 +2338,10 @@ HighsStatus Highs::getReducedColumn(const HighsInt col, double* col_vector,
23382338
return HighsStatus::kOk;
23392339
}
23402340

2341-
HighsStatus Highs::getKappa(double& kappa, const bool exact,
2342-
const bool report) const {
2341+
HighsStatus Highs::getKappa(double& kappa, const bool exact) const {
23432342
if (!ekk_instance_.status_.has_invert)
2344-
return invertRequirementError("getBasisInverseRow");
2345-
kappa = ekk_instance_.computeBasisCondition(this->model_.lp_, exact, report);
2343+
return invertRequirementError("getKappa");
2344+
kappa = ekk_instance_.computeBasisCondition(this->model_.lp_, exact);
23462345
return HighsStatus::kOk;
23472346
}
23482347

highs/lp_data/HighsLpUtils.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3077,3 +3077,29 @@ bool HighsUserScaleData::scaleWarning(std::string& message) const {
30773077
message = ss.str();
30783078
return true;
30793079
}
3080+
3081+
void HighsScale::print(const std::string& prefix,
3082+
const std::string& message) const {
3083+
double min_row_scale = kHighsInf;
3084+
double max_row_scale = -kHighsInf;
3085+
double min_col_scale = kHighsInf;
3086+
double max_col_scale = -kHighsInf;
3087+
HighsInt num_row = this->row.size();
3088+
HighsInt num_col = this->col.size();
3089+
for (HighsInt iCol = 0; iCol < num_col; iCol++) {
3090+
min_col_scale = std::min(this->col[iCol], min_col_scale);
3091+
max_col_scale = std::max(this->col[iCol], max_col_scale);
3092+
}
3093+
for (HighsInt iRow = 0; iRow < num_row; iRow++) {
3094+
min_row_scale = std::min(this->row[iRow], min_row_scale);
3095+
max_row_scale = std::max(this->row[iRow], max_row_scale);
3096+
}
3097+
printf(
3098+
"%sTxt HighsScale: Cost scale = %g; col scale in [%g, %g]; row scale in "
3099+
"[%g, %g]; %s\n",
3100+
prefix.c_str(), this->cost, min_col_scale, max_col_scale, min_row_scale,
3101+
max_row_scale, message.c_str());
3102+
printf("%sCsv,%s,%g,%g,%g,%g,%g\n", prefix.c_str(), message.c_str(),
3103+
this->cost, min_col_scale, max_col_scale, min_row_scale,
3104+
max_row_scale);
3105+
}

highs/simplex/HApp.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,9 @@ inline HighsStatus solveLpSimplex(HighsLpSolverObject& solver_object) {
154154
// considering computing scaling factors if there are none - and
155155
// then move to EKK
156156
considerSimplexScaling(options, incumbent_lp);
157-
//
157+
if (kSimplexScaleDev)
158+
incumbent_lp.scale_.print("grepSimplexScaling",
159+
"After scaling, " + incumbent_lp.model_name_);
158160
const bool was_scaled = incumbent_lp.is_scaled_;
159161
if (!status.has_basis && !basis.valid && basis.useful) {
160162
// There is no simplex basis, but there is a useful HiGHS basis
@@ -218,10 +220,10 @@ inline HighsStatus solveLpSimplex(HighsLpSolverObject& solver_object) {
218220
bool solve_unscaled_lp = false;
219221
bool solved_unscaled_lp = false;
220222
if (!incumbent_lp.scale_.has_scaling) {
223+
solve_unscaled_lp = true;
221224
//
222225
// Solve the unscaled LP with unscaled NLA
223226
//
224-
solve_unscaled_lp = true;
225227
return_status = ekk_instance.solve();
226228
solved_unscaled_lp = true;
227229
ekk_instance.unpermute();
@@ -447,6 +449,7 @@ inline HighsStatus solveLpSimplex(HighsLpSolverObject& solver_object) {
447449
assert(basis.valid);
448450
highs_info.basis_validity = kBasisValidityValid;
449451
}
452+
if (kSimplexScaleDev) ekk_instance.testBasisCondition();
450453
// Move the incumbent LP back from Ekk
451454
incumbent_lp = std::move(ekk_lp);
452455
incumbent_lp.is_moved_ = false;

highs/simplex/HEkk.cpp

Lines changed: 82 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -3603,8 +3603,22 @@ HighsStatus HEkk::returnFromSolve(const HighsStatus return_status) {
36033603
return return_status;
36043604
}
36053605

3606-
double HEkk::computeBasisCondition(const HighsLp& lp, const bool exact,
3607-
const bool report) const {
3606+
void HEkk::testBasisCondition(const std::string& message) const {
3607+
double exact_kappa_tt = -timer_->read();
3608+
bool exact = true;
3609+
double exact_kappa = this->computeBasisCondition(this->lp_, exact);
3610+
exact_kappa_tt += timer_->read();
3611+
double approx_kappa_tt = -timer_->read();
3612+
exact = false;
3613+
double approx_kappa = this->computeBasisCondition(this->lp_, exact);
3614+
approx_kappa_tt += timer_->read();
3615+
highsLogUser(options_->log_options, HighsLogType::kInfo,
3616+
"getKappa,%s,%g,%g,%g,%g,%s,%s\n", this->lp_.model_name_.c_str(),
3617+
exact_kappa, exact_kappa_tt, approx_kappa, approx_kappa_tt,
3618+
message.c_str(), this->lp_.origin_name_.c_str());
3619+
}
3620+
3621+
double HEkk::computeBasisCondition(const HighsLp& lp, const bool exact) const {
36083622
HighsInt solver_num_row = lp.num_row_;
36093623
HighsInt solver_num_col = lp.num_col_;
36103624
vector<double> bs_cond_x;
@@ -3616,9 +3630,10 @@ double HEkk::computeBasisCondition(const HighsLp& lp, const bool exact,
36163630

36173631
const HighsInt* Astart = lp.a_matrix_.start_.data();
36183632
const double* Avalue = lp.a_matrix_.value_.data();
3619-
double exact_norm_Binv = 0;
3633+
double norm_Binv = 0;
36203634
if (exact) {
3621-
// Compute the exact norm of B^{-1}
3635+
// Compute the exact 1-norm of B^{-1}
3636+
norm_Binv = 0;
36223637
for (HighsInt r_n = 0; r_n < solver_num_row; r_n++) {
36233638
row_ep.clear();
36243639
row_ep.index[row_ep.count] = r_n;
@@ -3630,79 +3645,80 @@ double HEkk::computeBasisCondition(const HighsLp& lp, const bool exact,
36303645
double c_norm = 0.0;
36313646
for (HighsInt iX = 0; iX < row_ep.count; iX++)
36323647
c_norm += std::fabs(row_ep.array[row_ep.index[iX]]);
3633-
exact_norm_Binv = std::max(c_norm, exact_norm_Binv);
3634-
}
3635-
}
3636-
// Compute the Hager condition number estimate for the basis matrix
3637-
const double expected_density = 1;
3638-
bs_cond_x.resize(solver_num_row);
3639-
bs_cond_y.resize(solver_num_row);
3640-
bs_cond_z.resize(solver_num_row);
3641-
bs_cond_w.resize(solver_num_row);
3642-
// x = ones(n,1)/n;
3643-
// y = A\x;
3644-
double mu = 1.0 / solver_num_row;
3645-
double norm_Binv = 0;
3646-
for (HighsInt r_n = 0; r_n < solver_num_row; r_n++) bs_cond_x[r_n] = mu;
3647-
row_ep.clear();
3648-
for (HighsInt r_n = 0; r_n < solver_num_row; r_n++) {
3649-
double value = bs_cond_x[r_n];
3650-
if (value) {
3651-
row_ep.index[row_ep.count] = r_n;
3652-
row_ep.array[r_n] = value;
3653-
row_ep.count++;
3654-
}
3655-
}
3656-
for (HighsInt ps_n = 1; ps_n <= 5; ps_n++) {
3657-
row_ep.packFlag = false;
3658-
simplex_nla_.ftran(row_ep, expected_density);
3659-
3660-
// zeta = sign(y);
3661-
for (HighsInt r_n = 0; r_n < solver_num_row; r_n++) {
3662-
bs_cond_y[r_n] = row_ep.array[r_n];
3663-
if (bs_cond_y[r_n] > 0)
3664-
bs_cond_w[r_n] = 1.0;
3665-
else if (bs_cond_y[r_n] < 0)
3666-
bs_cond_w[r_n] = -1.0;
3667-
else
3668-
bs_cond_w[r_n] = 0.0;
3648+
norm_Binv = std::max(c_norm, norm_Binv);
36693649
}
3670-
// z=A'\zeta;
3650+
} else {
3651+
// Compute the Hager 1-norm condition number estimate for the basis matrix
3652+
const double expected_density = 1;
3653+
bs_cond_x.resize(solver_num_row);
3654+
bs_cond_y.resize(solver_num_row);
3655+
bs_cond_z.resize(solver_num_row);
3656+
bs_cond_w.resize(solver_num_row);
3657+
// x = ones(n,1)/n;
3658+
// y = A\x;
3659+
double mu = 1.0 / solver_num_row;
3660+
norm_Binv = 0;
3661+
for (HighsInt r_n = 0; r_n < solver_num_row; r_n++) bs_cond_x[r_n] = mu;
36713662
row_ep.clear();
36723663
for (HighsInt r_n = 0; r_n < solver_num_row; r_n++) {
3673-
double value = bs_cond_w[r_n];
3664+
double value = bs_cond_x[r_n];
36743665
if (value) {
36753666
row_ep.index[row_ep.count] = r_n;
36763667
row_ep.array[r_n] = value;
36773668
row_ep.count++;
36783669
}
36793670
}
3680-
row_ep.packFlag = false;
3681-
simplex_nla_.btran(row_ep, expected_density);
3682-
double norm_z = 0.0;
3683-
double ztx = 0.0;
3684-
norm_Binv = 0.0;
3685-
HighsInt argmax_z = -1;
3686-
for (HighsInt r_n = 0; r_n < solver_num_row; r_n++) {
3687-
bs_cond_z[r_n] = row_ep.array[r_n];
3688-
double abs_z_v = fabs(bs_cond_z[r_n]);
3689-
if (abs_z_v > norm_z) {
3690-
norm_z = abs_z_v;
3691-
argmax_z = r_n;
3671+
for (HighsInt ps_n = 1; ps_n <= 5; ps_n++) {
3672+
row_ep.packFlag = false;
3673+
simplex_nla_.ftran(row_ep, expected_density);
3674+
// zeta = sign(y);
3675+
for (HighsInt r_n = 0; r_n < solver_num_row; r_n++) {
3676+
bs_cond_y[r_n] = row_ep.array[r_n];
3677+
if (bs_cond_y[r_n] > 0)
3678+
bs_cond_w[r_n] = 1.0;
3679+
else if (bs_cond_y[r_n] < 0)
3680+
bs_cond_w[r_n] = -1.0;
3681+
else
3682+
bs_cond_w[r_n] = 0.0;
3683+
}
3684+
// z=A'\zeta;
3685+
row_ep.clear();
3686+
for (HighsInt r_n = 0; r_n < solver_num_row; r_n++) {
3687+
double value = bs_cond_w[r_n];
3688+
if (value) {
3689+
row_ep.index[row_ep.count] = r_n;
3690+
row_ep.array[r_n] = value;
3691+
row_ep.count++;
3692+
}
36923693
}
3693-
ztx += bs_cond_z[r_n] * bs_cond_x[r_n];
3694-
norm_Binv += fabs(bs_cond_y[r_n]);
3694+
row_ep.packFlag = false;
3695+
simplex_nla_.btran(row_ep, expected_density);
3696+
double norm_z = 0.0;
3697+
double ztx = 0.0;
3698+
norm_Binv = 0.0;
3699+
HighsInt argmax_z = -1;
3700+
for (HighsInt r_n = 0; r_n < solver_num_row; r_n++) {
3701+
bs_cond_z[r_n] = row_ep.array[r_n];
3702+
double abs_z_v = fabs(bs_cond_z[r_n]);
3703+
if (abs_z_v > norm_z) {
3704+
norm_z = abs_z_v;
3705+
argmax_z = r_n;
3706+
}
3707+
ztx += bs_cond_z[r_n] * bs_cond_x[r_n];
3708+
norm_Binv += fabs(bs_cond_y[r_n]);
3709+
}
3710+
if (norm_z <= ztx) break;
3711+
// x = zeros(n,1);
3712+
// x(fd_i) = 1;
3713+
for (HighsInt r_n = 0; r_n < solver_num_row; r_n++) bs_cond_x[r_n] = 0.0;
3714+
row_ep.clear();
3715+
row_ep.count = 1;
3716+
row_ep.index[0] = argmax_z;
3717+
row_ep.array[argmax_z] = 1.0;
3718+
bs_cond_x[argmax_z] = 1.0;
36953719
}
3696-
if (norm_z <= ztx) break;
3697-
// x = zeros(n,1);
3698-
// x(fd_i) = 1;
3699-
for (HighsInt r_n = 0; r_n < solver_num_row; r_n++) bs_cond_x[r_n] = 0.0;
3700-
row_ep.clear();
3701-
row_ep.count = 1;
3702-
row_ep.index[0] = argmax_z;
3703-
row_ep.array[argmax_z] = 1.0;
3704-
bs_cond_x[argmax_z] = 1.0;
37053720
}
3721+
// Compute the exact 1-norm of B
37063722
double norm_B = 0.0;
37073723
for (HighsInt r_n = 0; r_n < solver_num_row; r_n++) {
37083724
HighsInt vr_n = basis_.basicIndex_[r_n];
@@ -3714,19 +3730,7 @@ double HEkk::computeBasisCondition(const HighsLp& lp, const bool exact,
37143730
c_norm += 1.0;
37153731
norm_B = max(c_norm, norm_B);
37163732
}
3717-
double cond_B = norm_Binv * norm_B;
3718-
double exact_cond_B = exact_norm_Binv * norm_B;
3719-
if (exact) {
3720-
assert(exact_norm_Binv > 0);
3721-
if (report)
3722-
highsLogUser(
3723-
options_->log_options, HighsLogType::kInfo,
3724-
"HEkk::computeBasisCondition: grep_kappa model,||B||_1,approx "
3725-
"||B^{-1}||_1,approx_kappa,||B^{-1}||_1,kappa = ,%s,%g,%g,%g,%g,%g\n",
3726-
lp.model_name_.c_str(), norm_B, norm_Binv, cond_B, exact_norm_Binv,
3727-
exact_cond_B);
3728-
return exact_cond_B;
3729-
}
3733+
double cond_B = norm_B * norm_Binv;
37303734
return cond_B;
37313735
}
37323736

highs/simplex/HEkk.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,10 @@ class HEkk {
127127
HighsBasis getHighsBasis(HighsLp& use_lp) const;
128128

129129
const SimplexBasis& getSimplexBasis() { return basis_; }
130-
double computeBasisCondition(const HighsLp& lp, const bool exact = false,
131-
const bool report = false) const;
132-
double computeBasisCondition() const {
133-
return computeBasisCondition(this->lp_, false, false);
134-
}
130+
131+
void testBasisCondition(const std::string& message = "") const;
132+
double computeBasisCondition(const HighsLp& lp,
133+
const bool exact = false) const;
135134

136135
HighsStatus initialiseSimplexLpBasisAndFactor(
137136
const bool only_from_known_basis = false);

0 commit comments

Comments
 (0)