/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* */ /* 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 util/HighsUtils.cpp * @brief Class-independent utilities for HiGHS */ #include "util/HighsUtils.h" #include #include #include #include #include "util/HighsSort.h" bool create(HighsIndexCollection& index_collection, const HighsInt from_col, const HighsInt to_col, const HighsInt dimension) { if (from_col < 0) return false; if (to_col >= dimension) return false; index_collection.dimension_ = dimension; index_collection.is_interval_ = true; index_collection.from_ = from_col; index_collection.to_ = to_col; return true; } bool create(HighsIndexCollection& index_collection, const HighsInt num_set_entries, const HighsInt* set, const HighsInt dimension) { // Create an index collection for the given set - so long as it is strictly // ordered index_collection.dimension_ = dimension; index_collection.is_set_ = true; index_collection.set_ = {set, set + num_set_entries}; index_collection.set_num_entries_ = num_set_entries; if (!increasingSetOk(index_collection.set_, 1, 0, true)) return false; return true; } void create(HighsIndexCollection& index_collection, const HighsInt* mask, const HighsInt dimension) { // Create an index collection for the given mask index_collection.dimension_ = dimension; index_collection.is_mask_ = true; index_collection.mask_ = {mask, mask + dimension}; } void highsSparseTranspose(HighsInt numRow, HighsInt numCol, const std::vector& Astart, const std::vector& Aindex, const std::vector& Avalue, std::vector& ARstart, std::vector& ARindex, std::vector& ARvalue) { // Make a AR copy std::vector iwork(numRow, 0); ARstart.resize(numRow + 1, 0); HighsInt AcountX = Aindex.size(); ARindex.resize(AcountX); ARvalue.resize(AcountX); for (HighsInt k = 0; k < AcountX; k++) { assert(Aindex[k] < numRow); iwork[Aindex[k]]++; } for (HighsInt i = 1; i <= numRow; i++) ARstart[i] = ARstart[i - 1] + iwork[i - 1]; for (HighsInt i = 0; i < numRow; i++) iwork[i] = ARstart[i]; for (HighsInt iCol = 0; iCol < numCol; iCol++) { for (HighsInt k = Astart[iCol]; k < Astart[iCol + 1]; k++) { HighsInt iRow = Aindex[k]; HighsInt iPut = iwork[iRow]++; ARindex[iPut] = iCol; ARvalue[iPut] = Avalue[k]; } } } bool ok(const HighsIndexCollection& index_collection) { // Check parameter for each technique of defining an index collection if (index_collection.is_interval_) { // Changing by interval: check the parameters and that check set and mask // are false if (index_collection.is_set_) { printf("Index collection is both interval and set\n"); return false; } if (index_collection.is_mask_) { printf("Index collection is both interval and mask\n"); return false; } if (index_collection.from_ < 0) { printf("Index interval lower limit is %" HIGHSINT_FORMAT " < 0\n", index_collection.from_); return false; } if (index_collection.to_ > index_collection.dimension_ - 1) { printf("Index interval upper limit is %" HIGHSINT_FORMAT " > %" HIGHSINT_FORMAT "\n", index_collection.to_, index_collection.dimension_ - 1); return false; } } else if (index_collection.is_set_) { // Changing by set: check the parameters and check that interval and mask // are false if (index_collection.is_interval_) { printf("Index collection is both set and interval\n"); return false; } if (index_collection.is_mask_) { printf("Index collection is both set and mask\n"); return false; } if (index_collection.set_.empty()) { printf("Index set is NULL\n"); return false; } // Check that the values in the vector of integers are ascending const vector& set = index_collection.set_; const HighsInt num_entries = index_collection.set_num_entries_; const HighsInt entry_upper = index_collection.dimension_ - 1; HighsInt prev_set_entry = -1; for (HighsInt k = 0; k < num_entries; k++) { if (set[k] < 0 || set[k] > entry_upper) { printf("Index set entry set[%" HIGHSINT_FORMAT "] = %" HIGHSINT_FORMAT " is out of bounds [0, %" HIGHSINT_FORMAT "]\n", k, set[k], entry_upper); return false; } if (set[k] <= prev_set_entry) { printf("Index set entry set[%" HIGHSINT_FORMAT "] = %" HIGHSINT_FORMAT " is not greater than " "previous entry %" HIGHSINT_FORMAT "\n", k, set[k], prev_set_entry); return false; } prev_set_entry = set[k]; } // This was the old check done independently, and should be // equivalent. assert(increasingSetOk(set, 0, entry_upper, true)); } else if (index_collection.is_mask_) { // Changing by mask: check the parameters and check that set and interval // are false if (index_collection.mask_.empty()) { printf("Index mask is NULL\n"); return false; } if (index_collection.is_interval_) { printf("Index collection is both mask and interval\n"); return false; } if (index_collection.is_set_) { printf("Index collection is both mask and set\n"); return false; } } else { // No method defined printf("Undefined index collection\n"); return false; } return true; } void limits(const HighsIndexCollection& index_collection, HighsInt& from_k, HighsInt& to_k) { if (index_collection.is_interval_) { from_k = index_collection.from_; to_k = index_collection.to_; } else if (index_collection.is_set_) { from_k = 0; to_k = index_collection.set_num_entries_ - 1; } else if (index_collection.is_mask_) { from_k = 0; to_k = index_collection.dimension_ - 1; } else { assert(1 == 0); } } void updateOutInIndex(const HighsIndexCollection& index_collection, HighsInt& out_from_ix, HighsInt& out_to_ix, HighsInt& in_from_ix, HighsInt& in_to_ix, HighsInt& current_set_entry) { if (index_collection.is_interval_) { out_from_ix = index_collection.from_; out_to_ix = index_collection.to_; in_from_ix = index_collection.to_ + 1; in_to_ix = index_collection.dimension_ - 1; } else if (index_collection.is_set_) { out_from_ix = index_collection.set_[current_set_entry]; out_to_ix = out_from_ix; current_set_entry++; HighsInt current_set_entry0 = current_set_entry; for (HighsInt set_entry = current_set_entry0; set_entry < index_collection.set_num_entries_; set_entry++) { HighsInt ix = index_collection.set_[set_entry]; if (ix > out_to_ix + 1) break; out_to_ix = index_collection.set_[current_set_entry]; current_set_entry++; } in_from_ix = out_to_ix + 1; if (current_set_entry < index_collection.set_num_entries_) { in_to_ix = index_collection.set_[current_set_entry] - 1; } else { // Account for getting to the end of the set in_to_ix = index_collection.dimension_ - 1; } } else { out_from_ix = in_to_ix + 1; out_to_ix = index_collection.dimension_ - 1; for (HighsInt ix = in_to_ix + 1; ix < index_collection.dimension_; ix++) { if (!index_collection.mask_[ix]) { out_to_ix = ix - 1; break; } } in_from_ix = out_to_ix + 1; in_to_ix = index_collection.dimension_ - 1; for (HighsInt ix = out_to_ix + 1; ix < index_collection.dimension_; ix++) { if (index_collection.mask_[ix]) { in_to_ix = ix - 1; break; } } } } HighsInt dataSize(const HighsIndexCollection& index_collection) { if (index_collection.is_set_) { return index_collection.set_num_entries_; } else { if (index_collection.is_interval_) { return index_collection.to_ - index_collection.from_ + 1; } else { return index_collection.dimension_; } } } bool highsVarTypeUserDataNotNull(const HighsLogOptions& log_options, const HighsVarType* user_data, const std::string name) { bool null_data = false; if (user_data == NULL) { highsLogUser(log_options, HighsLogType::kError, "User-supplied %s are NULL\n", name.c_str()); null_data = true; } assert(!null_data); return null_data; } bool intUserDataNotNull(const HighsLogOptions& log_options, const HighsInt* user_data, const std::string name) { bool null_data = false; if (user_data == NULL) { highsLogUser(log_options, HighsLogType::kError, "User-supplied %s are NULL\n", name.c_str()); null_data = true; } assert(!null_data); return null_data; } bool doubleUserDataNotNull(const HighsLogOptions& log_options, const double* user_data, const std::string name) { bool null_data = false; if (user_data == NULL) { highsLogUser(log_options, HighsLogType::kError, "User-supplied %s are NULL\n", name.c_str()); null_data = true; } assert(!null_data); return null_data; } double getNorm2(const std::vector values) { double sum = 0; HighsInt values_size = values.size(); for (HighsInt i = 0; i < values_size; i++) sum += values[i] * values[i]; return sum; } bool highs_isInfinity(double val) { if (val >= kHighsInf) return true; return false; } double highsRelativeDifference(const double v0, const double v1) { return fabs(v0 - v1) / std::max(v0, std::max(v1, 1.0)); } void analyseVectorValues(const HighsLogOptions* log_options, const std::string message, HighsInt vecDim, const std::vector& vec, bool analyseValueList, std::string model_name) { if (vecDim == 0) return; double log10 = log(10.0); const HighsInt nVK = 20; HighsInt nNz = 0; HighsInt nPosInfV = 0; HighsInt nNegInfV = 0; std::vector posVK; std::vector negVK; posVK.resize(nVK + 1, 0); negVK.resize(nVK + 1, 0); const HighsInt VLsMxZ = 10; std::vector VLsK; std::vector VLsV; VLsK.resize(VLsMxZ, 0); VLsV.resize(VLsMxZ, 0); // Ensure that 1.0 and -1.0 are counted const HighsInt PlusOneIx = 0; const HighsInt MinusOneIx = 1; bool excessVLsV = false; HighsInt VLsZ = 2; VLsV[PlusOneIx] = 1.0; VLsV[MinusOneIx] = -1.0; double min_abs_value = kHighsInf; double max_abs_value = 0; for (HighsInt ix = 0; ix < vecDim; ix++) { double v = vec[ix]; double absV = std::fabs(v); if (absV) { min_abs_value = std::min(absV, min_abs_value); max_abs_value = std::max(absV, max_abs_value); } HighsInt log10V; if (absV > 0) { // Nonzero value nNz++; if (highs_isInfinity(-v)) { //-Inf value nNegInfV++; } else if (highs_isInfinity(v)) { //+Inf value nPosInfV++; } else { // Finite nonzero value if (absV == 1) { log10V = 0; } else if (absV == 10) { log10V = 1; } else if (absV == 100) { log10V = 2; } else if (absV == 1000) { log10V = 3; } else { log10V = log(absV) / log10; } if (log10V >= 0) { HighsInt k = std::min(log10V, nVK); posVK[k]++; } else { HighsInt k = std::min(-log10V, nVK); negVK[k]++; } } } if (analyseValueList) { if (v == 1.0) { VLsK[PlusOneIx]++; } else if (v == -1.0) { VLsK[MinusOneIx]++; } else { HighsInt fdIx = -1; for (HighsInt ix = 2; ix < VLsZ; ix++) { if (v == VLsV[ix]) { fdIx = ix; break; } } if (fdIx == -1) { // New value if (VLsZ < VLsMxZ) { fdIx = VLsZ; VLsV[fdIx] = v; VLsK[fdIx]++; VLsZ++; } else { excessVLsV = true; } } else { // Existing value VLsK[fdIx]++; } } } } highsReportDevInfo( log_options, highsFormatToString( "%s of dimension %" HIGHSINT_FORMAT " with %" HIGHSINT_FORMAT " nonzeros (%3" HIGHSINT_FORMAT "%%) in [%11.4g, %11.4g]\n", message.c_str(), vecDim, nNz, 100 * nNz / vecDim, min_abs_value, max_abs_value)); if (nNegInfV > 0) highsReportDevInfo( log_options, highsFormatToString( "%12" HIGHSINT_FORMAT " values are -Inf\n", nNegInfV)); if (nPosInfV > 0) highsReportDevInfo( log_options, highsFormatToString( "%12" HIGHSINT_FORMAT " values are +Inf\n", nPosInfV)); HighsInt k = nVK; HighsInt vK = posVK[k]; if (vK > 0) highsReportDevInfo(log_options, highsFormatToString( "%12" HIGHSINT_FORMAT " values satisfy 10^(%3" HIGHSINT_FORMAT ") <= v < Inf\n", vK, k)); for (HighsInt k = nVK - 1; k >= 0; k--) { HighsInt vK = posVK[k]; if (vK > 0) highsReportDevInfo( log_options, highsFormatToString("%12" HIGHSINT_FORMAT " values satisfy 10^(%3" HIGHSINT_FORMAT ") <= v < 10^(%3" HIGHSINT_FORMAT ")\n", vK, k, k + 1)); } for (HighsInt k = 1; k <= nVK; k++) { HighsInt vK = negVK[k]; if (vK > 0) highsReportDevInfo( log_options, highsFormatToString("%12" HIGHSINT_FORMAT " values satisfy 10^(%3" HIGHSINT_FORMAT ") <= v < 10^(%3" HIGHSINT_FORMAT ")\n", vK, -k, 1 - k)); } vK = vecDim - nNz; if (vK > 0) highsReportDevInfo( log_options, highsFormatToString("%12" HIGHSINT_FORMAT " values are zero\n", vK)); if (analyseValueList) { highsReportDevInfo(log_options, highsFormatToString(" Value distribution:")); if (excessVLsV) highsReportDevInfo( log_options, highsFormatToString( " More than %" HIGHSINT_FORMAT " different values", VLsZ)); highsReportDevInfo( log_options, highsFormatToString("\n Value Count\n")); for (HighsInt ix = 0; ix < VLsZ; ix++) { HighsInt pct = ((100.0 * VLsK[ix]) / vecDim) + 0.5; highsReportDevInfo(log_options, highsFormatToString(" %12g %12" HIGHSINT_FORMAT " (%3" HIGHSINT_FORMAT "%%)\n", VLsV[ix], VLsK[ix], pct)); } highsReportDevInfo( log_options, highsFormatToString("grep_value_distrib,%s,%" HIGHSINT_FORMAT "", model_name.c_str(), VLsZ)); highsReportDevInfo(log_options, highsFormatToString(",")); if (excessVLsV) highsReportDevInfo(log_options, highsFormatToString("!")); for (HighsInt ix = 0; ix < VLsZ; ix++) highsReportDevInfo(log_options, highsFormatToString(",%g", VLsV[ix])); highsReportDevInfo(log_options, highsFormatToString("\n")); } } void analyseMatrixSparsity(const HighsLogOptions& log_options, const char* message, HighsInt numCol, HighsInt numRow, const std::vector& Astart, const std::vector& Aindex) { if (numCol == 0) return; std::vector rowCount; std::vector colCount; rowCount.assign(numRow, 0); colCount.resize(numCol); for (HighsInt col = 0; col < numCol; col++) { colCount[col] = Astart[col + 1] - Astart[col]; for (HighsInt el = Astart[col]; el < Astart[col + 1]; el++) rowCount[Aindex[el]]++; } const HighsInt maxCat = 10; std::vector CatV; std::vector rowCatK; std::vector colCatK; CatV.resize(maxCat + 1); rowCatK.assign(maxCat + 1, 0); colCatK.assign(maxCat + 1, 0); CatV[1] = 1; for (HighsInt cat = 2; cat < maxCat + 1; cat++) { CatV[cat] = 2 * CatV[cat - 1]; } HighsInt maxRowCount = 0; HighsInt maxColCount = 0; for (HighsInt col = 0; col < numCol; col++) { maxColCount = std::max(colCount[col], maxColCount); HighsInt fdCat = maxCat; for (HighsInt cat = 0; cat < maxCat - 1; cat++) { if (colCount[col] < CatV[cat + 1]) { fdCat = cat; break; } } colCatK[fdCat]++; } for (HighsInt row = 0; row < numRow; row++) { maxRowCount = std::max(rowCount[row], maxRowCount); HighsInt fdCat = maxCat; for (HighsInt cat = 0; cat < maxCat - 1; cat++) { if (rowCount[row] < CatV[cat + 1]) { fdCat = cat; break; } } rowCatK[fdCat]++; } highsLogDev(log_options, HighsLogType::kInfo, "\n%s\n\n", message); HighsInt lastRpCat = -1; for (HighsInt cat = 0; cat < maxCat + 1; cat++) { if (colCatK[cat]) lastRpCat = cat; } HighsInt cat = maxCat; if (colCatK[cat]) lastRpCat = cat; HighsInt pct; double v; for (HighsInt cat = 0; cat < lastRpCat; cat++) { v = 100 * colCatK[cat]; v = v / numCol + 0.5; pct = v; highsLogDev(log_options, HighsLogType::kInfo, "%12" HIGHSINT_FORMAT " (%3" HIGHSINT_FORMAT "%%) columns of count in [%3" HIGHSINT_FORMAT ", %3" HIGHSINT_FORMAT "]\n", colCatK[cat], pct, CatV[cat], CatV[cat + 1] - 1); } cat = lastRpCat; v = 100 * colCatK[cat]; v = v / numCol + 0.5; pct = v; if (cat == maxCat) { highsLogDev(log_options, HighsLogType::kInfo, "%12" HIGHSINT_FORMAT " (%3" HIGHSINT_FORMAT "%%) columns of count in [%3" HIGHSINT_FORMAT ", inf]\n", colCatK[cat], pct, CatV[cat]); } else { highsLogDev(log_options, HighsLogType::kInfo, "%12" HIGHSINT_FORMAT " (%3" HIGHSINT_FORMAT "%%) columns of count in [%3" HIGHSINT_FORMAT ", %3" HIGHSINT_FORMAT "]\n", colCatK[cat], pct, CatV[cat], CatV[cat + 1] - 1); } highsLogDev(log_options, HighsLogType::kInfo, "Max count is %" HIGHSINT_FORMAT " / %" HIGHSINT_FORMAT "\n\n", maxColCount, numRow); lastRpCat = -1; for (HighsInt cat = 0; cat < maxCat + 1; cat++) { if (rowCatK[cat]) lastRpCat = cat; } cat = maxCat; if (rowCatK[cat]) lastRpCat = cat; pct = 0; v = 0; for (HighsInt cat = 0; cat < lastRpCat; cat++) { v = 100 * rowCatK[cat]; v = v / numRow + 0.5; pct = v; highsLogDev(log_options, HighsLogType::kInfo, "%12" HIGHSINT_FORMAT " (%3" HIGHSINT_FORMAT "%%) rows of count in [%3" HIGHSINT_FORMAT ", %3" HIGHSINT_FORMAT "]\n", rowCatK[cat], pct, CatV[cat], CatV[cat + 1] - 1); } cat = lastRpCat; v = 100 * rowCatK[cat]; v = v / numRow + 0.5; pct = v; if (cat == maxCat) { highsLogDev(log_options, HighsLogType::kInfo, "%12" HIGHSINT_FORMAT " (%3" HIGHSINT_FORMAT "%%) rows of count in [%3" HIGHSINT_FORMAT ", inf]\n", rowCatK[cat], pct, CatV[cat]); } else { highsLogDev(log_options, HighsLogType::kInfo, "%12" HIGHSINT_FORMAT " (%3" HIGHSINT_FORMAT "%%) rows of count in [%3" HIGHSINT_FORMAT ", %3" HIGHSINT_FORMAT "]\n", rowCatK[cat], pct, CatV[cat], CatV[cat + 1] - 1); } highsLogDev(log_options, HighsLogType::kInfo, "Max count is %" HIGHSINT_FORMAT " / %" HIGHSINT_FORMAT "\n", maxRowCount, numCol); } bool initialiseValueDistribution(const std::string distribution_name, const std::string value_name, const double min_value_limit, const double max_value_limit, const double base_value_limit, HighsValueDistribution& value_distribution) { assert(min_value_limit > 0); assert(max_value_limit > 0); assert(base_value_limit > 1); value_distribution.distribution_name_ = distribution_name; value_distribution.value_name_ = value_name; if (min_value_limit <= 0) return false; if (max_value_limit < min_value_limit) return false; HighsInt num_count; if (min_value_limit == max_value_limit) { // For counting values below and above a value num_count = 1; } else { if (base_value_limit <= 0) return false; const double log_ratio = log(max_value_limit / min_value_limit); const double log_base_value_limit = log(base_value_limit); // printf("initialiseValueDistribution: log_ratio = %g; // log_base_value_limit = %g; log_ratio/log_base_value_limit = %g\n", // log_ratio, log_base_value_limit, log_ratio/log_base_value_limit); num_count = log_ratio / log_base_value_limit + 1; } // printf("initialiseValueDistribution: num_count = %" HIGHSINT_FORMAT "\n", // num_count); value_distribution.count_.assign(num_count + 1, 0); value_distribution.limit_.assign(num_count, 0); value_distribution.limit_[0] = min_value_limit; // printf("Interval 0 is [%10.4g, %10.4g)\n", 0.0, // value_distribution.limit_[0]); for (HighsInt i = 1; i < num_count; i++) { value_distribution.limit_[i] = base_value_limit * value_distribution.limit_[i - 1]; // printf("Interval %2" HIGHSINT_FORMAT " is [%10.4g, %10.4g)\n", i, // value_distribution.limit_[i-1], value_distribution.limit_[i]); } // printf("Interval %2" HIGHSINT_FORMAT " is [%10.4g, inf)\n", num_count, // value_distribution.limit_[num_count-1]); value_distribution.num_count_ = num_count; value_distribution.num_zero_ = 0; value_distribution.num_one_ = 0; value_distribution.min_value_ = kHighsInf; value_distribution.max_value_ = 0; value_distribution.sum_count_ = 0; return true; } bool updateValueDistribution(const double value, HighsValueDistribution& value_distribution) { if (value_distribution.num_count_ < 0) return false; value_distribution.sum_count_++; const double abs_value = fabs(value); value_distribution.min_value_ = std::min(abs_value, value_distribution.min_value_); value_distribution.max_value_ = std::max(abs_value, value_distribution.max_value_); if (!abs_value) { value_distribution.num_zero_++; return true; } if (abs_value == 1.0) { value_distribution.num_one_++; return true; } for (HighsInt i = 0; i < value_distribution.num_count_; i++) { if (abs_value < value_distribution.limit_[i]) { value_distribution.count_[i]++; return true; } } value_distribution.count_[value_distribution.num_count_]++; return true; } double doublePercentage(const HighsInt of, const HighsInt in) { return ((100.0 * of) / in); } HighsInt integerPercentage(const HighsInt of, const HighsInt in) { const double double_percentage = ((100.0 * of) / in) + 0.4999; return (HighsInt)double_percentage; } bool logValueDistribution(const HighsLogOptions& log_options, const HighsValueDistribution& value_distribution, const HighsInt mu) { if (value_distribution.sum_count_ <= 0) return false; const HighsInt num_count = value_distribution.num_count_; if (num_count < 0) return false; if (value_distribution.distribution_name_ != "") highsLogDev(log_options, HighsLogType::kInfo, "\n%s\n", value_distribution.distribution_name_.c_str()); std::string value_name = value_distribution.value_name_; bool not_reported_ones = true; HighsInt sum_count = value_distribution.num_zero_ + value_distribution.num_one_; const double min_value = value_distribution.min_value_; for (HighsInt i = 0; i < num_count + 1; i++) sum_count += value_distribution.count_[i]; if (!sum_count) return false; highsLogDev(log_options, HighsLogType::kInfo, "Min value = %g\n", min_value); highsLogDev(log_options, HighsLogType::kInfo, " Minimum %svalue is %10.4g", value_name.c_str(), min_value); if (mu > 0) { highsLogDev(log_options, HighsLogType::kInfo, " corresponding to %10" HIGHSINT_FORMAT " / %10" HIGHSINT_FORMAT "\n", (HighsInt)(min_value * mu), mu); } else { highsLogDev(log_options, HighsLogType::kInfo, "\n"); } highsLogDev(log_options, HighsLogType::kInfo, " Maximum %svalue is %10.4g", value_name.c_str(), value_distribution.max_value_); if (mu > 0) { highsLogDev(log_options, HighsLogType::kInfo, " corresponding to %10" HIGHSINT_FORMAT " / %10" HIGHSINT_FORMAT "\n", (HighsInt)(value_distribution.max_value_ * mu), mu); } else { highsLogDev(log_options, HighsLogType::kInfo, "\n"); } HighsInt sum_report_count = 0; double percentage; HighsInt int_percentage; HighsInt count = value_distribution.num_zero_; if (count) { percentage = doublePercentage(count, sum_count); int_percentage = percentage; highsLogDev(log_options, HighsLogType::kInfo, "%12" HIGHSINT_FORMAT " %svalues (%3" HIGHSINT_FORMAT "%%) are %10.4g\n", count, value_name.c_str(), int_percentage, 0.0); sum_report_count += count; } count = value_distribution.count_[0]; if (count) { percentage = doublePercentage(count, sum_count); int_percentage = percentage; highsLogDev(log_options, HighsLogType::kInfo, "%12" HIGHSINT_FORMAT " %svalues (%3" HIGHSINT_FORMAT "%%) in (%10.4g, %10.4g)", count, value_name.c_str(), int_percentage, 0.0, value_distribution.limit_[0]); sum_report_count += count; if (mu > 0) { highsLogDev(log_options, HighsLogType::kInfo, " corresponding to (%10" HIGHSINT_FORMAT ", %10" HIGHSINT_FORMAT ")\n", 0, (HighsInt)(value_distribution.limit_[0] * mu)); } else { highsLogDev(log_options, HighsLogType::kInfo, "\n"); } } for (HighsInt i = 1; i < num_count; i++) { if (not_reported_ones && value_distribution.limit_[i - 1] >= 1.0) { count = value_distribution.num_one_; if (count) { percentage = doublePercentage(count, sum_count); int_percentage = percentage; highsLogDev(log_options, HighsLogType::kInfo, "%12" HIGHSINT_FORMAT " %svalues (%3" HIGHSINT_FORMAT "%%) are %10.4g", count, value_name.c_str(), int_percentage, 1.0); sum_report_count += count; if (mu > 0) { highsLogDev(log_options, HighsLogType::kInfo, " corresponding to %10" HIGHSINT_FORMAT "\n", mu); } else { highsLogDev(log_options, HighsLogType::kInfo, "\n"); } } not_reported_ones = false; } count = value_distribution.count_[i]; if (count) { percentage = doublePercentage(count, sum_count); int_percentage = percentage; highsLogDev(log_options, HighsLogType::kInfo, "%12" HIGHSINT_FORMAT " %svalues (%3" HIGHSINT_FORMAT "%%) in [%10.4g, %10.4g)", count, value_name.c_str(), int_percentage, value_distribution.limit_[i - 1], value_distribution.limit_[i]); sum_report_count += count; if (mu > 0) { highsLogDev(log_options, HighsLogType::kInfo, " corresponding to [%10" HIGHSINT_FORMAT ", %10" HIGHSINT_FORMAT ")\n", (HighsInt)(value_distribution.limit_[i - 1] * mu), (HighsInt)(value_distribution.limit_[i] * mu)); } else { highsLogDev(log_options, HighsLogType::kInfo, "\n"); } } } if (not_reported_ones && value_distribution.limit_[num_count - 1] >= 1.0) { count = value_distribution.num_one_; if (count) { percentage = doublePercentage(count, sum_count); int_percentage = percentage; highsLogDev(log_options, HighsLogType::kInfo, "%12" HIGHSINT_FORMAT " %svalues (%3" HIGHSINT_FORMAT "%%) are %10.4g", count, value_name.c_str(), int_percentage, 1.0); sum_report_count += count; if (mu > 0) { highsLogDev(log_options, HighsLogType::kInfo, " corresponding to %10" HIGHSINT_FORMAT "\n", mu); } else { highsLogDev(log_options, HighsLogType::kInfo, "\n"); } } not_reported_ones = false; } count = value_distribution.count_[num_count]; if (count) { percentage = doublePercentage(count, sum_count); int_percentage = percentage; highsLogDev(log_options, HighsLogType::kInfo, "%12" HIGHSINT_FORMAT " %svalues (%3" HIGHSINT_FORMAT "%%) in [%10.4g, inf)", count, value_name.c_str(), int_percentage, value_distribution.limit_[num_count - 1]); sum_report_count += count; if (mu > 0) { highsLogDev(log_options, HighsLogType::kInfo, " corresponding to [%10" HIGHSINT_FORMAT ", inf)\n", (HighsInt)(value_distribution.limit_[num_count - 1] * mu)); } else { highsLogDev(log_options, HighsLogType::kInfo, "\n"); } } if (not_reported_ones) { count = value_distribution.num_one_; if (count) { percentage = doublePercentage(count, sum_count); int_percentage = percentage; highsLogDev(log_options, HighsLogType::kInfo, "%12" HIGHSINT_FORMAT " %svalues (%3" HIGHSINT_FORMAT "%%) are %10.4g", count, value_name.c_str(), int_percentage, 1.0); sum_report_count += count; if (mu > 0) { highsLogDev(log_options, HighsLogType::kInfo, " corresponding to %10" HIGHSINT_FORMAT "\n", mu); } else { highsLogDev(log_options, HighsLogType::kInfo, "\n"); } } } highsLogDev(log_options, HighsLogType::kInfo, "%12" HIGHSINT_FORMAT " %svalues\n", sum_count, value_name.c_str()); if (sum_report_count != sum_count) highsLogDev(log_options, HighsLogType::kInfo, "ERROR: %" HIGHSINT_FORMAT " = sum_report_count != sum_count = %" HIGHSINT_FORMAT "\n", sum_report_count, sum_count); return true; } bool initialiseScatterData(const HighsInt max_num_point, HighsScatterData& scatter_data) { if (max_num_point < 1) return false; scatter_data.max_num_point_ = max_num_point; scatter_data.num_point_ = 0; scatter_data.last_point_ = -1; scatter_data.value0_.resize(max_num_point); scatter_data.value1_.resize(max_num_point); scatter_data.have_regression_coeff_ = false; scatter_data.num_error_comparison_ = 0; scatter_data.num_awful_linear_ = 0; scatter_data.num_awful_log_ = 0; scatter_data.num_bad_linear_ = 0; scatter_data.num_bad_log_ = 0; scatter_data.num_fair_linear_ = 0; scatter_data.num_fair_log_ = 0; scatter_data.num_better_linear_ = 0; scatter_data.num_better_log_ = 0; return true; } bool updateScatterData(const double value0, const double value1, HighsScatterData& scatter_data) { if (value0 <= 0 || value0 <= 0) return false; scatter_data.num_point_++; scatter_data.last_point_++; if (scatter_data.last_point_ == scatter_data.max_num_point_) scatter_data.last_point_ = 0; scatter_data.value0_[scatter_data.last_point_] = value0; scatter_data.value1_[scatter_data.last_point_] = value1; return true; } bool regressScatterData(HighsScatterData& scatter_data) { if (scatter_data.num_point_ < 5) return true; double log_x; double log_y; double sum_log_x = 0; double sum_log_y = 0; double sum_log_xlog_x = 0; double sum_log_xlog_y = 0; double x; double y; double sum_x = 0; double sum_y = 0; double sum_xx = 0; double sum_xy = 0; HighsInt point_num = 0; for (HighsInt pass = 0; pass < 2; pass++) { HighsInt from_point; HighsInt to_point; if (pass == 0) { from_point = scatter_data.last_point_; to_point = std::min(scatter_data.num_point_, scatter_data.max_num_point_); } else { from_point = 0; to_point = scatter_data.last_point_; } for (HighsInt point = from_point; point < to_point; point++) { point_num++; x = scatter_data.value0_[point]; y = scatter_data.value1_[point]; sum_x += x; sum_y += y; sum_xx += x * x; sum_xy += x * y; log_x = log(x); log_y = log(y); sum_log_x += log_x; sum_log_y += log_y; sum_log_xlog_x += log_x * log_x; sum_log_xlog_y += log_x * log_y; } } double double_num = 1.0 * point_num; // Linear regression double det = double_num * sum_xx - sum_x * sum_x; if (fabs(det) < 1e-8) return true; scatter_data.linear_coeff0_ = (sum_xx * sum_y - sum_x * sum_xy) / det; scatter_data.linear_coeff1_ = (-sum_x * sum_y + double_num * sum_xy) / det; // Log regression det = double_num * sum_log_xlog_x - sum_log_x * sum_log_x; if (fabs(det) < 1e-8) return true; scatter_data.log_coeff0_ = (sum_log_xlog_x * sum_log_y - sum_log_x * sum_log_xlog_y) / det; scatter_data.log_coeff0_ = exp(scatter_data.log_coeff0_); scatter_data.log_coeff1_ = (-sum_log_x * sum_log_y + double_num * sum_log_xlog_y) / det; // Look at the errors in the two approaches scatter_data.have_regression_coeff_ = true; if (scatter_data.num_point_ < scatter_data.max_num_point_) return true; scatter_data.num_error_comparison_++; computeScatterDataRegressionError(scatter_data); const double linear_error = scatter_data.linear_regression_error_; const double log_error = scatter_data.log_regression_error_; const bool report_awful_error = false; if (linear_error > awful_regression_error || log_error > awful_regression_error) { if (linear_error > awful_regression_error) { scatter_data.num_awful_linear_++; if (report_awful_error) printf("Awful linear regression error = %g\n", linear_error); } if (log_error > awful_regression_error) { scatter_data.num_awful_log_++; if (report_awful_error) printf("Awful log regression error = %g\n", log_error); } if (report_awful_error) computeScatterDataRegressionError(scatter_data, true); } if (linear_error > bad_regression_error) scatter_data.num_bad_linear_++; if (log_error > bad_regression_error) scatter_data.num_bad_log_++; if (linear_error > fair_regression_error) scatter_data.num_fair_linear_++; if (log_error > fair_regression_error) scatter_data.num_fair_log_++; if (linear_error < log_error) { scatter_data.num_better_linear_++; } else if (linear_error > log_error) { scatter_data.num_better_log_++; } // printf("Linear regression error = %g\n", linear_error); // printf("Log regression error = %g\n", log_error); return true; } bool predictFromScatterData(const HighsScatterData& scatter_data, const double value0, double& predicted_value1, const bool log_regression) { if (!scatter_data.have_regression_coeff_) return false; if (log_regression) { predicted_value1 = scatter_data.log_coeff0_ * pow(value0, scatter_data.log_coeff1_); return true; } else { predicted_value1 = scatter_data.linear_coeff0_ + scatter_data.linear_coeff1_ * value0; return true; } } bool computeScatterDataRegressionError(HighsScatterData& scatter_data, const bool print) { if (!scatter_data.have_regression_coeff_) return false; if (scatter_data.num_point_ < scatter_data.max_num_point_) return false; double sum_log_error = 0; if (print) printf( "Log regression\nPoint Value0 Value1 PredValue1 Error\n"); for (HighsInt point = 0; point < scatter_data.max_num_point_; point++) { double value0 = scatter_data.value0_[point]; double value1 = scatter_data.value1_[point]; double predicted_value1; if (predictFromScatterData(scatter_data, value0, predicted_value1, true)) { double error = fabs(predicted_value1 - value1); // / fabs(value1); if ( // 10*error > awful_regression_error && print) printf("%5" HIGHSINT_FORMAT " %10.4g %10.4g %10.4g %10.4g\n", point, value0, value1, predicted_value1, error); sum_log_error += error; } } if (print) printf(" %10.4g\n", sum_log_error); double sum_linear_error = 0; if (print) printf( "Linear regression\nPoint Value0 Value1 PredValue1 " "Error\n"); for (HighsInt point = 0; point < scatter_data.max_num_point_; point++) { double value0 = scatter_data.value0_[point]; double value1 = scatter_data.value1_[point]; double predicted_value1; if (predictFromScatterData(scatter_data, value0, predicted_value1)) { double error = fabs(predicted_value1 - value1); // / fabs(value1); if ( // 10*error > awful_regression_error && print) printf("%5" HIGHSINT_FORMAT " %10.4g %10.4g %10.4g %10.4g\n", point, value0, value1, predicted_value1, error); sum_linear_error += error; } } if (print) printf(" %10.4g\n", sum_linear_error); scatter_data.log_regression_error_ = sum_log_error; scatter_data.linear_regression_error_ = sum_linear_error; return true; } bool printScatterData(std::string name, const HighsScatterData& scatter_data) { if (!scatter_data.num_point_) return true; double x; double y; HighsInt point_num = 0; printf("%s scatter data\n", name.c_str()); const HighsInt to_point = std::min(scatter_data.num_point_, scatter_data.max_num_point_); for (HighsInt point = scatter_data.last_point_ + 1; point < to_point; point++) { point_num++; x = scatter_data.value0_[point]; y = scatter_data.value1_[point]; printf("%" HIGHSINT_FORMAT ",%10.4g,%10.4g,%" HIGHSINT_FORMAT "\n", point, x, y, point_num); } for (HighsInt point = 0; point <= scatter_data.last_point_; point++) { point_num++; x = scatter_data.value0_[point]; y = scatter_data.value1_[point]; printf("%" HIGHSINT_FORMAT ",%10.4g,%10.4g,%" HIGHSINT_FORMAT "\n", point, x, y, point_num); } printf("Linear regression coefficients,%10.4g,%10.4g\n", scatter_data.linear_coeff0_, scatter_data.linear_coeff1_); printf("Log regression coefficients,%10.4g,%10.4g\n", scatter_data.log_coeff0_, scatter_data.log_coeff1_); return true; } void printScatterDataRegressionComparison( std::string name, const HighsScatterData& scatter_data) { if (!scatter_data.num_error_comparison_) return; printf("\n%s scatter data regression\n", name.c_str()); printf("%10" HIGHSINT_FORMAT " regression error comparisons\n", scatter_data.num_error_comparison_); printf("%10" HIGHSINT_FORMAT " regression awful linear (>%10.4g)\n", scatter_data.num_awful_linear_, awful_regression_error); printf("%10" HIGHSINT_FORMAT " regression awful log (>%10.4g)\n", scatter_data.num_awful_log_, awful_regression_error); printf("%10" HIGHSINT_FORMAT " regression bad linear (>%10.4g)\n", scatter_data.num_bad_linear_, bad_regression_error); printf("%10" HIGHSINT_FORMAT " regression bad log (>%10.4g)\n", scatter_data.num_bad_log_, bad_regression_error); printf("%10" HIGHSINT_FORMAT " regression fair linear (>%10.4g)\n", scatter_data.num_fair_linear_, fair_regression_error); printf("%10" HIGHSINT_FORMAT " regression fair log (>%10.4g)\n", scatter_data.num_fair_log_, fair_regression_error); printf("%10" HIGHSINT_FORMAT " regression better linear\n", scatter_data.num_better_linear_); printf("%10" HIGHSINT_FORMAT " regression better log\n", scatter_data.num_better_log_); } double nearestPowerOfTwoScale(const double value) { int exp_scale; // Decompose value into a normalized fraction and an integral power // of two. // // If arg is zero, returns zero and stores zero in *exp. Otherwise // (if arg is not zero), if no errors occur, returns the value x in // the range (-1;-0.5], [0.5; 1) and stores an integer value in *exp // such that x×2(*exp)=arg std::frexp(value, &exp_scale); exp_scale = -exp_scale; // Multiply a floating point value x(=1) by the number 2 raised to // the exp power double scale = std::ldexp(1, exp_scale); return scale; } void highsAssert(const bool assert_condition, const std::string message) { if (assert_condition) return; printf("Failing highsAssert(\"%s\")\n", message.c_str()); #ifdef NDEBUG // Standard assert won't trigger abort, so do it explicitly printf("assert(%s) failed ...\n", message.c_str()); fflush(stdout); abort(); #else // Standard assert assert(assert_condition); #endif } bool highsPause(const bool pause_condition, const std::string message) { if (!pause_condition) return pause_condition; printf("Satisfying highsPause(\"%s\")\n", message.c_str()); char str[100]; printf("Enter any value to continue:"); fflush(stdout); if (fgets(str, 100, stdin) != nullptr) { printf("You entered: \"%s\"\n", str); fflush(stdout); } return pause_condition; }