0
$\begingroup$

I'm trying to simulate the effect of a coherent unitary Z rotation at an arbitrary angle of a single qubit on the fidelity of a quantum circuit. I am using the qiskit Aer noisy simulator. More specifically, my goal is to quantify the loss of fidelity associated with the fact that when attempting to apply any gate with a unitary $U$, I apply instead the gate

$$ V = R_{i,z}^\dagger(\epsilon)UR_{i,z}(\epsilon) $$

where $i$ is an index of a single qubit.

As a sanity check, I am trying the single qubit case first, where I expect this error to not have any effect, since the initial state of the qubit is $|0\rangle$ and the measurement is in the Z basis. Thus, assuming that the coherent unitary channel acts on the state as $\mathcal{E}(\rho) = U\rho U^\dagger$, this error should get absorbed into the preperation and measurement and not have any effect on the fidelity.

When I simulate this circuit however, I find that the fidelity does go below 1 as I increase the error. It seems like the qiskit method coherent_unitary_error function from qiskit does not return a QuantumError object which has the effect of applying $\mathcal{E}(\rho)$ as defined above. Does that make sense?

I am including here a self-contained code sample which shows this behavior. One easy way to trigger this effect on and off is to change the optimization_level parameter in line 21 of the attached code. If it is 0, then the two Hadamards don't get merged into one and we see an error. If it is 1 or larger, they get merged and no error appears. Of course, manually adding the Z rotations to the circuit does not lead to an error.

Any ideas? Did I misunderstand how the noise model works, or is there a more fundamental issue here?

Thanks a lot in advance!!

import qiskit
from qiskit import QuantumCircuit, transpile
from qiskit.providers.aer import AerSimulator
from qiskit.providers.aer.noise import NoiseModel, coherent_unitary_error

epsilon = 0.1
z_rot = qiskit.circuit.library.U1Gate(epsilon)
z_rot_dg = qiskit.circuit.library.U1Gate(-epsilon)

noise_model = NoiseModel()
noise_model.add_all_qubit_quantum_error(coherent_unitary_error(z_rot), 'u2')
noise_model.add_all_qubit_quantum_error(coherent_unitary_error(z_rot), 'u3')
print(noise_model)
noise_sim = AerSimulator(noise_model=noise_model)

circ = QuantumCircuit(1, 1)
circ.h(0)
circ.h(0)
circ.measure(0, 0)

# change optimization level to 1 to turn off effect of channel
ct = transpile(circ, noise_sim, optimization_level=0) 
print(ct.draw())
nshots=10000
job = noise_sim.run(ct, shots=nshots)
print(job.result().get_counts()['0']/nshots)
```
$\endgroup$
2
  • $\begingroup$ The hadamards prepare the superpostion state $(|0\rangle + |1\rangle)/\sqrt{2}$, which is sensitive to $Z$ rotation error. $\endgroup$
    – forky40
    Aug 8 at 22:57
  • $\begingroup$ that's correct, but if I apply two Hadamards in a row as I did here, I expect to see only 1's in my measurement. This would be true for any kind of benchmark or characterization sequence, even when performing a single Hadamard. Also, the result shouldn't depend on the optimization level of the transpiler $\endgroup$
    – Lior
    Aug 8 at 23:01
1
$\begingroup$

after finding this: What are examples of Kraus operators describing the process of control error?

I think I understand the issue - I incorrectly assumed that the error operator is applied to the unitary and not to the state. Once I use the definition from the link above I get the correct result, here's an example:

import qiskit
from qiskit import QuantumCircuit, transpile
from qiskit.providers.aer import AerSimulator
from qiskit.providers.aer.noise import NoiseModel, coherent_unitary_error
import qiskit.quantum_info as qi
from qiskit.visualization import array_to_latex

epsilon = 0.1

err_h = QuantumCircuit(1, 1)
err_h.h(0)
err_h.p(epsilon, 0)
err_h.h(0)
err_h.p(-epsilon, 0)

u_err_h = qi.Operator(err_h)

noise_model = NoiseModel()
noise_model.add_all_qubit_quantum_error(coherent_unitary_error(u_err_h), 'h')
print(noise_model)
noise_sim = AerSimulator(noise_model=noise_model)

circ = QuantumCircuit(1, 1)
circ.h(0)
circ.h(0)
circ.measure(0, 0)

nshots = 10000
job = noise_sim.run(circ, shots=nshots)

job.result().get_counts()['0']/nshots
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.