Skip to content
Draft
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
9 changes: 9 additions & 0 deletions BREAKING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Noteworthy changes from v0.1 to v0.2

- optional parameters in the `solve` function are now grouped into Branch-and-Bound settings, Frank-Wolfe settings, tolerances
postprocessing settings, heuristic settings, tightening settings and settings for non-trivial domains.
- interface for different modes. For now, we have the `DEFAULT_MODE` and `HEURISTIC_MODE`.
- Renaming the Frank-Wolfe variants such that the names are consisent. We favour the full names.
- Decomposition Invariant Conditional Gradient settings are moved to the `DecompositionInvariantConditionalGradient` struct instead of having them as top-level settings.
- removed stale `clean_solutions`and `max_clean_iter` parameters.
- Boscia's own heuristics can now be constructed by just specifiying their probabilities.
4 changes: 3 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ SCIP = "0.11, 0.12"
SparseArrays = "1.10"
StableRNGs = "1"
Statistics = "1.6"
Suppressor = "0.2"
Test = "1"
julia = "1.6"

Expand All @@ -54,7 +55,8 @@ DoubleFloats = "497a8b3b-efae-58df-a0af-a86822472b78"
HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b"
SCIP = "82193955-e24f-5292-bf16-6f2c5261a85f"
StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3"
Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test", "HiGHS", "Distributions", "SCIP", "DoubleFloats", "StableRNGs", "Aqua"]
test = ["Test", "HiGHS", "Distributions", "SCIP", "DoubleFloats", "StableRNGs", "Aqua", "Suppressor"]
63 changes: 31 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,41 +83,34 @@ function grad!(storage, x)
@. storage = x-diffw
end

x, _, result = Boscia.solve(f, grad!, lmo, verbose = true)
x, _, result = Boscia.solve(f, grad!, lmo, settings_bnb=Boscia.settings_bnb(verbose = true))

Boscia Algorithm.

Parameter settings.
Tree traversal strategy: Move best bound
Branching strategy: Most infeasible
FrankWolfe variant: Blended Pairwise Conditional Gradient
Line Search Method: Secant
Lazification: true
Lazification Tolerance: 2
Absolute dual gap tolerance: 1.000000e-06
Relative dual gap tolerance: 1.000000e-02
Frank-Wolfe subproblem tolerance: 1.000000e-05
Total number of varibales: 6
Number of integer variables: 0
Number of binary variables: 6
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Iteration Open Bound Incumbent Gap (abs) Gap (rel) Time (s) Nodes/sec FW (ms) LMO (ms) LMO (calls c) FW (Its) #ActiveSet Discarded
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
* 1 2 -1.202020e-06 7.500000e-01 7.500012e-01 Inf 3.870000e-01 7.751938e+00 237 2 9 13 1 0
100 27 6.249998e-01 7.500000e-01 1.250002e-01 2.000004e-01 5.590000e-01 2.271914e+02 0 0 641 0 1 0
127 0 7.500000e-01 7.500000e-01 0.000000e+00 0.000000e+00 5.770000e-01 2.201040e+02 0 0 695 0 1 0
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Postprocessing

