Qiskit: First Circuit
Qiskit: First Circuit
In [1]: from
qiskit
import
QuantumCircuit,
assemble,
Aer
from
qiskit.visualization
import
plot_histogram
First circuit
In a circuit, we typically need to do three jobs: First, encode the input, then do some actual
computation, and finally extract an output. For your first quantum circuit, we'll focus on the last of these
jobs. We start by creating a circuit with eight qubits and eight outputs.
In [2]: qc_output
=
QuantumCircuit(8)
his circuit, which we have called qc_output, is created by Qiskit using QuantumCircuit. The
QuantumCircuit takes the number of qubits in the quantum circuit as an argument.
The extraction of outputs in a quantum circuit is done using an operation called measure_all(). Each
measurement tells a specific qubit to give an output to a specific output bit. The command
qc_output.measure_all() adds a measurement to each qubit in the circuit qc_output, and also adds
some classical bits to write the output to.
In [3]: qc_output.measure_all()
In [63]: qc_output.draw(initial_state=True)
░
┌─┐
Out[63]:
q_0:
|0>─░─┤M├─────────────────────
░
└╥┘┌─┐
q_1:
|0>─░──╫─┤M├──────────────────
░
║
└╥┘┌─┐
q_2:
|0>─░──╫──╫─┤M├───────────────
░
║
║
└╥┘┌─┐
q_3:
|0>─░──╫──╫──╫─┤M├────────────
░
║
║
║
└╥┘┌─┐
q_4:
|0>─░──╫──╫──╫──╫─┤M├─────────
░
║
║
║
║
└╥┘┌─┐
q_5:
|0>─░──╫──╫──╫──╫──╫─┤M├──────
░
║
║
║
║
║
└╥┘┌─┐
q_6:
|0>─░──╫──╫──╫──╫──╫──╫─┤M├───
░
║
║
║
║
║
║
└╥┘┌─┐
q_7:
|0>─░──╫──╫──╫──╫──╫──╫──╫─┤M├
░
║
║
║
║
║
║
║
└╥┘
meas:
0
8/════╩══╩══╩══╩══╩══╩══╩══╩═
0
1
2
3
4
5
6
7
In [5]: sim
=
Aer.get_backend('aer_simulator')
result
=
sim.run(qc_output).result()
counts
=
result.get_counts()
plot_histogram(counts)
Out[5]:
Example: Creating an Adder Circuit
Now let's look at how to encode a different binary string as an input. For this, we need what is known as
a NOT gate. This is the most basic operation that you can do in a computer. It simply flips the bit value:
0 becomes 1 and 1 becomes 0. For qubits, it is an operation called x that does the job of the NOT.
Below we create a new circuit dedicated to the job of encoding and call it qc_encode. For now, we only
specify the number of qubits.
In [6]: qc_encode
=
QuantumCircuit(8)
qc_encode.x(7)
qc_encode.draw()
Out[6]:
q_0:
─────
q_1:
─────
q_2:
─────
q_3:
─────
q_4:
─────
q_5:
─────
q_6:
─────
┌───┐
q_7:
┤
X
├
└───┘
In [7]: #€xtracting
results
qc_encode.measure_all()
qc_encode.draw()
░
┌─┐
Out[7]:
q_0:
──────░─┤M├─────────────────────
░
└╥┘┌─┐
q_1:
──────░──╫─┤M├──────────────────
░
║
└╥┘┌─┐
q_2:
──────░──╫──╫─┤M├───────────────
░
║
║
└╥┘┌─┐
q_3:
──────░──╫──╫──╫─┤M├────────────
░
║
║
║
└╥┘┌─┐
q_4:
──────░──╫──╫──╫──╫─┤M├─────────
░
║
║
║
║
└╥┘┌─┐
q_5:
──────░──╫──╫──╫──╫──╫─┤M├──────
░
║
║
║
║
║
└╥┘┌─┐
q_6:
──────░──╫──╫──╫──╫──╫──╫─┤M├───
┌───┐
░
║
║
║
║
║
║
└╥┘┌─┐
q_7:
┤
X
├─░──╫──╫──╫──╫──╫──╫──╫─┤M├
└───┘
░
║
║
║
║
║
║
║
└╥┘
meas:
8/═════════╩══╩══╩══╩══╩══╩══╩══╩═
0
1
2
3
4
5
6
7
In [8]: #look
at
the
results
sim
=
Aer.get_backend('aer_simulator')
result
=
sim.run(qc_encode).result()
counts
=
result.get_counts()
plot_histogram(counts)
Out[8]:
Out[9]:
q_0:
─────
┌───┐
q_1:
┤
X
├
└───┘
q_2:
─────
q_3:
─────
q_4:
─────
┌───┐
q_5:
┤
X
├
└───┘
q_6:
─────
q_7:
─────
In quantum computers, the job of the XOR gate is done by the controlled‑NOT gate. Since that's quite a
long name, we usually just call it the CNOT. In Qiskit its name is cx, which is even shorter. 1 2 XOR 0 0 0
110101011
In circuit diagrams, it is drawn as in the image below.
In [10]: qc_cnot
=
QuantumCircuit(2)
qc_cnot.cx(0,1)
qc_cnot.draw()
Out[10]:
q_0:
──■──
┌─┴─┐
q_1:
┤
X
├
└───┘
his is applied to a pair of qubits. One acts as the control qubit (this is the one with the little dot). The
other acts as the target qubit (with the big circle that has a + inside it).
There are multiple ways to explain the effect of the CNOT. One is to say that it looks at its two input bits
to see whether they are the same or different. Next, it overwrites the target qubit with the answer. The
target becomes 0 if they are the same, and 1 if they are different.
Another way of explaining the CNOT is to say that it does a NOT on the target if the control is 1, and
does nothing otherwise. This explanation is just as valid as the previous one (in fact, it’s the one that
gives the gate its name).
Try the CNOT out for yourself by trying each of the possible inputs. For example, here's a circuit that
tests the CNOT with the input 01.
In [11]: qc
=
QuantumCircuit(2,2)
qc.x(0)
qc.cx(0,1)
qc.measure(0,0)
qc.measure(1,1)
qc.draw()
┌───┐
┌─┐
Out[11]:
q_0:
┤
X
├──■──┤M├───
└───┘┌─┴─┐└╥┘┌─┐
q_1:
─────┤
X
├─╫─┤M├
└───┘
║
└╥┘
c:
2/═══════════╩══╩═
0
1
If you execute this circuit, you’ll find that the output is 11. We can think of this happening because of
either of the following reasons.
The
CNOT
calculates
whether
the
input
values
are
different
and
finds
that
they
are,
which
means
that
it
wants
to
output
1.
It
does
this
by
writing
over
the
state
of
qubit
1
(which,
remember,
is
on
the
left
of
the
bit
string),
turning
01
into
11.
The
CNOT
sees
that
qubit
0
is
in
state
1,
and
so
applies
a
NOT
to
qubit
1.
This
flips
the
0
of
qubit
1
into
a
1,
and
so
turns
01
into
11.
Here is a table showing all the possible inputs and corresponding outputs of the CNOT gate:
Q1 Q0 ‑> Q1 Q0 00 00 01 11 10 10 11 01 For our half adder, we don’t want to overwrite one of our inputs.
Instead, we want to write the result on a different pair of qubits. For this, we can use two CNOTs.
In [12]: qc_ha
=
QuantumCircuit(4,2)
#
encode
inputs
in
qubits
0
and
1
qc_ha.x(0)
#
For
a=0,
remove
this
line.
For
a=1,
leave
it.
qc_ha.x(1)
#
For
b=0,
remove
this
line.
For
b=1,
leave
it.
qc_ha.barrier()
#
use
cnots
to
write
the
XOR
of
the
inputs
on
qubit
2
qc_ha.cx(0,2)
qc_ha.cx(1,2)
qc_ha.barrier()
#
extract
outputs
qc_ha.measure(2,0)
#
extract
XOR
value
qc_ha.measure(3,1)
qc_ha.draw()
┌───┐
░
░
Out[12]:
q_0:
┤
X
├─░───■────────░───────
├───┤
░
│
░
q_1:
┤
X
├─░───┼────■───░───────
└───┘
░
┌─┴─┐┌─┴─┐
░
┌─┐
q_2:
──────░─┤
X
├┤
X
├─░─┤M├───
░
└───┘└───┘
░
└╥┘┌─┐
q_3:
──────░────────────░──╫─┤M├
░
░
║
└╥┘
c:
2/══════════════════════╩══╩═
0
1
In [14]: #In
Qiskit,
we
use
the
QuantumCircuit
object
to
store
our
circuits,
this
is
essentially
qc
=
QuantumCircuit(1)
#
Create
a
quantum
circuit
with
one
qubit
In our quantum circuits, our qubits always start out in the state |0⟩. We can use the initialize() method to
transform this into any state. We give initialize() the vector we want in the form of a list, and tell it which
qubit(s) we want to initialize in this state:
In [15]: qc
=
QuantumCircuit(1)
#
Create
a
quantum
circuit
with
one
qubit
initial_state
=
[0,1]
#
Define
initial_state
as
|1>
qc.initialize(initial_state,
0)
#
Apply
initialisation
operation
to
the
0th
qubit
qc.draw()
#
Let's
view
our
circuit
┌─────────────────┐
Out[15]:
q:
┤
Initialize(0,1)
├
└─────────────────┘
In [16]: #We
can
then
use
one
of
Qiskit’s
simulators
to
view
the
resulting
state
of
our
qubit.
sim
=
Aer.get_backend('aer_simulator')
#
Tell
Qiskit
how
to
simulate
our
circuit
To get the results from our circuit, we use run to execute our circuit, giving the circuit and the backend
as arguments. We then use .result() to get the result of this:
In [17]: qc
=
QuantumCircuit(1)
#
Create
a
quantum
circuit
with
one
qubit
initial_state
=
[0,1]
#
Define
initial_state
as
|1>
qc.initialize(initial_state,
0)
#
Apply
initialisation
operation
to
the
0th
qubit
qc.save_statevector()
#
Tell
simulator
to
save
statevector
qobj
=
assemble(qc)
#
Create
a
Qobj
from
the
circuit
for
the
simulator
to
run
result
=
sim.run(qobj).result()
#
Do
the
simulation
and
return
the
result
In [18]: #From
result,
we
can
then
get
the
final
statevector
using
.get_statevector﴾﴿:
out_state
=
result.get_statevector()
print(out_state)
#
Display
the
output
state
vector
Statevector([0.+0.j,
1.+0.j],
dims=(2,))
Note: Python uses j to represent i in complex numbers. We see a vector with two complex elements:
0.+0.j = 0, and 1.+0.j = 1.
In [19]: #Let’s
now
measure
our
qubit
as
we
would
in
a
real
quantum
computer
and
see
the
result:
qc.measure_all()
qc.draw()
┌─────────────────┐
statevector
░
┌─┐
Out[19]:
q:
┤
Initialize(0,1)
├──────░───────░─┤M├
└─────────────────┘
░
░
└╥┘
meas:
1/════════════════════════════════════╩═
0
In [21]: #This
time,
instead
of
the
statevector
we
will
get
the
counts
for
the
0
and
1
results
us
qobj
=
assemble(qc)
result
=
sim.run(qobj).result()
counts
=
result.get_counts()
plot_histogram(counts)
Out[21]:
We can see that we (unsurprisingly) have a 100% chance of measuring |1⟩. This time, let’s instead put
our qubit into a superposition and see what happens. We will use the state |q0⟩
from earlier in this section: |q0⟩=1√2|0⟩+i√2|1⟩
We need to add these amplitudes to a python list. To add a complex amplitude, Python uses j for the
imaginary unit (we normally call it "i " mathematically):
In [22]: initial_state
=
[1/sqrt(2),
1j/sqrt(2)]
#
Define
state
|q_0>
In [23]: #And
we
then
repeat
the
steps
for
initialising
the
qubit
as
before:
qc
=
QuantumCircuit(1)
#
Must
redefine
qc
qc.initialize(initial_state,
0)
#
Initialize
the
0th
qubit
in
the
state
`initial_state`
qc.save_statevector()
#
Save
statevector
qobj
=
assemble(qc)
state
=
sim.run(qobj).result().get_statevector()
#
Execute
the
circuit
print(state)
#
Print
the
result
Statevector([0.70710678+0.j
,
0.
+0.70710678j],
dims=(2,))
In [24]: qobj
=
assemble(qc)
results
=
sim.run(qobj).result().get_counts()
plot_histogram(results)
Out[24]:
┌───┐
Out[27]:
q:
┤
X
├
└───┘
In [28]: #Let's
see
the
result
of
the
above
circuit.
Note:
Here
we
use
plot_bloch_multivector﴾﴿
w
#
Let's
see
the
result
qc.save_statevector()
qobj
=
assemble(qc)
state
=
sim.run(qobj).result().get_statevector()
plot_bloch_multivector(state)
Out[28]:
In [29]: #The
Y
&
Zgates
#Below
is
a
widget
that
displays
a
qubit’s
state
on
the
Bloch
sphere,
pressing
one
of
th
#
Run
the
code
in
this
cell
to
see
the
widget
from
qiskit_textbook.widgets
import
gate_demo
gate_demo(gates='pauli')
ModuleNotFoundError
Traceback
(most
recent
call
last)
/var/folders/nd/vzx00q0j5615sxcrs0qldzh80000gn/T/ipykernel_2157/4291737448.py
in
<module
>
3
4
#
Run
the
code
in
this
cell
to
see
the
widget
>
5
from
qiskit_textbook.widgets
import
gate_demo
6
gate_demo(gates='pauli')
ModuleNotFoundError:
No
module
named
'qiskit_textbook'
In [30]: #In
Qiskit,
we
can
apply
the
Y
and
Zgates
to
our
circuit
using:
qc.y(0)
#
Do
Ygate
on
qubit
0
qc.z(0)
#
Do
Zgate
on
qubit
0
qc.draw()
┌───┐
statevector
┌───┐┌───┐
Out[30]:
q:
┤
X
├──────░──────┤
Y
├┤
Z
├
└───┘
░
└───┘└───┘
ModuleNotFoundError
Traceback
(most
recent
call
last)
/var/folders/nd/vzx00q0j5615sxcrs0qldzh80000gn/T/ipykernel_2157/1957695400.py
in
<module
>
4
5
#
Run
the
code
in
this
cell
to
see
the
widget
>
6
from
qiskit_textbook.widgets
import
gate_demo
7
gate_demo(gates='pauli+h')
ModuleNotFoundError:
No
module
named
'qiskit_textbook'
The P‑gate
The P‑gate (phase gate) is parametrised, that is, it needs a number (ϕ ) to tell it exactly what to do. The
P‑gate performs a rotation of ϕ around the Z‑axis direction. It has the matrix form:
In [33]: #You
can
use
the
widget
below
to
play
around
with
the
Pgate,
specify
ϕ
#using
the
slider:
#
Run
the
code
in
this
cell
to
see
the
widget
from
qiskit_textbook.widgets
import
gate_demo
gate_demo(gates='pauli+h+p')
ModuleNotFoundError
Traceback
(most
recent
call
last)
/var/folders/nd/vzx00q0j5615sxcrs0qldzh80000gn/T/ipykernel_2157/3404144451.py
in
<module
>
4
5
#
Run
the
code
in
this
cell
to
see
the
widget
>
6
from
qiskit_textbook.widgets
import
gate_demo
7
gate_demo(gates='pauli+h+p')
ModuleNotFoundError:
No
module
named
'qiskit_textbook'
In [34]: #
In
Qiskit,
we
specify
a
Pgate
using
p﴾phi,
qubit﴿:
qc
=
QuantumCircuit(1)
qc.p(pi/4,
0)
qc.draw()
┌────────┐
Out[34]:
q:
┤
P(π/4)
├
└────────┘
┌───┐┌─────┐
Out[35]:
q:
┤
S
├┤
Sdg
├
└───┘└─────┘
The T‑gate
The T‑gate is a very commonly used gate, it is an P‑gate with ϕ=π/4 :
In [36]: qc
=
QuantumCircuit(1)
qc.t(0)
#
Apply
Tgate
to
qubit
0
qc.tdg(0)
#
Apply
Tdggate
to
qubit
0
qc.draw()
┌───┐┌─────┐
Out[36]:
q:
┤
T
├┤
Tdg
├
└───┘└─────┘
In [37]: #
Run
the
code
in
this
cell
to
see
the
widget
from
qiskit_textbook.widgets
import
gate_demo
gate_demo()
ModuleNotFoundError
Traceback
(most
recent
call
last)
/var/folders/nd/vzx00q0j5615sxcrs0qldzh80000gn/T/ipykernel_2157/4127987965.py
in
<module
>
1
#
Run
the
code
in
this
cell
to
see
the
widget
>
2
from
qiskit_textbook.widgets
import
gate_demo
3
gate_demo()
ModuleNotFoundError:
No
module
named
'qiskit_textbook'
The U‑gate
As we saw earlier, the I, Z, S & T‑gates were all special cases of the more general P‑gate. In the same
way, the U‑gate is the most general of all single‑qubit quantum gates. It is a parametrised gate of the
form:
In [38]: #
Let's
have
Ugate
transform
a
|0>
to
|+>
state
qc
=
QuantumCircuit(1)
qc.u(pi/2,
0,
pi,
0)
qc.draw()
┌────────────┐
Out[38]:
q:
┤
U(π/2,0,π)
├
└────────────┘
In [39]: #
Let's
see
the
result
qc.save_statevector()
qobj
=
assemble(qc)
state
=
sim.run(qobj).result().get_statevector()
plot_bloch_multivector(state)
Out[39]:
In [41]: qc
=
QuantumCircuit(3)
#
Apply
Hgate
to
each
qubit:
for
qubit
in
range(3):
qc.h(qubit)
#
See
the
circuit:
qc.draw()
┌───┐
Out[41]:
q_0:
┤
H
├
├───┤
q_1:
┤
H
├
├───┤
q_2:
┤
H
├
└───┘
In [42]: #
Let's
see
the
result
svsim
=
Aer.get_backend('aer_simulator')
qc.save_statevector()
qobj
=
assemble(qc)
final_state
=
svsim.run(qobj).result().get_statevector()
#
In
Jupyter
Notebooks
we
can
display
this
nicely
using
Latex.
#
If
not
using
Jupyter
Notebooks
you
may
need
to
remove
the
#
array_to_latex
function
and
use
print﴾final_state﴿
instead.
from
qiskit.visualization
import
array_to_latex
array_to_latex(final_state,
prefix="\\text{Statevector}
=
")
Out[42]:
√2 √2 √2 √2 √2 √2 √2 √2
Statevector = [ ]
4 4 4 4 4 4 4 4
In [44]: #For
example,
in
the
circuit
below:
qc
=
QuantumCircuit(2)
qc.h(0)
qc.x(1)
qc.draw()
#we
can
represent
the
simultaneous
operations
﴾H
&
X﴿
using
their
kronecker
product:
┌───┐
Out[44]:
q_0:
┤
H
├
├───┤
q_1:
┤
X
├
└───┘
In [45]: #The
Aer
simulator
multiplies
all
the
gates
in
our
circuit
together
to
compile
a
single
usim
=
Aer.get_backend('aer_simulator')
qc.save_unitary()
qobj
=
assemble(qc)
unitary
=
usim.run(qobj).result().get_unitary()
In [46]: #
In
Jupyter
Notebooks
we
can
display
this
nicely
using
Latex.
#
If
not
using
Jupyter
Notebooks
you
may
need
to
remove
the
#
array_to_latex
function
and
use
print﴾unitary﴿
instead.
from
qiskit.visualization
import
array_to_latex
array_to_latex(unitary,
prefix="\\text{Circuit
=
}\n")
Out[46]:
√2 √2
0 0
⎡ 2 2 ⎤
⎢
√2 √2 ⎥
⎢
0 0 − ⎥
⎢
2 2 ⎥
Circuit
= ⎢
⎥
⎢
√2 √2 ⎥
⎢
0 0 ⎥
⎢
2 2 ⎥
⎢ ⎥
√2 √2
⎣ − 0 0 ⎦
2 2
In [47]: #If
we
want
to
apply
a
gate
to
only
one
qubit
at
a
time
﴾such
as
in
the
circuit
below﴿,
qc
=
QuantumCircuit(2)
qc.x(1)
qc.draw()
Out[47]:
q_0:
─────
┌───┐
q_1:
┤
X
├
└───┘
In [48]: #
Simulate
the
unitary
usim
=
Aer.get_backend('aer_simulator')
qc.save_unitary()
qobj
=
assemble(qc)
unitary
=
usim.run(qobj).result().get_unitary()
#
Display
the
results:
array_to_latex(unitary,
prefix="\\text{Circuit
=
}
")
Out[48]:
0 0 1 0
⎡ ⎤
0 0 0 1
⎢
⎥
Circuit
= ⎢
⎥
⎢1 0 0 0⎥
⎣ ⎦
0 1 0 0
Multi‑Qubit Gates
Now we know how to represent the state of multiple qubits, we are now ready to learn how qubits
interact with each other. An important two‑qubit gate is the CNOT‑gate.
The CNOT‑Gate
You have come across this gate before in The Atoms of Computation. This gate is a conditional gate
that performs an X‑gate on the second qubit (target), if the state of the first qubit (control) is |1⟩ . The
gate is drawn on a circuit like this, with q0 as the control and q1 as the target:
In [51]: qc
=
QuantumCircuit(2)
#
Apply
CNOT
qc.cx(0,1)
#
See
the
circuit:
qc.draw()
Out[51]:
q_0:
──■──
┌─┴─┐
q_1:
┤
X
├
└───┘
In [52]: qc
=
QuantumCircuit(2)
#
Apply
CNOT
qc.cx(0,1)
#
See
the
circuit:
qc.draw('mpl')
Out[52]:
In [53]: qc
=
QuantumCircuit(2)
#
Apply
Hgate
to
the
first:
qc.h(0)
qc.draw()
┌───┐
Out[53]:
q_0:
┤
H
├
└───┘
q_1:
─────
In [54]: qc
=
QuantumCircuit(2)
#
Apply
Hgate
to
the
first:
qc.h(0)
qc.draw('mpl')
Out[54]:
In [55]: #
Let's
see
the
result:
svsim
=
Aer.get_backend('aer_simulator')
qc.save_statevector()
qobj
=
assemble(qc)
final_state
=
svsim.run(qobj).result().get_statevector()
#
Print
the
statevector
neatly:
array_to_latex(final_state,
prefix="\\text{Statevector
=
}")
Out[55]:
√2 √2
Statevector
= [ 0 0]
2 2
In [56]: qc
=
QuantumCircuit(2)
#
Apply
Hgate
to
the
first:
qc.h(0)
#
Apply
a
CNOT:
qc.cx(0,1)
qc.draw()
┌───┐
Out[56]:
q_0:
┤
H
├──■──
└───┘┌─┴─┐
q_1:
─────┤
X
├
└───┘
In [57]: qc
=
QuantumCircuit(2)
#
Apply
Hgate
to
the
first:
qc.h(0)
#
Apply
a
CNOT:
qc.cx(0,1)
qc.draw('mpl')
Out[57]:
In [58]: #
Let's
get
the
result:
qc.save_statevector()
qobj
=
assemble(qc)
result
=
svsim.run(qobj).result()
#
Print
the
statevector
neatly:
final_state
=
result.get_statevector()
array_to_latex(final_state,
prefix="\\text{Statevector
=
}")
Out[58]:
√2 √2
Statevector
= [ 0 0 ]
2 2
Entangled States
We saw in the previous section we could create the state: 1√2(|00⟩+|11⟩)
This is known as a Bell state. We can see that this state has 50% probability of being measured in the
state |00⟩ , and 50% chance of being measured in the state |11⟩. Most interestingly, it has a 0% chance
of being measured in the states |01⟩ or |10⟩. We can see this in Qiskit:
In [59]: plot_histogram(result.get_counts())
Out[59]:
Visualizing Entangled States
We have seen that this state cannot be written as two separate qubit states, this also means we lose
information when we try to plot our state on separate Bloch spheres:
In [60]: plot_bloch_multivector(final_state)
Out[60]:
Given how we defined the Bloch sphere in the earlier chapters, it may not be clear how Qiskit even
calculates the Bloch vectors with entangled qubits like this. In the single‑qubit case, the position of the
Bloch vector along an axis nicely corresponds to the expectation value of measuring in that basis. If we
take this as the rule of plotting Bloch vectors, we arrive at this conclusion above. This shows us there is
no single‑qubit measurement basis for which a specific measurement is guaranteed. This contrasts
with our single qubit states, in which we could always pick a single‑qubit basis. Looking at the individual
qubits in this way, we miss the important effect of correlation between the qubits. We cannot
distinguish between different entangled states. For example, the two states:
1√2(|01⟩+|10⟩)and1√2(|00⟩+|11⟩)
will both look the same on these separate Bloch spheres, despite being very different states with
different measurement outcomes.
How else could we visualize this statevector? This statevector is simply a collection of four amplitudes
(complex numbers), and there are endless ways we can map this to an image. One such visualization is
the Q‑sphere, here each amplitude is represented by a blob on the surface of a sphere. The size of the
blob is proportional to the magnitude of the amplitude, and the colour is proportional to the phase of
the amplitude. The amplitudes for |00⟩ and |11⟩ are equal, and all other amplitudes are 0:
In [61]: from
qiskit.visualization
import
plot_state_qsphere
plot_state_qsphere(final_state)
Out[61]:
Shor's Algorithm
Shor’s algorithm is famous for factoring integers in polynomial time. Since the best‑known classical
algorithm requires superpolynomial time to factor the product of two primes, the widely used
cryptosystem, RSA, relies on factoring being impossible for large enough integers.
In this chapter we will focus on the quantum part of Shor’s algorithm, which actually solves the problem
of period finding. Since a factoring problem can be turned into a period finding problem in polynomial
time, an efficient period finding algorithm can be used to factor integers efficiently too.
In [64]: import
matplotlib.pyplot
as
plt
import
numpy
as
np
from
qiskit
import
QuantumCircuit,
Aer,
transpile,
assemble
from
qiskit.visualization
import
plot_histogram
from
math
import
gcd
from
numpy.random
import
randint
import
pandas
as
pd
from
fractions
import
Fraction
print("Imports
Successful")
Imports Successful
In [65]: def
c_amod15(a,
power):
"""Controlled
multiplication
by
a
mod
15"""
if
a
not
in
[2,4,7,8,11,13]:
raise
ValueError("'a'
must
be
2,4,7,8,11
or
13")
U
=
QuantumCircuit(4)
for
iteration
in
range(power):
if
a
in
[2,13]:
U.swap(2,3)
U.swap(1,2)
U.swap(0,1)
if
a
in
[7,8]:
U.swap(0,1)
U.swap(1,2)
U.swap(2,3)
if
a
in
[4,
11]:
U.swap(1,3)
U.swap(0,2)
if
a
in
[7,11,13]:
for
q
in
range(4):
U.x(q)
U
=
U.to_gate()
U.name
=
"%i^%i
mod
15"
%
(a,
power)
c_U
=
U.control()
return
c_U
In [66]: #
Specify
variables
n_count
=
8
#
number
of
counting
qubits
a
=
7
In [67]: def
qft_dagger(n):
"""nqubit
QFTdagger
the
first
n
qubits
in
circ"""
qc
=
QuantumCircuit(n)
#
Don't
forget
the
Swaps!
for
qubit
in
range(n//2):
qc.swap(qubit,
nqubit1)
for
j
in
range(n):
for
m
in
range(j):
qc.cp(np.pi/float(2**(jm)),
m,
j)
qc.h(j)
qc.name
=
"QFT†"
return
qc
In [68]: #
Create
QuantumCircuit
with
n_count
counting
qubits
#
plus
4
qubits
for
U
to
act
on
qc
=
QuantumCircuit(n_count
+
4,
n_count)
#
Initialize
counting
qubits
#
in
state
|+>
for
q
in
range(n_count):
qc.h(q)
#
And
auxiliary
register
in
state
|1>
qc.x(n_count)
#
Do
controlledU
operations
for
q
in
range(n_count):
qc.append(c_amod15(a,
2**q),
[q]
+
[i+n_count
for
i
in
range(4)])
#
Do
inverseQFT
qc.append(qft_dagger(n_count),
range(n_count))
#
Measure
circuit
qc.measure(range(n_count),
range(n_count))
qc.draw(fold=1)
#
1
means
'do
not
fold'
┌───┐
Out[68]:
q_0:
┤
H
├───────■────────────────────────────────────────────────────────────────
├───┤
│
q_1:
┤
H
├───────┼──────────────■─────────────────────────────────────────────────
├───┤
│
│
q_2:
┤
H
├───────┼──────────────┼──────────────■──────────────────────────────────
├───┤
│
│
│
q_3:
┤
H
├───────┼──────────────┼──────────────┼──────────────■───────────────────
├───┤
│
│
│
│
q_4:
┤
H
├───────┼──────────────┼──────────────┼──────────────┼──────────────■────
├───┤
│
│
│
│
│
q_5:
┤
H
├───────┼──────────────┼──────────────┼──────────────┼──────────────┼────
├───┤
│
│
│
│
│
q_6:
┤
H
├───────┼──────────────┼──────────────┼──────────────┼──────────────┼────
├───┤
│
│
│
│
│
q_7:
┤
H
├───────┼──────────────┼──────────────┼──────────────┼──────────────┼────
├───┤┌──────┴──────┐┌──────┴──────┐┌──────┴──────┐┌──────┴──────┐┌──────┴────
q_8:
┤
X
├┤0
├┤0
├┤0
├┤0
├┤0
└───┘│
││
││
││
││
q_9:
─────┤1
├┤1
├┤1
├┤1
├┤1
│
7^1
mod
15
││
7^2
mod
15
││
7^4
mod
15
││
7^8
mod
15
││
7^16
mod
q_10:
─────┤2
├┤2
├┤2
├┤2
├┤2
│
││
││
││
││
q_11:
─────┤3
├┤3
├┤3
├┤3
├┤3
└─────────────┘└─────────────┘└─────────────┘└─────────────┘└───────────
c:
8/═════════════════════════════════════════════════════════════════════════════
In [69]: qc.draw('mpl')
Out[69]:
In [70]: aer_sim
=
Aer.get_backend('aer_simulator')
t_qc
=
transpile(qc,
aer_sim)
qobj
=
assemble(t_qc)
results
=
aer_sim.run(qobj).result()
counts
=
results.get_counts()
plot_histogram(counts)
Out[70]:
In [71]: rows,
measured_phases
=
[],
[]
for
output
in
counts:
decimal
=
int(output,
2)
#
Convert
﴾base
2﴿
string
to
decimal
phase
=
decimal/(2**n_count)
#
Find
corresponding
eigenvalue
measured_phases.append(phase)
#
Add
these
values
to
the
rows
in
our
table:
rows.append([f"{output}(bin)
=
{decimal:>3}(dec)",
f"{decimal}/{2**n_count}
=
{phase:.2f}"])
#
Print
the
rows
in
a
table
headers=["Register
Output",
"Phase"]
df
=
pd.DataFrame(rows,
columns=headers)
print(df)
Register
Output
Phase
0
00000000(bin)
=
0(dec)
0/256
=
0.00
1
11000000(bin)
=
192(dec)
192/256
=
0.75
2
10000000(bin)
=
128(dec)
128/256
=
0.50
3
01000000(bin)
=
64(dec)
64/256
=
0.25
In [72]: Fraction(0.666)
Fraction(5998794703657501,
9007199254740992)
Out[72]:
In [73]: #
Get
fraction
that
most
closely
resembles
0.666
#
with
denominator
<
15
Fraction(0.666).limit_denominator(15)
Fraction(2,
3)
Out[73]:
In [74]: rows
=
[]
for
phase
in
measured_phases:
frac
=
Fraction(phase).limit_denominator(15)
rows.append([phase,
f"{frac.numerator}/{frac.denominator}",
frac.denominator])
#
Print
as
a
table
headers=["Phase",
"Fraction",
"Guess
for
r"]
df
=
pd.DataFrame(rows,
columns=headers)
print(df)
Phase
Fraction
Guess
for
r
0
0.00
0/1
1
1
0.75
3/4
4
2
0.50
1/2
2
3
0.25
1/4
4
In [75]: rows
=
[]
for
phase
in
measured_phases:
frac
=
Fraction(phase).limit_denominator(15)
rows.append([phase,
f"{frac.numerator}/{frac.denominator}",
frac.denominator])
#
Print
as
a
table
headers=["Phase",
"Fraction",
"Guess
for
r"]
df
=
pd.DataFrame(rows,
columns=headers)
print(df)
Phase
Fraction
Guess
for
r
0
0.00
0/1
1
1
0.75
3/4
4
2
0.50
1/2
2
3
0.25
1/4
4
In [76]: a2jmodN(7, 2049, 53)
NameError
Traceback
(most
recent
call
last)
/var/folders/nd/vzx00q0j5615sxcrs0qldzh80000gn/T/ipykernel_2157/108858282.py
in
<module>
>
1
a2jmodN(7,
2049,
53)
NameError:
name
'a2jmodN'
is
not
defined
In [ ]: