Home Algorithms Commercialization Data Science Information Theories Quantum Theories Lab Linear Algebra
<< Quantum Logic Gates PDF Square Root of Quantum Gates >>

$\require{cancel} \newcommand{\Ket}[1]{\left|{#1}\right\rangle} \newcommand{\Bra}[1]{\left\langle{#1}\right|} \newcommand{\Braket}[1]{\left\langle{#1}\right\rangle} \newcommand{\Rsr}[1]{\frac{1}{\sqrt{#1}}} \newcommand{\RSR}[1]{1/\sqrt{#1}} \newcommand{\Verti}{\rvert} \newcommand{\HAT}[1]{\hat{\,#1~}} \DeclareMathOperator{\Tr}{Tr}$

Quantum Logic Gates - 2

First created in August 2018

In [1]:
# Preamble
import numpy as np
import math
import matplotlib.pyplot as plt

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, execute
from qiskit.tools.visualization import plot_histogram, plot_state, circuit_drawer

# Compute the Expectation Value from circuit qc
shots = 1024
def run_job(qc_run, shots=shots):
    job = execute(qc_run, backend = 'local_qasm_simulator', shots=shots)
    result = job.result()
    data = result.get_counts(qc_run)
    for i in range(8):
        dataIndex = str(bin(i+8))[-3:]
        try:
            p = data[dataIndex]/shots
            print("P%s=%.3f" % (dataIndex, p))
        except:
            p = 0

    plot_histogram(job.result().get_counts(qc_run))
    return job

def print_vec(vec):
    vecLen = len(vec)
    qbCount = int(math.log(vecLen, 2))

    vecRnd = np.around(vec, 3)
    jn = ''
    for i in range(vecLen):
        ket = str(bin(i+vecLen))[-qbCount:]
        if np.abs(vecRnd[i]) >= 0.001:
            print("%s%s|%s>" % (jn, vecRnd[i], ket), end='')
            jn = ' + '

    print("")
    return vecRnd

Control Qubit in $cX$

From the $cX$ definition, the non-control qubit is the LSD. i.e. In $cU\Ket{ij}, i$ is the control and $j$ is to be acted on, or "qc.cx(q[0], q[1])" based on our convention.

$cX= \begin{bmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 1\\ 0 & 0 & 1 & 0 \end{bmatrix} .$

If we switch the two, $c'X$ is to take $j$ as the control and act on $i$, $c'X= \begin{bmatrix} 1 & 0 & 0 & 0\\ 0 & 0 & 0 & 1\\ 0 & 0 & 1 & 0\\ 0 & 1 & 0 & 0 \end{bmatrix} ,~~$ equivalent to "qc.cx(q[1], q[0])".

$cX$ Equivalent

One interesting fact is that $c'X=(H\otimes H)~cX~(H\otimes H).$ This means if $cX$ is applied to a Hardama basis, it is the control qubit that will be flipped.

Here $cX_{01}:=cX$ and $cX_{10}:=c'X.$

$(H\otimes H)~cX_{01}~(H\otimes H)=cX_{10}.$

Proof: $(H\otimes H)~cX_{01}~(H\otimes H) =\Rsr2 \begin{bmatrix} H&H\\ H&-H \end{bmatrix} \begin{bmatrix} I&0\\ 0&X \end{bmatrix} \Rsr2 \begin{bmatrix} H&H\\ H&-H \end{bmatrix}\\ ={1\over2} \begin{bmatrix} H&H\\ H&-H \end{bmatrix} \begin{bmatrix} IH&IH\\ XH&-XH \end{bmatrix} ={1\over2} \begin{bmatrix} HIH+HXH&HIH-HXH\\ HIH-HXH&HIH+HXH \end{bmatrix}\\ ={1\over2} \begin{bmatrix} I+Z&I-Z\\ I-Z&I+Z \end{bmatrix} =\begin{bmatrix} 1&0&0&0\\ 0&0&0&1\\ 0&0&1&0\\ 0&1&0&0\\ \end{bmatrix} =cX_{10} .$


Note: $H^2=I$, $HXH=Z$ and $HZH=X$.

In [2]:
# (HH)cX01(HH)
qbNum = 2
q = QuantumRegister(qbNum)
c = ClassicalRegister(qbNum)
qc = QuantumCircuit(q, c)

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

job = execute(qc, backend = 'local_statevector_simulator', shots=shots)
print_vec(job.result().get_statevector(qc))
circuit_drawer(qc)
(1-0j)|00>
Out[2]:
In [3]:
# The above is equivalent to ...
# cX10
qbNum = 2
q = QuantumRegister(qbNum)
c = ClassicalRegister(qbNum)
qc = QuantumCircuit(q, c)

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

job = execute(qc, backend = 'local_statevector_simulator', shots=shots)
print_vec(job.result().get_statevector(qc))
circuit_drawer(qc)
(1+0j)|00>
Out[3]:

SWAP

The $SWAP$ gate swaps two qubits, not the two basis vectors of a single qubit (as in $X$).

Let $\Ket\psi=\alpha\Ket0+\beta\Ket1=[\alpha,\beta]^T,~~ X\Ket\psi=\beta\Ket0+\alpha\Ket1=[\beta,\alpha]^T.$

Let $\Ket\phi=\alpha\Ket{00}+\beta\Ket{01}+\gamma\Ket{10}+\delta\Ket{11} =[\alpha,\beta,\gamma,\delta]^T,~~ SWAP\Ket\phi=\alpha\Ket{00}+\gamma\Ket{01}+\beta\Ket{10}+\delta\Ket{11} =[\alpha,\gamma,\beta,\delta]^T.$

Or in an unentangled form: $\Ket\phi=(a\Ket0+b\Ket1)(c\Ket0+d\Ket1),$

$SWAP\Ket\phi =SWAP(ac\Ket{00}+ad\Ket{01}+bc\Ket{10}+bd\Ket{11} =(ac\Ket{00}+bc\Ket{01}+ad\Ket{10}+bd\Ket{11}\\ =(c\Ket0+d\Ket1)(a\Ket0+b\Ket1) .$


$SWAP= \begin{bmatrix} 1 & 0 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 1 \end{bmatrix} .~~~ SWAP^2=I,~~~ SWAP^\dagger=SWAP,~~~$so $SWAP$ is unitary.

A SWAP gate is equivalent to

$cX~c'X~cX =\begin{bmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 1\\ 0 & 0 & 1 & 0 \end{bmatrix} \begin{bmatrix} 1 & 0 & 0 & 0\\ 0 & 0 & 0 & 1\\ 0 & 0 & 1 & 0\\ 0 & 1 & 0 & 0 \end{bmatrix} \begin{bmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 1\\ 0 & 0 & 1 & 0 \end{bmatrix} =\begin{bmatrix} 1 & 0 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 1 \end{bmatrix} =SWAP .$

In [4]:
# cX01 cX10 cX01
qbNum = 2
q = QuantumRegister(qbNum)
c = ClassicalRegister(qbNum)

# Prepare a 2-qubit state
# with q_0=(pi/2,pi/4)
# and q_1=(pi/4,3pi/2)
prep = QuantumCircuit(q, c)

prep.h(q[0])
prep.t(q[0])
prep.h(q[1])
prep.t(q[1])
prep.h(q[1])

job = execute(prep, backend = 'local_statevector_simulator', shots=shots)
print_vec(job.result().get_statevector(prep))
(0.604+0.25j)|00> + (0.25+0.604j)|01> + (0.104-0.25j)|10> + (0.25-0.104j)|11>
Out[4]:
array([0.604+0.25j , 0.25 +0.604j, 0.104-0.25j , 0.25 -0.104j])
In [5]:
# Equivalent to SWAP
swap = QuantumCircuit(q, c)
swap.cx(q[0], q[1])
swap.cx(q[1], q[0])
swap.cx(q[0], q[1])

qc = prep + swap

job = execute(qc, backend = 'local_statevector_simulator', shots=shots)
print_vec(job.result().get_statevector(qc))
circuit_drawer(swap)
(0.604+0.25j)|00> + (0.104-0.25j)|01> + (0.25+0.604j)|10> + (0.25-0.104j)|11>
Out[5]:

 

<< Quantum Logic Gates Top Square Root of Quantum Gates >>