Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expression testing and clean ups #154

Merged
merged 13 commits into from
Oct 16, 2020
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ os:
- osx
julia:
- 1.0
- 1
- 1 # Most recent v > 1.0
before_script:
- julia -e 'using Pkg; Pkg.add(PackageSpec(name="Pavito", rev="master"))' # TODO remove when Pavito#master is released.
- julia -e 'using Pkg; Pkg.add(PackageSpec(name="Pavito", rev="master"))' # TODO remove when Pavito #master is released.
harshangrjn marked this conversation as resolved.
Show resolved Hide resolved
jobs:
allow_failures:
- julia: nightly
Expand All @@ -16,7 +16,7 @@ jobs:
julia: 1
os: linux
script:
- julia -e 'using Pkg; Pkg.rm("Pavito")' # TODO Pkg seems to get into troubles because of the `before_script`. Remove when Pavito#master is released.
- julia -e 'using Pkg; Pkg.rm("Pavito")' # TODO Pkg seems to get into troubles because of the `before_script`. Remove when Pavito #master is released.
harshangrjn marked this conversation as resolved.
Show resolved Hide resolved
- julia --project=docs/ -e 'using Pkg; Pkg.instantiate(); Pkg.develop(PackageSpec(path=pwd()))'
- julia --project=docs/ docs/make.jl
after_success: skip
6 changes: 4 additions & 2 deletions src/algorithm.jl
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ function check_exit(m::Optimizer)

# constant objective with feasible local solve check
if expr_isconst(m.obj_expr_orig) && (m.status[:local_solve] == MOI.OPTIMAL || m.status == MOI.LOCALLY_SOLVED)
m.best_bound = eval(m.obj_expr_orig)
# m.best_bound = eval(m.obj_expr_orig)
m.best_bound = m.obj_expr_orig
m.best_rel_gap = 0.0
m.best_abs_gap = 0.0
m.status[:bounding_solve] = MOI.OPTIMAL
Expand Down Expand Up @@ -371,7 +372,8 @@ For advanced usage, `get_option(m, :disc_var_pick)` allows `::Function` inputs.
function pick_disc_vars(m::Optimizer)

if isa(get_option(m, :disc_var_pick), Function)
eval(get_option(m, :disc_var_pick))(m)
# eval(get_option(m, :disc_var_pick))(m)
get_option(m, :disc_var_pick)(m)
length(m.disc_vars) == 0 && length(m.nonconvex_terms) > 0 && error("[USER FUNCTION] must select at least one variable to perform discretization for convexificiation purpose")
elseif isa(get_option(m, :disc_var_pick), Int)
if get_option(m, :disc_var_pick) == 0
Expand Down
6 changes: 4 additions & 2 deletions src/amp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ function amp_post_convexification(m::Optimizer; use_disc=nothing)
use_disc == nothing ? discretization = m.discretization : discretization = use_disc

for i in 1:length(get_option(m, :method_convexification)) # Additional user-defined convexification method
eval(get_option(m, :method_convexification)[i])(m)
# eval(get_option(m, :method_convexification)[i])(m)
get_option(m, :method_convexification)[i](m)
end

amp_post_mccormick(m, use_disc=discretization) # handles all bi-linear and monomial convexificaitons
Expand Down Expand Up @@ -195,7 +196,8 @@ function add_partition(m::Optimizer; kwargs...)
haskey(options, :use_solution) ? point_vec = options[:use_solution] : point_vec = m.best_bound_sol

if isa(get_option(m, :disc_add_partition_method), Function)
m.discretization = eval(get_option(m, :disc_add_partition_method))(m, use_disc=discretization, use_solution=point_vec)
# m.discretization = eval(get_option(m, :disc_add_partition_method))(m, use_disc=discretization, use_solution=point_vec)
m.discretization = get_option(m, :disc_add_partition_method)(m, use_disc=discretization, use_solution=point_vec)
elseif get_option(m, :disc_add_partition_method) == "adaptive"
m.discretization = add_adaptive_partition(m, use_disc=discretization, use_solution=point_vec)
elseif get_option(m, :disc_add_partition_method) == "uniform"
Expand Down
4 changes: 2 additions & 2 deletions src/bounds.jl
Original file line number Diff line number Diff line change
Expand Up @@ -337,8 +337,8 @@ function resolve_var_bounds(m::Optimizer, d::Dict; kwargs...)
d = basic_binint_bounds(m, nlk, d)
elseif m.nonconvex_terms[nlk][:nonlinear_type] in [:BINPROD]
d = basic_binprod_bounds(m, nlk, d)
elseif m.nonconvex_terms[nlk][:nonlinear_type] in ALPINE_C_TRIGONOMETRIC
d = basic_sincos_bounds(m, nlk, d)
# elseif m.nonconvex_terms[nlk][:nonlinear_type] in ALPINE_C_TRIGONOMETRIC
# d = basic_sincos_bounds(m, nlk, d)
elseif m.nonconvex_terms[nlk][:nonlinear_type] in [:BINLIN]
d = basic_binlin_bounds(m, nlk, d)
elseif m.nonconvex_terms[nlk][:nonlinear_type] in [:INTLIN]
Expand Down
4 changes: 2 additions & 2 deletions src/log.jl
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ function logging_row_entry(m::Optimizer; kwargs...)


