from qiskit import *
from qiskit.tools.jupyter import *
from qiskit.visualization import *
import matplotlib.pyplot as plotter
import numpy as np
from IPython.display import display, Math, Latex
%matplotlib inline
# Loading your IBM Q account(s)
provider = IBMQ.load_account()

def run_circuit(qc):
    backend = Aer.get_backend('qasm_simulator') # we choose the simulator as our backend
    result = execute(qc, backend, shots = 200000).result() # we run the simulation
    counts = result.get_counts() # we get the counts
    return counts

# qc1 was already initialised to |1>|1>.
qc1.h(0)
# qc1 => (|0>|1> + |1>|1>)/sqrt(2)
qc1.cx(0,1)
# qc1 => (|0>|1> + |1>|0>)/sqrt(2)

#measure
qc1.measure([0,1],[0,1])
counts = run_circuit(qc1)
print(counts)
plot_histogram(counts)

qc_swap = QuantumCircuit(2,2)
qc_swap.x(0)
#initial state => |1>|0>

# applying swap
qc_swap.cx(0,1)
qc_swap.cx(1,0)
qc_swap.cx(0,1)  #Following from the circuit diagram.
# There is also qc_swap.swap(0,1)

# measure
qc_swap.measure([0,1],[0,1])
counts = run_circuit(qc_swap)
print(counts)
plot_histogram(counts)

qc5 = QuantumCircuit(2,2)
qc5.x(1)
# initial state is |0>|1> SO THAT WE KNOW WHAT BOTH |0> AND |1> MAP TO.
# A lot of people did a mistake here in not understanding why we are using |0> and |1>

#apply HZH on both qubits.
qc5.h([0,1])
qc5.z([0,1])
qc5.h([0,1])

# measure
qc5.measure([0,1],[0,1])

counts = run_circuit(qc5)
print(counts)
plot_histogram(counts)
# final state would be |1>|0> = X(|0>|1>)
# Hence they are equal (Actually not. We can only conclude that they agree only upto a global phase. I think now you can appreciate this now.)

qc6 = QuantumCircuit(3,3)
qc6.x(0) # Set any one qubit of the 2 swapping qubits to '1' to see a noticable difference between input and output.
qc6.initialize([1/np.sqrt(2), -complex(1,1)/2],2) # Optional. Initializing the 3rd qubit.

# convert 3rd qubit into |1>
qc6.tdg(2) 
qc6.h(2)
# OR
#qc6.t(2)
#qc6.s(2)
#qc6.h(2)
#qc6.x(2)
# You can check that they are equivalent.

# As you know now, it's a Fredkin gate.
qc6.toffoli(2,1,0)
qc6.toffoli(2,0,1)
qc6.toffoli(2,1,0)

#reset the third qubit to its intial state
qc6.h(2)
qc6.t(2)

# Measure
qc6.measure([0,1,2],[0,1,2])
counts = run_circuit(qc6)
print(counts)
plot_histogram(counts)