3-1. How to use Qulacs, the world’s fastest simulator

In Chapter 2, we used SymPy to actually run quantum algorithms, but SymPy is not good at large-scale and high-speed calculations because it is specialized for algebraic calculations. In this section, we introduce how to use Qulacs, a quantum computer simulator with the world’s fastest running speeds. Qulacs is implemented in C++ and runs very fast, but can be easily implemented through the Python interface.

In addition, there are other quantum computer simulators such as IBM’s Qiskit, Rigetti Conmputing’s PyQuil (Cloud Quantum Computer), Google’s Cirq, and Microsoft’s Q#.See also this article for more information on PyQuil.

installing Qulacs

You can easily install Qulacs uning pip. For more detail, please check Qulacs document.

[ ]:
## Please run only on Google colaboratory and local environment without Qulacs
!pip install qulacs

## Run only if you are in a Google Colaboratory / (Linux or Mac) jupyter notebook environment.
## Qulacs errors will be output normally.
!pip3 install wurlitzer
%load_ext wurlitzer

How to use Qulacs (1): Quantum states

Creating a quantum state

Qulacs can generate a quantum state (QuantumState class) of \(n\) qubits with the following code. The generated quantum state is initialized to \(|0\rangle^{\otimes n}\).

[1]:
from qulacs import QuantumState

# generate a 5-qubit state
n = 5
state = QuantumState(n)

## The initial state is |000000>
state.set_zero_state()

If \(n\) is very large, or if there is insufficient memory, the quantum state cannot be generated.

Getting Quantum State Data

Using QuantumState.get_vector(), you can get a \(2^n\) long array representing a quantum state. Note that this is a very heavy operation, especially if you create quantum states on a GPU or with large \(n\).

[2]:
from qulacs import QuantumState

n = 5
state = QuantumState(n)
state.set_zero_state()

# Get state vector as a numpy array
data = state.get_vector()
print(data)
[1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j]

Initializing the quantum state

The generated quantum state can be initialized using binary numbers (set_computational_basis) or to a random state (set_Haar_random_state).

[3]:
from qulacs import QuantumState

n = 5
state = QuantumState(n)
state.set_zero_state()

# initialize to |00101>
state.set_computational_basis(0b00101)
print(state.get_vector())

# Generate random initial state
state.set_Haar_random_state()
print(state.get_vector())

# Generate a random initial state by specifying a seed
seed = 0
state.set_Haar_random_state(seed)
print(state.get_vector())
[0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j]
[-0.02437065-0.24658958j -0.01613144+0.13503688j -0.18994795-0.12319831j
  0.06504845+0.16830255j -0.21598901+0.18730307j -0.16812459-0.02947785j
 -0.19220647-0.09961786j  0.10572396+0.00775966j -0.0680691 -0.02781797j
 -0.13307298+0.16938736j  0.03958908+0.10715579j  0.01297565-0.13433703j
 -0.06164082-0.07189014j -0.07461534+0.18664457j  0.12610337-0.12079184j
  0.04721677+0.05207131j -0.01081212+0.24851703j  0.02671697-0.00200128j
 -0.03405007-0.3028626j   0.04368712-0.047096j    0.27087639+0.20331916j
 -0.11928844+0.02439986j  0.09817583+0.22850291j -0.08822065-0.01851942j
 -0.08319959+0.12819258j  0.00568074-0.02627961j  0.08279638-0.10588521j
  0.04417022-0.01161947j -0.05749897-0.22828609j -0.00629634+0.04739956j
 -0.09164645-0.10147242j  0.13382742+0.04209609j]