if expr_isconst(m.obj_expr_orig)
bdstr = eval(m.obj_expr_orig)
spc = b_len - length(bdstr)
# bdstr = eval(m.obj_expr_orig)
spc = b_len - length(m.obj_expr_orig)
elseif isa(m.logs[:bound][end], Float64)
bdstr = string(round(m.logs[:bound][end]; digits=4))
spc = max(0, b_len - length(bdstr))
Expand Down
36 changes: 18 additions & 18 deletions src/multi.jl
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ function amp_warmstart_α(m::Optimizer, α::Dict)
partition_cnt = length(d[v])-1
active_j = get_active_partition_idx(d, m.bound_sol_pool[:sol][ws_idx][v], v)
for j = 1:partition_cnt
j == active_j ? setvalue(α[v][j], 1.0) : setvalue(α[v][j], 0.0)
j == active_j ? set_start_value(α[v][j], 1.0) : set_start_value(α[v][j], 0.0)
end
end
m.bound_sol_pool[:stat][ws_idx] = :Warmstarter
Expand Down Expand Up @@ -441,7 +441,7 @@ end

"""
[Experimental Function]
This is a utility function that used to measure the coefficients for formulations that convexify terms with integer variables.
This is a utility function that can be used to measure the coefficients for formulations that convexify terms with integer variables.
"""
function amp_pick_ratevec(partvec::Vector, i::Int)

