Từ lý thuyết lượng tử đến Quantum Neural Network (phần 2) – Simple Quantum Neural Network

Một số kỹ năng và kiến thức và kỹ năng cần nắm

Mình khuyến khích mọi người trước khi đọc bài này thì nên khám phá Quantum Computing hoặc đọc bài ra mắt cơ bản về giám sát lượng tử mà mình đã viết để trọn vẹn trọn vẹn trọn vẹn trọn vẹn hoàn toàn hoàn toàn hoàn toàn hoàn toàn hoàn toàn hoàn toàn hoàn toàn hoàn toàn hoàn toàn hoàn toàn hoàn toàn hoàn toàn hoàn toàn hoàn toàn hoàn toàn hoàn toàn có thể hiểu rõ hơn. Tuy nhiên, mình vẫn sẽ tóm tắt lại những ý chính của phần trước ở đây đồng thời có thêm một số ít ít ít đoạn code để những bạn dễ tưởng tượng hơn. Ở bài viết này mình sẽ lược qua một số kiến thức tiếp theo về quantum circuit cũng như một số thao tác cơ bản với thư viện Paddle Quantum để phong cách thiết kế kiến thiết thiết kế kiến thiết thiết kế thiết kế kiến thiết xây dựng Quantum Neural Network vì thư viện này khá dễ dùng và tường minh, những bài viết sau mình sẽ trình làng đến những bạn một thư viện khác là IBM Qiskit và tập trung chuyên sâu khai thác vào thư viện này vì tính linh động và ứng dụng cao của nó khi có thể phối hợp với Pytorch để xây dựng Model và can thiệp sâu vào quy trình training cũng như có thể xây dựng nhiều hướng như Quantum thuần hay Quantum – Classical,… Hơn nữa còn có thể chạy code với máy tính lượng tử được tương hỗ bởi IBM =)) Còn một điều nữa khá quan trọng là những phần code được xây dựng với hai thư viện mà mình ra mắt với những bạn hoàn toàn có thể chạy ở máy tính thường thì với mục đích test thuật toán và mô phỏng bởi simulator của IBM hay Baidu cung cấp, trong tương lai nếu có dự tính thì tổng thể hàng loạt tất cả tất cả tất cả tất cả tất cả tất cả tất cả tất cả tất cả tất cả tất cả tất cả tất cả tất cả tất cả chúng ta cũng hoàn toàn có thể thuận tiện request account để có thể chạy ở máy tính lượng tử thật sự.

Đặt vấn đề

Tính toán toán lượng tử (Quantum Computing) sử dụng những hiện tượng kỳ lạ độc lạ trong vật lý lượng tử (chồng chất lượng tử, giao thoa lượng tử và rối lượng tử) để thiết kế những thuật toán và giúp giải quyết và giải quyết và xử lý những trách nhiệm đơn cử trong vật lý, hóa học và thuyết tối ưu hóa. Một số quy mô đo lường và thống kê lượng tử hiện có bao gồm Tính toán lượng tử đoạn nhiệt (AQC) dựa trên định lý đoạn nhiệt và Tính toán lượng tử dựa trên phép measure (MBQC). Phần giới thiệu này sẽ tập trung vào quy mô vi mạch lượng tử được sử dụng thoáng rộng nhất. Trong những mạch lượng tử, đơn vị chức năng chức năng chức năng đo lường và thống kê cơ bản là bit lượng tử (qubit), tựa như như như như khái niệm bit trong máy tính cổ điển. Các bit cổ xưa chỉ có thể ở một trong hai trạng thái 0 hoặc 1. Để so sánh, những qubit không chỉ có thể ở nhiều trạng thái cơ bản khác nhau mà còn ở trạng thái chồng chất. Mô hình mạch lượng tử sử dụng những cổng logic lượng tử để tinh chỉnh và điều khiển trạng thái của những qubit này.

Qubit là gì?

Biểu diễn toán học

Trong cơ học lượng tử, trạng thái của hệ lượng tử hai cấp (ví dụ: spin của điện tử) có thể được biểu lộ dưới dạng một vectơ trạng thái thu được trải qua những tổng hợp tuyến tính của cơ sở trực chuẩn sau đây:

∣0⟩:=[10],∣1⟩:=[01](1)ket{0} := begin{bmatrix} 1 \ 0 end{bmatrix}, quad ket{1} := begin{bmatrix} 0 \ 1 end{bmatrix} tag{1}
∣0⟩:=[10​],∣1⟩:=[01​](1)

Biểu diễn vectơ ở đây tuân theo ký hiệu Dirac (bra-ket) trong vật lý lượng tử. Cơ sở trực chuẩn {∣0⟩,∣1⟩}{ket{0}, ket{1}}{∣0⟩,∣1⟩} chính là cơ sở tính toán. Về mặt vật lý, người ta có thể coi ∣0⟩ket{0}∣0⟩ và ∣1⟩ket{1}∣1⟩ lần lượt là trạng thái năng lượng cơ bản và trạng thái kích thích của một nguyên tử. Tất cả những trạng thái thuần túy có thể có của qubit có thể được coi là vectơ chuẩn hóa trong khoảng chừng trống Hilbert hai chiều. Hơn nữa, những trạng thái nhiều qubit có thể được màn màn biểu diễn bằng những vectơ đơn vị trong khoảng trống Hilbert nhiều chiều mà cơ sở là tích tensor của {∣0⟩,∣1⟩}{ket{0}, ket{1}}{∣0⟩,∣1⟩}. Ví dụ, trạng thái lượng tử 2 qubit có thể được biểu diễn bằng một vectơ đơn vị phức trong khoảng trống Hilbert 4 chiều với cơ sở trực chuẩn:

{∣00⟩=∣0⟩⊗∣0⟩:=[1000],∣01⟩=∣0⟩⊗∣1⟩:=[0100],∣10⟩=∣1⟩⊗∣0⟩:=[0010],∣11⟩=∣1⟩⊗∣1⟩:=[0001]}(2)left{
ket{00} = ket{0}otimes ket{0} := begin{bmatrix} 1 \ 0 \ 0 \ 0 end{bmatrix}, quad
ket{01} = ket{0}otimes ket{1} := begin{bmatrix} 0 \ 1 \ 0 \ 0 end{bmatrix}, quad
ket{10} = ket{1}otimes ket{0} := begin{bmatrix} 0 \ 0 \ 1 \ 0 end{bmatrix}, quad
ket{11} = ket{1}otimes ket{1} := begin{bmatrix} 0 \ 0 \ 0 \ 1 end{bmatrix}
right} qquad quad (2)⎩⎨⎧​∣00⟩=∣0⟩⊗∣0⟩:=⎣⎡​1000​⎦⎤​,∣01⟩=∣0⟩⊗∣1⟩:=⎣⎡​0100​⎦⎤​,∣10⟩=∣1⟩⊗∣0⟩:=⎣⎡​0010​⎦⎤​,∣11⟩=∣1⟩⊗∣1⟩:=⎣⎡​0001​⎦⎤​⎭⎬⎫​(2)

Theo quy ước, vị trí ngoài cùng bên trái trong ký hiệu ket đại diện thay mặt thay mặt thay mặt cho qubit tiên phong q0q_0q0​, vị trí thứ hai đại diện cho qubit thứ hai q1q_1q1​,… .Biểu tượng ⊗otimes⊗ biểu thị phép nhân của tích tensor, phép nhân hoạt động giải trí giải trí như sau: cho hai ma trận Am×nA_{mtimes n}Am×n​ và Bp×qB_{ptimes q}Bp×q​ khi đó tích tensor của A,BA,BA,B là:

A⊗B=[a11B⋯a1nB⋮⋱⋮am1B⋯amnB](mp)×(nq)(3)A otimes B =
begin{bmatrix}
a_{11}B & cdots & a_{1n}B\
vdots & ddots & vdots \
a_{m1}B & cdots & a_{m n}B
end{bmatrix}_{(mp)times (nq)}
tag{3}A⊗B=⎣⎡​a11​B⋮am1​B​⋯⋱⋯​a1n​B⋮amn​B​⎦⎤​(mp)×(nq)​(3)

