提交 af346570 编写于 作者: Q Quleaf

update to 2.0.1

上级 476abd12
......@@ -33,7 +33,7 @@ English | [简体中文](README_CN.md)
</a>
<!-- PyPI -->
<a href="https://pypi.org/project/paddle-quantum/">
<img src="https://img.shields.io/badge/pypi-v2.0.0-orange.svg?style=flat-square&logo=pypi"/>
<img src="https://img.shields.io/badge/pypi-v2.0.1-orange.svg?style=flat-square&logo=pypi"/>
</a>
<!-- Python -->
<a href="https://www.python.org/">
......@@ -55,12 +55,13 @@ Paddle Quantum aims at establishing a bridge between artificial intelligence (AI
## Features
- Easy-to-use
- Many online learning resources (16+ tutorials)
- Many online learning resources (17+ tutorials)
- High efficiency in building QNN with various QNN templates
- Automatic differentiation
- Versatile
- Multiple optimization tools and GPU mode
- Simulation with 25+ qubits
- Flexible noise models
- Featured Toolkits
- Toolboxes for Chemistry & Optimization
- LOCCNet for distributed quantum information processing
......@@ -143,8 +144,9 @@ We provide tutorials covering combinatorial optimization, quantum chemistry, qua
14. [Entanglement Distillation -- Protocol design with LOCCNet](./tutorial/LOCC)
15. [Quantum Teleportation](./tutorial/LOCC)
16. [Quantum State Discrimination](./tutorial/LOCC)
17. [Noise Model and Quantum Channel](./tutorial/Noise)
With the latest LOCCNet module, Paddle Quantum can efficiently simulate distributed quantum information processing tasks. Interested readers can start with this [tutorial on LOCCNet](./tutorial/LOCC/LOCCNET_Tutorial_EN.ipynb). In addition, Paddle Quantum supports QNN training on GPU. For users who want to get into more details, please check out the tutorial [Use Paddle Quantum on GPU](./introduction/PaddleQuantum_GPU_EN.ipynb).
With the latest LOCCNet module, Paddle Quantum can efficiently simulate distributed quantum information processing tasks. Interested readers can start with this [tutorial on LOCCNet](./tutorial/LOCC/LOCCNET_Tutorial_EN.ipynb). In addition, Paddle Quantum supports QNN training on GPU. For users who want to get into more details, please check out the tutorial [Use Paddle Quantum on GPU](./introduction/PaddleQuantum_GPU_EN.ipynb). Moreover, Paddle Quantum could design robust quantum algorithms under noise. For more information, please see [Noise tutorial](./tutorial/Noise/Noise_EN.ipynb).
### API documentation
......@@ -177,6 +179,8 @@ So far, we have done several projects with the help of Paddle Quantum as a power
[5] Wang, K., Song, Z., Zhao, X., Wang Z. & Wang, X. Detecting and quantifying entanglement on near-term quantum devices. arXiv:2012.14311 (2020). [[pdf]](https://arxiv.org/pdf/2012.14311.pdf)
[6] Zhao, X., Zhao, B., Wang, Z., Song, Z., & Wang, X. LOCCNet: a machine learning framework for distributed quantum information processing. arXiv:2101.12190 (2021). [[pdf]](https://arxiv.org/pdf/2101.12190.pdf)
## Frequently Asked Questions
1. **Question:** What is quantum machine learning? What are the applications?
......
......@@ -34,7 +34,7 @@
</a>
<!-- PyPI -->
<a href="https://pypi.org/project/paddle-quantum/">
<img src="https://img.shields.io/badge/pypi-v2.0.0-orange.svg?style=flat-square&logo=pypi"/>
<img src="https://img.shields.io/badge/pypi-v2.0.1-orange.svg?style=flat-square&logo=pypi"/>
</a>
<!-- Python -->
<a href="https://www.python.org/">
......@@ -55,12 +55,13 @@
## 特色
- 轻松上手
- 丰富的在线学习资源(16+ 教程案例)
- 丰富的在线学习资源(17+ 教程案例)
- 通过模板高效搭建量子神经网络
- 自动微分框架
- 功能丰富
- 提供多种优化工具和 GPU 模式
- 高性能模拟器支持25+量子比特的模拟运算
- 支持多种噪声模型的模拟
- 特色工具集
- 提供组合优化和量子化学等前沿领域的计算工具箱
- 分布式量子信息处理模组 LOCCNet
......@@ -148,8 +149,9 @@ Paddle Quantum(量桨)建立起了人工智能与量子计算的桥梁,为
- [纠缠蒸馏 -- LOCCNet 设计协议](./tutorial/LOCC)
- [量子隐态传输](./tutorial/LOCC)
- [量子态分辨](./tutorial/LOCC)
- [噪声模型与量子信道](./tutorial/Noise)
随着 LOCCNet 模组的推出,量桨现已支持分布式量子信息处理任务的高效模拟和开发。感兴趣的读者请参见[教程](./tutorial/LOCC/LOCCNET_Tutorial_CN.ipynb)此外,Paddle Quantum 也支持在 GPU 上进行量子机器学习的训练,具体的方法请参考案例:[在 GPU 上使用 Paddle Quantum](./introduction/PaddleQuantum_GPU_CN.ipynb)
随着 LOCCNet 模组的推出,量桨现已支持分布式量子信息处理任务的高效模拟和开发。感兴趣的读者请参见[教程](./tutorial/LOCC/LOCCNET_Tutorial_CN.ipynb)Paddle Quantum 也支持在 GPU 上进行量子机器学习的训练,具体的方法请参考案例:[在 GPU 上使用 Paddle Quantum](./introduction/PaddleQuantum_GPU_CN.ipynb)。此外,量桨可以基于噪声模块进行含噪算法的开发以及研究,详情请见[噪声模块教程](./tutorial/Noise/Noise_CN.ipynb)
### API 文档
......@@ -179,7 +181,7 @@ Paddle Quantum 使用 setuptools 的 develop 模式进行安装,相关代码
[5] Wang, K., et al. Detecting and quantifying entanglement on near-term quantum devices. arXiv:2012.14311 (2020). [[pdf]](https://arxiv.org/pdf/2012.14311.pdf)
[6] Zhao, X., Zhao, B., Wang, Z., Song, Z., & Wang, X. LOCCNet: a machine learning framework for distributed quantum information processing. arXiv:2101.12190 (2021). [[pdf]](https://arxiv.org/pdf/2101.12190.pdf)
## FAQ
......
......@@ -1551,7 +1551,6 @@
" self.theta = self.create_parameter(shape=shape, \n",
" default_initializer=paddle.nn.initializer.Uniform(low=0., high=2*np.pi), \n",
" dtype=dtype, is_bias=False)\n",
" self.H = paddle.to_tensor(H)\n",
" \n",
" # 定义损失函数和前向传播机制\n",
" def forward(self):\n",
......@@ -1704,7 +1703,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.0"
"version": "3.6.10"
},
"toc": {
"base_numbering": 1,
......
......@@ -17,281 +17,174 @@ Paddle_QAOA: To learn more about the functions and properties of this applicatio
you could check the corresponding Jupyter notebook under the Tutorial folder.
"""
import os
import paddle
from paddle_quantum.circuit import UAnsatz
from paddle_quantum.utils import pauli_str_to_matrix
from paddle_quantum.QAOA.QAOA_Prefunc import Generate_H_D, Generate_default_graph
from paddle_quantum.QAOA.QAOA_Prefunc import Draw_benchmark
from paddle_quantum.QAOA.QAOA_Prefunc import Draw_cut_graph, Draw_original_graph
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
import paddle
from paddle_quantum.circuit import UAnsatz
from paddle_quantum.utils import pauli_str_to_matrix
from paddle_quantum.QAOA.QAOA_Prefunc import generate_graph, H_generator
# Random seed for optimizer
SEED = 1024
__all__ = [
"circuit_QAOA",
"circuit_extend_QAOA",
"Net",
"Paddle_QAOA",
]
def circuit_QAOA(theta, adjacency_matrix, N, P):
def circuit_QAOA(E, V, n, p, gamma, beta):
"""
This function constructs the parameterized QAOA circuit which is composed of P layers of two blocks:
one block is based on the problem Hamiltonian H which encodes the classical problem,
and the other is constructed from the driving Hamiltonian describing the rotation around Pauli X
acting on each qubit. It outputs the final state of the QAOA circuit.
Args:
theta: parameters to be optimized in the QAOA circuit
adjacency_matrix: the adjacency matrix of the graph encoding the classical problem
N: number of qubits, or equivalently, the number of parameters in the original classical problem
P: number of layers of two blocks in the QAOA circuit
E: edges of the graph
V: vertices of the graph
n: number of qubits in th QAOA circuit
p: number of layers of two blocks in the QAOA circuit
gamma: parameter to be optimized in the QAOA circuit, parameter for the first block
beta: parameter to be optimized in the QAOA circui, parameter for the second block
Returns:
the QAOA circuit
"""
cir = UAnsatz(N)
# prepare the input state in the uniform superposition of 2^N bit-strings in the computational basis
cir = UAnsatz(n)
cir.superposition_layer()
# This loop defines the QAOA circuit with P layers of two blocks
for layer in range(P):
# The second and third loops construct the first block which involves two-qubit operation
# e^{-i\gamma Z_iZ_j} acting on a pair of qubits or nodes i and j in the circuit in each layer.
for row in range(N):
for col in range(N):
if adjacency_matrix[row, col] and row < col:
cir.cnot([row, col])
cir.rz(theta[layer][0], col)
cir.cnot([row, col])
# This loop constructs the second block only involving the single-qubit operation e^{-i\beta X}.
for i in range(N):
cir.rx(theta[layer][1], i)
for layer in range(p):
for (u, v) in E:
cir.cnot([u, v])
cir.rz(gamma[layer], v)
cir.cnot([u, v])
for v in V:
cir.rx(beta[layer], v)
return cir
def circuit_extend_QAOA(theta, adjacency_matrix, N, P):
"""
This is an extended version of the QAOA circuit, and the main difference is the block constructed
from the driving Hamiltonian describing the rotation around an arbitrary direction on each qubit.
Args:
theta: parameters to be optimized in the QAOA circuit
input_state: input state of the QAOA circuit which usually is the uniform superposition of 2^N bit-strings
in the computational basis
adjacency_matrix: the adjacency matrix of the problem graph encoding the original problem
N: number of qubits, or equivalently, the number of parameters in the original classical problem
P: number of layers of two blocks in the QAOA circuit
Returns:
the extended QAOA circuit
Note:
If this circuit_extend_QAOA function is used to construct QAOA circuit, then we need to change the parameter layer
in the Net function defined below from the Net(shape=[D, 2]) for circuit_QAOA function to Net(shape=[D, 4])
because the number of parameters doubles in each layer in this QAOA circuit.
"""
cir = UAnsatz(N)
# prepare the input state in the uniform superposition of 2^N bit-strings in the computational basis
cir.superposition_layer()
for layer in range(P):
for row in range(N):
for col in range(N):
if adjacency_matrix[row, col] and row < col:
cir.cnot([row, col])
cir.rz(theta[layer][0], col)
cir.cnot([row, col])
for i in range(N):
cir.u3(*theta[layer][1:], i)
return cir
class Net(paddle.nn.Layer):
"""
It constructs the net for QAOA which combines the QAOA circuit with the classical optimizer which sets rules
to update parameters described by theta introduced in the QAOA circuit.
"""
def __init__(
self,
shape,
param_attr=paddle.nn.initializer.Uniform(low=0.0, high=np.pi),
dtype="float64",
self,
p,
dtype="float64",
):
super(Net, self).__init__()
self.theta = self.create_parameter(
shape=shape, attr=param_attr, dtype=dtype, is_bias=False
)
def forward(self, adjacency_matrix, N, P, METHOD):
"""
This function constructs the loss function for the QAOA circuit.
Args:
adjacency_matrix: the adjacency matrix generated from the graph encoding the classical problem
N: number of qubits
P: number of layers
METHOD: which version of QAOA is chosen to solve the problem, i.e., standard version labeled by 1 or
extended version by 2.
Returns:
the loss function for the parameterized QAOA circuit and the circuit itself
"""
# Generate the problem_based quantum Hamiltonian H_problem based on the classical problem in paddle
H_problem = H_generator(N, adjacency_matrix)
# The standard QAOA circuit: the function circuit_QAOA is used to construct the circuit, indexed by METHOD 1.
if METHOD == 1:
cir = circuit_QAOA(self.theta, adjacency_matrix, N, P)
# The extended QAOA circuit: the function circuit_extend_QAOA is used to construct the net, indexed by METHOD 2.
elif METHOD == 2:
cir = circuit_extend_QAOA(self.theta, adjacency_matrix, N, P)
else:
raise ValueError("Wrong method called!")
self.p = p
self.gamma = self.create_parameter(shape = [self.p],
default_initializer = paddle.nn.initializer.Uniform(
low = 0.0,
high = 2 * np.pi
),
dtype = dtype,
is_bias = False)
self.beta = self.create_parameter(shape = [self.p],
default_initializer = paddle.nn.initializer.Uniform(
low = 0.0,
high = 2 * np.pi
),
dtype = dtype, is_bias = False)
def forward(self, n, E, V, H_D_list):
cir = circuit_QAOA(E, V, n, self.p, self.gamma, self.beta)
cir.run_state_vector()
loss = cir.expecval(H_problem)
loss = -cir.expecval(H_D_list)
return loss, cir
def Paddle_QAOA(classical_graph_adjacency, N, P, METHOD, ITR, LR):
def Paddle_QAOA(n, p, E, V, H_D_list, ITR, LR):
"""
This is the core function to run QAOA.
Args:
classical_graph_adjacency: adjacency matrix to describe the graph which encodes the classical problem
N: number of qubits (default value N=4)
P: number of layers of blocks in the QAOA circuit (default value P=4)
METHOD: which version of the QAOA circuit is used: 1, standard circuit (default); 2, extended circuit
n: number of qubits (default value N=4)
E: edges of the graph
V: vertices of the graph
p: number of layers of blocks in the QAOA circuit (default value p=4)
ITR: number of iteration steps for QAOA (default value ITR=120)
LR: learning rate for the gradient-based optimization method (default value LR=0.1)
Returns:
the optimized QAOA circuit
summary_iter
summary_loss
"""
# Construct the net or QAOA circuits based on the standard modules
if METHOD == 1:
net = Net(shape=[P, 2])
# Construct the net or QAOA circuits based on the extended modules
elif METHOD == 2:
net = Net(shape=[P, 4])
else:
raise ValueError("Wrong method called!")
# Classical optimizer
net = Net(p)
opt = paddle.optimizer.Adam(learning_rate=LR, parameters=net.parameters())
# Gradient descent loop
summary_iter, summary_loss = [], []
for itr in range(1, ITR + 1):
loss, cir = net(
classical_graph_adjacency, N, P, METHOD
)
loss, cir = net(n, E, V, H_D_list)
loss.backward()
opt.minimize(loss)
opt.clear_grad()
if itr % 10 == 0:
print("iter:", itr, " loss:", "%.4f" % loss.numpy())
summary_loss.append(loss[0][0].numpy())
summary_iter.append(itr)
theta_opt = net.parameters()[0].numpy()
# print("Optimized parameters theta:\n", theta_opt)
gamma_opt = net.gamma.numpy()
print("优化后的参数 gamma:\n", gamma_opt)
beta_opt = net.beta.numpy()
print("优化后的参数 beta:\n", beta_opt)
os.makedirs("output", exist_ok=True)
np.savez("./output/summary_data", iter=summary_iter, energy=summary_loss)
return cir, summary_iter, summary_loss
return cir
def main(N=4):
def main(n = 4, E = None):
paddle.seed(SEED)
# number of qubits or number of nodes in the graph
N = 4
classical_graph, classical_graph_adjacency = generate_graph(N, GRAPHMETHOD=1)
print(classical_graph_adjacency)
# Convert the Hamiltonian's list form to matrix form
H_matrix = pauli_str_to_matrix(H_generator(N, classical_graph_adjacency), N)
H_diag = np.diag(H_matrix).real
H_max = np.max(H_diag)
H_min = np.min(H_diag)
print(H_diag)
print('H_max:', H_max, ' H_min:', H_min)
pos = nx.circular_layout(classical_graph)
nx.draw(classical_graph, pos, width=4, with_labels=True, font_weight='bold')
plt.show()
classical_graph, classical_graph_adjacency = generate_graph(N, 1)
opt_cir = Paddle_QAOA(classical_graph_adjacency, N=4, P=4, METHOD=1, ITR=120, LR=0.1)
# Load the data of QAOA
x1 = np.load('./output/summary_data.npz')
H_min = np.ones([len(x1['iter'])]) * H_min
# Plot loss
loss_QAOA, = plt.plot(x1['iter'], x1['energy'], alpha=0.7, marker='', linestyle="--", linewidth=2, color='m')
benchmark, = plt.plot(x1['iter'], H_min, alpha=0.7, marker='', linestyle=":", linewidth=2, color='b')
plt.xlabel('Number of iteration')
plt.ylabel('Performance of the loss function for QAOA')
plt.legend(handles=[
loss_QAOA,
benchmark
],
labels=[
r'Loss function $\left\langle {\psi \left( {\bf{\theta }} \right)} '
r'\right|H\left| {\psi \left( {\bf{\theta }} \right)} \right\rangle $',
'The benchmark result',
], loc='best')
# Show the plot
plt.show()
# Measure the output state of the QAOA circuit for 1024 shots by default
prob_measure = opt_cir.measure(plot=True)
# Find the max value in measured probability of bitstrings
max_prob = max(prob_measure.values())
# Find the bitstring with max probability
solution_list = [result[0] for result in prob_measure.items() if result[1] == max_prob]
print("The output bitstring:", solution_list)
# Draw the graph representing the first bitstring in the solution_list to the MaxCut-like problem
head_bitstring = solution_list[0]
node_cut = ["blue" if head_bitstring[node] == "1" else "red" for node in classical_graph]
edge_cut = [
"solid" if head_bitstring[node_row] == head_bitstring[node_col] else "dashed"
for node_row, node_col in classical_graph.edges()
]
nx.draw(
classical_graph,
pos,
node_color=node_cut,
style=edge_cut,
width=4,
with_labels=True,
font_weight="bold",
)
plt.show()
p = 4 # number of layers in the circuit
ITR = 120 #number of iterations
LR = 0.1 #learning rate
if E is None:
G, V, E = Generate_default_graph(n)
else:
G = nx.Graph()
V = range(n)
G.add_nodes_from(V)
G.add_edges_from(E)
Draw_original_graph(G)
#construct the Hamiltonia
H_D_list, H_D_matrix = Generate_H_D(E, n)
H_D_diag = np.diag(H_D_matrix).real
H_max = np.max(H_D_diag)
H_min = -H_max
print(H_D_diag)
print('H_max:', H_max, ' H_min:', H_min)
cir, summary_iter, summary_loss = Paddle_QAOA(n, p, E, V, H_D_list, ITR, LR)
H_min = np.ones([len(summary_iter)]) * H_min
Draw_benchmark(summary_iter, summary_loss, H_min)
prob_measure = cir.measure(plot=True)
cut_bitstring = max(prob_measure, key=prob_measure.get)
print("找到的割的比特串形式:", cut_bitstring)
Draw_cut_graph(V, E, G, cut_bitstring)
if __name__ == "__main__":
main()
n = int(input("Please input the number of vertices: "))
user_input_edge_flag = int(input("Please choose if you want to input edges yourself (0 for yes, 1 for no): "))
if user_input_edge_flag == 1:
main(n)
else:
E = []
prompt = "Please input tuples indicating edges (e.g., (0, 1)), input 'z' if finished: "
while True:
edge = input(prompt)
if edge == 'z':
main(n, E)
break
else:
edge = eval(edge)
E.append(edge)
......@@ -15,134 +15,173 @@
"""
Aid func
"""
from matplotlib import pyplot
import matplotlib.pyplot as plt
import numpy as np
from numpy import abs, array, binary_repr, diag, kron, max, ones, real, where, zeros
import networkx
import networkx as nx
from paddle_quantum.utils import pauli_str_to_matrix
def plot_graph(measure_prob_distribution, graph, N):
def Draw_original_graph(G):
"""
This function plots the graph encoding the combinatorial problem such as Max-Cut and the final graph encoding the
approximate solution obtained from QAOA
This is to draw the original graph
Args:
G: the constructed graph
Returns:
Null
"""
pos = nx.circular_layout(G)
options = {
"with_labels": True,
"font_size": 20,
"font_weight": "bold",
"font_color": "white",
"node_size": 2000,
"width": 2
}
nx.draw_networkx(G, pos, **options)
ax = plt.gca()
ax.margins(0.20)
plt.axis("off")
plt.show()
return
def Draw_cut_graph(V, E, G, cut_bitstring):
"""
This is to draw the graph after cutting
Args:
measure_prob_distribution: the measurement probability distribution which is sampled from the output state
of optimized QAOA circuit.
graph: graph encoding the topology of the classical combinatorial problem, such as Max-Cut problem
N: number of qubits, or number of nodes in the graph
Return:
three graphs: the first displays the graph topology of the classical problem;
the second is the bar graph for the measurement distribution for each bitstring in the output state
the third plots the graph corresponding to the bitstring with maximal measurement probability
V: vertices in the graph
E: edges in the graph
cut_bitstring: bit string indicate whether vertices belongs to the first group or the second group
Returns:
Null
"""
# Find the position of max value in the measure_prob_distribution
max_prob_pos_list = where(
measure_prob_distribution == max(measure_prob_distribution))
# Store the max value from ndarray to list
max_prob_list = max_prob_pos_list[0].tolist()
# Store it in the binary format
solution_list = [binary_repr(index, width=N) for index in max_prob_list]
print("The output bitstring:", solution_list)
# Draw the graph representing the first bitstring in the solution_list to the MaxCut-like problem
head_bitstring = solution_list[0]
node_cut = [
"blue" if head_bitstring[node] == "1" else "red" for node in graph
]
node_cut = ["blue" if cut_bitstring[v] == "1" else "red" for v in V]
edge_cut = [
"solid"
if head_bitstring[node_row] == head_bitstring[node_col] else "dashed"
for node_row, node_col in graph.edges()
]
pos = networkx.circular_layout(graph)
pyplot.figure(0)
networkx.draw(graph, pos, width=4, with_labels=True, font_weight="bold")
# when N is large, it is not suggested to plot this figure
pyplot.figure(1)
name_list = [binary_repr(index, width=N) for index in range(0, 2**N)]
pyplot.bar(
range(len(real(measure_prob_distribution))),
real(measure_prob_distribution),
width=0.7,
tick_label=name_list, )
pyplot.xticks(rotation=90)
pyplot.figure(2)
networkx.draw(
graph,
pos,
node_color=node_cut,
style=edge_cut,
width=4,
with_labels=True,
font_weight="bold", )
pyplot.show()
def generate_graph(N, GRAPHMETHOD):
"solid" if cut_bitstring[u] == cut_bitstring[v] else "dashed"
for (u, v) in G.edges()
]
pos = nx.circular_layout(G)
options = {
"with_labels": True,
"font_size": 20,
"font_weight": "bold",
"font_color": "white",
"node_size": 2000,
"width": 2
}
nx.draw(
G,
pos,
node_color = node_cut,
style = edge_cut,
**options
)
ax = plt.gca()
ax.margins(0.20)
plt.axis("off")
plt.show()
return
def Generate_default_graph(n):
"""
It plots an N-node graph which is specified by Method 1 or 2.
Args:
N: number of nodes (vertices) in the graph
METHOD: choose which method to generate a graph
Returns:
the specific graph and its adjacency matrix
This is to generate a default graph if no input
Args:
n: number of vertices
Returns:
G: the graph
E: edges list
V: vertices list
"""
G = nx.Graph()
V = range(n)
G.add_nodes_from(V)
E = []
for i in range(n - 1):
E.append((i, i + 1))
E.append((0, n - 1))
G.add_edges_from(E)
return G, V, E
def Generate_H_D(E, n):
"""
# Method 1 generates a graph by self-definition
if GRAPHMETHOD == 1:
print("Method 1 generates the graph from self-definition using EDGE description")
graph = networkx.Graph()
graph_nodelist = range(N)
graph.add_edges_from([(0, 1), (1, 2), (2, 3), (3, 0)])
graph_adjacency = networkx.to_numpy_matrix(graph, nodelist=graph_nodelist)
# Method 2 generates a graph by using its adjacency matrix directly
elif GRAPHMETHOD == 2:
print("Method 2 generates the graph from networks using adjacency matrix")
graph_adjacency = np.array([[0, 1, 0, 1], [1, 0, 1, 0], [0, 1, 0, 1], [1, 0, 1, 0]])
graph = networkx.Graph(graph_adjacency)
else:
print("Method doesn't exist ")
return graph, graph_adjacency
def H_generator(N, adjacency_matrix):
This is to construct Hamiltonia H_D
Args:
E: edges of the graph
Returns:
Hamiltonia list
Hamiltonia H_D
"""
This function maps the given graph via its adjacency matrix to the corresponding Hamiltiona H_c.
H_D_list = []
for (u, v) in E:
H_D_list.append([-1.0, 'z' + str(u) + ',z' + str(v)])
print(H_D_list)
H_D_matrix = pauli_str_to_matrix(H_D_list, n)
return H_D_list, H_D_matrix
def Draw_benchmark(summary_iter, summary_loss, H_min):
"""
This is draw the learning tendency, and difference bwtween it and the benchmark
Args:
N: number of qubits, or number of nodes in the graph, or number of parameters in the classical problem
adjacency_matrix: the adjacency matrix generated from the graph encoding the classical problem
summary_iter: indicate which iteration
summary_loss: indicate the energy of that iteration
H_min: benchmark value H_min
Returns:
the problem-based Hmiltonian H's list form generated from the graph_adjacency matrix for the given graph
NULL
"""
H_list = []
# Generate the Hamiltonian H_c from the graph via its adjacency matrix
for row in range(N):
for col in range(N):
if adjacency_matrix[row, col] and row < col:
# Construct the Hamiltonian in the list form for the calculation of expectation value
H_list.append([1.0, 'z' + str(row) + ',z' + str(col)])
return H_list
plt.figure(1)
loss_QAOA, = plt.plot(
summary_iter,
summary_loss,
alpha=0.7,
marker='',
linestyle="--",
linewidth=2,
color='m')
benchmark, = plt.plot(
summary_iter,
H_min,
alpha=0.7,
marker='',
linestyle=":",
linewidth=2,
color='b')
plt.xlabel('Number of iteration')
plt.ylabel('Performance of the loss function for QAOA')
plt.legend(
handles=[loss_QAOA, benchmark],
labels=[
r'Loss function $\left\langle {\psi \left( {\bf{\theta }} \right)} '
r'\right|H\left| {\psi \left( {\bf{\theta }} \right)} \right\rangle $',
'The benchmark result',
],
loc='best')
# Show the picture
plt.show()
return
def main():
# number of qubits or number of nodes in the graph
N = 4
classical_graph, classical_graph_adjacency = generate_graph(N, GRAPHMETHOD=1)
print(classical_graph_adjacency)
pos = networkx.circular_layout(classical_graph)
networkx.draw(classical_graph, pos, width=4, with_labels=True, font_weight='bold')
pyplot.show()
n = 4
G, V, E = Generate_default_graph(n)
Draw_original_graph(G)
if __name__ == "__main__":
......
......@@ -15,93 +15,41 @@
"""
main
"""
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
import paddle
from paddle_quantum.utils import pauli_str_to_matrix
from paddle_quantum.QAOA.QAOA_Prefunc import Generate_H_D, Draw_cut_graph, Draw_original_graph, Generate_default_graph
from paddle_quantum.QAOA.Paddle_QAOA import Paddle_QAOA
from paddle_quantum.QAOA.QAOA_Prefunc import generate_graph, H_generator
def main(N=4):
# number of qubits or number of nodes in the graph
N = 4
classical_graph, classical_graph_adjacency = generate_graph(N, GRAPHMETHOD=1)
print(classical_graph_adjacency)
# Convert the Hamiltonian's list form to matrix form
H_matrix = pauli_str_to_matrix(H_generator(N, classical_graph_adjacency), N)
H_diag = np.diag(H_matrix).real
H_max = np.max(H_diag)
H_min = np.min(H_diag)
print(H_diag)
print('H_max:', H_max, ' H_min:', H_min)
pos = nx.circular_layout(classical_graph)
nx.draw(classical_graph, pos, width=4, with_labels=True, font_weight='bold')
plt.show()
classical_graph, classical_graph_adjacency = generate_graph(N, 1)
opt_cir = Paddle_QAOA(classical_graph_adjacency, N=4, P=4, METHOD=1, ITR=120, LR=0.1)
# Load the data of QAOA
x1 = np.load('./output/summary_data.npz')
H_min = np.ones([len(x1['iter'])]) * H_min
# Plot loss
loss_QAOA, = plt.plot(x1['iter'], x1['energy'], alpha=0.7, marker='', linestyle="--", linewidth=2, color='m')
benchmark, = plt.plot(x1['iter'], H_min, alpha=0.7, marker='', linestyle=":", linewidth=2, color='b')
plt.xlabel('Number of iteration')
plt.ylabel('Performance of the loss function for QAOA')
plt.legend(handles=[
loss_QAOA,
benchmark
],
labels=[
r'Loss function $\left\langle {\psi \left( {\bf{\theta }} \right)} '
r'\right|H\left| {\psi \left( {\bf{\theta }} \right)} \right\rangle $',
'The benchmark result',
], loc='best')
# Show the plot
plt.show()
# Measure the output state of the QAOA circuit for 1024 shots by default
prob_measure = opt_cir.measure(plot=True)
# Find the max value in measured probability of bitstrings
max_prob = max(prob_measure.values())
# Find the bitstring with max probability
solution_list = [result[0] for result in prob_measure.items() if result[1] == max_prob]
print("The output bitstring:", solution_list)
# Draw the graph representing the first bitstring in the solution_list to the MaxCut-like problem
head_bitstring = solution_list[0]
node_cut = ["blue" if head_bitstring[node] == "1" else "red" for node in classical_graph]
edge_cut = [
"solid" if head_bitstring[node_row] == head_bitstring[node_col] else "dashed"
for node_row, node_col in classical_graph.edges()
]
nx.draw(
classical_graph,
pos,
node_color=node_cut,
style=edge_cut,
width=4,
with_labels=True,
font_weight="bold",
)
plt.show()
SEED = 1024
def main(n = 4):
paddle.seed(SEED)
p = 4 # number of layers in the circuit
ITR = 120 #number of iterations
LR = 0.1 #learning rate
G, V, E = Generate_default_graph(n)
G.add_nodes_from(V)
G.add_edges_from(E)
Draw_original_graph(G)
#construct the Hamiltonia
H_D_list, H_D_matrix = Generate_H_D(E, n)
H_D_diag = np.diag(H_D_matrix).real
H_max = np.max(H_D_diag)
print(H_D_diag)
print('H_max:', H_max)
cir, _, _ = Paddle_QAOA(n, p, E, V, H_D_list, ITR, LR)
prob_measure = cir.measure(plot=True)
cut_bitstring = max(prob_measure, key=prob_measure.get)
print("找到的割的比特串形式:", cut_bitstring)
Draw_cut_graph(V, E, G, cut_bitstring)
if __name__ == "__main__":
......
......@@ -17,4 +17,4 @@ Paddle Quantum Library
"""
name = "paddle_quantum"
__version__ = "2.0.0"
__version__ = "2.0.1"
此差异已折叠。
......@@ -13,11 +13,13 @@
# limitations under the License.
import math
from functools import wraps
import numpy as np
from numpy import binary_repr
import paddle
from paddle import multiply, add, to_tensor
from paddle_quantum.simulator import StateTransfer
def dic_between2and10(n):
......@@ -115,3 +117,38 @@ def vec_expecval(H, vec):
vec_conj = paddle.conj(vec)
result = paddle.sum(multiply(vec_conj, H_vec(H, vec)))
return result
def transfer_by_history(state, history):
r"""
It transforms the input state according to the history give.
Note:
这是内部函数,你并不需要直接调用到该函数。
"""
for history_ele in history:
if history_ele[0] != 'channel':
state = StateTransfer(state, history_ele[0], history_ele[1], params=history_ele[2])
return state
def apply_channel(func):
r"""
Decorator for channels.
Note:
这是内部函数,你并不需要直接调用到该函数。
"""
@wraps(func)
def inner(self, *args):
"""
args should include channel parameters and which_qubit
"""
which_qubit = args[-1]
assert 0 <= which_qubit < self.n, "the qubit's index should >= 0 and < n(the number of qubit)"
self._UAnsatz__has_channel = True
ops = func(self, *args)
self._UAnsatz__history.append(['channel', ops, [which_qubit]])
return inner
......@@ -136,7 +136,7 @@ class LoccAnsatz(UAnsatz):
status (LoccStatus or list): 作为LOCC下的量子电路的输入的 LOCC 态节点,其类型应该为 ``LoccStatus`` 或由其组成的 ``list``
Returns:
LoccStatus or list: 量子线路运行后得到的 LOCC 态节点,类型为 ``LoccStatus`` 或由其组成的 ``list``
LoccStatus or list: 量子路运行后得到的 LOCC 态节点,类型为 ``LoccStatus`` 或由其组成的 ``list``
"""
if isinstance(status, LoccStatus):
assert int(log2(sqrt(status.state.numpy().size))) == self.n, "the length of qubits should be same"
......@@ -262,11 +262,34 @@ class LoccAnsatz(UAnsatz):
theta (Tensor): 旋转角度 :math:`\theta` 。
phi (Tensor): 旋转角度 :math:`\phi` 。
lam (Tensor): 旋转角度 :math:`\lambda` 。
which_qubit (int): 作用在的 qubit 的编号,其值应该在 :math:`[0, m)` 范围内, :math:`m` 为该量子线路的量子比特数
which_qubit (int): 作用在的 qubit 的编号,其值应该在 :math:`[0, m)` 范围内, :math:`m` 为该量子路的量子比特数
"""
which_qubit = self.party[which_qubit]
super(LoccAnsatz, self).u3(theta, phi, lam, which_qubit)
def universal_2_qubit_gate(self, theta, which_qubits):
r"""添加 2-qubit 通用门,这个通用门需要 15 个参数。
Args:
theta (Tensor): 2-qubit 通用门的参数,其维度为 ``(15, )``
which_qubits(list): 作用的量子比特编号
"""
super(LoccAnsatz, self).universal_2_qubit_gate(theta, which_qubits)
def universal_3_qubit_gate(self, theta, which_qubits):
r"""添加 3-qubit 通用门,这个通用门需要 81 个参数。
Note:
参考: https://cds.cern.ch/record/708846/files/0401178.pdf
Args:
theta (Tensor): 3-qubit 通用门的参数,其维度为 ``(81, )``
which_qubits(list): 作用的量子比特编号
"""
super(LoccAnsatz, self).universal_3_qubit_gate(theta, which_qubits)
def superposition_layer(self):
r"""添加一层 Hadamard 门。
"""
......@@ -280,7 +303,37 @@ class LoccAnsatz(UAnsatz):
for which_qubit in self.party.qubits:
self.ry(_theta, which_qubit)
def real_entangled_layer(self, theta, depth):
def linear_entangled_layer(self, theta, depth, which_qubits=None):
r"""添加 ``depth`` 层包含 Ry 门,Rz 门和 CNOT 门的线性纠缠层。
Attention:
``theta`` 的维度为 ``(depth, m, 2)`` ,最低维内容为对应的 ``ry`` 和 ``rz`` 的参数。
Args:
theta (Tensor): Ry 门和 Rz 门的旋转角度
depth (int): 纠缠层的深度
which_qubits(list): 作用的量子比特编号
"""
assert self.m > 1, 'you need at least 2 qubits'
assert len(theta.shape) == 3, 'the shape of theta is not right'
assert theta.shape[2] == 2, 'the shape of theta is not right'
# assert theta.shape[1] == self.m, 'the shape of theta is not right'
assert theta.shape[0] == depth, 'the depth of theta has a mismatch'
if which_qubits is None:
which_qubits = list(range(self.m))
for repeat in range(depth):
for i, q in enumerate(which_qubits):
self.ry(theta[repeat][i][0], q)
for i in range(len(which_qubits) - 1):
self.cnot([which_qubits[i], which_qubits[i + 1]])
for i, q in enumerate(which_qubits):
self.rz(theta[repeat][i][1], q)
for i in range(len(which_qubits) - 1):
self.cnot([which_qubits[i + 1], which_qubits[i]])
def real_entangled_layer(self, theta, depth, which_qubits=None):
r"""添加 ``depth`` 层包含 Ry 门和 CNOT 门的强纠缠层。
Note:
......@@ -297,15 +350,18 @@ class LoccAnsatz(UAnsatz):
assert self.m > 1, 'you need at least 2 qubits'
assert len(theta.shape) == 3, 'the shape of theta is not right'
assert theta.shape[2] == 1, 'the shape of theta is not right'
assert theta.shape[1] == len(self.party), 'the shape of theta is not right'
# assert theta.shape[1] == len(self.party), 'the shape of theta is not right'
assert theta.shape[0] == depth, 'the depth of theta has a mismatch'
if which_qubits is None:
which_qubits = list(range(self.m))
for repeat in range(depth):
for i in range(self.party.qubits):
self.ry(theta=theta[repeat][i][0], which_qubit=i)
for idx in range(0, self.m - 1):
self.cnot(control=[idx, idx + 1])
self.cnot([len(self.party) - 1, 0])
for i, q in enumerate(which_qubits):
self.ry(theta[repeat][i][0], q)
for i in range(len(which_qubits) - 1):
self.cnot([which_qubits[i], which_qubits[i + 1]])
self.cnot([which_qubits[-1], which_qubits[0]])
def complex_entangled_layer(self, theta, depth, which_qubits=None):
r"""添加 ``depth`` 层包含 U3 门和 CNOT 门的强纠缠层。
......@@ -321,44 +377,35 @@ class LoccAnsatz(UAnsatz):
depth (int): 纠缠层的深度
which_qubits(list): 作用的量子比特编号
"""
if which_qubits is None:
which_qubits = list(range(0, len(self.party)))
assert self.m > 1, 'you need at least 2 qubits'
assert len(theta.shape) == 3, 'the shape of theta is not right'
assert theta.shape[2] == 3, 'the shape of theta is not right'
# assert theta.shape[1] == self.m, 'the shape of theta is not right'
assert theta.shape[0] == depth, 'the depth of theta has a mismatch'
if which_qubits is None:
which_qubits = list(range(self.m))
for repeat in range(depth):
for i, q in enumerate(which_qubits):
self.u3(theta[repeat][i][0], theta[repeat][i][1], theta[repeat][i][2], q)
for i in range(self.m - 1):
for i in range(len(which_qubits) - 1):
self.cnot([which_qubits[i], which_qubits[i + 1]])
self.cnot([which_qubits[-1], which_qubits[0]])
def universal_2_qubit_gate(self, theta, which_qubits):
r"""添加 2-qubit 通用门,这个通用门需要 15 个参数。
Args:
theta (Tensor): 2-qubit 通用门的参数,其维度为 ``(15, )``
which_qubits(list): 作用的量子比特编号
"""
super(LoccAnsatz, self).universal_2_qubit_gate(theta, which_qubits)
def universal_3_qubit_gate(self, theta, which_qubits):
r"""添加 3-qubit 通用门,这个通用门需要 81 个参数。
Note:
参考: https://cds.cern.ch/record/708846/files/0401178.pdf
def real_block_layer(self, theta, depth):
r"""添加 ``depth`` 层包含 Ry 门和 CNOT 门的弱纠缠层。
Note:
这一层量子门的数学表示形式为实数酉矩阵。
Attention:
``theta`` 的维度为 ``(depth, m-1, 4)`` 。
Args:
theta (Tensor): 3-qubit 通用门的参数,其维度为 ``(81, )``
which_qubits(list): 作用的量子比特编号
theta(Tensor): Ry 门的旋转角度
depth(int): 纠缠层的深度
"""
super(LoccAnsatz, self).universal_3_qubit_gate(theta, which_qubits)
def real_block_layer(self, theta, depth):
assert self.m > 1, 'you need at least 2 qubits'
assert len(theta.shape) == 3, 'The dimension of theta is not right'
_depth, _number, block = theta.shape
......@@ -376,6 +423,18 @@ class LoccAnsatz(UAnsatz):
self.__add_real_layer(theta[i][int((self.m - 1) / 2):], [1, self.m - 1])
def complex_block_layer(self, theta, depth):
r"""添加 ``depth`` 层包含 U3 门和 CNOT 门的弱纠缠层。
Note:
这一层量子门的数学表示形式为复数酉矩阵。
Attention:
``theta`` 的维度为 ``(depth, m-1, 12)`` 。
Args:
theta (Tensor): U3 门的角度信息
depth (int): 纠缠层的深度
"""
assert self.m > 1, 'you need at least 2 qubits'
assert len(theta.shape) == 3, 'The dimension of theta is not right'
assert depth > 0, 'depth must be greater than zero'
......@@ -450,6 +509,154 @@ class LoccAnsatz(UAnsatz):
for i in range(position[0], position[1], 2):
self.__add_complex_block(theta[int((i - position[0]) / 2)], [i, i + 1])
def amplitude_damping(self, gamma, which_qubit):
r"""添加振幅阻尼信道。
其 Kraus 算符为:
.. math::
E_0 = \begin{bmatrix} 1 & 0 \\ 0 & \sqrt{1-\gamma} \end{bmatrix},
E_1 = \begin{bmatrix} 0 & \sqrt{\gamma} \\ 0 & 0 \end{bmatrix}.
Args:
gamma (float): 减振概率,其值应该在 :math:`[0, 1]` 区间内
which_qubit (int): 该信道作用在的 qubit 的编号,其值应该在 :math:`[0, m)` 范围内, :math:`m` 为该参与方的量子比特数
"""
which_qubit = self.party[which_qubit]
super(LoccAnsatz, self).amplitude_damping(gamma, which_qubit)
def generalized_amplitude_damping(self, gamma, p, which_qubit):
r"""添加广义振幅阻尼信道。
其 Kraus 算符为:
.. math::
E_0 = \sqrt(p) \begin{bmatrix} 1 & 0 \\ 0 & \sqrt{1-\gamma} \end{bmatrix},
E_1 = \sqrt(p) \begin{bmatrix} 0 & \sqrt{\gamma} \\ 0 & 0 \end{bmatrix},\\
E_2 = \sqrt(1-p) \begin{bmatrix} \sqrt{1-\gamma} & 0 \\ 0 & 1 \end{bmatrix},
E_3 = \sqrt(1-p) \begin{bmatrix} 0 & 0 \\ \sqrt{\gamma} & 0 \end{bmatrix}.
Args:
gamma (float): 减振概率,其值应该在 :math:`[0, 1]` 区间内
p (float): 激发概率,其值应该在 :math:`[0, 1]` 区间内
which_qubit (int): 该信道作用在的 qubit 的编号,其值应该在 :math:`[0, m)` 范围内, :math:`m` 为该参与方的量子比特数
"""
which_qubit = self.party[which_qubit]
super(LoccAnsatz, self).generalized_amplitude_damping(gamma, p, which_qubit)
def phase_damping(self, gamma, which_qubit):
r"""添加相位阻尼信道。
其 Kraus 算符为:
.. math::
E_0 = \begin{bmatrix} 1 & 0 \\ 0 & \sqrt{1-\gamma} \end{bmatrix},
E_1 = \begin{bmatrix} 0 & 0 \\ 0 & \sqrt{\gamma} \end{bmatrix}.
Args:
gamma (float): phase damping 信道的参数,其值应该在 :math:`[0, 1]` 区间内
which_qubit (int): 该信道作用在的 qubit 的编号,其值应该在 :math:`[0, m)` 范围内, :math:`m` 为该参与方的量子比特数
"""
which_qubit = self.party[which_qubit]
super(LoccAnsatz, self).phase_damping(gamma, which_qubit)
def bit_flip(self, p, which_qubit):
r"""添加比特反转信道。
其 Kraus 算符为:
.. math::
E_0 = \sqrt{1-p} I,
E_1 = \sqrt{p} X.
Args:
p (float): 发生 bit flip 的概率,其值应该在 :math:`[0, 1]` 区间内
which_qubit (int): 该信道作用在的 qubit 的编号,其值应该在 :math:`[0, m)` 范围内, :math:`m` 为该参与方的量子比特数
"""
which_qubit = self.party[which_qubit]
super(LoccAnsatz, self).bit_flip(p, which_qubit)
def phase_flip(self, p, which_qubit):
r"""添加相位反转信道。
其 Kraus 算符为:
.. math::
E_0 = \sqrt{1 - p} I,
E_1 = \sqrt{p} Z.
Args:
p (float): 发生 phase flip 的概率,其值应该在 :math:`[0, 1]` 区间内
which_qubit (int): 该信道作用在的 qubit 的编号,其值应该在 :math:`[0, m)` 范围内, :math:`m` 为该参与方的量子比特数
"""
which_qubit = self.party[which_qubit]
super(LoccAnsatz, self).phase_flip(p, which_qubit)
def bit_phase_flip(self, p, which_qubit):
r"""添加比特相位反转信道。
其 Kraus 算符为:
.. math::
E_0 = \sqrt{1 - p} I,
E_1 = \sqrt{p} Y.
Args:
p (float): 发生 bit phase flip 的概率,其值应该在 :math:`[0, 1]` 区间内
which_qubit (int): 该信道作用在的 qubit 的编号,其值应该在 :math:`[0, m)` 范围内, :math:`m` 为该参与方的量子比特数
"""
which_qubit = self.party[which_qubit]
super(LoccAnsatz, self).bit_phase_flip(p, which_qubit)
def depolarizing(self, p, which_qubit):
r"""添加去极化信道。
其 Kraus 算符为:
.. math::
E_0 = \sqrt{1-p} I,
E_1 = \sqrt{p/3} X,
E_2 = \sqrt{p/3} Y,
E_3 = \sqrt{p/3} Z.
Args:
p (float): depolarizing 信道的参数,其值应该在 :math:`[0, 1]` 区间内
which_qubit (int): 该信道作用在的 qubit 的编号,其值应该在 :math:`[0, m)` 范围内, :math:`m` 为该参与方的量子比特数
"""
which_qubit = self.party[which_qubit]
super(LoccAnsatz, self).depolarizing(p, which_qubit)
def pauli_channel(self, p_x, p_y, p_z, which_qubit):
r"""添加泡利信道。
Args:
p_x (float): 泡利矩阵 X 的对应概率,其值应该在 :math:`[0, 1]` 区间内
p_y (float): 泡利矩阵 Y 的对应概率,其值应该在 :math:`[0, 1]` 区间内
p_z (float): 泡利矩阵 Z 的对应概率,其值应该在 :math:`[0, 1]` 区间内
which_qubit (int): 该信道作用在的 qubit 的编号,其值应该在 :math:`[0, m)` 范围内, :math:`m` 为该参与方的量子比特数
Note:
三个输入的概率加起来需要小于等于 1。
"""
which_qubit = self.party[which_qubit]
super(LoccAnsatz, self).pauli_channel(p_x, p_y, p_z, which_qubit)
def customized_channel(self, ops, which_qubit):
r"""添加自定义的量子信道。
Args:
ops (list): 表示信道的 Kraus 算符的列表
which_qubit (int): 该信道作用在的 qubit 的编号,其值应该在 :math:`[0, m)` 范围内, :math:`m` 为该参与方的量子比特数
"""
which_qubit = self.party[which_qubit]
super(LoccAnsatz, self).customized_channel(ops, which_qubit)
class LoccNet(paddle.nn.Layer):
r"""用于设计我们的 LOCC 下的 protocol,并进行验证或者训练。
......
......@@ -24,6 +24,7 @@ __all__ = [
"vec",
"vec_random",
"w_state",
"GHZ_state",
"density_op",
"density_op_random",
"completely_mixed_computational",
......@@ -57,7 +58,7 @@ def vec(n):
[[1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j]]
"""
assert n > 0, 'qubit number must be larger than 1'
state = concatenate(([[1.0]], np_zeros([1, 2**n - 1])), axis=1)
state = concatenate(([[1.0]], np_zeros([1, (2 ** n) - 1])), axis=1)
return state.astype("complex128")
......@@ -75,11 +76,11 @@ def vec_random(n, real_or_complex=2):
assert real_or_complex == 1 or real_or_complex == 2, 'real_or_complex must be 1 or 2'
# real
if real_or_complex == 1:
psi = np_random.randn(1, 2**n)
psi = np_random.randn(1, 2 ** n)
# complex
else:
psi = np_random.randn(1, 2**n) + 1j * np_random.randn(1, 2**n)
psi = psi/np.linalg.norm(psi)
psi = np_random.randn(1, 2 ** n) + 1j * np_random.randn(1, 2 ** n)
psi = psi / np.linalg.norm(psi)
return psi.astype("complex128")
......@@ -109,16 +110,46 @@ def w_state(n, coeff=None):
"""
assert n > 0, 'qubit number must be larger than 1'
c = coeff if coeff is not None else np.ones((1, 2**n))/np.sqrt(n)
assert c.shape[0] == 1 and c.shape[1] == 2**n, 'The dimension of coeff is not right'
c = coeff if coeff is not None else np.ones((1, 2 ** n)) / np.sqrt(n)
assert c.shape[0] == 1 and c.shape[1] == 2 ** n, 'The dimension of coeff is not right'
state = np_zeros((1, 2**n))
state = np_zeros((1, 2 ** n))
for i in range(n):
state[0][2**i] = c[0][n-i-1]
state[0][2 ** i] = c[0][n - i - 1]
return state.astype("complex128")
def GHZ_state(n):
r"""生成一个 GHZ-state 的 numpy 形式。
Args:
n (int): 量子比特数量
Returns:
numpy.ndarray: 一个形状为 ``(1, 2**n)`` 的 numpy 数组
代码示例:
.. code-block:: python
from paddle_quantum.state import GHZ_state
vector = GHZ_state(3)
print(vector)
::
[[0.70710678+0.j 0. +0.j 0. +0.j 0. +0.j
0. +0.j 0. +0.j 0. +0.j 0.70710678+0.j]]
"""
assert n > 2, 'qubit number must be larger than 2'
state = np_zeros((1, 2 ** n))
state[0][0] = 1 / np.sqrt(2)
state[0][-1] = 1 / np.sqrt(2)
return state.astype("complex128")
def density_op(n):
r"""生成密度矩阵 :math:`|00..0\rangle \langle00..0|` 的 numpy 形式。
......@@ -145,7 +176,7 @@ def density_op(n):
"""
assert n > 0, 'qubit number must be positive'
rho = np_zeros((2**n, 2**n))
rho = np_zeros((2 ** n, 2 ** n))
rho[0, 0] = 1
return rho.astype("complex128")
......@@ -163,17 +194,17 @@ def density_op_random(n, real_or_complex=2, rank=None):
numpy.ndarray: 一个形状为 ``(2**n, 2**n)`` 的 numpy 数组
"""
assert n > 0, 'qubit number must be positive'
rank = rank if rank is not None else 2**n
assert 0 < rank <= 2**n, 'rank is an invalid number'
rank = rank if rank is not None else 2 ** n
assert 0 < rank <= 2 ** n, 'rank is an invalid number'
if real_or_complex == 1:
psi = np_random.randn(2**n, rank)
psi = np_random.randn(2 ** n, rank)
else:
psi = np_random.randn(2**n, rank) + 1j*np_random.randn(2**n, rank)
psi = np_random.randn(2 ** n, rank) + 1j * np_random.randn(2 ** n, rank)
psi_dagger = psi.conj().T
rho = np_matmul(psi, psi_dagger)
rho = rho/np_trace(rho)
rho = rho / np_trace(rho)
return rho.astype('complex128')
......@@ -209,7 +240,7 @@ def completely_mixed_computational(n):
[0. +0.j 0. +0.j 0. +0.j 0.25+0.j]]
"""
assert n > 0, 'qubit number must be positive'
rho = np_eye(2**n)/2**n
rho = np_eye(2 ** n) / (2 ** n)
return rho.astype('complex128')
......@@ -225,7 +256,7 @@ def bell_state(n):
Args:
n (int): 量子比特数量必须为大于等于 2 的偶数
n (int): 量子比特数量必须为大于等于 2 的偶数
Returns:
numpy.ndarray: 一个形状为 ``(2**n, 2**n)`` 的 numpy 数组
......@@ -248,8 +279,8 @@ def bell_state(n):
assert n > 0, "Qubit number must be positive"
assert n % 2 == 0, "Qubit number must be even"
dim = 2**n
local_dim = 2**int(n/2)
dim = 2 ** n
local_dim = 2 ** int(n / 2)
coeff = 1 / local_dim
state = np.zeros((dim, dim))
for i in range(0, dim, local_dim + 1):
......@@ -309,7 +340,7 @@ def bell_diagonal_state(p1, p2, p3, p4):
psi_m_vec = np.array([[0, coeff, -coeff, 0]])
psi_m_mat = np.matmul(psi_m_vec.T, psi_m_vec)
state = p1*phi_p_mat + p2*psi_p_mat + p3*phi_m_mat + p4*psi_m_mat
state = p1 * phi_p_mat + p2 * psi_p_mat + p3 * phi_m_mat + p4 * psi_m_mat
return state.astype("complex128")
......@@ -324,7 +355,7 @@ def R_state(p):
p|\Psi^{+}\rangle\langle\Psi^{+}| + (1 - p)|11\rangle\langle11|
Args:
p (float): 控制生成 R-state 的参数属于 :math:`[0, 1]` 区间内
p (float): 控制生成 R-state 的参数属于 :math:`[0, 1]` 区间内
Returns:
numpy.ndarray: 一个形状为 ``(4, 4)`` 的 numpy 数组
......@@ -352,7 +383,7 @@ def R_state(p):
state_11 = np.zeros((4, 4))
state_11[3, 3] = 1
state = p*psi_p_mat + (1-p)*state_11
state = p * psi_p_mat + (1 - p) * state_11
return state.astype("complex128")
......@@ -367,7 +398,7 @@ def S_state(p):
p|\Phi^{+}\rangle\langle\Phi^{+}| + (1 - p)|00\rangle\langle00|
Args:
p (float): 控制生成 S-state 的参数属于 :math:`[0, 1]` 区间内
p (float): 控制生成 S-state 的参数属于 :math:`[0, 1]` 区间内
Returns:
numpy.ndarray: 一个形状为 ``(4, 4)`` 的 numpy 数组
......@@ -393,7 +424,7 @@ def S_state(p):
psi0 = np.zeros_like(phi_p)
psi0[0, 0] = 1
state = p * phi_p + (1-p) * psi0
state = p * phi_p + (1 - p) * psi0
return state.astype("complex128")
......@@ -407,8 +438,8 @@ def isotropic_state(n, p):
p(\frac{1}{\sqrt{D}} \sum_{j=0}^{D-1}|j\rangle_{A}|j\rangle_{B}) + (1 - p)\frac{I}{2^n}
Args:
n (int): 量子比特数量
p (float): 控制生成 isotropic state 的参数属于 :math:`[0, 1]` 区间内
n (int): 量子比特数量
p (float): 控制生成 isotropic state 的参数属于 :math:`[0, 1]` 区间内
Returns:
numpy.ndarray: 一个形状为 ``(2**n, 2**n)`` 的 numpy 数组
......@@ -430,7 +461,7 @@ def isotropic_state(n, p):
"""
assert 0 <= p <= 1, "Probability must be in [0, 1]"
dim = 2**n
state = p*bell_state(n) + (1-p)*np.eye(dim)/dim
dim = 2 ** n
state = p * bell_state(n) + (1 - p) * np.eye(dim) / dim
return state
......@@ -23,15 +23,17 @@ with open("README.md", "r", encoding="utf-8") as fh:
setuptools.setup(
name='paddle-quantum',
version='2.0.0',
version='2.0.1',
author='Institute for Quantum Computing, Baidu INC.',
author_email='quantum@baidu.com',
description='Paddle Quantum is a quantum machine learning (QML) toolkit developed based on Baidu PaddlePaddle.',
long_description=long_description,
long_description_content_type="text/markdown",
url='http://qml.baidu.com',
packages=['paddle_quantum', 'paddle_quantum.GIBBS', 'paddle_quantum.QAOA', 'paddle_quantum.SSVQE', 'paddle_quantum.VQE', 'paddle_quantum.VQSD',
'paddle_quantum.GIBBS.example', 'paddle_quantum.QAOA.example', 'paddle_quantum.SSVQE.example', 'paddle_quantum.VQE.example', 'paddle_quantum.VQSD.example'],
packages=['paddle_quantum', 'paddle_quantum.GIBBS', 'paddle_quantum.QAOA', 'paddle_quantum.SSVQE',
'paddle_quantum.VQE', 'paddle_quantum.VQSD', 'paddle_quantum.GIBBS.example',
'paddle_quantum.QAOA.example', 'paddle_quantum.SSVQE.example', 'paddle_quantum.VQE.example',
'paddle_quantum.VQSD.example'],
install_requires=['paddlepaddle>=2.0.1', 'scipy', 'networkx', 'matplotlib', 'interval', 'tqdm'],
python_requires='>=3.6, <4',
classifiers=[
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册