Upload
german-ferrari
View
1.083
Download
0
Embed Size (px)
Citation preview
Germán FerrariInstituto de Computación, Universidad de la República, Uruguay
An embedded DSL to manipulate MathProg
Mixed Integer Programming
models within Scala
An embedded DSL to manipulate MathProg
Mixed Integer Programming
models within ScalaGermán Ferrari
Instituto de Computación, Universidad de la República, Uruguay
GNU MathProg
MathProg = Mathematical Programming
Has nothing to do with computer programming
It’s a synonym of “mathematical optimization”
A “program” here refers to a plan or schedule
GNU MathProg
It’s a modeling language
Optimization problem Mathematical modeling
GNU MathProg Computational experiments
Supported by the GNU Linear Programming Kit
GNU MathProg
Supports linear models, with either real or integer decision variables
This is called “Mixed Integer Programming” (MIP) because it mixes integer and real variables
Generic MIP optimization problemminimize f:
sum{j in J} c[j] * x[j] +
sum{k in K} d[k] * y[k];
subject to g{i in I}:
sum{j in J} a[i,j] * x[j] +
sum{k in K} e[i,k] * y[k] <= b[i];
Generic MIP optimization problem
Find x[j] and y[k], that minimize a function f, satisfying constraints g[i]
minimize f:
sum{j in J} c[j] * x[j] +
sum{k in K} d[k] * y[k];
subject to g{i in I}:
sum{j in J} a[i,j] * x[j] +
sum{k in K} e[i,k] * y[k] <= b[i];
Generic MIP optimization problem
Find x[j] and y[k], that minimize a function f, satisfying constraints g[i]
minimize f:
sum{j in J} c[j] * x[j] +
sum{k in K} d[k] * y[k];
subject to g{i in I}:
sum{j in J} a[i,j] * x[j] +
sum{k in K} e[i,k] * y[k] <= b[i];
Decision variables(real or integer)
Generic MIP optimization problem
Find x[j] and y[k], that minimize a function f, satisfying constraints g[i]
minimize f:
sum{j in J} c[j] * x[j] +
sum{k in K} d[k] * y[k];
subject to g{i in I}:
sum{j in J} a[i,j] * x[j] +
sum{k in K} e[i,k] * y[k] <= b[i];
Decision variables(real or integer)Objective
function
Generic MIP optimization problem
Find x[j] and y[k], that minimize a function f, satisfying constraints g[i]
minimize f:
sum{j in J} c[j] * x[j] +
sum{k in K} d[k] * y[k];
subject to g{i in I}:
sum{j in J} a[i,j] * x[j] +
sum{k in K} e[i,k] * y[k] <= b[i];
Decision variables(real or integer)Objective
function
Constraints
Generic MIP optimization problem
Find x[j] and y[k], that minimize a function f, satisfying constraints g[i]
minimize f:
sum{j in J} c[j] * x[j] +
sum{k in K} d[k] * y[k];
subject to g{i in I}:
sum{j in J} a[i,j] * x[j] +
sum{k in K} e[i,k] * y[k] <= b[i];
Decision variables(real or integer)Objective
function
ConstraintsLinear
Generic MIP optimization problem
Full modelparam m; param n; param l;
set I := 1 .. m;set J := 1 .. n;set K := 1 .. l;
param c{J}; param d{K};param a{I, J}; param e{I, K}; param b{I};
var x{J} integer, >= 0;var y{K} >= 0;
minimize f: sum{j in J} c[j] * x[j] + sum{k in K} d[k] * y[k];subject to g{i in I}: sum{j in J} a[i,j] * x[j] + sum{k in K} e[i,k] * y[k] <= b[i];
param m; param n; param l;
set I := 1 .. m;set J := 1 .. n;set K := 1 .. l;
param c{J}; param d{K};param a{I, J}; param e{I, K}; param b{I};
var x{J} integer, >= 0;var y{K} >= 0;
minimize f: sum{j in J} c[j] * x[j] + sum{k in K} d[k] * y[k];subject to g{i in I}: sum{j in J} a[i,j] * x[j] + sum{k in K} e[i,k] * y[k] <= b[i];
Generic MIP optimization problem
Declaration of variables
param m; param n; param l;
set I := 1 .. m;set J := 1 .. n;set K := 1 .. l;
param c{J}; param d{K};param a{I, J}; param e{I, K}; param b{I};
var x{J} integer, >= 0;var y{K} >= 0;
minimize f: sum{j in J} c[j] * x[j] + sum{k in K} d[k] * y[k];subject to g{i in I}: sum{j in J} a[i,j] * x[j] + sum{k in K} e[i,k] * y[k] <= b[i];
Generic MIP optimization problem
Parameters
Generic MIP optimization problemparam m; param n; param l;
set I := 1 .. m;set J := 1 .. n;set K := 1 .. l;
param c{J}; param d{K};param a{I, J}; param e{I, K}; param b{I};
var x{J} integer, >= 0;var y{K} >= 0;
minimize f: sum{j in J} c[j] * x[j] + sum{k in K} d[k] * y[k];subject to g{i in I}: sum{j in J} a[i,j] * x[j] + sum{k in K} e[i,k] * y[k] <= b[i];
Indexing sets
Why MathProg in Scala
Why MathProg in Scala
Generate constraints
Create derived models
Develop custom resolution methods at high level
...
MathProg in Scala (deep embedding)
SyntaxRepresent MathProg models with Scala data typesMimic MathProg syntax with Scala functions
SemanticsGenerate MathProg code, others ...
MathProg in Scala (deep embedding)
SyntaxRepresent MathProg models with Scala data typesMimic MathProg syntax with Scala functions
SemanticsGenerate MathProg code, others ...
Abstract syntax tree
MathProg in Scala (deep embedding)
SyntaxRepresent MathProg models with Scala data typesMimic MathProg syntax with Scala functions
SemanticsGenerate MathProg code, others ...
Smart constructors and combinators
Abstract syntax tree
MathProg EDSLparam m; param n; param l;
set I := 1 .. m;set J := 1 .. n;set K := 1 .. l;
param c{J}; param d{K};param a{I, J}; // ...
var x{J} integer, >= 0;var y{K} >= 0;
val m = param("m")val n = param("n")val l = param("l")
val I = set("I") := 1 to mval J = set("J") := 1 to n val K = set("K") := 1 to l
val c = param("c", J)val d = param("d", K)val a = param("a", I, J)// ...
val x = xvar("x", J).integer >= 0val y = xvar("y", K) >= 0
MathProg EDSLparam m; param n; param l;
set I := 1 .. m;set J := 1 .. n;set K := 1 .. l;
param c{J}; param d{K};param a{I, J}; // ...
var x{J} integer, >= 0;var y{K} >= 0;
val m = param("m")val n = param("n")val l = param("l")
val I = set("I") := 1 to mval J = set("J") := 1 to n val K = set("K") := 1 to l
val c = param("c", J)val d = param("d", K)val a = param("a", I, J)// ...
val x = xvar("x", J).integer >= 0val y = xvar("y", K) >= 0
set
param
xvar ...
MathProg EDSLparam m; param n; param l;
set I := 1 .. m;set J := 1 .. n;set K := 1 .. l;
param c{J}; param d{K};param a{I, J}; // ...
var x{J} integer, >= 0;var y{K} >= 0;
val m = param("m")val n = param("n")val l = param("l")
val I = set("I") := 1 to mval J = set("J") := 1 to n val K = set("K") := 1 to l
val c = param("c", J)val d = param("d", K)val a = param("a", I, J)// ...
val x = xvar("x", J).integer >= 0val y = xvar("y", K) >= 0
set
param
xvar ...
ParamStat( "c", domain = Some( IndExpr( List( IndEntry( Nil, SetRef(J) ) ))))
val m = param("m")val n = param("n")val l = param("l")
val I = set("I") := 1 to mval J = set("J") := 1 to n val K = set("K") := 1 to l
val c = param("c", J)val d = param("d", K)val a = param("a", I, J)// ...
val x = xvar("x", J).integer >= 0val y = xvar("y", K) >= 0
MathProg EDSL
Scala range notation for arithmetic sets
param m; param n; param l;
set I := 1 .. m;set J := 1 .. n;set K := 1 .. l;
param c{J}; param d{K};param a{I, J}; // ...
var x{J} integer, >= 0;var y{K} >= 0;
val m = param("m")val n = param("n")val l = param("l")
val I = set("I") := 1 to mval J = set("J") := 1 to n val K = set("K") := 1 to l
val c = param("c", J)val d = param("d", K)val a = param("a", I, J)// ...
val x = xvar("x", J).integer >= 0val y = xvar("y", K) >= 0
param m; param n; param l;
set I := 1 .. m;set J := 1 .. n;set K := 1 .. l;
param c{J}; param d{K};param a{I, J}; // ...
var x{J} integer, >= 0;var y{K} >= 0;
MathProg EDSL
Varargs for simple indexing expressions
val m = param("m")val n = param("n")val l = param("l")
val I = set("I") := 1 to mval J = set("J") := 1 to n val K = set("K") := 1 to l
val c = param("c", J)val d = param("d", K)val a = param("a", I, J)// ...
val x = xvar("x", J).integer >= 0val y = xvar("y", K) >= 0
param m; param n; param l;
set I := 1 .. m;set J := 1 .. n;set K := 1 .. l;
param c{J}; param d{K};param a{I, J}; // ...
var x{J} integer, >= 0;var y{K} >= 0;
MathProg EDSL
Returns a new variable with the `integer’ attribute
val m = param("m")val n = param("n")val l = param("l")
val I = set("I") := 1 to mval J = set("J") := 1 to n val K = set("K") := 1 to l
val c = param("c", J)val d = param("d", K)val a = param("a", I, J)// ...
val x = xvar("x", J).integer >= 0val y = xvar("y", K) >= 0
MathProg EDSL
Another variable, now adding the lower bound attribute
param m; param n; param l;
set I := 1 .. m;set J := 1 .. n;set K := 1 .. l;
param c{J}; param d{K};param a{I, J}; // ...
var x{J} integer, >= 0;var y{K} >= 0;
val m = param("m")val n = param("n")val l = param("l")
val I = set("I") := 1 to mval J = set("J") := 1 to n val K = set("K") := 1 to l
val c = param("c", J)val d = param("d", K)val a = param("a", I, J)// ...
val x = xvar("x", J).integer >= 0val y = xvar("y", K) >= 0
param m; param n; param l;
set I := 1 .. m;set J := 1 .. n;set K := 1 .. l;
param c{J}; param d{K};param a{I, J}; // ...
var x{J} integer, >= 0;var y{K} >= 0;
MathProg EDSL
Everything is immutable
MathProg EDSL
minimize f: sum{j in J} c[j] * x[j] + sum{k in K} d[k] * y[k];
s.t. g{i in I}: sum{j in J} a[i,j] * x[j] + sum{k in K} e[i,k] * y[k] <= b[i];
val f = minimize("f") { sum(j in J)(c(j) * x(j)) + sum(k in K)(d(k) * y(k)) } val g = st("g", i in I) { sum(j in J)(a(i, j) * x(j)) + sum(k in K)(e(i, k) * y(k)) <= b(i) }
MathProg EDSL minimize
st ...minimize f: sum{j in J} c[j] * x[j] + sum{k in K} d[k] * y[k];
s.t. g{i in I}: sum{j in J} a[i,j] * x[j] + sum{k in K} e[i,k] * y[k] <= b[i];
val f = minimize("f") { sum(j in J)(c(j) * x(j)) + sum(k in K)(d(k) * y(k)) } val g = st("g", i in I) { sum(j in J)(a(i, j) * x(j)) + sum(k in K)(e(i, k) * y(k)) <= b(i) }
minimize f: sum{j in J} c[j] * x[j] + sum{k in K} d[k] * y[k];
s.t. g{i in I}: sum{j in J} a[i,j] * x[j] + sum{k in K} e[i,k] * y[k] <= b[i];
val f = minimize("f") { sum(j in J)(c(j) * x(j)) + sum(k in K)(d(k) * y(k)) } val g = st("g", i in I) { sum(j in J)(a(i, j) * x(j)) + sum(k in K)(e(i, k) * y(k)) <= b(i) }
MathProg EDSLMultiple parameters lists
minimize f: sum{j in J} c[j] * x[j] + sum{k in K} d[k] * y[k];
s.t. g{i in I}: sum{j in J} a[i,j] * x[j] + sum{k in K} e[i,k] * y[k] <= b[i];
val f = minimize("f") { sum(j in J)(c(j) * x(j)) + sum(k in K)(d(k) * y(k)) } val g = st("g", i in I) { sum(j in J)(a(i, j) * x(j)) + sum(k in K)(e(i, k) * y(k)) <= b(i) }
MathProg EDSLMultiple parameters lists
minimize f: sum{j in J} c[j] * x[j] + sum{k in K} d[k] * y[k];
s.t. g{i in I}: sum{j in J} a[i,j] * x[j] + sum{k in K} e[i,k] * y[k] <= b[i];
val f = minimize("f") { sum(j in J)(c(j) * x(j)) + sum(k in K)(d(k) * y(k)) } val g = st("g", i in I) { sum(j in J)(a(i, j) * x(j)) + sum(k in K)(e(i, k) * y(k)) <= b(i) }
MathProg EDSLaddition,multiplication, summation ...
minimize f: sum{j in J} c[j] * x[j] + sum{k in K} d[k] * y[k];
s.t. g{i in I}: sum{j in J} a[i,j] * x[j] + sum{k in K} e[i,k] * y[k] <= b[i];
val f = minimize("f") { sum(j in J)(c(j) * x(j)) + sum(k in K)(d(k) * y(k)) } val g = st("g", i in I) { sum(j in J)(a(i, j) * x(j)) + sum(k in K)(e(i, k) * y(k)) <= b(i) }
MathProg EDSL References to individual parameters and variables by application
minimize f: sum{j in J} c[j] * x[j] + sum{k in K} d[k] * y[k];
s.t. g{i in I}: sum{j in J} a[i,j] * x[j] + sum{k in K} e[i,k] * y[k] <= b[i];
val f = minimize("f") { sum(j in J)(c(j) * x(j)) + sum(k in K)(d(k) * y(k)) } val g = st("g", i in I) { sum(j in J)(a(i, j) * x(j)) + sum(k in K)(e(i, k) * y(k)) <= b(i) }
MathProg EDSL
Constraint
minimize f: sum{j in J} c[j] * x[j] + sum{k in K} d[k] * y[k];
s.t. g{i in I}: sum{j in J} a[i,j] * x[j] + sum{k in K} e[i,k] * y[k] <= b[i];
val f = minimize("f") { sum(j in J)(c(j) * x(j)) + sum(k in K)(d(k) * y(k)) } val g = st("g", i in I) { sum(j in J)(a(i, j) * x(j)) + sum(k in K)(e(i, k) * y(k)) <= b(i) }
MathProg EDSL
Creates a new constraint with the specified name and indexing
Implementing the syntax
Implementing the syntax
Many of the syntactic constructs require:
A broad (open?) number of overloadings
Be used with infix notation
Many of the syntactic constructs require:
A broad (open?) number of overloadings
Be used with infix notation
Implementing the syntax
type classes and object-oriented forwarders
Many of the syntactic constructs require:
A broad (open?) number of overloadings
Be used with infix notation
Implementing the syntax
type classes and object-oriented forwarders
implicits prioritization
Implementing the syntax: “>=”
param("p", J) >= 0
Implementing the syntax: “>=”
param("p", J) >= 0
ParamStat
Implementing the syntax: “>=”
param("p", J) >= 0
ParamStat
ParamStat doesn’t have a `>=’ method
Implementing the syntax: “>=”
param("p", J) >= 0implicit conversion to something that provides the method
ParamStat doesn’t have a `>=’ method
ParamStat
Implementing the syntax: “>=”
param("p", J) >= 0 // ParamStatparam("p", J) >= p2 // ParamStat xvar("x", I) >= 0 // VarStat i >= j // LogicExpr p(j1) >= p(j2) // LogicExpr x(i) >= 0 // ConstraintStat 10 >= x(i) // ConstraintStat
Many overloadings
… more
Implementing the syntax: “>=”
implicit class GTESyntax[A](lhe: A) { def >=[B, C](rhe: B)(implicit GTEOp: GTEOp[A,B,C]): C = GTEOp.gte(lhe, rhe)}
Implementing the syntax: “>=”
implicit class GTESyntax[A](lhe: A) { def >=[B, C](rhe: B)(implicit GTEOp: GTEOp[A,B,C]): C = GTEOp.gte(lhe, rhe)}
Fully generic
Implementing the syntax: “>=”
implicit class GTESyntax[A](lhe: A) { def >=[B, C](rhe: B)(implicit GTEOp: GTEOp[A,B,C]): C = GTEOp.gte(lhe, rhe)}
trait GTEOp[A,B,C] { def gte(lhe: A, rhe: B): C}
Type class
Implementing the syntax: “>=”
implicit class GTESyntax[A](lhe: A) { def >=[B, C](rhe: B)(implicit GTEOp: GTEOp[A,B,C]): C = GTEOp.gte(lhe, rhe)}
trait GTEOp[A,B,C] { def gte(lhe: A, rhe: B): C}
Type classThe implementation is forwarded to the implicit instance
Implementing the syntax: “>=”
implicit class GTESyntax[A](lhe: A) { def >=[B, C](rhe: B)(implicit GTEOp: GTEOp[A,B,C]): C = GTEOp.gte(lhe, rhe)}
The available implicit instances encode the rules by which the operators can be used
Implementing the syntax: “>=”
implicit class GTESyntax[A](lhe: A) { def >=[B, C](rhe: B)(implicit GTEOp: GTEOp[A,B,C]): C = GTEOp.gte(lhe, rhe)}
The instance of the type class is required at the method level, so both A and B can be used in the implicits search
Implementing the syntax: “>=”instances
implicit def ParamStatGTEOp[A]( implicit conv: A => NumExpr): GTEOp[ParamStat, A, ParamStat] = /* ... */
>= for parameters and “numbers”param(“p”) >= m
Implementing the syntax: “>=”instances
implicit def ParamStatGTEOp[A]( implicit conv: A => NumExpr): GTEOp[ParamStat, A, ParamStat] = /* ... */
>= for parameters and “numbers”param(“p”) >= m
Implementing the syntax: “>=”instances
implicit def ParamStatGTEOp[A]( implicit conv: A => NumExpr): GTEOp[ParamStat, A, ParamStat] = /* ... */
>= for parameters and “numbers”param(“p”) >= m
View bound
Implementing the syntax: “>=”instances
implicit def VarStatGTEOp[A]( implicit conv: A => NumExpr): GTEOp[VarStat, A, VarStat] = /* ... */
>= for variables and “numbers”xvar(“x”) >= m
Analogous to the parameter case
Implementing the syntax: “>=”instances
implicit def NumExprGTEOp[A, B]( implicit convA: A => NumExpr, convB: B => NumExpr): GTEOp[A, B, LogicExpr] = /* ... */
>= for two “numbers”i >= j
Implementing the syntax: “>=”instances
implicit def NumExprGTEOp[A, B]( implicit convA: A => NumExpr, convB: B => NumExpr): GTEOp[A, B, LogicExpr] = /* ... */
>= for two “numbers”i >= j
Implementing the syntax: “>=”instances
implicit def NumExprGTEOp[A, B]( implicit convA: A => NumExpr, convB: B => NumExpr): GTEOp[A, B, LogicExpr] = /* ... */
>= for two “numbers”i >= j
Implementing the syntax: “>=”instances
implicit def LinExprGTEOp[A, B]( implicit convA: A => LinExpr, convB: B => LinExpr): GTEOp[A, B, ConstraintStat] = /* ... */
>= for two “linear expressions”x(i) >= 0 and 10 >= x(i)
Analogous to the numerical expression case
implicit def NumExprGTEOp[A, B]( implicit convA: A => NumExpr, convB: B => NumExpr)
conflicts with: implicit def ParamStatGTEOp[A]( implicit conv: A => NumExpr)
implicit def LinExprGTEOp[A, B]( implicit convA: A => LinExpr, convB: B => LinExpr)
Instances are ambiguous
implicit def NumExprGTEOp[A, B]( implicit convA: A => NumExpr, convB: B => NumExpr)
conflicts with: implicit def ParamStatGTEOp[A]( implicit conv: A => NumExpr)
implicit def LinExprGTEOp[A, B]( implicit convA: A => LinExpr, convB: B => LinExpr)
Instances are ambiguous
`ParamStat <% NumExpr‘to allow `param(“p2”) >= p1’
Instances are ambiguousimplicit def NumExprGTEOp[A, B]( implicit convA: A => NumExpr, convB: B => NumExpr)
conflicts with: implicit def ParamStatGTEOp[A]( implicit conv: A => NumExpr)
implicit def LinExprGTEOp[A, B]( implicit convA: A => LinExpr, convB: B => LinExpr)
NumExpr <: LinExpr => NumExpr <% LinExpr
Implicits prioritizationtrait GTEInstances extends GTEInstancesLowPriority1 { implicit def ParamStatGTEOp[A](implicit conv: A => NumExpr) /* */ implicit def VarStatGTEOp[A](implicit conv: A => NumExpr) /* */} trait GTEInstancesLowPriority1 extends GTEInstancesLowPriority2 { implicit def NumExprGTEOp[A, B]( implicit convA: A => NumExpr, convB: B => NumExpr) /* */} trait GTEInstancesLowPriority2 { implicit def LinExprGTEOp[A, B]( implicit convA: A => LinExpr, convB: B => LinExpr) /* */}
Implicits prioritizationtrait GTEInstances extends GTEInstancesLowPriority1 { implicit def ParamStatGTEOp[A](implicit conv: A => NumExpr) /* */ implicit def VarStatGTEOp[A](implicit conv: A => NumExpr) /* */} trait GTEInstancesLowPriority1 extends GTEInstancesLowPriority2 { implicit def NumExprGTEOp[A, B]( implicit convA: A => NumExpr, convB: B => NumExpr) /* */} trait GTEInstancesLowPriority2 { implicit def LinExprGTEOp[A, B]( implicit convA: A => LinExpr, convB: B => LinExpr) /* */}
> priority
> priority
Next steps
New semantics
More static controls
Support other kind of problems beyond MIP
Talk directly to solvers
Arity, scope, ...
Multi-stage stochastic programming
import amphip.dsl._import amphip.dsl._
amphip is a collection of experiments around manipulating MathProg models within Scala
github.com/gerferra/amphip
amphip is a collection of experiments around manipulating MathProg models within Scala
github.com/gerferra/amphip
FINgithub.com/gerferra/amphip