Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions projectq/tests/PhaseAgnosticStateComparator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import numpy as np

from projectq import MainEngine
from projectq.ops import CNOT, H
from projectq.tests.helpers import PhaseAgnosticStateComparator


def test_hadamard_twice():
eng = MainEngine()
q = eng.allocate_qureg(1)
H | q[0]
H | q[0]
eng.flush()
_, actual = eng.backend.cheat()
expected = np.array([1, 0], dtype=complex)
comparator = PhaseAgnosticStateComparator()
comparator.compare(actual, expected)


def test_bell_state():
eng = MainEngine()
q = eng.allocate_qureg(2)
H | q[0]
CNOT | (q[0], q[1])
eng.flush()
_, actual = eng.backend.cheat()
expected = np.array([1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2)], dtype=complex)
comparator = PhaseAgnosticStateComparator()
comparator.compare(actual, expected)
51 changes: 51 additions & 0 deletions projectq/tests/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import numpy as np


class PhaseAgnosticStateComparator:
def __init__(self, tol=1e-8):
self.tol = tol

def _validate_state(self, state, label="input"):
if not isinstance(state, np.ndarray):
raise TypeError(f"{label} must be a NumPy array.")
if state.ndim not in (1, 2):
raise ValueError(f"{label} must be a 1D or 2D array, got shape {state.shape}.")
if not np.isfinite(state).all():
raise ValueError(f"{label} contains NaN or Inf.")
if state.ndim == 1 and np.linalg.norm(state) == 0:
raise ValueError(f"{label} is a zero vector and cannot be normalized.")
if state.ndim == 2:
for i, vec in enumerate(state):
if np.linalg.norm(vec) == 0:
raise ValueError(f"{label}[{i}] is a zero vector.")

def normalize(self, state, label="input"):
self._validate_state(state, label)
if state.ndim == 1:
return state / np.linalg.norm(state)
else:
return np.array([vec / np.linalg.norm(vec) for vec in state])

def align_phase(self, actual, expected):
a = self.normalize(actual, "actual")
b = self.normalize(expected, "expected")
if a.ndim == 1:
phase = np.vdot(b, a)
return actual * phase.conjugate()
else:
aligned = []
for i in range(a.shape[0]):
phase = np.vdot(b[i], a[i])
aligned_vec = actual[i] * phase.conjugate()
aligned.append(aligned_vec)
return np.array(aligned)

def compare(self, actual, expected):
aligned = self.align_phase(actual, expected)
if actual.ndim == 1:
if not np.allclose(aligned, expected, atol=self.tol):
raise AssertionError("States differ beyond tolerance.")
else:
for i in range(actual.shape[0]):
if not np.allclose(aligned[i], expected[i], atol=self.tol):
raise AssertionError(f"State {i} differs:\nAligned: {aligned[i]}\nExpected: {expected[i]}")
Loading