Skip to content

Commit

Permalink
Consider expression result bounds #237
Browse files Browse the repository at this point in the history
Still explicify an expression via an auxiliary variable when the latter has stronger bounds

Refactor static/func constraint hierarchy, set some forgotten contexts
  • Loading branch information
glebbelov committed Aug 28, 2024
1 parent c2c2954 commit ea950a1
Show file tree
Hide file tree
Showing 10 changed files with 223 additions and 48 deletions.
11 changes: 10 additions & 1 deletion include/mp/flat/constr_2_expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ class Constraints2Expr {
assert( // Check: the result var has \a con as the init expr
MPD( template GetInitExpressionOfType<Con>(con.GetResultVar()) )
== &con);
MPD( MarkAsExpression(con.GetResultVar()) ); // can be changed later
if ( !MPD( IfVarBoundsStrongerThanInitExpr(con.GetResultVar()) ) )
MPD( MarkAsExpression(con.GetResultVar()) ); // can be changed later
}
}

Expand Down Expand Up @@ -226,6 +227,14 @@ class Constraints2Expr {
return false;
}

/// Any other static con.
template <class A, class P, class I>
bool ConvertWithExpressions(
const CustomStaticConstraint<A, P, I>& , int ,
ConstraintAcceptanceLevel , ExpressionAcceptanceLevel ) {
return false;
}

