Skip to content

feat: make .* and ./ synonyms for tensor scaling - #24

Merged
wardjm merged 1 commit into
mainfrom
tensor-broadcast
Jul 12, 2026
Merged

wardjm merged 1 commit into
mainfrom
tensor-broadcast

Conversation

@wardjm

@wardjm wardjm commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Closes #19.

AbstractTensor defined no Base.broadcastable, so weights .* ω failed with a MethodError that gave no hint that weights * ω is the supported spelling. Our port of upstream's test_spectrum_eigenvector_batch_scaling_with_numpy also had to drop its third arm, np.multiply(weights, vecs), which __array_ufunc__ routes back to Tensor.__mul__.

What this does

Of the three shapes the issue put up, this takes the custom BroadcastStyle one. A tensor is never an array to the broadcast machinery: it carries its own style, instantiate skips the axis computation, and copy hands a dotted * or / straight back to the tensor algebra.

  • weights .* ω is exactly weights * ω — a single batched tensor, never an Array{Form} (the divergence that made the Ref(t) option unattractive).
  • The synonymy is exact and applies to every operand * and / accept: batch weights, a scalar, a ScalarFunction, another tensor. So a1 .* b1 is the tensor product, just as a1 * b1 is.
  • @. flattens a chain into one n-ary call, so n-ary * and / are folded the way Julia associates them (@. w * X / 2).
  • Every other elementwise operation (ω .+ 1, abs.(ω)) is meaningless on basis-dependent coefficients and now throws an ArgumentError naming the supported spellings, instead of a bare MethodError.

Tests

  • test_tensor_algebra.jl gains a broadcasting is a synonym for scaling testset: the three scaling arms, scalar and function operands, tensor ⊙ tensor, nested and @.-flattened chains, and the ArgumentError arms.
  • test_pyclasses.jl restores the dropped np.multiply arm as weights .* vecs, so all three arms of the upstream test are now covered.

Full suite passes.

`AbstractTensor` defined no `Base.broadcastable`, so `weights .* ω` failed
with a `MethodError` that gave no hint that `weights * ω` is the supported
spelling — and our port of upstream's batch-scaling test had to drop the
`np.multiply(weights, vecs)` arm that `__array_ufunc__` routes back to
`Tensor.__mul__`.

A tensor now carries its own `BroadcastStyle`, so it is never an array to the
broadcast machinery: `copy` hands a dotted `*` or `/` straight back to the
tensor algebra, and every other elementwise operation — meaningless on
basis-dependent coefficients — throws an `ArgumentError` naming the supported
spellings.

Closes #19
@wardjm
wardjm merged commit 05725bc into main Jul 12, 2026
2 checks passed
wardjm added a commit that referenced this pull request Jul 12, 2026
Audited every API name and code block in the README against the package by
executing them, rather than reading. The prose was accurate but had fallen behind
three merged features and carried one broken link.

- The install URL pointed at `wardjm/DiffusionGeometryJ.jl`; the repository is
  `wardjm/DiffusionGeometryJ` (no `.jl`), so `Pkg.add` as documented would have
  failed.
- Batching went unmentioned entirely — no `batch_shape`, and no note that batch axes
  broadcast numpy-style from the right rather than by Julia's rule. That is a real
  trap when porting, and it was only recorded in the Conventions page.
- `.*` and `./` (#24) and the accessors were undocumented. A numeric array scaling a
  tensor means batch-wise scalars, which is what makes `exp.(-evals) .* evecs` a
  spectral filter; that now appears where a reader will look for it.
- The `show` methods added in c3bbd84 are shown, and the coefficients-not-values
  distinction that motivates `to_pointwise_basis` is stated in both the tensor
  section and Conventions.

The Documentation section now says the guide pages' examples run in the suite too
(true as of the previous commit) and links the Conventions and upstream-bugs pages.

Everything else — the constructor table, the operator and Betti sections, the
degree conventions, `hodge_decomposition`'s two return shapes — was checked against
a live geometry and is correct as written.
@wardjm
wardjm deleted the tensor-broadcast branch July 15, 2026 16:52
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.

Tensors are not broadcastable: weights .* ω is a MethodError

1 participant