Skip to content

Commit

Permalink
Merge pull request #168 from lanl-ansi/updated_tests_for_codecov
Browse files Browse the repository at this point in the history
More unit tests, drop un-used functions
  • Loading branch information
harshangrjn authored May 21, 2021
2 parents cd0d55b + af684c1 commit b59bc27
Show file tree
Hide file tree
Showing 10 changed files with 352 additions and 254 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Critical Updates for Alpine.jl

## v0.2.4
* Added unit tests for utility functions
* Removed un-used functions from utility functions which aren't part of Alpine's algorithm
* Dropped support for heuristic rounding algorithm (`heu_basic_rounding`) - `minlp_solver` input is a must
* Dropped support for adjusting branching priority in MIP solvers (`adjust_branch_priority`)


## v0.2.3
* Migrating from Travis to Github-actions
* Major documentation clean-up
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name = "Alpine"
uuid = "07493b3f-dabb-5b16-a503-4139292d7dd4"
authors = ["Harsha Nagarajan, Site Wang, Kaarthik Sundar and contributors"]
repo = "https://github.com/lanl-ansi/Alpine.jl.git"
version = "0.2.3"
version = "0.2.4"

[deps]
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
Expand Down
50 changes: 35 additions & 15 deletions src/algorithm.jl
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ function load_nonlinear_model(m::Optimizer, model::MOI.ModelLike, l_var, u_var)
MOI.set(model, MOI.NLPBlock(), block)
return x
end

function set_variable_type(model::MOI.ModelLike, xs, variable_types)
for (x, variable_type) in zip(xs, variable_types)
fx = MOI.SingleVariable(x)
Expand Down Expand Up @@ -244,38 +245,46 @@ function local_solve(m::Optimizer; presolve = false)
end
MOI.set(local_solve_model, MOI.VariablePrimalStart(), x, warmval)

do_heuristic = false
# do_heuristic = false

# The only case when MINLP solver is actually used
if presolve && !isempty(var_type_screener)
if get_option(m, :minlp_solver) === nothing
do_heuristic = true
error("Provide a valid MINLP solver")
# do_heuristic = true
else
set_variable_type(local_solve_model, x, m.var_type_orig)
end
end

MOI.optimize!(local_solve_model)
if !do_heuristic
local_nlp_status = MOI.get(local_solve_model, MOI.TerminationStatus())
end
local_nlp_status = MOI.get(local_solve_model, MOI.TerminationStatus())

# if !do_heuristic
# local_nlp_status = MOI.get(local_solve_model, MOI.TerminationStatus())
# end

cputime_local_solve = time() - start_local_solve
m.logs[:total_time] += cputime_local_solve
m.logs[:time_left] = max(0.0, get_option(m, :time_limit) - m.logs[:total_time])

if do_heuristic
m.status[:local_solve] = heu_basic_rounding(m, MOI.get(local_solve_model, MOI.VariablePrimal(), x))
return
elseif local_nlp_status in STATUS_OPT || local_nlp_status in STATUS_LIMIT
# if do_heuristic
# m.status[:local_solve] = heu_basic_rounding(m, MOI.get(local_solve_model, MOI.VariablePrimal(), x))
# return

if local_nlp_status in STATUS_OPT || local_nlp_status in STATUS_LIMIT
candidate_obj = MOI.get(local_solve_model, MOI.ObjectiveValue())
candidate_sol = round.(MOI.get(local_solve_model, MOI.VariablePrimal(), x); digits=5)
update_incumb_objective(m, candidate_obj, candidate_sol)
m.status[:local_solve] = local_nlp_status
return

elseif local_nlp_status in STATUS_INF
heu_pool_multistart(m) == MOI.LOCALLY_SOLVED && return
push!(m.logs[:obj], "INF")
m.status[:local_solve] = MOI.LOCALLY_INFEASIBLE
return

elseif local_nlp_status == MOI.DUAL_INFEASIBLE
push!(m.logs[:obj], "U")
m.status[:local_solve] = MOI.DUAL_INFEASIBLE
Expand All @@ -285,6 +294,7 @@ function local_solve(m::Optimizer; presolve = false)
@warn " Warning: NLP local solve is unbounded."
end
return

else
push!(m.logs[:obj], "E")
m.status[:local_solve] = MOI.OTHER_ERROR
Expand All @@ -303,11 +313,9 @@ end
"""
bounding_solve(m::Optimizer; kwargs...)
This process usually deals with a MILP or a MIQCP/MIQCQP problem for lower bounding the given problem.
It solves the problem built upon a convexification base on a discretization Dictionary of some variables.
The convexification utilized is Tighten McCormick scheme.
This step usually solves a convex MILP/MIQCP/MIQCQP problem for lower bounding the given minimization problem.
It solves the problem built upon a piecewise convexification based on the discretization sictionary of some variables.
See `create_bounding_mip` for more details of the problem solved here.
"""
function bounding_solve(m::Optimizer)

Expand All @@ -326,8 +334,10 @@ function bounding_solve(m::Optimizer)
# ================= Solve End ================ #

if status in STATUS_OPT || status in STATUS_LIMIT

candidate_bound = (status == MOI.OPTIMAL) ? objective_value(m.model_mip) : objective_bound(m.model_mip)
candidate_bound_sol = [round.(JuMP.value(_index_to_variable_ref(m.model_mip, i)); digits=6) for i in 1:(m.num_var_orig+m.num_var_linear_mip+m.num_var_nonlinear_mip)]

