Graphite
Loading...
Searching...
No Matches
graph.hpp
Go to the documentation of this file.
1
2#pragma once
3#include <graphite/factor.hpp>
4#include <graphite/stream.hpp>
5#include <graphite/vertex.hpp>
6#include <limits>
7#include <thrust/execution_policy.h>
8
9namespace graphite {
10
20template <typename T, typename S> class Graph {
21
22private:
23 std::vector<BaseVertexDescriptor<T, S> *> vertex_descriptors;
24 std::vector<BaseFactorDescriptor<T, S> *> factor_descriptors;
25 thrust::device_vector<T> b;
26 thrust::device_vector<T> jacobian_scales;
27 size_t hessian_column;
28 std::vector<size_t> hessian_offsets;
29 bool scale_jacobians;
30
31public:
32 Graph() : scale_jacobians(true) {}
33
34 size_t get_hessian_dimension() const { return hessian_column; }
35 size_t get_variable_dimension(const size_t block_index) const {
36 return hessian_offsets[block_index + 1] - hessian_offsets[block_index];
37 }
38 size_t get_num_block_columns() const { return hessian_offsets.size() - 1; }
39
40 const std::vector<size_t> &get_offset_vector() const {
41 return hessian_offsets;
42 }
43
44 thrust::device_vector<T> &get_b() { return b; }
45
46 std::vector<BaseVertexDescriptor<T, S> *> &get_vertex_descriptors() {
47 return vertex_descriptors;
48 }
49
50 std::vector<BaseFactorDescriptor<T, S> *> &get_factor_descriptors() {
51 return factor_descriptors;
52 }
53
54 thrust::device_vector<T> &get_jacobian_scales() { return jacobian_scales; }
55
56 template <typename V> void add_vertex_descriptor(V *descriptor) {
57 vertex_descriptors.push_back(descriptor);
58 }
59
60 template <typename F> void add_factor_descriptor(F *factor) {
61 factor_descriptors.push_back(factor);
62 }
63
64 template <typename D> void add_descriptor(D *descriptor) {
65 if constexpr (std::is_base_of<BaseVertexDescriptor<T, S>, D>::value) {
66 add_vertex_descriptor(descriptor);
67 } else if constexpr (std::is_base_of<BaseFactorDescriptor<T, S>,
68 D>::value) {
69 add_factor_descriptor(descriptor);
70 } else {
71 static_assert(std::is_base_of<BaseVertexDescriptor<T, S>, D>::value ||
72 std::is_base_of<BaseFactorDescriptor<T, S>, D>::value,
73 "You tried to add something strange to the graph.");
74 }
75 }
76
77 bool initialize_optimization(const uint8_t level) {
78
79 // For each vertex descriptor, take global to local id mapping and transform
80 // it into a Hessian column to local id mapping.
81
82 std::vector<std::pair<size_t, std::pair<size_t, size_t>>>
83 global_to_local_combined;
84
85 for (size_t i = 0; i < vertex_descriptors.size(); ++i) {
86 const auto &map = vertex_descriptors[i]->get_global_map();
87 for (const auto &entry : map) {
88 global_to_local_combined.push_back({entry.first, {i, entry.second}});
89 }
90 }
91
92 // Sort the combined list by global ID
93 std::sort(global_to_local_combined.begin(), global_to_local_combined.end(),
94 [](const auto &a, const auto &b) { return a.first < b.first; });
95
96 // Initialize device ids and copy over factor and current vertex state
97 for (auto &desc : factor_descriptors) {
98 desc->initialize_device_ids(level);
99 }
100 deactivate_unused_vertices(level);
101
102 // Assign Hessian columns to local indices
103 hessian_column = 0;
104 size_t hessian_block_index = 0;
105 hessian_offsets.clear();
106 for (const auto &entry : global_to_local_combined) {
107 if (vertex_descriptors[entry.second.first]->is_active(entry.first)) {
108 vertex_descriptors[entry.second.first]->set_hessian_column(
109 entry.first, hessian_column, hessian_block_index);
110 hessian_offsets.push_back(hessian_column);
111 hessian_column += vertex_descriptors[entry.second.first]->dimension();
112 hessian_block_index++;
113 }
114 }
115 hessian_offsets.push_back(hessian_column);
116
117 // Copy vertex values to device
118 for (auto &desc : vertex_descriptors) {
119 desc->to_device();
120 }
121
122 // Copy factors to device
123 for (auto &desc : factor_descriptors) {
124 desc->to_device();
125 }
126
127 // Initialize Jacobian storage
128 for (auto &f : factor_descriptors) {
129 f->initialize_jacobian_storage();
130 }
131
132 return true;
133 }
134
135 // Deactivates vertices of inactive factors
136 // Expects that vertices and factor states are finalized
137 void deactivate_unused_vertices(const uint8_t level) {
138
139 // Check for empty descriptors
140 for (size_t i = 0; i < vertex_descriptors.size(); ++i) {
141 if (vertex_descriptors[i]->count() == 0) {
142 std::cerr << "Error: Vertex descriptor " << i << " has no entries."
143 << std::endl;
144 }
145 }
146
147 for (size_t i = 0; i < factor_descriptors.size(); ++i) {
148 if (factor_descriptors[i]->active_count() == 0) {
149 std::cerr << "Error: Factor descriptor " << i << " has no entries."
150 << std::endl;
151 }
152 }
153
154 // For each vertex descriptor, set the state MSB to 0
155 for (auto &desc : vertex_descriptors) {
156 thrust::transform(
157 thrust::cuda::par_nosync.on(0), desc->get_active_state(),
158 desc->get_active_state() + desc->count(), desc->get_active_state(),
159 [] __device__(uint8_t state) { return state & 0x7F; });
160 }
161 // For each factor descriptor
162 // Go through each vertex descriptor and set the state MSB to 1 if the
163 // constraint is active
164 for (auto &desc : factor_descriptors) {
165 desc->flag_active_vertices_async(level); // stream 0
166 }
167 // For each vertex descriptor, MSB of the active state is XOR'd with 1
168 // (0->1, 1->0)
169 for (auto &desc : vertex_descriptors) {
170 thrust::transform(
171 thrust::cuda::par_nosync.on(0), desc->get_active_state(),
172 desc->get_active_state() + desc->count(), desc->get_active_state(),
173 [] __device__(uint8_t state) { return state ^ 0x80; });
174 }
175 cudaStreamSynchronize(0);
176 }
177
178 bool build_structure() {
179 // Allocate storage for solver vectors
180 const auto size_x = get_hessian_dimension();
181 b.resize(size_x);
182 jacobian_scales.resize(size_x);
183
184 return true;
185 }
186
187 void compute_error() {
188 for (auto &factor : factor_descriptors) {
189 factor->compute_error(); // TODO: Make non-autodiff version
190 }
191 cudaStreamSynchronize(0);
192 }
193
194 T chi2() {
195 T chi2 = static_cast<T>(0);
196 for (auto &factor : factor_descriptors) {
197 chi2 += factor->chi2();
198 }
199 return chi2;
200 }
201
202 void linearize(StreamPool &streams) {
203
204 for (auto &factor : factor_descriptors) {
205 // compute error
206 if (factor->use_autodiff() && (factor->store_jacobians() ||
207 !factor->supports_dynamic_jacobians())) {
208 factor->compute_error_autodiff(streams); // synchronous
209 } else {
210 factor->compute_error(); // synchronous
211 factor->compute_jacobians(streams); // synchronous
212 }
213 }
214 cudaStreamSynchronize(0);
215
216 // Compute chi2
217 chi2();
218
219 // Compute Jacobian scale
220 if (scale_jacobians) {
221 thrust::fill(jacobian_scales.begin(), jacobian_scales.end(), 0);
222 for (auto &factor : factor_descriptors) {
223 factor->compute_hessian_scalar_diagonal_async(
224 jacobian_scales.data().get(), nullptr);
225 }
226 cudaStreamSynchronize(0);
227
228 thrust::transform(
229 thrust::device, jacobian_scales.begin(), jacobian_scales.end(),
230 jacobian_scales.begin(), [] __device__(T value) {
231 const double denom = std::numeric_limits<double>::epsilon() +
232 sqrt(static_cast<double>(value));
233 // const double denom = 1.0
234 // + sqrt(static_cast<double>(value));
235 return static_cast<T>(1.0 / denom);
236 });
237 } else {
238 thrust::fill(jacobian_scales.begin(), jacobian_scales.end(), 1.0);
239 }
240
241 // Scale Jacobians
242 if (scale_jacobians) {
243 for (auto &factor : factor_descriptors) {
244 factor->scale_jacobians_async(jacobian_scales.data().get());
245 }
246 cudaStreamSynchronize(0);
247 }
248
249 // Calculate b=J^T * r
250 thrust::fill(thrust::cuda::par_nosync.on(0), b.begin(), b.end(), 0);
251 for (auto &fd : factor_descriptors) {
252 fd->compute_b_async(b.data().get(), jacobian_scales.data().get());
253 }
254
255 cudaStreamSynchronize(0);
256 }
257
258 void apply_update(const T *delta_x, StreamPool &streams) {
259 size_t i = 0;
260 for (auto &desc : vertex_descriptors) {
261 desc->apply_update_async(delta_x, jacobian_scales.data().get(),
262 streams.select(i));
263 i++;
264 }
265 streams.sync_n(i);
266 }
267
268 void backup_parameters() {
269
270 for (const auto &desc : vertex_descriptors) {
271 desc->backup_parameters_async();
272 }
273
274 cudaStreamSynchronize(0);
275 }
276
277 void revert_parameters() {
278
279 for (auto &desc : vertex_descriptors) {
280 desc->restore_parameters_async();
281 }
282
283 cudaStreamSynchronize(0);
284 }
285
286 void clear() {
287 vertex_descriptors.clear();
288 factor_descriptors.clear();
289 b.clear();
290 jacobian_scales.clear();
291 hessian_column = 0;
292 hessian_offsets.clear();
293 }
294
295 void scale_system(const bool enable_scaling) {
296 scale_jacobians = enable_scaling;
297 }
298};
299
307} // namespace graphite
Graph class which stores references to vertex and factor descriptors, and provides methods for optimi...
Definition graph.hpp:20
Definition stream.hpp:7