geometry_tools.utils.numerical

 1import numpy as np
 2
 3def svd_kernel(mat, assume_full_rank=False, matching_rank=True,
 4               tolerance=1e-8, with_dimensions=False,
 5               with_loc=False):
 6
 7    if assume_full_rank and not matching_rank:
 8        raise ValueError("matching_rank must be True if assume_full_rank is True")
 9
10    _, s, v = np.linalg.svd(mat)
11
12    min_kernel_dim = max(mat.shape[-1] - mat.shape[-2], 0)
13
14    if assume_full_rank:
15        kernel_dim = min_kernel_dim
16    else:
17        small_sv = s < tolerance
18        kernel_dims = min_kernel_dim + np.count_nonzero(small_sv, axis=-1)
19        actual_ranks_match = np.all(
20            kernel_dims == np.atleast_1d(kernel_dims)[0]
21        )
22
23        if matching_rank and not actual_ranks_match:
24            raise ValueError(
25                "Input matrices do not have matching rank. Try calling "
26                "this function with matching_rank=False."
27            )
28
29        kernel_dim = (kernel_dims).flatten()[0]
30
31    if matching_rank:
32        return v[..., -kernel_dim:, :].swapaxes(-1, -2)
33
34    possible_dims = np.unique(kernel_dims)
35    kernel_bases = []
36    kernel_dim_loc = []
37
38    for kernel_dim in possible_dims:
39        where_dim = (kernel_dims == kernel_dim)
40        kernel_bases.append(
41            v[where_dim, -kernel_dim:, :].swapaxes(-1, -2)
42        )
43        kernel_dim_loc.append(where_dim)
44
45    # flat is better than nested
46    if not with_loc and not with_dimensions:
47        return tuple(kernel_bases)
48
49    if with_dimensions and not with_loc:
50        return (possible_dims, tuple(kernel_bases))
51
52    if with_loc and not with_dimensions:
53        return (tuple(kernel_bases), tuple(kernel_dim_loc))
54
55    return (possible_dims, tuple(kernel_bases), tuple(kernel_dim_loc))
def svd_kernel( mat, assume_full_rank=False, matching_rank=True, tolerance=1e-08, with_dimensions=False, with_loc=False):
 4def svd_kernel(mat, assume_full_rank=False, matching_rank=True,
 5               tolerance=1e-8, with_dimensions=False,
 6               with_loc=False):
 7
 8    if assume_full_rank and not matching_rank:
 9        raise ValueError("matching_rank must be True if assume_full_rank is True")
10
11    _, s, v = np.linalg.svd(mat)
12
13    min_kernel_dim = max(mat.shape[-1] - mat.shape[-2], 0)
14
15    if assume_full_rank:
16        kernel_dim = min_kernel_dim
17    else:
18        small_sv = s < tolerance
19        kernel_dims = min_kernel_dim + np.count_nonzero(small_sv, axis=-1)
20        actual_ranks_match = np.all(
21            kernel_dims == np.atleast_1d(kernel_dims)[0]
22        )
23
24        if matching_rank and not actual_ranks_match:
25            raise ValueError(
26                "Input matrices do not have matching rank. Try calling "
27                "this function with matching_rank=False."
28            )
29
30        kernel_dim = (kernel_dims).flatten()[0]
31
32    if matching_rank:
33        return v[..., -kernel_dim:, :].swapaxes(-1, -2)
34
35    possible_dims = np.unique(kernel_dims)
36    kernel_bases = []
37    kernel_dim_loc = []
38
39    for kernel_dim in possible_dims:
40        where_dim = (kernel_dims == kernel_dim)
41        kernel_bases.append(
42            v[where_dim, -kernel_dim:, :].swapaxes(-1, -2)
43        )
44        kernel_dim_loc.append(where_dim)
45
46    # flat is better than nested
47    if not with_loc and not with_dimensions:
48        return tuple(kernel_bases)
49
50    if with_dimensions and not with_loc:
51        return (possible_dims, tuple(kernel_bases))
52
53    if with_loc and not with_dimensions:
54        return (tuple(kernel_bases), tuple(kernel_dim_loc))
55
56    return (possible_dims, tuple(kernel_bases), tuple(kernel_dim_loc))