jax-ml/jax · error · ValueError

CSC must have ndim=2; got {shape=}

Error message

CSC must have ndim=2; got {shape=}

What it means

The legacy CSC (compressed sparse column) format, like CSR, only supports 2D matrices; CSC._empty backs sparse.empty(format='csc') and sparse.eye(format='csc') and validates len(shape) == 2. Use BCOO or batched BCSR (with transposed semantics) for anything non-2D.

Source

Thrown at jax/experimental/sparse/csr.py:180

  def dtype(self) -> np.dtype:
    return self.data.dtype

  def __init__(self, args, *, shape):
    self.data, self.indices, self.indptr = map(jnp.asarray, args)
    super().__init__(args, shape=shape)

  @classmethod
  def fromdense(cls, mat, *, nse=None, index_dtype=np.int32):
    if nse is None:
      nse = (mat != 0).sum()
    return csr_fromdense(mat.T, nse=nse, index_dtype=index_dtype).T

  @classmethod
  def _empty(cls, shape, *, dtype=None, index_dtype='int32'):
    """Create an empty CSC instance. Public method is sparse.empty()."""
    shape = tuple(shape)
    if len(shape) != 2:
      raise ValueError(f"CSC must have ndim=2; got {shape=}")
    data = jnp.empty(0, dtype)
    indices = jnp.empty(0, index_dtype)
    indptr = jnp.zeros(shape[1] + 1, index_dtype)
    return cls((data, indices, indptr), shape=shape)

  @classmethod
  def _eye(cls, N, M, k, *, dtype=None, index_dtype='int32'):
    return CSR._eye(M, N, -k, dtype=dtype, index_dtype=index_dtype).T

  def todense(self):
    return csr_todense(self.T).T

  def transpose(self, axes=None):
    assert axes is None
    return CSR((self.data, self.indices, self.indptr), shape=self.shape[::-1])

  def __matmul__(self, other):
    if isinstance(other, JAXSparse):

View on GitHub (pinned to 1e1c6a8fc0)

Solutions

  1. Use format='bcoo' for arbitrary-dimensional sparse arrays
  2. For batched matrices, use BCSR (with transposed layout) or BCOO with n_batch
  3. Reshape to 2D if the legacy CSC API is required

Example fix

# before
m = sparse.empty((2, 3, 4), format='csc')  # ValueError

# after
m = sparse.empty((2, 3, 4), format='bcoo')
Defensive patterns

Strategy: validation

Validate before calling

assert len(tuple(shape)) == 2, 'CSC is 2D only; use bcoo'

Type guard

def csc_shape_ok(shape) -> bool:
    return len(tuple(shape)) == 2

Try / catch

try:
    m = sparse.empty(shape, format='csc')
except ValueError:
    m = sparse.empty(shape, format='bcoo')

Prevention

When it happens

Trigger: sparse.empty(shape, format='csc') or sparse.eye(..., format='csc') where len(shape) != 2, e.g. a 3D or 1D shape.

Common situations: Switching a 2D pipeline to batched tensors while keeping format='csc'; format chosen from a config string hitting CSC for non-matrix shapes.

Related errors


AI-assisted analysis of jax-ml/jax@1e1c6a8fc0 (2026-08-27). Data as JSON: /api/errors/1937dcbe0f8068ee. Report an issue: GitHub.