def test_indexing_into_sparse(self): """ Test indexing into SparseFunctions. """ grid = Grid(shape=(4, 4)) sf = SparseTimeFunction(name='sf', grid=grid, npoint=1, nt=10) sf.data[1:-1, 0] = np.arange(8) assert np.all(sf.data[1:-1, 0] == np.arange(8))
def test_scheduling_after_rewrite(): """Tests loop scheduling after DSE-induced expression hoisting.""" grid = Grid((10, 10)) u1 = TimeFunction(name="u1", grid=grid, save=10, time_order=2) u2 = TimeFunction(name="u2", grid=grid, time_order=2) sf1 = SparseTimeFunction(name='sf1', grid=grid, npoint=1, nt=10) const = Function(name="const", grid=grid, space_order=2) # Deliberately inject into u1, rather than u1.forward, to create a WAR eqn1 = Eq(u1.forward, u1 + sin(const)) eqn2 = sf1.inject(u1.forward, expr=sf1) eqn3 = Eq(u2.forward, u2 - u1.dt2 + sin(const)) op = Operator([eqn1] + eqn2 + [eqn3]) trees = retrieve_iteration_tree(op) # Check loop nest structure assert len(trees) == 4 assert all(i.dim == j for i, j in zip(trees[0], grid.dimensions)) # time invariant assert trees[1][0].dim == trees[2][0].dim == trees[3][0].dim == grid.time_dim
def test_scheduling(self): """ Affine iterations -> #pragma omp ... schedule(static,1) ... Non-affine iterations -> #pragma omp ... schedule(static) ... """ grid = Grid(shape=(11, 11)) u = TimeFunction(name='u', grid=grid, time_order=2, save=5, space_order=0) sf1 = SparseTimeFunction(name='s', grid=grid, npoint=1, nt=5) eqns = [Eq(u.forward, u + 1)] eqns += sf1.interpolate(u) op = Operator(eqns, dle='openmp') iterations = FindNodes(Iteration).visit(op) assert len(iterations) == 4 assert iterations[1].is_Affine assert 'schedule(static,1)' in iterations[1].pragmas[0].value assert not iterations[3].is_Affine assert 'schedule(static)' in iterations[3].pragmas[0].value
def test_operator_leakage_sparse(): """ Test to ensure that Operator creation does not cause memory leaks for SparseTimeFunctions. """ grid = Grid(shape=(5, 6)) a = Function(name='a', grid=grid) s = SparseTimeFunction(name='s', grid=grid, npoint=1, nt=1) w_a = weakref.ref(a) w_s = weakref.ref(s) # Create operator and delete everything again op = Operator(s.interpolate(a)) w_op = weakref.ref(op) del op del s del a clear_cache() # Test whether things are still hanging around assert w_a() is None assert w_s() is None assert w_op() is None
def test_empty_arrays(self): """ MFE for issue #1641. """ grid = Grid(shape=(4, 4), extent=(3.0, 3.0)) f = TimeFunction(name='f', grid=grid, space_order=0) f.data[:] = 1. sf1 = SparseTimeFunction(name='sf1', grid=grid, npoint=0, nt=10) sf2 = SparseTimeFunction(name='sf2', grid=grid, npoint=0, nt=10) assert sf1.size == 0 assert sf2.size == 0 eqns = sf1.inject(field=f, expr=sf1 + sf2 + 1.) op = Operator(eqns) op.apply() assert np.all(f.data == 1.) # Again, but with a MatrixSparseTimeFunction mat = scipy.sparse.coo_matrix((0, 0), dtype=np.float32) sf = MatrixSparseTimeFunction(name="s", grid=grid, r=2, matrix=mat, nt=10) assert sf.size == 0 eqns = sf.interpolate(f) op = Operator(eqns) sf.manual_scatter() op(time_m=0, time_M=9) sf.manual_gather() assert np.all(f.data == 1.)
def test_drop_redundants_after_fusion(self): """ Test for detection of redundant aliases that get exposed after Cluster fusion. """ grid = Grid(shape=(10, 10)) t = cos(Function(name="t", grid=grid)) p = sin(Function(name="p", grid=grid)) a = TimeFunction(name="a", grid=grid) b = TimeFunction(name="b", grid=grid) c = TimeFunction(name="c", grid=grid) d = TimeFunction(name="d", grid=grid) e = TimeFunction(name="e", grid=grid) f = TimeFunction(name="f", grid=grid) s1 = SparseTimeFunction(name="s1", grid=grid, npoint=1, nt=2) eqns = [Eq(a.forward, t*a.dx + p*b.dy), Eq(b.forward, p*b.dx + p*t*a.dy)] eqns += s1.inject(field=a.forward, expr=s1) eqns += s1.inject(field=b.forward, expr=s1) eqns += [Eq(c.forward, t*p*a.forward.dx + b.forward.dy), Eq(d.forward, t*d.dx + e.dy + p*a.dt), Eq(e.forward, p*d.dx + e.dy + t*b.dt)] eqns += [Eq(f.forward, t*p*e.forward.dx + p*d.forward.dy)] op = Operator(eqns) arrays = [i for i in FindSymbols().visit(op) if i.is_Array] assert len(arrays) == 2 assert all(i._mem_heap and not i._mem_external for i in arrays)
def setup(self): grid = Grid(shape=(5, 5, 5)) funcs = [Function(name='f%d' % n, grid=grid) for n in range(30)] tfuncs = [TimeFunction(name='u%d' % n, grid=grid) for n in range(30)] stfuncs = [SparseTimeFunction(name='su%d' % n, grid=grid, npoint=1, nt=100) for n in range(30)] v = TimeFunction(name='v', grid=grid, space_order=2) eq = Eq(v.forward, v.laplace + sum(funcs) + sum(tfuncs) + sum(stfuncs), subdomain=grid.interior) self.op = Operator(eq, opt='noop') # Allocate data, populate cached properties, etc. self.op.arguments(time_M=98)
def test_irregular_write(self): """ Compute a simple stencil S w/o offloading it to YASK because of the presence of indirect write accesses (e.g. A[B[i]] = ...); YASK grid functions are however used in the generated code to access the data at the right location. This test checks that the numerical output is correct after this transformation. Initially, the input array (a YASK grid, under the hood), at t=0 is (2D view): 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 Then, the Operator "flips" its content, and at timestep t=1 we get (2D view): 3 2 1 0 3 2 1 0 3 2 1 0 3 2 1 0 """ grid = Grid(shape=(4, 4, 4)) x, y, z = grid.dimensions t = grid.stepping_dim p = SparseTimeFunction(name='points', grid=grid, nt=1, npoint=4) u = TimeFunction(name='yu4D', grid=grid, space_order=0) for i in range(4): for j in range(4): for k in range(4): u.data[0, i, j, k] = k ind = lambda i: p.indexed[0, i] eqs = [ Eq(p.indexed[0, 0], 3.), Eq(p.indexed[0, 1], 2.), Eq(p.indexed[0, 2], 1.), Eq(p.indexed[0, 3], 0.), Eq(u.indexed[t + 1, ind(x), ind(y), ind(z)], u.indexed[t, x, y, z]) ] op = Operator(eqs, subs=grid.spacing_map) op(yu4D=u, time=0) assert 'run_solution' not in str(op) assert all(np.all(u.data[1, :, :, i] == 3 - i) for i in range(4))
def test_constants(self): """ Check that :class:`Constant` objects are treated correctly. """ grid = Grid(shape=(4, 4, 4)) c = Constant(name='c', value=2.) p = SparseTimeFunction(name='points', grid=grid, nt=1, npoint=1) u = TimeFunction(name='yu4D', grid=grid, space_order=0) u.data[:] = 0. op = Operator([Eq(u.forward, u + c), Eq(p.indexed[0, 0], 1. + c)]) assert 'run_solution' in str(op) op.apply(yu4D=u, c=c, t=11) # Check YASK did its job and could read constant grids w/o problems assert np.all(u.data[0] == 20.) # Check the Constant could be read correctly even in Devito-land, i.e., # outside of run_solution assert p.data[0][0] == 3. # Check re-executing with another constant gives the correct result c2 = Constant(name='c', value=5.) op.apply(yu4D=u, c=c2, t=4) assert np.all(u.data[0] == 30.) assert p.data[0][0] == 6.
def test_over_injection(self): nt = 10 grid = Grid(shape=(4, 4)) src = SparseTimeFunction(name='src', grid=grid, npoint=1, nt=nt) rec = SparseTimeFunction(name='rec', grid=grid, npoint=1, nt=nt) u = TimeFunction(name="u", grid=grid, time_order=2, space_order=2, save=nt) u1 = TimeFunction(name="u", grid=grid, time_order=2, space_order=2, save=nt) src.data[:] = 1. eqns = ([Eq(u.forward, u + 1)] + src.inject(field=u.forward, expr=src) + rec.interpolate(expr=u.forward)) op0 = Operator(eqns, opt='noop') op1 = Operator(eqns, opt='buffering') # Check generated code assert len(retrieve_iteration_tree(op1)) ==\ 5 + bool(configuration['language'] != 'C') buffers = [i for i in FindSymbols().visit(op1) if i.is_Array] assert len(buffers) == 1 op0.apply(time_M=nt - 2) op1.apply(time_M=nt - 2, u=u1) assert np.all(u.data == u1.data)
def test_injection_wodup_wtime(self): """ Just like ``test_injection_wodup``, but using a SparseTimeFunction instead of a SparseFunction. Hence, the data scattering/gathering now has to correctly pack/unpack multidimensional arrays. """ grid = Grid(shape=(4, 4), extent=(3.0, 3.0)) save = 3 f = TimeFunction(name='f', grid=grid, save=save, space_order=0) f.data[:] = 0. coords = np.array([(0.5, 0.5), (0.5, 2.5), (2.5, 0.5), (2.5, 2.5)]) sf = SparseTimeFunction(name='sf', grid=grid, nt=save, npoint=len(coords), coordinates=coords) sf.data[0, :] = 4. sf.data[1, :] = 8. sf.data[2, :] = 12. op = Operator(sf.inject(field=f, expr=sf + 1)) op.apply() assert np.all(f.data[0] == 1.25) assert np.all(f.data[1] == 2.25) assert np.all(f.data[2] == 3.25)
def solver(I, V, f, c, L, dt, C, T, user_action=None): """Solve u_tt=c^2*u_xx + f on (0,L)x(0,T].""" Nt = int(round(T / dt)) t = np.linspace(0, Nt * dt, Nt + 1) # Mesh points in time dx = dt * c / float(C) Nx = int(round(L / dx)) x = np.linspace(0, L, Nx + 1) # Mesh points in space C2 = C**2 # Help variable in the scheme # Make sure dx and dt are compatible with x and t dx = x[1] - x[0] dt = t[1] - t[0] # Initialising functions f and V if not provided if f is None or f == 0: f = lambda x, t: 0 if V is None or V == 0: V = lambda x: 0 t0 = time.perf_counter() # Measure CPU time # Set up grid grid = Grid(shape=(Nx + 1), extent=(L)) t_s = grid.stepping_dim # Create and initialise u u = TimeFunction(name='u', grid=grid, time_order=2, space_order=2) u.data[:, :] = I(x[:]) x_dim = grid.dimensions[0] t_dim = grid.time_dim # The wave equation we are trying to solve pde = (1 / c**2) * u.dt2 - u.dx2 # Source term and injection into equation dt_symbolic = grid.time_dim.spacing src = SparseTimeFunction(name='f', grid=grid, npoint=Nx + 1, nt=Nt + 1) for i in range(Nt): src.data[i] = f(x, t[i]) src.coordinates.data[:, 0] = x src_term = src.inject(field=u.forward, expr=src * (dt_symbolic**2)) stencil = Eq(u.forward, solve(pde, u.forward)) # Set up special stencil for initial timestep with substitution for u.backward v = Function(name='v', grid=grid, npoint=Nx + 1, nt=1) v.data[:] = V(x[:]) stencil_init = stencil.subs(u.backward, u.forward - dt_symbolic * v) # Boundary conditions bc = [Eq(u[t_s + 1, 0], 0)] bc += [Eq(u[t_s + 1, Nx], 0)] # Create and apply operators op_init = Operator([stencil_init] + src_term + bc) op = Operator([stencil] + src_term + bc) op_init.apply(time_M=1, dt=dt) op.apply(time_m=1, time_M=Nt, dt=dt) cpu_time = time.perf_counter() - t0 return u.data[-1], x, t, cpu_time