diff --git a/README.md b/README.md index 13c90d0..1943641 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ English | [简体中文](README_CN.md) - + @@ -90,22 +90,19 @@ pip install -e . ### Environment setup for Quantum Chemistry module -Our `qchem` module is based on `Psi4`, so before executing quantum chemistry, we have to install this Python package. +Currently, our `qchem` module uses `PySCF` as its backend to compute molecular integrals, so before executing quantum chemistry, we have to install this Python package. -> It is recommended that `Psi4` is installed in a Python 3.8 environment. +> It is recommended that `PySCF` is installed in a Python environment whose Python version >=3.6. -We highly recommend you to install `Psi4` via conda. **MacOS/Linux** user can use the command: +We highly recommend you to install `PySCF` via conda. **MacOS/Linux** user can use the command: ```bash -conda install psi4 -c psi4 +conda install -c pyscf pyscf ``` -For **Windows** user, the command is: +> NOTE: For **Windows** user, if your operating system is Windows10, you can install `PySCF` in Ubuntu subsystem provided by Windows 10's App Store. `PySCF` can't run directly in Windows, so we are working hard to develop more quantum chemistry backends. Our support for Windows will be improved in the coming release of Paddle Quantum. -```bash -conda install psi4 -c psi4 -c conda-forge -``` -**Note:** Please refer to [Psi4](https://psicode.org/installs/v14/) for more download options. +**Note:** Please refer to [PySCF](https://pyscf.org/install.html) for more download options. ### Run example diff --git a/README_CN.md b/README_CN.md index fe241cd..69c1ff0 100644 --- a/README_CN.md +++ b/README_CN.md @@ -34,7 +34,7 @@ - + @@ -91,23 +91,19 @@ pip install -e . ### 量子化学模块的环境设置 -我们的量子化学模块是基于 `Psi4` 进行开发的,所以在运行量子化学模块之前需要先行安装该 Python 包。 +当前我们的量子化学模块在后端使用 `PySCF` 来计算各类分子积分,所以在运行量子化学模块之前需要先行安装该 Python 包。 -> 推荐在 Python3.8 环境中安装。 +> 推荐在 Python>=3.6 环境中安装。 -在安装 `psi4` 时,我们建议您使用 conda。对于 **MacOS/Linux** 的用户,可以使用如下指令。 +在安装 `PySCF` 时,我们建议您使用 conda。对于 **MacOS/Linux** 的用户,可以使用如下指令。 ```bash -conda install psi4 -c psi4 +conda install -c pyscf pyscf ``` -对于 **Windows** 用户,请使用 +> 注:对于 **Windows** 用户,如果操作系统为 Windows10,可以在其应用商店提供的 Ubuntu 子系统中利用上述命令安装 `PySCF`。`PySCF` 并不支持直接在 Windows 下运行,我们正在努力开发更多的量子化学后端,在量桨的下一版本中将会有对 Windows 更好的支持。 -```bash -conda install psi4 -c psi4 -c conda-forge -``` - -**注意:** 更多的下载方法请参考 [Psi4](https://psicode.org/installs/v14/)。 +**注意:** 更多的下载方法请参考 [PySCF](https://pyscf.org/install.html)。 ### 运行 @@ -217,7 +213,7 @@ Paddle Quantum 使用 setuptools 的 develop 模式进行安装,相关代码 ## 交流与反馈 -- 我们非常欢迎您通过 [Github Issues](https://github.com/PaddlePaddle/Quantum/issues) 来提交问题、报告与建议。 +- 我们非常欢迎您通过 [GitHub Issues](https://github.com/PaddlePaddle/Quantum/issues) 来提交问题、报告与建议。 - 技术交流QQ群:1076223166 diff --git a/applications/README.md b/applications/README.md new file mode 100644 index 0000000..0787c79 --- /dev/null +++ b/applications/README.md @@ -0,0 +1,73 @@ +# Quantum Application Model Library + +- [Features](#features) +- [Installation](#installation) +- [How to Use](#how-to-use) +- [Application List](#application-list) + +**Q**uantum **A**pplication **M**odel Library (QAML) is a collection of out-of-box practical quantum algorithms, it is developed by [Institute for Quantum Computing at Baidu](https://quantum.baidu.com/), and aims to be a "supermarket" of quantum solutions for industry users. Currently, models in QAML have covered popular areas listed below: + +- Artificial Intelligence +- Medicine and Pharmaceuticals +- Material Simulation +- Financial Technology +- Manufacturing +- Data Analysis + +QAML is implemented on Paddle Quantum, a quantum machine learning platform, which can be found at https://qml.baidu.com and https://github.com/PaddlePaddle/Quantum. + +## Features + +- Industrialization: 10 models closely follow the 6 major industrial directions, covering hot topics such as artificial intelligence, chemical materials, manufacturing, finance, etc. +- End-to-end: Linking the whole process from application scenarios to quantum computing and solving the last mile of quantum applications. +- Out-of-box: No special configuration is required, the model is called directly by the Paddle Quantum, eliminating the tedious installation process. + +## Installation + +QAML depends on the `paddle-quantum` package. Users can install it by pip. + +```shell +pip install paddle-quantum +``` + +For those who are using old versions of Paddle Quantum, simply run `pip install --upgrade paddle-quantum` to install the latest package. + +QAML locates in Paddle Quantum's GitHub repository, you can download the zip file contains QAML source code by clicking [this link](https://github.com/PaddlePaddle/Quantum/archive/refs/heads/master.zip). After unzipping the package, you will find all the models in the `applications` folder in the extracted folder. + +You can also use git to get the QAML source code. + +```shell +git clone https://github.com/PaddlePaddle/Quantum.git +cd Quantum/applications +``` + +You can check your installation by going to the `handwritten_digits_classification` folder under `applications` and running + +```shell +python vsql_classification.py --example.toml +``` + +The installation is successful once the program terminates without errors. + +## How to Use + +In each application model, we provide Python scripts that can be run directly and the corresponding configuration files. The user can modify the configuration file to implement the corresponding requirements. + +Take handwritten digit classification as an example, it can be used by executing `python vsql_classification.py --example.toml` in the `handwritten_digits_classification` folder. We provide tutorials for each application model, which allows users to quickly understand and use it. + +## Application List + +*Continue update* + +Below we list instructions for all applications available in QAML, newly developed applications will be continuously integrated into QAML. + +1. [Handwritten digits classification](./handwritten_digits_classification/introduction_en.ipynb) +2. [Molecular ground state energy & dipole moment calculation](./lithium_ion_battery/introduction_en.ipynb) +3. [Text classification](./text_classification/introduction_en.ipynb) +4. [Protein folding](./protein_folding/introduction_en.ipynb) +5. [Medical image classification](./medical_image_classification/introduction_en.ipynb) +6. [Quality detection](./quality_detection/introduction_en.ipynb) +7. [Option pricing](./option_pricing/introduction_en.ipynb) +8. [Quantum portfolio optimization](./portfolio_optimization/introduction_en.ipynb) +9. [Regression](./regression/introduction_en.ipynb) +10. [Quantum linear equation solver](./linear_solver/introduction_en.ipynb) diff --git a/applications/README_CN.md b/applications/README_CN.md new file mode 100644 index 0000000..acb4ecc --- /dev/null +++ b/applications/README_CN.md @@ -0,0 +1,73 @@ +# 量子应用模型库 + +- [特色](#特色) +- [安装](#安装) +- [如何使用](#如何使用) +- [应用列表](#应用列表) + +量子应用模型库(**Q**uantum **A**pplication **M**odel **L**ibrary, QAML)是一个开箱即用的实用量子应用模型集合,它由[百度量子计算研究所](https://quantum.baidu.com/)研发,旨在成为企业用户的量子解决方案“超市”。目前,QAML 中的模型已经覆盖了以下领域: + +- 人工智能 +- 医学制药 +- 材料模拟 +- 金融科技 +- 汽车制造 +- 数据分析 + +QAML 基于量桨这一量子机器学习平台实现,关于量桨的内容可以参考 https://qml.baidu.com 和 https://github.com/PaddlePaddle/Quantum 。 + +## 特色 + +- 产业化:10 大应用模型紧贴 6 大产业方向,涵盖人工智能、化工材料、汽车制造、金融套利等热点话题。 +- 端到端:打通应用场景到量子算法的全流程,解决量子应用的最后一公里问题。 +- 开箱即用:无需特殊配置,通过量桨直接完成模型调用,省去繁琐安装环节。 + +## 安装 + +QAML 依赖于量桨( `paddle-quantum` )软件包。用户可以通过 pip 来安装: + +```shell +pip install paddle-quantum +``` + +对于那些使用旧版量桨的用户,只需运行 `pip install --upgrade paddle-quantum` 即可安装最新版量桨。 + +QAML 的内容在 Paddle Quantum 的 GitHub 仓库中,用户可以通过点击[此链接](https://github.com/PaddlePaddle/Quantum/archive/refs/heads/master.zip)下载包含 QAML 源代码的压缩包。QAML 的所有模型都在解压后的文件夹中的 `applications` 文件夹里。 + +用户也可以使用 git 来获取 QAML 的源码文件。 + +```shell +git clone https://github.com/PaddlePaddle/Quantum.git +cd Quantum/applications +``` + +用户可以进入到 `applications` 下的 `handwritten_digits_classification` 文件夹中,然后运行以下代码来检查安装是否成功。 + +```shell +python vsql_classification.py --example.toml +``` + +如果上面的程序没有报错、成功运行的话,则说明安装成功了。 + +## 如何使用 + +在每个应用模型中,我们都提供了可以直接运行的Python脚本和相应的配置文件。用户可以修改配置文件来实现对应的要求。 + +以手写数字识别为例,用户可以通过执行 `handwritten_digits_classification` 中的 `python vsql_classification.py --example.toml` 命令来快速使用。我们为每个应用模型提供了教程,方便用户快速理解和上手使用。 + +## 应用列表 + +*持续更新中* + +我们列出了目前 QAML 的所有应用案例的教程,新开发的应用案例也会持续添加进来。 + +1. [手写数字识别](./handwritten_digits_classification/introduction_cn.ipynb) +2. [分子基态能量 & 偶极矩计算](./lithium_ion_battery/introduction_cn.ipynb) +3. [中文文本分类](./text_classification/introduction_cn.ipynb) +4. [蛋白质折叠](./protein_folding/introduction_cn.ipynb) +5. [医学影像判别](./medical_image_classification/introduction_cn.ipynb) +6. [材料表面质量检测](./quality_detection/introduction_cn.ipynb) +7. [量子期权定价](./option_pricing/introduction_cn.ipynb) +8. [投资组合优化](./portfolio_optimization/introduction_cn.ipynb) +9. [回归分析](./regression/introduction_cn.ipynb) +10. [线性方程组求解](./linear_solver/introduction_cn.ipynb) diff --git a/applications/handwritten_digits_classification/data_0.png b/applications/handwritten_digits_classification/data_0.png new file mode 100644 index 0000000..3c0ac0d Binary files /dev/null and b/applications/handwritten_digits_classification/data_0.png differ diff --git a/applications/handwritten_digits_classification/example.toml b/applications/handwritten_digits_classification/example.toml new file mode 100644 index 0000000..b6e1e9d --- /dev/null +++ b/applications/handwritten_digits_classification/example.toml @@ -0,0 +1,9 @@ + +task = 'test' +image_path = 'data_0.png' +is_dir = false +model_path = 'vsql.pdparams' +num_qubits = 10 +num_shadow = 2 +depth = 1 +classes = [0, 1] diff --git a/applications/handwritten_digits_classification/introduction_cn.ipynb b/applications/handwritten_digits_classification/introduction_cn.ipynb new file mode 100644 index 0000000..70b8e4e --- /dev/null +++ b/applications/handwritten_digits_classification/introduction_cn.ipynb @@ -0,0 +1,218 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 手写数字识别简介\n", + "\n", + "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*\n", + "\n", + "计算机视觉(Computer Vision, CV)是指让计算机能够从图像、视频或其它视觉输入中获取有意义的信息。它是人工智能领域中的一个非常基础且重要的组成部分。在 CV 中,手写数字识别(handwritten digit classification)是一个较为基础的任务。它在 MNIST 数据集\\[1\\]上进行训练和测试,用来验证模型是否拥有 CV 方面的基础能力。\n", + "\n", + "MNIST 数据集中包含如下图所示的手写数字。MNIST 共包含 0-9 这 10 个类别,每个数字为 28\\*28 像素的灰度图片。其中,训练集有 60000 张图片,测试集有 10000 张图片。假设我们设计了一个可以用来进行图像分类的模型,那么我们可以在 MNIST 数据集上测试该模型的分类能力。\n", + "\n", + "![mnist-example](mnist_example.png)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 使用 VSQL 模型实现 MNIST 分类\n", + "\n", + "### 数据编码\n", + "\n", + "在手写数字识别问题中,输入是一张手写数字图片,输出是该图片对应的类别(即数字 0-9)。而由于量子计算机处理的输入是量子态,因此,我们需要将图片编码为量子态。在这里,我们首先使用一个二维矩阵表示一张图片。然后将该矩阵展开为一维向量,并通过补充 0 将向量长度补充到 2 的整数次幂。再对向量进行归一化,即可得到一个量子计算机可以处理的量子态。\n", + "\n", + "### VSQL 模型简介\n", + "\n", + "变分影子量子学习(variational shadow quantum learning, VSQL)是一个在监督学习框架下的量子–经典混合算法。它使用了参数化量子电路(parameterized quantum circuit, PQC)和经典影子(classical shadow),和通常使用的变分量子算法(variational quantum algorithm, VQA)不同的是,VSQL 只从子空间获取局部特征,而不是从量子态形成的整个希尔伯特空间获取特征。\n", + "\n", + "VSQL 的模型原理图如下:\n", + "\n", + "![vsql-model](vsql_model.png)\n", + "\n", + "VSQL 处理的输入是一个量子态。对于输入的量子态,迭代地作用一个局部参数化量子电路并进行测量,得到局部的影子特征。然后将得到的所有影子特征使用经典神经网络进行计算并得到预测标签。\n", + "\n", + "### 工作流\n", + "\n", + "根据以上原理,我们只需要使用 MNIST 数据集对 VSQL 模型进行训练。得到收敛后的模型。使用该模型即可进行手写数字的分类。模型的训练流程如下图:\n", + "\n", + "\n", + "![vsql-pipeline](vsql_pipeline_cn.png)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 如何使用\n", + "\n", + "### 使用模型进行预测\n", + "\n", + "这里,我们已经给出了一个训练好的模型,可以直接用于 0 和 1 的图片的预测。只需要在 `example.toml` 这个配置文件中进行对应的配置,然后输入命令 `python vsql_classification.py --config example.toml` 即可使用训练好的 VSQL 模型对输入的图片进行测试。\n", + "\n", + "### 在线演示\n", + "\n", + "这里,我们给出一个在线演示的版本,可以在线进行测试。首先定义配置文件的内容:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "test_toml = r\"\"\"\n", + "# 模型的整体配置文件。\n", + "# 输入当前的任务,可以是 'train' 或者 'test',分别代表训练和预测。这里我们使用 test,表示我们要进行预测。\n", + "task = 'test'\n", + "# 要预测的图片的文件路径。\n", + "image_path = 'data_0.png'\n", + "# 上面的图片路径是否是文件夹。对于文件夹路径,我们会对文件夹里面的所有图片文件进行预测。这种方式可以一次测试多个图片。\n", + "is_dir = false\n", + "# 训练好的模型参数文件的文件路径。\n", + "model_path = 'vsql.pdparams'\n", + "# 量子电路所包含的量子比特的数量。\n", + "num_qubits = 10\n", + "# 影子电路所包含的量子比特的数量。\n", + "num_shadow = 2\n", + "# 电路深度。\n", + "depth = 1\n", + "# 我们要预测的类别。这里我们对 0 和 1 进行分类。\n", + "classes = [0, 1]\n", + "\"\"\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "接下来是预测部分的代码:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "对于输入的图片,模型有 89.22% 的信心认为它是 0,和 10.78% 的信心认为它是 1。\n" + ] + } + ], + "source": [ + "import os\n", + "import warnings\n", + "\n", + "warnings.filterwarnings('ignore')\n", + "os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python'\n", + "\n", + "import toml\n", + "from paddle_quantum.qml.vsql import train, inference\n", + "\n", + "config = toml.loads(test_toml)\n", + "task = config.pop('task')\n", + "if task == 'train':\n", + " train(**config)\n", + "elif task == 'test':\n", + " prediction, prob = inference(**config)\n", + " if config['is_dir']:\n", + " print(f\"对输入图片的预测结果分别是 {str(prediction)[1:-1]}。\")\n", + " else:\n", + " prob = prob[0]\n", + " msg = '对于输入的图片,模型有'\n", + " for idx, item in enumerate(prob):\n", + " if idx == len(prob) - 1:\n", + " msg += '和'\n", + " label = config['classes'][idx]\n", + " msg += f' {item:3.2%} 的信心认为它是 {label:d}'\n", + " msg += '。' if idx == len(prob) - 1 else ','\n", + " print(msg)\n", + "else:\n", + " raise ValueError(\"未知的任务,它可以是'train'或'test'。\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "在这里,我们只需要修改要配置文件中的图片路径,再运行整个代码,就可以快速对其它图片进行测试。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 注意事项\n", + "\n", + "我们提供的模型为二分类模型,仅可以用来分辨手写数字 0 和 1。对于其它分类任务,需要重新进行训练。\n", + "\n", + "### 数据集结构\n", + "\n", + "如果想要使用自定义数据集进行训练,只需要按照规则来准备数据集即可。在数据集文件夹中准备 `train.txt` 和 `test.txt`,如果需要验证集的话还有 `dev.txt`。每个文件里使用一行代表一条数据。每行内容包含图片的文件路径和标签,使用制表符隔开。\n", + "\n", + "### 配置文件介绍\n", + "\n", + "在 `test.toml` 里有测试所需要的完整的配置文件内容参考。在 `train.toml` 里有训练所需要的完整的配置文件内容参考。使用配置文件的方式即可快速进行模型的训练和预测。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. 引用信息\n", + "\n", + "```tex\n", + "@inproceedings{li2021vsql,\n", + " title={VSQL: Variational shadow quantum learning for classification},\n", + " author={Li, Guangxi and Song, Zhixin and Wang, Xin},\n", + " booktitle={Proceedings of the AAAI Conference on Artificial Intelligence},\n", + " volume={35},\n", + " number={9},\n", + " pages={8357--8365},\n", + " year={2021}\n", + "}\n", + "```\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py37", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.15 (default, Nov 10 2022, 12:46:26) \n[Clang 14.0.6 ]" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "49b49097121cb1ab3a8a640b71467d7eda4aacc01fc9ff84d52fcb3bd4007bf1" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/applications/handwritten_digits_classification/introduction_en.ipynb b/applications/handwritten_digits_classification/introduction_en.ipynb new file mode 100644 index 0000000..b37d5ae --- /dev/null +++ b/applications/handwritten_digits_classification/introduction_en.ipynb @@ -0,0 +1,222 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Introduction to Handwritten Digit Classification\n", + "\n", + "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*\n", + "\n", + "Computer Vision (CV) refers to enabling computers to obtain meaningful information from images, videos, or other visual inputs. It is a fundamental and important field in artificial intelligence. In CV, handwritten digit classification is a relatively basic task. It is trained and tested on the MNIST dataset \\[1\\] to verify whether the model has the basic ability of CV.\n", + "\n", + "The MNIST dataset contains handwritten digits as shown in the figure below. MNIST contains a total of 10 categories from 0-9, and each digit is a grayscale image of 28\\*28 pixels. There are 60,000 images in the training set and 10,000 images in the test set. Suppose we design a model that can be used for image classification, then we can test the classification ability of the model on the MNIST dataset.\n", + "\n", + "![mnist-example](mnist_example.png)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## MNIST Classification Using VSQL Model\n", + "\n", + "### Data Encoding\n", + "\n", + "In the handwritten digit classification problem, the input is a picture of a handwritten digit and the output is the category corresponding to the picture (i.e., the digits 0-9). And since quantum computers deal with inputs that are quantum states, we need to encode the picture into a quantum state. Here, we first represent a picture using a two-dimensional matrix. This matrix is then expanded into a 1D vector, and the length of the vector is padded to an integer power of 2 by padding with zeros. The vector is then normalized to obtain a quantum state that can be processed by a quantum computer.\n", + "\n", + "\n", + "### Introduction to the VSQL Model\n", + "\n", + "Variational shadow quantum learning (VSQL) is a hybrid quantum-classical algorithm under the framework of supervised learning. It uses the parameterized quantum circuit (PQC) and the classical shadow. Unlike the common variational quantum algorithm (VQA), VSQL only obtains local features from the subspace rather than from the whole Hilbert space where the quantum states are formed.\n", + "\n", + "The schematic diagram of the VSQL model is as follows.\n", + "\n", + "![vsql-model](vsql_model.png)\n", + "\n", + "The input to the VSQL process is a quantum state. For the input quantum state, a local parameterized quantum circuit is iteratively applied and measured to obtain local shadow features. Then all the obtained shadow features are calculated using the classical neural network and the predicted labels are obtained.\n", + "\n", + "### Workflow\n", + "\n", + "Based on the above principles, we only need to train the VSQL model using the MNIST dataset to obtain a converged model. The model can be used to classify handwritten digits. The training process of the model is as follows.\n", + "\n", + "![vsql-pipeline](vsql_pipeline_en.png)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## How to Use\n", + "\n", + "### Predict Using the Model\n", + "\n", + "Here, we have given a trained model that can be used directly for the prediction of 0 and 1 images. Just make the corresponding configuration in the `example.toml` configuration file and enter the command `python vsql_classification.py --config example.toml` to test the input images with the trained VSQL model.\n", + "\n", + "### Online Demo\n", + "\n", + "Here, we give a version of the online demo that can be tested online. First define the contents of the configuration file." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "test_toml = r\"\"\"\n", + "# The overall configuration file of the model.\n", + "# Enter the current task, which can be 'train' or 'test', representing training and prediction respectively. Here we use test, indicating that we want to make a prediction.\n", + "task = 'test'\n", + "# The file path of the image to be predicted.\n", + "image_path = 'data_0.png'\n", + "# Whether the image path above is a folder or not. For folder paths, we will predict all image files inside the folder. This way you can test multiple images at once.\n", + "is_dir = false\n", + "# The file path of the trained model parameter file.\n", + "model_path = 'vsql.pdparams'\n", + "# The number of qubits that the quantum circuit contains.\n", + "num_qubits = 10\n", + "# The number of qubits that the shadow circuit contains.\n", + "num_shadow = 2\n", + "# Circuit depth.\n", + "depth = 1\n", + "# The class to be predicted by the model. Here, 0 and 1 are classified.\n", + "classes = [0, 1]\n", + "\"\"\"\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next is the code for the prediction section." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "For the input image, the model has 89.22% confidence that it is 0, and 10.78% confidence that it is 1.\n" + ] + } + ], + "source": [ + "import os\n", + "import warnings\n", + "\n", + "warnings.filterwarnings('ignore')\n", + "os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python'\n", + "\n", + "import toml\n", + "from paddle_quantum.qml.vsql import train, inference\n", + "\n", + "config = toml.loads(test_toml)\n", + "task = config.pop('task')\n", + "if task == 'train':\n", + " train(**config)\n", + "elif task == 'test':\n", + " prediction, prob = inference(**config)\n", + " if config['is_dir']:\n", + " print(f\"The prediction results of the input pictures are {str(prediction)[1:-1]} respectively.\")\n", + " else:\n", + " prob = prob[0]\n", + " msg = 'For the input image, the model has'\n", + " for idx, item in enumerate(prob):\n", + " if idx == len(prob) - 1:\n", + " msg += 'and'\n", + " label = config['classes'][idx]\n", + " msg += f' {item:3.2%} confidence that it is {label:d}'\n", + " msg += '.' if idx == len(prob) - 1 else ', '\n", + " print(msg)\n", + "else:\n", + " raise ValueError(\"Unknown task, it can be train or test.\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here, we only need to modify the image path in the configuration file, and then run the entire code to quickly test other images." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Note\n", + "\n", + "The model we provide is a binary classification model that can only be used to distinguish handwritten digits 0 and 1. For other classification tasks, it needs to be retrained.\n", + "\n", + "### Dataset Structure\n", + "\n", + "If you want to use a custom dataset for training, you just need to prepare the dataset according to the rules. Prepare `train.txt` and `test.txt` in the dataset folder, and `dev.txt` if a validation set is needed. Use one line in each file to represent one piece of data. Each line contains the file path and label of the image, separated by tabs.\n", + "\n", + "### Introduction to the Configuration File\n", + "\n", + "In `test.toml`, there is a complete reference to the configuration files needed for testing. In `train.toml`, there is a complete reference to the configuration files needed for training. You can use the configuration file to quickly use the model to train and test." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Citation\n", + "\n", + "```tex\n", + "@inproceedings{li2021vsql,\n", + " title={VSQL: Variational shadow quantum learning for classification},\n", + " author={Li, Guangxi and Song, Zhixin and Wang, Xin},\n", + " booktitle={Proceedings of the AAAI Conference on Artificial Intelligence},\n", + " volume={35},\n", + " number={9},\n", + " pages={8357--8365},\n", + " year={2021}\n", + "}\n", + "```\n", + "\n", + "## Reference\n", + "\n", + "\\[1\\] \"THE MNIST DATABASE of handwritten digits\". Yann LeCun, Courant Institute, NYU Corinna Cortes, Google Labs, New York Christopher J.C. Burges, Microsoft Research, Redmond." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py37", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.15 (default, Nov 10 2022, 12:46:26) \n[Clang 14.0.6 ]" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "49b49097121cb1ab3a8a640b71467d7eda4aacc01fc9ff84d52fcb3bd4007bf1" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/applications/handwritten_digits_classification/mnist_example.png b/applications/handwritten_digits_classification/mnist_example.png new file mode 100644 index 0000000..7829592 Binary files /dev/null and b/applications/handwritten_digits_classification/mnist_example.png differ diff --git a/applications/handwritten_digits_classification/test.toml b/applications/handwritten_digits_classification/test.toml new file mode 100644 index 0000000..074ef84 --- /dev/null +++ b/applications/handwritten_digits_classification/test.toml @@ -0,0 +1,19 @@ +# The full config for testing the VSQL model. +# The task of this config. Available values: 'train' | 'test'. +task = 'test' +# The path of the input images. +image_path = 'data_0.png' +# Whether the image_path is a directory. Available values: true | false. +# The value true means the path is a directory and all the images there will be predicted. +is_dir = false +# The path of the trained model, which will be loaded. +model_path = 'vsql.pdparams' +# The number of qubits which the quantum circuit contains. +num_qubits = 10 +# The number of qubits which the shadow circuit contains. +num_shadow = 2 +# The depth of the quantum circuit. Default to 1. +depth = 1 +# The classes of handwrite digits to be predicted. +# It will use all labels if the value is not provided. +classes = [0, 1] diff --git a/applications/handwritten_digits_classification/train.toml b/applications/handwritten_digits_classification/train.toml new file mode 100644 index 0000000..6a1b242 --- /dev/null +++ b/applications/handwritten_digits_classification/train.toml @@ -0,0 +1,42 @@ +# The full config for training the VSQL model. +# The task of this config. Available values: 'train' | 'test'. +task = 'train' +# The name of the model, which is used to save the model. +model_name = 'vsql-model' +# The path to save the model. Both relative and absolute paths are allowed. +# It saves the model to the current path by default. +# saved_path = './' +# The number of qubits which the quantum circuit contains. +num_qubits = 10 +# The number of qubits which the shadow circuit contains. +num_shadow = 2 +# The depth of the quantum circuit, default to 1. +# depth = 1 +# The size of the batch samplers. +batch_size = 16 +# The number of epochs to train the model. +num_epochs = 10 +# The learning rate used to update the parameters, default to 0.01. +# learning_rate = 0.01 +# The path of the dataset. It defaults to MNIST, which is a built-in dataset. +dataset = 'MNIST' +# The classes of handwrite digits to be predicted. +# It will use all labels if the value is not provided. +classes = [0, 1] +# Whether use the validation. +# It is true means the dataset contains training, validation and test datasets. +# It is false means the dataset only contains training datasets and test datasets. +using_validation = false +# The number of the data in the training dataset. +# The value defaults to 0 which means using all data. +# num_train = 0 +# The number of the data in the validation dataset. +# The value defaults to 0 which means using all data. +# num_dev = 0 +# The number of the data in the test dataset. +# The value defaults to 0 which means using all data. +# num_test = 0 +# Number of epochs with no improvement after which training will be stopped. +# early_stopping = 1000 +# The number of subprocess to load data, 0 for no subprocess used and loading data in main process, defaults to 0. +# num_workers = 0 diff --git a/applications/handwritten_digits_classification/vsql.pdparams b/applications/handwritten_digits_classification/vsql.pdparams new file mode 100644 index 0000000..59840f7 Binary files /dev/null and b/applications/handwritten_digits_classification/vsql.pdparams differ diff --git a/applications/handwritten_digits_classification/vsql_classification.py b/applications/handwritten_digits_classification/vsql_classification.py new file mode 100644 index 0000000..f692da7 --- /dev/null +++ b/applications/handwritten_digits_classification/vsql_classification.py @@ -0,0 +1,50 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import warnings + +warnings.filterwarnings('ignore') +os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python' + +import argparse +import toml +from paddle_quantum.qml.vsql import train, inference + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Classify the handwritten digits by the VSQL model.") + parser.add_argument("--config", type=str, help="Input the config file with toml format.") + args = parser.parse_args() + config = toml.load(args.config) + task = config.pop('task') + if task == 'train': + train(**config) + elif task == 'test': + prediction, prob = inference(**config) + if config['is_dir']: + print(f"The prediction results of the input pictures are {str(prediction)[1:-1]} respectively.") + else: + prob = prob[0] + msg = 'For the input image, the model has' + for idx, item in enumerate(prob): + if idx == len(prob) - 1: + msg += 'and' + label = config['classes'][idx] + msg += f' {item:3.2%} confidence that it is {label:d}' + msg += '.' if idx == len(prob) - 1 else ', ' + print(msg) + else: + raise ValueError("Unknown task, it can be train or test.") diff --git a/applications/handwritten_digits_classification/vsql_model.png b/applications/handwritten_digits_classification/vsql_model.png new file mode 100644 index 0000000..586462f Binary files /dev/null and b/applications/handwritten_digits_classification/vsql_model.png differ diff --git a/applications/handwritten_digits_classification/vsql_pipeline_cn.png b/applications/handwritten_digits_classification/vsql_pipeline_cn.png new file mode 100644 index 0000000..e976048 Binary files /dev/null and b/applications/handwritten_digits_classification/vsql_pipeline_cn.png differ diff --git a/applications/handwritten_digits_classification/vsql_pipeline_en.png b/applications/handwritten_digits_classification/vsql_pipeline_en.png new file mode 100644 index 0000000..60502a8 Binary files /dev/null and b/applications/handwritten_digits_classification/vsql_pipeline_en.png differ diff --git a/applications/linear_solver/A.npy b/applications/linear_solver/A.npy new file mode 100644 index 0000000..16d13c1 Binary files /dev/null and b/applications/linear_solver/A.npy differ diff --git a/applications/linear_solver/answer.npy b/applications/linear_solver/answer.npy new file mode 100644 index 0000000..2472d64 Binary files /dev/null and b/applications/linear_solver/answer.npy differ diff --git a/applications/linear_solver/b.npy b/applications/linear_solver/b.npy new file mode 100644 index 0000000..749c2d2 Binary files /dev/null and b/applications/linear_solver/b.npy differ diff --git a/applications/linear_solver/config.toml b/applications/linear_solver/config.toml new file mode 100644 index 0000000..3f6f133 --- /dev/null +++ b/applications/linear_solver/config.toml @@ -0,0 +1,18 @@ +# The path of the input matrix A. It should be a .npy file. +A_dir = './A.npy' + +# The path of the input vector b. It should be a .npy file. +b_dir = './b.npy' + +# The depth of the quantum ansatz circuit. +depth = 4 + +# Number optimization cycls. Use 100 as a starting point and adjust depend on how low the loss function reaches. +# Ideally, you want to reach 0.0001 +iterations = 100 + +# The learning rate of the optimizer. +LR = 0.1 + +# Threshold for loss value to end optimization early, default is 0. +gamma = 0 \ No newline at end of file diff --git a/applications/linear_solver/example.toml b/applications/linear_solver/example.toml new file mode 100644 index 0000000..e4055cb --- /dev/null +++ b/applications/linear_solver/example.toml @@ -0,0 +1,6 @@ +A_dir = './A.npy' +b_dir = './b.npy' +depth = 4 +iterations = 100 +LR = 0.1 +gamma = 0 \ No newline at end of file diff --git a/applications/linear_solver/introduction_cn.ipynb b/applications/linear_solver/introduction_cn.ipynb new file mode 100644 index 0000000..22c4473 --- /dev/null +++ b/applications/linear_solver/introduction_cn.ipynb @@ -0,0 +1,258 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 变分量子线性求解器\n", + "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*\n", + "## 背景介绍\n", + "\n", + "线性方程组是数学中一个基本但非常有用的工具。 一个例子是,在经济学中,可以使用线性方程对经济进行建模。 此外,它还为非线性的大型系统提供了简单的估计。 因此求解线性方程组是一项重要的任务。\n", + "\n", + "变分量子线性求解器(Variational quantum linear solver, VQLS)是一种求解线性方程组的变分量子算法,采用了经典-量子混合的方案,可以在近期的含噪中等规模量子计算机上运行。具体来说,对于一个矩阵 $A$ 和一个向量 $\\boldsymbol{b}$,我们的目标是找到一个向量 $\\boldsymbol{x}$ 使得 $A \\boldsymbol{x} = \\boldsymbol{b}$. 使用 VQLS 算法可以得到一个与 $\\boldsymbol{x}$ 成比例的量子态,即一个归一化的向量 $|x\\rangle = \\frac{\\boldsymbol{x}}{\\lVert \\boldsymbol{x} \\rVert_2}$。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 模型原理\n", + "\n", + "量子场景的线性方程求解问题和通常的设定略有不同,因为量子计算需要将酉算子应用到量子态上。对于输入的矩阵 $A$,我们需要将其分解成酉算子的线性组合 $A = \\sum_n c_n A_n$,其中每个 $A_n$ 都是酉算子,可以在量子线路上运行。对于输入的向量 $\\boldsymbol{b}$,我们需要假设它是一个能够被某个酉算子 $U$ 制备的量子态 $|b\\rangle$,即 $U|0\\rangle = |b\\rangle$。我们可以用下面这张图来概括 VQLS 算法的整体架构:\n", + "\n", + "![VQLS](vqls.png)\n", + "\n", + "可以看到,VQLS 算法是一种混合优化算法,可以分为经典和量子两部分,需要在量子计算机上准备参数化量子电路 $V(\\alpha)$ 并计算损失函数 $C(\\alpha)$,然后在经典计算机上对参数 $\\alpha$ 进行优化从而最小化损失函数,直到损失低于某个阈值,最后输出目标量子态 $|x\\rangle$。其中参数化电路 $V(\\alpha)$ 可以生成一个量子态 $|\\psi(\\alpha)\\rangle$,电路 $F(A)$ 可以计算 $A|\\psi(\\alpha)\\rangle$ 与 $|b\\rangle$ 的近似程度,即损失函数 $C(\\alpha)$。当量子态 $A|\\psi(\\alpha)\\rangle$ 与 $|b\\rangle$ 足够接近时,这就意味着量子态 $|\\psi(\\alpha)\\rangle$ 与目标态 $|x\\rangle$ 足够接近,我们可以输出量子态 $|\\psi(\\alpha)\\rangle$ 作为目标态 $|x\\rangle$ 的近似。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 量桨实现\n", + "\n", + "我们使用量桨中的 `Circuit` 类结合飞桨优化器来实现 VQLS 算法,其中量子部分中参数化量子电路 $V(\\alpha)$ 为 `Circuit` 中内置的 `complex_entangled_layer` 模板,损失函数计算电路 $F(A)$ 由 Hadamard Test 或 Hadamard-Overlap Test 组成,主要使用了量桨中的 `oracle` 量子门来实现控制 $A_n$ 门,在经典优化部分中我们使用 Adam 优化器来最小化损失函数。\n", + "\n", + "用户可以使用 toml 文件指定算法的输入,矩阵 $A$ 和向量 $\\boldsymbol{b}$,分别以 `.npy` 文件形式存储。用户可以使用以下代码,通过改变$n$的值随机生成一个 $n\\times n$ 的矩阵 $A$ 以及向量 $\\boldsymbol{b}$。" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "这是一个随机生成的A:\n", + "[[4.1702199e+00+7.203245j 1.1437482e-03+3.0233257j\n", + " 1.4675589e+00+0.9233859j 1.8626021e+00+3.4556072j\n", + " 3.9676747e+00+5.3881674j ]\n", + " [2.0445225e+00+8.781175j 2.7387592e-01+6.704675j\n", + " 4.1730480e+00+5.5868983j 1.4038694e+00+1.9810148j\n", + " 8.0074453e+00+9.682616j ]\n", + " [8.7638912e+00+8.946067j 8.5044211e-01+0.39054784j\n", + " 1.6983042e+00+8.781425j 9.8346835e-01+4.2110763j\n", + " 9.5788956e+00+5.3316526j ]\n", + " [6.8650093e+00+8.346256j 1.8288277e-01+7.5014434j\n", + " 9.8886108e+00+7.4816566j 2.8044400e+00+7.892793j\n", + " 1.0322601e+00+4.4789352j ]\n", + " [2.8777535e+00+1.3002857j 1.9366957e-01+6.7883554j\n", + " 2.1162813e+00+2.6554666j 4.9157314e+00+0.5336254j\n", + " 5.7411761e+00+1.4672858j ]]\n", + "这是一个随机生成的b:\n", + "[4.191945 +6.852195j 3.1342418+6.9232264j 6.9187713+3.1551564j\n", + " 9.085955 +2.9361415j 5.8930554+6.9975834j]\n" + ] + } + ], + "source": [ + "n = 5\n", + "\n", + "import numpy as np\n", + "\n", + "\n", + "np.random.seed(1)\n", + "A = np.zeros([n, n], dtype=\"complex64\")\n", + "b = np.zeros(n, dtype=\"complex64\")\n", + "for i in range(n):\n", + " for j in range(n):\n", + " x = np.random.rand() * 10\n", + " y = np.random.rand() * 10\n", + " A[i][j] = complex(x, y)\n", + " x = np.random.rand() * 10\n", + " y = np.random.rand() * 10\n", + " b[i] = complex(x, y)\n", + "np.save(\"./A.npy\", A)\n", + "np.save(\"./b.npy\", b)\n", + "print(\"这是一个随机生成的A:\")\n", + "print(A)\n", + "print(\"这是一个随机生成的b:\")\n", + "print(b)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "用户可以在 toml 文件中指定 VQLS 算法的参数 `depth`,`iterations`,`LR` 以及 `gamma`,分别对应参数化量子电路 $V(\\alpha)$ 的层数,优化器的迭代次数,优化器的学习率,和损失函数的阈值。在命令行输入 `python vqls.py --config config.toml` 即可完成线性方程组求解。这里我们给出一个在线演示的例子,首先定义配置文件的内容如下,用户可以自行更改 `test_toml` 中的参数:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "test_toml = r\"\"\"\n", + "# 存储矩阵A的.npy文件的路径。\n", + "A_dir = './A.npy'\n", + "# 存储向量b的.npy文件的路径。\n", + "b_dir = './b.npy'\n", + "# 参数化量子电路的层数。\n", + "depth = 4\n", + "# 优化器迭代次数。\n", + "iterations = 200\n", + "# 优化器的学习率。\n", + "LR = 0.1\n", + "# 损失函数的阈值。默认为0。\n", + "gamma = 0\n", + "\"\"\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "运行 VQLS 算法如下:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "c:\\Users\\yuzhan01\\Miniconda3\\envs\\pq_model\\lib\\site-packages\\paddle\\tensor\\creation.py:125: DeprecationWarning: `np.object` is a deprecated alias for the builtin `object`. To silence this warning, use `object` by itself. Doing this will not modify any behavior and is safe. \n", + "Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations\n", + " if data.dtype == np.object:\n", + " 88%|████████▊ | 176/200 [02:04<00:16, 1.42it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Threshold value gamma reached, ending optimization\n", + "这是求解Ax=b的x: [ 1.3475237 -0.7860472j 0.22970617-0.88826376j -0.35111237-0.31225887j\n", + " 0.07606918+1.2138402j -0.729564 +0.48393282j]\n", + "实际b的值: [4.191945 +6.852195j 3.1342418+6.9232264j 6.9187713+3.1551564j\n", + " 9.085955 +2.9361415j 5.8930554+6.9975834j]\n", + "算法得到的Ax的值: [4.185339 +6.8523855j 3.1297188+6.923625j 6.924285 +3.1467872j\n", + " 9.092921 +2.932943j 5.8879805+6.999589j ]\n", + "相对误差: 0.0008446976\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "import argparse\n", + "import os\n", + "import warnings\n", + "\n", + "warnings.filterwarnings(\"ignore\")\n", + "os.environ[\"PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION\"] = \"python\"\n", + "\n", + "import toml\n", + "import numpy as np\n", + "import paddle\n", + "from paddle_quantum.data_analysis.vqls import compute\n", + "\n", + "paddle.seed(0)\n", + "\n", + "if __name__ == \"__main__\":\n", + " config = toml.loads(test_toml)\n", + " A_dir = config.pop(\"A_dir\")\n", + " A = np.load(A_dir)\n", + " b_dir = config.pop(\"b_dir\")\n", + " b = np.load(b_dir)\n", + " result = compute(A, b, **config)\n", + "\n", + " print(\"求解 Ax=b 的x:\", result)\n", + " print(\"实际 b 的值:\", b)\n", + " print(\"算法得到的 Ax 的值:\", np.matmul(A, result))\n", + " relative_error = np.linalg.norm(b - np.matmul(A, result)) / np.linalg.norm(b)\n", + " print(\"相对误差: \", relative_error)\n", + " np.save(\"./answer.npy\", result)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 引用信息\n", + "\n", + "```\n", + "@misc{bravo-prieto2020variational,\n", + " title = {Variational {{Quantum Linear Solver}}},\n", + " author = {{Bravo-Prieto}, Carlos and LaRose, Ryan and Cerezo, M. and Subasi, Yigit and Cincio, Lukasz and Coles, Patrick J.},\n", + " year = {2020},\n", + " month = jun,\n", + " number = {arXiv:1909.05820},\n", + " eprint = {1909.05820},\n", + " eprinttype = {arxiv},\n", + " doi = {10.48550/arXiv.1909.05820}\n", + "}\n", + "```\n", + "\n", + "## 参考文献\n", + "\n", + "[1] “Variational Quantum Linear Solver: A Hybrid Algorithm for Linear Systems.” Carlos Bravo-Prieto, Ryan LaRose, Marco Cerezo, Yigit Subasi, Lukasz Cincio, Patrick J. Coles. arXiv:1909.05820, 2019." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pq-dev", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.15 (default, Nov 10 2022, 13:17:42) \n[Clang 14.0.6 ]" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "5fea01cac43c34394d065c23bb8c1e536fdb97a765a18633fd0c4eb359001810" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/applications/linear_solver/introduction_en.ipynb b/applications/linear_solver/introduction_en.ipynb new file mode 100644 index 0000000..16f22ba --- /dev/null +++ b/applications/linear_solver/introduction_en.ipynb @@ -0,0 +1,255 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Variational Quantum Linear Solver\n", + "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*\n", + "## Background\n", + "\n", + "System of linear equations is a basic yet extremely useful tool in mathematics. An example is that in economics, you can model the economy using linear equations. Also, it provides simple estimation for large system of non-linear systems. Hence solving system of linear equations is an important task.\n", + "\n", + "Variational Quantum Linear Solver (VQLS) is a variational quantum algorithm for solving system of linear equations. It's a classical-quantum hybrid algorithm that can run on recent Noisy Intermediate-Scale Quantum (NISQ) devices. To be more specific, given a matrix $A$ and a vector $\\boldsymbol{b}$, our goal is to find a vector $\\boldsymbol{x}$ so that $A \\boldsymbol{x} = \\boldsymbol{b}$. Using VQLS, we can obtain a quantum state that is proportional to $\\boldsymbol{x}$, i.e. a normalised vector $|x\\rangle = \\frac{\\boldsymbol{x}}{\\lVert \\boldsymbol{x} \\rVert_2}$." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Model Principle\n", + "\n", + "Solving linear equations in the quantum setting is different to the general setting due to the requirement in quantum computing that we can only apply unitary operators to a quantum state. For the input matrix $A$, we need to decompose it to a linear combination of unitary operators $A = \\sum_n c_n A_n$ where each $A_n$ is a unitary operator. For the input vector $\\boldsymbol{b}$, we need to assume that it's a quantum state that can be prepared by unitary operator $U$, i.e. $U|0\\rangle = |b\\rangle$.\n", + "\n", + "![VQLS](vqls.png)\n", + "\n", + "We can see that the algorithm consists of two parts. On a quantum computer, we prepare a parameterized quantum circuit (PQC) $V(\\alpha)$ and compute the loss function $C(\\alpha)$, then on a classical computer, we minimize parameters $\\alpha$ until the loss function is below a certain threshold, denoted as $\\gamma$. At the end, we output the target quantum state $|x\\rangle$. The main idea behind the algorithm is that PQC $V(\\alpha)$ gives us a quantum state $|\\psi(\\alpha)\\rangle$, circuit $F(A)$ then computes how similar $A|\\psi(\\alpha)\\rangle$ and $|b\\rangle$ are, which is what the loss function $C(\\alpha)$ is measuring. When the loss is small, $A|\\psi(\\alpha)\\rangle$ and $|b\\rangle$ are very close, it means $|\\psi(\\alpha)\\rangle$ and the target $|x\\rangle$ are very close, so we output $|\\psi(\\alpha)\\rangle$ as an approximation to $|x\\rangle$." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Paddle Quantum Implementation\n", + "\n", + "We use the `Circuit` class in Paddle Quantum and optimizer in Paddle Paddle to implement VQLS. For the quantum part, we use built-in `complex_entangled_layer` ansatz to build our PQC $V(\\alpha)$. To compute the loss function, we use Hadamard Test and Hadamard-Overlap Test which utilizes `oracle` gate to implement the controlled-$A_n$ gates. For the classical optimization part, we used Adam optimizer to minimize the loss function.\n", + "\n", + "User can use toml file to specify the input to the algorithm, matrix $A$ and vector $\\boldsymbol{b}$, stored as '.npy' files. You can run the following code to randomly generate a $n\\times n$ matrix $A$ and vector $\\boldsymbol{b}$:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Here is a randomly generated A:\n", + "[[4.1702199e+00+7.203245j 1.1437482e-03+3.0233257j\n", + " 1.4675589e+00+0.9233859j 1.8626021e+00+3.4556072j\n", + " 3.9676747e+00+5.3881674j ]\n", + " [2.0445225e+00+8.781175j 2.7387592e-01+6.704675j\n", + " 4.1730480e+00+5.5868983j 1.4038694e+00+1.9810148j\n", + " 8.0074453e+00+9.682616j ]\n", + " [8.7638912e+00+8.946067j 8.5044211e-01+0.39054784j\n", + " 1.6983042e+00+8.781425j 9.8346835e-01+4.2110763j\n", + " 9.5788956e+00+5.3316526j ]\n", + " [6.8650093e+00+8.346256j 1.8288277e-01+7.5014434j\n", + " 9.8886108e+00+7.4816566j 2.8044400e+00+7.892793j\n", + " 1.0322601e+00+4.4789352j ]\n", + " [2.8777535e+00+1.3002857j 1.9366957e-01+6.7883554j\n", + " 2.1162813e+00+2.6554666j 4.9157314e+00+0.5336254j\n", + " 5.7411761e+00+1.4672858j ]]\n", + "Here is a randomly generated b:\n", + "[4.191945 +6.852195j 3.1342418+6.9232264j 6.9187713+3.1551564j\n", + " 9.085955 +2.9361415j 5.8930554+6.9975834j]\n" + ] + } + ], + "source": [ + "n = 5\n", + "\n", + "import numpy as np\n", + "\n", + "\n", + "np.random.seed(1)\n", + "A = np.zeros([n, n], dtype=\"complex64\")\n", + "b = np.zeros(n, dtype=\"complex64\")\n", + "for i in range(n):\n", + " for j in range(n):\n", + " x = np.random.rand() * 10\n", + " y = np.random.rand() * 10\n", + " A[i][j] = complex(x, y)\n", + " x = np.random.rand() * 10\n", + " y = np.random.rand() * 10\n", + " b[i] = complex(x, y)\n", + "np.save(\"./A.npy\", A)\n", + "np.save(\"./b.npy\", b)\n", + "print(\"Here is a randomly generated A:\")\n", + "print(A)\n", + "print(\"Here is a randomly generated b:\")\n", + "print(b)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "User can specify the parameters of the VQLS in the toml file. They are `depth`, `iterations`, `LR` and `gamma`, which correspond to the number of layer in the PQC $V(\\alpha)$, number of iterations of the optimizer, learning rate of the optimizer and threshold of the loss function to end optimization early. By entering `python vqls.py --config config.toml` one could solve the linear system. Here we present an example of an online demo. First, define the content of the configuration file as follows, user can try out different settings by changing the parameters of `test_toml`:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "test_toml = r\"\"\"\n", + "# The path of the input matrix A. It should be a .npy file.\n", + "A_dir = './A.npy'\n", + "# The path of the input vector b. It should be a .npy file.\n", + "b_dir = './b.npy'\n", + "# The depth of the quantum ansatz circuit.\n", + "depth = 4\n", + "# Number optimization cycles.\n", + "iterations = 200\n", + "# The learning rate of the optimizer.\n", + "LR = 0.1\n", + "# Threshold for loss value to end optimization early, default is 0.\n", + "gamma = 0\n", + "\"\"\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then we run the VQLS:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 88%|████████▊ | 176/200 [02:03<00:16, 1.43it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Threshold value gamma reached, ending optimization\n", + "Here is x that solves Ax=b: [ 1.3475237 -0.7860472j 0.22970617-0.88826376j -0.35111237-0.31225887j\n", + " 0.07606918+1.2138402j -0.729564 +0.48393282j]\n", + "This is actual b: [4.191945 +6.852195j 3.1342418+6.9232264j 6.9187713+3.1551564j\n", + " 9.085955 +2.9361415j 5.8930554+6.9975834j]\n", + "This is Ax using estimated x: [4.185339 +6.8523855j 3.1297188+6.923625j 6.924285 +3.1467872j\n", + " 9.092921 +2.932943j 5.8879805+6.999589j ]\n", + "Relative error: 0.0008446976\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "import argparse\n", + "import os\n", + "import warnings\n", + "\n", + "warnings.filterwarnings(\"ignore\")\n", + "os.environ[\"PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION\"] = \"python\"\n", + "\n", + "import toml\n", + "import numpy as np\n", + "import paddle\n", + "from paddle_quantum.data_analysis.vqls import compute\n", + "\n", + "paddle.seed(0)\n", + "\n", + "if __name__ == \"__main__\":\n", + " config = toml.loads(test_toml)\n", + " A_dir = config.pop(\"A_dir\")\n", + " A = np.load(A_dir)\n", + " b_dir = config.pop(\"b_dir\")\n", + " b = np.load(b_dir)\n", + " result = compute(A, b, **config)\n", + "\n", + " print(\"Here is x that solves Ax=b:\", result)\n", + " print(\"This is actual b:\", b)\n", + " print(\"This is Ax using estimated x:\", np.matmul(A, result))\n", + " relative_error = np.linalg.norm(b - np.matmul(A, result)) / np.linalg.norm(b)\n", + " print(\"Relative error: \", relative_error)\n", + " np.save(\"./answer.npy\", result)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Citation\n", + "\n", + "```\n", + "@misc{bravo-prieto2020variational,\n", + " title = {Variational {{Quantum Linear Solver}}},\n", + " author = {{Bravo-Prieto}, Carlos and LaRose, Ryan and Cerezo, M. and Subasi, Yigit and Cincio, Lukasz and Coles, Patrick J.},\n", + " year = {2020},\n", + " month = jun,\n", + " number = {arXiv:1909.05820},\n", + " eprint = {1909.05820},\n", + " eprinttype = {arxiv},\n", + " doi = {10.48550/arXiv.1909.05820}\n", + "}\n", + "```\n", + "\n", + "## References\n", + "\n", + "[1] “Variational Quantum Linear Solver: A Hybrid Algorithm for Linear Systems.” Carlos Bravo-Prieto, Ryan LaRose, Marco Cerezo, Yigit Subasi, Lukasz Cincio, Patrick J. Coles. arXiv:1909.05820, 2019." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pq-dev", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.15 (default, Nov 10 2022, 13:17:42) \n[Clang 14.0.6 ]" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "5fea01cac43c34394d065c23bb8c1e536fdb97a765a18633fd0c4eb359001810" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/applications/linear_solver/vqls.png b/applications/linear_solver/vqls.png new file mode 100644 index 0000000..56b26cf Binary files /dev/null and b/applications/linear_solver/vqls.png differ diff --git a/applications/linear_solver/vqls.py b/applications/linear_solver/vqls.py new file mode 100644 index 0000000..303d359 --- /dev/null +++ b/applications/linear_solver/vqls.py @@ -0,0 +1,54 @@ +# !/usr/bin/env python3 +# Copyright (c) 2020 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +Variational Quantum Linear Solver +""" + +import argparse +import os +import warnings + +warnings.filterwarnings('ignore') +os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python' + +import toml +import logging +import numpy as np +from paddle_quantum.data_analysis.vqls import compute + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Solve system of linear equations.") + parser.add_argument("--config", type=str, help="Input the config file with toml format.") + args = parser.parse_args() + config = toml.load(args.config) + A_dir = config.pop('A_dir') + A = np.load(A_dir) + b_dir = config.pop('b_dir') + b = np.load(b_dir) + result = compute(A, b, **config) + + print('Here is x that solves Ax=b:', result) + relative_error = np.linalg.norm(b- np.matmul(A,result))/np.linalg.norm(b) + print('Relative error: ', relative_error) + logging.basicConfig( + filename='./linear_solver.log', + filemode='w', + format='%(asctime)s %(levelname)s %(message)s', + level=logging.INFO + ) + msg = f"Relative error: {relative_error}" + logging.info(msg) + np.save('./answer.npy', result) \ No newline at end of file diff --git a/applications/lithium_ion_battery/config.toml b/applications/lithium_ion_battery/config.toml new file mode 100644 index 0000000..5187651 --- /dev/null +++ b/applications/lithium_ion_battery/config.toml @@ -0,0 +1,40 @@ +# A description of the task of this configuration file, this is optional. "GroundState" stands for calculate the ground state energy of the molecule. +task = 'GroundState' + +# This field stores information related to the molecule is provided. +[molecule] +# Symbols of atoms inside the molecule. +symbols = ['H', 'H'] +# The cartesian coordinates of each atom inside the molecule. +coords = [ [ 0.0, 0.0, 0.0 ], [ 0.0, 0.0, 0.7 ] ] +# Quantum chemistry basis set used in the computation, see here for more information of the basis set, https://baike.baidu.com/item/%E5%9F%BA%E7%BB%84/6445527?fr=aladdin, Default is "sto-3g". +basis = 'sto-3g' +# Which unit system is used in the `coords` provided above. +# If set to `true` will use Angstrom. +# If set to `false` will use Bohr. +use_angstrom = true + +# This field specifies configurations of classical quantum chemistry driver used to calculate the molecular integrals. NOTE: Classical quantum chemistry package needs to be preinstalled. +[driver] +# If set to `pyscf`, means PySCF is used (currently only support `pyscf` driver, will add more classical driver in the future). +name = 'pyscf' + +# This field specifies configurations related to the quantum circuit in VQE is specified. +# NOTE: currently only support HardwareEfficient ansatz, more ansatz will come later! +[ansatz.HardwareEfficient] +# The depth of the HardwareEfficient ansatz. NOTE: on a personal laptop, we suggest the depth of the circuit should no more than 10. +depth = 2 + +# This field stores configurations of the variational quantum eigensolver (VQE) method. +[VQE] +# Number of optimization cycles, default is 100. +num_iterations = 100 +# The convergence criteria for the VQE optimization, default is 1e-5. +tol = 1e-5 +# The number of optimization steps after which we record the loss value. +save_every = 10 + +# This field specifies the optimizer used in the VQE method, default is `Adam`, see here for available optimizers https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/optimizer/Overview_cn.html +[optimizer.Adam] +# The learning rate of the optimizer, see here for more details https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/optimizer/Adam_cn.html, default is 0.4. +learning_rate = 0.4 diff --git a/applications/lithium_ion_battery/energy_material.py b/applications/lithium_ion_battery/energy_material.py new file mode 100644 index 0000000..6e3b625 --- /dev/null +++ b/applications/lithium_ion_battery/energy_material.py @@ -0,0 +1,106 @@ +# !/usr/bin/env python3 +# Copyright (c) 2020 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Dict +import time +import logging +import argparse +import toml +import paddle.optimizer as optim +from paddle_quantum import qchem +from paddle_quantum.qchem import Molecule +from paddle_quantum.qchem import PySCFDriver +from paddle_quantum.qchem import GroundStateSolver +from paddle_quantum.qchem import energy, dipole_moment + +#BUG: basicConfig changed in python3.7 +logging.basicConfig(filename="log", filemode="w", format="%(message)s", level=logging.INFO) + + +def main(args): + time_start = time.strftime("%Y%m%d-%H:%M:%S", time.localtime()) + logging.info(f"Job start at {time_start:s}") + + parsed_configs: Dict = toml.load(args.config) + + # create molecule + atom_symbols = parsed_configs["molecule"]["symbols"] + basis = parsed_configs["molecule"].get("basis", "sto-3g") + multiplicity = parsed_configs["molecule"].get("multiplicity") + charge = parsed_configs["molecule"].get("charge") + use_angstrom = parsed_configs["molecule"].get("use_angstrom", True) + + if parsed_configs.get("driver") is None or parsed_configs["driver"]["name"] == "pyscf": + driver = PySCFDriver() + else: + raise NotImplementedError("Drivers other than PySCFDriver are not implemented yet.") + + if isinstance(atom_symbols, str): + raise NotImplementedError("`load_geometry` function is not implemented yet.") + elif isinstance(atom_symbols, list): + atom_coords = parsed_configs["molecule"]["coords"] + geometry = list(zip(atom_symbols, atom_coords)) + mol = Molecule(geometry, basis, multiplicity, charge, use_angstrom=use_angstrom, driver=driver) + else: + raise ValueError("Symbols can only be string or list, e.g. 'LiH' or ['H', 'Li']") + mol.build() + + # create ansatz + num_qubits = mol.num_qubits + ansatz_settings = parsed_configs["ansatz"] + ansatz_name = list(ansatz_settings.keys())[0] + ansatz_class = getattr(qchem, ansatz_name) + ansatz = ansatz_class(num_qubits, **ansatz_settings[ansatz_name]) + + # load optimizer + if parsed_configs.get("optimizer") is None: + optimizer_name = "Adam" + optimizer_settings = { + "Adam": { + "learning_rate": 0.4 + } + } + optimizer = optim.Adam + else: + optimizer_settings = parsed_configs["optimizer"] + optimizer_name = list(optimizer_settings.keys())[0] + optimizer = getattr(optim, optimizer_name) + + # calculate properties + if parsed_configs.get("VQE") is None: + vqe_settings = { + "num_iterations": 100, + "tol": 1e-5, + "save_every": 10 + } + else: + vqe_settings = parsed_configs["VQE"] + solver = GroundStateSolver(optimizer, **vqe_settings) + _, psi = solver.solve(mol, ansatz, **optimizer_settings[optimizer_name]) + e = energy(psi, mol) + d = dipole_moment(psi, mol) + + logging.info("\n#######################################\nSummary\n#######################################") + logging.info(f"Ground state energy={e:.5f}") + logging.info(f"dipole moment=({d[0]:.5f}, {d[1]:.5f}, {d[2]:.5f}).") + + time_stop = time.strftime("%Y%m%d-%H:%M:%S", time.localtime()) + logging.info(f"\nJob end at {time_stop:s}\n") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Quantum chemistry task with paddle quantum.") + parser.add_argument("--config", type=str, help="Input the config file with toml format.") + main(parser.parse_args()) diff --git a/applications/lithium_ion_battery/example.toml b/applications/lithium_ion_battery/example.toml new file mode 100644 index 0000000..04fa61a --- /dev/null +++ b/applications/lithium_ion_battery/example.toml @@ -0,0 +1,8 @@ +[molecule] +symbols = ['H', 'H'] +coords = [ [ 0.0, 0.0, 0.0 ], [ 0.0, 0.0, 0.74 ] ] + +# NOTE: currently only support HardwareEfficient ansatz, more ansatz will come later! +# NOTE: on a personal laptop, we suggest the depth of the circuit should no more than 10. +[ansatz.HardwareEfficient] +depth = 2 diff --git a/applications/lithium_ion_battery/introduction_cn.ipynb b/applications/lithium_ion_battery/introduction_cn.ipynb new file mode 100644 index 0000000..f1d5cb2 --- /dev/null +++ b/applications/lithium_ion_battery/introduction_cn.ipynb @@ -0,0 +1,329 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import warnings\n", + "\n", + "warnings.filterwarnings('ignore')" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 锂电池材料计算简介\n", + "\n", + "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*\n", + "\n", + "锂电池是一种高效、长寿命的电池,广泛用于各种消费类电子产品、工业设备和汽车动力系统中。锂电池的发展促进了电动汽车的普及,也为可再生能源储存提供了重要支撑。由于锂电池具有轻量、高能量密度和环保等优点,其应用前景广阔,且行业发展前景好。目前,锂电池行业正在经历快速发展,并不断创新和推广新技术。\n", + "\n", + "在锂电池中,能量主要通过电池正负极物质之间的电化学反应产生,\n", + "$$\n", + "LiC_6+CoO_2\\stackrel{放电}{\\underset{\\text{充电}}{\\rightleftarrows}}6C+LiCoO_2.\n", + "$$\n", + "\n", + "根据热力学规律,这个能量等于放电反应前后正负极物质自由能\\[1\\]的变化量,在多数情况下,它可以通过反应前后物质的基态能量差进行估计\\[2\\]\n", + "$$\n", + "\\Delta G=G_{\\text{LiCoO}_2}+6G_{\\text{C}}-G_{\\text{CoO}_2}-G_{\\text{LiC}_6}.\n", + "$$\n", + "\n", + "综上,由于化学反应的能量与反应方程式两侧分子/物质的能量有紧密联系,我们在衡量锂电池释放的总能量时就需要对相关分子/物质的基态能量有比较精确的估计。同时,反应中锂离子通过电解液在正负极之间迁移的过程也会涉及到离子在分子和材料表面的吸附,这会受到分子电极化情况的影响。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 利用量子计算化学方法计算分子基态性质\n", + "### 构建分子哈密顿量\n", + "分子哈密顿量由分子的几何结构和组成的原子决定,利用 `paddle_quantum` 的 `qchem` 模块,用户可以很方便地完成从分子的结构到哈密顿量的计算过程。" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from paddle_quantum.qchem import Molecule, PySCFDriver\n", + "\n", + "mol = Molecule(\n", + " geometry = [(\"H\", [0.0, 0.0, 0.0]), (\"H\", [0.0, 0.0, 0.7])],\n", + " driver = PySCFDriver()\n", + ")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "由此,我们就新建了一个氢分子模型。在上面的代码中,`driver` 是用来计算哈密顿量中分子积分所使用的经典量子化学计算工具。" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "converged SCF energy = -1.11734903499028\n", + "-0.04207897647782183 I\n", + "0.17771287465139923 Z0\n", + "0.17771287465139923 Z1\n", + "-0.24274280513140506 Z2\n", + "-0.24274280513140506 Z3\n", + "0.17059738328801052 Z0, Z1\n", + "0.12293305056183806 Z0, Z2\n", + "0.16768319457718972 Z0, Z3\n", + "0.16768319457718972 Z1, Z2\n", + "0.12293305056183806 Z1, Z3\n", + "0.1762764080431961 Z2, Z3\n", + "-0.044750144015351656 X0, X1, Y2, Y3\n", + "0.044750144015351656 X0, Y1, Y2, X3\n", + "0.044750144015351656 Y0, X1, X2, Y3\n", + "-0.044750144015351656 Y0, Y1, X2, X3\n" + ] + } + ], + "source": [ + "h = mol.get_molecular_hamiltonian()\n", + "print(h)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "这样就构造了一个氢分子在 `STO-3G` 基组下的哈密顿量。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 搭建用于变分量子算法(VQE)的量子线路\n", + "变分量子算法通过使用经典优化器对量子算法中的可调参数进行优化的方式完成经典-量子混合计算,相比于经典算法,它可以在某些情况下更快地求解具有高维特征的优化问题。接下来,我们按照\\[2\\]中给出的方法构建如下图所示的量子线路。" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "converged SCF energy = -1.11734903499028\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from paddle_quantum.qchem import HardwareEfficient\n", + "\n", + "mol.build()\n", + "cir = HardwareEfficient(mol.num_qubits, depth=2)\n", + "cir.plot()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 计算氢分子势能面\n", + "下面我们通过计算氢分子势能面的任务来展示一下 VQE 方法的使用。分子势能面刻画了分子的能量随着分子内部原子的空间位置变化的规律,它在物理和化学中有很广泛的应用,经常被用于寻找分子的最优几何结构以及计算化学反应速率。对于氢分子来说,因为内部只有一个空间自由度(两个氢原子之间的距离),它的势能面实际上是一条曲线。" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "converged SCF energy = 2.71588739329275\n", + "converged SCF energy = -0.593827758535727\n", + "converged SCF energy = -1.04299627454009\n", + "converged SCF energy = -1.11675930739643\n", + "converged SCF energy = -1.09191404102006\n", + "converged SCF energy = -1.06610864931794\n", + "converged SCF energy = -0.910873554594387\n", + "converged SCF energy = -0.783792654277353\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import paddle\n", + "from paddle.optimizer import Adam\n", + "from paddle_quantum.qchem import GroundStateSolver\n", + "from paddle_quantum.qchem import dipole_moment\n", + "\n", + "paddle.seed(124)\n", + "\n", + "cir_depth = 2\n", + "bond_lengthes = [0.1, 0.3, 0.5, 0.74, 0.9, 1.0, 1.5, 2.0]\n", + "energies = []\n", + "dipole_moments = []\n", + "for bond_len in bond_lengthes:\n", + " mol = Molecule(\n", + " geometry = [(\"H\", [0.0, 0.0, 0.0]), (\"H\", [0.0, 0.0, bond_len])],\n", + " driver = PySCFDriver()\n", + " )\n", + " mol.build()\n", + " \n", + " cir = HardwareEfficient(mol.num_qubits, cir_depth)\n", + "\n", + " solver = GroundStateSolver(Adam, num_iterations=100, tol=1e-5)\n", + " e, psi = solver.solve(mol, cir, learning_rate=0.5)\n", + " energies.append(e)\n", + "\n", + " # calculate dipole moments\n", + " d = dipole_moment(psi, mol)\n", + " dipole_moments.append(np.linalg.norm(d))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "我们画出计算结果,可以看出氢分子的基态能量随着两个氢原子之间的距离增大先下降后上升,能量最低点对应的键长(也即平衡位置)为 0.74 埃。氢分子是一个非极性的分子,不管键长如何变化,它的偶极矩始终为零。" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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fvlFo31Xq1YNW7duxfbt29GwYUMsX74c06dPB/Dyqz+5XI4JEyaozletWrXwww8/AABcXV0xZ84cBAUFwcnJCQEBAQUey9XVFXv37sXp06fh7e2NcePGYdSoUfjss8+0Oh/52bRpE9566y24u7u/9r6Iyjq2uWxz2eaWDRKRs7+bSI917doVzs7O+Pnnn0tk/x9//DESExOxcuXKEtl/TgsWLMCKFSvw4MGDEj9WccjIyEDNmjWxceNGtGnTRtfhEFEpYJurO2xzX+LNXqSXUlNTsWLFCvj4+MDAwACbNm3CoUOHcPDgwRI75vTp0/HDDz9AoVC89ldCuf3www9o3rw5KlWqhBMnTuCrr74q9FN+WXP//n18+umnFb5BJSqv2OaWLWxzX2KPLOml58+fo3fv3jh//jzS0tLg5eWFzz77DO+8846uQyuSjz76CJs3b0ZcXByqVq2K9957D9OmTYOhIT9rEpHusc2lsoqJLBERERHpJd7sRURERER6iYksEREREeklJrJEREREpJeYyBIRERGRXmIiS0RERER6iYksEREREeklJrJEREREpJeYyBIRERGRXmIiS0RERER66f8llUqVQXVHtgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "fig = plt.figure(figsize=(7, 3), )\n", + "ax1 = fig.add_subplot(121)\n", + "ax1.plot(bond_lengthes, energies)\n", + "ax1.set_xlabel(\"Bond length (Angstrom)\")\n", + "ax1.set_ylabel(\"Energy (Hartree)\")\n", + "ax1.set_title(\"Potential energy curve\")\n", + "\n", + "ax2 = fig.add_subplot(122)\n", + "ax2.plot(bond_lengthes, dipole_moments)\n", + "ax2.set_xlabel(\"Bond length (Angstrom)\")\n", + "ax2.set_ylabel(\"Dipole moment\")\n", + "ax2.set_title(\"Dipole moment variation\")\n", + "ax2.set_ylim(-0.5, 0.5)\n", + "fig.tight_layout()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 如何使用\n", + "用户可以通过编辑配置文件`config.toml`对计算任务进行自定义,其中用户需要设置的参数如下:\n", + "```toml\n", + "# A description of the task of this configuration file, this is optional. \"GroundState\" stands for calculate the ground state energy of the molecule.\n", + "\n", + "# This field stores information related to the molecule is provided.\n", + "[molecule]\n", + "# Symbols of atoms inside the molecule.\n", + "symbols = ['H', 'H']\n", + "# The cartesian coordinates of each atom inside the molecule.\n", + "coords = [ [ 0.0, 0.0, 0.0 ], [ 0.0, 0.0, 0.7 ] ]\n", + "\n", + "# This field specifies configurations related to the quantum circuit in VQE is specified.\n", + "[ansatz.HardwareEfficient]\n", + "# The depth of the HardwareEfficient ansatz.\n", + "depth = 2\n", + "\n", + "# This field stores configurations of the variational quantum eigensolver (VQE) method. \n", + "[VQE]\n", + "# Number of optimization cycles, default is 100.\n", + "num_iterations = 100\n", + "```\n", + "用户可以通过在命令行中运行\n", + "```shell\n", + "python energy_material.py --config example.toml\n", + "```\n", + "的方式来完成量子化学计算任务。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 参考文献\n", + "\\[1\\] [百度百科-自由能](https://baike.baidu.com/item/%E8%87%AA%E7%94%B1%E8%83%BD/813477?fr=aladdin)\n", + "\n", + "\\[2\\] Aydinol, M. K., et al. \"Ab initio study of lithium intercalation in metal oxides and metal dichalcogenides.\" Physical Review B 56.3 (1997): 1354.\n", + "\n", + "\\[3\\] Kandala, Abhinav, et al. \"Hardware-efficient variational quantum eigensolver for small molecules and quantum magnets.\" Nature 549.7671 (2017): 242-246." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pq-dev", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.15 (default, Nov 10 2022, 13:17:42) \n[Clang 14.0.6 ]" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "5fea01cac43c34394d065c23bb8c1e536fdb97a765a18633fd0c4eb359001810" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/applications/lithium_ion_battery/introduction_en.ipynb b/applications/lithium_ion_battery/introduction_en.ipynb new file mode 100644 index 0000000..63dcdf2 --- /dev/null +++ b/applications/lithium_ion_battery/introduction_en.ipynb @@ -0,0 +1,328 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import warnings\n", + "\n", + "warnings.filterwarnings('ignore')" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Introduction to computational study of lithium ion battery\n", + "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*\n", + "\n", + "Lithium ion battery is an efficient and long-lasting battery, it has been widely used in various consumer electronics, industrial devices and automotive power systems. The development of lithium ion battery has promoted the popularization of electric vehicles and provided strong support for storage of renewable energy. Due to the advantages of lithium batteries, such as being lightweight, having high energy density, and being environmentally friendly, they have a wide range of applications and a good industry development prospect. Currently, the industry of lithium ion battery is under rapid development and is continuously innovating and promoting new technologies.\n", + "\n", + "The energy of lithium ion battery is generated by the electron-chemical interaction between its cathode and anode materials.\n", + "$$\n", + "LiC_6+CoO_2\\stackrel{Discharge}{\\underset{\\text{Charge}}{\\rightleftarrows}}6C+LiCoO_2.\n", + "$$\n", + "\n", + "According to the thermodynamics, this energy equals the difference of free energy \\[1\\] between materials in cathode and anode, which can be approximated by their ground state energy difference\\[2\\].\n", + "$$\n", + "\\Delta G=G_{\\text{LiCoO}_2}+6G_{\\text{C}}-G_{\\text{CoO}_2}-G_{\\text{LiC}_6}.\n", + "$$\n", + "\n", + "Since the energy emitted during the chemical reaction is closely related to the molecules/materials occur in above formula, we have to obtain the accurate ground state energy of these molecules/materials once we want to estimate the total energy of the lithium ion battery. Meanwhile, the transport of lithium ion between the cathode and anode involves ion adsorption, which can be affected by the polarizability of related molecules." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Calculate ground state properties of molecule by quantum computational chemistry\n", + "### Build molecular Hamiltonian\n", + "The Hamiltonian of molecule is determined by its geometry and composition. Though the underlying procedure is a little complicated, users can easily achieve it by using `paddle_quantum`'s `qchem` module." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from paddle_quantum.qchem import Molecule, PySCFDriver\n", + "\n", + "mol = Molecule(\n", + " geometry = [(\"H\", [0.0, 0.0, 0.0]), (\"H\", [0.0, 0.0, 0.7])],\n", + " driver = PySCFDriver()\n", + ")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we have built a hydrogen molecule model. In the code above, the `driver` is used to specify the classical quantum chemistry tool used in calculation of molecular integrals." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "converged SCF energy = -1.11734903499028\n", + "-0.04207897647782183 I\n", + "0.17771287465139923 Z0\n", + "0.17771287465139923 Z1\n", + "-0.24274280513140506 Z2\n", + "-0.24274280513140506 Z3\n", + "0.17059738328801052 Z0, Z1\n", + "0.12293305056183806 Z0, Z2\n", + "0.16768319457718972 Z0, Z3\n", + "0.16768319457718972 Z1, Z2\n", + "0.12293305056183806 Z1, Z3\n", + "0.1762764080431961 Z2, Z3\n", + "-0.044750144015351656 X0, X1, Y2, Y3\n", + "0.044750144015351656 X0, Y1, Y2, X3\n", + "0.044750144015351656 Y0, X1, X2, Y3\n", + "-0.044750144015351656 Y0, Y1, X2, X3\n" + ] + } + ], + "source": [ + "h = mol.get_molecular_hamiltonian()\n", + "print(h)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "At this step, we have built a Hamiltonian for hydrogen molecule under `STO-3G` basis." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Construct quantum circuit used in variational quantum eigensolver (VQE)\n", + "Variational quantum algorithm (VQE) performs hybrid classical-quantum computation by optimizing parameters in quantum circuit on a classical optimizer. Compared to its classical counterpart, VQE can efficiently solve optimization problem in high dimensional space in some situations. Let's now construct a quantum circuit for our VQE task from \\[3\\]." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "converged SCF energy = -1.11734903499028\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from paddle_quantum.qchem import HardwareEfficient\n", + "\n", + "mol.build()\n", + "cir = HardwareEfficient(mol.num_qubits, depth=2)\n", + "cir.plot()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Computing potential energy curve for hydrogen molecule\n", + "Next we will demostrate the usage of VQE method by calculating the potential energy curve of hydrogen molecule. Potential energy surface depicts the variation of molecular energy with respect to the position of inner atoms, it is widely used in physics and chemistry to find the optimal molecular structure or calculate the chemical reaction rate. For hydrogen molecule, the potential energy surface becomes a curve since hydrogen molecule only has one spatial degree of freedom (the distance between two hydrogen atoms)." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "converged SCF energy = 2.71588739329275\n", + "converged SCF energy = -0.593827758535727\n", + "converged SCF energy = -1.04299627454009\n", + "converged SCF energy = -1.11675930739643\n", + "converged SCF energy = -1.09191404102006\n", + "converged SCF energy = -1.06610864931794\n", + "converged SCF energy = -0.910873554594387\n", + "converged SCF energy = -0.783792654277353\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import paddle\n", + "from paddle.optimizer import Adam\n", + "from paddle_quantum.qchem import GroundStateSolver\n", + "from paddle_quantum.qchem import dipole_moment\n", + "\n", + "paddle.seed(124)\n", + "\n", + "cir_depth = 2\n", + "bond_lengthes = [0.1, 0.3, 0.5, 0.74, 0.9, 1.0, 1.5, 2.0]\n", + "energies = []\n", + "dipole_moments = []\n", + "for bond_len in bond_lengthes:\n", + " mol = Molecule(\n", + " geometry = [(\"H\", [0.0, 0.0, 0.0]), (\"H\", [0.0, 0.0, bond_len])],\n", + " driver = PySCFDriver()\n", + " )\n", + " mol.build()\n", + " \n", + " cir = HardwareEfficient(mol.num_qubits, cir_depth)\n", + "\n", + " solver = GroundStateSolver(Adam, num_iterations=100, tol=1e-5)\n", + " e, psi = solver.solve(mol, cir, learning_rate=0.5)\n", + " energies.append(e)\n", + "\n", + " # calculate dipole moments\n", + " d = dipole_moment(psi, mol)\n", + " dipole_moments.append(np.linalg.norm(d))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the first plot, we can see that the ground state energy of hydrogen molecule first decrease with the growth of the extension of two hydrogen atoms and then increase, the lowest ground state energy occurs at bond length equal to 0.74 Angstrom. In the second plot, we can see that hydrogen molecule is unpolarized, its dipole moment is approximately zero no matter how bond length varies." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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fvlFo31Xq1YNW7duxfbt29GwYUMsX74c06dPB/Dyqz+5XI4JEyaozletWrXwww8/AABcXV0xZ84cBAUFwcnJCQEBAQUey9XVFXv37sXp06fh7e2NcePGYdSoUfjss8+0Oh/52bRpE9566y24u7u/9r6Iyjq2uWxz2eaWDRKRs7+bSI917doVzs7O+Pnnn0tk/x9//DESExOxcuXKEtl/TgsWLMCKFSvw4MGDEj9WccjIyEDNmjWxceNGtGnTRtfhEFEpYJurO2xzX+LNXqSXUlNTsWLFCvj4+MDAwACbNm3CoUOHcPDgwRI75vTp0/HDDz9AoVC89ldCuf3www9o3rw5KlWqhBMnTuCrr74q9FN+WXP//n18+umnFb5BJSqv2OaWLWxzX2KPLOml58+fo3fv3jh//jzS0tLg5eWFzz77DO+8846uQyuSjz76CJs3b0ZcXByqVq2K9957D9OmTYOhIT9rEpHusc2lsoqJLBERERHpJd7sRURERER6iYksEREREeklJrJEREREpJeYyBIRERGRXmIiS0RERER6iYksEREREeklJrJEREREpJeYyBIRERGRXmIiS0RERER66f8llUqVQXVHtgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "fig = plt.figure(figsize=(7, 3), )\n", + "ax1 = fig.add_subplot(121)\n", + "ax1.plot(bond_lengthes, energies)\n", + "ax1.set_xlabel(\"Bond length (Angstrom)\")\n", + "ax1.set_ylabel(\"Energy (Hartree)\")\n", + "ax1.set_title(\"Potential energy curve\")\n", + "\n", + "ax2 = fig.add_subplot(122)\n", + "ax2.plot(bond_lengthes, dipole_moments)\n", + "ax2.set_xlabel(\"Bond length (Angstrom)\")\n", + "ax2.set_ylabel(\"Dipole moment\")\n", + "ax2.set_title(\"Dipole moment variation\")\n", + "ax2.set_ylim(-0.5, 0.5)\n", + "fig.tight_layout()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## How to use\n", + "Users can edit the configuration file `config.toml` to customize the task, and the required settings are listed below.\n", + "\n", + "```toml\n", + "# A description of the task of this configuration file, this is optional. \"GroundState\" stands for calculate the ground state energy of the molecule.\n", + "\n", + "# This field stores information related to the molecule is provided.\n", + "[molecule]\n", + "# Symbols of atoms inside the molecule.\n", + "symbols = ['H', 'H']\n", + "# The cartesian coordinates of each atom inside the molecule.\n", + "coords = [ [ 0.0, 0.0, 0.0 ], [ 0.0, 0.0, 0.7 ] ]\n", + "\n", + "# This field specifies configurations related to the quantum circuit in VQE is specified.\n", + "[ansatz.HardwareEfficient]\n", + "# The depth of the HardwareEfficient ansatz.\n", + "depth = 2\n", + "\n", + "# This field stores configurations of the variational quantum eigensolver (VQE) method. \n", + "[VQE]\n", + "# Number of optimization cycles, default is 100.\n", + "num_iterations = 100\n", + "```\n", + "After setup the configuration file, user can run the following command to perform the quantum chemistry task.\n", + "```shell\n", + "python energy_material.py --config example.toml\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Reference \n", + "\\[1\\] [Thermaldynamic free energy](https://en.wikipedia.org/wiki/Thermodynamic_free_energy)\n", + "\n", + "\\[2\\] Aydinol, M. K., et al. \"Ab initio study of lithium intercalation in metal oxides and metal dichalcogenides.\" Physical Review B 56.3 (1997): 1354.\n", + "\n", + "\\[3\\] Kandala, Abhinav, et al. \"Hardware-efficient variational quantum eigensolver for small molecules and quantum magnets.\" Nature 549.7671 (2017): 242-246." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pq-dev", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.15 (default, Nov 10 2022, 13:17:42) \n[Clang 14.0.6 ]" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "5fea01cac43c34394d065c23bb8c1e536fdb97a765a18633fd0c4eb359001810" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/applications/medical_image_classification/example.toml b/applications/medical_image_classification/example.toml new file mode 100644 index 0000000..5738e61 --- /dev/null +++ b/applications/medical_image_classification/example.toml @@ -0,0 +1,7 @@ +task = 'test' +image_path = 'pneumoniamnist' +num_samples = 10 +model_path = 'qnnmic.pdparams' +num_qubits = [8, 8] +num_depths = [2, 2] +observables = [['Z0', 'Z1', 'Z2', 'Z3'], ['X0', 'X1', 'X2', 'X3']] diff --git a/applications/medical_image_classification/introduction_cn.ipynb b/applications/medical_image_classification/introduction_cn.ipynb new file mode 100644 index 0000000..8469007 --- /dev/null +++ b/applications/medical_image_classification/introduction_cn.ipynb @@ -0,0 +1,213 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 医学图像分类简介\n", + "\n", + "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*\n", + "\n", + "医学图像分类(Medical image classification)是计算机辅助诊断系统的关键技术。医学图像分类问题主要是如何从图像中提取特征并进行分类,从而识别和了解人体的哪些部位受到特定疾病的影响。在这里我们主要使用量子神经网络对公开数据集 MedMNIST 中的胸腔数据进行分类。其中数据形式如下图所示:\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 使用 QNNMIC 模型进行医学图像分类\n", + "\n", + "### QNNMIC 模型简介\n", + "QNNMIC 模型是一个可以用于医学图像分类的量子机器学习模型(Quantum Machine Learning,QML)。我们具体称其为一种量子神经网络 (Quantum Neural Network, QNN),它结合了参数化量子电路(Parameterized Quantum Circuit, PQC)和经典神经网络。对于医学图像数据,QNNMIC 可以达到 85% 以上的分类准确率。模型主要分为量子和经典两部分,结构图如下:\n", + "\n", + "\n", + "\n", + "\n", + "注:\n", + "- 通常我们使用主成分分析将图片数据进行降维处理,使其更容易通过编码电路将经典数据编码为量子态。\n", + "- 参数化电路的作用是特征提取,其电路参数可以在训练中调整。\n", + "- 量子测量由一组测量算子表示,是将量子态转化为经典数据的过程,我们可以对得到的经典数据做进一步处理。\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 如何使用\n", + "\n", + "### 使用模型进行预测\n", + "\n", + "这里,我们已经给出了一个训练好的模型,可以直接用于医学图片的预测。只需要在 `test.toml` 这个配置文件中进行对应的配置,然后输入命令 `python qnn_medical_image.py --config test.toml` 即可使用训练好的医学图片分类模型对输入的图片进行测试。\n", + "\n", + "### 在线演示\n", + "\n", + "这里,我们给出一个在线演示的版本,可以在线进行测试。首先定义配置文件的内容对测试集中图片进行预测:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import toml\n", + "\n", + "test_toml = r\"\"\"\n", + "# 模型的整体配置文件。\n", + "# 图片的文件路径。\n", + "image_path = 'pneumoniamnist'\n", + "\n", + "# 训练集中的数据个数,默认值为 -1 即使用全部数据。\n", + "num_samples = 20\n", + "\n", + "# 训练好的模型参数文件的文件路径。\n", + "model_path = 'qnnmic.pdparams'\n", + "\n", + "# 量子电路所包含的量子比特的数量。\n", + "num_qubits = [8, 8]\n", + "\n", + "# 每一层量子电路中的电路深度。\n", + "num_depths = [2, 2]\n", + "\n", + "# 量子电路中可观测量的设置。\n", + "observables = [['Z0', 'Z1', 'Z2', 'Z3'], ['X0', 'X1', 'X2', 'X3']]\n", + "\"\"\"\n", + "\n", + "config = toml.loads(test_toml)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "图片的预测结果分别为 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0\n", + "图片的实际标签分别为 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0\n" + ] + } + ], + "source": [ + "from paddle_quantum.qml.qnnmic import inference\n", + "\n", + "prediction, prob, label = inference(**config)\n", + "print(f\"图片的预测结果分别为 {str(prediction)[1:-1]}\")\n", + "print(f\"图片的实际标签分别为 {str(label)[1:-1]}\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "其中标签 0 代表肺部异常,标签 1 代表正常。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "在 `test_toml` 配置文件中:\n", + "- `model_path`: 为训练好的模型,这里固定为 `qnnmic.pdparams`;\n", + "- `num_qubits`、`num_depths`、`observables` 三个参数应与训练好的模型 ``qnnmic.pdparams`` 相匹配。`num_qubits = [8,8]` 表示量子部分一共两层电路;每层电路为 8 的量子比特;`num_depths = [2,2]` 表示每层参数化电路深度为 2;`observables` 表示每层测量算子的具体形式。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "对于数据集中的某张肺部异常的图片:\n", + "\n", + "" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "对于上述输入的图片,模型有 98.30% 的置信度检测出肺部异常。\n" + ] + } + ], + "source": [ + "# 使用模型进行预测并得到对应概率值\n", + "msg = f'对于上述输入的图片,模型有 {prob[10][1]:3.2%} 的置信度检测出肺部异常。'\n", + "print(msg)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 注意事项\n", + "\n", + "我们通常考虑调整 `num_qubits`,`num_depths`,`observables` 三个主要超参数,对模型的影响较大。" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 引用信息\n", + "\n", + "```\n", + "@article{medmnistv2,\n", + " title={MedMNIST v2: A Large-Scale Lightweight Benchmark for 2D and 3D Biomedical Image Classification},\n", + " author={Yang, Jiancheng and Shi, Rui and Wei, Donglai and Liu, Zequan and Zhao, Lin and Ke, Bilian and Pfister, Hanspeter and Ni, Bingbing},\n", + " journal={arXiv preprint arXiv:2110.14795},\n", + " year={2021}\n", + "}\n", + "```\n", + "\n", + "## 参考文献\n", + "\\[1\\] Yang, Jiancheng, et al. \"Medmnist v2: A large-scale lightweight benchmark for 2d and 3d biomedical image classification.\" arXiv preprint arXiv:2110.14795 (2021)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "modellib", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.15 (default, Nov 24 2022, 18:44:54) [MSC v.1916 64 bit (AMD64)]" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "dfa0523b1e359b8fd3ea126fa0459d0c86d49956d91b464930b80cba21582eac" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/applications/medical_image_classification/introduction_en.ipynb b/applications/medical_image_classification/introduction_en.ipynb new file mode 100644 index 0000000..63617b7 --- /dev/null +++ b/applications/medical_image_classification/introduction_en.ipynb @@ -0,0 +1,210 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Medical image classification\n", + "\n", + "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*\n", + "\n", + "Medical image classification is the key technology of computer-aided diagnosis systems. The problem of medical image classification is how to extract features from images and classify them, so as to identify and understand which parts of the human body are affected by specific diseases. Here, we mainly use a quantum neural network to classify the chest data in the open data set MedMNIST, which has the following form\n", + "\n", + "\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## QNNMIC model for medical image classification\n", + "\n", + "### Introduction of QNNMIC\n", + "QNNMIC model is a quantum machine learning (QML) model that can be used for medical image classification. We specifically call it a quantum neural network (QNN), which combines parameterized quantum circuit (PQC) and a classical neural network. For medical image data, QNNMIC can achieve more than 85% classification accuracy. The model is mainly divided into quantum and classical parts. The structure diagram is as follows:\n", + "\n", + "\n", + "\n", + "\n", + "Remarks:\n", + "- In general, we use principal component analysis (PCA) to reduce the dimension of the image data, making it easier to encode classical data into quantum states through coding circuits.\n", + "- The parameterized circuit is used for feature extraction, and its circuit parameters can be adjusted during training.\n", + "- Quantum measurement, represented by a set of measurement operators, is the process of converting quantum states into classical data, which can be further processed.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Quick start\n", + "\n", + "### Use the model to make predictions\n", + "\n", + "Here, we have given a trained model saved in the format `qnnmic.pdparams` which can be directly used to distinguish medical images. One only needs to do the corresponding configuration in this file `test.toml`, and enter the command `python qnn_medical_image.py --config test.toml` to predict the input images.\n", + "\n", + "### Online Test\n", + "\n", + "The following shows how to configure the test file `test_toml` to make medical image prediction." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "import toml\n", + "\n", + "test_toml = r\"\"\"\n", + "# The config for testing the QNNMIC model.\n", + "\n", + "# The path of the input image.\n", + "image_path = 'pneumoniamnist'\n", + "\n", + "# The number of data in the test dataset.\n", + "# The value defaults to -1 which means using all data.\n", + "num_samples = 20\n", + "\n", + "# The path of the trained model, which will be loaded.\n", + "model_path = 'qnnmic.pdparams'\n", + "\n", + "# The number of qubits of the quantum circuit in each layer.\n", + "num_qubits = [8, 8]\n", + "\n", + "# The depth of the quantum circuit in each layer.\n", + "num_depths = [2, 2]\n", + "\n", + "# The observables of the quantum circuit in each layer.\n", + "observables = [['Z0','Z1','Z2','Z3'], ['X0','X1','X2','X3']]\n", + "\"\"\"\n", + "\n", + "config = toml.loads(test_toml)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The prediction results of the input images are 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0 respectively.\n", + "The labels of the input images are 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0 respectively.\n" + ] + } + ], + "source": [ + "from paddle_quantum.qml.qnnmic import inference\n", + "\n", + "prediction, prob, label = inference(**config)\n", + "print(f\"The prediction results of the input images are {str(prediction)[1:-1]} respectively.\")\n", + "print(f\"The labels of the input images are {str(label)[1:-1]} respectively.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here label 0 means abnormal lungs, and label 1 means normal lungs." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In `test_toml` :\n", + "- `model_path`: this is the trained model, here we set it as `qnnmic.pdparams`.\n", + "- `num_qubits`, `num_depths`, `observables` these parameters correspond to the model ``qnnmic.pdparams``, `num_qubits = [8,8]` represents the quantum part of a total of two layers of circuit, each layer of the circuit has 8 qubits; `num_depths = [2,2]` represents the depth of parameterized circuit of each layer is 2;`observables` is the specific form of the measurement operator at each layer.\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For an abnormal image from the dataset\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "For this input image, the model can detect the abnormality of the lung with 98.30% confidence.\n" + ] + } + ], + "source": [ + "# Use the model to make predictions and get the corresponding probability\n", + "msg = f'For this input image, the model can detect the abnormality of the lung with {prob[10][1]:3.2%} confidence.'\n", + "print(msg)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Remarks\n", + "\n", + "- We usually consider adjusting three hyperparameters,`num_qubits`, `num_depths` and `observables`, which have a greater impact on the model." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Reference information\n", + "\n", + "```\n", + "@article{medmnistv2,\n", + " title={MedMNIST v2: A Large-Scale Lightweight Benchmark for 2D and 3D Biomedical Image Classification},\n", + " author={Yang, Jiancheng and Shi, Rui and Wei, Donglai and Liu, Zequan and Zhao, Lin and Ke, Bilian and Pfister, Hanspeter and Ni, Bingbing},\n", + " journal={arXiv preprint arXiv:2110.14795},\n", + " year={2021}\n", + "}\n", + "```\n", + "\n", + "## Reference\n", + "\n", + "\\[1\\] Yang, Jiancheng, et al. \"Medmnist v2: A large-scale lightweight benchmark for 2d and 3d biomedical image classification.\" arXiv preprint arXiv:2110.14795 (2021)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "modellib", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.15 (default, Nov 24 2022, 18:44:54) [MSC v.1916 64 bit (AMD64)]" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "dfa0523b1e359b8fd3ea126fa0459d0c86d49956d91b464930b80cba21582eac" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/applications/medical_image_classification/med_image_example.png b/applications/medical_image_classification/med_image_example.png new file mode 100644 index 0000000..2345073 Binary files /dev/null and b/applications/medical_image_classification/med_image_example.png differ diff --git a/applications/medical_image_classification/medical_image/breastmnist.npz b/applications/medical_image_classification/medical_image/breastmnist.npz new file mode 100644 index 0000000..c66690f Binary files /dev/null and b/applications/medical_image_classification/medical_image/breastmnist.npz differ diff --git a/applications/medical_image_classification/medical_image/get_test_image.ipynb b/applications/medical_image_classification/medical_image/get_test_image.ipynb new file mode 100644 index 0000000..b859d78 --- /dev/null +++ b/applications/medical_image_classification/medical_image/get_test_image.ipynb @@ -0,0 +1,50 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from PIL import Image\n", + "image = np.load('pneumoniamnist.npz')\n", + "x_train = image['train_images']\n", + "x_label = image['train_labels']\n", + "\n", + "im1 = Image.fromarray(x_train[28])\n", + "im0 = Image.fromarray(x_train[-20])\n", + "\n", + "im1.save('image_label_1.png')\n", + "im0.save('image_label_0.png')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "modellib", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.15" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "dfa0523b1e359b8fd3ea126fa0459d0c86d49956d91b464930b80cba21582eac" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/applications/medical_image_classification/medical_image/image_label_0.png b/applications/medical_image_classification/medical_image/image_label_0.png new file mode 100644 index 0000000..10f6a66 Binary files /dev/null and b/applications/medical_image_classification/medical_image/image_label_0.png differ diff --git a/applications/medical_image_classification/medical_image/image_label_1.png b/applications/medical_image_classification/medical_image/image_label_1.png new file mode 100644 index 0000000..d5ae206 Binary files /dev/null and b/applications/medical_image_classification/medical_image/image_label_1.png differ diff --git a/applications/medical_image_classification/medical_image/pneumoniamnist.npz b/applications/medical_image_classification/medical_image/pneumoniamnist.npz new file mode 100644 index 0000000..c49d2d5 Binary files /dev/null and b/applications/medical_image_classification/medical_image/pneumoniamnist.npz differ diff --git a/applications/medical_image_classification/medical_image/test_image_1_0.npz b/applications/medical_image_classification/medical_image/test_image_1_0.npz new file mode 100644 index 0000000..bc2f20f Binary files /dev/null and b/applications/medical_image_classification/medical_image/test_image_1_0.npz differ diff --git a/applications/medical_image_classification/qnn_medical_image.py b/applications/medical_image_classification/qnn_medical_image.py new file mode 100644 index 0000000..b7521e7 --- /dev/null +++ b/applications/medical_image_classification/qnn_medical_image.py @@ -0,0 +1,40 @@ +# !/usr/bin/env python3 +# Copyright (c) 2020 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import os +import warnings + +warnings.filterwarnings('ignore') +os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python' + +import toml +from paddle_quantum.qml.qnnmic import train, inference + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Detect whether there are cracks on the surface of images by the QNNMIC model.") + parser.add_argument("--config", type=str, help="Input the config file with toml format.") + args = parser.parse_args() + config = toml.load(args.config) + task = config.pop('task') + + if task == 'train': + train(**config) + elif task == 'test': + prediction, prob, label = inference(**config) + print(f"The prediction results of the input pictures are {str(prediction)[1:-1]} respectively.") + else: + raise ValueError("Unknown task, it can be train or test.") diff --git a/applications/medical_image_classification/qnnmic.pdparams b/applications/medical_image_classification/qnnmic.pdparams new file mode 100644 index 0000000..eab7079 Binary files /dev/null and b/applications/medical_image_classification/qnnmic.pdparams differ diff --git a/applications/medical_image_classification/qnnmic_model_cn.png b/applications/medical_image_classification/qnnmic_model_cn.png new file mode 100644 index 0000000..52636f3 Binary files /dev/null and b/applications/medical_image_classification/qnnmic_model_cn.png differ diff --git a/applications/medical_image_classification/qnnmic_model_en.png b/applications/medical_image_classification/qnnmic_model_en.png new file mode 100644 index 0000000..955d02f Binary files /dev/null and b/applications/medical_image_classification/qnnmic_model_en.png differ diff --git a/applications/medical_image_classification/test.toml b/applications/medical_image_classification/test.toml new file mode 100644 index 0000000..f930454 --- /dev/null +++ b/applications/medical_image_classification/test.toml @@ -0,0 +1,22 @@ +# The config for testing the QNNMIC model. +# The task of this config. Available values: 'train' | 'test'. +task = 'test' + +# The path of the input image. +image_path = 'pneumoniamnist' + +# The number of the data in the test dataset. +# The value defaults to -1 which means using all data. +num_samples = 10 + +# The path of the trained model, which will be loaded. +model_path = 'qnnmic.pdparams' + +# The number of qubits of quantum circuit in each layer. +num_qubits = [8, 8] + +# The depth of quantum circuit in each layer. +num_depths = [2, 2] + +# The observables of quantum circuit in each layer. +observables = [['Z0', 'Z1', 'Z2', 'Z3'], ['X0', 'X1', 'X2', 'X3']] \ No newline at end of file diff --git a/applications/medical_image_classification/train.toml b/applications/medical_image_classification/train.toml new file mode 100644 index 0000000..48594b5 --- /dev/null +++ b/applications/medical_image_classification/train.toml @@ -0,0 +1,57 @@ +# The config for training the QNNMIC model. +# The task of this config. Available values: 'train' | 'test'. +task = 'train' + +# The name of the model, which is used to save the model. +model_name = 'qnnmic' + +# The path to save the model. Both relative and absolute paths are allowed. +# It saves the model to the current path by default. +# saved_path = './' + +# The number of qubits of the quantum circuit in each layer. +num_qubits = [8, 8] + +# # The depth of the quantum circuit in each layer. +num_depths = [2, 2] + +# The observables of the quantum circuit in each layer. +observables = [['Z0', 'Z1', 'Z2', 'Z3'], ['X0', 'X1', 'X2', 'X3']] + +# The size of the batch samplers. +batch_size = 40 + +# The number of epochs to train the model. +num_epochs = 20 + +# The learning rate used to update the parameters, default to 0.01. +learning_rate = 0.1 + +# The path of the dataset. It defaults to breastmnist. +dataset = 'pneumoniamnist' + +# The path used to save logs. Default to ``./``. +saved_dir = './' + +# Whether use the validation. +# It is true means the dataset contains training, validation and test datasets. +# It is false means the dataset only contains training datasets and test datasets. +using_validation = true + +# The number of the data in the training dataset. +# The value defaults to -1 which means using all data. +num_train = 500 + +# The number of the data in the validation dataset. +# The value defaults to -1 which means using all data. +num_val = -1 + +# The number of the data in the test dataset. +# The value defaults to -1 which means using all data. +num_test = -1 + +# Number of epochs with no improvement after which training will be stopped. +early_stopping = 1000 + +# The number of subprocess to load data, 0 for no subprocess used and loading data in main process, default to 0. +num_workers = 0 diff --git a/applications/option_pricing/config.toml b/applications/option_pricing/config.toml new file mode 100644 index 0000000..51a2b25 --- /dev/null +++ b/applications/option_pricing/config.toml @@ -0,0 +1,13 @@ +# The overall profile used to calculate the option pricing model +# initial price +initial_price = 100 +# strike price +strike_price = 110 +# risk-free rate +interest_rate = 0.05 +# Market volatility +volatility = 0.1 +# Option expiration date (in years) +maturity_date = 1 +# Estimation accuracy index +degree_of_estimation = 5 diff --git a/applications/option_pricing/euro_pricing.py b/applications/option_pricing/euro_pricing.py new file mode 100644 index 0000000..dcad115 --- /dev/null +++ b/applications/option_pricing/euro_pricing.py @@ -0,0 +1,50 @@ +# !/usr/bin/env python3 +# Copyright (c) 2020 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import os +import warnings +from typing import Dict + +warnings.filterwarnings('ignore') +os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python' + +import toml +from paddle_quantum.finance import EuroOptionEstimator + + +def main(args): + parsed_configs: Dict = toml.load(args.config) + + # input option settings + initial_price = parsed_configs["initial_price"] + strike_price = parsed_configs["strike_price"] + interest_rate = parsed_configs["interest_rate"] + volatility = parsed_configs["volatility"] + maturity_date = parsed_configs["maturity_date"] + degree_of_estimation = parsed_configs["degree_of_estimation"] + + estimator = EuroOptionEstimator(initial_price, strike_price, + interest_rate, volatility, + maturity_date, degree_of_estimation) + print("The risk-neutral price of this option is", estimator.estimate()) + print("Below is the circuit realization of this quantum solution.") + estimator.plot() + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="European option pricing with paddle quantum.") + parser.add_argument("--config", type=str, help="Input the config file with toml format.") + main(parser.parse_args()) diff --git a/applications/option_pricing/introduction_cn.ipynb b/applications/option_pricing/introduction_cn.ipynb new file mode 100644 index 0000000..0f41968 --- /dev/null +++ b/applications/option_pricing/introduction_cn.ipynb @@ -0,0 +1,205 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 期权定价问题\n", + "\n", + "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "期权定价的任务是评估当前股票期权在到期日的预期价值。决定期权价值的因素有很多,包括当前股票价格、内在价值、到期时间或时间价值、波动性、利率和支付的现金股息。许多期权定价模型以这些因素作为模型的参数,用来确定期权的公允市场价值,其中最广为人知的是布莱克-斯科尔斯模型(Black-Scholes model)。布莱克-斯科尔斯模型可以通过使用少量输入参数的简单且可分析求解的模型对各种金融衍生品进行定价。该模型认为,大量交易资产的价格遵循几何布朗运动,具有恒定的漂移和波动。当应用于股票期权时,该模型包含股票的恒定价格变化、货币的时间价值、期权的执行价格以及期权到期的时间。特别地,股票或期货合约等工具在随机游走后将具有服从对数正态分布的价格,具有恒定的漂移和波动性。\n", + "\n", + "欧式期权定价是布莱克-斯科尔斯模型的一个简单应用,该模型可以用来计算欧式看涨期权的价格。欧式看涨期权赋予期权持有人在到期日 $T$ 以预先商定的价格 $K$ 购买股票的权利。通常,期权收益函数为 \n", + "$$\n", + "f(S_{T})=\\max\\{0, S_{T}-K\\}, \\tag{1}\n", + "$$\n", + "其中 $S_{T}$ 表示期权到期日的资产价格(asset price),$K$ 表示预先商定的价格(strike price)。这里,资产价格 $S_{T}$ 在布莱克-斯科尔斯模型下服从一个已知的概率分布,且依赖于初始价格(initial price),无风险利率(risk-free interest rate),波动性(volatility)以及期权到期日(maturity date) $T$。在经典计算中,期权股票到期时的期望价值可以通过蒙特卡罗方法计算:从已知的(风险中性)概率分布中获取市场样本,然后计算给定该市场样本的资产价格。之后,计算给定资产价格的期权收益,最后,对多个样本的收益求平均值,得出期权价格的近似值。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 量子解决方案\n", + "\n", + "不同于经典算法,量子蒙特卡洛(quantum Monte Carlo)方法的核心是用量子电路模拟概率分布并将资产价格储存在量子态中,进而通过量子振幅估计算法并行计算收益的平均值。通过量子叠加和纠缠的特性,量子方案与经典方案相比具有二次加速的优势。接下来我们以欧式看涨期权为例,展示如何使用量桨来模拟该量子方案,从而完成欧式看涨期权的风险中性定价问题。" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 在线演示" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "我们已经给出了一个设置好的参数,可以直接用于欧式看涨期权的定价。只需要在 `config.toml` 这个配置文件中进行对应的配置,然后输入命令 \n", + "`python euro_pricing.py --config config.toml`\n", + "即可对配置好的欧式期权进行定价。\n", + "\n", + "这里,我们给出一个在线演示的版本,可以在线进行测试。首先定义配置文件的内容:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "euro_toml = r\"\"\"\n", + "# 用于计算期权定价模型的整体配置文件。\n", + "# 初始价格。\n", + "initial_price = 100\n", + "# 既定价格。\n", + "strike_price = 110\n", + "# 无风险利率。\n", + "interest_rate = 0.05\n", + "# 市场波动性。\n", + "volatility = 0.1\n", + "# 期权到期日(以年为单位)。\n", + "maturity_date = 1\n", + "# 估计精度指数。\n", + "degree_of_estimation = 5\n", + "\"\"\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "量桨的金融模块实现了量子蒙特卡洛方案的数值模拟。我们可以从 ``paddle_quantum.finance`` 模块里导入 ``EuroOptionEstimator`` 来解决配置好的期权定价问题。" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "该期权的风险中性价格大约为 2.2613329887390137\n", + "以下是该量子方案的电路实现图。\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABDwAAAUbCAYAAADCtLDZAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8qNh9FAAAACXBIWXMAAB7CAAAewgFu0HU+AABV1klEQVR4nO3dfXhdBZ3g8V+alLbhtUVpMk0QphalSIW2E17UEVcdQFjfEGEQR5ARBceCiwq+oLgL6IC8uOsgLo6iuIoi0hVYWBTQnXHoEwQqDTDFVkeTeqMUqA2UIIGzf2hiC7S5ac+9N/nl83kenyeTnnv64/6a9t7v3HNvU1EURQAAAAAkMqXRAwAAAACUTfAAAAAA0hE8AAAAgHQEDwAAACAdwQMAAABIR/AAAAAA0hE8AAAAgHQEDwAAACAdwQMAAABIR/AAAAAA0hE8AAAAgHQEDwAAACAdwQMAAABIR/AAAAAA0hE8AAAAgHQEDwAAACAdwQMAAABIR/AAAAAA0hE8AAAAgHQEDwAAACAdwQMAAABIR/AAAAAA0hE8AAAAgHQEDwAAACAdwQMAAABIR/AAAAAA0hE8AAAAgHQEDwAAACAdwQMAAABIR/AAAAAA0mlp9AC1MjQ0FP39/Y0eo1RtbW3R0pJ2ZQAAABGR8/nceJX5eWbO/6qI6O/vj87OzkaPUare3t7o6Oho9BgAAAA1lfH53HiV+XmmS1oAAACAdNK+wmNj3d3d0d7e3ugxtkqlUomurq5GjwEAANAQE/n53Hg1WZ5nTorg0d7envYlOgAAAJl5PsfWckkLAAAAkI7gAQAAAKQjeAAAAADpCB4AAABAOoIHAAAAkI7gAQAAAKQjeAAAAADpCB4AAABAOoIHAAAAkI7gAQAAAKQjeAAAAADpCB4AAABAOoIHAAAAkI7gAQAAAKQjeAAAAADpCB4AAABAOoIHAAAAkI7gAQAAAKQjeAAAAADpCB4AAABAOoIHAAAAkI7gAQAAAKQjeAAAAADpCB4AAABAOoLHGB1//PHR1NQU06dPj6GhoVGPX7JkSTQ1NUVzc3Ns2LChDhMCAAAAgscYLV++PCIi9t5772hpaRn1+BUrVkRExLx586K1tbWWowEAAAB/IniMweDgYKxcuTIiIvbdd9+qbtPT0xMREfvtt1+txgIAAACeRfAYg56enpHLWKoJHv39/bF27dqIEDwAAACgngSPMRi+nCWiuuAxfDlLhOABAAAA9TT6m1AwQvAAAADK8PTTT8eKFSvikUceidbW1njZy14WO+ywQ6PHglQEjzEYDh4zZ86MOXPmjHr88Pt3zJ49O9ra2mo5GgAAMAGsX78+Lr/88rj88svjl7/85cj3d9xxx3jXu94VS5YsiXnz5jVwQsjDJS1VKooi7r333oio/g1Lh1/h4dUdAABAX19fHHTQQXHmmWduEjsiIgYGBuILX/hCLFy4MG655ZYGTQi5eIVHlVavXh0DAwMRETFr1qxYtmzZqLe5//77I0LwAACAyW5gYCAOO+ywkecIm/PYY4/Fm9/85viXf/mXWLRoUZ2mg5wEjypt/P4dS5cujaVLl1Z9W8EDAAAmt6985Stx3333VXXsE088EZ/4xCfipptuqvFUkJtLWqq0cfAYK8EDAAAmr2eeeSYuu+yyMd3m5ptvjtWrV9doIpgcSn+FR19fX9mn3CqVSqXU8w0Hj6lTp8bAwEBMmzZti8efccYZcfHFF0dra2vstddepcxQ9n8TAABQe6tWrYoHH3xwzLf7xje+ESeddFINJhr/PPepn/F0X3d0dJR6vtKDR2dnZ9mnHBeGg8c+++wzauyIiLjrrrsi4o9vcDplSjkvpOnq6irlPAAAwPh3zjnnxDnnnNPoMUhuPD3PLIqi1PO5pKUKa9eujTVr1kRExMKFC0c9viiKuOeeeyLC5SwAAADQCKW/wqO3t7fsU26VSqVSWqna+P07qnmn5FWrVsX69esjotzg0d3dHe3t7aWdDwAAqL0nn3wyDjzwwFi7du2Ybvf9738/9t9//xpNNb6V+XyOLcv8PLP04FH2NTfjwViDx/DlLBHlBo/29vaU9y8AAGR38sknx/nnn1/18YsWLYojjzwympqaajgV5H6e6ZKWKgwHj+bm5liwYMGoxw8HjylTplR1PAAAkNupp54au+66a9XHn3322WIHbCPBowrDwWP+/PkxY8aMUY+/++67IyJi3rx50draWsvRAACACWDOnDlxww03xC677DLqsZdeemm86U1vqv1QkJzgMYrBwcFYuXJlRFR3OUvEn4OHNywFAACGHXjggbFs2bI45phjoqXlue8ucPDBB8f1118fp512WgOmg3wEj1H09PTE0NBQRFT3CS2rV6+OdevWRYTgAQAAbOolL3lJXH311dHb2xsXXnjhyPdvvvnm+MlPfhJHHnlkA6eDXEp/09JsFi9ePKbPAp47d27pnx0MAADk0tbWFscee2x8+MMfjoiIffbZp8ETQT5e4QEAAACkI3gAAAAA6QgeAAAAQDqCBwAAAJCO4AEAAACkI3gAAAAA6QgeAAAAQDqCBwAAAJCO4AEAAACkI3gAAAAA6QgeAAAAQDqCBwAAAJCO4AEAAACkI3gAAAAA6QgeAAAAQDqCBwAAAJCO4AEAAACkI3gAAAAA6QgeAAAAQDqCBwAAAJCO4AEAAACkI3gAAAAA6QgeAAAAQDqCBwAAAJCO4AEAAACkI3gAAAAA6QgeAAAAQDqCBwAAAJCO4AEAAACk09LoAeqhUqk0eoStNpFnBwAAgEaZFMGjq6ur0SMAAAAAdeSSFgAAACCdtK/waGtri97e3kaPUaq2trZGjwAAAAATQtrg0dLSEh0dHY0eAwAAAGgAl7QAAAAA6QgeAAAAQDqCBwAAAJCO4AEAAACkI3gAAAAA6QgeAAAAQDqCBwAAAJCO4AEAAACkI3gAAAAA6QgeAAAAQDqCBwAAAJCO4AEAAACkI3gAAAAA6QgeAAAAQDqCBwAAAJCO4AEAAACkI3gAAAAA6QgeAAAAQDqCBwAAAJCO4AEAAACkI3gAAAAA6QgeAAAAQDotjR6gVoaGhqK/v7/RY5Sqra0tWlrSrgwAAABKk/bZc39/f3R2djZ6jFL19vZGR0dHo8cAAACAcc8lLQAAAEA6aV/hsbHu7u5ob29v9BhbpVKpRFdXV6PHAAAAgAllUgSP9vZ2l4IAAADAJOKSFgAAACAdwQMAAABIR/AAAAAA0hE8AAAAgHQEDwAAACAdwQMAAABIR/AAAAAA0hE8AAAAgHQEDwAAACAdwQMAAABIR/AAAAAA0hE8AAAAgHQEDwAAACAdwQMAAABIR/AAAAAA0hE8AAAAgHQEDwAAACAdwQMAAABIR/AAAAAA0hE8AAAAgHQEDwAAACAdwQMAAABIR/AAAAAA0hE8xuj444+PpqammD59egwNDY16/JIlS6KpqSmam5tjw4YNdZgQAAAAEDzGaPny5RERsffee0dLS8uox69YsSIiIubNmxetra21HA0AAAD4E8FjDAYHB2PlypUREbHvvvtWdZuenp6IiNhvv/1qNRYAAADwLILHGPT09IxcxlJN8Ojv74+1a9dGhOABAAAA9SR4jMHw5SwR1QWP4ctZIgQPAAAAqCfBYwwEDwAAAJgYRn/XTUYMB4+ZM2fGnDlzRj1++P07Zs+eHW1tbbUcDQCA5AYHB+O73/1u3H777fH444/HTjvtFEcccUQcccQRVb2ZPsBk42/GKhVFEffee29EVP+GpcOv8PDqDgAAtlZRFPH5z38+zjvvvJH3hxt2xRVXRGdnZ3z2s5+N4447rkETAoxPgkeVVq9eHQMDAxERMWvWrFi2bNmot7n//vsjQvAAAGDrfehDH4qLL754s7/e29sb73jHO2Lt2rWxZMmSOk4GML4JHlXa+P07li5dGkuXLq36toIHAABb45vf/OYWY8fGTj/99Nh///3jVa96VY2nApgYvGlplTYOHmMleAAAMFZFUcTnPve5MR1/6aWX1m4ggAmm9Fd49PX1lX3KrVKpVEo933DwmDp1agwMDMS0adO2ePwZZ5wRF198cbS2tsZee+1Vygxl/zcBADB+3XPPPXHPPfeM6Tb/+3//7/jpT3/qDfMniI0f33usvyn3R/2Mp/u6o6Oj1POVHjw6OzvLPuW4MBw89tlnn1FjR0TEXXfdFRF/fIPTKVPKeSFNV1dXKecBACCnp59+Ov7qr/6q0WOwFTzWp1HG05+9oihKPZ9LWqqwdu3aWLNmTURELFy4cNTji6IYqfEuZwEAAID6K/0VHr29vWWfcqtUKpXSStXG79+xaNGiUY9ftWpVrF+/PiLKDR7d3d3R3t5e2vkAABi/brrppjj55JPHfLsbb7wxFixYUIOJKNvGz1k81t9Umc/n2LLMf/ZKDx5lX3MzHow1eAxfzhJRbvBob29Pef8CAPBc73jHO+LMM8+MRx99tOrbvPSlL43DDz88mpqaajgZteCxPo2S+c+eS1qqMBw8mpubq6rlw8FjypQp6joAAFultbU1TjzxxDHd5tRTTxU7AP5E8KjCcPCYP39+zJgxY9Tj77777oiImDdvXrS2ttZyNAAAEvv4xz8eL3nJS6o69pWvfGW85z3vqfFEABOH4DGKwcHBWLlyZURUdzlLxJ+DhzcsBQBgW8yaNStuvfXWePnLX77F4/7Tf/pPccMNN8T06dPrNBnA+Cd4jKKnpyeGhoYiorpPaFm9enWsW7cuIgQPAAC23Zw5c6K7uzu+/e1vxwEHHDDy/SlTpsQRRxwRN954Y9xyyy2x8847N3BKgPGn9DctzWbx4sVj+izguXPnlv7ZwQAATG7bbbddvP3tb4+DDz44Ojs7IyLiF7/4RbzoRS9q8GQA45dXeAAAwATU3Nzc6BEAxjXBAwAAAEhH8AAAAADSETwAAACAdAQPAAAAIB3BAwAAAEhH8AAAAADSETwAAACAdAQPAAAAIB3BAwAAAEhH8AAAAADSETwAAACAdAQPAAAAIB3BAwAAAEhH8AAAAADSETwAAACAdAQPAAAAIB3BAwAAAEhH8AAAAADSETwAAACAdAQPAAAAIB3BAwAAAEhH8AAAAADSETwAAACAdAQPAAAAIB3BAwAAAEhH8AAAAADSETwAAACAdAQPAAAAIB3BAwAAAEinpdED1EOlUmn0CFttIs8OAAAAjTIpgkdXV1ejRwAAAADqyCUtAAAAQDppX+HR1tYWvb29jR6jVG1tbY0eAQAAACaEtMGjpaUlOjo6Gj0GAAAA0AAuaQEAAADSETwAAACAdAQPAAAAIB3BAwAAAEhH8AAAAADSETwAAACAdAQPAAAAIB3BAwAAAEhH8AAAAADSETwAAACAdAQPAAAAIB3BAwAAAEhH8AAAAADSETwAAACAdAQPAAAAIB3BAwAAAEhH8AAAAADSETwAAACAdAQPAAAAIB3BAwAAAEhH8AAAAADSaWn0ALUyNDQU/f39jR6jVG1tbdHSknZlAAAAUJq0z577+/ujs7Oz0WOUqre3Nzo6Oho9BgAAAIx7LmkBAAAA0kn7Co+NdXd3R3t7e6PH2CqVSiW6uroaPQYAAABMKJMieLS3t7sUBAAAACYRl7QAAAAA6QgeAAAAQDqCBwAAAJCO4AEAAACkI3gAAAAA6QgeAAAAQDqCBwAAAJCO4AEAAACkI3gAAAAA6QgeAAAAQDqCBwAAAJCO4AEAAACkI3gAAAAA6QgeAAAAQDqCBwAAAJCO4AEAAACkI3gAAAAA6QgeAAAAQDqCBwAAAJCO4AEAAACkI3gAAAAA6QgeAAAAQDqCBwAAAJCO4AEAAACkI3iM0fHHHx9NTU0xffr0GBoaGvX4JUuWRFNTUzQ3N8eGDRvqMCEAAAAgeIzR8uXLIyJi7733jpaWllGPX7FiRUREzJs3L1pbW2s5GgAAAPAngscYDA4OxsqVKyMiYt99963qNj09PRERsd9++9VqLAAAAOBZBI8x6OnpGbmMpZrg0d/fH2vXro0IwQMAAADqSfAYg+HLWSKqCx7Dl7NECB4AAABQT4LHGAgeAMBE9eSTT8b69evjmWeeafQoAFAXgscYDAePmTNnxpw5c0Y9fvj9O2bPnh1tbW21HA0A4Dl+//vfx//4H/8j5s+fH9OnT4+dd945tt9++zjhhBPizjvvbPR4AFBTgkeViqKIe++9NyKqf8PS4Vd4eHUHAFBv3d3dMW/evFiyZEk88MADI98fHByMr33ta9HV1RWnnnrqyPuTAUA2o3+uKhERsXr16hgYGIiIiFmzZsWyZctGvc39998fEYIHAFBfPT098frXvz7Wr1+/xeO++MUvRlEUcdlll0VTU1OdpgOA+hA8qrTx+3csXbo0li5dWvVtBQ8AoJ7+4R/+YdTYMezyyy+Pd77znXHwwQfXeCoAqC+XtFRp4+AxVoIHAFAv9913X/z4xz8e020uu+yyGk0DAI1T+is8+vr6yj7lVqlUKqWebzh4TJ06NQYGBmLatGlbPP6MM86Iiy++OFpbW2OvvfYqZYay/5sAgHwuv/zyMd/mmmuuiU9/+tOjPr6h8TZ+POix4cRnn5vn/qif8XRfd3R0lHq+0oNHZ2dn2accF4aDxz777FPVg4G77rorIv74BqdTppTzQpqurq5SzgMAsLE//OEP8eIXv7jRYzBGHhvmYp80ynj6s1cURannc0lLFdauXRtr1qyJiIiFCxeOenxRFHHPPfdEhMtZAAAAoBFKf4VHb29v2afcKpVKpbRStfH7dyxatGjU41etWjXyRmFlBo/u7u5ob28v7XwAQD5f//rX4+Mf//iYbvMXf/EXcccdd5T2qlRqZ+PHuB4bTnz2uXllPp9jyzL/2Ss9eJR9zc14MNbgMXw5S0S5waO9vT3l/QsAlOcf/uEf4jOf+Uw89thjVd/m/e9/f+y+++41nIpa8NgwF/ukUTL/2ZPxqzAcPJqbm2PBggWjHj8cPKZMmVLV8QAAZdlpp53i5JNPHtPxJ510Ug0nAoDGEDyqMBw85s+fHzNmzBj1+LvvvjsiIubNmxetra21HA0A4Dk+85nPxOtf//pRj5s+fXp873vfi9mzZ9dhKgCoL8FjFIODg7Fy5cqIqO5ylog/Bw9vWAoANMJ2220X119/fXzgAx/Y7P+zZr/99ovbbrstXvva19Z5OgCoD8FjFD09PTE0NBQR1X1Cy+rVq2PdunURIXgAAI0zbdq0+O///b/HmjVr4lOf+tTI99/97nfHHXfcEXfffXccdNBBDZwQAGqr9DctzWbx4sVj+izguXPnlv7ZwQAAW2vmzJnx93//9/HpT386IiI+/elPp31zOgDYmFd4AAAAAOkIHgAAAEA6ggcAAACQjuABAAAApCN4AAAAAOkIHgAAAEA6ggcAAACQjuABAAAApCN4AAAAAOkIHgAAAEA6ggcAAACQjuABAAAApCN4AAAAAOkIHgAAAEA6ggcAAACQjuABAAAApCN4AAAAAOkIHgAAAEA6ggcAAACQjuABAAAApCN4AAAAAOkIHgAAAEA6ggcAAACQjuABAAAApCN4AAAAAOkIHgAAAEA6ggcAAACQjuABAAAApCN4AAAAAOm0NHqAeqhUKo0eYatN5NkBAACgUSZF8Ojq6mr0CAAAAEAduaQFAAAAJpgf/ehH0dTUFFdeeWWjRxm30r7Co62tLXp7exs9Rqna2toaPQIAAABMCGmDR0tLS3R0dDR6DAAAAKABXNICAABAak899VTccMMN8Y53vCP23XffmDVrVsyYMSN23333OOqoo+Jb3/pWPPPMM1s8x/HHHx9NTU1V/e+rX/1qfOQjH6n6+Of736GHHvqcGc4555yRX3/Na14TEREnnnjiyPcOOeSQWtx9E1baV3gAAADA97///ViyZEn86le/es6v9fb2Rm9vb3zve9+LCy64IK677rrYY489nvc8y5cvr/r3/Ku/+qv4+te/vpUT//kcz/bWt741XvziF0dExAMPPBDnn39+nHzyyfGqV70qIiJmz569Tb9nNoIHAAAAKZ111lnxj//4jxERsdtuu8X73//+OOyww2LPPfeMoijigQceiCuuuCL+1//6X7F8+fJ45StfGXfddddzwsHg4GCsXLkyIiKWLFkS55133hZ/3x122CFuvPHG533VyKpVq2L//fePiIhzzz03TjvttOc9x/Tp05/zvQULFsSCBQsi4o9vWnr++efHQQcdFMcff/wo98TkJHgAAACQzplnnhkXXHBBREQcddRR8ZWvfCV22mmnTY7Zbbfd4tWvfnUsXrw4PvjBD8aaNWvi1FNPjWuvvXaT43p6emJoaCgi/hgddthhh1F//9bW1uf9/qpVq0a+Xrx4cVXnYut4Dw8AAABSue6660Zix9ve9rb4zne+85zYsbHTTz995D0xrrvuunjwwQc3+fWNL2fZe++9t2m2n/3sZyNfv/zlL9+mc7FlggcAAABpPPzww/G+970vIiL+8i//Mq688sqYMmX0p74nnXRSREQURRE333zzJr9Wi+Cx2267RVtb2zadiy0TPAAAAEjj4osvjt/97ncREXHRRRfF9ttvX9XtFi9ePPJ1T0/PJr82HDxmz54dM2fO3Kb5hoOHV3fUnuABAABAChs2bIjLL788IiL22WefeNOb3lT1bWfNmjXy9cMPPzzydVEUce+990bEtr+6Y926dfHrX/86IgSPevCmpQAAAKTwgx/8IB555JGIiHjnO98ZTU1NVd/2iSeeGPl6u+22G/l69erVMTAwEBERc+fOjccee2yz59h+++23+Ht6/4768goPAAAAUrjllltGvn7DG94wptuuWbNm5OsXvOAFI19v/P4d//zP/xw77rjj8/5vl1122SSaPB/Bo74EDwAAAFIYDgrTp0+P+fPnj+m2d95558jXG8eIjYPHlsyfP3+zH0X77Pm22267eOlLXzqm+Z5txowZMXfu3C1++sxk55IWAAAAUujv74+IP765aHNz85hue+ONN458/cpXvnLk6+HgMW3atBgYGIipU6du9XzDwWP+/PnbdJ6IiAMOOCBWrVq1TefIzis8AAAASGHDhg0R8cdXeIxFX19f3HbbbRERsWjRok1efTEcPBYsWLBNkeLpp5+O++67LyJczlIvggcAAAApDH/SyqOPPjqm21144YUxNDQUERFLliwZ+f7atWtH3ttj0aJF2zTbypUrY3BwMCIEj3oRPAAAAEjhZS97WURE/O53v4u+vr6qbrNixYr44he/GBER+++/fxx//PEjv7bx+3dsa/DwhqX1J3gAAACQwuGHHz7y9Ve/+tVRj3/44Yfj6KOPjqeeeipaW1vj61//ekyZ8uenyYLHxCZ4AAAAkMKxxx4bc+bMiYiIz3zmM5t88sqz/fznP49DDjkkVq5cGc3NzXHllVeOvEJk2MZvWPrsXxur4eAxZ86c2HXXXbfpXFRH8AAAACCFadOmxTe+8Y1oaWmJJ554Ig455JA4++yz42c/+1k88sgj0d/fH7feemuccsopse+++0ZPT0+0trbGd77znTj66KOfc77h4LHvvvtu86eqDAcPr+6oHx9LCwAAQBqH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U06J9evXxy9/+cv40Ic+FHPnzq3z1Dyfse7y85//fLS1tcVb3vKWBkzLlox1l0uWLImVK1fG2rVr45vf/GZcffXV8dGPfrQBk8P4JngA48o///M/x+c+97nYfffd46Mf/WhccsklceCBB8Zll10W++yzT/z7v//7yLE77rhjRET8/ve/3+Qc69ati5122qmuc/NcY9kl49/W7vMrX/lKfOxjH4tbbrkl9thjj/oOzfPalp/NPfbYI4488sh4wxveEEVR1HFqns9Ydrlq1aq46KKL4gtf+EIDJ2ZzxvpzuXDhwthtt91iypQpsXDhwjjvvPPim9/8ZoOmh3GsgD/p7e0tIqKIiKK3t7fR42yzf/3Xfy1+/vOfN3qMTdTrPp7Iu7zzzjuLRx999Dnf/9KXvlRERHH00Udv8v3dd9+9+NrXvjbyfz/wwANFU1NT8Zvf/Kamc9bzPp6o+xzrLofdfvvtxbRp02o83Z/ZZXW2Zp+XXXZZ8cIXvrC45557aj9gYZfV2tqfzWE/+clPiqampuLxxx+v0YR2Wa2x7PKrX/1qsd122xW77rprseuuuxYzZ84sIqKYNWtWcfnll9d0To9/RretP5ff+973itmzZ9douj+byPcxk5NXeJDSb3/723jjG98YP/nJTxo9CmO0ePHi572u+Nhjj42IiHvvvXeT75988snx2c9+Nv7jP/4jfv/738eZZ54ZRx55pDcsHQfGusunn346BgcHR65BHhwcjMHBwZrPSXXGus9LL700zjnnnLj11ltjv/32q8OEVGssu9ywYUNcccUV8cgjj0RExOrVq+Oss86KV73qVT7lYxwYyy7f/va3x+rVq2P58uWxfPny+D//5/9ERMQPf/jDeMc73lGXedm8sf4d++1vf3vkFa4rVqyIs88+O44++uiazwkTjU9pIaVTTjklHnnkkZH3eGDiW7NmTUREzJ49e5Pvn3XWWfHII4/EokWL4g9/+EMceuih8aUvfakRI1Klze3yqquu2uQjhWfMmBER4WXz49zm9vnBD34wpk6dGgcddNAm33/sscfqNhtj83y7bGpqiu9+97vx0Y9+NJ544onYdddd47DDDvN+LOPc8+2ytbV1k0g1NDQUERHt7e2xww471HdAqra5v2Mvu+yyeN/73hdPPfVUtLW1xbHHHhuf+MQnGjEijGuCB+lcffXVcd1110VECB7jzIYNG6KpqWnkiexYnH322RERmzwhjohobm6Oiy66KC666KJSZqQ6tdjlCSecECeccEIZ4zFGtdinUNUYZe9yxowZ8X//7/8tbT6qV4ufy43tsccefk7rpBa7/PGPf1zKbJCdS1pI5Xe/+1184AMfGPm/fVJHYz355JNx5ZVXxuGHHx677LJLbL/99tHa2hq77rprvP3tb4+f/vSnVZ3n/PPPj2uvvTbe/OY3x7ve9a4aT83zsctc7DMPu8zDLvOwSxhHGvweIowjGd6E6G1ve1vx1re+deS/4/3vf3+jR9rEZHrTrqVLlxadnZ3FoYceWnz3u98tent7i8HBweLnP/95cdpppxVNTU3FlClTii996UtbPM+ll15aRERxyCGH1PQN8sZqMr2hnl1OzN9rczLv0y7tcrz/XpuTeZdF4fGPXULjCB6MmOh/gX3nO98pXvSiFxW//e1vR/47/u7v/q7RY21iMvyDPzQ0VJx11lnFjBkzim9+85ubPe5Tn/pUERFFc3NzcccddzzvMRdddFEREcVrX/vacfWPfVFMjgfjdjmxf69nmwz7tMtN2eX4+72ebTLssig8/tmYXUJ9CR6MmMh/gT300EPF7Nmzix/+8IdFURTFlClTiogo3vKWtzR4sk1l/wf/mWeeKY466qiiubm5uOGGG7Z47Pr164vtttuuiIji9a9//XN+/bOf/WwREcVhhx1WPPHEE7UaeatlfzBulxP/99rYZNmnXW7KLsff77WxybLLovD4Z2N2CfUleDBiIv8FdswxxxSnnHLKyP+94447FhFRvO51r2vgVM+V/R/8//bf/lsREcVZZ51V1fEHHnhgERFFS0tLsX79+pHvn3feeUVEFEceeWQxODhYq3G3SfYH43Y58X+vjU2Wfdrlc9nl+Pq9NjZZdlkUHv88m11C/fiUFia86667Lu68885NPp98hx12iIGBAZ/SUke33357fOpTn4rZs2fHOeecU9Vt/vIv/zKWLVsWQ0NDsWrVqth///3jn/7pn+LjH/94zJ49O9761rfGNddc85zbHX/88SVPz8bsMhf7zMMu87DLPOwSxjfBgwntkUceife///1x9dVXx/bbbz/y/eGvBY/6KIoi/st/+S/xzDPPxPve976YNm1aVbfbcccdR75+4oknIiLizjvvjIiI3/72t/Hud7/7eW/nH/zasctc7DMPu8zDLvOwSxj/fCwtE9oHPvCBOProo+Ov//qvN/m+4FFf/+///b9Yvnx5REQcccQRW3WOXXfdNSIirrzyyij+eLndZv9H7dhlLvaZh13mYZd52CWMf17hwYT1/e9/P7q7u2PZsmXx2GOPbfJr06dPj4iI9evXN2K0SWfp0qUREdHc3BwLFy6s+naPPvpoRETMmDEj5s6dW4vRGCO7zMU+87DLPOwyD7uE8U/wYEJ6+OGH433ve19MnTo1Zs+eHU8//fTzHvfsEEJt3H333RERMWvWrGhubq76dj09PRER8dd//dfR0uKvo/HALnOxzzzsMg+7zMMuYfxzSQsT0rvf/e54/etfH7/61a9iaGjoOS/5O/zwwyMi4umnn47HH3+8wdPm99BDD435NmvWrIkHHnggImKz16pSf3aZi33mYZd52GUedgnjn+DBuLZ+/fq44YYbNnmlxic/+cl44IEH4rLLLtvs7WbNmjXy9Zo1a2o6IxG77bZbRPzxH/5Vq1ZVdZvLL788iqKIBQsWxNve9rZajscY2GUu9pmHXeZhl3nYJYx/ggfj2jvf+c74z//5P8dxxx0XDz/8cJx55plxwQUXxLe+9a1NPpXl2V784hePfH3rrbdGRMT9998ffX19NZ95MnrjG9848vWSJUviqaeees4xg4ODI+9E3tvbG5dccklMnTo1rrjiipgyxV9F44Vd5mKfedhlHnaZh13C+OenjHHtN7/5TUREXH/99fGCF7wgLrjggvjSl74UixYt2uLt/uZv/mbk67POOive+MY3xlve8pbYbrvtajrvZHXKKafEQQcdFBERN910U7ziFa+I73znO7FixYpYtmxZXHLJJXHMMcdEpVKJJ598Mo477rjYsGFD/NM//VN0dXU1eHo2Zpe52GcedpmHXeZhlzABFPAnvb29RUQUEVH09vY2epyiKIri6quvLlpaWoqIKGbMmFFcccUVVd/2k5/8ZLHzzjsXO+20U/GmN72pWL16dQ0nrU697uNG7HLDhg3FeeedV+y7775Fa2trMWPGjGLPPfcsjjnmmOKaa64pnnrqqWLdunXF4YcfXmy33XbFl7/85brMVSv1vI/rvU+7zPF7DZtM+7RLuxzvv9ewybTLovD4xy6hcQQPRozXv8AefPDB4tprry3WrFnT6FG2WeZ/8Ldk3bp1xRVXXFF0dnYWixcvLn760582eqRtlv3B+ObY5cT5vaqRbZ92aZfj/feqRrZdFoXHP3YJjeNzkBj35s2bF/PmzWv0GFSpv78/fvrTn8bPfvazWLFiRaxcuTIeeOCBePWrXx2f//zn481vfnM0NTU1ekyqYJe52GcedpmHXeZhlzA+CR7ANhsaGorzzz8/rr/++rjrrruiKIqRXzv66KPjhhtuiDlz5mz29j/4wQ/i8ccfjze/+c11mJYtsctc7DMPu8zDLvOwSxj/vGkpsM2amppir732ir/927+NY489NnbfffeRX7vmmmtir732ir/7u7+LH/7wh/H0009vctt77703zj333DjssMPqPTbPwy5zsc887DIPu8zDLmH88woPYJs1NzfHscceu8n3uru741vf+lZ85zvfid/85jdx1VVXxVVXXRUzZ86MAw44IF7wghfEr371q/iP//iPuOGGG2L69OkNmp6N2WUu9pmHXeZhl3nYJYx/ggdQE11dXdHV1RUXXXRRLFu2LH7wgx/E3XffHWvXro0nn3wytttuu3jPe94TRx11VLS2tjZ6XLbALnOxzzzsMg+7zMMuYXwRPICamjJlShx88MFx8MEHN3oUtpFd5mKfedhlHnaZh13C+OA9PAAAAIB0BA8AAAAgHcEDAAAASEfwAAAAANIRPAAAAIB0BA8AAAAgHcEDAAAASEfwAAAAANIRPAAAAIB0BA8AAAAgHcEDAAAASEfwAAAAANIRPAAAAIB0BA8AAAAgHcEDAAAASEfwAAAAANIRPAAAAIB0BA8AAAAgHcEDAAAASEfwAAAAANIRPAAAAIB0BA8AAAAgHcEDAAAASEfwAAAAANIRPAAAAIB0BA8AAAAgHcEDAAAASEfwAAAAANIRPAAAAIB0BA8AAAAgHcEDAAAASEfwAAAAANIRPAAAAIB0BA8AAAAgHcEDAAAASKel7BP29fWVfUrqpFKpNHqESaWW97dd1let72/7rB+7zMMu87DLXDz+ycP9TS10dHSUer7Sg0dnZ2fZp4SUurq6Gj0CJbHLPOwyD7vMwy5zsc887JJaKIqi1PO5pAUAAABIp/RXePT29pZ9SuqkUqkotXXU3d0d7e3tNTm3XdZXLXcZYZ/1ZJd52GUedpmLxz951PpnE8pQevAo+5obyKq9vd3PSxJ2mYdd5mGXedhlLvaZh10yEbik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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import os\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\")\n", + "os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python'\n", + "\n", + "import toml\n", + "from paddle_quantum.finance import EuroOptionEstimator\n", + "\n", + "config = toml.loads(euro_toml)\n", + "initial_price = config[\"initial_price\"]\n", + "strike_price = config[\"strike_price\"]\n", + "interest_rate = config[\"interest_rate\"]\n", + "volatility = config[\"volatility\"]\n", + "maturity_date = config[\"maturity_date\"]\n", + "degree_of_estimation = config[\"degree_of_estimation\"]\n", + "\n", + "\n", + "estimator = EuroOptionEstimator(initial_price, strike_price, interest_rate, volatility, maturity_date, degree_of_estimation)\n", + "print(\"该期权的风险中性价格大约为\", estimator.estimate())\n", + "print(\"以下是该量子方案的电路实现图。\")\n", + "estimator.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "通过修改配置文件的内容,并运行预测代码,即可在线对模型进行测试。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "___\n", + "\n", + "# 注意事项\n", + "\n", + "这里提供的模型只用于解决布莱克-斯科尔斯模型的期权定价问题。\n", + "\n", + "# 引用信息\n", + "\n", + "```\n", + "@article{rebentrost2018quantum,\n", + " title = {Quantum Computational Finance: {{Monte Carlo}} Pricing of Financial Derivatives},\n", + " shorttitle = {Quantum Computational Finance},\n", + " author = {Rebentrost, Patrick and Gupt, Brajesh and Bromley, Thomas R.},\n", + " year = {2018},\n", + " month = aug,\n", + " journal = {Physical Review A},\n", + " volume = {98},\n", + " number = {2},\n", + " pages = {022321},\n", + " publisher = {{American Physical Society}},\n", + " doi = {10.1103/PhysRevA.98.022321},\n", + " url = {https://link.aps.org/doi/10.1103/PhysRevA.98.022321},\n", + "}\n", + "```\n", + "\n", + "# 参考文献\n", + "\n", + "[1] Rebentrost, Patrick, Brajesh Gupt, and Thomas R. Bromley. \"Quantum computational finance: Monte Carlo pricing of financial derivatives.\" Physical Review A 98.2 (2018): 022321." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pq", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.13 (default, Mar 28 2022, 06:16:26) \n[Clang 12.0.0 ]" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "08942b1340a5932ff3a93f52933a99b0e263568f3aace1d262ffa4d9a0f2da31" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/applications/option_pricing/introduction_en.ipynb b/applications/option_pricing/introduction_en.ipynb new file mode 100644 index 0000000..7c9ba94 --- /dev/null +++ b/applications/option_pricing/introduction_en.ipynb @@ -0,0 +1,205 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Option pricing problem\n", + "\n", + "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The task of option pricing is to assess the expected value of the current stock option at the expiration date. Many factors determine the value of an option, including the current stock price, intrinsic value, time to maturity or time value, volatility, interest rates, and cash dividends paid. Many option pricing models use these factors as parameters to determine the fair market value of an option, the most widely known of which is the Black-Scholes model. The Black-Scholes model can price a variety of financial derivatives through a simple and analytically solvable model with a small number of input parameters. The model assumes that the prices of a large number of traded assets follow geometric Brownian motion with constant drift and volatility. When applied to stock options, the model contains a constant price change for the stock, the time value of the currency, the strike price of the option, and the time the option expires. In particular, instruments such as stocks or futures contracts will have a lognormally distributed price after a random walk, with constant drift and volatility. \n", + "\n", + "European option pricing is a direct application of the Black-Scholes model, which can be used to calculate the price of a European call option. Europeans call options give the option holder the right to purchase shares at a pre-agreed price of $K$ at $T$ on the expiration date. Typically, the option payoff function is given by\n", + "$$\n", + "f(S_{T})=\\max\\{0, S_{T}-K\\}, \\tag{1}\n", + "$$\n", + "where $S_{T}$ represents the asset price at the expiration date of the option and $K$ represents the strike price. Here, the asset price $S_{T}$ follows a known probability distribution under the Black-Scholes model and depends on the initial price, risk-free interest rate, volatility, and maturity date $T$. In classical calculations, the expected value of an option's stock at maturity can be calculated by Monte Carlo methods: a market sample is taken from a known (risk-neutral) probability distribution, and then the asset price given that market sample is calculated. Afterwards, the payoff of the option for a given asset price is calculated, and finally, averaging payoffs overall multiple samples gives an approximation of the option price." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Quantum solutions\n", + "Unlike classical algorithms, the core of the quantum Monte Carlo method is to simulate probability distributions with quantum circuits and store asset prices in quantum states, and then calculate the average of returns in parallel through quantum amplitude estimation algorithms. Through the characteristics of quantum superposition and entanglement, quantum schemes have the advantage of quadratic acceleration compared with classical schemes. Next, we take a European call option as an example to show how to use Paddle Quantum to simulate this quantum scheme to complete the risk-neutral pricing problem of European call options." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Online demonstration" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We have set a parameter that can be used directly for the pricing of European call options. Just configure it in the configuration file `config.toml` and enter the command \n", + "`python euro_pricing.py --config config.toml`\n", + "The configured European options can be priced.\n", + "\n", + "Here, we give a version of the online demo that can be tested online. First define the contents of the configuration file:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "euro_toml = r\"\"\"\n", + "# The overall profile used to calculate the option pricing model\n", + "# initial price\n", + "initial_price = 100\n", + "# strike price\n", + "strike_price = 110\n", + "# risk-free rate\n", + "interest_rate = 0.05\n", + "# Market volatility\n", + "volatility = 0.1\n", + "# Option expiration date (in years)\n", + "maturity_date = 1\n", + "# Estimation accuracy index\n", + "degree_of_estimation = 5\n", + "\"\"\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The financial module of the Paddle Quantum enables numerical simulation of quantum Monte Carlo schemes. We can import ``EuroOptionEstimator`` from the ``paddle_quantum.finance`` module to solve the configured option pricing problem." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The risk-neutral price of the option is approximately 2.2613329887390137\n", + "The following is a circuit implementation diagram of this quantum scheme.\n" + ] + }, + { + "data": { + "image/png": "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PTE0NBQR1X1Cy+rVq2PdunURIXgAAI0zbdq0+O///b/HmjVr4lOf+tTI99/97nfHHXfcEXfffXccdNBBDZwQAGqr9DctzWbx4sVj+izguXPnlv7ZwQAAW2vmzJnx93//9/HpT386IiI+/elPp31zOgDYmFd4AAAAAOkIHgAAAEA6ggcAAACQjuABAAAApCN4AAAAAOkIHgAAAEA6ggcAAACQjuABAAAApCN4AAAAAOkIHgAAAEA6ggcAAACQjuABAAAApCN4AAAAAOkIHgAAAEA6ggcAAACQjuABAAAApCN4AAAAAOkIHgAAAEA6ggcAAACQjuABAAAApCN4AAAAAOkIHgAAAEA6ggcAAACQjuABAAAApCN4AAAAAOkIHgAAAEA6ggcAAACQjuABAAAApCN4AAAAAOm0NHqAeqhUKo0eYatN5NkBAACgUSZF8Ojq6mr0CAAAAEAduaQFAAAAJpgf/ehH0dTUFFdeeWWjRxm30r7Co62tLXp7exs9Rqna2toaPQIAAABMCGmDR0tLS3R0dDR6DAAAAKABXNICAABAak899VTccMMN8Y53vCP23XffmDVrVsyYMSN23333OOqoo+Jb3/pWPPPMM1s8x/HHHx9NTU1V/e+rX/1qfOQjH6n6+Of736GHHvqcGc4555yRX3/Na14TEREnnnjiyPcOOeSQWtx9E1baV3gAAADA97///ViyZEn86le/es6v9fb2Rm9vb3zve9+LCy64IK677rrYY489nvc8y5cvr/r3/Ku/+qv4+te/vpUT//kcz/bWt741XvziF0dExAMPPBDnn39+nHzyyfGqV70qIiJmz569Tb9nNoIHAAAAKZ111lnxj//4jxERsdtuu8X73//+OOyww2LPPfeMoijigQceiCuuuCL+1//6X7F8+fJ45StfGXfddddzwsHg4GCsXLkyIiKWLFkS55133hZ/3x122CFuvPHG533VyKpVq2L//fePiIhzzz03TjvttOc9x/Tp05/zvQULFsSCBQsi4o9vWnr++efHQQcdFMcff/wo98TkJHgAAACQzplnnhkXXHBBREQcddRR8ZWvfCV22mmnTY7Zbbfd4tWvfnUsXrw4PvjBD8aaNWvi1FNPjWuvvXaT43p6emJoaCgi/hgddthhh1F//9bW1uf9/qpVq0a+Xrx4cVXnYut4Dw8AAABSue6660Zix9ve9rb4zne+85zYsbHTTz995D0xrrvuunjwwQc3+fWNL2fZe++9t2m2n/3sZyNfv/zlL9+mc7FlggcAAABpPPzww/G+970vIiL+8i//Mq688sqYMmX0p74nnXRSREQURRE333zzJr9Wi+Cx2267RVtb2zadiy0TPAAAAEjj4osvjt/97ncREXHRRRfF9ttvX9XtFi9ePPJ1T0/PJr82HDxmz54dM2fO3Kb5hoOHV3fUnuABAABAChs2bIjLL788IiL22WefeNOb3lT1bWfNmjXy9cMPPzzydVEUce+990bEtr+6Y926dfHrX/86IgSPevCmpQAAAKTwgx/8IB555JGIiHjnO98ZTU1NVd/2iSeeGPl6u+22G/l69erVMTAwEBERc+fOjccee2yz59h+++23+Ht6/4768goPAAAAUrjllltGvn7DG94wptuuWbNm5OsXvOAFI19v/P4d//zP/xw77rjj8/5vl1122SSaPB/Bo74EDwAAAFIYDgrTp0+P+fPnj+m2d95558jXG8eIjYPHlsyfP3+zH0X77Pm22267eOlLXzqm+Z5txowZMXfu3C1++sxk55IWAAAAUujv74+IP765aHNz85hue+ONN458/cpXvnLk6+HgMW3atBgYGIipU6du9XzDwWP+/PnbdJ6IiAMOOCBWrVq1TefIzis8AAAASGHDhg0R8cdXeIxFX19f3HbbbRERsWjRok1efTEcPBYsWLBNkeLpp5+O++67LyJczlIvggcAAAApDH/SyqOPPjqm21144YUxNDQUERFLliwZ+f7atWtH3ttj0aJF2zTbypUrY3BwMCIEj3oRPAAAAEjhZS97WURE/O53v4u+vr6qbrNixYr44he/GBER+++/fxx//PEjv7bx+3dsa/DwhqX1J3gAAACQwuGHHz7y9Ve/+tVRj3/44Yfj6KOPjqeeeipaW1vj61//ekyZ8uenyYLHxCZ4AAAAkMKxxx4bc+bMiYiIz3zmM5t88sqz/fznP49DDjkkVq5cGc3NzXHllVeOvEJk2MZvWPrsXxur4eAxZ86c2HXXXbfpXFRH8AAAACCFadOmxTe+8Y1oaWmJJ554Ig455JA4++yz42c/+1k88sgj0d/fH7feemuccsopse+++0ZPT0+0trbGd77znTj66KOfc77h4LHvvvtu86eqDAcPr+6oHx9LCwAAQBqH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U06J9evXxy9/+cv40Ic+FHPnzq3z1Dyfse7y85//fLS1tcVb3vKWBkzLlox1l0uWLImVK1fG2rVr45vf/GZcffXV8dGPfrQBk8P4JngA48o///M/x+c+97nYfffd46Mf/WhccsklceCBB8Zll10W++yzT/z7v//7yLE77rhjRET8/ve/3+Qc69ati5122qmuc/NcY9kl49/W7vMrX/lKfOxjH4tbbrkl9thjj/oOzfPalp/NPfbYI4488sh4wxveEEVR1HFqns9Ydrlq1aq46KKL4gtf+EIDJ2ZzxvpzuXDhwthtt91iypQpsXDhwjjvvPPim9/8ZoOmh3GsgD/p7e0tIqKIiKK3t7fR42yzf/3Xfy1+/vOfN3qMTdTrPp7Iu7zzzjuLRx999Dnf/9KXvlRERHH00Udv8v3dd9+9+NrXvjbyfz/wwANFU1NT8Zvf/Kamc9bzPp6o+xzrLofdfvvtxbRp02o83Z/ZZXW2Zp+XXXZZ8cIXvrC45557aj9gYZfV2tqfzWE/+clPiqampuLxxx+v0YR2Wa2x7PKrX/1qsd122xW77rprseuuuxYzZ84sIqKYNWtWcfnll9d0To9/RretP5ff+973itmzZ9douj+byPcxk5NXeJDSb3/723jjG98YP/nJTxo9CmO0ePHi572u+Nhjj42IiHvvvXeT75988snx2c9+Nv7jP/4jfv/738eZZ54ZRx55pDcsHQfGusunn346BgcHR65BHhwcjMHBwZrPSXXGus9LL700zjnnnLj11ltjv/32q8OEVGssu9ywYUNcccUV8cgjj0RExOrVq+Oss86KV73qVT7lYxwYyy7f/va3x+rVq2P58uWxfPny+D//5/9ERMQPf/jDeMc73lGXedm8sf4d++1vf3vkFa4rVqyIs88+O44++uiazwkTjU9pIaVTTjklHnnkkZH3eGDiW7NmTUREzJ49e5Pvn3XWWfHII4/EokWL4g9/+EMceuih8aUvfakRI1Klze3yqquu2uQjhWfMmBER4WXz49zm9vnBD34wpk6dGgcddNAm33/sscfqNhtj83y7bGpqiu9+97vx0Y9+NJ544onYdddd47DDDvN+LOPc8+2ytbV1k0g1NDQUERHt7e2xww471HdAqra5v2Mvu+yyeN/73hdPPfVUtLW1xbHHHhuf+MQnGjEijGuCB+lcffXVcd1110VECB7jzIYNG6KpqWnkiexYnH322RERmzwhjohobm6Oiy66KC666KJSZqQ6tdjlCSecECeccEIZ4zFGtdinUNUYZe9yxowZ8X//7/8tbT6qV4ufy43tsccefk7rpBa7/PGPf1zKbJCdS1pI5Xe/+1184AMfGPm/fVJHYz355JNx5ZVXxuGHHx677LJLbL/99tHa2hq77rprvP3tb4+f/vSnVZ3n/PPPj2uvvTbe/OY3x7ve9a4aT83zsctc7DMPu8zDLvOwSxhHGvweIowjGd6E6G1ve1vx1re+deS/4/3vf3+jR9rEZHrTrqVLlxadnZ3FoYceWnz3u98tent7i8HBweLnP/95cdpppxVNTU3FlClTii996UtbPM+ll15aRERxyCGH1PQN8sZqMr2hnl1OzN9rczLv0y7tcrz/XpuTeZdF4fGPXULjCB6MmOh/gX3nO98pXvSiFxW//e1vR/47/u7v/q7RY21iMvyDPzQ0VJx11lnFjBkzim9+85ubPe5Tn/pUERFFc3NzcccddzzvMRdddFEREcVrX/vacfWPfVFMjgfjdjmxf69nmwz7tMtN2eX4+72ebTLssig8/tmYXUJ9CR6MmMh/gT300EPF7Nmzix/+8IdFURTFlClTiogo3vKWtzR4sk1l/wf/mWeeKY466qiiubm5uOGGG7Z47Pr164vtttuuiIji9a9//XN+/bOf/WwREcVhhx1WPPHEE7UaeatlfzBulxP/99rYZNmnXW7KLsff77WxybLLovD4Z2N2CfUleDBiIv8FdswxxxSnnHLKyP+94447FhFRvO51r2vgVM+V/R/8//bf/lsREcVZZ51V1fEHHnhgERFFS0tLsX79+pHvn3feeUVEFEceeWQxODhYq3G3SfYH43Y58X+vjU2Wfdrlc9nl+Pq9NjZZdlkUHv88m11C/fiUFia86667Lu68885NPp98hx12iIGBAZ/SUke33357fOpTn4rZs2fHOeecU9Vt/vIv/zKWLVsWQ0NDsWrVqth///3jn/7pn+LjH/94zJ49O9761rfGNddc85zbHX/88SVPz8bsMhf7zMMu87DLPOwSxjfBgwntkUceife///1x9dVXx/bbbz/y/eGvBY/6KIoi/st/+S/xzDPPxPve976YNm1aVbfbcccdR75+4oknIiLizjvvjIiI3/72t/Hud7/7eW/nH/zasctc7DMPu8zDLvOwSxj/fCwtE9oHPvCBOProo+Ov//qvN/m+4FFf/+///b9Yvnx5REQcccQRW3WOXXfdNSIirrzyyij+eLndZv9H7dhlLvaZh13mYZd52CWMf17hwYT1/e9/P7q7u2PZsmXx2GOPbfJr06dPj4iI9evXN2K0SWfp0qUREdHc3BwLFy6s+naPPvpoRETMmDEj5s6dW4vRGCO7zMU+87DLPOwyD7uE8U/wYEJ6+OGH433ve19MnTo1Zs+eHU8//fTzHvfsEEJt3H333RERMWvWrGhubq76dj09PRER8dd//dfR0uKvo/HALnOxzzzsMg+7zMMuYfxzSQsT0rvf/e54/etfH7/61a9iaGjoOS/5O/zwwyMi4umnn47HH3+8wdPm99BDD435NmvWrIkHHnggImKz16pSf3aZi33mYZd52GUedgnjn+DBuLZ+/fq44YYbNnmlxic/+cl44IEH4rLLLtvs7WbNmjXy9Zo1a2o6IxG77bZbRPzxH/5Vq1ZVdZvLL788iqKIBQsWxNve9rZajscY2GUu9pmHXeZhl3nYJYx/ggfj2jvf+c74z//5P8dxxx0XDz/8cJx55plxwQUXxLe+9a1NPpXl2V784hePfH3rrbdGRMT9998ffX19NZ95MnrjG9848vWSJUviqaeees4xg4ODI+9E3tvbG5dccklMnTo1rrjiipgyxV9F44Vd5mKfedhlHnaZh13C+OenjHHtN7/5TUREXH/99fGCF7wgLrjggvjSl74UixYt2uLt/uZv/mbk67POOive+MY3xlve8pbYbrvtajrvZHXKKafEQQcdFBERN910U7ziFa+I73znO7FixYpYtmxZXHLJJXHMMcdEpVKJJ598Mo477rjYsGFD/NM//VN0dXU1eHo2Zpe52GcedpmHXeZhlzABFPAnvb29RUQUEVH09vY2epyiKIri6quvLlpaWoqIKGbMmFFcccUVVd/2k5/8ZLHzzjsXO+20U/GmN72pWL16dQ0nrU697uNG7HLDhg3FeeedV+y7775Fa2trMWPGjGLPPfcsjjnmmOKaa64pnnrqqWLdunXF4YcfXmy33XbFl7/85brMVSv1vI/rvU+7zPF7DZtM+7RLuxzvv9ewybTLovD4xy6hcQQPRozXv8AefPDB4tprry3WrFnT6FG2WeZ/8Ldk3bp1xRVXXFF0dnYWixcvLn760582eqRtlv3B+ObY5cT5vaqRbZ92aZfj/feqRrZdFoXHP3YJjeNzkBj35s2bF/PmzWv0GFSpv78/fvrTn8bPfvazWLFiRaxcuTIeeOCBePWrXx2f//zn481vfnM0NTU1ekyqYJe52GcedpmHXeZhlzA+CR7ANhsaGorzzz8/rr/++rjrrruiKIqRXzv66KPjhhtuiDlz5mz29j/4wQ/i8ccfjze/+c11mJYtsctc7DMPu8zDLvOwSxj/vGkpsM2amppir732ir/927+NY489NnbfffeRX7vmmmtir732ir/7u7+LH/7wh/H0009vctt77703zj333DjssMPqPTbPwy5zsc887DIPu8zDLmH88woPYJs1NzfHscceu8n3uru741vf+lZ85zvfid/85jdx1VVXxVVXXRUzZ86MAw44IF7wghfEr371q/iP//iPuOGGG2L69OkNmp6N2WUu9pmHXeZhl3nYJYx/ggdQE11dXdHV1RUXXXRRLFu2LH7wgx/E3XffHWvXro0nn3wytttuu3jPe94TRx11VLS2tjZ6XLbALnOxzzzsMg+7zMMuYXwRPICamjJlShx88MFx8MEHN3oUtpFd5mKfedhlHnaZh13C+OA9PAAAAIB0BA8AAAAgHcEDAAAASEfwAAAAANIRPAAAAIB0BA8AAAAgHcEDAAAASEfwAAAAANIRPAAAAIB0BA8AAAAgHcEDAAAASEfwAAAAANIRPAAAAIB0BA8AAAAgHcEDAAAASEfwAAAAANIRPAAAAIB0BA8AAAAgHcEDAAAASEfwAAAAANIRPAAAAIB0BA8AAAAgHcEDAAAASEfwAAAAANIRPAAAAIB0BA8AAAAgHcEDAAAASEfwAAAAANIRPAAAAIB0BA8AAAAgHcEDAAAASEfwAAAAANIRPAAAAIB0BA8AAAAgHcEDAAAASKel7BP29fWVfUrqpFKpNHqESaWW97dd1let72/7rB+7zMMu87DLXDz+ycP9TS10dHSUer7Sg0dnZ2fZp4SUurq6Gj0CJbHLPOwyD7vMwy5zsc887JJaKIqi1PO5pAUAAABIp/RXePT29pZ9SuqkUqkotXXU3d0d7e3tNTm3XdZXLXcZYZ/1ZJd52GUedpmLxz951PpnE8pQevAo+5obyKq9vd3PSxJ2mYdd5mGXedhlLvaZh10yEbik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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import os\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\")\n", + "os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python'\n", + "\n", + "import toml\n", + "from paddle_quantum.finance import EuroOptionEstimator\n", + "\n", + "config = toml.loads(euro_toml)\n", + "initial_price = config[\"initial_price\"]\n", + "strike_price = config[\"strike_price\"]\n", + "interest_rate = config[\"interest_rate\"]\n", + "volatility = config[\"volatility\"]\n", + "maturity_date = config[\"maturity_date\"]\n", + "degree_of_estimation = config[\"degree_of_estimation\"]\n", + "\n", + "\n", + "estimator = EuroOptionEstimator(initial_price, strike_price, interest_rate, volatility, maturity_date, degree_of_estimation)\n", + "print(\"The risk-neutral price of the option is approximately \", estimator.estimate())\n", + "print(\"The following is a circuit implementation diagram of this quantum scheme.\")\n", + "estimator.plot()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "By modifying the contents of the configuration file and running the prediction code, you can test the model online." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "___\n", + "\n", + "# Note\n", + "\n", + "The models presented here are only intended to solve the option pricing problem of the Black-Scholes model.\n", + "\n", + "# Citation\n", + "\n", + "```\n", + "@article{rebentrost2018quantum,\n", + " title = {Quantum Computational Finance: {{Monte Carlo}} Pricing of Financial Derivatives},\n", + " shorttitle = {Quantum Computational Finance},\n", + " author = {Rebentrost, Patrick and Gupt, Brajesh and Bromley, Thomas R.},\n", + " year = {2018},\n", + " month = aug,\n", + " journal = {Physical Review A},\n", + " volume = {98},\n", + " number = {2},\n", + " pages = {022321},\n", + " publisher = {{American Physical Society}},\n", + " doi = {10.1103/PhysRevA.98.022321},\n", + " url = {https://link.aps.org/doi/10.1103/PhysRevA.98.022321},\n", + "}\n", + "```\n", + "\n", + "# References\n", + "\n", + "[1] Rebentrost, Patrick, Brajesh Gupt, and Thomas R. Bromley. \"Quantum computational finance: Monte Carlo pricing of financial derivatives.\" Physical Review A 98.2 (2018): 022321." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pq", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.13 (default, Mar 28 2022, 06:16:26) \n[Clang 12.0.0 ]" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "08942b1340a5932ff3a93f52933a99b0e263568f3aace1d262ffa4d9a0f2da31" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/applications/portfolio_optimization/config.toml b/applications/portfolio_optimization/config.toml new file mode 100644 index 0000000..74aa048 --- /dev/null +++ b/applications/portfolio_optimization/config.toml @@ -0,0 +1,38 @@ +# The configuration file of quantum portfolio optimization problem. + + +# This field specifies whether to use random data for stocks, or stock data provided by the customer. + + +# stock = 'random' +# stock = 'custom' +stock = 'demo' + +# Get stock data from demo file or custom file +demo_data_path = 'demo_stock.csv' +custom_data_path = 'file_name.csv' + +# Specifies the start_time and end_time of random stock +[random_data] +start_time = [2016, 1, 1] +end_time = [2016, 1, 30] + +# This field stores information about the asset, risk, etc. +[stock_para] +# Number of investable projects +num_asset = 7 +# The risk factor of investment decision making +risk_weight = 0.5 +# The budget +budget = 0 +#The penalty +penalty = 0 + +# This field stores parameters for the parametric quantum circuits +[train_para] +# The depth of the quantum circuits +circuit_depth = 2 +# Number of optimization cycles, default is 100. +iterations = 600 +# The rate of optimization of gradient descent, default is 0.4. +learning_rate = 0.2 diff --git a/applications/portfolio_optimization/demo_stock.csv b/applications/portfolio_optimization/demo_stock.csv new file mode 100644 index 0000000..291eb65 --- /dev/null +++ b/applications/portfolio_optimization/demo_stock.csv @@ -0,0 +1,37 @@ +closePrice0,closePrice1,closePrice2,closePrice3,closePrice4,closePrice5,closePrice6,closePrice7,closePrice8,closePrice9,closePrice10,closePrice11 +16.87,32.56,5.4,3.71,5.72,7.62,3.7,6.94,5.43,3.46,8.22,4.56 +17.18,32.05,5.48,3.75,5.75,7.56,3.7,6.89,5.37,3.45,8.14,4.54 +17.07,31.51,5.46,3.73,5.74,7.68,3.68,6.91,5.37,3.41,8.1,4.55 +17.15,31.76,5.49,3.79,5.81,7.75,3.7,6.97,5.42,3.5,8.16,4.58 +16.66,31.68,5.39,3.72,5.69,7.79,3.63,6.85,5.29,3.42,7.93,4.48 +16.79,32.2,5.47,3.77,5.79,7.84,3.66,6.94,5.41,3.46,8.02,4.56 +16.69,31.46,5.46,3.76,5.77,7.82,3.63,7.01,5.42,3.48,8,4.53 +16.99,31.68,5.53,3.74,5.8,7.8,3.63,7.03,5.39,3.47,8.03,4.53 +16.76,31.39,5.5,3.78,5.89,7.92,3.66,7.04,5.45,3.48,8.05,4.56 +16.52,30.49,5.47,3.71,5.78,7.96,3.63,7.01,5.43,3.45,7.95,4.47 +16.33,30.53,5.39,3.61,5.7,7.93,3.6,6.99,5.35,3.4,7.92,4.42 +16.39,30.46,5.35,3.58,5.69,7.87,3.59,6.95,5.26,3.41,7.93,4.38 +16.45,29.87,5.37,3.61,5.75,7.86,3.63,6.96,5.54,3.42,7.99,4.35 +16,29.21,5.24,3.53,5.7,7.82,3.6,6.87,6.09,3.32,7.9,4.32 +16.09,30.11,5.26,3.5,5.71,7.9,3.61,6.87,6.7,3.33,8.01,4.34 +15.54,28.98,5.08,3.42,5.54,7.7,3.54,6.58,7.37,3.23,7.73,4.13 +13.99,26.63,4.57,3.08,4.99,6.93,3.19,5.92,8.11,2.91,6.96,3.72 +14.6,27.62,4.44,2.95,4.89,6.91,3.27,5.78,8.1,2.96,7.01,3.51 +14.63,27.64,4.5,3.04,4.94,7.18,3.27,5.89,8.91,3.02,7.06,3.61 +14.77,27.9,4.56,3.05,5.08,7.31,3.31,5.94,9.8,3.06,7.08,3.88 +14.62,27.5,4.52,3.05,5.39,7.35,3.3,5.93,10.78,3.05,7.07,3.87 +14.5,28.67,4.59,3.13,5.35,7.53,3.32,6.06,11.86,3.13,7.15,3.9 +14.79,29.08,4.66,3.12,5.23,7.47,3.33,6.16,10.67,3.15,7.17,3.91 +14.77,29.08,4.67,3.14,5.26,7.48,3.38,6.18,11.36,3.17,7.21,3.95 +14.65,29.95,4.66,3.11,5.19,7.35,3.36,6.15,10.56,3.14,7.19,3.94 +15.03,30.8,4.72,3.07,5.18,7.33,3.34,6.11,9.56,3.15,7.29,3.96 +15.37,30.42,4.84,3.23,5.31,7.46,3.39,6.35,9.15,3.18,7.41,4.04 +15.2,29.7,4.81,3.3,5.33,7.47,3.39,6.34,9.11,3.17,7.4,4.06 +15.24,29.65,4.84,3.31,5.31,7.39,3.37,6.26,8.89,3.12,7.34,3.99 +15.59,29.85,4.88,3.3,5.38,7.47,3.42,6.44,8.36,3.15,7.42,4.04 +15.58,29.25,4.89,3.33,5.39,7.48,3.43,6.46,8.68,3.16,7.52,4.03 +15.23,28.9,4.82,3.31,5.41,8.06,3.37,6.41,8.77,3.12,7.41,3.97 +15.04,29.33,4.74,3.22,5.28,8.02,3.32,6.32,9.65,3.06,7.31,3.9 +14.99,30.11,4.84,3.31,5.3,8.01,3.36,6.32,9.11,3.13,7.51,3.9 +15.11,29.67,4.79,3.25,5.38,8.11,3.37,6.42,8.41,3.15,7.5,3.88 +14.5,29.59,4.63,3.12,5.12,7.87,3.3,6.15,8.4,3.08,7.18,3.76 \ No newline at end of file diff --git a/applications/portfolio_optimization/introduction_cn.ipynb b/applications/portfolio_optimization/introduction_cn.ipynb new file mode 100644 index 0000000..93cc646 --- /dev/null +++ b/applications/portfolio_optimization/introduction_cn.ipynb @@ -0,0 +1,253 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 量子金融投资组合优化简介\n", + "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*\n", + "\n", + "假如你是一位资产管理人,想要将数额为$K$的基金一次性投入到$N$个可投资的项目中,各项目都有自己的投资回报率和风险,你的目标就是在考虑到市场影响和交易费用的的基础上找到一个最佳的投资组合,使得该笔资产以最优的投资方案实施。\n", + "\n", + "为了方便建模,我们做如下两点假设:\n", + " 1.每个项目都是等额投资的;\n", + " 2.给定的预算是投资一个项目金额的整数倍,且必须全部花完。\n", + "\n", + "在投资组合的基本理论中,投资组合的总体风险与项目间的协方差有关,而协方差与任意两项目的相关系数成正比。相关系数越小,其协方差就越小,投资组合的总体风险也就越小。在这里我们给出了采用均值方差组合优化的方法下的该问题的建模方程:\n", + "$$\n", + "\\omega=\\max _{x \\in\\{0,1\\}^n} \\mu^T x-q x^T S x \\quad \\text { subject to: } 1^T x=B,\n", + "$$\n", + "该式子中各符号代表的含义如下:\n", + "- $x \\in \\{0, 1\\}^{n}$ 表示一个向量,其中每一个元素均为二进制变量,即如果资产$i$被投资了,则 $x_i$=1,如果没有被选择,则 $x_i=0$;\n", + "- $\\mu \\in \\mathbb{R}^n$ 表示投资每个项目的预期回报率;\n", + "- $S \\in \\mathbb{R}^{n \\times n}$ 表示各投资项目回报率之间的协方差矩阵;\n", + "- $q > 0$ 表示做出该投资决定的风险系数;\n", + "- $B$ 代表投资预算,即我们可以投资的项目数。\n", + "\n", + "让我们对这个方程的含义进行说明。$\\mu^T x$ 刻画 $x$ 代表的投资方案的预期收益。$x^T S x$ 刻画投资项目之间的关联性,乘上风险系数 $q$ 之后,代表该投资方案包含的风险。$1^T x=B$ 要求我们投资的项目数等于我们的预算总数。因此,当我们对所有的投资方案寻找等式右边的最大值,得到的 $\\omega$ 就是我们理论上可以得到的最大收益。\n", + "\n", + "为了方便寻找使收益最大化的投资组合,我们定义如下的损失函数:\n", + "$$\n", + "C_x=q \\sum_i \\sum_j S_{j i} x_i x_j-\\sum_i x_i \\mu_i+A\\left(B-\\sum_i x_i\\right)^2,\n", + "$$\n", + "其中,约束条件以拉格朗日乘子的形式进入方程。于是,我们的任务转化成寻找使损失函数最小的$x$。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 量子编码及求解\n", + "我们通过变换 $x_i \\mapsto \\frac{I-Z_i}{2}$ 将损失函数转为一个哈密顿量,从而完成投资组合优化问题的编码。这里$Z_i=I \\otimes I \\otimes \\ldots \\otimes Z \\otimes \\ldots \\otimes I$,即 $Z_{i}$ 是作用在第$i$ 个比特上的Pauli算符。我们用这个映射将 $C_x$ 转化成量子比特数为 $n$ 的系统的哈密顿矩阵 $H_C$,其基态即为投资组合优化问题的最优解。为了寻找这一哈密顿量的基态,我们使用变分量子算法的思想,通过一个参数化量子线路,生成一个试验态 $|\\theta^* \\rangle$。我们通过量子线路获得哈密顿量在该试验态上的期望值,然后,通过经典的梯度下降算法调节参数化量子线路的参数,使期望值向基态能量靠拢。重复若干次之后,我们找到最优解:\n", + "$$\n", + "|\\theta^* \\rangle = \\argmin_\\theta L(\\vec{\\theta})=\\argmin_\\theta \\left\\langle\\vec{\\theta}\\left|H_C\\right| \\vec{\\theta}\\right\\rangle.\n", + "$$\n", + "最后,我们读出测量结果的概率分布:$p(z)=\\left|\\left\\langle z \\mid \\vec{\\theta}^*\\right\\rangle\\right|^2$,即由量子编码还原出原先比特串的信息。某个比特串出现的概率越大,意味着其是投资组合优化问题最优解的可能性越大。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 使用教程\n", + "### 配置文件\n", + "我们给出了一个设置好参数,可以直接进行组合优化计算的配置文件。用户只需在`config.toml`里修改相应的参数,并在终端运行\n", + "`python qpo.py --config config.toml --logger qpo_log.log`,即可计算最优投资组合。\n", + "### 输出结果\n", + "运行结果将输出到文件 `qpo_log.log` 中。我们的优化过程将被记录在日志中。用户可以看到随着循环数的增加,损失大小的变化。最后我们会输出优化得到的方案选择。\n", + "\n", + "### 参数说明\n", + "- `stock`,默认为 `'demo'`,即使用我们提供的样本数据。也可选 `'random'` 或 `'custom'` 来随机生成或使用自定义数据。\n", + "若用户选择随机生成数据,用户可以通过修改 `start_time` 和 `end_time` 参数来选择股票数据的起止日期。对于自定义数据,用户可以使用格式和表头命名规则(即 `csv` 文件的第一行)与 `demo_stock.csv` 文件相同的自定义文件,并在配置文件修改该文件路径:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "custom_data_path = 'file_name.csv'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 在线演示\n", + "这里,我们给出一个在线演示的版本,可以在线进行测试。首先定义配置文件的内容:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "config_toml = r\"\"\"\n", + "# 用于计算金融组合优化问题模型的整体配置文件。\n", + "# 使用样例股票数据\n", + "stock = 'demo' \n", + "demo_data_path = 'demo_stock.csv'\n", + "# 可投资项目的数目\n", + "num_asset = 7\n", + "# 决策风险系数\n", + "risk_weight = 0.5\n", + "# 投资预算\n", + "budget = 0\n", + "# 投资惩罚\n", + "penalty = 0\n", + "# 量子电路深度\n", + "circuit_depth = 2\n", + "# 优化循环次数\n", + "iterations = 600\n", + "# 梯度下降优化的学习速率\n", + "learning_rate = 0.2 \n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "量桨 PaddleQuantum 的金融模块实现了量子金融优化的数值模拟。我们可以从 ``paddle_quantum.finance.qpo`` 模块里导入 ``portfolio_combination_optimization`` 来解决配置好的金融组合优化问题。" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 600/600 [01:15<00:00, 7.93it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "******************* 最优组合为: [2, 5, 6] *******************\n" + ] + } + ], + "source": [ + "import os\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\")\n", + "os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python'\n", + "import pandas as pd\n", + "\n", + "import toml\n", + "from paddle_quantum.finance.qpo import portfolio_combination_optimization\n", + "from paddle_quantum.finance import DataSimulator\n", + "\n", + "config = toml.loads(config_toml)\n", + "demo_data_path = config[\"demo_data_path\"]\n", + "num_asset = config[\"num_asset\"]\n", + "risk_weight = config[\"risk_weight\"]\n", + "budget = config[\"budget\"]\n", + "penalty = config[\"penalty\"]\n", + "circuit_depth = config[\"circuit_depth\"]\n", + "iterations = config[\"iterations\"]\n", + "learning_rate = config[\"learning_rate\"]\n", + "\n", + "stocks_name = [(\"STOCK%s\" % i) for i in range(num_asset)]\n", + "source_data = pd.read_csv(demo_data_path)\n", + "processed_data = [source_data['closePrice'+str(i)].tolist() for i in range(num_asset)]\n", + "data = DataSimulator(stocks_name)\n", + "data.set_data(processed_data)\n", + "\n", + "invest = portfolio_combination_optimization(num_asset, data, iterations, learning_rate, risk_weight, budget,\n", + " penalty, circuit=circuit_depth)\n", + "print(f\"******************* 最优组合为: {invest} *******************\")\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## 注意事项\n", + "若投资方案数较小(`num_asset`$< 12$),我们可以通过严格对角化哈密顿量来计算真实的损失最小值,并与优化的结果比较。若二者的差别较大,该优化结果不可靠,需要重新选择训练参数。\n", + "## 相关论文以及引用信息\n", + "```\n", + "@article{ORUS2019100028,\n", + "title = {Quantum computing for finance: Overview and prospects},\n", + "journal = {Reviews in Physics},\n", + "volume = {4},\n", + "pages = {100028},\n", + "year = {2019},\n", + "issn = {2405-4283},\n", + "doi = {https://doi.org/10.1016/j.revip.2019.100028},\n", + "url = {https://www.sciencedirect.com/science/article/pii/S2405428318300571},\n", + "author = {Román Orús and Samuel Mugel and Enrique Lizaso}\n", + "}\n", + "\n", + "@ARTICLE{2020arXiv200614510E,\n", + " author = {{Egger}, Daniel J. and {Gambella}, Claudio and {Marecek}, Jakub and {McFaddin}, Scott and {Mevissen}, Martin and {Raymond}, Rudy and {Simonetto}, Andrea and {Woerner}, Stefan and {Yndurain}, Elena},\n", + " title = \"{Quantum Computing for Finance: State of the Art and Future Prospects}\",\n", + " journal = {arXiv e-prints},\n", + " keywords = {Quantum Physics, Quantitative Finance - Statistical Finance},\n", + " year = 2020,\n", + " month = jun,\n", + " eid = {arXiv:2006.14510},\n", + " pages = {arXiv:2006.14510},\n", + "archivePrefix = {arXiv},\n", + " eprint = {2006.14510},\n", + " primaryClass = {quant-ph},\n", + " adsurl = {https://ui.adsabs.harvard.edu/abs/2020arXiv200614510E},\n", + " adsnote = {Provided by the SAO/NASA Astrophysics Data System}\n", + "}\n", + "\n", + "@article{10.2307/2975974,\n", + " ISSN = {00221082, 15406261},\n", + " URL = {http://www.jstor.org/stable/2975974},\n", + " author = {Harry Markowitz},\n", + " journal = {The Journal of Finance},\n", + " number = {1},\n", + " pages = {77--91},\n", + " publisher = {[American Finance Association, Wiley]},\n", + " title = {Portfolio Selection},\n", + " urldate = {2022-12-07},\n", + " volume = {7},\n", + " year = {1952}\n", + "}\n", + "```" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.8.13 ('pq')", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.13" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "d3caffbb123012c2d0622db402df9f37d80adc57c1cef1fdb856f61446d88d0a" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/applications/portfolio_optimization/introduction_en.ipynb b/applications/portfolio_optimization/introduction_en.ipynb new file mode 100644 index 0000000..975edc9 --- /dev/null +++ b/applications/portfolio_optimization/introduction_en.ipynb @@ -0,0 +1,266 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Introduction of quantum portfolio optimization\n", + "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*\n", + "\n", + "If you are an active investment manager who wants to invest $K$ dollars to $N$ projects, each with its return and risk, your goal is to find an optimal way to invest in the projects, taking into account the market impact and transaction costs.\n", + "\n", + "To make the modeling easy to formulate, two assumptions are made to constrain the problem:\n", + " 1.Each asset is invested with an equal amount of money;\n", + " 2.Budget is a multiple of each investment amount and must be fully spent.\n", + "\n", + "In the theory of portfolio optimization, the overall risk of a portfolio is related to the covariance between assets, which is proportional to the correlation coefficients of any two assets. The smaller the correlation coefficients, the smaller the covariance, and then the smaller the overall risk of the portfolio. Here we use the mean-variance approach to model this problem:\n", + "$$\n", + "\\omega=\\max _{x \\in\\{0,1\\}^n} \\mu^T x-q x^T S x \\quad \\text { subject to: } 1^T x=B,\n", + "$$\n", + "where each symbol has the following meaning:\n", + "- $x \\in \\{0, 1\\}^{n}$ denotes the vector of binary decision variables, which indicate which each assets is picked ($x_i$=1) or not ($x_i=0$)\n", + "- $\\mu \\in \\mathbb{R}^n$ defines the expected returns for the assets\n", + "- $S \\in \\mathbb{R}^{n \\times n}$ represents the covariances between the assets\n", + "- $q > 0$ represents the risk factor of investment decision making\n", + "- $B$ denotes the budget, i.e. the number of assets to be selected out of $N$\n", + "\n", + "Let us illustrate on the meaning of this equation. $\\mu^T x$ describes the expected benefit of the investment plan represented by $x$. $x^T S x$ describes the correlation between the projects, which, after producting with the risk coefficient $q$, represents the risk incorporated in the investment plan. The restriction $1^T x=B$ requires the number of invested projects equals to our total budget. Therefore, $\\omega$ represents the largest benefit we could get theoretically.\n", + "\n", + "In order to find the optimal investment plan more easily, we can define the loss function\n", + "$$\n", + "C_x=q \\sum_i \\sum_j S_{j i} x_i x_j-\\sum_i x_i \\mu_i+A\\left(B-\\sum_i x_i\\right)^2,\n", + "$$\n", + "where the restriction condition enters the function with the form of Lagrange multiplier. Therefore, our task becomes finding the investment plan $x$ that minimizes the loss $C_x$." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Quantum encoding and solution\n", + "\n", + "We now need to transform the cost function $C_x$ into a Hamiltonian to realize the encoding of the portfolio optimization problem. One just needs to do the following transformation:\n", + "$\n", + "x_i \\mapsto \\frac{I-Z_i}{2},\n", + "$\n", + "where $Z_i=I \\otimes I \\otimes \\ldots \\otimes Z \\otimes \\ldots \\otimes I$, i.e., $Z_{i}$ is the Pauli operator acting solely on the $i$-th qubit. Thus using the above mapping, we can transform the cost function $C_x$ into a Hamiltonian $H_C$ for the system of $n$ qubits, the ground state of which represents the solution of the portfolio optimization problem. In order to find the ground state, we use the idea of variational quantum algorithms. We implement a parametric quantum circuit, and use it to generate a trial state $|\\theta^* \\rangle$. We use the quantum circuit to measure the expectation value of the Hamiltonian on this state. Then, classical gradient descent algorithm is implemented to adjust the parameters of the parametric circuit, where the expectation value evolves towards the ground state energy. After some iterations, we arrive at the optimal value\n", + "$$\n", + "|\\theta^* \\rangle = \\argmin_\\theta L(\\vec{\\theta})=\\argmin_\\theta \\left\\langle\\vec{\\theta}\\left|H_C\\right| \\vec{\\theta}\\right\\rangle.\n", + "$$\n", + "\n", + "Finally, we read out the probability distribution from the measurement result (i.e. decoding the quantum problem to give information about the original bit string)\n", + "$\n", + "p(z)=\\left|\\left\\langle z \\mid \\vec{\\theta}^*\\right\\rangle\\right|^2.\n", + "$\n", + "In the case of quantum parameterized circuits with sufficient expressiveness, the greater the probability of a certain bit string, the greater the probability that it corresponds to an optimal solution to the portfolio optimization problem." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## User's guide\n", + "### Configuration file and input parameters\n", + "We provide a configuration file with previously chosen parameter. The user just needs to change the parameters in the `config.toml` file, and run `python qpo.py --config config.toml --logger qpo_log.log` in the terminal, to solve the portfolio optimization problem.\n", + "### Output\n", + "The results will be output to the `qpo_log.log` file. First of all, the process of optimization will be documented in the log. Users can see the evolution of loss function as the looping times increases. \n", + "### Parameters\n", + "- `stock`, default is `'demo'`, i.e., using the stock data we provide in the demo file. Users can switch to `'random'` or `'custom'` to generate random stock data or use custom stock data. If user chooses to generate data randomly, the parameters `start_time` and `endtime` can be altered to specify the start and end date of the stock data. If user chooses to use custom data, he or she can store the information of the stock in a csv file, and write in the configuration file:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "custom_data_path = 'file_name.csv'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Online demonstration\n", + "Here, we provide an online demonstration version. Firstly, we define the parameters in the configuration file:" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [], + "source": [ + "config_toml = r\"\"\"\n", + "# # The configuration file of quantum portfolio optimization problem.\n", + "# Use demo stock data\n", + "stock = 'demo' \n", + "demo_data_path = 'demo_stock.csv'\n", + "# Number of investable projects\n", + "num_asset = 7\n", + "# Risk of decision making\n", + "risk_weight = 0.5\n", + "# Budget\n", + "budget = 0\n", + "# Penalty\n", + "penalty = 0\n", + "# The depth of the quantum circuit\n", + "circuit_depth = 2\n", + "# Number of loop cycles used in the optimization\n", + "iterations = 600\n", + "# Learning rate of gradient descent\n", + "learning_rate = 0.2\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The finance module in PaddleQuantum realizes the numerical simulation of the quantum portfolio optimization problem." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 600/600 [01:04<00:00, 9.24it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "******************* The optimal investment plan is: [2, 5, 6] *******************\n" + ] + } + ], + "source": [ + "import os\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\")\n", + "os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python'\n", + "import pandas as pd\n", + "\n", + "import toml\n", + "from paddle_quantum.finance.qpo import portfolio_combination_optimization\n", + "from paddle_quantum.finance import DataSimulator\n", + "\n", + "config = toml.loads(config_toml)\n", + "demo_data_path = config[\"demo_data_path\"]\n", + "num_asset = config[\"num_asset\"]\n", + "risk_weight = config[\"risk_weight\"]\n", + "budget = config[\"budget\"]\n", + "penalty = config[\"penalty\"]\n", + "circuit_depth = config[\"circuit_depth\"]\n", + "iterations = config[\"iterations\"]\n", + "learning_rate = config[\"learning_rate\"]\n", + "\n", + "stocks_name = [(\"STOCK%s\" % i) for i in range(num_asset)]\n", + "source_data = pd.read_csv(demo_data_path)\n", + "processed_data = [source_data['closePrice'+str(i)].tolist() for i in range(num_asset)]\n", + "data = DataSimulator(stocks_name)\n", + "data.set_data(processed_data)\n", + "\n", + "invest = portfolio_combination_optimization(num_asset, data, iterations, learning_rate, risk_weight, budget,\n", + " penalty, circuit=circuit_depth)\n", + "print(f\"******************* The optimal investment plan is: {invest} *******************\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Note\n", + "If the number of investable projects is small (`num_asset`$< 12$), we can diagonalize the Hamiltonian exactly, and compare the real minimum loss value with that found by the optimization process. If the difference is large, the optimization result may be unreliable, and re-choosing of the training parameters might be necessary. Finally, we output the optimal investment plan." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## References\n", + "```\n", + "@article{ORUS2019100028,\n", + "title = {Quantum computing for finance: Overview and prospects},\n", + "journal = {Reviews in Physics},\n", + "volume = {4},\n", + "pages = {100028},\n", + "year = {2019},\n", + "issn = {2405-4283},\n", + "doi = {https://doi.org/10.1016/j.revip.2019.100028},\n", + "url = {https://www.sciencedirect.com/science/article/pii/S2405428318300571},\n", + "author = {Román Orús and Samuel Mugel and Enrique Lizaso}\n", + "}\n", + "\n", + "@ARTICLE{2020arXiv200614510E,\n", + " author = {{Egger}, Daniel J. and {Gambella}, Claudio and {Marecek}, Jakub and {McFaddin}, Scott and {Mevissen}, Martin and {Raymond}, Rudy and {Simonetto}, Andrea and {Woerner}, Stefan and {Yndurain}, Elena},\n", + " title = \"{Quantum Computing for Finance: State of the Art and Future Prospects}\",\n", + " journal = {arXiv e-prints},\n", + " keywords = {Quantum Physics, Quantitative Finance - Statistical Finance},\n", + " year = 2020,\n", + " month = jun,\n", + " eid = {arXiv:2006.14510},\n", + " pages = {arXiv:2006.14510},\n", + "archivePrefix = {arXiv},\n", + " eprint = {2006.14510},\n", + " primaryClass = {quant-ph},\n", + " adsurl = {https://ui.adsabs.harvard.edu/abs/2020arXiv200614510E},\n", + " adsnote = {Provided by the SAO/NASA Astrophysics Data System}\n", + "}\n", + "\n", + "@article{10.2307/2975974,\n", + " ISSN = {00221082, 15406261},\n", + " URL = {http://www.jstor.org/stable/2975974},\n", + " author = {Harry Markowitz},\n", + " journal = {The Journal of Finance},\n", + " number = {1},\n", + " pages = {77--91},\n", + " publisher = {[American Finance Association, Wiley]},\n", + " title = {Portfolio Selection},\n", + " urldate = {2022-12-07},\n", + " volume = {7},\n", + " year = {1952}\n", + "}\n", + "```" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.8.13 ('pq')", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.13" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "d3caffbb123012c2d0622db402df9f37d80adc57c1cef1fdb856f61446d88d0a" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/applications/portfolio_optimization/qpo.py b/applications/portfolio_optimization/qpo.py new file mode 100644 index 0000000..8c50388 --- /dev/null +++ b/applications/portfolio_optimization/qpo.py @@ -0,0 +1,100 @@ +# !/usr/bin/env python3 +# Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +Quantum portfolio optimization. +""" +import os +import sys +from typing import Dict +import logging +import argparse +import toml +import datetime +import pandas as pd + +from paddle_quantum.finance.qpo import portfolio_combination_optimization +from paddle_quantum.finance import DataSimulator + + +def main(args): + # logger configure + log_path = args.logger + logger = logging.Logger(name='logger_qpo') + logger_file_handler = logging.FileHandler(log_path) + logger_file_handler.setFormatter(logging.Formatter(r'%(levelname)s %(asctime)s %(message)s')) + logger_file_handler.setLevel(logging.INFO) + logger.addHandler(logger_file_handler) + logger.warning("------------------- Process starts -------------------") + + # data preparation + parsed_configs: Dict = toml.load(args.config) + num_asset = parsed_configs["stock_para"]["num_asset"] + + if parsed_configs['stock'] == 'demo': + stock_file_path = os.path.join(this_file_path, './demo_stock.csv') + stocks_name = [("STOCK%s" % i) for i in range(num_asset)] + source_data = pd.read_csv(stock_file_path) + processed_data = [source_data['closePrice'+str(i)].tolist() for i in range(num_asset)] + data = DataSimulator(stocks_name) + data.set_data(processed_data) + logger.warning(f"******************* {num_asset} stocks processed *******************") + + elif parsed_configs['stock'] == 'random': + stocks_name = [("STOCK%s" % i) for i in range(num_asset)] + data = DataSimulator(stocks=stocks_name, start=datetime.datetime( + *parsed_configs['random_data']['start_time']), end=datetime.datetime(*parsed_configs['random_data']['end_time'])) + data.randomly_generate() + logger.warning(f"******************* {num_asset} stocks randomly generated *******************") + + elif parsed_configs['stock'] == 'custom': + stock_file_path = parsed_configs["custom_data_path"] + stocks_name = [("STOCK%s" % i) for i in range(num_asset)] + source_data = pd.read_csv(stock_file_path) + processed_data = [source_data['closePrice'+str(i)].tolist() for i in range(num_asset)] + data = DataSimulator(stocks_name) + data.set_data(processed_data) + logger.warning(f"******************* {num_asset} stocks processed *******************") + + # load model parameters + risk_weight = parsed_configs["stock_para"]["risk_weight"] + budget = parsed_configs["stock_para"]["budget"] + penalty = parsed_configs["stock_para"]["penalty"] + circuit_depth = parsed_configs["train_para"]["circuit_depth"] + iters = parsed_configs["train_para"]["iterations"] + lr = parsed_configs["train_para"]["learning_rate"] + + # optimization + logger.warning("******************* Train starts *******************") + invest = portfolio_combination_optimization(num_asset, data, iters, lr, risk_weight, budget, + penalty, circuit=circuit_depth, logger=logger, compare=True) + logger.warning("******************* Train ends *******************") + logger.warning(f"******************* Output is {invest} *******************") + logger.warning("------------------- Process ends -------------------") + + +if __name__ == "__main__": + this_file_path = sys.path[0] + parser = argparse.ArgumentParser(description="Quantum chemistry task with paddle quantum.") + parser.add_argument( + "--config", default=os.path.join(this_file_path, './config.toml'), type=str, help="The path of toml format config file.") + parser.add_argument( + "--logger", default=os.path.join(this_file_path, './qpo_log.log'), type=str, help="The path of log file saved.") + main(parser.parse_args()) + + + + + diff --git a/applications/protein_folding/APRLRFY_3d_structure.jpg b/applications/protein_folding/APRLRFY_3d_structure.jpg new file mode 100644 index 0000000..fbf6554 Binary files /dev/null and b/applications/protein_folding/APRLRFY_3d_structure.jpg differ diff --git a/applications/protein_folding/config.toml b/applications/protein_folding/config.toml new file mode 100644 index 0000000..1f60d44 --- /dev/null +++ b/applications/protein_folding/config.toml @@ -0,0 +1,40 @@ +# The configuration file for protein folding problem + +# The amino acids sequence that form a protein. +# Valid amino acid labels are (https://en.wikipedia.org/wiki/Amino_acid): +# C: Cysteine +# M: Methionine +# F: Phenylalanine +# I: Isoleucine +# L: Leucine +# V: Valine +# W: Tryptophan +# Y: Tyrosine +# A: Alanine +# G: Glycine +# T: Threonine +# S: Serine +# N: Asparagine +# Q: Glutamine +# D: Aspartate +# E: Glutamate +# H: Histidine +# R: Arginine +# K: Lysine +# P: Proline +# NOTE: the more amino acids in the sequence, the longer program will run +# NOTE: the example below takes approximately 0.5h! +amino_acids = ["A", "P", "R", "L", "R", "F", "Y"] +# Pair of indices indicates the potentially interact amino acide pair, below indicates that +# the 0-th and 5-th acids will interact and 1-th and 6-th acids will interact. +possible_contractions = [[0, 5], [1, 6]] +# Depth of the quantum circuit used in VQE +depth = 1 +# Number of VQE iterations +num_iterations = 200 +# The condition for VQE convergence +tol = 1e-3 +# The number of steps between two consecutive loss records +save_every = 10 +# learning rate for the optimizer +learning_rate = 0.5 \ No newline at end of file diff --git a/applications/protein_folding/folding_protein.py b/applications/protein_folding/folding_protein.py new file mode 100644 index 0000000..fea56e2 --- /dev/null +++ b/applications/protein_folding/folding_protein.py @@ -0,0 +1,86 @@ +# !/usr/bin/env python3 +# Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import toml +import time +import os +import warnings +import logging +from paddle import optimizer as paddle_optimizer +from paddle_quantum.ansatz import Circuit +from paddle_quantum.biocomputing import Protein +from paddle_quantum.biocomputing import ProteinFoldingSolver +from paddle_quantum.biocomputing import visualize_protein_structure + +warnings.filterwarnings('ignore') +os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python' +logging.basicConfig(filename="log", filemode="w", level=logging.INFO, format="%(message)s") + + +def circuit(num_qubits: int, depth: int) -> Circuit: + r"""Ansatz used in protein folding VQE. + """ + cir = Circuit(num_qubits) + cir.superposition_layer() + for _ in range(depth): + cir.ry() + cir.cx() + cir.ry() + return cir + + +def main(args): + time_start = time.strftime("%Y%m%d-%H:%M:%S", time.localtime()) + logging.info(f"Job start at {time_start:s}") + + # construct the protein + parsed_configs = toml.load(args.config) + aa_seq = parsed_configs["amino_acids"] + contact_pairs = parsed_configs["possible_contactions"] + num_aa = len(aa_seq) + protein = Protein("".join(aa_seq), {(0, 1): 1, (1, 2): 0, (num_aa-2, num_aa-1): 3}, contact_pairs) + + # build the solver + cir_depth = parsed_configs["depth"] + cir = circuit(protein.num_qubits, cir_depth) + + penalty_factors = [10.0, 10.0] + alpha = 0.5 + optimizer = paddle_optimizer.Adam + num_iterations = parsed_configs["num_iterations"] + tol = parsed_configs["tol"] + save_every = parsed_configs["save_every"] + learning_rate = parsed_configs["learning_rate"] + problem = ProteinFoldingSolver(penalty_factors, alpha, optimizer, num_iterations, tol, save_every) + _, protein_str = problem.solve(protein, cir, learning_rate=learning_rate) + + # parse results & plot the 3d structure of protein + num_config_qubits = protein.num_config_qubits + bond_directions = [1, 0] + bond_directions.extend(int(protein_str[slice(i, i + 2)], 2) for i in range(0, num_config_qubits, 2)) + bond_directions.append(3) + visualize_protein_structure(aa_seq, bond_directions) + + logging.info("\n#######################################\nSummary\n#######################################") + logging.info(f"Protein bonds direction: {bond_directions}.") + time_stop = time.strftime("%Y%m%d-%H:%M:%S", time.localtime()) + logging.info(f"\nJob end at {time_stop:s}\n") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Protein folding task with paddle quantum.") + parser.add_argument("--config", type=str, help="Input the config file with toml format.") + main(parser.parse_args()) diff --git a/applications/protein_folding/introduction_cn.ipynb b/applications/protein_folding/introduction_cn.ipynb new file mode 100644 index 0000000..b59a0b5 --- /dev/null +++ b/applications/protein_folding/introduction_cn.ipynb @@ -0,0 +1,1640 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import warnings\n", + "\n", + "warnings.filterwarnings(\"ignore\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 蛋白质功能与结构设计简介\n", + "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*\n", + "\n", + "蛋白质是生物体中重要的结构和功能分子,它们通过组装氨基酸形成长链,并通过氨基酸链的空间结构来实现特定的功能。蛋白质的空间结构指的是氨基酸链在三维空间中的构型。这种构型可以通过X射线衍射、核磁共振或其它方法测定。研究表明,蛋白质的空间结构与其功能密切相关。例如,酶作为一类重要的蛋白质,其空间结构决定了其与底物的相互作用,从而实现了化学反应的催化作用。此外,蛋白质的空间结构还可以通过改变氨基酸序列来控制其功能。\n", + "\n", + "蛋白质在不同的折叠方式下会产生不同的空间结构,因此研究蛋白质折叠对于研究蛋白质功能有重要意义。由于蛋白质的结构和功能问题具有高度的复杂性和非线性性,目前用经典计算求解蛋白质折叠问题的效率很低。量子计算可以通过量子力学原理来快速求解复杂的非线性优化问题,普遍认为将会在未来对蛋白质折叠方面的研究起到极大的帮助。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 利用量子计算方法模拟蛋白质折叠过程\n", + "### 晶格模型\n", + "在蛋白质结构研究中,一种常用的方法是晶格模型\\[1\\],它将蛋白质中的氨基酸链划分为一系列晶格单元,每个晶格单元包括若干个氨基酸。通过对晶格单元之间相互作用的分析,我们可以推测出蛋白质的空间结构。晶格模型可以帮助我们更好地理解蛋白质的结构与功能,为药物设计和治疗疾病提供重要的理论依据。\n", + "![](lattice_model_demo.jpg)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Protein with 7 nodes and 6 edges\n" + ] + } + ], + "source": [ + "from paddle_quantum.biocomputing import Protein\n", + "\n", + "protein = Protein(\"APRLRFY\")\n", + "print(protein)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "利用 `paddle_quantum` 的 `biocomputing` 模块, 我们可以很容易地完成一个蛋白质的构建,上面的代码就构造了一个包含7个氨基酸的氨基酸链。\n", + "\n", + "从晶格模型出发,我们可以根据格点之间的相互作用得到一个蛋白质的哈密顿量\\[2\\],我们可以基于这个哈密顿量构造变分量子算法(VQE)来求解蛋白质分子的稳定结构(能量最低时的结构)。" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "327.0085 I\n", + "-47.5 Z1, Z3\n", + "65.0 Z1, Z5\n", + "-50.0 Z1, Z7\n", + "52.5 Z1, Z9\n", + "-47.5 Z0, Z2\n", + "65.0 Z0, Z4\n", + "-50.0 Z0, Z6\n", + "52.5 Z0, Z8\n", + "-47.5 Z0, Z1, Z2, Z3\n", + "65.0 Z0, Z1, Z4, Z5\n", + "-50.0 Z0, Z1, Z6, Z7\n", + "52.5 Z0, Z1, Z8, Z9\n", + "-65.0 Z3, Z5\n", + "92.5 Z3, Z7\n", + "-60.0 Z3, Z9\n", + "-65.0 Z2, Z4\n", + "92.5 Z2, Z6\n", + "-60.0 Z2, Z8\n", + "-65.0 Z2, Z3, Z4, Z5\n", + "92.5 Z2, Z3, Z6, Z7\n", + "-60.0 Z2, Z3, Z8, Z9\n", + "-72.5 Z5, Z7\n", + "92.5 Z5, Z9\n", + "-72.5 Z4, Z6\n", + "92.5 Z4, Z8\n", + "-72.5 Z4, Z5, Z6, Z7\n", + "92.5 Z4, Z5, Z8, Z9\n", + "-65.0 Z7, Z9\n", + "-65.0 Z6, Z8\n", + "-65.0 Z6, Z7, Z8, Z9\n", + "-157.168 Z12\n", + "30.0 Z1, Z3, Z12\n", + "-40.0 Z1, Z5, Z12\n", + "30.0 Z1, Z7, Z12\n", + "-27.5 Z1, Z9, Z12\n", + "30.0 Z0, Z2, Z12\n", + "-40.0 Z0, Z4, Z12\n", + "30.0 Z0, Z6, Z12\n", + "-27.5 Z0, Z8, Z12\n", + "30.0 Z0, Z1, Z2, Z3, Z12\n", + "-40.0 Z0, Z1, Z4, Z5, Z12\n", + "30.0 Z0, Z1, Z6, Z7, Z12\n", + "-27.5 Z0, Z1, Z8, Z9, Z12\n", + "37.5 Z3, Z5, Z12\n", + "-52.5 Z3, Z7, Z12\n", + "30.0 Z3, Z9, Z12\n", + "37.5 Z2, Z4, Z12\n", + "-52.5 Z2, Z6, Z12\n", + "30.0 Z2, Z8, Z12\n", + "37.5 Z2, Z3, Z4, Z5, Z12\n", + "-52.5 Z2, Z3, Z6, Z7, Z12\n", + "30.0 Z2, Z3, Z8, Z9, Z12\n", + "37.5 Z5, Z7, Z12\n", + "-40.0 Z5, Z9, Z12\n", + "37.5 Z4, Z6, Z12\n", + "-40.0 Z4, Z8, Z12\n", + "37.5 Z4, Z5, Z6, Z7, Z12\n", + "-40.0 Z4, Z5, Z8, Z9, Z12\n", + "30.0 Z7, Z9, Z12\n", + "30.0 Z6, Z8, Z12\n", + "30.0 Z6, Z7, Z8, Z9, Z12\n", + "-12.5 Z2, Z3, Z5, Z6\n", + "-10.0 Z2, Z3, Z5, Z8\n", + "-12.5 Z2, Z5, Z6, Z7\n", + "-10.0 Z2, Z5, Z8, Z9\n", + "-12.5 Z3, Z4, Z5, Z6\n", + "-10.0 Z3, Z4, Z5, Z8\n", + "-12.5 Z3, Z4, Z6, Z7\n", + "-10.0 Z3, Z4, Z8, Z9\n", + "30.0 Z3, Z5, Z7, Z9\n", + "10.0 Z3, Z5, Z6, Z8\n", + "10.0 Z3, Z5, Z6, Z7, Z8, Z9\n", + "-12.5 Z2, Z3, Z4, Z7\n", + "-10.0 Z2, Z3, Z7, Z8\n", + "-12.5 Z2, Z4, Z5, Z7\n", + "-10.0 Z2, Z7, Z8, Z9\n", + "10.0 Z3, Z4, Z7, Z8\n", + "10.0 Z3, Z4, Z5, Z7, Z8, Z9\n", + "-10.0 Z3, Z6, Z7, Z8\n", + "-10.0 Z3, Z6, Z8, Z9\n", + "-10.0 Z2, Z3, Z4, Z9\n", + "-10.0 Z2, Z3, Z6, Z9\n", + "-10.0 Z2, Z4, Z5, Z9\n", + "-10.0 Z2, Z6, Z7, Z9\n", + "10.0 Z3, Z4, Z6, Z9\n", + "10.0 Z3, Z4, Z5, Z6, Z7, Z9\n", + "10.0 Z2, Z4, Z7, Z9\n", + "30.0 Z2, Z4, Z6, Z8\n", + "10.0 Z2, Z4, Z6, Z7, Z8, Z9\n", + "10.0 Z2, Z5, Z6, Z9\n", + "10.0 Z2, Z4, Z5, Z6, Z8, Z9\n", + "10.0 Z2, Z5, Z7, Z8\n", + "10.0 Z2, Z4, Z5, Z6, Z7, Z8\n", + "10.0 Z2, Z3, Z4, Z5, Z7, Z9\n", + "10.0 Z2, Z3, Z4, Z5, Z6, Z8\n", + "30.0 Z2, Z3, Z4, Z5, Z6, Z7, Z8, Z9\n", + "10.0 Z2, Z3, Z5, Z6, Z7, Z9\n", + "10.0 Z2, Z3, Z4, Z6, Z7, Z8\n", + "10.0 Z2, Z3, Z5, Z7, Z8, Z9\n", + "10.0 Z2, Z3, Z4, Z6, Z8, Z9\n", + "-12.5 Z4, Z5, Z7, Z8\n", + "-12.5 Z4, Z7, Z8, Z9\n", + "-12.5 Z5, Z6, Z7, Z8\n", + "-12.5 Z5, Z6, Z8, Z9\n", + "-12.5 Z4, Z5, Z6, Z9\n", + "-12.5 Z4, Z6, Z7, Z9\n", + "7.5 Z2, Z3, Z5, Z6, Z12\n", + "5.0 Z2, Z3, Z5, Z8, Z12\n", + "7.5 Z2, Z5, Z6, Z7, Z12\n", + "5.0 Z2, Z5, Z8, Z9, Z12\n", + "7.5 Z3, Z4, Z5, Z6, Z12\n", + "5.0 Z3, Z4, Z5, Z8, Z12\n", + "7.5 Z3, Z4, Z6, Z7, Z12\n", + "5.0 Z3, Z4, Z8, Z9, Z12\n", + "-15.0 Z3, Z5, Z7, Z9, Z12\n", + "-5.0 Z3, Z5, Z6, Z8, Z12\n", + "-5.0 Z3, Z5, Z6, Z7, Z8, Z9, Z12\n", + "7.5 Z2, Z3, Z4, Z7, Z12\n", + "5.0 Z2, Z3, Z7, Z8, Z12\n", + "7.5 Z2, Z4, Z5, Z7, Z12\n", + "5.0 Z2, Z7, Z8, Z9, Z12\n", + "-5.0 Z3, Z4, Z7, Z8, Z12\n", + "-5.0 Z3, Z4, Z5, Z7, Z8, Z9, Z12\n", + "5.0 Z3, Z6, Z7, Z8, Z12\n", + "5.0 Z3, Z6, Z8, Z9, Z12\n", + "5.0 Z2, Z3, Z4, Z9, Z12\n", + "5.0 Z2, Z3, Z6, Z9, Z12\n", + "5.0 Z2, Z4, Z5, Z9, Z12\n", + "5.0 Z2, Z6, Z7, Z9, Z12\n", + "-5.0 Z3, Z4, Z6, Z9, Z12\n", + "-5.0 Z3, Z4, Z5, Z6, Z7, Z9, Z12\n", + "-5.0 Z2, Z4, Z7, Z9, Z12\n", + "-15.0 Z2, Z4, Z6, Z8, Z12\n", + "-5.0 Z2, Z4, Z6, Z7, Z8, Z9, Z12\n", + "-5.0 Z2, Z5, Z6, Z9, Z12\n", + "-5.0 Z2, Z4, Z5, Z6, Z8, Z9, Z12\n", + "-5.0 Z2, Z5, Z7, Z8, Z12\n", + "-5.0 Z2, Z4, Z5, Z6, Z7, Z8, Z12\n", + "-5.0 Z2, Z3, Z4, Z5, Z7, Z9, Z12\n", + "-5.0 Z2, Z3, Z4, Z5, Z6, Z8, Z12\n", + "-15.0 Z2, Z3, Z4, Z5, Z6, Z7, Z8, Z9, Z12\n", + "-5.0 Z2, Z3, Z5, Z6, Z7, Z9, Z12\n", + "-5.0 Z2, Z3, Z4, Z6, Z7, Z8, Z12\n", + "-5.0 Z2, Z3, Z5, Z7, Z8, Z9, Z12\n", + "-5.0 Z2, Z3, Z4, Z6, Z8, Z9, Z12\n", + "5.0 Z4, Z5, Z7, Z8, Z12\n", + "5.0 Z4, Z7, Z8, Z9, Z12\n", + "5.0 Z5, Z6, Z7, Z8, Z12\n", + "5.0 Z5, Z6, Z8, Z9, Z12\n", + "5.0 Z4, Z5, Z6, Z9, Z12\n", + "5.0 Z4, Z6, Z7, Z9, Z12\n", + "-7.5 Z0, Z1, Z3, Z4\n", + "-7.5 Z0, Z1, Z3, Z6\n", + "-7.5 Z0, Z3, Z4, Z5\n", + "-7.5 Z0, Z3, Z6, Z7\n", + "-7.5 Z1, Z2, Z3, Z4\n", + "-7.5 Z1, Z2, Z3, Z6\n", + "-7.5 Z1, Z2, Z4, Z5\n", + "-7.5 Z1, Z2, Z6, Z7\n", + "22.5 Z1, Z3, Z5, Z7\n", + "7.5 Z1, Z3, Z4, Z6\n", + "7.5 Z1, Z3, Z4, Z5, Z6, Z7\n", + "-7.5 Z0, Z1, Z2, Z5\n", + "-7.5 Z0, Z1, Z5, Z6\n", + "-7.5 Z0, Z2, Z3, Z5\n", + "-7.5 Z0, Z5, Z6, Z7\n", + "7.5 Z1, Z2, Z5, Z6\n", + "7.5 Z1, Z2, Z3, Z5, Z6, Z7\n", + "-7.5 Z1, Z4, Z5, Z6\n", + "-7.5 Z1, Z4, Z6, Z7\n", + "-7.5 Z0, Z1, Z2, Z7\n", + "-7.5 Z0, Z1, Z4, Z7\n", + "-7.5 Z0, Z2, Z3, Z7\n", + "-7.5 Z0, Z4, Z5, Z7\n", + "7.5 Z1, Z2, Z4, Z7\n", + "7.5 Z1, Z2, Z3, Z4, Z5, Z7\n", + "7.5 Z0, Z2, Z5, Z7\n", + "22.5 Z0, Z2, Z4, Z6\n", + "7.5 Z0, Z2, Z4, Z5, Z6, Z7\n", + "7.5 Z0, Z3, Z4, Z7\n", + "7.5 Z0, Z2, Z3, Z4, Z6, Z7\n", + "7.5 Z0, Z3, Z5, Z6\n", + "7.5 Z0, Z2, Z3, Z4, Z5, Z6\n", + "7.5 Z0, Z1, Z2, Z3, Z5, Z7\n", + "7.5 Z0, Z1, Z2, Z3, Z4, Z6\n", + "22.5 Z0, Z1, Z2, Z3, Z4, Z5, Z6, Z7\n", + "7.5 Z0, Z1, Z3, Z4, Z5, Z7\n", + "7.5 Z0, Z1, Z2, Z4, Z5, Z6\n", + "7.5 Z0, Z1, Z3, Z5, Z6, Z7\n", + "7.5 Z0, Z1, Z2, Z4, Z6, Z7\n", + "5.0 Z0, Z1, Z3, Z4, Z12\n", + "5.0 Z0, Z1, Z3, Z6, Z12\n", + "5.0 Z0, Z3, Z4, Z5, Z12\n", + "5.0 Z0, Z3, Z6, Z7, Z12\n", + "5.0 Z1, Z2, Z3, Z4, Z12\n", + "5.0 Z1, Z2, Z3, Z6, Z12\n", + "5.0 Z1, Z2, Z4, Z5, Z12\n", + "5.0 Z1, Z2, Z6, Z7, Z12\n", + "-15.0 Z1, Z3, Z5, Z7, Z12\n", + "-5.0 Z1, Z3, Z4, Z6, Z12\n", + "-5.0 Z1, Z3, Z4, Z5, Z6, Z7, Z12\n", + "5.0 Z0, Z1, Z2, Z5, Z12\n", + "5.0 Z0, Z1, Z5, Z6, Z12\n", + "5.0 Z0, Z2, Z3, Z5, Z12\n", + "5.0 Z0, Z5, Z6, Z7, Z12\n", + "-5.0 Z1, Z2, Z5, Z6, Z12\n", + "-5.0 Z1, Z2, Z3, Z5, Z6, Z7, Z12\n", + "5.0 Z1, Z4, Z5, Z6, Z12\n", + "5.0 Z1, Z4, Z6, Z7, Z12\n", + "5.0 Z0, Z1, Z2, Z7, Z12\n", + "5.0 Z0, Z1, Z4, Z7, Z12\n", + "5.0 Z0, Z2, Z3, Z7, Z12\n", + "5.0 Z0, Z4, Z5, Z7, Z12\n", + "-5.0 Z1, Z2, Z4, Z7, Z12\n", + "-5.0 Z1, Z2, Z3, Z4, Z5, Z7, Z12\n", + "-5.0 Z0, Z2, Z5, Z7, Z12\n", + "-15.0 Z0, Z2, Z4, Z6, Z12\n", + "-5.0 Z0, Z2, Z4, Z5, Z6, Z7, Z12\n", + "-5.0 Z0, Z3, Z4, Z7, Z12\n", + "-5.0 Z0, Z2, Z3, Z4, Z6, Z7, Z12\n", + "-5.0 Z0, Z3, Z5, Z6, Z12\n", + "-5.0 Z0, Z2, Z3, Z4, Z5, Z6, Z12\n", + "-5.0 Z0, Z1, Z2, Z3, Z5, Z7, Z12\n", + "-5.0 Z0, Z1, Z2, Z3, Z4, Z6, Z12\n", + "-15.0 Z0, Z1, Z2, Z3, Z4, Z5, Z6, Z7, Z12\n", + "-5.0 Z0, Z1, Z3, Z4, Z5, Z7, Z12\n", + "-5.0 Z0, Z1, Z2, Z4, Z5, Z6, Z12\n", + "-5.0 Z0, Z1, Z3, Z5, Z6, Z7, Z12\n", + "-5.0 Z0, Z1, Z2, Z4, Z6, Z7, Z12\n", + "-40.0 Z1, Z11\n", + "-40.0 Z0, Z10\n", + "-40.0 Z0, Z1, Z10, Z11\n", + "52.5 Z3, Z11\n", + "52.5 Z2, Z10\n", + "52.5 Z2, Z3, Z10, Z11\n", + "-50.0 Z5, Z11\n", + "-50.0 Z4, Z10\n", + "-50.0 Z4, Z5, Z10, Z11\n", + "65.0 Z7, Z11\n", + "65.0 Z6, Z10\n", + "65.0 Z6, Z7, Z10, Z11\n", + "-47.5 Z9, Z11\n", + "-47.5 Z8, Z10\n", + "-47.5 Z8, Z9, Z10, Z11\n", + "-5.0 Z0, Z1, Z3, Z8\n", + "-5.0 Z0, Z1, Z3, Z10\n", + "-5.0 Z0, Z3, Z8, Z9\n", + "-5.0 Z0, Z3, Z10, Z11\n", + "-5.0 Z1, Z2, Z3, Z8\n", + "-5.0 Z1, Z2, Z3, Z10\n", + "-5.0 Z1, Z2, Z8, Z9\n", + "-5.0 Z1, Z2, Z10, Z11\n", + "-15.0 Z1, Z3, Z5, Z9\n", + "15.0 Z1, Z3, Z5, Z11\n", + "-5.0 Z1, Z3, Z4, Z8\n", + "5.0 Z1, Z3, Z4, Z10\n", + "-5.0 Z1, Z3, Z4, Z5, Z8, Z9\n", + "5.0 Z1, Z3, Z4, Z5, Z10, Z11\n", + "15.0 Z1, Z3, Z7, Z9\n", + "-15.0 Z1, Z3, Z7, Z11\n", + "5.0 Z1, Z3, Z6, Z8\n", + "-5.0 Z1, Z3, Z6, Z10\n", + "5.0 Z1, Z3, Z6, Z7, Z8, Z9\n", + "-5.0 Z1, Z3, Z6, Z7, Z10, Z11\n", + "15.0 Z1, Z3, Z9, Z11\n", + "5.0 Z1, Z3, Z8, Z10\n", + "5.0 Z1, Z3, Z8, Z9, Z10, Z11\n", + "15.0 Z0, Z1, Z5, Z8\n", + "-5.0 Z0, Z1, Z5, Z10\n", + "15.0 Z0, Z5, Z8, Z9\n", + "-5.0 Z0, Z5, Z10, Z11\n", + "-5.0 Z1, Z2, Z5, Z8\n", + "5.0 Z1, Z2, Z5, Z10\n", + "-5.0 Z1, Z2, Z3, Z5, Z8, Z9\n", + "5.0 Z1, Z2, Z3, Z5, Z10, Z11\n", + "15.0 Z1, Z4, Z5, Z8\n", + "-5.0 Z1, Z4, Z5, Z10\n", + "15.0 Z1, Z4, Z8, Z9\n", + "-5.0 Z1, Z4, Z10, Z11\n", + "-15.0 Z1, Z5, Z7, Z9\n", + "15.0 Z1, Z5, Z7, Z11\n", + "-5.0 Z1, Z5, Z6, Z8\n", + "5.0 Z1, Z5, Z6, Z10\n", + "-5.0 Z1, Z5, Z6, Z7, Z8, Z9\n", + "5.0 Z1, Z5, Z6, Z7, Z10, Z11\n", + "-15.0 Z1, Z5, Z9, Z11\n", + "-5.0 Z1, Z5, Z8, Z10\n", + "-5.0 Z1, Z5, Z8, Z9, Z10, Z11\n", + "-5.0 Z0, Z1, Z7, Z8\n", + "-5.0 Z0, Z1, Z7, Z10\n", + "-5.0 Z0, Z7, Z8, Z9\n", + "-5.0 Z0, Z7, Z10, Z11\n", + "5.0 Z1, Z2, Z7, Z8\n", + "-5.0 Z1, Z2, Z7, Z10\n", + "5.0 Z1, Z2, Z3, Z7, Z8, Z9\n", + "-5.0 Z1, Z2, Z3, Z7, Z10, Z11\n", + "-5.0 Z1, Z4, Z7, Z8\n", + "5.0 Z1, Z4, Z7, Z10\n", + "-5.0 Z1, Z4, Z5, Z7, Z8, Z9\n", + "5.0 Z1, Z4, Z5, Z7, Z10, Z11\n", + "-5.0 Z1, Z6, Z7, Z8\n", + "-5.0 Z1, Z6, Z7, Z10\n", + "-5.0 Z1, Z6, Z8, Z9\n", + "-5.0 Z1, Z6, Z10, Z11\n", + "15.0 Z1, Z7, Z9, Z11\n", + "5.0 Z1, Z7, Z8, Z10\n", + "5.0 Z1, Z7, Z8, Z9, Z10, Z11\n", + "-5.0 Z0, Z1, Z2, Z9\n", + "15.0 Z0, Z1, Z4, Z9\n", + "-5.0 Z0, Z1, Z6, Z9\n", + "-5.0 Z0, Z1, Z9, Z10\n", + "-5.0 Z0, Z2, Z3, Z9\n", + "15.0 Z0, Z4, Z5, Z9\n", + "-5.0 Z0, Z6, Z7, Z9\n", + "-5.0 Z0, Z9, Z10, Z11\n", + "-5.0 Z1, Z2, Z4, Z9\n", + "5.0 Z1, Z2, Z6, Z9\n", + "5.0 Z1, Z2, Z9, Z10\n", + "-5.0 Z1, Z2, Z3, Z4, Z5, Z9\n", + "5.0 Z1, Z2, Z3, Z6, Z7, Z9\n", + "5.0 Z1, Z2, Z3, Z9, Z10, Z11\n", + "-5.0 Z1, Z4, Z6, Z9\n", + "-5.0 Z1, Z4, Z9, Z10\n", + "-5.0 Z1, Z4, Z5, Z6, Z7, Z9\n", + "-5.0 Z1, Z4, Z5, Z9, Z10, Z11\n", + "5.0 Z1, Z6, Z9, Z10\n", + "5.0 Z1, Z6, Z7, Z9, Z10, Z11\n", + "-5.0 Z1, Z8, Z9, Z10\n", + "-5.0 Z1, Z8, Z10, Z11\n", + "-5.0 Z0, Z1, Z2, Z11\n", + "-5.0 Z0, Z1, Z4, Z11\n", + "-5.0 Z0, Z1, Z6, Z11\n", + "-5.0 Z0, Z1, Z8, Z11\n", + "-5.0 Z0, Z2, Z3, Z11\n", + "-5.0 Z0, Z4, Z5, Z11\n", + "-5.0 Z0, Z6, Z7, Z11\n", + "-5.0 Z0, Z8, Z9, Z11\n", + "5.0 Z1, Z2, Z4, Z11\n", + "-5.0 Z1, Z2, Z6, Z11\n", + "5.0 Z1, Z2, Z8, Z11\n", + "5.0 Z1, Z2, Z3, Z4, Z5, Z11\n", + "-5.0 Z1, Z2, Z3, Z6, Z7, Z11\n", + "5.0 Z1, Z2, Z3, Z8, Z9, Z11\n", + "5.0 Z1, Z4, Z6, Z11\n", + "-5.0 Z1, Z4, Z8, Z11\n", + "5.0 Z1, Z4, Z5, Z6, Z7, Z11\n", + "-5.0 Z1, Z4, Z5, Z8, Z9, Z11\n", + "5.0 Z1, Z6, Z8, Z11\n", + "5.0 Z1, Z6, Z7, Z8, Z9, Z11\n", + "-5.0 Z0, Z2, Z5, Z9\n", + "5.0 Z0, Z2, Z5, Z11\n", + "-15.0 Z0, Z2, Z4, Z8\n", + "15.0 Z0, Z2, Z4, Z10\n", + "-5.0 Z0, Z2, Z4, Z5, Z8, Z9\n", + "5.0 Z0, Z2, Z4, Z5, Z10, Z11\n", + "5.0 Z0, Z2, Z7, Z9\n", + "-5.0 Z0, Z2, Z7, Z11\n", + "15.0 Z0, Z2, Z6, Z8\n", + "-15.0 Z0, Z2, Z6, Z10\n", + "5.0 Z0, Z2, Z6, Z7, Z8, Z9\n", + "-5.0 Z0, Z2, Z6, Z7, Z10, Z11\n", + "5.0 Z0, Z2, Z9, Z11\n", + "15.0 Z0, Z2, Z8, Z10\n", + "5.0 Z0, Z2, Z8, Z9, Z10, Z11\n", + "-5.0 Z0, Z3, Z4, Z9\n", + "5.0 Z0, Z3, Z4, Z11\n", + "-5.0 Z0, Z2, Z3, Z4, Z8, Z9\n", + "5.0 Z0, Z2, Z3, Z4, Z10, Z11\n", + "-5.0 Z0, Z4, Z7, Z9\n", + "5.0 Z0, Z4, Z7, Z11\n", + "-15.0 Z0, Z4, Z6, Z8\n", + "15.0 Z0, Z4, Z6, Z10\n", + "-5.0 Z0, Z4, Z6, Z7, Z8, Z9\n", + "5.0 Z0, Z4, Z6, Z7, Z10, Z11\n", + "-5.0 Z0, Z4, Z9, Z11\n", + "-15.0 Z0, Z4, Z8, Z10\n", + "-5.0 Z0, Z4, Z8, Z9, Z10, Z11\n", + "5.0 Z0, Z3, Z6, Z9\n", + "-5.0 Z0, Z3, Z6, Z11\n", + "5.0 Z0, Z2, Z3, Z6, Z8, Z9\n", + "-5.0 Z0, Z2, Z3, Z6, Z10, Z11\n", + "-5.0 Z0, Z5, Z6, Z9\n", + "5.0 Z0, Z5, Z6, Z11\n", + "-5.0 Z0, Z4, Z5, Z6, Z8, Z9\n", + "5.0 Z0, Z4, Z5, Z6, Z10, Z11\n", + "5.0 Z0, Z6, Z9, Z11\n", + "15.0 Z0, Z6, Z8, Z10\n", + "5.0 Z0, Z6, Z8, Z9, Z10, Z11\n", + "-5.0 Z0, Z3, Z5, Z8\n", + "5.0 Z0, Z3, Z7, Z8\n", + "5.0 Z0, Z3, Z8, Z11\n", + "-5.0 Z0, Z2, Z3, Z4, Z5, Z8\n", + "5.0 Z0, Z2, Z3, Z6, Z7, Z8\n", + "5.0 Z0, Z2, Z3, Z8, Z10, Z11\n", + "-5.0 Z0, Z5, Z7, Z8\n", + "-5.0 Z0, Z5, Z8, Z11\n", + "-5.0 Z0, Z4, Z5, Z6, Z7, Z8\n", + "-5.0 Z0, Z4, Z5, Z8, Z10, Z11\n", + "5.0 Z0, Z7, Z8, Z11\n", + "5.0 Z0, Z6, Z7, Z8, Z10, Z11\n", + "5.0 Z0, Z3, Z5, Z10\n", + "-5.0 Z0, Z3, Z7, Z10\n", + "5.0 Z0, Z3, Z9, Z10\n", + "5.0 Z0, Z2, Z3, Z4, Z5, Z10\n", + "-5.0 Z0, Z2, Z3, Z6, Z7, Z10\n", + "5.0 Z0, Z2, Z3, Z8, Z9, Z10\n", + "5.0 Z0, Z5, Z7, Z10\n", + "-5.0 Z0, Z5, Z9, Z10\n", + "5.0 Z0, Z4, Z5, Z6, Z7, Z10\n", + "-5.0 Z0, Z4, Z5, Z8, Z9, Z10\n", + "5.0 Z0, Z7, Z9, Z10\n", + "5.0 Z0, Z6, Z7, Z8, Z9, Z10\n", + "-5.0 Z0, Z1, Z2, Z3, Z5, Z9\n", + "5.0 Z0, Z1, Z2, Z3, Z5, Z11\n", + "-5.0 Z0, Z1, Z2, Z3, Z4, Z8\n", + "5.0 Z0, Z1, Z2, Z3, Z4, Z10\n", + "-15.0 Z0, Z1, Z2, Z3, Z4, Z5, Z8, Z9\n", + "15.0 Z0, Z1, Z2, Z3, Z4, Z5, Z10, Z11\n", + "5.0 Z0, Z1, Z2, Z3, Z7, Z9\n", + "-5.0 Z0, Z1, Z2, Z3, Z7, Z11\n", + "5.0 Z0, Z1, Z2, Z3, Z6, Z8\n", + "-5.0 Z0, Z1, Z2, Z3, Z6, Z10\n", + "15.0 Z0, Z1, Z2, Z3, Z6, Z7, Z8, Z9\n", + "-15.0 Z0, Z1, Z2, Z3, Z6, Z7, Z10, Z11\n", + "5.0 Z0, Z1, Z2, Z3, Z9, Z11\n", + "5.0 Z0, Z1, Z2, Z3, Z8, Z10\n", + "15.0 Z0, Z1, Z2, Z3, Z8, Z9, Z10, Z11\n", + "-5.0 Z0, Z1, Z3, Z4, Z5, Z9\n", + "5.0 Z0, Z1, Z3, Z4, Z5, Z11\n", + "-5.0 Z0, Z1, Z2, Z4, Z5, Z8\n", + "5.0 Z0, Z1, Z2, Z4, Z5, Z10\n", + "-5.0 Z0, Z1, Z4, Z5, Z7, Z9\n", + "5.0 Z0, Z1, Z4, Z5, Z7, Z11\n", + "-5.0 Z0, Z1, Z4, Z5, Z6, Z8\n", + "5.0 Z0, Z1, Z4, Z5, Z6, Z10\n", + "-15.0 Z0, Z1, Z4, Z5, Z6, Z7, Z8, Z9\n", + "15.0 Z0, Z1, Z4, Z5, Z6, Z7, Z10, Z11\n", + "-5.0 Z0, Z1, Z4, Z5, Z9, Z11\n", + "-5.0 Z0, Z1, Z4, Z5, Z8, Z10\n", + "-15.0 Z0, Z1, Z4, Z5, Z8, Z9, Z10, Z11\n", + "5.0 Z0, Z1, Z3, Z6, Z7, Z9\n", + "-5.0 Z0, Z1, Z3, Z6, Z7, Z11\n", + "5.0 Z0, Z1, Z2, Z6, Z7, Z8\n", + "-5.0 Z0, Z1, Z2, Z6, Z7, Z10\n", + "-5.0 Z0, Z1, Z5, Z6, Z7, Z9\n", + "5.0 Z0, Z1, Z5, Z6, Z7, Z11\n", + "-5.0 Z0, Z1, Z4, Z6, Z7, Z8\n", + "5.0 Z0, Z1, Z4, Z6, Z7, Z10\n", + "5.0 Z0, Z1, Z6, Z7, Z9, Z11\n", + "5.0 Z0, Z1, Z6, Z7, Z8, Z10\n", + "15.0 Z0, Z1, Z6, Z7, Z8, Z9, Z10, Z11\n", + "-5.0 Z0, Z1, Z3, Z5, Z8, Z9\n", + "5.0 Z0, Z1, Z3, Z7, Z8, Z9\n", + "5.0 Z0, Z1, Z3, Z8, Z9, Z11\n", + "-5.0 Z0, Z1, Z2, Z4, Z8, Z9\n", + "5.0 Z0, Z1, Z2, Z6, Z8, Z9\n", + "5.0 Z0, Z1, Z2, Z8, Z9, Z10\n", + "-5.0 Z0, Z1, Z5, Z7, Z8, Z9\n", + "-5.0 Z0, Z1, Z5, Z8, Z9, Z11\n", + "-5.0 Z0, Z1, Z4, Z6, Z8, Z9\n", + "-5.0 Z0, Z1, Z4, Z8, Z9, Z10\n", + "5.0 Z0, Z1, Z7, Z8, Z9, Z11\n", + "5.0 Z0, Z1, Z6, Z8, Z9, Z10\n", + "5.0 Z0, Z1, Z3, Z5, Z10, Z11\n", + "-5.0 Z0, Z1, Z3, Z7, Z10, Z11\n", + "5.0 Z0, Z1, Z3, Z9, Z10, Z11\n", + "5.0 Z0, Z1, Z2, Z4, Z10, Z11\n", + "-5.0 Z0, Z1, Z2, Z6, Z10, Z11\n", + "5.0 Z0, Z1, Z2, Z8, Z10, Z11\n", + "5.0 Z0, Z1, Z5, Z7, Z10, Z11\n", + "-5.0 Z0, Z1, Z5, Z9, Z10, Z11\n", + "5.0 Z0, Z1, Z4, Z6, Z10, Z11\n", + "-5.0 Z0, Z1, Z4, Z8, Z10, Z11\n", + "5.0 Z0, Z1, Z7, Z9, Z10, Z11\n", + "5.0 Z0, Z1, Z6, Z8, Z10, Z11\n", + "-5.0 Z2, Z3, Z5, Z10\n", + "-5.0 Z2, Z5, Z10, Z11\n", + "-5.0 Z3, Z4, Z5, Z10\n", + "-5.0 Z3, Z4, Z10, Z11\n", + "-15.0 Z3, Z5, Z7, Z11\n", + "-5.0 Z3, Z5, Z6, Z10\n", + "-5.0 Z3, Z5, Z6, Z7, Z10, Z11\n", + "15.0 Z3, Z5, Z9, Z11\n", + "5.0 Z3, Z5, Z8, Z10\n", + "5.0 Z3, Z5, Z8, Z9, Z10, Z11\n", + "15.0 Z2, Z3, Z7, Z10\n", + "15.0 Z2, Z7, Z10, Z11\n", + "-5.0 Z3, Z4, Z7, Z10\n", + "-5.0 Z3, Z4, Z5, Z7, Z10, Z11\n", + "15.0 Z3, Z6, Z7, Z10\n", + "15.0 Z3, Z6, Z10, Z11\n", + "-15.0 Z3, Z7, Z9, Z11\n", + "-5.0 Z3, Z7, Z8, Z10\n", + "-5.0 Z3, Z7, Z8, Z9, Z10, Z11\n", + "-5.0 Z2, Z3, Z9, Z10\n", + "-5.0 Z2, Z9, Z10, Z11\n", + "5.0 Z3, Z4, Z9, Z10\n", + "5.0 Z3, Z4, Z5, Z9, Z10, Z11\n", + "-5.0 Z3, Z6, Z9, Z10\n", + "-5.0 Z3, Z6, Z7, Z9, Z10, Z11\n", + "-5.0 Z3, Z8, Z9, Z10\n", + "-5.0 Z3, Z8, Z10, Z11\n", + "-5.0 Z2, Z3, Z4, Z11\n", + "15.0 Z2, Z3, Z6, Z11\n", + "-5.0 Z2, Z3, Z8, Z11\n", + "-5.0 Z2, Z4, Z5, Z11\n", + "15.0 Z2, Z6, Z7, Z11\n", + "-5.0 Z2, Z8, Z9, Z11\n", + "-5.0 Z3, Z4, Z6, Z11\n", + "5.0 Z3, Z4, Z8, Z11\n", + "-5.0 Z3, Z4, Z5, Z6, Z7, Z11\n", + "5.0 Z3, Z4, Z5, Z8, Z9, Z11\n", + "-5.0 Z3, Z6, Z8, Z11\n", + "-5.0 Z3, Z6, Z7, Z8, Z9, Z11\n", + "-5.0 Z2, Z4, Z7, Z11\n", + "-15.0 Z2, Z4, Z6, Z10\n", + "-5.0 Z2, Z4, Z6, Z7, Z10, Z11\n", + "5.0 Z2, Z4, Z9, Z11\n", + "15.0 Z2, Z4, Z8, Z10\n", + "5.0 Z2, Z4, Z8, Z9, Z10, Z11\n", + "-5.0 Z2, Z5, Z6, Z11\n", + "-5.0 Z2, Z4, Z5, Z6, Z10, Z11\n", + "-5.0 Z2, Z6, Z9, Z11\n", + "-15.0 Z2, Z6, Z8, Z10\n", + "-5.0 Z2, Z6, Z8, Z9, Z10, Z11\n", + "5.0 Z2, Z5, Z8, Z11\n", + "5.0 Z2, Z4, Z5, Z8, Z10, Z11\n", + "-5.0 Z2, Z7, Z8, Z11\n", + "-5.0 Z2, Z6, Z7, Z8, Z10, Z11\n", + "-5.0 Z2, Z5, Z7, Z10\n", + "5.0 Z2, Z5, Z9, Z10\n", + "-5.0 Z2, Z4, Z5, Z6, Z7, Z10\n", + "5.0 Z2, Z4, Z5, Z8, Z9, Z10\n", + "-5.0 Z2, Z7, Z9, Z10\n", + "-5.0 Z2, Z6, Z7, Z8, Z9, Z10\n", + "-5.0 Z2, Z3, Z4, Z5, Z7, Z11\n", + "-5.0 Z2, Z3, Z4, Z5, Z6, Z10\n", + "-15.0 Z2, Z3, Z4, Z5, Z6, Z7, Z10, Z11\n", + "5.0 Z2, Z3, Z4, Z5, Z9, Z11\n", + "5.0 Z2, Z3, Z4, Z5, Z8, Z10\n", + "15.0 Z2, Z3, Z4, Z5, Z8, Z9, Z10, Z11\n", + "-5.0 Z2, Z3, Z5, Z6, Z7, Z11\n", + "-5.0 Z2, Z3, Z4, Z6, Z7, Z10\n", + "-5.0 Z2, Z3, Z6, Z7, Z9, Z11\n", + "-5.0 Z2, Z3, Z6, Z7, Z8, Z10\n", + "-15.0 Z2, Z3, Z6, Z7, Z8, Z9, Z10, Z11\n", + "5.0 Z2, Z3, Z5, Z8, Z9, Z11\n", + "5.0 Z2, Z3, Z4, Z8, Z9, Z10\n", + "-5.0 Z2, Z3, Z7, Z8, Z9, Z11\n", + "-5.0 Z2, Z3, Z6, Z8, Z9, Z10\n", + "-5.0 Z2, Z3, Z5, Z7, Z10, Z11\n", + "5.0 Z2, Z3, Z5, Z9, Z10, Z11\n", + "-5.0 Z2, Z3, Z4, Z6, Z10, Z11\n", + "5.0 Z2, Z3, Z4, Z8, Z10, Z11\n", + "-5.0 Z2, Z3, Z7, Z9, Z10, Z11\n", + "-5.0 Z2, Z3, Z6, Z8, Z10, Z11\n", + "-7.5 Z4, Z5, Z7, Z10\n", + "-7.5 Z4, Z7, Z10, Z11\n", + "-7.5 Z5, Z6, Z7, Z10\n", + "-7.5 Z5, Z6, Z10, Z11\n", + "22.5 Z5, Z7, Z9, Z11\n", + "7.5 Z5, Z7, Z8, Z10\n", + "7.5 Z5, Z7, Z8, Z9, Z10, Z11\n", + "-7.5 Z4, Z5, Z9, Z10\n", + "-7.5 Z4, Z9, Z10, Z11\n", + "7.5 Z5, Z6, Z9, Z10\n", + "7.5 Z5, Z6, Z7, Z9, Z10, Z11\n", + "-7.5 Z5, Z8, Z9, Z10\n", + "-7.5 Z5, Z8, Z10, Z11\n", + "-7.5 Z4, Z5, Z6, Z11\n", + "-7.5 Z4, Z5, Z8, Z11\n", + "-7.5 Z4, Z6, Z7, Z11\n", + "-7.5 Z4, Z8, Z9, Z11\n", + "7.5 Z5, Z6, Z8, Z11\n", + "7.5 Z5, Z6, Z7, Z8, Z9, Z11\n", + "7.5 Z4, Z6, Z9, Z11\n", + "22.5 Z4, Z6, Z8, Z10\n", + "7.5 Z4, Z6, Z8, Z9, Z10, Z11\n", + "7.5 Z4, Z7, Z8, Z11\n", + "7.5 Z4, Z6, Z7, Z8, Z10, Z11\n", + "7.5 Z4, Z7, Z9, Z10\n", + "7.5 Z4, Z6, Z7, Z8, Z9, Z10\n", + "7.5 Z4, Z5, Z6, Z7, Z9, Z11\n", + "7.5 Z4, Z5, Z6, Z7, Z8, Z10\n", + "22.5 Z4, Z5, Z6, Z7, Z8, Z9, Z10, Z11\n", + "7.5 Z4, Z5, Z7, Z8, Z9, Z11\n", + "7.5 Z4, Z5, Z6, Z8, Z9, Z10\n", + "7.5 Z4, Z5, Z7, Z9, Z10, Z11\n", + "7.5 Z4, Z5, Z6, Z8, Z10, Z11\n", + "-7.5 Z6, Z7, Z9, Z10\n", + "-7.5 Z6, Z9, Z10, Z11\n", + "-7.5 Z7, Z8, Z9, Z10\n", + "-7.5 Z7, Z8, Z10, Z11\n", + "-7.5 Z6, Z7, Z8, Z11\n", + "-7.5 Z6, Z8, Z9, Z11\n", + "20.0 Z1, Z11, Z12\n", + "20.0 Z0, Z10, Z12\n", + "20.0 Z0, Z1, Z10, Z11, Z12\n", + "-25.0 Z3, Z11, Z12\n", + "-25.0 Z2, Z10, Z12\n", + "-25.0 Z2, Z3, Z10, Z11, Z12\n", + "20.0 Z5, Z11, Z12\n", + "20.0 Z4, Z10, Z12\n", + "20.0 Z4, Z5, Z10, Z11, Z12\n", + "-25.0 Z7, Z11, Z12\n", + "-25.0 Z6, Z10, Z12\n", + "-25.0 Z6, Z7, Z10, Z11, Z12\n", + "20.0 Z9, Z11, Z12\n", + "20.0 Z8, Z10, Z12\n", + "20.0 Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z3, Z8, Z12\n", + "2.5 Z0, Z1, Z3, Z10, Z12\n", + "2.5 Z0, Z3, Z8, Z9, Z12\n", + "2.5 Z0, Z3, Z10, Z11, Z12\n", + "2.5 Z1, Z2, Z3, Z8, Z12\n", + "2.5 Z1, Z2, Z3, Z10, Z12\n", + "2.5 Z1, Z2, Z8, Z9, Z12\n", + "2.5 Z1, Z2, Z10, Z11, Z12\n", + "7.5 Z1, Z3, Z5, Z9, Z12\n", + "-7.5 Z1, Z3, Z5, Z11, Z12\n", + "2.5 Z1, Z3, Z4, Z8, Z12\n", + "-2.5 Z1, Z3, Z4, Z10, Z12\n", + "2.5 Z1, Z3, Z4, Z5, Z8, Z9, Z12\n", + "-2.5 Z1, Z3, Z4, Z5, Z10, Z11, Z12\n", + "-7.5 Z1, Z3, Z7, Z9, Z12\n", + "7.5 Z1, Z3, Z7, Z11, Z12\n", + "-2.5 Z1, Z3, Z6, Z8, Z12\n", + "2.5 Z1, Z3, Z6, Z10, Z12\n", + "-2.5 Z1, Z3, Z6, Z7, Z8, Z9, Z12\n", + "2.5 Z1, Z3, Z6, Z7, Z10, Z11, Z12\n", + "-7.5 Z1, Z3, Z9, Z11, Z12\n", + "-2.5 Z1, Z3, Z8, Z10, Z12\n", + "-2.5 Z1, Z3, Z8, Z9, Z10, Z11, Z12\n", + "-7.5 Z0, Z1, Z5, Z8, Z12\n", + "2.5 Z0, Z1, Z5, Z10, Z12\n", + "-7.5 Z0, Z5, Z8, Z9, Z12\n", + "2.5 Z0, Z5, Z10, Z11, Z12\n", + "2.5 Z1, Z2, Z5, Z8, Z12\n", + "-2.5 Z1, Z2, Z5, Z10, Z12\n", + "2.5 Z1, Z2, Z3, Z5, Z8, Z9, Z12\n", + "-2.5 Z1, Z2, Z3, Z5, Z10, Z11, Z12\n", + "-7.5 Z1, Z4, Z5, Z8, Z12\n", + "2.5 Z1, Z4, Z5, Z10, Z12\n", + "-7.5 Z1, Z4, Z8, Z9, Z12\n", + "2.5 Z1, Z4, Z10, Z11, Z12\n", + "7.5 Z1, Z5, Z7, Z9, Z12\n", + "-7.5 Z1, Z5, Z7, Z11, Z12\n", + "2.5 Z1, Z5, Z6, Z8, Z12\n", + "-2.5 Z1, Z5, Z6, Z10, Z12\n", + "2.5 Z1, Z5, Z6, Z7, Z8, Z9, Z12\n", + "-2.5 Z1, Z5, Z6, Z7, Z10, Z11, Z12\n", + "7.5 Z1, Z5, Z9, Z11, Z12\n", + "2.5 Z1, Z5, Z8, Z10, Z12\n", + "2.5 Z1, Z5, Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z7, Z8, Z12\n", + "2.5 Z0, Z1, Z7, Z10, Z12\n", + "2.5 Z0, Z7, Z8, Z9, Z12\n", + "2.5 Z0, Z7, Z10, Z11, Z12\n", + "-2.5 Z1, Z2, Z7, Z8, Z12\n", + "2.5 Z1, Z2, Z7, Z10, Z12\n", + "-2.5 Z1, Z2, Z3, Z7, Z8, Z9, Z12\n", + "2.5 Z1, Z2, Z3, Z7, Z10, Z11, Z12\n", + "2.5 Z1, Z4, Z7, Z8, Z12\n", + "-2.5 Z1, Z4, Z7, Z10, Z12\n", + "2.5 Z1, Z4, Z5, Z7, Z8, Z9, Z12\n", + "-2.5 Z1, Z4, Z5, Z7, Z10, Z11, Z12\n", + "2.5 Z1, Z6, Z7, Z8, Z12\n", + "2.5 Z1, Z6, Z7, Z10, Z12\n", + "2.5 Z1, Z6, Z8, Z9, Z12\n", + "2.5 Z1, Z6, Z10, Z11, Z12\n", + "-7.5 Z1, Z7, Z9, Z11, Z12\n", + "-2.5 Z1, Z7, Z8, Z10, Z12\n", + "-2.5 Z1, Z7, Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z2, Z9, Z12\n", + "-7.5 Z0, Z1, Z4, Z9, Z12\n", + "2.5 Z0, Z1, Z6, Z9, Z12\n", + "2.5 Z0, Z1, Z9, Z10, Z12\n", + "2.5 Z0, Z2, Z3, Z9, Z12\n", + "-7.5 Z0, Z4, Z5, Z9, Z12\n", + "2.5 Z0, Z6, Z7, Z9, Z12\n", + "2.5 Z0, Z9, Z10, Z11, Z12\n", + "2.5 Z1, Z2, Z4, Z9, Z12\n", + "-2.5 Z1, Z2, Z6, Z9, Z12\n", + "-2.5 Z1, Z2, Z9, Z10, Z12\n", + "2.5 Z1, Z2, Z3, Z4, Z5, Z9, Z12\n", + "-2.5 Z1, Z2, Z3, Z6, Z7, Z9, Z12\n", + "-2.5 Z1, Z2, Z3, Z9, Z10, Z11, Z12\n", + "2.5 Z1, Z4, Z6, Z9, Z12\n", + "2.5 Z1, Z4, Z9, Z10, Z12\n", + "2.5 Z1, Z4, Z5, Z6, Z7, Z9, Z12\n", + "2.5 Z1, Z4, Z5, Z9, Z10, Z11, Z12\n", + "-2.5 Z1, Z6, Z9, Z10, Z12\n", + "-2.5 Z1, Z6, Z7, Z9, Z10, Z11, Z12\n", + "2.5 Z1, Z8, Z9, Z10, Z12\n", + "2.5 Z1, Z8, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z2, Z11, Z12\n", + "2.5 Z0, Z1, Z4, Z11, Z12\n", + "2.5 Z0, Z1, Z6, Z11, Z12\n", + "2.5 Z0, Z1, Z8, Z11, Z12\n", + "2.5 Z0, Z2, Z3, Z11, Z12\n", + "2.5 Z0, Z4, Z5, Z11, Z12\n", + "2.5 Z0, Z6, Z7, Z11, Z12\n", + "2.5 Z0, Z8, Z9, Z11, Z12\n", + "-2.5 Z1, Z2, Z4, Z11, Z12\n", + "2.5 Z1, Z2, Z6, Z11, Z12\n", + "-2.5 Z1, Z2, Z8, Z11, Z12\n", + "-2.5 Z1, Z2, Z3, Z4, Z5, Z11, Z12\n", + "2.5 Z1, Z2, Z3, Z6, Z7, Z11, Z12\n", + "-2.5 Z1, Z2, Z3, Z8, Z9, Z11, Z12\n", + "-2.5 Z1, Z4, Z6, Z11, Z12\n", + "2.5 Z1, Z4, Z8, Z11, Z12\n", + "-2.5 Z1, Z4, Z5, Z6, Z7, Z11, Z12\n", + "2.5 Z1, Z4, Z5, Z8, Z9, Z11, Z12\n", + "-2.5 Z1, Z6, Z8, Z11, Z12\n", + "-2.5 Z1, Z6, Z7, Z8, Z9, Z11, Z12\n", + "2.5 Z0, Z2, Z5, Z9, Z12\n", + "-2.5 Z0, Z2, Z5, Z11, Z12\n", + "7.5 Z0, Z2, Z4, Z8, Z12\n", + "-7.5 Z0, Z2, Z4, Z10, Z12\n", + "2.5 Z0, Z2, Z4, Z5, Z8, Z9, Z12\n", + "-2.5 Z0, Z2, Z4, Z5, Z10, Z11, Z12\n", + "-2.5 Z0, Z2, Z7, Z9, Z12\n", + "2.5 Z0, Z2, Z7, Z11, Z12\n", + "-7.5 Z0, Z2, Z6, Z8, Z12\n", + "7.5 Z0, Z2, Z6, Z10, Z12\n", + "-2.5 Z0, Z2, Z6, Z7, Z8, Z9, Z12\n", + "2.5 Z0, Z2, Z6, Z7, Z10, Z11, Z12\n", + "-2.5 Z0, Z2, Z9, Z11, Z12\n", + "-7.5 Z0, Z2, Z8, Z10, Z12\n", + "-2.5 Z0, Z2, Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z0, Z3, Z4, Z9, Z12\n", + "-2.5 Z0, Z3, Z4, Z11, Z12\n", + "2.5 Z0, Z2, Z3, Z4, Z8, Z9, Z12\n", + "-2.5 Z0, Z2, Z3, Z4, Z10, Z11, Z12\n", + "2.5 Z0, Z4, Z7, Z9, Z12\n", + "-2.5 Z0, Z4, Z7, Z11, Z12\n", + "7.5 Z0, Z4, Z6, Z8, Z12\n", + "-7.5 Z0, Z4, Z6, Z10, Z12\n", + "2.5 Z0, Z4, Z6, Z7, Z8, Z9, Z12\n", + "-2.5 Z0, Z4, Z6, Z7, Z10, Z11, Z12\n", + "2.5 Z0, Z4, Z9, Z11, Z12\n", + "7.5 Z0, Z4, Z8, Z10, Z12\n", + "2.5 Z0, Z4, Z8, Z9, Z10, Z11, Z12\n", + "-2.5 Z0, Z3, Z6, Z9, Z12\n", + "2.5 Z0, Z3, Z6, Z11, Z12\n", + "-2.5 Z0, Z2, Z3, Z6, Z8, Z9, Z12\n", + "2.5 Z0, Z2, Z3, Z6, Z10, Z11, Z12\n", + "2.5 Z0, Z5, Z6, Z9, Z12\n", + "-2.5 Z0, Z5, Z6, Z11, Z12\n", + "2.5 Z0, Z4, Z5, Z6, Z8, Z9, Z12\n", + "-2.5 Z0, Z4, Z5, Z6, Z10, Z11, Z12\n", + "-2.5 Z0, Z6, Z9, Z11, Z12\n", + "-7.5 Z0, Z6, Z8, Z10, Z12\n", + "-2.5 Z0, Z6, Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z0, Z3, Z5, Z8, Z12\n", + "-2.5 Z0, Z3, Z7, Z8, Z12\n", + "-2.5 Z0, Z3, Z8, Z11, Z12\n", + "2.5 Z0, Z2, Z3, Z4, Z5, Z8, Z12\n", + "-2.5 Z0, Z2, Z3, Z6, Z7, Z8, Z12\n", + "-2.5 Z0, Z2, Z3, Z8, Z10, Z11, Z12\n", + "2.5 Z0, Z5, Z7, Z8, Z12\n", + "2.5 Z0, Z5, Z8, Z11, Z12\n", + "2.5 Z0, Z4, Z5, Z6, Z7, Z8, Z12\n", + "2.5 Z0, Z4, Z5, Z8, Z10, Z11, Z12\n", + "-2.5 Z0, Z7, Z8, Z11, Z12\n", + "-2.5 Z0, Z6, Z7, Z8, Z10, Z11, Z12\n", + "-2.5 Z0, Z3, Z5, Z10, Z12\n", + "2.5 Z0, Z3, Z7, Z10, Z12\n", + "-2.5 Z0, Z3, Z9, Z10, Z12\n", + "-2.5 Z0, Z2, Z3, Z4, Z5, Z10, Z12\n", + "2.5 Z0, Z2, Z3, Z6, Z7, Z10, Z12\n", + "-2.5 Z0, Z2, Z3, Z8, Z9, Z10, Z12\n", + "-2.5 Z0, Z5, Z7, Z10, Z12\n", + "2.5 Z0, Z5, Z9, Z10, Z12\n", + "-2.5 Z0, Z4, Z5, Z6, Z7, Z10, Z12\n", + "2.5 Z0, Z4, Z5, Z8, Z9, Z10, Z12\n", + "-2.5 Z0, Z7, Z9, Z10, Z12\n", + "-2.5 Z0, Z6, Z7, Z8, Z9, Z10, Z12\n", + "2.5 Z0, Z1, Z2, Z3, Z5, Z9, Z12\n", + "-2.5 Z0, Z1, Z2, Z3, Z5, Z11, Z12\n", + "2.5 Z0, Z1, Z2, Z3, Z4, Z8, Z12\n", + "-2.5 Z0, Z1, Z2, Z3, Z4, Z10, Z12\n", + "7.5 Z0, Z1, Z2, Z3, Z4, Z5, Z8, Z9, Z12\n", + "-7.5 Z0, Z1, Z2, Z3, Z4, Z5, Z10, Z11, Z12\n", + "-2.5 Z0, Z1, Z2, Z3, Z7, Z9, Z12\n", + "2.5 Z0, Z1, Z2, Z3, Z7, Z11, Z12\n", + "-2.5 Z0, Z1, Z2, Z3, Z6, Z8, Z12\n", + "2.5 Z0, Z1, Z2, Z3, Z6, Z10, Z12\n", + "-7.5 Z0, Z1, Z2, Z3, Z6, Z7, Z8, Z9, Z12\n", + "7.5 Z0, Z1, Z2, Z3, Z6, Z7, Z10, Z11, Z12\n", + "-2.5 Z0, Z1, Z2, Z3, Z9, Z11, Z12\n", + "-2.5 Z0, Z1, Z2, Z3, Z8, Z10, Z12\n", + "-7.5 Z0, Z1, Z2, Z3, Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z3, Z4, Z5, Z9, Z12\n", + "-2.5 Z0, Z1, Z3, Z4, Z5, Z11, Z12\n", + "2.5 Z0, Z1, Z2, Z4, Z5, Z8, Z12\n", + "-2.5 Z0, Z1, Z2, Z4, Z5, Z10, Z12\n", + "2.5 Z0, Z1, Z4, Z5, Z7, Z9, Z12\n", + "-2.5 Z0, Z1, Z4, Z5, Z7, Z11, Z12\n", + "2.5 Z0, Z1, Z4, Z5, Z6, Z8, Z12\n", + "-2.5 Z0, Z1, Z4, Z5, Z6, Z10, Z12\n", + "7.5 Z0, Z1, Z4, Z5, Z6, Z7, Z8, Z9, Z12\n", + "-7.5 Z0, Z1, Z4, Z5, Z6, Z7, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z4, Z5, Z9, Z11, Z12\n", + "2.5 Z0, Z1, Z4, Z5, Z8, Z10, Z12\n", + "7.5 Z0, Z1, Z4, Z5, Z8, Z9, Z10, Z11, Z12\n", + "-2.5 Z0, Z1, Z3, Z6, Z7, Z9, Z12\n", + "2.5 Z0, Z1, Z3, Z6, Z7, Z11, Z12\n", + "-2.5 Z0, Z1, Z2, Z6, Z7, Z8, Z12\n", + "2.5 Z0, Z1, Z2, Z6, Z7, Z10, Z12\n", + "2.5 Z0, Z1, Z5, Z6, Z7, Z9, Z12\n", + "-2.5 Z0, Z1, Z5, Z6, Z7, Z11, Z12\n", + "2.5 Z0, Z1, Z4, Z6, Z7, Z8, Z12\n", + "-2.5 Z0, Z1, Z4, Z6, Z7, Z10, Z12\n", + "-2.5 Z0, Z1, Z6, Z7, Z9, Z11, Z12\n", + "-2.5 Z0, Z1, Z6, Z7, Z8, Z10, Z12\n", + "-7.5 Z0, Z1, Z6, Z7, Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z3, Z5, Z8, Z9, Z12\n", + "-2.5 Z0, Z1, Z3, Z7, Z8, Z9, Z12\n", + "-2.5 Z0, Z1, Z3, Z8, Z9, Z11, Z12\n", + "2.5 Z0, Z1, Z2, Z4, Z8, Z9, Z12\n", + "-2.5 Z0, Z1, Z2, Z6, Z8, Z9, Z12\n", + "-2.5 Z0, Z1, Z2, Z8, Z9, Z10, Z12\n", + "2.5 Z0, Z1, Z5, Z7, Z8, Z9, Z12\n", + "2.5 Z0, Z1, Z5, Z8, Z9, Z11, Z12\n", + "2.5 Z0, Z1, Z4, Z6, Z8, Z9, Z12\n", + "2.5 Z0, Z1, Z4, Z8, Z9, Z10, Z12\n", + "-2.5 Z0, Z1, Z7, Z8, Z9, Z11, Z12\n", + "-2.5 Z0, Z1, Z6, Z8, Z9, Z10, Z12\n", + "-2.5 Z0, Z1, Z3, Z5, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z3, Z7, Z10, Z11, Z12\n", + "-2.5 Z0, Z1, Z3, Z9, Z10, Z11, Z12\n", + "-2.5 Z0, Z1, Z2, Z4, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z2, Z6, Z10, Z11, Z12\n", + "-2.5 Z0, Z1, Z2, Z8, Z10, Z11, Z12\n", + "-2.5 Z0, Z1, Z5, Z7, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z5, Z9, Z10, Z11, Z12\n", + "-2.5 Z0, Z1, Z4, Z6, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z4, Z8, Z10, Z11, Z12\n", + "-2.5 Z0, Z1, Z7, Z9, Z10, Z11, Z12\n", + "-2.5 Z0, Z1, Z6, Z8, Z10, Z11, Z12\n", + "2.5 Z2, Z3, Z5, Z10, Z12\n", + "2.5 Z2, Z5, Z10, Z11, Z12\n", + "2.5 Z3, Z4, Z5, Z10, Z12\n", + "2.5 Z3, Z4, Z10, Z11, Z12\n", + "7.5 Z3, Z5, Z7, Z11, Z12\n", + "2.5 Z3, Z5, Z6, Z10, Z12\n", + "2.5 Z3, Z5, Z6, Z7, Z10, Z11, Z12\n", + "-7.5 Z3, Z5, Z9, Z11, Z12\n", + "-2.5 Z3, Z5, Z8, Z10, Z12\n", + "-2.5 Z3, Z5, Z8, Z9, Z10, Z11, Z12\n", + "-7.5 Z2, Z3, Z7, Z10, Z12\n", + "-7.5 Z2, Z7, Z10, Z11, Z12\n", + "2.5 Z3, Z4, Z7, Z10, Z12\n", + "2.5 Z3, Z4, Z5, Z7, Z10, Z11, Z12\n", + "-7.5 Z3, Z6, Z7, Z10, Z12\n", + "-7.5 Z3, Z6, Z10, Z11, Z12\n", + "7.5 Z3, Z7, Z9, Z11, Z12\n", + "2.5 Z3, Z7, Z8, Z10, Z12\n", + "2.5 Z3, Z7, Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z2, Z3, Z9, Z10, Z12\n", + "2.5 Z2, Z9, Z10, Z11, Z12\n", + "-2.5 Z3, Z4, Z9, Z10, Z12\n", + "-2.5 Z3, Z4, Z5, Z9, Z10, Z11, Z12\n", + "2.5 Z3, Z6, Z9, Z10, Z12\n", + "2.5 Z3, Z6, Z7, Z9, Z10, Z11, Z12\n", + "2.5 Z3, Z8, Z9, Z10, Z12\n", + "2.5 Z3, Z8, Z10, Z11, Z12\n", + "2.5 Z2, Z3, Z4, Z11, Z12\n", + "-7.5 Z2, Z3, Z6, Z11, Z12\n", + "2.5 Z2, Z3, Z8, Z11, Z12\n", + "2.5 Z2, Z4, Z5, Z11, Z12\n", + "-7.5 Z2, Z6, Z7, Z11, Z12\n", + "2.5 Z2, Z8, Z9, Z11, Z12\n", + "2.5 Z3, Z4, Z6, Z11, Z12\n", + "-2.5 Z3, Z4, Z8, Z11, Z12\n", + "2.5 Z3, Z4, Z5, Z6, Z7, Z11, Z12\n", + "-2.5 Z3, Z4, Z5, Z8, Z9, Z11, Z12\n", + "2.5 Z3, Z6, Z8, Z11, Z12\n", + "2.5 Z3, Z6, Z7, Z8, Z9, Z11, Z12\n", + "2.5 Z2, Z4, Z7, Z11, Z12\n", + "7.5 Z2, Z4, Z6, Z10, Z12\n", + "2.5 Z2, Z4, Z6, Z7, Z10, Z11, Z12\n", + "-2.5 Z2, Z4, Z9, Z11, Z12\n", + "-7.5 Z2, Z4, Z8, Z10, Z12\n", + "-2.5 Z2, Z4, Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z2, Z5, Z6, Z11, Z12\n", + "2.5 Z2, Z4, Z5, Z6, Z10, Z11, Z12\n", + "2.5 Z2, Z6, Z9, Z11, Z12\n", + "7.5 Z2, Z6, Z8, Z10, Z12\n", + "2.5 Z2, Z6, Z8, Z9, Z10, Z11, Z12\n", + "-2.5 Z2, Z5, Z8, Z11, Z12\n", + "-2.5 Z2, Z4, Z5, Z8, Z10, Z11, Z12\n", + "2.5 Z2, Z7, Z8, Z11, Z12\n", + "2.5 Z2, Z6, Z7, Z8, Z10, Z11, Z12\n", + "2.5 Z2, Z5, Z7, Z10, Z12\n", + "-2.5 Z2, Z5, Z9, Z10, Z12\n", + "2.5 Z2, Z4, Z5, Z6, Z7, Z10, Z12\n", + "-2.5 Z2, Z4, Z5, Z8, Z9, Z10, Z12\n", + "2.5 Z2, Z7, Z9, Z10, Z12\n", + "2.5 Z2, Z6, Z7, Z8, Z9, Z10, Z12\n", + "2.5 Z2, Z3, Z4, Z5, Z7, Z11, Z12\n", + "2.5 Z2, Z3, Z4, Z5, Z6, Z10, Z12\n", + "7.5 Z2, Z3, Z4, Z5, Z6, Z7, Z10, Z11, Z12\n", + "-2.5 Z2, Z3, Z4, Z5, Z9, Z11, Z12\n", + "-2.5 Z2, Z3, Z4, Z5, Z8, Z10, Z12\n", + "-7.5 Z2, Z3, Z4, Z5, Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z2, Z3, Z5, Z6, Z7, Z11, Z12\n", + "2.5 Z2, Z3, Z4, Z6, Z7, Z10, Z12\n", + "2.5 Z2, Z3, Z6, Z7, Z9, Z11, Z12\n", + "2.5 Z2, Z3, Z6, Z7, Z8, Z10, Z12\n", + "7.5 Z2, Z3, Z6, Z7, Z8, Z9, Z10, Z11, Z12\n", + "-2.5 Z2, Z3, Z5, Z8, Z9, Z11, Z12\n", + "-2.5 Z2, Z3, Z4, Z8, Z9, Z10, Z12\n", + "2.5 Z2, Z3, Z7, Z8, Z9, Z11, Z12\n", + "2.5 Z2, Z3, Z6, Z8, Z9, Z10, Z12\n", + "2.5 Z2, Z3, Z5, Z7, Z10, Z11, Z12\n", + "-2.5 Z2, Z3, Z5, Z9, Z10, Z11, Z12\n", + "2.5 Z2, Z3, Z4, Z6, Z10, Z11, Z12\n", + "-2.5 Z2, Z3, Z4, Z8, Z10, Z11, Z12\n", + "2.5 Z2, Z3, Z7, Z9, Z10, Z11, Z12\n", + "2.5 Z2, Z3, Z6, Z8, Z10, Z11, Z12\n", + "2.5 Z4, Z5, Z7, Z10, Z12\n", + "2.5 Z4, Z7, Z10, Z11, Z12\n", + "2.5 Z5, Z6, Z7, Z10, Z12\n", + "2.5 Z5, Z6, Z10, Z11, Z12\n", + "-7.5 Z5, Z7, Z9, Z11, Z12\n", + "-2.5 Z5, Z7, Z8, Z10, Z12\n", + "-2.5 Z5, Z7, Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z4, Z5, Z9, Z10, Z12\n", + "2.5 Z4, Z9, Z10, Z11, Z12\n", + "-2.5 Z5, Z6, Z9, Z10, Z12\n", + "-2.5 Z5, Z6, Z7, Z9, Z10, Z11, Z12\n", + "2.5 Z5, Z8, Z9, Z10, Z12\n", + "2.5 Z5, Z8, Z10, Z11, Z12\n", + "2.5 Z4, Z5, Z6, Z11, Z12\n", + "2.5 Z4, Z5, Z8, Z11, Z12\n", + "2.5 Z4, Z6, Z7, Z11, Z12\n", + "2.5 Z4, Z8, Z9, Z11, Z12\n", + "-2.5 Z5, Z6, Z8, Z11, Z12\n", + "-2.5 Z5, Z6, Z7, Z8, Z9, Z11, Z12\n", + "-2.5 Z4, Z6, Z9, Z11, Z12\n", + "-7.5 Z4, Z6, Z8, Z10, Z12\n", + "-2.5 Z4, Z6, Z8, Z9, Z10, Z11, Z12\n", + "-2.5 Z4, Z7, Z8, Z11, Z12\n", + "-2.5 Z4, Z6, Z7, Z8, Z10, Z11, Z12\n", + "-2.5 Z4, Z7, Z9, Z10, Z12\n", + "-2.5 Z4, Z6, Z7, Z8, Z9, Z10, Z12\n", + "-2.5 Z4, Z5, Z6, Z7, Z9, Z11, Z12\n", + "-2.5 Z4, Z5, Z6, Z7, Z8, Z10, Z12\n", + "-7.5 Z4, Z5, Z6, Z7, Z8, Z9, Z10, Z11, Z12\n", + "-2.5 Z4, Z5, Z7, Z8, Z9, Z11, Z12\n", + "-2.5 Z4, Z5, Z6, Z8, Z9, Z10, Z12\n", + "-2.5 Z4, Z5, Z7, Z9, Z10, Z11, Z12\n", + "-2.5 Z4, Z5, Z6, Z8, Z10, Z11, Z12\n", + "2.5 Z6, Z7, Z9, Z10, Z12\n", + "2.5 Z6, Z9, Z10, Z11, Z12\n", + "2.5 Z7, Z8, Z9, Z10, Z12\n", + "2.5 Z7, Z8, Z10, Z11, Z12\n", + "2.5 Z6, Z7, Z8, Z11, Z12\n", + "2.5 Z6, Z8, Z9, Z11, Z12\n", + "-157.34050000000002 Z13\n", + "30.0 Z3, Z5, Z13\n", + "-40.0 Z3, Z7, Z13\n", + "30.0 Z3, Z9, Z13\n", + "-27.5 Z3, Z11, Z13\n", + "30.0 Z2, Z4, Z13\n", + "-40.0 Z2, Z6, Z13\n", + "30.0 Z2, Z8, Z13\n", + "-27.5 Z2, Z10, Z13\n", + "30.0 Z2, Z3, Z4, Z5, Z13\n", + "-40.0 Z2, Z3, Z6, Z7, Z13\n", + "30.0 Z2, Z3, Z8, Z9, Z13\n", + "-27.5 Z2, Z3, Z10, Z11, Z13\n", + "37.5 Z5, Z7, Z13\n", + "-52.5 Z5, Z9, Z13\n", + "30.0 Z5, Z11, Z13\n", + "37.5 Z4, Z6, Z13\n", + "-52.5 Z4, Z8, Z13\n", + "30.0 Z4, Z10, Z13\n", + "37.5 Z4, Z5, Z6, Z7, Z13\n", + "-52.5 Z4, Z5, Z8, Z9, Z13\n", + "30.0 Z4, Z5, Z10, Z11, Z13\n", + "37.5 Z7, Z9, Z13\n", + "-40.0 Z7, Z11, Z13\n", + "37.5 Z6, Z8, Z13\n", + "-40.0 Z6, Z10, Z13\n", + "37.5 Z6, Z7, Z8, Z9, Z13\n", + "-40.0 Z6, Z7, Z10, Z11, Z13\n", + "30.0 Z9, Z11, Z13\n", + "30.0 Z8, Z10, Z13\n", + "30.0 Z8, Z9, Z10, Z11, Z13\n", + "20.0 Z1, Z3, Z13\n", + "-25.0 Z1, Z5, Z13\n", + "20.0 Z1, Z7, Z13\n", + "-25.0 Z1, Z9, Z13\n", + "20.0 Z1, Z11, Z13\n", + "20.0 Z0, Z2, Z13\n", + "-25.0 Z0, Z4, Z13\n", + "20.0 Z0, Z6, Z13\n", + "-25.0 Z0, Z8, Z13\n", + "20.0 Z0, Z10, Z13\n", + "20.0 Z0, Z1, Z2, Z3, Z13\n", + "-25.0 Z0, Z1, Z4, Z5, Z13\n", + "20.0 Z0, Z1, Z6, Z7, Z13\n", + "-25.0 Z0, Z1, Z8, Z9, Z13\n", + "20.0 Z0, Z1, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z3, Z4, Z13\n", + "2.5 Z0, Z1, Z3, Z6, Z13\n", + "2.5 Z0, Z1, Z3, Z8, Z13\n", + "2.5 Z0, Z1, Z3, Z10, Z13\n", + "2.5 Z0, Z3, Z4, Z5, Z13\n", + "2.5 Z0, Z3, Z6, Z7, Z13\n", + "2.5 Z0, Z3, Z8, Z9, Z13\n", + "2.5 Z0, Z3, Z10, Z11, Z13\n", + "2.5 Z1, Z2, Z3, Z4, Z13\n", + "2.5 Z1, Z2, Z3, Z6, Z13\n", + "2.5 Z1, Z2, Z3, Z8, Z13\n", + "2.5 Z1, Z2, Z3, Z10, Z13\n", + "2.5 Z1, Z2, Z4, Z5, Z13\n", + "2.5 Z1, Z2, Z6, Z7, Z13\n", + "2.5 Z1, Z2, Z8, Z9, Z13\n", + "2.5 Z1, Z2, Z10, Z11, Z13\n", + "-7.5 Z1, Z3, Z5, Z7, Z13\n", + "7.5 Z1, Z3, Z5, Z9, Z13\n", + "-7.5 Z1, Z3, Z5, Z11, Z13\n", + "-2.5 Z1, Z3, Z4, Z6, Z13\n", + "2.5 Z1, Z3, Z4, Z8, Z13\n", + "-2.5 Z1, Z3, Z4, Z10, Z13\n", + "-2.5 Z1, Z3, Z4, Z5, Z6, Z7, Z13\n", + "2.5 Z1, Z3, Z4, Z5, Z8, Z9, Z13\n", + "-2.5 Z1, Z3, Z4, Z5, Z10, Z11, Z13\n", + "-7.5 Z1, Z3, Z7, Z9, Z13\n", + "7.5 Z1, Z3, Z7, Z11, Z13\n", + "-2.5 Z1, Z3, Z6, Z8, Z13\n", + "2.5 Z1, Z3, Z6, Z10, Z13\n", + "-2.5 Z1, Z3, Z6, Z7, Z8, Z9, Z13\n", + "2.5 Z1, Z3, Z6, Z7, Z10, Z11, Z13\n", + "-7.5 Z1, Z3, Z9, Z11, Z13\n", + "-2.5 Z1, Z3, Z8, Z10, Z13\n", + "-2.5 Z1, Z3, Z8, Z9, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z2, Z5, Z13\n", + "2.5 Z0, Z1, Z5, Z6, Z13\n", + "-7.5 Z0, Z1, Z5, Z8, Z13\n", + "2.5 Z0, Z1, Z5, Z10, Z13\n", + "2.5 Z0, Z2, Z3, Z5, Z13\n", + "2.5 Z0, Z5, Z6, Z7, Z13\n", + "-7.5 Z0, Z5, Z8, Z9, Z13\n", + "2.5 Z0, Z5, Z10, Z11, Z13\n", + "-2.5 Z1, Z2, Z5, Z6, Z13\n", + "2.5 Z1, Z2, Z5, Z8, Z13\n", + "-2.5 Z1, Z2, Z5, Z10, Z13\n", + "-2.5 Z1, Z2, Z3, Z5, Z6, Z7, Z13\n", + "2.5 Z1, Z2, Z3, Z5, Z8, Z9, Z13\n", + "-2.5 Z1, Z2, Z3, Z5, Z10, Z11, Z13\n", + "2.5 Z1, Z4, Z5, Z6, Z13\n", + "-7.5 Z1, Z4, Z5, Z8, Z13\n", + "2.5 Z1, Z4, Z5, Z10, Z13\n", + "2.5 Z1, Z4, Z6, Z7, Z13\n", + "-7.5 Z1, Z4, Z8, Z9, Z13\n", + "2.5 Z1, Z4, Z10, Z11, Z13\n", + "7.5 Z1, Z5, Z7, Z9, Z13\n", + "-7.5 Z1, Z5, Z7, Z11, Z13\n", + "2.5 Z1, Z5, Z6, Z8, Z13\n", + "-2.5 Z1, Z5, Z6, Z10, Z13\n", + "2.5 Z1, Z5, Z6, Z7, Z8, Z9, Z13\n", + "-2.5 Z1, Z5, Z6, Z7, Z10, Z11, Z13\n", + "7.5 Z1, Z5, Z9, Z11, Z13\n", + "2.5 Z1, Z5, Z8, Z10, Z13\n", + "2.5 Z1, Z5, Z8, Z9, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z2, Z7, Z13\n", + "2.5 Z0, Z1, Z4, Z7, Z13\n", + "2.5 Z0, Z1, Z7, Z8, Z13\n", + "2.5 Z0, Z1, Z7, Z10, Z13\n", + "2.5 Z0, Z2, Z3, Z7, Z13\n", + "2.5 Z0, Z4, Z5, Z7, Z13\n", + "2.5 Z0, Z7, Z8, Z9, Z13\n", + "2.5 Z0, Z7, Z10, Z11, Z13\n", + "-2.5 Z1, Z2, Z4, Z7, Z13\n", + "-2.5 Z1, Z2, Z7, Z8, Z13\n", + "2.5 Z1, Z2, Z7, Z10, Z13\n", + "-2.5 Z1, Z2, Z3, Z4, Z5, Z7, Z13\n", + "-2.5 Z1, Z2, Z3, Z7, Z8, Z9, Z13\n", + "2.5 Z1, Z2, Z3, Z7, Z10, Z11, Z13\n", + "2.5 Z1, Z4, Z7, Z8, Z13\n", + "-2.5 Z1, Z4, Z7, Z10, Z13\n", + "2.5 Z1, Z4, Z5, Z7, Z8, Z9, Z13\n", + "-2.5 Z1, Z4, Z5, Z7, Z10, Z11, Z13\n", + "2.5 Z1, Z6, Z7, Z8, Z13\n", + "2.5 Z1, Z6, Z7, Z10, Z13\n", + "2.5 Z1, Z6, Z8, Z9, Z13\n", + "2.5 Z1, Z6, Z10, Z11, Z13\n", + "-7.5 Z1, Z7, Z9, Z11, Z13\n", + "-2.5 Z1, Z7, Z8, Z10, Z13\n", + "-2.5 Z1, Z7, Z8, Z9, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z2, Z9, Z13\n", + "-7.5 Z0, Z1, Z4, Z9, Z13\n", + "2.5 Z0, Z1, Z6, Z9, Z13\n", + "2.5 Z0, Z1, Z9, Z10, Z13\n", + "2.5 Z0, Z2, Z3, Z9, Z13\n", + "-7.5 Z0, Z4, Z5, Z9, Z13\n", + "2.5 Z0, Z6, Z7, Z9, Z13\n", + "2.5 Z0, Z9, Z10, Z11, Z13\n", + "2.5 Z1, Z2, Z4, Z9, Z13\n", + "-2.5 Z1, Z2, Z6, Z9, Z13\n", + "-2.5 Z1, Z2, Z9, Z10, Z13\n", + "2.5 Z1, Z2, Z3, Z4, Z5, Z9, Z13\n", + "-2.5 Z1, Z2, Z3, Z6, Z7, Z9, Z13\n", + "-2.5 Z1, Z2, Z3, Z9, Z10, Z11, Z13\n", + "2.5 Z1, Z4, Z6, Z9, Z13\n", + "2.5 Z1, Z4, Z9, Z10, Z13\n", + "2.5 Z1, Z4, Z5, Z6, Z7, Z9, Z13\n", + "2.5 Z1, Z4, Z5, Z9, Z10, Z11, Z13\n", + "-2.5 Z1, Z6, Z9, Z10, Z13\n", + "-2.5 Z1, Z6, Z7, Z9, Z10, Z11, Z13\n", + "2.5 Z1, Z8, Z9, Z10, Z13\n", + "2.5 Z1, Z8, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z2, Z11, Z13\n", + "2.5 Z0, Z1, Z4, Z11, Z13\n", + "2.5 Z0, Z1, Z6, Z11, Z13\n", + "2.5 Z0, Z1, Z8, Z11, Z13\n", + "2.5 Z0, Z2, Z3, Z11, Z13\n", + "2.5 Z0, Z4, Z5, Z11, Z13\n", + "2.5 Z0, Z6, Z7, Z11, Z13\n", + "2.5 Z0, Z8, Z9, Z11, Z13\n", + "-2.5 Z1, Z2, Z4, Z11, Z13\n", + "2.5 Z1, Z2, Z6, Z11, Z13\n", + "-2.5 Z1, Z2, Z8, Z11, Z13\n", + "-2.5 Z1, Z2, Z3, Z4, Z5, Z11, Z13\n", + "2.5 Z1, Z2, Z3, Z6, Z7, Z11, Z13\n", + "-2.5 Z1, Z2, Z3, Z8, Z9, Z11, Z13\n", + "-2.5 Z1, Z4, Z6, Z11, Z13\n", + "2.5 Z1, Z4, Z8, Z11, Z13\n", + "-2.5 Z1, Z4, Z5, Z6, Z7, Z11, Z13\n", + "2.5 Z1, Z4, Z5, Z8, Z9, Z11, Z13\n", + "-2.5 Z1, Z6, Z8, Z11, Z13\n", + "-2.5 Z1, Z6, Z7, Z8, Z9, Z11, Z13\n", + "-2.5 Z0, Z2, Z5, Z7, Z13\n", + "2.5 Z0, Z2, Z5, Z9, Z13\n", + "-2.5 Z0, Z2, Z5, Z11, Z13\n", + "-7.5 Z0, Z2, Z4, Z6, Z13\n", + "7.5 Z0, Z2, Z4, Z8, Z13\n", + "-7.5 Z0, Z2, Z4, Z10, Z13\n", + "-2.5 Z0, Z2, Z4, Z5, Z6, Z7, Z13\n", + "2.5 Z0, Z2, Z4, Z5, Z8, Z9, Z13\n", + "-2.5 Z0, Z2, Z4, Z5, Z10, Z11, Z13\n", + "-2.5 Z0, Z2, Z7, Z9, Z13\n", + "2.5 Z0, Z2, Z7, Z11, Z13\n", + "-7.5 Z0, Z2, Z6, Z8, Z13\n", + "7.5 Z0, Z2, Z6, Z10, Z13\n", + "-2.5 Z0, Z2, Z6, Z7, Z8, Z9, Z13\n", + "2.5 Z0, Z2, Z6, Z7, Z10, Z11, Z13\n", + "-2.5 Z0, Z2, Z9, Z11, Z13\n", + "-7.5 Z0, Z2, Z8, Z10, Z13\n", + "-2.5 Z0, Z2, Z8, Z9, Z10, Z11, Z13\n", + "-2.5 Z0, Z3, Z4, Z7, Z13\n", + "2.5 Z0, Z3, Z4, Z9, Z13\n", + "-2.5 Z0, Z3, Z4, Z11, Z13\n", + "-2.5 Z0, Z2, Z3, Z4, Z6, Z7, Z13\n", + "2.5 Z0, Z2, Z3, Z4, Z8, Z9, Z13\n", + "-2.5 Z0, Z2, Z3, Z4, Z10, Z11, Z13\n", + "2.5 Z0, Z4, Z7, Z9, Z13\n", + "-2.5 Z0, Z4, Z7, Z11, Z13\n", + "7.5 Z0, Z4, Z6, Z8, Z13\n", + "-7.5 Z0, Z4, Z6, Z10, Z13\n", + "2.5 Z0, Z4, Z6, Z7, Z8, Z9, Z13\n", + "-2.5 Z0, Z4, Z6, Z7, Z10, Z11, Z13\n", + "2.5 Z0, Z4, Z9, Z11, Z13\n", + "7.5 Z0, Z4, Z8, Z10, Z13\n", + "2.5 Z0, Z4, Z8, Z9, Z10, Z11, Z13\n", + "-2.5 Z0, Z3, Z5, Z6, Z13\n", + "-2.5 Z0, Z3, Z6, Z9, Z13\n", + "2.5 Z0, Z3, Z6, Z11, Z13\n", + "-2.5 Z0, Z2, Z3, Z4, Z5, Z6, Z13\n", + "-2.5 Z0, Z2, Z3, Z6, Z8, Z9, Z13\n", + "2.5 Z0, Z2, Z3, Z6, Z10, Z11, Z13\n", + "2.5 Z0, Z5, Z6, Z9, Z13\n", + "-2.5 Z0, Z5, Z6, Z11, Z13\n", + "2.5 Z0, Z4, Z5, Z6, Z8, Z9, Z13\n", + "-2.5 Z0, Z4, Z5, Z6, Z10, Z11, Z13\n", + "-2.5 Z0, Z6, Z9, Z11, Z13\n", + "-7.5 Z0, Z6, Z8, Z10, Z13\n", + "-2.5 Z0, Z6, Z8, Z9, Z10, Z11, Z13\n", + "2.5 Z0, Z3, Z5, Z8, Z13\n", + "-2.5 Z0, Z3, Z7, Z8, Z13\n", + "-2.5 Z0, Z3, Z8, Z11, Z13\n", + "2.5 Z0, Z2, Z3, Z4, Z5, Z8, Z13\n", + "-2.5 Z0, Z2, Z3, Z6, Z7, Z8, Z13\n", + "-2.5 Z0, Z2, Z3, Z8, Z10, Z11, Z13\n", + "2.5 Z0, Z5, Z7, Z8, Z13\n", + "2.5 Z0, Z5, Z8, Z11, Z13\n", + "2.5 Z0, Z4, Z5, Z6, Z7, Z8, Z13\n", + "2.5 Z0, Z4, Z5, Z8, Z10, Z11, Z13\n", + "-2.5 Z0, Z7, Z8, Z11, Z13\n", + "-2.5 Z0, Z6, Z7, Z8, Z10, Z11, Z13\n", + "-2.5 Z0, Z3, Z5, Z10, Z13\n", + "2.5 Z0, Z3, Z7, Z10, Z13\n", + "-2.5 Z0, Z3, Z9, Z10, Z13\n", + "-2.5 Z0, Z2, Z3, Z4, Z5, Z10, Z13\n", + "2.5 Z0, Z2, Z3, Z6, Z7, Z10, Z13\n", + "-2.5 Z0, Z2, Z3, Z8, Z9, Z10, Z13\n", + "-2.5 Z0, Z5, Z7, Z10, Z13\n", + "2.5 Z0, Z5, Z9, Z10, Z13\n", + "-2.5 Z0, Z4, Z5, Z6, Z7, Z10, Z13\n", + "2.5 Z0, Z4, Z5, Z8, Z9, Z10, Z13\n", + "-2.5 Z0, Z7, Z9, Z10, Z13\n", + "-2.5 Z0, Z6, Z7, Z8, Z9, Z10, Z13\n", + "-2.5 Z0, Z1, Z2, Z3, Z5, Z7, Z13\n", + "2.5 Z0, Z1, Z2, Z3, Z5, Z9, Z13\n", + "-2.5 Z0, Z1, Z2, Z3, Z5, Z11, Z13\n", + "-2.5 Z0, Z1, Z2, Z3, Z4, Z6, Z13\n", + "2.5 Z0, Z1, Z2, Z3, Z4, Z8, Z13\n", + "-2.5 Z0, Z1, Z2, Z3, Z4, Z10, Z13\n", + "-7.5 Z0, Z1, Z2, Z3, Z4, Z5, Z6, Z7, Z13\n", + "7.5 Z0, Z1, Z2, Z3, Z4, Z5, Z8, Z9, Z13\n", + "-7.5 Z0, Z1, Z2, Z3, Z4, Z5, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z2, Z3, Z7, Z9, Z13\n", + "2.5 Z0, Z1, Z2, Z3, Z7, Z11, Z13\n", + "-2.5 Z0, Z1, Z2, Z3, Z6, Z8, Z13\n", + "2.5 Z0, Z1, Z2, Z3, Z6, Z10, Z13\n", + "-7.5 Z0, Z1, Z2, Z3, Z6, Z7, Z8, Z9, Z13\n", + "7.5 Z0, Z1, Z2, Z3, Z6, Z7, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z2, Z3, Z9, Z11, Z13\n", + "-2.5 Z0, Z1, Z2, Z3, Z8, Z10, Z13\n", + "-7.5 Z0, Z1, Z2, Z3, Z8, Z9, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z3, Z4, Z5, Z7, Z13\n", + "2.5 Z0, Z1, Z3, Z4, Z5, Z9, Z13\n", + "-2.5 Z0, Z1, Z3, Z4, Z5, Z11, Z13\n", + "-2.5 Z0, Z1, Z2, Z4, Z5, Z6, Z13\n", + "2.5 Z0, Z1, Z2, Z4, Z5, Z8, Z13\n", + "-2.5 Z0, Z1, Z2, Z4, Z5, Z10, Z13\n", + "2.5 Z0, Z1, Z4, Z5, Z7, Z9, Z13\n", + "-2.5 Z0, Z1, Z4, Z5, Z7, Z11, Z13\n", + "2.5 Z0, Z1, Z4, Z5, Z6, Z8, Z13\n", + "-2.5 Z0, Z1, Z4, Z5, Z6, Z10, Z13\n", + "7.5 Z0, Z1, Z4, Z5, Z6, Z7, Z8, Z9, Z13\n", + "-7.5 Z0, Z1, Z4, Z5, Z6, Z7, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z4, Z5, Z9, Z11, Z13\n", + "2.5 Z0, Z1, Z4, Z5, Z8, Z10, Z13\n", + "7.5 Z0, Z1, Z4, Z5, Z8, Z9, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z3, Z5, Z6, Z7, Z13\n", + "-2.5 Z0, Z1, Z3, Z6, Z7, Z9, Z13\n", + "2.5 Z0, Z1, Z3, Z6, Z7, Z11, Z13\n", + "-2.5 Z0, Z1, Z2, Z4, Z6, Z7, Z13\n", + "-2.5 Z0, Z1, Z2, Z6, Z7, Z8, Z13\n", + "2.5 Z0, Z1, Z2, Z6, Z7, Z10, Z13\n", + "2.5 Z0, Z1, Z5, Z6, Z7, Z9, Z13\n", + "-2.5 Z0, Z1, Z5, Z6, Z7, Z11, Z13\n", + "2.5 Z0, Z1, Z4, Z6, Z7, Z8, Z13\n", + "-2.5 Z0, Z1, Z4, Z6, Z7, Z10, Z13\n", + "-2.5 Z0, Z1, Z6, Z7, Z9, Z11, Z13\n", + "-2.5 Z0, Z1, Z6, Z7, Z8, Z10, Z13\n", + "-7.5 Z0, Z1, Z6, Z7, Z8, Z9, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z3, Z5, Z8, Z9, Z13\n", + "-2.5 Z0, Z1, Z3, Z7, Z8, Z9, Z13\n", + "-2.5 Z0, Z1, Z3, Z8, Z9, Z11, Z13\n", + "2.5 Z0, Z1, Z2, Z4, Z8, Z9, Z13\n", + "-2.5 Z0, Z1, Z2, Z6, Z8, Z9, Z13\n", + "-2.5 Z0, Z1, Z2, Z8, Z9, Z10, Z13\n", + "2.5 Z0, Z1, Z5, Z7, Z8, Z9, Z13\n", + "2.5 Z0, Z1, Z5, Z8, Z9, Z11, Z13\n", + "2.5 Z0, Z1, Z4, Z6, Z8, Z9, Z13\n", + "2.5 Z0, Z1, Z4, Z8, Z9, Z10, Z13\n", + "-2.5 Z0, Z1, Z7, Z8, Z9, Z11, Z13\n", + "-2.5 Z0, Z1, Z6, Z8, Z9, Z10, Z13\n", + "-2.5 Z0, Z1, Z3, Z5, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z3, Z7, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z3, Z9, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z2, Z4, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z2, Z6, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z2, Z8, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z5, Z7, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z5, Z9, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z4, Z6, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z4, Z8, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z7, Z9, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z6, Z8, Z10, Z11, Z13\n", + "5.0 Z2, Z3, Z5, Z6, Z13\n", + "5.0 Z2, Z3, Z5, Z8, Z13\n", + "2.5 Z2, Z3, Z5, Z10, Z13\n", + "5.0 Z2, Z5, Z6, Z7, Z13\n", + "5.0 Z2, Z5, Z8, Z9, Z13\n", + "2.5 Z2, Z5, Z10, Z11, Z13\n", + "5.0 Z3, Z4, Z5, Z6, Z13\n", + "5.0 Z3, Z4, Z5, Z8, Z13\n", + "2.5 Z3, Z4, Z5, Z10, Z13\n", + "5.0 Z3, Z4, Z6, Z7, Z13\n", + "5.0 Z3, Z4, Z8, Z9, Z13\n", + "2.5 Z3, Z4, Z10, Z11, Z13\n", + "-15.0 Z3, Z5, Z7, Z9, Z13\n", + "7.5 Z3, Z5, Z7, Z11, Z13\n", + "-5.0 Z3, Z5, Z6, Z8, Z13\n", + "2.5 Z3, Z5, Z6, Z10, Z13\n", + "-5.0 Z3, Z5, Z6, Z7, Z8, Z9, Z13\n", + "2.5 Z3, Z5, Z6, Z7, Z10, Z11, Z13\n", + "-7.5 Z3, Z5, Z9, Z11, Z13\n", + "-2.5 Z3, Z5, Z8, Z10, Z13\n", + "-2.5 Z3, Z5, Z8, Z9, Z10, Z11, Z13\n", + "5.0 Z2, Z3, Z4, Z7, Z13\n", + "5.0 Z2, Z3, Z7, Z8, Z13\n", + "-7.5 Z2, Z3, Z7, Z10, Z13\n", + "5.0 Z2, Z4, Z5, Z7, Z13\n", + "5.0 Z2, Z7, Z8, Z9, Z13\n", + "-7.5 Z2, Z7, Z10, Z11, Z13\n", + "-5.0 Z3, Z4, Z7, Z8, Z13\n", + "2.5 Z3, Z4, Z7, Z10, Z13\n", + "-5.0 Z3, Z4, Z5, Z7, Z8, Z9, Z13\n", + "2.5 Z3, Z4, Z5, Z7, Z10, Z11, Z13\n", + "5.0 Z3, Z6, Z7, Z8, Z13\n", + "-7.5 Z3, Z6, Z7, Z10, Z13\n", + "5.0 Z3, Z6, Z8, Z9, Z13\n", + "-7.5 Z3, Z6, Z10, Z11, Z13\n", + "7.5 Z3, Z7, Z9, Z11, Z13\n", + "2.5 Z3, Z7, Z8, Z10, Z13\n", + "2.5 Z3, Z7, Z8, Z9, Z10, Z11, Z13\n", + "5.0 Z2, Z3, Z4, Z9, Z13\n", + "5.0 Z2, Z3, Z6, Z9, Z13\n", + "2.5 Z2, Z3, Z9, Z10, Z13\n", + "5.0 Z2, Z4, Z5, Z9, Z13\n", + "5.0 Z2, Z6, Z7, Z9, Z13\n", + "2.5 Z2, Z9, Z10, Z11, Z13\n", + "-5.0 Z3, Z4, Z6, Z9, Z13\n", + "-2.5 Z3, Z4, Z9, Z10, Z13\n", + "-5.0 Z3, Z4, Z5, Z6, Z7, Z9, Z13\n", + "-2.5 Z3, Z4, Z5, Z9, Z10, Z11, Z13\n", + "2.5 Z3, Z6, Z9, Z10, Z13\n", + "2.5 Z3, Z6, Z7, Z9, Z10, Z11, Z13\n", + "2.5 Z3, Z8, Z9, Z10, Z13\n", + "2.5 Z3, Z8, Z10, Z11, Z13\n", + "2.5 Z2, Z3, Z4, Z11, Z13\n", + "-7.5 Z2, Z3, Z6, Z11, Z13\n", + "2.5 Z2, Z3, Z8, Z11, Z13\n", + "2.5 Z2, Z4, Z5, Z11, Z13\n", + "-7.5 Z2, Z6, Z7, Z11, Z13\n", + "2.5 Z2, Z8, Z9, Z11, Z13\n", + "2.5 Z3, Z4, Z6, Z11, Z13\n", + "-2.5 Z3, Z4, Z8, Z11, Z13\n", + "2.5 Z3, Z4, Z5, Z6, Z7, Z11, Z13\n", + "-2.5 Z3, Z4, Z5, Z8, Z9, Z11, Z13\n", + "2.5 Z3, Z6, Z8, Z11, Z13\n", + "2.5 Z3, Z6, Z7, Z8, Z9, Z11, Z13\n", + "-5.0 Z2, Z4, Z7, Z9, Z13\n", + "2.5 Z2, Z4, Z7, Z11, Z13\n", + "-15.0 Z2, Z4, Z6, Z8, Z13\n", + "7.5 Z2, Z4, Z6, Z10, Z13\n", + "-5.0 Z2, Z4, Z6, Z7, Z8, Z9, Z13\n", + "2.5 Z2, Z4, Z6, Z7, Z10, Z11, Z13\n", + "-2.5 Z2, Z4, Z9, Z11, Z13\n", + "-7.5 Z2, Z4, Z8, Z10, Z13\n", + "-2.5 Z2, Z4, Z8, Z9, Z10, Z11, Z13\n", + "-5.0 Z2, Z5, Z6, Z9, Z13\n", + "2.5 Z2, Z5, Z6, Z11, Z13\n", + "-5.0 Z2, Z4, Z5, Z6, Z8, Z9, Z13\n", + "2.5 Z2, Z4, Z5, Z6, Z10, Z11, Z13\n", + "2.5 Z2, Z6, Z9, Z11, Z13\n", + "7.5 Z2, Z6, Z8, Z10, Z13\n", + "2.5 Z2, Z6, Z8, Z9, Z10, Z11, Z13\n", + "-5.0 Z2, Z5, Z7, Z8, Z13\n", + "-2.5 Z2, Z5, Z8, Z11, Z13\n", + "-5.0 Z2, Z4, Z5, Z6, Z7, Z8, Z13\n", + "-2.5 Z2, Z4, Z5, Z8, Z10, Z11, Z13\n", + "2.5 Z2, Z7, Z8, Z11, Z13\n", + "2.5 Z2, Z6, Z7, Z8, Z10, Z11, Z13\n", + "2.5 Z2, Z5, Z7, Z10, Z13\n", + "-2.5 Z2, Z5, Z9, Z10, Z13\n", + "2.5 Z2, Z4, Z5, Z6, Z7, Z10, Z13\n", + "-2.5 Z2, Z4, Z5, Z8, Z9, Z10, Z13\n", + "2.5 Z2, Z7, Z9, Z10, Z13\n", + "2.5 Z2, Z6, Z7, Z8, Z9, Z10, Z13\n", + "-5.0 Z2, Z3, Z4, Z5, Z7, Z9, Z13\n", + "2.5 Z2, Z3, Z4, Z5, Z7, Z11, Z13\n", + "-5.0 Z2, Z3, Z4, Z5, Z6, Z8, Z13\n", + "2.5 Z2, Z3, Z4, Z5, Z6, Z10, Z13\n", + "-15.0 Z2, Z3, Z4, Z5, Z6, Z7, Z8, Z9, Z13\n", + "7.5 Z2, Z3, Z4, Z5, Z6, Z7, Z10, Z11, Z13\n", + "-2.5 Z2, Z3, Z4, Z5, Z9, Z11, Z13\n", + "-2.5 Z2, Z3, Z4, Z5, Z8, Z10, Z13\n", + "-7.5 Z2, Z3, Z4, Z5, Z8, Z9, Z10, Z11, Z13\n", + "-5.0 Z2, Z3, Z5, Z6, Z7, Z9, Z13\n", + "2.5 Z2, Z3, Z5, Z6, Z7, Z11, Z13\n", + "-5.0 Z2, Z3, Z4, Z6, Z7, Z8, Z13\n", + "2.5 Z2, Z3, Z4, Z6, Z7, Z10, Z13\n", + "2.5 Z2, Z3, Z6, Z7, Z9, Z11, Z13\n", + "2.5 Z2, Z3, Z6, Z7, Z8, Z10, Z13\n", + "7.5 Z2, Z3, Z6, Z7, Z8, Z9, Z10, Z11, Z13\n", + "-5.0 Z2, Z3, Z5, Z7, Z8, Z9, Z13\n", + "-2.5 Z2, Z3, Z5, Z8, Z9, Z11, Z13\n", + "-5.0 Z2, Z3, Z4, Z6, Z8, Z9, Z13\n", + "-2.5 Z2, Z3, Z4, Z8, Z9, Z10, Z13\n", + "2.5 Z2, Z3, Z7, Z8, Z9, Z11, Z13\n", + "2.5 Z2, Z3, Z6, Z8, Z9, Z10, Z13\n", + "2.5 Z2, Z3, Z5, Z7, Z10, Z11, Z13\n", + "-2.5 Z2, Z3, Z5, Z9, Z10, Z11, Z13\n", + "2.5 Z2, Z3, Z4, Z6, Z10, Z11, Z13\n", + "-2.5 Z2, Z3, Z4, Z8, Z10, Z11, Z13\n", + "2.5 Z2, Z3, Z7, Z9, Z10, Z11, Z13\n", + "2.5 Z2, Z3, Z6, Z8, Z10, Z11, Z13\n", + "7.5 Z4, Z5, Z7, Z8, Z13\n", + "5.0 Z4, Z5, Z7, Z10, Z13\n", + "7.5 Z4, Z7, Z8, Z9, Z13\n", + "5.0 Z4, Z7, Z10, Z11, Z13\n", + "7.5 Z5, Z6, Z7, Z8, Z13\n", + "5.0 Z5, Z6, Z7, Z10, Z13\n", + "7.5 Z5, Z6, Z8, Z9, Z13\n", + "5.0 Z5, Z6, Z10, Z11, Z13\n", + "-15.0 Z5, Z7, Z9, Z11, Z13\n", + "-5.0 Z5, Z7, Z8, Z10, Z13\n", + "-5.0 Z5, Z7, Z8, Z9, Z10, Z11, Z13\n", + "7.5 Z4, Z5, Z6, Z9, Z13\n", + "5.0 Z4, Z5, Z9, Z10, Z13\n", + "7.5 Z4, Z6, Z7, Z9, Z13\n", + "5.0 Z4, Z9, Z10, Z11, Z13\n", + "-5.0 Z5, Z6, Z9, Z10, Z13\n", + "-5.0 Z5, Z6, Z7, Z9, Z10, Z11, Z13\n", + "5.0 Z5, Z8, Z9, Z10, Z13\n", + "5.0 Z5, Z8, Z10, Z11, Z13\n", + "5.0 Z4, Z5, Z6, Z11, Z13\n", + "5.0 Z4, Z5, Z8, Z11, Z13\n", + "5.0 Z4, Z6, Z7, Z11, Z13\n", + "5.0 Z4, Z8, Z9, Z11, Z13\n", + "-5.0 Z5, Z6, Z8, Z11, Z13\n", + "-5.0 Z5, Z6, Z7, Z8, Z9, Z11, Z13\n", + "-5.0 Z4, Z6, Z9, Z11, Z13\n", + "-15.0 Z4, Z6, Z8, Z10, Z13\n", + "-5.0 Z4, Z6, Z8, Z9, Z10, Z11, Z13\n", + "-5.0 Z4, Z7, Z8, Z11, Z13\n", + "-5.0 Z4, Z6, Z7, Z8, Z10, Z11, Z13\n", + "-5.0 Z4, Z7, Z9, Z10, Z13\n", + "-5.0 Z4, Z6, Z7, Z8, Z9, Z10, Z13\n", + "-5.0 Z4, Z5, Z6, Z7, Z9, Z11, Z13\n", + "-5.0 Z4, Z5, Z6, Z7, Z8, Z10, Z13\n", + "-15.0 Z4, Z5, Z6, Z7, Z8, Z9, Z10, Z11, Z13\n", + "-5.0 Z4, Z5, Z7, Z8, Z9, Z11, Z13\n", + "-5.0 Z4, Z5, Z6, Z8, Z9, Z10, Z13\n", + "-5.0 Z4, Z5, Z7, Z9, Z10, Z11, Z13\n", + "-5.0 Z4, Z5, Z6, Z8, Z10, Z11, Z13\n", + "5.0 Z6, Z7, Z9, Z10, Z13\n", + "5.0 Z6, Z9, Z10, Z11, Z13\n", + "5.0 Z7, Z8, Z9, Z10, Z13\n", + "5.0 Z7, Z8, Z10, Z11, Z13\n", + "5.0 Z6, Z7, Z8, Z11, Z13\n", + "5.0 Z6, Z8, Z9, Z11, Z13\n" + ] + } + ], + "source": [ + "# 其中 lambda0 和 lambda1 是蛋白质哈密顿量中的两个参数,用于约束蛋白质的空间结构。\n", + "h = protein.get_protein_hamiltonian(lambda0=10.0, lambda1=10.0)\n", + "print(h)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 变分量子线路\n", + "用户可以使用变分量子算法(VQE)来解决蛋白质折叠问题。这个问题中的变分量子线路如下。" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from folding_protein import circuit\n", + "\n", + "# 下面的代码搭建了一个宽度为4,包含两个线路单元的参数化量子线路(注:一个线路单元包含一层RY和一层CNOT)。\n", + "cir = circuit(4, 2)\n", + "cir.plot()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 如何使用\n", + "用户可以通过编辑配置文件config.toml对任务进行自定义,其中用户可以设置的参数如下\n", + "```toml\n", + "# The configuration file for protein folding problem\n", + "\n", + "# The amino acides consists in protein. \n", + "amino_acids = [\"A\", \"P\", \"R\", \"L\", \"R\", \"F\", \"Y\"]\n", + "# Pair of indices indicates the potentially interact amino acide pair.\n", + "possible_contactions = [[0, 5], [1, 6]]\n", + "# Depth of the quantum circuit used in VQE\n", + "depth = 1\n", + "# Number of VQE iterations\n", + "num_iterations = 200\n", + "# The condition for VQE convergence\n", + "tol = 1e-3\n", + "# The number of steps between two consecutive loss records\n", + "save_every = 10\n", + "# learning rate for the optimizer\n", + "learning_rate = 0.5\n", + "```\n", + "用户可以在命令行中运行\n", + "```shell\n", + "python folding_protein.py --config config.toml\n", + "```\n", + "来得到上面氨基酸序列在空间中折叠的三维结构,程序会自动保存这个空间结构的图片。\n", + "\n", + "![](APRLRFY_3d_structure.jpg)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 参考文献\n", + "\\[1\\] Pande, Vijay S., and Daniel S. Rokhsar. \"Folding pathway of a lattice model for proteins.\" Proceedings of the National Academy of Sciences 96.4 (1999): 1273-1278.\n", + "\n", + "\\[2\\] Robert, Anton, et al. \"Resource-efficient quantum algorithm for protein folding.\" npj Quantum Information 7.1 (2021): 1-5." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "modellib", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.15 (default, Nov 10 2022, 12:46:26) \n[Clang 14.0.6 ]" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "8f24120f890011f53feb4ed62c47961d8565ec1de8b7cb23548c15bd6da8f2d2" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/applications/protein_folding/introduction_en.ipynb b/applications/protein_folding/introduction_en.ipynb new file mode 100644 index 0000000..0dde5a5 --- /dev/null +++ b/applications/protein_folding/introduction_en.ipynb @@ -0,0 +1,1640 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import warnings\n", + "\n", + "warnings.filterwarnings(\"ignore\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Introduction to protein functionality and structure design\n", + "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*\n", + "\n", + "Proteins are important structural and functional molecules in biological organisms. They form long chains by assembling amino acids and realize specific functionality through the spatial structure of amino acid chains. The spatial structure of proteins refers to the conformation of amino acid chains in three-dimensional space. This conformation can be determined by X-ray diffraction, nuclear magnetic resonance, or other methods. Studies have shown that the spatial structure of proteins is closely related to their functionalities. For example, as an important class of proteins, enzymes determine their interaction with substrates through their spatial structure, thus realizing the catalytic effect of chemical reactions. In addition, the spatial structure of proteins can be controlled by changing the amino acid sequence to regulate their functionality.\n", + "\n", + "Proteins produce different spatial structures in different folding modes, it is important to study protein folding in order to predict protein behaviors. Due to the high complexity and non-linearity appearing in the protein structures, it's inefficient to solve protein folding problems via classical computation. Quantum computation can quickly solve complex non-linear optimization problems based on the principles of quantum mechanics, and is generally believed to be of great help in future research on protein folding." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Using quantum computation method to simulate the protein folding\n", + "### Lattice model\n", + "In protein structure research, a common method is the lattice model \\[1\\], which divides the amino acid chain in the protein into a series of lattice units, each of which includes several amino acids. By analyzing the interaction between lattice units, we can infer the spatial structure of the protein. The lattice model can help us better understand the structure and function of proteins, and provide an important theoretical basis for drug design and disease treatment.\n", + "![](lattice_model_en.jpg)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Protein with 7 nodes and 6 edges\n" + ] + } + ], + "source": [ + "from paddle_quantum.biocomputing import Protein\n", + "\n", + "protein = Protein(\"APRLRFY\")\n", + "print(protein)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With the `biocomputing` module in `paddle_quantum`, we can easily construct a protein., the above code has built an amino acid chain contains 7 amino acids.\n", + "\n", + "Starting from the lattice model, we can obtain a protein Hamiltonian \\[2\\] based on the interactions between lattice points. We can then construct a variational quantum algorithm (VQE) based on this Hamiltonian to solve for the stable structure (the structure with the lowest energy) of the protein molecule." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "327.0085 I\n", + "-47.5 Z1, Z3\n", + "65.0 Z1, Z5\n", + "-50.0 Z1, Z7\n", + "52.5 Z1, Z9\n", + "-47.5 Z0, Z2\n", + "65.0 Z0, Z4\n", + "-50.0 Z0, Z6\n", + "52.5 Z0, Z8\n", + "-47.5 Z0, Z1, Z2, Z3\n", + "65.0 Z0, Z1, Z4, Z5\n", + "-50.0 Z0, Z1, Z6, Z7\n", + "52.5 Z0, Z1, Z8, Z9\n", + "-65.0 Z3, Z5\n", + "92.5 Z3, Z7\n", + "-60.0 Z3, Z9\n", + "-65.0 Z2, Z4\n", + "92.5 Z2, Z6\n", + "-60.0 Z2, Z8\n", + "-65.0 Z2, Z3, Z4, Z5\n", + "92.5 Z2, Z3, Z6, Z7\n", + "-60.0 Z2, Z3, Z8, Z9\n", + "-72.5 Z5, Z7\n", + "92.5 Z5, Z9\n", + "-72.5 Z4, Z6\n", + "92.5 Z4, Z8\n", + "-72.5 Z4, Z5, Z6, Z7\n", + "92.5 Z4, Z5, Z8, Z9\n", + "-65.0 Z7, Z9\n", + "-65.0 Z6, Z8\n", + "-65.0 Z6, Z7, Z8, Z9\n", + "-157.168 Z12\n", + "30.0 Z1, Z3, Z12\n", + "-40.0 Z1, Z5, Z12\n", + "30.0 Z1, Z7, Z12\n", + "-27.5 Z1, Z9, Z12\n", + "30.0 Z0, Z2, Z12\n", + "-40.0 Z0, Z4, Z12\n", + "30.0 Z0, Z6, Z12\n", + "-27.5 Z0, Z8, Z12\n", + "30.0 Z0, Z1, Z2, Z3, Z12\n", + "-40.0 Z0, Z1, Z4, Z5, Z12\n", + "30.0 Z0, Z1, Z6, Z7, Z12\n", + "-27.5 Z0, Z1, Z8, Z9, Z12\n", + "37.5 Z3, Z5, Z12\n", + "-52.5 Z3, Z7, Z12\n", + "30.0 Z3, Z9, Z12\n", + "37.5 Z2, Z4, Z12\n", + "-52.5 Z2, Z6, Z12\n", + "30.0 Z2, Z8, Z12\n", + "37.5 Z2, Z3, Z4, Z5, Z12\n", + "-52.5 Z2, Z3, Z6, Z7, Z12\n", + "30.0 Z2, Z3, Z8, Z9, Z12\n", + "37.5 Z5, Z7, Z12\n", + "-40.0 Z5, Z9, Z12\n", + "37.5 Z4, Z6, Z12\n", + "-40.0 Z4, Z8, Z12\n", + "37.5 Z4, Z5, Z6, Z7, Z12\n", + "-40.0 Z4, Z5, Z8, Z9, Z12\n", + "30.0 Z7, Z9, Z12\n", + "30.0 Z6, Z8, Z12\n", + "30.0 Z6, Z7, Z8, Z9, Z12\n", + "-12.5 Z2, Z3, Z5, Z6\n", + "-10.0 Z2, Z3, Z5, Z8\n", + "-12.5 Z2, Z5, Z6, Z7\n", + "-10.0 Z2, Z5, Z8, Z9\n", + "-12.5 Z3, Z4, Z5, Z6\n", + "-10.0 Z3, Z4, Z5, Z8\n", + "-12.5 Z3, Z4, Z6, Z7\n", + "-10.0 Z3, Z4, Z8, Z9\n", + "30.0 Z3, Z5, Z7, Z9\n", + "10.0 Z3, Z5, Z6, Z8\n", + "10.0 Z3, Z5, Z6, Z7, Z8, Z9\n", + "-12.5 Z2, Z3, Z4, Z7\n", + "-10.0 Z2, Z3, Z7, Z8\n", + "-12.5 Z2, Z4, Z5, Z7\n", + "-10.0 Z2, Z7, Z8, Z9\n", + "10.0 Z3, Z4, Z7, Z8\n", + "10.0 Z3, Z4, Z5, Z7, Z8, Z9\n", + "-10.0 Z3, Z6, Z7, Z8\n", + "-10.0 Z3, Z6, Z8, Z9\n", + "-10.0 Z2, Z3, Z4, Z9\n", + "-10.0 Z2, Z3, Z6, Z9\n", + "-10.0 Z2, Z4, Z5, Z9\n", + "-10.0 Z2, Z6, Z7, Z9\n", + "10.0 Z3, Z4, Z6, Z9\n", + "10.0 Z3, Z4, Z5, Z6, Z7, Z9\n", + "10.0 Z2, Z4, Z7, Z9\n", + "30.0 Z2, Z4, Z6, Z8\n", + "10.0 Z2, Z4, Z6, Z7, Z8, Z9\n", + "10.0 Z2, Z5, Z6, Z9\n", + "10.0 Z2, Z4, Z5, Z6, Z8, Z9\n", + "10.0 Z2, Z5, Z7, Z8\n", + "10.0 Z2, Z4, Z5, Z6, Z7, Z8\n", + "10.0 Z2, Z3, Z4, Z5, Z7, Z9\n", + "10.0 Z2, Z3, Z4, Z5, Z6, Z8\n", + "30.0 Z2, Z3, Z4, Z5, Z6, Z7, Z8, Z9\n", + "10.0 Z2, Z3, Z5, Z6, Z7, Z9\n", + "10.0 Z2, Z3, Z4, Z6, Z7, Z8\n", + "10.0 Z2, Z3, Z5, Z7, Z8, Z9\n", + "10.0 Z2, Z3, Z4, Z6, Z8, Z9\n", + "-12.5 Z4, Z5, Z7, Z8\n", + "-12.5 Z4, Z7, Z8, Z9\n", + "-12.5 Z5, Z6, Z7, Z8\n", + "-12.5 Z5, Z6, Z8, Z9\n", + "-12.5 Z4, Z5, Z6, Z9\n", + "-12.5 Z4, Z6, Z7, Z9\n", + "7.5 Z2, Z3, Z5, Z6, Z12\n", + "5.0 Z2, Z3, Z5, Z8, Z12\n", + "7.5 Z2, Z5, Z6, Z7, Z12\n", + "5.0 Z2, Z5, Z8, Z9, Z12\n", + "7.5 Z3, Z4, Z5, Z6, Z12\n", + "5.0 Z3, Z4, Z5, Z8, Z12\n", + "7.5 Z3, Z4, Z6, Z7, Z12\n", + "5.0 Z3, Z4, Z8, Z9, Z12\n", + "-15.0 Z3, Z5, Z7, Z9, Z12\n", + "-5.0 Z3, Z5, Z6, Z8, Z12\n", + "-5.0 Z3, Z5, Z6, Z7, Z8, Z9, Z12\n", + "7.5 Z2, Z3, Z4, Z7, Z12\n", + "5.0 Z2, Z3, Z7, Z8, Z12\n", + "7.5 Z2, Z4, Z5, Z7, Z12\n", + "5.0 Z2, Z7, Z8, Z9, Z12\n", + "-5.0 Z3, Z4, Z7, Z8, Z12\n", + "-5.0 Z3, Z4, Z5, Z7, Z8, Z9, Z12\n", + "5.0 Z3, Z6, Z7, Z8, Z12\n", + "5.0 Z3, Z6, Z8, Z9, Z12\n", + "5.0 Z2, Z3, Z4, Z9, Z12\n", + "5.0 Z2, Z3, Z6, Z9, Z12\n", + "5.0 Z2, Z4, Z5, Z9, Z12\n", + "5.0 Z2, Z6, Z7, Z9, Z12\n", + "-5.0 Z3, Z4, Z6, Z9, Z12\n", + "-5.0 Z3, Z4, Z5, Z6, Z7, Z9, Z12\n", + "-5.0 Z2, Z4, Z7, Z9, Z12\n", + "-15.0 Z2, Z4, Z6, Z8, Z12\n", + "-5.0 Z2, Z4, Z6, Z7, Z8, Z9, Z12\n", + "-5.0 Z2, Z5, Z6, Z9, Z12\n", + "-5.0 Z2, Z4, Z5, Z6, Z8, Z9, Z12\n", + "-5.0 Z2, Z5, Z7, Z8, Z12\n", + "-5.0 Z2, Z4, Z5, Z6, Z7, Z8, Z12\n", + "-5.0 Z2, Z3, Z4, Z5, Z7, Z9, Z12\n", + "-5.0 Z2, Z3, Z4, Z5, Z6, Z8, Z12\n", + "-15.0 Z2, Z3, Z4, Z5, Z6, Z7, Z8, Z9, Z12\n", + "-5.0 Z2, Z3, Z5, Z6, Z7, Z9, Z12\n", + "-5.0 Z2, Z3, Z4, Z6, Z7, Z8, Z12\n", + "-5.0 Z2, Z3, Z5, Z7, Z8, Z9, Z12\n", + "-5.0 Z2, Z3, Z4, Z6, Z8, Z9, Z12\n", + "5.0 Z4, Z5, Z7, Z8, Z12\n", + "5.0 Z4, Z7, Z8, Z9, Z12\n", + "5.0 Z5, Z6, Z7, Z8, Z12\n", + "5.0 Z5, Z6, Z8, Z9, Z12\n", + "5.0 Z4, Z5, Z6, Z9, Z12\n", + "5.0 Z4, Z6, Z7, Z9, Z12\n", + "-7.5 Z0, Z1, Z3, Z4\n", + "-7.5 Z0, Z1, Z3, Z6\n", + "-7.5 Z0, Z3, Z4, Z5\n", + "-7.5 Z0, Z3, Z6, Z7\n", + "-7.5 Z1, Z2, Z3, Z4\n", + "-7.5 Z1, Z2, Z3, Z6\n", + "-7.5 Z1, Z2, Z4, Z5\n", + "-7.5 Z1, Z2, Z6, Z7\n", + "22.5 Z1, Z3, Z5, Z7\n", + "7.5 Z1, Z3, Z4, Z6\n", + "7.5 Z1, Z3, Z4, Z5, Z6, Z7\n", + "-7.5 Z0, Z1, Z2, Z5\n", + "-7.5 Z0, Z1, Z5, Z6\n", + "-7.5 Z0, Z2, Z3, Z5\n", + "-7.5 Z0, Z5, Z6, Z7\n", + "7.5 Z1, Z2, Z5, Z6\n", + "7.5 Z1, Z2, Z3, Z5, Z6, Z7\n", + "-7.5 Z1, Z4, Z5, Z6\n", + "-7.5 Z1, Z4, Z6, Z7\n", + "-7.5 Z0, Z1, Z2, Z7\n", + "-7.5 Z0, Z1, Z4, Z7\n", + "-7.5 Z0, Z2, Z3, Z7\n", + "-7.5 Z0, Z4, Z5, Z7\n", + "7.5 Z1, Z2, Z4, Z7\n", + "7.5 Z1, Z2, Z3, Z4, Z5, Z7\n", + "7.5 Z0, Z2, Z5, Z7\n", + "22.5 Z0, Z2, Z4, Z6\n", + "7.5 Z0, Z2, Z4, Z5, Z6, Z7\n", + "7.5 Z0, Z3, Z4, Z7\n", + "7.5 Z0, Z2, Z3, Z4, Z6, Z7\n", + "7.5 Z0, Z3, Z5, Z6\n", + "7.5 Z0, Z2, Z3, Z4, Z5, Z6\n", + "7.5 Z0, Z1, Z2, Z3, Z5, Z7\n", + "7.5 Z0, Z1, Z2, Z3, Z4, Z6\n", + "22.5 Z0, Z1, Z2, Z3, Z4, Z5, Z6, Z7\n", + "7.5 Z0, Z1, Z3, Z4, Z5, Z7\n", + "7.5 Z0, Z1, Z2, Z4, Z5, Z6\n", + "7.5 Z0, Z1, Z3, Z5, Z6, Z7\n", + "7.5 Z0, Z1, Z2, Z4, Z6, Z7\n", + "5.0 Z0, Z1, Z3, Z4, Z12\n", + "5.0 Z0, Z1, Z3, Z6, Z12\n", + "5.0 Z0, Z3, Z4, Z5, Z12\n", + "5.0 Z0, Z3, Z6, Z7, Z12\n", + "5.0 Z1, Z2, Z3, Z4, Z12\n", + "5.0 Z1, Z2, Z3, Z6, Z12\n", + "5.0 Z1, Z2, Z4, Z5, Z12\n", + "5.0 Z1, Z2, Z6, Z7, Z12\n", + "-15.0 Z1, Z3, Z5, Z7, Z12\n", + "-5.0 Z1, Z3, Z4, Z6, Z12\n", + "-5.0 Z1, Z3, Z4, Z5, Z6, Z7, Z12\n", + "5.0 Z0, Z1, Z2, Z5, Z12\n", + "5.0 Z0, Z1, Z5, Z6, Z12\n", + "5.0 Z0, Z2, Z3, Z5, Z12\n", + "5.0 Z0, Z5, Z6, Z7, Z12\n", + "-5.0 Z1, Z2, Z5, Z6, Z12\n", + "-5.0 Z1, Z2, Z3, Z5, Z6, Z7, Z12\n", + "5.0 Z1, Z4, Z5, Z6, Z12\n", + "5.0 Z1, Z4, Z6, Z7, Z12\n", + "5.0 Z0, Z1, Z2, Z7, Z12\n", + "5.0 Z0, Z1, Z4, Z7, Z12\n", + "5.0 Z0, Z2, Z3, Z7, Z12\n", + "5.0 Z0, Z4, Z5, Z7, Z12\n", + "-5.0 Z1, Z2, Z4, Z7, Z12\n", + "-5.0 Z1, Z2, Z3, Z4, Z5, Z7, Z12\n", + "-5.0 Z0, Z2, Z5, Z7, Z12\n", + "-15.0 Z0, Z2, Z4, Z6, Z12\n", + "-5.0 Z0, Z2, Z4, Z5, Z6, Z7, Z12\n", + "-5.0 Z0, Z3, Z4, Z7, Z12\n", + "-5.0 Z0, Z2, Z3, Z4, Z6, Z7, Z12\n", + "-5.0 Z0, Z3, Z5, Z6, Z12\n", + "-5.0 Z0, Z2, Z3, Z4, Z5, Z6, Z12\n", + "-5.0 Z0, Z1, Z2, Z3, Z5, Z7, Z12\n", + "-5.0 Z0, Z1, Z2, Z3, Z4, Z6, Z12\n", + "-15.0 Z0, Z1, Z2, Z3, Z4, Z5, Z6, Z7, Z12\n", + "-5.0 Z0, Z1, Z3, Z4, Z5, Z7, Z12\n", + "-5.0 Z0, Z1, Z2, Z4, Z5, Z6, Z12\n", + "-5.0 Z0, Z1, Z3, Z5, Z6, Z7, Z12\n", + "-5.0 Z0, Z1, Z2, Z4, Z6, Z7, Z12\n", + "-40.0 Z1, Z11\n", + "-40.0 Z0, Z10\n", + "-40.0 Z0, Z1, Z10, Z11\n", + "52.5 Z3, Z11\n", + "52.5 Z2, Z10\n", + "52.5 Z2, Z3, Z10, Z11\n", + "-50.0 Z5, Z11\n", + "-50.0 Z4, Z10\n", + "-50.0 Z4, Z5, Z10, Z11\n", + "65.0 Z7, Z11\n", + "65.0 Z6, Z10\n", + "65.0 Z6, Z7, Z10, Z11\n", + "-47.5 Z9, Z11\n", + "-47.5 Z8, Z10\n", + "-47.5 Z8, Z9, Z10, Z11\n", + "-5.0 Z0, Z1, Z3, Z8\n", + "-5.0 Z0, Z1, Z3, Z10\n", + "-5.0 Z0, Z3, Z8, Z9\n", + "-5.0 Z0, Z3, Z10, Z11\n", + "-5.0 Z1, Z2, Z3, Z8\n", + "-5.0 Z1, Z2, Z3, Z10\n", + "-5.0 Z1, Z2, Z8, Z9\n", + "-5.0 Z1, Z2, Z10, Z11\n", + "-15.0 Z1, Z3, Z5, Z9\n", + "15.0 Z1, Z3, Z5, Z11\n", + "-5.0 Z1, Z3, Z4, Z8\n", + "5.0 Z1, Z3, Z4, Z10\n", + "-5.0 Z1, Z3, Z4, Z5, Z8, Z9\n", + "5.0 Z1, Z3, Z4, Z5, Z10, Z11\n", + "15.0 Z1, Z3, Z7, Z9\n", + "-15.0 Z1, Z3, Z7, Z11\n", + "5.0 Z1, Z3, Z6, Z8\n", + "-5.0 Z1, Z3, Z6, Z10\n", + "5.0 Z1, Z3, Z6, Z7, Z8, Z9\n", + "-5.0 Z1, Z3, Z6, Z7, Z10, Z11\n", + "15.0 Z1, Z3, Z9, Z11\n", + "5.0 Z1, Z3, Z8, Z10\n", + "5.0 Z1, Z3, Z8, Z9, Z10, Z11\n", + "15.0 Z0, Z1, Z5, Z8\n", + "-5.0 Z0, Z1, Z5, Z10\n", + "15.0 Z0, Z5, Z8, Z9\n", + "-5.0 Z0, Z5, Z10, Z11\n", + "-5.0 Z1, Z2, Z5, Z8\n", + "5.0 Z1, Z2, Z5, Z10\n", + "-5.0 Z1, Z2, Z3, Z5, Z8, Z9\n", + "5.0 Z1, Z2, Z3, Z5, Z10, Z11\n", + "15.0 Z1, Z4, Z5, Z8\n", + "-5.0 Z1, Z4, Z5, Z10\n", + "15.0 Z1, Z4, Z8, Z9\n", + "-5.0 Z1, Z4, Z10, Z11\n", + "-15.0 Z1, Z5, Z7, Z9\n", + "15.0 Z1, Z5, Z7, Z11\n", + "-5.0 Z1, Z5, Z6, Z8\n", + "5.0 Z1, Z5, Z6, Z10\n", + "-5.0 Z1, Z5, Z6, Z7, Z8, Z9\n", + "5.0 Z1, Z5, Z6, Z7, Z10, Z11\n", + "-15.0 Z1, Z5, Z9, Z11\n", + "-5.0 Z1, Z5, Z8, Z10\n", + "-5.0 Z1, Z5, Z8, Z9, Z10, Z11\n", + "-5.0 Z0, Z1, Z7, Z8\n", + "-5.0 Z0, Z1, Z7, Z10\n", + "-5.0 Z0, Z7, Z8, Z9\n", + "-5.0 Z0, Z7, Z10, Z11\n", + "5.0 Z1, Z2, Z7, Z8\n", + "-5.0 Z1, Z2, Z7, Z10\n", + "5.0 Z1, Z2, Z3, Z7, Z8, Z9\n", + "-5.0 Z1, Z2, Z3, Z7, Z10, Z11\n", + "-5.0 Z1, Z4, Z7, Z8\n", + "5.0 Z1, Z4, Z7, Z10\n", + "-5.0 Z1, Z4, Z5, Z7, Z8, Z9\n", + "5.0 Z1, Z4, Z5, Z7, Z10, Z11\n", + "-5.0 Z1, Z6, Z7, Z8\n", + "-5.0 Z1, Z6, Z7, Z10\n", + "-5.0 Z1, Z6, Z8, Z9\n", + "-5.0 Z1, Z6, Z10, Z11\n", + "15.0 Z1, Z7, Z9, Z11\n", + "5.0 Z1, Z7, Z8, Z10\n", + "5.0 Z1, Z7, Z8, Z9, Z10, Z11\n", + "-5.0 Z0, Z1, Z2, Z9\n", + "15.0 Z0, Z1, Z4, Z9\n", + "-5.0 Z0, Z1, Z6, Z9\n", + "-5.0 Z0, Z1, Z9, Z10\n", + "-5.0 Z0, Z2, Z3, Z9\n", + "15.0 Z0, Z4, Z5, Z9\n", + "-5.0 Z0, Z6, Z7, Z9\n", + "-5.0 Z0, Z9, Z10, Z11\n", + "-5.0 Z1, Z2, Z4, Z9\n", + "5.0 Z1, Z2, Z6, Z9\n", + "5.0 Z1, Z2, Z9, Z10\n", + "-5.0 Z1, Z2, Z3, Z4, Z5, Z9\n", + "5.0 Z1, Z2, Z3, Z6, Z7, Z9\n", + "5.0 Z1, Z2, Z3, Z9, Z10, Z11\n", + "-5.0 Z1, Z4, Z6, Z9\n", + "-5.0 Z1, Z4, Z9, Z10\n", + "-5.0 Z1, Z4, Z5, Z6, Z7, Z9\n", + "-5.0 Z1, Z4, Z5, Z9, Z10, Z11\n", + "5.0 Z1, Z6, Z9, Z10\n", + "5.0 Z1, Z6, Z7, Z9, Z10, Z11\n", + "-5.0 Z1, Z8, Z9, Z10\n", + "-5.0 Z1, Z8, Z10, Z11\n", + "-5.0 Z0, Z1, Z2, Z11\n", + "-5.0 Z0, Z1, Z4, Z11\n", + "-5.0 Z0, Z1, Z6, Z11\n", + "-5.0 Z0, Z1, Z8, Z11\n", + "-5.0 Z0, Z2, Z3, Z11\n", + "-5.0 Z0, Z4, Z5, Z11\n", + "-5.0 Z0, Z6, Z7, Z11\n", + "-5.0 Z0, Z8, Z9, Z11\n", + "5.0 Z1, Z2, Z4, Z11\n", + "-5.0 Z1, Z2, Z6, Z11\n", + "5.0 Z1, Z2, Z8, Z11\n", + "5.0 Z1, Z2, Z3, Z4, Z5, Z11\n", + "-5.0 Z1, Z2, Z3, Z6, Z7, Z11\n", + "5.0 Z1, Z2, Z3, Z8, Z9, Z11\n", + "5.0 Z1, Z4, Z6, Z11\n", + "-5.0 Z1, Z4, Z8, Z11\n", + "5.0 Z1, Z4, Z5, Z6, Z7, Z11\n", + "-5.0 Z1, Z4, Z5, Z8, Z9, Z11\n", + "5.0 Z1, Z6, Z8, Z11\n", + "5.0 Z1, Z6, Z7, Z8, Z9, Z11\n", + "-5.0 Z0, Z2, Z5, Z9\n", + "5.0 Z0, Z2, Z5, Z11\n", + "-15.0 Z0, Z2, Z4, Z8\n", + "15.0 Z0, Z2, Z4, Z10\n", + "-5.0 Z0, Z2, Z4, Z5, Z8, Z9\n", + "5.0 Z0, Z2, Z4, Z5, Z10, Z11\n", + "5.0 Z0, Z2, Z7, Z9\n", + "-5.0 Z0, Z2, Z7, Z11\n", + "15.0 Z0, Z2, Z6, Z8\n", + "-15.0 Z0, Z2, Z6, Z10\n", + "5.0 Z0, Z2, Z6, Z7, Z8, Z9\n", + "-5.0 Z0, Z2, Z6, Z7, Z10, Z11\n", + "5.0 Z0, Z2, Z9, Z11\n", + "15.0 Z0, Z2, Z8, Z10\n", + "5.0 Z0, Z2, Z8, Z9, Z10, Z11\n", + "-5.0 Z0, Z3, Z4, Z9\n", + "5.0 Z0, Z3, Z4, Z11\n", + "-5.0 Z0, Z2, Z3, Z4, Z8, Z9\n", + "5.0 Z0, Z2, Z3, Z4, Z10, Z11\n", + "-5.0 Z0, Z4, Z7, Z9\n", + "5.0 Z0, Z4, Z7, Z11\n", + "-15.0 Z0, Z4, Z6, Z8\n", + "15.0 Z0, Z4, Z6, Z10\n", + "-5.0 Z0, Z4, Z6, Z7, Z8, Z9\n", + "5.0 Z0, Z4, Z6, Z7, Z10, Z11\n", + "-5.0 Z0, Z4, Z9, Z11\n", + "-15.0 Z0, Z4, Z8, Z10\n", + "-5.0 Z0, Z4, Z8, Z9, Z10, Z11\n", + "5.0 Z0, Z3, Z6, Z9\n", + "-5.0 Z0, Z3, Z6, Z11\n", + "5.0 Z0, Z2, Z3, Z6, Z8, Z9\n", + "-5.0 Z0, Z2, Z3, Z6, Z10, Z11\n", + "-5.0 Z0, Z5, Z6, Z9\n", + "5.0 Z0, Z5, Z6, Z11\n", + "-5.0 Z0, Z4, Z5, Z6, Z8, Z9\n", + "5.0 Z0, Z4, Z5, Z6, Z10, Z11\n", + "5.0 Z0, Z6, Z9, Z11\n", + "15.0 Z0, Z6, Z8, Z10\n", + "5.0 Z0, Z6, Z8, Z9, Z10, Z11\n", + "-5.0 Z0, Z3, Z5, Z8\n", + "5.0 Z0, Z3, Z7, Z8\n", + "5.0 Z0, Z3, Z8, Z11\n", + "-5.0 Z0, Z2, Z3, Z4, Z5, Z8\n", + "5.0 Z0, Z2, Z3, Z6, Z7, Z8\n", + "5.0 Z0, Z2, Z3, Z8, Z10, Z11\n", + "-5.0 Z0, Z5, Z7, Z8\n", + "-5.0 Z0, Z5, Z8, Z11\n", + "-5.0 Z0, Z4, Z5, Z6, Z7, Z8\n", + "-5.0 Z0, Z4, Z5, Z8, Z10, Z11\n", + "5.0 Z0, Z7, Z8, Z11\n", + "5.0 Z0, Z6, Z7, Z8, Z10, Z11\n", + "5.0 Z0, Z3, Z5, Z10\n", + "-5.0 Z0, Z3, Z7, Z10\n", + "5.0 Z0, Z3, Z9, Z10\n", + "5.0 Z0, Z2, Z3, Z4, Z5, Z10\n", + "-5.0 Z0, Z2, Z3, Z6, Z7, Z10\n", + "5.0 Z0, Z2, Z3, Z8, Z9, Z10\n", + "5.0 Z0, Z5, Z7, Z10\n", + "-5.0 Z0, Z5, Z9, Z10\n", + "5.0 Z0, Z4, Z5, Z6, Z7, Z10\n", + "-5.0 Z0, Z4, Z5, Z8, Z9, Z10\n", + "5.0 Z0, Z7, Z9, Z10\n", + "5.0 Z0, Z6, Z7, Z8, Z9, Z10\n", + "-5.0 Z0, Z1, Z2, Z3, Z5, Z9\n", + "5.0 Z0, Z1, Z2, Z3, Z5, Z11\n", + "-5.0 Z0, Z1, Z2, Z3, Z4, Z8\n", + "5.0 Z0, Z1, Z2, Z3, Z4, Z10\n", + "-15.0 Z0, Z1, Z2, Z3, Z4, Z5, Z8, Z9\n", + "15.0 Z0, Z1, Z2, Z3, Z4, Z5, Z10, Z11\n", + "5.0 Z0, Z1, Z2, Z3, Z7, Z9\n", + "-5.0 Z0, Z1, Z2, Z3, Z7, Z11\n", + "5.0 Z0, Z1, Z2, Z3, Z6, Z8\n", + "-5.0 Z0, Z1, Z2, Z3, Z6, Z10\n", + "15.0 Z0, Z1, Z2, Z3, Z6, Z7, Z8, Z9\n", + "-15.0 Z0, Z1, Z2, Z3, Z6, Z7, Z10, Z11\n", + "5.0 Z0, Z1, Z2, Z3, Z9, Z11\n", + "5.0 Z0, Z1, Z2, Z3, Z8, Z10\n", + "15.0 Z0, Z1, Z2, Z3, Z8, Z9, Z10, Z11\n", + "-5.0 Z0, Z1, Z3, Z4, Z5, Z9\n", + "5.0 Z0, Z1, Z3, Z4, Z5, Z11\n", + "-5.0 Z0, Z1, Z2, Z4, Z5, Z8\n", + "5.0 Z0, Z1, Z2, Z4, Z5, Z10\n", + "-5.0 Z0, Z1, Z4, Z5, Z7, Z9\n", + "5.0 Z0, Z1, Z4, Z5, Z7, Z11\n", + "-5.0 Z0, Z1, Z4, Z5, Z6, Z8\n", + "5.0 Z0, Z1, Z4, Z5, Z6, Z10\n", + "-15.0 Z0, Z1, Z4, Z5, Z6, Z7, Z8, Z9\n", + "15.0 Z0, Z1, Z4, Z5, Z6, Z7, Z10, Z11\n", + "-5.0 Z0, Z1, Z4, Z5, Z9, Z11\n", + "-5.0 Z0, Z1, Z4, Z5, Z8, Z10\n", + "-15.0 Z0, Z1, Z4, Z5, Z8, Z9, Z10, Z11\n", + "5.0 Z0, Z1, Z3, Z6, Z7, Z9\n", + "-5.0 Z0, Z1, Z3, Z6, Z7, Z11\n", + "5.0 Z0, Z1, Z2, Z6, Z7, Z8\n", + "-5.0 Z0, Z1, Z2, Z6, Z7, Z10\n", + "-5.0 Z0, Z1, Z5, Z6, Z7, Z9\n", + "5.0 Z0, Z1, Z5, Z6, Z7, Z11\n", + "-5.0 Z0, Z1, Z4, Z6, Z7, Z8\n", + "5.0 Z0, Z1, Z4, Z6, Z7, Z10\n", + "5.0 Z0, Z1, Z6, Z7, Z9, Z11\n", + "5.0 Z0, Z1, Z6, Z7, Z8, Z10\n", + "15.0 Z0, Z1, Z6, Z7, Z8, Z9, Z10, Z11\n", + "-5.0 Z0, Z1, Z3, Z5, Z8, Z9\n", + "5.0 Z0, Z1, Z3, Z7, Z8, Z9\n", + "5.0 Z0, Z1, Z3, Z8, Z9, Z11\n", + "-5.0 Z0, Z1, Z2, Z4, Z8, Z9\n", + "5.0 Z0, Z1, Z2, Z6, Z8, Z9\n", + "5.0 Z0, Z1, Z2, Z8, Z9, Z10\n", + "-5.0 Z0, Z1, Z5, Z7, Z8, Z9\n", + "-5.0 Z0, Z1, Z5, Z8, Z9, Z11\n", + "-5.0 Z0, Z1, Z4, Z6, Z8, Z9\n", + "-5.0 Z0, Z1, Z4, Z8, Z9, Z10\n", + "5.0 Z0, Z1, Z7, Z8, Z9, Z11\n", + "5.0 Z0, Z1, Z6, Z8, Z9, Z10\n", + "5.0 Z0, Z1, Z3, Z5, Z10, Z11\n", + "-5.0 Z0, Z1, Z3, Z7, Z10, Z11\n", + "5.0 Z0, Z1, Z3, Z9, Z10, Z11\n", + "5.0 Z0, Z1, Z2, Z4, Z10, Z11\n", + "-5.0 Z0, Z1, Z2, Z6, Z10, Z11\n", + "5.0 Z0, Z1, Z2, Z8, Z10, Z11\n", + "5.0 Z0, Z1, Z5, Z7, Z10, Z11\n", + "-5.0 Z0, Z1, Z5, Z9, Z10, Z11\n", + "5.0 Z0, Z1, Z4, Z6, Z10, Z11\n", + "-5.0 Z0, Z1, Z4, Z8, Z10, Z11\n", + "5.0 Z0, Z1, Z7, Z9, Z10, Z11\n", + "5.0 Z0, Z1, Z6, Z8, Z10, Z11\n", + "-5.0 Z2, Z3, Z5, Z10\n", + "-5.0 Z2, Z5, Z10, Z11\n", + "-5.0 Z3, Z4, Z5, Z10\n", + "-5.0 Z3, Z4, Z10, Z11\n", + "-15.0 Z3, Z5, Z7, Z11\n", + "-5.0 Z3, Z5, Z6, Z10\n", + "-5.0 Z3, Z5, Z6, Z7, Z10, Z11\n", + "15.0 Z3, Z5, Z9, Z11\n", + "5.0 Z3, Z5, Z8, Z10\n", + "5.0 Z3, Z5, Z8, Z9, Z10, Z11\n", + "15.0 Z2, Z3, Z7, Z10\n", + "15.0 Z2, Z7, Z10, Z11\n", + "-5.0 Z3, Z4, Z7, Z10\n", + "-5.0 Z3, Z4, Z5, Z7, Z10, Z11\n", + "15.0 Z3, Z6, Z7, Z10\n", + "15.0 Z3, Z6, Z10, Z11\n", + "-15.0 Z3, Z7, Z9, Z11\n", + "-5.0 Z3, Z7, Z8, Z10\n", + "-5.0 Z3, Z7, Z8, Z9, Z10, Z11\n", + "-5.0 Z2, Z3, Z9, Z10\n", + "-5.0 Z2, Z9, Z10, Z11\n", + "5.0 Z3, Z4, Z9, Z10\n", + "5.0 Z3, Z4, Z5, Z9, Z10, Z11\n", + "-5.0 Z3, Z6, Z9, Z10\n", + "-5.0 Z3, Z6, Z7, Z9, Z10, Z11\n", + "-5.0 Z3, Z8, Z9, Z10\n", + "-5.0 Z3, Z8, Z10, Z11\n", + "-5.0 Z2, Z3, Z4, Z11\n", + "15.0 Z2, Z3, Z6, Z11\n", + "-5.0 Z2, Z3, Z8, Z11\n", + "-5.0 Z2, Z4, Z5, Z11\n", + "15.0 Z2, Z6, Z7, Z11\n", + "-5.0 Z2, Z8, Z9, Z11\n", + "-5.0 Z3, Z4, Z6, Z11\n", + "5.0 Z3, Z4, Z8, Z11\n", + "-5.0 Z3, Z4, Z5, Z6, Z7, Z11\n", + "5.0 Z3, Z4, Z5, Z8, Z9, Z11\n", + "-5.0 Z3, Z6, Z8, Z11\n", + "-5.0 Z3, Z6, Z7, Z8, Z9, Z11\n", + "-5.0 Z2, Z4, Z7, Z11\n", + "-15.0 Z2, Z4, Z6, Z10\n", + "-5.0 Z2, Z4, Z6, Z7, Z10, Z11\n", + "5.0 Z2, Z4, Z9, Z11\n", + "15.0 Z2, Z4, Z8, Z10\n", + "5.0 Z2, Z4, Z8, Z9, Z10, Z11\n", + "-5.0 Z2, Z5, Z6, Z11\n", + "-5.0 Z2, Z4, Z5, Z6, Z10, Z11\n", + "-5.0 Z2, Z6, Z9, Z11\n", + "-15.0 Z2, Z6, Z8, Z10\n", + "-5.0 Z2, Z6, Z8, Z9, Z10, Z11\n", + "5.0 Z2, Z5, Z8, Z11\n", + "5.0 Z2, Z4, Z5, Z8, Z10, Z11\n", + "-5.0 Z2, Z7, Z8, Z11\n", + "-5.0 Z2, Z6, Z7, Z8, Z10, Z11\n", + "-5.0 Z2, Z5, Z7, Z10\n", + "5.0 Z2, Z5, Z9, Z10\n", + "-5.0 Z2, Z4, Z5, Z6, Z7, Z10\n", + "5.0 Z2, Z4, Z5, Z8, Z9, Z10\n", + "-5.0 Z2, Z7, Z9, Z10\n", + "-5.0 Z2, Z6, Z7, Z8, Z9, Z10\n", + "-5.0 Z2, Z3, Z4, Z5, Z7, Z11\n", + "-5.0 Z2, Z3, Z4, Z5, Z6, Z10\n", + "-15.0 Z2, Z3, Z4, Z5, Z6, Z7, Z10, Z11\n", + "5.0 Z2, Z3, Z4, Z5, Z9, Z11\n", + "5.0 Z2, Z3, Z4, Z5, Z8, Z10\n", + "15.0 Z2, Z3, Z4, Z5, Z8, Z9, Z10, Z11\n", + "-5.0 Z2, Z3, Z5, Z6, Z7, Z11\n", + "-5.0 Z2, Z3, Z4, Z6, Z7, Z10\n", + "-5.0 Z2, Z3, Z6, Z7, Z9, Z11\n", + "-5.0 Z2, Z3, Z6, Z7, Z8, Z10\n", + "-15.0 Z2, Z3, Z6, Z7, Z8, Z9, Z10, Z11\n", + "5.0 Z2, Z3, Z5, Z8, Z9, Z11\n", + "5.0 Z2, Z3, Z4, Z8, Z9, Z10\n", + "-5.0 Z2, Z3, Z7, Z8, Z9, Z11\n", + "-5.0 Z2, Z3, Z6, Z8, Z9, Z10\n", + "-5.0 Z2, Z3, Z5, Z7, Z10, Z11\n", + "5.0 Z2, Z3, Z5, Z9, Z10, Z11\n", + "-5.0 Z2, Z3, Z4, Z6, Z10, Z11\n", + "5.0 Z2, Z3, Z4, Z8, Z10, Z11\n", + "-5.0 Z2, Z3, Z7, Z9, Z10, Z11\n", + "-5.0 Z2, Z3, Z6, Z8, Z10, Z11\n", + "-7.5 Z4, Z5, Z7, Z10\n", + "-7.5 Z4, Z7, Z10, Z11\n", + "-7.5 Z5, Z6, Z7, Z10\n", + "-7.5 Z5, Z6, Z10, Z11\n", + "22.5 Z5, Z7, Z9, Z11\n", + "7.5 Z5, Z7, Z8, Z10\n", + "7.5 Z5, Z7, Z8, Z9, Z10, Z11\n", + "-7.5 Z4, Z5, Z9, Z10\n", + "-7.5 Z4, Z9, Z10, Z11\n", + "7.5 Z5, Z6, Z9, Z10\n", + "7.5 Z5, Z6, Z7, Z9, Z10, Z11\n", + "-7.5 Z5, Z8, Z9, Z10\n", + "-7.5 Z5, Z8, Z10, Z11\n", + "-7.5 Z4, Z5, Z6, Z11\n", + "-7.5 Z4, Z5, Z8, Z11\n", + "-7.5 Z4, Z6, Z7, Z11\n", + "-7.5 Z4, Z8, Z9, Z11\n", + "7.5 Z5, Z6, Z8, Z11\n", + "7.5 Z5, Z6, Z7, Z8, Z9, Z11\n", + "7.5 Z4, Z6, Z9, Z11\n", + "22.5 Z4, Z6, Z8, Z10\n", + "7.5 Z4, Z6, Z8, Z9, Z10, Z11\n", + "7.5 Z4, Z7, Z8, Z11\n", + "7.5 Z4, Z6, Z7, Z8, Z10, Z11\n", + "7.5 Z4, Z7, Z9, Z10\n", + "7.5 Z4, Z6, Z7, Z8, Z9, Z10\n", + "7.5 Z4, Z5, Z6, Z7, Z9, Z11\n", + "7.5 Z4, Z5, Z6, Z7, Z8, Z10\n", + "22.5 Z4, Z5, Z6, Z7, Z8, Z9, Z10, Z11\n", + "7.5 Z4, Z5, Z7, Z8, Z9, Z11\n", + "7.5 Z4, Z5, Z6, Z8, Z9, Z10\n", + "7.5 Z4, Z5, Z7, Z9, Z10, Z11\n", + "7.5 Z4, Z5, Z6, Z8, Z10, Z11\n", + "-7.5 Z6, Z7, Z9, Z10\n", + "-7.5 Z6, Z9, Z10, Z11\n", + "-7.5 Z7, Z8, Z9, Z10\n", + "-7.5 Z7, Z8, Z10, Z11\n", + "-7.5 Z6, Z7, Z8, Z11\n", + "-7.5 Z6, Z8, Z9, Z11\n", + "20.0 Z1, Z11, Z12\n", + "20.0 Z0, Z10, Z12\n", + "20.0 Z0, Z1, Z10, Z11, Z12\n", + "-25.0 Z3, Z11, Z12\n", + "-25.0 Z2, Z10, Z12\n", + "-25.0 Z2, Z3, Z10, Z11, Z12\n", + "20.0 Z5, Z11, Z12\n", + "20.0 Z4, Z10, Z12\n", + "20.0 Z4, Z5, Z10, Z11, Z12\n", + "-25.0 Z7, Z11, Z12\n", + "-25.0 Z6, Z10, Z12\n", + "-25.0 Z6, Z7, Z10, Z11, Z12\n", + "20.0 Z9, Z11, Z12\n", + "20.0 Z8, Z10, Z12\n", + "20.0 Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z3, Z8, Z12\n", + "2.5 Z0, Z1, Z3, Z10, Z12\n", + "2.5 Z0, Z3, Z8, Z9, Z12\n", + "2.5 Z0, Z3, Z10, Z11, Z12\n", + "2.5 Z1, Z2, Z3, Z8, Z12\n", + "2.5 Z1, Z2, Z3, Z10, Z12\n", + "2.5 Z1, Z2, Z8, Z9, Z12\n", + "2.5 Z1, Z2, Z10, Z11, Z12\n", + "7.5 Z1, Z3, Z5, Z9, Z12\n", + "-7.5 Z1, Z3, Z5, Z11, Z12\n", + "2.5 Z1, Z3, Z4, Z8, Z12\n", + "-2.5 Z1, Z3, Z4, Z10, Z12\n", + "2.5 Z1, Z3, Z4, Z5, Z8, Z9, Z12\n", + "-2.5 Z1, Z3, Z4, Z5, Z10, Z11, Z12\n", + "-7.5 Z1, Z3, Z7, Z9, Z12\n", + "7.5 Z1, Z3, Z7, Z11, Z12\n", + "-2.5 Z1, Z3, Z6, Z8, Z12\n", + "2.5 Z1, Z3, Z6, Z10, Z12\n", + "-2.5 Z1, Z3, Z6, Z7, Z8, Z9, Z12\n", + "2.5 Z1, Z3, Z6, Z7, Z10, Z11, Z12\n", + "-7.5 Z1, Z3, Z9, Z11, Z12\n", + "-2.5 Z1, Z3, Z8, Z10, Z12\n", + "-2.5 Z1, Z3, Z8, Z9, Z10, Z11, Z12\n", + "-7.5 Z0, Z1, Z5, Z8, Z12\n", + "2.5 Z0, Z1, Z5, Z10, Z12\n", + "-7.5 Z0, Z5, Z8, Z9, Z12\n", + "2.5 Z0, Z5, Z10, Z11, Z12\n", + "2.5 Z1, Z2, Z5, Z8, Z12\n", + "-2.5 Z1, Z2, Z5, Z10, Z12\n", + "2.5 Z1, Z2, Z3, Z5, Z8, Z9, Z12\n", + "-2.5 Z1, Z2, Z3, Z5, Z10, Z11, Z12\n", + "-7.5 Z1, Z4, Z5, Z8, Z12\n", + "2.5 Z1, Z4, Z5, Z10, Z12\n", + "-7.5 Z1, Z4, Z8, Z9, Z12\n", + "2.5 Z1, Z4, Z10, Z11, Z12\n", + "7.5 Z1, Z5, Z7, Z9, Z12\n", + "-7.5 Z1, Z5, Z7, Z11, Z12\n", + "2.5 Z1, Z5, Z6, Z8, Z12\n", + "-2.5 Z1, Z5, Z6, Z10, Z12\n", + "2.5 Z1, Z5, Z6, Z7, Z8, Z9, Z12\n", + "-2.5 Z1, Z5, Z6, Z7, Z10, Z11, Z12\n", + "7.5 Z1, Z5, Z9, Z11, Z12\n", + "2.5 Z1, Z5, Z8, Z10, Z12\n", + "2.5 Z1, Z5, Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z7, Z8, Z12\n", + "2.5 Z0, Z1, Z7, Z10, Z12\n", + "2.5 Z0, Z7, Z8, Z9, Z12\n", + "2.5 Z0, Z7, Z10, Z11, Z12\n", + "-2.5 Z1, Z2, Z7, Z8, Z12\n", + "2.5 Z1, Z2, Z7, Z10, Z12\n", + "-2.5 Z1, Z2, Z3, Z7, Z8, Z9, Z12\n", + "2.5 Z1, Z2, Z3, Z7, Z10, Z11, Z12\n", + "2.5 Z1, Z4, Z7, Z8, Z12\n", + "-2.5 Z1, Z4, Z7, Z10, Z12\n", + "2.5 Z1, Z4, Z5, Z7, Z8, Z9, Z12\n", + "-2.5 Z1, Z4, Z5, Z7, Z10, Z11, Z12\n", + "2.5 Z1, Z6, Z7, Z8, Z12\n", + "2.5 Z1, Z6, Z7, Z10, Z12\n", + "2.5 Z1, Z6, Z8, Z9, Z12\n", + "2.5 Z1, Z6, Z10, Z11, Z12\n", + "-7.5 Z1, Z7, Z9, Z11, Z12\n", + "-2.5 Z1, Z7, Z8, Z10, Z12\n", + "-2.5 Z1, Z7, Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z2, Z9, Z12\n", + "-7.5 Z0, Z1, Z4, Z9, Z12\n", + "2.5 Z0, Z1, Z6, Z9, Z12\n", + "2.5 Z0, Z1, Z9, Z10, Z12\n", + "2.5 Z0, Z2, Z3, Z9, Z12\n", + "-7.5 Z0, Z4, Z5, Z9, Z12\n", + "2.5 Z0, Z6, Z7, Z9, Z12\n", + "2.5 Z0, Z9, Z10, Z11, Z12\n", + "2.5 Z1, Z2, Z4, Z9, Z12\n", + "-2.5 Z1, Z2, Z6, Z9, Z12\n", + "-2.5 Z1, Z2, Z9, Z10, Z12\n", + "2.5 Z1, Z2, Z3, Z4, Z5, Z9, Z12\n", + "-2.5 Z1, Z2, Z3, Z6, Z7, Z9, Z12\n", + "-2.5 Z1, Z2, Z3, Z9, Z10, Z11, Z12\n", + "2.5 Z1, Z4, Z6, Z9, Z12\n", + "2.5 Z1, Z4, Z9, Z10, Z12\n", + "2.5 Z1, Z4, Z5, Z6, Z7, Z9, Z12\n", + "2.5 Z1, Z4, Z5, Z9, Z10, Z11, Z12\n", + "-2.5 Z1, Z6, Z9, Z10, Z12\n", + "-2.5 Z1, Z6, Z7, Z9, Z10, Z11, Z12\n", + "2.5 Z1, Z8, Z9, Z10, Z12\n", + "2.5 Z1, Z8, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z2, Z11, Z12\n", + "2.5 Z0, Z1, Z4, Z11, Z12\n", + "2.5 Z0, Z1, Z6, Z11, Z12\n", + "2.5 Z0, Z1, Z8, Z11, Z12\n", + "2.5 Z0, Z2, Z3, Z11, Z12\n", + "2.5 Z0, Z4, Z5, Z11, Z12\n", + "2.5 Z0, Z6, Z7, Z11, Z12\n", + "2.5 Z0, Z8, Z9, Z11, Z12\n", + "-2.5 Z1, Z2, Z4, Z11, Z12\n", + "2.5 Z1, Z2, Z6, Z11, Z12\n", + "-2.5 Z1, Z2, Z8, Z11, Z12\n", + "-2.5 Z1, Z2, Z3, Z4, Z5, Z11, Z12\n", + "2.5 Z1, Z2, Z3, Z6, Z7, Z11, Z12\n", + "-2.5 Z1, Z2, Z3, Z8, Z9, Z11, Z12\n", + "-2.5 Z1, Z4, Z6, Z11, Z12\n", + "2.5 Z1, Z4, Z8, Z11, Z12\n", + "-2.5 Z1, Z4, Z5, Z6, Z7, Z11, Z12\n", + "2.5 Z1, Z4, Z5, Z8, Z9, Z11, Z12\n", + "-2.5 Z1, Z6, Z8, Z11, Z12\n", + "-2.5 Z1, Z6, Z7, Z8, Z9, Z11, Z12\n", + "2.5 Z0, Z2, Z5, Z9, Z12\n", + "-2.5 Z0, Z2, Z5, Z11, Z12\n", + "7.5 Z0, Z2, Z4, Z8, Z12\n", + "-7.5 Z0, Z2, Z4, Z10, Z12\n", + "2.5 Z0, Z2, Z4, Z5, Z8, Z9, Z12\n", + "-2.5 Z0, Z2, Z4, Z5, Z10, Z11, Z12\n", + "-2.5 Z0, Z2, Z7, Z9, Z12\n", + "2.5 Z0, Z2, Z7, Z11, Z12\n", + "-7.5 Z0, Z2, Z6, Z8, Z12\n", + "7.5 Z0, Z2, Z6, Z10, Z12\n", + "-2.5 Z0, Z2, Z6, Z7, Z8, Z9, Z12\n", + "2.5 Z0, Z2, Z6, Z7, Z10, Z11, Z12\n", + "-2.5 Z0, Z2, Z9, Z11, Z12\n", + "-7.5 Z0, Z2, Z8, Z10, Z12\n", + "-2.5 Z0, Z2, Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z0, Z3, Z4, Z9, Z12\n", + "-2.5 Z0, Z3, Z4, Z11, Z12\n", + "2.5 Z0, Z2, Z3, Z4, Z8, Z9, Z12\n", + "-2.5 Z0, Z2, Z3, Z4, Z10, Z11, Z12\n", + "2.5 Z0, Z4, Z7, Z9, Z12\n", + "-2.5 Z0, Z4, Z7, Z11, Z12\n", + "7.5 Z0, Z4, Z6, Z8, Z12\n", + "-7.5 Z0, Z4, Z6, Z10, Z12\n", + "2.5 Z0, Z4, Z6, Z7, Z8, Z9, Z12\n", + "-2.5 Z0, Z4, Z6, Z7, Z10, Z11, Z12\n", + "2.5 Z0, Z4, Z9, Z11, Z12\n", + "7.5 Z0, Z4, Z8, Z10, Z12\n", + "2.5 Z0, Z4, Z8, Z9, Z10, Z11, Z12\n", + "-2.5 Z0, Z3, Z6, Z9, Z12\n", + "2.5 Z0, Z3, Z6, Z11, Z12\n", + "-2.5 Z0, Z2, Z3, Z6, Z8, Z9, Z12\n", + "2.5 Z0, Z2, Z3, Z6, Z10, Z11, Z12\n", + "2.5 Z0, Z5, Z6, Z9, Z12\n", + "-2.5 Z0, Z5, Z6, Z11, Z12\n", + "2.5 Z0, Z4, Z5, Z6, Z8, Z9, Z12\n", + "-2.5 Z0, Z4, Z5, Z6, Z10, Z11, Z12\n", + "-2.5 Z0, Z6, Z9, Z11, Z12\n", + "-7.5 Z0, Z6, Z8, Z10, Z12\n", + "-2.5 Z0, Z6, Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z0, Z3, Z5, Z8, Z12\n", + "-2.5 Z0, Z3, Z7, Z8, Z12\n", + "-2.5 Z0, Z3, Z8, Z11, Z12\n", + "2.5 Z0, Z2, Z3, Z4, Z5, Z8, Z12\n", + "-2.5 Z0, Z2, Z3, Z6, Z7, Z8, Z12\n", + "-2.5 Z0, Z2, Z3, Z8, Z10, Z11, Z12\n", + "2.5 Z0, Z5, Z7, Z8, Z12\n", + "2.5 Z0, Z5, Z8, Z11, Z12\n", + "2.5 Z0, Z4, Z5, Z6, Z7, Z8, Z12\n", + "2.5 Z0, Z4, Z5, Z8, Z10, Z11, Z12\n", + "-2.5 Z0, Z7, Z8, Z11, Z12\n", + "-2.5 Z0, Z6, Z7, Z8, Z10, Z11, Z12\n", + "-2.5 Z0, Z3, Z5, Z10, Z12\n", + "2.5 Z0, Z3, Z7, Z10, Z12\n", + "-2.5 Z0, Z3, Z9, Z10, Z12\n", + "-2.5 Z0, Z2, Z3, Z4, Z5, Z10, Z12\n", + "2.5 Z0, Z2, Z3, Z6, Z7, Z10, Z12\n", + "-2.5 Z0, Z2, Z3, Z8, Z9, Z10, Z12\n", + "-2.5 Z0, Z5, Z7, Z10, Z12\n", + "2.5 Z0, Z5, Z9, Z10, Z12\n", + "-2.5 Z0, Z4, Z5, Z6, Z7, Z10, Z12\n", + "2.5 Z0, Z4, Z5, Z8, Z9, Z10, Z12\n", + "-2.5 Z0, Z7, Z9, Z10, Z12\n", + "-2.5 Z0, Z6, Z7, Z8, Z9, Z10, Z12\n", + "2.5 Z0, Z1, Z2, Z3, Z5, Z9, Z12\n", + "-2.5 Z0, Z1, Z2, Z3, Z5, Z11, Z12\n", + "2.5 Z0, Z1, Z2, Z3, Z4, Z8, Z12\n", + "-2.5 Z0, Z1, Z2, Z3, Z4, Z10, Z12\n", + "7.5 Z0, Z1, Z2, Z3, Z4, Z5, Z8, Z9, Z12\n", + "-7.5 Z0, Z1, Z2, Z3, Z4, Z5, Z10, Z11, Z12\n", + "-2.5 Z0, Z1, Z2, Z3, Z7, Z9, Z12\n", + "2.5 Z0, Z1, Z2, Z3, Z7, Z11, Z12\n", + "-2.5 Z0, Z1, Z2, Z3, Z6, Z8, Z12\n", + "2.5 Z0, Z1, Z2, Z3, Z6, Z10, Z12\n", + "-7.5 Z0, Z1, Z2, Z3, Z6, Z7, Z8, Z9, Z12\n", + "7.5 Z0, Z1, Z2, Z3, Z6, Z7, Z10, Z11, Z12\n", + "-2.5 Z0, Z1, Z2, Z3, Z9, Z11, Z12\n", + "-2.5 Z0, Z1, Z2, Z3, Z8, Z10, Z12\n", + "-7.5 Z0, Z1, Z2, Z3, Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z3, Z4, Z5, Z9, Z12\n", + "-2.5 Z0, Z1, Z3, Z4, Z5, Z11, Z12\n", + "2.5 Z0, Z1, Z2, Z4, Z5, Z8, Z12\n", + "-2.5 Z0, Z1, Z2, Z4, Z5, Z10, Z12\n", + "2.5 Z0, Z1, Z4, Z5, Z7, Z9, Z12\n", + "-2.5 Z0, Z1, Z4, Z5, Z7, Z11, Z12\n", + "2.5 Z0, Z1, Z4, Z5, Z6, Z8, Z12\n", + "-2.5 Z0, Z1, Z4, Z5, Z6, Z10, Z12\n", + "7.5 Z0, Z1, Z4, Z5, Z6, Z7, Z8, Z9, Z12\n", + "-7.5 Z0, Z1, Z4, Z5, Z6, Z7, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z4, Z5, Z9, Z11, Z12\n", + "2.5 Z0, Z1, Z4, Z5, Z8, Z10, Z12\n", + "7.5 Z0, Z1, Z4, Z5, Z8, Z9, Z10, Z11, Z12\n", + "-2.5 Z0, Z1, Z3, Z6, Z7, Z9, Z12\n", + "2.5 Z0, Z1, Z3, Z6, Z7, Z11, Z12\n", + "-2.5 Z0, Z1, Z2, Z6, Z7, Z8, Z12\n", + "2.5 Z0, Z1, Z2, Z6, Z7, Z10, Z12\n", + "2.5 Z0, Z1, Z5, Z6, Z7, Z9, Z12\n", + "-2.5 Z0, Z1, Z5, Z6, Z7, Z11, Z12\n", + "2.5 Z0, Z1, Z4, Z6, Z7, Z8, Z12\n", + "-2.5 Z0, Z1, Z4, Z6, Z7, Z10, Z12\n", + "-2.5 Z0, Z1, Z6, Z7, Z9, Z11, Z12\n", + "-2.5 Z0, Z1, Z6, Z7, Z8, Z10, Z12\n", + "-7.5 Z0, Z1, Z6, Z7, Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z3, Z5, Z8, Z9, Z12\n", + "-2.5 Z0, Z1, Z3, Z7, Z8, Z9, Z12\n", + "-2.5 Z0, Z1, Z3, Z8, Z9, Z11, Z12\n", + "2.5 Z0, Z1, Z2, Z4, Z8, Z9, Z12\n", + "-2.5 Z0, Z1, Z2, Z6, Z8, Z9, Z12\n", + "-2.5 Z0, Z1, Z2, Z8, Z9, Z10, Z12\n", + "2.5 Z0, Z1, Z5, Z7, Z8, Z9, Z12\n", + "2.5 Z0, Z1, Z5, Z8, Z9, Z11, Z12\n", + "2.5 Z0, Z1, Z4, Z6, Z8, Z9, Z12\n", + "2.5 Z0, Z1, Z4, Z8, Z9, Z10, Z12\n", + "-2.5 Z0, Z1, Z7, Z8, Z9, Z11, Z12\n", + "-2.5 Z0, Z1, Z6, Z8, Z9, Z10, Z12\n", + "-2.5 Z0, Z1, Z3, Z5, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z3, Z7, Z10, Z11, Z12\n", + "-2.5 Z0, Z1, Z3, Z9, Z10, Z11, Z12\n", + "-2.5 Z0, Z1, Z2, Z4, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z2, Z6, Z10, Z11, Z12\n", + "-2.5 Z0, Z1, Z2, Z8, Z10, Z11, Z12\n", + "-2.5 Z0, Z1, Z5, Z7, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z5, Z9, Z10, Z11, Z12\n", + "-2.5 Z0, Z1, Z4, Z6, Z10, Z11, Z12\n", + "2.5 Z0, Z1, Z4, Z8, Z10, Z11, Z12\n", + "-2.5 Z0, Z1, Z7, Z9, Z10, Z11, Z12\n", + "-2.5 Z0, Z1, Z6, Z8, Z10, Z11, Z12\n", + "2.5 Z2, Z3, Z5, Z10, Z12\n", + "2.5 Z2, Z5, Z10, Z11, Z12\n", + "2.5 Z3, Z4, Z5, Z10, Z12\n", + "2.5 Z3, Z4, Z10, Z11, Z12\n", + "7.5 Z3, Z5, Z7, Z11, Z12\n", + "2.5 Z3, Z5, Z6, Z10, Z12\n", + "2.5 Z3, Z5, Z6, Z7, Z10, Z11, Z12\n", + "-7.5 Z3, Z5, Z9, Z11, Z12\n", + "-2.5 Z3, Z5, Z8, Z10, Z12\n", + "-2.5 Z3, Z5, Z8, Z9, Z10, Z11, Z12\n", + "-7.5 Z2, Z3, Z7, Z10, Z12\n", + "-7.5 Z2, Z7, Z10, Z11, Z12\n", + "2.5 Z3, Z4, Z7, Z10, Z12\n", + "2.5 Z3, Z4, Z5, Z7, Z10, Z11, Z12\n", + "-7.5 Z3, Z6, Z7, Z10, Z12\n", + "-7.5 Z3, Z6, Z10, Z11, Z12\n", + "7.5 Z3, Z7, Z9, Z11, Z12\n", + "2.5 Z3, Z7, Z8, Z10, Z12\n", + "2.5 Z3, Z7, Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z2, Z3, Z9, Z10, Z12\n", + "2.5 Z2, Z9, Z10, Z11, Z12\n", + "-2.5 Z3, Z4, Z9, Z10, Z12\n", + "-2.5 Z3, Z4, Z5, Z9, Z10, Z11, Z12\n", + "2.5 Z3, Z6, Z9, Z10, Z12\n", + "2.5 Z3, Z6, Z7, Z9, Z10, Z11, Z12\n", + "2.5 Z3, Z8, Z9, Z10, Z12\n", + "2.5 Z3, Z8, Z10, Z11, Z12\n", + "2.5 Z2, Z3, Z4, Z11, Z12\n", + "-7.5 Z2, Z3, Z6, Z11, Z12\n", + "2.5 Z2, Z3, Z8, Z11, Z12\n", + "2.5 Z2, Z4, Z5, Z11, Z12\n", + "-7.5 Z2, Z6, Z7, Z11, Z12\n", + "2.5 Z2, Z8, Z9, Z11, Z12\n", + "2.5 Z3, Z4, Z6, Z11, Z12\n", + "-2.5 Z3, Z4, Z8, Z11, Z12\n", + "2.5 Z3, Z4, Z5, Z6, Z7, Z11, Z12\n", + "-2.5 Z3, Z4, Z5, Z8, Z9, Z11, Z12\n", + "2.5 Z3, Z6, Z8, Z11, Z12\n", + "2.5 Z3, Z6, Z7, Z8, Z9, Z11, Z12\n", + "2.5 Z2, Z4, Z7, Z11, Z12\n", + "7.5 Z2, Z4, Z6, Z10, Z12\n", + "2.5 Z2, Z4, Z6, Z7, Z10, Z11, Z12\n", + "-2.5 Z2, Z4, Z9, Z11, Z12\n", + "-7.5 Z2, Z4, Z8, Z10, Z12\n", + "-2.5 Z2, Z4, Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z2, Z5, Z6, Z11, Z12\n", + "2.5 Z2, Z4, Z5, Z6, Z10, Z11, Z12\n", + "2.5 Z2, Z6, Z9, Z11, Z12\n", + "7.5 Z2, Z6, Z8, Z10, Z12\n", + "2.5 Z2, Z6, Z8, Z9, Z10, Z11, Z12\n", + "-2.5 Z2, Z5, Z8, Z11, Z12\n", + "-2.5 Z2, Z4, Z5, Z8, Z10, Z11, Z12\n", + "2.5 Z2, Z7, Z8, Z11, Z12\n", + "2.5 Z2, Z6, Z7, Z8, Z10, Z11, Z12\n", + "2.5 Z2, Z5, Z7, Z10, Z12\n", + "-2.5 Z2, Z5, Z9, Z10, Z12\n", + "2.5 Z2, Z4, Z5, Z6, Z7, Z10, Z12\n", + "-2.5 Z2, Z4, Z5, Z8, Z9, Z10, Z12\n", + "2.5 Z2, Z7, Z9, Z10, Z12\n", + "2.5 Z2, Z6, Z7, Z8, Z9, Z10, Z12\n", + "2.5 Z2, Z3, Z4, Z5, Z7, Z11, Z12\n", + "2.5 Z2, Z3, Z4, Z5, Z6, Z10, Z12\n", + "7.5 Z2, Z3, Z4, Z5, Z6, Z7, Z10, Z11, Z12\n", + "-2.5 Z2, Z3, Z4, Z5, Z9, Z11, Z12\n", + "-2.5 Z2, Z3, Z4, Z5, Z8, Z10, Z12\n", + "-7.5 Z2, Z3, Z4, Z5, Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z2, Z3, Z5, Z6, Z7, Z11, Z12\n", + "2.5 Z2, Z3, Z4, Z6, Z7, Z10, Z12\n", + "2.5 Z2, Z3, Z6, Z7, Z9, Z11, Z12\n", + "2.5 Z2, Z3, Z6, Z7, Z8, Z10, Z12\n", + "7.5 Z2, Z3, Z6, Z7, Z8, Z9, Z10, Z11, Z12\n", + "-2.5 Z2, Z3, Z5, Z8, Z9, Z11, Z12\n", + "-2.5 Z2, Z3, Z4, Z8, Z9, Z10, Z12\n", + "2.5 Z2, Z3, Z7, Z8, Z9, Z11, Z12\n", + "2.5 Z2, Z3, Z6, Z8, Z9, Z10, Z12\n", + "2.5 Z2, Z3, Z5, Z7, Z10, Z11, Z12\n", + "-2.5 Z2, Z3, Z5, Z9, Z10, Z11, Z12\n", + "2.5 Z2, Z3, Z4, Z6, Z10, Z11, Z12\n", + "-2.5 Z2, Z3, Z4, Z8, Z10, Z11, Z12\n", + "2.5 Z2, Z3, Z7, Z9, Z10, Z11, Z12\n", + "2.5 Z2, Z3, Z6, Z8, Z10, Z11, Z12\n", + "2.5 Z4, Z5, Z7, Z10, Z12\n", + "2.5 Z4, Z7, Z10, Z11, Z12\n", + "2.5 Z5, Z6, Z7, Z10, Z12\n", + "2.5 Z5, Z6, Z10, Z11, Z12\n", + "-7.5 Z5, Z7, Z9, Z11, Z12\n", + "-2.5 Z5, Z7, Z8, Z10, Z12\n", + "-2.5 Z5, Z7, Z8, Z9, Z10, Z11, Z12\n", + "2.5 Z4, Z5, Z9, Z10, Z12\n", + "2.5 Z4, Z9, Z10, Z11, Z12\n", + "-2.5 Z5, Z6, Z9, Z10, Z12\n", + "-2.5 Z5, Z6, Z7, Z9, Z10, Z11, Z12\n", + "2.5 Z5, Z8, Z9, Z10, Z12\n", + "2.5 Z5, Z8, Z10, Z11, Z12\n", + "2.5 Z4, Z5, Z6, Z11, Z12\n", + "2.5 Z4, Z5, Z8, Z11, Z12\n", + "2.5 Z4, Z6, Z7, Z11, Z12\n", + "2.5 Z4, Z8, Z9, Z11, Z12\n", + "-2.5 Z5, Z6, Z8, Z11, Z12\n", + "-2.5 Z5, Z6, Z7, Z8, Z9, Z11, Z12\n", + "-2.5 Z4, Z6, Z9, Z11, Z12\n", + "-7.5 Z4, Z6, Z8, Z10, Z12\n", + "-2.5 Z4, Z6, Z8, Z9, Z10, Z11, Z12\n", + "-2.5 Z4, Z7, Z8, Z11, Z12\n", + "-2.5 Z4, Z6, Z7, Z8, Z10, Z11, Z12\n", + "-2.5 Z4, Z7, Z9, Z10, Z12\n", + "-2.5 Z4, Z6, Z7, Z8, Z9, Z10, Z12\n", + "-2.5 Z4, Z5, Z6, Z7, Z9, Z11, Z12\n", + "-2.5 Z4, Z5, Z6, Z7, Z8, Z10, Z12\n", + "-7.5 Z4, Z5, Z6, Z7, Z8, Z9, Z10, Z11, Z12\n", + "-2.5 Z4, Z5, Z7, Z8, Z9, Z11, Z12\n", + "-2.5 Z4, Z5, Z6, Z8, Z9, Z10, Z12\n", + "-2.5 Z4, Z5, Z7, Z9, Z10, Z11, Z12\n", + "-2.5 Z4, Z5, Z6, Z8, Z10, Z11, Z12\n", + "2.5 Z6, Z7, Z9, Z10, Z12\n", + "2.5 Z6, Z9, Z10, Z11, Z12\n", + "2.5 Z7, Z8, Z9, Z10, Z12\n", + "2.5 Z7, Z8, Z10, Z11, Z12\n", + "2.5 Z6, Z7, Z8, Z11, Z12\n", + "2.5 Z6, Z8, Z9, Z11, Z12\n", + "-157.34050000000002 Z13\n", + "30.0 Z3, Z5, Z13\n", + "-40.0 Z3, Z7, Z13\n", + "30.0 Z3, Z9, Z13\n", + "-27.5 Z3, Z11, Z13\n", + "30.0 Z2, Z4, Z13\n", + "-40.0 Z2, Z6, Z13\n", + "30.0 Z2, Z8, Z13\n", + "-27.5 Z2, Z10, Z13\n", + "30.0 Z2, Z3, Z4, Z5, Z13\n", + "-40.0 Z2, Z3, Z6, Z7, Z13\n", + "30.0 Z2, Z3, Z8, Z9, Z13\n", + "-27.5 Z2, Z3, Z10, Z11, Z13\n", + "37.5 Z5, Z7, Z13\n", + "-52.5 Z5, Z9, Z13\n", + "30.0 Z5, Z11, Z13\n", + "37.5 Z4, Z6, Z13\n", + "-52.5 Z4, Z8, Z13\n", + "30.0 Z4, Z10, Z13\n", + "37.5 Z4, Z5, Z6, Z7, Z13\n", + "-52.5 Z4, Z5, Z8, Z9, Z13\n", + "30.0 Z4, Z5, Z10, Z11, Z13\n", + "37.5 Z7, Z9, Z13\n", + "-40.0 Z7, Z11, Z13\n", + "37.5 Z6, Z8, Z13\n", + "-40.0 Z6, Z10, Z13\n", + "37.5 Z6, Z7, Z8, Z9, Z13\n", + "-40.0 Z6, Z7, Z10, Z11, Z13\n", + "30.0 Z9, Z11, Z13\n", + "30.0 Z8, Z10, Z13\n", + "30.0 Z8, Z9, Z10, Z11, Z13\n", + "20.0 Z1, Z3, Z13\n", + "-25.0 Z1, Z5, Z13\n", + "20.0 Z1, Z7, Z13\n", + "-25.0 Z1, Z9, Z13\n", + "20.0 Z1, Z11, Z13\n", + "20.0 Z0, Z2, Z13\n", + "-25.0 Z0, Z4, Z13\n", + "20.0 Z0, Z6, Z13\n", + "-25.0 Z0, Z8, Z13\n", + "20.0 Z0, Z10, Z13\n", + "20.0 Z0, Z1, Z2, Z3, Z13\n", + "-25.0 Z0, Z1, Z4, Z5, Z13\n", + "20.0 Z0, Z1, Z6, Z7, Z13\n", + "-25.0 Z0, Z1, Z8, Z9, Z13\n", + "20.0 Z0, Z1, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z3, Z4, Z13\n", + "2.5 Z0, Z1, Z3, Z6, Z13\n", + "2.5 Z0, Z1, Z3, Z8, Z13\n", + "2.5 Z0, Z1, Z3, Z10, Z13\n", + "2.5 Z0, Z3, Z4, Z5, Z13\n", + "2.5 Z0, Z3, Z6, Z7, Z13\n", + "2.5 Z0, Z3, Z8, Z9, Z13\n", + "2.5 Z0, Z3, Z10, Z11, Z13\n", + "2.5 Z1, Z2, Z3, Z4, Z13\n", + "2.5 Z1, Z2, Z3, Z6, Z13\n", + "2.5 Z1, Z2, Z3, Z8, Z13\n", + "2.5 Z1, Z2, Z3, Z10, Z13\n", + "2.5 Z1, Z2, Z4, Z5, Z13\n", + "2.5 Z1, Z2, Z6, Z7, Z13\n", + "2.5 Z1, Z2, Z8, Z9, Z13\n", + "2.5 Z1, Z2, Z10, Z11, Z13\n", + "-7.5 Z1, Z3, Z5, Z7, Z13\n", + "7.5 Z1, Z3, Z5, Z9, Z13\n", + "-7.5 Z1, Z3, Z5, Z11, Z13\n", + "-2.5 Z1, Z3, Z4, Z6, Z13\n", + "2.5 Z1, Z3, Z4, Z8, Z13\n", + "-2.5 Z1, Z3, Z4, Z10, Z13\n", + "-2.5 Z1, Z3, Z4, Z5, Z6, Z7, Z13\n", + "2.5 Z1, Z3, Z4, Z5, Z8, Z9, Z13\n", + "-2.5 Z1, Z3, Z4, Z5, Z10, Z11, Z13\n", + "-7.5 Z1, Z3, Z7, Z9, Z13\n", + "7.5 Z1, Z3, Z7, Z11, Z13\n", + "-2.5 Z1, Z3, Z6, Z8, Z13\n", + "2.5 Z1, Z3, Z6, Z10, Z13\n", + "-2.5 Z1, Z3, Z6, Z7, Z8, Z9, Z13\n", + "2.5 Z1, Z3, Z6, Z7, Z10, Z11, Z13\n", + "-7.5 Z1, Z3, Z9, Z11, Z13\n", + "-2.5 Z1, Z3, Z8, Z10, Z13\n", + "-2.5 Z1, Z3, Z8, Z9, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z2, Z5, Z13\n", + "2.5 Z0, Z1, Z5, Z6, Z13\n", + "-7.5 Z0, Z1, Z5, Z8, Z13\n", + "2.5 Z0, Z1, Z5, Z10, Z13\n", + "2.5 Z0, Z2, Z3, Z5, Z13\n", + "2.5 Z0, Z5, Z6, Z7, Z13\n", + "-7.5 Z0, Z5, Z8, Z9, Z13\n", + "2.5 Z0, Z5, Z10, Z11, Z13\n", + "-2.5 Z1, Z2, Z5, Z6, Z13\n", + "2.5 Z1, Z2, Z5, Z8, Z13\n", + "-2.5 Z1, Z2, Z5, Z10, Z13\n", + "-2.5 Z1, Z2, Z3, Z5, Z6, Z7, Z13\n", + "2.5 Z1, Z2, Z3, Z5, Z8, Z9, Z13\n", + "-2.5 Z1, Z2, Z3, Z5, Z10, Z11, Z13\n", + "2.5 Z1, Z4, Z5, Z6, Z13\n", + "-7.5 Z1, Z4, Z5, Z8, Z13\n", + "2.5 Z1, Z4, Z5, Z10, Z13\n", + "2.5 Z1, Z4, Z6, Z7, Z13\n", + "-7.5 Z1, Z4, Z8, Z9, Z13\n", + "2.5 Z1, Z4, Z10, Z11, Z13\n", + "7.5 Z1, Z5, Z7, Z9, Z13\n", + "-7.5 Z1, Z5, Z7, Z11, Z13\n", + "2.5 Z1, Z5, Z6, Z8, Z13\n", + "-2.5 Z1, Z5, Z6, Z10, Z13\n", + "2.5 Z1, Z5, Z6, Z7, Z8, Z9, Z13\n", + "-2.5 Z1, Z5, Z6, Z7, Z10, Z11, Z13\n", + "7.5 Z1, Z5, Z9, Z11, Z13\n", + "2.5 Z1, Z5, Z8, Z10, Z13\n", + "2.5 Z1, Z5, Z8, Z9, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z2, Z7, Z13\n", + "2.5 Z0, Z1, Z4, Z7, Z13\n", + "2.5 Z0, Z1, Z7, Z8, Z13\n", + "2.5 Z0, Z1, Z7, Z10, Z13\n", + "2.5 Z0, Z2, Z3, Z7, Z13\n", + "2.5 Z0, Z4, Z5, Z7, Z13\n", + "2.5 Z0, Z7, Z8, Z9, Z13\n", + "2.5 Z0, Z7, Z10, Z11, Z13\n", + "-2.5 Z1, Z2, Z4, Z7, Z13\n", + "-2.5 Z1, Z2, Z7, Z8, Z13\n", + "2.5 Z1, Z2, Z7, Z10, Z13\n", + "-2.5 Z1, Z2, Z3, Z4, Z5, Z7, Z13\n", + "-2.5 Z1, Z2, Z3, Z7, Z8, Z9, Z13\n", + "2.5 Z1, Z2, Z3, Z7, Z10, Z11, Z13\n", + "2.5 Z1, Z4, Z7, Z8, Z13\n", + "-2.5 Z1, Z4, Z7, Z10, Z13\n", + "2.5 Z1, Z4, Z5, Z7, Z8, Z9, Z13\n", + "-2.5 Z1, Z4, Z5, Z7, Z10, Z11, Z13\n", + "2.5 Z1, Z6, Z7, Z8, Z13\n", + "2.5 Z1, Z6, Z7, Z10, Z13\n", + "2.5 Z1, Z6, Z8, Z9, Z13\n", + "2.5 Z1, Z6, Z10, Z11, Z13\n", + "-7.5 Z1, Z7, Z9, Z11, Z13\n", + "-2.5 Z1, Z7, Z8, Z10, Z13\n", + "-2.5 Z1, Z7, Z8, Z9, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z2, Z9, Z13\n", + "-7.5 Z0, Z1, Z4, Z9, Z13\n", + "2.5 Z0, Z1, Z6, Z9, Z13\n", + "2.5 Z0, Z1, Z9, Z10, Z13\n", + "2.5 Z0, Z2, Z3, Z9, Z13\n", + "-7.5 Z0, Z4, Z5, Z9, Z13\n", + "2.5 Z0, Z6, Z7, Z9, Z13\n", + "2.5 Z0, Z9, Z10, Z11, Z13\n", + "2.5 Z1, Z2, Z4, Z9, Z13\n", + "-2.5 Z1, Z2, Z6, Z9, Z13\n", + "-2.5 Z1, Z2, Z9, Z10, Z13\n", + "2.5 Z1, Z2, Z3, Z4, Z5, Z9, Z13\n", + "-2.5 Z1, Z2, Z3, Z6, Z7, Z9, Z13\n", + "-2.5 Z1, Z2, Z3, Z9, Z10, Z11, Z13\n", + "2.5 Z1, Z4, Z6, Z9, Z13\n", + "2.5 Z1, Z4, Z9, Z10, Z13\n", + "2.5 Z1, Z4, Z5, Z6, Z7, Z9, Z13\n", + "2.5 Z1, Z4, Z5, Z9, Z10, Z11, Z13\n", + "-2.5 Z1, Z6, Z9, Z10, Z13\n", + "-2.5 Z1, Z6, Z7, Z9, Z10, Z11, Z13\n", + "2.5 Z1, Z8, Z9, Z10, Z13\n", + "2.5 Z1, Z8, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z2, Z11, Z13\n", + "2.5 Z0, Z1, Z4, Z11, Z13\n", + "2.5 Z0, Z1, Z6, Z11, Z13\n", + "2.5 Z0, Z1, Z8, Z11, Z13\n", + "2.5 Z0, Z2, Z3, Z11, Z13\n", + "2.5 Z0, Z4, Z5, Z11, Z13\n", + "2.5 Z0, Z6, Z7, Z11, Z13\n", + "2.5 Z0, Z8, Z9, Z11, Z13\n", + "-2.5 Z1, Z2, Z4, Z11, Z13\n", + "2.5 Z1, Z2, Z6, Z11, Z13\n", + "-2.5 Z1, Z2, Z8, Z11, Z13\n", + "-2.5 Z1, Z2, Z3, Z4, Z5, Z11, Z13\n", + "2.5 Z1, Z2, Z3, Z6, Z7, Z11, Z13\n", + "-2.5 Z1, Z2, Z3, Z8, Z9, Z11, Z13\n", + "-2.5 Z1, Z4, Z6, Z11, Z13\n", + "2.5 Z1, Z4, Z8, Z11, Z13\n", + "-2.5 Z1, Z4, Z5, Z6, Z7, Z11, Z13\n", + "2.5 Z1, Z4, Z5, Z8, Z9, Z11, Z13\n", + "-2.5 Z1, Z6, Z8, Z11, Z13\n", + "-2.5 Z1, Z6, Z7, Z8, Z9, Z11, Z13\n", + "-2.5 Z0, Z2, Z5, Z7, Z13\n", + "2.5 Z0, Z2, Z5, Z9, Z13\n", + "-2.5 Z0, Z2, Z5, Z11, Z13\n", + "-7.5 Z0, Z2, Z4, Z6, Z13\n", + "7.5 Z0, Z2, Z4, Z8, Z13\n", + "-7.5 Z0, Z2, Z4, Z10, Z13\n", + "-2.5 Z0, Z2, Z4, Z5, Z6, Z7, Z13\n", + "2.5 Z0, Z2, Z4, Z5, Z8, Z9, Z13\n", + "-2.5 Z0, Z2, Z4, Z5, Z10, Z11, Z13\n", + "-2.5 Z0, Z2, Z7, Z9, Z13\n", + "2.5 Z0, Z2, Z7, Z11, Z13\n", + "-7.5 Z0, Z2, Z6, Z8, Z13\n", + "7.5 Z0, Z2, Z6, Z10, Z13\n", + "-2.5 Z0, Z2, Z6, Z7, Z8, Z9, Z13\n", + "2.5 Z0, Z2, Z6, Z7, Z10, Z11, Z13\n", + "-2.5 Z0, Z2, Z9, Z11, Z13\n", + "-7.5 Z0, Z2, Z8, Z10, Z13\n", + "-2.5 Z0, Z2, Z8, Z9, Z10, Z11, Z13\n", + "-2.5 Z0, Z3, Z4, Z7, Z13\n", + "2.5 Z0, Z3, Z4, Z9, Z13\n", + "-2.5 Z0, Z3, Z4, Z11, Z13\n", + "-2.5 Z0, Z2, Z3, Z4, Z6, Z7, Z13\n", + "2.5 Z0, Z2, Z3, Z4, Z8, Z9, Z13\n", + "-2.5 Z0, Z2, Z3, Z4, Z10, Z11, Z13\n", + "2.5 Z0, Z4, Z7, Z9, Z13\n", + "-2.5 Z0, Z4, Z7, Z11, Z13\n", + "7.5 Z0, Z4, Z6, Z8, Z13\n", + "-7.5 Z0, Z4, Z6, Z10, Z13\n", + "2.5 Z0, Z4, Z6, Z7, Z8, Z9, Z13\n", + "-2.5 Z0, Z4, Z6, Z7, Z10, Z11, Z13\n", + "2.5 Z0, Z4, Z9, Z11, Z13\n", + "7.5 Z0, Z4, Z8, Z10, Z13\n", + "2.5 Z0, Z4, Z8, Z9, Z10, Z11, Z13\n", + "-2.5 Z0, Z3, Z5, Z6, Z13\n", + "-2.5 Z0, Z3, Z6, Z9, Z13\n", + "2.5 Z0, Z3, Z6, Z11, Z13\n", + "-2.5 Z0, Z2, Z3, Z4, Z5, Z6, Z13\n", + "-2.5 Z0, Z2, Z3, Z6, Z8, Z9, Z13\n", + "2.5 Z0, Z2, Z3, Z6, Z10, Z11, Z13\n", + "2.5 Z0, Z5, Z6, Z9, Z13\n", + "-2.5 Z0, Z5, Z6, Z11, Z13\n", + "2.5 Z0, Z4, Z5, Z6, Z8, Z9, Z13\n", + "-2.5 Z0, Z4, Z5, Z6, Z10, Z11, Z13\n", + "-2.5 Z0, Z6, Z9, Z11, Z13\n", + "-7.5 Z0, Z6, Z8, Z10, Z13\n", + "-2.5 Z0, Z6, Z8, Z9, Z10, Z11, Z13\n", + "2.5 Z0, Z3, Z5, Z8, Z13\n", + "-2.5 Z0, Z3, Z7, Z8, Z13\n", + "-2.5 Z0, Z3, Z8, Z11, Z13\n", + "2.5 Z0, Z2, Z3, Z4, Z5, Z8, Z13\n", + "-2.5 Z0, Z2, Z3, Z6, Z7, Z8, Z13\n", + "-2.5 Z0, Z2, Z3, Z8, Z10, Z11, Z13\n", + "2.5 Z0, Z5, Z7, Z8, Z13\n", + "2.5 Z0, Z5, Z8, Z11, Z13\n", + "2.5 Z0, Z4, Z5, Z6, Z7, Z8, Z13\n", + "2.5 Z0, Z4, Z5, Z8, Z10, Z11, Z13\n", + "-2.5 Z0, Z7, Z8, Z11, Z13\n", + "-2.5 Z0, Z6, Z7, Z8, Z10, Z11, Z13\n", + "-2.5 Z0, Z3, Z5, Z10, Z13\n", + "2.5 Z0, Z3, Z7, Z10, Z13\n", + "-2.5 Z0, Z3, Z9, Z10, Z13\n", + "-2.5 Z0, Z2, Z3, Z4, Z5, Z10, Z13\n", + "2.5 Z0, Z2, Z3, Z6, Z7, Z10, Z13\n", + "-2.5 Z0, Z2, Z3, Z8, Z9, Z10, Z13\n", + "-2.5 Z0, Z5, Z7, Z10, Z13\n", + "2.5 Z0, Z5, Z9, Z10, Z13\n", + "-2.5 Z0, Z4, Z5, Z6, Z7, Z10, Z13\n", + "2.5 Z0, Z4, Z5, Z8, Z9, Z10, Z13\n", + "-2.5 Z0, Z7, Z9, Z10, Z13\n", + "-2.5 Z0, Z6, Z7, Z8, Z9, Z10, Z13\n", + "-2.5 Z0, Z1, Z2, Z3, Z5, Z7, Z13\n", + "2.5 Z0, Z1, Z2, Z3, Z5, Z9, Z13\n", + "-2.5 Z0, Z1, Z2, Z3, Z5, Z11, Z13\n", + "-2.5 Z0, Z1, Z2, Z3, Z4, Z6, Z13\n", + "2.5 Z0, Z1, Z2, Z3, Z4, Z8, Z13\n", + "-2.5 Z0, Z1, Z2, Z3, Z4, Z10, Z13\n", + "-7.5 Z0, Z1, Z2, Z3, Z4, Z5, Z6, Z7, Z13\n", + "7.5 Z0, Z1, Z2, Z3, Z4, Z5, Z8, Z9, Z13\n", + "-7.5 Z0, Z1, Z2, Z3, Z4, Z5, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z2, Z3, Z7, Z9, Z13\n", + "2.5 Z0, Z1, Z2, Z3, Z7, Z11, Z13\n", + "-2.5 Z0, Z1, Z2, Z3, Z6, Z8, Z13\n", + "2.5 Z0, Z1, Z2, Z3, Z6, Z10, Z13\n", + "-7.5 Z0, Z1, Z2, Z3, Z6, Z7, Z8, Z9, Z13\n", + "7.5 Z0, Z1, Z2, Z3, Z6, Z7, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z2, Z3, Z9, Z11, Z13\n", + "-2.5 Z0, Z1, Z2, Z3, Z8, Z10, Z13\n", + "-7.5 Z0, Z1, Z2, Z3, Z8, Z9, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z3, Z4, Z5, Z7, Z13\n", + "2.5 Z0, Z1, Z3, Z4, Z5, Z9, Z13\n", + "-2.5 Z0, Z1, Z3, Z4, Z5, Z11, Z13\n", + "-2.5 Z0, Z1, Z2, Z4, Z5, Z6, Z13\n", + "2.5 Z0, Z1, Z2, Z4, Z5, Z8, Z13\n", + "-2.5 Z0, Z1, Z2, Z4, Z5, Z10, Z13\n", + "2.5 Z0, Z1, Z4, Z5, Z7, Z9, Z13\n", + "-2.5 Z0, Z1, Z4, Z5, Z7, Z11, Z13\n", + "2.5 Z0, Z1, Z4, Z5, Z6, Z8, Z13\n", + "-2.5 Z0, Z1, Z4, Z5, Z6, Z10, Z13\n", + "7.5 Z0, Z1, Z4, Z5, Z6, Z7, Z8, Z9, Z13\n", + "-7.5 Z0, Z1, Z4, Z5, Z6, Z7, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z4, Z5, Z9, Z11, Z13\n", + "2.5 Z0, Z1, Z4, Z5, Z8, Z10, Z13\n", + "7.5 Z0, Z1, Z4, Z5, Z8, Z9, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z3, Z5, Z6, Z7, Z13\n", + "-2.5 Z0, Z1, Z3, Z6, Z7, Z9, Z13\n", + "2.5 Z0, Z1, Z3, Z6, Z7, Z11, Z13\n", + "-2.5 Z0, Z1, Z2, Z4, Z6, Z7, Z13\n", + "-2.5 Z0, Z1, Z2, Z6, Z7, Z8, Z13\n", + "2.5 Z0, Z1, Z2, Z6, Z7, Z10, Z13\n", + "2.5 Z0, Z1, Z5, Z6, Z7, Z9, Z13\n", + "-2.5 Z0, Z1, Z5, Z6, Z7, Z11, Z13\n", + "2.5 Z0, Z1, Z4, Z6, Z7, Z8, Z13\n", + "-2.5 Z0, Z1, Z4, Z6, Z7, Z10, Z13\n", + "-2.5 Z0, Z1, Z6, Z7, Z9, Z11, Z13\n", + "-2.5 Z0, Z1, Z6, Z7, Z8, Z10, Z13\n", + "-7.5 Z0, Z1, Z6, Z7, Z8, Z9, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z3, Z5, Z8, Z9, Z13\n", + "-2.5 Z0, Z1, Z3, Z7, Z8, Z9, Z13\n", + "-2.5 Z0, Z1, Z3, Z8, Z9, Z11, Z13\n", + "2.5 Z0, Z1, Z2, Z4, Z8, Z9, Z13\n", + "-2.5 Z0, Z1, Z2, Z6, Z8, Z9, Z13\n", + "-2.5 Z0, Z1, Z2, Z8, Z9, Z10, Z13\n", + "2.5 Z0, Z1, Z5, Z7, Z8, Z9, Z13\n", + "2.5 Z0, Z1, Z5, Z8, Z9, Z11, Z13\n", + "2.5 Z0, Z1, Z4, Z6, Z8, Z9, Z13\n", + "2.5 Z0, Z1, Z4, Z8, Z9, Z10, Z13\n", + "-2.5 Z0, Z1, Z7, Z8, Z9, Z11, Z13\n", + "-2.5 Z0, Z1, Z6, Z8, Z9, Z10, Z13\n", + "-2.5 Z0, Z1, Z3, Z5, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z3, Z7, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z3, Z9, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z2, Z4, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z2, Z6, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z2, Z8, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z5, Z7, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z5, Z9, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z4, Z6, Z10, Z11, Z13\n", + "2.5 Z0, Z1, Z4, Z8, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z7, Z9, Z10, Z11, Z13\n", + "-2.5 Z0, Z1, Z6, Z8, Z10, Z11, Z13\n", + "5.0 Z2, Z3, Z5, Z6, Z13\n", + "5.0 Z2, Z3, Z5, Z8, Z13\n", + "2.5 Z2, Z3, Z5, Z10, Z13\n", + "5.0 Z2, Z5, Z6, Z7, Z13\n", + "5.0 Z2, Z5, Z8, Z9, Z13\n", + "2.5 Z2, Z5, Z10, Z11, Z13\n", + "5.0 Z3, Z4, Z5, Z6, Z13\n", + "5.0 Z3, Z4, Z5, Z8, Z13\n", + "2.5 Z3, Z4, Z5, Z10, Z13\n", + "5.0 Z3, Z4, Z6, Z7, Z13\n", + "5.0 Z3, Z4, Z8, Z9, Z13\n", + "2.5 Z3, Z4, Z10, Z11, Z13\n", + "-15.0 Z3, Z5, Z7, Z9, Z13\n", + "7.5 Z3, Z5, Z7, Z11, Z13\n", + "-5.0 Z3, Z5, Z6, Z8, Z13\n", + "2.5 Z3, Z5, Z6, Z10, Z13\n", + "-5.0 Z3, Z5, Z6, Z7, Z8, Z9, Z13\n", + "2.5 Z3, Z5, Z6, Z7, Z10, Z11, Z13\n", + "-7.5 Z3, Z5, Z9, Z11, Z13\n", + "-2.5 Z3, Z5, Z8, Z10, Z13\n", + "-2.5 Z3, Z5, Z8, Z9, Z10, Z11, Z13\n", + "5.0 Z2, Z3, Z4, Z7, Z13\n", + "5.0 Z2, Z3, Z7, Z8, Z13\n", + "-7.5 Z2, Z3, Z7, Z10, Z13\n", + "5.0 Z2, Z4, Z5, Z7, Z13\n", + "5.0 Z2, Z7, Z8, Z9, Z13\n", + "-7.5 Z2, Z7, Z10, Z11, Z13\n", + "-5.0 Z3, Z4, Z7, Z8, Z13\n", + "2.5 Z3, Z4, Z7, Z10, Z13\n", + "-5.0 Z3, Z4, Z5, Z7, Z8, Z9, Z13\n", + "2.5 Z3, Z4, Z5, Z7, Z10, Z11, Z13\n", + "5.0 Z3, Z6, Z7, Z8, Z13\n", + "-7.5 Z3, Z6, Z7, Z10, Z13\n", + "5.0 Z3, Z6, Z8, Z9, Z13\n", + "-7.5 Z3, Z6, Z10, Z11, Z13\n", + "7.5 Z3, Z7, Z9, Z11, Z13\n", + "2.5 Z3, Z7, Z8, Z10, Z13\n", + "2.5 Z3, Z7, Z8, Z9, Z10, Z11, Z13\n", + "5.0 Z2, Z3, Z4, Z9, Z13\n", + "5.0 Z2, Z3, Z6, Z9, Z13\n", + "2.5 Z2, Z3, Z9, Z10, Z13\n", + "5.0 Z2, Z4, Z5, Z9, Z13\n", + "5.0 Z2, Z6, Z7, Z9, Z13\n", + "2.5 Z2, Z9, Z10, Z11, Z13\n", + "-5.0 Z3, Z4, Z6, Z9, Z13\n", + "-2.5 Z3, Z4, Z9, Z10, Z13\n", + "-5.0 Z3, Z4, Z5, Z6, Z7, Z9, Z13\n", + "-2.5 Z3, Z4, Z5, Z9, Z10, Z11, Z13\n", + "2.5 Z3, Z6, Z9, Z10, Z13\n", + "2.5 Z3, Z6, Z7, Z9, Z10, Z11, Z13\n", + "2.5 Z3, Z8, Z9, Z10, Z13\n", + "2.5 Z3, Z8, Z10, Z11, Z13\n", + "2.5 Z2, Z3, Z4, Z11, Z13\n", + "-7.5 Z2, Z3, Z6, Z11, Z13\n", + "2.5 Z2, Z3, Z8, Z11, Z13\n", + "2.5 Z2, Z4, Z5, Z11, Z13\n", + "-7.5 Z2, Z6, Z7, Z11, Z13\n", + "2.5 Z2, Z8, Z9, Z11, Z13\n", + "2.5 Z3, Z4, Z6, Z11, Z13\n", + "-2.5 Z3, Z4, Z8, Z11, Z13\n", + "2.5 Z3, Z4, Z5, Z6, Z7, Z11, Z13\n", + "-2.5 Z3, Z4, Z5, Z8, Z9, Z11, Z13\n", + "2.5 Z3, Z6, Z8, Z11, Z13\n", + "2.5 Z3, Z6, Z7, Z8, Z9, Z11, Z13\n", + "-5.0 Z2, Z4, Z7, Z9, Z13\n", + "2.5 Z2, Z4, Z7, Z11, Z13\n", + "-15.0 Z2, Z4, Z6, Z8, Z13\n", + "7.5 Z2, Z4, Z6, Z10, Z13\n", + "-5.0 Z2, Z4, Z6, Z7, Z8, Z9, Z13\n", + "2.5 Z2, Z4, Z6, Z7, Z10, Z11, Z13\n", + "-2.5 Z2, Z4, Z9, Z11, Z13\n", + "-7.5 Z2, Z4, Z8, Z10, Z13\n", + "-2.5 Z2, Z4, Z8, Z9, Z10, Z11, Z13\n", + "-5.0 Z2, Z5, Z6, Z9, Z13\n", + "2.5 Z2, Z5, Z6, Z11, Z13\n", + "-5.0 Z2, Z4, Z5, Z6, Z8, Z9, Z13\n", + "2.5 Z2, Z4, Z5, Z6, Z10, Z11, Z13\n", + "2.5 Z2, Z6, Z9, Z11, Z13\n", + "7.5 Z2, Z6, Z8, Z10, Z13\n", + "2.5 Z2, Z6, Z8, Z9, Z10, Z11, Z13\n", + "-5.0 Z2, Z5, Z7, Z8, Z13\n", + "-2.5 Z2, Z5, Z8, Z11, Z13\n", + "-5.0 Z2, Z4, Z5, Z6, Z7, Z8, Z13\n", + "-2.5 Z2, Z4, Z5, Z8, Z10, Z11, Z13\n", + "2.5 Z2, Z7, Z8, Z11, Z13\n", + "2.5 Z2, Z6, Z7, Z8, Z10, Z11, Z13\n", + "2.5 Z2, Z5, Z7, Z10, Z13\n", + "-2.5 Z2, Z5, Z9, Z10, Z13\n", + "2.5 Z2, Z4, Z5, Z6, Z7, Z10, Z13\n", + "-2.5 Z2, Z4, Z5, Z8, Z9, Z10, Z13\n", + "2.5 Z2, Z7, Z9, Z10, Z13\n", + "2.5 Z2, Z6, Z7, Z8, Z9, Z10, Z13\n", + "-5.0 Z2, Z3, Z4, Z5, Z7, Z9, Z13\n", + "2.5 Z2, Z3, Z4, Z5, Z7, Z11, Z13\n", + "-5.0 Z2, Z3, Z4, Z5, Z6, Z8, Z13\n", + "2.5 Z2, Z3, Z4, Z5, Z6, Z10, Z13\n", + "-15.0 Z2, Z3, Z4, Z5, Z6, Z7, Z8, Z9, Z13\n", + "7.5 Z2, Z3, Z4, Z5, Z6, Z7, Z10, Z11, Z13\n", + "-2.5 Z2, Z3, Z4, Z5, Z9, Z11, Z13\n", + "-2.5 Z2, Z3, Z4, Z5, Z8, Z10, Z13\n", + "-7.5 Z2, Z3, Z4, Z5, Z8, Z9, Z10, Z11, Z13\n", + "-5.0 Z2, Z3, Z5, Z6, Z7, Z9, Z13\n", + "2.5 Z2, Z3, Z5, Z6, Z7, Z11, Z13\n", + "-5.0 Z2, Z3, Z4, Z6, Z7, Z8, Z13\n", + "2.5 Z2, Z3, Z4, Z6, Z7, Z10, Z13\n", + "2.5 Z2, Z3, Z6, Z7, Z9, Z11, Z13\n", + "2.5 Z2, Z3, Z6, Z7, Z8, Z10, Z13\n", + "7.5 Z2, Z3, Z6, Z7, Z8, Z9, Z10, Z11, Z13\n", + "-5.0 Z2, Z3, Z5, Z7, Z8, Z9, Z13\n", + "-2.5 Z2, Z3, Z5, Z8, Z9, Z11, Z13\n", + "-5.0 Z2, Z3, Z4, Z6, Z8, Z9, Z13\n", + "-2.5 Z2, Z3, Z4, Z8, Z9, Z10, Z13\n", + "2.5 Z2, Z3, Z7, Z8, Z9, Z11, Z13\n", + "2.5 Z2, Z3, Z6, Z8, Z9, Z10, Z13\n", + "2.5 Z2, Z3, Z5, Z7, Z10, Z11, Z13\n", + "-2.5 Z2, Z3, Z5, Z9, Z10, Z11, Z13\n", + "2.5 Z2, Z3, Z4, Z6, Z10, Z11, Z13\n", + "-2.5 Z2, Z3, Z4, Z8, Z10, Z11, Z13\n", + "2.5 Z2, Z3, Z7, Z9, Z10, Z11, Z13\n", + "2.5 Z2, Z3, Z6, Z8, Z10, Z11, Z13\n", + "7.5 Z4, Z5, Z7, Z8, Z13\n", + "5.0 Z4, Z5, Z7, Z10, Z13\n", + "7.5 Z4, Z7, Z8, Z9, Z13\n", + "5.0 Z4, Z7, Z10, Z11, Z13\n", + "7.5 Z5, Z6, Z7, Z8, Z13\n", + "5.0 Z5, Z6, Z7, Z10, Z13\n", + "7.5 Z5, Z6, Z8, Z9, Z13\n", + "5.0 Z5, Z6, Z10, Z11, Z13\n", + "-15.0 Z5, Z7, Z9, Z11, Z13\n", + "-5.0 Z5, Z7, Z8, Z10, Z13\n", + "-5.0 Z5, Z7, Z8, Z9, Z10, Z11, Z13\n", + "7.5 Z4, Z5, Z6, Z9, Z13\n", + "5.0 Z4, Z5, Z9, Z10, Z13\n", + "7.5 Z4, Z6, Z7, Z9, Z13\n", + "5.0 Z4, Z9, Z10, Z11, Z13\n", + "-5.0 Z5, Z6, Z9, Z10, Z13\n", + "-5.0 Z5, Z6, Z7, Z9, Z10, Z11, Z13\n", + "5.0 Z5, Z8, Z9, Z10, Z13\n", + "5.0 Z5, Z8, Z10, Z11, Z13\n", + "5.0 Z4, Z5, Z6, Z11, Z13\n", + "5.0 Z4, Z5, Z8, Z11, Z13\n", + "5.0 Z4, Z6, Z7, Z11, Z13\n", + "5.0 Z4, Z8, Z9, Z11, Z13\n", + "-5.0 Z5, Z6, Z8, Z11, Z13\n", + "-5.0 Z5, Z6, Z7, Z8, Z9, Z11, Z13\n", + "-5.0 Z4, Z6, Z9, Z11, Z13\n", + "-15.0 Z4, Z6, Z8, Z10, Z13\n", + "-5.0 Z4, Z6, Z8, Z9, Z10, Z11, Z13\n", + "-5.0 Z4, Z7, Z8, Z11, Z13\n", + "-5.0 Z4, Z6, Z7, Z8, Z10, Z11, Z13\n", + "-5.0 Z4, Z7, Z9, Z10, Z13\n", + "-5.0 Z4, Z6, Z7, Z8, Z9, Z10, Z13\n", + "-5.0 Z4, Z5, Z6, Z7, Z9, Z11, Z13\n", + "-5.0 Z4, Z5, Z6, Z7, Z8, Z10, Z13\n", + "-15.0 Z4, Z5, Z6, Z7, Z8, Z9, Z10, Z11, Z13\n", + "-5.0 Z4, Z5, Z7, Z8, Z9, Z11, Z13\n", + "-5.0 Z4, Z5, Z6, Z8, Z9, Z10, Z13\n", + "-5.0 Z4, Z5, Z7, Z9, Z10, Z11, Z13\n", + "-5.0 Z4, Z5, Z6, Z8, Z10, Z11, Z13\n", + "5.0 Z6, Z7, Z9, Z10, Z13\n", + "5.0 Z6, Z9, Z10, Z11, Z13\n", + "5.0 Z7, Z8, Z9, Z10, Z13\n", + "5.0 Z7, Z8, Z10, Z11, Z13\n", + "5.0 Z6, Z7, Z8, Z11, Z13\n", + "5.0 Z6, Z8, Z9, Z11, Z13\n" + ] + } + ], + "source": [ + "# lambda0 and lambda1 are two parameters used in constraining the spatial structure of protein.\n", + "h = protein.get_protein_hamiltonian(lambda0=10.0, lambda1=10.0)\n", + "print(h)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Variational quantum circuit\n", + "Users can use variational quantum algorithm (VQA) to solve protein folding problem, the variational quantum circuit is shown below" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from folding_protein import circuit\n", + "\n", + "# The following code builds a 4-qubit parameterized circuit that contains two circuit blocks (NOTE: one circuit block contains a layer of RY and a layer of CNOT).\n", + "cir = circuit(4, 2)\n", + "cir.plot()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## How to use\n", + "Users can customize their task via a configuration file `config.toml`, the user defined settings are listed below.\n", + "```toml\n", + "# The configuration file for protein folding problem\n", + "\n", + "# The amino acides consists in protein. \n", + "amino_acids = [\"A\", \"P\", \"R\", \"L\", \"R\", \"F\", \"Y\"]\n", + "# Pair of indices indicates the potentially interact amino acide pair.\n", + "possible_contactions = [[0, 5], [1, 6]]\n", + "# Depth of the quantum circuit used in VQE\n", + "depth = 1\n", + "# Number of VQE iterations\n", + "num_iterations = 200\n", + "# The condition for VQE convergence\n", + "tol = 1e-3\n", + "# The number of steps between two consecutive loss records\n", + "save_every = 10\n", + "# learning rate for the optimizer\n", + "learning_rate = 0.5\n", + "```\n", + "In order to obtained the 3D structure of the amino acid sequence, user can run\n", + "```shell\n", + "python folding_protein.py --config config.toml\n", + "```\n", + "in terminal, the program will save the figure automatically after the computation.\n", + "\n", + "![](APRLRFY_3d_structure.jpg)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## References \n", + "\\[1\\] Pande, Vijay S., and Daniel S. Rokhsar. \"Folding pathway of a lattice model for proteins.\" Proceedings of the National Academy of Sciences 96.4 (1999): 1273-1278.\n", + "\n", + "\\[2\\] Robert, Anton, et al. \"Resource-efficient quantum algorithm for protein folding.\" npj Quantum Information 7.1 (2021): 1-5." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "modellib", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.15 (default, Nov 10 2022, 12:46:26) \n[Clang 14.0.6 ]" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "8f24120f890011f53feb4ed62c47961d8565ec1de8b7cb23548c15bd6da8f2d2" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/applications/protein_folding/lattice_model_demo.jpg b/applications/protein_folding/lattice_model_demo.jpg new file mode 100644 index 0000000..5a20316 Binary files /dev/null and b/applications/protein_folding/lattice_model_demo.jpg differ diff --git a/applications/protein_folding/lattice_model_en.jpg b/applications/protein_folding/lattice_model_en.jpg new file mode 100644 index 0000000..ffb754f Binary files /dev/null and b/applications/protein_folding/lattice_model_en.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00401.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00401.jpg new file mode 100644 index 0000000..47d1f98 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00401.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00402.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00402.jpg new file mode 100644 index 0000000..0b16e7c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00402.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00403.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00403.jpg new file mode 100644 index 0000000..72be47d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00403.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00404.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00404.jpg new file mode 100644 index 0000000..ff5e331 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00404.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00405.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00405.jpg new file mode 100644 index 0000000..9166873 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00405.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00406.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00406.jpg new file mode 100644 index 0000000..a0a1b51 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00406.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00407.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00407.jpg new file mode 100644 index 0000000..1047835 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00407.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00408.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00408.jpg new file mode 100644 index 0000000..0aa142a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00408.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00409.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00409.jpg new file mode 100644 index 0000000..d68f142 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00409.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00410.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00410.jpg new file mode 100644 index 0000000..ec3f06c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00410.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00411.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00411.jpg new file mode 100644 index 0000000..561a952 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00411.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00412.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00412.jpg new file mode 100644 index 0000000..5898db1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00412.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00413.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00413.jpg new file mode 100644 index 0000000..9d228c5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00413.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00414.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00414.jpg new file mode 100644 index 0000000..bd9749b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00414.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00415.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00415.jpg new file mode 100644 index 0000000..940ec55 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00415.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00416.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00416.jpg new file mode 100644 index 0000000..c7c637e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00416.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00417.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00417.jpg new file mode 100644 index 0000000..956b40f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00417.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00418.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00418.jpg new file mode 100644 index 0000000..a3232e4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00418.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00419.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00419.jpg new file mode 100644 index 0000000..bc209e9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00419.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00420.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00420.jpg new file mode 100644 index 0000000..5d71ae8 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00420.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00421.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00421.jpg new file mode 100644 index 0000000..5df395f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00421.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00422.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00422.jpg new file mode 100644 index 0000000..f8d9f30 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00422.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00423.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00423.jpg new file mode 100644 index 0000000..2375722 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00423.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00424.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00424.jpg new file mode 100644 index 0000000..7c16fd6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00424.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00425.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00425.jpg new file mode 100644 index 0000000..a419d81 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00425.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00426.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00426.jpg new file mode 100644 index 0000000..63cf1cf Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00426.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00427.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00427.jpg new file mode 100644 index 0000000..063b6aa Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00427.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00428.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00428.jpg new file mode 100644 index 0000000..01a770e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00428.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00429.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00429.jpg new file mode 100644 index 0000000..36ae8fe Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00429.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00430.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00430.jpg new file mode 100644 index 0000000..530330f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00430.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00431.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00431.jpg new file mode 100644 index 0000000..15adcf1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00431.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00432.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00432.jpg new file mode 100644 index 0000000..5b3abe9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00432.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00433.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00433.jpg new file mode 100644 index 0000000..cff9c63 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00433.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00434.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00434.jpg new file mode 100644 index 0000000..5a18a56 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00434.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00435.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00435.jpg new file mode 100644 index 0000000..973e796 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00435.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00436.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00436.jpg new file mode 100644 index 0000000..505ed9c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00436.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00437.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00437.jpg new file mode 100644 index 0000000..ee115f3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00437.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00438.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00438.jpg new file mode 100644 index 0000000..ec17c7c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00438.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00439.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00439.jpg new file mode 100644 index 0000000..7ff794d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00439.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00440.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00440.jpg new file mode 100644 index 0000000..1d86409 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00440.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00441.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00441.jpg new file mode 100644 index 0000000..5e58490 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00441.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00442.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00442.jpg new file mode 100644 index 0000000..e0f6b7d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00442.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00443.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00443.jpg new file mode 100644 index 0000000..5135d5f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00443.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00444.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00444.jpg new file mode 100644 index 0000000..aba1abd Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00444.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00445.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00445.jpg new file mode 100644 index 0000000..d6e3df4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00445.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00446.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00446.jpg new file mode 100644 index 0000000..f2dc83f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00446.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00447.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00447.jpg new file mode 100644 index 0000000..cc317f6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00447.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00448.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00448.jpg new file mode 100644 index 0000000..90f397f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00448.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00449.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00449.jpg new file mode 100644 index 0000000..063a057 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00449.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00450.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00450.jpg new file mode 100644 index 0000000..48be1cc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00450.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00451.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00451.jpg new file mode 100644 index 0000000..646f275 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00451.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00452.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00452.jpg new file mode 100644 index 0000000..e5744fc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00452.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00453.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00453.jpg new file mode 100644 index 0000000..3b5af8a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00453.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00454.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00454.jpg new file mode 100644 index 0000000..fc5ed95 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00454.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00455.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00455.jpg new file mode 100644 index 0000000..ec8c206 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00455.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00456.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00456.jpg new file mode 100644 index 0000000..02b4675 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00456.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00457.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00457.jpg new file mode 100644 index 0000000..fbcefff Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00457.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00458.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00458.jpg new file mode 100644 index 0000000..5bf8631 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00458.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00459.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00459.jpg new file mode 100644 index 0000000..ce9898b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00459.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00460.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00460.jpg new file mode 100644 index 0000000..d29d203 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00460.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00461.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00461.jpg new file mode 100644 index 0000000..fcabaa5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00461.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00462.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00462.jpg new file mode 100644 index 0000000..605a4eb Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00462.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00463.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00463.jpg new file mode 100644 index 0000000..291856a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00463.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00464.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00464.jpg new file mode 100644 index 0000000..ec42f37 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00464.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00465.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00465.jpg new file mode 100644 index 0000000..8db6a9b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00465.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00466.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00466.jpg new file mode 100644 index 0000000..a9470ec Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00466.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00467.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00467.jpg new file mode 100644 index 0000000..cdf489a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00467.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00468.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00468.jpg new file mode 100644 index 0000000..77a2ca2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00468.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00469.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00469.jpg new file mode 100644 index 0000000..cefb88c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00469.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00470.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00470.jpg new file mode 100644 index 0000000..b2f3f34 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00470.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00471.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00471.jpg new file mode 100644 index 0000000..d386e9f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00471.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00472.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00472.jpg new file mode 100644 index 0000000..674ed7f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00472.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00473.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00473.jpg new file mode 100644 index 0000000..0d11969 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00473.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00474.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00474.jpg new file mode 100644 index 0000000..4130c3b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00474.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00475.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00475.jpg new file mode 100644 index 0000000..157329f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00475.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00476.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00476.jpg new file mode 100644 index 0000000..365baa6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00476.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00477.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00477.jpg new file mode 100644 index 0000000..22e6f96 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00477.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00478.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00478.jpg new file mode 100644 index 0000000..7ea35f9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00478.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00479.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00479.jpg new file mode 100644 index 0000000..f1c8bd3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00479.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00480.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00480.jpg new file mode 100644 index 0000000..63b213a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00480.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00481.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00481.jpg new file mode 100644 index 0000000..f83507b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00481.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00482.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00482.jpg new file mode 100644 index 0000000..3555089 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00482.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00483.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00483.jpg new file mode 100644 index 0000000..52a80b7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00483.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00484.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00484.jpg new file mode 100644 index 0000000..7775961 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00484.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00485.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00485.jpg new file mode 100644 index 0000000..8707733 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00485.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00486.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00486.jpg new file mode 100644 index 0000000..d7b26ad Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00486.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00487.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00487.jpg new file mode 100644 index 0000000..47a23e3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00487.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00488.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00488.jpg new file mode 100644 index 0000000..9864690 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00488.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00489.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00489.jpg new file mode 100644 index 0000000..28f8c62 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00489.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00490.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00490.jpg new file mode 100644 index 0000000..56fa8fe Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00490.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00491.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00491.jpg new file mode 100644 index 0000000..8d2edaa Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00491.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00492.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00492.jpg new file mode 100644 index 0000000..6a1ec25 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00492.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00493.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00493.jpg new file mode 100644 index 0000000..37c1fcf Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00493.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00494.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00494.jpg new file mode 100644 index 0000000..00f4dec Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00494.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00495.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00495.jpg new file mode 100644 index 0000000..b7fd5d1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00495.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00496.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00496.jpg new file mode 100644 index 0000000..dcd8c2b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00496.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00497.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00497.jpg new file mode 100644 index 0000000..fb57029 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00497.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00498.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00498.jpg new file mode 100644 index 0000000..ba7f1ef Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00498.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00499.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00499.jpg new file mode 100644 index 0000000..00a0148 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00499.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Negative/00500.jpg b/applications/quality_detection/SurfaceCrack/test_data/Negative/00500.jpg new file mode 100644 index 0000000..31272ee Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Negative/00500.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00401.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00401.jpg new file mode 100644 index 0000000..a99b43a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00401.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00402.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00402.jpg new file mode 100644 index 0000000..bcb8a72 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00402.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00403.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00403.jpg new file mode 100644 index 0000000..6260502 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00403.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00404.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00404.jpg new file mode 100644 index 0000000..10ed12c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00404.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00405.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00405.jpg new file mode 100644 index 0000000..ffac815 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00405.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00406.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00406.jpg new file mode 100644 index 0000000..1f83e3d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00406.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00407.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00407.jpg new file mode 100644 index 0000000..24dd4e4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00407.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00408.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00408.jpg new file mode 100644 index 0000000..efc4fb7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00408.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00409.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00409.jpg new file mode 100644 index 0000000..02b1500 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00409.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00410.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00410.jpg new file mode 100644 index 0000000..fe8806d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00410.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00411.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00411.jpg new file mode 100644 index 0000000..46f0f5c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00411.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00412.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00412.jpg new file mode 100644 index 0000000..54007ae Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00412.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00413.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00413.jpg new file mode 100644 index 0000000..b3e3347 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00413.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00414.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00414.jpg new file mode 100644 index 0000000..b0e1b8c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00414.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00415.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00415.jpg new file mode 100644 index 0000000..108094d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00415.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00416.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00416.jpg new file mode 100644 index 0000000..782bfdf Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00416.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00417.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00417.jpg new file mode 100644 index 0000000..cef53ac Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00417.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00418.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00418.jpg new file mode 100644 index 0000000..23604f3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00418.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00419.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00419.jpg new file mode 100644 index 0000000..0b56811 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00419.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00420.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00420.jpg new file mode 100644 index 0000000..44a2a86 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00420.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00421.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00421.jpg new file mode 100644 index 0000000..ce557ff Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00421.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00422.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00422.jpg new file mode 100644 index 0000000..c478014 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00422.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00423.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00423.jpg new file mode 100644 index 0000000..1fe968c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00423.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00424.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00424.jpg new file mode 100644 index 0000000..5c40e2a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00424.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00425.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00425.jpg new file mode 100644 index 0000000..2d0525f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00425.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00426.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00426.jpg new file mode 100644 index 0000000..c14bcf7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00426.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00427.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00427.jpg new file mode 100644 index 0000000..44c9d9e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00427.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00428.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00428.jpg new file mode 100644 index 0000000..57f20dc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00428.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00429.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00429.jpg new file mode 100644 index 0000000..c9e8952 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00429.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00430.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00430.jpg new file mode 100644 index 0000000..69d0593 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00430.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00431.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00431.jpg new file mode 100644 index 0000000..b7b2833 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00431.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00432.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00432.jpg new file mode 100644 index 0000000..bb19713 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00432.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00433.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00433.jpg new file mode 100644 index 0000000..79ccfc0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00433.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00434.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00434.jpg new file mode 100644 index 0000000..90c61dc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00434.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00435.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00435.jpg new file mode 100644 index 0000000..5ae50e8 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00435.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00436.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00436.jpg new file mode 100644 index 0000000..53aa063 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00436.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00437.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00437.jpg new file mode 100644 index 0000000..0f0450f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00437.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00438.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00438.jpg new file mode 100644 index 0000000..a3213e6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00438.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00439.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00439.jpg new file mode 100644 index 0000000..8867058 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00439.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00440.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00440.jpg new file mode 100644 index 0000000..6625473 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00440.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00441.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00441.jpg new file mode 100644 index 0000000..c421fca Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00441.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00442.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00442.jpg new file mode 100644 index 0000000..de340fa Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00442.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00443.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00443.jpg new file mode 100644 index 0000000..c3be122 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00443.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00444.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00444.jpg new file mode 100644 index 0000000..2eaac27 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00444.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00445.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00445.jpg new file mode 100644 index 0000000..09195ca Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00445.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00446.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00446.jpg new file mode 100644 index 0000000..7ce585a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00446.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00447.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00447.jpg new file mode 100644 index 0000000..0948de6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00447.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00448.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00448.jpg new file mode 100644 index 0000000..7da4e8e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00448.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00449.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00449.jpg new file mode 100644 index 0000000..2c0f351 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00449.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00450.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00450.jpg new file mode 100644 index 0000000..0074f65 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00450.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00451.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00451.jpg new file mode 100644 index 0000000..9f5da30 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00451.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00452.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00452.jpg new file mode 100644 index 0000000..fd00d43 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00452.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00453.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00453.jpg new file mode 100644 index 0000000..22b1b8b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00453.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00454.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00454.jpg new file mode 100644 index 0000000..f1e3085 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00454.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00455.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00455.jpg new file mode 100644 index 0000000..21d04bc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00455.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00456.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00456.jpg new file mode 100644 index 0000000..b1c368b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00456.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00457.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00457.jpg new file mode 100644 index 0000000..07d38f5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00457.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00458.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00458.jpg new file mode 100644 index 0000000..2a3bc58 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00458.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00459.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00459.jpg new file mode 100644 index 0000000..cdabdb4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00459.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00460.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00460.jpg new file mode 100644 index 0000000..519a186 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00460.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00461.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00461.jpg new file mode 100644 index 0000000..1aa272d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00461.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00462.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00462.jpg new file mode 100644 index 0000000..eb4fffc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00462.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00463.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00463.jpg new file mode 100644 index 0000000..5d67230 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00463.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00464.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00464.jpg new file mode 100644 index 0000000..882f344 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00464.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00465.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00465.jpg new file mode 100644 index 0000000..38ebd59 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00465.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00466.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00466.jpg new file mode 100644 index 0000000..efb546d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00466.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00467.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00467.jpg new file mode 100644 index 0000000..26645da Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00467.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00468.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00468.jpg new file mode 100644 index 0000000..d9b7889 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00468.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00469.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00469.jpg new file mode 100644 index 0000000..a29e1f2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00469.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00470.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00470.jpg new file mode 100644 index 0000000..62d5689 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00470.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00471.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00471.jpg new file mode 100644 index 0000000..5137a73 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00471.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00472.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00472.jpg new file mode 100644 index 0000000..286510f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00472.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00473.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00473.jpg new file mode 100644 index 0000000..3ba8c29 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00473.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00474.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00474.jpg new file mode 100644 index 0000000..e4d604e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00474.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00475.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00475.jpg new file mode 100644 index 0000000..5e43d3a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00475.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00476.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00476.jpg new file mode 100644 index 0000000..b6eff55 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00476.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00477.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00477.jpg new file mode 100644 index 0000000..6b9004d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00477.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00478.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00478.jpg new file mode 100644 index 0000000..0ca84a0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00478.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00479.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00479.jpg new file mode 100644 index 0000000..2b37277 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00479.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00480.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00480.jpg new file mode 100644 index 0000000..2832a1a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00480.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00481.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00481.jpg new file mode 100644 index 0000000..647bf58 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00481.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00482.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00482.jpg new file mode 100644 index 0000000..bba967b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00482.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00483.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00483.jpg new file mode 100644 index 0000000..00cb310 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00483.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00484.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00484.jpg new file mode 100644 index 0000000..7206473 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00484.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00485.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00485.jpg new file mode 100644 index 0000000..a0f8526 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00485.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00486.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00486.jpg new file mode 100644 index 0000000..1494f04 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00486.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00487.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00487.jpg new file mode 100644 index 0000000..d2c8596 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00487.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00488.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00488.jpg new file mode 100644 index 0000000..1743f8f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00488.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00489.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00489.jpg new file mode 100644 index 0000000..20817bb Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00489.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00490.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00490.jpg new file mode 100644 index 0000000..779ab5e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00490.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00491.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00491.jpg new file mode 100644 index 0000000..e9b1e5a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00491.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00492.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00492.jpg new file mode 100644 index 0000000..68a6233 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00492.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00493.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00493.jpg new file mode 100644 index 0000000..2bb74c7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00493.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00494.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00494.jpg new file mode 100644 index 0000000..3cedd29 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00494.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00495.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00495.jpg new file mode 100644 index 0000000..91e7db3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00495.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00496.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00496.jpg new file mode 100644 index 0000000..902aa68 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00496.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00497.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00497.jpg new file mode 100644 index 0000000..7193eff Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00497.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00498.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00498.jpg new file mode 100644 index 0000000..22978f9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00498.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00499.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00499.jpg new file mode 100644 index 0000000..487bc20 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00499.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/test_data/Positive/00500.jpg b/applications/quality_detection/SurfaceCrack/test_data/Positive/00500.jpg new file mode 100644 index 0000000..6e3443c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/test_data/Positive/00500.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00001.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00001.jpg new file mode 100644 index 0000000..7022b79 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00001.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00002.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00002.jpg new file mode 100644 index 0000000..011cf75 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00002.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00003.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00003.jpg new file mode 100644 index 0000000..b44d936 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00003.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00004.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00004.jpg new file mode 100644 index 0000000..27549a9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00004.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00005.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00005.jpg new file mode 100644 index 0000000..43f24ff Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00005.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00006.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00006.jpg new file mode 100644 index 0000000..0a146fd Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00006.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00007.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00007.jpg new file mode 100644 index 0000000..763aad1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00007.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00008.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00008.jpg new file mode 100644 index 0000000..a5301bd Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00008.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00009.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00009.jpg new file mode 100644 index 0000000..0d0f480 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00009.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00010.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00010.jpg new file mode 100644 index 0000000..961f194 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00010.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00011.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00011.jpg new file mode 100644 index 0000000..4567875 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00011.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00012.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00012.jpg new file mode 100644 index 0000000..1e6662b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00012.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00013.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00013.jpg new file mode 100644 index 0000000..5aca703 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00013.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00014.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00014.jpg new file mode 100644 index 0000000..42913ce Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00014.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00015.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00015.jpg new file mode 100644 index 0000000..ac5f503 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00015.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00016.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00016.jpg new file mode 100644 index 0000000..7b1c44e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00016.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00017.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00017.jpg new file mode 100644 index 0000000..553fc88 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00017.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00018.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00018.jpg new file mode 100644 index 0000000..4ba2f49 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00018.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00019.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00019.jpg new file mode 100644 index 0000000..5524c8d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00019.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00020.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00020.jpg new file mode 100644 index 0000000..b95ca43 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00020.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00021.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00021.jpg new file mode 100644 index 0000000..965ae3b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00021.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00022.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00022.jpg new file mode 100644 index 0000000..d68f142 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00022.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00023.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00023.jpg new file mode 100644 index 0000000..c39af1d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00023.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00024.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00024.jpg new file mode 100644 index 0000000..e58806a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00024.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00025.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00025.jpg new file mode 100644 index 0000000..40c7d2b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00025.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00026.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00026.jpg new file mode 100644 index 0000000..b2f44dd Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00026.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00027.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00027.jpg new file mode 100644 index 0000000..cdf3134 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00027.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00028.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00028.jpg new file mode 100644 index 0000000..4edc374 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00028.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00029.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00029.jpg new file mode 100644 index 0000000..cff260e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00029.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00030.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00030.jpg new file mode 100644 index 0000000..99987a1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00030.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00031.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00031.jpg new file mode 100644 index 0000000..ac5bac9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00031.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00032.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00032.jpg new file mode 100644 index 0000000..f288cb4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00032.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00033.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00033.jpg new file mode 100644 index 0000000..d901232 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00033.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00034.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00034.jpg new file mode 100644 index 0000000..bc8b2fa Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00034.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00035.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00035.jpg new file mode 100644 index 0000000..c97d943 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00035.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00036.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00036.jpg new file mode 100644 index 0000000..05aa344 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00036.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00037.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00037.jpg new file mode 100644 index 0000000..d646cff Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00037.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00038.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00038.jpg new file mode 100644 index 0000000..3305da0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00038.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00039.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00039.jpg new file mode 100644 index 0000000..57cd638 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00039.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00040.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00040.jpg new file mode 100644 index 0000000..47a2145 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00040.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00041.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00041.jpg new file mode 100644 index 0000000..567e87f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00041.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00042.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00042.jpg new file mode 100644 index 0000000..db9c9a7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00042.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00043.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00043.jpg new file mode 100644 index 0000000..2d43ac4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00043.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00044.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00044.jpg new file mode 100644 index 0000000..db121f1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00044.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00045.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00045.jpg new file mode 100644 index 0000000..7c8bbf5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00045.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00046.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00046.jpg new file mode 100644 index 0000000..a631c1b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00046.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00047.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00047.jpg new file mode 100644 index 0000000..0b00176 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00047.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00048.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00048.jpg new file mode 100644 index 0000000..094cfce Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00048.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00049.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00049.jpg new file mode 100644 index 0000000..78daf83 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00049.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00050.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00050.jpg new file mode 100644 index 0000000..769a7e1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00050.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00051.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00051.jpg new file mode 100644 index 0000000..0d494d3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00051.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00052.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00052.jpg new file mode 100644 index 0000000..3dfe4a4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00052.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00053.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00053.jpg new file mode 100644 index 0000000..0227279 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00053.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00054.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00054.jpg new file mode 100644 index 0000000..51065c8 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00054.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00055.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00055.jpg new file mode 100644 index 0000000..19abf84 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00055.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00056.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00056.jpg new file mode 100644 index 0000000..e483f43 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00056.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00057.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00057.jpg new file mode 100644 index 0000000..0e95ee7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00057.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00058.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00058.jpg new file mode 100644 index 0000000..f6c5b29 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00058.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00059.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00059.jpg new file mode 100644 index 0000000..36608b1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00059.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00060.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00060.jpg new file mode 100644 index 0000000..ee8a86c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00060.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00061.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00061.jpg new file mode 100644 index 0000000..5dbe5cc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00061.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00062.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00062.jpg new file mode 100644 index 0000000..2a24aed Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00062.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00063.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00063.jpg new file mode 100644 index 0000000..76303e7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00063.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00064.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00064.jpg new file mode 100644 index 0000000..ee5dc93 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00064.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00065.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00065.jpg new file mode 100644 index 0000000..456c910 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00065.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00066.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00066.jpg new file mode 100644 index 0000000..6c9a15d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00066.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00067.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00067.jpg new file mode 100644 index 0000000..2f3555a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00067.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00068.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00068.jpg new file mode 100644 index 0000000..75fc980 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00068.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00069.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00069.jpg new file mode 100644 index 0000000..9041f15 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00069.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00070.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00070.jpg new file mode 100644 index 0000000..1a4902d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00070.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00071.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00071.jpg new file mode 100644 index 0000000..52c3e5a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00071.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00072.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00072.jpg new file mode 100644 index 0000000..82124f4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00072.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00073.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00073.jpg new file mode 100644 index 0000000..ad9707d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00073.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00074.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00074.jpg new file mode 100644 index 0000000..02d9f37 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00074.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00075.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00075.jpg new file mode 100644 index 0000000..33b6801 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00075.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00076.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00076.jpg new file mode 100644 index 0000000..164bcd7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00076.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00077.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00077.jpg new file mode 100644 index 0000000..3d7dfc0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00077.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00078.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00078.jpg new file mode 100644 index 0000000..cc06b2e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00078.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00079.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00079.jpg new file mode 100644 index 0000000..34cab2d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00079.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00080.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00080.jpg new file mode 100644 index 0000000..d853f75 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00080.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00081.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00081.jpg new file mode 100644 index 0000000..d4ce015 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00081.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00082.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00082.jpg new file mode 100644 index 0000000..4d125d6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00082.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00083.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00083.jpg new file mode 100644 index 0000000..59b83ca Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00083.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00084.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00084.jpg new file mode 100644 index 0000000..2b8839e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00084.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00085.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00085.jpg new file mode 100644 index 0000000..ae9bee1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00085.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00086.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00086.jpg new file mode 100644 index 0000000..ecde63e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00086.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00087.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00087.jpg new file mode 100644 index 0000000..7790582 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00087.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00088.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00088.jpg new file mode 100644 index 0000000..a66671f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00088.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00089.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00089.jpg new file mode 100644 index 0000000..76df911 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00089.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00090.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00090.jpg new file mode 100644 index 0000000..4288e20 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00090.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00091.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00091.jpg new file mode 100644 index 0000000..ddcc75b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00091.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00092.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00092.jpg new file mode 100644 index 0000000..2f00166 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00092.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00093.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00093.jpg new file mode 100644 index 0000000..ef94f88 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00093.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00094.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00094.jpg new file mode 100644 index 0000000..08dd2c0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00094.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00095.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00095.jpg new file mode 100644 index 0000000..4ed400a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00095.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00096.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00096.jpg new file mode 100644 index 0000000..3196914 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00096.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00097.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00097.jpg new file mode 100644 index 0000000..97ab50e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00097.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00098.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00098.jpg new file mode 100644 index 0000000..edf7912 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00098.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00099.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00099.jpg new file mode 100644 index 0000000..faf49ce Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00099.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00100.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00100.jpg new file mode 100644 index 0000000..97cd17e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00100.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00101.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00101.jpg new file mode 100644 index 0000000..387fa78 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00101.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00102.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00102.jpg new file mode 100644 index 0000000..ba4c5b7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00102.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00103.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00103.jpg new file mode 100644 index 0000000..d3fa1b0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00103.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00104.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00104.jpg new file mode 100644 index 0000000..a0de783 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00104.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00105.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00105.jpg new file mode 100644 index 0000000..292a0d8 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00105.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00106.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00106.jpg new file mode 100644 index 0000000..704c88b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00106.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00107.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00107.jpg new file mode 100644 index 0000000..5588551 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00107.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00108.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00108.jpg new file mode 100644 index 0000000..af49990 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00108.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00109.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00109.jpg new file mode 100644 index 0000000..8f838e3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00109.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00110.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00110.jpg new file mode 100644 index 0000000..f817bc2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00110.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00111.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00111.jpg new file mode 100644 index 0000000..b73ac23 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00111.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00112.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00112.jpg new file mode 100644 index 0000000..44299ec Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00112.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00113.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00113.jpg new file mode 100644 index 0000000..a4a4399 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00113.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00114.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00114.jpg new file mode 100644 index 0000000..47dd584 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00114.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00115.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00115.jpg new file mode 100644 index 0000000..d785b38 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00115.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00116.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00116.jpg new file mode 100644 index 0000000..7517ebc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00116.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00117.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00117.jpg new file mode 100644 index 0000000..a2d97ef Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00117.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00118.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00118.jpg new file mode 100644 index 0000000..9bdcfaf Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00118.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00119.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00119.jpg new file mode 100644 index 0000000..586ac56 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00119.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00120.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00120.jpg new file mode 100644 index 0000000..f960f05 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00120.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00121.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00121.jpg new file mode 100644 index 0000000..17518fc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00121.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00122.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00122.jpg new file mode 100644 index 0000000..ad533b0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00122.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00123.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00123.jpg new file mode 100644 index 0000000..7192862 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00123.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00124.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00124.jpg new file mode 100644 index 0000000..8d6d43a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00124.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00125.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00125.jpg new file mode 100644 index 0000000..4895e5b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00125.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00126.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00126.jpg new file mode 100644 index 0000000..d1c247b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00126.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00127.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00127.jpg new file mode 100644 index 0000000..d44c015 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00127.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00128.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00128.jpg new file mode 100644 index 0000000..5e03aad Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00128.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00129.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00129.jpg new file mode 100644 index 0000000..678242b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00129.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00130.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00130.jpg new file mode 100644 index 0000000..eff2cb2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00130.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00131.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00131.jpg new file mode 100644 index 0000000..df35324 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00131.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00132.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00132.jpg new file mode 100644 index 0000000..edc69aa Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00132.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00133.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00133.jpg new file mode 100644 index 0000000..75eef58 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00133.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00134.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00134.jpg new file mode 100644 index 0000000..95078a8 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00134.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00135.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00135.jpg new file mode 100644 index 0000000..e7d6164 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00135.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00136.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00136.jpg new file mode 100644 index 0000000..00f4e7d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00136.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00137.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00137.jpg new file mode 100644 index 0000000..31c39e2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00137.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00138.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00138.jpg new file mode 100644 index 0000000..06a5002 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00138.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00139.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00139.jpg new file mode 100644 index 0000000..f867695 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00139.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00140.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00140.jpg new file mode 100644 index 0000000..fe19086 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00140.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00141.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00141.jpg new file mode 100644 index 0000000..18f05e7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00141.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00142.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00142.jpg new file mode 100644 index 0000000..100a46d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00142.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00143.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00143.jpg new file mode 100644 index 0000000..c6391b7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00143.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00144.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00144.jpg new file mode 100644 index 0000000..89d822a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00144.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00145.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00145.jpg new file mode 100644 index 0000000..7c54de5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00145.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00146.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00146.jpg new file mode 100644 index 0000000..6b1c1a7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00146.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00147.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00147.jpg new file mode 100644 index 0000000..cbdbfc7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00147.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00148.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00148.jpg new file mode 100644 index 0000000..749d8e1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00148.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00149.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00149.jpg new file mode 100644 index 0000000..dc4ea66 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00149.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00150.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00150.jpg new file mode 100644 index 0000000..b6f6663 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00150.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00151.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00151.jpg new file mode 100644 index 0000000..e1dd7e2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00151.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00152.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00152.jpg new file mode 100644 index 0000000..96562da Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00152.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00153.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00153.jpg new file mode 100644 index 0000000..b96dcb1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00153.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00154.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00154.jpg new file mode 100644 index 0000000..724e98c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00154.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00155.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00155.jpg new file mode 100644 index 0000000..56fc9ba Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00155.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00156.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00156.jpg new file mode 100644 index 0000000..1a0caee Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00156.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00157.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00157.jpg new file mode 100644 index 0000000..c61eac5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00157.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00158.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00158.jpg new file mode 100644 index 0000000..d0cdf1c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00158.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00159.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00159.jpg new file mode 100644 index 0000000..f4c2ea6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00159.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00160.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00160.jpg new file mode 100644 index 0000000..3189897 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00160.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00161.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00161.jpg new file mode 100644 index 0000000..1455832 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00161.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00162.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00162.jpg new file mode 100644 index 0000000..e5d94b2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00162.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00163.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00163.jpg new file mode 100644 index 0000000..7270cfd Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00163.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00164.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00164.jpg new file mode 100644 index 0000000..4ba5542 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00164.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00165.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00165.jpg new file mode 100644 index 0000000..fb2612d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00165.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00166.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00166.jpg new file mode 100644 index 0000000..b2456d4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00166.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00167.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00167.jpg new file mode 100644 index 0000000..5964e15 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00167.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00168.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00168.jpg new file mode 100644 index 0000000..da516cc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00168.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00169.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00169.jpg new file mode 100644 index 0000000..1aaed8b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00169.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00170.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00170.jpg new file mode 100644 index 0000000..cdbd717 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00170.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00171.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00171.jpg new file mode 100644 index 0000000..6330bb3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00171.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00172.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00172.jpg new file mode 100644 index 0000000..b4a86f5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00172.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00173.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00173.jpg new file mode 100644 index 0000000..ca55f15 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00173.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00174.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00174.jpg new file mode 100644 index 0000000..29ea8a6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00174.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00175.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00175.jpg new file mode 100644 index 0000000..5d2cf0a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00175.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00176.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00176.jpg new file mode 100644 index 0000000..78d3fcb Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00176.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00177.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00177.jpg new file mode 100644 index 0000000..b8fbd9f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00177.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00178.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00178.jpg new file mode 100644 index 0000000..a290915 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00178.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00179.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00179.jpg new file mode 100644 index 0000000..a3fa295 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00179.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00180.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00180.jpg new file mode 100644 index 0000000..efcd3aa Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00180.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00181.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00181.jpg new file mode 100644 index 0000000..35437d4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00181.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00182.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00182.jpg new file mode 100644 index 0000000..dbebc61 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00182.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00183.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00183.jpg new file mode 100644 index 0000000..ba6f751 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00183.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00184.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00184.jpg new file mode 100644 index 0000000..609499d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00184.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00185.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00185.jpg new file mode 100644 index 0000000..3b17edc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00185.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00186.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00186.jpg new file mode 100644 index 0000000..ffbe973 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00186.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00187.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00187.jpg new file mode 100644 index 0000000..f9d2156 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00187.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00188.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00188.jpg new file mode 100644 index 0000000..3ed2c56 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00188.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00189.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00189.jpg new file mode 100644 index 0000000..53bbe45 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00189.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00190.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00190.jpg new file mode 100644 index 0000000..dafb498 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00190.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00191.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00191.jpg new file mode 100644 index 0000000..734b1b6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00191.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00192.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00192.jpg new file mode 100644 index 0000000..42c0c6e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00192.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00193.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00193.jpg new file mode 100644 index 0000000..d7b9720 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00193.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00194.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00194.jpg new file mode 100644 index 0000000..302c41b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00194.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00195.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00195.jpg new file mode 100644 index 0000000..aea405e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00195.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00196.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00196.jpg new file mode 100644 index 0000000..dac73cb Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00196.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00197.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00197.jpg new file mode 100644 index 0000000..d20fa21 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00197.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00198.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00198.jpg new file mode 100644 index 0000000..14f379d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00198.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00199.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00199.jpg new file mode 100644 index 0000000..7a135d3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00199.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00200.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00200.jpg new file mode 100644 index 0000000..4a94ace Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00200.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00201.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00201.jpg new file mode 100644 index 0000000..4c3219a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00201.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00202.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00202.jpg new file mode 100644 index 0000000..ce8badf Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00202.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00203.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00203.jpg new file mode 100644 index 0000000..13d6c16 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00203.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00204.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00204.jpg new file mode 100644 index 0000000..70d98ea Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00204.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00205.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00205.jpg new file mode 100644 index 0000000..9679658 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00205.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00206.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00206.jpg new file mode 100644 index 0000000..7563e17 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00206.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00207.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00207.jpg new file mode 100644 index 0000000..c305ddb Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00207.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00208.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00208.jpg new file mode 100644 index 0000000..97b99f7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00208.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00209.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00209.jpg new file mode 100644 index 0000000..11e4582 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00209.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00210.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00210.jpg new file mode 100644 index 0000000..22b810d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00210.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00211.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00211.jpg new file mode 100644 index 0000000..39e2aad Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00211.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00212.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00212.jpg new file mode 100644 index 0000000..6932a72 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00212.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00213.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00213.jpg new file mode 100644 index 0000000..6237f86 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00213.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00214.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00214.jpg new file mode 100644 index 0000000..2759bd2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00214.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00215.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00215.jpg new file mode 100644 index 0000000..64384a3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00215.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00216.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00216.jpg new file mode 100644 index 0000000..03eeda7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00216.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00217.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00217.jpg new file mode 100644 index 0000000..fd6d5d7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00217.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00218.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00218.jpg new file mode 100644 index 0000000..f930284 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00218.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00219.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00219.jpg new file mode 100644 index 0000000..46282cb Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00219.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00220.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00220.jpg new file mode 100644 index 0000000..e7f0d69 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00220.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00221.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00221.jpg new file mode 100644 index 0000000..4353839 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00221.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00222.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00222.jpg new file mode 100644 index 0000000..da342ad Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00222.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00223.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00223.jpg new file mode 100644 index 0000000..1439ba7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00223.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00224.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00224.jpg new file mode 100644 index 0000000..1692b3f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00224.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00225.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00225.jpg new file mode 100644 index 0000000..ee8bb90 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00225.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00226.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00226.jpg new file mode 100644 index 0000000..2b6d002 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00226.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00227.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00227.jpg new file mode 100644 index 0000000..9b41b3c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00227.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00228.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00228.jpg new file mode 100644 index 0000000..83f24df Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00228.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00229.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00229.jpg new file mode 100644 index 0000000..92e503a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00229.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00230.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00230.jpg new file mode 100644 index 0000000..c63c97a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00230.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00231.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00231.jpg new file mode 100644 index 0000000..b6a1469 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00231.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00232.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00232.jpg new file mode 100644 index 0000000..f8d365d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00232.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00233.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00233.jpg new file mode 100644 index 0000000..5582ef9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00233.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00234.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00234.jpg new file mode 100644 index 0000000..6c7bd5d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00234.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00235.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00235.jpg new file mode 100644 index 0000000..911344b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00235.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00236.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00236.jpg new file mode 100644 index 0000000..7cc2a47 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00236.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00237.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00237.jpg new file mode 100644 index 0000000..353e540 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00237.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00238.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00238.jpg new file mode 100644 index 0000000..b199b3b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00238.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00239.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00239.jpg new file mode 100644 index 0000000..b392552 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00239.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00240.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00240.jpg new file mode 100644 index 0000000..fd64e0c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00240.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00241.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00241.jpg new file mode 100644 index 0000000..eefa994 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00241.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00242.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00242.jpg new file mode 100644 index 0000000..e938958 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00242.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00243.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00243.jpg new file mode 100644 index 0000000..4db4f5d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00243.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00244.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00244.jpg new file mode 100644 index 0000000..218031f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00244.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00245.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00245.jpg new file mode 100644 index 0000000..54578ae Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00245.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00246.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00246.jpg new file mode 100644 index 0000000..5a0d243 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00246.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00247.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00247.jpg new file mode 100644 index 0000000..4853bea Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00247.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00248.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00248.jpg new file mode 100644 index 0000000..099d23d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00248.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00249.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00249.jpg new file mode 100644 index 0000000..0514e74 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00249.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00250.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00250.jpg new file mode 100644 index 0000000..3a4a05f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00250.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00251.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00251.jpg new file mode 100644 index 0000000..43ff593 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00251.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00252.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00252.jpg new file mode 100644 index 0000000..b8c22b4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00252.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00253.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00253.jpg new file mode 100644 index 0000000..8ee6b28 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00253.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00254.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00254.jpg new file mode 100644 index 0000000..c180c5b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00254.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00255.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00255.jpg new file mode 100644 index 0000000..4e05740 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00255.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00256.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00256.jpg new file mode 100644 index 0000000..c283d4b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00256.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00257.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00257.jpg new file mode 100644 index 0000000..0e7de3b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00257.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00258.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00258.jpg new file mode 100644 index 0000000..ac50387 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00258.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00259.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00259.jpg new file mode 100644 index 0000000..f7efeda Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00259.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00260.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00260.jpg new file mode 100644 index 0000000..56dbaaa Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00260.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00261.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00261.jpg new file mode 100644 index 0000000..4a10fdc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00261.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00262.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00262.jpg new file mode 100644 index 0000000..ca808c1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00262.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00263.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00263.jpg new file mode 100644 index 0000000..e0d651e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00263.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00264.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00264.jpg new file mode 100644 index 0000000..4d7fd6d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00264.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00265.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00265.jpg new file mode 100644 index 0000000..e1cd52e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00265.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00266.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00266.jpg new file mode 100644 index 0000000..079960d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00266.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00267.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00267.jpg new file mode 100644 index 0000000..b7ba63f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00267.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00268.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00268.jpg new file mode 100644 index 0000000..1251325 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00268.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00269.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00269.jpg new file mode 100644 index 0000000..b591544 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00269.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00270.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00270.jpg new file mode 100644 index 0000000..6a072d6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00270.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00271.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00271.jpg new file mode 100644 index 0000000..ed4fa3a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00271.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00272.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00272.jpg new file mode 100644 index 0000000..78f1e7c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00272.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00273.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00273.jpg new file mode 100644 index 0000000..b4fc719 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00273.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00274.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00274.jpg new file mode 100644 index 0000000..1f0cffd Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00274.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00275.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00275.jpg new file mode 100644 index 0000000..9b17fe0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00275.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00276.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00276.jpg new file mode 100644 index 0000000..a3e9b1e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00276.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00277.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00277.jpg new file mode 100644 index 0000000..c5410f4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00277.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00278.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00278.jpg new file mode 100644 index 0000000..690f748 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00278.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00279.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00279.jpg new file mode 100644 index 0000000..da77755 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00279.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00280.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00280.jpg new file mode 100644 index 0000000..1f3b926 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00280.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00281.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00281.jpg new file mode 100644 index 0000000..198a265 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00281.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00282.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00282.jpg new file mode 100644 index 0000000..bd9d675 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00282.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00283.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00283.jpg new file mode 100644 index 0000000..9ff844d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00283.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00284.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00284.jpg new file mode 100644 index 0000000..d42160e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00284.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00285.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00285.jpg new file mode 100644 index 0000000..1f067c6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00285.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00286.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00286.jpg new file mode 100644 index 0000000..a0bbb89 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00286.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00287.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00287.jpg new file mode 100644 index 0000000..671f468 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00287.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00288.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00288.jpg new file mode 100644 index 0000000..6dd9f37 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00288.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00289.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00289.jpg new file mode 100644 index 0000000..2997302 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00289.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00290.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00290.jpg new file mode 100644 index 0000000..0491cf7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00290.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00291.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00291.jpg new file mode 100644 index 0000000..880556a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00291.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00292.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00292.jpg new file mode 100644 index 0000000..bdae8cf Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00292.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00293.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00293.jpg new file mode 100644 index 0000000..640b4b3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00293.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00294.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00294.jpg new file mode 100644 index 0000000..094067b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00294.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00295.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00295.jpg new file mode 100644 index 0000000..c67e26c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00295.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00296.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00296.jpg new file mode 100644 index 0000000..4eb5940 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00296.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00297.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00297.jpg new file mode 100644 index 0000000..824dbbd Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00297.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00298.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00298.jpg new file mode 100644 index 0000000..54b09b7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00298.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00299.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00299.jpg new file mode 100644 index 0000000..c454d95 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00299.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00300.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00300.jpg new file mode 100644 index 0000000..638655a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00300.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00301.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00301.jpg new file mode 100644 index 0000000..ef24327 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00301.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00302.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00302.jpg new file mode 100644 index 0000000..293fb34 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00302.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00303.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00303.jpg new file mode 100644 index 0000000..90a854c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00303.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00304.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00304.jpg new file mode 100644 index 0000000..c145a1b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00304.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00305.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00305.jpg new file mode 100644 index 0000000..b342ccd Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00305.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00306.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00306.jpg new file mode 100644 index 0000000..729cedd Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00306.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00307.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00307.jpg new file mode 100644 index 0000000..7a69874 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00307.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00308.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00308.jpg new file mode 100644 index 0000000..3df1306 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00308.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00309.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00309.jpg new file mode 100644 index 0000000..3a62563 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00309.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00310.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00310.jpg new file mode 100644 index 0000000..7c1e64c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00310.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00311.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00311.jpg new file mode 100644 index 0000000..9e822a1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00311.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00312.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00312.jpg new file mode 100644 index 0000000..8ed9ff6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00312.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00313.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00313.jpg new file mode 100644 index 0000000..f001b2a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00313.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00314.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00314.jpg new file mode 100644 index 0000000..5211add Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00314.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00315.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00315.jpg new file mode 100644 index 0000000..6be1234 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00315.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00316.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00316.jpg new file mode 100644 index 0000000..192c2d7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00316.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00317.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00317.jpg new file mode 100644 index 0000000..5f047e3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00317.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00318.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00318.jpg new file mode 100644 index 0000000..bd3f3cc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00318.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00319.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00319.jpg new file mode 100644 index 0000000..7c799f1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00319.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00320.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00320.jpg new file mode 100644 index 0000000..3a48006 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00320.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00321.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00321.jpg new file mode 100644 index 0000000..3d3c1f4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00321.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00322.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00322.jpg new file mode 100644 index 0000000..e94bbf5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00322.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00323.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00323.jpg new file mode 100644 index 0000000..7b252d4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00323.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00324.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00324.jpg new file mode 100644 index 0000000..964f3ec Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00324.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00325.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00325.jpg new file mode 100644 index 0000000..b73d7e6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00325.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00326.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00326.jpg new file mode 100644 index 0000000..260bfbc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00326.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00327.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00327.jpg new file mode 100644 index 0000000..ec90f2d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00327.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00328.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00328.jpg new file mode 100644 index 0000000..8d211e5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00328.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00329.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00329.jpg new file mode 100644 index 0000000..bc80f46 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00329.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00330.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00330.jpg new file mode 100644 index 0000000..a104189 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00330.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00331.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00331.jpg new file mode 100644 index 0000000..9225d59 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00331.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00332.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00332.jpg new file mode 100644 index 0000000..d96b8ed Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00332.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00333.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00333.jpg new file mode 100644 index 0000000..04b9d5d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00333.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00334.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00334.jpg new file mode 100644 index 0000000..1b2b127 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00334.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00335.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00335.jpg new file mode 100644 index 0000000..b72a8a7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00335.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00336.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00336.jpg new file mode 100644 index 0000000..5c27c22 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00336.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00337.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00337.jpg new file mode 100644 index 0000000..714760c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00337.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00338.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00338.jpg new file mode 100644 index 0000000..9a0355d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00338.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00339.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00339.jpg new file mode 100644 index 0000000..4ee5256 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00339.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00340.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00340.jpg new file mode 100644 index 0000000..0f1dda5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00340.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00341.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00341.jpg new file mode 100644 index 0000000..3445ceb Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00341.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00342.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00342.jpg new file mode 100644 index 0000000..0088b65 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00342.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00343.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00343.jpg new file mode 100644 index 0000000..e035a21 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00343.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00344.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00344.jpg new file mode 100644 index 0000000..343b27b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00344.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00345.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00345.jpg new file mode 100644 index 0000000..486f9e9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00345.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00346.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00346.jpg new file mode 100644 index 0000000..8572f38 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00346.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00347.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00347.jpg new file mode 100644 index 0000000..d9629cd Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00347.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00348.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00348.jpg new file mode 100644 index 0000000..c1f1616 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00348.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00349.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00349.jpg new file mode 100644 index 0000000..d44c67e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00349.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00350.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00350.jpg new file mode 100644 index 0000000..e6538b0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00350.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00351.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00351.jpg new file mode 100644 index 0000000..3890551 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00351.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00352.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00352.jpg new file mode 100644 index 0000000..8c4b5d6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00352.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00353.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00353.jpg new file mode 100644 index 0000000..ae415df Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00353.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00354.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00354.jpg new file mode 100644 index 0000000..731a3d8 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00354.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00355.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00355.jpg new file mode 100644 index 0000000..b5df1b4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00355.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00356.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00356.jpg new file mode 100644 index 0000000..7e4e351 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00356.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00357.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00357.jpg new file mode 100644 index 0000000..ad1af3f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00357.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00358.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00358.jpg new file mode 100644 index 0000000..f10344d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00358.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00359.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00359.jpg new file mode 100644 index 0000000..528f8a3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00359.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00360.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00360.jpg new file mode 100644 index 0000000..e24008f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00360.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00361.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00361.jpg new file mode 100644 index 0000000..820ad4b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00361.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00362.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00362.jpg new file mode 100644 index 0000000..7e1e66a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00362.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00363.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00363.jpg new file mode 100644 index 0000000..e523aa9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00363.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00364.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00364.jpg new file mode 100644 index 0000000..b80e153 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00364.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00365.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00365.jpg new file mode 100644 index 0000000..f32f292 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00365.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00366.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00366.jpg new file mode 100644 index 0000000..712ba0e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00366.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00367.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00367.jpg new file mode 100644 index 0000000..a0a6b7d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00367.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00368.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00368.jpg new file mode 100644 index 0000000..01e6684 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00368.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00369.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00369.jpg new file mode 100644 index 0000000..0b468b8 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00369.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00370.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00370.jpg new file mode 100644 index 0000000..9da4b2f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00370.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00371.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00371.jpg new file mode 100644 index 0000000..b5c1c12 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00371.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00372.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00372.jpg new file mode 100644 index 0000000..5f6285e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00372.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00373.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00373.jpg new file mode 100644 index 0000000..3ff4b46 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00373.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00374.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00374.jpg new file mode 100644 index 0000000..26c45b4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00374.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00375.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00375.jpg new file mode 100644 index 0000000..19d8fcb Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00375.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00376.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00376.jpg new file mode 100644 index 0000000..595a600 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00376.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00377.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00377.jpg new file mode 100644 index 0000000..607ae18 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00377.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00378.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00378.jpg new file mode 100644 index 0000000..d02cdca Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00378.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00379.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00379.jpg new file mode 100644 index 0000000..8763f4b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00379.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00380.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00380.jpg new file mode 100644 index 0000000..a881c80 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00380.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00381.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00381.jpg new file mode 100644 index 0000000..3ecb466 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00381.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00382.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00382.jpg new file mode 100644 index 0000000..73ca902 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00382.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00383.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00383.jpg new file mode 100644 index 0000000..15d3b4d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00383.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00384.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00384.jpg new file mode 100644 index 0000000..d591cc2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00384.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00385.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00385.jpg new file mode 100644 index 0000000..0808aeb Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00385.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00386.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00386.jpg new file mode 100644 index 0000000..c8af4fb Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00386.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00387.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00387.jpg new file mode 100644 index 0000000..b6d2ba3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00387.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00388.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00388.jpg new file mode 100644 index 0000000..a2b535d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00388.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00389.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00389.jpg new file mode 100644 index 0000000..7a385cf Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00389.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00390.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00390.jpg new file mode 100644 index 0000000..1d77b17 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00390.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00391.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00391.jpg new file mode 100644 index 0000000..a31e2dc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00391.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00392.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00392.jpg new file mode 100644 index 0000000..569461a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00392.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00393.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00393.jpg new file mode 100644 index 0000000..6266251 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00393.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00394.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00394.jpg new file mode 100644 index 0000000..45008e6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00394.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00395.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00395.jpg new file mode 100644 index 0000000..90722d3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00395.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00396.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00396.jpg new file mode 100644 index 0000000..48cf049 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00396.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00397.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00397.jpg new file mode 100644 index 0000000..ec82d1b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00397.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00398.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00398.jpg new file mode 100644 index 0000000..6efa2e1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00398.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00399.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00399.jpg new file mode 100644 index 0000000..b8d38ce Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00399.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Negative/00400.jpg b/applications/quality_detection/SurfaceCrack/training_data/Negative/00400.jpg new file mode 100644 index 0000000..48080c0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Negative/00400.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00001.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00001.jpg new file mode 100644 index 0000000..311fcac Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00001.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00002.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00002.jpg new file mode 100644 index 0000000..df6c9c8 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00002.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00003.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00003.jpg new file mode 100644 index 0000000..5c8d0d9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00003.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00004.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00004.jpg new file mode 100644 index 0000000..f5cf567 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00004.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00005.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00005.jpg new file mode 100644 index 0000000..e6297f2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00005.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00006.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00006.jpg new file mode 100644 index 0000000..dc08c82 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00006.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00007.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00007.jpg new file mode 100644 index 0000000..886fdd6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00007.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00008.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00008.jpg new file mode 100644 index 0000000..662c1b5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00008.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00009.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00009.jpg new file mode 100644 index 0000000..8273805 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00009.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00010.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00010.jpg new file mode 100644 index 0000000..5eb2eac Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00010.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00011.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00011.jpg new file mode 100644 index 0000000..cc793ed Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00011.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00012.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00012.jpg new file mode 100644 index 0000000..70f4d51 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00012.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00013.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00013.jpg new file mode 100644 index 0000000..a588449 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00013.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00014.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00014.jpg new file mode 100644 index 0000000..ea49922 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00014.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00015.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00015.jpg new file mode 100644 index 0000000..2e87f9e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00015.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00016.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00016.jpg new file mode 100644 index 0000000..03bf509 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00016.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00017.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00017.jpg new file mode 100644 index 0000000..1da5d33 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00017.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00018.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00018.jpg new file mode 100644 index 0000000..41fd359 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00018.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00019.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00019.jpg new file mode 100644 index 0000000..506ec7e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00019.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00020.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00020.jpg new file mode 100644 index 0000000..ed7fbd5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00020.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00021.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00021.jpg new file mode 100644 index 0000000..62db505 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00021.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00022.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00022.jpg new file mode 100644 index 0000000..e4bfcb5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00022.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00023.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00023.jpg new file mode 100644 index 0000000..8677172 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00023.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00024.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00024.jpg new file mode 100644 index 0000000..5390223 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00024.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00025.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00025.jpg new file mode 100644 index 0000000..b7cbfce Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00025.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00026.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00026.jpg new file mode 100644 index 0000000..539e13b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00026.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00027.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00027.jpg new file mode 100644 index 0000000..ed61f90 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00027.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00028.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00028.jpg new file mode 100644 index 0000000..7e07db2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00028.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00029.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00029.jpg new file mode 100644 index 0000000..b330009 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00029.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00030.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00030.jpg new file mode 100644 index 0000000..7b7e30c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00030.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00031.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00031.jpg new file mode 100644 index 0000000..8033fea Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00031.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00032.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00032.jpg new file mode 100644 index 0000000..fd6c89e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00032.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00033.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00033.jpg new file mode 100644 index 0000000..c82de3f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00033.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00034.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00034.jpg new file mode 100644 index 0000000..24b3217 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00034.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00035.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00035.jpg new file mode 100644 index 0000000..d0d15e4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00035.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00036.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00036.jpg new file mode 100644 index 0000000..2abac5f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00036.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00037.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00037.jpg new file mode 100644 index 0000000..21b2b4e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00037.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00038.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00038.jpg new file mode 100644 index 0000000..7101d76 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00038.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00039.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00039.jpg new file mode 100644 index 0000000..c220299 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00039.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00040.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00040.jpg new file mode 100644 index 0000000..0b99a9a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00040.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00041.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00041.jpg new file mode 100644 index 0000000..7d7906d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00041.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00042.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00042.jpg new file mode 100644 index 0000000..31af8dd Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00042.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00043.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00043.jpg new file mode 100644 index 0000000..3025999 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00043.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00044.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00044.jpg new file mode 100644 index 0000000..2291ba5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00044.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00045.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00045.jpg new file mode 100644 index 0000000..ec3091c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00045.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00046.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00046.jpg new file mode 100644 index 0000000..db24584 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00046.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00047.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00047.jpg new file mode 100644 index 0000000..3384d51 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00047.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00048.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00048.jpg new file mode 100644 index 0000000..adc5d44 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00048.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00049.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00049.jpg new file mode 100644 index 0000000..0d7cb1a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00049.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00050.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00050.jpg new file mode 100644 index 0000000..59451ee Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00050.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00051.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00051.jpg new file mode 100644 index 0000000..dd19cea Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00051.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00052.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00052.jpg new file mode 100644 index 0000000..a49aef2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00052.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00053.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00053.jpg new file mode 100644 index 0000000..1a24271 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00053.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00054.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00054.jpg new file mode 100644 index 0000000..5a64933 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00054.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00055.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00055.jpg new file mode 100644 index 0000000..c7ba6c0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00055.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00056.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00056.jpg new file mode 100644 index 0000000..0032230 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00056.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00057.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00057.jpg new file mode 100644 index 0000000..d4c4a5f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00057.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00058.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00058.jpg new file mode 100644 index 0000000..ce6871e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00058.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00059.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00059.jpg new file mode 100644 index 0000000..f3869ec Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00059.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00060.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00060.jpg new file mode 100644 index 0000000..4d2b55f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00060.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00061.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00061.jpg new file mode 100644 index 0000000..005b170 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00061.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00062.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00062.jpg new file mode 100644 index 0000000..681764c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00062.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00063.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00063.jpg new file mode 100644 index 0000000..fe85455 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00063.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00064.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00064.jpg new file mode 100644 index 0000000..6b4c799 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00064.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00065.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00065.jpg new file mode 100644 index 0000000..e103f3a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00065.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00066.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00066.jpg new file mode 100644 index 0000000..71b673c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00066.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00067.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00067.jpg new file mode 100644 index 0000000..4824273 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00067.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00068.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00068.jpg new file mode 100644 index 0000000..f2a8243 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00068.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00069.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00069.jpg new file mode 100644 index 0000000..12c4d72 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00069.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00070.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00070.jpg new file mode 100644 index 0000000..78fb871 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00070.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00071.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00071.jpg new file mode 100644 index 0000000..22ee13e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00071.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00072.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00072.jpg new file mode 100644 index 0000000..ab6cb72 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00072.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00073.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00073.jpg new file mode 100644 index 0000000..0e8d580 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00073.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00074.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00074.jpg new file mode 100644 index 0000000..4e7c3e1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00074.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00075.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00075.jpg new file mode 100644 index 0000000..c0d28ec Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00075.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00076.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00076.jpg new file mode 100644 index 0000000..f0a38cb Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00076.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00077.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00077.jpg new file mode 100644 index 0000000..590584c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00077.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00078.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00078.jpg new file mode 100644 index 0000000..af1b15d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00078.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00079.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00079.jpg new file mode 100644 index 0000000..3ca15f1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00079.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00080.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00080.jpg new file mode 100644 index 0000000..3d90351 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00080.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00081.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00081.jpg new file mode 100644 index 0000000..9352354 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00081.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00082.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00082.jpg new file mode 100644 index 0000000..4552ab7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00082.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00083.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00083.jpg new file mode 100644 index 0000000..f677496 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00083.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00084.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00084.jpg new file mode 100644 index 0000000..cde6821 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00084.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00085.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00085.jpg new file mode 100644 index 0000000..4622472 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00085.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00086.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00086.jpg new file mode 100644 index 0000000..3a2560e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00086.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00087.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00087.jpg new file mode 100644 index 0000000..91eb401 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00087.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00088.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00088.jpg new file mode 100644 index 0000000..05f2903 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00088.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00089.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00089.jpg new file mode 100644 index 0000000..ef247cc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00089.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00090.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00090.jpg new file mode 100644 index 0000000..2fa936e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00090.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00091.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00091.jpg new file mode 100644 index 0000000..37e93a7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00091.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00092.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00092.jpg new file mode 100644 index 0000000..ba2cdc5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00092.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00093.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00093.jpg new file mode 100644 index 0000000..9cbf88e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00093.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00094.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00094.jpg new file mode 100644 index 0000000..0b6b02f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00094.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00095.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00095.jpg new file mode 100644 index 0000000..3e4a854 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00095.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00096.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00096.jpg new file mode 100644 index 0000000..4774219 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00096.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00097.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00097.jpg new file mode 100644 index 0000000..ac24474 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00097.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00098.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00098.jpg new file mode 100644 index 0000000..70cde04 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00098.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00099.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00099.jpg new file mode 100644 index 0000000..9aa3ed0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00099.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00100.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00100.jpg new file mode 100644 index 0000000..0f42674 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00100.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00101.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00101.jpg new file mode 100644 index 0000000..5ea57de Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00101.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00102.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00102.jpg new file mode 100644 index 0000000..e719f4a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00102.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00103.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00103.jpg new file mode 100644 index 0000000..4d5ab50 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00103.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00104.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00104.jpg new file mode 100644 index 0000000..c1acd7b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00104.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00105.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00105.jpg new file mode 100644 index 0000000..172ffd8 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00105.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00106.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00106.jpg new file mode 100644 index 0000000..33471cf Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00106.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00107.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00107.jpg new file mode 100644 index 0000000..a262d0c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00107.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00108.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00108.jpg new file mode 100644 index 0000000..f76e312 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00108.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00109.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00109.jpg new file mode 100644 index 0000000..5d94b3e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00109.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00110.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00110.jpg new file mode 100644 index 0000000..1ef6442 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00110.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00111.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00111.jpg new file mode 100644 index 0000000..784616f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00111.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00112.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00112.jpg new file mode 100644 index 0000000..5136d4f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00112.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00113.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00113.jpg new file mode 100644 index 0000000..c9c1093 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00113.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00114.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00114.jpg new file mode 100644 index 0000000..500a87b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00114.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00115.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00115.jpg new file mode 100644 index 0000000..f31a220 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00115.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00116.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00116.jpg new file mode 100644 index 0000000..7b49a44 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00116.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00117.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00117.jpg new file mode 100644 index 0000000..997948a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00117.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00118.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00118.jpg new file mode 100644 index 0000000..074272d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00118.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00119.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00119.jpg new file mode 100644 index 0000000..0b38bb6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00119.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00120.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00120.jpg new file mode 100644 index 0000000..66623ab Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00120.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00121.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00121.jpg new file mode 100644 index 0000000..609fee6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00121.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00122.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00122.jpg new file mode 100644 index 0000000..20e2dc0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00122.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00123.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00123.jpg new file mode 100644 index 0000000..ba558cf Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00123.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00124.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00124.jpg new file mode 100644 index 0000000..e67362d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00124.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00125.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00125.jpg new file mode 100644 index 0000000..e04b86e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00125.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00126.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00126.jpg new file mode 100644 index 0000000..2f67d49 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00126.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00127.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00127.jpg new file mode 100644 index 0000000..204f5d9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00127.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00128.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00128.jpg new file mode 100644 index 0000000..ef0b4c6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00128.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00129.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00129.jpg new file mode 100644 index 0000000..8b5fec7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00129.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00130.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00130.jpg new file mode 100644 index 0000000..c4bf603 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00130.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00131.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00131.jpg new file mode 100644 index 0000000..3b04675 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00131.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00132.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00132.jpg new file mode 100644 index 0000000..2838813 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00132.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00133.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00133.jpg new file mode 100644 index 0000000..3f16828 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00133.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00134.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00134.jpg new file mode 100644 index 0000000..26679c3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00134.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00135.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00135.jpg new file mode 100644 index 0000000..7f95417 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00135.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00136.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00136.jpg new file mode 100644 index 0000000..efb33df Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00136.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00137.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00137.jpg new file mode 100644 index 0000000..080a8bc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00137.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00138.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00138.jpg new file mode 100644 index 0000000..aba1965 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00138.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00139.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00139.jpg new file mode 100644 index 0000000..a613c7f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00139.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00140.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00140.jpg new file mode 100644 index 0000000..b80e437 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00140.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00141.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00141.jpg new file mode 100644 index 0000000..108d147 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00141.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00142.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00142.jpg new file mode 100644 index 0000000..b7f36c6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00142.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00143.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00143.jpg new file mode 100644 index 0000000..23b8292 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00143.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00144.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00144.jpg new file mode 100644 index 0000000..16c6363 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00144.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00145.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00145.jpg new file mode 100644 index 0000000..73a17de Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00145.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00146.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00146.jpg new file mode 100644 index 0000000..dc55d9c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00146.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00147.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00147.jpg new file mode 100644 index 0000000..ae3252b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00147.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00148.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00148.jpg new file mode 100644 index 0000000..95012cb Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00148.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00149.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00149.jpg new file mode 100644 index 0000000..d6de016 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00149.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00150.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00150.jpg new file mode 100644 index 0000000..757f71c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00150.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00151.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00151.jpg new file mode 100644 index 0000000..7d262fb Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00151.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00152.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00152.jpg new file mode 100644 index 0000000..86d721f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00152.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00153.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00153.jpg new file mode 100644 index 0000000..a8439cd Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00153.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00154.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00154.jpg new file mode 100644 index 0000000..dc0b8bd Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00154.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00155.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00155.jpg new file mode 100644 index 0000000..ba3a2b0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00155.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00156.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00156.jpg new file mode 100644 index 0000000..ca0007e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00156.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00157.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00157.jpg new file mode 100644 index 0000000..9c39bd2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00157.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00158.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00158.jpg new file mode 100644 index 0000000..cd9d4d6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00158.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00159.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00159.jpg new file mode 100644 index 0000000..d821928 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00159.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00160.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00160.jpg new file mode 100644 index 0000000..015d317 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00160.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00161.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00161.jpg new file mode 100644 index 0000000..7e1d054 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00161.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00162.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00162.jpg new file mode 100644 index 0000000..cf9199d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00162.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00163.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00163.jpg new file mode 100644 index 0000000..89b520f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00163.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00164.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00164.jpg new file mode 100644 index 0000000..88a79a2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00164.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00165.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00165.jpg new file mode 100644 index 0000000..3e5e3e9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00165.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00166.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00166.jpg new file mode 100644 index 0000000..ca1d51d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00166.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00167.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00167.jpg new file mode 100644 index 0000000..f278e1e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00167.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00168.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00168.jpg new file mode 100644 index 0000000..ee996c9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00168.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00169.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00169.jpg new file mode 100644 index 0000000..c9a2f4c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00169.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00170.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00170.jpg new file mode 100644 index 0000000..5913ea4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00170.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00171.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00171.jpg new file mode 100644 index 0000000..c1f2cb4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00171.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00172.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00172.jpg new file mode 100644 index 0000000..b0e88e7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00172.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00173.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00173.jpg new file mode 100644 index 0000000..9a9ac43 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00173.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00174.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00174.jpg new file mode 100644 index 0000000..75929a5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00174.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00175.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00175.jpg new file mode 100644 index 0000000..f8408cb Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00175.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00176.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00176.jpg new file mode 100644 index 0000000..25ff541 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00176.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00177.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00177.jpg new file mode 100644 index 0000000..83bbf41 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00177.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00178.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00178.jpg new file mode 100644 index 0000000..c7e072c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00178.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00179.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00179.jpg new file mode 100644 index 0000000..e8e545e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00179.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00180.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00180.jpg new file mode 100644 index 0000000..01e1724 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00180.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00181.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00181.jpg new file mode 100644 index 0000000..0738e3a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00181.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00182.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00182.jpg new file mode 100644 index 0000000..7e766cf Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00182.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00183.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00183.jpg new file mode 100644 index 0000000..faa1973 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00183.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00184.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00184.jpg new file mode 100644 index 0000000..694f4ea Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00184.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00185.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00185.jpg new file mode 100644 index 0000000..c03862b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00185.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00186.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00186.jpg new file mode 100644 index 0000000..44c8985 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00186.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00187.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00187.jpg new file mode 100644 index 0000000..bfcf2cd Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00187.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00188.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00188.jpg new file mode 100644 index 0000000..80c0665 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00188.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00189.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00189.jpg new file mode 100644 index 0000000..69ba8a8 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00189.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00190.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00190.jpg new file mode 100644 index 0000000..da6ba75 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00190.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00191.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00191.jpg new file mode 100644 index 0000000..5af48a7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00191.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00192.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00192.jpg new file mode 100644 index 0000000..91da991 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00192.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00193.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00193.jpg new file mode 100644 index 0000000..5ea205f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00193.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00194.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00194.jpg new file mode 100644 index 0000000..4116bde Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00194.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00195.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00195.jpg new file mode 100644 index 0000000..c5f0d95 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00195.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00196.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00196.jpg new file mode 100644 index 0000000..1dba90f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00196.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00197.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00197.jpg new file mode 100644 index 0000000..947ae0f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00197.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00198.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00198.jpg new file mode 100644 index 0000000..06e4f21 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00198.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00199.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00199.jpg new file mode 100644 index 0000000..f3c6dfe Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00199.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00200.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00200.jpg new file mode 100644 index 0000000..7ec5d53 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00200.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00201.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00201.jpg new file mode 100644 index 0000000..1139331 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00201.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00202.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00202.jpg new file mode 100644 index 0000000..302a8c5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00202.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00203.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00203.jpg new file mode 100644 index 0000000..ef69569 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00203.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00204.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00204.jpg new file mode 100644 index 0000000..b5962b3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00204.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00205.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00205.jpg new file mode 100644 index 0000000..0ace235 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00205.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00206.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00206.jpg new file mode 100644 index 0000000..aac43ba Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00206.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00207.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00207.jpg new file mode 100644 index 0000000..3c7c91a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00207.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00208.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00208.jpg new file mode 100644 index 0000000..649bbbc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00208.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00209.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00209.jpg new file mode 100644 index 0000000..f82d128 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00209.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00210.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00210.jpg new file mode 100644 index 0000000..e345771 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00210.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00211.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00211.jpg new file mode 100644 index 0000000..60398fe Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00211.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00212.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00212.jpg new file mode 100644 index 0000000..054b5ed Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00212.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00213.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00213.jpg new file mode 100644 index 0000000..1357bef Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00213.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00214.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00214.jpg new file mode 100644 index 0000000..97ffe43 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00214.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00215.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00215.jpg new file mode 100644 index 0000000..c7d67ed Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00215.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00216.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00216.jpg new file mode 100644 index 0000000..1a6faac Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00216.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00217.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00217.jpg new file mode 100644 index 0000000..f72b345 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00217.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00218.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00218.jpg new file mode 100644 index 0000000..5afe1f1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00218.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00219.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00219.jpg new file mode 100644 index 0000000..042f4ce Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00219.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00220.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00220.jpg new file mode 100644 index 0000000..89fda99 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00220.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00221.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00221.jpg new file mode 100644 index 0000000..6aa0014 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00221.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00222.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00222.jpg new file mode 100644 index 0000000..199261e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00222.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00223.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00223.jpg new file mode 100644 index 0000000..02a61e1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00223.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00224.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00224.jpg new file mode 100644 index 0000000..e3abe53 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00224.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00225.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00225.jpg new file mode 100644 index 0000000..63128b6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00225.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00226.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00226.jpg new file mode 100644 index 0000000..e2b9b0a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00226.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00227.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00227.jpg new file mode 100644 index 0000000..ec783ef Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00227.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00228.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00228.jpg new file mode 100644 index 0000000..544118d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00228.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00229.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00229.jpg new file mode 100644 index 0000000..462bb7e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00229.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00230.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00230.jpg new file mode 100644 index 0000000..5a7fe0d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00230.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00231.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00231.jpg new file mode 100644 index 0000000..2a54d66 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00231.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00232.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00232.jpg new file mode 100644 index 0000000..775a2f8 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00232.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00233.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00233.jpg new file mode 100644 index 0000000..0723725 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00233.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00234.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00234.jpg new file mode 100644 index 0000000..489d247 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00234.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00235.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00235.jpg new file mode 100644 index 0000000..254fe2f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00235.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00236.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00236.jpg new file mode 100644 index 0000000..c25f365 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00236.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00237.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00237.jpg new file mode 100644 index 0000000..8a2bdb9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00237.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00238.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00238.jpg new file mode 100644 index 0000000..19d2239 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00238.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00239.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00239.jpg new file mode 100644 index 0000000..e61f084 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00239.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00240.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00240.jpg new file mode 100644 index 0000000..92d42d5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00240.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00241.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00241.jpg new file mode 100644 index 0000000..f3299d4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00241.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00242.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00242.jpg new file mode 100644 index 0000000..23d3409 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00242.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00243.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00243.jpg new file mode 100644 index 0000000..e7044fe Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00243.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00244.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00244.jpg new file mode 100644 index 0000000..7324ee5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00244.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00245.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00245.jpg new file mode 100644 index 0000000..224c277 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00245.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00246.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00246.jpg new file mode 100644 index 0000000..6a9129f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00246.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00247.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00247.jpg new file mode 100644 index 0000000..384e79f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00247.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00248.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00248.jpg new file mode 100644 index 0000000..ae39139 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00248.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00249.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00249.jpg new file mode 100644 index 0000000..7019378 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00249.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00250.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00250.jpg new file mode 100644 index 0000000..544287f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00250.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00251.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00251.jpg new file mode 100644 index 0000000..777f49f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00251.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00252.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00252.jpg new file mode 100644 index 0000000..51db722 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00252.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00253.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00253.jpg new file mode 100644 index 0000000..18677a5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00253.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00254.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00254.jpg new file mode 100644 index 0000000..4b9f534 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00254.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00255.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00255.jpg new file mode 100644 index 0000000..50e724e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00255.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00256.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00256.jpg new file mode 100644 index 0000000..ab69a4f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00256.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00257.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00257.jpg new file mode 100644 index 0000000..afbae26 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00257.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00258.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00258.jpg new file mode 100644 index 0000000..09cacc7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00258.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00259.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00259.jpg new file mode 100644 index 0000000..f0ab093 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00259.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00260.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00260.jpg new file mode 100644 index 0000000..25befd9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00260.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00261.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00261.jpg new file mode 100644 index 0000000..3352421 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00261.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00262.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00262.jpg new file mode 100644 index 0000000..a4fdcdb Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00262.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00263.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00263.jpg new file mode 100644 index 0000000..e421f0f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00263.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00264.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00264.jpg new file mode 100644 index 0000000..1a27bd6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00264.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00265.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00265.jpg new file mode 100644 index 0000000..bdaa59d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00265.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00266.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00266.jpg new file mode 100644 index 0000000..a243f1e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00266.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00267.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00267.jpg new file mode 100644 index 0000000..b8a3a20 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00267.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00268.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00268.jpg new file mode 100644 index 0000000..9e097da Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00268.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00269.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00269.jpg new file mode 100644 index 0000000..128bd99 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00269.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00270.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00270.jpg new file mode 100644 index 0000000..98f8d31 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00270.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00271.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00271.jpg new file mode 100644 index 0000000..e548835 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00271.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00272.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00272.jpg new file mode 100644 index 0000000..ed86ec2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00272.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00273.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00273.jpg new file mode 100644 index 0000000..745d404 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00273.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00274.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00274.jpg new file mode 100644 index 0000000..517c376 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00274.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00275.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00275.jpg new file mode 100644 index 0000000..23a126a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00275.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00276.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00276.jpg new file mode 100644 index 0000000..5be881f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00276.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00277.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00277.jpg new file mode 100644 index 0000000..a4bf0be Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00277.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00278.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00278.jpg new file mode 100644 index 0000000..358257f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00278.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00279.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00279.jpg new file mode 100644 index 0000000..2b256c3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00279.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00280.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00280.jpg new file mode 100644 index 0000000..35440f5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00280.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00281.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00281.jpg new file mode 100644 index 0000000..70f1d90 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00281.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00282.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00282.jpg new file mode 100644 index 0000000..349a319 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00282.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00283.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00283.jpg new file mode 100644 index 0000000..729a91d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00283.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00284.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00284.jpg new file mode 100644 index 0000000..3362a9e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00284.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00285.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00285.jpg new file mode 100644 index 0000000..844e5f5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00285.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00286.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00286.jpg new file mode 100644 index 0000000..1b987d2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00286.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00287.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00287.jpg new file mode 100644 index 0000000..d21e8b0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00287.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00288.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00288.jpg new file mode 100644 index 0000000..57226f3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00288.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00289.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00289.jpg new file mode 100644 index 0000000..9d555a4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00289.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00290.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00290.jpg new file mode 100644 index 0000000..b6f3432 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00290.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00291.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00291.jpg new file mode 100644 index 0000000..5d9bb3a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00291.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00292.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00292.jpg new file mode 100644 index 0000000..8535274 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00292.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00293.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00293.jpg new file mode 100644 index 0000000..c7a4e88 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00293.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00294.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00294.jpg new file mode 100644 index 0000000..e455813 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00294.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00295.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00295.jpg new file mode 100644 index 0000000..13fc1dc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00295.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00296.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00296.jpg new file mode 100644 index 0000000..04dbc55 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00296.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00297.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00297.jpg new file mode 100644 index 0000000..352552f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00297.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00298.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00298.jpg new file mode 100644 index 0000000..75e9b2c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00298.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00299.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00299.jpg new file mode 100644 index 0000000..91d17a4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00299.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00300.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00300.jpg new file mode 100644 index 0000000..d6240d8 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00300.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00301.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00301.jpg new file mode 100644 index 0000000..43fbd2e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00301.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00302.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00302.jpg new file mode 100644 index 0000000..7a06873 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00302.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00303.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00303.jpg new file mode 100644 index 0000000..9d15a46 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00303.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00304.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00304.jpg new file mode 100644 index 0000000..78927ed Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00304.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00305.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00305.jpg new file mode 100644 index 0000000..2056c09 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00305.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00306.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00306.jpg new file mode 100644 index 0000000..df7a656 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00306.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00307.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00307.jpg new file mode 100644 index 0000000..87ab080 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00307.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00308.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00308.jpg new file mode 100644 index 0000000..5d41a4f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00308.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00309.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00309.jpg new file mode 100644 index 0000000..1c656a9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00309.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00310.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00310.jpg new file mode 100644 index 0000000..5c3d2f4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00310.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00311.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00311.jpg new file mode 100644 index 0000000..84a759a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00311.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00312.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00312.jpg new file mode 100644 index 0000000..0262190 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00312.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00313.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00313.jpg new file mode 100644 index 0000000..b355386 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00313.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00314.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00314.jpg new file mode 100644 index 0000000..fb48697 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00314.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00315.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00315.jpg new file mode 100644 index 0000000..fb26830 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00315.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00316.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00316.jpg new file mode 100644 index 0000000..859cbba Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00316.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00317.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00317.jpg new file mode 100644 index 0000000..b40f68a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00317.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00318.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00318.jpg new file mode 100644 index 0000000..4749586 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00318.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00319.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00319.jpg new file mode 100644 index 0000000..9ed1d41 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00319.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00320.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00320.jpg new file mode 100644 index 0000000..b6a1f52 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00320.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00321.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00321.jpg new file mode 100644 index 0000000..1979b35 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00321.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00322.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00322.jpg new file mode 100644 index 0000000..dfff078 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00322.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00323.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00323.jpg new file mode 100644 index 0000000..659142c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00323.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00324.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00324.jpg new file mode 100644 index 0000000..a8264df Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00324.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00325.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00325.jpg new file mode 100644 index 0000000..06e2b58 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00325.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00326.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00326.jpg new file mode 100644 index 0000000..29821cf Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00326.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00327.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00327.jpg new file mode 100644 index 0000000..6a44bdf Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00327.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00328.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00328.jpg new file mode 100644 index 0000000..9df0dcb Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00328.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00329.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00329.jpg new file mode 100644 index 0000000..8bd5cfa Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00329.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00330.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00330.jpg new file mode 100644 index 0000000..67c8672 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00330.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00331.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00331.jpg new file mode 100644 index 0000000..56850d9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00331.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00332.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00332.jpg new file mode 100644 index 0000000..98059d5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00332.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00333.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00333.jpg new file mode 100644 index 0000000..f199025 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00333.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00334.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00334.jpg new file mode 100644 index 0000000..c7ce008 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00334.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00335.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00335.jpg new file mode 100644 index 0000000..61ae06f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00335.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00336.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00336.jpg new file mode 100644 index 0000000..779e8c1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00336.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00337.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00337.jpg new file mode 100644 index 0000000..59b6a2d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00337.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00338.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00338.jpg new file mode 100644 index 0000000..d7372f0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00338.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00339.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00339.jpg new file mode 100644 index 0000000..4f4ed5f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00339.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00340.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00340.jpg new file mode 100644 index 0000000..2a0566e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00340.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00341.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00341.jpg new file mode 100644 index 0000000..1a0ba74 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00341.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00342.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00342.jpg new file mode 100644 index 0000000..3cc1efc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00342.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00343.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00343.jpg new file mode 100644 index 0000000..f53d5ab Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00343.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00344.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00344.jpg new file mode 100644 index 0000000..22e70eb Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00344.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00345.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00345.jpg new file mode 100644 index 0000000..5fe6d07 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00345.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00346.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00346.jpg new file mode 100644 index 0000000..261595e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00346.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00347.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00347.jpg new file mode 100644 index 0000000..85ff21c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00347.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00348.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00348.jpg new file mode 100644 index 0000000..415dbb9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00348.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00349.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00349.jpg new file mode 100644 index 0000000..40059c5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00349.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00350.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00350.jpg new file mode 100644 index 0000000..1d7d3c6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00350.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00351.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00351.jpg new file mode 100644 index 0000000..bfef4eb Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00351.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00352.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00352.jpg new file mode 100644 index 0000000..44b455b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00352.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00353.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00353.jpg new file mode 100644 index 0000000..9af9f22 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00353.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00354.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00354.jpg new file mode 100644 index 0000000..30a00f7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00354.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00355.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00355.jpg new file mode 100644 index 0000000..4a4722d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00355.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00356.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00356.jpg new file mode 100644 index 0000000..4a76751 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00356.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00357.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00357.jpg new file mode 100644 index 0000000..1629582 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00357.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00358.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00358.jpg new file mode 100644 index 0000000..e414691 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00358.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00359.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00359.jpg new file mode 100644 index 0000000..2cfa383 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00359.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00360.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00360.jpg new file mode 100644 index 0000000..abc9c35 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00360.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00361.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00361.jpg new file mode 100644 index 0000000..23937db Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00361.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00362.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00362.jpg new file mode 100644 index 0000000..d3f6f24 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00362.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00363.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00363.jpg new file mode 100644 index 0000000..6505c28 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00363.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00364.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00364.jpg new file mode 100644 index 0000000..36faf56 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00364.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00365.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00365.jpg new file mode 100644 index 0000000..04534f0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00365.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00366.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00366.jpg new file mode 100644 index 0000000..d29a7c5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00366.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00367.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00367.jpg new file mode 100644 index 0000000..81aca1c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00367.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00368.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00368.jpg new file mode 100644 index 0000000..58612af Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00368.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00369.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00369.jpg new file mode 100644 index 0000000..64212f9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00369.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00370.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00370.jpg new file mode 100644 index 0000000..9d1c5a4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00370.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00371.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00371.jpg new file mode 100644 index 0000000..202bbb6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00371.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00372.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00372.jpg new file mode 100644 index 0000000..7e47306 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00372.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00373.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00373.jpg new file mode 100644 index 0000000..0a957c5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00373.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00374.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00374.jpg new file mode 100644 index 0000000..bda048d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00374.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00375.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00375.jpg new file mode 100644 index 0000000..0f9009d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00375.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00376.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00376.jpg new file mode 100644 index 0000000..dbcafc5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00376.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00377.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00377.jpg new file mode 100644 index 0000000..3d8b0cf Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00377.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00378.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00378.jpg new file mode 100644 index 0000000..fd4e75d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00378.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00379.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00379.jpg new file mode 100644 index 0000000..030da36 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00379.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00380.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00380.jpg new file mode 100644 index 0000000..db3dcd2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00380.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00381.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00381.jpg new file mode 100644 index 0000000..25e7f61 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00381.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00382.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00382.jpg new file mode 100644 index 0000000..1dbbb6e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00382.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00383.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00383.jpg new file mode 100644 index 0000000..02ff019 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00383.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00384.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00384.jpg new file mode 100644 index 0000000..405fab7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00384.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00385.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00385.jpg new file mode 100644 index 0000000..cf0feba Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00385.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00386.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00386.jpg new file mode 100644 index 0000000..96ed2e2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00386.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00387.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00387.jpg new file mode 100644 index 0000000..7a16f61 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00387.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00388.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00388.jpg new file mode 100644 index 0000000..f514132 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00388.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00389.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00389.jpg new file mode 100644 index 0000000..dea9c70 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00389.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00390.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00390.jpg new file mode 100644 index 0000000..90bb265 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00390.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00391.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00391.jpg new file mode 100644 index 0000000..9ce335b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00391.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00392.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00392.jpg new file mode 100644 index 0000000..581f640 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00392.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00393.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00393.jpg new file mode 100644 index 0000000..b05b1bc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00393.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00394.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00394.jpg new file mode 100644 index 0000000..0b35d58 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00394.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00395.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00395.jpg new file mode 100644 index 0000000..499da95 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00395.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00396.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00396.jpg new file mode 100644 index 0000000..4f6b814 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00396.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00397.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00397.jpg new file mode 100644 index 0000000..da4018f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00397.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00398.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00398.jpg new file mode 100644 index 0000000..baa63f6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00398.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00399.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00399.jpg new file mode 100644 index 0000000..a470c31 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00399.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/training_data/Positive/00400.jpg b/applications/quality_detection/SurfaceCrack/training_data/Positive/00400.jpg new file mode 100644 index 0000000..35203ed Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/training_data/Positive/00400.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00501.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00501.jpg new file mode 100644 index 0000000..d201983 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00501.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00502.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00502.jpg new file mode 100644 index 0000000..9182502 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00502.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00503.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00503.jpg new file mode 100644 index 0000000..0285ca2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00503.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00504.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00504.jpg new file mode 100644 index 0000000..347b624 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00504.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00505.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00505.jpg new file mode 100644 index 0000000..59ceb40 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00505.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00506.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00506.jpg new file mode 100644 index 0000000..2b94264 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00506.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00507.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00507.jpg new file mode 100644 index 0000000..f58971d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00507.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00508.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00508.jpg new file mode 100644 index 0000000..56d84fe Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00508.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00509.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00509.jpg new file mode 100644 index 0000000..4597927 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00509.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00510.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00510.jpg new file mode 100644 index 0000000..1ac96bd Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00510.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00511.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00511.jpg new file mode 100644 index 0000000..d5f54ca Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00511.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00512.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00512.jpg new file mode 100644 index 0000000..dba98fe Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00512.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00513.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00513.jpg new file mode 100644 index 0000000..f6fdf1b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00513.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00514.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00514.jpg new file mode 100644 index 0000000..cfb201f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00514.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00515.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00515.jpg new file mode 100644 index 0000000..ff20707 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00515.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00516.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00516.jpg new file mode 100644 index 0000000..663478c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00516.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00517.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00517.jpg new file mode 100644 index 0000000..3dfa39c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00517.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00518.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00518.jpg new file mode 100644 index 0000000..827c426 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00518.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00519.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00519.jpg new file mode 100644 index 0000000..52a3574 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00519.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00520.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00520.jpg new file mode 100644 index 0000000..881d000 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00520.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00521.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00521.jpg new file mode 100644 index 0000000..3d7dfc0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00521.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00522.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00522.jpg new file mode 100644 index 0000000..0e0b354 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00522.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00523.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00523.jpg new file mode 100644 index 0000000..3678191 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00523.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00524.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00524.jpg new file mode 100644 index 0000000..96c3ba1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00524.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00525.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00525.jpg new file mode 100644 index 0000000..f444cee Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00525.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00526.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00526.jpg new file mode 100644 index 0000000..fdc743e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00526.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00527.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00527.jpg new file mode 100644 index 0000000..867f9d1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00527.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00528.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00528.jpg new file mode 100644 index 0000000..65c5ee6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00528.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00529.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00529.jpg new file mode 100644 index 0000000..4775df9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00529.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00530.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00530.jpg new file mode 100644 index 0000000..fdd999e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00530.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00531.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00531.jpg new file mode 100644 index 0000000..dc3e7ef Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00531.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00532.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00532.jpg new file mode 100644 index 0000000..8279929 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00532.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00533.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00533.jpg new file mode 100644 index 0000000..e2a01e5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00533.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00534.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00534.jpg new file mode 100644 index 0000000..7aa421b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00534.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00535.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00535.jpg new file mode 100644 index 0000000..705053e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00535.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00536.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00536.jpg new file mode 100644 index 0000000..2e3debb Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00536.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00537.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00537.jpg new file mode 100644 index 0000000..9b5e410 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00537.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00538.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00538.jpg new file mode 100644 index 0000000..2a0ead6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00538.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00539.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00539.jpg new file mode 100644 index 0000000..a6b7c06 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00539.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00540.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00540.jpg new file mode 100644 index 0000000..ac50387 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00540.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00541.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00541.jpg new file mode 100644 index 0000000..e660978 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00541.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00542.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00542.jpg new file mode 100644 index 0000000..1672e71 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00542.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00543.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00543.jpg new file mode 100644 index 0000000..b50f093 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00543.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00544.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00544.jpg new file mode 100644 index 0000000..8d9b2c2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00544.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00545.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00545.jpg new file mode 100644 index 0000000..13a8c23 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00545.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00546.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00546.jpg new file mode 100644 index 0000000..91e0fc4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00546.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00547.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00547.jpg new file mode 100644 index 0000000..32777b5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00547.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00548.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00548.jpg new file mode 100644 index 0000000..5a8da5c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00548.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00549.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00549.jpg new file mode 100644 index 0000000..b416521 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00549.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00550.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00550.jpg new file mode 100644 index 0000000..a2feed9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00550.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00551.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00551.jpg new file mode 100644 index 0000000..4c38eac Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00551.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00552.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00552.jpg new file mode 100644 index 0000000..630d175 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00552.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00553.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00553.jpg new file mode 100644 index 0000000..51e591e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00553.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00554.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00554.jpg new file mode 100644 index 0000000..950d366 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00554.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00555.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00555.jpg new file mode 100644 index 0000000..f7d356c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00555.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00556.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00556.jpg new file mode 100644 index 0000000..034af73 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00556.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00557.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00557.jpg new file mode 100644 index 0000000..3ca8621 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00557.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00558.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00558.jpg new file mode 100644 index 0000000..2d51514 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00558.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00559.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00559.jpg new file mode 100644 index 0000000..e15da36 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00559.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00560.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00560.jpg new file mode 100644 index 0000000..ab98d03 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00560.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00561.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00561.jpg new file mode 100644 index 0000000..69f49bd Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00561.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00562.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00562.jpg new file mode 100644 index 0000000..0fc9a34 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00562.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00563.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00563.jpg new file mode 100644 index 0000000..dd76d43 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00563.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00564.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00564.jpg new file mode 100644 index 0000000..ef98a2a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00564.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00565.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00565.jpg new file mode 100644 index 0000000..0883f1f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00565.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00566.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00566.jpg new file mode 100644 index 0000000..3cb5f19 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00566.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00567.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00567.jpg new file mode 100644 index 0000000..a866fa1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00567.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00568.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00568.jpg new file mode 100644 index 0000000..05a3036 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00568.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00569.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00569.jpg new file mode 100644 index 0000000..97845ab Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00569.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00570.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00570.jpg new file mode 100644 index 0000000..0e41d96 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00570.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00571.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00571.jpg new file mode 100644 index 0000000..2be0d4c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00571.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00572.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00572.jpg new file mode 100644 index 0000000..f5e755b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00572.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00573.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00573.jpg new file mode 100644 index 0000000..272886e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00573.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00574.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00574.jpg new file mode 100644 index 0000000..4e645c6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00574.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00575.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00575.jpg new file mode 100644 index 0000000..655408d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00575.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00576.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00576.jpg new file mode 100644 index 0000000..9a5f505 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00576.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00577.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00577.jpg new file mode 100644 index 0000000..18722e1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00577.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00578.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00578.jpg new file mode 100644 index 0000000..be014bb Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00578.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00579.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00579.jpg new file mode 100644 index 0000000..9ff1e60 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00579.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00580.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00580.jpg new file mode 100644 index 0000000..8dc7361 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00580.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00581.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00581.jpg new file mode 100644 index 0000000..d06b1ab Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00581.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00582.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00582.jpg new file mode 100644 index 0000000..4d76244 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00582.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00583.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00583.jpg new file mode 100644 index 0000000..7d286fd Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00583.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00584.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00584.jpg new file mode 100644 index 0000000..4f4a87d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00584.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00585.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00585.jpg new file mode 100644 index 0000000..d6a36fc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00585.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00586.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00586.jpg new file mode 100644 index 0000000..4ec53ba Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00586.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00587.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00587.jpg new file mode 100644 index 0000000..d1c49ac Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00587.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00588.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00588.jpg new file mode 100644 index 0000000..d38b832 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00588.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00589.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00589.jpg new file mode 100644 index 0000000..c001cbc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00589.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00590.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00590.jpg new file mode 100644 index 0000000..f4fb2fa Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00590.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00591.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00591.jpg new file mode 100644 index 0000000..109ecec Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00591.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00592.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00592.jpg new file mode 100644 index 0000000..5d42ecc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00592.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00593.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00593.jpg new file mode 100644 index 0000000..bbac0c4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00593.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00594.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00594.jpg new file mode 100644 index 0000000..f0a66d2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00594.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00595.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00595.jpg new file mode 100644 index 0000000..d8f152f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00595.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00596.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00596.jpg new file mode 100644 index 0000000..fe994c7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00596.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00597.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00597.jpg new file mode 100644 index 0000000..b3b0201 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00597.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00598.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00598.jpg new file mode 100644 index 0000000..fd4e9f4 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00598.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00599.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00599.jpg new file mode 100644 index 0000000..d0303c3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00599.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Negative/00600.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00600.jpg new file mode 100644 index 0000000..e9a28bf Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Negative/00600.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00501.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00501.jpg new file mode 100644 index 0000000..55ce07c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00501.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00502.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00502.jpg new file mode 100644 index 0000000..cc1cac3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00502.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00503.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00503.jpg new file mode 100644 index 0000000..e000670 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00503.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00504.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00504.jpg new file mode 100644 index 0000000..e7fbf3c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00504.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00505.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00505.jpg new file mode 100644 index 0000000..069573a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00505.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00506.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00506.jpg new file mode 100644 index 0000000..eff27b2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00506.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00507.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00507.jpg new file mode 100644 index 0000000..8d03b4c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00507.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00508.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00508.jpg new file mode 100644 index 0000000..3fb8015 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00508.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00509.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00509.jpg new file mode 100644 index 0000000..b899167 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00509.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00510.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00510.jpg new file mode 100644 index 0000000..0ae1184 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00510.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00511.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00511.jpg new file mode 100644 index 0000000..029a9f9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00511.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00512.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00512.jpg new file mode 100644 index 0000000..448c1d0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00512.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00513.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00513.jpg new file mode 100644 index 0000000..2e50f81 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00513.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00514.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00514.jpg new file mode 100644 index 0000000..9e66497 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00514.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00515.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00515.jpg new file mode 100644 index 0000000..7768be0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00515.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00516.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00516.jpg new file mode 100644 index 0000000..aeedecd Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00516.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00517.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00517.jpg new file mode 100644 index 0000000..88c5bd1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00517.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00518.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00518.jpg new file mode 100644 index 0000000..2cbd4a9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00518.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00519.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00519.jpg new file mode 100644 index 0000000..430a600 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00519.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00520.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00520.jpg new file mode 100644 index 0000000..81700c1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00520.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00521.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00521.jpg new file mode 100644 index 0000000..96336ea Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00521.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00522.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00522.jpg new file mode 100644 index 0000000..e49d13f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00522.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00523.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00523.jpg new file mode 100644 index 0000000..792dda1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00523.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00524.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00524.jpg new file mode 100644 index 0000000..238f2a2 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00524.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00525.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00525.jpg new file mode 100644 index 0000000..619a55e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00525.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00526.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00526.jpg new file mode 100644 index 0000000..3f6ae6c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00526.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00527.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00527.jpg new file mode 100644 index 0000000..da3c8a6 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00527.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00528.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00528.jpg new file mode 100644 index 0000000..317a163 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00528.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00529.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00529.jpg new file mode 100644 index 0000000..b79b2b9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00529.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00530.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00530.jpg new file mode 100644 index 0000000..75565a1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00530.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00531.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00531.jpg new file mode 100644 index 0000000..0899e37 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00531.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00532.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00532.jpg new file mode 100644 index 0000000..d3d5905 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00532.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00533.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00533.jpg new file mode 100644 index 0000000..f1e8137 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00533.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00534.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00534.jpg new file mode 100644 index 0000000..5da333e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00534.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00535.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00535.jpg new file mode 100644 index 0000000..93b98a1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00535.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00536.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00536.jpg new file mode 100644 index 0000000..a8ae01f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00536.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00537.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00537.jpg new file mode 100644 index 0000000..82ac6ed Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00537.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00538.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00538.jpg new file mode 100644 index 0000000..f215a9a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00538.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00539.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00539.jpg new file mode 100644 index 0000000..9628f8f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00539.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00540.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00540.jpg new file mode 100644 index 0000000..6b2ef62 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00540.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00541.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00541.jpg new file mode 100644 index 0000000..c3698b8 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00541.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00542.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00542.jpg new file mode 100644 index 0000000..3b7c839 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00542.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00543.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00543.jpg new file mode 100644 index 0000000..7d9b802 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00543.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00544.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00544.jpg new file mode 100644 index 0000000..9075840 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00544.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00545.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00545.jpg new file mode 100644 index 0000000..1125324 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00545.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00546.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00546.jpg new file mode 100644 index 0000000..a25fa7a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00546.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00547.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00547.jpg new file mode 100644 index 0000000..424560c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00547.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00548.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00548.jpg new file mode 100644 index 0000000..332c20d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00548.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00549.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00549.jpg new file mode 100644 index 0000000..b44bf13 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00549.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00550.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00550.jpg new file mode 100644 index 0000000..f67fc96 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00550.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00551.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00551.jpg new file mode 100644 index 0000000..ff85f59 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00551.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00552.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00552.jpg new file mode 100644 index 0000000..c1511c9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00552.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00553.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00553.jpg new file mode 100644 index 0000000..db4fbae Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00553.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00554.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00554.jpg new file mode 100644 index 0000000..62d0a1e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00554.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00555.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00555.jpg new file mode 100644 index 0000000..1ccdb93 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00555.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00556.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00556.jpg new file mode 100644 index 0000000..b274766 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00556.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00557.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00557.jpg new file mode 100644 index 0000000..7c0d72d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00557.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00558.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00558.jpg new file mode 100644 index 0000000..ff47e3b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00558.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00559.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00559.jpg new file mode 100644 index 0000000..bfa4cdf Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00559.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00560.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00560.jpg new file mode 100644 index 0000000..4f34324 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00560.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00561.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00561.jpg new file mode 100644 index 0000000..74d868b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00561.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00562.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00562.jpg new file mode 100644 index 0000000..3da3d1a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00562.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00563.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00563.jpg new file mode 100644 index 0000000..9e1c193 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00563.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00564.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00564.jpg new file mode 100644 index 0000000..070118e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00564.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00565.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00565.jpg new file mode 100644 index 0000000..8f3cfb3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00565.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00566.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00566.jpg new file mode 100644 index 0000000..8ce14d3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00566.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00567.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00567.jpg new file mode 100644 index 0000000..5869e8b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00567.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00568.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00568.jpg new file mode 100644 index 0000000..00859f0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00568.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00569.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00569.jpg new file mode 100644 index 0000000..6c13b81 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00569.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00570.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00570.jpg new file mode 100644 index 0000000..d16196d Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00570.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00571.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00571.jpg new file mode 100644 index 0000000..c20db5f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00571.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00572.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00572.jpg new file mode 100644 index 0000000..2808d48 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00572.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00573.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00573.jpg new file mode 100644 index 0000000..68fc35b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00573.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00574.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00574.jpg new file mode 100644 index 0000000..c049b47 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00574.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00575.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00575.jpg new file mode 100644 index 0000000..ee849c3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00575.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00576.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00576.jpg new file mode 100644 index 0000000..92520aa Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00576.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00577.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00577.jpg new file mode 100644 index 0000000..5ed21a9 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00577.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00578.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00578.jpg new file mode 100644 index 0000000..48e9b6b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00578.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00579.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00579.jpg new file mode 100644 index 0000000..915485e Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00579.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00580.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00580.jpg new file mode 100644 index 0000000..47c0b24 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00580.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00581.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00581.jpg new file mode 100644 index 0000000..923816c Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00581.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00582.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00582.jpg new file mode 100644 index 0000000..4f2327a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00582.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00583.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00583.jpg new file mode 100644 index 0000000..9e2f45f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00583.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00584.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00584.jpg new file mode 100644 index 0000000..7ac6837 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00584.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00585.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00585.jpg new file mode 100644 index 0000000..5645bf7 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00585.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00586.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00586.jpg new file mode 100644 index 0000000..299e25b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00586.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00587.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00587.jpg new file mode 100644 index 0000000..d1adee5 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00587.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00588.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00588.jpg new file mode 100644 index 0000000..89db089 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00588.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00589.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00589.jpg new file mode 100644 index 0000000..3f2706b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00589.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00590.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00590.jpg new file mode 100644 index 0000000..43867a1 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00590.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00591.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00591.jpg new file mode 100644 index 0000000..4e84237 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00591.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00592.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00592.jpg new file mode 100644 index 0000000..a7fb0cc Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00592.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00593.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00593.jpg new file mode 100644 index 0000000..3786dbd Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00593.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00594.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00594.jpg new file mode 100644 index 0000000..044e813 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00594.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00595.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00595.jpg new file mode 100644 index 0000000..bf611eb Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00595.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00596.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00596.jpg new file mode 100644 index 0000000..f4af29b Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00596.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00597.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00597.jpg new file mode 100644 index 0000000..3f28b4a Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00597.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00598.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00598.jpg new file mode 100644 index 0000000..dc3efc3 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00598.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00599.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00599.jpg new file mode 100644 index 0000000..66a01c0 Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00599.jpg differ diff --git a/applications/quality_detection/SurfaceCrack/validation_data/Positive/00600.jpg b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00600.jpg new file mode 100644 index 0000000..0d5527f Binary files /dev/null and b/applications/quality_detection/SurfaceCrack/validation_data/Positive/00600.jpg differ diff --git a/applications/quality_detection/example.toml b/applications/quality_detection/example.toml new file mode 100644 index 0000000..15c3f14 --- /dev/null +++ b/applications/quality_detection/example.toml @@ -0,0 +1,7 @@ +task = 'test' +image_path = 'SurfaceCrack/test_data/' +num_samples = 10 +model_path = 'qnnqd.pdparams' +num_qubits = [4,4] +num_depths = [2,2] +observables = [['Z0','Z1','Z2','Z3'], ['Z0','Z1','Z2','Z3']] diff --git a/applications/quality_detection/introduction_cn.ipynb b/applications/quality_detection/introduction_cn.ipynb new file mode 100644 index 0000000..9e23d49 --- /dev/null +++ b/applications/quality_detection/introduction_cn.ipynb @@ -0,0 +1,358 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "d24ddfbe", + "metadata": {}, + "source": [ + "## 质量检测任务\n", + "\n", + "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*\n", + "\n", + "在建筑领域中物理环境、材料等因素会对工程质量产生影响。例如混凝土表面出现裂缝会严重损害建筑物使用寿命。因此工程质量检测至关重要,其中图片分类是一种常用技术。具体来说,我们的任务是给定具体建筑材料图片,然后对其进行二分类,以此判断材料表面是否出现了裂缝。我们使用的数据来自公开数据集 \\[1\\],其中数据形式如下\n", + "\n", + "\n", + "\n", + "我们具体使用的训练集包含 1000 张如上图所示的图片,为了测试模型的检测能力,我们选择了 200 张图片作为测试集。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "15abdd24", + "metadata": {}, + "source": [ + "## 使用 QNNQD 模型进行裂缝图片检测" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "977c0662", + "metadata": {}, + "source": [ + "### QNNQD 模型简介\n", + "\n", + "QNNQD 模型是一个可以用于图片分类的量子机器学习模型(Quantum Machine Learning,QML)。我们具体称其为一种量子神经网络 (Quantum Neural Network, QNN),它结合了参数化量子电路(Parameterized Quantum Circuit, PQC)和经典神经网络。对于表面裂缝图片数据,QNNQD 可以达到 90% 以上的分类准确率。模型主要分为量子和经典两部分,结构图如下:\n", + "\n", + "\n", + "\n", + "模型处理数据的主要过程:\n", + "1. 通常我们使用主成分分析将图片数据进行降维处理,使得编码电路更容易将经典数据编码为量子态。\n", + "2. 参数化电路的作用是特征提取,其电路参数可以在训练中被调整,可类比与经典神经网络中的可训练参数。\n", + "3. 量子测量由一组测量算子表示,是将量子态转化为经典数据的过程,我们可以对得到的经典数据做进一步处理。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "17661d06", + "metadata": {}, + "source": [ + "## 模型快速使用" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "f8a3ebf3", + "metadata": {}, + "source": [ + "### 使用模型进行预测\n", + "\n", + "我们可以通过在命令行输入 `python qnn_quality_detection.py --config train.toml` 即可开始训练模型; 这里,我们已经给出了一个训练好的模型,保存格式为 `qnnqd.pdparams`,可以直接用于区分表面裂缝数据。只需要在 `test.toml` 这个配置文件中进行对应的配置,然后在终端输入命令 `python qnn_quality_detection.py --config test.toml` 即可对图片进行预测。为了避免配置过多参数,我们提供了 `example.toml` 文件,可以简单的配置之后执行 `python qnn_quality_detection.py --config example.toml` 完成图片预测。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4f739274", + "metadata": {}, + "source": [ + "### 在线演示\n", + "\n", + "下面展示如果通过配置测试文件 `test_toml` 进行裂缝图片预测。" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "3e3772fe", + "metadata": {}, + "outputs": [], + "source": [ + "import toml\n", + "\n", + "test_toml = r\"\"\"\n", + "# 模型的整体配置文件。\n", + "# 图片的文件路径。\n", + "image_path = 'SurfaceCrack/test_data/'\n", + "\n", + "# 训练集中的数据个数,默认值为 -1 即使用全部数据。\n", + "num_samples = 20\n", + "\n", + "# 训练好的模型参数文件的文件路径。\n", + "model_path = 'qnnqd.pdparams'\n", + "\n", + "# 量子电路所包含的量子比特的数量。\n", + "num_qubits = [4, 4]\n", + "\n", + "# 每一层量子电路中的电路深度。\n", + "num_depths = [2, 2]\n", + "\n", + "# 量子电路中可观测量的设置。\n", + "observables = [['Z0', 'Z1', 'Z2', 'Z3'], ['Z0', 'Z1', 'Z2', 'Z3']]\n", + "\"\"\"\n", + "\n", + "config = toml.loads(test_toml)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "e5abd75c", + "metadata": {}, + "source": [ + "在 `test_toml` 配置文件中:\n", + "- `model_path`: 为训练好的模型,这里固定为 `qnnqd.pdparams`;\n", + "- `num_qubits`、`num_depths`、`observables` 三个参数应与训练好的模型 ``qnnqd.pdparams`` 相匹配。`num_qubits = [4,4]` 表示量子部分一共两层电路;每层电路为 4 的量子比特;`num_depths = [2,2]` 表示每层参数化电路深度为 2;`observables` 表示每层测量算子的具体形式。" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c6c9f96b", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "d:\\anaconda3\\envs\\modellib\\lib\\site-packages\\paddle\\tensor\\creation.py:125: DeprecationWarning: `np.object` is a deprecated alias for the builtin `object`. To silence this warning, use `object` by itself. Doing this will not modify any behavior and is safe. \n", + "Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations\n", + " if data.dtype == np.object:\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "图片的预测结果分别为 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1\n", + "图片的实际标签分别为 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1\n" + ] + } + ], + "source": [ + "from paddle_quantum.qml.qnnqd import inference\n", + "\n", + "prediction, prob, label = inference(**config)\n", + "print(f\"图片的预测结果分别为 {str(prediction)[1:-1]}\")\n", + "print(f\"图片的实际标签分别为 {str(label)[1:-1]}\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "5b1bc8f9", + "metadata": {}, + "source": [ + "以上是模型对于测试集给出的预测结果。下面具体给出两张图片的预测细节" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "230c5283", + "metadata": {}, + "outputs": [], + "source": [ + "# 导入所需要的包\n", + "import os\n", + "import warnings\n", + "\n", + "warnings.filterwarnings('ignore')\n", + "os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python'\n", + "\n", + "import numpy as np\n", + "import paddle\n", + "import matplotlib.pyplot as plt\n", + "from paddle_quantum.qml.qnnqd import QNNQD, ImageDataset\n", + "\n", + "# 设置模型参数\n", + "num_qubits = [4,4]\n", + "num_depths = [2,2]\n", + "observables = [['Z0','Z1','Z2','Z3'], ['Z0','Z1','Z2','Z3']]\n", + "\n", + "# 加载已训练的模型\n", + "model = QNNQD(\n", + " num_qubits=num_qubits,\n", + " num_depths=num_depths,\n", + " observables=observables,\n", + ")\n", + "state_dict = paddle.load('./qnnqd.pdparams')\n", + "model.set_state_dict(state_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "db11f93d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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amtr+++LxWKqqanRjh07LljT1dWleDyedAMADH+DGkAtLS2SpLKysqT7y8rK+h/7ovr6esVisf5bVVXVYA4JAJCmvF8Ft3LlSrW3t/ffDh8+7HtIAIAQDGoAlZeXS5JaW1uT7m9tbe1/7Iui0agKCwuTbgCA4W9QA6i6ulrl5eXasmVL/33xeFy7du3SjBkzBnNXAIAhzvkquFOnTungwYP9Xzc3N2vv3r0qLi7W2LFjtXz5cv385z/Xtddeq+rqaj311FOqrKzUggULBnPcAIAhzjmAdu/erdtuu63/6xUrVkiSFi1apHXr1unxxx9XIpHQQw89pLa2Nt18883avHmzcnJyBm/UAIAhLyOwdPZLoXg8rlgspk2bNjk1I7U087M2I7XsyxLALsd/juXpPH36tHONZGvUeObMGdO+XKV7M1JLc0zLfizNPqXzr2QdCEsD00Qi4VxjmQfL2KwsPx8sjYct82Ctc31dJBIJLViwQO3t7V/6vr73q+AAAFcmAggA4AUBBADwggACAHhBAAEAvCCAAABeEEAAAC8IIACAFwQQAMALAggA4AUBBADwggACAHhBAAEAvHBvwRqS7u5upw6xRUVFzvvIzc11rpFsHZ27urqca06dOuVc89lnn4VSI9k6Tlu6M588edK5xio7O9u5xtL92LIeLB20LWtIkq677jrnmoKCAueaeDzuXGMxcuRIU10sFnOusfwssnTLt3aWt6wj1/U60LFxBgQA8IIAAgB4QQABALwggAAAXhBAAAAvCCAAgBcEEADACwIIAOAFAQQA8IIAAgB4QQABALwggAAAXqRtM9LPPvtMnZ2dA97e0swvKyvLuUaSWltbnWuamppC2U8ikXCucZnnz2tra3OuCatZqrVRY2ZmOP8nszQwtdSMHj3auUaSjh496lwTVrNUy+s2IyPDuUaSSkpKnGuuv/5655rp06c717g0a77cOtc5D4JgQNtxBgQA8IIAAgB4QQABALwggAAAXhBAAAAvCCAAgBcEEADACwIIAOAFAQQA8IIAAgB4QQABALwggAAAXqRtM9I//elPys7OHvD25eXlzvuwNDCVpOPHjzvXWBqL9vb2OtdEo1HnGmujxsOHDzvXWJuEusrLyzPVWRo1WpqEWp7bgTZ4/DzLepBsDT8tz63lmCwNYy2NcyXbGm9ubnau2b9/v3ON9bm1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Kx3mIQgCnTx5UpWVlV969pl2v4LLzMz80sSUpMLCwit6gZ3DPJzFPJzFPJzFPJzlex5isdglt+EiBACAFwQQAMCLIRVA0WhUq1atUjQa9T0Ur5iHs5iHs5iHs5iHs4bSPKTdRQgAgCvDkDoDAgAMHwQQAMALAggA4AUBBADwYsgE0OrVq/WVr3xFOTk5qqmp0V//+lffQwrdM888o4yMjKTbpEmTfA8r5bZv367bb79dlZWVysjI0MaNG5MeD4JATz/9tCoqKpSbm6va2lodOHDAz2BT6FLzsHjx4vPWx7x58/wMNkXq6+t10003qaCgQKWlpVqwYIEaGxuTtuns7FRdXZ1GjRqlkSNHauHChWptbfU04tQYyDzceuut562Hhx9+2NOIL2xIBNCbb76pFStWaNWqVfr44481depUzZ07V8ePH/c9tNDdcMMNOnbsWP/to48+8j2klEskEpo6dapWr159wcefe+45vfTSS3rllVe0a9cu5efna+7cuers7Ax5pKl1qXmQpHnz5iWtj9dffz3EEaZeQ0OD6urqtHPnTr3//vvq6enRnDlzlEgk+rd59NFH9c477+itt95SQ0ODjh49qrvuusvjqAffQOZBkpYsWZK0Hp577jlPI76IYAiYPn16UFdX1/91b29vUFlZGdTX13scVfhWrVoVTJ061fcwvJIUbNiwof/rvr6+oLy8PHj++ef772trawui0Wjw+uuvexhhOL44D0EQBIsWLQruuOMOL+Px5fjx44GkoKGhIQiCs899dnZ28NZbb/Vv8/e//z2QFOzYscPXMFPui/MQBEHwf//3f8EPf/hDf4MagLQ/A+ru7taePXtUW1vbf19mZqZqa2u1Y8cOjyPz48CBA6qsrNT48eN1//3369ChQ76H5FVzc7NaWlqS1kcsFlNNTc0VuT62bdum0tJSTZw4UUuXLtWJEyd8Dyml2tvbJUnFxcWSpD179qinpydpPUyaNEljx44d1uvhi/NwzmuvvaaSkhJNnjxZK1euNP0JlVRKu2akX/Tpp5+qt7dXZWVlSfeXlZXpH//4h6dR+VFTU6N169Zp4sSJOnbsmJ599lndcsst2r9/vwoKCnwPz4uWlhZJuuD6OPfYlWLevHm66667VF1draamJv3kJz/R/PnztWPHDtPfH0p3fX19Wr58uWbOnKnJkydLOrseIpGIioqKkrYdzuvhQvMgSffdd5/GjRunyspK7du3T0888YQaGxv19ttvexxtsrQPIPzP/Pnz+/89ZcoU1dTUaNy4cfrDH/6gBx980OPIkA7uueee/n/feOONmjJliiZMmKBt27Zp9uzZHkeWGnV1ddq/f/8V8T7ol7nYPDz00EP9/77xxhtVUVGh2bNnq6mpSRMmTAh7mBeU9r+CKykpUVZW1nlXsbS2tqq8vNzTqNJDUVGRrrvuOh08eND3ULw5twZYH+cbP368SkpKhuX6WLZsmd599119+OGHSX++pby8XN3d3Wpra0vafriuh4vNw4XU1NRIUlqth7QPoEgkomnTpmnLli399/X19WnLli2aMWOGx5H5d+rUKTU1NamiosL3ULyprq5WeXl50vqIx+PatWvXFb8+jhw5ohMnTgyr9REEgZYtW6YNGzZo69atqq6uTnp82rRpys7OTloPjY2NOnTo0LBaD5eahwvZu3evJKXXevB9FcRAvPHGG0E0Gg3WrVsX/O1vfwseeuihoKioKGhpafE9tFD96Ec/CrZt2xY0NzcHf/7zn4Pa2tqgpKQkOH78uO+hpdTJkyeDTz75JPjkk08CScELL7wQfPLJJ8G//vWvIAiC4Be/+EVQVFQUbNq0Kdi3b19wxx13BNXV1cHp06c9j3xwfdk8nDx5MnjssceCHTt2BM3NzcEHH3wQfOMb3wiuvfbaoLOz0/fQB83SpUuDWCwWbNu2LTh27Fj/raOjo3+bhx9+OBg7dmywdevWYPfu3cGMGTOCGTNmeBz14LvUPBw8eDD46U9/GuzevTtobm4ONm3aFIwfPz6YNWuW55EnGxIBFARB8PLLLwdjx44NIpFIMH369GDnzp2+hxS6u+++O6ioqAgikUhw9dVXB3fffXdw8OBB38NKuQ8//DCQdN5t0aJFQRCcvRT7qaeeCsrKyoJoNBrMnj07aGxs9DvoFPiyeejo6AjmzJkTjB49OsjOzg7GjRsXLFmyZNj9J+1Cxy8pWLt2bf82p0+fDn7wgx8EV111VZCXlxfceeedwbFjx/wNOgUuNQ+HDh0KZs2aFRQXFwfRaDS45pprgh//+MdBe3u734F/AX+OAQDgRdq/BwQAGJ4IIACAFwQQAMALAggA4AUBBADwggACAHhBAAEAvCCAAABeEEAAAC8IIACAFwQQAMALAggA4MX/Axp8bMAGbhcOAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "crack_img = plt.imread('positive_crack.jpg') # Positive, label=[1.0,0.0]\n", + "plt.imshow(crack_img, cmap='gray', vmin=0, vmax=255)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "563d6922", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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aFBBQcHQraKiIplTAgCkKfer4NasWaPOzs6h25EjR7ynBACIQFIDqLS0VJLU0dGRcH9HR8fQ974qFospPz8/4QYAGP2SGkCVlZUqLS1VY2Pj0H1dXV3as2ePampqkrkrAMAIF/oquO7ubrW2tg593dbWpv3796uwsFCTJ0/WqlWr9Mtf/lI33nijKisr9eyzz6q8vFyLFy9O5rwBACNc6ADau3ev7rzzzqGvV69eLUlaunSpNm3apKeeeko9PT169NFHderUKd1+++3avn27xo0bl7xZAwBGvIwgqra9Yerq6lJBQYGampo0YcKEYY+L8jDGjg3f4RqLxUKPsZSRWkoNrWvX398fyb6ifGyjKoWMah2sZaQWUR2TZYyl0DZK1vLcdNXd3a3q6mp1dnZe9n390XXUAIARgwACALgggAAALgggAIALAggA4IIAAgC4IIAAAC4IIACACwIIAOCCAAIAuCCAAAAuCCAAgAsCCADgInytc0QyMzNDNfmmext2VK3EUTYFW9q6BwYGIhkTpaganS3rEFW7d5Qsx2R5zsJuuO3evAICALgggAAALgggAIALAggA4IIAAgC4IIAAAC4IIACACwIIAOCCAAIAuCCAAAAuCCAAgAsCCADgYtQ09FkKCq0FocMt2rta1pLQqPaTzkWX1nJayzFZxljWPKoCUyvL8yKdzyEpuvlFWaacTngFBABwQQABAFwQQAAAFwQQAMAFAQQAcEEAAQBcEEAAABcEEADABQEEAHBBAAEAXBBAAAAXBBAAwEXalpH29/erv79/2NtbikWtRYOW0kVL2aBlTFRlmpJCPT5Rj7EaOzb8U8KyfpZzyFJgGlVxrlVU57i17DOqktAoS2OjMNznbHqfnQCAUYsAAgC4IIAAAC4IIACACwIIAOCCAAIAuCCAAAAuCCAAgAsCCADgggACALgggAAALgggAICLtC0jHRgYCFVCORpLF6NiKXKVpHg8HnqMpVjUUsJpLViNqhzTIsqi2ahEVdJrOYeilM6/HyyGezzp/VsbADBqEUAAABehA2jnzp266667VF5eroyMDG3dujXh+8uWLVNGRkbCra6uLlnzBQCMEqEDqKenR1VVVVq/fv0lt6mrq9Px48eHbm+++eZVTRIAMPqEvghh0aJFWrRo0WW3icViKi0tNU8KADD6peQ9oKamJhUXF+vmm2/W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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "non_crack_img = plt.imread('negative_non_crack.jpg') # Negative, label=[0,1]\n", + "plt.imshow(non_crack_img, cmap='gray', vmin=0, vmax=255)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "85369b8e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "对于裂缝图片,模型有 99.99% 的置信度检测出其质量受损。\n", + "对于无裂缝的图片,模型有 77.33% 的置信度检测出其质量无损。\n" + ] + } + ], + "source": [ + "# 将图片编码为量子态\n", + "preprocess = ImageDataset(file_path='SurfaceCrack/training_data/', num_samples=500)\n", + "\n", + "crack_img_data = np.reshape(crack_img, 784)\n", + "non_crack_img_data = np.reshape(non_crack_img, 784)\n", + "test_data = np.array([crack_img_data, non_crack_img_data])\n", + "\n", + "test_data = preprocess.centering.transform(test_data)\n", + "test_data = preprocess.pca.transform(test_data)\n", + "test_data = preprocess.scaler.transform(test_data)\n", + "\n", + "test_data = paddle.to_tensor(test_data, dtype='float64')\n", + "\n", + "# 使用模型进行预测并得到对应的概率值\n", + "prob = model(test_data)\n", + "print(f\"对于裂缝图片,模型有 {prob[0][0].item():3.2%} 的置信度检测出其质量受损。\")\n", + "print(f\"对于无裂缝的图片,模型有 {prob[1][1].item():3.2%} 的置信度检测出其质量无损。\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8f6f3b91", + "metadata": {}, + "source": [ + "## 注意事项\n", + "\n", + "我们通常考虑调整 `num_qubits`、`num_depths`、`observables` 三个主要超参数,对模型的影响较大。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "813ba2ae", + "metadata": {}, + "source": [ + "## 引用\n", + "\n", + "[1] Özgenel, Çağlar Fırat (2019), “Concrete Crack Images for Classification”, Mendeley Data, V2, doi: 10.17632/5y9wdsg2zt.2 " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "modellib", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.15" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "vscode": { + "interpreter": { + "hash": "dfa0523b1e359b8fd3ea126fa0459d0c86d49956d91b464930b80cba21582eac" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/applications/quality_detection/introduction_en.ipynb b/applications/quality_detection/introduction_en.ipynb new file mode 100644 index 0000000..72ffe90 --- /dev/null +++ b/applications/quality_detection/introduction_en.ipynb @@ -0,0 +1,354 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "d24ddfbe", + "metadata": {}, + "source": [ + "## Quality Detection task\n", + "\n", + "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*\n", + "\n", + "In the field of architecture, the physical environment, materials, and other factors will have an impact on the quality of the project. For example, cracks on the concrete surface can seriously damage the service life of the building. Therefore, engineering quality inspection is very important, among which picture classification is a common technology. Specifically, our task is to give a picture of a specific building material and then to determine whether there are cracks on the surface of the material. The data we use is from the public data set \\[1\\], which has the following form\n", + "\n", + "\n", + "\n", + "\n", + "The training set we use specifically includes 1000 pictures as shown in the figure above. In order to test the detection capability of the model, we selected 200 pictures as the test set." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "15abdd24", + "metadata": {}, + "source": [ + "## QNNQD model for crack image detection" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "977c0662", + "metadata": {}, + "source": [ + "### Introduction of QNNQD\n", + "\n", + "QNNQD model is a Quantum Machine Learning (QML) model that can be used for picture classification. We specifically call it a Quantum Neural Network (QNN), which combines Parameterized Quantum Circuit (PQC) and a classical neural network. For surface crack image data, QNNQD can achieve more than 90% classification accuracy. The model is mainly divided into quantum and classical parts. The structure diagram is as follows:\n", + "\n", + "\n", + "\n", + "\n", + "Main process:\n", + "- In general, we use principal component analysis (PCA) to reduce the dimension of image data, making it easier to encode classical data into quantum states through encoding circuits.\n", + "- The parameterized circuit is used for feature extraction, and its circuit parameters can be adjusted during training. Similar to the trainable parameters in traditional neural networks.\n", + "- Quantum measurement, represented by a set of measurement operators, is the process of converting quantum states into classical data, which can be further processed." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "17661d06", + "metadata": {}, + "source": [ + "## Quick start" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "f8a3ebf3", + "metadata": {}, + "source": [ + "### Use the model to make predictions\n", + "\n", + "Entering the command `python qnn_quality_detection.py --config train.toml` will initiate the model training. Here, we have given a trained model saved in the format `qnnqd.pdparams`, which can be directly used to distinguish surface crack data. One only needs to do the corresponding configuration in this file `test.toml`, and enter the command `python qnn_quality_detection.py --config test.toml` to predict the image. To avoid configuring too many parameters, we provide the `example.toml` file which can be more easily configured. One can execute `python qnn_quality_detection.py --config example.toml` to complete image prediction." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4f739274", + "metadata": {}, + "source": [ + "### Online Test\n", + "\n", + "The following shows how to configure the test file `test_toml` to make crack image prediction." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "3e3772fe", + "metadata": {}, + "outputs": [], + "source": [ + "import toml\n", + "\n", + "test_toml = r\"\"\"\n", + "# The config for test the QNNQD model.\n", + "# The path of the input image.\n", + "image_path = 'SurfaceCrack/test_data/'\n", + "\n", + "# The number of the data in the test dataset.\n", + "# The value defaults to -1 which means using all data.\n", + "num_samples = 20\n", + "\n", + "# The path of the trained model, which will be loaded.\n", + "model_path = 'qnnqd.pdparams'\n", + "\n", + "# The number of qubits of quantum circuit in each layer.\n", + "num_qubits = [4, 4]\n", + "\n", + "# The depth of quantum circuit in each layer.\n", + "num_depths = [2, 2]\n", + "\n", + "# The observables of quantum circuit in each layer.\n", + "observables = [['Z0', 'Z1', 'Z2', 'Z3'], ['Z0', 'Z1', 'Z2', 'Z3']]\n", + "\"\"\"\n", + "\n", + "config = toml.loads(test_toml)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "e5abd75c", + "metadata": {}, + "source": [ + "In `test_toml` :\n", + "- `model_path`: this is the trained model, here we set it as `qnnqd.pdparams`;\n", + "- `num_qubits`、`num_depths`、`observables` these parameters correspond to the model ``qnnqd.pdparams`` , `num_qubits = [4,4]` represents the quantum part of a total of two layers of circuit, each layer of the circuit has 4 qubits; `num_depths = [2,2]` represents the depth of parameterized circuit of each layer is 2;`observables` is the specific form of the measurement operator at each layer.\n", + "\n", + "Test:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "c6c9f96b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The prediction results of the input images are 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1 respectively.\n", + "The labels of the input images are 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1 respectively.\n" + ] + } + ], + "source": [ + "from paddle_quantum.qml.qnnqd import inference\n", + "\n", + "prediction, prob, label = inference(**config)\n", + "print(f\"The prediction results of the input images are {str(prediction)[1:-1]} respectively.\")\n", + "print(f\"The labels of the input images are {str(label)[1:-1]} respectively.\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "5b1bc8f9", + "metadata": {}, + "source": [ + "The above is the prediction result of the model for the test set. The prediction details of the two images are given below." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "8d4191f4", + "metadata": {}, + "outputs": [], + "source": [ + "# import\n", + "import os\n", + "import warnings\n", + "\n", + "warnings.filterwarnings('ignore')\n", + "os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python'\n", + "\n", + "import numpy as np\n", + "import paddle\n", + "import matplotlib.pyplot as plt\n", + "from paddle_quantum.qml.qnnqd import QNNQD, ImageDataset\n", + "\n", + "# Set model parameters\n", + "num_qubits = [4,4]\n", + "num_depths = [2,2]\n", + "observables = [['Z0','Z1','Z2','Z3'], ['Z0','Z1','Z2','Z3']]\n", + "\n", + "# Load the trained model\n", + "model = QNNQD(\n", + " num_qubits=num_qubits,\n", + " num_depths=num_depths,\n", + " observables=observables,\n", + ")\n", + "state_dict = paddle.load('./qnnqd.pdparams')\n", + "model.set_state_dict(state_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "d74ec0d5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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amtr+++LxWKqqanRjh07LljT1dWleDyedAMADH+DGkAtLS2SpLKysqT7y8rK+h/7ovr6esVisf5bVVXVYA4JAJCmvF8Ft3LlSrW3t/ffDh8+7HtIAIAQDGoAlZeXS5JaW1uT7m9tbe1/7Iui0agKCwuTbgCA4W9QA6i6ulrl5eXasmVL/33xeFy7du3SjBkzBnNXAIAhzvkquFOnTungwYP9Xzc3N2vv3r0qLi7W2LFjtXz5cv385z/Xtddeq+rqaj311FOqrKzUggULBnPcAIAhzjmAdu/erdtuu63/6xUrVkiSFi1apHXr1unxxx9XIpHQQw89pLa2Nt18883avHmzcnJyBm/UAIAhLyOwdPZLoXg8rlgspk2bNjk1I7U087M2I7XsyxLALsd/juXpPH36tHONZGvUeObMGdO+XKV7M1JLc0zLfizNPqXzr2QdCEsD00Qi4VxjmQfL2KwsPx8sjYct82Ctc31dJBIJLViwQO3t7V/6vr73q+AAAFcmAggA4AUBBADwggACAHhBAAEAvCCAAABeEEAAAC8IIACAFwQQAMALAggA4AUBBADwggACAHhBAAEAvHBvwRqS7u5upw6xRUVFzvvIzc11rpFsHZ27urqca06dOuVc89lnn4VSI9k6Tlu6M588edK5xio7O9u5xtL92LIeLB20LWtIkq677jrnmoKCAueaeDzuXGMxcuRIU10sFnOusfwssnTLt3aWt6wj1/U60LFxBgQA8IIAAgB4QQABALwggAAAXhBAAAAvCCAAgBcEEADACwIIAOAFAQQA8IIAAgB4QQABALwggAAAXqRtM9LPPvtMnZ2dA97e0swvKyvLuUaSWltbnWuamppC2U8ikXCucZnnz2tra3OuCatZqrVRY2ZmOP8nszQwtdSMHj3auUaSjh496lwTVrNUy+s2IyPDuUaSSkpKnGuuv/5655rp06c717g0a77cOtc5D4JgQNtxBgQA8IIAAgB4QQABALwggAAAXhBAAAAvCCAAgBcEEADACwIIAOAFAQQA8IIAAgB4QQABALwggAAAXqRtM9I//elPys7OHvD25eXlzvuwNDCVpOPHjzvXWBqL9vb2OtdEo1HnGmujxsOHDzvXWJuEusrLyzPVWRo1WpqEWp7bgTZ4/DzLepBsDT8tz63lmCwNYy2NcyXbGm9ubnau2b9/v3ON9bm1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Kx3mIQgCnTx5UpWVlV969pl2v4LLzMz80sSUpMLCwit6gZ3DPJzFPJzFPJzFPJzlex5isdglt+EiBACAFwQQAMCLIRVA0WhUq1atUjQa9T0Ur5iHs5iHs5iHs5iHs4bSPKTdRQgAgCvDkDoDAgAMHwQQAMALAggA4AUBBADwYsgE0OrVq/WVr3xFOTk5qqmp0V//+lffQwrdM888o4yMjKTbpEmTfA8r5bZv367bb79dlZWVysjI0MaNG5MeD4JATz/9tCoqKpSbm6va2lodOHDAz2BT6FLzsHjx4vPWx7x58/wMNkXq6+t10003qaCgQKWlpVqwYIEaGxuTtuns7FRdXZ1GjRqlkSNHauHChWptbfU04tQYyDzceuut562Hhx9+2NOIL2xIBNCbb76pFStWaNWqVfr44481depUzZ07V8ePH/c9tNDdcMMNOnbsWP/to48+8j2klEskEpo6dapWr159wcefe+45vfTSS3rllVe0a9cu5efna+7cuers7Ax5pKl1qXmQpHnz5iWtj9dffz3EEaZeQ0OD6urqtHPnTr3//vvq6enRnDlzlEgk+rd59NFH9c477+itt95SQ0ODjh49qrvuusvjqAffQOZBkpYsWZK0Hp577jlPI76IYAiYPn16UFdX1/91b29vUFlZGdTX13scVfhWrVoVTJ061fcwvJIUbNiwof/rvr6+oLy8PHj++ef772trawui0Wjw+uuvexhhOL44D0EQBIsWLQruuOMOL+Px5fjx44GkoKGhIQiCs899dnZ28NZbb/Vv8/e//z2QFOzYscPXMFPui/MQBEHwf//3f8EPf/hDf4MagLQ/A+ru7taePXtUW1vbf19mZqZqa2u1Y8cOjyPz48CBA6qsrNT48eN1//3369ChQ76H5FVzc7NaWlqS1kcsFlNNTc0VuT62bdum0tJSTZw4UUuXLtWJEyd8Dyml2tvbJUnFxcWSpD179qinpydpPUyaNEljx44d1uvhi/NwzmuvvaaSkhJNnjxZK1euNP0JlVRKu2akX/Tpp5+qt7dXZWVlSfeXlZXpH//4h6dR+VFTU6N169Zp4sSJOnbsmJ599lndcsst2r9/vwoKCnwPz4uWlhZJuuD6OPfYlWLevHm66667VF1draamJv3kJz/R/PnztWPHDtPfH0p3fX19Wr58uWbOnKnJkydLOrseIpGIioqKkrYdzuvhQvMgSffdd5/GjRunyspK7du3T0888YQaGxv19ttvexxtsrQPIPzP/Pnz+/89ZcoU1dTUaNy4cfrDH/6gBx980OPIkA7uueee/n/feOONmjJliiZMmKBt27Zp9uzZHkeWGnV1ddq/f/8V8T7ol7nYPDz00EP9/77xxhtVUVGh2bNnq6mpSRMmTAh7mBeU9r+CKykpUVZW1nlXsbS2tqq8vNzTqNJDUVGRrrvuOh08eND3ULw5twZYH+cbP368SkpKhuX6WLZsmd599119+OGHSX++pby8XN3d3Wpra0vafriuh4vNw4XU1NRIUlqth7QPoEgkomnTpmnLli399/X19WnLli2aMWOGx5H5d+rUKTU1NamiosL3ULyprq5WeXl50vqIx+PatWvXFb8+jhw5ohMnTgyr9REEgZYtW6YNGzZo69atqq6uTnp82rRpys7OTloPjY2NOnTo0LBaD5eahwvZu3evJKXXevB9FcRAvPHGG0E0Gg3WrVsX/O1vfwseeuihoKioKGhpafE9tFD96Ec/CrZt2xY0NzcHf/7zn4Pa2tqgpKQkOH78uO+hpdTJkyeDTz75JPjkk08CScELL7wQfPLJJ8G//vWvIAiC4Be/+EVQVFQUbNq0Kdi3b19wxx13BNXV1cHp06c9j3xwfdk8nDx5MnjssceCHTt2BM3NzcEHH3wQfOMb3wiuvfbaoLOz0/fQB83SpUuDWCwWbNu2LTh27Fj/raOjo3+bhx9+OBg7dmywdevWYPfu3cGMGTOCGTNmeBz14LvUPBw8eDD46U9/GuzevTtobm4ONm3aFIwfPz6YNWuW55EnGxIBFARB8PLLLwdjx44NIpFIMH369GDnzp2+hxS6u+++O6ioqAgikUhw9dVXB3fffXdw8OBB38NKuQ8//DCQdN5t0aJFQRCcvRT7qaeeCsrKyoJoNBrMnj07aGxs9DvoFPiyeejo6AjmzJkTjB49OsjOzg7GjRsXLFmyZNj9J+1Cxy8pWLt2bf82p0+fDn7wgx8EV111VZCXlxfceeedwbFjx/wNOgUuNQ+HDh0KZs2aFRQXFwfRaDS45pprgh//+MdBe3u734F/AX+OAQDgRdq/BwQAGJ4IIACAFwQQAMALAggA4AUBBADwggACAHhBAAEAvCCAAABeEEAAAC8IIACAFwQQAMALAggA4MX/Axp8bMAGbhcOAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "crack_img = plt.imread('positive_crack.jpg') # Positive, label=[1.0,0.0]\n", + "plt.imshow(crack_img, cmap='gray', vmin=0, vmax=255)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "57975e9b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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aFBBQcHQraKiIplTAgCkKfer4NasWaPOzs6h25EjR7ynBACIQFIDqLS0VJLU0dGRcH9HR8fQ974qFospPz8/4QYAGP2SGkCVlZUqLS1VY2Pj0H1dXV3as2ePampqkrkrAMAIF/oquO7ubrW2tg593dbWpv3796uwsFCTJ0/WqlWr9Mtf/lI33nijKisr9eyzz6q8vFyLFy9O5rwBACNc6ADau3ev7rzzzqGvV69eLUlaunSpNm3apKeeeko9PT169NFHderUKd1+++3avn27xo0bl7xZAwBGvIwgqra9Yerq6lJBQYGampo0YcKEYY+L8jDGjg3f4RqLxUKPsZSRWkoNrWvX398fyb6ifGyjKoWMah2sZaQWUR2TZYyl0DZK1vLcdNXd3a3q6mp1dnZe9n390XXUAIARgwACALgggAAALgggAIALAggA4IIAAgC4IIAAAC4IIACACwIIAOCCAAIAuCCAAAAuCCAAgAsCCADgInytc0QyMzNDNfmmext2VK3EUTYFW9q6BwYGIhkTpaganS3rEFW7d5Qsx2R5zsJuuO3evAICALgggAAALgggAIALAggA4IIAAgC4IIAAAC4IIACACwIIAOCCAAIAuCCAAAAuCCAAgAsCCADgYtQ09FkKCq0FocMt2rta1pLQqPaTzkWX1nJayzFZxljWPKoCUyvL8yKdzyEpuvlFWaacTngFBABwQQABAFwQQAAAFwQQAMAFAQQAcEEAAQBcEEAAABcEEADABQEEAHBBAAEAXBBAAAAXBBAAwEXalpH29/erv79/2NtbikWtRYOW0kVL2aBlTFRlmpJCPT5Rj7EaOzb8U8KyfpZzyFJgGlVxrlVU57i17DOqktAoS2OjMNznbHqfnQCAUYsAAgC4IIAAAC4IIACACwIIAOCCAAIAuCCAAAAuCCAAgAsCCADgggACALgggAAALgggAICLtC0jHRgYCFVCORpLF6NiKXKVpHg8HnqMpVjUUsJpLViNqhzTIsqi2ahEVdJrOYeilM6/HyyGezzp/VsbADBqEUAAABehA2jnzp266667VF5eroyMDG3dujXh+8uWLVNGRkbCra6uLlnzBQCMEqEDqKenR1VVVVq/fv0lt6mrq9Px48eHbm+++eZVTRIAMPqEvghh0aJFWrRo0WW3icViKi0tNU8KADD6peQ9oKamJhUXF+vmm2/W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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "non_crack_img = plt.imread('negative_non_crack.jpg') # Negative, label=[0,1]\n", + "plt.imshow(non_crack_img, cmap='gray', vmin=0, vmax=255)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "4611ad8a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "For the cracked images, the model has 99.99% of confidence in detecting their impaired quality.\n", + "For images without cracks, the model has 77.33% of confidence to detect the quality without damage.\n" + ] + } + ], + "source": [ + "# Encoding images into quantum states\n", + "preprocess = ImageDataset(file_path='SurfaceCrack/training_data/', num_samples=500)\n", + "\n", + "crack_img_data = np.reshape(crack_img, 784)\n", + "non_crack_img_data = np.reshape(non_crack_img, 784)\n", + "test_data = np.array([crack_img_data, non_crack_img_data])\n", + "\n", + "test_data = preprocess.centering.transform(test_data)\n", + "test_data = preprocess.pca.transform(test_data)\n", + "test_data = preprocess.scaler.transform(test_data)\n", + "\n", + "test_data = paddle.to_tensor(test_data, dtype='float64')\n", + "\n", + "# Use the model to make predictions and get the corresponding probability values\n", + "prob = model(test_data)\n", + "print(f\"For the cracked images, the model has {prob[0][0].item():3.2%} of confidence in detecting their impaired quality.\")\n", + "print(f\"For images without cracks, the model has {prob[1][1].item():3.2%} of confidence to detect the quality without damage.\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8f6f3b91", + "metadata": {}, + "source": [ + "## Remarks\n", + "\n", + "We usually consider adjusting `num_qubits`、`num_depths`、`observables` these three hyperparameters, which have a greater impact on the model." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "813ba2ae", + "metadata": {}, + "source": [ + "## References\n", + "\n", + "[1] Özgenel, Çağlar Fırat (2019), “Concrete Crack Images for Classification”, Mendeley Data, V2, doi: 10.17632/5y9wdsg2zt.2 " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "modellib", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.15 (default, Nov 24 2022, 18:44:54) [MSC v.1916 64 bit (AMD64)]" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "vscode": { + "interpreter": { + "hash": "dfa0523b1e359b8fd3ea126fa0459d0c86d49956d91b464930b80cba21582eac" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/applications/quality_detection/negative_non_crack.jpg b/applications/quality_detection/negative_non_crack.jpg new file mode 100644 index 0000000..0aa142a Binary files /dev/null and b/applications/quality_detection/negative_non_crack.jpg differ diff --git a/applications/quality_detection/positive_crack.jpg b/applications/quality_detection/positive_crack.jpg new file mode 100644 index 0000000..d2c8596 Binary files /dev/null and b/applications/quality_detection/positive_crack.jpg differ diff --git a/applications/quality_detection/qnn_quality_detection.py b/applications/quality_detection/qnn_quality_detection.py new file mode 100644 index 0000000..176cc70 --- /dev/null +++ b/applications/quality_detection/qnn_quality_detection.py @@ -0,0 +1,40 @@ +# !/usr/bin/env python3 +# Copyright (c) 2020 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import os +import warnings + +warnings.filterwarnings('ignore') +os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python' + +import toml +from paddle_quantum.qml.qnnqd import train, inference + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Detect whether there are cracks on the surface of images by the QNNQD model.") + parser.add_argument("--config", type=str, help="Input the config file with toml format.") + args = parser.parse_args() + config = toml.load(args.config) + task = config.pop('task') + + if task == 'train': + train(**config) + elif task == 'test': + prediction, prob, label = inference(**config) + print(f"The prediction results of the input pictures are {str(prediction)[1:-1]} respectively.") + else: + raise ValueError("Unknown task, it can be train or test.") diff --git a/applications/quality_detection/qnnqd.pdparams b/applications/quality_detection/qnnqd.pdparams new file mode 100644 index 0000000..42431a7 Binary files /dev/null and b/applications/quality_detection/qnnqd.pdparams differ diff --git a/applications/quality_detection/qnnqd_model_cn.png b/applications/quality_detection/qnnqd_model_cn.png new file mode 100644 index 0000000..52636f3 Binary files /dev/null and b/applications/quality_detection/qnnqd_model_cn.png differ diff --git a/applications/quality_detection/qnnqd_model_en.png b/applications/quality_detection/qnnqd_model_en.png new file mode 100644 index 0000000..955d02f Binary files /dev/null and b/applications/quality_detection/qnnqd_model_en.png differ diff --git a/applications/quality_detection/surface_crack_example.png b/applications/quality_detection/surface_crack_example.png new file mode 100644 index 0000000..1fab468 Binary files /dev/null and b/applications/quality_detection/surface_crack_example.png differ diff --git a/applications/quality_detection/test.toml b/applications/quality_detection/test.toml new file mode 100644 index 0000000..5ec9457 --- /dev/null +++ b/applications/quality_detection/test.toml @@ -0,0 +1,22 @@ +# The config for testing the QNNQD model. +# The task of this config. Available values: 'train' | 'test'. +task = 'test' + +# The path of the input image. +image_path = 'SurfaceCrack/test_data/' + +# The number of the data in the test dataset. +# The value defaults to -1 which means using all data. +num_samples = -1 + +# The path of the trained model, which will be loaded. +model_path = 'qnnqd.pdparams' + +# The number of qubits of the quantum circuit in each layer. +num_qubits = [4,4] + +# The depth of the quantum circuit in each layer. +num_depths = [2,2] + +# The observables of the quantum circuit in each layer. +observables = [['Z0','Z1','Z2','Z3'], ['Z0','Z1','Z2','Z3']] diff --git a/applications/quality_detection/train.toml b/applications/quality_detection/train.toml new file mode 100644 index 0000000..a452995 --- /dev/null +++ b/applications/quality_detection/train.toml @@ -0,0 +1,57 @@ +# The config for training the QNNQD model. +# The task of this config. Available values: 'train' | 'test'. +task = 'train' + +# The name of the model, which is used to save the model. +model_name = 'qnnqd' + +# The path to save the model. Both relative and absolute paths are allowed. +# It save the model to the current path by default. +# saved_path = './' + +# The number of qubits of the quantum circuit in each layer. +num_qubits = [4,4] + +# The depth of quantum circuit in each layer. +num_depths = [2,2] + +# The observables of the quantum circuit in each layer. +observables = [['Z0','Z1','Z2','Z3'], ['Z0','Z1','Z2','Z3']] + +# The size of the batch samplers. +batch_size = 40 + +# The number of epochs to train the model. +num_epochs = 20 + +# The learning rate used to update the parameters, default to 0.1. +learning_rate = 0.1 + +# The path of the dataset. It defaults to SurfaceCrack. +dataset = 'SurfaceCrack' + +# The path used to save logs. Default to ``./``. +saved_dir = './' + +# Whether use the validation. +# It is true means the dataset contains training, validation and test datasets. +# It is false means the dataset only contains training datasets and test datasets. +using_validation = false + +# The number of the data in the training dataset. +# The value defaults to -1 which means using all data. +num_train = 500 + +# The number of the data in the validation dataset. +# The value defaults to -1 which means using all data. +num_val = 200 + +# The number of the data in the test dataset. +# The value defaults to -1 which means using all data. +num_test = 200 + +# Number of epochs with no improvement after which training will be stopped. +early_stopping = 1000 + +# The number of subprocess to load data, 0 for no subprocess used and loading data in main process, default to 0. +num_workers = 0 diff --git a/applications/regression/datasets/Fish.csv b/applications/regression/datasets/Fish.csv new file mode 100644 index 0000000..a0bbd32 --- /dev/null +++ b/applications/regression/datasets/Fish.csv @@ -0,0 +1,160 @@ +Species,Weight,Length1,Length2,Length3,Height,Width +Bream,242,23.2,25.4,30,11.52,4.02 +Bream,290,24,26.3,31.2,12.48,4.3056 +Bream,340,23.9,26.5,31.1,12.3778,4.6961 +Bream,363,26.3,29,33.5,12.73,4.4555 +Bream,430,26.5,29,34,12.444,5.134 +Bream,450,26.8,29.7,34.7,13.6024,4.9274 +Bream,500,26.8,29.7,34.5,14.1795,5.2785 +Bream,390,27.6,30,35,12.67,4.69 +Bream,450,27.6,30,35.1,14.0049,4.8438 +Bream,500,28.5,30.7,36.2,14.2266,4.9594 +Bream,475,28.4,31,36.2,14.2628,5.1042 +Bream,500,28.7,31,36.2,14.3714,4.8146 +Bream,500,29.1,31.5,36.4,13.7592,4.368 +Bream,340,29.5,32,37.3,13.9129,5.0728 +Bream,600,29.4,32,37.2,14.9544,5.1708 +Bream,600,29.4,32,37.2,15.438,5.58 +Bream,700,30.4,33,38.3,14.8604,5.2854 +Bream,700,30.4,33,38.5,14.938,5.1975 +Bream,610,30.9,33.5,38.6,15.633,5.1338 +Bream,650,31,33.5,38.7,14.4738,5.7276 +Bream,575,31.3,34,39.5,15.1285,5.5695 +Bream,685,31.4,34,39.2,15.9936,5.3704 +Bream,620,31.5,34.5,39.7,15.5227,5.2801 +Bream,680,31.8,35,40.6,15.4686,6.1306 +Bream,700,31.9,35,40.5,16.2405,5.589 +Bream,725,31.8,35,40.9,16.36,6.0532 +Bream,720,32,35,40.6,16.3618,6.09 +Bream,714,32.7,36,41.5,16.517,5.8515 +Bream,850,32.8,36,41.6,16.8896,6.1984 +Bream,1000,33.5,37,42.6,18.957,6.603 +Bream,920,35,38.5,44.1,18.0369,6.3063 +Bream,955,35,38.5,44,18.084,6.292 +Bream,925,36.2,39.5,45.3,18.7542,6.7497 +Bream,975,37.4,41,45.9,18.6354,6.7473 +Bream,950,38,41,46.5,17.6235,6.3705 +Roach,40,12.9,14.1,16.2,4.1472,2.268 +Roach,69,16.5,18.2,20.3,5.2983,2.8217 +Roach,78,17.5,18.8,21.2,5.5756,2.9044 +Roach,87,18.2,19.8,22.2,5.6166,3.1746 +Roach,120,18.6,20,22.2,6.216,3.5742 +Roach,0,19,20.5,22.8,6.4752,3.3516 +Roach,110,19.1,20.8,23.1,6.1677,3.3957 +Roach,120,19.4,21,23.7,6.1146,3.2943 +Roach,150,20.4,22,24.7,5.8045,3.7544 +Roach,145,20.5,22,24.3,6.6339,3.5478 +Roach,160,20.5,22.5,25.3,7.0334,3.8203 +Roach,140,21,22.5,25,6.55,3.325 +Roach,160,21.1,22.5,25,6.4,3.8 +Roach,169,22,24,27.2,7.5344,3.8352 +Roach,161,22,23.4,26.7,6.9153,3.6312 +Roach,200,22.1,23.5,26.8,7.3968,4.1272 +Roach,180,23.6,25.2,27.9,7.0866,3.906 +Roach,290,24,26,29.2,8.8768,4.4968 +Roach,272,25,27,30.6,8.568,4.7736 +Roach,390,29.5,31.7,35,9.485,5.355 +Whitefish,270,23.6,26,28.7,8.3804,4.2476 +Whitefish,270,24.1,26.5,29.3,8.1454,4.2485 +Whitefish,306,25.6,28,30.8,8.778,4.6816 +Whitefish,540,28.5,31,34,10.744,6.562 +Whitefish,800,33.7,36.4,39.6,11.7612,6.5736 +Whitefish,1000,37.3,40,43.5,12.354,6.525 +Parkki,55,13.5,14.7,16.5,6.8475,2.3265 +Parkki,60,14.3,15.5,17.4,6.5772,2.3142 +Parkki,90,16.3,17.7,19.8,7.4052,2.673 +Parkki,120,17.5,19,21.3,8.3922,2.9181 +Parkki,150,18.4,20,22.4,8.8928,3.2928 +Parkki,140,19,20.7,23.2,8.5376,3.2944 +Parkki,170,19,20.7,23.2,9.396,3.4104 +Parkki,145,19.8,21.5,24.1,9.7364,3.1571 +Parkki,200,21.2,23,25.8,10.3458,3.6636 +Parkki,273,23,25,28,11.088,4.144 +Parkki,300,24,26,29,11.368,4.234 +Perch,5.9,7.5,8.4,8.8,2.112,1.408 +Perch,32,12.5,13.7,14.7,3.528,1.9992 +Perch,40,13.8,15,16,3.824,2.432 +Perch,51.5,15,16.2,17.2,4.5924,2.6316 +Perch,70,15.7,17.4,18.5,4.588,2.9415 +Perch,100,16.2,18,19.2,5.2224,3.3216 +Perch,78,16.8,18.7,19.4,5.1992,3.1234 +Perch,80,17.2,19,20.2,5.6358,3.0502 +Perch,85,17.8,19.6,20.8,5.1376,3.0368 +Perch,85,18.2,20,21,5.082,2.772 +Perch,110,19,21,22.5,5.6925,3.555 +Perch,115,19,21,22.5,5.9175,3.3075 +Perch,125,19,21,22.5,5.6925,3.6675 +Perch,130,19.3,21.3,22.8,6.384,3.534 +Perch,120,20,22,23.5,6.11,3.4075 +Perch,120,20,22,23.5,5.64,3.525 +Perch,130,20,22,23.5,6.11,3.525 +Perch,135,20,22,23.5,5.875,3.525 +Perch,110,20,22,23.5,5.5225,3.995 +Perch,130,20.5,22.5,24,5.856,3.624 +Perch,150,20.5,22.5,24,6.792,3.624 +Perch,145,20.7,22.7,24.2,5.9532,3.63 +Perch,150,21,23,24.5,5.2185,3.626 +Perch,170,21.5,23.5,25,6.275,3.725 +Perch,225,22,24,25.5,7.293,3.723 +Perch,145,22,24,25.5,6.375,3.825 +Perch,188,22.6,24.6,26.2,6.7334,4.1658 +Perch,180,23,25,26.5,6.4395,3.6835 +Perch,197,23.5,25.6,27,6.561,4.239 +Perch,218,25,26.5,28,7.168,4.144 +Perch,300,25.2,27.3,28.7,8.323,5.1373 +Perch,260,25.4,27.5,28.9,7.1672,4.335 +Perch,265,25.4,27.5,28.9,7.0516,4.335 +Perch,250,25.4,27.5,28.9,7.2828,4.5662 +Perch,250,25.9,28,29.4,7.8204,4.2042 +Perch,300,26.9,28.7,30.1,7.5852,4.6354 +Perch,320,27.8,30,31.6,7.6156,4.7716 +Perch,514,30.5,32.8,34,10.03,6.018 +Perch,556,32,34.5,36.5,10.2565,6.3875 +Perch,840,32.5,35,37.3,11.4884,7.7957 +Perch,685,34,36.5,39,10.881,6.864 +Perch,700,34,36,38.3,10.6091,6.7408 +Perch,700,34.5,37,39.4,10.835,6.2646 +Perch,690,34.6,37,39.3,10.5717,6.3666 +Perch,900,36.5,39,41.4,11.1366,7.4934 +Perch,650,36.5,39,41.4,11.1366,6.003 +Perch,820,36.6,39,41.3,12.4313,7.3514 +Perch,850,36.9,40,42.3,11.9286,7.1064 +Perch,900,37,40,42.5,11.73,7.225 +Perch,1015,37,40,42.4,12.3808,7.4624 +Perch,820,37.1,40,42.5,11.135,6.63 +Perch,1100,39,42,44.6,12.8002,6.8684 +Perch,1000,39.8,43,45.2,11.9328,7.2772 +Perch,1100,40.1,43,45.5,12.5125,7.4165 +Perch,1000,40.2,43.5,46,12.604,8.142 +Perch,1000,41.1,44,46.6,12.4888,7.5958 +Pike,200,30,32.3,34.8,5.568,3.3756 +Pike,300,31.7,34,37.8,5.7078,4.158 +Pike,300,32.7,35,38.8,5.9364,4.3844 +Pike,300,34.8,37.3,39.8,6.2884,4.0198 +Pike,430,35.5,38,40.5,7.29,4.5765 +Pike,345,36,38.5,41,6.396,3.977 +Pike,456,40,42.5,45.5,7.28,4.3225 +Pike,510,40,42.5,45.5,6.825,4.459 +Pike,540,40.1,43,45.8,7.786,5.1296 +Pike,500,42,45,48,6.96,4.896 +Pike,567,43.2,46,48.7,7.792,4.87 +Pike,770,44.8,48,51.2,7.68,5.376 +Pike,950,48.3,51.7,55.1,8.9262,6.1712 +Pike,1250,52,56,59.7,10.6863,6.9849 +Pike,1600,56,60,64,9.6,6.144 +Pike,1550,56,60,64,9.6,6.144 +Pike,1650,59,63.4,68,10.812,7.48 +Smelt,6.7,9.3,9.8,10.8,1.7388,1.0476 +Smelt,7.5,10,10.5,11.6,1.972,1.16 +Smelt,7,10.1,10.6,11.6,1.7284,1.1484 +Smelt,9.7,10.4,11,12,2.196,1.38 +Smelt,9.8,10.7,11.2,12.4,2.0832,1.2772 +Smelt,8.7,10.8,11.3,12.6,1.9782,1.2852 +Smelt,10,11.3,11.8,13.1,2.2139,1.2838 +Smelt,9.9,11.3,11.8,13.1,2.2139,1.1659 +Smelt,9.8,11.4,12,13.2,2.2044,1.1484 +Smelt,12.2,11.5,12.2,13.4,2.0904,1.3936 +Smelt,13.4,11.7,12.4,13.5,2.43,1.269 +Smelt,12.2,12.1,13,13.8,2.277,1.2558 +Smelt,19.7,13.2,14.3,15.2,2.8728,2.0672 +Smelt,19.9,13.8,15,16.2,2.9322,1.8792 diff --git a/applications/regression/fig/Evaluation_Index.png b/applications/regression/fig/Evaluation_Index.png new file mode 100644 index 0000000..dd4b824 Binary files /dev/null and b/applications/regression/fig/Evaluation_Index.png differ diff --git a/applications/regression/fig/Linear_Regression.png b/applications/regression/fig/Linear_Regression.png new file mode 100644 index 0000000..6f2c98a Binary files /dev/null and b/applications/regression/fig/Linear_Regression.png differ diff --git a/applications/regression/fig/Poly_Regression.png b/applications/regression/fig/Poly_Regression.png new file mode 100644 index 0000000..4881a15 Binary files /dev/null and b/applications/regression/fig/Poly_Regression.png differ diff --git a/applications/regression/fig/flowchart_CN.png b/applications/regression/fig/flowchart_CN.png new file mode 100644 index 0000000..f2f165a Binary files /dev/null and b/applications/regression/fig/flowchart_CN.png differ diff --git a/applications/regression/fig/flowchart_EN.png b/applications/regression/fig/flowchart_EN.png new file mode 100644 index 0000000..814747d Binary files /dev/null and b/applications/regression/fig/flowchart_EN.png differ diff --git a/applications/regression/introduction_cn.ipynb b/applications/regression/introduction_cn.ipynb new file mode 100644 index 0000000..b74edc0 --- /dev/null +++ b/applications/regression/introduction_cn.ipynb @@ -0,0 +1,324 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "chronic-tunisia", + "metadata": {}, + "source": [ + "## 变分量子回归模型简介\n", + "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*\n", + "\n", + "回归分析(regression analysis)是一种用于估计因变量和一个或多个自变量间相互依赖的定量关系的统计分析方法。回归分析根据自变量的多少,可以分为一元回归和多元回归;根据自变量和因变量间的关系类型,可以分为线性回归和非线性回归。回归分析被广泛应用于预测及推断变量间的因果关系(如预测人口增长趋势),对各学科领域的发展起到重要推动作用。\n", + "\n", + "变分量子回归(variational quantum regression, VQR)是一个在监督学习框架下的量子-经典混合算法。VQR 结合了经典与量子的优势,通过将数据编码为量子态,经过量子电路的演化,再结合经典机器学习中的优化算法(如梯度下降法)不断调整参数使得损失函数最小化,从而得到拟合函数。\n", + "\n", + "本教程首先介绍了 VQR 的原理,接着展示了利用量桨进行线性回归和高阶多项式回归的效果,最后介绍了如何利用量桨实现 VQR。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8429d648", + "metadata": {}, + "source": [ + "## 模型原理简介\n", + "\n", + "以多项式函数拟合为例,其训练步骤主要可以分为:**输入** -> **信息预处理** -> **优化迭代** -> **输出**,四个主要部分。用户只需输入所需回归分析的经典数据集(为 ``.csv`` 文件)以及想要拟合的函数的阶 $k$。VQR 模型将初始化相应模型,\n", + "$$y = c_0 + c_1x^1 + c_2 x^2 + \\cdots + c_k x^k,$$\n", + "同时自动将经典数据集融合迭代参数(也就是多项式系数 $\\bm{c}$)输入进量子设备并进行算法优化迭代,直到损失函数收敛。输出完成优化的参数 $\\bm{c}^*$ 以及相应的模型,以便后续使用。\n", + "\n", + "VQR 流程图如下:\n", + "\n", + "![flowchart](./fig/flowchart_CN.png \"图1:变分量子回归流程图。\")\n", + "
VQR 流程图
" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2f0070ae", + "metadata": {}, + "source": [ + "## 模型效果" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "7fb04ce4", + "metadata": {}, + "source": [ + "我们通常采用均方误差(mean squared error, MSE)、平均绝对误差(mean absolute error, MAE)和可决系数(R-squared)来评价回归效果,其中 MSE 可以评价数据的变化程度,MSE 越小,说明拟合函数在预测数据上具有更好的精确度;MAE 反映预测值误差的实际情况,MAE 越小,说明拟合效果越好;可决系数衡量了回归函数整体的拟合度,越接近于 $1$ 说明拟合效果越好。\n", + "\n", + "利用量桨拟合简单的线性函数和三阶多项式函数效果图如下,可以看到两者的可决系数都接近于 $1$,说明拟合效果较好。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "f1e406c1", + "metadata": {}, + "source": [ + "![LR](./fig/Evaluation_Index.png \"拟合效果图\")\n", + "拟合效果图:(a) 图表示拟合线性函数 $y=x+c_1$ 的效果图($c_1$ 为属于 $[-1,1)$ 的噪声);(b) 图表示拟合多项式函数 $y=(2x-1)^3+c_2$ 的效果图($c_2$ 为属于 $[-1,1)$ 的噪声);(c) 图为拟合效果评价指标。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "17661d06", + "metadata": {}, + "source": [ + "## 模型演示\n", + "\n", + "我们已经给出了两个设置好的参数,可以直接用于线性和多项式回归分析。只需要在 `linear.toml` 和 `poly.toml` 配置文件中进行对应的配置,然后输入命令 `python vqr_analysis.py --config linear(poly).toml` 即可进行线性或多项式变分量子回归。\n", + "\n", + "这里我们利用 VQR 进行 `poly` 模型演示来拟合不同种类鱼的重量和宽度之间的关系,其中将鱼的宽度设置为自变量 $x$,重量设置为因变量 $y$。首先按照如下代码来配置环境:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "eb7a2be4", + "metadata": {}, + "outputs": [], + "source": [ + "# 安装量桨\n", + "# %pip install paddle-quantum" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "0de32c6b", + "metadata": {}, + "outputs": [], + "source": [ + "poly_toml = r\"\"\"\n", + "# 模型的整体配置文件。\n", + "# 输入当前的任务,可以是 'linear' 或者 'poly', 分别代表线性和多项式回归。这里我们使用 poly, 进行回归分析。\n", + "# 用于分析的模型类型。\n", + "model_name = 'poly'\n", + "# 要分析的 .csv 文件路径。\n", + "data_file = './datasets/Fish.csv'\n", + "# 需要分析的自变量数据名称 List。\n", + "x_feature = ['Width']\n", + "# 因变量数据名称。\n", + "y_feature = ['Weight']\n", + "# 所需优化参数量。对于线性回归模型,其值等于自变量数量;对于多项式模型,其值代表多项式阶数。\n", + "num_variable = 3\n", + "# 初始优化参数值。参数数量需等于 num_variable + 1。\n", + "init_params = [0.0, 0.45, 0.5, 0.5]\n", + "# 所需量子比特数。默认为 6。\n", + "num_qubits = 6\n", + "# 优化过程学习率。默认为 0.1。\n", + "learning_rate = 0.1\n", + "# 优化总迭代步数。默认为 100。\n", + "iteration = 100\n", + "# 打印的语言。默认为 'CN'。\n", + "language = 'CN'\n", + "\"\"\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "1afb7e34", + "metadata": {}, + "source": [ + "量桨 PaddleQuantum 的数据处理模块存有量子数据处理工具。我们可以从 `paddle_quantum.data_analysis.vqr` 模块里导入 `QRegressionModel` 来进行数据的回归分析。" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "53a5f59a", + "metadata": {}, + "outputs": [], + "source": [ + "# 导入所需要的包\n", + "import os\n", + "import warnings\n", + "import toml\n", + "\n", + "warnings.filterwarnings('ignore')\n", + "os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python'\n", + "\n", + "import paddle_quantum as pq\n", + "from paddle_quantum.data_analysis.vqr import QRegressionModel\n", + "poly_config = toml.loads(poly_toml)\n", + "\n", + "pq.set_backend(\"state_vector\")\n", + "pq.set_dtype(\"complex128\")" + ] + }, + { + "cell_type": "markdown", + "id": "4edaf932", + "metadata": {}, + "source": [ + "### 高阶多项式回归" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "2e3be304", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "模型是否被训练:False。当前模型 R2 得分:-0.51802。\n", + "拟合后的模型为:Weight = 0.00000 + (0.45000*Width^1) + (0.50000*Width^2) + (0.50000*Width^3)。\n" + ] + } + ], + "source": [ + "# 初始化回归模型\n", + "poly_model = QRegressionModel(**poly_config)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "97e8760c", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|##########| 100/100 [00:02<00:00, 43.36it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "模型是否被训练:True。当前模型 R2 得分:0.74693。\n", + "拟合后的模型为:Weight = -0.04323 + (1.20800*Width^1) + (1.83084*Width^2) + (2.22695*Width^3)。\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# 模型训练和结果\n", + "fitted_poly_estimator = poly_model.regression_analyse()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "f8a3ebf3", + "metadata": {}, + "source": [ + "通过修改配置文件的内容,并运行分析代码,即可在线对模型进行测试。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8f6f3b91", + "metadata": {}, + "source": [ + "## 注意事项\n", + "\n", + "模型的最终训练效果与初始参数的选取有很大的关系,实际情形下需要考虑不同初始参数的情形以达到最好的拟合效果。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4857182b", + "metadata": {}, + "source": [ + "## 引用信息\n", + "\n", + "```\n", + "@article{bravo2019variational,\n", + " title={Variational quantum linear solver},\n", + " author={Bravo-Prieto, Carlos and LaRose, Ryan and Cerezo, Marco and Subasi, Yigit and Cincio, Lukasz and Coles, Patrick J},\n", + " journal={arXiv preprint arXiv:1909.05820},\n", + " year={2019}\n", + "}\n", + "```\n", + "\n", + "```\n", + "@article{chicco2021coefficient,\n", + " title={The coefficient of determination R-squared is more informative than SMAPE, MAE, MAPE, MSE and RMSE in regression analysis evaluation},\n", + " author={Chicco, Davide and Warrens, Matthijs J and Jurman, Giuseppe},\n", + " journal={PeerJ Computer Science},\n", + " volume={7},\n", + " pages={e623},\n", + " year={2021},\n", + " publisher={PeerJ Inc.}\n", + "}\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "135dcf17", + "metadata": {}, + "source": [ + "## 参考文献\n", + "\n", + "[1] Bravo-Prieto, Carlos, et al. \"Variational quantum linear solver.\" arXiv preprint arXiv:1909.05820 (2019).\n", + "\n", + "[2] Chicco, Davide, Matthijs J. Warrens, and Giuseppe Jurman. \"The coefficient of determination R-squared is more informative than SMAPE, MAE, MAPE, MSE and RMSE in regression analysis evaluation.\" PeerJ Computer Science 7 (2021): e623." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pq_model", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.15 (default, Nov 24 2022, 18:44:54) [MSC v.1916 64 bit (AMD64)]" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "vscode": { + "interpreter": { + "hash": "f72ee4b6f68a242207141d57b3cf31c0f7a33872846f5d3d99c291777ab7c6d3" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/applications/regression/introduction_en.ipynb b/applications/regression/introduction_en.ipynb new file mode 100644 index 0000000..84d742b --- /dev/null +++ b/applications/regression/introduction_en.ipynb @@ -0,0 +1,345 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "chronic-tunisia", + "metadata": {}, + "source": [ + "## Introduction - Variational quantum regression\n", + "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*\n", + "\n", + "Regression analysis(RA)is a statistical method used to quantitatively estimate the interrelationship among multiple variables. There are different types of regression models based on the number of independent variables, named single- and multiple-variable regression, and distinct relationships among dependent and independent variables, such as linear and polynomial regression. RA is widely applied to build up the causal connections between variables in the physical datasets, which then could be accessed to determine and predict the outcomes from a further unknown data configuration (such as predicting population growth trends). It plays a significant role in the development of scientific aspects.\n", + "\n", + "Variational quantum regression (VQR) is a hybrid quantum-classical algorithm embedded in the supervised-learning framework. It combines the advantages from both the classical and quantum aspects. By encoding the classical data into a quantum state followed by an evolution of the state via a quantum circuit, we could successively adjust the parameters of the circuit based on some classical optimization method (e.g. gradient decent) to minimize the regression loss, and at last derive the explicit expression that best fits the dataset.\n", + "\n", + "In this tutorial, we introduce the VQR model based on the ``paddle_quantum`` library. We give a brief interpretation of the performance of using our regression model to analyze a physical Fish-species dataset in the following notebook. Please enjoy your journey." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8429d648", + "metadata": {}, + "source": [ + "## VQR in a nutshell\n", + "\n", + "We take a polynomial regression task as an example. The entire process can be separated into four main parts, including, **Input** -> **Pre-process** -> **Optimization** -> **Output**. Users only need to prepare a classical dataset (as a ``.csv`` file) and an order $k$ of the fitted function. Our program will initialize a corresponding model in the form of,\n", + "$$y = c_0 + c_1x^1 + c_2 x^2 + \\cdots + c_k x^k,$$\n", + "and at the same time, automatically combine the classical data and the iterative parameters (the coefficients $\\bm{c}$) into our quantum devices to run the optimization algorithm, until the cost function converges. Then the program output all the optimized parameters $\\bm{c}^*$ together with the regression model for further usages.\n", + "\n", + "The flowchart of VQR is as follows.\n", + "\n", + "![flowchart](./fig/flowchart_EN.png \"图1:变分量子回归流程图。\")\n", + "
VQR flowchart
" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2f0070ae", + "metadata": {}, + "source": [ + "## Effectiveness of VQR model" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "62e20ab0", + "metadata": {}, + "source": [ + "We usually use mean squared error (MSE), mean absolute error (MAE) and coefficient of determination (i.e., R-squared) to evaluate the regression performance. MSE can evaluate the degree of change of data. The smaller MSE is, the higher the accuracy of the fitting function is. MAE reflects the actual situation of predicted value error. The smaller MAE is, the better fitting performance is. The coefficient of determination measures the overall fitting accuracy of the regression function. The closer it is to $1$, the better the fitting performance will be.\n", + "\n", + "The results of fitting simple linear function and third-order polynomial function using Paddle Quantum are as follows. It can be seen that the coefficients of determination of both are close to $1$, indicating good fitting performance." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "098a09d6", + "metadata": {}, + "source": [ + "![LR](./fig/Evaluation_Index.png \"Results of fitting\")\n", + "Results of fitting: (a) is the result of fitting the linear function $y=x+c_1$ (c1 is a random noise belonging to $[-1,1)$); (b) is the result of fitting the third-order polynomial function $y=(2x-1)^3+c_2$ ($c_2$ is a random noise belonging to $[-1,1)$); (c) gives the values of some fitting evaluation indices." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "17661d06", + "metadata": {}, + "source": [ + "## How to use\n", + "\n", + "" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "b2e7a0e8", + "metadata": {}, + "source": [ + "We have set two parameters that can be used directly for linear and polynomial regression. Just configure it in the configuration files `linear.toml` and `poly.toml`, then enter the command \n", + "`python vqr_analysis.py --config linear(poly).toml`.\n", + "\n", + "Here, we give a version of a `poly` model to fit the relationship between the width and weight of different fishes. We set the fish width as an independent variable and set the fish weight as a dependent variable. First define the contents of the configuration file:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "eb7a2be4", + "metadata": {}, + "outputs": [], + "source": [ + "# Install paddle_quantum\n", + "# %pip install paddle-quantum" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "6c5b25a2", + "metadata": {}, + "outputs": [], + "source": [ + "poly_toml = r\"\"\"\n", + "# The total configuration file of regression mode.\n", + "# Input current task, should be either 'linear' or 'poly', for linear or polynomial regression.\n", + "# The used regression model name.\n", + "model_name = 'poly'\n", + "# The saved path of 'dataset.csv'.\n", + "data_file = './datasets/Fish.csv'\n", + "# The list of independent variable names in the dataset.\n", + "x_feature = ['Width']\n", + "# The name of dependent variable.\n", + "y_feature = ['Weight']\n", + "# The number of optimized variables.\n", + "num_variable = 3\n", + "# Initialization of the optimized variables.\n", + "init_params = [0.0, 0.45, 0.5, 0.5]\n", + "# The number of required qubits. Defaults to 6.\n", + "num_qubits = 6\n", + "# The learning rate of optimization. Defaults to 0.1.\n", + "learning_rate = 0.1\n", + "# The total number of optimization iterations. Defaults to 100.\n", + "iteration = 100\n", + "# The print language. Defaults to 'CN'.\n", + "language = 'EN'\n", + "\"\"\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bdd5e365", + "metadata": {}, + "source": [ + "PaddleQuantum has data analysis package containing the quantum data analysis tools. Users can import `QRegressionModel` from `paddle_quantum.data_analysis.vqr` to perform regression analysis." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "53a5f59a", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "d:\\programs\\Anaconda\\envs\\pq_model\\lib\\site-packages\\paddle\\tensor\\creation.py:125: DeprecationWarning: `np.object` is a deprecated alias for the builtin `object`. To silence this warning, use `object` by itself. Doing this will not modify any behavior and is safe. \n", + "Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations\n", + " if data.dtype == np.object:\n" + ] + } + ], + "source": [ + "# import libs\n", + "import os\n", + "import warnings\n", + "import toml\n", + "\n", + "warnings.filterwarnings('ignore')\n", + "os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python'\n", + "\n", + "import paddle_quantum as pq\n", + "from paddle_quantum.data_analysis.vqr import QRegressionModel\n", + "poly_config = toml.loads(poly_toml)\n", + "\n", + "pq.set_backend(\"state_vector\")\n", + "pq.set_dtype(\"complex128\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "f09ef329", + "metadata": {}, + "source": [ + "### Polynomial regression" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "5f6d56d5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model status: False. Current regression R2 score: -0.51802. \n", + "The trained model: Weight = 0.00000 + (0.45000*Width^1) + (0.50000*Width^2) + (0.50000*Width^3). \n" + ] + } + ], + "source": [ + "# Initialize regression model\n", + "poly_model = QRegressionModel(**poly_config)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "1dda91b6", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|##########| 100/100 [00:05<00:00, 19.99it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model status: True. Current regression R2 score: 0.74693. \n", + "The trained model: Weight = -0.04323 + (1.20800*Width^1) + (1.83084*Width^2) + (2.22695*Width^3). \n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Training and testing\n", + "fitted_poly_estimator = poly_model.regression_analyse()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "3432ba48", + "metadata": {}, + "source": [ + "We could test the model online by modifying the configuration file and running the program." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8f6f3b91", + "metadata": {}, + "source": [ + "## Caution\n", + "\n", + "In the practical cases, the fitting performance could be influenced by different initialization values of the parameters. It has to consider various initialized strategies to achieve a best fitting." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4857182b", + "metadata": {}, + "source": [ + "## Reference information\n", + "\n", + "```\n", + "@article{bravo2019variational,\n", + " title={Variational quantum linear solver},\n", + " author={Bravo-Prieto, Carlos and LaRose, Ryan and Cerezo, Marco and Subasi, Yigit and Cincio, Lukasz and Coles, Patrick J},\n", + " journal={arXiv preprint arXiv:1909.05820},\n", + " year={2019}\n", + "}\n", + "```\n", + "\n", + "```\n", + "@article{chicco2021coefficient,\n", + " title={The coefficient of determination R-squared is more informative than SMAPE, MAE, MAPE, MSE and RMSE in regression analysis evaluation},\n", + " author={Chicco, Davide and Warrens, Matthijs J and Jurman, Giuseppe},\n", + " journal={PeerJ Computer Science},\n", + " volume={7},\n", + " pages={e623},\n", + " year={2021},\n", + " publisher={PeerJ Inc.}\n", + "}\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "135dcf17", + "metadata": {}, + "source": [ + "## References\n", + "\n", + "[1] Bravo-Prieto, Carlos, et al. \"Variational quantum linear solver.\" arXiv preprint arXiv:1909.05820 (2019).\n", + "\n", + "[2] Chicco, Davide, Matthijs J. Warrens, and Giuseppe Jurman. \"The coefficient of determination R-squared is more informative than SMAPE, MAE, MAPE, MSE and RMSE in regression analysis evaluation.\" PeerJ Computer Science 7 (2021): e623." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pq-dev", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.15 (default, Nov 10 2022, 13:17:42) \n[Clang 14.0.6 ]" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "vscode": { + "interpreter": { + "hash": "5fea01cac43c34394d065c23bb8c1e536fdb97a765a18633fd0c4eb359001810" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/applications/regression/linear.toml b/applications/regression/linear.toml new file mode 100644 index 0000000..b390271 --- /dev/null +++ b/applications/regression/linear.toml @@ -0,0 +1,38 @@ +# The total configuration file of regression mode. +# Input current task, should be either 'linear' or 'poly', for linear or polynomial regression. + +# The used regression model name. +model_name = 'linear' + +# The saved path of 'dataset.csv'. +data_file = './datasets/Fish.csv' + +# The list of independent variable names in the dataset. +x_feature = ['Length2', 'Length3', 'Height'] + +# The name of dependent variable. +y_feature = ['Length1'] + +# The number of optimized variables. +# NOTE: For linear regression, it gives the number of independent variables. +# NOTE: poly regression, it gives the degree of the polynomial. +num_variable = 3 + +# Initialization of the optimized variables. +# NOTE: The number of params should equal to num_variable + 1. +init_params = [0.0, 0.45, 0.5, 0.5] + +# The number of required qubits. Defaults to 6. +num_qubits = 6 + +# The learning rate of optimization. Defaults to 0.1. +learning_rate = 0.1 + +# The total number of optimization iterations. Defaults to 100. +iteration = 100 + +# The print language. Defaults to 'CN'. +language = 'CN' + + + diff --git a/applications/regression/poly.toml b/applications/regression/poly.toml new file mode 100644 index 0000000..300032c --- /dev/null +++ b/applications/regression/poly.toml @@ -0,0 +1,35 @@ +# The total configuration file of regression mode. +# Input current task, should be either 'linear' or 'poly', for linear or polynomial regression. + +# The used regression model name. +model_name = 'poly' + +# The saved path of 'dataset.csv'. +data_file = './datasets/Fish.csv' + +# The list of independent variable names in the dataset. +x_feature = ['Width'] + +# The name of dependent variable. +y_feature = ['Weight'] + +# The number of optimized variables. +# NOTE: For linear regression, it gives the number of independent variables. +# NOTE: For poly regression, it gives the degree of the polynomial. +num_variable = 3 + +# Initialization of the optimized variables. +# NOTE: The number of params should equal to num_variable + 1. +init_params = [0.0, 0.45, 0.5, 0.5] + +# The number of required qubits. Defaults to 6. +num_qubits = 6 + +# The learning rate of optimization. Defaults to 0.1. +learning_rate = 0.1 + +# The total number of optimization iterations. Defaults to 100. +iteration = 100 + +# The print language. Defaults to 'CN'. +language = 'CN' \ No newline at end of file diff --git a/applications/regression/vqr_analysis.py b/applications/regression/vqr_analysis.py new file mode 100644 index 0000000..d8f2ff8 --- /dev/null +++ b/applications/regression/vqr_analysis.py @@ -0,0 +1,49 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +The VQR app cmdline file. +""" + +import argparse +import os +import warnings + +warnings.filterwarnings("ignore") +os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = "python" + +import toml +from paddle_quantum.data_analysis.vqr import QRegressionModel +import paddle_quantum as pq + +pq.set_backend("state_vector") +pq.set_dtype("complex128") + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Performing regression analysis on Fish dataset.") + parser.add_argument("--config", type=str, help="Input the config file with toml format.") + args = parser.parse_args() + config = toml.load(args.config) + + if config["model_name"] == "linear": + linear_model = QRegressionModel(**config) + fitted_linear_estimator = linear_model.regression_analyse() + + elif config["model_name"] == "poly": + poly_model = QRegressionModel(**config) + fitted_poly_estimator = poly_model.regression_analyse() + else: + raise ValueError("Unknown task. Should be either 'linear' or 'poly' for current usage.") diff --git a/applications/text_classification/example.toml b/applications/text_classification/example.toml new file mode 100644 index 0000000..1d16c9a --- /dev/null +++ b/applications/text_classification/example.toml @@ -0,0 +1,11 @@ +task = 'test' +text = '奔驰GLS怎么样?' +model_path = 'qsann.pdparams' +vocab_path = 'headlines500/vocab.txt' +num_qubits = 6 +num_layers = 1 +depth_ebd = 1 +depth_query = 1 +depth_key = 1 +depth_value = 1 +classes = ['房地产行业', '汽车行业'] diff --git a/applications/text_classification/headlines500/test.txt b/applications/text_classification/headlines500/test.txt new file mode 100644 index 0000000..41ddae0 --- /dev/null +++ b/applications/text_classification/headlines500/test.txt @@ -0,0 +1,100 @@ +吃 药 驾 车 有 危 险 1 +南 宁 的 城 建 怎 么 样 ? 0 +长 春 建 筑 学 院 怎 么 样 ? 0 +司 机 师 傅 , 你 还 好 吗 1 +嘉 兴 南 湖 — 路 劲 金 茂 府 0 +邢 台 市 的 房 价 正 常 吗 ? 0 +房 贷 审 批 需 要 多 久 呢 ? 0 +汽 车 如 何 修 改 电 路 ? 1 +无 人 驾 驶 是 未 来 1 +“ 大 白 ” X T 5 提 回 家 1 +滴 滴 不 顺 风 1 +如 何 评 价 襄 阳 房 价 ? 0 +如 何 评 价 林 肯 的 车 ? 1 +水 箱 开 锅 怎 么 办 ? 1 +应 该 买 学 区 房 吗 ? 0 +“ 它 ” 和 她 一 见 钟 情 1 +西 安 部 分 楼 盘 库 存 播 报 0 +绝 地 求 生 沙 漠 飙 车 赛 高 1 +徐 州 动 物 园 自 驾 游 玩 1 +现 在 买 房 子 真 的 好 么 ? 0 +玛 莎 拉 蒂 为 何 那 么 贵 ? 1 +家 装 效 果 图 C P 组 合 0 +如 何 学 好 科 目 二 ? 1 +三 十 万 买 什 么 二 手 车 ? 1 +八 达 岭 孔 雀 城 图 集 0 +成 都 有 地 铁 吗 ? 0 +红 旗 H 5 这 车 怎 么 样 ? 1 +拆 个 变 速 箱 给 你 们 看 看 1 +被 打 车 主 下 午 回 应 1 +原 报 道 | 金 地 旧 改 新 局 0 +现 在 一 定 要 买 房 吗 ? 0 +衡 水 市 房 价 会 回 落 吗 ? 0 +W E Y P 8 : 我 超 凶 ! 1 +小 产 权 房 子 有 风 险 吗 ? 0 +本 田 X R - V 多 少 钱 ? 1 +卖 房 要 收 税 吗 ? 0 +幻 速 s 6 的 性 能 如 何 ? 1 +重 庆 是 一 线 城 市 吗 ? 0 +无 锡 水 韵 金 阁 0 +奥 迪 官 降 : 不 止 于 价 1 +实 习 销 售 感 悟 1 +成 都 红 树 湾 楼 盘 好 吗 ? 0 +抚 顺 适 宜 居 住 吗 ? 0 +房 产 销 售 入 门 三 步 走 0 +滴 滴 , 让 安 全 伴 您 左 右 1 +二 手 房 好 卖 吗 ? 0 +衡 阳 房 价 7 千 正 常 吗 ? 0 +小 产 权 房 子 怎 么 交 易 ? 0 +如 何 选 择 踏 板 摩 托 ? 1 +分 享 一 套 二 层 别 墅 图 纸 0 +零 成 本 处 理 车 辆 异 响 1 +如 何 避 免 车 辆 自 燃 1 +直 线 倒 车 该 怎 么 倒 ? 1 +石 家 庄 房 价 算 不 算 贵 ? 0 +你 喜 欢 你 的 房 吗 ? 0 +菏 泽 市 是 几 线 城 市 ? 0 +汽 修 技 术 哪 家 好 ? 1 +兰 州 新 区 适 合 定 居 吗 ? 0 +科 目 二 倒 车 入 库 ? 1 +无 锡 这 个 城 市 怎 么 样 ? 0 +大 众 新 迈 腾 怎 么 样 ? 1 +这 款 旅 行 车 , 真 的 豪 华 1 +永 康 房 价 还 会 涨 吗 ? 0 +关 于 要 不 要 买 房 ? 0 +房 地 产 开 发 商 团 购 公 司 0 +武 汉 现 在 适 合 买 房 吗 ? 0 +汽 电 动 车 靠 谱 吗 ? 1 +电 动 车 对 人 有 幅 射 吗 ? 1 +调 节 好 自 己 的 心 理 0 +现 在 适 合 买 房 吗 ? 0 +途 观 L 和 冠 道 怎 么 选 ? 1 +正 在 施 工 的 高 速 公 路 1 +怎 么 看 待 济 南 的 房 价 ? 0 +首 付 三 成 ? 老 黄 历 了 0 +吉 利 远 景 怎 么 样 ? 1 +汽 车 业 跨 界 合 作 成 主 流 1 +轻 骑 摩 托 怎 么 样 ? 1 +买 卖 房 如 何 选 中 介 ? 0 +宝 骏 5 3 0 , 再 放 大 招 1 +宾 智 的 车 怎 样 ? 1 +七 彩 云 南 一 元 谋 土 林 1 +名 车 标 志 及 来 历 ( 下 ) 1 +如 何 买 二 手 车 ? 1 +购 房 六 大 注 意 事 项 ! 0 +珠 海 市 房 价 是 多 少 ? 0 +郑 州 名 吃 是 什 么 ? 0 +大 风 天 气 用 车 注 意 事 项 1 +房 产 税 怎 么 个 收 法 ? 0 +节 气 门 多 久 洗 一 次 ? 1 +验 房 时 应 该 怎 么 做 呢 ? 0 +楼 市 限 购 下 的 北 京 中 年 0 +房 价 真 跌 ? 假 摔 ! 0 +学 车 大 概 要 多 少 天 ? 1 +如 何 能 在 桂 林 买 房 ? 0 +合 资 摩 托 哪 家 强 ? 1 +客 车 遇 险 如 何 逃 生 ? 1 +西 宁 哪 里 卖 摩 托 车 ? 1 +保 时 捷 为 什 么 这 么 贵 1 +过 渡 安 置 房 火 热 建 设 中 0 +什 么 是 存 量 房 ? 0 diff --git a/applications/text_classification/headlines500/train.txt b/applications/text_classification/headlines500/train.txt new file mode 100644 index 0000000..8ab3f64 --- /dev/null +++ b/applications/text_classification/headlines500/train.txt @@ -0,0 +1,400 @@ +国 三 和 国 四 怎 么 区 分 ? 1 +人 佩 玉 的 原 因 有 哪 些 ? 1 +威 海 的 富 人 区 在 哪 呢 ? 0 +贵 州 第 一 位 买 汽 车 的 人 1 +未 来 会 有 空 中 汽 车 吗 ? 1 +绵 阳 碧 桂 园 怎 么 样 ? 0 +旅 游 房 产 前 景 如 何 ? 0 +盘 锦 是 几 线 城 市 ? 0 +郑 州 租 房 价 格 降 了 吗 ? 0 +周 口 未 来 房 价 怎 样 ? 0 +如 何 看 待 即 墨 的 房 价 ? 0 +成 都 哪 里 适 合 买 房 ? 0 +如 何 看 待 常 州 的 房 价 ? 0 +如 何 选 房 子 ? 0 +如 何 安 装 倒 车 影 像 ? 1 +房 车 多 少 钱 ? 1 +溜 车 ? 不 用 怕 ! 1 +本 土 B 级 车 如 何 致 胜 ? 1 +永 川 房 价 会 涨 吗 ? 0 +珠 海 哪 里 租 房 便 宜 ? 0 +兰 州 的 房 价 会 跌 吗 ? 0 +海 南 的 生 态 环 境 如 何 ? 0 +贝 加 尔 湖 本 是 中 国 的 ? 0 +车 联 网 在 风 口 上 吗 ? 1 +买 房 贷 款 为 何 叫 按 揭 ? 0 +上 海 部 分 二 手 房 降 价 0 +单 词 轻 松 记 第 1 0 5 天 1 +难 道 我 买 的 是 假 宝 骏 ? 1 +珠 海 哪 个 驾 校 好 ? 1 +雪 铁 龙 的 “ 命 运 史 ” 1 +没 有 驾 照 可 以 买 车 吗 ? 1 +银 川 哪 个 区 房 子 便 宜 ? 0 +合 肥 的 房 价 高 吗 ? 0 +盛 鸣 初 起 , 名 贯 盛 京 0 +汽 车 美 容 行 业 如 何 ? 1 +这 房 子 , 难 怪 房 东 要 卖 0 +宅 基 地 可 以 转 让 吗 ? 0 +大 众 新 桑 塔 纳 怎 么 样 ? 1 +茶 山 , 庆 丰 7 栋 花 园 区 0 +练 车 , 练 车 , 练 车 1 +通 往 天 堂 的 客 车 1 +车 缝 塞 得 进 手 指 1 +学 会 这 些 远 离 追 尾 1 +远 景 这 车 怎 么 样 ? 1 +厉 害 了 ! 我 的 国 ! 1 +丹 东 不 懂 深 圳 的 伤 悲 ! 0 +2 0 1 8 房 价 怎 么 走 ? 0 +家 用 车 都 上 什 么 保 险 ? 1 +房 子 为 什 么 要 降 呢 ? 0 +小 产 权 房 的 前 景 如 何 ? 0 +铜 陵 , 雨 后 的 江 边 … … 1 +北 京 上 调 首 套 房 贷 利 率 0 +广 汽 传 祺 质 量 怎 么 样 ? 1 +迷 之 粘 贴 可 以 么 1 +成 都 最 高 楼 有 多 少 米 ? 0 +重 庆 的 房 价 还 会 涨 吗 ? 0 +变 频 器 在 行 车 上 的 应 用 1 +温 暖 龙 岩 ! 0 +洒 大 地 上 放 1 +大 风 天 气 交 通 安 全 提 示 1 +亚 洲 十 大 超 级 豪 宅 赏 析 0 +现 在 做 房 产 销 售 如 何 ? 0 +汽 车 全 身 打 满 铜 钉 1 +房 地 产 成 本 核 算 课 程 二 0 +荷 兰 房 租 爆 涨 0 +奔 驰 G L S 怎 么 样 ? 1 +十 里 春 风 - 留 在 北 京 0 +3 5 万 以 内 房 车 ? 1 +房 产 交 易 费 用 ? 0 +科 目 二 S 弯 教 程 1 +十 年 后 房 子 会 贬 值 吗 ? 0 +如 何 评 价 全 新 X 5 ? 1 +等 你 哦 法 拉 利 1 +各 国 领 导 人 座 驾 1 +房 地 产 成 本 核 销 三 0 +七 十 年 后 房 子 怎 么 办 ? 0 +看 着 这 些 照 片 1 +保 定 楼 市 5 月 开 盘 预 告 0 +中 国 最 保 值 车 型 排 行 榜 1 +川 藏 线 穿 越 之 旅 第 一 天 1 +买 房 子 要 注 意 什 么 ? 0 +男 女 住 在 一 起 违 法 么 ? 0 +如 何 看 待 新 乡 的 房 价 ? 0 +开 车 需 防 静 电 1 +二 手 车 怎 么 算 价 格 ? 1 +嘉 兴 孔 雀 城 怎 么 样 ? 0 +贵 阳 的 “ 融 万 之 争 ” 0 +买 黄 金 会 没 用 吗 ? 0 +如 何 看 待 汕 头 的 房 价 ? 0 +3 个 售 楼 人 员 0 +为 什 么 斯 柯 达 卖 不 好 ? 1 +自 贡 市 的 房 价 会 跌 吗 ? 0 +鹰 潭 属 于 几 线 城 市 ? 0 +泰 国 别 墅 怎 么 租 ? 0 +怎 么 操 作 自 动 挡 ? 1 +“ 鸡 贼 ” 的 北 京 院 子 0 +昌 黎 县 房 价 怎 么 样 ? 0 +大 通 好 还 是 全 顺 好 ? 1 +无 锡 哪 里 的 楼 盘 最 贵 ? 0 +直 接 买 现 房 好 不 好 ? 0 +自 己 洗 车 要 注 意 什 么 ? 1 +挑 别 墅 教 你 五 招 0 +科 目 三 怎 样 轻 松 过 ? 1 +苏 州 房 价 您 怎 么 看 ? 0 +变 速 箱 异 响 都 有 哪 些 ? 1 +西 安 房 价 走 势 如 何 ? 0 +好 地 块 必 须 得 “ 抢 ” ! 0 +高 层 住 宅 几 层 最 方 便 ? 0 +房 车 旅 游 来 了 ! 1 +沪 州 的 房 价 怎 么 样 ? 0 +涡 轮 增 压 发 动 机 汽 车 ? 1 +五 菱 宏 光 怎 么 样 ? 1 +长 安 奔 奔 方 向 盘 特 别 沉 1 +科 沃 兹 这 车 怎 么 样 ? 1 +安 全 行 车 三 字 经 1 +江 西 三 清 山 自 驾 游 游 玩 1 +公 司 买 房 破 限 购 总 结 0 +如 何 给 刹 车 油 放 气 ? 1 +如 何 看 待 佛 山 的 房 价 ? 0 +郑 州 西 区 前 景 如 何 ? 0 +安 全 倒 车 9 不 要 1 +右 转 的 车 如 坦 克 1 +天 津 楼 市 两 极 分 化 加 剧 0 +什 么 时 候 买 车 最 划 算 ? 1 +保 险 定 损 旧 痕 怎 么 算 ? 1 +汽 修 店 都 有 哪 些 内 幕 ? 1 +唐 山 现 在 买 房 合 适 吗 ? 0 +韩 系 车 好 吗 ? 1 +巴 中 这 个 城 市 怎 么 样 ? 0 +兰 博 基 尼 U r u s 1 +为 什 么 房 子 越 来 越 贵 ? 0 +练 习 半 坡 起 步 费 电 吗 ? 1 +青 岛 房 价 明 年 会 长 吗 ? 0 +在 成 都 哪 里 买 房 好 ? 0 +什 么 情 况 要 委 屈 自 己 ? 1 +夏 天 早 上 需 要 热 车 吗 ? 1 +窑 湾 古 镇 自 驾 游 1 +仁 寿 的 房 价 是 虚 高 吗 ? 0 +财 源 国 际 中 心 出 租 0 +炒 房 团 的 套 路 有 哪 些 ? 0 +关 于 楼 房 采 光 问 题 ? 0 +独 立 悬 挂 有 哪 几 种 ? 1 +郑 州 为 什 么 不 建 高 楼 ? 0 +房 地 产 税 改 革 渐 行 渐 近 0 +迈 腾 值 得 入 手 吗 ? 1 +比 速 乐 享 幸 福 远 航 1 +汽 车 贴 膜 有 必 要 吗 ? 1 +5 万 买 桑 塔 纳 怎 么 样 ? 1 +滴 滴 司 机 杀 害 空 姐 事 件 1 +平 行 进 口 有 哪 些 老 车 ? 1 +车 子 性 能 表 现 怎 么 样 ? 1 +途 锐 和 x 5 哪 个 好 ? 1 +日 产 途 达 有 什 么 缺 陷 ? 1 +带 刺 的 槐 树 花 1 +如 何 考 过 科 目 二 ? 1 +汽 车 保 养 周 期 是 多 久 ? 1 +前 风 挡 外 面 起 雾 咋 办 ? 1 +学 习 榜 样 碧 桂 园 ! 0 +三 个 售 楼 人 员 0 +红 旗 — — 国 产 首 长 专 车 1 +龙 岗 万 科 广 场 5 1 2 0 +空 姐 案 件 以 破 ( 快 讯 ) 1 +房 子 买 在 嘉 兴 哪 里 好 ? 0 +开 车 是 女 人 的 弱 项 吗 ? 1 +遇 到 了 迷 你 狗 怎 么 办 ? 1 +人 性 的 致 命 点 之 一 ! 1 +养 飞 度 一 年 要 多 少 钱 ? 1 +一 楼 的 房 子 可 以 买 吗 ? 0 +盐 城 房 价 怎 么 这 么 高 ? 0 +什 么 是 棚 户 区 ? 0 +怎 么 选 房 子 ? 0 +安 徽 的 富 人 区 在 哪 儿 ? 0 +廊 坊 属 于 几 线 城 市 ? 0 +深 圳 哪 个 区 比 较 发 达 ? 0 +成 交 客 户 两 大 秘 诀 0 +买 房 注 意 事 项 有 哪 些 ? 0 +众 泰 怎 么 样 ? 1 +现 在 买 房 合 算 吗 ? 0 +奔 驰 e 如 何 尽 快 提 车 ? 1 +安 徽 池 州 房 价 怎 么 样 ? 0 +什 么 叫 宅 基 地 换 房 ? 0 +塞 浦 路 斯 买 房 子 0 +为 什 么 大 车 不 拉 钢 卷 ? 1 +摩 托 车 年 审 车 要 去 吗 ? 1 +汽 车 安 全 灯 语 您 知 道 嘛 1 +切 记 ! 这 类 车 膜 不 能 贴 1 +什 么 时 候 买 车 最 合 适 ? 1 +什 么 是 4 p 营 销 ? 0 +领 克 0 3 这 款 车 如 何 ? 1 +治 理 违 法 大 货 车 进 行 时 1 +男 人 最 烧 钱 的 几 个 爱 好 1 +勇 士 — 乔 治 巴 顿 内 饰 篇 1 +现 在 应 该 买 房 吗 ? 0 +哪 个 汽 车 方 向 盘 最 帅 ? 1 +对 开 门 汽 车 都 有 哪 些 ? 1 +5 G 在 车 联 网 领 域 应 用 1 +你 好 , 这 里 不 能 停 车 ! 1 +莆 田 属 于 几 线 城 市 ? 0 +车 祸 现 场 爱 惜 生 命 吧 1 +学 车 必 看 ! 1 +投 资 房 产 真 的 靠 谱 吗 ? 0 +汽 车 方 向 盘 要 带 套 吗 ? 1 +国 产 丰 田 C - H R 1 +雷 克 萨 斯 E S 怎 么 样 ? 1 +在 包 头 买 房 现 在 贵 吗 ? 0 +城 市 中 心 论 0 +买 房 与 租 房 谁 更 好 ? 0 +大 风 天 气 行 车 注 意 事 项 1 +驻 马 店 宜 居 吗 ? 0 +高 速 公 路 怎 么 收 费 的 ? 1 +如 何 正 确 选 择 车 蜡 ? 1 +车 主 信 用 卡 选 哪 个 好 ? 1 +有 哪 些 车 比 较 好 呢 ? 1 +奥 迪 A 4 L 怎 么 样 ? 1 +商 丘 哪 个 县 最 宜 居 ? 0 +南 昌 南 外 环 四 通 八 达 1 +如 何 尽 快 提 车 ? 1 +胎 压 监 测 有 必 要 装 吗 ? 1 +本 田 c r v 混 动 怎 样 ? 1 +普 拉 多 的 故 事 1 +美 国 物 价 有 多 便 宜 ? 1 +留 给 滴 滴 的 时 间 不 多 了 1 +碧 桂 园 精 装 房 怎 么 样 ? 0 +晚 上 开 车 有 什 么 技 巧 ? 1 +提 前 还 贷 划 算 吗 ? 0 +手 动 挡 如 何 正 确 换 挡 ? 1 +宜 昌 有 哪 些 高 档 小 区 ? 0 +西 安 的 房 价 会 降 吗 ? 0 +宝 马 3 系 到 底 多 靠 谱 ? 1 +戒 毒 方 法 0 1 1 +工 程 进 度 报 道 0 +房 产 税 是 每 年 交 吗 ? 0 +茶 山 低 价 楼 盘 , 地 铁 站 0 +千 万 不 可 房 贷 断 供 ! 0 +奔 驰 G 5 0 0 好 吗 ? 1 +郑 州 有 9 8 5 大 学 吗 ? 0 +荣 威 R X 8 怎 么 样 ? 1 +嘀 , 前 方 道 路 请 掉 头 ! 1 +今 年 小 产 权 房 利 好 新 政 0 +话 说 “ 六 个 钱 包 ” 0 +美 国 富 人 的 那 些 豪 宅 0 +万 达 是 否 已 入 驻 南 阳 ? 0 +让 生 活 复 归 悠 然 惬 意 0 +小 学 生 暑 假 安 全 教 育 1 +龙 城 之 巅 见 证 伟 大 0 +拖 挂 式 旅 居 房 车 1 +赣 州 有 什 么 好 大 学 ? 0 +东 莞 的 富 人 区 在 哪 ? 0 +买 房 , 如 何 选 择 ? 0 +自 动 变 速 箱 分 解 图 1 +海 马 汽 车 0 元 购 车 1 +房 屋 买 卖 纠 纷 ? 0 +捷 达 和 桑 塔 纳 哪 个 好 ? 1 +淮 安 房 价 高 吗 ? 0 +摩 托 车 把 斜 怎 么 办 ? 1 +现 在 房 产 值 得 投 资 吗 ? 0 +顺 风 车 开 上 命 运 拐 点 1 +如 何 计 算 工 资 和 提 成 ? 0 +摩 托 车 配 件 哪 里 批 ? 1 +无 锡 龙 玺 太 湖 湾 开 盘 价 0 +一 般 多 少 转 速 换 挡 ? 1 +长 江 新 城 规 划 ? 0 +茅 台 酒 分 哪 些 档 次 ? 1 +哪 些 城 市 适 合 居 住 ? 0 +房 车 可 以 代 替 房 子 吗 ? 0 +魏 崔 牌 茅 台 酒 0 +衡 阳 的 房 价 还 会 跌 吗 ? 0 +网 上 的 机 油 靠 谱 吗 ? 1 +兰 州 车 辆 现 行 规 定 1 +C 1 怎 么 增 加 到 A 1 ? 1 +龙 湖 冠 寓 青 岛 找 楼 0 +现 在 昆 明 买 房 合 适 吗 ? 0 +「 游 记 」 老 君 山 的 怀 恋 1 +自 尊 , 是 自 己 给 的 1 +自 吸 好 还 是 涡 轮 好 ? 1 +卖 房 前 需 要 准 备 什 么 ? 0 +重 申 房 企 不 得 夜 间 开 盘 0 +河 北 承 德 房 价 能 降 吗 ? 0 +环 京 利 好 不 断 0 +三 缸 发 动 机 好 不 好 ? 1 +生 活 没 有 如 果 1 +一 车 一 桩 , 一 公 里 一 站 1 +这 两 个 户 型 哪 个 好 ? 0 +建 筑 资 质 在 哪 办 理 ? 0 +学 汽 修 哪 最 好 ? 1 +交 房 时 需 要 注 意 什 么 ? 0 +荣 放 和 欧 蓝 德 哪 个 好 ? 1 +汽 车 该 如 何 防 晒 ? 1 +荣 威 R X 5 油 耗 多 少 ? 1 +惠 州 聚 揽 福 庭 楼 盘 信 息 0 +买 公 寓 好 还 是 住 宅 好 ? 0 +怎 么 样 防 止 被 跑 单 ? 0 +平 方 青 年 肆 意 无 止 0 2 1 +宝 骏 这 车 怎 么 样 ? 1 +松 涛 苑 稀 缺 房 源 0 +年 轻 安 全 哈 弗 H 4 1 +西 部 行 青 海 湖 自 驾 游 玩 1 +房 产 税 筹 划 小 案 例 分 析 0 +汽 车 安 全 倒 车 有 技 巧 1 +公 寓 买 来 住 好 么 ? 0 +房 价 上 涨 的 可 能 性 0 +回 归 测 试 B 5 1 +日 系 车 有 什 么 通 病 吗 ? 1 +安 娜 · 高 美 在 上 海 0 +注 意 ! 买 房 又 出 新 规 ! 0 +创 业 要 御 风 而 行 1 +买 商 品 房 攻 略 ( 珍 藏 ) 0 +公 寓 " 税 " 让 你 心 碎 。 0 +现 代 朗 动 还 值 得 买 吗 ? 1 +揭 秘 物 业 费 花 在 了 哪 里 0 +油 灯 亮 了 , 开 始 方 了 ? 1 +限 量 版 跑 车 1 +土 地 闲 置 如 何 认 定 ? 0 +我 的 座 驾 是 奇 葩 1 +4 0 8 和 明 锐 怎 么 选 ? 1 +房 地 产 为 什 么 如 此 重 要 0 +去 嘉 兴 买 房 合 适 吗 ? 0 +境 界 不 同 视 界 自 不 同 0 +泉 州 房 价 为 什 么 便 宜 ? 0 +秦 皇 岛 万 达 在 哪 呢 ? 0 +瑞 虎 3 x 怎 么 样 ? 1 +武 汉 哪 里 买 房 靠 谱 ? 0 +为 什 么 不 能 猛 踩 油 门 ? 1 +百 花 齐 放 盛 在 天 街 0 +青 岛 的 房 价 会 跌 吗 ? 0 +如 何 投 资 海 外 房 产 ? 0 +驾 车 阳 光 刺 眼 怎 么 办 1 +汽 车 是 如 何 偷 空 间 的 ? 1 +北 汽 幻 速 s 7 怎 样 ? 1 +久 违 了 , 我 的 童 年 ! 1 +劳 斯 莱 斯 的 爱 情 故 事 1 +买 车 怎 么 分 期 最 好 ? 1 +内 蒙 古 哪 里 租 房 便 宜 ? 0 +周 口 是 四 线 城 市 吗 ? 0 +火 电 厂 是 怎 么 启 动 的 ? 1 +巴 中 房 价 怎 么 样 ? 0 +新 手 九 个 “ 不 要 ” 1 +重 庆 房 产 税 怎 么 收 ? 0 +如 何 更 换 刹 车 片 ? 1 +说 好 的 “ 房 住 不 炒 ” 0 +奇 骏 真 实 油 耗 是 多 少 ? 1 +一 亩 地 = 多 少 平 方 米 ? 0 +大 爱 ! 1 +雷 克 萨 斯 C T 怎 么 样 ? 1 +如 何 看 待 合 肥 房 价 ? 0 +珠 海 是 否 适 合 买 房 ? 0 +摩 托 改 装 , 酷 炫 狂 拽 1 +想 知 道 这 是 什 么 车 ? 1 +如 何 能 买 到 房 子 ? 0 +新 摩 托 车 为 什 么 没 力 ? 1 +如 何 评 价 领 克 0 1 ? 1 +为 什 么 赣 州 没 有 万 达 ? 0 +奥 迪 A 7 怎 么 样 ? 1 +未 来 南 通 房 价 会 如 何 ? 0 +大 淄 博 新 气 象 0 +信 和 财 富 靠 谱 吗 ? 0 +一 辆 奥 迪 A 7 多 少 钱 ? 1 +新 英 朗 等 同 于 凯 越 吗 ? 1 +奔 驰 G L S 级 怎 么 样 ? 1 +x r v 值 不 值 的 买 ? 1 +疲 劳 驾 驶 可 不 是 小 事 1 +如 何 完 善 网 约 车 的 管 理 1 +欧 尚 发 动 机 怎 么 样 ? 1 +许 昌 的 房 子 还 能 买 吗 ? 0 +郑 州 租 房 记 0 +加 油 站 里 的 行 车 法 则 1 +中 国 房 地 产 野 史 ( 中 ) 0 +途 观 直 降 3 万 1 +太 原 房 子 可 以 入 手 吗 ? 0 +无 锡 龙 玺 太 湖 湾 4 房 0 +主 动 安 全 系 统 真 的 强 1 +麦 当 劳 思 维 0 +河 北 唐 山 是 几 线 城 市 ? 0 +盛 世 万 家 乐 , 求 下 联 ? 0 +轻 钢 房 屋 结 实 吗 ? 0 +C Y S 的 优 势 1 +买 房 应 该 重 点 看 什 么 0 +现 在 该 不 该 投 资 房 产 ? 0 +房 价 为 什 么 不 便 宜 ? 0 +房 子 过 户 需 要 多 少 钱 ? 0 +走 进 贺 兰 山 正 义 关 1 +宝 马 车 行 里 的 狠 货 ! 1 +无 锡 市 房 价 多 少 ? 0 +钢 筋 加 腋 怎 么 区 分 ? 0 +你 若 酒 驾 , 成 本 如 下 1 +现 在 买 房 适 合 吗 ? 0 +西 安 房 价 是 多 少 ? 0 +客 厅 横 梁 要 怎 么 处 理 ? 0 +房 地 产 调 控 不 能 懈 怠 0 +中 山 东 凤 镇 万 科 地 产 0 +本 月 首 周 新 房 加 快 入 市 0 +如 何 评 价 鲁 能 集 团 ? 0 +你 知 道 怎 么 选 福 特 吗 ? 1 +一 款 不 错 的 进 口 轿 车 1 +如 何 看 待 韶 关 的 房 价 ? 0 +公 摊 面 积 多 少 才 合 理 ? 0 +认 识 再 多 小 老 板 也 没 用 1 +邢 台 现 在 房 价 多 少 ? 0 +云 度 新 能 源 怎 么 样 ? 1 +国 产 车 的 时 代 你 会 买 么 1 diff --git a/applications/text_classification/headlines500/vocab.txt b/applications/text_classification/headlines500/vocab.txt new file mode 100644 index 0000000..1da5305 --- /dev/null +++ b/applications/text_classification/headlines500/vocab.txt @@ -0,0 +1,986 @@ +[unknown] +落 +还 +转 +红 +卖 +式 +南 +问 +桩 +才 +阁 +总 +证 +作 +零 +· +厉 +懂 +题 +命 +基 +档 +锐 +需 +异 +官 +级 +普 +堂 +业 +司 +雷 +视 +话 +越 +r +势 +闲 +桑 +揭 +花 +控 +宝 +呢 +难 +贵 +把 +权 +s +珍 +适 +蜡 +达 +彩 +直 +校 +力 +湖 +气 +前 +庄 +追 +铜 +瑞 +际 +语 +洒 +最 +成 +局 +日 +析 +七 +赣 +膜 +质 +摔 +宾 +客 +须 +正 +网 +右 +雾 +近 +R +史 +启 +是 +态 +横 +寿 +x +G +米 +秦 +尾 +外 +菱 +归 +么 +哈 +特 +拆 +然 +评 +停 +镇 +毒 +耗 +创 +博 +求 +刺 +钉 +0 +应 +论 +逃 +承 +精 +革 +弗 +带 +店 +京 +绝 +玉 +漠 +戒 +感 +辆 +也 +部 +厅 +珠 +更 +分 +因 +射 +坊 +请 +县 +园 +与 +户 +代 +节 +错 +骏 +玛 +悟 +介 +假 +待 +以 +尼 +传 +混 +年 +陵 +奔 +老 +概 +目 +九 +般 +蒂 +里 +佛 +c +度 +考 +想 +较 +浦 +儿 +快 +穿 +B +梁 +降 +清 +麦 +巴 +吧 +衡 +迷 +五 +火 +恋 +习 +玩 +碧 +海 +等 +幻 +见 +损 +飙 +便 +起 +准 +皇 +弯 +进 +历 +替 +康 +暖 +货 +维 +上 +费 +河 +诀 +凯 +活 +轿 +半 +如 +该 +P +迈 +懈 +茅 +西 +悲 +洲 +技 +静 +城 +莱 +场 +入 +间 +饰 +处 +楼 +贴 +尚 +电 +到 +攻 +富 +厂 +防 +意 +墅 +韶 +率 +租 +门 +讯 +赛 +爱 +词 +豪 +什 +野 +照 +嘛 +骑 +注 +兴 +时 +采 +财 +两 +济 +供 +克 +指 +价 +施 +优 +百 +组 +再 +斜 +川 +黄 +格 +襄 +风 +, +摩 +怠 +劲 +松 +置 +例 +座 +认 +有 +我 +专 +响 +后 +8 +遇 +观 +已 +欢 +违 +伟 +八 +田 +傅 +斯 +性 +底 +猛 +白 +告 +惜 +口 +纠 +屈 +安 +土 +域 +可 +企 +9 +榜 +果 +凶 +名 +阳 +托 +试 +否 +类 +沙 +记 +病 +六 +L +站 +屋 +实 +T +合 +捷 +鸡 +不 +选 +丰 +跌 +源 +频 +剧 +用 +个 +影 +士 +唐 +聚 +齐 +祸 +立 +郑 +故 +手 +抚 +左 +肥 +始 +首 +苑 +次 +箱 +伤 +位 +华 +亚 +留 +Y +悬 +下 +明 +住 +奥 +市 +属 +挂 +套 +刹 +止 +按 +劳 +庆 +主 +H +字 +盛 +州 +荣 +胎 +津 +怪 +爆 +真 +存 +环 +蒙 +葩 +岛 +择 +找 +炫 +交 +案 +疲 +幕 +马 +内 +棚 +许 +帅 +税 +。 +嘀 +倒 +值 +治 +V +了 +掉 +昆 +贯 +威 +树 +迪 +团 +古 +贷 +通 +比 +审 +痕 +崔 +则 +期 +开 +家 +像 +工 +春 +超 +接 +筑 +缝 +会 +项 +院 +监 +教 +远 +秘 +尔 +v +种 +卡 +燃 +p +们 +车 +泽 +型 +变 +湾 +算 +雀 +肆 +乡 +容 +片 +及 +宜 +台 +况 +空 +狂 +每 +胜 +来 +办 +佩 +新 +人 +温 +报 +飞 +杀 +渡 +它 +惬 +热 +害 +对 +独 +荷 +断 +X +驰 +约 +房 +要 +略 +e +惠 +放 +旗 +腾 +测 +钢 +粘 +缸 +岗 +化 +邢 +丘 +宏 +没 +候 +同 +师 +林 +旧 +夏 +招 +道 +而 +天 +集 +的 +? +发 +避 +千 +物 +即 +绵 +汕 +| +致 +府 +伴 +购 +吃 +筋 +头 +预 +铁 +篇 +夜 +面 +切 +装 +东 +桂 +走 +生 +又 +示 +都 +C +识 +批 +偷 +看 +确 +踏 +在 +调 +课 +: +狠 +稀 +久 +巧 +女 +碎 +破 +被 +龙 +沉 +驶 +产 +墨 +乐 +汉 +拖 +盐 +柯 +十 +暑 +钱 +尊 +u +几 +付 +纷 +改 +少 +争 +灯 +积 +己 +配 +挡 +踩 +纸 +你 +能 +早 +揽 +栋 +常 +何 +( +幸 +鸣 +鲁 +机 +娜 +悠 +坦 +领 +她 +自 +廊 +弱 +此 +动 +练 +顿 +缺 +地 +金 +孔 +" +菏 +得 +版 +情 +4 +极 +周 +亮 +操 +渐 +美 +大 +建 +巅 +” +备 +原 +「 +狗 +提 +乔 +坡 +岩 +徐 +规 +层 +魏 +为 +万 +排 +管 +线 +油 +青 +申 +众 +雪 +低 +往 +吗 +于 +- +窑 +泰 +眼 +泉 +沪 +腋 +徽 +高 +1 +步 +驾 +咋 +塞 +回 +身 +槐 +贼 +怎 +好 +滴 +溜 +智 +表 +跑 +子 +您 +淄 +知 +顺 +售 +U +拉 +区 += +怀 +陷 +5 +联 +吉 +图 +享 +去 +贬 +药 +件 +涡 +法 +板 +点 +核 +! +加 +凤 +岭 +养 +些 +限 +寓 +方 +晚 +拐 +这 +童 +那 +藏 +莆 +政 +涛 +块 +委 +库 +器 +航 +谋 +嘉 +涨 +7 +标 +叫 +旅 +庭 +现 +太 +速 +之 +员 +锦 +) +跨 +小 +勇 +肯 +纳 +卷 +义 +投 +重 +潭 +增 +茂 +中 +兰 +酷 +虚 +境 +长 +仁 +永 +行 +理 +居 +划 +午 +牌 +“ +淮 +圳 +定 +黎 +哦 +洗 +摊 +朗 +… +钟 +世 +全 +验 +信 +喜 +着 +品 +让 +无 +别 +盘 +谱 +包 +离 +危 +萨 +事 +W +一 +多 +山 +兹 +北 +茶 +给 +系 +福 +晒 +息 +各 +压 +流 +必 +向 +尽 +解 +途 +收 +欧 +雨 +设 +奇 +公 +炒 +汽 +第 +沃 +修 +虎 +营 +冠 +武 +贡 +志 +销 +鹰 +6 +男 +当 +四 +思 +E +界 +贺 +结 +易 +象 +轻 +说 +过 +御 +光 +哪 +锅 +三 +谁 +丹 +酒 +莞 +挑 +驻 +街 +导 +亩 +吸 +二 +经 +轮 +2 +今 +做 +— +云 +靠 +育 +元 +深 +」 +平 +换 +德 +宅 +赏 +路 +运 +韵 +韩 +锡 +苏 +满 +科 +买 +边 +石 +强 +出 +样 +资 +塔 +君 +播 +关 +广 +学 +初 +完 +复 +池 +程 +A +打 +蓝 +款 +量 +烧 +本 +宁 +免 +单 +筹 +景 +月 +险 +若 +姐 +玺 +银 +善 +心 +利 +国 +统 +怕 +贝 +水 +游 +昌 +和 +商 +3 +拽 +英 +S +莎 +祺 +保 +术 +效 +幅 +未 +融 +江 +计 +抢 diff --git a/applications/text_classification/introduction_cn.ipynb b/applications/text_classification/introduction_cn.ipynb new file mode 100644 index 0000000..1e13897 --- /dev/null +++ b/applications/text_classification/introduction_cn.ipynb @@ -0,0 +1,218 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 文本分类简介\n", + "\n", + "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*\n", + "\n", + "自然语言处理(Natural Language Processing, NLP)是一种机器学习技术,使计算机具有解释、理解和使用人类语言的能力。现在的各类政企拥有大量的语音和文本数据,这些数据来自各种通信渠道,如电子邮件、文本信息、社交媒体新闻报道、视频、音频等等。他们使用NLP软件来自动处理这些数据,分析信息中的意图或情绪,并实时回应人们的沟通。\n", + "\n", + "文本分类任务是 NLP 中的基础任务之一。它对输入的文本预测其类别。新闻标题分类、情感分析等应用背后的技术都是文本分类。\n", + "\n", + "在这里,我们使用新闻标题分类为例来展示量子机器学习(Quantum Machine Learning, QML)处理文本分类问题的能力。\n", + "\n", + "我们使用房地产行业和汽车行业这两类新闻标题作为数据集,对其进行分类。在这个数据集中,训练集包括400条文本数据,测试集包括100条文本数据。数据的样例如下:\n", + "\n", + "- 奔驰GLS怎么样?\n", + "- 如何评价襄阳房价?\n", + "- 南宁的城建怎么样?\n", + "- 保时捷为什么这么贵" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 使用 QSANN 模型实现新闻标题分类\n", + "\n", + "### QSANN 模型简介\n", + "\n", + "量子自注意力神经网络(Quantum Self-Attention Neural Networks for Text Classification, QSANN)是一个在监督学习框架下的量子–经典混合算法。它先使用了参数化量子电路(Parameterized Quantum Circuit, PQC)对文本数据进行特征编码,然后再使用自注意力机制进行特征提取,最后使用全连接神经网络处理得到分类结果。\n", + "\n", + "总结来说,QSANN 的大致原理如下:\n", + "\n", + "1. 将输入文本的每个字映射成对应的参数化量子电路。该电路演化得到的量子态即为这个字对应的特征表示。\n", + "2. 使用自注意力机制对量子态进行处理,并得到处理后的特征表示。\n", + "3. 使用全连接神经网络对得到的特征进行处理,并得到预测的分类结果。\n", + "\n", + "### 工作流\n", + "\n", + "QSANN 是学习类的模型。我们需要先使用数据集对模型进行训练。在训练收敛后,我们便得到了一个训练好的模型,这个模型可以对这类数据进行分类。因此,其工作流如下:\n", + "\n", + "1. 制备数据集。\n", + "2. 使用数据集进行训练,得到训练好的模型。\n", + "3. 使用该模型对输入的文本进行预测,得到预测结果。\n", + "\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 如何使用\n", + "\n", + "### 使用模型进行预测\n", + "\n", + "这里,我们已经给出了一个训练好的模型,可以直接用于房地产行业和汽车行业的新闻标题分类预测。只需要在 `example.toml` 这个配置文件中进行对应的配置,然后输入命令 `python qsann_classification.py --config example.toml` 即可使用训练好的 QSANN 模型对输入的文本进行预测。\n", + "\n", + "### 在线演示\n", + "\n", + "这里,我们给出一个在线演示的版本,可以在线进行预测。首先定义配置文件的内容:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "test_toml = r\"\"\"\n", + "# 模型配置文件。\n", + "# 输入当前的任务,可以是 'train' 或者 'test',分别代表训练和预测。这里我们使用 test,表示我们要进行预测。\n", + "task = 'test'\n", + "# 输入要预测的文本。\n", + "text = '奔驰GLS怎么样?'\n", + "# 训练好的模型参数文件的文件路径。\n", + "model_path = 'qsann.pdparams'\n", + "# 数据集的字典文件路径。\n", + "vocab_path = 'headlines500/vocab.txt'\n", + "# 量子电路所包含的量子比特的数量。\n", + "num_qubits = 6\n", + "# 自注意力层的层数。\n", + "num_layers = 1\n", + "# 词嵌入电路的电路深度。\n", + "depth_ebd = 1\n", + "# 注意力机制中 query 电路的电路深度。\n", + "depth_query = 1\n", + "# 注意力机制中 key 电路的电路深度。\n", + "depth_key = 1\n", + "# 注意力机制中 value 电路的电路深度。\n", + "depth_value = 1\n", + "# 对输入文本要预测的类别。\n", + "classes = ['房地产行业', '汽车行业']\n", + "\"\"\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "接下来是预测部分的代码:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "输入的文本是:奔驰GLS怎么样?。\n", + "模型的预测结果是:汽车行业。\n" + ] + } + ], + "source": [ + "import os\n", + "import warnings\n", + "\n", + "warnings.filterwarnings('ignore')\n", + "os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python'\n", + "\n", + "import toml\n", + "from paddle_quantum.qml.qsann import train, inference\n", + "\n", + "config = toml.loads(test_toml)\n", + "task = config.pop('task')\n", + "if task == 'train':\n", + " train(**config)\n", + "elif task == 'test':\n", + " prediction = inference(**config)\n", + " text = config['text']\n", + " print(f'输入的文本是:{text}。')\n", + " print(f'模型的预测结果是:{prediction}。')\n", + "else:\n", + " raise ValueError(\"未知的任务,它可以是'train'或'test'。\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "在这里,我们只需要修改要配置文件中的 text 的内容,再运行整个代码,就可以快速对其它文本测试。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 注意事项\n", + "\n", + "在这里,我们提供的模型是汽车行业和房地产行业的新闻标题文本分类模型。开发者也可以使用自己的数据集来训练对应的模型。\n", + "\n", + "### 数据集结构\n", + "\n", + "如果想要使用自定义数据集进行训练,只需要按照规则来准备数据集即可。在数据集文件夹中准备 `train.txt` 和 `test.txt`,如果需要验证集的话还有 `dev.txt`。每个文件里使用一行代表一条数据。每行内容包含文本和对应的标签,使用制表符隔开。文本是由空格隔开的文字组成。\n", + "\n", + "### 配置文件介绍\n", + "\n", + "在 `test.toml` 里有测试所需要的完整的配置文件内容参考。在 `train.toml` 里有训练所需要的完整的配置文件内容参考。使用 `python qsann_classification --config train.toml` 可以对模型进行训练。使用 `python qsann_classification --config test.toml` 可以加载训练好的模型进行测试。" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 引用信息\n", + "\n", + "```tex\n", + "@article{li2022quantum,\n", + " title={Quantum Self-Attention Neural Networks for Text Classification},\n", + " author={Li, Guangxi and Zhao, Xuanqiang and Wang, Xin},\n", + " journal={arXiv preprint arXiv:2205.05625},\n", + " year={2022}\n", + "}\n", + "```" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py37", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.15 (default, Nov 10 2022, 12:46:26) \n[Clang 14.0.6 ]" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "49b49097121cb1ab3a8a640b71467d7eda4aacc01fc9ff84d52fcb3bd4007bf1" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/applications/text_classification/introduction_en.ipynb b/applications/text_classification/introduction_en.ipynb new file mode 100644 index 0000000..9bd682f --- /dev/null +++ b/applications/text_classification/introduction_en.ipynb @@ -0,0 +1,217 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Introduction to Text Classification\n", + "\n", + "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*\n", + "\n", + "Natural language processing (NLP) is a machine learning technology that gives computers the ability to interpret, manipulate, and comprehend human language. Organizations today have large volumes of voice and text data from various communication channels like emails, text messages, social media newsfeeds, video, audio, and more. They use NLP software to automatically process this data, analyze the intent or sentiment in the message, and respond in real time to human communication.\n", + "\n", + "The text classification task is one of the fundamental tasks in NLP. It predicts the category of the input text. It is the technique behind the applications such as news headline classification and sentiment analysis.\n", + "\n", + "Here, we use news headline classification as an example to demonstrate the power of Quantum Machine Learning (QML) for the text classification problems.\n", + "\n", + "We use two types of news headlines, real estate industry and automobile industry, as the dataset to classify them. In this dataset, the training set consists of 400 text data and the test set consists of 100 text data. A sample of the data is as follows.\n", + "\n", + "- 奔驰GLS怎么样?\n", + "- 如何评价襄阳房价?\n", + "- 南宁的城建怎么样?\n", + "- 保时捷为什么这么贵" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## News Headline Classification Using QSANN Model\n", + "\n", + "### Introduction to the QSANN Model\n", + "\n", + "Quantum Self-Attention Neural Networks (QSANN) is a hybrid quantum-classical algorithm in a supervised learning framework. It uses the parameterized quantum circuit (PQC) to encode features on text data, the self-attention mechanism for feature extraction, and finally a fully connected neural network to process the classification results.\n", + "\n", + "In summary, the general principle of QSANN is as follows.\n", + "\n", + "1. Each word of the input text is mapped into a corresponding parameterized quantum circuit. The quantum state obtained from the evolution of this circuit is the corresponding feature representation of this word.\n", + "2. Use the self-attention mechanism to process the quantum state and obtain the processed feature representation.\n", + "3. Use the fully connected neural network to process the obtained features and get the predicted classification results.\n", + "\n", + "### Workflow\n", + "\n", + "QSANN is a learning model. So we need to first train the model using the dataset. After the training converges, we get a trained model which can classify the data corresponding to the task. Thus, the workflow is as follows.\n", + "\n", + "1. Prepare the dataset.\n", + "2. Train with the dataset to obtain a trained model.\n", + "3. Use the model to predict the input text and get the prediction results." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## How to Use\n", + "\n", + "### Predict Using the Model\n", + "\n", + "Here, we have presented a trained model that can be directly used for news headline classification in the real estate industry and the automobile industry. Just make the corresponding configuration in the configuration file `example.toml` and enter the command `python qsann_classification.py --config example.toml` to use the trained QSANN model for predicting the input text.\n", + "\n", + "### Online Demo\n", + "\n", + "Here, we give a version of the demo that can be tested online. First define the contents of the configuration file." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "test_toml = r\"\"\"\n", + "# The overall configuration file of the model.\n", + "# Enter the current task, which can be 'train' or 'test', representing training and prediction respectively. Here we use test, indicating that we want to make a prediction.\n", + "task = 'test'\n", + "# The text to be tested.\n", + "text = '奔驰GLS怎么样?'\n", + "# The path of the trained model, which will be loaded.\n", + "model_path = 'qsann.pdparams'\n", + "# The path of the vocabulary file in the dataset.\n", + "vocab_path = 'headlines500/vocab.txt'\n", + "# The number of qubits which the quantum circuit contains.\n", + "num_qubits = 6\n", + "# The number of the self-attention layers.\n", + "num_layers = 1\n", + "# The depth of the embedding circuit.\n", + "depth_ebd = 1\n", + "# The depth of the query circuit.\n", + "depth_query = 1\n", + "# The depth of the key circuit.\n", + "depth_key = 1\n", + "# The depth of the value circuit.\n", + "depth_value = 1\n", + "# The classes of input text to be predicted.\n", + "classes = ['房地产行业', '汽车行业']\n", + "\"\"\"\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next is the code for the prediction section." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The input text is 奔驰GLS怎么样?.\n", + "The prediction of the model is 汽车行业.\n" + ] + } + ], + "source": [ + "import os\n", + "import warnings\n", + "\n", + "warnings.filterwarnings('ignore')\n", + "os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python'\n", + "\n", + "import toml\n", + "from paddle_quantum.qml.qsann import train, inference\n", + "\n", + "config = toml.loads(test_toml)\n", + "task = config.pop('task')\n", + "if task == 'train':\n", + " train(**config)\n", + "elif task == 'test':\n", + " prediction = inference(**config)\n", + " text = config['text']\n", + " print(f'The input text is {text}.')\n", + " print(f'The prediction of the model is {prediction}.')\n", + "else:\n", + " raise ValueError(\"Unknown task, it can be train or test.\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here, we only need to modify the content of the text in the configuration file, and then run the entire code to quickly test other texts." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Note\n", + "\n", + "Here, we provide models for text classification of news headlines in the automotive and real estate industries. Developers can also use their own datasets to train the corresponding models.\n", + "\n", + "### The structure of the dataset\n", + "\n", + "If you want to use a custom dataset for training, you just need to prepare the dataset according to the rules. Prepare `train.txt` and `test.txt` in the dataset folder, and `dev.txt` if a validation set is needed. One line is used to represent one piece of data in each file. Each line contains text and a corresponding label, separated by tabs. Text is composed of space-separated words.\n", + "\n", + "### Introduction to the Configuration File\n", + "\n", + "In `test.toml`, there is a complete reference to the configuration files needed for testing. In `train.toml`, there is a complete reference to the configuration files needed for training. Use `python qsann_classification --config train.toml` to train the model. Use `python qsann_classification --config test.toml` to load the trained model for testing.\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Citation\n", + "\n", + "```tex\n", + "@article{li2022quantum,\n", + " title={Quantum Self-Attention Neural Networks for Text Classification},\n", + " author={Li, Guangxi and Zhao, Xuanqiang and Wang, Xin},\n", + " journal={arXiv preprint arXiv:2205.05625},\n", + " year={2022}\n", + "}\n", + "```\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py37", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.15 (default, Nov 10 2022, 12:46:26) \n[Clang 14.0.6 ]" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "49b49097121cb1ab3a8a640b71467d7eda4aacc01fc9ff84d52fcb3bd4007bf1" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/applications/text_classification/qsann.pdparams b/applications/text_classification/qsann.pdparams new file mode 100644 index 0000000..ff8abce Binary files /dev/null and b/applications/text_classification/qsann.pdparams differ diff --git a/applications/text_classification/qsann_classification.py b/applications/text_classification/qsann_classification.py new file mode 100644 index 0000000..c097332 --- /dev/null +++ b/applications/text_classification/qsann_classification.py @@ -0,0 +1,41 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import warnings + +warnings.filterwarnings('ignore') +os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python' + +import argparse +import toml +from paddle_quantum.qml.qsann import train, inference + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Classify the headlines by the QSANN model.") + parser.add_argument("--config", type=str, help="Input the config file with toml format.") + args = parser.parse_args() + config = toml.load(args.config) + task = config.pop('task') + if task == 'train': + train(**config) + elif task == 'test': + prediction = inference(**config) + text = config['text'] + print(f'The input text is {text}.') + print(f'The prediction of the model is {prediction}.') + else: + raise ValueError("Unknown task, it can be train or test.") diff --git a/applications/text_classification/test.toml b/applications/text_classification/test.toml new file mode 100644 index 0000000..033cfbf --- /dev/null +++ b/applications/text_classification/test.toml @@ -0,0 +1,23 @@ +# The full config for test the QSANN model. +# The task of this config. Available values: 'train' | 'test'. +task = 'test' +# The text to be tested. +text = 'The text to be predicted.' +# The path of the trained model, which will be loaded. +model_path = 'qsann.pdparams' +# The path of the vocabulary file in the dataset. +vocab_path = 'headlines500/vocab.txt' +# The number of qubits which the quantum circuit contains. +num_qubits = 6 +# The number of the self-attention layers. +num_layers = 1 +# The depth of the embedding circuit. +depth_ebd = 1 +# The depth of the query circuit. +depth_query = 1 +# The depth of the key circuit. +depth_key = 1 +# The depth of the value circuit. +depth_value = 1 +# The classes of input text to be predicted. +classes = ['房地产行业', '汽车行业'] diff --git a/applications/text_classification/train.toml b/applications/text_classification/train.toml new file mode 100644 index 0000000..cc79daf --- /dev/null +++ b/applications/text_classification/train.toml @@ -0,0 +1,34 @@ +# The full config for train the QSANN model. +# The task of this config. Available values: 'train' | 'test'. +task = 'train' +# The name of the model, which is used to save the model. +model_name = 'qsann-model' +# The path to save the model. Both relative and absolute paths are allowed. +# It save the model to the current path by default. +# saved_path = './' +# The number of qubits which the quantum circuit contains. +num_qubits = 6 +# The number of the self-attention layers. +num_layers = 1 +# The depth of the embedding circuit. Defaults to 1. +depth_ebd = 1 +# The depth of the query circuit. Defaults to 1. +depth_query = 1 +# The depth of the key circuit. Defaults to 1. +depth_key = 1 +# The depth of the value circuit. Defaults to 1. +depth_value = 1 +# The size of the batch samplers. +batch_size = 8 +# The number of epochs to train the model. +num_epochs = 10 +# The learning rate used to update the parameters, defaults to 0.01. +# learning_rate = 0.01 +# The path of the dataset. +dataset = 'headlines500' +# Whether use the validation. +# It is true means the dataset contains training, validation and test datasets. +# It is false means the dataset only contains training datasets and test datasets. +using_validation = false +# Number of epochs with no improvement after which training will be stopped. +# early_stopping = 1000 \ No newline at end of file diff --git a/docs/source/_static/.gitkeep b/docs/source/_static/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/source/introduction.rst b/docs/source/introduction.rst index 21d0396..bbcf546 100644 --- a/docs/source/introduction.rst +++ b/docs/source/introduction.rst @@ -115,7 +115,7 @@ Now, you can try to run a program to verify whether the Paddle Quantum has been Feedbacks ---------- -- Users are encouraged to report issues and submit suggestions on `Github Issues `__. +- Users are encouraged to report issues and submit suggestions on `GitHub Issues `__. - QQ group: 1076223166 .. _header-n118: diff --git a/docs/source/locale/en/LC_MESSAGES/introduction.po b/docs/source/locale/en/LC_MESSAGES/introduction.po index 0464b72..ddd4ff1 100644 --- a/docs/source/locale/en/LC_MESSAGES/introduction.po +++ b/docs/source/locale/en/LC_MESSAGES/introduction.po @@ -182,7 +182,7 @@ msgstr "" #: ../../source/introduction.rst:118 msgid "" -"Users are encouraged to report issues and submit suggestions on `Github " +"Users are encouraged to report issues and submit suggestions on `GitHub " "Issues `__." msgstr "" diff --git a/docs/source/modules.rst b/docs/source/modules.rst index d870e56..cd04910 100644 --- a/docs/source/modules.rst +++ b/docs/source/modules.rst @@ -3,25 +3,28 @@ paddle_quantum.ansatz paddle_quantum.backend + paddle_quantum.biocomputing paddle_quantum.channel + paddle_quantum.data_analysis + paddle_quantum.finance paddle_quantum.gate paddle_quantum.locc paddle_quantum.loss paddle_quantum.mbqc paddle_quantum.operator paddle_quantum.qchem + paddle_quantum.qml + paddle_quantum.qpp + paddle_quantum.qsvt paddle_quantum.state paddle_quantum.base paddle_quantum.dataset - paddle_quantum.finance paddle_quantum.fisher paddle_quantum.gradtool paddle_quantum.hamiltonian paddle_quantum.linalg + paddle_quantum.model paddle_quantum.qinfo - paddle_quantum.qml paddle_quantum.shadow paddle_quantum.trotter paddle_quantum.visual - paddle_quantum.qsvt - paddle_quantum.qpp diff --git a/docs/source/paddle_quantum.biocomputing.algorithm.rst b/docs/source/paddle_quantum.biocomputing.algorithm.rst new file mode 100644 index 0000000..edbcf26 --- /dev/null +++ b/docs/source/paddle_quantum.biocomputing.algorithm.rst @@ -0,0 +1,6 @@ +paddle\_quantum.biocomputing.algorithm +=========================================== + +.. automodule:: paddle_quantum.biocomputing.algorithm + :members: + :show-inheritance: diff --git a/docs/source/paddle_quantum.biocomputing.data_loader.rst b/docs/source/paddle_quantum.biocomputing.data_loader.rst new file mode 100644 index 0000000..112048d --- /dev/null +++ b/docs/source/paddle_quantum.biocomputing.data_loader.rst @@ -0,0 +1,6 @@ +paddle\_quantum.biocomputing.data\_loader +============================================= + +.. automodule:: paddle_quantum.biocomputing.data_loader + :members: + :show-inheritance: diff --git a/docs/source/paddle_quantum.biocomputing.operators.rst b/docs/source/paddle_quantum.biocomputing.operators.rst new file mode 100644 index 0000000..8548a08 --- /dev/null +++ b/docs/source/paddle_quantum.biocomputing.operators.rst @@ -0,0 +1,6 @@ +paddle\_quantum.biocomputing.operators +============================================== + +.. automodule:: paddle_quantum.biocomputing.operators + :members: + :show-inheritance: diff --git a/docs/source/paddle_quantum.biocomputing.protein.rst b/docs/source/paddle_quantum.biocomputing.protein.rst new file mode 100644 index 0000000..863e0ff --- /dev/null +++ b/docs/source/paddle_quantum.biocomputing.protein.rst @@ -0,0 +1,6 @@ +paddle\_quantum.biocomputing.protein +============================================= + +.. automodule:: paddle_quantum.biocomputing.protein + :members: + :show-inheritance: diff --git a/docs/source/paddle_quantum.biocomputing.rst b/docs/source/paddle_quantum.biocomputing.rst new file mode 100644 index 0000000..3549496 --- /dev/null +++ b/docs/source/paddle_quantum.biocomputing.rst @@ -0,0 +1,17 @@ +paddle\_quantum.biocomputing +============================== + +.. automodule:: paddle_quantum.biocomputing + :members: + :show-inheritance: + +.. rubric:: Submodules + +.. toctree:: + :maxdepth: 4 + + paddle_quantum.biocomputing.algorithm + paddle_quantum.biocomputing.data_loader + paddle_quantum.biocomputing.operators + paddle_quantum.biocomputing.protein + paddle_quantum.biocomputing.visualize diff --git a/docs/source/paddle_quantum.biocomputing.visualize.rst b/docs/source/paddle_quantum.biocomputing.visualize.rst new file mode 100644 index 0000000..773e8d3 --- /dev/null +++ b/docs/source/paddle_quantum.biocomputing.visualize.rst @@ -0,0 +1,6 @@ +paddle\_quantum.biocomputing.visualize +================================================== + +.. automodule:: paddle_quantum.biocomputing.visualize + :members: + :show-inheritance: diff --git a/docs/source/paddle_quantum.channel.representation.rst b/docs/source/paddle_quantum.channel.representation.rst new file mode 100644 index 0000000..5c41d8a --- /dev/null +++ b/docs/source/paddle_quantum.channel.representation.rst @@ -0,0 +1,6 @@ +paddle\_quantum.channel.representation +=========================================== + +.. automodule:: paddle_quantum.channel.representation + :members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/paddle_quantum.channel.rst b/docs/source/paddle_quantum.channel.rst index 6c75824..61ff470 100644 --- a/docs/source/paddle_quantum.channel.rst +++ b/docs/source/paddle_quantum.channel.rst @@ -20,3 +20,4 @@ paddle\_quantum.channel paddle_quantum.channel.base paddle_quantum.channel.common paddle_quantum.channel.custom + paddle_quantum.channel.representation diff --git a/docs/source/paddle_quantum.data_analysis.rst b/docs/source/paddle_quantum.data_analysis.rst new file mode 100644 index 0000000..97b0269 --- /dev/null +++ b/docs/source/paddle_quantum.data_analysis.rst @@ -0,0 +1,15 @@ +paddle\_quantum.data\_analysis +================================ + +.. automodule:: paddle_quantum.data_analysis + :members: + :undoc-members: + :show-inheritance: + +.. rubric:: Submodules + +.. toctree:: + :maxdepth: 4 + + paddle_quantum.data_analysis.vqr + paddle_quantum.data_analysis.vqls \ No newline at end of file diff --git a/docs/source/paddle_quantum.data_analysis.vqls.rst b/docs/source/paddle_quantum.data_analysis.vqls.rst new file mode 100644 index 0000000..a3522bf --- /dev/null +++ b/docs/source/paddle_quantum.data_analysis.vqls.rst @@ -0,0 +1,7 @@ +paddle\_quantum.data\_analysis.vqls +========================================== + +.. automodule:: paddle_quantum.data_analysis.vqls + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/paddle_quantum.data_analysis.vqr.rst b/docs/source/paddle_quantum.data_analysis.vqr.rst new file mode 100644 index 0000000..7923bd3 --- /dev/null +++ b/docs/source/paddle_quantum.data_analysis.vqr.rst @@ -0,0 +1,7 @@ +paddle\_quantum.data\_analysis.vqr +======================================= + +.. automodule:: paddle_quantum.data_analysis.vqr + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/paddle_quantum.finance.finance.rst b/docs/source/paddle_quantum.finance.finance.rst new file mode 100644 index 0000000..f386209 --- /dev/null +++ b/docs/source/paddle_quantum.finance.finance.rst @@ -0,0 +1,7 @@ +paddle\_quantum.finance.finance +================================= + +.. automodule:: paddle_quantum.finance.finance + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/paddle_quantum.finance.pricing.rst b/docs/source/paddle_quantum.finance.pricing.rst new file mode 100644 index 0000000..09f536f --- /dev/null +++ b/docs/source/paddle_quantum.finance.pricing.rst @@ -0,0 +1,7 @@ +paddle\_quantum.finance.pricing +================================= + +.. automodule:: paddle_quantum.finance.pricing + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/paddle_quantum.qchem.loss.rst b/docs/source/paddle_quantum.finance.qpo.rst similarity index 55% rename from docs/source/paddle_quantum.qchem.loss.rst rename to docs/source/paddle_quantum.finance.qpo.rst index 5fda443..ba85d54 100644 --- a/docs/source/paddle_quantum.qchem.loss.rst +++ b/docs/source/paddle_quantum.finance.qpo.rst @@ -1,7 +1,7 @@ -paddle\_quantum.qchem.loss +paddle\_quantum.finance.qpo ================================= -.. automodule:: paddle_quantum.qchem.loss +.. automodule:: paddle_quantum.finance.qpo :members: :undoc-members: :show-inheritance: diff --git a/docs/source/paddle_quantum.finance.rst b/docs/source/paddle_quantum.finance.rst index c2aef5c..1a39b8f 100644 --- a/docs/source/paddle_quantum.finance.rst +++ b/docs/source/paddle_quantum.finance.rst @@ -5,3 +5,12 @@ paddle\_quantum.finance :members: :undoc-members: :show-inheritance: + +.. rubric:: Submodules + +.. toctree:: + :maxdepth: 4 + + paddle_quantum.finance.finance + paddle_quantum.finance.pricing + paddle_quantum.finance.qpo diff --git a/docs/source/paddle_quantum.model.rst b/docs/source/paddle_quantum.model.rst new file mode 100644 index 0000000..ddabfa5 --- /dev/null +++ b/docs/source/paddle_quantum.model.rst @@ -0,0 +1,7 @@ +paddle\_quantum.model +============================= + +.. automodule:: paddle_quantum.model + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/paddle_quantum.qchem.algorithm.rst b/docs/source/paddle_quantum.qchem.algorithm.rst new file mode 100644 index 0000000..02f543d --- /dev/null +++ b/docs/source/paddle_quantum.qchem.algorithm.rst @@ -0,0 +1,7 @@ +paddle\_quantum.qchem.algorithm +========================================= + +.. automodule:: paddle_quantum.qchem.algorithm + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/paddle_quantum.qchem.ansatz.rst b/docs/source/paddle_quantum.qchem.ansatz.rst new file mode 100644 index 0000000..9992283 --- /dev/null +++ b/docs/source/paddle_quantum.qchem.ansatz.rst @@ -0,0 +1,7 @@ +paddle\_quantum.qchem.ansatz +================================= + +.. automodule:: paddle_quantum.qchem.ansatz + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/paddle_quantum.qchem.complex_utils.rst b/docs/source/paddle_quantum.qchem.complex_utils.rst deleted file mode 100644 index 9847560..0000000 --- a/docs/source/paddle_quantum.qchem.complex_utils.rst +++ /dev/null @@ -1,7 +0,0 @@ -paddle\_quantum.qchem.complex\_utils -=========================================== - -.. automodule:: paddle_quantum.qchem.complex_utils - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/paddle_quantum.qchem.density_matrix.rst b/docs/source/paddle_quantum.qchem.density_matrix.rst deleted file mode 100644 index e134afb..0000000 --- a/docs/source/paddle_quantum.qchem.density_matrix.rst +++ /dev/null @@ -1,7 +0,0 @@ -paddle\_quantum.qchem.density\_matrix -============================================ - -.. automodule:: paddle_quantum.qchem.density_matrix - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/paddle_quantum.qchem.drivers.rst b/docs/source/paddle_quantum.qchem.drivers.rst new file mode 100644 index 0000000..7c8cd62 --- /dev/null +++ b/docs/source/paddle_quantum.qchem.drivers.rst @@ -0,0 +1,7 @@ +paddle\_quantum.qchem.drivers +================================== + +.. automodule:: paddle_quantum.qchem.drivers + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/paddle_quantum.qchem.fermionic_state.rst b/docs/source/paddle_quantum.qchem.fermionic_state.rst new file mode 100644 index 0000000..1da57e5 --- /dev/null +++ b/docs/source/paddle_quantum.qchem.fermionic_state.rst @@ -0,0 +1,7 @@ +paddle\_quantum.qchem.fermionic\_state +========================================= + +.. automodule:: paddle_quantum.qchem.fermionic_state + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/paddle_quantum.qchem.hardware_efficient.rst b/docs/source/paddle_quantum.qchem.hardware_efficient.rst deleted file mode 100644 index 3939418..0000000 --- a/docs/source/paddle_quantum.qchem.hardware_efficient.rst +++ /dev/null @@ -1,7 +0,0 @@ -paddle\_quantum.qchem.hardware\_efficient -================================================ - -.. automodule:: paddle_quantum.qchem.hardware_efficient - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/paddle_quantum.qchem.molecule.rst b/docs/source/paddle_quantum.qchem.molecule.rst new file mode 100644 index 0000000..af0e72d --- /dev/null +++ b/docs/source/paddle_quantum.qchem.molecule.rst @@ -0,0 +1,7 @@ +paddle\_quantum.qchem.molecule +========================================= + +.. automodule:: paddle_quantum.qchem.molecule + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/paddle_quantum.qchem.properties.rst b/docs/source/paddle_quantum.qchem.properties.rst new file mode 100644 index 0000000..efa5932 --- /dev/null +++ b/docs/source/paddle_quantum.qchem.properties.rst @@ -0,0 +1,7 @@ +paddle\_quantum.qchem.properties +========================================= + +.. automodule:: paddle_quantum.qchem.properties + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/paddle_quantum.qchem.rst b/docs/source/paddle_quantum.qchem.rst index f2beea7..a3d9f4a 100644 --- a/docs/source/paddle_quantum.qchem.rst +++ b/docs/source/paddle_quantum.qchem.rst @@ -11,10 +11,10 @@ paddle\_quantum.qchem .. toctree:: :maxdepth: 4 - paddle_quantum.qchem.complex_utils - paddle_quantum.qchem.density_matrix - paddle_quantum.qchem.hardware_efficient - paddle_quantum.qchem.loss - paddle_quantum.qchem.qchem - paddle_quantum.qchem.slater_determinant - paddle_quantum.qchem.uccsd + paddle_quantum.qchem.algorithm + paddle_quantum.qchem.ansatz + paddle_quantum.qchem.drivers + paddle_quantum.qchem.fermionic_state + paddle_quantum.qchem.molecule + paddle_quantum.qchem.properties + paddle_quantum.qchem.utils diff --git a/docs/source/paddle_quantum.qchem.slater_determinant.rst b/docs/source/paddle_quantum.qchem.slater_determinant.rst deleted file mode 100644 index 8fb5f3d..0000000 --- a/docs/source/paddle_quantum.qchem.slater_determinant.rst +++ /dev/null @@ -1,7 +0,0 @@ -paddle\_quantum.qchem.slater\_determinant -================================================ - -.. automodule:: paddle_quantum.qchem.slater_determinant - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/paddle_quantum.qchem.utils.rst b/docs/source/paddle_quantum.qchem.utils.rst new file mode 100644 index 0000000..8098353 --- /dev/null +++ b/docs/source/paddle_quantum.qchem.utils.rst @@ -0,0 +1,7 @@ +paddle\_quantum.qchem.utils +========================================= + +.. automodule:: paddle_quantum.qchem.utils + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/paddle_quantum.qchem.uccsd.rst b/docs/source/paddle_quantum.qml.qnnmic.rst similarity index 55% rename from docs/source/paddle_quantum.qchem.uccsd.rst rename to docs/source/paddle_quantum.qml.qnnmic.rst index 68a4aee..a9fd0b0 100644 --- a/docs/source/paddle_quantum.qchem.uccsd.rst +++ b/docs/source/paddle_quantum.qml.qnnmic.rst @@ -1,7 +1,7 @@ -paddle\_quantum.qchem.uccsd +paddle\_quantum.qml.qnnmic ================================== -.. automodule:: paddle_quantum.qchem.uccsd +.. automodule:: paddle_quantum.qml.qnnmic :members: :undoc-members: :show-inheritance: diff --git a/docs/source/paddle_quantum.qchem.qchem.rst b/docs/source/paddle_quantum.qml.qnnqd.rst similarity index 55% rename from docs/source/paddle_quantum.qchem.qchem.rst rename to docs/source/paddle_quantum.qml.qnnqd.rst index eb0e565..00689b8 100644 --- a/docs/source/paddle_quantum.qchem.qchem.rst +++ b/docs/source/paddle_quantum.qml.qnnqd.rst @@ -1,7 +1,7 @@ -paddle\_quantum.qchem.qchem +paddle\_quantum.qml.qnnqd ================================== -.. automodule:: paddle_quantum.qchem.qchem +.. automodule:: paddle_quantum.qml.qnnqd :members: :undoc-members: :show-inheritance: diff --git a/docs/source/paddle_quantum.qml.qsann.rst b/docs/source/paddle_quantum.qml.qsann.rst new file mode 100644 index 0000000..7650ee8 --- /dev/null +++ b/docs/source/paddle_quantum.qml.qsann.rst @@ -0,0 +1,7 @@ +paddle\_quantum.qml.qsann +================================== + +.. automodule:: paddle_quantum.qml.qsann + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/paddle_quantum.qml.rst b/docs/source/paddle_quantum.qml.rst index 270547c..788edb9 100644 --- a/docs/source/paddle_quantum.qml.rst +++ b/docs/source/paddle_quantum.qml.rst @@ -11,4 +11,7 @@ paddle\_quantum.qml .. toctree:: :maxdepth: 4 + paddle_quantum.qml.qnnmic + paddle_quantum.qml.qnnqd + paddle_quantum.qml.qsann paddle_quantum.qml.vsql diff --git a/docs_zh_CN/source/_static/.gitkeep b/docs_zh_CN/source/_static/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs_zh_CN/source/introduction.rst b/docs_zh_CN/source/introduction.rst index 9eba1a9..e203088 100644 --- a/docs_zh_CN/source/introduction.rst +++ b/docs_zh_CN/source/introduction.rst @@ -56,17 +56,17 @@ Paddle Quantum(量桨) 我们推荐通过 ``pip`` 完成安装, -.. code:: shell +.. code-block:: shell pip install paddle-quantum 用户也可以选择下载全部文件后进行本地安装, -.. code:: shell +.. code-block:: shell git clone http://github.com/PaddlePaddle/quantum -.. code:: shell +.. code-block:: shell cd quantum pip install -e . @@ -86,13 +86,13 @@ Paddle Quantum(量桨) 在安装 ``psi4`` 时,我们建议您使用 conda。对于 **MacOS/Linux** 的用户,可以使用如下指令。 -.. code:: shell +.. code-block:: shell conda install psi4 -c psi4 对于 **Windows** 用户,请使用 -.. code:: shell +.. code-block:: shell conda install psi4 -c psi4 -c conda-forge @@ -106,7 +106,7 @@ Paddle Quantum(量桨) 现在,可以试着运行一段程序来验证量桨是否已安装成功。这里我们运行量桨提供的量子近似优化算法(QAOA)的例子。 -.. code:: shell +.. code-block:: shell cd paddle_quantum/QAOA/example python main.py @@ -120,7 +120,7 @@ Paddle Quantum(量桨) 交流与反馈 ---------- -- 我们非常欢迎您通过 `Github +- 我们非常欢迎您通过 `GitHub Issues `__ 来提交问题、报告与建议。 - 技术交流QQ群:1076223166 diff --git a/docs_zh_CN/source/modules.rst b/docs_zh_CN/source/modules.rst index d870e56..cd04910 100644 --- a/docs_zh_CN/source/modules.rst +++ b/docs_zh_CN/source/modules.rst @@ -3,25 +3,28 @@ paddle_quantum.ansatz paddle_quantum.backend + paddle_quantum.biocomputing paddle_quantum.channel + paddle_quantum.data_analysis + paddle_quantum.finance paddle_quantum.gate paddle_quantum.locc paddle_quantum.loss paddle_quantum.mbqc paddle_quantum.operator paddle_quantum.qchem + paddle_quantum.qml + paddle_quantum.qpp + paddle_quantum.qsvt paddle_quantum.state paddle_quantum.base paddle_quantum.dataset - paddle_quantum.finance paddle_quantum.fisher paddle_quantum.gradtool paddle_quantum.hamiltonian paddle_quantum.linalg + paddle_quantum.model paddle_quantum.qinfo - paddle_quantum.qml paddle_quantum.shadow paddle_quantum.trotter paddle_quantum.visual - paddle_quantum.qsvt - paddle_quantum.qpp diff --git a/docs_zh_CN/source/paddle_quantum.ansatz.circuit.rst b/docs_zh_CN/source/paddle_quantum.ansatz.circuit.rst index 55285b3..6425197 100644 --- a/docs_zh_CN/source/paddle_quantum.ansatz.circuit.rst +++ b/docs_zh_CN/source/paddle_quantum.ansatz.circuit.rst @@ -1016,6 +1016,17 @@ paddle\_quantum.ansatz.circuit :type qubits_idx: Union[Iterable[int], int, str], optional :param num_qubits: 总的量子比特个数,默认为 ``None``。 :type num_qubits: int, optional + + .. py:method:: generalized_depolarizing(prob, qubits_idx, num_qubits=None) + + 添加一个广义去极化信道。 + + :param prob: 该信道的参数。 + :type prob: Union[paddle.Tensor, float] + :param qubits_idx: 作用在的量子比特的编号。 + :type qubits_idx: Union[Iterable[int], int, str], optional + :param num_qubits: 总的量子比特个数,默认为 ``None``。 + :type num_qubits: int, optional .. py:method:: pauli_channel(prob, qubits_idx='full', num_qubits=None) diff --git a/docs_zh_CN/source/paddle_quantum.biocomputing.rst b/docs_zh_CN/source/paddle_quantum.biocomputing.rst new file mode 100644 index 0000000..b4c01aa --- /dev/null +++ b/docs_zh_CN/source/paddle_quantum.biocomputing.rst @@ -0,0 +1,2 @@ +paddle_quantum.biocomputing +======================================= \ No newline at end of file diff --git a/docs_zh_CN/source/paddle_quantum.channel.base.rst b/docs_zh_CN/source/paddle_quantum.channel.base.rst index dc0d8ff..8274e03 100644 --- a/docs_zh_CN/source/paddle_quantum.channel.base.rst +++ b/docs_zh_CN/source/paddle_quantum.channel.base.rst @@ -17,3 +17,30 @@ paddle\_quantum.channel.base 参数名为"my_layer_0.w_n",其中 "w" 是参数的名称,"n" 为自动生成的具有唯一性的后缀。如果为 ``None``, 前缀名将为小写的类名。默认为 ``None``。 :type name_scope: str, optional + + .. py:property:: choi_repr() + + 该信道的 Choi 表达式。 + + :raises NotImplementedError: 无法返回一个广义信道的 Choi 表达式。使用 ``ChoiRepr`` 来具体化你的信道。 + + :return: 一个形状为 :math:`[d_\text{out}^2, d_\text{in}^2]` 的 Tensor,这里 :math:`d_\text{in/out}` 为信道的输入/出维度。 + :rtype: paddle.Tensor + + .. py:property:: kraus_repr() + + 该信道的 Kraus 表达式。 + + :raises NotImplementedError: 无法返回一个广义信道的 Kraus 表达式。使用 ``KrausRepr`` 来具体化你的信道。 + + :return: 一个形状为 :math:`[d_\text{out}, d_\text{in}]` 的 Tensor,这里 :math:`d_\text{in/out}` 为信道的输入/出维度。 + :rtype: paddle.Tensor + + .. py:property:: stinespring_repr() + + 该信道的 Stinespring 表达式。 + + :raises NotImplementedError: 无法返回一个广义信道的 Stinespring 表达式。使用 ``StinespringRepr`` 来具体化你的信道。 + + :return: 一个形状为 :math:`[r * d_\text{out}, d_\text{in}]` 的 Tensor,这里 :math:`r` 为信道的秩,且 :math:`d_\text{in/out}` 为信道的输入/出维度。 + :rtype: paddle.Tensor \ No newline at end of file diff --git a/docs_zh_CN/source/paddle_quantum.channel.common.rst b/docs_zh_CN/source/paddle_quantum.channel.common.rst index ff5adc2..c0db7c5 100644 --- a/docs_zh_CN/source/paddle_quantum.channel.common.rst +++ b/docs_zh_CN/source/paddle_quantum.channel.common.rst @@ -3,9 +3,9 @@ paddle\_quantum.channel.common 常用的量子信道的功能实现。 -.. py:class:: BitFlip(prob, qubits_idx='full', num_qubits) +.. py:class:: BitFlip(prob, qubits_idx='full', num_qubits=None) - 基类::py:class:`paddle_quantum.channel.base.Channel` + 基类::py:class:`paddle_quantum.channel.custom.KrausRepr` 比特反转信道。 @@ -23,9 +23,9 @@ paddle\_quantum.channel.common :param num_qubits: 总的量子比特个数,默认为 ``None``。 :type num_qubits: int, optional -.. py:class:: PhaseFlip(prob, qubits_idx='full', num_qubits) +.. py:class:: PhaseFlip(prob, qubits_idx='full', num_qubits=None) - 基类::py:class:`paddle_quantum.channel.base.Channel` + 基类::py:class:`paddle_quantum.channel.custom.KrausRepr` 相位反转信道。 @@ -43,9 +43,9 @@ paddle\_quantum.channel.common :param num_qubits: 总的量子比特个数,默认为 ``None``。 :type num_qubits: int, optional -.. py:class:: BitPhaseFlip(prob, qubits_idx='full', num_qubits) +.. py:class:: BitPhaseFlip(prob, qubits_idx='full', num_qubits=None) - 基类::py:class:`paddle_quantum.channel.base.Channel` + 基类::py:class:`paddle_quantum.channel.custom.KrausRepr` 比特相位反转信道。 @@ -63,9 +63,9 @@ paddle\_quantum.channel.common :param num_qubits: 总的量子比特个数,默认为 ``None``。 :type num_qubits: int, optional -.. py:class:: AmplitudeDamping(gamma, qubits_idx='full', num_qubits) +.. py:class:: AmplitudeDamping(gamma, qubits_idx='full', num_qubits=None) - 基类::py:class:`paddle_quantum.channel.base.Channel` + 基类::py:class:`paddle_quantum.channel.custom.KrausRepr` 振幅阻尼信道。 @@ -91,9 +91,9 @@ paddle\_quantum.channel.common :param num_qubits: 总的量子比特个数,默认为 ``None``。 :type num_qubits: int, optional -.. py:class:: GeneralizedAmplitudeDamping(gamma, prob, qubits_idx='full', num_qubits) +.. py:class:: GeneralizedAmplitudeDamping(gamma, prob, qubits_idx='full', num_qubits=None) - 基类::py:class:`paddle_quantum.channel.base.Channel` + 基类::py:class:`paddle_quantum.channel.custom.KrausRepr` 广义振幅阻尼信道。 @@ -119,9 +119,9 @@ paddle\_quantum.channel.common :param num_qubits: 总的量子比特个数,默认为 ``None``。 :type num_qubits: int, optional -.. py:class:: PhaseDamping(gamma, qubits_idx='full', num_qubits) +.. py:class:: PhaseDamping(gamma, qubits_idx='full', num_qubits=None) - 基类::py:class:`paddle_quantum.channel.base.Channel` + 基类::py:class:`paddle_quantum.channel.custom.KrausRepr` 相位阻尼信道。 @@ -147,9 +147,9 @@ paddle\_quantum.channel.common :param num_qubits: 总的量子比特个数,默认为 ``None``。 :type num_qubits: int, optional -.. py:class:: Depolarizing(prob, qubits_idx='full', num_qubits) +.. py:class:: Depolarizing(prob, qubits_idx='full', num_qubits=None) - 基类::py:class:`paddle_quantum.channel.base.Channel` + 基类::py:class:`paddle_quantum.channel.custom.KrausRepr` 去极化信道。 @@ -174,9 +174,29 @@ paddle\_quantum.channel.common 当前版本请参考 M.A.Nielsen and I.L.Chuang 所著 Quantum Computation and Quantum Information 第10版中的 (8.102) 式。 参考文献: Nielsen, M., & Chuang, I. (2010). Quantum Computation and Quantum Information: 10th Anniversary Edition. Cambridge: Cambridge University Press. doi:10.1017/CBO9780511976667 -.. py:class:: PauliChannel(prob, qubits_idx='full', num_qubits) +.. py:class:: GeneralizedDepolarizing(prob, qubits_idx='full', num_qubits=None) - 基类::py:class:`paddle_quantum.channel.base.Channel` + 基类::py:class:`paddle_quantum.channel.custom.KrausRepr` + + 广义去极化信道。 + + 其 Kraus 算符为: + + .. math:: + + E_0 = \sqrt{1-(D - 1)p/D} I, \text{ where } D = 4^n, \\ + E_k = \sqrt{p/D} \sigma_k, \text{ for } 0 < k < D. + + :param prob: 该信道的参数 :math:`p`,其值应该在 :math:`[0, 1]` 区间内。 + :type prob: Union[paddle.Tensor, float] + :param qubits_idx: 长度为 :math:`n` 的作用在的量子比特的编号,默认为 ``'full'``。 + :type qubits_idx: Union[Iterable[int], int, str], optional + :param num_qubits: 总的量子比特个数,默认为 ``None``。 + :type num_qubits: int, optional + +.. py:class:: PauliChannel(prob, qubits_idx='full', num_qubits=None) + + 基类::py:class:`paddle_quantum.channel.custom.KrausRepr` 泡利信道。 @@ -191,9 +211,9 @@ paddle\_quantum.channel.common 三个输入的概率加起来需要小于等于 1。 -.. py:class:: ResetChannel(prob, qubits_idx='full', num_qubits) +.. py:class:: ResetChannel(prob, qubits_idx='full', num_qubits=None) - 基类::py:class:`paddle_quantum.channel.base.Channel` + 基类::py:class:`paddle_quantum.channel.custom.KrausRepr` 重置信道。 @@ -236,7 +256,7 @@ paddle\_quantum.channel.common .. py:class:: ThermalRelaxation(const_t, exec_time, qubits_idx='full', num_qubits=None) - 基类::py:class:`paddle_quantum.channel.base.Channel` + 基类::py:class:`paddle_quantum.channel.custom.KrausRepr` 热弛豫信道。 @@ -257,7 +277,7 @@ paddle\_quantum.channel.common .. py:class:: MixedUnitaryChannel(num_unitary, qubits_idx='full', num_qubits=None) - 基类::py:class:`paddle_quantum.channel.base.Channel` + 基类::py:class:`paddle_quantum.channel.custom.KrausRepr` 混合酉矩阵信道。 diff --git a/docs_zh_CN/source/paddle_quantum.channel.custom.rst b/docs_zh_CN/source/paddle_quantum.channel.custom.rst index 4c79d0c..b174674 100644 --- a/docs_zh_CN/source/paddle_quantum.channel.custom.rst +++ b/docs_zh_CN/source/paddle_quantum.channel.custom.rst @@ -3,6 +3,46 @@ paddle\_quantum.channel.custom 自定义量子信道的类的功能实现。 +.. py:class:: ChoiRepr(choi_oper, qubits_idx, num_qubits=None) + + 基类::py:class:`paddle_quantum.channel.base.Channel` + + Choi 表示的自定义量子信道。 + + :param choi_oper: 该信道的 Choi 算符。 + :type choi_oper: paddle.Tensor + :param qubits_idx: 作用在的量子比特的编号。 + :type qubits_idx: Union[Iterable[Iterable[int]], Iterable[int], int] + :param num_qubits: 总的量子比特个数,默认为 ``None``。 + :type num_qubits: int, optional + :raises NotImplementedError: 噪声信道只能在密度矩阵模式下运行。 + + .. py:property:: choi_repr() + + 该信道的 Choi 表达式。 + + .. py:property:: kraus_repr() + + 该信道的 Kraus 表达式。 + + .. py:property:: stinespring_repr() + + 该信道的 Stinespring 表达式。 + + .. py:method:: to_kraus() + + 返回一个转为 Kraus 表示及其逻辑运算的该信道。 + + :return: Kraus 表示的该信道。 + :rtype: KrausRepr + + .. py:method:: to_stinespring() + + 返回一个转为 Stinespring 表示及其逻辑运算的该信道。 + + :return: Stinespring 表示的该信道。 + :rtype: StinespringRepr + .. py:class:: KrausRepr(kraus_oper, qubits_idx, num_qubits=None) 基类::py:class:`paddle_quantum.channel.base.Channel` @@ -15,7 +55,70 @@ paddle\_quantum.channel.custom :type qubits_idx: Union[Iterable[Iterable[int]], Iterable[int], int] :param num_qubits: 总的量子比特个数,默认为 ``None``。 :type num_qubits: int, optional + :raises NotImplementedError: 噪声信道只能在密度矩阵模式下运行。 + + .. py:property:: choi_repr() + + 该信道的 Choi 表达式。 + + .. py:property:: kraus_repr() -.. py:class:: ChoiRepr() + 该信道的 Kraus 表达式。 + + .. py:property:: stinespring_repr() + + 该信道的 Stinespring 表达式。 + + .. py:method:: to_choi() + + 返回一个转为 Choi 表示及其逻辑运算的该信道。 + + :return: Choi 表示的该信道。 + :rtype: ChoiRepr + + .. py:method:: to_stinespring() + + 返回一个转为 Stinespring 表示及其逻辑运算的该信道。 + + :return: Stinespring 表示的该信道。 + :rtype: StinespringRepr + +.. py:class:: StinespringRepr(stinespring_mat, qubits_idx, num_qubits=None) 基类::py:class:`paddle_quantum.channel.base.Channel` + + Stinespring 表示的自定义量子信道。 + + :param stinespring_mat: 一个用来表示该信道的 Stinespring 矩阵。 + :type stinespring_mat: paddle.Tensor + :param qubits_idx: 作用在的量子比特的编号。 + :type qubits_idx: Union[Iterable[Iterable[int]], Iterable[int], int] + :param num_qubits: 总的量子比特个数,默认为 ``None``。 + :type num_qubits: int, optional + :raises NotImplementedError: 噪声信道只能在密度矩阵模式下运行。 + + .. py:property:: choi_repr() + + 该信道的 Choi 表达式。 + + .. py:property:: kraus_repr() + + 该信道的 Kraus 表达式。 + + .. py:property:: stinespring_repr() + + 该信道的 Stinespring 表达式。 + + .. py:method:: to_choi() + + 返回一个转为 Choi 表示及其逻辑运算的该信道。 + + :return: Choi 表示的该信道。 + :rtype: ChoiRepr + + .. py:method:: to_kraus() + + 返回一个转为 Kraus 表示及其逻辑运算的该信道。 + + :return: Kraus 表示的该信道。 + :rtype: KrausRepr diff --git a/docs_zh_CN/source/paddle_quantum.channel.functional.common.rst b/docs_zh_CN/source/paddle_quantum.channel.functional.common.rst index 40e421c..dac001b 100644 --- a/docs_zh_CN/source/paddle_quantum.channel.functional.common.rst +++ b/docs_zh_CN/source/paddle_quantum.channel.functional.common.rst @@ -1,208 +1,72 @@ paddle\_quantum.channel.functional.common ================================================ -常见量子信道的函数的功能实现。 +量子信道的底层逻辑。 -.. py:function:: bit_flip(state, prob, qubit_idx, dtype, backend) +.. py:function:: kraus_repr(state, kraus_oper, qubit_idx, dtype, backend) - 在输入态上作用一个比特反转信道。 + 在输入态上作用一个 Kraus 表示的自定义量子信道。 :param state: 输入态。 :type state: paddle_quantum.State - :param prob: 发生比特反转的概率。 - :type prob: paddle.Tensor + :param kraus_oper: 该信道的 Kraus 算符。 + :type kraus_oper: Iterable[paddle.Tensor] :param qubit_idx: 作用在的量子比特的编号。 :type qubit_idx: int - :param dtype: 数据的类型。 + :param dtype: 数据类型。 :type dtype: str :param backend: 运行模拟的后端。 :type backend: paddle_quantum.Backend :raises RuntimeError: 噪声信道只能在密度矩阵模式下运行。 - :return: 输出态。 - :rtype: paddle_quantum.State - -.. py:function:: phase_flip(state, prob, qubit_idx, dtype, backend) - - 在输入态上作用一个相位反转信道。 - :param state: 输入态。 - :type state: paddle_quantum.State - :param prob: 发生相位反转的概率。 - :type prob: paddle.Tensor - :param qubit_idx: 作用在的量子比特的编号。 - :type qubit_idx: int - :param dtype: 数据的类型。 - :type dtype: str - :param backend: 运行模拟的后端。 - :type backend: paddle_quantum.Backend - :raises RuntimeError: 噪声信道只能在密度矩阵模式下运行。 :return: 输出态。 :rtype: paddle_quantum.State -.. py:function:: bit_phase_flip(state, prob, qubit_idx, dtype, backend) +.. py:function:: choi_repr(state, choi_oper, qubit_idx:, dtype, backend) - 在输入态上作用一个比特相位反转信道。 - - :param state: 输入态。 - :type state: paddle_quantum.State - :param prob: 发生比特相位反转的概率。 - :type prob: paddle.Tensor - :param qubit_idx: 作用在的量子比特的编号。 - :type qubit_idx: int - :param dtype: 数据的类型。 - :type dtype: str - :param backend: 运行模拟的后端。 - :type backend: paddle_quantum.Backend - :raises RuntimeError: 噪声信道只能在密度矩阵模式下运行。 - :return: 输出态。 - :rtype: paddle_quantum.State + 在输入态上作用一个 Choi 表示的自定义量子信道。Choi 表示的数学形式为 -.. py:function:: amplitude_damping(state, gamma, qubit_idx, dtype, backend) + .. math:: - 在输入态上作用一个振幅阻尼信道。 + \sum_{i, j} |i\rangle\langle j| \otimes N(|i\rangle\langle j|) :param state: 输入态。 :type state: paddle_quantum.State - :param gamma: 减振概率。 - :type gamma: paddle.Tensor + :param choi_oper: 该信道 :math:`N` 的 Choi 算符。 + :type choi_oper: paddle.Tensor :param qubit_idx: 作用在的量子比特的编号。 :type qubit_idx: int - :param dtype: 数据的类型。 + :param dtype: 数据类型。 :type dtype: str :param backend: 运行模拟的后端。 :type backend: paddle_quantum.Backend :raises RuntimeError: 噪声信道只能在密度矩阵模式下运行。 - :return: 输出态。 - :rtype: paddle_quantum.State - -.. py:function:: generalized_amplitude_damping(state, gamma, prob, qubit_idx, dtype, backend) - - 在输入态上作用一个广义振幅阻尼信道。 - :param state: 输入态。 - :type state: paddle_quantum.State - :param gamma: 减振概率。 - :type gamma: paddle.Tensor - :param prob: 激发概率。 - :type prob: paddle.Tensor - :param qubit_idx: 作用在的量子比特的编号。 - :type qubit_idx: int - :param dtype: 数据的类型。 - :type dtype: str - :param backend: 运行模拟的后端。 - :type backend: paddle_quantum.Backend - :raises RuntimeError: 噪声信道只能在密度矩阵模式下运行。 :return: 输出态。 :rtype: paddle_quantum.State -.. py:function:: phase_damping(state, gamma, qubit_idx, dtype, backend) - - 在输入态上作用一个相位阻尼信道。 +.. py:function:: stinespring_repr(state, stinespring_mat, qubit_idx:, dtype, backend) - :param state: 输入态。 - :type state: paddle_quantum.State - :param gamma: 该信道的参数。 - :type gamma: paddle.Tensor - :param qubit_idx: 作用在的量子比特的编号。 - :type qubit_idx: int - :param dtype: 数据的类型。 - :type dtype: str - :param backend: 运行模拟的后端。 - :type backend: paddle_quantum.Backend - :raises RuntimeError: 噪声信道只能在密度矩阵模式下运行。 - :return: 输出态。 - :rtype: paddle_quantum.State + 在输入态上作用一个 Stinespring 表示的自定义量子信道。 ``stinespring_mat`` 是一个 :math:`(d_1 * d_2) \times d_1` 的长方矩阵。 + 其中 :math:`d_1` 为 ``qubit_idx`` 所在系统维度;:math:`d_2` 为辅助系统维度。通过 Dirac 符号我们可以将 ``stinespring_mat`` 表示为 -.. py:function:: depolarizing(state, prob, qubit_idx, dtype, backend) + .. math:: + + \text{stinespring_mat.reshape}([d_1, d_2, d_1])[i, j, k] = \langle i, j| A |k \rangle - 在输入态上作用一个去极化信道。 + 这里 :math:`A` 为 Stinespring 表示,且该信道可定义为 :math:`\rho \mapsto \text{tr}_2 (A \rho A^dagger)`。 :param state: 输入态。 :type state: paddle_quantum.State - :param prob: 该信道的参数。 - :type prob: paddle.Tensor + :param stinespring_mat: 该信道的 Stinespring 表示所组成的矩阵。 + :type stinespring_mat: paddle.Tensor :param qubit_idx: 作用在的量子比特的编号。 :type qubit_idx: int - :param dtype: 数据的类型。 + :param dtype: 数据类型。 :type dtype: str :param backend: 运行模拟的后端。 :type backend: paddle_quantum.Backend :raises RuntimeError: 噪声信道只能在密度矩阵模式下运行。 - :return: 输出态。 - :rtype: paddle_quantum.State - -.. py:function:: pauli_channel(state, prob, qubit_idx, dtype, backend) - 在输入态上作用一个泡利信道。 - - :param state: 输入态。 - :type state: paddle_quantum.State - :param prob: 泡利算符 X、Y、Z 对应的概率。 - :type prob: paddle.Tensor - :param qubit_idx: 作用在的量子比特的编号。 - :type qubit_idx: int - :param dtype: 数据的类型。 - :type dtype: str - :param backend: 运行模拟的后端。 - :type backend: paddle_quantum.Backend - :raises RuntimeError: 噪声信道只能在密度矩阵模式下运行。 - :return: 输出态。 - :rtype: paddle_quantum.State - -.. py:function:: reset_channel(state, prob, qubit_idx, dtype, backend) - - 在输入态上作用一个重置信道。 - - :param state: 输入态。 - :type state: paddle_quantum.State - :param prob: 重置为 :math:`|0\rangle` 和重置为 :math:`|1\rangle` 的概率。 - :type prob: paddle.Tensor - :param qubit_idx: 作用在的量子比特的编号。 - :type qubit_idx: int - :param dtype: 数据的类型。 - :type dtype: str - :param backend: 运行模拟的后端。 - :type backend: paddle_quantum.Backend - :raises RuntimeError: 噪声信道只能在密度矩阵模式下运行。 :return: 输出态。 :rtype: paddle_quantum.State - -.. py:function:: thermal_relaxation(state, const_t, exec_time, qubit_idx, dtype, backend) - - 在输入态上作用一个热弛豫信道。 - - :param state: 输入态。 - :type state: paddle_quantum.State - :param const_t: :math:`T_1` 和 :math:`T_2` 过程的弛豫时间常数,单位是微秒。 - :type const_t: paddle.Tensor - :param exec_time: 弛豫过程中量子门的执行时间,单位是纳秒。 - :type exec_time: paddle.Tensor - :param qubit_idx: 作用在的量子比特的编号。 - :type qubit_idx: int - :param dtype: 数据的类型。 - :type dtype: str - :param backend: 运行模拟的后端。 - :type backend: paddle_quantum.Backend - :raises RuntimeError: 噪声信道只能在密度矩阵模式下运行。 - :return: 输出态。 - :rtype: paddle_quantum.State - -.. py:function:: kraus_repr(state, kraus_oper, qubit_idx:, dtype, backend) - - 在输入态上作用一个 Kraus 表示的自定义量子信道。 - - :param state: 输入态。 - :type state: paddle_quantum.State - :param kraus_oper: 该信道的 Kraus 算符。 - :type kraus_oper: Iterable[paddle.Tensor] - :param qubit_idx: 作用在的量子比特的编号。 - :type qubit_idx: int - :param dtype: 数据的类型。 - :type dtype: str - :param backend: 运行模拟的后端。 - :type backend: paddle_quantum.Backend - :raises RuntimeError: 噪声信道只能在密度矩阵模式下运行。 - :return: 输出态。 - :rtype: paddle_quantum.State - -.. py:function:: choi_repr() diff --git a/docs_zh_CN/source/paddle_quantum.channel.representation.rst b/docs_zh_CN/source/paddle_quantum.channel.representation.rst new file mode 100644 index 0000000..fbb9a51 --- /dev/null +++ b/docs_zh_CN/source/paddle_quantum.channel.representation.rst @@ -0,0 +1,226 @@ +paddle\_quantum.channel.representation +========================================== + +量桨量子信道的表达式库。 + +.. py:function:: bit_flip_kraus(prob, dtype=None) + + 比特反转信道的Kraus表达式,其形式为 + + .. math:: + + E_0 = \sqrt{1-p} I, + E_1 = \sqrt{p} X. + + :param prob: 概率 :math:`p`。 + :type prob: Union[float, np.ndarray, paddle.Tensor] + :param dtype: 数据类型。默认为 ``None``。 + :type dtype: str, optional + + :return: 返回对应的 Kraus 算符 + :rtype: List[paddle.Tensor] + +.. py:function:: phase_flip_kraus(prob, dtype=None) + + 相位反转信道的Kraus表达式,其形式为 + + .. math:: + + E_0 = \sqrt{1-p} I, + E_1 = \sqrt{p} Z. + + :param prob: 概率 :math:`p`。 + :type prob: Union[float, np.ndarray, paddle.Tensor] + :param dtype: 数据类型。默认为 ``None``。 + :type dtype: str, optional + + :return: 返回对应的 Kraus 算符 + :rtype: List[paddle.Tensor] + +.. py:function:: bit_phase_flip_kraus(prob, dtype=None) + + 比特相位反转信道的Kraus表达式,其形式为 + + .. math:: + + E_0 = \sqrt{1-p} I, + E_1 = \sqrt{p} Y. + + :param prob: 概率 :math:`p`。 + :type prob: Union[float, np.ndarray, paddle.Tensor] + :param dtype: 数据类型。默认为 ``None``。 + :type dtype: str, optional + + :return: 返回对应的 Kraus 算符 + :rtype: List[paddle.Tensor] + +.. py:function:: amplitude_damping_kraus(gamma, dtype=None) + + 振幅阻尼信道的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}. + + :param gamma: 系数 :math:`\gamma`。 + :type gamma: Union[float, np.ndarray, paddle.Tensor] + :param dtype: 数据类型。默认为 ``None``。 + :type dtype: str, optional + + :return: 返回对应的 Kraus 算符 + :rtype: List[paddle.Tensor] + +.. py:function:: generalized_amplitude_damping_kraus(gamma, prob, dtype=None) + + 广义振幅阻尼信道的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}. + + :param gamma: 系数 :math:`\gamma`。 + :type gamma: Union[float, np.ndarray, paddle.Tensor] + :param prob: 概率 :math:`p`。 + :type prob: Union[float, np.ndarray, paddle.Tensor] + :param dtype: 数据类型。默认为 ``None``。 + :type dtype: str, optional + + :return: 返回对应的 Kraus 算符 + :rtype: List[paddle.Tensor] + +.. py:function:: phase_damping_kraus(gamma, dtype=None) + + 相位阻尼信道的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}. + + :param gamma: 系数 :math:`\gamma`。 + :type gamma: Union[float, np.ndarray, paddle.Tensor] + :param dtype: 数据类型。默认为 ``None``。 + :type dtype: str, optional + + :return: 返回对应的 Kraus 算符 + :rtype: List[paddle.Tensor] + +.. py:function:: depolarizing_kraus(prob, dtype=None) + + 去极化信道的Kraus表达式,其形式为 + + .. math:: + + E_0 = \sqrt{1-3p/4} I, + E_1 = \sqrt{p/4} X, + E_2 = \sqrt{p/4} Y, + E_3 = \sqrt{p/4} Z. + + :param prob: 概率 :math:`p`。 + :type prob: Union[float, np.ndarray, paddle.Tensor] + :param dtype: 数据类型。默认为 ``None``。 + :type dtype: str, optional + + :return: 返回对应的 Kraus 算符 + :rtype: List[paddle.Tensor] + +.. py:function:: generalized_depolarizing_kraus(prob, num_qubits, dtype=None) + + 广义去极化信道的Kraus表达式,其形式为 + + .. math:: + + E_0 = \sqrt{1-(D - 1)p/D} I, \text{ where } D = 4^n, \\ + E_k = \sqrt{p/D} \sigma_k, \text{ for } 0 < k < D. + + :param prob: 概率 :math:`p`。 + :type prob: float + :param num_qubits: 信道的比特数 :math:`n`。 + :type num_qubits: int + :param dtype: 数据类型。默认为 ``None``。 + :type dtype: str, optional + + :return: 返回对应的 Kraus 算符 + :rtype: List[paddle.Tensor] + +.. py:function:: pauli_kraus(prob, dtype=None) + + 泡利信道的Kraus表达式。 + + :param prob: 泡利算符 X、Y、Z 对应的概率。 + :type prob: Union[List[float], np.ndarray, paddle.Tensor] + :param dtype: 数据类型。默认为 ``None``。 + :type dtype: str, optional + + :return: 返回对应的 Kraus 算符 + :rtype: List[paddle.Tensor] + +.. py:function:: reset_kraus(prob, dtype=None) + + 重置信道的Kraus表达式,其形式为 + + .. math:: + + E_0 = + \begin{bmatrix} + \sqrt{p} & 0 \\ + 0 & 0 + \end{bmatrix}, + E_1 = + \begin{bmatrix} + 0 & \sqrt{p} \\ + 0 & 0 + \end{bmatrix},\\ + E_2 = + \begin{bmatrix} + 0 & 0 \\ + \sqrt{q} & 0 + \end{bmatrix}, + E_3 = + \begin{bmatrix} + 0 & 0 \\ + 0 & \sqrt{q} + \end{bmatrix},\\ + E_4 = \sqrt{1-p-q} I. + + :param prob: 重置为 :math:`|0\rangle` 和重置为 :math:`|1\rangle` 的概率。 + :type prob: Union[List[float], np.ndarray, paddle.Tensor] + :param dtype: 数据类型。默认为 ``None``。 + :type dtype: str, optional + + :return: 返回对应的 Kraus 算符 + :rtype: List[paddle.Tensor] + +.. py:function:: thermal_relaxation_kraus(const_t, exec_time, dtype=None) + + 热弛豫信道的Kraus表达式。 + + :param const_t: :math:`T_1` 和 :math:`T_2` 过程的弛豫时间常数,单位是微秒。 + :type const_t: Union[List[float], np.ndarray, paddle.Tensor] + :param exec_time: 弛豫过程中量子门的执行时间,单位是纳秒。 + :type exec_time: Union[List[float], np.ndarray, paddle.Tensor] + :param dtype: 数据类型。默认为 ``None``。 + :type dtype: str, optional + + :return: 返回对应的 Kraus 算符 + :rtype: List[paddle.Tensor] \ No newline at end of file diff --git a/docs_zh_CN/source/paddle_quantum.channel.rst b/docs_zh_CN/source/paddle_quantum.channel.rst index caf396f..5df7015 100644 --- a/docs_zh_CN/source/paddle_quantum.channel.rst +++ b/docs_zh_CN/source/paddle_quantum.channel.rst @@ -23,3 +23,4 @@ paddle\_quantum.channel paddle_quantum.channel.base paddle_quantum.channel.common paddle_quantum.channel.custom + paddle_quantum.channel.representation diff --git a/docs_zh_CN/source/paddle_quantum.data_analysis.rst b/docs_zh_CN/source/paddle_quantum.data_analysis.rst new file mode 100644 index 0000000..22bab8a --- /dev/null +++ b/docs_zh_CN/source/paddle_quantum.data_analysis.rst @@ -0,0 +1,12 @@ +paddle\_quantum.data_analysis +============================= + +量子数据分析模块。 + +.. rubric:: Submodules + +.. toctree:: + :maxdepth: 4 + + paddle_quantum.data_analysis.vqr + paddle_quantum.data_analysis.vqls \ No newline at end of file diff --git a/docs_zh_CN/source/paddle_quantum.data_analysis.vqls.rst b/docs_zh_CN/source/paddle_quantum.data_analysis.vqls.rst new file mode 100644 index 0000000..4e5afd2 --- /dev/null +++ b/docs_zh_CN/source/paddle_quantum.data_analysis.vqls.rst @@ -0,0 +1,76 @@ +paddle\_quantum.data_analysis.vqls +============================================= +VQLS模型 + +.. py:function:: hadamard_test(phi, U, num_qubits) + + 给定酉算子 U 和量子态 :math:`|\phi\rangle`,计算 U 关于 :math:`|\phi\rangle` 的期望,即 :math:`\langle\phi|U|\phi\rangle`。 + + :param phi: 期望值里的量子态。 + :type phi: State + :param U: 期望值里的酉算子。 + :type U: paddle.Tensor + :param num_qubits: 量子态的比特数。 + :type num_qubits: int + + :return: 返回计算的期望值的实数和虚数部分。 + :rtype: Tuple[paddle.Tensor, paddle.Tensor] + +.. py:function:: hadamard_overlap_test(phi, b, An, Am, num_qubits) + + 给定酉算子 Am, An 和量子态 :math:`|\phi\rangle`, b, 计算 :math:`\langle{b}| An |\phi\rangle\langle\phi| Am^\dagger |b\rangle` 的值。 + + :param phi: 计算里的量子态。 + :type phi: State + :param b: 计算里的量子态。 + :type b: State + :param Am: 计算里的酉算子。 + :type Am: paddle.Tensor + :param An: 计算里的酉算子。 + :type An: paddle.Tensor + :param num_qubits: 量子态的比特数。 + :type num_qubits: int + + :return: 返回计算的实数和虚数部分。 + :rtype: Tuple[paddle.Tensor, paddle.Tensor] + +.. py:class:: VQLS(num_qubits, A, coefficients_real, coefficients_img, b, depth) + + 基类::py:class:`paddle.nn.Layer` + + 变分量子线性求解器(variational quantum linear solver, VQLS)模型的实现。 + + :param num_qubits: 量子电路所包含的量子比特的数量。 + :type num_qubits: int + :param A: 分解输入矩阵所需要的酉矩阵列表。 + :type A: List[paddle.Tensor] + :param coefficients_real: 对应酉矩阵系数的实数部分。 + :type coefficients_real: List[float] + :param coefficients_img: 对应酉矩阵系数的虚数部分。 + :type coefficients_img: List[float] + :param b: 输入答案被编码成的量子态。 + :type b: State + :param depth: 模拟电路的深度。 + :type depth: int + + .. py:method:: forward() + + :return: 返回模型的输出。 + :rtype: paddle.Tensor + +.. py:function:: compute(A, b, depth, iterations, LR, gamma) + + 求解线性方程组 Ax=b。 + + :param A: 输入矩阵。 + :type A: numpy.ndarray + :param b: 输入向量。 + :type b: numpy.ndarray + :param depth: 模拟电路的深度。 + :type depth: int + :param iterations: 优化的迭代次数。 + :type iterations: int + :param LR: 优化器的学习率。 + :type LR: float + :param gamma: 如果损失函数低于此值,则可以提前结束优化。 默认值为 ``0``。 + :type gamma: Optional[float] \ No newline at end of file diff --git a/docs_zh_CN/source/paddle_quantum.data_analysis.vqr.rst b/docs_zh_CN/source/paddle_quantum.data_analysis.vqr.rst new file mode 100644 index 0000000..b7a1b8c --- /dev/null +++ b/docs_zh_CN/source/paddle_quantum.data_analysis.vqr.rst @@ -0,0 +1,126 @@ +paddle\_quantum.data_analysis.vqr +============================================= + +量子回归分析的相关函数和模拟器类。 + +.. py:function:: load_dataset(data_file, model_name) + + 加载需要分析的数据集 .csv 文件。 + + :param data_file: 数据集文件所在目录。 + :type data_file: str + :param model_name: 目前支持的所有模型类型,包括 ``linear`` 和 ``poly`` 两种模式。 + :type model_name: str + + :return: 返回计算所需的 pandas 解码文件。 + +.. py:function:: IPEstimator(circuit, input_state: State, measure_idx) + + 基于量桨模拟器运行的电路来实现量子态内积的估计器。 + + :param circuit: 运行电路。 + :type circuit: Circuit + :param input_state: 电路输入的量子态。 + :type input_state: State + :param measure_idx: 需要测量的比特序号。默认为 ``[0]``。 + :type measure_idx: List[int] + + :return: 返回计算的内积值(支持梯度分析)。 + :rtype: paddle.Tensor + +.. py:class:: QRegressionModel(data_file, model_name, x_feature, + y_feature, num_variable, init_params, + num_qubits, learning_rate, + iteration, language) + + 基类::py:class: `object` + + 变分量子回归分析器(variational quantum regression, VQR)模型实现。 + + :param data_file: 需要分析的数据集路径。 + :type data_file: str + :param model_name: 所需使用的模型类型。目前只支持 ``linear`` 和 ``poly``。 + :type model_name: str + :param x_feature: 自变量名称。 + :type x_feature: List[str] + :param y_feature: 因变量名称。 + :type y_feature: List[str] + :param num_variable: 需要调用的模型参数量,即所有回归模型中的系数量。 + :type num_variable: int + :param init_params: 调用参数的初始化数值。 + :type init_params: List[float] + :param num_qubits: 所需使用到的量子比特数量。默认值为 ``6``。 + :type num_qubits: int + :param learning_rate: 学习率。默认值为 ``0.1``。 + :type learning_rate: float + :param iteration: 学习迭代次数。默认值为 ``100``。 + :type iteration: int + :param language: 结果显示的语言。默认值为 ``CN``。 + :type iteration: str + + .. py:method:: regression_analyse() + + 对输入的数据进行回归分析。 + + :return: 返回可继续用来预测的模型。 + :rtype: Union[LinearRegression, PolyRegression] + +.. py:class:: LinearRegression(num_qubits, num_x) + + 基类::py:class: `paddle.nn.Layer` + + 量子线性回归分析器。 + + :param num_qubits: 需要的量子比特数。 + :type num_qubits: int + :param num_x: 线性自变量个数。默认为 ``1``。 + :type num_x: int + + .. py:method:: reg_param() + + 输出当前回归分析器中的参数值。 + + :return: 返回当前模型中的参数值。 + :rtype: paddle.Tensor + + .. py:method:: set_params(new_params) + + 设定回归分析器中的参数。 + + :param new_params: 输入的新参数值。 + :type new_params: Union[paddle.Tensor, np.ndarray] + + .. py:method:: fit(X, y, learning_rate, iteration, + saved_dir, print_score, model_name) + + 输入训练集数据用来训练回归模型。 + + :param X: 自变量训练集数据。 + :type X: Union[paddle.Tensor, np.ndarray] + :param y: 因变量训练集数据。 + :type y: Union[paddle.Tensor, np.ndarray] + + .. py:method:: predict(X) + + 根据现有模型预测测试集数据。 + + :param X: 自变量测试集数据。 + :type X: Union[paddle.Tensor, np.ndarray] + + :return: 返回当前模型的预测值。 + :rtype: Union[paddle.Tensor, np.ndarray] + + .. py:method:: score(X, y, metric) + + 计算模型对测试集数据的回归拟合度。 + + :param X: 自变量测试集数据。 + :type X: Union[paddle.Tensor, np.ndarray] + :param y: 自变量测试集数据。 + :type y: Union[paddle.Tensor, np.ndarray] + :param metric: 用于计算的度量类型。默认为 ``R2``。 + :type metric: str + + :return: 返回当前模型的拟合度。 + :rtype: float + diff --git a/docs_zh_CN/source/paddle_quantum.finance.finance.rst b/docs_zh_CN/source/paddle_quantum.finance.finance.rst new file mode 100644 index 0000000..c8d47c6 --- /dev/null +++ b/docs_zh_CN/source/paddle_quantum.finance.finance.rst @@ -0,0 +1,130 @@ +paddle\_quantum.finance.finance +=============================== + +量子金融的相关函数和模拟器类。 + +.. py:class:: DataSimulator(stocks, start=None, end=None) + + 基类: :py:class:`object` + + 用于生成和计算投资组合优化和投资分散化问题要用的数据和相关参数。 + + :param stocks: 表示所有可投资股票的名字。 + :type stocks: list + :param start: 表示随机生成股票数据时交易日的起始日期, 默认为 ``None``。 + :type start: datetime, optional + :param end: 表示随机生成股票数据时交易日的结束日期, 默认为 ``None``。 + :type end: datetime, optional + + .. py:method:: set_data(data) + + 决定实验使用的数据是随机生成的还是用户本地输入。 + + :param data: 用户输入的股票数据。 + :type data: list + + .. py:method:: randomly_generate() + + 根据开始日期和结束日期随机生成用于实验的股票数据。 + + .. Note:: + + 若要随机生成股票数据,需要以 ``datetime`` 包中的格式指定开始日期和结束日期,如 ``start = datetime.datetime(2016, 1, 1)``。 + + .. py:method:: get_asset_return_mean_vector() + + 用于计算所有可投资股票的平均投资回报率。 + + :return: 所有可投资的股票的平均投资回报率。 + :rtype: list + + .. py:method:: get_asset_return_covariance_matrix() + + 用于计算所有可投资股票回报率之间的协方差矩阵。 + + :return: 所有可投资股票回报率之间的协方差矩阵。 + :rtype: list + + .. py:method:: get_similarity_matrix() + + 计算各股票之间的相似矩阵。 + + 通过动态时间规整算法(Dynamic Time Warping, DTW)计算两股票之间的相似性。 + + :return: 各股票间的相似矩阵。 + :rtype: list + +.. py:function:: portfolio_optimization_hamiltonian(penalty, mu, sigma, q, budget) + + 构建投资组合优化问题的哈密顿量。 + + :param penalty: 惩罚参数。 + :type penalty: int + :param mu: 各股票的预期回报率。 + :type mu: list + :param sigma: 各股票回报率间的协方差矩阵。 + :type sigma: list + :param q: 投资股票的风险。 + :type q: float + :param budget: 投资预算, 即要投资的股票数量。 + :type budget: int + + .. math:: + + C(x) = q \sum_i \sum_j S_{ji}x_ix_j - \sum_{i}x_i \mu_i + A \left(B - \sum_i x_i\right)^2 + + .. Hint:: + + 将布尔变量 :math:`x_i` 映射到哈密顿矩阵上,:math:`x_i \mapsto \frac{I-Z_i}{2}`。 + + :return: 投资组合优化问题的哈密顿量。 + :rtype: paddle_quantum.Hamiltonian + +.. py:function:: portfolio_diversification_hamiltonian(penalty, rho, q) + + 构建投资组合分散化问题的哈密顿量。 + + :param penalty: 惩罚参数。 + :type penalty: int + :param rho: 各股票间的相似矩阵。 + :type rho: list + :param q: 股票聚类的类别数。 + :type q: int + + .. math:: + + \begin{aligned} + C_x &= -\sum_{i=1}^{n}\sum_{j=1}^{n}\rho_{ij}x_{ij} + A\left(q- \sum_{j=1}^n y_j \right)^2 + \sum_{i=1}^n A\left(\sum_{j=1}^n 1- x_{ij} \right)^2 \\ + &\quad + \sum_{j=1}^n A\left(x_{jj} - y_j\right)^2 + \sum_{i=1}^n \sum_{j=1}^n A\left(x_{ij}(1 - y_j)\right).\\ + \end{aligned} + + .. Hint:: + + 将布尔变量 :math:`x_{ij}` 映射到哈密顿矩阵上,:math:`x_{ij} \mapsto \frac{I-Z_{ij}}{2}`。 + + :return: 投资组合分散化问题的哈密顿量。 + :rtype: paddle_quantum.Hamiltonian + +.. py:function:: arbitrage_opportunities_hamiltonian(g, penalty, n, k) + + 构建最佳套利机会问题的哈密顿量。 + + :param g: 不同货币市场间转换的图形化表示。 + :type g: networkx.DiGraph + :param penalty: 惩罚参数。 + :type penalty: int + :param n: 货币种类的数量,即图 g 中的顶点数量。 + :type n: int + :param k: 套利回路中包含的顶点数。 + :type k: int + + .. math:: + + C(x) = - P(x) + A\sum_{k=0}^{K-1} \left(1 - \sum_{i=0}^{n-1} x_{i,k}\right)^2 + A\sum_{k=0}^{K-1}\sum_{(i,j)\notin E}x_{i,k}x_{j,k+1} + + .. Hint:: + + 将布尔变量 :math:`x_{i,k}` 映射到哈密顿矩阵上,:math:`x_{i,k} \mapsto \frac{I-Z_{i,k}}{2}`。 + + :return: 最佳套利机会问题的哈密顿量。 + :rtype: paddle_quantum.Hamiltonian diff --git a/docs_zh_CN/source/paddle_quantum.finance.pricing.rst b/docs_zh_CN/source/paddle_quantum.finance.pricing.rst new file mode 100644 index 0000000..b94732f --- /dev/null +++ b/docs_zh_CN/source/paddle_quantum.finance.pricing.rst @@ -0,0 +1,85 @@ +paddle\_quantum.finance.pricing +================================= + +量子蒙特卡洛及期权定价相关工具。 + +.. py:function:: qae_cir(oracle, num_ancilla) + + 根据给定酉算子搭建一条量子振幅估计电路。 + + :param oracle: 给定酉算子。 + :type oracle: paddle.Tensor + :param num_ancilla: 辅助比特使用数。 + :type num_ancilla: int + + :return: 一条用于量子振幅估计的量子电路 + :rtype: Circuit + +.. py:function:: qae_alg(oracle, : int) + + 量子振幅估计算法。 + + :param oracle: 一个 :math:`n`-比特酉算子 :math:`\mathcal{A}`。 + :type oracle: paddle.Tensor + :param num_ancilla: 辅助比特使用数。 + :type num_ancilla: int + + :return: 包含如下元素的 tuple: + - 用于量子振幅估计的量子电路。 + - 振幅估计结果,即 :math:`|\sin(2\pi\theta)|`。 + :rtype: Tuple[Circuit, paddle.Tensor] + + .. note:: + + :math:`\mathcal{A}` 满足 :math:`\mathcal{A}|0^{\otimes n}\rangle=\cos(2\pi\theta)|0\rangle|\psi\rangle+\sin(2\pi\theta)|1\rangle|\phi\rangle.` + +.. py:function:: qmc_alg(fcn, list_prob, num_ancilla=6) + + 量子蒙特卡洛算法。 + + :param fcn: 应用于随机变量 :math:`X` 的实函数 :math:`f`。 + :type fcn: Callable[[float], float] + :param list_prob: 随机变量 :math:`X` 的概率分布,其中第 j 个元素对应第 j 个事件的发生几率。 + :type list_prob: List[float] + :param num_ancilla: 辅助比特使用数。默认为 ``6``。 + :type num_ancilla: int + + :return: 包含如下元素的 tuple: + - 用于量子蒙特卡洛的量子电路。 + - 期望值估计结果,即 :math:`\mathbb{E}[f(X)]`。 + :rtype: Tuple[Circuit, paddle.Tensor] + +.. py:class:: EuroOptionEstimator(initial_price, strike_price, interest_rate, volatility, maturity_date, degree_of_estimation=5) + + 基类: :py:class:`object` + + 欧式期权定价估算器 + + :param initial_price: 初始价格。 + :type initial_price: float + :param strike_price: 成交价。 + :type strike_price: float + :param interest_rate: 无风险利率。 + :type interest_rate: float + :param volatility: 市场波动性。 + :type volatility: float + :param maturity_date: 期权到期日(以年为单位)。 + :type maturity_date: float + :param degree_of_estimation: 估计精度指数。 + :type degree_of_estimation: int + + .. note:: + + 假设欧式期权定价处于 `Black-Scholes-Merton 模型 `_ 中。 + + .. py:method:: estimate() + + 使用量子蒙特卡洛算法估算欧式期权定价。 + + :return: 给定资产的期权定价。 + :rtype: float + + .. py:method:: plot() + + 画出在该方案中使用的量子电路。 + \ No newline at end of file diff --git a/docs_zh_CN/source/paddle_quantum.finance.qpo.rst b/docs_zh_CN/source/paddle_quantum.finance.qpo.rst new file mode 100644 index 0000000..9275fb3 --- /dev/null +++ b/docs_zh_CN/source/paddle_quantum.finance.qpo.rst @@ -0,0 +1,42 @@ +paddle\_quantum.finance.qpo +=============================== + +量子金融优化模型库的封装函数 + +.. py:function:: portfolio_combination_optimization(num_asset, data, iter, lr, risk, budget, penalty, circuit, init_state, optimizer, measure_shots, logger, compare) + + 用于解决金融组合优化问题的高度封装的函数 + + :param num_asset: 可投资项目的数目。 + :type num_asset: int + :param data: 股票数据。 + :type data: Union[pq.finance.DataSimulator, Tuple[paddle.Tensor, paddle.Tensor]] + :param iter: 循环迭代次数。 + :type iter: int + :param lr: 梯度下降学习速率。 + :type lr: Optional[float] = None + :param risk: 投资的风险系数。 + :type risk: float + :param budget: 投资红利。 + :type budget: int + :param penalty: 投资惩罚。 + :type penalty: float + :param circuit: 量子电路的种类,若输入整数则搭建该整数层complex_entangled_layer。 + :type circuit: Union[pq.ansatz.Circuit, int] = 2 + :param init_state: 输入到变分量子电路的初态,默认为零态的直积。 + :type init_state: Optional[pq.state.State] = None + :param optimizer: 优化器类型,默认为 `paddle.optimizer.Adam` + :type optimizer: Optional[paddle.optimizer.Optimizer] = None + :param measure_shots: 对末态做测量的次数,默认为2048。 + :type measure_shots: int + :param logger: 开启日志记录。 + :type logger: Optional[logging.Logger] = None + :param compare: 是否把梯度下降优化得到的损失最小值与真实损失最小值相比。 + :type compare: bool = False + + :return: 列表形式的最优的投资组合 + :rtype: List[int] + + .. note:: + + 此函数只用于解决一个特定问题,见:https://qml.baidu.com/tutorials/combinatorial-optimization/quantum-finance-application-on-portfolio-optimization.html diff --git a/docs_zh_CN/source/paddle_quantum.finance.rst b/docs_zh_CN/source/paddle_quantum.finance.rst index 2fef78c..cb5947b 100644 --- a/docs_zh_CN/source/paddle_quantum.finance.rst +++ b/docs_zh_CN/source/paddle_quantum.finance.rst @@ -1,130 +1,13 @@ paddle\_quantum.finance -======================= +========================= -量子金融的相关函数和模拟器类。 +量子金融模块 -.. py:class:: DataSimulator(stocks, start=None, end=None) +.. rubric:: Submodules - 基类: :py:class:`object` +.. toctree:: + :maxdepth: 4 - 用于生成和计算投资组合优化和投资分散化问题要用的数据和相关参数。 - - :param stocks: 表示所有可投资股票的名字。 - :type stocks: list - :param start: 表示随机生成股票数据时交易日的起始日期, 默认为 ``None``。 - :type start: datetime, optional - :param end: 表示随机生成股票数据时交易日的结束日期, 默认为 ``None``。 - :type end: datetime, optional - - .. py:method:: set_data(data) - - 决定实验使用的数据是随机生成的还是用户本地输入。 - - :param data: 用户输入的股票数据。 - :type data: list - - .. py:method:: randomly_generate() - - 根据开始日期和结束日期随机生成用于实验的股票数据。 - - .. Note:: - - 若要随机生成股票数据,需要以 ``datetime`` 包中的格式指定开始日期和结束日期,如 ``start = datetime.datetime(2016, 1, 1)``。 - - .. py:method:: get_asset_return_mean_vector() - - 用于计算所有可投资股票的平均投资回报率。 - - :return: 所有可投资的股票的平均投资回报率。 - :rtype: list - - .. py:method:: get_asset_return_covariance_matrix() - - 用于计算所有可投资股票回报率之间的协方差矩阵。 - - :return: 所有可投资股票回报率之间的协方差矩阵。 - :rtype: list - - .. py:method:: get_similarity_matrix() - - 计算各股票之间的相似矩阵。 - - 通过动态时间规整算法(Dynamic Time Warping, DTW)计算两股票之间的相似性。 - - :return: 各股票间的相似矩阵。 - :rtype: list - -.. py:function:: portfolio_optimization_hamiltonian(penalty, mu, sigma, q, budget) - - 构建投资组合优化问题的哈密顿量。 - - :param penalty: 惩罚参数。 - :type penalty: int - :param mu: 各股票的预期回报率。 - :type mu: list - :param sigma: 各股票回报率间的协方差矩阵。 - :type sigma: list - :param q: 投资股票的风险。 - :type q: float - :param budget: 投资预算, 即要投资的股票数量。 - :type budget: int - - .. math:: - - C(x) = q \sum_i \sum_j S_{ji}x_ix_j - \sum_{i}x_i \mu_i + A \left(B - \sum_i x_i\right)^2 - - .. Hint:: - - 将布尔变量 :math:`x_i` 映射到哈密顿矩阵上,:math:`x_i \mapsto \frac{I-Z_i}{2}`。 - - :return: 投资组合优化问题的哈密顿量。 - :rtype: paddle_quantum.Hamiltonian - -.. py:function:: portfolio_diversification_hamiltonian(penalty, rho, q) - - 构建投资组合分散化问题的哈密顿量。 - - :param penalty: 惩罚参数。 - :type penalty: int - :param rho: 各股票间的相似矩阵。 - :type rho: list - :param q: 股票聚类的类别数。 - :type q: int - - .. math:: - - \begin{aligned} - C_x &= -\sum_{i=1}^{n}\sum_{j=1}^{n}\rho_{ij}x_{ij} + A\left(q- \sum_{j=1}^n y_j \right)^2 + \sum_{i=1}^n A\left(\sum_{j=1}^n 1- x_{ij} \right)^2 \\ - &\quad + \sum_{j=1}^n A\left(x_{jj} - y_j\right)^2 + \sum_{i=1}^n \sum_{j=1}^n A\left(x_{ij}(1 - y_j)\right).\\ - \end{aligned} - - .. Hint:: - - 将布尔变量 :math:`x_{ij}` 映射到哈密顿矩阵上,:math:`x_{ij} \mapsto \frac{I-Z_{ij}}{2}`。 - - :return: 投资组合分散化问题的哈密顿量。 - :rtype: paddle_quantum.Hamiltonian - -.. py:function:: arbitrage_opportunities_hamiltonian(g, penalty, n, k) - - 构建最佳套利机会问题的哈密顿量。 - - :param g: 不同货币市场间转换的图形化表示。 - :type g: networkx.DiGraph - :param penalty: 惩罚参数。 - :type penalty: int - :param n: 货币种类的数量,即图 g 中的顶点数量。 - :type n: int - :param k: 套利回路中包含的顶点数。 - :type k: int - - .. math:: - - C(x) = - P(x) + A\sum_{k=0}^{K-1} \left(1 - \sum_{i=0}^{n-1} x_{i,k}\right)^2 + A\sum_{k=0}^{K-1}\sum_{(i,j)\notin E}x_{i,k}x_{j,k+1} - - .. Hint:: - - 将布尔变量 :math:`x_{i,k}` 映射到哈密顿矩阵上,:math:`x_{i,k} \mapsto \frac{I-Z_{i,k}}{2}`。 - - :return: 最佳套利机会问题的哈密顿量。 - :rtype: paddle_quantum.Hamiltonian + paddle_quantum.finance.finance + paddle_quantum.finance.pricing + paddle_quantum.finance.qpo diff --git a/docs_zh_CN/source/paddle_quantum.linalg.rst b/docs_zh_CN/source/paddle_quantum.linalg.rst index e81e31a..c961c2a 100644 --- a/docs_zh_CN/source/paddle_quantum.linalg.rst +++ b/docs_zh_CN/source/paddle_quantum.linalg.rst @@ -35,6 +35,18 @@ paddle\_quantum.linalg :return: 决定是否 :math:`P - P^\dagger = 0` :rtype: bool +.. py:function:: is_positive(mat, eps=1e-6) + + 验证矩阵 ``P`` 是否为半正定矩阵 + + :param mat: 半正定矩阵 + :type mat: Union[np.ndarray, paddle.Tensor] + :param eps: 容错率 + :type eps: float, optional + + :return: 决定是否 :math:`P` 为厄密矩阵且本征值均为非负实数 + :rtype: bool + .. py:function:: is_projector(mat, eps=1e-6) 验证矩阵 ``P`` 是否为映射算子 @@ -229,4 +241,40 @@ paddle\_quantum.linalg :type ignore_zero: bool, optional :return: :math:`f(H)` - :rtype: paddle.Tensor \ No newline at end of file + :rtype: paddle.Tensor + +.. py:function:: pauli_basis_generation(num_qubits) + + 生成一组泡利基 + + :param num_qubits: 量子比特数 :math:`n` + :type num_qubits: int + + :return: 空间 :math:`\mathbb{C}^{2^n \times 2^n}` 上的泡利基 + :rtype: List[paddle.Tensor] + +.. py:function:: pauli_decomposition(mat) + + 目标矩阵在泡利基下的分解 + + :param mat: 目标矩阵 + :type mat: Union[np.ndarray, paddle.Tensor] + + :return: 泡利基的系数列表 + :rtype: Union[np.ndarray, paddle.Tensor] + +.. py:function:: subsystem_decomposition(mat, first_basis, second_basis, inner_prod) + + 目标矩阵在两个子系统中给定两个基上的分解 + + :param mat: 目标矩阵 :math:`w` + :type mat: Union[np.ndarray, paddle.Tensor] + :param first_basis: 第一个空间上的基 :math:`\{e_i\}_i` + :type first_basis: Union[List[np.ndarray], List[paddle.Tensor]] + :param second_basis: 第二个空间上的基 :math:`\{f_j\}_j` + :type second_basis: Union[List[np.ndarray], List[paddle.Tensor]] + :param inner_prod: 两个空间上的内积 + :type inner_prod: Union[Callable[[np.ndarray, np.ndarray], np.ndarray], Callable[[paddle.Tensor, paddle.Tensor], paddle.Tensor]] + + :return: 系数矩阵 :math:`[\beta_{ij}]` 满足 :math:`w = \sum_{i, j} \beta_{ij} e_i \otimes f_j` + :rtype: Union[np.ndarray, paddle.Tensor] \ No newline at end of file diff --git a/docs_zh_CN/source/paddle_quantum.model.rst b/docs_zh_CN/source/paddle_quantum.model.rst new file mode 100644 index 0000000..9472249 --- /dev/null +++ b/docs_zh_CN/source/paddle_quantum.model.rst @@ -0,0 +1,395 @@ +paddle\_quantum.model +================================== + +量子神经网络的通用模型模板 + +.. py:function:: reset_settings() + + 重置 ``rcParams`` 为默认设定。 + +.. py:function:: random_batch(data, label, batch_size) + + 从数据集和标签集中随机返回一个数据批次。 + + :param data: 数据集。 + :type data: Iterable + :param label: 标签集。 + :type label: Iterable + :param batch_size: 数据批次的大小。 + :type batch_size: int + + :return: 一批随机的数据。 + :rtype: Tuple[list, list] + +.. py:class:: NullScheduler() + + 基类::py:class:`paddle.optimizer.lr.LRScheduler` + + 用于不希望使用学习率策略的用户,可以被以下代码激活 + + .. code-block:: python + + from paddle_quantum.model import rcParams + rcParams['scheduler'] = None + +.. py:class:: Model(network, name="Model") + + 基类::py:class:`object` + + 量子神经网络模型的通用模板。 + + :param network: 一个量子神经网络。 + :type network: paddle.nn.Layer + :param name: 模型的名字。默认为 ``"Model"``。 + :type name: str + + .. py:method:: parameters() + + 返回神经网络的参数。 + + :return: 神经网络的参数。 + :rtype: List[paddle.fluid.framework.ParamBase] + + .. py:method:: prepare(loss_fcn, metric_fcn=None, metric_name=None) + + 量子神经网络的常规功能设置。 + + :param loss_fcn: 量子神经网络的损失函数。 + :type loss_fcn: Callable[[Union[State, Circuit, Sequential], Any], Any] + :param metric_fcn: 量子神经网络的度量函数,不会干扰训练过程。默认为 ``None``。 + :type metric_fcn: Callable[[Union[Circuit, Sequential]], float] + :param metric_name: 度量函数的名字。默认为 ``None``。 + :type metric_name: str + :raises ValueError: 度量函数的输出必须为 float。 + + .. note:: + + 该函数同时会引入 ``rcParams`` 里的参数。 + + .. py:method:: check_prepared() + + 检测模型是否准备好训练。 + + .. py:method:: train(loss_generator) + + 量子神经网络单批次训练的通用模板 + + :param loss_generator: 计算量子神经网络,以 ``Model.network`` 为输入的损失函数。默认为 ``None`` ,即使用在 ``Model.prepare`` 里定义的损失函数。 + :type loss_generator: Callable[[Any], Any] + + :return: 包含以下元素: + + - 一组损失数值。 + - 若给定了度量函数,同时返回一组度量值。 + + :rtype: Union[List[float], Tuple[List[float], List[float]]] + + .. py:method:: evaluate(loss_generator) + + 量子神经网络评估的通用模板 + + :param loss_generator: 计算量子神经网络,以 ``Model.network`` 为输入的损失函数。默认为 ``None`` ,即使用在 ``Model.prepare`` 里定义的损失函数。 + + :return: + 包含以下元素: + + - 损失数值; + - 若给定了度量函数,同时返回度量值。 + + :rtype: Union[float, Tuple[float, float]] + + .. py:method:: fit(train_data, train_label, test_data, test_label) + + 量子神经网络训练的通用模板 + + :param train_data: 训练集的数据。 + :type train_data: Iterable + :param train_label: 训练集的标签。 + :type train_label: Iterable + :param test_data: 测试集的数据。 + :type test_data: Iterable + :param test_label: 测试集的标签。 + :type test_label: Iterable + + .. py:method:: plot(include_metric, apply_log, has_epoch) + + 画图展示训练数据 + + :param include_metric: 是否包含度量值。 + :type include_metric: bool + :param apply_log: 是否对数据施加 log。 + :type apply_log: bool + :param has_epoch: 是否数据分批次训练 + :type has_epoch: bool + +.. py:class:: OptModel(circuit, name="OptModel") + + 基类::py:class:`paddle_quantum.model.Model` + + 用于实现优化类量子神经网络的类。 + + :param circuit: 被优化的 Circuit 的实例。 + :type data: Circuit + :param name: 模型的名字。默认为 ``"OptModel"``。 + :type name: str + + .. py:method:: prepare(loss_fcn, metric_fcn=None, metric_name=None, *loss_args) + + 准备及检查优化类量子神经网络的功能设置。 + + :param loss_fcn: 量子神经网络的损失函数。 + :type loss_fcn: Callable[[Circuit, Any], paddle.Tensor] + :param metric_fcn: 量子神经网络的度量函数,不会干扰训练过程。默认为 ``None``。 + :type metric_fcn: Callable[[Union[Circuit, Sequential]], float] + :param metric_name: 度量函数的名字。默认为 ``None``。 + :type metric_name: str + :param loss_args: loss_fcn 除了量子神经网络输入以外的的参数。 + :type loss_args: any + :raises ValueError: 损失函数的输出必须为 paddle.Tensor。 + + .. note:: + + 该函数同时会引入 ``rcParams`` 里的参数。 + + .. py:method:: optimize() + + 根据损失函数优化电路。 + + + :return: + 包含以下元素: + + - 一组损失数值; + - 若给定了度量函数,同时返回一组度量值。 + + :rtype: Union[List[float], Tuple[List[float], List[float]]] + + .. py:method:: evaluate() + + 计算当前量子神经网络的损失值和度量值。 + + :return: + 包含以下元素: + + - 损失数值; + - 若给定了度量函数,同时返回度量值。 + + :rtype: Union[float, Tuple[float, float]] + + .. py:method:: fit() + + :raises NotImplementedError: 优化模型不支持 fit 功能:请直接使用 OptModel.optimize。 + + .. py:method:: plot(include_metric=True, apply_log=False) + + 画图展示训练数据 + + :param include_metric: 是否包含度量值。 默认为 ``True``。 + :type include_metric: bool + :param apply_log: 是否对数据施加 log。 默认为 ``False``。 + :type apply_log: bool + +.. py:class:: LearningModel(circuit, name="LearningModel") + + 基类::py:class:`paddle_quantum.model.Model` + + 用于实现学习类量子神经网络的类。 + + :param circuit: 被优化的 Circuit 的实例。 + :type data: Circuit + :param name: 模型的名字。默认为 ``"LearningModel"``。 + :type name: str + + .. py:method:: prepare(loss_fcn, metric_fcn=None, metric_name=None, *loss_args) + + 准备及检查学习类量子神经网络的功能设置。 + + :param loss_fcn: 量子神经网络输出的损失函数。 + :type loss_fcn: Callable[[State, Any, Any], Any] + :param metric_fcn: 量子神经网络的度量函数,不会干扰训练过程。默认为 ``None``。 + :type metric_fcn: Callable[[Union[Circuit, Sequential]], float] + :param metric_name: 度量函数的名字。默认为 ``None``。 + :type metric_name: str + :param loss_args: loss_fcn 除了量子神经网络输入和标签以外的的参数。 + :type loss_args: any + + .. note:: + + - 此类模型的数据输入必须为 ``paddle_quantum.State``。 + 若数据需要编码到量子电路中,请使用 ``paddle_quantum.model.EncodingModel``。 + - 该函数同时会引入 ``rcParams`` 里的参数。 + + .. py:method:: train_batch(data, label) + + 用单批次数据训练电路。 + + :param data: 一组输入量子态 + :type param: List[State] + :param data: 预期标签 + :type param: List[Any] + + :return: + 包含以下元素: + + - 一组损失数值; + - 若给定了度量函数,同时返回一组度量值。 + + :rtype: Union[List[float], Tuple[List[float], List[float]]] + + .. py:method:: eval_batch(data, label) + + 用单批次数据评估电路。 + + :param data: 一组输入量子态 + :type data: List[State] + :param label: 预期标签 + :type label: List[Any] + + :return: + 包含以下元素: + + - 损失数值; + - 若给定了度量函数,同时返回度量值。 + + :rtype: Union[float, Tuple[float, float]] + + .. py:method:: fit(train_data, train_label, test_data, test_label) + + 使用输入数据训练电路。 + + :param train_data: 训练集的数据。 + :type train_data: List[State] + :param train_label: 训练集的标签。 + :type train_label: Iterable + :param test_data: 测试集的数据。 + :type test_data: List[State] + :param test_label: 测试集的标签。 + :type test_label: Iterable + + .. py:method:: plot(include_metric=True, apply_log=False) + + 画图展示训练数据 + + :param include_metric: 是否包含度量值。 默认为 ``True``。 + :type include_metric: bool + :param apply_log: 是否对数据施加 log。 默认为 ``False``。 + :type apply_log: bool + +.. py:class:: EncodingNetwork(encoding_func, param_shape, initial_state=None) + + 基类::py:class:`paddle.nn.Layer` + + 编码模型的量子神经网络。 + + :param encoding_func: 决定如何构建量子电路的编码函数。 + :type encoding_func: Callable[[Any, paddle.Tensor], Circuit] + :param param_shape: 输入参数的 shape。 + :type param_shape: Iterable[int] + :param initial_state: 电路的初始态。 + :type initial_state: State + + .. note:: + + 仅用于 ``paddle_quantum.model.EncodingModel``。 + + .. py:method:: forward(input_data) + + 计算输入对应的输出。 + + :param input_data: 用于编码电路的输入数据。 + :type input_data: List[Any] + :return: 电路的输出态。 + :rtype: List[State] + +.. py:class:: EncodingModel(encoding_fcn, param_shape, initial_state=None, name="EncodingModel") + + 基类::py:class:`Model` + + 用于实现编码类量子神经网络的类。 + + :param encoding_fcn: 编码函数,用编码数据和参数决定如何构建量子电路。 + :type encoding_fcn: Callable[[Any, paddle.Tensor], Circuit] + :param param_shape: encoding_fcn 参数的 shape。 + :type param_shape: Iterable[int] + :param initial_state: 电路的初始态。默认为 ``None``,即零态。 + :type initial_state: State + :param name: 模型的名字。默认为 ``"EncodingModel"``。 + :type name: str + + .. note:: + + 与 ``paddle_quantum.model.LearningModel`` 不同的是,该模型的数据需要编码至量子电路而不是量子态。 + 因此该模型需要知道输入数据是如何编入至量子电路的。该模型会根据 ``param_shape`` 自动生成所需的训练参数。 + + .. py:method:: prepare(loss_fcn, metric_fcn=None, metric_name=None, *loss_args) + + 准备及检查编码类量子神经网络的功能设置。 + + :param loss_fcn: 量子神经网络输出的损失函数。 + :type loss_fcn: Callable[[State, Any, Any], Any] + :param metric_fcn: 量子神经网络的度量函数,不会干扰训练过程。默认为 ``None``。 + :type metric_fcn: Callable[[Union[Circuit, Sequential]], float] + :param metric_name: 度量函数的名字。默认为 ``None``。 + :type metric_name: str + :param loss_args: loss_fcn 除了量子神经网络输入和标签以外的的参数。 + :type loss_args: any + + .. note:: + + 该函数同时会引入 ``rcParams`` 里的参数。 + + .. py:method:: train_batch(data, label) + + 用单批次数据训练电路。 + + :param data: 一组数据 + :type param: Iterable + :param data: 预期标签 + :type param: Iterable + + :return: + 包含以下元素: + + - 一组损失数值; + - 若给定了度量函数,同时返回一组度量值。 + + :rtype: Union[List[float], Tuple[List[float], List[float]]] + + .. py:method:: eval_batch(data, label) + + 用单批次数据评估电路。 + + :param data: 一组数据 + :type data: Iterable + :param label: 预期标签 + :type label: Iterable + + :return: + 包含以下元素: + + - 损失数值; + - 若给定了度量函数,同时返回度量值。 + + :rtype: Union[float, Tuple[float, float]] + + .. py:method:: fit(train_data, train_label, test_data, test_label) + + 使用输入数据训练电路。 + + :param train_data: 训练集的数据。 + :type train_data: Iterable + :param train_label: 训练集的标签。 + :type train_label: Iterable + :param test_data: 测试集的数据。 + :type test_data: Iterable + :param test_label: 测试集的标签。 + :type test_label: Iterable + + .. py:method:: plot(include_metric=True, apply_log=False) + + 画图展示训练数据 + + :param include_metric: 是否包含度量值。 默认为 ``True``。 + :type include_metric: bool + :param apply_log: 是否对数据施加 log。 默认为 ``False``。 + :type apply_log: bool diff --git a/docs_zh_CN/source/paddle_quantum.qchem.algorithm.rst b/docs_zh_CN/source/paddle_quantum.qchem.algorithm.rst new file mode 100644 index 0000000..04ac75c --- /dev/null +++ b/docs_zh_CN/source/paddle_quantum.qchem.algorithm.rst @@ -0,0 +1,3 @@ +paddle\_quantum.qchem.algorithm +========================================= + diff --git a/docs_zh_CN/source/paddle_quantum.qchem.ansatz.rst b/docs_zh_CN/source/paddle_quantum.qchem.ansatz.rst new file mode 100644 index 0000000..869cd45 --- /dev/null +++ b/docs_zh_CN/source/paddle_quantum.qchem.ansatz.rst @@ -0,0 +1,3 @@ +paddle\_quantum.qchem.ansatz +================================= + diff --git a/docs_zh_CN/source/paddle_quantum.qchem.drivers.rst b/docs_zh_CN/source/paddle_quantum.qchem.drivers.rst new file mode 100644 index 0000000..0c13fd8 --- /dev/null +++ b/docs_zh_CN/source/paddle_quantum.qchem.drivers.rst @@ -0,0 +1,3 @@ +paddle\_quantum.qchem.drivers +================================== + diff --git a/docs_zh_CN/source/paddle_quantum.qchem.fermionic_state.rst b/docs_zh_CN/source/paddle_quantum.qchem.fermionic_state.rst new file mode 100644 index 0000000..a1be0b2 --- /dev/null +++ b/docs_zh_CN/source/paddle_quantum.qchem.fermionic_state.rst @@ -0,0 +1,3 @@ +paddle\_quantum.qchem.fermionic\_state +========================================= + diff --git a/docs_zh_CN/source/paddle_quantum.qchem.molecule.rst b/docs_zh_CN/source/paddle_quantum.qchem.molecule.rst new file mode 100644 index 0000000..aec92b9 --- /dev/null +++ b/docs_zh_CN/source/paddle_quantum.qchem.molecule.rst @@ -0,0 +1,3 @@ +paddle\_quantum.qchem.molecule +========================================= + diff --git a/docs_zh_CN/source/paddle_quantum.qchem.properties.rst b/docs_zh_CN/source/paddle_quantum.qchem.properties.rst new file mode 100644 index 0000000..c7c4c7c --- /dev/null +++ b/docs_zh_CN/source/paddle_quantum.qchem.properties.rst @@ -0,0 +1,3 @@ +paddle\_quantum.qchem.properties +========================================= + diff --git a/docs_zh_CN/source/paddle_quantum.qchem.qchem.rst b/docs_zh_CN/source/paddle_quantum.qchem.qchem.rst index e02d7b8..d160856 100644 --- a/docs_zh_CN/source/paddle_quantum.qchem.qchem.rst +++ b/docs_zh_CN/source/paddle_quantum.qchem.qchem.rst @@ -4,6 +4,7 @@ paddle\_quantum.qchem.qchem 量子化学中的功能函数。 .. py:function:: qubitOperator_to_Hamiltonian(spin_h,tol) + 将openfermion形式转化为量桨的哈密顿量形式。 :param spin_h: openfermion形式的哈密顿量。 @@ -38,7 +39,7 @@ paddle\_quantum.qchem.qchem :type charge: int, optional :param multiplicity: 分子的多重度, 默认值为 ``1``。 :type multiplicity: int, optional - :param basis: 常用的基组是 ``sto-3g、6-31g``等, 默认的基组是 ``sto-3g``,更多的基组选择可以参考网站 + :param basis: 常用的基组是 ``sto-3g、6-31g`` 等, 默认的基组是 ``sto-3g``,更多的基组选择可以参考网站 https://psicode.org/psi4manual/master/basissets_byelement.html#apdx-basiselement。 :type basis: str, optional :param method: 用于计算基态能量的方法, 包括 ``scf`` 和 ``fci``,默认的方法为 ``scf``。 diff --git a/docs_zh_CN/source/paddle_quantum.qchem.rst b/docs_zh_CN/source/paddle_quantum.qchem.rst index 4422905..b8a7dd4 100644 --- a/docs_zh_CN/source/paddle_quantum.qchem.rst +++ b/docs_zh_CN/source/paddle_quantum.qchem.rst @@ -8,9 +8,10 @@ paddle\_quantum.qchem .. toctree:: :maxdepth: 4 - paddle_quantum.qchem.density_matrix - paddle_quantum.qchem.hardware_efficient - paddle_quantum.qchem.loss - paddle_quantum.qchem.qchem - paddle_quantum.qchem.slater_determinant - paddle_quantum.qchem.uccsd + paddle_quantum.qchem.algorithm + paddle_quantum.qchem.ansatz + paddle_quantum.qchem.drivers + paddle_quantum.qchem.fermionic_state + paddle_quantum.qchem.molecule + paddle_quantum.qchem.properties + paddle_quantum.qchem.utils diff --git a/docs_zh_CN/source/paddle_quantum.qchem.utils.rst b/docs_zh_CN/source/paddle_quantum.qchem.utils.rst new file mode 100644 index 0000000..13cdf47 --- /dev/null +++ b/docs_zh_CN/source/paddle_quantum.qchem.utils.rst @@ -0,0 +1,4 @@ +paddle\_quantum.qchem.utils +========================================= + + diff --git a/docs_zh_CN/source/paddle_quantum.qinfo.rst b/docs_zh_CN/source/paddle_quantum.qinfo.rst index e5c8715..89eb842 100644 --- a/docs_zh_CN/source/paddle_quantum.qinfo.rst +++ b/docs_zh_CN/source/paddle_quantum.qinfo.rst @@ -72,12 +72,12 @@ paddle\_quantum.qinfo :math:`U` 是一个 :math:`2^n\times 2^n` 的 Unitary 矩阵。 - :param U: 量子门 :math:`U` 的酉矩阵形式 + :param U: 量子门 :math:`U` 的酉矩阵形式。 :type U: Union[np.ndarray, paddle.Tensor] - :param V: 量子门 :math:`V` 的酉矩阵形式 + :param V: 量子门 :math:`V` 的酉矩阵形式。 :type V: Union[np.ndarray, paddle.Tensor] - :return: 输入的量子门之间的保真度 + :return: 输入的量子门之间的保真度。 :rtype: Union[np.ndarray, paddle.Tensor] .. py:function:: purity(rho) @@ -118,14 +118,14 @@ paddle\_quantum.qinfo S(\rho \| \sigma)=\text{tr} \rho(\log \rho-\log \sigma) - :param rho: 量子态的密度矩阵形式 + :param rho: 量子态的密度矩阵形式。 :type rho: Union[np.ndarray, paddle.Tensor, State] - :param sig: 量子态的密度矩阵形式 + :param sig: 量子态的密度矩阵形式。 :type sig: Union[np.ndarray, paddle.Tensor, State] - :param base: 对数的底。默认为2. + :param base: 对数的底,默认为2。 :type base: int, optional - :return: 输入的量子态之间的相对熵 + :return: 输入的量子态之间的相对熵。 :rtype: Union[np.ndarray, paddle.Tensor] .. py:function:: random_pauli_str_generator(n, terms=3) @@ -186,6 +186,20 @@ paddle\_quantum.qinfo :return: 输入的量子态的 partial transpose。 :rtype: Union[np.ndarray, paddle.Tensor] +.. py:function:: partial_transpose(mat, perm_list, dim_list) + + 根据输入顺序组合量子系统。 + + :param mat: 输入矩阵,通常为量子态。 + :type mat: Union[np.ndarray, paddle.Tensor, State] + :param perm: 排列顺序,例如输入 ``[0,2,1,3]`` 将会交换第 2、3 个子系统的顺序。 + :type perm: List[int] + :param dim: 每个子系统维度列表。 + :type dim: List[int] + + :return: 排序后的矩阵。 + :rtype: Union[np.ndarray, paddle.Tensor, State] + .. py:function:: negativity(density_op) 计算输入量子态的 Negativity :math:`N = ||\frac{\rho^{T_A}-1}{2}||`。 @@ -216,6 +230,19 @@ paddle\_quantum.qinfo :return: 输入的量子态是否满足 PPT 条件。 :rtype: bool +.. py:function:: is_choi(op) + + 判断输入算子是否为某个量子操作的 Choi 算子。 + + :param op: 线性算子的矩阵形式。 + :type op: Union[np.ndarray, paddle.Tensor] + + :return: 输入算子是否为某个量子操作的 Choi 算子。 + :rtype: bool + + .. note:: + 输入算子默认作用在第二个系统上。 + .. py:function:: schmidt_decompose(psi, sys_A=None) 计算输入量子态的施密特分解 :math:`\lvert\psi\rangle=\sum_ic_i\lvert i_A\rangle\otimes\lvert i_B \rangle`。 @@ -257,20 +284,20 @@ paddle\_quantum.qinfo :param method: 使用 shadow 来进行估计的方法,可选 "CS"、"LBCS"、"APS" 三种方法,默认为 ``CS``。 :type method: str, optional - :raises ValueError: 输入的哈密顿量 (Hamiltonian) 形式不合法 + :raises ValueError: 输入的哈密顿量 (Hamiltonian) 形式不合法。 :return: 估计可观测量 :math:`H` 的期望值。 :rtype: float .. py:function:: tensor_state(state_a, state_b, *args) - 计算输入的量子态(至少两个)的直积形式, 输出将自动返回 State 实例 + 计算输入的量子态(至少两个)的直积形式, 输出将自动返回 State 实例。 - :param state_a: 量子态A + :param state_a: 量子态 A。 :type state_a: State - :param state_b: 量子态B + :param state_b: 量子态 B。 :type state_b: State - :param args: 其他量子态 + :param args: 其他量子态。 :type args: State .. note:: @@ -278,7 +305,7 @@ paddle\_quantum.qinfo 需要注意输入态使用的 backend; 若输入数据为 ``paddle.Tensor`` 或者 ``numpy.ndarray``,请使用 ``paddle_quantum.linalg.NKron`` 函数处理。 - :return: 输入量子态的直积 + :return: 输入量子态的直积。 :rtype: State .. py:function:: diamond_norm(channel_repr, dim_io, **kwargs) @@ -295,41 +322,91 @@ paddle\_quantum.qinfo :raises RuntimeError: ``channel_repr`` 必须是 ``ChoiRepr`` 或 ``KrausRepr`` 或 ``StinespringRepr`` 或 ``paddle.Tensor``。 :raises TypeError: "dim_io" 必须是 "int" 或者 "tuple"。 - :warning: 输入的 ``channel_repr`` 不是choi表示, 已被转换成 ``ChoiRepr``。 + :warning: 输入的 ``channel_repr`` 不是choi表示,已被转换成 ``ChoiRepr``。 :return: 返回菱形范数 :rtype: float -.. py:function:: channel_convert(original_channel, target, tol) - 将给定的信道转换成目标形式 +.. py:function:: channel_repr_convert(representation, source, target, tol) - :param original_channel: 输入信道 - :type original_channel: Union[ChoiRepr, KrausRepr, StinespringRepr] - :param target: 目标形式,应为 ``Choi``, ``Kraus`` 或 ``Stinespring`` + 将给定的信道转换成目标形式。 + + :param representation: 输入信道的一种表示。 + :type representation: Union[paddle.Tensor, np.ndarray, List[paddle.Tensor], List[np.ndarray]] + :param source: 输入信道的表示名称,应为 ``Choi``, ``Kraus`` 或 ``Stinespring``。 + :type source: str + :param target: 可选 ``Choi``, ``Kraus`` 或 ``Stinespring``。 :type target: str - :param tol: 容错误差 + :param tol: 容错误差。 :type tol: float, optional - :raises ValueError: 不支持的信道表示形式,应为 ``Choi``, ``Kraus`` 或 ``Stinespring``. + :raises ValueError: 不支持的信道表示形式,应为 ``Choi``,``Kraus`` 或 ``Stinespring``。 .. note:: - choi变为kraus目前因为eigh的精度会存在1e-6的误差 + Choi 变为 Kraus 目前因为 eigh 的精度会存在1e-6的误差。 + + :raises NotImplementedError: 不支持输入数据类型的信道转换。 + + :return: 返回目标形式的信道。 + :rtype: Union[paddle.Tensor, np.ndarray, List[paddle.Tensor], List[np.ndarray]] + +.. py:function:: random_channel(num_qubits, rank, target) - :raises NotImplementedError: 不支持输入数据类型的信道转换 + 从 Stinespring 表示中随机生成一个量子信道。 - :return: 返回目标形式的信道 - :rtype: Union[ChoiRepr, KrausRepr, StinespringRepr] + :param num_qubits: 量子比特数 :math:`n`。 + :type num_qubits: int + :param rank: 信道的秩,默认从 :math:`[0, 2^n]` 中随机选择。 + :type rank: str + :param target: 信道的表示,可选 ``Choi``,``Kraus`` 或 ``Stinespring``。 + :type target: str + + :return: 返回目标表示下的随机信道。 + :rtype: Union[paddle.Tensor, List[paddle.Tensor]] -.. py:function:: kraus_oper_random(num_qubits: int, num_oper: int) +.. py:function:: kraus_unitary_random(num_qubits, num_oper) - 随机输出一组描述量子信道的Kraus算符 + 随机输出一组描述量子信道的 Kraus 算符。 - :param num_qubits: 信道对应的量子比特数量 + :param num_qubits: 信道对应的量子比特数量。 :type num_qubits: int - :param num_oper: Kraus算符的数量 + :param num_oper: Kraus算符的数量。 :type num_oper: int - :return: 一组Kraus算符 - :rtype: list \ No newline at end of file + :return: 一组 Kraus 算符。 + :rtype: list + +.. py:function:: grover_generation(oracle) + + Grover 算子生成函数。 + + :param oracle: 给定酉算子。 + :type oracle: Union[np.ndarray, paddle.Tensor] + + :return: 根据 ``oracle`` 搭建的 Grover 算子。 + :rtype: Union[np.ndarray, paddle.Tensor] + +.. py:function:: qft_generation(num_qubits) + + 量子傅里叶变换算子生成函数。其矩阵形式为 + + .. math:: + + \begin{align} + QFT = \frac{1}{\sqrt{N}} + \begin{bmatrix} + 1 & 1 & .. & 1 \\ + 1 & \omega_N & .. & \omega_N^{N-1} \\ + .. & .. & .. & .. \\ + 1 & \omega_N^{N-1} & .. & \omega_N^{(N-1)^2} + \end{bmatrix} + \end{align} + + :param num_qubits: 算子作用的系统比特数。 + :type num_qubits: int + + :return: 量子傅里叶变换算子。 + :rtype: paddle.Tensor + diff --git a/docs_zh_CN/source/paddle_quantum.qml.qnnmic.rst b/docs_zh_CN/source/paddle_quantum.qml.qnnmic.rst new file mode 100644 index 0000000..da7e084 --- /dev/null +++ b/docs_zh_CN/source/paddle_quantum.qml.qnnmic.rst @@ -0,0 +1,73 @@ +paddle\_quantum.qml.qnnmic +============================================== +QNNMIC 模型。 + + +.. py:class:: QNNMIC(num_qubits, num_depths, observables) + + 基类::py:class:`paddle.nn.Layer` + + 基于量子神经网络进行医学图片分类。 + + :param num_qubits: 每层量子电路的比特数。 + :type num_qubits: List[int] + :param num_depths: 每层参数化部分的电路深度。 + :type num_depths: List[int] + :param observables: 每层电路的测量算子。 + :type observables: List + + .. py:method:: forward(batch_input) + + :param batch_input: 模型的输入,其形状为 :math:`(\text{batch_size}, -1)` 。 + :type batch_input: List[paddle.Tensor] + + :return: 返回模型的输出,其形状为 :math:`(\text{batch_size}, \text{num_classes})` 。 + :rtype: paddle.Tensor + +.. py:function:: train(model_name, num_qubits, num_depths, observables, batch_size: int=20, num_epochs: int=4, learning_rate: float=0.1, dataset: str='SurfaceCrack', saved_dir: str='./', using_validation: bool=False, num_train: int=-1, num_val: int=-1, num_test: int=-1) + + 训练 QNNMIC 模型。 + + :param model_name: 模型的名字,用于保存模型。 + :type model_name: str + :param num_qubits: 每层量子电路的比特数。 + :type num_qubits: List[int] + :param num_depths: 每层参数化部分的电路深度。 + :type num_depths: List[int] + :param observables: 每层电路的测量算子。 + :type observables: List + :param batch_size: 数据的批大小,默认为 ``20`` 。 + :type batch_size: Optional[int] + :param num_epochs: 训练的轮数,默认为 ``4`` 。 + :type epoch: Optional[int] + :param learning_rate: 更新参数的学习率,默认为 ``0.1`` 。 + :type learning_rate: Optional[float] + :param dataset: 需要使用的数据集,默认为 ``SurfaceCrack``。 + :type dataset: str + :param saved_dir: 日志文件保存的路径,默认为 ``./``。 + :type saved_dir: str + :param using_validation: 是否使用验证集,默认为 ``False``。 + :type using_validation: bool + :param num_train: 训练集的数据量。默认为 ``-1`` ,即使用所有的训练数据。 + :type num_train: Optional[int] + :param num_val: 验证集的数据量。默认为 ``-1`` ,即使用所有的训练数据。 + :type num_val: Optional[int] + :param num_test: 测试集的数据量。默认为 ``-1`` ,即使用所有的训练数据。 + :type num_test: Optional[int] + +.. py:function:: inference(image_path: str, num_samples: int, model_path: str, num_qubits: List, num_depths: List, observables: List) + + 使用 QNNMIC 模型进行推理。 + + :param image_path: 需要推理的数据集。 + :type image_path: str + :param num_samples: 需要推理数据集中图片的数量。 + :type num_samples: Optional[int] + :param model_path: 推理使用的模型。 + :type model_path: str + :param num_qubits: 每层量子电路的比特数。 + :type num_qubits: List[int] + :param num_depths: 每层参数化部分的电路深度。 + :type num_depths: List[int] + :param observables: 每层电路的测量算子。 + :type observables: List \ No newline at end of file diff --git a/docs_zh_CN/source/paddle_quantum.qml.qnnqd.rst b/docs_zh_CN/source/paddle_quantum.qml.qnnqd.rst new file mode 100644 index 0000000..5a99c11 --- /dev/null +++ b/docs_zh_CN/source/paddle_quantum.qml.qnnqd.rst @@ -0,0 +1,73 @@ +paddle\_quantum.qml.qnnqd +============================================== +QNNQD 模型。 + + +.. py:class:: QNNQD(num_qubits, num_depths, observables) + + 基类::py:class:`paddle.nn.Layer` + + 基于量子神经网络进行质量检测。 + + :param num_qubits: 每层量子电路的比特数。 + :type num_qubits: List[int] + :param num_depths: 每层参数化部分的电路深度。 + :type num_depths: List[int] + :param observables: 每层电路的测量算子。 + :type observables: List + + .. py:method:: forward(batch_input) + + :param batch_input: 模型的输入,其形状为 :math:`(\text{batch_size}, -1)` 。 + :type batch_input: List[paddle.Tensor] + + :return: 返回模型的输出,其形状为 :math:`(\text{batch_size}, \text{num_classes})` 。 + :rtype: paddle.Tensor + +.. py:function:: train(model_name, num_qubits, num_depths, observables, batch_size: int=20, num_epochs: int=4, learning_rate: float=0.1, dataset: str='SurfaceCrack', saved_dir: str='./', using_validation: bool=False, num_train: int=-1, num_val: int=-1, num_test: int=-1) + + 训练 QNNQD 模型。 + + :param model_name: 模型的名字,用于保存模型。 + :type model_name: str + :param num_qubits: 每层量子电路的比特数。 + :type num_qubits: List[int] + :param num_depths: 每层参数化部分的电路深度。 + :type num_depths: List[int] + :param observables: 每层电路的测量算子。 + :type observables: List + :param batch_size: 数据的批大小,默认为 ``20`` 。 + :type batch_size: Optional[int] + :param num_epochs: 训练的轮数,默认为 ``4`` 。 + :type epoch: Optional[int] + :param learning_rate: 更新参数的学习率,默认为 ``0.1`` 。 + :type learning_rate: Optional[float] + :param dataset: 需要使用的数据集,默认为 ``SurfaceCrack``。 + :type dataset: str + :param saved_dir: 日志文件保存的路径,默认为 ``./``。 + :type saved_dir: str + :param using_validation: 是否使用验证集,默认为 ``False``。 + :type using_validation: bool + :param num_train: 训练集的数据量。默认为 ``-1`` ,即使用所有的训练数据。 + :type num_train: Optional[int] + :param num_val: 验证集的数据量。默认为 ``-1`` ,即使用所有的训练数据。 + :type num_val: Optional[int] + :param num_test: 测试集的数据量。默认为 ``-1`` ,即使用所有的训练数据。 + :type num_test: Optional[int] + +.. py:function:: inference(image_path: str, num_samples: int, model_path: str, num_qubits: List, num_depths: List, observables: List) + + 使用 QNNQD 模型进行推理。 + + :param image_path: 需要推理的数据集。 + :type image_path: str + :param num_samples: 需要推理数据集中图片的数量。 + :type num_samples: Optional[int] + :param model_path: 推理使用的模型。 + :type model_path: str + :param num_qubits: 每层量子电路的比特数。 + :type num_qubits: List[int] + :param num_depths: 每层参数化部分的电路深度。 + :type num_depths: List[int] + :param observables: 每层电路的测量算子。 + :type observables: List \ No newline at end of file diff --git a/docs_zh_CN/source/paddle_quantum.qml.qsann.rst b/docs_zh_CN/source/paddle_quantum.qml.qsann.rst new file mode 100644 index 0000000..0b37dc7 --- /dev/null +++ b/docs_zh_CN/source/paddle_quantum.qml.qsann.rst @@ -0,0 +1,164 @@ +paddle\_quantum.qml.qsann +============================================== + +量子自注意力神经网络(Quantum Self-Attention Neural Network, QSANN)模型 + +.. py:function:: generate_observable(num_qubits, num_terms) + + 生成测量量子态所需要的可观测量。 + + :param num_qubits: 量子比特的数量。 + :type num_qubits: int + :param num_terms: 生成的可观测量的项数。 + :type num_terms: int + + :return: 返回生成的可观测量。 + :rtype: paddle_quantum.Hamiltonian + +.. py:class:: QSANN(num_qubits, len_vocab, num_layers, depth_ebd, depth_query, depth_key, depth_value) + + 基类::py:class:`paddle.nn.Layer` + + 量子自注意力神经网络(Quantum Self-Attention Neural Network, QSANN)模型的实现。具体细节可以参考:https://arxiv.org/abs/2205.05625 。 + + :param num_qubits: 量子电路所包含的量子比特的数量。 + :type num_qubits: int + :param len_vocab: 数据集的词表的长度。 + :type len_vocab: int + :param num_layers: 自注意力层的层数。 + :type num_layers: int + :param depth_ebd: embedding 电路的深度。 + :type depth_ebd: int + :param depth_query: query 电路的深度。 + :type depth_query: int + :param depth_key: key 电路的深度。 + :type depth_key: int + :param depth_value: value 电路的深度。 + :type depth_value: int + + .. py:method:: forward(batch_text) + + 模型的前向执行函数。 + + :param batch_input: 模型的输入,它是一个列表,每一项都是一个由整数组成的列表。 + :type batch_input: List[List[int]] + + :return: 返回一个列表,其每一项都是对输入文本的预测结果。 + :rtype: List[paddle.Tensor] + +.. py:function:: deal_vocab(vocab_path) + + 根据输入的词汇表文件,得到从词到索引的映射。 + + :param vocab_path: 词表文件的路径。 + :type vocab_path: str + + :return: 返回从词到对应的索引的映射。 + :rtype: Dict[str, int] + +.. py:class:: TextDataset(file_path, word_idx, pad_size) + + 基类::py:class:`paddle.io.Dataset` + + 实现文本数据集的类。 + + :param file_path: 数据集的文件路径。其里面应该由多行组成。每一行包含文本标签,由制表符或空格分开。 + :type file_path: str + :param word2idx: 数据集的数据量大小。默认为 ``0`` ,表示使用所有数据。 + :type word2idx: dict + :param pad_size: 要将文本序列填充到的长度。默认为 ``0`` ,即不进行填充。 + :type pad_size: int + +.. py:function:: build_iter(dataset, batch_size, shuffle) + + 建立批数据的可迭代类型。 + + :param dataset: 输入的数据集,对其进行构建批数据的可迭代类型。 + :type dataset: paddle.io.Dataset + :param batch_size: 批数据的大小。 + :type batch_size: int + :param shuffle: 是否要随机打乱数据。默认为 ``Flase`` ,即不随机打乱。 + :type shuffle: bool + + :return: 构建的可迭代类型,其中包含生成的批数据。 + :rtype: list + +.. py:function:: train(model_name, dataset, num_qubits, num_layers, depth_ebd, depth_query, depth_key, depth_value, batch_size, num_epochs, learning_rate, saved_dir, using_validation, early_stopping) + + 训练 VSQL 模型的函数。 + + :param model_name: 模型的名字,用于作为保存的模型参数的文件名。 + :type model_name: str + :param dataset: 模型的名字,用于作为保存的模型参数的文件名。 + :type dataset: str + :param num_qubits: 量子电路所包含的量子比特的数量。 + :type num_qubits: int + :param num_layers: 自注意力层的层数。 + :type num_layers: int + :param depth_ebd: embedding 电路的深度。 + :type depth_ebd: int + :param depth_query: query 电路的深度。 + :type depth_query: int + :param depth_key: key 电路的深度。 + :type depth_key: int + :param depth_value: value 电路的深度。 + :type depth_value: int + :param batch_size: 数据的批大小。 + :type batch_size: int + :param num_epochs: 训练的轮数。 + :type num_epochs: int + :param learning_rate: 更新参数的学习率,默认为 ``0.01`` 。 + :type learning_rate: float + :param saved_dir: 训练得到的模型文件的保存路径,默认使用当前目录。 + :type saved_dir: str + :param using_validation: 是否使用验证集。默认为 ``False`` ,即不包含验证集。 + :type using_validation: bool + :param early_stopping: 默认为 ``1000`` ,即如果模型在 1000 次迭代中,在验证集上的 loss 没有提升,则会自动停止训练。 + :type early_stopping: int + +.. py:function:: evaluate(model, data_loader) + + 对模型进行评估。 + + :param model: 训练得到的模型,用于被评估。 + :type model: paddle.nn.Layer + :param data_loader: 用于评估模型的数据加载器。 + :type data_loader: list + + :return: 返回模型在输入数据上的平均的损失值和平均准确率。 + :rtype: Tuple[float, float] + +.. py:function:: test(model, model_path, test_loader) + + 使用测试集对模型进行测试。 + + :param model: 训练得到的模型,用于被评估。 + :type model: paddle.nn.Layer + :param model_path: 保存的模型参数的文件路径。 + :type model_path: str + :param test_loader: 测试集的数据加载器。 + :type test_loader: list + +.. py:function:: inference() + + 推理函数。使用训练好的模型对输入的图片进行预测。 + + :param text: 要预测的图片的路径。 + :type text: str + :param model_path: 保存的模型参数的文件路径。 + :type model_path: str + :param num_qubits: 量子电路所包含的量子比特的数量。 + :type num_qubits: int + :param num_layers: 自注意力层的层数。 + :type num_layers: int + :param depth_ebd: embedding 电路的深度。 + :type depth_ebd: int + :param depth_query: query 电路的深度。 + :type depth_query: int + :param depth_key: key 电路的深度。 + :type depth_key: int + :param depth_value: value 电路的深度。 + :type depth_value: int + + :return: 返回模型预测的类别。 + :rtype: str diff --git a/docs_zh_CN/source/paddle_quantum.qml.rst b/docs_zh_CN/source/paddle_quantum.qml.rst index 57ad53f..8882871 100644 --- a/docs_zh_CN/source/paddle_quantum.qml.rst +++ b/docs_zh_CN/source/paddle_quantum.qml.rst @@ -8,4 +8,7 @@ paddle\_quantum.qml .. toctree:: :maxdepth: 4 + paddle_quantum.qml.qnnmic + paddle_quantum.qml.qnnqd + paddle_quantum.qml.qsann paddle_quantum.qml.vsql diff --git a/docs_zh_CN/source/paddle_quantum.qml.vsql.rst b/docs_zh_CN/source/paddle_quantum.qml.vsql.rst index fcb22bb..b85e507 100644 --- a/docs_zh_CN/source/paddle_quantum.qml.vsql.rst +++ b/docs_zh_CN/source/paddle_quantum.qml.vsql.rst @@ -1,49 +1,43 @@ paddle\_quantum.qml.vsql ============================================== -VSQL 模型。 -.. py:function:: norm_image(images, num_qubits) +变分影子量子学习(variational shadow quantum learning, VSQL)模型。 + +.. py:function:: image_process(images, num_qubits) 对输入的图片进行归一化。先将图片展开为向量,再进行归一化。 :param images: 输入的图片。 - :type images: List[np.ndarray] + :type images: numpy.ndarray :param num_qubits: 量子比特的数量,决定了归一化向量的维度。 :type num_qubits: int :return: 返回归一化之后的向量,它是由 ``paddle.Tensor`` 组成的列表。 - :rtype: List[paddle.Tensor] - -.. py:function:: data_loading(num_qubits, mode, classes, num_data) + :rtype: numpy.ndarray - 加载 MNIST 数据集,其中只包含指定的数据。 - - :param num_qubits: 量子比特的数量,决定了归一化向量的维度。 - :type num_qubits: int - :param mode: 指定要加载的数据集,为 ``'train'`` 或 ``'test'`` 。 +.. py:class:: ImageDataset(file_path, num_samples, transform) - - ``'train'`` :表示加载训练集。 - - ``'test'`` :表示加载测试集。 + 基类::py:class:`paddle.io.Dataset` - :type mode: str - :param classes: 要加载的数据的标签。对应标签的数据会被加载。 - :type classes: list - :param num_data: 要加载的数据的数量。默认为 ``None`` ,加载所有数据。 - :type num_data: Optional[int] + 实现图片数据集的类。 - :return: 返回加载的数据集,其组成为 ``(images, labels)`` 。 - :rtype: Tuple[List[np.ndarray], List[int]] + :param file_path: 数据集的文件路径。其里面应该由多行组成。每一行包含图片的文件路径和标签,由制表符分开。 + :type file_path: str + :param num_samples: 数据集的数据量大小。默认为 ``0`` ,表示使用所有数据。 + :type num_samples: int + :param transform: 对图片进行预处理的方法。默认为 ``None`` ,即不进行任何预处理。 + :type transform: Optional[Callable] -.. py:function:: observable(start_idx, num_shadow) +.. py:function:: generate_observable(start_idx, num_shadow) - 生成测量量子态所需要的哈密顿量。 + 生成测量量子态所需要的可观测量。 :param start_idx: 要测量的量子比特的起始索引。 :type start_idx: int :param num_shadow: 影子电路所包含的量子比特的数量。 :type num_shadow: int - :return: 返回生成的哈密顿量。 + :return: 返回生成的可观测量。 :rtype: paddle_quantum.Hamiltonian .. py:class:: VSQL(num_qubits, num_shadow, num_classes, depth) @@ -63,31 +57,92 @@ VSQL 模型。 .. py:method:: forward(batch_input) + 模型的前向执行函数。 + :param batch_input: 模型的输入,其形状为 :math:`(\text{batch_size}, 2^{\text{num_qubits}})` 。 :type batch_input: List[paddle.Tensor] :return: 返回模型的输出,其形状为 :math:`(\text{batch_size}, \text{num_classes})` 。 :rtype: paddle.Tensor -.. py:function:: train(num_qubits, num_shadow, depth, batch_size, epoch, learning_rate, classes, num_train, num_test) +.. py:function:: train(model_name, num_qubits, num_shadow, classes, batch_size, num_epochs, depth, datasets, saved_dir, learning_rate, using_validation, num_workers, early_stopping, num_train, num_dev, num_test) - 训练 VSQL 模型。 + 训练 VSQL 模型的函数。 + :param model_name: 模型的名字,用于作为保存的模型参数的文件名。 + :type model_name: str :param num_qubits: 量子电路所包含的量子比特的数量。 :type num_qubits: int :param num_shadow: 影子电路所包含的量子比特的数量。 :type num_shadow: int + :param classes: 要预测的图片的类别。 + :type classes: list + :param batch_size: 数据的批大小。 + :type batch_size: int + :param num_epochs: 训练的轮数。 + :type num_epochs: int :param depth: 量子电路的深度,默认为 ``1`` 。 - :type depth: Optional[int] - :param batch_size: 数据的批大小,默认为 ``16`` 。 - :type batch_size: Optional[int] - :param epoch: 训练的轮数,默认为 ``10`` 。 - :type epoch: Optional[int] + :type depth: int + :param datasets: 训练所使用的数据集文件夹路径。默认为 ``MNIST``,即使用内置的 MNIST 数据集。 + :type datasets: str + :param saved_dir: 训练得到的模型文件的保存路径,默认使用当前目录。 + :type saved_dir: str :param learning_rate: 更新参数的学习率,默认为 ``0.01`` 。 - :type learning_rate: Optional[float] - :param classes: 要预测的手写数字的类别。默认为 ``None`` ,即预测所有的类别。 - :type classes: Optional[list] - :param num_train: 训练集的数据量。默认为 ``None`` ,即使用所有的训练数据。 - :type num_train: Optional[int] - :param num_test: 测试集的数据量。默认为 ``None`` ,即使用所有的训练数据。 - :type num_test: Optional[int] + :type learning_rate: float + :param using_validation: 是否使用验证集。默认为 ``False`` ,即不包含验证集。 + :type using_validation: bool + :param num_workers: 构建数据集加载器的线程数,默认为 ``0`` ,即不使用额外线程。 + :type num_workers: int + :param early_stopping: 默认为 ``1000`` ,即如果模型在 1000 次迭代中,在验证集上的 loss 没有提升,则会自动停止训练。 + :type early_stopping: int + :param num_train: 训练集的数据量。默认为 ``0`` ,即使用所有的训练数据。 + :type num_train: int + :param num_dev: 验证集的数据量。默认为 ``0`` ,即使用所有的训练数据。 + :type num_dev: int + :param num_test: 测试集的数据量。默认为 ``0`` ,即使用所有的训练数据。 + :type num_test: int + +.. py:function:: evaluate(model, data_loader) + + 对模型进行评估。 + + :param model: 训练得到的模型,用于被评估。 + :type model: paddle.nn.Layer + :param data_loader: 用于评估模型的数据集的 dataloader。 + :type data_loader: paddle.io.DataLoader + + :return: 返回模型在输入数据上的平均的损失值和平均准确率。 + :rtype: Tuple[float, float] + +.. py:function:: test(model, model_path, test_loader) + + 使用测试集对模型进行测试。 + + :param model: 训练得到的模型,用于被评估。 + :type model: paddle.nn.Layer + :param model_path: 保存的模型参数的文件路径。 + :type model_path: str + :param test_loader: 测试集的 dataloader。 + :type test_loader: paddle.io.DataLoader + +.. py:function:: inference(image_path, is_dir, model_path, num_qubits, num_shadow, classes, depth) + + 推理函数。使用训练好的模型对输入的图片进行预测。 + + :param image_path: 要预测的图片的路径。 + :type image_path: str + :param is_dir: 所输入的 ``image_path`` 是否为文件夹路径。如果是文件夹路径,则会对文件夹下的所有图片都进行预测。 + :type is_dir: bool + :param model_path: 保存的模型参数的文件路径。 + :type model_path: str + :param num_qubits: 量子电路所包含的量子比特的数量。 + :type num_qubits: int + :param num_shadow: 影子电路所包含的量子比特的数量。 + :type num_shadow: int + :param classes: 要预测的图片的类别。 + :type classes: list + :param depth: 量子电路的深度,默认为 ``1`` 。 + :type depth: int + + :return: 返回模型预测的类别,以及模型对每个类别的置信度。 + :rtype: Tuple[int, list] diff --git a/introduction/PaddleQuantum_Qchem_Tutorial_CN.ipynb b/introduction/PaddleQuantum_Qchem_Tutorial_CN.ipynb index b8da1e7..d6fea60 100644 --- a/introduction/PaddleQuantum_Qchem_Tutorial_CN.ipynb +++ b/introduction/PaddleQuantum_Qchem_Tutorial_CN.ipynb @@ -12,17 +12,18 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "qchem 是基于 Paddle Quantum 推出的用于量子化学研究的工具集。qchem 为量子化学领域的研究者提供了一系列工具,使他们可以利用量子计算方法完成量子化学任务。与此同时,qchem 也提供了方便开发者进行功能拓展的方式。目前,qchem 正处于开发之中,您可以将需求和建议通过 Github 的 issue 或 pull request 反馈给我们,我们会及时给出回复。" + "qchem 是基于 Paddle Quantum 推出的用于量子化学研究的工具集。qchem 为量子化学领域的研究者提供了一系列工具,使他们可以利用量子计算方法完成量子化学任务。与此同时,qchem 也提供了方便开发者进行功能拓展的方式。目前,qchem 正处于开发之中,您可以将需求和建议通过 GitHub 的 issue 或 pull request 反馈给我们,我们会及时给出回复。" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## 分子基态能量计算\n", "qchem 为量子化学计算提供了很多便捷的工具。目前,qchem 模块支持下列分子波函数模版线路:\n", "* Hardware Efficient ansatz[1](#refer-1),\n", - "* Slater determinant ansatz[2](#refer-2),\n", + "* Hartree Fock ansatz[2](#refer-2),\n", "* Unitary Coupled Cluster singles and doubles (UCCSD) ansatz[3](#refer-3).\n", "\n", "让我们从具体的例子出发了解 qchem 的使用方法,下面我们演示了利用 qchem 求解氢分子基态的过程。" @@ -37,14 +38,17 @@ "import paddle_quantum as pq\n", "from paddle_quantum import qchem as pq_qchem\n", "import warnings\n", - "warnings.filterwarnings(\"ignore\")" + "warnings.filterwarnings(\"ignore\")\n", + "import logging\n", + "logging.basicConfig(level=logging.INFO)" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "接下来,我们需要提供量子化学计算需要用到的一些分子性质,包括:分子的几何结构、分子的电荷、计算需要用到的量子化学基函数等。" + "接下来,我们会利用分子的一些主要性质,包括:分子的几何结构、分子的电荷、计算需要用到的量子化学基函数等,来构建一个 qchem 中的 `Molecule` 类。 " ] }, { @@ -53,359 +57,125 @@ "metadata": {}, "outputs": [], "source": [ - "# 定义氢分子的几何结构,长度单位为埃\n", - "h2_geometry = \"H 0.0 0.0 0.0; H 0.0 0.0 0.74\"\n", - "basis_set = \"sto-3g\"\n", - "multiplicity = 1\n", - "charge = 0" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "然后,我们需要为氢分子选择一种波函数模版线路。我们选择 `UCCSDModel` 作为模版线路并且用 `MolEnergyLoss` 作为损失函数。`UCCSDModel` 需要用 Trotter-Suzuki 方法构造其量子线路,关于 Trotter-Suzuki 方法,感兴趣的读者可以阅读这篇[教程](https://qml.baidu.com/tutorials/quantum-simulation/hamiltonian-simulation-with-product-formula.html)。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# 构建 UCCSD 线路.\n", - "n_qubits = 4\n", - "n_electrons = 2\n", - "uccsd_ansatz = pq_qchem.UCCSDModel(n_qubits, n_electrons, n_trotter_steps=3)\n", + "# `driver` 用来计算分子中的各种积分\n", + "driver = pq_qchem.PySCFDriver()\n", "\n", - "# 设置损失函数\n", - "loss_fn = pq_qchem.MolEnergyLoss(h2_geometry, basis_set)" + "# 通过氢分子的性质构造一个 Molecule 类,注:长度单位为埃\n", + "mol = pq_qchem.Molecule(\n", + " geometry=[(\"H\", [0.0, 0.0, 0.0]), (\"H\", [0.0, 0.0, 0.74])],\n", + " basis=\"sto-3g\",\n", + " multiplicity=1, \n", + " driver=driver\n", + ")" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "完成上面的步骤之后,我们可以按照[教程](https://qml.baidu.com/tutorials/quantum-simulation/variational-quantum-eigensolver.html)中的方法利用 paddlepaddle 中的优化器来训练参数化量子线路。" + "然后,我们需要为氢分子选择一种波函数模版线路。我们选择 `HardwareEfficient` 作为模版线路。" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:root:\n", + "#######################################\n", + "Molecule\n", + "#######################################\n", + "INFO:root:H2\n", + "INFO:root:Geometry:\n", + "INFO:root:H 0.00000, 0.00000, 0.00000\n", + "H 0.00000, 0.00000, 0.74000\n", + "INFO:root:Charge: 0\n", + "INFO:root:Multiplicity: 1\n", + "INFO:root:Unit: Angstrom\n", + "INFO:root:\n", + "#######################################\n", + "SCF Calculation (Classical)\n", + "#######################################\n", + "INFO:root:Basis: sto-3g\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "The iter is 0, loss is 0.71510.\n", - "The iter is 1, loss is 0.63558.\n", - "The iter is 2, loss is 0.13140.\n", - "The iter is 3, loss is -0.20547.\n", - "The iter is 4, loss is -0.59966.\n", - "The iter is 5, loss is -0.30310.\n", - "The iter is 6, loss is -0.69676.\n", - "The iter is 7, loss is -0.75225.\n", - "The iter is 8, loss is -0.75798.\n", - "The iter is 9, loss is -0.92072.\n", - "The iter is 10, loss is -0.89658.\n", - "The iter is 11, loss is -0.90863.\n", - "The iter is 12, loss is -0.96792.\n", - "The iter is 13, loss is -0.95464.\n", - "The iter is 14, loss is -0.96280.\n", - "The iter is 15, loss is -1.00236.\n", - "The iter is 16, loss is -1.02617.\n", - "The iter is 17, loss is -1.06962.\n", - "The iter is 18, loss is -1.06430.\n", - "The iter is 19, loss is -1.04589.\n", - "The iter is 20, loss is -1.06411.\n", - "The iter is 21, loss is -1.05254.\n", - "The iter is 22, loss is -1.04040.\n", - "The iter is 23, loss is -1.07904.\n", - "The iter is 24, loss is -1.10329.\n", - "The iter is 25, loss is -1.09534.\n", - "The iter is 26, loss is -1.10908.\n", - "The iter is 27, loss is -1.10671.\n", - "The iter is 28, loss is -1.09553.\n", - "The iter is 29, loss is -1.11231.\n", - "The iter is 30, loss is -1.11247.\n", - "The iter is 31, loss is -1.11016.\n", - "The iter is 32, loss is -1.12201.\n", - "The iter is 33, loss is -1.12010.\n", - "The iter is 34, loss is -1.11167.\n", - "The iter is 35, loss is -1.12422.\n", - "The iter is 36, loss is -1.12399.\n", - "The iter is 37, loss is -1.12124.\n", - "The iter is 38, loss is -1.12681.\n", - "The iter is 39, loss is -1.12666.\n", - "The iter is 40, loss is -1.12503.\n", - "The iter is 41, loss is -1.13086.\n", - "The iter is 42, loss is -1.13036.\n", - "The iter is 43, loss is -1.13068.\n", - "The iter is 44, loss is -1.13146.\n", - "The iter is 45, loss is -1.13127.\n", - "The iter is 46, loss is -1.13295.\n", - "The iter is 47, loss is -1.13266.\n", - "The iter is 48, loss is -1.13277.\n", - "The iter is 49, loss is -1.13409.\n", - "The iter is 50, loss is -1.13268.\n", - "The iter is 51, loss is -1.13372.\n", - "The iter is 52, loss is -1.13478.\n", - "The iter is 53, loss is -1.13374.\n", - "The iter is 54, loss is -1.13571.\n", - "The iter is 55, loss is -1.13538.\n", - "The iter is 56, loss is -1.13525.\n", - "The iter is 57, loss is -1.13623.\n", - "The iter is 58, loss is -1.13576.\n", - "The iter is 59, loss is -1.13595.\n", - "The iter is 60, loss is -1.13564.\n", - "The iter is 61, loss is -1.13593.\n", - "The iter is 62, loss is -1.13649.\n", - "The iter is 63, loss is -1.13643.\n", - "The iter is 64, loss is -1.13676.\n", - "The iter is 65, loss is -1.13627.\n", - "The iter is 66, loss is -1.13635.\n", - "The iter is 67, loss is -1.13670.\n", - "The iter is 68, loss is -1.13649.\n", - "The iter is 69, loss is -1.13696.\n", - "The iter is 70, loss is -1.13686.\n", - "The iter is 71, loss is -1.13687.\n", - "The iter is 72, loss is -1.13697.\n", - "The iter is 73, loss is -1.13685.\n", - "The iter is 74, loss is -1.13703.\n", - "The iter is 75, loss is -1.13698.\n", - "The iter is 76, loss is -1.13710.\n", - "The iter is 77, loss is -1.13706.\n", - "The iter is 78, loss is -1.13706.\n", - "The iter is 79, loss is -1.13706.\n", - "The iter is 80, loss is -1.13700.\n", - "The iter is 81, loss is -1.13716.\n", - "The iter is 82, loss is -1.13715.\n", - "The iter is 83, loss is -1.13721.\n", - "The iter is 84, loss is -1.13717.\n", - "The iter is 85, loss is -1.13714.\n", - "The iter is 86, loss is -1.13717.\n", - "The iter is 87, loss is -1.13719.\n", - "The iter is 88, loss is -1.13724.\n", - "The iter is 89, loss is -1.13723.\n", - "The iter is 90, loss is -1.13722.\n", - "The iter is 91, loss is -1.13721.\n", - "The iter is 92, loss is -1.13723.\n", - "The iter is 93, loss is -1.13721.\n", - "The iter is 94, loss is -1.13725.\n", - "The iter is 95, loss is -1.13724.\n", - "The iter is 96, loss is -1.13724.\n", - "The iter is 97, loss is -1.13725.\n", - "The iter is 98, loss is -1.13725.\n", - "The iter is 99, loss is -1.13725.\n", - "The theoretical value is -1.137283834485513.\n" + "converged SCF energy = -1.11675930739643\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:root:SCF energy: -1.11676.\n" ] } ], "source": [ - "# 选择 paddlepaddle 中的 Adam 优化器\n", - "import paddle\n", - "\n", - "optimizer = paddle.optimizer.Adam(parameters=uccsd_ansatz.parameters(), learning_rate=0.1)\n", - "\n", - "# 制备初始量子态, e.g. |0000>\n", - "init_state = pq.state.computational_basis(n_qubits, 0)\n", - "\n", - "# 定义优化步数\n", - "num_itr = 100\n", - "for itr in range(0, num_itr):\n", - " # 运行量子线路得到末态\n", - " state = uccsd_ansatz(init_state)\n", - " # 计算损失函数,即期望值\n", - " loss = loss_fn(state)\n", - " # 反向传播梯度\n", - " loss.backward()\n", - " # 通过loss值更新参数\n", - " optimizer.minimize(loss)\n", - " # 清除当前梯度\n", - " optimizer.clear_grad()\n", - " print(f\"The iter is {itr:3d}, loss is {loss.item():3.5f}.\")\n", - "print(\"The theoretical value is -1.137283834485513.\")" + "# 构建 HardwareEfficient 线路.\n", + "mol.build()\n", + "n_qubits = mol.num_qubits\n", + "depth = 2\n", + "cir = pq_qchem.HardwareEfficient(n_qubits, depth)" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "现在你可以把波函数模版替换为 `HardwareEfficientModel`,然后尝试比较一下使用两种方法计算出来的基态能量。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 利用Hatree Fock方法计算分子基态能量\n", - "Hartree Fock方法是量子化学中非常重要的方法。如果要利用qchem模块运行Hartree Fock方法的话,我们只需要把前面的波函数模版线路换成`RHFSlaterDeterminantModel`,并把损失函数换成`RHFEnergyLoss`(注意:使用Hartree Fock方法需要安装PySCF,`pip install -U pyscf`)。" + "完成上面的步骤之后,我们可以调用 `GroundStateSolver` 求解器,并利用 PaddlePaddle 中的优化器来训练参数化量子线路。" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "Overwritten attributes multiplicity of \n" - ] - } - ], - "source": [ - "# 构建Hartree Fock线路\n", - "n_qubits = 4\n", - "n_electrons = 2\n", - "hartreefock_ansatz = pq_qchem.RHFSlaterDeterminantModel(n_qubits, n_electrons)\n", - "\n", - "# 设置Hartree Fock损失函数\n", - "loss_fn = pq_qchem.RHFEnergyLoss(h2_geometry, basis_set)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The iter is 0, loss is 0.37967.\n", - "The iter is 1, loss is 0.31168.\n", - "The iter is 2, loss is 0.22180.\n", - "The iter is 3, loss is 0.10975.\n", - "The iter is 4, loss is -0.02357.\n", - "The iter is 5, loss is -0.17552.\n", - "The iter is 6, loss is -0.34124.\n", - "The iter is 7, loss is -0.51346.\n", - "The iter is 8, loss is -0.68254.\n", - "The iter is 9, loss is -0.83712.\n", - "The iter is 10, loss is -0.96538.\n", - "The iter is 11, loss is -1.05717.\n", - "The iter is 12, loss is -1.10677.\n", - "The iter is 13, loss is -1.11545.\n", - "The iter is 14, loss is -1.09212.\n", - "The iter is 15, loss is -1.05087.\n", - "The iter is 16, loss is -1.00622.\n", - "The iter is 17, loss is -0.96914.\n", - "The iter is 18, loss is -0.94574.\n", - "The iter is 19, loss is -0.93791.\n", - "The iter is 20, loss is -0.94471.\n", - "The iter is 21, loss is -0.96343.\n", - "The iter is 22, loss is -0.99040.\n", - "The iter is 23, loss is -1.02149.\n", - "The iter is 24, loss is -1.05253.\n", - "The iter is 25, loss is -1.07977.\n", - "The iter is 26, loss is -1.10035.\n", - "The iter is 27, loss is -1.11271.\n", - "The iter is 28, loss is -1.11676.\n", - "The iter is 29, loss is -1.11378.\n", - "The iter is 30, loss is -1.10610.\n", - "The iter is 31, loss is -1.09642.\n", - "The iter is 32, loss is -1.08731.\n", - "The iter is 33, loss is -1.08072.\n", - "The iter is 34, loss is -1.07775.\n", - "The iter is 35, loss is -1.07866.\n", - "The iter is 36, loss is -1.08294.\n", - "The iter is 37, loss is -1.08958.\n", - "The iter is 38, loss is -1.09727.\n", - "The iter is 39, loss is -1.10472.\n", - "The iter is 40, loss is -1.11083.\n", - "The iter is 41, loss is -1.11488.\n", - "The iter is 42, loss is -1.11665.\n", - "The iter is 43, loss is -1.11636.\n", - "The iter is 44, loss is -1.11457.\n", - "The iter is 45, loss is -1.11207.\n", - "The iter is 46, loss is -1.10960.\n", - "The iter is 47, loss is -1.10780.\n", - "The iter is 48, loss is -1.10702.\n", - "The iter is 49, loss is -1.10736.\n", - "The iter is 50, loss is -1.10865.\n", - "The iter is 51, loss is -1.11056.\n", - "The iter is 52, loss is -1.11265.\n", - "The iter is 53, loss is -1.11454.\n", - "The iter is 54, loss is -1.11592.\n", - "The iter is 55, loss is -1.11664.\n", - "The iter is 56, loss is -1.11672.\n", - "The iter is 57, loss is -1.11630.\n", - "The iter is 58, loss is -1.11561.\n", - "The iter is 59, loss is -1.11489.\n", - "The iter is 60, loss is -1.11436.\n", - "The iter is 61, loss is -1.11412.\n", - "The iter is 62, loss is -1.11423.\n", - "The iter is 63, loss is -1.11462.\n", - "The iter is 64, loss is -1.11519.\n", - "The iter is 65, loss is -1.11579.\n", - "The iter is 66, loss is -1.11629.\n", - "The iter is 67, loss is -1.11663.\n", - "The iter is 68, loss is -1.11676.\n", - "The iter is 69, loss is -1.11670.\n", - "The iter is 70, loss is -1.11653.\n", - "The iter is 71, loss is -1.11631.\n", - "The iter is 72, loss is -1.11612.\n", - "The iter is 73, loss is -1.11601.\n", - "The iter is 74, loss is -1.11601.\n", - "The iter is 75, loss is -1.11611.\n", - "The iter is 76, loss is -1.11628.\n", - "The iter is 77, loss is -1.11646.\n", - "The iter is 78, loss is -1.11662.\n", - "The iter is 79, loss is -1.11672.\n", - "The iter is 80, loss is -1.11676.\n", - "The iter is 81, loss is -1.11674.\n", - "The iter is 82, loss is -1.11668.\n", - "The iter is 83, loss is -1.11661.\n", - "The iter is 84, loss is -1.11656.\n", - "The iter is 85, loss is -1.11653.\n", - "The iter is 86, loss is -1.11654.\n", - "The iter is 87, loss is -1.11658.\n", - "The iter is 88, loss is -1.11663.\n", - "The iter is 89, loss is -1.11669.\n", - "The iter is 90, loss is -1.11673.\n", - "The iter is 91, loss is -1.11676.\n", - "The iter is 92, loss is -1.11676.\n", - "The iter is 93, loss is -1.11674.\n", - "The iter is 94, loss is -1.11672.\n", - "The iter is 95, loss is -1.11670.\n", - "The iter is 96, loss is -1.11669.\n", - "The iter is 97, loss is -1.11669.\n", - "The iter is 98, loss is -1.11670.\n", - "The iter is 99, loss is -1.11671.\n", - "The theoretical value is -1.11675.\n" + "INFO:root:\n", + "#######################################\n", + "VQE (Ground State)\n", + "#######################################\n", + "INFO:root:Number of qubits: 4\n", + "INFO:root:Ansatz: HardwareEfficient\n", + "INFO:root:Optimizer: Adam\n", + "INFO:root:\tlearning_rate: 0.5\n", + "INFO:root:\n", + "Optimization:\n", + "INFO:root:\tItr 0, loss=-0.32028.\n", + "INFO:root:\tItr 10, loss=-1.02221.\n", + "INFO:root:\tItr 20, loss=-1.12635.\n", + "INFO:root:\tItr 30, loss=-1.13166.\n", + "INFO:root:\tItr 40, loss=-1.13439.\n", + "INFO:root:\tItr 50, loss=-1.13665.\n", + "INFO:root:\tItr 60, loss=-1.13680.\n", + "INFO:root:Optimization converged after 65 iterations.\n", + "INFO:root:The final loss = -1.13705.\n" ] } ], "source": [ "# 选择 paddlepaddle 中的 Adam 优化器\n", - "import paddle\n", - "\n", - "optimizer = paddle.optimizer.Adam(parameters=hartreefock_ansatz.parameters(), learning_rate=0.1)\n", - "\n", - "# 制备初始量子态, e.g. |1100>\n", - "init_state = pq.state.computational_basis(n_qubits, 12)\n", + "from paddle.optimizer import Adam\n", "\n", - "# 定义优化步数\n", - "num_itr = 100\n", - "for itr in range(0, num_itr):\n", - " # 运行量子线路得到末态\n", - " state = hartreefock_ansatz(init_state)\n", - " # 计算损失函数,即期望值\n", - " loss = loss_fn(state)\n", - " # 反向传播梯度\n", - " loss.backward()\n", - " # 通过loss值更新参数\n", - " optimizer.minimize(loss)\n", - " # 清除当前梯度\n", - " optimizer.clear_grad()\n", - " print(f\"The iter is {itr:3d}, loss is {loss.item():3.5f}.\")\n", - "print(\"The theoretical value is -1.11675.\")" + "solver = pq_qchem.GroundStateSolver(Adam, num_iterations=100, tol=1e-5, save_every=10)\n", + "e, psi = solver.solve(mol, cir, learning_rate=0.5)" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -414,16 +184,15 @@ "\n", "[1] Kandala, Abhinav, et al. \"Hardware-efficient variational quantum eigensolver for small molecules and quantum magnets.\" [Nature 549.7671 (2017): 242-246.](https://www.nature.com/articles/nature23879)\n", "\n", - "[2] Arute, Frank, et al. \"Hartree-Fock on a superconducting qubit quantum computer.\" [Science 369.6507 (2020): 1084-1089.](https://www.science.org/doi/10.1126/science.abb9811)" + "[2] Arute, Frank, et al. \"Hartree-Fock on a superconducting qubit quantum computer.\" [Science 369.6507 (2020): 1084-1089.](https://www.science.org/doi/10.1126/science.abb9811)\n", + "\n", + "[3] Abhinav, Aspuru-Guzik, et al. \"A Quantum Computing View on Unitary Coupled Cluster Theory\" (https://arxiv.org/abs/2109.15176)" ] } ], "metadata": { - "interpreter": { - "hash": "f7cfecff1ef1940b21a48efa1b88278bb096bd916f13c2df11af4810c38b47e1" - }, "kernelspec": { - "display_name": "Python 3.8.0 ('pq')", + "display_name": "modellib", "language": "python", "name": "python3" }, @@ -437,7 +206,12 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.0" + "version": "3.7.15" + }, + "vscode": { + "interpreter": { + "hash": "8f24120f890011f53feb4ed62c47961d8565ec1de8b7cb23548c15bd6da8f2d2" + } } }, "nbformat": 4, diff --git a/introduction/PaddleQuantum_Qchem_Tutorial_EN.ipynb b/introduction/PaddleQuantum_Qchem_Tutorial_EN.ipynb index 81800f5..c87dc4d 100644 --- a/introduction/PaddleQuantum_Qchem_Tutorial_EN.ipynb +++ b/introduction/PaddleQuantum_Qchem_Tutorial_EN.ipynb @@ -11,13 +11,14 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Calculating ground state energy of a molecule\n", "qchem provides various tools for you to do quantum chemistry calculation. Currently, qchem module supports the following molecular wave function ansatz:\n", "* Hardware Efficient ansatz[1](#refer-1),\n", - "* Slater determinant ansatz[2](#refer-2),\n", + "* Hartree Fock ansatz[2](#refer-2),\n", "* Unitary Coupled Cluster singles and doubles (UCCSD) ansatz[3](#refer-3).\n", "\n", "Let's start from example and try to calculate the ground state energy of hydrogen molecule. First, we need to import qchem." @@ -32,14 +33,17 @@ "import paddle_quantum as pq\n", "from paddle_quantum import qchem as pq_qchem\n", "import warnings\n", - "warnings.filterwarnings(\"ignore\")" + "warnings.filterwarnings(\"ignore\")\n", + "import logging\n", + "logging.basicConfig(level=logging.INFO)" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "Then, we need to provide chemical information required by running quantum chemistry calculation, including geometry, charge, basis function etc." + "Then, we will build a `Molecule` class for a molecule based on properites such as geometry, charge, chemical basis set." ] }, { @@ -48,217 +52,82 @@ "metadata": {}, "outputs": [], "source": [ - "# define the geometry of hydrogen molecule, length unit use angstrom.\n", - "h2_geometry = \"H 0.0 0.0 0.0; H 0.0 0.0 0.74\"\n", - "basis_set = \"sto-3g\"\n", - "multiplicity = 1\n", - "charge = 0" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we need to choose an wavefunction ansatz for our hydrogen molecule. Let's choose `UCCSDModel` as the ansatz and use `MolEnergyLoss` as the loss function. `UCCSDModel` use Trotter-Suzuki method to build its wavefunction ansatz, see [here](https://qml.baidu.com/tutorials/quantum-simulation/hamiltonian-simulation-with-product-formula.html) if you want to know more about Trotter-Suzuki method." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Build UCCSD ansatz.\n", - "n_qubits = 4\n", - "n_electrons = 2\n", - "uccsd_ansatz = pq_qchem.UCCSDModel(n_qubits, n_electrons, n_trotter_steps=3)\n", + "# `driver` is used to calculate various molecular integrals.\n", + "driver = pq_qchem.PySCFDriver()\n", "\n", - "# Setup the loss function\n", - "loss_fn = pq_qchem.MolEnergyLoss(h2_geometry, basis_set)" + "# Build a Molecule class based on its properties, note, the length unit is Angstrom.\n", + "mol = pq_qchem.Molecule(\n", + " geometry=[(\"H\", [0.0, 0.0, 0.0]), (\"H\", [0.0, 0.0, 0.74])],\n", + " basis=\"sto-3g\",\n", + " multiplicity=1, \n", + " driver=driver\n", + ")" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "Finally, you can follow the optimization procedure [here](https://qml.baidu.com/tutorials/quantum-simulation/variational-quantum-eigensolver.html) and use any paddlepaddle optimizer to train the ansatz you have built." + "Next, we need to choose an wavefunction ansatz for our hydrogen molecule. Let's choose `HardwareEfficient` as the ansatz. " ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:root:\n", + "#######################################\n", + "Molecule\n", + "#######################################\n", + "INFO:root:H2\n", + "INFO:root:Geometry:\n", + "INFO:root:H 0.00000, 0.00000, 0.00000\n", + "H 0.00000, 0.00000, 0.74000\n", + "INFO:root:Charge: 0\n", + "INFO:root:Multiplicity: 1\n", + "INFO:root:Unit: Angstrom\n", + "INFO:root:\n", + "#######################################\n", + "SCF Calculation (Classical)\n", + "#######################################\n", + "INFO:root:Basis: sto-3g\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "The iter is 0, loss is 0.71510.\n", - "The iter is 1, loss is 0.68522.\n", - "The iter is 2, loss is -0.33124.\n", - "The iter is 3, loss is -0.17871.\n", - "The iter is 4, loss is -0.46901.\n", - "The iter is 5, loss is -0.35388.\n", - "The iter is 6, loss is -0.49272.\n", - "The iter is 7, loss is -0.65490.\n", - "The iter is 8, loss is -0.74100.\n", - "The iter is 9, loss is -0.75261.\n", - "The iter is 10, loss is -0.91565.\n", - "The iter is 11, loss is -1.00675.\n", - "The iter is 12, loss is -0.86924.\n", - "The iter is 13, loss is -0.91493.\n", - "The iter is 14, loss is -1.00358.\n", - "The iter is 15, loss is -0.97359.\n", - "The iter is 16, loss is -0.99694.\n", - "The iter is 17, loss is -1.05768.\n", - "The iter is 18, loss is -1.07174.\n", - "The iter is 19, loss is -1.05568.\n", - "The iter is 20, loss is -1.05740.\n", - "The iter is 21, loss is -1.06293.\n", - "The iter is 22, loss is -1.06410.\n", - "The iter is 23, loss is -1.08027.\n", - "The iter is 24, loss is -1.07894.\n", - "The iter is 25, loss is -1.09911.\n", - "The iter is 26, loss is -1.09829.\n", - "The iter is 27, loss is -1.09884.\n", - "The iter is 28, loss is -1.10620.\n", - "The iter is 29, loss is -1.11663.\n", - "The iter is 30, loss is -1.10698.\n", - "The iter is 31, loss is -1.10733.\n", - "The iter is 32, loss is -1.11066.\n", - "The iter is 33, loss is -1.11710.\n", - "The iter is 34, loss is -1.11634.\n", - "The iter is 35, loss is -1.12369.\n", - "The iter is 36, loss is -1.12732.\n", - "The iter is 37, loss is -1.11927.\n", - "The iter is 38, loss is -1.12664.\n", - "The iter is 39, loss is -1.12917.\n", - "The iter is 40, loss is -1.12288.\n", - "The iter is 41, loss is -1.12396.\n", - "The iter is 42, loss is -1.13229.\n", - "The iter is 43, loss is -1.13172.\n", - "The iter is 44, loss is -1.12766.\n", - "The iter is 45, loss is -1.13348.\n", - "The iter is 46, loss is -1.13214.\n", - "The iter is 47, loss is -1.13093.\n", - "The iter is 48, loss is -1.13300.\n", - "The iter is 49, loss is -1.13404.\n", - "The iter is 50, loss is -1.13151.\n", - "The iter is 51, loss is -1.13338.\n", - "The iter is 52, loss is -1.13581.\n", - "The iter is 53, loss is -1.13447.\n", - "The iter is 54, loss is -1.13422.\n", - "The iter is 55, loss is -1.13551.\n", - "The iter is 56, loss is -1.13554.\n", - "The iter is 57, loss is -1.13492.\n", - "The iter is 58, loss is -1.13543.\n", - "The iter is 59, loss is -1.13569.\n", - "The iter is 60, loss is -1.13606.\n", - "The iter is 61, loss is -1.13590.\n", - "The iter is 62, loss is -1.13651.\n", - "The iter is 63, loss is -1.13620.\n", - "The iter is 64, loss is -1.13619.\n", - "The iter is 65, loss is -1.13652.\n", - "The iter is 66, loss is -1.13650.\n", - "The iter is 67, loss is -1.13633.\n", - "The iter is 68, loss is -1.13683.\n", - "The iter is 69, loss is -1.13688.\n", - "The iter is 70, loss is -1.13674.\n", - "The iter is 71, loss is -1.13682.\n", - "The iter is 72, loss is -1.13688.\n", - "The iter is 73, loss is -1.13673.\n", - "The iter is 74, loss is -1.13705.\n", - "The iter is 75, loss is -1.13694.\n", - "The iter is 76, loss is -1.13698.\n", - "The iter is 77, loss is -1.13711.\n", - "The iter is 78, loss is -1.13707.\n", - "The iter is 79, loss is -1.13706.\n", - "The iter is 80, loss is -1.13707.\n", - "The iter is 81, loss is -1.13709.\n", - "The iter is 82, loss is -1.13717.\n", - "The iter is 83, loss is -1.13711.\n", - "The iter is 84, loss is -1.13714.\n", - "The iter is 85, loss is -1.13723.\n", - "The iter is 86, loss is -1.13714.\n", - "The iter is 87, loss is -1.13719.\n", - "The iter is 88, loss is -1.13721.\n", - "The iter is 89, loss is -1.13718.\n", - "The iter is 90, loss is -1.13723.\n", - "The iter is 91, loss is -1.13721.\n", - "The iter is 92, loss is -1.13722.\n", - "The iter is 93, loss is -1.13725.\n", - "The iter is 94, loss is -1.13723.\n", - "The iter is 95, loss is -1.13722.\n", - "The iter is 96, loss is -1.13724.\n", - "The iter is 97, loss is -1.13725.\n", - "The iter is 98, loss is -1.13725.\n", - "The iter is 99, loss is -1.13726.\n", - "The theoretical value is -1.137283834485513.\n" + "converged SCF energy = -1.11675930739643\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:root:SCF energy: -1.11676.\n" ] } ], "source": [ - "# use paddlepaddle's optimizer\n", - "import paddle\n", - "\n", - "optimizer = paddle.optimizer.Adam(parameters=uccsd_ansatz.parameters(), learning_rate=0.1)\n", - "\n", - "# prepare the initial quantum state, e.g. |0000>\n", - "init_state = pq.state.computational_basis(n_qubits, 0)\n", - "\n", - "# define the optimization steps\n", - "num_itr = 100\n", - "for itr in range(0, num_itr):\n", - " # run quantum circuit to arrive at the final state\n", - " state = uccsd_ansatz(init_state)\n", - " # calculate loss\n", - " loss = loss_fn(state)\n", - " # backpropagate the gradient\n", - " loss.backward()\n", - " # update the ansatz's parameter\n", - " optimizer.minimize(loss)\n", - " # clear current gradient\n", - " optimizer.clear_grad()\n", - " print(f\"The iter is {itr:3d}, loss is {loss.item():3.5f}.\")\n", - "print(\"The theoretical value is -1.137283834485513.\")" + "# Build a quantum circuit using HardwareEfficient ansatz.\n", + "mol.build()\n", + "n_qubits = mol.num_qubits\n", + "depth = 2\n", + "cir = pq_qchem.HardwareEfficient(n_qubits, depth)" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "You can now change the ansatz to `HardwareEfficientModel` and compare its energy with the one you just obtained." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Calculating the ground state energy using Hartree Fock method\n", - "Hartree Fock method is often considered as the starting point for more accurate quantum chemistry calculations. In order to run Hartree Fock calculation in qchem, you just need to replace the ansatz and the loss function to `RHFSlaterDeterminantModel` and `RHFEnergyLoss` (**NOTE: You need PySCF be installed before running Hartree Fock calculation**, `pip install -U pyscf`)." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Overwritten attributes multiplicity of \n" - ] - } - ], - "source": [ - "# Build a Hartree Fock ansatz.\n", - "n_qubits = 4\n", - "n_electrons = 2\n", - "hartreefock_ansatz = pq_qchem.RHFSlaterDeterminantModel(n_qubits, n_electrons)\n", - "\n", - "# Setup the loss function\n", - "loss_fn = pq_qchem.RHFEnergyLoss(h2_geometry, basis_set)" + "Finally, we can call `GroundStateSolver` and use PaddlePaddle's optimizer to update parameters in the ansatz." ] }, { @@ -267,137 +136,38 @@ "metadata": {}, "outputs": [ { - "name": "stdout", + "name": "stderr", "output_type": "stream", "text": [ - "The iter is 0, loss is 0.34341.\n", - "The iter is 1, loss is 0.26274.\n", - "The iter is 2, loss is 0.15970.\n", - "The iter is 3, loss is 0.03472.\n", - "The iter is 4, loss is -0.11024.\n", - "The iter is 5, loss is -0.27116.\n", - "The iter is 6, loss is -0.44170.\n", - "The iter is 7, loss is -0.61316.\n", - "The iter is 8, loss is -0.77486.\n", - "The iter is 9, loss is -0.91509.\n", - "The iter is 10, loss is -1.02291.\n", - "The iter is 11, loss is -1.09057.\n", - "The iter is 12, loss is -1.11626.\n", - "The iter is 13, loss is -1.10573.\n", - "The iter is 14, loss is -1.07116.\n", - "The iter is 15, loss is -1.02724.\n", - "The iter is 16, loss is -0.98662.\n", - "The iter is 17, loss is -0.95737.\n", - "The iter is 18, loss is -0.94303.\n", - "The iter is 19, loss is -0.94371.\n", - "The iter is 20, loss is -0.95742.\n", - "The iter is 21, loss is -0.98086.\n", - "The iter is 22, loss is -1.01012.\n", - "The iter is 23, loss is -1.04104.\n", - "The iter is 24, loss is -1.06973.\n", - "The iter is 25, loss is -1.09296.\n", - "The iter is 26, loss is -1.10862.\n", - "The iter is 27, loss is -1.11600.\n", - "The iter is 28, loss is -1.11581.\n", - "The iter is 29, loss is -1.10996.\n", - "The iter is 30, loss is -1.10099.\n", - "The iter is 31, loss is -1.09156.\n", - "The iter is 32, loss is -1.08386.\n", - "The iter is 33, loss is -1.07934.\n", - "The iter is 34, loss is -1.07862.\n", - "The iter is 35, loss is -1.08148.\n", - "The iter is 36, loss is -1.08713.\n", - "The iter is 37, loss is -1.09438.\n", - "The iter is 38, loss is -1.10193.\n", - "The iter is 39, loss is -1.10859.\n", - "The iter is 40, loss is -1.11348.\n", - "The iter is 41, loss is -1.11618.\n", - "The iter is 42, loss is -1.11671.\n", - "The iter is 43, loss is -1.11550.\n", - "The iter is 44, loss is -1.11325.\n", - "The iter is 45, loss is -1.11073.\n", - "The iter is 46, loss is -1.10863.\n", - "The iter is 47, loss is -1.10741.\n", - "The iter is 48, loss is -1.10728.\n", - "The iter is 49, loss is -1.10818.\n", - "The iter is 50, loss is -1.10983.\n", - "The iter is 51, loss is -1.11186.\n", - "The iter is 52, loss is -1.11384.\n", - "The iter is 53, loss is -1.11543.\n", - "The iter is 54, loss is -1.11642.\n", - "The iter is 55, loss is -1.11676.\n", - "The iter is 56, loss is -1.11653.\n", - "The iter is 57, loss is -1.11594.\n", - "The iter is 58, loss is -1.11521.\n", - "The iter is 59, loss is -1.11459.\n", - "The iter is 60, loss is -1.11423.\n", - "The iter is 61, loss is -1.11419.\n", - "The iter is 62, loss is -1.11447.\n", - "The iter is 63, loss is -1.11497.\n", - "The iter is 64, loss is -1.11556.\n", - "The iter is 65, loss is -1.11611.\n", - "The iter is 66, loss is -1.11652.\n", - "The iter is 67, loss is -1.11673.\n", - "The iter is 68, loss is -1.11674.\n", - "The iter is 69, loss is -1.11661.\n", - "The iter is 70, loss is -1.11640.\n", - "The iter is 71, loss is -1.11620.\n", - "The iter is 72, loss is -1.11605.\n", - "The iter is 73, loss is -1.11601.\n", - "The iter is 74, loss is -1.11608.\n", - "The iter is 75, loss is -1.11622.\n", - "The iter is 76, loss is -1.11639.\n", - "The iter is 77, loss is -1.11656.\n", - "The iter is 78, loss is -1.11669.\n", - "The iter is 79, loss is -1.11675.\n", - "The iter is 80, loss is -1.11675.\n", - "The iter is 81, loss is -1.11671.\n", - "The iter is 82, loss is -1.11664.\n", - "The iter is 83, loss is -1.11658.\n", - "The iter is 84, loss is -1.11654.\n", - "The iter is 85, loss is -1.11654.\n", - "The iter is 86, loss is -1.11656.\n", - "The iter is 87, loss is -1.11661.\n", - "The iter is 88, loss is -1.11667.\n", - "The iter is 89, loss is -1.11672.\n", - "The iter is 90, loss is -1.11675.\n", - "The iter is 91, loss is -1.11676.\n", - "The iter is 92, loss is -1.11675.\n", - "The iter is 93, loss is -1.11673.\n", - "The iter is 94, loss is -1.11671.\n", - "The iter is 95, loss is -1.11669.\n", - "The iter is 96, loss is -1.11669.\n", - "The iter is 97, loss is -1.11669.\n", - "The iter is 98, loss is -1.11671.\n", - "The iter is 99, loss is -1.11673.\n", - "The theoretical value is -1.11675.\n" + "INFO:root:\n", + "#######################################\n", + "VQE (Ground State)\n", + "#######################################\n", + "INFO:root:Number of qubits: 4\n", + "INFO:root:Ansatz: HardwareEfficient\n", + "INFO:root:Optimizer: Adam\n", + "INFO:root:\tlearning_rate: 0.5\n", + "INFO:root:\n", + "Optimization:\n", + "INFO:root:\tItr 0, loss=-0.26383.\n", + "INFO:root:\tItr 10, loss=-1.02679.\n", + "INFO:root:\tItr 20, loss=-1.08981.\n", + "INFO:root:\tItr 30, loss=-1.12582.\n", + "INFO:root:\tItr 40, loss=-1.13435.\n", + "INFO:root:\tItr 50, loss=-1.13531.\n", + "INFO:root:\tItr 60, loss=-1.13634.\n", + "INFO:root:\tItr 70, loss=-1.13713.\n", + "INFO:root:Optimization converged after 73 iterations.\n", + "INFO:root:The final loss = -1.13703.\n" ] } ], "source": [ - "# use paddlepaddle's optimizer\n", - "import paddle\n", - "\n", - "optimizer = paddle.optimizer.Adam(parameters=hartreefock_ansatz.parameters(), learning_rate=0.1)\n", + "# We choose `Adam` optimizer in PaddlePaddle\n", + "from paddle.optimizer import Adam\n", "\n", - "# prepare the initial quantum state, e.g. |1100>\n", - "init_state = pq.state.computational_basis(n_qubits, 12)\n", - "\n", - "# define the optimization steps\n", - "num_itr = 100\n", - "for itr in range(0, num_itr):\n", - " # run quantum circuit to arrive at the final state\n", - " state = hartreefock_ansatz(init_state)\n", - " # calculate loss\n", - " loss = loss_fn(state)\n", - " # backpropagate the gradient\n", - " loss.backward()\n", - " # update the ansatz's parameter\n", - " optimizer.minimize(loss)\n", - " # clear current gradient\n", - " optimizer.clear_grad()\n", - " print(f\"The iter is {itr:3d}, loss is {loss.item():3.5f}.\")\n", - "print(\"The theoretical value is -1.11675.\")" + "solver = pq_qchem.GroundStateSolver(Adam, num_iterations=100, tol=1e-5, save_every=10)\n", + "e, psi = solver.solve(mol, cir, learning_rate=0.5)" ] }, { @@ -416,11 +186,8 @@ } ], "metadata": { - "interpreter": { - "hash": "f7cfecff1ef1940b21a48efa1b88278bb096bd916f13c2df11af4810c38b47e1" - }, "kernelspec": { - "display_name": "Python 3.8.0 ('pq')", + "display_name": "modellib", "language": "python", "name": "python3" }, @@ -434,7 +201,12 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.0" + "version": "3.7.15" + }, + "vscode": { + "interpreter": { + "hash": "8f24120f890011f53feb4ed62c47961d8565ec1de8b7cb23548c15bd6da8f2d2" + } } }, "nbformat": 4, diff --git a/introduction/PaddleQuantum_Tutorial_CN.ipynb b/introduction/PaddleQuantum_Tutorial_CN.ipynb index 45c5123..deb27c2 100644 --- a/introduction/PaddleQuantum_Tutorial_CN.ipynb +++ b/introduction/PaddleQuantum_Tutorial_CN.ipynb @@ -121,7 +121,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "接着我们安装 Paddle Quantum 包,用户可以直接通过 `pip install paddle-quantum` 完成安装。关于本地安装方式,用户可以通过 Terminal 界面使用 git指令 `git clone http://github.com/PaddlePaddle/quantum` 或者直接下载 `zip` 压缩包,然后找到对应本地文件的路径输入 `cd quantum` 和 `pip install -e .` 完成安装。接着在 Terminal 界面输入`pip list`查看是否在正确的环境中安装完成。关于 git的使用和安装,请参考这篇 [教程](https://git-scm.com/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E5%AE%89%E8%A3%85-Git)。此外,如果你需要更多的关于安装 Paddle Quantum 的帮助,可以参考我们的 [Github链接](https://github.com/PaddlePaddle/Quantum) 或者通过 Github Issues联系我们。" + "接着我们安装 Paddle Quantum 包,用户可以直接通过 `pip install paddle-quantum` 完成安装。关于本地安装方式,用户可以通过 Terminal 界面使用 git指令 `git clone http://github.com/PaddlePaddle/quantum` 或者直接下载 `zip` 压缩包,然后找到对应本地文件的路径输入 `cd quantum` 和 `pip install -e .` 完成安装。接着在 Terminal 界面输入`pip list`查看是否在正确的环境中安装完成。关于 git的使用和安装,请参考这篇 [教程](https://git-scm.com/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E5%AE%89%E8%A3%85-Git)。此外,如果你需要更多的关于安装 Paddle Quantum 的帮助,可以参考我们的 [GitHub链接](https://github.com/PaddlePaddle/Quantum) 或者通过 GitHub Issues联系我们。" ] }, { diff --git a/paddle_quantum/__init__.py b/paddle_quantum/__init__.py index 342a18a..1e2d697 100644 --- a/paddle_quantum/__init__.py +++ b/paddle_quantum/__init__.py @@ -34,7 +34,7 @@ from . import operator from . import base from . import dataset -from . import finance +#from . import finance from . import fisher from . import gradtool from . import hamiltonian @@ -44,6 +44,7 @@ from . import shadow from . import trotter from . import visual +from . import qchem name = 'paddle_quantum' -__version__ = '2.2.2' +__version__ = '2.3.0' diff --git a/paddle_quantum/ansatz/circuit.py b/paddle_quantum/ansatz/circuit.py index 88e2dea..4a4f490 100644 --- a/paddle_quantum/ansatz/circuit.py +++ b/paddle_quantum/ansatz/circuit.py @@ -31,7 +31,8 @@ from ..gate import QAOALayer from ..gate import AmplitudeEncoding from ..channel import BitFlip, PhaseFlip, BitPhaseFlip, AmplitudeDamping, GeneralizedAmplitudeDamping, PhaseDamping -from ..channel import Depolarizing, PauliChannel, ResetChannel, ThermalRelaxation, MixedUnitaryChannel, KrausRepr +from ..channel import Depolarizing, GeneralizedDepolarizing, PauliChannel, ResetChannel, ThermalRelaxation +from ..channel import MixedUnitaryChannel, KrausRepr from ..intrinsic import _get_float_dtype from ..state import zero_state from ..operator import Collapse @@ -1273,7 +1274,7 @@ def generalized_amplitude_damping( num_qubits: Total number of qubits. Defaults to None. """ self.__num_qubits_update(qubits_idx) - self.append(GeneralizedAmplitudeDamping(gamma, qubits_idx, + self.append(GeneralizedAmplitudeDamping(gamma, prob, qubits_idx, self.num_qubits if num_qubits is None else num_qubits)) def phase_damping( @@ -1304,7 +1305,22 @@ def depolarizing( """ self.__num_qubits_update(qubits_idx) self.append(Depolarizing(prob, qubits_idx, - self.num_qubits if num_qubits is None else num_qubits)) + self.num_qubits if num_qubits is None else num_qubits)) + + def generalized_depolarizing( + self, prob: Union[paddle.Tensor, float], qubits_idx: Union[Iterable[int], int, str], + num_qubits: int = None + ) -> None: + r"""Add a general depolarizing channel. + + Args: + prob: Probabilities corresponding to the Pauli basis. + qubits_idx: Indices of the qubits on which the channel is applied. + num_qubits: Total number of qubits. Defaults to None. + """ + self.__num_qubits_update(qubits_idx) + self.append(PauliChannel(prob, qubits_idx, + self.num_qubits if num_qubits is None else num_qubits)) def pauli_channel( self, prob: Union[paddle.Tensor, float], qubits_idx: Union[Iterable[int], int, str] = 'full', diff --git a/paddle_quantum/biocomputing/__init__.py b/paddle_quantum/biocomputing/__init__.py new file mode 100644 index 0000000..b9c7098 --- /dev/null +++ b/paddle_quantum/biocomputing/__init__.py @@ -0,0 +1,22 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +module for bio-computing +""" + +from .protein import Protein +from .algorithm import ProteinFoldingSolver +from .visualize import visualize_protein_structure diff --git a/paddle_quantum/biocomputing/algorithm.py b/paddle_quantum/biocomputing/algorithm.py new file mode 100644 index 0000000..e3881bf --- /dev/null +++ b/paddle_quantum/biocomputing/algorithm.py @@ -0,0 +1,157 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +VQE algorithm to solve protein folding problem +""" + +from typing import Tuple, List +import logging +import math +import numpy as np +import paddle +from paddle.optimizer import Optimizer +from paddle_quantum.ansatz import Circuit +from paddle_quantum import Hamiltonian +from paddle_quantum.loss import ExpecVal +from paddle_quantum.state import State, computational_basis +from paddle_quantum.qchem.algorithm import VQESolver +from paddle_quantum.biocomputing import Protein + +__all__ = ["ProteinFoldingSolver"] + + +# TODO: add this function to a method of State! +def cvar_expectation(psi: State, h: Hamiltonian, alpha: float) -> paddle.Tensor: + r"""Calculate CVaR expectation value + + .. math:: + + \sum_{i<=j} p_i \le alpha + (1/\alpha) * (\sum_{i 0 and alpha <= 1, "alpha must in (0, 1]." + + if math.isclose(alpha, 1.0): + return ExpecVal(h)(psi) + + probabilities = paddle.real(paddle.multiply(psi.data.conj(), psi.data)) + num_qubits = psi.num_qubits + energies = [] + for i in range(2**num_qubits): + phi = computational_basis(num_qubits, i) + energies.append(phi.expec_val(h)) + results = sorted(zip(energies, probabilities)) + + running_probability = 0 + cvar_val0 = 0 + cutoff_idx = 0 + for e, p in results: + if running_probability >= alpha: + break + running_probability += p + cvar_val0 += e*p + cutoff_idx += 1 + Hj = results[cutoff_idx][0] + return (1/alpha)*(cvar_val0 + Hj*(alpha - running_probability)) + + +class ProteinFoldingSolver(VQESolver): + def __init__( + self, + penalty_factors: List[float], + alpha: float, + optimizer: Optimizer, + num_iterations: int, + tol: float = 1e-8, + save_every: int = 1, + ) -> None: + r""" + Args: + penalty_factors: penalty factor ``[lambda0, lambda1]`` used in building the protein's Hamiltonian. + alpha: the cutoff probability for CVaR expectation value calculation. + optimizer: paddle's optimizer used to perform parameter update. + num_iterations : number of VQE iterations. + tol: convergence criteria. + save_every : number of steps between two recorded VQE loss. + """ + super().__init__(optimizer, num_iterations, tol, save_every) + self.lambda0 = penalty_factors[0] + self.lambda1 = penalty_factors[1] + self.alpha = alpha + + def solve( + self, + protein: Protein, + ansatz: Circuit, + **optimizer_kwargs + ) -> Tuple[float, str]: + r"""Run VQE to calculate the structure of the protein that satisfies various constraints. + + Args: + protein: the protein structure to be optimized. + ansatz: the quantum circuit represents the unitary transformation. + optimizer_kwargs: see PaddlePaddle's optimizer API for details https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/optimizer/Overview_cn.html (Chinese) or https://www.paddlepaddle.org.cn/documentation/docs/en/api/paddle/optimizer/Optimizer_en.html (English). + + Returns: + A tuple contains the final loss value and optimal basis state. + """ + h = protein.get_protein_hamiltonian(self.lambda0, self.lambda1, 1.0) + + logging.info("\n#######################################\nVQE (Protein Folding)\n#######################################") + logging.info(f"Number of qubits: {h.n_qubits:d}") + logging.info(f"Ansatz: {ansatz.__class__.__name__:s}") + logging.info(f"Optimizer: {self.optimizer.__name__:s}") + + optimizer = self.optimizer(parameters=ansatz.parameters(), **optimizer_kwargs) + logging.info(f"\tlearning_rate: {optimizer.get_lr()}") + + logging.info("\nOptimization:") + loss0 = paddle.to_tensor(np.inf) + for n in range(self.num_iters): + intm_state: State = ansatz() + loss = cvar_expectation(intm_state, h, self.alpha) + + with paddle.no_grad(): + if n % self.save_every == 0: + loss_v = loss.detach().item() + logging.info(f"\tItr {n:d}, loss={loss_v:.5f}.") + # pay attention to the order of x and y !!! + # see https://www.paddlepaddle.org.cn/documentation/docs/en/api/paddle/isclose_en.html for details. + if paddle.isclose(loss0, loss, atol=self.tol).item(): + logging.info(f"Optimization converged after {n:d} iterations.") + break + + loss.backward() + optimizer.step() + optimizer.clear_grad() + loss0 = loss + + with paddle.no_grad(): + final_state: State = ansatz() + final_loss = cvar_expectation(final_state, h, self.alpha) + logging.info(f"The final loss = {final_loss.item():.5f}.") + results = final_state.measure() + sol_str = max(results.items(), key=lambda x: x[1])[0] + logging.info(f"The solution is {sol_str}.") + return final_loss.item(), sol_str diff --git a/paddle_quantum/biocomputing/data_loader.py b/paddle_quantum/biocomputing/data_loader.py new file mode 100644 index 0000000..31930f6 --- /dev/null +++ b/paddle_quantum/biocomputing/data_loader.py @@ -0,0 +1,59 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +Loads the energy matrix from the Miyazawa-Jernigan potential file. +""" + +import os +from typing import Tuple, List +import logging +import numpy as np + +__all__ = ["load_energy_matrix_file"] + +MJ_POTENTIAL_FILE_PATH = os.path.realpath( + os.path.dirname(__file__) +) + + +def load_energy_matrix_file() -> Tuple[np.ndarray, List[str]]: + r"""Returns the energy matrix from the Miyazawa-Jernigan potential file. + + Note: + This is an internal function, user does not intended to call it directly. + """ + logging.info("! Use Miyazawa-Jernigan potential for interaction between amino acides in protein") + matrix = np.loadtxt(f"{MJ_POTENTIAL_FILE_PATH}/mj_matrix.txt", dtype=str) + energy_matrix = _parse_energy_matrix(matrix) + symbols = list(matrix[0, :]) + return energy_matrix, symbols + + +def _parse_energy_matrix(matrix: np.ndarray) -> np.ndarray: + r""" + Parses a matrix loaded from the Miyazawa-Jernigan potential file. + + Note: + This is an internal function, user does not intended to call it directly. + """ + energy_matrix = np.zeros((np.shape(matrix)[0], np.shape(matrix)[1])) + for row in range(1, np.shape(matrix)[0]): + for col in range(row - 1, np.shape(matrix)[1]): + energy_matrix[row, col] = float(matrix[row, col]) + energy_matrix = energy_matrix[ + 1:, + ] + return energy_matrix diff --git a/paddle_quantum/biocomputing/mj_matrix.txt b/paddle_quantum/biocomputing/mj_matrix.txt new file mode 100644 index 0000000..7d97c29 --- /dev/null +++ b/paddle_quantum/biocomputing/mj_matrix.txt @@ -0,0 +1,21 @@ + C M F I L V W Y A G T S N Q D E H R K P +-5.44e+00 -4.99e+00 -5.80e+00 -5.50e+00 -5.83e+00 -4.96e+00 -4.95e+00 -4.16e+00 -3.57e+00 -3.16e+00 -3.11e+00 -2.86e+00 -2.59e+00 -2.85e+00 -2.41e+00 -2.27e+00 -3.60e+00 -2.57e+00 -1.95e+00 -3.07e+00 +0.00e+00 -5.46e+00 -6.56e+00 -6.02e+00 -6.41e+00 -5.32e+00 -5.55e+00 -4.91e+00 -3.94e+00 -3.39e+00 -3.51e+00 -3.03e+00 -2.95e+00 -3.30e+00 -2.57e+00 -2.89e+00 -3.98e+00 -3.12e+00 -2.48e+00 -3.45e+00 +0.00e+00 0.00e+00 -7.26e+00 -6.84e+00 -7.28e+00 -6.29e+00 -6.16e+00 -5.66e+00 -4.81e+00 -4.13e+00 -4.28e+00 -4.02e+00 -3.75e+00 -4.10e+00 -3.48e+00 -3.56e+00 -4.77e+00 -3.98e+00 -3.36e+00 -4.25e+00 +0.00e+00 0.00e+00 0.00e+00 -6.54e+00 -7.04e+00 -6.05e+00 -5.78e+00 -5.25e+00 -4.58e+00 -3.78e+00 -4.03e+00 -3.52e+00 -3.24e+00 -3.67e+00 -3.17e+00 -3.27e+00 -4.14e+00 -3.63e+00 -3.01e+00 -3.76e+00 +0.00e+00 0.00e+00 0.00e+00 0.00e+00 -7.37e+00 -6.48e+00 -6.14e+00 -5.67e+00 -4.91e+00 -4.16e+00 -4.34e+00 -3.92e+00 -3.74e+00 -4.04e+00 -3.40e+00 -3.59e+00 -4.54e+00 -4.03e+00 -3.37e+00 -4.20e+00 +0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 -5.52e+00 -5.18e+00 -4.62e+00 -4.04e+00 -3.38e+00 -3.46e+00 -3.05e+00 -2.83e+00 -3.07e+00 -2.48e+00 -2.67e+00 -3.58e+00 -3.07e+00 -2.49e+00 -3.32e+00 +0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 -5.06e+00 -4.66e+00 -3.82e+00 -3.42e+00 -3.22e+00 -2.99e+00 -3.07e+00 -3.11e+00 -2.84e+00 -2.99e+00 -3.98e+00 -3.41e+00 -2.69e+00 -3.73e+00 +0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 -4.17e+00 -3.36e+00 -3.01e+00 -3.01e+00 -2.78e+00 -2.76e+00 -2.97e+00 -2.76e+00 -2.79e+00 -3.52e+00 -3.16e+00 -2.60e+00 -3.19e+00 +0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 -2.72e+00 -2.31e+00 -2.32e+00 -2.01e+00 -1.84e+00 -1.89e+00 -1.70e+00 -1.51e+00 -2.41e+00 -1.83e+00 -1.31e+00 -2.03e+00 +0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 -2.24e+00 -2.08e+00 -1.82e+00 -1.74e+00 -1.66e+00 -1.59e+00 -1.22e+00 -2.15e+00 -1.72e+00 -1.15e+00 -1.87e+00 +0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 -2.12e+00 -1.96e+00 -1.88e+00 -1.90e+00 -1.80e+00 -1.74e+00 -2.42e+00 -1.90e+00 -1.31e+00 -1.90e+00 +0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 -1.67e+00 -1.58e+00 -1.49e+00 -1.63e+00 -1.48e+00 -2.11e+00 -1.62e+00 -1.05e+00 -1.57e+00 +0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 -1.68e+00 -1.71e+00 -1.68e+00 -1.51e+00 -2.08e+00 -1.64e+00 -1.21e+00 -1.53e+00 +0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 -1.54e+00 -1.46e+00 -1.42e+00 -1.98e+00 -1.80e+00 -1.29e+00 -1.73e+00 +0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 -1.21e+00 -1.02e+00 -2.32e+00 -2.29e+00 -1.68e+00 -1.33e+00 +0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 -9.10e-01 -2.15e+00 -2.27e+00 -1.80e+00 -1.26e+00 +0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 -3.05e+00 -2.16e+00 -1.35e+00 -2.25e+00 +0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 -1.55e+00 -5.90e-01 -1.70e+00 +0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 -1.20e-01 -9.70e-01 +0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 -1.75e+00 diff --git a/paddle_quantum/biocomputing/operators.py b/paddle_quantum/biocomputing/operators.py new file mode 100644 index 0000000..c38ae88 --- /dev/null +++ b/paddle_quantum/biocomputing/operators.py @@ -0,0 +1,106 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +various indicators used to build the protein Hamiltonian +""" + + +from typing import Dict, Tuple, Optional, List +from openfermion import QubitOperator + +__all__ = ["edge_direction_indicator", "contact_indicator", "backwalk_indicator"] + + +def edge_direction_indicator( + edge: Tuple[int], + affected_qubits: Optional[List[int]] = None, + direction: Optional[int] = None +) -> Tuple[float, Dict]: + r"""Calculate the direction indicate operator for a given edge at given + affected qubits. + + .. math:: + + I_0(e)=(1-q_0)(1-q_1) \\ + I_1(e)=(1-q_0)q_1 \\ + I_2(e)=q_0(1-q_1) \\ + I_3(e)=q_0q_1 \\ + satisfies \sum_{k} I_k(e) == 1 + + Args: + edge: Edge index in protein's directed graph. + affected_qubits: The indices of qubits used to encode the indicator. + direction: Direction of edge in the diamond lattice, valid values 0, 1, 2, 3. + + Returns: + A tuple contains the sign and indicator operator corresponds to that edge. + """ + indicators = {} + if isinstance(direction, int): + for i in range(4): + if i != direction: + indicators[i] = QubitOperator("", 0.0) + else: + indicators[i] = QubitOperator("", 1.0) + elif isinstance(affected_qubits, list): + qa, qb = affected_qubits + one_plus_za = 0.5*QubitOperator("") + 0.5*QubitOperator(f"Z{qa:d}") + one_plus_zb = 0.5*QubitOperator("") + 0.5*QubitOperator(f"Z{qb:d}") + one_minus_za = 0.5*QubitOperator("") - 0.5*QubitOperator(f"Z{qa:d}") + one_minus_zb = 0.5*QubitOperator("") - 0.5*QubitOperator(f"Z{qb:d}") + indicators[0] = (one_plus_za*one_plus_zb) + indicators[1] = (one_plus_za*one_minus_zb) + indicators[2] = (one_minus_za*one_plus_zb) + indicators[3] = (one_minus_za*one_minus_zb) + else: + raise ValueError("One of the `affected_qubits` and `direction` kwargs must be specified.") + return (-1)**edge[0], indicators + + +def contact_indicator(qindex: int) -> QubitOperator: + r"""The indicator which indicates whether two nodes in the protein are contact. + + .. math:: + + qindex = contactor_start + index_of_contact_pair + + Args: + qindex : index of qubit used as indicator of whether two nodes in the protein chain contact. + + Returns: + Contact indicator in QubitOperator form. + """ + return QubitOperator("", 0.5) - QubitOperator(f"Z{qindex:d}", 0.5) + + +def backwalk_indicator(e0_attrs: Dict, e1_attrs: Dict) -> QubitOperator: + r"""Indicator of whether two consecutive bonds in protein overlap. + + .. math:: + + \sum_{a=0}^3 I_a(e_{i})I_a(e_{i+1}) + + Args: + e0_attrs: Attributes of e0 edge. + e1_attrs : Attributes of e1 (edge next to e0) edge. + + Returns: + Backwalk indicator in QubitOperator form. + """ + h = 0.0 + for i in range(4): + h += e0_attrs[i]*e1_attrs[i] + return h diff --git a/paddle_quantum/biocomputing/protein.py b/paddle_quantum/biocomputing/protein.py new file mode 100644 index 0000000..e8a111b --- /dev/null +++ b/paddle_quantum/biocomputing/protein.py @@ -0,0 +1,214 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +a class that holds all the information of the protein model +""" + +from typing import List, Tuple, Dict, Optional +import logging +import networkx as nx +from openfermion import QubitOperator +from paddle_quantum import Hamiltonian +from paddle_quantum.biocomputing.data_loader import load_energy_matrix_file +from paddle_quantum.biocomputing.operators import edge_direction_indicator, contact_indicator, backwalk_indicator + +__all__ = ["Protein"] + +AA_SYMBOLS = [ + "C", "M", "F", "I", "L", "V", "W", "Y", "A", "G", "T", "S", "N", "Q", + "D", "E", "H", "R", "K", "P" +] + + +def _check_valid_aa_seq(aa_seq: str) -> bool: + r"""check if a given amino acid sequence is valid. + + Note: + This is an internal function, users don't intended to use it directly. + + Args: + aa_seq: Amino acides in the protein. + + Return: + If input is a valid amino acide sequence, will return True, otherwise, will return False. + """ + for a in aa_seq: + if a not in AA_SYMBOLS: + raise ValueError(f"Input amino acid symbol must in {AA_SYMBOLS}, get {a:s}.") + num_aa = len(aa_seq) + assert num_aa >= 2, "The smallest allowed number of amino acids in protein is 2." + return True + + +def _generate_valid_contact_pairs(num_aa: int) -> List[Tuple[int]]: + r"""A function that generate the potential contact node pairs. Then number of + nodes in a protein is given by ``num_aa`` . + + Note: + This is an internal function, users don't intended to use it directly. + + Args: + num_aa: Number of amino acides in the protein. + + Return: + Possible contact pairs for the given protein. + """ + pairs = [] + for i in range(num_aa): + i1 = i + 5 + if i1 <= num_aa: + pairs.extend((i, j) for j in range(i1, num_aa) if (j-i) % 2 == 1) + return pairs + + +class Protein(nx.Graph): + r""" + Protein object will build a protein from given amino acides sequence. + The 3D structure of the protein is build on a diamond lattice, each bond is + mapped to an edge in the lattice. We can get the spatial direction of the + bonds and the distance between the two amino acides from it. + + Args: + aa_seqs: an ordered string in which each symbol represents an amino acide (aa). + fixed_bond_directions: a list contains the directions of prefixed + bonds, they are represented by a (edge, direction) pairs, e.g. (0, 1)->"10" means the + bond between 0 and 1 is along the 2nd direction. Default is None, which means no bond + is fixed a prior. + contact_pairs: a list of potentially contact amino acides pairs. Default + is None and will use all the valid pairs generated by ``_generate_valid_contact_pairs`` . + + """ + def __init__( + self, + aa_seqs: str, + fixed_bond_directions: Optional[Dict[Tuple, int]] = None, + contact_pairs: Optional[List[Tuple[int]]] = None, + ) -> None: + super().__init__() + _check_valid_aa_seq(aa_seqs) + + num_aa = len(aa_seqs) + config_qblock_idx = 0 + if fixed_bond_directions is None: + fixed_bond_directions = {} + + for e_a, e_b in zip(range(num_aa-1), range(1, num_aa)): + direction = fixed_bond_directions.get((e_a, e_b)) + if isinstance(direction, int): + sign, indicator = edge_direction_indicator((e_a, e_b), direction=direction) + else: + sign, indicator = edge_direction_indicator((e_a, e_b), [2*config_qblock_idx, 2*config_qblock_idx+1]) + config_qblock_idx += 1 + self.add_edge(e_a, e_b, sign=sign, indicator=indicator) + self.nodes[e_a]["symbol"] = aa_seqs[e_a] + self.nodes[e_b]["symbol"] = aa_seqs[e_b] + self._num_config_qubits = 2*config_qblock_idx + + if contact_pairs is None: + contact_pairs = _generate_valid_contact_pairs(num_aa) + self._num_contact_qubits = len(contact_pairs) + + logging.info("\n#######################################\nProtein (lattice model)\n#######################################") + logging.info(f"Symbols: {'-'.join(aa_seqs):s}") + logging.info("Lattice: diamond lattice") + energies, _ = load_energy_matrix_file() + self._energy_matrix = energies + logging.info("Assumed contact pairs [energy]:") + contact_energies = {} + for p, q in contact_pairs: + i, j = sorted([AA_SYMBOLS.index(aa_seqs[p]), AA_SYMBOLS.index(aa_seqs[q])]) + en = energies[i, j] + contact_energies[(p, q)] = en + logging.info(f"\t({p:d},{q:d}) [{en:.5f}]") + self.contact_energies = contact_energies + + @property + def num_config_qubits(self): + return self._num_config_qubits + + @property + def num_contact_qubits(self): + return self._num_contact_qubits + + @property + def num_qubits(self): + return self.num_config_qubits + self.num_contact_qubits + + def distance_operator(self, p: int, q: int) -> QubitOperator: + r"""Distance between p-th and q-th nodes in the protein graph. + + Args: + p: index of node p + q: index of node q + + Returns: + The operator. + """ + assert p != q, "The node indices shouldn't be equal." + p, q = sorted([p, q]) # make sure that p Hamiltonian: + r""" + The Hamiltonian used in VQE algorithm to solve protein folding problem (will take into account the first and + second neighbor interactions). + + .. math:: + + \begin{array}{rcl} + \hat{H} & = &\lambda_0\hat{H}_{backward} + \sum_{i=0}^{N-1}\sum_{j}q_{ij}(\epsilon_{ij}+\lambda_1(d(i,j)-1))\\ + & & +\lambda_1(4-d(i,neighbor(j)-d(neighbor(i),j)))\\ + \text{where} & & \hat{H}_{backward}=\sum_{e_{i}} (-1)^{e_i[0]+e_{i+1}[0]} f_a(e_i)f_a(e_{i+1})j\in\{k|k>=i+5, (k-i)%2==1\} + \end{array} + + Args: + protein: protein for which to build the Hamiltonian. + lambda0: the penalty factor for the backwalk constraint. + lambda1: the penalty factor for the contaction constraints. + + Returns: + Hamiltonian. + """ + h = 0*QubitOperator("") + contact_edges = sorted(self.contact_energies.keys()) + for (p, q), e_pq in self.contact_energies.items(): + qindex = self.num_config_qubits + contact_edges.index((p, q)) + I_pq = contact_indicator(qindex) + h += I_pq * (energy_multiplier*e_pq + lambda1*(self.distance_operator(p, q) - 1)) + # restrict the neighbor distance + # use squared constraint to restrict the positivity. + for r in self.neighbors(p): + e2_rq = self._energy_matrix[AA_SYMBOLS.index(self.nodes[r]["symbol"]), AA_SYMBOLS.index(self.nodes[q]["symbol"])] + h += I_pq * (energy_multiplier*e2_rq + lambda1*(2 - self.distance_operator(r, q))**2) + for s in self.neighbors(q): + e2_rq = self._energy_matrix[AA_SYMBOLS.index(self.nodes[p]["symbol"]), AA_SYMBOLS.index(self.nodes[s]["symbol"])] + h += I_pq * (energy_multiplier*e2_rq + lambda1*(2 - self.distance_operator(p, s))**2) + + # forbid backwalk + edges = sorted(self.edges) + for i in range(len(edges) - 1): + e0_attr = self.edges[edges[i]]["indicator"] + e1_attr = self.edges[edges[i+1]]["indicator"] + h += lambda0*backwalk_indicator(e0_attr, e1_attr) + + return Hamiltonian.from_qubit_operator(h) diff --git a/paddle_quantum/biocomputing/visualize.py b/paddle_quantum/biocomputing/visualize.py new file mode 100644 index 0000000..fb4023b --- /dev/null +++ b/paddle_quantum/biocomputing/visualize.py @@ -0,0 +1,76 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +visualize the protein structure +""" + +from typing import List, Optional +import numpy as np +import matplotlib.pyplot as plt + +__all__ = ["visualize_protein_structure"] + +COORDINATE = (1.0/np.sqrt(3)) * np.asarray( + [[-1, 1, 1], [1, 1, -1], [-1, -1, -1], [1, -1, 1]] +) + + +def visualize_protein_structure( + aa_seq: List[str], + bond_directions: List[int], + view_angles: Optional[List[float]] = None +): + r""" + Args: + aa_seq: Amino acides sequence. + bond_directions: Direction of bonds connect neighboring amino acides. + view_angles: horizontal and azimuthal angles for the final output image. + """ + if view_angles is None: + view_angles = [0.0, 0.0] + + num_aa = len(aa_seq) + relative_coords = np.zeros((num_aa, 3)) + for i in range(1, num_aa): + relative_coords[i, :] += (-1)**i * COORDINATE[bond_directions[i-1]] + aa_coords = relative_coords.cumsum(axis=0) + + x_coords = aa_coords[:, 0] + y_coords = aa_coords[:, 1] + z_coords = aa_coords[:, 2] + + fig = plt.figure() + ax = fig.add_subplot(projection="3d") + ax.set_box_aspect([1, 1, 1]) + for i, aa_label in enumerate(aa_seq): + ax.text( + x_coords[i], + y_coords[i], + z_coords[i], + aa_label, + size=10, + zorder=10, + color="k" + ) + ax.plot(x_coords, y_coords, z_coords) + ax.scatter(x_coords, y_coords, z_coords, s=500) + + ax.set_xlabel("x") + ax.set_ylabel("y") + ax.set_zlabel("z") + ax.set_title("3D structure of protein") + ax.view_init(elev=view_angles[0], azim=view_angles[1]) + fig.savefig(f"{''.join(aa_seq)}_3d_structure.jpg") diff --git a/paddle_quantum/channel/__init__.py b/paddle_quantum/channel/__init__.py index f084fbf..9a900ec 100644 --- a/paddle_quantum/channel/__init__.py +++ b/paddle_quantum/channel/__init__.py @@ -18,17 +18,6 @@ """ from .base import Channel -from .common import BitFlip -from .common import PhaseFlip -from .common import BitPhaseFlip -from .common import AmplitudeDamping -from .common import GeneralizedAmplitudeDamping -from .common import PhaseDamping -from .common import Depolarizing -from .common import PauliChannel -from .common import ResetChannel -from .common import ThermalRelaxation -from .common import MixedUnitaryChannel -from .custom import KrausRepr -from .custom import ChoiRepr -from .custom import StinespringRepr +from .common import * +from .custom import ChoiRepr, KrausRepr, StinespringRepr +from .representation import * diff --git a/paddle_quantum/channel/base.py b/paddle_quantum/channel/base.py index 33bdcd2..fc0d304 100644 --- a/paddle_quantum/channel/base.py +++ b/paddle_quantum/channel/base.py @@ -17,11 +17,14 @@ The source file of the basic class for the quantum channels. """ -from typing import Any, Optional -import paddle_quantum +from typing import Any, Optional, List +import paddle +from ..base import Operator, get_backend, get_dtype +from ..backend import Backend -class Channel(paddle_quantum.Operator): + +class Channel(Operator): r"""Basic class for quantum channels. Args: @@ -32,7 +35,7 @@ class Channel(paddle_quantum.Operator): auto-generated. If ``None``, prefix name will be snake cased class name. Defaults to ``None``. """ def __init__( - self, backend: paddle_quantum.Backend = None, dtype: str = None, name_scope: str = None + self, backend: Backend = None, dtype: str = None, name_scope: str = None ) -> None: super().__init__(backend, dtype, name_scope) @@ -49,6 +52,51 @@ def __setattr__(self, name, value): super().__setattr__(name, value) if isinstance(value, Channel): if value.backend is None: - value.backend = paddle_quantum.get_backend() if self.backend is None else self.backend + value.backend = get_backend() if self.backend is None else self.backend if value.dtype is None: - value.dtype = paddle_quantum.get_dtype() if self.dtype is None else self.dtype + value.dtype = get_dtype() if self.dtype is None else self.dtype + + @property + def choi_repr(self) -> paddle.Tensor: + r"""Choi representation of a channel + + Returns: + a tensor with shape :math:`[d_\text{out}^2, d_\text{in}^2]`, where :math:`d_\text{in/out}` is the input/output + dimension of this channel + + Raises: + Cannot return the Choi representation of a general channel: use ChoiRepr instead to specify your channel + + """ + raise NotImplementedError( + "Cannot return the Choi representation of a general channel: use ChoiRepr instead to specify your channel") + + @property + def kraus_repr(self) -> List[paddle.Tensor]: + r"""Kraus representation of a channel + + Returns: + a list of tensors with shape :math:`[d_\text{out}, d_\text{in}]`, where :math:`d_\text{in/out}` is the input/output + dimension of this channel + + Raises: + Cannot return the Kraus representation of a general channel: use KrausRepr instead to specify your channel + + """ + raise NotImplementedError( + "Cannot return the Kraus representation of a general channel: use KrausRepr instead to specify your channel") + + @property + def stinespring_repr(self) -> paddle.Tensor: + r"""Stinespring representation of a channel + + Returns: + a tensor with shape :math:`[r * d_\text{out}, d_\text{in}]`, where :math:`r` is the rank of this channel and + :math:`d_\text{in/out}` is the input/output dimension of this channel + + Raises: + Cannot return the Stinespring representation of a general channel: use StinespringRepr instead to specify your channel + + """ + raise NotImplementedError( + "Cannot return the Stinespring representation of a general channel: use StinespringRepr instead to specify your channel") diff --git a/paddle_quantum/channel/common.py b/paddle_quantum/channel/common.py index 3c9825a..31bdb3a 100644 --- a/paddle_quantum/channel/common.py +++ b/paddle_quantum/channel/common.py @@ -14,20 +14,18 @@ # limitations under the License. r""" -The source file of the classes for several quantum channel. +The source file of the classes for common quantum channels. """ import paddle -import paddle_quantum +from .custom import KrausRepr from ..intrinsic import _format_qubits_idx -from ..qinfo import kraus_oper_random -from .base import Channel -from . import functional -from ..backend import Backend +from ..qinfo import kraus_unitary_random +from .representation import * from typing import Union, Iterable -class BitFlip(Channel): +class BitFlip(KrausRepr): r"""A collection of bit flip channels. Such a channel's Kraus operators are @@ -46,19 +44,10 @@ def __init__( self, prob: Union[paddle.Tensor, float], qubits_idx: Union[Iterable[int], int, str] = 'full', num_qubits: int = None ): - super().__init__() - self.qubits_idx = _format_qubits_idx(qubits_idx, num_qubits) - self.prob = paddle.to_tensor(prob) if isinstance(prob, (int, float)) else prob + super().__init__(bit_flip_kraus(prob), qubits_idx, num_qubits, check_complete=False) + - def forward(self, state: paddle_quantum.State) -> paddle_quantum.State: - if self.backend == Backend.QuLeaf and state.backend == Backend.QuLeaf: - raise NotImplementedError - for qubit_idx in self.qubits_idx: - state = functional.bit_flip(state, self.prob, qubit_idx, self.dtype, self.backend) - return state - - -class PhaseFlip(Channel): +class PhaseFlip(KrausRepr): r"""A collection of phase flip channels. Such a channel's Kraus operators are @@ -77,19 +66,10 @@ def __init__( self, prob: Union[paddle.Tensor, float], qubits_idx: Union[Iterable[int], int, str] = 'full', num_qubits: int = None ): - super().__init__() - self.qubits_idx = _format_qubits_idx(qubits_idx, num_qubits) - self.prob = paddle.to_tensor(prob) if isinstance(prob, (int, float)) else prob - - def forward(self, state: paddle_quantum.State) -> paddle_quantum.State: - if self.backend == Backend.QuLeaf and state.backend == Backend.QuLeaf: - raise NotImplementedError - for qubit_idx in self.qubits_idx: - state = functional.phase_flip(state, self.prob, qubit_idx, self.dtype, self.backend) - return state + super().__init__(phase_flip_kraus(prob), qubits_idx, num_qubits, check_complete=False) -class BitPhaseFlip(Channel): +class BitPhaseFlip(KrausRepr): r"""A collection of bit phase flip channels. Such a channel's Kraus operators are @@ -108,19 +88,10 @@ def __init__( self, prob: Union[paddle.Tensor, float], qubits_idx: Union[Iterable[int], int, str] = 'full', num_qubits: int = None ): - super().__init__() - self.qubits_idx = _format_qubits_idx(qubits_idx, num_qubits) - self.prob = paddle.to_tensor(prob) if isinstance(prob, (int, float)) else prob + super().__init__(bit_phase_flip_kraus(prob), qubits_idx, num_qubits, check_complete=False) - def forward(self, state: paddle_quantum.State) -> paddle_quantum.State: - if self.backend == Backend.QuLeaf and state.backend == Backend.QuLeaf: - raise NotImplementedError - for qubit_idx in self.qubits_idx: - state = functional.bit_phase_flip(state, self.prob, qubit_idx, self.dtype, self.backend) - return state - -class AmplitudeDamping(Channel): +class AmplitudeDamping(KrausRepr): r"""A collection of amplitude damping channels. Such a channel's Kraus operators are @@ -147,19 +118,10 @@ def __init__( self, gamma: Union[paddle.Tensor, float], qubits_idx: Union[Iterable[int], int, str] = 'full', num_qubits: int = None ): - super().__init__() - self.qubits_idx = _format_qubits_idx(qubits_idx, num_qubits) - self.gamma = paddle.to_tensor(gamma) if isinstance(gamma, (int, float)) else gamma - - def forward(self, state: paddle_quantum.State) -> paddle_quantum.State: - if self.backend == Backend.QuLeaf and state.backend == Backend.QuLeaf: - raise NotImplementedError - for qubit_idx in self.qubits_idx: - state = functional.amplitude_damping(state, self.gamma, qubit_idx, self.dtype, self.backend) - return state + super().__init__(amplitude_damping_kraus(gamma), qubits_idx, num_qubits, check_complete=False) -class GeneralizedAmplitudeDamping(Channel): +class GeneralizedAmplitudeDamping(KrausRepr): r"""A collection of generalized amplitude damping channels. Such a channel's Kraus operators are @@ -185,21 +147,10 @@ def __init__( self, gamma: Union[paddle.Tensor, float], prob: Union[paddle.Tensor, float], qubits_idx: Union[Iterable[int], int, str] = 'full', num_qubits: int = None ): - super().__init__() - self.qubits_idx = _format_qubits_idx(qubits_idx, num_qubits) - self.prob = paddle.to_tensor(prob) if isinstance(prob, (int, float)) else prob - self.gamma = paddle.to_tensor(gamma) if isinstance(gamma, (int, float)) else gamma - - def forward(self, state: paddle_quantum.State) -> paddle_quantum.State: - if self.backend == Backend.QuLeaf and state.backend == Backend.QuLeaf: - raise NotImplementedError - for qubit_idx in self.qubits_idx: - state = functional.generalized_amplitude_damping( - state, self.gamma, self.prob, qubit_idx, self.dtype, self.backend) - return state + super().__init__(generalized_amplitude_damping_kraus(gamma, prob), qubits_idx, num_qubits, check_complete=False) -class PhaseDamping(Channel): +class PhaseDamping(KrausRepr): r"""A collection of phase damping channels. Such a channel's Kraus operators are @@ -226,19 +177,10 @@ def __init__( self, gamma: Union[paddle.Tensor, float], qubits_idx: Union[Iterable[int], int, str] = 'full', num_qubits: int = None ): - super().__init__() - self.qubits_idx = _format_qubits_idx(qubits_idx, num_qubits) - self.gamma = paddle.to_tensor(gamma) if isinstance(gamma, (int, float)) else gamma + super().__init__(phase_damping_kraus(gamma), qubits_idx, num_qubits, check_complete=False) - def forward(self, state: paddle_quantum.State) -> paddle_quantum.State: - if self.backend == Backend.QuLeaf and state.backend == Backend.QuLeaf: - raise NotImplementedError - for qubit_idx in self.qubits_idx: - state = functional.phase_damping(state, self.gamma, qubit_idx, self.dtype, self.backend) - return state - -class Depolarizing(Channel): +class Depolarizing(KrausRepr): r"""A collection of depolarizing channels. Such a channel's Kraus operators are @@ -266,19 +208,35 @@ def __init__( self, prob: Union[paddle.Tensor, float], qubits_idx: Union[Iterable[int], int, str] = 'full', num_qubits: int = None ): - super().__init__() - self.qubits_idx = _format_qubits_idx(qubits_idx, num_qubits) - self.prob = paddle.to_tensor(prob) if isinstance(prob, (int, float)) else prob + super().__init__(depolarizing_kraus(prob), qubits_idx, num_qubits, check_complete=False) + + +class GeneralizedDepolarizing(KrausRepr): + r"""A generalized depolarizing channel. + + Such a channel's Kraus operators are - def forward(self, state: paddle_quantum.State) -> paddle_quantum.State: - if self.backend == Backend.QuLeaf and state.backend == Backend.QuLeaf: - raise NotImplementedError - for qubit_idx in self.qubits_idx: - state = functional.depolarizing(state, self.prob, qubit_idx, self.dtype, self.backend) - return state + .. math:: + + E_0 = \sqrt{1-(D - 1)p/D} I, \text{ where } D = 4^n, \\ + E_k = \sqrt{p/D} \sigma_k, \text{ for } 0 < k < D. + + Args: + prob: probability :math:`p`. Its value should be in the range :math:`[0, 1]`. + qubits_idx: Indices of the qubits on which the channels act, the length of which is :math:`n`. + num_qubits: Total number of qubits. Defaults to ``None``. + + """ + def __init__( + self, prob: Union[paddle.Tensor, float], + qubits_idx: Union[Iterable[int], int, str], num_qubits: int = None + ): + num_acted_qubits = np.size(np.array(qubits_idx)).item() + super().__init__(generalized_depolarizing_kraus(prob, num_acted_qubits), + qubits_idx, num_qubits, check_complete=False) -class PauliChannel(Channel): +class PauliChannel(KrausRepr): r"""A collection of Pauli channels. Args: @@ -294,19 +252,10 @@ def __init__( self, prob: Union[paddle.Tensor, Iterable[float]], qubits_idx: Union[Iterable[int], int, str] = 'full', num_qubits: int = None ): - super().__init__() - self.qubits_idx = _format_qubits_idx(qubits_idx, num_qubits) - self.prob = paddle.to_tensor(prob) if isinstance(prob, Iterable) else prob - - def forward(self, state: paddle_quantum.State) -> paddle_quantum.State: - if self.backend == Backend.QuLeaf and state.backend == Backend.QuLeaf: - raise NotImplementedError - for qubit_idx in self.qubits_idx: - state = functional.pauli_channel(state, self.prob, qubit_idx, self.dtype, self.backend) - return state + super().__init__(pauli_kraus(prob), qubits_idx, num_qubits, check_complete=False) -class ResetChannel(Channel): +class ResetChannel(KrausRepr): r"""A collection of reset channels. Such a channel reset the state to :math:`|0\rangle` with a probability of p and to :math:`|1\rangle` with @@ -349,19 +298,10 @@ def __init__( self, prob: Union[paddle.Tensor, Iterable[float]], qubits_idx: Union[Iterable[int], int, str] = 'full', num_qubits: int = None ): - super().__init__() - self.qubits_idx = _format_qubits_idx(qubits_idx, num_qubits) - self.prob = paddle.to_tensor(prob) if isinstance(prob, Iterable) else prob + super().__init__(reset_kraus(prob), qubits_idx, num_qubits, check_complete=False) - def forward(self, state: paddle_quantum.State) -> paddle_quantum.State: - if self.backend == Backend.QuLeaf and state.backend == Backend.QuLeaf: - raise NotImplementedError - for qubit_idx in self.qubits_idx: - state = functional.reset_channel(state, self.prob, qubit_idx, self.dtype, self.backend) - return state - -class ThermalRelaxation(Channel): +class ThermalRelaxation(KrausRepr): r"""A collection of thermal relaxation channels. Such a channel simulates the mixture of the :math:`T_1` and the :math:`T_2` processes on superconducting devices. @@ -379,22 +319,11 @@ def __init__( self, const_t: Union[paddle.Tensor, Iterable[float]], exec_time: Union[paddle.Tensor, float], qubits_idx: Union[Iterable[int], int, str] = 'full', num_qubits: int = None ): - super().__init__() - self.qubits_idx = _format_qubits_idx(qubits_idx, num_qubits) - self.const_t = paddle.to_tensor(const_t) if isinstance(const_t, (int, float)) else const_t - self.exec_time = paddle.to_tensor(exec_time) if isinstance(exec_time, (int, float)) else exec_time - - def forward(self, state: paddle_quantum.State) -> paddle_quantum.State: - if self.backend == Backend.QuLeaf and state.backend == Backend.QuLeaf: - raise NotImplementedError - for qubit_idx in self.qubits_idx: - state = functional.thermal_relaxation( - state, self.const_t, self.exec_time, qubit_idx, self.dtype, self.backend) - return state + super().__init__(thermal_relaxation_kraus(const_t, exec_time), qubits_idx, num_qubits, check_complete=False) -class MixedUnitaryChannel(Channel): - r"""A collection of mixed unitary channels. +class MixedUnitaryChannel(KrausRepr): + r"""A collection of single-qubit mixed unitary channels. Such a channel's Kraus operators are randomly generated unitaries times related probabilities .. math:: @@ -413,13 +342,7 @@ def __init__( self, num_unitary: int, qubits_idx: Union[Iterable[int], int, str] = 'full', num_qubits: int = None ): - super().__init__() - self.qubits_idx = _format_qubits_idx(qubits_idx, num_qubits) - self.kraus_oper = kraus_oper_random(1, num_unitary) - - def forward(self, state: paddle_quantum.State) -> paddle_quantum.State: - if self.backend == Backend.QuLeaf and state.backend == Backend.QuLeaf: - raise NotImplementedError - for qubit_idx in self.qubits_idx: - state = functional.kraus_repr(state, self.kraus_oper, qubit_idx, self.dtype, self.backend) - return state + #TODO: increase the number of acting qubits, currently only support 1 + super().__init__(kraus_unitary_random(1, num_unitary), + _format_qubits_idx(qubits_idx, num_qubits, 1), + num_qubits, check_complete=False) diff --git a/paddle_quantum/channel/custom.py b/paddle_quantum/channel/custom.py index 82421f2..0891ab9 100644 --- a/paddle_quantum/channel/custom.py +++ b/paddle_quantum/channel/custom.py @@ -20,43 +20,122 @@ import math import paddle import warnings -from typing import Union, Iterable +import numpy as np +from typing import Union, Iterable, List import paddle_quantum -from .base import Channel from . import functional +from .base import Channel +from ..backend import Backend from ..intrinsic import _format_qubits_idx +class ChoiRepr(Channel): + r"""A custom channel in Choi representation. + + Args: + choi_repr: Choi operator of this channel. + qubits_idx: Indices of the qubits on which this channel acts. + num_qubits: Total number of qubits. Defaults to ``None``. + + Raises: + NotImplementedError: The noisy channel can only run in density matrix mode. + + """ + def __init__( + self, + choi_repr: paddle.Tensor, + qubits_idx: Union[Iterable[Iterable[int]], Iterable[int], int], + num_qubits: int = None + ): + super().__init__() + num_acted_qubits = int(math.log2(choi_repr.shape[0]) / 2) + assert 2 ** (2 * num_acted_qubits) == choi_repr.shape[0], "The length of oracle should be integer power of 2." + + #TODO: need to add sanity check for choi + self.__choi_repr = choi_repr + self.qubits_idx = _format_qubits_idx(qubits_idx, num_qubits, num_acted_qubits) + + if self.backend != Backend.DensityMatrix: + raise NotImplementedError( + "The noisy channel can only run in density matrix mode.") + + @property + def choi_repr(self) -> paddle.Tensor: + r"""Choi representation + """ + return self.__choi_repr + + @property + def kraus_repr(self) -> List[paddle.Tensor]: + r"""Kraus representation + """ + return _choi_to_kraus(self.__choi_repr, tol=1e-6) + + @property + def stinespring_repr(self) -> paddle.Tensor: + r"""Stinespring representation + """ + return _choi_to_stinespring(self.__choi_repr, tol=1e-6) + + def to_kraus(self) -> 'KrausRepr': + r"""Convert to Kraus representation of this chanel + """ + return KrausRepr(self.kraus_repr, qubits_idx=self.qubits_idx) + + def to_stinespring(self) -> 'StinespringRepr': + r"""Convert to Stinespring representation of this chanel + """ + return StinespringRepr(self.stinespring_repr, qubits_idx=self.qubits_idx) + + def forward(self, state: paddle_quantum.State) -> paddle_quantum.State: + if self.backend == Backend.QuLeaf and state.backend == Backend.QuLeaf: + raise NotImplementedError + for qubits_idx in self.qubits_idx: + state = functional.choi_repr(state, self.__choi_repr, qubits_idx, self.dtype, self.backend) + return state + + class KrausRepr(Channel): r"""A custom channel in Kraus representation. Args: - kraus_oper: Kraus operators of this channel. + kraus_repr: list of Kraus operators of this channel. qubits_idx: Indices of the qubits on which this channel acts. num_qubits: Total number of qubits. Defaults to ``None``. + check_complete: whether check the kraus representation is valid. Defaults to be ``True``. + Set to ``False`` only if the data correctness is guaranteed. + + Raises: + NotImplementedError: The noisy channel can only run in density matrix mode. + """ def __init__( - self, kraus_oper: Union[paddle.Tensor, Iterable[paddle.Tensor]], - qubits_idx: Union[Iterable[Iterable[int]], Iterable[int], int], - num_qubits: int = None + self, kraus_repr: Union[paddle.Tensor, List[paddle.Tensor]], qubits_idx: Union[Iterable[Iterable[int]], Iterable[int], int], + num_qubits: int = None, check_complete: bool = True ): super().__init__() - num_acted_qubits = int(math.log2(kraus_oper[0].shape[0])) - assert 2 ** num_acted_qubits == kraus_oper[0].shape[0], "The length of oracle should be integer power of 2." + num_acted_qubits = int(math.log2(kraus_repr[0].shape[0])) + assert 2 ** num_acted_qubits == kraus_repr[0].shape[0], "The length of oracle should be integer power of 2." - self.kraus_oper = [oper.cast(self.dtype) for oper in kraus_oper] if isinstance(kraus_oper, Iterable) else [kraus_oper.cast(self.dtype)] + # kraus operation formalize + self.__kraus_repr = [oper.cast(self.dtype) for oper in kraus_repr] if isinstance(kraus_repr, List) else [kraus_repr.cast(self.dtype)] self.qubits_idx = _format_qubits_idx(qubits_idx, num_qubits, num_acted_qubits) # sanity check - dimension = 2 ** num_acted_qubits - oper_sum = paddle.zeros([dimension, dimension]).cast(self.dtype) - for oper in self.kraus_oper: - oper_sum = oper_sum + oper @ paddle.conj(oper.T) - err = paddle.norm(paddle.abs(oper_sum - paddle.eye(dimension).cast(self.dtype))).item() - if err > min(1e-6 * dimension * len(kraus_oper), 0.01): - warnings.warn( - f"\nThe input data may not be a Kraus representation of a channel: norm(sum(E * E^d) - I) = {err}.", UserWarning) + if check_complete: + dimension = 2 ** num_acted_qubits + oper_sum = paddle.zeros([dimension, dimension]).cast(self.dtype) + for oper in self.__kraus_repr: + oper_sum = oper_sum + oper @ paddle.conj(oper.T) + err = paddle.norm(paddle.abs(oper_sum - paddle.eye(dimension).cast(self.dtype))).item() + if err > min(1e-6 * dimension * len(kraus_repr), 0.01): + warnings.warn( + f"\nThe input data may not be a Kraus representation of a channel: norm(sum(E * E^d) - I) = {err}.", UserWarning) + + if self.backend != Backend.DensityMatrix: + raise NotImplementedError( + "The noisy channel can only run in density matrix mode.") def __matmul__(self, other: 'KrausRepr') -> 'KrausRepr': r"""Composition between channels with Kraus representations @@ -71,60 +150,164 @@ def __matmul__(self, other: 'KrausRepr') -> 'KrausRepr': for this_kraus in self.kraus_oper: new_kraus_oper.extend([this_kraus @ other_kraus for other_kraus in other.kraus_oper]) return KrausRepr(new_kraus_oper, self.qubits_idx) + + @property + def choi_repr(self) -> paddle.Tensor: + r"""Choi representation + """ + return _kraus_to_choi(self.__kraus_repr) + + @property + def kraus_repr(self) -> List[paddle.Tensor]: + r"""Kraus representation + """ + return self.__kraus_repr + + @property + def stinespring_repr(self) -> paddle.Tensor: + r"""Stinespring representation + """ + return _kraus_to_stinespring(self.__kraus_repr) + + def to_choi(self) -> 'ChoiRepr': + r"""Convert to Choi representation of this chanel + """ + return ChoiRepr(self.choi_repr, qubits_idx=self.qubits_idx) + + def to_stinespring(self) -> 'StinespringRepr': + r"""Convert to Stinespring representation of this chanel + """ + return StinespringRepr(self.stinespring_repr, qubits_idx=self.qubits_idx) def forward(self, state: paddle_quantum.State) -> paddle_quantum.State: + if self.backend == Backend.QuLeaf and state.backend == Backend.QuLeaf: + raise NotImplementedError for qubits_idx in self.qubits_idx: - state = functional.kraus_repr(state, self.kraus_oper, qubits_idx, self.dtype, self.backend) + state = functional.kraus_repr(state, self.__kraus_repr, qubits_idx, self.dtype, self.backend) return state -class ChoiRepr(Channel): +class StinespringRepr(Channel): + r"""A custom channel in Stinespring representation. + + Args: + stinespring_mat: Stinespring matrix that represents this channel. + qubits_idx: Indices of the qubits on which this channel acts. + num_qubits: Total number of qubits. Defaults to ``None``. + + Raises: + NotImplementedError: The noisy channel can only run in density matrix mode. + + """ def __init__( self, - choi_oper: paddle.Tensor, + stinespring_mat: paddle.Tensor, qubits_idx: Union[Iterable[Iterable[int]], Iterable[int], int], num_qubits: int = None ): super().__init__() - num_acted_qubits = int(math.log2(choi_oper.shape[0]) / 2) - assert 2 ** (2 * num_acted_qubits) == choi_oper.shape[0], "The length of oracle should be integer power of 2." - self.choi_oper = choi_oper + num_acted_qubits = int(math.log2(stinespring_mat.shape[1])) + dim_ancilla = stinespring_mat.shape[0] // stinespring_mat.shape[1] + dim_act = stinespring_mat.shape[1] + assert dim_act * dim_ancilla == stinespring_mat.shape[0], 'The width of stinespring matrix should be the factor of its height' + + #TODO: need to add sanity check for stinespring + self.__stinespring_repr = stinespring_mat self.qubits_idx = _format_qubits_idx(qubits_idx, num_qubits, num_acted_qubits) + + if self.backend != Backend.DensityMatrix: + raise NotImplementedError( + "The noisy channel can only run in density matrix mode.") + + @property + def choi_repr(self) -> paddle.Tensor: + r"""Choi representation + """ + return _stinespring_to_choi(self.__stinespring_repr) + + @property + def kraus_repr(self) -> List[paddle.Tensor]: + r"""Kraus representation + """ + return _stinespring_to_kraus(self.__stinespring_repr) + + @property + def stinespring_repr(self) -> paddle.Tensor: + r"""Stinespring representation + """ + return self.__stinespring_repr + + def to_choi(self) -> 'ChoiRepr': + r"""Convert to Choi representation of this chanel + """ + return ChoiRepr(self.choi_repr, qubits_idx=self.qubits_idx) + + def to_kraus(self) -> 'KrausRepr': + r"""Convert to Kraus representation of this chanel + """ + return KrausRepr(self.kraus_repr, qubits_idx=self.qubits_idx) def forward(self, state: paddle_quantum.State) -> paddle_quantum.State: + if self.backend == Backend.QuLeaf and state.backend == Backend.QuLeaf: + raise NotImplementedError for qubits_idx in self.qubits_idx: - state = functional.choi_repr( - state, - self.choi_oper, - qubits_idx, - self.dtype, - self.backend - ) + state = functional.stinespring_repr(state, self.__stinespring_repr, qubits_idx, self.dtype, self.backend) return state + +def _choi_to_kraus(choi_repr: paddle.Tensor, tol: float) -> List[paddle.Tensor]: + r"""Transform the Choi representation to the Kraus representation + """ + ndim = int(math.sqrt(choi_repr.shape[0])) + w, v = paddle.linalg.eigh(choi_repr) -class StinespringRepr(Channel): - def __init__( - self, - stinespring_matrix: paddle.Tensor, - qubits_idx: Union[Iterable[Iterable[int]], Iterable[int], int], - num_qubits: int = None - ): - super().__init__() - num_acted_qubits = int(math.log2(stinespring_matrix.shape[1])) - dim_ancilla = stinespring_matrix.shape[0] // stinespring_matrix.shape[1] - dim_act = stinespring_matrix.shape[1] - assert dim_act * dim_ancilla == stinespring_matrix.shape[0], 'The width of stinespring matrix should be the factor of its height' - self.stinespring_matrix = stinespring_matrix - self.qubits_idx = _format_qubits_idx(qubits_idx, num_qubits, num_acted_qubits) - - def forward(self, state: paddle_quantum.State) -> paddle_quantum.State: - for qubits_idx in self.qubits_idx: - state = functional.stinespring_repr( - state, - self.stinespring_matrix, - qubits_idx, - self.dtype, - self.backend - ) - return state + # add abs to make eigvals safe + w = paddle.abs(w) + l_cut = 0 + for l in range(len(w) - 1, -1, -1): + if paddle.sum(paddle.abs(w[l:])) / paddle.sum(paddle.abs(w)) > 1 - tol: + l_cut = l + break + return [(v * paddle.sqrt(w))[:, l].reshape([ndim, ndim]).T for l in range(l_cut, ndim**2)] + + +def _choi_to_stinespring(choi_repr: paddle.Tensor, tol: float) -> List[paddle.Tensor]: + r"""Transform the Choi representation to the Stinespring representation + """ + # TODO: need a more straightforward transformation + return _kraus_to_stinespring(_choi_to_kraus(choi_repr, tol)) + + +def _kraus_to_choi(kraus_repr: List[paddle.Tensor]) -> paddle.Tensor: + r"""Transform the Kraus representation to the Choi representation + """ + ndim = kraus_repr[0].shape[0] + kraus_oper_tensor = paddle.concat([paddle.kron(x, x.conj().T) for x in kraus_repr]).reshape([len(kraus_repr), ndim, -1]) + choi_repr = paddle.sum(kraus_oper_tensor, axis=0).reshape([ndim for _ in range(4)]).transpose([2, 1, 0, 3]) + return choi_repr.transpose([0, 2, 1, 3]).reshape([ndim * ndim, ndim * ndim]) + + +def _kraus_to_stinespring(kraus_repr: List[paddle.Tensor]) -> paddle.Tensor: + r"""Transform the Kraus representation to the Stinespring representation + """ + j_dim = len(kraus_repr) + i_dim = kraus_repr[0].shape[0] + kraus_oper_tensor = paddle.concat(kraus_repr).reshape([j_dim, i_dim, -1]) + stinespring_repr = kraus_oper_tensor.transpose([1, 0, 2]) + return stinespring_repr.reshape([i_dim * j_dim, i_dim]) + + +def _stinespring_to_choi(stinespring_repr: paddle.Tensor) -> paddle.Tensor: + r"""Transform the Stinespring representation to the Choi representation + """ + # TODO: need a more straightforward transformation + return _kraus_to_choi(_stinespring_to_kraus(stinespring_repr)) + + +def _stinespring_to_kraus(stinespring_repr: paddle.Tensor) -> List[paddle.Tensor]: + r"""Transform the Stinespring representation to the Kraus representation + """ + i_dim = stinespring_repr.shape[1] + j_dim = stinespring_repr.shape[0] // i_dim + kraus_oper = stinespring_repr.reshape([i_dim, j_dim, i_dim]).transpose([1, 0, 2]) + return [kraus_oper[j] for j in range(j_dim)] diff --git a/paddle_quantum/channel/functional/__init__.py b/paddle_quantum/channel/functional/__init__.py index 633c3cc..63f3ccd 100644 --- a/paddle_quantum/channel/functional/__init__.py +++ b/paddle_quantum/channel/functional/__init__.py @@ -17,16 +17,6 @@ The module that contains the functions of various quantum channels. """ -from .common import bit_flip -from .common import phase_flip -from .common import bit_phase_flip -from .common import amplitude_damping -from .common import generalized_amplitude_damping -from .common import phase_damping -from .common import depolarizing -from .common import pauli_channel -from .common import reset_channel -from .common import thermal_relaxation from .common import kraus_repr from .common import choi_repr from .common import stinespring_repr diff --git a/paddle_quantum/channel/functional/common.py b/paddle_quantum/channel/functional/common.py index 851b8d8..ea42269 100644 --- a/paddle_quantum/channel/functional/common.py +++ b/paddle_quantum/channel/functional/common.py @@ -14,551 +14,25 @@ # limitations under the License. r""" -The source file of the various quantum channels. +The underlying logic operations of quantum channels. """ import functools import paddle -import paddle_quantum -from ...backend import density_matrix -from ...intrinsic import _zero, _one +from ...backend import Backend, density_matrix +from ...state import State from typing import Iterable, List, Tuple, Union -def bit_flip( - state: paddle_quantum.State, prob: paddle.Tensor, qubit_idx: Union[List[int], int], - dtype: str, backend: paddle_quantum.Backend -) -> paddle_quantum.State: - r"""Apply a bit flip channel on the input state. - - Args: - state: Input state. - prob: Probability of a bit flip. - qubit_idx: Index of the qubit on which the channel acts. - dtype: Type of data. - backend: Backend on which the simulation is run. - - Raises: - RuntimeError: The noisy channel can only run in density matrix mode. - - Returns: - Output state. - """ - if backend != paddle_quantum.Backend.DensityMatrix: - raise RuntimeError("The noisy channel can only run in density matrix mode.") - kraus_oper = [ - [ - paddle.sqrt(1 - prob).cast(dtype), _zero(dtype), - _zero(dtype), paddle.sqrt(1 - prob).cast(dtype), - ], - [ - _zero(dtype), paddle.sqrt(prob).cast(dtype), - paddle.sqrt(prob).cast(dtype), _zero(dtype), - ] - ] - for idx, oper in enumerate(kraus_oper): - kraus_oper[idx] = paddle.reshape(paddle.concat(oper), [2, 2]) - state_data = [ - density_matrix.unitary_transformation( - state.data, - oper, - qubit_idx if isinstance(qubit_idx, list) else [qubit_idx], - state.num_qubits - ) for oper in kraus_oper - ] - state_data = functools.reduce(lambda x, y: x + y, state_data) - transformed_state = state.clone() - transformed_state.data = state_data - return transformed_state - - -def phase_flip( - state: paddle_quantum.State, prob: paddle.Tensor, qubit_idx: Union[List[int], int], - dtype: str, backend: paddle_quantum.Backend -) -> paddle_quantum.State: - r"""Apply a phase flip channel on the input state. - - Args: - state: Input state. - prob: Probability of a phase flip. - qubit_idx: Index of the qubit on which the channel acts. - dtype: Type of data. - backend: Backend on which the simulation is run. - - Raises: - RuntimeError: The noisy channel can only run in density matrix mode. - - Returns: - Output state. - """ - if backend != paddle_quantum.Backend.DensityMatrix: - raise RuntimeError("The noisy channel can only run in density matrix mode.") - kraus_oper = [ - [ - paddle.sqrt(1 - prob).cast(dtype), _zero(dtype), - _zero(dtype), paddle.sqrt(1 - prob).cast(dtype), - ], - [ - paddle.sqrt(prob).cast(dtype), _zero(dtype), - _zero(dtype), (-paddle.sqrt(prob)).cast(dtype), - ] - ] - for idx, oper in enumerate(kraus_oper): - kraus_oper[idx] = paddle.reshape(paddle.concat(oper), [2, 2]) - state_data = [ - density_matrix.unitary_transformation( - state.data, - oper, - qubit_idx if isinstance(qubit_idx, list) else [qubit_idx], - state.num_qubits - ) for oper in kraus_oper - ] - state_data = functools.reduce(lambda x, y: x + y, state_data) - transformed_state = state.clone() - transformed_state.data = state_data - return transformed_state - - -def bit_phase_flip( - state: paddle_quantum.State, prob: paddle.Tensor, qubit_idx: Union[List[int], int], - dtype: str, backend: paddle_quantum.Backend -) -> paddle_quantum.State: - r"""Apply a bit phase flip channel on the input state. - - Args: - state: Input state. - prob: Probability of a bit phase flip. - qubit_idx: Index of the qubit on which the channel acts. - dtype: Type of data. - backend: Backend on which the simulation is run. - - Raises: - RuntimeError: The noisy channel can only run in density matrix mode. - - Returns: - Output state. - """ - if backend != paddle_quantum.Backend.DensityMatrix: - raise RuntimeError("The noisy channel can only run in density matrix mode.") - kraus_oper = [ - [ - paddle.sqrt(1 - prob).cast(dtype), _zero(dtype), - _zero(dtype), paddle.sqrt(1 - prob).cast(dtype), - ], - [ - _zero(dtype), -1j * paddle.sqrt(prob), - 1j * -paddle.sqrt(prob), _zero(dtype), - ] - ] - for idx, oper in enumerate(kraus_oper): - kraus_oper[idx] = paddle.reshape(paddle.concat(oper), [2, 2]) - state_data = [ - density_matrix.unitary_transformation( - state.data, - oper, - qubit_idx if isinstance(qubit_idx, list) else [qubit_idx], - state.num_qubits - ) for oper in kraus_oper - ] - state_data = functools.reduce(lambda x, y: x + y, state_data) - transformed_state = state.clone() - transformed_state.data = state_data - return transformed_state - - -def amplitude_damping( - state: paddle_quantum.State, gamma: paddle.Tensor, qubit_idx: Union[List[int], int], - dtype: str, backend: paddle_quantum.Backend -) -> paddle_quantum.State: - r"""Apply an amplitude damping channel on the input state. - - Args: - state: Input state. - gamma: Damping probability. - qubit_idx: Index of the qubit on which the channel acts. - dtype: Type of data. - backend: Backend on which the simulation is run. - - Raises: - RuntimeError: The noisy channel can only run in density matrix mode. - - Returns: - Output state. - """ - if backend != paddle_quantum.Backend.DensityMatrix: - raise RuntimeError("The noisy channel can only run in density matrix mode.") - kraus_oper = [ - [ - _one(dtype), _zero(dtype), - _zero(dtype), paddle.sqrt(1 - gamma).cast(dtype), - ], - [ - _zero(dtype), paddle.sqrt(gamma).cast(dtype), - _zero(dtype), _zero(dtype)], - - ] - for idx, oper in enumerate(kraus_oper): - kraus_oper[idx] = paddle.reshape(paddle.concat(oper), [2, 2]) - state_data = [ - density_matrix.unitary_transformation( - state.data, - oper, - qubit_idx if isinstance(qubit_idx, list) else [qubit_idx], - state.num_qubits - ) for oper in kraus_oper - ] - state_data = functools.reduce(lambda x, y: x + y, state_data) - transformed_state = state.clone() - transformed_state.data = state_data - return transformed_state - - -def generalized_amplitude_damping( - state: paddle_quantum.State, gamma: paddle.Tensor, prob: paddle.Tensor, - qubit_idx: Union[List[int], int], dtype: str, backend: paddle_quantum.Backend -) -> paddle_quantum.State: - r"""Apply a generalized amplitude damping channel on the input state. - - Args: - state: Input state. - gamma: Damping probability. - prob: Excitation probability. - qubit_idx: Index of the qubit on which the channel acts. - dtype: Type of data. - backend: Backend on which the simulation is run. - - Raises: - RuntimeError: The noisy channel can only run in density matrix mode. - - Returns: - Output state. - """ - if backend != paddle_quantum.Backend.DensityMatrix: - raise RuntimeError("The noisy channel can only run in density matrix mode.") - kraus_oper = [ - [ - paddle.sqrt(prob).cast(dtype), _zero(dtype), - _zero(dtype), (paddle.sqrt(prob).cast(dtype) * paddle.sqrt(1 - gamma)).cast(dtype), - ], - [ - _zero(dtype), (paddle.sqrt(prob) * paddle.sqrt(gamma)).cast(dtype), - _zero(dtype), _zero(dtype), - ], - [ - (paddle.sqrt(1 - prob) * paddle.sqrt(1 - gamma)).cast(dtype), _zero(dtype), - _zero(dtype), paddle.sqrt(1 - prob).cast(dtype), - ], - [ - _zero(dtype), _zero(dtype), - (paddle.sqrt(1 - prob) * paddle.sqrt(gamma)).cast(dtype), _zero(dtype), - ], - ] - for idx, oper in enumerate(kraus_oper): - kraus_oper[idx] = paddle.reshape(paddle.concat(oper), [2, 2]) - state_data = [ - density_matrix.unitary_transformation( - state.data, - oper, - qubit_idx if isinstance(qubit_idx, list) else [qubit_idx], - state.num_qubits - ) for oper in kraus_oper - ] - state_data = functools.reduce(lambda x, y: x + y, state_data) - transformed_state = state.clone() - transformed_state.data = state_data - return transformed_state - - -def phase_damping( - state: paddle_quantum.State, gamma: paddle.Tensor, qubit_idx: Union[List[int], int], - dtype: str, backend: paddle_quantum.Backend -) -> paddle_quantum.State: - r"""Apply a phase damping channel on the input state. - - Args: - state: Input state. - gamma: Parameter of the phase damping channel. - qubit_idx: Index of the qubit on which the channel acts. - dtype: Type of data. - backend: Backend on which the simulation is run. - - Raises: - RuntimeError: The noisy channel can only run in density matrix mode. - - Returns: - Output state. - """ - if backend != paddle_quantum.Backend.DensityMatrix: - raise RuntimeError("The noisy channel can only run in density matrix mode.") - kraus_oper = [ - [ - _one(dtype), _zero(dtype), - _zero(dtype), paddle.sqrt(1 - gamma).cast(dtype), - ], - [ - _zero(dtype), _zero(dtype), - _zero(dtype), paddle.sqrt(gamma).cast(dtype), - ] - ] - for idx, oper in enumerate(kraus_oper): - kraus_oper[idx] = paddle.reshape(paddle.concat(oper), [2, 2]) - state_data = [ - density_matrix.unitary_transformation( - state.data, - oper, - qubit_idx if isinstance(qubit_idx, list) else [qubit_idx], - state.num_qubits - ) for oper in kraus_oper - ] - state_data = functools.reduce(lambda x, y: x + y, state_data) - transformed_state = state.clone() - transformed_state.data = state_data - return transformed_state - - -def depolarizing( - state: paddle_quantum.State, prob: paddle.Tensor, qubit_idx: Union[List[int], int], - dtype: str, backend: paddle_quantum.Backend -) -> paddle_quantum.State: - r"""Apply a depolarizing channel on the input state. - - Args: - state: Input state. - prob: Parameter of the depolarizing channel. - qubit_idx: Index of the qubit on which the channel acts. - dtype: Type of data. - backend: Backend on which the simulation is run. - - Raises: - RuntimeError: The noisy channel can only run in density matrix mode. - - Returns: - Output state. - """ - if backend != paddle_quantum.Backend.DensityMatrix: - raise RuntimeError("The noisy channel can only run in density matrix mode.") - kraus_oper = [ - [ - paddle.sqrt(1 - 3 * prob / 4).cast(dtype), _zero(dtype), - _zero(dtype), paddle.sqrt(1 - 3 * prob / 4).cast(dtype), - ], - [ - _zero(dtype), paddle.sqrt(prob / 4).cast(dtype), - paddle.sqrt(prob / 4).cast(dtype), _zero(dtype), - ], - [ - _zero(dtype), -1j * paddle.sqrt(prob / 4).cast(dtype), - 1j * paddle.sqrt(prob / 4).cast(dtype), _zero(dtype), - ], - [ - paddle.sqrt(prob / 4).cast(dtype), _zero(dtype), - _zero(dtype), (-1 * paddle.sqrt(prob / 4)).cast(dtype), - ], - ] - for idx, oper in enumerate(kraus_oper): - kraus_oper[idx] = paddle.reshape(paddle.concat(oper), [2, 2]) - state_data = [ - density_matrix.unitary_transformation( - state.data, - oper, - qubit_idx if isinstance(qubit_idx, list) else [qubit_idx], - state.num_qubits - ) for oper in kraus_oper - ] - state_data = functools.reduce(lambda x, y: x + y, state_data) - transformed_state = state.clone() - transformed_state.data = state_data - return transformed_state - - -def pauli_channel( - state: paddle_quantum.State, prob: paddle.Tensor, qubit_idx: Union[List[int], int], - dtype: str, backend: paddle_quantum.Backend -) -> paddle_quantum.State: - r"""Apply a Pauli channel on the input state. - - Args: - state: Input state. - prob: Probabilities corresponding to the Pauli X, Y, and Z operators. - qubit_idx: Index of the qubit on which the channel acts. - dtype: Type of data. - backend: Backend on which the simulation is run. - - Raises: - RuntimeError: The noisy channel can only run in density matrix mode. - - Returns: - Output state. - """ - if backend != paddle_quantum.Backend.DensityMatrix: - raise RuntimeError("The noisy channel can only run in density matrix mode.") - prob_x, prob_y, prob_z = prob - prob_i = paddle.sqrt(1 - paddle.sum(prob)) - kraus_oper = [ - [ - paddle.sqrt(prob_i).cast(dtype), _zero(dtype), - _zero(dtype), paddle.sqrt(prob_i).cast(dtype), - ], - [ - _zero(dtype), paddle.sqrt(prob_x).cast(dtype), - paddle.sqrt(prob_x).cast(dtype), _zero(dtype), - ], - [ - _zero(dtype), -1j * paddle.sqrt(prob_y).cast(dtype), - 1j * paddle.sqrt(prob_y).cast(dtype), _zero(dtype), - ], - [ - paddle.sqrt(prob_z).cast(dtype), _zero(dtype), - _zero(dtype), (-paddle.sqrt(prob_z)).cast(dtype), - ], - ] - for idx, oper in enumerate(kraus_oper): - kraus_oper[idx] = paddle.reshape(paddle.concat(oper), [2, 2]) - state_data = [ - density_matrix.unitary_transformation( - state.data, - oper, - qubit_idx if isinstance(qubit_idx, list) else [qubit_idx], - state.num_qubits - ) for oper in kraus_oper - ] - state_data = functools.reduce(lambda x, y: x + y, state_data) - transformed_state = state.clone() - transformed_state.data = state_data - return transformed_state - - -def reset_channel( - state: paddle_quantum.State, prob: paddle.Tensor, qubit_idx: Union[List[int], int], - dtype: str, backend: paddle_quantum.Backend -) -> paddle_quantum.State: - r"""Apply a reset channel on the input state. - - Args: - state: Input state. - prob: Probabilities of resetting to :math:`|0\rangle` and to :math:`|1\rangle`. - qubit_idx: Index of the qubit on which the channel acts. - dtype: Type of data. - backend: Backend on which the simulation is run. - - Raises: - RuntimeError: The noisy channel can only run in density matrix mode. - - Returns: - Output state. - """ - if backend != paddle_quantum.Backend.DensityMatrix: - raise RuntimeError("The noisy channel can only run in density matrix mode.") - prob_0, prob_1 = prob - prob_i = 1 - paddle.sum(prob) - kraus_oper = [ - [ - paddle.sqrt(prob_0).cast(dtype), _zero(dtype), - _zero(dtype), _zero(dtype), - ], - [ - _zero(dtype), paddle.sqrt(prob_0).cast(dtype), - _zero(dtype), _zero(dtype), - ], - [ - _zero(dtype), _zero(dtype), - paddle.sqrt(prob_1).cast(dtype), _zero(dtype), - ], - [ - _zero(dtype), _zero(dtype), - _zero(dtype), paddle.sqrt(prob_1).cast(dtype), - ], - [ - paddle.sqrt(prob_i).cast(dtype), _zero(dtype), - _zero(dtype), paddle.sqrt(prob_i).cast(dtype), - ], - ] - for idx, oper in enumerate(kraus_oper): - kraus_oper[idx] = paddle.reshape(paddle.concat(oper), [2, 2]) - state_data = [ - density_matrix.unitary_transformation( - state.data, - oper, - qubit_idx if isinstance(qubit_idx, list) else [qubit_idx], - state.num_qubits - ) for oper in kraus_oper - ] - state_data = functools.reduce(lambda x, y: x + y, state_data) - transformed_state = state.clone() - transformed_state.data = state_data - return transformed_state - - -def thermal_relaxation( - state: paddle_quantum.State, const_t: paddle.Tensor, exec_time: paddle.Tensor, - qubit_idx: Union[List[int], int], dtype: str, backend: paddle_quantum.Backend -) -> paddle_quantum.State: - r"""Apply a thermal relaxation channel on the input state. - - Args: - state: Input state. - const_t: :math:`T_1` and :math:`T_2` relaxation time in microseconds. - exec_time: Quantum gate execution time in the process of relaxation in nanoseconds. - qubit_idx: Index of the qubit on which the channel acts. - dtype: Type of data. - backend: Backend on which the simulation is run. - - Raises: - RuntimeError: The noisy channel can only run in density matrix mode. - - Returns: - Output state. - """ - if backend != paddle_quantum.Backend.DensityMatrix: - raise RuntimeError("The noisy channel can only run in density matrix mode.") - t1, t2 = const_t - exec_time = exec_time / 1000 - prob_reset = 1 - paddle.exp(-exec_time / t1) - prob_z = (1 - prob_reset) * (1 - paddle.exp(-exec_time / t2) * paddle.exp(exec_time / t1)) / 2 - prob_i = 1 - prob_reset - prob_z - kraus_oper = [ - [ - paddle.sqrt(prob_i).cast(dtype), _zero(dtype), - _zero(dtype), paddle.sqrt(prob_i).cast(dtype), - ], - [ - paddle.sqrt(prob_z).cast(dtype), _zero(dtype), - _zero(dtype), (-paddle.sqrt(prob_z)).cast(dtype), - ], - [ - paddle.sqrt(prob_reset).cast(dtype), _zero(dtype), - _zero(dtype), _zero(dtype), - ], - [ - _zero(dtype), paddle.sqrt(prob_reset).cast(dtype), - _zero(dtype), _zero(dtype), - ], - ] - for idx, oper in enumerate(kraus_oper): - kraus_oper[idx] = paddle.reshape(paddle.concat(oper), [2, 2]) - state_data = [ - density_matrix.unitary_transformation( - state.data, - oper, - qubit_idx if isinstance(qubit_idx, list) else [qubit_idx], - state.num_qubits - ) for oper in kraus_oper - ] - state_data = functools.reduce(lambda x, y: x + y, state_data) - transformed_state = state.clone() - transformed_state.data = state_data - return transformed_state - - def kraus_repr( - state: paddle_quantum.State, kraus_oper: Iterable[paddle.Tensor], qubit_idx: Union[List[int], int], - dtype: str, backend: paddle_quantum.Backend -) -> paddle_quantum.State: + state: State, list_kraus_oper: Iterable[paddle.Tensor], qubit_idx: Union[List[int], int], + dtype: str, backend: Backend +) -> State: r"""Apply a custom channel in the Kraus representation on the input state. Args: state: Input state. - kraus_oper: Kraus operators of this channel. + list_kraus_oper: Kraus operators of this channel. qubit_idx: Index of the qubit on which the channel acts. dtype: Type of data. backend: Backend on which the simulation is run. @@ -569,7 +43,7 @@ def kraus_repr( Returns: Output state. """ - if backend != paddle_quantum.Backend.DensityMatrix: + if state.backend != Backend.DensityMatrix or backend != Backend.DensityMatrix: raise RuntimeError("The noisy channel can only run in density matrix mode.") state_data = [ density_matrix.unitary_transformation( @@ -577,7 +51,7 @@ def kraus_repr( oper, qubit_idx if isinstance(qubit_idx, list) else [qubit_idx], state.num_qubits - ) for oper in kraus_oper + ) for oper in list_kraus_oper ] state_data = functools.reduce(lambda x, y: x + y, state_data) transformed_state = state.clone() @@ -585,27 +59,29 @@ def kraus_repr( return transformed_state -def choi_repr( - state: paddle_quantum.State, choi_oper: paddle.Tensor, qubit_idx: Union[List[int], int], - dtype: str, backend: paddle_quantum.Backend -) -> paddle_quantum.State: - r"""choi_repr implement - - Assume the choi state has the shape of sum :math:`|i\rangle\langle j|` :math:`N(|i\rangle\langle j|)` . +def choi_repr(state: State, choi_oper: paddle.Tensor, qubit_idx: Union[List[int], int], + dtype: str, backend: Backend) -> State: + r"""Apply a custom channel in the Choi representation on the input state. + The choi operator is with form + + .. math:: + + \sum_{i, j} |i\rangle\langle j| \otimes N(|i\rangle\langle j|) Args: - state: input quantum state - choi_oper: choi representation for the channel - qubit_idx: which qubits the channel acts on - dtype: data dtype - backend: data backend - - Raises: - RuntimeError: _description_ + state: Input quantum state + choi_oper: Choi representation for the channel :math:`N` + qubit_idx: Index of the qubit on which the channel acts on + dtype: Type of data + backend: Backend on which the simulation is run Returns: - paddle_quantum.State: output from the channel + Output state. + """ + if state.backend != Backend.DensityMatrix or backend != Backend.DensityMatrix: + raise RuntimeError("The noisy channel can only run in density matrix mode.") + qubit_idx = qubit_idx if isinstance(qubit_idx, list) else [qubit_idx] def genSwapList(origin: List[int], target: List[int]) -> List[Tuple[int, int]]: @@ -637,9 +113,7 @@ def positionOfValueAt(idx): next_idx = positionOfValueAt(next_idx) return swap_ops - - if backend != paddle_quantum.Backend.DensityMatrix: - raise RuntimeError("The noisy channel can only run in density matrix mode.") + assert len(choi_oper) == 2 ** (2 * len(qubit_idx)) num_qubits = state.num_qubits @@ -737,36 +211,37 @@ def positionOfValueAt(idx): ) new_state = paddle.reshape(new_state, higher_dims.copy() + [2 ** num_qubits, 2 ** num_qubits]) - return paddle_quantum.State(new_state, dtype=dtype, backend=backend) - + return State(new_state, dtype=dtype, backend=backend) -def stinespring_repr( - state: paddle_quantum.State, - stinespring_mat: paddle.Tensor, - qubit_idx: Union[List[int], int], - dtype: str, - backend: paddle_quantum.Backend -): - """stinespring representation for quantum channel - assuming stinespring_mat being the rectangle matrix of shape (dim1 * dim2, dim1) - where dim1 is the dimension of qubit_idx, dim2 needs to be partial traced. With - Dirac notation we have the elements +def stinespring_repr(state: State, stinespring_mat: paddle.Tensor, qubit_idx: Union[List[int], int], + dtype: str, backend: Backend) -> State: + r"""Apply a custom channel in the Stinespring representation on the input state. + ``stinespring_mat`` is a :math:`(d_1 * d_2) \times d_1` rectangular matrix. + Here :math:`d_1` is the dimension of space where ``qubit_idx`` locates, + :math:`d_2` is the dimension of auxillary system. + With Dirac notation ``stinespring_mat`` can be defined as - stinespring_mat.reshape([dim1, dim2, dim1])[i, j, k] = + .. math:: + + \text{stinespring_mat.reshape}([d_1, d_2, d_1])[i, j, k] = \langle i, j| A |k \rangle - with A being the stinespring operator, the channel acts as rho -> Tr_2 A rho A^dagger. + with :math:`A` being the Stinespring operator and the channel acting as :math:`\rho \mapsto \text{tr}_2 (A \rho A^\dagger)`. Args: - state: input quantum state - stinespring_mat: Stinespring representation for the channel - qubit_idx: which qubits the channel acts on - dtype: data dtype - backend: data backend + state: Input quantum state + stinespring_mat: Stinespring representation :math:`A` for the channel + qubit_idx: Index of the qubit on which the channel acts on + dtype: Type of data + backend: Backend on which the simulation is run Returns: - paddle_quantum.State: output from the channel + Output state. + """ + if state.backend != Backend.DensityMatrix or backend != Backend.DensityMatrix: + raise RuntimeError("The noisy channel can only run in density matrix mode.") + qubit_idx = qubit_idx if isinstance(qubit_idx, list) else [qubit_idx] def genSwapList(origin: List[int], target: List[int]) -> List[Tuple[int, int]]: @@ -904,4 +379,4 @@ def positionOfValueAt(idx): ) state_data = paddle.reshape(state_data, higher_dims.copy() + [2 ** num_qubits, 2 ** num_qubits]) - return paddle_quantum.State(state_data, dtype=dtype, backend=backend) + return State(state_data, dtype=dtype, backend=backend) diff --git a/paddle_quantum/channel/representation.py b/paddle_quantum/channel/representation.py new file mode 100644 index 0000000..0a1a9d8 --- /dev/null +++ b/paddle_quantum/channel/representation.py @@ -0,0 +1,481 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +The library of representations of channels +""" + +import numpy as np + +import paddle +from ..base import get_dtype +from ..intrinsic import _zero, _one, _get_float_dtype +from ..linalg import pauli_basis_generation +from typing import List, Union + + +def bit_flip_kraus(prob: Union[float, np.ndarray, paddle.Tensor], dtype: str = None) -> List[paddle.Tensor]: + r"""Kraus representation of a bit flip channel with form + + .. math:: + + E_0 = \sqrt{1-p} I, + E_1 = \sqrt{p} X. + + Args: + prob: probability :math:`p`. + dtype: data type. Defaults to be ``None``. + + Returns: + a list of Kraus operators + + """ + dtype = get_dtype() if dtype is None else dtype + prob = prob if isinstance(prob, paddle.Tensor) else paddle.to_tensor(prob, dtype=_get_float_dtype(dtype)) + kraus_oper = [ + [ + paddle.sqrt(1 - prob).cast(dtype), _zero(dtype), + _zero(dtype), paddle.sqrt(1 - prob).cast(dtype), + ], + [ + _zero(dtype), paddle.sqrt(prob).cast(dtype), + paddle.sqrt(prob).cast(dtype), _zero(dtype), + ] + ] + for idx, oper in enumerate(kraus_oper): + kraus_oper[idx] = paddle.reshape(paddle.concat(oper), [2, 2]) + return kraus_oper + + +def phase_flip_kraus(prob: Union[float, np.ndarray, paddle.Tensor], dtype: str = None) -> List[paddle.Tensor]: + r"""Kraus representation of a phase flip channel with form + + .. math:: + + E_0 = \sqrt{1 - p} I, + E_1 = \sqrt{p} Z. + + Args: + prob: probability :math:`p`. + dtype: data type. Defaults to be ``None``. + + Returns: + a list of Kraus operators + + """ + dtype = get_dtype() if dtype is None else dtype + prob = prob if isinstance(prob, paddle.Tensor) else paddle.to_tensor(prob, dtype=_get_float_dtype(dtype)) + kraus_oper = [ + [ + paddle.sqrt(1 - prob).cast(dtype), _zero(dtype), + _zero(dtype), paddle.sqrt(1 - prob).cast(dtype), + ], + [ + paddle.sqrt(prob).cast(dtype), _zero(dtype), + _zero(dtype), (-paddle.sqrt(prob)).cast(dtype), + ] + ] + for idx, oper in enumerate(kraus_oper): + kraus_oper[idx] = paddle.reshape(paddle.concat(oper), [2, 2]) + return kraus_oper + + +def bit_phase_flip_kraus(prob: Union[float, np.ndarray, paddle.Tensor], dtype: str = None) -> List[paddle.Tensor]: + r"""Kraus representation of a bit-phase flip channel with form + + .. math:: + + E_0 = \sqrt{1 - p} I, + E_1 = \sqrt{p} Y. + + Args: + prob: probability :math:`p`. + dtype: data type. Defaults to be ``None``. + + Returns: + a list of Kraus operators + + """ + dtype = get_dtype() if dtype is None else dtype + prob = prob if isinstance(prob, paddle.Tensor) else paddle.to_tensor(prob, dtype=_get_float_dtype(dtype)) + kraus_oper = [ + [ + paddle.sqrt(1 - prob).cast(dtype), _zero(dtype), + _zero(dtype), paddle.sqrt(1 - prob).cast(dtype), + ], + [ + _zero(dtype), -1j * paddle.sqrt(prob), + 1j * -paddle.sqrt(prob), _zero(dtype), + ] + ] + for idx, oper in enumerate(kraus_oper): + kraus_oper[idx] = paddle.reshape(paddle.concat(oper), [2, 2]) + return kraus_oper + + +def amplitude_damping_kraus(gamma: Union[float, np.ndarray, paddle.Tensor], dtype: str = None) -> List[paddle.Tensor]: + r"""Kraus representation of an amplitude damping channel with form + + .. 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: coefficient :math:`\gamma`. + dtype: data type. Defaults to be ``None``. + + Returns: + a list of Kraus operators + + """ + dtype = get_dtype() if dtype is None else dtype + gamma = gamma if isinstance(gamma, paddle.Tensor) else paddle.to_tensor(gamma, dtype=_get_float_dtype(dtype)) + kraus_oper = [ + [ + _one(dtype), _zero(dtype), + _zero(dtype), paddle.sqrt(1 - gamma).cast(dtype), + ], + [ + _zero(dtype), paddle.sqrt(gamma).cast(dtype), + _zero(dtype), _zero(dtype)], + + ] + for idx, oper in enumerate(kraus_oper): + kraus_oper[idx] = paddle.reshape(paddle.concat(oper), [2, 2]) + return kraus_oper + + +def generalized_amplitude_damping_kraus(gamma: Union[float, np.ndarray, paddle.Tensor], + prob: Union[float, np.ndarray, paddle.Tensor], dtype: str = None) -> List[paddle.Tensor]: + r"""Kraus representation of a generalized amplitude damping channel with form + + .. 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: coefficient :math:`\gamma`. + prob: probability :math:`p`. + dtype: data type. Defaults to be ``None``. + + Returns: + a list of Kraus operators + + """ + dtype = get_dtype() if dtype is None else dtype + float_dtype = _get_float_dtype(dtype) + gamma = gamma if isinstance(gamma, paddle.Tensor) else paddle.to_tensor(gamma, dtype=float_dtype) + prob = prob if isinstance(prob, paddle.Tensor) else paddle.to_tensor(prob, dtype=float_dtype) + kraus_oper = [ + [ + paddle.sqrt(prob).cast(dtype), _zero(dtype), + _zero(dtype), (paddle.sqrt(prob).cast(dtype) * paddle.sqrt(1 - gamma)).cast(dtype), + ], + [ + _zero(dtype), (paddle.sqrt(prob) * paddle.sqrt(gamma)).cast(dtype), + _zero(dtype), _zero(dtype), + ], + [ + (paddle.sqrt(1 - prob) * paddle.sqrt(1 - gamma)).cast(dtype), _zero(dtype), + _zero(dtype), paddle.sqrt(1 - prob).cast(dtype), + ], + [ + _zero(dtype), _zero(dtype), + (paddle.sqrt(1 - prob) * paddle.sqrt(gamma)).cast(dtype), _zero(dtype), + ], + ] + for idx, oper in enumerate(kraus_oper): + kraus_oper[idx] = paddle.reshape(paddle.concat(oper), [2, 2]) + return kraus_oper + + +def phase_damping_kraus(gamma: Union[float, np.ndarray, paddle.Tensor], dtype: str = None) -> List[paddle.Tensor]: + r"""Kraus representation of a phase damping channel with form + + .. 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: coefficient :math:`\gamma`. + dtype: data type. Defaults to be ``None``. + + Returns: + a list of Kraus operators + + """ + dtype = get_dtype() if dtype is None else dtype + gamma = gamma if isinstance(gamma, paddle.Tensor) else paddle.to_tensor(gamma, dtype=_get_float_dtype(dtype)) + kraus_oper = [ + [ + _one(dtype), _zero(dtype), + _zero(dtype), paddle.sqrt(1 - gamma).cast(dtype), + ], + [ + _zero(dtype), _zero(dtype), + _zero(dtype), paddle.sqrt(gamma).cast(dtype), + ] + ] + for idx, oper in enumerate(kraus_oper): + kraus_oper[idx] = paddle.reshape(paddle.concat(oper), [2, 2]) + return kraus_oper + + +def depolarizing_kraus(prob: Union[float, np.ndarray, paddle.Tensor], dtype: str = None) -> List[paddle.Tensor]: + r"""Kraus representation of a depolarizing channel with form + + .. math:: + + E_0 = \sqrt{1-3p/4} I, + E_1 = \sqrt{p/4} X, + E_2 = \sqrt{p/4} Y, + E_3 = \sqrt{p/4} Z. + + Args: + prob: probability :math:`p`. + dtype: data type. Defaults to be ``None``. + + Returns: + a list of Kraus operators + + """ + dtype = get_dtype() if dtype is None else dtype + prob = prob if isinstance(prob, paddle.Tensor) else paddle.to_tensor(prob, dtype=_get_float_dtype(dtype)) + kraus_oper = [ + [ + paddle.sqrt(1 - 3 * prob / 4).cast(dtype), _zero(dtype), + _zero(dtype), paddle.sqrt(1 - 3 * prob / 4).cast(dtype), + ], + [ + _zero(dtype), paddle.sqrt(prob / 4).cast(dtype), + paddle.sqrt(prob / 4).cast(dtype), _zero(dtype), + ], + [ + _zero(dtype), -1j * paddle.sqrt(prob / 4).cast(dtype), + 1j * paddle.sqrt(prob / 4).cast(dtype), _zero(dtype), + ], + [ + paddle.sqrt(prob / 4).cast(dtype), _zero(dtype), + _zero(dtype), (-1 * paddle.sqrt(prob / 4)).cast(dtype), + ], + ] + for idx, oper in enumerate(kraus_oper): + kraus_oper[idx] = paddle.reshape(paddle.concat(oper), [2, 2]) + return kraus_oper + + +def generalized_depolarizing_kraus(prob: float, num_qubits: int, dtype: str = None) -> List[paddle.Tensor]: + r"""Kraus representation of a generalized depolarizing channel with form + + .. math:: + + E_0 = \sqrt{1-(D - 1)p/D} I, \text{ where } D = 4^n, + E_k = \sqrt{p/D} \sigma_k, \text{ for } 0 < k < D. + + Args: + prob: probability :math:`p`. + num_qubits: number of qubits :math:`n` of this channel. + dtype: data type. Defaults to be ``None``. + + Returns: + a list of Kraus operators + + """ + dtype = get_dtype() if dtype is None else dtype + prob = prob if isinstance(prob, paddle.Tensor) else paddle.to_tensor(prob, dtype=_get_float_dtype(dtype)) + + basis = [ele.cast(dtype) * (2 ** num_qubits + 0j) for ele in pauli_basis_generation(num_qubits)] + I, other_elements = basis[0], basis[1:] + + dim = 4 ** num_qubits + return ([I * (paddle.sqrt(1 - (dim - 1) * prob / dim) + 0j)] + + [ele * (paddle.sqrt(prob / dim) + 0j) for ele in other_elements]) + + +def pauli_kraus(prob: Union[List[float], np.ndarray, paddle.Tensor], dtype: str = None) -> List[paddle.Tensor]: + r"""Kraus representation of a pauli channel + + Args: + prob: a list of three probabilities corresponding to X, Y, Z gate :math:`p`. + dtype: data type. Defaults to be ``None``. + + Returns: + a list of Kraus operators + + """ + dtype = get_dtype() if dtype is None else dtype + float_dtype = _get_float_dtype(dtype) + prob = prob.cast(float_dtype) if isinstance(prob, paddle.Tensor) else paddle.to_tensor(prob, dtype=float_dtype) + prob_x, prob_y, prob_z = prob[0], prob[1], prob[2] + prob_sum = paddle.sum(prob) + assert prob_sum <= 1, \ + f"The sum of input probabilities should not be greater than 1: received {prob_sum.item()}" + prob_i = paddle.sqrt(1 - prob_sum) + kraus_oper = [ + [ + paddle.sqrt(prob_i).cast(dtype), _zero(dtype), + _zero(dtype), paddle.sqrt(prob_i).cast(dtype), + ], + [ + _zero(dtype), paddle.sqrt(prob_x).cast(dtype), + paddle.sqrt(prob_x).cast(dtype), _zero(dtype), + ], + [ + _zero(dtype), -1j * paddle.sqrt(prob_y).cast(dtype), + 1j * paddle.sqrt(prob_y).cast(dtype), _zero(dtype), + ], + [ + paddle.sqrt(prob_z).cast(dtype), _zero(dtype), + _zero(dtype), (-paddle.sqrt(prob_z)).cast(dtype), + ], + ] + for idx, oper in enumerate(kraus_oper): + kraus_oper[idx] = paddle.reshape(paddle.concat(oper), [2, 2]) + return kraus_oper + + +def reset_kraus(prob: Union[List[float], np.ndarray, paddle.Tensor], dtype: str = None) -> List[paddle.Tensor]: + r"""Kraus representation of a reset channel with form + + .. math:: + + E_0 = + \begin{bmatrix} + \sqrt{p} & 0 \\ + 0 & 0 + \end{bmatrix}, + E_1 = + \begin{bmatrix} + 0 & \sqrt{p} \\ + 0 & 0 + \end{bmatrix},\\ + E_2 = + \begin{bmatrix} + 0 & 0 \\ + \sqrt{q} & 0 + \end{bmatrix}, + E_3 = + \begin{bmatrix} + 0 & 0 \\ + 0 & \sqrt{q} + \end{bmatrix},\\ + E_4 = \sqrt{1-p-q} I. + + Args: + prob: list of two probabilities of resetting to state :math:`|0\rangle` and :math:`|1\rangle`. + dtype: data type. Defaults to be ``None``. + + Returns: + a list of Kraus operators + + """ + dtype = get_dtype() if dtype is None else dtype + float_dtype = _get_float_dtype(dtype) + prob = prob.cast(float_dtype) if isinstance(prob, paddle.Tensor) else paddle.to_tensor(prob, dtype=float_dtype) + prob_0, prob_1 = prob[0], prob[1] + prob_sum = paddle.sum(prob) + assert prob_sum <= 1, \ + f"The sum of input probabilities should not be greater than 1: received {prob_sum.item()}" + prob_i = 1 - prob_sum + kraus_oper = [ + [ + paddle.sqrt(prob_0).cast(dtype), _zero(dtype), + _zero(dtype), _zero(dtype), + ], + [ + _zero(dtype), paddle.sqrt(prob_0).cast(dtype), + _zero(dtype), _zero(dtype), + ], + [ + _zero(dtype), _zero(dtype), + paddle.sqrt(prob_1).cast(dtype), _zero(dtype), + ], + [ + _zero(dtype), _zero(dtype), + _zero(dtype), paddle.sqrt(prob_1).cast(dtype), + ], + [ + paddle.sqrt(prob_i).cast(dtype), _zero(dtype), + _zero(dtype), paddle.sqrt(prob_i).cast(dtype), + ], + ] + for idx, oper in enumerate(kraus_oper): + kraus_oper[idx] = paddle.reshape(paddle.concat(oper), [2, 2]) + return kraus_oper + + +def thermal_relaxation_kraus(const_t: Union[List[float], np.ndarray, paddle.Tensor], + exec_time: Union[List[float], np.ndarray, paddle.Tensor], dtype: str = None) -> List[paddle.Tensor]: + r"""Kraus representation of a thermal relaxation channel + + Args: + const_t: list of :math:`T_1` and :math:`T_2` relaxation time in microseconds. + exec_time: quantum gate execution time in the process of relaxation in nanoseconds. + dtype: data type. Defaults to be ``None``. + + Returns: + a list of Kraus operators. + + """ + dtype = get_dtype() if dtype is None else dtype + float_dtype = _get_float_dtype(dtype) + const_t = const_t.cast(float_dtype) if isinstance(const_t, paddle.Tensor) else paddle.to_tensor(const_t, dtype=float_dtype) + t1, t2 = const_t[0], const_t[1] + exec_time = exec_time.cast(float_dtype) / 1000 if isinstance(exec_time, paddle.Tensor) else paddle.to_tensor(exec_time / 1000, dtype=float_dtype) + prob_reset = 1 - paddle.exp(-exec_time / t1) + prob_z = (1 - prob_reset) * (1 - paddle.exp(-exec_time / t2) * paddle.exp(exec_time / t1)) / 2 + prob_i = 1 - prob_reset - prob_z + kraus_oper = [ + [ + paddle.sqrt(prob_i).cast(dtype), _zero(dtype), + _zero(dtype), paddle.sqrt(prob_i).cast(dtype), + ], + [ + paddle.sqrt(prob_z).cast(dtype), _zero(dtype), + _zero(dtype), (-paddle.sqrt(prob_z)).cast(dtype), + ], + [ + paddle.sqrt(prob_reset).cast(dtype), _zero(dtype), + _zero(dtype), _zero(dtype), + ], + [ + _zero(dtype), paddle.sqrt(prob_reset).cast(dtype), + _zero(dtype), _zero(dtype), + ], + ] + for idx, oper in enumerate(kraus_oper): + kraus_oper[idx] = paddle.reshape(paddle.concat(oper), [2, 2]) + return kraus_oper \ No newline at end of file diff --git a/paddle_quantum/data_analysis/vqls.py b/paddle_quantum/data_analysis/vqls.py new file mode 100644 index 0000000..ad2649a --- /dev/null +++ b/paddle_quantum/data_analysis/vqls.py @@ -0,0 +1,362 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +The VQLS model. +""" + +import logging +import os +from functools import partial +from tqdm import tqdm +from typing import Optional, List, Tuple, Callable, Union + +# import cv2 +import numpy as np +import paddle +from paddle.io import Dataset, DataLoader + +import paddle_quantum as pq +from paddle_quantum.ansatz import Circuit +from paddle_quantum.gate import * +from paddle_quantum.state import * +from paddle_quantum.linalg import * +import warnings + +warnings.filterwarnings("ignore", category=Warning) +pq.set_backend("state_vector") + +def _nextPowerOf2(n: int): + count = 0 + if n and not (n & (n - 1)): + return n + while n != 0: + n >>= 1 + count += 1 + return 1 << count + + +def _preprocess(A: np.ndarray, b: np.ndarray): + # extend input so dimensions are power of 2 + fill_dim = _nextPowerOf2(len(A)) + original_dim = len(A) + A = np.pad(A, ((0, fill_dim-len(A)), (0, fill_dim-len(A))), 'constant', constant_values=0) + + # rescale b and convert to state + b = np.pad(b, (0,fill_dim-len(b)), 'constant', constant_values=0) + b_scale = np.linalg.norm(b) + b = b / b_scale + b = to_state(b) + num_qubits = b.num_qubits + + # decompose A into 2 unitaries + _, s, _ = np.linalg.svd(A) + A_scale = max(np.abs(s)) + A_copy = A / (A_scale + 1) + + u, s, v = np.linalg.svd(A_copy) + z = [] + z_conj = [] + for idx in range(len(s)): + val = s[idx] + np.sqrt(1 - s[idx] ** 2) * 1j + z.append(val) + z_conj.append(np.conj(val)) + + z = np.diag(z) + z_conj = np.diag(z_conj) + list_A = [] + coefficients_real = [] + coefficients_img = [] + list_A.append(paddle.to_tensor(np.matmul(np.matmul(u, z), v))) + coefficients_real.append((A_scale+1)/2) + coefficients_img.append(0) + + list_A.append(paddle.to_tensor(np.matmul(np.matmul(u, z_conj), v))) + coefficients_real.append((A_scale+1)/2) + coefficients_img.append(0) + return b, num_qubits, list_A, coefficients_real, coefficients_img, original_dim, b_scale + + +def hadamard_test(phi: pq.state.State, U: paddle.Tensor, num_qubits: int) -> Tuple[paddle.Tensor, paddle.Tensor]: + r""" + Given unitary U and state :math:`|\phi\rangle`, it computes the expectation of U with respect to :math:`|\phi\rangle`, i.e. :math:`|\phi\rangle`. + + Args: + phi: State which the expectation is with respect to. + U: Unitary which we are taking expectation of. + num_qubits: Number of qubits of phi/U. + + Returns: + Return the real and imaginary part of the expectation value. + + """ + # Return real part + cir = Circuit(num_qubits + 1) + cir.h([0]) + cir.control_oracle(U, range(num_qubits + 1)) + cir.h([0]) + + input_state = to_state(paddle.kron(zero_state(1).data, phi.data)) + result_state = cir(input_state) + measure = pq.loss.measure.Measure() + prob_0 = measure(result_state, qubits_idx=0, desired_result='0') + prob_1 = measure(result_state, qubits_idx=0, desired_result='1') + real_exp = prob_0 - prob_1 + + # Return imaginary part + cir = Circuit(num_qubits + 1) + cir.h([0]) + cir.sdg([0]) + cir.control_oracle(U, range(num_qubits + 1)) + cir.h([0]) + + input_state = to_state(np.kron(zero_state(1).numpy(), phi.numpy())) + result_state = cir(input_state) + prob_0 = measure(result_state, qubits_idx=0, desired_result='0') + prob_1 = measure(result_state, qubits_idx=0, desired_result='1') + img_exp = prob_0 - prob_1 + + return real_exp, img_exp + + +def hadamard_overlap_test(phi: State, b: State, An: paddle.Tensor, Am: paddle.Tensor, num_qubits: int) -> Tuple[paddle.Tensor, paddle.Tensor]: + r""" + Given unitary Am, An and state :math:`|\phi\rangle`, b, it computes the value of :math:`\langle{b}| An |\phi\rangle\langle\phi| Am^\dagger |b\rangle`. + + Args: + phi: State in the calculation. + b: State in the calculation. + Am: Unitary matrix in the calculation. + An: Unitary matrix in the calculation. + num_qubits: Number of qubits of the system. + Returns: + Return the real and imaginary part of the calculation. + + """ + # Return the real part + cir = Circuit(2*num_qubits + 1) + cir.h([0]) + cir.control_oracle(An, range(num_qubits + 1), num_qubits=num_qubits+1) + cir.control_oracle(Am.conj().T, [0]+list(range(num_qubits+1, 2*num_qubits+1)), num_qubits=num_qubits+1) + for idx in range(num_qubits): + cir.cnot([idx+1, idx+1+num_qubits]) + cir.h(range(num_qubits+1)) + + input_state = to_state(paddle.kron(paddle.kron(zero_state(1).data, phi.data), b.data)) + result_state = cir(input_state) + measure = pq.loss.measure.Measure() + + # Calculate the result of the Overlap Circuit, obtain P_0 and P_1 as described in paper + # See section IV.B. of https://arxiv.org/pdf/1303.6814.pdf + bin_string = [] + for i in range(2 ** num_qubits, 2 ** (num_qubits + 1)): + bin_string.append(bin(i)[3:]) + + P_0 = paddle.zeros([1]) + P_1 = paddle.zeros([1]) + for i in bin_string: + for j in bin_string: + a = bin(int(i, base=2) & int(j, base=2))[2:].zfill(num_qubits) + parity = a.count('1') % 2 + if parity == 0: + P_0 = P_0 + measure(result_state, desired_result='0'+i+j) + P_1 = P_1 + measure(result_state, desired_result='1'+i+j) + else: + P_0 = P_0 - measure(result_state, desired_result='0'+i+j) + P_1 = P_1 - measure(result_state, desired_result='1'+i+j) + real_exp = P_0 - P_1 + + # Return the imaginary part + cir = Circuit(2*num_qubits + 1) + cir.h([0]) + cir.control_oracle(An, range(num_qubits + 1), num_qubits=num_qubits+1) + cir.control_oracle(Am.conj().T, [0]+list(range(num_qubits+1, 2*num_qubits+1)), num_qubits=num_qubits+1) + for idx in range(num_qubits): + cir.cnot([idx+1,idx+1+num_qubits]) + cir.rz(qubits_idx=0, param=-np.pi/2) + cir.h(range(num_qubits+1)) + + result_state = cir(input_state) + + P_0 = paddle.zeros([1]) + P_1 = paddle.zeros([1]) + for i in bin_string: + for j in bin_string: + a = bin(int(i, base=2) & int(j, base=2))[2:].zfill(num_qubits) + parity = a.count('1') % 2 + if parity == 0: + P_0 = P_0 + measure(result_state, desired_result='0'+i+j) + P_1 = P_1 + measure(result_state, desired_result='1'+i+j) + else: + P_0 = P_0 - measure(result_state, desired_result='0'+i+j) + P_1 = P_1 - measure(result_state, desired_result='1'+i+j) + img_exp = P_0 - P_1 + + return real_exp, img_exp + + +def _complex_multiplication (x_real: float, x_img: float, y_real: float, y_img: float): + real = x_real*y_real - x_img*y_img + img = x_real*y_img + x_img*y_real + return real, img + + +class VQLS(paddle.nn.Layer): + r""" + The class of the variational quantum linear solver (VQLS). + + Args: + num_qubits: The number of qubits which the quantum circuit contains. + A: List of unitaries in the decomposition of the input matrix. + coefficients_real: Real part of coefficients of corresponding unitaries in the decomposition of the input matrix. + coefficients_img: Imaginary part of coefficients of corresponding unitaries in the decomposition of the input matrix. + b: The state which the input answer is encoded into. + depth: Depth of the ansatz circuit. + + """ + def __init__(self, num_qubits: int, A: List[paddle.Tensor], coefficients_real: List[float], + coefficients_img: List[float], b: State, depth: int): + super().__init__() + self.num_qubits = num_qubits + self.A = A + self.b = b + self.coefficients_real = coefficients_real + self.coefficients_img = coefficients_img + cir = Circuit(num_qubits) + cir.complex_entangled_layer(depth=depth) + self.cir = cir + + def forward(self) -> paddle.Tensor: + r""" + The forward function. + + Returns: + Return the output of the model. + """ + phi = self.cir(zero_state(self.num_qubits)) + numerator_real = paddle.zeros([1]) + denominator_real = paddle.zeros([1]) + for m in range(len(self.A)): + for n in range(len(self.A)): + prod_coeff_real, prod_coeff_img = _complex_multiplication(self.coefficients_real[m],-self.coefficients_img[m],self.coefficients_real[n],self.coefficients_img[n]) + temp_real, temp_img = hadamard_test(phi=phi, U=self.A[m].conj().T @ self.A[n], num_qubits=self.num_qubits) + temp_real, temp_img = _complex_multiplication(prod_coeff_real, prod_coeff_img, temp_real, temp_img) + denominator_real = denominator_real + temp_real + + temp_real, temp_img = hadamard_overlap_test(phi=phi, b=self.b, An=self.A[n], Am=self.A[m], num_qubits=self.num_qubits) + temp_real, temp_img = _complex_multiplication(prod_coeff_real, prod_coeff_img, temp_real, temp_img) + numerator_real = numerator_real + temp_real + loss = 1 - numerator_real/denominator_real + return loss + + +def _postprocess(scale: float, original_dim: int, A: np.ndarray, x: np.ndarray, b: np.ndarray) -> np.ndarray: + # scale x to have the correct norm + x = x[:original_dim] + estimate = np.matmul(A,x) + estimate_norm = np.linalg.norm(estimate) + x = x * scale / estimate_norm + + # rotate x to have the correct phase + phase = 0 + for i in range(len(b)): + phase = phase + b[i] / (len(b) * np.matmul(A, x)[i]) + x = x * phase + return x + + +def compute(A: np.ndarray, b: np.ndarray, depth: int, iterations: int, LR: float, gamma: Optional[float]=0) -> np.ndarray: + r""" + Solve the linear equation Ax=b. + + Args: + A: Input matrix. + b: Input vector. + depth: Depth of ansatz circuit. + iterations: Number of iterations for optimization. + LR: Learning rate of optimizer. + gamma: Extra option to end optimization early if loss is below this value. Default to '0'. + + Returns: + Return the vector x that solves Ax=b. + + Raises: + ValueError: A is not a square matrix. + ValueError: dimension of A and b don't match. + ValueError: A is a singular matrix hence there's no unique solution. + """ + # check dimension of input and invertibility of A + if A.shape[0] != A.shape[1]: + raise ValueError("A is not a square matrix") + if len(A) != len(b): + raise ValueError("dimension of A and b don't match") + if np.linalg.det(A) == 0: + raise ValueError("A cannot be inverted") + + logging.basicConfig( + filename='./linear_solver.log', + filemode='w', + format='%(asctime)s %(levelname)s %(message)s', + level=logging.INFO + ) + + msg = f"Input parameters:" + logging.info(msg) + msg = f"Depth of ansatz circuit: {depth}" + logging.info(msg) + msg = f"Learning rate: {LR}" + logging.info(msg) + msg = f"Number of iterations: {iterations}" + logging.info(msg) + if gamma == 0: + msg = f"No threshold value given." + else: + msg = f"Threshold value: {gamma}" + logging.info(msg) + msg = f"Matrix A:\n{A};" + logging.info(msg) + msg = f"Vector b:\n{b}" + logging.info(msg) + + b_rescale, num_qubits, list_A, coefficients_real, coefficients_img, original_dim, scale = _preprocess(A=A,b=b) + vqls = VQLS(num_qubits=num_qubits, A=list_A, coefficients_real=coefficients_real, coefficients_img=coefficients_img, b=b_rescale, depth=depth) + opt = paddle.optimizer.Adam(learning_rate=LR, parameters=vqls.parameters()) + + for itr in tqdm(range(1, iterations + 1)): + loss = vqls() + loss.backward() + opt.minimize(loss) + opt.clear_grad() + if itr % 10 == 0: + msg = ( + f"Iter:{itr:5d}, Loss:{loss.item(): 3.5f}" + ) + logging.info(msg) + if loss.item() paddle.Tensor: + """Kernel-1 using direct encoded data state to evaluate inner product + + Args: + circuit: Executed quantum circuit + input_state: Input state. Defaults to None. + + Returns: + The value of inner product + """ + evolve_state = circuit(input_state) + # measure the first qubit + measure_instance = Measure() + prob = measure_instance(evolve_state, qubits_idx=measure_idx, desired_result='0') + result = (prob - 0.5) * 2 + + return result + + +def _data_transform_(X: Union[paddle.Tensor, np.ndarray], y: Union[paddle.Tensor, np.ndarray]) -> Tuple[paddle.Tensor]: + r"""Normalize classical data + + Args: + X: Independent data in an array. + y: Dependent data in an array. + + Returns: + Normalized data + """ + rawX, rawy = _type_transform(X, "numpy"), _type_transform(y, "numpy") + + Xdata_norm = normalize(rawX, axis=0) + ydata_norm = rawy / np.linalg.norm(rawy) + + return _type_transform(Xdata_norm, "tensor"), _type_transform(ydata_norm, "tensor") + + +def _data_verifying_(num_qubits: int, X: Union[paddle.Tensor, np.ndarray], y: Union[paddle.Tensor, np.ndarray]) -> None: + r"""Verifying the data dimension + """ + if X.shape[0] != y.shape[0]: + raise ValueError("Input dimension does not match!") + if 2**num_qubits < y.shape[0]: + raise ValueError("Insufficient number of qubits for a batch fitting.") + + +def _dtype_transform_(arg: Union[paddle.Tensor, np.ndarray], dt: str = "float32"): + r"""dtype transforming + """ + type_str = _type_fetch(arg) + arg = _type_transform(arg, 'numpy').astype(dt) + return _type_transform(arg, type_str) + + +def _logger_init_(log_name: str, filename: str): + r"""Initialize a logger + + Args: + log_name: logger name + filename: filename + """ + logger = logging.Logger(name=log_name) + handler = logging.FileHandler(filename=filename) + handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s')) + handler.setLevel(logging.INFO) + logger.addHandler(handler) + + return logger + + +class QRegressionModel: + r"""Regression model covering all classes. + + Args: + data_file: The dataset.csv file. + model_name: The regression model. + x_feature: Independent variables from data labels. + y_feature: Dependent feature values. + num_variable: The number of variable initialized in the model. + init_params: The initial parameters. + num_qubits: The number of qubits in the estimator. Defaults to 6. + learning_rate: The learning rate. Defaults to 0.1. + iteration: The number optimization steps. Defaults to 100. + language: The print language, Defaults to ``CN`` . + """ + + def __init__(self, data_file: str, model_name: str, x_feature: List[str], + y_feature: str, num_variable: int, init_params: List[float], + num_qubits: int = 6, learning_rate: float = 0.1, + iteration: int = 100, language: str = 'CN') -> None: + self.data = load_dataset(data_file, model_name) + self.x_data = self.data[x_feature].values.astype("float32") + self.y_data = self.data[y_feature].values.astype("float32").flatten() + + self.model_name = model_name + self.x_feature = x_feature + self.y_feature = y_feature + self.num_variable = num_variable + self.num_qubits = num_qubits + self.lr = learning_rate + self.itr = iteration + self.lrg = language + + # initialize the regression model + if model_name == "linear": + if len(self.x_feature) != self.num_variable: + raise ValueError(f"Invalid number of independent variables of 'linear': expected {len(self.x_feature)}, received {self.num_variable}.") + model = LinearRegression(num_qubits = self.num_qubits + 1, num_x = self.num_variable) + self.variable = [self.x_feature[i] for i in range(self.num_variable)] + elif model_name == "poly": + if len(self.x_feature) != 1: + raise ValueError(f"Invalid number of independent variables of 'poly': expected 1, received {len(self.x_feature)}.") + model = PolyRegression(num_qubits = self.num_qubits + 1, order = self.num_variable) + self.variable = [f"{self.x_feature[0]}^{i+1}" for i in range(self.num_variable)] + else: + raise ValueError("Invalid model name. Should be either 'linear' or 'poly'.") + + # set parameters using set_params + model.set_params(np.array(init_params, dtype="float32")) + self.model = model + self._outcome_printer_() + + def _outcome_printer_(self): + if self.lrg == "CN": + print(f"模型是否被训练:{self.model.status}。当前模型 R2 得分:{self.model.score(self.x_data, self.y_data):2.5f}。") + print(f"拟合后的模型为:{self.y_feature[0]} = " + f"{self.model.reg_param[0].item():2.5f} + " + + "".join([f"({self.model.reg_param[var_i + 1].item():2.5f}*{self.variable[var_i]}) + " for var_i in range(len(self.variable))])[:-3] + "。" + ) + elif self.lrg == "EN": + print(f"Model status: {self.model.status}. Current regression R2 score: {self.model.score(self.x_data, self.y_data):2.5f}. ") + print(f"The trained model: {self.y_feature[0]} = " + f"{self.model.reg_param[0].item():2.5f} + " + + "".join([f"({self.model.reg_param[var_i + 1].item():2.5f}*{self.variable[var_i]}) + " for var_i in range(len(self.variable))])[:-3] + ". " + ) + else: + print("The language style is not found. Translate to 'EN'. ") + print(f"Model status: {self.model.status}. Current regression R2 score: {self.model.score(self.x_data, self.y_data):2.5f}. ") + print(f"The trained model: {self.y_feature[0]} = " + f"{self.model.reg_param[0].item():2.5f} + " + + "".join([f"({self.model.reg_param[var_i + 1].item():2.5f}*{self.variable[var_i]}) + " for var_i in range(len(self.variable))])[:-3] + ". " + ) + + def regression_analyse(self): + # optimize the model parameters to complete the linear regression analysis + self.model.fit(self.x_data, self.y_data, learning_rate = self.lr, iteration = self.itr) + self._outcome_printer_() + + fig = plt.figure(figsize=(6,4)) + # predict with optimized model + if self.model_name == 'linear': + + fitted_predict = self.model.predict(self.x_data) + # baseline y_true = y_pred + x_base = np.linspace(0, 100, 50) + y_base = x_base + plt.scatter(fitted_predict, self.y_data, marker="^", + label = "predict data", color="#800000") + plt.plot(x_base, y_base, "--", color="#008000") + plt.xlabel("True value", fontdict=font_label) + plt.ylabel("Predict", fontdict=font_label) + + elif self.model_name == "poly": + + x_base = np.linspace(0, 9, 50) + fitted_predict = self.model.predict(x_base.reshape([50,1])) + plt.plot(x_base, fitted_predict, "--", + label = "predict data", color="#800000") + plt.scatter(self.x_data, self.y_data, marker="*", + label = "test data", color="#108090") + plt.xlabel("Independent variable", fontdict=font_label) + plt.ylabel("Dependent variable", fontdict=font_label) + + plt.title("Fish dataset", fontdict=font_label) + plt.grid() + plt.legend(prop=font_legend) + plt.show() + + return self.model + + +class LinearRegression(paddle.nn.Layer): + r"""Regression class for initializing a quantum linear regression model + + Args: + num_qubits: The number of qubits which the quantum circuit contains. + num_x: The the number of independent variables of data. Defaults to ``1``. + """ + + def __init__(self, num_qubits: int, num_x: int = 1) -> None: + super(LinearRegression, self).__init__(name_scope = "LinearRegression") + self.num_qubits = num_qubits + self.num_x = num_x + param = self.create_parameter([self.num_x + 1], + attr=None, dtype="float32", + is_bias=False, + default_initializer=None) + self.add_parameter("param", param) + self.status = False + + @property + def reg_param(self) -> paddle.Tensor: + r"""Flattened parameters in the Layer. + """ + if len(self.parameters()) == 0: + return [] + return paddle.concat([paddle.flatten(param) for param in self.parameters()]) + + def set_params(self, new_params: Union[paddle.Tensor, np.ndarray]) -> None: + r"""set parameters of the model. + + Args: + params: New parameters + """ + + if not isinstance(new_params, paddle.Tensor): + new_params = paddle.to_tensor(new_params, dtype='float32') + new_params = paddle.flatten(new_params) + + if new_params.shape[0] != self.num_x + 1: + raise ValueError(f"Incorrect number of params for the model: expect {self.num_x + 1}, received {new_params.shape[0]}") + + update_param = paddle.create_parameter( + shape=self.param.shape, + dtype='float32', + default_initializer=paddle.nn.initializer.Assign(new_params.reshape(self.param.shape)), + ) + + setattr(self, 'param', update_param) + + def _init_state_preparation_(self, X_data: paddle.Tensor, y_data: paddle.Tensor) -> State: + r"""Generate an initial state for compute inner product + """ + X_data, y_data = _dtype_transform_(X_data), _dtype_transform_(y_data) + + Phi_data = self.reg_param[0] * paddle.ones([y_data.shape[0]], dtype="float32") + for i in range(self.num_x): + Phi_data = Phi_data + self.reg_param[i + 1] * X_data.T[i] + init_state = State((1/math.sqrt(2)) * paddle.concat((y_data, Phi_data))) + return init_state + + def fit(self, X: Union[paddle.Tensor, np.ndarray], y: Union[paddle.Tensor, np.ndarray], + learning_rate: float = 0.01, iteration: int = 200, + saved_dir: str = '', model_name: str = 'linear' + ) -> None: + + r"""Fitting method used for training the model + + Args: + X: Independent data in a 2D array. + y: Dependent data in an 1D array. + learning_rate: Learning rate of optimization. Defaults to ``0.01``. + iteration: Total training iteration. Defaults to ``200``. + saved_dir: The path for saving the fitted data. Defaults to ``''``. + model_name: The model name. Defaults to ``linear``. + + Returns: + Trained model + """ + if not saved_dir: + saved_dir = './' + elif saved_dir[-1] != '/': + saved_dir += '/' + filename=f'{saved_dir}{model_name}.log' + linearlog = _logger_init_('linearlog', filename) + msg = ( + f"\n####################################################\n" + f"The model: {model_name} with following set-up:\n" + f"####################################################\n" + f"No. of qubits: {self.num_qubits - 1}; \n" + f"Raw model: y=a0 + a1*x1 + a2*x2 + ... + ak*xk; \n" + f"Optimizer: Adam\n" + f" Initial params: {self.reg_param.tolist()}; \n" + f" Learning rate: {learning_rate}; \n" + f" No. of iteration: {iteration}.\n" + ) + linearlog.info(msg) + + # hyper parameters + opt = paddle.optimizer.Adam(learning_rate=learning_rate, parameters=self.parameters()) + + # data processing + X_data, y_data = _data_transform_(X, y) + X_data, y_data = X_data[:2**(self.num_qubits-1), :], y_data[:2**(self.num_qubits-1)] + + # verifying input data + _data_verifying_(self.num_qubits, X_data, y_data) + + # initialize circuit + cir_inner_prod = Circuit(self.num_qubits) + cir_inner_prod.h(0) + + p_bar = tqdm( + total=iteration, + ascii=True, + dynamic_ncols=True, + ) + linearlog.info("Optimization:") + for _ in range(iteration): + p_bar.update(1) + # fitting data and training parameters + init_state = self._init_state_preparation_(X_data, y_data) + loss = (1 - IPEstimator(cir_inner_prod, init_state))**2 + loss.backward() + opt.minimize(loss) + opt.clear_grad() + + if _ % int(iteration * 0.1) == 0: + msg = ( + f"Train loss: {loss.item():2.5f}; " + f"The model has been fitted. Score: {self.score(X, y):2.5f}; " + ) + linearlog.info(msg) + + p_bar.close() + self.status = True + deter_score = self.score(X, y) + + msg = ( + f"\n####################################################\n" + f"Summary\n" + f"####################################################\n" + f"The fitting score: {deter_score:2f};\n" + f"The model params: {self.reg_param.tolist()}.\n" + ) + linearlog.info(msg) + + paddle.save(self.state_dict(), f'{saved_dir}/{model_name}.pdparams') + msg = "The fitted model state_dict has been saved to '.pdparams' file." + linearlog.info(msg) + linearlog.info("="*25) + + def predict(self, X: Union[paddle.Tensor, np.ndarray]) -> Union[paddle.Tensor, np.ndarray]: + + r"""Predict value based on current model parameters + + Args: + X: Independent data in a 2D array. Every column indicates an independent variable. + Every row indicates a sample of data. + Returns: + predicted value + """ + X = _dtype_transform_(X) + type_str = _type_fetch(X) + X = _type_transform(X, "tensor") + + predict_data = self.reg_param[0] * paddle.ones([X.shape[0]], dtype="float32") + for i in range(self.num_x): + predict_data = predict_data + self.reg_param[i + 1] * X.T[i] + + return _type_transform(predict_data, type_str) + + def score(self, X: Union[paddle.Tensor, np.ndarray], y: Union[paddle.Tensor, np.ndarray], metric: Union[str, Callable] = "r2_score") -> float: + + r"""Quantifying the quality of predictions given test set + + Args: + X: Independent data in a 2D array. Every column indicates an independent variable. + Every row indicates a sample of data. + y: Dependent data in a 1D array. + metric: The metric name for the quality. Defaults to ``r2``. If the metric is a callable function, the function should be in the expression + function(y_true: np.ndarray, y_pred: np.ndarray) -> float. + + Returns: + The model score. Based on sklearn.metric class. + """ + + if type(metric) == str: + if metric not in SKLEARN_REG_SCORER: + raise ValueError("The metric is not a valid sklearn.metrics.") + else: + scorer = SKLEARN_METRIC[metric] + else: + if type(metric) != Callable: + raise ValueError("The metric is not a valid Callable metric.") + else: + scorer = metric + + X, y = _type_transform(X, "numpy"), _type_transform(y, "numpy") + y_pred = self.predict(X) + + return scorer(y, y_pred) + + +class PolyRegression(paddle.nn.Layer): + r"""Regression class for initializing a quantum polynomial regression model + + Args: + num_qubits: The number of qubits which the quantum circuit contains. + order: The order of the polynomial regression model. Defaults to ``1``. + """ + def __init__(self, num_qubits: int, order: int = 1) -> None: + super(PolyRegression, self).__init__(name_scope = "PolyRegression") + self.num_qubits = num_qubits + self.order = order + param = self.create_parameter([self.order + 1], + attr=None, dtype="float32", + is_bias=False, + default_initializer=None) + self.add_parameter("param", param) + self.status = False + + @property + def reg_param(self) -> paddle.Tensor: + r"""Flattened parameters in the Layer. + """ + if len(self.parameters()) == 0: + return [] + + return paddle.concat([paddle.flatten(param) for param in self.parameters()]) + + def set_params(self, new_params: Union[paddle.Tensor, np.ndarray]) -> None: + r"""set parameters of the model. + + Args: + params: New parameters + """ + + if not isinstance(new_params, paddle.Tensor): + new_params = paddle.to_tensor(new_params, dtype='float32') + new_params = paddle.flatten(new_params) + + if new_params.shape[0] != self.order + 1: + raise ValueError(f"Incorrect number of params for the model: expect {self.order + 1}, received {new_params.shape[0]}") + + update_param = paddle.create_parameter( + shape=self.param.shape, + dtype='float32', + default_initializer=paddle.nn.initializer.Assign(new_params.reshape(self.param.shape)), + ) + + setattr(self, 'param', update_param) + + def _init_state_preparation_(self, X_data: paddle.Tensor, y_data: paddle.Tensor) -> State: + r"""Generate an initial state for compute inner product + """ + X_data, y_data = _dtype_transform_(X_data), _dtype_transform_(y_data) + Phi_data = 0 + for i in range(self.order + 1): + Phi_data += self.reg_param[i] * X_data.T[0] ** i + init_state = State((1/math.sqrt(2)) * paddle.concat((y_data, Phi_data))) + + return init_state + + def fit(self, X: Union[paddle.Tensor, np.ndarray], y: Union[paddle.Tensor, np.ndarray], + learning_rate: float = 0.01, iteration: int = 200, + saved_dir: str = '', model_name: str = 'poly' + ) -> None: + + r"""Fitting method used for training the model + + Args: + X: Independent data in a 2D array. + y: Dependent data in an 1D array. + learning_rate: Learning rate of optimization. Defaults to ``0.01``. + iteration: Total training iteration. Defaults to ``200``. + saved_dir: The path for saving the fitted data. Defaults to ``''``. + model_name: The model name. Defaults to ``poly``. + + Returns: + Trained model + """ + if not saved_dir: + saved_dir = './' + elif saved_dir[-1] != '/': + saved_dir += '/' + filename=f'{saved_dir}{model_name}.log' + polylog = _logger_init_('polylog', filename) + msg = ( + f"\n####################################################\n" + f"The model: {model_name} with following set-up:\n" + f"####################################################\n" + f"No. of qubits: {self.num_qubits - 1}; \n" + f"Raw model: y=a0 + a1*x^1 + a2*x^2 + ... + ak*x^k; \n" + f"Optimizer: Adam\n" + f" Initial params: {self.reg_param.tolist()}; \n" + f" Learning rate: {learning_rate}; \n" + f" No. of iteration: {iteration}.\n" + ) + polylog.info(msg) + + # hyper parameters + opt = paddle.optimizer.Adam(learning_rate=learning_rate, parameters=self.parameters()) + + # data processing + X_data, y_data = _data_transform_(X, y) + X_data, y_data = X_data[:2**(self.num_qubits-1), :], y_data[:2**(self.num_qubits-1)] + + # verifying input data + _data_verifying_(self.num_qubits, X_data, y_data) + + # initialize circuit + cir_inner_prod = Circuit(self.num_qubits) + cir_inner_prod.h(0) + + p_bar = tqdm( + total=iteration, + ascii=True, + dynamic_ncols=True, + ) + polylog.info("Optimization:") + for _ in range(iteration): + p_bar.update(1) + # fitting data and training parameters + init_state = self._init_state_preparation_(X_data, y_data) + loss = (1 - IPEstimator(cir_inner_prod, init_state))**2 + loss.backward() + opt.minimize(loss) + opt.clear_grad() + + if _ % int(iteration * 0.1) == 0: + msg = ( + f"Train loss: {loss.item():2.5f}; " + f"The model has been fitted. Score: {self.score(X, y):2.5f}; " + ) + polylog.info(msg) + + p_bar.close() + self.status = True + deter_score = self.score(X, y) + + msg = ( + f"\n####################################################\n" + f"Summary\n" + f"####################################################\n" + f"The fitting score: {deter_score:2f};\n" + f"The model params: {self.reg_param.tolist()}.\n" + ) + polylog.info(msg) + + paddle.save(self.state_dict(), f'{saved_dir}/{model_name}.pdparams') + msg = "The fitted model state_dict has been saved to '.pdparams' file." + polylog.info(msg) + polylog.info("="*25) + + def predict(self, X: Union[paddle.Tensor, np.ndarray]) -> Union[paddle.Tensor, np.ndarray]: + r"""Predict value based on current model parameters + + Args: + x: A sample of data in an array. + + Returns: + predicted value + """ + X = _dtype_transform_(X) + type_str = _type_fetch(X) + X = _type_transform(X, "tensor") + + predict_data = 0 + for i in range(self.order + 1): + predict_data += self.reg_param[i] * X.T[0] ** i + + return _type_transform(predict_data, type_str) + + def score(self, X: Union[paddle.Tensor, np.ndarray], y: Union[paddle.Tensor, np.ndarray], metric: Union[str, Callable] = "r2_score") -> float: + r"""Quantifying the quality of predictions given test set + + Args: + X: Independent data in a 2D array. Every column indicates an independent variable. + Every row indicates a sample of data. + y: Dependent data in a 1D array. + metric: The metric name for the quality. Defaults to ``r2``. If the metric is a callable function, the function should be in the expression + function(y_true: np.ndarray, y_pred: np.ndarray) -> float. + + Returns: + The model score. Based on sklearn.metric class. + """ + + if type(metric) == str: + if metric not in SKLEARN_REG_SCORER: + raise ValueError("The metric is not a valid sklearn.metrics.") + else: + scorer = SKLEARN_METRIC[metric] + else: + if type(metric) != Callable: + raise ValueError("The metric is not a valid Callable metric.") + else: + scorer = metric + + X, y = _type_transform(X, "numpy"), _type_transform(y, "numpy") + y_pred = self.predict(X) + + return scorer(y, y_pred) + + +if __name__ == '__main__': + exit(0) diff --git a/paddle_quantum/finance/__init__.py b/paddle_quantum/finance/__init__.py new file mode 100644 index 0000000..0d68e5a --- /dev/null +++ b/paddle_quantum/finance/__init__.py @@ -0,0 +1,26 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +The module of quantum finance. +""" + +from .finance import( + DataSimulator, + portfolio_optimization_hamiltonian, + portfolio_diversification_hamiltonian, + arbitrage_opportunities_hamiltonian +) +from .pricing import qae_alg, qae_cir, qmc_alg, EuroOptionEstimator diff --git a/paddle_quantum/finance.py b/paddle_quantum/finance/finance.py similarity index 100% rename from paddle_quantum/finance.py rename to paddle_quantum/finance/finance.py diff --git a/paddle_quantum/finance/pricing.py b/paddle_quantum/finance/pricing.py new file mode 100644 index 0000000..4814205 --- /dev/null +++ b/paddle_quantum/finance/pricing.py @@ -0,0 +1,264 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +Tools for QMC and option pricing. +""" + +import logging +import math +import numpy as np +import paddle + +from paddle_quantum.ansatz import Circuit +from paddle_quantum.intrinsic import get_dtype +from paddle_quantum.linalg import dagger +from paddle_quantum.loss import Measure +from paddle_quantum.qinfo import grover_generation, qft_generation +from typing import Tuple, Callable, List + + +__all__ = ['qae_cir', 'qae_alg', 'qmc_alg', 'EuroOptionEstimator'] + + +def qae_cir(oracle: paddle.Tensor, num_ancilla: int) -> Circuit: + r"""Create a QAE circuit based on the ``oracle`` given. + + Args: + oracle: input oracle. + num_ancilla: number of ancilla qubits used. + + Returns: + a circuit used for quantum amplitude estimation. + + """ + assert num_ancilla > 0 + grover = grover_generation(oracle) + sys_num_qubits = int(math.log2(oracle.shape[0])) + + # registers + aux_reg = list(range(num_ancilla)) + sys_reg = list(range(num_ancilla, sys_num_qubits + num_ancilla)) + + cir = Circuit(num_ancilla + sys_num_qubits) + cir.superposition_layer(aux_reg) + cir.oracle(oracle, sys_reg, latex_name=r'$\mathcal{A}$') + + for i in reversed(range(num_ancilla)): + cir.control_oracle(grover, [i] + sys_reg, + latex_name= r'$\mathcal{Q}^{2^' + str(num_ancilla - 1 - i) + r'}$') + grover = grover @ grover + + cir.oracle(dagger(qft_generation(num_ancilla)), aux_reg, latex_name=r'$QFT^\dagger$') + + return cir + + +def qae_alg(oracle: paddle.Tensor, num_ancilla: int) -> Tuple[Circuit, paddle.Tensor]: + r"""Quantum amplitude estimation (QAE) algorithm + + Args: + oracle: an :math:`n`-qubit oracle :math:`\mathcal{A}`. + num_ancilla: number of ancilla qubits used. + + Returns: + a QAE circuit and :math:`|\sin(2\pi\theta)|` + + Note: + :math:`\mathcal{A}` satisfies + + .. math:: + + \mathcal{A}|0^{\otimes n}\rangle=\cos(2\pi\theta)|0\rangle|\psi\rangle+\sin(2\pi\theta)|1\rangle|\phi\rangle. + + """ + complex_dtype = get_dtype() + op_M = Measure() + aux_reg = list(range(num_ancilla)) + + oracle = oracle.cast(complex_dtype) + cir = qae_cir(oracle, num_ancilla) + state = cir() + measured_result = paddle.argmax(op_M(state, qubits_idx=aux_reg)) + estimated_sin = paddle.abs(paddle.sin(measured_result / (2 ** num_ancilla) * math.pi)) + + return cir, estimated_sin + + +def __standardize_fcn(fcn: Callable[[float], float], list_input: List[float]) -> Tuple[float, float, List[float]]: + r"""Make input ``fcn`` discretized and normalized. + + Args: + fcn: input function, can be continuous. + domain: list of input data of ``fcn``. + + Returns: + maximum, minimum of output data, and a list of output data of normalized ``fcn`` + + """ + list_output = [fcn(x) for x in list_input] + maximum, minimum = max(list_output), min(list_output) + list_output = (np.array(list_output, dtype='float64') - minimum) / (maximum - minimum) + return maximum, minimum, list_output.tolist() + + +def __u_d(list_output: List[float]) -> paddle.Tensor: + dimension = len(list_output) + complex_dtype = get_dtype() + + def reflection(x: float) -> np.ndarray: + return np.array([[math.sqrt(1 - x), math.sqrt(x)], + [math.sqrt(x), -1 * math.sqrt(1 - x)]], dtype=complex_dtype) + + def ket_bra(j: int) -> np.ndarray: + mat = np.zeros([dimension, dimension]) + mat[j, j] += 1 + return mat + + mat = sum(np.kron(reflection(list_output[j]), ket_bra(j)) for j in range(dimension)) + return paddle.to_tensor(mat, dtype=complex_dtype) + + +def __u_p(list_prob: List[float]) -> paddle.Tensor: + dimension = len(list_prob) + list_prob = np.array(list_prob) + + # construct basis + max_index = np.argmax(list_prob).item() + mat = np.eye(dimension) + mat[:, max_index] = np.sqrt(list_prob) + mat[:, [max_index, 0]] = mat[:, [0, max_index]] + + # qr decomposition + Q, _ = np.linalg.qr(mat) + return paddle.to_tensor(Q * -1, dtype=get_dtype()) + + +def qmc_alg(fcn: Callable[[float], float], list_prob: List[float], + num_ancilla: int = 6) -> Tuple[Circuit, paddle.Tensor]: + r"""Quantum Monte Carlo (QMC) algorithm. + + Args: + fcn: real-valued function :math:`f` applied to a random variable :math:`X`. + list_prob: probability distribution of :math:`X`, where the j-th probability corresponds to the j-th outcome. + num_ancilla: number of ancilla qubits used. Defaults to be ``6``. + + Returns: + a QAE circuit and an estimation of :math:`\mathbb{E}[f(X)]`. + + """ + num_qubits = math.ceil(math.log2(len(list_prob))) + dimension = 2 ** num_qubits + list_input = list(range(dimension)) + list_prob += [0] * (dimension - len(list_prob)) + + f_max, f_min, list_standard_output = __standardize_fcn(fcn, list_input) + oracle = __u_d(list_standard_output) @ paddle.kron(paddle.eye(2), __u_p(list_prob)) + cir, val = qae_alg(oracle, num_ancilla) + return cir, (val ** 2) * (f_max - f_min) + f_min + + +class EuroOptionEstimator(object): + r"""European option pricing estimator. + + Args: + initial_price: initial price of the asset. + strike_price: pre-fixed price of the asset. + interest_rate: risk-free interest rate. + volatility: dispersion of returns for the asset. + maturity_date: date when option is expired. + degree_of_estimation: degree of price estimation. Defaults to be ``5``. + + Note: + Option price is evaluated under + the `Black-Scholes-Merton model `_. + + + """ + def __init__(self, initial_price: float, strike_price: float, + interest_rate: float, volatility: float, maturity_date: float, + degree_of_estimation: int = 5) -> None: + + logging.basicConfig( + filename='./euro_pricing.log', + filemode='w', + format='%(asctime)s %(levelname)s %(message)s', + level=logging.INFO + ) + + logging.info( + "Received the following information of this asset:" + ) + logging.info( + f" initial price {initial_price}, strike price: {strike_price}," + ) + logging.info( + f" interest_rate: {interest_rate}, volatility: {volatility}, maturity date: {maturity_date}." + ) + logging.info("Begin initialization.") + + x_max = 5 * math.sqrt(maturity_date) + sample_points = np.linspace(start=-x_max, stop=x_max, num=2**degree_of_estimation) + + logging.info( + f" uniformly sampling {len(sample_points)} points between -{x_max:<.3f} and {x_max:<.3f};") + + bs_model_fcn = lambda x: \ + initial_price * math.exp(volatility * x + (interest_rate - 0.5 * (volatility ** 2)) * maturity_date) - strike_price + normal_pdf = lambda x: \ + np.exp(-1 * (x ** 2) / 2 / maturity_date) / math.sqrt(2 * math.pi * maturity_date) + list_prob = normal_pdf(sample_points) + + self.__fcn = lambda j: max(0, bs_model_fcn(sample_points[j])) * math.exp(-1 * interest_rate * maturity_date) + + logging.info( + " the function of random variable has been set up;") + + self.__list_prob = (list_prob / np.sum(list_prob)).tolist() + + logging.info( + " the probability distribution of random variable has been set up;") + + self.__cir = None + + logging.info( + " the quantum circuit has been set up.") + + def estimate(self) -> float: + r"""Estimate the European option price using Quantum Monte Carlo (QMC) methods. + + Returns: + Risk-neutral price of the given asset. + + """ + logging.info("Begin estimation.") + cir, estimated_expectance = qmc_alg(self.__fcn, self.__list_prob) + self.__cir = cir + estimated_expectance = estimated_expectance.item() + logging.info( + f" retrieve estimation result {estimated_expectance:<.5f} from QMC algorithm.") + return estimated_expectance + + def plot(self, dpi: int = 100) -> None: + r"""Plot the quantum circuit used in pricing. + + Args: + dpi: image clarity of the plotted circuit. Defaults to be ``200``. + + """ + if self.__cir is None: + raise UserWarning( + "You need to estimate the option first before plotting the circuit.") + self.__cir.plot(dpi=dpi) diff --git a/paddle_quantum/finance/qpo.py b/paddle_quantum/finance/qpo.py new file mode 100644 index 0000000..27292ad --- /dev/null +++ b/paddle_quantum/finance/qpo.py @@ -0,0 +1,117 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +Quantum portfolio optimization tools. +""" +from typing import List, Union, Tuple, Optional +import logging +import warnings +import numpy as np +import paddle +import paddle_quantum as pq +from paddle_quantum.ansatz import Circuit +from paddle_quantum.finance import DataSimulator, portfolio_optimization_hamiltonian +from tqdm import tqdm + + +def portfolio_combination_optimization( + num_asset: int, + data: Union[pq.finance.DataSimulator, Tuple[paddle.Tensor, paddle.Tensor]], + iter: int, + lr: Optional[float] = None, + risk: float = 0.5, + budget: int = None, + penalty: float = None, + circuit: Union[pq.ansatz.Circuit, int] = 2, + init_state: Optional[pq.state.State] = None, + optimizer: Optional[paddle.optimizer.Optimizer] = None, + measure_shots: int = 2048, + logger: Optional[logging.Logger] = None, + compare: bool = False, +) -> List[int]: + r"""A highly encapsuled method of the portfolio combination optimization. + + Args: + num_asset: the number of investable asset. + data: stock data, a `DataSimulator` or `tuple` (covariance_matrix, return_rate_vector). + iter: number of optimization cycles. + lr: learning rate. + risk: the coeffcient of risk. + budget: investment budget, or the maximum counts of projects we invest. + penalty: the weight of regular terms. + circuit: the `Circuit` we use to inference. If `int` is input, we will construct `complex_entangled_layer` that times. + init_state: the initial state of inference circuit, default to be the product state of zero states. + optimizer: the `paddle.optimizer.Optimizer` instance, default to be `paddle.optimizer.Adam`. + measure_shots: the times we measure the end state, default to be 2048. + logger: `logging.Logger` instance for detail record. + compare: whether compare the loss of end state with the loss of ground state. This will be costly when `num_asset` too large. + + Returns: + investment_plan: the optimal investment strategy as a list. + + Note: + This function is only applied to a well defined problem introduced in + `Portfolio Optimization `_. + """ + if isinstance(data, pq.finance.DataSimulator): + covar = data.get_asset_return_covariance_matrix() + return_rate = data.get_asset_return_mean_vector() + elif isinstance(data, Tuple): + covar, return_rate = data + + if not budget: + budget = num_asset // 2 + if not penalty: + penalty = num_asset + + hamiltonian = portfolio_optimization_hamiltonian(penalty, return_rate, covar, risk, budget) + loss_func = pq.loss.ExpecVal(hamiltonian) + num_qubits = num_asset + if not init_state: + init_state = pq.state.zero_state(num_qubits) + + if isinstance(circuit, int): + depth = circuit + circuit = Circuit(num_qubits) + circuit.complex_entangled_layer(depth=depth) + + if not optimizer: + opt = paddle.optimizer.Adam(learning_rate=lr, parameters=circuit.parameters()) + + for itr in tqdm(range(iter)): + state = circuit(init_state) + loss = loss_func(state) + loss.backward() + opt.minimize(loss) + opt.clear_grad() + if logger: + logger.info(f'iters: {itr} loss: {float(loss):.7f}') + + final_state = circuit(init_state) + prob_measure = final_state.measure(shots = measure_shots) + investment = max(prob_measure, key = prob_measure.get) + investment_plan = [enum + 1 for enum, flag in enumerate(list(investment)) if flag == '1'] + + if compare: + if not logger: + raise RuntimeError('if compared, logger must exist') + if num_qubits > 12: + warnings.warn('comparison beyond 12 qubits will cost unexpected time.') + hc_mat = hamiltonian.construct_h_matrix() + logger.info(f'the loss of ground state: {float(np.linalg.eigvalsh(hc_mat)[0]):.7f}') + logger.info(f'the loss of ours: {float(loss):.7f}') + + return investment_plan diff --git a/paddle_quantum/gate/base.py b/paddle_quantum/gate/base.py index 7f986d8..9df7509 100644 --- a/paddle_quantum/gate/base.py +++ b/paddle_quantum/gate/base.py @@ -107,13 +107,13 @@ def theta_generation(self, param: Union[paddle.Tensor, float, List[float]], para param_shape: shape for theta Note: - in the following cases ``param`` will be transformed to a parameter: + In the following cases ``param`` will be transformed to a parameter: - ``param`` is None - in the following cases ``param`` will be added to the parameter list: - - ``param`` is ParamBase - in the following cases ``param`` will keep unchange: - - ``param`` is a Tensor but not a parameter - - ``param`` is a float or list of floats + In the following cases ``param`` will be added to the parameter list: + - ``param`` is a ParamBase + In the following cases ``param`` will keep unchanged: + - ``param`` is a Tensor but not a ParamBase + - ``param`` is a float or a list of floats """ @@ -158,7 +158,7 @@ def gate_history_generation(self) -> None: def display_in_circuit(self, ax: matplotlib.axes.Axes, x: float,) -> float: r'''The display function called by circuit instance when plotting. - Argrs: + Args: ax: the ``matplotlib.axes.Axes`` instance x: the start horizontal position diff --git a/paddle_quantum/hamiltonian.py b/paddle_quantum/hamiltonian.py index 4e3dd90..931d8b8 100644 --- a/paddle_quantum/hamiltonian.py +++ b/paddle_quantum/hamiltonian.py @@ -24,6 +24,7 @@ from scipy import sparse import paddle import paddle_quantum +import openfermion class Hamiltonian: @@ -201,7 +202,7 @@ def __decompose(self): Raises: Exception: Operators should be defined with a string composed of Pauli operators followed by qubit index on which it act, separated with ",". i.e. "Z0, X1" - Notes: + Note: This is an intrinsic function, user do not need to call this directly This is a fundamental function, it decomposes the input Pauli string into different forms and stores them into private variables. """ @@ -253,7 +254,7 @@ def __decompose(self): def __compress(self): r"""combine like terms - Notes: + Note: This is an intrinsic function, user do not need to call this directly """ if self.__update_flag: @@ -328,6 +329,19 @@ def construct_h_matrix(self, qubit_num: Optional[int] = None) -> np.ndarray: h_matrix += op return h_matrix + @classmethod + def from_qubit_operator(cls, qubitOp: openfermion.QubitOperator): + pauli_strs = [] + for xyz_tuple, coef in qubitOp.terms.items(): + if len(xyz_tuple) == 0: + pauli_strs.append((coef, "I")) + else: + term = ", ".join( + [f"{g:s}{i:d}" for i, g in xyz_tuple] + ) + pauli_strs.append((coef, term)) + return cls(pauli_strs) + class SpinOps: r"""The spin operators in matrix forms, could be used to construct Hamiltonian matrix or spin observables. diff --git a/paddle_quantum/intrinsic.py b/paddle_quantum/intrinsic.py index 44e3f00..a21442a 100644 --- a/paddle_quantum/intrinsic.py +++ b/paddle_quantum/intrinsic.py @@ -38,6 +38,8 @@ def _format_qubits_idx( qubits_idx: Union[Iterable[Iterable[int]], Iterable[int], int, str], num_qubits: int, num_acted_qubits: int = 1 ) -> Union[List[List[int]], List[int]]: + assert not (isinstance(qubits_idx, str) and num_qubits is None), \ + f"Cannot specify the qubit indices when num_qubits is None: received qubit_idx {qubits_idx} and num_qubits {num_qubits}" if num_acted_qubits == 1: if qubits_idx == 'full': qubits_idx = list(range(0, num_qubits)) diff --git a/paddle_quantum/linalg.py b/paddle_quantum/linalg.py index 720850b..d46cbbe 100644 --- a/paddle_quantum/linalg.py +++ b/paddle_quantum/linalg.py @@ -21,9 +21,10 @@ import math import numpy as np import scipy +import itertools from scipy.stats import unitary_group from functools import reduce -from typing import Optional, Union, Callable +from typing import Optional, Union, Callable, List import paddle_quantum as pq from .intrinsic import _get_float_dtype @@ -37,7 +38,7 @@ def abs_norm(mat: Union[np.ndarray, paddle.Tensor, State]) -> float: mat: matrix Returns: - norm of mat + norm of input matrix """ mat = _type_transform(mat, "tensor") @@ -70,13 +71,28 @@ def is_hermitian(mat: Union[np.ndarray, paddle.Tensor], eps: Optional[float] = 1 determine whether :math:`P - P^\dagger = 0` """ - mat = _type_transform(mat, "tensor") + mat = _type_transform(mat, "tensor").cast('complex128') shape = mat.shape if len(shape) != 2 or shape[0] != shape[1] or math.log2(shape[0]) != math.ceil(math.log2(shape[0])): # not a mat / not a square mat / shape is not in form 2^num_qubits x 2^num_qubits return False return abs_norm(mat - dagger(mat)) < eps +def is_positive(mat: Union[np.ndarray, paddle.Tensor], eps: Optional[float] = 1e-6) -> bool: + r""" verify whether ``mat`` is a positive semi-definite matrix. + + Args: + mat: positive operator candidate :math:`P` + eps: tolerance of error + + Returns: + determine whether :math:`P` is Hermitian and eigenvalues are non-negative + + """ + if is_hermitian(mat, eps): + mat = _type_transform(mat, "tensor").cast('complex128') + return (min(paddle.linalg.eigvalsh(mat)) >= -eps).item() + return False def is_projector(mat: Union[np.ndarray, paddle.Tensor], eps: Optional[float] = 1e-6) -> bool: r""" verify whether ``mat`` is a projector @@ -89,7 +105,7 @@ def is_projector(mat: Union[np.ndarray, paddle.Tensor], eps: Optional[float] = 1 determine whether :math:`PP - P = 0` """ - mat = _type_transform(mat, "tensor") + mat = _type_transform(mat, "tensor").cast('complex128') shape = mat.shape if len(shape) != 2 or shape[0] != shape[1] or math.log2(shape[0]) != math.ceil(math.log2(shape[0])): # not a mat / not a square mat / shape is not in form 2^num_qubits x 2^num_qubits @@ -444,3 +460,105 @@ def herm_transform(fcn: Callable[[float], float], mat: Union[paddle.Tensor, np.n continue mat += (fcn(eigval[i]) + 0j) * vec @ dagger(vec) return mat.numpy() if type_str == "numpy" else mat + + +def pauli_basis_generation(num_qubits: int) -> List[paddle.Tensor]: + r"""Generate a Pauli basis. + + Args: + num_qubits: the number of qubits :math:`n`. + + Returns: + The Pauli basis of :math:`\mathbb{C}^{2^n \times 2^n}`. + + """ + + def __single_pauli_basis() -> List[paddle.Tensor]: + r"""The Pauli basis in single-qubit case. + """ + complex_dtype = pq.get_dtype() + I = paddle.to_tensor([[0.5, 0], + [0, 0.5]], dtype=complex_dtype) + X = paddle.to_tensor([[0, 0.5], + [0.5, 0]], dtype=complex_dtype) + Y = paddle.to_tensor([[0, -0.5j], + [0.5j, 0]], dtype=complex_dtype) + Z = paddle.to_tensor([[0.5, 0], + [0, -0.5]], dtype=complex_dtype) + return [I, X, Y, Z] + + def __basis_kron(basis_A: List[paddle.Tensor], basis_B: List[paddle.Tensor]) -> List[paddle.Tensor]: + r"""Kronecker product between bases + """ + return [ + paddle.kron(basis_A[i], basis_B[j]) + for i, j in itertools.product(range(len(basis_A)), range(len(basis_B))) + ] + + + list_bases = [__single_pauli_basis() for _ in range(num_qubits)] + if num_qubits == 1: + return list_bases[0] + + return reduce(lambda result, index: __basis_kron(result, index), list_bases[2:], __basis_kron(list_bases[0], list_bases[1])) + + +def pauli_decomposition(mat: Union[np.ndarray, paddle.Tensor]) -> Union[np.ndarray, paddle.Tensor]: + r"""Decompose the matrix by the Pauli basis. + + Args: + mat: the matrix to be decomposed + + Returns: + The list of coefficients corresponding to Pauli basis. + + """ + type_str = _type_fetch(mat) + mat = _type_transform(mat, "tensor") + + dimension = mat.shape[0] + num_qubits = int(math.log2(dimension)) + assert 2 ** num_qubits == dimension, \ + f"Input matrix is not a valid quantum data: received shape {mat.shape}" + + basis = pauli_basis_generation(num_qubits) + decomp = paddle.concat([paddle.trace(mat @ basis[i]) for i in range(dimension ** 2)]) + return _type_transform(decomp, type_str) + + +def subsystem_decomposition(mat: Union[np.ndarray, paddle.Tensor], + first_basis: Union[List[np.ndarray], List[paddle.Tensor]], + second_basis: Union[List[np.ndarray], List[paddle.Tensor]], + inner_prod: Union[Callable[[np.ndarray, np.ndarray], np.ndarray], + Callable[[paddle.Tensor, paddle.Tensor], paddle.Tensor]] + ) -> Union[np.ndarray, paddle.Tensor]: + r"""Decompose the input matrix by two given bases in two subsystems. + + Args: + mat: the matrix :math:`w` to be decomposed + first_basis: a basis :math:`\{e_i\}_i` from the first space + second_basis: a basis :math:`\{f_j\}_j` from the second space + inner_prod: the inner product of these subspaces + + Returns: + a coefficient matrix :math:`[\beta_{ij}]` such that :math:`w = \sum_{i, j} \beta_{ij} e_i \otimes f_j`. + + """ + type_str = _type_fetch(mat) + mat = _type_transform(mat, "tensor") + + if type_str == "numpy": + first_basis = [paddle.to_tensor(ele) for ele in first_basis] + second_basis = [paddle.to_tensor(ele) for ele in second_basis] + + assert mat.shape == paddle.kron(first_basis[0], second_basis[0]).shape, \ + f"The shape does not agree: received {mat.shape, first_basis[0].shape, second_basis[0].shape}" + + first_dim, second_dim = len(first_basis), len(second_basis) + coef = [ + inner_prod(paddle.kron(first_basis[i], second_basis[j]), mat) + for i, j in itertools.product(range(first_dim), range(second_dim)) + ] + coef = paddle.concat(coef).reshape([first_dim, second_dim]) + + return _type_transform(coef, type_str) diff --git a/paddle_quantum/loss/measure.py b/paddle_quantum/loss/measure.py index 8ae1706..acb40e9 100644 --- a/paddle_quantum/loss/measure.py +++ b/paddle_quantum/loss/measure.py @@ -102,7 +102,8 @@ def forward(self, state: paddle_quantum.State) -> paddle.Tensor: return expec_val num_qubits = state.num_qubits - expec_val = paddle.zeros([1]) + float_dtype = _get_float_dtype(paddle_quantum.get_dtype()) + expec_val = paddle.zeros([1], dtype=float_dtype) state_data = state.data for i in range(0, self.num_terms): pauli_site = self.sites[i] diff --git a/paddle_quantum/model.py b/paddle_quantum/model.py new file mode 100644 index 0000000..e9795e9 --- /dev/null +++ b/paddle_quantum/model.py @@ -0,0 +1,806 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +General model templates for Quantum Neural Network +""" + +import matplotlib.pyplot as plt +import time +import rich +from rich.progress import ( + Progress, TextColumn, BarColumn, + SpinnerColumn, TimeElapsedColumn, TimeRemainingColumn +) +import numpy as np +import math +from typing import Iterable, List, Callable, Any, Tuple, Union + +import paddle +from .ansatz import Sequential, Circuit +from .base import get_dtype +from .state import State +from .intrinsic import _get_float_dtype + + +__all__ = [ + 'rcParams', 'reset_settings', 'random_batch', + 'NullScheduler', 'Model', 'OptModel', 'LearningModel', 'EncodingNetwork', 'EncodingModel' +] + + +# General settings for the QNN model +rcParams = { + # settings for the optimizer + 'optimizer': 'Adam', # optimizer in PaddlePaddle + 'scheduler': 'StepDecay', # scheduler in PaddlePaddle: can be set to None. + 'learning_rate': 0.2, # (initial) learning rate of the optimizer + 'scheduler_args': 100, # arguments of the scheduler other than learning rate + + # settings for the training + 'num_itr': 200, # number of iterations during the training + 'num_print': 10, # number of messages printed during the training + 'print_digits': 6, # number of decimal digits for printed number + 'print_style': 'uniform', # the style of how printed iterations are arranged: can be 'exponential' + + # settings for the fitting + 'num_epoch': 5, # number of epochs during the fitting + 'test_frequency': 1, # frequency of evaluating test data set during the fitting + 'batch_size': None # size of trained data for each epoch. Defaults to be half. +} + + +def reset_settings() -> None: + r"""Set the current ``rcParams`` to Default settings. + """ + global rcParams + rcParams.update({ + 'optimizer': 'Adam', 'scheduler': 'StepDecay', 'learning_rate': 0.1, 'scheduler_args': 20, + 'num_itr': 200, 'num_print': 10, 'print_digits': 6, 'print_style': 'uniform', + 'num_epoch': 5, 'num_tests': 5, 'batch_size': None + }) + + +def random_batch(data: Iterable, label: Iterable, batch_size: int = None) -> Tuple[List, List]: + r"""Randomly return a data batch from dataset and label set + + Args: + data: dataset + label: label set + batch_size: size of data batch. Defaults to be half of the data size. + + Returns: + a random data batch + + """ + size_data = len(data) + batch_idx = np.random.choice(list(range(size_data)), + size=size_data // 2 if batch_size is None else batch_size, + replace=False) + + data_batch, label_batch = [], [] + for idx in batch_idx: + data_batch.append(data[idx]) + label_batch.append(label[idx]) + + if isinstance(label, np.ndarray): + return data_batch, np.concatenate(label_batch) + if isinstance(label, paddle.Tensor): + return data_batch, paddle.concat(label_batch) + return data_batch, label_batch + + +class NullScheduler(paddle.optimizer.lr.LRScheduler): + r"""An empty scheduler class, for users who expect no schedulers in Model. Can be activated by command + + .. code:: + + from paddle_quantum.model import rcParams + rcParams['scheduler'] = None + + """ + def get_lr(self): + return self.base_lr + + +class Model(object): + r"""General template for models of QNN + + Args: + network: a quantum neural network + name: name of this model. Default to be ``"Model"`` + + """ + def __init__(self, network: paddle.nn.Layer, name: str = "Model") -> None: + self.network = network + self.name = name + self.__prepared = False + self.loss_data, self.metric_data = [], [] + + def clear_data(self) -> None: + r"""Clear the current data + """ + self.loss_data, self.metric_data = [], [] + + def parameters(self) -> List[paddle.fluid.framework.ParamBase]: + r"""Return the parameters of this network + """ + return self.network.parameters() + + def __validate_settings(self) -> None: + r"""Assert whether rcParams can be a valid setting in Model + """ + setting_list = ['num_itr', 'num_print', 'print_digits', 'num_epoch', 'test_frequency', 'batch_size'] + # assert non-negative + if any((rcParams[key] is not None) and (rcParams[key] < 0) for key in setting_list): + raise ValueError( + "Received negative numbers: check rcParams.") + + # assert inequality + if rcParams['num_print'] >= rcParams['num_itr']: + raise ValueError( + "The number of messages cannot be larger than the number of iterations: check rcParams.") + + def __print_itr_generation(self, num_itr: int, num_print: int): + r"""Determine the list of iterations to be printed during the training process + """ + if num_itr < 1: + return [] + + if rcParams['print_style'] == 'exponential': + # print list is generated by poisson distribution + lamb = 4 * math.log(2) / num_itr + poisson = lambda x: lamb * math.exp(-lamb * x) + list_itr = list(range(1, num_itr)) + print_prob = [poisson(itr) for itr in list_itr] + print_prob /= np.sum(print_prob) + print_list = np.sort(np.random.choice(list_itr, size=num_print - 1, replace=False, p=print_prob)).tolist() + print_list.append(num_itr) + else: + # print list is uniformly distributed + print_ratio = num_itr / num_print + print_list = list(filter(lambda itr: itr % print_ratio < 1, list(range(1, num_itr + 1)))) + print_list = print_list + [num_itr] if print_list[-1] != num_itr else print_list + + return print_list + + def prepare(self, loss_fcn: Callable[[Union[State, Circuit, Sequential], Any], Any], + metric_fcn: Callable[[Union[Circuit, Sequential]], float] = None, + metric_name: str = None) -> None: + r"""General function setup for QNN + + Args: + loss_fcn: loss function for the QNN + metric_fcn: metric function for the QNN, which does not mess with the training process. Defaults to be ``None`` + metric_name: name of the metric function. Defaults to be ``None`` + + Raises: + ValueError: the output datatype of metric function must be float + + Note: + The prepare function will also take the settings of ``rcParams`` + + """ + # we cannot generally check loss function since the label input is unknown + self._loss_fcn = loss_fcn + + # sanity check for metric function + if metric_fcn is not None: + data = metric_fcn(self.network) + if not isinstance(data, float): + raise ValueError( + f"The output data of print function must be a float: received {type(data)}.") + if metric_name is None: + raise ValueError( + "The metric_name cannot be empty for a metric function.") + + self._metric_fcn, self._metric_name = metric_fcn, metric_name + + # setup the scheduler and optimizer + self.__sch = NullScheduler(learning_rate=rcParams['learning_rate']) if rcParams['scheduler'] is None else \ + getattr(paddle.optimizer.lr, rcParams['scheduler'])(rcParams['learning_rate'], rcParams['scheduler_args']) + self.__opt = getattr(paddle.optimizer, rcParams["optimizer"])(learning_rate=self.__sch, parameters=self.parameters()) + + # take the setting of rcParams + self.__validate_settings() + self.__print_list = self.__print_itr_generation(rcParams['num_itr'], rcParams['num_print']) + self.__num_itr, self.__print_digits = rcParams['num_itr'], rcParams['print_digits'] + self.__num_epoch, self.__test_frequency, self.__batch_size = rcParams['num_epoch'], rcParams['test_frequency'], rcParams['batch_size'] + + self.__prepared = True + + def check_prepared(self) -> None: + r"""Assert whether Model is prepared for training + """ + assert self.__prepared, \ + f"The model was not set properly: run the prepare function of {self.name} first." + + def __print_loss_message(self, loss: float, metric: float, metric_name: str, + itr: int, digits: int, itr_max_digits: int) -> None: + r"""Message print function used in loss training + """ + loss = f'% .{digits}f' % loss + + if metric_name is not None: + metric = f'% .{digits}f' % metric + + diff_digits = itr_max_digits - math.floor(math.log10(itr)) + itr = str(itr) + ''.join([' ' for _ in range(diff_digits)]) + rich.print(f" iter: [magenta]{itr}[/magenta] [i]loss[/i]: [red]{loss}[/red]", + "" if metric_name is None else f"with [i]{metric_name}[/i] as [green]{metric}[/green]") + + def __print_summary_message(self, best_loss: float, best_loss_itr: int, + best_metric: float, best_metric_itr: int, metric_name: str, + model_name: str, total_time: float, num_itr: int) -> None: + r"""Message print function used after loss training + """ + rich.print(f" the [u]best [i]loss[/i][/u] is {best_loss} existing at iter [magenta]{best_loss_itr}[/magenta];") + if metric_name is not None: + rich.print(f" the [u]best [i]{metric_name}[/i][/u] is {best_metric} existing at iter [magenta]{best_metric_itr}[/magenta];") + + avg_time = round(total_time / num_itr, 3) + total_time = round(total_time, 3) + rich.print(f" {model_name} took [gold3]{total_time}[/gold3] seconds", + f"with [gold3]{avg_time}[/gold3] seconds per iteration in average.\n") + + def train(self, loss_generator: Callable[[Any], Any] = None) -> Union[List[float], Tuple[List[float], List[float]]]: + r"""General template for QNN training + + Args: + loss_generator: loss generator of the QNN, with ``Model.network`` as input. Defaults to ``None`` i.e. + use the loss function defined in ``Model.prepare`` + + Returns: + contains the following elements: + + - a list of loss values + - a list of metric values if a metric function is given + + """ + loss_generator = self._loss_fcn if loss_generator is None else loss_generator + num_itr, print_list, digits = self.__num_itr, self.__print_list.copy(), self.__print_digits + metric_name = self._metric_name + + loss_list, metric_list = [], [] + best_loss, best_loss_itr = float('inf'), 0 + metric, best_metric, best_metric_itr = None, 0, 0 + + itr_max_digits = math.floor(math.log10(num_itr)) + + # start training + print() + start_time = time.time() + with Progress(TextColumn("[progress.description]{task.description}"), + SpinnerColumn(), + BarColumn(), + TextColumn("[progress.percentage]{task.percentage:>3.0f}%"), + TimeRemainingColumn(), + TimeElapsedColumn()) as progress: + train_tqdm = progress.add_task(description="Training...", total=num_itr) + for itr in range(1, num_itr + 1): + + # evaluate loss + loss = loss_generator(self.network) + loss.backward() + + # evaluate and update metric + if metric_name is not None: + metric = self._metric_fcn(self.network) + metric_list.append(metric) + if best_metric <= metric: + best_metric, best_metric_itr = metric, itr + + self.__opt.minimize(loss) + self.__opt.clear_grad() + self.__sch.step() + + # update loss + loss = loss.item() + loss_list.append(loss) + if loss <= best_loss: + best_loss, best_loss_itr = loss, itr + + if itr in print_list: + self.__print_loss_message(loss, metric, metric_name, itr, digits, itr_max_digits) + print_list.pop(0) + + progress.advance(train_tqdm, advance=1) + + total_time = time.time() - start_time + self.__print_summary_message(best_loss, best_loss_itr, best_metric, best_metric_itr, + metric_name, self.name, total_time, num_itr) + + return loss_list if metric_name is None else (loss_list, metric_list) + + def evaluate(self, loss_generator: Callable[[Any], Any] = None) -> Union[float, Tuple[float, float]]: + r"""General template for QNN evaluation + + Args: + loss_generator: loss generator of the QNN, with ``Model.network`` as input. Defaults to ``None`` i.e. + use the loss function defined in ``Model.prepare`` + + Returns: + contains the following elements: + + - a loss value + - a metric value if a metric function is given + + """ + loss_generator = self._loss_fcn if loss_generator is None else loss_generator + loss = loss_generator(self.network).item() + return loss if self._metric_name is None else (loss, self._metric_fcn(self.network)) + + def fit(self, train_data: Iterable, train_label: Iterable, + test_data: Iterable, test_label: Iterable) -> None: + r"""General template for QNN data fitting + + Args: + train_data: data of the train set + train_label: label of the train set + test_data: data of the test set + test_label: label of the test set + + """ + train_size, test_size = len(train_data), len(test_data) + num_epoch, test_frequency = self.__num_epoch, self.__test_frequency, + batch_size = max(train_size // num_epoch, 2) if self.__batch_size is None else self.__batch_size + + assert batch_size <= train_size, \ + f"The batch size cannot be larger than the size of train dataset: \ + received {batch_size}, expected no larger than {train_size}" + + rich.print(f"\nFitting of {self.name} starts under setting: \n", + f" size of train data: [medium_violet_red]{train_size}[/medium_violet_red],", + f"size of test data: [medium_violet_red]{test_size}[/medium_violet_red],", + f"size of batches: [medium_violet_red]{batch_size}[/medium_violet_red]. \n") + + self.clear_data() + for epoch in range(1, num_epoch + 1): + data_batch, label_batch = random_batch(train_data, train_label, batch_size) + rich.print(f"[bold bright_blue]Epoch {epoch}[/bold bright_blue] of {self.name} begins:") + result = self.train_batch(data_batch, label_batch) + + if self._metric_name is None: + self.loss_data.extend(result) + else: + self.loss_data.extend(result[0]) + self.metric_data.extend(result[1]) + + if epoch % test_frequency < 1 or epoch == num_epoch: + evaluation = self.eval_batch(test_data, test_label) + evaluation = evaluation if self._metric_name is None else evaluation[0] + rich.print(f"TEST: the [i]loss[/i] of the test set is [u bold yellow]{evaluation}[/u bold yellow].\n") + + def plot(self, include_metric: bool, apply_log: bool, has_epoch: bool) -> None: + r"""Plot the trained data + + Args: + include_metric: whether include the metric data + apply_log: whether apply the log function on the data + has_epoch: whether the data list is composed by data in several epochs + + """ + loss_data, metric_data = np.array(self.loss_data), np.array(self.metric_data) if include_metric else None + if apply_log: + assert all(i > 0 for i in loss_data.flatten()), \ + "Cannot apply log function on non-positive loss: check your loss data" + loss_data = np.log(loss_data) + + if include_metric: + assert all(i > 0 for i in metric_data.flatten()), \ + "Cannot apply log function on non-positive metric: check your metric data" + metric_data = np.log(metric_data) + + data_size, min_loss, max_loss = len(loss_data), min(loss_data), max(loss_data) + assert (not include_metric) or data_size == len(metric_data), \ + "The metric data size does not agree with the loss data." + + x_list = list(range(data_size)) + plt.figure(figsize=[16, 9]) + plt.xlabel("iterations") + plt.plot(x_list, loss_data, color="blue", ls="-", label="loss") + if include_metric: + plt.plot(x_list, metric_data, color="red", ls="-", label=self._metric_name) + if has_epoch and self.__num_epoch > 1: + epoch_itr = list(range(0, data_size, self.__num_itr)) + epoch_itr.pop(0) + for itr in epoch_itr: + plt.axvline(itr, ls=':', color='black') + plt.legend(prop={"size": 12}) + plt.show() + + +class OptModel(Model): + r"""Class for optimization-based QNN model + + Args: + circuit: a ``Circuit`` instance ready to be optimized + name: name of this model. Default to be ``"OptModel"`` + + """ + def __init__(self, circuit: Circuit, name: str = "OptModel") -> None: + super().__init__(network=circuit, name=name) + + def prepare(self, loss_fcn: Callable[[Circuit, Any], paddle.Tensor], + metric_fcn: Callable[[Union[Circuit, Sequential]], float] = None, + metric_name: str = None, *loss_args: Any) -> None: + r"""Prepare and check the function setup for optimized-based QNN + + Args: + loss_fcn: loss function for the QNN + metric_fcn: metric function for the QNN, which does not mess with the training process. Defaults to be ``None`` + metric_name: name of the metric function. Defaults to be ``None`` + loss_args: function arguments for loss_fcn other than QNN input + + Raises: + ValueError: the output datatype of loss function must be paddle.Tensor + + Note: + The prepare function will also take the settings of ``rcParams`` + + """ + # since optimization-based model has no labels, we can check the validity of loss function + data = paddle.squeeze(loss_fcn(self.network, *loss_args)) + if not isinstance(data, paddle.Tensor) and data.shape == []: + raise ValueError( + f"The output data of loss function must be a tensor: received type {type(data)} and shape {data.shape}.") + + # fix loss function by loss arguments + fixed_loss_fcn = lambda network: loss_fcn(network, *loss_args) + return super().prepare(fixed_loss_fcn, metric_fcn, metric_name) + + def optimize(self) -> Union[List[float], Tuple[List[float], List[float]]]: + r"""Optimize the circuit in terms of the loss function + + Returns: + contains the following elements: + + - a list of loss values + - a list of metric values if a metric function is given + + """ + self.check_prepared() + print(f"\nTraining of {self.name} begins:") + result = super().train() + + # log data + if self._metric_name is None: + self.loss_data = result + else: + self.loss_data, self.metric_data = result + return result + + def evaluate(self) -> Union[float, Tuple[float, float]]: + r"""Evaluate the loss and metric value of the current QNN + + Returns: + contains the following elements: + + - a loss value + - a metric value if a metric function is given + + """ + self.check_prepared() + return super().evaluate() + + def fit(self) -> None: + r""" + Raises: + NotImplementedError: Optimization model does not have fit function + + """ + raise NotImplementedError( + "Optimization-model does not have fit function: please use OptModel.optimize directly") + + def plot(self, include_metric: bool = True, apply_log: bool = False) -> None: + r"""Plot the loss (and metric) data + + Args: + include_metric: include the metric data. Defaults to be ``True``. + apply_log: whether apply the log function on the data. Defaults to be ``False``. + + """ + if np.size(self.loss_data) == 0: + raise ValueError( + "The data of this model is empty: run OptModel.optimize first.") + + return super().plot(False if self._metric_name is None else include_metric, apply_log, False) + + +class LearningModel(Model): + r"""Class for learning-based QNN model + + Args: + circuit: a ``Circuit`` instance ready to be trained + name: name of this model. Default to be ``"LearningModel"`` + + """ + def __init__(self, circuit: Circuit, name: str = "LearningModel") -> None: + super().__init__(network=circuit, name=name) + self.__is_fitting = False # whether Model is fitting + + def prepare(self, loss_fcn: Callable[[State, Any, Any], Any], + metric_fcn: Callable[[Union[Circuit, Sequential]], float] = None, + metric_name: str = None, *loss_args: Any) -> None: + r"""Prepare and check the function setup for learning-based QNN + + Args: + loss_fcn: loss function for the output data of QNN + metric_fcn: metric function for the QNN, which does not mess with the training process. Defaults to be ``None`` + metric_name: name of the metric function. Defaults to be ``None`` + loss_args: function arguments for loss_fcn other than QNN and label inputs + + Note: + + - The ``data`` input of this Model needs to be ``paddle_quantum.State``. Use ``EncodingModel`` if data is + expected to be encoded into quantum circuits + - The prepare function will take the settings of ``rcParams`` + + """ + # fix loss function by loss arguments + fixed_loss_fcn = lambda output_states, label: loss_fcn(output_states, label, *loss_args) + return super().prepare(fixed_loss_fcn, metric_fcn, metric_name) + + def train_batch(self, data: List[State], label: List[Any]) -> Union[List[float], Tuple[List[float], List[float]]]: + r"""Train the circuit by input batch data + + Args: + data: list of input ``State`` s + label: expected label + + Returns: + contains the following elements: + + - a list of loss values + - a list of metric values if a metric function is given + + """ + self.check_prepared() + + # define the network loss function + def network_loss_fcn(cir: Circuit) -> paddle.Tensor: + output_states = [cir(state) for state in data] + return self._loss_fcn(output_states, label) + + if not self.__is_fitting: + print(f"\nTraining of {self.name} begins:") + return super().train(loss_generator=network_loss_fcn) + + def eval_batch(self, data: List[State], label: List[Any]) -> Union[float, Tuple[float, float]]: + r"""Evaluate the circuit by input batch data + + Args: + data: list of input ``State`` s + label: expected label + + Returns: + contains the following elements: + + - a loss value + - a metric value if a metric function is given + + """ + self.check_prepared() + # define the network loss function + def network_loss_fcn(cir: Circuit) -> paddle.Tensor: + output_states = [cir(state) for state in data] + return self._loss_fcn(output_states, label) + return super().evaluate(loss_generator=network_loss_fcn) + + def fit(self, train_data: List[State], train_label: Iterable, + test_data: List[State], test_label: Iterable) -> None: + r"""Fit the circuit by input train_data + + Args: + train_data: data of the train set + train_label: label of the train set + test_data: data of the test set + test_label: label of the test set + + """ + self.check_prepared() + self.__is_fitting = True + + train_size = len(train_data) + assert isinstance(train_data[0], State), \ + f"The input data must be paddle_quantum.State: received {type(train_data[0])}" + assert train_size == len(train_label), \ + f"The size of train data should be the same as that of labels: \ + received {len(train_label)}, expected {train_size}" + assert len(test_data) == len(test_label), \ + f"The size of test data should be the same as that of labels: \ + received {len(test_label)}, expected {len(test_data)}" + + super().fit(train_data, train_label, test_data, test_label) + self.__is_fitting = False + + def plot(self, include_metric: bool = True, apply_log: bool = False) -> None: + r"""Plot the loss (and metric) data + + Args: + include_metric: include the metric data. Defaults to be ``True``. + apply_log: whether apply the log function on the data. Defaults to be ``False``. + + """ + if np.size(self.loss_data) == 0: + raise ValueError( + "The data of this model is empty: run LearningModel.fit first.") + + return super().plot(False if self._metric_name is None else include_metric, apply_log, True) + + +class EncodingNetwork(paddle.nn.Layer): + r"""QNN for Encoding model + + Args: + encoding_func: an encoding function that determines how to construct quantum circuits + param_shape: the shape of input parameters + initial_state: the initial state of circuits + + Note: + Used for ``paddle_quantum.model.EncodingModel`` only. + + """ + def __init__(self, encoding_func: Callable[[Any, paddle.Tensor], Circuit], + param_shape: Iterable[int], initial_state: State) -> None: + super().__init__() + float_dtype = _get_float_dtype(get_dtype()) + theta = self.create_parameter( + shape=param_shape, dtype=float_dtype, + default_initializer=paddle.nn.initializer.Uniform(low=0, high=2 * math.pi), + ) + self.add_parameter('theta', theta) + + self.encoding_func = encoding_func + self.initial_state = initial_state + + def forward(self, input_data: List[Any]) -> List[State]: + r"""Compute the output states corresponding to the input data + + Args: + input_data: the list of input data that encodes circuits + + Returns: + the output states from these circuits + + """ + return [self.encoding_func(data, self.theta)(self.initial_state) for data in input_data] + + +class EncodingModel(Model): + r"""Class for encoding-based QNN model + + Args: + encoding_fcn: an encoding function that determines how to construct quantum circuits by the encoding + data and the parameters. + param_shape: the shape of input parameters for ``encoding_func`` + initial_state: the initial state of circuits. Default to be ``None`` i.e. the zero state + name: name of this model. Default to be ``"EncodingModel"`` + + Note: + Unlike LearningModel, the data of ``EncodingModel`` is encoded into quantum circuits instead of states. + Therefore, ``EncodingModel`` requires the information of how circuits are encoded by input classical data. + ``EncodingModel`` will automatically generate the parameters according to input ``param_shape``. + + """ + def __init__(self, encoding_fcn: Callable[[Any, paddle.Tensor], Circuit], + param_shape: Iterable[int], initial_state: State = None, + name: str = "EncodingModel") -> None: + network = EncodingNetwork(encoding_fcn, param_shape, initial_state) + + super().__init__(network=network, name=name) + self.__is_fitting = False # whether Model is fitting + + def prepare(self, loss_fcn: Callable[[State, Any, Any], Any], + metric_fcn: Callable[[Union[Circuit, Sequential]], float] = None, + metric_name: str = None, *loss_args: Any) -> None: + r"""Prepare and check the function setup for encoding-based QNN + + Args: + loss_fcn: loss function for the output data of QNN + metric_fcn: metric function for the QNN, which does not mess with the training process. Defaults to be ``None`` + metric_name: name of the metric function. Defaults to be ``None`` + loss_args: function arguments for loss_fcn other than QNN and label inputs + + Note: + The prepare function will take the settings of ``rcParams`` + + """ + # fix loss function by loss arguments + fixed_loss_fcn = lambda output_states, label: loss_fcn(output_states, label, *loss_args) + return super().prepare(fixed_loss_fcn, metric_fcn, metric_name) + + def train_batch(self, data: Iterable, label: Iterable) -> Union[List[float], Tuple[List[float], List[float]]]: + r"""Train the circuit by input batch data + + Args: + data: list of input data + label: expected label + + Returns: + contains the following elements: + + - a list of loss values + - a list of metric values if a metric function is given + + """ + self.check_prepared() + + # define the network loss function + def network_loss_fcn(network: paddle.nn.Layer) -> paddle.Tensor: + return self._loss_fcn(network(data), label) + + if not self.__is_fitting: + print(f"\nTraining of {self.name} begins:") + return super().train(loss_generator=network_loss_fcn) + + def eval_batch(self, data: Iterable, label: Iterable) -> Union[float, Tuple[float, float]]: + r"""Evaluate the circuit by input batch data + + Args: + data: list of input data + label: expected label + + Returns: + contains the following elements: + + - a loss value + - a metric value if a metric function is given + + """ + self.check_prepared() + # define the network loss function + def network_loss_fcn(network: paddle.nn.Layer) -> paddle.Tensor: + return self._loss_fcn(network(data), label) + return super().evaluate(loss_generator=network_loss_fcn) + + def fit(self, train_data: Iterable, train_label: Iterable, + test_data: Iterable, test_label: Iterable) -> None: + r"""Fit the circuit by input train_data + + Args: + train_data: data of the train set + train_label: label of the train set + test_data: data of the test set + test_label: label of the test set + + """ + self.check_prepared() + self.__is_fitting = True + + train_size = len(train_data) + assert train_size == len(train_label), \ + f"The size of train data should be the same as that of labels: \ + received {len(train_label)}, expected {train_size}" + assert len(test_data) == len(test_label), \ + f"The size of test data should be the same as that of labels: \ + received {len(test_label)}, expected {len(test_data)}" + + super().fit(train_data, train_label, test_data, test_label) + self.__is_fitting = False + + def plot(self, include_metric: bool = True, apply_log: bool = False) -> None: + r"""Plot the loss (and metric) data + + Args: + include_metric: include the metric data. Defaults to be ``True``. + apply_log: whether apply the log function on the data. Defaults to be ``False``. + + """ + if np.size(self.loss_data) == 0: + raise ValueError( + "The data of this model is empty: run EncodingModel.fit first.") + + return super().plot(False if self._metric_name is None else include_metric, apply_log, True) diff --git a/paddle_quantum/qchem/__init__.py b/paddle_quantum/qchem/__init__.py index 1765692..592c61a 100644 --- a/paddle_quantum/qchem/__init__.py +++ b/paddle_quantum/qchem/__init__.py @@ -17,9 +17,10 @@ The module of the quantum chemistry. """ -from .hardware_efficient import HardwareEfficientModel -from .slater_determinant import RHFSlaterDeterminantModel -from .uccsd import UCCSDModel -from .density_matrix import get_spinorb_onebody_dm -from .qchem import * -from .loss import RHFEnergyLoss, MolEnergyLoss +from . import utils +from .drivers import PySCFDriver +from .molecule import Molecule +from .fermionic_state import WaveFunction +from .ansatz import HardwareEfficient, UCC, HartreeFock +from .properties import energy, symmetric_rdm1e, dipole_moment +from .algorithm import GroundStateSolver diff --git a/paddle_quantum/qchem/algorithm.py b/paddle_quantum/qchem/algorithm.py new file mode 100644 index 0000000..8eb9dc3 --- /dev/null +++ b/paddle_quantum/qchem/algorithm.py @@ -0,0 +1,133 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +The solver in the qchem. +""" + +from typing import Optional, Tuple +import logging +import numpy as np +import paddle +from paddle.optimizer import Optimizer +from paddle_quantum.loss import ExpecVal +from ..ansatz import Circuit +from ..state import State +from .molecule import Molecule + +__all__ = ["VQESolver", "GroundStateSolver"] + + +class VQESolver(object): + r""" + VQE solver class. + + Args: + optimizer: paddle optimizer. + num_iterations: number of iterations during the optimization. + tol: convergence criteria, if :math`|L1-L0| None: + self.optimizer = optimizer + self.num_iters = num_iterations + self.tol = tol + self.save_every = save_every + + def solve(self): + raise NotImplementedError("Specific VQE solver should implement their own solve method.") + + +class GroundStateSolver(VQESolver): + r""" + The ground state solver class. + + Args: + optimizer: paddle optimizer. + num_iterations: number of iterations during the optimization. + tol: convergence criteria, if :math:`|L1-L0| None: + super().__init__(optimizer, num_iterations, tol, save_every) + + def solve( + self, + mol: Molecule, + ansatz: Circuit, + init_state: Optional[State] = None, + **optimizer_kwargs + ) -> Tuple[float, paddle.Tensor]: + r"""Run VQE to calculate the ground state energy for a given molecule. + + Args: + mol: the molecule object. + ansatz : the quantum circuit represents the wfn transformation. + init_state: default is None, the initial state passed to the ansatz. + **optimizer_kwargs: The other args. + + Returns: + The estimated ground state energy and the ground state wave function. + """ + logging.info("\n#######################################\nVQE (Ground State)\n#######################################") + logging.info(f"Number of qubits: {mol.num_qubits:d}") + logging.info(f"Ansatz: {ansatz.__class__.__name__:s}") + logging.info(f"Optimizer: {self.optimizer.__name__:s}") + + optimizer = self.optimizer(parameters=ansatz.parameters(), **optimizer_kwargs) + logging.info(f"\tlearning_rate: {optimizer.get_lr()}") + + h = mol.get_molecular_hamiltonian() + energy_fn = ExpecVal(h) + + logging.info("\nOptimization:") + loss0 = paddle.to_tensor(np.inf) + for n in range(self.num_iters): + intm_state: State = ansatz(init_state) + loss = energy_fn(intm_state) + + with paddle.no_grad(): + if n % self.save_every == 0: + loss_v = loss.detach().item() + logging.info(f"\tItr {n:d}, loss={loss_v:.5f}.") + # pay attention to the order of x and y !!! + # see https://www.paddlepaddle.org.cn/documentation/docs/en/api/paddle/isclose_en.html for details. + if paddle.isclose(loss0, loss, atol=self.tol).item(): + logging.info(f"Optimization converged after {n:d} iterations.") + loss_v = loss.detach().item() + logging.info(f"The final loss = {loss_v:.5f}.") + break + + loss.backward() + optimizer.step() + optimizer.clear_grad() + loss0 = loss + + with paddle.no_grad(): + final_state: State = ansatz(init_state) + final_loss: float = final_state.expec_val(h) + return final_loss, final_state diff --git a/paddle_quantum/qchem/ansatz.py b/paddle_quantum/qchem/ansatz.py new file mode 100644 index 0000000..52528b3 --- /dev/null +++ b/paddle_quantum/qchem/ansatz.py @@ -0,0 +1,216 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +Ansatz for quantum chemistry +""" + +from typing import Optional +import numpy as np +from openfermion import InteractionOperator, jordan_wigner +from openfermion.utils import is_hermitian +from ..ansatz import Circuit +from ..hamiltonian import Hamiltonian +from ..trotter import construct_trotter_circuit +from .utils import orb2spinorb + +__all__ = ["HardwareEfficient", "UCC", "HartreeFock"] + + +class HardwareEfficient(Circuit): + def __init__( + self, + num_qubits: int, + depth: int, + use_cz: bool = True, + angles: Optional[np.ndarray] = None, + rot_type: str = "ry", + ) -> None: + r""" + Args: + num_qubits (int): number of qubits. + depth (int): the number of circuit units contained in the circuit. A circuit unit is defined as [Rot, Entangle]. + use_cz (bool): whether use CZ gate in the entangling layer, default is True, will use CNOT if this value is set to False. + angles (np.ndarray): initial parameters of rotation gates inside the circuit, default is None and angles will + be initialized randomly. + rot_type (str): the form of rotation unit, default is None and will use "RY" gate, can be chosen among ["RY", "RX", "RZ", "U3"] + """ + super().__init__(num_qubits) + + if rot_type not in {"rx", "ry", "rz", "u3"}: + raise ValueError("`rot_type` needs to be chosen from `rx`, `ry`, `rz` and `u3`.") + rot_type = rot_type.lower() + + if isinstance(angles, np.ndarray): + if rot_type in {"rx", "ry"}: + assert angles.shape == (num_qubits, depth+1) + elif rot_type == "u3": + assert angles.shape == (num_qubits, 3*(depth+1)) + else: + raise ValueError("rot_type should be set to rx, ry or u3") + + entangle_qindex_pair = [(n, n+1) for n in range(num_qubits-1)] + entangle_type = "cz" if use_cz else "cx" + + for _ in range(depth): + getattr(self, rot_type)(qubits_idx="full") + getattr(self, entangle_type)(qubits_idx=entangle_qindex_pair) + getattr(self, rot_type)(qubits_idx="full") + + if isinstance(angles, np.ndarray): + self.update_param(angles.flatten()) + + self._rot_type = rot_type + self._entangle_type = entangle_type + + @property + def rot_type(self): + r"""Type of rotation gate in the circuit. + """ + return self._rot_type + + @property + def entangle_type(self): + r"""Type of entangling gate in the circuit. + """ + return self._entangle_type + + +class UCC(Circuit): + def __init__( + self, + num_qubits: int, + ucc_order: str = "sd", + single_ex_amps: Optional[np.ndarray] = None, + double_ex_amps: Optional[np.ndarray] = None, + **trotter_kwargs + ) -> None: + r""" + UCCSD ansatz has a form + + .. math:: + + e^{-i\hat{H}t}, + + where :math:`\hat{H}` is a Hamiltonian operator with upto two-body terms. + + Args: + num_qubits: number of qubits of the circuit. + ucc_order: the order of UCC ansatz, default to "sd" which means UCCSD, allowed values are "s", "d", "sd". + single_ex_amps: the amplitude for single excitation operator (or one-body operator), default is None and will be + generated randomly. + double_ex_amps: the amplitude for double excitation operator (or two-body operator), default is None and will be + generated randomly. + trotter_kwargs: see ``construct_trotter_circuit`` function for available kwargs. + """ + super().__init__(num_qubits) + + if ucc_order.lower() not in {"s", "d", "sd"}: + raise ValueError("The allowed `ucc_order` are `s`, `d` and `sd`.") + + # NOTE: we assume the \hat{H} is symmetric w.r.t spin flip + if isinstance(single_ex_amps, np.ndarray): + assert single_ex_amps.shape == (num_qubits//2, num_qubits//2), ValueError("shape of `single_ex_amps` mismatches `num_qubits`.") + np.testing.assert_array_almost_equal( + single_ex_amps, + single_ex_amps.T, + err_msg="The single excitation coefficients provided will lead to a non-Hermitian operator for UCC." + ) + elif ucc_order.lower() in {"s", "sd"}: + single_ex_amps = np.random.randn(num_qubits//2, num_qubits//2) + single_ex_amps += single_ex_amps.T + else: + single_ex_amps = np.zeros((num_qubits//2, num_qubits//2)) + + if isinstance(double_ex_amps, np.ndarray): + assert double_ex_amps.shape == (num_qubits//2, num_qubits//2, num_qubits//2, num_qubits//2), ValueError("shape of `double_ex_amps` mismatches `num_qubits`.") + np.testing.assert_array_almost_equal( + double_ex_amps, np.transpose(double_ex_amps, (3, 2, 1, 0)), + err_msg="The double excitation coefficients provided will lead to a non-Hermitian operator for UCC." + ) + elif ucc_order.lower() in {"d", "sd"}: + double_ex_amps = np.random.randn(num_qubits//2, num_qubits//2, num_qubits//2, num_qubits//2) + #NOTE: in order to make the resulting operator Hermitian, A_{prsq}=A_{qsrp} + double_ex_amps += np.transpose(double_ex_amps, (3, 2, 1, 0)) + else: + double_ex_amps = np.zeros((num_qubits//2, num_qubits//2, num_qubits//2, num_qubits//2)) + + num_modes = num_qubits//2 + + # NOTE: We assume the order of Fermion operator in JW transform to be updownupdown... + single_ex_amps_so, double_ex_amps_so = orb2spinorb(num_modes, single_ex_amps, double_ex_amps) + self._onebody_tensor = single_ex_amps_so + self._twobody_tensor = double_ex_amps_so + + uccsd_h = InteractionOperator(0.0, single_ex_amps_so, double_ex_amps_so) + assert is_hermitian(uccsd_h), "The operator on the exponent of the UCC operator isn't Hermitian." + + uccsd_QH = jordan_wigner(uccsd_h) + pq_H = Hamiltonian.from_qubit_operator(uccsd_QH) + #BUG: the np.ndarray may not be a parameter in circuit. + construct_trotter_circuit(self, pq_H, **trotter_kwargs) + + @property + def onebody_tensor(self): + r""" + :math:`T_{pq}` in UCCSD method. + """ + return self._onebody_tensor + + @property + def twobody_tensor(self): + r""" + :math:`V_{pqrs}` in UCCSD method. + """ + return self._twobody_tensor + + +class HartreeFock(Circuit): + def __init__( + self, + num_qubits: int, + angles: Optional[np.ndarray] = None + ) -> None: + r""" + Hartree-Fock (HF) ansatz. + The HF ansatz will leads to a Slater determinant encoded by a ``num_qubits`` quantum state. + + Args: + num_qubits: number of qubits used in the HF ansatz. + angles: parameters of HF ansatz. + + Note: + The ``num_qubits`` == ``num_modes`` in case of HF ansatz. + """ + super().__init__(num_qubits) + qubit_idx = [] + last_qindex = num_qubits-1 + for first_qindex, _ in enumerate(range(num_qubits-1)): + qubit_idx.extend( + [q0, q1] for q0, q1 in zip( + range(last_qindex-1, first_qindex-1, -1), + range(last_qindex, first_qindex, -1) + ) + ) + + #TODO: further reduce the number of Givens rotations. + for qubits in qubit_idx: + self.cnot(qubits[::-1]) + self.cry(qubits) + self.cnot(qubits[::-1]) + self.rz(qubits[1]) + + if isinstance(angles, np.ndarray): + self.update_param(angles.flatten()) diff --git a/paddle_quantum/qchem/complex_utils.py b/paddle_quantum/qchem/complex_utils.py deleted file mode 100644 index 70566b5..0000000 --- a/paddle_quantum/qchem/complex_utils.py +++ /dev/null @@ -1,115 +0,0 @@ -# !/usr/bin/env python3 -# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -r""" -Linear algebra functions which support complex data type (Complex64, Complex128). -""" - -import paddle - -__all__ = ["_general_vnorm", "_general_vv", "_general_mv", "_hermitian_expv"] - - -def _general_vnorm(vec: paddle.Tensor) -> paddle.Tensor: - r""" - Calculate the vector norm for complex vector ||v||^2. - - Args: - vec: Complex valued vector. - - Returns: - Real scalar. - """ - - if vec.dtype in [paddle.complex64, paddle.complex128]: - return paddle.dot(vec.real(), vec.real()) + paddle.dot(vec.imag(), vec.imag()) - else: - return paddle.dot(vec, vec) - - -def _general_vv(vec1: paddle.Tensor, vec2: paddle.Tensor) -> paddle.Tensor: - r""" - Calculate the vector dot product between two complex vectors. - - Args: - vec1: Complex valued vector. - vec2: Complex valued vector. - - Returns: - Complex scalar. - """ - - try: - vec1.dtype == vec2.dtype - except AssertionError: - raise ValueError(f"Tensors are expected to have the same dtype, but receive {vec1.dtype} and {vec2.dtype}") - if vec1.dtype in [paddle.complex64, paddle.complex128]: - return paddle.dot(vec1.real(), vec2.real()) + paddle.dot(vec2.imag(), vec2.imag()) + \ - 1j * paddle.dot(vec1.real(), vec2.imag()) - 1j * paddle.dot(vec1.imag(), vec2.real()) - else: - return paddle.dot(vec1, vec2) - - -def _general_mv(mat: paddle.Tensor, vec: paddle.Tensor) -> paddle.Tensor: - r""" - Calculate the complex matrix vector multiplication. - - Args: - mat: Complex valued matrix. - vec: Complex valued vector. - - Returns: - Complex valued tensor. - """ - - try: - mat.dtype == vec.dtype - except AssertionError: - raise ValueError(f"Tensors are expected to have the same dtype, but receive {mat.dtype} and {vec.dtype}") - if mat.dtype in [paddle.complex64, paddle.complex128]: - return paddle.mv(mat.real(), vec.real()) - paddle.mv(mat.imag(), vec.imag()) + \ - 1j * paddle.mv(mat.real(), vec.imag()) + 1j * paddle.mv(mat.imag(), vec.real()) - else: - return paddle.mv(mat, vec) - - -def _hermitian_expv(mat: paddle.Tensor, vec: paddle.Tensor) -> paddle.Tensor: - r""" - Calculate - .. math: - \langle v|M|v\rangle - - where M is a Hermitian matrix: - M.real(): real symmetric matrix. - M.imag(): real antisymmetric matrix. - - Args: - mat: Complex valued Hermitian matrix. - vec: Complex valued vector. - - Returns: - A scalar, paddle.Tensor. - """ - - try: - mat.dtype == vec.dtype - except AssertionError: - raise ValueError(f"Tensors are expected to have the same dtype, but receive {mat.dtype} and {vec.dtype}") - if mat.dtype in [paddle.complex64, paddle.complex128]: - return paddle.dot(vec.real(), paddle.mv(mat.real(), vec.real())) + \ - paddle.dot(vec.imag(), paddle.mv(mat.real(), vec.imag())) + \ - 2 * paddle.dot(vec.imag(), paddle.mv(mat.imag(), vec.real())) - else: - return paddle.dot(vec, paddle.mv(mat, vec)) diff --git a/paddle_quantum/qchem/density_matrix.py b/paddle_quantum/qchem/density_matrix.py deleted file mode 100644 index 431b7e4..0000000 --- a/paddle_quantum/qchem/density_matrix.py +++ /dev/null @@ -1,131 +0,0 @@ -# !/usr/bin/env python3 -# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -r""" -Measure onebody density matrix from quantum state. -""" - -from typing import List, Tuple -import paddle -import openfermion -import paddle_quantum - -from .complex_utils import * - -__all__ = ["get_spinorb_onebody_dm"] - - -def _get_float_dtype(dtype): - r""" - Get the compatible floating data type from the given complex data type. - """ - if dtype == paddle.complex64: - return paddle.float32 - elif dtype == paddle.complex128: - return paddle.float64 - - -def _get_onebody_hermitian_operator(n_qubits: int, i: int, j: int, with_spin: bool, dtype) -> paddle.Tensor: - r""" - Return the matrix corresponds to the - .. math: - \hat{c}_i^{\dagger}\hat{c}_j+\hat{c}_j^{\dagger}\hat{c}_i. - - Args: - n_qubits: Number of qubits in the quantum circuit. - i: Operator index. - j: Operator index. - with_spin: Whether use spin orbital or orbital to label the qubits in the quantum circuit. - - Returns: - Hermitian matrix. - """ - - if i == j: - ops = openfermion.QubitOperator("", 0.5) - openfermion.QubitOperator(f"Z{i}", 0.5) - ops_array = openfermion.get_sparse_operator(ops, n_qubits).toarray() - else: - if with_spin: - qstr_xzx = f"X{i} " + " ".join([f"Z{k}" for k in range(i + 2, j, 2)]) + f" X{j}" - qstr_yzy = f"Y{i} " + " ".join([f"Z{k}" for k in range(i + 2, j, 2)]) + f" Y{j}" - else: - qstr_xzx = f"X{i} " + " ".join([f"Z{k}" for k in range(i + 1, j)]) + f" X{j}" - qstr_yzy = f"Y{i} " + " ".join([f"Z{k}" for k in range(i + 1, j)]) + f" Y{j}" - ops = openfermion.QubitOperator(qstr_xzx, 0.5) + openfermion.QubitOperator(qstr_yzy, 0.5) - ops_array = openfermion.get_sparse_operator(ops, n_qubits).toarray() - return paddle.to_tensor(ops_array, dtype=dtype) - - -class OneBodyDensityMatrix(paddle.autograd.PyLayer): - r""" - Measure the onebody density matrix from a quantum state. - """ - @staticmethod - def forward(ctx, n_qubits: int, orb_index: List[int], with_spin: bool, state: paddle.Tensor) -> paddle.Tensor: - ctx.with_spin = with_spin - ctx.n_qubits = n_qubits - ctx.orb_index = orb_index - ctx.save_for_backward(state) - - nao = len(orb_index) - dm = paddle.zeros((nao, nao), dtype=_get_float_dtype(state.dtype)) - for i in range(nao): - dm_op = _get_onebody_hermitian_operator(n_qubits, orb_index[i], orb_index[i], with_spin, state.dtype) - dm[i, i] = _hermitian_expv(dm_op, state) - for j in range(i + 1, nao): - dm_op = _get_onebody_hermitian_operator(n_qubits, orb_index[i], orb_index[j], with_spin, state.dtype) - dm[i, j] = 0.5 * _hermitian_expv(dm_op, state) - dm[j, i] = dm[i, j] - return dm - - @staticmethod - def backward(ctx, grad_dm: paddle.Tensor): - n_qubits = ctx.n_qubits - state, = ctx.saved_tensor() - orb_index = ctx.orb_index - - grad_state = 0.0 + 0.0j - nao = len(orb_index) - for i in range(nao): - dm_op = _get_onebody_hermitian_operator(n_qubits, orb_index[i], orb_index[i], ctx.with_spin, state.dtype) - grad_state += grad_dm[i, i] * 2 * _general_mv(dm_op, state) - for j in range(i + 1, nao): - dm_op = _get_onebody_hermitian_operator(n_qubits, orb_index[i], orb_index[j], ctx.with_spin, - state.dtype) - grad_state += (grad_dm[i, j] + grad_dm[j, i]) * _general_mv(dm_op, state) - return grad_state - - -get_onebody_dm = OneBodyDensityMatrix.apply - - -def get_spinorb_onebody_dm(n_qubits: int, state: paddle.Tensor) -> Tuple[paddle.Tensor]: - r""" - Get the onebody density matrix from a given state in which qubits are label by spin orbital index. - - Args: - n_qubits: number of qubits in the quantum circuit. - state: the given quantum state. - - Returns: - spin up and spin down onebody density matrix. - """ - - assert n_qubits % 2 == 0, "number of spin orbital should be even." - a_orb_index = range(0, n_qubits, 2) - b_orb_index = range(1, n_qubits, 2) - dm_a = get_onebody_dm(n_qubits, a_orb_index, True, state) - dm_b = get_onebody_dm(n_qubits, b_orb_index, True, state) - return dm_a, dm_b diff --git a/paddle_quantum/qchem/drivers.py b/paddle_quantum/qchem/drivers.py new file mode 100644 index 0000000..2ed8e7d --- /dev/null +++ b/paddle_quantum/qchem/drivers.py @@ -0,0 +1,173 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +The drivers of the classical quantum chemistry calculation. +""" + +from typing import List, Tuple, Optional +import sys +import logging +import numpy as np + +__all__ = ["Driver", "PySCFDriver"] + + +class Driver(object): + def run_scf(self): + raise NotImplementedError + + @property + def num_modes(self): + raise NotImplementedError + + @property + def energy_nuc(self): + raise NotImplementedError + + @property + def mo_coeff(self): + raise NotImplementedError + + def load_molecule(self): + raise NotImplementedError + + def get_onebody_tensor(self): + raise NotImplementedError + + def get_twobody_tensor(self): + raise NotImplementedError + + +class PySCFDriver(Driver): + r"""Use pyscf to perform classical quantum chemistry calculation. + """ + def __init__(self) -> None: + if sys.platform not in ["linux", "darwin"]: + raise ModuleNotFoundError("NOTE your operating system is Windows, PySCF doesn't support Windows yet, sorry....") + + try: + import pyscf + except ImportError as e: + raise ModuleNotFoundError( + "PySCF doesn't find on the current path, you can install it by `pip install pyscf`." + ) from e + self.mol = pyscf.gto.Mole() + + def load_molecule( + self, + atom: List[Tuple[str, List[float]]], + basis: str, + multiplicity: int, + charge: int, + unit: str + ) -> None: + r"""construct a pyscf molecule from the given information. + + Args: + atom: atom symbol and their coordinate, same format + as in `Molecule`. + basis: basis set. + multiplicity: spin multiplicity 2S+1. + charge: charge of the molecule. + unit: `Angstrom` or `Bohr`. + """ + pyscf_atom = "; ".join( + f"{symbol:s} {x:.8f} {y:.8f} {z:.8f}" + for symbol, (x, y, z) in atom + ) + self.mol.build( + atom=pyscf_atom, + basis=basis, + spin=multiplicity-1, + charge=charge, + unit=unit + ) + self._energy_nuc = self.mol.energy_nuc() + + def run_scf(self): + r"""perform RHF calculation on the molecule. + """ + from pyscf.scf import RHF + + logging.info("\n#######################################\nSCF Calculation (Classical)\n#######################################") + logging.info(f"Basis: {self.mol.basis:s}") + + mf = RHF(self.mol) + mf.kernel() + self.scf = mf + self.scf_e_tot = mf.e_tot + self._mo_coeff = mf.mo_coeff + self._num_modes = mf.mo_coeff.shape[1] + + logging.info(f"SCF energy: {self.scf_e_tot:.5f}.") + + @property + def energy_nuc(self): + r"""Potential energy of nuclears. + """ + return self._energy_nuc + + @property + def mo_coeff(self): + r"""Transformation matrix between atomic orbitals and molecular orbitals. + """ + return self._mo_coeff + + @property + def num_modes(self): + r"""Number of molecular orbitals. + """ + return self._num_modes + + def get_onebody_tensor(self, integral_type: Optional[str] = None) -> np.ndarray: + r""" + :math:`T[p,q] = \int\phi_p^*(x) f(x) \phi_q(x)dx` + + Args: + integral_type (str): the type of integral, e.g. "int1e_ovlp", "int1e_kin", etc., + see https://github.com/pyscf/pyscf/blob/master/pyscf/gto/moleintor.py for details. + + Returns: + np.ndarray. + """ + try: + mo_coeff = self.mo_coeff + except AttributeError: + self.run_scf() + mo_coeff = self.mo_coeff + + if integral_type is None: + raise ValueError("An integral type needs to be specified.") + + with self.mol.with_common_orig((0.0, 0.0, 0.0)): + ao_integral = self.mol.intor(integral_type) + return np.einsum("...ij,ik,jl->...kl", ao_integral, mo_coeff, mo_coeff) + + def get_twobody_tensor(self) -> np.ndarray: + r""" + :math:`V[p,r,s,q] = \int\phi^*_p(x)\phi^*_r(x')(1/|x-x'|)\phi_s(x')\phi_q(x)dxdx'=(pq|rs)` in pyscf. + """ + from pyscf import ao2mo + + try: + mo_coeff = self.mo_coeff + except AttributeError: + self.run_scf() + mo_coeff = self.mo_coeff + + eri = ao2mo.kernel(self.mol, mo_coeff) + eri = ao2mo.restore(1, eri, mo_coeff.shape[1]) + return np.transpose(eri, (0, 2, 3, 1)) diff --git a/paddle_quantum/qchem/fermionic_state.py b/paddle_quantum/qchem/fermionic_state.py new file mode 100644 index 0000000..6a5d1a3 --- /dev/null +++ b/paddle_quantum/qchem/fermionic_state.py @@ -0,0 +1,252 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +Wave function module. +""" + +#TODO: wave function, explor the symmetry +#TODO: how to distribute the wave function to cluster + +from typing import Union, Optional +import math +import numpy as np +import paddle +from openfermion import InteractionOperator, jordan_wigner +from ..base import get_dtype +from ..backend import Backend +from ..gate.functional.base import simulation +from ..state import State +from ..hamiltonian import Hamiltonian +from ..intrinsic import _get_float_dtype + +__all__ = ["WaveFunction"] + + +class WaveFunction(State): + _fswap_matrix = paddle.to_tensor( + [ + [1, 0, 0, 0], + [0, 0, 1, 0], + [0, 1, 0, 0], + [0, 0, 0, -1] + ], + dtype=_get_float_dtype(get_dtype()) + ) + + def __init__( + self, + data: Union[paddle.Tensor, np.ndarray], + convention: str = "mixed", + backend: Optional[Backend] = None, + dtype: Optional[str] = None, + override: Optional[bool] = False + ) -> None: + r""" + Quantum state as a Fermionic Fock state :math:`|\Psi\rangle` . The number of qubits of the quantum state equals + the number of spin-orbitals in :math:`|\Psi\rangle`, + each qubit corresponds to a spin orbital state in :math:`|\Psi\rangle` . + + .. math:: + + |\Psi\rangle=\sum_{\alpha}C_{\alpha}|\alpha\rangle, C_{\alpha}\in\mathbb{C}. \\ + |\alpha\rangle=\otimes_{i=1}^N|n_i\rangle=\Pi_{i=1}^N \left(\hat{a}^{\dagger}_i\right)^{n_i}|\vec{0}\rangle, n_i\in\{0, 1\}. + + Args: + data: a complex value vector contains coefficients of :math:`|\Psi\rangle` in computational basis. + convention: labeling of spin orbitals, if set to ``mixed``, then spin up and spin down creation operators are intersecting + each other, else, if set to ``separated``, spin up and spin down creation operators are belong to different sections. + backend: paddle_quantum Backend on which to perform simulation, default is statevector backend. + dtype: data type of wave function, default is ``complex64``. + override: whether to override settings in wave function, default is False. + """ + num_qubits = math.floor(math.log2(len(data))) + assert 2**num_qubits == len(data), f"The input `data` is not a valid quantum state, `len(data)` should be 2**{num_qubits:d}, got {len(data):d}." + assert num_qubits % 2 == 0, f"The input data is not a valid Fermionic Fock state (spin-orbital form), the `num_qubits` should be a multiple of 2, got {num_qubits:d}." + + super().__init__(data, num_qubits, backend, dtype, override) + + assert convention in {"mixed", "separated"}, "Only `mixed` and `separated` convention is valid." + self.convention = convention + + def clone(self): + r"""Return a copy of the wavefunction. + + Returns: + A new state which is identical to this state. + """ + return WaveFunction(self.data, self.convention, self.backend, self.dtype, override=True) + + def swap(self, p: int, q: int) -> None: + r""" + Switching p-th qubit and q-th qubit. + + Note: + Since qubit represents fermion state, exchanging them will results in a minus sign. + + Args: + p: index of the qubit being exchanged. + q : index of another qubit being exchanged. + """ + new_data = simulation(self, self._fswap_matrix, [p, q], self.num_qubits, self.backend) + self.data = new_data + + def to_spin_mixed(self) -> None: + r""" + If the wavefunction is in convention "separated", convert it to a "mixed" convention state. + """ + if self.convention == "mixed": + print("Already in spin mixed mode, nothing changed.") + else: + num_orbs = self.num_qubits//2 + for head, cur in enumerate(range(num_orbs, self.num_qubits)): + for p, q in zip(range(cur-1, 2*head, -1), range(cur, 2*head+1, -1)): + self.swap(p, q) + self.convention = "mixed" + + def to_spin_separated(self) -> None: + r""" + If the wavefunction is in convention "mixed", convert it to a "separated" convention state. + """ + if self.convention == "separated": + print("Already in spin separated mode, nothing changed.") + else: + # 0 represents spin up mode, 1 represents spin down mode. + spin_mode = [0, 1]*(self.num_qubits//2) + while True: + num_switch = 0 + for p in range(self.num_qubits-1): + if spin_mode[p] == 1 and spin_mode[p+1] == 0: + self.swap(p, p+1) + num_switch += 1 + spin1, spin2 = spin_mode[p], spin_mode[p+1] + spin_mode[p] = spin2 + spin_mode[p+1] = spin1 + if num_switch == 0: + break + self.convention = "separated" + + @classmethod + def slater_determinant_state( + cls, + num_qubits: int, + num_elec: int, + mz: int, + backend: Optional[Backend] = None, + dtype: Optional[str] = None, + ): + r""" + Construct a single Slater determinant state whose length is ``num_qubits`` . + The number of "1" in the Slater determinant state equals `num_elec`, the difference + between number of spin up electrons and the number of spin down electrons is ``mz`` . + The prepared Slater determinant is in mixed spin orbital mode, which is "updownupdown...". + + Args: + num_qubits: number of qubits used in preparing Slater determinant state. + num_elec: number of electrons in Slater determinant state. + mz: :math:`n_{\uparrow}-n_{\downarrow}` . + + Returns: + WaveFunction. + """ + assert num_qubits >= num_elec, "Need more qubits to hold the Slater determinant state." + num_alpha = math.floor(0.5*(num_elec + mz)) + num_beta = math.floor(0.5*(num_elec - mz)) + assert num_alpha + num_beta == num_elec + + bstr_list = ["1", "1"]*min(num_alpha, num_beta) + if mz > 0: + bstr_list.extend(["1", "0"]*abs(mz)) + if mz < 0: + bstr_list.extend(["0", "1"]*abs(mz)) + bstr_list.extend(["0"]*(num_qubits-2*max(num_alpha, num_beta))) + bstr = "".join(bstr_list) + + psi = np.zeros(2**num_qubits, dtype=np.complex64) + psi[int(bstr, 2)] = 1.0+0.0j + return cls(psi, dtype=dtype, backend=backend) + + @classmethod + def zero_state(cls, num_qubits: int, backend: Optional[Backend] = None, dtype: Optional[str] = None): + r""" + Construct a zero state, :math:`|0000....\rangle` . + """ + psi = np.zeros(2**num_qubits, dtype=np.complex64) + psi[0] = 1.0+0.0j + return cls(psi, dtype=dtype, backend=backend) + + def num_elec(self, shots: int = 0) -> float: + r"""Calculate the total number of electrons in the wave function. + + .. math:: + + \langle\Psi|\sum_{i\sigma}\hat{a}_{i\sigma}^{\dagger}\hat{a}_{i\sigma}|\Psi\rangle. + + """ + num_qubits = self.num_qubits + number_operator = Hamiltonian( + [(0.5*num_qubits, "I")].extend( + (-0.5, f"Z{i:d}") for i in range(num_qubits) + ) + ) + return self.expec_val(number_operator, shots) + + def total_SpinZ(self, shots: int = 0) -> float: + r"""Calculate the total spin Z component of the wave function. + + .. math:: + + \langle\Psi|\sum_{i}\hat{S}_z|\Psi\rangle \\ + \hat{S}_z = 0.5*\sum_{p}(\hat{n}_{p\alpha}-\hat{n}_{p\beta}) \\ + \alpha\equiv\uparrow, \beta\equiv\downarrow, \hat{n}_{p\sigma}=\hat{a}^{\dagger}_{p\sigma}\hat{a}_{p\sigma} + + """ + num_qubits = self.num_qubits + sz_operator = Hamiltonian( + [ + (((-1)**(k+1))*0.25, f"Z{k:d}") for k in range(num_qubits) + ] + ) + return self.expec_val(sz_operator, shots) + + def total_Spin2(self, shots: int = 0) -> float: + r"""Calculate the expectation value of :math:`\hat{S}^2` operator on the wave function. + + .. math:: + + \langle\Psi|\hat{S}_+\hat{S}_- +\hat{S}_z(\hat{S}_z-1)|\Psi\rangle \\ + \hat{S}_+ = \sum_{p}\hat{a}_{p\alpha}^{\dagger}\hat{a}_{p\beta} \\ + \hat{S}_- = \sum_{p}\hat{a}_{p\beta}^{\dagger}\hat{a}_{p\alpha} \\ + \hat{S}_z = 0.5*\sum_{p}(\hat{n}_{p\alpha}-\hat{n}_{p\beta}) \\ + \alpha\equiv\uparrow, \beta\equiv\downarrow, \hat{n}_{p\sigma}=\hat{a}^{\dagger}_{p\sigma}\hat{a}_{p\sigma} + + """ + # construct \hat{S}^2 + num_modes = self.num_qubits//2 + T = np.zeros([self.num_qubits, self.num_qubits]) + V = np.zeros([self.num_qubits, self.num_qubits, self.num_qubits, self.num_qubits]) + for p in range(num_modes): + T[2*p, 2*p] = 0.75 + T[2*p+1, 2*p+1] = 0.75 + for q in range(num_modes): + V[2*p, 2*q+1, 2*p+1, 2*q] = -1.0 + V[2*p, 2*q, 2*q, 2*p] = 0.25 + V[2*p+1, 2*q+1, 2*q+1, 2*p+1] = 0.25 + V[2*p, 2*q+1, 2*q+1, 2*p] = -0.5 + s2 = InteractionOperator(0.0, T, V) + s2_qubit = jordan_wigner(s2) + s2_h = Hamiltonian.from_qubit_operator(s2_qubit) + # calculate expect value + return self.expec_val(s2_h, shots) diff --git a/paddle_quantum/qchem/hardware_efficient.py b/paddle_quantum/qchem/hardware_efficient.py deleted file mode 100644 index 8d24e83..0000000 --- a/paddle_quantum/qchem/hardware_efficient.py +++ /dev/null @@ -1,64 +0,0 @@ -# !/usr/bin/env python3 -# Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -r""" -Hardware Efficient ansatz. -""" - -from typing import Union, Optional -import paddle -import paddle_quantum as pq - -__all__ = ["HardwareEfficientModel"] - - -class HardwareEfficientModel(pq.gate.Gate): - r""" - Args: - n_qubits: number of qubits. - depth: depth of the circuit, a layer in Hardware efficient circuit contains [Ry, Rz, CNOT]. - theta: parameters for the Ry and Rz gates inside the circuit. - """ - - def __init__( - self, - n_qubits: int, - depth: int, - theta: Optional[paddle.Tensor] = None - ): - super().__init__(depth, backend=pq.Backend.StateVector) - - layers = [] - if theta is not None: - assert theta.shape == [n_qubits, 2, - depth], "shape of the parameter should be compatible to n_qubits and depths" - for d in range(depth - 1): - layers.append(pq.gate.RY("full", n_qubits, param=theta[:, 0, d])) - layers.append(pq.gate.RZ("full", n_qubits, param=theta[:, 1, d])) - layers.append(pq.gate.CNOT("cycle", n_qubits)) - layers.append(pq.gate.RY("full", n_qubits, param=theta[:, 0, depth - 1])) - layers.append(pq.gate.RZ("full", n_qubits, param=theta[:, 1, depth - 1])) - else: - for d in range(depth - 1): - layers.append(pq.gate.RY("full", n_qubits)) - layers.append(pq.gate.RZ("full", n_qubits)) - layers.append(pq.gate.CNOT("cycle", n_qubits)) - layers.append(pq.gate.RY("full", n_qubits)) - layers.append(pq.gate.RZ("full", n_qubits)) - - self.model = pq.ansatz.Sequential(*layers) - - def forward(self, state: pq.State) -> pq.State: - return self.model(state) diff --git a/paddle_quantum/qchem/loss.py b/paddle_quantum/qchem/loss.py deleted file mode 100644 index 0b9ce14..0000000 --- a/paddle_quantum/qchem/loss.py +++ /dev/null @@ -1,111 +0,0 @@ -# !/usr/bin/env python3 -# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -r""" -Loss functions for quantum chemistry calculation. -""" - -import paddle -import numpy as np -import paddle_quantum as pq - -from .qchem import get_molecular_data, spin_hamiltonian -from .density_matrix import get_spinorb_onebody_dm - -__all__ = ["MolEnergyLoss", "RHFEnergyLoss"] - - -class MolEnergyLoss(pq.loss.ExpecVal): - r"""Loss function for molecular ground state calculation. - - Args: - geometry: e.g. "H 0.0 0.0 0.0; H 0.0 0.0 0.74". - basis: chemical basis, e.g. "sto-3g". - multiplicity: spin multiplicity. - charge: charge of the molecule. - """ - - def __init__( - self, - geometry: str, - basis: str, - multiplicity: int = 1, - charge: int = 0) -> None: - geometry_internal = [] - for atom in geometry.split(";"): - atom = atom.strip() - atom_list = atom.split(" ") - atom_symbol = atom_list[0] - atom_coord = atom_list[1:] - geometry_internal.append((atom_symbol, [float(x) for x in atom_coord])) - - mol = get_molecular_data(geometry_internal, charge, multiplicity, basis) - mol_H = spin_hamiltonian(mol) - super().__init__(mol_H) - - -class RHFEnergyLoss(pq.Operator): - r"""Loss function for Restricted Hartree Fock calculation. - NOTE: This function needs PySCF be installed! - - Args: - geometry: e.g. "H 0.0 0.0 0.0; H 0.0 0.0 0.74". - basis: chemical basis, e.g. "sto-3g". - multiplicity: spin multiplicity. - charge: charge of the molecule. - - Raises: - ModuleNotFoundError: `hartree fock` method needs pyscf being installed, please run `pip install -U pyscf`. - """ - - def __init__( - self, - geometry: str, - basis: str, - multiplicity: int = 1, - charge: int = 0 - ) -> None: - super().__init__(backend=pq.Backend.StateVector) - - try: - import pyscf - except ModuleNotFoundError: - raise ModuleNotFoundError( - "`hartree fock` method needs pyscf being installed, please run `pip install -U pyscf`.") - - from pyscf.lo import lowdin - - mol = pyscf.gto.Mole(atom=geometry, basis=basis, multiplicity=multiplicity, charge=charge) - mol.build() - ovlp = mol.intor_symmetric("int1e_ovlp") - kin = mol.intor_symmetric("int1e_kin") - vext = mol.intor_symmetric("int1e_nuc") - vint = np.transpose(mol.intor("int2e"), (0, 2, 3, 1)) - V = lowdin(ovlp) - onebody_tensor, twobody_tensor, V_tensor = map(paddle.to_tensor, [kin + vext, 0.5 * vint, V]) - - self.energy_nuc = mol.energy_nuc() - self.onebody = onebody_tensor - self.twobody = twobody_tensor - self._V = V_tensor - - def forward(self, state: pq.State) -> paddle.Tensor: - state_tensor = state.data - rdm_spinup, _ = get_spinorb_onebody_dm(state.num_qubits, state_tensor) - rdm = 2 * (self._V @ rdm_spinup @ self._V) - rhf_energy = self.energy_nuc + paddle.einsum("pq,qp->", self.onebody, rdm) + \ - paddle.einsum("pqrs,qp,sr->", self.twobody, rdm, rdm) - \ - 0.5 * paddle.einsum("pqrs,sp,qr->", self.twobody, rdm, rdm) - return rhf_energy diff --git a/paddle_quantum/qchem/molecule.py b/paddle_quantum/qchem/molecule.py new file mode 100644 index 0000000..24a6fbc --- /dev/null +++ b/paddle_quantum/qchem/molecule.py @@ -0,0 +1,181 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +The module of the molecule. +""" + +from typing import List, Tuple, Optional +import logging +import numpy as np +from openfermion import InteractionOperator, jordan_wigner +from ..hamiltonian import Hamiltonian +from .drivers import Driver, PySCFDriver +from .utils import orb2spinorb + +__all__ = ["Molecule"] + + +class Molecule(object): + def __init__( + self, + geometry: Optional[List[Tuple[str, List]]] = None, + basis: Optional[str] = None, + multiplicity: Optional[int] = None, + charge: Optional[int] = None, + mol_expr: Optional[str] = None, + use_angstrom: bool = True, + driver: Optional[Driver] = None + ) -> None: + r"""Construct molecule object from given information. + + Args: + basis: basis set for computation chemistry. + multiplicity: spin multiplicity of molecule, 2S+1. + charge: charge of molecule. + mol_expr: molecular expression, e.g. "CO2", "CH3COOH". + geometry: atom symbol and their coordinate, + e.g. ``[("H", [0.0, 0.0, 0.0]), ("H", [0.0, 0.0, 1.4])]`` . Default is None, if it's + None, ``mol_expr`` should be specified, geometry will be download from internet. + use_angstrom (bool): the length unit, default is True, if set to False, + will use Atomic unit. + driver: classical quantum chemistry calculator, default is None. + """ + self.basis = "sto-3g" if basis is None else basis + self.multiplicity = 1 if multiplicity is None else multiplicity + self.charge = 0 if charge is None else charge + self._unit = "Angstrom" if use_angstrom else "Bohr" + if geometry is None and mol_expr is None: + raise ValueError("One of the `mol_expr` and `geometry` shouldn't be None.") + elif geometry is None: + self.geometry = self.load_geometry(mol_expr) + else: + self.geometry = geometry + + if driver is None: + raise ValueError("You need to specify a driver to perform classical quantum chemistry calculation.") + + driver.load_molecule( + self.geometry, + self.basis, + self.multiplicity, + self.charge, + self.unit + ) + self.driver = driver + self._charges = driver.mol.atom_charges() + self._coords = driver.mol.atom_coords() + + if mol_expr is None: + mol_el: List[str] = driver.mol.elements + mol_expr = "" + for el in np.unique(mol_el).tolist(): + num_el = mol_el.count(el) + mol_expr += f"{el:s}{num_el:d}" if num_el > 1 else f"{el:s}" + self.mol_expr = mol_expr + + def build(self): + r"""Use driver to calculate molecular integrals. + """ + logging.info("\n#######################################\nMolecule\n#######################################") + logging.info(f"{self.mol_expr:s}") + logging.info("Geometry:") + logging.info( + "\n".join( + f"{t[0]:s} {t[1][0]:.5f}, {t[1][1]:.5f}, {t[1][2]:.5f}" + for t in self.geometry + ) + ) + logging.info(f"Charge: {self.charge:d}") + logging.info(f"Multiplicity: {self.multiplicity:d}") + logging.info(f"Unit: {self.unit:s}") + + self.driver.run_scf() + self._num_qubits = 2*self.driver.num_modes + + @property + def atom_charges(self) -> np.ndarray: + r"""Charges on each nuclei. + """ + return self._charges + + @property + def atom_coords(self) -> np.ndarray: + r"""Atom's coordinate. + """ + return self._coords + + @property + def num_qubits(self) -> int: + r"""Number of qubits used to encode the molecular quantum state on a quantum computer. + """ + try: + self._num_qubits + except AttributeError as e: + raise Exception("You need to run Molecule.build() method in order to access this attribute.").with_traceback(e.__traceback__) + return self._num_qubits + + @property + def unit(self): + r"""Unit used for measuring the spatial distance of atoms in molecule. + """ + return self._unit + + #TODO: develop load_geometry method that can automatically load from internet. + def load_geometry(self, mol_expr: str): + r"""load geometry of a molecule from internet. + """ + pass + + def get_mo_integral( + self, + integral_type: str + ) -> np.ndarray: + r"""calculate integrals using chosen driver. + + Args: + integral_type: type of integral, the name is different for different driver. + + Returns: + The integrals. + """ + if self.driver is None: + raise ValueError("You need a driver to do classical mean field calculation to get molecular orbit.") + + if isinstance(self.driver, PySCFDriver): + if integral_type.split("_")[0] == "int1e": + return self.driver.get_onebody_tensor(integral_type) + elif integral_type == "int2e" or integral_type.split("_")[0] == "int2e": + return self.driver.get_twobody_tensor() + else: + raise NotImplementedError("Only PySCFDriver is currently implemented.") + + def get_molecular_hamiltonian(self) -> Hamiltonian: + r"""returns the molecular hamiltonian for the given molecule. + """ + hcore = self.get_mo_integral("int1e_nuc") + self.get_mo_integral("int1e_kin") + eri = self.get_mo_integral("int2e") + constant = self.driver.energy_nuc + + hcore_so, eri_so = orb2spinorb(self.driver.num_modes, hcore, eri) + + # set the values in the array that are lower than 1e-16 to zero. + eps = np.finfo(hcore.dtype).eps + hcore_so[abs(hcore_so) < eps] = 0.0 + eri_so[abs(eri_so) < eps] = 0.0 + + h = InteractionOperator(constant, hcore_so, 0.5*eri_so) + self._of_h = h # for testing purpose + return Hamiltonian.from_qubit_operator(jordan_wigner(h)) diff --git a/paddle_quantum/qchem/properties.py b/paddle_quantum/qchem/properties.py new file mode 100644 index 0000000..b2e9a73 --- /dev/null +++ b/paddle_quantum/qchem/properties.py @@ -0,0 +1,127 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +Calculate the properties of the molecule. +""" + +import warnings +import numpy as np +from ..state import State +from ..hamiltonian import Hamiltonian +from ..shadow import shadow_sample +from ..qinfo import shadow_trace +from .molecule import Molecule + +__all__ = ["energy", "dipole_moment"] + + +def energy( + psi: State, + mol: Molecule, + shots: int = 0, + use_shadow: bool = False, + **shadow_kwargs +) -> float: + r"""Calculate the energy of a molecule w.r.t a quantum state :math:`\psi` . + + Args: + psi: a quantum state. + mol: the molecule instance. + shots: number of shots used to estimate the expectation value, default is 0 and will calculate + the ideal expectation value. + use_shadow: whether use classical shadow to estimate the energy, default is False and will + evalute the energy by matrix multiplication. + shadow_kwargs + + Returns: + The energy of the molecule. + """ + warnings.warn("This method shouldn't be used as a loss function since it won't return tracked Tensor.") + + h = mol.get_molecular_hamiltonian() + v = psi.expec_val(h, shots) + return v if isinstance(v, float) else v.item() + + +def symmetric_rdm1e(psi: State, shots: int = 0, use_shadow: bool = False, **shadow_kwargs) -> np.ndarray: + r"""Calculate the symmetric 1-RDM from a given quantum state :math:`\psi` . + + .. math:: + + D_{pq}=<\hat{c}_p^{\dagger}\hat{c}_q+\hat{c}_{q}^{\dagger}\hat{c}_p>, p>q \\ + D_{pq}=0, p>q \\ + D_{pq}=<\hat{c}_p^{\dagger}\hat{c}_p>, p=q + + Args: + psi: quantum state. + shots: number of shots used to estimate the expectation value. default is 0, and will calculate + the ideal expectation value. + use_shadow: whether use classical shadow to estimate the energy, default is False and will + evalute the energy by matrix multiplication. + **shadow_kwargs: The other args. + + Returns: + The symmetric 1-RDM. + """ + num_qubits = psi.num_qubits + symm_rdm1 = np.zeros((num_qubits, num_qubits)) + for i in range(num_qubits): + diag_el = Hamiltonian([(0.5, "I"), (-0.5, f"Z{i:d}")]) + v = psi.expec_val(diag_el, shots) + symm_rdm1[i, i] = v if isinstance(v, float) else v.item() + for j in range(i+2, num_qubits, 2): + qubit_op_str1 = f"X{i:d}, " + ", ".join(f"Z{k:d}" for k in range(i+2, j, 2)) + f"X{j:d}" + qubit_op_str2 = f"Y{i:d}, " + ", ".join(f"Z{k:d}" for k in range(i+2, j, 2)) + f"Y{j:d}" + offdiag_el = Hamiltonian([(0.5, qubit_op_str1), (0.5, qubit_op_str2)]) + v = psi.expec_val(offdiag_el, shots) + symm_rdm1[j, i] = v if isinstance(v, float) else v.item() + return symm_rdm1 + + +def dipole_moment(psi: State, mol: Molecule, shots: int = 0, use_shadow: bool = False, **shadow_kwargs) -> np.ndarray: + r"""Calculate the dipole moment of a molecule w.r.t a given quantum state. + + Args: + psi: a quantum state. + mol: the molecule instance. + shots: number of shots used to estimate the expectation value. default is 0, and will calculate + the ideal expectation value. + use_shadow: whether use classical shadow to estimate the energy, default is False and will + evalute the energy by matrix multiplication. + **shadow_kwargs: The other args. + + Returns: + The dipole moment of the input molecule. + """ + warnings.warn("This method shouldn't be used as a loss function since it won't return tracked Tensor.") + + # get (p|x-R_c|q) + int1e_r = mol.get_mo_integral("int1e_r") + np.testing.assert_array_almost_equal(int1e_r, np.transpose(int1e_r, (0, 2, 1))) + + # nuclei dipole moment. + charges = mol.atom_charges + coords = mol.atom_coords + nucl_dip = np.einsum('i,ix->x', charges, coords) + + # electron dipole moment. + num_qubits = psi.num_qubits + symm_rdm1 = symmetric_rdm1e(psi, shots, use_shadow, **shadow_kwargs) + symm_rdm1_a = symm_rdm1[::2, ::2] + symm_rdm1_b = symm_rdm1[1:num_qubits:2, 1:num_qubits:2] + el_dip = -np.einsum("ijk,kj->i", int1e_r, (symm_rdm1_a+symm_rdm1_b)) + + return nucl_dip + el_dip \ No newline at end of file diff --git a/paddle_quantum/qchem/qchem.py b/paddle_quantum/qchem/qchem.py deleted file mode 100644 index f15ed6f..0000000 --- a/paddle_quantum/qchem/qchem.py +++ /dev/null @@ -1,420 +0,0 @@ -# !/usr/bin/env python3 -# Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -r""" -The function for quantum chemistry. -""" - -import os -import re -import string -from typing import Optional -import numpy as np -import openfermion -from openfermion import MolecularData, transforms -from openfermion.ops import general_basis_change -from paddle_quantum.hamiltonian import Hamiltonian - - -__all__ = [ - "qubitOperator_to_Hamiltonian", - "geometry", - "get_molecular_data", - "active_space", - "fermionic_hamiltonian", - "spin_hamiltonian" -] - - -def qubitOperator_to_Hamiltonian(spin_h: openfermion.ops.operators.qubit_operator.QubitOperator, tol: Optional[float] = 1e-8) -> Hamiltonian: - r"""Transfer openfermion form to Paddle Quantum Hamiltonian form. - - Args: - spin_h: Hamiltonian in openfermion form. - tol: Value less than tol will be ignored. Defaults to 1e-8. - - Returns: - Hamiltonian in Paddle Quantum form. - """ - terms = spin_h.__str__().split('+\n') - spin_h.compress(abs_tol=tol) - pauli_str = [] - for term in terms: - decomposed_term = re.match(r"(.*) \[(.*)\].*", term).groups() - if decomposed_term[1] == '': - try: - pauli_str.append([float(decomposed_term[0]), 'I']) - except ValueError: - if complex(decomposed_term[0]).real > 0: - pauli_str.append([abs(complex(decomposed_term[0])), 'I']) - else: - pauli_str.append([-abs(complex(decomposed_term[0])), 'I']) - else: - term_str = ', '.join(re.split(r' ', decomposed_term[1])) - try: - pauli_str.append([float(decomposed_term[0]), term_str]) - except ValueError: - if complex(decomposed_term[0]).real > 0: - pauli_str.append([abs(complex(decomposed_term[0])), term_str]) - else: - pauli_str.append([-abs(complex(decomposed_term[0])), term_str]) - return Hamiltonian(pauli_str) - - -def _geo_str(geometry: list) -> str: - r"""String of molecular geometry information - - Args: - geometry: contains the geometry of the molecule, for example, the H2 molecule - [['H', [-1.68666, 1.79811, 0.0]], ['H', [-1.12017, 1.37343, 0.0]]] - - Returns: - String of molecular geometry information - """ - geo_str = '' - for item in geometry: - atom_symbol = item[0] - position = item[1] - line = '{} {} {} {}'.format(atom_symbol, - position[0], - position[1], - position[2]) - if len(geo_str) > 0: - geo_str += '\n' - geo_str += line - geo_str += '\nsymmetry c1' - return geo_str - - -def _run_psi4( - molecule: MolecularData, - charge: int, - multiplicity: int, - method: str, - basis: str, - if_print: bool, - if_save: bool -) -> None: - r"""The necessary information to calculate molecules, including one-body integrations and two-body integrations, as well as the energy of ground states by scf and fci methods. - - Args: - molecule: Class containing all information about a molecule. - charge: Charge of the molecule. - multiplicity: The multiplicity of the molecule. - method: Method used to calculate the ground state energy, including 'scf' and 'fci'. - basis: Most common used basis are ‘sto-3g’, ‘6-31g’. For more basis options, please refer to - https://psicode.org/psi4manual/master/basissets_byelement.html#apdx-basiselement. - if_print: If or not the base state energy of the molecule calculated by the selected method should be printed. - if_save: If the molecular information needs to be stored as an .hdf5 file. - """ - - import psi4 - psi4.set_memory('500 MB') - psi4.set_options({'soscf': 'false', - 'scf_type': 'pk'}) - geo = molecule.geometry - mol = psi4.geometry(_geo_str(geo)) - mol.set_multiplicity(multiplicity) - mol.set_molecular_charge(charge) - - if molecule.multiplicity == 1: - psi4.set_options({'reference': 'rhf', - 'guess': 'sad'}) - else: - psi4.set_options({'reference': 'rohf', - 'guess': 'gwh'}) - - # HF calculation - hf_energy, hf_wfn = psi4.energy('scf/' + basis, molecule=mol, return_wfn='on') - # Get orbitals and Fock matrix. - molecule.hf_energy = hf_energy - molecule.nuclear_repulsion = mol.nuclear_repulsion_energy() - molecule.canonical_orbitals = np.asarray(hf_wfn.Ca()) - molecule.overlap_integrals = np.asarray(hf_wfn.S()) - molecule.n_orbitals = molecule.canonical_orbitals.shape[0] - molecule.n_qubits = 2 * molecule.n_orbitals - molecule.orbital_energies = np.asarray(hf_wfn.epsilon_a()) - molecule.fock_matrix = np.asarray(hf_wfn.Fa()) - - # Get integrals using MintsHelper. - mints = psi4.core.MintsHelper(hf_wfn.basisset()) - - molecule.one_body_integrals = general_basis_change( - np.asarray(mints.ao_kinetic()), molecule.canonical_orbitals, (1, 0)) - molecule.one_body_integrals += general_basis_change( - np.asarray(mints.ao_potential()), molecule.canonical_orbitals, (1, 0)) - two_body_integrals = np.asarray(mints.ao_eri()) - two_body_integrals.reshape((molecule.n_orbitals, molecule.n_orbitals, - molecule.n_orbitals, molecule.n_orbitals)) - two_body_integrals = np.einsum('psqr', two_body_integrals) - two_body_integrals = general_basis_change( - two_body_integrals, molecule.canonical_orbitals, (1, 1, 0, 0)) - molecule.two_body_integrals = two_body_integrals - - # FCI calculation - psi4.set_options({'qc_module': 'detci'}) - fci_energy, fci_wfn = psi4.energy('fci/' + basis, molecule=mol, return_wfn='on') - molecule.fci_energy = fci_energy - - if if_save is True: - molecule.save() - - if if_print is True: - if method == 'scf': - print('Hartree-Fock energy for {} ({} electrons) is {}.'.format( - molecule.name, molecule.n_electrons, hf_energy)) - - elif method == 'fci': - print('FCI energy for {} ({} electrons) is {}.'.format( - molecule.name, molecule.n_electrons, fci_energy)) - elif method == '': - print('Calculation is done') - - -def geometry(structure: Optional[str] = None, file: Optional[str] = None) -> str: - r"""Read molecular geometry information. - - Args: - structure: Including molecular geometry information in string, take H2 as an example - ``[['H', [-1.68666, 1.79811, 0.0]], ['H', [-1.12017, 1.37343, 0.0]]]``. Defaults to None. - file: The path of .xyz file. Defaults to None. - - Raises: - AssertionError: The two optional input cannot be None simultaneously. - - Returns: - Molecular geometry information. - """ - if structure is None and file is None: - raise AssertionError('Input must be structure or .xyz file') - elif file is None: - shape = np.array(structure).shape - assert shape[1] == 2, 'The shape of structure must be (n, 2)' - for i in range(shape[0]): - assert isinstance(np.array(structure)[:, 0][i], str), 'The first position must be element symbol' - assert len(np.array(structure)[:, 1][i]) == 3, 'The second position represents coordinate ' \ - 'of particle: x, y, z' - geo = structure - elif structure is None: - assert file[-4:] == '.xyz', 'The file is supposed to be .xyz' - geo = [] - with open(file) as f: - for line in f.readlines()[2:]: - one_geo = [] - - symbol, x, y, z = line.split() - one_geo.append(symbol) - one_geo.append([float(x), float(y), float(z)]) - geo.append(one_geo) - - return geo - - -def get_molecular_data( - geometry: str, - charge: int=0, - multiplicity: int=1, - basis: str='sto-3g', - method: str='scf', - if_save: bool=True, - if_print: bool=True, - name: str="", - file_path: str="." -) -> MolecularData: - r"""Calculate necessary values of molecule, including one-body integrations, two-body integrations, and the ground state energy calculated by a chosen method - - Args: - geometry: Molecular geometry information. - charge: Molecular charge. Defaults to 0. - multiplicity: Molecular multiplicity. Defaults to 1. - basis: Most common used basis are ‘sto-3g’, ‘6-31g’. For more basis options, please refer to - https://psicode.org/psi4manual/master/basissets_byelement.html#apdx-basiselement. Defaults to 'sto-3g'. - method: Method to calculate ground state energy, including ``scf``, ``fci``. Defaults to ``scf``. - if_save: If need to save molecule information as .hdf5 file. Defaults to True. - if_print: If need to print ground state energy calculated by chosen method. Defaults to True. - name: The name of the file to save. Defaults to "". - file_path: The path of the file to save. Defaults to ".". - - Returns: - A class contains information of the molecule. - """ - methods = ['scf', 'fci'] - assert method in methods, 'We provide 2 methods: scf and fci' - - if if_save is True: - path = file_path + '/qchem_data/' - folder = os.path.exists(path) - if not folder: - os.makedirs(path) - if name == "": - elements = np.array(geometry)[:, 0] - symbol, counts = np.unique(elements, return_counts=True) - filename = path - for i in range(len(symbol)): - filename += symbol[i]+str(counts[i])+'_' - filename += basis + '_' + method + '.hdf5' - else: - if name[-5:] == '.hdf5': - filename = name - else: - filename = name + '.hdf5' - - molecule = MolecularData( - geometry, - basis=basis, - multiplicity=multiplicity, - charge=charge, - filename=filename - ) - - _run_psi4( - molecule, - charge, - multiplicity, - method, - basis, - if_print, - if_save - ) - - return molecule - - -def active_space( - electrons: int, - orbitals: int, - multiplicity: int=1, - active_electrons: int=None, - active_orbitals: int=None -) -> tuple: - r"""Calculate active space by nominating the number of active electrons and active orbitals. - - Args: - electrons: Number of total electrons. - orbitals: Number of total orbitals. - multiplicity: Spin multiplicity. Defaults to 1. - active_electrons: Number of active electrons, default to the case that all electrons are active. - active_orbitals: Number of active orbitals, default to the case that all orbitals are active. - - Returns: - Index for core orbitals and active orbitals. - """ - assert type(electrons) == int and electrons > 0, 'Number of electrons must be positive integer.' - assert type(orbitals) == int and orbitals > 0, 'Number of orbitals must be positive integer.' - assert type(multiplicity) == int and multiplicity >= 0, 'The multiplicity must be non-negative integer.' - - if active_electrons is None: - no_core_orbitals = 0 - core_orbitals = [] - else: - assert type(active_electrons) == int, 'The number of active electrons must be integer.' - assert active_electrons > 0, 'The number of active electrons must be greater than 0.' - assert electrons >= active_electrons, 'The number of electrons should more than or equal ' \ - 'to the number of active electrons.' - assert active_electrons >= multiplicity - 1, 'The number of active electrons should greater than ' \ - 'or equal to multiplicity - 1.' - assert multiplicity % 2 != active_electrons % 2, 'Mulitiplicity and active electrons should be one odd ' \ - 'and the other one even.' - - no_core_orbitals = (electrons - active_electrons) // 2 - core_orbitals = list(np.arange(0, no_core_orbitals)) - - if active_orbitals is None: - active_orbitals = list(np.arange(no_core_orbitals, orbitals)) - else: - assert type(active_orbitals) == int, 'The number of active orbitals must be integer.' - assert active_orbitals > 0, 'The number of active orbitals must be greater than 0.' - assert no_core_orbitals + active_orbitals <= orbitals, 'The summation of core orbitals and active ' \ - 'orbitals should be smaller than orbitals.' - assert no_core_orbitals + active_orbitals > (electrons + multiplicity - 1) / 2, \ - 'The summation of core orbitals and active orbitals should be greater than ' \ - '(electrons + multiplicity - 1)/2.' - - active_orbitals = list(np.arange(no_core_orbitals, no_core_orbitals + active_orbitals)) - - return core_orbitals, active_orbitals - - -def fermionic_hamiltonian( - molecule: MolecularData, - filename: str=None, - multiplicity: int=1, - active_electrons: int=None, - active_orbitals: int=None -) -> openfermion.ops.operators.qubit_operator.QubitOperator: - r"""Calculate the fermionic hamiltonian of the given molecule. - - Args: - molecule: A class contains information of the molecule. - filename: Path of .hdf5 file of molecule. Defaults to None. - multiplicity: Spin multiplicity. Defaults to 1. - active_electrons: Number of active electrons, default to the case that all electrons are active. - active_orbitals: Number of active orbitals, default to the case that all orbitals are active. - - Returns: - Hamiltonian in openfermion form. - """ - if molecule is None: - assert type(filename) == str, 'Please provide the path of .hdf5 file.' - assert filename[-5:] == '.hdf5', 'The filename is supposed to be .hdf5 file' - molecule = MolecularData(filename=filename) - core_orbitals, active_orbitals = active_space( - molecule.n_electrons, - molecule.n_orbitals, - multiplicity, - active_electrons, - active_orbitals) - terms_molecular_hamiltonian = molecule.get_molecular_hamiltonian( - occupied_indices=core_orbitals, active_indices=active_orbitals - ) - fermionic_hamiltonian = openfermion.transforms.get_fermion_operator(terms_molecular_hamiltonian) - - return fermionic_hamiltonian - - -def spin_hamiltonian( - molecule: openfermion.ops.operators.qubit_operator.QubitOperator , - filename: str=None, - multiplicity: int=1, - mapping_method: str='jordan_wigner', - active_electrons: int=None, - active_orbitals: int=None -) -> Hamiltonian: - r"""Generate Hamiltonian in Paddle Quantum form. - - Args: - molecule: Hamiltonian in openfermion form. - filename: Path of .hdf5 file of molecule. Defaults to None. - multiplicity: Spin multiplicity. Defaults to 1. - mapping_method: Transformation method, default to ``jordan_wigner``, besides, ``bravyi_kitaev`` is supported. Defaults to ``jordan_wigner``. - active_electrons: Number of active electrons, default to the case that all electrons are active. - active_orbitals: Number of active orbitals, default to the case that all orbitals are active. - - Returns: - Hamiltonian in Paddle Quantum form - """ - assert mapping_method in ['jordan_wigner', 'bravyi_kitaev'], "Please choose the mapping " \ - "in ['jordan_wigner', 'bravyi_kitaev']." - fermionic_h = fermionic_hamiltonian(molecule, - filename, - multiplicity, - active_electrons, - active_orbitals) - - if mapping_method == 'jordan_wigner': - spin_h = transforms.jordan_wigner(fermionic_h) - elif mapping_method == 'bravyi_kitaev': - spin_h = transforms.bravyi_kitaev(fermionic_h) - return qubitOperator_to_Hamiltonian(spin_h, tol=1e-8) diff --git a/paddle_quantum/qchem/slater_determinant.py b/paddle_quantum/qchem/slater_determinant.py deleted file mode 100644 index 5124076..0000000 --- a/paddle_quantum/qchem/slater_determinant.py +++ /dev/null @@ -1,95 +0,0 @@ -# !/usr/bin/env python3 -# Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -r""" -Slater determinant ansatz used in restricted Hartree Fock method. -""" - -import logging -from typing import Union, cast -import numpy as np -from scipy.stats import unitary_group -import openfermion -import paddle -import paddle_quantum as pq - -__all__ = ["RHFSlaterDeterminantModel"] - - -class GivensRotationBlock(pq.gate.Gate): - r"""This is a two-qubit gate performs the Givens rotation. - - .. math: - U(\theta)=e^{-i\frac{\theta}{2}(Y\otimes X-X\otimes Y)} - - Args: - pindex, qindex qubits where Givens rotation gate acts on. - theta: Givens rotation angle. - """ - def __init__( - self, - pindex: int, - qindex: int, - theta: float) -> None: - super().__init__(backend=pq.Backend.StateVector) - - cnot1 = pq.gate.CNOT([qindex, pindex]) - cnot2 = pq.gate.CNOT([pindex, qindex]) - ry_pos = pq.gate.RY(qindex, param=paddle.to_tensor(theta)) - ry_neg = pq.gate.RY(qindex, param=paddle.to_tensor(-theta)) - self.model = pq.ansatz.Sequential(cnot1, cnot2, ry_pos, cnot2, ry_neg, cnot1) - - def forward(self, state: pq.State) -> pq.State: - return self.model(state) - - -class RHFSlaterDeterminantModel(pq.gate.Gate): - r"""Slater determinant ansatz used in Restricted Hartree Fock calculation. - - Args: - n_qubits: number of qubits used to encode the Slater determinant state. - n_electrons: number of electrons inside the molecule. - mo_coeff: parameters used to initialize Slater determinant state. - """ - def __init__( - self, - n_qubits: int, - n_electrons: int, - mo_coeff: Union[np.array, None] = None) -> None: - assert (n_qubits % 2 == 0 and n_electrons % 2 == 0), "Restricted Hartree Fock calculation\ - should only be carried out for molecules with even number of electrons" - super().__init__(n_qubits, backend=pq.Backend.StateVector) - if mo_coeff is not None: - assert mo_coeff.shape == ( - n_electrons // 2, n_qubits // 2), f"The shape of `mo_coeff` should be {(n_electrons // 2, n_qubits // 2)},\ - but get {mo_coeff.shape}." - else: - logging.info("Will randomly initialize the circuit parameters.") - U = unitary_group.rvs(n_qubits // 2) - mo_coeff = U[:, :n_electrons // 2].T - - circuit_description = openfermion.slater_determinant_preparation_circuit(mo_coeff) - models = [] - for parallel_ops in circuit_description: - for op in parallel_ops: - qi, qj, theta, _ = cast((int, int, float, float), op) - givens_block_spinup = GivensRotationBlock(2 * qi, 2 * qj, theta) - givens_block_spindown = GivensRotationBlock(2 * qi + 1, 2 * qj + 1, theta) - models.append(givens_block_spinup) - models.append(givens_block_spindown) - self.model = pq.ansatz.Sequential(*models) - - def forward(self, state: pq.State) -> pq.State: - return self.model(state) diff --git a/paddle_quantum/qchem/uccsd.py b/paddle_quantum/qchem/uccsd.py deleted file mode 100644 index f8cb957..0000000 --- a/paddle_quantum/qchem/uccsd.py +++ /dev/null @@ -1,163 +0,0 @@ -# !/usr/bin/env python3 -# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -r""" -Unitary coupled cluster with singles and doubles for molecular ground state calculation. -""" - -from typing import Union -import numpy as np -import openfermion -from openfermion import FermionOperator -from openfermion.transforms import normal_ordered -import paddle_quantum as pq -from paddle_quantum.trotter import construct_trotter_circuit -from .qchem import qubitOperator_to_Hamiltonian - - -def _get_single_excitation_operator(p: int, q: int) -> FermionOperator: - r""" - Args: - p: index of the unoccupied orbital. - q: index of the occupied orbital. - - Returns: - FermionOperator, - .. math: - \hat{E}_{pq}\equiv\sum_{\sigma}\hat{c}^{\dagger}_{p\sigma}\hat{c}_{q\sigma} - """ - - return FermionOperator(f"{2 * p}^ {2 * q}") + FermionOperator(f"{2 * p + 1}^ {2 * q + 1}") - - -def _get_double_excitation_operator(p: int, q: int, r: int, s: int) -> FermionOperator: - r""" - Args: - p, r: index of the unoccupied orbital. - q, s: index of the occupied orbital. - - Returns: - FermionOperator, - .. math: - \hat{e}_{pqrs}=\sum_{\sigma\tau}\hat{c}^{\dagger}_{p\sigma}\hat{c}^{\dagger}_{r\tau}\hat{c}_{s\tau}\hat{c}_{q\sigma} - """ - - if p == r or q == s: - e2 = FermionOperator(f"{2 * p}^ {2 * r + 1}^ {2 * s + 1} {2 * q}") + \ - FermionOperator(f"{2 * p + 1}^ {2 * r}^ {2 * s} {2 * q + 1}") - else: - e2 = FermionOperator(f"{2 * p}^ {2 * r}^ {2 * s} {2 * q}") + \ - FermionOperator(f"{2 * p}^ {2 * r + 1}^ {2 * s + 1} {2 * q}") + \ - FermionOperator(f"{2 * p + 1}^ {2 * r}^ {2 * s} {2 * q + 1}") + \ - FermionOperator(f"{2 * p + 1}^ {2 * r + 1}^ {2 * s + 1} {2 * q + 1}") - return normal_ordered(e2) - - -def _get_antiH_single_excitation_operator(p: int, q: int) -> FermionOperator: - r""" - Args: - p: index of the unoccupied orbital. - q: index of the occupied orbital. - - Returns: - FermionOperator, - .. math: - \hat{E}_{pq}-\hat{E}_{qp} - """ - - e1_pq = _get_single_excitation_operator(p, q) - e1_qp = _get_single_excitation_operator(q, p) - return e1_pq - e1_qp - - -def _get_antiH_double_excitation_operator(p: int, q: int, r: int, s: int) -> FermionOperator: - r""" - Args: - p, r: index of the unoccupied orbital. - q, s: index of the occupied orbital. - - Returns: - FermionOperator, - .. math: - \hat{e}_{pqrs}-\hat{e}_{srqp} - """ - - e2_pqrs = _get_double_excitation_operator(p, q, r, s) - e2_srqp = _get_double_excitation_operator(s, r, q, p) - return e2_pqrs - e2_srqp - - -class UCCSDModel(pq.gate.Gate): - r"""Unitary Coupled Cluster ansatz for quantum chemistry calculation. - - .. note:: - UCCSD model typically results in REALLY deep quantum circuit. Training UCCSD ansatz for molecules beyond H2 is time consuming and demands better hardware. - - .. math:: - - \begin{align} - U(\theta)&=e^{\hat{T}-\hat{T}^{\dagger}}\\ - \hat{T}&=\hat{T}_1+\hat{T}_2\\ - \hat{T}_1&=\sum_{a\in{\text{virt}}}\sum_{i\in\text{occ}}t_{ai}\sum_{\sigma}\hat{c}^{\dagger}_{a\sigma}\hat{c}_{i\sigma}-h.c.\\ - \hat{T}_2&=\frac{1}{2}\sum_{a,b\in\text{virt}}\sum_{i,j\in\text{occ}}t_{aibj}\sum_{\sigma\tau}\hat{c}^{\dagger}_{a\sigma}\hat{c}^{\dagger}_{b\tau}\hat{c}_{j\tau}\hat{c}_{i\sigma}-h.c. - \end{align} - - Args: - n_qubits: number of qubits used to represent the quantum system. - n_electrons: number of electrons inside the system. - n_trotter_steps: number of Trotter steps required to build the UCCSD circuit. - single_excitation_amplitude: :math:`t_{ai}` in the definition of :math:`\hat{T}_1`. - double_excitation_amplitude: :math:`t_{aibj}` in the definition of :math:`\hat{T}_2`. - """ - def __init__( - self, - n_qubits: int, - n_electrons: int, - n_trotter_steps: int, - single_excitation_amplitude: Union[np.array, None] = None, - double_excitation_amplitude: Union[np.array, None] = None) -> None: - super().__init__(n_qubits, backend=pq.Backend.StateVector) - - n_occupied_mos = n_electrons // 2 - n_mos = n_qubits // 2 - occupied_orbitals = list(range(n_occupied_mos)) - virtual_orbitals = list(range(n_occupied_mos, n_mos)) - - if single_excitation_amplitude is None: - single_excitation_amplitude = np.random.randn(n_qubits, n_qubits) - if double_excitation_amplitude is None: - double_excitation_amplitude = np.random.randn(n_qubits, n_qubits, n_qubits, n_qubits) - - ucc_generator = FermionOperator() - for a in virtual_orbitals: - for i in occupied_orbitals: - ucc_generator += single_excitation_amplitude[a, i] * _get_antiH_single_excitation_operator(a, i) - for b in virtual_orbitals: - for j in occupied_orbitals: - ucc_generator += double_excitation_amplitude[ - a, i, b, j] * _get_antiH_double_excitation_operator(a, i, b, j) - - ucc_hamiltonian = -1j * ucc_generator - ucc_hamilton_qubit = openfermion.jordan_wigner(ucc_hamiltonian) - ucc_pqH = qubitOperator_to_Hamiltonian(ucc_hamilton_qubit) - self.uccsd_hamilton = ucc_pqH - - circuit = pq.ansatz.Circuit() - tau = 1.0 / n_trotter_steps - construct_trotter_circuit(circuit, ucc_pqH, tau=tau, steps=n_trotter_steps) - self.model = circuit - - def forward(self, state: pq.State) -> pq.State: - return self.model(state) diff --git a/paddle_quantum/qchem/utils.py b/paddle_quantum/qchem/utils.py new file mode 100644 index 0000000..89d00d2 --- /dev/null +++ b/paddle_quantum/qchem/utils.py @@ -0,0 +1,99 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +The utilities. +""" + +from typing import Tuple, Optional +from itertools import product +import numpy as np + +__all__ = ["orb2spinorb"] + + +def orb2spinorb( + num_modes: int, + single_ex_amps: Optional[np.ndarray] = None, + double_ex_amps: Optional[np.ndarray] = None +) -> Tuple[np.ndarray]: + r""" + Transform molecular orbital integral into spin orbital integral, assume + the quantum system is spin restricted. + + Args: + num_modes: Number of molecular orbitals. + single_ex_amps: One electron integral. + double_ex_amps: Two electron integral. + + Return: + The molecular integral in spin orbital form. + """ + if isinstance(single_ex_amps, np.ndarray): + assert single_ex_amps.shape == (num_modes, num_modes) + single_ex_amps_so = np.zeros((2*num_modes, 2*num_modes)) + for p in range(num_modes): + single_ex_amps_so[2*p, 2*p] = single_ex_amps[p, p] + single_ex_amps_so[2*p+1, 2*p+1] = single_ex_amps[p, p] + for q in range(p+1, num_modes): + single_ex_amps_so[2*p, 2*q] = single_ex_amps[p, q] + single_ex_amps_so[2*p+1, 2*q+1] = single_ex_amps[p, q] + + single_ex_amps_so[2*q, 2*p] = single_ex_amps[p, q] + single_ex_amps_so[2*q+1, 2*p+1] = single_ex_amps[p, q] + if isinstance(double_ex_amps, np.ndarray): + assert double_ex_amps.shape == (num_modes, num_modes, num_modes, num_modes) + double_ex_amps_so = np.zeros((2*num_modes, 2*num_modes, 2*num_modes, 2*num_modes)) + for p, r, s, q in product(range(num_modes), repeat=4): + double_ex_amps_so[2*p, 2*r, 2*s, 2*q] = double_ex_amps[p, r, s, q] + double_ex_amps_so[2*p+1, 2*r, 2*s, 2*q+1] = double_ex_amps[p, r, s, q] + double_ex_amps_so[2*p, 2*r+1, 2*s+1, 2*q] = double_ex_amps[p, r, s, q] + double_ex_amps_so[2*p+1, 2*r+1, 2*s+1, 2*q+1] = double_ex_amps[p, r, s, q] + + if isinstance(single_ex_amps, np.ndarray) and isinstance(double_ex_amps, np.ndarray): + return single_ex_amps_so, double_ex_amps_so + elif isinstance(single_ex_amps, np.ndarray): + return single_ex_amps_so + elif isinstance(double_ex_amps, np.ndarray): + return double_ex_amps_so + else: + raise ValueError("One of the `single_ex_amps` and `double_ex_amps` should be an np.ndarray.") + + +if __name__ == "__main__": + import numpy as np + from openfermion import InteractionOperator + from openfermion.utils import is_hermitian + + # build symmetric matrix + num_modes = 2 + A = np.random.randn(num_modes, num_modes) + A = A + A.T + B = np.random.randn(num_modes, num_modes, num_modes, num_modes) + B = B + np.transpose(B, (3, 2, 1, 0)) + + def test_array(): + P, Q = orb2spinorb(num_modes, A, B) + np.testing.assert_array_almost_equal(P, P.T) + np.testing.assert_array_almost_equal(A, P[::2, ::2]) + np.testing.assert_array_almost_equal(A, P[1:2*num_modes:2, 1:2*num_modes:2]) + np.testing.assert_array_almost_equal(Q, np.transpose(Q, (3, 2, 1, 0))) + np.testing.assert_array_almost_equal(Q[::2, ::2, ::2, ::2], B) + np.testing.assert_array_almost_equal(Q[1:2*num_modes:2, ::2, ::2, 1:2*num_modes:2], B) + + V = InteractionOperator(0.0, P, Q) + assert is_hermitian(V) + + test_array() \ No newline at end of file diff --git a/paddle_quantum/qinfo.py b/paddle_quantum/qinfo.py index abe02bc..842fd7e 100644 --- a/paddle_quantum/qinfo.py +++ b/paddle_quantum/qinfo.py @@ -22,16 +22,22 @@ import re import numpy as np from scipy.linalg import logm, sqrtm +from scipy.stats import unitary_group import cvxpy import matplotlib.image +from itertools import product import paddle import paddle_quantum as pq from .state import State, _type_fetch, _type_transform from .base import get_dtype -from .intrinsic import _get_float_dtype -from .linalg import dagger, NKron, unitary_random +from .linalg import abs_norm, dagger, NKron, unitary_random, is_positive from .channel.custom import ChoiRepr, KrausRepr, StinespringRepr +from .channel.custom import ( + _choi_to_kraus, _choi_to_stinespring, + _kraus_to_choi, _kraus_to_stinespring, + _stinespring_to_choi, _stinespring_to_kraus +) from typing import Optional, Tuple, List, Union @@ -399,6 +405,39 @@ def partial_transpose(density_op: Union[np.ndarray, paddle.Tensor, State], return density_op.numpy() if type_str == "numpy" else density_op +def permute_systems(mat: Union[np.ndarray, paddle.Tensor, State], + perm_list: List[int], dim_list: List[int]) -> Union[np.ndarray, paddle.Tensor, State]: + r"""Permute quantum system based on a permute list + + Args: + mat: A given matrix representation which is usually a quantum state. + perm: The permute list. e.g. input ``[0,2,1,3]`` will permute the 2nd and 3rd subsystems. + dim: A list of dimension sizes of each subsystem. + + Returns: + The permuted matrix + """ + #TODO: modifying the code with paddle.tranpose + if len(perm_list) != len(dim_list): + raise ValueError(f"The dimensions does not match: expected {len(perm_list)}, received {len(dim_list)}.") + + mat_type = _type_fetch(mat) + mat = _type_transform(mat, "numpy") + + shape = mat.shape + if any(np.array(shape) - 2**5 * np.ones(len(shape))): + warnings.warn("The method is inefficient for large systems.") + perm_mat = np.zeros(shape, dtype = "complex128") + perm_idx = np.array(list(product(range(dim_list[0]), repeat=len(dim_list))))[:, perm_list] + for i in range(shape[0]): + for j in range(shape[1]): + row_perm_idx = sum(perm_idx[i] * dim_list ** np.array(range(len(perm_idx[i])-1, -1, -1))) + col_perm_idx = sum(perm_idx[j] * dim_list ** np.array(range(len(perm_idx[i])-1, -1, -1))) + perm_mat[row_perm_idx, col_perm_idx] = mat[i,j].item() + + return _type_transform(perm_mat, mat_type) + + def negativity(density_op: Union[np.ndarray, paddle.Tensor, State]) -> Union[np.ndarray, paddle.Tensor]: r"""Compute the Negativity :math:`N = ||\frac{\rho^{T_A}-1}{2}||` of the input quantum state. @@ -450,6 +489,31 @@ def is_ppt(density_op: Union[np.ndarray, paddle.Tensor, State]) -> bool: return bool(negativity(density_op) <= 0) +def is_choi(op: Union[np.ndarray, paddle.Tensor]) -> bool: + r"""Check if the input op is a Choi operator of a quantum operation. + + Args: + op: matrix form of the linear operation. + + Returns: + Whether the input op is a valid quantum operation Choi operator. + + Note: + The operation op is (default) applied to the second system. + """ + op = _type_transform(op, "tensor").cast('complex128') + shape = op.shape + n = int(math.log2(shape[-1])) + sys_dim = 2 ** (n // 2) + + # CP condition and Trace non-increasing condition + if is_positive(op): + + op_partial = partial_trace(op, sys_dim, sys_dim, 2) + + return is_positive(paddle.eye(sys_dim) - op_partial) + return False + def schmidt_decompose(psi: Union[np.ndarray, paddle.Tensor, State], sys_A: List[int]=None) -> Union[Tuple[paddle.Tensor, paddle.Tensor, paddle.Tensor], Tuple[np.ndarray, np.ndarray, np.ndarray]]: @@ -670,7 +734,7 @@ def tensor_state(state_a: State, state_b: State, *args: State) -> State: Returns: tensor product state of input states - Notes: + Note: Need to be careful with the backend of states; Use ``paddle_quantum.linalg.NKron`` if the input datatype is ``paddle.Tensor`` or ``numpy.ndarray``. @@ -705,14 +769,14 @@ def diamond_norm(channel_repr: Union[ChoiRepr, KrausRepr, StinespringRepr, paddl Theory of Computing 5.1(2009):217-238. ''' if isinstance(channel_repr, ChoiRepr): - choi_matrix = channel_repr.choi_oper + choi_matrix = channel_repr.choi_repr elif isinstance(channel_repr, paddle.Tensor): choi_matrix = channel_repr elif isinstance(channel_repr, (KrausRepr, StinespringRepr)): warnings.warn('`channel_repr` is not in Choi representaiton, and is converted into `ChoiRepr`') - choi_matrix = channel_convert(channel_repr, 'Choi').choi_oper + choi_matrix = channel_repr.to_choi().choi_repr else: raise RuntimeError('`channel_repr` must be `ChoiRepr`or `KrausRepr` or `StinespringRepr` or `paddle.Tensor`.') @@ -753,19 +817,21 @@ def diamond_norm(channel_repr: Union[ChoiRepr, KrausRepr, StinespringRepr, paddl return prob.solve(**kwargs) -def channel_convert(original_channel: Union[ChoiRepr, KrausRepr, StinespringRepr], target: str, tol: float = 1e-6) -> Union[ChoiRepr, KrausRepr, StinespringRepr]: - r"""convert the given channel to the target implementation +def channel_repr_convert(representation: Union[paddle.Tensor, np.ndarray, List[paddle.Tensor], List[np.ndarray]], source: str, + target: str, tol: float = 1e-6) -> Union[paddle.Tensor, np.ndarray, List[paddle.Tensor], List[np.ndarray]]: + r"""convert the given representation of a channel to the target implementation Args: - original_channel: input quantum channel - target: target implementation, should to be ``Choi``, ``Kraus`` or ``Stinespring`` - tol: error tolerance of the convert, 1e-6 by default + representation: input representation + source: input form, should be ``'Choi'``, ``'Kraus'`` or ``'Stinespring'`` + target: target form, should be ``'Choi'``, ``'Kraus'`` or ``'Stinespring'`` + tol: error tolerance for the conversion from Choi, :math:`10^{-6}` by default Raises: ValueError: Unsupported channel representation: require Choi, Kraus or Stinespring. Returns: - Union[ChoiRepr, KrausRepr]: quantum channel by the target implementation + quantum channel by the target implementation Note: choi -> kraus currently has the error of order 1e-6 caused by eigh @@ -774,75 +840,145 @@ def channel_convert(original_channel: Union[ChoiRepr, KrausRepr, StinespringRepr NotImplementedError: does not support the conversion of input data type """ - target = target.capitalize() + source, target = source.capitalize(), target.capitalize() if target not in ['Choi', 'Kraus', 'Stinespring']: raise ValueError(f"Unsupported channel representation: require Choi, Kraus or Stinespring, not {target}") - if type(original_channel).__name__ == f'{target}Repr': - return original_channel - - if isinstance(original_channel, KrausRepr) and target == 'Choi': - kraus_oper = original_channel.kraus_oper - ndim = original_channel.kraus_oper[0].shape[0] - kraus_oper_tensor = paddle.concat([paddle.kron(x, x.conj().T) for x in kraus_oper]).reshape([len(kraus_oper), ndim, -1]) - chan = paddle.sum(kraus_oper_tensor, axis=0).reshape([ndim for _ in range(4)]).transpose([2, 1, 0, 3]) - choi_repr = chan.transpose([0, 2, 1, 3]).reshape([ndim * ndim, ndim * ndim]) - result = ChoiRepr(choi_repr, original_channel.qubits_idx) - result.qubits_idx = original_channel.qubits_idx - - elif isinstance(original_channel, KrausRepr) and target == 'Stinespring': - kraus_oper = original_channel.kraus_oper - j_dim = len(kraus_oper) - i_dim = kraus_oper[0].shape[0] - kraus_oper_tensor = paddle.concat(kraus_oper).reshape([j_dim, i_dim, -1]) - stinespring_mat = kraus_oper_tensor.transpose([1, 0, 2]) - stinespring_mat = stinespring_mat.reshape([i_dim * j_dim, i_dim]) - result = StinespringRepr(stinespring_mat, original_channel.qubits_idx) - result.qubits_idx = original_channel.qubits_idx - - elif isinstance(original_channel, ChoiRepr) and target == 'Kraus': - choi_repr = original_channel.choi_oper - ndim = int(math.sqrt(original_channel.choi_oper.shape[0])) - w, v = paddle.linalg.eigh(choi_repr) - - # add abs to make eigvals safe - w = paddle.abs(w) - l_cut = 0 - for l in range(len(w) - 1, -1, -1): - if paddle.sum(paddle.abs(w[l:])) / paddle.sum(paddle.abs(w)) > 1 - tol: - l_cut = l - break - kraus = [(v * paddle.sqrt(w))[:, l].reshape([ndim, ndim]).T for l in range(l_cut, ndim**2)] - - result = KrausRepr(kraus, original_channel.qubits_idx) - result.qubits_idx = original_channel.qubits_idx - - elif isinstance(original_channel, StinespringRepr) and target == 'Kraus': - stinespring_mat = original_channel.stinespring_matrix - i_dim = stinespring_mat.shape[1] - j_dim = stinespring_mat.shape[0] // i_dim - kraus_oper = stinespring_mat.reshape([i_dim, j_dim, i_dim]).transpose([1, 0, 2]) - kraus_oper = [kraus_oper[j] for j in range(j_dim)] - result = KrausRepr(kraus_oper, original_channel.qubits_idx) - result.qubits_idx = original_channel.qubits_idx + if source == target: + return representation + if source not in ['Choi', 'Kraus', 'Stinespring']: + raise ValueError(f"Unsupported channel representation: require Choi, Kraus or Stinespring, not {source}") + is_ndarray = False + if isinstance(representation, np.ndarray): + is_ndarray = True + representation = paddle.to_tensor(representation) + elif isinstance(representation, List) and isinstance(representation[0], np.ndarray): + is_ndarray = True + representation = [paddle.to_tensor(representation[i]) for i in range(len(representation))] + + if source == 'Choi': + if target == 'Kraus': + representation = _choi_to_kraus(representation, tol) + return [representation[i].numpy() for i in range(len(representation))] if is_ndarray else representation + + # stinespring repr + representation = _choi_to_stinespring(representation, tol) - else: - raise NotImplementedError( - f"does not support the conversion of {type(original_channel)}") + elif source == 'Kraus': + representation = representation if isinstance(representation, List) else [representation] + representation = _kraus_to_choi(representation) if target == 'Choi' else _kraus_to_stinespring(representation) + + else: # if source == 'Stinespring' + if target == 'Kraus': + representation = _stinespring_to_kraus(representation) + return [representation[i].numpy() for i in range(len(representation))] if is_ndarray else representation + + # choi repr + representation = _stinespring_to_choi(representation) + + return representation.numpy() if is_ndarray else representation + + +def random_channel(num_qubits: int, rank: int = None, target: str = 'Kraus') -> Union[paddle.Tensor, List[paddle.Tensor]]: + r"""Generate a random channel from its Stinespring representation + + Args: + num_qubits: number of qubits :math:`n` + rank: rank of this Channel. Defaults to be random sampled from :math:`[0, 2^n]` + target: target representation, should to be ``'Choi'``, ``'Kraus'`` or ``'Stinespring'`` - return result + Returns: + the target representation of a random channel. + + """ + target = target.capitalize() + dim = 2 ** num_qubits + rank = np.random.randint(dim) + 1 if rank is None else rank + assert 1 <= rank <= dim, \ + f"rank must be positive and no larger than the dimension {dim} of the channel: received {rank}" + + # rank = 1 + unitary = unitary_group.rvs(rank * dim) + stinespring_mat = paddle.to_tensor(unitary[:, :dim], dtype=pq.get_dtype()).reshape([rank, dim, dim]) + list_kraus = [stinespring_mat[j] for j in list(range(rank))] + + if target == 'Choi': + return channel_repr_convert(list_kraus, source='Kraus', target='Choi') + elif target == 'Stinespring': + return channel_repr_convert(list_kraus, source='Kraus', target='Stinespring') + return list_kraus -def kraus_oper_random(num_qubits: int, num_oper: int) -> list: - r""" randomly generate a set of kraus operators for quantum channel +def kraus_unitary_random(num_qubits: int, num_oper: int) -> list: + r""" randomly generate a set of unitaries as kraus operators for a quantum channel Args: num_qubits: The amount of qubits of quantum channel. - num_oper: The amount of kraus operators to be generated. + num_oper: The amount of unitaries to be generated. Returns: a list of kraus operators """ - float_dtype = _get_float_dtype(get_dtype()) - prob = [paddle.sqrt(paddle.to_tensor(1/num_oper, dtype = float_dtype))] * num_oper + prob = paddle.rand([num_oper]) + prob = paddle.sqrt(prob / paddle.sum(prob)).cast(get_dtype()) return [prob[idx] * unitary_random(num_qubits) for idx in range(num_oper)] + + +def grover_generation(oracle: Union[np.ndarray, paddle.Tensor]) -> Union[np.ndarray, paddle.Tensor]: + r"""Construct the Grover operator based on ``oracle``. + + Args: + oracle: the input oracle :math:`A` to be rotated. + + Returns: + Grover operator in form + + .. math:: + + G = A (2 |0^n \rangle\langle 0^n| - I^n) A^\dagger \cdot (I - 2|1 \rangle\langle 1|) \otimes I^{n-1} + + """ + type_str = _type_fetch(oracle) + oracle = _type_transform(oracle, "tensor") + complex_dtype = oracle.dtype + dimension = oracle.shape[0] + ket_zero = paddle.eye(dimension, 1).cast(complex_dtype) + + diffusion_op = (2 + 0j) * ket_zero @ ket_zero.T - paddle.eye(dimension).cast(complex_dtype) + reflection_op = paddle.kron(paddle.to_tensor([[1, 0], [0, -1]], dtype=complex_dtype), paddle.eye(dimension // 2)) + + grover = oracle @ diffusion_op @ dagger(oracle) @ reflection_op + return grover.numpy() if type_str == 'numpy' else grover + + +def qft_generation(num_qubits: int) -> paddle.Tensor: + r"""Construct the quantum fourier transpose (QFT) gate. + + Args: + num_qubits: number of qubits :math:`n` st. :math:`N = 2^n`. + + Returns: + a gate in below matrix form, here :math:`\omega_N = \text{exp}(\frac{2 \pi i}{N})` + + .. math:: + + \begin{align} + QFT = \frac{1}{\sqrt{N}} + \begin{bmatrix} + 1 & 1 & .. & 1 \\ + 1 & \omega_N & .. & \omega_N^{N-1} \\ + .. & .. & .. & .. \\ + 1 & \omega_N^{N-1} & .. & \omega_N^{(N-1)^2} + \end{bmatrix} + \end{align} + + """ + N = 2 ** num_qubits + omega_N = np.exp(1j * 2 * math.pi / N) + + qft_mat = np.ones([N, N], dtype=get_dtype()) + for i in range(1, N): + for j in range(1, N): + qft_mat[i, j] = omega_N ** ((i * j) % N) + + return paddle.to_tensor(qft_mat / math.sqrt(N)) diff --git a/paddle_quantum/qml/qnnmic.py b/paddle_quantum/qml/qnnmic.py new file mode 100644 index 0000000..6742bec --- /dev/null +++ b/paddle_quantum/qml/qnnmic.py @@ -0,0 +1,458 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +The medical image classification model based on quantum neural networks. +""" + +import logging +import warnings +from tqdm import tqdm +from typing import Optional, List, Tuple, Union + +from PIL import Image +from sklearn.preprocessing import StandardScaler, MinMaxScaler +from sklearn.decomposition import PCA +from imblearn.combine import SMOTETomek +import numpy as np +import paddle +import paddle.nn.functional as F +from paddle.io import Dataset, DataLoader +import paddle_quantum as pq +from paddle_quantum.ansatz import Circuit + + +warnings.filterwarnings("ignore", category=Warning) +pq.set_dtype('complex128') + + +class ImageDataset(Dataset): + r""" + The class used for loading classical datasets. + + Args: + file_path: The path of the input image. + num_samples: The number of the data in the test dataset. + task: The training, validation, or testing task. + pca: Whether use principal component analysis. Defaults to None. + scaler: Whether scale the data. Defaults to None. + centering: Whether remove the mean. Defaults to None. + + Raises: + ValueError: If the task is not training, validation, or test, raises the error. + """ + def __init__(self, file_path: str, num_samples: int, task: str, pca: PCA=None, scaler: StandardScaler=None, centering: MinMaxScaler=None): + super().__init__() + # load data + data, labels = [], [] + npz_file = np.load('medical_image/' + file_path + '.npz') + + if task == "train": + data_set = list(npz_file['train_images']) + data_label = list(npz_file['train_labels']) + data_set = np.array(data_set) + data_label = np.array(data_label) + + elif task == "val": + data_set = npz_file['val_images'] + data_label = npz_file['val_labels'] + elif task == 'test': + data_set = npz_file['test_images'] + data_label = npz_file['test_labels'] + else: + raise ValueError + + data = np.reshape(data_set, (data_set.shape[0], data_set.shape[1] * data_set.shape[2])) + + # use sampling to avoid imbalance classification + if task == "train": + smote_tomek = SMOTETomek(random_state=0) + data, data_label = smote_tomek.fit_resample(data,data_label) + + for l in data_label: + if l == np.array([1]): + labels.append([1.0, 0.0]) + else: + labels.append([0.0, 1.0]) + labels = np.array(labels) + + if pca is None: # training task + # preprocess + # remove the mean + self.centering = StandardScaler(with_std=False) + self.centering.fit(data) + data = self.centering.transform(data) + # PCA + self.pca = PCA(n_components=16) + self.pca.fit(data) + data = self.pca.transform(data) + # scale the data + self.scaler = MinMaxScaler((0, np.pi)) + self.scaler.fit(data) + data = self.scaler.transform(data) + else: # test or validation + data = centering.transform(data) + data = pca.transform(data) + data = scaler.transform(data) + + if num_samples == -1: + self.num_samples = len(data) + elif num_samples >= len(data): + self.num_samples = len(data) + else: + self.num_samples = num_samples + + idx = np.arange(len(data)) + np.random.seed(0) + np.random.shuffle(idx) + data, labels = data[idx], labels[idx] + self.image_list = data[:self.num_samples] + self.label_list = labels[:self.num_samples] + + + def __getitem__(self, idx): + return self.image_list[idx], self.label_list[idx] + + def __len__(self): + return self.num_samples + + +def _filter_circuit(num_qubits: int, depth:int) -> pq.ansatz.Circuit: + r""" + The function that generates a filter circuit for extracting features. + """ + cir = Circuit(num_qubits) + + cir.complex_entangled_layer(qubits_idx='full', depth=depth) + + return cir + + +def _encoding_circuit(num_qubits: int, data: paddle.Tensor) -> pq.ansatz.Circuit: + r""" + The function that encodes the classical data into quantum states. + """ + cir = Circuit(num_qubits) + depth = int(np.ceil(len(data)/num_qubits)) + t = 0 + + for d in range(depth): + if d%2==0: + for q in range(num_qubits): + cir.ry(qubits_idx=q, param=data[t%len(data)]) + t += 1 + else: + for q in range(num_qubits): + cir.rx(qubits_idx=q, param=data[t%len(data)]) + t += 1 + + return cir + + +class QNNMIC(paddle.nn.Layer): + r""" + The class of the medical image classification using quantum neural networks (QNN). + + Args: + num_qubits: The number of qubits of quantum circuit in each layer. Defaults to ``[8, 8]``. + num_depths: The number of depths of quantum circuit in each layer. Defaults to ``[2, 2]`` + observables: The observables of quantum circuit in each layer. Defaults to ``[['Z0','Z1','Z2','Z3'], ['X0','X1','X2','X3']]``. + """ + def __init__(self, num_qubits: List[int], num_depths: List[int], observables: List) -> None: + super(QNNMIC, self).__init__() + self.num_qubits = num_qubits + self.num_depths = num_depths + self.observables = observables + self.cirs = paddle.nn.Sequential(*[_filter_circuit(num_qubits[j], num_depths[j]) for j in range(len(self.num_qubits))]) + + self.fc = paddle.nn.Linear(len(self.observables[-1]), 2, + weight_attr=paddle.ParamAttr(initializer=paddle.nn.initializer.Normal()), + bias_attr=paddle.ParamAttr(initializer=paddle.nn.initializer.Normal())) + + + def forward(self, batch_input: List[paddle.Tensor]) -> paddle.Tensor: + r""" + The forward function. + + Args: + batch_input: The input of the model. It's shape is :math:`(\text{batch_size}, 2^{\text{num_qubits}})` . + + Returns: + Return the output of the model. It's shape is :math:`(\text{batch_size}, -1)` . + """ + features = [] + + # quantum part + for data in batch_input: + # from layer one to layer N + for j in range(len(self.num_qubits)): + if j==0: # initialization + f_i = data + enc_cir = _encoding_circuit(self.num_qubits[j], f_i) + init_state = pq.state.zero_state(self.num_qubits[j]) + enc_state = enc_cir(init_state) + layer_state = self.cirs[j](enc_state) + + f_j = [] + for ob_str in self.observables[j]: + ob = pq.Hamiltonian([[1.0, ob_str]]) + expecval = pq.loss.ExpecVal(ob) + f_ij = expecval(layer_state) + f_j.append(f_ij) + f_i = paddle.concat(f_j) + + features.append(f_i) + + features = paddle.stack(features) + + # classical part + outputs = self.fc(features) + + return F.softmax(outputs) + + +def train( + model_name: str, num_qubits: List, num_depths: List[int], observables: List, + batch_size: int=20, num_epochs: int=4, learning_rate: float = 0.1, + dataset: str = 'pneumoniamnist', saved_dir: str = './', + using_validation: bool = False, + num_train: int = -1, num_val: int = -1, num_test: int = -1, + early_stopping: Optional[int] = 1000, num_workers: Optional[int] = 0 +) -> None: + """ + The function of training the QNNMIC model. + + Args: + model_name: The name of the model, which is used to save the model. + num_qubits: The number of qubits of quantum circuit in each layer. + num_depths: The depth of quantum circuit in each layer. + observables: The observables of quantum circuit in each layer. + batch_size: The size of the batch samplers. Defaults to ``20`` . + num_epochs: The number of epochs to train the model. Defaults to ``4`` . + learning_rate: The learning rate used to update the parameters. Defaults to ``0.1``. + dataset: The path of the dataset. It defaults to ``pneumoniamnist``. + saved_dir: The path used to save logs. Defaults to ``./``. + using_validation: Whether use the validation. It is false means the dataset only contains training datasets and test datasets. + num_train: The number of the data in the training dataset. Defaults to ``-1`` , which will use all training data. + num_val: The number of the data in the validation dataset. Defaults to ``-1`` , which will use all validation data. + num_test: The number of the data in the test dataset. Defaults to ``-1`` , which will use all test data. + early_stopping: Number of the iterations with no improvement after which training will be stopped. Defaults to ``1000``. + num_workers: The number of subprocess to load data, 0 for no subprocess used and loading data in main process. Defaults to 0. + """ + if saved_dir[-1] != '/': + saved_dir += '/' + + logging.basicConfig( + filename=f'{saved_dir}{model_name}.log', + filemode='w', + format='%(asctime)s %(levelname)s %(message)s', + level=logging.INFO, + ) + + train_dataset = ImageDataset(file_path=dataset, num_samples=num_train, task="train") + train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=num_workers) + + test_dataset = ImageDataset(file_path=dataset, num_samples=num_test, task='test', + pca=train_dataset.pca, scaler=train_dataset.scaler, centering=train_dataset.centering) + test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=True, num_workers=num_workers) + + if using_validation: + val_dataset = ImageDataset(file_path=dataset, num_samples=num_val, task='val', + pca=train_dataset.pca, scaler=train_dataset.scaler, centering=train_dataset.centering) + val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=True, num_workers=num_workers) + + paddle.seed(0) + model = QNNMIC(num_qubits, num_depths, observables) + model.train() + opt = paddle.optimizer.Adam(learning_rate=learning_rate, parameters=model.parameters()) + + total_batch = 0 + val_best_loss = float('inf') + last_improve = 0 + stopping_flag = False + + for epoch in range(num_epochs): + p_bar = tqdm( + total=len(train_loader), + desc=f'Epoch[{epoch: 3d}]', + ascii=True, + dynamic_ncols=True, + ) + + for images, labels in train_loader: + p_bar.update(1) + prob = model(images) + loss = - paddle.mean(paddle.log(prob) * labels) + loss.backward() + opt.minimize(loss) + opt.clear_grad() + + if total_batch % 10 == 0: + predicts = paddle.argmax(prob, axis=1).tolist() + labels = paddle.argmax(labels, axis=1).tolist() + train_acc = sum(labels[idx] == predicts[idx] for idx in range(len(labels))) / len(labels) + if using_validation: + with paddle.no_grad(): + val_loss, val_acc = evaluate(model, val_loader) + if val_loss < val_best_loss: + paddle.save(model.state_dict(), f'{saved_dir}/{model_name}.pdparams') + improve = '*' + last_improve = total_batch + val_best_loss = val_acc + else: + improve = ' ' + msg = ( + f"Iter:{total_batch: 5d}, Train loss:{loss.item(): 3.5f}, acc:{train_acc: 3.2%}; " + f"Val loss:{val_loss: 3.5f}, acc:{val_acc: 3.2%}{improve}" + ) + else: + with paddle.no_grad(): + test_loss, test_acc = evaluate(model, test_loader) + paddle.save(model.state_dict(), f'{saved_dir}{model_name}.pdparams') + msg = ( + f"Iter:{total_batch: 5d}, Train loss:{loss.item(): 3.5f}, acc:{train_acc: 3.2%}; " + f"Test loss:{test_loss: 3.5f}, acc:{test_acc: 3.2%}" + ) + + model.train() + p_bar.set_postfix_str(msg) + logging.info(msg) + total_batch += 1 + if using_validation and total_batch - last_improve >= early_stopping: + stopping_flag = True + break + + p_bar.close() + if stopping_flag: + break + + if stopping_flag: + msg = "No optimization for a long time, auto-stopping..." + else: + msg = "The training of the model has been finished." + logging.info(msg) + print(msg) + + if using_validation: + test(model, saved_dir, model_name, test_loader) + else: + paddle.save(model.state_dict(), f'{saved_dir}/{model_name}.pdparams') + with paddle.no_grad(): + test_loss, test_acc = evaluate(model, test_loader) + msg = f"Test loss: {test_loss:3.5f}, acc: {test_acc:3.2%}" + logging.info(msg) + print(msg) + + +def evaluate( + model:paddle.nn.Layer, + data_loader: paddle.io.DataLoader +) -> Tuple[float, float]: + r""" + Evaluating the performance of the model on the dataset. + + Args: + model: The QNN model. + data_loader: The data loader for the data. + + Returns: + Return the accuracy of the model on the given datasets. + """ + val_loss = 0 + model.eval() + labels_all = [] + predicts_all = [] + + with paddle.no_grad(): + for images, labels in data_loader: + prob = model(images) + loss = - paddle.mean(paddle.log(prob) * labels) + labels = paddle.argmax(labels, axis=1).tolist() + val_loss += loss.item() * len(labels) + labels_all.extend(labels) + predict = paddle.argmax(prob, axis=1).tolist() + predicts_all.extend(predict) + val_acc = sum(labels_all[idx] == predicts_all[idx] for idx in range(len(labels_all))) + + return val_loss / len(labels_all), val_acc / len(labels_all) + + +def test( + model: paddle.nn.Layer, + saved_dir: str, + model_name: str, + test_loader: paddle.io.DataLoader +) -> None: + r""" + Evaluating the performance of the model on the test dataset. + + Args: + model: QNN model. + saved_dir: The path of the saved model. + model_name: The name of the model. + test_loader: The data loader for testing datasets. + """ + model.set_state_dict(paddle.load(f'{saved_dir}{model_name}.pdparams')) + test_loss, test_acc = evaluate(model, test_loader) + msg = f"Test loss: {test_loss:3.5f}, acc: {test_acc:3.2%}" + logging.info(msg) + print(msg) + + +def inference( + image_path: str, num_samples: int, model_path: str, + num_qubits: List, num_depths: List, observables: List, +) -> Union[float, list]: + r""" + The prediction function for the provided test dataset. + + Args: + image_path: The path of the input image. + num_samples: The number of the data need to be classified. + model_path: The path of the trained model, which will be loaded. + num_qubits: The number of qubits of quantum circuit in each layer. + num_depths: The depth of quantum circuit in each layer. + observables: The observables of quantum circuit in each layer. + + Returns: + Return the prediction of the given datasets together with the associated level of certainty. + """ + logging.basicConfig( + filename=f'./{"qnnmic"}.log', + filemode='w', + format='%(asctime)s %(levelname)s %(message)s', + level=logging.INFO, + ) + msg = f"Start Inferencing" + logging.info(msg) + + model = QNNMIC(num_qubits, num_depths, observables) + model.set_state_dict(paddle.load(model_path)) + train_dataset = ImageDataset(file_path="pneumoniamnist", num_samples=num_samples, task="train") + dataset = ImageDataset(file_path=image_path, num_samples=num_samples, task='test', pca=train_dataset.pca, scaler=train_dataset.scaler, centering=train_dataset.centering) + + model.eval() + prob = model(paddle.to_tensor(dataset.image_list)) + prediction = [int(1-paddle.argmax(i)) for i in prob] + label = np.argmin(dataset.label_list[:num_samples], axis=1) + + msg = f"Finish Inferencing" + logging.info(msg) + + return prediction, prob.tolist(), label.tolist() + + +if __name__ == '__main__': + exit(0) \ No newline at end of file diff --git a/paddle_quantum/qml/qnnqd.py b/paddle_quantum/qml/qnnqd.py new file mode 100644 index 0000000..3007ceb --- /dev/null +++ b/paddle_quantum/qml/qnnqd.py @@ -0,0 +1,443 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +The quality detection model based on quantum neural networks. +""" + +import logging +import os +import warnings +from tqdm import tqdm +from typing import Optional, List, Tuple, Union + +from PIL import Image +from sklearn.preprocessing import StandardScaler, MinMaxScaler +from sklearn.decomposition import PCA +import numpy as np +import paddle +import paddle.nn.functional as F +from paddle.io import Dataset, DataLoader +import paddle_quantum as pq +from paddle_quantum.ansatz import Circuit + +warnings.filterwarnings("ignore", category=Warning) +pq.set_dtype('complex128') + + +class ImageDataset(Dataset): + r""" + The class used for loading classical datasets. + + Args: + file_path: The path of the input image. + num_samples: The number of the data in the test dataset. + pca: Whether use principal component analysis. Default to None. + scaler: Whether scale the data. Default to None. + centering: Whether remove the mean. Default to None. + + """ + def __init__( + self, file_path: str, num_samples: int, pca: PCA=None, + scaler: StandardScaler=None, centering: MinMaxScaler=None + ): + super().__init__() + + # load data + data, labels = [], [] + pos_files = os.listdir(file_path + 'Positive/') + neg_files = os.listdir(file_path + 'Negative/') + + for fd in pos_files: + pos_img = Image.open(file_path + 'Positive/' + fd) + data.append(np.reshape(pos_img, 784)) + labels.append([1.0, 0.0]) + + for fd in neg_files: + pos_img = Image.open(file_path + 'Negative/' + fd) + data.append(np.reshape(pos_img, 784)) + labels.append([0.0, 1.0]) + + data = np.array(data) + labels = np.array(labels) + idx = np.arange(len(data)) + np.random.seed(0) + np.random.shuffle(idx) + data, labels = data[idx], labels[idx] + + if pca is None: # training task + # preprocess + # remove the mean + self.centering = StandardScaler(with_std=False) + self.centering.fit(data) + data = self.centering.transform(data) + # PCA + self.pca = PCA(n_components=16) + self.pca.fit(data) + data = self.pca.transform(data) + # scale the data + self.scaler = MinMaxScaler((0, np.pi)) + self.scaler.fit(data) + data = self.scaler.transform(data) + else: # test or validation task + data = centering.transform(data) + data = pca.transform(data) + data = scaler.transform(data) + + if num_samples == -1: + self.num_samples = len(data) + elif num_samples >= len(data): + self.num_samples = len(data) + else: + self.num_samples = num_samples + self.image_list = data[:self.num_samples] + self.label_list = labels[:self.num_samples] + + def __getitem__(self, idx): + return self.image_list[idx], self.label_list[idx] + + def __len__(self): + return self.num_samples + + +def _filter_circuit(num_qubits: int, depth:int) -> pq.ansatz.Circuit: + r""" + The function that generates a filter circuit for extracting features. + """ + cir = Circuit(num_qubits) + + cir.complex_entangled_layer(qubits_idx='full', depth=depth) + + return cir + + +def _encoding_circuit(num_qubits: int, data: paddle.Tensor) -> pq.ansatz.Circuit: + r""" + The function that encodes the classical data into quantum states. + """ + cir = Circuit(num_qubits) + depth = int(np.ceil(len(data)/num_qubits)) + t = 0 + + for d in range(depth): + if d%2==0: + for q in range(num_qubits): + cir.ry(qubits_idx=q, param=data[t%len(data)]) + t += 1 + else: + for q in range(num_qubits): + cir.rx(qubits_idx=q, param=data[t%len(data)]) + t += 1 + + return cir + + +class QNNQD(paddle.nn.Layer): + r""" + The class of the quality detection using quantum neural networks (QNN). + + Args: + num_qubits: The number of qubits of the quantum circuit in each layer. + num_depths: The depth of quantum circuit in each layer. + observables: The observables of the quantum circuit in each layer. + """ + def __init__(self, num_qubits: List[int], num_depths: List[int], observables: List) -> None: + super(QNNQD, self).__init__() + self.num_qubits = num_qubits + self.num_depths = num_depths + self.observables = observables + self.cirs = paddle.nn.Sequential(*[_filter_circuit(num_qubits[j], num_depths[j]) for j in range(len(self.num_qubits))]) + + self.fc = paddle.nn.Linear(len(self.observables[-1]), 2, + weight_attr=paddle.ParamAttr(initializer=paddle.nn.initializer.Normal()), + bias_attr=paddle.ParamAttr(initializer=paddle.nn.initializer.Normal())) + + + def forward(self, batch_input: List[paddle.Tensor]) -> paddle.Tensor: + r""" + The forward function. + + Args: + batch_input: The input of the model. It's shape is :math:`(\text{batch_size}, -1)` . + + Returns: + Return the output of the model. It's shape is :math:`(\text{batch_size}, \text{num_classes})` . + """ + features = [] + + # quantum part + for data in batch_input: + # from layer one to layer N + for j in range(len(self.num_qubits)): + if j==0: # initialization + f_i = data + enc_cir = _encoding_circuit(self.num_qubits[j], f_i) + init_state = pq.state.zero_state(self.num_qubits[j]) + enc_state = enc_cir(init_state) + layer_state = self.cirs[j](enc_state) + + f_j = [] + for ob_str in self.observables[j]: + ob = pq.Hamiltonian([[1.0, ob_str]]) + expecval = pq.loss.ExpecVal(ob) + f_ij = expecval(layer_state) + f_j.append(f_ij) + f_i = paddle.concat(f_j) + + features.append(f_i) + + features = paddle.stack(features) + + # classical part + outputs = self.fc(features) + + return F.softmax(outputs) + + +def train( + model_name: str, num_qubits: List, num_depths: List[int], observables: List, + batch_size: int=20, num_epochs: int=4, learning_rate: float = 0.1, + dataset: str = 'SurfaceCrack', saved_dir: str = './', + using_validation: bool = False, + num_train: int = -1, num_val: int = -1, num_test: int = -1, + early_stopping: Optional[int] = 1000, num_workers: Optional[int] = 0 +) -> None: + """ + The function of training the QNNQD model. + + Args: + model_name: The name of the model, which is used to save the model. + num_qubits: The number of qubits of the quantum circuit in each layer. + num_depths: The depth of quantum circuit in each layer. + observables: The observables of the quantum circuit in each layer. + batch_size: The size of the batch samplers. Default to ``20`` . + num_epochs: The number of epochs to train the model. Default to ``4`` . + learning_rate: The learning rate used to update the parameters. Default to ``0.1``. + dataset: The path of the dataset. It defaults to SurfaceCrack. + saved_dir: The path used to save logs. Default to ``./``. + using_validation: Whether use the validation. It is false means the dataset only contains training datasets and test datasets. + num_train: The number of the data in the training dataset. Default to ``-1`` , which will use all training data. + num_val: The number of the data in the validation dataset. Default to ``-1`` , which will use all validation data. + num_test: The number of the data in the test dataset. Default to ``-1`` , which will use all test data. + early_stopping: Number of the iterations with no improvement after which training will be stopped. Defulat to ``1000``. + num_workers: The number of subprocess to load data, 0 for no subprocess used and loading data in main process. Default to 0. + """ + if saved_dir[-1] != '/': + saved_dir += '/' + if dataset[-1] != '/': + dataset += '/' + + logging.basicConfig( + filename=f'{saved_dir}{model_name}.log', + filemode='w', + format='%(asctime)s %(levelname)s %(message)s', + level=logging.INFO, + ) + + train_dataset = ImageDataset(file_path=dataset+'training_data/', num_samples=num_train) + train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=num_workers) + + test_dataset = ImageDataset(file_path=dataset+'test_data/', num_samples=num_test, + pca=train_dataset.pca, scaler=train_dataset.scaler, centering=train_dataset.centering) + test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=True, num_workers=num_workers) + + if using_validation: + val_dataset = ImageDataset(file_path=dataset+'validation_data/', num_samples=num_val, + pca=train_dataset.pca, scaler=train_dataset.scaler, centering=train_dataset.centering) + val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=True, num_workers=num_workers) + + paddle.seed(0) + model = QNNQD(num_qubits, num_depths, observables) + model.train() + opt = paddle.optimizer.Adam(learning_rate=learning_rate, parameters=model.parameters()) + + total_batch = 0 + val_best_loss = float('inf') + last_improve = 0 + stopping_flag = False + + for epoch in range(num_epochs): + p_bar = tqdm( + total=len(train_loader), + desc=f'Epoch[{epoch: 3d}]', + ascii=True, + dynamic_ncols=True, + ) + + for images, labels in train_loader: + p_bar.update(1) + prob = model(images) + loss = - paddle.mean(paddle.log(prob) * labels) + loss.backward() + opt.minimize(loss) + opt.clear_grad() + + if total_batch % 10 == 0: + predicts = paddle.argmax(prob, axis=1).tolist() + labels = paddle.argmax(labels, axis=1).tolist() + train_acc = sum(labels[idx] == predicts[idx] for idx in range(len(labels))) / len(labels) + if using_validation: + with paddle.no_grad(): + val_loss, val_acc = evaluate(model, val_loader) + if val_loss < val_best_loss: + paddle.save(model.state_dict(), f'{saved_dir}/{model_name}.pdparams') + improve = '*' + last_improve = total_batch + val_best_loss = val_acc + else: + improve = ' ' + msg = ( + f"Iter:{total_batch: 5d}, Train loss:{loss.item(): 3.5f}, acc:{train_acc: 3.2%}; " + f"Val loss:{val_loss: 3.5f}, acc:{val_acc: 3.2%}{improve}" + ) + else: + with paddle.no_grad(): + test_loss, test_acc = evaluate(model, test_loader) + paddle.save(model.state_dict(), f'{saved_dir}{model_name}.pdparams') + msg = ( + f"Iter:{total_batch: 5d}, Train loss:{loss.item(): 3.5f}, acc:{train_acc: 3.2%}; " + f"Test loss:{test_loss: 3.5f}, acc:{test_acc: 3.2%}" + ) + + model.train() + p_bar.set_postfix_str(msg) + logging.info(msg) + total_batch += 1 + if using_validation and total_batch - last_improve >= early_stopping: + stopping_flag = True + break + + p_bar.close() + if stopping_flag: + break + + if stopping_flag: + msg = "No optimization for a long time, auto-stopping..." + else: + msg = "The training of the model has been finished." + logging.info(msg) + print(msg) + + if using_validation: + test(model, saved_dir, model_name, test_loader) + else: + paddle.save(model.state_dict(), f'{saved_dir}/{model_name}.pdparams') + with paddle.no_grad(): + test_loss, test_acc = evaluate(model, test_loader) + msg = f"Test loss: {test_loss:3.5f}, acc: {test_acc:3.2%}" + logging.info(msg) + print(msg) + + +def evaluate( + model:paddle.nn.Layer, + data_loader: paddle.io.DataLoader +) -> Tuple[float, float]: + r""" + Evaluating the performance of the model on the dataset. + + Args: + model: The QNN model. + data_loader: The data loader for the data. + + Returns: + Return the accuracy of the model on the given datasets. + """ + val_loss = 0 + model.eval() + labels_all = [] + predicts_all = [] + + with paddle.no_grad(): + for images, labels in data_loader: + prob = model(images) + loss = - paddle.mean(paddle.log(prob) * labels) + labels = paddle.argmax(labels, axis=1).tolist() + val_loss += loss.item() * len(labels) + labels_all.extend(labels) + predict = paddle.argmax(prob, axis=1).tolist() + predicts_all.extend(predict) + val_acc = sum(labels_all[idx] == predicts_all[idx] for idx in range(len(labels_all))) + + return val_loss / len(labels_all), val_acc / len(labels_all) + + +def test( + model: paddle.nn.Layer, + saved_dir: str, + model_name: str, + test_loader: paddle.io.DataLoader +) -> None: + r""" + Evaluating the performance of the model on the test dataset. + + Args: + model: QNN model. + saved_dir: The path of the saved model. + model_name: The name of the model. + test_loader: The data loader for testing datasets. + """ + model.set_state_dict(paddle.load(f'{saved_dir}{model_name}.pdparams')) + test_loss, test_acc = evaluate(model, test_loader) + msg = f"Test loss: {test_loss:3.5f}, acc: {test_acc:3.2%}" + logging.info(msg) + print(msg) + + +def inference( + image_path: str, num_samples: int, model_path: str, + num_qubits: List, num_depths: List, observables: List, +) -> Union[float, list]: + r""" + The prediction function for the provided test dataset. + + Args: + image_path: The path of the input image. + num_samples: The number of the data need to be classified. + model_path: The path of the trained model, which will be loaded. + num_qubits: The number of qubits of the quantum circuit in each layer. + num_depths: The depth of quantum circuit in each layer. + observables: The observables of the quantum circuit in each layer. + + Returns: + Return the prediction of the given datasets together with the associated level of certainty. + """ + logging.basicConfig( + filename=f'./{"qnnqd"}.log', + filemode='w', + format='%(asctime)s %(levelname)s %(message)s', + level=logging.INFO, + ) + msg = f"Start Inferencing" + logging.info(msg) + + model = QNNQD(num_qubits, num_depths, observables) + model.set_state_dict(paddle.load(model_path)) + train_dataset = ImageDataset(file_path='SurfaceCrack/training_data/', num_samples=500) + dataset = ImageDataset(file_path=image_path, num_samples=num_samples, pca=train_dataset.pca, scaler=train_dataset.scaler, centering=train_dataset.centering) + + model.eval() + prob = model(paddle.to_tensor(dataset.image_list)) + prediction = paddle.argmin(prob, axis=1) + label = np.argmin(dataset.label_list[:num_samples], axis=1) + + msg = f"Finish Inferencing" + logging.info(msg) + + return prediction.tolist(), prob.tolist(), label.tolist() + + +if __name__ == '__main__': + exit(0) \ No newline at end of file diff --git a/paddle_quantum/qml/qsann.py b/paddle_quantum/qml/qsann.py new file mode 100644 index 0000000..4d90712 --- /dev/null +++ b/paddle_quantum/qml/qsann.py @@ -0,0 +1,514 @@ +# !/usr/bin/env python3 +# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +r""" +The Quantum Self-Attention Neural Network (QSANN) model. +""" + +import logging +import random +from tqdm import tqdm +from typing import List, Tuple, Dict + +import numpy as np +import paddle +import paddle_quantum as pq +from paddle.io import Dataset +from paddle_quantum.loss import ExpecVal +from paddle_quantum.gate import functional + + +def generate_observable(num_qubits: int, num_terms: int) -> List[list]: + """ + Generate the observables to observe the quantum state. + + Args: + num_qubits: The number of the qubits. + num_terms: The number of the generated observables. + + Returns: + Return the generated observables. + """ + ob = [[[1.0, f'z{idx:d}']] for idx in range(num_qubits)] + ob.extend([[1.0, f'y{idx:d}']] for idx in range(num_qubits)) + ob.extend([[1.0, f'x{idx:d}']] for idx in range(num_qubits)) + if len(ob) >= num_terms: + ob = ob[:num_terms] + else: + ob.extend(ob * (num_terms // len(ob) - 1)) + ob.extend(ob[:num_terms % len(ob)]) + return ob + + +class QSANN(paddle.nn.Layer): + r""" + The class of the quantum self-attention neural network (QSANN) model. + + Args: + num_qubits: The number of the qubits which the quantum circuit contains. + len_vocab: The length of the vocabulary. + num_layers: The number of the self-attention layers. + depth_ebd: The depth of the embedding circuit. + depth_query: The depth of the query circuit. + depth_key: The depth of the key circuit. + depth_value: The depth of the value circuit. + """ + def __init__( + self, num_qubits: int, len_vocab: int, num_layers: int, + depth_ebd: int, depth_query: int, depth_key: int, depth_value: int, + ): + super().__init__() + self.num_qubits = num_qubits + self.len_vocab = len_vocab + self.num_layers = num_layers + self.depth_ebd = depth_ebd + self.depth_query = depth_query + self.depth_key = depth_key + self.depth_value = depth_value + self.embedding_param = self.create_parameter( + shape=[len_vocab, num_qubits * (depth_ebd * 2 + 1), 2], + default_initializer=paddle.nn.initializer.Uniform(low=-np.pi, high=np.pi), + dtype=paddle.get_default_dtype(), + is_bias=False, + ) + self.weight = self.create_parameter( + shape=[num_qubits * (depth_ebd * 2 + 1) * 2], + default_initializer=paddle.nn.initializer.Normal(std=0.001), + dtype=paddle.get_default_dtype(), + is_bias=False) + self.bias = self.create_parameter( + shape=[1], + default_initializer=paddle.nn.initializer.Normal(std=0.001), + dtype=paddle.get_default_dtype(), + is_bias=False) + query_circuits = self.__circuit_list(num_layers, num_qubits, depth_query) + self.query_circuits = paddle.nn.LayerList(query_circuits) + key_circuits = self.__circuit_list(num_layers, num_qubits, depth_key) + self.key_circuits = paddle.nn.LayerList(key_circuits) + value_circuits = self.__circuit_list(num_layers, num_qubits, depth_value) + self.value_circuits = paddle.nn.LayerList(value_circuits) + observables = generate_observable(self.num_qubits, self.embedding_param[0].size) + self.ob_query = pq.Hamiltonian(observables[0]) + self.ob_key = pq.Hamiltonian(observables[0]) + self.ob_value = [pq.Hamiltonian(ob_item) for ob_item in observables] + + def __embedding_circuit(self, num_qubits, params, depth=1) -> pq.State: + r""" + The circuit to implement the embedding. + + Args: + num_qubits: The number of the qubits. + params: The parameters in the quantum circuit. + depth: The depth of the quantum circuit. Defaults to ``1``. + + Returns: + The quantum state which embeds the word. + """ + embedding_state = pq.state.zero_state(num_qubits) + for d in range(depth): + for idx in range(num_qubits): + param_idx = 2 * num_qubits * d + 2 * idx + embedding_state = functional.rx( + state=embedding_state, qubit_idx=idx, + theta=params[param_idx][0], + dtype=embedding_state.dtype, backend=embedding_state.backend + ) + embedding_state = functional.ry( + state=embedding_state, qubit_idx=idx, + theta=params[param_idx][1], + dtype=embedding_state.dtype, backend=embedding_state.backend + ) + embedding_state = functional.rx( + state=embedding_state, qubit_idx=(idx + 1) % num_qubits, + theta=params[param_idx + 1][0], + dtype=embedding_state.dtype, backend=embedding_state.backend + ) + embedding_state = functional.ry( + state=embedding_state, qubit_idx=(idx + 1) % num_qubits, + theta=params[param_idx + 1][1], + dtype=embedding_state.dtype, backend=embedding_state.backend + ) + embedding_state = functional.cnot( + state=embedding_state, qubit_idx=[idx, (idx + 1) % num_qubits], + dtype=embedding_state.dtype, backend=embedding_state.backend + ) + for idx in range(num_qubits): + param_idx = 2 * num_qubits * depth + idx + embedding_state = functional.rx( + state=embedding_state, qubit_idx=idx, theta=params[param_idx][0], + dtype=embedding_state.dtype, backend=embedding_state.backend + ) + embedding_state = functional.ry( + state=embedding_state, qubit_idx=idx, theta=params[param_idx][1], + dtype=embedding_state.dtype, backend=embedding_state.backend + ) + return embedding_state + + def forward(self, batch_text: List[List[int]]) -> List[paddle.Tensor]: + r""" + The forward function to excute the model. + + Args: + batch_text: The batch of input texts. Each of them is a list of int. + + Returns: + Retrun a list which contains the predictions of the input texts. + """ + predictions = [] + for text in batch_text: + text_feature = [self.embedding_param[word] for word in text] + for layer_idx in range(self.num_layers): + queries = [] + keys = [] + values = [] + for char_idx in range(len(text_feature)): + embedding_state = self.__embedding_circuit(self.num_qubits, params=text_feature[char_idx]) + query_state = self.query_circuits[layer_idx](embedding_state) + key_state = self.key_circuits[layer_idx](embedding_state) + value_state = self.value_circuits[layer_idx](embedding_state) + query = ExpecVal(self.ob_query)(query_state) + key = ExpecVal(self.ob_key)(key_state) + value = [ExpecVal(ob_item)(value_state) for ob_item in self.ob_value] + value = paddle.concat(value) + queries.append(query) + keys.append(key) + values.append(value) + feature = [] + for char_idx in range(len(text_feature)): + query = queries[char_idx] + output = paddle.zeros_like(values[0]) + alpha_sum = 0 + for idx in range(len(keys)): + alpha = (keys[idx] - query) ** 2 + alpha = paddle.exp(-1 * alpha) + output += alpha * values[idx] + alpha_sum += alpha + output = output / alpha_sum * np.pi + output = paddle.reshape(output, self.embedding_param[0].shape) + feature.append(output) + text_feature = feature + output = paddle.flatten(sum(text_feature) / len(text_feature)) + predictions.append(1 / (1 + paddle.exp(-output @ self.weight - self.bias))) + return predictions + + def __circuit_list(self, num_layer, num_qubits, depth) -> List[pq.ansatz.Circuit]: + r""" + Generate a series of circuits. + + Args: + num_layer: The number of the self-attention layers, which means the number of the circuits. + num_qubits: The number of the qubits which the circuits contains. + depth: The depth of the quantum circuits. + + Returns: + A list of the generated circuits. + """ + circuits = [] + for _ in range(num_layer): + cir = pq.ansatz.Circuit(num_qubits) + for _ in range(depth): + for idx in range(num_qubits): + cir.rx(idx) + cir.ry(idx) + cir.rx((idx + 1) % num_qubits) + cir.ry((idx + 1) % num_qubits) + cir.cnot([idx, (idx + 1) % num_qubits]) + cir.rx('full') + cir.ry('full') + circuits.append(cir) + return circuits + + +def deal_vocab(vocab_path: str) -> Dict[str, int]: + r""" + Get the map from the word to the index by the input vocabulary file. + + Args: + vocab_path: The path of the vocabulary file. + + Returns: + Return the map from the word to the corresponding index. + """ + with open(vocab_path, 'r', encoding='utf-8') as file: + lines = file.readlines() + word2idx = {word.strip(): idx for idx, word in enumerate(lines)} + return word2idx + + +class TextDataset(Dataset): + r""" + The class to implement the text dataset. + + Args: + file_path: The dataset file. + word2idx: The map from the word to the corresponding index. + pad_size: The size pad the text sequence to. Defaults to ``0``, which means no padding. + """ + def __init__(self, file_path: str, word2idx: dict, pad_size: int = 0): + super().__init__() + self.contents = [] + with open(file_path, 'r', encoding='utf-8') as file: + lines = file.readlines() + for line in lines: + text, label = line.strip().split('\t') + text = [word2idx.get(word, 0) for word in text.split()] + if pad_size != 0: + if len(text) >= pad_size: + text = text[:pad_size] + else: + text.extend([0] * (pad_size - len(text))) + self.contents.append((text, int(label))) + self.len_data = len(self.contents) + + def __getitem__(self, idx): + return self.contents[idx] + + def __len__(self): + return self.len_data + + +def build_iter(dataset: TextDataset, batch_size: int, shuffle: bool = False) -> list: + r""" + Build the iteration of the batch data. + + Args: + dataset: The dataset to be built. + batch_size: The number of the data in a batch. + shuffle: Whether to randomly shuffle the order of the data. Defaults to ``False``. + + Returns: + The built iteration which contains the batches of the data. + """ + data_iter = [] + # 是否需要拼接成tensor + if shuffle: + random.shuffle(dataset.contents) + for idx in range(0, len(dataset), batch_size): + batch_data = dataset[idx: idx + batch_size] + texts = [token_ids for token_ids, _ in batch_data] + labels = [label for _, label in batch_data] + data_iter.append((texts, labels)) + return data_iter + + +def train( + model_name: str, dataset: str, num_qubits: int, num_layers: int, + depth_ebd: int, depth_query: int, depth_key: int, depth_value: int, + batch_size: int, num_epochs: int, learning_rate: float = 0.01, + saved_dir: str = '', using_validation: bool = False, + early_stopping: int = 1000, +) -> None: + r""" + The function of training the QSANN model. + + Args: + model_name: The name of the model. It is the filename of the saved model. + dataset: The dataset used to train the model, which should be a directory. + num_qubits: The number of the qubits which the quantum circuit contains. + num_layers: The number of the self-attention layers. + depth_ebd: The depth of the embedding circuit. + depth_query: The depth of the query circuit. + depth_key: The depth of the key circuit. + depth_value: The depth of the value circuit. + batch_size: The size of the batch samplers. + num_epochs: The number of the epochs to train the model. + learning_rate: The learning rate used to update the parameters. Defaults to ``0.01`` . + saved_dir: The directory to saved the trained model and the training log. Defaults to use the current path. + using_validation: If the datasets contains the validation dataset. + Defaults to ``False`` , which means the validation dataset is not included. + early_stopping: Number of iterations with no improvement after which training will be stopped. + Defaults to ``1000`` . + """ + if not saved_dir: + saved_dir = './' + elif saved_dir[-1] != '/': + saved_dir += '/' + if dataset[-1] != '/': + dataset += '/' + logging.basicConfig( + filename=f'{saved_dir}{model_name}.log', + filemode='w', + format='%(asctime)s %(levelname)s %(message)s', + level=logging.INFO, + ) + word2idx = deal_vocab(f'{dataset}vocab.txt') + len_vocab = len(word2idx) + train_dataset = TextDataset(file_path=f'{dataset}train.txt', word2idx=word2idx) + if using_validation: + dev_dataset = TextDataset(file_path=f'{dataset}dev.txt', word2idx=word2idx) + test_dataset = TextDataset(file_path=f'{dataset}test.txt', word2idx=word2idx) + train_iter = build_iter(train_dataset, batch_size=batch_size, shuffle=True) + if using_validation: + dev_iter = build_iter(dev_dataset, batch_size=batch_size, shuffle=True) + test_iter = build_iter(test_dataset, batch_size=batch_size, shuffle=True) + model = QSANN( + num_qubits=num_qubits, len_vocab=len_vocab, num_layers=num_layers, + depth_ebd=depth_ebd, depth_query=depth_query, depth_key=depth_key, depth_value=depth_value, + ) + model.train() + opt = paddle.optimizer.Adam(learning_rate=learning_rate, parameters=model.parameters()) + total_batch = 0 + dev_best_loss = float('inf') + last_improve = 0 + stopping_flag = False + for epoch in range(num_epochs): + p_bar = tqdm( + total=len(train_iter), + desc=f'Epoch[{epoch: 3d}]', + ascii=True, + dynamic_ncols=True, + ) + for texts, labels in train_iter: + p_bar.update(1) + model.clear_gradients() + predictions = model(texts) + loss = sum((prediction - label) ** 2 for prediction, label in zip(predictions, labels)) / len(labels) + loss.backward() + opt.minimize(loss) + opt.clear_grad() + if total_batch % 10 == 0: + predictions = [0 if item < 0.5 else 1 for item in predictions] + train_acc = sum(labels[idx] == predictions[idx] for idx in range(len(labels))) / len(labels) + if using_validation: + with paddle.no_grad(): + dev_loss, dev_acc = evaluate(model, dev_iter) + if dev_loss < dev_best_loss: + paddle.save(model.state_dict(), f'{saved_dir}/{model_name}.pdparams') + improve = '*' + last_improve = total_batch + dev_best_loss = dev_loss + else: + improve = ' ' + msg = ( + f"Iter:{total_batch: 5d}, Train loss:{loss.item(): 3.5f}, acc:{train_acc: 3.2%}; " + f"Val loss:{dev_loss: 3.5f}, acc:{dev_acc: 3.2%}{improve}" + ) + else: + with paddle.no_grad(): + test_loss, test_acc = evaluate(model, test_iter) + paddle.save(model.state_dict(), f'{saved_dir}{model_name}.pdparams') + msg = ( + f"Iter:{total_batch: 5d}, Train loss:{loss.item(): 3.5f}, acc:{train_acc: 3.2%}; " + f"Test loss:{test_loss: 3.5f}, acc:{test_acc: 3.2%}" + ) + model.train() + p_bar.set_postfix_str(msg) + logging.info(msg) + total_batch += 1 + if using_validation and total_batch - last_improve >= early_stopping: + stopping_flag = True + break + p_bar.close() + if stopping_flag: + break + if stopping_flag: + msg = "No optimization for a long time, auto-stopping..." + else: + msg = "The training of the model has been finished." + logging.info(msg) + print(msg) + if using_validation: + test(model, f'{saved_dir}/{model_name}.pdparams', test_iter) + else: + paddle.save(model.state_dict(), f'{saved_dir}/{model_name}.pdparams') + with paddle.no_grad(): + test_loss, test_acc = evaluate(model, test_iter) + msg = f"Test loss: {test_loss:3.5f}, acc: {test_acc:3.2%}" + logging.info(msg) + print(msg) + + +def evaluate(model: paddle.nn.Layer, data_loader: list) -> Tuple[float, float]: + r""" + Evaluate the model. + + Args: + model: The trained model to be evaluated. + data_loader: The dataloader of the data used to evaluate the model. + + Returns: + Return the average loss and accuracy in the data of the input dataloader. + """ + dev_loss = 0 + model.eval() + labels_all = [] + predicts_all = [] + with paddle.no_grad(): + for texts, labels in data_loader: + predictions = model(texts) + loss = sum((prediction - label) ** 2 for prediction, label in zip(predictions, labels)) + dev_loss += loss.item() + labels_all.extend(labels) + predictions = [0 if item < 0.5 else 1 for item in predictions] + predicts_all.extend(predictions) + dev_acc = sum(labels_all[idx] == predicts_all[idx] for idx in range(len(labels_all))) + return dev_loss / len(labels_all), dev_acc / len(labels_all) + + +def test(model: paddle.nn.Layer, model_path: str, test_loader: list) -> None: + r""" + Use the test dataset to test the model. + + Args: + model: The model to be tested. + model_path: The file path of the models' file. + test_loader: The dataloader of the test dataset. + """ + model.set_state_dict(paddle.load(model_path)) + with paddle.no_grad(): + test_loss, test_acc = evaluate(model, test_loader) + msg = f"Test loss: {test_loss:3.5f}, acc: {test_acc:3.2%}" + logging.info(msg) + print(msg) + + +def inference( + text: str, model_path: str, vocab_path: str, classes: List[str], + num_qubits: int, num_layers: int, depth_ebd: int, + depth_query: int, depth_key: int, depth_value: int, +) -> str: + r""" + The inference function. Using the trained model to predict new data. + + Args: + text: The path of the image to be predicted. + model_path: The path of the model file. + vocab_path: The path of the vocabulary file. + classes: The classes of all the labels. + num_qubits: The number of the qubits which the quantum circuit contains. + num_layers: The number of the self-attention layers. + depth_ebd: The depth of the embedding circuit. + depth_query: The depth of the query circuit. + depth_key: The depth of the key circuit. + depth_value: The depth of the value circuit. + + Returns: + Return the class which the model predicted. + """ + word2idx = deal_vocab(vocab_path) + model = QSANN( + num_qubits=num_qubits, len_vocab=len(word2idx), num_layers=num_layers, + depth_ebd=depth_ebd, depth_query=depth_query, depth_key=depth_key, depth_value=depth_value, + ) + model.set_state_dict(paddle.load(model_path)) + model.eval() + text = [word2idx.get(word, 0) for word in list(text)] + prediction = model([text]) + prediction = 0 if prediction[0] < 0.5 else 1 + return classes[prediction] + + +if __name__ == '__main__': + exit(0) diff --git a/paddle_quantum/qml/vsql.py b/paddle_quantum/qml/vsql.py index 823e67e..7f3642c 100644 --- a/paddle_quantum/qml/vsql.py +++ b/paddle_quantum/qml/vsql.py @@ -14,75 +14,82 @@ # limitations under the License. r""" -The VSQL model. +The Variational Shadow Quantum Learning (VSQL) model. """ -import random -from typing import Optional, List, Tuple +import logging +import os +from functools import partial +from tqdm import tqdm +from typing import Optional, List, Tuple, Callable +import cv2 import numpy as np import paddle import paddle.nn.functional as F from paddle.vision.datasets import MNIST +from paddle.io import Dataset, DataLoader import paddle_quantum as pq from paddle_quantum.ansatz import Circuit -def norm_image(images: List[np.ndarray], num_qubits: int) -> List[paddle.Tensor]: +def image_preprocess(image: np.ndarray, num_qubits: int) -> np.ndarray: r""" - Normalize the input images. Flatten them and make them to normalized vectors. + Normalize the input image. Flatten them and make them to normalized vectors. Args: - images: The input images. - num_qubits: The number of qubits, which decides the dimension of the vector. + image: The input image. + num_qubits: The number of the qubits, which decides the dimension of the vector. Returns: - Return the normalized vectors, which is the list of paddle's tensors. + Return the preprocessed image which is a normalized vector. """ - # pad and normalize the image - _images = [] - for image in images: - image = image.flatten() - if image.size < 2 ** num_qubits: - _images.append(np.pad(image, pad_width=(0, 2 ** num_qubits - image.size))) - else: - _images.append(image[:2 ** num_qubits]) - return [paddle.to_tensor(image / np.linalg.norm(image), dtype=pq.get_dtype()) for image in _images] - - -def data_loading( - num_qubits: int, mode: str, classes: list, num_data: Optional[int] = None -) -> Tuple[List[np.ndarray], List[int]]: - r""" - Loading the MNIST dataset, which only contains the specified data. - - Args: - num_qubits: The number of qubits, which determines the dimension of the normalized vector. - mode: Specifies the loaded dataset: ``'train'`` | ``'test'`` . + image = np.pad(image.flatten(), pad_width=(0, 2 ** num_qubits - image.size)) + return image / np.linalg.norm(image) - - ``'train'`` : Load the training dataset. - - ``'test'`` : Load the test dataset. - classes: The labels of the data which will be loaded. - num_data: The number of data to be loaded. Defaults to ``None``, which means loading all data. +class ImageDataset(Dataset): + r""" + The class to implement the image dataset. - Returns: - Return the loaded dataset, which is ``(images, labels)`` . + Args: + file_path: The path of the dataset file, each line of which represents a piece of data. + Each line contains the file path and label of the image, separated by tabs. + num_samples: The number of the samples. It is the number of the data that the dataset contains. + Defaults to ``0`` , which means contains all data in the dataset file. + transform: The function to transform the image. Defaults to ``None`` , which means do nothing on the image. """ - data = MNIST(mode=mode, backend='cv2') - filtered_data = [item for item in data if item[1].item() in classes] - random.shuffle(filtered_data) - if num_data is None: - num_data = len(filtered_data) - images = [filtered_data[idx][0] for idx in range(min(len(filtered_data), num_data))] - labels = [filtered_data[idx][1] for idx in range(min(len(filtered_data), num_data))] - images = norm_image(images, num_qubits=num_qubits) - labels = [label.item() for label in labels] - return images, labels + def __init__(self, file_path: str, num_samples: int = 0, transform: Optional[Callable] = None): + super().__init__() + self.transform = transform + with open(file_path, 'r', encoding='utf-8') as f: + lines = f.readlines() + self.data_list = [line.strip().split('\t') for line in lines] + if num_samples > 0: + self.data_list = self.data_list[:self.data_list] + self.num_samples = len(self.data_list) + + def __getitem__(self, idx): + image_path, label = self.data_list[idx] + image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) + image = image.astype(paddle.get_default_dtype()) + if self.transform is not None: + image = self.transform(image) + return image, np.array(int(label)) + + def __len__(self): + return self.num_samples def _slide_circuit(cir: pq.ansatz.Circuit, distance: int): + r""" + Slide the shadow circuit to obtain the different shadow feature. + + Args: + cir: The quantum circuit which only contains shadow circuit. + distance: The distance to move the shadow circuit. + """ # slide to get the local feature for sublayer in cir.sublayers(): qubits_idx = np.array(sublayer.qubits_idx) @@ -90,13 +97,13 @@ def _slide_circuit(cir: pq.ansatz.Circuit, distance: int): sublayer.qubits_idx = qubits_idx.tolist() -def observable(start_idx: int, num_shadow: int) -> pq.Hamiltonian: +def generate_observable(start_idx: int, num_shadow: int) -> pq.Hamiltonian: r""" Generate the observable to measure the quantum states. Args: start_idx: The start index of the qubits. - num_shadow: The number of qubits which the shadow circuit contains. + num_shadow: The number of the qubits which the shadow circuit contains. Returns: Return the generated observable. @@ -108,14 +115,14 @@ def observable(start_idx: int, num_shadow: int) -> pq.Hamiltonian: class VSQL(paddle.nn.Layer): r""" - The class of the variational shadow quantum learning (VSQL). + The class of the variational shadow quantum learning (VSQL) model. The details can be referred to https://ojs.aaai.org/index.php/AAAI/article/view/17016 . Args: - num_qubits: The number of qubits which the quantum circuit contains. - num_shadow: The number of qubits which the shadow circuit contains. - num_classes: The number of class which the modell will classify. + num_qubits: The number of the qubits which the quantum circuit contains. + num_shadow: The number of the qubits which the shadow circuit contains. + num_classes: The number of the classes which the model will classify. depth: The depth of the quantum circuit. Defaults to ``1`` . """ def __init__(self, num_qubits: int, num_shadow: int, num_classes: int, depth: int = 1): @@ -156,7 +163,7 @@ def forward(self, batch_input: List[paddle.Tensor]) -> paddle.Tensor: _state = pq.State(input) feature = [] for idx_start in range(self.num_qubits - self.num_shadow + 1): - ob = observable(idx_start, num_shadow=self.num_shadow) + ob = generate_observable(idx_start, num_shadow=self.num_shadow) _slide_circuit(cir=self.cir, distance=1 if idx_start != 0 else 0) expec_val_func = pq.loss.ExpecVal(ob) out_state = self.cir(_state) @@ -171,67 +178,257 @@ def forward(self, batch_input: List[paddle.Tensor]) -> paddle.Tensor: def train( - num_qubits: int, num_shadow: int, depth: int = 1, - batch_size: int = 16, epoch: int = 10, learning_rate: float = 0.01, - classes: Optional[list] = None, num_train: Optional[int] = None, num_test: Optional[int] = None + model_name: str, num_qubits: int, num_shadow: int, classes: list, + batch_size: int, num_epochs: int, depth: int = 1, dataset: str = 'MNIST', saved_dir: str = '', + learning_rate: float = 0.01, using_validation: bool = False, num_workers: int = 0, + early_stopping: int = 1000, num_train: int = 0, num_dev: int = 0, num_test: int = 0, ) -> None: """ The function of training the VSQL model. Args: - num_qubits: The number of qubits which the quantum circuit contains. - num_shadow: The number of qubits which the shadow circuit contains. - depth: The depth of the quantum circuit. Defaults to ``1`` . - batch_size: The size of the batch samplers. Defaults to ``16`` . - epoch: The number of epochs to train the model. Defaults to ``10`` . - learning_rate: The learning rate used to update the parameters. Defaults to ``0.01`` . + model_name: The name of the model. It is the filename of the saved model. + num_qubits: The number of the qubits which the quantum circuit contains. + num_shadow: The number of the qubits which the shadow circuit contains. classes: The classes of handwrite digits to be predicted. Defaults to ``None`` , which means predict all the classes. + batch_size: The size of the batch samplers. + num_epochs: The number of the epochs to train the model. + depth: The depth of the quantum circuit. Defaults to ``1`` . + dataset: The dataset used to train the model, which should be a directory. + Defaults to ``'MNIST'`` , which means using the built-in MNIST dataset. + saved_dir: The directory to saved the trained model and the training log. Defaults to use the current path. + learning_rate: The learning rate used to update the parameters. Defaults to ``0.01`` . + using_validation: If the datasets contains the validation dataset. + Defaults to ``False`` , which means the validation dataset is not included. + num_workers: The number of the subprocess to load data, 0 for no subprocess used and loading data in main process. + Defaults to ``0`` . + early_stopping: Number of epochs with no improvement after which training will be stopped. + Defaults to ``1000`` . num_train: The number of the data in the training dataset. - Defaults to ``None`` , which will use all training data. - num_test: The number of the data in the test dataset. Defaults to ``None`` , which will use all test data. + Defaults to ``0`` , which will use all training data. + num_dev: The number of the data in the test dataset. Defaults to ``0`` , which will use all validation data. + num_test: The number of the data in the test dataset. Defaults to ``0`` , which will use all test data. """ - if classes is None: - classes = list(range(10)) - train_input, train_label = data_loading(num_qubits=num_qubits, mode='train', classes=classes, num_data=num_train) - test_input, test_label = data_loading(num_qubits=num_qubits, mode='test', classes=classes, num_data=num_test) - net = VSQL(num_qubits, num_shadow=num_shadow, num_classes=len(classes), depth=depth) - opt = paddle.optimizer.Adam(learning_rate=learning_rate, parameters=net.parameters()) - num_train = len(train_label) if num_train is None else num_train - num_test = len(test_label) if num_test is None else num_test - for idx_epoch in range(epoch): - for itr in range(num_train // batch_size): - output = net(train_input[itr * batch_size:(itr + 1) * batch_size]) - labels = paddle.to_tensor(train_label[itr * batch_size:(itr + 1) * batch_size]) + if not saved_dir: + saved_dir = './' + elif saved_dir[-1] != '/': + saved_dir += '/' + if dataset != 'MNIST' and dataset[-1] != '/': + dataset += '/' + logging.basicConfig( + filename=f'{saved_dir}{model_name}.log', + filemode='w', + format='%(asctime)s %(levelname)s %(message)s', + level=logging.INFO, + ) + if dataset == 'MNIST': + train_dataset = MNIST( + mode='train', backend='cv2', + transform=partial(image_preprocess, num_qubits=num_qubits) + ) + test_dataset = MNIST( + mode='test', backend='cv2', + transform=partial(image_preprocess, num_qubits=num_qubits) + ) + + def filter_data(dataset: MNIST, classes: list): + _images = dataset.images + _labels = dataset.labels + _len = len(dataset) + dataset.images = [_images[idx] for idx in range(_len) if _labels[idx] in classes] + dataset.labels = [_labels[idx] for idx in range(_len) if _labels[idx] in classes] + if classes != list(range(10)): + filter_data(train_dataset, classes) + filter_data(test_dataset, classes) + if num_train > 0: + train_dataset.images = train_dataset.images[:num_train] + train_dataset.labels = train_dataset.labels[:num_train] + if num_test > 0: + test_dataset.images = test_dataset.images[:num_test] + test_dataset.labels = test_dataset.labels[:num_test] + else: + train_dataset = ImageDataset( + file_path=f'{dataset}train.txt', num_samples=num_train, + transform=partial(image_preprocess, num_qubits=num_qubits) + ) + if using_validation: + dev_dataset = ImageDataset( + file_path=f'{dataset}dev.txt', num_samples=num_dev, + transform=partial(image_preprocess, num_qubits=num_qubits) + ) + test_dataset = ImageDataset( + file_path=f'{dataset}test.txt', num_samples=num_test, + transform=partial(image_preprocess, num_qubits=num_qubits) + ) + train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=num_workers) + if using_validation: + dev_loader = DataLoader(dev_dataset, batch_size=batch_size, shuffle=True, num_workers=num_workers) + test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=True, num_workers=num_workers) + model = VSQL(num_qubits, num_shadow=num_shadow, num_classes=len(classes), depth=depth) + model.train() + opt = paddle.optimizer.Adam(learning_rate=learning_rate, parameters=model.parameters()) + total_batch = 0 + dev_best_loss = float('inf') + last_improve = 0 + stopping_flag = False + for epoch in range(num_epochs): + p_bar = tqdm( + total=len(train_loader), + desc=f'Epoch[{epoch: 3d}]', + ascii=True, + dynamic_ncols=True, + ) + for images, labels in train_loader: + p_bar.update(1) + model.clear_gradients() + output = model(images) loss = F.cross_entropy(output, labels) loss.backward() opt.minimize(loss) opt.clear_grad() - if itr % 10 == 0: - predictions = paddle.argmax(output, axis=-1).tolist() - labels = labels.tolist() - train_acc = sum(labels[idx] == predictions[idx] for idx in range(len(labels))) / len(labels) - output = net(test_input[:num_test]) - labels = test_label[:num_test] - predictions = paddle.argmax(output, axis=-1).tolist() - test_acc = sum(labels[idx] == predictions[idx] for idx in range(len(labels))) / num_test - print( - f"Epoch: {idx_epoch: 3d}, iter: {itr: 3d}, loss: {loss.item(): .4f}, " - f"batch_acc: {train_acc: .2%}, test_acc: {test_acc: .2%}." - ) - state_dict = net.state_dict() - paddle.save(state_dict, 'vsql.pdparams') + if total_batch % 10 == 0: + predicts = paddle.argmax(output, axis=1).tolist() + labels = labels.flatten().tolist() + train_acc = sum(labels[idx] == predicts[idx] for idx in range(len(labels))) / len(labels) + if using_validation: + with paddle.no_grad(): + dev_loss, dev_acc = evaluate(model, dev_loader) + if dev_loss < dev_best_loss: + paddle.save(model.state_dict(), f'{saved_dir}/{model_name}.pdparams') + improve = '*' + last_improve = total_batch + dev_best_loss = dev_loss + else: + improve = ' ' + msg = ( + f"Iter:{total_batch: 5d}, Train loss:{loss.item(): 3.5f}, acc:{train_acc: 3.2%}; " + f"Val loss:{dev_loss: 3.5f}, acc:{dev_acc: 3.2%}{improve}" + ) + else: + with paddle.no_grad(): + test_loss, test_acc = evaluate(model, test_loader) + paddle.save(model.state_dict(), f'{saved_dir}{model_name}.pdparams') + msg = ( + f"Iter:{total_batch: 5d}, Train loss:{loss.item(): 3.5f}, acc:{train_acc: 3.2%}; " + f"Test loss:{test_loss: 3.5f}, acc:{test_acc: 3.2%}" + ) + model.train() + p_bar.set_postfix_str(msg) + logging.info(msg) + total_batch += 1 + if using_validation and total_batch - last_improve >= early_stopping: + stopping_flag = True + break + p_bar.close() + if stopping_flag: + break + if stopping_flag: + msg = "No optimization for a long time, auto-stopping..." + else: + msg = "The training of the model has been finished." + logging.info(msg) + print(msg) + if using_validation: + test(model, f'{saved_dir}/{model_name}.pdparams', test_loader) + else: + paddle.save(model.state_dict(), f'{saved_dir}/{model_name}.pdparams') + with paddle.no_grad(): + test_loss, test_acc = evaluate(model, test_loader) + msg = f"Test loss: {test_loss:3.5f}, acc: {test_acc:3.2%}" + logging.info(msg) + print(msg) + + +def evaluate(model: paddle.nn.Layer, data_loader: paddle.io.DataLoader) -> Tuple[float, float]: + r""" + Evaluate the model. + Args: + model: The trained model to be evaluated. + data_loader: The dataloader of the data used to evaluate the model. + Returns: + Return the average loss and accuracy in the data of the input dataloader. + """ + dev_loss = 0 + model.eval() + labels_all = [] + predicts_all = [] + with paddle.no_grad(): + for images, labels in data_loader: + prob = model(images) + loss = paddle.nn.functional.cross_entropy(prob, labels) + labels = labels.flatten().tolist() + dev_loss += loss.item() * len(labels) + labels_all.extend(labels) + predict = paddle.argmax(prob, axis=1) + predicts_all.extend(predict.tolist()) + dev_acc = sum(labels_all[idx] == predicts_all[idx] for idx in range(len(labels_all))) + return dev_loss / len(labels_all), dev_acc / len(labels_all) + + +def test(model: paddle.nn.Layer, model_path: str, test_loader: paddle.io.DataLoader) -> None: + r""" + Use the test dataset to test the model. -if __name__ == '__main__': - train( - num_qubits=10, - num_shadow=2, - depth=1, - batch_size=20, - epoch=10, - learning_rate=0.01, - classes=[0, 1], - num_train=1000, - num_test=100 + Args: + model: The model to be tested. + model_path: The file path of the models' file. + test_loader: The dataloader of the test dataset. + """ + model.set_state_dict(paddle.load(f'{model_path}')) + with paddle.no_grad(): + test_loss, test_acc = evaluate(model, test_loader) + msg = f"Test loss: {test_loss:3.5f}, acc: {test_acc:3.2%}" + logging.info(msg) + print(msg) + + +def inference( + image_path: str, is_dir: bool, model_path: str, + num_qubits: int, num_shadow: int, classes: list, depth: int = 1, +) -> Tuple[int, list]: + r""" + The inference function. Using the trained model to predict new data. + + Args: + image_path: The path of the image to be predicted. + is_dir: Whether the input ``image_path`` is a directory. + If it is a directory, the model will predict all images under it. + model_path: The path of the model file. + num_qubits: The number of the qubits which the quantum circuit contains. + num_shadow: The number of the qubits which the shadow circuit contains. + classes: The classes which the input image belongs to. + depth: The depth of the quantum circuit. Defaults to ``1`` . + + Returns: + Return the class the model predicted and the probability of each class. + """ + num_classes = len(classes) + model = VSQL( + num_qubits=num_qubits, num_shadow=num_shadow, + num_classes=num_classes, depth=depth ) + model.set_state_dict(paddle.load(model_path)) + if is_dir: + filter_ext = ['.jpg', '.jpeg', '.png', '.ppm', '.bmp', '.pgm', '.tif', '.tiff', '.webp'] + path_list = [ + image_path + filename + for filename in os.listdir(image_path) + if os.path.splitext(filename)[-1] in filter_ext + ] + else: + path_list = [image_path] + images = [] + for path in path_list: + image = cv2.imread(path, cv2.IMREAD_GRAYSCALE).astype(paddle.get_default_dtype()) + images.append(image_preprocess(image, num_qubits)) + images = paddle.to_tensor(np.stack(images)) + model.eval() + prob = model(images) + prediction = paddle.argmax(prob, axis=1).tolist() + return prediction, paddle.nn.functional.softmax(prob).tolist() + + +if __name__ == '__main__': + exit(0) diff --git a/paddle_quantum/qpp/laurent.py b/paddle_quantum/qpp/laurent.py index 28d27e6..2594d00 100644 --- a/paddle_quantum/qpp/laurent.py +++ b/paddle_quantum/qpp/laurent.py @@ -236,7 +236,7 @@ def is_parity(self, p: int) -> Tuple[bool, complex]: Args: p: parity. - Returns + Returns: contains the following elements: * whether parity is p % 2; * if not, then return the the (maximum) absolute coef term breaking such parity; @@ -367,7 +367,7 @@ def sqrt_generation(A: Laurent) -> Laurent: Returns: a Laurent polynomial :math:`Q` such that :math:`QQ^* = A`. - Notes: + Note: More details are in Lemma S1 of the paper https://arxiv.org/abs/2209.14278. """ @@ -546,7 +546,7 @@ def deg_finder(fn: Callable[[np.ndarray], np.ndarray], Returns: the degree of approximation: - Notes: + Note: used to fix the problem of function `laurent_generator`. """ @@ -570,7 +570,7 @@ def step_laurent(deg: int) -> Laurent: Returns: a Laurent poly approximating :math:`f(x) = 0.5` if :math:`x <= 0` else :math:`0`. - Notes: + Note: used in Hamiltonian energy solver """ @@ -625,7 +625,7 @@ def ln_laurent(deg: int, t: float) -> Laurent: Returns: a Laurent poly approximating :math:`ln(cos(x)^2) / t`. - Notes: + Note: used in von Neumann entropy estimation. """ diff --git a/paddle_quantum/state/state.py b/paddle_quantum/state/state.py index f1e7d54..8625721 100644 --- a/paddle_quantum/state/state.py +++ b/paddle_quantum/state/state.py @@ -32,6 +32,7 @@ from ..hamiltonian import Hamiltonian from typing import Optional, Union, Iterable, Tuple + class State(object): r"""The quantum state class. @@ -55,7 +56,7 @@ def __init__( self.dtype = dtype if dtype is not None else paddle_quantum.get_dtype() if self.backend != Backend.QuLeaf and not isinstance(data, paddle.Tensor): data = paddle.to_tensor(data, dtype=self.dtype) - self.data = data + self.data = paddle.cast(data, self.dtype) if data.dtype != self.dtype else data # data input test if self.backend == Backend.StateVector: @@ -301,7 +302,7 @@ def expec_val(self, hamiltonian: Hamiltonian, shots: Optional[int] = 0) -> float if shots == 0: func = paddle_quantum.loss.ExpecVal(hamiltonian) result = func(self) - return result + return result.item() result = 0 gate_for_x = paddle.to_tensor([ [1 / math.sqrt(2), 1 / math.sqrt(2)], @@ -564,7 +565,7 @@ def is_state_vector(vec: Union[np.ndarray, paddle.Tensor], Returns: determine whether :math:`x^\dagger x = 1`, and return the number of qubits or an error message - Notes: + Note: error message is: * ``-1`` if the above equation does not hold * ``-2`` if the dimension of ``vec`` is not a power of 2 @@ -602,7 +603,7 @@ def is_density_matrix(rho: Union[np.ndarray, paddle.Tensor], Returns: determine whether ``rho`` is a PSD matrix with trace 1 and return the number of qubits or an error message. - Notes: + Note: error message is: * ``-1`` if ``rho`` is not PSD * ``-2`` if the trace of ``rho`` is not 1 diff --git a/paddle_quantum/trotter.py b/paddle_quantum/trotter.py index e4d6fe3..940a8fb 100644 --- a/paddle_quantum/trotter.py +++ b/paddle_quantum/trotter.py @@ -246,7 +246,7 @@ def _add_custom_block(circuit, tau, grouped_hamiltonian, custom_coefficients, pe def __add_first_order_trotter_block(circuit, tau, grouped_hamiltonian, reverse=False, optimization=False): r"""Add a time evolution block of the first order Trotter-Suzuki decompositon - Notes: + Note: This is an intrinsic function, user do not need to call this directly """ if not reverse: @@ -349,7 +349,7 @@ def optimal_circuit(circuit: paddle_quantum.ansatz.Circuit, theta: Union[paddle. def __add_second_order_trotter_block(circuit, tau, grouped_hamiltonian): r"""Add a time evolution block of the second order Trotter-Suzuki decompositon - Notes: + Note: This is an intrinsic function, user do not need to call this directly """ __add_first_order_trotter_block(circuit, tau / 2, grouped_hamiltonian) @@ -359,7 +359,7 @@ def __add_second_order_trotter_block(circuit, tau, grouped_hamiltonian): def __add_higher_order_trotter_block(circuit, tau, grouped_hamiltonian, order): r"""Add a time evolution block of the higher order (2k) Trotter-Suzuki decompositon - Notes: + Note: This is an intrinsic function, user do not need to call this directly """ assert order % 2 == 0 @@ -437,7 +437,7 @@ def __group_hamiltonian_xyz(hamiltonian): Args: hamiltonian: Hamiltonian class in Paddle Quantum - Notes: + Note: X, (Y, Z) means the terms whose Pauli word only include X, (Y, Z). For example, 'XXXY' would be a remainder term. """ grouped_hamiltonian = [] diff --git a/requirements.txt b/requirements.txt index ccc38bb..f97f2fc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,3 +11,4 @@ scikit-learn opencv-python cvxpy rich +imbalanced-learn diff --git a/setup.py b/setup.py index 6bbef53..1d9d57e 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ setuptools.setup( name='paddle-quantum', - version='2.2.2', + version='2.3.0', author='Institute for Quantum Computing, Baidu INC.', author_email='qml@baidu.com', description='Paddle Quantum is a quantum machine learning (QML) toolkit developed based on Baidu PaddlePaddle.', @@ -35,19 +35,21 @@ 'paddle_quantum', 'paddle_quantum.ansatz', 'paddle_quantum.backend', + 'paddle_quantum.biocomputing', 'paddle_quantum.channel', 'paddle_quantum.channel.functional', + 'paddle_quantum.data_analysis', + 'paddle_quantum.finance', 'paddle_quantum.gate', 'paddle_quantum.gate.functional', 'paddle_quantum.locc', 'paddle_quantum.loss', 'paddle_quantum.operator', - 'paddle_quantum.state', 'paddle_quantum.qchem', 'paddle_quantum.qml', - 'paddle_quantum.qsvt', 'paddle_quantum.qpp', - 'paddle_quantum.mbqc', + 'paddle_quantum.qsvt', + 'paddle_quantum.state', 'paddle_quantum.GIBBS', 'paddle_quantum.GIBBS.example', 'paddle_quantum.QAOA', @@ -58,6 +60,7 @@ 'paddle_quantum.VQE.example', 'paddle_quantum.VQSD', 'paddle_quantum.VQSD.example', + 'paddle_quantum.mbqc', 'paddle_quantum.mbqc.gates', 'paddle_quantum.mbqc.gates.mcalculus_tests', 'paddle_quantum.mbqc.gates.simulator_tests', @@ -73,6 +76,7 @@ 'paddle_quantum.mbqc.VQSVD.example', ], package_data={ + 'paddle_quantum.biocomputing': ['*.txt'], 'paddle_quantum.VQE': ['*.xyz'], 'paddle_quantum.VQE.example': ['*.xyz'], 'paddle_quantum.mbqc.GRCS.example': ['*.txt'], @@ -95,6 +99,7 @@ 'fastdtw', 'cvxpy', 'rich', + 'imbalanced-learn', ], python_requires='>=3.6, <4', classifiers=[ diff --git a/tutorials/machine_learning/QKernel_EN.ipynb b/tutorials/machine_learning/QKernel_EN.ipynb index 2cedc0b..75d3799 100644 --- a/tutorials/machine_learning/QKernel_EN.ipynb +++ b/tutorials/machine_learning/QKernel_EN.ipynb @@ -217,7 +217,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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", "text/plain": [ "
" ] @@ -284,7 +284,7 @@ "# Global variable for manual updates of the progress bar\n", "N = 1\n", "\n", - "# The QKE circuit simulated by paddle quantm\n", + "# The QKE circuit simulated by paddle quantum\n", "def q_kernel_estimator(x1, x2):\n", " \n", " # Transform data vectors into tensors\n", @@ -369,7 +369,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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", "text/plain": [ "
" ] @@ -437,7 +437,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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", "text/plain": [ "
" ] @@ -557,7 +557,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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", "text/plain": [ "
" ] @@ -625,7 +625,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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", "text/plain": [ "
" ] @@ -687,7 +687,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "pq", "language": "python", "name": "python3" }, @@ -701,7 +701,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.13" + "version": "3.8.13 (default, Mar 28 2022, 06:16:26) \n[Clang 12.0.0 ]" }, "toc": { "base_numbering": 1, @@ -744,6 +744,11 @@ "_Feature" ], "window_display": false + }, + "vscode": { + "interpreter": { + "hash": "08942b1340a5932ff3a93f52933a99b0e263568f3aace1d262ffa4d9a0f2da31" + } } }, "nbformat": 4, diff --git a/tutorials/mbqc/Pattern_EN.ipynb b/tutorials/mbqc/Pattern_EN.ipynb index 26f04c7..db301b8 100644 --- a/tutorials/mbqc/Pattern_EN.ipynb +++ b/tutorials/mbqc/Pattern_EN.ipynb @@ -2,17 +2,20 @@ "cells": [ { "cell_type": "markdown", + "metadata": { + "tags": [] + }, "source": [ "# Speeding Up Quantum Circuit Simulation by MBQC\n", "\n", " Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. " - ], - "metadata": { - "tags": [] - } + ] }, { "cell_type": "markdown", + "metadata": { + "tags": [] + }, "source": [ "## Introduction\n", "\n", @@ -23,13 +26,11 @@ "Another idea to solve the bottleneck of computation resources is to jump out of the framework of the quantum circuit model and simulate a circuit by its equivalents. **Measurement-Based Quantum Computation (MBQC)** [3-6] is another universal quantum computation model, that receives wide attention by its unique way of performing computation since its proposal. As is mentioned in [MBQC Quick Start Guide](MBQC_EN.ipynb), for non-adaptive measurements, not only can they be carried out simultaneously in physical implementation, but in classical simulation, they can also help to reduce the effective number of qubits in the computation, thus reducing the consumption of memory and computational resources. \n", "\n", "In this tutorial, we will introduce a new scheme of quantum circuit simulation by firstly translating it into its MBQC equivalent and then optimizing the order of measurements to finally improve the simulation efficiency. In the meanwhile, we will also demonstrate applications of the ``circuit`` and ``mcalculus`` modules in our MBQC package by two concrete examples. " - ], - "metadata": { - "tags": [] - } + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "## Quantum Circuit Simulation\n", "\n", @@ -44,11 +45,11 @@ "
Table 1: Quantum circuit simulation scheme by MBQC proposed in this tutorial
\n", "\n", "Next, we will introduce the above three steps in more details and with code implementations." - ], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "### Quantum circuit construction\n", "\n", @@ -58,12 +59,13 @@ "
Figure 1: A simple example of quantum circuit
\n", "\n", "In the figure $Ry$ stands for a rotation-y gate, the 2-qubits gate is $CNOT$ gate, $|0\\rangle$ is the initial quantum state. The code implementation using ``Circuit`` to construct such a circuit is as follows:" - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# Import common modules\n", "from numpy import pi, random\n", @@ -92,39 +94,38 @@ "# Add Ry gate\n", "cir.ry(theta[2], 0)\n", "cir.ry(theta[3], 1)" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "Then, we add measurements to the circuit.\n", "\n", "**Note**: The way to add measurements in `Circuit` is different from `UAnsatz`! The former is to call `.measure` method before running the circuit, while the latter is to call `.measure` method after running the circuit." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# Input measurement information\n", "# Measure all qubits by default\n", "cir.measure()" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "Then, we need to pass the constructed circuit to the translation module ``mcalculus`` for further process." - ], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "### Translation and optimization\n", "\n", @@ -137,19 +138,20 @@ "- standardization: integrate all subpatterns into a standard pattern;\n", "\n", "- simplification and optimization: simplify and optimize all measurement commands in the standard pattern." - ], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "In terms of code implementation, we provide `MCalculus` class for this task. We can call the method `set_circuit` to pass the constructed circuit ``cir`` to `MCalculus`." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# Import mcalculus module\n", "from paddle_quantum.mbqc.mcalculus import MCalculus\n", @@ -159,12 +161,11 @@ "\n", "# Pass the circuit to MCalculus\n", "mc.set_circuit(cir)" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "#### One-by-one translation\n", "\n", @@ -181,21 +182,21 @@ "We arrange the above commands from left to right and get a command list $[E_{12}E_{23}E_{34}E_{45}M_1M_2M_3M_4X_5Z_5]$. The detailed parameters in the commands are omitted here for simplicity. \n", "\n", "Similarly, the command list of $CNOT$ gate can be given by \\[$E_{12}E_{23}E_{24}M_1 M_2 X_4 Z_3 Z_4$\\]. A measurement in the circuit model can be simply given by a command list \\[$M_1$\\] as measurements in the circuit model play the same role as $Z$ measurements on the output qubits in MBQC." - ], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "As for the code implementation, we provide a private method `__to_subpattern` to translate quantum gates and measurements one by one. (This method records all the one-to-one correspondence between gates and subpatterns. You can also customize this function if needed.) Once all the gates and measurements are translated, the information of all the subpatterns is stored in a list named **wild pattern** which will be used for further processes.\n", "\n", "![Wild pattern](./figures/mbqc-fig-wild_pat.jpg)\n", "
Figure 2: Translate quantum gates and measurements one by one to obtain a wild pattern
" - ], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "#### Standardization\n", "\n", @@ -205,106 +206,107 @@ "\n", "![Standard pattern](./figures/mbqc-fig-pat_std.jpg)\n", "
Figure 3: Transform a wild pattern to a standard pattern
" - ], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "In terms of code implementation, we can directly call the method `standardize` to complete the standarization process. " - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# Standarization\n", "mc.standardize()" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "#### Simplification and optimization\n", "\n", "Once a standard pattern is obtained, one option is to directly pass it to the MBQC simulation module and run the simulation. However, a better job can be done before the simulation. Just like the quantum circuit optimization which aims to find a simpler but equivalent representation of the circuit, we can also find a refined pattern based on the standard one. Such refinement can be done in two approaches: removing the measurement dependency as much as possible and reordering the measurement commands. Both approaches aim to reduce the number of effective qubits involved in the actual simulation.\n", "\n", "Due to the above two considerations, we use a **signal shifting** operation to simplify the measurement dependencies and use a **row-major order optimization algorithm** to optimize the order of measurement commands." - ], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "**Signal shifting**\n", "\n", "The operation of signal shifting aims to simplify dependency by pulling out a particular type of dependency from the measurement command and compensating it with a \"signal\" command [7]. In terms of code implementation, we can directly call the method `shift_signals` to realize this." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# Signal shifting\n", "mc.shift_signals()" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "**Row-major order optimization algorithm**\n", "\n", "The row-major order optimization algorithm is a new algorithm we propose. The intention is to measure vertices with row-major order. Once all vertices in a row are measured, we can completely remove this row, thereby reducing the number of effective vertices involved in subsequent operations. Intuitively speaking, this method makes quantum gates and measurements executed by rows in some sense. Numerical experiments show that this optimization technique provides a significant improvement for the simulation of quantum shallow circuits (see below).\n", "\n", "In terms of code implementation, we can directly call `optimize_by_row` to implement the row-major optimization algorithm." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# Optimize the measurement order\n", "mc.optimize_by_row()" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "Now, we can call the method `get_pattern` to get the optimized pattern." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# Get the Pattern\n", "pattern = mc.get_pattern()" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "### Simulation\n", "\n", "Once we have the optimized pattern, we can call the method `set_pattern` in `MBQC` to pass it to the simulation module and call `run_pattern` to start the simulation process." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# Import required modules\n", "from paddle_quantum.mbqc.simulator import MBQC\n", @@ -329,34 +331,34 @@ "\n", "# Obtain quantum output\n", "quantum_output = mbqc.get_quantum_output()\n", - "print(\"The qunatum output state is:\", quantum_output)\n", + "print(\"The quantum output state is:\", quantum_output)\n", "\n", "# Obtain classcial output\n", "classical_output = mbqc.get_classical_output()\n", "print(\"The classical output is:\", classical_output)" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "At this point, we have achieved the entire process of circuit simulation." - ], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "### Function \"simulate_by_mbqc\"\n", "\n", "For convenience, we provide a function `simulate_by_mbqc` to pack the whole process of quantum circuit simulation. We can construct a circuit from ``Circuit`` first and then call `simulate_by_mbqc` to run the simulation process. This function translates the constructed circuit to its MBQC equivalent, runs the simulation, and finally returns the classical outcomes or quantum state vector equivalent to the quantum circuit model. An example is given as follows." - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# Import required modules\n", "from numpy import random, pi\n", @@ -406,12 +408,11 @@ "# Print the returned classical and quantum outputs\n", "print(\"Classical output is:\", classical_output)\n", "print(\"Quantum output is:\", quantum_output)\n" - ], - "outputs": [], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "## Examples\n", "\n", @@ -436,21 +437,21 @@ "|8|inst_5x5_10_7.txt|25|**\\|**| 18|inst_5x6_10_7.txt|30|**\\|**|28|inst_6x6_10_7.txt|36|**\\|**|38|inst_6x7_10_7.txt|42|**\\|**|48|inst_7x7_10_7.txt|49|\n", "|9|inst_5x5_10_8.txt|25|**\\|**| 19|inst_5x6_10_8.txt|30|**\\|**|29|inst_6x6_10_8.txt|36|**\\|**|39|inst_6x7_10_8.txt|42|**\\|**|49|inst_7x7_10_8.txt|49|\n", "|10|inst_5x5_10_9.txt|25|**\\|**| 20|inst_5x6_10_9.txt|30|**\\|**|30|inst_6x6_10_9.txt|36|**\\|**|40|inst_6x7_10_9.txt|42|**\\|**|50|inst_7x7_10_9.txt|49|" - ], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "We compare the running time between our simulation scheme and two other simulators (`statevector` and `matrix_product_state`) in Qiskit. We choose a shorter time from `statevector` and `matrix_product_state` as the running time of the Qiskit simulator. All the numerical experiments are conducted on a standard laptop with 16G RAM and Intel Core i7 10TH GEN CPU. The time comparison is shown in Figure 4. It is clear that our new simulation scheme provides a significant improvement over the Qiskit simulators for simulating shallow circuits.\n", "\n", "![GRCS plot](./figures/mbqc-fig-GRCS_plot.jpg)\n", "
Figure 4: Time comparison between Qiskit and our MBQC simulation scheme for different instances
" - ], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "## Conclusion\n", "\n", @@ -459,11 +460,11 @@ "Although we adopt the state vector for underlying simulation in the current MBQC package, the idea of quantum circuit simulation by MBQC is not limited to any data structure. \n", "\n", "In terms of quantum circuit simulation and MBQC based algorithms, there are still lots of unknowns to explore. Welcome to join us and discover the infinite possibilities of MBQC together!" - ], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "---\n", "## References\n", @@ -485,8 +486,7 @@ "[8] Broadbent, Anne, and Elham Kashefi. \"Parallelizing quantum circuits.\" [Theoretical computer science 410.26 (2009): 2489-2510.](https://arxiv.org/abs/0704.1736)\n", "\n", "[9] Boixo, Sergio, et al. \"Characterizing quantum supremacy in near-term devices.\" [Nature Physics 14.6 (2018): 595-600.](https://www.nature.com/articles/s41567-018-0124-x)" - ], - "metadata": {} + ] } ], "metadata": { @@ -559,4 +559,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +} diff --git a/tutorials/qnn_research/Noise_CN.ipynb b/tutorials/qnn_research/Noise_CN.ipynb index 3478c27..f3eb5b4 100644 --- a/tutorials/qnn_research/Noise_CN.ipynb +++ b/tutorials/qnn_research/Noise_CN.ipynb @@ -465,11 +465,12 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "id": "comparable-athletics", "metadata": {}, "source": [ - "**注释:** 在 [纠缠蒸馏](../locc/EntanglementDistillation_LOCCNET_CN.ipynb) 的教程中我们介绍了如何利用 Paddle Quantm 中的 LoccNet 模块来研究纠缠蒸馏,即利用多个含噪声的纠缠对来提取高保真度的纠缠对,感兴趣的读者可以前往阅读。" + "**注释:** 在 [纠缠蒸馏](../locc/EntanglementDistillation_LOCCNET_CN.ipynb) 的教程中我们介绍了如何利用 Paddle Quantum 中的 LoccNet 模块来研究纠缠蒸馏,即利用多个含噪声的纠缠对来提取高保真度的纠缠对,感兴趣的读者可以前往阅读。" ] }, { diff --git a/tutorials/quantum_simulation/BuildingMolecule_CN.ipynb b/tutorials/quantum_simulation/BuildingMolecule_CN.ipynb index a1563b8..386bcaa 100644 --- a/tutorials/quantum_simulation/BuildingMolecule_CN.ipynb +++ b/tutorials/quantum_simulation/BuildingMolecule_CN.ipynb @@ -10,6 +10,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "id": "52532fe2", "metadata": {}, @@ -34,7 +35,7 @@ "\n", "该公式使用了[原子单位](https://en.wikipedia.org/wiki/Hartree_atomic_units)。该公式包含了 $N$ 个电子和 $M$ 个原子核,其中 $x_i$ 是第 $i$ 个电子的位置,$R_I$ 是第 $I$ 个原子核的位置。\n", "\n", - "本教程分为四部分,首先我们先来讨论如何使用 `qchem` 模块构造一个分子。之后,我们将简要描述如何通过调用外部量子化学来计算\n", + "本教程分为四部分,首先我们先来讨论如何使用 `qchem` 模块构造一个分子。之后,我们将简要描述如何通过调用外部量子化学工具来计算\n", "[Hartree Fock](https://en.wikipedia.org/wiki/Hartree%E2%80%93Fock_method)\n", "单粒子轨道。接下来,我们展示如何得到二次量子化的哈密顿量。最后,我们将描述如何将费米子的哈密顿量 (Fermionic Hamiltonian) 转化为适用于量子计算机的泡利字符串 (Pauli strings)。 " ] @@ -64,8 +65,9 @@ "source": [ "# Eliminate noisy python warnings\n", "import warnings\n", - "\n", - "warnings.filterwarnings(\"ignore\")" + "warnings.filterwarnings(\"ignore\")\n", + "import logging\n", + "logging.basicConfig(level=logging.INFO)" ] }, { @@ -76,25 +78,13 @@ "outputs": [], "source": [ "# in Angstrom\n", - "h2o_structure_direct = [[\"H\", [-0.02111417,0.8350417,1.47688078]], # H 代表着水分子中的氢元素\n", - " [\"O\", [0.0, 0.0, 0.0]], # O 代表着水分子中的氧元素\n", - " [\"H\", [-0.00201087,0.45191737,-0.27300254]]]" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "d354c5dd", - "metadata": {}, - "outputs": [], - "source": [ - "from paddle_quantum.qchem import geometry\n", - "\n", - "h2o_structure_xyz = geometry(file=\"h2o.xyz\")\n", - "assert h2o_structure_xyz == h2o_structure_direct" + "h2o_coords = [(\"H\", [-0.02111417,0.8350417,1.47688078]), # H 代表着水分子中的氢元素\n", + " (\"O\", [0.0, 0.0, 0.0]), # O 代表着水分子中的氧元素\n", + " (\"H\", [-0.00201087,0.45191737,-0.27300254])]" ] }, { + "attachments": {}, "cell_type": "markdown", "id": "e996795a", "metadata": {}, @@ -103,38 +93,57 @@ "\n", "Hartree Fock 方法使用 [Slater 行列式](https://en.wikipedia.org/wiki/Slater_determinant)来表示 $N$ 电子波函数。它可以为我们提供一套单粒子轨道,这些轨道通常被作为其他量子化学方法的输入。 \n", "\n", - "量桨使用 psi4 [1] 作为它的量子化学引擎。我们可以使用 `qchem` 模块提供的 `get_molecular_data` 函数来执行相关计算,得到分子的所需信息。 `get_molecular_data` 函数以分子结构、分子总电荷和自旋多重性为主要输入,并返回一个 OpenFermion [2] `MolecularData` 对象。 \n", - "\n", - "我们继续以水分子为例。为了运行 Hartree Fock 计算,我们需要将 `method` 设置为 *scf* (Self - Consistent Field)。 我们还可以通过在 `basis` 参数中指定 [basis set](https://en.wikipedia.org/wiki/Basis_set_(chemistry)) 的类型来提高 Hartree Fock 计算的精度。" + "量桨当前的量子化学引擎是 PySCF [1],未来我们也将支持 psi4 等更多的量子化学工具包(注:PySCF 目前仅支持 Mac 和 Linux 平台)。我们可以使用 `qchem` 模块提供的 `PySCFDriver` 类来进行 Hartree Fock 计算,它以分子结构、分子总电荷数和自旋多重度为主要输入,我们还可以通过在 `basis` 参数中指定不同的 [basis set](https://en.wikipedia.org/wiki/Basis_set_(chemistry)) 来控制 Hartree Fock 计算的精度。" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "29945676", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:root:\n", + "#######################################\n", + "SCF Calculation (Classical)\n", + "#######################################\n", + "INFO:root:Basis: sto-3g\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "converged SCF energy = -73.9677038774737\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:root:SCF energy: -73.96770.\n" + ] + } + ], "source": [ - "from paddle_quantum.qchem import get_molecular_data\n", - "\n", - "h2o_moledata = get_molecular_data(\n", - " h2o_structure_direct,\n", - " charge=0, # 水分子是中性的,不带电\n", - " multiplicity=1, # 水分子只有一个未配对电子\n", - " basis=\"sto-3g\",\n", - " method=\"scf\",\n", - " if_save=True, # 是否将 MolecularData 中的信息存储成 hdf5 文件\n", - " if_print=True, # 是否需要打印出水分子的基态能量\n", - " name=\"\", # 指定 hdf5 文件的名字\n", - " file_path=\".\" # 指定 hdf5 文件的路径 \n", + "from paddle_quantum.qchem import PySCFDriver\n", + "\n", + "driver = PySCFDriver()\n", + "driver.load_molecule(\n", + " atom=h2o_coords, # H2O 分子几何结构\n", + " basis=\"sto-3g\", # 量子化学计算基组(basis set)\n", + " multiplicity=1, # 分子的自旋多重度,因为水分子总自旋为S=0,所以自旋多重度2S+1=1\n", + " charge=0, # 分子电荷,水分子是中性分子,总电荷为0\n", + " unit=\"Angstrom\"\n", ")\n", - "\n", - "from openfermion.chem import MolecularData\n", - "\n", - "assert isinstance(h2o_moledata, MolecularData)" + "driver.run_scf() # 进行 Hartree Fock 计算" ] }, { + "attachments": {}, "cell_type": "markdown", "id": "973af7d1", "metadata": {}, @@ -146,12 +155,12 @@ "$$\n", "\\hat{H}=\\sum_{p,q}h_{pq}\\hat{c}^{\\dagger}_p\\hat{c}_q+\\frac{1}{2}\\sum_{p,q,r,s}v_{pqrs}\\hat{c}^{\\dagger}_p\\hat{c}^{\\dagger}_q\\hat{c}_r\\hat{c}_s,\\tag{3}$$\n", "\n", - "其中 $p$, $q$, $r$ 和 $s$ 是 Hartree Fock 轨道,$\\hat{c}^{\\dagger}_p$ 和 $\\hat{c}_q$ 分别是生成算子 (creation operation) 和湮灭算子 (annihilation operation)。系数 $h_{pq}$ 和 $v_{pqrs}$ 分别是单体积分 (one-body integrations) 和双体积分 (two-body integrations),该信息可以用如下的方式从 `MolecularData` 提取出来。" + "其中 $p$, $q$, $r$ 和 $s$ 是 Hartree Fock 轨道,$\\hat{c}^{\\dagger}_p$ 和 $\\hat{c}_q$ 分别是生成算子 (creation operation) 和湮灭算子 (annihilation operation)。系数 $h_{pq}$ 和 $v_{pqrs}$ 分别是单体积分 (one-body integrations) 和双体积分 (two-body integrations),该信息可以用如下的方式从 `PySCFDriver` 中计算出来。" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "id": "a9a906ad", "metadata": {}, "outputs": [ @@ -159,13 +168,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[-3.2911e+01 5.5623e-01 2.8755e-01 1.4640e-15 -7.4568e-02 -9.4552e-02 2.8670e-01]\n", - " [ 5.5623e-01 -8.0729e+00 -4.0904e-02 -1.6823e-15 1.7890e-01 3.5048e-01 -1.3460e+00]\n", - " [ 2.8755e-01 -4.0904e-02 -7.3355e+00 -4.8424e-15 4.1911e-01 5.2109e-01 7.0928e-01]\n", - " [ 1.4640e-15 -1.6823e-15 -4.8424e-15 -7.5108e+00 -1.4127e-14 -2.6576e-14 -1.5008e-15]\n", - " [-7.4568e-02 1.7890e-01 4.1911e-01 -1.4127e-14 -5.7849e+00 2.0887e+00 1.2427e-01]\n", - " [-9.4552e-02 3.5048e-01 5.2109e-01 -2.6576e-14 2.0887e+00 -5.0803e+00 1.3967e-02]\n", - " [ 2.8670e-01 -1.3460e+00 7.0928e-01 -1.5008e-15 1.2427e-01 1.3967e-02 -5.0218e+00]]\n" + "[[-3.2911e+01 5.5623e-01 2.8755e-01 -5.1653e-15 -7.4568e-02 -9.4552e-02 -2.8670e-01]\n", + " [ 5.5623e-01 -8.0729e+00 -4.0904e-02 1.5532e-15 1.7890e-01 3.5048e-01 1.3460e+00]\n", + " [ 2.8755e-01 -4.0904e-02 -7.3355e+00 6.8611e-16 4.1911e-01 5.2109e-01 -7.0928e-01]\n", + " [-5.1650e-15 1.5617e-15 6.7472e-16 -7.5108e+00 5.4096e-16 1.7947e-15 4.5448e-16]\n", + " [-7.4568e-02 1.7890e-01 4.1911e-01 5.3561e-16 -5.7849e+00 2.0887e+00 -1.2427e-01]\n", + " [-9.4552e-02 3.5048e-01 5.2109e-01 1.8235e-15 2.0887e+00 -5.0803e+00 -1.3967e-02]\n", + " [-2.8670e-01 1.3460e+00 -7.0928e-01 4.8475e-16 -1.2427e-01 -1.3967e-02 -5.0218e+00]]\n" ] } ], @@ -173,107 +182,77 @@ "import numpy as np \n", "np.set_printoptions(precision=4, linewidth=150)\n", "\n", - "hpq, vpqrs = h2o_moledata.get_integrals()\n", - "assert np.shape(hpq)==(7, 7) # When use sto3g basis, the total number of molecular orbitals used in water calculation is 7\n", + "hpq = driver.get_onebody_tensor(\"int1e_kin\") + driver.get_onebody_tensor(\"int1e_nuc\")\n", + "vpqrs = driver.get_twobody_tensor()\n", + "assert np.shape(hpq)==(7, 7) # H2O 分子在STO-3G基组下的轨道数量为7个\n", "assert np.shape(vpqrs)==(7, 7, 7, 7)\n", "\n", "print(hpq)\n", - "# print(vpqrs)" + "# print(vpqrs) # vpqrs张量规模较大,为使输出更简洁,我们默认将这一行注释,感兴趣的用户可以将这个张量打印出来" ] }, { + "attachments": {}, "cell_type": "markdown", "id": "de4f1bf2", "metadata": {}, "source": [ - "大多数情况下,我们并不需要把这些积分信息手动的提取出来,*qchem* 模块已经将该步骤融入函数 `fermionic_hamiltonian` 中,我们可以直接进行下一步的运算,获得哈密顿量。" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "069cfcd5", - "metadata": {}, - "outputs": [], - "source": [ - "from paddle_quantum.qchem import fermionic_hamiltonian\n", - "\n", - "H_of_water = fermionic_hamiltonian(\n", - " h2o_moledata,\n", - " multiplicity=1,\n", - " active_electrons=4,\n", - " active_orbitals=4\n", - ")\n", - "\n", - "from openfermion.ops import FermionOperator\n", - "\n", - "assert isinstance(H_of_water, FermionOperator)" - ] - }, - { - "cell_type": "markdown", - "id": "47f24b4b", - "metadata": {}, - "source": [ - "通过指定 `active_electrons` 和 `active_orbitals`,我们可以限制哈密顿的自由度,从而可以减少哈密顿量的的项数,加速运算。我们还可以使用 *qchem* 中 `active_space` 函数来查看哪些轨道是**核心 (core)** 轨道,哪些轨道是**活跃 (active)** 轨道。" + "大多数情况下,我们并不需要把这些积分信息手动的提取出来,*qchem* 模块已经将该过程封装在 `Molecule` 类中,我们可以直接从它里面得到水分子的哈密顿量。" ] }, { "cell_type": "code", "execution_count": 8, - "id": "c136344d", + "id": "069cfcd5", "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:root:\n", + "#######################################\n", + "SCF Calculation (Classical)\n", + "#######################################\n", + "INFO:root:Basis: sto-3g\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "List of core orbitals: [0, 1, 2]\n", - "List of active orbitals: [3, 4, 5, 6]\n" + "converged SCF energy = -73.9677038774737\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:root:SCF energy: -73.96770.\n" ] } ], "source": [ - "from paddle_quantum.qchem import active_space\n", + "# 构建 H2O 的 `Molecule` 实例\n", + "from paddle_quantum.qchem import Molecule\n", "\n", - "core_orbits_list, act_orbits_list = active_space(\n", - " 10, # number of electrons in water molecule\n", - " 7, # number of molecular orbitals in water molecule\n", - " active_electrons=4,\n", - " active_orbitals=4\n", + "mol = Molecule(\n", + " geometry=h2o_coords,\n", + " basis=\"sto-3g\",\n", + " driver=PySCFDriver()\n", ")\n", "\n", - "print(\"List of core orbitals: {:}\".format(core_orbits_list))\n", - "print(\"List of active orbitals: {:}\".format(act_orbits_list))" + "\n", + "H_of_water = mol.get_molecular_hamiltonian()" ] }, { + "attachments": {}, "cell_type": "markdown", - "id": "05b6aefc", + "id": "e71ea17b", "metadata": {}, "source": [ - "## 从费米子哈密顿量到自旋哈密顿量\n", - "\n", - "在量子计算中,我们只能使用由泡利矩阵构成的算符(operator)\n", - "\n", - "$$\n", - "\\boldsymbol{\\sigma}_x=\\begin{pmatrix}\n", - "0 & 1\\\\\n", - "1 & 0\n", - "\\end{pmatrix},\\quad \\boldsymbol{\\sigma}_y=\\begin{pmatrix}\n", - "0 & -i\\\\\n", - "i & 0\n", - "\\end{pmatrix},\\quad \\boldsymbol{\\sigma}_z=\\begin{pmatrix}\n", - "1 & 0\\\\\n", - "0 & -1\n", - "\\end{pmatrix}.\\tag{4}\n", - "$$\n", - "\n", - "因此,我们需要将现有的哈密顿量(二次量子化形式)变换成量子比特算符 (qubit operator),这种变换被称为 [Jordan-Wigner 变换](https://en.wikipedia.org/wiki/Jordan%E2%80%93Wigner_transformation)。\n", - "\n", - "> 此外,Bravyi-Kitaev 变换也可以达到相同的效果,只需要将参数 mapping_method 改成 'bravyi_kitaev' 即可。\n", - "\n", - "在量桨中,我们提供 `spin_hamiltonian` 函数来实现从费米子哈密顿量到自旋哈密顿量的变换,代码如下:" + "由于量子计算中允许的操作是基于泡利算符$\\hat{\\sigma}_x$、$\\hat{\\sigma}_y$、$\\hat{\\sigma}_z$的,因此,我们需要将上式中的哈密顿量(二次量子化形式)变换成量子比特算符 (qubit operator),这可以通过[Jordan-Wigner 变换](https://en.wikipedia.org/wiki/Jordan%E2%80%93Wigner_transformation)实现。在*qchem*中,这一功能已经被集成到 `get_molecular_hamiltonian` 方法中了。" ] }, { @@ -286,34 +265,24 @@ "name": "stdout", "output_type": "stream", "text": [ - "There are 193 terms in H2O Hamiltonian in total.\n", + "There are 2110 terms in H2O Hamiltonian in total.\n", "The first 10 terms are \n", - " -72.10615980544185 I\n", - "-0.007310917992546774 X0, X1, Y2, Y3\n", - "0.0052460870730834325 X0, X1, Y2, Z3, Z4, Y5\n", - "0.0016283548447087654 X0, X1, Y2, Z3, Z4, Z5, Z6, Y7\n", - "0.0052460870730834325 X0, X1, X3, X4\n", - "0.0016283548447087654 X0, X1, X3, Z4, Z5, X6\n", - "-0.005994544380559027 X0, X1, Y4, Y5\n", - "0.001387644178102622 X0, X1, Y4, Z5, Z6, Y7\n", - "0.001387644178102622 X0, X1, X5, X6\n", - "-0.009538223793221182 X0, X1, Y6, Y7\n" + " -44.808115297466266 I\n", + "12.537584025014317 Z0\n", + "12.537584025014313 Z1\n", + "1.8115178684479893 Z2\n", + "1.8115178684479893 Z3\n", + "1.4546840922727915 Z4\n", + "1.4546840922727915 Z5\n", + "1.4299903414012243 Z6\n", + "1.429990341401224 Z7\n", + "1.0849294605602529 Z8\n" ] } ], "source": [ - "from paddle_quantum.qchem import spin_hamiltonian\n", - "\n", - "pauli_H_of_water_ = spin_hamiltonian(\n", - " h2o_moledata,\n", - " multiplicity=1,\n", - " active_electrons=4,\n", - " active_orbitals=4,\n", - " mapping_method='jordan_wigner'\n", - ")\n", - "\n", - "print('There are', pauli_H_of_water_.n_terms, 'terms in H2O Hamiltonian in total.')\n", - "print('The first 10 terms are \\n', pauli_H_of_water_[:10])" + "print('There are', H_of_water.n_terms, 'terms in H2O Hamiltonian in total.')\n", + "print('The first 10 terms are \\n', H_of_water[:10])" ] }, { @@ -325,6 +294,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "id": "d7417802", "metadata": {}, @@ -332,19 +302,13 @@ "---\n", "## 参考文献\n", "\n", - "[1] [Psi4: an open-source ab initio electronic structure program](https://wires.onlinelibrary.wiley.com/doi/abs/10.1002/wcms.93)\n", - "\n", - "[2] [OpenFermion: the electronic structure package for quantum computers\n", - "](https://iopscience.iop.org/article/10.1088/2058-9565/ab8ebc)" + "[1] [PySCF: Python-based Simulations of Chemistry Framework](https://pyscf.org/)" ] } ], "metadata": { - "interpreter": { - "hash": "f7cfecff1ef1940b21a48efa1b88278bb096bd916f13c2df11af4810c38b47e1" - }, "kernelspec": { - "display_name": "Python 3.8.0 ('pq')", + "display_name": "modellib", "language": "python", "name": "python3" }, @@ -358,7 +322,12 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.0" + "version": "3.7.15" + }, + "vscode": { + "interpreter": { + "hash": "8f24120f890011f53feb4ed62c47961d8565ec1de8b7cb23548c15bd6da8f2d2" + } } }, "nbformat": 4, diff --git a/tutorials/quantum_simulation/BuildingMolecule_EN.ipynb b/tutorials/quantum_simulation/BuildingMolecule_EN.ipynb index 5079640..eff7ae0 100644 --- a/tutorials/quantum_simulation/BuildingMolecule_EN.ipynb +++ b/tutorials/quantum_simulation/BuildingMolecule_EN.ipynb @@ -9,12 +9,13 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Overview\n", "\n", - "In this tutorial, we will demonstrate how to use Paddle Quantum's `qchem` module to build valid Hamiltonian for simulating chemical molecules on a quantum computer. We will go step by step how to build the second quantized Hamiltonian from a molecular structure and how to transform it to a set of Pauli matrices. \n", + "In this tutorial, we will demonstrate how to use Paddle Quantum's *qchem* module to build valid Hamiltonian for simulating chemical molecules on a quantum computer. We will go step by step how to build the second quantized Hamiltonian from a molecular structure and how to transform it to a set of Pauli matrices. \n", "\n", "Hamiltonian is a physical quantity related to the total energy of a physical system. In general, it can be represented as \n", "\n", @@ -32,7 +33,7 @@ "\n", "when we use [atomic units](https://en.wikipedia.org/wiki/Hartree_atomic_units). Our electronic problem contains $N$ electrons and $M$ nucleus. We use $x_i$ to denote position of the $i$-th electron, and use $R_I$ to denote position of the $I$-th nuclei. \n", "\n", - "This tutorial will have the following parts. Let's first talk about how to construct a molecule in `qchem`. After that, we will briefly describe how to calculate [Hartree Fock](https://en.wikipedia.org/wiki/Hartree%E2%80%93Fock_method) single particle orbitals by calling external quantum chemistry within Paddle Quantum. Next, we show how we can obtain the Hamiltonian in second quantization representation. Finally, we describe how to transform the Fermionic Hamiltonian to Pauli strings recognized by quantum computer." + "This tutorial will have the following parts. Let's first talk about how to construct a molecule in *qchem*. After that, we will briefly describe how to calculate [Hartree Fock](https://en.wikipedia.org/wiki/Hartree%E2%80%93Fock_method) single particle orbitals by calling external quantum chemistry within Paddle Quantum. Next, we show how we can obtain molecular Hamiltonian using *qchem*'s `Molecule` class." ] }, { @@ -57,8 +58,9 @@ "source": [ "# Eliminate noisy python warnings\n", "import warnings\n", - "\n", - "warnings.filterwarnings(\"ignore\")" + "warnings.filterwarnings(\"ignore\")\n", + "import logging\n", + "logging.basicConfig(level=logging.INFO)" ] }, { @@ -68,68 +70,69 @@ "outputs": [], "source": [ "# in Angstrom\n", - "h2o_structure_direct = [[\"H\", [-0.02111417,0.8350417,1.47688078]], # H stands for hydrogen element in water\n", - " [\"O\", [0.0, 0.0, 0.0]], # O stands for oxygen element in water\n", - " [\"H\", [-0.00201087,0.45191737,-0.27300254]]]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Instead of specifying molecular structure directly, we can also pass the \\*.xyz file to the `geometry` function to get the same structure." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "from paddle_quantum.qchem import geometry\n", - "\n", - "h2o_structure_xyz = geometry(file=\"h2o.xyz\")\n", - "assert h2o_structure_xyz == h2o_structure_direct" + "h2o_coords = [(\"H\", [-0.02111417,0.8350417,1.47688078]), # H stands for hydrogen element in water\n", + " (\"O\", [0.0, 0.0, 0.0]), # O stands for oxygen element in water\n", + " (\"H\", [-0.00201087,0.45191737,-0.27300254])]" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Calculate Hartree Fock orbitals\n", "Hartree Fock method uses the [Slater determinant](https://en.wikipedia.org/wiki/Slater_determinant) to represent the $N$-electron wavefunction. It could provide us with a set of single particle orbitals which are often taken as input to more advanced quantum chemistry methods. \n", "\n", - "Paddle Quantum uses psi4 [1] as its quantum chemistry engine. We could use the `get_molecular_data` function provided in `qchem` module to manage the quantum chemistry calculation and get the necessary information about the molecule. `get_molecular_data` function takes molecular structure, total molecular charge, and spin multiplicity as its major inputs, it will return an OpenFermion [2] `MolecularData` object. \n", - "\n", - "Let's continue with our water molecule example. To run the Hartree Fock calculation, we need to set the `method` keyword argument to *scf* (Self Consistent Field). We can also improve the quality of Hartree Fock calculation by specifying the type of [basis set](https://en.wikipedia.org/wiki/Basis_set_(chemistry)) in the `basis` argument. " + "Currently, Paddle Quantum uses PySCF [1] as its quantum chemistry engine, we will support more quantum chemistry toolkits, such as psi4, in the future (NOTE: PySCF currently only support Mac and Linux platform). We could use the `PySCFDriver` provided in `qchem` module to manage the quantum chemistry calculation and get the necessary information about the molecule. It takes molecular structure, total molecular charge, and spin multiplicity as its major inputs. We can also control the precision of Hartree Fock calculation by setting different [basis set](https://en.wikipedia.org/wiki/Basis_set_(chemistry)) for `basis` keyword. " ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:root:\n", + "#######################################\n", + "SCF Calculation (Classical)\n", + "#######################################\n", + "INFO:root:Basis: sto-3g\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "converged SCF energy = -73.9677038774737\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:root:SCF energy: -73.96770.\n" + ] + } + ], "source": [ - "from paddle_quantum.qchem import get_molecular_data\n", - "\n", - "h2o_moledata = get_molecular_data(\n", - " h2o_structure_direct,\n", - " charge=0, # Water molecule is charge neutral\n", - " multiplicity=1, # In the ground state, the lowest 5 molecular orbitals of water molecular will be occupied by a pair of electrons with opposite spin\n", - " basis=\"sto-3g\",\n", - " method=\"scf\",\n", - " if_save=True, # Whether to save information contained in MolecularData object to a hdf5 file\n", - " if_print=True, # Wheter to print the ground state energy of water molecule\n", - " name=\"\", # Specifies the name of the hdf5 file\n", - " file_path=\".\" # Specifies where to store the hdf5 file \n", + "from paddle_quantum.qchem import PySCFDriver\n", + "\n", + "driver = PySCFDriver()\n", + "driver.load_molecule(\n", + " atom=h2o_coords, # Geometry of H2O molecule\n", + " basis=\"sto-3g\", # Basis set for quantum chemistry calculation\n", + " multiplicity=1, # Spin multiplicity for molecule, since the total spin of H2O is S=0,its spin multiplicity is 2S+1=1\n", + " charge=0, # Total charge of molecule, since H2O is charge neutral, its charge=0\n", + " unit=\"Angstrom\"\n", ")\n", - "\n", - "from openfermion.chem import MolecularData\n", - "\n", - "assert isinstance(h2o_moledata, MolecularData)" + "driver.run_scf() # Perform Hartree Fock calculation" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -139,25 +142,25 @@ "$$\n", "\\hat{H}=\\sum_{p,q}h_{pq}\\hat{c}^{\\dagger}_p\\hat{c}_q+\\frac{1}{2}\\sum_{p,q,r,s}v_{pqrs}\\hat{c}^{\\dagger}_p\\hat{c}^{\\dagger}_q\\hat{c}_r\\hat{c}_s,\\tag{3}$$\n", "\n", - "where $p$, $q$, $r$ and $s$ are Hartree Fock orbitals computed in the previous section. $\\hat{c}^{\\dagger}_p$ and $\\hat{c}_q$ are creation and annihilation operations, respectively. The two coefficients $h_{pq}$ and $v_{pqrs}$ are called molecular integrals, and can be obtained from `MolecularData` object in the following way." + "where $p$, $q$, $r$ and $s$ are Hartree Fock orbitals computed in the previous section. $\\hat{c}^{\\dagger}_p$ and $\\hat{c}_q$ are creation and annihilation operations, respectively. The two coefficients $h_{pq}$ and $v_{pqrs}$ are called molecular integrals, and can be obtained from `PySCFDriver` in the following way." ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "[[-3.2911e+01 5.5623e-01 2.8755e-01 1.4640e-15 -7.4568e-02 -9.4552e-02 2.8670e-01]\n", - " [ 5.5623e-01 -8.0729e+00 -4.0904e-02 -1.6823e-15 1.7890e-01 3.5048e-01 -1.3460e+00]\n", - " [ 2.8755e-01 -4.0904e-02 -7.3355e+00 -4.8424e-15 4.1911e-01 5.2109e-01 7.0928e-01]\n", - " [ 1.4640e-15 -1.6823e-15 -4.8424e-15 -7.5108e+00 -1.4127e-14 -2.6576e-14 -1.5008e-15]\n", - " [-7.4568e-02 1.7890e-01 4.1911e-01 -1.4127e-14 -5.7849e+00 2.0887e+00 1.2427e-01]\n", - " [-9.4552e-02 3.5048e-01 5.2109e-01 -2.6576e-14 2.0887e+00 -5.0803e+00 1.3967e-02]\n", - " [ 2.8670e-01 -1.3460e+00 7.0928e-01 -1.5008e-15 1.2427e-01 1.3967e-02 -5.0218e+00]]\n" + "[[-3.2911e+01 5.5623e-01 2.8755e-01 -5.1653e-15 -7.4568e-02 -9.4552e-02 -2.8670e-01]\n", + " [ 5.5623e-01 -8.0729e+00 -4.0904e-02 1.5532e-15 1.7890e-01 3.5048e-01 1.3460e+00]\n", + " [ 2.8755e-01 -4.0904e-02 -7.3355e+00 6.8611e-16 4.1911e-01 5.2109e-01 -7.0928e-01]\n", + " [-5.1650e-15 1.5617e-15 6.7472e-16 -7.5108e+00 5.4096e-16 1.7947e-15 4.5448e-16]\n", + " [-7.4568e-02 1.7890e-01 4.1911e-01 5.3561e-16 -5.7849e+00 2.0887e+00 -1.2427e-01]\n", + " [-9.4552e-02 3.5048e-01 5.2109e-01 1.8235e-15 2.0887e+00 -5.0803e+00 -1.3967e-02]\n", + " [-2.8670e-01 1.3460e+00 -7.0928e-01 4.8475e-16 -1.2427e-01 -1.3967e-02 -5.0218e+00]]\n" ] } ], @@ -165,139 +168,103 @@ "import numpy as np \n", "np.set_printoptions(precision=4, linewidth=150)\n", "\n", - "hpq, vpqrs = h2o_moledata.get_integrals()\n", - "assert np.shape(hpq)==(7, 7) # When use sto3g basis, the total number of molecular orbitals used in water calculation is 7\n", + "hpq = driver.get_onebody_tensor(\"int1e_kin\") + driver.get_onebody_tensor(\"int1e_nuc\")\n", + "vpqrs = driver.get_twobody_tensor()\n", + "assert np.shape(hpq)==(7, 7) # H2O has 7 orbitals when using STO-3G basis.\n", "assert np.shape(vpqrs)==(7, 7, 7, 7)\n", "\n", "print(hpq)\n", - "# print(vpqrs)" + "# print(vpqrs) # vpqrs is a large tensor,for brevity, we comment this line by default, interested users can uncomment this line to see it." ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "Most of the time, we don't need to extract those integrals and assemble the Hamiltonian manually, *qchem* module has already helped us take care of this by providing the `fermionic_hamiltonian` function." + "Most of the time, we don't need to extract those integrals and assemble the Hamiltonian manually, *qchem* has already ecapsulated this procedure in `Molecule` class, we can get the Hamiltonian of water molecule from it." ] }, { "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "from paddle_quantum.qchem import fermionic_hamiltonian\n", - "\n", - "H_of_water = fermionic_hamiltonian(\n", - " h2o_moledata,\n", - " multiplicity=1,\n", - " active_electrons=4,\n", - " active_orbitals=4\n", - ")\n", - "\n", - "from openfermion.ops import FermionOperator\n", - "\n", - "assert isinstance(H_of_water, FermionOperator)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "By specifying `active_electrons` and `active_orbitals` keyword arguments, we can reduce the number of freedom of our Hamiltonian and thus reduce the number of terms in the spin Hamiltonian described in the next section. We can also use `active_space` function in *qchem* to return a list of *core* orbitals and *active* orbitals. " - ] - }, - { - "cell_type": "code", - "execution_count": 8, + "execution_count": 6, "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:root:\n", + "#######################################\n", + "SCF Calculation (Classical)\n", + "#######################################\n", + "INFO:root:Basis: sto-3g\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "List of core orbitals: [0, 1, 2]\n", - "List of active orbitals: [3, 4, 5, 6]\n" + "converged SCF energy = -73.9677038774737\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:root:SCF energy: -73.96770.\n" ] } ], "source": [ - "from paddle_quantum.qchem import active_space\n", + "# Build `Molecule` for water molecule.\n", + "from paddle_quantum.qchem import Molecule\n", "\n", - "core_orbits_list, act_orbits_list = active_space(\n", - " 10, # number of electrons in water molecule\n", - " 7, # number of molecular orbitals in water molecule\n", - " active_electrons=4,\n", - " active_orbitals=4\n", + "mol = Molecule(\n", + " geometry=h2o_coords,\n", + " basis=\"sto-3g\",\n", + " driver=PySCFDriver()\n", ")\n", "\n", - "print(\"List of core orbitals: {:}\".format(core_orbits_list))\n", - "print(\"List of active orbitals: {:}\".format(act_orbits_list))" + "\n", + "H_of_water = mol.get_molecular_hamiltonian()" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "## From Fermionic Hamiltonian to spin Hamiltonian\n", - "In quantum computing, we only have qubit operators composed of Pauli matrices\n", - "\n", - "$$\n", - "\\boldsymbol{\\sigma}_x=\\begin{pmatrix}\n", - "0 & 1\\\\\n", - "1 & 0\n", - "\\end{pmatrix},\\quad \\boldsymbol{\\sigma}_y=\\begin{pmatrix}\n", - "0 & -i\\\\\n", - "i & 0\n", - "\\end{pmatrix},\\quad \\boldsymbol{\\sigma}_z=\\begin{pmatrix}\n", - "1 & 0\\\\\n", - "0 & -1\n", - "\\end{pmatrix}.\\tag{4}\n", - "$$\n", - "\n", - "Therefore, we need to transform our Hamiltonian in the previous section to qubit operators, [Jordan-Wigner transform](https://en.wikipedia.org/wiki/Jordan%E2%80%93Wigner_transformation) is one of the well-known methods to realize the transformation.\n", - "> Alternatively, we also provide Bravyi-Kitaev transformation, by changing the argument, mapping_method, to 'bravyi_kitaev'.\n", - "\n", - "In *paddle quantum*, Hamiltonian is encoded in *pauli_str*. To avoid tedious manipulation of *string* object, we have provided `spin_hamiltonian` function which can generate the needed *pauli_str* from molecular structure on the fly." + "Since quantum computing only allows operation based on Pauli operators $\\hat{\\sigma}_x$, $\\hat{\\sigma}_y$, $\\hat{\\sigma}_z$, we have to transform Hamiltonian in above expression to a form represented by Pauli operators. This can be achieved via [Jordan-Wigner 变换](https://en.wikipedia.org/wiki/Jordan%E2%80%93Wigner_transformation) and we have already integrated it in `get_molecular_hamiltonian` method." ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "There are 193 terms in H2O Hamiltonian in total.\n", + "There are 2110 terms in H2O Hamiltonian in total.\n", "The first 10 terms are \n", - " -72.10615980544185 I\n", - "-0.007310917992546774 X0, X1, Y2, Y3\n", - "0.0052460870730834325 X0, X1, Y2, Z3, Z4, Y5\n", - "0.0016283548447087654 X0, X1, Y2, Z3, Z4, Z5, Z6, Y7\n", - "0.0052460870730834325 X0, X1, X3, X4\n", - "0.0016283548447087654 X0, X1, X3, Z4, Z5, X6\n", - "-0.005994544380559027 X0, X1, Y4, Y5\n", - "0.001387644178102622 X0, X1, Y4, Z5, Z6, Y7\n", - "0.001387644178102622 X0, X1, X5, X6\n", - "-0.009538223793221182 X0, X1, Y6, Y7\n" + " -44.808115297466266 I\n", + "12.537584025014317 Z0\n", + "12.537584025014313 Z1\n", + "1.8115178684479893 Z2\n", + "1.8115178684479893 Z3\n", + "1.4546840922727915 Z4\n", + "1.4546840922727915 Z5\n", + "1.4299903414012243 Z6\n", + "1.429990341401224 Z7\n", + "1.0849294605602529 Z8\n" ] } ], "source": [ - "from paddle_quantum.qchem import spin_hamiltonian\n", - "\n", - "pauli_H_of_water_ = spin_hamiltonian(\n", - " h2o_moledata,\n", - " multiplicity=1,\n", - " active_electrons=4,\n", - " active_orbitals=4,\n", - " mapping_method='jordan_wigner'\n", - ")\n", - "\n", - "print('There are ', pauli_H_of_water_.n_terms, 'terms in H2O Hamiltonian in total.')\n", - "print('The first 10 terms are \\n', pauli_H_of_water_[:10])" + "print('There are', H_of_water.n_terms, 'terms in H2O Hamiltonian in total.')\n", + "print('The first 10 terms are \\n', H_of_water[:10])" ] }, { @@ -308,25 +275,20 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "## References\n", "\n", - "[1] [Psi4: an open-source ab initio electronic structure program](https://wires.onlinelibrary.wiley.com/doi/abs/10.1002/wcms.93)\n", - "\n", - "[2] [OpenFermion: the electronic structure package for quantum computers\n", - "](https://iopscience.iop.org/article/10.1088/2058-9565/ab8ebc)" + "[1] [PySCF: Python-based Simulations of Chemistry Framework](https://pyscf.org/)" ] } ], "metadata": { - "interpreter": { - "hash": "f7cfecff1ef1940b21a48efa1b88278bb096bd916f13c2df11af4810c38b47e1" - }, "kernelspec": { - "display_name": "Python 3.8.0 ('pq')", + "display_name": "modellib", "language": "python", "name": "python3" }, @@ -340,7 +302,12 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.0" + "version": "3.7.15" + }, + "vscode": { + "interpreter": { + "hash": "8f24120f890011f53feb4ed62c47961d8565ec1de8b7cb23548c15bd6da8f2d2" + } } }, "nbformat": 4, diff --git a/tutorials/quantum_simulation/VQE_CN.ipynb b/tutorials/quantum_simulation/VQE_CN.ipynb index 349e137..13bdf69 100644 --- a/tutorials/quantum_simulation/VQE_CN.ipynb +++ b/tutorials/quantum_simulation/VQE_CN.ipynb @@ -103,6 +103,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -110,7 +111,7 @@ "\n", "### 构造电子哈密顿量\n", "\n", - "首先,让我们通过下面几行代码引入必要的 library 和 package。量桨的量子化学工具包是基于 `psi4` 和 `openfermion` 进行开发的,所以需要读者先行安装这两个语言包。在进入下面的教程之前,我们强烈建议您先阅读[哈密顿量的构造](./BuildingMolecule_CN.ipynb)教程,该教程介绍了如何使用量桨的量子化学工具包。\n", + "首先,让我们通过下面几行代码引入必要的 library 和 package。量桨的量子化学工具包是基于 *openfermion* 进行开发的,目前使用的量子化学引擎是 *PySCF*,所以需要读者先行安装这两个语言包(注:*PySCF* 当前仅支持 Mac 和 Linux 平台,我们正在努力开发支持更多量子化学工具的引擎,将在 Paddle Quantum 下一次更新中完善对 Windows 平台的支持)。在进入下面的教程之前,我们强烈建议您先阅读[哈密顿量的构造](./BuildingMolecule_CN.ipynb)教程,该教程介绍了如何使用量桨的量子化学工具包。\n", "\n", "**注意:关于环境设置,请参考 [README_CN.md](https://github.com/PaddlePaddle/Quantum/blob/master/README_CN.md).**" ] @@ -148,10 +149,11 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "对于具体需要分析的分子,我们需要其**几何构型** (geometry)、**基组**(basis set,例如 STO-3G 基于高斯函数)、**多重度**(multiplicity)以及**分子的净电荷数** (charge) 等多项信息来建模计算出该分子单体积分 (one-body integrations),双体积分(two-body integrations) 以及哈密顿量等信息。接下来,通过量桨的量子化学工具包将分子的哈密顿量提取出来并储存为 paddle quantum 的 `Hamiltonian` 类,方便我们下一步的操作。" + "对于具体需要分析的分子,我们需要其**几何构型** (geometry)、**基组**(basis set,例如 STO-3G 基于高斯函数)、**多重度**(multiplicity)以及**分子的净电荷数** (charge) 等多项信息来建模计算出该分子单体积分 (one-body integrations),双体积分(two-body integrations) 以及哈密顿量等信息。利用量桨量子化学模块的 `Molecule` 类可以方便地获得储存为paddle quantum 的 `Hamiltonian` 类的分子哈密顿量,方便我们下一步的操作。" ] }, { @@ -168,57 +170,50 @@ "name": "stdout", "output_type": "stream", "text": [ - "FCI energy for H2_sto-3g_singlet (2 electrons) is -1.137283834485513.\n", + "converged SCF energy = -1.11675930739643\n", "\n", "The generated h2 Hamiltonian is \n", - " -0.09706626861762556 I\n", - "-0.04530261550868938 X0, X1, Y2, Y3\n", - "0.04530261550868938 X0, Y1, Y2, X3\n", - "0.04530261550868938 Y0, X1, X2, Y3\n", - "-0.04530261550868938 Y0, Y1, X2, X3\n", - "0.1714128263940239 Z0\n", - "0.16868898168693286 Z0, Z1\n", - "0.12062523481381837 Z0, Z2\n", - "0.16592785032250773 Z0, Z3\n", - "0.17141282639402394 Z1\n", - "0.16592785032250773 Z1, Z2\n", - "0.12062523481381837 Z1, Z3\n", - "-0.2234315367466399 Z2\n", - "0.17441287610651626 Z2, Z3\n", - "-0.2234315367466399 Z3\n" + " -0.09706626816763092 I\n", + "0.17141282644776917 Z0\n", + "0.17141282644776917 Z1\n", + "-0.2234315369081346 Z2\n", + "-0.2234315369081346 Z3\n", + "0.16868898170361205 Z0, Z1\n", + "0.12062523483390414 Z0, Z2\n", + "0.1659278503377034 Z0, Z3\n", + "0.1659278503377034 Z1, Z2\n", + "0.12062523483390414 Z1, Z3\n", + "0.1744128761226159 Z2, Z3\n", + "-0.04530261550379926 X0, X1, Y2, Y3\n", + "0.04530261550379926 X0, Y1, Y2, X3\n", + "0.04530261550379926 Y0, X1, X2, Y3\n", + "-0.04530261550379926 Y0, Y1, X2, X3\n" ] } ], "source": [ - "geo = qchem.geometry(structure=[['H', [-0., 0., 0.0]], ['H', [-0., 0., 0.74]]])\n", - "# geo = qchem.geometry(file='h2.xyz')\n", - "\n", - "# 将分子信息存储在 molecule 里,包括单体积分(one-body integrations),双体积分(two-body integrations),分子的哈密顿量等\n", - "molecule = qchem.get_molecular_data(\n", - " geometry=geo,\n", - " basis='sto-3g',\n", - " charge=0,\n", - " multiplicity=1,\n", - " method=\"fci\",\n", - " if_save=True,\n", - " if_print=True\n", + "mol = qchem.Molecule(\n", + " geometry=[('H', [-0., 0., 0.0]), ('H', [-0., 0., 0.74])], # 分子几何结构\n", + " basis=\"sto-3g\", # 基组\n", + " multiplicity=1, # 多重度\n", + " charge=0, # 净电荷数\n", + " driver=qchem.PySCFDriver() # 量子化学积分计算引擎\n", ")\n", "# 提取哈密顿量\n", - "molecular_hamiltonian = qchem.spin_hamiltonian(molecule=molecule,\n", - " filename=None, \n", - " multiplicity=1, \n", - " mapping_method='jordan_wigner',)\n", + "molecular_hamiltonian = mol.get_molecular_hamiltonian()\n", + "\n", "# 打印结果\n", "print(\"\\nThe generated h2 Hamiltonian is \\n\", molecular_hamiltonian)" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "**注释4:** 生成这个哈密顿量的几何构型中,两个氢原子间的原子间隔(interatomic distance)为 $d = 74$ pm。\n", "\n", - "除了输入分子的几何结构外,我们还支持读取分子的几何构型文件 (`.xyz` 文件),关于量子化学工具包更多的用法请参考[哈密顿量的构造](./BuildingMolecule_CN.ipynb)教程。如果你需要测试更多分子的几何构型,请移步至这个[数据库](http://smart.sns.it/molecules/index.html)。" + "关于量子化学工具包更多的用法请参考[哈密顿量的构造](./BuildingMolecule_CN.ipynb)教程。如果你需要测试更多分子的几何构型,请移步至这个[数据库](http://smart.sns.it/molecules/index.html)。" ] }, { @@ -349,23 +344,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "iter: 20 loss: -1.0700\n", - "iter: 20 Ground state energy: -1.0700 Ha\n", - "iter: 40 loss: -1.1309\n", - "iter: 40 Ground state energy: -1.1309 Ha\n", - "iter: 60 loss: -1.1362\n", - "iter: 60 Ground state energy: -1.1362 Ha\n", + "iter: 20 loss: -1.0784\n", + "iter: 20 Ground state energy: -1.0784 Ha\n", + "iter: 40 loss: -1.1300\n", + "iter: 40 Ground state energy: -1.1300 Ha\n", + "iter: 60 loss: -1.1366\n", + "iter: 60 Ground state energy: -1.1366 Ha\n", "iter: 80 loss: -1.1372\n", "iter: 80 Ground state energy: -1.1372 Ha\n", "\n", "训练后的电路:\n", - "--Ry(7.856)----*--------------x----Ry(4.698)----*--------------x----Ry(6.277)--\n", + "--Ry(1.589)----*--------------x----Ry(4.701)----*--------------x----Ry(3.124)--\n", " | | | | \n", - "--Ry(1.548)----x----*---------|----Ry(-1.56)----x----*---------|----Ry(5.041)--\n", + "--Ry(4.703)----x----*---------|----Ry(1.573)----x----*---------|----Ry(1.524)--\n", " | | | | \n", - "--Ry(3.441)---------x----*----|----Ry(4.474)---------x----*----|----Ry(1.745)--\n", + "--Ry(3.096)---------x----*----|----Ry(4.493)---------x----*----|----Ry(4.904)--\n", " | | | | \n", - "--Ry(-0.17)--------------x----*----Ry(1.646)--------------x----*----Ry(3.152)--\n", + "--Ry(3.340)--------------x----*----Ry(1.555)--------------x----*----Ry(3.157)--\n", " \n" ] } @@ -437,14 +432,12 @@ "outputs": [ { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ - "
" + "
" ] }, - "metadata": { - "needs_background": "light" - }, + "metadata": {}, "output_type": "display_data" } ], @@ -520,7 +513,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "modellib", "language": "python", "name": "python3" }, @@ -534,7 +527,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.13" + "version": "3.7.15" }, "toc": { "base_numbering": 1, @@ -548,6 +541,11 @@ "toc_position": {}, "toc_section_display": true, "toc_window_display": false + }, + "vscode": { + "interpreter": { + "hash": "8f24120f890011f53feb4ed62c47961d8565ec1de8b7cb23548c15bd6da8f2d2" + } } }, "nbformat": 4, diff --git a/tutorials/quantum_simulation/VQE_EN.ipynb b/tutorials/quantum_simulation/VQE_EN.ipynb index 9d20b85..36b263d 100644 --- a/tutorials/quantum_simulation/VQE_EN.ipynb +++ b/tutorials/quantum_simulation/VQE_EN.ipynb @@ -95,6 +95,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -102,7 +103,7 @@ "\n", "### Building electronic Hamiltonian\n", "\n", - "First of all, let us import the necessary libraries and packages.`qchem` module in Paddle Quantum is developed basing on `psi4` and `openfermion`, so you need to install these two packages before executing the following codes. We strongly encourage you to read the tutorial [Building molecular Hamiltonian](./BuildingMolecule_EN.ipynb) first, which introduces how to utilize our quantum chemistry toolkit `qchem`.\n", + "First of all, let us import the necessary libraries and packages. *qchem* module in Paddle Quantum is developed on top of *openfermion*, and currently, its quantum chemistry backend is *PySCF* so you need to install these two packages before executing the following codes (NOTE: PySCF only support Mac and Linux platform, we are adding support for more quantum chemistry backends, and will improve our support for Windows in our next release). We strongly encourage you to read the tutorial [Building molecular Hamiltonian](./BuildingMolecule_EN.ipynb) first, which introduces how to utilize our quantum chemistry toolkit `qchem`.\n", "\n", "**Note: As to the environment setting, please refer to [README.md](https://github.com/PaddlePaddle/Quantum/blob/master/README.md).**" ] @@ -140,10 +141,11 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "To analyze specific molecules, we need several key information such as **geometry**, **basis set** (such as STO-3G), **multiplicity**, and **charge** to model the molecule and achieve more information about the molecule, such as one-body integrations, two-body integrations, and others. Next, through our built-in quantum chemistry toolkit, we could set a `Hamiltonian` class to carry molecule Hamiltonian information, which may simplify the following calculations." + "To analyze specific molecules, we need several key information such as **geometry**, **basis set** (such as STO-3G), **multiplicity**, and **charge** to model the molecule and to calculate its one-body integral, two-body integral and the Hamiltonian. We can use `Molecule` class provided by *qchem* in Paddle Quantum to easily obtain molecular Hamiltonian stored as Paddle Quantum's `Hamiltonian` object. This will facilitates our further operations." ] }, { @@ -160,47 +162,39 @@ "name": "stdout", "output_type": "stream", "text": [ - "FCI energy for H2_sto-3g_singlet (2 electrons) is -1.137283834485513.\n", + "converged SCF energy = -1.11675930739643\n", "\n", "The generated h2 Hamiltonian is \n", - " -0.09706626861762556 I\n", - "-0.04530261550868938 X0, X1, Y2, Y3\n", - "0.04530261550868938 X0, Y1, Y2, X3\n", - "0.04530261550868938 Y0, X1, X2, Y3\n", - "-0.04530261550868938 Y0, Y1, X2, X3\n", - "0.1714128263940239 Z0\n", - "0.16868898168693286 Z0, Z1\n", - "0.12062523481381837 Z0, Z2\n", - "0.16592785032250773 Z0, Z3\n", - "0.17141282639402394 Z1\n", - "0.16592785032250773 Z1, Z2\n", - "0.12062523481381837 Z1, Z3\n", - "-0.2234315367466399 Z2\n", - "0.17441287610651626 Z2, Z3\n", - "-0.2234315367466399 Z3\n" + " -0.09706626816763092 I\n", + "0.17141282644776917 Z0\n", + "0.17141282644776917 Z1\n", + "-0.2234315369081346 Z2\n", + "-0.2234315369081346 Z3\n", + "0.16868898170361205 Z0, Z1\n", + "0.12062523483390414 Z0, Z2\n", + "0.1659278503377034 Z0, Z3\n", + "0.1659278503377034 Z1, Z2\n", + "0.12062523483390414 Z1, Z3\n", + "0.1744128761226159 Z2, Z3\n", + "-0.04530261550379926 X0, X1, Y2, Y3\n", + "0.04530261550379926 X0, Y1, Y2, X3\n", + "0.04530261550379926 Y0, X1, X2, Y3\n", + "-0.04530261550379926 Y0, Y1, X2, X3\n" ] } ], "source": [ - "geo = qchem.geometry(structure=[['H', [-0., 0., 0.0]], ['H', [-0., 0., 0.74]]])\n", - "# geo = qchem.geometry(file='h2.xyz')\n", - "\n", - "# Save molecule information in to variable molecule, including one-body integrations, one-body integrations, molecular and Hamiltonian\n", - "molecule = qchem.get_molecular_data(\n", - " geometry=geo,\n", - " basis='sto-3g',\n", - " charge=0,\n", - " multiplicity=1,\n", - " method=\"fci\",\n", - " if_save=True,\n", - " if_print=True\n", + "mol = qchem.Molecule(\n", + " geometry=[('H', [-0., 0., 0.0]), ('H', [-0., 0., 0.74])], # Molecular geometry\n", + " basis=\"sto-3g\", # Basis set\n", + " multiplicity=1, # Spin multiplicity\n", + " charge=0, # Total charge\n", + " driver=qchem.PySCFDriver() # Quantum chemistry engine\n", ")\n", - "# Recall Hamiltonian\n", - "molecular_hamiltonian = qchem.spin_hamiltonian(molecule=molecule,\n", - " filename=None, \n", - " multiplicity=1, \n", - " mapping_method='jordan_wigner',)\n", - "# Print results\n", + "# extract Hamiltonian\n", + "molecular_hamiltonian = mol.get_molecular_hamiltonian()\n", + "\n", + "# print result\n", "print(\"\\nThe generated h2 Hamiltonian is \\n\", molecular_hamiltonian)" ] }, @@ -356,23 +350,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "iter: 20 loss: -1.0628\n", - "iter: 20 Ground state energy: -1.0628 Ha\n", - "iter: 40 loss: -1.1323\n", - "iter: 40 Ground state energy: -1.1323 Ha\n", - "iter: 60 loss: -1.1361\n", - "iter: 60 Ground state energy: -1.1361 Ha\n", + "iter: 20 loss: -1.0769\n", + "iter: 20 Ground state energy: -1.0769 Ha\n", + "iter: 40 loss: -1.1309\n", + "iter: 40 Ground state energy: -1.1309 Ha\n", + "iter: 60 loss: -1.1365\n", + "iter: 60 Ground state energy: -1.1365 Ha\n", "iter: 80 loss: -1.1372\n", "iter: 80 Ground state energy: -1.1372 Ha\n", "\n", "Circuit after training:\n", - "--Ry(1.603)----*--------------x----Ry(4.714)----*--------------x----Ry(3.110)--\n", + "--Ry(4.710)----*--------------x----Ry(1.564)----*--------------x----Ry(6.274)--\n", " | | | | \n", - "--Ry(1.548)----x----*---------|----Ry(1.566)----x----*---------|----Ry(-1.65)--\n", + "--Ry(4.674)----x----*---------|----Ry(4.707)----x----*---------|----Ry(3.918)--\n", " | | | | \n", - "--Ry(-0.07)---------x----*----|----Ry(4.486)---------x----*----|----Ry(1.732)--\n", + "--Ry(2.376)---------x----*----|----Ry(5.028)---------x----*----|----Ry(4.713)--\n", " | | | | \n", - "--Ry(0.148)--------------x----*----Ry(7.876)--------------x----*----Ry(0.031)--\n", + "--Ry(-0.00)--------------x----*----Ry(4.937)--------------x----*----Ry(0.044)--\n", " \n" ] } @@ -434,7 +428,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 8, "metadata": { "ExecuteTime": { "end_time": "2021-04-30T09:15:18.096944Z", @@ -444,14 +438,12 @@ "outputs": [ { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ - "
" + "
" ] }, - "metadata": { - "needs_background": "light" - }, + "metadata": {}, "output_type": "display_data" } ], @@ -525,7 +517,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "modellib", "language": "python", "name": "python3" }, @@ -539,7 +531,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.13" + "version": "3.7.15" }, "toc": { "base_numbering": 1, @@ -558,6 +550,11 @@ }, "toc_section_display": true, "toc_window_display": false + }, + "vscode": { + "interpreter": { + "hash": "8f24120f890011f53feb4ed62c47961d8565ec1de8b7cb23548c15bd6da8f2d2" + } } }, "nbformat": 4, diff --git a/tutorials/quantum_simulation/output/summary_data.npz b/tutorials/quantum_simulation/output/summary_data.npz index 1de85fd..88a36f9 100644 Binary files a/tutorials/quantum_simulation/output/summary_data.npz and b/tutorials/quantum_simulation/output/summary_data.npz differ