def test_linearity_adjoint_F(self, shape, dtype, so): """ Test the linearity of the adjoint modeling operator by verifying: a F^T(r) = F^T(a r) """ np.random.seed(0) solver = acoustic_ssa_setup(shape=shape, dtype=dtype, space_order=so) src0 = solver.geometry.src rec, _, _ = solver.forward(src0) a = -1 + 2 * np.random.rand() src1, _, _ = solver.adjoint(rec) rec.data[:] = a * rec.data[:] src2, _, _ = solver.adjoint(rec) src1.data[:] *= a # Check adjoint source wavefeild linearity # Normalize by rms of rec2, to enable using abolute tolerance below rms2 = np.sqrt(np.mean(src2.data**2)) diff = (src1.data - src2.data) / rms2 info("linearity adjoint F %s (so=%d) rms 1,2,diff; " "%+16.10e %+16.10e %+16.10e" % (shape, so, np.sqrt(np.mean(src1.data**2)), np.sqrt(np.mean(src2.data**2)), np.sqrt(np.mean(diff**2)))) tol = 1.e-12 assert np.allclose(diff, 0.0, atol=tol)
def test_adjoint_J(self, shape, dtype, so): """ Test the Jacobian of the forward modeling operator by verifying for 'random' dm, dr: dr . J(dm) = J^T(dr) . dm """ np.random.seed(0) solver = acoustic_ssa_setup(shape=shape, dtype=dtype, space_order=so) src0 = solver.geometry.src m0 = Function(name='m0', grid=solver.model.grid, space_order=so) dm1 = Function(name='dm1', grid=solver.model.grid, space_order=so) m0.data[:] = 1.5 # Model perturbation, box of random values centered on middle of model dm1.data[:] = 0 size = 5 ns = 2 * size + 1 if len(shape) == 2: nx2, nz2 = shape[0] // 2, shape[1] // 2 dm1.data[nx2-size:nx2+size, nz2-size:nz2+size] = \ -1 + 2 * np.random.rand(ns, ns) else: nx2, ny2, nz2 = shape[0] // 2, shape[1] // 2, shape[2] // 2 nx, ny, nz = shape dm1.data[nx2-size:nx2+size, ny2-size:ny2+size, nz2-size:nz2+size] = \ -1 + 2 * np.random.rand(ns, ns, ns) # Data perturbation rec1 = solver.geometry.rec nt, nr = rec1.data.shape rec1.data[:] = np.random.rand(nt, nr) # Linearized modeling rec2, u0, _, _ = solver.jacobian_forward(dm1, src0, vp=m0, save=True) dm2, _, _, _ = solver.jacobian_adjoint(rec1, u0, vp=m0) sum_m = np.dot(dm1.data.reshape(-1), dm2.data.reshape(-1)) sum_d = np.dot(rec1.data.reshape(-1), rec2.data.reshape(-1)) diff = (sum_m - sum_d) / (sum_m + sum_d) info( "adjoint J %s (so=%d) sum_m, sum_d, diff; %16.10e %+16.10e %+16.10e" % (shape, so, sum_m, sum_d, diff)) assert np.isclose(diff, 0., atol=1.e-11)
def test_linearity_forward_J(self, shape, dtype, so): """ Test the linearity of the forward Jacobian of the forward modeling operator by verifying a J(dm) = J(a dm) """ np.random.seed(0) solver = acoustic_ssa_setup(shape=shape, dtype=dtype, space_order=so) src0 = solver.geometry.src m0 = Function(name='m0', grid=solver.model.grid, space_order=so) m1 = Function(name='m1', grid=solver.model.grid, space_order=so) m0.data[:] = 1.5 # Model perturbation, box of random values centered on middle of model m1.data[:] = 0 size = 5 ns = 2 * size + 1 if len(shape) == 2: nx2, nz2 = shape[0]//2, shape[1]//2 m1.data[nx2-size:nx2+size, nz2-size:nz2+size] = \ -1 + 2 * np.random.rand(ns, ns) else: nx2, ny2, nz2 = shape[0]//2, shape[1]//2, shape[2]//2 nx, ny, nz = shape m1.data[nx2-size:nx2+size, ny2-size:ny2+size, nz2-size:nz2+size] = \ -1 + 2 * np.random.rand(ns, ns, ns) a = np.random.rand() rec1, _, _, _ = solver.jacobian(m1, src0, vp=m0, save=True) rec1.data[:] = a * rec1.data[:] m1.data[:] = a * m1.data[:] rec2, _, _, _ = solver.jacobian(m1, src0, vp=m0) # Normalize by rms of rec2, to enable using abolute tolerance below rms2 = np.sqrt(np.mean(rec2.data**2)) diff = (rec1.data - rec2.data) / rms2 info("linearity forward J %s (so=%d) rms 1,2,diff; " "%+16.10e %+16.10e %+16.10e" % (shape, so, np.sqrt(np.mean(rec1.data**2)), np.sqrt(np.mean(rec2.data**2)), np.sqrt(np.mean(diff**2)))) tol = 1.e-12 assert np.allclose(diff, 0.0, atol=tol)
def test_adjoint_F(self, shape, dtype, so): """ Test the forward modeling operator by verifying for random s, r: r . F(s) = F^T(r) . s """ solver = acoustic_ssa_setup(shape=shape, dtype=dtype, space_order=so) src1 = solver.geometry.src rec1 = solver.geometry.rec rec2, _, _ = solver.forward(src1) # flip sign of receiver data for adjoint to make it interesting rec1.data[:] = rec2.data[:] src2, _, _ = solver.adjoint(rec1) sum_s = np.dot(src1.data.reshape(-1), src2.data.reshape(-1)) sum_r = np.dot(rec1.data.reshape(-1), rec2.data.reshape(-1)) diff = (sum_s - sum_r) / (sum_s + sum_r) info("adjoint F %s (so=%d) sum_s, sum_r, diff; %+16.10e %+16.10e %+16.10e" % (shape, so, sum_s, sum_r, diff)) assert np.isclose(diff, 0., atol=1.e-12)
def test_linearity_forward_F(self, shape, dtype, so): """ Test the linearity of the forward modeling operator by verifying: a F(s) = F(a s) """ solver = acoustic_ssa_setup(shape=shape, dtype=dtype, space_order=so) src = solver.geometry.src a = -1 + 2 * np.random.rand() rec1, _, _ = solver.forward(src) src.data[:] *= a rec2, _, _ = solver.forward(src) rec1.data[:] *= a # Check receiver wavefeild linearity # Normalize by rms of rec2, to enable using abolute tolerance below rms2 = np.sqrt(np.mean(rec2.data**2)) diff = (rec1.data - rec2.data) / rms2 info("linearity forward F %s (so=%d) rms 1,2,diff; " "%+16.10e %+16.10e %+16.10e" % (shape, so, np.sqrt(np.mean(rec1.data**2)), np.sqrt(np.mean(rec2.data**2)), np.sqrt(np.mean(diff**2)))) tol = 1.e-12 assert np.allclose(diff, 0.0, atol=tol)
def test_linearization_F(self, shape, dtype, so): """ Test the linearization of the forward modeling operator by verifying for sequence of h decreasing that the error in the linearization E is of second order. E = 0.5 || F(m + h dm) - F(m) - h J(dm) ||^2 This is done by fitting a 1st order polynomial to the norms """ np.random.seed(0) solver = acoustic_ssa_setup(shape=shape, dtype=dtype, space_order=so) src = solver.geometry.src # Create Functions for models and perturbation m0 = Function(name='m0', grid=solver.model.grid, space_order=so) mm = Function(name='mm', grid=solver.model.grid, space_order=so) dm = Function(name='dm', grid=solver.model.grid, space_order=so) # Background model m0.data[:] = 1.5 # Model perturbation, box of random values centered on middle of model dm.data[:] = 0 size = 5 ns = 2 * size + 1 if len(shape) == 2: nx2, nz2 = shape[0]//2, shape[1]//2 dm.data[nx2-size:nx2+size, nz2-size:nz2+size] = \ -1 + 2 * np.random.rand(ns, ns) else: nx2, ny2, nz2 = shape[0]//2, shape[1]//2, shape[2]//2 nx, ny, nz = shape dm.data[nx2-size:nx2+size, ny2-size:ny2+size, nz2-size:nz2+size] = \ -1 + 2 * np.random.rand(ns, ns, ns) # Compute F(m + dm) rec0, u0, summary0 = solver.forward(src, vp=m0) # Compute J(dm) rec1, u1, du, summary1 = solver.jacobian(dm, src=src, vp=m0) # Linearization test via polyfit (see devito/tests/test_gradient.py) # Solve F(m + h dm) for sequence of decreasing h dh = np.sqrt(2.0) h = 0.1 nstep = 7 scale = np.empty(nstep) norm1 = np.empty(nstep) norm2 = np.empty(nstep) for kstep in range(nstep): h = h / dh mm.data[:] = m0.data + h * dm.data rec2, _, _ = solver.forward(src, vp=mm) scale[kstep] = h norm1[kstep] = 0.5 * np.linalg.norm(rec2.data - rec0.data)**2 norm2[kstep] = 0.5 * np.linalg.norm(rec2.data - rec0.data - h * rec1.data)**2 # Fit 1st order polynomials to the error sequences # Assert the 1st order error has slope dh^2 # Assert the 2nd order error has slope dh^4 p1 = np.polyfit(np.log10(scale), np.log10(norm1), 1) p2 = np.polyfit(np.log10(scale), np.log10(norm2), 1) info("linearization F %s (so=%d) 1st (%.1f) = %.4f, 2nd (%.1f) = %.4f" % (shape, so, dh**2, p1[0], dh**4, p2[0])) # we only really care the 2nd order err is valid, not so much the 1st order error assert np.isclose(p1[0], dh**2, rtol=0.25) assert np.isclose(p2[0], dh**4, rtol=0.10)