ZonoOpt 2.3.0
Loading...
Searching...
No Matches
BranchAndBound.hpp
Go to the documentation of this file.
1#ifndef ZONOOPT_BRANCH_AND_BOUND_
2#define ZONOOPT_BRANCH_AND_BOUND_
3
15#include <thread>
16#include <atomic>
17#include <mutex>
18#include <condition_variable>
19#include <sstream>
20#include <iomanip>
21#include <variant>
22#include <memory_resource>
23#include <cmath>
24#include <random>
25
26#include "BnbDataStructures.hpp"
28#include "ADMM.hpp"
29
30namespace ZonoOpt::detail
31{
32 class BranchAndBound
33 {
34 public:
35 explicit BranchAndBound(const MI_data& data);
36
37 // solve
38 OptSolution solve();
39
40 // branch and bound where all possible solutions are returned
41 std::pair<std::vector<OptSolution>, OptSolution> multi_solve(int max_sols = std::numeric_limits<int>::max());
42
43 // warmstart - only used for ADMM-FP
44 void warmstart(const Eigen::Vector<zono_float, -1>& xi_ws, const Eigen::Vector<zono_float, -1>& u_ws)
45 {
46 this->xi_ws = xi_ws;
47 this->u_ws = u_ws;
48 }
49
50 private:
51 struct NodeDeleter
52 {
53 std::pmr::synchronized_pool_resource* pool_ptr;
54
55 explicit NodeDeleter(std::pmr::synchronized_pool_resource* pool_ptr) : pool_ptr(pool_ptr)
56 {
57 }
58
59 void operator()(Node* node) const
60 {
61 if (node)
62 {
63 node->~Node();
64 pool_ptr->deallocate(node, sizeof(Node), alignof(Node));
65 }
66 }
67 };
68
69 struct NodeCompare
70 {
71 bool operator()(const std::unique_ptr<Node, NodeDeleter>& n1,
72 const std::unique_ptr<Node, NodeDeleter>& n2) const
73 {
74 return n1->solution.J > n2->solution.J; // min-heap on J
75 }
76 };
77
78 struct JThreadCompare
79 {
80 bool operator()(const std::pair<int, zono_float>& v1,
81 const std::pair<int, zono_float>& v2) const
82 {
83 if (v1.second != v2.second) return v1.second < v2.second;
84 return v1.first < v2.first;
85 }
86 };
87
88 template <typename T, typename Comp=std::less<T>>
89 struct ThreadGuard
90 {
91 ThreadSafeSet<T, Comp>& thread_tags;
92 T tag;
93 bool specified = false;
94
95 explicit ThreadGuard(ThreadSafeSet<T, Comp>& thread_tags): thread_tags(thread_tags)
96 {
97 }
98
99 void specify_tag(const T& tag)
100 {
101 if (!specified)
102 {
103 this->tag = tag;
104 this->specified = true;
105 this->thread_tags.add(tag);
106 }
107 }
108
109 ~ThreadGuard()
110 {
111 if (specified)
112 this->thread_tags.remove(tag);
113 }
114 };
115
116 const MI_data data;
117 std::pmr::synchronized_pool_resource pool;
118 NodeCompare comp;
119
120 PriorityQueuePrunable<std::unique_ptr<Node, NodeDeleter>, NodeCompare> node_queue; // priority queue for nodes
121 PriorityQueuePrunable<std::unique_ptr<Node, NodeDeleter>, NodeCompare> dive_queue; // dive queue for search mode 1
122 mutable std::mutex pq_mtx;
123 mutable std::mutex incumbent_mtx; // guards atomic check-and-update of incumbent (J_max, z, x, u, etc.)
124 std::condition_variable pq_cv_bnb, pq_cv_admm_fp;
125 // condition variables for branch-and-bound and ADMM-FP threads
126
127 bool multi_sol = false;
128 std::shared_ptr<ADMM_data> bnb_data, admm_fp_data; // data for branch-and-bound threads / ADMM-FP threads
129
130 std::atomic<bool> converged = false;
131 std::atomic<bool> done = false;
132 std::atomic<bool> feasible = false; // feasible solution found
133 std::atomic<bool> admm_fp_incumbent = false; // incumbent is from ADMM FP
134 std::atomic<long int> qp_iter = 0; // number of QP iterations
135 std::atomic<int> iter = 0; // number of iterations
136 std::atomic<int> iter_admm_fp = 0; // number of feasibility pump iterations
137 std::atomic<zono_float> J_max = std::numeric_limits<zono_float>::infinity(); // upper bound
138 ThreadSafeAccess<Eigen::Vector<zono_float, -1>> z, x, u; // solution vector
139 std::atomic<zono_float> primal_residual = std::numeric_limits<zono_float>::infinity();
140 std::atomic<zono_float> dual_residual = std::numeric_limits<zono_float>::infinity();
141 ThreadSafeIncrementable<double> total_startup_time{0.0};
142 ThreadSafeIncrementable<double> total_run_time{0.0};
143 ThreadSafeSet<std::pair<int, zono_float>, JThreadCompare> J_threads; // threads for J values
144 ThreadSafeVector<OptSolution> solutions; // solutions found
145 std::uniform_int_distribution<int> uniform_dist{0, std::numeric_limits<int>::max()};
146 std::mt19937 rng{0};
147
148 // warmstart variables
149 Eigen::Vector<zono_float, -1> xi_ws, u_ws;
150
151 // allocate nodes
152 std::unique_ptr<Node, NodeDeleter> make_node(const std::shared_ptr<ADMM_data>& admm_data);
153
154 std::unique_ptr<Node, NodeDeleter> clone_node(const std::unique_ptr<Node, NodeDeleter>& other);
155
156 // solver core
157 std::variant<OptSolution, std::pair<std::vector<OptSolution>, OptSolution>> solver_core(
158 int max_sols = std::numeric_limits<int>::max());
159
160 // solve node and branch
161 void solve_and_branch(const std::unique_ptr<Node, NodeDeleter>& node);
162
163 void admm_fp_solve(const std::unique_ptr<ADMM_FP_solver>& node);
164
165 // check if integer feasible, xb is vector of relaxed binary variables
166 bool is_integer_feasible(const Eigen::Ref<const Eigen::Vector<zono_float, -1>> xb) const;
167
168 // most fractional branching
169 void branch_most_frac(const std::unique_ptr<Node, NodeDeleter>& node);
170
171 // loop for multithreading
172 void worker_loop();
173
174 void admm_fp_loop(std::unique_ptr<ADMM_FP_solver>&& node);
175
176 // push node to queue
177 void push_node(std::unique_ptr<Node, NodeDeleter>&& node);
178
179 // push node to dive queue (search mode 1)
180 void push_dive_node(std::unique_ptr<Node, NodeDeleter>&& node);
181
182 // prune
183 void prune(zono_float J_prune);
184
185 // check if 2 solutions correspond to the same binaries
186 bool check_bin_equal(const OptSolution& sol1, const OptSolution& sol2) const;
187
188 // check whether integer variables are fully specified
189 bool is_box_integer(const Box& box) const;
190
191 // get lower bound
192 zono_float get_lower_bound();
193 };
194} // namespace ZonoOpt::detail
195
196
197#endif
Convex and mixed-integer ADMM implementations used within ZonoOpt.
Data structures for mixed-integer optimization in ZonoOpt library.
Optimization settings and solution data structures for ZonoOpt library.
#define zono_float
Defines the floating-point type used in ZonoOpt.
Definition ZonoOpt.hpp:45