#include "lp_data/HighsOptions.h"
#include <algorithm>
#include <cassert>
#include <cctype>
#include "util/stringutil.h"
void highsOpenLogFile(HighsLogOptions& log_options,
std::vector<OptionRecord*>& option_records,
const std::string& log_file) {
HighsInt index;
OptionStatus status =
getOptionIndex(log_options, "log_file", option_records, index);
assert(status == OptionStatus::kOk);
if (log_options.log_stream != nullptr) {
fflush(log_options.log_stream);
fclose(log_options.log_stream);
log_options.log_stream = nullptr;
}
assert(!log_options.log_stream);
if (log_file.compare(""))
log_options.log_stream = fopen(log_file.c_str(), "a");
OptionRecordString& option = *(OptionRecordString*)option_records[index];
option.assignvalue(log_file);
}
static std::string optionEntryTypeToString(const HighsOptionType type) {
if (type == HighsOptionType::kBool) {
return "bool";
} else if (type == HighsOptionType::kInt) {
return "HighsInt";
} else if (type == HighsOptionType::kDouble) {
return "double";
} else {
return "string";
}
}
bool optionOffChooseOnOk(const HighsLogOptions& report_log_options,
const string& name, const string& value) {
if (value == kHighsOffString || value == kHighsChooseString ||
value == kHighsOnString)
return true;
highsLogUser(
report_log_options, HighsLogType::kError,
"Value \"%s\" for %s option is not one of \"%s\", \"%s\" or \"%s\"\n",
value.c_str(), name.c_str(), kHighsOffString.c_str(),
kHighsChooseString.c_str(), kHighsOnString.c_str());
return false;
}
bool optionOffOnOk(const HighsLogOptions& report_log_options,
const string& name, const string& value) {
if (value == kHighsOffString || value == kHighsOnString) return true;
highsLogUser(report_log_options, HighsLogType::kError,
"Value \"%s\" for %s option is not one of \"%s\" or \"%s\"\n",
value.c_str(), name.c_str(), kHighsOffString.c_str(),
kHighsOnString.c_str());
return false;
}
bool optionSolverOk(const HighsLogOptions& report_log_options,
const string& value) {
#ifndef HIPO
if (value == kHipoString) {
highsLogUser(
report_log_options, HighsLogType::kError,
"The HiPO solver was requested via the \"%s\" option, but this build "
"was compiled without HiPO support. Reconfigure with FAST_BUILD=ON "
"and -DHIPO=ON to enable HiPO.\n",
kSolverString.c_str());
return false;
}
#endif
if (value == kHighsChooseString || value == kSimplexString ||
value == kIpmString ||
#ifdef HIPO
value == kHipoString ||
#endif
value == kIpxString || value == kPdlpString)
return true;
highsLogUser(report_log_options, HighsLogType::kError,
"Value \"%s\" for LP solver option (\"%s\") is not one of "
#ifdef HIPO
"\"%s\", "
#endif
"\"%s\", \"%s\", \"%s\", \"%s\" or \"%s\"\n",
value.c_str(), kSolverString.c_str(), kHighsChooseString.c_str(),
kSimplexString.c_str(), kIpmString.c_str(),
#ifdef HIPO
kHipoString.c_str(),
#endif
kIpxString.c_str(), kPdlpString.c_str());
return false;
}
bool optionMipLpSolverOk(const HighsLogOptions& report_log_options,
const string& value) {
#ifndef HIPO
if (value == kHipoString) {
highsLogUser(
report_log_options, HighsLogType::kError,
"The HiPO solver was requested via the \"%s\" option, but this build "
"was compiled without HiPO support. Reconfigure with FAST_BUILD=ON "
"and -DHIPO=ON to enable HiPO.\n",
kMipLpSolverString.c_str());
return false;
}
#endif
if (value == kHighsChooseString || value == kSimplexString ||
value == kIpmString ||
#ifdef HIPO
value == kHipoString ||
#endif
value == kIpxString)
return true;
highsLogUser(report_log_options, HighsLogType::kError,
"Value \"%s\" for MIP LP solver option (\"%s\") is not one of "
#ifdef HIPO
"\"%s\", "
#endif
"\"%s\", \"%s\", \"%s\" or \"%s\"\n",
value.c_str(), kMipLpSolverString.c_str(),
kHighsChooseString.c_str(), kSimplexString.c_str(),
kIpmString.c_str(),
#ifdef HIPO
kHipoString.c_str(),
#endif
kIpxString.c_str());
return false;
}
bool optionMipIpmSolverOk(const HighsLogOptions& report_log_options,
const string& value) {
#ifndef HIPO
if (value == kHipoString) {
highsLogUser(
report_log_options, HighsLogType::kError,
"The HiPO solver was requested via the \"%s\" option, but this build "
"was compiled without HiPO support. Reconfigure with FAST_BUILD=ON "
"and -DHIPO=ON to enable HiPO.\n",
kMipIpmSolverString.c_str());
return false;
}
#endif
if (value == kHighsChooseString || value == kIpmString ||
#ifdef HIPO
value == kHipoString ||
#endif
value == kIpxString)
return true;
highsLogUser(report_log_options, HighsLogType::kError,
"Value \"%s\" for MIP IPM solver (\"%s\") option is not one of "
#ifdef HIPO
"\"%s\", "
#endif
"\"%s\", \"%s\" or \"%s\"\n",
value.c_str(), kMipIpmSolverString.c_str(),
kHighsChooseString.c_str(), kIpmString.c_str(),
#ifdef HIPO
kHipoString.c_str(),
#endif
kIpxString.c_str());
return false;
}
bool boolFromString(std::string value, bool& bool_value) {
std::transform(value.begin(), value.end(), value.begin(),
[](unsigned char c) { return std::tolower(c); });
if (value == "t" || value == "true" || value == "1" ||
value == kHighsOnString) {
bool_value = true;
} else if (value == "f" || value == "false" || value == "0" ||
value == kHighsOffString) {
bool_value = false;
} else {
return false;
}
return true;
}
OptionStatus getOptionIndex(const HighsLogOptions& report_log_options,
const std::string& name,
const std::vector<OptionRecord*>& option_records,
HighsInt& index) {
HighsInt num_options = option_records.size();
for (index = 0; index < num_options; index++)
if (option_records[index]->name == name) return OptionStatus::kOk;
highsLogUser(report_log_options, HighsLogType::kError,
"getOptionIndex: Option \"%s\" is unknown\n", name.c_str());
return OptionStatus::kUnknownOption;
}
OptionStatus checkOptions(const HighsLogOptions& report_log_options,
const std::vector<OptionRecord*>& option_records) {
bool error_found = false;
HighsInt num_options = option_records.size();
for (HighsInt index = 0; index < num_options; index++) {
std::string name = option_records[index]->name;
HighsOptionType type = option_records[index]->type;
for (HighsInt check_index = 0; check_index < num_options; check_index++) {
if (check_index == index) continue;
std::string check_name = option_records[check_index]->name;
if (check_name == name) {
highsLogUser(report_log_options, HighsLogType::kError,
"checkOptions: Option %" HIGHSINT_FORMAT
" (\"%s\") has the same name as "
"option %" HIGHSINT_FORMAT " \"%s\"\n",
index, name.c_str(), check_index, check_name.c_str());
error_found = true;
}
}
if (type == HighsOptionType::kBool) {
OptionRecordBool& option = ((OptionRecordBool*)option_records[index])[0];
bool* value_pointer = option.value;
for (HighsInt check_index = 0; check_index < num_options; check_index++) {
if (check_index == index) continue;
if (option_records[check_index]->type == HighsOptionType::kBool) {
OptionRecordBool& check_option =
((OptionRecordBool*)option_records[check_index])[0];
if (check_option.value == value_pointer) {
highsLogUser(report_log_options, HighsLogType::kError,
"checkOptions: Option %" HIGHSINT_FORMAT
" (\"%s\") has the same "
"value pointer as option %" HIGHSINT_FORMAT
" (\"%s\")\n",
index, option.name.c_str(), check_index,
check_option.name.c_str());
error_found = true;
}
}
}
} else if (type == HighsOptionType::kInt) {
OptionRecordInt& option = ((OptionRecordInt*)option_records[index])[0];
if (checkOption(report_log_options, option) != OptionStatus::kOk)
error_found = true;
HighsInt* value_pointer = option.value;
for (HighsInt check_index = 0; check_index < num_options; check_index++) {
if (check_index == index) continue;
if (option_records[check_index]->type == HighsOptionType::kInt) {
OptionRecordInt& check_option =
((OptionRecordInt*)option_records[check_index])[0];
if (check_option.value == value_pointer) {
highsLogUser(report_log_options, HighsLogType::kError,
"checkOptions: Option %" HIGHSINT_FORMAT
" (\"%s\") has the same "
"value pointer as option %" HIGHSINT_FORMAT
" (\"%s\")\n",
index, option.name.c_str(), check_index,
check_option.name.c_str());
error_found = true;
}
}
}
} else if (type == HighsOptionType::kDouble) {
OptionRecordDouble& option =
((OptionRecordDouble*)option_records[index])[0];
if (checkOption(report_log_options, option) != OptionStatus::kOk)
error_found = true;
double* value_pointer = option.value;
for (HighsInt check_index = 0; check_index < num_options; check_index++) {
if (check_index == index) continue;
if (option_records[check_index]->type == HighsOptionType::kDouble) {
OptionRecordDouble& check_option =
((OptionRecordDouble*)option_records[check_index])[0];
if (check_option.value == value_pointer) {
highsLogUser(report_log_options, HighsLogType::kError,
"checkOptions: Option %" HIGHSINT_FORMAT
" (\"%s\") has the same "
"value pointer as option %" HIGHSINT_FORMAT
" (\"%s\")\n",
index, option.name.c_str(), check_index,
check_option.name.c_str());
error_found = true;
}
}
}
} else if (type == HighsOptionType::kString) {
OptionRecordString& option =
((OptionRecordString*)option_records[index])[0];
std::string* value_pointer = option.value;
for (HighsInt check_index = 0; check_index < num_options; check_index++) {
if (check_index == index) continue;
if (option_records[check_index]->type == HighsOptionType::kString) {
OptionRecordString& check_option =
((OptionRecordString*)option_records[check_index])[0];
if (check_option.value == value_pointer) {
highsLogUser(report_log_options, HighsLogType::kError,
"checkOptions: Option %" HIGHSINT_FORMAT
" (\"%s\") has the same "
"value pointer as option %" HIGHSINT_FORMAT
" (\"%s\")\n",
index, option.name.c_str(), check_index,
check_option.name.c_str());
error_found = true;
}
}
}
}
}
if (error_found) return OptionStatus::kIllegalValue;
highsLogUser(report_log_options, HighsLogType::kInfo,
"checkOptions: Options are OK\n");
return OptionStatus::kOk;
}
OptionStatus checkOption(const HighsLogOptions& report_log_options,
const OptionRecordInt& option) {
if (option.lower_bound > option.upper_bound) {
highsLogUser(
report_log_options, HighsLogType::kError,
"checkOption: Option \"%s\" has inconsistent bounds [%" HIGHSINT_FORMAT
", %" HIGHSINT_FORMAT "]\n",
option.name.c_str(), option.lower_bound, option.upper_bound);
return OptionStatus::kIllegalValue;
}
if (option.default_value < option.lower_bound ||
option.default_value > option.upper_bound) {
highsLogUser(
report_log_options, HighsLogType::kError,
"checkOption: Option \"%s\" has default value %" HIGHSINT_FORMAT
" "
"inconsistent with bounds [%" HIGHSINT_FORMAT ", %" HIGHSINT_FORMAT
"]\n",
option.name.c_str(), option.default_value, option.lower_bound,
option.upper_bound);
return OptionStatus::kIllegalValue;
}
HighsInt value = *option.value;
if (value < option.lower_bound || value > option.upper_bound) {
highsLogUser(report_log_options, HighsLogType::kError,
"checkOption: Option \"%s\" has value %" HIGHSINT_FORMAT
" inconsistent with "
"bounds [%" HIGHSINT_FORMAT ", %" HIGHSINT_FORMAT "]\n",
option.name.c_str(), value, option.lower_bound,
option.upper_bound);
return OptionStatus::kIllegalValue;
}
return OptionStatus::kOk;
}
OptionStatus checkOption(const HighsLogOptions& report_log_options,
const OptionRecordDouble& option) {
if (option.lower_bound > option.upper_bound) {
highsLogUser(
report_log_options, HighsLogType::kError,
"checkOption: Option \"%s\" has inconsistent bounds [%g, %g]\n",
option.name.c_str(), option.lower_bound, option.upper_bound);
return OptionStatus::kIllegalValue;
}
if (option.default_value < option.lower_bound ||
option.default_value > option.upper_bound) {
highsLogUser(report_log_options, HighsLogType::kError,
"checkOption: Option \"%s\" has default value %g "
"inconsistent with bounds [%g, %g]\n",
option.name.c_str(), option.default_value, option.lower_bound,
option.upper_bound);
return OptionStatus::kIllegalValue;
}
double value = *option.value;
if (value < option.lower_bound || value > option.upper_bound) {
highsLogUser(report_log_options, HighsLogType::kError,
"checkOption: Option \"%s\" has value %g inconsistent with "
"bounds [%g, %g]\n",
option.name.c_str(), value, option.lower_bound,
option.upper_bound);
return OptionStatus::kIllegalValue;
}
return OptionStatus::kOk;
}
OptionStatus checkOptionValue(const HighsLogOptions& report_log_options,
OptionRecordInt& option, const HighsInt value) {
if (value < option.lower_bound) {
highsLogUser(report_log_options, HighsLogType::kError,
"checkOptionValue: Value %" HIGHSINT_FORMAT
" for option \"%s\" is below "
"lower bound of %" HIGHSINT_FORMAT "\n",
value, option.name.c_str(), option.lower_bound);
return OptionStatus::kIllegalValue;
} else if (value > option.upper_bound) {
highsLogUser(report_log_options, HighsLogType::kError,
"checkOptionValue: Value %" HIGHSINT_FORMAT
" for option \"%s\" is above "
"upper bound of %" HIGHSINT_FORMAT "\n",
value, option.name.c_str(), option.upper_bound);
return OptionStatus::kIllegalValue;
}
return OptionStatus::kOk;
}
OptionStatus checkOptionValue(const HighsLogOptions& report_log_options,
OptionRecordDouble& option, const double value) {
if (value < option.lower_bound) {
highsLogUser(report_log_options, HighsLogType::kError,
"checkOptionValue: Value %g for option \"%s\" is below "
"lower bound of %g\n",
value, option.name.c_str(), option.lower_bound);
return OptionStatus::kIllegalValue;
} else if (value > option.upper_bound) {
highsLogUser(report_log_options, HighsLogType::kError,
"checkOptionValue: Value %g for option \"%s\" is above "
"upper bound of %g\n",
value, option.name.c_str(), option.upper_bound);
return OptionStatus::kIllegalValue;
}
return OptionStatus::kOk;
}
OptionStatus checkOptionValue(const HighsLogOptions& report_log_options,
OptionRecordString& option,
const std::string& value) {
if (option.name == kPresolveString) {
if (!optionOffChooseOnOk(report_log_options, option.name, value) &&
value != "mip")
return OptionStatus::kIllegalValue;
} else if (option.name == kSolverString) {
if (!optionSolverOk(report_log_options, value))
return OptionStatus::kIllegalValue;
} else if (option.name == kMipLpSolverString) {
if (!optionMipLpSolverOk(report_log_options, value))
return OptionStatus::kIllegalValue;
} else if (option.name == kMipIpmSolverString) {
if (!optionMipIpmSolverOk(report_log_options, value))
return OptionStatus::kIllegalValue;
} else if (option.name == kParallelString) {
if (!optionOffChooseOnOk(report_log_options, option.name, value))
return OptionStatus::kIllegalValue;
} else if (option.name == kRunCrossoverString) {
if (!optionOffChooseOnOk(report_log_options, option.name, value))
return OptionStatus::kIllegalValue;
} else if (option.name == kRangingString) {
if (!optionOffOnOk(report_log_options, option.name, value))
return OptionStatus::kIllegalValue;
}
return OptionStatus::kOk;
}
OptionStatus setLocalOptionValue(const HighsLogOptions& report_log_options,
const std::string& name,
std::vector<OptionRecord*>& option_records,
const bool value) {
HighsInt index;
OptionStatus status =
getOptionIndex(report_log_options, name, option_records, index);
if (status != OptionStatus::kOk) return status;
HighsOptionType type = option_records[index]->type;
if (type != HighsOptionType::kBool) {
highsLogUser(
report_log_options, HighsLogType::kError,
"setLocalOptionValue: Option \"%s\" cannot be assigned a bool\n",
name.c_str());
return OptionStatus::kIllegalValue;
}
return setLocalOptionValue(((OptionRecordBool*)option_records[index])[0],
value);
}
OptionStatus setLocalOptionValue(const HighsLogOptions& report_log_options,
const std::string& name,
std::vector<OptionRecord*>& option_records,
const HighsInt value) {
HighsInt index;
OptionStatus status =
getOptionIndex(report_log_options, name, option_records, index);
if (status != OptionStatus::kOk) return status;
HighsOptionType type = option_records[index]->type;
if (type != HighsOptionType::kInt) {
if (type == HighsOptionType::kDouble) {
double use_value = value;
return setLocalOptionValue(
report_log_options, ((OptionRecordDouble*)option_records[index])[0],
use_value);
}
highsLogUser(
report_log_options, HighsLogType::kError,
"setLocalOptionValue: Option \"%s\" cannot be assigned an int\n",
name.c_str());
return OptionStatus::kIllegalValue;
}
return setLocalOptionValue(
report_log_options, ((OptionRecordInt*)option_records[index])[0], value);
}
OptionStatus setLocalOptionValue(const HighsLogOptions& report_log_options,
const std::string& name,
std::vector<OptionRecord*>& option_records,
const double value) {
HighsInt index;
OptionStatus status =
getOptionIndex(report_log_options, name, option_records, index);
if (status != OptionStatus::kOk) return status;
HighsOptionType type = option_records[index]->type;
if (type != HighsOptionType::kDouble) {
highsLogUser(
report_log_options, HighsLogType::kError,
"setLocalOptionValue: Option \"%s\" cannot be assigned a double\n",
name.c_str());
return OptionStatus::kIllegalValue;
}
return setLocalOptionValue(report_log_options,
((OptionRecordDouble*)option_records[index])[0],
value);
}
OptionStatus setLocalOptionValue(const HighsLogOptions& report_log_options,
const std::string& name,
HighsLogOptions& log_options,
std::vector<OptionRecord*>& option_records,
const std::string& value_passed) {
std::string value_trim = value_passed;
trim(value_trim, " ");
HighsInt index;
OptionStatus status =
getOptionIndex(report_log_options, name, option_records, index);
if (status != OptionStatus::kOk) return status;
HighsOptionType type = option_records[index]->type;
if (type == HighsOptionType::kBool) {
bool value_bool;
bool return_status = boolFromString(value_trim, value_bool);
if (!return_status) {
highsLogUser(
report_log_options, HighsLogType::kError,
"setLocalOptionValue: Value \"%s\" cannot be interpreted as a bool\n",
value_trim.c_str());
return OptionStatus::kIllegalValue;
}
return setLocalOptionValue(((OptionRecordBool*)option_records[index])[0],
value_bool);
} else if (type == HighsOptionType::kInt) {
if (value_trim.find_first_not_of("+-0123456789eE") != std::string::npos)
return OptionStatus::kIllegalValue;
HighsInt value_int;
int scanned_num_char;
const char* value_char = value_trim.c_str();
sscanf(value_char, "%" HIGHSINT_FORMAT "%n", &value_int, &scanned_num_char);
const int value_num_char = strlen(value_char);
const bool converted_ok = scanned_num_char == value_num_char;
if (!converted_ok) {
highsLogDev(report_log_options, HighsLogType::kError,
"setLocalOptionValue: Value = \"%s\" converts via sscanf as "
"%" HIGHSINT_FORMAT
" "
"by scanning %" HIGHSINT_FORMAT " of %" HIGHSINT_FORMAT
" characters\n",
value_trim.c_str(), value_int, scanned_num_char,
value_num_char);
return OptionStatus::kIllegalValue;
}
return setLocalOptionValue(report_log_options,
((OptionRecordInt*)option_records[index])[0],
value_int);
} else if (type == HighsOptionType::kDouble) {
double value_double = 0;
tolower(value_trim);
if (value_trim == "inf" || value_trim == "+inf") {
value_double = kHighsInf;
} else if (value_trim == "-inf") {
value_double = -kHighsInf;
} else {
if (value_trim.find_first_not_of("+-.0123456789eE") != std::string::npos)
return OptionStatus::kIllegalValue;
HighsInt value_int = atoi(value_trim.c_str());
value_double = atof(value_trim.c_str());
double value_int_double = value_int;
if (value_double == value_int_double) {
highsLogDev(report_log_options, HighsLogType::kInfo,
"setLocalOptionValue: Value = \"%s\" converts via atoi as "
"%" HIGHSINT_FORMAT
" "
"so is %g as double, and %g via atof\n",
value_trim.c_str(), value_int, value_int_double,
value_double);
}
}
return setLocalOptionValue(report_log_options,
((OptionRecordDouble*)option_records[index])[0],
value_double);
} else {
if (!name.compare(kLogFileString)) {
OptionRecordString& option = *(OptionRecordString*)option_records[index];
std::string original_log_file = *(option.value);
if (value_passed.compare(original_log_file)) {
highsOpenLogFile(log_options, option_records, value_passed);
}
}
if (!name.compare(kModelFileString)) {
highsLogUser(report_log_options, HighsLogType::kError,
"setLocalOptionValue: model filename cannot be set\n");
return OptionStatus::kUnknownOption;
} else {
return setLocalOptionValue(
report_log_options, ((OptionRecordString*)option_records[index])[0],
value_passed);
}
}
}
OptionStatus setLocalOptionValue(const HighsLogOptions& report_log_options,
const std::string& name,
HighsLogOptions& log_options,
std::vector<OptionRecord*>& option_records,
const char* value) {
std::string value_as_string(value);
return setLocalOptionValue(report_log_options, name, log_options,
option_records, value_as_string);
}
OptionStatus setLocalOptionValue(OptionRecordBool& option, const bool value) {
option.assignvalue(value);
return OptionStatus::kOk;
}
OptionStatus setLocalOptionValue(const HighsLogOptions& report_log_options,
OptionRecordInt& option,
const HighsInt value) {
OptionStatus return_status =
checkOptionValue(report_log_options, option, value);
if (return_status != OptionStatus::kOk) return return_status;
option.assignvalue(value);
return OptionStatus::kOk;
}
OptionStatus setLocalOptionValue(const HighsLogOptions& report_log_options,
OptionRecordDouble& option,
const double value) {
OptionStatus return_status =
checkOptionValue(report_log_options, option, value);
if (return_status != OptionStatus::kOk) return return_status;
option.assignvalue(value);
return OptionStatus::kOk;
}
OptionStatus setLocalOptionValue(const HighsLogOptions& report_log_options,
OptionRecordString& option,
const std::string& value) {
OptionStatus return_status =
checkOptionValue(report_log_options, option, value);
if (return_status != OptionStatus::kOk) return return_status;
option.assignvalue(value);
return OptionStatus::kOk;
}
OptionStatus passLocalOptions(const HighsLogOptions& report_log_options,
const HighsOptions& from_options,
HighsOptions& to_options) {
OptionStatus return_status;
HighsInt num_options = to_options.records.size();
for (HighsInt index = 0; index < num_options; index++) {
HighsOptionType type = to_options.records[index]->type;
if (type == HighsOptionType::kInt) {
HighsInt value =
*(((OptionRecordInt*)from_options.records[index])[0].value);
return_status = checkOptionValue(
report_log_options, ((OptionRecordInt*)to_options.records[index])[0],
value);
if (return_status != OptionStatus::kOk) return return_status;
} else if (type == HighsOptionType::kDouble) {
double value =
*(((OptionRecordDouble*)from_options.records[index])[0].value);
return_status = checkOptionValue(
report_log_options,
((OptionRecordDouble*)to_options.records[index])[0], value);
if (return_status != OptionStatus::kOk) return return_status;
} else if (type == HighsOptionType::kString) {
std::string value =
*(((OptionRecordString*)from_options.records[index])[0].value);
return_status = checkOptionValue(
report_log_options,
((OptionRecordString*)to_options.records[index])[0], value);
if (return_status != OptionStatus::kOk) return return_status;
}
}
for (HighsInt index = 0; index < num_options; index++) {
HighsOptionType type = to_options.records[index]->type;
if (type == HighsOptionType::kBool) {
bool value = *(((OptionRecordBool*)from_options.records[index])[0].value);
return_status = setLocalOptionValue(
((OptionRecordBool*)to_options.records[index])[0], value);
if (return_status != OptionStatus::kOk) return return_status;
} else if (type == HighsOptionType::kInt) {
HighsInt value =
*(((OptionRecordInt*)from_options.records[index])[0].value);
return_status = setLocalOptionValue(
report_log_options, ((OptionRecordInt*)to_options.records[index])[0],
value);
if (return_status != OptionStatus::kOk) return return_status;
} else if (type == HighsOptionType::kDouble) {
double value =
*(((OptionRecordDouble*)from_options.records[index])[0].value);
return_status = setLocalOptionValue(
report_log_options,
((OptionRecordDouble*)to_options.records[index])[0], value);
if (return_status != OptionStatus::kOk) return return_status;
} else {
std::string value =
*(((OptionRecordString*)from_options.records[index])[0].value);
return_status = setLocalOptionValue(
report_log_options,
((OptionRecordString*)to_options.records[index])[0], value);
if (return_status != OptionStatus::kOk) return return_status;
}
}
return OptionStatus::kOk;
}
OptionStatus getLocalOptionValues(
const HighsLogOptions& report_log_options, const std::string& option,
const std::vector<OptionRecord*>& option_records, bool* current_value,
bool* default_value) {
HighsInt index;
OptionStatus status =
getOptionIndex(report_log_options, option, option_records, index);
if (status != OptionStatus::kOk) return status;
HighsOptionType type = option_records[index]->type;
if (type != HighsOptionType::kBool) {
highsLogUser(report_log_options, HighsLogType::kError,
"getLocalOptionValue: Option \"%s\" requires value of type "
"%s, not bool\n",
option.c_str(), optionEntryTypeToString(type).c_str());
return OptionStatus::kIllegalValue;
}
OptionRecordBool& option_record =
((OptionRecordBool*)option_records[index])[0];
if (current_value) *current_value = *(option_record.value);
if (default_value) *default_value = option_record.default_value;
return OptionStatus::kOk;
}
OptionStatus getLocalOptionValues(
const HighsLogOptions& report_log_options, const std::string& option,
const std::vector<OptionRecord*>& option_records, HighsInt* current_value,
HighsInt* min_value, HighsInt* max_value, HighsInt* default_value) {
HighsInt index;
OptionStatus status =
getOptionIndex(report_log_options, option, option_records, index);
if (status != OptionStatus::kOk) return status;
HighsOptionType type = option_records[index]->type;
if (type != HighsOptionType::kInt) {
highsLogUser(report_log_options, HighsLogType::kError,
"getLocalOptionValue: Option \"%s\" requires value of type "
"%s, not HighsInt\n",
option.c_str(), optionEntryTypeToString(type).c_str());
return OptionStatus::kIllegalValue;
}
OptionRecordInt& option_record = ((OptionRecordInt*)option_records[index])[0];
if (current_value) *current_value = *(option_record.value);
if (min_value) *min_value = option_record.lower_bound;
if (max_value) *max_value = option_record.upper_bound;
if (default_value) *default_value = option_record.default_value;
return OptionStatus::kOk;
}
OptionStatus getLocalOptionValues(
const HighsLogOptions& report_log_options, const std::string& option,
const std::vector<OptionRecord*>& option_records, double* current_value,
double* min_value, double* max_value, double* default_value) {
HighsInt index;
OptionStatus status =
getOptionIndex(report_log_options, option, option_records, index);
if (status != OptionStatus::kOk) return status;
HighsOptionType type = option_records[index]->type;
if (type != HighsOptionType::kDouble) {
highsLogUser(report_log_options, HighsLogType::kError,
"getLocalOptionValue: Option \"%s\" requires value of type "
"%s, not double\n",
option.c_str(), optionEntryTypeToString(type).c_str());
return OptionStatus::kIllegalValue;
}
OptionRecordDouble& option_record =
((OptionRecordDouble*)option_records[index])[0];
if (current_value) *current_value = *(option_record.value);
if (min_value) *min_value = option_record.lower_bound;
if (max_value) *max_value = option_record.upper_bound;
if (default_value) *default_value = option_record.default_value;
return OptionStatus::kOk;
}
OptionStatus getLocalOptionValues(
const HighsLogOptions& report_log_options, const std::string& option,
const std::vector<OptionRecord*>& option_records,
std::string* current_value, std::string* default_value) {
HighsInt index;
OptionStatus status =
getOptionIndex(report_log_options, option, option_records, index);
if (status != OptionStatus::kOk) return status;
HighsOptionType type = option_records[index]->type;
if (type != HighsOptionType::kString) {
highsLogUser(report_log_options, HighsLogType::kError,
"getLocalOptionValue: Option \"%s\" requires value of type "
"%s, not string\n",
option.c_str(), optionEntryTypeToString(type).c_str());
return OptionStatus::kIllegalValue;
}
OptionRecordString& option_record =
((OptionRecordString*)option_records[index])[0];
if (current_value) *current_value = *(option_record.value);
if (default_value) *default_value = option_record.default_value;
return OptionStatus::kOk;
}
OptionStatus getLocalOptionType(
const HighsLogOptions& report_log_options, const std::string& option,
const std::vector<OptionRecord*>& option_records, HighsOptionType* type) {
HighsInt index;
OptionStatus status =
getOptionIndex(report_log_options, option, option_records, index);
if (status != OptionStatus::kOk) return status;
if (type) *type = option_records[index]->type;
return OptionStatus::kOk;
}
void resetLocalOptions(std::vector<OptionRecord*>& option_records) {
HighsInt num_options = option_records.size();
for (HighsInt index = 0; index < num_options; index++) {
HighsOptionType type = option_records[index]->type;
if (type == HighsOptionType::kBool) {
OptionRecordBool& option = ((OptionRecordBool*)option_records[index])[0];
*(option.value) = option.default_value;
} else if (type == HighsOptionType::kInt) {
OptionRecordInt& option = ((OptionRecordInt*)option_records[index])[0];
*(option.value) = option.default_value;
} else if (type == HighsOptionType::kDouble) {
OptionRecordDouble& option =
((OptionRecordDouble*)option_records[index])[0];
*(option.value) = option.default_value;
} else {
OptionRecordString& option =
((OptionRecordString*)option_records[index])[0];
*(option.value) = option.default_value;
}
}
}
HighsStatus writeOptionsToFile(FILE* file, const HighsLogOptions& log_options,
const std::vector<OptionRecord*>& option_records,
const bool report_only_deviations,
const HighsFileType file_type) {
reportOptions(file, log_options, option_records, report_only_deviations,
file_type);
return HighsStatus::kOk;
}
void reportOptions(FILE* file, const HighsLogOptions& log_options,
const std::vector<OptionRecord*>& option_records,
const bool report_only_deviations,
const HighsFileType file_type) {
HighsInt num_options = option_records.size();
const bool not_md_or_full =
file_type != HighsFileType::kMd && file_type != HighsFileType::kFull;
if (file_type == HighsFileType::kMd)
fprintf(file, "# [List of options](@id option-definitions)\n\n");
for (HighsInt index = 0; index < num_options; index++) {
HighsOptionType type = option_records[index]->type;
if (not_md_or_full) {
if (option_records[index]->name == kLogFileString) {
if (*((OptionRecordString*)option_records[index])[0].value ==
kHighsRunLogFile)
continue;
}
}
if (option_records[index]->advanced) {
if (!kAdvancedInDocumentation) continue;
}
if (type == HighsOptionType::kBool) {
reportOption(file, log_options,
((OptionRecordBool*)option_records[index])[0],
report_only_deviations, file_type);
} else if (type == HighsOptionType::kInt) {
reportOption(file, log_options,
((OptionRecordInt*)option_records[index])[0],
report_only_deviations, file_type);
} else if (type == HighsOptionType::kDouble) {
reportOption(file, log_options,
((OptionRecordDouble*)option_records[index])[0],
report_only_deviations, file_type);
} else {
reportOption(file, log_options,
((OptionRecordString*)option_records[index])[0],
report_only_deviations, file_type);
}
}
}
void reportOption(FILE* file, const HighsLogOptions& log_options,
const OptionRecordBool& option,
const bool report_only_deviations,
const HighsFileType file_type) {
if (!report_only_deviations || option.default_value != *option.value) {
if (file_type == HighsFileType::kMd) {
fprintf(file,
"## [%s](@id option-%s)\n- %s\n- Type: boolean\n- Default: "
"\"%s\"\n\n",
highsInsertMdEscapes(option.name).c_str(),
highsInsertMdId(option.name).c_str(),
highsInsertMdEscapes(option.description).c_str(),
highsBoolToString(option.default_value).c_str());
} else if (file_type == HighsFileType::kFull) {
fprintf(file, "\n# %s\n", option.description.c_str());
fprintf(
file,
"# [type: bool, advanced: %s, range: {false, true}, default: %s]\n",
highsBoolToString(option.advanced).c_str(),
highsBoolToString(option.default_value).c_str());
fprintf(file, "%s = %s\n", option.name.c_str(),
highsBoolToString(*option.value).c_str());
} else {
std::string line =
highsFormatToString("Set option %s to %s\n", option.name.c_str(),
highsBoolToString(*option.value).c_str());
if (file == stdout) {
highsLogUser(log_options, HighsLogType::kInfo, "%s", line.c_str());
} else {
fprintf(file, "%s", line.c_str());
}
}
}
}
void reportOption(FILE* file, const HighsLogOptions& log_options,
const OptionRecordInt& option,
const bool report_only_deviations,
const HighsFileType file_type) {
if (!report_only_deviations || option.default_value != *option.value) {
if (file_type == HighsFileType::kMd) {
fprintf(file,
"## [%s](@id option-%s)\n- %s\n- Type: integer\n- Range: {%d, "
"%d}\n- Default: %d\n\n",
highsInsertMdEscapes(option.name).c_str(),
highsInsertMdId(option.name).c_str(),
highsInsertMdEscapes(option.description).c_str(),
int(option.lower_bound), int(option.upper_bound),
int(option.default_value));
} else if (file_type == HighsFileType::kFull) {
fprintf(file, "\n# %s\n", option.description.c_str());
fprintf(file,
"# [type: integer, advanced: %s, range: {%" HIGHSINT_FORMAT
", %d}, default: %d]\n",
highsBoolToString(option.advanced).c_str(), option.lower_bound,
int(option.upper_bound), int(option.default_value));
fprintf(file, "%s = %d\n", option.name.c_str(), int(*option.value));
} else {
std::string line = highsFormatToString(
"Set option %s to %d\n", option.name.c_str(), int(*option.value));
if (file == stdout) {
highsLogUser(log_options, HighsLogType::kInfo, "%s", line.c_str());
} else {
fprintf(file, "%s", line.c_str());
}
}
}
}
void reportOption(FILE* file, const HighsLogOptions& log_options,
const OptionRecordDouble& option,
const bool report_only_deviations,
const HighsFileType file_type) {
if (!report_only_deviations || option.default_value != *option.value) {
if (file_type == HighsFileType::kMd) {
fprintf(file,
"## [%s](@id option-%s)\n- %s\n- Type: double\n- Range: [%g, "
"%g]\n- Default: %g\n\n",
highsInsertMdEscapes(option.name).c_str(),
highsInsertMdId(option.name).c_str(),
highsInsertMdEscapes(option.description).c_str(),
option.lower_bound, option.upper_bound, option.default_value);
} else if (file_type == HighsFileType::kFull) {
fprintf(file, "\n# %s\n", option.description.c_str());
fprintf(file,
"# [type: double, advanced: %s, range: [%g, %g], default: %g]\n",
highsBoolToString(option.advanced).c_str(), option.lower_bound,
option.upper_bound, option.default_value);
fprintf(file, "%s = %g\n", option.name.c_str(), *option.value);
} else {
std::string line = highsFormatToString(
"Set option %s to %g\n", option.name.c_str(), *option.value);
if (file == stdout) {
highsLogUser(log_options, HighsLogType::kInfo, "%s", line.c_str());
} else {
fprintf(file, "%s", line.c_str());
}
}
}
}
void reportOption(FILE* file, const HighsLogOptions& log_options,
const OptionRecordString& option,
const bool report_only_deviations,
const HighsFileType file_type) {
if (option.name == kOptionsFileString) return;
if (!report_only_deviations || option.default_value != *option.value) {
if (file_type == HighsFileType::kMd) {
fprintf(
file,
"## [%s](@id option-%s)\n- %s\n- Type: string\n- Default: \"%s\"\n\n",
highsInsertMdEscapes(option.name).c_str(),
highsInsertMdId(option.name).c_str(),
highsInsertMdEscapes(option.description).c_str(),
option.default_value.c_str());
} else if (file_type == HighsFileType::kFull) {
fprintf(file, "\n# %s\n", option.description.c_str());
fprintf(file, "# [type: string, advanced: %s, default: \"%s\"]\n",
highsBoolToString(option.advanced).c_str(),
option.default_value.c_str());
fprintf(file, "%s = %s\n", option.name.c_str(), (*option.value).c_str());
} else {
std::string line =
highsFormatToString("Set option %s to \"%s\"\n", option.name.c_str(),
(*option.value).c_str());
if (file == stdout) {
highsLogUser(log_options, HighsLogType::kInfo, "%s", line.c_str());
} else {
fprintf(file, "%s", line.c_str());
}
}
}
}
void HighsOptions::setLogOptions() {
this->log_options.output_flag = &this->output_flag;
this->log_options.log_to_console = &this->log_to_console;
this->log_options.log_dev_level = &this->log_dev_level;
}