LCOV - code coverage report
Current view: top level - tests - test_ket.py (source / functions) Hit Total Coverage
Test: coverage.info Lines: 97 97 100.0 %
Date: 2026-09-14 15:58:41 Functions: 6 6 100.0 %

          Line data    Source code
       1             : # SPDX-FileCopyrightText: 2024 PairInteraction Developers
       2             : # SPDX-License-Identifier: LGPL-3.0-or-later
       3             : 
       4           1 : from __future__ import annotations
       5             : 
       6           1 : from typing import TYPE_CHECKING, Literal
       7             : 
       8           1 : import pytest
       9           1 : from pairinteraction.units import ureg
      10             : 
      11             : if TYPE_CHECKING:
      12             :     from .utils import PairinteractionModule
      13             : 
      14             : 
      15           1 : def test_ket(pi_module: PairinteractionModule) -> None:
      16           1 :     ket = pi_module.KetAtom("Rb", n=60, l=0, j=0.5, m=0.5)
      17           1 :     assert ket.species == "Rb"
      18           1 :     assert ket.n == 60
      19           1 :     assert ket.l == 0
      20           1 :     assert pytest.approx(ket.s) == 0.5  # NOSONAR
      21           1 :     assert pytest.approx(ket.j) == 0.5  # NOSONAR
      22           1 :     assert pytest.approx(ket.m) == 0.5  # NOSONAR
      23           1 :     assert ket.parity == "even"
      24           1 :     assert ket.get_energy().units == ureg.Unit("bohr^2 electron_mass/atomic_unit_of_time^2")
      25           1 :     assert pytest.approx(ket.get_energy().magnitude) == 0.15335264334573842  # NOSONAR
      26           1 :     assert pytest.approx(ket.get_energy("GHz")) == 1009011.9215883961  # NOSONAR
      27             : 
      28           1 :     assert ket == pi_module.KetAtom("Rb", n=60, l=0, j=0.5, m=0.5)
      29             : 
      30           1 :     ket_odd = pi_module.KetAtom("Rb", n=60, l=1, j=1.5, m=-0.5)
      31           1 :     assert ket_odd.l == 1
      32           1 :     assert pytest.approx(ket_odd.f) == 1.5  # NOSONAR
      33           1 :     assert pytest.approx(ket_odd.m) == -0.5  # NOSONAR
      34           1 :     assert ket_odd.parity == "odd"
      35             : 
      36           1 :     formats: list[Literal["raw", "ket", "bra"]] = ["raw", "ket", "bra"]
      37           1 :     for fmt in formats:
      38           1 :         label = ket_odd.get_label(fmt)
      39           1 :         assert all(str(qn) in label for qn in ["Rb", 60, "P", "3/2", "-1/2"])
      40             : 
      41           1 :     assert ket_odd != ket
      42           1 :     assert ket != pi_module.KetAtom("Rb", n=60, l=1, j=1.5, m=0.5)
      43             : 
      44           1 :     assert ket.get_matrix_element(ket_odd, "electric_dipole", q=-1) != 0
      45           1 :     assert ket.get_matrix_element(ket_odd, "electric_dipole", q=0) == 0
      46           1 :     assert ket.get_matrix_element(ket_odd, "electric_dipole", q=+1) == 0
      47             : 
      48             : 
      49           1 : def test_get_label_sqdt(pi_module: PairinteractionModule) -> None:
      50           1 :     ket1 = pi_module.KetAtom("Rb", n=60, l=1, j=1.5, m=-0.5)
      51           1 :     assert ket1.get_label("raw") == "Rb:60,P_3/2,-1/2"
      52           1 :     ket2 = pi_module.KetAtom("Sr88_sqdt", n=60, l=0, s=0, j=0, m=0)
      53           1 :     assert ket2.get_label("raw") == "Sr88:S=0,60,S_0,0"
      54           1 :     ket3 = pi_module.KetAtom("Sr88_sqdt", n=60, l=1, s=1, j=2, m=1)
      55           1 :     assert ket3.get_label("raw") == "Sr88:S=1,60,P_2,1"
      56           1 :     ket4 = pi_module.KetAtom("Sr88_ion", n=60, l=0, j=0.5, m=0.5)
      57           1 :     assert ket4.get_label("raw") == "Sr88+:60,S_1/2,1/2"
      58           1 :     assert ket4.get_label("ket") == "|Sr88+:60,S_1/2,1/2⟩"
      59             : 
      60             : 
      61           1 : def test_get_label_mqdt(pi_module: PairinteractionModule) -> None:
      62           1 :     ket1 = pi_module.KetAtom("Yb171_mqdt", nu=55.5, l=0, f=1.5, m=1.5)
      63           1 :     assert ket1.get_label("raw") == "Yb171:S=1.0,nu=55.6,L=0.0,F=3/2,3/2"
      64           1 :     ket2 = pi_module.KetAtom("Yb171_mqdt", nu=55.1, l=1, f=2.5, m=2.5)
      65           1 :     assert ket2.get_label("raw") == "Yb171:S=1.0,nu=55.1,L=1.0,F=5/2,5/2"
      66           1 :     ket3 = pi_module.KetAtom("Yb174_mqdt", nu=60, l=1, f=1, m=1)
      67           1 :     assert ket3.get_label("raw") == "Yb174:S=0.0,nu=60.0,L=1.0,J=1,1"
      68             : 
      69             : 
      70           1 : def test_ket_equal(pi_module: PairinteractionModule) -> None:
      71           1 :     ket1 = pi_module.KetAtom("Rb", n=60, l=0, j=0.5, m=0.5)
      72           1 :     ket2 = pi_module.KetAtom("Rb", n=60, l=0, j=0.5, m=0.5)
      73           1 :     ket3 = pi_module.KetAtom("Rb", n=60, l=0, j=0.5, m=-0.5)
      74           1 :     ket4 = pi_module.KetAtom("Rb", n=61, l=0, j=0.5, m=0.5)
      75           1 :     assert ket1 == ket2
      76           1 :     assert ket1 != ket3
      77           1 :     assert ket1 != ket4
      78             : 
      79             : 
      80           1 : def test_ket_to_state(pi_module: PairinteractionModule) -> None:
      81           1 :     ket = pi_module.KetAtom("Rb", n=60, l=0, j=0.5, m=0.5)
      82           1 :     state = ket.to_state()
      83             : 
      84             :     # the state is trivial, i.e. a single ket with a coefficient of one
      85           1 :     assert isinstance(state, pi_module.StateAtom)
      86           1 :     assert state.number_of_kets == 1
      87           1 :     assert state.is_canonical
      88           1 :     assert state.is_normalized()
      89           1 :     assert state.get_corresponding_ket() == ket
      90           1 :     assert pytest.approx(state.get_coefficients()) == [1.0]  # NOSONAR
      91           1 :     assert pytest.approx(state.get_overlap(ket)) == 1.0  # NOSONAR
      92             : 
      93             :     # the state has the same data type (real/complex) as the ket, so both can be combined
      94           1 :     combined = (state + pi_module.KetAtom("Rb", n=60, l=1, j=1.5, m=0.5).to_state()).normalize()
      95           1 :     assert combined.number_of_kets == 2
      96           1 :     assert pytest.approx(combined.get_overlap(ket)) == 0.5  # NOSONAR
      97             : 
      98             : 
      99           1 : def test_ket_arithmetic(pi_module: PairinteractionModule) -> None:
     100           1 :     ket_s = pi_module.KetAtom("Rb", n=60, l=0, j=0.5, m=0.5)
     101           1 :     ket_p = pi_module.KetAtom("Rb", n=60, l=1, j=1.5, m=0.5)
     102             : 
     103             :     # adding two kets gives the equally weighted superposition
     104           1 :     combined = (ket_s + ket_p).normalize()
     105           1 :     assert isinstance(combined, pi_module.StateAtom)
     106           1 :     assert combined.number_of_kets == 2
     107           1 :     assert pytest.approx(combined.get_overlap(ket_s)) == 0.5  # NOSONAR
     108           1 :     assert pytest.approx(combined.get_overlap(ket_p)) == 0.5  # NOSONAR
     109             : 
     110             :     # multiplying with a scalar works from both sides and weights the kets accordingly
     111           1 :     for weighted in [(ket_s + 2 * ket_p).normalize(), (ket_s + ket_p * 2).normalize()]:
     112           1 :         assert pytest.approx(weighted.get_overlap(ket_s)) == 1 / 5  # NOSONAR
     113           1 :         assert pytest.approx(weighted.get_overlap(ket_p)) == 4 / 5  # NOSONAR
     114             : 
     115             :     # dividing by a scalar is equivalent to multiplying with the inverse
     116           1 :     assert pytest.approx((ket_s / 2).get_coefficients()) == (ket_s * 0.5).get_coefficients()  # NOSONAR
     117             : 
     118             :     # negating a ket is equivalent to multiplying it with minus one
     119           1 :     assert pytest.approx((-ket_s).get_coefficients()) == (ket_s * -1).get_coefficients()  # NOSONAR
     120           1 :     assert pytest.approx((-ket_s).get_amplitude(ket_s)) == -1.0  # NOSONAR
     121           1 :     assert ((ket_s + ket_p) + (-ket_p)).number_of_kets == 2
     122             : 
     123             :     # subtracting gives the same overlaps, but with an opposite relative sign
     124           1 :     difference = (ket_s - ket_p).normalize()
     125           1 :     assert pytest.approx(difference.get_overlap(ket_s)) == 0.5  # NOSONAR
     126           1 :     assert pytest.approx(difference.get_overlap(ket_p)) == 0.5  # NOSONAR
     127           1 :     assert difference.get_amplitude(ket_s) * difference.get_amplitude(ket_p) < 0  # type: ignore [operator]
     128             : 
     129             :     # a state and a ket can be added directly, in both orders
     130           1 :     state_s = ket_s.to_state()
     131           1 :     for mixed in [(state_s + ket_p).normalize(), (ket_p + state_s).normalize()]:
     132           1 :         assert mixed.number_of_kets == 2
     133           1 :         assert pytest.approx(mixed.get_overlap(ket_s)) == 0.5  # NOSONAR
     134           1 :         assert pytest.approx(mixed.get_overlap(ket_p)) == 0.5  # NOSONAR
     135             : 
     136             :     # a ket can also be combined with an already existing state, from both sides
     137           1 :     ket_d = pi_module.KetAtom("Rb", n=60, l=2, j=2.5, m=0.5)
     138           1 :     assert ((ket_s + ket_p) + ket_d).number_of_kets == 3
     139           1 :     assert (ket_d + (ket_s + ket_p)).number_of_kets == 3
     140           1 :     assert ((ket_s + ket_p) - ket_d).number_of_kets == 3

Generated by: LCOV version 1.16