diff --git a/include/mp/flat/expr_quadratic.h b/include/mp/flat/expr_quadratic.h index 0f4d5c3b6..a9a76e019 100644 --- a/include/mp/flat/expr_quadratic.h +++ b/include/mp/flat/expr_quadratic.h @@ -210,6 +210,12 @@ class QuadAndLinTerms : QuadTerms::add(qlt.GetQPTerms()); } + /// Multiply by const + void operator*=(double n) { + GetLinTerms() *= n; + GetQPTerms() *= n; + } + /// Value at given variable vector template long double ComputeValue(const VarInfo& x) const { diff --git a/include/mp/flat/problem_flattener.h b/include/mp/flat/problem_flattener.h index adfd96313..5d1f0c382 100644 --- a/include/mp/flat/problem_flattener.h +++ b/include/mp/flat/problem_flattener.h @@ -497,6 +497,25 @@ class ProblemFlattener : return AssignResult2Args( std::move(fc) ); } + /// Specialize for Div: + /// For constant divisor, return linear expression. + /// Why we need this: AMPL 20240331 does not preprocess this. + template > + EExpr VisitDivExpression(ExprArray ea) { + auto dividend = Convert2EExpr(*ea.begin()); + auto it = ea.begin(); + auto divisor = Convert2EExpr(*(++it)); + if (divisor.is_constant()) { + if (!divisor.constant_term()) + MP_RAISE("Division by 0 in the model."); + dividend *= (1.0 / divisor.constant_term()); + return dividend; + } + DivConstraint fc + {{ Convert2Var(std::move(dividend)), Convert2Var(std::move(divisor)) }}; + return AssignResult2Args( std::move(fc) ); + } + template void Exprs2Vars(const ExprArray& ea, std::vector& result) { assert(result.empty()); @@ -666,7 +685,7 @@ class ProblemFlattener : } EExpr VisitDiv(BinaryExpr e) { - return VisitFunctionalExpression( + return VisitDivExpression( { e.lhs(), e.rhs() }); } diff --git a/test/end2end/cases/categorized/fast/nonlinear/div_by_const.mod b/test/end2end/cases/categorized/fast/nonlinear/div_by_const.mod new file mode 100644 index 000000000..c84a0fff4 --- /dev/null +++ b/test/end2end/cases/categorized/fast/nonlinear/div_by_const.mod @@ -0,0 +1,15 @@ +#### See if Div/const is well presolved +#### MP does this because AMPL 20240331 does not +#### See if abs() is well redefined (or used well natively) +################################################### + +var x {1..3} >=-200, <=200; + +minimize Unbalance: + sum {i in 1..3} abs(x[i] - sum {i1 in 1..3} x[i1] / 3); + +## Some extra constraints + s.t. C1: sum {I in 1..3} x[I] == 15; + + s.t. C2: x[1] + 2*x[2] + 4*x[3] == 18; + diff --git a/test/end2end/cases/categorized/fast/nonlinear/modellist.json b/test/end2end/cases/categorized/fast/nonlinear/modellist.json index d2109dbd9..ef5e88c25 100644 --- a/test/end2end/cases/categorized/fast/nonlinear/modellist.json +++ b/test/end2end/cases/categorized/fast/nonlinear/modellist.json @@ -1,4 +1,9 @@ [ + { + "name" : "div_by_const", + "tags" : ["linear"], + "objective": 11.33333333333333 + }, { "name" : "expA_1", "tags" : ["nonlinear"],