Repository navigation
Fix assembly and solves with the Hessian of an interpolation - #5478
danshapero wants to merge 19 commits into
Conversation
Solving derivative(J, u) == 0 for a functional J that contains interpolate(u, P), with P a space on a vertex-only mesh, failed in two places in the base form assembler: - The residual assembler of the solver uses mat_type="matfree", which skipped the expansion of derivatives. The chain rule through the interpolation turns the residual into a FormSum, which the 1-form assembler rejects. The mat_type only applies to 2-forms, so derivatives of 0-forms and 1-forms are now always expanded. - Every inner matrix of the DAG was allocated with the integral types of the output matrix. The mass matrix on the vertex-only mesh then asked for interior facets, which that mesh does not have. Only the output matrix now uses those integral types. The test also needs FEniCS/ufl#525, which fixes the symbolic Hessian. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| original_expr = expr | ||
| if mat_type != "matfree": | ||
| # Don't expand derivatives if `mat_type` is 'matfree' | ||
| if mat_type != "matfree" or len(expr.arguments()) < 2: |
There was a problem hiding this comment.
don't we set mat_type="mafree" if len(expr.arguments()) < 2 already?
This is not ideal. We should only be expanding them as a last resort |
I think the logic should involve checking for the vertex-only mesh, as opposed to changing the generic case |
|
Other tests seem to be breaking, please attribute failures and come back |
Actually this might be fine. We should not be propagating |
Install UFL from danshapero/fix-bfo-hessian (FEniCS/ufl#525) in the shared and documentation CI environments, so that CI tests this PR against the matching UFL fix. Drop this commit once firedrakeproject#525 merges. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…late Reconstructing an Interpolate now accepts the argument_slots keyword, and replaces a plain UFL Coargument in the dual slot with a Firedrake Argument on the dual of the target space. Also expand derivatives of matfree 0- and 1-forms that contain base form operators, so that the DAG traversal can assemble them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| assert np.allclose(u.dat.data, u2.dat.data) | ||
|
|
||
|
|
||
| @pytest.mark.parallel([1, 3]) |
There was a problem hiding this comment.
| @pytest.mark.parallel([1, 3]) | |
| @pytest.mark.parallel([1, 3]) | |
| @pytest.mark.skipcomplex |
| bcs = self._bcs if isinstance(e, ufl.BaseForm) and len(e.arguments()) == 2 else () | ||
| return self.base_form_assembly_visitor(e, t, bcs, *operands) | ||
| if needs_matfree_assembler(self._form, self._mat_type, self._diagonal): | ||
| return self._matrix_free_assembler.assemble(tensor=tensor) |
There was a problem hiding this comment.
Can this check not happen inside the visitor in some way?
There was a problem hiding this comment.
We need to check this early enough because this replaces a 2-form with its action (a 1-form). I wouldn't move it inside the visitor
There was a problem hiding this comment.
Why not here? It's the next call site after this.
There was a problem hiding this comment.
Actually, you are right. It makes a lot of sense to have this inside the visitor, to make sure inner matrices do not get explicitly assembled in matfree mode.
There was a problem hiding this comment.
Putting this inside the visitor is harder, it will skip preprocessing steps.
There was a problem hiding this comment.
How? It is effectively the same code just moved about.
There was a problem hiding this comment.
The post-order visitor cannot safely be the dispatch point for
MatrixFreeAssembler: it evaluates child operators before its callback, while an unexpanded matrix-free Hessian must reach the implicit matrix as a whole so its action can be preprocessed.The right seam is the current early branch in
BaseFormAssembler.assemble(), before_assemble_base_form; leaf two-forms already select their own assembler throughTwoFormAssembler.
Also consider compositions like assemble(FormSum(Matrix, Matrix), mat_type="matfree"). It is much more simpler to apply the Action at the root, otherwise we would need a specialized traversal that applies the distributive property down to each two-from leaf.
Expanding the derivatives of a Form with base form operators may turn it into another BaseForm, which determines how it is assembled. Plain Forms and Slate tensors keep their type, so TSFC expands their derivatives. A 'matfree' matrix is still expanded when its action is taken. `preprocess_base_form` used `mat_type` to decide, so a 1-form with base form operators, whose default `mat_type` is 'matfree', was not expanded. `ufl.replace` then expanded it inside the assembly visitor, turning it into a FormSum. Expand it in preprocessing instead, and drop the second traversal in the visitor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FEniCS/ufl#525 was split into firedrakeproject#530, firedrakeproject#531 and firedrakeproject#532. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
I pushed two commits here, to go with FEniCS/ufl#532 (#525 was split into #530, #531 and #532). 0f5df75: Only expand derivatives of forms with base form operators. In UFL,
Before, the check was 7077538: DROP BEFORE MERGE. CI now installs Tested locally with that UFL branch:
🤖 Generated with Claude Code |
UFL now simplifies Action(N(u; v*), F) to N(u; F) for any base form operator N and 1-form F, so the assembler can no longer split an operator from its dual slot by building an Action: restructure case (5) and the Interpolate split folded straight back. Instead, when an operator cannot consume its assembled dual slot, assemble N(u; vhat) with a coargument vhat and contract it with the assembled dual slot: - An Interpolate whose dual slot assembles to a Matrix. - An ExternalOperator with a Cofunction or Matrix in its dual slot and no assembly method registered for it. `assembly_method()` looks this up. The numerics of the Action branch move to `contract`, which both paths use. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An assembled Function has no arguments, so the contraction of an external operator with its assembled dual slot failed. Use the arguments of N(u; vhat) instead. Rename dual_slot_coargument to replace_dual_slot_by_coargument, which is what it does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolves #5477: solving
derivative(J, u) == 0failed for a functionalJcontaininginterpolate(u, P)withPon aVertexOnlyMesh. The Hessian of the data term isIᵀ M I.Depends on FEniCS/ufl#532, which computes that Hessian symbolically. The
DROP BEFORE MERGEcommit installs that branch in CI.Derivative expansion
preprocess_base_formexpands the derivatives of a Form only if it has base form operators, whatever itsmat_type. Plain Forms and Slate tensors are left to TSFC. Before, the"matfree"residual assembler skipped the expansion, so the chain rule through the interpolation produced aFormSumthatOneFormAssemblerrejects."matfree"2-form is still expanded only when its action is taken.MatrixFreeAssembleraccepts any 2-form BaseForm, such as a FormSum, and keeps the tree intact so that a single implicit matrix assembles its action.Dual slot of base form operators
UFL now simplifies
Action(N(u; v̂), F)toN(u; F)for any base form operator and 1-formF, so the assembler can no longer split an operator from its dual slot by building anAction. Restructure case (5) and theInterpolate(v, 2-form)split are removed. Instead, an operator that cannot consume its assembled dual slot is assembled asN(u; v̂)and contracted with it:Interpolatewhose dual slot assembles to a Matrix;ExternalOperatorwith a Cofunction or Matrix in its dual slot and no assembly method registered for it (assembly_method()).The numerics of the
Actionbranch move tocontract, which both paths use.Vertex-only meshes
Only the output matrix uses
allocation_integral_types. An inner matrix allocated with the output's facet integral types crashed on a vertex-only mesh.Tests
test_solve_interp_hessian: a Newton solve with the Hessian of an interpolation onto a vertex-only mesh.test_matfree_form_sum_uses_implicit_matrixandtest_matfree_slate_sum_product_uses_implicit_matrix.test_reduced_functional.py::test_interpolateruns a second-order Taylor test with the Hessian.🤖 Generated with Claude Code