Example #1
0
def _repeated_svd(x, lwork, overwrite_a=False):
    """Mimic scipy.linalg.svd, avoid lwork and get_lapack_funcs overhead."""
    if x.dtype == np.float64:
        gesdd, gesvd = dgesdd, zgesdd
    else:
        assert x.dtype == np.complex128
        gesdd, gesvd = zgesdd, zgesvd
    # this has to use overwrite_a=False in case we need to fall back to gesvd
    u, s, v, info = gesdd(x,
                          compute_uv=True,
                          lwork=lwork[0],
                          full_matrices=False,
                          overwrite_a=False)
    if info > 0:
        # Fall back to slower gesvd, sometimes gesdd fails
        u, s, v, info = gesvd(x,
                              compute_uv=True,
                              lwork=lwork[1],
                              full_matrices=False,
                              overwrite_a=overwrite_a)
    if info > 0:
        raise LinAlgError("SVD did not converge")
    if info < 0:
        raise ValueError('illegal value in %d-th argument of internal gesdd' %
                         -info)
    return u, s, v
Example #2
0
File: base.py Project: juijan/sisl
def solve(a, b, overwrite_a=False, overwrite_b=False):
    """
    Solve a linear system ``a x = b``

    Parameters
    ----------
    a : (N, N) array_like
       left-hand-side matrix
    b : (N, NRHS) array_like
       right-hand-side matrix
    overwrite_a : bool, optional
       whether we are allowed to overwrite the matrix `a`
    overwrite_b : bool, optional
       whether we are allowed to overwrite the matrix `b`

    Returns
    -------
    x : (N, NRHS) numpy.ndarray
        solution matrix
    """
    a1 = atleast_2d(_asarray_validated(a, check_finite=False))
    b1 = atleast_1d(_asarray_validated(b, check_finite=False))
    n = a1.shape[0]

    overwrite_a = overwrite_a or _datacopied(a1, a)
    overwrite_b = overwrite_b or _datacopied(b1, b)

    if a1.shape[0] != a1.shape[1]:
        raise ValueError('LHS needs to be a square matrix.')

    if n != b1.shape[0]:
        # Last chance to catch 1x1 scalar a and 1D b arrays
        if not (n == 1 and b1.size != 0):
            raise ValueError('Input b has to have same number of rows as '
                             'input a')

    # regularize 1D b arrays to 2D
    if b1.ndim == 1:
        if n == 1:
            b1 = b1[None, :]
        else:
            b1 = b1[:, None]
        b_is_1D = True
    else:
        b_is_1D = False

    gesv = get_lapack_funcs('gesv', (a1, b1))
    _, _, x, info = gesv(a1,
                         b1,
                         overwrite_a=overwrite_a,
                         overwrite_b=overwrite_b)
    if info > 0:
        raise LinAlgError("Singular matrix")
    elif info < 0:
        raise ValueError('illegal value in %d-th argument of internal '
                         'gesv' % -info)
    if b_is_1D:
        return x.ravel()

    return x
Example #3
0
def _check_info(info, driver, positive='did not converge (LAPACK info=%d)'):
    """Check info return value."""
    if info < 0:
        raise ValueError('illegal value in argument %d of internal %s'
                         % (-info, driver))
    if info > 0 and positive:
        raise LinAlgError(("%s " + positive) % (driver, info,))
Example #4
0
    def logposterior(self, params):
        r"""
        Compute the negative log posterior for a particular set of parameters

        Computes the negative log posterior (negative marginal log-likelihood minus any
        prior log-probabilities). This is computed on the root process and then broadcast
        to all processes.

        The main computational expense is computing the prior mean and covariance, which only
        needs to be done once and can be cached. This also requires computing the Cholesky
        decomposition of the covariance plus model discrepancy.

        New parameters must be a numpy array of length 3. First parameter is the data/model
        scaling factor :math:`{\rho}`, second parameter is the model discrepancy covariance,
        and the third parameter is the model discrepancy correlation length. All parameters
        are assumed to be on a logarithmic scale to enforce positivity.

        :param params: New set of parameters (must be a numpy array of length 3)
        :type params: ndarray
        :returns: negative log posterior
        :rtype: float
        """

        self.set_params(params)
        rho = np.exp(self.params[0])

        if self.Cu is None or self.mu is None:
            self.solve_prior()

        # compute log-likelihood on root process and broadcast

        if COMM_WORLD.rank == 0:
            KCu = rho**2 * self.Cu + self.data.calc_K_plus_sigma(
                self.params[1:])
            try:
                L = cho_factor(KCu)
            except LinAlgError:
                raise LinAlgError(
                    "Error attempting to factorize the covariance matrix " +
                    "in model_loglikelihood")
            invKCudata = cho_solve(L, self.data.get_data() - rho * self.mu)
            log_posterior = 0.5 * (
                self.data.get_n_obs() * np.log(2. * np.pi) +
                2. * np.sum(np.log(np.diag(L[0]))) +
                np.dot(self.data.get_data() - rho * self.mu, invKCudata))
            for i in range(3):
                if not self.priors[i] is None:
                    log_posterior -= self.priors[i].logp(self.params[i])
        else:
            log_posterior = None

        log_posterior = COMM_WORLD.bcast(log_posterior, root=0)

        assert not log_posterior is None, "error in broadcasting the log likelihood"

        COMM_WORLD.barrier()

        return log_posterior
Example #5
0
def solve_triangular(A, B, trans=False):
    """
    Solve the system Ax=B where A is lower triangular. If `trans` is True then
    solve the system A'x=B.
    """
    X, info = lapack.dtrtrs(A, B, lower=1, trans=int(trans))
    if info != 0:
        raise LinAlgError('Matrix is singular')
    return X
Example #6
0
def cholesky_inverse(A):
    """
    Compute the inverse of the matrix AA' given its cholesky decomposition A.
    """
    X, info = lapack.dpotri(A, lower=1)
    if info != 0:
        raise LinAlgError('Matrix is not invertible')
    triu = np.triu_indices_from(X, k=1)
    X[triu] = X.T[triu]
    return X
Example #7
0
def cholesky(A, maxtries=5):
    """
    Compute the cholesky of A. If the matrix is singular make `maxtries`
    additional attempts to invert it by adding small amounts to the diagonal
    before giving up.
    """
    L, info = lapack.dpotrf(A, lower=1)
    if info == 0:
        return L
    else:
        d = A.diagonal()
        if np.any(d <= 0):
            raise LinAlgError('Matrix has non-positive diagonal elements')
        j = d.mean() * 1e-6
        for _ in xrange(maxtries):
            L, info = lapack.dpotrf(add_diagonal(A, j, True), lower=1)
            if info == 0:
                message = 'jitter of {:s} required to compute the cholesky'
                warnings.warn(message.format(str(j)), stacklevel=2)
                return L
            else:
                j *= 10
        raise LinAlgError('Matrix is not positive definite, even with jitter')
    def f(self, *args, **kwargs):

        refactor_now = False
        out = None

        if method.__name__ == "update":
            # this will get zeroed if we refactor
            self.updates += 1

            # if average solve time is increasing, then it would
            # be faster to refactor from scratch
            slowing_down = (self.average_solve_times[1] >
                            self.average_solve_times[0])

            # if update limit is reached, we should refactor to
            # limit error buildup
            too_many_updates = self.updates >= self.max_updates

            if self.mast:
                refactor_now = (slowing_down or too_many_updates)
            else:
                refactor_now = too_many_updates

            if refactor_now:
                # update basis indices and factor from scratch
                self.update_basis(*args, **kwargs)
                out = self.refactor()  # time will be recorded

        # If refactor_now is True, then self.refactor() is called
        # We don't want to call method = self.update again here
        if not refactor_now:
            # record the time it took to call the method
            t0 = timer()
            out = method(self, *args, **kwargs)
            if isinstance(out, np.ndarray) and np.any(np.isnan(out)):
                raise LinAlgError("Nans in output")
            t1 = timer()
            self.bglu_time += (t1 - t0)

        # calculate average solve time,
        # considering all significant method calls
        if method.__name__ == "solve":
            self.solves += 1
            avg = self.bglu_time / self.solves
            self.average_solve_times = [self.average_solve_times[1], avg]

        return out
Example #9
0
def solve_posdef(A, b):
    """
    Solve the system :math:`A X = b` for :math:`X` where :math:`A` is a
    positive semi-definite matrix.

    This first tries cholesky, and if numerically unstable with solve using a
    truncated SVD (see solve_posdef_svd).

    The log-determinant of :math:`A` is also returned since it requires minimal
    overhead.

    Parameters
    ----------
    A: ndarray
        A positive semi-definite matrix.
    b: ndarray
        An array or matrix

    Returns
    -------
    X: ndarray
        The result of :math:`X = A^-1 b`
    logdet: float
        The log-determinant of :math:`A`
    """

    # Try cholesky for speed
    try:
        lower = False
        L = cholesky(A, lower=lower)
        if any(L.diagonal() < cholthresh):
            raise LinAlgError("Unstable cholesky factor detected")
        X = cho_solve((L, lower), b)
        logdet = cho_log_det(L)

    # Failed cholesky, use svd to do the inverse
    except LinAlgError:

        U, s, V = svd(A)
        X = svd_solve(U, s, V, b)
        logdet = svd_log_det(s)

    return X, logdet
Example #10
0
def eigh(a, overwrite_a=False, check_finite=True):
    """Efficient wrapper for eigh.

    Parameters
    ----------
    a : ndarray, shape (n_components, n_components)
        The symmetric array operate on.
    overwrite_a : bool
        If True, the contents of a can be overwritten for efficiency.
    check_finite : bool
        If True, check that all elements are finite.

    Returns
    -------
    w : ndarray, shape (n_components,)
        The N eigenvalues, in ascending order, each repeated according to
        its multiplicity.
    v : ndarray, shape (n_components, n_components)
        The normalized eigenvector corresponding to the eigenvalue ``w[i]``
        is the column ``v[:, i]``.
    """
    # We use SYEVD, see https://github.com/scipy/scipy/issues/9212
    if check_finite:
        a = _asarray_validated(a, check_finite=check_finite)
    if a.dtype == np.float64:
        evr, driver = dsyevd, 'syevd'
    else:
        assert a.dtype == np.complex128
        evr, driver = zheevd, 'heevd'
    w, v, info = evr(a, lower=1, overwrite_a=overwrite_a)
    if info == 0:
        return w, v
    if info < 0:
        raise ValueError('illegal value in argument %d of internal %s' %
                         (-info, driver))
    else:
        raise LinAlgError("internal fortran routine failed to converge: "
                          "%i off-diagonal elements of an "
                          "intermediate tridiagonal form did not converge"
                          " to zero." % info)
Example #11
0
def _repeated_inv(a, lwork, overwrite_a=False):
    if a.dtype == np.float64:
        getrf, getri = dgetrf, dgetri
    else:
        assert a.dtype == np.complex128
        getrf, getri = zgetrf, zgetri
    lu, piv, info = getrf(a, overwrite_a=overwrite_a)
    if info == 0:
        # XXX: the following line fixes curious SEGFAULT when
        # benchmarking 500x500 matrix inverse. This seems to
        # be a bug in LAPACK ?getri routine because if lwork is
        # minimal (when using lwork[0] instead of lwork[1]) then
        # all tests pass. Further investigation is required if
        # more such SEGFAULTs occur.
        lwork = int(1.01 * lwork)
        inv_a, info = getri(lu, piv, lwork=lwork, overwrite_lu=1)
    if info > 0:
        raise LinAlgError("singular matrix")
    if info < 0:
        raise ValueError('illegal value in %d-th argument of internal '
                         'getrf|getri' % -info)
    return inv_a