Bất kỳ trạng thái lượng tử qubit đơn ∣ψ⟩ket{psi}∣ψ⟩ nào cũng có thể được viết dưới dạng tích hợp tuyến tính (chồng chất) của những vectơ cơ sở ∣0⟩ket{0}∣0⟩ và ∣1⟩ket{1}∣1⟩.

∣ψ⟩=α∣0⟩+β∣1⟩:=[αβ].(4)|psirangle = alpha |0rangle + beta |1rangle:= begin{bmatrix} alpha \ beta end{bmatrix}. tag{4}
∣ψ⟩=α∣0⟩+β∣1⟩:=[αβ​].(4)

trong đó αalphaα và βbetaβ là những số phức được gọi là biên độ xác suất. Theo Quy tắc Sinh trong cơ học lượng tử, Xác Suất để tìm thấy qubit ở trạng thái ∣0⟩ket{0}∣0⟩ là ∣α∣2|alpha|^2∣α∣2; và Tỷ Lệ của ∣1⟩ket{1}∣1⟩ là ∣β∣2|beta|^2∣β∣2. Vì tổng những Tỷ Lệ bằng 1, nên người ta đưa ra ràng buộc:∣α∣2+∣β∣2=1|alpha|^2 + |beta|^2 = 1∣α∣2+∣β∣2=1.

Biểu diễn bloch sphere

Quả cầu Bloch là một quả cầu có bán kính đơn vị. Nó được sử dụng để biểu diễn những qubit một cách trực quan. Vị trí của mỗi qubit được xác lập rõ ràng trải qua những tham số θthetaθ và φvarphiφ.

∣ψ⟩=α∣0⟩+β∣1⟩=cos⁡(θ2)∣0⟩+eiφsin⁡(θ2)∣1⟩(5)|psirangle = alpha |0rangle + beta |1rangle= cosbigg(frac{theta}{2}bigg) |0rangle + e^{ivarphi}sinbigg(frac{theta}{2}bigg)|1rangle tag{5}
∣ψ⟩=α∣0⟩+β∣1⟩=cos(2θ​)∣0⟩+eiφsin(2θ​)∣1⟩(5)

Với 0≤φ<2π0 ≤ φ < 2π0≤φ<2π và 0≤θ<π20 ≤ θ < frac{pi}{2}0≤θ<2π​, hay là chỉ với một nửa quả cầu ta đã có thể biểu diễn vô hạn hàng loạt những qubit

Biểu diễn bloch sphere với paddle quantum

Lý thuyết như vậy là đủ rồi, giờ đây chúng ta sẽ mở màn làm quen với thư viện paddle-quantum nhé. Đầu tiên để cài thư viện chúng ta chỉ cẩn chạy dòng lệnh:pip install paddle-quantum trên terminal.

Chúng ta sẽ dùng thử thư viện này để biểu diễn những trạng thái lượng tử của qubit trên bloch sphere :

from paddle_quantum.circuit import UAnsatz
from paddle_quantum.utils import plot_state_in_bloch_sphere, plot_rotation_in_bloch_sphere
import numpy as np
import paddle

# Set random seed
np.random.seed(42)
# Number of samples
num_samples = 15
# Store the sampled quantum states
state = []

for i in range(num_samples):
    
    # Create a single qubit circuit
    cir = UAnsatz(1)
    # Generate random rotation angles
    phi, theta, omega = 2 * np.pi * np.random.uniform(size=3)
    phi = paddle.to_tensor(phi, dtype='float64')
    theta = paddle.to_tensor(theta, dtype='float64')
    omega = paddle.to_tensor(omega, dtype='float64')
    
    # Quantum gate operation
    cir.rz(phi, 0)
    cir.ry(theta, 0)
    cir.rz(omega, 0)
    
    # Store the sampled quantum states
    state.append(cir.run_state_vector())

# Call the Bloch sphere display function, enter the state parameter, and display the vector.
plot_state_in_bloch_sphere(state, show_arrow=True, save_gif=True)

Như trong hình trên, mình đã lấy mẫu ngẫu nhiên 15 lần để tạo ra 15 trạng thái lượng tử qubit đơn khác nhau và hiển thị lần lượt 15 trạng thái lượng tử này trên quả cầu Bloch.

Làm thế nào để diễn đạt “quỹ đạo” hoạt động quay của một trạng thái lượng tử qubit đơn trên quả cầu Bloch?
Lấy ví dụ toán tử đơn nhất của một qubit là phép quay một vectơ trên hình cầu Bloch. Trạng thái ban đầu của mạch lượng tử tương ứng với vị trí ban đầu của vectơ đầu trên quả cầu Bloch, và trạng thái ở đầu cuối của mạch lượng tử tương ứng với vị trí cuối cùng của vectơ sau khi quay trên quả cầu Bloch. Ta xét trạng thái lượng tử của ∣0⟩ket{0}∣0⟩ qua cổng quay Ry(π4)R_y(frac{pi}{4})Ry​(4π​) và Rz(π2)R_z(frac{pi}{2})Rz​(2π​) là trạng thái ban đầu, và trạng thái sau khi qua cổng U3(θ=π2,ϕ=7π8,λ=2π)U3(theta = frac{pi}{2}, phi = frac{7pi}{8}, lambda = 2pi)U3(θ=2π​,ϕ=87π​,λ=2π) là cuối cùng. Code mô tả hoạt động của vector:

# Set the initial state of the quantum state
cir.ry(paddle.to_tensor(np.pi/4, dtype="float64"), 0)
cir.rz(paddle.to_tensor(np.pi/2, dtype="float64"), 0)
init_state = cir.run_density_matrix()

# Unitary operator operation to be performed
theta = np.pi/2
phi = 7*np.pi/8
lam = 2*np.pi
rotating_angle = [theta, phi, lam]

# Call Bloch sphere display function,input init_state,rotating_angle
plot_rotation_in_bloch_sphere(init_state, rotating_angle)

Cổng Logic Quantum

Trong máy tính cổ điển, chúng ta có thể vận dụng những phép toán logic cơ bản (cổng NOT, cổng NAND, cổng XOR, cổng AND và cổng OR) trên những bit cổ xưa và kết hợp chúng thành những phép toán phức tạp hơn. Máy tính lượng tử có một tập hợp những phép toán logic hoàn toàn khác, chúng được gọi là những cổng lượng tử và chúng ta không hề biên dịch những chương trình C ++ hiện có trên máy tính lượng tử. Bởi vì máy tính cổ xưa và máy tính lượng tử có cấu trúc cổng logic khác nhau, những thuật toán lượng tử cần được xây dựng bằng cách sử dụng cổng lượng tử. Về mặt toán học, một cổng lượng tử có thể được biểu diễn như một ma trận đơn nguyên. Các phép toán đơn nguyên có thể bảo toàn độ dài vectơ – đây là thuộc tính mà chúng ta mong muốn. Ma trận đơn nguyên được định nghĩa như sau:

U†U=UU†=I,vaˋ∥∣ψ⟩∥=∥U∣ψ⟩∥=1.(6)U^{dagger}U = UU^{dagger} = I, quad text{và} quad Vert |psirangle Vert = Vert U|psirangleVert = 1. tag{6}
U†U=UU†=I,vaˋ∥∣ψ⟩∥=∥U∣ψ⟩∥=1.(6)

Trong đó U†U^{dagger}U† là chuyển vị liên hợp của UUU và là đại diện cho ma trận đơn vị.

Vậy ý nghĩa vật lý của việc biểu diễn những cổng lượng tử dưới dạng ma trận đơn nguyên là gì? Điều này ý niệm rằng tất cả những cổng lượng tử đều có thể hòn đảo ngược. Đối với bất kỳ cổng logic UUU nào, người ta luôn có thể tìm thấy cổng đảo U†U^{dagger}U† tương ứng. Ngoài ra, ma trận đơn nguyên phải là ma trận vuông, vì nguồn vào và đầu ra của phép toán lượng tử nhu yếu cùng một lượng qubit. Cổng lượng tử ảnh hưởng tác động lên nnn qubit có thể được viết dưới dạng 2n×2n2^n times 2^n2n×2n ma trận đơn nguyên. Các cổng lượng tử phổ biến nhất hoạt động trên một hoặc hai qubit, giống như những cổng logic cổ điển.

