From 105706a540ef415cfa809e1d894721caf478dd20 Mon Sep 17 00:00:00 2001 From: Pablo Brubeck Date: Fri, 10 Jul 2026 00:16:38 +0100 Subject: [PATCH 1/4] tsfc: cancel Jacobian products and reciprocal factors - Enable do_cancel_jacobian_products in compute_form_data, so that contractions of the Jacobian with its inverse are cancelled before the inverse is expanded into individual matrix entries, and apply the same simplifications in preprocess_expression. - Cancel reciprocal factors, such as detJ**2 * (1 / detJ)**2, in spectral mode and in dual evaluation. Co-Authored-By: Claude Fable 5 --- tsfc/driver.py | 1 + tsfc/spectral.py | 5 ++++- tsfc/ufl_utils.py | 14 +++++++++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/tsfc/driver.py b/tsfc/driver.py index 6ee929eced..4f1e0d34b8 100644 --- a/tsfc/driver.py +++ b/tsfc/driver.py @@ -375,6 +375,7 @@ def predicate(index): # TODO: one should apply some GEM optimisations as in assembly, # but we don't for now. + evaluation, = gem.optimise.cancel_reciprocals([evaluation]) evaluation, = impero_utils.preprocess_gem([evaluation]) pairs = unconcatenate([(return_expr, evaluation)]) impero_c = impero_utils.compile_gem(pairs, return_indices) diff --git a/tsfc/spectral.py b/tsfc/spectral.py index 69e471104e..c8251d61a6 100644 --- a/tsfc/spectral.py +++ b/tsfc/spectral.py @@ -6,7 +6,7 @@ from gem.node import Memoizer, MemoizerArg from gem.optimise import filtered_replace_indices from gem.optimise import delta_elimination as _delta_elimination -from gem.optimise import replace_division, unroll_indexsum +from gem.optimise import cancel_reciprocals, replace_division, unroll_indexsum from gem.refactorise import ATOMIC, COMPOUND, OTHER, MonomialSum, collect_monomials from gem.unconcatenate import unconcatenate from gem.coffee import optimise_monomial_sum @@ -34,6 +34,9 @@ def Integrals(expressions, quadrature_multiindex, argument_multiindices, paramet # Rewrite: a / b => a * (1 / b) expressions = replace_division(expressions) + # Cancel reciprocal factors, such as detJ**2 * (1 / detJ)**2 + expressions = cancel_reciprocals(expressions) + # Unroll max_extent = parameters["unroll_indexsum"] if max_extent: diff --git a/tsfc/ufl_utils.py b/tsfc/ufl_utils.py index cf47fb8974..ce25dc3087 100644 --- a/tsfc/ufl_utils.py +++ b/tsfc/ufl_utils.py @@ -14,6 +14,8 @@ from ufl.algorithms.apply_derivatives import apply_derivatives from ufl.algorithms.apply_geometry_lowering import apply_geometry_lowering from ufl.algorithms.apply_restrictions import apply_restrictions +from ufl.algorithms.cancel_jacobian_products import cancel_jacobian_products +from ufl.algorithms.remove_component_tensors import remove_component_tensors from ufl.algorithms.comparison_checker import do_comparison_check from ufl.algorithms.remove_complex_nodes import remove_complex_nodes from ufl.algorithms.signature import compute_expression_signature @@ -40,6 +42,7 @@ def compute_form_data(form, do_apply_integral_scaling=True, do_apply_geometry_lowering=True, preserve_geometry_types=preserve_geometry_types, + do_cancel_jacobian_products=True, do_apply_default_restrictions=True, do_apply_restrictions=True, do_estimate_degrees=True, @@ -62,6 +65,7 @@ def compute_form_data(form, do_apply_integral_scaling=do_apply_integral_scaling, do_apply_geometry_lowering=do_apply_geometry_lowering, preserve_geometry_types=preserve_geometry_types, + do_cancel_jacobian_products=do_cancel_jacobian_products, do_apply_default_restrictions=do_apply_default_restrictions, do_apply_restrictions=do_apply_restrictions, do_estimate_degrees=do_estimate_degrees, @@ -136,11 +140,19 @@ def preprocess_expression(expression, complex_mode=False, expression = do_comparison_check(expression) else: expression = remove_complex_nodes(expression) + jacobian_types = (Jacobian, JacobianInverse, JacobianDeterminant) + lowering_preserve_types = preserve_geometry_types + jacobian_types expression = apply_algebra_lowering(expression) expression = apply_derivatives(expression) expression = apply_function_pullbacks(expression) - expression = apply_geometry_lowering(expression, preserve_geometry_types) + expression = apply_geometry_lowering(expression, lowering_preserve_types) + expression = apply_derivatives(expression) + expression = apply_geometry_lowering(expression, lowering_preserve_types) expression = apply_derivatives(expression) + # Cancel contractions of the Jacobian with its inverse before + # expanding the inverse into individual matrix entries + expression = remove_component_tensors(expression) + expression = cancel_jacobian_products(expression) expression = apply_geometry_lowering(expression, preserve_geometry_types) expression = apply_derivatives(expression) if not complex_mode: From 8837275c556c7e486abd3b01276b283e1b1f504f Mon Sep 17 00:00:00 2001 From: Pablo Brubeck Date: Fri, 10 Jul 2026 00:38:29 +0100 Subject: [PATCH 2/4] DROP BEFORE MERGE Co-Authored-By: Claude Fable 5 --- .github/actions/install/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/install/action.yml b/.github/actions/install/action.yml index 54ca08a5d1..1197be6ac9 100644 --- a/.github/actions/install/action.yml +++ b/.github/actions/install/action.yml @@ -155,6 +155,7 @@ runs: --extra-index-url https://download.pytorch.org/whl/cpu \ "./firedrake-repo[${{ inputs.deps }}]" + pip install -v --no-deps --ignore-installed git+https://github.com/firedrakeproject/ufl.git@pbrubeck/cancel-jacobian firedrake-clean pip list From 11de37a3b5eee00d2f1ba505ed31d5bb2e08edba Mon Sep 17 00:00:00 2001 From: Pablo Brubeck Date: Fri, 10 Jul 2026 12:58:00 +0100 Subject: [PATCH 3/4] DROP BEFORE MERGE --- .github/actions/install/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/install/action.yml b/.github/actions/install/action.yml index 1197be6ac9..7dbca1ee09 100644 --- a/.github/actions/install/action.yml +++ b/.github/actions/install/action.yml @@ -155,6 +155,7 @@ runs: --extra-index-url https://download.pytorch.org/whl/cpu \ "./firedrake-repo[${{ inputs.deps }}]" + pip install -v --no-deps --ignore-installed git+https://github.com/firedrakeproject/fiat.git@pbrubeck/cancel-jacobian pip install -v --no-deps --ignore-installed git+https://github.com/firedrakeproject/ufl.git@pbrubeck/cancel-jacobian firedrake-clean pip list From 0abd85a6917d5c49ff772523d453c39483d40c42 Mon Sep 17 00:00:00 2001 From: Pablo Brubeck Date: Sun, 12 Jul 2026 14:31:48 +0100 Subject: [PATCH 4/4] drop cancel_reciprocal --- tsfc/driver.py | 1 - tsfc/spectral.py | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/tsfc/driver.py b/tsfc/driver.py index 4f1e0d34b8..6ee929eced 100644 --- a/tsfc/driver.py +++ b/tsfc/driver.py @@ -375,7 +375,6 @@ def predicate(index): # TODO: one should apply some GEM optimisations as in assembly, # but we don't for now. - evaluation, = gem.optimise.cancel_reciprocals([evaluation]) evaluation, = impero_utils.preprocess_gem([evaluation]) pairs = unconcatenate([(return_expr, evaluation)]) impero_c = impero_utils.compile_gem(pairs, return_indices) diff --git a/tsfc/spectral.py b/tsfc/spectral.py index c8251d61a6..69e471104e 100644 --- a/tsfc/spectral.py +++ b/tsfc/spectral.py @@ -6,7 +6,7 @@ from gem.node import Memoizer, MemoizerArg from gem.optimise import filtered_replace_indices from gem.optimise import delta_elimination as _delta_elimination -from gem.optimise import cancel_reciprocals, replace_division, unroll_indexsum +from gem.optimise import replace_division, unroll_indexsum from gem.refactorise import ATOMIC, COMPOUND, OTHER, MonomialSum, collect_monomials from gem.unconcatenate import unconcatenate from gem.coffee import optimise_monomial_sum @@ -34,9 +34,6 @@ def Integrals(expressions, quadrature_multiindex, argument_multiindices, paramet # Rewrite: a / b => a * (1 / b) expressions = replace_division(expressions) - # Cancel reciprocal factors, such as detJ**2 * (1 / detJ)**2 - expressions = cancel_reciprocals(expressions) - # Unroll max_extent = parameters["unroll_indexsum"] if max_extent: