Skip to content

Commit eed70f1

Browse files
authored
Merge pull request #10 from mrava87/master
TorchOperator cupy integration
2 parents 6bbe251 + db3b339 commit eed70f1

File tree

12 files changed

+153
-43
lines changed

12 files changed

+153
-43
lines changed

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,22 @@ This repository is organized as follows:
8383
You need **Python 3.5 or greater**.
8484

8585
#### From PyPi
86-
Coming soon...
86+
87+
If you want to use PyLops-gpu within your codes,
88+
install it in your Python-gpu environment by typing the following command in your terminal:
89+
90+
```
91+
pip install pylops-gpu
92+
```
93+
94+
Open a python terminal and type:
95+
96+
```
97+
import pylops_gpu
98+
```
99+
100+
If you do not see any error, you should be good to go, enjoy!
101+
87102

88103
#### From Github
89104

examples/plot_convolve.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
plt.close('all')
2323

2424
###############################################################################
25-
# We will start by creating a zero signal of lenght :math:`nt` and we will
25+
# We will start by creating a zero signal of length :math:`nt` and we will
2626
# place a unitary spike at its center. We also create our filter to be
2727
# applied by means of :py:class:`pylops_gpu.signalprocessing.Convolve1D`
2828
# operator.

pylops_gpu/TorchOperator.py

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
import torch
22

3+
from torch.utils.dlpack import from_dlpack, to_dlpack
4+
from pylops.utils import deps
5+
6+
if deps.cupy_enabled:
7+
import cupy as cp
8+
else:
9+
cp = None
10+
311

