Quantum Gates

From cod3v

Introduction

Import the modules

from qiskit import QuantumCircuit, execute, Aer

from qiskit import QuantumRegister, ClassicalRegister #The newer version uses Registers

from qiskit.visualization import plot_histogram, plot_bloch_vector
from math import pi
a = 0
b = 1
t = 2


Plot the quantum circuit

qc.draw('mpl')
#qc.draw('latex')
#qc.draw('latex_source')

Plot the results

backend = Aer.get_backend('statevector_simulator') # Tell Qiskit how to simulate our circuit
qc.measure_all()
qc.draw()
result = execute(qc,backend).result()  #.get_statevector()  #.get_counts()
plot_bloch_multivector(result)

counts = result.get_counts()
plot_histogram(counts)

Theory

Quantum Gates of One Qubit

There are only two reversible gates, also identity (return the input unchanged) and NOT (return the opposite of the input), but neither is universal.

Identity gate.

Pauli X gate.

Pauli Y gate

Pauli Z gate

Hadamard gate

R gate

S gate or gate

T gate

U1 gate:

U2 gate:

qc = QuantumCircuit(1)
qc.x(0)
#qc.y(0) # Y-gate on qubit 0
#qc.z(0) # Z-gate on qubit 0
#qc.rz(pi/4, 0)
#qc.s(0)   # Apply S-gate to qubit 0
#qc.sdg(0) # Apply Sdg-gate to qubit 0
qc.t(0)   # Apply T-gate to qubit 0
qc.tdg(0) # Apply Tdg-gate to qubit 0

Two Qubit Quantum Gates

The reversibel gates are eg. identity, or CNOT.

Eg. .

CNOT gate as a pictoram.


Eg. CNOT is a conditional gate that performs an X-gate on the second qubit, if the state of the first qubit (control) is . . This matrix swaps the amplitudes of |01⟩ and |11⟩ in the statevector. .

CNOT if a control qubit is on the superposition:

, which is Bell State. Entanglement, but no-communication theorem.

. Unchanged.

.

. Affects the state of the control qubit, only.

qc = QuantumCircuit(2)
qc.h(0)     # Apply H-gate to the first:
qc.cx(0,1)  # Apply a CNOT:


Any controlled quantum gate is and in Qiskit formalism is written in matrix as

Controlled-Z. Because and we can write

qc = QuantumCircuit(2)
# also a controlled-Z
qc.h(1)
qc.cx(0,1)
qc.h(1)

Controlled-Y is

qc = QuantumCircuit(2)
# a controlled-Y
qc.sdg(1)
qc.cx(0,1)
qc.s(1)

or Controlled-H is

qc = QuantumCircuit(2)
# a controlled-H
qc.ry(pi/4,1)
qc.cx(0,1)
qc.ry(-pi/4,1)

Swap gate: CNOT.

An arbitrary controlled-controlled-U for any single-qubit rotation U. We need and

#The controls are qubits a and b, and the target is qubit t.
#Subroutines cu1(theta,c,t) and cu1(-theta,c,t) need to be defined
qc = QuantumCircuit(3)
qc.cu1(theta,b,t)
qc.cx(a,b)
qc.cu1(-theta,b,t)
qc.cx(a,b)
qc.cu1(theta,a,t)

Three Qubit Gates

Toffoli gate made using CNOTs.


For universal computations we need more qubits. Eg. the AND gate is not reversible, and thus we need eg. Toffoli (CCNOT) gate.

Toffoli gate performs on target qubit if both control cubits are set to state .

qc = QuantumCircuit(3)
# Toffoli with control qubits a and b and target t
qc.ccx(a,b,t)


Toffoli using CNOTs uses fewer gates.

qc = QuantumCircuit(3)
qc.ch(a,t)
qc.cz(b,t)
qc.ch(a,t)

AND gate is Toffoli gate with . . .

The nand gate

gives the reversible NAND


NAND gate is


Multiple Controlled Toffoli

mct()

Logical Gates

The gates must be reversible.

Not Gate

The X gate is a not gate, and is reversible.

And Gate

The classical And mixes the inputs, thus we need two inputs. The Toffoli gate will do

qc.ccx(q[0], q[1], q[2])

Nand Gate

