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

Indicator update best bound #175

Merged
merged 7 commits into from
Jun 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# ConstrainSolver.jl - Changelog

## v0.2.1
## v0.2.2 (26th of June 2020)
- Actually use best bound [#175](https://github.com/Wikunia/ConstraintSolver.jl/pull/175)
- Select next var based on objective (still hacky solution) [#176](https://github.com/Wikunia/ConstraintSolver.jl/issues/176)

## v0.2.1 (26th of June 2020)
- Bugfixes in indicator constraint [#170](https://github.com/Wikunia/ConstraintSolver.jl/issues/170)
- Calling finished constraints and other functions for i.e `TableConstraint` as an inner constraint
- Use correct best bound when inactive vs active
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "ConstraintSolver"
uuid = "e0e52ebd-5523-408d-9ca3-7641f1cd1405"
authors = ["Ole Kröger <[email protected]>"]
version = "0.2.1"
version = "0.2.2"

[deps]
Formatting = "59287772-0a20-5a39-b81b-1366585eb4c0"
Expand Down
4 changes: 3 additions & 1 deletion benchmark/benchmarks.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using BenchmarkTools
using ConstraintSolver, JuMP, MathOptInterface
using GLPK, JSON
using GLPK, JSON, Cbc

const CS = ConstraintSolver
const MOI = MathOptInterface
Expand All @@ -23,6 +23,8 @@ SUITE["eternity"] = BenchmarkGroup(["alldifferent", "table", "equal"])
# compiling run
solve_eternity("eternity_6x5"; height=6, width=5)
SUITE["eternity"]["6x5"] = @benchmarkable solve_eternity("eternity_6x5"; height=6, width=5) seconds=30
SUITE["eternity"]["5x5_opt"] = @benchmarkable solve_eternity("eternity_5x5"; height=5, width=5, optimize=true) seconds=120
SUITE["eternity"]["5x5_opt_ind"] = @benchmarkable solve_eternity("eternity_5x5"; height=5, width=5, optimize=true, indicator=true) seconds=120
SUITE["eternity"]["5x5_all"] = @benchmarkable solve_eternity("eternity_5x5"; all_solutions=true) seconds=30

include(joinpath(dir, "benchmark/lp/benchmark.jl"))
Expand Down
28 changes: 25 additions & 3 deletions benchmark/eternity/benchmark.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ function get_rotations(puzzle)
return rotations
end

function solve_eternity(fname="eternity_7"; height=nothing, width=nothing, all_solutions=false)
function solve_eternity(fname="eternity_7"; height=nothing, width=nothing, all_solutions=false, optimize=false, indicator=false)
puzzle = read_puzzle(fname)
rotations = get_rotations(puzzle)
npieces = size(puzzle)[1]
Expand All @@ -36,15 +36,27 @@ function solve_eternity(fname="eternity_7"; height=nothing, width=nothing, all_s
ncolors = maximum(puzzle[:,2:end])

m = Model(optimizer_with_attributes(CS.Optimizer, "logging" => [], "all_solutions"=>all_solutions))
if optimize
cbc_optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0)
m = Model(optimizer_with_attributes(CS.Optimizer, "logging" => [], "all_solutions"=>all_solutions, "lp_optimizer" => cbc_optimizer))
end

@variable(m, 1 <= p[1:height, 1:width] <= npieces, Int)
@variable(m, 0 <= pu[1:height, 1:width] <= ncolors, Int)
@variable(m, 0 <= pr[1:height, 1:width] <= ncolors, Int)
@variable(m, 0 <= pd[1:height, 1:width] <= ncolors, Int)
@variable(m, 0 <= pl[1:height, 1:width] <= ncolors, Int)
if indicator
@variable(m, b, Bin)
end

@constraint(m, p[:] in CS.AllDifferentSet())
for i=1:height, j=1:width
@constraint(m, [p[i,j], pu[i,j], pr[i,j], pd[i,j], pl[i,j]] in CS.TableSet(rotations))
if indicator
@constraint(m, b => {[p[i,j], pu[i,j], pr[i,j], pd[i,j], pl[i,j]] in CS.TableSet(rotations)})
else
@constraint(m, [p[i,j], pu[i,j], pr[i,j], pd[i,j], pl[i,j]] in CS.TableSet(rotations))
end
end

# borders
Expand Down Expand Up @@ -75,9 +87,19 @@ function solve_eternity(fname="eternity_7"; height=nothing, width=nothing, all_s
@constraint(m, pr[i,j] == pl[i, j+1])
end

if width == height
if !optimize && indicator
@constraint(m, b == 1)
end

if !optimize && width == height
start_piece = findfirst(i->count(c->c == 0, puzzle[i,:]) == 2,1:npieces)
@constraint(m, p[1,1] == start_piece)
elseif optimize
if indicator
@objective(m, Max, 1000*b + p[1,1] + p[1,2])
else
@objective(m, Max, p[1,1] + p[1,2])
end
end

optimize!(m)
Expand Down
2 changes: 1 addition & 1 deletion benchmark/run_benchmarks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ end
if isinteractive() == false
args = parse_commandline()
using PkgBenchmark
using ConstraintSolver
using ConstraintSolver, Cbc
using GitHub, JSON, Statistics

github_auth = GitHub.authenticate(ENV["GITHUB_AUTH"])
Expand Down
9 changes: 2 additions & 7 deletions src/ConstraintSolver.jl
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ function add_var!(com::CS.CoM, from::Int, to::Int; fix = nothing)
push!(com.search_space, var)
push!(com.subscription, Int[])
push!(com.bt_infeasible, 0)
push!(com.var_in_obj, false)
return var
end

Expand Down Expand Up @@ -488,13 +489,7 @@ function update_best_bound!(backtrack_obj::BacktrackObj, com::CS.CoM, constraint
further_pruning = true
feasible = true
for constraint in constraints
relevant = false
for obj_index in com.objective.indices
if obj_index in constraint.std.indices
relevant = true
break
end
end
relevant = any(com.var_in_obj[i] for i in constraint.std.indices)
if relevant
feasible = prune_constraint!(
com,
Expand Down
2 changes: 2 additions & 0 deletions src/MOI_wrapper/objective.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ end

function MOI.set(model::Optimizer, ::MOI.ObjectiveFunction, func::SVF)
check_inbounds(model, func)
model.inner.var_in_obj[func.variable.value] = true
model.inner.objective =
SingleVariableObjective(func, func.variable.value, [func.variable.value])
return
Expand All @@ -24,6 +25,7 @@ function MOI.set(model::Optimizer, ::MOI.ObjectiveFunction, func::SAF{T}) where
indices = [func.terms[i].variable_index.value for i = 1:length(func.terms)]
coeffs = [func.terms[i].coefficient for i = 1:length(func.terms)]
lc = LinearCombination(indices, coeffs)
model.inner.var_in_obj[indices] .= true
model.inner.objective = LinearCombinationObjective(func, lc, func.constant, indices)
return
end
1 change: 1 addition & 0 deletions src/MOI_wrapper/variables.jl
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ function MOI.add_variable(model::Optimizer)
model.variable_info[index].changes = changes
push!(model.inner.subscription, Int[])
push!(model.inner.bt_infeasible, 0)
push!(model.inner.var_in_obj, false)
addupd_var_in_inner_model(model, index)
return MOI.VariableIndex(index)
end
Expand Down
23 changes: 17 additions & 6 deletions src/branching.jl
Original file line number Diff line number Diff line change
Expand Up @@ -92,18 +92,29 @@ function get_next_branch_variable(com::CS.CoM)
biggest_inf = -1
best_ind = -1
biggest_dependent = typemax(Int)
is_in_objective = false
found = false

for ind = 1:length(com.search_space)
if !isfixed(com.search_space[ind])
num_pvals = nvalues(com.search_space[ind])
inf = com.bt_infeasible[ind]
if inf >= biggest_inf
if inf > biggest_inf || num_pvals < lowest_num_pvals
lowest_num_pvals = num_pvals
biggest_inf = inf
best_ind = ind
found = true
if !is_in_objective && com.var_in_obj[ind]
is_in_objective = true
lowest_num_pvals = num_pvals
biggest_inf = inf
best_ind = ind
found = true
continue
end
if !is_in_objective || com.var_in_obj[ind]
if inf >= biggest_inf
if inf > biggest_inf || num_pvals < lowest_num_pvals
lowest_num_pvals = num_pvals
biggest_inf = inf
best_ind = ind
found = true
end
end
end
end
Expand Down
2 changes: 0 additions & 2 deletions src/constraints/indicator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ the possible values the table constraint allows. `var_idx`, `lb` and `ub` don't
Additionally only a rough estimated bound is used which can be computed relatively fast.
This method calls the inner_constraint method if it exists and the indicator is activated.
"""
#=
function update_best_bound_constraint!(com::CS.CoM,
constraint::IndicatorConstraint,
fct::Union{MOI.VectorOfVariables, VAF{T}},
Expand All @@ -162,7 +161,6 @@ function update_best_bound_constraint!(com::CS.CoM,
end
return true
end
=#

function single_reverse_pruning_constraint!(
com::CoM,
Expand Down
3 changes: 2 additions & 1 deletion src/type_inits.jl
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ function ConstraintSolverModel(::Type{T} = Float64) where {T<:Real}
1, # c_backtrack_idx
Vector{BacktrackObj{T}}(), # backtrack_vec
MOI.FEASIBILITY_SENSE, #
NoObjective(), #
NoObjective(), #
Vector{Bool}(), # var_in_obj
get_traverse_strategy(),
get_branch_split(),
zero(T), # best_sol,
Expand Down
1 change: 1 addition & 0 deletions src/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ mutable struct ConstraintSolverModel{T<:Real}
backtrack_vec::Vector{BacktrackObj{T}}
sense::MOI.OptimizationSense
objective::ObjectiveFunction
var_in_obj::Vector{Bool} # saves whether a variable is part of the objective function
traverse_strategy::Val
branch_split::Val
best_sol::T # Objective of the best solution
Expand Down