Example #12
0
    def solve_posterior_generating(self):
        r"""
        Solve for the posterior of the generating process

        This method solves for the posterior of the generating process before looking at the data.
        The main computational cost is solving for the prior of the covariance, so if this is
        cached from a previous solve this is a simple calculation.

        :returns: FEM posterior mean and covariance of the true generating process (as a tuple of
                  numpy arrays) on the root process. Non-root processes return numpy arrays of
                  shape ``(0,)`` (mean) and ``(0, 0)`` (covariance).
        :rtype: tuple of ndarrays
        """

        # create interpolation matrix if not cached

        m_eta, C_eta = self.solve_prior_generating()

        if self.ensemble_comm.rank == 0 and self.G.comm.rank == 0:
            try:
                L = cho_factor(self.data.get_unc()**2 *
                               np.eye(self.data.get_n_obs()) + C_eta)
            except LinAlgError:
                raise LinAlgError(
                    "Cholesky factorization of the covariance matrix failed")

            C_etay = cho_solve(L, self.data.get_unc()**2 * C_eta)

            m_etay = cho_solve(
                L,
                np.dot(C_eta, self.data.get_data()) +
                self.data.get_unc()**2 * m_eta)
        else:
            m_etay = np.zeros(0)
            C_etay = np.zeros((0, 0))

        return m_etay, C_etay
Example #13
0
File: base.py Project: juijan/sisl
def inv(a, overwrite_a=False):
    """
    Inverts a matrix

    Parameters
    ----------
    a : (N, N) array_like
       the matrix to be inverted.
    overwrite_a : bool, optional
       whether we are allowed to overwrite the matrix `a`

    Returns
    -------
    x : (N, N) numpy.ndarray
        The inverted matrix
    """
    a1 = atleast_2d(_asarray_validated(a, check_finite=False))

    overwrite_a = overwrite_a or _datacopied(a1, a)

    if a1.shape[0] != a1.shape[1]:
        raise ValueError('Input a needs to be a square matrix.')

    getrf, getri, getri_lwork = get_lapack_funcs(
        ('getrf', 'getri', 'getri_lwork'), (a1, ))
    lu, piv, info = getrf(a1, overwrite_a=overwrite_a)
    if info == 0:
        lwork = _compute_lwork(getri_lwork, a1.shape[0])
        lwork = int(1.01 * lwork)
        x, info = getri(lu, piv, lwork=lwork, overwrite_lu=True)
    if info > 0:
        raise LinAlgError("Singular matrix")
    elif info < 0:
        raise ValueError('illegal value in %d-th argument of internal '
                         'getrf|getri' % -info)
    return x
Example #14
0
def LSfit(funct, data, X, param0, weights=-1, quiet=True, **kwargs):
    """ 
    #####  USAGE  #####
     Least-square minimization algorithm.
     Parametric least-square fitting.
     
     Minimises Xhi2 = sum(weights*(f(X,param) - data)^2)
     where the sum(.) runs over the coordinates X
     minimization is performed over the unknown "param"
     "data" is the noisy observation
     "f(X,param)" is your model, set by the vector of parameters "param" and evaluated on coordinates X
    
    Algorithm is Levenberg-Marquardt, with adaptive damping factor
    
    #####  SYNTAX  #####
    Two syntaxes are possible
    
    param        = LSfit(funct,data,X,param0)
    param,status = LSfit(funct,data,X,param0)
    
    "param"  is the returned solution
    "status" contains extra information about convergence
             status = {"xhi2","mu","param"}
    
    #####  INPUTS  #####
     funct   - [Function] The function to call
     data    - [Vector] Noisy data
     X       - [Vector] Coordinates where to evaluate the function
     param0  - [Vector,LSparam] First estimation of parameters
                 param0 can be simply a vector
                 if param0 is a LSparam, you can define bounds, fixed values...
                 see LSparam documentation
    
    ##### OPTIONAL INPUTS #####
     weights - [Vector] Weights (1/noise^2) on each coordinate X
                  Default : no weighting (weights=1)
                  weights = 1/sigma^2 for a gaussian noise
                  weights = 1/data for a Poisson-like noise
     quiet   - [Boolean] Don't print status of algorithm
                  Default : quiet = True
    **kwargs - [Dictionary] Use a dictionary to pass some keywords to your function
    
    #####  OUTPUT  #####
     param   - [Vector] Best parameters for least-square fitting
     status  - [dictionary] Optional output, contains some information about convergence
    """
    # INITIALIZATION
    Xarr = array(X)
    sX = size(Xarr)
    param = LSparam(param0)
    param.initParam()
    mu_cond = 0
    Id = eye(param.nb_valid_param)  # Identity matrix for conditioning

    J = zeros((sX, param.nb_param))  # Jacobian
    h = 1e-9  #step to compute Jacobian
    bad_cond = 1e10  #conditioning higher than this value is bad

    status = {"xhi2": [], "mu": [], "param": []}
    nout = _n_out(offset=3)
    if nout > 2:
        raise ValueError(
            "Number of outpt argument for LSfit can be only 1 or 2")

    # STOPPING CONDITIONS
    J_min = 1e-5 * param.nb_param**2  # stopping criteria based on small Jacobian
    dp_min = 1e-7  #(1e-8) stopping criteria based on small variation of parameters
    max_iter = 1e4  # stopping criteria based on max number of iteration
    stop_loop = False  # boolean for stopping the loop
    stop_trace = "LSfit started but not finished correctly"

    # WEIGHTS
    if size(weights) == 1 and weights <= 0:
        weights = zeros(sX) + 1
    elif size(weights) > 1 and size(weights) != sX:
        raise ValueError("WEIGHTS should be a scalar or same size as X")

    # LEVENBERG-MARQUARDT
    mu = 1

    # Print some information
    f = funct(Xarr, param.value, **kwargs)
    Xhi2 = sum(weights * (f - data)**2)
    status["xhi2"].append(Xhi2)
    if not quiet:
        print("[Iter=0] Xhi2 = " + str(Xhi2))

    # LOOP

    iteration = 0
    f = funct(Xarr, param.value, **kwargs)

    while (iteration < max_iter) and not stop_loop:

        mu_cond = 0

        ## Iterate over each parameter to compute derivative
        for ip in param.validValue:
            J[:, ip] = weights * (funct(Xarr, param.step(ip, h), **kwargs) -
                                  f) / h

        ## Compute dvalue = -{(transpose(J)*J)^(-1)}*transpose(J)*(weights*(func-data))
        ## Reduce the matrix J only on the moving parameters
        JTJ = dot(J[:, param.validValue].T, J[:, param.validValue])
        JTJ += mu * diag(JTJ.diagonal())

        ## Try to improve conditioning
        c = cond(JTJ)
        if c > bad_cond:
            mu_cond = _improve_cond(JTJ, bad_cond, Id)

        ## Try inversion, here we might encounter numerical issues
        try:
            param.dvalue[param.validValue] = -dot(
                dot(inv(JTJ + mu_cond * Id), J[:, param.validValue].T),
                weights * (f - data))
        except LinAlgError as exception_message:
            _print_info_on_error(JTJ, iteration, mu, param.value)
            raise LinAlgError(exception_message)
        except ValueError as exception_message:
            _print_info_on_error(JTJ, iteration, mu, param.value)
            raise ValueError(exception_message)

        ## Xhi square old
        Xhi2 = sum(weights * (f - data)**2)

        if nout == 2:
            status["xhi2"].append(Xhi2)
            status["mu"].append(mu)
            status["param"].append(param.value)

        # Step forward with dvalue
        param.forward()
        # New value of f(.)
        f = funct(Xarr, param.value, **kwargs)

        ## Levenberg-Marquardt update for mu
        Xhi2_new = sum(weights * (f - data)**2)
        if Xhi2_new > Xhi2:
            mu = min(10 * mu, 1e10)
        else:
            mu = max(0.1 * mu, 1e-10)

        ## Stop loop based on small variation of parameter
        if param.dvalueNorm() < dp_min * param.valueNorm():
            stop_loop = True
            stop_trace = "Parameter not evolving enough at iter=" + str(
                iteration)

        ## Stop loop based on small Jacobian
        if abs(J[:, param.validValue]).sum() < J_min:
            stop_loop = True
            stop_trace = "Jacobian small enough at iter=" + str(iteration)

        ## Increment Loop
        iteration += 1

    ## END of LOOP and SHOW RESULTS

    if iteration >= max_iter:
        stop_trace = "Maximum iteration reached (iter=" + str(iteration) + ")"

    if not quiet:
        print("[Iter=" + str(iteration) + "] Xhi2 = " + str(Xhi2))
        print(" ")
        print("Stopping condition: " + stop_trace)
        print(" ")

    if nout == 2:
        status["param"] = array(status["param"])
        return param.value, status

    return param.value
