-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add density matrix simulation via qiskit Aer #380
Changes from all commits
607f639
7c73f2d
35fd4f0
611f5ae
8b6c4b8
166333b
67456b7
853ca8e
4e8e969
a6ede1f
36953f9
7a62e76
152d569
04db9ed
4b9d421
efbeed7
1d5fd8c
64a3ad8
3dcdd81
6f5cc47
b9e2b0d
10078ab
6d00b85
8ef8332
01f1fdd
ff91d0a
6f1e99a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -292,10 +292,16 @@ def process_circuits( | |
c0, ppcirc_rep = tkc, None | ||
|
||
qc = tk_to_qiskit(c0, replace_implicit_swaps) | ||
|
||
if self.supports_state: | ||
qc.save_state() | ||
|
||
elif self.supports_density_matrix: | ||
qc.save_density_matrix() | ||
|
||
elif self.supports_unitary: | ||
qc.save_unitary() | ||
|
||
qcs.append(qc) | ||
tkc_qubits_count.append(c0.n_qubits) | ||
ppcirc_strs.append(json.dumps(ppcirc_rep)) | ||
|
@@ -478,16 +484,16 @@ class AerBackend(_AerBaseBackend): | |
:param n_qubits: The maximum number of qubits supported by the backend. | ||
""" | ||
|
||
_persistent_handles = False | ||
_supports_shots = True | ||
_supports_counts = True | ||
_supports_expectation = True | ||
_expectation_allows_nonhermitian = False | ||
_persistent_handles: bool = False | ||
_supports_shots: bool = True | ||
_supports_counts: bool = True | ||
_supports_expectation: bool = True | ||
_expectation_allows_nonhermitian: bool = False | ||
|
||
_memory = True | ||
_memory: bool = True | ||
|
||
_qiskit_backend_name = "aer_simulator" | ||
_allowed_special_gates = { | ||
_qiskit_backend_name: str = "aer_simulator" | ||
_allowed_special_gates: set[OpType] = { | ||
OpType.Measure, | ||
OpType.Barrier, | ||
OpType.Reset, | ||
|
@@ -504,9 +510,9 @@ def __init__( | |
super().__init__() | ||
self._qiskit_backend = qiskit_aer_backend(self._qiskit_backend_name) | ||
self._qiskit_backend.set_options(method=simulation_method) | ||
gate_set = _tket_gate_set_from_qiskit_backend(self._qiskit_backend).union( | ||
self._allowed_special_gates | ||
) | ||
gate_set: set[OpType] = _tket_gate_set_from_qiskit_backend( | ||
self._qiskit_backend | ||
).union(self._allowed_special_gates) | ||
|
||
self._crosstalk_params = crosstalk_params | ||
if self._crosstalk_params is not None: | ||
|
@@ -575,15 +581,15 @@ class AerStateBackend(_AerBaseBackend): | |
:param n_qubits: The maximum number of qubits supported by the backend. | ||
""" | ||
|
||
_persistent_handles = False | ||
_supports_state = True | ||
_supports_expectation = True | ||
_expectation_allows_nonhermitian = False | ||
_persistent_handles: bool = False | ||
_supports_state: bool = True | ||
_supports_expectation: bool = True | ||
_expectation_allows_nonhermitian: bool = False | ||
|
||
_noise_model = None | ||
_memory = False | ||
_noise_model: Optional[NoiseModel] = None | ||
_memory: bool = False | ||
|
||
_qiskit_backend_name = "aer_simulator_statevector" | ||
_qiskit_backend_name: str = "aer_simulator_statevector" | ||
|
||
def __init__( | ||
self, | ||
|
@@ -613,14 +619,14 @@ class AerUnitaryBackend(_AerBaseBackend): | |
:param n_qubits: The maximum number of qubits supported by the backend. | ||
""" | ||
|
||
_persistent_handles = False | ||
_supports_unitary = True | ||
_persistent_handles: bool = False | ||
_supports_unitary: bool = True | ||
|
||
_memory = False | ||
_noise_model = None | ||
_needs_transpile = True | ||
_memory: bool = False | ||
_noise_model: Optional[NoiseModel] = None | ||
_needs_transpile: bool = True | ||
|
||
_qiskit_backend_name = "aer_simulator_unitary" | ||
_qiskit_backend_name: str = "aer_simulator_unitary" | ||
|
||
def __init__(self, n_qubits: int = 40) -> None: | ||
super().__init__() | ||
|
@@ -641,6 +647,75 @@ def __init__(self, n_qubits: int = 40) -> None: | |
] | ||
|
||
|
||
class AerDensityMatrixBackend(_AerBaseBackend): | ||
""" | ||
Backend for running simulations on the Qiskit Aer density matrix simulator. | ||
|
||
:param noise_model: Noise model to apply during simulation. Defaults to None. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should these be defined here or in the init? I'm not sure what our standard is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems to me that we document this in the class docstring rather than the |
||
:param n_qubits: The maximum number of qubits supported by the backend. | ||
""" | ||
|
||
_supports_density_matrix: bool = True | ||
_supports_state: bool = False | ||
_memory: bool = False | ||
_noise_model: Optional[NoiseModel] = None | ||
_needs_transpile: bool = True | ||
_supports_expectation: bool = True | ||
|
||
_qiskit_backend_name: str = "aer_simulator_density_matrix" | ||
|
||
_allowed_special_gates: set[OpType] = { | ||
OpType.Measure, | ||
OpType.Barrier, | ||
OpType.Reset, | ||
OpType.RangePredicate, | ||
} | ||
|
||
def __init__( | ||
self, | ||
noise_model: Optional[NoiseModel] = None, | ||
n_qubits: int = 40, | ||
) -> None: | ||
super().__init__() | ||
self._qiskit_backend = qiskit_aer_backend(self._qiskit_backend_name) | ||
|
||
gate_set: set[OpType] = _tket_gate_set_from_qiskit_backend( | ||
self._qiskit_backend | ||
).union(self._allowed_special_gates) | ||
self._noise_model = _map_trivial_noise_model_to_none(noise_model) | ||
characterisation: NoiseModelCharacterisation = ( | ||
_get_characterisation_of_noise_model(self._noise_model, gate_set) | ||
) | ||
self._has_arch: bool = bool(characterisation.architecture) and bool( | ||
characterisation.architecture.nodes | ||
) | ||
|
||
self._backend_info = BackendInfo( | ||
name=type(self).__name__, | ||
device_name=self._qiskit_backend_name, | ||
version=__extension_version__, | ||
architecture=( | ||
FullyConnected(n_qubits) | ||
if not self._has_arch | ||
else characterisation.architecture | ||
), | ||
gate_set=_tket_gate_set_from_qiskit_backend(self._qiskit_backend), | ||
supports_midcircuit_measurement=True, | ||
supports_reset=True, | ||
supports_fast_feedforward=True, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is definitely true for the density matrix simulator? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe so. I have a test case for this here |
||
all_node_gate_errors=characterisation.node_errors, | ||
all_edge_gate_errors=characterisation.edge_errors, | ||
all_readout_errors=characterisation.readout_errors, | ||
averaged_node_gate_errors=characterisation.averaged_node_errors, | ||
averaged_edge_gate_errors=characterisation.averaged_edge_errors, | ||
averaged_readout_errors=characterisation.averaged_readout_errors, | ||
misc={"characterisation": characterisation.generic_q_errors}, | ||
) | ||
self._required_predicates = [ | ||
GateSetPredicate(self._backend_info.gate_set), | ||
] | ||
|
||
|
||
def _process_noise_model( | ||
noise_model: NoiseModel, gate_set: set[OpType] | ||
) -> NoiseModelCharacterisation: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -103,7 +103,7 @@ def qiskit_experimentresult_to_backendresult( | |
for index in range(size): | ||
q_bits.append(Qubit(name, index)) | ||
|
||
shots, counts, state, unitary = (None,) * 4 | ||
shots, counts, state, unitary, density_matrix = (None,) * 5 | ||
datadict = result.data.to_dict() | ||
if _result_is_empty_shots(result): | ||
n_bits = len(c_bits) if c_bits else 0 | ||
|
@@ -129,13 +129,17 @@ def qiskit_experimentresult_to_backendresult( | |
if "unitary" in datadict: | ||
unitary = datadict["unitary"].reverse_qargs().data | ||
|
||
if "density_matrix" in datadict: | ||
density_matrix = datadict["density_matrix"].reverse_qargs().data | ||
|
||
return BackendResult( | ||
c_bits=c_bits, | ||
q_bits=q_bits, | ||
shots=shots, | ||
counts=counts, | ||
state=state, | ||
unitary=unitary, | ||
density_matrix=density_matrix, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm just passing it in as an arg when defining the |
||
ppcirc=ppcirc, | ||
) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this deliberate? I'm unfamiliar with the syntax
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, basically it means that the refernces to classes and functions don't need to be fully qualified. The link is also generated
i.e.
vs