Esempio n. 1
0
def is_spd(A):
  """Returns True if A is symmetric positive definite.
  """
  assert A.ndim == 2, "Expecting 2D matrix."
  if not is_symmetric(A):
    return False
  eigvals, _ = eig(A, sort=False)
  return np.all(eigvals >= 0)
Esempio n. 2
0
def projected_iteration(A, k, max_iter=1000, sort=True):
    """Sequentially find the k eigenpairs of a symmetric matrix.

  Concretely, combines power iteration and deflation to find
  eigenpairs in order of decreasing magnitude.

  Args:
    A: a square symmetric array of shape (N, N).
    k (int): the number of eigenpairs to return.
    sort (bool): Whether to sort by decreasing eigenvalue magnitude.

  Returns:
    e, v: eigenvalues and eigenvectors. The eigenvectors are
      stacked column-wise.
  """
    assert utils.is_symmetric(A), "[!] Matrix must be symmetric."
    assert k > 0 and k <= A.shape[0], "[!] k must be between 1 and {}.".format(
        A.shape[0])

    eigvecs = np.zeros((A.shape[0], k))
    eigvals = np.zeros(A.shape[0])
    for i in range(k):
        v = np.random.randn(A.shape[0])
        for _ in range(max_iter):
            # project out computed eigenvectors
            proj_sum = np.zeros_like(v)
            for j in range(i):
                proj_sum += utils.projection(v, eigvecs[:, j])
            v -= proj_sum

            v_new = A @ v
            v_new = utils.normalize(v_new)
            if np.all(np.abs(v_new - v) < 1e-8):
                break
            v = v_new
        e = single.rayleigh_quotient(A, v)

        # store eigenpair
        eigvecs[:, i] = np.array(v)
        eigvals[i] = e

    # sort by largest absolute eigenvalue
    if sort:
        idx = np.abs(eigvals).argsort()[::-1]
        eigvecs = eigvecs[:, idx]
        eigvals = eigvals[idx]

    return eigvals, eigvecs
Esempio n. 3
0
def hessenberg(A, calc_q=False):
    """Reduce a square matrix to upper Hessenberg form using Householder reflections.

  If the input matrix is symmetric, the resulting Hessenberg form is
  reduced to tridiagonal form.

  Args:
    A: A square matrix of shape (M, M).
    calc_q (bool): Whether to explicitly compute the product of
      similarity transform Householder matrices.

  Returns:
    A: The upper Hessenberg form of the matrix of shape (M, M).
    Q: The product of Householder matrices of shape (M, M). Only
      returned if `calc_q=True`.
  """
    assert utils.is_square(A), "[!] Matrix must be square."
    is_symm = utils.is_symmetric(A)
    A = np.array(A)
    M, _ = A.shape
    vs = []
    for i in range(M - 2):
        a = A[i + 1:, i]
        c = utils.l2_norm(a)
        s = utils.sign(a[0])
        e = utils.basis_vec(0, len(a), flat=True)
        v = a + s * c * e
        vs.append(v)
        # left transform
        for j in range(i, M):
            A[i + 1:,
              j] = A[i + 1:, j] - (2 * v.T @ A[i + 1:, j]) / (v.T @ v) * v
        # right transform
        for j in range(i if is_symm else 0, M):
            A[j, i + 1:M] = A[j, i + 1:M] - 2 * ((A[j, i + 1:M].T @ v) /
                                                 (v.T @ v)) * v.T
    if calc_q:
        Q = np.eye(M)
        for i in range(M):
            for j, v in enumerate(reversed(vs)):
                Q[M - j - 2:,
                  i] -= (2 * v.T @ Q[M - j - 2:, i]) / (v.T @ v) * v
        return A, Q
    return A
Esempio n. 4
0
def power_iteration(A, max_iter=1000):
    """Finds the largest eigenpair of a symmetric matrix.

  Args:
    A: a square symmetric array of shape (N, N).

  Returns:
    e, v: eigenvalue and right eigenvector.
  """
    assert utils.is_symmetric(A), "[!] Matrix must be symmetric."
    v = np.random.randn(A.shape[0])
    for i in range(max_iter):
        v_new = A @ v
        v_new = utils.normalize(v_new)
        if np.all(np.abs(v_new - v) < 1e-8):
            break
        v = v_new
    e = rayleigh_quotient(A, v)
    return e, v
Esempio n. 5
0
def rayleigh_quotient_iteration(A, mu, max_iter=1000):
    """Finds an eigenpair closest to an initial eigenvalue guess.

  Args:
    A: a square symmetric array of shape (N, N).
    mu: an initial eigenvalue guess.

  Returns:
    e, v: eigenvalue and right eigenvector.
  """
    assert utils.is_symmetric(A), "[!] Matrix must be symmetric."
    v = np.random.randn(A.shape[0])
    for i in range(max_iter):
        v_new = solve(A - mu * np.eye(A.shape[0]), v)
        v_new = utils.normalize(v_new)
        if np.all(np.abs(v_new - v) < 1e-8):
            break
        v = v_new
        mu = rayleigh_quotient(A, v)
    return mu, v
Esempio n. 6
0
def inverse_iteration(A, max_iter=1000):
    """Finds the smallest eigenpair of a symmetric matrix.

  Args:
    A: a square symmetric array of shape (N, N).

  Returns:
    e, v: eigenvalue and right eigenvector.
  """
    assert utils.is_symmetric(A), "[!] Matrix must be symmetric."
    v = np.random.randn(A.shape[0])
    PLU = LU(A, pivoting='partial').decompose()
    for i in range(max_iter):
        v_new = solve(PLU, v)
        v_new = utils.normalize(v_new)
        if np.all(np.abs(v_new - v) < 1e-8):
            break
        v = v_new
    e = rayleigh_quotient(A, v)
    return e, v
Esempio n. 7
0
def qr_algorithm(A, hess=True, sort=True):
    """The de-facto algorithm for finding all eigenpairs of a symmetric matrix.

  Args:
    A: a square symmetric array of shape (N, N).
    hess (bool): Whether to compute the Hessenberg form
      of the matrix before starting the QR iterations.
    sort (bool): Whether to sort by decreasing eigenvalue magnitude.

  Returns:
    e, v: eigenvalues and eigenvectors. The eigenvectors are
      stacked column-wise.
  """
    assert utils.is_symmetric(A), "[!] Matrix must be symmetric."
    backup = np.array(A)
    if hess:
        A = hessenberg(A, calc_q=False)
    M = A.shape[0]
    for k in range(1000):
        mu = A[M - 1, M - 1]  # set the shift to be the last diagonal element
        Q, R = QR(A - mu * np.eye(M)).decompose()
        A_new = R @ Q + mu * np.eye(M)
        if np.all(np.abs(A_new - A) < 1e-8):
            break
        A = A_new
    mus = utils.diag(A)
    eigvecs = np.zeros_like(A)
    eigvals = np.zeros(A.shape[0])
    for i, mu in enumerate(mus):
        eigval, eigvec = single.rayleigh_quotient_iteration(backup,
                                                            mu,
                                                            max_iter=1)
        eigvecs[:, i] = eigvec
        eigvals[i] = eigval
    if sort:
        idx = np.abs(eigvals).argsort()[::-1]
        eigvecs = eigvecs[:, idx]
        eigvals = eigvals[idx]
    return eigvals, eigvecs
Esempio n. 8
0
    def test_hessenberg_symmetric_stays_symmetric(self):
        M = random_symmetric(4)

        hess = multi.hessenberg(M)
        self.assertTrue(is_symmetric(hess))