!pip install -U -r grading_tools/requirements.txt

from IPython.display import clear_output
clear_output()

from qiskit.quantum_info import Operator
from qiskit import QuantumCircuit
import numpy as np

def phase_oracle(n, indices_to_mark, name = 'Oracle'):
    
    # create a quantum circuit on n qubits
    qc = QuantumCircuit(n, name=name)

    ### WRITE YOUR CODE BETWEEN THESE LINES - START
    for qubits in range(n):
        qc.h(qubits)
        
    for index, value in enumerate(indices_to_mark):
        if index == 0:
            for qubits in range(1, n):
                qc.x(qubits)

            qc.cz()
    
            for qubits in range(1, n):
                qc.x(qubits)
                
            oracle_matrix = qc.diagonal
                
        if index == 1:
            for qubits in range(1, n, 2):
                qc.x(qubits)
    
            qc.cz()
    
            for qubits in range(1, n, 2):
                qc.x(qubits)
            
            oracle_matrix = qc.diagonal
    
    ### WRITE YOUR CODE BETWEEN THESE LINES - END

    # convert your matrix (called oracle_matrix) into an operator, and add it to the quantum circuit
    qc.unitary(Operator(oracle_matrix), range(n))
    
    return qc

def diffuser(n):
    
    # create a quantum circuit on n qubits
    qc = QuantumCircuit(n, name='Diffuser')
    
    ### WRITE YOUR CODE BETWEEN THESE LINES - START
    
    
    ### WRITE YOUR CODE BETWEEN THESE LINES - END
    
    return qc

def Grover(n, indices_of_marked_elements):
    
    # Create a quantum circuit on n qubits
    qc = QuantumCircuit(n, n)
    
    # Determine r
    r = int(np.floor(np.pi/4*np.sqrt(2**n/len(indices_of_marked_elements))))
    print(f'{n} qubits, basis states {indices_of_marked_elements} marked, {r} rounds')
    
    # step 1: apply Hadamard gates on all qubits
    qc.h(range(n))
    
    # step 2: apply r rounds of the phase oracle and the diffuser
    for _ in range(r):
        qc.append(phase_oracle(n, indices_of_marked_elements), range(n))
        qc.append(diffuser(n), range(n))
        
    # step 3: measure all qubits
    qc.measure(range(n), range(n))
  
    return qc

mycircuit = Grover(6, [1, 42])
mycircuit.draw(output='text')

from qiskit import Aer, execute
simulator = Aer.get_backend('qasm_simulator')
counts = execute(mycircuit, backend=simulator, shots=1000).result().get_counts(mycircuit)
from qiskit.visualization import plot_histogram
plot_histogram(counts)

name = 'Saasha Joshi'
email = 'saashajoshi08@gmail.com'

### Do not change the lines below
from qiskit import transpile
mycircuit_t = transpile(mycircuit, basis_gates=['u1', 'u2', 'u3', 'cx'], optimization_level=0)
from grading_tools import grade
grade(answer=mycircuit_t, name=name, email=email, labid='lab2', exerciseid='ex1')