Exemple #1
0
    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)
Exemple #2
0
    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}
Exemple #4
0
    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}
Exemple #5
0
    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
Exemple #7
0
    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
Exemple #8
0
    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
Exemple #11
0
    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
Exemple #12
0
    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
Exemple #14
0
    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)]
Exemple #16
0
    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
Exemple #17
0
    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)]
Exemple #19
0
 def obs_mesh(obs_mesh=obs_mesh):
     return regularize_array(obs_mesh)
Exemple #20
0
    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)]
Exemple #22
0
    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
Exemple #24
0
    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
Exemple #26
0
    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)]
Exemple #30
0
 def obs_mesh(obs_mesh=obs_mesh):
     return regularize_array(obs_mesh)
Exemple #31
0
    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)