1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
//! Reductions whose result is a function of the *set* of terms, not of the
//! order they were written in.
//!
//! # Why this exists
//!
//! IEEE addition and multiplication are **commutative but not associative**:
//! `(a + b) + c` and `(b + c) + a` differ in the last bits. That is harmless
//! until something permutes the order, and in this crate something does — a
//! lattice rotation permutes the axis labels of a vector and the edge labels of
//! a cell, so a running sum taken in label order is a function of *how the cell
//! is oriented on the grid* rather than of the cell.
//!
//! The audit measured it directly on the three-term case: **4328 of 9600**
//! lattice-symmetry trials disagreed with an unsorted dot product, and **0 of
//! 9600** with a sorted one (✗12).
//!
//! Sorting by magnitude first fixes it, because magnitude order is invariant
//! under permutation of the terms. The sum then depends only on which values are
//! present.
//!
//! # Why the reductions are fixed-size
//!
//! Every caller reduces over a slot per *label* — three axes, or twelve cell
//! edges — with absent labels left at `R::ZERO`. That is deliberate rather than
//! wasteful: padding with `+0.0` sorts first and adds exactly, so a twelve-slot
//! reduction over three real terms gives the same bits as a three-slot one.
//!
//! **Including for negative zero**, which is worth stating because it is the
//! obvious place to expect an exception and there isn't one. `+0.0 + (−0.0)` is
//! `+0.0` under round-to-nearest, and [`sum_equivariant`] seeds its accumulator
//! at `R::ZERO`, so a reduction whose every term is `−0.0` returns `+0.0`
//! whether it was padded or not — the padding changes nothing a golden hash
//! could see (M-176).
use crateReal;
/// The order the reductions sum in: ascending by magnitude, ties broken by the
/// signed value.
///
/// `total_cmp` is a total order even across NaN and signed zero, so the network
/// cannot produce an order that depends on which comparison ran first.
///
/// # The tie-break is load-bearing, and the comment this replaced said it was not
///
/// The original form sorted on the absolute values alone and argued that ties
/// need no tie-break, *"since IEEE addition and multiplication are both
/// commutative — only associativity fails — so two terms of equal magnitude give
/// the same answer either way round."* That is true of `a + b` against `b + a`
/// and false of everything longer, which is the only case that occurs here.
/// Measured (M-175): summing `[1e-16, +1, −1]` smallest-first gives `0`, and
/// `[1e-16, −1, +1]` gives `1.11e-16` — the pair ties in magnitude, so a *stable*
/// sort left their order to the order they arrived in, which for this crate is
/// the axis or edge labelling that a lattice rotation permutes.
///
/// Comparing the signed values on a tie removes it: `−c` sorts before `+c` for
/// any `c`, so the sequence is a function of the multiset of terms alone. It
/// survives negation, which is what a rotation can do to a component: negating
/// every term maps each magnitude group onto itself (a group is either repeats
/// of one value, or a `±c` pair), and `−(a + b)` is exactly `(−a) + (−b)`.
///
/// Insertion sort, because `N` is 3, 6 or 12 here and it is branch-predictable
/// and allocation-free, which a comparison sort over a slice would not be in
/// `no_std`. At the largest of those it is 66 comparisons worst case, against a
/// reduction that would otherwise be 11 additions — the cost is real and it buys
/// the only property that makes the result a function of the cell.
pub
/// Whether `a` sums before `b`: smaller magnitude first, then smaller value.
/// Sum smallest-magnitude-first, so the result is a function of the *set* of
/// terms rather than of the order they were written in.
pub