Repository navigation
Conversation
| m = 10 | ||
| axis = op3.Axis(m) | ||
| dat0 = op3.Dat(axis, data=np.arange(m, dtype=op3.ScalarType)) | ||
| dat1 = op3.Dat(axis, data=np.full(m, (m - 1) / 2, dtype=op3.ScalarType)) |
There was a problem hiding this comment.
Seems weird to use (m - 1) / 2. It is confusing to read because it makes me think that there's something special about that value.
There was a problem hiding this comment.
I changed to an explicit 4.5
| @pytest.mark.skipcomplex # complex numbers are not orderable | ||
| def test_interpolate_periodic_coords_max(): | ||
| @pytest.mark.parallel([1, 3]) | ||
| @pytest.mark.parametrize("access,seam", [(MAX, 1), (op3.MAX_WRITE, 1), (op3.MIN_WRITE, 0)]) |
There was a problem hiding this comment.
MIN and MAX_RW and MIN_RW?
Definitely should check MIN but not sure about the others. I had to introduce RW and WRITE variants in pyop3 but the distinction isn't important at the Firedrake level.
There was a problem hiding this comment.
MIN, MAX are just the same as MIN_RW, MAX_RW
Stack the feature on #5543, which supports MIN and MAX reductions in pyop3. firedrake/interpolation.py keeps the feature's version. #5543 changes SameMeshInterpolator._get_tensor to accept MIN_RW and MAX_RW. The feature removes _get_tensor, and its Interpolator.__init__ already converts MIN_RW and MAX_RW to MIN_WRITE and MAX_WRITE. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Kernel calls with MIN_WRITE or MAX_WRITE access now pack a temporary that starts from the identity of the reduction, and unpack it with a "min" or "max" assignment. Assignments of type "min" and "max" now get their intents from assignment_type_as_intent, so they can be put in a loop. Fixes #5524 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TSFC kernels add their result to the output argument. A MIN_WRITE or MAX_WRITE temporary that starts from the identity of the reduction therefore stays at the identity. op3.Function now takes requires_zeroed_output_arguments, like the PyOP2 Kernel did. When it is set, these temporaries start from zero. as_pyop3_local_kernel sets it for TSFC kernels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4850bd3 to
455a844
Compare
| The library directories that are needed to link the kernel. | ||
| libs | ||
| The libraries that are needed to link the kernel. | ||
| requires_zeroed_output_arguments |
There was a problem hiding this comment.
No. This is exactly the difference between MAX_RW and MAX_WRITE.
There was a problem hiding this comment.
Could you elaborate please?
There was a problem hiding this comment.
The entire difference between these operations is what we pack on the way into the local kernel. Is it the actual data or is it something null.
t <- -inf
kernel(t)
dat <- max(dat, t)
vs
t <- dat
kernel(t)
dat <- max(dat, t)
There was a problem hiding this comment.
The standard Interpolate(expr, V, access=WRITE) generates a kernel whose output tensor should be zeroed at the start. For each degree of freedom we compute a weighted sum of the expr at the element's quadrature points.
tensor[i] = 0
for j in range(dual_mat.shape[1]):
tensor[i] += dual_mat[i, j] * expr(pts[j])
For Interpolate(expr, V, access=MAX) one would like to set -inf on the dat first, which means that we are no longer able to accumulate on the output. Instead we need to call the WRITE kernel on a temporary, on which we still acumulate the weighted sum.
tensor[i] = -inf
def kernel(temp)
temp[i] = 0
for j in range(dual_mat.shape[1]):
temp[i] += dual_mat[i, j] * expr(pts[j])
tensor[i] = max(tensor[i], temp[i])
There was a problem hiding this comment.
I have just played around with this. If you delete requires_zeroed_output_arguments then the interpolate tests still pass
There was a problem hiding this comment.
The flag is here for #5258, it is not needed for the bespoke Interpolate codegen, only for the unified TSFC approach.
There was a problem hiding this comment.
I think #5258 should use generate_loopy(..., return_increments=(acces == INC))
Fixes #5524.
Summary
Kernel calls (
pyop3/insn/visitors.py,expand_transformsforCalledFunction):The temporary starts from the identity of the reduction, so the entries that the kernel does not write leave
xunchanged.exec.pyuses the same identities on the leaves.Assignments (
pyop3/insn/base.py,AbstractAssignment):Evidence
Before:
After: both reproducers from pyop3: MIN_WRITE/MAX_WRITE kernel calls and max/min assignments in loops fail #5524 give the correct values on 1 and 3 ranks.
New tests, all fail without the fix (except the existing
MAXcase) and pass with it:tests/pyop3/insn/test_loop.py::test_reduction_assignment[max/min]tests/firedrake/regression/test_interpolate.py::test_interpolate_periodic_coords_reduction[MAX/MAX_WRITE/MIN_WRITE], which replacestest_interpolate_periodic_coords_maxand runs onnprocs=1andnprocs=3. WithMIN_WRITEthe nodes on the periodic seam take the value 0, withMAX/MAX_WRITEthey take 1.Merge Danger
Door: two-way
Blast Radius: small
Only the
MIN_WRITE/MAX_WRITEintents and the"min"/"max"assignments change; these paths raised an error before.AI assistance: this PR was written with Claude Code (Claude Opus 5.5).
🤖 Generated with Claude Code