Skip to content

Sum factorise independent contractions separately - #269

Merged
pbrubeck merged 2 commits into
mainfrom
pbrubeck/sum-factorise
Aug 19, 2026
Merged

Sum factorise independent contractions separately#269
pbrubeck merged 2 commits into
mainfrom
pbrubeck/sum-factorise

Conversation

@pbrubeck

@pbrubeck pbrubeck commented Jul 29, 2026

Copy link
Copy Markdown

Stack

Merge in this order, each PR retargets to main once the one above it lands:

# PR what
1 #269 gem: sum factorise independent contractions
2 #271 FInAT: select H(div)/H(curl) components with a Delta
3 #268 FInAT: dual evaluate on each sub-element's own points
4 firedrakeproject/firedrake#5295 tsfc: dual evaluation against a Cofunction
5 firedrakeproject/firedrake#5289 p-multigrid: remove custom interpolation
6 firedrakeproject/firedrake#5297 tsfc: contract each block after unconcatenating

2 and 6 are a pair: neither is worth anything alone, though 2 is harmless on its
own — it changes how a component is selected, not what is selected.
5 and 6 are siblings on 4, in either order.

Only 4 carries a DROP BEFORE MERGE commit, pinning FIAT to
pbrubeck/fix/dual-enriched, which is now the top of the FIAT stack; 5 and 6
inherit it. Drop it once the FIAT side has landed.

Interpolating a product of three coefficients into a hexahedron CG1 space:

mesh = ExtrudedMesh(UnitSquareMesh(1, 1, quadrilateral=True), 1)
V = FunctionSpace(mesh, "CG", 1)
Function(V).interpolate(Function(V)*Function(V)*Function(V))

raised

File "gem/optimise.py", line 397, in sum_factorise
    raise NotImplementedError("Too many indices for sum factorisation!")

sum_factorise searches every ordering of the contraction indices, which is
factorial in their number, so it gave up past six of them. On a tensor product cell
each coefficient evaluation contributes one index per direction, so three
coefficients on a hexahedron already need nine.

But those indices do not interact: no factor carries the indices of more than one
coefficient, so the orderings that interleave them are never worth searching. This
splits the contraction into the connected components of the graph joining indices
that share a factor, and searches each component separately.

That fixes the failure above, and is also cheaper wherever it already worked, since
the search is over the orderings of each component rather than of all the indices at
once. A single connected contraction is still bounded at six.

Testing

test/gem/test_sum_factorise.py checks the factorised expression against the value
obtained by contracting the tables directly, for products of up to five independent
contractions of up to five indices each — well beyond the old limit — and checks that
one connected contraction is still rejected. Both new cases were confirmed to fail on
the unpatched code with the original error.

Verified end to end in Firedrake that f**n interpolates correctly for n up to 5 on
quadrilateral, triangle, hexahedron and prism meshes, to machine precision.

Full FIAT suite: 2431 passed, 26 skipped, 31 xfailed.

🤖 Generated with Claude Code

@pbrubeck

Copy link
Copy Markdown
Author

@connorjward this is a bugfix, should I target release?

@pbrubeck

Copy link
Copy Markdown
Author

I think that we only hit this in interpolate and not in assemble because assemble does some extra COFFEE processing. But arguably the right thing is to have the decomposition into smaller subproblems in one place.

@connorjward

Copy link
Copy Markdown

@connorjward this is a bugfix, should I target release?

I don't especially mind. Given that we are releasing 2026.10.0 soonish, and I have no intention of making another patch release unless required, the issue is somewhat moot.

@rckirby rckirby left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is fine, if kind of complicated. Is there a test that this is correct, or at least returns gem that evaluates to something equivalent to the previous version?

Comment thread gem/optimise.py
Comment thread gem/optimise.py Outdated
pbrubeck and others added 2 commits August 18, 2026 11:03
sum_factorise searches every ordering of the contraction indices, so it
gave up past six of them.  But a product of tensor product coefficient
evaluations has one set of indices per coefficient, and no factor carries
the indices of another, so the orderings that interleave them are never
worth searching.

Split the contraction into connected components of the graph joining the
indices that share a factor, and search each separately.  Interpolating
f*f*f into a hexahedron CG1 space raised

    NotImplementedError: Too many indices for sum factorisation!

as three coefficients contribute three indices each; it now factorises as
three independent contractions.  This is also cheaper wherever it already
worked, as the search is over the orderings of each component rather than
of all the indices at once.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@pbrubeck
pbrubeck merged commit 77cbe19 into main Aug 19, 2026
9 checks passed
@pbrubeck
pbrubeck deleted the pbrubeck/sum-factorise branch August 19, 2026 14:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants