def compare_analytical_derivative(n, nonuniform=False): #======================================================================= if nonuniform: r = np.linspace(0, 4.5**9, 2500)**(1 / 9) else: r = np.linspace(2, 5, 2500) dr = np.mean(np.diff(r)) # Distance axis for the derivative if n == 0: rn = r elif n == 1: rn = r - dr / 2 elif n == 2: rn = r elif n == 3: rn = r - dr / 2 # Test function sig = 0.2 rmean = 3.5 P = 1 / sig / np.sqrt(2 * pi) * np.exp(-(r - rmean)**2 / 2 / sig**2) # Analytical derivatives of the Gaussian function if n == 0: dPnref = P elif n == 1: dPnref = -(rn - rmean) / sig**3 / np.sqrt(2 * pi) * np.exp( -(rn - rmean)**2 / 2 / sig**2) elif n == 2: dPnref = -(sig**2 - (rn - rmean)**2) / sig**5 / np.sqrt( 2 * pi) * np.exp(-(rn - rmean)**2 / 2 / sig**2) elif n == 3: dPnref = -((rn - rmean)**3 - 3 * ((rn - rmean)) * sig**2) / sig**7 / np.sqrt( 2 * pi) * np.exp(-(rn - rmean)**2 / 2 / sig**2) # Numerical finite-difference operators Ln = regoperator(r, n, includeedges=True) # Estimated derivatives of the Gaussian function dPn = Ln @ P if nonuniform: dPn = dPn[:-1] dPnref = dPnref[:-1] if nonuniform and n == 0: accuracy = 1e-5 elif nonuniform and n == 1: accuracy = 5e-2 elif nonuniform and n == 2: accuracy = 5e-1 elif nonuniform and n == 3: accuracy = 5e-0 else: accuracy = 10**(-5 + n) assert max(abs(dPn - dPnref)) < accuracy
def test_extrapenalty(): # ====================================================================== "Check that custom penalties can be passed and act on the solution" t = np.linspace(0, 3, 300) r = np.linspace(2, 5, 200) P = dd_gauss2(r, 3.5, 0.5, 0.5, 4, 0.1, 0.5) K = dipolarkernel(t, r) V = K @ P + whitegaussnoise(t, 0.15, seed=1) par0 = [2.5, 0.01, 0.1, 4.5, 0.01, 0.6] lb = [1, 0.01, 0, 1, 0.01, 0] ub = [20, 1, 1, 20, 1, 1] # Fit case it fails, stuck at "spicky" Gaussians model = lambda p: K @ dd_gauss2(r, *p) fit = snlls(V, model, par0, lb, ub) # Fit with Tikhonov penalty on the Gaussians model L = regoperator(r, 2) alpha = 1e-4 tikhonov = lambda p, _: alpha * L @ dd_gauss2(r, *p) fit_tikh = snlls(V, model, par0, lb, ub, extrapenalty=tikhonov) Pfit = dd_gauss2(r, *fit.nonlin) Pfit_tikh = dd_gauss2(r, *fit_tikh.nonlin) assert ovl(P, Pfit) < ovl(P, Pfit_tikh)
def assert_full_output(method): t = np.linspace(0, 5, 80) r = np.linspace(2, 5, 80) P = dd_gauss(r, 3, 0.4) K = dipolarkernel(t, r) L = regoperator(r, 2) V = K @ P alpha, alphas_evaled, functional, residuals, penalties = selregparam( V, K, cvxnnls, method='aic', algorithm=method, full_output=True, regop=L) errors = [] if np.size(alpha) != 1: errors.append("alphaopt is not a scalar") if len(functional) != len(alphas_evaled): errors.append( "The number of elements of functional values and evaluated alphas are different." ) if len(residuals) != len(penalties): errors.append( "The number of elements of evluated residuals and penalties are different" ) if not alpha in alphas_evaled: errors.append("The optimal alpha is not part of the evaluated alphas") assert not errors, f"Errors occured:\n{chr(10).join(errors)}"
def test_algorithms(): #======================================================================= "Check that the value returned by the the grid and Brent algorithms coincide" t = np.linspace(0, 5, 80) r = np.linspace(2, 5, 80) P = dd_gauss(r, 3, 0.2) K = dipolarkernel(t, r) L = regoperator(r, 2) V = K @ P + whitegaussnoise(t, 0.02, seed=1) alpha_grid = selregparam(V, K, cvxnnls, method='aic', algorithm='grid', regop=L) alpha_brent = selregparam(V, K, cvxnnls, method='aic', algorithm='brent', regop=L) assert abs(1 - alpha_grid / alpha_brent) < 0.15
def _prepare_linear_lsq(A, lb, ub, reg, L, tol, maxiter, nnlsSolver): """ Preparation of linear least-squares =================================== Evaluates the conditions of the linear least-squares problem and determines: - whether regularization is required - the type of boundary constraints - optimal linear least-squares solver to use """ Nlin = np.shape(A)[1] # Determine whether to use regularization penalty if reg == 'auto': illConditioned = np.linalg.cond(A) > 10 includeRegularization = illConditioned else: includeRegularization = reg # Check if the nonlinear and linear problems are constrained linearConstrained = (not np.all(np.isinf(lb))) or (not np.all( np.isinf(ub))) # Check for non-negativity constraints on the linear solution nonNegativeOnly = (np.all(lb == 0)) and (np.all(np.isinf(ub))) # Use an arbitrary axis axis = np.arange(Nlin) if L is None and includeRegularization: d = np.minimum(2, len(axis)) L = dl.regoperator(axis, d) # Prepare the linear solver # ---------------------------------------------------------- if not linearConstrained: # Unconstrained linear LSQ linSolver = np.linalg.solve parseResult = lambda result: result elif linearConstrained and not nonNegativeOnly: # Constrained linear LSQ linSolver = lambda AtA, Aty: lsq_linear( AtA, Aty, bounds=(lb, ub), method='bvls') parseResult = lambda result: result.x elif linearConstrained and nonNegativeOnly: # Non-negative linear LSQ if nnlsSolver == 'fnnls': linSolver = lambda AtA, Aty: fnnls( AtA, Aty, tol=tol, maxiter=maxiter) elif nnlsSolver == 'cvx': linSolver = lambda AtA, Aty: cvxnnls( AtA, Aty, tol=tol, maxiter=maxiter) parseResult = lambda result: result # Ensure correct formatting and shield against float-point errors validateResult = lambda result: np.maximum( lb, np.minimum(ub, np.atleast_1d(result))) # ---------------------------------------------------------- return axis, L, linSolver, parseResult, validateResult, includeRegularization
def test_L2shape_noedges(): #======================================================================= "Check that L2 is returned with correct size if edges are excluded" n = 100 r = np.linspace(2, 6, n) L = regoperator(r, 2, includeedges=False) assert L.shape == (n - 2, n)
def test_L0shape(): #======================================================================= "Check that L0 is returned with correct size" n = 100 r = np.linspace(2, 6, n) L = regoperator(r, 0) assert L.shape == (n, n)
def test_L2shape(): #======================================================================= "Check that L2 is returned with correct size" n = 100 r = np.linspace(2, 6, n) L = regoperator(r, 2, includeedges=True) assert L.shape == (n, n)
def test_edge_smoothing(): #======================================================================= "Check that L applies to edges and is NxN" r = np.arange(5) n = len(r) L = regoperator(r, 2, includeedges=True) Lref = (np.eye(n, n) * (-2) + np.eye(n, n, k=-1) + np.eye(n, n, k=1)) assert np.max(np.abs(L - Lref)) < 1e-10
def get_alpha_from_method(method): t = np.linspace(0, 5, 500) r = np.linspace(2, 5, 80) P = dd_gauss(r, 3.0, 0.16986436005760383) K = dipolarkernel(t, r) L = regoperator(r, 2, includeedges=True) V = K @ P alpha = selregparam(V, K, cvxnnls, method=method, noiselvl=0, regop=L) return np.log10(alpha)
def test_unconstrained(): #============================================================ "Check that unconstrained distributions are correctly fitted" t = np.linspace(-2, 4, 300) r = np.linspace(2, 6, 200) P = dd_gauss(r, 3, 0.2) K = dipolarkernel(t, r) L = regoperator(np.arange(len(r)), 2) V = K @ P fit = snlls(V, K, regparam=0.2) Pfit_ref = np.linalg.solve(K.T @ K + 0.2**2 * L.T @ L, K.T @ V) assert ovl(Pfit_ref, fit.param) > 0.99 # more than 99% overlap
def test_nonuniform_r(): #======================================================================= "Check the value returned when using a non-uniform distance axis" t = np.linspace(0, 3, 200) r = np.sqrt(np.linspace(1, 7**2, 200)) P = dd_gauss(r, 3, 0.2) K = dipolarkernel(t, r) L = regoperator(r, 2) V = K @ P logalpha = np.log10(selregparam(V, K, cvxnnls, method='aic', regop=L)) logalpharef = -6.8517 assert abs(1 - logalpha / logalpharef) < 0.2
def test_unconstrained(): #======================================================================= "Check the algorithm works with unconstrained disributions" t = np.linspace(0, 5, 80) r = np.linspace(2, 5, 80) P = dd_gauss(r, 3, 0.15) K = dipolarkernel(t, r) L = regoperator(r, 2, includeedges=True) V = K @ P logalpha = np.log10( selregparam(V, K, np.linalg.solve, method='aic', regop=L)) logalpharef = -8.87 assert abs(1 - logalpha / logalpharef) < 0.1
def test_tikh_value(): #======================================================================= "Check that the value returned by Tikhonov regularization" np.random.seed(1) t = np.linspace(0, 5, 500) r = np.linspace(2, 5, 80) P = dd_gauss(r, 3, 0.15) K = dipolarkernel(t, r) V = K @ P + whitegaussnoise(t, 0.01) L = regoperator(r, 2, includeedges=True) alpha = selregparam(V, K, cvxnnls, method='aic', regop=L) loga = np.log10(alpha) logaref = -3.51 # Computed with DeerLab-Matlab (0.9.2) assert abs(1 - loga / logaref) < 0.02 # less than 2% error
def test_manual_candidates(): #======================================================================= "Check that the alpha-search range can be manually passed" t = np.linspace(0, 5, 80) r = np.linspace(2, 5, 80) P = dd_gauss(r, 3, 0.15) K = dipolarkernel(t, r) L = regoperator(r, 2, includeedges=True) alphas = np.linspace(-8, 2, 60) V = K @ P alpha_manual = np.log10( selregparam(V, K, cvxnnls, method='aic', candidates=alphas, regop=L)) alpha_auto = np.log10(selregparam(V, K, cvxnnls, method='aic', regop=L)) assert abs(alpha_manual - alpha_auto) < 1e-4