def inverse_exp_coord(beta1, beta2): """ Calculate the inverse exponential to obtain a shooting vector from beta1 to beta2 in shape space of open curves :param beta1: numpy ndarray of shape (2,M) of M samples :param beta2: numpy ndarray of shape (2,M) of M samples :rtype: numpy ndarray :return v: shooting vectors :return dist: distance """ T = beta1.shape[1] centroid1 = calculatecentroid(beta1) beta1 = beta1 - tile(centroid1, [T, 1]).T centroid2 = calculatecentroid(beta2) beta2 = beta2 - tile(centroid2, [T, 1]).T q1 = curve_to_q(beta1) # Iteratively optimize over SO(n) x Gamma for i in range(0, 1): # Optimize over SO(n) beta2, O_hat, tau = find_rotation_and_seed_coord(beta1, beta2) q2 = curve_to_q(beta2) # Optimize over Gamma gam = optimum_reparam_curve(q2, q1, 0.0) gamI = uf.invertGamma(gam) # Applying optimal re-parameterization to the second curve beta2 = group_action_by_gamma_coord(beta2, gamI) # Optimize over SO(n) beta2, O_hat, tau = find_rotation_and_seed_coord(beta1, beta2) q2n = curve_to_q(beta2) # Compute geodesic distance q1dotq2 = innerprod_q2(q1, q2n) dist = arccos(q1dotq2) # Compute shooting vector if q1dotq2 > 1: q1dotq2 = 1 u = q2n - q1dotq2 * q1 normu = sqrt(innerprod_q2(u, u)) if normu > 1e-4: v = u * arccos(q1dotq2) / normu else: v = zeros((2, T)) return (v, dist)
def find_C(C, qn, vec, q0, m, mu_psi): qhat, gamhat, a, U, s, mu_g = jointfPCAd(qn, vec, C, m, mu_psi) (M,N) = qn.shape time = np.linspace(0,1,M-1) d = np.zeros(N) for i in range(0,N): tmp = uf.warp_q_gamma(time, qhat[0:(M-1),i], uf.invertGamma(gamhat[:,i])) d[i] = trapz((tmp-q0[:,i])*(tmp-q0[:,i]), time) out = sum(d*d)/N return out
def oc_srvf_align(beta, mode='O'): """ This claculates the mean of a set of curves and aligns them :param beta: numpy ndarray of shape (n, M, N) describing N curves in R^M :param mode: Open ('O') or closed curve ('C') (default 'O') :rtype: tuple of numpy array :return betan: aligned curves :return qn: aligned srvf :return betamean: mean curve :return mu: mean srvf """ n, T, N = beta.shape # find mean mu, betamean, v, q = curve_karcher_mean(beta, mode=mode) qn = zeros((n, T, N)) betan = zeros((n, T, N)) centroid2 = cf.calculatecentroid(betamean) betamean = betamean - tile(centroid2, [T, 1]).T q_mu = cf.curve_to_q(betamean) # align to mean for ii in range(0, N): beta1 = beta[:, :, ii] centroid1 = cf.calculatecentroid(beta1) beta1 = beta1 - tile(centroid1, [T, 1]).T # Iteratively optimize over SO(n) x Gamma for i in range(0, 1): # Optimize over SO(n) beta1, O_hat, tau = cf.find_rotation_and_seed_coord(betamean, beta1) q1 = cf.curve_to_q(beta1) # Optimize over Gamma gam = cf.optimum_reparam_curve(q1, q_mu, 0.0) gamI = uf.invertGamma(gam) # Applying optimal re-parameterization to the second curve beta1 = cf.group_action_by_gamma_coord(beta1, gamI) # Optimize over SO(n) beta1, O_hat, tau = cf.find_rotation_and_seed_coord(betamean, beta1) qn[:, :, ii] = cf.curve_to_q(beta1) betan[:, :, ii] = beta1 align_results = collections.namedtuple('align', ['betan', 'qn', 'betamean', 'mu']) out = align_results(betan, qn, betamean, q_mu) return out
def geod_sphere(beta1, beta2, k=5): """ This function caluclates the geodecis between open curves beta1 and beta2 with k steps along path :param beta1: numpy ndarray of shape (2,M) of M samples :param beta2: numpy ndarray of shape (2,M) of M samples :param k: number of samples along path (Default = 5) :rtype: numpy ndarray :return dist: geodesic distance :return path: geodesic path :return O: rotation matrix """ lam = 0.0 elastic = 1 rotation = 1 returnpath = 1 n, T = beta1.shape beta1 = cf.resamplecurve(beta1, T) beta2 = cf.resamplecurve(beta2, T) centroid1 = cf.calculatecentroid(beta1) beta1 = beta1 - tile(centroid1, [T, 1]).T centroid2 = cf.calculatecentroid(beta2) beta2 = beta2 - tile(centroid2, [T, 1]).T q1 = cf.curve_to_q(beta1) if rotation: beta2, O1, tau = cf.find_rotation_and_seed_coord(beta1, beta2) q2 = cf.curve_to_q(beta2) else: O1 = eye(2) q2 = cf.curve_to_q(beta2) if elastic: # Find the optimal coorespondence gam = cf.optimum_reparam_curve(q2, q1, lam) gamI = uf.invertGamma(gam) # Applying optimal re-parameterization to the second curve beta2n = cf.group_action_by_gamma_coord(beta2, gamI) q2n = cf.curve_to_q(beta2n) if rotation: beta2n, O2, tau = cf.find_rotation_and_seed_coord(beta1, beta2n) centroid2 = cf.calculatecentroid(beta2n) beta2n = beta2n - tile(centroid2, [T, 1]).T q2n = cf.curve_to_q(beta2n) O = O1.dot(O2) else: q2n = q2 O = O1 # Forming geodesic between the registered curves dist = arccos(cf.innerprod_q2(q1, q2n)) if returnpath: PsiQ = zeros((n, T, k)) PsiX = zeros((n, T, k)) for tau in range(0, k): s = dist * tau / (k - 1.) PsiQ[:, :, tau] = (sin(dist-s)*q1+sin(s)*q2n)/sin(dist) PsiX[:, :, tau] = cf.q_to_curve(PsiQ[:, :, tau]) path = PsiQ else: path = 0 return(dist, path, O)
def init_path_rand(beta1, beta_mid, beta2, T=100, k=5): """ Initializes a path in \cal{C}. beta1, beta_mid beta2 are already standardized curves. Creates a path from beta1 to beta_mid to beta2 in shape space, then projects to the closed shape manifold. :param beta1: numpy ndarray of shape (2,M) of M samples (first curve) :param betamid: numpy ndarray of shape (2,M) of M samples (mid curve) :param beta2: numpy ndarray of shape (2,M) of M samples (end curve) :param T: Number of samples of curve (Default = 100) :param k: number of samples along path (Default = 5) :rtype: numpy ndarray :return alpha: a path between two q-functions :return beta: a path between two curves :return O: rotation matrix """ alpha = zeros((2, T, k)) beta = zeros((2, T, k)) q1 = cf.curve_to_q(beta1) q_mid = cf.curve_to_q(beta_mid) # find optimal rotation of q2 beta2, O1, tau1 = cf.find_rotation_and_seed_coord(beta1, beta2) q2 = cf.curve_to_q(beta2) # find the optimal coorespondence gam = cf.optimum_reparam_curve(q2, q1) gamI = uf.invertGamma(gam) # apply optimal reparametrization beta2n = cf.group_action_by_gamma_coord(beta2, gamI) # find optimal rotation of q2 beta2n, O2, tau1 = cf.find_rotation_and_seed_coord(beta1, beta2n) centroid2 = cf.calculatecentroid(beta2n) beta2n = beta2n - tile(centroid2, [T, 1]).T q2n = cf.curve_to_q(beta2n) O = O1.dot(O2) # Initialize a path as a geodesic through q1 --- q_mid --- q2 theta1 = arccos(cf.innerprod_q2(q1, q_mid)) theta2 = arccos(cf.innerprod_q2(q_mid, q2n)) tmp = arange(2, int((k - 1) / 2) + 1) t = zeros(tmp.size) alpha[:, :, 0] = q1 beta[:, :, 0] = beta1 i = 0 for tau in range(2, int((k - 1) / 2) + 1): t[i] = (tau - 1.0) / ((k - 1) / 2.0) qnew = (1 / sin(theta1)) * (sin((1 - t[i]) * theta1) * q1 + sin(t[i] * theta1) * q_mid) alpha[:, :, tau - 1] = cf.project_curve(qnew) x = cf.q_to_curve(alpha[:, :, tau - 1]) a = -1 * cf.calculatecentroid(x) beta[:, :, tau - 1] = x + tile(a, [T, 1]).T i += 1 alpha[:, :, int((k - 1) / 2)] = q_mid beta[:, :, int((k - 1) / 2)] = beta_mid i = 0 for tau in range(int((k - 1) / 2) + 1, k - 1): qnew = (1 / sin(theta2)) * (sin((1 - t[i]) * theta2) * q_mid + sin(t[i] * theta2) * q2n) alpha[:, :, tau] = cf.project_curve(qnew) x = cf.q_to_curve(alpha[:, :, tau]) a = -1 * cf.calculatecentroid(x) beta[:, :, tau] = x + tile(a, [T, 1]).T i += 1 alpha[:, :, k - 1] = q2n beta[:, :, k - 1] = beta2n return (alpha, beta, O)
def geod_sphere(beta1, beta2, k=5): """ This function caluclates the geodecis between open curves beta1 and beta2 with k steps along path :param beta1: numpy ndarray of shape (2,M) of M samples :param beta2: numpy ndarray of shape (2,M) of M samples :param k: number of samples along path (Default = 5) :rtype: numpy ndarray :return dist: geodesic distance :return path: geodesic path :return O: rotation matrix """ lam = 0.0 elastic = 1 rotation = 1 returnpath = 1 n, T = beta1.shape beta1 = cf.resamplecurve(beta1, T) beta2 = cf.resamplecurve(beta2, T) centroid1 = cf.calculatecentroid(beta1) beta1 = beta1 - tile(centroid1, [T, 1]).T centroid2 = cf.calculatecentroid(beta2) beta2 = beta2 - tile(centroid2, [T, 1]).T q1 = cf.curve_to_q(beta1) if rotation: beta2, O1, tau = cf.find_rotation_and_seed_coord(beta1, beta2) q2 = cf.curve_to_q(beta2) else: O1 = eye(2) q2 = cf.curve_to_q(beta2) if elastic: # Find the optimal coorespondence gam = cf.optimum_reparam_curve(q2, q1, lam) gamI = uf.invertGamma(gam) # Applying optimal re-parameterization to the second curve beta2n = cf.group_action_by_gamma_coord(beta2, gamI) q2n = cf.curve_to_q(beta2n) if rotation: beta2n, O2, tau = cf.find_rotation_and_seed_coord(beta1, beta2n) centroid2 = cf.calculatecentroid(beta2n) beta2n = beta2n - tile(centroid2, [T, 1]).T q2n = cf.curve_to_q(beta2n) O = O1.dot(O2) else: q2n = q2 O = O1 # Forming geodesic between the registered curves dist = arccos(cf.innerprod_q2(q1, q2n)) if returnpath: PsiQ = zeros((n, T, k)) PsiX = zeros((n, T, k)) for tau in range(0, k): s = dist * tau / (k - 1.0) PsiQ[:, :, tau] = (sin(dist - s) * q1 + sin(s) * q2n) / sin(dist) PsiX[:, :, tau] = cf.q_to_curve(PsiQ[:, :, tau]) path = PsiQ else: path = 0 return (dist, path, O)
def find_C_sub(time, qhat, gamhat, q0): tmp = uf.warp_q_gamma(time, qhat, uf.invertGamma(gamhat)) d = trapz((tmp - q0) * (tmp - q0), time) return d
def gauss_model(fn, time, qn, gam, n=1, sort_samples=False): """ This function models the functional data using a Gaussian model extracted from the principal components of the srvfs :param fn: numpy ndarray of shape (M,N) of N aligned functions with M samples :param time: vector of size M describing the sample points :param qn: numpy ndarray of shape (M,N) of N aligned srvfs with M samples :param gam: warping functions :param n: number of random samples :param sort_samples: sort samples (default = T) :type n: integer :type sort_samples: bool :type fn: np.ndarray :type qn: np.ndarray :type gam: np.ndarray :type time: np.ndarray :rtype: tuple of numpy array :return fs: random aligned samples :return gams: random warping functions :return ft: random samples """ # Parameters eps = np.finfo(np.double).eps binsize = np.diff(time) binsize = binsize.mean() M = time.size # compute mean and covariance in q-domain mq_new = qn.mean(axis=1) mididx = np.round(time.shape[0] / 2) m_new = np.sign(fn[mididx, :]) * np.sqrt(np.abs(fn[mididx, :])) mqn = np.append(mq_new, m_new.mean()) qn2 = np.vstack((qn, m_new)) C = np.cov(qn2) q_s = np.random.multivariate_normal(mqn, C, n) q_s = q_s.transpose() # compute the correspondence to the original function domain fs = np.zeros((M, n)) for k in range(0, n): fs[:, k] = uf.cumtrapzmid(time, q_s[0:M, k] * np.abs(q_s[0:M, k]), np.sign(q_s[M, k]) * (q_s[M, k] ** 2), mididx) fbar = fn.mean(axis=1) fsbar = fs.mean(axis=1) err = np.transpose(np.tile(fbar-fsbar, (n,1))) fs += err # random warping generation rgam = uf.randomGamma(gam, n) gams = np.zeros((M, n)) for k in range(0, n): gams[:, k] = uf.invertGamma(rgam[:, k]) # sort functions and warping if sort_samples: mx = fs.max(axis=0) seq1 = mx.argsort() # compute the psi-function fy = np.gradient(rgam, binsize) psi = fy / np.sqrt(abs(fy) + eps) ip = np.zeros(n) len = np.zeros(n) for i in range(0, n): tmp = np.ones(M) ip[i] = tmp.dot(psi[:, i] / M) len[i] = np.acos(tmp.dot(psi[:, i] / M)) seq2 = len.argsort() # combine x-variability and y-variability ft = np.zeros((M, n)) for k in range(0, n): ft[:, k] = np.interp(gams[:, seq2[k]], np.arange(0, M) / np.double(M - 1), fs[:, seq1[k]]) tmp = np.isnan(ft[:, k]) while tmp.any(): rgam2 = uf.randomGamma(gam, 1) ft[:, k] = np.interp(gams[:, seq2[k]], np.arange(0, M) / np.double(M - 1), uf.invertGamma(rgam2)) else: # combine x-variability and y-variability ft = np.zeros((M, n)) for k in range(0, n): ft[:, k] = np.interp(gams[:, k], np.arange(0, M) / np.double(M - 1), fs[:, k]) tmp = np.isnan(ft[:, k]) while tmp.any(): rgam2 = uf.randomGamma(gam, 1) ft[:, k] = np.interp(gams[:, k], np.arange(0, M) / np.double(M - 1), uf.invertGamma(rgam2)) samples = collections.namedtuple('samples', ['fs', 'gams', 'ft']) out = samples(fs, rgam, ft) return out
def init_path_rand(beta1, beta_mid, beta2, T=100, k=5): r""" Initializes a path in :math:`\cal{C}`. beta1, beta_mid beta2 are already standardized curves. Creates a path from beta1 to beta_mid to beta2 in shape space, then projects to the closed shape manifold. :param beta1: numpy ndarray of shape (2,M) of M samples (first curve) :param betamid: numpy ndarray of shape (2,M) of M samples (mid curve) :param beta2: numpy ndarray of shape (2,M) of M samples (end curve) :param T: Number of samples of curve (Default = 100) :param k: number of samples along path (Default = 5) :rtype: numpy ndarray :return alpha: a path between two q-functions :return beta: a path between two curves :return O: rotation matrix """ alpha = zeros((2, T, k)) beta = zeros((2, T, k)) q1 = cf.curve_to_q(beta1)[0] q_mid = cf.curve_to_q(beta_mid)[0] # find optimal rotation of q2 beta2, O1, tau1 = cf.find_rotation_and_seed_coord(beta1, beta2) q2 = cf.curve_to_q(beta2)[0] # find the optimal coorespondence gam = cf.optimum_reparam_curve(q2, q1) gamI = uf.invertGamma(gam) # apply optimal reparametrization beta2n = cf.group_action_by_gamma_coord(beta2, gamI) # find optimal rotation of q2 beta2n, O2, tau1 = cf.find_rotation_and_seed_coord(beta1, beta2n) centroid2 = cf.calculatecentroid(beta2n) beta2n = beta2n - tile(centroid2, [T, 1]).T q2n = cf.curve_to_q(beta2n)[0] O = O1 @ O2 # Initialize a path as a geodesic through q1 --- q_mid --- q2 theta1 = arccos(cf.innerprod_q2(q1, q_mid)) theta2 = arccos(cf.innerprod_q2(q_mid, q2n)) tmp = arange(2, int((k - 1) / 2) + 1) t = zeros(tmp.size) alpha[:, :, 0] = q1 beta[:, :, 0] = beta1 i = 0 for tau in range(2, int((k - 1) / 2) + 1): t[i] = (tau - 1.) / ((k - 1) / 2.) qnew = (1 / sin(theta1)) * (sin( (1 - t[i]) * theta1) * q1 + sin(t[i] * theta1) * q_mid) alpha[:, :, tau - 1] = cf.project_curve(qnew) x = cf.q_to_curve(alpha[:, :, tau - 1]) a = -1 * cf.calculatecentroid(x) beta[:, :, tau - 1] = x + tile(a, [T, 1]).T i += 1 alpha[:, :, int((k - 1) / 2)] = q_mid beta[:, :, int((k - 1) / 2)] = beta_mid i = 0 for tau in range(int((k - 1) / 2) + 1, k - 1): qnew = (1 / sin(theta2)) * (sin( (1 - t[i]) * theta2) * q_mid + sin(t[i] * theta2) * q2n) alpha[:, :, tau] = cf.project_curve(qnew) x = cf.q_to_curve(alpha[:, :, tau]) a = -1 * cf.calculatecentroid(x) beta[:, :, tau] = x + tile(a, [T, 1]).T i += 1 alpha[:, :, k - 1] = q2n beta[:, :, k - 1] = beta2n return (alpha, beta, O)
def find_rotation_and_seed_q(q1, q2, closed=0, rotation=True, method="DP"): """ This function returns a candidate list of optimally oriented and registered (seed) srvs w.r.t. q1 :param q1: numpy ndarray of shape (2,M) of M samples :param q2: numpy ndarray of shape (2,M) of M samples :param closed: Open (0) or Closed (1) :param rotation: find rotation (default=True) :param method: method to apply optimization (default="DP") options are "DP" or "RBFGS" :rtype: numpy ndarray :return q2best: optimal aligned q2 to q1 :return Rbest: rotation matrix :return gamIbest: warping function """ n, T = q1.shape scl = 4. minE = 4000 if closed == 1: end_idx = int(floor(T/scl)) scl = 4 else: end_idx = 0 for ctr in range(0, end_idx+1): if closed == 1: q2n = shift_f(q2, scl*ctr) else: q2n = q2 if rotation: q2new, R = find_best_rotation(q1, q2n) else: q2new = q2n.copy() R = eye(n) # Reparam if norm(q1-q2new,'fro') > 0.0001: gam = optimum_reparam_curve(q2new, q1, 0.0, method) gamI = uf.invertGamma(gam) q2new = group_action_by_gamma(q2new,gamI) if closed == 1: q2new = project_curve(q2new) else: gamI = linspace(0,1,T) tmp = innerprod_q2(q1,q2new) if tmp > 1: tmp = 1 if tmp < -1: tmp = -1 Ec = arccos(tmp) if Ec < minE: Rbest = R q2best = q2new gamIbest = gamI minE = Ec return (q2best, Rbest, gamIbest)
def gauss_model(fn, time, qn, gam, n=1, sort_samples=False): """ This function models the functional data using a Gaussian model extracted from the principal components of the srvfs :param fn: numpy ndarray of shape (M,N) of N aligned functions with M samples :param time: vector of size M describing the sample points :param qn: numpy ndarray of shape (M,N) of N aligned srvfs with M samples :param gam: warping functions :param n: number of random samples :param sort_samples: sort samples (default = T) :type n: integer :type sort_samples: bool :type fn: np.ndarray :type qn: np.ndarray :type gam: np.ndarray :type time: np.ndarray :rtype: tuple of numpy array :return fs: random aligned samples :return gams: random warping functions :return ft: random samples """ # Parameters eps = np.finfo(np.double).eps binsize = np.diff(time) binsize = binsize.mean() M = time.size # compute mean and covariance in q-domain mq_new = qn.mean(axis=1) mididx = np.round(time.shape[0] / 2) m_new = np.sign(fn[mididx, :]) * np.sqrt(np.abs(fn[mididx, :])) mqn = np.append(mq_new, m_new.mean()) qn2 = np.vstack((qn, m_new)) C = np.cov(qn2) q_s = np.random.multivariate_normal(mqn, C, n) q_s = q_s.transpose() # compute the correspondence to the original function domain fs = np.zeros((M, n)) for k in range(0, n): fs[:, k] = uf.cumtrapzmid(time, q_s[0:M, k] * np.abs(q_s[0:M, k]), np.sign(q_s[M, k]) * (q_s[M, k]**2)) # random warping generation rgam = uf.randomGamma(gam, n) gams = np.zeros((M, n)) for k in range(0, n): gams[:, k] = uf.invertGamma(rgam[:, k]) # sort functions and warping if sort_samples: mx = fs.max(axis=0) seq1 = mx.argsort() # compute the psi-function fy = np.gradient(rgam, binsize) psi = fy / np.sqrt(abs(fy) + eps) ip = np.zeros(n) len = np.zeros(n) for i in range(0, n): tmp = np.ones(M) ip[i] = tmp.dot(psi[:, i] / M) len[i] = np.acos(tmp.dot(psi[:, i] / M)) seq2 = len.argsort() # combine x-variability and y-variability ft = np.zeros((M, n)) for k in range(0, n): ft[:, k] = np.interp(gams[:, seq2[k]], np.arange(0, M) / np.double(M - 1), fs[:, seq1[k]]) tmp = np.isnan(ft[:, k]) while tmp.any(): rgam2 = uf.randomGamma(gam, 1) ft[:, k] = np.interp(gams[:, seq2[k]], np.arange(0, M) / np.double(M - 1), uf.invertGamma(rgam2)) else: # combine x-variability and y-variability ft = np.zeros((M, n)) for k in range(0, n): ft[:, k] = np.interp(gams[:, k], np.arange(0, M) / np.double(M - 1), fs[:, k]) tmp = np.isnan(ft[:, k]) while tmp.any(): rgam2 = uf.randomGamma(gam, 1) ft[:, k] = np.interp(gams[:, k], np.arange(0, M) / np.double(M - 1), uf.invertGamma(rgam2)) samples = collections.namedtuple('samples', ['fs', 'gams', 'ft']) out = samples(fs, rgam, ft) return out
def gauss_model(self, n=1, sort_samples=False): """ This function models the functional data using a Gaussian model extracted from the principal components of the srvfs :param n: number of random samples :param sort_samples: sort samples (default = T) :type n: integer :type sort_samples: bool """ fn = self.fn time = self.time qn = self.qn gam = self.gam # Parameters eps = np.finfo(np.double).eps binsize = np.diff(time) binsize = binsize.mean() M = time.size # compute mean and covariance in q-domain mq_new = qn.mean(axis=1) mididx = np.round(time.shape[0] / 2) m_new = np.sign(fn[mididx, :]) * np.sqrt(np.abs(fn[mididx, :])) mqn = np.append(mq_new, m_new.mean()) qn2 = np.vstack((qn, m_new)) C = np.cov(qn2) q_s = np.random.multivariate_normal(mqn, C, n) q_s = q_s.transpose() # compute the correspondence to the original function domain fs = np.zeros((M, n)) for k in range(0, n): fs[:, k] = uf.cumtrapzmid(time, q_s[0:M, k] * np.abs(q_s[0:M, k]), np.sign(q_s[M, k]) * (q_s[M, k]**2), mididx) fbar = fn.mean(axis=1) fsbar = fs.mean(axis=1) err = np.transpose(np.tile(fbar - fsbar, (n, 1))) fs += err # random warping generation rgam = uf.randomGamma(gam, n) gams = np.zeros((M, n)) for k in range(0, n): gams[:, k] = uf.invertGamma(rgam[:, k]) # sort functions and warping if sort_samples: mx = fs.max(axis=0) seq1 = mx.argsort() # compute the psi-function fy = np.gradient(rgam, binsize) psi = fy / np.sqrt(abs(fy) + eps) ip = np.zeros(n) len = np.zeros(n) for i in range(0, n): tmp = np.ones(M) ip[i] = tmp.dot(psi[:, i] / M) len[i] = np.arccos(tmp.dot(psi[:, i] / M)) seq2 = len.argsort() # combine x-variability and y-variability ft = np.zeros((M, n)) for k in range(0, n): ft[:, k] = np.interp(gams[:, seq2[k]], np.arange(0, M) / np.double(M - 1), fs[:, seq1[k]]) tmp = np.isnan(ft[:, k]) while tmp.any(): rgam2 = uf.randomGamma(gam, 1) ft[:, k] = np.interp(gams[:, seq2[k]], np.arange(0, M) / np.double(M - 1), uf.invertGamma(rgam2)) else: # combine x-variability and y-variability ft = np.zeros((M, n)) for k in range(0, n): ft[:, k] = np.interp(gams[:, k], np.arange(0, M) / np.double(M - 1), fs[:, k]) tmp = np.isnan(ft[:, k]) while tmp.any(): rgam2 = uf.randomGamma(gam, 1) ft[:, k] = np.interp(gams[:, k], np.arange(0, M) / np.double(M - 1), uf.invertGamma(rgam2)) self.rsamps = True self.fs = fs self.gams = rgam self.ft = ft self.qs = q_s[0:M, :] return