[ 0.09232558+0.06460115j  0.14544754-0.10413526j  0.11300793-0.02455806j
  0.00811251+0.2426378j  -0.01116588+0.23770313j -0.10691448+0.0487731j
 -0.01654446+0.17073103j  0.22250403+0.01934699j  0.04728154+0.22585226j
  0.04475383+0.20375993j -0.10592159+0.10428549j -0.10175932-0.04016904j
  0.04241271+0.08723859j  0.18205362+0.06190871j  0.14103367-0.12925877j
 -0.08269267+0.08879486j -0.14479848-0.0183179j  -0.32601567+0.06762062j
  0.03482754+0.04464901j  0.09181499+0.05497985j  0.06870746+0.12628442j
 -0.00624006-0.21793139j -0.11181371+0.2659879j  -0.04589826+0.00891387j
 -0.04058365+0.30265587j -0.13894575-0.04392724j -0.03499327+0.0184768j
  0.05033425-0.07376874j  0.07124237+0.15451312j  0.09319498+0.08341551j
 -0.03002195-0.14677347j -0.05309219+0.10184815j]

Copy and load quantum state data

You can copy (copy) a quantum state and load (load) data from another quantum state.

[1]:
from qulacs import QuantumState

n = 5
state = QuantumState(n)
state.set_computational_basis(0b00101)

# Copy and create a new quantum state
second_state = state.copy()
print(second_state.get_vector())

# Create a new quantum state and copy the vector of existing states
third_state = QuantumState(n)
third_state.load(state)
print(third_state.get_vector())
[0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j]
[0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j]

Calculations on quantum states

In addition to the above, various other operations are possible on quantum states (QuantumState).

[5]:
from qulacs import QuantumState

n = 5
state = QuantumState(n)
state.set_Haar_random_state()

# Calculation of norm (renamed from get_norm to get_squared_norm in qulacs v0.1.8)
norm = state.get_squared_norm()
print("squared_norm : ", norm)

# compute entropy when measured in Z basis
entropy = state.get_entropy()
print("entropy : ",entropy)

# Calculate the probability of getting 0 when measuring index-th qubit in Z basis
index = 3
zero_probability = state.get_zero_probability(index)
print("prob_meas_3rd : ",zero_probability)

# Calculation of the marginal probability (below is an example of the probability that 0,3-th qubit is measured as 0 and 1,2-th qubit is measured as 1)
value_list = [0,1,1,0,2]
marginal_probability = state.get_marginal_probability(value_list)
print("marginal_prob : ",marginal_probability)
squared_norm :  1.0
entropy :  3.0719687986623603
prob_meas_3rd :  0.5740657157322318
marginal_prob :  0.04265173032311748

inner product of quantum states

The inner product can be calculated with the inner_product function.

[6]:
from qulacs import QuantumState
from qulacs.state import inner_product

n = 5
state_bra = QuantumState(n)
state_ket = QuantumState(n)
state_bra.set_Haar_random_state()
state_ket.set_computational_basis(0)

# Calculate the inner product value
value = inner_product(state_bra, state_ket)
print(value)
(0.07448994812927281-0.1223698589819414j)

Releasing a quantum state

Quantum states can be forcibly released from memory using del. It is useful when memory is tight. Also, quantum states will be released when it is no longer used without del.

[7]:
from qulacs import QuantumState

n = 5
state = QuantumState(n)

# Release a quantum state
del state

Getting detailed information about a quantum state

If you print an object of QuantumState class directly, you can get the information of the quantum state.

[8]:
from qulacs import QuantumState

n = 5
state = QuantumState(n)

print(state)
 *** Quantum State ***
 * Qubit Count : 5
 * Dimension   : 32
 * State vector :
(1,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)

How to use Qulacs (2): Quantum gate

Creation and action of quantum gates

The quantum gates implemented by default are defined in the qulacs.gate module.

[9]:
import numpy as np
from qulacs import QuantumState
from qulacs.gate import X, RY, DenseMatrix

n = 3
state = QuantumState(n)
state.set_zero_state()
print(state.get_vector())

# X operation on 1st-qubit (|000> -> |010>)
index = 1
x_gate = X(index)
x_gate.update_quantum_state(state)
print(state.get_vector())

# pi/4.0 rotation on 1st-qubit with Y-pauli
angle = np.pi / 4.0
ry_gate = RY(index, angle)
ry_gate.update_quantum_state(state)
print(state.get_vector())

# Apply gate created by gate matrix to 2nd-qubit
dense_gate = DenseMatrix(2, [[0,1],[1,0]])
dense_gate.update_quantum_state(state)
print(state.get_vector())

