Skip to content

Commit 5d2e206

Browse files
authored
Simplify batched Hurwitz operations (#54)
1 parent 12ff9c2 commit 5d2e206

2 files changed

Lines changed: 14 additions & 12 deletions

File tree

‎geotorch/hurwitz.py‎

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import torch
2-
import math
32
from .psd import PSD
43
from .skew import Skew
54
from .product import ProductManifold
@@ -89,16 +88,8 @@ def submersion_inv(self, A: torch.Tensor, check_in_manifold=True, rho=1, tol=1e-
8988
A_shifted = A + self.alpha * self.In
9089
A_shifted_T = A_shifted.mT.contiguous()
9190

92-
# Batched Kronecker product using vmap for cleaner implementation
93-
def _single_kron_sum(A_single):
94-
I_single = torch.eye(self.n, device=A.device, dtype=A.dtype)
95-
return torch.kron(I_single, A_single) + torch.kron(A_single, I_single)
96-
97-
flat_batch_size = math.prod(self.tensorial_size)
98-
A_flat = A_shifted_T.reshape(flat_batch_size, self.n, self.n)
99-
100-
M_flat = torch.vmap(_single_kron_sum)(A_flat)
101-
M = M_flat.reshape(*self.tensorial_size, self.n * self.n, self.n * self.n)
91+
identity = torch.eye(self.n, device=A.device, dtype=A.dtype)
92+
M = torch.kron(identity, A_shifted_T) + torch.kron(A_shifted_T, identity)
10293

10394
Q = rho * self.In
10495
flat_Q = Q.flatten(-2, -1)
@@ -157,7 +148,7 @@ def sample(self, init_=torch.nn.init.xavier_normal_):
157148
with torch.no_grad():
158149
P = self[0].sample(init_) + self.In
159150
Q = self[1].sample(init_) + self.In
160-
S = self[2](init_(torch.empty_like(P)))
151+
S = self[2].sample(init_)
161152

162153
A = self.submersion(Q, P, S)
163154
return A

‎test/test_hurwitz.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,17 @@ def test_batch_submersion_inv(self):
109109

110110
self.assertTrue(torch.allclose(A_batch, A_reconstructed, atol=1e-4))
111111

112+
def test_multi_batch_submersion_inv(self):
113+
size = (2, 3, 2, 2)
114+
hurwitz_batch = Hurwitz(size).double()
115+
A_batch = hurwitz_batch.sample()
116+
alpha = float(get_lyap_exp(A_batch)) / 2
117+
inverse = Hurwitz(size, alpha=alpha).double()
118+
Q, P_inv, S = inverse.submersion_inv(A_batch)
119+
A_reconstructed = inverse.submersion(Q, P_inv, S)
120+
121+
self.assertTrue(torch.allclose(A_batch, A_reconstructed, atol=1e-4))
122+
112123
def test_register_hurwitz(self):
113124
layer = torch.nn.Linear(self.size[-2], self.size[-1])
114125
geotorch.alpha_stable(layer, "weight", alpha=0.5)

0 commit comments

Comments
 (0)