Skip to content

Commit

Permalink
NL alg expr explicifiers #237
Browse files Browse the repository at this point in the history
Similar to NLEquivalence etc, dedicated high-level constraints for var <=/==/>= expr, flexible for more solver APIs
  • Loading branch information
glebbelov committed Aug 30, 2024
1 parent 7524883 commit 1a67972
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 52 deletions.
55 changes: 45 additions & 10 deletions include/mp/flat/constr_2_expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,25 @@ class Constraints2Expr {
ConstraintAcceptanceLevel , ExpressionAcceptanceLevel ) {
return false;
}
/// NLAssignEQ: just produced.
bool ConvertWithExpressions(
const NLAssignEQ& , int ,
ConstraintAcceptanceLevel , ExpressionAcceptanceLevel ) {
return false;
}
/// NLAssignLE: just produced.
bool ConvertWithExpressions(
const NLAssignLE& , int ,
ConstraintAcceptanceLevel , ExpressionAcceptanceLevel ) {
return false;
}
/// NLAssignGE: just produced.
bool ConvertWithExpressions(
const NLAssignGE& , int ,
ConstraintAcceptanceLevel , ExpressionAcceptanceLevel ) {
return false;
}

/// NLLogical: just produced.
bool ConvertWithExpressions(
const NLLogical& , int ,
Expand Down Expand Up @@ -310,7 +329,10 @@ class Constraints2Expr {
return false;
}

/// Convert algebraic con to a NLConstraint
/// Convert algebraic con to a \a NLConstraint,
/// if \a NLConstraint's are accepted. Otherwise,
/// explicify the expression and convert to
/// \a LinConLE/EQ/GE/Range.
template <class Body, class RhsOrRange>
void ConvertToNLCon(
const AlgebraicConstraint<Body, RhsOrRange>& con, int i) {
Expand All @@ -324,7 +346,6 @@ class Constraints2Expr {
int exprResVar = -1;
if (exprTerm.GetArguments().is_variable()) {
exprResVar = exprTerm.GetArguments().get_representing_variable();
// assert( !MPCD( IsProperVar(exprResVar) ) ); // is an expr
} else {
assert( !exprTerm.GetArguments().empty() ); // has more terms, or coef != 1.0
exprTerm.AddContext( // Context is compulsory
Expand All @@ -334,13 +355,28 @@ class Constraints2Expr {
: Context::CTX_NEG);
exprResVar = MPD( AssignResultVar2Args(std::move(exprTerm)) );
}
if (!MPCD(VarHasMarking(exprResVar))) // mark as expr if new
if (!MPCD(VarHasMarking(exprResVar))) // mark as expr if new
MPD( MarkAsExpression(exprResVar) );
if ( !MPCD( ModelAPIAcceptsAndRecommends((const NLConstraint*)nullptr) ) )
MPD( MarkAsResultVar(exprResVar) );
/// Exists and marked a variable
else if (MPCD( IsProperVar(exprResVar) )) { // Not an expression after all
if (MPCD( IsProperVar(exprResVar) )) { // Not an expression after all
lt.add_term(1.0, exprResVar);
exprResVar = -1; // no expression
lt.sort_terms();
if (lt.size()>1) { // have other variables
if (MPCD( ModelAPIAcceptsAndRecommends( // Accepts LinCon..
(const AlgebraicConstraint<LinTerms, RhsOrRange>*)nullptr) )) {
AlgebraicConstraint<LinTerms, RhsOrRange> lc {lt, con.GetRhsOrRange(), false};
MPD( AddConstraint( std::move(lc) ) );
return;
} else {
assert( MPCD( ModelAPIAcceptsAndRecommends((const NLConstraint*)nullptr) ) );
}
} else { // single variable, its expression will be explicified
MPD( NarrowVarBounds(exprResVar, rng.lb(), rng.ub()) );
return;
}
exprResVar = -1; // no expression
} // else, stays as -1
NLConstraint nlc{lt, exprResVar, rng, false}; // no sorting
MPD( AddConstraint( std::move(nlc) ) );
Expand Down Expand Up @@ -490,13 +526,12 @@ class Constraints2Expr {
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};
MPD( AddConstraint(NLAssignEQ(resvar)) );
else if (con.GetContext().HasPositive())
rng = {0.0, INFINITY};
MPD( AddConstraint( // -var + expr (in) rng
NLConstraint{ { {-1.0}, {resvar} }, resvar, rng } ) );
MPD( AddConstraint(NLAssignLE(resvar)) );
else
MPD( AddConstraint(NLAssignGE(resvar)) );
}

/// Add expr = var assignment for logical expression (NLEquivalence, NLImpl, NLRImpl).
Expand Down
7 changes: 7 additions & 0 deletions include/mp/flat/converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -1587,6 +1587,13 @@ class FlatConverter :
////////////////////// NL constraints & expressions ///////////////////////
STORE_CONSTRAINT_TYPE__NO_MAP(
NLConstraint, "acc:nlcon acc:nlalgcon")
STORE_CONSTRAINT_TYPE__NO_MAP(
NLAssignEQ, "acc:nlassigneq")
STORE_CONSTRAINT_TYPE__NO_MAP(
NLAssignLE, "acc:nlassignle")
STORE_CONSTRAINT_TYPE__NO_MAP(
NLAssignGE, "acc:nlassignge")

STORE_CONSTRAINT_TYPE__NO_MAP(
NLLogical, "acc:nllogcon acc:nllogical")
STORE_CONSTRAINT_TYPE__NO_MAP(
Expand Down
74 changes: 66 additions & 8 deletions include/mp/flat/nl_expr/constr_nl.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace mp {

/// Class NLConstraint.
/// Algebraic range constraint with a linear part
/// and an expression term.
/// and an expression term: `lb <= a'x + expr <= ub`.
/// LinConRange is a member to avoid overloading
/// when deriving from an existing constraint type.
class NLConstraint
Expand Down Expand Up @@ -92,6 +92,64 @@ inline void WriteModelItem(Writer& wrt,
}


/// Syntax sugar for the assignment: var <=/==/>= expr.
/// Can have special meaning in certain solvers.
/// Sense: equality (0), >= (1), <= (-1).
/// Can be implemented as == for all senses (e.g., GRBaddgenconstrNL),
/// the inequalities can be used to preserve convexity.
/// This is a static constraint.
template <int sense>
class NLBaseAssign
: public BasicConstraint, public NumericFunctionalConstraintTraits {
public:
/// Constraint type name
static const char* GetTypeName() {
if (0==sense) return "NLAssignEQ";
if (-1==sense) return "NLAssignLE";
if (1==sense) return "NLAssignGE";
MP_RAISE("NLBaseAssign: unknown sense");
}

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

/// Get var
int GetVar() 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 NLAssignEQ
using NLAssignEQ = NLBaseAssign<0>;
/// Typedef NLAssignLE
using NLAssignLE = NLBaseAssign<-1>;
/// Typedef NLAssignGE
using NLAssignGE = NLBaseAssign<1>;

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

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


/// NLComplementarity
/// TODO extra class, to enable ACCEPT_CONSTRAINT
using NLComplementarity = ComplementarityConstraint<AffineExpr>;
Expand Down Expand Up @@ -142,7 +200,7 @@ inline void WriteModelItem(Writer& wrt,


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

Expand All @@ -175,15 +233,15 @@ class NLReification
/// Typedef NLEquivalence
using NLEquivalence = NLReification<0>;
/// Typedef NLImpl
using NLImpl = NLReification<1>;
using NLImpl = NLReification<-1>;
/// Typedef NLRImpl
using NLRimpl = NLReification<-1>;
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["var_explicit_reif"] = reif.GetBVar();
jw["sense"] = sense;
}

Expand All @@ -192,7 +250,7 @@ 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());
wrt << "EXPLICIT REIF var: " << vnam.at(nlr.GetBVar());
}

} // namespace mp
Expand Down
26 changes: 20 additions & 6 deletions include/mp/flat/nl_expr/model_api_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class BasicExprModelAPI
? nlc.ExprIndex() : -1;
if (i_expr<0)
return MPD( GetZeroExpression() );
return GetPureInitExpression(i_expr);
return GetInitExpression(i_expr); // could be explicified
}

/// Get NLConstraint's lower bound
Expand All @@ -113,24 +113,38 @@ class BasicExprModelAPI
return nlc.GetMainCon().ub();
}

/// Get the expression term of an \a NLBaseAssign.
template <int sense>
ExprType GetExpression(const NLBaseAssign<sense>& nll) {
assert( nll.GetVar()>=0 );
return GetPureInitExpression(nll.GetVar());
}

/// Get the variable of an \a NLBaseAssign.
template <int sense>
int GetVariable(const NLBaseAssign<sense>& nll) {
assert( nll.GetVar()>=0 );
return nll.GetVar();
}

/// Get the expression term of an \a NLLogical.
ExprType GetExpression(const NLLogical& nll) {
assert( nll.GetResultVar()>=0 );
return GetInitExpression(nll.GetResultVar());
return GetPureInitExpression(nll.GetResultVar());
}

/// Get the expression term of an \a NLReification.
template <int sense>
ExprType GetExpression(const NLReification<sense>& nll) {
assert( nll.GetResultVar()>=0 );
return GetPureInitExpression(nll.GetResultVar());
assert( nll.GetBVar()>=0 );
return GetPureInitExpression(nll.GetBVar());
}

/// Get the variable of an \a NLReification.
template <int sense>
int GetVariable(const NLReification<sense>& nll) {
assert( nll.GetResultVar()>=0 );
return nll.GetResultVar();
assert( nll.GetBVar()>=0 );
return nll.GetBVar();
}

/// GetLinSize(le)
Expand Down
34 changes: 34 additions & 0 deletions solvers/scipmp/scipmpmodelapi.cc
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,40 @@ void ScipModelAPI::AddConstraint( const NLConstraint& nlc ) {
getPROBDATA()->vars[ GetLinVar(nlc, i) ], GetLinCoef(nlc, i)) );
}

