def test_compute_modes(self): """Test building of modes.""" vec_array = _parallel.call_and_bcast(np.random.random, ((self.num_states, self.num_vecs))) if _parallel.is_rank_zero(): for vec_index, handle in enumerate(self.vec_handles): handle.put(np.array(vec_array[:, vec_index]).squeeze()) # Compute DMD directly from data, for a sequential dataset ritz_vals, ritz_vecs, build_coeffs, mode_norms =\ self._helper_compute_DMD_from_data(vec_array[:, :-1], vec_array[:, 1:], util.InnerProductBlock(np.vdot)) # Check direct computation against modred _parallel.barrier() self._helper_check_modes(ritz_vecs, build_coeffs, self.vec_handles) # Generate data for a non-sequential dataset adv_vec_array = _parallel.call_and_bcast(np.random.random, ((self.num_states, self.num_vecs))) if _parallel.is_rank_zero(): for vec_index, handle in enumerate(self.adv_vec_handles): handle.put(np.array(adv_vec_array[:, vec_index]).squeeze()) # Compute DMD directly from data, for a non-sequential dataset ritz_vals, ritz_vecs, build_coeffs, mode_norms =\ self._helper_compute_DMD_from_data(vec_array, adv_vec_array, util.InnerProductBlock(np.vdot)) # Check direct computation against modred _parallel.barrier() self._helper_check_modes(ritz_vecs, build_coeffs, self.vec_handles)
def test_compute_spectrum(self): """Test DMD spectrum""" rtol = 1e-10 atol = 1e-16 # Define an array of vectors, with corresponding handles vec_array = _parallel.call_and_bcast( np.random.random, ((self.num_states, self.num_vecs))) if _parallel.is_rank_zero(): for vec_index, handle in enumerate(self.vec_handles): handle.put(np.array(vec_array[:, vec_index]).squeeze()) # Check that spectral coefficients computed using adjoints match those # computed using a pseudoinverse. Perform the decomp only once using # the DMD object, so that the spectral coefficients are computed from # the same data, but in two different ways. _parallel.barrier() self.my_DMD.compute_decomp(self.vec_handles) spectral_coeffs_computed = self.my_DMD.compute_spectrum() spectral_coeffs_true = self._helper_compute_DMD_from_data( vec_array, util.InnerProductBlock(np.vdot))[2] self._helper_test_1D_array_to_sign(spectral_coeffs_true, spectral_coeffs_computed, rtol=rtol, atol=atol) # Create more data, to check a non-sequential dataset adv_vec_array = _parallel.call_and_bcast( np.random.random, ((self.num_states, self.num_vecs))) if _parallel.is_rank_zero(): for vec_index, handle in enumerate(self.adv_vec_handles): handle.put(np.array(adv_vec_array[:, vec_index]).squeeze()) # Check that spectral coefficients computed using adjoints match those # computed using a pseudoinverse _parallel.barrier() self.my_DMD.compute_decomp(self.vec_handles, adv_vec_handles=self.adv_vec_handles) spectral_coeffs_computed = self.my_DMD.compute_spectrum() spectral_coeffs_true = self._helper_compute_DMD_from_data( vec_array, util.InnerProductBlock(np.vdot), adv_vec_array=adv_vec_array)[2] self._helper_test_1D_array_to_sign(spectral_coeffs_true, spectral_coeffs_computed, rtol=rtol, atol=atol)
def _helper_check_decomp(self, vec_array, vec_handles, adv_vec_array=None, adv_vec_handles=None, max_num_eigvals=None): # Set tolerance. tol = 1e-10 # Compute reference DMD values (eigvals_true, R_low_order_eigvecs_true, L_low_order_eigvecs_true, correlation_mat_eigvals_true, correlation_mat_eigvecs_true) = (self._helper_compute_DMD_from_data( vec_array, util.InnerProductBlock(np.vdot), adv_vec_array=adv_vec_array, max_num_eigvals=max_num_eigvals))[3:-1] # Compute DMD using modred (eigvals_returned, R_low_order_eigvecs_returned, L_low_order_eigvecs_returned, correlation_mat_eigvals_returned, correlation_mat_eigvecs_returned) = self.my_DMD.compute_decomp( vec_handles, adv_vec_handles=adv_vec_handles, max_num_eigvals=max_num_eigvals) # Test that matrices were correctly computed. For build coeffs, check # column by column, as it is ok to be off by a negative sign. np.testing.assert_allclose(self.my_DMD.eigvals, eigvals_true, rtol=tol) self._helper_test_mat_to_sign(self.my_DMD.R_low_order_eigvecs, R_low_order_eigvecs_true, rtol=tol) self._helper_test_mat_to_sign(self.my_DMD.L_low_order_eigvecs, L_low_order_eigvecs_true, rtol=tol) np.testing.assert_allclose(self.my_DMD.correlation_mat_eigvals, correlation_mat_eigvals_true, rtol=tol) self._helper_test_mat_to_sign(self.my_DMD.correlation_mat_eigvecs, correlation_mat_eigvecs_true, rtol=tol) # Test that matrices were correctly returned np.testing.assert_allclose(eigvals_returned, eigvals_true, rtol=tol) self._helper_test_mat_to_sign(R_low_order_eigvecs_returned, R_low_order_eigvecs_true, rtol=tol) self._helper_test_mat_to_sign(L_low_order_eigvecs_returned, L_low_order_eigvecs_true, rtol=tol) np.testing.assert_allclose(correlation_mat_eigvals_returned, correlation_mat_eigvals_true, rtol=tol) self._helper_test_mat_to_sign(correlation_mat_eigvecs_returned, correlation_mat_eigvecs_true, rtol=tol)
def test_compute_decomp(self): """Test DMD decomposition (ritz values, build coefficients, mode norms)""" # Define an array of vectors, with corresponding handles vec_array = _parallel.call_and_bcast(np.random.random, ((self.num_states, self.num_vecs))) if _parallel.is_rank_zero(): for vec_index, handle in enumerate(self.vec_handles): handle.put(np.array(vec_array[:, vec_index]).squeeze()) # Compute DMD directly from data, for a sequential dataset ritz_vals, ritz_vecs, build_coeffs, mode_norms =\ self._helper_compute_DMD_from_data(vec_array[:, :-1], vec_array[:, 1:], util.InnerProductBlock(np.vdot)) # Check modred against direct computation, for a sequential dataset _parallel.barrier() self._helper_check_decomp(ritz_vals, build_coeffs, mode_norms, self.vec_handles) # np.w create more data, to check a non-sequential dataset adv_vec_array = _parallel.call_and_bcast(np.random.random, ((self.num_states, self.num_vecs))) if _parallel.is_rank_zero(): for vec_index, handle in enumerate(self.adv_vec_handles): handle.put(np.array(adv_vec_array[:, vec_index]).squeeze()) # Compute DMD directly from data, for a non-sequential dataset ritz_vals, ritz_vecs, build_coeffs, mode_norms =\ self._helper_compute_DMD_from_data(vec_array, adv_vec_array, util.InnerProductBlock(np.vdot)) # Check modred against direct computation, for a non-sequential dataset _parallel.barrier() self._helper_check_decomp(ritz_vals, build_coeffs, mode_norms, self.vec_handles, adv_vec_handles=self.adv_vec_handles) # Check that if mismatched sets of handles are passed in, an error is # raised. self.assertRaises(ValueError, self.my_DMD.compute_decomp, self.vec_handles, self.adv_vec_handles[:-1])
def test_compute_proj_coeffs(self): rtol = 1e-10 atol = 1e-13 # Sometimes fails if tol too high # Define an array of vectors, with corresponding handles vec_array = _parallel.call_and_bcast( np.random.random, ((self.num_states, self.num_vecs))) if _parallel.is_rank_zero(): for vec_index, handle in enumerate(self.vec_handles): handle.put(np.array(vec_array[:, vec_index]).squeeze()) # Check the spectral coefficient values. Sometimes the values in the # projections are not just scaled by -1 column-wise, but element-wise. # So just test that the projection coefficients differ by sign at most, # element-wise. _parallel.barrier() self.my_DMD.compute_decomp(self.vec_handles) proj_coeffs, adv_proj_coeffs = self.my_DMD.compute_proj_coeffs() adj_modes = self._helper_compute_DMD_from_data( vec_array, util.InnerProductBlock(np.vdot))[-1] proj_coeffs_true = np.dot(adj_modes.conj().T, vec_array[:, :-1]) adv_proj_coeffs_true = np.dot(adj_modes.conj().T, vec_array[:, 1:]) np.testing.assert_allclose(np.abs(proj_coeffs / proj_coeffs_true), np.ones(proj_coeffs.shape), rtol=rtol, atol=atol) np.testing.assert_allclose(np.abs(adv_proj_coeffs / adv_proj_coeffs_true), np.ones(adv_proj_coeffs.shape), rtol=rtol, atol=atol) # Create more data, to check a non-sequential dataset adv_vec_array = _parallel.call_and_bcast( np.random.random, ((self.num_states, self.num_vecs))) if _parallel.is_rank_zero(): for vec_index, handle in enumerate(self.adv_vec_handles): handle.put(np.array(adv_vec_array[:, vec_index]).squeeze()) # Check the spectral coefficient values. Sometimes the values in the # projections are not just scaled by -1 column-wise, but element-wise. # So just test that the projection coefficients differ by sign at most, # element-wise. _parallel.barrier() self.my_DMD.compute_decomp(self.vec_handles, adv_vec_handles=self.adv_vec_handles) adj_modes = self._helper_compute_DMD_from_data( vec_array, util.InnerProductBlock(np.vdot), adv_vec_array=adv_vec_array)[-1] proj_coeffs, adv_proj_coeffs = self.my_DMD.compute_proj_coeffs() proj_coeffs_true = np.dot(adj_modes.conj().T, vec_array) adv_proj_coeffs_true = np.dot(adj_modes.conj().T, adv_vec_array) np.testing.assert_allclose(np.abs(proj_coeffs / proj_coeffs_true), np.ones(proj_coeffs.shape), rtol=rtol, atol=atol) np.testing.assert_allclose(np.abs(adv_proj_coeffs / adv_proj_coeffs_true), np.ones(adv_proj_coeffs.shape), rtol=rtol, atol=atol)
def test_compute_modes(self): """Test building of modes.""" # Generate path names for saving modes to disk mode_path = join(self.test_dir, 'dmd_mode_%03d.pkl') ### SEQUENTIAL DATASET ### # Generate data seq_vec_array = _parallel.call_and_bcast( np.random.random, ((self.num_states, self.num_vecs))) if _parallel.is_rank_zero(): for vec_index, handle in enumerate(self.vec_handles): handle.put(np.array(seq_vec_array[:, vec_index]).squeeze()) # Compute DMD directly from data (modes_exact, modes_proj, spectral_coeffs, eigvals, R_low_order_eigvecs, L_low_order_eigvecs, correlation_mat_eigvals, correlation_mat_eigvecs) = self._helper_compute_DMD_from_data( seq_vec_array, util.InnerProductBlock(np.vdot))[:-1] # Set the build_coeffs attribute of an empty DMD object each time, so # that the modred computation uses the same coefficients as the direct # computation. _parallel.barrier() self.my_DMD.eigvals = eigvals self.my_DMD.R_low_order_eigvecs = R_low_order_eigvecs self.my_DMD.correlation_mat_eigvals = correlation_mat_eigvals self.my_DMD.correlation_mat_eigvecs = correlation_mat_eigvecs # Generate mode paths for saving modes to disk seq_mode_path_list = [mode_path % i for i in range(eigvals.size)] seq_mode_indices = range(len(seq_mode_path_list)) # Compute modes by passing in handles self.my_DMD.compute_exact_modes( seq_mode_indices, [V.VecHandlePickle(path) for path in seq_mode_path_list], adv_vec_handles=self.vec_handles[1:]) self._helper_check_modes(modes_exact, seq_mode_path_list) self.my_DMD.compute_proj_modes( seq_mode_indices, [V.VecHandlePickle(path) for path in seq_mode_path_list], vec_handles=self.vec_handles) self._helper_check_modes(modes_proj, seq_mode_path_list) # Compute modes without passing in handles, so first set full # sequential dataset as vec_handles. self.my_DMD.vec_handles = self.vec_handles self.my_DMD.compute_exact_modes( seq_mode_indices, [V.VecHandlePickle(path) for path in seq_mode_path_list]) self._helper_check_modes(modes_exact, seq_mode_path_list) self.my_DMD.compute_proj_modes( seq_mode_indices, [V.VecHandlePickle(path) for path in seq_mode_path_list]) self._helper_check_modes(modes_proj, seq_mode_path_list) # For exact modes, also compute by setting adv_vec_handles self.my_DMD.vec_handles = None self.my_DMD.adv_vec_handles = self.vec_handles[1:] self.my_DMD.compute_exact_modes( seq_mode_indices, [V.VecHandlePickle(path) for path in seq_mode_path_list]) self._helper_check_modes(modes_exact, seq_mode_path_list) ### NONSEQUENTIAL DATA ### # Generate data vec_array = _parallel.call_and_bcast( np.random.random, ((self.num_states, self.num_vecs))) adv_vec_array = _parallel.call_and_bcast( np.random.random, ((self.num_states, self.num_vecs))) if _parallel.is_rank_zero(): for vec_index, (handle, adv_handle) in enumerate( zip(self.vec_handles, self.adv_vec_handles)): handle.put(np.array(vec_array[:, vec_index]).squeeze()) adv_handle.put(np.array(adv_vec_array[:, vec_index]).squeeze()) # Compute DMD directly from data (modes_exact, modes_proj, spectral_coeffs, eigvals, R_low_order_eigvecs, L_low_order_eigvecs, correlation_mat_eigvals, correlation_mat_eigvecs) = self._helper_compute_DMD_from_data( vec_array, util.InnerProductBlock(np.vdot), adv_vec_array=adv_vec_array)[:-1] # Set the build_coeffs attribute of an empty DMD object each time, so # that the modred computation uses the same coefficients as the direct # computation. _parallel.barrier() self.my_DMD.eigvals = eigvals self.my_DMD.R_low_order_eigvecs = R_low_order_eigvecs self.my_DMD.correlation_mat_eigvals = correlation_mat_eigvals self.my_DMD.correlation_mat_eigvecs = correlation_mat_eigvecs # Generate mode paths for saving modes to disk mode_path_list = [mode_path % i for i in range(eigvals.size)] mode_indices = range(len(mode_path_list)) # Compute modes by passing in handles self.my_DMD.compute_exact_modes( mode_indices, [V.VecHandlePickle(path) for path in mode_path_list], adv_vec_handles=self.adv_vec_handles) self._helper_check_modes(modes_exact, mode_path_list) self.my_DMD.compute_proj_modes( mode_indices, [V.VecHandlePickle(path) for path in mode_path_list], vec_handles=self.vec_handles) self._helper_check_modes(modes_proj, mode_path_list) # Compute modes without passing in handles, so first set full # sequential dataset as vec_handles. self.my_DMD.vec_handles = self.vec_handles self.my_DMD.adv_vec_handles = self.adv_vec_handles self.my_DMD.compute_exact_modes( mode_indices, [V.VecHandlePickle(path) for path in mode_path_list]) self._helper_check_modes(modes_exact, mode_path_list) self.my_DMD.compute_proj_modes( mode_indices, [V.VecHandlePickle(path) for path in mode_path_list]) self._helper_check_modes(modes_proj, mode_path_list)