Source code for tests.test_scipy_plus

""" Tests for a few numerical methods that could be a part of scipy.

.. autosummary::
    :toctree: _generated/
    :template: tests-formatting/base.rst
    :nosignatures:

    test_random_orthogonal_matrix
    test_nullspace
    test_blas_matmul

"""

import pytest
from .test_config import *
import numpy as np
import choreo

[docs] @ParametrizeDocstrings @ProbabilisticTest() @pytest.mark.parametrize("n", Dense_linalg_dims) def test_random_orthogonal_matrix(float64_tols, n): """ Tests whether random orthogonal matrices are indeed orthogonal enough. """ rot = choreo.scipy_plus.linalg.random_orthogonal_matrix(n) assert np.allclose(np.matmul(rot , rot.T), np.identity(n), rtol = float64_tols.rtol, atol = float64_tols.atol) assert np.allclose(np.matmul(rot.T, rot ), np.identity(n), rtol = float64_tols.rtol, atol = float64_tols.atol)
[docs] @ParametrizeDocstrings @ProbabilisticTest() @pytest.mark.parametrize("m", Dense_linalg_dims) @pytest.mark.parametrize("n", Dense_linalg_dims) def test_nullspace(float64_tols, n, m): """ Tests properties of nullspace computation. """ P = choreo.scipy_plus.linalg.random_orthogonal_matrix(n) if (n == m): Z = choreo.scipy_plus.linalg.null_space(P) assert Z.shape[0] == m assert Z.shape[1] == 0 for rank in range(min(n,m)+1): nullspace_dim = m - rank Q = choreo.scipy_plus.linalg.random_orthogonal_matrix(m) diag = np.random.rand(rank) diagmat = np.zeros((n,m)) for i in range(rank): diagmat[i,i] = 2 + diag[i] A = np.matmul(P, np.matmul(diagmat, Q)) Z = choreo.scipy_plus.linalg.null_space(A) # Dimensions assert Z.shape[0] == m assert Z.shape[1] == nullspace_dim # Nullspace property assert np.allclose(np.matmul(A,Z), 0, rtol = float64_tols.rtol, atol = float64_tols.atol) # Orthogonality assert np.allclose(np.matmul(Z.T,Z), np.identity(nullspace_dim), rtol = float64_tols.rtol, atol = float64_tols.atol)
@ParametrizeDocstrings @ProbabilisticTest() @pytest.mark.parametrize("m", Dense_linalg_dims) @pytest.mark.parametrize("n", Dense_linalg_dims) @pytest.mark.parametrize("k", Dense_linalg_dims) def test_blas_matmul(float64_tols, m, n, k): """ Tests calling dgemm directly from blas for regular matmul. """ A = np.random.random((m,k)) B = np.random.random((k,n)) AB_np = np.matmul(A,B) AB_blas = choreo.scipy_plus.cython.blas_cheatsheet.blas_matmul(A,B) print(np.linalg.norm(AB_np - AB_blas)) assert np.allclose(AB_np, AB_blas, rtol = float64_tols.rtol, atol = float64_tols.atol)
[docs] @ParametrizeDocstrings @ProbabilisticTest() @pytest.mark.parametrize("m", Dense_linalg_dims) @pytest.mark.parametrize("n", Dense_linalg_dims) def test_blas_matmul(float64_tols, m, n): """ Tests calling dgemv directly from blas for regular matmul. """ A = np.random.random((m,n)) v = np.random.random((n)) Av_np = np.matmul(A,v) Av_blas = choreo.scipy_plus.cython.blas_cheatsheet.blas_matvecmul(A,v) print(np.linalg.norm(Av_np - Av_blas)) assert np.allclose(Av_np, Av_blas, rtol = float64_tols.rtol, atol = float64_tols.atol)
@ParametrizeDocstrings def test_kepler(float64_tols_strict, float64_tols_loose): """ Tests Kepler solver. """ for M in [0., 0.5, 10., 100.]: for ecc in [0., 0.5, 0.8, 0.95, 0.99, 0.99999]: E = choreo.scipy_plus.cython.kepler.solve(M, ecc) print(abs(E - ecc * np.sin(E) - M)) assert abs(E - ecc * np.sin(E) - M) <= float64_tols_strict.atol + 2 * float64_tols_strict.rtol * M def f(x): Ep, cosfp, sinfp, dcosfp, dsinfp = choreo.scipy_plus.cython.kepler.kepler(x[0], ecc) return np.array([cosfp, sinfp]) def grad_f(x, dx): Ep, cosfp, sinfp, dcosfp, dsinfp = choreo.scipy_plus.cython.kepler.kepler(x[0], ecc) return np.array([dcosfp * dx[0], dsinfp * dx[0]]) err = compare_FD_and_exact_grad( f , grad_f , np.array([M]) , order=2 , vectorize=False , relative = True , ) print(err.min()) assert err.min() < 100 * float64_tols_loose.rtol # precision is not great when e is near 1 imax = 2**12 for i in range(imax): M = i*2*np.pi / imax for ecc in [0., 0.5, 0.8, 0.95, 0.99, 0.99999]: E = choreo.scipy_plus.cython.kepler.solve(M, ecc) print(M, ecc, abs(E - ecc * np.sin(E) - M)) assert abs(E - ecc * np.sin(E) - M) <= float64_tols_strict.atol + 2 * float64_tols_strict.rtol * M