Last active
April 4, 2017 02:47
-
-
Save odow/620d4f28dc58fe43c4c2a7fa53207ebd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using JuMP | |
abstract VariableDomain | |
immutable Continuous <: VariableDomain | |
lower::Float64 | |
upper::Float64 | |
end | |
Continuous() = Continuous(-Inf, Inf) | |
Continuous(;lowerbound=-Inf, upperbound=Inf) = Continuous(lowerbound, upperbound) | |
function setdomain!(m::JuMP.Model, x, c::Continuous) | |
m.colLower[x.col] = max(m.colLower[x.col], c.lower) | |
m.colUpper[x.col] = min(m.colUpper[x.col], c.upper) | |
end | |
function setdomain!(m::JuMP.Model, x::JuMP.Variable, domain::Tuple) | |
for d in domain | |
setdomain!(m, x, d) | |
end | |
end | |
immutable Binary <: VariableDomain end | |
function setdomain!(m::JuMP.Model, x, ::Binary) | |
m.colCat[x.col] = :Bin | |
end | |
immutable Integers <: VariableDomain | |
lower::Float64 | |
upper::Float64 | |
end | |
Integers() = Integers(-Inf, Inf) | |
Integers(;lowerbound=-Inf, upperbound=Inf) = Integers(lowerbound, upperbound) | |
function setdomain!(m::JuMP.Model, x, int::Integers) | |
m.colCat[x.col] = :Int | |
m.colLower[x.col] = max(m.colLower[x.col], int.lower) | |
m.colUpper[x.col] = min(m.colUpper[x.col], int.upper) | |
end | |
function setdomain!{T<:Real}(m::JuMP.Model, x, d::Vector{T}) | |
@assert length(d) == 2 | |
m.colLower[x.col] = d[1] | |
m.colUpper[x.col] = d[2] | |
end | |
# User has previously defined | |
immutable OddIntegers <: VariableDomain end | |
function setdomain!(m, x, d::OddIntegers) | |
y = @variable(m, Integer) | |
@constraint(m, x == 2y + 1) | |
nothing | |
end | |
function applycomparison(sym, value) | |
lowerbound = -Inf | |
upperbound = Inf | |
value = esc(value) | |
if sym == :(>=) | |
lowerbound = value | |
elseif sym == :(<=) | |
upperbound = value | |
elseif sym == :(==) | |
upperbound = value | |
lowerbound = value | |
else | |
error("Comparison Symbol not recognised: $(sym).") | |
end | |
(lowerbound, upperbound) | |
end | |
_getname(c::Symbol) = c | |
_getname(c::Void) = () | |
_getname(c::AbstractString) = c | |
function _getname(ex::Expr) | |
# ex = x[i] >= l in D | |
if ex.head == :string | |
return ex | |
elseif ex.head == :comparison | |
return ex.args[1] | |
elseif ex.head == :call | |
return ex.args[2] | |
else | |
error("Unable to get name from from $ex") | |
end | |
end | |
function getbounds(ex::Expr) | |
# ex = x[i] >= l in D | |
if ex.head == :comparison | |
return applycomparison(ex.args[2], ex.args[3]) | |
elseif ex.head == :call && ex.args[1] != :in | |
return applycomparison(ex.args[1], ex.args[3]) | |
else | |
return (-Inf, Inf) | |
end | |
end | |
function getdomain(ex::Expr) | |
if ex.head == :comparison && length(ex.args) == 5 | |
return esc(ex.args[5]) | |
elseif ex.head == :call && ex.args[1] == :in | |
return esc(ex.args[3]) | |
else | |
return :(Continuous()) | |
end | |
end | |
function _addsingletonvariable!(m, ex) | |
# assumes m is escaped | |
_var = _getname(ex) | |
quot_name = Base.Meta.quot(_getname(_var)) | |
esc_var_name = esc(_getname(_var)) | |
variable = gensym() | |
domain = getdomain(ex) | |
(lowerbound, upperbound) = getbounds(ex) | |
quote | |
$variable = JuMP.Variable($m, $lowerbound, $upperbound, :Cont, string($quot_name)) | |
setdomain!($m, $variable, $domain) | |
push!($(m).dictList, $variable) | |
JuMP.registervar($m, $quot_name, $variable) | |
$esc_var_name = $variable | |
end | |
end | |
function generatedvariable!(m, ex) | |
# assumes m is escaped | |
_var = _getname(ex.args[1]) | |
quot_name = Base.Meta.quot(_getname(_var)) | |
esc_var_name = esc(_getname(_var)) | |
variable = gensym() | |
code = quote | |
$variable = Array(JuMP.Variable, size($generator)) | |
end | |
domain = getdomain(ex) | |
(lowerbound, upperbound) = getbounds(ex) | |
$variable[tup...] = JuMP.Variable($m, $lowerbound, $upperbound, :Cont, JuMP.EMPTYSTRING, NaN) | |
JuMP.registervar($m, $quot_name, $variable) | |
$esc_var_name = $variable | |
end | |
end | |
macro var(m, ex) | |
m = esc(m) | |
if ex.head == :comparison || ex.head == :call | |
# x >= l in D | |
return _addsingletonvariable!(m, ex) | |
# elseif ex.head == :generator | |
# x[i] >= l[i] in D[i] for i in R if cond(i) | |
# @assert length(ex.args) == 2 | |
# _addsingletonvariable!(code, m, ex) | |
end | |
end | |
m=Model() | |
@var(m, x in (Integers(), [2,4])) | |
macroexpand(:(@var(m, x in (Integers(), [2,4])))) | |
@variables(m, begin | |
# continuous in (-∞, ∞) | |
x | |
x[i] for i in S if cond(i) | |
# continuous in [l, ∞) | |
x >= l | |
x[i] >= l[i] for i in S if cond(i) | |
# continuous in (-∞, u] | |
x <= u | |
x[i] <= u[i] for i in S if cond(i) | |
# continuous in [l, u] | |
x in [l, u] | |
x[i] in [l[i], u[i]] for i in S if cond(i) | |
# Binary | |
x in {0, 1} # sugar for Binary | |
x[i] in {0, 1} for i in S if cond(i) | |
domains = [Binary, Binary] | |
x[i] in T for (i, T) in enumerate(domains) if cond(i) | |
# x in [l, l+1, ...] | |
x >= l in Integers() | |
x[i] >= l in Integer() for i in S if cond(i) | |
x[i] in ([l, u], Integer()) for i in S if cond(i) | |
domains = [Integer, Integer(l,u)] | |
x[i] in T for (i, T) in enumerate(domains) if cond(i) | |
# concatenate domains in a tuple | |
# x is in intersection of domains | |
x in [l, u], Integer | |
domains = [ | |
(Integer, NonPositive), | |
(Integer, SemiContinuous(l, u)), | |
{0} | [2,3] # sugar for => SemiContinuous(2,3) | |
] | |
x[i] in D for (i, D) in enumerate(domains) if cond(i) | |
# keywords are wrapped in a tuple as the first argument (i.e. away from the domains and generator syntax) | |
(x[i], start=i, name="Var$i") >= l[i] in Domain for i in S if cond(i) | |
end) | |
# Anonymous Variables | |
@variable(m) | |
@variable(m, (lowerbound=l) for i in S if cond(i)) | |
@variable(m, () in Domain for i in S if cond(i)) | |
@variable(m, (lowerbound=i, name="Anon$i") in Domain for i in S if cond(i)) | |
@variable(x in OddIntegers(because_i_can=true)) | |
# general syntax | |
# (variable symbol; kwargs...) [comparison: >= l] [domain: in Domain] [generator: for i in S] | |
# constraint sets | |
@constraint m, x >= 0, cone=:S, param=1 | |
@constraint m, x >= 0, cone=S(param=1) | |
@constraint m, x in S(param=1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment