Source code for mud.util

import numpy as np
from scipy.special import erfinv


[docs]def std_from_equipment(tolerance=0.1, probability=0.95): """ Converts tolerance `tolerance` for precision of measurement equipment to a standard deviation, scaling so that (100`probability`) percent of measurements are within `tolerance`. A mean of zero is assumed. `erfinv` is imported from `scipy.special` """ standard_deviation = tolerance / (erfinv(probability) * np.sqrt(2)) return standard_deviation
[docs]def transform_linear_map(operator, data, std): """ Takes a linear map `operator` of size (len(data), dim_input) or (1, dim_input) for repeated observations, along with a vector `data` representing observations. It is assumed that `data` is formed with `M@truth + sigma` where `sigma ~ N(0, std)` This then transforms it to the MWE form expected by the DCI framework. It returns a matrix `A` of shape (1, dim_input) and np.float `b` and transforms it to the MWE form expected by the DCI framework. >>> X = np.ones((10, 2)) >>> x = np.array([0.5, 0.5]).reshape(-1, 1) >>> std = 1 >>> d = X @ x >>> A, b = transform_linear_map(X, d, std) >>> np.linalg.norm(A @ x + b) 0.0 >>> A, b = transform_linear_map(X, d, [std]*10) >>> np.linalg.norm(A @ x + b) 0.0 >>> A, b = transform_linear_map(np.array([[1, 1]]), d, std) >>> np.linalg.norm(A @ x + b) 0.0 >>> A, b = transform_linear_map(np.array([[1, 1]]), d, [std]*10) Traceback (most recent call last): ... ValueError: For repeated measurements, pass a float for std """ if isinstance(data, np.ndarray): data = data.ravel() num_observations = len(data) if operator.shape[0] > 1: # if not repeated observations assert ( operator.shape[0] == num_observations ), f"Operator shape mismatch, op={operator.shape}, obs={num_observations}" if isinstance(std, (float, int)): std = np.array([std] * num_observations) if isinstance(std, (list, tuple)): std = np.array(std) assert len(std) == num_observations, "Standard deviation shape mismatch" assert 0 not in np.round(std, 14), "Std must be > 1E-14" D = np.diag(1.0 / (std * np.sqrt(num_observations))) A = np.sum(D @ operator, axis=0) else: if isinstance(std, (list, tuple, np.ndarray)): raise ValueError("For repeated measurements, pass a float for std") assert std > 1e-14, "Std must be > 1E-14" A = np.sqrt(num_observations) / std * operator b = -1.0 / np.sqrt(num_observations) * np.sum(np.divide(data, std)) return A, b
[docs]def transform_linear_setup(operator_list, data_list, std_list): if isinstance(std_list, (float, int)): std_list = [std_list] * len(data_list) # repeat process for multiple quantities of interest results = [ transform_linear_map(o, d, s) for o, d, s in zip(operator_list, data_list, std_list) ] operators = [r[0] for r in results] datas = [r[1] for r in results] return np.vstack(operators), np.vstack(datas)
[docs]def null_space(A, rcond=None): """ Construct an orthonormal basis for the null space of A using SVD Method is slight modification of ``scipy.linalg`` Parameters ---------- A : (M, N) array_like Input array rcond : float, optional Relative condition number. Singular values ``s`` smaller than ``rcond * max(s)`` are considered zero. Default: floating point eps * max(M,N). Returns ------- Z : (N, K) ndarray Orthonormal basis for the null space of A. K = dimension of effective null space, as determined by rcond Examples -------- One-dimensional null space: >>> import numpy as np >>> from mud.util import null_space >>> A = np.array([[1, 1], [1, 1]]) >>> ns = null_space(A) >>> ns * np.sign(ns[0,0]) # Remove the sign ambiguity of the vector array([[ 0.70710678], [-0.70710678]]) Two-dimensional null space: >>> B = np.random.rand(3, 5) >>> Z = null_space(B) >>> Z.shape (5, 2) >>> np.allclose(B.dot(Z), 0) True The basis vectors are orthonormal (up to rounding error): >>> np.allclose(Z.T.dot(Z), np.eye(2)) True """ u, s, vh = np.linalg.svd(A, full_matrices=True) M, N = u.shape[0], vh.shape[1] if rcond is None: rcond = np.finfo(s.dtype).eps * max(M, N) tol = np.amax(s) * rcond num = np.sum(s > tol, dtype=int) Q = vh[num:, :].T.conj() return Q