# Release gates
del x_gate
del ry_gate
del dense_gate
[1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j]
[0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j]
[0.38268343+0.j 0.        +0.j 0.92387953+0.j 0.        +0.j
 0.        +0.j 0.        +0.j 0.        +0.j 0.        +0.j]
[0.        +0.j 0.        +0.j 0.        +0.j 0.        +0.j
 0.38268343+0.j 0.        +0.j 0.92387953+0.j 0.        +0.j]

The predefined gates are as follows.

  • single-qubit Pauli operation: Identity, X, Y, Z

  • single-qubit Clifford operation : H, S, Sdag, T, Tdag, sqrtX, sqrtXdag, sqrtY, sqrtYdag

  • two-qubit Clifford operation : CNOT, CZ, SWAP

  • single-qubit Pauli rotation : RX, RY, RZ

  • General Pauli operation : Pauli, PauliRotation

  • IBMQ basis-gate : U1, U2, U3

  • General gate : DenseMatrix

  • Measurement : Measurement

  • Noise : BitFlipNoise, DephasingNoise, IndepenedentXZNoise, DepolarizingNoise

The rotation gates, RX, RY, RZ, and PauliRotation, operate on a given Pauli operator \(P\) with \(\exp(i\frac{\theta}{2}P)\) for the argument \(\theta\). See API documentation for details on each gate.

Quantum gate synthesis

A new single quantum gate can be generated by synthesizing quantum gates that act in succession.

[10]:
import numpy as np
from qulacs import QuantumState
from qulacs.gate import X, RY, merge

n = 3
state = QuantumState(n)
state.set_zero_state()

index = 1
x_gate = X(index)
angle = np.pi / 4.0
ry_gate = RY(index, angle)

# Synthesize gates to create a new gate
# First argument acts first
x_and_ry_gate = merge(x_gate, ry_gate)
x_and_ry_gate.update_quantum_state(state)
print(state.get_vector())
[0.38268343+0.j 0.        +0.j 0.92387953+0.j 0.        +0.j
 0.        +0.j 0.        +0.j 0.        +0.j 0.        +0.j]

Sum of quantum gate matrices

It is generally difficult to perform this operation on a real quantum computer, but it is possible to sum the gate elements of quantum gates. (Currently, it is recommended not to use the sum in the case where there is a control-qubit since the operation is undefined.)

[11]:
import numpy as np
from qulacs import QuantumState
from qulacs.gate import P0,P1,add, merge, Identity, X, Z

gate00 = merge(P0(0),P0(1))
gate11 = merge(P1(0),P1(1))
# |00><00| + |11><11|
proj_00_or_11 = add(gate00, gate11)
print(proj_00_or_11)

gate_ii_zz = add(Identity(0), merge(Z(0),Z(1)))
gate_ii_xx = add(Identity(0), merge(X(0),X(1)))
proj_00_plus_11 = merge(gate_ii_zz, gate_ii_xx)
# ((|00>+|11>)(<00|+<11|))/2 = (II + ZZ)(II + XX)/4
proj_00_plus_11.multiply_scalar(0.25)
print(proj_00_plus_11)
 *** gate info ***
 * gate name : DenseMatrix
 * target    :
 0 : commute
 1 : commute
 * control   :
 * Pauli     : no
 * Clifford  : no
 * Gaussian  : no
 * Parametric: no
 * Diagonal  : no
 * Matrix
(1,0) (0,0) (0,0) (0,0)
(0,0) (0,0) (0,0) (0,0)
(0,0) (0,0) (0,0) (0,0)
(0,0) (0,0) (0,0) (1,0)

 *** gate info ***
 * gate name : DenseMatrix
 * target    :
 0 : commute
 1 : commute
 * control   :
 * Pauli     : no
 * Clifford  : no
 * Gaussian  : no
 * Parametric: no
 * Diagonal  : no
 * Matrix
(0.5,0)   (0,0)   (0,0) (0.5,0)
  (0,0)   (0,0)   (0,0)   (0,0)
  (0,0)   (0,0)   (0,0)   (0,0)
