Loading...
Searching...
No Matches
ExactReduction.h
Go to the documentation of this file.
1/*
2** This file is part of eOn.
3**
4** SPDX-License-Identifier: BSD-3-Clause
5**
6** Copyright (c) 2010--present, eOn Development Team
7** All rights reserved.
8**
9** Repo:
10** https://github.com/TheochemUI/eOn
11*/
12#pragma once
13
14// Correctly rounded dot products and norms.
15//
16// Every finite double is an integer times a power of two, so x_i * y_i is
17// the 106-bit integer product of the two significands times 2^(e_x + e_y),
18// with no rounding. The products are added into a fixed-point integer
19// accumulator that spans the whole exponent range (Kulisch), where addition
20// is associative, and the exact total is rounded once to nearest, ties to
21// even. The result is therefore a function of the multiset of products
22// alone: the same bits for any summation order, vector width, contraction
23// setting, compiler or Eigen release. This is the integer formulation of
24// the error-free splitting in Ozaki, Ogita, Oishi and Rump, Numer.
25// Algorithms 59 (2012), doi:10.1007/s11075-011-9478-1, and of Ozaki,
26// Uchino and Imamura's scheme II (arXiv:2504.08009), taken to a single
27// correctly rounded scalar.
28//
29// Use these where a reduction decides something (step lengths, curvature
30// estimates, convergence tests, acceptance); Eigen's own reductions change
31// their association between releases (3.4 sums two packet accumulators,
32// 5.0 four) and with the SIMD width.
33
34#include <cmath>
35#include <cstddef>
36
37#include <Eigen/Core>
38
39namespace eonc::exact {
40
44[[nodiscard]] double dot(const double *x, const double *y, std::size_t n);
45
47[[nodiscard]] double sum(const double *x, std::size_t n);
48
51[[nodiscard]] double sumStrided(const double *x, std::size_t n,
52 std::size_t stride);
53
60[[nodiscard]] double orderedDot(const double *x, const double *y,
61 std::size_t n);
62
63// Eigen front ends. A plain object or a contiguous block is read in place;
64// any other expression is evaluated into a plain temporary first. The
65// result does not depend on the storage order or the stride.
66template <typename A, typename B>
67[[nodiscard]] double dot(const Eigen::MatrixBase<A> &x,
68 const Eigen::MatrixBase<B> &y) {
69 eigen_assert(x.size() == y.size());
70 const auto &xe = x.derived().eval();
71 const auto &ye = y.derived().eval();
72 return dot(xe.data(), ye.data(), static_cast<std::size_t>(xe.size()));
73}
74
75template <typename A, typename B>
76[[nodiscard]] double orderedDot(const Eigen::MatrixBase<A> &x,
77 const Eigen::MatrixBase<B> &y) {
78 eigen_assert(x.size() == y.size());
79 const auto &xe = x.derived().eval();
80 const auto &ye = y.derived().eval();
81 return orderedDot(xe.data(), ye.data(), static_cast<std::size_t>(xe.size()));
82}
83
84template <typename A>
85[[nodiscard]] double squaredNorm(const Eigen::MatrixBase<A> &x) {
86 const auto &xe = x.derived().eval();
87 return dot(xe.data(), xe.data(), static_cast<std::size_t>(xe.size()));
88}
89
92template <typename A> [[nodiscard]] double norm(const Eigen::MatrixBase<A> &x) {
93 return std::sqrt(squaredNorm(x));
94}
95
99template <typename A>
100[[nodiscard]] double orderedNorm(const Eigen::MatrixBase<A> &x) {
101 return std::sqrt(orderedDot(x, x));
102}
103
105template <typename A>
106[[nodiscard]] typename A::PlainObject normalized(const Eigen::MatrixBase<A> &x,
107 double min_norm = 1e-300) {
108 const double n = norm(x);
109 if (n < min_norm) [[unlikely]] {
110 return A::PlainObject::Zero(x.rows(), x.cols());
111 }
112 return (x / n).eval();
113}
114
115} // namespace eonc::exact
double squaredNorm(const Eigen::MatrixBase< A > &x)
A::PlainObject normalized(const Eigen::MatrixBase< A > &x, double min_norm=1e-300)
x / norm(x), or zero below min_norm (the safemath convention).
double sumStrided(const double *x, std::size_t n, std::size_t stride)
Same sum for x[i * stride].
double norm(const Eigen::MatrixBase< A > &x)
sqrt of the correctly rounded squared norm; IEEE sqrt is itself correctly rounded,...
double dot(const double *x, const double *y, std::size_t n)
Round-to-nearest-even value of the exact sum of x[i] * y[i].
double orderedNorm(const Eigen::MatrixBase< A > &x)
sqrt of orderedDot(x, x): reproducible, not correctly rounded.
double sum(const double *x, std::size_t n)
Round-to-nearest-even value of the exact sum of x[i].
double orderedDot(const double *x, const double *y, std::size_t n)
A fixed summation tree: four lanes (term i goes to lane i mod 4, each lane a left-to-right sum of rou...