Skip to content

Commit b767338

Browse files
committed
Exponential cone LHS as AffineExpr #192
Recognize exp cones where LHS is an affine expression
1 parent b15a483 commit b767338

File tree

9 files changed

+94
-61
lines changed

9 files changed

+94
-61
lines changed

CHANGES.mp.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
Summary of recent updates to the AMPL MP Library
22
================================================
33

4-
5-
## TBD
4+
## 20230531
5+
- Cones: recognize (affine_expr) >= y * exp(z/y)
6+
as exponential cone.
67
- Cones: recognize xy >= 1 as rotated SOC.
8+
- Wrong solver options are gracefully reported via
9+
solve_message.
710

811

912
## 20230515

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ endmacro()
214214
include_directories(include)
215215

216216
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
217-
set(MP_DATE 20230515)
217+
set(MP_DATE 20230531)
218218

219219
set(MP_SYSINFO "${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_PROCESSOR}")
220220

include/mp/flat/redef/conic/cones.h

Lines changed: 45 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -631,8 +631,6 @@ class Convert1ExpC : public MCKeeper<MCType> {
631631
bool valid_ {false};
632632
};
633633

634-
/// LHS of ax >= by * exp( cz / by )
635-
using LhsTraits = SubexprTraits<1>;
636634
/// RHS
637635
using RhsTraits = SubexprTraits<2>;
638636

@@ -651,30 +649,20 @@ class Convert1ExpC : public MCKeeper<MCType> {
651649
if (1 == body.GetQPTerms().size()) {
652650
const auto& lt = body.GetLinTerms(); // But we have c0*x + c1*y*zz <=> 0
653651
const auto& qt = body.GetQPTerms();
654-
LhsTraits lhst;
655-
if (0.0 == std::fabs(rhs) && // rhs==0:
656-
1 == lt.size()) // ax >= by exp(cz / by) ?
657-
lhst = ClassifyLhs(lt.coef(0)*sens, lt.var(0));
658-
else if (0 == lt.size()) // const >= by exp(cz / by) ?
659-
lhst = ClassifyLhs(-rhs*sens, -1);
660-
661-
if (lhst) {
662-
if (auto rhst = ClassifyRhsQuadr(
663-
-qt.coef(0)*sens, qt.var1(0), qt.var2(0)))
664-
return AddExpCone(lhst, rhst);
665-
else if (auto rhst = ClassifyRhsQuadr(
666-
-qt.coef(0)*sens, qt.var2(0), qt.var1(0)))
667-
return AddExpCone(lhst, rhst);
668-
}
652+
653+
if (auto rhst = ClassifyRhsQuadr(
654+
-qt.coef(0)*sens, qt.var1(0), qt.var2(0)))
655+
return AddExpCone(lt, -rhs, -1, sens, rhst);
656+
else if (auto rhst = ClassifyRhsQuadr(
657+
-qt.coef(0)*sens, qt.var2(0), qt.var1(0)))
658+
return AddExpCone(lt, -rhs, -1, sens, rhst);
669659
}
670660
return false;
671661
}
672662