Cổng đơn qubit

Tiếp theo đây mình sẽ giới thiệu đến những bạn bộ 3 ma trận Pauli {X,Y,Z}{X, Y, Z}{X,Y,Z} tương ứng với 3 cổng quay {Rx,Ry,Rz}{R_x, R_y, R_z}{Rx​,Ry​,Rz​} và sau đó sẽ là cổng Hadamard HHH. Đầu tiên chúng ta nói qua về cổng NOT – một cổng khá cơ bản và quan trọng ở cả thống kê giám sát cổ điển và lượng tử,

X:=[0110].(7)X := begin{bmatrix} 0 &1 \ 1 &0 end{bmatrix}. tag{7}
X:=[01​10​].(7)

Cổng Pauli XXX còn được gọi là cổng NOT. Cổng này có ý nghĩa là tạo ra trang thái “ngược” với trạng thái ∣0⟩ket{0}∣0⟩ hoặc ∣1⟩ket{1}∣1⟩ đầu vào, tương tự với việc quay trạng thái qubit trên mặt cầu Bloch sang điểm đối lập với nó trên mặt cầu và những thao tác đo lường và thống kê với cổng này đơn thuần chỉ là nhân ma trận với vector cột :

X∣0⟩:=[0110][10]=[01]=∣1⟩,vaˋX∣1⟩:=[0110][01]=[10]=∣0⟩.X |0rangle := begin{bmatrix} 0 &1 \ 1 &0 end{bmatrix} begin{bmatrix} 1 \0 end{bmatrix}
=begin{bmatrix} 0 \1 end{bmatrix} = |1rangle, quad text{và} quad X |1rangle := begin{bmatrix} 0 &1 \ 1 &0 end{bmatrix} begin{bmatrix} 0 \1 end{bmatrix} =begin{bmatrix} 1 \0 end{bmatrix}=|0rangle. X∣0⟩:=[01​10​][10​]=[01​]=∣1⟩,vaˋX∣1⟩:=[01​10​][01​]=[10​]=∣0⟩.

