pub struct Quad2 { /* private fields */ }Expand description
A polynomial of total degree ≤ 2 in its own shape: a constant, the linear coefficients keyed by variable, and the quadratic coefficients keyed by the (i ≤ j) variable pair.
This replaces a general BTreeMap<Vec<usize>, f64> polynomial. Degree
is a property of the type here rather than of the data, so the
“is it still quadratic?” test is three is_empty() calls instead of a
scan, and no monomial key is ever allocated or cloned.
§Zero coefficients
Stored coefficients are nonzero: add and
mul drop any entry they leave at exactly zero, which is
what makes degree and as_constant
answerable in O(1). constant is the one exception and needs none:
both 0.0 and -0.0 in that field mean “no constant term”, every
consumer guards on != 0.0, and analyze_quadratic_full normalizes
the sign on the way out.
§…and when dropping one loses something
Dropping is right for the storage question and can be wrong for the
degree question, and lost_terms is the
difference (gh #683, sharpened by gh #687). A coefficient that reaches
zero is a coefficient that was summed, and it is the arithmetic of
that sum — not the drop — that decides whether anything went missing:
x − xfoldsfl(1) + fl(−1)to0.0, and that add is exact. The term really is absent, the body really is degree 0, and the maps are not a lower bound on anything.2⁵³·x² + x² − 2⁵³·x²loses thex²atfl(2⁵³ + 1) = 2⁵³, which is an inexact add, and only then does2⁵³ − 2⁵³drop the survivor. The body isx²; the maps say degree 0.
The loss happens at the inexact fold; the drop is only where it becomes visible. So the flag is set from the fold: a form whose arithmetic never rounded — and never flushed a live coefficient to zero — carries coefficients that are exactly what real arithmetic on the writer’s own literals would give, and a term missing from its maps is a term that is genuinely absent. Flagging the drop instead refused both of the above alike, which was sound and cost reach on the first (gh #687).
Implementations§
Source§impl Quad2
impl Quad2
Sourcepub fn quadratic(&self) -> &BTreeMap<(usize, usize), f64>
pub fn quadratic(&self) -> &BTreeMap<(usize, usize), f64>
The degree-2 terms as polynomial coefficients keyed (i ≤ j) —
the coefficient of xᵢxⱼ, not the Hessian entry (they differ by
a factor of 2 on the diagonal; analyze_quadratic_full applies it).
Sourcepub fn lost_terms(&self) -> bool
pub fn lost_terms(&self) -> bool
Whether a term may be missing from this form’s maps: something
was dropped on the way here and the arithmetic that produced it
had already rounded, or a live coefficient was flushed to zero (or
to NaN) outright.
This is what turns the term maps from an answer about degree into a
lower bound on it (gh #683). When it is set, an empty
quadratic map is no longer evidence that the
body is affine, and a consumer asking a degree question — as
opposed to a storage or evaluation question — must say “not
established” instead of “affine”. The two questions used to share
one predicate, which is how a genuinely quadratic row came to be
reported as proved affine and had its Jacobian frozen for a whole
solve.
It was originally set by the drop alone, which conflated an
exact cancellation with a lossy one and refused both (gh #687):
x − x is degree 0 by an exact add, and a consumer that treats it
as “not established” gives up a proved degree, a matrix evaluation
and — once the classifier gates on this too — the convex route, for
a body that lost nothing. The gate is now the fold, so the exact
case keeps its fast paths and the lossy one is refused for the
reason it deserves. inexact is the other half of
that answer.
It is deliberately one bit for the whole form rather than a record
per monomial: the consumer’s question is about the form, and Q3’s
point was that a Quad2 allocates nothing per term (gh #588, Q3).
Two bits, now, and still nothing per term — the cost of a per-key
provenance record is what keeps this a conservative answer: an
inexact fold on one monomial makes a cancellation on an unrelated
one count as lossy.
A linear or constant term that goes missing sets it too, even
though losing one cannot understate an affine body’s degree by
itself: it can once the form is multiplied, because
mul’s degree guard reads the same maps —
(2⁵³ + 1 − 2⁵³)·x · x² is degree 3 and folds to an apparent
degree 0. Distinguishing the two would take a third flag to buy
back reach that the corpus does not contain.
Sourcepub fn inexact(&self) -> bool
pub fn inexact(&self) -> bool
Whether any arithmetic behind this form’s coefficients rounded.
Clear means every stored coefficient is exact, which is what makes a
coefficient of zero a proof of absence rather than a lower bound —
see lost_terms, which is this bit read at the
moment a term is dropped. Public because it is the thing a test
about the sharpened gate has to be able to see; no consumer routes
on it.
Trait Implementations§
impl StructuralPartialEq for Quad2
Auto Trait Implementations§
impl Freeze for Quad2
impl RefUnwindSafe for Quad2
impl Send for Quad2
impl Sync for Quad2
impl Unpin for Quad2
impl UnsafeUnpin for Quad2
impl UnwindSafe for Quad2
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T, U> Imply<T> for U
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more