void ScipModelAPI::AddConstraint( const NLAssignEQ& nlae ) {
getPROBDATA()->nlconss.push_back(nullptr);

SCIP_CCALL( SCIPcreateConsBasicNonlinear(
getSCIP(), &getPROBDATA()->nlconss.back(), nlae.GetName(),
GetExpression(nlae), 0.0, 0.0) );
SCIP_CCALL( SCIPaddCons(getSCIP(), getPROBDATA()->nlconss.back()) );
SCIP_CCALL( SCIPaddLinearVarNonlinear(
getSCIP(), getPROBDATA()->nlconss.back(),
getPROBDATA()->vars[ GetVariable(nlae) ], -1.0) );
}
void ScipModelAPI::AddConstraint( const NLAssignLE& nlae ) {
getPROBDATA()->nlconss.push_back(nullptr);

SCIP_CCALL( SCIPcreateConsBasicNonlinear(
getSCIP(), &getPROBDATA()->nlconss.back(), nlae.GetName(),
GetExpression(nlae), 0.0, Infinity()) );
SCIP_CCALL( SCIPaddCons(getSCIP(), getPROBDATA()->nlconss.back()) );
SCIP_CCALL( SCIPaddLinearVarNonlinear(
getSCIP(), getPROBDATA()->nlconss.back(),
getPROBDATA()->vars[ GetVariable(nlae) ], -1.0) );
}
void ScipModelAPI::AddConstraint( const NLAssignGE& nlae ) {
getPROBDATA()->nlconss.push_back(nullptr);

SCIP_CCALL( SCIPcreateConsBasicNonlinear(
getSCIP(), &getPROBDATA()->nlconss.back(), nlae.GetName(),
GetExpression(nlae), MinusInfinity(), 0.0) );
SCIP_CCALL( SCIPaddCons(getSCIP(), getPROBDATA()->nlconss.back()) );
SCIP_CCALL( SCIPaddLinearVarNonlinear(
getSCIP(), getPROBDATA()->nlconss.back(),
getPROBDATA()->vars[ GetVariable(nlae) ], -1.0) );
}

void ScipModelAPI::AddConstraint( const NLLogical& nll ) {
getPROBDATA()->nlconss.push_back(nullptr);

Expand Down
Loading

0 comments on commit 1a67972

Please sign in to comment.