limes 3.1.0
Composable Calculus Expressions for C++20
Loading...
Searching...
No Matches
accumulators.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cmath>
4#include <memory>
5#include "../concepts/concepts.hpp"
6
8
9using concepts::Field;
10using concepts::Accumulator;
11
12// Simple accumulator - no error compensation
13template<Field T>
15public:
16 using value_type = T;
17
18 constexpr simple_accumulator() noexcept : sum_{} {}
19 constexpr explicit simple_accumulator(T init) noexcept : sum_{init} {}
20
21 constexpr simple_accumulator& operator+=(T value) noexcept {
22 sum_ += value;
23 return *this;
24 }
25
26 constexpr simple_accumulator& operator-=(T value) noexcept {
27 sum_ -= value;
28 return *this;
29 }
30
31 constexpr T operator()() const noexcept { return sum_; }
32 constexpr operator T() const noexcept { return sum_; }
33
34 constexpr void reset() noexcept { sum_ = T{}; }
35
36private:
37 T sum_;
38};
39
40// Kahan-Babuska accumulator with error compensation
41template<Field T>
43public:
44 using value_type = T;
45
46 constexpr kahan_accumulator() noexcept : sum_{}, correction_{} {}
47 constexpr explicit kahan_accumulator(T init) noexcept : sum_{init}, correction_{} {}
48
49 constexpr kahan_accumulator& operator+=(T value) noexcept {
50 T y = value - correction_;
51 T t = sum_ + y;
52 correction_ = (t - sum_) - y;
53 sum_ = t;
54 return *this;
55 }
56
57 constexpr T operator()() const noexcept { return sum_; }
58 constexpr operator T() const noexcept { return sum_; }
59
60 constexpr T error() const noexcept { return correction_; }
61 constexpr T correction() const noexcept { return correction_; }
62
63 constexpr void reset() noexcept {
64 sum_ = T{};
65 correction_ = T{};
66 }
67
68private:
69 T sum_;
70 T correction_;
71};
72
73// Neumaier accumulator - improved Kahan for all magnitudes
74template<Field T>
76public:
77 using value_type = T;
78
79 constexpr neumaier_accumulator() noexcept : sum_{}, correction_{} {}
80 constexpr explicit neumaier_accumulator(T init) noexcept : sum_{init}, correction_{} {}
81
82 constexpr neumaier_accumulator& operator+=(T value) noexcept {
83 T t = sum_ + value;
84 if (std::abs(sum_) >= std::abs(value)) {
85 correction_ += (sum_ - t) + value;
86 } else {
87 correction_ += (value - t) + sum_;
88 }
89 sum_ = t;
90 return *this;
91 }
92
93 constexpr T operator()() const noexcept { return sum_ + correction_; }
94 constexpr operator T() const noexcept { return sum_ + correction_; }
95
96 constexpr T error() const noexcept { return correction_; }
97 constexpr T correction() const noexcept { return correction_; }
98
99 constexpr void reset() noexcept {
100 sum_ = T{};
101 correction_ = T{};
102 }
103
104private:
105 T sum_;
106 T correction_;
107};
108
109// Klein accumulator - second-order error compensation
110template<Field T>
112public:
113 using value_type = T;
114
115 constexpr klein_accumulator() noexcept : sum_{}, cs_{}, ccs_{} {}
116 constexpr explicit klein_accumulator(T init) noexcept : sum_{init}, cs_{}, ccs_{} {}
117
118 constexpr klein_accumulator& operator+=(T value) noexcept {
119 T t = sum_ + value;
120 T c = T{};
121
122 if (std::abs(sum_) >= std::abs(value)) {
123 c = (sum_ - t) + value;
124 } else {
125 c = (value - t) + sum_;
126 }
127
128 sum_ = t;
129 t = cs_ + c;
130
131 if (std::abs(cs_) >= std::abs(c)) {
132 ccs_ += (cs_ - t) + c;
133 } else {
134 ccs_ += (c - t) + cs_;
135 }
136
137 cs_ = t;
138 return *this;
139 }
140
141 constexpr T operator()() const noexcept { return sum_ + cs_ + ccs_; }
142 constexpr operator T() const noexcept { return sum_ + cs_ + ccs_; }
143
144 constexpr T error() const noexcept { return cs_ + ccs_; }
145 constexpr T correction() const noexcept { return cs_; }
146 constexpr T second_correction() const noexcept { return ccs_; }
147
148 constexpr void reset() noexcept {
149 sum_ = T{};
150 cs_ = T{};
151 ccs_ = T{};
152 }
153
154private:
155 T sum_;
156 T cs_; // first-order correction
157 T ccs_; // second-order correction
158};
159
160// Pairwise accumulator for better accuracy with many terms
161template<Field T, std::size_t ChunkSize = 128>
163public:
164 using value_type = T;
165
166 constexpr pairwise_accumulator() noexcept : buffer_{}, count_{0}, sum_{} {}
167
168 constexpr pairwise_accumulator& operator+=(T value) noexcept {
169 if (count_ < ChunkSize) {
170 buffer_[count_++] = value;
171 } else {
172 sum_ += pairwise_sum(buffer_, ChunkSize);
173 buffer_[0] = value;
174 count_ = 1;
175 }
176 return *this;
177 }
178
179 constexpr T operator()() const noexcept {
180 if (count_ == 0) return sum_;
181 return sum_ + pairwise_sum(buffer_, count_);
182 }
183
184 constexpr operator T() const noexcept { return operator()(); }
185
186 constexpr void reset() noexcept {
187 count_ = 0;
188 sum_ = T{};
189 }
190
191private:
192 T buffer_[ChunkSize];
193 std::size_t count_;
194 T sum_;
195
196 static constexpr T pairwise_sum(const T* data, std::size_t n) noexcept {
197 if (n <= 1) return n ? data[0] : T{};
198 if (n == 2) return data[0] + data[1];
199
200 std::size_t mid = n / 2;
201 return pairwise_sum(data, mid) + pairwise_sum(data + mid, n - mid);
202 }
203};
204
205// Type-erased accumulator wrapper
206template<Field T>
208public:
209 using value_type = T;
210
211 template<Accumulator<T> A>
212 explicit any_accumulator(A acc)
213 : impl_{std::make_unique<accumulator_impl<A>>(std::move(acc))} {}
214
216 : impl_{other.impl_->clone()} {}
217
219 impl_ = other.impl_->clone();
220 return *this;
221 }
222
224 impl_->add(value);
225 return *this;
226 }
227
228 T operator()() const { return impl_->get(); }
229 operator T() const { return impl_->get(); }
230
231 void reset() { impl_->reset(); }
232
233private:
234 struct accumulator_base {
235 virtual ~accumulator_base() = default;
236 virtual void add(T value) = 0;
237 virtual T get() const = 0;
238 virtual void reset() = 0;
239 virtual std::unique_ptr<accumulator_base> clone() const = 0;
240 };
241
242 template<typename A>
243 struct accumulator_impl : accumulator_base {
244 A acc;
245
246 explicit accumulator_impl(A a) : acc{std::move(a)} {}
247
248 void add(T value) override { acc += value; }
249 T get() const override { return acc(); }
250 void reset() override { acc.reset(); }
251
252 std::unique_ptr<accumulator_base> clone() const override {
253 return std::make_unique<accumulator_impl>(acc);
254 }
255 };
256
257 std::unique_ptr<accumulator_base> impl_;
258};
259
260} // namespace limes::algorithms::accumulators
any_accumulator & operator=(const any_accumulator &other)
constexpr kahan_accumulator & operator+=(T value) noexcept
constexpr klein_accumulator & operator+=(T value) noexcept
constexpr neumaier_accumulator & operator+=(T value) noexcept
constexpr pairwise_accumulator & operator+=(T value) noexcept
constexpr simple_accumulator & operator-=(T value) noexcept
constexpr simple_accumulator & operator+=(T value) noexcept