Source code for qiskit_qulacs.qulacs_backend

"""QulacsBackend class."""

import copy
import time
import uuid
from collections import Counter
from typing import List, Union

import numpy as np
from qiskit import QuantumCircuit
from qiskit.providers import BackendV1 as Backend
from qiskit.providers import JobStatus, Options
from qiskit.providers.models import QasmBackendConfiguration
from qiskit.result import Result
from qiskit.result.models import ExperimentResult, ExperimentResultData

import qulacs
from qulacs.circuit import QuantumCircuitOptimizer

from .adapter import MAX_QUBITS, qiskit_to_qulacs
from .backend_utils import BASIS_GATES, available_devices, generate_config
from .qulacs_job import QulacsJob
from .version import __version__


[docs] class QulacsBackend(Backend): """QulacsBackend class.""" _BASIS_GATES = BASIS_GATES _DEFAULT_CONFIGURATION = { "backend_name": "qulacs_simulator", "backend_version": __version__, "n_qubits": MAX_QUBITS, "url": "https://github.com/Gopal-Dahale/qiskit-qulacs", "simulator": True, "local": True, "conditional": True, "open_pulse": False, "memory": True, "max_shots": int(1e6), "description": "A Qulacs fast quantum circuit simulator", "coupling_map": None, "basis_gates": _BASIS_GATES, "gates": [], } _SIMULATION_DEVICES = ("CPU", "GPU") _AVAILABLE_DEVICES = None def __init__( self, configuration=None, properties=None, provider=None, **backend_options ): # Update available devices for class if QulacsBackend._AVAILABLE_DEVICES is None: QulacsBackend._AVAILABLE_DEVICES = available_devices( QulacsBackend._SIMULATION_DEVICES ) # Default configuration if configuration is None: configuration = QasmBackendConfiguration.from_dict( QulacsBackend._DEFAULT_CONFIGURATION ) super().__init__( configuration, provider=provider, ) # Initialize backend properties self._properties = properties # Set options from backend_options dictionary if backend_options is not None: self.set_options(**backend_options) # Quantum circuit optimizer (if needed) self.qc_opt = QuantumCircuitOptimizer() self.class_suffix = { "GPU": "Gpu", "CPU": "", }
[docs] @classmethod def _default_options(cls): return Options( # Global options shots=0, device="CPU", seed_simulator=None, # Quantum Circuit Optimizer options qco_enable=False, qco_method="light", qco_max_block_size=2, )
def __repr__(self): """String representation of an QulacsBackend.""" name = self.__class__.__name__ display = f"'{self.name()}'" return f"{name}({display})"
[docs] def available_devices(self): """Return the available simulation methods.""" return copy.copy(self._AVAILABLE_DEVICES)
def _execute_circuits_job(self, circuits, states, run_options, job_id=""): """Run a job""" shots = run_options.shots seed = ( run_options.seed_simulator if run_options.seed_simulator else np.random.randint(1000) ) # Start timer start = time.time() expt_results = [] if shots: for state, circuit in zip(states, circuits): circuit.update_quantum_state(state) n = circuit.get_qubit_count() samples = state.sampling(shots, seed) bitstrings = [format(x, f"0{n}b") for x in samples] counts = dict(Counter(bitstrings)) expt_results.append( ExperimentResult( shots=shots, success=True, status=JobStatus.DONE, data=ExperimentResultData(counts=counts, memory=bitstrings), ) ) else: for state, circuit in zip(states, circuits): circuit.update_quantum_state(state) # Statevector expt_results.append( ExperimentResult( shots=shots, success=True, status=JobStatus.DONE, data=ExperimentResultData( statevector=state.get_vector(), ), ) ) return Result( backend_name=self.name(), backend_version=self.configuration().backend_version, job_id=job_id, qobj_id=0, success=True, results=expt_results, status=JobStatus.DONE, time_taken=time.time() - start, )
[docs] def run( self, run_input: Union[QuantumCircuit, List[QuantumCircuit]], **run_options, ) -> QulacsJob: run_input = [run_input] if isinstance(run_input, QuantumCircuit) else run_input run_input = list(qiskit_to_qulacs(run_input)) config = ( generate_config(self.options, run_options) if run_options else self.options ) # Use GPU if available if config.device not in self.available_devices(): if config.device == "GPU": raise ValueError("GPU support not installed. Install qulacs-gpu.") raise ValueError(f"Device {config.device} not found.") class_name = f'QuantumState{self.class_suffix.get(config.device, "")}' state_class = getattr(qulacs, class_name) # Use Quantum Circuit Optimizer if config.qco_enable: if config.qco_method == "light": for circuit in run_input: self.qc_opt.optimize_light(circuit) elif config.qco_method == "greedy": for circuit in run_input: self.qc_opt.optimize(circuit, config.qco_max_block_size) # Create quantum states states = [state_class(circuit.get_qubit_count()) for circuit in run_input] # Submit job job_id = str(uuid.uuid4()) qulacs_job = QulacsJob( self, job_id, self._execute_circuits_job, circuits=run_input, states=states, run_options=config, ) qulacs_job.submit() return qulacs_job