def continue_cholesky(self, x, x_old, U_old, observed=True, nugget=None): """ U = C.continue_cholesky(x, x_old, U_old[, observed=True, nugget=None]) Computes Cholesky factorization of self(z,z). Assumes the Cholesky factorization of self(x_old, x_old) has already been computed. :Arguments: - `x`: The input array on which to evaluate the Cholesky factorization. - `x_old`: The input array on which the Cholesky factorization has been computed. - `U_old`: The Cholesky factorization of C(x_old, x_old). - `observed`: If 'True', any observations are taken into account when computing the Cholesky factor. If not, the unobserved version of self is used. - `nugget`: The 'nugget' parameter, which will essentially be added to the diagonal of C(x,x) before Cholesky factorizing. """ # Concatenation of the old points and new points. xtot = vstack((x_old, x)) # Number of old points. N_old = x_old.shape[0] # Number of new points. N_new = x.shape[0] U_new = self.__call__(x, x, regularize=False, observed=observed) # not really implemented yet. if nugget is not None: for i in xrange(N_new): U_new[i, i] += nugget[i] U = asmatrix( zeros((N_new + N_old, N_old + N_new), dtype=float, order='F')) U[:N_old, :N_old] = U_old offdiag = self.__call__(x=x_old, y=x, observed=observed, regularize=False) trisolve(U_old, offdiag, uplo='U', transa='T', inplace=True) U[:N_old, N_old:] = offdiag U_new -= offdiag.T * offdiag info = dpotrf_wrap(U_new) if info > 0: raise LinAlgError, "Matrix does not appear to be positive definite by row %i. Consider another Covariance subclass, such as NearlyFullRankCovariance." % info U[N_old:, N_old:] = U_new return U
def continue_cholesky(self, x, x_old, U_old, observed=True, nugget=None): """ U = C.continue_cholesky(x, x_old, U_old[, observed=True, nugget=None]) Computes Cholesky factorization of self(z,z). Assumes the Cholesky factorization of self(x_old, x_old) has already been computed. :Arguments: - `x`: The input array on which to evaluate the Cholesky factorization. - `x_old`: The input array on which the Cholesky factorization has been computed. - `U_old`: The Cholesky factorization of C(x_old, x_old). - `observed`: If 'True', any observations are taken into account when computing the Cholesky factor. If not, the unobserved version of self is used. - `nugget`: The 'nugget' parameter, which will essentially be added to the diagonal of C(x,x) before Cholesky factorizing. """ # Concatenation of the old points and new points. xtot = vstack((x_old,x)) # Number of old points. N_old = x_old.shape[0] # Number of new points. N_new = x.shape[0] U_new = self.__call__(x, x, regularize=False, observed=observed) # not really implemented yet. if nugget is not None: for i in xrange(N_new): U_new[i,i] += nugget[i] U = asmatrix(zeros((N_new + N_old, N_old + N_new), dtype=float, order='F')) U[:N_old, :N_old] = U_old offdiag = self.__call__(x=x_old, y=x, observed=observed, regularize=False) trisolve(U_old,offdiag,uplo='U',transa='T', inplace=True) U[:N_old, N_old:] = offdiag U_new -= offdiag.T*offdiag info = dpotrf_wrap(U_new) if info>0: raise LinAlgError, "Matrix does not appear to be positive definite by row %i. Consider another Covariance subclass, such as NearlyFullRankCovariance." %info U[N_old:,N_old:] = U_new return U
def _obs_reg(self, M, dev_new, m_old): # reg_mat = chol(C(obs_mesh_*, obs_mesh_*)).T.I * M.dev reg_mat_new = -1. * dot( self.Uo[:m_old, m_old:].T, trisolve(self.Uo[:m_old, :m_old], M.dev, uplo='U', transa='T')).T trisolve(self.Uo[m_old:, m_old:].T, reg_mat_new, 'L', inplace=True) reg_mat_new += asmatrix( trisolve(self.Uo[m_old:, m_old:], dev_new.T, uplo='U', transa='T')).T return asmatrix(vstack((M.reg_mat, reg_mat_new)))
def observe(self, obs_mesh, obs_V, output_type='o'): __doc__ = Covariance.observe.__doc__ ndim = obs_mesh.shape[1] nobs = obs_mesh.shape[0] if self.ndim is not None: if not ndim==self.ndim: raise ValueError, "Dimension of observation mesh is not equal to dimension of base mesh." else: self.ndim = ndim # bases_o is the basis evaluated on the observation mesh. basis_o = asmatrix(self.eval_basis(obs_mesh, regularize=False)) # chol(basis_o.T * coef_cov * basis_o) chol_inner = self.coef_U * basis_o if output_type=='s': C_eval = dot(chol_inner.T, chol_inner).copy('F') U_eval = linalg.cholesky(C_eval).T.copy('F') U = U_eval m = U_eval.shape[0] piv = arange(m) else: # chol(basis_o.T * coef_cov * basis_o + diag(obs_V)). Really should do this as low-rank update of covariance # of V. U, piv, m = ichol_basis(basis=chol_inner, nug=obs_V, reltol=self.relative_precision) U = asmatrix(U) piv_new = piv[:m] self.obs_piv = piv_new obs_mesh_new = obs_mesh[piv_new,:] self.Uo = U[:m,:m] # chol(basis_o.T * coef_cov * basis_o + diag(obs_V)) ^ -T * basis_o.T self.Uo_cov = trisolve(self.Uo, basis_o[:,piv[:m]].T, uplo='U', transa='T') # chol(basis_o.T * coef_cov * basis_o + diag(obs_V)).T.I * basis_o.T * coef_cov self.Uo_cov = self.Uo_cov * self.coef_cov # coef_cov = coef_cov - coef_cov * basis_o * (basis_o.T * coef_cov * basis_o + diag(obs_V)).I * basis_o.T * coef_cov self.coef_cov = self.coef_cov - self.Uo_cov.T * self.Uo_cov # coef_U = chol(coef_cov) U, m, piv = ichol_full(c=self.coef_cov, reltol=self.relative_precision) U = asmatrix(U) self.coef_U = U[:m,argsort(piv)] self.m = m if output_type=='o': return piv_new, obs_mesh_new if output_type=='s': return U_eval, C_eval, basis_o raise ValueError, 'Output type not recognized.'
def _obs_eval(self, M, M_out, x, Uo_Cxo=None): if Uo_Cxo is None: Uo_Cxo = trisolve(M.Uo, self(M.obs_mesh, x, observed=False), uplo='U', transa='T') M_out += dot(asarray(M.reg_mat).squeeze(), asarray(Uo_Cxo)).squeeze() return M_out
def observe(self, obs_mesh, obs_V): __doc__ = Covariance.observe.__doc__ ndim = obs_mesh.shape[1] nobs = obs_mesh.shape[0] if self.ndim is not None: if not ndim==self.ndim: raise ValueError, "Dimension of observation mesh is not equal to dimension of base mesh." else: self.ndim = ndim # bases_o is the basis evaluated on the observation mesh. basis_o = asmatrix(self.eval_basis(obs_mesh, regularize=False)) # chol(basis_o.T * coef_cov * basis_o) chol_inner = self.coef_U * basis_o # chol(basis_o.T * coef_cov * basis_o + diag(obs_V)). Really should do this as low-rank update of covariance # of V. U, piv, m = ichol_basis(basis=chol_inner, nug=obs_V, reltol=self.relative_precision) U = asmatrix(U) piv_new = piv[:m] self.obs_piv = piv_new obs_mesh_new = obs_mesh[piv_new,:] self.Uo = U[:m,:m] # chol(basis_o.T * coef_cov * basis_o + diag(obs_V)) ^ -T * basis_o.T self.Uo_cov = trisolve(self.Uo, basis_o[:,piv[:m]].T, uplo='U', transa='T') # chol(basis_o.T * coef_cov * basis_o + diag(obs_V)).T.I * basis_o.T * coef_cov self.Uo_cov = self.Uo_cov * self.coef_cov # coef_cov -= coef_cov * basis_o * (basis_o.T * coef_cov * basis_o + diag(obs_V)).I * basis_o.T * coef_cov self.coef_cov -= self.Uo_cov.T * self.Uo_cov # coef_U = chol(coef_cov) U, m, piv = ichol_full(c=self.coef_cov, reltol=self.relative_precision) U = asmatrix(U) self.coef_U = U[:m,argsort(piv)] self.m = m return piv_new, obs_mesh_new, None
def _unobs_reg(self, M): # reg_mat = chol(C(obs_mesh_*, obs_mesh_*)).T.I * M.dev return asmatrix(trisolve(self.Uo, M.dev.T, uplo='U', transa='T')).T
def __call__(self, x, y=None, observed=True, regularize=True, return_Uo_Cxo=False): if y is x: symm = True else: symm = False # Remember shape of x, and then 'regularize' it. orig_shape = shape(x) if len(orig_shape) > 1: orig_shape = orig_shape[:-1] if regularize: x = regularize_array(x) ndimx = x.shape[-1] lenx = x.shape[0] if return_Uo_Cxo: Uo_Cxo = None # Safety if self.ndim is not None: if not self.ndim == ndimx: raise ValueError, "The number of spatial dimensions of x, "+\ ndimx.__str__()+\ ", does not match the number of spatial dimensions of the Covariance instance's base mesh, "+\ self.ndim.__str__()+"." # If there are observation points, prepare self(obs_mesh, x) # and chol(self(obs_mesh, obs_mesh)).T.I * self(obs_mesh, x) # ========================================================== # = If only one argument is provided, return the diagonal. = # ========================================================== if y is None: # Special fast-path for functions that have an 'amp' parameter if hasattr(self.eval_fun, 'diag_call'): V = self.eval_fun.diag_call(x, **self.params) # Otherwise, evaluate the diagonal in a loop. else: V = empty(lenx, dtype=float) for i in xrange(lenx): this_x = x[i].reshape((1, -1)) V[i] = self.eval_fun(this_x, this_x, **self.params) if self.observed and observed: sqpart = empty(lenx, dtype=float) Cxo = self.eval_fun(self.obs_mesh, x, **self.params) Uo_Cxo = trisolve(self.Uo, Cxo, uplo='U', transa='T') square_and_sum(Uo_Cxo, sqpart) V -= sqpart if return_Uo_Cxo: return V.reshape(orig_shape), Uo_Cxo else: return V.reshape(orig_shape) else: # ==================================================== # = # If x and y are the same array, save some work: = # ==================================================== if symm: C = self.eval_fun(x, x, symm=True, **self.params) # Update return value using observations. if self.observed and observed: Cxo = self.eval_fun(self.obs_mesh, x, **self.params) Uo_Cxo = trisolve(self.Uo, Cxo, uplo='U', transa='T') C -= Uo_Cxo.T * Uo_Cxo if return_Uo_Cxo: return C, Uo_Cxo else: return C # ====================================== # = # If x and y are different arrays: = # ====================================== else: if regularize: y = regularize_array(y) ndimy = y.shape[-1] leny = y.shape[0] if not ndimx == ndimy: raise ValueError, 'The last dimension of x and y (the number of spatial dimensions) must be the same.' C = self.eval_fun(x, y, **self.params) # Update return value using observations. if self.observed and observed: # If there are observation points, prepare self(obs_mesh, y) # and chol(self(obs_mesh, obs_mesh)).T.I * self(obs_mesh, y) Cyo = self.eval_fun(self.obs_mesh, y, **self.params) Uo_Cyo = trisolve(self.Uo, Cyo, uplo='U', transa='T') C -= Uo_Cxo.T * Uo_Cyo return C
def continue_cholesky(self, x, x_old, chol_dict_old, apply_pivot=True, observed=True, nugget=None, regularize=True, assume_full_rank=False, rank_limit=0): """ U = C.continue_cholesky(x, x_old, chol_dict_old[, observed=True, nugget=None, rank_limit=0]) returns {'pivots': piv, 'U': U} Computes incomplete Cholesky factorization of self(z,z), without actually evaluating the matrix first. Here z is the concatenation of x and x_old. Assumes the Cholesky factorization of self(x_old, x_old) has already been computed. :Arguments: - `x`: The input array on which to evaluate the Cholesky factorization. - `x_old`: The input array on which the Cholesky factorization has been computed. - `chol_dict_old`: A dictionary with kbasis_ys ['pivots', 'U']. Would be the output of either this method or C.cholesky(). - `apply_pivot`: A flag. If it's set to 'True', it returns a matrix U (not necessarily triangular) such that U.T*U=C(x,x). If it's set to 'False', the return value is a dictionary. Item 'pivots' is a vector of pivots, and item 'U' is an upper-triangular matrix (not necessarily square) such that U[:,argsort(piv)].T * U[:,argsort(piv)] = C(x,x). - `observed`: If 'True', any observations are taken into account when computing the Cholesky factor. If not, the unobserved version of self is used. - `nugget`: The 'nugget' parameter, which will essentially be added to the diagonal of C(x,x) before Cholesky factorizing. - `rank_limit`: If rank_limit > 0, the factor will have at most rank_limit rows. """ if regularize: x = regularize_array(x) # Concatenation of the old points and new points. xtot = vstack((x_old, x)) # Extract information from chol_dict_old. U_old = chol_dict_old['U'] m_old = U_old.shape[0] piv_old = chol_dict_old['pivots'] # Number of old points. N_old = x_old.shape[0] # Number of new points. N_new = x.shape[0] if rank_limit == 0: m_new_max = N_new else: m_new_max = min(N_new, max(0, rank_limit - m_old)) # get-row function def rowfun(i, xpiv, rowvec): """ A function that can be used to overwrite an input array with superdiagonal rows. """ rowvec[i:] = self.__call__(x=xpiv[i - 1, :].reshape(1, -1), y=xpiv[i:, :], regularize=False, observed=observed) # diagonal diag = self.__call__(x, y=None, regularize=False, observed=observed) # not really implemented yet. if nugget is not None: diag += nugget.ravel() # Arrange U for input to ichol. See documentation. U = asmatrix( zeros((m_new_max + m_old, N_old + N_new), dtype=float, order='F')) U[:m_old, :m_old] = U_old[:, :m_old] U[:m_old, N_new + m_old:] = U_old[:, m_old:] offdiag = self.__call__(x=x_old[piv_old[:m_old], :], y=x, observed=observed, regularize=False) trisolve(U_old[:, :m_old], offdiag, uplo='U', transa='T', inplace=True) U[:m_old, m_old:N_new + m_old] = offdiag # Initialize pivot vector: # [old_posdef_pivots new_pivots old_singular_pivots] # - old_posdef_pivots are the indices of the rows that made it into the Cholesky factor so far. # - old_singular_pivots are the indices of the rows that haven't made it into the Cholesky factor so far. # - new_pivots are the indices of the rows that are going to be incorporated now. piv = zeros(N_new + N_old, dtype=int) piv[:m_old] = piv_old[:m_old] piv[N_new + m_old:] = piv_old[m_old:] piv[m_old:N_new + m_old] = arange(N_new) + N_old # ============================================ # = Call to Fortran function ichol_continue. = # ============================================ # Early return if rank is all used up. if m_new_max > 0: # ============================================ # = Call to Fortran function ichol_continue. = # ============================================ if not assume_full_rank: m, piv = ichol_continue(U, diag=diag, reltol=self.relative_precision, rowfun=rowfun, piv=piv, x=xtot[piv, :], mold=m_old) else: m = m_old + N_new C_eval = self.__call__(x, x, observed=True, regularize=False) U2 = cholesky(C_eval).T U[m_old:, m_old:N_new + m_old] = U2 if m_old < N_old: offdiag2 = self.__call__(x=x, y=x_old[piv_old[m_old:]], observed=observed, regularize=False) trisolve(U2, offdiag2, uplo='U', transa='T', inplace=True) U[m_old:, N_new + m_old:] = offdiag2 else: m = m_old # Arrange output matrix and return. if m < 0: raise ValueError, 'Matrix does not appear positive semidefinite.' if not apply_pivot: # Useful for self.observe. U is upper triangular. U = U[:m, :] if assume_full_rank: return {'pivots': piv, 'U': U, 'C_eval': C_eval, 'U_new': U2} else: return {'pivots': piv, 'U': U} else: # Useful for the user. U.T * U = C(x,x). return U[:m, argsort(piv)]
def _obs_reg(self, M, dev_new, m_old): # reg_mat = chol(self.basis_o.T * self.coef_cov * self.basis_o + diag(obs_V)).T.I * self.basis_o.T * self.coef_cov * # chol(self(obs_mesh_*, obs_mesh_*)).T.I * M.dev M.reg_mat += self.Uo_cov.T * asmatrix(trisolve(self.Uo, dev_new, uplo='U', transa='T')).T return M.reg_mat
def _unobs_reg(self, M): # reg_mat = chol(self.basis_o.T * self.coef_cov * self.basis_o + diag(obs_V)).T.I * self.basis_o.T * self.coef_cov * # chol(self(obs_mesh_*, obs_mesh_*)).T.I * M.dev return self.Uo_cov.T * asmatrix(trisolve(self.Uo, M.dev, uplo='U', transa='T')).T
def continue_cholesky(self, x, x_old, chol_dict_old, apply_pivot = True, observed=True, nugget=None, regularize=True, assume_full_rank = False, rank_limit=0): """ U = C.continue_cholesky(x, x_old, chol_dict_old[, observed=True, nugget=None, rank_limit=0]) returns {'pivots': piv, 'U': U} Computes incomplete Cholesky factorization of self(z,z), without actually evaluating the matrix first. Here z is the concatenation of x and x_old. Assumes the Cholesky factorization of self(x_old, x_old) has already been computed. :Arguments: - `x`: The input array on which to evaluate the Cholesky factorization. - `x_old`: The input array on which the Cholesky factorization has been computed. - `chol_dict_old`: A dictionary with kbasis_ys ['pivots', 'U']. Would be the output of either this method or C.cholesky(). - `apply_pivot`: A flag. If it's set to 'True', it returns a matrix U (not necessarily triangular) such that U.T*U=C(x,x). If it's set to 'False', the return value is a dictionary. Item 'pivots' is a vector of pivots, and item 'U' is an upper-triangular matrix (not necessarily square) such that U[:,argsort(piv)].T * U[:,argsort(piv)] = C(x,x). - `observed`: If 'True', any observations are taken into account when computing the Cholesky factor. If not, the unobserved version of self is used. - `nugget`: The 'nugget' parameter, which will essentially be added to the diagonal of C(x,x) before Cholesky factorizing. - `rank_limit`: If rank_limit > 0, the factor will have at most rank_limit rows. """ if regularize: x=regularize_array(x) # Concatenation of the old points and new points. xtot = vstack((x_old,x)) # Extract information from chol_dict_old. U_old = chol_dict_old['U'] m_old = U_old.shape[0] piv_old = chol_dict_old['pivots'] # Number of old points. N_old = x_old.shape[0] # Number of new points. N_new = x.shape[0] if rank_limit == 0: m_new_max = N_new else: m_new_max = min(N_new,max(0,rank_limit-m_old)) # get-row function def rowfun(i,xpiv,rowvec): """ A function that can be used to overwrite an input array with superdiagonal rows. """ rowvec[i:] = self.__call__(x=xpiv[i-1,:].reshape(1,-1), y=xpiv[i:,:], regularize=False, observed = observed) # diagonal diag = self.__call__(x, y=None, regularize=False, observed = observed) # not really implemented yet. if nugget is not None: diag += nugget.ravel() # Arrange U for input to ichol. See documentation. U = asmatrix(zeros((m_new_max + m_old, N_old + N_new), dtype=float, order='F')) U[:m_old, :m_old] = U_old[:,:m_old] U[:m_old,N_new+m_old:] = U_old[:,m_old:] offdiag = self.__call__(x=x_old[piv_old[:m_old],:], y=x, observed=observed, regularize=False) trisolve(U_old[:,:m_old],offdiag,uplo='U',transa='T', inplace=True) U[:m_old, m_old:N_new+m_old] = offdiag # Initialize pivot vector: # [old_posdef_pivots new_pivots old_singular_pivots] # - old_posdef_pivots are the indices of the rows that made it into the Cholesky factor so far. # - old_singular_pivots are the indices of the rows that haven't made it into the Cholesky factor so far. # - new_pivots are the indices of the rows that are going to be incorporated now. piv = zeros(N_new + N_old, dtype=int) piv[:m_old] = piv_old[:m_old] piv[N_new + m_old:] = piv_old[m_old:] piv[m_old:N_new + m_old] = arange(N_new)+N_old # ============================================ # = Call to Fortran function ichol_continue. = # ============================================ # Early return if rank is all used up. if m_new_max > 0: # ============================================ # = Call to Fortran function ichol_continue. = # ============================================ if not assume_full_rank: m, piv = ichol_continue(U, diag = diag, reltol = self.relative_precision, rowfun = rowfun, piv=piv, x=xtot[piv,:], mold=m_old) else: m = m_old + N_new C_eval = self.__call__(x,x,observed=True,regularize=False) U2 = cholesky(C_eval).T U[m_old:,m_old:N_new+m_old] = U2 if m_old < N_old: offdiag2 = self.__call__(x=x, y=x_old[piv_old[m_old:]], observed=observed, regularize=False) trisolve(U2,offdiag2,uplo='U',transa='T',inplace=True) U[m_old:,N_new+m_old:] = offdiag2 else: m = m_old # Arrange output matrix and return. if m<0: raise ValueError, 'Matrix does not appear positive semidefinite.' if not apply_pivot: # Useful for self.observe. U is upper triangular. U = U[:m,:] if assume_full_rank: return {'pivots': piv, 'U': U, 'C_eval':C_eval, 'U_new': U2} else: return {'pivots': piv, 'U': U} else: # Useful for the user. U.T * U = C(x,x). return U[:m,argsort(piv)]
def _obs_eval(self, M, M_out, x): Cxo = self(M.obs_mesh, x, observed = False) Cxo_Uo_inv = trisolve(M.Uo, Cxo, uplo='U', transa='T') M_out += dot(asarray(M.reg_mat).T,asarray(Cxo_Uo_inv)).squeeze() return M_out
def continue_cholesky(self, x, x_old, chol_dict_old, apply_pivot = True, observed=True, nugget=None, regularize=True, assume_full_rank=False): """ U = C.continue_cholesky(x, x_old, chol_dict_old[, observed=True, nugget=None]) {'pivots': piv, 'U': U} = \ C.cholesky(x, x_old, chol_dict_old, apply_pivot = False[, observed=True, nugget=None]) Computes incomplete Cholesky factorization of self(z,z). Here z is the concatenation of x and x_old. Assumes the Cholesky factorization of self(x_old, x_old) has already been computed. :Arguments: - `x`: The input array on which to evaluate the Cholesky factorization. - `x_old`: The input array on which the Cholesky factorization has been computed. - `chol_dict_old`: A dictionary with kbasis_ys ['pivots', 'U']. Would be the output of either this method or C.cholesky(). - `apply_pivot`: A flag. If it's set to 'True', it returns a matrix U (not necessarily triangular) such that U.T*U=C(x,x). If it's set to 'False', the return value is a dictionary. Item 'pivots' is a vector of pivots, and item 'U' is an upper-triangular matrix (not necessarily square) such that U[:,argsort(piv)].T * U[:,argsort(piv)] = C(x,x). - `observed`: If 'True', any observations are taken into account when computing the Cholesky factor. If not, the unobserved version of self is used. - `nugget`: The 'nugget' parameter, which will essentially be added to the diagonal of C(x,x) before Cholesky factorizing. """ if regularize: x=regularize_array(x) # Concatenation of the old points and new points. xtot = vstack((x_old,x)) # Extract information from chol_dict_old. U_old = chol_dict_old['U'] m_old = U_old.shape[0] piv_old = chol_dict_old['pivots'] # Number of old points. N_old = x_old.shape[0] # Number of new points. N_new = x.shape[0] # Compute off-diagonal part of Cholesky factor offdiag = self.__call__(x=x_old[piv_old[:m_old],:], y=x, observed=observed, regularize=False) trisolve(U_old[:,:m_old],offdiag,uplo='U',transa='T', inplace=True) # Compute new diagonal part of Cholesky factor C_new = self.__call__(x=x, y=x, observed=observed, regularize=False) if nugget is not None: for i in xrange(N_new): C_new[i,i] += nugget[i] C_new -= offdiag.T*offdiag if not assume_full_rank: U_new, m_new, piv_new = ichol_full(c=C_new, reltol=self.relative_precision) else: U_new = cholesky(C_new).T m_new = U_new.shape[0] piv_new = arange(m_new) U_new = asmatrix(U_new[:m_new,:]) U = asmatrix(zeros((m_new + m_old, N_old + N_new), dtype=float, order='F')) # Top portion of U U[:m_old, :m_old] = U_old[:,:m_old] U[:m_old,N_new+m_old:] = U_old[:,m_old:] offdiag=offdiag[:,piv_new] U[:m_old, m_old:N_new+m_old] = offdiag # Lower portion of U U[m_old:,m_old:m_old+N_new] = U_new if m_old < N_old and m_new > 0: offdiag_lower = self.__call__( x=x[piv_new[:m_new],:], y=x_old[piv_old[m_old:],:], observed=observed, regularize=False) offdiag_lower -= offdiag[:,:m_new].T*U[:m_old,m_old+N_new:] trisolve(U_new[:,:m_new],offdiag_lower,uplo='U',transa='T', inplace=True) U[m_old:,m_old+N_new:] = offdiag_lower # Rank and pivots m=m_old+m_new piv=hstack((piv_old[:m_old],piv_new+N_old,piv_old[m_old:])) # Arrange output matrix and return. if m<0: raise ValueError, 'Matrix does not appear positive semidefinite.' if not apply_pivot: # Useful for self.observe. U is upper triangular. return {'pivots': piv, 'U': U} else: # Useful for the user. U.T * U = C(x,x). return U[:,argsort(piv)]
def _obs_eval(self, M, M_out, x, Uo_Cxo=None): if Uo_Cxo is None: Uo_Cxo = trisolve(M.Uo, self(M.obs_mesh, x, observed = False), uplo='U', transa='T') M_out += dot(asarray(M.reg_mat).squeeze(),asarray(Uo_Cxo)).squeeze() return M_out
def _obs_reg(self, M, dev_new, m_old): # reg_mat = chol(C(obs_mesh_*, obs_mesh_*)).T.I * M.dev reg_mat_new = -1.*dot(self.Uo[:m_old,m_old:].T , trisolve(self.Uo[:m_old,:m_old], M.dev, uplo='U', transa='T')).T trisolve(self.Uo[m_old:,m_old:].T, reg_mat_new, 'L', inplace=True) reg_mat_new += asmatrix(trisolve(self.Uo[m_old:,m_old:], dev_new.T, uplo='U', transa='T')).T return asmatrix(vstack((M.reg_mat,reg_mat_new)))
def _unobs_reg(self, M): # reg_mat = chol(C(obs_mesh_*, obs_mesh_*)).T.I * M.dev return asmatrix(trisolve(self.Uo, M.dev.T, uplo='U',transa='T')).T
def __call__(self, x, y=None, observed=True, regularize=True, return_Uo_Cxo=False): if y is x: symm=True else: symm=False # Remember shape of x, and then 'regularize' it. orig_shape = shape(x) if len(orig_shape)>1: orig_shape = orig_shape[:-1] if regularize: x=regularize_array(x) ndimx = x.shape[-1] lenx = x.shape[0] if return_Uo_Cxo: Uo_Cxo = None # Safety if self.ndim is not None: if not self.ndim == ndimx: raise ValueError, "The number of spatial dimensions of x, "+\ ndimx.__str__()+\ ", does not match the number of spatial dimensions of the Covariance instance's base mesh, "+\ self.ndim.__str__()+"." # If there are observation points, prepare self(obs_mesh, x) # and chol(self(obs_mesh, obs_mesh)).T.I * self(obs_mesh, x) # ========================================================== # = If only one argument is provided, return the diagonal. = # ========================================================== if y is None: # Special fast-path for functions that have an 'amp' parameter if hasattr(self.eval_fun, 'diag_call'): V = self.eval_fun.diag_call(x, **self.params) # Otherwise, evaluate the diagonal in a loop. else: V=empty(lenx,dtype=float) for i in xrange(lenx): this_x = x[i].reshape((1,-1)) V[i] = self.eval_fun(this_x, this_x, **self.params) if self.observed and observed: sqpart = empty(lenx,dtype=float) Cxo = self.eval_fun(self.obs_mesh, x, **self.params) Uo_Cxo = trisolve(self.Uo, Cxo, uplo='U', transa='T') square_and_sum(Uo_Cxo, sqpart) V -= sqpart if return_Uo_Cxo: return V.reshape(orig_shape), Uo_Cxo else: return V.reshape(orig_shape) else: # ==================================================== # = # If x and y are the same array, save some work: = # ==================================================== if symm: C=self.eval_fun(x,x,symm=True,**self.params) # Update return value using observations. if self.observed and observed: Cxo = self.eval_fun(self.obs_mesh, x, **self.params) Uo_Cxo = trisolve(self.Uo, Cxo, uplo='U', transa='T') C -= Uo_Cxo.T * Uo_Cxo if return_Uo_Cxo: return C, Uo_Cxo else: return C # ====================================== # = # If x and y are different arrays: = # ====================================== else: if regularize: y=regularize_array(y) ndimy = y.shape[-1] leny = y.shape[0] if not ndimx==ndimy: raise ValueError, 'The last dimension of x and y (the number of spatial dimensions) must be the same.' C = self.eval_fun(x,y,**self.params) # Update return value using observations. if self.observed and observed: # If there are observation points, prepare self(obs_mesh, y) # and chol(self(obs_mesh, obs_mesh)).T.I * self(obs_mesh, y) Cyo = self.eval_fun(self.obs_mesh, y, **self.params) Uo_Cyo = trisolve(self.Uo, Cyo,uplo='U', transa='T') C -= Uo_Cxo.T * Uo_Cyo return C
def observe(self, obs_mesh, obs_V, output_type='o'): __doc__ = Covariance.observe.__doc__ ndim = obs_mesh.shape[1] nobs = obs_mesh.shape[0] if self.ndim is not None: if not ndim == self.ndim: raise ValueError, "Dimension of observation mesh is not equal to dimension of base mesh." else: self.ndim = ndim # bases_o is the basis evaluated on the observation mesh. basis_o = asmatrix(self.eval_basis(obs_mesh, regularize=False)) # chol(basis_o.T * coef_cov * basis_o) chol_inner = self.coef_U * basis_o if output_type == 's': C_eval = dot(chol_inner.T, chol_inner).copy('F') U_eval = linalg.cholesky(C_eval).T.copy('F') U = U_eval m = U_eval.shape[0] piv = arange(m) else: # chol(basis_o.T * coef_cov * basis_o + diag(obs_V)). Really should do this as low-rank update of covariance # of V. U, piv, m = ichol_basis(basis=chol_inner, nug=obs_V, reltol=self.relative_precision) U = asmatrix(U) piv_new = piv[:m] self.obs_piv = piv_new obs_mesh_new = obs_mesh[piv_new, :] self.Uo = U[:m, :m] # chol(basis_o.T * coef_cov * basis_o + diag(obs_V)) ^ -T * basis_o.T self.Uo_cov = trisolve(self.Uo, basis_o[:, piv[:m]].T, uplo='U', transa='T') # chol(basis_o.T * coef_cov * basis_o + diag(obs_V)).T.I * basis_o.T * coef_cov self.Uo_cov = self.Uo_cov * self.coef_cov # coef_cov = coef_cov - coef_cov * basis_o * (basis_o.T * coef_cov * basis_o + diag(obs_V)).I * basis_o.T * coef_cov self.coef_cov = self.coef_cov - self.Uo_cov.T * self.Uo_cov # coef_U = chol(coef_cov) U, m, piv = ichol_full(c=self.coef_cov, reltol=self.relative_precision) U = asmatrix(U) self.coef_U = U[:m, argsort(piv)] self.m = m if output_type == 'o': return piv_new, obs_mesh_new if output_type == 's': return U_eval, C_eval, basis_o raise ValueError, 'Output type not recognized.'
def _unobs_reg(self, M): # reg_mat = chol(self.basis_o.T * self.coef_cov * self.basis_o + diag(obs_V)).T.I * self.basis_o.T * self.coef_cov * # chol(self(obs_mesh_*, obs_mesh_*)).T.I * M.dev return self.Uo_cov.T * asmatrix( trisolve(self.Uo, M.dev, uplo='U', transa='T')).T
def _obs_reg(self, M, dev_new, m_old): # reg_mat = chol(self.basis_o.T * self.coef_cov * self.basis_o + diag(obs_V)).T.I * self.basis_o.T * self.coef_cov * # chol(self(obs_mesh_*, obs_mesh_*)).T.I * M.dev M.reg_mat = M.reg_mat + self.Uo_cov.T * asmatrix( trisolve(self.Uo, dev_new, uplo='U', transa='T')).T return M.reg_mat
def continue_cholesky(self, x, x_old, chol_dict_old, apply_pivot = True, observed=True, nugget=None, regularize=True, assume_full_rank=False, rank_limit=0): """ U = C.continue_cholesky(x, x_old, chol_dict_old[, observed=True, nugget=None, rank_limit=0]) Returns {'pivots': piv, 'U': U} Computes incomplete Cholesky factorization of self(z,z). Here z is the concatenation of x and x_old. Assumes the Cholesky factorization of self(x_old, x_old) has already been computed. :Arguments: - `x`: The input array on which to evaluate the Cholesky factorization. - `x_old`: The input array on which the Cholesky factorization has been computed. - `chol_dict_old`: A dictionary with kbasis_ys ['pivots', 'U']. Would be the output of either this method or C.cholesky(). - `apply_pivot`: A flag. If it's set to 'True', it returns a matrix U (not necessarily triangular) such that U.T*U=C(x,x). If it's set to 'False', the return value is a dictionary. Item 'pivots' is a vector of pivots, and item 'U' is an upper-triangular matrix (not necessarily square) such that U[:,argsort(piv)].T * U[:,argsort(piv)] = C(x,x). - `observed`: If 'True', any observations are taken into account when computing the Cholesky factor. If not, the unobserved version of self is used. - `nugget`: The 'nugget' parameter, which will essentially be added to the diagonal of C(x,x) before Cholesky factorizing. - `rank_limit`: If rank_limit > 0, the factor will have at most rank_limit rows. """ if regularize: x=regularize_array(x) if rank_limit > 0: raise ValueError, 'NearlyFullRankCovariance does not accept a rank_limit argument. Use Covariance instead.' if rank_limit > 0: raise ValueError, 'NearlyFullRankCovariance does not accept a rank_limit argument. Use Covariance instead.' # Concatenation of the old points and new points. xtot = vstack((x_old,x)) # Extract information from chol_dict_old. U_old = chol_dict_old['U'] m_old = U_old.shape[0] piv_old = chol_dict_old['pivots'] # Number of old points. N_old = x_old.shape[0] # Number of new points. N_new = x.shape[0] # Compute off-diagonal part of Cholesky factor offdiag = self.__call__(x=x_old[piv_old[:m_old],:], y=x, observed=observed, regularize=False) trisolve(U_old[:,:m_old],offdiag,uplo='U',transa='T', inplace=True) # Compute new diagonal part of Cholesky factor C_new = self.__call__(x=x, y=x, observed=observed, regularize=False) if nugget is not None: for i in xrange(N_new): C_new[i,i] += nugget[i] C_new -= offdiag.T*offdiag if not assume_full_rank: U_new, m_new, piv_new = ichol_full(c=C_new, reltol=self.relative_precision) else: U_new = cholesky(C_new).T m_new = U_new.shape[0] piv_new = arange(m_new) U_new = asmatrix(U_new[:m_new,:]) U = asmatrix(zeros((m_new + m_old, N_old + N_new), dtype=float, order='F')) # Top portion of U U[:m_old, :m_old] = U_old[:,:m_old] U[:m_old,N_new+m_old:] = U_old[:,m_old:] offdiag=offdiag[:,piv_new] U[:m_old, m_old:N_new+m_old] = offdiag # Lower portion of U U[m_old:,m_old:m_old+N_new] = U_new if m_old < N_old and m_new > 0: offdiag_lower = self.__call__( x=x[piv_new[:m_new],:], y=x_old[piv_old[m_old:],:], observed=observed, regularize=False) offdiag_lower -= offdiag[:,:m_new].T*U[:m_old,m_old+N_new:] trisolve(U_new[:,:m_new],offdiag_lower,uplo='U',transa='T', inplace=True) U[m_old:,m_old+N_new:] = offdiag_lower # Rank and pivots m=m_old+m_new piv=hstack((piv_old[:m_old],piv_new+N_old,piv_old[m_old:])) # Arrange output matrix and return. if m<0: raise ValueError, 'Matrix does not appear positive semidefinite.' if not apply_pivot: # Useful for self.observe. U is upper triangular. if assume_full_rank: return {'pivots':piv,'U':U,'C_eval':C_new,'U_new':U_new} else: return {'pivots': piv, 'U': U} else: # Useful for the user. U.T * U = C(x,x). return U[:,argsort(piv)]
def __call__(self, x, y=None, observed=True, regularize=True): if y is x: symm=True else: symm=False # Remember shape of x, and then 'regularize' it. orig_shape = shape(x) if len(orig_shape)>1: orig_shape = orig_shape[:-1] if regularize: x=regularize_array(x) ndimx = x.shape[-1] lenx = x.shape[0] # Safety if self.ndim is not None: if not self.ndim == ndimx: raise ValueError, "The number of spatial dimensions of x, "+\ ndimx.__str__()+\ ", does not match the number of spatial dimensions of the Covariance instance's base mesh, "+\ self.ndim.__str__()+"." # If there are observation points, prepare self(obs_mesh, x) # and chol(self(obs_mesh, obs_mesh)).T.I * self(obs_mesh, x) if self.observed and observed: Cxo = self.eval_fun(self.obs_mesh, x, **self.params) Uo_Cxo = trisolve(self.Uo, Cxo, uplo='U', transa='T') # ========================================================== # = If only one argument is provided, return the diagonal. = # ========================================================== # See documentation. if y is None: # V = diag_call(x=x, cov_fun = self.diag_cov_fun) # ============================================================================= # = Come up with a better solution, the diagonal is not necessarily constant. = # ============================================================================= V=empty(lenx,dtype=float) # V.fill(self.params['amp']**2) for i in xrange(lenx): this_x = x[i].reshape((1,-1)) V[i] = self.eval_fun(this_x, this_x,**self.params) if self.observed and observed: for i in range(lenx): this_Uo_Cxo = Uo_Cxo[:,i] V[i] -= this_Uo_Cxo.T*this_Uo_Cxo return V.reshape(orig_shape) else: # ==================================================== # = # If x and y are the same array, save some work: = # ==================================================== if symm: C=self.eval_fun(x,x,symm=True,**self.params) # Update return value using observations. if self.observed and observed: C -= Uo_Cxo.T * Uo_Cxo return C # ====================================== # = # If x and y are different arrays: = # ====================================== else: if regularize: y=regularize_array(y) ndimy = y.shape[-1] leny = y.shape[0] if not ndimx==ndimy: raise ValueError, 'The last dimension of x and y (the number of spatial dimensions) must be the same.' C = self.eval_fun(x,y,**self.params) # Update return value using observations. if self.observed and observed: # If there are observation points, prepare self(obs_mesh, y) # and chol(self(obs_mesh, obs_mesh)).T.I * self(obs_mesh, y) Cyo = self.eval_fun(self.obs_mesh, y, **self.params) Uo_Cyo = trisolve(self.Uo, Cyo,uplo='U', transa='T') C -= Uo_Cxo.T * Uo_Cyo return C