673663
/// DoRun.
674-
/// Body: linear, i.e., var1 (or const) >= var2 (or const).
675-
/// In particular, the following cases.
676-
/// ///
677-
/// Considering const>=0.
664+
/// Body: linear, ax >= b exp(cz / b)
665+
///
678666
/// Accept non-(+-1) coefficients.
679667
///
680668
/// @param body: linear constraint body.
@@ -685,40 +673,17 @@ class Convert1ExpC : public MCKeeper<MCType> {
685673
bool DoRun(const LinTerms& lt,
686674
int sens, double rhs) {
687675
assert((sens==1 || sens==-1) && "sens 1 or -1 only");
688-
if (0.0 == std::fabs(rhs) && // rhs==0: y==const
689-
2 == lt.size()) { // ax >= b exp(cz / b)
690-
if (auto rhst = ClassifyRhsLin(-lt.coef(0)*sens, lt.var(0))) {
691-
// c2*x >= -c1*exp()
692-
if (auto lhst = ClassifyLhs(lt.coef(1)*sens, lt.var(1))) {
693-
return AddExpCone(lhst, rhst);
694-
}
695-
} else if (auto rhst = ClassifyRhsLin(-lt.coef(1)*sens, lt.var(1))) {
696-
// c1*x >= -c2*exp()
697-
if (auto lhst = ClassifyLhs(lt.coef(0)*sens, lt.var(0))) {
698-
return AddExpCone(lhst, rhst);
699-
}
700-
}
701-
} else if (1 == lt.size()) { // const >= b exp(cz / b)
702-
if (auto lhst = ClassifyLhs(-rhs*sens, -1)) {
703-
if (auto rhst = ClassifyRhsLin(-lt.coef(0)*sens, lt.var(0))) {
704-
return AddExpCone(lhst, rhst);
705-
}
676+
for (size_t i=0; i<lt.size(); ++i) {
677+
if (auto rhst = ClassifyRhsLin(-lt.coef(i)*sens, lt.var(i))) {
678+
// ... >= -c[i]*exp(...)
679+
return AddExpCone(lt, -rhs, i, sens, rhst);
706680
}
707681
}
708682
return false;
709683
}
710684

711685

712686
protected:
713-
LhsTraits ClassifyLhs(double a, int x) {
714-
LhsTraits result {{a}, {x}};
715-
if ((x<0 && a>=0.0) || // Lhs is const, >=0
716-
(x>=0 && a>=0.0 && MC().lb(x)>=0.0) || // c*v >= 0
717-
(x>=0 && a<=0.0 && MC().ub(x)>=0.0))
718-
result.valid_ = true;
719-
return result;
720-
}
721-
722687
/// The RHS is just b * (v=exp(z))?
723688
RhsTraits ClassifyRhsLin(double b, int v) {
724689
assert(v>=0);
@@ -758,7 +723,7 @@ class Convert1ExpC : public MCKeeper<MCType> {
758723
const auto& ae = pConLin->GetAffineExpr();
759724
if (0.0 == std::fabs(ae.constant_term() &&
760725
1==ae.GetBody().size())) {
761-
const auto& body = ae.GetBody(); // can we ever get here?
726+
const auto& body = ae.GetBody(); // tested by expcones_06.mod
762727
if (y == body.var(0)) { // v2 = z / (c1*y)
763728
result.coefs_ = {b, b/body.coef(0)};
764729
result.vars_ = {y, z};
@@ -773,21 +738,46 @@ class Convert1ExpC : public MCKeeper<MCType> {
773738
return result;
774739
}
775740

776-
bool AddExpCone(LhsTraits l, RhsTraits r) {
777-
assert(l.vars2del_.empty());
741+
/// LHS: sens * (lt+cterm [no lt_i_skip element])
742+
bool AddExpCone(const LinTerms& lt, double cterm,
743+
int lt_i_skip, int sens,
744+
RhsTraits r) {
778745
for (auto v2d: r.vars2del_) // unuse result vars
779746
MC().DecrementVarUsage(v2d);
780747
std::array<int, 3> args
781-
{l.vars_[0], r.vars_[0], r.vars_[1]};
748+
{0, r.vars_[0], r.vars_[1]};
749+
std::array<double, 3> coefs
750+
{1.0*sens, r.coefs_[0], r.coefs_[1]};
751+
auto n_lterms = lt.size() - (lt_i_skip>=0);
752+
if (0==n_lterms) { // LHS = const
753+
args[0] = -1; // to be replaced by fixed var==1
754+
coefs[0] = cterm * sens;
755+
} else if (1==n_lterms && 0.0==std::fabs(cterm)) {
756+
for (size_t i=0; i<lt.size(); ++i) { // LHS = c * x
757+
if ((int)i!=lt_i_skip) {
758+
args[0] = lt.var(i);
759+
coefs[0] = lt.coef(i) * sens; // * sens
760+
}
761+
}
762+
} else { // LHS = affine_expr
763+
AffineExpr ae; // all 3 cases tested by
764+
ae.constant_term(cterm); // expcones_..mod
765+
ae.reserve(n_lterms);
766+
for (size_t i=0; i<lt.size(); ++i) {
767+
if ((int)i!=lt_i_skip)
768+
ae.add_term( lt.coef(i), lt.var(i) );
769+
}
770+
args[0] = MC().AssignResultVar2Args(
771+
LinearFunctionalConstraint(std::move(ae)));
772+
coefs[0] = sens; // * sens
773+
}
782774
for (int i=0; i<3; ++i) {
783775
if (args[i]<0) { // marked as -1?
784776
args[i] = MC().MakeFixedVar(1.0);
785777
}
786778
}
787779
MC().AddConstraint(
788-
ExponentialConeConstraint(
789-
args,
790-
{l.coefs_[0], r.coefs_[0], r.coefs_[1]}));
780+
ExponentialConeConstraint(args, coefs));
791781
return true;
792782
}
793783

solvers/gurobi/CHANGES.gurobi.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
Summary of recent updates to gurobi for AMPL
22
============================================
33

4+
## 20230531
5+
- Wrong solver options are gracefully reported via
6+
solve_message.
7+
8+
49
## 20230522
510
- Added option 'lim:sol' to set a limit on the number of solutions found
611

solvers/highsmp/CHANGES.highsmp.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
Summary of recent updates to HiGHS for AMPL
22
===========================================
33

4-
## TBD
5-
- MIP start
4+
## 20230531
5+
- *MIP start*.
6+
HiGHS 1.5 supports complete MIP starts only.
7+
For partial MIP start, user solve with fixed known
8+
variables first (Modeling Tip #7).
9+
- Wrong solver options are gracefully reported via
10+
solve_message.
11+
612

713
## 20230522
814
- Fixed solution status reporting to AMPL
915
- Fixed basis input with obj offset or missing data
1016
- Added warm start for LP problems
1117
- Reading column names from *col* file is present
1218

19+
1320
## 20230424
1421
- *Changes in the MP library*: added variable names support
1522
and removed spurious starting solution

solvers/highsmp/highsmp-lib.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ APIEXPORT void* AMPLloadmodel(int argc, char** argv, void** slvout) {
1616
AMPLS_MP_Solver* slv = AMPLSOpenHighs(slv_opt, cb);
1717
if (!slv)
1818
return NULL;
19-
AMPLSLoadNLModel(slv, nl_filename);
19+
AMPLSLoadNLModel(slv, nl_filename, (char**)0);
2020
return slv;
2121
}
2222

solvers/mosek/CHANGES.mosek.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
Summary of recent updates to MOSEK for AMPL
22
===========================================
33

4+
## 20230531
5+
- Cones: recognize (affine_expr) >= y * exp(z/y)
6+
as exponential cone.
7+
- Cones: recognize xy >= 1 as rotated SOC.
8+
- Wrong solver options are gracefully reported via
9+
solve_message.
10+
411

512
## 20230515
613
- *Exponential cones*. MP driver recognizes exponential
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# parameter values
2+
param b default 1.25;
3+
param p default 0.51;
4+
5+
# decision variables
6+
var q1;
7+
var q2 >= 0;
8+
var w >= 0;
9+
10+
# objective
11+
maximize ElogR:
12+
p*q1 + (1-p)*q2;
13+
14+
# conic constraints
15+
s.t. T1: 1 + b*w >= exp( q1 );
16+
s.t. T2: -1 + w +10*q2 <= -5 * q2 * exp( q1 / (q2*5) );

test/end2end/cases/categorized/fast/conic/modellist.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,5 +83,10 @@
8383
"name" : "expcones_05__const",
8484
"objective" : -0.8988331541,
8585
"tags" : ["expcones"]
86+
},
87+
{
88+
"name" : "expcones_06__kelly__affexp_lhs",
89+
"objective" : 0.1116637435,
90+
"tags" : ["expcones"]
8691
}
8792
]

0 commit comments

Comments
 (0)