Skip to content

Commit

Permalink
Adding tests for single qubit simulation, and working on support
Browse files Browse the repository at this point in the history
  • Loading branch information
PabloAndresCQ committed Sep 11, 2024
1 parent e115cda commit 0cf40ec
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 2 deletions.
15 changes: 13 additions & 2 deletions pytket/extensions/cutensornet/structured_state/mps.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,6 @@ def __init__(
n_tensors = len(qubits)
if n_tensors == 0: # There's no initialisation to be done
pass
elif n_tensors == 1:
raise ValueError("Please, provide at least two qubits.")
else:
self.qubit_position = {q: i for i, q in enumerate(qubits)}

Expand Down Expand Up @@ -238,6 +236,8 @@ def apply_qubit_relabelling(self, qubit_map: dict[Qubit, Qubit]) -> MPS:
Raises:
ValueError: If any of the keys in ``qubit_map`` are not qubits in the state.
"""
# TODO: Is this properly supported in MPSxMPO?

new_qubit_position = dict()
for q_orig, q_new in qubit_map.items():
# Check the qubit is in the state
Expand Down Expand Up @@ -268,6 +268,8 @@ def add_qubit(self, new_qubit: Qubit, position: int, state: int = 0) -> MPS:
ValueError: If ``position`` is negative or larger than ``len(self)``.
ValueError: If ``state`` is not ``0`` or ``1``.
"""
# TODO: Is this properly supported in MPSxMPO?

options = {"handle": self._lib.handle, "device_id": self._lib.device_id}

if new_qubit in self.qubit_position.keys():
Expand Down Expand Up @@ -575,6 +577,7 @@ def measure(self, qubits: set[Qubit], destructive: bool = True) -> dict[Qubit, i
Raises:
ValueError: If an element in ``qubits`` is not a qubit in the state.
"""
self._flush()
result = dict()

# Obtain the positions that need to be measured and build the reverse dict
Expand Down Expand Up @@ -658,6 +661,8 @@ def postselect(self, qubit_outcomes: dict[Qubit, int]) -> float:
ValueError: If all of the qubits in the MPS are being postselected. Instead,
you may wish to use ``get_amplitude()``.
"""
self._flush()

for q, v in qubit_outcomes.items():
if q not in self.qubit_position:
raise ValueError(f"Qubit {q} is not a qubit in the MPS.")
Expand Down Expand Up @@ -756,6 +761,8 @@ def expectation_value(self, pauli_string: QubitPauliString) -> float:
Raises:
ValueError: If a key in ``pauli_string`` is not a qubit in the MPS.
"""
self._flush()

for q in pauli_string.map.keys():
if q not in self.qubit_position:
raise ValueError(f"Qubit {q} is not a qubit in the MPS.")
Expand Down Expand Up @@ -795,6 +802,7 @@ def expectation_value(self, pauli_string: QubitPauliString) -> float:

def get_fidelity(self) -> float:
"""Returns the current fidelity of the state."""
self._flush()
return self.fidelity

def get_statevector(self) -> np.ndarray:
Expand All @@ -803,6 +811,8 @@ def get_statevector(self) -> np.ndarray:
Raises:
ValueError: If there are no qubits left in the MPS.
"""
self._flush()

if len(self) == 0:
raise ValueError("There are no qubits left in this MPS.")

Expand Down Expand Up @@ -860,6 +870,7 @@ def get_amplitude(self, state: int) -> complex:
Returns:
The amplitude of the computational state in the MPS.
"""
self._flush()

# Auxiliar dictionary of physical bonds to qubit IDs
qubit_id = {location: qubit for qubit, location in self.qubit_position.items()}
Expand Down
9 changes: 9 additions & 0 deletions pytket/extensions/cutensornet/structured_state/mps_mpo.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ def _apply_1q_unitary(self, unitary: cp.ndarray, qubit: Qubit) -> MPSxMPO:
# Apply the gate to the MPS with eager approximation
self._aux_mps._apply_1q_unitary(unitary, qubit)

if len(self) == 1:
# If there is only one qubit, there is no benefit in using an MPO, so
# simply copy from the standard MPS
self.tensors[0] = self._aux_mps.tensors[0].copy()

# Glossary of bond IDs
# i -> input to the MPO tensor
# o -> output of the MPO tensor
Expand Down Expand Up @@ -337,6 +342,10 @@ def _flush(self) -> None:
The method applies variational optimisation of the MPS until it
converges. Based on https://arxiv.org/abs/2207.05612.
"""
if self._mpo_bond_counter == 0:
# MPO is empty, there is nothing to flush
return None

self._logger.info("Applying variational optimisation.")
self._logger.info(f"Fidelity before optimisation={self._aux_mps.fidelity}")

Expand Down
13 changes: 13 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ def quantum_volume_circuit(n_qubits: int) -> Circuit:
return c


@pytest.fixture
def q1_empty() -> Circuit:
circuit = Circuit(1)
return circuit


@pytest.fixture
def q5_empty() -> Circuit:
circuit = Circuit(5)
Expand All @@ -63,6 +69,13 @@ def q8_empty() -> Circuit:
return circuit


@pytest.fixture
def q1_h0rz() -> Circuit:
circuit = Circuit(1)
circuit.H(0).Rz(0.3, 0)
return circuit


@pytest.fixture
def q2_x0() -> Circuit:
circuit = Circuit(2)
Expand Down
11 changes: 11 additions & 0 deletions tests/test_structured_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,10 @@ def test_canonicalise_ttn(center: Union[RootPath, Qubit]) -> None:
@pytest.mark.parametrize(
"circuit",
[
pytest.lazy_fixture("q1_empty"), # type: ignore
pytest.lazy_fixture("q5_empty"), # type: ignore
pytest.lazy_fixture("q8_empty"), # type: ignore
pytest.lazy_fixture("q1_h0rz"), # type: ignore
pytest.lazy_fixture("q2_x0"), # type: ignore
pytest.lazy_fixture("q2_x1"), # type: ignore
pytest.lazy_fixture("q2_v0"), # type: ignore
Expand Down Expand Up @@ -308,7 +310,9 @@ def test_exact_circ_sim(circuit: Circuit, algorithm: SimulationAlgorithm) -> Non
@pytest.mark.parametrize(
"circuit",
[
pytest.lazy_fixture("q1_empty"), # type: ignore
pytest.lazy_fixture("q5_empty"), # type: ignore
pytest.lazy_fixture("q1_h0rz"), # type: ignore
pytest.lazy_fixture("q2_lcu1"), # type: ignore
pytest.lazy_fixture("q2_lcu2"), # type: ignore
pytest.lazy_fixture("q2_lcu3"), # type: ignore
Expand Down Expand Up @@ -363,8 +367,10 @@ def test_prepare_circuit_mps(circuit: Circuit, algorithm: SimulationAlgorithm) -
@pytest.mark.parametrize(
"circuit",
[
pytest.lazy_fixture("q1_empty"), # type: ignore
pytest.lazy_fixture("q5_empty"), # type: ignore
pytest.lazy_fixture("q8_empty"), # type: ignore
pytest.lazy_fixture("q1_h0rz"), # type: ignore
pytest.lazy_fixture("q2_x0"), # type: ignore
pytest.lazy_fixture("q2_x1"), # type: ignore
pytest.lazy_fixture("q2_v0"), # type: ignore
Expand Down Expand Up @@ -409,8 +415,10 @@ def test_approx_circ_sim_gate_fid(
@pytest.mark.parametrize(
"circuit",
[
pytest.lazy_fixture("q1_empty"), # type: ignore
pytest.lazy_fixture("q5_empty"), # type: ignore
pytest.lazy_fixture("q8_empty"), # type: ignore
pytest.lazy_fixture("q1_h0rz"), # type: ignore
pytest.lazy_fixture("q2_x0"), # type: ignore
pytest.lazy_fixture("q2_x1"), # type: ignore
pytest.lazy_fixture("q2_v0"), # type: ignore
Expand Down Expand Up @@ -453,7 +461,9 @@ def test_approx_circ_sim_chi(circuit: Circuit, algorithm: SimulationAlgorithm) -
@pytest.mark.parametrize(
"circuit",
[
pytest.lazy_fixture("q1_empty"), # type: ignore
pytest.lazy_fixture("q5_empty"), # type: ignore
pytest.lazy_fixture("q1_h0rz"), # type: ignore
pytest.lazy_fixture("q2_x0cx01cx10"), # type: ignore
pytest.lazy_fixture("q2_lcu2"), # type: ignore
pytest.lazy_fixture("q3_cx01cz12x1rx0"), # type: ignore
Expand Down Expand Up @@ -715,6 +725,7 @@ def test_expectation_value(circuit: Circuit, observable: QubitPauliString) -> No
@pytest.mark.parametrize(
"circuit",
[
pytest.lazy_fixture("q1_h0rz"), # type: ignore
pytest.lazy_fixture("q2_v0cx01cx10"), # type: ignore
pytest.lazy_fixture("q2_hadamard_test"), # type: ignore
pytest.lazy_fixture("q2_lcu2"), # type: ignore
Expand Down

0 comments on commit 0cf40ec

Please sign in to comment.