From e20edc500e66abcfcc83106a141f91875b5f2845 Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Tue, 30 Apr 2024 21:17:24 +1000 Subject: [PATCH] Mark result and arg variables #237 Mark as proper variables when expression not accepted and for non-algebraic static constraints --- CMakeLists.txt | 4 +- include/mp/flat/constr_2_expr.h | 68 +++++++++++++++++++++++++++++ include/mp/flat/constr_algebraic.h | 2 +- include/mp/flat/constr_base.h | 22 ++++++++++ include/mp/flat/constr_eval.h | 2 +- include/mp/flat/constr_functional.h | 2 +- include/mp/flat/constr_general.h | 22 +++++++++- include/mp/flat/constr_hash.h | 2 - include/mp/flat/constr_keeper.h | 31 ++++++++++--- include/mp/flat/constr_prepro.h | 2 +- include/mp/flat/constr_prop_down.h | 2 +- include/mp/flat/constr_static.h | 2 +- include/mp/flat/constr_std.h | 2 +- include/mp/flat/converter.h | 17 +++++--- include/mp/flat/converter_model.h | 18 ++++++++ include/mp/flat/expr_affine.h | 3 ++ include/mp/flat/expr_algebraic.h | 5 +++ include/mp/flat/expr_quadratic.h | 6 +++ include/mp/utils-vec.h | 10 +++++ src/mp/flat/std_constr.cc | 16 +++++++ 20 files changed, 213 insertions(+), 25 deletions(-) create mode 100644 include/mp/flat/constr_2_expr.h create mode 100644 include/mp/utils-vec.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b9cccca49..c8fe9371a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -304,6 +304,7 @@ endfunction () add_mp_library(format OBJECT include/mp/format.h include/mp/posix.h src/format.cc src/posix.cc) + # gen-expr-info set(MP_EXPR_INFO_FILE ${MP_SOURCE_DIR}/src/expr-info.cc) set(MP_NL_OPCODES_FILE @@ -356,10 +357,11 @@ add_prefix(MP_HEADERS include/mp/ utils-clock.h utils-file.h util-json-write.h util-json-write.hpp utils-hash.h utils-hash-stream.h - utils-string.h utils-math.h) + utils-string.h utils-math.h utils-vec.h) add_prefix(MP_FLAT_HEADERS include/mp/flat/ backend_flat.h + constr_2_expr.h constr_base.h constr_eval.h constr_keeper.h constr_hash.h constr_std.h constr_static.h constr_functional.h constr_algebraic.h constr_general.h diff --git a/include/mp/flat/constr_2_expr.h b/include/mp/flat/constr_2_expr.h new file mode 100644 index 000000000..158ea3cd0 --- /dev/null +++ b/include/mp/flat/constr_2_expr.h @@ -0,0 +1,68 @@ +#ifndef CONSTR_2_EXPR_H +#define CONSTR_2_EXPR_H + +#include + +#include "mp/flat/constr_keeper.h" + +namespace mp { + +/// A mix-in base class +/// facilitating "inlining" of some functional constraints +/// into expression trees. +template +class Constraints2Expr { +public: + + /// Consider marking the result and argument variables as + /// "explicit variables" (not expressions) + template + void ConsiderMarkingResultAndArgVars( + const Con& con, int i, ExpressionAcceptanceLevel eal) { + if (con.HasResultVar()) { // A functional constraint + if (ExpressionAcceptanceLevel::NotAccepted==eal) { + MPD( MarkAsResultVar(con.GetResultVar()) ); + } + } // Any constraint + MPD( ConsiderMarkingArgumentsAsVars(con, i, eal) ); + } + + /// Generic request to consider marking arguments + template + void ConsiderMarkingArgumentsAsVars( + const Con& con, int i, ExpressionAcceptanceLevel eal) { + bool fMarkArgs = false; + if (con.HasResultVar()) // func cons: non-accepted ones by default + fMarkArgs = (ExpressionAcceptanceLevel::NotAccepted==eal); + else + fMarkArgs = true; // static cons: all non-algebraic by default + if (fMarkArgs) + MPD( DoMarkArgsAsVars(con, i) ); + } + + +protected: + /// Algebraic cons: no marking (when NLConstraint accepted?) + template + void DoMarkArgsAsVars( // needs to appear before the most generic template + const AlgebraicConstraint& , int ) { } + + /// @todo not mark Complementarity (NL accepts expressions) + + /// Generic arguments marking call + template + void DoMarkArgsAsVars(const Con& con, int ) { + VisitArguments(con, MarkVar_); + } + + +private: + /// (Argument) variable visitor + std::function MarkVar_ = [this](int v){ + MPD( MarkAsResultVar(v) ); // no recursion + }; +}; + +} // namespace mp + +#endif // CONSTR_2_EXPR_H diff --git a/include/mp/flat/constr_algebraic.h b/include/mp/flat/constr_algebraic.h index a0feed1aa..fcde3ab70 100644 --- a/include/mp/flat/constr_algebraic.h +++ b/include/mp/flat/constr_algebraic.h @@ -1,7 +1,7 @@ #ifndef CONSTRAINTS_ALGEBRAIC_H #define CONSTRAINTS_ALGEBRAIC_H -/** +/* * Static algebraic constraints */ diff --git a/include/mp/flat/constr_base.h b/include/mp/flat/constr_base.h index 30ec0af44..ce060f1c7 100644 --- a/include/mp/flat/constr_base.h +++ b/include/mp/flat/constr_base.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -277,6 +278,27 @@ inline void WriteJSON(JW jw, } +/// Argument container visitor: VarArrayN +template // Needs to appear before the generic template version, CLang 15 +inline void VisitArguments(const std::array& cnt, std::function argv) { + for (auto v: cnt) + argv(v); +} + +/// Generic constraint/objective argument visitor +template +inline void VisitArguments(const Item& item, std::function argv) { + VisitArguments(item.GetArguments(), argv); // redirect to the arguments' visitor +} + +/// Argument container visitor: VarArray +template <> +inline void VisitArguments(const std::vector& cnt, std::function argv) { + for (auto v: cnt) + argv(v); +} + + /// Dummy template: compute result of functional constraint. /// Should be implemented for proper functional specializations, /// but has no sense for conic constraints, for example. diff --git a/include/mp/flat/constr_eval.h b/include/mp/flat/constr_eval.h index 4524b0d3a..012150844 100644 --- a/include/mp/flat/constr_eval.h +++ b/include/mp/flat/constr_eval.h @@ -1,7 +1,7 @@ #ifndef CONSTR_EVAL_H #define CONSTR_EVAL_H -/** +/* * Evaluations and violations * of (mainly functional) constraints. * diff --git a/include/mp/flat/constr_functional.h b/include/mp/flat/constr_functional.h index 35193b705..5407a8578 100644 --- a/include/mp/flat/constr_functional.h +++ b/include/mp/flat/constr_functional.h @@ -1,7 +1,7 @@ #ifndef CONSTRAINTS_FUNCTIONAL_H #define CONSTRAINTS_FUNCTIONAL_H -/** +/* * Functional flat constraints */ diff --git a/include/mp/flat/constr_general.h b/include/mp/flat/constr_general.h index 7f1c21a5b..6b86befc9 100644 --- a/include/mp/flat/constr_general.h +++ b/include/mp/flat/constr_general.h @@ -1,7 +1,7 @@ #ifndef CONSTRAINTS_GENERAL_H #define CONSTRAINTS_GENERAL_H -/** +/* * Static general constraints */ @@ -99,6 +99,15 @@ inline void WriteJSON(JSONW jw, } +/// Specialize +template +inline void VisitArguments(const IndicatorConstraint& ic, + std::function argv) { + argv(ic.get_binary_var()); + VisitArguments(ic.get_constraint(), argv); +} + + /// Unary encoding. /// Currently a dummy constraint just to build /// the reformulation graph. @@ -147,6 +156,8 @@ class SOS_1or2_Constraint: public BasicConstraint { int size() const { return (int)v_.size(); } /// Returns vector of variables, sorted by weights const std::vector& get_vars() const { return v_; } + /// vars via GetArguments() + const std::vector& GetArguments() const { return get_vars(); } /// Returns weights, sorted const std::vector& get_weights() const { return w_; } /// SOS2 extra info @@ -340,6 +351,15 @@ inline void WriteJSON(JSONW jw, } +/// Specialize +template +inline void VisitArguments(const ComplementarityConstraint& cc, + std::function argv) { + argv(cc.GetVariable()); + VisitArguments(cc.GetExpression(), argv); +} + + /// Quadratic cone DEF_STATIC_CONSTR_WITH_PRM( QuadraticCone, VarArray, DblParamArray, "Quadratic cone p1*x1 >= sqrt((p2*x2)^2 + ...))," diff --git a/include/mp/flat/constr_hash.h b/include/mp/flat/constr_hash.h index f32fff9eb..790eeea12 100644 --- a/include/mp/flat/constr_hash.h +++ b/include/mp/flat/constr_hash.h @@ -1,8 +1,6 @@ #ifndef CONSTRAINT_HASH_H #define CONSTRAINT_HASH_H -/// Specialize std::hash<> for some standard constraints - #include #include #include diff --git a/include/mp/flat/constr_keeper.h b/include/mp/flat/constr_keeper.h index 36b4596c4..cb33e85e4 100644 --- a/include/mp/flat/constr_keeper.h +++ b/include/mp/flat/constr_keeper.h @@ -386,9 +386,9 @@ class BasicConstraintKeeper { /// Convert to use expressions virtual void ConvertWithExpressions(BasicFlatConverter& cvt) = 0; - /// Query (user-chosen, if sensible) constraint acceptance level. + /// Query (user-chosen) acceptance level. /// This is "combined" for constraint or expression - virtual ConstraintAcceptanceLevel GetChosenAcceptanceLevel() const { + ConstraintAcceptanceLevel GetChosenAcceptanceLevel() const { if (acceptance_level_<0) { // not initialized std::array alv = {0, 1, 2, 1, 2}; acceptance_level_ = alv.at(acc_level_item_); @@ -396,6 +396,15 @@ class BasicConstraintKeeper { return ConstraintAcceptanceLevel(acceptance_level_); } + /// Query (user-chosen) expression acceptance level. + ExpressionAcceptanceLevel GetChosenAcceptanceLevelEXPR() const { + if (acc_level_expr_<0) { // not initialized + std::array alv = {0, 0, 0, 1, 2}; + acc_level_expr_ = alv.at(acc_level_item_); + } + return ExpressionAcceptanceLevel(acc_level_expr_); + } + /// Converter's ability to convert the constraint type virtual bool IfConverterConverts( BasicFlatConverter& cvt ) const = 0; @@ -465,14 +474,15 @@ class BasicConstraintKeeper { /// See what options are available for this constraint: /// whether it is accepted natively by ModelAPI, /// as flat constraint or expression. - /// If both, add constraint acceptance option. + /// Add acceptance option(s) "acc:...". + /// Populate constraint list for -c output. /// @note This should be called before using the class. void ConsiderAcceptanceOptions( BasicFlatConverter& cvt, const BasicFlatModelAPI& ma, Env& env) { DoAddAcceptanceOptions(cvt, ma, env); - DoPopulateConstraintList(cvt, ma, env); + DoPopulateConstraintList(cvt, ma, env); // for -c option } /// Mark as bridged. Use index only. @@ -507,6 +517,7 @@ class BasicConstraintKeeper { BasicFlatConverter& cvt, const BasicFlatModelAPI& ma, Env& env); + /// For -c void DoPopulateConstraintList( BasicFlatConverter& cvt, const BasicFlatModelAPI& ma, @@ -891,9 +902,15 @@ class ConstraintKeeper final } void DoMarkForResultVars() { - const auto acceptanceLevel = - GetChosenAcceptanceLevel(); - + const auto eal // expr only + = GetChosenAcceptanceLevelEXPR(); + for (int i=0; i< (int)cons_.size(); ++i) { + const auto& cnt = cons_[i]; + if (!cnt.IsBridged()) { // Delegate actual logic to Converter + const auto& con = cnt.GetCon(); + GetConverter().ConsiderMarkingResultAndArgVars(con, i, eal); + } + } } void DoCvtWithExprs() { } diff --git a/include/mp/flat/constr_prepro.h b/include/mp/flat/constr_prepro.h index 8f865595e..ae859731d 100644 --- a/include/mp/flat/constr_prepro.h +++ b/include/mp/flat/constr_prepro.h @@ -1,7 +1,7 @@ #ifndef CONSTR_PREPRO_H #define CONSTR_PREPRO_H -/** +/* * Preprocess flat constraints before adding. * * Possible tasks: diff --git a/include/mp/flat/constr_prop_down.h b/include/mp/flat/constr_prop_down.h index 6263e88a3..79eafc574 100644 --- a/include/mp/flat/constr_prop_down.h +++ b/include/mp/flat/constr_prop_down.h @@ -1,7 +1,7 @@ #ifndef CONSTR_PROP_DOWN_H #define CONSTR_PROP_DOWN_H -/** +/* * Propagate flat constraints from result (result bounds & context) * "down", i.e., to the arguments */ diff --git a/include/mp/flat/constr_static.h b/include/mp/flat/constr_static.h index 4e4780ced..2dfa92a7e 100644 --- a/include/mp/flat/constr_static.h +++ b/include/mp/flat/constr_static.h @@ -1,7 +1,7 @@ #ifndef CONSTRAINTS_STATIC_H #define CONSTRAINTS_STATIC_H -/** +/* * Static (non-functional) flat constraints */ diff --git a/include/mp/flat/constr_std.h b/include/mp/flat/constr_std.h index 50ab5b54c..bb4db841c 100644 --- a/include/mp/flat/constr_std.h +++ b/include/mp/flat/constr_std.h @@ -1,7 +1,7 @@ #ifndef STD_CONSTR_H #define STD_CONSTR_H -/** +/* * Convenience header to include all standard * flat constraints */ diff --git a/include/mp/flat/converter.h b/include/mp/flat/converter.h index adfe4ca9e..727584140 100644 --- a/include/mp/flat/converter.h +++ b/include/mp/flat/converter.h @@ -17,6 +17,7 @@ #include "mp/flat/expr_bounds.h" #include "mp/flat/constr_prepro.h" #include "mp/flat/constr_prop_down.h" +#include "mp/flat/constr_2_expr.h" #include "mp/flat/sol_check.h" #include "mp/valcvt.h" #include "mp/flat/redef/std/range_con.h" @@ -35,13 +36,14 @@ namespace mp { template > class FlatConverter : - public BasicFlatConverter, - public FlatModel, - public BoundComputations, - public ConstraintPreprocessors, - public ConstraintPropagatorsDown, - public SolutionChecker, - public EnvKeeper + public BasicFlatConverter, + public FlatModel, + public BoundComputations, + public ConstraintPreprocessors, + public ConstraintPropagatorsDown, + public Constraints2Expr, + public SolutionChecker, + public EnvKeeper { public: /// Class name @@ -854,6 +856,7 @@ class FlatConverter : == ci.GetCK(); } + /////////////////////// AUTO LINKING //////////////////////////// /// Auto link node range \a nr. diff --git a/include/mp/flat/converter_model.h b/include/mp/flat/converter_model.h index ada892ba0..3b9db88bb 100644 --- a/include/mp/flat/converter_model.h +++ b/include/mp/flat/converter_model.h @@ -228,6 +228,20 @@ class FlatModel var_ub_[v] = u; } + /// Mark as an explicit result variable + void MarkAsResultVar(int v) { + if (var_result_.size()<=v) + var_result_.resize(num_vars()); + var_result_[v] = true; + } + + /// Is the variable an explicit result var? + bool IsResultVar(int v) const { + if (var_result_.size()<=v) + var_result_.resize(num_vars()); + return var_result_[v]; + } + ///////////////////////////// OBJECTIVES //////////////////////////// public: /// List of objectives @@ -379,6 +393,10 @@ class FlatModel VarBndVec var_lb_, var_ub_; /// Variables' types VarTypeVec var_type_; + /// Whether the variable, being the result variable of a functional constraint, + /// needs to stay a variable (vs being eliminated because the constraint + /// is becoming an expression) + std::vector var_result_; /// Variables' names mutable VarNameVec var_names_; std::vector var_names_storage_; diff --git a/include/mp/flat/expr_affine.h b/include/mp/flat/expr_affine.h index 262d7db2f..b2750196a 100644 --- a/include/mp/flat/expr_affine.h +++ b/include/mp/flat/expr_affine.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "mp/arrayref.h" @@ -159,6 +160,8 @@ class LinTerms { template <> void WriteJSON(JSONW jw, const LinTerms& qt); +/// Specialize +void VisitArguments(const LinTerms& lt, std::function argv); /// Typedef AffineExpr using AffineExpr = AlgebraicExpression; diff --git a/include/mp/flat/expr_algebraic.h b/include/mp/flat/expr_algebraic.h index 30c494e16..dc28941a7 100644 --- a/include/mp/flat/expr_algebraic.h +++ b/include/mp/flat/expr_algebraic.h @@ -65,6 +65,11 @@ class AlgebraicExpression : public Body { /// Get the body Body& GetBody() { return (Body&)(*this); } + /// Get the body, const + const Body& GetArguments() const { return GetBody(); } + /// Get the body + Body& GetArguments() { return GetBody(); } + /// Get the body (variable terms) /// of a corresponding algebraic constraint const Body& GetAlgConBody() const { return GetBody(); } diff --git a/include/mp/flat/expr_quadratic.h b/include/mp/flat/expr_quadratic.h index 2e9be94a4..0f4d5c3b6 100644 --- a/include/mp/flat/expr_quadratic.h +++ b/include/mp/flat/expr_quadratic.h @@ -236,6 +236,12 @@ class QuadAndLinTerms : template <> void WriteJSON(JSONW jw, const QuadAndLinTerms& qt); +/// Specialize +void VisitArguments(const QuadTerms& lt, std::function argv); + +/// Specialize +void VisitArguments(const QuadAndLinTerms& lt, std::function argv); + /// Typedef QuadraticExpr using QuadraticExpr = AlgebraicExpression; diff --git a/include/mp/utils-vec.h b/include/mp/utils-vec.h new file mode 100644 index 000000000..05e7f6e96 --- /dev/null +++ b/include/mp/utils-vec.h @@ -0,0 +1,10 @@ +#ifndef UTILSVEC_H +#define UTILSVEC_H + + +namespace mp { + +} // namespace mp + + +#endif // UTILSVEC_H diff --git a/src/mp/flat/std_constr.cc b/src/mp/flat/std_constr.cc index d88d6d04e..eb5cb9048 100644 --- a/src/mp/flat/std_constr.cc +++ b/src/mp/flat/std_constr.cc @@ -308,6 +308,22 @@ void WriteJSON(JSONW jw, const QuadAndLinTerms& qlt) { WriteJSON(jw["lin_terms"], qlt.GetLinTerms()); } +void VisitArguments(const LinTerms& lt, std::function argv) { + for (auto v: lt.vars()) + argv(v); +} + +void VisitArguments(const QuadTerms& lt, std::function argv) { + for (auto v: lt.vars1()) + argv(v); + for (auto v: lt.vars2()) + argv(v); +} + +void VisitArguments(const QuadAndLinTerms& qlt, std::function argv) { + VisitArguments(qlt.GetLinTerms(), argv); + VisitArguments(qlt.GetQPTerms(), argv); +} /// FlatModelInfo factory std::unique_ptr CreateFlatModelInfo() {