(0.5,0)   (0,0)   (0,0) (0.5,0)

Special and general quantum gates

Basic quantum gates in Qulacs can be divided into the following two categories

  • Special gates: those that have a dedicated accelerated function for the gate’s action.

  • General gates: gates that maintain a gate matrix and act by multiplying the matrix.

The former is faster than the latter because a dedicated function has been created for the former. However, it is not possible to change the action of the quantum gate later, such as increasing the number of control qubits. If you want to make such changes, you need to convert the special gate to a general gate, which can be achieved with gate.to_matrix_gate.

[12]:
import numpy as np
from qulacs import QuantumState
from qulacs.gate import to_matrix_gate, X
n = 3
state = QuantumState(n)
state.set_zero_state()

index = 0
x_gate = X(index)
x_mat_gate = to_matrix_gate(x_gate)

# Apply gates only if 1st-qubit is 0
control_index = 1
control_with_value = 0
x_mat_gate.add_control_qubit(control_index, control_with_value)

x_mat_gate.update_quantum_state(state)
print(state.get_vector())
[0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j]

Getting the gate matrix of a quantum gate

You can get the gate matrix of the generated quantum gate. (control qubits, etc. are not included in the gate matrix.) Be especially careful with gates that do not have a gate matrix (e.g. Pauli rotation gates with \(n\) qubits), as they require a lot of memory and time.

[13]:
from qulacs.gate import X, to_matrix_gate
gate = X(0)
print(gate)
print(to_matrix_gate(gate))
 *** gate info ***
 * gate name : X
 * target    :
 0 : commute X
 * control   :
 * Pauli     : yes
 * Clifford  : yes
 * Gaussian  : no
 * Parametric: no
 * Diagonal  : no

 *** gate info ***
 * gate name : DenseMatrix
 * target    :
 0 : commute X
 * control   :
 * Pauli     : no
 * Clifford  : no
 * Gaussian  : no
 * Parametric: no
 * Diagonal  : no
 * Matrix
(0,0) (1,0)
(1,0) (0,0)

General Quantum Gate Realization

Using qulacs.gate.DenseMatrix, you can generate a gate from a general matrix.

[14]:
from qulacs.gate import DenseMatrix

# For 1-qubit gate.
gate = DenseMatrix(0, [[0,1],[1,0]])
print(gate)

# For 2-qubit gate.
gate = DenseMatrix([0,1], [[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]])
print(gate)
 *** gate info ***
 * gate name : DenseMatrix
 * target    :
 0 : commute
 * control   :
 * Pauli     : no
 * Clifford  : no
 * Gaussian  : no
 * Parametric: no
 * Diagonal  : no
 * Matrix
(0,0) (1,0)
(1,0) (0,0)

 *** gate info ***
 * gate name : DenseMatrix
 * target    :
 0 : commute
 1 : commute
 * control   :
 * Pauli     : no
 * Clifford  : no
 * Gaussian  : no
 * Parametric: no
 * Diagonal  : no
 * Matrix
(1,0) (0,0) (0,0) (0,0)
(0,0) (1,0) (0,0) (0,0)
(0,0) (0,0) (0,0) (1,0)
(0,0) (0,0) (1,0) (0,0)

How to use Qulacs (3): Quantum circuits

Composition of quantum circuits

A quantum circuit (QuantumCircuit class) is represented as a set of quantum gates and can be composed in the following way.

[15]:
from qulacs import QuantumState, QuantumCircuit
from qulacs.gate import Z
n = 5
state = QuantumState(n)
state.set_zero_state()

# define a quantum circuit
circuit = QuantumCircuit(n)

# add hadamard gate to a quantum circuit
for i in range(n):
    circuit.add_H_gate(i)

# you can create a gate and add it
for i in range(n):
    circuit.add_gate(Z(i))

# apply a quantum cirtuit to a quantum state
circuit.update_quantum_state(state)