Xét trên quả cầu bloch, khi ma trận XXX này đo lường và thống kê với trạng thái của 1 qubit thì tương ứng với một phép quay ở trục xxx của quả cầu với góc πpiπ. Điều này lý giải tại sao ma trận XXX được biểu diễn dưới dạng Rx(π)R_x(pi)Rx​(π) ( được phân biệt với pha toàn cục e−iπ/2=−ie^{-ipi/2} = -ie−iπ/2=−i). Hai ma trận Pauli khác là Y vaˋ ZY text{ và } ZY vaˋ Z cũng có thể được lý giải theo cách tương tự (đại biểu được cho phép quay quanh trục y vaˋ zy text{ và } zy vaˋ z với góc πpiπ:

Y:=[0−ii0], vaˋZ:=[100−1].(9)Y := begin{bmatrix} 0 &-i \ i &0 end{bmatrix}, quad text{ và} quad Z := begin{bmatrix} 1 &0 \ 0 &-1 end{bmatrix}. tag{9}
Y:=[0i​−i0​], vaˋZ:=[10​0−1​].(9)

Nói chung, bất kỳ cổng lượng tử nào quay một góc θthetaθ quanh trục tương ứng trên hình cầu Bloch có thể được biểu thị như sau :

Rx(θ):=[cos⁡θ2−isin⁡θ2−isin⁡θ2cos⁡θ2],Ry(θ):=[cos⁡θ2−sin⁡θ2sin⁡θ2cos⁡θ2],Rz(θ):=[e−iθ200eiθ2].(10)R_x(theta) :=
begin{bmatrix}
cos frac{theta}{2} &-isin frac{theta}{2} \
-isin frac{theta}{2} &cos frac{theta}{2}
end{bmatrix}
,quad
R_y(theta) :=
begin{bmatrix}
cos frac{theta}{2} &-sin frac{theta}{2} \
sin frac{theta}{2} &cos frac{theta}{2}
end{bmatrix}
,quad
R_z(theta) :=
begin{bmatrix}
e^{-ifrac{theta}{2}} & 0 \
0 & e^{ifrac{theta}{2}}
end{bmatrix}.
tag{10}Rx​(θ):=[cos2θ​−isin2θ​​−isin2θ​cos2θ​​],Ry​(θ):=[cos2θ​sin2θ​​−sin2θ​cos2θ​​],Rz​(θ):=[e−i2θ​0​0ei2θ​​].(10)

Mình sẽ ví dụ tạo cổng RxR_xRx​ với paddle, những cổng Ry,RzR_y,R_zRy​,Rz​ hoàn toàn tương tự:

# Set the angle parameter theta = pi
theta = np.pi

# Set the number of qubits required for calculation
num_qubits = 1

# Initialize the single-bit quantum circuit
cir = Circuit(num_qubits)

# Apply an Rx rotation gate to the first qubit (q0), the angle is pi
cir.rx(0, param=theta)

# Convert to numpy.ndarray
# Print out this quantum gate
print('The matrix representation of quantum gate is:')
print(cir.unitary_matrix().numpy())

Output:


The matrix representation of quantum gate is:
[[-4.371139e-08+0.j  0.000000e+00-1.j]
 [ 0.000000e+00-1.j -4.371139e-08+0.j]]

Ngoài những cổng xoay nói trên, cổng đơn qubit cuối cùng và cũng là quan trọng nhất mà mình muốn nói đến là cổng Hadamard. Xét trên quả cầu Bloch thì cổng này bao gồm hai phép quay riêng biệt, đầu tiên quay một góc πpiπ quanh trục zzz và sau đó quay góc π/2pi/2π/2 quanh trục yyy. Cổng này có ý nghĩa là tạo ra trang thái “trộn” đều từ trạng thái ∣0⟩ket{0}∣0⟩ hoặc ∣1⟩ket{1}∣1⟩ đầu vào. Biểu diễn ma trận của nó như sau :

H:=12[111−1].(11)H := frac{1}{sqrt{2}}begin{bmatrix} 1 &1 \ 1 &-1 end{bmatrix}.
tag{11}H:=2​1​[11​1−1​].(11)

image.png
Một mạch lượng tử tạo bởi những cổng Hadamard như hình trên được gọi là mạch khởi tạo thanh ghi lượng tử. Đầu ra của mạch này là:

 ∣ψ⟩=12(∣0⟩+∣1⟩)12(∣0⟩+∣1⟩)…12(∣0⟩+∣1⟩)=12N/2(∣00…0⟩+∣00…1⟩+…+∣11…1⟩) ket{psi} =frac{1}{sqrt{2}}(ket{0}+ket{1})frac{1}{sqrt{2}}(ket{0}+ket{1})…frac{1}{sqrt{2}}(ket{0}+ket{1}) \
=frac{1}{2^{N/2}}(ket{00…0}+ket{00…1}+…+ket{11…1}) ∣ψ⟩=2​1​(∣0⟩+∣1⟩)2​1​(∣0⟩+∣1⟩)…2​1​(∣0⟩+∣1⟩)=2N/21​(∣00…0⟩+∣00…1⟩+…+∣11…1⟩)

Ở đây, NNN là số qubit đầu vào. Như vậy đầu ra của mạch khởi tạo thanh ghi lượng tử, với nguồn vào NNN qubit ở trạng thái ∣0⟩ket{0}∣0⟩, là trạng thái “trộn” đều của tất cả những véc tơ trong hệ cơ sở của khoảng trống 2N2^N2N chiều chứa những trạng thái của NNN qubit.

Cổng đa qubit

Như đã nói ở bài trước thì multiple qubit chính là sự kết hợp của nhiều qubit đơn lẻ tạo thành một hệ đa qubit và một hệ đa qubit như vậy cần những cổng đa qubit để có thể tương tác với nhau. Chúng ta sẽ bắt đầu phần này với việc tìm hiểu và khám phá và khám phá và khám phá và khám phá một cổng logic khá cơ bản đó là CNOT.

Untitled Diagram.drawio(1).png

CNOT∣00⟩=∣00⟩,CNOT∣01⟩=∣01⟩,CNOT∣10⟩=∣11⟩,CNOT∣11⟩=∣10⟩.text{CNOT} |00rangle = |00rangle, quad
text{CNOT} |01rangle = |01rangle, quad
text{CNOT} |10rangle = |11rangle, quad
text{CNOT} |11rangle = |10rangle.CNOT∣00⟩=∣00⟩,CNOT∣01⟩=∣01⟩,CNOT∣10⟩=∣11⟩,CNOT∣11⟩=∣10⟩.

Cách hoạt động của cổng CNOT khá dễ hình dung, nguồn vào của cổng CNOT gồm 2 Qubit: qubit thứ nhất là qubit Điều khiển (control), qubit thứ hai là qubit mục tiêu (target). Qubit Điều khiển sẽ không bị đổi khác sau khi qua cổng CNOT, qubit Mục tiêu sẽ bị đảo ngược khi và chỉ khi Qubit Điều khiển là 1, giá trị của Qubit Mục tiêu chính là tác dụng của phép XOR giữa hai Qubit đầu vào. Nhưng vẫn có một điều mà chúng ta cần lưu tâm, nếu qubit điều khiển là ∣0⟩ket{0}∣0⟩ hoặc ∣1⟩ket{1}∣1⟩ thì những điều mình nói ở trên là đúng. Vậy nếu nó nằm ngoài 2 trường hợp trên thì sao ? Cùng xem qua 3 ví dụ này nhé!

  • Ví dụ 1: Xét qubit

    ∣ψ⟩=a∣0⟩+b∣1⟩=[ab]|psirangle=a|0rangle+b|1rangle=left[begin{array}{}a \ bend{array}right]

    ψ

    =

    a

    ∣0

    +

    b

    ∣1

    =

    [

    a

    b

    ]

    là qubit mục tiêu và

    ∣1⟩ket{1}

    1

    là qubit điều khiển. Ta sẽ dùng ma trận để tính kết quả của CNOT

    ∣1,ψ⟩ket{1, psi}

    1

    ,

    ψ

    Ta coˊ: ∣1,ψ⟩=a∣10⟩+b∣11⟩=[00ab]text{Ta có: }|1, psirangle=a|10rangle+b|11rangle=left[begin{array}{l}0 \ 0 \ a \ bend{array}right]

    Ta c

    o

    ˊ

    ∣1

    ,

    ψ

    =

    a

    ∣10

    +

    b

    ∣11

    =

    0

    0

    a

    b

         ~~~~~~~~~          CNOTmathrm{CNOT}CNOT ∣1,ψ⟩=[1000010000010010][00ab]=[00ba]=∣1,ϕ⟩|1, psirangle=left[begin{array}{llll}1 & 0 & 0 & 0 \ 0 & 1 & 0 & 0 \ 0 & 0 & 0 & 1 \ 0 & 0 & 1 & 0end{array}right]left[begin{array}{l}0 \ 0 \ a \ bend{array}right]=left[begin{array}{l}0 \ 0 \ b \ aend{array}right]=|1, phirangle∣1,ψ⟩=⎣⎡​1000​0100​0001​0010​⎦⎤​⎣⎡​00ab​⎦⎤​=⎣⎡​00ba​⎦⎤​=∣1,ϕ⟩. Với ∣ϕ⟩=b∣0⟩+a∣1⟩|phirangle=b|0rangle+a|1rangle∣ϕ⟩=b∣0⟩+a∣1⟩.

          ~~~~~~~~~~           Vậy ∣1,ψ⟩ket{1, psi}∣1,ψ⟩ sau khi đi qua CNOT sẽ thành ∣1,ϕ⟩ket{1, phi}∣1,ϕ⟩

Untitled Diagram.drawio(2).png

  • Ví dụ 2 : Xét qubit

    ∣1⟩ket{1}

    1

    là qubit mục tiêu và

    ψ=12∣0⟩+12∣1⟩psi = frac{1}{sqrt{2}}ket{0}+frac{1}{sqrt{2}}ket{1}

    ψ

    =

    2

    1

    0

    +

    2

    1

    1

    là qubit điều khiển.

    Ta coˊ: ∣ψ,1⟩=12(∣0⟩+∣1⟩)⊗∣1⟩=12(∣01⟩+∣11⟩)=[012012].text{Ta có: }|psi, 1rangle=frac{1}{sqrt{2}}(|0rangle+|1rangle)otimes|1rangle=frac{1}{sqrt{2}}(|01rangle+|11rangle)=left[begin{array}{c}0 \ frac{1}{sqrt{2}} \ 0 \ frac{1}{sqrt{2}}end{array}right] .

    Ta c

    o

    ˊ

    ψ

    ,

    1

    =

    2

    1

    (

    ∣0

    +

    ∣1

    ⟩)

    ∣1

    =

    2

    1

    (

    ∣01

    +

    ∣11

    ⟩)

    =

    0

    2

    1

    0

    2

    1

    .

         CNOT∣ψ,1⟩=[1000010000010010][012012]=[012120]=∣ϕ⟩~~~~~~~~~mathrm{CNOT}|psi, 1rangle=left[begin{array}{cccc}1 & 0 & 0 & 0 \ 0 & 1 & 0 & 0 \ 0 & 0 & 0 & 1 \ 0 & 0 & 1 & 0end{array}right]left[begin{array}{c}0 \ frac{1}{sqrt{2}} \ 0 \ frac{1}{sqrt{2}}end{array}right]=left[begin{array}{c}0 \ frac{1}{sqrt{2}} \ frac{1}{sqrt{2}} \ 0end{array}right]=|phirangle         CNOT∣ψ,1⟩=⎣⎡​1000​0100​0001​0010​⎦⎤​⎣⎡​02​1​02​1​​⎦⎤​=⎣⎡​02​1​2​1​0​⎦⎤​=∣ϕ⟩

          ∣ϕ⟩=12(∣01⟩+∣10⟩)~~~~~~~~~~ket{phi}=frac{1}{sqrt{2}}(ket{01}+ket{10})          ∣ϕ⟩=2​1​(∣01⟩+∣10⟩). Như vậy hệ ∣ψ⟩ket{psi}∣ψ⟩ đã bị rối lượng tử sau khi đi qua cổng CNOT

Untitled Diagram.drawio(3).png

  • Ví dụ 3 : Xét hệ rối lượng tử

    ∣ϕ⟩=12(∣00⟩+∣11⟩)=[120012]|phirangle=frac{1}{sqrt{2}}(|00rangle+|11rangle)=left[begin{array}{c}frac{1}{sqrt{2}} \ 0 \ 0 \ frac{1}{sqrt{2}}end{array}right]

    ϕ

    =

    2

    1

    (

    ∣00

    +

    ∣11

    ⟩)

    =

    2

    1

    0

    0

    2

    1

    . Cho hệ đi qua cổng CNOT:

    CNOT∣ϕ⟩=[1000010000010010][120012]=[120120]=∣ψ⟩mathrm{CNOT}|phirangle=left[begin{array}{cccc}1 & 0 & 0 & 0 \ 0 & 1 & 0 & 0 \ 0 & 0 & 0 & 1 \ 0 & 0 & 1 & 0end{array}right]left[begin{array}{c}frac{1}{sqrt{2}} \ 0 \ 0 \ frac{1}{sqrt{2}}end{array}right]=left[begin{array}{c}frac{1}{sqrt{2}} \ 0 \ frac{1}{sqrt{2}} \ 0end{array}right]=|psirangle

    CNOT

    ϕ

    =

    1

    0

    0

    0

    0

    1

    0

    0

    0

    0

    0

    1

    0

    0

    1

    0

    2

    1

    0

    0

    2

    1

    =

    2

    1

    0

    2

    1

    0

    =

    ψ

    .

          ∣ψ⟩=12(∣00⟩+∣10⟩)=12(∣0⟩+∣1⟩)∣0⟩~~~~~~~~~~|psirangle=frac{1}{sqrt{2}}(|00rangle+|10rangle)=frac{1}{sqrt{2}}(|0rangle+|1rangle)|0rangle          ∣ψ⟩=2​1​(∣00⟩+∣10⟩)=2​1​(∣0⟩+∣1⟩)∣0⟩. Như vậy hệ ϕphiϕ đã được gỡ rối.

Ba ví dụ trên đã minh họa cho chúng ta 3 cách sử dụng của cổng CNOT đó là : đảo bit có điều kiện, tạo rối lượng tử, tháo gỡ lượng tử. Ngoài ra còn một số cổng nữa mình không tiện đề cập ở đây, những bạn có thể vào link wiki này để tìm hiểu.

image.png
                        ~~~~~~~~~~~~~~~~~~~~~~~~                        https://en.wikipedia.org/wiki/Quantum_logic_gate

Measurement

Đối với hệ lượng tử hai cấp, ví dụ điển hình như spin của electron, nó có thể quay lên ↑uparrow↑ hoặc quay xuống ↓downarrow↓, tương ứng với trạng thái ∣0⟩ket{0}∣0⟩ và ∣1⟩ket{1}∣1⟩. Như đã đề cập trước đây, electron có thể ở trạng thái chồng chất của spin ↑uparrow↑ và ↓downarrow↓ : ∣ψ⟩=α∣0⟩+β∣1⟩|psirangle =alpha |0rangle + beta |1rangle∣ψ⟩=α∣0⟩+β∣1⟩ và phép measurement sẽ giúp chúng ta hiểu thêm về trạng thái chồng chất là gì. Cần quan tâm rằng phép measurement trong cơ học lượng tử thường mang tính thống kê Tỷ Lệ hơn là một phép measurement đơn lẻ. Điều này là do bản chất của các phép measurement trong vật lý lượng tử, nó làm sụp đổ trạng thái lượng tử quan sát được. Ví dụ, nếu chúng ta measure một electron ở trạng thái ∣ψ⟩=α∣0⟩+β∣1⟩|psirangle =alpha |0rangle + beta |1rangle∣ψ⟩=α∣0⟩+β∣1⟩, chúng ta sẽ có Xác Suất ∣α∣2|alpha|^2∣α∣2 thu được kết quả measure spin up, và sau khi measure, trạng thái lượng tử chuyển sang trạng thái sau đo ∣0⟩|0rangle∣0⟩. Tương tự, chúng ta cũng có Tỷ Lệ ∣β∣2|beta|^2∣β∣2 để có được trạng thái ∣1⟩|1rangle∣1⟩ quay xuống sau khi measure. Vì vậy, nếu chúng ta muốn nhận được giá trị chính xác của αalphaα, chỉ một thử nghiệm thì rõ ràng là không đủ. Chúng ta cần sẵn sàng chuẩn bị rất nhiều electron ở trạng thái chồng chất, measure spin của từng electron, rồi đếm tần số. Phép measure có một vị trí đặc biệt trong cơ học lượng tử và mạng neuron lượng tử. Nếu bạn đọc cảm thấy khó hiểu, các bạn có thể tham khảo link Measurement in quantum mechanics để tìm hiểu kỹ hơn

Quantum Neural Network

Mạng nơron lượng tử là một lớp con của các thuật toán biến đổi lượng tử, bao gồm các mạch lượng tử có chứa các hoạt động cổng lượng tử tham số hóa. Dữ liệu đầu tiên được mã hóa thành trạng thái lượng tử trải qua các cổng lượng tử hoặc bản đồ đặc trưng. Việc lựa chọn bản đồ đặc trưng thường hướng tới việc nâng cao hiệu suất của quy mô lượng tử và thường không được tối ưu hóa cũng như không được training. Khi tài liệu được mã hóa sang trạng thái lượng tử, một mô hình biến thể chứa các cổng tham số hóa sẽ được áp dụng và tối ưu hóa cho một task cụ thể. Điều này được triển khai trải qua tối thiểu hàm mất mát tương tự như những bài toán machine learning thông thường, sau đó đầu ra của mô hình lượng tử được measure thông qua hàm Hamiltonian như thể các cổng Pauli chẳng hạn.
image.png

Tổng quan về quantum neural network: nguồn vào x∈Rsin x in mathbb{R}^{s_{text {in }}}x∈Rsin ​ đầu tiên được mã hóa vào không gian lượng tử HilbertHilbertHilbert SSS-qubit thông qua khối feature mapping ∣ψx⟩:=Ux∣0⟩⊗Sleft|psi_{x}rightrangle:=mathcal{U}_{x}|0rangle^{otimes S}∣ψx​⟩:=Ux​∣0⟩⊗S (một mạch tham số hóa lượng tử được định nghĩa sẵn). Dữ liệu sau khi mã hóa sẽ được “học” bởi khối variational Model hay cũng chính là các mạch tham số hóa lượng tử ∣gθ(x)⟩:=Gθ∣ψx⟩| g _ { theta } left ( x right ) rangle : = G _ { theta } | psi _ { x } rangle∣gθ​(x)⟩:=Gθ​∣ψx​⟩. Đầu ra cuối cùng được đưa qua phép measurement và hậu xử lý để trích xuất output của model và mapping về dạng classical.

QClassifier

Như vậy là chúng ta đã đi qua những phần kim chỉ nan cơ bản và thiết yếu để có thể xây dựng một mạng neuron lượng tử đơn giản, giờ đây sẽ đi vào ví dụ để có thể trực quan hóa những gì chúng ta vừa tìm hiểu nhé !

Mình sẽ thực thi xây dựng pipeline cho bài toán adhoc phân loại đơn giản. Đầu tiên ta cần import các thư viện cần thiết:

# Import numpy,paddle and paddle_quantum
import numpy as np
import paddle
import paddle_quantum

# To construct quantum circuit
from paddle_quantum.ansatz import Circuit

# Some functions
from numpy import pi as PI
from paddle import matmul, transpose, reshape  # paddle matrix multiplication and transpose
from paddle_quantum.qinfo import pauli_str_to_matrix # N qubits Pauli matrix
from paddle_quantum.linalg import dagger  # complex conjugate

# Plot figures, calculate the run time
from matplotlib import pyplot as plt
import time

Khai báo các tham số được sử dụng cho model :

# Parameters for generating the data set
Ntrain = 200        # Specify the training set size
Ntest = 100         # Specify the test set size
boundary_gap = 0.5  # Set the width of the decision boundary
seed_data = 2       # Fixed random seed required to generate the data set
# Parameters for training
N = 4               # Number of qubits required
DEPTH = 1           # Circuit depth
BATCH = 20          # Batch size during training
EPOCH = int(200 * BATCH / Ntrain)
                    # Number of training epochs, the total iteration number "EPOCH * (Ntrain / BATCH)" is chosen to be about 200
LR = 0.01           # Set the learning rate
seed_paras = 19     # Set random seed to initialize various parameters

Data processing

Ở ví dụ này mình sẽ dùng bộ tài liệu của paper Supervised learning with quantum enhanced feature spaces
Hàm khởi tạo tài liệu cho bài toán phân loại nhị phân:

# Generate a binary classification data set with circular decision boundary
def circle_data_point_generator(Ntrain, Ntest, boundary_gap, seed_data):
    """
    :param Ntrain: number of training samples
    :param Ntest: number of test samples
    :param boundary_gap: value in (0, 0.5), means the gap between two labels
    :param seed_data: random seed
    :return: 'Ntrain' samples for training and
             'Ntest' samples for testing
    """
    # Generate "Ntrain + Ntest" pairs of data, x for 2-dim data points, y for labels.
    # The first "Ntrain" pairs are used as training set, the last "Ntest" pairs are used as testing set
    train_x, train_y = [], []
    num_samples, seed_para = 0, 0
    while num_samples < Ntrain + Ntest:
        np.random.seed((seed_data + 10) * 1000 + seed_para + num_samples)
        data_point = np.random.rand(2) * 2 - 1  # 2-dim vector in range [-1, 1]

        # If the modulus of the data point is less than (0.7 - gap), mark it as 0
        if np.linalg.norm(data_point) < 0.7-boundary_gap / 2:
            train_x.append(data_point)
            train_y.append(0.)
            num_samples += 1

        # If the modulus of the data point is greater than (0.7 + gap), mark it as 1
        elif np.linalg.norm(data_point) > 0.7 + boundary_gap / 2:
            train_x.append(data_point)
            train_y.append(1.)
            num_samples += 1
        else:
            seed_para += 1

    train_x = np.array(train_x).astype("float64")
    train_y = np.array([train_y]).astype("float64").T

    print("The dimensions of the training set x {} and y {}".format(np.shape(train_x[0:Ntrain]), np.shape(train_y[0:Ntrain])))
    print("The dimensions of the test set x {} and y {}".format(np.shape(train_x[Ntrain:]), np.shape(train_y[Ntrain:])), "n")

    return train_x[0:Ntrain], train_y[0:Ntrain], train_x[Ntrain:], train_y[Ntrain:]

Ở đây mình sẽ tạo training set là 200 mẫu và test set là 100 mẫu, sau đó visualize tài liệu để dễ hình dung hơn nhé :

def data_point_plot(data, label):
    """
    :param data: shape [M, 2], means M 2-D data points
    :param label: value 0 or 1
    :return: plot these data points
    """
    dim_samples, dim_useless = np.shape(data)
    plt.figure(1)
    for i in range(dim_samples):
        if label[i] == 0:
            plt.plot(data[i][0], data[i][1], color="r", marker="o")
        elif label[i] == 1:
            plt.plot(data[i][0], data[i][1], color="b", marker="o")
    plt.show()
# Generate data set
train_x, train_y, test_x, test_y = circle_data_point_generator(
        Ntrain, Ntest, boundary_gap, seed_data)

# Visualization
print("Visualization of {} data points in the training set: ".format(Ntrain))
data_point_plot(train_x, train_y)
print("Visualization of {} data points in the test set: ".format(Ntest))
data_point_plot(test_x, test_y)

The dimensions of the training set x (200, 2) and y (200, 1)

The dimensions of the test set x (100, 2) and y (100, 1)

Visualization of 200 data points in the training set:

image.png
Visualization of 100 data points in the test set:

image.png

Khác với học máy cổ điển, mạng neuron lượng tử cần phải xem xét nhiều hơn đến việc tiền xử lý dữ liệu. Chúng ta cần thêm một bước để chuyển đổi tài liệu cổ điển thành tài liệu lượng tử trước khi chạy mô phỏng thuật toán. Tiếp theo đây mình sẽ sử dụng phương pháp “Angle Encoding” để mã hóa tài liệu sang không gian HilbertHilbertHilbert.

Đầu tiên chúng ta cần quyết định hành động xem cần sử dụng bao nhiêu qubit, do tại tài liệu mình sử dụng chỉ có 2 chiều {xk=(x0k,x1k)}{x^{k} = (x^{k}_0, x^{k}_1)}{xk=(x0k​,x1k​)}, theo như paper Quantum Circuit Learning thì mình sẽ cần tối thiểu 2 qubit để mã hóa dữ liệu. Sau đó đưa {xk}{x^{k}}{xk} qua một tổ hợp cổng lượng tử U(xk)U(x^{k})U(xk) và xử lý dựa trên trạng thái khởi tạo ∣00⟩|00rangle∣00⟩. Thông qua cách này, mình đã mã hóa được tài liệu ∣ψin⟩|psi_{rm in}rangle∣ψin​⟩ từ không gian 2 chiều ở hệ DescartesDescartesDescartes sang không gian lượng tử HilbertHilbertHilbert:

∣ψin⟩=U(x)∣00⟩=12[1−i0−1+i001+i0−1−i1−i01−i001+i01+i][1000]=12[1−i01−i0].|psi_{rm in}rangle =
U(x)|00rangle = frac{1}{2}
begin{bmatrix}
1-i &0 &-1+i &0 \
0 &1+i &0 &-1-i \
1-i &0 &1-i &0 \
0 &1+i &0 &1+i
end{bmatrix}
begin{bmatrix}
1 \
0 \
0 \
0
end{bmatrix}
= frac{1}{2}
begin{bmatrix}
1-i \
0 \
1-i \
0
end{bmatrix}.
∣ψin​⟩=U(x)∣00⟩=21​⎣⎡​1−i01−i0​01+i01+i​−1+i01−i0​0−1−i01+i​⎦⎤​⎣⎡​1000​⎦⎤​=21​⎣⎡​1−i01−i0​⎦⎤​.

# Gate: rotate around Y-axis, Z-axis with angle theta
def Ry(theta):
    """
    :param theta: parameter
    :return: Y rotation matrix
    """
    return np.array([[np.cos(theta / 2), -np.sin(theta / 2)],
                     [np.sin(theta / 2), np.cos(theta / 2)]])

def Rz(theta):
    """
    :param theta: parameter
    :return: Z rotation matrix
    """
    return np.array([[np.cos(theta / 2) - np.sin(theta / 2) * 1j, 0],
                     [0, np.cos(theta / 2) + np.sin(theta / 2) * 1j]])

# Classical -> Quantum Data Encoder
def datapoints_transform_to_state(data, n_qubits):
    """
    :param data: shape [-1, 2]
    :param n_qubits: the number of qubits to which
    the data transformed
    :return: shape [-1, 1, 2 ^ n_qubits]
        the first parameter -1 in this shape means can be arbitrary. In this tutorial, it equals to BATCH.
    """
    dim1, dim2 = data.shape
    res = []
    for sam in range(dim1):
        res_state = 1.
        zero_state = np.array([[1, 0]])
        # Angle Encoding
        for i in range(n_qubits):
            # For even number qubits, perform Rz(arccos(x0^2)) Ry(arcsin(x0))
            if i % 2 == 0:
                state_tmp=np.dot(zero_state, Ry(np.arcsin(data[sam][0])).T)
                state_tmp=np.dot(state_tmp, Rz(np.arccos(data[sam][0] ** 2)).T)
                res_state=np.kron(res_state, state_tmp)
            # For odd number qubits, perform Rz(arccos(x1^2)) Ry(arcsin(x1))
            elif i% 2 == 1:
                state_tmp=np.dot(zero_state, Ry(np.arcsin(data[sam][1])).T)
                state_tmp=np.dot(state_tmp, Rz(np.arccos(data[sam][1] ** 2)).T)
                res_state=np.kron(res_state, state_tmp)
        res.append(res_state)
    res = np.array(res, dtype=paddle_quantum.get_dtype())
    
    return res

Dữ liệu sau khi được encode :

print("As a test, we enter the classical information:")
print("(x_0, x_1) = (1, 0)")
print("The 2-qubit quantum state output after encoding is:")
print(datapoints_transform_to_state(np.array([[1, 0]]), n_qubits=2))

image.png

Build Quantum Neural Network

Để thuận tiện thì chúng ta sẽ gọi mạng lượng tử tham số hóa là U(θ)U(boldsymbol{theta})U(θ). Tương tự như các mạng neuron cổ điển, chúng ta hoàn toàn có thể tự định nghĩa cấu trúc của mạng neuron lượng tử thông qua các cổng logic lượng tử. Sau khi có được dữ liệu đã qua mã hóa ∣ψin⟩|psi_{rm in}rangle∣ψin​⟩ ta sẽ đưa nó vào mạng neuron lượng tử, thực ra chỉ là một phép nhân ma trận đơn nhất với vector nguồn vào để lấy trạng thái quantum đầu ra ∣φ⟩|varphirangle∣φ⟩

∣ψout⟩=U(θ)∣ψin⟩.|psi_{rm out}rangle = U(boldsymbol{theta})|psi_{rm in}rangle.
∣ψout​⟩=U(θ)∣ψin​⟩.

Giả sử toàn bộ tham số θ=πtheta = piθ=π ta sẽ có trạng thái đầu ra như sau :

∣ψout⟩=U(θ=π)∣ψin⟩=[00−10−100001000001]⋅12[1−i01−i0]=12[−1+i−1+i00].|psi_{rm out}rangle =
U(boldsymbol{theta} =pi)|psi_{rm in}rangle =
begin{bmatrix}
0 &0 &-1 &0 \
-1 &0 &0 &0 \
0 &1 &0 &0 \
0 &0 &0 &1
end{bmatrix}
cdot
frac{1}{2}
begin{bmatrix}
1-i \
0 \
1-i \
0
end{bmatrix}
= frac{1}{2}
begin{bmatrix}
-1+i \
-1+i \
0 \
0
end{bmatrix}.
∣ψout​⟩=U(θ=π)∣ψin​⟩=⎣⎡​0−100​0010​−1000​0001​⎦⎤​⋅21​⎣⎡​1−i01−i0​⎦⎤​=21​⎣⎡​−1+i−1+i00​⎦⎤​.

Measurement

Sau khi đi qua PQC U(θ)U(theta)U(θ), dữ liệu lượng tử trở thành ∣ψout⟩k=U(θ)∣ψin⟩klvert psi_{rm out}rangle^k = U(theta)lvert psi_{rm in} rangle^k∣ψout​⟩k=U(θ)∣ψin​⟩k. Để có được nhãn của dữ liệu, chúng ta cần measure trạng thái lượng tử mới để thu được dữ liệu classical. Những dữ liệu classica này sau đó sẽ được sử dụng để tính toán hàm mất mát L(θ)mathcal{L}(boldsymbol{theta})L(θ). Cuối cùng, dựa trên thuật toán gradient descent, ta liên tục update các thông số PQC và tối ưu hóa hàm mất mát. Giá trị mà chúng ta đạt được sau phép measure :

⟨Z⟩=⟨ψout∣Z⊗I∣ψout⟩=12[−1−i−1−i00][1000010000−10000−1]⋅12[−1+i−1+i00]=1.langle Z rangle =
langle psi_{rm out} |Zotimes I| psi_{rm out}rangle =
frac{1}{2}
begin{bmatrix}
-1-i quad
-1-i quad
0 quad
0
end{bmatrix}
begin{bmatrix}
1 &0 &0 &0 \
0 &1 &0 &0 \
0 &0 &-1 &0 \
0 &0 &0 &-1
end{bmatrix}
cdot
frac{1}{2}
begin{bmatrix}
-1+i \
-1+i \
0 \
0
end{bmatrix}
= 1. ⟨Z⟩=⟨ψout​∣Z⊗I∣ψout​⟩=21​[−1−i−1−i00​]⎣⎡​1000​0100​00−10​000−1​⎦⎤​⋅21​⎣⎡​−1+i−1+i00​⎦⎤​=1.

Thông thường đầu ra ⟨Z⟩langle Z rangle⟨Z⟩ sẽ nằm trong khoảng [−1,1][-1,1][−1,1]. Để đầu ra này khớp với nhãn đã định nghĩa trước yk∈{0,1}y^{k} in {0,1}yk∈{0,1}, ta sẽ cần 1 hàm ánh xạ đơn giản:

y~k=⟨Z⟩2+12+bias∈[0,1].tilde{y}^{k} = frac{langle Z rangle}{2} + frac{1}{2} + bias quad in [0, 1].
y~​k=2⟨Z⟩​+21​+bias∈[0,1].

Sau khi ánh xạ, chúng ta có thể coi y~ktilde{y}^{k}y~​k là nhãn mà cần tìm với y~k<0.5tilde{y}^{k}< 0.5y~​k<0.5 tương ứng nhãn 0 và y~k≥0.5tilde{y}^{k}geq 0.5y~​k≥0.5 tương ứng nhãn 1. Toàn bộ quy trình từ bước xử lý dữ liệu đến tính toán đầu ra có thể mô tả như sau:

xk→∣ψin⟩k→U(θ)∣ψin⟩k→∣ψout⟩k→k⟨ψout∣Z⊗I⋯⊗I∣ψout⟩k→⟨Z⟩→y~k.x^{k} rightarrow |psi_{rm in}rangle^{k} rightarrow U(boldsymbol{theta})|psi_{rm in}rangle^{k} rightarrow
|psi_{rm out}rangle^{k} rightarrow ^{k}langle psi_{rm out} |Zotimes Icdots otimes I| psi_{rm out} rangle^{k}
rightarrow langle Z rangle rightarrow tilde{y}^{k}.xk→∣ψin​⟩k→U(θ)∣ψin​⟩k→∣ψout​⟩k→k⟨ψout​∣Z⊗I⋯⊗I∣ψout​⟩k→⟨Z⟩→y~​k.

Loss function

Hàm loss được tính toán như sau :

Li=∑k=1BATCH1BATCH∣yi,k−y~i,k∣2mathcal{L}_{i} = sum_{k=1}^{BATCH} frac{1}{BATCH} |y^{i,k} – tilde{y}^{i,k}|^2
Li​=k=1∑BATCH​BATCH1​∣yi,k−y~​i,k∣2

Quantum Neural Network:

# Generate Pauli Z operator that only acts on the first qubit
# Act the identity matrix on rest of the qubits
def Observable(n):
    r"""
    :param n: number of qubits
    :return: local observable: Z otimes I otimes ...otimes I
    """
    Ob = pauli_str_to_matrix([[1.0, 'z0']], n)

    return Ob
# Build the computational graph
class Opt_Classifier(paddle_quantum.gate.Gate):
    """
    Construct the model net
    """
    def __init__(self, n, depth, seed_paras=1):
        # Initialization, use n, depth give the initial PQC
        super(Opt_Classifier, self).__init__()
        self.n = n
        self.depth = depth
        # Initialize bias
        self.bias = self.create_parameter(
            shape=[1],
            default_initializer=paddle.nn.initializer.Normal(std=0.01),
            dtype='float32',
            is_bias=False)
        
        self.circuit = Circuit(n)
        # Build a generalized rotation layer
        for i in range(n):
            self.circuit.rz(qubits_idx=i)
            self.circuit.ry(qubits_idx=i)
            self.circuit.rz(qubits_idx=i)

        # The default depth is depth = 1
        # Build the entangleed layer and Ry rotation layer
        for d in range(3, depth + 3):
            # The entanglement layer
            for i in range(n-1):
                self.circuit.cnot(qubits_idx=[i, i + 1])
            self.circuit.cnot(qubits_idx=[n-1, 0])
            # Add Ry to each qubit
            for i in range(n):
                self.circuit.ry(qubits_idx=i)

    # Define forward propagation mechanism, and then calculate loss function and cross-validation accuracy
    def forward(self, state_in, label):
        """
        Args:
            state_in: The input quantum state, shape [-1, 1, 2^n] -- in this tutorial: [BATCH, 1, 2^n]
            label: label for the input state, shape [-1, 1]
        Returns:
            The loss:
                L = 1/BATCH * (( + 1)/2 + bias - label)^2
        """
        # Convert Numpy array to tensor
        Ob = paddle.to_tensor(Observable(self.n))
        label_pp = reshape(paddle.to_tensor(label), [-1, 1])

        # Build the quantum circuit
        Utheta = self.circuit.unitary_matrix()

        # Because Utheta is achieved by learning, we compute with row vectors to speed up without affecting the training effect
        state_out = matmul(state_in, Utheta)  # shape:[-1, 1, 2 ** n], the first parameter is BATCH in this tutorial

        # Measure the expectation value of Pauli Z operator  -- shape [-1,1,1]
        E_Z = matmul(matmul(state_out, Ob), transpose(paddle.conj(state_out), perm=[0, 2, 1]))

        # Mapping  to the estimated value of the label
        state_predict = paddle.real(E_Z)[:, 0] * 0.5 + 0.5 + self.bias  # |y^{i,k} - tilde{y}^{i,k}|^2
        loss = paddle.mean((state_predict - label_pp) ** 2)  # Get average for "BATCH" |y^{i,k} - tilde{y}^{i,k}|^2: L_i:shape:[1,1]

        # Calculate the accuracy of cross-validation
        is_correct = (paddle.abs(state_predict - label_pp) < 0.5).nonzero().shape[0]
        acc = is_correct / label.shape[0]

        return loss, acc, state_predict.numpy(), self.circuit

Training Process

# Draw the figure of the final training classifier
def heatmap_plot(Opt_Classifier, N):
    # generate data points x_y_
    Num_points = 30
    x_y_ = []
    for row_y in np.linspace(0.9, -0.9, Num_points):
        row = []
        for row_x in np.linspace(-0.9, 0.9, Num_points):
            row.append([row_x, row_y])
        x_y_.append(row)
    x_y_ = np.array(x_y_).reshape(-1, 2).astype("float64")

    # make prediction: heat_data
    input_state_test = paddle.to_tensor(
        datapoints_transform_to_state(x_y_, N))
    loss_useless, acc_useless, state_predict, cir = Opt_Classifier(state_in=input_state_test, label=x_y_[:, 0])
    heat_data = state_predict.reshape(Num_points, Num_points)

    # plot
    fig = plt.figure(1)
    ax = fig.add_subplot(111)
    x_label = np.linspace(-0.9, 0.9, 3)
    y_label = np.linspace(0.9, -0.9, 3)
    ax.set_xticks([0, Num_points // 2, Num_points - 1])
    ax.set_xticklabels(x_label)
    ax.set_yticks([0, Num_points // 2, Num_points - 1])
    ax.set_yticklabels(y_label)
    im = ax.imshow(heat_data, cmap=plt.cm.RdBu)
    plt.colorbar(im)
    plt.show()

# Learn the PQC via Adam

def QClassifier(Ntrain, Ntest, gap, N, DEPTH, EPOCH, LR, BATCH, seed_paras, seed_data):
    """
    Quantum Binary Classifier
    Input:
        Ntrain         # Specify the training set size
        Ntest          # Specify the test set size
        gap            # Set the width of the decision boundary
        N              # Number of qubits required
        DEPTH          # Circuit depth
        BATCH          # Batch size during training
        EPOCH          # Number of training epochs, the total iteration number "EPOCH * (Ntrain / BATCH)" is chosen to be about 200
        LR             # Set the learning rate
        seed_paras     # Set random seed to initialize various parameters
        seed_data      # Fixed random seed required to generate the data set
        plot_heat_map  # Whether to plot heat map, default True
    """
    # Generate data set
    train_x, train_y, test_x, test_y = circle_data_point_generator(Ntrain=Ntrain, Ntest=Ntest, boundary_gap=gap, seed_data=seed_data)
    # Read the dimension of the training set
    N_train = train_x.shape[0]
    
    paddle.seed(seed_paras)
    # Initialize the registers to store the accuracy rate and other information
    summary_iter, summary_test_acc = [], []

    # Generally, we use Adam optimizer to get relatively good convergence
    # Of course, it can be changed to SGD or RMSprop
    myLayer = Opt_Classifier(n=N, depth=DEPTH)  # Initial PQC
    opt = paddle.optimizer.Adam(learning_rate=LR, parameters=myLayer.parameters())


    # Optimize iteration
    # We divide the training set into "Ntrain/BATCH" groups
    # For each group the final circuit will be used as the initial circuit for the next group
    # Use cir to record the final circuit after learning.
    i = 0  # Record the iteration number
    for ep in range(EPOCH):
        # Learn for each group
        for itr in range(N_train // BATCH):
            i += 1  # Record the iteration number
            # Encode classical data into a quantum state |psi>, dimension [BATCH, 2 ** N]
            input_state = paddle.to_tensor(datapoints_transform_to_state(train_x[itr * BATCH:(itr + 1) * BATCH], N))

            # Run forward propagation to calculate loss function
            loss, train_acc, state_predict_useless, cir 
                = myLayer(state_in=input_state, label=train_y[itr * BATCH:(itr + 1) * BATCH])  # optimize the given PQC
            # Print the performance in iteration
            if i % 30 == 5:
                # Calculate the correct rate on the test set test_acc
                input_state_test = paddle.to_tensor(datapoints_transform_to_state(test_x, N))
                loss_useless, test_acc, state_predict_useless, t_cir 
                    = myLayer(state_in=input_state_test,label=test_y)
                print("epoch:", ep, "iter:", itr,
                      "loss: %.4f" % loss.numpy(),
                      "train acc: %.4f" % train_acc,
                      "test acc: %.4f" % test_acc)
                # Store accuracy rate and other information
                summary_iter.append(itr + ep * N_train)
                summary_test_acc.append(test_acc) 

            # Run back propagation to minimize the loss function
            loss.backward()
            opt.minimize(loss)
            opt.clear_grad()
            
    # Print the final circuit
    print("The trained circuit:")
    print(cir)
    # Draw the decision boundary represented by heatmap
    heatmap_plot(myLayer, N=N)

    return summary_test_acc
def main():
    """
    main
    """
    time_start = time.time()
    acc = QClassifier(
        Ntrain = 200,    # Specify the training set size
        Ntest = 100,     # Specify the test set size
        gap = 0.5,       # Set the width of the decision boundary
        N = 4,           # Number of qubits required
        DEPTH = 1,       # Circuit depth
        BATCH = 20,      # Batch size during training
        EPOCH = int(200 * BATCH / Ntrain),
                        # Number of training epochs, the total iteration number "EPOCH * (Ntrain / BATCH)" is chosen to be about 200
        LR = 0.01,       # Set the learning rate
        seed_paras = 19, # Set random seed to initialize various parameters
        seed_data = 2,   # Fixed random seed required to generate the data set
    )
    
    time_span = time.time()-time_start
    print('The main program finished running in ', time_span, 'seconds.')

if __name__ == '__main__':
    main()
The dimensions of the training set x (200, 2) and y (200, 1)
The dimensions of the test set x (100, 2) and y (100, 1) 

epoch: 0 iter: 4 loss: 0.2750 train acc: 0.7000 test acc: 0.6700

epoch: 3 iter: 4 loss: 0.2471 train acc: 0.2500 test acc: 0.5500
epoch: 6 iter: 4 loss: 0.1976 train acc: 0.8000 test acc: 0.9200
epoch: 9 iter: 4 loss: 0.1639 train acc: 1.0000 test acc: 1.0000
epoch: 12 iter: 4 loss: 0.1441 train acc: 1.0000 test acc: 1.0000
epoch: 15 iter: 4 loss: 0.1337 train acc: 1.0000 test acc: 1.0000
epoch: 18 iter: 4 loss: 0.1287 train acc: 1.0000 test acc: 1.0000
The trained circuit:
--Rz(3.490)----Ry(5.436)----Rz(3.281)----*--------------x----Ry(0.098)--
                                         |              |               
--Rz(1.499)----Ry(2.579)----Rz(3.496)----x----*---------|----Ry(1.282)--
                                              |         |               
--Rz(5.956)----Ry(3.158)----Rz(3.949)---------x----*----|----Ry(1.418)--
                                                   |    |               
--Rz(1.604)----Ry(0.722)----Rz(5.037)--------------x----*----Ry(2.437)--

image.png

The main program finished running in  7.628719806671143 seconds.

Tổng kết

Như vậy chúng ta đã đi qua một loạt các khái niệm cơ bản về Quantum Computing và Quantum Neural Network, đồng thời xây dựng một mạng neuron lượng tử đơn giản. Theo như những nghiên cứu và điều tra gần đây thì mặc dù đã có nhiều bước phát triển trong nghành nghề dịch vụ học máy lượng tử thế nhưng câu hỏi liệu mạng nơ-ron lượng tử có mạnh hơn mạng nơ-ron cổ điển hay không vẫn còn bỏ ngỏ. Đây là một công nghệ tiên tiến khá non trẻ nhưng tiềm năng của nó lại rất lớn trong ngành trí tuệ nhân tạo. Bài viết cũng đã dài, cảm ơn các bạn dành thời gian đọc, nếu có sai sót hoặc vướng mắc các bạn có thể comment ở bên dưới và mình sẽ nỗ lực giải đáp nếu trong tầm hiểu biết của mình ?. Hẹn các bạn trong bài viết tiếp theo.

References

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *