Skip to content

Commit e6abf47

Browse files
committed
feat: add keops full matrix multiplication
1 parent 6dae0d0 commit e6abf47

2 files changed

Lines changed: 89 additions & 104 deletions

File tree

src/pyGroupedTransforms/GroupedTransform.py

Lines changed: 89 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import numpy as np
2+
import torch
23

34
from pyGroupedTransforms import *
5+
from .NFFTtools import index_set_without_zeros
6+
7+
import pykeops
8+
from pykeops.torch import LazyTensor
9+
410

511
# All code that is linked to NFMTtools or to system = "mixed" is not tested yet....
612

@@ -266,24 +272,87 @@ def __init__(
266272
bandwidths=s.bandwidths, X=np.copy(X[:, u], order="C")
267273
)
268274
elif algorithm == "keops":
269-
self.matrix = np.empty((0,0), dtype=object)
275+
self.matrix = np.empty((0,0), dtype=object)
276+
277+
self.transforms = [
278+
DeferredLinearOperator()
279+
for _ in range(len(self.settings))
280+
]
281+
282+
D = X.shape[1]
270283

271-
self.transforms = [
272-
DeferredLinearOperator()
273-
for _ in range(len(self.settings))
274-
]
284+
freq_list = []
275285

276-
for idx, s in enumerate(self.settings):
286+
for s in self.settings:
277287

278-
if len(s.bandwidths) == 0:
279-
u = (0,)
280-
else:
281-
u = s.u
288+
if len(s.bandwidths) == 0:
289+
full = np.zeros((1, D), dtype=np.int32)
290+
291+
292+
else:
293+
local = index_set_without_zeros(
294+
np.array(s.bandwidths, dtype=np.int32)
295+
).T
296+
297+
full = np.zeros((local.shape[0], D), dtype=np.int32)
298+
299+
for i, dim in enumerate(s.u):
300+
full[:, dim] = local[:, i]
301+
302+
freq_list.append(full)
303+
304+
freq = np.vstack(freq_list)
305+
306+
X_torch = torch.tensor(X, dtype=torch.float64, device="cuda")
307+
I_torch = torch.tensor(
308+
freq,
309+
dtype=torch.float64,
310+
device="cuda"
311+
)
312+
313+
def trafo(fhat):
314+
X_i = LazyTensor(X_torch[:, None, :].contiguous())
315+
K_j = LazyTensor(I_torch[None, :, :].contiguous())
316+
phase_fwd = (X_i * K_j).sum(-1)
317+
two_pi_phase = -2 * torch.pi * phase_fwd
318+
kernel_fwd = two_pi_phase.cos() + 1j * two_pi_phase.sin()
319+
fhat_torch = torch.tensor(fhat, dtype=torch.complex128, device="cuda")
320+
fhat_j = LazyTensor(fhat_torch[None, :, None].contiguous())
321+
322+
try:
323+
result = (kernel_fwd * fhat_j).sum(dim=1)
324+
torch.cuda.synchronize()
325+
result = result.squeeze().cpu().numpy()
326+
return result
327+
except Exception as e:
328+
print("Error in KeOps trafo:", e)
329+
return None
330+
331+
def adjoint(f):
332+
f_torch = torch.tensor(f, dtype=torch.complex128, device="cuda").contiguous()
333+
334+
X_i = LazyTensor(X_torch[None, :, :].contiguous())
335+
K_j = LazyTensor(I_torch[:, None, :].contiguous())
282336

283-
self.transforms[idx] = s.mode.get_transform_keops(
284-
bandwidths=s.bandwidths,
285-
X=np.copy(X[:,u], order="C")
286-
)
337+
phase = (K_j * X_i).sum(-1)
338+
kernel = (-2*torch.pi*phase).cos() - 1j*(-2*torch.pi*phase).sin()
339+
340+
f_i = LazyTensor(f_torch[None, :, None].contiguous())
341+
342+
try:
343+
result = (kernel * f_i).sum(dim=1)
344+
torch.cuda.synchronize()
345+
result = result.squeeze().cpu().numpy()
346+
347+
return result
348+
except Exception as e:
349+
print("Error in KeOps adjoint:", e)
350+
return None
351+
352+
self.transforms = [DeferredLinearOperator(
353+
dtype=np.complex128, shape=(X.shape[0], len(freq)), mfunc=trafo, rmfunc=adjoint
354+
)]
355+
287356
else:
288357
self.transforms = []
289358
s1 = self.settings[0]
@@ -363,14 +432,10 @@ def adjoint_worker(i):
363432
adjoint_worker(i)
364433

365434
return fhat
435+
366436
elif self.algorithm == "keops":
367-
fhat = GroupedCoefficients(self.settings)
368-
369-
for i in range(len(self.transforms)):
370-
adjoint_result = self.transforms[i].H @ other
371-
fhat[self.settings[i].u] = adjoint_result
372-
373-
return fhat
437+
return GroupedCoefficients(self.settings, self.transforms[0].H @ other)
438+
374439
elif self.algorithm == "direct":
375440
return GroupedCoefficients(
376441
self.settings, (self.matrix.conj()).T @ other
@@ -403,15 +468,10 @@ def worker(i):
403468
worker(i)
404469

405470
return sum(results)
471+
406472
elif self.algorithm == "keops":
407-
results = []
408-
409-
for i in range(len(self.transforms)):
410-
u = self.settings[i].u
411-
result = self.transforms[i] @ other[u]
412-
results.append(result)
413-
414-
return sum(results)
473+
return self.transforms[0] @ other.data
474+
415475
elif self.algorithm == "direct":
416476
return self.matrix @ other.data
417477
else:

src/pyGroupedTransforms/NFFTtools.py

Lines changed: 0 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -172,81 +172,6 @@ def adjoint(f): # function adjoint(f::Vector{ComplexF64})::Vector{ComplexF64}
172172
return DeferredLinearOperator(
173173
dtype=np.complex128, shape=(M, N), mfunc=trafo, rmfunc=adjoint
174174
)
175-
176-
def get_transform_keops(
177-
bandwidths: np.array, X: np.array
178-
):
179-
if bandwidths.ndim > 1 or bandwidths.dtype != "int32":
180-
return "Please use an zero or one-dimensional numpy.array with dtype 'int32' as input"
181-
182-
M, d = np.shape(X)
183-
184-
if len(bandwidths) == 0:
185-
return DeferredLinearOperator(
186-
dtype=np.complex128,
187-
shape=(M, 1),
188-
mfunc=lambda fhat: np.full(M, fhat[0]),
189-
rmfunc=lambda f: np.array([np.sum(f)]),
190-
)
191-
192-
X_torch = torch.tensor(X, dtype=torch.float64, device="cuda")
193-
freq = index_set_without_zeros(
194-
np.array(bandwidths, dtype=np.int32)
195-
).T
196-
197-
I_torch = torch.tensor(
198-
freq,
199-
dtype=torch.float64,
200-
device="cuda"
201-
)
202-
203-
204-
def trafo(fhat):
205-
X_i = LazyTensor(X_torch[:, None, :].contiguous())
206-
K_j = LazyTensor(I_torch[None, :, :].contiguous())
207-
phase_fwd = (X_i * K_j).sum(-1)
208-
two_pi_phase = -2 * torch.pi * phase_fwd
209-
kernel_fwd = two_pi_phase.cos() + 1j * two_pi_phase.sin()
210-
fhat_torch = torch.tensor(fhat, dtype=torch.complex128, device="cuda")
211-
fhat_j = LazyTensor(fhat_torch[None, :, None].contiguous())
212-
213-
try:
214-
result = (kernel_fwd * fhat_j).sum(dim=1)
215-
torch.cuda.synchronize()
216-
result = result.squeeze().cpu().numpy()
217-
return result
218-
except Exception as e:
219-
print("Error in KeOps trafo:", e)
220-
return None
221-
222-
def adjoint(f):
223-
f_torch = torch.tensor(f, dtype=torch.complex128, device="cuda").contiguous()
224-
225-
X_i = LazyTensor(X_torch[None, :, :].contiguous())
226-
K_j = LazyTensor(I_torch[:, None, :].contiguous())
227-
228-
phase = (K_j * X_i).sum(-1)
229-
kernel = (-2*torch.pi*phase).cos() - 1j*(-2*torch.pi*phase).sin()
230-
231-
f_i = LazyTensor(f_torch[None, :, None].contiguous())
232-
233-
try:
234-
result = (kernel * f_i).sum(dim=1)
235-
torch.cuda.synchronize()
236-
result = result.squeeze().cpu().numpy()
237-
238-
return result
239-
except Exception as e:
240-
print("Error in KeOps adjoint:", e)
241-
return None
242-
243-
244-
Ncoeffs = np.prod(bandwidths - 1)
245-
246-
return DeferredLinearOperator(
247-
dtype=np.complex128, shape=(M, Ncoeffs), mfunc=trafo, rmfunc=adjoint
248-
)
249-
250175

251176
def get_matrix(
252177
bandwidths, X

0 commit comments

Comments
 (0)