print(state.get_vector())
[ 0.1767767+0.j -0.1767767-0.j -0.1767767-0.j  0.1767767+0.j
 -0.1767767-0.j  0.1767767+0.j  0.1767767+0.j -0.1767767-0.j
 -0.1767767-0.j  0.1767767+0.j  0.1767767+0.j -0.1767767-0.j
  0.1767767+0.j -0.1767767-0.j -0.1767767-0.j  0.1767767+0.j
 -0.1767767-0.j  0.1767767+0.j  0.1767767+0.j -0.1767767-0.j
  0.1767767+0.j -0.1767767-0.j -0.1767767-0.j  0.1767767+0.j
  0.1767767+0.j -0.1767767-0.j -0.1767767-0.j  0.1767767+0.j
 -0.1767767-0.j  0.1767767+0.j  0.1767767+0.j -0.1767767-0.j]

Note that the quantum circuit added by add_gate is released together with the quantum circuit when it is released. Therefore, the assigned gate cannot be reused. If you want to reuse a gate given as an argument, you need to make a copy of it using gate.copy or use the add_gate_copy function.

Calculating and Optimizing Depth of a Quantum Circuit

Sometimes it is possible to reduce the number of quantum gates by grouping them together into a single quantum gate, thereby reducing the numerical computation time. (Of course, if the number of target qubits increases, or if the qubits with dedicated functions are combined into a quantum gate with no dedicated function, the total reduction in computation time depends on the situation.)

The code below uses the optimize function to repeatedly synthesize the quantum gates of a quantum circuit using the greedy method until there are three target qubits.

[16]:
from qulacs import QuantumCircuit
from qulacs.circuit import QuantumCircuitOptimizer
n = 5
depth = 10
circuit = QuantumCircuit(n)
for d in range(depth):
    for i in range(n):
        circuit.add_H_gate(i)

# calculate depth (depth =10)
print(circuit.calculate_depth())

# optimization
opt = QuantumCircuitOptimizer()

# the maximum size of quantum gate
max_block_size = 1
opt.optimize(circuit, max_block_size)

# calculate depth(depth=1)
print(circuit.calculate_depth())
10
1

Debugging Quantum Circuit Information

When you print a quantum circuit, you can see the statistical information of the gates in the quantum circuit, etc.

[17]:
from qulacs import QuantumCircuit
from qulacs.circuit import QuantumCircuitOptimizer
n = 5
depth = 10
circuit = QuantumCircuit(n)
for d in range(depth):
    for i in range(n):
        circuit.add_H_gate(i)
print(circuit)
*** Quantum Circuit Info ***
# of qubit: 5
# of step : 10
# of gate : 50
# of 1 qubit gate: 50
Clifford  : yes
Gaussian  : no


How to use Qulacs (4): Observables

(Readers who are not familiar with quantum mechanical observables can read this section later.)
As you will learn the details in the next chapter, Chapter 4, in quantum mechanics, physical quantities are represented by Hermitian operators\(A\), also called observable (Hermitian operators satisfy \(A^\dagger=A\)). If we perform what is called a “projective measurement” of \(A\) for the state \(|\psi\rangle\),
\[|\psi\rangle = \sum_i c_i |a_i\rangle\]

the value \(a_i\) is obtained with probability \(|c_i|^2\) depending on the coefficients of \(|\psi\rangle\) expanded in the eigenstate \(\{|a_i\rangle\}_i\) of A (eigenvalue \(a_i\)).

Its expected value is \(\langle\psi|A|\psi\rangle\).

Generating Observables

In Qulacs, an observable is represented as a set of (direct product of) Pauli operators \(X,Y,Z\). (Since Hermitian operators are always represented by a sum of direct products of Pauli operators.) The Pauli operator can be defined as follows.

[18]:
from qulacs import Observable
n = 5
coef = 2.0
# define Pauli operators 2.0 X_0 X_1 Y_2 Z_4
Pauli_string = "X 0 X 1 Y 2 Z 4"
observable = Observable(n)
observable.add_operator(coef,Pauli_string)

Evaluating Observables

The expected value of an observable can be evaluated for a state.

[19]:
from qulacs import Observable, QuantumState