Example #15
0
def make_interp_spline(x,
                       y,
                       k=3,
                       t=None,
                       bc_type=None,
                       axis=0,
                       check_finite=True):
    """Compute the (coefficients of) interpolating B-spline.

    Parameters
    ----------
    x : array_like, shape (n,)
        Abscissas.
    y : array_like, shape (n, ...)
        Ordinates.
    k : int, optional
        B-spline degree. Default is cubic, k=3.
    t : array_like, shape (nt + k + 1,), optional.
        Knots.
        The number of knots needs to agree with the number of datapoints and
        the number of derivatives at the edges. Specifically, ``nt - n`` must
        equal ``len(deriv_l) + len(deriv_r)``.
    bc_type : 2-tuple or None
        Boundary conditions.
        Default is None, which means choosing the boundary conditions
        automatically. Otherwise, it must be a length-two tuple where the first
        element sets the boundary conditions at ``x[0]`` and the second
        element sets the boundary conditions at ``x[-1]``. Each of these must
        be an iterable of pairs ``(order, value)`` which gives the values of
        derivatives of specified orders at the given edge of the interpolation
        interval.
        Alternatively, the following string aliases are recognized:

        * ``"clamped"``: The first derivatives at the ends are zero. This is
           equivalent to ``bc_type=([(1, 0.0)], [(1, 0.0)])``.
        * ``"natural"``: The second derivatives at ends are zero. This is
          equivalent to ``bc_type=([(2, 0.0)], [(2, 0.0)])``.
        * ``"not-a-knot"`` (default): The first and second segments are the same
          polynomial. This is equivalent to having ``bc_type=None``.

    axis : int, optional
        Interpolation axis. Default is 0.
    check_finite : bool, optional
        Whether to check that the input arrays contain only finite numbers.
        Disabling may give a performance gain, but may result in problems
        (crashes, non-termination) if the inputs do contain infinities or NaNs.
        Default is True.

    Returns
    -------
    b : a BSpline object of the degree ``k`` and with knots ``t``.

    Examples
    --------

    Use cubic interpolation on Chebyshev nodes:

    >>> def cheb_nodes(N):
    ...     jj = 2.*np.arange(N) + 1
    ...     x = np.cos(np.pi * jj / 2 / N)[::-1]
    ...     return x

    >>> x = cheb_nodes(20)
    >>> y = np.sqrt(1 - x**2)

    >>> from scipy.interpolate import BSpline, make_interp_spline
    >>> b = make_interp_spline(x, y)
    >>> np.allclose(b(x), y)
    True

    Note that the default is a cubic spline with a not-a-knot boundary condition

    >>> b.k
    3

    Here we use a 'natural' spline, with zero 2nd derivatives at edges:

    >>> l, r = [(2, 0.0)], [(2, 0.0)]
    >>> b_n = make_interp_spline(x, y, bc_type=(l, r))  # or, bc_type="natural"
    >>> np.allclose(b_n(x), y)
    True
    >>> x0, x1 = x[0], x[-1]
    >>> np.allclose([b_n(x0, 2), b_n(x1, 2)], [0, 0])
    True

    Interpolation of parametric curves is also supported. As an example, we
    compute a discretization of a snail curve in polar coordinates

    >>> phi = np.linspace(0, 2.*np.pi, 40)
    >>> r = 0.3 + np.cos(phi)
    >>> x, y = r*np.cos(phi), r*np.sin(phi)  # convert to Cartesian coordinates

    Build an interpolating curve, parameterizing it by the angle

    >>> from scipy.interpolate import make_interp_spline
    >>> spl = make_interp_spline(phi, np.c_[x, y])

    Evaluate the interpolant on a finer grid (note that we transpose the result
    to unpack it into a pair of x- and y-arrays)

    >>> phi_new = np.linspace(0, 2.*np.pi, 100)
    >>> x_new, y_new = spl(phi_new).T

    Plot the result

    >>> import matplotlib.pyplot as plt
    >>> plt.plot(x, y, 'o')
    >>> plt.plot(x_new, y_new, '-')
    >>> plt.show()

    See Also
    --------
    BSpline : base class representing the B-spline objects
    CubicSpline : a cubic spline in the polynomial basis
    make_lsq_spline : a similar factory function for spline fitting
    UnivariateSpline : a wrapper over FITPACK spline fitting routines
    splrep : a wrapper over FITPACK spline fitting routines

    """
    # convert string aliases for the boundary conditions
    if bc_type is None or bc_type == 'not-a-knot':
        deriv_l, deriv_r = None, None
    elif isinstance(bc_type, str):
        deriv_l, deriv_r = bc_type, bc_type
    else:
        try:
            deriv_l, deriv_r = bc_type
        except TypeError:
            raise ValueError("Unknown boundary condition: %s" % bc_type)

    y = np.asarray(y)

    axis = normalize_axis_index(axis, y.ndim)

    # special-case k=0 right away
    if k == 0:
        if any(_ is not None for _ in (t, deriv_l, deriv_r)):
            raise ValueError("Too much info for k=0: t and bc_type can only "
                             "be None.")
        x = _as_float_array(x, check_finite)
        t = np.r_[x, x[-1]]
        c = np.asarray(y)
        c = np.rollaxis(c, axis)
        c = np.ascontiguousarray(c, dtype=_get_dtype(c.dtype))
        return BSpline.construct_fast(t, c, k, axis=axis)

    # special-case k=1 (e.g., Lyche and Morken, Eq.(2.16))
    if k == 1 and t is None:
        if not (deriv_l is None and deriv_r is None):
            raise ValueError(
                "Too much info for k=1: bc_type can only be None.")
        x = _as_float_array(x, check_finite)
        t = np.r_[x[0], x, x[-1]]
        c = np.asarray(y)
        c = np.rollaxis(c, axis)
        c = np.ascontiguousarray(c, dtype=_get_dtype(c.dtype))
        return BSpline.construct_fast(t, c, k, axis=axis)

    x = _as_float_array(x, check_finite)
    y = _as_float_array(y, check_finite)
    k = operator.index(k)

    # come up with a sensible knot vector, if needed
    if t is None:
        if deriv_l is None and deriv_r is None:
            if k == 2:
                # OK, it's a bit ad hoc: Greville sites + omit
                # 2nd and 2nd-to-last points, a la not-a-knot
                t = (x[1:] + x[:-1]) / 2.
                t = np.r_[(x[0], ) * (k + 1), t[1:-1], (x[-1], ) * (k + 1)]
            else:
                t = _not_a_knot(x, k)
        else:
            t = _augknt(x, k)

    t = _as_float_array(t, check_finite)

    y = np.rollaxis(y, axis)  # now internally interp axis is zero

    if x.ndim != 1 or np.any(x[1:] <= x[:-1]):
        raise ValueError("Expect x to be a 1-D sorted array_like.")
    if k < 0:
        raise ValueError("Expect non-negative k.")
    if t.ndim != 1 or np.any(t[1:] < t[:-1]):
        raise ValueError("Expect t to be a 1-D sorted array_like.")
    if x.size != y.shape[0]:
        raise ValueError('x and y are incompatible.')
    if t.size < x.size + k + 1:
        raise ValueError('Got %d knots, need at least %d.' %
                         (t.size, x.size + k + 1))
    if (x[0] < t[k]) or (x[-1] > t[-k]):
        raise ValueError('Out of bounds w/ x = %s.' % x)

    # Here : deriv_l, r = [(nu, value), ...]
    deriv_l = _convert_string_aliases(deriv_l, y.shape[1:])
    deriv_l_ords, deriv_l_vals = _process_deriv_spec(deriv_l)
    nleft = deriv_l_ords.shape[0]

    deriv_r = _convert_string_aliases(deriv_r, y.shape[1:])
    deriv_r_ords, deriv_r_vals = _process_deriv_spec(deriv_r)
    nright = deriv_r_ords.shape[0]

    # have `n` conditions for `nt` coefficients; need nt-n derivatives
    n = x.size
    nt = t.size - k - 1

    if nt - n != nleft + nright:
        raise ValueError("The number of derivatives at boundaries does not "
                         "match: expected %s, got %s+%s" %
                         (nt - n, nleft, nright))

    # set up the LHS: the collocation matrix + derivatives at boundaries
    kl = ku = k
    ab = np.zeros((2 * kl + ku + 1, nt), dtype=np.float_, order='F')
    _bspl._colloc(x, t, k, ab, offset=nleft)
    if nleft > 0:
        _bspl._handle_lhs_derivatives(t, k, x[0], ab, kl, ku, deriv_l_ords)
    if nright > 0:
        _bspl._handle_lhs_derivatives(t,
                                      k,
                                      x[-1],
                                      ab,
                                      kl,
                                      ku,
                                      deriv_r_ords,
                                      offset=nt - nright)

    # set up the RHS: values to interpolate (+ derivative values, if any)
    extradim = prod(y.shape[1:])
    rhs = np.empty((nt, extradim), dtype=y.dtype)
    if nleft > 0:
        rhs[:nleft] = deriv_l_vals.reshape(-1, extradim)
    rhs[nleft:nt - nright] = y.reshape(-1, extradim)
    if nright > 0:
        rhs[nt - nright:] = deriv_r_vals.reshape(-1, extradim)

    # solve Ab @ x = rhs; this is the relevant part of linalg.solve_banded
    if check_finite:
        ab, rhs = map(np.asarray_chkfinite, (ab, rhs))
    gbsv, = get_lapack_funcs(('gbsv', ), (ab, rhs))
    lu, piv, c, info = gbsv(kl,
                            ku,
                            ab,
                            rhs,
                            overwrite_ab=True,
                            overwrite_b=True)

    if info > 0:
        raise LinAlgError("Collocation matix is singular.")
    elif info < 0:
        raise ValueError('illegal value in %d-th argument of internal gbsv' %
                         -info)

    c = np.ascontiguousarray(c.reshape((nt, ) + y.shape[1:]))
    return BSpline.construct_fast(t, c, k, axis=axis)
Example #16
0
    def inference_nonroot(self,
                          kern,
                          X,
                          Z,
                          likelihood,
                          Y,
                          Y_metadata=None,
                          Lm=None,
                          dL_dKmm=None):

        num_data, output_dim = Y.shape
        num_data_total = allReduceArrays([np.int32(num_data)],
                                         self.mpi_comm)[0]

        input_dim = Z.shape[0]
        uncertain_inputs = isinstance(X, VariationalPosterior)
        uncertain_outputs = isinstance(Y, VariationalPosterior)
        beta = 1. / np.fmax(likelihood.variance, 1e-6)

        psi0, psi2, YRY, psi1, psi1Y, Shalf, psi1S = self.gatherPsiStat(
            kern, X, Z, Y, beta, uncertain_inputs)

        flag = np.zeros((1, ), dtype=np.int32)
        self.mpi_comm.Bcast(flag, root=self.root)
        if flag[0] == 1: raise LinAlgError('Linalg error!')

        LmInv, LLInv = np.empty((input_dim, input_dim)).T, np.empty(
            (input_dim, input_dim)).T
        broadcastArrays([LmInv, LLInv], self.mpi_comm, self.root)
        LmLLInv = LLInv.dot(LmInv)
        b = psi1Y.dot(LmLLInv.T)
        v = b.dot(LmLLInv)

        if psi1S is not None:
            psi1SLLinv = psi1S.dot(LmLLInv.T)
            bbt_sum = np.square(psi1SLLinv).sum()
            LLinvPsi1TYYTPsi1LLinvT_sum = tdot(psi1SLLinv.T)
            reduceArrays([bbt_sum, LLinvPsi1TYYTPsi1LLinvT_sum], self.mpi_comm,
                         self.root)
            psi1SP = psi1SLLinv.dot(LmLLInv)

        dL_dpsi2R = np.empty((input_dim, input_dim))
        broadcastArrays([dL_dpsi2R], self.mpi_comm, self.root)

        dL_dpsi0 = -output_dim * (beta * np.ones((num_data, ))) / 2.

        if uncertain_outputs:
            m, s = Y.mean, Y.variance
            dL_dpsi1 = beta * (np.dot(m, v) + Shalf[:, None] * psi1SP)
        else:
            dL_dpsi1 = beta * np.dot(Y, v)

        if uncertain_inputs:
            dL_dpsi2 = beta * dL_dpsi2R
        else:
            dL_dpsi1 += np.dot(psi1, dL_dpsi2R) * 2.
            dL_dpsi2 = None

        if uncertain_inputs:
            grad_dict = {
                'dL_dKmm': None,
                'dL_dpsi0': dL_dpsi0,
                'dL_dpsi1': dL_dpsi1,
                'dL_dpsi2': dL_dpsi2,
                'dL_dthetaL': None
            }
        else:
            grad_dict = {
                'dL_dKmm': None,
                'dL_dKdiag': dL_dpsi0,
                'dL_dKnm': dL_dpsi1,
                'dL_dthetaL': None
            }
        if uncertain_outputs:
            m, s = Y.mean, Y.variance
            psi1LmiLLi = psi1.dot(LmLLInv.T)
            LLiLmipsi1Y = b.T
            grad_dict['dL_dYmean'] = -m * beta + psi1LmiLLi.dot(LLiLmipsi1Y)
            grad_dict['dL_dYvar'] = beta / -2. + np.square(psi1LmiLLi).sum(
                axis=1) / 2

        return None, 0, grad_dict
