Skip to content

Tensor: host ops cover all batches of a batched tensor - #614

Merged
evaleev merged 2 commits into
masterfrom
evaleev/fix/tensor-host-batch
Nov 12, 2026
Merged

evaleev merged 2 commits into
masterfrom
evaleev/fix/tensor-host-batch

Conversation

@evaleev

@evaleev evaleev commented Nov 12, 2026 •

Copy link
Copy Markdown
Member

Host TA::Tensor ops ignored all but the first batch of a batched tensor (nbatch() > 1):

  • Value-returning ops returned one batch. clone, scale, neg, add, subt, mult, unary, binary (with or without a permutation) go through the op-constructors Tensor(other, op[, perm]) / Tensor(left, right, op[, perm]), which hard-coded nbatch = 1.
  • In-place ops touched only batch 0. scale_to, neg_to, add_to, subt_to, mult_to, inplace_unary etc. go through the contiguous inplace_tensor_op kernel, which swept range().volume() elements.
  • Permutation threw. The permute kernel addresses elements via at_ordinal, which asserts nbatch() == 1.

Reductions (sum, squared_norm, dot, ...) already used total_size() and were correct.

Changes

  • tensor/utility.h: detail::nbatch(t) (1 for types without nbatch()), detail::is_batched_tensor_v<T> (has nbatch() and batch(idx)), detail::is_nbatch_congruent(ts...), and detail::check_nbatch_congruent(ts...), which throws TA_EXCEPTION in all builds. A comment lists which Tensor accessors describe only the first batch (range(), size(), begin()/end(), operator[], at_ordinal).
  • tensor/kernels.h: the contiguous inplace_tensor_op and tensor_init kernels sweep range().volume() * nbatch elements. Every inplace_tensor_op/tensor_init kernel, contiguous or not, rejects operands with different batch counts.
  • tensor/permute.h: detail::permute loops over batches, permuting each batch(b) (the range describes one batch), and rejects mismatched batch counts.
  • tensor/tensor.h: the op-constructors and the converting constructor take the batch count from their (left) argument. For a tensor of tensors, Tensor(other, op, perm) and Tensor(left, right, op, perm) apply the inner permutation to every batch.

Behavior for nbatch() == 1 tensors is unchanged.

Behavior change: combining tensors with different batch counts now throws TiledArray::Exception in every build. Before, such a call silently processed only the first batch. The check is a hard check rather than a TA_ASSERT because with whole-tensor sweeps a mismatch would otherwise read or write past the end of the smaller operand in Release. A batched tensor combined with a non-contiguous view (TensorInterface) is likewise rejected; without the check it returned batches that were never written.

Testing

New tests in tensor_suite:

  • batched_ops checks every op above on a 3-batch Tensor<double> against the op applied batch-by-batch to plain copies, plus the reductions. Each op is checked independently, so one throwing op doesn't hide the rest. On master it fails (clone returns 1 batch; permute throws). It also checks that mismatched batch counts and view operands throw.
  • batched_tot_ops does the same for a batched Tensor<Tensor<double>>: clone, scale, permute and add with a bipartite permutation, and the op+permutation constructor. With the old inner-permutation loop it fails.

Local macOS Debug build (TA_ASSERT_THROW, Pthreads): full ta_test passes at np=1 (!@distributed) and np=2 (!@serial).

Found while adding batched-tile tests for the device path in #554, whose device branches already cover all batches.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VUXmaoaBxkXhMkXUT1Vn9v

Value-returning ops (clone, scale, neg, add, subt, mult, unary, binary,
with or without permutation) returned a single batch because the
op-constructors hard-coded nbatch = 1; in-place ops (scale_to, neg_to,
add_to, ..., inplace_unary) touched only batch 0 because the contiguous
inplace_tensor_op/tensor_init kernels swept range().volume() elements;
permutation threw on at_ordinal's nbatch() == 1 assertion.

- op-constructors take the batch count from their (left) argument
- contiguous kernels sweep volume * nbatch and assert batch congruence
- detail::permute permutes each batch separately
- add detail::nbatch, is_batched_tensor_v, is_nbatch_congruent helpers
- test tensor_suite/batched_ops
…ll batches

- check_nbatch_congruent (TA_EXCEPTION, all builds) in every
  inplace_tensor_op/tensor_init kernel and in detail::permute; the
  non-contiguous kernels previously had no check, so a batched tensor
  combined with a view returned uninitialized batches, and in Release a
  mismatch read/wrote past the smaller operand
- Tensor(other, op, perm) and Tensor(left, right, op, perm) apply the
  inner permutation of a tensor of tensors to all batches, not just the
  first (range-for stops at size())
- document which Tensor accessors describe only the first batch
- tests: each op checked independently; mismatched and view operands are
  rejected; batched tensor-of-tensors ops incl. inner permutation
@evaleev
evaleev merged commit f5da2c1 into master Nov 12, 2026
9 checks passed
@evaleev
evaleev deleted the evaleev/fix/tensor-host-batch branch November 12, 2026 18:56
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