Skip to content

Instantly share code, notes, and snippets.

@odow
Last active April 4, 2017 02:47
Show Gist options
  • Save odow/620d4f28dc58fe43c4c2a7fa53207ebd to your computer and use it in GitHub Desktop.
Save odow/620d4f28dc58fe43c4c2a7fa53207ebd to your computer and use it in GitHub Desktop.
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