Expand Down Expand Up @@ -518,22 +518,6 @@ function amp_post_λ_upperbound(m::Optimizer, λ::Dict, indices::Any, ub::Float6
return
end

function collect_indices(l::Array, locator::Tuple, dim::Tuple)

k = 0
indices = Vector{Int}(2^length(dim))
for i in 1:prod(dim)
ind = Tuple(CartesianIndices(l)[i])
diff = [((ind[i] - locator[i] == 0) || (ind[i] - locator[i] == 1)) for i in 1:length(dim)]
if prod(diff)
k +=1
indices[k] = i
end
end

return indices
end

function collect_indices(l::Array, fixed_dim::Int, fixed_partition::Array, dim::Tuple)

k = 0
Expand All @@ -548,3 +532,19 @@ function collect_indices(l::Array, fixed_dim::Int, fixed_partition::Array, dim::

return indices
end

# function collect_indices(l::Array, locator::Tuple, dim::Tuple)

# k = 0
# indices = Vector{Int}(2^length(dim))
# for i in 1:prod(dim)
# ind = Tuple(CartesianIndices(l)[i])
# diff = [((ind[i] - locator[i] == 0) || (ind[i] - locator[i] == 1)) for i in 1:length(dim)]
# if prod(diff)
# k +=1
# indices[k] = i
# end
# end

# return indices
# end
4 changes: 2 additions & 2 deletions src/nlexpr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -542,11 +542,11 @@ function expr_resolve_const(expr)

isa(expr, Number) && return
for i in 1:length(expr.args)
if isa(expr.args[i], Float64) || isa(expr.args[i], Int) || isa(expr.args[i], Symbol)
if isa(expr.args[i], Number) || isa(expr.args[i], Symbol)
continue
elseif expr.args[i].head == :call
(expr_isconst(expr.args[i])) && (expr.args[i] = eval(expr.args[i]))
if isa(expr.args[i], Float64) || isa(expr.args[i], Int) || isa(expr.args[i], Symbol)
if isa(expr.args[i], Number) || isa(expr.args[i], Symbol)
continue
else
expr_resolve_const(expr.args[i])
Expand Down
143 changes: 72 additions & 71 deletions src/operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ function detect_nonconvex_terms(expr::Any, constr_id::Int, m::Optimizer; kwargs.
skip, expr = detect_multilinear_term(expr, constr_id, m) #L2
skip && return expr

skip, expr = detect_sincos_term(expr, constr_id, m) #L2
skip && return expr
# skip, expr = detect_sincos_term(expr, constr_id, m) #L2
# skip && return expr

return expr # if no structure is detected, simply return the original tree
end
Expand Down Expand Up @@ -275,7 +275,7 @@ intlin(k,vec) = prod([vec[i] for i in k[:var_idxs]])
bilinear(k,vec) = prod([vec[i] for i in k[:var_idxs]])
multilinear(k,vec) = prod([vec[i] for i in k[:var_idxs]])
monomial(k, vec) = vec[k[:var_idxs][1]]^2
sincos(k, vec) = eval(k[:nonlinear_type])(vec[k[:var_idxs][1]])
# sincos(k, vec) = eval(k[:nonlinear_type])(vec[k[:var_idxs][1]])
linear(k, vec) = k[:ref][:scalar] .+ sum([i[1]*vec[i[2]] for i in k[:ref][:coef_var]])

"""
Expand Down Expand Up @@ -1085,74 +1085,6 @@ function collect_monomial_discvar(m::Optimizer, k::Any; var_bowl=nothing)
return
end

"""
Recognize sin/cos terms: sin(x) / cos(x), where x "should" be continous variables
# TODO future call detect_TRIGONOMETRIC_term
"""
function detect_sincos_term(expr::Any, constr_id::Int, m::Optimizer)

@assert (expr.head == :call || expr.head == :ref)

if expr.args[1] in [:sin, :cos]
# Pattern: sin(a*x) or cos(a*x)
operator = expr.args[1]
scalar = 1.0
var_idxs = []
for i in 1:length(expr.args)
if isa(expr.args[i], Float64) || isa(expr.args[i], Int)
scalar *= expr.args[i]
continue
end
isa(expr.args[i], Symbol) && continue
(expr.args[i].head == :ref) && isa(expr.args[i].args[2], Int) && push!(var_idxs, expr.args[i].args[2])
if (expr.args[i].head == :call)
down_check, linear_lift_var = detect_linear_term(expr.args[i], constr_id, m)
!down_check && return false, expr
push!(var_idxs, linear_lift_var.args[2])
continue
end
end
if length(var_idxs) == 1
term_key = Dict(:operator=>operator, :scalar=>scalar, :vars=>var_idxs)
term_key in keys(m.nonconvex_terms) || store_nonconvex_term(m, term_key, var_idxs, term_key[:operator], term_key[:operator], sincos, basic_sincos_bounds, collect_sincos_discvar)
return true, lift_nonconvex_term(m, term_key, constr_id, scalar)
end
end

return false, expr
end

function basic_sincos_bounds(m::Optimizer, k::Any)

lifted_idx = m.nonconvex_terms[k][:lifted_var_ref].args[2]
m.l_var_tight[lifted_idx] = -1 # TODO can be improved
m.u_var_tight[lifted_idx] = 1

return
end

function basic_sincos_bounds(m::Optimizer, k::Any, d::Dict)

lifted_idx = m.nonconvex_terms[k][:lifted_var_ref].args[2]
d[lifted_idx][1] = -1 # TODO can be improved
d[lifted_idx][end] = 1

return d
end


function collect_sincos_discvar(m::Optimizer, k::Any; var_bowl=nothing)
for var in m.nonconvex_terms[k][:var_idxs]
@assert isa(var, Int)
if var_bowl == nothing
var in m.candidate_disc_vars || push!(m.candidate_disc_vars, var)
else
var in var_bowl || push!(var_bowl, var)
end
end
return
end

"""
Recognize convex constraints
A catch for type-A convex constraint expression
Expand Down Expand Up @@ -1344,3 +1276,72 @@ function resolve_convex_constr(expr::Any, m::Optimizer=nothing, idx::Int=0, scal

return false
end

"""
Recognize sin/cos terms: sin(x) / cos(x), where x "should" be continous variables
# TODO future call detect_TRIGONOMETRIC_term
# Deactivitating this part of the code until complete support for trigonometric functions are added
"""
# function detect_sincos_term(expr::Any, constr_id::Int, m::Optimizer)

# @assert (expr.head == :call || expr.head == :ref)

# if expr.args[1] in [:sin, :cos]
# # Pattern: sin(a*x) or cos(a*x)
# operator = expr.args[1]
# scalar = 1.0
# var_idxs = []
# for i in 1:length(expr.args)
# if isa(expr.args[i], Float64) || isa(expr.args[i], Int)
# scalar *= expr.args[i]
# continue
# end
# isa(expr.args[i], Symbol) && continue
# (expr.args[i].head == :ref) && isa(expr.args[i].args[2], Int) && push!(var_idxs, expr.args[i].args[2])
# if (expr.args[i].head == :call)
# down_check, linear_lift_var = detect_linear_term(expr.args[i], constr_id, m)
# !down_check && return false, expr
# push!(var_idxs, linear_lift_var.args[2])
# continue
# end
# end
# if length(var_idxs) == 1
# term_key = Dict(:operator=>operator, :scalar=>scalar, :vars=>var_idxs)
# term_key in keys(m.nonconvex_terms) || store_nonconvex_term(m, term_key, var_idxs, term_key[:operator], term_key[:operator], sincos, basic_sincos_bounds, collect_sincos_discvar)
# return true, lift_nonconvex_term(m, term_key, constr_id, scalar)
# end
# end

# return false, expr
# end

# function basic_sincos_bounds(m::Optimizer, k::Any)

# lifted_idx = m.nonconvex_terms[k][:lifted_var_ref].args[2]
# m.l_var_tight[lifted_idx] = -1 # TODO can be improved
# m.u_var_tight[lifted_idx] = 1

# return
# end

# function basic_sincos_bounds(m::Optimizer, k::Any, d::Dict)

# lifted_idx = m.nonconvex_terms[k][:lifted_var_ref].args[2]
# d[lifted_idx][1] = -1 # TODO can be improved
# d[lifted_idx][end] = 1

# return d
# end


# function collect_sincos_discvar(m::Optimizer, k::Any; var_bowl=nothing)
# for var in m.nonconvex_terms[k][:var_idxs]
# @assert isa(var, Int)
# if var_bowl == nothing
# var in m.candidate_disc_vars || push!(m.candidate_disc_vars, var)
# else
# var in var_bowl || push!(var_bowl, var)
# end
# end
# return
# end
9 changes: 6 additions & 3 deletions src/presolve.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ function bound_tightening(m::Optimizer; use_bound = true, kwargs...)
elseif get_option(m, :presolve_bt_algo) == 2
minmax_bound_tightening(m, use_bound=use_bound, use_tmc=true)
elseif isa(get_option(m, :presolve_bt_algo), Function)
eval(get_option(m, :presolve_bt_algo))(m)
# eval(get_option(m, :presolve_bt_algo))(m)
get_option(m, :presolve_bt_algo)(m)
else
error("Unrecognized optimization-based bound tightening algorithm")
end
Expand Down Expand Up @@ -98,9 +99,11 @@ function minmax_bound_tightening(m::Optimizer; use_bound = true, timelimit = Inf
@objective(m.model_mip, sense, _index_to_variable_ref(m.model_mip, var_idx))
status = solve_bound_tightening_model(m)
if status in STATUS_OPT
temp_bounds[var_idx][tell_side[sense]] = eval(tell_round[sense])(JuMP.objective_value(m.model_mip)/get_option(m, :presolve_bt_output_tol))*get_option(m, :presolve_bt_output_tol) # Objective truncation for numerical issues
# temp_bounds[var_idx][tell_side[sense]] = eval(tell_round[sense])(JuMP.objective_value(m.model_mip)/get_option(m, :presolve_bt_output_tol))*get_option(m, :presolve_bt_output_tol) # Objective truncation for numerical issues
temp_bounds[var_idx][tell_side[sense]] = tell_round[sense](JuMP.objective_value(m.model_mip)/get_option(m, :presolve_bt_output_tol))*get_option(m, :presolve_bt_output_tol) # Objective truncation for numerical issues
elseif status in STATUS_LIMIT
temp_bounds[var_idx][tell_side[sense]] = eval(tell_round[sense])(JuMP.objective_bound(m.model_mip)/get_option(m, :presolve_bt_output_tol))*get_option(m, :presolve_bt_output_tol)
# temp_bounds[var_idx][tell_side[sense]] = eval(tell_round[sense])(JuMP.objective_bound(m.model_mip)/get_option(m, :presolve_bt_output_tol))*get_option(m, :presolve_bt_output_tol)
temp_bounds[var_idx][tell_side[sense]] = tell_round[sense](JuMP.objective_bound(m.model_mip)/get_option(m, :presolve_bt_output_tol))*get_option(m, :presolve_bt_output_tol)
else
print("!")
end
Expand Down
2 changes: 1 addition & 1 deletion src/utility.jl
Original file line number Diff line number Diff line change
Expand Up @@ -800,7 +800,7 @@ function fetch_minlp_solver_identifier(m::Optimizer;override="")
elseif occursin("Juniper", solverstring)
m.minlp_solver_id = "Juniper"
else
error("Unsupported MINLP local solver $solverstring; use a Alpine-supported MINLP local solver")
error("Unsupported MINLP local solver $solverstring; use an Alpine-supported MINLP local solver (Juniper.jl for example)")
end

return
Expand Down
Loading