Skip to content

Commit

Permalink
Expressions explicifiers #237
Browse files Browse the repository at this point in the history
Assign expression to a variable - extra API
  • Loading branch information
glebbelov committed Aug 13, 2024
1 parent 1dafa25 commit 0cd74af
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 22 deletions.
62 changes: 52 additions & 10 deletions include/mp/flat/constr_2_expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ class Constraints2Expr {
const ConditionalConstraint< AlgebraicConstraint<Body, RhsOrRange> >& con,
int i,
ConstraintAcceptanceLevel ) {
ConsiderExplicifyingExpression(con); // this is a func con too
ConsiderExplicifyingExpression(con, i); // this is a func con too
if (!con.GetConstraint().GetBody().is_variable()) { // already a variable
ConvertConditionalConLHS(con, i);
return true;
Expand All @@ -116,8 +116,8 @@ class Constraints2Expr {
std::enable_if_t<
std::is_base_of_v<FunctionalConstraint, FuncCon>, bool > = true >
bool ConvertWithExpressions(
const FuncCon& con, int , ConstraintAcceptanceLevel ) {
ConsiderExplicifyingExpression(con);
const FuncCon& con, int i, ConstraintAcceptanceLevel ) {
ConsiderExplicifyingExpression(con, i);
return false; // leave it active
}

Expand Down Expand Up @@ -169,6 +169,26 @@ class Constraints2Expr {
const NLConstraint& , int , ConstraintAcceptanceLevel ) {
return false;
}
/// NLLogical: just produced.
bool ConvertWithExpressions(
const NLLogical& , int , ConstraintAcceptanceLevel ) {
return false;
}
/// NLEquivalence: just produced.
bool ConvertWithExpressions(
const NLEquivalence& , int , ConstraintAcceptanceLevel ) {
return false;
}
/// NLImpl: just produced.
bool ConvertWithExpressions(
const NLImpl& , int , ConstraintAcceptanceLevel ) {
return false;
}
/// NLRimpl: just produced.
bool ConvertWithExpressions(
const NLRimpl& , int , ConstraintAcceptanceLevel ) {
return false;
}


protected:
Expand Down Expand Up @@ -332,17 +352,26 @@ class Constraints2Expr {

/// Consider explicifying an expression
template <class FuncCon>
void ConsiderExplicifyingExpression(const FuncCon& con) {
void ConsiderExplicifyingExpression(const FuncCon& con, int i) {
if (MPCD( IsProperVar(con.GetResultVar()) ))
DoExplicify(con);
DoExplicify(con, i);
}

/// Add expr = var assignment for algebraic expression
template <class FuncCon,
std::enable_if_t<
std::is_base_of_v<NumericFunctionalConstraintTraits, FuncCon>, bool > = true >
void DoExplicify(const FuncCon& con) {

void DoExplicify(const FuncCon& con, int i) {
auto alscope = MPD( MakeAutoLinker( con, i ) ); // link from \a con
assert(!con.GetContext().IsNone());
auto resvar = con.GetResultVar();
AlgConRange rng {-INFINITY, 0.0}; // ctx-: var >= expr(var)
if (con.GetContext().IsMixed())
rng = {0.0, 0.0};
else if (con.GetContext().HasPositive())
rng = {0.0, INFINITY};
MPD( AddConstraint( // -var + expr (in) rng
NLConstraint{ { {-1.0}, {resvar} }, resvar, rng } ) );
}

/// Add expr = var assignment for logical expression (NLEquivalence, NLImpl, NLRImpl).
Expand All @@ -351,9 +380,22 @@ class Constraints2Expr {
template <class FuncCon,
std::enable_if_t<
std::is_base_of_v<LogicalFunctionalConstraintTraits, FuncCon>, bool > = true >
void DoExplicify(const FuncCon& con) {
// If root...
// else...
void DoExplicify(const FuncCon& con, int i) {
auto alscope = MPD( MakeAutoLinker( con, i ) ); // link from \a con
auto resvar = con.GetResultVar();
if (MPCD( is_fixed(resvar) ) // "root" context
&& MPCD( fixed_value(resvar) )) { // static logical constraint
MPD( AddConstraint(NLLogical(resvar)) );
}
else { // A (half-)reified function constraint
assert(!con.GetContext().IsNone());
if (con.GetContext().IsMixed())
MPD( AddConstraint(NLEquivalence(resvar)) );
else if (con.GetContext().HasPositive())
MPD( AddConstraint(NLImpl(resvar)) );
else
MPD( AddConstraint(NLRimpl(resvar)) );
}
}


Expand Down
12 changes: 12 additions & 0 deletions include/mp/flat/constr_prop_down.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "mp/common.h"
#include "mp/flat/constr_std.h"
#include "mp/flat/nl_expr/constr_nl.h"

namespace mp {

Expand Down Expand Up @@ -325,6 +326,17 @@ class ConstraintPropagatorsDown {
}
}

/// Do nothing
inline void PropagateResult(NLConstraint& , double , double , Context ) { }
/// Do nothing
inline void PropagateResult(NLLogical& , double , double , Context ) { }
/// Do nothing
inline void PropagateResult(NLEquivalence& , double , double , Context ) { }
/// Do nothing
inline void PropagateResult(NLImpl& , double , double , Context ) { }
/// Do nothing
inline void PropagateResult(NLRimpl& , double , double , Context ) { }

};

} // namespace mp
Expand Down
17 changes: 16 additions & 1 deletion include/mp/flat/converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,13 @@ class FlatConverter :
return var_info_.at(var);
}

/// Get func con context.
/// Does not check if that's a func con,
/// user check for != CTX_NONE
Context GetInitExprContext(int var) const {
return GetInitExpression(var).GetConstraint().GetContext();
}

/// Get the init expression pointer.
/// @return nullptr if no init expr or not this type
template <class ConType>
Expand Down Expand Up @@ -1538,9 +1545,17 @@ class FlatConverter :
return true;
}

////////////////////// NL Expressions ///////////////////////
////////////////////// NL constraints & expressions ///////////////////////
STORE_CONSTRAINT_TYPE__NO_MAP(
NLConstraint, "acc:nlcon acc:nlalgcon")
STORE_CONSTRAINT_TYPE__NO_MAP(
NLLogical, "acc:nllogcon acc:nllogical")
STORE_CONSTRAINT_TYPE__NO_MAP(
NLEquivalence, "acc:nlequiv")
STORE_CONSTRAINT_TYPE__NO_MAP(
NLImpl, "acc:nlimpl")
STORE_CONSTRAINT_TYPE__NO_MAP(
NLRimpl, "acc:nlrimpl")


protected:
Expand Down
140 changes: 129 additions & 11 deletions include/mp/flat/nl_expr/constr_nl.h
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
#ifndef CONSTR_NL_H
#define CONSTR_NL_H

#include <cmath>
#include <cassert>

#include "mp/error.h"
#include "mp/flat/constr_std.h"

namespace mp {

/// Class NLConstraint.
/// Algebraic range constraint with a linear part
/// and an expression term.
/// LinConRange is a member to avoid overloading
/// when deriving from an existing constraint type.
class NLConstraint
: public LinConRange {
: public BasicConstraint, public NumericFunctionalConstraintTraits {
public:
/// Constraint type name
static const char* GetTypeName() {
Expand All @@ -26,7 +30,10 @@ class NLConstraint
NLConstraint(
const LinTerms& lt, int expr, AlgConRange rng,
bool fSort=true)
: LinConRange(lt, rng, fSort), expr_(expr) { }
: lcr_(lt, rng, fSort), expr_(expr) { }

/// Get the main algebraic constraint
const LinConRange& GetMainCon() const { return lcr_; }

/// Has expression term?
bool HasExpr() const { return expr_>=0; }
Expand All @@ -37,39 +44,150 @@ class NLConstraint
/// to obtain the expression term.
int ExprIndex() const { assert(HasExpr()); return expr_; }

/// Throw - should not be used
VarArray1 GetArguments() const { MP_RAISE("No marking for NL items"); }

/// Compute violation.
template <class VarInfo>
Violation
ComputeViolation(const VarInfo& x, bool logical=false) const {
double bd = GetBody().ComputeValue(x);
double bd = lcr_.GetBody().ComputeValue(x);
if (HasExpr())
bd += x[ExprIndex()]; // Add expr value. Assume it's precomputed
if (!logical) {
if (lb() > bd)
return {lb() - bd, lb()};
if (bd > ub())
return {bd - ub(), ub()};
if (lcr_.lb() > bd)
return {lcr_.lb() - bd, lcr_.lb()};
if (bd > lcr_.ub())
return {bd - lcr_.ub(), lcr_.ub()};
return
{std::max( // negative. Same for strict cmp?
lb() - bd, bd - ub()),
lcr_.lb() - bd, bd - lcr_.ub()),
0.0};
}
return {double(!is_valid(bd)), 1.0};
return {double(!lcr_.is_valid(bd)), 1.0};
}


private:
LinConRange lcr_;
int expr_ {-1};
};


/// Export to JSON
inline void WriteJSON(JSONW jw,
const NLConstraint& nlc) {
WriteJSON(jw["lin_part"], nlc.GetBody());
WriteJSON(jw["main_alg_con"], nlc.GetMainCon());
if (nlc.HasExpr())
WriteJSON(jw["expr_index"], nlc.ExprIndex());
WriteJSON(jw["rhs_or_range"], nlc.GetRhsOrRange());
}

/// Write RhsCon without name.
template <class Writer, class Names>
inline void WriteModelItem(Writer& wrt,
const NLConstraint& nlc,
const Names& vnam) {
wrt << "NLExprIndex: " << vnam.at(nlc.ExprIndex()) << " IN: ";
WriteModelItem(wrt, nlc.GetMainCon(), vnam);
}


/// NL logical constraint: expr(resvar) == true
class NLLogical
: public BasicConstraint, public LogicalFunctionalConstraintTraits {
public:
/// Constraint type name
static const char* GetTypeName() {
return "NLLogical";
}

/// Construct from the result variable
NLLogical(int rv) : resvar_(rv) { assert(rv>=0); }

/// Get resvar
int GetResultVar() const { return resvar_; }

/// Throw - should not be used
VarArray1 GetArguments() const { MP_RAISE("No marking for NL items"); }

/// Compute violation
template <class VarInfo>
Violation
ComputeViolation(const VarInfo& x) const {
return {std::fabs(x[GetResultVar() - 1.0]), 1.0};
}

private:
int resvar_ {-1};
};

/// Write an NLLogical
inline void WriteJSON(JSONW jw,
const NLLogical& nll) {
jw["resvar"] = nll.GetResultVar();
}

/// Write RhsCon without name.
template <class Writer, class Names>
inline void WriteModelItem(Writer& wrt,
const NLLogical& nllc,
const Names& vnam) {
wrt << "NLLogicalExprIndex: " << vnam.at(nllc.GetResultVar());
}


/// Syntax sugar for reification: b==1 <==> expr(b)==1.
/// Sense: equivalence (0), impl(1), rimpl (-1).
/// This is a static constraint.
template <int sense>
class NLReification
: public BasicConstraint, public LogicalFunctionalConstraintTraits {
public:
/// Constraint type name
static const char* GetTypeName() {
if (0==sense) return "NLEquivalence";
if (-1==sense) return "NLRimpl";
if (1==sense) return "NLImpl";
MP_RAISE("NLReif: unknown sense");
}

/// Construct
NLReification(int b) : bvar_(b) { }

/// Get bvar
int GetBVar() const { return bvar_; }

/// Throw - should not be used
VarArray1 GetArguments() const { MP_RAISE("No marking for NL items"); }

// Compute violation... Should be 0

private:
int bvar_ {-1};
};


/// Typedef NLEquivalence
using NLEquivalence = NLReification<0>;
/// Typedef NLImpl
using NLImpl = NLReification<1>;
/// Typedef NLRImpl
using NLRimpl = NLReification<-1>;

/// Write a Reification
template <int sense>
inline void WriteJSON(JSONW jw,
const NLReification<sense>& reif) {
jw["var_explicit"] = reif.GetBVar();
jw["sense"] = sense;
}

/// Write RhsCon without name.
template <class Writer, int sense, class Names>
inline void WriteModelItem(Writer& wrt,
const NLReification<sense>& nlr,
const Names& vnam) {
wrt << "EXPLICIT var: " << vnam.at(nlr.GetBVar());
}

} // namespace mp
Expand Down

0 comments on commit 0cd74af

Please sign in to comment.