QDCS Lab Session - Coding Grover¶

This is only for practice.¶

But first, some libraries to load (nothing to modify here)

In [1]:
! python -m pip install qiskit qiskit-aer
Requirement already satisfied: qiskit in /home/benoit/qiskit-env/lib/python3.13/site-packages (2.2.1)
Requirement already satisfied: qiskit-aer in /home/benoit/qiskit-env/lib/python3.13/site-packages (0.17.2)
Requirement already satisfied: rustworkx>=0.15.0 in /home/benoit/qiskit-env/lib/python3.13/site-packages (from qiskit) (0.17.1)
Requirement already satisfied: numpy<3,>=1.17 in /home/benoit/qiskit-env/lib/python3.13/site-packages (from qiskit) (1.26.4)
Requirement already satisfied: scipy>=1.5 in /home/benoit/qiskit-env/lib/python3.13/site-packages (from qiskit) (1.16.2)
Requirement already satisfied: dill>=0.3 in /home/benoit/qiskit-env/lib/python3.13/site-packages (from qiskit) (0.4.0)
Requirement already satisfied: stevedore>=3.0.0 in /home/benoit/qiskit-env/lib/python3.13/site-packages (from qiskit) (5.5.0)
Requirement already satisfied: typing-extensions in /home/benoit/qiskit-env/lib/python3.13/site-packages (from qiskit) (4.15.0)
Requirement already satisfied: psutil>=5 in /home/benoit/qiskit-env/lib/python3.13/site-packages (from qiskit-aer) (7.1.0)
Requirement already satisfied: python-dateutil>=2.8.0 in /home/benoit/qiskit-env/lib/python3.13/site-packages (from qiskit-aer) (2.9.0.post0)
Requirement already satisfied: six>=1.5 in /home/benoit/qiskit-env/lib/python3.13/site-packages (from python-dateutil>=2.8.0->qiskit-aer) (1.17.0)
In [2]:
import numpy as np
from math import pi, gcd
from qiskit import *
from qiskit_aer import AerSimulator, StatevectorSimulator

def processOneState(st): # Longueur = puissance de 2
        s = list(st)
        if len(s) == 2:
            return {'0' : s[0], '1' : s[1]}
        else:
            a0 = processOneState(s[:len(s)//2])
            a1 = processOneState(s[len(s)//2:])
            r = {}
            for k in a0:
                r['0' + k] = a0[k]
            for k in a1:
                r['1' + k] = a1[k]
            return r

def printOneState(d): # get a dict as per processStates output
    for k in d:
        im = d[k].imag
        re = d[k].real
        if abs(im) >= 0.001 or abs(re) >= 0.001:
            print("% .3f + % .3fj |%s>" % (re,im,k))

def printFinalRes(result):
    printOneState(processOneState(list(np.asarray(result))))


def runStateVector(qc):
    simulator = StatevectorSimulator()
    job = simulator.run(qc.decompose(reps=6), memory=True)
    job_result = job.result()
    result = job_result.results[0].to_dict()['data']['statevector']
    printFinalRes(result)

def runStateVectorSeveralTimes(qc, howmany):
    qc.save_statevector(label = 'collect', pershot = True)
    simulator = StatevectorSimulator()
    job = simulator.run(qc.decompose(reps=6), memory=True, shots=howmany)
    result = job.result()
    memory = result.data(0)['memory']
    collect = result.data(0)['collect']
    r = {}
    for i in range(len(collect)):
        r[str(collect[i])] = (0, collect[i])
    for i in range(len(collect)):
        n, v = r[str(collect[i])]
        r[str(collect[i])] = (n+1, v)
    for k in r:
        i, v = r[k]
        print(f"With {i} occurences:")
        printFinalRes(v)

def runSample(qc,howmany):
    simulator = AerSimulator()
    job = simulator.run(qc.decompose(reps=6), shots=howmany)
    res = dict(job.result().get_counts(qc))
    return res

0 - Introduction¶

In this exercice, we are going to experimentally verify the amplitude amplification coming from Grover algorithm. We shall test the algorithm on a function over N variables: $$f(x_1,\ldots,x_n) = (x_1\wedge \ldots \wedge x_n)$$ The set $f^{-1}\{1\}$ is of size $1$.

1 - Build the circuit TODO¶

Complete the missing bits in the following code. You might find useful the gate qc.mcx(controls, target). For instance

qc.mcx(q[1:], q[0])

place a multi-controlled NOT gate on q[0] with all the other wires of q as controls.

In [3]:
N = 5 # Number of variables of f


def oracle(qc,q,aux):
    pass # TODO !!!!

def u0bot(qc,q):
    pass # TODO !!!!!

def grover(n_iter):
    q = QuantumRegister(N)    # The variables in superposition
    c = ClassicalRegister(N)  # Registers to store their measure
    aux = QuantumRegister(1)  # to store the additional wire used for O. Note that there is no need to measure it !
    qc = QuantumCircuit(q,aux,c)

    # TODO initialize the circuit
    
    for _ in range(n_iter):
        qc.barrier()       # To physically separate each iteration (does nothing but renders the circuit more legible)
        # TODO

    qc.measure(q,c)        # Measure
    return qc


print(grover(2))   # Show the circuit for 2 iterations
       ░  ░ ┌─┐            
q0_0: ─░──░─┤M├────────────
       ░  ░ └╥┘┌─┐         
q0_1: ─░──░──╫─┤M├─────────
       ░  ░  ║ └╥┘┌─┐      
q0_2: ─░──░──╫──╫─┤M├──────
       ░  ░  ║  ║ └╥┘┌─┐   
q0_3: ─░──░──╫──╫──╫─┤M├───
       ░  ░  ║  ║  ║ └╥┘┌─┐
q0_4: ─░──░──╫──╫──╫──╫─┤M├
       ░  ░  ║  ║  ║  ║ └╥┘
  q1: ─░──░──╫──╫──╫──╫──╫─
       ░  ░  ║  ║  ║  ║  ║ 
c0: 5/═══════╩══╩══╩══╩══╩═
             0  1  2  3  4 

2 - Run the circuit TODO¶

What does the following code do ?

In [4]:
for i in range(15):
    s = runSample(grover(i),1000)
    k=""
    for _ in range(N):
        k += "1"
    if k in s:
        print(i, s[k])
    else:
        print(i, 0)
0 0
1 0
2 0
3 0
4 0
5 0
6 0
7 0
8 0
9 0
10 0
11 0
12 0
13 0
14 0

3 - Discussion TODO¶

  • Can you explain the result ? Is there any regularity ? Why ? When is the maximum value obtained ? Why ?
  • Change the value of N to 8: Can you explain the changes?