Mitiq paper codeblocks

Codeblocks from the main text of the Mitiq paper [1].

Codeblock 1

# !pip install mitiq --quiet

Codeblock 2

import mitiq


mitiq.about()
Mitiq: A Python toolkit for implementing error mitigation on quantum computers
==============================================================================
Authored by: Mitiq team, 2020 & later (https://github.com/unitaryfund/mitiq)

Mitiq Version:	0.11.0dev

Core Dependencies
-----------------
Cirq Version:	0.10.0
NumPy Version:	1.20.3
SciPy Version:	1.7.1

Optional Dependencies
---------------------
PyQuil Version:	2.28.2
Qiskit Version:	None
Braket Version:	1.9.1

Python Version:	3.7.9
Platform Info:	Linux (x86_64)

Codeblock 4

Note: The paper just shows the signature of an executor function, but here we explicitly define one to use in examples.

import cirq
from mitiq.interface import accept_any_qprogram_as_input


@accept_any_qprogram_as_input
def executor(circuit: mitiq.QPROGRAM) -> float:
    return cirq.DensityMatrixSimulator().simulate(
        circuit.with_noise(cirq.depolarize(p=0.01))
    ).final_density_matrix[0, 0].real

Codeblock 5

from mitiq import zne


circuit = cirq.Circuit([cirq.X.on(cirq.LineQubit(0))] * 50)

zne_value = zne.execute_with_zne(circuit, executor)
print("ZNE value:", zne_value)
ZNE value: 0.9415782690048214

Codeblock 6

Note: The paper shows pseudocode; here we show an example.

zne_value = zne.execute_with_zne(
    circuit,
    executor,
    scale_noise=zne.scaling.fold_global,
    factory=zne.inference.ExpFactory(scale_factors=[1.0, 3.0, 5.0, 7.0, 9.0])
)
print("ZNE value:", zne_value)
ZNE value: 0.9999961467128782

Codeblock 7

Note: The paper shows pseudocode; here we show an example.

from mitiq import pec

representation = pec.represent_operation_with_local_depolarizing_noise(
    ideal_operation=cirq.Circuit(circuit[0]), noise_level=0.01
)
print("Representation of ideal operation:", representation, sep="\n\n")

pec_value = pec.execute_with_pec(
    circuit,
    executor,
    representations=[representation],
    num_samples=10,  # Remove argument or increase for better accuracy.
)
print("\n\nPEC value:", pec_value)
Representation of ideal operation:

0: ───X─── = 1.010*(0: ───X───)-0.003*(0: ───X───X───)-0.003*(0: ───X───Y───)-0.003*(0: ───X───Z───)
PEC value: 0.9627989122880454

Codeblock 8

qreg = cirq.LineQubit.range(2)
circ = cirq.Circuit(
    cirq.ops.H.on(qreg[0]),
    cirq.ops.CNOT.on(qreg[0] , qreg[1])
)
print("Original circuit:", circ, sep="\n")
Original circuit:
0: ───H───@───
          │
1: ───────X───

Codeblock 9

folded = zne.scaling.fold_gates_from_left(
    circ, scale_factor=2
)
print("Folded circuit:", folded, sep="\n")
Folded circuit:
0: ───H───H───H───@───
                  │
1: ───────────────X───

Codeblock 10

folded = zne.scaling.fold_gates_from_right(
    circ, scale_factor=2
)
print("Folded circuit:", folded, sep="\n")
Folded circuit:
0: ───H───@───@───@───
          │   │   │
1: ───────X───X───X───

Codeblock 11

folded = zne.scaling.fold_global(circ, scale_factor=3.)
print("Folded circuit:", folded, sep="\n")
Folded circuit:
0: ───H───@───@───H───H───@───
          │   │           │
1: ───────X───X───────────X───

Codeblock 12

Note: The paper shows pseudocode; here we show an example.

q = cirq.LineQubit(0)
circuit2 = cirq.Circuit(cirq.X.on(q) ** (3 / 5), cirq.Z.on(q) ** (4 / 5))
print("Circuit:", circuit2, sep="\n")

scaled = zne.scaling.scale_parameters(circuit2, scale_factor=2.0, base_variance=0.01)
print("\nScaled circuit:", scaled, sep="\n")
Circuit:
0: ───X^0.6───Z^0.8───

Scaled circuit:
0: ───X^0.579───Z^0.782───

Codeblock 13

zne_value = zne.execute_with_zne(
    circuit,
    executor,
    scale_noise=zne.scaling.fold_global,
)
print("ZNE value:", zne_value)
ZNE value: 0.9415782690048214

Codeblock 14

linear_factory = zne.inference.LinearFactory(scale_factors=[1.0, 2.0, 3.0])

Codeblock 15

zne_value = zne.execute_with_zne(
    circuit,
    executor,
    factory=linear_factory,
)
print("ZNE value:", zne_value)
ZNE value: 0.8397777080535891

Codeblock 16

zne_value = zne.execute_with_zne(
    circuit,
    executor,
    factory=zne.inference.PolyFactory(
        scale_factors=[1.0, 2.0, 3.0], order=2
    ),
)
print("ZNE value:", zne_value)
ZNE value: 0.9415782690048214

Codeblock 17

zne_value = zne.execute_with_zne(
    circuit,
    executor,
    factory=zne.inference.AdaExpFactory(
        scale_factor=2.0, steps=5
    ),
)
print("ZNE value:", zne_value)
ZNE value: 0.9998500777120343

Codeblock 18

from mitiq.zne.inference import BatchedFactory, PolyFactory
import numpy as np


class MyFactory(BatchedFactory):
    @staticmethod
    def extrapolate(scale_factors, exp_values, full_output):
        zne_result, *extras = PolyFactory.extrapolate(
            scale_factors, exp_values, order=2, full_output=True
        )
        zne_result = np.clip(zne_result, -1.0, 1.0)
        return zne_result if not full_output else (zne_result, *extras)


# Example usage.
zne_value = zne.execute_with_zne(
    circuit,
    executor,
    factory=MyFactory(scale_factors=[1, 3, 5])
)
print("ZNE value:", zne_value)
ZNE value: 0.9022606164216984

Codeblock 19

from mitiq import pec


noisy_x = pec.NoisyOperation(
    circuit=cirq.Circuit(cirq.X.on(q)),
    channel_matrix=np.random.randn(4, 4),
)

noisy_z = pec.NoisyOperation(
    circuit=cirq.Circuit(cirq.Z.on(q)),
    channel_matrix=np.random.randn(4, 4),
)

Codeblock 20 & 21

h_rep = pec.OperationRepresentation(
    ideal=cirq.Circuit(cirq.H.on(q)),
    basis_expansion = {noisy_x: 0.52, noisy_z: -0.48}
)

Codeblock 22

noisy_op, sign, coeff = h_rep.sample()

Codeblock 23

circuit3 = cirq.Circuit([cirq.H.on(q)] * 5)

sampled, sign, norm = pec.sample_circuit(circuit3, representations=[h_rep])
print("Sampled circuit:", sampled, sep="\n")  # Run many times to see different sampled circuits!
Sampled circuit:
[cirq.Circuit([
    cirq.Moment(
        cirq.Z(cirq.LineQubit(0)),
    ),
    cirq.Moment(
        cirq.X(cirq.LineQubit(0)),
    ),
    cirq.Moment(
        cirq.X(cirq.LineQubit(0)),
    ),
    cirq.Moment(
        cirq.Z(cirq.LineQubit(0)),
    ),
    cirq.Moment(
        cirq.Z(cirq.LineQubit(0)),
    ),
])]

Codeblock 24 & 25

See https://mitiq.readthedocs.io/en/stable/examples/cdr_api.html.

Codeblock 26

mitigated_executor = zne.mitigate_executor(
    executor, scale_noise=zne.scaling.fold_global, factory=zne.inference.ExpFactory(scale_factors=[1, 3, 5, 7])
)
zne_value = mitigated_executor(circuit)
print("ZNE value:", zne_value)
ZNE value: 0.9999972208991856

Codeblock 27

@zne.zne_decorator(factory=zne.inference.ExpFactory(scale_factors=[1, 3, 5, 7]), scale_noise=zne.scaling.fold_gates_at_random)
def execute(circuit: cirq.Circuit) -> float:
    return cirq.DensityMatrixSimulator().simulate(
        circuit.with_noise(cirq.depolarize(p=0.01))
    ).final_density_matrix[0, 0].real


zne_value = execute(circuit)
print("ZNE value:", zne_value)
ZNE value: 0.9999972208991856

Codeblock 28

@pec.pec_decorator(representations=[h_rep], num_samples=10)
@zne.zne_decorator(
    factory=zne.inference.ExpFactory(scale_factors=[1, 3, 5, 7]),
    scale_noise=zne.scaling.fold_gates_at_random
)
def execute(circuit: cirq.Circuit) -> float:
    return cirq.DensityMatrixSimulator().simulate(
        circuit.with_noise(cirq.depolarize(p=0.01))
    ).final_density_matrix[0, 0].real


zne_then_pec_value = execute(circuit3)
print("ZNE then PEC value:", zne_then_pec_value)  # Note this is not accurate (bad representation).
ZNE then PEC value: -0.5000000668528946

Codeblock 29

!pip install qiskit==0.24.0 --quiet
import qiskit


provider = qiskit.BasicAer  # Use of a simulator as backend.
# provider = qiskit.IBMQ.load_account()  # Alternative way to run the blocks, with saved credentials.

def execute(
    circuit: qiskit.QuantumCircuit,
    backend_name: str = "qasm_simulator",
    shots: int = 1024
) -> float:
    job = qiskit.execute(
        experiments=circuit,
        backend=provider.get_backend(backend_name),
        optimization_level=0,
        shots=shots,
    )

    counts = job.result().get_counts()
    return counts.get("00", 0.0) / shots
# Example usage.
qreg = qiskit.QuantumRegister(2)
creg = qiskit.ClassicalRegister(2)
circ = qiskit.QuantumCircuit(qreg, creg)
circ.h(qreg[0])
circ.cx(*qreg)
circ.measure(qreg, creg)
print("Circuit:")
print(circ)

execute(circ)
Circuit:
      ┌───┐     ┌─┐   
q0_0: ┤ H ├──■──┤M├───
      └───┘┌─┴─┐└╥┘┌─┐
q0_1: ─────┤ X ├─╫─┤M├
           └───┘ ║ └╥┘
c0: 2/═══════════╩══╩═
                 0  1 
0.4775390625

References

[1] Mitiq: A software package for error mitigation on noisy quantum computers, Ryan LaRose, Andrea Mari, Sarah Kaiser, Peter J. Karalekas, Andre A. Alves, Piotr Czarnik, Mohamed El Mandouh, Max H. Gordon, Yousef Hindy, Aaron Robertson, Purva Thakre, Nathan Shammah, and William J. Zeng, https://arxiv.org/abs/2009.04417