#include "simplex/HSimplexNla.h"
#include "util/HighsRandom.h"
const double kResidualLargeError = 1e-8;
const double kResidualExcessiveError = sqrt(kResidualLargeError);
const double kSolveLargeError = 1e-8;
const double kSolveExcessiveError = sqrt(kSolveLargeError);
HighsDebugStatus HSimplexNla::debugCheckInvert(
const std::string message, const HighsInt alt_debug_level) const {
const HighsInt use_debug_level = alt_debug_level >= 0
? alt_debug_level
: this->options_->highs_debug_level;
if (use_debug_level < kHighsDebugLevelCostly)
return HighsDebugStatus::kNotChecked;
const bool force = alt_debug_level > this->options_->highs_debug_level;
if (force)
highsLogDev(this->options_->log_options, HighsLogType::kInfo,
"CheckNlaINVERT: Forcing debug\n");
HighsDebugStatus return_status = HighsDebugStatus::kNotChecked;
return_status = HighsDebugStatus::kOk;
const HighsInt num_row = this->lp_->num_row_;
const HighsInt num_col = this->lp_->num_col_;
const vector<HighsInt>& a_matrix_start = this->lp_->a_matrix_.start_;
const vector<HighsInt>& a_matrix_index = this->lp_->a_matrix_.index_;
const vector<double>& a_matrix_value = this->lp_->a_matrix_.value_;
const HighsInt* basic_index = this->basic_index_;
const HighsOptions* options = this->options_;
assert(num_row == this->lp_->a_matrix_.num_row_);
assert(num_col == this->lp_->a_matrix_.num_col_);
const bool report = (options->log_dev_level != 0);
highsLogDev(options->log_options, HighsLogType::kInfo, "\nCheckINVERT: %s\n",
message.c_str());
HVector column;
HVector rhs;
column.setup(num_row);
rhs.setup(num_row);
HVector residual;
double expected_density = 1;
const bool random_ftran_test = true;
const bool report_basis = options->log_dev_level > 1 && num_row < 20;
if (random_ftran_test) {
HighsRandom random(1);
column.clear();
rhs.clear();
column.count = -1;
if (report_basis)
highsLogDev(options_->log_options, HighsLogType::kInfo, "Basis:");
for (HighsInt iRow = 0; iRow < num_row; iRow++) {
rhs.index[rhs.count++] = iRow;
double value = random.fraction();
column.array[iRow] = value;
HighsInt iCol = basic_index[iRow];
if (report_basis)
highsLogDev(options_->log_options, HighsLogType::kInfo, " %1d",
(int)iCol);
if (iCol < num_col) {
for (HighsInt iEl = a_matrix_start[iCol];
iEl < a_matrix_start[iCol + 1]; iEl++) {
HighsInt index = a_matrix_index[iEl];
rhs.array[index] += value * a_matrix_value[iEl];
}
} else {
HighsInt index = iCol - num_col;
assert(index < num_row);
rhs.array[index] += value;
}
}
if (report_basis)
highsLogDev(options_->log_options, HighsLogType::kInfo, "\n");
residual = rhs;
this->ftran(rhs, expected_density);
return_status =
debugReportInvertSolutionError(false, column, rhs, residual, force);
}
const bool random_btran_test = true;
if (random_btran_test) {
rhs.clear();
for (HighsInt iRow = 0; iRow < num_row; iRow++) {
rhs.index[rhs.count++] = iRow;
HighsInt iCol = basic_index[iRow];
if (iCol < num_col) {
for (HighsInt iEl = a_matrix_start[iCol];
iEl < a_matrix_start[iCol + 1]; iEl++) {
rhs.array[iRow] +=
column.array[a_matrix_index[iEl]] * a_matrix_value[iEl];
}
} else {
rhs.array[iRow] += column.array[iCol - num_col];
}
}
residual = rhs;
this->btran(rhs, expected_density);
return_status =
debugReportInvertSolutionError(true, column, rhs, residual, force);
}
if (use_debug_level < kHighsDebugLevelExpensive) return return_status;
std::string value_adjective;
expected_density = 0;
double inverse_error_norm = 0;
double residual_error_norm = 0;
const bool test_inverse_ftran = true;
if (test_inverse_ftran) {
HighsInt check_col = -1;
for (HighsInt iRow = 0; iRow < num_row; iRow++) {
HighsInt iCol = basic_index[iRow];
column.clear();
column.packFlag = true;
if (iCol < num_col) {
for (HighsInt k = a_matrix_start[iCol]; k < a_matrix_start[iCol + 1];
k++) {
HighsInt index = a_matrix_index[k];
column.array[index] = a_matrix_value[k];
column.index[column.count++] = index;
}
} else {
HighsInt index = iCol - num_col;
column.array[index] = 1.0;
column.index[column.count++] = index;
}
const bool report_col = report && iRow == check_col;
if (report_col) {
reportArray("Check col before FTRAN", &column, true);
}
HVector residual = column;
this->ftran(column, expected_density);
if (report_col) reportArray("Check col after FTRAN", &column, true);
double inverse_col_error_norm = 0;
for (HighsInt lc_iRow = 0; lc_iRow < num_row; lc_iRow++) {
double ckValue = lc_iRow == iRow ? 1 : 0;
double inverse_error = fabs(column.array[lc_iRow] - ckValue);
inverse_col_error_norm =
std::max(inverse_error, inverse_col_error_norm);
}
if (report_col)
highsLogDev(
options->log_options, HighsLogType::kInfo,
"CheckINVERT: Basic column %2d = %2d has inverse error %11.4g\n",
(int)iRow, int(iCol), inverse_col_error_norm);
inverse_error_norm = std::max(inverse_col_error_norm, inverse_error_norm);
double residual_col_error_norm =
debugInvertResidualError(false, column, residual);
residual_error_norm =
std::max(residual_col_error_norm, residual_error_norm);
}
return_status = debugReportInvertSolutionError(
"inverse", false, inverse_error_norm, residual_error_norm, force);
}
const bool test_inverse_btran = true;
if (test_inverse_btran) {
HighsInt check_row = -2;
inverse_error_norm = 0;
residual_error_norm = 0;
for (HighsInt iRow = 0; iRow < num_row; iRow++) {
column.clear();
column.packFlag = true;
for (HighsInt iCol = 0; iCol < num_row; iCol++) {
HighsInt iVar = basic_index[iCol];
if (iVar < num_col) {
for (HighsInt k = a_matrix_start[iVar]; k < a_matrix_start[iVar + 1];
k++) {
if (a_matrix_index[k] == iRow) {
column.array[iCol] = a_matrix_value[k];
break;
}
}
} else {
if (iVar == num_col + iRow) column.array[iCol] = 1.0;
}
}
for (HighsInt iCol = 0; iCol < num_row; iCol++) {
if (column.array[iCol]) column.index[column.count++] = iCol;
}
const bool report_row = report && iRow == check_row;
if (report_row) {
reportArray("Check col before BTRAN", &column, true);
}
HVector residual = column;
this->btran(column, expected_density);
if (report_row) reportArray("Check col after BTRAN", &column, true);
double inverse_col_error_norm = 0;
for (HighsInt lc_iRow = 0; lc_iRow < num_row; lc_iRow++) {
double value = column.array[lc_iRow];
double ckValue = lc_iRow == iRow ? 1 : 0;
double inverse_error = fabs(value - ckValue);
inverse_col_error_norm =
std::max(inverse_error, inverse_col_error_norm);
}
if (report_row)
highsLogDev(
options->log_options, HighsLogType::kInfo,
"CheckINVERT: Basis matrix row %2d has inverse error %11.4g\n",
(int)iRow, inverse_col_error_norm);
inverse_error_norm = std::max(inverse_col_error_norm, inverse_error_norm);
double residual_col_error_norm =
debugInvertResidualError(true, column, residual);
residual_error_norm =
std::max(residual_col_error_norm, residual_error_norm);
}
return_status = debugReportInvertSolutionError(
"inverse", true, inverse_error_norm, residual_error_norm, force);
}
return return_status;
}
double HSimplexNla::debugInvertResidualError(const bool transposed,
const HVector& solution,
HVector& residual) const {
const HighsInt num_row = this->lp_->num_row_;
const HighsInt num_col = this->lp_->num_col_;
const vector<HighsInt>& a_matrix_start = this->lp_->a_matrix_.start_;
const vector<HighsInt>& a_matrix_index = this->lp_->a_matrix_.index_;
const vector<double>& a_matrix_value = this->lp_->a_matrix_.value_;
const HighsInt* basic_index = this->basic_index_;
if (transposed) {
for (HighsInt iRow = 0; iRow < num_row; iRow++) {
HighsInt iCol = basic_index[iRow];
if (iCol < num_col) {
for (HighsInt iEl = a_matrix_start[iCol];
iEl < a_matrix_start[iCol + 1]; iEl++) {
HighsInt index = a_matrix_index[iEl];
double value = solution.array[index];
residual.array[iRow] -= value * a_matrix_value[iEl];
}
} else {
HighsInt index = iCol - num_col;
double value = solution.array[index];
residual.array[iRow] -= value;
}
}
} else {
for (HighsInt iRow = 0; iRow < num_row; iRow++) {
double value = solution.array[iRow];
HighsInt iCol = basic_index[iRow];
if (iCol < num_col) {
for (HighsInt iEl = a_matrix_start[iCol];
iEl < a_matrix_start[iCol + 1]; iEl++) {
HighsInt index = a_matrix_index[iEl];
residual.array[index] -= value * a_matrix_value[iEl];
}
} else {
HighsInt index = iCol - num_col;
residual.array[index] -= value;
}
}
}
double residual_error_norm = 0;
for (HighsInt iRow = 0; iRow < num_row; iRow++) {
double residual_error = fabs(residual.array[iRow]);
residual_error_norm = std::max(residual_error, residual_error_norm);
}
return residual_error_norm;
}
HighsDebugStatus HSimplexNla::debugReportInvertSolutionError(
const bool transposed, const HVector& true_solution,
const HVector& solution, HVector& residual, const bool force) const {
const HighsInt num_row = this->lp_->num_row_;
double solve_error_norm = 0;
for (HighsInt iRow = 0; iRow < num_row; iRow++) {
double solve_error = fabs(solution.array[iRow] - true_solution.array[iRow]);
solve_error_norm = std::max(solve_error, solve_error_norm);
}
double residual_error_norm =
debugInvertResidualError(transposed, solution, residual);
return debugReportInvertSolutionError("random solution", transposed,
solve_error_norm, residual_error_norm,
force);
}
HighsDebugStatus HSimplexNla::debugReportInvertSolutionError(
const std::string source, const bool transposed,
const double solve_error_norm, const double residual_error_norm,
const bool force) const {
const HighsOptions* options = this->options_;
std::string value_adjective;
HighsLogType report_level;
HighsDebugStatus return_status = HighsDebugStatus::kOk;
std::string type = "";
if (transposed) type = "transposed ";
if (solve_error_norm) {
if (solve_error_norm > kSolveExcessiveError) {
value_adjective = "Excessive";
report_level = HighsLogType::kError;
} else if (solve_error_norm > kSolveLargeError) {
value_adjective = "Large";
report_level = HighsLogType::kWarning;
} else {
value_adjective = "Small";
report_level = HighsLogType::kInfo;
}
if (force) report_level = HighsLogType::kInfo;
highsLogDev(options->log_options, report_level,
"CheckINVERT: %-9s (%9.4g) norm for %s%s solve error\n",
value_adjective.c_str(), solve_error_norm, type.c_str(),
source.c_str());
}
if (residual_error_norm) {
if (residual_error_norm > kResidualExcessiveError) {
value_adjective = "Excessive";
report_level = HighsLogType::kError;
return_status = HighsDebugStatus::kError;
} else if (residual_error_norm > kResidualLargeError) {
value_adjective = "Large";
report_level = HighsLogType::kWarning;
return_status = HighsDebugStatus::kWarning;
} else {
value_adjective = "Small";
report_level = HighsLogType::kInfo;
}
if (force) report_level = HighsLogType::kInfo;
highsLogDev(options->log_options, report_level,
"CheckINVERT: %-9s (%9.4g) norm for %s%s "
"residual error\n",
value_adjective.c_str(), residual_error_norm, type.c_str(),
source.c_str());
}
return return_status;
}