Completed
Branch master (f50597)
by Wouter
52s
created

libtlda.util.regularize_matrix()   B

Complexity

Conditions 5

Size

Total Lines 43
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 15
dl 0
loc 43
rs 8.0894
c 0
b 0
f 0
cc 5
nop 2
1
"""
2
Utility functions necessary for different classifiers.
3
4
Contains algebraic operations, and label encodings.
5
"""
6
7 1
import numpy as np
8 1
import numpy.linalg as al
9 1
import scipy.stats as st
10
11
12 1
def one_hot(y, fill_k=False, one_not=False):
13
    """Map to one-hot encoding."""
14
    # Check for negative labels
15
    assert np.all(y >= 0)
16
17
    # Number of samples
18
    N = y.shape[0]
19
20
    # Number of classes
21
    K = len(np.unique(y))
22
23
    # Preallocate array
24
    if one_not:
25
        Y = -np.ones((N, K))
26
    else:
27
        Y = np.zeros((N, K))
28
29
    # Set k-th column to 1 for n-th sample
30
    for n in range(N):
31
32
        if fill_k:
33
            Y[n, y[n]] = y[n]
34
        else:
35
            Y[n, y[n]] = 1
36
37
    return Y
38
39
40 1
def regularize_matrix(A, a=0.0):
41
    """
42
    Regularize matrix by ensuring minimum eigenvalues.
43
44
    INPUT   (1) array 'A': square matrix
45
            (2) float 'a': constraint on minimum eigenvalue
46
    OUTPUT  (1) array 'B': constrained matrix
47
    """
48
    # Check for square matrix
49
    N, M = A.shape
50
    assert N == M
51
52
    # Check for valid matrix entries
53
    assert not np.any(np.isnan(A)) or np.any(np.isinf(A))
54
55
    # Check for non-negative minimum eigenvalue
56
    if a < 0:
57
        raise ValueError('minimum eigenvalue cannot be negative.')
58
59
    elif a == 0:
60
        return A
61
62
    else:
63
        # Ensure symmetric matrix
64
        A = (A + A.T) / 2
65
66
        # Eigenvalue decomposition
67
        E, V = al.eig(A)
68
69
        # Regularization matrix
70
        aI = a * np.eye(N)
71
72
        # Subtract regularization
73
        E = np.diag(E) + aI
74
75
        # Cap negative eigenvalues at zero
76
        E = np.maximum(0, E)
77
78
        # Reconstruct matrix
79
        B = np.dot(np.dot(V, E), V.T)
80
81
        # Add back subtracted regularization
82
        return B + aI
83
84
85 1
def is_pos_def(X):
86
    """Check for positive definiteness."""
87 1
    return np.all(np.linalg.eigvals(X) > 0)
88
89
90 1
def nullspace(A, atol=1e-13, rtol=0):
91
    """
92
    Compute an approximate basis for the nullspace of A.
93
94
    INPUT   (1) array 'A': 1-D array with length k will be treated
95
                as a 2-D with shape (1, k).
96
            (2) float 'atol': the absolute tolerance for a zero singular value.
97
                Singular values smaller than `atol` are considered to be zero.
98
            (3) float 'rtol': relative tolerance. Singular values less than
99
                rtol*smax are considered to be zero, where smax is the largest
100
                singular value.
101
102
                If both `atol` and `rtol` are positive, the combined tolerance
103
                is the maximum of the two; tol = max(atol, rtol * smax)
104
                Singular values smaller than `tol` are considered to be zero.
105
    OUTPUT  (1) array 'B': if A is an array with shape (m, k), then B will be
106
                an array with shape (k, n), where n is the estimated dimension
107
                of the nullspace of A.  The columns of B are a basis for the
108
                nullspace; each element in np.dot(A, B) will be
109
                approximately zero.
110
    """
111
    # Expand A to a matrix
112
    A = np.atleast_2d(A)
113
114
    # Singular value decomposition
115
    u, s, vh = al.svd(A)
116
117
    # Set tolerance
118
    tol = max(atol, rtol * s[0])
119
120
    # Compute the number of non-zero entries
121
    nnz = (s >= tol).sum()
122
123
    # Conjugate and transpose to ensure real numbers
124
    ns = vh[nnz:].conj().T
125
126
    return ns
127