def update_L_C_single(x, L_C, n, omega, u): D = omega.shape[0] assert x.ndim == 1 assert len(x) == D m = 1 if np.isscalar(u) else len(u) N = 1 L_C *= np.sqrt(n) # since cholupdate works on transposed version L_C = L_C.T projection = np.dot(x[np.newaxis, :], omega) + u np.sin(projection, projection) projection *= -np.sqrt(2. / m) temp = np.zeros((N, m)) for d in range(D): temp = -projection * omega[d, :] # here temp is 1xm, this costs O(m^2) cholupdate(L_C, temp[0]) # since cholupdate works on transposed version L_C = L_C.T # since the new C has a 1/(n+1) term in it L_C /= np.sqrt(n + 1) return L_C
def update_mean_cov_L_lmbda(X, old_mean, old_cov_L, lmbdas): assert len(X) == len(lmbdas) # work on upper triangular cholesky internally old_cov_R = old_cov_L.T mean = old_mean for x, lmbda in zip(X, lmbdas): old_cov_R *= np.sqrt(1 - lmbda) update_vec = np.sqrt(lmbda) * (x - mean) cholupdate(old_cov_R, update_vec) mean = (1 - lmbda) * mean + lmbda * x # transform back to lower triangular version cov_L = old_cov_R.T return mean, cov_L
def update_b_L_C_weighted(X, b, L_C, log_sum_weights, log_weights, omega, u): m = 1 if np.isscalar(u) else len(u) N = X.shape[0] D = X.shape[1] # transform weights into (1-\lmbda)*old_mean+ \lmbda*new_term style updates lmbdas = log_weights_to_lmbdas(log_sum_weights, log_weights) # first and (negative) second derivatives of rff feature map projection = np.dot(X, omega) + u Phi = np.cos(projection) * np.sqrt(2. / m) Phi2 = np.sin(projection) * np.sqrt(2. / m) # not needed any longer del projection # work on upper triangular cholesky internally L_R = L_C.T b_new_term = np.zeros(m) for i in range(N): # downscale L_C once for every datum L_R *= np.sqrt(1 - lmbdas[i]) b_new_term[:] = 0 for d in range(D): b_new_term += Phi[i] * (omega[d, :]**2) # L_C is updated D times for every datum, each with fixed lmbda C_new_term = Phi2[i] * omega[d, :] * np.sqrt(lmbdas[i]) cholupdate(L_R, C_new_term) # b is updated once per datum b = (1 - lmbdas[i]) * b + lmbdas[i] * b_new_term # transform back to lower triangular version L_C = L_R.T return b, L_C
def update_b_L_C_weighted(X, b, L_C, log_sum_weights, log_weights, omega, u): m = 1 if np.isscalar(u) else len(u) N = X.shape[0] D = X.shape[1] # transform weights into (1-\lmbda)*old_mean+ \lmbda*new_term style updates lmbdas = log_weights_to_lmbdas(log_sum_weights, log_weights) # first and (negative) second derivatives of rff feature map projection = np.dot(X, omega) + u Phi = np.cos(projection) * np.sqrt(2. / m) Phi2 = np.sin(projection) * np.sqrt(2. / m) # not needed any longer del projection # work on upper triangular cholesky internally L_R = L_C.T b_new_term = np.zeros(m) for i in range(N): # downscale L_C once for every datum L_R *= np.sqrt(1 - lmbdas[i]) b_new_term[:] = 0 for d in range(D): b_new_term += Phi[i] * (omega[d, :] ** 2) # L_C is updated D times for every datum, each with fixed lmbda C_new_term = Phi2[i] * omega[d, :] * np.sqrt(lmbdas[i]) cholupdate(L_R, C_new_term) # b is updated once per datum b = (1 - lmbdas[i]) * b + lmbdas[i] * b_new_term # transform back to lower triangular version L_C = L_R.T return b, L_C
def rank_one_update_mean_covariance_cholesky_lmbda(u, lmbda=.1, mean=None, cov_L=None, nu2=1., gamma2=None): """ Returns updated mean and Cholesky of sum of outer products following a (1-lmbda)*old + lmbda* step_size*uu^T+lmbda*gamm2*I rule Optional: If gamma2 is given, an isotropic term gamma2 * I is added to the uu^T part where old mean and cov_L=Cholesky(old) (lower Cholesky) are given. Performs efficient rank-one updates of the Cholesky directly. """ assert lmbda >= 0 and lmbda <= 1 assert u.ndim == 1 D = len(u) # check if first term if mean is None or cov_L is None : # in that case, zero mean and scaled identity matrix mean = np.zeros(D) cov_L = np.eye(D) * nu2 else: assert len(mean) == D assert mean.ndim == 1 assert cov_L.ndim == 2 assert cov_L.shape[0] == D assert cov_L.shape[1] == D # update mean updated_mean = (1 - lmbda) * mean + lmbda * u # update Cholesky: first downscale existing Cholesky update_cov_L = np.sqrt(1 - lmbda) * cov_L.T # rank-one update of the centered new vector update_vec = np.sqrt(lmbda) * np.sqrt(nu2) * (u - mean) cholupdate(update_cov_L, update_vec) # optional: add isotropic term if specified, requires looping rank-one updates over # all basis vectors e_1, ..., e_D if gamma2 is not None: e_d = np.zeros(D) for d in range(D): e_d[:] = 0 e_d[d] = np.sqrt(gamma2) # could do a Cholesky update, but this routine does a loop over dimensions # where the vector only has one non-zero component # That is O(D^2) and therefore not efficient when used in a loop cholupdate(update_cov_L, np.sqrt(lmbda) * e_d) # TODO: # in contrast, can do a simplified update when knowing that e_d is sparse # manual Cholesky update (only doing the d-th component of algorithm on # https://en.wikipedia.org/wiki/Cholesky_decomposition#Rank-one_update # # wiki (MB) code: # r = sqrt(L(k,k)^2 + x(k)^2); # c = r / L(k, k); # s = x(k) / L(k, k); # L(k, k) = r; # L(k+1:n,k) = (L(k+1:n,k) + s*x(k+1:n)) / c; # x(k+1:n) = c*x(k+1:n) - s*L(k+1:n,k); # since cholupdate works on transposed version update_cov_L = update_cov_L.T # done updating Cholesky return updated_mean, update_cov_L