Example #17
0
def cossin(X,
           p=None,
           q=None,
           separate=False,
           swap_sign=False,
           compute_u=True,
           compute_vh=True):
    """
    Compute the cosine-sine (CS) decomposition of an orthogonal/unitary matrix.

    X is an ``(m, m)`` orthogonal/unitary matrix, partitioned as the following
    where upper left block has the shape of ``(p, q)``::

                                   ┌                   ┐
                                   │ I  0  0 │ 0  0  0 │
        ┌           ┐   ┌         ┐│ 0  C  0 │ 0 -S  0 │┌         ┐*
        │ X11 │ X12 │   │ U1 │    ││ 0  0  0 │ 0  0 -I ││ V1 │    │
        │ ────┼──── │ = │────┼────││─────────┼─────────││────┼────│
        │ X21 │ X22 │   │    │ U2 ││ 0  0  0 │ I  0  0 ││    │ V2 │
        └           ┘   └         ┘│ 0  S  0 │ 0  C  0 │└         ┘
                                   │ 0  0  I │ 0  0  0 │
                                   └                   ┘

    ``U1``, ``U2``, ``V1``, ``V2`` are square orthogonal/unitary matrices of
    dimensions ``(p,p)``, ``(m-p,m-p)``, ``(q,q)``, and ``(m-q,m-q)``
    respectively, and ``C`` and ``S`` are ``(r, r)`` nonnegative diagonal
    matrices satisfying ``C^2 + S^2 = I`` where ``r = min(p, m-p, q, m-q)``.

    Moreover, the rank of the identity matrices are ``min(p, q) - r``,
    ``min(p, m - q) - r``, ``min(m - p, q) - r``, and ``min(m - p, m - q) - r``
    respectively.

    X can be supplied either by itself and block specifications p, q or its
    subblocks in an iterable from which the shapes would be derived. See the
    examples below.

    Parameters
    ----------
    X : array_like, iterable
        complex unitary or real orthogonal matrix to be decomposed, or iterable
        of subblocks ``X11``, ``X12``, ``X21``, ``X22``, when ``p``, ``q`` are
        omitted.
    p : int, optional
        Number of rows of the upper left block ``X11``, used only when X is
        given as an array.
    q : int, optional
        Number of columns of the upper left block ``X11``, used only when X is
        given as an array.
    separate : bool, optional
        if ``True``, the low level components are returned instead of the
        matrix factors, i.e. ``(u1,u2)``, ``theta``, ``(v1h,v2h)`` instead of
        ``u``, ``cs``, ``vh``.
    swap_sign : bool, optional
        if ``True``, the ``-S``, ``-I`` block will be the bottom left,
        otherwise (by default) they will be in the upper right block.
    compute_u : bool, optional
        if ``False``, ``u`` won't be computed and an empty array is returned.
    compute_vh : bool, optional
        if ``False``, ``vh`` won't be computed and an empty array is returned.

    Returns
    -------
    u : ndarray
        When ``compute_u=True``, contains the block diagonal orthogonal/unitary
        matrix consisting of the blocks ``U1`` (``p`` x ``p``) and ``U2``
        (``m-p`` x ``m-p``) orthogonal/unitary matrices. If ``separate=True``,
        this contains the tuple of ``(U1, U2)``.
    cs : ndarray
        The cosine-sine factor with the structure described above.
         If ``separate=True``, this contains the ``theta`` array containing the
         angles in radians.
    vh : ndarray
        When ``compute_vh=True`, contains the block diagonal orthogonal/unitary
        matrix consisting of the blocks ``V1H`` (``q`` x ``q``) and ``V2H``
        (``m-q`` x ``m-q``) orthogonal/unitary matrices. If ``separate=True``,
        this contains the tuple of ``(V1H, V2H)``.

    Examples
    --------
    >>> from scipy.linalg import cossin
    >>> from scipy.stats import unitary_group
    >>> x = unitary_group.rvs(4)
    >>> u, cs, vdh = cossin(x, p=2, q=2)
    >>> np.allclose(x, u @ cs @ vdh)
    True

    Same can be entered via subblocks without the need of ``p`` and ``q``. Also
    let's skip the computation of ``u``

    >>> ue, cs, vdh = cossin((x[:2, :2], x[:2, 2:], x[2:, :2], x[2:, 2:]),
    ...                      compute_u=False)
    >>> print(ue)
    []
    >>> np.allclose(x, u @ cs @ vdh)
    True

    References
    ----------
    .. [1] : Brian D. Sutton. Computing the complete CS decomposition. Numer.
           Algorithms, 50(1):33-65, 2009.

    """

    if p or q:
        p = 1 if p is None else int(p)
        q = 1 if q is None else int(q)
        X = _asarray_validated(X, check_finite=True)
        if not np.equal(*X.shape):
            raise ValueError("Cosine Sine decomposition only supports square"
                             " matrices, got {}".format(X.shape))
        m = X.shape[0]
        if p >= m or p <= 0:
            raise ValueError("invalid p={}, 0<p<{} must hold".format(
                p, X.shape[0]))
        if q >= m or q <= 0:
            raise ValueError("invalid q={}, 0<q<{} must hold".format(
                q, X.shape[0]))

        x11, x12, x21, x22 = X[:p, :q], X[:p, q:], X[p:, :q], X[p:, q:]
    elif not isinstance(X, Iterable):
        raise ValueError("When p and q are None, X must be an Iterable"
                         " containing the subblocks of X")
    else:
        if len(X) != 4:
            raise ValueError("When p and q are None, exactly four arrays"
                             " should be in X, got {}".format(len(X)))

        x11, x12, x21, x22 = [np.atleast_2d(x) for x in X]
        for name, block in zip(["x11", "x12", "x21", "x22"],
                               [x11, x12, x21, x22]):
            if block.shape[1] == 0:
                raise ValueError("{} can't be empty".format(name))
        p, q = x11.shape
        mmp, mmq = x22.shape

        if x12.shape != (p, mmq):
            raise ValueError("Invalid x12 dimensions: desired {}, "
                             "got {}".format((p, mmq), x12.shape))

        if x21.shape != (mmp, q):
            raise ValueError("Invalid x21 dimensions: desired {}, "
                             "got {}".format((mmp, q), x21.shape))

        if p + mmp != q + mmq:
            raise ValueError("The subblocks have compatible sizes but "
                             "don't form a square array (instead they form a"
                             " {}x{} array). This might be due to missing "
                             "p, q arguments.".format(p + mmp, q + mmq))

        m = p + mmp

    cplx = any([np.iscomplexobj(x) for x in [x11, x12, x21, x22]])
    driver = "uncsd" if cplx else "orcsd"
    csd, csd_lwork = get_lapack_funcs([driver, driver + "_lwork"],
                                      [x11, x12, x21, x22])
    lwork = _compute_lwork(csd_lwork, m=m, p=p, q=q)
    lwork_args = ({
        'lwork': lwork[0],
        'lrwork': lwork[1]
    } if cplx else {
        'lwork': lwork
    })
    *_, theta, u1, u2, v1h, v2h, info = csd(x11=x11,
                                            x12=x12,
                                            x21=x21,
                                            x22=x22,
                                            compute_u1=compute_u,
                                            compute_u2=compute_u,
                                            compute_v1t=compute_vh,
                                            compute_v2t=compute_vh,
                                            trans=False,
                                            signs=swap_sign,
                                            **lwork_args)

    method_name = csd.typecode + driver
    if info < 0:
        raise ValueError('illegal value in argument {} of internal {}'.format(
            -info, method_name))
    if info > 0:
        raise LinAlgError("{} did not converge: {}".format(method_name, info))

    if separate:
        return (u1, u2), theta, (v1h, v2h)

    U = block_diag(u1, u2)
    VDH = block_diag(v1h, v2h)

    # Construct the middle factor CS
    c = np.diag(np.cos(theta))
    s = np.diag(np.sin(theta))
    r = min(p, q, m - p, m - q)
    n11 = min(p, q) - r
    n12 = min(p, m - q) - r
    n21 = min(m - p, q) - r
    n22 = min(m - p, m - q) - r
    Id = np.eye(np.max([n11, n12, n21, n22, r]), dtype=theta.dtype)
    CS = np.zeros((m, m), dtype=theta.dtype)

    CS[:n11, :n11] = Id[:n11, :n11]

    xs = n11 + r
    xe = n11 + r + n12
    ys = n11 + n21 + n22 + 2 * r
    ye = n11 + n21 + n22 + 2 * r + n12
    CS[xs:xe, ys:ye] = Id[:n12, :n12] if swap_sign else -Id[:n12, :n12]

    xs = p + n22 + r
    xe = p + n22 + r + +n21
    ys = n11 + r
    ye = n11 + r + n21
    CS[xs:xe, ys:ye] = -Id[:n21, :n21] if swap_sign else Id[:n21, :n21]

    CS[p:p + n22, q:q + n22] = Id[:n22, :n22]
    CS[n11:n11 + r, n11:n11 + r] = c
    CS[p + n22:p + n22 + r, r + n21 + n22:2 * r + n21 + n22] = c

    xs = n11
    xe = n11 + r
    ys = n11 + n21 + n22 + r
    ye = n11 + n21 + n22 + 2 * r
    CS[xs:xe, ys:ye] = s if swap_sign else -s

    CS[p + n22:p + n22 + r, n11:n11 + r] = -s if swap_sign else s

    return U, CS, VDH
Example #18
0
    def logpost_deriv(self, params):
        r"""
        Compute the gradient of the negative log posterior for a particular set of parameters

        Computes the gradient of the negative log posterior (negative marginal log-likelihood
        minus any prior log-probabilities). This is computed on the root process and then broadcast
        to all processes.

        The main computational expense is computing the prior mean and covariance, which only
        needs to be done once and can be cached. This also requires computing the Cholesky
        decomposition of the covariance plus model discrepancy.

        New parameters must be a numpy array of length 3. First parameter is the data/model
        scaling factor :math:`{\rho}`, second parameter is the model discrepancy covariance,
        and the third parameter is the model discrepancy correlation length. All parameters
        are assumed to be on a logarithmic scale to enforce positivity.

        The returned log posterior gradient is a numpy array of length 3, with each component
        corresponding to the derivative of each of the input parameters.

        :param params: New set of parameters (must be a numpy array of length 3)
        :type params: ndarray
        :returns: gradient of the negative log posterior
        :rtype: ndarray
        """

        self.set_params(params)
        rho = np.exp(self.params[0])

        if self.Cu is None or self.mu is None:
            self.solve_prior()

        # compute log-likelihood on root process

        if COMM_WORLD.rank == 0:
            KCu = rho**2 * self.Cu + self.data.calc_K_plus_sigma(params[1:])
            try:
                L = cho_factor(KCu)
            except LinAlgError:
                raise LinAlgError(
                    "Error attempting to factorize the covariance matrix " +
                    "in model_loglikelihood")
            invKCudata = cho_solve(L, self.data.get_data() - rho * self.mu)

            K_deriv = self.data.calc_K_deriv(self.params[1:])

            deriv = np.zeros(3)

            deriv[0] = (
                -rho * np.dot(self.mu, invKCudata) -
                rho**2 * np.linalg.multi_dot([invKCudata, self.Cu, invKCudata])
                + rho**2 * np.trace(cho_solve(L, self.Cu)))
            for i in range(0, 2):
                deriv[i + 1] = -0.5 * (
                    np.linalg.multi_dot([invKCudata, K_deriv[i], invKCudata]) -
                    np.trace(cho_solve(L, K_deriv[i])))

            for i in range(3):
                if not self.priors[i] is None:
                    deriv[i] -= self.priors[i].dlogpdtheta(self.params[i])
        else:
            deriv = None

        deriv = COMM_WORLD.bcast(deriv, root=0)

        assert not deriv is None, "error in broadcasting the log likelihood derivative"

        COMM_WORLD.barrier()

        return deriv
