ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
Vector.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2014-2026 The ESPResSo project
3 *
4 * This file is part of ESPResSo.
5 *
6 * ESPResSo is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * ESPResSo is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#pragma once
21
22/**
23 * @file
24 *
25 * @brief Vector implementation and trait types
26 * for boost qvm interoperability.
27 */
28
29#include <boost/qvm/deduce_vec.hpp>
30#include <boost/qvm/vec_traits.hpp>
31
32#include "utils/Array.hpp"
33
34#include <algorithm>
35#include <cassert>
36#include <cmath>
37#include <concepts>
38#include <cstddef>
39#include <functional>
40#include <initializer_list>
41#include <iterator>
42#include <numeric>
43#include <ranges>
44#include <span>
45#include <type_traits>
46#include <vector>
47
48namespace Utils {
49
50template <typename T, std::size_t N> class Vector : public Array<T, N> {
51 using Base = Array<T, N>;
52
53public:
54 using Base::at;
55 using Base::Base;
56 using Base::operator[];
57 using Base::back;
58 using Base::begin;
59 using Base::cbegin;
60 using Base::cend;
61 using Base::data;
62 using Base::empty;
63 using Base::end;
64 using Base::fill;
65 using Base::front;
66 using Base::max_size;
67 using Base::size;
68
69 template <class U> struct is_vector : std::false_type {};
70 template <class U, std::size_t Np>
71 struct is_vector<Vector<U, Np>> : std::true_type {};
72
73 Vector() noexcept = default;
74 Vector(Vector const &) = default;
75 Vector &operator=(Vector const &) = default;
76
77 void swap(Vector &rhs) { std::ranges::swap_ranges(*this, rhs); }
78
79private:
80 constexpr void copy_init(T const *values) noexcept {
81 for (std::size_t i{0}; i != N; ++i) {
82 (*this)[i] = values[i];
83 }
84 }
85
86public:
87 // range-based ctor that excludes Vector<T,N> to avoid ambiguous calls with
88 // the copy ctor, move ctor and cast operator; std::ranges::input_range is
89 // not used due to conflicts with move assignment in recursive variant types
90 // and T[N] must be excluded to avoid shadowing the noexcept ctor
91 template <class Range>
92 requires(not is_vector<std::remove_cvref_t<Range>>::value and
93 not std::is_same_v<std::remove_cvref_t<Range>, T[N]>)
94 explicit constexpr Vector(Range &&rng)
95 : Vector(std::begin(rng), std::end(rng)) {}
96
97#if __cpp_lib_containers_ranges
98 template <std::ranges::input_range Range>
99 Vector(std::from_range_t, Range &&rng)
100 : Vector(std::begin(rng), std::end(rng)) {}
101#endif
102
103 explicit constexpr Vector(T const (&v)[N]) noexcept : Base() {
104 if constexpr (N != 0) {
105 copy_init(std::cbegin(v));
106 }
107 }
108
109 constexpr Vector(std::initializer_list<T> v) : Base() {
110 if (N != v.size()) {
111 throw std::length_error(
112 "Construction of Vector from Container of wrong length.");
113 }
114 if constexpr (N != 0) {
115 copy_init(v.begin());
116 }
117 }
118
119 template <typename InputIterator>
120 Vector(InputIterator first, InputIterator last) : Base() {
121 if (std::distance(first, last) == N) {
122 std::copy_n(first, N, begin());
123 } else {
124 throw std::length_error(
125 "Construction of Vector from Container of wrong length.");
126 }
127 }
128
129 /** @brief Create a vector that has all entries set to the same value. */
130 DEVICE_QUALIFIER static constexpr Vector<T, N>
131 broadcast(typename Base::value_type const &value) noexcept {
132 Vector<T, N> ret;
133 for (std::size_t i = 0u; i != N; ++i) {
134 ret[i] = value;
135 }
136 return ret;
137 }
138
139 std::vector<T> as_vector() const { return std::vector<T>(begin(), end()); }
140
141 operator std::vector<T>() const { return as_vector(); }
142
143 constexpr std::span<T, N> as_span() const {
144 return std::span<T, N>(const_cast<T *>(begin()), size());
145 }
146
147 constexpr operator std::span<T, N>() const { return as_span(); }
148
149 template <class U> explicit operator Vector<U, N>() const {
150 Vector<U, N> ret;
151
152 std::ranges::transform(*this, ret.begin(),
153 [](T const &e) { return static_cast<U>(e); });
154
155 return ret;
156 }
157
158 constexpr T norm2() const { return (*this) * (*this); }
159 T norm() const { return std::sqrt(norm2()); }
160
161 /*
162 * @brief Normalize the vector.
163 *
164 * Normalize the vector by its length,
165 * if not zero, otherwise the vector is unchanged.
166 */
168 auto const l = norm();
169 if (l != T(0)) {
170 *this /= l;
171 }
172
173 return *this;
174 }
175
176 /*
177 * @brief Return a normalized copy of the vector.
178 *
179 * Normalize the vector by its length,
180 * if not zero, otherwise the vector is unchanged.
181 */
183 auto const l = norm();
184 return (l != T(0)) ? (*this) / l : *this;
185 }
186};
187
188template <class T> using Vector3 = Vector<T, 3>;
189
190template <std::size_t N> using VectorXd = Vector<double, N>;
196
197template <std::size_t N> using VectorXf = Vector<float, N>;
199
200template <std::size_t N> using VectorXi = Vector<int, N>;
202
203namespace detail {
204template <std::size_t N, typename T, typename U, typename Op>
205auto binary_op(Vector<T, N> const &a, Vector<U, N> const &b, Op op) {
206 // we must use the non-range version std::transform for Vector<T, N> because:
207 // GCC 12 cannot inspect -Wmaybe-uninitialized through std::ranges::transform
208 // Clang/Xcode libc++ cannot select the binary form of std::ranges::transform
209 using R = decltype(std::declval<T>() + std::declval<U>());
210 Vector<R, N> ret;
211
212 // NOLINTNEXTLINE(modernize-use-ranges)
213 std::transform(std::begin(a), std::end(a), std::begin(b), std::begin(ret),
214 op);
215
216 return ret;
217}
218
219template <std::size_t N, typename T, typename Op>
220constexpr bool all_of(Vector<T, N> const &a, Vector<T, N> const &b, Op op) {
221 for (std::size_t i = 0u; i < N; ++i) {
222 if (not op(a[i], b[i])) {
223 return false;
224 }
225 }
226 return true;
227}
228} // namespace detail
229
230template <std::size_t N, typename T>
231constexpr bool operator<(Vector<T, N> const &a, Vector<T, N> const &b) {
232 return detail::all_of(a, b, std::less<T>());
233}
234
235template <std::size_t N, typename T>
236constexpr bool operator>(Vector<T, N> const &a, Vector<T, N> const &b) {
237 return detail::all_of(a, b, std::greater<T>());
238}
239
240template <std::size_t N, typename T>
241constexpr bool operator<=(Vector<T, N> const &a, Vector<T, N> const &b) {
242 return detail::all_of(a, b, std::less_equal<T>());
243}
244
245template <std::size_t N, typename T>
246constexpr bool operator>=(Vector<T, N> const &a, Vector<T, N> const &b) {
247 return detail::all_of(a, b, std::greater_equal<T>());
248}
249
250template <std::size_t N, typename T>
251constexpr bool operator==(Vector<T, N> const &a, Vector<T, N> const &b) {
252 return detail::all_of(a, b, std::equal_to<T>());
253}
254
255template <std::size_t N, typename T>
256constexpr bool operator!=(Vector<T, N> const &a, Vector<T, N> const &b) {
257 return not(a == b);
258}
259
260template <std::size_t N, typename T, typename U>
261auto operator+(Vector<T, N> const &a, Vector<U, N> const &b) {
262 return detail::binary_op(a, b, std::plus<>());
263}
264
265template <std::size_t N, typename T>
267 std::ranges::transform(a, b, std::begin(a), std::plus<T>());
268 return a;
269}
270
271template <std::size_t N, typename T, typename U>
272auto operator-(Vector<T, N> const &a, Vector<U, N> const &b) {
273 return detail::binary_op(a, b, std::minus<>());
274}
275
276template <std::size_t N, typename T>
278 Vector<T, N> ret;
279 std::ranges::transform(a, std::begin(ret), std::negate<T>());
280 return ret;
281}
282
283template <std::size_t N, typename T>
285 std::ranges::transform(a, b, std::begin(a), std::minus<T>());
286 return a;
287}
288
289/* Scalar multiplication */
290template <std::size_t N, typename T, class U>
291 requires(std::is_arithmetic_v<U>)
292auto operator*(U const &a, Vector<T, N> const &b) {
293 using R = decltype(a * std::declval<T>());
294 Vector<R, N> ret;
295 std::ranges::transform(b, std::begin(ret), [a](T const &v) { return a * v; });
296 return ret;
297}
298
299template <std::size_t N, typename T, class U>
300 requires(std::is_arithmetic_v<U>)
301auto operator*(Vector<T, N> const &a, U const &b) {
302 using R = decltype(std::declval<T>() * b);
303 Vector<R, N> ret;
304 std::ranges::transform(a, std::begin(ret), [b](T const &v) { return b * v; });
305 return ret;
306}
307
308template <std::size_t N, typename T>
309auto &operator*=(Vector<T, N> &b, T const &a) {
310 std::ranges::transform(b, std::begin(b), [a](T const &v) { return a * v; });
311 return b;
312}
313
314/* Scalar division */
315template <std::size_t N, typename T, class U>
316auto operator/(Vector<T, N> const &a, U const &b) {
317 using R = decltype(std::declval<T>() / b);
318 Vector<R, N> ret;
319 std::ranges::transform(a, std::begin(ret), [b](T const &v) { return v / b; });
320 return ret;
321}
322
323template <std::size_t N, typename T, class U>
324auto operator/(U const &a, Vector<T, N> const &b) {
325 using R = decltype(a / std::declval<T>());
326 Vector<R, N> ret;
327 std::ranges::transform(b, std::begin(ret), [a](T const &v) { return a / v; });
328 return ret;
329}
330
331template <std::size_t N, typename T>
332auto &operator/=(Vector<T, N> &a, T const &b) {
333 std::ranges::transform(a, std::begin(a), [b](T const &v) { return v / b; });
334 return a;
335}
336
337namespace detail {
338template <class T> using is_vector = Vector<int, 1>::is_vector<T>;
339} // namespace detail
340
341/* Scalar product */
342template <std::size_t N, typename T, class U>
343 requires(not(detail::is_vector<T>::value or detail::is_vector<U>::value))
344auto constexpr operator*(Vector<T, N> const &a, Vector<U, N> const &b) {
345 using R = decltype(std::declval<T>() * std::declval<U>());
346 // std::inner_product isn't always inlined on Intel CPUs even with -O3,
347 // but a for loop can be inlined
348 R acc{};
349 for (std::size_t i = 0u; i < N; ++i) {
350 acc += a[i] * b[i];
351 }
352 return acc;
353}
354
355template <std::size_t N, typename T, class U>
356 requires(std::is_integral_v<T> and std::is_integral_v<U>)
357auto operator%(Vector<T, N> const &a, Vector<U, N> const &b) {
358 using R = decltype(std::declval<T>() % std::declval<U>());
359 Vector<R, N> ret;
360 std::ranges::transform(a, b, std::begin(ret), std::modulus<>());
361 return ret;
362}
363
364/* Componentwise square root */
365template <std::size_t N, typename T> auto sqrt(Vector<T, N> const &a) {
366 using std::sqrt;
367 using R = decltype(sqrt(std::declval<T>()));
368 Vector<R, N> ret;
369 std::ranges::transform(a, ret.begin(), [](T const &v) { return sqrt(v); });
370 return ret;
371}
372
373template <class T>
375 return {a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2],
376 a[0] * b[1] - a[1] * b[0]};
377}
378
379// Product of array elements.
380template <class T, std::size_t N> T product(Vector<T, N> const &v) {
381 return std::accumulate(v.cbegin(), v.cend(), T{1}, std::multiplies<T>());
382}
383
384template <class T, class U, std::size_t N>
386 using R = decltype(std::declval<T>() * std::declval<U>());
387 auto constexpr proj = std::identity{}; // required by Clang/Xcode libc++
388 Vector<R, N> ret;
389 std::ranges::transform(a, b, ret.begin(), std::multiplies<>(), proj, proj);
390 return ret;
391}
392
393// specialization for when one or both operands is a scalar depending on
394// compile time features (e.g. when PARTICLE_ANISOTROPY is not enabled)
395template <typename T, typename U>
396 requires(not(detail::is_vector<T>::value and detail::is_vector<U>::value))
397auto hadamard_product(T const &a, U const &b) {
398 return a * b;
399}
400
401template <class T, class U, std::size_t N>
403 using R = decltype(std::declval<T>() / std::declval<U>());
404 auto constexpr proj = std::identity{}; // required by Clang/Xcode libc++
405 Vector<R, N> ret;
406 std::ranges::transform(a, b, std::begin(ret), std::divides<>(), proj, proj);
407 return ret;
408}
409
410// specialization for when one or both operands is a scalar depending on
411// compile time features (e.g. when PARTICLE_ANISOTROPY is not enabled)
412template <typename T, typename U>
413 requires(not(detail::is_vector<T>::value and detail::is_vector<U>::value))
414auto hadamard_division(T const &a, U const &b) {
415 return a / b;
416}
417
418template <typename T> Vector<T, 3> unit_vector(unsigned int i) {
419 if (i == 0u)
420 return {T{1}, T{0}, T{0}};
421 if (i == 1u)
422 return {T{0}, T{1}, T{0}};
423 if (i == 2u)
424 return {T{0}, T{0}, T{1}};
425 throw std::domain_error("coordinate out of range");
426}
427
428/**
429 * @brief Meta function to turn a Vector<T, 1> into T.
430 */
431template <typename T> struct decay_to_scalar {};
432template <typename T, std::size_t N> struct decay_to_scalar<Vector<T, N>> {
434};
435
436template <typename T> struct decay_to_scalar<Vector<T, 1>> {
437 using type = T;
438};
439
440template <std::size_t I, class T, std::size_t N>
441T &get(Vector<T, N> &a) noexcept {
442 return a[I];
443}
444
445template <std::size_t I, class T, std::size_t N>
446T const &get(Vector<T, N> const &a) noexcept {
447 return a[I];
448}
449
450} // namespace Utils
451
452template <std::size_t I, class T, std::size_t N>
453struct std::tuple_element<I, Utils::Vector<T, N>> {
454 static_assert(I < N, "Utils::Vector index must be in range");
455 using type = T;
456};
457
458template <class T, std::size_t N>
459struct std::tuple_size<Utils::Vector<T, N>>
460 : std::integral_constant<std::size_t, N> {};
461
462namespace boost::qvm {
463
464template <class T, std::size_t N> struct vec_traits<::Utils::Vector<T, N>> {
465
466 static constexpr std::size_t dim = N;
467 using scalar_type = T;
468
469 template <std::size_t I>
470 static constexpr inline scalar_type &write_element(::Utils::Vector<T, N> &v) {
471 return v[I];
472 }
473
474 template <std::size_t I>
475 static constexpr inline scalar_type
477 return v[I];
478 }
479
480 static inline scalar_type read_element_idx(std::size_t i,
481 ::Utils::Vector<T, N> const &v) {
482 return v[i];
483 }
484 static inline scalar_type &write_element_idx(std::size_t i,
486 return v[i];
487 }
488};
489
490template <typename T> struct deduce_vec<Utils::Vector<T, 3>, 3> {
492};
493
494} // namespace boost::qvm
495
498UTILS_ARRAY_BOOST_CLASS(Utils::Vector, N, object_serializable)
Array implementation with CUDA support.
#define UTILS_ARRAY_BOOST_MPI_T(Container, N)
Mark array types as MPI data types.
Definition array.hpp:50
#define UTILS_ARRAY_BOOST_BIT_S(Container, N)
Mark array types as MPI bitwise serializable.
Definition array.hpp:63
#define UTILS_ARRAY_BOOST_CLASS(Container, N, ImplementationLevel)
Redefinition of BOOST_CLASS_IMPLEMENTATION for array types.
Definition array.hpp:78
#define UTILS_ARRAY_BOOST_TRACK(Container, N, TrackingLevel)
Redefinition of BOOST_CLASS_TRACKING for array types.
Definition array.hpp:97
Vector() noexcept=default
Vector & normalize()
Definition Vector.hpp:167
T norm() const
Definition Vector.hpp:159
constexpr Vector(T const (&v)[N]) noexcept
Definition Vector.hpp:103
void swap(Vector &rhs)
Definition Vector.hpp:77
DEVICE_QUALIFIER constexpr iterator begin() noexcept
Definition Array.hpp:140
Vector normalized() const
Definition Vector.hpp:182
DEVICE_QUALIFIER constexpr const_iterator cbegin() const noexcept
Definition Array.hpp:148
std::vector< T > as_vector() const
Definition Vector.hpp:139
Vector(InputIterator first, InputIterator last)
Definition Vector.hpp:120
constexpr Vector(std::initializer_list< T > v)
Definition Vector.hpp:109
DEVICE_QUALIFIER constexpr const_iterator cend() const noexcept
Definition Array.hpp:160
DEVICE_QUALIFIER constexpr size_type size() const noexcept
Definition Array.hpp:166
constexpr std::span< T, N > as_span() const
Definition Vector.hpp:143
DEVICE_QUALIFIER constexpr iterator end() noexcept
Definition Array.hpp:152
static DEVICE_QUALIFIER constexpr Vector< T, N > broadcast(typename Base::value_type const &value) noexcept
Create a vector that has all entries set to the same value.
Definition Vector.hpp:131
constexpr Vector(Range &&rng)
Definition Vector.hpp:94
constexpr T norm2() const
Definition Vector.hpp:158
#define DEVICE_QUALIFIER
auto operator+(Vector< T, N > const &a, Vector< U, N > const &b)
Definition Vector.hpp:261
Vector< T, 3 > unit_vector(unsigned int i)
Definition Vector.hpp:418
T product(Vector< T, N > const &v)
Definition Vector.hpp:380
Vector< T, N > & operator-=(Vector< T, N > &a, Vector< T, N > const &b)
Definition Vector.hpp:284
constexpr bool operator<(Vector< T, N > const &a, Vector< T, N > const &b)
Definition Vector.hpp:231
constexpr bool operator>=(Vector< T, N > const &a, Vector< T, N > const &b)
Definition Vector.hpp:246
constexpr bool operator>(Vector< T, N > const &a, Vector< T, N > const &b)
Definition Vector.hpp:236
Vector< T, 3 > vector_product(Vector< T, 3 > const &a, Vector< T, 3 > const &b)
Definition Vector.hpp:374
auto & operator+=(Vector< T, N > &a, Vector< T, N > const &b)
Definition Vector.hpp:266
T & get(Array< T, N > &a) noexcept
Definition Array.hpp:209
auto operator/(Vector< T, N > const &a, U const &b)
Definition Vector.hpp:316
constexpr bool operator==(Vector< T, N > const &a, Vector< T, N > const &b)
Definition Vector.hpp:251
constexpr bool operator!=(Vector< T, N > const &a, Vector< T, N > const &b)
Definition Vector.hpp:256
constexpr bool operator<=(Vector< T, N > const &a, Vector< T, N > const &b)
Definition Vector.hpp:241
auto hadamard_division(Vector< T, N > const &a, Vector< U, N > const &b)
Definition Vector.hpp:402
auto & operator*=(Vector< T, N > &b, T const &a)
Definition Vector.hpp:309
auto hadamard_product(Vector< T, N > const &a, Vector< U, N > const &b)
Definition Vector.hpp:385
auto operator-(Vector< T, N > const &a, Vector< U, N > const &b)
Definition Vector.hpp:272
auto sqrt(Vector< T, N > const &a)
Definition Vector.hpp:365
auto & operator/=(Vector< T, N > &a, T const &b)
Definition Vector.hpp:332
STL namespace.
DEVICE_QUALIFIER constexpr reference at(size_type i)
Definition Array.hpp:97
DEVICE_QUALIFIER constexpr bool empty() const noexcept
Definition Array.hpp:164
DEVICE_QUALIFIER constexpr reference back()
Definition Array.hpp:126
DEVICE_QUALIFIER constexpr pointer data() noexcept
Definition Array.hpp:132
DEVICE_QUALIFIER constexpr size_type max_size() const noexcept
Definition Array.hpp:168
DEVICE_QUALIFIER constexpr iterator begin() noexcept
Definition Array.hpp:140
DEVICE_QUALIFIER constexpr const_iterator cbegin() const noexcept
Definition Array.hpp:148
DEVICE_QUALIFIER constexpr const_iterator cend() const noexcept
Definition Array.hpp:160
DEVICE_QUALIFIER constexpr size_type size() const noexcept
Definition Array.hpp:166
DEVICE_QUALIFIER constexpr reference front()
Definition Array.hpp:122
DEVICE_QUALIFIER constexpr iterator end() noexcept
Definition Array.hpp:152
DEVICE_QUALIFIER void fill(const value_type &value)
Definition Array.hpp:170
Meta function to turn a Vector<T, 1> into T.
Definition Vector.hpp:431
static constexpr scalar_type read_element(::Utils::Vector< T, N > const &v)
Definition Vector.hpp:476
static scalar_type & write_element_idx(std::size_t i, ::Utils::Vector< T, N > &v)
Definition Vector.hpp:484
static constexpr scalar_type & write_element(::Utils::Vector< T, N > &v)
Definition Vector.hpp:470
static scalar_type read_element_idx(std::size_t i, ::Utils::Vector< T, N > const &v)
Definition Vector.hpp:480