Skip to content
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

Merged
merged 27 commits into from
Sep 5, 2024
Merged

Conversation

CalMacCQ
Copy link
Contributor

@CalMacCQ CalMacCQ commented Aug 22, 2024

Description

Adding a new AerDensityMatrixBackend class. Unlike other density matrix simulators available through the pytket extensions this emulator should support an optional NoiseModel in the constructor.

There's a decent amount of duplication from the AerBackend implementation for BackendInfo. Maybe there's a smarter and less error prone way to do it.

Related issues

closes #231

Checklist

  • I have performed a self-review of my code.
  • I have commented hard-to-understand parts of my code.
  • I have made corresponding changes to the public API documentation.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have updated the changelog with any user-facing changes.

@CalMacCQ CalMacCQ marked this pull request as draft August 22, 2024 14:55
@CalMacCQ CalMacCQ requested a review from yao-cqc August 23, 2024 11:28
@CalMacCQ CalMacCQ marked this pull request as ready for review August 23, 2024 11:29
@CalMacCQ CalMacCQ marked this pull request as draft August 23, 2024 11:36
@CalMacCQ CalMacCQ requested review from sjdilkes and removed request for yao-cqc August 23, 2024 11:57
@@ -1,9 +1,13 @@
Changelog
~~~~~~~~~

.. currentmodule:: pytket.extensions.qiskit
Copy link
Contributor

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

Copy link
Contributor Author

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.

:py:class:`IBMQBackend`

vs

:py:class:`~pytket.extensions.qiskit.IBMQBackend`

"""
Backend for running simulations on the Qiskit Aer density matrix simulator.

:param noise_model: Noise model to apply during simulation. Defaults to None.
Copy link
Contributor

Choose a reason for hiding this comment

The 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

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 __init__ method. I don't have a strong prefernce either way.

_supports_density_matrix = True
_supports_state = False
_memory = False
_noise_model = None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type information or all these attributes would be helpful

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added in 1d5fd8c

super().__init__()
self._qiskit_backend = qiskit_aer_backend(self._qiskit_backend_name)
self._noise_model = noise_model
gate_set = _tket_gate_set_from_qiskit_backend(self._qiskit_backend).union(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type for this would be great

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added in 1d5fd8c

name=type(self).__name__,
device_name=self._qiskit_backend_name,
version=__extension_version__,
architecture=FullyConnected(n_qubits),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this switch between characterisation.architecture and FullyConnected? Though maybe using an architecture with a density matrix is not likely behaviour anyway?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you may be right. See 64a3ad8

gate_set=_tket_gate_set_from_qiskit_backend(self._qiskit_backend),
supports_midcircuit_measurement=True,
supports_reset=True,
supports_fast_feedforward=True,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely true for the density matrix simulator?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe so. I have a test case for this here

return BackendResult(
c_bits=c_bits,
q_bits=q_bits,
shots=shots,
counts=counts,
state=state,
unitary=unitary,
density_matrix=density_matrix,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BackendResult already has a density_matrix attribute?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just passing it in as an arg when defining the BackendResult though right?

@CalMacCQ CalMacCQ requested a review from sjdilkes August 30, 2024 14:08
@CalMacCQ CalMacCQ marked this pull request as ready for review August 30, 2024 14:13
@CalMacCQ CalMacCQ requested review from cqc-alec and sjdilkes and removed request for sjdilkes and cqc-alec September 3, 2024 09:17
result1.get_density_matrix(), np.outer(output_state, output_state.conj())
)
# Example with resets and conditional gates
# Deterministic if input is a computational basis state
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't quite understand this comment. What exactly is deterministic?

Copy link
Contributor Author

@CalMacCQ CalMacCQ Sep 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So despite the fact that the circuit contains measures and resets it prepares a state deterministically assuming that it starts from the $|0\rangle^{\otimes n}$ state.

updated the comment.

assert noisy_density_sim.valid_circuit(circ)

result = noisy_density_sim.run_circuit(circ)
assert result.get_density_matrix().shape == (8, 8)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also check that the density matrix differs from what we get with a non-noisy simulation?

Copy link
Contributor Author

@CalMacCQ CalMacCQ Sep 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way I've done this is to check the purity $\gamma$ of the state represented by the density matrix $\rho$

$$ \gamma = \text{Tr}(\rho^2) $$

If $\gamma =1$, the state is pure. If $\gamma < 1$ the state is mixed. I've verified in 6d00b85 that the density matrix given by the noisy simulator is a mixed state and the noisless a pure state.

Another option would be to calcluate the expectation value of some observable $E$ and verify that noise changes the value of $\langle E \rangle$.

Comment on lines 304 to 305
if self.supports_state or self.supports_density_matrix:
qc.save_state()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the QuantumCircuit.save_state gave equivalent answers but I think you're right. Its more correct to save the density matrix.

Thanks

Copy link
Collaborator

@cqc-alec cqc-alec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of small comments.

noisy_dm = result.get_density_matrix()
assert noisy_dm.shape == (8, 8)
# Check purity to verify mixed state
assert np.trace(noisy_dm**2).real < 1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we change this to "< 0.99" or something so that we rule out the possibility of rounding errors causing the test to pass when it shouldn't?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, done.

noiseless_dm2 = result2.get_density_matrix()
assert np.allclose(noiseless_dm2, np.outer(statevector, statevector.conj()))
# Check purity to verify that we have a pure state
assert np.trace(noiseless_dm2**2).real == 1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to use np.isclose() for floating-point comparison, to account for rounding error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, done in ff91d0a

@CalMacCQ CalMacCQ merged commit 368df3e into main Sep 5, 2024
6 checks passed
@CalMacCQ CalMacCQ deleted the feat/Aer_density branch September 6, 2024 14:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support density matrix simulation
3 participants