Example #19
0
def Discretize(V, **kwargs):
    """Adapted from :func:`~sklearn.cluster.spectral.discretize`. Copyright please 
    see LICENSE.rst.
    """

    from scipy.sparse import csc_matrix
    from scipy.linalg import LinAlgError

    copy = kwargs.pop('copy', False)
    max_svd_restarts = kwargs.pop('max_svd_restarts', 30)
    n_iter_max = kwargs.pop('n_iter_max', 20)
    random_state = kwargs.pop('random_state', None)
    info = kwargs.pop('info', None)

    def check_random_state(seed):
        """Adapted from :func:`~sklearn.utils.validation.check_random_state`."""
        if seed is None or seed is np.random:
            return np.random.mtrand._rand
        if isinstance(seed, Integral):
            return np.random.RandomState(seed)
        if isinstance(seed, np.random.RandomState):
            return seed
        raise ValueError('%r cannot be used to seed a numpy.random.RandomState'
                         ' instance' % seed)

    random_state = check_random_state(random_state)

    eps = np.finfo(float).eps
    vectors = np.array(V, dtype=float, copy=copy)
    n_samples, n_components = vectors.shape

    # Normalize the eigenvectors to an equal length of a vector of ones.
    # Reorient the eigenvectors to point in the negative direction with respect
    # to the first element.  This may have to do with constraining the
    # eigenvectors to lie in a specific quadrant to make the discretization
    # search easier.
    norm_ones = np.sqrt(n_samples)
    for i in range(vectors.shape[1]):
        vectors[:, i] *= norm_ones
        if vectors[0, i] != 0:
            vectors[:, i] = -1 * vectors[:, i] * np.sign(vectors[0, i])

    # Normalize the rows of the eigenvectors.  Samples should lie on the unit
    # hypersphere centered at the origin.  This transforms the samples in the
    # embedding space to the space of partition matrices.
    vectors = vectors / np.sqrt((vectors**2).sum(axis=1))[:, np.newaxis]

    svd_restarts = 0
    has_converged = False

    # If there is an exception we try to randomize and rerun SVD again
    # do this max_svd_restarts times.
    while (svd_restarts < max_svd_restarts) and not has_converged:

        # Initialize first column of rotation matrix with a row of the
        # eigenvectors
        rotation = np.zeros((n_components, n_components))
        rotation[:, 0] = vectors[random_state.randint(n_samples), :].T

        # To initialize the rest of the rotation matrix, find the rows
        # of the eigenvectors that are as orthogonal to each other as
        # possible
        c = np.zeros(n_samples)
        for j in range(1, n_components):
            # Accumulate c to ensure row is as orthogonal as possible to
            # previous picks as well as current one
            c += np.abs(np.dot(vectors, rotation[:, j - 1]))
            rotation[:, j] = vectors[c.argmin(), :].T

        last_objective_value = 0.0
        n_iter = 0

        while not has_converged:
            n_iter += 1

            t_discrete = np.dot(vectors, rotation)

            labels = t_discrete.argmax(axis=1)
            vectors_discrete = csc_matrix(
                (np.ones(len(labels)), (np.arange(0, n_samples), labels)),
                shape=(n_samples, n_components))

            t_svd = vectors_discrete.T * vectors

            try:
                U, S, Vh = np.linalg.svd(t_svd)
                svd_restarts += 1
            except LinAlgError:
                print("SVD did not converge, randomizing and trying again")
                break

            ncut_value = 2.0 * (n_samples - S.sum())
            if ((abs(ncut_value - last_objective_value) < eps)
                    or (n_iter > n_iter_max)):
                has_converged = True
            else:
                # otherwise calculate rotation and continue
                last_objective_value = ncut_value
                rotation = np.dot(Vh.T, U.T)

    if info is not None:
        if not isinstance(info, dict):
            raise TypeError('info must be a dict')
        info['indicators'] = t_discrete
        info['vectors'] = vectors

    if not has_converged:
        raise LinAlgError('SVD did not converge')
    return labels
Example #20
0
    def predict_covariance(self, coords, unc):
        r"""
        Compute the predictive covariance

        This method computes the predictive covariance of data values at unmeasured locations.
        It returns the array of predicted sensor value covariances on the root process as numpy
        array. Unlike the mean, the predictive covariance requires doing two additional sets of
        covariance solves: one on the new sensor locations (to get the baseline covariance),
        and one set of solves that interpolates between the predictive points and the original
        sensor locations. This can be thought of as doing the covariance solves at the new points
        to get a baseline uncertainty, and then the cross-solves determine if any of the sensor
        data is close enough to the predictive points to reduce this uncertainty.

        :param coords: Spatial coordinates at which the mean will be predicted. Must be a
                       2D Numpy array (or a 1D array, which will assume the second axis has length
                       1)
        :type coords: ndarray
        :param unc: Uncertainty for unmeasured sensor locations (i.e. the statistical error one would
                    expect if these measurements were made). Can be a single non-negative float,
                    or an array of non-negative floats with the same length as the first axis of
                    ``coords``.
        :type unc: float or ndarray
        :returns: FEM predictive covariance at specified sensor locations as a numpy array on the
                  root process. All other processes will have a numpy array of shape ``(0, 0)``.
        :rtype: ndarray
        """

        coords = np.array(coords, dtype=np.float64)
        if coords.ndim == 1:
            coords = np.reshape(coords, (-1, 1))
        assert coords.ndim == 2, "coords must be a 1d or 2d array"
        assert coords.shape[1] == self.data.get_n_dim(
        ), "axis 1 of coords must be the same length as the FEM dimension"

        if self.Cu is None:
            self.solve_prior()

        if self.params is None:
            raise ValueError("must set parameter values to make predictions")

        rho = np.exp(self.params[0])

        im_coords = InterpolationMatrix(self.G.function_space, coords)

        if coords.shape[0] > self.data.get_n_obs():
            Cucd = interp_covariance_to_data(im_coords, self.G, self.solver,
                                             self.im, self.ensemble_comm)
        else:
            Cucd = interp_covariance_to_data(self.im, self.G, self.solver,
                                             im_coords, self.ensemble_comm).T
        Cucc = interp_covariance_to_data(im_coords, self.G, self.solver,
                                         im_coords, self.ensemble_comm)

        if self.ensemble_comm.rank == 0 and self.G.comm.rank == 0:
            try:
                Ks = self.data.calc_K_plus_sigma(self.params[1:])
                LC = cho_factor(Ks + rho**2 * self.Cu)
            except LinAlgError:
                raise LinAlgError(
                    "Cholesky factorization of one of the covariance matrices failed"
                )

            # compute predictive covariance

            Cuy = Cucc - rho**2 * np.dot(Cucd, cho_solve(LC, Cucd.T))

            Cuy = ObsData(coords, np.zeros(coords.shape[0]),
                          unc).calc_K_plus_sigma(
                              self.params[1:]) + rho**2 * Cuy

        else:
            Cuy = np.zeros((0, 0))

        im_coords.destroy()

        return Cuy
Example #21
0
def _cholesky(a,
              lower=False,
              overwrite_a=False,
              clean=True,
              check_finite=True,
              full_pivot=False,
              pivot_tol=-1):
    """Common code for cholesky() and cho_factor()."""

    a1 = asarray_chkfinite(a) if check_finite else asarray(a)
    a1 = atleast_2d(a1)

    # Dimension check
    if a1.ndim != 2:
        raise ValueError('Input array needs to be 2 dimensional but received '
                         'a {}d-array.'.format(a1.ndim))
    # Squareness check
    if a1.shape[0] != a1.shape[1]:
        raise ValueError('Input array is expected to be square but has '
                         'the shape: {}.'.format(a1.shape))

    # Quick return for square empty array
    if a1.size == 0:
        return a1.copy(), lower

    #if not is_hermitian():
    #    raise LinAlgError("Expected symmetric or hermitian matrix")

    overwrite_a = overwrite_a or _datacopied(a1, a)

    # if the pivot flag is false, return the result
    if not full_pivot:
        potrf, = get_lapack_funcs(('potrf', ), (a1, ))
        c, info = potrf(a1, lower=lower, overwrite_a=overwrite_a, clean=clean)
        if info > 0:
            raise LinAlgError(
                "%d-th leading minor of the array is not positive "
                "definite" % info)
        if info < 0:
            raise ValueError(
                'LAPACK reported an illegal value in {}-th argument'
                'on entry to "POTRF".'.format(-info))
        return c, lower
    else:  # if the pivot flag is true, return the result plus rank and pivot

        pstrf, = get_lapack_funcs(('pstrf', ), (a1, ))
        c, pivot, rank, info = pstrf(a1,
                                     lower=lower,
                                     overwrite_a=overwrite_a,
                                     tol=pivot_tol)  #

        if info > 0:
            if rank == 0:
                raise LinAlgError(
                    "%d-th leading minor of the array is not positive "
                    "semidefinite" % info)
            else:
                raise LinAlgError("The array is rank deficient with "
                                  "computed rank %d" % info)

        if info < 0:
            raise ValueError(
                'LAPACK reported an illegal value in {}-th argument'
                'on entry to "PSTRF".'.format(-info))
        return c, lower, rank, pivot
