Skip to content

Commit c3fea56

Browse files
committed
Reduce manifold sampling temporaries
1 parent a0c03a5 commit c3fea56

8 files changed

Lines changed: 33 additions & 8 deletions

File tree

‎geotorch/fixedrank.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ def sample(self, init_=torch.nn.init.xavier_normal_, eps=5e-6, factorized=False)
113113
"""
114114
U, S, V = super().sample(factorized=True, init_=init_)
115115
with torch.no_grad():
116-
# S >= 0, as given by torch.linalg.eigvalsh()
117-
S[S < eps] = eps
116+
# S >= 0, as given by torch.linalg.svd()
117+
S.clamp_min_(eps)
118118
if factorized:
119119
return U, S, V
120120
else:

‎geotorch/lowrank.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def in_manifold_singular_values(self, S, eps=1e-5):
107107
return True
108108
# We compute the \infty-norm of the remaining dimension
109109
D = S[..., self.rank :]
110-
infty_norm_err = D.abs().max(dim=-1).values
110+
infty_norm_err = D.abs().amax(dim=-1)
111111
return (infty_norm_err < eps).all()
112112

113113
def in_manifold(self, X, eps=1e-5):

‎geotorch/pssdfixedrank.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,5 +95,5 @@ def sample(self, init_=torch.nn.init.xavier_normal_, eps=5e-6):
9595
L, Q = super().sample(factorized=True, init_=init_)
9696
with torch.no_grad():
9797
# L >= 0, as given by torch.linalg.eigvalsh()
98-
L[L < eps] = eps
98+
L.clamp_min_(eps)
9999
return (Q * L.unsqueeze(-2)) @ Q.transpose(-2, -1)

‎geotorch/sphere.py‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,13 @@ def uniform_init_sphere_(x, r=1.0):
1616
"""
1717
with torch.no_grad():
1818
x.normal_()
19-
x.copy_(r * project(x))
19+
x.copy_(project(x)).mul_(r)
2020
return x
2121

2222

2323
def _in_sphere(x, r, eps):
2424
norm = torch.linalg.vector_norm(x, dim=-1)
25-
rs = torch.full_like(norm, r)
26-
return (torch.linalg.vector_norm(norm - rs, ord=float("inf")) < eps).all()
25+
return (norm - r).abs().amax() < eps
2726

2827

2928
class SphereEmbedded(nn.Module):

‎geotorch/symmetric.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ def in_manifold_eigen(self, L, eps=1e-6):
168168
if L.size(-1) > self.rank:
169169
# We compute the \infty-norm of the remaining dimension
170170
D = L[..., : -self.rank]
171-
infty_norm_err = D.abs().max(dim=-1).values
171+
infty_norm_err = D.abs().amax(dim=-1)
172172
if (infty_norm_err > 5.0 * eps).any():
173173
return False
174174
return (L[..., -self.rank :] >= -eps).all().item()

‎test/test_lowrank.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,12 @@ def test_inverse_softplus_is_stable(self):
3535
inverse = inv_softplus_epsilon(x)
3636
self.assertTrue(torch.isfinite(inverse).all())
3737
self.assertTrue(torch.allclose(softplus_epsilon(inverse), x))
38+
39+
def test_fixed_rank_sample_clamps_singular_values(self):
40+
manifold = FixedRank(size=(3, 3), rank=2).double()
41+
_, singular_values, _ = manifold.sample(
42+
init_=lambda X: X.zero_(), eps=0.25, factorized=True
43+
)
44+
self.assertTrue(
45+
torch.equal(singular_values, torch.full_like(singular_values, 0.25))
46+
)

‎test/test_positive_semidefinite.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from unittest import TestCase
22

3+
import torch
4+
35
from geotorch.pssdlowrank import PSSDLowRank
46
from geotorch.pssdfixedrank import PSSDFixedRank
57
from geotorch.pssd import PSSD
@@ -40,3 +42,10 @@ def test_positive_semidefinite_errors(self):
4042
PSD(size=(5, 2), f=3)
4143
with self.assertRaises(ValueError):
4244
PSD(size=(5, 3), f="fail")
45+
46+
def test_fixed_rank_sample_clamps_eigenvalues(self):
47+
manifold = PSSDFixedRank(size=(3, 3), rank=2).double()
48+
sample = manifold.sample(init_=lambda X: X.zero_(), eps=0.25)
49+
eigenvalues = torch.linalg.eigvalsh(sample)
50+
expected = torch.tensor([0.0, 0.25, 0.25], dtype=torch.float64)
51+
torch.testing.assert_close(eigenvalues, expected)

‎test/test_sphere.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,11 @@ def test_embedded_sample_uses_module_dtype(self):
5151
sample = sphere.sample()
5252
self.assertEqual(sample.dtype, torch.float64)
5353
self.assertEqual(sample.shape, (2, 3))
54+
55+
def test_membership_preserves_strict_epsilon_boundary(self):
56+
sphere = SphereEmbedded(size=(1,), radius=2.0)
57+
x = torch.tensor([2.0001], dtype=torch.float64)
58+
eps = (torch.linalg.vector_norm(x) - sphere.radius).item()
59+
60+
self.assertFalse(sphere.in_manifold(x, eps=eps))
61+
self.assertTrue(sphere.in_manifold(x, eps=2.0 * eps))

0 commit comments

Comments
 (0)