35#include <gtest/gtest.h>
59struct ArtificialMaxDomain
61 using State = ArtificialState;
62 using Move = ArtificialMove;
63 using Objective =
int;
65 bool is_complete(
const State &state)
const
67 return state.depth == 2;
70 Objective objective_value(
const State &state)
const
75 Objective bound(
const State &state)
const
82 default:
return state.value;
86 void apply(State &state,
const Move &move)
const
88 state.code = move.target_code;
89 state.value += move.delta;
93 void undo(State &state,
const Move &move)
const
96 state.value -= move.delta;
100 template <
typename Visitor>
101 bool for_each_successor(
const State &state,
Visitor visit)
const
103 if (state.depth >= 2)
111 return visit(Move{3, 0,
'R'});
116 return visit(Move{5, 9,
'R'});
121 return visit(Move{7, 4,
'R'});
129struct ArtificialMaxBadOrderDomain
131 using State = ArtificialState;
132 using Move = ArtificialMove;
133 using Objective =
int;
135 bool is_complete(
const State &state)
const
137 return state.depth == 2;
140 Objective objective_value(
const State &state)
const
145 Objective bound(
const State &state)
const
152 default:
return state.value;
156 void apply(State &state,
const Move &move)
const
158 state.code = move.target_code;
159 state.value += move.delta;
163 void undo(State &state,
const Move &move)
const
166 state.value -= move.delta;
170 template <
typename Visitor>
171 bool for_each_successor(
const State &state,
Visitor visit)
const
173 if (state.depth >= 2)
181 return visit(Move{2, 0,
'L'});
186 return visit(Move{5, 9,
'R'});
191 return visit(Move{7, 4,
'R'});
199struct ArtificialMaxVisitedDomain : ArtificialMaxDomain
201 using State_Key =
int;
203 [[
nodiscard]] State_Key state_key(
const State &state)
const noexcept
209struct ArtificialMinDomain
211 using State = ArtificialState;
212 using Move = ArtificialMove;
213 using Objective =
int;
215 bool is_complete(
const State &state)
const
217 return state.depth == 2;
220 Objective objective_value(
const State &state)
const
225 Objective bound(
const State &state)
const
232 default:
return state.value;
236 void apply(State &state,
const Move &move)
const
238 state.code = move.target_code;
239 state.value += move.delta;
243 void undo(State &state,
const Move &move)
const
246 state.value -= move.delta;
250 template <
typename Visitor>
251 bool for_each_successor(
const State &state,
Visitor visit)
const
253 if (state.depth >= 2)
261 return visit(Move{3, 0,
'R'});
266 return visit(Move{5, 6,
'R'});
271 return visit(Move{7, 9,
'R'});
287 : index(0), weight(0), value(0), chosen(n, 0)
293class KnapsackBBDomain
309 suffix_values_(items.
size() + 1, 0.0),
313 for (
size_t i = items_.size(); i > 0; --i)
314 suffix_values_[i - 1] = suffix_values_[i] + items_[i - 1].value;
317 bool is_complete(
const State &state)
const
319 return state.index == items_.size();
322 Objective objective_value(
const State &state)
const
327 Objective bound(
const State &state)
const
329 if (
not use_fractional_bound_)
330 return state.value + suffix_values_[state.index];
333 int remaining = capacity_ - state.weight;
335 for (
size_t i = state.index; i < items_.size()
and remaining > 0; ++i)
336 if (items_[i].weight <= remaining)
339 remaining -= items_[i].weight;
343 optimistic += items_[i].value * (
static_cast<Objective
>(remaining)/
344 static_cast<Objective
>(items_[i].weight));
351 void apply(State &state,
const Move &move)
const
355 state.weight += items_[state.index].weight;
356 state.value += items_[state.index].value;
357 state.chosen[state.index] = 1;
360 state.chosen[state.index] = 0;
365 void undo(State &state,
const Move &move)
const
370 state.weight -= items_[state.index].weight;
371 state.value -= items_[state.index].value;
374 state.chosen[state.index] = 0;
377 template <
typename Visitor>
378 bool for_each_successor(
const State &state,
Visitor visit)
const
380 if (state.index >= items_.size())
383 if (state.weight + items_[state.index].weight <= capacity_)
387 return visit(Move{
false});
394 bool use_fractional_bound_ =
true;
397struct AssignmentState
404 explicit AssignmentState(
const size_t n = 0)
405 : row(0), total_cost(0), used_columns(n, 0), assigned_column(n, -1)
411class AssignmentBBDomain
419 using State = AssignmentState;
420 using Objective =
int;
428 bool is_complete(
const State &state)
const
430 return state.row == costs_.size();
433 Objective objective_value(
const State &state)
const
435 return state.total_cost;
438 Objective bound(
const State &state)
const
442 for (
size_t row = state.row; row < costs_.size(); ++row)
445 for (
size_t col = 0; col < costs_[row].size(); ++col)
447 best = costs_[row][col];
455 void apply(State &state,
const Move &move)
const
457 state.total_cost += costs_[state.row][move.col];
458 state.used_columns[move.col] = 1;
459 state.assigned_column[state.row] =
static_cast<int>(move.col);
463 void undo(State &state,
const Move &move)
const
466 state.total_cost -= costs_[state.row][move.col];
467 state.used_columns[move.col] = 0;
468 state.assigned_column[state.row] = -1;
471 template <
typename Visitor>
472 bool for_each_successor(
const State &state,
Visitor visit)
const
474 if (state.row >= costs_.size())
477 for (
size_t col = 0; col < costs_[state.row].size(); ++col)
488struct ThrowingApplyState
490 std::shared_ptr<bool> undo_called;
494struct ThrowingApplyMove
499struct ThrowingApplyBBDomain
501 using State = ThrowingApplyState;
502 using Move = ThrowingApplyMove;
503 using Objective =
int;
505 bool is_complete(
const State &state)
const
507 return state.node == 1;
510 Objective objective_value(
const State &)
const
515 Objective bound(
const State &)
const
520 void apply(State &state,
const Move &move)
const
528 void undo(State &state,
const Move &)
const
530 *state.undo_called =
true;
534 template <
typename Visitor>
535 bool for_each_successor(
const State &state,
Visitor visit)
const
540 return visit(Move{
true});
544struct ThrowingApplyVisitedBBDomain : ThrowingApplyBBDomain
546 using State_Key = size_t;
548 [[
nodiscard]] State_Key state_key(
const State &state)
const noexcept
567 for (
const auto &move : path)
576 if (row ==
costs.size())
580 for (
size_t col = 0; col <
costs[row].size(); ++col)
598struct IncrementalBoundDomain
600 using State = ArtificialState;
601 using Move = ArtificialMove;
602 using Objective =
int;
604 mutable size_t bound_after_calls = 0;
605 mutable size_t apply_calls = 0;
607 bool is_complete(
const State &state)
const
609 return state.depth == 2;
612 Objective objective_value(
const State &state)
const
617 Objective bound(
const State &state)
const
624 default:
return state.value;
628 Objective
bound_after(
const State &,
const Move &move)
const
632 switch (move.target_code)
636 default:
return move.delta;
640 void apply(State &state,
const Move &move)
const
643 state.code = move.target_code;
644 state.value += move.delta;
648 void undo(State &state,
const Move &move)
const
651 state.value -= move.delta;
655 template <
typename Visitor>
656 bool for_each_successor(
const State &state,
Visitor visit)
const
658 if (state.depth >= 2)
666 return visit(Move{3, 0,
'R'});
671 return visit(Move{5, 9,
'R'});
676 return visit(Move{7, 4,
'R'});
695 first.objective_value = 5;
705 ArtificialMaxDomain domain;
712 EXPECT_EQ(result.incumbent.best_value(), 9);
714 EXPECT_EQ(result.stats.solutions_found, 2u);
715 EXPECT_EQ(result.stats.pruned_by_bound, 1u);
716 EXPECT_EQ(result.stats.incumbent_updates, 2u);
722 ArtificialMaxVisitedDomain domain;
726 auto result =
engine.search(ArtificialState{}, visited);
735 ArtificialMinDomain domain;
738 policy.
strategy = ExplorationPolicy::Strategy::Best_First;
741 auto result =
engine.search(ArtificialState{});
745 EXPECT_EQ(result.incumbent.best_value(), 6);
747 EXPECT_GT(result.stats.pruned_by_bound, 0u);
752 ArtificialMaxBadOrderDomain domain;
761 best_policy.strategy = ExplorationPolicy::Strategy::Best_First;
779 auto undo_called = std::make_shared<bool>(
false);
780 ThrowingApplyBBDomain domain;
786 EXPECT_THROW((
void)
engine.search(ThrowingApplyState{undo_called, 0}), std::runtime_error);
792 auto undo_called = std::make_shared<bool>(
false);
793 ThrowingApplyBBDomain domain;
796 EXPECT_THROW((
void)
engine.search(ThrowingApplyState{undo_called, 0}), std::runtime_error);
802 auto undo_called = std::make_shared<bool>(
false);
803 ThrowingApplyVisitedBBDomain domain;
819 void apply(State &state,
const Move &move)
const
825 Objective
bound(
const State &state)
const
835 auto undo_called = std::make_shared<bool>(
false);
839 EXPECT_THROW((
void)
engine.search(ThrowingApplyState{undo_called, 0}), std::runtime_error);
854 auto undo_called = std::make_shared<bool>(
false);
860 EXPECT_THROW((
void)
engine.search(ThrowingApplyState{undo_called, 0}, visited), std::runtime_error);
865 *undo_called =
false;
866 EXPECT_THROW((
void)
engine.search(ThrowingApplyState{undo_called, 0}, visited), std::runtime_error);
873 ArtificialMaxDomain domain;
881 EXPECT_EQ(result.stats.solutions_found, 1u);
882 EXPECT_EQ(result.incumbent.best_value(), 7);
889 {2, 40.0}, {5, 30.0}, {10, 50.0}, {5, 10.0}
891 constexpr int capacity = 16;
893 KnapsackBBDomain domain(items, capacity,
true);
905 {2, 40.0}, {5, 30.0}, {10, 50.0}, {5, 10.0}
907 constexpr int capacity = 16;
909 KnapsackBBDomain domain(items, capacity,
true);
913 policy.
strategy = ExplorationPolicy::Strategy::Best_First;
924 {10, 100.0}, {9, 80.0}, {9, 80.0}, {9, 80.0}
926 constexpr int capacity = 10;
948 AssignmentBBDomain domain(
costs);
962 EXPECT_EQ(result.incumbent.best_value(), 13);
974 AssignmentBBDomain domain(
costs);
984 policy.
strategy = ExplorationPolicy::Strategy::Best_First;
1002 ArtificialMaxDomain domain;
1010 auto result =
engine.search(ArtificialState{});
1013 EXPECT_GT(result.stats.pruned_by_depth, 0u);
1020 ArtificialMaxDomain domain;
1028 auto result =
engine.search(ArtificialState{});
1031 EXPECT_EQ(result.stats.expanded_states, 1u);
1040 {2, 40.0}, {5, 30.0}, {10, 50.0}
1042 constexpr int capacity = 0;
1044 KnapsackBBDomain domain(items, capacity,
true);
1057 IncrementalBoundDomain domain;
1063 auto result =
engine.search(ArtificialState{});
1067 EXPECT_EQ(result.incumbent.best_value(), 9);
1073 EXPECT_GT(result.stats.move_ordering.priority_estimates, 0u);
Classical knapsack problem variants (0/1, unbounded, bounded).
Umbrella header for the implicit state-space search framework.
#define ah_runtime_error()
Throws std::runtime_error unconditionally.
Simple dynamic array with automatic resizing and functional operations.
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Reusable branch-and-bound engine over implicit state spaces.
static ExplorationPolicy default_policy() noexcept
Return the default branch-and-bound exploration policy.
Result search(State initial_state)
Execute branch and bound and keep only the incumbent.
Global incumbent manager for branch and bound.
Collector that stores accepted solutions in an Aleph list.
Main namespace for Aleph-w library functions.
and
Check uniqueness with explicit hash + equality functors.
size_t size(Node *root) noexcept
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.
std::string code(Node *root)
Compute a string with the Lukasiewicz`s word of a tree.
auto branch_and_bound_search(Domain domain, typename Domain::State initial_state, ExplorationPolicy policy=Branch_And_Bound< Domain, ObjectivePolicy >::default_policy(), SearchLimits limits={}, ObjectivePolicy objective={})
Convenience wrapper for one-shot branch and bound.
Exploration controls shared across engines.
bool stop_at_first_solution
Stop when the first goal is found.
Strategy strategy
Traversal strategy.
MoveOrderingMode move_ordering
Successor-ordering mode.
An item for knapsack problems.
Objective policy for maximization problems.
Objective policy for minimization problems.
Snapshot of a complete optimization solution.
Hard bounds applied by the search engine.
size_t max_expansions
Maximum expanded states.
size_t max_depth
Maximum expansion depth.
void apply(State &state, const Move &move) const
bool is_complete(const State &) const
Objective bound(const State &state) const
State_Key state_key(const State &state) const noexcept