Here, I introduce the quantum computing library under development written in Julia.
Codes written in Julia is intuitive, easily be fast than Python, and possibly as fast as C++.
Glowing number of people are starting to use Julia.
Quantum computing (QC) simulation is hard computational problem so Julia is a good option. Among the popular QC libraries, the fastest are Qulacs and Yao. Qulacs is written in Python with parallelized C/C++ backend and Yao is written in Julia. Others are mainly written in Python.
However, the most significant problem in QC simulation is memory amount to keep quantum states with high dimension Hilbert space. It’s not a problem of programming language.
To relax the restriction of memory, tensor network based methods are presented. These methods don't keep quantum states as ordinary state vectors. Instead, quantum states are represented, such as Matrix Product State (MPS), or graph network of tensors. It enable simulation of QC with more qubits than state vector methods.
The developing library adopts one of the tensor network based method, "Undirected graph method" (arXiv:1712.05384 [quant-ph]).
It calculate quantum circuit using Feynman path method with graph contraction.
The github repositroy is here (https://github.com/ryuNagai/julia_QC).
To use the library, as for now, please clone the files and see "Julia_Module.ipynb".
I provide a simple demonstration for memory saving calculation.
First, look at Qiskit's "statevector_simulator".
I created functions to make complicated quantum circuit with {CZ, RX(pi/2), RY(pi/2), T} gates.
"n" is number of qubits. "depth" is number of layers. There are two kinds of layer, "CZ layer" and "Single qubit gate layer", which are alternately added to circuit.
import qiskit
from qiskit.providers.aer import QasmSimulator
import numpy as np
import time
def random_qc(n, depth):
qc = qiskit.QuantumCircuit(n)
for i in range(n):
qc.h((n-1) - i)
qc.barrier()
for d in range(depth):
cz_config(qc, n, d)
qc.barrier()
single_config(qc, n, d)
qc.barrier()
return qc
def cz_config(qc, n, select):
if select%2 == 1:
for i in range(0, np.int(n), 2):
qc.cz((n-1) - i, (n-1) - (i + 1))
elif select%2 == 0:
for i in range(1, np.int(n)-1, 2):
qc.cz((n-1) - i, (n-1) - (i + 1))
def single_config(qc, n, select):
for i in range(n):
if (i + select) % 3 == 0:
qc.rx(np.pi/2, (n-1) - i)
elif (i + select) % 3 == 1:
qc.ry(np.pi/2, (n-1) - i)
else:
qc.t((n-1) - i)
qc = random_qc(30, 8)
backend = qiskit.Aer.get_backend('statevector_simulator')
start = time.time()
results = qiskit.execute(qc, backend=backend).result()
elapsed_time = time.time() - start
state_vec = results.get_statevector(qc)
print(elapsed_time)
------------------------------------------------------------------
QiskitError: 'Data for experiment "circuit152" could not be found.'
Checking results, it is caused by insufficient memory error.
print(results.status)
ERROR: [Experiment 0] Insufficient memory to run circuit "circuit152" using the statevector simulator.
OK, let's create same quantum circuit in my Julia library.
include("./Juliaqat.jl")
using .Juliaqat
function random_qc(n::Int, depth::Int)
input = Input(n)
Hgates = []
for i in 1:n
push!(Hgates, H(i))
end
apply!(input, Hgates)
for d in 1:depth
CZgates = cz_config(n, d)
apply!(input, CZgates)
Single_gates = single_config(n, d)
apply!(input, Single_gates)
end
return input
end
function cz_config(n::Int, select::Int)
gates = []
if select%2 == 0
for i in 1:2:convert(Int, n)
push!(gates, CZ(i, i+1))
end
return gates
elseif select%2 == 1
for i in 2:2:convert(Int, n-1)
push!(gates, CZ(i, i+1))
end
return gates
end
end
function single_config(n::Int, select::Int)
gates = []
single_gates = [RX, RY, T]
for i in 1:n
if (i + (select-1))%3 == 1
push!(gates, single_gates[1](i, pi/2))
elseif (i + (select-1))%3 == 2
push!(gates, single_gates[2](i, pi/2))
else
push!(gates, single_gates[3](i))
end
end
return gates
end
Note the difference of bit order and indexing between Qiskit and my library.
Run this circuit as following.
n = 30
input = random_qc(n, 8)
device = get_device("UndirectedGraph", zeros(Int, n))
@time state = execute(input, device)
Result is following.
"Undirected graph method" calculate only one amplitude of output state vector at once.
Here I calculate only the amplitude of |00...0>. 2^n times calculation is needed to get full state vector.
0.025648 seconds (202.79 k allocations: 9.007 MiB)
-0.004294803611085845 - 0.0003431775694928119im
Yes, I can get output of 30 qubits complicated quantum circuit with lap-top PC (8GB memory).
As a note, getting full state vector need mach more time (2^n times of 25 ms in this case).
In addition, if the sufficient memory is given, Qiskit's "statevector_simulator" is much more faster.
"Undirected graph method" can relax the memory restriction but it not always equal to fast calculation.
Consequently, correct choice of state vector simulators and tensor network based simulators can push up the limitation of QC simulation with classical computer.