diff --git a/doc/rst/solution-check.rst b/doc/rst/solution-check.rst index 3e5355e51..172538272 100644 --- a/doc/rst/solution-check.rst +++ b/doc/rst/solution-check.rst @@ -18,20 +18,61 @@ a tolerance. In "idealistic" mode, all expression trees are recomputed. +Motivation +********************** + +Consider the disjunction constraint + +.. code-block:: ampl + + C: y<=6 or z>=10; + +With ``y=6.0000000001`` and ``z=9.9999999999``, and assuming the solver's +feasibility tolerance is at a typical value (such as :math:`10^{-6}`), +most Mathematical Programming solvers consider the disjunction satisfied. +And, from a practical viewpoint, it is (given finite-precision +computations). + +Our "realistic" checking mode does exactly this: it trusts the solver results +up to a tolerance. + +In contrast, AMPL reports the constraint violated: + +.. code-block:: ampl + + ampl: let y:=6.0000000001; + ampl: let z:=9.9999999999; + ampl: display C.val; + C.val = 0 + +That is, when expressions ``y<=6`` and ``z>=10`` are re-evaluated +and their results substituted into ``C``, ``C`` holds false. + +The role of the "idealistic" checking mode is to warn the user about the fact, +that even if the solver has a correct solution up to its tolerances +(which is examined by the "realistic" mode), +it can be wrong for a tolerance-unaware checker. + +By default, "idealistic" check is performed for objective values only, +see example below. To enable it for constraints, use +:ref:`option ` ``chk:mode``. + + + "Realistic" solution check ****************************** In this mode, variable values are taken as they were reported by the solver -(with possible modifications using AMPL's options -``solution_round`` and ``solution_precision``, see driver options -``sol:chk:no...``.). This check is enough for most practical situations. +(with possible modifications via options +``sol:chk:round`` and ``sol:chk:prec``.) +This check is enough for most practical situations. .. code-block:: ampl ------------ WARNINGS ------------ WARNING: "Solution Check" - [ sol:chk:feastol=1e-06, sol:chk:inttol=1e-05, - solution_round='', solution_precision='' ] + [ sol:chk:feastol=1e-06, :feastolrel=1e-06, :inttol=1e-05, + :round='', :prec='' ] Algebraic expression violations: - 1 original expression(s) of type ':quadrange', up to 1E+00 (item 'socp[13]') @@ -70,11 +111,12 @@ Most solvers apply a constraint feasibility tolerance of the order :math:`10^{-6 ------------ WARNINGS ------------ WARNING: "Solution Check (Idealistic)" - [ sol:chk:feastol=1e-06, sol:chk:inttol=1e-05, - solution_round='', solution_precision='' ] + [ sol:chk:feastol=1e-06, :feastolrel=1e-06, :inttol=1e-05, + :round='', :prec='' ] Objective value violations: - 1 objective value(s) violated, - up to 1E+01 + up to 1E+01 (abs) + Idealistic check is an indicator only, see documentation. ampl: display x; x = 5 @@ -105,16 +147,17 @@ use driver option ``chk:mode``: ------------ WARNINGS ------------ WARNING: "Solution Check (Idealistic)" - [ sol:chk:feastol=1e-06, sol:chk:inttol=1e-05, - solution_round='', solution_precision='' ] + [ sol:chk:feastol=1e-06, :feastolrel=1e-06, :inttol=1e-05, + :round='', :prec='' ] Algebraic expression violations: - 1 original expression(s) of type ':ifthen', - up to 1E+01 + up to 1E+01 (abs) Logical expression violations: - 1 original expression(s) of type ':and' Objective value violations: - 1 objective value(s) violated, - up to 1E+01 + up to 1E+01 (abs) + Idealistic check is an indicator only, see documentation. *Hint*: to display AMPL model names, set ``option (solver_)auxfiles rc;`` as follows: @@ -129,17 +172,18 @@ set ``option (solver_)auxfiles rc;`` as follows: ------------ WARNINGS ------------ WARNING: "Solution Check (Idealistic)" - [ sol:chk:feastol=1e-06, sol:chk:inttol=1e-05, - solution_round='', solution_precision='' ] + [ sol:chk:feastol=1e-06, :feastolrel=1e-06, :inttol=1e-05, + :round='', :prec='' ] Algebraic expression violations: - 1 original expression(s) of type ':ifthen', - up to 1E+01 (item 'Total_11_') + up to 1E+01 (abs, item 'Total_11_') Logical expression violations: - - 1 original expression(s) of type ':and' - (item 'Total_7_') + - 1 original expression(s) of type ':and', + (item 'Total_7_') Objective value violations: - 1 objective value(s) violated, - up to 1E+01 (item 'Total') + up to 1E+01 (abs, item 'Total') + Idealistic check is an indicator only, see documentation. Remedies diff --git a/include/mp/flat/constr_keeper.h b/include/mp/flat/constr_keeper.h index b0c3f36f4..7f244ff62 100644 --- a/include/mp/flat/constr_keeper.h +++ b/include/mp/flat/constr_keeper.h @@ -128,7 +128,7 @@ class VarInfoImpl { ArrayRef x_raw, ArrayRef type, ArrayRef lb, ArrayRef ub, - const char* sol_rnd, const char* sol_prec) + int sol_rnd, int sol_prec) : feastol_(ft), recomp_vals_(recomp_vals), x_(std::move(x)), x_raw_(x_raw), type_(type), lb_(lb), ub_(ub) { @@ -200,10 +200,10 @@ class VarInfoImpl { protected: void apply_precision_options( - const char* sol_rnd, const char* sol_prec) { + int sol_rnd, int sol_prec) { try { // Apply sol_rnd - if (sol_rnd) { - sol_rnd_ = std::stoi(sol_rnd); + if (sol_rnd<100) { + sol_rnd_ = (sol_rnd); auto scale = std::pow(10, sol_rnd_); auto scale_rec = 1.0/scale; for (auto& x: x_) @@ -211,8 +211,8 @@ class VarInfoImpl { } } catch (...) { sol_rnd_=100; } // Could add a warning try { // Apply sol_prec - if (sol_prec) { - sol_prec_ = std::stoi(sol_prec); + if (sol_prec<100) { + sol_prec_ = (sol_prec); for (auto& x: x_) x = round_to_digits(x, sol_prec_); } @@ -258,7 +258,7 @@ struct SolCheck { ArrayRef vtype, ArrayRef lb, ArrayRef ub, double feastol, double feastolrel, - const char* sol_rnd, const char* sol_prec, + int sol_rnd, int sol_prec, bool recomp_vals, int chk_mode) : x_(feastol, recomp_vals, x, x_raw, vtype, lb, ub, sol_rnd, sol_prec), diff --git a/include/mp/flat/converter.h b/include/mp/flat/converter.h index 0cadf4a01..1ada3c236 100644 --- a/include/mp/flat/converter.h +++ b/include/mp/flat/converter.h @@ -672,7 +672,7 @@ class FlatConverter : GetModel().var_type_vec(), GetModel().var_lb_vec(), GetModel().var_ub_vec(), - nullptr, nullptr + options_.sol_round_, options_.sol_prec_ }; vir.get_x().set_p_var_info(&vir); for (auto i=vir.size(); i--; ) @@ -698,10 +698,8 @@ class FlatConverter : GetModel().var_ub_vec(), options_.solfeastol_, options_.solfeastolrel_, - options_.dont_use_sol_round_ - ? "" : std::getenv("solution_round"), - options_.dont_use_sol_prec_ - ? "" : std::getenv("solution_precision"), + options_.sol_round_, + options_.sol_prec_, if_recomp_vals, if_recomp_vals ? (options_.solcheckmode_ >> 5) @@ -720,7 +718,7 @@ class FlatConverter : // Should be fine - warning by default, // fail if requested explicitly. // If warning, we should add the report - // to that solutions' solve message, and + // to that solution's solve message, and // a summary in the final solve message. // For now, do this via warnings? if (chk.HasAnyViols()) { @@ -787,7 +785,7 @@ class FlatConverter : if (chk.HasAnyViols()) wrt.write( " [ sol:chk:feastol={}, :feastolrel={}, :inttol={},\n" - " solution_round='{}', solution_precision='{}' ]\n", + " :round='{}', :prec='{}' ]\n", options_.solfeastol_, options_.solfeastolrel_, options_.solinttol_, chk.x_ext().solution_round(), @@ -1183,8 +1181,8 @@ class FlatConverter : double solfeastol_ = 1e-6; double solfeastolrel_ = 1e-6; double solinttol_ = 1e-5; - bool dont_use_sol_round_ = false; - bool dont_use_sol_prec_ = false; + int sol_round_ = 100; + int sol_prec_ = 100; int solchkoutlev_ = 0; }; Options options_; @@ -1282,27 +1280,30 @@ class FlatConverter : "\n" "Default: 1+2+16+512.", options_.solcheckmode_, 0, 1024); - GetEnv().AddOption("sol:chk:feastol sol:chk:eps chk:eps chk:tol", + GetEnv().AddOption("sol:chk:feastol sol:chk:eps chk:eps chk:feastol", "Absolute tolerance to check objective values, variable " "and constraint bounds. Default 1e-6.", options_.solfeastol_, 0.0, 1e100); - GetEnv().AddOption("sol:chk:feastolrel sol:chk:epsrel chk:epsrel chk:tolrel", + GetEnv().AddOption("sol:chk:feastolrel sol:chk:epsrel chk:epsrel chk:feastolrel", "Relative tolerance to check objective values, variable " "and constraint bounds. Default 1e-6.", options_.solfeastolrel_, 0.0, 1e100); - GetEnv().AddOption("sol:chk:inttol sol:chk:inteps sol:inteps chk:inteps", + GetEnv().AddOption("sol:chk:inttol sol:chk:inteps sol:inteps chk:inttol", "Solution checking tolerance for variables' integrality. " "Default 1e-5.", options_.solinttol_, 0.0, 1e100); GetEnv().AddOption("sol:chk:fail chk:fail checkfail", "Fail on solution checking violations.", options_.solcheckfail_, false, true); - GetEnv().AddOption("sol:chk:noround chk:noround chk:no_solution_round", - "Don't use AMPL solution_round option when checking.", - options_.dont_use_sol_round_, false, true); - GetEnv().AddOption("sol:chk:noprec chk:noprec chk:no_solution_precision", - "Don't use AMPL solution_precision option when checking.", - options_.dont_use_sol_prec_, false, true); + GetEnv().AddOption("sol:chk:round chk:round chk:rnd", + "AMPL solution_round option when checking: " + "round to this number of decimals after comma " + "(before comma if negative.)", + options_.sol_round_, -1000, 1000); + GetEnv().AddOption("sol:chk:prec chk:prec chk:precision", + "AMPL solution_precision option when checking: " + "number of significant digits.", + options_.sol_prec_, -1000, 1000); }