Skip to content

Skip tabulating full tables of tensor factorised elements - #903

Open
jorgensd wants to merge 4 commits into
mainfrom
dokken/skip-tensor-product-tables
Open

jorgensd wants to merge 4 commits into
mainfrom
dokken/skip-tensor-product-tables

Conversation

@jorgensd

Copy link
Copy Markdown
Member

With sum_factorization=True, FFCx tabulates each tensor product element both as 1D factors and as a full table on the whole tensor product rule. Kernels only read the 1D factors; the full table is used only for its shape and its type (zeros, ones, piecewise, ...). Tabulating it costs O(Q^d N^2d) time and O(Q^d N^d) memory, which dominates code generation at high degree.

For a P=80 mass matrix on a quadrilateral, the full table is 6724 points × 6561 dofs = 353 MB, and Basix takes 13.6 s to build it, once for the trial and once for the test function. Code generation took 33 s, almost all of it in this tabulation and in scanning the table. With this change it takes 0.7 s, with a peak of 2 MB.

Changes

  • For cell integrals without permutations or averages, when an element has tensor factors on a tensor product rule, build_optimized_tables no longer forms the full table:
    • analyse_tensor_product_table_type derives its type from the 1D factors.
    • Its values are a TensorProductTablePlaceholder, which has the table's shape but raises if its values are read. Tables that reach code generation are asserted to be formed.
    • Offset and stride come from get_component_element.
  • The 1D factors are clamped like full tables (clamp_table_small_numbers). A table is "zeros" when every product of its factors clamps to zero, as the clamped full table would be.

Generated code

Kernels without sum factorisation are unchanged. Of the 66 sum-factorised kernels I compared (11 forms, quadrilateral and hexahedron, P = 1–3), 8 differ, only where clamping the factors now writes round-off such as -5.55e-17 as 0.0.

Tests

  • test_tensor_product_table_type: the type derived from the factors matches analyse_table_type on the full table, for quadrilaterals and hexahedra, P = 1–3, several 1D rules including collocated GLL points (quadrature tables), and derivatives up to second order, including vanishing ones.
  • test_tensor_product_table_type_clamped_to_zero: factors whose products all clamp to zero give a zero table, as the full table does.
  • test_tensor_product_table_placeholder: reading the placeholder's values raises.

🤖 Generated with Claude Code

I have reviewed the contribution and stand by the implementation

jorgensd and others added 2 commits October 13, 2026 21:06
With sum factorisation, the full table of a tensor product element on a
tensor product rule is only used for its shape and type: kernels read the
1D tensor factors. Tabulating it costs O(Q^d N^2d) time and O(Q^d N^d)
memory, which dominated code generation at high degree (33 s and a
353 MB table per argument at P=80 on a quadrilateral).

For cell integrals without permutations or averages, derive the table
type from the 1D factors and store a zero-memory placeholder of the right
shape instead. Generated code is unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- The placeholder of a table that isn't formed was an all-zero array,
  which any code reading its values would have used silently. It now has
  a shape but raises if its values are read, and tables that reach code
  generation are checked to be formed.
- Full tables are clamped before their type is analysed, but tensor
  factors weren't. Clamp them the same way, and find a zero table when
  every product of the factors clamps to zero, as the full table would.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Conflicts in ffcx/ir/elementtables.py and test/test_tensor_product.py.

This branch moves the permutation block of `build_optimized_tables`
after the sum factorisation block and wraps it in a branch, so the full
table can be skipped when an element has tensor factors. Meanwhile main
edited that block in place: #900 added a 'peak' entity branch and #902
gave the tensor factor tables a per-quadrature-rule name. Kept the
restructure and moved main's 'peak' branch into it; the table rename
and main's expression codimension check merged cleanly.

Both branches appended tests to test/test_tensor_product.py. Kept both.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@IgorBaratta

Copy link
Copy Markdown
Member

LGTM.
One minor comment,sub_tbl is clamped before the tensor product is formed, which I don't think is equivalent to clamping the full table. With atol=1e-9, factors containing 1e-10 and 1e2 become 0 and 1e2 -> 0, whereas the full product would 1e-8.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants