def unique_coords_nd(N, ndim, shifted=False, normalized=True): """ Generate unique polar coordinates from 2D or 3D rectangular coordinates. :param N: length size of a square or cube. :param ndim: number of dimension, 2 or 3. :param shifted: shifted half pixel or not for odd N. :param normalized: normalize the grid or not. :return: The unique polar coordinates in 2D or 3D """ ensure(ndim in (2, 3), 'Only two- or three-dimensional basis functions are supported.') ensure(N > 0, 'Number of grid points should be greater than 0.') if ndim == 2: grid = grid_2d(N, shifted=shifted, normalized=normalized) mask = grid['r'] <= 1 # Minor differences in r/theta/phi values are unimportant for the purpose # of this function, so round off before proceeding # TODO: numpy boolean indexing will return a 1d array (like MATLAB) # However, it always searches in row-major order, unlike MATLAB (column-major), # with no options to change the search order. The results we'll be getting back are thus not comparable. # We transpose the appropriate ndarrays before applying the mask to obtain the same behavior as MATLAB. r = grid['r'].T[mask].round(5) phi = grid['phi'].T[mask].round(5) r_unique, r_idx = np.unique(r, return_inverse=True) ang_unique, ang_idx = np.unique(phi, return_inverse=True) else: grid = grid_3d(N, shifted=shifted, normalized=normalized) mask = grid['r'] <= 1 # In Numpy, elements in the indexed array are always iterated and returned in row-major (C-style) order. # To emulate a behavior where iteration happens in Fortran order, we swap axes 0 and 2 of both the array # being indexed (r/theta/phi), as well as the mask itself. # TODO: This is only for the purpose of getting the same behavior as MATLAB while porting the code, and is # likely not needed in the final version. # Minor differences in r/theta/phi values are unimportant for the purpose of this function, # so we round off before proceeding. mask_ = np.swapaxes(mask, 0, 2) r = np.swapaxes(grid['r'], 0, 2)[mask_].round(5) theta = np.swapaxes(grid['theta'], 0, 2)[mask_].round(5) phi = np.swapaxes(grid['phi'], 0, 2)[mask_].round(5) r_unique, r_idx = np.unique(r, return_inverse=True) ang_unique, ang_idx = np.unique(np.vstack([theta, phi]), axis=1, return_inverse=True) return { 'r_unique': r_unique, 'ang_unique': ang_unique, 'r_idx': r_idx, 'ang_idx': ang_idx, 'mask': mask }
def testGrid3d(self): grid3d = grid_3d(8) self.assertTrue(np.allclose(grid3d['x'], np.load(os.path.join(DATA_DIR, 'grid3d_8_x.npy')))) self.assertTrue(np.allclose(grid3d['y'], np.load(os.path.join(DATA_DIR, 'grid3d_8_y.npy')))) self.assertTrue(np.allclose(grid3d['z'], np.load(os.path.join(DATA_DIR, 'grid3d_8_z.npy')))) self.assertTrue(np.allclose(grid3d['r'], np.load(os.path.join(DATA_DIR, 'grid3d_8_r.npy')))) self.assertTrue(np.allclose(grid3d['phi'], np.load(os.path.join(DATA_DIR, 'grid3d_8_phi.npy')))) self.assertTrue(np.allclose(grid3d['theta'], np.load(os.path.join(DATA_DIR, 'grid3d_8_theta.npy'))))
def testDownsample(self): # generate a 3D map with density decays as Gaussian function g3d = grid_3d(self.L, dtype=self.dtype) coords = np.array( [g3d["x"].flatten(), g3d["y"].flatten(), g3d["z"].flatten()]) sigma = 0.2 vol = np.exp(-0.5 * np.sum(np.abs(coords / sigma)**2, axis=0)).astype( self.dtype) vol = np.reshape(vol, g3d["x"].shape) vols = Volume(vol) # set noise to zero and CFT filters to unity for simulation object noise_var = 0 noise_filter = ScalarFilter(dim=2, value=noise_var) sim = Simulation( L=self.L, n=self.n, vols=vols, offsets=0.0, amplitudes=1.0, unique_filters=[ ScalarFilter(dim=2, value=1) for d in np.linspace(1.5e4, 2.5e4, 7) ], noise_filter=noise_filter, dtype=self.dtype, ) # get images before downsample imgs_org = sim.images(start=0, num=self.n) # get images after downsample max_resolution = 32 sim.downsample(max_resolution) imgs_ds = sim.images(start=0, num=self.n) # Check individual grid points self.assertTrue( np.allclose( imgs_org[:, 32, 32], imgs_ds[:, 16, 16], atol=utest_tolerance(self.dtype), )) # check resolution self.assertTrue(np.allclose(max_resolution, imgs_ds.shape[1])) # check energy conservation after downsample self.assertTrue( np.allclose( anorm(imgs_org.asnumpy(), axes=(1, 2)) / self.L, anorm(imgs_ds.asnumpy(), axes=(1, 2)) / max_resolution, atol=utest_tolerance(self.dtype), ))
def eval_gaussian_blobs(self, L, Q, D, mu): g = grid_3d(L) coords = np.array( [g['x'].flatten(), g['y'].flatten(), g['z'].flatten()]) K = Q.shape[-1] vol = np.zeros(shape=(1, coords.shape[-1])).astype(self.dtype) for k in range(K): coords_k = coords - mu[:, k, np.newaxis] coords_k = Q[:, :, k] / np.sqrt(np.diag( D[:, :, k])) @ Q[:, :, k].T @ coords_k vol += np.exp(-0.5 * np.sum(np.abs(coords_k)**2, axis=0)) vol = np.reshape(vol, g['x'].shape) return vol
def eval_gaussian_blobs(self, L, Q, D, mu): g = grid_3d(L) # Migration Note - Matlab (:) flattens in column-major order, so specify 'F' with flatten() coords = np.array( [g['x'].flatten('F'), g['y'].flatten('F'), g['z'].flatten('F')]) K = Q.shape[-1] vol = np.zeros(shape=(1, coords.shape[-1])).astype(self.dtype) for k in range(K): coords_k = coords - mu[:, k, np.newaxis] coords_k = Q[:, :, k] / np.sqrt(np.diag( D[:, :, k])) @ Q[:, :, k].T @ coords_k vol += np.exp(-0.5 * np.sum(np.abs(coords_k)**2, axis=0)) vol = m_reshape(vol, g['x'].shape) return vol
def downsample(insamples, szout): """ Blur and downsample 1D to 3D objects such as, curves, images or volumes :param insamples: Set of objects to be downsampled in the form of an array, the last dimension is the number of objects. :param szout: The desired resolution of for output objects. :return: An array consists of the blurred and downsampled objects. """ ensure( insamples.ndim - 1 == szout.ndim, 'The number of downsampling dimensions is not the same as that of objects.' ) L_in = insamples.shape[0] L_out = szout.shape[0] ndata = insamples.shape(-1) outdims = szout outdims.push_back(ndata) outsamples = np.zeros((outdims)).astype(insamples.dtype) if insamples.ndim == 2: # one dimension object grid_in = grid_1d(L_in) grid_out = grid_1d(L_out) # x values corresponding to 'grid'. This is what scipy interpolator needs to function. x = np.ceil(np.arange(-L_in / 2, L_in / 2)) / (L_in / 2) mask = (np.abs(grid_in['x']) < L_out / L_in) insamples_fft = np.real( centered_ifft1(centered_fft1(insamples) * np.expand_dims(mask, 1))) for idata in range(ndata): interpolator = RegularGridInterpolator((x, ), insamples_fft[:, idata], bounds_error=False, fill_value=0) outsamples[:, :, idata] = interpolator(np.dstack([grid_out['x']])) elif insamples.ndim == 3: grid_in = grid_2d(L_in) grid_out = grid_2d(L_out) # x, y values corresponding to 'grid'. This is what scipy interpolator needs to function. x = y = np.ceil(np.arange(-L_in / 2, L_in / 2)) / (L_in / 2) mask = (np.abs(grid_in['x']) < L_out / L_in) & (np.abs(grid_in['y']) < L_out / L_in) insamples_fft = np.real( centered_ifft2(centered_fft2(insamples) * np.expand_dims(mask, 2))) for idata in range(ndata): interpolator = RegularGridInterpolator((x, y), insamples_fft[:, :, idata], bounds_error=False, fill_value=0) outsamples[:, :, idata] = interpolator( np.dstack([grid_out['x'], grid_out['y']])) elif insamples.ndim == 4: grid_in = grid_3d(L_in) grid_out = grid_3d(L_out) # x, y, z values corresponding to 'grid'. This is what scipy interpolator needs to function. x = y = z = np.ceil(np.arange(-L_in / 2, L_in / 2)) / (L_in / 2) mask = (np.abs(grid_in['x']) < L_out / L_in) & (np.abs( grid_in['y']) < L_out / L_in) & (np.abs(grid_in['z']) < L_out / L_in) insamples_fft = np.real( centered_ifft3(centered_fft3(insamples) * np.expand_dims(mask, 3))) for idata in range(ndata): interpolator = RegularGridInterpolator((x, y, z), insamples_fft[:, :, :, idata], bounds_error=False, fill_value=0) outsamples[:, :, :, idata] = interpolator( np.dstack([grid_out['x'], grid_out['y'], grid_out['z']])) return outsamples