def _exact_pmf(system_type, pmf_bin_edges): if system_type == "symmetric": U0 = U0_sym elif system_type == "asymmetric": U0 = U0_asym else: raise ValueError("Unknown system_type: " + system_type) centers = bin_centers(pmf_bin_edges) exact_pmf = U0(centers) return centers, exact_pmf
def _exact_pmf(system_type, pmf_bin_edges): if system_type == "symmetric": U0 = U0_sym elif system_type == "asymmetric": U0 = U0_asym centers = bin_centers(pmf_bin_edges) exact_pmf = U0(centers) pmf = {} pmf["pmf_bin_edges"] = pmf_bin_edges pmf["pmf"] = exact_pmf return pmf
def uni_pmf(z_t, w_t, lambda_t, V, ks, bin_edges): """ unidirectional estimator for PMF :param z_t: ndarray with shape (N, number_of_steps) the (fluctuating) coordinate pulled by a protocol :param w_t: ndarray with shape (N, number_of_steps) works done in forward direction, in unit of kT :param lambda_t: ndarray of shape (number_of_steps) coordinate controlled by the pulling procedure :param V: a python function, pulling harmonic potential, e.g., 0.5 * ks * (z - lambda)**2 :param ks: float, harmonic force constant of V, the unit of ks is such that V is in unit of kT :param bin_edges: ndarray with shape ( nbins+1, ), same distance unit as zF_t and zR_t :return: (centers, pmf) centers : ndarray with shape (bin_edges.shape[0]-1 ), bin centers pmf : ndarray with shape (bin_edges.shape[0]-1 ) potential of mean force """ assert z_t.ndim == w_t.ndim == 2, "z_t and w_t must be 2d array" assert z_t.shape == w_t.shape, "z_t and w_t must have the same shape" assert lambda_t.ndim == 1, "lambda_t must be 1d array" assert z_t.shape[1] == lambda_t.shape[0], "z_t.shape[1], lambda_t.shape[0] must be the same" assert bin_edges.ndim == 1, "bin_edges must be 1d array" df_t = uni_df_t(w_t) N = z_t.shape[0] weights = np.exp(-w_t) / N histograms = hist_counts(z_t, bin_edges, weights) centers = bin_centers(bin_edges) denominator = np.exp( -V( centers[None,:], ks, lambda_t[:,None] ) + df_t[:,None] ) denominator = denominator.sum(axis=0) numerator = histograms * np.exp(df_t[:,None]) numerator = numerator.sum(axis=0) pmf = -np.log(numerator / denominator) return centers, pmf
xlimits=xlimits_fe, ylimits=ylimits_fe, lw=1.0, markersize=4, alpha=1., n_xtics=8, n_ytics=8) # plot pmf rmse start_pmf_ind = args.bin_ind_to_start_to_plot xs = [] ys = [] yerrs = [] for label in data_estimator_pairs: x = bin_centers(all_data[label]["pmfs"]["pmf_bin_edges"]) end_pmf_ind = len(x) - start_pmf_ind xs.append(x[start_pmf_ind:end_pmf_ind]) ys.append(pmf_rmse[label][start_pmf_ind:end_pmf_ind]) yerrs.append(pmf_rmse_std_error[label][start_pmf_ind:end_pmf_ind] / 2) if args.xlimits_pmf.lower() != "none": xlimits_pmf = [float(s) for s in args.xlimits_pmf.split()] else: xlimits_pmf = None if args.ylimits_pmf.lower() != "none": ylimits_pmf = [float(s) for s in args.ylimits_pmf.split()] else: ylimits_pmf = None
def sym_est_pmf_v2(z_t, w_t, lambda_t, V, ks, bin_edges, symmetrize_pmf): """ Version 2 of symmetric estimator for PMF The symmetric estimator of path ensemble average is <F> = (1/N) * sum{ ( F[x_n] + F[xr_n] * exp( -w_tau[x_n] ) ) / ( 1 + exp( -w_tau[x_n] ) ) } where xr_n is the time reversal of x_n Substitute the symmetric estimator (v2) of path ensemble average defined above into Eq. (9) in Minh and Adib, Phys. Rev. Lett. 100, 180602 (2008) :param z_t: np.ndarray with shape (N, number_of_steps), the (fluctuating) coordinate pulled by a symmetric protocol or in a symmetric :param w_t: np.ndarray with shape (N, number_of_steps), works done in forward direction, in unit of kT :param lambda_t: np.ndarray of shape (number_of_steps) coordinate controlled by the pulling procedure :param V: a python function, pulling harmonic potential e.g., 0.5 * ks * (z - lambda)**2 :param ks: float, harmonic force constant of V the unit of ks is such that V is in unit of kT :param bin_edges: np.ndarray with shape ( nbins+1, ) same distance unit as zF_t and zR_t :param symmetrize_pmf: bool should set to True when both system and protocol are symmetric :return: (centers, pmf) centers : np.ndarray with shape (bin_edges.shape[0]-1 ) bin centers pmf : np.ndarray with shape (bin_edges.shape[0]-1 ) potential of mean force """ assert z_t.ndim == w_t.ndim == 2, "z_t and w_t must be 2d array" assert z_t.shape == w_t.shape, "z_t and w_t must have the same shape" assert lambda_t.ndim == 1, "lambda_t must be 1d array" assert z_t.shape[1] == lambda_t.shape[0], "z_t.shape[1] and lambda_t.shape[0] must be the same" assert bin_edges.ndim == 1, "bin_edges must be 1d array" df_t = sym_est_df_t_v2(w_t) wTR_t = time_reversal_of_work(w_t) zTR_t = time_reversal_of_trajectory(z_t) if symmetrize_pmf: symm_center = (lambda_t[0] + lambda_t[-1])/2. zTR_t = center_reflection(zTR_t, symm_center) centers = bin_centers(bin_edges) outer_denominator = np.exp( -V( centers[None,:], ks, lambda_t[:,None] ) + df_t[:,None] ) outer_denominator = outer_denominator.sum(axis=0) w_tau = w_t[:, -1] N = w_tau.shape[0] weights_1 = np.exp(-w_t) / (1 + np.exp(-w_tau[:,None])) / N histograms_1 = hist_counts(z_t, bin_edges, weights_1) weights_2 = np.exp(-wTR_t) * np.exp(-w_tau[:,None] ) / (1 + np.exp(-w_tau[:,None])) / N histograms_2 = hist_counts(zTR_t, bin_edges, weights_2) numerator = (histograms_1 + histograms_2) * np.exp(df_t[:,None]) numerator = numerator.sum(axis=0) pmf = -np.log(numerator / outer_denominator) return centers, pmf
def bi_pmf(zF_t, wF_t, zR_t, wR_t, lambda_t, V, ks, bin_edges): """ Bidirectional estimator for PMF Implement the PMF which is resulted from substituting Eq (17) in Minh and Chodera into Eq (9) in Minh and Adib Refs: Minh and Abid, Optimized Free Energies from Bidirectional Single-Molecule Force Spectroscopy, Phys. Rev. Lett. 100, 180602 (2008) Minh and Chodera, Optimal estimators and asymptotic variances for nonequilibrium path-ensemble averages, J. Chem. Phys. 131, 134110 (2009) ----------------------- :param zF_t: ndarray with shape (NF, number_of_steps), the (fluctuating) coordinate pulled in forward direction :param wF_t: ndarray with shape (NF, number_of_steps), works done in forward direction, in unit of kT :param zR_t: ndarray with shape (NR, number_of_steps) the (fluctuating) coordinate pulled in reverse direction :param wR_t: ndarray with shape (NR, number_of_steps) the (fluctuating) coordinate pulled in reverse direction :param lambda_t: ndarray of shape (number_of_steps) coordinate controlled by the pulling procedure :param V: a python function, pulling harmonic potential e.g., 0.5 * ks * (z - lambda)**2 :param ks: float, harmonic force constant of V the unit of ks is such that V is in unit of kT :param bin_edges: ndarray with shape ( nbins+1, ) same distance unit as zF_t and zR_t :return: (centers, pmf) centers : ndarray with shape (bin_edges.shape[0]-1 ) bin centers pmf : ndarray with shape (bin_edges.shape[0]-1 ) potential of mean force """ assert zF_t.ndim == zR_t.ndim == wF_t.ndim == wR_t.ndim == 2, " zF_t, wF_t, zR_t, wR_t must be 2D array" assert zF_t.shape == wF_t.shape, "zF_t and wF_t must have the same shape" assert zR_t.shape == wR_t.shape, "zR_t and wR_t must have the same shape" assert lambda_t.ndim == 1, "lambda_t must be 1d array" assert zF_t.shape[1] == zR_t.shape[1] == lambda_t.shape[0], "zF_t.shape[1], zR_t.shape[1], lambda_t.shape[0] must be the same" assert bin_edges.ndim == 1, "bin_edges must be 1d array" df_t = bi_df_t(wF_t, wR_t) wR_t = time_reversal_of_work(wR_t) zR_t = time_reversal_of_trajectory(zR_t) NF = wF_t.shape[0] NR = wR_t.shape[0] wF_tau = wF_t[:, -1] wR_tau = wR_t[:, -1] dt_tau = df_t[-1] centers = bin_centers(bin_edges) denominator = np.exp(-V(centers[None, :], ks, lambda_t[:, None]) + df_t[:, None]) denominator = denominator.sum(axis=0) weights_F = np.exp(-wF_t) / (NF + NR * np.exp(dt_tau - wF_tau[:, None])) histograms_F = hist_counts(zF_t, bin_edges, weights_F) weights_R = np.exp(-wR_t) / (NF + NR * np.exp(dt_tau - wR_tau[:, None])) histograms_R = hist_counts(zR_t, bin_edges, weights_R) numerator = (histograms_F + histograms_R) * np.exp(df_t[:, None]) numerator = numerator.sum(axis=0) pmf = -np.log(numerator / denominator) return centers, pmf
print("Right replicate for", label) data = replicate_data_cuc7_da(data, args.system_type) # put first of fes to zero data = put_first_of_fe_to_zero(data) # put argmin of pmf to pmf_exact["pmf"] data = put_argmin_of_pmf_to_target(data, pmf_us["pmf"]) fe_x = data["free_energies"]["lambdas"] fe_ys = np.array(data["free_energies"]["main_estimates"].values()) fe_y = fe_ys.mean(axis=0) fe_error = fe_ys.std(axis=0) free_energies[label] = {"x": fe_x, "y": fe_y, "error": fe_error} pmf_x = bin_centers(data["pmfs"]["pmf_bin_edges"]) pmf_ys = np.array(data["pmfs"]["main_estimates"].values()) pmf_y = pmf_ys.mean(axis=0) pmf_error = pmf_ys.std(axis=0) pmfs[label] = {"x": pmf_x, "y": pmf_y, "error": pmf_error} # plot free energies xs = [] ys = [] yerrs = [] for label in data_estimator_pairs: x = free_energies[label]["x"] y = free_energies[label]["y"] yerr = free_energies[label]["error"] / 2 # error bars are one std