You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grogu/tests/test_utilities.py

262 lines
7.7 KiB

# Copyright (c) [2024] []
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import numpy as np
import pytest
from hypothesis import given
from hypothesis import strategies as st
from numpy.testing import assert_allclose, assert_array_almost_equal
from grogupy.constants import TAU_0, TAU_X, TAU_Y, TAU_Z
from grogupy.utilities import (
RotM,
RotMa2b,
crossM,
hsk,
int_de_ke,
make_contour,
make_kset,
read_siesta_emin,
tau_u,
)
# Test basic Pauli matrix properties
def test_pauli_matrices_properties():
"""Test fundamental properties of Pauli matrices"""
# Test anticommutation relations
assert_array_almost_equal(TAU_X @ TAU_Y + TAU_Y @ TAU_X, np.zeros((2, 2)))
assert_array_almost_equal(TAU_Y @ TAU_Z + TAU_Z @ TAU_Y, np.zeros((2, 2)))
assert_array_almost_equal(TAU_Z @ TAU_X + TAU_X @ TAU_Z, np.zeros((2, 2)))
# Test square of Pauli matrices equals identity
assert_array_almost_equal(TAU_X @ TAU_X, TAU_0)
assert_array_almost_equal(TAU_Y @ TAU_Y, TAU_0)
assert_array_almost_equal(TAU_Z @ TAU_Z, TAU_0)
# Test Hermiticity
assert_array_almost_equal(TAU_X, TAU_X.conj().T)
assert_array_almost_equal(TAU_Y, TAU_Y.conj().T)
assert_array_almost_equal(TAU_Z, TAU_Z.conj().T)
# Test hsk function
def test_hsk_basics():
"""Test basic properties of the hsk function"""
size = 4
H = np.random.random((3, size, size)) + 1j * np.random.random((3, size, size))
ss = np.random.random((3, size, size)) + 1j * np.random.random((3, size, size))
sc_off = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0]])
k = np.array([0.1, 0.2, 0.3])
HK, SK = hsk(H, ss, sc_off, k)
# Check shapes
assert HK.shape == (size, size)
assert SK.shape == (size, size)
# Test k=0 case gives the central cell contribution
HK0, SK0 = hsk(H, ss, sc_off, np.zeros(3))
assert_array_almost_equal(HK0, H[0])
assert_array_almost_equal(SK0, ss[0])
@given(st.integers(min_value=1, max_value=20))
def test_make_kset_dimensions(num_k):
"""Test k-point set generation with different dimensions"""
# Test 1D
kset_x = make_kset("x", num_k)
assert kset_x.shape[1] == 3
assert len(np.unique(kset_x[:, 1])) == 1 # y coordinates should be same
assert len(np.unique(kset_x[:, 2])) == 1 # z coordinates should be same
# Test 2D
kset_xy = make_kset("xy", num_k)
assert kset_xy.shape[1] == 3
assert len(np.unique(kset_xy[:, 2])) == 1 # z coordinates should be same
# Test 3D
kset_xyz = make_kset("xyz", num_k)
assert kset_xyz.shape[1] == 3
assert len(kset_xyz) == num_k**3
def test_make_contour():
"""Test contour generation for energy integration"""
emin = -20
emax = 0.0
enum = 42
p = 150
cont = make_contour(emin, emax, enum, p)
# Check attributes
assert hasattr(cont, "R")
assert hasattr(cont, "z0")
assert hasattr(cont, "ze")
assert hasattr(cont, "we")
assert hasattr(cont, "enum")
# Check dimensions
assert len(cont.ze) == enum
assert len(cont.we) == enum
# Check basic properties
assert cont.z0 == (emax + emin) / 2
assert cont.R == (emax - emin) / 2
assert cont.enum == enum
@given(st.lists(st.floats(min_value=-1, max_value=1), min_size=3, max_size=3))
def test_tau_u(u):
"""Test generation of Pauli matrix in arbitrary direction"""
u = np.array(u)
if np.all(u == 0):
u = np.array([1, 0, 0]) # Handle zero vector case
u = u / np.linalg.norm(u)
result = tau_u(u)
# Check result is 2x2
assert result.shape == (2, 2)
# Check Hermiticity
assert_array_almost_equal(result, result.conj().T)
# Check trace is zero
assert abs(np.trace(result)) < 1e-10
# Check square equals identity
assert_array_almost_equal(result @ result, np.eye(2))
def test_crossM():
"""Test cross product matrix generation"""
u = np.array([1.0, 2.0, 3.0])
v = np.array([4.0, 5.0, 6.0])
# Test that crossM(u) @ v equals np.cross(u, v)
assert_array_almost_equal(crossM(u) @ v, np.cross(u, v))
# Test antisymmetry
assert_array_almost_equal(crossM(u), -crossM(u).T)
@pytest.mark.parametrize("theta", [0, np.pi / 4, np.pi / 2, np.pi])
def test_RotM(theta):
"""Test rotation matrix generation"""
# Test rotation around z-axis
u = np.array([0, 0, 1])
R = RotM(theta, u)
# Check orthogonality
assert_array_almost_equal(R @ R.T, np.eye(3))
# Check determinant is 1
assert_allclose(np.linalg.det(R), 1.0)
# Test rotation of vector
v = np.array([1, 0, 0])
rotated_v = R @ v
expected_v = np.array([np.cos(theta), np.sin(theta), 0])
assert_array_almost_equal(rotated_v, expected_v)
def test_RotMa2b():
"""Test rotation matrix between two vectors"""
# Test simple cases
a = np.array([1, 0, 0])
b = np.array([0, 1, 0])
R = RotMa2b(a, b)
# Check that R rotates a to b
assert_array_almost_equal(R @ a, b)
# Check orthogonality
assert_array_almost_equal(R @ R.T, np.eye(3))
# Check determinant is 1
assert_allclose(np.linalg.det(R), 1.0)
def test_read_siesta_emin(tmpdir):
"""Test reading minimum energy from SIESTA .EIG file"""
# Create mock .EIG file
eig_file = tmpdir / "test.EIG"
with open(eig_file, "w") as f:
# Write mock eigenvalues
f.write("1.0 2.0 3.0\n-1.0 0.0 1.0\n")
# Test reading minimum energy
emin = read_siesta_emin(str(eig_file))
assert emin == -1.0
def test_int_de_ke():
"""Test energy integral calculation"""
# Create test data
energy = np.linspace(-10, 10, 1000)
traced = np.exp(-(energy**2)) # Gaussian function
we = np.ones_like(energy) # Unit weights
result = int_de_ke(traced, we)
# Result should be real for real input
assert np.imag(result) < 1e-10
# Test with complex input
traced_complex = traced + 1j * traced
result_complex = int_de_ke(traced_complex, we)
assert isinstance(result_complex, complex)
# Property-based tests
@given(st.integers(min_value=2, max_value=10))
def test_make_kset_dimensions_property(num_k):
"""Property-based test for k-point set generation"""
kset = make_kset("xyz", num_k)
# All k-points should be in [0,1)
assert np.all(kset >= 0)
assert np.all(kset < 1)
# Shape should be (num_k^3, 3)
assert kset.shape == (num_k**3, 3)
@given(
st.floats(min_value=-20, max_value=-1),
st.floats(min_value=0, max_value=1),
st.integers(min_value=10, max_value=100),
)
def test_make_contour_properties(emin, emax, enum):
"""Property-based test for contour generation"""
cont = make_contour(emin, emax, enum, 150)
# Check basic properties
assert len(cont.ze) == enum
assert len(cont.we) == enum
assert cont.R > 0
assert cont.z0 == (emax + emin) / 2
if __name__ == "__main__":
pytest.main([__file__])