From 45b26ba819cdd1313cf94ac4d02ebd9e4cca010c Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Fri, 16 Aug 2024 14:27:14 +1000 Subject: [PATCH] Expression interface: eliminate implicit vars #237 --- include/mp/flat/constr_2_expr.h | 8 ++++++++ include/mp/flat/constr_keeper.h | 2 +- include/mp/flat/constr_prop_down.h | 4 ++-- include/mp/flat/convert_functional.h | 2 +- include/mp/flat/converter.h | 15 +++++++++----- include/mp/flat/converter_model.h | 29 +++++++++++++++++++++++++--- include/mp/utils-vec.h | 1 + solvers/scipmp/scipmpmodelapi.h | 2 +- 8 files changed, 50 insertions(+), 13 deletions(-) diff --git a/include/mp/flat/constr_2_expr.h b/include/mp/flat/constr_2_expr.h index 393ddeb70..b6388f99b 100644 --- a/include/mp/flat/constr_2_expr.h +++ b/include/mp/flat/constr_2_expr.h @@ -33,6 +33,7 @@ class Constraints2Expr { MPD( GetModel() ).ConvertAllWithExpressions(*(Impl*)this); stage_cvt2expr_ = 2; // func cons -> explicifiers MPD( GetModel() ).ConvertAllWithExpressions(*(Impl*)this); + MPD( EliminateExprResultVars() ); // In the very end } /// Mark which functional constraints to be used as expressions, @@ -216,6 +217,13 @@ class Constraints2Expr { } } + /// Mark expr result vars for elimination + void EliminateExprResultVars() { + for (auto i = MPCD(num_vars()); i--; ) + if (!MPCD( IsProperVar(i) )) + MPD( MarkVarAsEliminated(i) ); + } + protected: /// Algebraic cons: no marking (when NLConstraint accepted?) diff --git a/include/mp/flat/constr_keeper.h b/include/mp/flat/constr_keeper.h index d1fd9293a..203363902 100644 --- a/include/mp/flat/constr_keeper.h +++ b/include/mp/flat/constr_keeper.h @@ -253,7 +253,7 @@ class ConstraintKeeper final bool IsUnused() const { return is_unused_; } /// Mark as unused void MarkAsUnused() { - MarkAsBridged(); + MarkAsBridged(); // also inactive is_unused_=true; } diff --git a/include/mp/flat/constr_prop_down.h b/include/mp/flat/constr_prop_down.h index 1a98743a3..5a0c72cad 100644 --- a/include/mp/flat/constr_prop_down.h +++ b/include/mp/flat/constr_prop_down.h @@ -109,9 +109,9 @@ class ConstraintPropagatorsDown { void PropagateResult(AndConstraint& con, double lb, double ub, Context ctx) { MPD( NarrowVarBounds(con.GetResultVar(), lb, ub) ); con.AddContext(ctx); - MPD( PropagateResult2Vars(con.GetArguments(), lb, 1.0, +ctx) ); + MPD( PropagateResult2Vars(con.GetArguments(), lb, 1.0, +ctx) ); // in any ctx?? if (lb>0.5) // Remove, arguments are fixed - MPD( DecrementVarUsage(con.GetResultVar()) ); + MPD( DecrementVarUsage(con.GetResultVar()) ); // Or, remove completely? } void PropagateResult(OrConstraint& con, double lb, double ub, Context ctx) { diff --git a/include/mp/flat/convert_functional.h b/include/mp/flat/convert_functional.h index 33ec54664..f1fea579f 100644 --- a/include/mp/flat/convert_functional.h +++ b/include/mp/flat/convert_functional.h @@ -96,10 +96,10 @@ class BasicFCC { auto r = int( GetConverter().AddVar(lb(), ub(), type()) ); SetResultVar( r ); GetConstraint().SetResultVar( r ); - GetConverter().IncrementVarUsage(r); } void AddConstraint() { GetConverter().AddConstraint( std::move(GetConstraint()) ); + GetConverter().IncrementVarUsage(GetResultVar()); // ater adding the con } }; diff --git a/include/mp/flat/converter.h b/include/mp/flat/converter.h index 16eb5dc8f..82fccea97 100644 --- a/include/mp/flat/converter.h +++ b/include/mp/flat/converter.h @@ -207,6 +207,7 @@ class FlatConverter : /// Use "+1" a variable void IncrementVarUsage(int v) { ++VarUsageRef(v); + assert(!IsUnused(GetInitExpression(v))); } /// Unuse result variable. @@ -221,14 +222,13 @@ class FlatConverter : } } - /// Fix unused defined vars. + /// Mark unused defined vars for elimination. /// Normally should delete them. - void FixUnusedDefinedVars() { + void EliminateUnusedDefinedVars() { for (auto i=num_vars(); i--; ) { if (HasInitExpression(i) && ! VarUsageRef(i)) { - set_var_lb(i, 0.0); // fix to 0 - set_var_ub(i, 0.0); + MPD( MarkVarAsEliminated(i) ); } } } @@ -535,6 +535,11 @@ class FlatConverter : ci.GetCK()->MarkAsUnused(ci.GetIndex()); } + /// Is constraint unused? + bool IsUnused(const ConInfo& ci) const { + return ci.GetCK()->IsUnused(ci.GetIndex()); + } + protected: USE_BASE_MAP_FINDERS( BaseConverter ) @@ -575,7 +580,7 @@ class FlatConverter : MPD( ConvertModel() ); if (relax()) GetModel().RelaxIntegrality(); - FixUnusedDefinedVars(); // Until we have proper var deletion + EliminateUnusedDefinedVars(); // Until we have proper var deletion CheckLinearCons(); PresolveNames(); GetModel().PushModelTo(GetModelAPI()); diff --git a/include/mp/flat/converter_model.h b/include/mp/flat/converter_model.h index 946915de9..fc29d5f91 100644 --- a/include/mp/flat/converter_model.h +++ b/include/mp/flat/converter_model.h @@ -247,7 +247,7 @@ class FlatModel AutoExpand(var_result_, v) = false; } - /// Variable has marking? + /// Variable has expr marking? bool VarHasMarking(int v) const { assert(v>=0); return v < (int)var_result_.size(); @@ -258,8 +258,12 @@ class FlatModel /// (Otheriwse, it's marked as implicit /// - the init expr will be an expression) bool IsProperVar(int v) const { - assert(VarHasMarking(v)); - return var_result_[v]; + return !VarHasMarking(v) || var_result_[v]; + } + + /// Mark var as eliminated. + void MarkVarAsEliminated(int v) { + AutoExpand(var_elim_, v) = true; } @@ -373,6 +377,20 @@ class FlatModel template void PushVariablesTo(Backend& backend) const { + /// Fix 'eliminated' variables - no proper deletion + var_lb_subm_ = var_lb_; + var_ub_subm_ = var_ub_; + for (auto i=std::min(var_elim_.size(), var_lb_subm_.size()); i--; ) { + if (var_elim_[i]) { + if (var_lb_subm_[i] > -1e20) + var_ub_subm_[i] = var_lb_subm_[i]; + else if (var_ub_subm_[i] < 1e20) + var_lb_subm_[i] = var_ub_subm_[i]; + else + var_lb_subm_[i] = var_ub_subm_[i] = 0.0; + } + } + /// Push variables if (var_names_storage_.size() > 0) { // Convert names to c-str if needed for (const std::string& s : var_names_storage_) @@ -424,6 +442,8 @@ class FlatModel private: /// Variables' bounds VarBndVec var_lb_, var_ub_; + /// Variables' submitted bounds - what goes to the solver + mutable VarBndVec var_lb_subm_, var_ub_subm_; /// Variables' types VarTypeVec var_type_; /// Whether the variable, being the result variable of a functional constraint, @@ -431,6 +451,9 @@ class FlatModel /// is becoming an expression.) /// Normal variables are marked too. std::vector var_result_; + /// Eliminated variables. + /// Currenlty they are just fixed. + std::vector var_elim_; /// Variables' names mutable VarNameVec var_names_; std::vector var_names_storage_; diff --git a/include/mp/utils-vec.h b/include/mp/utils-vec.h index e7b811bfb..ef38e3b0f 100644 --- a/include/mp/utils-vec.h +++ b/include/mp/utils-vec.h @@ -21,6 +21,7 @@ using SmallVecDefSz = gch::small_vector; /// Grow vector capacity by a factor if needed. /// @return reference to the element at \a i. +/// @note better preallocate, or call in the reverse order of indexes. template auto AutoExpand(Vec& vec, typename Vec::size_type i) { if (vec.size()<=i) { diff --git a/solvers/scipmp/scipmpmodelapi.h b/solvers/scipmp/scipmpmodelapi.h index 83d84cfae..ac5b30e0b 100644 --- a/solvers/scipmp/scipmpmodelapi.h +++ b/solvers/scipmp/scipmpmodelapi.h @@ -50,7 +50,7 @@ class ScipModelAPI : /// 'AcceptedButNotRecommended' would outline each expression /// with an auxiliary variable. /// See also per-expression type switches. - ACCEPT_EXPRESSION_INTERFACE(NotAccepted); + ACCEPT_EXPRESSION_INTERFACE(AcceptedButNotRecommended); /// For each suppoted constraint type, add the ACCEPT_CONSTRAINT macro /// and the relative AddConstraint function.