Example #22
0
def _svdp(A,
          k,
          which='LM',
          irl_mode=True,
          kmax=None,
          compute_u=True,
          compute_v=True,
          v0=None,
          full_output=False,
          tol=0,
          delta=None,
          eta=None,
          anorm=0,
          cgs=False,
          elr=True,
          min_relgap=0.002,
          shifts=None,
          maxiter=None,
          random_state=None):
    """
    Compute the singular value decomposition of a linear operator using PROPACK

    Parameters
    ----------
    A : array_like, sparse matrix, or LinearOperator
        Operator for which SVD will be computed.  If `A` is a LinearOperator
        object, it must define both ``matvec`` and ``rmatvec`` methods.
    k : int
        Number of singular values/vectors to compute
    which : {"LM", "SM"}
        Which singluar triplets to compute:
        - 'LM': compute triplets corresponding to the `k` largest singular
                values
        - 'SM': compute triplets corresponding to the `k` smallest singular
                values
        `which='SM'` requires `irl_mode=True`.  Computes largest singular
        values by default.
    irl_mode : bool, optional
        If `True`, then compute SVD using IRL (implicitly restarted Lanczos)
        mode.  Default is `True`.
    kmax : int, optional
        Maximal number of iterations / maximal dimension of the Krylov
        subspace. Default is ``10 * k``.
    compute_u : bool, optional
        If `True` (default) then compute left singular vectors, `u`.
    compute_v : bool, optional
        If `True` (default) then compute right singular vectors, `v`.
    tol : float, optional
        The desired relative accuracy for computed singular values.
        If not specified, it will be set based on machine precision.
    v0 : array_like, optional
        Starting vector for iterations: must be of length ``A.shape[0]``.
        If not specified, PROPACK will generate a starting vector.
    full_output : bool, optional
        If `True`, then return sigma_bound.  Default is `False`.
    delta : float, optional
        Level of orthogonality to maintain between Lanczos vectors.
        Default is set based on machine precision.
    eta : float, optional
        Orthogonality cutoff.  During reorthogonalization, vectors with
        component larger than `eta` along the Lanczos vector will be purged.
        Default is set based on machine precision.
    anorm : float, optional
        Estimate of ``||A||``.  Default is `0`.
    cgs : bool, optional
        If `True`, reorthogonalization is done using classical Gram-Schmidt.
        If `False` (default), it is done using modified Gram-Schmidt.
    elr : bool, optional
        If `True` (default), then extended local orthogonality is enforced
        when obtaining singular vectors.
    min_relgap : float, optional
        The smallest relative gap allowed between any shift in IRL mode.
        Default is `0.001`.  Accessed only if ``irl_mode=True``.
    shifts : int, optional
        Number of shifts per restart in IRL mode.  Default is determined
        to satisfy ``k <= min(kmax-shifts, m, n)``.  Must be
        >= 0, but choosing 0 might lead to performance degredation.
        Accessed only if ``irl_mode=True``.
    maxiter : int, optional
        Maximum number of restarts in IRL mode.  Default is `1000`.
        Accessed only if ``irl_mode=True``.
    random_state : {None, int, `numpy.random.Generator`,
                    `numpy.random.RandomState`}, optional

        Pseudorandom number generator state used to generate resamples.

        If `random_state` is ``None`` (or `np.random`), the
        `numpy.random.RandomState` singleton is used.
        If `random_state` is an int, a new ``RandomState`` instance is used,
        seeded with `random_state`.
        If `random_state` is already a ``Generator`` or ``RandomState``
        instance then that instance is used.

    Returns
    -------
    u : ndarray
        The `k` largest (``which="LM"``) or smallest (``which="SM"``) left
        singular vectors, ``shape == (A.shape[0], 3)``, returned only if
        ``compute_u=True``.
    sigma : ndarray
        The top `k` singular values, ``shape == (k,)``
    vt : ndarray
        The `k` largest (``which="LM"``) or smallest (``which="SM"``) right
        singular vectors, ``shape == (3, A.shape[1])``, returned only if
        ``compute_v=True``.
    sigma_bound : ndarray
        the error bounds on the singular values sigma, returned only if
        ``full_output=True``.

    """
    # 32-bit complex PROPACK functions have Fortran LAPACK ABI
    # incompatibility issues
    if np.iscomplexobj(A) and (np.intp(0).itemsize < 8):
        raise TypeError('PROPACK complex-valued SVD methods not available '
                        'for 32-bit builds')

    random_state = check_random_state(random_state)

    which = which.upper()
    if which not in {'LM', 'SM'}:
        raise ValueError("`which` must be either 'LM' or 'SM'")
    if not irl_mode and which == 'SM':
        raise ValueError("`which`='SM' requires irl_mode=True")

    aprod = _AProd(A)
    typ = aprod.dtype.char

    try:
        lansvd_irl = _lansvd_irl_dict[typ]
        lansvd = _lansvd_dict[typ]
    except KeyError:
        # work with non-supported types using native system precision
        if np.iscomplexobj(np.empty(0, dtype=typ)):
            typ = np.dtype(complex).char
        else:
            typ = np.dtype(float).char
        lansvd_irl = _lansvd_irl_dict[typ]
        lansvd = _lansvd_dict[typ]

    m, n = aprod.shape
    if (k < 1) or (k > min(m, n)):
        raise ValueError("k must be positive and not greater than m or n")

    if kmax is None:
        kmax = 10 * k
    if maxiter is None:
        maxiter = 1000

    # guard against unnecessarily large kmax
    kmax = min(m + 1, n + 1, kmax)
    if kmax < k:
        raise ValueError("kmax must be greater than or equal to k, "
                         f"but kmax ({kmax}) < k ({k})")

    # convert python args to fortran args
    jobu = 'y' if compute_u else 'n'
    jobv = 'y' if compute_v else 'n'

    # these will be the output arrays
    u = np.zeros((m, kmax + 1), order='F', dtype=typ)
    v = np.zeros((n, kmax), order='F', dtype=typ)

    # Specify the starting vector.  if v0 is all zero, PROPACK will generate
    # a random starting vector: the random seed cannot be controlled in that
    # case, so we'll instead use numpy to generate a random vector
    if v0 is None:
        u[:, 0] = random_state.uniform(size=m)
        if np.iscomplexobj(np.empty(0, dtype=typ)):  # complex type
            u[:, 0] += 1j * random_state.uniform(size=m)
    else:
        try:
            u[:, 0] = v0
        except ValueError:
            raise ValueError(f"v0 must be of length {m}")

    # process options for the fit
    if delta is None:
        delta = np.sqrt(np.finfo(typ).eps)
    if eta is None:
        eta = np.finfo(typ).eps**0.75

    if irl_mode:
        doption = np.array((delta, eta, anorm, min_relgap), dtype=typ.lower())

        # validate or find default shifts
        if shifts is None:
            shifts = kmax - k
        if k > min(kmax - shifts, m, n):
            raise ValueError('shifts must satisfy '
                             'k <= min(kmax-shifts, m, n)!')
        elif shifts < 0:
            raise ValueError('shifts must be >= 0!')

    else:
        doption = np.array((delta, eta, anorm), dtype=typ.lower())

    ioption = np.array((int(bool(cgs)), int(bool(elr))), dtype='i')

    # If computing `u` or `v` (left and right singular vectors,
    # respectively), `blocksize` controls how large a fraction of the
    # work is done via fast BLAS level 3 operations.  A larger blocksize
    # may lead to faster computation at the expense of greater memory
    # consumption.  `blocksize` must be ``>= 1``.  Choosing blocksize
    # of 16, but docs don't specify; it's almost surely a
    # power of 2.
    blocksize = 16

    # Determine lwork & liwork:
    # the required lengths are specified in the PROPACK documentation
    if compute_u or compute_v:
        lwork = m + n + 9 * kmax + 5 * kmax * kmax + 4 + max(
            3 * kmax * kmax + 4 * kmax + 4, blocksize * max(m, n))
        liwork = 8 * kmax
    else:
        lwork = m + n + 9 * kmax + 2 * kmax * kmax + 4 + max(
            m + n, 4 * kmax + 4)
        liwork = 2 * kmax + 1
    work = np.empty(lwork, dtype=typ.lower())
    iwork = np.empty(liwork, dtype=np.int32)

    # dummy arguments: these are passed to aprod, and not used in this wrapper
    dparm = np.empty(1, dtype=typ.lower())
    iparm = np.empty(1, dtype=np.int32)

    if typ.isupper():
        # PROPACK documentation is unclear on the required length of zwork.
        # Use the same length Julia's wrapper uses
        # see https://github.com/JuliaSmoothOptimizers/PROPACK.jl/
        zwork = np.empty(m + n + 32 * m, dtype=typ)
        works = work, zwork, iwork
    else:
        works = work, iwork

    if irl_mode:
        u, sigma, bnd, v, info = lansvd_irl(_which_converter[which], jobu,
                                            jobv, m, n, shifts, k, maxiter,
                                            aprod, u, v, tol, *works, doption,
                                            ioption, dparm, iparm)
    else:
        u, sigma, bnd, v, info = lansvd(jobu, jobv, m, n, k, aprod, u, v, tol,
                                        *works, doption, ioption, dparm, iparm)

    if info > 0:
        raise LinAlgError(
            f"An invariant subspace of dimension {info} was found.")
    elif info < 0:
        raise LinAlgError(f"k={k} singular triplets did not converge within "
                          f"kmax={kmax} iterations")

    # info == 0: The K largest (or smallest) singular triplets were computed
    # succesfully!

    return u[:, :k], sigma, v[:, :k].conj().T, bnd
Example #23
0
    def solve_posterior_covariance(self, scale_mean=False):
        r"""
        Solve posterior FEM and covariance interpolated to the data locations

        This method solves the posterior FEM and covariance interpolated to the sensor
        locations. The method returns solution as numpy arrays on the root process (rank 0).

        Note that unlike the solve done in the meshspace, this uses a return value rather than a
        Firedrake/PETSc style interface to place the solution in a pre-allocated ``Function``.
        This is because each process has a different array size, so would require correctly
        pre-allocating arrays of different lengths on each process.

        The optional ``scale_mean`` argument determines if the solution is to be re-scaled
        by the model discrepancy scaling factor. This value is by default ``False``.
        To re-scale to match the data, pass ``scale_mean=True``.

        :returns: FEM posterior mean and covariance (as a tuple of numpy arrays) on the root process.
                  Non-root processes return numpy arrays of shape ``(0,)`` (mean) and ``(0, 0)``
                  (covariance).
        :param scale_mean: Boolean indicating if the mean should be scaled by the model
                           discrepancy scaling factor. Optional, default is ``False``
        :type scale_mean: bool
        :rtype: tuple of ndarrays
        """

        if not isinstance(bool(scale_mean), bool):
            raise TypeError("scale_mean argument must be boolean-like")

        # create interpolation matrix if not cached

        if self.mu is None or self.Cu is None:
            self.solve_prior()

        if self.params is None:
            raise ValueError("must set parameter values to solve posterior")

        rho = np.exp(self.params[0])

        if scale_mean:
            scalefact = rho
        else:
            scalefact = 1.

        if self.ensemble_comm.rank == 0 and self.G.comm.rank == 0:
            try:
                Ks = self.data.calc_K_plus_sigma(self.params[1:])
                LK = cho_factor(Ks)
                LC = cho_factor(Ks + rho**2 * self.Cu)
            except LinAlgError:
                raise LinAlgError(
                    "Cholesky factorization of one of the covariance matrices failed"
                )

            # compute posterior mean

            muy = rho * np.dot(self.Cu, cho_solve(
                LK, self.data.get_data())) + self.mu
            muy_tmp = rho**2 * np.dot(self.Cu, cho_solve(LC, muy))
            muy = muy - muy_tmp

            # compute posterior covariance

            Cuy = self.Cu - rho**2 * np.dot(self.Cu, cho_solve(LC, self.Cu))

        else:
            muy = np.zeros(0)
            Cuy = np.zeros((0, 0))

        return scalefact * muy, Cuy
Example #24
0
def jitchol(a,
            jit=None,
            jit_max=1e-3,
            returns_jit=False,
            lower=False,
            overwrite_a=False,
            check_finite=True):
    """
    Do cholesky decomposition with a bit of diagonal jitter if needs be.

    Arguments:
        A: a [NxN] positive definite symmetric matrix to be decomposed as
            A = L.dot(L.T).
        lower: Return lower triangular factor, default False (upper).

    Returns:
        An upper or lower triangular matrix factor, L, also [NxN].
        Also wheter or not a the matrix is lower triangular form,
        (L, lower).

    Examples
    --------
    >>> a = np.array([[1, -2j],
    ...               [2j, 5]])
    >>> jitchol(a, lower=True)
    array([[ 1.+0.j,  0.+0.j],
           [ 0.+2.j,  1.+0.j]])
    >>> np.all(a == np.array([[1, -2j],
    ...                       [2j, 5]]))
    True

    >>> b = np.array([[ 2, -1,  0],
    ...               [-1,  2, -1],
    ...               [ 0, -1,  2]])
    >>> U, jit = jitchol(b, returns_jit=True)
    >>> U.round(2)
    array([[ 1.41, -0.71,  0.  ],
           [ 0.  ,  1.22, -0.82],
           [ 0.  ,  0.  ,  1.15]])
    >>> jit is None
    True

    Should remain unchanged

    >>> b
    array([[ 2, -1,  0],
           [-1,  2, -1],
           [ 0, -1,  2]])

    >>> c = np.array([[1, 2],
    ...               [2, 1]])
    >>> jitchol(c) # doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
        ...
    LinAlgError: Exceeded maximum jitter limit, yet a is still not positive semidefinite!
    """

    try:
        chol = cholesky(a,
                        lower=lower,
                        overwrite_a=overwrite_a,
                        check_finite=check_finite)
        if returns_jit:
            return chol, jit
        else:
            return chol

    except LinAlgError:

        if jit is None:
            jit = 1e-16

        if jit > jit_max:
            raise LinAlgError('Exceeded maximum jitter limit, yet a is still'
                              ' not positive semidefinite!')

        diag = np.diag(a)
        diag_mean = diag.mean()
        diag_delta = jit * diag_mean

        if overwrite_a:
            diag_ind = np.diag_indices_from(a)
            a[diag_ind] += diag_delta
            return jitchol(a,
                           jit=10 * jit,
                           jit_max=jit_max,
                           returns_jit=returns_jit,
                           lower=lower,
                           overwrite_a=overwrite_a,
                           check_finite=check_finite)

        return jitchol(a + diag_delta * np.eye(*a.shape),
                       jit=10 * jit,
                       jit_max=jit_max,
                       returns_jit=returns_jit,
                       lower=lower,
                       overwrite_a=overwrite_a,
                       check_finite=check_finite)