# Experimental code
measure_relaxed_deviation(m, sol=candidate_bound_sol)
if get_option(m, :disc_consecutive_forbid) > 0
Expand All @@ -340,16 +350,26 @@ function bounding_solve(m::Optimizer)
m.status[:bounding_solve] = status
m.detected_bound = true
end
collect_lb_pool(m) # Always collect details sub-optimal solution

collect_lb_pool(m) # Collect a pool of sub-optimal solutions - currently implemented for Gurobi only

elseif status in STATUS_INF || status == MOI.INFEASIBLE_OR_UNBOUNDED

push!(m.logs[:bound], "-")
m.status[:bounding_solve] = MOI.INFEASIBLE
ALPINE_DEBUG && print_iis_gurobi(m.model_mip) # Diagnostic code
@warn " Warning: Infeasibility detected in the MIP solver"

if ALPINE_DEBUG
@warn "Use Alpine.print_iis_gurobi(m.model_mip) function in src/utility.jl (commented out code) for further investigation, if your MIP solver is Gurobi"
end

elseif status == :Unbounded

m.status[:bounding_solve] = MOI.DUAL_INFEASIBLE
@warn " Warning: MIP solver returns unbounded"

else

error(" Warning: MIP solver failure $(status)")
end

Expand Down
75 changes: 40 additions & 35 deletions src/heuristics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -53,41 +53,6 @@ function update_disc_int_var(m::Optimizer)
return
end

"""
One-time rounding heuristic to obtain a feasible solution
For integer solutions
"""
function heu_basic_rounding(m::Optimizer, relaxed_sol)

println("Basic Rounding Heuristic Activated...")

convertor = Dict(MOI.MAX_SENSE => :>, MOI.MIN_SENSE => :<)

rounded_sol = round_sol(m, relaxed_sol)
l_var, u_var = fix_domains(m, discrete_sol = rounded_sol)

heuristic_model = MOI.instantiate(get_option(m, :nlp_solver), with_bridge_type=Float64)
x = load_nonlinear_model(m, heuristic_model, l_var, u_var)
MOI.optimize!(heuristic_model)
heuristic_model_status = MOI.get(heuristic_model, MOI.TerminationStatus())

if heuristic_model_status == MOI.OTHER_ERROR || heuristic_model_status in STATUS_INF
get_option(m, :log_level) > 0 && println("Rounding obtained an Infeasible point.")
push!(m.logs[:obj], "INF")
return MOI.LOCALLY_INFEASIBLE
elseif heuristic_model_status in STATUS_OPT || heuristic_model_status in STATUS_LIMIT
candidate_obj = MOI.get(heuristic_model, MOI.ObjectiveValue())
candidate_sol = round.(MOI.get(heuristic_model, MOI.VariablePrimal(), x), 5)
update_incumb_objective(m, candidate_obj, candidate_sol)
get_option(m, :log_level) > 0 && println("Rounding obtained a feasible solution OBJ = $(m.best_obj)")
return MOI.LOCALLY_SOLVED
else
error("[EXCEPTION] Unknown NLP solver status.")
end

return
end

"""
Use solutions from the MIP solution pool as starting points
"""
Expand Down Expand Up @@ -128,3 +93,43 @@ function heu_pool_multistart(m::Optimizer)

return MOI.LOCALLY_INFEASIBLE
end


#-----------------------------------------------------------------#
# UNSUPPORTED FUNCTIONS #
#-----------------------------------------------------------------#

# """
# One-time rounding heuristic to obtain a feasible solution
# For integer solutions
# """
# function heu_basic_rounding(m::Optimizer, relaxed_sol)

# println("Basic Rounding Heuristic Activated...")

# convertor = Dict(MOI.MAX_SENSE => :>, MOI.MIN_SENSE => :<)

# rounded_sol = round_sol(m, relaxed_sol)
# l_var, u_var = fix_domains(m, discrete_sol = rounded_sol)

# heuristic_model = MOI.instantiate(get_option(m, :nlp_solver), with_bridge_type=Float64)
# x = load_nonlinear_model(m, heuristic_model, l_var, u_var)
# MOI.optimize!(heuristic_model)
# heuristic_model_status = MOI.get(heuristic_model, MOI.TerminationStatus())

# if heuristic_model_status == MOI.OTHER_ERROR || heuristic_model_status in STATUS_INF
# get_option(m, :log_level) > 0 && println("Rounding obtained an Infeasible point.")
# push!(m.logs[:obj], "INF")
# return MOI.LOCALLY_INFEASIBLE
# elseif heuristic_model_status in STATUS_OPT || heuristic_model_status in STATUS_LIMIT
# candidate_obj = MOI.get(heuristic_model, MOI.ObjectiveValue())
# candidate_sol = round.(MOI.get(heuristic_model, MOI.VariablePrimal(), x), 5)
# update_incumb_objective(m, candidate_obj, candidate_sol)
# get_option(m, :log_level) > 0 && println("Rounding obtained a feasible solution OBJ = $(m.best_obj)")
# return MOI.LOCALLY_SOLVED
# else
# error("[EXCEPTION] Unknown NLP solver status.")
# end

# return
# end
4 changes: 2 additions & 2 deletions src/solver.jl
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ function default_options()
disc_divert_chunks = 5
disc_abs_width_tol = 1e-4
disc_rel_width_tol = 1e-6
disc_consecutive_forbid = 0
disc_ratio_branch=false
disc_consecutive_forbid = false
disc_ratio_branch = false

convhull_formulation = "sos2"
convhull_ebd = false
Expand Down
Loading

0 comments on commit b59bc27

Please sign in to comment.