Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Scale/Translation/Affine.__eq__ raise ValueError for a different number of axes (breaks aggregate on Xenium transcripts vs 2D cells)

Open
#1,205 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 2 days

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
75/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
numpy, python
Domain
data

Research direction

Start in src/spatialdata/transformations/transformations.py at Translation.eq, Scale.eq, and Affine.eq, then run the supplied repro.py to confirm the shape-mismatch failures. Compare the existing equality behavior with the stated expected results, add coverage for 2D-versus-3D transformations, and verify that the Xenium aggregation comparison no longer raises ValueError.

Written by the indexing model from the issue text.

Description

bug 🚨 method: aggregation 🔢 method: transforms 🔃 needs: triage priority: high

[!NOTE]
This whole message is AI-generated. The issue was automatically discovered and reported by an AI agent (Claude) during an autonomous bug hunt on the spatialdata code base. It has not been verified or triaged by a human yet; the needs: triage label is set so that a maintainer can confirm it. The reproduction script below was executed by the agent in an isolated environment (see Environment) and its output is pasted verbatim.

Summary

Scale([2, 2, 1], axes=("x", "y", "z")) == Scale([2, 2], axes=("x", "y")) raises ValueError: operands could not be broadcast together with shapes (3,) (2,) instead of returning False; same for Translation and Affine. aggregate() compares the two transformations with == (by_transform == values_transform), so on the Xenium dataset from spatialdata-sandbox (xenium_rep1_io), where transcripts carry Scale (x, y, z) and cell boundaries Scale (x, y), the count-matrix aggregation crashes. spatialdata.testing.assert_elements_are_identical hits the same error after a write/read round trip of a raster whose Scale was defined on a subset of the axes.

Severity (agent's assessment): high — the canonical Xenium workflow aggregate(transcripts, by=cell_boundaries, value_key="feature_name", agg_func="count") crashes on real data

Where: src/spatialdata/transformations/transformations.py — Translation.__eq__, Scale.__eq__, Affine.__eq__ evaluate np.allclose(...) before comparing the axes

Expected behaviour

== returns False for transformations with different axes.

Reproduction

Save as repro.py and run uv run repro.py (the PEP 723 header pins spatialdata to the commit the bug was found on; replace the URL fragment with @main to test the current main branch).

# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "spatialdata @ git+https://github.com/scverse/spatialdata.git@ccf1ea048d054b6624214bf618008a9f9ae223e0",
# ]
# ///
"""Scale/Translation/Affine.__eq__ raise ValueError instead of returning False for a different number of axes."""
import warnings
import numpy as np
from spatialdata.transformations import Affine, Identity, Scale, Translation

warnings.simplefilter("ignore")
pairs = [
    ("Scale (x,y,z) vs Scale (x,y)", Scale([2, 2, 1], axes=("x", "y", "z")), Scale([2, 2], axes=("x", "y"))),
    ("Translation (x,y,z) vs (x,y)", Translation([1, 2, 3], axes=("x", "y", "z")), Translation([1, 2], axes=("x", "y"))),
    ("Affine 3D vs 2D", Affine(np.eye(4), input_axes=("x", "y", "z"), output_axes=("x", "y", "z")), Affine(np.eye(3), input_axes=("x", "y"), output_axes=("x", "y"))),
    ("Scale vs Identity (control)", Scale([2, 2], axes=("x", "y")), Identity()),
]
bug = False
for name, a, b in pairs:
    try:
        print(f"{name:32s}: a == b -> {a == b}")
    except Exception as e:  # noqa: BLE001
        print(f"{name:32s}: {type(e).__name__}: {e}")
        bug = True
print("VERDICT:", "BUG REPRODUCED" if bug else "NOT REPRODUCED")
# Real-data consequence: Xenium transcripts carry Scale(x, y, z) while cell boundaries carry Scale(x, y), so
# aggregate(transcripts, by=cell_boundaries, value_key="feature_name", agg_func="count") crashes in the
# `by_transform == values_transform` comparison inside aggregate().
Observed output
Scale (x,y,z) vs Scale (x,y)    : ValueError: operands could not be broadcast together with shapes (3,) (2,)
Translation (x,y,z) vs (x,y)    : ValueError: operands could not be broadcast together with shapes (3,) (2,)
Affine 3D vs 2D                 : ValueError: operands could not be broadcast together with shapes (4,4) (3,3)
Scale vs Identity (control)     : a == b -> False
VERDICT: BUG REPRODUCED

Possible fix direction (unverified)

Compare axes (and matrix shapes) first and short-circuit, e.g. return isinstance(other, Scale) and self.axes == other.axes and np.allclose(self.scale, other.scale); same for Translation and Affine (input/output axes and matrix.shape). Add tests comparing 2D vs 3D transformations.

Environment

uv run repro.py with the PEP 723 metadata in the script (fresh, isolated environment; spatialdata built from main @ ccf1ea0 (2026-08-28); Python 3.13, latest releases of the dependencies at run time: pandas 3.0, anndata 0.13, zarr 3.3, dask 2026.8, numpy 2.5, geopandas 1.1, shapely 2.1). macOS (arm64). Also reproduced in a second environment with pandas 2.3.3 / anndata 0.12.11 / numpy 2.4.4 / zarr 3.2.1.

Possibly related issues

#1029


Automatically generated; discovered by an AI agent (Claude) and not yet reviewed by a human.

Dominant language
Python
Stars
397
Forks
98
Avg merge
5d 9h
Merged PRs (30d)
3

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from scverse/spatialdata

All issues in scverse/spatialdata

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.