Example #25
0
def spsolve_triangular(A, b, lower=True, overwrite_A=False, overwrite_b=False):
    """
    Solve the equation `A x = b` for `x`, assuming A is a triangular matrix.

    Parameters
    ----------
    A : (M, M) sparse matrix
        A sparse square triangular matrix. Should be in CSR format.
    b : (M,) or (M, N) array_like
        Right-hand side matrix in `A x = b`
    lower : bool, optional
        Whether `A` is a lower or upper triangular matrix.
        Default is lower triangular matrix.
    overwrite_A : bool, optional
        Allow changing `A`. The indices of `A` are going to be sorted and zero
        entries are going to be removed.
        Enabling gives a performance gain. Default is False.
    overwrite_b : bool, optional
        Allow overwriting data in `b`.
        Enabling gives a performance gain. Default is False.
        If `overwrite_b` is True, it should be ensured that
        `b` has an appropriate dtype to be able to store the result.

    Returns
    -------
    x : (M,) or (M, N) ndarray
        Solution to the system `A x = b`.  Shape of return matches shape of `b`.

    Raises
    ------
    LinAlgError
        If `A` is singular or not triangular.
    ValueError
        If shape of `A` or shape of `b` do not match the requirements.

    Notes
    -----
    .. versionadded:: 0.19.0

    Examples
    --------
    >>> from scipy.sparse import csr_matrix
    >>> from scipy.sparse.linalg import spsolve_triangular
    >>> A = csr_matrix([[3, 0, 0], [1, -1, 0], [2, 0, 1]], dtype=float)
    >>> B = np.array([[2, 0], [-1, 0], [2, 0]], dtype=float)
    >>> x = spsolve_triangular(A, B)
    >>> np.allclose(A.dot(x), B)
    True
    """

    # Check the input for correct type and format.
    if not isspmatrix_csr(A):
        warn('CSR matrix format is required. Converting to CSR matrix.',
             SparseEfficiencyWarning)
        A = csr_matrix(A)
    elif not overwrite_A:
        A = A.copy()

    if A.shape[0] != A.shape[1]:
        raise ValueError(
            'A must be a square matrix but its shape is {}.'.format(A.shape))

    A.eliminate_zeros()
    A.sort_indices()

    b = np.asanyarray(b)

    if b.ndim not in [1, 2]:
        raise ValueError('b must have 1 or 2 dims but its shape is {}.'.format(
            b.shape))
    if A.shape[0] != b.shape[0]:
        raise ValueError(
            'The size of the dimensions of A must be equal to '
            'the size of the first dimension of b but the shape of A is '
            '{} and the shape of b is {}.'.format(A.shape, b.shape))

    # Init x as (a copy of) b.
    x_dtype = np.result_type(A.data, b, np.float)
    if overwrite_b:
        if np.can_cast(b.dtype, x_dtype, casting='same_kind'):
            x = b
        else:
            raise ValueError('Cannot overwrite b (dtype {}) with result '
                             'of type {}.'.format(b.dtype, x_dtype))
    else:
        x = b.astype(x_dtype, copy=True)

    # Choose forward or backward order.
    if lower:
        row_indices = range(len(b))
    else:
        row_indices = range(len(b) - 1, -1, -1)

    # Fill x iteratively.
    for i in row_indices:

        # Get indices for i-th row.
        indptr_start = A.indptr[i]
        indptr_stop = A.indptr[i + 1]
        if lower:
            A_diagonal_index_row_i = indptr_stop - 1
            A_off_diagonal_indices_row_i = slice(indptr_start, indptr_stop - 1)
        else:
            A_diagonal_index_row_i = indptr_start
            A_off_diagonal_indices_row_i = slice(indptr_start + 1, indptr_stop)

        # Check regularity and triangularity of A.
        if indptr_stop <= indptr_start or A.indices[A_diagonal_index_row_i] < i:
            raise LinAlgError('A is singular: diagonal {} is zero.'.format(i))
        if A.indices[A_diagonal_index_row_i] > i:
            raise LinAlgError('A is not triangular: A[{}, {}] is nonzero.'
                              ''.format(i, A.indices[A_diagonal_index_row_i]))

        # Incorporate off-diagonal entries.
        A_column_indices_in_row_i = A.indices[A_off_diagonal_indices_row_i]
        A_values_in_row_i = A.data[A_off_diagonal_indices_row_i]
        x[i] -= np.dot(x[A_column_indices_in_row_i].T, A_values_in_row_i)

        # Compute i-th entry of x.
        x[i] /= A.data[A_diagonal_index_row_i]

    return x
Example #26
0
def firls(numtaps, bands, desired, weight=None, nyq=None, fs=None):
    """
    FIR filter design using least-squares error minimization.

    Calculate the filter coefficients for the linear-phase finite
    impulse response (FIR) filter which has the best approximation
    to the desired frequency response described by `bands` and
    `desired` in the least squares sense (i.e., the integral of the
    weighted mean-squared error within the specified bands is
    minimized).

    Parameters
    ----------
    numtaps : int
        The number of taps in the FIR filter. `numtaps` must be odd.
    bands : array_like
        A monotonic nondecreasing sequence containing the band edges in
        Hz. All elements must be non-negative and less than or equal to
        the Nyquist frequency given by `nyq`.
    desired : array_like
        A sequence the same size as `bands` containing the desired gain
        at the start and end point of each band.
    weight : array_like, optional
        A relative weighting to give to each band region when solving
        the least squares problem. `weight` has to be half the size of
        `bands`.
    nyq : float, optional
        *Deprecated. Use `fs` instead.*
        Nyquist frequency. Each frequency in `bands` must be between 0
        and `nyq` (inclusive). Default is 1.
    fs : float, optional
        The sampling frequency of the signal. Each frequency in `bands`
        must be between 0 and ``fs/2`` (inclusive). Default is 2.

    Returns
    -------
    coeffs : ndarray
        Coefficients of the optimal (in a least squares sense) FIR filter.

    See also
    --------
    firwin
    firwin2
    minimum_phase
    remez

    Notes
    -----
    This implementation follows the algorithm given in [1]_.
    As noted there, least squares design has multiple advantages:

        1. Optimal in a least-squares sense.
        2. Simple, non-iterative method.
        3. The general solution can obtained by solving a linear
           system of equations.
        4. Allows the use of a frequency dependent weighting function.

    This function constructs a Type I linear phase FIR filter, which
    contains an odd number of `coeffs` satisfying for :math:`n < numtaps`:

    .. math:: coeffs(n) = coeffs(numtaps - 1 - n)

    The odd number of coefficients and filter symmetry avoid boundary
    conditions that could otherwise occur at the Nyquist and 0 frequencies
    (e.g., for Type II, III, or IV variants).

    .. versionadded:: 0.18

    References
    ----------
    .. [1] Ivan Selesnick, Linear-Phase Fir Filter Design By Least Squares.
           OpenStax CNX. Aug 9, 2005.
           http://cnx.org/contents/eb1ecb35-03a9-4610-ba87-41cd771c95f2@7

    Examples
    --------
    We want to construct a band-pass filter. Note that the behavior in the
    frequency ranges between our stop bands and pass bands is unspecified,
    and thus may overshoot depending on the parameters of our filter:

    >>> from scipy import signal
    >>> import matplotlib.pyplot as plt
    >>> fig, axs = plt.subplots(2)
    >>> fs = 10.0  # Hz
    >>> desired = (0, 0, 1, 1, 0, 0)
    >>> for bi, bands in enumerate(((0, 1, 2, 3, 4, 5), (0, 1, 2, 4, 4.5, 5))):
    ...     fir_firls = signal.firls(73, bands, desired, fs=fs)
    ...     fir_remez = signal.remez(73, bands, desired[::2], fs=fs)
    ...     fir_firwin2 = signal.firwin2(73, bands, desired, fs=fs)
    ...     hs = list()
    ...     ax = axs[bi]
    ...     for fir in (fir_firls, fir_remez, fir_firwin2):
    ...         freq, response = signal.freqz(fir)
    ...         hs.append(ax.semilogy(0.5*fs*freq/np.pi, np.abs(response))[0])
    ...     for band, gains in zip(zip(bands[::2], bands[1::2]),
    ...                            zip(desired[::2], desired[1::2])):
    ...         ax.semilogy(band, np.maximum(gains, 1e-7), 'k--', linewidth=2)
    ...     if bi == 0:
    ...         ax.legend(hs, ('firls', 'remez', 'firwin2'),
    ...                   loc='lower center', frameon=False)
    ...     else:
    ...         ax.set_xlabel('Frequency (Hz)')
    ...     ax.grid(True)
    ...     ax.set(title='Band-pass %d-%d Hz' % bands[2:4], ylabel='Magnitude')
    ...
    >>> fig.tight_layout()
    >>> plt.show()

    """  # noqa
    nyq = 0.5 * _get_fs(fs, nyq)

    numtaps = int(numtaps)
    if numtaps % 2 == 0 or numtaps < 1:
        raise ValueError("numtaps must be odd and >= 1")
    M = (numtaps-1) // 2

    # normalize bands 0->1 and make it 2 columns
    nyq = float(nyq)
    if nyq <= 0:
        raise ValueError('nyq must be positive, got %s <= 0.' % nyq)
    bands = np.asarray(bands).flatten() / nyq
    if len(bands) % 2 != 0:
        raise ValueError("bands must contain frequency pairs.")
    if (bands < 0).any() or (bands > 1).any():
        raise ValueError("bands must be between 0 and 1 relative to Nyquist")
    bands.shape = (-1, 2)

    # check remaining params
    desired = np.asarray(desired).flatten()
    if bands.size != desired.size:
        raise ValueError("desired must have one entry per frequency, got %s "
                         "gains for %s frequencies."
                         % (desired.size, bands.size))
    desired.shape = (-1, 2)
    if (np.diff(bands) <= 0).any() or (np.diff(bands[:, 0]) < 0).any():
        raise ValueError("bands must be monotonically nondecreasing and have "
                         "width > 0.")
    if (bands[:-1, 1] > bands[1:, 0]).any():
        raise ValueError("bands must not overlap.")
    if (desired < 0).any():
        raise ValueError("desired must be non-negative.")
    if weight is None:
        weight = np.ones(len(desired))
    weight = np.asarray(weight).flatten()
    if len(weight) != len(desired):
        raise ValueError("weight must be the same size as the number of "
                         "band pairs (%s)." % (len(bands),))
    if (weight < 0).any():
        raise ValueError("weight must be non-negative.")

    # Set up the linear matrix equation to be solved, Qa = b

    # We can express Q(k,n) = 0.5 Q1(k,n) + 0.5 Q2(k,n)
    # where Q1(k,n)=q(k−n) and Q2(k,n)=q(k+n), i.e. a Toeplitz plus Hankel.

    # We omit the factor of 0.5 above, instead adding it during coefficient
    # calculation.

    # We also omit the 1/π from both Q and b equations, as they cancel
    # during solving.

    # We have that:
    #     q(n) = 1/π ∫W(ω)cos(nω)dω (over 0->π)
    # Using our nomalization ω=πf and with a constant weight W over each
    # interval f1->f2 we get:
    #     q(n) = W∫cos(πnf)df (0->1) = Wf sin(πnf)/πnf
    # integrated over each f1->f2 pair (i.e., value at f2 - value at f1).
    n = np.arange(numtaps)[:, np.newaxis, np.newaxis]
    q = np.dot(np.diff(np.sinc(bands * n) * bands, axis=2)[:, :, 0], weight)

    # Now we assemble our sum of Toeplitz and Hankel
    Q1 = toeplitz(q[:M+1])
    Q2 = hankel(q[:M+1], q[M:])
    Q = Q1 + Q2

    # Now for b(n) we have that:
    #     b(n) = 1/π ∫ W(ω)D(ω)cos(nω)dω (over 0->π)
    # Using our normalization ω=πf and with a constant weight W over each
    # interval and a linear term for D(ω) we get (over each f1->f2 interval):
    #     b(n) = W ∫ (mf+c)cos(πnf)df
    #          = f(mf+c)sin(πnf)/πnf + mf**2 cos(nπf)/(πnf)**2
    # integrated over each f1->f2 pair (i.e., value at f2 - value at f1).
    n = n[:M + 1]  # only need this many coefficients here
    # Choose m and c such that we are at the start and end weights
    m = (np.diff(desired, axis=1) / np.diff(bands, axis=1))
    c = desired[:, [0]] - bands[:, [0]] * m
    b = bands * (m*bands + c) * np.sinc(bands * n)
    # Use L'Hospital's rule here for cos(nπf)/(πnf)**2 @ n=0
    b[0] -= m * bands * bands / 2.
    b[1:] += m * np.cos(n[1:] * np.pi * bands) / (np.pi * n[1:]) ** 2
    b = np.dot(np.diff(b, axis=2)[:, :, 0], weight)

    # Now we can solve the equation
    try:  # try the fast way
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter('always')
            a = solve(Q, b, sym_pos=True, check_finite=False)
        for ww in w:
            if (ww.category == LinAlgWarning and
                    str(ww.message).startswith('Ill-conditioned matrix')):
                raise LinAlgError(str(ww.message))
    except LinAlgError:  # in case Q is rank deficient
        # This is faster than pinvh, even though we don't explicitly use
        # the symmetry here. gelsy was faster than gelsd and gelss in
        # some non-exhaustive tests.
        a = lstsq(Q, b, lapack_driver='gelsy')[0]

    # make coefficients symmetric (linear phase)
    coeffs = np.hstack((a[:0:-1], 2 * a[0], a[1:]))
    return coeffs
Example #27
0
 def new_svd(*args, **kwargs):
     raise LinAlgError()
Example #28
0
def spsolve_triangular(A, b, lower=True, overwrite_A=False, overwrite_b=False):
    """
    Solve the equation `A x = b` for `x`, assuming A is a triangular matrix.

    Parameters
    ----------
    A : (M, M) sparse matrix
        A sparse square triangular matrix. Should be in CSR format.
    b : (M,) or (M, N) array_like
        Right-hand side matrix in `A x = b`
    lower : bool, optional
        Whether `A` is a lower or upper triangular matrix.
        Default is lower triangular matrix.
    overwrite_A : bool, optional
        Allow changing `A`. The indices of `A` are going to be sorted and zero
        entries are going to be removed.
        Enabling gives a performance gain. Default is False.
    overwrite_b : bool, optional
        Allow overwriting data in `b`.
        Enabling gives a performance gain. Default is False.

    Returns
    -------
    x : (M,) or (M, N) ndarray
        Solution to the system `A x = b`.  Shape of return matches shape of `b`.

    Raises
    ------
    LinAlgError
        If `A` is singular or not triangular.
    ValueError
        If shape of `A` or shape of `b` do not match the requirements.

    Notes
    -----
    .. versionadded:: 0.19.0
    """

    # Check the input for correct type and format.
    if not isspmatrix_csr(A):
        warn('CSR matrix format is required. Converting to CSR matrix.',
             SparseEfficiencyWarning)
        A = csr_matrix(A)
    elif not overwrite_A:
        A = A.copy()

    if A.shape[0] != A.shape[1]:
        raise ValueError(
            'A must be a square matrix but its shape is {}.'.format(A.shape))

    A.eliminate_zeros()
    A.sort_indices()

    b = np.asanyarray(b)

    if b.ndim not in [1, 2]:
        raise ValueError('b must have 1 or 2 dims but its shape is {}.'.format(
            b.shape))
    if A.shape[0] != b.shape[0]:
        raise ValueError(
            'The size of the dimensions of A must be equal to '
            'the size of the first dimension of b but the shape of A is '
            '{} and the shape of b is {}.'.format(A.shape, b.shape))

    # Init x as copy of b.
    if overwrite_b:
        x = b
    else:
        x = b.copy()

    # Choose forward or backward order.
    if lower:
        row_indices = range(len(b))
    else:
        row_indices = range(len(b) - 1, -1, -1)

    # Fill x iteratively.
    for i in row_indices:

        # Get indices for i-th row.
        indptr_start = A.indptr[i]
        indptr_stop = A.indptr[i + 1]
        if lower:
            A_diagonal_index_row_i = indptr_stop - 1
            A_off_diagonal_indices_row_i = slice(indptr_start, indptr_stop - 1)
        else:
            A_diagonal_index_row_i = indptr_start
            A_off_diagonal_indices_row_i = slice(indptr_start + 1, indptr_stop)

        # Check regularity and triangularity of A.
        if indptr_stop <= indptr_start or A.indices[A_diagonal_index_row_i] < i:
            raise LinAlgError('A is singular: '
                              '{}th diagonal is zero!'.format(i))
        if A.indices[A_diagonal_index_row_i] > i:
            raise LinAlgError('A is no triangular matrix: entry '
                              '[{},{}] is not zero!'.format(
                                  i, A.indices[A_diagonal_index_row_i]))

        # Incorporate off-diagonal entries.
        A_column_indices_in_row_i = A.indices[A_off_diagonal_indices_row_i]
        A_values_in_row_i = A.data[A_off_diagonal_indices_row_i]
        x[i] -= np.dot(x[A_column_indices_in_row_i].T, A_values_in_row_i)

        # Compute i-th entry of x.
        x[i] /= A.data[A_diagonal_index_row_i]

    return x
Example #29
0
def discretize(vectors,
               copy=True,
               max_svd_restarts=30,
               n_iter_max=20,
               random_state=None):
    """Search for a partition matrix (clustering) which is closest to the
    eigenvector embedding.

    Parameters
    ----------
    vectors : array-like, shape: (n_samples, n_clusters)
        The embedding space of the samples.

    copy : boolean, optional, default: True
        Whether to copy vectors, or perform in-place normalization.

    max_svd_restarts : int, optional, default: 30
        Maximum number of attempts to restart SVD if convergence fails

    n_iter_max : int, optional, default: 30
        Maximum number of iterations to attempt in rotation and partition
        matrix search if machine precision convergence is not reached

    random_state: int seed, RandomState instance, or None (default)
        A pseudo random number generator used for the initialization of the
        of the rotation matrix

    Returns
    -------
    labels : array of integers, shape: n_samples
        The labels of the clusters.

    References
    ----------

    - Multiclass spectral clustering, 2003
      Stella X. Yu, Jianbo Shi
      http://www1.icsi.berkeley.edu/~stellayu/publication/doc/2003kwayICCV.pdf

    Notes
    -----

    The eigenvector embedding is used to iteratively search for the
    closest discrete partition.  First, the eigenvector embedding is
    normalized to the space of partition matrices. An optimal discrete
    partition matrix closest to this normalized embedding multiplied by
    an initial rotation is calculated.  Fixing this discrete partition
    matrix, an optimal rotation matrix is calculated.  These two
    calculations are performed until convergence.  The discrete partition
    matrix is returned as the clustering solution.  Used in spectral
    clustering, this method tends to be faster and more robust to random
    initialization than k-means.

    """

    from scipy.sparse import csc_matrix
    from scipy.linalg import LinAlgError

    random_state = check_random_state(random_state)

    vectors = as_float_array(vectors, copy=copy)

    eps = np.finfo(float).eps
    n_samples, n_components = vectors.shape

    # Normalize the eigenvectors to an equal length of a vector of ones.
    # Reorient the eigenvectors to point in the negative direction with respect
    # to the first element.  This may have to do with constraining the
    # eigenvectors to lie in a specific quadrant to make the discretization
    # search easier.
    norm_ones = np.sqrt(n_samples)
    for i in range(vectors.shape[1]):
        vectors[:, i] = (vectors[:, i] / norm(vectors[:, i])) \
            * norm_ones
        if vectors[0, i] != 0:
            vectors[:, i] = -1 * vectors[:, i] * np.sign(vectors[0, i])

    # Normalize the rows of the eigenvectors.  Samples should lie on the unit
    # hypersphere centered at the origin.  This transforms the samples in the
    # embedding space to the space of partition matrices.
    vectors = vectors / np.sqrt((vectors**2).sum(axis=1))[:, np.newaxis]

    svd_restarts = 0
    has_converged = False

    # If there is an exception we try to randomize and rerun SVD again
    # do this max_svd_restarts times.
    while (svd_restarts < max_svd_restarts) and not has_converged:

        # Initialize first column of rotation matrix with a row of the
        # eigenvectors
        rotation = np.zeros((n_components, n_components))
        rotation[:, 0] = vectors[random_state.randint(n_samples), :].T

        # To initialize the rest of the rotation matrix, find the rows
        # of the eigenvectors that are as orthogonal to each other as
        # possible
        c = np.zeros(n_samples)
        for j in range(1, n_components):
            # Accumulate c to ensure row is as orthogonal as possible to
            # previous picks as well as current one
            c += np.abs(np.dot(vectors, rotation[:, j - 1]))
            rotation[:, j] = vectors[c.argmin(), :].T

        last_objective_value = 0.0
        n_iter = 0

        while not has_converged:
            n_iter += 1

            t_discrete = np.dot(vectors, rotation)

            labels = t_discrete.argmax(axis=1)
            vectors_discrete = csc_matrix(
                (np.ones(len(labels)), (np.arange(0, n_samples), labels)),
                shape=(n_samples, n_components))

            t_svd = vectors_discrete.T * vectors

            try:
                U, S, Vh = np.linalg.svd(t_svd)
                svd_restarts += 1
            except LinAlgError:
                print "SVD did not converge, randomizing and trying again"
                break

            ncut_value = 2.0 * (n_samples - S.sum())
            if ((abs(ncut_value - last_objective_value) < eps)
                    or (n_iter > n_iter_max)):
                has_converged = True
            else:
                # otherwise calculate rotation and continue
                last_objective_value = ncut_value
                rotation = np.dot(Vh.T, U.T)

    if not has_converged:
        raise LinAlgError('SVD did not converge')
    return labels
Example #30
0
    def solve_posterior(self, x, scale_mean=False):
        r"""
        Solve FEM posterior in mesh space

        Solve for the FEM posterior conditioned on the data on the FEM mesh. The solution
        is stored in the preallocated Firedrake ``Function``.

        Note that if an ensemble communicator was used to parallelize the covariance solves,
        the solution is only stored in the root of the ensemble communicator. The Firedrake
        ``Function`` on the other processes will not be modified.

        The optional ``scale_mean`` argument determines if the solution is to be re-scaled
        by the model discrepancy scaling factor. This value is by default ``False``.
        To re-scale to match the data, pass ``scale_mean=True``.

        :param x: Firedrake ``Function`` for holding the solution. This is modified in place
                  by the method.
        :type x: Firedrake Function
        :param scale_mean: Boolean indicating if the mean should be scaled by the model
                           discrepancy scaling factor. Optional, default is ``False``
        :type scale_mean: bool
        :returns: None
        """

        if not isinstance(bool(scale_mean), bool):
            raise TypeError("scale_mean argument must be boolean-like")

        # create interpolation matrix if not cached

        if self.Cu is None or self.x is None:
            self.solve_prior()

        if self.params is None:
            raise ValueError("must set parameter values to solve posterior")

        rho = np.exp(self.params[0])

        if scale_mean:
            scalefact = rho
        else:
            scalefact = 1.

        # remaining solves are just done on ensemble root

        if self.ensemble_comm.rank == 0:

            if self.G.comm.rank == 0:
                Ks = self.data.calc_K_plus_sigma(self.params[1:])
                try:
                    LK = cho_factor(Ks)
                except LinAlgError:
                    raise LinAlgError(
                        "Error attempting to compute the Cholesky factorization "
                        + "of the model discrepancy")
                tmp_dataspace_1 = cho_solve(LK, self.data.get_data())
            else:
                tmp_dataspace_1 = np.zeros(0)

            # interpolate to dataspace

            tmp_meshspace_1 = self.im.interp_data_to_mesh(tmp_dataspace_1)

            # solve forcing covariance and interpolate to dataspace

            tmp_meshspace_2 = solve_forcing_covariance(
                self.G, self.solver,
                tmp_meshspace_1)._scale(rho) + self.x.vector()

            tmp_dataspace_1 = self.im.interp_mesh_to_data(tmp_meshspace_2)

            if self.G.comm.rank == 0:
                try:
                    L = cho_factor(Ks + rho**2 * self.Cu)
                except LinAlgError:
                    raise LinAlgError(
                        "Error attempting to compute the Cholesky factorization "
                        + "of the model discrepancy plus forcing covariance")
                tmp_dataspace_2 = cho_solve(L, tmp_dataspace_1)
            else:
                tmp_dataspace_2 = np.zeros(0)

            tmp_meshspace_1 = self.im.interp_data_to_mesh(tmp_dataspace_2)

            tmp_meshspace_1 = solve_forcing_covariance(
                self.G, self.solver, tmp_meshspace_1)._scale(rho**2)

            x.assign(
                (tmp_meshspace_2 - tmp_meshspace_1)._scale(scalefact).function)