412
class _TorchOperator(torch.autograd.Function):
513
"""Wrapper class for PyLops operators into Torch functions
@@ -16,20 +24,47 @@ def forward(ctx, x, forw, adj, pylops, device):
1624
ctx.pylops = pylops
1725
ctx.device = device
1826

27+
# prepare input
1928
if ctx.pylops:
20-
x = x.cpu().detach().numpy()
29+
if ctx.device == 'cpu':
30+
# bring x to cpu and numpy
31+
x = x.cpu().detach().numpy()
32+
else:
33+
# pass x to cupy using DLPack
34+
x = cp.fromDlpack(to_dlpack(x))
35+
36+
# apply forward operator
2137
y = ctx.forw(x)
38+
39+
# prepare output
2240
if ctx.pylops:
23-
y = torch.from_numpy(y).to(ctx.device)
41+
if ctx.device == 'cpu':
42+
# move y to torch and device
43+
y = torch.from_numpy(y).to(ctx.device)
44+
else:
45+
# move y to torch and device
46+
y = from_dlpack(y.toDlpack())
2447
return y
2548

2649
@staticmethod
2750
def backward(ctx, y):
51+
# prepare input
2852
if ctx.pylops:
29-
y = y.cpu().detach().numpy()
53+
if ctx.device == 'cpu':
54+
y = y.cpu().detach().numpy()
55+
else:
56+
# pass x to cupy using DLPack
57+
y = cp.fromDlpack(to_dlpack(y))
58+
59+
# apply adjoint operator
3060
x = ctx.adj(y)
61+
62+
# prepare output
3163
if ctx.pylops:
32-
x = torch.from_numpy(x).to(ctx.device)
64+
if ctx.device == 'cpu':
65+
x = torch.from_numpy(x).to(ctx.device)
66+
else:
67+
x = from_dlpack(x.toDlpack())
3368
return x, None, None, None, None
3469

3570

@@ -75,6 +110,7 @@ def __init__(self, Op, batch=False, pylops=False, device='cpu'):
75110
else:
76111
self.matvec = lambda x: Op.matmat(x, kfirst=True)
77112
self.rmatvec = lambda x: Op.rmatmat(x, kfirst=True)
113+
self.Top = _TorchOperator.apply
78114

79115
def apply(self, x):
80116
"""Apply forward pass to input vector
@@ -90,5 +126,5 @@ def apply(self, x):
90126
Output array resulting from the application of the operator to ``x``.
91127
92128
"""
93-
return _TorchOperator.apply(x, self.matvec, self.rmatvec,
94-
self.pylops, self.device)
129+
return self.Top(x, self.matvec, self.rmatvec,
130+
self.pylops, self.device)

pylops_gpu/avo/poststack.py

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
from pylops import MatrixMult, FirstDerivative
77
from pylops.utils.signalprocessing import convmtx, nonstationary_convmtx
88
from pylops.signalprocessing import Convolve1D
9-
#from pylops.avo.poststack import _PoststackLinearModelling
9+
from pylops.avo.poststack import _PoststackLinearModelling
1010

1111
from pylops_gpu.utils import dottest as Dottest
12+
from pylops_gpu.utils.torch2numpy import torchtype_from_numpytype
1213
from pylops_gpu import MatrixMult as gMatrixMult
1314
from pylops_gpu import FirstDerivative as gFirstDerivative
1415
from pylops_gpu import SecondDerivative as gSecondDerivative
@@ -19,24 +20,16 @@
1920

2021
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.WARNING)
2122

22-
23+
"""
2324
def _PoststackLinearModelling(wav, nt0, spatdims=None, explicit=False,
2425
sparse=False, _MatrixMult=MatrixMult,
2526
_Convolve1D=Convolve1D,
2627
_FirstDerivative=FirstDerivative,
2728
args_MatrixMult={}, args_Convolve1D={},
2829
args_FirstDerivative={}):
30+
# define dtype to be used (ensure wav.dtype rules that of operator)
31+
dtype = torchtype_from_numpytype(wav.dtype)
2932
30-
31-
"""Post-stack linearized seismic modelling operator.
32-
33-
Used to be able to provide operators from different libraries to
34-
PoststackLinearModelling. It operates in the same way as public method
35-
(PoststackLinearModelling) but has additional input parameters allowing
36-
passing a different operator and additional arguments to be passed to such
37-
operator.
38-
39-
"""
4033
if len(wav.shape) == 2 and wav.shape[0] != nt0:
4134
raise ValueError('Provide 1d wavelet or 2d wavelet composed of nt0 '
4235
'wavelets')
@@ -67,23 +60,27 @@ def _PoststackLinearModelling(wav, nt0, spatdims=None, explicit=False,
6760
M = np.dot(C, D)
6861
if sparse:
6962
M = csc_matrix(M)
70-
Pop = _MatrixMult(M, dims=spatdims, **args_MatrixMult)
63+
Pop = _MatrixMult(M, dims=spatdims, dtype=dtype, **args_MatrixMult)
7164
else:
7265
# Create wavelet operator
7366
if len(wav.shape) == 1:
7467
Cop = _Convolve1D(np.prod(np.array(dims)), h=wav,
7568
offset=len(wav) // 2, dir=0, dims=dims,
76-
**args_Convolve1D)
69+
dtype=dtype, **args_Convolve1D)
7770
else:
7871
Cop = _MatrixMult(nonstationary_convmtx(wav, nt0,
7972
hc=wav.shape[1] // 2,
8073
pad=(nt0, nt0)),
81-
dims=spatdims, **args_MatrixMult)
74+
dims=spatdims, dtype=dtype,
75+
**args_MatrixMult)
8276
# Create derivative operator
8377
Dop = _FirstDerivative(np.prod(np.array(dims)), dims=dims,
84-
dir=0, sampling=1., **args_FirstDerivative)
78+
dir=0, sampling=1., dtype=dtype,
79+
**args_FirstDerivative)
80+
8581
Pop = Cop * Dop
8682
return Pop
83+
"""
8784

8885

8986
def PoststackLinearModelling(wav, nt0, spatdims=None, explicit=False,
@@ -129,8 +126,9 @@ def PoststackLinearModelling(wav, nt0, spatdims=None, explicit=False,
129126
implementation details.
130127
131128
"""
132-
if not isinstance(wav, torch.Tensor) and not explicit:
133-
wav = torch.from_numpy(wav).to(device)
129+
# ensure wav is always numpy, it will be converted later back to torch
130+
if isinstance(wav, torch.Tensor):
131+
wav = wav.cpu().numpy()
134132
return _PoststackLinearModelling(wav, nt0, spatdims=spatdims,
135133
explicit=explicit, sparse=False,
136134
_MatrixMult=gMatrixMult,

pylops_gpu/basicoperators/Diagonal.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from pylops_gpu import LinearOperator
66
from pylops_gpu.utils.complex import conj, flatten, reshape, \
77
complextorch_fromnumpy
8+
from pylops_gpu.utils.torch2numpy import torchtype_from_numpytype
89

910

1011
class Diagonal(LinearOperator):
@@ -78,7 +79,7 @@ def __init__(self, diag, dims=None, dir=0, device='cpu',
7879
self.device = device
7980
self.togpu = togpu
8081
self.tocpu = tocpu
81-
self.dtype = dtype
82+
self.dtype = torchtype_from_numpytype(dtype)
8283
self.explicit = False
8384
self.Op = None
8485

pylops_gpu/basicoperators/FirstDerivative.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from pylops_gpu import LinearOperator
44
from pylops_gpu.signalprocessing import Convolve1D
5+
from pylops_gpu.utils.torch2numpy import torchtype_from_numpytype
56

67

78
"""
@@ -39,7 +40,7 @@ class FirstDerivative(LinearOperator):
3940
tocpu : :obj:`tuple`, optional
4041
Move data and model from gpu to cpu after applying ``matvec`` and
4142
``rmatvec``, respectively (only when ``device='gpu'``)
42-
dtype : :obj:`torch.dtype`, optional
43+
dtype : :obj:`torch.dtype` or :obj:`np.dtype`, optional
4344
Type of elements in input array.
4445
4546
Attributes
@@ -63,6 +64,9 @@ class FirstDerivative(LinearOperator):
6364
def __init__(self, N, dims=None, dir=0, sampling=1., device='cpu',
6465
togpu=(False, False), tocpu=(False, False),
6566
dtype=torch.float32):
67+
# convert dtype to torch.dtype
68+
dtype = torchtype_from_numpytype(dtype)
69+
6670
h = torch.torch.tensor([0.5, 0, -0.5],
6771
dtype=dtype).to(device) / sampling
6872
self.device = device
@@ -73,4 +77,4 @@ def __init__(self, N, dims=None, dir=0, sampling=1., device='cpu',
7377
self.explicit = False
7478
self.Op = Convolve1D(N, h, offset=1, dims=dims, dir=dir,
7579
zero_edges=True, device=device,
76-
togpu=togpu, tocpu=tocpu, dtype=dtype)
80+
togpu=togpu, tocpu=tocpu, dtype=self.dtype)

pylops_gpu/basicoperators/MatrixMult.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
from pytorch_complex_tensor import ComplexTensor
55
from pylops_gpu.LinearOperator import LinearOperator
66
from pylops_gpu.utils.complex import conj, reshape, flatten
7-
from pylops_gpu.utils.torch2numpy import numpytype_from_torchtype
7+
from pylops_gpu.utils.torch2numpy import numpytype_from_torchtype, \
8+
torchtype_from_numpytype
89

910

1011
class MatrixMult(LinearOperator):
@@ -29,7 +30,7 @@ class MatrixMult(LinearOperator):
2930
tocpu : :obj:`tuple`, optional
3031
Move data and model from gpu to cpu after applying ``matvec`` and
3132
``rmatvec``, respectively (only when ``device='gpu'``)
32-
dtype : :obj:`torch.dtype`, optional
33+
dtype : :obj:`torch.dtype` or :obj:`np.dtype`, optional
3334
Type of elements in input array.
3435
3536
Attributes
@@ -49,10 +50,12 @@ class MatrixMult(LinearOperator):
4950
def __init__(self, A, dims=None, device='cpu',
5051
togpu=(False, False), tocpu=(False, False),
5152
dtype=torch.float32):
53+
# convert A to torch tensor if provided as numpy array numpy
5254
if not isinstance(A, (torch.Tensor, ComplexTensor)):
53-
self.complex = True if np.iscomplexobj(A) else False
55+
dtype = numpytype_from_torchtype(dtype)
5456
self.A = \
5557
torch.from_numpy(A.astype(numpytype_from_torchtype(dtype))).to(device)
58+
self.complex = True if np.iscomplexobj(A) else False
5659
else:
5760
self.complex = True if isinstance(A, ComplexTensor) else False
5861
self.A = A
@@ -76,7 +79,7 @@ def __init__(self, A, dims=None, device='cpu',
7679
self.device = device
7780
self.togpu = togpu
7881
self.tocpu = tocpu
79-
self.dtype = dtype
82+
self.dtype = torchtype_from_numpytype(dtype)
8083
self.explicit = True
8184
self.Op = None
8285

pylops_gpu/basicoperators/Restriction.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import numpy as np
33

44
from pylops_gpu.LinearOperator import LinearOperator
5+
from pylops_gpu.utils.torch2numpy import torchtype_from_numpytype
56

67

78
class Restriction(LinearOperator):
@@ -78,7 +79,7 @@ def __init__(self, M, iava, dims=None, dir=0, inplace=True,
7879
self.device = device
7980
self.togpu = togpu
8081
self.tocpu = tocpu
81-
self.dtype = dtype
82+
self.dtype = torchtype_from_numpytype(dtype)
8283
self.explicit = True
8384
self.Op = None
8485

pylops_gpu/basicoperators/SecondDerivative.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from pylops_gpu import LinearOperator
44
from pylops_gpu.signalprocessing import Convolve1D
5+
from pylops_gpu.utils.torch2numpy import torchtype_from_numpytype
56

67

78
class SecondDerivative(LinearOperator):
@@ -28,7 +29,7 @@ class SecondDerivative(LinearOperator):
2829
tocpu : :obj:`tuple`, optional
2930
Move data and model from gpu to cpu after applying ``matvec`` and
3031
``rmatvec``, respectively (only when ``device='gpu'``)
31-
dtype : :obj:`torch.dtype`, optional
32+
dtype : :obj:`torch.dtype` or :obj:`np.dtype`, optional
3233
Type of elements in input array.
3334
3435
Attributes
@@ -52,6 +53,9 @@ class SecondDerivative(LinearOperator):
5253
def __init__(self, N, dims=None, dir=0, sampling=1., device='cpu',
5354
togpu=(False, False), tocpu=(False, False),
5455
dtype=torch.float32):
56+
# convert dtype to torch.dtype
57+
dtype = torchtype_from_numpytype(dtype)
58+
5559
h = torch.torch.tensor([1., -2, 1.],
5660
dtype=dtype).to(device) / sampling**2
5761
self.device = device

pylops_gpu/signalprocessing/Convolve1D.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from torch.nn.functional import pad
55
from pylops_gpu import LinearOperator
6+
from pylops_gpu.utils.torch2numpy import torchtype_from_numpytype
67

78

89
class Convolve1D(LinearOperator):
@@ -16,7 +17,7 @@ class Convolve1D(LinearOperator):
1617
----------
1718
N : :obj:`int`
1819
Number of samples in model.
19-
h : :obj:`torch.Tensor`
20+
h : :obj:`torch.Tensor` or :obj:`numpy.ndarray`
2021
1d compact filter to be convolved to input signal
2122
offset : :obj:`int`
2223
Index of the center of the compact filter
@@ -55,6 +56,13 @@ class Convolve1D(LinearOperator):
5556
def __init__(self, N, h, offset=0, dims=None, dir=0, zero_edges=False,
5657
device='cpu', togpu=(False, False), tocpu=(False, False),
5758
dtype=torch.float32):
59+
# convert dtype to torch.dtype
60+
if not isinstance(dtype, torch.dtype):
61+
dtype = torchtype_from_numpytype(dtype)
62+
63+
# convert h to torch if numpy
64+
if not isinstance(h, torch.Tensor):
65+
h = torch.from_numpy(h).to(device)
5866
self.nh = h.size()[0]
5967
self.h = h.reshape(1, 1, self.nh)
6068
self.offset = 2*(self.nh // 2 - int(offset))

0 commit comments

Comments
 (0)