From 9ee422314c66d4e3e4474092670fc7e8eae14639 Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Thu, 17 Oct 2024 15:37:18 +1100 Subject: [PATCH] MP2NL:logicalize alg expr #237 When logical argument required but an algerbaic expression is given, write it as !=0 --- include/mp/flat/constr_2_expr.h | 14 ++++- include/mp/flat/constr_prop_down.h | 2 +- include/mp/flat/converter.h | 29 ++++++++- include/mp/flat/nl_expr/model_api_base.h | 5 ++ include/mp/flat/prepro_prod.h | 12 ++-- solvers/mp2nl/mp2nlmodelapi.cc | 75 ++++++++++++++++-------- solvers/mp2nl/mp2nlmodelapi.h | 18 ++++-- 7 files changed, 110 insertions(+), 45 deletions(-) diff --git a/include/mp/flat/constr_2_expr.h b/include/mp/flat/constr_2_expr.h index 896a59d39..e00ea9323 100644 --- a/include/mp/flat/constr_2_expr.h +++ b/include/mp/flat/constr_2_expr.h @@ -380,7 +380,7 @@ class Constraints2Expr { template bool HandleLogicalArgs( const AlgebraicConstraint& con, int i) { - if (HandleLogicalArgs_SpecialCases(con, i)) + if (false && HandleLogicalArgs_SpecialCases(con, i)) return true; VisitArguments(con, MarkVarIfLogical_); // Mark as proper vars return false; // don't remove immediately @@ -395,10 +395,15 @@ class Constraints2Expr { } /// Special linear cases. + /// Not doing any more because these simplifications + /// interfere withg result variable marking. + /// These simplifications are general presolve + /// and should better have been done in normal conversion stage. /// @todo atleast, atmost, exactly template bool HandleLogicalArgs_SpecialCases( const AlgebraicConstraint& con, int ) { + /* const auto& body = con.GetBody(); if (!con.lb() && !con.ub() // == 0.0 && 2==body.size()) { // 2 terms @@ -433,7 +438,7 @@ class Constraints2Expr { MPD( FixAsTrue(body.var(0)) ); return true; } - } + } */ return false; } @@ -441,6 +446,8 @@ class Constraints2Expr { /// if \a NLConstraint's are accepted. Otherwise, /// explicify the expression and convert to /// \a LinConLE/EQ/GE/Range. + /// @return true iff the original constraint should be deleted. + /// @todo leave QCP terms here if accepted, even if other expr terms? template bool ConvertToNLCon( const AlgebraicConstraint& con, int ) { @@ -504,7 +511,8 @@ class Constraints2Expr { LinTerms lt_varsonly; LinTerms lt_in_expr = SplitLinTerms(qobj.GetLinTerms(), lt_varsonly); // Have expression(s) or QP terms? - // Might need to hide them into the expression part + // Might need to hide them into the expression part. + // @todo leave QP terms here if accepted, even if other expr terms? if (lt_in_expr.size() || (qobj.GetQPTerms().size() && (!MPCD(IfPassQuadObj()) // cannot or want not diff --git a/include/mp/flat/constr_prop_down.h b/include/mp/flat/constr_prop_down.h index 5f2038008..64daeff04 100644 --- a/include/mp/flat/constr_prop_down.h +++ b/include/mp/flat/constr_prop_down.h @@ -32,7 +32,7 @@ class ConstraintPropagatorsDown { Context ctx) { MPD( NarrowVarBounds(con.GetResultVar(), lb, ub) ); con.AddContext(ctx); - PropagateResult2LinTerms(con.GetAffineExpr(), + PropagateResult2LinTerms(con.GetAffineExpr(), // @todo better in special cases MPD( MinusInfty() ), MPD( Infty() ), +ctx); } diff --git a/include/mp/flat/converter.h b/include/mp/flat/converter.h index 13140fda6..dee2c8509 100644 --- a/include/mp/flat/converter.h +++ b/include/mp/flat/converter.h @@ -374,13 +374,23 @@ class FlatConverter : } /// Query if a constraint type - /// is natively accepted by the solver. + /// is natively accepted by the solver (and user). /// The parameter is only needed for type. template ConstraintAcceptanceLevel GetConstraintAcceptance(Con* ) const { return GET_CONST_CONSTRAINT_KEEPER(Con).GetChosenAcceptanceLevel(); } + /// Query if an expression type + /// is natively accepted by the solver (and user). + /// The parameter is only needed for type. + template + ExpressionAcceptanceLevel GetConstraintAcceptanceEXPR( + const ExprWrapper< Con >* ) const { + return GET_CONST_CONSTRAINT_KEEPER(Con).GetChosenAcceptanceLevelEXPR(); + } + + /// Query the number of addable constraints of type. template int GetNumberOfAddable(Con* ) const { @@ -448,19 +458,32 @@ class FlatConverter : return false; } - /// Check whether ModelAPI accepts and recommends the constraint + /// Check whether ModelAPI and user accept and recommend the constraint template bool ModelAPIOk() const { return ModelAPIAcceptsAndRecommends((const Constraint*)0); } - /// Check whether ModelAPI accepts and recommends the constraint + /// Check whether ModelAPI and user accept and recommend the constraint template bool ModelAPIAcceptsAndRecommends(const Constraint* pcon) const { return ConstraintAcceptanceLevel::Recommended == GetConstraintAcceptance(pcon); } + /// Check whether ModelAPI and user accept and recommend the expression + template + bool ModelAPIOkEXPR() const { + return ModelAPIAcceptsAndRecommendsEXPR((const Expression*)0); + } + + /// Check whether ModelAPI and user accept and recommend the expression + template + bool ModelAPIAcceptsAndRecommendsEXPR(const Expression* pcon) const { + return ExpressionAcceptanceLevel::Recommended == + GetConstraintAcceptanceEXPR(pcon); + } + /// Generic adapter for old non-bridged Convert() methods /// /// New way is to use the \a i parameter for bridging diff --git a/include/mp/flat/nl_expr/model_api_base.h b/include/mp/flat/nl_expr/model_api_base.h index f23f155ca..cdb5366d7 100644 --- a/include/mp/flat/nl_expr/model_api_base.h +++ b/include/mp/flat/nl_expr/model_api_base.h @@ -77,6 +77,11 @@ class BasicExprModelAPI /// NL model item accessors + /// @brief Is expression logical? + template + static bool IsLogical(const ExprWrapper& expr) + { return expr.GetFlatConstraint().IsLogical(); } + /// Get num linear terms int GetLinSize(const NLConstraint& nlc) const { return nlc.GetMainCon().size(); diff --git a/include/mp/flat/prepro_prod.h b/include/mp/flat/prepro_prod.h index b1c15e626..06684896c 100644 --- a/include/mp/flat/prepro_prod.h +++ b/include/mp/flat/prepro_prod.h @@ -60,7 +60,7 @@ class PreproProd std::make_tuple< TermCmp, FlatExpr, std::pair >( {}, GetFlt().Visit(e), {} ); - terms_flt_.push_back(t); + terms_flt_.push_back( std::move(t) ); } protected: @@ -88,7 +88,7 @@ class PreproProd is_int ? 2 : 3; std::get<0>(tpl) = {category, bnds.ub()-bnds.lb() }; std::get<2>(tpl) = - {bnds.lb(), bnds.ub() }; + {bnds.lb(), bnds.ub()}; } std::sort(terms_flt_.begin(), terms_flt_.end(), [](const auto& tpl1, const auto& tpl2) { @@ -115,7 +115,8 @@ class PreproProd std::get<2>(terms_flt_[i]).second - std::get<2>(terms_flt_[i]).first ); int binvar = GetFlt().Convert2Var(std::move(std::get<1>(terms_flt_[i]))); - if (-1.0 == std::get<2>(terms_flt_[i]).first) { // negated binary + auto is_lb_minus1 = (-1.0 == std::get<2>(terms_flt_[i]).first); + if (is_lb_minus1) { // negated binary coef00 *= -1; binvar = GetFlt().Convert2Var( { {-1.0}, {binvar} } ); } else { @@ -124,6 +125,7 @@ class PreproProd args_forall.push_back( binvar ); } result = GetFlt().AssignResult2Args( AndConstraint{args_forall} ); + // Context should be set when adding the top contraint } result *= coef00; @@ -146,9 +148,7 @@ class PreproProd Flattener& flt_; /// tuple: comparator, term, bounds std::vector< - std::tuple< TermCmp, FlatExpr, std::pair > - - > + std::tuple< TermCmp, FlatExpr, std::pair > > terms_flt_; int n_terms_const_ = 0; int n_terms_binary_ = 0; diff --git a/solvers/mp2nl/mp2nlmodelapi.cc b/solvers/mp2nl/mp2nlmodelapi.cc index 9ce07cb6d..36cb22c40 100644 --- a/solvers/mp2nl/mp2nlmodelapi.cc +++ b/solvers/mp2nl/mp2nlmodelapi.cc @@ -41,7 +41,8 @@ void MP2NLModelAPI::AddVariables(const VarArrayDef& vad) { void MP2NLModelAPI::SetLinearObjective( int iobj, const LinearObjective& lo ) { assert(iobj == (int)obj_info_.size()); - obj_info_.push_back(MakeItemInfo(lo, StaticItemTypeID::ID_LinearObjective)); + obj_info_.push_back( + MakeItemInfo(lo, StaticItemTypeID::ID_LinearObjective, false)); } void MP2NLModelAPI::SetQuadraticObjective(int iobj, const QuadraticObjective& qo) { @@ -52,21 +53,22 @@ void MP2NLModelAPI::SetQuadraticObjective(int iobj, const QuadraticObjective& qo void MP2NLModelAPI::SetNLObjective( int iobj, const NLObjective& nlo ) { assert(iobj == (int)obj_info_.size()); - obj_info_.push_back(MakeItemInfo(nlo, StaticItemTypeID::ID_NLObjective)); + obj_info_.push_back(MakeItemInfo(nlo, StaticItemTypeID::ID_NLObjective, false)); } -void MP2NLModelAPI::AddConstraint(const LinConRange& lc) -{ alg_con_info_.push_back(MakeItemInfo(lc, StaticItemTypeID::ID_LinConRange)); } +void MP2NLModelAPI::AddConstraint(const LinConRange& lc) { + alg_con_info_.push_back(MakeItemInfo(lc, StaticItemTypeID::ID_LinConRange, false)); +} void MP2NLModelAPI::AddConstraint(const LinConLE& lc) -{ alg_con_info_.push_back(MakeItemInfo(lc, StaticItemTypeID::ID_LinConLE)); } +{ alg_con_info_.push_back(MakeItemInfo(lc, StaticItemTypeID::ID_LinConLE, false)); } void MP2NLModelAPI::AddConstraint(const LinConEQ& lc) -{ alg_con_info_.push_back(MakeItemInfo(lc, StaticItemTypeID::ID_LinConEQ)); } +{ alg_con_info_.push_back(MakeItemInfo(lc, StaticItemTypeID::ID_LinConEQ, false)); } void MP2NLModelAPI::AddConstraint(const LinConGE& lc) -{ alg_con_info_.push_back(MakeItemInfo(lc, StaticItemTypeID::ID_LinConGE)); } +{ alg_con_info_.push_back(MakeItemInfo(lc, StaticItemTypeID::ID_LinConGE, false)); } /// To access information from an NLConstraint, @@ -76,43 +78,46 @@ void MP2NLModelAPI::AddConstraint(const LinConGE& lc) /// /// Implementation follows partly reader_nl.cc from SCIP. void MP2NLModelAPI::AddConstraint( const NLConstraint& nlc ) -{ alg_con_info_.push_back(MakeItemInfo(nlc, StaticItemTypeID::ID_NLConstraint)); } +{ alg_con_info_.push_back(MakeItemInfo(nlc, StaticItemTypeID::ID_NLConstraint, false)); } void MP2NLModelAPI::AddConstraint( const NLAssignEQ& nlae ) -{ alg_con_info_.push_back(MakeItemInfo(nlae, StaticItemTypeID::ID_NLAssignEQ)); } +{ alg_con_info_.push_back(MakeItemInfo(nlae, StaticItemTypeID::ID_NLAssignEQ, false)); } void MP2NLModelAPI::AddConstraint( const NLAssignLE& nlae ) -{ alg_con_info_.push_back(MakeItemInfo(nlae, StaticItemTypeID::ID_NLAssignLE)); } +{ alg_con_info_.push_back(MakeItemInfo(nlae, StaticItemTypeID::ID_NLAssignLE, false)); } void MP2NLModelAPI::AddConstraint( const NLAssignGE& nlae ) -{ alg_con_info_.push_back(MakeItemInfo(nlae, StaticItemTypeID::ID_NLAssignGE)); } +{ alg_con_info_.push_back(MakeItemInfo(nlae, StaticItemTypeID::ID_NLAssignGE, false)); } void MP2NLModelAPI::AddConstraint(const NLComplementarity& cc) -{ alg_con_info_.push_back(MakeItemInfo(cc, StaticItemTypeID::ID_NLComplementarity)); } +{ alg_con_info_.push_back(MakeItemInfo(cc, StaticItemTypeID::ID_NLComplementarity, false)); } void MP2NLModelAPI::AddConstraint( const NLLogical& nll ) -{ log_con_info_.push_back(MakeItemInfo(nll, StaticItemTypeID::ID_NLLogical)); } +{ log_con_info_.push_back(MakeItemInfo(nll, StaticItemTypeID::ID_NLLogical, true)); } void MP2NLModelAPI::AddConstraint( const NLReifEquiv& nll ) -{ log_con_info_.push_back(MakeItemInfo(nll, StaticItemTypeID::ID_NLReifEquiv)); } +{ log_con_info_.push_back(MakeItemInfo(nll, StaticItemTypeID::ID_NLReifEquiv, true)); } void MP2NLModelAPI::AddConstraint( const NLReifImpl& nll ) -{ log_con_info_.push_back(MakeItemInfo(nll, StaticItemTypeID::ID_NLReifImpl)); } +{ log_con_info_.push_back(MakeItemInfo(nll, StaticItemTypeID::ID_NLReifImpl, true)); } void MP2NLModelAPI::AddConstraint( const NLReifRimpl& nll ) -{ log_con_info_.push_back(MakeItemInfo(nll, StaticItemTypeID::ID_NLReifRimpl)); } +{ log_con_info_.push_back(MakeItemInfo(nll, StaticItemTypeID::ID_NLReifRimpl, true)); } void MP2NLModelAPI::AddConstraint(const IndicatorConstraintLinLE &ic) -{ log_con_info_.push_back(MakeItemInfo(ic, StaticItemTypeID::ID_IndicatorConstraintLinLE)); } +{ log_con_info_.push_back( + MakeItemInfo(ic, StaticItemTypeID::ID_IndicatorConstraintLinLE, true)); } void MP2NLModelAPI::AddConstraint(const IndicatorConstraintLinEQ &ic) -{ log_con_info_.push_back(MakeItemInfo(ic, StaticItemTypeID::ID_IndicatorConstraintLinEQ)); } +{ log_con_info_.push_back( + MakeItemInfo(ic, StaticItemTypeID::ID_IndicatorConstraintLinEQ, true)); } void MP2NLModelAPI::AddConstraint(const IndicatorConstraintLinGE &ic) -{ log_con_info_.push_back(MakeItemInfo(ic, StaticItemTypeID::ID_IndicatorConstraintLinGE)); } +{ log_con_info_.push_back( + MakeItemInfo(ic, StaticItemTypeID::ID_IndicatorConstraintLinGE, true)); } void MP2NLModelAPI::AddConstraint(const SOS1Constraint& sos) -{ sos_info_.push_back(MakeItemInfo(sos, StaticItemTypeID::ID_SOS1Constraint)); } +{ sos_info_.push_back(MakeItemInfo(sos, StaticItemTypeID::ID_SOS1Constraint, false)); } void MP2NLModelAPI::AddConstraint(const SOS2Constraint& sos) -{ sos_info_.push_back(MakeItemInfo(sos, StaticItemTypeID::ID_SOS2Constraint)); } +{ sos_info_.push_back(MakeItemInfo(sos, StaticItemTypeID::ID_SOS2Constraint, false)); } template @@ -131,7 +136,12 @@ MP2NL_Expr MP2NLModelAPI::AddExpression( template MP2NL_Expr MP2NLModelAPI::StoreMP2NLExprID( const Expr &expr, ExpressionTypeID eid) { - expr_info_.push_back(MakeItemInfo(expr, eid)); + // std::printf(" Storing MP2NL_Expr[%ld]: type %s, logical = %d, @%p\n", + // expr_info_.size(), + // expr.GetFlatConstraint().GetTypeName(), + // IsLogical(expr), &expr); + expr_info_.push_back( + MakeItemInfo(expr, eid, IsLogical(expr))); expr_counter_.push_back(0); return MakeExprID( int(expr_info_.size()-1) ); } @@ -835,6 +845,8 @@ void MP2NLModelAPI::FdArgs( template void MP2NLModelAPI::FdLogicArgs( const MPExpr& e, ArgWriter ew_arg) { + // std::printf(" Feed '%s': %d args\n", + // GetItemName(e), GetNumArguments(e)); for (int i=0; i void MP2NLModelAPI::FeedLogicalExpression( MP2NL_Expr mp2nle, ArgWriter& aw) { if (mp2nle.IsVariable()) { - auto arg2 = aw.OPut2(nl::EQ); + auto arg2 = aw.OPut2(nl::NE); arg2.EPut(mp2nle); - arg2.NPut(1.0); - } else - aw.EPut(mp2nle); + arg2.NPut(0.0); + } else { + bool f_logical = false; + if (mp2nle.IsExpression()) { + f_logical + = expr_info_.at(mp2nle.GetExprIndex()).IsLogical(); + } + if (!f_logical) { + auto argw = aw.OPut2(nl::NE); + argw.EPut(mp2nle); + argw.NPut(0.0); + } else + aw.EPut(mp2nle); + } } template diff --git a/solvers/mp2nl/mp2nlmodelapi.h b/solvers/mp2nl/mp2nlmodelapi.h index c6b19eba0..88c23ab0f 100644 --- a/solvers/mp2nl/mp2nlmodelapi.h +++ b/solvers/mp2nl/mp2nlmodelapi.h @@ -1293,16 +1293,19 @@ class MP2NLModelAPI class ItemInfo { public: /// Construct - ItemInfo (BasicItemDispatcher& disp, void* pitem + ItemInfo (BasicItemDispatcher& disp, void* pitem, + bool fLogical //, StaticItemTypeID iid, ExpressionTypeID eid ) - : disp_(disp), p_item_(pitem) + : disp_(disp), p_item_(pitem), f_logical_(fLogical) // no storing, itemID_(iid), exprID_(eid) { } /// Get dispatcher BasicItemDispatcher& GetDispatcher() const { return disp_; } /// Get &item void* GetPItem() const { return p_item_; } + /// Is logical? + bool IsLogical() const { return f_logical_; } /// Is a static type? bool IsItemTypeStatic() const { return GetDispatcher().IsItemTypeStatic(); } @@ -1316,22 +1319,25 @@ class MP2NLModelAPI private: BasicItemDispatcher& disp_; void* p_item_; + bool f_logical_ {}; // StaticItemTypeID itemID_ {StaticItemTypeID::ID_None}; // ExpressionTypeID exprID_ {ExpressionTypeID::ID_None}; }; /// Fill static item info template - ItemInfo MakeItemInfo(const Item& i, StaticItemTypeID ) { - return { GetItemDispatcher(), (void*)&i + ItemInfo MakeItemInfo( + const Item& i, StaticItemTypeID , bool fLogical) { + return { GetItemDispatcher(), (void*)&i, fLogical // , sid, ExpressionTypeID::ID_None }; } /// Fill expression item info template - ItemInfo MakeItemInfo(const Item& i, ExpressionTypeID ) { - return { GetItemDispatcher(), (void*)&i + ItemInfo MakeItemInfo( + const Item& i, ExpressionTypeID , bool fLogical) { + return { GetItemDispatcher(), (void*)&i, fLogical //, StaticItemTypeID::ID_None, eid }; }