fix: fixes the broken implementation of the tarucha frequency calculation
All checks were successful
gitea-physics/pdme/pipeline/head This commit looks good

This commit is contained in:
2024-04-28 21:27:41 -05:00
parent e2bdeda638
commit 631ba13c79
3 changed files with 160 additions and 0 deletions

View File

@@ -100,6 +100,132 @@ def fast_s_nonlocal_dipoleses(
return numpy.einsum("...j->...", alphses1 * alphses2 * bses)
def fast_s_spin_qubit_tarucha_nonlocal_dipoleses(
dot_pair_inputs: numpy.ndarray, dipoleses: numpy.ndarray
) -> numpy.ndarray:
"""
No error correction here baby.
"""
# We're going to annotate the indices on this class.
# Let's define some indices:
# A -> index of dipoleses configurations
# j -> within a particular configuration, indexes dipole j
# measurement_index -> if we have 100 frequencies for example, indexes which one of them it is
# If we need to use numbers, let's use A -> 2, j -> 10, measurement_index -> 9 for consistency with
# my other notes
# axes are [dipole_config_idx A, dipole_idx j, {px, py, pz}3]
ps = dipoleses[:, :, 0:3]
# axes are [dipole_config_idx A, dipole_idx j, {sx, sy, sz}3]
ss = dipoleses[:, :, 3:6]
# axes are [dipole_config_idx A, dipole_idx j, w], last axis is just 1
ws = dipoleses[:, :, 6]
_logger.debug(f"ps: {ps}")
_logger.debug(f"ss: {ss}")
_logger.debug(f"ws: {ws}")
# dot_index is either 0 or 1 for dot1 or dot2
# hopefully this adhoc grammar is making sense, with the explicit labelling of the values of the last axis in cartesian space
# axes are [measurement_idx, {dot_index}, {rx, ry, rz}] where the inner {dot_index} is gone
# [measurement_idx, cartesian3]
r1s = dot_pair_inputs[:, 0, 0:3]
r2s = dot_pair_inputs[:, 1, 0:3]
# axes are [measurement_idx]
f1s = dot_pair_inputs[:, 0, 3]
f2s = dot_pair_inputs[:, 1, 3]
if (f1s != f2s).all():
raise ValueError(f"Dot pair frequencies are inconsistent: {dot_pair_inputs}")
# first operation!
# r1s has shape [measurement_idx, rxs]
# None inserts an extra axis so the r1s[:, None] has shape
# [measurement_idx, 1]([rxs]) with the last rxs hidden
#
# ss has shape [ A, j, {sx, sy, sz}3], so second term has shape [A, 1, j]([sxs])
# these broadcast from right to left
# [ measurement_idx, 1, rxs]
# [A, 1, j, sxs]
# resulting in [A, measurement_idx, j, cart3] sxs rxs are both cart3
diffses1 = r1s[:, None] - ss[:, None, :]
diffses2 = r2s[:, None] - ss[:, None, :]
if _logger.isEnabledFor(logging.DEBUG):
_logger.warning(f"diffses1: {diffses1}")
_logger.warning(f"diffses2: {diffses2}")
# norms takes out axis 3, the last one, giving [A, measurement_idx, j]
norms1 = numpy.linalg.norm(diffses1, axis=3)
norms2 = numpy.linalg.norm(diffses2, axis=3)
if _logger.isEnabledFor(logging.DEBUG):
_logger.warning(f"norms1: {norms1}")
_logger.warning(f"norms2: {norms2}")
# _logger.info(f"norms1: {norms1}")
# _logger.info(f"norms1 shape: {norms1.shape}")
#
# diffses1 (A, measurement_idx, j, xs)
# ps: (A, j, px)
# result is (A, measurement_idx, j)
# intermediate_dot_prod = numpy.einsum("abcd,acd->abc", diffses1, ps)
# _logger.info(f"dot product shape: {intermediate_dot_prod.shape}")
# transpose makes it (j, measurement_idx, A)
# transp_intermediate_dot_prod = numpy.transpose(numpy.einsum("abcd,acd->abc", diffses1, ps) / (norms1**3))
# transpose of diffses has shape (xs, j, measurement_idx, A)
# numpy.transpose(diffses1)
# _logger.info(f"dot product shape: {transp_intermediate_dot_prod.shape}")
# inner transpose is (j, measurement_idx, A) * (xs, j, measurement_idx, A)
# next transpose puts it back to (A, measurement_idx, j, xs)
# p_dot_r_times_r_term = 3 * numpy.transpose(numpy.transpose(numpy.einsum("abcd,acd->abc", diffses1, ps) / (norms1**3)) * numpy.transpose(diffses1))
# _logger.info(f"p_dot_r_times_r_term: {p_dot_r_times_r_term.shape}")
# only x axis puts us at (A, measurement_idx, j)
# p_dot_r_times_r_term_x_only = p_dot_r_times_r_term[:, :, :, 0]
# _logger.info(f"p_dot_r_times_r_term_x_only.shape: {p_dot_r_times_r_term_x_only.shape}")
# now to complete the numerator we subtract the ps, which are (A, j, px):
# slicing off the end gives us (A, j), so we newaxis to get (A, 1, j)
# _logger.info(ps[:, numpy.newaxis, :, 0].shape)
alphses1 = (
(
3
* numpy.transpose(
numpy.transpose(
numpy.einsum("abcd,acd->abc", diffses1, ps) / (norms1**2)
)
* numpy.transpose(diffses1)
)[:, :, :, 0]
)
- ps[:, numpy.newaxis, :, 0]
) / (norms1**3)
alphses2 = (
(
3
* numpy.transpose(
numpy.transpose(
numpy.einsum("abcd,acd->abc", diffses2, ps) / (norms2**2)
)
* numpy.transpose(diffses2)
)[:, :, :, 0]
)
- ps[:, numpy.newaxis, :, 0]
) / (norms2**3)
if _logger.isEnabledFor(logging.DEBUG):
_logger.warning(f"alphses1: {alphses1}")
_logger.warning(f"alphses2: {alphses2}")
bses = (1 / numpy.pi) * (ws[:, None, :] / (f1s[:, None] ** 2 + ws[:, None, :] ** 2))
if _logger.isEnabledFor(logging.DEBUG):
_logger.warning(f"bses: {bses}")
_logger.warning(f"Raw pair calc: [{alphses1 * alphses2 * bses}]")
return numpy.einsum("...j->...", alphses1 * alphses2 * bses)
def signarg(x, **kwargs):
"""
uses numpy.sign to implement Arg for real numbers only. Should return pi for negative inputs, 0 for positive.

View File

@@ -11,3 +11,15 @@
]),
])
# ---
# name: test_fast_spin_qubit_frequency_tarucha_calc
list([
list([
0.24485375860291592,
0.009068657726033923,
]),
list([
0.0001499877991005419,
0.00011239982757520363,
]),
])
# ---

View File

@@ -3,6 +3,8 @@ import pdme.util.fast_nonlocal_spectrum
import logging
import pytest
_logger = logging.getLogger(__name__)
def test_fast_nonlocal_calc_multidipole():
d1 = [1, 2, 3, 4, 5, 6, 7]
@@ -74,3 +76,23 @@ def test_fast_nonlocal_calc_multidipole_phase_snapshot(snapshot):
pdme.util.fast_nonlocal_spectrum.fast_s_nonlocal_dipoleses(dot_pairs, dipoleses)
)
assert actual_phases.tolist() == snapshot
def test_fast_spin_qubit_frequency_tarucha_calc(snapshot):
d1 = [1, 2, 3, 0, 0, 0, 5]
d2 = [6, 7, 8, 5, 4, 3, 8]
dipoleses = numpy.array([[d1], [d2]])
dot_pairs = numpy.array(
[[[1, 0, 0, 1], [1, 0, 0, 1]], [[1, 0, 0, 1], [3, 0, 0, 1]]]
)
actual = (
pdme.util.fast_nonlocal_spectrum.fast_s_spin_qubit_tarucha_nonlocal_dipoleses(
dot_pairs, dipoleses
)
)
pdme.util.fast_nonlocal_spectrum._logger.setLevel(logging.DEBUG)
_logger.info(actual)
assert actual.tolist() == snapshot