An embedded DSL to manipulate MathProg Mixed Integer Programming models within Scala

  • View
    1.083

  • Download
    0

  • Category

    Software

Preview:

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