Closed ACE07-Sev closed 5 months ago
I'm not quite sure what you're saying the problem here is in the "update". If you set the global phase to an incorrect value, the matrices will of course appear different.
In the main body: your result_qc
is not the same as your transpiled_qc
, because you're only copying across the gates and not the global phase of the circuit. You should set result_qc.global_phase = transpiled_qc.global_phase
, but also, I'm very unclear what you're trying to do with result_qc
, since transpiled_qc
already appears to be the object you wanted.
You can make a faithful copy of a QuantumCircuit
using QuantumCircuit.copy()
, or if you must use QuantumCircuit.data
for some reason:
result_qc = transpiled_qc.copy_empty_like()
(includes global phase, and uses the exact same qubit objects, etc)for instr in transpiled_qc.data: result_qc.append(instr.operation, instr.qubits, instr.clbits)
. Try not to access private members of Qiskit classes (Qubit._index
) here: they are implementation-only details and very likely to change without warning. If you must get the qubit index, use QuantumCircuit.find_bit(qubit).index
.So, I have made a wrapper for many qc frameworks, and for my implementation to work, as I mentioned above I need to do it like so (I make the result_qc
differently, this was just to show the issue, I make calls to my own .U3
and .CX
methods).
So, is there a way to apply a global phase gate on the transpiled circuit to get my correct unitary matrix whilst having a global phase of 0? Or to perform the transpilation with a global phase of 0?
I can share my full code for you, but I doubt it'll be relevant as this is as I can understand just a global phase thing, not a code implementation issue thing. My package is basically a dedicated wrapper which allows for using all these different packages (currently qiskit, cirq, pennylane, and pytket) to be usable with a single syntax, and the user can convert back and forth between these frameworks.
There's a GlobalPhaseGate
in qiskit.circuit.library
that you can append
to a circuit (it takes 0 qubits and 0 clbits). I'd strongly suggest making sure you're always handling the global_phase
field of QuantumCircuit
properly, though - it's a core part of the QuantumCircuit
class. tket also has a Circuit.phase
property.
GlobalPhaseGate
isn't a special gate or anything, it's just a gate whose "definition" is the empty circuit with QuantumCircuit.global_phase
set to the right value. Any Qiskit optimisation or synthesis pass will remove it and replace it with a modification of the QuantumCircuit.global_phase
, since GlobalPhaseGate
is not in the instruction sets of hardware, so the most efficient "implementation" of it is just to take a note of what it should be, then do nothing.
Technically, I could do everything with a global phase of 0 right? Meaning, could I just add a GlobalPhaseGate
to get the correct qc that implements the unitary with a global phase of 0.
By the way, if it's actually nothing, then why does transpile change it, and why does it impact the overall unitary?
So for clarity, I would like to transpile the unitary to a circuit which would have a global phase of 0, and when I extract the unitary, it would be basically the same.
Yes, you could do result_qc.append(GlobalPhaseGate(transpiled_qc.global_phase), (), ())
and it'd be the same as setting result_qc.global_phase = transpiled_qc.global_phase
.
If you want the matrix forms to match exactly, you have to ensure that you include the global-phase factor in them as well. A global phase is not observable under physical measurements (${\bigl\lvert \langle\psi\rvert U\lvert\phi\rangle \bigr\rvert}^2$ is invariant under the map $U \to e^{i\theta}U$ for real scalar $\theta$) but if you want to examine the raw matrix elements, you can see a difference. The transpiler tracks any global-phase difference that its synthesis induces in the output specifically so that you can account for it if you want to retrieve the matrices afterwards.
You mean inverse of GlobalPhaseGate
right? I want to set the global phase to 0, and ensure the unitary is still correct. So, I want the result_qc.global_phase
to be 0, and when I call Operator(result_qc).data
to get the initial unitary as expected.
This will allow me to ignore global phase as all the other gates (X, Y, Z, RX, RY, RZ, H, S, T, SWAP, and their controlled and multi-controlled variations) don't change the global phase, so it's only being changed here because of transpile.
No, you'd need the explicit gate to advance result_qc.global_phase
by the same amount as transpiled_qc.global_phase
if you want the matrices to match (whether explicitly by modifying the property, or implicitly by a GlobalPhaseGate
), and both GlobalPhaseGate
and QuantumCircuit.global_phase
represent an additive phase, so there's no inverse.
I'm not sure what you mean by "other gates don't change the global phase". Global phase is only a relative thing. Qiskit's matrix definition of $R_z(\theta)$ differs from $P(\theta)$ by a phase term $e^{i\theta/2}$, so, for example, if transpile
is given a circuit containing $P$ gates and the backend only claims to support $R_z$ gates, then we can make a one-to-one translation from $P$ to $R_z$, but we then track the induced difference in global phase in QuantumCircuit.global_phase
. The same thing is happening in your circuit during the unitary synthesis; we happened to synthesise your matrix in an efficient way using the ${U_3,CX}$ basis you asked for, including a global-phase term. It would be wasteful synthesis for us to insert additional gates to make the global phase exactly equal when it's unobservable (and for certain gate sets, would potentially be impossible), so we don't, and we just store the factor out-of-band. Things like Operator
will (as you've found) read that off correctly when building a matrix representation, and any handler of QuantumCircuit
should do the same, if it cares about it.
I see, so I should make global phase in all of the frameworks, i.e., TKET, Pennylane, and cirq then?
They will also all have some representation of global phase, if they have can preserve the exact matrix definitions of unitaries. If there's any framework you're targetting that only defines circuits up-to-phase (which is typically allowed for circuits representing only complete physical programs), then the global phase is irrelevant and unobservable.
I see. At the risk of sounding stupid, could I implement the global phase difference using RZ gate or P gate applied on all qubits? The reason I ask is because I am not sure what GlobalPhaseGate
actually is, hence I can't find the equivalents of the gate in the other frameworks, i.e., TKET, Pennylane and cirq.
In Cirq it's called GlobalPhaseGate
like us, and in tket it's a method on Circuit
called add_phase
which (I think - I'm not sure) is equivalent in spirit to qc.global_phase += phase
in Qiskit, i.e. there's no associated Op
. PennyLane appears to call it GlobalPhase
.
Applying $P$ or $R_z$ on all qubits isn't the same as a global phase; those gates advance the $\lvert1\rangle$ state relative to the $\lvert0\rangle$ state for the targeted qubit.
Oh, but if you want, you can apply a global phase by doing a $P(\theta)$ followed by an $R_z(-\theta)$ on the same qubit. That unnecessarily targets a qubit (global phase is equivalent to the identity operation on all qubits up to phase(!), so doesn't need an explicit target). Since $P(\theta) = e^{i\theta/2}R_z(\theta)$, $R_z(-\theta)P(\theta)$ advances the global phase by $\theta/2$ but is locally equivalent to the identity (i.e. it's equal to $e^{i\theta/2}I$).
I'd recommend against doing that though, because any simulation or unitary synthesis would have to either do two unnecessary matrix multiplications to apply a phase like that (rather than a simple multiplication by a scalar) or otherwise re-simplify the expression back to the identity up-to-phase.
Thank you so so much! I'll test them out now, and see if the issue persists.
Quick question, in Cirq it only allows for a value between 0 to 1. How would the phase value from for instance qiskit translate to cirq?
I'm not familiar with Cirq, but the global phase is generally understood as an angle (including in Qiskit) with a period of $2\pi$. The other thing Cirq might be doing is storing the value as the complex number $e^{i\theta}$ (which is what you actually multiply the matrices by), whereas Qiskit stores the angle $\theta$ directly (which is faster and more precise to update and for humans to interpret).
I see. Quick question, in operations like adjoint of a circuit (where we reverse the order of the gates, and flip the sign of the angles), would global phase also be flipped? I apologize if this is a stupid question, still trying to fully understand the details.
Yes, phase would be inverted too.
Since there's no problem with Qiskit here, I'm going to close this issue now. It might be better to ask the more mathematical on the Qiskit slack, where there are more people are to help.
Environment
What is happening?
So, I am trying to make a function where the user would pass a unitary matrix, and the function would return the circuit that implements the unitary matrix. This is for a package I am creating, so I have to do so as follows: 1) Define a circuit, and extract its unitary (this is the circuit we are trying to implement). 2) Define a new circuit, which transpiles the unitary into a circuit. 3) Define another circuit, which applies the gates from (2) using .data attribute from circuit (2).
So, the issue is that apparently the resulting circuit from (3), whilst being the same circuit as (2), returns a vastly different unitary matrix, ergo not approximating the result correctly. I imagine this is being caused from the global phase difference, though even as I am writing this, it feels wrong to say given phase does not impact the unitary matrix (circuit definition) itself.
I would immensely appreciate getting this code to run as expected as it is a crucial feature for my package for the time being until I implement a unitary synthesis algorithm from scratch myself.
How can we reproduce the issue?
What should happen?
I expect the
result_qc
to return the exact same matrix astranspiled_qc
, given both are the same circuits. I also believe even if the global phase difference is not the source of the problem, the resulting circuit should not have a different global phase.Any suggestions?
UPDATE : I manually set the
transpiled_qc
which returns the correct result to have a global phase of 0, and was able to reproduce the issue seen inresult_qc
, hence I think if there's a way to do the initial transpilation fortranspiled_qc
with a global phase of 0, it should be fixed.