The Nand Gate is easy after the And gate. Just apply the Not gate.

qc.ccx(q[0], q[1], q[2])
qc.x(q[2])

Xor gate

Or gate is true only if either of inputs is true. Thus we have:

qc.cx(q[1], q[2])
qc.cx(q[0], q[2])

Or Gate

The Or gate is true if either is true, thus first check the nots and then toffoli (and):

qc.cx(q[0], q[2])
qc.cx(q[1], q[2])
qc.ccx(q[0], q[1], q[2])

Nor Gate

The Nor gate is the negation of Or gate:

qc.cx(q[0], q[2])
qc.cx(q[1], q[2])
qc.ccx(q[0], q[1], q[2])
qc.x(q[2])

Half Adder --- Results

qc.initialize([1,0], 0)
qc.initialize([1,0], 1)
print(count)
-> {'00': 1000}
qc.initialize([1,0], 0)
qc.initialize([0,1], 1)
-> {'01': 1000}
qc.initialize([0,1], 0)
qc.initialize([1,0], 1)
-> {'01': 1000}
qc.initialize([0,1], 0)
qc.initialize([0,1], 1)
-> {'10': 1000}



Full Adder

The state is initialized to , thus to set it to we may initialize it or use X Gate.

##Define registers and a quantum circuit
q = QuantumRegister(4)
c = ClassicalRegister(2)
qc = QuantumCircuit(q,c)

#
#qc.initialize([1,0], 0)
#qc.initialize([0,1], 1)
#qc.initialize([1,0], 2)

qc.x(q[0])
qc.x(q[2])

##AND -- carry
qc.ccx(q[0], q[1], q[3])
qc.cx(q[0], q[1])
qc.ccx(q[1], q[2], q[3])
qc.cx(q[1], q[2])
qc.cx(q[0], q[1])


##Sum
qc.measure(q[2], c[0])
##Carry out
qc.measure(q[3], c[1])

Oops 😕! Result did not match expected values Please review your answer and try again.:

from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit import IBMQ, Aer, execute

##Define registers and a quantum circuit
q = QuantumRegister(5)
c = ClassicalRegister(2)
qc = QuantumCircuit(q,c)

#
qc.initialize([1,0], 0)
qc.initialize([0,0], 1)
qc.initialize([1,0], 2)


##AND -- carry
qc.ccx(q[0], q[1], q[4])
qc.ccx(q[0], q[2], q[4])
qc.ccx(q[1], q[2], q[4])
qc.barrier()


##XOR - sum
qc.cx(q[0], q[3])
qc.cx(q[1], q[3])
qc.cx(q[2], q[3])
qc.barrier()


emulator = Aer.get_backend('qasm_simulator')
job = execute(qc, emulator, shots=2048 )
hist = job.result().get_counts()
plot_histogram(hist)



##Sum
qc.measure(q[3], c[0])
##Carry out
qc.measure(q[4], c[1])

backend = Aer.get_backend('qasm_simulator')
job = execute(qc, backend, shots=1000)
result = job.result()
count =result.get_counts()
print(count)
qc.draw(output='mpl')


Results

Results for the second case:

qc.initialize([0,1], 0)
qc.initialize([0,1], 1)
qc.initialize([0,1], 2)
-> {'11': 1000}
qc.initialize([0,1], 0)
qc.initialize([0,1], 1)
qc.initialize([1,0], 2)
-> {'10': 1000}
qc.initialize([0,1], 0)
qc.initialize([1,0], 1)
qc.initialize([0,1], 2)
-> {'10': 1000}
qc.initialize([0,1], 0)
qc.initialize([1,0], 1)
qc.initialize([1,0], 2)
-> {'01': 1000}
qc.initialize([1,0], 0)
qc.initialize([0,1], 1)
qc.initialize([0,1], 2)
-> {'10': 1000}
qc.initialize([1,0], 0)
qc.initialize([0,1], 1)
qc.initialize([1,0], 2)
-> {'01': 1000}
qc.initialize([1,0], 0)
qc.initialize([1,0], 1)
qc.initialize([0,1], 2)
-> {'01': 1000}
qc.initialize([1,0], 0)
qc.initialize([1,0], 1)
qc.initialize([1,0], 2)
-> {'00': 1000}