Skip to main content

Quad2

Struct Quad2 

Source
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 − x folds fl(1) + fl(−1) to 0.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 the at fl(2⁵³ + 1) = 2⁵³, which is an inexact add, and only then does 2⁵³ − 2⁵³ drop the survivor. The body is ; 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

Source

pub fn constant(&self) -> f64

The degree-0 term.

Source

pub fn linear(&self) -> &BTreeMap<usize, f64>

The degree-1 terms, ascending by variable index.

Source

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).

Source

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.

Source

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§

Source§

impl Clone for Quad2

Source§

fn clone(&self) -> Quad2

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Quad2

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for Quad2

Source§

fn default() -> Quad2

Returns the “default value” for a type. Read more
Source§

impl PartialEq for Quad2

Source§

fn eq(&self, other: &Quad2) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

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> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> ByRef<T> for T

Source§

fn by_ref(&self) -> &T

Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Imply<T> for U
where T: ?Sized, U: ?Sized,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more