Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
branch_and_bound_df_vs_bf_example.cc
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
55#include <cstddef>
56#include <iostream>
57#include <string>
58
59#include <Branch_And_Bound.H>
60#include <tpl_array.H>
61
62using namespace Aleph;
63
64// ---------------------------------------------------------------------------
65// 0/1 knapsack domain
66// ---------------------------------------------------------------------------
67
68struct Item
69{
70 std::string name;
71 double weight = 0.0;
72 double value = 0.0;
73};
74
76{
77 size_t next_item = 0;
78 double weight_so_far = 0.0;
79 double value_so_far = 0.0;
80};
81
83{
84public:
85 struct Move
86 {
87 bool include = false;
88 };
89
92
93 explicit KnapsackDomain(const Array<Item> &items, double capacity)
94 : items_(items), capacity_(capacity)
95 {
96 // Ensure items are sorted by descending value/weight density so that
97 // bound() produces an admissible upper bound.
98 const size_t n = items_.size();
99 for (size_t i = 0; i + 1 < n; ++i)
100 for (size_t j = i + 1; j < n; ++j)
101 if (items_[j].value / items_[j].weight > items_[i].value / items_[i].weight)
102 {
103 Item tmp = items_[i];
104 items_[i] = items_[j];
105 items_[j] = tmp;
106 }
107 }
108
110 [[nodiscard]] bool is_complete(const State &s) const noexcept
111 {
112 return s.next_item == static_cast<size_t>(items_.size());
113 }
114
116 [[nodiscard]] Objective objective_value(const State &s) const noexcept
117 {
118 return s.value_so_far;
119 }
120
126 [[nodiscard]] Objective bound(const State &s) const noexcept
127 {
128 double remaining = capacity_ - s.weight_so_far;
129 double ub = s.value_so_far;
130 size_t i = s.next_item;
131 while (i < static_cast<size_t>(items_.size()) and remaining > 0.0)
132 {
133 const Item &item = items_[i];
134 if (item.weight <= remaining)
135 {
136 ub += item.value;
137 remaining -= item.weight;
138 }
139 else
140 {
141 ub += item.value * (remaining / item.weight);
142 remaining = 0.0;
143 }
144 ++i;
145 }
146
147 return ub;
148 }
149
150 void apply(State &s, const Move &m) const noexcept
151 {
152 if (m.include)
153 {
154 const Item &item = items_[s.next_item];
155 s.weight_so_far += item.weight;
156 s.value_so_far += item.value;
157 }
158 ++s.next_item;
159 }
160
161 void undo(State &s, const Move &m) const noexcept
162 {
163 --s.next_item;
164 if (m.include)
165 {
166 const Item &item = items_[s.next_item];
167 s.weight_so_far -= item.weight;
168 s.value_so_far -= item.value;
169 }
170 }
171
172 [[nodiscard]] bool is_feasible(const State &s) const noexcept
173 {
174 return s.weight_so_far <= capacity_;
175 }
176
177 template <typename Visitor>
178 bool for_each_successor(const State &s, Visitor visit) const
179 {
180 if (s.next_item >= static_cast<size_t>(items_.size()))
181 return true;
182
183 // Try including the item first (often finds better solutions sooner).
184 const Item &item = items_[s.next_item];
185 if (s.weight_so_far + item.weight <= capacity_)
186 {
187 if (not visit(Move{true}))
188 return false;
189 }
190
191 return visit(Move{false});
192 }
193
194private:
196 double capacity_;
197};
198
199// ---------------------------------------------------------------------------
200// Helper to run and print one strategy
201// ---------------------------------------------------------------------------
202
203static void run_strategy(const Array<Item> &items,
204 double capacity,
206 const std::string &label)
207{
208 KnapsackDomain domain(items, capacity);
209 ExplorationPolicy policy;
210 policy.strategy = strategy;
211
213 const auto result = engine.search(KnapsackState{});
214
215 std::cout << label << ":\n";
216 if (result.found_solution())
217 {
218 std::cout << " Optimum value : " << result.incumbent.best_value() << "\n";
219 std::cout << " Expanded states: " << result.stats.expanded_states << "\n";
220 std::cout << " Pruned by bound: " << result.stats.pruned_by_bound << "\n";
221 }
222 else
223 {
224 std::cout << " No solution found.\n";
225 }
226}
227
228// ---------------------------------------------------------------------------
229// Main
230// ---------------------------------------------------------------------------
231
232int main()
233{
234 // Items sorted by value/weight ratio (required for the fractional bound).
235 // Densities: watch=6.0, book=5.0, camera=4.0, tablet=3.25, laptop=3.2
236 const Array<Item> items = {
237 {"watch", 1.0, 6.0},
238 {"book", 2.0, 10.0},
239 {"camera", 3.0, 12.0},
240 {"tablet", 4.0, 13.0},
241 {"laptop", 5.0, 16.0},
242 };
243
244 const double capacity = 7.0;
245
246 run_strategy(items, capacity, ExplorationPolicy::Strategy::Depth_First, "Depth-First B&B");
247 std::cout << "\n";
248 run_strategy(items, capacity, ExplorationPolicy::Strategy::Best_First, "Best-First B&B");
249
250 return 0;
251}
Reusable branch-and-bound engine for implicit optimization spaces.
static void run_strategy(const Array< Item > &items, double capacity, ExplorationPolicy::Strategy strategy, const std::string &label)
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:139
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:351
Reusable branch-and-bound engine over implicit state spaces.
void undo(State &s, const Move &m) const noexcept
KnapsackDomain(const Array< Item > &items, double capacity)
Objective bound(const State &s) const noexcept
Greedy fractional upper bound on the remaining items.
bool for_each_successor(const State &s, Visitor visit) const
bool is_feasible(const State &s) const noexcept
void apply(State &s, const Move &m) const noexcept
bool is_complete(const State &s) const noexcept
True when all items have been decided.
Objective objective_value(const State &s) const noexcept
Objective value for a complete assignment.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
Exploration controls shared across engines.
Strategy strategy
Traversal strategy.
Strategy
Traversal strategy supported by the engine.
size_t next_item
Index of the next item to decide.
FooMap m(5, fst_unit_pair_hash, snd_unit_pair_hash)
static mt19937 engine
Dynamic array container with automatic resizing.