From 78bed783e8b25b55edc3bc00aac7b712760f931f Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Tue, 20 Aug 2024 21:36:27 +1000 Subject: [PATCH] More SCIP expressions #237 --- include/mp/flat/constr_keeper.h | 3 + include/mp/flat/nl_expr/model_api_base.h | 68 ++++++++++++- solvers/scipmp/scipmpmodelapi.cc | 119 +++++++++++++++++++++-- solvers/scipmp/scipmpmodelapi.h | 67 +++++++++---- 4 files changed, 232 insertions(+), 25 deletions(-) diff --git a/include/mp/flat/constr_keeper.h b/include/mp/flat/constr_keeper.h index 01dacf342..82866ddb9 100644 --- a/include/mp/flat/constr_keeper.h +++ b/include/mp/flat/constr_keeper.h @@ -200,6 +200,9 @@ class ConstraintKeeper final void AddUnbridgedToBackend( BasicFlatModelAPI& be, const std::vector* pvnam) override { + if (ExpressionAcceptanceLevel::NotAccepted + == GetChosenAcceptanceLevelEXPR() + || !GetConverter().IfWantNLOutput()) try { AddAllUnbridged(be, pvnam); } catch (const std::exception& exc) { diff --git a/include/mp/flat/nl_expr/model_api_base.h b/include/mp/flat/nl_expr/model_api_base.h index 9f6f433b8..b32b61b89 100644 --- a/include/mp/flat/nl_expr/model_api_base.h +++ b/include/mp/flat/nl_expr/model_api_base.h @@ -119,9 +119,75 @@ class BasicExprModelAPI return GetInitExpression(nll.GetResultVar()); } + /// Get the expression term of an \a NLReification. + template + ExprType GetExpression(const NLReification& nll) { + assert( nll.GetResultVar()>=0 ); + return GetInitExpression(nll.GetResultVar()); + } + + /// Get the variable of an \a NLReification. + template + int GetVariable(const NLReification& nll) { + assert( nll.GetResultVar()>=0 ); + return nll.GetResultVar(); + } + + /// GetLinSize(le) + int GetLinSize(const LinExpression& le) const + { return le.GetFlatConstraint().GetAffineExpr().size(); } + /// GetLinCoef(le, i) + double GetLinCoef(const LinExpression& le, int i) const + { return le.GetFlatConstraint().GetAffineExpr().coef(i); } + /// GetLinTerm(le, i) + Expr GetLinTerm(const LinExpression& le, int i) + { return GetInitExpression(le.GetFlatConstraint().GetAffineExpr().var(i)); } + /// GetConstTerm(le) + double GetConstTerm(const LinExpression& le) const + { return le.GetFlatConstraint().GetAffineExpr().constant_term(); } + + /// GetLinSize(qe) + int GetLinSize(const QuadExpression& qe) const + { return qe.GetFlatConstraint().GetQuadExpr().GetBody().GetLinTerms().size(); } + /// GetLinCoef(qe, i) + double GetLinCoef(const QuadExpression& qe, int i) const + { return qe.GetFlatConstraint().GetQuadExpr().GetBody().GetLinTerms().coef(i); } + /// GetLinTerm(qe, i) + Expr GetLinTerm(const QuadExpression& qe, int i) + { return GetInitExpression(qe.GetFlatConstraint().GetQuadExpr().GetBody().GetLinTerms().var(i)); } + + /// GetQuadSize(qe) + int GetQuadSize(const QuadExpression& qe) const + { return qe.GetFlatConstraint().GetQuadExpr().GetBody().GetQPTerms().size(); } + /// GetQuadCoef(qe, i) + double GetQuadCoef(const QuadExpression& qe, int i) const + { return qe.GetFlatConstraint().GetQuadExpr().GetBody().GetQPTerms().coef(i); } + /// GetQuadTerm1(qe, i) + Expr GetQuadTerm1(const QuadExpression& qe, int i) + { return GetInitExpression(qe.GetFlatConstraint().GetQuadExpr().GetBody().GetQPTerms().var1(i)); } + /// GetQuadTerm2(qe, i) + Expr GetQuadTerm2(const QuadExpression& qe, int i) + { return GetInitExpression(qe.GetFlatConstraint().GetQuadExpr().GetBody().GetQPTerms().var2(i)); } + + /// GetConstTerm(qe) + double GetConstTerm(const QuadExpression& qe) const + { return qe.GetFlatConstraint().GetQuadExpr().constant_term(); } + + /// Get argument expression [\a i] + template + Expr GetArgExpression(const FlatExpression& fe, int i) + { return GetInitExpression(fe.GetFlatConstraint().GetArguments().at(i)); } + + /// Get expression parameter [\a i] + template + double GetParameter(const FlatExpression& fe, int i) + { return fe.GetFlatConstraint().GetParameters().at(i); } + ////////////////////// INTERNAL //////////////////////// - /// Get InitExpression() + /// Get InitExpression(). + /// Solver expression for the given implicit variable, + /// or for the given independent variable. Expr GetInitExpression(int i_expr) { assert(i_expr < is_expr_stored_.size()); assert(i_expr < expr_stored_.size()); diff --git a/solvers/scipmp/scipmpmodelapi.cc b/solvers/scipmp/scipmpmodelapi.cc index 410b6380b..e22a005bb 100644 --- a/solvers/scipmp/scipmpmodelapi.cc +++ b/solvers/scipmp/scipmpmodelapi.cc @@ -111,6 +111,14 @@ void ScipModelAPI::AddConstraint(const LinConGE& lc) { } +SCIP_EXPR* ScipModelAPI::AddExpression(const AbsExpression &abse) { + SCIP_EXPR* result_expr; + SCIP_CCALL( SCIPcreateExprAbs(getSCIP(), &result_expr, + GetArgExpression(abse, 0), NULL, NULL) ); + return result_expr; +} + + void ScipModelAPI::AddConstraint(const AbsConstraint &absc) { SCIP_VAR* x = getPROBDATA()->vars[absc.GetArguments()[0]]; SCIP_VAR* res = getPROBDATA()->vars[absc.GetResultVar()]; @@ -306,16 +314,82 @@ void ScipModelAPI::AddConstraint( const NLLogical& nll ) { SCIP_CCALL( SCIPaddCons(getSCIP(), getPROBDATA()->nlconss.back()) ); } -void ScipModelAPI::AddConstraint( const NLEquivalence& nll ) { } -void ScipModelAPI::AddConstraint( const NLImpl& nll ) { } -void ScipModelAPI::AddConstraint( const NLRimpl& nll ) { } +void ScipModelAPI::AddConstraint( const NLEquivalence& nll ) { + getPROBDATA()->nlconss.push_back(nullptr); + + SCIP_CCALL( SCIPcreateConsBasicNonlinear( + getSCIP(), &getPROBDATA()->nlconss.back(), nll.GetName(), + GetExpression(nll), 0.0, 0.0) ); + SCIP_CCALL( SCIPaddCons(getSCIP(), getPROBDATA()->nlconss.back()) ); + SCIP_CCALL( SCIPaddLinearVarNonlinear( + getSCIP(), getPROBDATA()->nlconss.back(), + getPROBDATA()->vars[ GetVariable(nll) ], -1.0) ); +} +void ScipModelAPI::AddConstraint( const NLImpl& nll ) { + getPROBDATA()->nlconss.push_back(nullptr); + + SCIP_CCALL( SCIPcreateConsBasicNonlinear( + getSCIP(), &getPROBDATA()->nlconss.back(), nll.GetName(), + GetExpression(nll), 0.0, Infinity()) ); + SCIP_CCALL( SCIPaddCons(getSCIP(), getPROBDATA()->nlconss.back()) ); + SCIP_CCALL( SCIPaddLinearVarNonlinear( + getSCIP(), getPROBDATA()->nlconss.back(), + getPROBDATA()->vars[ GetVariable(nll) ], -1.0) ); +} +void ScipModelAPI::AddConstraint( const NLRimpl& nll ) { + getPROBDATA()->nlconss.push_back(nullptr); + + SCIP_CCALL( SCIPcreateConsBasicNonlinear( + getSCIP(), &getPROBDATA()->nlconss.back(), nll.GetName(), + GetExpression(nll), MinusInfinity(), 0.0) ); + SCIP_CCALL( SCIPaddCons(getSCIP(), getPROBDATA()->nlconss.back()) ); + SCIP_CCALL( SCIPaddLinearVarNonlinear( + getSCIP(), getPROBDATA()->nlconss.back(), + getPROBDATA()->vars[ GetVariable(nll) ], -1.0) ); +} SCIP_EXPR* ScipModelAPI::AddExpression(const LinExpression &le) { - return {}; + std::vector terms(GetLinSize(le)); + std::vector coefs(GetLinSize(le)); + + for (size_t i=0; i terms(GetLinSize(qe) + GetQuadSize(qe)); + std::vector coefs(GetLinSize(qe) + GetQuadSize(qe)); + + for (int i=0; ivars[cc.GetArguments()[0]]; SCIP_VAR* res = getPROBDATA()->vars[cc.GetResultVar()]; @@ -442,6 +526,15 @@ void ScipModelAPI::AddConstraint(const LogConstraint &cc) { SCIP_CCALL( SCIPreleaseExpr(getSCIP(), &xexpr) ); } +SCIP_EXPR* ScipModelAPI::AddExpression(const PowExpression &ee) { + SCIP_EXPR* result_expr; + SCIP_CCALL( SCIPcreateExprPow(getSCIP(), &result_expr, + GetArgExpression(ee, 0), + GetParameter(ee, 0), + NULL, NULL) ); + return result_expr; +} + void ScipModelAPI::AddConstraint(const PowConstraint &cc) { SCIP_VAR* x = getPROBDATA()->vars[cc.GetArguments()[0]]; SCIP_VAR* res = getPROBDATA()->vars[cc.GetResultVar()]; @@ -470,6 +563,13 @@ void ScipModelAPI::AddConstraint(const PowConstraint &cc) { SCIP_CCALL( SCIPreleaseExpr(getSCIP(), &xexpr) ); } +SCIP_EXPR* ScipModelAPI::AddExpression(const SinExpression &ee) { + SCIP_EXPR* result_expr; + SCIP_CCALL( SCIPcreateExprSin(getSCIP(), &result_expr, + GetArgExpression(ee, 0), NULL, NULL) ); + return result_expr; +} + void ScipModelAPI::AddConstraint(const SinConstraint &cc) { SCIP_VAR* x = getPROBDATA()->vars[cc.GetArguments()[0]]; SCIP_VAR* res = getPROBDATA()->vars[cc.GetResultVar()]; @@ -498,6 +598,13 @@ void ScipModelAPI::AddConstraint(const SinConstraint &cc) { SCIP_CCALL( SCIPreleaseExpr(getSCIP(), &xexpr) ); } +SCIP_EXPR* ScipModelAPI::AddExpression(const CosExpression &ee) { + SCIP_EXPR* result_expr; + SCIP_CCALL( SCIPcreateExprCos(getSCIP(), &result_expr, + GetArgExpression(ee, 0), NULL, NULL) ); + return result_expr; +} + void ScipModelAPI::AddConstraint(const CosConstraint &cc) { SCIP_VAR* x = getPROBDATA()->vars[cc.GetArguments()[0]]; SCIP_VAR* res = getPROBDATA()->vars[cc.GetResultVar()]; diff --git a/solvers/scipmp/scipmpmodelapi.h b/solvers/scipmp/scipmpmodelapi.h index a26285971..17ee74e1b 100644 --- a/solvers/scipmp/scipmpmodelapi.h +++ b/solvers/scipmp/scipmpmodelapi.h @@ -183,16 +183,42 @@ class ScipModelAPI : ACCEPT_EXPRESSION(QuadExpression, Recommended); SCIP_EXPR* AddExpression(const QuadExpression& le); - /// Linear indicator constraints can be used as - /// auxiliary constraints for logical conditions. - /// If not handled, the compared expressions need - /// deducible finite bounds for a big-M redefinition. + /// Each expression can be accpeted as a proper expression, + /// or a flat constraint var == expr (with var arguments). + /// + /// For each expression, + /// say ACCEPT_EXPRESSION(Recommended) + /// and/or ACCEPT_EXPRESSION(AcceptedButNotRecommended). + /// This can be user-configured via options 'acc:exp' etc. + /// + /// Use accessor: GetArgExpression(ee, 0) + /// - don't ExpExpression's methods. + /// + /// Similar for other expression types. + ACCEPT_EXPRESSION(AbsExpression, Recommended) + SCIP_EXPR* AddExpression(const AbsExpression& absc); + + /// For each flat constraint type, + /// say ACCEPT_CONSTRAINT(Recommended) + /// and/or ACCEPT_CONSTRAINT(AcceptedButNotRecommended). + /// This can be user-configured via options 'acc:exp' etc. ACCEPT_CONSTRAINT(AbsConstraint, Recommended, CG_General) void AddConstraint(const AbsConstraint& absc); + + // SCIP 9 has AND/OR as constraints only: + // ACCEPT_EXPRESSION(AndExpression, AcceptedButNotRecommended) + // void AddExpression(const AndExpression& cc); + // ACCEPT_EXPRESSION(OrExpression, Recommended) + // void AddExpression(const OrExpression& dc); ACCEPT_CONSTRAINT(AndConstraint, AcceptedButNotRecommended, CG_General) void AddConstraint(const AndConstraint& cc); ACCEPT_CONSTRAINT(OrConstraint, Recommended, CG_General) void AddConstraint(const OrConstraint& dc); + + /// Linear indicator constraints can be used as + /// auxiliary constraints for logical conditions. + /// If not handled, the compared expressions need + /// deducible finite bounds for a big-M redefinition. ACCEPT_CONSTRAINT(IndicatorConstraintLinLE, AcceptedButNotRecommended, CG_General) void AddConstraint(const IndicatorConstraintLinLE& mc); ACCEPT_CONSTRAINT(IndicatorConstraintLinEQ, AcceptedButNotRecommended, CG_General) @@ -213,32 +239,37 @@ class ScipModelAPI : ACCEPT_CONSTRAINT(SOS2Constraint, AcceptedButNotRecommended, CG_SOS) void AddConstraint(const SOS2Constraint& cc); - /// SCIP nonlinear generals. - - /// Each expression can be accpeted as a proper expression, - /// or a flat constraint var == expr (with var arguments). + /// SCIP nonlinear general constraints and expressions. - /// For each expression, - /// say ACCEPT_EXPRESSION(Recommended) - /// and/or ACCEPT_EXPRESSION(AcceptedButNotRecommended). - /// This can be user-configured via options 'acc:exp' etc. ACCEPT_EXPRESSION(ExpExpression, Recommended) SCIP_EXPR* AddExpression(const ExpExpression& ); - - /// For each flat constraint type, - /// say ACCEPT_CONSTRAINT(Recommended) - /// and/or ACCEPT_CONSTRAINT(AcceptedButNotRecommended). - /// This can be user-configured via options 'acc:exp' etc. ACCEPT_CONSTRAINT(ExpConstraint, Recommended, CG_General) void AddConstraint(const ExpConstraint& cc); + + ACCEPT_EXPRESSION(LogExpression, Recommended) + SCIP_EXPR* AddExpression(const LogExpression& ); ACCEPT_CONSTRAINT(LogConstraint, Recommended, CG_General) void AddConstraint(const LogConstraint& cc); + + /// Use accessor: GetParameter(pe, 0) + /// - don't use PowExpression's methods. + ACCEPT_EXPRESSION(PowExpression, Recommended) + SCIP_EXPR* AddExpression(const PowExpression& ); ACCEPT_CONSTRAINT(PowConstraint, Recommended, CG_General) void AddConstraint(const PowConstraint& cc); + + ACCEPT_EXPRESSION(SinExpression, Recommended) + SCIP_EXPR* AddExpression(const SinExpression& ); ACCEPT_CONSTRAINT(SinConstraint, Recommended, CG_General) void AddConstraint(const SinConstraint& cc); - ACCEPT_CONSTRAINT(CosConstraint, AcceptedButNotRecommended, CG_General) //pretty slow + + ACCEPT_EXPRESSION(CosExpression, Recommended) + SCIP_EXPR* AddExpression(const CosExpression& ); + ACCEPT_CONSTRAINT(CosConstraint, Recommended, CG_General) //pretty slow in SCIP 8 void AddConstraint(const CosConstraint& cc); + + // TODO Div; PowVarExponent; + // CondLin... - not really, reader_nl.cpp only handles bool args }; } // namespace mp