Blended Pairwise Conditional Gradient Algorithm.
MEMORY_MODE: FrankWolfe.InplaceEmphasis() STEPSIZE: Adaptive EPSILON: 1.0e-7 MAXITERATION: 10000 TYPE: Float64
GRADIENTTYPE: Nothing LAZY: true lazy_tolerance: 2.0
[ Info: In memory_mode memory iterates are written back into x0!

----------------------------------------------------------------------------------------------------------------
Type Iteration Primal Dual Dual Gap Time It/sec #ActiveSet
----------------------------------------------------------------------------------------------------------------
Last 0 7.500000e-01 7.500000e-01 0.000000e+00 1.086583e-03 0.000000e+00 1
----------------------------------------------------------------------------------------------------------------
PP 0 7.500000e-01 7.500000e-01 0.000000e+00 1.927792e-03 0.000000e+00 1
----------------------------------------------------------------------------------------------------------------
Frank-Wolfe subproblem tolerance: 1.000000e-02
Frank-Wolfe dual gap decay factor: 8.000000e-01
Additional kwargs:
Total number of variables: 6
Number of integer variables: 6


-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Iter Open Bound Incumbent Gap (abs) Gap (rel) Time (s) Nodes/sec FW (ms) LMO (ms) LMO (calls c) FW (its) #activeset #shadow
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
* 1 2 0.000000e+00 7.500000e-01 7.500000e-01 Inf 2.000000e-03 1.500000e+03 1 1 4 2 1 0
100 27 6.250000e-01 7.500000e-01 1.250000e-01 2.000000e-01 6.400000e-02 1.984375e+03 0 0 326 0 1 0
127 0 7.500000e-01 7.500000e-01 0.000000e+00 0.000000e+00 7.300000e-02 1.739726e+03 0 0 380 0 1 0
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Solution Statistics.
Solution Status: Optimal (tree empty)
Expand All @@ -127,9 +120,15 @@ Solution Statistics.

Search Statistics.
Total number of nodes processed: 127
Total number of lmo calls: 699
Total time (s): 0.58
LMO calls / sec: 1205.1724137931035
Nodes / sec: 218.96551724137933
LMO calls / node: 5.503937007874016
Total number of lmo calls: 380
Total time (s): 0.074
LMO calls / sec: 5135.135135135135
Nodes / sec: 1716.2162162162163
LMO calls / node: 2.9921259842519685

Total number of global tightenings: 0
Global tightenings / node: 0.0
Total number of local tightenings: 0
Local tightenings / node: 0.0
Total number of potential local tightenings: 0
```
16 changes: 16 additions & 0 deletions docs/src/reference/1_algorithms.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,19 @@ Boscia's `solve` function only requires the oracles of the objective function `f
Modules = [Boscia]
Pages = ["src/interface.jl"]
```

## Optional settings

Boscia has a lot of settings to customize the solving process. These are grouped by general Branch-and-Bound settings, settings specific for Frank-Wolfe, tolerances settings for both the tree as well as Frank-Wolfe.
Furthermore, there are settings for the heuristics, for bound tightenings, postprocessing and for the case of a non-trivial domain, i.e. the objective cannot be evaluated at all points of the feasible region.

```@autodocs
Modules = [Boscia]
Pages = ["src/settings.jl"]
```

## Definitions

Boscia defines its own solving state.
Additionally, Boscia has different modes, like the `DEFAULT_MODE` and `HEURISTIC_MODE`.
These have their own default settings for the optional parameters.
2 changes: 1 addition & 1 deletion examples/HiGHS_example.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ function grad!(storage, x)
@. storage = x - diffw
end

x, _, result = Boscia.solve(f, grad!, lmo, verbose=true)
x, _, result = Boscia.solve(f, grad!, lmo, settings_bnb=Boscia.settings_bnb(verbose=true))
12 changes: 6 additions & 6 deletions examples/approx_planted_point.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ diffi = rand(rng, Bool, n) * 0.6 .+ 0.3
end
lmo = FrankWolfe.MathOptLMO(o)

x, _, result = Boscia.solve(f, grad!, lmo, verbose=true)
x, _, result = Boscia.solve(f, grad!, lmo, settings_bnb=Boscia.settings_bnb(verbose=true))

@test x == round.(diffi)
@test isapprox(f(x), f(result[:raw_solution]), atol=1e-6, rtol=1e-3)
Expand All @@ -56,7 +56,7 @@ diffi = rand(rng, Bool, n) * 0.6 .+ 0.3
end
blmo = CubeBLMO(n, int_vars, bounds)

x, _, result = Boscia.solve(f, grad!, blmo, verbose=true)
x, _, result = Boscia.solve(f, grad!, blmo, settings_bnb=Boscia.settings_bnb(verbose=true))

@test x == round.(diffi)
@test isapprox(f(x), f(result[:raw_solution]), atol=1e-6, rtol=1e-3)
Expand All @@ -70,7 +70,7 @@ diffi = rand(rng, Bool, n) * 0.6 .+ 0.3
sblmo = Boscia.CubeSimpleBLMO(lbs, ubs, int_vars)

x, _, result =
Boscia.solve(f, grad!, sblmo, lbs[int_vars], ubs[int_vars], int_vars, n, verbose=true)
Boscia.solve(f, grad!, sblmo, lbs[int_vars], ubs[int_vars], int_vars, n, settings_bnb=Boscia.settings_bnb(verbose=true))

@test x == round.(diffi)
@test isapprox(f(x), f(result[:raw_solution]), atol=1e-6, rtol=1e-3)
Expand Down Expand Up @@ -103,7 +103,7 @@ end
end
lmo = FrankWolfe.MathOptLMO(o)

x, _, result = Boscia.solve(f, grad!, lmo, verbose=true)
x, _, result = Boscia.solve(f, grad!, lmo, settings_bnb=Boscia.settings_bnb(verbose=true))

sol = diffi
sol[int_vars] = round.(sol[int_vars])
Expand All @@ -119,7 +119,7 @@ end
end
blmo = CubeBLMO(n, int_vars, bounds)

x, _, result = Boscia.solve(f, grad!, blmo, verbose=true)
x, _, result = Boscia.solve(f, grad!, blmo, settings_bnb=Boscia.settings_bnb(verbose=true))

sol = diffi
sol[int_vars] = round.(sol[int_vars])
Expand All @@ -134,7 +134,7 @@ end
sblmo = Boscia.CubeSimpleBLMO(lbs, ubs, int_vars)

x, _, result =
Boscia.solve(f, grad!, sblmo, lbs[int_vars], ubs[int_vars], int_vars, n, verbose=true)
Boscia.solve(f, grad!, sblmo, lbs[int_vars], ubs[int_vars], int_vars, n, settings_bnb=Boscia.settings_bnb(verbose=true))

sol = diffi
sol[int_vars] = round.(sol[int_vars])
Expand Down
10 changes: 4 additions & 6 deletions examples/big_float_example.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,8 @@ diffi = rand(rng, Bool, n) * 0.6 .+ 0.3
ubs[int_vars],
int_vars,
n,
verbose=true,
time_limit=120,
custom_heuristics=custom_heuristics,
settings_bnb=Boscia.settings_bnb(verbose=true, time_limit=60),
settings_heuristic=Boscia.settings_heuristic(custom_heuristics=custom_heuristics),
)

if result[:total_time_in_sec] < 125
Expand Down Expand Up @@ -79,9 +78,8 @@ end
ubs[int_vars],
int_vars,
n,
verbose=true,
time_limit=125,
custom_heuristics=custom_heuristics,
settings_bnb=Boscia.settings_bnb(verbose=true, time_limit=60),
settings_heuristic=Boscia.settings_heuristic(custom_heuristics=custom_heuristics),
)

if result[:total_time_in_sec] < 125
Expand Down
10 changes: 6 additions & 4 deletions examples/birkhoff_decomposition.jl
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,10 @@ function build_birkhoff_lmo()
end

lmo = build_birkhoff_lmo()
x, _, _ = Boscia.solve(f, grad!, lmo, verbose=true)
x, _, _ = Boscia.solve(f, grad!, lmo, verbose=true, lazy=false, variant=Boscia.DICG())
x, _, _ = Boscia.solve(f, grad!, lmo, settings_bnb=Boscia.settings_bnb(verbose=true))
x, _, _ = Boscia.solve(f, grad!, lmo,
settings_bnb=Boscia.settings_bnb(verbose=true),
settings_frank_wolfe=Boscia.settings_frank_wolfe(lazy=false, variant=Boscia.DecompositionInvariantConditionalGradient()))


# TODO the below needs to be fixed
Expand All @@ -107,14 +109,14 @@ x, _, _ = Boscia.solve(f, grad!, lmo, verbose=true, lazy=false, variant=Boscia.D

@testset "Birkhoff decomposition" begin
lmo = build_birkhoff_lmo()
x, _, result_baseline = Boscia.solve(f, grad!, lmo, verbose=true)
x, _, result_baseline = Boscia.solve(f, grad!, lmo, settings_bnb=Boscia.settings_bnb(verbose=true))
@test f(x) <= f(result_baseline[:raw_solution]) + 1e-6
lmo = build_birkhoff_lmo()
blmo = Boscia.MathOptBLMO(HiGHS.Optimizer())
branching_strategy = Boscia.PartialStrongBranching(10, 1e-3, blmo)
MOI.set(branching_strategy.bounded_lmo.o, MOI.Silent(), true)
x_strong, _, result_strong =
Boscia.solve(f, grad!, lmo, verbose=true, branching_strategy=branching_strategy)
Boscia.solve(f, grad!, lmo, settings_bnb=Boscia.settings_bnb(verbose=true, branching_strategy=branching_strategy))
@test isapprox(f(x), f(x_strong), atol=1e-5, rtol=1e-2)
@test f(x) <= f(result_strong[:raw_solution]) + 1e-6
end
5 changes: 4 additions & 1 deletion examples/int_sparse_reg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,10 @@ const y_d = D * sol_x
MOI.set(branching_strategy.pstrong.optimizer, MOI.Silent(), true)=#


x, _, result = Boscia.solve(f, grad!, lmo, verbose=true, max_fw_iter=10001, rel_dual_gap=1e-3)
x, _, result = Boscia.solve(f, grad!, lmo,
settings_bnb=Boscia.settings_bnb(verbose=true),
settings_frank_wolfe=Boscia.settings_frank_wolfe(max_fw_iter=10001),
settings_tolerances=Boscia.settings_tolerances(rel_dual_gap=1e-3))

val_min, x_min = Boscia.sparse_min_via_enum(f, n, k, fill(0:l, n))
#@show x_min
Expand Down
4 changes: 3 additions & 1 deletion examples/lasso.jl
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,9 @@ push!(groups, ((k_int-1)*group_size+1):p)
return storage
end

x, _, result = Boscia.solve(f, grad!, lmo, verbose=true, rel_dual_gap=1e-5)
x, _, result = Boscia.solve(f, grad!, lmo,
settings_bnb=Boscia.settings_bnb(verbose=true),
settings_tolerances=Boscia.settings_tolerances(rel_dual_gap=1e-2, dual_gap=1e-5))

# println("Solution: $(x[1:p])")
z = x[p+1:2p]
Expand Down
4 changes: 2 additions & 2 deletions examples/low_dim_in_high_dim.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ end
end
lmo = FrankWolfe.MathOptLMO(o)

x, _, result = Boscia.solve(f, grad!, lmo, verbose=true)
x, _, result = Boscia.solve(f, grad!, lmo, settings_bnb=Boscia.settings_bnb(verbose=true))

if n < 15 # only do for small n
valopt, xopt = Boscia.min_via_enum(f, n)
Expand All @@ -67,7 +67,7 @@ end

# modified solve call from managed_blmo.jl automatically wraps sblmo into a managed_blmo
x, _, result =
Boscia.solve(f, grad!, sblmo, lbs[int_vars], ubs[int_vars], int_vars, n, verbose=true)
Boscia.solve(f, grad!, sblmo, lbs[int_vars], ubs[int_vars], int_vars, n, settings_bnb=Boscia.settings_bnb(verbose=true))

if n < 15 # only do for small n
valopt, xopt = Boscia.min_via_enum(f, n)
Expand Down
2 changes: 1 addition & 1 deletion examples/mps-example.jl
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,6 @@ function grad!(storage, x)
end

@testset "MPS 22433 instance" begin
x, _, result = Boscia.solve(f, grad!, lmo, verbose=true)
x, _, result = Boscia.solve(f, grad!, lmo, settings_bnb=Boscia.settings_bnb(verbose=true))
@test f(x) <= f(result[:raw_solution])
end
8 changes: 2 additions & 6 deletions examples/mps-examples/mip-examples.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ using SCIP
using LinearAlgebra
import MathOptInterface
const MOI = MathOptInterface
import Ipopt


# MIPLIB instances
Expand Down Expand Up @@ -94,11 +93,8 @@ test_instance = string("MPS ", example, " instance")
f,
grad!,
lmo,
verbose=true,
print_iter=10,
fw_epsilon=1e-1,
min_node_fw_epsilon=1e-3,
time_limit=600,
settings_bnb=Boscia.settings_bnb(verbose=true, print_iter=10, time_limit=600),
settings_tolerances=Boscia.settings_tolerances(fw_epsilon=1e-1, min_node_fw_epsilon=1e-3),
)
@test f(x) <= f(result[:raw_solution])
end
8 changes: 4 additions & 4 deletions examples/nonlinear.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ seed = rand(UInt64)
@show seed
rng = StableRNG(seed)

n = 30

# using SCIP
# const MOI = MathOptInterface

Expand Down Expand Up @@ -122,10 +124,8 @@ x, _, _ = Boscia.solve(
f,
grad!,
lmo,
verbose=true,
print_iter=500,
custom_heuristics=heuristics,
time_limit=300,
settings_bnb=Boscia.settings_bnb(verbose=true, print_iter=500, time_limit=300),
settings_heuristic=Boscia.settings_heuristic(custom_heuristics=heuristics),
)

@show x
Loading
Loading