from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector, Operator
from qiskit.visualization import plot_histogram

# let me consider an arbitrary quantum circuit with 4 qubits and 4 classical bits
qc = QuantumCircuit(4,4)
# I apply some initial single quantum gates 
# These gates are also arbitrary without a particular purpose
qc.h(range(4))
qc.barrier()
qc.draw('mpl',initial_state=True)

# To build a gate we define a quantum circuit without classical bit like below
gate1 = QuantumCircuit(2, name = 'gate1')
# Then we can use a bunch of the Qiskit gates in order to build an arbitrary gate.
gate1.cz(0,1)
gate1.x(0)
gate1.barrier()
gate1.h(1)
gate1.barrier()
gate1.cx(0,1)
# Make this circuit as a gate for qiskit
gate1.to_gate

gate1.draw('mpl')

# Now we can apply our gate on the qc circuit
qc.append(gate1,[0,1])
qc.append(gate1,[0,2])
qc.append(gate1,[1,3])
qc.append(gate1,[0,3])
display(qc.draw('mpl'),Statevector(qc).draw('latex'))

import numpy as np

# you can write any unitary gate that you need. In the following I am trying to build one
uni = np.zeros((4,4))

uni[0,0] = 1
uni[3,3] = 1
uni[1,2] = 1
uni[2,1] = 1
print(uni)
# test of being unitary
np.dot(np.transpose(uni),uni)

from qiskit.quantum_info import Operator

gate2 = Operator(uni)

qc.barrier()
qc.unitary(gate2,(2,3),label='gate2')
qc.draw('mpl')

for i in range(4):
    qc.measure(i,i)

qc.draw('mpl',cregbundle=False)

from qiskit import transpile
from qiskit.visualization import plot_histogram
from qiskit.providers.aer import QasmSimulator

backend = QasmSimulator()
job = transpile(qc,backend)
result = backend.run(job,shots=1024).result()
counts = result.get_counts()
plot_histogram(counts)

counts

