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

            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 2.0-1