gh-40113: Support implicitly defined species inside compositions#42442
Open
mwhansen wants to merge 2 commits into
Open
gh-40113: Support implicitly defined species inside compositions#42442mwhansen wants to merge 2 commits into
mwhansen wants to merge 2 commits into
Conversation
…ate species Composing a univariate (or multivariate) species `F` with single sorts, e.g. `F(X)`, previously went through the full composition machinery even though it only relabels the sorts of the molecular species in the support of `F`. This was very slow: `L1.Cycles()(X)[20]` took ~2s (and ~19s on a cold cache). Add a fast path inside `CompositionSpeciesElement` that detects when every argument is a singleton `Y_i` of the target ring and computes the result by relabelling the domain partitions, via the new helper `PolynomialSpeciesElement._relabel_sorts`. The result is still a `CompositionSpeciesElement`, so its specialized `structures`, `generating_series` and `cycle_index_series` are preserved unchanged. `L1.Cycles()(X)[20]` now takes ~0.0002s.
Make define_implicitly work when an implicitly defined species appears
inside a composition (plethysm), e.g. the implicit compositional inverse
defined by E_{>=1}(Omega) = X.
The undetermined coefficients of an implicitly defined species are
molecular multiplicities, hence constants of the lambda-ring, so the
power-sum (Adams) operations fix them; only the base-ring variables
(weights) are treated as monomials. This keeps each degree's coefficient
comparison linear in the newest unknowns.
- species.py: in _compose_with_weighted_singletons, compute the plethysm
over a ring containing the (possibly undetermined) multiplicities, and
route stretch through CoefficientRing._power_sum_plethysm.
- stream.py: add CoefficientRing._power_sum_plethysm (fixes undetermined
coefficients, stretches base-ring monomials); make
Stream_function.input_streams also detect streams captured inside a
list or tuple in the closure.
- lazy_species.py: build the composite coefficient over the ring its
coefficients live in, rebuild the per-call argument snapshots so that
values refined by the solver are re-read rather than memoized while
undetermined, and expose the argument streams so the solver
substitutes determined values into their caches.
|
Documentation preview for this PR (built with commit 282fe74; changes) is ready! 🎉 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This makes
define_implicitlywork when an implicitly defined species occurs inside a composition (plethysm), resolving #40113. The motivating example is the implicit compositional inverse ofE_{\geq 1}:The obstruction and its resolution
The difficulty raised in #40113 was computing, for instance,
E(f_0 X)_3withf_0an undetermined coefficient, since the result depends polynomially rather than linearly onf_0.The resolution is to recognise which kind of
\lambda-ring element an undetermined coefficient is: thef_{n,M}in\Omega_n = \sum_M f_{n,M} Mare molecular multiplicities — eventually non-negative integers — hence constants, so the power-sum (Adams) operations fix them,p_k[f_{n,M}] = f_{n,M}. Only the genuine weight variables (yourq) are monomials, withp_k[q] = q^k. With this convention,which specialises at
f_0 = 3to3*E_3 + 6*X*E_2 + X^3, as it must.The polynomial dependence does not break the solver: for an equation
F(\Omega) = 0of positive valuation,\Omega_nenters the degree-ncomparison only through the degree-one (identity) part of the outer factors, hence linearly; any product or genuine plethysm of\Omega_nneeds total degree> n. So the higher powersf_0^2, f_0^3only ever multiply already determined lower-degree coefficients, and the existing "keep the linear equations" strategy still determines everything.Implementation
p_k(thestretchinPolynomialSpecies._exponential) now fixes undetermined coefficients and stretches only base-ring monomials, via the newCoefficientRing._power_sum_plethysm._compose_with_weighted_singletonsis computed over the coefficient ring extended by the solver's variables.Stream_function.input_streamsnow also detects streams captured inside a list/tuple in the closure, so the composition's argument streams (in particular derived ones such asq*\Omega) are substituted into; and the per-call argument snapshots are re-read rather than memoised while still undetermined.Tested cases
Correctness is checked by cross-validating independent code paths — against
revert(), against an explicitdefine()(which feeds determined coefficients into the composition), self-consistency, and counts:E_{>=1}(\Omega) = Xrevert()(implicit Lagrange inversion)A = X*E(A)define== implicit; countn^{n-1}A = X + E_2(A)A = X + E_2(B),B = X + E_2(A)A = BA = Y + q*E_2(A)QQ[q])B = Y + E_2(q*B)D = Y*E(q*D)E, valuation 1C = Y + (E(q*C) - 1)QQ(q); needs the fraction fieldOne boundary
When the linear part scales the unknown by a weight (e.g.
E_1(q\Omega) = q\Omegaoccurs in the equation), then(1 - q) f_{n,M} = (\text{lower}), so the solution lies inFrac(k)and the problem has to be posed over, say,QQ(q)rather thanQQ[q]. This is the same reasonrevert()currently requires a field base ring.Dependencies