Expand description
Degree-≤2 recognition over an Expr DAG.
This is the classifier’s “is this row a quadratic, and which one?”
question, answered once and reused. pounce-cli’s dispatch owns the
routing decision (which ProblemClass, which solver); what lives here
is only the algebra, so the consumers that are not the CLI —
NlTnlp’s constant-structure evaluation, the parse-time recognizer, the
QcqpProblem extractor — can reach it without depending on the
command-line driver. It moved out of pounce-cli/src/dispatch.rs in
Q3 of the #588 series; see
dev-notes/quadratic-structure-exploitation.md.
§Two properties this module is built around
It is iterative. The walk carries its own work stack rather than
recursing, because the trees it is handed are not shallow: a .nl
writer that emits o0 (binary +) chains for a long sum — Pyomo does —
produces a left-deep Add tree one level per term. The recursive
predecessor aborted the process somewhere between 4 000 and 6 000 terms
on a 2 MB thread (which is what a test gets, and where the crash was
first reproduced) and between 16 000 and 24 000 on the CLI’s 8 MB main
thread. A stack overflow is an abort, not an error return, so the depth a
recognizer survives must not depend on which thread called it.
It is worth knowing what this does not fix: nl_reader’s parser
recurses too, with a fatter frame — it gives out at ~6 000 on that same
8 MB thread — so a deep .nl file still fails to load, and it fails
before reaching this module. What is fixed is every path where the tree
is already built (NlProblem::from_expressions, a model handed across
threads) and the ceiling this module used to impose on the parser’s
successor.
It never allocates per monomial. The predecessor keyed monomials on
BTreeMap<Vec<usize>, f64>, so every term cost a heap allocation and
every merge cloned one (entry(m.clone())). A degree-≤2 form has only
three shapes of term, so Quad2 stores them in three fields with
inline keys and the allocation disappears. The same change removes the
O(N²) accumulation on Add chains: the old add re-scanned the whole
accumulated map for zeros on every merge, which is quadratic down a
left-deep chain. Zeros can only appear where a merge touched, so that is
all this one looks at.
Structs§
- Factored
Quadratic - A degree-2 form kept as
Σ wₖ(bₖᵀx + dₖ)² + aᵀx + c— the shape the.nlwriter wrote, rather than its expansion about the origin. - Quad2
- 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.
- Squared
Affine - One
w·(bᵀx + d)²term of aFactoredQuadratic.
Functions§
- analyze_
quadratic - Attempt to read an expression as a polynomial of total degree ≤ 2 and
return its Hessian (constant, since the form is quadratic).
Noneif the expression is not provably quadratic ⇒ treat as general nonlinear. - analyze_
quadratic_ full - Like
analyze_quadraticbut also returns the degree-1 (linear) coefficients and the degree-0 (constant) term of the form:(Hessian, [(var, coef), …], constant). - is_
expanded_ quadratic - Is this expression already a flat sum of monomials — that is, would
reading it as
½xᵀHx + aᵀx + creproduce the same additions the writer wrote, rather than algebraically expanding something it did not? - is_
monomial_ expr - Is this expression a single monomial — the leaf shape
is_expanded_quadraticadmits on its sum spine? - is_
trivially_ zero - True if the expression is the literal constant zero the
.nlreader uses for “no nonlinear part”. - quad_
form_ readout - The
(Hessian, linear, constant)read-out of an already-recognized form — the second half ofanalyze_quadratic_full, split out so a caller holding aQuad2the parser produced (gh #588, Q5) reaches the identical numbers by the identical route. There is exactly one conversion in the crate; a second one is a second thing to keep in step. - recognize_
expr - Lower an
Exprto aQuad2, orNoneif it contains anything the recognizer cannot prove is a degree-≤2 polynomial (transcendental ops, division by a non-constant,Powwith an exponent ∉ {0, 1, 2}, products of degree > 2, external calls, comparisons,if-then-else,min/max, …).None⇒ treat as general nonlinear. - recognize_
factored_ quadratic - Recognize a degree-2 body as a sum of squared affine forms plus degree-≤1 leftovers, keeping the squares factored (gh #673).
Type Aliases§
- Quad
Form - Full quadratic read-out:
(Hessian, [(var, linear coef), …], constant). The linear and constant parts are the pieces AMPL/Pyomo fold into the nonlinear objective tree (seeanalyze_quadratic_full). - Quad
Hessian - The symmetric Hessian of a quadratic form, stored as a sparse upper-
triangular (i ≤ j) map of
(i, j) -> ∂²/∂xᵢ∂xⱼ. Empty means the expression is (at most) linear.