Coverage for gpaw/test/linalg/test_magma.py: 30%
56 statements
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-20 00:19 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-20 00:19 +0000
1"""Tests for MAGMA eigensolver wrappers"""
2import numpy as np
3import pytest
4from gpaw.new.magma import eigh_magma_cpu, eigh_magma_gpu
5from gpaw.cgpaw import have_magma
6from gpaw.gpu import cupy as cp
7from gpaw.gpu import cupy_is_fake
10def fix_eigenvector_phase(inout_arr):
11 """Helper function for comparing eigenvector output from different
12 solvers. Rotates eigenvectors in the input matrix so that the first
13 element of each vector is real and non-negative.
14 Input is modified in-place.
15 NB: eigenvectors are on columns.
16 """
17 assert inout_arr.ndim == 2
18 # Works for cupy arrays too because the dtypes are compatible
20 if np.issubdtype(inout_arr.dtype, np.complexfloating):
21 # Complex matrices
22 for i in range(inout_arr.shape[1]):
23 phase = np.angle(inout_arr[0, i])
24 if phase != 0:
25 rotation = np.exp(phase * (-1j))
26 inout_arr[:, i] *= rotation
28 elif np.issubdtype(inout_arr.dtype, np.floating):
29 # Real matrices
30 for i in range(inout_arr.shape[1]):
31 if inout_arr[0, i] < 0:
32 inout_arr[:, i] *= -1
34 return inout_arr
37@pytest.fixture
38def eigh_test_matrix():
39 def _generate(n: int, type: str = 'symmetric',
40 backend: str = 'numpy', seed: int = 42):
42 assert type in ['symmetric', 'hermitian']
43 assert backend in ['numpy', 'cupy']
45 if backend == 'cupy':
46 xp = cp
47 else:
48 xp = np
50 rng = xp.random.default_rng(seed)
51 if type == 'symmetric':
52 A = rng.random((n, n))
53 return (A + A.T) / 2
55 else:
56 # Create Hermitian matrix
57 A = rng.random((n, n)) + 1j * rng.random((n, n))
58 return (A + A.T.conj()) / 2
60 return _generate
63@pytest.mark.skipif(not have_magma, reason="No MAGMA")
64@pytest.mark.parametrize("matrix_size, matrix_type, uplo",
65 [(2, 'symmetric', 'L'), (4, 'hermitian', 'U')])
66def test_eigh_magma_cpu(eigh_test_matrix: np.ndarray,
67 matrix_size: int,
68 matrix_type: str,
69 uplo: str) -> None:
70 """Compare eigh output of Numpy and MAGMA"""
72 arr = eigh_test_matrix(matrix_size, type=matrix_type, backend='numpy')
73 eigvals, eigvects = eigh_magma_cpu(arr, uplo)
75 eigvals_np, eigvects_np = np.linalg.eigh(arr, UPLO=uplo)
77 fix_eigenvector_phase(eigvects)
78 fix_eigenvector_phase(eigvects_np)
80 np.testing.assert_allclose(eigvals, eigvals_np, atol=1e-12)
81 np.testing.assert_allclose(eigvects, eigvects_np, atol=1e-12)
84# MAGMA seems to do small matrices (N <= 128) on the CPU.
85# So need a large matrix for honest GPU tests
86@pytest.mark.skipif(not have_magma, reason="No MAGMA")
87@pytest.mark.skipif(cupy_is_fake,
88 reason="MAGMA GPU tests disabled for fake cupy")
89@pytest.mark.gpu
90@pytest.mark.parametrize("matrix_size, matrix_type, uplo",
91 [(16, 'symmetric', 'L'),
92 (150, 'hermitian', 'L'),
93 (256, 'symmetric', 'U')])
94def test_eigh_magma_gpu(eigh_test_matrix: cp.ndarray,
95 matrix_size: int,
96 matrix_type: str,
97 uplo: str):
98 """Compare eigh output of CUPY and MAGMA"""
100 arr = eigh_test_matrix(matrix_size, type=matrix_type, backend='cupy')
101 eigvals, eigvects = eigh_magma_gpu(arr, uplo)
103 eigvals_cp, eigvects_cp = cp.linalg.eigh(arr, UPLO=uplo)
105 fix_eigenvector_phase(eigvects)
106 fix_eigenvector_phase(eigvects_cp)
108 cp.testing.assert_allclose(eigvals, eigvals_cp, atol=1e-12)
109 cp.testing.assert_allclose(eigvects, eigvects_cp, atol=1e-12)