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

Transpiler may permute qubit ordering in statevectors, but not density matrices #5839

Closed
Serock3 opened this issue Feb 11, 2021 · 6 comments
Closed
Labels
bug Something isn't working

Comments

@Serock3
Copy link

Serock3 commented Feb 11, 2021

Information

  • Qiskit Terra version: 0.16.4
  • Python version: 3.8.5
  • Operating system: Windows 10 (running through WSL)

What is the current behavior?

Similar issues have been noted before (#2980, #5350, #4608), but I wanted to add how density matrices behave.

After transpiling the circuit the state vector (may be) permuted, this only affects snapshots of the state vector and not density matrices or measurements. There are two causes of this. First, the layout the transpiler chooses. This can be retrieved from transpiled_circ._layout. Second, the SWAPs inserted by the routing method permutes the qubit ordering. This can change throughout the circuit, but I don't know of any way to get this information.

Steps to reproduce the problem

This circuit creates a circuit for a device with no connectivity between qubit 1 and 3. Which causes the transpiler to both permutate the layout and insert SWAPs. It takes three snapshots throughout the operation and compares the normal circuit to its transpiled version using both state vector and density matrix snapshots.

from qiskit import QuantumCircuit,QuantumRegister, Aer, execute
from qiskit.compiler import transpile
from qiskit.quantum_info.states.measures import state_fidelity
from IPython.core.display import display

# Create circuit
circ = QuantumCircuit(3)
circ.x(0)
circ.cnot(0,1)
circ.snapshot('snap1', snapshot_type='statevector')
circ.snapshot('snap1', snapshot_type='density_matrix')
circ.cnot(0,2)
circ.snapshot('snap2', snapshot_type='statevector')
circ.snapshot('snap2', snapshot_type='density_matrix')
circ.cnot(1,2)
circ.snapshot('snap3', snapshot_type='statevector')
circ.snapshot('snap3', snapshot_type='density_matrix')
circ.measure_all()

transpiled_circ = transpile(circ, coupling_map=[[0,1],[1,0],[1,2],[2,1]],optimization_level=2)

display(circ.draw())
display(transpiled_circ.draw())

results = execute(circ,Aer.get_backend('qasm_simulator')).result()
statevector1 = results.data()['snapshots']['statevector']['snap1'][0]
statevector2 = results.data()['snapshots']['statevector']['snap2'][0]
statevector3 = results.data()['snapshots']['statevector']['snap2'][0]
density_matrix1 = results.data()['snapshots']['density_matrix']['snap1'][0]['value']
density_matrix2 = results.data()['snapshots']['density_matrix']['snap2'][0]['value']
density_matrix3 = results.data()['snapshots']['density_matrix']['snap3'][0]['value']

results = execute(transpiled_circ,Aer.get_backend('qasm_simulator')).result()
statevector_transpiled1 = results.data()['snapshots']['statevector']['snap1'][0]
statevector_transpiled2 = results.data()['snapshots']['statevector']['snap2'][0]
statevector_transpiled3 = results.data()['snapshots']['statevector']['snap3'][0]
density_matrix_transpiled1 = results.data()['snapshots']['density_matrix']['snap1'][0]['value']
density_matrix_transpiled2 = results.data()['snapshots']['density_matrix']['snap2'][0]['value']
density_matrix_transpiled3 = results.data()['snapshots']['density_matrix']['snap3'][0]['value']

print('Fidelity between transpiled and normal statevector snapshots')
print('snap 1',state_fidelity(statevector_transpiled1,statevector1))
print('snap 2',state_fidelity(statevector_transpiled2,statevector2))
print('snap 3',state_fidelity(statevector_transpiled3,statevector3))
print('\nFidelity between transpiled and normal density matrix snapshots')
print('snap 1',state_fidelity(density_matrix_transpiled1,density_matrix1))
print('snap 2',state_fidelity(density_matrix_transpiled2,density_matrix2))
print('snap 3',state_fidelity(density_matrix_transpiled3,density_matrix3))
print('Counts normal: ',results.get_counts(circ))
print('Counts transpiled: ',results.get_counts(transpiled_circ))

Result of running:

Circuit pre transpiling
image

Circuit after transpiling
image

Fidelity between transpiled and normal statevector snapshots
snap 1 0.0
snap 2 1.0
snap 3 0.0

Fidelity between transpiled and normal density matrix snapshots
snap 1 1.0
snap 2 1.0
snap 3 1.0
Counts normal:  {'011': 1024}
Counts transpiled:  {'011': 1024}

Note how the fidelity between the state vectors of the normal and the transpiled circuit goes from 0 to 1 to 0 again, as the permutation changes from the inserted SWAPs. The snapshot of the density matrix is never permuted, neither are the measurements.

What is the expected behavior?

The snapshots of the state vector should undo the permutation, as it does for density matrix snapshots.

Suggested solutions

Not sure.

@Serock3 Serock3 added the bug Something isn't working label Feb 11, 2021
@ajavadia
Copy link
Member

ajavadia commented Feb 11, 2021

Interesting, I agree the snapshots should undo permutations as the intent of the snapshot is to save information about the original circuit (it is easy to keep track of the permutations, starting from layout and going past each swap).

We don't want this restoring of the permutation to be done by the transpiler using extra gates prior to each snapshot, because adding gates could affect noisy simulation.

Snapshot (which is becoming Save instructions in @chriseclectic PRs) already has an implicit ordering on the qubits it attaches to. So I think the transpiler should just change the qargs to match the current permutation every time it encounters one of these instructions.

I doubt this is done currently for density matrices, however, so seems like something is happening by chance to cause the permutation to be correct.

@chriseclectic
Copy link
Member

chriseclectic commented Feb 11, 2021

Density matrix snapshots behave quite differently to statevector.

  • Statevector is always defined on full width of circuit and ignores the ordering of qubits in its qarg.
  • Density matrix is defined as the reduced density matrix on the specified qubits in the qargs of the instruction, so changing qubits arg will permute the returned density matrix.

So if you permute qubits in the transpiler the statevector will change, but with density matrix if you also permute qubits in the density matrix snapshot to account for this (which the transpiler does), the final returned density matrix should be unchanged.

Maybe in the future we could add ability to permute qubits in the save statevector qubits arg (as long as its still defined across all qubits), and then it would behave like the density matrix one does.

@ajavadia
Copy link
Member

Ok I agree the Statevector snapshot should be constrained to be defined over all qubits, but I don't think it should be constrained to be defined over qubit order 0..N-1. We should allow that to be permuted.

@ajavadia ajavadia added the good first issue Good for newcomers label Feb 11, 2021
@chriseclectic
Copy link
Member

Yeah it would be good to have but simulators don't support computing that permutation at the moment. It would have to be an added feature.

@Qiskit Qiskit deleted a comment from fs1132429 Jul 15, 2021
@epelaaez
Copy link
Contributor

epelaaez commented Oct 2, 2021

Hi @chriseclectic, is the consensus that the feature to compute permutations of the statevector should be added? If so, I'd like to work on it if possible.

@1ucian0 1ucian0 removed the good first issue Good for newcomers label May 21, 2022
@jakelishman
Copy link
Member

Since the snapshot instructions don't exist in Qiskit 1.0 any more, I'll close this issue as obsolete. The "save" functionality is now specific to simulators; there's functionality along these lines in Qiskit Aer, which would be the right place to go if there's further issues about this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

6 participants