Skip to content
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
3 changes: 3 additions & 0 deletions CHANGELOG.MD
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## v0.1.5
- Implementation of DFS

## v0.1.4
- Allow DataStructures 0.19

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 = "Bonobo"
uuid = "f7b14807-3d4d-461a-888a-05dd4bca8bc3"
authors = ["Ole Kroeger <o.kroeger@opensourc.es> and contributors"]
version = "0.1.4"
version = "0.1.5"

[deps]
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
Expand Down
20 changes: 14 additions & 6 deletions src/Bonobo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ This needs to be added by every `AbstractNode` as `std::BnBNodeInfo`
id :: Int
lb :: Float64
ub :: Float64
depth :: Int
```
"""
mutable struct BnBNodeInfo
id :: Int
lb :: Float64
ub :: Float64
depth :: Int
end

"""
Expand Down Expand Up @@ -98,6 +100,8 @@ If there is a tie then the smallest node id is used as a tie breaker.
"""
struct BestFirstSearch <: AbstractTraverseStrategy end

struct DepthFirstSearch <: AbstractTraverseStrategy end

@deprecate BFS() BestFirstSearch() false

"""
Expand Down Expand Up @@ -272,8 +276,12 @@ function optimize!(tree::BnBTree; callback=(args...; kwargs...)->())
end

tree.node_queue[node.id] = (node.lb, node.id)
_ , prio = peek(tree.node_queue)
@assert tree.lb <= prio[1]
_ , prio = first(tree.node_queue)
if tree.lb > prio[1]+1e-6
@show tree.lb
@show prio[1]
end
@assert tree.lb <= prio[1]+1e-6
tree.lb = prio[1]

updated = update_best_solution!(tree, node)
Expand All @@ -288,16 +296,16 @@ function optimize!(tree::BnBTree; callback=(args...; kwargs...)->())
branch!(tree, node)
callback(tree, node)
end
sort_solutions!(tree.solutions, tree.sense)
sort_solutions!(tree.solutions)
end

"""
sort_solutions!(solutions::Vector{<:AbstractSolution}, sense::Symbol)
sort_solutions!(solutions::Vector{<:AbstractSolution})

Sort the solutions vector by objective value such that the best solution is at index 1.
"""
function sort_solutions!(solutions::Vector{<:AbstractSolution}, sense::Symbol)
sort!(solutions; rev = sense == :Max, by=s->s.objective)
function sort_solutions!(solutions::Vector{<:AbstractSolution})
sort!(solutions; by=s->s.objective)
end

"""
Expand Down
11 changes: 9 additions & 2 deletions src/node.jl
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@ For information on that see [`set_root!`](@ref).
"""
function create_node(Node, node_id::Int, parent::Union{AbstractNode, Nothing}, node_info::NamedTuple)
lb = -Inf
depth = 1
if !isnothing(parent)
lb = parent.lb
depth = parent.depth + 1
end
bnb_node = structfromnt(BnBNodeInfo, (id = node_id, lb = lb, ub = Inf))
bnb_node = structfromnt(BnBNodeInfo, (id = node_id, lb = lb, ub = Inf, depth = depth))
bnb_nt = (std = bnb_node,)
node_nt = merge(bnb_nt, node_info)
return structfromnt(Node, node_nt)
Expand All @@ -69,7 +71,12 @@ Get the next node of the tree which shall be evaluted next by [`evaluate_node!`]
If you want to implement your own traversing strategy check out [`AbstractTraverseStrategy`](@ref).
"""
function get_next_node(tree::BnBTree, ::BestFirstSearch)
node_id, _ = peek(tree.node_queue)
node_id, _ = first(tree.node_queue)
return tree.nodes[node_id]
end

function get_next_node(tree::BnBTree, ::DepthFirstSearch)
node_id = argmax(k -> tree.nodes[k].depth, keys(tree.nodes))
return tree.nodes[node_id]
end

Expand Down
2 changes: 2 additions & 0 deletions src/util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
:id,
:lb,
:ub,
:depth
)
Core.getproperty(Core.getproperty(c, :std), s)
else
Expand All @@ -18,6 +19,7 @@ end
:id,
:lb,
:ub,
:depth
)
Core.setproperty!(c.std, s, v)
else
Expand Down
29 changes: 29 additions & 0 deletions test/end2end/mip.jl
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,35 @@ end
@test BB.get_objective_value(bnb_model) ≈ 5.2
end

@testset "MIP Problem with 3 variables DFS" begin
m = Model(HiGHS.Optimizer)
set_optimizer_attribute(m, "log_to_console", false)
@variable(m, x[1:3] >= 0)
@constraint(m, 0.5x[1]+3.1x[2]+4.2x[3] <= 6.1)
@constraint(m, 1.9x[1]+0.7x[2]+0.2x[3] <= 8.1)
@constraint(m, 2.9x[1]-2.3x[2]+4.2x[3] <= 10.5)
@objective(m, Max, x[1]+1.2x[2]+3.2x[3])

bnb_model = BB.initialize(;
traverse_strategy = BB.DepthFirstSearch(),
Node = MIPNode,
root = m,
sense = objective_sense(m) == MOI.MAX_SENSE ? :Max : :Min
)
BB.set_root!(bnb_model, (
lbs = zeros(length(x)),
ubs = fill(Inf, length(x)),
status = MOI.OPTIMIZE_NOT_CALLED
))

BB.optimize!(bnb_model)

sol_x = convert.(Int, BB.get_solution(bnb_model))

@test sol_x == [2,0,1]
@test BB.get_objective_value(bnb_model) ≈ 5.2
end

@testset "MIP Problem with 3 variables minimize" begin
m = Model(HiGHS.Optimizer)
set_optimizer_attribute(m, "log_to_console", false)
Expand Down
Loading