Skip to content

Commit a0d68e3

Browse files
[pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
1 parent 9e4523f commit a0d68e3

20 files changed

Lines changed: 85 additions & 48 deletions

docs/source/examples/sparse.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Quick example
22
==============================
33

44
TorchJD now offers helpers that make working with sparse adjacency matrices
5-
transparent.
5+
transparent.
66
The key entry-point is :pyfunc:`torchjd.sparse.sparse_mm`,
77
a vmap-aware autograd function that replaces the usual
88
``torch.sparse.mm`` inside Jacobian Descent pipelines.

src/torchjd/_autojac/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from torchjd.sparse import sparse_mm
2+
13
from ._backward import backward
24
from ._mtl_backward import mtl_backward
3-
from torchjd.sparse import sparse_mm

src/torchjd/aggregation/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
from ._sum import Sum
2020
from ._trimmed_mean import TrimmedMean
2121
from ._upgrad import UPGrad
22-
2322
from ._utils.check_dependencies import (
2423
OptionalDepsNotInstalledError as _OptionalDepsNotInstalledError,
2524
)

src/torchjd/sparse/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@
1414

1515
__all__ = ["sparse_mm"]
1616

17-
# feature flag
17+
# feature flag
1818
if os.getenv("TORCHJD_DISABLE_SPARSE", "0") != "1":
1919
enable_seamless_sparse()

src/torchjd/sparse/_autograd.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ def forward(A_like: torch.Tensor, X: torch.Tensor) -> torch.Tensor: # noqa: D40
2121
if X.dim() == 3: # (B, N, d)
2222
B, N, d = X.shape
2323
X2d = X.reshape(B * N, d).view(N, B * d)
24-
Y2d = _orig_sparse_mm(A, X2d) # pragma: no cover
25-
return Y2d.view(N, B, d).permute(1, 0, 2) # pragma: no cover
24+
Y2d = _orig_sparse_mm(A, X2d) # pragma: no cover
25+
return Y2d.view(N, B, d).permute(1, 0, 2) # pragma: no cover
2626

2727
return _orig_sparse_mm(A, X)
2828

@@ -47,16 +47,16 @@ def backward(ctx, dY: torch.Tensor) -> Tuple[None, torch.Tensor]:
4747

4848
@staticmethod
4949
def vmap(info, in_dims, A_unbatched, X_batched): # noqa: D401
50-
A = A_unbatched # shared
51-
X = X_batched # (B, N, d)
50+
A = A_unbatched # shared
51+
X = X_batched # (B, N, d)
5252

5353
B, N, d = X.shape
5454
X2d = X.reshape(B * N, d).view(N, B * d)
5555
Y2d = _orig_sparse_mm(A, X2d)
5656
Y = Y2d.view(N, B, d).permute(1, 0, 2)
57-
return Y, 0 # output & out-dims
57+
return Y, 0 # output & out-dims
5858

5959

6060
def sparse_mm(A_like: torch.Tensor, X: torch.Tensor) -> torch.Tensor:
6161
"""Return ``A @ X`` through the vmap-safe sparse Function."""
62-
return _SparseMatMul.apply(A_like, X)
62+
return _SparseMatMul.apply(A_like, X)

src/torchjd/sparse/_patch.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
from ._autograd import sparse_mm
1818

19-
2019
# The wheel might exist yet be ABI-incompatible with the current
2120
# PyTorch, which raises *OSError* at import-time.
2221

@@ -29,6 +28,7 @@
2928
# Helpers
3029
def _wrap_mm(orig_fn: Callable, wrapper: Callable) -> Callable:
3130
"""Return a patched ``torch.sparse.mm`` that defers to *wrapper*."""
31+
3232
def patched(A, X): # noqa: D401
3333
if isinstance(A, torch.Tensor) and A.is_sparse and X.dim() >= 2:
3434
return wrapper(A, X)
@@ -52,11 +52,9 @@ def enable_seamless_sparse() -> None:
5252
# torch.sparse.mm
5353
if getattr(torch.sparse, "_orig_mm", None) is None:
5454
torch.sparse._orig_mm = torch.sparse.mm # type: ignore[attr-defined]
55-
torch.sparse.mm = _wrap_mm( # type: ignore[attr-defined]
56-
torch.sparse._orig_mm, sparse_mm
57-
)
55+
torch.sparse.mm = _wrap_mm(torch.sparse._orig_mm, sparse_mm) # type: ignore[attr-defined]
5856

59-
# tensor @ dense
57+
# tensor @ dense
6058
if getattr(torch.Tensor, "_orig_matmul", None) is None:
6159
torch.Tensor._orig_matmul = torch.Tensor.__matmul__ # type: ignore[attr-defined] # noqa: E501
6260
torch.Tensor.__matmul__ = _wrap_tensor_matmul(
@@ -69,10 +67,11 @@ def enable_seamless_sparse() -> None:
6967
"torch_sparse not found: SpSpMM will use slow fallback.",
7068
RuntimeWarning,
7169
stacklevel=2,
72-
) # pragma: no cover
70+
) # pragma: no cover
7371
return
7472