/// Convert objectives
void ConvertObjectivesWithExpressions() {
auto& objs = MPD( get_objectives() );
Expand Down
162 changes: 131 additions & 31 deletions include/mp/flat/constr_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,10 @@

namespace mp {

/// Custom constraints to derive from, so that overloaded default settings work
/// Custom constraints to derive from, so that overloaded default settings work.
/// @note Defaults to static constraint.
class BasicConstraint {
public:
/// Constraint type name for messages
static constexpr const char* GetTypeName()
{ return "BasicConstraint"; }
/// Constraint name
const char* GetName() const { return name_.c_str(); }
/// Constraint name
Expand Down Expand Up @@ -73,14 +71,14 @@ class ExprWrapper {
};


/// A special constraint 'var=...', which defines a result variable
/// A special constraint 'var=...', which defines a result variable.
/// @note Each functional constraint should derive from here.
/// @note Each functional constraint should have context,
/// either set explicitly, or via PropagateResult().
class FunctionalConstraint : public BasicConstraint {
int result_var_=-1; // defined var is optional
mutable Context ctx; // always store context
public:
/// Constraint type name for messages
static constexpr const char* GetTypeName()
{ return "FunctionalConstraint"; }
/// Constructor
/// @param v: result variable
FunctionalConstraint(int v=-1) : result_var_(v) {}
Expand All @@ -102,7 +100,7 @@ class FunctionalConstraint : public BasicConstraint {
/// Set it
void SetContext(Context c) const { ctx=c; }
/// Add context
void AddContext(Context c) { ctx.Add(c); }
void AddContext(Context c) const { ctx.Add(c); }
};


Expand Down Expand Up @@ -137,53 +135,128 @@ using DblParamArray3 = ParamArrayN<double, 3>;
using DblParamArray = std::vector<double>;


/// A functional constraint with given arguments
/// and further info as parameters
/// Custom constraint data: given arguments
/// and further info as parameters / ID
/// @param Args: arguments type
/// @param Params: parameters type
/// @param NumOrLogic: base class defining a numeric or logic constraint
/// @param Id: a struct with GetTypeName()
template <class Args, class Params, class NumOrLogic, class Id>
class CustomFunctionalConstraint :
public FunctionalConstraint, public NumOrLogic, public Id {
template <class Args, class Params, class Id>
class CustomConstraintData
: public Id {
Args args_;
Params params_;

public:
/// Constraint type name for messages
static const char* GetTypeName() { return Id::GetTypeName(); }
/// Default constructor
CustomFunctionalConstraint() = default;
CustomConstraintData() = default;
/// Arguments typedef
using Arguments = Args;
/// Parameters typedef
using Parameters = Params;
/// Construct from arguments only
CustomConstraintData(Arguments args) noexcept :
args_(std::move(args)) { }
/// Construct from arguments and parameters
CustomConstraintData(Arguments args, Parameters prm) noexcept :
args_(std::move(args)), params_(std::move(prm)) { }

/////////////////////////////////////////////////////////////////////

/// Get const Arguments&
const Arguments& GetArguments() const { return args_; }
/// Get Arguments&
Arguments& GetArguments() { return args_; }
/// Get const Parameters&
const Parameters& GetParameters() const { return params_; }
/// Get Parameters&
Parameters& GetParameters() { return params_; }
};


/// A static constraint with given arguments
/// and further info as parameters
/// @param Args: arguments type
/// @param Params: parameters type
/// @param Id: a struct with GetTypeName()
template <class Args, class Params, class Id>
class CustomStaticConstraint
: public BasicConstraint,
public CustomConstraintData<Args, Params, Id> {
using BaseType = CustomConstraintData<Args, Params, Id>;
public:
/// Default constructor
CustomStaticConstraint() = default;

/// Is logical? All logical flat cons are functional currently.
static bool IsLogical() { return false; }

/// Arguments typedef
using typename BaseType::Arguments;
/// Parameters typedef
using typename BaseType::Parameters;
/// Construct from arguments only
CustomStaticConstraint(Arguments args) noexcept :
BaseType(std::move(args)) { }
/// Construct from arguments and parameters
CustomStaticConstraint(Arguments args, Parameters prm) noexcept :
BaseType(std::move(args), std::move(prm)) { }

/////////////////////////////////////////////////////////////////////

/// Get (const) Arguments&
using BaseType::GetArguments;
/// Get (const) Parameters&
using BaseType::GetParameters;

/// Compute violation
template <class VarVec>
Violation ComputeViolation(const VarVec& x) const;
};


/// A functional constraint with given arguments
/// and further info as parameters
/// @param Args: arguments type
/// @param Params: parameters type
/// @param NumOrLogic: base class defining a numeric or logic constraint
/// @param Id: a struct with GetTypeName()
template <class Args, class Params, class NumOrLogic, class Id>
class CustomFunctionalConstraint
: public FunctionalConstraint,
public NumOrLogic,
public CustomConstraintData<Args, Params, Id> {
using BaseType = CustomConstraintData<Args, Params, Id>;
public:
/// Default constructor
CustomFunctionalConstraint() = default;
/// Arguments typedef
using typename BaseType::Arguments;
/// Parameters typedef
using typename BaseType::Parameters;
/// Construct from arguments only
CustomFunctionalConstraint(Arguments args) noexcept :
args_(std::move(args)) { }
BaseType(std::move(args)) { }
/// Construct from arguments and parameters
/// Might need to use explicit types when using initializer lists,
/// in order to distinguish from the next 2 constructors
CustomFunctionalConstraint(Arguments args, Parameters prm) noexcept :
args_(std::move(args)), params_(std::move(prm)) { }
BaseType(std::move(args), std::move(prm)) { }
/// Construct from resvar and arguments.
/// If Arguments = VarArray1, distinguish from the previous
/// constructor by putting first int in just one {}
CustomFunctionalConstraint(int varr, Arguments args) noexcept :
FunctionalConstraint(varr), args_(std::move(args)) { }
FunctionalConstraint(varr), BaseType(std::move(args)) { }

/////////////////////////////////////////////////////////////////////

/// Reuse GetResultVar()
using FunctionalConstraint::GetResultVar;
/// Get const Arguments&
const Arguments& GetArguments() const { return args_; }
/// Get Arguments&
Arguments& GetArguments() { return args_; }
/// Get const Parameters&
const Parameters& GetParameters() const { return params_; }
/// Get Parameters&
Parameters& GetParameters() { return params_; }
/// Get (const) Arguments&
using BaseType::GetArguments;
/// Get (const) Parameters&
using BaseType::GetParameters;

/// Compute violation
template <class VarVec>
Expand Down Expand Up @@ -235,6 +308,16 @@ inline void WriteModelItem(
const std::vector<std::string>& vnam) {
if (cfc.HasResultVar()) // really functional
wrt << vnam.at(cfc.GetResultVar()) << " == ";
WriteModelItem(
wrt, (const CustomConstraintData<A, P, I>&)cfc, vnam);
}

/// Specialize WriteModelItem() for CustomCon<> and CustomConData<>
template <class Writer, class A, class P, class I>
inline void WriteModelItem(
Writer& wrt,
const CustomConstraintData<A,P,I>& cfc,
const std::vector<std::string>& vnam) {
wrt << cfc.GetTypeName();
wrt << '(';
WriteModelItem(wrt, cfc.GetArguments(), vnam);
Expand All @@ -243,6 +326,7 @@ inline void WriteModelItem(
wrt << ')';
}


/// Very general template to write any flat constraint
/// with name.
template <class Writer, class Con>
Expand All @@ -252,6 +336,14 @@ inline void WriteFlatCon(Writer& wrt, const Con& c,
WriteModelItem(wrt, c, vnam);
}

/// Write a CustomStaticConstraint<>
template <class JW, class A, class P,class I>
inline void WriteJSON(JW jw,
const CustomStaticConstraint<A,P,I>& cfc) {
WriteJSON(jw["args"], cfc.GetArguments());
WriteJSON(jw["params"], cfc.GetParameters());
}

/// Write a CustomFunctionalConstraint<>
template <class JW, class A, class P, class N, class I>
inline void WriteJSON(JW jw,
Expand Down Expand Up @@ -462,6 +554,16 @@ 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.
#define DEF_CUSTOM_STATIC_CONSTR_WITH_PRM(Name, Args, Params, Descr) \
struct Name ## Id { \
static constexpr const char* description() { return Descr; } \
static constexpr const char* GetTypeName() { return #Name; } \
}; \
using Name ## Constraint = CustomStaticConstraint<Args, Params, Name ## Id>;

////////////////////////////////////////////////////////////////////////
/// 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.
Expand Down Expand Up @@ -497,12 +599,10 @@ inline void WriteJSON(JW jw,

////////////////////////////////////////////////////////////////////////
/// STATIC CONSTRAINTS
/// Workaround: defining as functional constraint (result unused)
/// Could be solved by a mix-in parent
#define DEF_STATIC_CONSTR(Name, Args, Descr) \
DEF_LOGICAL_FUNC_CONSTR(Name, Args, Descr)
DEF_CUSTOM_STATIC_CONSTR_WITH_PRM(Name, Args, ParamArray0, Descr)
#define DEF_STATIC_CONSTR_WITH_PRM(Name, Args, Params, Descr) \
DEF_LOGICAL_FUNC_CONSTR_WITH_PRM(Name, Args, Params, Descr)
DEF_CUSTOM_STATIC_CONSTR_WITH_PRM(Name, Args, Params, Descr)


} // namespace mp
Expand Down
41 changes: 38 additions & 3 deletions include/mp/flat/constr_eval.h
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,31 @@ Violation ComputeViolation( // where ax, by >= 0
by * std::exp(cz / by)};
}

/// Compute violation of the ExponentialCone constraint.
template <class VarVec> // ax >= by exp(cz / (by))
Violation ComputeViolation( // where ax, by >= 0
const PowerConeConstraint& con, const VarVec& x) {
// TODO
return {1000.0, 1.0};
}

/// Compute violation of the ExponentialCone constraint.
template <class VarVec> // ax >= by exp(cz / (by))
Violation ComputeViolation( // where ax, by >= 0
const GeometricConeConstraint& con, const VarVec& x) {
// TODO
return {1000.0, 1.0};
}

/// Compute violation of the ExponentialCone constraint.
template <class VarVec> // ax >= by exp(cz / (by))
Violation ComputeViolation( // where ax, by >= 0
const UnaryEncodingConstraint& con, const VarVec& x) {
// TODO
return {1000.0, 1.0};
}


/// Compute result of the PL constraint.
template <class VarVec>
double ComputeValue(const PLConstraint& con, const VarVec& x) {
Expand All @@ -354,14 +379,24 @@ double ComputeValue(const PLConstraint& con, const VarVec& x) {
/// Should be here,
/// after ComputeViolation() is specialized
/// for some constraints.
template <class Args, class Params,
class NumOrLogic, class Id>
template <class Args, class Params, class Id>
template <class VarVec>
Violation
CustomFunctionalConstraint<Args, Params, NumOrLogic, Id>
CustomStaticConstraint<Args, Params, Id>
::ComputeViolation(const VarVec& x) const
{ return mp::ComputeViolation(*this, x); }

/// Should be here,
/// after ComputeViolation() is specialized
/// for some constraints.
template <class Args, class Params,
class NumOrLogic, class Id>
template <class VarVec>
Violation
CustomFunctionalConstraint<Args, Params, NumOrLogic, Id>
::ComputeViolation(const VarVec& x) const
{ return mp::ComputeViolation(*this, x); }


} // namespace mp

Expand Down
10 changes: 10 additions & 0 deletions include/mp/flat/constr_keeper.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ class ConstraintKeeper final
void SetContext(int i, Context ctx) override
{ assert(check_index(i)); cons_[i].GetCon().SetContext(ctx); }

/// Propagate expression result of constraint \a i bottom-up
void PreprocessConstraint(int i, PreprocessInfoStd& preinfo) override {
if constexpr (std::is_base_of_v<FunctionalConstraint, Constraint>) {
PreprocessInfo<Constraint> prepro;
GetConverter().PreprocessConstraint(cons_[i].GetCon(), prepro);
preinfo.narrow_result_bounds(prepro.lb(), prepro.ub());
preinfo.set_result_type(prepro.get_result_type());
}
}

/// Propagate expression result of constraint \a i top-down
void PropagateResult(BasicFlatConverter& cvt,
int i,
Expand Down
4 changes: 2 additions & 2 deletions include/mp/flat/constr_prop_down.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ class ConstraintPropagatorsDown {
}

/// Propagate a root algebraic range constraint
template <class Body>
void PropagateResult(const AlgebraicConstraint<Body, AlgConRange>& con) {
template <class Body, class RangeOrRhs>
void PropagateResult(const AlgebraicConstraint<Body, RangeOrRhs>& con) {
PropagateResult(con, Context::CTX_POS);
}

Expand Down
Loading

0 comments on commit ea950a1

Please sign in to comment.