def test_Diagonal_1dsignal(par): """Dot-test and inversion for Diagonal operator for 1d signal """ for ddim in (par['nx'], par['nt']): d = (np.arange(0, ddim, dtype=par['dtype']) + 1.) + \ par['imag']*(np.arange(0, ddim, dtype=par['dtype']) + 1.) if par['imag'] == 0: d = torch.from_numpy(d).to(dev) else: d = complextorch_fromnumpy(d).to(dev) Dop = Diagonal(d, dtype=d.dtype) assert dottest(Dop, ddim, ddim, tol=1e-4, complexflag=0 if par['imag'] == 0 else 3) x = np.ones(ddim, dtype=par['dtype']) + \ par['imag'] * np.ones(ddim, dtype=par['dtype']) if par['imag'] == 0: x = torch.from_numpy(x).to(dev) else: x = complextorch_fromnumpy(x).to(dev) xcg = cg(Dop, Dop * x, niter=ddim)[0] assert_array_almost_equal(x.numpy(), xcg.cpu().numpy(), decimal=4)
def test_Diagonal_2dsignal(par): """Dot-test and inversion for Diagonal operator for 2d signal """ for idim, ddim in enumerate((par['nx'], par['nt'])): d = (np.arange(0, ddim, dtype=par['dtype']) + 1.) + \ par['imag'] * (np.arange(0, ddim, dtype=par['dtype']) + 1.) if par['imag'] == 0: d = torch.from_numpy(d).to(dev) else: d = complextorch_fromnumpy(d).to(dev) Dop = Diagonal(d, dims=(par['nx'], par['nt']), dir=idim, dtype=par['dtype']) assert dottest(Dop, par['nx'] * par['nt'], par['nx'] * par['nt'], tol=1e-4, complexflag=0 if par['imag'] == 0 else 3) x = np.ones((par['nx'], par['nt']), dtype=par['dtype']) + \ par['imag'] * np.ones((par['nx'], par['nt']), dtype=par['dtype']) if par['imag'] == 0: x = torch.from_numpy(x).to(dev) else: x = complextorch_fromnumpy(x).to(dev) xcg = cg(Dop, Dop * x.flatten(), niter=Dop.shape[0])[0] assert_array_almost_equal(x.flatten().numpy(), xcg.flatten().cpu().numpy(), decimal=4)
def test_MatrixMult_repeated(par): """Dot-test and inversion for test_MatrixMult operator repeated along another dimension """ np.random.seed(10) G = np.random.normal(0, 10, (par['ny'], par['nx'])).astype(par['dtype']) + \ par['imag'] * np.random.normal(0, 10, (par['ny'], par['nx'])).astype(par['dtype']) if par['imag'] == 0: G = torch.from_numpy(G).to(dev) else: G = complextorch_fromnumpy(G).to(dev) Gop = MatrixMult(G, dims=5, dtype=G.dtype) assert dottest(Gop, par['ny'] * 5, par['nx'] * 5, tol=1e-4, complexflag=0 if par['imag'] == 0 else 3) x = (np.ones((par['nx'], 5), dtype=par['dtype'])).flatten() +\ (par['imag'] * np.ones((par['nx'], 5), dtype=par['dtype'])).flatten() if par['imag'] == 0: x = torch.from_numpy(x).to(dev) else: x = complextorch_fromnumpy(x).to(dev) y = Gop * x xcg = cg(Gop.H * Gop, Gop.H * y, niter=2 * par['nx'])[0] if par['imag'] == 0: assert_array_almost_equal(x.numpy(), xcg.numpy(), decimal=3)
def test_MatrixMult(par): """Dot-test and inversion for MatrixMult operator """ np.random.seed(10) G = np.random.normal(0, 10, (par['ny'], par['nx'])).astype(par['dtype']) + \ par['imag']*np.random.normal(0, 10, (par['ny'], par['nx'])).astype(par['dtype']) if par['imag'] == 0: G = torch.from_numpy(G).to(dev) else: G = complextorch_fromnumpy(G).to(dev) Gop = MatrixMult(G, dtype=G.dtype) assert dottest(Gop, par['ny'], par['nx'], tol=1e-4, complexflag=0 if par['imag'] == 0 else 3) x = np.ones(par['nx'], dtype=par['dtype']) + \ par['imag']*np.ones(par['nx'], dtype=par['dtype']) if par['imag'] == 0: x = torch.from_numpy(x).to(dev) else: x = complextorch_fromnumpy(x).to(dev) y = Gop * x xcg = cg(Gop.H * Gop, Gop.H * y, niter=2 * par['nx'])[0] if par['imag'] == 0: # need to also get test to work with complex numbers! assert_array_almost_equal(x.numpy(), xcg.numpy(), decimal=3)
def test_VStack(par): """Dot-test and inversion for VStack operator """ np.random.seed(10) G1 = torch.from_numpy(np.random.normal(0, 10, (par['ny'], par['nx'])).astype('float32')) G2 = torch.from_numpy(np.random.normal(0, 10, (par['ny'], par['nx'])).astype('float32')) x = torch.ones(par['nx'], dtype=torch.float32) + \ par['imag']*torch.ones(par['nx'], dtype=torch.float32) Vop = VStack([MatrixMult(G1, dtype=torch.float32), MatrixMult(G2, dtype=torch.float32)], dtype=torch.float32) assert dottest(Vop, 2*par['ny'], par['nx'], complexflag=0 if par['imag'] == 0 else 3) xcg = cg(Vop.H * Vop, Vop.H * (Vop * x), niter=300)[0] assert_array_almost_equal(x.numpy(), xcg.numpy(), decimal=4)
dtype=torch.float32) y = Cop*x xinv = Cop / y fig, ax = plt.subplots(1, 1, figsize=(10, 3)) ax.plot(t, x.cpu().numpy(), 'k', lw=2, label=r'$x$') ax.plot(t, y.cpu().numpy(), 'r', lw=2, label=r'$y=Ax$') ax.plot(t, xinv.cpu().numpy(), '--g', lw=2, label=r'$x_{ext}$') ax.set_title('Convolve in 1st direction', fontsize=14, fontweight='bold') ax.legend() ax.set_xlim(1.9, 2.1) ############################################################################### # We show now that also a filter with mixed phase (i.e., not centered around zero) # can be applied and inverted for using the :py:class:`pylops.signalprocessing.Convolve1D` # operator. Cop = pylops_gpu.signalprocessing.Convolve1D(nt, h=h, offset=hcenter - 3, dtype=torch.float32) y = Cop * x y1 = Cop.H * x xinv = cg(Cop.H*Cop, Cop.H*y, niter=100)[0] fig, ax = plt.subplots(1, 1, figsize=(10, 3)) ax.plot(t, x.cpu().numpy(), 'k', lw=2, label=r'$x$') ax.plot(t, y.cpu().numpy(), 'r', lw=2, label=r'$y=Ax$') ax.plot(t, y1.cpu().numpy(), 'b', lw=2, label=r'$y=A^Hx$') ax.plot(t, xinv.cpu().numpy(), '--g', lw=2, label=r'$x_{ext}$') ax.set_title('Convolve in 1st direction', fontsize=14, fontweight='bold') ax.set_xlim(1.9, 2.1) ax.legend()
def PoststackInversion(data, wav, m0=None, explicit=False, simultaneous=False, epsI=None, epsR=None, dottest=False, epsRL1=None, device='cpu', togpu=(False, False), tocpu=(False, False), **kwargs_solver): r"""Post-stack linearized seismic inversion. Invert post-stack seismic operator to retrieve an acoustic impedance profile from band-limited seismic post-stack data. Depending on the choice of input parameters, inversion can be trace-by-trace with explicit operator or global with either explicit or linear operator. Parameters ---------- data : :obj:`np.ndarray` Band-limited seismic post-stack data of size :math:`[n_{t0} (\times n_x \times n_y)]` wav : :obj:`np.ndarray` Wavelet in time domain (must have odd number of elements and centered to zero). If 1d, assume stationary wavelet for the entire time axis. If 2d of size :math:`[n_{t0} \times n_h]` use as non-stationary wavelet m0 : :obj:`np.ndarray`, optional Background model of size :math:`[n_{t0} (\times n_x \times n_y)]` explicit : :obj:`bool`, optional Create a chained linear operator (``False``, preferred for large data) or a ``MatrixMult`` linear operator with dense matrix (``True``, preferred for small data) simultaneous : :obj:`bool`, optional Simultaneously invert entire data (``True``) or invert trace-by-trace (``False``) when using ``explicit`` operator (note that the entire data is always inverted when working with linear operator) epsI : :obj:`float`, optional Damping factor for Tikhonov regularization term epsR : :obj:`float`, optional Damping factor for additional Laplacian regularization term dottest : :obj:`bool`, optional Apply dot-test epsRL1 : :obj:`float`, optional Damping factor for additional blockiness regularization term device : :obj:`str`, optional Device to be used togpu : :obj:`tuple`, optional Move model and data from cpu to gpu prior to applying ``matvec`` and ``rmatvec``, respectively (only when ``device='gpu'``) tocpu : :obj:`tuple`, optional Move data and model from gpu to cpu after applying ``matvec`` and ``rmatvec``, respectively (only when ``device='gpu'``) **kwargs_solver Arbitrary keyword arguments for :py:func:`scipy.linalg.lstsq` solver (if ``explicit=True`` and ``epsR=None``) or :py:func:`scipy.sparse.linalg.lsqr` solver (if ``explicit=False`` and/or ``epsR`` is not ``None``) Returns ------- minv : :obj:`np.ndarray` Inverted model of size :math:`[n_{t0} (\times n_x \times n_y)]` datar : :obj:`np.ndarray` Residual data (i.e., data - background data) of size :math:`[n_{t0} (\times n_x \times n_y)]` Notes ----- Refer to :class:`pylops.avo.poststack.PoststackInversion` for implementation details. """ # check if background model and data have same shape if m0 is not None and data.shape != m0.shape: raise ValueError('data and m0 must have same shape') # find out dimensions if len(data.shape) == 1: dims = 1 nt0 = data.shape[0] nspat = None nspatprod = nx = 1 elif len(data.shape) == 2: dims = 2 nt0, nx = data.shape nspat = (nx, ) nspatprod = nx else: dims = 3 nt0, nx, ny = data.shape nspat = (nx, ny) nspatprod = nx * ny data = data.reshape(nt0, nspatprod) # create operator PPop = PoststackLinearModelling(wav, nt0=nt0, spatdims=nspat, explicit=explicit, tocpu=tocpu, togpu=togpu, device=device) if dottest: Dottest(PPop, nt0 * nspatprod, nt0 * nspatprod, raiseerror=True, verb=True) # create and remove background data from original data datar = data.flatten() if m0 is None else \ data.flatten() - PPop * m0.flatten() # invert model if epsR is None: # inversion without spatial regularization if explicit: if epsI is None and not simultaneous: # solve unregularized equations indipendently trace-by-trace minv = torch.solve( datar.reshape(nt0, nspatprod), PPop.A.reshape(nt0, nt0) + 1e-3 * torch.eye(nt0, dtype=torch.float32)).solution elif epsI is None and simultaneous: # solve unregularized equations simultaneously minv = cg(PPop.H * PPop, PPop.H * datar, **kwargs_solver)[0] elif epsI is not None: # create regularized normal equations PP = torch.matmul(PPop.A.t(), PPop.A) + \ epsI * torch.eye(nt0, dtype=torch.float32) datarn = torch.matmul(PPop.A.t(), datar.reshape(nt0, nspatprod)) if not simultaneous: # solve regularized normal eqs. trace-by-trace minv = torch.solve(datarn.reshape(nt0, nspatprod), PP).solution else: # solve regularized normal equations simultaneously PPop_reg = gMatrixMult(PP, dims=nspatprod, device=device, togpu=togpu, tocpu=tocpu) minv = cg(PPop_reg.H * PPop_reg, PPop_reg.H * datar.flatten(), **kwargs_solver)[0] else: # create regularized normal eqs. and solve them simultaneously PP = np.dot(PPop.A.T, PPop.A) + epsI * np.eye(nt0) datarn = PPop.A.T * datar.reshape(nt0, nspatprod) PPop_reg = gMatrixMult(PP, dims=nspatprod, device=device, togpu=togpu, tocpu=tocpu) minv = torch.solve(datarn.reshape(nt0, nspatprod), PPop_reg.A).solution else: # solve unregularized normal equations simultaneously with lop minv = cg(PPop.H * PPop, PPop.H * datar, **kwargs_solver)[0] else: if epsRL1 is None: # L2 inversion with spatial regularization if dims == 1: Regop = gSecondDerivative(nt0, device=device, togpu=togpu, tocpu=tocpu, dtype=PPop.dtype) elif dims == 2: Regop = gLaplacian((nt0, nx), device=device, togpu=togpu, tocpu=tocpu, dtype=PPop.dtype) else: Regop = gLaplacian((nt0, nx, ny), dirs=(1, 2), device=device, togpu=togpu, tocpu=tocpu, dtype=PPop.dtype) minv = RegularizedInversion( PPop, [Regop], data.flatten(), x0=None if m0 is None else m0.flatten(), epsRs=[epsR], **kwargs_solver)[0] else: # Blockiness-promoting inversion with spatial regularization raise NotImplementedError('SplitBregman not available...') # compute residual if epsR is None: datar -= PPop * minv.flatten() else: datar = data.flatten() - PPop * minv.flatten() # reshape inverted model and residual data if dims == 1: minv = minv.squeeze() datar = datar.squeeze() elif dims == 2: minv = minv.reshape(nt0, nx) datar = datar.reshape(nt0, nx) else: minv = minv.reshape(nt0, nx, ny) datar = datar.reshape(nt0, nx, ny) if m0 is not None and epsR is None: minv = minv + m0 return minv, datar
def __truediv__(self, y, niter=None, tol=1e-4): xest = cg(self, y, niter=self.shape[1] if niter is None else niter, tol=tol)[0] return xest
def test_Convolve1D(par): """Dot-test, comparison with pylops and inversion for Convolve1D operator """ np.random.seed(10) #1D if par['dir'] == 0: gCop = gConvolve1D(par['nx'], h=h1, offset=par['offset'], dtype=torch.float32) assert dottest(gCop, par['nx'], par['nx'], tol=1e-3) x = torch.zeros((par['nx']), dtype=torch.float32) x[par['nx'] // 2] = 1. # comparison with pylops Cop = Convolve1D(par['nx'], h=h1.cpu().numpy(), offset=par['offset'], dtype='float32') assert_array_almost_equal(gCop * x, Cop * x.cpu().numpy(), decimal=3) #assert_array_equal(gCop * x, Cop * x.cpu().numpy()) # inversion if par['offset'] == nfilt[0] // 2: # zero phase xcg = cg(gCop, gCop * x, niter=100)[0] else: # non-zero phase xcg = cg(gCop.H * gCop, gCop.H * (gCop * x), niter=100)[0] assert_array_almost_equal(x, xcg, decimal=1) # 1D on 2D gCop = gConvolve1D(par['ny'] * par['nx'], h=h1, offset=par['offset'], dims=(par['ny'], par['nx']), dir=par['dir'], dtype=torch.float32) assert dottest(gCop, par['ny'] * par['nx'], par['ny'] * par['nx'], tol=1e-3) x = torch.zeros((par['ny'], par['nx']), dtype=torch.float32) x[int(par['ny'] / 2 - 3):int(par['ny'] / 2 + 3), int(par['nx'] / 2 - 3):int(par['nx'] / 2 + 3)] = 1. x = x.flatten() # comparison with pylops Cop = Convolve1D(par['ny'] * par['nx'], h=h1.cpu().numpy(), offset=par['offset'], dims=(par['ny'], par['nx']), dir=par['dir'], dtype='float32') assert_array_almost_equal(gCop * x, Cop * x.cpu().numpy(), decimal=3) # assert_array_equal(gCop * x, Cop * x.cpu().numpy()) # inversion if par['offset'] == nfilt[0] // 2: # zero phase xcg = cg(gCop, gCop * x, niter=100)[0] else: # non-zero phase xcg = cg(gCop.H * gCop, gCop.H * (gCop * x), niter=100)[0] assert_array_almost_equal(x, xcg, decimal=1) # 1D on 3D gCop = gConvolve1D(par['nz'] * par['ny'] * par['nx'], h=h1, offset=par['offset'], dims=(par['nz'], par['ny'], par['nx']), dir=par['dir'], dtype=torch.float32) assert dottest(gCop, par['nz'] * par['ny'] * par['nx'], par['nz'] * par['ny'] * par['nx'], tol=1e-3) x = torch.zeros((par['nz'], par['ny'], par['nx']), dtype=torch.float32) x[int(par['nz'] / 2 - 3):int(par['nz'] / 2 + 3), int(par['ny'] / 2 - 3):int(par['ny'] / 2 + 3), int(par['nx'] / 2 - 3):int(par['nx'] / 2 + 3)] = 1. x = x.flatten() # comparison with pylops Cop = Convolve1D(par['nz'] * par['ny'] * par['nx'], h=h1.cpu().numpy(), offset=par['offset'], dims=(par['nz'], par['ny'], par['nx']), dir=par['dir'], dtype='float32') assert_array_almost_equal(gCop * x, Cop * x.cpu().numpy(), decimal=3) # inversion if par['offset'] == nfilt[0] // 2: # zero phase xcg = cg(gCop, gCop * x, niter=100)[0] else: # non-zero phase xcg = cg(gCop.H * gCop, gCop.H * (gCop * x), niter=100)[0] assert_array_almost_equal(x, xcg, decimal=1)