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
2 changes: 1 addition & 1 deletion lib/PlasmoBenders/Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "PlasmoBenders"
uuid = "491f1417-53b2-48aa-b9da-44cdd6c031b7"
authors = ["David Cole"]
version = "0.1.3"
version = "0.1.4"

[deps]
Plasmo = "d3f7391f-f14a-50cc-bbe4-76a32d1bad3c"
Expand Down
12 changes: 6 additions & 6 deletions lib/PlasmoBenders/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ Pkg.add("PlasmoBenders")

[Benders decomposition](https://en.wikipedia.org/wiki/Benders_decomposition) (BD) is a decomposition approach that breaks problems into a master problem and a subproblem(s) and is typically applied to linear and mixed integer linear programs. BD is an iterative algorithm that can be useful for problems where there are a set of complicating variables (in the master problem) that, once fixed, make the subproblem easier to solve. An iteration of BD generally includes 1) solving the master problem, 2) passing the solution of the master problem to the subproblem, 3) solving the subproblem with the master problem solution, and 4) passing primal and dual information from the subproblem to the master problem and forming cutting planes on the master problem. PlasmoBenders applies this approach to graph-based problems where each subgraph of an OptiGraph is the master problem or a subproblem. PD is applied to the graph based on the user-defined subproblems.

Nested Benders Decomposition (NBD; also called [dual dynamic programming](https://www-sciencedirect-com.ezproxy.library.wisc.edu/science/article/pii/S0098135421000430)) uses similar ideas to BD but can have a sequence of subproblems (i.e., not all subproblems are connected to the original master problem, forming a nested structure). NBD can be applied to graphs with a tree structure, and NBD is likewise implemented in PlasmoBenders.
Nested Benders Decomposition (NBD; also called [dual dynamic programming](https://doi.org/10.1016/j.compchemeng.2021.107265)) uses similar ideas to BD but can have a sequence of subproblems (i.e., not all subproblems are connected to the original master problem, forming a nested structure). NBD can be applied to graphs with a tree structure, and NBD is likewise implemented in PlasmoBenders.

> [!NOTE]
> PlasmoBenders requires Plasmo v0.6.2 or later

PlasmoBenders is built on a `BendersOptimizer` object which requires a user-defined graph and a subgraph of that graph as the root (master problem) graph. After the information is past, the `BendersOptimizer` constructor updates the graph to apply BD or NBD. `JuMP.optimize!` is extended so that the iterative BD/NBD algorithm is applied to find a solution.
PlasmoBenders is built on a `BendersAlgorithm` object which requires a user-defined graph and a subgraph of that graph as the root (master problem) graph. After the information is passed, the `BendersAlgorithm` object is instantiated and can be solved with the `run_algorithm!` function.


### Simple Exmaple
Expand Down Expand Up @@ -63,16 +63,16 @@ set_to_node_objectives(g22)

solver = optimizer_with_attributes(HiGHS.Optimizer, "output_flag" => false)

BendersOpt = BendersOptimizer(g0, g1; solver = solver)
BendersAlg = BendersAlgorithm(g0, g1; solver = solver)

optimize!(BendersOpt)
run_algorithm!(BendersAlg)
```

The `BendersOptimizer` constructor takes the overall graph, `g0` as the first object and then the root/master subgraph `g1` as the second argument.
The `BendersAlgorithm` constructor takes the overall graph, `g0` as the first object and then the root/master subgraph `g1` as the second argument.

### Additional Functionality

PlasmoBenders includes additional functionality. The following keyword arguments can be passed to the `BendersOptimizer` constructor. These include the following key word arguments:
PlasmoBenders includes additional functionality. The following keyword arguments can be passed to the `BendersAlgorithm` constructor. These include the following key word arguments:

* `max_iters` - maximum number of iterations to use
* `tol` - termination tolerance between upper and lower bounds
Expand Down
15 changes: 11 additions & 4 deletions lib/PlasmoBenders/src/initialize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,12 @@ function _add_complicating_variables!(
con_obj = constraint_object(link)
vars = con_obj.func.terms.keys

next_object_link_vars = [var for var in vars if source_graph(JuMP.owner_model(var)) == next_object]
last_object_link_vars = [var for var in vars if source_graph(JuMP.owner_model(var)) == last_object]
# Get the variables from the constraint in the next_object
next_object_nodes = all_nodes(next_object)
last_object_nodes = all_nodes(last_object)

next_object_link_vars = [var for var in vars if JuMP.owner_model(var) in next_object_nodes]
last_object_link_vars = [var for var in vars if JuMP.owner_model(var) in last_object_nodes]

# Get the optinodes containing the next set of variables
#next_optinode = optinode(next_object_vars[1]) #NEXT: Fix this!
Expand Down Expand Up @@ -239,8 +243,11 @@ function _add_complicating_variables!(
vars = con_obj.func.terms.keys

# Get the variables from the constraint in the next_object
next_object_link_vars = [var for var in vars if source_graph(JuMP.owner_model(var)) == next_object]
last_object_link_vars = [var for var in vars if source_graph(JuMP.owner_model(var)) == last_object]
next_object_nodes = all_nodes(next_object)
last_object_nodes = all_nodes(last_object)

next_object_link_vars = [var for var in vars if JuMP.owner_model(var) in next_object_nodes]
last_object_link_vars = [var for var in vars if JuMP.owner_model(var) in last_object_nodes]
next_object_copy_vars = [var_copy_map[var] for var in last_object_link_vars]

# Get the set of nodes in the next_object that are included in the constraint
Expand Down
27 changes: 26 additions & 1 deletion lib/PlasmoBenders/src/utils.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
# Enable printing the optimizer
function Base.string(optimizer::BendersAlgorithm)
return return @sprintf(
"""
A BendersAlgorithm
-------------------------------------------
%32s %9s
%32s %9s
%32s %9s
%32s %9s
""",
"Num subproblem subgraphs:",
length(optimizer.solve_order),
"MIP subproblems (nonroot):",
optimizer.is_MIP,
"Absolute Tolerance:",
optimizer.tol,
"Maximum Iterations:",
optimizer.max_iters,
)
end

Base.print(io::IO, optimizer::BendersAlgorithm) = Base.print(io, Base.string(optimizer))
Base.show(io::IO, optimizer::BendersAlgorithm) = Base.print(io, optimizer)

function _get_hyper_projection(optimizer::BendersAlgorithm, graph::Plasmo.OptiGraph)
if haskey(optimizer.ext, "_projection")
return optimizer.ext["_projection"]
Expand Down Expand Up @@ -413,7 +438,7 @@ function get_upper_bounds(optimizer::BendersAlgorithm; monotonic = true)
if optimizer.current_iter == 0
return ubs
else
return [minimum(ubs[1:i] for i in 1:length(ubs))]
return [minimum(ubs[1:i]) for i in 1:length(ubs)]
end
else
return ubs
Expand Down
Loading