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-2022 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::broadcast;
60 using Base::cbegin;
61 using Base::cend;
62 using Base::data;
63 using Base::empty;
64 using Base::end;
65 using Base::fill;
66 using Base::front;
67 using Base::max_size;
68 using Base::size;
69
70 template <class U> struct is_vector : std::false_type {};
71 template <class U, std::size_t Np>
72 struct is_vector<Vector<U, Np>> : std::true_type {};
73
74 Vector() noexcept = default;
75 Vector(Vector const &) = default;
76 Vector &operator=(Vector const &) = default;
77
78 void swap(Vector &rhs) { std::ranges::swap_ranges(*this, rhs); }
79
80private:
81 constexpr void copy_init(T const *values) noexcept {
82 for (std::size_t i{0}; i != N; ++i) {
83 (*this)[i] = values[i];
84 }
85 }
86
87public:
88 // range-based ctor that excludes Vector<T,N> to avoid ambiguous calls with
89 // the copy ctor, move ctor and cast operator; std::ranges::input_range is
90 // not used due to conflicts with move assignment in recursive variant types
91 // and T[N] must be excluded to avoid shadowing the noexcept ctor
92 template <class Range>
93 requires(not is_vector<std::remove_cvref_t<Range>>::value and
94 not std::is_same_v<std::remove_cvref_t<Range>, T[N]>)
95 explicit constexpr Vector(Range &&rng)
96 : Vector(std::begin(rng), std::end(rng)) {}
97
98#if __cpp_lib_containers_ranges
99 template <std::ranges::input_range Range>
100 Vector(std::from_range_t, Range &&rng)
101 : Vector(std::begin(rng), std::end(rng)) {}
102#endif
103
104 explicit constexpr Vector(T const (&v)[N]) noexcept : Base() {
105 if constexpr (N != 0) {
106 copy_init(std::cbegin(v));
107 }
108 }
109
110 constexpr Vector(std::initializer_list<T> v) : Base() {
111 if (N != v.size()) {
112 throw std::length_error(
113 "Construction of Vector from Container of wrong length.");
114 }
115 if constexpr (N != 0) {
116 copy_init(v.begin());
117 }
118 }
119
120 template <typename InputIterator>
121 Vector(InputIterator first, InputIterator last) : Base() {
122 if (std::distance(first, last) == N) {
123 std::copy_n(first, N, begin());
124 } else {
125 throw std::length_error(
126 "Construction of Vector from Container of wrong length.");
127 }
128 }
129
130 /** @brief Create a vector that has all entries set to the same value. */
131 DEVICE_QUALIFIER static constexpr Vector<T, N>
132 broadcast(typename Base::value_type const &value) noexcept {
133 Vector<T, N> ret;
134 for (std::size_t i = 0u; i != N; ++i) {
135 ret[i] = value;
136 }
137 return ret;
138 }
139
140 std::vector<T> as_vector() const { return std::vector<T>(begin(), end()); }
141
142 operator std::vector<T>() const { return as_vector(); }
143
144 constexpr std::span<T, N> as_span() const {
145 return std::span<T, N>(const_cast<T *>(begin()), size());
146 }
147
148 constexpr operator std::span<T, N>() const { return as_span(); }
149
150 template <class U> explicit operator Vector<U, N>() const {
151 Vector<U, N> ret;
152
153 std::ranges::transform(*this, ret.begin(),
154 [](T const &e) { return static_cast<U>(e); });
155
156 return ret;
157 }
158
159 constexpr T norm2() const { return (*this) * (*this); }
160 T norm() const { return std::sqrt(norm2()); }
161
162 /*
163 * @brief Normalize the vector.
164 *
165 * Normalize the vector by its length,
166 * if not zero, otherwise the vector is unchanged.
167 */
169 auto const l = norm();
170 if (l != T(0)) {
171 for (std::size_t i = 0u; i < N; ++i)
172 (*this)[i] /= l;
173 }
174
175 return *this;
176 }
177
178 Vector normalized() const { return (*this) / (*this).norm(); }
179};
180
181template <class T> using Vector3 = Vector<T, 3>;
182
183template <std::size_t N> using VectorXd = Vector<double, N>;
189
190template <std::size_t N> using VectorXf = Vector<float, N>;
192
193template <std::size_t N> using VectorXi = Vector<int, N>;
195
196namespace detail {
197template <std::size_t N, typename T, typename U, typename Op>
198auto binary_op(Vector<T, N> const &a, Vector<U, N> const &b, Op op) {
199 // we must use the non-range version std::transform for Vector<T, N> because:
200 // GCC 12 cannot inspect -Wmaybe-uninitialized through std::ranges::transform
201 // Clang/Xcode libc++ cannot select the binary form of std::ranges::transform
202 using R = decltype(std::declval<T>() + std::declval<U>());
203 Vector<R, N> ret;
204
205 // NOLINTNEXTLINE(modernize-use-ranges)
206 std::transform(std::begin(a), std::end(a), std::begin(b), std::begin(ret),
207 op);
208
209 return ret;
210}
211
212template <std::size_t N, typename T, typename Op>
213constexpr bool all_of(Vector<T, N> const &a, Vector<T, N> const &b, Op op) {
214 for (std::size_t i = 0u; i < N; ++i) {
215 if (not op(a[i], b[i])) {
216 return false;
217 }
218 }
219 return true;
220}
221} // namespace detail
222
223template <std::size_t N, typename T>
224constexpr bool operator<(Vector<T, N> const &a, Vector<T, N> const &b) {
225 return detail::all_of(a, b, std::less<T>());
226}
227
228template <std::size_t N, typename T>
229constexpr bool operator>(Vector<T, N> const &a, Vector<T, N> const &b) {
230 return detail::all_of(a, b, std::greater<T>());
231}
232
233template <std::size_t N, typename T>
234constexpr bool operator<=(Vector<T, N> const &a, Vector<T, N> const &b) {
235 return detail::all_of(a, b, std::less_equal<T>());
236}
237
238template <std::size_t N, typename T>
239constexpr bool operator>=(Vector<T, N> const &a, Vector<T, N> const &b) {
240 return detail::all_of(a, b, std::greater_equal<T>());
241}
242
243template <std::size_t N, typename T>
244constexpr bool operator==(Vector<T, N> const &a, Vector<T, N> const &b) {
245 return detail::all_of(a, b, std::equal_to<T>());
246}
247
248template <std::size_t N, typename T>
249constexpr bool operator!=(Vector<T, N> const &a, Vector<T, N> const &b) {
250 return not(a == b);
251}
252
253template <std::size_t N, typename T, typename U>
254auto operator+(Vector<T, N> const &a, Vector<U, N> const &b) {
255 return detail::binary_op(a, b, std::plus<>());
256}
257
258template <std::size_t N, typename T>
260 std::ranges::transform(a, b, std::begin(a), std::plus<T>());
261 return a;
262}
263
264template <std::size_t N, typename T, typename U>
265auto operator-(Vector<T, N> const &a, Vector<U, N> const &b) {
266 return detail::binary_op(a, b, std::minus<>());
267}
268
269template <std::size_t N, typename T>
271 Vector<T, N> ret;
272 std::ranges::transform(a, std::begin(ret), std::negate<T>());
273 return ret;
274}
275
276template <std::size_t N, typename T>
278 std::ranges::transform(a, b, std::begin(a), std::minus<T>());
279 return a;
280}
281
282/* Scalar multiplication */
283template <std::size_t N, typename T, class U>
284 requires(std::is_arithmetic_v<U>)
285auto operator*(U const &a, Vector<T, N> const &b) {
286 using R = decltype(a * std::declval<T>());
287 Vector<R, N> ret;
288 std::ranges::transform(b, std::begin(ret), [a](T const &v) { return a * v; });
289 return ret;
290}
291
292template <std::size_t N, typename T, class U>
293 requires(std::is_arithmetic_v<U>)
294auto operator*(Vector<T, N> const &a, U const &b) {
295 using R = decltype(std::declval<T>() * b);
296 Vector<R, N> ret;
297 std::ranges::transform(a, std::begin(ret), [b](T const &v) { return b * v; });
298 return ret;
299}
300
301template <std::size_t N, typename T>
302auto &operator*=(Vector<T, N> &b, T const &a) {
303 std::ranges::transform(b, std::begin(b), [a](T const &v) { return a * v; });
304 return b;
305}
306
307/* Scalar division */
308template <std::size_t N, typename T, class U>
309auto operator/(Vector<T, N> const &a, U const &b) {
310 using R = decltype(std::declval<T>() / b);
311 Vector<R, N> ret;
312 std::ranges::transform(a, std::begin(ret), [b](T const &v) { return v / b; });
313 return ret;
314}
315
316template <std::size_t N, typename T, class U>
317auto operator/(U const &a, Vector<T, N> const &b) {
318 using R = decltype(a / std::declval<T>());
319 Vector<R, N> ret;
320 std::ranges::transform(b, std::begin(ret), [a](T const &v) { return a / v; });
321 return ret;
322}
323
324template <std::size_t N, typename T>
325auto &operator/=(Vector<T, N> &a, T const &b) {
326 std::ranges::transform(a, std::begin(a), [b](T const &v) { return v / b; });
327 return a;
328}
329
330namespace detail {
331template <class T> using is_vector = Vector<int, 1>::is_vector<T>;
332} // namespace detail
333
334/* Scalar product */
335template <std::size_t N, typename T, class U>
336 requires(not(detail::is_vector<T>::value or detail::is_vector<U>::value))
337auto constexpr operator*(Vector<T, N> const &a, Vector<U, N> const &b) {
338 using R = decltype(std::declval<T>() * std::declval<U>());
339 // std::inner_product isn't always inlined on Intel CPUs even with -O3,
340 // but a for loop can be inlined
341 R acc{};
342 for (std::size_t i = 0u; i < N; ++i) {
343 acc += a[i] * b[i];
344 }
345 return acc;
346}
347
348template <std::size_t N, typename T, class U>
349 requires(std::is_integral_v<T> and std::is_integral_v<U>)
350auto operator%(Vector<T, N> const &a, Vector<U, N> const &b) {
351 using R = decltype(std::declval<T>() % std::declval<U>());
352 Vector<R, N> ret;
353 std::ranges::transform(a, b, std::begin(ret), std::modulus<>());
354 return ret;
355}
356
357/* Componentwise square root */
358template <std::size_t N, typename T> auto sqrt(Vector<T, N> const &a) {
359 using std::sqrt;
360 using R = decltype(sqrt(std::declval<T>()));
361 Vector<R, N> ret;
362 std::ranges::transform(a, ret.begin(), [](T const &v) { return sqrt(v); });
363 return ret;
364}
365
366template <class T>
368 return {a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2],
369 a[0] * b[1] - a[1] * b[0]};
370}
371
372// Product of array elements.
373template <class T, std::size_t N> T product(Vector<T, N> const &v) {
374 return std::accumulate(v.cbegin(), v.cend(), T{1}, std::multiplies<T>());
375}
376
377template <class T, class U, std::size_t N>
379 using R = decltype(std::declval<T>() * std::declval<U>());
380 auto constexpr proj = std::identity{}; // required by Clang/Xcode libc++
381 Vector<R, N> ret;
382 std::ranges::transform(a, b, ret.begin(), std::multiplies<>(), proj, proj);
383 return ret;
384}
385
386// specialization for when one or both operands is a scalar depending on
387// compile time features (e.g. when PARTICLE_ANISOTROPY is not enabled)
388template <typename T, typename U>
389 requires(not(detail::is_vector<T>::value and detail::is_vector<U>::value))
390auto hadamard_product(T const &a, U const &b) {
391 return a * b;
392}
393
394template <class T, class U, std::size_t N>
396 using R = decltype(std::declval<T>() / std::declval<U>());
397 auto constexpr proj = std::identity{}; // required by Clang/Xcode libc++
398 Vector<R, N> ret;
399 std::ranges::transform(a, b, std::begin(ret), std::divides<>(), proj, proj);
400 return ret;
401}
402
403// specialization for when one or both operands is a scalar depending on
404// compile time features (e.g. when PARTICLE_ANISOTROPY is not enabled)
405template <typename T, typename U>
406 requires(not(detail::is_vector<T>::value and detail::is_vector<U>::value))
407auto hadamard_division(T const &a, U const &b) {
408 return a / b;
409}
410
411template <typename T> Vector<T, 3> unit_vector(unsigned int i) {
412 if (i == 0u)
413 return {T{1}, T{0}, T{0}};
414 if (i == 1u)
415 return {T{0}, T{1}, T{0}};
416 if (i == 2u)
417 return {T{0}, T{0}, T{1}};
418 throw std::domain_error("coordinate out of range");
419}
420
421/**
422 * @brief Meta function to turn a Vector<T, 1> into T.
423 */
424template <typename T> struct decay_to_scalar {};
425template <typename T, std::size_t N> struct decay_to_scalar<Vector<T, N>> {
427};
428
429template <typename T> struct decay_to_scalar<Vector<T, 1>> {
430 using type = T;
431};
432
433template <std::size_t I, class T, std::size_t N>
434T &get(Vector<T, N> &a) noexcept {
435 return a[I];
436}
437
438template <std::size_t I, class T, std::size_t N>
439T const &get(Vector<T, N> const &a) noexcept {
440 return a[I];
441}
442
443} // namespace Utils
444
445template <std::size_t I, class T, std::size_t N>
446struct std::tuple_element<I, Utils::Vector<T, N>> {
447 static_assert(I < N, "Utils::Vector index must be in range");
448 using type = T;
449};
450
451template <class T, std::size_t N>
452struct std::tuple_size<Utils::Vector<T, N>>
453 : std::integral_constant<std::size_t, N> {};
454
455namespace boost::qvm {
456
457template <class T, std::size_t N> struct vec_traits<::Utils::Vector<T, N>> {
458
459 static constexpr std::size_t dim = N;
460 using scalar_type = T;
461
462 template <std::size_t I>
463 static constexpr inline scalar_type &write_element(::Utils::Vector<T, N> &v) {
464 return v[I];
465 }
466
467 template <std::size_t I>
468 static constexpr inline scalar_type
470 return v[I];
471 }
472
473 static inline scalar_type read_element_idx(std::size_t i,
474 ::Utils::Vector<T, N> const &v) {
475 return v[i];
476 }
477 static inline scalar_type &write_element_idx(std::size_t i,
479 return v[i];
480 }
481};
482
483template <typename T> struct deduce_vec<Utils::Vector<T, 3>, 3> {
485};
486
487} // namespace boost::qvm
488
491UTILS_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:168
T norm() const
Definition Vector.hpp:160
constexpr Vector(T const (&v)[N]) noexcept
Definition Vector.hpp:104
void swap(Vector &rhs)
Definition Vector.hpp:78
DEVICE_QUALIFIER constexpr iterator begin() noexcept
Definition Array.hpp:140
Vector normalized() const
Definition Vector.hpp:178
DEVICE_QUALIFIER constexpr const_iterator cbegin() const noexcept
Definition Array.hpp:148
std::vector< T > as_vector() const
Definition Vector.hpp:140
Vector(InputIterator first, InputIterator last)
Definition Vector.hpp:121
constexpr Vector(std::initializer_list< T > v)
Definition Vector.hpp:110
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:144
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:132
constexpr Vector(Range &&rng)
Definition Vector.hpp:95
constexpr T norm2() const
Definition Vector.hpp:159
#define DEVICE_QUALIFIER
auto operator+(Vector< T, N > const &a, Vector< U, N > const &b)
Definition Vector.hpp:254
Vector< T, 3 > unit_vector(unsigned int i)
Definition Vector.hpp:411
T product(Vector< T, N > const &v)
Definition Vector.hpp:373
Vector< T, N > & operator-=(Vector< T, N > &a, Vector< T, N > const &b)
Definition Vector.hpp:277
constexpr bool operator<(Vector< T, N > const &a, Vector< T, N > const &b)
Definition Vector.hpp:224
constexpr bool operator>=(Vector< T, N > const &a, Vector< T, N > const &b)
Definition Vector.hpp:239
constexpr bool operator>(Vector< T, N > const &a, Vector< T, N > const &b)
Definition Vector.hpp:229
Vector< T, 3 > vector_product(Vector< T, 3 > const &a, Vector< T, 3 > const &b)
Definition Vector.hpp:367
auto & operator+=(Vector< T, N > &a, Vector< T, N > const &b)
Definition Vector.hpp:259
T & get(Array< T, N > &a) noexcept
Definition Array.hpp:218
auto operator/(Vector< T, N > const &a, U const &b)
Definition Vector.hpp:309
constexpr bool operator==(Vector< T, N > const &a, Vector< T, N > const &b)
Definition Vector.hpp:244
constexpr bool operator!=(Vector< T, N > const &a, Vector< T, N > const &b)
Definition Vector.hpp:249
constexpr bool operator<=(Vector< T, N > const &a, Vector< T, N > const &b)
Definition Vector.hpp:234
auto hadamard_division(Vector< T, N > const &a, Vector< U, N > const &b)
Definition Vector.hpp:395
auto & operator*=(Vector< T, N > &b, T const &a)
Definition Vector.hpp:302
auto hadamard_product(Vector< T, N > const &a, Vector< U, N > const &b)
Definition Vector.hpp:378
auto operator-(Vector< T, N > const &a, Vector< U, N > const &b)
Definition Vector.hpp:265
auto sqrt(Vector< T, N > const &a)
Definition Vector.hpp:358
auto & operator/=(Vector< T, N > &a, T const &b)
Definition Vector.hpp:325
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
static DEVICE_QUALIFIER constexpr Array< T, N > broadcast(const value_type &value)
Definition Array.hpp:177
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:424
static constexpr scalar_type read_element(::Utils::Vector< T, N > const &v)
Definition Vector.hpp:469
static scalar_type & write_element_idx(std::size_t i, ::Utils::Vector< T, N > &v)
Definition Vector.hpp:477
static constexpr scalar_type & write_element(::Utils::Vector< T, N > &v)
Definition Vector.hpp:463
static scalar_type read_element_idx(std::size_t i, ::Utils::Vector< T, N > const &v)
Definition Vector.hpp:473