/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* */ /* 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 simplex/HSimplexNlaDebug.cpp * * @brief Debugging code for simplex NLA */ #include "simplex/HSimplexNla.h" #include "util/HighsRandom.h" //#include const double kResidualLargeError = 1e-8; const double kResidualExcessiveError = sqrt(kResidualLargeError); const double kSolveLargeError = 1e-8; const double kSolveExcessiveError = sqrt(kSolveLargeError); const double kInverseLargeError = 1e-8; const double kInverseExcessiveError = sqrt(kInverseLargeError); HighsDebugStatus HSimplexNla::debugCheckInvert( const std::string message, const HighsInt alt_debug_level) const { // Sometimes a value other than highs_debug_level is passed as // alt_debug_level, either to force debugging, or to limit // debugging. If no value is passed, then alt_debug_level = -1, and // highs_debug_level is used 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; // If highs_debug_level isn't being used, then indicate that it's // being forced, and also force reporting of OK errors 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& a_matrix_start = this->lp_->a_matrix_.start_; const vector& a_matrix_index = this->lp_->a_matrix_.index_; const vector& a_matrix_value = this->lp_->a_matrix_.value_; const HighsInt* basic_index = this->basic_index_; const HighsOptions* options = this->options_; // Make sure that this isn't called between the matrix and LP resizing 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; 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) { // Solve for a random solution HighsRandom random(1); // Solve Bx=b 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; // if (options->log_dev_level) { // reportArray("Random solution before FTRAN", &column, true); // reportArray("Random RHS before FTRAN", &rhs, true); // } this->ftran(rhs, expected_density); return_status = debugReportInvertSolutionError(false, column, rhs, residual, force); } const bool random_btran_test = true; if (random_btran_test) { // Solve B^Tx=b 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; HighsLogType report_level; expected_density = 0; double inverse_error_norm = 0; double residual_error_norm = 0; const bool test_inverse_ftran = true; if (test_inverse_ftran) { // Solve BX=B 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); // factor_.reportLu(kReportLuBoth, 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); } // Extra printing of intermediate errors 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) { // Solve B^TX=B^T 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); // factor_.reportLu(kReportLuBoth, 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); } // Extra printing of intermediate errors 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& a_matrix_start = this->lp_->a_matrix_.start_; const vector& a_matrix_index = this->lp_->a_matrix_.index_; const vector& 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_; const HighsOptions* options = this->options_; 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; // printf("%s\n", value_adjective.c_str()); // printf("%g\n", solve_error_norm); // printf("%s\n", type.c_str()); // printf("%s\n", source.c_str()); 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; }