Skip to content

Make sparse block tensors consistent with TensorOperations allocators - #82

Open
lkdvos wants to merge 5 commits into
mainfrom
allocator
Open

lkdvos wants to merge 5 commits into
mainfrom
allocator

Conversation

@lkdvos

@lkdvos lkdvos commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

Sparse block tensors were allocated empty and filled with heap-allocated blocks. tensorfree! then gave those blocks to the allocator, which had never handed them out. After this PR, tensorfree! only receives arrays that were allocated as temporaries, and every one of them is freed.

  • Sparse temporaries keep their allocator (nothing for other tensors). Any kernel that needs a missing block of a temporary, including mul! and trace_permute!, which TensorKit calls without an allocator, gets it from there. tensorfree! only releases the blocks of temporaries.
  • TO.tensoralloc stores blocks as the type the allocator hands out, e.g. PtrArray temporaries from ManualAllocator. Before, those were silently copied into the input's storage type: the copy got freed and the original leaked. This applies to dense and sparse block tensors.
  • tensoralloc_add/tensoralloc_contract allocate the blocks an operation will write up front. This is an optimisation; blocks that are still missing, e.g. in a sum like (D + 2E), are allocated on demand.
  • Sparse tensorcontract! into a C that needs permuting goes through a temporary that holds only the product blocks, instead of a copy of all of C. With 256 blocks in C and 32 in the product, this goes from 329 ms to 42 ms.

New tests use a strict tracking allocator that hands out temporaries as PtrArrays. It fails on blocks that get converted, on frees of arrays that weren't handed out as temporaries, and on temporaries that are never freed.

Follow-up: @tensor allocates the temporary for a sum from its first term only. A TensorOperations hook that sees every term would let these be allocated completely up front.

🤖 Generated with Claude Code

@codecov

codecov Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.29412% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/tensors/indexmanipulations.jl 25.00% 3 Missing ⚠️
src/tensors/sparseblocktensor.jl 93.75% 1 Missing ⚠️
Files with missing lines Coverage Δ
src/linalg/linalg.jl 81.56% <100.00%> (-1.18%) ⬇️
src/tensors/abstractblocktensor/abstractarray.jl 71.24% <100.00%> (-0.47%) ⬇️
src/tensors/blocktensor.jl 70.10% <100.00%> (+0.31%) ⬆️
src/tensors/tensoroperations.jl 90.71% <100.00%> (+5.48%) ⬆️
src/tensors/vectorinterface.jl 97.50% <100.00%> (+0.13%) ⬆️
src/tensors/sparseblocktensor.jl 71.42% <93.75%> (+2.95%) ⬆️
src/tensors/indexmanipulations.jl 23.52% <25.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

lkdvos and others added 3 commits October 8, 2026 18:58
…nsoralloc_contract`

Sparse block tensors were allocated empty and filled lazily on the heap, while
`tensorfree!` released every block through the allocator. Allocate exactly the
blocks that will be written instead, and pre-insert the product blocks into a
sparse destination so that temporaries derived from it are complete.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Contract into a sparse `C` through a temporary holding only the product blocks
  unless `C` is a valid BLAS destination, instead of copying all blocks of `C`.
- Allocate missing blocks of sparse destinations lazily through the operation's
  allocator (as non-temporaries), so that adding a differently-patterned tensor
  into an allocator-backed temporary stays consistent.
- Deduplicate the block keys in `tensoralloc_add`, which projects several source
  blocks onto the same output block for traces.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`TO.tensoralloc` returns an empty sparse tensor that remembers whether it is a
temporary. Blocks added later are requested from the allocator with the matching
`istemp`, and `tensorfree!` (and `zerovector!`) only release blocks of temporaries,
so `tensorfree!` never receives arrays that were not allocated as temporaries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lkdvos and others added 2 commits October 9, 2026 10:17
`TO.tensoralloc` for block tensors kept the block type of the inputs, so blocks
from allocators with a different storage type (e.g. `PtrArray` temporaries of
`ManualAllocator`) were silently converted on insertion: the copy was passed to
`tensorfree!`, and the original was never released. Infer the block type from
the allocator instead, for both dense and sparse block tensors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace the `istemp` flag by the allocator itself, so that kernels which do not
receive an allocator (`mul!` from TensorKit's `blas_contract!`/`planarcontract!`,
`trace_permute!` from `tensortrace!`) can still obtain missing blocks of a
temporary through it. Allocating the blocks up front in `tensoralloc_add` and
`tensoralloc_contract` is now an optimization rather than required for correctness.

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

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant