Source code for squirrel.util
"""This module contains class and functions for general use."""
from scipy import linalg as la
import numpy as np
[docs]
def get_nearest_positive_definite_matrix(matrix):
"""Find the nearest positive-definite matrix to input.
A Python/Numpy port of John D'Errico's `nearestSPD` MATLAB code [1], which
credits [2].
[1] https://www.mathworks.com/matlabcentral/fileexchange/42885-nearestspd
[2] N.J. Higham, "Computing a nearest symmetric positive semidefinite
matrix" (1988): https://doi.org/10.1016/0024-3795(88)90223-6
Adapted from https://gist.github.com/fasiha/fdb5cec2054e6f1c6ae35476045a0bbd
:param matrix: matrix to find nearest positive-definite matrix to
:type matrix: numpy.ndarray
:return: nearest positive-definite matrix
:rtype: numpy.ndarray
"""
# Step 1: Make the matrix symmetric
b = (matrix + matrix.T) / 2
# Step 2: Perform Singular Value Decomposition (SVD)
_, s, v = la.svd(b)
# Step 3: Construct the symmetric positive semi-definite matrix
h = np.dot(v.T, np.dot(np.diag(s), v))
# Step 4: Average b and h to get a new symmetric matrix
matrix_2 = (b + h) / 2
# Step 5: Ensure the matrix is symmetric
matrix_3 = (matrix_2 + matrix_2.T) / 2
# Step 6: Check if the matrix is positive-definite
if is_positive_definite(matrix_3):
return matrix_3
# Step 7: If not, adjust the matrix to make it positive-definite
spacing = np.spacing(la.norm(matrix))
identity = np.eye(matrix.shape[0])
k = 1
while not is_positive_definite(matrix_3):
min_eigenvalue = np.min(np.real(la.eigvals(matrix_3)))
matrix_3 += identity * (-min_eigenvalue * k**2 + spacing)
k += 1
return matrix_3
[docs]
def is_positive_definite(matrix):
"""Returns true when input is positive-definite, via Cholesky.
This function attempts to perform a Cholesky decomposition of the input matrix. If
the decomposition is successful, the matrix is positive-definite. If it fails, the
matrix is not positive-definite.
:param matrix: matrix to check
:type matrix: numpy.ndarray
:return: True if matrix is positive-definite
:rtype: bool
"""
try:
_ = la.cholesky(matrix, lower=True)
return True
except la.LinAlgError:
return False
[docs]
def powerbin_capacity_spec(index, signal, noise):
"""Calculates (S/N)^2 for a bin from its pixel indices for PowerBin method.
This function is the default specified for get_power_binning_map().
:param index: The pixel indices for the bin.
:type index: array
:param signal: The signal image for the datacube.
:type signal: array
:param noise: The signal image for the datacube.
:type noise: array
:return: (S/N)^2 for the bin at its pixel indices.
:rtype: float
"""
# Standard S/N formula for uncorrelated noise
sn = np.sum(signal[index]) / np.sqrt(np.sum(noise[index] ** 2))
# Example for correlated noise (see full example file for details):
# sn /= 1 + 1.07 * np.log10(len(index))
return sn**2