def __call__(self, x, observed = True, regularize=True, Uo_Cxo=None): # Record original shape of x and 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 does not match the number of spatial dimensions of the Mean instance's base mesh." # Evaluate the unobserved mean M = self.eval_fun(x,**self.params).squeeze() # Condition. Basis covariances get special treatment. See documentation for formulas. if self.observed and observed: M = self.C._obs_eval(self, M, x, Uo_Cxo) return M.reshape(orig_shape)
def __call__(self, x, regularize=True): # TODO: check repeats for basis covariances too. # If initial values were passed in, observe on them. if self.need_init_obs: self._init_obs() # Record original shape of x and regularize it. orig_shape = shape(x) if len(orig_shape) > 1: orig_shape = orig_shape[:-1] if regularize: if any(isnan(x)): raise ValueError, 'Input argument to Realization contains NaNs.' x = regularize_array(x) if x is self.x_sofar: return self.f_sofar if self.check_repeats: # use caching_call to save duplicate calls. f, self.x_sofar, self.f_sofar = caching_call( self.draw_vals, x, self.x_sofar, self.f_sofar) else: # Call to self.draw_vals. f = self.draw_vals(x) if regularize: return f.reshape(orig_shape) else: return f
def cholesky(self, x, apply_pivot=True, observed=True, nugget=None, regularize=True): __doc__ = Covariance.cholesky.__doc__ if regularize: x = regularize_array(x) # The pivots are just 1:N, N being the shape of x. piv_return = arange(x.shape[1], dtype=int) # The rank of the Cholesky factor is just the rank of the # coefficient covariance matrix. if observed: coef_U = self.coef_U m = self.m else: coef_U = self.unobs_coef_U m = self.unobs_m # The Cholesky factor is the Cholesky factor of the basis times # the basis evaluated on x. This isn't triangular, but it does have # the property U.T*U = self(x,x). U_return = asmatrix(self.eval_basis(x, regularize=False)) U_return = coef_U * U_return if apply_pivot: # Good for users. return U_return else: # Good for self.observe. return {'piv': piv_return, 'U': U_return}
def cholesky(self, x, apply_pivot = True, observed=True, nugget=None, regularize=True): __doc__ = Covariance.cholesky.__doc__ if regularize: x = regularize_array(x) # The pivots are just 1:N, N being the shape of x. piv_return = arange(x.shape[1], dtype=int) # The rank of the Cholesky factor is just the rank of the # coefficient covariance matrix. if observed: coef_U = self.coef_U m = self.m else: coef_U = self.unobs_coef_U m = self.unobs_m # The Cholesky factor is the Cholesky factor of the basis times # the basis evaluated on x. This isn't triangular, but it does have # the property U.T*U = self(x,x). U_return = asmatrix(self.eval_basis(x, regularize=False)) U_return = coef_U * U_return if apply_pivot: # Good for users. return U_return else: # Good for self.observe. return {'piv': piv_return, 'U': U_return}
def __call__(self, x, regularize=True): # TODO: check repeats for basis covariances too. # TODO: Do the timing trick down through this method to see where the bottleneck is. # Record original shape of x and regularize it. orig_shape = shape(x) if len(orig_shape)>1: orig_shape = orig_shape[:-1] if regularize: if any(isnan(x)): raise ValueError, 'Input argument to Realization contains NaNs.' x = regularize_array(x) if x is self.x_sofar: return self.f_sofar if self.check_repeats: # use caching_call to save duplicate calls. f, self.x_sofar, self.f_sofar = caching_call(self.draw_vals, x, self.x_sofar, self.f_sofar) else: # Call to self.draw_vals. f = self.draw_vals(x) if regularize: return f.reshape(orig_shape) else: return f
def __call__(self, x, regularize=True): # TODO: check repeats for basis covariances too. # If initial values were passed in, observe on them. if self.need_init_obs: self._init_obs() # Record original shape of x and regularize it. orig_shape = shape(x) if len(orig_shape)>1: orig_shape = orig_shape[:-1] if regularize: if any(isnan(x)): raise ValueError, 'Input argument to Realization contains NaNs.' x = regularize_array(x) if x is self.x_sofar: return self.f_sofar if self.check_repeats: # use caching_call to save duplicate calls. f, self.x_sofar, self.f_sofar = caching_call(self.draw_vals, x, self.x_sofar, self.f_sofar) else: # Call to self.draw_vals. f = self.draw_vals(x) if regularize: return f.reshape(orig_shape) else: return f
def eval_basis(self, x, regularize=True): """ basis_mat = C.eval_basis(x) Evaluates self's basis functions on x and returns them stacked in a matrix. basis_mat[i,j] gives basis function i (formed by multiplying basis functions) evaluated at x[j,:]. """ # Make object of same shape as self.basis, fill in with evals of each individual basis factor. # Make object of same shape as diag(coef_cov), fill in with products of those evals. # Reshape and return. if regularize: x = regularize_array(x) out = zeros(self.shape+(x.shape[0],), dtype=float,order='F') # Evaluate the basis factors basis_factors = [] for i in xrange(self.ndim): basis_factors.append([]) for j in xrange(self.n_per_dim[i]): basis_factors[i].append(self.basis[i][j](x, **self.params)) out = ones((self.n, x.shape[0]), dtype=float) out_reshaped = out.reshape(self.shape + (x.shape[0],)) for ind in ndindex(self.shape): for dim in xrange(self.ndim): out_reshaped[ind] *= basis_factors[dim][ind[dim]] return out
def __call__(self, x, observed=True, regularize=True): # Record original shape of x and 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 does not match the number of spatial dimensions of the Mean instance's base mesh." # Evaluate the unobserved mean M = self.eval_fun(x, **self.params).squeeze() # Condition. Basis covariances get special treatment. See documentation for formulas. if self.observed and observed: M = self.C._obs_eval(self, M, x) return M.reshape(orig_shape)
def eval_basis(self, x, regularize=True): """ basis_mat = C.eval_basis(x) Evaluates self's basis functions on x and returns them stacked in a matrix. basis_mat[i,j] gives basis function i (formed by multiplying basis functions) evaluated at x[j,:]. """ # Make object of same shape as self.basis, fill in with evals of each individual basis factor. # Make object of same shape as diag(coef_cov), fill in with products of those evals. # Reshape and return. if regularize: x = regularize_array(x) out = zeros(self.shape + (x.shape[0], ), dtype=float, order='F') # Evaluate the basis factors basis_factors = [] for i in xrange(self.ndim): basis_factors.append([]) for j in xrange(self.n_per_dim[i]): basis_factors[i].append(self.basis[i][j](x, **self.params)) out = ones((self.n, x.shape[0]), dtype=float) out_reshaped = out.reshape(self.shape + (x.shape[0], )) for ind in ndindex(self.shape): for dim in xrange(self.ndim): out_reshaped[ind] *= basis_factors[dim][ind[dim]] return out
def eval_basis(self, x, regularize=True): """ basis_mat = C.eval_basis(x) Evaluates self's basis functions on x and returns them stacked in a matrix. basis_mat[i,j] gives basis function i evaluated at x[j,:]. """ if regularize: x = regularize_array(x) out = zeros((self.n, x.shape[0]), dtype=float, order='F') for i in xrange(self.n): out[i] = self.basis[i](x, **self.params) return out
def eval_basis(self, x, regularize = True): """ basis_mat = C.eval_basis(x) Evaluates self's basis functions on x and returns them stacked in a matrix. basis_mat[i,j] gives basis function i evaluated at x[j,:]. """ if regularize: x = regularize_array(x) out = zeros((self.n, x.shape[0]), dtype=float, order='F') for i in xrange(self.n): out[i] = self.basis[i](x, **self.params) return out
def __init__(self, M, C, init_mesh = None, init_vals = None, check_repeats = True, regularize = True): # Make internal copies of M and C. Note that subsequent observations of M and C # will not affect self. M_internal = copy.copy(M) C_internal = copy.copy(C) M_internal.C = C_internal # If initial values were specified on a mesh: if init_mesh is not None: if regularize: init_mesh = regularize_array(init_mesh) init_vals = init_vals.ravel() # Observe internal M and C with self's value on init_mesh. observe(M_internal, C_internal, obs_mesh=init_mesh, obs_vals=init_vals, obs_V=zeros(len(init_vals),dtype=float), lintrans=None, cross_validate = False) # Store init_mesh. if check_repeats: self.x_sofar = init_mesh self.f_sofar = init_vals elif check_repeats: # Store init_mesh. self.x_sofar = None self.f_sofar = None self.check_repeats = check_repeats self.M_internal = M_internal self.C_internal = C_internal
def _init_obs(self): # If initial values were specified on a mesh: if self.init_mesh is not None: if self.regularize: self.init_mesh = regularize_array(self.init_mesh) self.init_vals = self.init_vals.ravel() # Observe internal M and C with self's value on init_mesh. observe(self.M_internal, self.C_internal, obs_mesh=self.init_mesh, obs_vals=self.init_vals, obs_V=zeros(len(self.init_vals),dtype=float), lintrans=None, cross_validate = False) # Store init_mesh. if self.check_repeats: self.x_sofar = self.init_mesh self.f_sofar = self.init_vals self.need_init_obs = False
def _init_obs(self): # If initial values were specified on a mesh: if self.init_mesh is not None: if self.regularize: self.init_mesh = regularize_array(self.init_mesh) self.init_vals = self.init_vals.ravel() # Observe internal M and C with self's value on init_mesh. observe(self.M_internal, self.C_internal, obs_mesh=self.init_mesh, obs_vals=self.init_vals, obs_V=zeros(len(self.init_vals), dtype=float), lintrans=None, cross_validate=False) # Store init_mesh. if self.check_repeats: self.x_sofar = self.init_mesh self.f_sofar = self.init_vals self.need_init_obs = False
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 __call__(self, x, y=None, observed=True, regularize=True): # Record the initial shape of x and 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] # Get the correct version of the Cholesky factor of the coefficient covariance. if observed: coef_U = self.coef_U else: coef_U = self.unobs_coef_U # Safety. if self.ndim is not None: if not self.ndim == ndimx: raise ValueError, "The number of spatial dimensions of x does not match the number of spatial dimensions of the Covariance instance's base mesh." # Evaluate the Cholesky factor of self's evaluation on x. # Will be observed or not depending on which version of coef_U # is used. basis_x = self.eval_basis(x, regularize=False) basis_x = coef_U*basis_x # ========================================================== # = If only one argument is provided, return the diagonal: = # ========================================================== if y is None: # Diagonal calls done in Fortran for speed. V = basis_diag_call(basis_x) return V.reshape(orig_shape) # =========================================================== # = If the same argument is provided twice, save some work: = # =========================================================== if y is x: return basis_x.T*basis_x # ========================================= # = If y and x are different, do needful: = # ========================================= else: # Regularize y and record its original shape. 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.' # Evaluate the Cholesky factor of self's evaluation on y. # Will be observed or not depending on which version of coef_U # is used. basis_y = self.eval_basis(y, regularize=False) basis_y = coef_U*basis_y return basis_x.T*basis_y
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
def cholesky(self, x, apply_pivot = True, observed=True, nugget=None, regularize=True, rank_limit=0): """ U = C.cholesky(x[, observed=True, nugget=None, rank_limit=0]) {'pivots': piv, 'U': U} = \ C.cholesky(x, apply_pivot = False[, observed=True, nugget=None]) Computes incomplete Cholesky factorization of self(x,x). :Arguments: - `x`: The input array on which to evaluate the covariance. - `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 rank_limit > 0: raise ValueError, 'NearlyFullRankCovariance does not accept a rank_limit argument. Use Covariance instead.' if regularize: x=regularize_array(x) # Number of points in x. N_new = x.shape[0] # Special fast version for single points. if N_new==1: V = self.__call__(x, regularize = False, observed = observed) if nugget is not None: V += nugget U=asmatrix(sqrt(V)) # print U if not apply_pivot: return {'pivots': array([0]), 'U': U} else: return U C = self.__call__(x, x, regularize=False, observed=observed) if nugget is not None: for i in xrange(N_new): C[i,i] += nugget[i] # ======================================= # = Call to Fortran function ichol_full = # ======================================= U, m, piv = ichol_full(c=C, reltol=self.relative_precision) U = asmatrix(U) # Arrange output matrix and return. if m<0: raise ValueError, "Matrix does not appear to be positive semidefinite" if not apply_pivot: # Useful for self.observe and Realization.__call__. U is upper triangular. U = U[:m,:] return {'pivots': piv, 'U': U} else: # Useful for users. U.T*U = C(x,x) return U[:m,argsort(piv)]
def obs_mesh(obs_mesh=obs_mesh): return regularize_array(obs_mesh)
def __init__(self, name, M, C, mesh=None, doc="A GP realization-valued stochastic variable", init_mesh_vals = None, mesh_eval_observed = False, trace=True, cache_depth=2, verbose=False): self.conjugate=True if not isinstance(mesh, np.ndarray) and mesh is not None: raise ValueError, name + ": __init__ argument 'mesh' must be ndarray." self.mesh = regularize_array(mesh) # Safety if isinstance(M, pm.Deterministic): if not isinstance(M.value, Mean): raise ValueError, 'GP' + self.__name__ + ': Argument M must be Mean instance '+\ 'or pm.Deterministic valued as Mean instance, got pm.Deterministic valued as ' + M.__class__.__name__ elif not isinstance(M, Mean): raise ValueError, 'GP' + self.__name__ + ': Argument M must be Mean instance '+\ 'or pm.Deterministic valued as Mean instance, got ' + M.__class__.__name__ if isinstance(C, pm.Deterministic): if not isinstance(C.value, Covariance): raise ValueError, 'GP' + self.__name__ + ': Argument C must be Covariance instance '+\ 'or pm.Deterministic valued as Covariance instance, got pm.Deterministic valued as ' + C.__class__.__name__ elif not isinstance(C, Covariance): raise ValueError, 'GP' + self.__name__ + ': Argument C must be Covariance instance '+\ 'or pm.Deterministic valued as Covariance instance, got ' + C.__class__.__name__ # This function will be used to draw values for self conditional on self's parents. def random_fun(M,C): return Realization(M,C) self._random = random_fun if self.mesh is not None: self.mesh = regularize_array(self.mesh) @pm.deterministic(verbose=verbose-1, cache_depth=cache_depth, trace=False) def M_mesh(M=M, mesh=self.mesh): """ The mean function evaluated on the mesh, cached for speed. """ return M(mesh, regularize=False) M_mesh.__name__ = name + '_M_mesh' @pm.deterministic(verbose=verbose-1, cache_depth=cache_depth, trace=False) def C_and_U_mesh(C=C, mesh=self.mesh): """ The upper-triangular Cholesky factor of the covariance function evaluated on the mesh, cached for speed. """ # TODO: Will need to change this when sparse covariances # are available. dpotrf doesn't know about sparse covariances. C_old = C C = copy.copy(C) # Most covariances generate U_mesh automatically when observed, # and after observation they can be used to efficiently construct realizations. try: junk1, junk2, U = C.observe(mesh, np.zeros(mesh.shape[0]), assume_full_rank=True) except np.linalg.LinAlgError: return np.linalg.LinAlgError # Handle BasisCovariances separately if U is None: try: U = np.linalg.cholesky(C_old(mesh,mesh,regularize=False)).T except np.linalg.LinAlgError: return np.linalg.LinAlgError # print U.T*U - C_old(mesh,mesh,regularize=False) if U.shape[0] < U.shape[1]: return np.linalg.LinAlgError return U, C C_and_U_mesh.__name__ = name + 'C_and_U_mesh' self.M_mesh = M_mesh self.C_and_U_mesh = C_and_U_mesh def logp_fun(value, M, C): if self.mesh is None: if self.verbose > 1: print '\t%s: No mesh, returning 0.' % self.__name__ return 0. elif self.C_and_U_mesh.value is np.linalg.LinAlgError: if self.verbose > 1: print '\t%s: Covariance not positive definite on mesh, returning -infinity.' % self.__name__ return -np.Inf else: if self.verbose > 1: print '\t%s: Computing log-probability.' % self.__name__ return linalg_utils.gp_array_logp(value(self.mesh), self.M_mesh.value, self.C_and_U_mesh.value[0]) @pm.deterministic(verbose=verbose-1, cache_depth=cache_depth, trace=False) def init_val(M=M, C=C, init_mesh = self.mesh, init_mesh_vals = init_mesh_vals): if init_mesh_vals is not None: return Realization(M, C, init_mesh, init_mesh_vals.ravel(), regularize=False) else: return Realization(M, C) init_val.__name__ = name + '_init_val' pm.Stochastic.__init__( self, logp=logp_fun, doc=doc, name=name, parents={'M': M, 'C': C}, dtype=np.dtype('object'), random = random_fun, trace=trace, value=init_val.value, observed=False, cache_depth=cache_depth, verbose = verbose)
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 __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 __call__(self, x, y=None, observed=True, regularize=True, return_Uo_Cxo=False): # Record the initial shape of x and 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] # Get the correct version of the Cholesky factor of the coefficient covariance. if observed: coef_U = self.coef_U else: coef_U = self.unobs_coef_U # Safety. if self.ndim is not None: if not self.ndim == ndimx: raise ValueError, "The number of spatial dimensions of x does not match the number of spatial dimensions of the Covariance instance's base mesh." # Evaluate the Cholesky factor of self's evaluation on x. # Will be observed or not depending on which version of coef_U # is used. basis_x_ = self.eval_basis(x, regularize=False) basis_x = coef_U * basis_x_ # ========================================================== # = If only one argument is provided, return the diagonal: = # ========================================================== if y is None: # Diagonal calls done in Fortran for speed. V = basis_diag_call(basis_x) if return_Uo_Cxo: return V.reshape(orig_shape), basis_x_ else: return V.reshape(orig_shape) # =========================================================== # = If the same argument is provided twice, save some work: = # =========================================================== if y is x: if return_Uo_Cxo: return basis_x.T * basis_x, basis_x_ else: return basis_x # ========================================= # = If y and x are different, do needful: = # ========================================= else: # Regularize y and record its original shape. 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.' # Evaluate the Cholesky factor of self's evaluation on y. # Will be observed or not depending on which version of coef_U # is used. basis_y = self.eval_basis(y, regularize=False) basis_y = coef_U * basis_y return basis_x.T * basis_y
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 __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 cholesky(self, x, apply_pivot=True, observed=True, nugget=None, regularize=True, rank_limit=0): """ U = C.cholesky(x[, observed=True, nugget=None, rank_limit=00]) {'pivots': piv, 'U': U} = \ C.cholesky(x, apply_pivot = False[, observed=True, nugget=None]) Computes incomplete Cholesky factorization of self(x,x), without actually evaluating the matrix first. :Arguments: - `x`: The input array on which to evaluate the covariance. - `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) # Number of points in x. N_new = x.shape[0] # diagonal diag = self.__call__(x, y=None, regularize=False, observed=observed) if nugget is not None: diag += nugget.ravel() # Special fast version for single points. if N_new == 1: U = asmatrix(sqrt(diag)) # print U if not apply_pivot: return {'pivots': array([0]), 'U': U} else: return U # Create the diagonal and the get-row function differently depending on whether self # has been observed. If self hasn't been observed, send the calls straight to eval_fun # to skip the extra formatting. # get-row function # TODO: Forbid threading here due to callbacks. def rowfun(i, xpiv, rowvec): """ A function that can be used to overwrite an input array with rows. """ rowvec[i:] = self.__call__(x=xpiv[i - 1, :].reshape((1, -1)), y=xpiv[i:, :], regularize=False, observed=observed) # ================================== # = Call to Fortran function ichol = # ================================== if rank_limit == 0: rank_limit = N_new U, m, piv = ichol(diag=diag, reltol=self.relative_precision, rowfun=rowfun, x=x, rl=min(rank_limit, N_new)) U = asmatrix(U) # Arrange output matrix and return. if m < 0: raise ValueError, "Matrix does not appear to be positive semidefinite" if not apply_pivot: # Useful for self.observe and Realization.__call__. U is upper triangular. U = U[:m, :] return {'pivots': piv, 'U': U} else: # Useful for users. U.T*U = C(x,x) return U[:m, argsort(piv)]
def cholesky(self, x, apply_pivot = True, observed=True, nugget=None, regularize=True, rank_limit=0): """ U = C.cholesky(x[, observed=True, nugget=None, rank_limit=00]) {'pivots': piv, 'U': U} = \ C.cholesky(x, apply_pivot = False[, observed=True, nugget=None]) Computes incomplete Cholesky factorization of self(x,x), without actually evaluating the matrix first. :Arguments: - `x`: The input array on which to evaluate the covariance. - `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) # Number of points in x. N_new = x.shape[0] # diagonal diag = self.__call__(x, y=None, regularize=False, observed=observed) if nugget is not None: diag += nugget.ravel() # Special fast version for single points. if N_new==1: U=asmatrix(sqrt(diag)) # print U if not apply_pivot: return {'pivots': array([0]), 'U': U} else: return U # Create the diagonal and the get-row function differently depending on whether self # has been observed. If self hasn't been observed, send the calls straight to eval_fun # to skip the extra formatting. # get-row function # TODO: Forbid threading here due to callbacks. def rowfun(i,xpiv,rowvec): """ A function that can be used to overwrite an input array with rows. """ rowvec[i:]=self.__call__(x=xpiv[i-1,:].reshape((1,-1)), y=xpiv[i:,:], regularize=False, observed=observed) # ================================== # = Call to Fortran function ichol = # ================================== if rank_limit == 0: rank_limit = N_new U, m, piv = ichol(diag=diag, reltol=self.relative_precision, rowfun=rowfun, x=x, rl=min(rank_limit,N_new)) U = asmatrix(U) # Arrange output matrix and return. if m<0: raise ValueError, "Matrix does not appear to be positive semidefinite" if not apply_pivot: # Useful for self.observe and Realization.__call__. U is upper triangular. U = U[:m,:] return {'pivots': piv, 'U': U} else: # Useful for users. U.T*U = C(x,x) return U[:m,argsort(piv)]
def cholesky(self, x, apply_pivot = True, observed=True, nugget=None, regularize=True): """ U = C.cholesky(x[, observed=True, nugget=None]) {'pivots': piv, 'U': U} = \ C.cholesky(x, apply_pivot = False[, observed=True, nugget=None]) Computes incomplete Cholesky factorization of self(x,x). :Arguments: - `x`: The input array on which to evaluate the covariance. - `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) # Number of points in x. N_new = x.shape[0] # Special fast version for single points. if N_new==1: U=asmatrix(sqrt(self.__call__(x, regularize = False, observed = observed))) # print U if not apply_pivot: return {'pivots': array([0]), 'U': U} else: return U C = self.__call__(x, x, regularize=False, observed=observed) if nugget is not None: for i in xrange(N_new): C[i,i] += nugget[i] # ======================================= # = Call to Fortran function ichol_full = # ======================================= U, m, piv = ichol_full(c=C, reltol=self.relative_precision) U = asmatrix(U) # Arrange output matrix and return. if m<0: raise ValueError, "Matrix does not appear to be positive semidefinite" if not apply_pivot: # Useful for self.observe and Realization.__call__. U is upper triangular. U = U[:m,:] return {'pivots': piv, 'U': U} else: # Useful for users. U.T*U = C(x,x) return U[:m,argsort(piv)]
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 __init__(self, name, M, C, mesh=None, doc="A GP realization-valued stochastic variable", init_mesh_vals=None, mesh_eval_observed=False, trace=True, cache_depth=2, verbose=False): self.conjugate = True if not isinstance(mesh, np.ndarray) and mesh is not None: raise ValueError, name + ": __init__ argument 'mesh' must be ndarray." self.mesh = regularize_array(mesh) # Safety if isinstance(M, pm.Deterministic): if not isinstance(M.value, Mean): raise ValueError, 'GP' + self.__name__ + ': Argument M must be Mean instance '+\ 'or pm.Deterministic valued as Mean instance, got pm.Deterministic valued as ' + M.__class__.__name__ elif not isinstance(M, Mean): raise ValueError, 'GP' + self.__name__ + ': Argument M must be Mean instance '+\ 'or pm.Deterministic valued as Mean instance, got ' + M.__class__.__name__ if isinstance(C, pm.Deterministic): if not isinstance(C.value, Covariance): raise ValueError, 'GP' + self.__name__ + ': Argument C must be Covariance instance '+\ 'or pm.Deterministic valued as Covariance instance, got pm.Deterministic valued as ' + C.__class__.__name__ elif not isinstance(C, Covariance): raise ValueError, 'GP' + self.__name__ + ': Argument C must be Covariance instance '+\ 'or pm.Deterministic valued as Covariance instance, got ' + C.__class__.__name__ # This function will be used to draw values for self conditional on self's parents. def random_fun(M, C): return Realization(M, C) self._random = random_fun if self.mesh is not None: self.mesh = regularize_array(self.mesh) @pm.deterministic(verbose=verbose - 1, cache_depth=cache_depth, trace=False) def M_mesh(M=M, mesh=self.mesh): """ The mean function evaluated on the mesh, cached for speed. """ return M(mesh, regularize=False) M_mesh.__name__ = name + '_M_mesh' @pm.deterministic(verbose=verbose - 1, cache_depth=cache_depth, trace=False) def C_and_U_mesh(C=C, mesh=self.mesh): """ The upper-triangular Cholesky factor of the covariance function evaluated on the mesh, cached for speed. """ # TODO: Will need to change this when sparse covariances # are available. dpotrf doesn't know about sparse covariances. C_old = C C = copy.copy(C) # Most covariances generate U_mesh automatically when observed, # and after observation they can be used to efficiently construct realizations. try: junk1, junk2, U = C.observe(mesh, np.zeros(mesh.shape[0]), assume_full_rank=True) except np.linalg.LinAlgError: return np.linalg.LinAlgError # Handle BasisCovariances separately if U is None: try: U = np.linalg.cholesky( C_old(mesh, mesh, regularize=False)).T except np.linalg.LinAlgError: return np.linalg.LinAlgError # print U.T*U - C_old(mesh,mesh,regularize=False) if U.shape[0] < U.shape[1]: return np.linalg.LinAlgError return U, C C_and_U_mesh.__name__ = name + 'C_and_U_mesh' self.M_mesh = M_mesh self.C_and_U_mesh = C_and_U_mesh def logp_fun(value, M, C): if self.mesh is None: if self.verbose > 1: print '\t%s: No mesh, returning 0.' % self.__name__ return 0. elif self.C_and_U_mesh.value is np.linalg.LinAlgError: if self.verbose > 1: print '\t%s: Covariance not positive definite on mesh, returning -infinity.' % self.__name__ return -np.Inf else: if self.verbose > 1: print '\t%s: Computing log-probability.' % self.__name__ return linalg_utils.gp_array_logp(value(self.mesh), self.M_mesh.value, self.C_and_U_mesh.value[0]) @pm.deterministic(verbose=verbose - 1, cache_depth=cache_depth, trace=False) def init_val(M=M, C=C, init_mesh=self.mesh, init_mesh_vals=init_mesh_vals): if init_mesh_vals is not None: return Realization(M, C, init_mesh, init_mesh_vals.ravel(), regularize=False) else: return Realization(M, C) init_val.__name__ = name + '_init_val' pm.Stochastic.__init__(self, logp=logp_fun, doc=doc, name=name, parents={ 'M': M, 'C': C }, random=random_fun, trace=trace, value=init_val.value, observed=False, cache_depth=cache_depth, verbose=verbose)