def f_updateg_pw(g_coef_curr, g_basis, var1_curr, q1, q2, SSE_curr, propose_g_coef): g_coef_prop = propose_g_coef(g_coef_curr) tst = uf.f_exp1( uf.f_basistofunction(g_basis["x"], 0, g_coef_prop["prop"], g_basis)) while tst.min() < 0: g_coef_prop = propose_g_coef(g_coef_curr) tst = uf.f_exp1( uf.f_basistofunction(g_basis["x"], 0, g_coef_prop["prop"], g_basis)) if SSE_curr == 0: SSE_curr = f_SSEg_pw( uf.f_basistofunction(g_basis["x"], 0, g_coef_curr, g_basis), q1, q2) SSE_prop = f_SSEg_pw( uf.f_basistofunction(g_basis["x"], 0, g_coef_prop["prop"], g_basis), q1, q2) logl_curr = f_logl_pw( uf.f_basistofunction(g_basis["x"], 0, g_coef_curr, g_basis), q1, q2, var1_curr, SSE_curr) logl_prop = f_logl_pw( uf.f_basistofunction(g_basis["x"], 0, g_coef_prop["prop"], g_basis), q1, q2, var1_curr, SSE_prop) ratio = np.minimum(1, np.exp(logl_prop - logl_curr)) u = np.random.rand() if u <= ratio: g_coef = g_coef_prop["prop"] logl = logl_prop SSE = SSE_prop accept = True zpcnInd = g_coef_prop["ind"] if u > ratio: g_coef = g_coef_curr logl = logl_curr SSE = SSE_curr accept = False zpcnInd = g_coef_prop["ind"] return g_coef, logl, SSE, accept, zpcnInd
def f_dlogl_pw(v_coef, v_basis, d_basis, sigma_curr, q1, q2): vec = uf.f_basistofunction(v_basis["x"], 0, v_coef, v_basis) psi = uf.f_exp1(vec) N = q1.shape[0] obs_domain = np.linspace(0, 1, N) binsize = np.diff(obs_domain) binsize = binsize.mean() gamma = uf.f_phiinv(psi) q2_warp = uf.warp_q_gamma(obs_domain, q2, gamma) q2_warp_grad = np.gradient(q2_warp, binsize) basismat = d_basis["matrix"] g = np.zeros(N) for i in range(0, basismat.shape[1]): ubar = cumtrapz(basismat[:, i], obs_domain, initial=0) integrand = (q1 - q2_warp) * (-2 * q2_warp_grad * ubar - q2_warp * basismat[:, i]) tmp = 1 / sigma_curr * trapz(integrand, obs_domain) g += tmp * basismat[:, i] out, SSEv = f_vpostlogl_pw(vec, q1, q2, sigma_curr, 0) nll = -1 * out g_coef = v_basis["matrix"].T @ g return nll, g_coef, SSEv
def f_SSEg_pw(g, q1, q2): obs_domain = np.linspace(0, 1, g.shape[0]) exp1g_temp = uf.f_predictfunction(uf.f_exp1(g), obs_domain, 0) pt = np.insert(bay.bcuL2norm2(obs_domain, exp1g_temp), 0, 0) tmp = uf.f_predictfunction(q2, pt, 0) vec = (q1 - tmp * exp1g_temp)**2 out = vec.sum() return (out)
def f_warp_pw(v, q1, q2): obs_domain = np.linspace(0, 1, v.shape[0]) exp1g_temp = uf.f_predictfunction(uf.f_exp1(v), obs_domain, 0) pt = np.insert(bay.bcuL2norm2(obs_domain, exp1g_temp), 0, 0) tmp = uf.f_predictfunction(q2, pt, 0) out = tmp * exp1g_temp return (out)
def pairwise_align_bayes(f1i, f2i, time, mcmcopts=None): """ This function aligns two functions using Bayesian framework. It will align f2 to f1. It is based on mapping warping functions to a hypersphere, and a subsequent exponential mapping to a tangent space. In the tangent space, the Z-mixture pCN algorithm is used to explore both local and global structure in the posterior distribution. The Z-mixture pCN algorithm uses a mixture distribution for the proposal distribution, controlled by input parameter zpcn. The zpcn$betas must be between 0 and 1, and are the coefficients of the mixture components, with larger coefficients corresponding to larger shifts in parameter space. The zpcn["probs"] give the probability of each shift size. Usage: out = pairwise_align_bayes(f1i, f2i, time) out = pairwise_align_bayes(f1i, f2i, time, mcmcopts) :param f1i: vector defining M samples of function 1 :param f2i: vector defining M samples of function 2 :param time: time vector of length M :param mcmopts: dict of mcmc parameters :type mcmcopts: dict default mcmc options: tmp = {"betas":np.array([0.5,0.5,0.005,0.0001]),"probs":np.array([0.1,0.1,0.7,0.1])} mcmcopts = {"iter":2*(10**4) ,"burnin":np.minimum(5*(10**3),2*(10**4)//2), "alpha0":0.1, "beta0":0.1,"zpcn":tmp,"propvar":1, "initcoef":np.repeat(0,20), "npoints":200, "extrainfo":True} :rtype collection containing :return f2_warped: aligned f2 :return gamma: warping function :return g_coef: final g_coef :return psi: final psi :return sigma1: final sigma if extrainfo :return accept: accept of psi samples :return betas_ind :return logl: log likelihood :return gamma_mat: posterior gammas :return gamma_stats: posterior gamma stats :return xdist: phase distance posterior :return ydist: amplitude distance posterior) """ if mcmcopts is None: tmp = { "betas": np.array([0.5, 0.5, 0.005, 0.0001]), "probs": np.array([0.1, 0.1, 0.7, 0.1]) } mcmcopts = { "iter": 2 * (10**4), "burnin": np.minimum(5 * (10**3), 2 * (10**4) // 2), "alpha0": 0.1, "beta0": 0.1, "zpcn": tmp, "propvar": 1, "initcoef": np.repeat(0, 20), "npoints": 200, "extrainfo": True } if f1i.shape[0] != f2i.shape[0]: raise Exception('Length of f1 and f2 must be equal') if f1i.shape[0] != time.shape[0]: raise Exception('Length of f1 and time must be equal') if mcmcopts["zpcn"]["betas"].shape[0] != mcmcopts["zpcn"]["probs"].shape[0]: raise Exception('In zpcn, betas must equal length of probs') if np.mod(mcmcopts["initcoef"].shape[0], 2) != 0: raise Exception('Length of mcmcopts.initcoef must be even') # Number of sig figs to report in gamma_mat SIG_GAM = 13 iter = mcmcopts["iter"] # parameter settings pw_sim_global_burnin = mcmcopts["burnin"] valid_index = np.arange(pw_sim_global_burnin - 1, iter) pw_sim_global_Mg = mcmcopts["initcoef"].shape[0] // 2 g_coef_ini = mcmcopts["initcoef"] numSimPoints = mcmcopts["npoints"] pw_sim_global_domain_par = np.linspace(0, 1, numSimPoints) g_basis = uf.basis_fourier(pw_sim_global_domain_par, pw_sim_global_Mg, 1) sigma1_ini = 1 zpcn = mcmcopts["zpcn"] pw_sim_global_sigma_g = mcmcopts["propvar"] def propose_g_coef(g_coef_curr): pCN_beta = zpcn["betas"] pCN_prob = zpcn["probs"] probm = np.insert(np.cumsum(pCN_prob), 0, 0) z = np.random.rand() result = {"prop": g_coef_curr, "ind": 1} for i in range(0, pCN_beta.shape[0]): if z <= probm[i + 1] and z > probm[i]: g_coef_new = normal( 0, pw_sim_global_sigma_g / np.repeat(np.arange(1, pw_sim_global_Mg + 1), 2)) result["prop"] = np.sqrt( 1 - pCN_beta[i]**2) * g_coef_curr + pCN_beta[i] * g_coef_new result["ind"] = i return result # normalize time to [0,1] time = (time - time.min()) / (time.max() - time.min()) timet = np.linspace(0, 1, numSimPoints) f1 = uf.f_predictfunction(f1i, timet, 0) f2 = uf.f_predictfunction(f2i, timet, 0) # srsf transformation q1 = uf.f_to_srsf(f1, timet) q1i = uf.f_to_srsf(f1i, time) q2 = uf.f_to_srsf(f2, timet) tmp = uf.f_exp1(uf.f_basistofunction(g_basis["x"], 0, g_coef_ini, g_basis)) if tmp.min() < 0: raise Exception("Invalid initial value of g") # result vectors g_coef = np.zeros((iter, g_coef_ini.shape[0])) sigma1 = np.zeros(iter) logl = np.zeros(iter) SSE = np.zeros(iter) accept = np.zeros(iter, dtype=bool) accept_betas = np.zeros(iter) # init g_coef_curr = g_coef_ini sigma1_curr = sigma1_ini SSE_curr = f_SSEg_pw( uf.f_basistofunction(g_basis["x"], 0, g_coef_ini, g_basis), q1, q2) logl_curr = f_logl_pw( uf.f_basistofunction(g_basis["x"], 0, g_coef_ini, g_basis), q1, q2, sigma1_ini**2, SSE_curr) g_coef[0, :] = g_coef_ini sigma1[0] = sigma1_ini SSE[0] = SSE_curr logl[0] = logl_curr # update the chain for iter-1 times for m in tqdm(range(1, iter)): # update g g_coef_curr, tmp, SSE_curr, accepti, zpcnInd = f_updateg_pw( g_coef_curr, g_basis, sigma1_curr**2, q1, q2, SSE_curr, propose_g_coef) # update sigma1 newshape = q1.shape[0] / 2 + mcmcopts["alpha0"] newscale = 1 / 2 * SSE_curr + mcmcopts["beta0"] sigma1_curr = np.sqrt(1 / np.random.gamma(newshape, 1 / newscale)) logl_curr = f_logl_pw( uf.f_basistofunction(g_basis["x"], 0, g_coef_curr, g_basis), q1, q2, sigma1_curr**2, SSE_curr) # save updates to results g_coef[m, :] = g_coef_curr sigma1[m] = sigma1_curr SSE[m] = SSE_curr if mcmcopts["extrainfo"]: logl[m] = logl_curr accept[m] = accepti accept_betas[m] = zpcnInd # calculate posterior mean of psi pw_sim_est_psi_matrix = np.zeros((numSimPoints, valid_index.shape[0])) for k in range(0, valid_index.shape[0]): g_temp = uf.f_basistofunction(g_basis["x"], 0, g_coef[valid_index[k], :], g_basis) psi_temp = uf.f_exp1(g_temp) pw_sim_est_psi_matrix[:, k] = psi_temp result_posterior_psi_simDomain = uf.f_psimean(pw_sim_global_domain_par, pw_sim_est_psi_matrix) # resample to same number of points as the input f1 and f2 interp = interp1d(np.linspace(0, 1, result_posterior_psi_simDomain.shape[0]), result_posterior_psi_simDomain, fill_value="extrapolate") result_posterior_psi = interp(np.linspace(0, 1, f1i.shape[0])) # transform posterior mean of psi to gamma result_posterior_gamma = uf.f_phiinv(result_posterior_psi) result_posterior_gamma = uf.norm_gam(result_posterior_gamma) # warped f2 f2_warped = uf.warp_f_gamma(time, f2i, result_posterior_gamma) if mcmcopts["extrainfo"]: M, N = pw_sim_est_psi_matrix.shape gamma_mat = np.zeros((time.shape[0], N)) one_v = np.ones(M) Dx = np.zeros(N) Dy = Dx for ii in range(0, N): interp = interp1d(np.linspace( 0, 1, result_posterior_psi_simDomain.shape[0]), pw_sim_est_psi_matrix[:, ii], fill_value="extrapolate") result_i = interp(time) tmp = uf.f_phiinv(result_i) gamma_mat[:, ii] = uf.norm_gam(tmp) v, theta = geo.inv_exp_map(one_v, pw_sim_est_psi_matrix[:, ii]) Dx[ii] = np.sqrt(trapz(v**2, pw_sim_global_domain_par)) q2warp = uf.warp_q_gamma(pw_sim_global_domain_par, q2, gamma_mat[:, ii]) Dy[ii] = np.sqrt(trapz((q1i - q2warp)**2, time)) gamma_stats = uf.statsFun(gamma_mat) results_o = collections.namedtuple('align_bayes', [ 'f2_warped', 'gamma', 'g_coef', 'psi', 'sigma1', 'accept', 'betas_ind', 'logl', 'gamma_mat', 'gamma_stats', 'xdist', 'ydist' ]) out = results_o(f2_warped, result_posterior_gamma, g_coef, result_posterior_psi, sigma1, accept[1:], accept_betas[1:], logl, gamma_mat, gamma_stats, Dx, Dy) return (out)