import numpy as np
import qiskit
from qiskit import QuantumCircuit
import matplotlib.pyplot as plt

from qiskit.circuit import QuantumCircuit, Parameter

import warnings
warnings.filterwarnings('ignore')

theta = Parameter("θ")
phi = Parameter("φ")
lamb = Parameter("λ")

def sampleCircuitA(layer=1, qubits=4):
    circuit = QuantumCircuit(qubits)
    
    for i in range(layer):
        for i in range(qubits - 1):
            circuit.cx(i, i + 1)
        circuit.cx(qubits - 1, 0)
        
        for i in range(qubits - 1):
            circuit.cx(i, i + 1)
        circuit.cx(qubits - 1, 0)
        
        circuit.barrier()
        
        for i in range(qubits):
            circuit.u3(theta, phi, lamb, i)

    return circuit

circuitA = sampleCircuitA(qubits=4)
circuitA.draw(output='mpl')

def sampleCircuitB1(layer=1, qubits=4):
    circuit = QuantumCircuit(qubits)
    
    for i in range(qubits):
        circuit.u1(theta, i)
    
    for i in range(layer):
        for j in range(qubits - 1):
            circuit.cz(j, j + 1)
        circuit.cz(qubits - 1, 0)

        circuit.barrier()
        
        for j in range(qubits):
            circuit.u1(theta, j)

    return circuit

def sampleCircuitB2(layer=1, qubits=4):
    circuit = QuantumCircuit(qubits)
    
    for i in range(qubits):
        circuit.u2(phi, lamb, i)
    
    for i in range(layer):
        for j in range(qubits - 1):
            circuit.cz(j, j + 1)
        circuit.cz(qubits - 1, 0)
        
        circuit.barrier()
        
        
        for j in range(qubits):
            circuit.u2(phi, lamb, j)

    return circuit

def sampleCircuitB3(layer=1, qubits=4):
    circuit = QuantumCircuit(qubits)
    
    for i in range(qubits):
        circuit.u3(theta, phi, lamb, i)
    
    for i in range(layer):
        for j in range(qubits - 1):
            circuit.cz(j, j + 1)
        circuit.cz(qubits - 1, 0)
        
        circuit.barrier()
        
        for j in range(qubits):
            circuit.u3(theta, phi, lamb, j)
        
    return circuit

circuitB1 = sampleCircuitB1(qubits=4)
circuitB1.draw(output='mpl')

circuitB2 = sampleCircuitB2(qubits=4)
circuitB2.draw(output='mpl')

circuitB3 = sampleCircuitB3(qubits=4)
circuitB3.draw(output='mpl')

def sampleCircuitC(layer=1, qubits=4):
    circuit = QuantumCircuit(qubits)
    
    for i in range(layer):
        for j in range(qubits):
            circuit.ry(theta, j)
            
        circuit.crx(theta, qubits - 1, 0)
        for j in range(qubits - 1, 0, -1):
            circuit.crx(theta, j - 1, j)
        
        circuit.barrier()
        
        for j in range(qubits):
            circuit.ry(theta, j)
            
        circuit.crx(theta, 3, 2)
        circuit.crx(theta, 0, 3)
        circuit.crx(theta, 1, 0)
        circuit.crx(theta, 2, 1)
        
    return circuit

circuitC = sampleCircuitC(qubits=4)
circuitC.draw(output='mpl')

def sampleCircuitD(layer=1, qubits=4):
    circuit = QuantumCircuit(qubits)
    
    for i in range(layer):
        for j in range(qubits):
            circuit.rx(theta, j)
            circuit.rz(theta, j)
            
        for j in range(qubits - 1, -1, -1):
            for k in range(qubits - 1, -1, -1):
                if j != k:
                    circuit.crx(theta, j, k)
        circuit.barrier()
            
        for j in range(qubits):
            circuit.rx(theta, j)
            circuit.rz(theta, j)
        
    return circuit

circuitD = sampleCircuitD(qubits=4)
circuitD.draw(output='mpl')

def sampleCircuitE(layer=1, qubits=4):
    circuit = QuantumCircuit(qubits)

    for i in range(layer):
        for j in range(qubits):
            circuit.rx(theta, j)
            circuit.rz(theta, j)
            
        for j in range(1, qubits, 2):
            circuit.crx(theta, j, j - 1)
        
        for j in range(qubits):
            circuit.rx(theta, j)
            circuit.rz(theta, j)
        
        for j in range(2, qubits, 2):
            circuit.crx(theta, j, j - 1)
        
    return circuit

circuitE = sampleCircuitE(qubits=4)
circuitE.draw(output='mpl')

def sampleCircuitF(layer=1, qubits=4):
    circuit = QuantumCircuit(qubits)
    
    for i in range(layer):
        for j in range(qubits):
            circuit.rx(theta, j)
            circuit.rz(theta, j)
            
        circuit.crx(theta, qubits - 1, 0)
        for j in range(qubits - 1, 0, -1):
            circuit.crx(theta, j - 1, j)

    return circuit

circuitF = sampleCircuitF(qubits=4)
circuitF.draw(output='mpl')

def sampleEncoding(qubits):
    circuit = QuantumCircuit(qubits)
    
    for i in range(qubits):
        circuit.h(i)
        circuit.ry(theta, i)
        
    return circuit

circuit = sampleEncoding(4)
circuit.draw(output='mpl')

# demo:
circuit = sampleEncoding(5).compose(sampleCircuitB3(layer=2, qubits=5))
circuit.draw(output='mpl')