n = 5
coef = 2.0
Pauli_string = "X 0 X 1 Y 2 Z 4"
observable = Observable(n)
observable.add_operator(coef,Pauli_string)

state = QuantumState(n)
state.set_Haar_random_state()

# calculating expected value
value = observable.get_expectation_value(state)
print(value)
-0.027383733754500428

How to use Qulacs (5): Variational quantum circuits

If you define a quantum circuit as a ParametricQuantumCircuit class, in addition to the usual QuantumCircuit class functions, you can use some functions that are useful for optimizing a quantum circuit using variational methods. This is very useful when implementing variational quantum circuits, which you will learn in Chapter 5.

Examples of Variational Quantum Circuit Applications

Quantum gates with a single rotation angle (X-rot, Y-rot, Z-rot, multi_qubit_pauli_rotation) can be added to a quantum circuit as parametric quantum gates. For quantum gates added as parametric gates, the number of parametric gates can be taken out after the quantum circuit is configured, or the rotation angle can be changed later.

[20]:
from qulacs import ParametricQuantumCircuit
from qulacs import QuantumState
import numpy as np

n = 5
depth = 10

# construct parametric quantum circuit with random rotation
circuit = ParametricQuantumCircuit(n)
for d in range(depth):
    for i in range(n):
            angle = np.random.rand()
            circuit.add_parametric_RX_gate(i,angle)
            angle = np.random.rand()
            circuit.add_parametric_RY_gate(i,angle)
            angle = np.random.rand()
            circuit.add_parametric_RZ_gate(i,angle)
    for i in range(d%2, n-1, 2):
            circuit.add_CNOT_gate(i,i+1)

# add multi-qubit Pauli rotation gate as parametric gate (X_0 Y_3 Y_1 X_4)
target = [0,3,1,4]
pauli_ids = [1,2,2,1]
angle = np.random.rand()
circuit.add_parametric_multi_Pauli_rotation_gate(target, pauli_ids, angle)

# get variable parameter count, and get current parameter
parameter_count = circuit.get_parameter_count()
param = [circuit.get_parameter(ind) for ind in range(parameter_count)]

# set 3rd parameter to 0
circuit.set_parameter(3, 0.)

# update quantum state
state = QuantumState(n)
circuit.update_quantum_state(state)

# output state and circuit info
print(state)
print(circuit)
 *** Quantum State ***
 * Qubit Count : 5
 * Dimension   : 32
 * State vector :
  (-0.0673932,0.0932352)
   (0.0793816,0.0803179)
 (-0.0240998,-0.0720735)
    (0.0267232,0.103591)
  (-0.089328,-0.0454438)
  (-0.0273612,-0.172908)
   (0.0753719,-0.185454)
    (0.120598,0.0489211)
(-0.0433311,-0.00542669)
   (0.407492,-0.0683546)
    (0.0712898,0.029486)
   (-0.0374001,0.100097)
    (0.0395997,0.166802)
    (0.113313,0.0278069)
  (0.00456149,0.0702255)
    (-0.121551,0.306851)
  (-0.0113109,0.0106071)
    (0.158906,0.0897413)
   (0.276642,0.00709558)
   (-0.163862,0.0615158)
  (-0.0507503,0.0898438)
    (0.221342,0.0332379)
   (-0.125741,-0.130305)
    (0.0463867,0.225922)
   (0.0493533,-0.127222)
   (-0.235716,0.0564754)
   (0.0206978,-0.129814)
     (0.108871,0.107555)
  (0.0917828,-0.0557612)
     (0.180461,0.121747)
   (0.0456678,0.0580318)
    (0.0311144,0.203219)

*** Quantum Circuit Info ***
# of qubit: 5
# of step : 41
# of gate : 171
# of 1 qubit gate: 150
# of 2 qubit gate: 20
# of 3 qubit gate: 0
# of 4 qubit gate: 1
Clifford  : no
Gaussian  : no

*** Parameter Info ***
# of parameter: 151

Conclusion

The above run-through has shown how to use Qulacs, which is a very fast and easy-to-use library once you get used to it. For more details on the various functions, please refer to the official API Documentation and Tutorial.