def test_load_and_save(self): params1 = np.zeros((2, 4)) weights1 = np.zeros((2, 4)) journal = Journal(0) journal.add_parameters(params1) journal.add_weights(weights1) journal.save('journal_tests_testfile.pkl') new_journal = Journal.fromFile('journal_tests_testfile.pkl') np.testing.assert_equal(journal.parameters, new_journal.parameters) np.testing.assert_equal(journal.weights, new_journal.weights)
def test_add_parameters(self): params1 = np.zeros((2, 4)) params2 = np.ones((2, 4)) # test whether production mode only stores the last set of parameters journal_prod = Journal(0) journal_prod.add_parameters(params1) journal_prod.add_parameters(params2) self.assertEqual(len(journal_prod.parameters), 1) np.testing.assert_equal(journal_prod.parameters[0], params2) # test whether reconstruction mode stores all parameter sets journal_recon = Journal(1) journal_recon.add_parameters(params1) journal_recon.add_parameters(params2) self.assertEqual(len(journal_recon.parameters), 2) np.testing.assert_equal(journal_recon.parameters[0], params1) np.testing.assert_equal(journal_recon.parameters[1], params2)
def sample(self, observations, steps, epsilon, n_samples=10000, n_samples_per_param=1, beta=2, delta=0.2, v=0.3, ar_cutoff=0.05, resample=None, n_update=None, adaptcov=1, full_output=0): """Samples from the posterior distribution of the model parameter given the observed data observations. Parameters ---------- observations : numpy.ndarray Observed data. steps : integer Number of maximum iterations in the sequential algoritm ("generations") epsilon : numpy.float An array of proposed values of epsilon to be used at each steps. n_samples : integer, optional Number of samples to generate. The default value is 10000. n_samples_per_param : integer, optional Number of data points in each simulated data set. The default value is 1. beta : numpy.float Tuning parameter of SABC delta : numpy.float Tuning parameter of SABC v : numpy.float, optional Tuning parameter of SABC, The default value is 0.3. ar_cutoff : numpy.float Acceptance ratio cutoff, The default value is 0.5 resample: int, optional Resample after this many acceptance, The default value if n_samples n_update: int, optional Number of perturbed parameters at each step, The default value if n_samples adaptcov : boolean, optional Whether we adapt the covariance matrix in iteration stage. The default value TRUE. full_output: integer, optional If full_output==1, intermediate results are included in output journal. The default value is 0, meaning the intermediate results are not saved. Returns ------- abcpy.output.Journal A journal containing simulation results, metadata and optionally intermediate results. """ journal = Journal(full_output) journal.configuration["type_model"] = type(self.model) journal.configuration["type_dist_func"] = type(self.distance) journal.configuration["type_kernel_func"] = type(self.kernel) journal.configuration["n_samples"] = n_samples journal.configuration["n_samples_per_param"] = n_samples_per_param journal.configuration["beta"] = beta journal.configuration["delta"] = delta journal.configuration["v"] = v journal.configuration["ar_cutoff"] = ar_cutoff journal.configuration["resample"] = resample journal.configuration["n_update"] = n_update journal.configuration["adaptcov"] = adaptcov journal.configuration["full_output"] = full_output accepted_parameters = np.zeros( shape=(n_samples, len(self.model.get_parameters()))) distances = np.zeros(shape=(n_samples, )) smooth_distances = np.zeros(shape=(n_samples, )) accepted_weights = np.ones(shape=(n_samples, 1)) all_distances = None accepted_cov_mat = None if resample == None: resample = n_samples if n_update == None: n_update = n_samples sample_array = np.ones(shape=(steps, )) sample_array[0] = n_samples sample_array[1:] = n_update ## Acceptance counter to determine the resampling step accept = 0 samples_until = 0 # Initialize variables that need to be available remotely rc = _RemoteContextSABCDiffusion(self.backend, self.model, self.distance, self.kernel, \ observations, n_samples, n_samples_per_param) for aStep in range(0, steps): # main SABC algorithm # print("INFO: Initialization of SABC") seed_arr = self.rng.randint(0, np.iinfo(np.uint32).max, size=int(sample_array[aStep]), dtype=np.uint32) seed_pds = self.backend.parallelize(seed_arr) # 0: update remotely required variables # print("INFO: Broadcasting parameters.") rc.epsilon = epsilon rc._update_broadcasts(self.backend, accepted_parameters, accepted_cov_mat, smooth_distances, all_distances) # 1: Calculate parameters # print("INFO: Initial accepted parameter parameters") params_and_dists_pds = self.backend.map(rc._accept_parameter, seed_pds) params_and_dists = self.backend.collect(params_and_dists_pds) new_parameters, new_distances, new_all_parameters, new_all_distances, index, acceptance = [ list(t) for t in zip(*params_and_dists) ] new_parameters = np.array(new_parameters) new_distances = np.array(new_distances) new_all_distances = np.concatenate(new_all_distances) index = np.array(index) acceptance = np.array(acceptance) # print('parallel stuff finsihed') # Reading all_distances at Initial step if aStep == 0: index = np.linspace(0, n_samples - 1, n_samples).astype(int).reshape( n_samples, ) accept = 0 all_distances = new_all_distances # print(index[acceptance == 1]) # Initialize/Update the accepted parameters and their corresponding distances accepted_parameters[index[acceptance == 1], :] = new_parameters[ acceptance == 1, :] distances[index[acceptance == 1]] = new_distances[acceptance == 1] # 2: Smoothing of the distances smooth_distances[index[acceptance == 1]] = self._smoother_distance( distances[index[acceptance == 1]], all_distances) # 3: Initialize/Update U, epsilon and covariance of perturbation kernel if aStep == 0: U = self._avergae_redefined_distance( self._smoother_distance(all_distances, all_distances), epsilon) else: U = np.mean(smooth_distances) epsilon = self._schedule(U, v) # if accepted_parameters.shape[1] > 1: # accepted_cov_mat = beta*np.cov(np.transpose(accepted_parameters)) + \ # 0.0001*np.trace(np.cov(np.transpose(accepted_parameters)))*np.eye(accepted_parameters.shape[1]) # else: accepted_cov_mat = beta* np.cov(np.transpose(accepted_parameters[:, [0, 1]])) + \ 0.0001 * (np.cov(np.transpose(accepted_parameters[:, [0, 1]]))) * np.eye(1) ## 4: Show progress and if acceptance rate smaller than a value break the iteration # print("INFO: Saving intermediate configuration to output journal.") if full_output == 1: journal.add_parameters(accepted_parameters) journal.add_weights(accepted_weights) if aStep > 0: accept = accept + np.sum(acceptance) samples_until = samples_until + sample_array[aStep] acceptance_rate = accept / samples_until print('updates: ', np.sum(sample_array[1:aStep + 1]) / np.sum(sample_array[1:]) * 100, ' epsilon: ', epsilon, \ 'u.mean: ', U, 'acceptance rate: ', acceptance_rate) if acceptance_rate < ar_cutoff: break # 5: Resampling if number of accepted particles greater than resample if accept >= resample and U > 1e-100: ## Weighted resampling: weight = np.exp(-smooth_distances * delta / U) weight = weight / sum(weight) index_resampled = self.rng.choice(np.arange(n_samples), n_samples, replace=1, p=weight) accepted_parameters = accepted_parameters[index_resampled, :] smooth_distances = smooth_distances[index_resampled] ## Update U and epsilon: epsilon = epsilon * (1 - delta) U = np.mean(smooth_distances) epsilon = self._schedule(U, v) ## Print effective sampling size print('Resampling: Effective sampling size: ', 1 / sum(pow(weight / sum(weight), 2))) accept = 0 samples_until = 0 # Add epsilon_arr, number of final steps and final output to the journal # print("INFO: Saving final configuration to output journal.") if full_output == 0: journal.add_parameters(accepted_parameters) journal.add_weights(accepted_weights) journal.configuration["steps"] = aStep + 1 journal.configuration["epsilon"] = epsilon return journal