Skip to content

Commit 9411580

Browse files
committed
feat: added savetaper to Sliding1d and Sliding2d
1 parent 63425e3 commit 9411580

File tree

3 files changed

+227
-27
lines changed

3 files changed

+227
-27
lines changed

pylops/signalprocessing/sliding1d.py

Lines changed: 90 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ class Sliding1D(LinearOperator):
127127
Number of samples of overlapping part of window
128128
tapertype : :obj:`str`, optional
129129
Type of taper (``hanning``, ``cosine``, ``cosinesquare`` or ``None``)
130+
savetaper: :obj:`bool`, optional
131+
Save all tapers and apply them in one go (``True``) or save unique tapers and apply them one by one (``False``).
132+
The first option is more computationally efficient, whilst the second is more memory efficient.
130133
name : :obj:`str`, optional
131134
.. versionadded:: 2.0.0
132135
@@ -148,6 +151,7 @@ def __init__(
148151
nwin: int,
149152
nover: int,
150153
tapertype: str = "hanning",
154+
savetaper: bool = True,
151155
name: str = "S",
152156
) -> None:
153157

@@ -172,19 +176,23 @@ def __init__(
172176

173177
# create tapers
174178
self.tapertype = tapertype
179+
self.savetaper = savetaper
175180
if self.tapertype is not None:
176181
tap = taper(nwin, nover, tapertype=self.tapertype)
177182
tapin = tap.copy()
178183
tapin[:nover] = 1
179184
tapend = tap.copy()
180185
tapend[-nover:] = 1
181-
self.taps = [
182-
tapin,
183-
]
184-
for i in range(1, nwins - 1):
185-
self.taps.append(tap)
186-
self.taps.append(tapend)
187-
self.taps = np.vstack(self.taps)
186+
if self.savetaper:
187+
self.taps = [
188+
tapin,
189+
]
190+
for i in range(1, nwins - 1):
191+
self.taps.append(tap)
192+
self.taps.append(tapend)
193+
self.taps = np.vstack(self.taps)
194+
else:
195+
self.taps = np.vstack([tapin, tap, tapend])
188196

189197
# check if operator is applied to all windows simultaneously
190198
self.simOp = False
@@ -200,28 +208,30 @@ def __init__(
200208
name=name,
201209
)
202210

211+
self._register_multiplications(self.savetaper)
212+
203213
@reshaped
204-
def _matvec(self, x: NDArray) -> NDArray:
214+
def _matvec_savetaper(self, x: NDArray) -> NDArray:
205215
ncp = get_array_module(x)
206216
if self.tapertype is not None:
207217
self.taps = to_cupy_conditional(x, self.taps)
208218
y = ncp.zeros(self.dimsd, dtype=self.dtype)
209219
if self.simOp:
210220
x = self.Op @ x
221+
if self.tapertype is not None:
222+
x = self.taps * x
211223
for iwin0 in range(self.dims[0]):
212224
if self.simOp:
213-
xx = x[iwin0]
214-
else:
215-
xx = self.Op.matvec(x[iwin0])
216-
if self.tapertype is not None:
217-
xxwin = self.taps[iwin0] * xx
225+
xxwin = x[iwin0]
218226
else:
219-
xxwin = xx
227+
xxwin = self.Op.matvec(x[iwin0])
228+
if self.tapertype is not None:
229+
xxwin = self.taps[iwin0] * xxwin
220230
y[self.dwin_inends[0][iwin0] : self.dwin_inends[1][iwin0]] += xxwin
221231
return y
222232

223233
@reshaped
224-
def _rmatvec(self, x: NDArray) -> NDArray:
234+
def _rmatvec_savetaper(self, x: NDArray) -> NDArray:
225235
ncp = get_array_module(x)
226236
ncp_sliding_window_view = get_sliding_window_view(x)
227237
if self.tapertype is not None:
@@ -236,3 +246,68 @@ def _rmatvec(self, x: NDArray) -> NDArray:
236246
for iwin0 in range(self.dims[0]):
237247
y[iwin0] = self.Op.rmatvec(ywins[iwin0])
238248
return y
249+
250+
@reshaped
251+
def _matvec_nosavetaper(self, x: NDArray) -> NDArray:
252+
ncp = get_array_module(x)
253+
if self.tapertype is not None:
254+
self.taps = to_cupy_conditional(x, self.taps)
255+
y = ncp.zeros(self.dimsd, dtype=self.dtype)
256+
if self.simOp:
257+
x = self.Op @ x
258+
for iwin0 in range(self.dims[0]):
259+
if self.simOp:
260+
xxwin = x[iwin0]
261+
else:
262+
xxwin = self.Op.matvec(x[iwin0])
263+
if self.tapertype is not None:
264+
if iwin0 == 0:
265+
xxwin = self.taps[0] * xxwin
266+
elif iwin0 == self.dims[0] - 1:
267+
xxwin = self.taps[-1] * xxwin
268+
else:
269+
xxwin = self.taps[1] * xxwin
270+
y[self.dwin_inends[0][iwin0] : self.dwin_inends[1][iwin0]] += xxwin
271+
return y
272+
273+
@reshaped
274+
def _rmatvec_nosavetaper(self, x: NDArray) -> NDArray:
275+
ncp = get_array_module(x)
276+
ncp_sliding_window_view = get_sliding_window_view(x)
277+
if self.tapertype is not None:
278+
self.taps = to_cupy_conditional(x, self.taps)
279+
ywins = ncp_sliding_window_view(x, self.nwin)[:: self.nwin - self.nover].copy()
280+
if self.simOp:
281+
if self.tapertype is not None:
282+
for iwin0 in range(self.dims[0]):
283+
if iwin0 == 0:
284+
ywins[0] = ywins[0] * self.taps[0]
285+
elif iwin0 == self.dims[0] - 1:
286+
ywins[-1] = ywins[-1] * self.taps[-1]
287+
else:
288+
ywins[iwin0] = ywins[iwin0] * self.taps[1]
289+
y = self.Op.H @ ywins
290+
else:
291+
y = ncp.zeros(self.dims, dtype=self.dtype)
292+
for iwin0 in range(self.dims[0]):
293+
if iwin0 == 0:
294+
if self.tapertype is not None:
295+
ywins[0] = ywins[0] * self.taps[0]
296+
y[0] = self.Op.rmatvec(ywins[0])
297+
elif iwin0 == self.dims[0] - 1:
298+
if self.tapertype is not None:
299+
ywins[-1] = ywins[-1] * self.taps[-1]
300+
y[-1] = self.Op.rmatvec(ywins[-1])
301+
else:
302+
if self.tapertype is not None:
303+
ywins[iwin0] = ywins[iwin0] * self.taps[1]
304+
y[iwin0] = self.Op.rmatvec(ywins[iwin0])
305+
return y
306+
307+
def _register_multiplications(self, savetaper: bool) -> None:
308+
if savetaper:
309+
self._matvec = self._matvec_savetaper
310+
self._rmatvec = self._rmatvec_savetaper
311+
else:
312+
self._matvec = self._matvec_nosavetaper
313+
self._rmatvec = self._rmatvec_nosavetaper

pylops/signalprocessing/sliding2d.py

Lines changed: 98 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,9 @@ class Sliding2D(LinearOperator):
163163
Number of samples of overlapping part of window
164164
tapertype : :obj:`str`, optional
165165
Type of taper (``hanning``, ``cosine``, ``cosinesquare`` or ``None``)
166+
savetaper: :obj:`bool`, optional
167+
Save all tapers and apply them in one go (``True``) or save unique tapers and apply them one by one (``False``).
168+
The first option is more computationally efficient, whilst the second is more memory efficient.
166169
name : :obj:`str`, optional
167170
.. versionadded:: 2.0.0
168171
@@ -189,6 +192,7 @@ def __init__(
189192
nwin: int,
190193
nover: int,
191194
tapertype: str = "hanning",
195+
savetaper: bool = True,
192196
name: str = "S",
193197
) -> None:
194198

@@ -213,19 +217,25 @@ def __init__(
213217

214218
# create tapers
215219
self.tapertype = tapertype
220+
self.savetaper = savetaper
216221
if self.tapertype is not None:
217222
tap = taper2d(dimsd[1], nwin, nover, tapertype=self.tapertype)
218223
tapin = tap.copy()
219224
tapin[:nover] = 1
220225
tapend = tap.copy()
221226
tapend[-nover:] = 1
222-
self.taps = [
223-
tapin[np.newaxis, :],
224-
]
225-
for i in range(1, nwins - 1):
226-
self.taps.append(tap[np.newaxis, :])
227-
self.taps.append(tapend[np.newaxis, :])
228-
self.taps = np.concatenate(self.taps, axis=0)
227+
if self.savetaper:
228+
self.taps = [
229+
tapin[np.newaxis, :],
230+
]
231+
for i in range(1, nwins - 1):
232+
self.taps.append(tap[np.newaxis, :])
233+
self.taps.append(tapend[np.newaxis, :])
234+
self.taps = np.concatenate(self.taps, axis=0)
235+
else:
236+
self.taps = np.vstack(
237+
[tapin[np.newaxis, :], tap[np.newaxis, :], tapend[np.newaxis, :]]
238+
)
229239

230240
# check if operator is applied to all windows simultaneously
231241
self.simOp = False
@@ -241,8 +251,10 @@ def __init__(
241251
name=name,
242252
)
243253

254+
self._register_multiplications(self.savetaper)
255+
244256
@reshaped
245-
def _matvec(self, x: NDArray) -> NDArray:
257+
def _matvec_savetaper(self, x: NDArray) -> NDArray:
246258
ncp = get_array_module(x)
247259
if self.tapertype is not None:
248260
self.taps = to_cupy_conditional(x, self.taps)
@@ -262,7 +274,7 @@ def _matvec(self, x: NDArray) -> NDArray:
262274
return y
263275

264276
@reshaped
265-
def _rmatvec(self, x: NDArray) -> NDArray:
277+
def _rmatvec_savetaper(self, x: NDArray) -> NDArray:
266278
ncp = get_array_module(x)
267279
ncp_sliding_window_view = get_sliding_window_view(x)
268280
if self.tapertype is not None:
@@ -281,3 +293,80 @@ def _rmatvec(self, x: NDArray) -> NDArray:
281293
self.dims[1], self.dims[2]
282294
)
283295
return y
296+
297+
@reshaped
298+
def _matvec_nosavetaper(self, x: NDArray) -> NDArray:
299+
ncp = get_array_module(x)
300+
if self.tapertype is not None:
301+
self.taps = to_cupy_conditional(x, self.taps)
302+
y = ncp.zeros(self.dimsd, dtype=self.dtype)
303+
if self.simOp:
304+
x = self.Op @ x
305+
for iwin0 in range(self.dims[0]):
306+
if self.simOp:
307+
xxwin = x[iwin0].reshape(self.nwin, self.dimsd[-1])
308+
else:
309+
xxwin = self.Op.matvec(x[iwin0].ravel()).reshape(
310+
self.nwin, self.dimsd[-1]
311+
)
312+
if self.tapertype is not None:
313+
if iwin0 == 0:
314+
xxwin = self.taps[0] * xxwin
315+
elif iwin0 == self.dims[0] - 1:
316+
xxwin = self.taps[-1] * xxwin
317+
else:
318+
xxwin = self.taps[1] * xxwin
319+
y[self.dwin_inends[0][iwin0] : self.dwin_inends[1][iwin0]] += xxwin
320+
return y
321+
322+
@reshaped
323+
def _rmatvec_nosavetaper(self, x: NDArray) -> NDArray:
324+
ncp = get_array_module(x)
325+
ncp_sliding_window_view = get_sliding_window_view(x)
326+
if self.tapertype is not None:
327+
self.taps = to_cupy_conditional(x, self.taps)
328+
ywins = (
329+
ncp_sliding_window_view(x, self.nwin, axis=0)[:: self.nwin - self.nover]
330+
.transpose(0, 2, 1)
331+
.copy()
332+
)
333+
if self.simOp:
334+
if self.tapertype is not None:
335+
for iwin0 in range(self.dims[0]):
336+
if iwin0 == 0:
337+
ywins[0] = ywins[0] * self.taps[0]
338+
elif iwin0 == self.dims[0] - 1:
339+
ywins[-1] = ywins[-1] * self.taps[-1]
340+
else:
341+
ywins[iwin0] = ywins[iwin0] * self.taps[1]
342+
y = self.Op.H @ ywins
343+
else:
344+
y = ncp.zeros(self.dims, dtype=self.dtype)
345+
for iwin0 in range(self.dims[0]):
346+
if iwin0 == 0:
347+
if self.tapertype is not None:
348+
ywins[0] = ywins[0] * self.taps[0]
349+
y[0] = self.Op.rmatvec(ywins[0].ravel()).reshape(
350+
self.dims[1], self.dims[2]
351+
)
352+
elif iwin0 == self.dims[0] - 1:
353+
if self.tapertype is not None:
354+
ywins[-1] = ywins[-1] * self.taps[-1]
355+
y[-1] = self.Op.rmatvec(ywins[-1].ravel()).reshape(
356+
self.dims[1], self.dims[2]
357+
)
358+
else:
359+
if self.tapertype is not None:
360+
ywins[iwin0] = ywins[iwin0] * self.taps[1]
361+
y[iwin0] = self.Op.rmatvec(ywins[iwin0].ravel()).reshape(
362+
self.dims[1], self.dims[2]
363+
)
364+
return y
365+
366+
def _register_multiplications(self, savetaper: bool) -> None:
367+
if savetaper:
368+
self._matvec = self._matvec_savetaper
369+
self._rmatvec = self._rmatvec_savetaper
370+
else:
371+
self._matvec = self._matvec_nosavetaper
372+
self._rmatvec = self._rmatvec_nosavetaper

0 commit comments

Comments
 (0)