From 74c394fe678490e432197601d3ca1a5679d200f4 Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Thu, 25 Apr 2024 18:26:05 +1000 Subject: [PATCH] ACCEPT_EXPRESSION() #237 --- CMakeLists.txt | 4 +- include/mp/flat/constr_algebraic.h | 4 +- include/mp/flat/constr_base.h | 30 +++++- include/mp/flat/constr_functional.h | 114 +++++++++++--------- include/mp/flat/constr_general.h | 22 ++-- include/mp/flat/constr_keeper.h | 70 +++++++----- include/mp/flat/converter.h | 8 +- include/mp/{ => flat}/expr/model_api_base.h | 2 +- include/mp/flat/model_api_base.h | 44 ++++++-- solvers/gurobi/gurobimodelapi.h | 4 - solvers/scipmp/scipmpmodelapi.h | 15 ++- solvers/visitor/visitormodelapi.h | 3 - 12 files changed, 194 insertions(+), 126 deletions(-) rename include/mp/{ => flat}/expr/model_api_base.h (97%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5540f5629..afe47b405 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -396,14 +396,14 @@ add_prefix(MP_FLAT_REDEF_MIP_HEADERS include/mp/flat/redef/MIP/ piecewise_linear.h power_const.h sos2.h) -add_prefix(MP_EXPR_HEADERS include/mp/expr/ +add_prefix(MP_FLAT_EXPR_HEADERS include/mp/flat/expr/ model_api_base.h) set(MP_ALL_HEADERS ${MP_HEADERS} ${MP_FLAT_HEADERS} ${MP_FLAT_REDEF_HEADERS} ${MP_FLAT_REDEF_STD_HEADERS} ${MP_FLAT_REDEF_MIP_HEADERS} - ${MP_EXPR_HEADERS} + ${MP_FLAT_EXPR_HEADERS} ) diff --git a/include/mp/flat/constr_algebraic.h b/include/mp/flat/constr_algebraic.h index 680b89b4a..a0feed1aa 100644 --- a/include/mp/flat/constr_algebraic.h +++ b/include/mp/flat/constr_algebraic.h @@ -24,12 +24,12 @@ class AlgebraicConstraint : public BasicConstraint, public Body, public RhsOrRange { public: /// Constraint type name - static const std::string& GetTypeName() { + static const char* GetTypeName() { static std::string name { std::string("AlgebraicConstraint< ") + Body::GetTypeName() + ", " + RhsOrRange::GetTypeName() + " >" }; - return name; + return name.c_str(); } /// Is logical? diff --git a/include/mp/flat/constr_base.h b/include/mp/flat/constr_base.h index 15f36a082..ef4c9a8cc 100644 --- a/include/mp/flat/constr_base.h +++ b/include/mp/flat/constr_base.h @@ -13,7 +13,6 @@ #include "mp/common.h" #include "mp/flat/context.h" -#include "mp/arrayref.h" namespace mp { @@ -70,6 +69,25 @@ class BasicConstraint { }; +/// Wrap (functional) constraint to represent it as an expression. +/// +/// (Solver)ModelAPI should only inspect it via the +/// BasicExprModelAPI<>::GetExpr... methods. +template +class ExprWrapper { + Con con_flat_; +public: + /// Construct + ExprWrapper(Con c) : con_flat_(std::move(c)) { } + /// Constraint type + using FlatConType = Con; + /// Get const & (con) + const Con& GetFlatConstraint() const { return con_flat_; } + /// Get & (con) + Con& GetFlatConstraint() { return con_flat_; } +}; + + /// A special constraint 'var=...', which defines a result variable class FunctionalConstraint : public BasicConstraint { int result_var_=-1; // defined var is optional @@ -439,14 +457,15 @@ inline void WriteJSON(JW jw, //////////////////////////////////////////////////////////////////////// -/// Args is the argument type, e.g., array of variables, or an expression -/// Params is the parameter type, e.g., array of numbers. Can be empty +/// Args is the argument type, e.g., array of variables, or an expression. +/// Params is the parameter type, e.g., array of numbers. Can be empty. #define DEF_CUSTOM_FUNC_CONSTR_WITH_PRM(Name, Args, Params, NumLogic, Descr) \ struct Name ## Id { \ static constexpr const char* description() { return Descr; } \ static constexpr const char* GetTypeName() { return #Name; } \ }; \ - using Name = CustomFunctionalConstraint + using Name ## Constraint = CustomFunctionalConstraint; \ + using Name ## Expression = ExprWrapper< Name ## Constraint > /// Custom numeric constraint without fixed parameters #define DEF_NUMERIC_FUNC_CONSTR(Name, Args, Descr) \ @@ -466,7 +485,8 @@ inline void WriteJSON(JW jw, /// A wrapper on a static constraint making it conditional #define DEF_CONDITIONAL_CONSTRAINT_WRAPPER(Name, StaticConName) \ - using Name = ConditionalConstraint< StaticConName > + using Name ## Constraint = ConditionalConstraint< StaticConName >; \ + using Name ## Expression = ExprWrapper< Name ## Constraint > //////////////////////////////////////////////////////////////////////// /// STATIC CONSTRAINTS diff --git a/include/mp/flat/constr_functional.h b/include/mp/flat/constr_functional.h index 0c41d6b21..35193b705 100644 --- a/include/mp/flat/constr_functional.h +++ b/include/mp/flat/constr_functional.h @@ -13,169 +13,169 @@ namespace mp { //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR( MaxConstraint, VarArray, +DEF_NUMERIC_FUNC_CONSTR( Max, VarArray, "r = max(v1, v2, ..., vn)"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR( MinConstraint, VarArray, +DEF_NUMERIC_FUNC_CONSTR( Min, VarArray, "r = min(v1, v2, ..., vn)"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR( AbsConstraint, VarArray1, +DEF_NUMERIC_FUNC_CONSTR( Abs, VarArray1, "r = abs(v)"); //////////////////////////////////////////////////////////////////////// -DEF_LOGICAL_FUNC_CONSTR( AndConstraint, VarArray, +DEF_LOGICAL_FUNC_CONSTR( And, VarArray, "r = forall({vi})"); //////////////////////////////////////////////////////////////////////// -DEF_LOGICAL_FUNC_CONSTR( OrConstraint, VarArray, +DEF_LOGICAL_FUNC_CONSTR( Or, VarArray, "r = exists({vi})"); //////////////////////////////////////////////////////////////////////// -DEF_LOGICAL_FUNC_CONSTR( NotConstraint, VarArray1, +DEF_LOGICAL_FUNC_CONSTR( Not, VarArray1, "r = !v"); //////////////////////////////////////////////////////////////////////// -/// \brief DivConstraint -DEF_NUMERIC_FUNC_CONSTR( DivConstraint, VarArray2, +/// \brief DivConstraint and DivExpression +DEF_NUMERIC_FUNC_CONSTR( Div, VarArray2, "r = v1 / v2 and v2!=0"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR( IfThenConstraint, VarArrayN<3>, +DEF_NUMERIC_FUNC_CONSTR( IfThen, VarArrayN<3>, "Expr-valued: if (cond) then (expr1) else (expr2)"); //////////////////////////////////////////////////////////////////////// -DEF_LOGICAL_FUNC_CONSTR( ImplicationConstraint, VarArrayN<3>, +DEF_LOGICAL_FUNC_CONSTR( Implication, VarArrayN<3>, "Logic-valued: if (cond) then (con1) else (con2)"); //////////////////////////////////////////////////////////////////////// -DEF_LOGICAL_FUNC_CONSTR( AllDiffConstraint, VarArray, +DEF_LOGICAL_FUNC_CONSTR( AllDiff, VarArray, "alldiff({})"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR_WITH_PRM( NumberofConstConstraint, +DEF_NUMERIC_FUNC_CONSTR_WITH_PRM( NumberofConst, VarArray, DblParamArray1, "numberof_const(k, {x0...xn})"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR( NumberofVarConstraint, VarArray, +DEF_NUMERIC_FUNC_CONSTR( NumberofVar, VarArray, "numberof_var(x0, {x1...xn})"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR( CountConstraint, VarArray, +DEF_NUMERIC_FUNC_CONSTR( Count, VarArray, "count({x0...xn})"); //////////////////// NONLINEAR FUNCTIONS ////////////////////// //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR( ExpConstraint, VarArray1, +DEF_NUMERIC_FUNC_CONSTR( Exp, VarArray1, "r = exp(v)"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR_WITH_PRM( ExpAConstraint, +DEF_NUMERIC_FUNC_CONSTR_WITH_PRM( ExpA, VarArray1, DblParamArray1, "r = a**v"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR( LogConstraint, VarArray1, +DEF_NUMERIC_FUNC_CONSTR( Log, VarArray1, "r = log(v)"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR_WITH_PRM( LogAConstraint, +DEF_NUMERIC_FUNC_CONSTR_WITH_PRM( LogA, VarArray1, DblParamArray1, "r = log(v)/log(a)"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR_WITH_PRM( PowConstraint, +DEF_NUMERIC_FUNC_CONSTR_WITH_PRM( Pow, VarArray1, DblParamArray1, "r = v ** a"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR( SinConstraint, VarArray1, +DEF_NUMERIC_FUNC_CONSTR( Sin, VarArray1, "r = sin(v)"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR( CosConstraint, VarArray1, +DEF_NUMERIC_FUNC_CONSTR( Cos, VarArray1, "r = cos(v)"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR( TanConstraint, VarArray1, +DEF_NUMERIC_FUNC_CONSTR( Tan, VarArray1, "r = tan(v)"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR( AsinConstraint, VarArray1, +DEF_NUMERIC_FUNC_CONSTR( Asin, VarArray1, "r = asin(v)"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR( AcosConstraint, VarArray1, +DEF_NUMERIC_FUNC_CONSTR( Acos, VarArray1, "r = acos(v)"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR( AtanConstraint, VarArray1, +DEF_NUMERIC_FUNC_CONSTR( Atan, VarArray1, "r = atan(v)"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR( SinhConstraint, VarArray1, +DEF_NUMERIC_FUNC_CONSTR( Sinh, VarArray1, "r = sinh(v)"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR( CoshConstraint, VarArray1, +DEF_NUMERIC_FUNC_CONSTR( Cosh, VarArray1, "r = cosh(v)"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR( TanhConstraint, VarArray1, +DEF_NUMERIC_FUNC_CONSTR( Tanh, VarArray1, "r = tanh(v)"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR( AsinhConstraint, VarArray1, +DEF_NUMERIC_FUNC_CONSTR( Asinh, VarArray1, "r = asinh(v)"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR( AcoshConstraint, VarArray1, +DEF_NUMERIC_FUNC_CONSTR( Acosh, VarArray1, "r = acosh(v)"); //////////////////////////////////////////////////////////////////////// -DEF_NUMERIC_FUNC_CONSTR( AtanhConstraint, VarArray1, +DEF_NUMERIC_FUNC_CONSTR( Atanh, VarArray1, "r = atanh(v)"); /// Not using: var1 != var2. /// Represented by Not { Eq0Constraint... } //////////////////////////////////////////////////////////////////////// -// DEF_LOGICAL_FUNC_CONSTR( NEConstraint__unused, VarArray2, +// DEF_LOGICAL_FUNC_CONSTR( NE__unused, VarArray2, // "r = (v1 != v2)"); //////////////////////////////////////////////////////////////////////// -DEF_CONDITIONAL_CONSTRAINT_WRAPPER(CondLinConLT, LinConLT); - +DEF_CONDITIONAL_CONSTRAINT_WRAPPER(CondLinLT, LinConLT); +using CondLinConLT = CondLinLTConstraint; //////////////////////////////////////////////////////////////////////// -DEF_CONDITIONAL_CONSTRAINT_WRAPPER(CondLinConLE, LinConLE); - +DEF_CONDITIONAL_CONSTRAINT_WRAPPER(CondLinLE, LinConLE); +using CondLinConLE = CondLinLEConstraint; //////////////////////////////////////////////////////////////////////// -DEF_CONDITIONAL_CONSTRAINT_WRAPPER(CondLinConEQ, LinConEQ); - +DEF_CONDITIONAL_CONSTRAINT_WRAPPER(CondLinEQ, LinConEQ); +using CondLinConEQ = CondLinEQConstraint; //////////////////////////////////////////////////////////////////////// -DEF_CONDITIONAL_CONSTRAINT_WRAPPER(CondLinConGE, LinConGE); - +DEF_CONDITIONAL_CONSTRAINT_WRAPPER(CondLinGE, LinConGE); +using CondLinConGE = CondLinGEConstraint; //////////////////////////////////////////////////////////////////////// -DEF_CONDITIONAL_CONSTRAINT_WRAPPER(CondLinConGT, LinConGT); - +DEF_CONDITIONAL_CONSTRAINT_WRAPPER(CondLinGT, LinConGT); +using CondLinConGT = CondLinGTConstraint; //////////////////////////////////////////////////////////////////////// -DEF_CONDITIONAL_CONSTRAINT_WRAPPER(CondQuadConLT, QuadConLT); - +DEF_CONDITIONAL_CONSTRAINT_WRAPPER(CondQuadLT, QuadConLT); +using CondQuadConLT = CondQuadLTConstraint; //////////////////////////////////////////////////////////////////////// -DEF_CONDITIONAL_CONSTRAINT_WRAPPER(CondQuadConLE, QuadConLE); - +DEF_CONDITIONAL_CONSTRAINT_WRAPPER(CondQuadLE, QuadConLE); +using CondQuadConLE = CondQuadLEConstraint; //////////////////////////////////////////////////////////////////////// -DEF_CONDITIONAL_CONSTRAINT_WRAPPER(CondQuadConEQ, QuadConEQ); - +DEF_CONDITIONAL_CONSTRAINT_WRAPPER(CondQuadEQ, QuadConEQ); +using CondQuadConEQ = CondQuadEQConstraint; //////////////////////////////////////////////////////////////////////// -DEF_CONDITIONAL_CONSTRAINT_WRAPPER(CondQuadConGE, QuadConGE); - +DEF_CONDITIONAL_CONSTRAINT_WRAPPER(CondQuadGE, QuadConGE); +using CondQuadConGE = CondQuadGEConstraint; //////////////////////////////////////////////////////////////////////// -DEF_CONDITIONAL_CONSTRAINT_WRAPPER(CondQuadConGT, QuadConGT); - +DEF_CONDITIONAL_CONSTRAINT_WRAPPER(CondQuadGT, QuadConGT); +using CondQuadConGT = CondQuadGTConstraint; //////////////////////////////////////////////////////////////////////// @@ -215,6 +215,10 @@ class LinearFunctionalConstraint : } }; + +/// Typedef LinExpression +using LinExpression = ExprWrapper; + /// Write LFC without name. template inline void WriteModelItem(Writer& wrt, @@ -283,6 +287,10 @@ class QuadraticFunctionalConstraint : } }; + +/// Typedef LinExpression +using QuadExpression = ExprWrapper; + /// Write LFC without name. template inline void WriteModelItem(Writer& wrt, @@ -405,7 +413,7 @@ class PLConParams { /// Define PLConstraint -DEF_NUMERIC_FUNC_CONSTR_WITH_PRM( PLConstraint, +DEF_NUMERIC_FUNC_CONSTR_WITH_PRM( PL, VarArray1, PLConParams, "r = piecewise_linear(x)"); /// Write flat expr/obj/con parameters: diff --git a/include/mp/flat/constr_general.h b/include/mp/flat/constr_general.h index c56a99074..7f1c21a5b 100644 --- a/include/mp/flat/constr_general.h +++ b/include/mp/flat/constr_general.h @@ -21,10 +21,10 @@ template class IndicatorConstraint: public BasicConstraint { public: /// Constraint type name - static const std::string& GetTypeName() { + static const char* GetTypeName() { static std::string name - { "IndicatorConstraint[" + Con::GetTypeName() + ']' }; - return name; + { std::string("IndicatorConstraint[") + Con::GetTypeName() + ']' }; + return name.c_str(); } /// Is logical? @@ -102,7 +102,7 @@ inline void WriteJSON(JSONW jw, /// Unary encoding. /// Currently a dummy constraint just to build /// the reformulation graph. -DEF_STATIC_CONSTR(UnaryEncodingConstraint, VarArray1, +DEF_STATIC_CONSTR(UnaryEncoding, VarArray1, "Unary encoding of an integer bounded variable"); @@ -277,11 +277,11 @@ class ComplementarityConstraint : public BasicConstraint { using ExprType = Expr; /// Constraint type name - static const std::string& GetTypeName() { + static const char* GetTypeName() { static std::string name { std::string("ComplementarityConstraint[") + Expr::GetTypeName() + ']' }; - return name; + return name.c_str(); } /// Is logical? @@ -341,23 +341,23 @@ inline void WriteJSON(JSONW jw, /// Quadratic cone -DEF_STATIC_CONSTR_WITH_PRM( QuadraticConeConstraint, VarArray, DblParamArray, +DEF_STATIC_CONSTR_WITH_PRM( QuadraticCone, VarArray, DblParamArray, "Quadratic cone p1*x1 >= sqrt((p2*x2)^2 + ...))," " with factors p1..pn"); /// Rotated quadratic cone -DEF_STATIC_CONSTR_WITH_PRM( RotatedQuadraticConeConstraint, VarArray, DblParamArray, +DEF_STATIC_CONSTR_WITH_PRM( RotatedQuadraticCone, VarArray, DblParamArray, "Rotated quadratic cone " "2 * p1*x1*p2*x2 >= (p3*x3)^2 + ...)," " x1, x2 >= 0 or x1, x2 <= 0, with factors p1..pn"); /// Exponential cone -DEF_STATIC_CONSTR_WITH_PRM( ExponentialConeConstraint, VarArray3, DblParamArray3, +DEF_STATIC_CONSTR_WITH_PRM( ExponentialCone, VarArray3, DblParamArray3, "Exponential cone ax >= by exp(cz / (by))," " where ax, by >= 0, with factors a,b,c"); /// Power cone -DEF_STATIC_CONSTR_WITH_PRM( PowerConeConstraint, VarArray, DblParamArray, +DEF_STATIC_CONSTR_WITH_PRM( PowerCone, VarArray, DblParamArray, "Power cone with factors"); /// Geometric cone -DEF_STATIC_CONSTR_WITH_PRM( GeometricConeConstraint, VarArray, DblParamArray, +DEF_STATIC_CONSTR_WITH_PRM( GeometricCone, VarArray, DblParamArray, "Geometric with factors"); } // namespace mp diff --git a/include/mp/flat/constr_keeper.h b/include/mp/flat/constr_keeper.h index 757d43ac2..91a4c4299 100644 --- a/include/mp/flat/constr_keeper.h +++ b/include/mp/flat/constr_keeper.h @@ -439,9 +439,13 @@ class BasicConstraintKeeper { pre::NodeRange SelectValueNodeRange(int pos, int n=1) { return GetValueNode().Select(pos, n); } - /// Constraint name + /// Constraint type name, e.g., 'AbsConstraint' const char* GetConstraintName() const { return constr_name_; } + /// Expression type, or, if appropriate, constraint type name, + /// e.g., 'Abs' + virtual const char* GetExprOrConstraintName() const =0; + /// Acceptance option names virtual const char* GetAcceptanceOptionNames() const { return solver_opt_nm_; } @@ -455,7 +459,7 @@ class BasicConstraintKeeper { /// whether it is accepted natively by ModelAPI and/or can be /// converted by the Converter. /// If both, add constraint acceptance option. - /// This should be called before using the class. + /// @note This should be called before using the class. virtual void ConsiderAcceptanceOptions( BasicFlatConverter& cvt, const BasicFlatModelAPI& ma, @@ -518,7 +522,7 @@ class BasicConstraintKeeper { virtual void SetChosenAcceptanceLevel( ConstraintAcceptanceLevel acc) { acceptance_level_ = static_cast< - typename std::underlying_type::type >(acc); + std::underlying_type_t >(acc); } /// Mark as bridged. Use index only. @@ -695,6 +699,11 @@ class ConstraintKeeper final /// Constraint type using ConstraintType = Constraint; + /// Expression type, or, if appropriate, constraint type name, + /// e.g., 'Abs' + const char* GetExprOrConstraintName() const override + { return Constraint::GetTypeName(); } + /// Constrint Keeper description const std::string& GetDescription() const override { return desc_; } @@ -715,11 +724,11 @@ class ConstraintKeeper final /// Get const constraint \a i const Constraint& GetConstraint(int i) const - { assert(check_index(i)); return cons_[i].con_; } + { assert(check_index(i)); return cons_[i].GetCon(); } /// Get constraint \a i Constraint& GetConstraint(int i) - { assert(check_index(i)); return cons_[i].con_; } + { assert(check_index(i)); return cons_[i].GetCon(); } /// Get constraint depth in the reformulation tree int GetConstraintDepth(int i) const @@ -727,7 +736,7 @@ class ConstraintKeeper final /// Get context of contraint \a i Context GetContext(int i) const override - { assert(check_index(i)); return cons_[i].con_.GetContext(); } + { assert(check_index(i)); return cons_[i].GetCon().GetContext(); } /// Propagate expression result of constraint \a i top-down void PropagateResult(BasicFlatConverter& cvt, @@ -747,7 +756,7 @@ class ConstraintKeeper final /// Result variable of constraint \a i. Returns -1 if none int GetResultVar(int i) const override - { assert(check_index(i)); return cons_[i].con_.GetResultVar(); } + { assert(check_index(i)); return cons_[i].GetCon().GetResultVar(); } /// Conversion priority. Uses that from Converter double ConversionPriority() const @@ -853,7 +862,8 @@ class ConstraintKeeper final bool check_index(int i) const { return i>=0 && i<(int)cons_.size(); } /// Container for a single constraint - struct Container { + class Container { + public: Container(int d, Constraint&& c) noexcept : con_(std::move(c)), depth_(d) { } @@ -875,7 +885,15 @@ class ConstraintKeeper final is_unused_=true; } - Constraint con_; + /// Get the flat constraint, const & + const Constraint& GetCon() const { return con_.GetFlatConstraint(); } + /// Get the flat constraint & + Constraint& GetCon() { return con_.GetFlatConstraint(); } + + private: + // Storing in the ExprWrapper, + // so we can send (wrapper &) to ModelAPI::AddExpression(). + ExprWrapper con_; int depth_ = 0; bool is_bridged_ = false; bool is_unused_ = false; @@ -906,7 +924,7 @@ class ConstraintKeeper final } else { // Recommended == acceptanceLevel && for (; ++i != (int)cons_.size(); ) if (!cons_[i].IsBridged() && - GetConverter().IfNeedsConversion(cons_[i].con_, i)) + GetConverter().IfNeedsConversion(cons_[i].GetCon(), i)) ConvertConstraint(cons_[i], i); } bool any_converted = i_last!=i-1; @@ -929,7 +947,7 @@ class ConstraintKeeper final /// @param i constraint index, needed for bridging void ConvertConstraint(Container& cnt, int i) { assert(!cnt.IsBridged()); - GetConverter().RunConversion(cnt.con_, i, cnt.GetDepth()); + GetConverter().RunConversion(cnt.GetCon(), i, cnt.GetDepth()); MarkAsBridged(cnt, i); } @@ -954,10 +972,10 @@ class ConstraintKeeper final MiniJSONWriter jw(wrt); jw["CON_TYPE"] = GetShortTypeName(); jw["index"] = i_con; - if (*cnt.con_.name()) - jw["name"] = cnt.con_.name(); + if (*cnt.GetCon().name()) + jw["name"] = cnt.GetCon().name(); jw["depth"] = cnt.GetDepth(); - WriteJSON(jw["data"], cnt.con_); + WriteJSON(jw["data"], cnt.GetCon()); } wrt.write("\n"); // EOL GetLogger()->Append(wrt); @@ -975,11 +993,11 @@ class ConstraintKeeper final MiniJSONWriter jw(wrt); jw["CON_TYPE"] = GetShortTypeName(); jw["index"] = i_con; - if (*cnt.con_.name()) { - jw["name"] = cnt.con_.name(); + if (*cnt.GetCon().name()) { + jw["name"] = cnt.GetCon().name(); if (pvnam && pvnam->size()) { fmt::MemoryWriter pr; - WriteFlatCon(pr, cnt.con_, *pvnam); + WriteFlatCon(pr, cnt.GetCon(), *pvnam); jw["printed"] = pr.c_str(); } } @@ -1016,7 +1034,7 @@ class ConstraintKeeper final const auto& vn = GetValueNode().GetStrVec(); assert(vn.size()==cons_.size()); for (auto i=vn.size(); i--; ) - cons_[i].con_.SetName(vn[i].MakeCurrentName()); + cons_[i].GetCon().SetName(vn[i].MakeCurrentName()); } /// Copy names to ValueNodes @@ -1024,7 +1042,7 @@ class ConstraintKeeper final auto& vn = GetValueNode().GetStrVec(); assert(vn.size()==cons_.size()); for (auto i=vn.size(); i--; ) - vn[i] = std::string(cons_[i].con_.name()); + vn[i] = std::string(cons_[i].GetCon().name()); } /// ForEachActive(). @@ -1033,7 +1051,7 @@ class ConstraintKeeper final void ForEachActive(Fn fn) { for (int i=0; i<(int)cons_.size(); ++i) if (!cons_[i].IsBridged()) - if (fn(cons_[i].con_, i)) + if (fn(cons_[i].GetCon(), i)) MarkAsBridged(cons_[i], i); } @@ -1041,8 +1059,8 @@ class ConstraintKeeper final /// (for functional constraints). double ComputeValue(int i, const VarInfoRecomp& vir) override { - assert(cons_[i].con_.GetResultVar() >= 0); - return mp::ComputeValue(cons_[i].con_, vir); + assert(cons_[i].GetCon().GetResultVar() >= 0); + return mp::ComputeValue(cons_[i].GetCon(), vir); } /// Compute violations for this constraint type. @@ -1050,7 +1068,7 @@ class ConstraintKeeper final void ComputeViolations(SolCheck& chk) override { if (cons_.size()) { auto& conviolmap = - cons_.front().con_.IsLogical() ? + cons_.front().GetCon().IsLogical() ? chk.ConViolLog() : chk.ConViolAlg(); const auto& x = chk.x_ext(); @@ -1065,7 +1083,7 @@ class ConstraintKeeper final if (!c_class) c_class = 4; // intermediate if (c_class & chk.check_mode()) { - auto viol = cons_[i].con_.ComputeViolation(x); + auto viol = cons_[i].GetCon().ComputeViolation(x); auto cr = viol.Check( chk.GetFeasTol(), chk.GetFeasTolRel()); if (cr.first) { @@ -1079,7 +1097,7 @@ class ConstraintKeeper final ? 2 : 1; assert(index < (int)conviolarray->size()); (*conviolarray)[index].CountViol( - viol, cr.second, cons_[i].con_.name()); + viol, cr.second, cons_[i].GetCon().name()); } } } @@ -1098,7 +1116,7 @@ class ConstraintKeeper final for (const auto& cont: cons_) { bool adding = !cont.IsBridged(); if (adding) { - static_cast(be).AddConstraint(cont.con_); + static_cast(be).AddConstraint(cont.GetCon()); GetConverter().GetCopyLink(). AddEntry({ GetValueNode().Select(con_index), diff --git a/include/mp/flat/converter.h b/include/mp/flat/converter.h index a68a45d0c..adfe4ca9e 100644 --- a/include/mp/flat/converter.h +++ b/include/mp/flat/converter.h @@ -980,7 +980,7 @@ class FlatConverter : int passExpCones_ = 0; int accExpr_ = static_cast< - typename std::underlying_type_t > + std::underlying_type_t > (ModelAPI::ExpressionInterfaceAcceptanceLevel()) -1; // If available, 0 or 1 @@ -1065,7 +1065,7 @@ class FlatConverter : "in particular if the objective is quadratic", 1}, { "2", "Always convert", 2} }; - const mp::OptionValueInfo values_allexpr_acceptance[2] = { + const mp::OptionValueInfo values_allexpr_acceptance_[2] = { { "0", "Not accepted, all expressions will be treated as flat constraints, " "or redefined", 0}, { "1", "Accepted. See the individual acc:... options", 1} @@ -1137,13 +1137,13 @@ class FlatConverter : options_.passSOCP2QC_, socp2qc_values_); options_.passSOCP2QC_ = DefaultSOCP2QCMode(); - if (IfAcceptingNLOutput()) + if constexpr (IfAcceptingNLOutput()) GetEnv().AddStoredOption("acc:_expr", fmt::format( "Solver acceptance level for all expressions, " "default {}:\n\n.. value-table::", options_.accExpr_).c_str(), - options_.accExpr_, values_allexpr_acceptance); + options_.accExpr_, values_allexpr_acceptance_); else GetEnv().AddStoredOption("acc:_expr", "HIDDEN", options_.accExpr_, 0, 1); diff --git a/include/mp/expr/model_api_base.h b/include/mp/flat/expr/model_api_base.h similarity index 97% rename from include/mp/expr/model_api_base.h rename to include/mp/flat/expr/model_api_base.h index 246ac3257..ae6170cc1 100644 --- a/include/mp/expr/model_api_base.h +++ b/include/mp/flat/expr/model_api_base.h @@ -35,7 +35,7 @@ class BasicExprModelAPI /// Placeholder for GetTypeName() static const char* GetTypeName() { return "BasicExprModelAPI"; } -/// A ModelAPI accepting NL trees can declare this. +/// A ModelAPI accepting NL trees should declare this. /// /// - NotAccepted: not compiled /// - AcceptedButNotRecommended: compiled but off by default (option acc:_expr) diff --git a/include/mp/flat/model_api_base.h b/include/mp/flat/model_api_base.h index 9d736a8bf..fc80b3ed7 100644 --- a/include/mp/flat/model_api_base.h +++ b/include/mp/flat/model_api_base.h @@ -83,6 +83,28 @@ enum class ExpressionAcceptanceLevel { Recommended=2 }; + +/// ... then for a certain constraint it can be specified +#define ACCEPT_CONSTRAINT(ConstrType, level, con_grp) \ +static mp::ConstraintAcceptanceLevel \ + AcceptanceLevel(const ConstrType*) \ +{ return mp::ConstraintAcceptanceLevel::level; } \ + static constexpr int \ + GroupNumber(const ConstrType*) { return con_grp; } + +/// ... and/or for expressions, e.g., AbsExpression: +#define ACCEPT_EXPRESSION(FlatExprType, level) \ +static mp::ExpressionAcceptanceLevel \ +AcceptanceLevel(const FlatExprType*) { \ + typename FlatExprType::FlatConType* pc{}; \ + static_assert( std::is_same_v >, \ + #FlatExprType \ + " should be an ExprWrapper<> - use standard MP flat expressions" ); \ + return mp::ExpressionAcceptanceLevel::level; \ +} // pc{} checks that FlatExprType = ExprWrapper<...> + + /// Constraint group names. /// @todo Keep consistent with the \a ConstraintGroups enum. const char* ConGroupName(int cg); @@ -173,16 +195,24 @@ class BasicFlatModelAPI { return CG_Default; } - /// By default, we say constraint XYZ is not accepted but... - static constexpr ConstraintAcceptanceLevel AcceptanceLevel(const BasicConstraint*) { + /// By default, we say constraint XYZ is not accepted + static constexpr ConstraintAcceptanceLevel AcceptanceLevel( + const BasicConstraint*) { return ConstraintAcceptanceLevel::NotAccepted; } - /// By default, no expressions + /// By default, no expressions (global switch) static constexpr ExpressionAcceptanceLevel \ ExpressionInterfaceAcceptanceLevel() { return ExpressionAcceptanceLevel::NotAccepted; } + /// By default, we say expression XYZ is not accepted + template + static constexpr ExpressionAcceptanceLevel AcceptanceLevel( + const ExprWrapper*) { + return ExpressionAcceptanceLevel::NotAccepted; + } + /// Specifically, ask if the solver accepts non-convex quadratic constraints static constexpr bool AcceptsNonconvexQC() { return false; } @@ -201,14 +231,6 @@ class BasicFlatModelAPI { }; -/// ... then for a certain constraint it can be specified -#define ACCEPT_CONSTRAINT(ConstrType, level, con_grp) \ - static mp::ConstraintAcceptanceLevel \ - AcceptanceLevel(const ConstrType*) \ - { return mp::ConstraintAcceptanceLevel::level; } \ - static constexpr int \ - GroupNumber(const ConstrType*) { return con_grp; } - } // namespace mp #endif // FLAT_MODEL_API_BASE_H_ diff --git a/solvers/gurobi/gurobimodelapi.h b/solvers/gurobi/gurobimodelapi.h index 2aeb4d721..f264f0b0d 100644 --- a/solvers/gurobi/gurobimodelapi.h +++ b/solvers/gurobi/gurobimodelapi.h @@ -1,12 +1,8 @@ #ifndef GUROBIMODELAPI_H #define GUROBIMODELAPI_H -#include - #include "mp/env.h" #include "mp/flat/model_api_base.h" -#include "mp/flat/constr_std.h" - #include "gurobicommon.h" namespace mp { diff --git a/solvers/scipmp/scipmpmodelapi.h b/solvers/scipmp/scipmpmodelapi.h index 1bd16a8c6..6f5144428 100644 --- a/solvers/scipmp/scipmpmodelapi.h +++ b/solvers/scipmp/scipmpmodelapi.h @@ -1,12 +1,9 @@ #ifndef SCIPMODELAPI_H #define SCIPMODELAPI_H -#include - #include "mp/env.h" #include "scipmpcommon.h" -#include "mp/expr/model_api_base.h" -#include "mp/flat/constr_std.h" +#include "mp/flat/expr/model_api_base.h" namespace mp { @@ -47,7 +44,12 @@ class ScipModelAPI : //////////////////////////// GENERAL CONSTRAINTS //////////////////////////// USE_BASE_CONSTRAINT_HANDLERS(BaseModelAPI) + //////////////////////////// EXPRESSION TREES //////////////////////////// + /// + /// 'AcceptedButNotRecommended' would outline each expression + /// with an auxiliary variable. + /// See also per-expression type switches. ACCEPT_EXPRESSION_INTERFACE(Recommended); /// For each suppoted constraint type, add the ACCEPT_CONSTRAINT macro @@ -61,6 +63,11 @@ class ScipModelAPI : /// - Logical, counting, piecewise-linear constraints. /// See \a constr_std.h and other drivers. + /// For each expression, + /// say ACCEPT_EXPRESSION(Recommended) + /// or ACCEPT_EXPRESSION(AcceptedButNotRecommended). + ACCEPT_EXPRESSION(ExpExpression, Recommended) + void AddExpression(const ExpExpression& ); /// The linear range constraint, if fully supported with basis info etc. ACCEPT_CONSTRAINT(LinConRange, Recommended, CG_Linear) diff --git a/solvers/visitor/visitormodelapi.h b/solvers/visitor/visitormodelapi.h index d30b2c045..fb77cea17 100644 --- a/solvers/visitor/visitormodelapi.h +++ b/solvers/visitor/visitormodelapi.h @@ -1,12 +1,9 @@ #ifndef VISITORMODELAPI_H #define VISITORMODELAPI_H -#include - #include "mp/env.h" #include "visitorcommon.h" #include "mp/flat/model_api_base.h" -#include "mp/flat/constr_std.h" namespace mp {