UFO 1.0.0
An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown
Loading...
Searching...
No Matches
algorithm.hpp
1
42#ifndef UFO_EXECUTION_ALGORITHM_HPP
43#define UFO_EXECUTION_ALGORITHM_HPP
44
45// UFO
46#include <ufo/execution/execution.hpp>
47
48// STL
49#include <algorithm>
50#include <atomic>
51#include <concepts>
52#include <functional>
53#include <iterator>
54#include <mutex>
55#include <ranges>
56#include <utility>
57
58namespace ufo
59{
60using execution::ExecutionPolicy;
61/**************************************************************************************
62| |
63| For each |
64| |
65**************************************************************************************/
66
79template <std::integral Index, class UnaryFunc, class Proj = std::identity>
80constexpr UnaryFunc for_each(Index first, Index last, UnaryFunc f, Proj proj = {})
81{
82 for (; last != first; ++first) {
83 std::invoke(f, std::invoke(proj, first));
84 }
85 return f;
86}
87
106template <ufo::execution::ExecutionPolicy T, std::integral Index, class UnaryFunc,
107 class Proj = std::identity>
108void for_each(T&& policy, Index first, Index last, UnaryFunc f, Proj proj = {})
109{
110 if constexpr (execution::STLBackend<T>) {
111 auto r = std::views::iota(first, last) | std::views::transform(std::move(proj));
112 std::for_each(execution::toSTL(std::forward<T>(policy)), std::ranges::begin(r),
113 std::ranges::end(r), f);
114 }
115#if defined(UFO_PAR_GCD)
116 else if constexpr (execution::GCDBackend<T>) {
117 dispatch_apply(static_cast<std::size_t>(last - first),
118 dispatch_get_global_queue(0, 0), ^(std::size_t i) {
119 std::invoke(f, std::invoke(proj, first + static_cast<Index>(i)));
120 });
121 }
122#endif
123#if defined(UFO_PAR_TBB)
124 else if constexpr (execution::TBBBackend<T>) {
125 oneapi::tbb::parallel_for(
126 first, last, [f, proj](Index i) { std::invoke(f, std::invoke(proj, i)); });
127 }
128#endif
129 else if constexpr (execution::OMPBackend<T>) {
130 auto s_first = static_cast<std::make_signed_t<Index>>(first);
131 auto s_last = static_cast<std::make_signed_t<Index>>(last);
132#pragma omp parallel for
133 for (auto i = s_first; i < s_last; ++i) {
134 std::invoke(f, std::invoke(proj, static_cast<Index>(i)));
135 }
136 } else {
137 std::unreachable();
138 }
139}
140
152template <std::input_iterator InputIt, class UnaryFunc, class Proj = std::identity>
153constexpr UnaryFunc for_each(InputIt first, InputIt last, UnaryFunc f, Proj proj = {})
154{
155 for (; first != last; ++first) {
156 std::invoke(f, std::invoke(proj, *first));
157 }
158 return f;
159}
160
181template <ufo::execution::ExecutionPolicy T, std::random_access_iterator RandomIt,
182 class UnaryFunc, class Proj = std::identity>
183void for_each(T&& policy, RandomIt first, RandomIt last, UnaryFunc f, Proj proj = {})
184{
185 if constexpr (execution::STLBackend<T>) {
186 for_each(
187 std::forward<T>(policy), std::size_t{},
188 static_cast<std::size_t>(std::distance(first, last)),
189 [first, f, proj](std::size_t i) { std::invoke(f, std::invoke(proj, first[i])); });
190 }
191#if defined(UFO_PAR_GCD)
192 else if constexpr (execution::GCDBackend<T>) {
193 std::size_t s = static_cast<std::size_t>(std::distance(first, last));
194 dispatch_apply(s, dispatch_get_global_queue(0, 0), ^(std::size_t i) {
195 std::invoke(f, std::invoke(proj, first[i]));
196 });
197 }
198#endif
199#if defined(UFO_PAR_TBB)
200 else if constexpr (execution::TBBBackend<T>) {
201 // TODO: Benchmark against parallel_for_each
202 std::size_t s = static_cast<std::size_t>(std::distance(first, last));
203 oneapi::tbb::parallel_for(std::size_t{}, s, [first, f, proj](std::size_t i) {
204 std::invoke(f, std::invoke(proj, first[i]));
205 });
206 }
207#endif
208 else if constexpr (execution::OMPBackend<T>) {
209 auto s = static_cast<std::make_signed_t<std::size_t>>(std::distance(first, last));
210#pragma omp parallel for
211 for (auto i = static_cast<decltype(s)>(0); i < s; ++i) {
212 std::invoke(f, std::invoke(proj, first[i]));
213 }
214 } else {
215 std::unreachable();
216 }
217}
218
228template <std::ranges::input_range Range, class UnaryFunc, class Proj = std::identity>
230constexpr std::ranges::for_each_result<std::ranges::iterator_t<Range>, UnaryFunc>
231for_each(Range&& r, UnaryFunc f, Proj proj = {})
232{
233 return std::ranges::for_each(std::forward<Range>(r), std::move(f), std::move(proj));
234}
235
246template <ufo::execution::ExecutionPolicy T, std::ranges::random_access_range Range,
247 class UnaryFunc, class Proj = std::identity>
248void for_each(T&& policy, Range&& r, UnaryFunc f, Proj proj = {})
249{
250 ufo::for_each(std::forward<T>(policy), std::ranges::begin(r), std::ranges::end(r),
251 std::move(f), std::move(proj));
252}
253
254/**************************************************************************************
255| |
256| Transform |
257| |
258**************************************************************************************/
259
273template <std::input_iterator InputIt, class OutputIt, class UnaryOp,
274 class Proj = std::identity>
275constexpr OutputIt transform(InputIt first1, InputIt last1, OutputIt d_first,
276 UnaryOp unary_op, Proj proj = {})
277{
278 for (; first1 != last1; ++first1, ++d_first) {
279 *d_first = std::invoke(unary_op, std::invoke(proj, *first1));
280 }
281 return d_first;
282}
283
299template <ufo::execution::ExecutionPolicy T, std::random_access_iterator RandomIt1,
300 std::random_access_iterator RandomIt2, class UnaryOp,
301 class Proj = std::identity>
302 requires(!std::input_or_output_iterator<UnaryOp>)
303
304RandomIt2 transform(T&& policy, RandomIt1 first1, RandomIt1 last1, RandomIt2 d_first,
305 UnaryOp unary_op, Proj proj = {})
306{
307 if constexpr (execution::STLBackend<T>) {
308 std::transform(execution::toSTL(std::forward<T>(policy)), first1, last1, d_first,
309 [&unary_op, &proj](auto&& x) {
310 return std::invoke(unary_op,
311 std::invoke(proj, std::forward<decltype(x)>(x)));
312 });
313 return d_first + std::distance(first1, last1);
314 } else {
315 std::size_t s = static_cast<std::size_t>(std::distance(first1, last1));
316 for_each(std::forward<T>(policy), std::size_t{}, s,
317 [first1, d_first, unary_op, proj](std::size_t i) {
318 d_first[i] = std::invoke(unary_op, std::invoke(proj, first1[i]));
319 });
320
321 return d_first + s;
322 }
323}
324
337template <std::ranges::input_range Range1, std::weakly_incrementable OutputIt,
338 class UnaryOp, class Proj = std::identity>
339 requires(
341 std::indirectly_writable<
342 OutputIt, std::indirect_result_t<
343 UnaryOp, std::projected<std::ranges::iterator_t<Range1>, Proj>>>)
344constexpr std::ranges::unary_transform_result<std::ranges::iterator_t<Range1>, OutputIt>
345transform(Range1&& r1, OutputIt d_first, UnaryOp unary_op, Proj proj = {})
346{
347 return std::ranges::transform(std::forward<Range1>(r1), d_first, unary_op,
348 std::move(proj));
349}
350
365template <ufo::execution::ExecutionPolicy T, std::ranges::random_access_range Range1,
366 std::random_access_iterator RandomIt2, class UnaryOp,
367 class Proj = std::identity>
368 requires(
369 std::indirectly_writable<
370 RandomIt2, std::indirect_result_t<
371 UnaryOp, std::projected<std::ranges::iterator_t<Range1>, Proj>>>)
372RandomIt2 transform(T&& policy, Range1&& r1, RandomIt2 d_first, UnaryOp unary_op,
373 Proj proj = {})
374{
375 return ufo::transform(std::forward<T>(policy), std::ranges::begin(r1),
376 std::ranges::end(r1), d_first, std::move(unary_op),
377 std::move(proj));
378}
379
396template <std::input_iterator InputIt1, std::input_iterator InputIt2, class OutputIt,
397 class BinaryOp, class Proj1 = std::identity, class Proj2 = std::identity>
398constexpr OutputIt transform(InputIt1 first1, InputIt1 last1, InputIt2 first2,
399 OutputIt d_first, BinaryOp binary_op, Proj1 proj1 = {},
400 Proj2 proj2 = {})
401{
402 for (; first1 != last1; ++first1, ++first2, ++d_first) {
403 *d_first =
404 std::invoke(binary_op, std::invoke(proj1, *first1), std::invoke(proj2, *first2));
405 }
406 return d_first;
407}
408
428template <ufo::execution::ExecutionPolicy T, std::random_access_iterator RandomIt1,
429 std::random_access_iterator RandomIt2, std::random_access_iterator RandomIt3,
430 class BinaryOp, class Proj1 = std::identity, class Proj2 = std::identity>
431RandomIt3 transform(T&& policy, RandomIt1 first1, RandomIt1 last1, RandomIt2 first2,
432 RandomIt3 d_first, BinaryOp binary_op, Proj1 proj1 = {},
433 Proj2 proj2 = {})
434{
435 if constexpr (execution::STLBackend<T>) {
436 std::transform(execution::toSTL(std::forward<T>(policy)), first1, last1, first2,
437 d_first, [&binary_op, &proj1, &proj2](auto&& x, auto&& y) {
438 return std::invoke(binary_op,
439 std::invoke(proj1, std::forward<decltype(x)>(x)),
440 std::invoke(proj2, std::forward<decltype(y)>(y)));
441 });
442 return d_first + std::distance(first1, last1);
443 } else {
444 std::size_t s = static_cast<std::size_t>(std::distance(first1, last1));
445 for_each(std::forward<T>(policy), std::size_t{}, s,
446 [first1, first2, d_first, binary_op, proj1, proj2](std::size_t i) {
447 d_first[i] = std::invoke(binary_op, std::invoke(proj1, first1[i]),
448 std::invoke(proj2, first2[i]));
449 });
450
451 return d_first + s;
452 }
453}
454
470template <std::ranges::input_range Range1, std::input_iterator InputIt2,
471 std::weakly_incrementable OutputIt, class BinaryOp, class Proj1 = std::identity,
472 class Proj2 = std::identity>
473 requires(
475 std::indirectly_writable<
476 OutputIt, std::indirect_result_t<
477 BinaryOp, std::projected<std::ranges::iterator_t<Range1>, Proj1>,
478 std::projected<InputIt2, Proj2>>>)
479constexpr std::ranges::binary_transform_result<std::ranges::iterator_t<Range1>, InputIt2,
480 OutputIt>
481transform(Range1&& r1, InputIt2 first2, OutputIt d_first, BinaryOp binary_op,
482 Proj1 proj1 = {}, Proj2 proj2 = {})
483{
484 // std::ranges::transform for binary expects a range for the second input too.
485 // But UFOMap provides an iterator. We can wrap it in an unbounded view if we know it's
486 // safe, or just implement it manually. Actually, std::ranges::transform (binary)
487 // doesn't have an iterator-based overload for the second range. So we implement it
488 // manually to maintain the same interface.
489 auto first1 = std::ranges::begin(r1);
490 auto last1 = std::ranges::end(r1);
491 for (; first1 != last1; ++first1, ++first2, ++d_first) {
492 *d_first =
493 std::invoke(binary_op, std::invoke(proj1, *first1), std::invoke(proj2, *first2));
494 }
495 return {std::move(first1), std::move(first2), std::move(d_first)};
496}
497
515template <ufo::execution::ExecutionPolicy T, std::ranges::random_access_range Range1,
516 std::random_access_iterator RandomIt2, std::random_access_iterator RandomIt3,
517 class BinaryOp, class Proj1 = std::identity, class Proj2 = std::identity>
518 requires(
519 std::indirectly_writable<
520 RandomIt3, std::indirect_result_t<
521 BinaryOp, std::projected<std::ranges::iterator_t<Range1>, Proj1>,
522 std::projected<RandomIt2, Proj2>>>)
523RandomIt3 transform(T&& policy, Range1&& r1, RandomIt2 first2, RandomIt3 d_first,
524 BinaryOp binary_op, Proj1 proj1 = {}, Proj2 proj2 = {})
525{
526 return ufo::transform(std::forward<T>(policy), std::ranges::begin(r1),
527 std::ranges::end(r1), first2, d_first, std::move(binary_op),
528 std::move(proj1), std::move(proj2));
529}
530/**************************************************************************************
531| |
532| Reduce |
533| |
534**************************************************************************************/
535
548template <ufo::execution::ExecutionPolicy P, std::input_iterator InputIt, class T>
549[[nodiscard]] T reduce(P&& policy, InputIt first, InputIt last, T init)
550{
551 if constexpr (execution::STLBackend<P>) {
552 return std::reduce(execution::toSTL(std::forward<P>(policy)), first, last, init);
553 } else {
554 // Generic implementation using for_each
555 // For parallel backends, we use a simple approach here for now.
556 // A more efficient implementation would use backend-specific features.
557 std::mutex m;
558 for_each(std::forward<P>(policy), first, last, [&](auto const& x) {
559 std::lock_guard lock(m);
560 init = init + x;
561 });
562 return init;
563 }
564}
565
576template <ufo::execution::ExecutionPolicy P, std::ranges::input_range Range, class T>
577[[nodiscard]] T reduce(P&& policy, Range&& r, T init)
578{
579 return reduce(std::forward<P>(policy), std::ranges::begin(r), std::ranges::end(r),
580 std::move(init));
581}
582
583/**************************************************************************************
584| |
585| Predicates |
586| |
587**************************************************************************************/
588
601template <ufo::execution::ExecutionPolicy P, std::input_iterator InputIt, class UnaryPred>
602[[nodiscard]] bool any_of(P&& policy, InputIt first, InputIt last, UnaryPred p)
603{
604 if constexpr (execution::STLBackend<P>) {
605 return std::any_of(execution::toSTL(std::forward<P>(policy)), first, last, p);
606 } else {
607 std::atomic<bool> found = false;
608 for_each(std::forward<P>(policy), first, last, [&](auto const& x) {
609 if (p(x)) {
610 found = true;
611 }
612 });
613 return found;
614 }
615}
616
628template <ufo::execution::ExecutionPolicy P, std::ranges::input_range Range,
629 class UnaryPred>
630[[nodiscard]] bool any_of(P&& policy, Range&& r, UnaryPred p)
631{
632 return any_of(std::forward<P>(policy), std::ranges::begin(r), std::ranges::end(r),
633 std::move(p));
634}
635
648template <ufo::execution::ExecutionPolicy P, std::input_iterator InputIt, class UnaryPred>
649[[nodiscard]] bool all_of(P&& policy, InputIt first, InputIt last, UnaryPred p)
650{
651 return !any_of(std::forward<P>(policy), first, last,
652 [&p](auto const& x) { return !p(x); });
653}
654
666template <ufo::execution::ExecutionPolicy P, std::ranges::input_range Range,
667 class UnaryPred>
668[[nodiscard]] bool all_of(P&& policy, Range&& r, UnaryPred p)
669{
670 return all_of(std::forward<P>(policy), std::ranges::begin(r), std::ranges::end(r),
671 std::move(p));
672}
673
674/**************************************************************************************
675| |
676| Searching |
677| |
678**************************************************************************************/
679
693template <ufo::execution::ExecutionPolicy P, std::random_access_iterator RandomIt,
694 class UnaryPred>
695[[nodiscard]] RandomIt find_if(P&& policy, RandomIt first, RandomIt last, UnaryPred p)
696{
697 if constexpr (execution::STLBackend<P>) {
698 return std::find_if(execution::toSTL(std::forward<P>(policy)), first, last, p);
699 } else {
700 std::atomic<std::size_t> min_idx =
701 static_cast<std::size_t>(std::distance(first, last));
702 for_each(std::forward<P>(policy), std::size_t{}, min_idx.load(), [&](std::size_t i) {
703 if (p(first[i])) {
704 std::size_t expected = min_idx.load();
705 while (i < expected && !min_idx.compare_exchange_weak(expected, i)) {
706 // Keep trying
707 }
708 }
709 });
710 return first + min_idx.load();
711 }
712}
713
726template <ufo::execution::ExecutionPolicy P, std::ranges::random_access_range Range,
727 class UnaryPred>
728[[nodiscard]] auto find_if(P&& policy, Range&& r, UnaryPred p)
729{
730 return find_if(std::forward<P>(policy), std::ranges::begin(r), std::ranges::end(r),
731 std::move(p));
732}
733
734/**************************************************************************************
735| |
736| Counting |
737| |
738**************************************************************************************/
739
752template <ufo::execution::ExecutionPolicy P, std::input_iterator InputIt, class UnaryPred>
753[[nodiscard]] std::size_t count_if(P&& policy, InputIt first, InputIt last, UnaryPred p)
754{
755 if constexpr (execution::STLBackend<P>) {
756 return std::count_if(execution::toSTL(std::forward<P>(policy)), first, last, p);
757 } else {
758 std::atomic<std::size_t> count = 0;
759 for_each(std::forward<P>(policy), first, last, [&](auto const& x) {
760 if (p(x)) {
761 ++count;
762 }
763 });
764 return count.load();
765 }
766}
767
779template <ufo::execution::ExecutionPolicy P, std::ranges::input_range Range,
780 class UnaryPred>
781[[nodiscard]] std::size_t count_if(P&& policy, Range&& r, UnaryPred p)
782{
783 return count_if(std::forward<P>(policy), std::ranges::begin(r), std::ranges::end(r),
784 std::move(p));
785}
786
787} // namespace ufo
788
789#endif // UFO_EXECUTION_ALGORITHM_HPP
All vision-related classes and functions.
Definition cloud.hpp:49
constexpr UnaryFunc for_each(Index first, Index last, UnaryFunc f, Proj proj={})
Applies f to each integer index in [first, last) sequentially.
Definition algorithm.hpp:80
bool all_of(P &&policy, InputIt first, InputIt last, UnaryPred p)
Checks if all elements in [first, last) satisfy p using the given execution policy.
RandomIt find_if(P &&policy, RandomIt first, RandomIt last, UnaryPred p)
Finds the first element in [first, last) satisfying p using the given execution policy.
bool any_of(P &&policy, InputIt first, InputIt last, UnaryPred p)
Checks if any element in [first, last) satisfies p using the given execution policy.
T reduce(P &&policy, InputIt first, InputIt last, T init)
Reduces the range [first, last) using init and the given execution policy.
std::size_t count_if(P &&policy, InputIt first, InputIt last, UnaryPred p)
Counts elements in [first, last) satisfying p using the given execution policy.
PointCloud< Dim, T, Rest... > transform(Transform< Dim, T > const &t, PointCloud< Dim, T, Rest... > pc)
Applies a rigid transform to every point position and returns the result.
Represents a closed interval [lower, upper] of a scalar type.
Definition range.hpp:66