6#ifndef NUMERICAL_SIMD_HPP
7#define NUMERICAL_SIMD_HPP
19#if defined(_MSC_VER) && !defined(__clang__)
20# define NUMERICAL_ALWAYS_INLINE __forceinline
22# define NUMERICAL_ALWAYS_INLINE [[gnu::always_inline]] inline
47 std::array<T, symmetric_row_group>
sums)
50 const T*
row {rows + (r * n)};
51 const T factor {v[i + r]};
52 for (std::size_t k {done}; k < i + r; ++k) {
64 concept split_complex = std::is_same_v<T, std::complex<float>> || std::is_same_v<T, std::complex<double>>;
94 concept native_scalar = std::is_same_v<T, float> || std::is_same_v<T, double>;
101 template <native_scalar T>
106 using lane =
typename native_vector<T>::type;
108 constexpr std::size_t
lanes {4};
109 std::array<lane, lanes>
sum {};
110 const std::size_t done {count - (count % (width *
lanes))};
111 for (std::size_t i {0}; i < done; i += width *
lanes) {
113 for (std::size_t
l {0};
l <
lanes; ++
l) {
116 std::memcpy(&
xv, x + i + (
l * width),
sizeof(
lane));
117 std::memcpy(&
yv,
y + i + (
l * width),
sizeof(
lane));
122 for (std::size_t
l {0};
l <
lanes; ++
l) {
123 for (std::size_t k {0}; k < width; ++k) {
127 for (std::size_t i {done}; i < count; ++i) {
142 template <split_complex C>
147 using R =
typename C::value_type;
148 using lane =
typename native_vector<R>::type;
150 constexpr std::size_t
lanes {4};
151 const auto*
const a {
reinterpret_cast<const R*
>(x)};
152 const auto*
const b {
reinterpret_cast<const R*
>(
y)};
153 const std::size_t
parts {2 * count};
154 std::array<lane, lanes>
same {};
155 std::array<lane, lanes>
cross {};
157 for (std::size_t i {0}; i < done; i += width *
lanes) {
159 for (std::size_t
l {0};
l <
lanes; ++
l) {
162 std::memcpy(&
av, a + i + (
l * width),
sizeof(
lane));
163 std::memcpy(&
bv, b + i + (
l * width),
sizeof(
lane));
165 for (std::size_t k {0}; k < width; ++k) {
176 for (std::size_t
l {0};
l <
lanes; ++
l) {
177 for (std::size_t k {0}; k < width; k += 2) {
184 for (std::size_t i {done}; i <
parts; i += 2) {
186 ii += a[i + 1] * b[i + 1];
187 ri += a[i] * b[i + 1];
188 ir += a[i + 1] * b[i];
194 template <split_complex C>
205 template <split_complex C>
221 template <native_scalar T>
229 using lane =
typename native_vector<T>::type;
231 constexpr std::size_t
lanes {4};
232 constexpr std::size_t
group {4};
233 const std::size_t done {count - (count % (width *
lanes))};
236 std::array<std::array<lane, lanes>,
group>
sum {};
237 for (std::size_t i {0}; i < done; i += width *
lanes) {
239 for (std::size_t
l {0};
l <
lanes; ++
l) {
241 std::memcpy(&
xv, x + i + (
l * width),
sizeof(
lane));
243 for (std::size_t r {0}; r <
group; ++r) {
245 std::memcpy(&
mv, m + ((
r0 + r) * ld) + i + (
l * width),
sizeof(
lane));
250 for (std::size_t r {0}; r <
group; ++r) {
252 for (std::size_t
l {0};
l <
lanes; ++
l) {
253 for (std::size_t k {0}; k < width; ++k) {
257 const T*
const row {m + ((
r0 + r) * ld)};
258 for (std::size_t i {done}; i < count; ++i) {
264 for (;
r0 < rows; ++
r0) {
274 template <native_scalar T>
281 using lane =
typename native_vector<T>::type;
283 constexpr std::size_t
lanes {4};
286 std::array<lane, lanes>
sum {};
287 const std::size_t done {count - (count % (width *
lanes))};
288 for (std::size_t i {0}; i < done; i += width *
lanes) {
290 for (std::size_t
l {0};
l <
lanes; ++
l) {
294 std::memcpy(&
rv,
row + i + (
l * width),
sizeof(
lane));
295 std::memcpy(&
vv, v + i + (
l * width),
sizeof(
lane));
296 std::memcpy(&
ov,
out + i + (
l * width),
sizeof(
lane));
299 std::memcpy(
out + i + (
l * width), &
ov,
sizeof(
lane));
303 for (std::size_t
l {0};
l <
lanes; ++
l) {
304 for (std::size_t k {0}; k < width; ++k) {
308 for (std::size_t i {done}; i < count; ++i) {
310 out[i] +=
row[i] * factor;
321 template <native_scalar T>
328 using lane =
typename native_vector<T>::type;
330 std::array<lane, symmetric_row_group>
sum {};
331 std::array<lane, symmetric_row_group>
splat {};
335 const std::size_t done {i - (i % width)};
336 for (std::size_t j {0}; j < done; j += width) {
339 std::memcpy(&
vv, v + j,
sizeof(
lane));
344 std::memcpy(&
rv, rows + (r * n) + j,
sizeof(
lane));
350 std::array<T, symmetric_row_group>
sums {};
352 for (std::size_t k {0}; k < width; ++k) {
373 template <native_scalar T>
379 using lane =
typename native_vector<T>::type;
381 using index = std::remove_cvref_t<
decltype(
mask {}[0])>;
383 constexpr std::size_t
ways {4};
384 if (count < 1 + (
ways * width)) {
388 std::array<lane, ways>
top;
389 std::array<mask, ways>
where {};
390 std::array<mask, ways> next;
391 for (std::size_t
w {0};
w <
ways; ++
w) {
393 for (std::size_t k {0}; k < width; ++k) {
394 next[
w][k] =
static_cast<index
>(1 + (
w * width) + k);
398 for (; i + (
ways * width) <= count; i +=
ways * width) {
400 for (std::size_t
w {0};
w <
ways; ++
w) {
402 std::memcpy(&v, x + i + (
w * width),
sizeof(
lane));
403 const lane v_abs {std::bit_cast<lane>(std::bit_cast<mask>(v) & magnitude)};
407 next[
w] +=
static_cast<index
>(
ways * width);
410 for (std::size_t
w {0};
w <
ways; ++
w) {
411 for (std::size_t k {0}; k < width; ++k) {
427#if defined(__GNUC__) && !defined(__clang__)
430 template <native_scalar T>
436 using lane =
typename native_vector<T>::type;
438 const std::size_t done {count - (count % width)};
439 for (std::size_t i {0}; i < done; i += width) {
442 std::memcpy(&
yv,
y + i,
sizeof(
lane));
443 std::memcpy(&
xv, x + i,
sizeof(
lane));
445 std::memcpy(
y + i, &
yv,
sizeof(
lane));
458 template <native_scalar T>
466 using lane =
typename native_vector<T>::type;
467 constexpr std::size_t
lanes {4};
469 const std::size_t done {count - (count % (
lanes * width))};
470 for (std::size_t i {0}; i < done; i +=
lanes * width) {
471 std::array<lane, lanes>
held {};
473 for (std::size_t u {0}; u <
lanes; ++u) {
474 std::memcpy(&
held[u],
y + i + (u * width),
sizeof(
lane));
476 for (std::size_t t {0}; t <
terms; ++t) {
477 const T factor {
f[t]};
479 for (std::size_t u {0}; u <
lanes; ++u) {
481 std::memcpy(&
row, x + (t *
ldx) + i + (u * width),
sizeof(
lane));
486 for (std::size_t u {0}; u <
lanes; ++u) {
487 std::memcpy(
y + i + (u * width), &
held[u],
sizeof(
lane));
498 template <native_scalar T>
508 using lane =
typename native_vector<T>::type;
510 const std::size_t done {count - (count % width)};
511 for (std::size_t i {0}; i < done; i += width) {
512 std::array<lane, product_rows>
held {};
517 for (std::size_t t {0}; t <
terms; ++t) {
519 std::memcpy(&
row, x + (t *
ldx) + i,
sizeof(
lane));
522 const T factor {
f[(r *
ldf) + t]};
535 template <native_scalar T>
541 using lane =
typename native_vector<T>::type;
543 const std::size_t done {count - (count % width)};
544 for (std::size_t i {0}; i < done; i += width) {
547 std::memcpy(&
yv,
y + i,
sizeof(
lane));
548 std::memcpy(&
xv, x + i,
sizeof(
lane));
550 std::memcpy(
y + i, &
yv,
sizeof(
lane));
561 template <split_complex C>
567 using R = C::value_type;
568 using lane =
typename native_vector<R>::type;
570 auto*
const out {
reinterpret_cast<R*
>(
y)};
571 const auto*
const in {
reinterpret_cast<const R*
>(x)};
575 for (std::size_t k {0}; k < width; ++k) {
578 sign[k] = (k % 2 == 0) ?
R {-1} :
R {1};
580 const std::size_t
parts {2 * count};
581 const std::size_t done {
parts - (
parts % width)};
582 for (std::size_t i {0}; i < done; i += width) {
586 std::memcpy(&
xv,
in + i,
sizeof(
lane));
588 for (std::size_t k {0}; k < width; ++k) {
609 template <std::size_t
Rows,
623 std::array<T, Rows * Cols>
edge {};
624 for (std::size_t i {0}; i < rows; ++i) {
625 for (std::size_t j {0}; j < cols; ++j) {
626 edge[(i *
Cols) + j] = c[(i * ldc) + j];
630 for (std::size_t i {0}; i < rows; ++i) {
631 for (std::size_t j {0}; j < cols; ++j) {
632 c[(i * ldc) + j] =
edge[(i *
Cols) + j];
638 template <
typename T>
648 template <
typename T,
660 template <
typename T>
673 template <
typename T>
687 template <
typename T>
697 template <
typename T>
706 for (std::size_t i {0}; i < count; ++i) {
708 out[i] +=
row[i] * factor;
714 template <
typename T>
725 template <
typename T>
731 for (std::size_t i {0}; i < count; ++i) {
738 template <
typename T>
744 for (std::size_t i {0}; i < count; ++i) {
745 total += std::conj(x[i]) *
y[i];
751 template <
typename T>
759 for (std::size_t r {0}; r < rows; ++r) {
771 template <
typename T>
778#if defined(_LIBCPP_VERSION)
780 template <
typename R>
782 const std::complex<R>& b)
784 const R ac {a.real() * b.real()};
785 const R bd {a.imag() * b.imag()};
786 const R ad {a.real() * b.imag()};
787 const R bc {a.imag() * b.real()};
790 if (std::isnan(x) && std::isnan(
y)) [[
unlikely]] {
808 template <
typename T>
825 template <
typename T>
833 for (std::size_t t {0}; t <
terms; ++t) {
846 template <
typename T>
847 requires std::is_floating_point_v<T>
860 const auto blocks = [&]<std::size_t
Width>(std::integral_constant<std::size_t, Width>) {
862 std::array<T, Width>
held {};
864 for (std::size_t t {0}; t <
terms; ++t) {
865 const T factor {
f[t]};
866 const T*
const row {x + (t *
ldx) + i};
867 for (std::size_t u {0}; u <
Width; ++u) {
874 blocks(std::integral_constant<std::size_t, 32> {});
875 blocks(std::integral_constant<std::size_t, 8> {});
876 blocks(std::integral_constant<std::size_t, 4> {});
877 blocks(std::integral_constant<std::size_t, 2> {});
878 blocks(std::integral_constant<std::size_t, 1> {});
888 template <
typename T>
889 requires std::is_floating_point_v<T>
899 constexpr std::size_t width {4};
901 const auto columns = [&]<std::size_t
Width>(std::integral_constant<std::size_t, Width>) {
907 for (std::size_t t {0}; t <
terms; ++t) {
908 const T*
const row {x + (t *
ldx) + i};
910 const T factor {
f[(r *
ldf) + t]};
911 for (std::size_t u {0}; u <
Width; ++u) {
921 columns(std::integral_constant<std::size_t, width> {});
922 columns(std::integral_constant<std::size_t, 1> {});
926 template <
typename T>
940 template <split_complex C>
945 using R = C::value_type;
946 const R ac {
f.real() * x.real()};
947 const R bd {
f.imag() * x.imag()};
948 const R ad {
f.real() * x.imag()};
949 const R bc {
f.imag() * x.real()};
950 const R re {
ac -
bd};
951 const R im {
ad +
bc};
952 y =
C {
y.real() + re,
y.imag() + im};
957 template <split_complex C>
970 template <
typename T>
979 template <split_complex C>
988 template <
typename T>
A complex number of float or double, which gemm_split multiplies through the real kernel and the kern...
T symmetric_row(const T *row, const T *v, const T &f, T *out, std::size_t count)
Σ row[i] · v[i] and out[i] += row[i] · f, in order, for a type no native vector holds.
void subtract_rows(double *y, std::size_t ldy, const double *f, std::size_t ldf, const double *x, std::size_t ldx, std::size_t terms, std::size_t count)
subtract_rows for double, the units chosen as subtract_products chooses them: each element the value ...
T conjugate_dot_product(const T *x, const T *y, std::size_t count)
Σ conj(x[i]) · y[i], in order, where no native vector holds the parts.
constexpr std::size_t transpose_tile
The side of the tiles a transposing copy goes by (an einsum permutation of two axes,...
void add_one_product(T &y, const T &f, const T &x)
y += f · x of one element, as add_product computes each of its elements.
std::size_t subtract_rows_vectors(T *, std::size_t, const T *, std::size_t, const T *, std::size_t, std::size_t, std::size_t)
No leading columns in vectors, for a type no native vector holds or a compiler that vectorises the sc...
constexpr std::size_t symmetric_row_group
The rows of a lower triangle symmetric_rows takes at once.
T operator_times(const T &a, const T &b)
a · b, as std::operator* computes it, in line. libc++'s complex product (the four products and their ...
std::array< T, symmetric_row_group > symmetric_rows_tail(const T *rows, std::size_t n, const T *v, T *out, std::size_t i, std::size_t done, std::array< T, symmetric_row_group > sums)
The columns done…i+r of symmetric_rows, one by one: for each row r in order, out[k] += row_r[k] · v[i...
void add_product(double *y, const double &f, const double *x, std::size_t count)
y[i] += f · x[i] for double, as subtract_product.
std::size_t subtract_scaled_vectors(T *, const T *, const T &, std::size_t)
No leading elements in vectors, for a type no native vector holds.
void row_dots(const std::complex< double > *m, std::size_t ld, std::size_t rows, const std::complex< double > *x, std::size_t count, std::complex< double > *out)
row_dots for std::complex<double>, through complex_row_dots.
void subtract_products(double *y, const double *f, const double *x, std::size_t ldx, std::size_t terms, std::size_t count)
y[i] -= Σₜ f[t] · x[(t · ldx) + i] for double, the units chosen as subtract_product chooses them,...
void subtract_product(double *y, const double &f, const double *x, std::size_t count)
y[i] −= f · x[i] for double on the widest x86-64 units the matrix product's kernel may use (gemm_path...
std::array< T, symmetric_row_group > symmetric_rows(const T *rows, std::size_t n, const T *v, T *out, std::size_t i)
symmetric_rows one column at a time, for a type no native vector holds.
double real_part(const T &x)
The real part of a real (or general) scalar as a double.
std::size_t subtract_combined_vectors(T *, const T *, const T *, std::size_t, std::size_t, std::size_t)
No leading blocks in vectors, for a type no native vector holds or a compiler that vectorises the sca...
double dot_product(const double *x, const double *y, std::size_t count)
Σ x[i] · y[i] for double, as subtract_product chooses its units (in another order of summation there,...
void complex_scaled_add(C &y, const C &f, const C &x)
y += f · x of one complex number, its part products rounded apart as libc++'s operator computes them ...
constexpr std::size_t product_rows
The rows of y subtract_rows takes at once.
std::size_t add_scaled_vectors(T *, const T *, const T &, std::size_t)
No leading elements in vectors, for a type no native vector holds.
void through_edge(T *c, std::size_t ldc, std::size_t rows, std::size_t cols, Full full) noexcept
One tile of a matrix product kernel, rows × cols of C (at most Rows × Cols, leading dimension ldc),...
std::size_t largest_magnitude_vectors(const T *, std::size_t, Magnitude &, std::size_t &)
Only x[0], the one best and at come in with, for a type no native vector holds.
T sum(const tensor< T > &t)
The sum of every element, by pairwise halving (0 for an empty tensor).
tensor< T > full(std::vector< std::size_t > shape, const T &value)
A tensor of shape with every element value.
#define NUMERICAL_ALWAYS_INLINE