Sum factorisation on simplices#5263
Open
pbrubeck wants to merge 11 commits into
Open
Conversation
Extends tsfc/fem.py's translate_coefficient and translate_argument to
take a sum-factorized (Duffy/lattice) tabulation path for simplicial DG
Legendre elements under dx(scheme="collapsed"), targeting O(p^{d+1})
flops for the matrix-free residual instead of the dense O(p^{2d}).
The lattice multiindex from Legendre.duffy_evaluation is gathered
against coefficients and scattered back to the flat dof index through
FIAT's existing Morton dof numbering (new morton_forward_table /
morton_inverse_table in FIAT.expansions), entirely inside fem.py:
element.index_shape and argument_multiindices stay flat, so no
driver.py or kernel_interface changes are needed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
pbrubeck
commented
Jul 20, 2026
pbrubeck
commented
Jul 20, 2026
pbrubeck
commented
Jul 20, 2026
pbrubeck
commented
Jul 20, 2026
pbrubeck
commented
Jul 20, 2026
pbrubeck
commented
Jul 20, 2026
pbrubeck
left a comment
Contributor
Author
There was a problem hiding this comment.
TSFC codegen diff should be as tight as possible. Add the new code in a separate finat submodule.
pbrubeck
commented
Jul 20, 2026
Comment on lines
+712
to
+742
| """Whether the sum-factorized (Duffy/lattice) coefficient contraction | ||
| applies. | ||
|
|
||
| This holds exactly when `element` is a simplicial DG element whose | ||
| nodal basis coincides with the Dubiner expansion set (any | ||
| `finat.duffy.DuffyElement`), evaluation points come from a collapsed | ||
| tensor-product quadrature rule (requested via | ||
| ``dx(scheme="collapsed")``), and the integral is over the cell | ||
| interior. In that case `DuffyElement.duffy_contraction` contracts a | ||
| `Coefficient` against the element in O(p^d) space/time using the | ||
| lattice multi-index, whereas the standard dense contraction | ||
| materializes all O(p^d) basis functions at all O(p^d) points before | ||
| contracting. | ||
|
|
||
| The argument (basis evaluation) side needs no such dispatch: it is | ||
| handled transparently by `DuffyElement.basis_evaluation`, reached | ||
| through the usual `~.PointSetContext.basis_evaluation` call. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| element : finat.finiteelementbase.FiniteElementBase | ||
| The element being tabulated. | ||
| ctx : ContextBase | ||
| The translation context. | ||
|
|
||
| Returns | ||
| ------- | ||
| bool | ||
| Whether to use `DuffyElement.duffy_contraction` in place of the | ||
| standard dense contraction. | ||
| """ |
Contributor
Author
There was a problem hiding this comment.
One-line docstring
pbrubeck
commented
Jul 20, 2026
…rred Legendre's dof order is now lattice-lexicographic (permuted in FIAT), which simplified finat/duffy.py's index arithmetic but did not eliminate the VariableIndex gather/scatter: get_indices() must stay a flat index because it can't distinguish a cell-interior kernel from a facet-integral kernel, and facet tabulation always uses the dense, flat-(ndof,) FIAT path. Fully eliminating the gather/scatter would need a bespoke jagged gem.FlexiblyIndexed view in kernel_interface/ common.py's prepare_arguments/prepare_coefficient, shared code every Firedrake kernel depends on -- deliberately deferred as a separate, higher-risk follow-up. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Temporarily pins firedrake-fiat to pbrubeck/simplex-sum-factor so CI exercises this branch's paired FIAT changes (dof-order permutation, gem.Delta fix). Revert to @main once the FIAT PR merges. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
pbrubeck
commented
Jul 21, 2026
| u = TrialFunction(V) | ||
| v = TestFunction(V) | ||
| w = Function(V) | ||
| w.dat.data[:] = np.random.default_rng(0).random(w.dat.data.shape) |
Contributor
Author
There was a problem hiding this comment.
use rg = RandomGenerator(PCG64(seed=0))
pbrubeck
commented
Jul 21, 2026
pbrubeck
commented
Jul 21, 2026
translate_coefficient no longer dispatches on isinstance(element, DuffyElement): finat.duffy.DuffyElement.basis_evaluation now returns an already flat-dof-indexed tabulation, so the generic dense contraction path applies uniformly and recovers the same sum-factorized complexity without any special-casing. This also makes the Duffy fast path compose with Vector/TensorElement, which previously fell through to the slow path since TensorFiniteElement is never itself a DuffyElement instance. test_duffy_scatter_and_contract updated to contract generically instead of calling the now-removed duffy_contraction. test_collapsed_quadrature_sum_factorisation now compares against dx(scheme="canonical") instead of the default scheme: same collapsed Gauss-Jacobi points/weights as dx(scheme="collapsed"), but tabulated via the dense FIAT path, isolating the comparison to the sum-factorized tabulation itself rather than to a difference in quadrature rule. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
pbrubeck
commented
Jul 21, 2026
|
|
||
| @pytest.mark.parametrize("element_name", ["Legendre", "IntegratedLegendre"]) | ||
| @pytest.mark.parametrize("cellname,degree", [("triangle", 3), ("tetrahedron", 2)]) | ||
| def test_duffy_scatter_and_contract(cellname, degree, element_name): |
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.
Depends on firedrakeproject/fiat#262
Real-kernel benchmark (tsfc-compiled, DG triangle mass action)
Jagged index support (gem + tsfc)
gem.JaggedIndexis a subclass ofIndexcarrying a parents tuple; its .extent stays the static rectangular bound, so every existing consumer that ignores jaggedness remains correct via the zero-padding invariant.In tsfc/loopy.py, a For loop over a jagged index nested inside its parents now gets a parametrized ISL domain ([p] -> {[q]: 0 <= q < E - p}); otherwise it silently falls back to the rectangular bound. The proof is an executed-kernel test: a 2D Morton-gather contraction compiled through compile_gem → tsfc.loopy.generate produces for (i_2 = 0; i_2 <= 4 + -1*i_1; ...) in the C and matches numpy to machine precision. duffy_evaluation now returns jagged lattice indices, so its consumers get the triangular loops for free.
Matrix-free sum-factorized residual (tsfc/fem.py)
translate_coefficient/translate_argumenttake the sum-factorized (Duffy/lattice) tabulation path for elements mixing infinat.duffy.DuffyElement(DG Legendre, and CG IntegratedLegendre once the paired FIAT PR added C0 recombination), targeting O(p^{d+1}) flops for the coefficient-contraction path instead of dense O(p^{2d}).element.index_shape/argument_multiindicesstay flat; the lattice ⟷ flat-dof reindexing happens entirely insidefinat/duffy.py(paired PR), so no driver.py or kernel_interface changes were needed for this part.Sum-factorise by default
tsfc/kernel_interface/common.py::set_quad_rulenow defaults toscheme="collapsed"whenever any element in the form is aDuffyElement, instead of requiringdx(scheme="collapsed")spelled out explicitly.Tests
tests/tsfc/test_codegen.py:duffy_evaluation/scatter/contract correctness against a dense reference, for both Legendre and IntegratedLegendre, triangle and tetrahedron.tests/tsfc/test_sum_factorisation.py: flop-scaling for mass/Laplacian action (DG+CG, triangle/tetrahedron), and separately for the bilinear form with noaction— both test and trial bases scattered simultaneously, the configuration that exposed the loopy scheduling bug fixed in the paired FIAT PR.tests/firedrake/regression/test_quadrature.py: end-to-endassemble()comparison ofdxvsdx(scheme="collapsed")for DG/CG mass matrices and a gradient residual.