"""Module to draw samples from the circuit.
Used for computing properties of the circuit like Entanglability and Expressibility.
"""
import typing
import cirq
import numpy as np
import qiskit
from qiskit.providers.aer.noise import NoiseModel as qiskitNoiseModel
from cirq.devices.noise_model import NoiseModel as cirqNoiseModel
from pyquil.noise import NoiseModel as pyquilNoiseModel
from ..interface.circuit import CircuitDescriptor
[docs]class CircuitSimulator:
"""The interface for users to execute their CircuitDescriptor objects"""
def __init__(
self,
circuit: CircuitDescriptor,
noise_model: typing.Union[
cirqNoiseModel, qiskitNoiseModel, pyquilNoiseModel, None
] = None,
) -> None:
"""Initialize the state simulator
:type circuit: CircuitDescriptor
:param circuit: the target circuit to simulate
:type noise_model: Noise model as a dict or in the library format
:param noise_model: the noise model as dict or empty dict for density matrix simulations,
None if performing state vector simulations
"""
self.circuit = circuit
self.noise_model = noise_model
self._result = None
@property
def result(
self,
) -> typing.Optional[np.ndarray]:
"""Get the results stored from the circuit simulator
:return: stored result of the circuit simulation if it has been performed, else None.
:rtype: np.array or None
"""
return self._result
[docs] def simulate(
self,
param_resolver: typing.Dict[qiskit.circuit.Parameter, float],
shots: int = 1024,
) -> np.ndarray:
"""Simulate to get the state vector or the density matrix
:type param_resolver: Dict to resolve all parameters to a static float value
:param param_resolver: a dictionary of all the symbols/parameters mapping to their values
:type shots: int
:param shots: number of times to run the qiskit density matrix simulator
:returns: state vector or density matrix resulting from the simulation
:rtype: np.array
:raises NotImplementedError: if circuit simulation is not supported for a backend
"""
if self.circuit.default_backend == "qiskit":
circuit = self.circuit.qiskit_circuit.bind_parameters(param_resolver)
if self.noise_model is not None:
circuit.snapshot("final", snapshot_type="density_matrix")
result = qiskit.execute(
circuit,
qiskit.Aer.get_backend("qasm_simulator"),
shots=shots,
noise_model=self.noise_model,
backend_options={"method": "density_matrix"},
).result()
result_data = result.data(0)["snapshots"]["density_matrix"]["final"][0][
"value"
]
else:
circuit.snapshot("final", snapshot_type="statevector")
result = qiskit.execute(
circuit, qiskit.Aer.get_backend("aer_simulator_statevector")
).result()
result_data = result.data(0)["snapshots"]["statevector"]["final"][0]
elif self.circuit.default_backend == "cirq":
circuit = self.circuit.cirq_circuit
non_unitary_flag = False
for op in circuit.all_operations():
op_name = str(op).split("(")[0]
if op_name in [
"phase_flip",
"phase_damp",
"amplitude_damp",
"depolarize",
"asymmetric_depolarize",
]:
non_unitary_flag = True
break
if self.noise_model is None and not non_unitary_flag:
simulator = cirq.Simulator() # type: ignore
result = simulator.simulate(self.circuit.cirq_circuit, param_resolver)
result_data = result.final_state_vector
else:
simulator = cirq.DensityMatrixSimulator(noise=self.noise_model) # type: ignore
result = simulator.simulate(self.circuit.cirq_circuit, param_resolver)
result_data = result.final_density_matrix
else:
raise NotImplementedError(
"Parametrized circuit simulation is not implemented for this backend."
)
self._result = result_data
return result_data