diff --git a/docs/changelog.rst b/docs/changelog.rst index c857a39..bc2378f 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,11 @@ Changelog ~~~~~~~~~ +Unreleased +-------------------- + +* Allow setting of rng seed in qulacs backend. + 0.29.0 (June 2023) ------------------ diff --git a/pytket/extensions/qulacs/backends/qulacs_backend.py b/pytket/extensions/qulacs/backends/qulacs_backend.py index 859383e..552af9b 100644 --- a/pytket/extensions/qulacs/backends/qulacs_backend.py +++ b/pytket/extensions/qulacs/backends/qulacs_backend.py @@ -15,8 +15,9 @@ """Methods to allow tket circuits to be ran on the Qulacs simulator """ -from typing import List, Optional, Sequence, Union +from typing import List, Optional, Sequence, Union, cast from logging import warning +from random import Random from uuid import uuid4 import numpy as np from sympy import Expr # type: ignore @@ -181,6 +182,9 @@ def process_circuits( if valid_check: self._check_all_circuits(circuits, nomeasure_warn=False) + seed = cast(Optional[int], kwargs.get("seed")) + rng = Random(seed) if seed else None + handle_list = [] for circuit, n_shots_circ in zip(circuits, n_shots_list): qulacs_state = self._sim(circuit.n_qubits) @@ -205,7 +209,9 @@ def process_circuits( else: bits, choose_indices = zip(*bits2index) - samples = qulacs_state.sampling(n_shots_circ) + samples = self._sample_quantum_state( + qulacs_state, n_shots_circ, rng + ) shots = OutcomeArray.from_ints(samples, circuit.n_qubits) shots = shots.choose_indices(choose_indices) try: @@ -228,6 +234,14 @@ def process_circuits( del qulacs_circ return handle_list + def _sample_quantum_state( + self, quantum_state: QuantumState, n_shots: int, rng: Optional[Random] + ) -> List[int]: + if rng: + return quantum_state.sampling(n_shots, rng.randint(0, 2**32 - 1)) + else: + return quantum_state.sampling(n_shots) + def circuit_status(self, handle: ResultHandle) -> CircuitStatus: if handle in self._cache: return CircuitStatus(StatusEnum.COMPLETED) diff --git a/tests/test_qulacs_backend.py b/tests/test_qulacs_backend.py index 8e251ff..2244bb2 100644 --- a/tests/test_qulacs_backend.py +++ b/tests/test_qulacs_backend.py @@ -13,6 +13,7 @@ # limitations under the License. from collections import Counter +from typing import List, Sequence, Union, Optional import warnings import math from hypothesis import given, strategies @@ -20,18 +21,43 @@ import pytest from openfermion.ops import QubitOperator # type: ignore from openfermion.linalg import eigenspectrum # type: ignore +from pytket.backends import ResultHandle from pytket.circuit import Circuit, BasisOrder, OpType, Qubit # type: ignore from pytket.pauli import Pauli, QubitPauliString # type: ignore from pytket.passes import CliffordSimp # type: ignore from pytket.utils.operators import QubitPauliOperator +from pytket.utils.results import KwargTypes from pytket.extensions.qulacs import QulacsBackend -backends = [QulacsBackend()] + +def make_seeded_QulacsBackend(base: type[QulacsBackend]) -> type: + class SeededQulacsBackend(base): # type: ignore + def __init__(self, seed: int): + base.__init__(self) + self._seed = seed + + def process_circuits( + self, + circuits: Sequence[Circuit], + n_shots: Union[None, int, Sequence[Optional[int]]] = None, + valid_check: bool = True, + **kwargs: KwargTypes + ) -> List[ResultHandle]: + if not "seed" in kwargs: + kwargs["seed"] = self._seed + return base.process_circuits(self, circuits, n_shots, valid_check, **kwargs) + + return SeededQulacsBackend + + +backends = [QulacsBackend(), make_seeded_QulacsBackend(QulacsBackend)(-1)] try: from pytket.extensions.qulacs import QulacsGPUBackend - backends.append(QulacsGPUBackend()) + backends.extend( + [QulacsGPUBackend(), make_seeded_QulacsBackend(QulacsGPUBackend)(1)] + ) except ImportError: warnings.warn("local settings failed to import QulacsGPUBackend", ImportWarning) @@ -266,7 +292,6 @@ def test_shots_bits_edgecases(n_shots, n_bits) -> None: c = Circuit(n_bits, n_bits) for qulacs_backend in backends: - # TODO TKET-813 add more shot based backends and move to integration tests h = qulacs_backend.process_circuit(c, n_shots) res = qulacs_backend.get_result(h)