SuperDex Physics C++ API
Loading...
Searching...
No Matches
nd_array.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
23
24#include <algorithm>
25#include <array>
26#include <type_traits>
27
28namespace superdex {
29
30/**************************************************************************************************
31 MultiplyConstexpr - Return the product of all arguments as a constexpr. Used in later
32 declarations.
33*/
34template <typename A>
36 return a;
37}
38template <typename A, typename... ARGS>
39MOCHI_FORCE_INLINE constexpr A MultiplyConstexpr(A a, ARGS... args) {
40 return a * MultiplyConstexpr(args...);
41}
42
43/**************************************************************************************************
44 NdArray<T, D0, DIMS...>
45
46 Template for fixed-sized N-dimensional arrays like Int2, Real3, Matrix3x3r, etc...
47 Most methods can be used in constant (compile-time) expressions. NdArray currently has no
48 special alignment requirements and does not use SIMD explicitly.
49*/
50template <typename T, size_t D0 = 1, size_t... DIMS>
51class NdArray final {
52 public:
53 // Size of each dimension
54 constexpr static size_t num_dims = 1 + sizeof...(DIMS);
55 constexpr static size_t dims[num_dims] = {D0, DIMS...};
56 constexpr static size_t flattened_size = MultiplyConstexpr(D0, DIMS...);
57
58 // If this NdArray is 1D, then "value_type" is just "T". Else, it is an array of dimension (N-1)
59 using value_type = typename std::conditional_t<num_dims == 1, T, NdArray<T, DIMS...>>;
60
61 // Indicates the element type "T" in the last dimension of the array.
62 using element_type = T;
63
64 // Construct default
65 MOCHI_ANY MOCHI_FORCE_INLINE constexpr NdArray() = default;
66
67 // Construct from exactly D0 parameters (array elements that are convertible to T).
68 // NOLINTNEXTLINE(hicpp-explicit-conversions)
69 template <
70 typename... U,
71 std::enable_if_t<
72 (sizeof...(U) == D0) && std::conjunction_v<std::is_convertible<value_type, U>...>,
73 void*> = nullptr>
74 MOCHI_ANY MOCHI_FORCE_INLINE constexpr NdArray(U const&... p) : _data{p...} {}
75
76 // clang-format off
77 // These member names are lower case in keeping with the std library conventions.
78 MOCHI_ANY MOCHI_FORCE_INLINE constexpr static int size() { return D0; } // size of 1st dimension
79 MOCHI_ANY MOCHI_FORCE_INLINE constexpr value_type* data() { return _data; }
80 MOCHI_ANY MOCHI_FORCE_INLINE constexpr value_type const* data() const { return _data; }
81 MOCHI_ANY MOCHI_FORCE_INLINE constexpr auto begin() { return _data; }
82 MOCHI_ANY MOCHI_FORCE_INLINE constexpr auto begin() const { return _data; }
83 MOCHI_ANY MOCHI_FORCE_INLINE constexpr auto end() { return _data + D0; }
84 MOCHI_ANY MOCHI_FORCE_INLINE constexpr auto end() const { return _data + D0; }
85 // clang-format on
86
87 // Index operator
88 MOCHI_ANY MOCHI_FORCE_INLINE constexpr value_type const& operator[](size_t i) const;
90
91 private:
92 value_type _data[D0];
93};
94
95// Type-trait to detect NdArray types
96template <typename T>
97struct IsNdArray : std::false_type {};
98template <typename T, size_t D0, size_t... DIMS>
99struct IsNdArray<NdArray<T, D0, DIMS...>> : std::true_type {};
100template <typename T>
101static constexpr bool kIsNdArray = IsNdArray<std::decay_t<T>>::value;
102
103// Common 1D floating-point arrays types
110
111// For cases that always need 32-bit floats
115
116// For cases that always need 64-bit floats
120
121// Common 2D floating point array types
128
135
136// Common 1D integral array types
140
141// Common 4d floating point tensors
144
147
148/**************************************************************************************************
149 Loop unrolling macro
150*/
151
152// Unroll a statement BODY over i = 0, ..., D0-1 (manually unrolled when D0 <= 4). `D0` must be in
153// scope. BODY may reference `i` as an index and must not introduce its own `i`. BODY is expanded as
154// a single statement, so unguarded commas (outside parentheses) must be avoided.
155#define MOCHI_DETAILS_UNROLL_D0(BODY) \
156 if constexpr (D0 <= 4) { \
157 if constexpr (D0 > 0) { \
158 constexpr size_t i = 0; \
159 BODY; \
160 } \
161 if constexpr (D0 > 1) { \
162 constexpr size_t i = 1; \
163 BODY; \
164 } \
165 if constexpr (D0 > 2) { \
166 constexpr size_t i = 2; \
167 BODY; \
168 } \
169 if constexpr (D0 > 3) { \
170 constexpr size_t i = 3; \
171 BODY; \
172 } \
173 } else { \
174 for (size_t i = 0; i < D0; ++i) { \
175 BODY; \
176 } \
177 }
178
179/**************************************************************************************************
180 NdArray Operators
181*/
182
183template <typename T, size_t D0, size_t... DIMS>
184MOCHI_FORCE_INLINE constexpr NdArray<T, D0, DIMS...> operator-(NdArray<T, D0, DIMS...> const& a) {
185 NdArray<T, D0, DIMS...> result{};
186 MOCHI_DETAILS_UNROLL_D0(result[i] = -a[i]);
187 return result;
188}
189
190template <typename T, size_t D0, size_t... DIMS>
192 NdArray<T, D0, DIMS...> const& lhs,
193 NdArray<T, D0, DIMS...> const& rhs) {
194 bool isEqual = (lhs[0] == rhs[0]);
195 if constexpr (D0 > 1) {
196 isEqual &= (lhs[1] == rhs[1]);
197 }
198 if constexpr (D0 > 2) {
199 isEqual &= (lhs[2] == rhs[2]);
200 }
201 if constexpr (D0 > 3) {
202 isEqual &= (lhs[3] == rhs[3]);
203 }
204 if constexpr (D0 > 4) {
205 for (size_t i = 4; i < D0; ++i) {
206 isEqual &= (lhs[i] == rhs[i]);
207 }
208 }
209 return isEqual;
210}
211
212template <typename T, size_t D0, size_t... DIMS>
213MOCHI_FORCE_INLINE constexpr typename NdArray<T, D0, DIMS...>::value_type const&
215 MOCHI_ASSERT_VERBOSE(i < D0, "Index out-of-range");
216 return _data[i];
217}
218
219template <typename T, size_t D0, size_t... DIMS>
220MOCHI_FORCE_INLINE constexpr typename NdArray<T, D0, DIMS...>::value_type&
222 MOCHI_ASSERT_VERBOSE(i < D0, "Index out-of-range");
223 return _data[i];
224}
225
226template <typename T, size_t D0, size_t... DIMS>
228 NdArray<T, D0, DIMS...> const& lhs,
229 NdArray<T, D0, DIMS...> const& rhs) {
230 return !(lhs == rhs);
231}
232
233// NdArray memberwise math operators (+=, -=, *=, /=, +, -, *, /)
234//
235// In-place OP_EQ variants (NdArray += NdArray, NdArray += scalar) avoid creating a temporary. For
236// large or nested NdArrays, the temporary may otherwise spill out of the register file.
237#define MOCHI_DETAILS_NDARRAY_MEMBERWISE_OP(OP_EQ, OP) \
238 \
239 template <typename T, size_t D0, size_t... DIMS> \
240 MOCHI_FORCE_INLINE constexpr NdArray<T, D0, DIMS...>& operator OP_EQ( \
241 NdArray<T, D0, DIMS...>& lhs, NdArray<T, D0, DIMS...> const& rhs) { \
242 MOCHI_DETAILS_UNROLL_D0(lhs[i] OP_EQ rhs[i]); \
243 return lhs; \
244 } \
245 \
246 template <typename T, size_t D0, size_t... DIMS> \
247 MOCHI_FORCE_INLINE constexpr NdArray<T, D0, DIMS...>& operator OP_EQ( \
248 NdArray<T, D0, DIMS...>& lhs, T rhs) { \
249 MOCHI_DETAILS_UNROLL_D0(lhs[i] OP_EQ rhs); \
250 return lhs; \
251 } \
252 \
253 template <typename T, size_t D0, size_t... DIMS, typename AnyRHS> \
254 MOCHI_FORCE_INLINE constexpr NdArray<T, D0, DIMS...>& operator OP_EQ( \
255 NdArray<T, D0, DIMS...>& lhs, AnyRHS const& rhs) { \
256 MOCHI_DETAILS_UNROLL_D0(lhs[i] OP_EQ rhs); \
257 return lhs; \
258 } \
259 \
260 template <typename T, size_t D0, size_t... DIMS> \
261 MOCHI_FORCE_INLINE constexpr NdArray<T, D0, DIMS...> operator OP( \
262 NdArray<T, D0, DIMS...> const& lhs, NdArray<T, D0, DIMS...> const& rhs) { \
263 NdArray<T, D0, DIMS...> result{}; \
264 MOCHI_DETAILS_UNROLL_D0(result[i] = lhs[i] OP rhs[i]); \
265 return result; \
266 } \
267 \
268 template <typename T, size_t D0, size_t... DIMS> \
269 MOCHI_FORCE_INLINE constexpr NdArray<T, D0, DIMS...> operator OP( \
270 NdArray<T, D0, DIMS...> const& lhs, T rhs) { \
271 NdArray<T, D0, DIMS...> result{}; \
272 MOCHI_DETAILS_UNROLL_D0(result[i] = lhs[i] OP rhs); \
273 return result; \
274 } \
275 \
276 template <typename T, size_t D0, size_t... DIMS> \
277 MOCHI_FORCE_INLINE constexpr NdArray<T, D0, DIMS...> operator OP( \
278 T lhs, NdArray<T, D0, DIMS...> const& rhs) { \
279 NdArray<T, D0, DIMS...> result{}; \
280 MOCHI_DETAILS_UNROLL_D0(result[i] = lhs OP rhs[i]); \
281 return result; \
282 }
283
288
289#undef MOCHI_DETAILS_NDARRAY_MEMBERWISE_OP
290#undef MOCHI_DETAILS_UNROLL_D0
291
292// Support for associative containers of small int arrays:
293struct Int2Hash {
294 MOCHI_FORCE_INLINE size_t operator()(Int2 const& x) const {
295 return std::hash<uint64_t>()(static_cast<uint64_t>(x[0]) | (static_cast<uint64_t>(x[1]) << 32));
296 }
297};
298struct Int2Less {
299 MOCHI_FORCE_INLINE bool operator()(Int2 const& a, Int2 const& b) const {
300 return (a[0] < b[0]) || (a[0] == b[0] && a[1] < b[1]);
301 };
302};
304 MOCHI_FORCE_INLINE size_t operator()(Int3 const& xUnsorted) const {
305 Int3 xSorted = xUnsorted;
306 std::sort(xSorted.begin(), xSorted.end());
307 return Int2Hash{}({xSorted[0], xSorted[1]}) ^ std::hash<uint64_t>()(xSorted[2]);
308 }
309};
311 MOCHI_FORCE_INLINE bool operator()(Int3 const& aUnsorted, Int3 const& bUnsorted) const {
312 Int3 aSorted = aUnsorted;
313 std::sort(aSorted.begin(), aSorted.end());
314 Int3 bSorted = bUnsorted;
315 std::sort(bSorted.begin(), bSorted.end());
316 return aSorted == bSorted;
317 }
318};
319
320/************************************************************************************
321 By declaring explicit instantiations for common types, we can reduce code bloat
322 especially in debug builds where the template functions are not inlined.
323*/
324#if MOCHI_USE_EXTERN_TEMPLATE
325extern template class NdArray<int, 2>;
326extern template class NdArray<int, 3>;
327extern template class NdArray<int, 4>;
328extern template class NdArray<real, 2>;
329extern template class NdArray<real, 3>;
330extern template class NdArray<real, 4>;
331extern template class NdArray<real, 2, 2>;
332extern template class NdArray<real, 2, 3>;
333extern template class NdArray<real, 3, 2>;
334extern template class NdArray<real, 3, 3>;
335extern template class NdArray<real, 4, 3>;
336#endif // MOCHI_USE_EXTERN_TEMPLATE
337
338// ScalarType specialization: recursively unwrap element type.
339namespace details {
340template <class T, size_t D0, size_t... DIMS>
341struct ScalarTypeDef<NdArray<T, D0, DIMS...>, void> {
342 using type = ScalarType<T>;
343};
344} // namespace details
345
346} // namespace superdex
347
348/************************************************************************************
349 Reflection support for NdArray using SReflect::ArrayTypeInfo
350 NdArray<T, D0> will be treated as a fixed-size array of type T, similar to std::array<T, D0>.
351 NdArray<T, D0, D1> will be treated an array of arrays like std::array<std::array<T, D1>, D0>.
352 etc...
353*/
354#if MOCHI_USE_REFLECTION
355template <typename T, size_t D0, size_t... DIMS>
356struct SReflectTypeTraits<superdex::NdArray<T, D0, DIMS...>> {
357 static constexpr SReflect::CoreType coreType = SReflect::CoreType::CT_array;
358 static SReflect::ArrayTypeInfo const& GetTypeInfo() {
359 static auto const* s_typeInfo = []() {
360 using MyType = superdex::NdArray<T, D0, DIMS...>;
361 using InnerType = typename MyType::value_type;
362 // Format the name like, "NdArray<int, 2>", "NdArray<real, 3, 3>", etc...
363 char dimsStr[128];
364 for (size_t i = 0, offset = 0; i < MyType::num_dims; ++i) {
365 offset += snprintf(dimsStr + offset, sizeof(dimsStr) - offset, ",%zu", MyType::dims[i]);
366 }
367 char const* tName = SReflect::GetTypeInfo<T>()._nameWithNamespace;
368 char const* myName = SReflect::detail::MakeTypeName("superdex::NdArray<", tName, dimsStr, ">");
369 static constexpr bool kFormatAsTemplate = false; // No. Use our formatted name.
370 return SReflect::MakeFixedArrayTypeInfo<MyType, InnerType, D0>(myName, kFormatAsTemplate);
371 }();
372 return *s_typeInfo;
373 }
374};
375#endif // MOCHI_USE_REFLECTION
constexpr value_type * data()
Definition nd_array.h:79
constexpr auto begin() const
Definition nd_array.h:82
constexpr NdArray()=default
constexpr auto end()
Definition nd_array.h:83
constexpr value_type const * data() const
Definition nd_array.h:80
static constexpr size_t num_dims
Definition nd_array.h:54
constexpr auto begin()
Definition nd_array.h:81
typename std::conditional_t< num_dims==1, T, NdArray< T, DIMS... > > value_type
Definition nd_array.h:59
static constexpr size_t dims[num_dims]
Definition nd_array.h:55
constexpr value_type & operator[](size_t i)
Definition nd_array.h:221
constexpr value_type const & operator[](size_t i) const
Definition nd_array.h:214
static constexpr int size()
Definition nd_array.h:78
constexpr NdArray(U const &... p)
Definition nd_array.h:74
constexpr auto end() const
Definition nd_array.h:84
static constexpr size_t flattened_size
Definition nd_array.h:56
#define MOCHI_ASSERT_VERBOSE(condition_without_side_effects,...)
Definition debug.h:102
#define MOCHI_FORCE_INLINE
#define MOCHI_ANY
constexpr bool operator==(NdArray< T, D0, DIMS... > const &lhs, NdArray< T, D0, DIMS... > const &rhs)
Definition nd_array.h:191
NdArray< real, 2, 3 > Matrix2x3r
Definition nd_array.h:123
NdArray< float, 3, 2 > Matrix3x2f
Definition nd_array.h:131
NdArray< float, 4, 4 > Matrix4x4f
Definition nd_array.h:134
NdArray< real, 3, 2 > Matrix3x2r
Definition nd_array.h:124
NdArray< float, 4 > Float4
Definition nd_array.h:114
NdArray< real, 4, 3 > Matrix4x3r
Definition nd_array.h:126
NdArray< float, 3, 3, 3 > Tensor3x3x3f
Definition nd_array.h:146
NdArray< float, 4, 3 > Matrix4x3f
Definition nd_array.h:133
NdArray< real, 3, 3, 3, 3 > Tensor3x3x3x3r
Definition nd_array.h:142
constexpr A MultiplyConstexpr(A a)
Definition nd_array.h:35
NdArray< int, 4 > Int4
Definition nd_array.h:139
NdArray< real, 5 > Real5
Definition nd_array.h:108
NdArray< real, 2, 2 > Matrix2x2r
Definition nd_array.h:122
NdArray< int, 3 > Int3
Definition nd_array.h:138
NdArray< real, 4, 4 > Matrix4x4r
Definition nd_array.h:127
NdArray< float, 2, 3 > Matrix2x3f
Definition nd_array.h:130
NdArray< real, 3 > Real3
Definition nd_array.h:106
NdArray< float, 2, 2 > Matrix2x2f
Definition nd_array.h:129
NdArray< double, 2 > Double2
Definition nd_array.h:117
NdArray< float, 3, 3 > Matrix3x3f
Definition nd_array.h:132
NdArray< real, 4 > Real4
Definition nd_array.h:107
NdArray< double, 3 > Double3
Definition nd_array.h:118
NdArray< double, 4 > Double4
Definition nd_array.h:119
NdArray< real, 3, 3, 3 > Tensor3x3x3r
Definition nd_array.h:143
NdArray< real, 3, 3 > Matrix3x3r
Definition nd_array.h:125
NdArray< int, 2 > Int2
Definition nd_array.h:137
constexpr bool operator!=(NdArray< T, D0, DIMS... > const &lhs, NdArray< T, D0, DIMS... > const &rhs)
Definition nd_array.h:227
NdArray< real, 6 > Real6
Definition nd_array.h:109
constexpr NdArray< T, D0, DIMS... > operator-(NdArray< T, D0, DIMS... > const &a)
Definition nd_array.h:184
static constexpr bool kIsNdArray
Definition nd_array.h:101
NdArray< float, 3 > Float3
Definition nd_array.h:113
NdArray< real, 2 > Real2
Definition nd_array.h:105
NdArray< float, 3, 3, 3, 3 > Tensor3x3x3x3f
Definition nd_array.h:145
NdArray< real, 1 > Real1
Definition nd_array.h:104
NdArray< float, 2 > Float2
Definition nd_array.h:112
#define MOCHI_DETAILS_NDARRAY_MEMBERWISE_OP(OP_EQ, OP)
Definition nd_array.h:237
#define MOCHI_DETAILS_UNROLL_D0(BODY)
Definition nd_array.h:155
size_t operator()(Int2 const &x) const
Definition nd_array.h:294
bool operator()(Int2 const &a, Int2 const &b) const
Definition nd_array.h:299
bool operator()(Int3 const &aUnsorted, Int3 const &bUnsorted) const
Definition nd_array.h:311
size_t operator()(Int3 const &xUnsorted) const
Definition nd_array.h:304