Implementing a basic correction error algorithm

I am trying to implement a code correction error in jupyter notebook using Pennylane. I create the superposition alpha|000> + beta|111>

I rotate the state to apply a weak error

I measure Z1@Z2 and Z1@Z3 to check for errors and fix them using an X gate.

Then I try to plot the results but it appears like nothing is being fixed why is that?

# Importing necessary libraries
import pennylane as qml
import numpy as np
import matplotlib.pyplot as plt
import random

def plot_probs(probs, description=""):
    states = [f"|{i:03b}⟩" for i in range(8)]
    plt.bar(states, probs)
    plt.xlabel('States')
    plt.ylabel('Probability')
    plt.title(f'Probability distribution after {description}')
    plt.show()
    # Print the probabilities
    print(f"Probabilities of states after {description}:")
    for i, p in enumerate(probs):
        print(f"State |{i:03b}⟩: {p:.4f}")
    print("\n")
    
# Define the number of qubits
n_qubits = 3

# Initialize a device
dev = qml.device('default.qubit', wires=n_qubits, shots=1000)

# Define the initial state preparation function
def initial_state(alpha, beta):
    qml.RY(2 * np.arccos(alpha), wires=0)
    qml.CNOT(wires=[0, 1])
    qml.CNOT(wires=[0, 2])

# Define the weak error operation
def apply_weak_error():
    qubit_to_flip = random.randint(0, 2)
    epsilon = np.pi / 10
    qml.RX(epsilon, wires=qubit_to_flip)


@qml.qnode(dev)
def circuit1(alpha, beta):
    initial_state(alpha, beta)
    apply_weak_error()
    
    return qml.probs(wires=[0, 1, 2])

# Define the circuit with error correction
@qml.qnode(dev)
def circuit2(alpha, beta):
    initial_state(alpha, beta)
    apply_weak_error()
    
    # Measure the stabilizers
    Z1Z2 = qml.sample(qml.PauliZ(0) @ qml.PauliZ(1))
    Z1Z3 = qml.sample(qml.PauliZ(0) @ qml.PauliZ(2))
    
    # Define error correction based on the stabilizer measurements
    if Z1Z2 == 1 and Z1Z3 == 1:
        pass
    elif Z1Z2 == -1 and Z1Z3 == -1:
        qml.PauliX(wires=0)
    elif Z1Z2 == -1 and Z1Z3 == 1:
        qml.PauliX(wires=1)
    elif Z1Z3 == 1 and Z1Z2 == -1:
        qml.PauliX(wires=2)
    
    # Measure the corrected state after error correction
    corrected_state = qml.probs(wires=[0, 1, 2])
    
    # Return stabilizer measurements and corrected state
    return Z1Z2, Z1Z3, corrected_state

# Parameters for the state |ψ⟩ = α|000⟩ + β|111⟩
alpha = 0.4
beta = np.sqrt(1 - alpha ** 2)

probs_error = circuit1(alpha, beta)
plot_probs(probs_error, description="weak error")

# Run the circuit with error correction
Z1Z2, Z1Z3, corrected_state = circuit2(alpha, beta)
plot_probs(corrected_state, "Corrected State After Error Correction")

Hi @justsme I’m not sure what’s going on but let me check and get back to you.

In the meantime, you might also like exploring FlamingPy, our library designed for simulations of error correction!

Hi @justsme ,

I used simple print statements and it looks like you’re never matching the conditions in your elif statements. Are you sure they’re set up correctly?

# Define error correction based on the stabilizer measurements
    if Z1Z2 == 1 and Z1Z3 == 1:
        print('in 1')
        pass
    elif Z1Z2 == -1 and Z1Z3 == -1:
        print('in 2')
        qml.PauliX(wires=0)
    elif Z1Z2 == -1 and Z1Z3 == 1:
        print('in 3')
        qml.PauliX(wires=1)
    elif Z1Z3 == 1 and Z1Z2 == -1:
        print('in 4')
        qml.PauliX(wires=2)
1 Like

Hi @justme,

I think that one issue is the following two lines:

Z1Z2 = qml.sample(qml.PauliZ(0) @ qml.PauliZ(1))
Z1Z3 = qml.sample(qml.PauliZ(0) @ qml.PauliZ(2))

The two lines of code occur about halfway inside your quantum function called circuit2. And, if you print out the values of Z1Z2 and Z1Z3, you get:
Z1Z2=sample(Z(0) @ Z(1))
Z1Z3=sample(Z(0) @ Z(2))

Usually, qml.sample is placed at the end of a quantum function. It’s something that a quantum function can return. E.g.,

def my_simple_quantum_function():
      return qml.sample(qml.PauliX(0))

To make a measurement partway through a quantum function, and then perform quantum operations based on its result, I think you have to use the following:

qml.measure (to do a mid-circuit measurement)
qml.cond (to perform a quantum operation based on the values of measurement results)

I think you’ll also need to add some extra “ancilla” qubits to implement the error correction.

Hope this helps!

Best

Damian

2 Likes

I will try with your suggestions, Thanks a lot for your exhaustive response.

Great catch @Damian_Pope! :raised_hands:

Thanks @justsme. Good luck!

I think that one way of implementing the error-correction scheme in PL is as follows:

Note: I’m assuming that you’re trying to implement the well-known three-qubit “bitflip” scheme that’s detailed in Section IV of this paper: https://arxiv.org/abs/0905.2794

*create a dev with 2 more qubits (i.e., two ancilla qubits).
(These will be used to detect and then correct the type of error that occurs, i.e., the error syndrome)

*After creating the logical state and implementing a weak error, determine the values of Z1Z2 and Z1Z3 as follows:

Z1Z2:
qml.CNOT(0,3)
qml.CNOT(1,3)

Z1Z3:
qml.CNOT(0,4)
qml.CNOT(2,4)

(This is just implementing the last 4 CNOTs shown in Figure 3 of Section IV of the paper.)

*Perform mid-circuit measurements on qubits 3 and 4. This measures the values of Z1Z2 and Z1Z3.
qml.measure(3)
qml.measure(4)

*The values of qubits 3 and 4 in the computational basis now store the measured values of the two stabilizers. Qubit 3 stores the value of Z1Z2 and qubit 4 stores the value of Z1Z3.

So, now we just need to follow Table 1 in Section IV of the paper and implement a Pauli X gate on the appropriate qubit (i.e, 0, 1 or 2) to correct the error that we’ve diagnosed:

Case 1: Qubit 3 and Qubit 4 both in |1> [Error on qubit 0]
qml.Toffoli([3,4,0])

Case 2: Qubit 3 in |0> and Qubit 4 in |1> [Error on qubit 2]
qml.multiControlledX(wires=[3,4,2],control_values=[0,1])

Case 3: Qubit 3 in |1> and Qubit 4 in |0> [Error on qubit 1]
qml.multiControlledX(wires=[3,4,1],control_values=[1,0])

Hope this helps! Good luck!

Hi @Damian_Pope , thanks for the detailed answer!

Another (maybe easier) way of correcting the errors is to apply PauliX gates depending on your results of the mid-circuit measurement, as per Table II in section IV of the paper.

Both ways are correct. It’s good to see that there’s more than one solution possible!

1 Like

Thanks @CatalinaAlbornoz! Always great to see a different & better way of solving a problem.

1 Like