ZonoOpt 2.2.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
25#include "BnbDataStructures.hpp"
27#include "ADMM.hpp"
28
29namespace ZonoOpt::detail
30{
31 class BranchAndBound
32 {
33 public:
34 explicit BranchAndBound(const MI_data& data);
35
36 // solve
37 OptSolution solve();
38
39 // branch and bound where all possible solutions are returned
40 std::pair<std::vector<OptSolution>, OptSolution> multi_solve(int max_sols = std::numeric_limits<int>::max());
41
42 // warmstart - only used for ADMM-FP
43 void warmstart(const Eigen::Vector<zono_float, -1>& xi_ws, const Eigen::Vector<zono_float, -1>& u_ws)
44 {
45 this->xi_ws = xi_ws;
46 this->u_ws = u_ws;
47 }
48
49 private:
50 struct NodeDeleter
51 {
52 std::pmr::synchronized_pool_resource* pool_ptr;
53
54 explicit NodeDeleter(std::pmr::synchronized_pool_resource* pool_ptr) : pool_ptr(pool_ptr)
55 {
56 }
57
58 void operator()(Node* node) const
59 {
60 if (node)
61 {
62 node->~Node();
63 pool_ptr->deallocate(node, sizeof(Node), alignof(Node));
64 }
65 }
66 };
67
68 struct NodeCompare
69 {
70 bool operator()(const std::unique_ptr<Node, NodeDeleter>& n1,
71 const std::unique_ptr<Node, NodeDeleter>& n2) const
72 {
73 if (n2->is_priority() && !n1->is_priority())
74 return true;
75 else if (!n2->is_priority() && n1->is_priority())
76 return false;
77 else
78 return n1->solution.J > n2->solution.J;
79 }
80 };
81
82 struct JThreadGuard
83 {
84 ThreadSafeMultiset& J_threads;
85 zono_float J = zero;
86 bool specified = false;
87
88 explicit JThreadGuard(ThreadSafeMultiset& J_threads): J_threads(J_threads)
89 {
90 }
91
92 void specify_J(zono_float J)
93 {
94 if (!specified)
95 {
96 this->J = J;
97 this->specified = true;
98 this->J_threads.add(J);
99 }
100 }
101
102 ~JThreadGuard()
103 {
104 if (specified)
105 this->J_threads.remove(J);
106 }
107 };
108
109 const MI_data data;
110 std::pmr::synchronized_pool_resource pool;
111 NodeCompare comp;
112
113 PriorityQueuePrunable<std::unique_ptr<Node, NodeDeleter>, NodeCompare> node_queue; // priority queue for nodes
114 mutable std::mutex pq_mtx;
115 std::condition_variable pq_cv_bnb, pq_cv_admm_fp;
116 // condition variables for branch-and-bound and ADMM-FP threads
117
118 bool multi_sol = false;
119 std::shared_ptr<ADMM_data> bnb_data, admm_fp_data; // data for branch-and-bound threads / ADMM-FP threads
120
121 std::atomic<bool> converged = false;
122 std::atomic<bool> done = false;
123 std::atomic<bool> feasible = false; // feasible solution found
124 std::atomic<bool> admm_fp_incumbent = false; // incumbent is from ADMM FP
125 std::atomic<long int> qp_iter = 0; // number of QP iterations
126 std::atomic<int> iter = 0; // number of iterations
127 std::atomic<int> iter_admm_fp = 0; // number of feasibility pump iterations
128 std::atomic<zono_float> J_max = std::numeric_limits<zono_float>::infinity(); // upper bound
129 ThreadSafeAccess<Eigen::Vector<zono_float, -1>> z, x, u; // solution vector
130 std::atomic<zono_float> primal_residual = std::numeric_limits<zono_float>::infinity();
131 std::atomic<zono_float> dual_residual = std::numeric_limits<zono_float>::infinity();
132 ThreadSafeIncrementable<double> total_startup_time{0.0};
133 ThreadSafeIncrementable<double> total_run_time{0.0};
134 ThreadSafeMultiset J_threads; // threads for J values
135 ThreadSafeVector<OptSolution> solutions; // solutions found
136
137 // warmstart variables
138 Eigen::Vector<zono_float, -1> xi_ws, u_ws;
139
140 // allocate nodes
141 std::unique_ptr<Node, NodeDeleter> make_node(const std::shared_ptr<ADMM_data>& admm_data);
142
143 std::unique_ptr<Node, NodeDeleter> clone_node(const std::unique_ptr<Node, NodeDeleter>& other);
144
145 // solver core
146 std::variant<OptSolution, std::pair<std::vector<OptSolution>, OptSolution>> solver_core(
147 int max_sols = std::numeric_limits<int>::max());
148
149 // solve node and branch
150 void solve_and_branch(const std::unique_ptr<Node, NodeDeleter>& node);
151
152 void admm_fp_solve(const std::unique_ptr<ADMM_FP_solver>& node);
153
154 // check if integer feasible, xb is vector of relaxed binary variables
155 bool is_integer_feasible(const Eigen::Ref<const Eigen::Vector<zono_float, -1>> xb) const;
156
157 // most fractional branching
158 void branch_most_frac(const std::unique_ptr<Node, NodeDeleter>& node);
159
160 // loop for multithreading
161 void worker_loop();
162
163 void admm_fp_loop(std::unique_ptr<ADMM_FP_solver>&& node);
164
165 // push node to queue
166 void push_node(std::unique_ptr<Node, NodeDeleter>&& node);
167
168 // prune
169 void prune(zono_float J_prune);
170
171 // check if 2 solutions correspond to the same binaries
172 bool check_bin_equal(const OptSolution& sol1, const OptSolution& sol2) const;
173 };
174} // namespace ZonoOpt::detail
175
176
177#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