7573
if not hasattr(torch_sparse.SparseTensor, "_orig_matmul"):
74+
7675
def _sparse_tensor_matmul(self, dense): # noqa: D401
7776
return sparse_mm(self, dense)
7877

src/torchjd/sparse/_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,4 @@ def to_coalesced_coo(x: Any) -> torch.Tensor:
3434
except ModuleNotFoundError: # pragma: no cover
3535
pass
3636

37-
raise TypeError(f"Unsupported sparse type: {type(x)}") # pragma: no cover
37+
raise TypeError(f"Unsupported sparse type: {type(x)}") # pragma: no cover

tests/unit/sparse/test_mm.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
import torch
21
import pytest
2+
import torch
3+
34
from torchjd.sparse import sparse_mm
45
from torchjd.sparse._utils import to_coalesced_coo
56

67
try:
7-
import importlib, types
8+
import importlib
9+
import types
10+
811
torch_sparse = importlib.import_module("torch_sparse") # noqa: E402
912
HAVE_TORCH_SPARSE = isinstance(torch_sparse, types.ModuleType)
1013
except (ModuleNotFoundError, OSError):
@@ -13,6 +16,7 @@
1316

1417
try:
1518
import scipy.sparse as sp
19+
1620
HAVE_SCIPY = True
1721
except ModuleNotFoundError:
1822
HAVE_SCIPY = False
@@ -32,7 +36,7 @@ def _batched_features(device):
3236
def test_vmap_branch(device):
3337
A = _dense_graph().to(device)
3438
X = _batched_features(device)
35-
Y = sparse_mm(A, X) # calls vmap-aware branch
39+
Y = sparse_mm(A, X) # calls vmap-aware branch
3640
assert Y.shape == X.shape
3741

3842

tests/unit/sparse/test_mm_3d.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
import torch
2+
23
from torchjd.sparse import sparse_mm
34

5+
46
def test_forward_backward_3d():
57
# sparse 2×2 matrix
68
A = torch.sparse_coo_tensor([[0, 1], [1, 0]], [1.0, 1.0]).coalesce()
79

810
# 3-D dense tensor (B=3, N=2, d=4)
911
X = torch.randn(3, 2, 4, requires_grad=True)
1012

11-
Y = sparse_mm(A, X) # exercises 3-D forward branch
13+
Y = sparse_mm(A, X) # exercises 3-D forward branch
1214
loss = Y.sum()
13-
loss.backward() # exercises 3-D backward branch
15+
loss.backward() # exercises 3-D backward branch
1416

1517
# Gradient should be ones because A.T @ 1 = [1,1] → broadcast
1618
assert torch.allclose(X.grad, torch.ones_like(X), atol=1e-6)

tests/unit/sparse/test_mm_sequential.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import torch
2+
23
from torchjd._autojac import backward
34
from torchjd.aggregation import UPGrad
45
from torchjd.sparse import sparse_mm
56

7+
68
def test_sequential_backward():
79
A = torch.sparse_coo_tensor([[0, 1], [1, 0]], [1.0, 1.0]).coalesce()
810
p = torch.tensor([1.0, 2.0], requires_grad=True)

0 commit comments

Comments
 (0)