Source code for scikit_quri.backend.oqtopus_estimator

from collections.abc import Iterable, Sequence
from typing import Optional

from quri_parts.circuit import NonParametricQuantumCircuit
from quri_parts.circuit.transpile import (
    SequentialTranspiler,
    SingleQubitUnitaryMatrix2RYRZTranspiler,
)
from quri_parts.core.estimator import Estimatable, Estimate
from quri_parts.core.operator import Operator, PauliLabel
from quri_parts_oqtopus.backend import OqtopusConfig, OqtopusEstimationBackend
from quri_parts.qulacs.estimator import _Estimate

from .base_estimator import BaseEstimator


[docs]class OqtopusEstimator(BaseEstimator): """Estimator class that computes expectation values on real quantum hardware via quri-parts-oqtopus. Requires an OQTOPUS configuration file at ``~/.oqtopus``. See: https://quri-parts-oqtopus.readthedocs.io/en/stable/usage/getting_started/#prepare-oqtopus-configuration-file Args: device_id: ID of the device to run on. shots: Number of shots per circuit execution. Defaults to 1000. config: OQTOPUS configuration. Defaults to None. """ def __init__( self, device_id: str, shots: int = 1000, config: Optional[OqtopusConfig] = None, ) -> None: self.backend = OqtopusEstimationBackend(config) self.device_id = device_id self.shots = shots
[docs] def estimate(self, operators, states): """Compute expectation values for combinations of operators and states. If either operators or states contains a single element, it is broadcast to match the length of the other. If both contain multiple elements, they must have the same length and are paired one-to-one. Args: operators: List of operators for which to compute expectation values. states: List of quantum states. Returns: List of expectation values for each (operator, state) pair. Raises: ValueError: If operators or states is empty, or if both have multiple elements with mismatched lengths. BackendError: If execution on OQTOPUS fails. """ num_ops = len(operators) num_states = len(states) if num_ops == 0: raise ValueError("No operator specified.") if num_states == 0: raise ValueError("No state specified.") if num_ops > 1 and num_states > 1 and num_ops != num_states: raise ValueError( f"Number of operators ({num_ops}) does not matchnumber of states ({num_states}).", ) if num_states == 1: # Reuse the same transpiled circuit for all operators (shallow copy for memory efficiency) circuits = [self._transpile_circuit(states[0].circuit)] * num_ops return self._estimate_concurrently(operators, circuits) if num_ops == 1: operators = [next(iter(operators))] * num_states circuits = [self._transpile_circuit(state.circuit) for state in states] return self._estimate_concurrently(operators, circuits)
def _estimate_concurrently( self, operators: Sequence[Estimatable], circuits: Sequence[NonParametricQuantumCircuit], ) -> Iterable[Estimate[complex]]: """Compute expectation values for one-to-one pairs of operators and circuits. Args: operators: List of operators for which to compute expectation values. circuits: List of quantum circuits, paired with operators by index. Returns: List of expectation values for each (operator, circuit) pair. Raises: BackendError: If execution on OQTOPUS fails. """ results: list[Estimate[complex]] = [] for circuit, operator in zip(circuits, operators): # Normalize Estimatable to Operator if isinstance(operator, PauliLabel): operator = Operator({operator: 1.0}) job = self.backend.estimate( circuit, operator=operator, device_id=self.device_id, shots=self.shots, ) result = job.result() exp_real = result.exp_value # On failure the backend raises an exception, so exp_value is None only when the result is 0 if exp_real is None: exp_real = 0.0 results.append(_Estimate(value=complex(exp_real, 0.0))) return results def _transpile_circuit( self, circuit: NonParametricQuantumCircuit, ) -> NonParametricQuantumCircuit: """Transpile a circuit for submission to OQTOPUS. Args: circuit: Circuit before transpilation. Returns: Transpiled circuit. """ transpiler = SequentialTranspiler( [ # quri-parts' QASM does not support UnitaryMatrix gates; convert them to RY/RZ SingleQubitUnitaryMatrix2RYRZTranspiler(), ], ) transpiled_circuit = transpiler(circuit) return transpiled_circuit