def test_point_collocation_scalar_valued_with_param(func_param_nd): """Check collocation of scalar-valued functions with parameters.""" domain = odl.IntervalProd([0, 0], [1, 1]) points = _points(domain, 3) mesh_shape = (2, 3) mesh = _meshgrid(domain, mesh_shape) func_ref, func = func_param_nd true_values_points = func_ref(points, c=2.5) true_values_mesh = func_ref(mesh, c=2.5) sampl_func = sampling_function(func, domain, out_dtype='float64') collocator = partial(point_collocation, sampl_func) # Out of place result_points = collocator(points, c=2.5) result_mesh = collocator(mesh, c=2.5) assert all_almost_equal(result_points, true_values_points) assert all_almost_equal(result_mesh, true_values_mesh) # In place out_points = np.empty(3, dtype='float64') out_mesh = np.empty(mesh_shape, dtype='float64') collocator(points, out=out_points, c=2.5) collocator(mesh, out=out_mesh, c=2.5) assert all_almost_equal(out_points, true_values_points) assert all_almost_equal(out_mesh, true_values_mesh) # Complex output true_values_points = func_ref(points, c=2j) true_values_mesh = func_ref(mesh, c=2j) sampl_func = sampling_function(func, domain, out_dtype='complex128') collocator = partial(point_collocation, sampl_func) result_points = collocator(points, c=2j) result_mesh = collocator(mesh, c=2j) assert all_almost_equal(result_points, true_values_points) assert all_almost_equal(result_mesh, true_values_mesh)
def performance_example(): # Simple function, already supports vectorization f_vec = sampling_function(lambda x: x**2, domain=odl.IntervalProd(0, 1)) # Vectorized with NumPy's poor man's vectorization function f_novec = np.vectorize(lambda x: x**2) # We test both versions with 10000 evaluation points. The natively # vectorized version should be much faster than the one using # numpy.vectorize. points = np.linspace(0, 1, 10000) print('Vectorized runtime: {:5f}' ''.format(timeit.timeit(lambda: f_vec(points), number=100))) print('Non-vectorized runtime: {:5f}' ''.format(timeit.timeit(lambda: f_novec(points), number=100)))
def test_fspace_elem_eval_unusual_dtypes(): """Check evaluation with unusual data types (int and string).""" domain = odl.Strings(3) strings = np.array(['aa', 'b', 'cab', 'aba']) out_vec = np.empty((4, ), dtype='int64') # Can be vectorized for arrays only sampl_func = sampling_function( lambda s: np.array([str(si).count('a') for si in s]), domain, out_dtype='int64') collocator = partial(point_collocation, sampl_func) true_values = [2, 0, 1, 2] assert collocator('abc') == 1 assert all_equal(collocator(strings), true_values) collocator(strings, out=out_vec) assert all_equal(out_vec, true_values)
def test_point_collocation_vector_valued(func_vec_nd): """Check collocation of vector-valued functions.""" domain = odl.IntervalProd([0, 0], [1, 1]) points = _points(domain, 3) mesh_shape = (2, 3) mesh = _meshgrid(domain, mesh_shape) point = [0.5, 0.5] values_points_shape = (2, 3) values_mesh_shape = (2, 2, 3) func_ref, func = func_vec_nd true_values_points = func_ref(points) true_values_mesh = func_ref(mesh) true_value_point = func_ref(point) sampl_func = sampling_function(func, domain, out_dtype=('float64', (2, ))) collocator = partial(point_collocation, sampl_func) # Out of place result_points = collocator(points) result_mesh = collocator(mesh) assert all_almost_equal(result_points, true_values_points) assert all_almost_equal(result_mesh, true_values_mesh) assert result_points.dtype == 'float64' assert result_mesh.dtype == 'float64' assert result_points.flags.writeable assert result_mesh.flags.writeable # In place out_points = np.empty(values_points_shape, dtype='float64') out_mesh = np.empty(values_mesh_shape, dtype='float64') collocator(points, out=out_points) collocator(mesh, out=out_mesh) assert all_almost_equal(out_points, true_values_points) assert all_almost_equal(out_mesh, true_values_mesh) # Single point evaluation result_point = collocator(point) assert all_almost_equal(result_point, true_value_point) out_point = np.empty((2, ), dtype='float64') collocator(point, out=out_point) assert all_almost_equal(out_point, true_value_point)
def test_fspace_elem_eval_vec_1d(func_vec_1d): """Test evaluation in 1d since it's a corner case regarding shapes.""" domain = odl.IntervalProd(0, 1) points = _points(domain, 3) mesh_shape = (4, ) mesh = _meshgrid(domain, mesh_shape) point1 = 0.5 point2 = [0.5] values_points_shape = (2, 3) values_mesh_shape = (2, 4) value_point_shape = (2, ) func_ref, func = func_vec_1d true_result_points = np.array(func_ref(points)) true_result_mesh = np.array(func_ref(mesh)) true_result_point = np.array(func_ref(np.array([point1]))).squeeze() sampl_func = sampling_function(func, domain, out_dtype=('float64', (2, ))) collocator = partial(point_collocation, sampl_func) result_points = collocator(points) result_mesh = collocator(mesh) result_point1 = collocator(point1) result_point2 = collocator(point2) assert all_almost_equal(result_points, true_result_points) assert all_almost_equal(result_mesh, true_result_mesh) assert all_almost_equal(result_point1, true_result_point) assert all_almost_equal(result_point2, true_result_point) out_points = np.empty(values_points_shape, dtype='float64') out_mesh = np.empty(values_mesh_shape, dtype='float64') out_point1 = np.empty(value_point_shape, dtype='float64') out_point2 = np.empty(value_point_shape, dtype='float64') collocator(points, out=out_points) collocator(mesh, out=out_mesh) collocator(point1, out=out_point1) collocator(point2, out=out_point2) assert all_almost_equal(out_points, true_result_points) assert all_almost_equal(out_mesh, true_result_mesh) assert all_almost_equal(out_point1, true_result_point) assert all_almost_equal(out_point2, true_result_point)
def test_point_collocation_tensor_valued(func_tens): """Check collocation of tensor-valued functions.""" domain = odl.IntervalProd([0, 0], [1, 1]) points = _points(domain, 4) mesh_shape = (4, 5) mesh = _meshgrid(domain, mesh_shape) point = [0.5, 0.5] values_points_shape = (2, 3, 4) values_mesh_shape = (2, 3, 4, 5) value_point_shape = (2, 3) func_ref, func = func_tens true_result_points = np.array(func_ref(points)) true_result_mesh = np.array(func_ref(mesh)) true_result_point = np.array(func_ref(np.array(point)[:, None])).squeeze() sampl_func = sampling_function(func, domain, out_dtype=('float64', (2, 3))) collocator = partial(point_collocation, sampl_func) result_points = collocator(points) result_mesh = collocator(mesh) result_point = collocator(point) assert all_almost_equal(result_points, true_result_points) assert all_almost_equal(result_mesh, true_result_mesh) assert all_almost_equal(result_point, true_result_point) assert result_points.flags.writeable assert result_mesh.flags.writeable assert result_point.flags.writeable out_points = np.empty(values_points_shape, dtype='float64') out_mesh = np.empty(values_mesh_shape, dtype='float64') out_point = np.empty(value_point_shape, dtype='float64') collocator(points, out=out_points) collocator(mesh, out=out_mesh) collocator(point, out=out_point) assert all_almost_equal(out_points, true_result_points) assert all_almost_equal(out_mesh, true_result_mesh) assert all_almost_equal(out_point, true_result_point)
def test_point_collocation_scalar_valued(domain_ndim, out_dtype, func_nd): """Check collocation of scalar-valued functions.""" domain = odl.IntervalProd([0] * domain_ndim, [1] * domain_ndim) points = _points(domain, 3) mesh_shape = tuple(range(2, 2 + domain_ndim)) mesh = _meshgrid(domain, mesh_shape) point = [0.5] * domain_ndim func_ref, func = func_nd true_values_points = func_ref(points) true_values_mesh = func_ref(mesh) true_value_point = func_ref(point) sampl_func = sampling_function(func, domain, out_dtype) collocator = partial(point_collocation, sampl_func) # Out of place result_points = collocator(points) result_mesh = collocator(mesh) assert all_almost_equal(result_points, true_values_points) assert all_almost_equal(result_mesh, true_values_mesh) assert result_points.dtype == out_dtype assert result_mesh.dtype == out_dtype assert result_points.flags.writeable assert result_mesh.flags.writeable # In place out_points = np.empty(3, dtype=out_dtype) out_mesh = np.empty(mesh_shape, dtype=out_dtype) collocator(points, out=out_points) collocator(mesh, out=out_mesh) assert all_almost_equal(out_points, true_values_points) assert all_almost_equal(out_mesh, true_values_mesh) # Single point evaluation result_point = collocator(point) assert all_almost_equal(result_point, true_value_point)
def numba_example(): # Some functions are not easily vectorized, here we can use Numba to # improve performance. # See http://numba.pydata.org/ try: import numba except ImportError: print('Numba not installed, skipping.') return def myfunc(x): """Return x - y if x > y, otherwise return x + y.""" if x[0] > x[1]: return x[0] - x[1] else: return x[0] + x[1] # Numba expects functions f(x1, x2, x3, ...), while we have the # convention f(x) with x = (x1, x2, x3, ...). Therefore we need # to wrap the Numba-vectorized function. vectorized = numba.vectorize(lambda x, y: x - y if x > y else x + y) def myfunc_numba(x): """Return x - y if x > y, otherwise return x + y.""" return vectorized(x[0], x[1]) def myfunc_vec(x): """Return x - y if x > y, otherwise return x + y.""" # This implementation uses Numpy's fast built-in vectorization # directly. The function np.where checks the condition in the # first argument and takes the values from the second argument # for all entries where the condition is `True`, otherwise # the values from the third argument are taken. The arrays are # automatically broadcast, i.e. the broadcast shape of the # condition expression determines the output shape. return np.where(x[0] > x[1], x[0] - x[1], x[0] + x[1]) # Create (continuous) functions in the space of function defined # on the rectangle [0, 1] x [0, 1]. f_vec = sampling_function(myfunc_vec, domain=odl.IntervalProd([0, 0], [1, 1])) f_numba = sampling_function(myfunc_numba, domain=odl.IntervalProd([0, 0], [1, 1])) # Create a unform grid in [0, 1] x [0, 1] (fspace.domain) with 2000 # samples per dimension. grid = odl.uniform_grid([0, 0], [1, 1], shape=(2000, 2000)) # The points() method really creates all grid points (2000^2) and # stores them one-by-one (row-wise) in a large array with shape # (2000*2000, 2). Since the function expects points[i] to be the # array of i-th components of all points, we need to transpose. points = grid.points().T # The meshgrid property only returns a sparse representation of the # grid, a tuple whose i-th entry is the vector of all possible i-th # components in the grid (2000). Extra dimensions are added to the # vector in order to support automatic broadcasting. This is both # faster and more memory-friendly than creating the full point array. # See the numpy.meshgrid function for more information. mesh = grid.meshgrid # Returns a sparse meshgrid (2000 * 2) print('Native vectorized runtime (points): {:5f}' ''.format(timeit.timeit(lambda: f_vec(points), number=1))) print('Native vectorized runtime (meshgrid): {:5f}' ''.format(timeit.timeit(lambda: f_vec(mesh), number=1))) print('Numba vectorized runtime (points): {:5f}' ''.format(timeit.timeit(lambda: f_numba(points), number=1))) print('Numba vectorized runtime (meshgrid): {:5f}' ''.format(timeit.timeit(lambda: f_numba(mesh), number=1)))