Repository navigation
Conversation
d700027 to
5e39fb2
Compare
The hash of a Slate expression used the signature of each Tensor form, which numbers the coefficients of that form only. Two expressions that use different coefficients in the same position got the same hash, and the second reused the kernel of the first. The terminals of every operand are now numbered by their position in the whole expression, as the kernel numbers its arguments. The hash is renamed to signature(), as for UFL forms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UGJuwu7dPzKbSmATxWo9Qc
Dat, Scalar and Tensor copies were all named "<name>_copy", so two copies of the same tensor shared a name. Loop arguments are replaced by name, and pyop3 drops a name that two arguments share. The copy name is now made unique, as for buffers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UGJuwu7dPzKbSmATxWo9Qc
5e39fb2 to
f1bfde4
Compare
…ients The loops of a ParloopFormAssembler are cached on the mesh, keyed by the signature of the form, its function spaces, its subdomain data and the assembler options. A form with new coefficients reuses the loops, and its coefficients are substituted when it is assembled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UGJuwu7dPzKbSmATxWo9Qc
f1bfde4 to
d4209ce
Compare
|
Hi Pablo, thanks for this, this seems to help the regressions in our timings the most. I tested the three commits cherry-picked onto The first gives wrong values without any error. from firedrake import *
mesh = UnitSquareMesh(10, 10)
x, y = SpatialCoordinate(mesh)
W = FunctionSpace(mesh, "DG", 1) * FunctionSpace(mesh, "CG", 2)
# Standalone Functions on the two subspaces of W
u1 = Function(W.sub(0)).interpolate(x + 2*y)
v1 = Function(W.sub(1)).interpolate(sin(4*pi*x)*cos(4*pi*y))
print(assemble((u1 - v1)**2*dx))
# The same values in the subfunctions of a Function on W
w = Function(W)
u2, v2 = w.subfunctions
u2.assign(u1)
v2.assign(v1)
print(assemble((u2 - v2)**2*dx)) # should print the same numberOn d4209ce this prints 2.9112 and then 0.1828 (pyop2 gives 2.9112 twice, and on 3 ranks the second value can be nan). It also makes The second is speed. There is a PR fixing each of these against your branch: #5573 for the key and #5574 for the speed. |
* Assembly: Add the coefficient layouts to the loop cache key A subfunction of a mixed Function is a view into the dat of its parent, and a Function on the same subspace has a dat of its own. Both have the same space, parent and index, so they got the same key and shared a loop that was built for the other layout, which gives wrong values. * Test loop sharing between coefficients with different layouts * Group the axis trees at the end of the loop cache key * Apply suggestion from @pbrubeck --------- Co-authored-by: Pablo Brubeck <brubeck@protonmail.com>
… pbrubeck/interpolate-cache
c407caf to
0e10a44
Compare
Stacked on #5258. Includes #5567.
Summary
Forms that differ only in their coefficients now share their loops.
Function.interpolatebuilds a new form on every call, and so do multigrid transfers through a quadrature space. Before, each call built and hashed new loops.ParloopBuilderrecords the names of the loop arguments that take the coefficients and constants. Two commits make those names reliable:signature()that does not depend on the coefficients, so they share loops too. It also fixes a wrong-result bug: two Slate expressions with different coefficients in the same position shared a kernel.Datwere both named<name>_copy, and pyop3 drops a loop argument name that two arguments share.Evidence
test_loops_shared_between_coefficients(new, 2 ranks, forms of rank 0, 1 and 2) andtest_interpolate_new_coefficients(new, 2 ranks, forward and adjoint) assemble forms with new coefficients, a mixed coefficient and a newConstant:After: one set of loops; all 7 cases fail without the coefficient substitution
test_caching_distinguishes_coefficient_positions(new) assemblesTensor(f*m) + Tensor(2*g*m) + Tensor(3*c*m)withc = f, thenc = g:After: 24 M and 51 M
test_hessian.py::test_burgers[nlvs]failed in CI withKeyError: 'function_13_copy'and passes with the unique copy names.Repeated interpolation on BDM2 16×16 through
Vq = V.quadrature_space, per call:Function(V).interpolate(q)Cofunction(Vq.dual()).interpolate(r)qon each callron each callMerge Danger
Door: two-way
Blast Radius: form assembly
A form that shares loops reads the wrong data if the key misses something the loops depend on. The key holds the parent and index of each space (subspaces of a mixed space compare equal) and the subdomain data (the subset of an
Interpolateis not in its signature). The loops of the first form of each key keep its dats alive with the mesh. Slate kernels are compiled again once, because their cache key changes.AI assistance: this PR was written with Claude Code (Claude Opus 5.5).
🤖 Generated with Claude Code
https://claude.ai/code/session_01UGJuwu7dPzKbSmATxWo9Qc