Wildmeshing Toolkit
Loading...
Searching...
No Matches
Scheduler.cpp
Go to the documentation of this file.
1#include "Scheduler.hpp"
2
9
10#include <polysolve/Utils.hpp>
11
12#ifdef __GNUC__
13#pragma GCC diagnostic push
14#pragma GCC diagnostic ignored "-Wredundant-decls"
15#endif
16#include <tbb/parallel_for.h>
17#ifdef __GNUC__
18#pragma GCC diagnostic pop
19#endif
20
21// #include <tbb/task_arena.h>
22#include <atomic>
23
24
25#include <algorithm>
26#include <random>
27
28namespace wmtk {
29
30Scheduler::Scheduler() = default;
31Scheduler::~Scheduler() = default;
32
34{
36 std::vector<simplex::Simplex> simplices;
37 // op.reserve_enough_simplices();
38
39 const auto type = op.primitive_type();
40 {
41 POLYSOLVE_SCOPED_STOPWATCH("Collecting primitives", res.collecting_time, logger());
42
43 const auto tups = op.mesh().get_all(type);
44 simplices =
46 }
47
48 logger().debug("Executing on {} simplices", simplices.size());
49 std::vector<std::pair<int64_t, double>> order;
50
51 {
52 POLYSOLVE_SCOPED_STOPWATCH("Sorting", res.sorting_time, logger());
53 if (op.use_random_priority()) {
54 std::mt19937 gen(utils::get_random_seed());
55
56 std::shuffle(simplices.begin(), simplices.end(), gen);
57 } else {
58 order.reserve(simplices.size());
59 for (int64_t i = 0; i < simplices.size(); ++i) {
60 order.emplace_back(i, op.priority(simplices[i]));
61 }
62
63 std::stable_sort(order.begin(), order.end(), [](const auto& s_a, const auto& s_b) {
64 return s_a.second < s_b.second;
65 });
66 }
67 }
68
69
70 {
71 POLYSOLVE_SCOPED_STOPWATCH("Executing operation", res.executing_time, logger());
72
73 size_t total_simplices = simplices.size();
74
75 if (op.use_random_priority()) {
76 for (const auto& s : simplices) {
77 log(res, total_simplices);
78 auto mods = op(s);
79 if (mods.empty())
80 res.fail();
81 else
82 res.succeed();
83 }
84 } else {
85 for (const auto& o : order) {
86 log(res, total_simplices);
87 auto mods = op(simplices[o.first]);
88 if (mods.empty())
89 res.fail();
90 else
91 res.succeed();
92 }
93 }
95 res.print_update_log(total_simplices);
96 }
97 }
98
99 // logger().debug(
100 // "Ran {} ops, {} succeeded, {} failed",
101 // res.number_of_performed_operations(),
102 // res.number_of_successful_operations(),
103 // res.number_of_failed_operations());
104
105 m_stats += res;
106
107 return res;
108}
109
112 const TypedAttributeHandle<char>& flag_handle)
113{
114 std::vector<simplex::Simplex> simplices;
115 const auto type = op.primitive_type();
116
117 auto flag_accessor = op.mesh().create_accessor(flag_handle);
118 auto tups = op.mesh().get_all(type);
119
120 SchedulerStats res;
121 int64_t success = -1;
122 std::vector<std::pair<int64_t, double>> order;
123
124 for (const auto& t : tups) {
125 flag_accessor.scalar_attribute(t) = char(1);
126 }
127
128 do {
129 SchedulerStats internal_stats;
130 // op.reserve_enough_simplices();
131
132 {
133 POLYSOLVE_SCOPED_STOPWATCH(
134 "Collecting primitives",
135 internal_stats.collecting_time,
136 logger());
137
138 tups = op.mesh().get_all(type);
139 const auto n_primitives = tups.size();
140 tups.erase(
141 std::remove_if(
142 tups.begin(),
143 tups.end(),
144 [&](const Tuple& t) { return flag_accessor.scalar_attribute(t) == char(0); }),
145 tups.end());
146 for (const auto& t : tups) {
147 flag_accessor.scalar_attribute(t) = char(0);
148 }
149
150 logger().debug("Processing {}/{}", tups.size(), n_primitives);
151
153 op.mesh(),
154 tups,
155 type);
156 }
157
158 {
159 POLYSOLVE_SCOPED_STOPWATCH("Sorting", internal_stats.sorting_time, logger());
160 if (op.use_random_priority()) {
161 std::mt19937 gen(utils::get_random_seed());
162
163 std::shuffle(simplices.begin(), simplices.end(), gen);
164 } else {
165 order.clear();
166 order.reserve(simplices.size());
167 for (int64_t i = 0; i < simplices.size(); ++i) {
168 order.emplace_back(i, op.priority(simplices[i]));
169 }
170
171 std::stable_sort(order.begin(), order.end(), [](const auto& s_a, const auto& s_b) {
172 return s_a.second < s_b.second;
173 });
174 }
175 }
176
177 {
178 size_t total_simplices = order.size();
179 POLYSOLVE_SCOPED_STOPWATCH(
180 "Executing operation",
181 internal_stats.executing_time,
182 logger());
183
184 if (op.use_random_priority()) {
185 for (const auto& s : simplices) {
186 log(internal_stats, total_simplices);
187 auto mods = op(s);
188 // assert(wmtk::multimesh::utils::check_child_maps_valid(op.mesh()));
189 if (mods.empty()) {
190 res.fail();
191 } else {
192 res.succeed();
193 }
194 }
195 } else {
196 for (const auto& o : order) {
197 log(internal_stats, total_simplices);
198 auto mods = op(simplices[o.first]);
199 // assert(wmtk::multimesh::utils::check_child_maps_valid(op.mesh()));
200 if (mods.empty()) {
201 internal_stats.fail();
202 } else {
203 internal_stats.succeed();
204 }
205 }
206 }
207 }
208
209 success = internal_stats.number_of_successful_operations();
210 res += internal_stats;
211 res.sub_stats.push_back(internal_stats);
212 m_stats += internal_stats;
213 m_stats.sub_stats.push_back(internal_stats);
214 } while (success > 0);
215
216 // // reset flag to 1, not necessaty
217 // auto tups = op.mesh().get_all(type);
218 // for (const auto& t : tups) {
219 // flag_accessor.scalar_attribute(t) = char(1);
220 // }
221
222 return res;
223}
224
225int64_t first_available_color(std::vector<int64_t>& used_neighbor_coloring)
226{
227 if (used_neighbor_coloring.size() == 0) return 0;
228
229 std::sort(used_neighbor_coloring.begin(), used_neighbor_coloring.end());
230 int64_t color = 0;
231
232 for (const auto c : used_neighbor_coloring) {
233 if (color < c) {
234 return color;
235 } else {
236 color = c + 1;
237 }
238 }
239 return color;
240}
241
244 const TypedAttributeHandle<int64_t>& color_handle)
245{
246 // this only works on vertex operations
247 SchedulerStats res;
248 std::vector<std::vector<simplex::Simplex>> colored_simplices;
249 // op.reserve_enough_simplices();
250 auto color_accessor = op.mesh().create_accessor<int64_t>(color_handle);
251
252 const auto type = op.primitive_type();
253 assert(type == PrimitiveType::Vertex);
254
255 const auto tups = op.mesh().get_all(type);
256 int64_t color_max = -1;
257 {
258 POLYSOLVE_SCOPED_STOPWATCH("Collecting primitives", res.collecting_time, logger());
259
260
261 // reset all coloring as -1
262 // TODO: parallelfor
263 // for (const auto& v : tups) {
264 // color_accessor.scalar_attribute(v) = -1;
265 // }
266
267 tbb::parallel_for(
268 tbb::blocked_range<int64_t>(0, tups.size()),
269 [&](tbb::blocked_range<int64_t> r) {
270 for (int64_t i = r.begin(); i < r.end(); ++i) {
271 color_accessor.scalar_attribute(tups[i]) = -1;
272 }
273 });
274
275 // greedy coloring
276 std::vector<int64_t> used_colors;
277 for (const auto& v : tups) {
278 // get used colors in neighbors
279 used_colors.clear();
280 for (const auto& v_one_ring :
281 simplex::link(op.mesh(), simplex::Simplex::vertex(op.mesh(), v), false)
283 int64_t color = color_accessor.const_scalar_attribute(v_one_ring.tuple());
284 if (color > -1) {
285 used_colors.push_back(color);
286 }
287 }
288 int64_t c = first_available_color(used_colors);
289 color_accessor.scalar_attribute(v) = c;
290 color_max = std::max(color_max, c);
291
292 // push into vectors
293 if (c + 1 > colored_simplices.size()) {
294 colored_simplices.push_back({simplex::Simplex::vertex(op.mesh(), v)});
295 } else {
296 colored_simplices[c].push_back(simplex::Simplex::vertex(op.mesh(), v));
297 }
298 }
299
300 logger().info("Have {} colors among {} vertices", colored_simplices.size(), tups.size());
301
302 // debug code
303
304 {
305 for (const auto& v : tups) {
306 auto current_color = color_accessor.const_scalar_attribute(v);
307 if (current_color == -1) {
308 std::cout << "vertex not assigned color!!!" << std::endl;
309 }
310
311 for (const auto& v_one_ring :
314 if (current_color ==
315 color_accessor.const_scalar_attribute(v_one_ring.tuple())) {
316 std::cout << "adjacent vertices have same color!!!" << std::endl;
317 }
318 }
319 }
320 }
321 }
322
323 logger().debug("Executing on {} simplices", tups.size());
324
325 {
326 POLYSOLVE_SCOPED_STOPWATCH("Sorting", res.sorting_time, logger());
327
328 // do sth or nothing
329 }
330
331
332 {
333 POLYSOLVE_SCOPED_STOPWATCH("Executing operation", res.executing_time, logger());
334
335 std::atomic_int suc_cnt = 0;
336 std::atomic_int fail_cnt = 0;
337
338 for (int64_t i = 0; i < colored_simplices.size(); ++i) {
339 tbb::parallel_for(
340 tbb::blocked_range<int64_t>(0, colored_simplices[i].size()),
341 [&](tbb::blocked_range<int64_t> r) {
342 for (int64_t k = r.begin(); k < r.end(); ++k) {
343 auto mods = op(colored_simplices[i][k]);
344 if (mods.empty()) {
345 fail_cnt++;
346 } else {
347 suc_cnt++;
348 }
349 }
350 });
351
352 // for (int64_t k = 0; k < colored_simplices[i].size(); ++k) {
353 // auto mods = op(colored_simplices[i][k]);
354 // if (mods.empty()) {
355 // fail_cnt++;
356 // } else {
357 // suc_cnt++;
358 // }
359 // }
360 }
361
362 res.m_num_op_success = suc_cnt;
363 res.m_num_op_fail = fail_cnt;
364 }
365
366 // logger().debug(
367 // "Ran {} ops, {} succeeded, {} failed",
368 // res.number_of_performed_operations(),
369 // res.number_of_successful_operations(),
370 // res.number_of_failed_operations());
371
372 m_stats += res;
373
374 return res;
375}
376
377void Scheduler::log(size_t total)
378{
379 log(m_stats, total);
380}
381void Scheduler::log(const SchedulerStats& stats, size_t total)
382{
383 if (m_update_frequency) {
384 const size_t freq = m_update_frequency.value();
385 size_t count = stats.number_of_performed_operations();
386 if (count % freq == 0) {
387 stats.print_update_log(total);
388 }
389 count++;
390 }
391}
392
393void Scheduler::set_update_frequency(std::optional<size_t>&& freq)
394{
395 m_update_frequency = std::move(freq);
396}
397
398
399void SchedulerStats::print_update_log(size_t total, spdlog::level::level_enum level) const
400{
401 logger().log(
402 level,
403 "{} success + {} fail = {} out of {}",
404 number_of_successful_operations(),
405 number_of_failed_operations(),
406 number_of_performed_operations(),
407 total);
408}
409
410} // namespace wmtk
attribute::Accessor< T, Mesh, D > create_accessor(const attribute::MeshAttributeHandle &handle)
std::vector< Tuple > get_all(PrimitiveType type) const
Generate a vector of Tuples from global vertex/edge/triangle/tetrahedron index.
Definition Mesh.cpp:18
void log(const size_t total)
SchedulerStats run_operation_on_all(operations::Operation &op)
Definition Scheduler.cpp:33
SchedulerStats run_operation_on_all_coloring(operations::Operation &op, const TypedAttributeHandle< int64_t > &color_handle)
SchedulerStats m_stats
std::optional< size_t > m_update_frequency
std::vector< SchedulerStats > sub_stats
Definition Scheduler.hpp:52
int64_t number_of_performed_operations() const
Returns the number of performed operations performed by the scheduler.
Definition Scheduler.hpp:30
int64_t number_of_successful_operations() const
Returns the number of successful operations performed by the scheduler.
Definition Scheduler.hpp:16
void print_update_log(size_t total, spdlog::level::level_enum=spdlog::level::info) const
The Tuple is the basic navigation tool in our mesh data structure.
Definition Tuple.hpp:19
Handle that represents attributes for some mesh.
virtual PrimitiveType primitive_type() const =0
const Mesh & mesh() const
Definition Operation.hpp:45
virtual double priority(const simplex::Simplex &simplex) const
Definition Operation.hpp:35
virtual bool use_random_priority() const
Definition Operation.hpp:40
const std::vector< Simplex > & simplex_vector() const
Return const reference to the simplex vector.
static Simplex vertex(const Mesh &m, const Tuple &t)
Definition Simplex.hpp:56
std::vector< Simplex > tuple_vector_to_homogeneous_simplex_vector(const Mesh &m, const std::vector< Tuple > &tups, PrimitiveType primitive)
SimplexCollection link(const Mesh &mesh, const simplex::Simplex &simplex, const bool sort_and_clean)
Definition link.cpp:84
SimplexCollection k_ring(const Mesh &mesh, const Simplex &simplex, int64_t k)
Definition k_ring.cpp:6
unsigned int get_random_seed()
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:58
int64_t first_available_color(std::vector< int64_t > &used_neighbor_coloring)