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

Simulate circuits containing PauliMeasurements using PauliFrames #412

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

J-C-Q
Copy link
Contributor

@J-C-Q J-C-Q commented Nov 1, 2024

This PR adds a layer of abstraction that uses option 2 of #411 to make it easier to simulate circuits containing PauliMeasurements using PauliFrames.

  • The PauliFrame contractor adds an extra qubit (at the end), which then gets used as an ancillary qubits for the PauliMeasurements.
  • The apply! function has a method for PauliMeasurements that basically reuses ECC.naive_ancillary_paulimeasurement to convert a PauliMeasurement to a list of gates (which already have apply! methods) that perform the measurement using the ancillary qubit.
  • Since PauliMeasurement is parametric, add a concrete_typeparams method for PauliMeasurements to make CompactifiedGate possible. (I'm not sure if the one possible type parameter I specify here is exhaustive... probably not.)

Copy link
Member

@Krastanov Krastanov left a comment

Choose a reason for hiding this comment

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

This is awesome, thank you!

I think we just need to add some tests (potentially including a test that uses @allocated to verify that memory is not churned).

end
end
op.pauli.phase[] == 0 || apply!(frame, sX(n + 1))
apply!(frame, sMRX(n + 1, op.bit))
Copy link
Member

Choose a reason for hiding this comment

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

I think this should be sMRZ

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Of course...

@@ -26,7 +26,8 @@ $(TYPEDSIGNATURES)
Prepare an empty set of Pauli frames with the given number of `frames` and `qubits`. Preallocates spaces for `measurement` number of measurements.
"""
function PauliFrame(frames, qubits, measurements)
stab = fastcolumn(zero(Stabilizer, frames, qubits)) # TODO this should really be a Tableau
# one extra qubit for ancilla measurements
stab = fastcolumn(zero(Stabilizer, frames, qubits + 1)) # TODO this should really be a Tableau
Copy link
Member

Choose a reason for hiding this comment

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

I agree with this change. I think now we need to update show and nqubits and maybe others. Thankfully, for show it can be as simple as using @view frame[:,1:end-1]

Copy link
Member

Choose a reason for hiding this comment

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

actually, we do not even have a show method implemented, so this probably only needs a correct nqubits

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 subtracted the ancillary qubit in the nqubits(f::PauliFrame) function. I think that should be fine.

@@ -120,6 +121,24 @@ function apply!(frame::PauliFrame, op::sMRZ) # TODO sMRY, and faster sMRX
return frame
end

function apply!(frame::PauliFrame, op::PauliMeasurement)
# this is inspired by ECC.naive_ancillary_paulimeasurement. Not sure if it's better to import and call that.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
# this is inspired by ECC.naive_ancillary_paulimeasurement. Not sure if it's better to import and call that.
# this is inspired by ECC.naive_syndrome_circuit

Better to keep it the way you did it here, as this is non-allocating while naive_syndrome_circuit would create a temporary array

@Krastanov
Copy link
Member

The test failures happen due to this:

pₙ = embed(naive_qubits, 1:dataqubits, p)
pₛ = embed(shor_qubits, 1:dataqubits, p)
mul_left!(naive_frames.frame, pₙ)
mul_left!(shor_frames.frame, pₛ)

The cleanest way to fix this is probably to do mul_left!(tab(naive_frames), pₙ) instead of mul_left!(naive_frames.frame, pₙ)

with a newly defined

tab(p::PauliFrame) = @view p.frame[:,1:end-1] # to account of the buffer ancillary qubit

@J-C-Q
Copy link
Contributor Author

J-C-Q commented Nov 1, 2024

Somehow this is more complicated because the view implementations for PauliFrame, Stabilizer and Tableau only allow one dimensional indexing as far as I can tell.

@Krastanov
Copy link
Member

darn, you are right. Thankfully, here we are adding a qubit at the very end, so there is still an easy workaround

tab(pf::PauliFrame{Stabilizer{Tableau{V,F}}, M}) where {V,F,M} = Tableau{V,F}(pf.frame.tab.phases, pf.frame.tab.xzs, nqubits(pf))

If I am not getting this wrong, the internal indexing utilities of Tableau will take care of skipping over the extra qubit. This would need some testing though

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.

2 participants