diff --git a/README.md b/README.md index 13c90d0543398efb1c3d59b95b7b4bfdf1fc7325..194364183dce53f1073576c7b361da85b29f3d3e 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 fe241cdbc6d1bde8823658d31254cda819791898..69c1ff0eb5685397cf204f56443a9e85b24ab988 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 0000000000000000000000000000000000000000..0787c795aaf21d14d16f5cd3cd36819997fe9d60 --- /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 0000000000000000000000000000000000000000..acb4eccf8c49acfe1c88b4d587b45dc258c5ae51 --- /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 0000000000000000000000000000000000000000..3c0ac0d2947d077e162e3967713c84145ec77368 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 0000000000000000000000000000000000000000..b6e1e9dba50d49d0cc1c3b5942fb43c76d821c98 --- /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 0000000000000000000000000000000000000000..70b8e4ee287e0632caf594dc4dea93d4b3cae09a --- /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 0000000000000000000000000000000000000000..b37d5ae6b1abeb6141c16e31c85653d4ca787917 --- /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 0000000000000000000000000000000000000000..782959249c84bc2f21da610b0c047b8115ca338d 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 0000000000000000000000000000000000000000..074ef8432aed0845f664a96077819e7f4bca5bfe --- /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 0000000000000000000000000000000000000000..6a1b242c11896c143cdc941821649a6f6e97eae0 --- /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 0000000000000000000000000000000000000000..59840f7a870866e3f1877cc6c018607708ddb0bd 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 0000000000000000000000000000000000000000..f692da72127e37f33611d2a9aa3dcfd3e3a6d678 --- /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 0000000000000000000000000000000000000000..586462f3c137cef88f4401780fef49d431516854 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 0000000000000000000000000000000000000000..e976048d48e19d0746298596f90ee8ab0368d33f 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 0000000000000000000000000000000000000000..60502a87048b6aa533e84235f8e41212e5178589 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 0000000000000000000000000000000000000000..16d13c10beccd8dca64e15eb2b8f38118a5b9435 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 0000000000000000000000000000000000000000..2472d64dd6b4634442568fe3d17d61c47d9c2ff0 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 0000000000000000000000000000000000000000..749c2d2ec6189105e11bf62db1ad9efb34e221dc 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 0000000000000000000000000000000000000000..3f6f133bc53928761a415f2bd8306a70302ae2d1 --- /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 0000000000000000000000000000000000000000..e4055cbd28de00d6fa65e9f630e09bff0a71041f --- /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 0000000000000000000000000000000000000000..22c4473047f7f9a05484dda196688094f9c2ff84 --- /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 0000000000000000000000000000000000000000..16f22bacd3bf2d261b654a8c8fd2751181028346 --- /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 0000000000000000000000000000000000000000..56b26cf2b56aa3e046c81f74f8f3694a877f8f11 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 0000000000000000000000000000000000000000..303d3595e6d98a13a38813f339fd0a06c128d18a --- /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 0000000000000000000000000000000000000000..518765140c556fbf2552829c297461709bf88c5b --- /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 0000000000000000000000000000000000000000..6e3b6257129fefa6570d891568355aecf9f985fd --- /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 0000000000000000000000000000000000000000..04fa61afceed42620c0a6a23c304c9197c358585 --- /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 0000000000000000000000000000000000000000..f1d5cb2f0780ca10e6eff7eba32a7ef453365076 --- /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": "iVBORw0KGgoAAAANSUhEUgAAArIAAAEiCAYAAAAF9zFeAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAABgLklEQVR4nO3dd1gUxx8G8PeOcvQmVURQUezYjS32YA/GHo2gxhJFY9DkhzF2DYlRY4qxJNYkGo0aYzeKJVGJxt4QFbvSFOnS7ub3B97J0bxD4Dh4P0/uMTs7u/u9BWa/Nzc7KxFCCBARERER6RmprgMgIiIiIioKJrJEREREpJeYyBIRERGRXmIiS0RERER6iYksEREREeklJrJEREREpJeYyBIRERGRXmIiS0RERER6iYksEREREeklJrJUqiQSCWbPnq31dkePHoVEIsHRo0eLPSYiotxmz54NiURSosdgu1Yx3L17FxKJBOvWrSvW/Xp4eMDf379Y96mPmMiWUevWrYNEIlG9TExMUKtWLQQEBCA6Olrr/f3www/F/kdUkL179xYpWSUiKgn5taeVK1eGj48Pvv32WyQlJek6RCoGpXmdKy0nT57E7NmzER8fr+tQyixDXQdAhZs7dy6qVauGtLQ0HD9+HMuXL8fevXtx5coVmJmZabyfH374Afb29qXy6W3v3r1YtmxZvsns8+fPYWjIXzsiKn3K9jQzMxNRUVE4evQoJk+ejCVLlmDnzp1o2LChqu5nn32GoKAgHUZL2irN65w23N3d8fz5cxgZGWm97cmTJzFnzhz4+/vDxsZGbV14eDikUvZHMqMo47p3745mzZoBAN5//31UqlQJS5YswZ9//okhQ4boODrtmZiY6DqEUpWSkgJzc3Ndh1Fsytv7oYolZ3sKANOmTcPhw4fRq1cv9OnTB2FhYTA1NQUAGBoa8kM3vZasrCwoFAoYGxuXyLVPJpMV+z71EVN5PdOpUycAwJ07dwBk/6HMmzcPNWrUgEwmg4eHBz799FOkp6ertvHw8MDVq1dx7Ngx1VdrHTp0UK2Pj4/H5MmT4ebmBplMBk9PT3z55ZdQKBSqOsoxPosWLcKqVatUx2vevDn+++8/VT1/f38sW7YMANS+ylPKPUb23r17GD9+PLy8vGBqaopKlSphwIABuHv3bpHP0aNHjzBy5Eg4OTlBJpOhXr16WLNmjVod5di0LVu2YMGCBahSpQpMTEzQuXNn3Lp1K88+T506hW7dusHa2hpmZmZo3749Tpw4oVZHOabu2rVrePfdd2Fra4u2bdsCABQKBWbPno3KlSvDzMwMHTt2xLVr19TGON2+fRsSiQRff/11nuOfPHkSEokEmzZtKvS9p6WlYfbs2ahVqxZMTEzg4uKCd955BxEREWrvO/eYvPzGcPn7+8PCwgIRERHo0aMHLC0tMXToUAQEBMDCwgKpqal5jj9kyBA4OztDLperyvbt24d27drB3NwclpaW6NmzJ65evVro+yAqLZ06dcKMGTNw7949/PLLL6ry/MbISiQSBAQE4Ndff4WXlxdMTEzQtGlT/P3333n2e/78eXTv3h1WVlawsLBA586d8e+//2oUkybtTX5ytmtz5syBq6srLC0t0b9/fyQkJCA9PR2TJ0+Go6MjLCwsMGLECLVrBaDZNQXIvq706tULR48eRbNmzWBqaooGDRqo2pbt27ejQYMGqnN0/vz5PPFev34d/fv3h52dHUxMTNCsWTPs3LlTrY5yWMiJEycQGBgIBwcHmJubo2/fvoiNjVWLp7DrXE6ZmZmws7PDiBEj8qxLTEyEiYkJpk6dCgDIyMjAzJkz0bRpU1hbW8Pc3Bzt2rXDkSNH1LbLeY1cunSp6vxdu3Yt3/b10qVL8Pf3R/Xq1WFiYgJnZ2eMHDkST58+VdWZPXs2Pv74YwBAtWrVVO9LeX3Mb4zs7du3MWDAANjZ2cHMzAxvvPEG9uzZo1ZH2+tfWcePm3pGmZBUqlQJQHYv7fr169G/f39MmTIFp06dQnBwMMLCwvDHH38AAJYuXYqJEyfCwsIC06dPBwA4OTkBAFJTU9G+fXs8evQIY8eORdWqVXHy5ElMmzYNkZGRWLp0qdrxN27ciKSkJIwdOxYSiQQLFy7EO++8g9u3b8PIyAhjx47F48ePcfDgQfz888+vfD///fcfTp48icGDB6NKlSq4e/culi9fjg4dOuDatWtaDZ8AgOjoaLzxxhuqC46DgwP27duHUaNGITExEZMnT1ar/8UXX0AqlWLq1KlISEjAwoULMXToUJw6dUpV5/Dhw+jevTuaNm2KWbNmQSqVYu3atejUqRP++ecftGjRQm2fAwYMQM2aNfH5559DCAEgu+dn4cKF6N27N3x8fHDx4kX4+PggLS1NtV316tXRpk0b/Prrr/joo4/U9vnrr7/C0tISb7/9doHvXS6Xo1evXggJCcHgwYPx4YcfIikpCQcPHsSVK1dQo0YNrc4lkH1R8/HxQdu2bbFo0SKYmZnBw8MDy5Ytw549ezBgwABV3dTUVOzatQv+/v4wMDAAAPz888/w8/ODj48PvvzyS6SmpmL58uVo27Ytzp8/Dw8PD61jIipu7733Hj799FP89ddfGD16dKF1jx07hs2bN2PSpEmQyWT44Ycf0K1bN5w+fRr169cHAFy9ehXt2rWDlZUVPvnkExgZGWHlypXo0KEDjh07hpYtWxa4f23bm/wEBwfD1NQUQUFBuHXrFr777jsYGRlBKpXi2bNnmD17Nv7991+sW7cO1apVw8yZM1XbanJNUbp16xbeffddjB07FsOGDcOiRYvQu3dvrFixAp9++inGjx+vimfgwIFqX4VfvXoVbdq0gaurK4KCgmBubo4tW7bA19cX27ZtQ9++fdWONXHiRNja2mLWrFm4e/culi5dioCAAGzevBlA4de53IyMjNC3b19s374dK1euhLGxsWrdjh07kJ6ejsGDBwPITmx/+uknDBkyBKNHj0ZSUhJWr14NHx8fnD59Go0aNVLb99q1a5GWloYxY8ZAJpPBzs5OrVNI6eDBg7h9+zZGjBgBZ2dnXL16FatWrcLVq1fx77//QiKR4J133sGNGzewadMmfP3117C3twcAODg45Pu+oqOj0bp1a6SmpmLSpEmoVKkS1q9fjz59+mDr1q15zqkm1z+9IKhMWrt2rQAgDh06JGJjY8WDBw/Eb7/9JipVqiRMTU3Fw4cPxYULFwQA8f7776ttO3XqVAFAHD58WFVWr1490b59+zzHmTdvnjA3Nxc3btxQKw8KChIGBgbi/v37Qggh7ty5IwCISpUqibi4OFW9P//8UwAQu3btUpVNmDBBFPSrBUDMmjVLtZyampqnTmhoqAAgNmzYoCo7cuSIACCOHDmS736VRo0aJVxcXMSTJ0/UygcPHiysra1Vx1Pur06dOiI9PV1V75tvvhEAxOXLl4UQQigUClGzZk3h4+MjFAqFWtzVqlUTXbt2VZXNmjVLABBDhgxRO3ZUVJQwNDQUvr6+auWzZ88WAISfn5+qbOXKlQKACAsLU5VlZGQIe3t7tXr5WbNmjQAglixZkmedMvaCzqPy57t27VpVmZ+fnwAggoKC8uzL1dVV9OvXT618y5YtAoD4+++/hRBCJCUlCRsbGzF69Og858Pa2jpPOVFJUban//33X4F1rK2tRePGjVXLyr/nnAAIAOLMmTOqsnv37gkTExPRt29fVZmvr68wNjYWERERqrLHjx8LS0tL8eabb6rKcv89atPe5Ee5v/r164uMjAxV+ZAhQ4REIhHdu3dXq9+qVSvh7u6uWtbmmuLu7i4AiJMnT6rKDhw4IAAIU1NTce/ePVW5sl3L2e507txZNGjQQKSlpanKFAqFaN26tahZs6aqTPmz69Kli9o5+eijj4SBgYGIj49XlRV0ncuPMtac1y4hhOjRo4eoXr26ajkrK0vtGiGEEM+ePRNOTk5i5MiRqjJlG2plZSViYmLU6ufXvuZ37du0aZNaGyqEEF999ZUAIO7cuZOnvru7u9p1YfLkyQKA+Oeff1RlSUlJolq1asLDw0PI5XIhhObXP33BoQVlXJcuXeDg4AA3NzcMHjwYFhYW+OOPP+Dq6oq9e/cCAAIDA9W2mTJlCgDk+TohP7///jvatWsHW1tbPHnyRPXq0qUL5HJ5nq/MBg0aBFtbW9Vyu3btAGR/nVEUyvFoQPbXPU+fPoWnpydsbGxw7tw5rfYlhMC2bdvQu3dvCCHU3o+Pjw8SEhLy7HPEiBFqn8Zzv58LFy7g5s2bePfdd/H06VPV/lJSUtC5c2f8/fffeT5tjxs3Tm05JCQEWVlZqt4JpYkTJ+Z5DwMHDoSJiQl+/fVXVdmBAwfw5MkTDBs2rND3v23bNtjb2+e739eZRuiDDz7Is68BAwZg7969SE5OVpVv3rwZrq6uquEUBw8eRHx8PIYMGaL2szAwMEDLli3zfDVHpEsWFhYazV7QqlUrNG3aVLVctWpVvP322zhw4ADkcjnkcjn++usv+Pr6onr16qp6Li4uePfdd3H8+HEkJibmu++itDf5GT58uNqNRS1btoQQAiNHjlSr17JlSzx48ABZWVkAoPU1pW7dumjVqpXa/oDs4RpVq1bNU65sV+Pi4nD48GEMHDgQSUlJqvf59OlT+Pj44ObNm3j06JHascaMGaPWjrVr1w5yuRz37t175fnIT6dOnWBvb6/q0QWAZ8+e4eDBgxg0aJCqzMDAQHWNUCgUiIuLQ1ZWFpo1a5bvNapfv34F9pjmlPPal5aWhidPnuCNN94AAK2vfUp79+5FixYtVG0wkP17PWbMGNy9exfXrl1Tq/+q65++4NCCMm7ZsmWoVasWDA0N4eTkBC8vL9VXM/fu3YNUKoWnp6faNs7OzrCxsdHoD/zmzZu4dOlSgX94MTExass5GycAqqT22bNnGr+nnJ4/f47g4GCsXbsWjx49Un0VDwAJCQla7Ss2Nhbx8fFYtWoVVq1alW8dbd/PzZs3AQB+fn4FHjchIUEtua9WrZraeuXPIffPyc7OTm07ALCxsUHv3r2xceNGzJs3D0D2sAJXV1fV+OiCREREwMvLq1hvUDE0NESVKlXylA8aNAhLly7Fzp078e677yI5ORl79+5VDTkBXp67guK2srIqtjiJXldycjIcHR1fWa9mzZp5ymrVqoXU1FTVmM3U1FR4eXnlqVenTh0oFAo8ePAA9erVy7O+KO1NfnK3a9bW1gAANze3POUKhQIJCQmoVKmS1tcUbY4DvGxXb926BSEEZsyYgRkzZuT7HmJiYuDq6lrgsV732mNoaIh+/fph48aNSE9Ph0wmw/bt25GZmamWyALA+vXrsXjxYly/fh2ZmZmq8txtfUFl+YmLi8OcOXPw22+/5bkuaXvtU7p3716+w1bq1KmjWq8c/gIU/znVFSayZVyLFi3U7rLNz+v0tikUCnTt2hWffPJJvutr1aqltqwc+5hbzgRUGxMnTsTatWsxefJktGrVCtbW1pBIJBg8eLBGPQ85KesPGzaswAtBzul1gFe/H+U+v/rqqzxjoZQsLCzUlnN+0i6K4cOH4/fff8fJkyfRoEED7Ny5E+PHjy+WaVYK+l3JeXNWTjKZLN/jvvHGG/Dw8MCWLVvw7rvvYteuXXj+/LnaBUB57n7++Wc4Ozvn2QfvCKey4uHDh0hISMiTwJW2orQ3+SmoXdO0/db0mlLU4yjf59SpU+Hj45Nv3dw/i+K+9gDA4MGDsXLlSuzbtw++vr7YsmULateuDW9vb1WdX375Bf7+/vD19cXHH38MR0dHGBgYIDg4WHXPSk6atv8DBw7EyZMn8fHHH6NRo0awsLCAQqFAt27dtL72FVVJnFNd4JVEj7m7u0OhUODmzZuqT1xA9oDv+Ph4uLu7q8oKaphq1KiB5ORkdOnSpdji0iax3rp1K/z8/LB48WJVWVpaWpEmf3ZwcIClpSXkcnmxvR/lDVJWVlZF3qfy53Dr1i21T+tPnz7N95Nvt27d4ODggF9//RUtW7ZEamoq3nvvPY1iPXXqFDIzMwucr1D5iTv3+S3K13MDBw7EN998g8TERGzevBkeHh6qr8aU8QCAo6Njsf5+ERU35Y2pBSVVOSl7TXO6ceMGzMzMVN9smZmZITw8PE+969evQyqV5umxVCqO9uZ1aHNNeR3KIRdGRkY6u/YAwJtvvgkXFxds3rwZbdu2xeHDh1U3iilt3boV1atXx/bt29X2P2vWrCLH+ezZM4SEhGDOnDlqN9rl97ulzXtyd3cv8PdOub484hhZPdajRw8AyDOzwJIlSwAAPXv2VJWZm5vnmxwOHDgQoaGhOHDgQJ518fHxqrFT2lDOM6pJMmpgYJDn0993331XYA/hq/bVr18/bNu2DVeuXMmzPudULZpq2rQpatSogUWLFqmNB9Vmn507d4ahoSGWL1+uVv7999/nW9/Q0BBDhgzBli1bsG7dOjRo0CBPT3J++vXrhydPnuS7X+U5dnd3h4GBQZ6xzz/88MMr95/boEGDkJ6ejvXr12P//v0YOHCg2nofHx9YWVnh888/V/s6TqkoPw+i4nb48GHMmzcP1apVw9ChQ19ZPzQ0VG0M44MHD/Dnn3/irbfegoGBAQwMDPDWW2/hzz//VJtGMDo6Ghs3bkTbtm0LHFZTHO3N69DmmvI6HB0d0aFDB6xcuRKRkZF51hf1fRZ0nSuIVCpF//79sWvXLvz888/IysrKM6xA2WuZ8zp16tQphIaGFinGgvYJ5D3vgHbX0x49euD06dNqsaWkpGDVqlXw8PBA3bp1ixxzWcYeWT3m7e0NPz8/rFq1CvHx8Wjfvj1Onz6N9evXw9fXFx07dlTVbdq0KZYvX4758+fD09MTjo6O6NSpEz7++GPs3LkTvXr1gr+/P5o2bYqUlBRcvnwZW7duxd27d1VTfmhKeSPEpEmT4OPjAwMDA9VUJrn16tULP//8M6ytrVG3bl2Ehobi0KFDqunFtPXFF1/gyJEjaNmyJUaPHo26desiLi4O586dw6FDhxAXF6fV/qRSKX766Sd0794d9erVw4gRI+Dq6opHjx7hyJEjsLKywq5duwrdh5OTEz788EMsXrwYffr0Qbdu3XDx4kXs27cP9vb2+X7iHj58OL799lscOXIEX375pUaxDh8+HBs2bEBgYCBOnz6Ndu3aISUlBYcOHcL48ePx9ttvw9raGgMGDMB3330HiUSCGjVqYPfu3XnGaGmiSZMm8PT0xPTp05Genp7nAmBlZYXly5fjvffeQ5MmTTB48GA4ODjg/v372LNnD9q0aVNgMk9UEvbt24fr168jKysL0dHROHz4MA4ePAh3d3fs3LlTo0nr69evDx8fH7XptwBgzpw5qjrz58/HwYMH0bZtW4wfPx6GhoZYuXIl0tPTsXDhwgL3XRztzevQ5pryupYtW4a2bduiQYMGGD16NKpXr47o6GiEhobi4cOHuHjxotb7LOg6V5hBgwbhu+++w6xZs9CgQQO1nmgg+xq1fft29O3bFz179sSdO3ewYsUK1K1bN98PG5qwsrLCm2++iYULFyIzMxOurq7466+/VPPD535PADB9+nQMHjwYRkZG6N27d74PpgkKCsKmTZvQvXt3TJo0CXZ2dli/fj3u3LmDbdu2ld+ngOlgpgTSgCbTxQghRGZmppgzZ46oVq2aMDIyEm5ubmLatGlqU5oIkT3lUc+ePYWlpaUAoDZFSVJSkpg2bZrw9PQUxsbGwt7eXrRu3VosWrRINYWLcvqQr776Kk8MyDWlVlZWlpg4caJwcHAQEolEbQqb3HWfPXsmRowYIezt7YWFhYXw8fER169fzzOtiKbTbwkhRHR0tJgwYYJwc3MTRkZGwtnZWXTu3FmsWrUqz/5+//13tW3zmyZFCCHOnz8v3nnnHVGpUiUhk8mEu7u7GDhwoAgJCVHVUU7XExsbmyemrKwsMWPGDOHs7CxMTU1Fp06dRFhYmKhUqZIYN25cvu+jXr16QiqViocPH77yPSulpqaK6dOnq34fnJ2dRf/+/dWmAYqNjRX9+vUTZmZmwtbWVowdO1ZcuXIl3+m3zM3NCz3e9OnTBQDh6elZYJ0jR44IHx8fYW1tLUxMTESNGjWEv7+/2hRGRCVJ2Z4qX8bGxsLZ2Vl07dpVfPPNNyIxMTHPNgVNvzVhwgTxyy+/iJo1awqZTCYaN26cb7t07tw54ePjIywsLISZmZno2LGj2lRVQhTcrmnS3uSnoHatoOtJfm2WptcUd3d30bNnzzwxKM9RTgVdPyIiIsTw4cOFs7OzMDIyEq6urqJXr15i69atr4w9v3NX2HWuIAqFQri5uQkAYv78+fmu//zzz4W7u7vq5717927h5+enNnVZYdfI/K4rDx8+FH379hU2NjbC2tpaDBgwQDx+/DjPNVKI7GkyXV1dhVQqVZuKK/d1Uojsc9q/f39hY2MjTExMRIsWLcTu3bvzPXeaXv/KOokQejaql6iciI+Ph62tLebPn59nXBYANG7cGHZ2dggJCdFBdESUm0QiwYQJE/hNAlEZUk77mYnKlufPn+cpU46Hyu8ximfOnMGFCxcwfPjwEo6MiIhIf3GMLFEp2Lx5M9atW4cePXrAwsICx48fx6ZNm/DWW2+hTZs2qnpXrlzB2bNnsXjxYri4uOQZd0pEREQvMZElKgUNGzaEoaEhFi5ciMTERNUNYPPnz1ert3XrVsydOxdeXl7YtGmTRjefEBERVVQcI0tEREREeoljZImIiIhILzGRJSIiIiK9VKHGyCoUCjx+/BiWlpZaP8qOiCouIQSSkpJQuXLl8jupeAlgm0tERaFNm1uhEtnHjx8X+IxrIqJXefDgAapUqaLrMPQG21wieh2atLkVKpG1tLQEkH1iCnrWNRFRbomJiXBzc1O1IaQZtrlEVBTatLkVKpFVfrVlZWXFRpWItMavx7XDNpeIXocmbS4HexERERGRXmIiS0RERER6iYksEREREeklJrJEREREpJeYyBIRERGRXmIiW4BMuQK7Lz3GN4duQq4Qug6HiIiIiHKpUNNvaUMqkWDKlotIz1Lg7UaV4WFvruuQiIiIiCgH9sgWwEAqQXUHCwDAzZhkHUdDRERERLkxkS1ETcfsRPYWE1kiIiKiMoeJbCE8mcgSERERlVlMZAvxMpFN0nEkRERERJQbE9lC5BxaIARnLiAiIiIqS5jIFsK9kjkMpBKkZMgRmZCm63CIiIiIKAcmsoUwNpTCo5IZAI6TJSL9t2zZMnh4eMDExAQtW7bE6dOnNdrut99+g0Qiga+vb8kGSESkJSayr8AbvoioPNi8eTMCAwMxa9YsnDt3Dt7e3vDx8UFMTEyh2929exdTp05Fu3btSilSIiLNMZF9hZqOlgA4lywR6bclS5Zg9OjRGDFiBOrWrYsVK1bAzMwMa9asKXAbuVyOoUOHYs6cOahevXopRktEpBkmsq+g7JGNYCJLRHoqIyMDZ8+eRZcuXVRlUqkUXbp0QWhoaIHbzZ07F46Ojhg1alRphElEpDU+ovYVlInsTU7BRUR66smTJ5DL5XByclIrd3JywvXr1/Pd5vjx41i9ejUuXLig8XHS09ORnp6uWk5MTCxSvEREmmKP7CvUcLCARAI8S83E0+T0V29ARKTnkpKS8N577+HHH3+Evb29xtsFBwfD2tpa9XJzcyvBKImI2CP7SqbGBnC1McXDZ89xMyYZlSxkug6JiEgr9vb2MDAwQHR0tFp5dHQ0nJ2d89SPiIjA3bt30bt3b1WZQqEAABgaGiI8PBw1atTIs920adMQGBioWk5MTGQyS0Qlij2yGqjJmQuISI8ZGxujadOmCAkJUZUpFAqEhISgVatWeerXrl0bly9fxoULF1SvPn36oGPHjrhw4UKByalMJoOVlZXai4ioJLFHVgOejhY4Eh7LRJaI9FZgYCD8/PzQrFkztGjRAkuXLkVKSgpGjBgBABg+fDhcXV0RHBwMExMT1K9fX217GxsbAMhTTkSkS0xkNcC5ZIlI3w0aNAixsbGYOXMmoqKi0KhRI+zfv191A9j9+/chlfJLOiLSL0xkNeD5Yi5ZJrJEpM8CAgIQEBCQ77qjR48Wuu26deuKPyAiotfEj98aUPbIRiWmITEtU8fREBERERHARFYj1qZGcLTMnq2AD0YgIiIiKhuYyGqI42SJiIiIyhYmshriFFxEREREZQsTWQ2xR5aIiIiobGEiq6EaLxLZm0xkiYiIiMoEJrIaqvliCq4Hz1KRlinXcTRERERExERWQ/YWxrA2NYIQQEQse2WJiIiIdI2JrIYkEglv+CIiIiIqQ5jIakF5wxfnkiUiIiLSPSayWvDkDV9EREREZQYTWS1wCi4iIiKisoOJrBaUieydJynIlCt0HA0RERFRxaY3iWxwcDCaN28OS0tLODo6wtfXF+Hh4aUaQ2VrU5gZGyBLIXDvaWqpHpuIiIiI1OlNInvs2DFMmDAB//77Lw4ePIjMzEy89dZbSElJKbUYpFIJajhweAERERFRWWCo6wA0tX//frXldevWwdHREWfPnsWbb75ZanHUdLTA5UcJuBWTBMC51I5LREREROr0JpHNLSEhAQBgZ2dXYJ309HSkp6erlhMTE1/7uDV4wxcRERFRmaA3QwtyUigUmDx5Mtq0aYP69esXWC84OBjW1taql5ub22sfm1NwEREREZUNepnITpgwAVeuXMFvv/1WaL1p06YhISFB9Xrw4MFrH1v5dK+I2GQoFOK190dERERERaP10IL09HScOnUK9+7dQ2pqKhwcHNC4cWNUq1atJOLLIyAgALt378bff/+NKlWqFFpXJpNBJpMV6/Gr2pnB2ECKtEwFHsU/h5udWbHun4iIiIg0o3Eie+LECXzzzTfYtWsXMjMzYW1tDVNTU8TFxSE9PR3Vq1fHmDFjMG7cOFhaWhZ7oEIITJw4EX/88QeOHj1aaolzboYGUlSzN0d4dBJuxSQzkSUiIiLSEY2GFvTp0weDBg2Ch4cH/vrrLyQlJeHp06d4+PAhUlNTcfPmTXz22WcICQlBrVq1cPDgwWIPdMKECfjll1+wceNGWFpaIioqClFRUXj+/HmxH+tV+IQvIiIiIt3TqEe2Z8+e2LZtG4yMjPJdX716dVSvXh1+fn64du0aIiMjizVIAFi+fDkAoEOHDmrla9euhb+/f7EfrzA1VDd8JZXqcYmIiIjoJY16ZMeOHVtgEptb3bp10blz59cKKj9CiHxfpZ3EAi9v+GKPLBFpY+TIkUhKyvsBOCUlBSNHjtRBRERE+q1IsxbEx8fjp59+wrRp0xAXFwcAOHfuHB49elSswZVVOafgEoIzFxCRZtavX5/vcKjnz59jw4YNOoiIiEi/aT1rwaVLl9ClSxdYW1vj7t27GD16NOzs7LB9+3bcv3+/QjTG1ezNIZUASWlZiE1Kh6OVia5DIqIyLDExUfUtUlJSEkxMXrYZcrkce/fuhaOjow4jJCLST1onsoGBgfD398fChQvVZifo0aMH3n333WINrqwyMTJAVTsz3H2ailsxyUxkiahQNjY2kEgkkEgkqFWrVp71EokEc+bM0UFkRET6TetE9r///sPKlSvzlLu6uiIqKqpYgtIHno6WuPs0FTdjktHa017X4RBRGXbkyBEIIdCpUyds27ZN7dHaxsbGcHd3R+XKlXUYIRGRftI6kZXJZEhMTMxTfuPGDTg4OBRLUPrA09ECh8KiecMXEb1S+/btAQB37tyBm5sbpFK9fKgiEVGZo3Ui26dPH8ydOxdbtmwBkP2V2P379/G///0P/fr1K/YAyypPTsFFRFpyd3dHfHw8Tp8+jZiYGCgUCrX1w4cP11FkRET6SetugcWLFyM5ORmOjo54/vw52rdvD09PT1haWmLBggUlEWOZ9HIKrhQdR0JE+mLXrl2oWrUqunXrhoCAAHz44Yeq1+TJk0v8+MuWLYOHhwdMTEzQsmVLnD59usC6P/74I9q1awdbW1vY2tqiS5cuhdYnItIFrRNZa2trHDx4ELt27cK3336LgIAA7N27F8eOHYO5uXlJxFgmKR+K8CQ5HfGpGTqOhoj0wZQpUzBy5EgkJycjPj4ez549U72UUxmWlM2bNyMwMBCzZs3CuXPn4O3tDR8fH8TExORb/+jRoxgyZAiOHDmC0NBQuLm54a233qow0ywSkX6QiNeYCDUtLQ0ymQwSiaQ4YyoxiYmJsLa2RkJCAqysrF57f62DQ/A4IQ1bx7VCMw+7V29ARHqpuNoOc3NzXL58GdWrVy/G6DTTsmVLNG/eHN9//z0AQKFQwM3NDRMnTkRQUNArt5fL5bC1tcX333+v8RCI4m5ziahi0Kbt0LpHVqFQYN68eXB1dYWFhQXu3LkDAJgxYwZWr15dtIj1VA0+4YuItODj44MzZ86U+nEzMjJw9uxZdOnSRVUmlUrRpUsXhIaGarSP1NRUZGZmqs24QESka1rf7DV//nysX78eCxcuxOjRo1Xl9evXx9KlSzFq1KhiDbAs83S0wD83n+AmE1ki0kDPnj3x8ccf49q1a2jQoEGeR3/36dOnRI775MkTyOVyODk5qZU7OTnh+vXrGu3jf//7HypXrqyWDOeWnp6O9PR01XJ+M9wQERUnrRPZDRs2YNWqVejcuTPGjRunKvf29ta4QSwvajpmPxCCPbJEpAnlh/+5c+fmWSeRSCCXy0s7JI188cUX+O2333D06FG1p5LlFhwczAc7EFGp0npowaNHj+Dp6ZmnXKFQIDMzs1iC0heeHFpARFpQKBQFvkoyibW3t4eBgQGio6PVyqOjo+Hs7FzotosWLcIXX3yBv/76Cw0bNiy07rRp05CQkKB6PXjw4LVjJyIqjNaJbN26dfHPP//kKd+6dSsaN25cLEHpC+UUXI/inyMlPUvH0RCRPklLSyu1YxkbG6Np06YICQlRlSkUCoSEhKBVq1YFbrdw4ULMmzcP+/fvR7NmzV55HJlMBisrK7UXEVFJ0npowcyZM+Hn54dHjx5BoVBg+/btCA8Px4YNG7B79+6SiLHMsjU3RiVzYzxNycDt2BQ0qGKt65CIqAyTy+X4/PPPsWLFCkRHR+PGjRuoXr06ZsyYAQ8PjxK9xyAwMBB+fn5o1qwZWrRogaVLlyIlJQUjRowAkP0wBldXVwQHBwMAvvzyS8ycORMbN26Eh4eH6hHkFhYWsLCwKLE4iYi0oXWP7Ntvv41du3bh0KFDMDc3x8yZMxEWFoZdu3aha9euJRFjmcYnfBGRphYsWIB169Zh4cKFMDY2VpXXr18fP/30U4kee9CgQVi0aBFmzpyJRo0a4cKFC9i/f7/qBrD79+8jMjJSVX/58uXIyMhA//794eLionotWrSoROMkItKGVj2yWVlZ+PzzzzFy5EgcPHiwpGLSK56OFjh1J47jZInolXR9s2xAQAACAgLyXXf06FG15bt375Z4PEREr0urHllDQ0MsXLgQWVkcD6r0skeWiSwRFY43yxIRFS+thxZ07twZx44dK4lY9JJyCq4IJrJE9Aq8WZaIqHhpfbNX9+7dERQUhMuXL6Np06YwNzdXW19SE3qXVcoe2XtxqUjPkkNmaKDjiIiorOLNskRExUvrRHb8+PEAgCVLluRZV5Yn9C4pTlYyWMoMkZSehbtPUuHlbKnrkIiojFLeLDt37lzVzbJNmjSpsDfLEhG9Lq0TWYVCURJx6C2JRIIajha48CAet2KSmcgSUaHatWvHm2WJiIqJ1mNkN2zYoPYsbaWMjAxs2LChWILSN5yCi4i0lZycjMTERLUXERFpR+tEdsSIEUhISMhTnpSUpJpYu6KpyUfVEpEG7ty5g549e8Lc3BzW1tawtbWFra0tbGxsYGtrq+vwiIj0jtZDC4QQkEgkecofPnwIa+uK+WQrTyayRKSBYcOGQQiBNWvWwMnJKd+2lIiINKdxItu4cWNIJBJIJBJ07twZhoYvN5XL5bhz5w66detWIkGWdcopuG4/SYFcIWAg5cWJiPK6ePEizp49Cy8vL12HQkRULmicyPr6+gIALly4AB8fH7VnbRsbG8PDwwP9+vUr9gD1gautKWSGUqRnKfAgLhUe9uav3oiIKpzmzZvjwYMHTGSJiIqJxonsrFmzIJfL4eHhgbfeegsuLi4lGZdeMZBKUMPBAtciE3EzJpmJLBHl66effsK4cePw6NEj1K9fH0ZGRmrrGzZsqKPIiIj0k1ZjZA0MDDB27FiEhYWVVDx6y9MxO5G9FZOMrnWddB0OEZVBsbGxiIiIULsxViKRqO49qGjzcBMRvS6tb/aqX78+bt++jWrVqpVEPHqLU3AR0auMHDkSjRs3xqZNm3izFxFRMdA6kZ0/fz6mTp2KefPm5fuIWisrq2ILTp8op+CK4MwFRFSAe/fuYefOnfD09NR1KERE5YLWiWyPHj0AAH369FHrTajoX43lnIKroCnKiKhi69SpEy5evMhEloiomGidyB45cqQk4tB77pXMYSiVICVDjsiENFS2MdV1SERUxvTu3RsfffQRLl++jAYNGuS52atPnz46ioyISD9pnci2b9++JOLQe8aGUrhXMkNEbApuxSQzkSWiPMaNGwcAmDt3bp51FfkbLSKiotI6kVVKTU3F/fv3kZGRoVZekaeP8XS0QERsCm7GJOPNWg66DoeIyhiFQqHrEIiIyhWtE9nY2FiMGDEC+/bty3d9Re5RqOloiQNXo/moWiIiIqJSINV2g8mTJyM+Ph6nTp2Cqakp9u/fj/Xr16NmzZrYuXNnScSoN17e8MUpuIgof8eOHUPv3r3h6ekJT09P9OnTB//884+uwyIi0ktaJ7KHDx/GkiVL0KxZM0ilUri7u2PYsGFYuHAhgoODSyJGlb///hu9e/dG5cqVIZFIsGPHjhI9nrZyzlxARJTbL7/8gi5dusDMzAyTJk3CpEmTYGpqis6dO2Pjxo26Do+ISO9oncimpKTA0dERAGBra4vY2FgAQIMGDXDu3LnijS6fY3t7e2PZsmUlepyiquFgAYkEeJaaiafJ6boOh4jKmAULFmDhwoXYvHmzKpHdvHkzvvjiC8ybN0/X4RER6R2tE1kvLy+Eh4cDALy9vbFy5Uo8evQIK1asgIuLS7EHmFP37t0xf/589O3bt0SPU1SmxgaoYps9W8FN9soSUS63b99G796985T36dMHd+7c0UFERET6TetE9sMPP0RkZCQAYNasWdi3bx+qVq2Kb7/9Fp9//nmxB6hvPB04vICI8ufm5oaQkJA85YcOHYKbm5sOIiIi0m9az1owbNgw1f83bdoU9+7dw/Xr11G1alXY29sXa3CvKz09HenpL7/iT0xMLPFjejpa4Eh4LBNZIspjypQpmDRpEi5cuIDWrVsDAE6cOIF169bhm2++0XF0RET6p8jzyCqZmZmhSZMmxRFLsQsODsacOXNK9Zg1HS0BsEeWiPL64IMP4OzsjMWLF2PLli0AgDp16mDz5s14++23dRwdEZH+0TiRDQwM1KjekiVLihxMcZs2bZpa3ImJiSX+9V0NzlxARIXo27dvmR3nT0SkbzROZM+fP6+2fPz4cTRt2hSmpi8fxSqRSIovsmIgk8kgk8lK9ZjKKbiiEtOQmJYJKxOjV2xBRBVRcnJynid9WVlZ6SgaIiL9pHEie+TIEbVlS0tLbNy4EdWrVy/2oAqSnJyMW7duqZbv3LmDCxcuwM7ODlWrVi21OApjbWoER0sZYpLSERGTjMZVbXUdEhGVEXfu3EFAQACOHj2KtLQ0VbkQAhKJpEI/GZGIqChee4xsaTpz5gw6duyoWlYOG/Dz88O6det0FFVeno4WiElKx00mskSUw7BhwyCEwJo1a+Dk5FTmvsUiItI3epXIdujQAUIIXYfxSjUdLXAy4ikiOE6WiHK4ePEizp49Cy8vL50cf9myZfjqq68QFRUFb29vfPfdd2jRokWB9X///XfMmDEDd+/eRc2aNfHll1+iR48epRgxEVHhtJ5Hll5NOU6WD0UgopyaN2+OBw8e6OTYmzdvRmBgIGbNmoVz587B29sbPj4+iImJybf+yZMnMWTIEIwaNQrnz5+Hr68vfH19ceXKlVKOnIioYBKhYRfnpUuX1JZbt26NLVu2oEqVKmrlDRs2LL7oilliYiKsra2RkJBQojdVhEY8xZAf/0VVOzP8/UnHV29ARGVacbUdERERGDduHIYNG4b69evDyEj9ZtCSbD9btmyJ5s2b4/vvvwcAKBQKuLm5YeLEiQgKCspTf9CgQUhJScHu3btVZW+88QYaNWqEFStWaHTM0mpztSGEQKZcQEDASCqFVMrhHURljTZth8ZDCxo1agSJRKL21X6vXr0AQFXOmxWyKXtkHzxLRVqmHCZGBjqOiIjKgtjYWERERGDEiBGqstJoPzMyMnD27FlMmzZNVSaVStGlSxeEhobmu01oaGieaRd9fHywY8eOEokRAJYeuoHnGXJkygWyFIrsf+UKZCkEMuUKZOUsz7Neva5cWabI3i7zxTq5Qr3vRioBDA2kMJJKsv81kMBQKoWhgQRGBlIYqpWr1ymobva/2WUSCaBQCCgEIFcICCEgF9nLihfxKASgEAIKIV7Uya4rFy/q51NHochezrkv1boXZcrfK6kEkEACiST7900CQCrNLpNKAKjqIJ/6gFQiUf0rlUhgIM1+Gar9m/2+pbnLDbK3oWzKFEpA9T85/3lRR+Sqq75tzu3z64pUbV/QMQvddz51tNg+Z53c762OiyXGvFkjb8CvSeNEls8B15y9hTFszIwQn5qJiNhk1KtsreuQiKgMGDlyJBo3boxNmzaV6s1eT548gVwuh5OTk1q5k5MTrl+/nu82UVFR+daPiooq8Div+zTF1f/cQVJ6llbbvC6FADKyFMgAALAjhqikPEt10G0i6+7uXuwHL68kEgk8HSxw5t4z3IphIktE2e7du4edO3fC09NT16GUiNd9muJ7rdyRKVeo9ZAaGkhg9KLXs+CeU/WeUQPpy20K6jWVSJDdwytXIFPx4t98enDz9ATn6g1WbpslF8h8sa2yZzhLIaBQCBi86KmUSgADiQSSF72aUglelEtelONFeXZ9A0muOtKXvaLKZcmLbaVS9R5TZS+ogMCL/6AQ2b29CvGi30zkLRMvenJz1s8uf9HTq8juLc560WMsz9HTnfNfxYt/s+QKtd5GXdD1PeICAtn94Nk93ErK/1WWKT/YSnJXAPJsn2fbHJVz10Gu/Uq02W8+H7ZzHzPP8QqoU8XWFCVBo0T2/v37Ws3T+ujRI7i6uhY5qPLA0/FlIktEBACdOnXCxYsXSz2Rtbe3h4GBAaKjo9XKo6Oj4ezsnO82zs7OWtUHXv9pip90q61xXSIiQMNZC5o3b46xY8fiv//+K7BOQkICfvzxR9SvXx/btm0rtgD1lScfVUtEufTu3RsfffQRZs+ejW3btmHnzp1qr5JibGyMpk2bIiQkRFWmUCgQEhKCVq1a5btNq1at1OoDwMGDBwusD2Q/TdHKykrtRURUkjTqkb127RoWLFiArl27wsTEBE2bNkXlypVhYmKCZ8+e4dq1a7h69SqaNGmChQsXcp5BMJElorzGjRsHAJg7d26edSV9s2xgYCD8/PzQrFkztGjRAkuXLkVKSorqxrPhw4fD1dUVwcHBAIAPP/wQ7du3x+LFi9GzZ0/89ttvOHPmDFatWlViMRIRaUujRLZSpUpYsmQJFixYgD179uD48eO4d+8enj9/Dnt7ewwdOhQ+Pj6oX79+ScerN2o6WQIA7jxJQaZcASMDTtlLVNEpFAqdHXvQoEGIjY3FzJkzERUVhUaNGmH//v2qG7ru378PqfRlO9W6dWts3LgRn332GT799FPUrFkTO3bsYDtPRGWKxvPIlgelOaehEAL1Zh1AaoYchwLbq3poiUj/lMX5UPUBzxsRFYU2bQe7CUuIRCJBDQfl8IIkHUdDREREVP4wkS1BNTlOloiIiKjEMJEtQTVeJLI3mcgSERERFTsmsiWIPbJEREREJUfrRDYlJaUk4iiXlDd4RcQmQ6GoMPfUEVEhIiIi8Nlnn2HIkCGIiYkBAOzbtw9Xr17VcWRERPpH60TWyckJI0eOxPHjx0sinnKlqp0ZjA2kSMtU4FH8c12HQ0Q6duzYMTRo0ACnTp3C9u3bkZyc/W3NxYsXMWvWLB1HR0Skf7ROZH/55RfExcWhU6dOqFWrFr744gs8fvy4JGLTe4YGUlSzNwfA4QVEBAQFBWH+/Pk4ePAgjI2NVeWdOnXCv//+q8PIiIj0k9aJrK+vL3bs2IFHjx5h3Lhx2LhxI9zd3dGrVy9s374dWVlZJRGn3vJU3fDFKbiIKrrLly+jb9++ecodHR3x5MkTHURERKTfinyzl4ODAwIDA3Hp0iUsWbIEhw4dQv/+/VG5cmXMnDkTqampxRmn3uKjaolIycbGBpGRkXnKz58/D1dXVx1ERESk34qcyEZHR2PhwoWoW7cugoKC0L9/f4SEhGDx4sXYvn07fH19izFM/cVEloiUBg8ejP/973+IioqCRCKBQqHAiRMnMHXqVAwfPlzX4RER6R1DbTfYvn071q5diwMHDqBu3boYP348hg0bBhsbG1Wd1q1bo06dOsUZp96q6fRyLlkhBCQSiY4jIiJd+fzzzzFhwgS4ublBLpejbt26kMvlePfdd/HZZ5/pOjwiIr2jdSI7YsQIDB48GCdOnEDz5s3zrVO5cmVMnz79tYMrD6rZm0MqAZLSshCblA5HKxNdh0REOmJsbIwff/wRM2bMwJUrV5CcnIzGjRujZs2aug6NiEgvaZ3IRkZGwszMrNA6pqamnErmBZmhAarameHu01TcjElmIktEqFq1KqpWrarrMIiI9J7WiWxWVhYSExPzlEskEshkMrUpZSibp6Ml7j5Nxa2YZLTxtNd1OERUigIDAzWuu2TJkhKMhIio/NE6kbWxsSl0nGeVKlXg7++PWbNmQSrlE3CB7Bu+DoVFcwouogro/PnzGtXj+HkiIu1pnciuW7cO06dPh7+/P1q0aAEAOH36NNavX4/PPvsMsbGxWLRoEWQyGT799NNiD1gf1eTMBUQV1pEjR3QdAhFRuaV1Irt+/XosXrwYAwcOVJX17t0bDRo0wMqVKxESEoKqVatiwYIFTGRfeDkFV4qOIyGisuLhw4cAsr/FIiKiotH6u/+TJ0+icePGecobN26M0NBQAEDbtm1x//7914+unKjxIpF9kpyO+NQMHUdDRLqiUCgwd+5cWFtbw93dHe7u7rCxscG8efOgUCh0HR4Rkd7ROpF1c3PD6tWr85SvXr0abm5uAICnT5/C1tb29aMrJyxkhqhsnT1bAYcXEFVc06dPx/fff48vvvgC58+fx/nz5/H555/ju+++w4wZM3QdHhGR3tF6aMGiRYswYMAA7Nu3TzWP7JkzZ3D9+nVs3boVAPDff/9h0KBBxRupnqvhaIHHCWm4GZOMZh52ug6HiHRg/fr1+Omnn9CnTx9VWcOGDeHq6orx48djwYIFOoyOiEj/aJ3I9unTB+Hh4Vi5ciXCw8MBAN27d8eOHTvg4eEBAPjggw+KNcjyoKajJf65+YQ9skQVWFxcHGrXrp2nvHbt2oiLi9NBRERE+k2rRDYzMxPdunXDihUrEBwcXFIxlUuenLmAqMLz9vbG999/j2+//Vat/Pvvv4e3t7eOoiIi0l9aJbJGRka4dOlSScVSrtV0YiJLVNEtXLgQPXv2xKFDh9CqVSsAQGhoKB48eIC9e/fqODoiIv2j9c1ew4YNy/dmLyqcp0N2Ivso/jlS0rN0HA0R6UL79u1x48YN9O3bF/Hx8YiPj8c777yD8PBwtGvXTtfhERHpnSI9onbNmjU4dOgQmjZtCnNzc7X1fMRi/mzNjVHJ3BhPUzIQEZuMhlVsdB0SEelA5cqVeVMXEVEx0TqRvXLlCpo0aQIAuHHjhto6PmKxcJ6OFnh6Jw63YpjIElVUz549w+rVqxEWFgYAqFu3LkaMGAE7O85mQkSkLa0TWT5useg8HS1w6kUiS0QVz99//43evXvD2toazZo1AwB8++23mDt3Lnbt2oU333xTxxESEekXrRNZpVu3biEiIgJvvvkmTE1NIYRgj+wr1Hwxc8FNJrJEFdKECRMwaNAgLF++HAYGBgAAuVyO8ePHY8KECbh8+bKOIyQi0i9a3+z19OlTdO7cGbVq1UKPHj0QGRkJABg1ahSmTJlS7AHmtmzZMnh4eMDExAQtW7bE6dOnS/yYxcXT0RIAEMFElqhCunXrFqZMmaJKYgHAwMAAgYGBuHXrlg4jIyLST1onsh999BGMjIxw//59mJmZqcoHDRqE/fv3F2twuW3evBmBgYGYNWsWzp07B29vb/j4+CAmJqZEj1tclFNw3X2agvQsuY6jIaLS1qRJE9XY2JzCwsI4jywRURFoncj+9ddf+PLLL1GlShW18po1a+LevXvFFlh+lixZgtGjR2PEiBGoW7cuVqxYATMzM6xZs6ZEj1tcHC1lsJQZQiGAu09SdR0OEZWySZMm4cMPP8SiRYtw/PhxHD9+HIsWLcJHH32Ejz76CJcuXVK9ilNcXByGDh0KKysr2NjYYNSoUUhOLvibobi4OEycOBFeXl4wNTVF1apVMWnSJCQkJBRrXEREr0vrMbIpKSlqPbFKcXFxkMlkxRJUfjIyMnD27FlMmzZNVSaVStGlSxeEhobmu016ejrS09NVy4mJiSUWnyYkEglqOFrgwoN43IxJgpezpU7jIaLSNWTIEADAJ598ku86iUSiut9ALi++b22GDh2KyMhIHDx4EJmZmRgxYgTGjBmDjRs35lv/8ePHePz4MRYtWoS6devi3r17GDduHB4/foytW7cWW1xERK9L60S2Xbt22LBhA+bNmwcgOzlTKBRYuHAhOnbsWOwBKj158gRyuRxOTk5q5U5OTrh+/Xq+2wQHB2POnDklFlNR1HyRyHLmAqKK586dO6V+zLCwMOzfvx///fefaqaE7777Dj169MCiRYtQuXLlPNvUr18f27ZtUy3XqFEDCxYswLBhw5CVlQVDwyLfJ0xEVKy0bo0WLlyIzp0748yZM8jIyMAnn3yCq1evIi4uDidOnCiJGIts2rRpCAwMVC0nJibCzc1NhxFlT8EF8FG1RBWRu7t7qR8zNDQUNjY2qiQWALp06QKpVIpTp06hb9++Gu0nISEBVlZWhSaxZe1bMCIq/7ROZOvXr48bN27g+++/h6WlJZKTk/HOO+9gwoQJcHFxKYkYAQD29vYwMDBAdHS0Wnl0dDScnZ3z3UYmk5XocIeiUN7wxUSWqGLYuXMnunfvDiMjI+zcubPQun369Cn240dFRcHR0VGtzNDQEHZ2doiKitJoH0+ePMG8efMwZsyYQuuVxW/BiKh8K9L3Q9bW1pg+fXpxx1IoY2NjNG3aFCEhIfD19QUAKBQKhISEICAgoFRjeR2eDtnjYm8/SUGWXAFDA63vtyMiPeLr66tKJpVtV360HRcbFBSEL7/8stA6+c2QoK3ExET07NkTdevWxezZswutWxa/BSOi8q1IiWx8fDxOnz6NmJgYKBQKtXXDhw8vlsDyExgYCD8/PzRr1gwtWrTA0qVLkZKSghEjRpTYMYubq60pZIZSpGcp8ODZc1SzN9d1SERUgnK2kbnby9cxZcoU+Pv7F1qnevXqcHZ2zjNFYVZWFuLi4gr8NkspKSkJ3bp1g6WlJf744w8YGRkVWr8sfgtGROWb1onsrl27MHToUCQnJ8PKykrtaV4SiaREE9lBgwYhNjYWM2fORFRUFBo1aoT9+/fnuQGsLDOQSlDDwQLXIhNxKyaZiSwRFYmDgwMcHBxeWa9Vq1aIj4/H2bNn0bRpUwDA4cOHoVAo0LJlywK3S0xMhI+PD2QyGXbu3AkTE5Nii52IqLho/b32lClTMHLkSCQnJyM+Ph7Pnj1TveLi4koiRjUBAQG4d+8e0tPTcerUqUIb4rKKN3wRVTwKhQJr1qxBr169UL9+fTRo0AB9+vTBhg0bIIQosePWqVMH3bp1w+jRo3H69GmcOHECAQEBGDx4sGrGgkePHqF27dqqJyUmJibirbfeQkpKClavXo3ExERERUUhKiqqWKcFIyJ6XVr3yD569AiTJk3Kdy5Z0kzNF4nszZgkHUdCRKVBCIE+ffpg79698Pb2RoMGDSCEQFhYGPz9/bF9+3bs2LGjxI7/66+/IiAgAJ07d4ZUKkW/fv3w7bffqtZnZmYiPDwcqanZD2o5d+4cTp06BQDw9PRU29edO3fg4eFRYrESEWlD60TWx8cHZ86cQfXq1UsingpB2SMbwR5Zogph3bp1+PvvvxESEpJnvu3Dhw/D19cXGzZsKLGhWXZ2dgU+/AAAPDw81HqFO3ToUKK9xERExUXrRLZnz574+OOPce3aNTRo0CDP4P+SmD6mvMk5BZfyKT5EVH5t2rQJn376ab4PjenUqROCgoLw66+/lug9BkRE5ZHWiezo0aMBAHPnzs2zrrgfq1heuVcyh6FUgpQMOSIT0lDZxlTXIRFRCbp06RIWLlxY4Pru3burfdVPRESa0fpmL4VCUeCLSaxmjAykcK+UPcb4JocXEJV7cXFxhc6u4uTkhGfPnpViRERE5QNn49eRmo7ZD0bgzAVE5Z9cLi/00a4GBgbIysoqxYiIiMoHjYcW9OjRA5s2bYK1tTUA4IsvvsC4ceNgY2MDAHj69CnatWuHa9eulUig5Y2nowVwlYksUUUghIC/v3+BDwtIT08v5YiIiMoHjRPZAwcOqDW2n3/+OQYOHKhKZLOyshAeHl7sAZZXL2/44hRcROWdn5/fK+vwRi8iIu1pnMjmnoqFU7O8nhoOyrlkOXMBUXm3du1aXYdARFQucYysjtRwsIBEAsSnZuJpSoauwyEiIiLSOxonshKJJE+vIXsRi87U2ABVbLOn3eI4WSIiIiLtaTW0IOfNCmlpaRg3bhzMzc0B8GaFovB0sMCDuOe4FZOMN6pX0nU4RERERHpF40Q2980Kw4YNy1OHNytop6aTJY6Ex7JHloiIiKgINE5kebNC8fN0ePmoWiIiIiLSDm/20iFPJ+XMBZyCi4iIiEhbTGR1yNMxO5GNTkxHYlqmjqMhIiIi0i9MZHXIysQIjpbZN89xeAERERGRdpjI6tjLJ3wxkSUiIiLSBhNZHVPe8BXBRJaIiIhIK0xkdczTyRJA9qNqiYiIiEhzTGR1jFNwERERERUNE1kdU85c8OBZKm5GcxouIiIiIk0xkdUxewtjNKxiDSGAd5afxN83YnUdEhEREZFeYCKrYxKJBOtGtEBzD1skpWVhxLr/8PO/93QdFhEREVGZx0S2DLAzN8Yv77fEO01cIVcIzNhxBbN3XkWWXKHr0IiIiIjKLCayZYTM0ACLB3jjk25eAIB1J+/i/Q1nkMQnfhERERHli4lsGSKRSDC+gyeWD20CEyMpjobHot/yk3gQl6rr0IiIiIjKHCayZVD3Bi74fWxrOFnJcCM6Gb7LTuDsvThdh0VERERUpjCRLaMaVLHGnxPaol5lKzxNycCQVaew4/wjXYdFREREVGYwkS3DnK1N8Pu4VnirrhMy5ApM3nwBS/4Kh0IhdB0aERERkc4xkS3jzIwNsWJYU4xrXwMA8O3hW5j423mkZcp1HBkRERGRbjGR1QNSqQRB3Wvjq/4NYWQgwZ5LkRi06l/EJKbpOjQiIiIinWEiq0cGNHPDL6NawsbMCBcfxOPtZSdw9XGCrsMiojIuLi4OQ4cOhZWVFWxsbDBq1CgkJydrtK0QAt27d4dEIsGOHTtKNlAiIi0xkdUzLatXwo7xbVDdwRyRCWkYsCIUB69F6zosIirDhg4diqtXr+LgwYPYvXs3/v77b4wZM0ajbZcuXQqJRFLCERIRFQ0TWT3kYW+OPz5og7ae9kjNkGPMz2fw49+3IQRvAiMidWFhYdi/fz9++ukntGzZEm3btsV3332H3377DY8fPy502wsXLmDx4sVYs2ZNKUVLRKQdJrJ6ytrMCGtHNMe7LatCCGDB3jBM234ZGVl8rC0RvRQaGgobGxs0a9ZMVdalSxdIpVKcOnWqwO1SU1Px7rvvYtmyZXB2dtboWOnp6UhMTFR7ERGVJCayeszIQIoFvvUxs1ddSCXAb/89gN+a04hPzdB1aERURkRFRcHR0VGtzNDQEHZ2doiKiipwu48++gitW7fG22+/rfGxgoODYW1trXq5ubkVOW4iIk0Y6joATS1YsAB79uzBhQsXYGxsjPj4eF2HVCZIJBKMbFsNHvZmmLjxPEJvP0XfH05itV8zVHew0HV4RCUmU65AcloWktOzkJiWqfr/5PQsJCn/Py0LSWmZSHrx/8r1yWlZaO5hhy/7N9T12yiyoKAgfPnll4XWCQsLK9K+d+7cicOHD+P8+fNabTdt2jQEBgaqlhMTE5nMElGJ0ptENiMjAwMGDECrVq2wevVqXYdT5nSq7YRt41tj1LozuPMkBX1/OInlw5qgdQ17XYdGpCY9S65KKtUSzvTMF//mSDpfLCelZaqWldulv+Ywmso2psX0jnRjypQp8Pf3L7RO9erV4ezsjJiYGLXyrKwsxMXFFThk4PDhw4iIiICNjY1aeb9+/dCuXTscPXo03+1kMhlkMpmmb4GI6LVJhJ7dIbRu3TpMnjy5SD2yiYmJsLa2RkJCAqysrIo/uDIgNikdY34+g/P342EolWC+b30MblFV12GRnhNCID1LkW/imbMXNHt9Zt5EVfn/aVnIkBfvOG5TIwNYmBjCUmYICxNDWMgMYWliCAuZ0Yt/c5cbwtLECPYWxhp/a6HPbUdYWBjq1q2LM2fOoGnTpgCAv/76C926dcPDhw9RuXLlPNtERUXhyZMnamUNGjTAN998g969e6NatWoaHVufzxsR6Y42bYfe9MiSZhwsZdg0+g18svUSdl58jKDtlxERm4yg7nVgIOUUOhVVepYc8amZiEvJwLPUDCQ+Vyak2T2dSbl6O1U9ozmS0kx58X7mNTc2UCWYFiZG2Ynoi6TTUi0xNco3UbWUGcFcZgBDAw71L0ydOnXQrVs3jB49GitWrEBmZiYCAgIwePBgVRL76NEjdO7cGRs2bECLFi3g7Oycb29t1apVNU5iiYhKQ7lOZNPT05Genq5arih30JoYGeCbwY1Qw8ECXx+6gR//uYM7T1LwzeDGMJeV6x95haBMSp+lZmQnpimZiEvNQHxKBuJSM/AsJQPP1NZnICWj+B5pnLNnM3dPpzLptMp3vdHLxFVmyA9WpejXX39FQEAAOnfuDKlUin79+uHbb79Vrc/MzER4eDhSU1N1GCURkfZ0mtVoerNC7dq1i7T/4OBgzJkzp0jb6juJRIIPu9RENQdzTP39Ig6FxaD/ilCsHNYUbnamnOC8jMjIUiA+NTsBVSalz14ko3GpGWq9qHEp2cvJ6VlFOpaBVAJbMyPYmBnD2tToZe+nLG/SaWmSa/2LdebGhpAyAdU7dnZ22LhxY4HrPTw8XjkPtZ6NQiOiCkKnY2RjY2Px9OnTQutUr14dxsbGqmVtxsjm1yPr5uZW4cZrnbv/DGM2nMWT5OxzYW5sABcbU7hYm8DF2gTO1qaobG0CZ2sTVLYxhbO1CaxMjHQctf7JnZSqktDcPaSpL14pr5eU2pgawdbcGHZmxrA1N4KtmbFq2cbMCHbmL5dtzYxhacIktKg41rNoeN6IqCj0Zoysg4MDHBwcSmz/vIM2W5OqtvgzoA0CNp7D+fvxSMmQ41ZMMm7FFPysdQuZIZxfJLoVMdnNmZQqe0nzS0pz1ilqUiqVoMAk1NYsO0F9uZxdh0kpERGRHo2RvX//PuLi4nD//n3I5XJcuHABAODp6QkLC86X+iquNqb4Y3wbpGZkISohDZHKV/xzRCa++PdFWcLz7KRM22TXxfpFL++L3l5dJ7sKhUBKRhZS0uVITs9Cyou75xOeZ75MQpVf5ef4Or+4ktKcSaiNmTHszHMvMyklIiJ6HXqTyM6cORPr169XLTdu3BgAcOTIEXTo0EFHUekfM2NDVHewKHTaodSMLEQmpCEqIQ2P459n/5uQhqiE10t27cyNoUzXco7RleT5H0CSY0FZNWeqpyxLz1KoEtSUdDlSXtyBn5KehdTXvMFJmZSqekjzSUpVPakvElcrEyMmpURERKVE7+aRfR0cr1V8NE12ywJDqQTmL25oMpcZwNo0dxKafQOUHZNSKgDbjqLheSOiotCbMbKkv8yMDVHDwQI1CunZTUnPQlRiGiLj0xCZ8Fwtsc358UlAFFCOPOW568oMpaokNTtRzU5Wlf9vITOEzFDKWRqIiIjKISayVGLMZa9OdomIiIiKio/EISIiIiK9xESWiIiIiPQSE1kiIiIi0ktMZImIiIhILzGRJSIiIiK9xESWiIiIiPRShZp+S/nsh8TERB1HQkT6RNlmVKDnxxQLtrlEVBTatLkVKpFNSkoCALi5uek4EiLSR0lJSbC2ttZ1GHqDbS4RvQ5N2twK9YhahUKBx48fw9LSskI+6SkxMRFubm548OBBhX1cJM8BzwGg/TkQQiApKQmVK1eGVMoRWZpim8u/NZ4DngOgZNvcCtUjK5VKUaVKFV2HoXNWVlYV9o9JieeA5wDQ7hywJ1Z7bHOz8W+N5wDgOQBKps1l1wIRERER6SUmskRERESkl5jIViAymQyzZs2CTCbTdSg6w3PAcwDwHFDp4O8ZzwHAcwCU7DmoUDd7EREREVH5wR5ZIiIiItJLTGSJiIiISC8xkSUiIiIivcREtpxZtmwZPDw8YGJigpYtW+L06dMF1l23bh0kEonay8TEpBSjLV5///03evfujcqVK0MikWDHjh2v3Obo0aNo0qQJZDIZPD09sW7duhKPsyRpew6OHj2a53dAIpEgKiqqdAIuAcHBwWjevDksLS3h6OgIX19fhIeHv3K733//HbVr14aJiQkaNGiAvXv3lkK0pO/Y5rLNZZur2zaXiWw5snnzZgQGBmLWrFk4d+4cvL294ePjg5iYmAK3sbKyQmRkpOp17969Uoy4eKWkpMDb2xvLli3TqP6dO3fQs2dPdOzYERcuXMDkyZPx/vvv48CBAyUcacnR9hwohYeHq/0eODo6llCEJe/YsWOYMGEC/v33Xxw8eBCZmZl46623kJKSUuA2J0+exJAhQzBq1CicP38evr6+8PX1xZUrV0oxctI3bHPZ5rLNLQNtrqByo0WLFmLChAmqZblcLipXriyCg4Pzrb927VphbW1dStGVLgDijz/+KLTOJ598IurVq6dWNmjQIOHj41OCkZUeTc7BkSNHBADx7NmzUolJF2JiYgQAcezYsQLrDBw4UPTs2VOtrGXLlmLs2LElHR7pMba5L7HNZZurVNptLntky4mMjAycPXsWXbp0UZVJpVJ06dIFoaGhBW6XnJwMd3d3uLm54e2338bVq1dLI9wyITQ0VO18AYCPj0+h56u8atSoEVxcXNC1a1ecOHFC1+EUq4SEBACAnZ1dgXX4u0DaYpurPf6dvcQ2t/h+F5jIlhNPnjyBXC6Hk5OTWrmTk1OBY2+8vLywZs0a/Pnnn/jll1+gUCjQunVrPHz4sDRC1rmoqKh8z1diYiKeP3+uo6hKl4uLC1asWIFt27Zh27ZtcHNzQ4cOHXDu3Dldh1YsFAoFJk+ejDZt2qB+/foF1ivod0Gfx61RyWKbqz22uWxzlYqzzTXUegsqN1q1aoVWrVqpllu3bo06depg5cqVmDdvng4jo9Li5eUFLy8v1XLr1q0RERGBr7/+Gj///LMOIyseEyZMwJUrV3D8+HFdh0LENpfY5pYA9siWE/b29jAwMEB0dLRaeXR0NJydnTXah5GRERo3boxbt26VRIhljrOzc77ny8rKCqampjqKSvdatGhRLn4HAgICsHv3bhw5cgRVqlQptG5Bvwua/u1QxcM2V3tsc/PHNjdbUdtcJrLlhLGxMZo2bYqQkBBVmUKhQEhIiFoPQGHkcjkuX74MFxeXkgqzTGnVqpXa+QKAgwcPany+yqsLFy7o9e+AEAIBAQH4448/cPjwYVSrVu2V2/B3gbTFNld7/DvLH9vcbEX+XdD69jAqs3777Tchk8nEunXrxLVr18SYMWOEjY2NiIqKEkII8d5774mgoCBV/Tlz5ogDBw6IiIgIcfbsWTF48GBhYmIirl69qqu38FqSkpLE+fPnxfnz5wUAsWTJEnH+/Hlx7949IYQQQUFB4r333lPVv337tjAzMxMff/yxCAsLE8uWLRMGBgZi//79unoLr03bc/D111+LHTt2iJs3b4rLly+LDz/8UEilUnHo0CFdvYXX9sEHHwhra2tx9OhRERkZqXqlpqaq6uT+Wzhx4oQwNDQUixYtEmFhYWLWrFnCyMhIXL58WRdvgfQE21y2uWxzdd/mMpEtZ7777jtRtWpVYWxsLFq0aCH+/fdf1br27dsLPz8/1fLkyZNVdZ2cnESPHj3EuXPndBB18VBOa5L7pXzPfn5+on379nm2adSokTA2NhbVq1cXa9euLfW4i5O25+DLL78UNWrUECYmJsLOzk506NBBHD58WDfBF5P83j8AtZ9t7r8FIYTYsmWLqFWrljA2Nhb16tUTe/bsKd3ASS+xzWWbyzZXt22u5EUQRERERER6hWNkiYiIiEgvMZElIiIiIr3ERJaIiIiI9BITWSIiIiLSS0xkiYiIiEgvMZElIiIiIr3ERJaIiIiI9BITWSIiIiLSS0xk6bV5eHhg6dKlBa739/eHr69vqcVTmLt370IikeDChQtabxsSEoI6depALpcXf2B67tq1a6hSpQpSUlJ0HQpRucc2l9jmvsREthzw9/eHRCJRvSpVqoRu3brh0qVLug5Np4q7Mf/kk0/w2WefwcDAQK38+fPnsLOzg729PdLT04vteJo6evQoJBIJ4uPjS/3YSnXr1sUbb7yBJUuW6CwGotLCNjd/bHNLD9vcl5jIlhPdunVDZGQkIiMjERISAkNDQ/Tq1UvXYZUbx48fR0REBPr165dn3bZt21CvXj3Url0bO3bsKP3gNJSRkVGi+x8xYgSWL1+OrKysEj0OUVnANrdksc19Nba52ZjIlhMymQzOzs5wdnZGo0aNEBQUhAcPHiA2NlZV5/Lly+jUqRNMTU1RqVIljBkzBsnJyar1yk/TixYtgouLCypVqoQJEyYgMzNTVScmJga9e/eGqakpqlWrhl9//VXrWBUKBYKDg1GtWjWYmprC29sbW7duVa1XftoNCQlBs2bNYGZmhtatWyM8PFxtP/Pnz4ejoyMsLS3x/vvvIygoCI0aNQIAzJ49G+vXr8eff/6p6jU5evSoatvbt2+jY8eOMDMzg7e3N0JDQwuN+bfffkPXrl1hYmKSZ93q1asxbNgwDBs2DKtXr86zXiKR4KeffkLfvn1hZmaGmjVrYufOnWp1du7ciZo1a8LExAQdO3bE+vXr1T7x37t3D71794atrS3Mzc1Rr1497N27F3fv3kXHjh0BALa2tpBIJPD39wcAdOjQAQEBAZg8eTLs7e3h4+MDADh27BhatGgBmUwGFxcXBAUFqTWEHTp0wMSJEzF58mTY2trCyckJP/74I1JSUjBixAhYWlrC09MT+/btU3sPXbt2RVxcHI4dO1bouSQqD9jmss1lm1tGCNJ7fn5+4u2331YtJyUlibFjxwpPT08hl8uFEEIkJycLFxcX8c4774jLly+LkJAQUa1aNeHn56e2HysrKzFu3DgRFhYmdu3aJczMzMSqVatUdbp37y68vb1FaGioOHPmjGjdurUwNTUVX3/9tcbxzZ8/X9SuXVvs379fREREiLVr1wqZTCaOHj0qhBDiyJEjAoBo2bKlOHr0qLh69apo166daN26tWofv/zyizAxMRFr1qwR4eHhYs6cOcLKykp4e3urzsHAgQNFt27dRGRkpIiMjBTp6enizp07AoCoXbu22L17twgPDxf9+/cX7u7uIjMzs8D30LBhQ/HFF1/kKb9165aQyWQiLi5OPH36VJiYmIi7d++q1QEgqlSpIjZu3Chu3rwpJk2aJCwsLMTTp0+FEELcvn1bGBkZialTp4rr16+LTZs2CVdXVwFAPHv2TAghRM+ePUXXrl3FpUuXREREhNi1a5c4duyYyMrKEtu2bRMARHh4uIiMjBTx8fFCCCHat28vLCwsxMcffyyuX78url+/Lh4+fCjMzMzE+PHjRVhYmPjjjz+Evb29mDVrlire9u3bC0tLSzFv3jxx48YNMW/ePGFgYCC6d+8uVq1aJW7cuCE++OADUalSJZGSkqL2Xlu2bKm2L6LyiG0u21y2uWUHE9lywM/PTxgYGAhzc3Nhbm4uAAgXFxdx9uxZVZ1Vq1YJW1tbkZycrCrbs2ePkEqlIioqSrUfd3d3kZWVpaozYMAAMWjQICGEEOHh4QKAOH36tGp9WFiYAKBxo5qWlibMzMzEyZMn1eqMGjVKDBkyRAjxslE9dOiQWqwAxPPnz4UQ2X+8EyZMUNtHmzZtVI1q7uMqKRvVn376SVV29epVAUCEhYUV+B6sra3Fhg0b8pR/+umnwtfXV7X89ttv52lUAIjPPvtMtZycnCwAiH379gkhhPjf//4n6tevr7bN9OnT1RrVBg0aiNmzZ+cbm/J8KesqtW/fXjRu3DhPvF5eXkKhUKjKli1bJiwsLFQX4Pbt24u2bduq1mdlZQlzc3Px3nvvqcoiIyMFABEaGqq2/759+wp/f/984yQqL9jmZmOb+0ytnG2ubnBoQTnRsWNHXLhwARcuXMDp06fh4+OD7t274969ewCAsLAweHt7w9zcXLVNmzZtoFAo1L4+qlevntrAehcXF8TExKj2YWhoiKZNm6rW165dGzY2NhrHeevWLaSmpqJr166wsLBQvTZs2ICIiAi1ug0bNlSLA4AqlvDwcLRo0UKtfu7lwhS27/w8f/48z1dccrkc69evx7Bhw1Rlw4YNw7p166BQKAo8nrm5OaysrNTeS/PmzQt9L5MmTcL8+fPRpk0bzJo1S+ObSnL+rIDsn2GrVq0gkUhUZW3atEFycjIePnyYb7wGBgaoVKkSGjRooCpzcnICkPecmZqaIjU1VaPYiPQZ21y2uflhm1v6DHUdABUPc3NzeHp6qpZ/+uknWFtb48cff8T8+fM13o+RkZHaskQiydNAvA7l+LA9e/bA1dVVbZ1MJiswFmUjUFyxaLtve3t7PHv2TK3swIEDePToEQYNGqRWLpfLERISgq5du+Z7POUxtXkv77//Pnx8fLBnzx789ddfCA4OxuLFizFx4sRCt8t5EdVGfvFqcs7i4uJQo0aNIh2TSJ+wzdUO29zCsc0tOvbIllMSiQRSqRTPnz8HANSpUwcXL15Um3PuxIkTkEql8PLy0miftWvXRlZWFs6ePasqCw8P12oKkrp160Imk+H+/fvw9PRUe7m5uWm8Hy8vL/z3339qZbmXjY2Ni23+wcaNG+PatWtqZatXr8bgwYNVvTLK1+DBg/O9AaEgXl5eOHPmjFpZ7vcCAG5ubhg3bhy2b9+OKVOm4McffwSQ/T4BaPRe69Spg9DQUAghVGUnTpyApaUlqlSponHMBbly5QoaN2782vsh0jdsc9nm5odtbsljIltOpKenIyoqClFRUQgLC8PEiRORnJyM3r17AwCGDh0KExMT+Pn54cqVKzhy5AgmTpyI9957T/WVxat4eXmhW7duGDt2LE6dOoWzZ8/i/fffh6mpqcZxWlpaYurUqfjoo4+wfv16RERE4Ny5c/juu++wfv16jfczceJErF69GuvXr8fNmzcxf/58XLp0Se3rGw8PD1y6dAnh4eF48uSJ2p3A2vLx8cHx48dVy7Gxsdi1axf8/PxQv359tdfw4cOxY8cOxMXFabTvsWPH4vr16/jf//6HGzduYMuWLVi3bh2Al5/CJ0+ejAMHDuDOnTs4d+4cjhw5gjp16gAA3N3dIZFIsHv3bsTGxqrdFZ3b+PHj8eDBA0ycOBHXr1/Hn3/+iVmzZiEwMBBS6es1B3fv3sWjR4/QpUuX19oPkT5gm8s2l21u2cBEtpzYv38/XFxc4OLigpYtW+K///7D77//jg4dOgAAzMzMcODAAcTFxaF58+bo378/OnfujO+//16r46xduxaVK1dG+/bt8c4772DMmDFwdHTUah/z5s3DjBkzEBwcjDp16qBbt27Ys2cPqlWrpvE+hg4dimnTpmHq1Klo0qQJ7ty5A39/f7UxVaNHj4aXlxeaNWsGBwcHnDhxQqs4cx/v6tWrqrFtGzZsgLm5OTp37pynbufOnWFqaopffvlFo31Xq1YNW7duxfbt29GwYUMsX74c06dPB/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 0000000000000000000000000000000000000000..63dcdf2c966ae604b3cb73c576fb2d53b46fc706 --- /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 0000000000000000000000000000000000000000..5738e6141606039eb1b135d493f3b5e8f21252f3 --- /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 0000000000000000000000000000000000000000..84690075318d5176fe18300496d2d0c45443f233 --- /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 0000000000000000000000000000000000000000..63617b7dc0a4ae6e703ec6a4de9a9888995cc99d --- /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 0000000000000000000000000000000000000000..2345073f2107d79375f615cecfbb7d2bf0f391d0 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 0000000000000000000000000000000000000000..c66690fd2e48868972f28f39943d6fd61bf469af 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 0000000000000000000000000000000000000000..b859d78466ae193a96ab264dbb3d896b2ef2b763 --- /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 0000000000000000000000000000000000000000..10f6a66fd0c19b0403838c27d4df59639f15b2c7 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 0000000000000000000000000000000000000000..d5ae2061b911a697acecc75044e24854e81b6429 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 0000000000000000000000000000000000000000..c49d2d5676b4404001f0a84f7f6df8cd163288cc 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 0000000000000000000000000000000000000000..bc2f20fff34ec448e7189043edadcc611fe033be 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 0000000000000000000000000000000000000000..b7521e746901db89af1cffe5257cac6cca95b592 --- /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 0000000000000000000000000000000000000000..eab7079b3ef787a57e665a10bd313b5c4887c600 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 0000000000000000000000000000000000000000..52636f3be04ff4ce8677fbbbfa2284a237020b3b 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 0000000000000000000000000000000000000000..955d02f6a06fa520fa4ccfdf7ea427213ea24acf 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 0000000000000000000000000000000000000000..f930454b1b2a47f6e5025bdaff2e1f8cb15156e1 --- /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 0000000000000000000000000000000000000000..48594b5ee285241f3f479329441dd1bfefb3528e --- /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 0000000000000000000000000000000000000000..51a2b2593d83a071f5043b2dea25eee2a2a98a8e --- /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 0000000000000000000000000000000000000000..dcad115e3af46522be2a53bbabb9788453e5d139 --- /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 0000000000000000000000000000000000000000..0f41968ee45582e829f774f49ab98dc6d6f4b578 --- /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": "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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(\"该期权的风险中性价格大约为\", 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 0000000000000000000000000000000000000000..7c9ba949dec426961d862e278b18500b64f898fd --- /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 0000000000000000000000000000000000000000..74aa04806e40036748643bdb24e7f198da1ca985 --- /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 0000000000000000000000000000000000000000..291eb65d0e862d02c2c4966c19456b7d04fb3c5f --- /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 0000000000000000000000000000000000000000..93cc64650c0ecde14d858c980a430128df87fd1f --- /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 0000000000000000000000000000000000000000..975edc9c22c1d1f63ce44f34ce2cfd1e365f8d6c --- /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 0000000000000000000000000000000000000000..8c5038880238e5686babcf6689e1cdb1544f31c2 --- /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 0000000000000000000000000000000000000000..fbf6554adfaea4490a0b350b5dada0925eb36b30 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 0000000000000000000000000000000000000000..1f60d44ddc9f8771b36e77801a2789e179caaaba --- /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 0000000000000000000000000000000000000000..fea56e226358d302aa1b7572091c84bd95f1a031 --- /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 0000000000000000000000000000000000000000..b59a0b54c934f74c2db5a4d6f220af0ade397ca6 --- /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 0000000000000000000000000000000000000000..0dde5a57685e03278980b48de7a22d721a3542a4 --- /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 0000000000000000000000000000000000000000..5a2031657557329cc76e2c1348c9bf39dc9b055e 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 0000000000000000000000000000000000000000..ffb754f918f4ba7886b8d8b67e2d020ec7a3cf8d 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 0000000000000000000000000000000000000000..47d1f9828074f102eaa5b1d6bc854d80cbc825b9 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 0000000000000000000000000000000000000000..0b16e7c017a7be06809bec21b0297303fd837d7d 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 0000000000000000000000000000000000000000..72be47dbbe5a53e5da35f8dd232ae192306b7b7e 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 0000000000000000000000000000000000000000..ff5e331ed4b401234eab184c04bbca8b2991f9e3 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 0000000000000000000000000000000000000000..91668734737b5f8a90481ff5d236c9cff516046b 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 0000000000000000000000000000000000000000..a0a1b51790212a9f762637a1d5c21b8fbd436949 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 0000000000000000000000000000000000000000..1047835dd6bc139aa531ca0fa3a21bf1bf6d458d 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 0000000000000000000000000000000000000000..0aa142acfda64ac8f3f3c2d8992b6a673003320d 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 0000000000000000000000000000000000000000..d68f142e1da3d1f37568c93196abcfdb586a9a59 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 0000000000000000000000000000000000000000..ec3f06c27744a840aaa1fa59dd8c03d1d16d0dda 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 0000000000000000000000000000000000000000..561a952da190de657a609019d3a2d2104df4efc8 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 0000000000000000000000000000000000000000..5898db1ba1be5f0ee25c76734185050e4e78fb9d 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 0000000000000000000000000000000000000000..9d228c5988074a99bcf4daa19252002464e4e9d8 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 0000000000000000000000000000000000000000..bd9749bf57f9e9e126e9bbacae1516cc0380f0da 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 0000000000000000000000000000000000000000..940ec552629e7129c22d0c3ccbf4bb82e0b7e1c0 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 0000000000000000000000000000000000000000..c7c637eb7597721ad4cbb2eee229dc43b0c79477 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 0000000000000000000000000000000000000000..956b40f4e852cebc90d7bce8e7127c12fb6216be 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 0000000000000000000000000000000000000000..a3232e46f79573772ca1f66419008efb86290f83 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 0000000000000000000000000000000000000000..bc209e912603a81916b3af2f8cbf33f580d910e1 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 0000000000000000000000000000000000000000..5d71ae8935b5b036dd7c310d96973c410d8eb5a7 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 0000000000000000000000000000000000000000..5df395f9bdcb0e19b486f9465a2745061fcb8a24 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 0000000000000000000000000000000000000000..f8d9f30e3035af9340648b9438f37d9475ba0af2 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 0000000000000000000000000000000000000000..23757225d38a9cef22eb1967e0ea1e1a30b1a124 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 0000000000000000000000000000000000000000..7c16fd6fc22d60236790217f58f23ac01ca4754c 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 0000000000000000000000000000000000000000..a419d81de73faa9cdf35ee4cf3fe163b9f63f1cc 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 0000000000000000000000000000000000000000..63cf1cfa0381f8b99984c01fe05ca9d1979b00dd 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 0000000000000000000000000000000000000000..063b6aaee5abcb66ef0366c15d066ed85ec20bc8 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 0000000000000000000000000000000000000000..01a770e91db58e07ff6ea922000526315533c29c 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 0000000000000000000000000000000000000000..36ae8fe220d2465d34afadfc0492c22f8037dc08 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 0000000000000000000000000000000000000000..530330fc9fd4a616f58166b1ae6852388c1b5187 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 0000000000000000000000000000000000000000..15adcf10e3641ceab6c57306aca2130bddd2d560 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 0000000000000000000000000000000000000000..5b3abe9079d9217a73b16fa574516fbf3dc7c30b 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 0000000000000000000000000000000000000000..cff9c6309d78b8990d119fab3a449852d538b1a1 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 0000000000000000000000000000000000000000..5a18a56e4fe88af5c9a10974a3d2402ac4d90d62 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 0000000000000000000000000000000000000000..973e796e0ed3929fd18bbafae5038f432985990a 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 0000000000000000000000000000000000000000..505ed9c6b46b2b9bb968f6588d4c84a7c08472ca 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 0000000000000000000000000000000000000000..ee115f357cfea5bd035a45d3fa475381bd849897 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 0000000000000000000000000000000000000000..ec17c7c0e86372eee549748b828cfcad4eba1e29 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 0000000000000000000000000000000000000000..7ff794df9a816a7fc8be746f1e5507ec2df64627 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 0000000000000000000000000000000000000000..1d8640926b308c90ded0dbb88ae5b69f831e01cb 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 0000000000000000000000000000000000000000..5e58490f81e799eb983e5de53c1e2ec9273e91f4 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 0000000000000000000000000000000000000000..e0f6b7d8618c30a42219acd50256a864203a1198 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 0000000000000000000000000000000000000000..5135d5fcf0d1a2b49dae3a331b2bcc94ee14680a 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 0000000000000000000000000000000000000000..aba1abdd705e4de5c5cabf0e2cf5d50410f2e029 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 0000000000000000000000000000000000000000..d6e3df458daff0372cf7c4eedbcd85d6c24c1e2b 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 0000000000000000000000000000000000000000..f2dc83fe4e5d78db531e9e7512784defe987a9e7 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 0000000000000000000000000000000000000000..cc317f64a1c3d495bcf02c680a3e331e53227207 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 0000000000000000000000000000000000000000..90f397f5b14e4b464ac187e704d0e59820cceb57 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 0000000000000000000000000000000000000000..063a05790b1199c3b0362c94f5767bd471e2fc03 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 0000000000000000000000000000000000000000..48be1cc7923804c5daa3f57538b05ee3c301d47c 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 0000000000000000000000000000000000000000..646f275babb9f16c81582d660a0926d4618130f2 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 0000000000000000000000000000000000000000..e5744fc0a337450aaa52b01af5c8a951788546ef 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 0000000000000000000000000000000000000000..3b5af8a74be1fe9a35ecab6073ae4df0a1dd5544 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 0000000000000000000000000000000000000000..fc5ed95963439e1a345d6620f11935869352077c 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 0000000000000000000000000000000000000000..ec8c206954c47a3c135d67c445d83c07b1b74c7c 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 0000000000000000000000000000000000000000..02b4675b9414d1aa98c5d1c1323f56e98db15a5d 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 0000000000000000000000000000000000000000..fbcefffff2ac8412efc0df828a553ef588eed2a2 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 0000000000000000000000000000000000000000..5bf8631dd9d34994142957bae592e32190b74bc8 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 0000000000000000000000000000000000000000..ce9898bb8f51a0546f2598f5d1e03ecc158e02ab 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 0000000000000000000000000000000000000000..d29d20316d97c766190cbb2e7c56cd9ed64a68a0 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 0000000000000000000000000000000000000000..fcabaa5d1a6e45da192c3d30e4f0dc64b9f9d6f8 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 0000000000000000000000000000000000000000..605a4ebb5897b401216b98a1d6bc3de7873bb6fa 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 0000000000000000000000000000000000000000..291856a986f7c2d39b6e2bff1a269a6aff7daed5 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 0000000000000000000000000000000000000000..ec42f377fceb130aa5fffed48700968dea0e3470 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 0000000000000000000000000000000000000000..8db6a9b5127fb0332bf12620031aecc181793b16 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 0000000000000000000000000000000000000000..a9470ecb20c8fffe9e235622caa8a8a3e0be2d3d 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 0000000000000000000000000000000000000000..cdf489a2ab1f3dc94aa0fbcfbc202b3710609819 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 0000000000000000000000000000000000000000..77a2ca2506835288d749594ef30e9d1042b7969e 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 0000000000000000000000000000000000000000..cefb88c1849395a31b12423a1c9713352a5c0a22 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 0000000000000000000000000000000000000000..b2f3f34bbd798feae91d0a241df312c0c5143720 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 0000000000000000000000000000000000000000..d386e9f2c831f406d01894429b900df0c3633cd5 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 0000000000000000000000000000000000000000..674ed7f33202013f0ffc9ff439a45ffe5616f46f 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 0000000000000000000000000000000000000000..0d11969344247a370357684fa27f8bbed665cff4 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 0000000000000000000000000000000000000000..4130c3bec1487e871df6184d134ac802aef1f53a 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 0000000000000000000000000000000000000000..157329f7e119f7e29283e9d26648ceaccd4f6029 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 0000000000000000000000000000000000000000..365baa62349d171d7cc85990886f3efabae0d439 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 0000000000000000000000000000000000000000..22e6f960eb996e1fafd5cfe5cfc53cb5ee324024 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 0000000000000000000000000000000000000000..7ea35f94e7f4755d47a61b34fbe12f167a06a83b 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 0000000000000000000000000000000000000000..f1c8bd3cafac6477987943fd4febeff7d32e634a 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 0000000000000000000000000000000000000000..63b213a59fccce271858c05b9cfb77692b8bc610 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 0000000000000000000000000000000000000000..f83507b5b3f38991e047fd93f20cea869f0a9658 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 0000000000000000000000000000000000000000..35550890d5146e9b6787262503b877f6fd16dbfa 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 0000000000000000000000000000000000000000..52a80b75b405d5a502c1a99fd379bdd565ae288b 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 0000000000000000000000000000000000000000..77759610db1b51750e5f0052b413f127fb224336 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 0000000000000000000000000000000000000000..8707733de62fdacd3484eddfaa61e879798d2a46 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 0000000000000000000000000000000000000000..d7b26adba59e65445ffd5dc7e8d1f69e9b53fef3 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 0000000000000000000000000000000000000000..47a23e37c69176cb35c9c988813ec168debd1848 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 0000000000000000000000000000000000000000..986469079312f248330428043f023bd138b961e7 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 0000000000000000000000000000000000000000..28f8c62122e94270d49d305e03957ddcac3def07 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 0000000000000000000000000000000000000000..56fa8feb104c75967260a6cbbeaf37be35f4c3d5 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 0000000000000000000000000000000000000000..8d2edaa8c1c2cfc2e4aab41313628a13b34ecd9e 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 0000000000000000000000000000000000000000..6a1ec25eb4b5202f113e8c5d299f408329811872 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 0000000000000000000000000000000000000000..37c1fcf19e83ac83a53ab71e5dd6c2cf1246550a 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 0000000000000000000000000000000000000000..00f4dec4ef115bad3a99f2951e91599d73dd4635 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 0000000000000000000000000000000000000000..b7fd5d1a5dbe6931c273a973f7393d7b925fedfc 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 0000000000000000000000000000000000000000..dcd8c2b950b4fce1ac0cf229c62ef402ee227514 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 0000000000000000000000000000000000000000..fb570294219964753cb245b47f5322bd49c26a54 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 0000000000000000000000000000000000000000..ba7f1efc4fca230ac8fc5246c770feabdb547b3f 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 0000000000000000000000000000000000000000..00a01482084c896f7324906b92b1c3072ff4e534 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 0000000000000000000000000000000000000000..31272eee8593bb8c37ff5e178a962cc2d5f1b1f1 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 0000000000000000000000000000000000000000..a99b43a47b5b442249f5a5af23ccbd674e9a3540 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 0000000000000000000000000000000000000000..bcb8a72396be59ad733d31f216cd9c5b13bb4504 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 0000000000000000000000000000000000000000..62605022d7b83a8ae3bcd78e0af8ca9fd7985673 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 0000000000000000000000000000000000000000..10ed12c67d454103c8b1950f7acda5d5c6688d87 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 0000000000000000000000000000000000000000..ffac81533d1ee8503b1183d28856ac74f2603702 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 0000000000000000000000000000000000000000..1f83e3d1e0fe6a27b236621a2c75f84124178d3e 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 0000000000000000000000000000000000000000..24dd4e4c6a79f7595efad6ed4fffc213aefab74f 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 0000000000000000000000000000000000000000..efc4fb7f902f1ec8f4833447dd365441eb303757 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 0000000000000000000000000000000000000000..02b1500283516a4a02b62a8bf4b6a4d4830e5a7e 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 0000000000000000000000000000000000000000..fe8806d7e1f8f24bffdf2bf71eb76e691d7a5555 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 0000000000000000000000000000000000000000..46f0f5c830b9af562cffd77aa1d26a2911ff9f67 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 0000000000000000000000000000000000000000..54007aecc5ff45c0890d913ed8154e30482db0e3 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 0000000000000000000000000000000000000000..b3e3347fe040d47dc8172161c57158b274edcf03 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 0000000000000000000000000000000000000000..b0e1b8c433fbc85a5ee88b84b953c4807ca04ef2 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 0000000000000000000000000000000000000000..108094d668dba058e2d2a7138ec27217fef82a9f 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 0000000000000000000000000000000000000000..782bfdf80e842b33e22a877b36ae31b6a0e8263e 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 0000000000000000000000000000000000000000..cef53aca2e946a1cf768a47349dc5a70a0814df5 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 0000000000000000000000000000000000000000..23604f39e32bfa33ee65f965378fa5b48f7a825f 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 0000000000000000000000000000000000000000..0b5681176a5409ea624afc26c0f93f5e6ee85c47 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 0000000000000000000000000000000000000000..44a2a8643e5206f42e7f2a4fb8e1d496b52fe143 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 0000000000000000000000000000000000000000..ce557ff416e86c500824b1216f3ef4b33df9d31a 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 0000000000000000000000000000000000000000..c47801491f21497d15fbb52e54f3dc95108d81c6 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 0000000000000000000000000000000000000000..1fe968c6c738432392807f87aa88d6a16bf73f5b 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 0000000000000000000000000000000000000000..5c40e2ad90418a7e91dab7630dd69ff0b9f20260 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 0000000000000000000000000000000000000000..2d0525f8e240a6ff626c59993564b6b0f92a446c 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 0000000000000000000000000000000000000000..c14bcf71c6b59a6c97332c60c1cf047ec96ba44e 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 0000000000000000000000000000000000000000..44c9d9e1406baf9f55e7e7486065633e3fb2932c 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 0000000000000000000000000000000000000000..57f20dc2f5f3a67a82a00389bb5c76db7ba77a19 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 0000000000000000000000000000000000000000..c9e89525fee9d99269c4eeaacd0b6727eff79004 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 0000000000000000000000000000000000000000..69d059386a524972df6ad5514f60159d2ecd7343 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 0000000000000000000000000000000000000000..b7b2833d634ce54eefb3a52c688c65e3d2fe996f 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 0000000000000000000000000000000000000000..bb19713d2220ce9ca5a9b2cf1ae183e12f92a90f 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 0000000000000000000000000000000000000000..79ccfc075687c4bb48f1dcf4c73f1109da7afa9c 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 0000000000000000000000000000000000000000..90c61dc4c81b70269e4e1b0c6a4d19cb5e1bd94c 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 0000000000000000000000000000000000000000..5ae50e8409fd550f2a280143ee2acbcb403fae9c 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 0000000000000000000000000000000000000000..53aa0638deabb9bf90f75ea9854864bc3f02d313 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 0000000000000000000000000000000000000000..0f0450f290d1ea5b8c9f9b2e57397697fcc89d4e 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 0000000000000000000000000000000000000000..a3213e66bd2c7d4cda014d691e271d5af2a535b6 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 0000000000000000000000000000000000000000..8867058e277dfc6c5fef385dea19cd1f2c190d01 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 0000000000000000000000000000000000000000..6625473771ec045f094a5a383f2ca92f9c8e1656 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 0000000000000000000000000000000000000000..c421fca8cc9b841a5b6a19ebe586f2611c061fe3 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 0000000000000000000000000000000000000000..de340fa06ace50efc0a9b1b29afbe422e7a8a78f 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 0000000000000000000000000000000000000000..c3be1229ba97a1c9fac4739f87ce948d651d3168 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 0000000000000000000000000000000000000000..2eaac27872eb02bc09d6df8f5c19c15d6042c7b5 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 0000000000000000000000000000000000000000..09195ca0a7b0efd54f12d29092b1370a6d543ce4 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 0000000000000000000000000000000000000000..7ce585a5b493f820ced5d87c0c6a700cb925bb78 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 0000000000000000000000000000000000000000..0948de6ea9a0640b932a7f4e5d6a1f39e20afc42 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 0000000000000000000000000000000000000000..7da4e8e059b28a0f931f461e8a6af6369df5c5b7 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 0000000000000000000000000000000000000000..2c0f3519106961def1b82fc55b96b8212ced2186 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 0000000000000000000000000000000000000000..0074f6554ac20ab3da97f1e536a9f0928709b7cf 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 0000000000000000000000000000000000000000..9f5da3089371b003a6977c3d7ec57db88fe944dd 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 0000000000000000000000000000000000000000..fd00d43608766fed5f9fe07daf8f5a85d05cadc3 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 0000000000000000000000000000000000000000..22b1b8b4c97653e03b3e33c3c4195f5e7987aed5 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 0000000000000000000000000000000000000000..f1e3085f3d714103c379522c0a6243e38c1baaa3 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 0000000000000000000000000000000000000000..21d04bcbe2831e9ba2bae860f4df9d058c50e704 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 0000000000000000000000000000000000000000..b1c368bb13b2fe4633fd6ff686d7276e585963b1 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 0000000000000000000000000000000000000000..07d38f59dcd8d8f4b943f95ab86ddf0842caddfb 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 0000000000000000000000000000000000000000..2a3bc58069893c8056609852a48fea85d1881b71 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 0000000000000000000000000000000000000000..cdabdb4f9d46e0fda336f98b8a1e4d39ecfadb55 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 0000000000000000000000000000000000000000..519a186de0f734b2f6a21fd2f30744fe4816a7c7 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 0000000000000000000000000000000000000000..1aa272d97d343f76d729c08eb5a00305112cd4be 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 0000000000000000000000000000000000000000..eb4fffcf6d4b745ae589670cd4eaecf67f32f763 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 0000000000000000000000000000000000000000..5d672300e8e595f18e0111aab0688115023cdc16 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 0000000000000000000000000000000000000000..882f344c692d0059fa3f139e02b34fa13ec2758e 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 0000000000000000000000000000000000000000..38ebd596dc0eb0e123e7e83b675c7360bc44b287 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 0000000000000000000000000000000000000000..efb546da9ea215109cba15e21d77fa7ca11c228e 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 0000000000000000000000000000000000000000..26645da42edc2111e4069fc66d5a0cf49d903e0f 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 0000000000000000000000000000000000000000..d9b788978844604e9a0fd15093325cef4ac7b12e 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 0000000000000000000000000000000000000000..a29e1f29d59ee46572dbadaa5c55343d416a8e5e 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 0000000000000000000000000000000000000000..62d5689d348a5d53f290f34e630b919c7862870b 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 0000000000000000000000000000000000000000..5137a739289763b8579635b93fd96cfeccbf4c11 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 0000000000000000000000000000000000000000..286510fb1e54af658025258e2150986cd16e87fb 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 0000000000000000000000000000000000000000..3ba8c292e7fcace8e8e0c4075066aafdc262bdab 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 0000000000000000000000000000000000000000..e4d604e200c157668e553c93a2ae48ae51f71cdb 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 0000000000000000000000000000000000000000..5e43d3af5f08935270f72ada2e18e198d08d7f6e 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 0000000000000000000000000000000000000000..b6eff55afc54bf6239fd69ba4503754f83e103fe 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 0000000000000000000000000000000000000000..6b9004d28a385cfc2874a7b3ad19e564315c6d84 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 0000000000000000000000000000000000000000..0ca84a0cd69ac1b29f726c6ae6ebf05e65caee96 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 0000000000000000000000000000000000000000..2b37277aec3ff2e0f4773c8b11f5973c18fc5e20 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 0000000000000000000000000000000000000000..2832a1aa57e918f3195448c61f64f17c46e8ce7e 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 0000000000000000000000000000000000000000..647bf5875727cce55e1ad2d575eab6a7d29d503e 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 0000000000000000000000000000000000000000..bba967b972234f05618b7c4066096fe978b8fb16 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 0000000000000000000000000000000000000000..00cb310bc3b653a292a6584f78448ca37addeb53 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 0000000000000000000000000000000000000000..7206473ff29afe4237fed030f24269e457c80043 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 0000000000000000000000000000000000000000..a0f8526a398baba56b489b9d11073c347c5ebb9e 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 0000000000000000000000000000000000000000..1494f04b852820bf378dca55107cc47b5580b765 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 0000000000000000000000000000000000000000..d2c85962d3bea4b3fd78d9530820d4407a6158b3 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 0000000000000000000000000000000000000000..1743f8f05e5f38ee268902963fa3c5d71c8dc44b 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 0000000000000000000000000000000000000000..20817bbcdcd8474e6d329b71d967df4d9d59510f 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 0000000000000000000000000000000000000000..779ab5ec28bdcfc0f90bd1b4c2a6d6dbb6d302f3 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 0000000000000000000000000000000000000000..e9b1e5abb06c4c01f70948ad9f66941abf73e43d 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 0000000000000000000000000000000000000000..68a623391bc4c76e29f47318c5d2e7e9aec020e6 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 0000000000000000000000000000000000000000..2bb74c7734572f45b8b58587e7ecef4885c9d202 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 0000000000000000000000000000000000000000..3cedd29e5ff1a90bdbec1270e4430bfa9ddce9b8 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 0000000000000000000000000000000000000000..91e7db313fa1f13c426de34a64cc493b18ebd0ec 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 0000000000000000000000000000000000000000..902aa68f3569252e942ba5a52f8358feac811ae9 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 0000000000000000000000000000000000000000..7193eff32ddd74f8f17ab4daebd42bbe6d36261b 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 0000000000000000000000000000000000000000..22978f914fb7cbc9179ba2ad7cec0cc4c71a3c34 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 0000000000000000000000000000000000000000..487bc20805beeabac0a2f0d15d927ae8bf2838f2 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 0000000000000000000000000000000000000000..6e3443cc5e8c984bc87fb4252bea00ff5f7b0f2f 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 0000000000000000000000000000000000000000..7022b792daf1ffe2443ae95068bcb03e9f9120f0 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 0000000000000000000000000000000000000000..011cf75a3097fb82c99a9f492f1d2ea51413ff5a 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 0000000000000000000000000000000000000000..b44d936e04296fc5a05fd3d3c76e5a5cb064f7ce 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 0000000000000000000000000000000000000000..27549a9c39a452fae8d5c3c22149705bc5c2453b 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 0000000000000000000000000000000000000000..43f24ff5c92d103381bc1ad6e0b2cd3520109750 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 0000000000000000000000000000000000000000..0a146fdf4fff79d47e52cb9dcfb1fabb57ac259d 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 0000000000000000000000000000000000000000..763aad1d62682aeb139154fa398e6dc160782db4 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 0000000000000000000000000000000000000000..a5301bdd7915275d4a72b3fd07f280a20dcd8369 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 0000000000000000000000000000000000000000..0d0f4808815c85b23e9ae9d689f0ef904388f95a 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 0000000000000000000000000000000000000000..961f1944aa32dd6f3ece50981b46f9cb9fcccb08 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 0000000000000000000000000000000000000000..45678756e77b5aa8cf1e115766a661c7566883cb 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 0000000000000000000000000000000000000000..1e6662bb377eaf66b35e0823ce91ef4c10268a47 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 0000000000000000000000000000000000000000..5aca703acf5e5ba537c6edf7c6fab388fd4432bf 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 0000000000000000000000000000000000000000..42913cea9e5b2c2dc2c2d764ac723d82c432cee4 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 0000000000000000000000000000000000000000..ac5f503652003828246fc3a16942771a3b733498 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 0000000000000000000000000000000000000000..7b1c44e0705a8e58366b203b104d5650759a3729 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 0000000000000000000000000000000000000000..553fc887f5b07b56f808309d1dfcea444d2d1f05 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 0000000000000000000000000000000000000000..4ba2f498f69736d83365f70bb9a3511cee58a38a 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 0000000000000000000000000000000000000000..5524c8de0c02ee735b933a7111416f2eed0f85e9 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 0000000000000000000000000000000000000000..b95ca4328279c75fb994182273d106afae43ad25 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 0000000000000000000000000000000000000000..965ae3b5640b6e4b08612b7a7de5c9d2e8394c06 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 0000000000000000000000000000000000000000..d68f142e1da3d1f37568c93196abcfdb586a9a59 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 0000000000000000000000000000000000000000..c39af1d2d42644c6320bf6616f909e579d7265fc 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 0000000000000000000000000000000000000000..e58806a19713dd7efae6e04a4ac60e6bcba75845 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 0000000000000000000000000000000000000000..40c7d2bcafd282a9b8e1e08d82e548b138bc246f 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 0000000000000000000000000000000000000000..b2f44dd8f3b23e713838e8194ac4068ca8a18cd2 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 0000000000000000000000000000000000000000..cdf3134c36aa8a6208448aacc45f12e9606a8dfa 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 0000000000000000000000000000000000000000..4edc374c762d498aef676059e2f2aa86bc3f776d 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 0000000000000000000000000000000000000000..cff260e159ee6e1983da7e202e40586b09cab521 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 0000000000000000000000000000000000000000..99987a10732f79ce2330169e17d8f043bfd8ee4c 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 0000000000000000000000000000000000000000..ac5bac93917312292810e1ea25fa8cbb6de74168 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 0000000000000000000000000000000000000000..f288cb45e9374f88a1639ce3d02014e874d09ef2 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 0000000000000000000000000000000000000000..d90123277fcd50651723043eeb758fbc3f5228ae 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 0000000000000000000000000000000000000000..bc8b2fadbb1292f03dcc6ea68cf3d482fc65628d 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 0000000000000000000000000000000000000000..c97d94364476624a45e34c962c81452a63b3c3a6 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 0000000000000000000000000000000000000000..05aa344088f5a75841802f442c4a8b6eb0bf34b1 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 0000000000000000000000000000000000000000..d646cff2c8d194d855c9a27fef91c184e8b6239f 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 0000000000000000000000000000000000000000..3305da0f0bd027711499fe6e7cf47896c81e27db 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 0000000000000000000000000000000000000000..57cd638080aca8c1b360ff76a8277951af340e41 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 0000000000000000000000000000000000000000..47a2145da6b70367e9c0d7d296cd729046d9a055 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 0000000000000000000000000000000000000000..567e87f66e5028ae53f3f96976e0e0434f415b32 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 0000000000000000000000000000000000000000..db9c9a71d5dbc93edf5d1d75164dd2dac8a42c4a 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 0000000000000000000000000000000000000000..2d43ac4a3d7f9adf1bdab279a53911cffe492af1 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 0000000000000000000000000000000000000000..db121f135222c94893afff4770168fc2fedf714a 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 0000000000000000000000000000000000000000..7c8bbf5939c535760c630ae840249e82c8ae54c8 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 0000000000000000000000000000000000000000..a631c1b766b18f1b6f8146f54125270bf6d48bd3 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 0000000000000000000000000000000000000000..0b00176810431ab562efdf68a3dde8b8f38c396e 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 0000000000000000000000000000000000000000..094cfce59f27026b60ff99f0104b38fdfce1f065 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 0000000000000000000000000000000000000000..78daf83e94f4caa767cf442d72d83fd2a19a3605 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 0000000000000000000000000000000000000000..769a7e15d80ad10141d235a7bfc8437f98fef024 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 0000000000000000000000000000000000000000..0d494d35cde47d26899f425576e6c3d377dd0dfd 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 0000000000000000000000000000000000000000..3dfe4a4a940453c1249a5ee26cfb20565b15d019 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 0000000000000000000000000000000000000000..0227279cf06ebe1b8579db73f84e298beaa54d53 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 0000000000000000000000000000000000000000..51065c8a4bd5d543b139fa12732f4104c2285915 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 0000000000000000000000000000000000000000..19abf849dfd1953ba542ba9ef17a81e72cff9faf 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 0000000000000000000000000000000000000000..e483f437f951c13cd203e5245895e0e781101f87 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 0000000000000000000000000000000000000000..0e95ee7d0c8ed3b55715725eaf393757a28dbbcf 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 0000000000000000000000000000000000000000..f6c5b290d58070e34151a459aff33911de4b863f 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 0000000000000000000000000000000000000000..36608b15d3a28a1c2db4a5c2ea7cfe22d69c707e 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 0000000000000000000000000000000000000000..ee8a86c8a84fcf020090d9de1f629b5c29c2e0af 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 0000000000000000000000000000000000000000..5dbe5cc941420fe064e55d6be658907c03983746 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 0000000000000000000000000000000000000000..2a24aed061f99ed18e58171756f41ef1d9e2f9fc 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 0000000000000000000000000000000000000000..76303e7ce07763d61aaa6eb4289f8409a7bf614a 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 0000000000000000000000000000000000000000..ee5dc939a192e3c8fe9199d901f4bf60aa6b0fbc 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 0000000000000000000000000000000000000000..456c910ee4c80d9381b71d428cf54b06f5c61cc0 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 0000000000000000000000000000000000000000..6c9a15dd7cfb15d4505d5726a4f52e18750ba937 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 0000000000000000000000000000000000000000..2f3555abc50294c7b8d02ebb7c6017ae95ff7958 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 0000000000000000000000000000000000000000..75fc98016f144db5bffeb6cbd7829c01b960103c 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 0000000000000000000000000000000000000000..9041f152605e4c6078a1ec9b4a356a72ee367b0a 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 0000000000000000000000000000000000000000..1a4902df74c5d444cb7c8b21fadf871be470b1fe 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 0000000000000000000000000000000000000000..52c3e5a487f8a283d60ec5efd53fe86d49847c8e 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 0000000000000000000000000000000000000000..82124f462834f73967bf3b6d00d81b8fb47b4435 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 0000000000000000000000000000000000000000..ad9707d82bd896af63e269bd2f2e115c8e1c9784 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 0000000000000000000000000000000000000000..02d9f3713812ba5ffad32238e02f751027fd00bf 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 0000000000000000000000000000000000000000..33b6801caff0d63f53c1fc82cf80b363c7f4901d 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 0000000000000000000000000000000000000000..164bcd7da2483ae1a4e4b683e0441bac16ba28bf 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 0000000000000000000000000000000000000000..3d7dfc0f0891bd7b13304625cfde13b33c69a024 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 0000000000000000000000000000000000000000..cc06b2eba58e7758f659cabd821999bbf635e1df 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 0000000000000000000000000000000000000000..34cab2d0da8348df1e60ab91a81d42f6ac738bb4 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 0000000000000000000000000000000000000000..d853f7560863be38a002db9f09147e6b07d37b04 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 0000000000000000000000000000000000000000..d4ce015eab636714ee7ef0d8386b872cd95802ca 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 0000000000000000000000000000000000000000..4d125d62d8a5a0d74f4f4854fd030b82af9ac722 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 0000000000000000000000000000000000000000..59b83ca8b2712826c015a1e8e0dab044334df6b4 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 0000000000000000000000000000000000000000..2b8839e64e7ab897aa895f2b4f008665e894235d 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 0000000000000000000000000000000000000000..ae9bee1bb62f6ab005d974c92b304d494c0b78f6 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 0000000000000000000000000000000000000000..ecde63e918e5878519f3dfabb24b3e95fb59ebdf 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 0000000000000000000000000000000000000000..779058243bc81e0391fd3dc65bb542aaa0b5b9c4 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 0000000000000000000000000000000000000000..a66671fbc8c689605a26c1c796471f7a30acdd37 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 0000000000000000000000000000000000000000..76df911a0c2dc98311e68471115bab55b6f023c2 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 0000000000000000000000000000000000000000..4288e2094f8cbcc0e05006aa291d9122bcd7cf6c 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 0000000000000000000000000000000000000000..ddcc75b1517238e059311a0855248c9586a2437b 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 0000000000000000000000000000000000000000..2f00166eb09bd6a65a7a99fa393145c9092f3199 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 0000000000000000000000000000000000000000..ef94f8812bda238849d1f4054f1732ad70bb7afb 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 0000000000000000000000000000000000000000..08dd2c099ac244d96405f3fcbe04fb0cae47c3e6 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 0000000000000000000000000000000000000000..4ed400acdd72a152635ab87b75b32df4bc464a82 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 0000000000000000000000000000000000000000..3196914d790c46a6ba100f53d9ed1003dd7c15ee 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 0000000000000000000000000000000000000000..97ab50e0b68edfa9382286bec9e074876aa20ec7 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 0000000000000000000000000000000000000000..edf79126cc7bb7ec7d902b27e4ee55c38dddbfcd 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 0000000000000000000000000000000000000000..faf49ce1d6843ba3ec0c38a512a84ba0ed959be1 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 0000000000000000000000000000000000000000..97cd17efbc423164f7bbee91070063ea9f48ccdc 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 0000000000000000000000000000000000000000..387fa78fa40097b5146ffb9963c52948d27badcd 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 0000000000000000000000000000000000000000..ba4c5b7e368369e18b06c4e4c7b65bf80ae94403 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 0000000000000000000000000000000000000000..d3fa1b0831fe2ad25c0b89adc77c0d6775d6b87e 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 0000000000000000000000000000000000000000..a0de78350e77a16f5172dc735117d86d69267cc8 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 0000000000000000000000000000000000000000..292a0d88eec48181fe017a8a85a033100005766b 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 0000000000000000000000000000000000000000..704c88ba5128c96418851d4e40f13a74301d76a5 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 0000000000000000000000000000000000000000..5588551f537fc8107b176803fcc5a445eebc62bf 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 0000000000000000000000000000000000000000..af499907f7e0c60293b8f8147704e52a551b2454 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 0000000000000000000000000000000000000000..8f838e36b8df3460aa167fdb61ba7f25bffc2e77 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 0000000000000000000000000000000000000000..f817bc2dddc8a36300375041fca3db6dad448e2d 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 0000000000000000000000000000000000000000..b73ac2399197573887a9dc067dbd57604eec3ca8 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 0000000000000000000000000000000000000000..44299ecfe1d2025ca42cc306ac1564ecdf9b1721 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 0000000000000000000000000000000000000000..a4a4399052ca009fa0580223827cacfdb8069c25 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 0000000000000000000000000000000000000000..47dd5841c4d6f31819d5e6c9268734484f4fe196 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 0000000000000000000000000000000000000000..d785b3862926571806196a67dc915db5bb929e05 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 0000000000000000000000000000000000000000..7517ebc4ca53021a81ca3f547672b4fb59110e8f 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 0000000000000000000000000000000000000000..a2d97ef640f8fcff1a3af9e243808621d8f0dc97 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 0000000000000000000000000000000000000000..9bdcfaf6b67b024d570ca69127b65db1e17392fb 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 0000000000000000000000000000000000000000..586ac5649f156f4ebf6b695e3358c31a2d3e35a4 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 0000000000000000000000000000000000000000..f960f05a177ecc4ac10b4756f8cdcfbfe105ffa3 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 0000000000000000000000000000000000000000..17518fc83f514dd9c091907fdf406597d4177e4a 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 0000000000000000000000000000000000000000..ad533b08c59a226cf681e1edca51168f076d942f 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 0000000000000000000000000000000000000000..71928621745db47ff89a4b0668ca58afc035c9a7 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 0000000000000000000000000000000000000000..8d6d43a4177a999c85afc3c7c26b41b9ab1b0a66 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 0000000000000000000000000000000000000000..4895e5b840f5a9d6b83d8dc5e34c96e00a4aaf6b 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 0000000000000000000000000000000000000000..d1c247be1ee42686b951180b3c5b37f65591286b 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 0000000000000000000000000000000000000000..d44c0156810ec05a0ce3205e3cb6e4e1e2143b5f 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 0000000000000000000000000000000000000000..5e03aadec8471d07d2c6e0dfd516b19fef430db9 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 0000000000000000000000000000000000000000..678242bdec55a5f5e05c24e1db128417efb2b53a 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 0000000000000000000000000000000000000000..eff2cb274b3d7311b41e3fb4c78e2c4c1a1f43a3 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 0000000000000000000000000000000000000000..df35324f76dd98fd4b84cab8c78a8f24ab0e81dd 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 0000000000000000000000000000000000000000..edc69aaad0e863eb72e6a5a9b1fe57529364e679 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 0000000000000000000000000000000000000000..75eef5811c3691819ca0173dae72ec8677674bba 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 0000000000000000000000000000000000000000..95078a83e15b9e5277d91875984debea766af4c1 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 0000000000000000000000000000000000000000..e7d6164ef5001a3c17af919d41431e1b7801c793 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 0000000000000000000000000000000000000000..00f4e7db4a4b570bfbac2b4c4c3edc9bc94b6676 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 0000000000000000000000000000000000000000..31c39e268b1a6b33157d3c719c02fa37e47c5e29 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 0000000000000000000000000000000000000000..06a5002d41e51bee40e97ad1455b14bc6147b661 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 0000000000000000000000000000000000000000..f8676957ab9818b6d3f3e2ba20e05016f7ad29a2 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 0000000000000000000000000000000000000000..fe190868da55971cd3cc59dcc737c5b9e8aa5e25 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 0000000000000000000000000000000000000000..18f05e72d25f96a17c83238c749befc0939c971c 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 0000000000000000000000000000000000000000..100a46dc60a477f325d80f895f3a991c0cf32948 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 0000000000000000000000000000000000000000..c6391b748802ead4fe5f0b51c561d3fe14e1567e 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 0000000000000000000000000000000000000000..89d822ab76882c73620bebabea523c11f6ce2448 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 0000000000000000000000000000000000000000..7c54de5331894039222611e958b370c4d1d9eebe 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 0000000000000000000000000000000000000000..6b1c1a735072a04500bcd921c0cdecce623e5eaf 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 0000000000000000000000000000000000000000..cbdbfc7b51aaf4005b79c5e8d0538e90f22d0366 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 0000000000000000000000000000000000000000..749d8e10e1ff3429aa0e95283c2a4da0fcc802fc 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 0000000000000000000000000000000000000000..dc4ea668c688f166406e8c85bf8e3730e34aef6e 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 0000000000000000000000000000000000000000..b6f66638f25d3022ff889ef684ebdb647f6234c8 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 0000000000000000000000000000000000000000..e1dd7e23dc7f7cdd17500499423ef91994d3b49e 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 0000000000000000000000000000000000000000..96562da9fbe164c8300a455b02268763671fdb5c 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 0000000000000000000000000000000000000000..b96dcb199f375c159585870ab72d6a1d6ee2a714 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 0000000000000000000000000000000000000000..724e98c7acc365136848f7216f690836b781b702 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 0000000000000000000000000000000000000000..56fc9bad9b5b6b789ad971eae19c4e8239b974ad 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 0000000000000000000000000000000000000000..1a0caeed7ca43e4e722884b0a4ac111066d8d903 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 0000000000000000000000000000000000000000..c61eac53afa2b62491b3cd010b15e5eb8fadba70 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 0000000000000000000000000000000000000000..d0cdf1cfffe4cc14c97c8afca0fea98795a8cd32 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 0000000000000000000000000000000000000000..f4c2ea6653bee22506935143da2e38fd8def4864 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 0000000000000000000000000000000000000000..31898970da0df06106a5e1f8c52f718a992a174e 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 0000000000000000000000000000000000000000..14558326ef16d4ce09474502a945732feb1b7191 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 0000000000000000000000000000000000000000..e5d94b227bccc1604682a0b0ee03b377ed3ebc42 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 0000000000000000000000000000000000000000..7270cfd37e0cfa3af05df3298d5d2fe0314badf1 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 0000000000000000000000000000000000000000..4ba55421146b4d41c7d51f6a5ab29f6441dcb5d2 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 0000000000000000000000000000000000000000..fb2612d08a5e3b5a073c5baca5136e9b79ead3ee 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 0000000000000000000000000000000000000000..b2456d4770cba2dfd46dce6beea2a61804237fab 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 0000000000000000000000000000000000000000..5964e15472cacdacc318bf750922a2e3edfec6d8 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 0000000000000000000000000000000000000000..da516cce4a48da629d4714fcadaba656b5acb4f9 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 0000000000000000000000000000000000000000..1aaed8ba5494cba319470289b8f2b211dc059161 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 0000000000000000000000000000000000000000..cdbd717db4295d2eb9b321054e06904effdf9cae 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 0000000000000000000000000000000000000000..6330bb33f99873f5d4de683351f0810f7bc9f98e 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 0000000000000000000000000000000000000000..b4a86f51344075b36dfa01844c8c00bff6d9dc20 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 0000000000000000000000000000000000000000..ca55f15d30c7e9758947cd8f98ab3c5a46eac6f5 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 0000000000000000000000000000000000000000..29ea8a60b4d3d8996b68ed8116324d29dcf3f9ac 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 0000000000000000000000000000000000000000..5d2cf0a2f2b58747bd3f1eec7e942d3586294147 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 0000000000000000000000000000000000000000..78d3fcb3a24e765c18c30aa38240177c5bebc73a 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 0000000000000000000000000000000000000000..b8fbd9f359987e283325b636e04cc156a1475a02 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 0000000000000000000000000000000000000000..a2909151ee1c58cff12f83cbb9592ae38ea34d85 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 0000000000000000000000000000000000000000..a3fa295a5d273eecbb2c705030b285c15f21a98e 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 0000000000000000000000000000000000000000..efcd3aa2b58c36b385d3e2aa6a959a4140ed946e 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 0000000000000000000000000000000000000000..35437d44f403ccde8891870e4637992d827d6067 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 0000000000000000000000000000000000000000..dbebc611902df45992e52d07cffccf3efb00e859 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 0000000000000000000000000000000000000000..ba6f75147df4a21260d9eb2681811e3e21e90138 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 0000000000000000000000000000000000000000..609499d96f83555a7595381867e7604f3beb56ae 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 0000000000000000000000000000000000000000..3b17edc99327d3044002e6dc8ca11d8926132a33 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 0000000000000000000000000000000000000000..ffbe973527ad9241921a0c79318662a520cd8a2f 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 0000000000000000000000000000000000000000..f9d21568b4453824a31843acd6e499569a0860f4 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 0000000000000000000000000000000000000000..3ed2c56926d2bc60ade066836b494557a1a098bf 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 0000000000000000000000000000000000000000..53bbe45f772c0a16d9a21806c5deef73ce98d993 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 0000000000000000000000000000000000000000..dafb498b54a8a1d9c3e5934c0832c848c777ef82 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 0000000000000000000000000000000000000000..734b1b6767a69fe053035f1bfb2a9e39f1b62170 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 0000000000000000000000000000000000000000..42c0c6e05bca27f3f2baa2baf3188f797881f5d5 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 0000000000000000000000000000000000000000..d7b97208966557b07ccc7d16740441cfd821722f 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 0000000000000000000000000000000000000000..302c41b705b92cce46b8489c772a5f0e5b1e67f1 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 0000000000000000000000000000000000000000..aea405ed1c3f9049530a4473aa048b9e1372ba30 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 0000000000000000000000000000000000000000..dac73cbcfd4851c026d0aac0d1d497c0ba34598a 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 0000000000000000000000000000000000000000..d20fa2107420d80e9cd1db8402720a6a3302e932 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 0000000000000000000000000000000000000000..14f379d71697a69713a7b3d07e41b5e15725832a 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 0000000000000000000000000000000000000000..7a135d32816b4e271e0235845ab325421ee06680 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 0000000000000000000000000000000000000000..4a94aced1c754d534f0b53a2b54c24b8c3dbde05 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 0000000000000000000000000000000000000000..4c3219aea7ae50aea960b17c396d868f145f0700 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 0000000000000000000000000000000000000000..ce8badf1e8edd56578cc8d7084ce3a572fb584fe 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 0000000000000000000000000000000000000000..13d6c16af1a8195b9f43e16a86b4cbe2d713b47e 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 0000000000000000000000000000000000000000..70d98ea40eab8b49b7dc1648a501292fb2078113 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 0000000000000000000000000000000000000000..9679658c8f356fa716aaea3c3f69e3b5f8671d75 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 0000000000000000000000000000000000000000..7563e171437b8692ecce1cbc69c1afb1c14f24f0 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 0000000000000000000000000000000000000000..c305ddb8f474200c3697c44432b3bb6d30034b1f 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 0000000000000000000000000000000000000000..97b99f7504ffe106f3e269746a5283d1719c2417 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 0000000000000000000000000000000000000000..11e4582150a09c830665c819ef8be9b59741cfb1 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 0000000000000000000000000000000000000000..22b810d53025bafcfbeafad9fc245afac15c78b6 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 0000000000000000000000000000000000000000..39e2aad8ddf62efc9e9e115573bfb5f5268dd2d9 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 0000000000000000000000000000000000000000..6932a724e1091e18a53a33db9da4cd92ea0013e1 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 0000000000000000000000000000000000000000..6237f8673992de52691d60d271f3a83680684533 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 0000000000000000000000000000000000000000..2759bd22ab0fdca6a5c8cd9df44d7dedacc75468 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 0000000000000000000000000000000000000000..64384a3aefacedcfb847aceed8e80403eedc6e35 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 0000000000000000000000000000000000000000..03eeda73fdb8c9fafa27c70778cbc279a403a70e 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 0000000000000000000000000000000000000000..fd6d5d77be7e516cc61ecb955e3630cc46efbafc 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 0000000000000000000000000000000000000000..f9302848a909d751d88bc36927bf57142db5150f 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 0000000000000000000000000000000000000000..46282cbcf410a02d7f88a3cb18a6991b87999249 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 0000000000000000000000000000000000000000..e7f0d699a6d37de9c98a1af17c10cfbd8ba9b6c7 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 0000000000000000000000000000000000000000..4353839642b84b2274a6ff17e9631a75c0c5582d 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 0000000000000000000000000000000000000000..da342ad9975be583f5b177eecf426287bc1d03a6 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 0000000000000000000000000000000000000000..1439ba71e7bdcbdf8367b1d9e1d4e96264b83ee7 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 0000000000000000000000000000000000000000..1692b3f37db12cf9af78e524f4136d4fb5330ec4 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 0000000000000000000000000000000000000000..ee8bb90caaee473b418f01983026a33454968302 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 0000000000000000000000000000000000000000..2b6d002b0e40137d7a3bed16d9fca24213eb5036 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 0000000000000000000000000000000000000000..9b41b3cd5b7ca071476b9aa6b14e6f94ca9eb854 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 0000000000000000000000000000000000000000..83f24dfc3aef77e3040a3497294a35b0bf662fe4 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 0000000000000000000000000000000000000000..92e503a2d479712640171ed89c6529d1bbd717a3 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 0000000000000000000000000000000000000000..c63c97a5312983a983fb28be311412d96758dde9 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 0000000000000000000000000000000000000000..b6a14690201b9ec9f820113551df54f8b982db01 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 0000000000000000000000000000000000000000..f8d365d92a1b943bd2239c7d6d30c7a80b9a2659 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 0000000000000000000000000000000000000000..5582ef9cf46d29f5772f48d0b4905e1e347c40aa 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 0000000000000000000000000000000000000000..6c7bd5dd5190890b47d445e7005fd2ee45bed2e4 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 0000000000000000000000000000000000000000..911344b015d24dce38d741a7243d0d3d91371e01 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 0000000000000000000000000000000000000000..7cc2a477b8c8b60602c4db13a752f78ed2cff6c8 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 0000000000000000000000000000000000000000..353e54019e6332be976b35e5aa5fdb7d223d5099 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 0000000000000000000000000000000000000000..b199b3baf783fa1e9789db2c17c5cc6c20d3dfd1 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 0000000000000000000000000000000000000000..b392552364d31c6b589f5e19c1f49793a10cfc9b 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 0000000000000000000000000000000000000000..fd64e0c3028b909de5cf8d991862f855e3b91c3e 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 0000000000000000000000000000000000000000..eefa9946bb8cf064e356370020e665a9948ea3fb 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 0000000000000000000000000000000000000000..e938958bec6b5eefd2fc10d4cc1ea6caf11b6f2b 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 0000000000000000000000000000000000000000..4db4f5dcb0812adc2fe4801efaab68dd2ac1f89e 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 0000000000000000000000000000000000000000..218031ff6b1a5744374c415a8d2e4d5495cd88da 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 0000000000000000000000000000000000000000..54578aee32a0f4cff18e22ace69bc3d0bb1b5a24 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 0000000000000000000000000000000000000000..5a0d243471cb78d12edd25f16d66ade90bb613cb 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 0000000000000000000000000000000000000000..4853bea26694b427957d99948d166e25f63a14f2 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 0000000000000000000000000000000000000000..099d23dd803689a04df14721f99a00f521177005 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 0000000000000000000000000000000000000000..0514e7419b890120e1ff036066d812dc2ae9c752 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 0000000000000000000000000000000000000000..3a4a05f5ce2092eac6364c4a0c5554827c97c39d 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 0000000000000000000000000000000000000000..43ff59359aac555dd96c2894135b450aaef6750d 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 0000000000000000000000000000000000000000..b8c22b4f3214642b3d1eaf605408a61b52a7b0ff 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 0000000000000000000000000000000000000000..8ee6b28f0b27e10e55e966609f81dba8acf410a0 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 0000000000000000000000000000000000000000..c180c5bbd08a5ea3bcc9271aef2db651f67e87ea 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 0000000000000000000000000000000000000000..4e0574006568022195650afd429933644bc031e8 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 0000000000000000000000000000000000000000..c283d4bc697c80bf3ad6a6e615b798944064e94b 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 0000000000000000000000000000000000000000..0e7de3b7191a94af765521dbdeada154c8433298 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 0000000000000000000000000000000000000000..ac503873f23700c703b76eb7b565fea61aff3f4e 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 0000000000000000000000000000000000000000..f7efedabab8dd3230cd0160aa1244d8926add212 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 0000000000000000000000000000000000000000..56dbaaa8643565278b3d54b2d37b2a94253b2d66 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 0000000000000000000000000000000000000000..4a10fdcf10e776ac4584f7cf99b09a691ea4306c 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 0000000000000000000000000000000000000000..ca808c1658523f45e39b43958944c7e710c15fd5 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 0000000000000000000000000000000000000000..e0d651e6101a4a326a85c65c0355458a593b2727 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 0000000000000000000000000000000000000000..4d7fd6dede36280b4deb972f50c294c32a6d1cc3 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 0000000000000000000000000000000000000000..e1cd52e8d5a2ee5ecc4108521c367889cc90ae23 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 0000000000000000000000000000000000000000..079960d9018fc48b83e145552d8d742efba01e11 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 0000000000000000000000000000000000000000..b7ba63ffabcfdb99997ee17f89929a8a81cc3023 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 0000000000000000000000000000000000000000..1251325519217d73b68dd16324f7920abbd1d813 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 0000000000000000000000000000000000000000..b591544f96c6322b80d3ccbc03ffce55b1c05747 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 0000000000000000000000000000000000000000..6a072d63f781e3ba268c03098a1d67dcff5f60cd 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 0000000000000000000000000000000000000000..ed4fa3af346f66d6aff73bfdd5be46f1032be56f 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 0000000000000000000000000000000000000000..78f1e7cd0710bdb2a8df75887b5d09a30f79a175 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 0000000000000000000000000000000000000000..b4fc719cbc238881c7c84a294afbf73623d4d4d6 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 0000000000000000000000000000000000000000..1f0cffd6243c926f8da0a3900756fff971b140f1 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 0000000000000000000000000000000000000000..9b17fe08816425675ca293af4c414dde181e38a5 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 0000000000000000000000000000000000000000..a3e9b1e28c5a88618e7d0ce228649695b59782ce 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 0000000000000000000000000000000000000000..c5410f42e75a33a4be17f6415431aafa7ad88625 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 0000000000000000000000000000000000000000..690f74801c562f44642eb09a2feffbac43276f56 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 0000000000000000000000000000000000000000..da77755de0de3b9c5176df0d1d506c69dbd4adee 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 0000000000000000000000000000000000000000..1f3b92601773d2172fd103666956d6f231ce7cc2 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 0000000000000000000000000000000000000000..198a2657f75342c2efeffd580c94fc268368db9b 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 0000000000000000000000000000000000000000..bd9d675e24c987df4ef049ff8c0ba2ef6d57dd34 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 0000000000000000000000000000000000000000..9ff844de0ee6fb0daa928955a0f840fe06b0a27f 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 0000000000000000000000000000000000000000..d42160e0cb44d73ce792df071d4a29ce5826f7d9 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 0000000000000000000000000000000000000000..1f067c667757ac400b5f7833b5d7dc8234e83545 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 0000000000000000000000000000000000000000..a0bbb8940717203717d3203d6d2005217ccf4659 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 0000000000000000000000000000000000000000..671f46805ccec28f082df30dce5f1326429a5fc6 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 0000000000000000000000000000000000000000..6dd9f378b115b6dd072d1de18a5fb2506676bb2b 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 0000000000000000000000000000000000000000..29973020f23ec4068952651e29cf2f55226d1585 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 0000000000000000000000000000000000000000..0491cf7148f4f24e4ba761035725deef789b7c0a 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 0000000000000000000000000000000000000000..880556acb295ef00506a4fc9a7e7e37cf8374e7c 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 0000000000000000000000000000000000000000..bdae8cfe351cedc47eb96a2e1a72590d6fb69ef8 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 0000000000000000000000000000000000000000..640b4b3470fd49e12e4ab1d9fd4510c3303837de 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 0000000000000000000000000000000000000000..094067bca2ccc32451343724b41278f704712d27 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 0000000000000000000000000000000000000000..c67e26c9602b863bc831c33a6ed686771d482e2f 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 0000000000000000000000000000000000000000..4eb5940dece2ae459323b6403513f199e577fb12 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 0000000000000000000000000000000000000000..824dbbdacd563168b76d5b78af4b9020b637413b 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 0000000000000000000000000000000000000000..54b09b755c2ad9f0f7f0f32c0a6a0e8436172336 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 0000000000000000000000000000000000000000..c454d95ee1ee3cf2943b6394b4e263bd6db51ef3 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 0000000000000000000000000000000000000000..638655aa69f8c66f018cd1759c6636335e63b5d6 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 0000000000000000000000000000000000000000..ef243274f586e943be6e4a478be9a38b47feca67 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 0000000000000000000000000000000000000000..293fb34c295197e92c30db3714e5c490b2c43cec 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 0000000000000000000000000000000000000000..90a854cb5400032bd53240942bbd64a1d54f48cb 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 0000000000000000000000000000000000000000..c145a1bd7b27bc4018222d61d0437f4c4a87e140 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 0000000000000000000000000000000000000000..b342ccd439cb5f0eb42415bcc8d4b7d114fe43ce 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 0000000000000000000000000000000000000000..729cedd46959edbcfc1927634ea2d908125c7882 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 0000000000000000000000000000000000000000..7a698748bb26c1d5b678389096cf33a04aa77717 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 0000000000000000000000000000000000000000..3df1306e2c1e5009e8d6b1e79e977219bec1a590 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 0000000000000000000000000000000000000000..3a625630abba29e34939eb264ce5e1937c021ca2 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 0000000000000000000000000000000000000000..7c1e64c13a5849aafe8b8e4cdc88230fa572a592 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 0000000000000000000000000000000000000000..9e822a1fd3cb24a49bed0caa1c8bd13c84bdad18 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 0000000000000000000000000000000000000000..8ed9ff6ab4622f33671ae343acdc227de0d54a70 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 0000000000000000000000000000000000000000..f001b2a95ba38f86befff60edaf68f69c41813f3 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 0000000000000000000000000000000000000000..5211add40e5501bce8c539639507fdcd5f5629bc 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 0000000000000000000000000000000000000000..6be1234600b56c96666f44785c157bdb7cf1d6a4 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 0000000000000000000000000000000000000000..192c2d7f4346c2a76f97689f852fe672481ae470 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 0000000000000000000000000000000000000000..5f047e3b263fe96ed5c68d8884148108ec8aed04 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 0000000000000000000000000000000000000000..bd3f3cc6f8a909f991218ed692b6ea67b777e751 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 0000000000000000000000000000000000000000..7c799f142555ab8a42720e7a02f5304d15750d5c 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 0000000000000000000000000000000000000000..3a480069024ef34aaaf8de51bca643fdf9d83238 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 0000000000000000000000000000000000000000..3d3c1f4d9de19723911d4d2dd34b8f6d0d52821a 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 0000000000000000000000000000000000000000..e94bbf5d14e0b4d4b6740a28797abb6df84fb0c4 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 0000000000000000000000000000000000000000..7b252d49969bfc7630a572b398ba44a6e479b987 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 0000000000000000000000000000000000000000..964f3ec233394448d7672890cdd42f90fd9c79b9 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 0000000000000000000000000000000000000000..b73d7e6d6d783b7404f5e66deb79552eec40f53b 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 0000000000000000000000000000000000000000..260bfbcaadd531cc143127c4fc9c91cebd194eb1 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 0000000000000000000000000000000000000000..ec90f2d18893f6753b334861d5d0c11d35f80d84 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 0000000000000000000000000000000000000000..8d211e593b6a89bce0066454ae5f628745a2a2e5 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 0000000000000000000000000000000000000000..bc80f466a61718dfc821d5ba3e68d591be18a81a 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 0000000000000000000000000000000000000000..a104189c6d6d2d2cc7e3297ae03189d67c09b562 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 0000000000000000000000000000000000000000..9225d59e550cc06b2be15c01aef185e65a1ce843 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 0000000000000000000000000000000000000000..d96b8edffff3d0e7cfd869113fcceba448fcd0dc 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 0000000000000000000000000000000000000000..04b9d5d990ce59f74a90d40a231b33b212f1ac8e 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 0000000000000000000000000000000000000000..1b2b1278788d69fa641439906b85f43be8709b60 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 0000000000000000000000000000000000000000..b72a8a7415a1c962728c0c0d3fae76a258aaab6f 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 0000000000000000000000000000000000000000..5c27c2219530f680c7f1c17557694b1badfd0e7c 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 0000000000000000000000000000000000000000..714760c1c66b095aa793073f668281b9cd812d79 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 0000000000000000000000000000000000000000..9a0355dbd54919ecfaee93150140ffa065cc2b15 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 0000000000000000000000000000000000000000..4ee5256a2267e5b40b7c7e219e72d388b1d80f25 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 0000000000000000000000000000000000000000..0f1dda5095b94a8f56af29c09d5480f4f31b8725 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 0000000000000000000000000000000000000000..3445ceb15a2c672f1ec97088c8a8e005b2f244ea 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 0000000000000000000000000000000000000000..0088b6532e12499f55bc5a4dcf9f85f97d143287 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 0000000000000000000000000000000000000000..e035a211d8e17bd32709cf95509e888b40987943 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 0000000000000000000000000000000000000000..343b27b6daf687831693e57a992cc84a631f4b0b 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 0000000000000000000000000000000000000000..486f9e924590f142239b0e8b676454588bd5ac78 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 0000000000000000000000000000000000000000..8572f38aca113949232683e4d3e870801238c759 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 0000000000000000000000000000000000000000..d9629cd89cce4fd60c8be87411c7789d5a3f95f0 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 0000000000000000000000000000000000000000..c1f1616d157332fe718cce4a8f9c47a69f05e2c2 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 0000000000000000000000000000000000000000..d44c67e9eb367a041d02bf163b4f1d9011cdbc32 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 0000000000000000000000000000000000000000..e6538b064bf6a0cc133529481843ee598323e46a 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 0000000000000000000000000000000000000000..389055169aa7fc4e968ebe12cf90cf43a93770c2 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 0000000000000000000000000000000000000000..8c4b5d6f37bc53def08bcf4c2a030cf3092c6f9e 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 0000000000000000000000000000000000000000..ae415df6826451635093df4970a9d2e07e8e1fcd 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 0000000000000000000000000000000000000000..731a3d81c98e7c7321fad5c60caafc0039fc8f59 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 0000000000000000000000000000000000000000..b5df1b41e76c2115d1eb3e3b7d1ce997b596cb9e 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 0000000000000000000000000000000000000000..7e4e351007d34819a46cd6a9691c6c459b5dd753 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 0000000000000000000000000000000000000000..ad1af3fcf15665bc25a5ad6e2a265389e50ff6ee 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 0000000000000000000000000000000000000000..f10344d58ba6f130637126825f4c7916c8cc110a 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 0000000000000000000000000000000000000000..528f8a38cccc85021a852dd6467fa6d5a79e6a61 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 0000000000000000000000000000000000000000..e24008fc08541a23dcee99b365923ffb8f44bd1c 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 0000000000000000000000000000000000000000..820ad4b2268e13d7534344e35b7624e5488346cc 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 0000000000000000000000000000000000000000..7e1e66a263e9a2eb28f4694c5be89494360c9ebf 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 0000000000000000000000000000000000000000..e523aa951e31677e719d8a9b278b70d9c886f70f 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 0000000000000000000000000000000000000000..b80e15388f4f7c4fc0a60343cf91d0d1a273e2ac 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 0000000000000000000000000000000000000000..f32f29253051c4da12ec22207bd871cecfbdadfd 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 0000000000000000000000000000000000000000..712ba0e0309ef2c398d0fd0c0119e090ccf4bf66 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 0000000000000000000000000000000000000000..a0a6b7dd8784f183904320a1e8b1c82a08035960 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 0000000000000000000000000000000000000000..01e66841925367d60659114e3b302908c4488f70 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 0000000000000000000000000000000000000000..0b468b86c5266a766a85b93e29f1a187cea822fc 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 0000000000000000000000000000000000000000..9da4b2fde308060138d62b56caefcf75f15aabac 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 0000000000000000000000000000000000000000..b5c1c120a453f9d420f72332e48abb7520ee4531 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 0000000000000000000000000000000000000000..5f6285e4d0105c6cf234211b46d64461509f6fa8 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 0000000000000000000000000000000000000000..3ff4b4629fda1cfc22e0c23c5f620eac2ff75370 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 0000000000000000000000000000000000000000..26c45b4b1e4fe11d8540f6c444ef0681b1de2318 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 0000000000000000000000000000000000000000..19d8fcb090138a56261c4bb3ef6866b214aff401 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 0000000000000000000000000000000000000000..595a6000c8383018cba719937d9dff7b9d930759 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 0000000000000000000000000000000000000000..607ae18074b2e0a311677e0ce9a5b2d1fef0ddcd 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 0000000000000000000000000000000000000000..d02cdca482b28510b6d164650d9a6472f674c4e3 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 0000000000000000000000000000000000000000..8763f4bb8ff9c5a3ba990451222e98d88eb2c2ff 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 0000000000000000000000000000000000000000..a881c802c07563d093004e79e24d5908e21098b0 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 0000000000000000000000000000000000000000..3ecb466e906d9443d3c3034031517c0597d140ea 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 0000000000000000000000000000000000000000..73ca902a5ba5c117c38aa339b58ff9c4152d8e7d 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 0000000000000000000000000000000000000000..15d3b4d0d74d5328f5befdb1a97c7d315342768a 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 0000000000000000000000000000000000000000..d591cc20fbfe7aedda8bb08542f18abb928c4f37 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 0000000000000000000000000000000000000000..0808aeb58c6d6f926ad231a61b095c4c8e1a7dd7 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 0000000000000000000000000000000000000000..c8af4fb33a4eb7ed81e234fafea26d892453423c 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 0000000000000000000000000000000000000000..b6d2ba339f3a14bba53ad7b649555af7e2e5a3a7 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 0000000000000000000000000000000000000000..a2b535dccff9d1f1030ab1516524da4a4aabd8c3 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 0000000000000000000000000000000000000000..7a385cf760aa84160b05808befd9306391c1d96f 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 0000000000000000000000000000000000000000..1d77b178b73cd0159dfcba437a9f4cb723b180b6 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 0000000000000000000000000000000000000000..a31e2dc9d3509f404291d2bf6affbc9422a6a202 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 0000000000000000000000000000000000000000..569461ab3470907675509d098533d22606f0baac 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 0000000000000000000000000000000000000000..62662518e6e585eb2a2434ca3f23cacee0db91c7 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 0000000000000000000000000000000000000000..45008e63b10ffabd2168a3846dcb4ee0725d67e5 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 0000000000000000000000000000000000000000..90722d3b4c623719cba041a2733442f8b616b234 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 0000000000000000000000000000000000000000..48cf0494ef33429c4d888c375aee6e447c92c5df 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 0000000000000000000000000000000000000000..ec82d1baa8b162d58737c34c3532d5fc98756537 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 0000000000000000000000000000000000000000..6efa2e1dd79d9def7ef3195d1582d1b6d1f35f3b 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 0000000000000000000000000000000000000000..b8d38ce165ec2b240f9779ea3e4721a6cc30e3b2 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 0000000000000000000000000000000000000000..48080c003328d27b362b1b61876b1276b55a72a7 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 0000000000000000000000000000000000000000..311fcac9de3b83c62c0f8b5807480c0bef69d08e 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 0000000000000000000000000000000000000000..df6c9c889f33ba2df97d643ca0e97609738b07cf 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 0000000000000000000000000000000000000000..5c8d0d903600791ac2635a1a860dc877c6f2dd60 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 0000000000000000000000000000000000000000..f5cf5675410a0d4f0487bd6229021832acac3913 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 0000000000000000000000000000000000000000..e6297f28b1b78c0499a7b9524ba544dc1653169e 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 0000000000000000000000000000000000000000..dc08c82d3dcb6fa6deeb9944adb078ad40b7a91f 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 0000000000000000000000000000000000000000..886fdd62d5d6424b2e44c04723c6a70e16dabea9 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 0000000000000000000000000000000000000000..662c1b57f467ccc7a1cd353810538555fbfd26e1 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 0000000000000000000000000000000000000000..82738052c20ab97ebaad25b8867ced68f01b059b 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 0000000000000000000000000000000000000000..5eb2eacfc3279f4e3e30913d4f54eaa98324087e 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 0000000000000000000000000000000000000000..cc793eda68669fa822adab03506f0377dd1292fd 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 0000000000000000000000000000000000000000..70f4d51cec9077d664a45a3dd7a782797527027c 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 0000000000000000000000000000000000000000..a588449b6858c0e1e00e8ce41bde72b0048a1397 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 0000000000000000000000000000000000000000..ea499222a33591bd0d4c1e3ff3749d36ccdde1f9 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 0000000000000000000000000000000000000000..2e87f9eb0e0c3a46db774eccdcf137fe98aae82f 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 0000000000000000000000000000000000000000..03bf509fb261eddfa4a205d89375fa05c6858ad8 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 0000000000000000000000000000000000000000..1da5d33e8115bf6afcef5c6d1a3b3a573d0dcdba 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 0000000000000000000000000000000000000000..41fd35927c6c4c51d78387ab6706f9af88c41a1d 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 0000000000000000000000000000000000000000..506ec7e5059f85aeec14ba5fc7137ae5db6a3f68 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 0000000000000000000000000000000000000000..ed7fbd5b3340b6f7ccab24252b80bf23fae4e2ee 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 0000000000000000000000000000000000000000..62db5059fbd6dc2528761abe1e15a7a840c6b194 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 0000000000000000000000000000000000000000..e4bfcb5e73180a82560d2d12f608a2e3c25a37f3 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 0000000000000000000000000000000000000000..867717287efe0e7ead4aa4f14380db4c6763652d 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 0000000000000000000000000000000000000000..5390223a17319914113e1194f305f0ba682079f9 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 0000000000000000000000000000000000000000..b7cbfcec14d5ebba9b20260ccf721db430e7e6fd 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 0000000000000000000000000000000000000000..539e13bc574da716fb940b127b48c4229da2d546 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 0000000000000000000000000000000000000000..ed61f9008b08601650b6e786b23a4c658a4adf97 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 0000000000000000000000000000000000000000..7e07db2de564ab17a85fa86d2eb4efada10244f0 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 0000000000000000000000000000000000000000..b330009d037f641657719c4b8f828da0a4c20b1e 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 0000000000000000000000000000000000000000..7b7e30c6a06dc55cfd0d323a162b62b02b79ccb7 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 0000000000000000000000000000000000000000..8033fea8844a3c70744dd1b77318418a427c42df 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 0000000000000000000000000000000000000000..fd6c89ea8890e7cae03cfa37df3ac3656df8935f 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 0000000000000000000000000000000000000000..c82de3f6bfb18047a058c67847e5648abef342e7 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 0000000000000000000000000000000000000000..24b3217fe8869bd3e16d4c75c8609eb2783c82af 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 0000000000000000000000000000000000000000..d0d15e4c2242eb561975b94f54a25324f67a9fea 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 0000000000000000000000000000000000000000..2abac5ff3ff9428557a061cedc6cec22dcc55b72 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 0000000000000000000000000000000000000000..21b2b4e4c1571faa1ead3c0e0343f68f1b5dc961 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 0000000000000000000000000000000000000000..7101d7690c4bf78db5c00525b6a177db1dd8bbdf 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 0000000000000000000000000000000000000000..c2202999ca8acf42e0433be8b879a63ea000ea2e 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 0000000000000000000000000000000000000000..0b99a9acb731c5407a08e2818aa40bfca1276809 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 0000000000000000000000000000000000000000..7d7906db9b30fedc99b2ffe3f51701754b61e63e 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 0000000000000000000000000000000000000000..31af8dd685b27a8fd2f3327b6c0a98be5e04c515 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 0000000000000000000000000000000000000000..3025999b9ec8fdc193d325403964ba1d13a51dc3 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 0000000000000000000000000000000000000000..2291ba5d559405d8ffee014e1cbdb5d9a88a56fa 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 0000000000000000000000000000000000000000..ec3091c17410348c22a6fbeb8de53e3fbca80f30 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 0000000000000000000000000000000000000000..db2458475af0d0fdb48a879479b53a0883a24efa 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 0000000000000000000000000000000000000000..3384d51abd69021951009121dff57f459b6ae990 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 0000000000000000000000000000000000000000..adc5d443acbbc1fd31d7bcaa10e57e53e23d8cab 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 0000000000000000000000000000000000000000..0d7cb1a72749276ff18465770e22f11d2c3a3447 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 0000000000000000000000000000000000000000..59451ee9e8d19a9720fcbfd0456aa3e6ec8b4735 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 0000000000000000000000000000000000000000..dd19ceabb5ec42aa5faa1f754d8faeb58d24e804 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 0000000000000000000000000000000000000000..a49aef2f3096cf0336611b620d4e72646b760f8c 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 0000000000000000000000000000000000000000..1a24271e99670a791e94c80a4fc2da2519a7e5ba 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 0000000000000000000000000000000000000000..5a649332e84c5eae7b1495d1a1791133e12a3000 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 0000000000000000000000000000000000000000..c7ba6c0d7cfd5c35b4b28bfad6423b3b7c1cc25e 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 0000000000000000000000000000000000000000..0032230b1e5197a27addd85b93055b3bd36f65d0 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 0000000000000000000000000000000000000000..d4c4a5ffb119d7912f169b8e5af6490ad08125fb 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 0000000000000000000000000000000000000000..ce6871e7b230e9e7979f99c4c1b846ade44cd87b 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 0000000000000000000000000000000000000000..f3869ecf2c2191caa097b4c2b255042c64716ed1 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 0000000000000000000000000000000000000000..4d2b55f73df262430ba2def2eb36ed59726ae28b 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 0000000000000000000000000000000000000000..005b1707c98cc478e81b90223f4744e3503e7c1f 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 0000000000000000000000000000000000000000..681764c347b38611b70b19f725b8be36b36b4c0f 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 0000000000000000000000000000000000000000..fe8545583581a72ffde99d374cf2a1914ef4f38c 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 0000000000000000000000000000000000000000..6b4c799505f3d0b574a65347a3450e5748f6ff71 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 0000000000000000000000000000000000000000..e103f3a283d877dd4cc0d6188b88b0effa031043 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 0000000000000000000000000000000000000000..71b673c8840a92e05a12018cf65c4642d43658c8 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 0000000000000000000000000000000000000000..482427357646bcdc6a7836ce2cc2228a686d4783 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 0000000000000000000000000000000000000000..f2a8243a3ae3900e8697d880739017cfc3c93700 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 0000000000000000000000000000000000000000..12c4d72e7bc3f2c51ad58392c726b6ed3bcb1f78 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 0000000000000000000000000000000000000000..78fb871f2b6d389e7e2349d527cfd9f584768a1a 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 0000000000000000000000000000000000000000..22ee13e6fcbc601254f0884d30846d10babf7a92 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 0000000000000000000000000000000000000000..ab6cb72d95278fa570a3532bf810055d013aa867 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 0000000000000000000000000000000000000000..0e8d580172420a1eb16ca0a85458a465dfc2f375 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 0000000000000000000000000000000000000000..4e7c3e1b5efeb7b510b0a610220360a1ed942b60 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 0000000000000000000000000000000000000000..c0d28ec0ea90c5ba6cf9b7078dc02943f9d44eb5 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 0000000000000000000000000000000000000000..f0a38cbeba102fff6ab60df33c047501a5f7bcba 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 0000000000000000000000000000000000000000..590584c86b05bc1afe3ab698de1ad0e6c4741280 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 0000000000000000000000000000000000000000..af1b15d0579c8f820b1381bf51f34ce607bfc32b 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 0000000000000000000000000000000000000000..3ca15f111ede0451541e051f84693bb4c3e5abbb 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 0000000000000000000000000000000000000000..3d903514c41066f9aba6c81a281de92e9a6b60e9 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 0000000000000000000000000000000000000000..935235467c5abfd336f067f421a98106967268c2 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 0000000000000000000000000000000000000000..4552ab7073f97b5b622e15829956146290e6769a 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 0000000000000000000000000000000000000000..f677496ebde90dac4f180e9395d5a8ca9dc38ac3 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 0000000000000000000000000000000000000000..cde6821b278cc03395feb6a7911d86fac96ae9d5 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 0000000000000000000000000000000000000000..4622472332ead90797ec43be97903a9e941ddd7e 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 0000000000000000000000000000000000000000..3a2560e0434367a39a10f9800af924beb135e5dc 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 0000000000000000000000000000000000000000..91eb401d10c276354a264c9a7172d50ca12b425b 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 0000000000000000000000000000000000000000..05f29035b4b70de585d003209f497cbbff1545f2 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 0000000000000000000000000000000000000000..ef247ccc7d44d2b01dba1ea379da734d96bdf47b 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 0000000000000000000000000000000000000000..2fa936ec11b7fcf581b9fde76a65553633795b47 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 0000000000000000000000000000000000000000..37e93a7d77b393001cd6427587e533f62757603f 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 0000000000000000000000000000000000000000..ba2cdc50d4c955b8bc5a394109b2c724311dc956 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 0000000000000000000000000000000000000000..9cbf88e7a0c96e31fcce8215865b8dc1735a1f1a 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 0000000000000000000000000000000000000000..0b6b02fc7603e288870642984ededc5eaf30422d 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 0000000000000000000000000000000000000000..3e4a854f6c451acb17881bae8327817ee2529e62 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 0000000000000000000000000000000000000000..4774219ba5a5c8b7071e75ded6f231e40e40975d 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 0000000000000000000000000000000000000000..ac24474160bc86a0d6088188be5992cbd23777b2 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 0000000000000000000000000000000000000000..70cde04c3d9a61413a6f978282907478fa42d473 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 0000000000000000000000000000000000000000..9aa3ed0d8bdf4eec59a11ae084b444e0027b9772 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 0000000000000000000000000000000000000000..0f426743aa345e76b23cbf894418311b4451f721 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 0000000000000000000000000000000000000000..5ea57dee5bcc055f301f3a32013f9f2f1489b8d0 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 0000000000000000000000000000000000000000..e719f4abafe982303ec362777b5f70c395c0a59f 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 0000000000000000000000000000000000000000..4d5ab506329e8bc234e74c0066846e78137b5c55 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 0000000000000000000000000000000000000000..c1acd7b3bfc8ac9f2d073b7ecce938719c06ecf6 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 0000000000000000000000000000000000000000..172ffd8fac2550051e95919b4e8e2344f647ef9d 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 0000000000000000000000000000000000000000..33471cf8a636c9d4f5603272f54e99db182382c2 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 0000000000000000000000000000000000000000..a262d0cad73bb65bbb412dc199ebd4f956496eca 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 0000000000000000000000000000000000000000..f76e31206bd6d5d7fa849d5a152afc63f3c14d40 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 0000000000000000000000000000000000000000..5d94b3e0168445e1ff2d44fc4ff0c9b011322da8 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 0000000000000000000000000000000000000000..1ef6442c08a50b8bf42cec0aaabbd3d8ab4ce20b 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 0000000000000000000000000000000000000000..784616fbf31396c20d75d1bc5f26b7a49c329712 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 0000000000000000000000000000000000000000..5136d4f33770b6d897bffdd561013ff353535c0d 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 0000000000000000000000000000000000000000..c9c1093766b4b793e0e42ab7a8b92ffb827c5a90 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 0000000000000000000000000000000000000000..500a87bfe65acfb125b9a1ac404d9dbdb249b6b4 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 0000000000000000000000000000000000000000..f31a220360e7bead749aae27b84a4fd8e2ffba84 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 0000000000000000000000000000000000000000..7b49a44f29b35cb6d9e742ea7087d20ec8252554 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 0000000000000000000000000000000000000000..997948ab05ebf6e37eca05b59f77d73ced7a5e56 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 0000000000000000000000000000000000000000..074272db0e1f8370c19e031c4ab31273c0ceed71 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 0000000000000000000000000000000000000000..0b38bb635e5ea133b871ebbc83ece4698629a91e 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 0000000000000000000000000000000000000000..66623ab10a426fed98eb7083074de4565aeaac23 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 0000000000000000000000000000000000000000..609fee67623dde30de67089648d7b34131ff5e64 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 0000000000000000000000000000000000000000..20e2dc075eb0d0c44f114c5519482e970e095972 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 0000000000000000000000000000000000000000..ba558cf57625e0e9aff6b2dd1b5e1a7d5e4fec56 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 0000000000000000000000000000000000000000..e67362d6d955c06cb4de54432e8c7aa1fbd8ae66 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 0000000000000000000000000000000000000000..e04b86ee38a2fc73abf3462bc03593b7a279ea2b 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 0000000000000000000000000000000000000000..2f67d492dba1a4a0e67c83aa39eea75dcc398837 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 0000000000000000000000000000000000000000..204f5d9caa028ea88c7a974063a97e5dc11fdf99 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 0000000000000000000000000000000000000000..ef0b4c6330ed2fc52242477de81664bde1579cbe 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 0000000000000000000000000000000000000000..8b5fec7f9df09ae09da11fb2958a3fd2903ca058 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 0000000000000000000000000000000000000000..c4bf6031a2db3245cf7ad6bd65526c1a8f16628f 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 0000000000000000000000000000000000000000..3b0467509f90715beb66d415236af37a99f27ff9 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 0000000000000000000000000000000000000000..28388134cb3ce3c30972543edf56b030e8354284 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 0000000000000000000000000000000000000000..3f168288ac3fd7b179facfe3dcaf7a7ba90462d6 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 0000000000000000000000000000000000000000..26679c3ea0a5fabe98f5dab2dc27f1822170e82c 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 0000000000000000000000000000000000000000..7f95417008336c2cfb51a6cc080c0720ff1c150a 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 0000000000000000000000000000000000000000..efb33df144bb21d7468d1093e4bed49f2b0ad76e 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 0000000000000000000000000000000000000000..080a8bc608d45c40fbfe8f7b98fc1483288f6d39 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 0000000000000000000000000000000000000000..aba1965ec92387fda6e6481ebd12685035891fd9 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 0000000000000000000000000000000000000000..a613c7f76a72ec0db75a8739a4a9182161af5c1c 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 0000000000000000000000000000000000000000..b80e437772a1c08270ade6881c7ed9e1970dd57e 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 0000000000000000000000000000000000000000..108d1470ab981d53345bf9e66b474a50075ebea3 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 0000000000000000000000000000000000000000..b7f36c64c93ebac2000669da6c66446833425324 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 0000000000000000000000000000000000000000..23b8292aa5a21de9eda2d255ea82f935eefb9695 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 0000000000000000000000000000000000000000..16c6363b92026cb9ee04cf29b37aabca45bb290e 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 0000000000000000000000000000000000000000..73a17ded8133d417cea7c31ffceb9f539b57aa98 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 0000000000000000000000000000000000000000..dc55d9cc5abb3327ccbecd6743a85139e28bafe4 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 0000000000000000000000000000000000000000..ae3252bbbc6431c44cf8bb132c02c0d5a20009e2 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 0000000000000000000000000000000000000000..95012cbe96ac561009b0a038b3bf6b975c3b7b1e 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 0000000000000000000000000000000000000000..d6de016e2f6a25b25dfa1273052f784fee3c1028 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 0000000000000000000000000000000000000000..757f71c66e505b3b34adec37a46d23253de52387 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 0000000000000000000000000000000000000000..7d262fbb8004c3f8eec0aceb7c9f7880103a6814 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 0000000000000000000000000000000000000000..86d721fba1e961884efc32998928a50d68af8c03 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 0000000000000000000000000000000000000000..a8439cdf45c8b4983088ec34b6de3539eab0d550 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 0000000000000000000000000000000000000000..dc0b8bd116c65b7afee61e90095d2ebec5395f10 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 0000000000000000000000000000000000000000..ba3a2b062380e783e4f570875783b3eee3e0dbab 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 0000000000000000000000000000000000000000..ca0007ef04f86e5cc957febed29a2686b51298a7 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 0000000000000000000000000000000000000000..9c39bd2dd77de825e61d3ec9f584a85ed4cfbe88 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 0000000000000000000000000000000000000000..cd9d4d6a6e82b21db4c5716a855fee868b873e26 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 0000000000000000000000000000000000000000..d8219281166427d2220327bb94acc3880ea4295a 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 0000000000000000000000000000000000000000..015d31727ecf4ca08c231d8d249d7cb4e6a19847 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 0000000000000000000000000000000000000000..7e1d054c557a1082059ecd65e7f0c6087e6d433a 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 0000000000000000000000000000000000000000..cf9199d9d3601dc3548b91bf309201d514fe4ace 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 0000000000000000000000000000000000000000..89b520fd1653911b2c3f64259447b1c628617504 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 0000000000000000000000000000000000000000..88a79a2cdaad4c4482d7b6bf27edc9d82472d1c4 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 0000000000000000000000000000000000000000..3e5e3e9bb58ab1f62f654cc1fad5fb752c568ecb 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 0000000000000000000000000000000000000000..ca1d51d2212ccf03d0e63d0ce128abe45a4d6601 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 0000000000000000000000000000000000000000..f278e1eb11caebfafb840d3a9bfa3aef7567f002 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 0000000000000000000000000000000000000000..ee996c902445680ab084a68d590e2fcebfe7f996 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 0000000000000000000000000000000000000000..c9a2f4c78962de713837f48103528c1825b1704c 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 0000000000000000000000000000000000000000..5913ea483ee1712ff32f79bdd7fd110ea0636d00 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 0000000000000000000000000000000000000000..c1f2cb4a6cd4ee796de5206538d9e683d309e328 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 0000000000000000000000000000000000000000..b0e88e7f3bcc1caeafcbec7566cc7f9ed2bceb5a 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 0000000000000000000000000000000000000000..9a9ac43f7e731147c68d49e836be2975f03202d0 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 0000000000000000000000000000000000000000..75929a5bde2a6a33df30ef06deb8667730be3946 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 0000000000000000000000000000000000000000..f8408cbaaeaba57bb02112d9440481000ac51594 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 0000000000000000000000000000000000000000..25ff54182b5528d823547cb21e4c01805853dd61 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 0000000000000000000000000000000000000000..83bbf41ceb72775f64baad3e768a778f6fa6a7fd 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 0000000000000000000000000000000000000000..c7e072c58af96230b38df515beb428214c13caaa 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 0000000000000000000000000000000000000000..e8e545edbf336b3ab065bc5b5683878cead8ad44 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 0000000000000000000000000000000000000000..01e1724ccd20431bf123ca0bb36ce673a94bb50f 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 0000000000000000000000000000000000000000..0738e3a86b6a99ddd643a33900c41ec94bb0b0fe 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 0000000000000000000000000000000000000000..7e766cf2d17e128229bf72c0692b6ed2f46d0aaf 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 0000000000000000000000000000000000000000..faa19730b2fe1c97c2385be778767772fd152075 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 0000000000000000000000000000000000000000..694f4eaaeff39d4b3565834021cab7eddb75d66d 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 0000000000000000000000000000000000000000..c03862b695d0d172373cc39c77b31860982d720b 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 0000000000000000000000000000000000000000..44c898594d5297a0a656c80d6b2a6397dddedfd8 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 0000000000000000000000000000000000000000..bfcf2cd5a6fba8caf6c76f0c683a3c51c7af0c15 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 0000000000000000000000000000000000000000..80c06651c369f254e0d514159766e280dbf51d8c 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 0000000000000000000000000000000000000000..69ba8a87410b9584bdcbca8830ad7a3f4df7cd73 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 0000000000000000000000000000000000000000..da6ba75ff061a621af7f4afef00c36f931f5403f 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 0000000000000000000000000000000000000000..5af48a725d6cace1fa917f95baebd7b0b1442617 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 0000000000000000000000000000000000000000..91da9917162f68d92472c260a4ec0fc20fdf96ed 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 0000000000000000000000000000000000000000..5ea205f1a710b801e074a4d23f8799a3d53886c9 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 0000000000000000000000000000000000000000..4116bdef79020413eba2fb818d3160c9d3c41f3d 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 0000000000000000000000000000000000000000..c5f0d95cc0a288c8114eebadbb1a539043d37f97 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 0000000000000000000000000000000000000000..1dba90f76f455532cfd3f07a72cc1fce6000f4b9 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 0000000000000000000000000000000000000000..947ae0fda54d947071eca6b3afe5e076bec32012 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 0000000000000000000000000000000000000000..06e4f21d11394adea734d5a90f5759dafb54af99 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 0000000000000000000000000000000000000000..f3c6dfe35c3fdddcc324902991b1327b8ca83c7e 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 0000000000000000000000000000000000000000..7ec5d53fb85a928322fb4b5f20b1be5af51fdf7b 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 0000000000000000000000000000000000000000..1139331a6c379706484fdc6a96323a7ce590db01 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 0000000000000000000000000000000000000000..302a8c57fdd32127c927f7588ca6e831aaf38921 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 0000000000000000000000000000000000000000..ef695693f6cc52b5d38521edb024b0709e65a9a3 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 0000000000000000000000000000000000000000..b5962b347a4bb875e89b4e8c32a23349769ccade 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 0000000000000000000000000000000000000000..0ace2350bdeec4d8e95e5552b071f63d60c03773 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 0000000000000000000000000000000000000000..aac43ba214c01fc6666c45c099b96423f3f05155 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 0000000000000000000000000000000000000000..3c7c91ab24040dd2fe6fff51062a14c581c9d394 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 0000000000000000000000000000000000000000..649bbbcce5230ec8ad1287eba148e942789c892f 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 0000000000000000000000000000000000000000..f82d1282009eb2ff76e34a0e37f36786ca5bea4a 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 0000000000000000000000000000000000000000..e3457715f9fcc59e2223e5322ce6d8631c189dfa 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 0000000000000000000000000000000000000000..60398fe8f4bf5cbafc3113f5a360acdb767493a6 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 0000000000000000000000000000000000000000..054b5ed5281798800c9364fcc8e19df2e3c4037b 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 0000000000000000000000000000000000000000..1357bef328f204fa2498ab3d366696bfae99c52e 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 0000000000000000000000000000000000000000..97ffe43d2325fd542220ea12870d7a321eb9d498 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 0000000000000000000000000000000000000000..c7d67edc484f900339f08298e1ed891cde47c0de 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 0000000000000000000000000000000000000000..1a6faaced1e559594d366262014a14c6ef90cc8c 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 0000000000000000000000000000000000000000..f72b345334aa6fa4ec92beeb970361cc81d7020b 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 0000000000000000000000000000000000000000..5afe1f1cd8021c201932fff9533b6347b8aff9f4 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 0000000000000000000000000000000000000000..042f4ce4de8805fb8b59d432c775d6a15f025cf5 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 0000000000000000000000000000000000000000..89fda99a592f4362b61e6eb68cb2fe4ea24406f0 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 0000000000000000000000000000000000000000..6aa0014c4533887de204919e4a4efbe110bb8d28 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 0000000000000000000000000000000000000000..199261ee9bc0374e1c1359ec113a91d142a19267 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 0000000000000000000000000000000000000000..02a61e1cc590575ccb870c5bd1d28e48751ab13e 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 0000000000000000000000000000000000000000..e3abe5329a4e054350f34632ac96af42ae94ec75 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 0000000000000000000000000000000000000000..63128b6b499ad97a1fb6d0787b46799ef0f5415d 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 0000000000000000000000000000000000000000..e2b9b0a8185dd5e92dbecf78a81f0b22cac43852 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 0000000000000000000000000000000000000000..ec783efb2ec2629fca362668b7e745fade442789 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 0000000000000000000000000000000000000000..544118d074c929431c210dec61312d6871cb177e 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 0000000000000000000000000000000000000000..462bb7eef8f7e6d8b1dd9da422712609a8346c42 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 0000000000000000000000000000000000000000..5a7fe0df3fc306646397a12c15eb08e962581493 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 0000000000000000000000000000000000000000..2a54d6664c72f15d0aec5a386fa90d569060bf4a 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 0000000000000000000000000000000000000000..775a2f81f014c6ccab88d3be1df93c0bb495938d 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 0000000000000000000000000000000000000000..0723725a4f01b044aa2e1192800bc5829fe8d12f 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 0000000000000000000000000000000000000000..489d247c30d76100b7426734490d8888f30bca5a 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 0000000000000000000000000000000000000000..254fe2f43f4040816adcb5e362886e23fdc1f6ba 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 0000000000000000000000000000000000000000..c25f36585b7bd9c41f18c0a3415135f3af61498b 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 0000000000000000000000000000000000000000..8a2bdb993ffda511db2ddcf8448294752cd93e58 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 0000000000000000000000000000000000000000..19d22399bba6ab4b0d11220abd7227d247828be9 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 0000000000000000000000000000000000000000..e61f0843d2737a61c3fbbce37a815873805b8556 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 0000000000000000000000000000000000000000..92d42d5bebcbe63f249ccc72f549c99decd62e3a 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 0000000000000000000000000000000000000000..f3299d400ff3b268f607c96ebfd2006809008182 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 0000000000000000000000000000000000000000..23d340911dccd36d3103c20a8980e6c508509819 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 0000000000000000000000000000000000000000..e7044fe2a8d77bb55824206ebf80d60c2b3387ab 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 0000000000000000000000000000000000000000..7324ee590b67c5346a2901265ee351f061218e2d 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 0000000000000000000000000000000000000000..224c277ea75f1d305b2380f0c34bc9343dee7e2f 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 0000000000000000000000000000000000000000..6a9129fbdc59176d4c6d57acf079f4f1f3eb3079 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 0000000000000000000000000000000000000000..384e79f69311b9b29e0cfe883801910e84763bb0 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 0000000000000000000000000000000000000000..ae39139d6155becb918520edadf364e4dd39509b 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 0000000000000000000000000000000000000000..701937869c608e51bcbccfcd05f14ad275cffd83 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 0000000000000000000000000000000000000000..544287fa352e76eb720b5733236310bd2d11cdce 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 0000000000000000000000000000000000000000..777f49f4bc09ce9f5bc449dafb9593af3fba734c 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 0000000000000000000000000000000000000000..51db722cff418ed062a4fd1221e431b8a36e93e0 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 0000000000000000000000000000000000000000..18677a5e8cd1c35802496a01846c7600748b8d6f 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 0000000000000000000000000000000000000000..4b9f5349d0f3de1825a12c3eb09295c5ed2cf4e1 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 0000000000000000000000000000000000000000..50e724e0db4f15766c3f89418db5c48914f6b646 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 0000000000000000000000000000000000000000..ab69a4f62c33ccd36d3b598020d3be51e239803a 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 0000000000000000000000000000000000000000..afbae26d9396b687c52be5578517dd86a8d46355 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 0000000000000000000000000000000000000000..09cacc721ac795229819f23e659b0e55b4034d99 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 0000000000000000000000000000000000000000..f0ab0938d690beb65f1dd677906a3888b2d20961 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 0000000000000000000000000000000000000000..25befd9d87a97d0def0e4c88be94409bac281a67 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 0000000000000000000000000000000000000000..3352421443c816d27cc96a780adcf7948375d51f 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 0000000000000000000000000000000000000000..a4fdcdba257b6e3a092ae88ce98003a2307154ca 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 0000000000000000000000000000000000000000..e421f0ff67378d3dddd8a45a1bc4b03a528da07c 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 0000000000000000000000000000000000000000..1a27bd6daac167566a84e4254628c830eccaf594 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 0000000000000000000000000000000000000000..bdaa59d613a1c3edfb79a500116447ee34451c30 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 0000000000000000000000000000000000000000..a243f1e38f33d3e58d1e90bec7e348ad5258463d 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 0000000000000000000000000000000000000000..b8a3a20588dfe7df9fef4dab1caaab238ae5e0e3 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 0000000000000000000000000000000000000000..9e097da850c3fff84780ac88c0a3afc71a402f24 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 0000000000000000000000000000000000000000..128bd990a5c16845f9f2de3e49db467c57bd02f2 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 0000000000000000000000000000000000000000..98f8d314a94cac393cb159135f139d605ed0929a 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 0000000000000000000000000000000000000000..e548835924641ccbb5a2a1a6809cd032d20ec5fd 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 0000000000000000000000000000000000000000..ed86ec200ec845205d8b3909104d739e9c79bf03 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 0000000000000000000000000000000000000000..745d4045e8c167eb2ca68e538941eb042165449b 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 0000000000000000000000000000000000000000..517c376a02a1df060af74e2b9f8ca0650e922beb 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 0000000000000000000000000000000000000000..23a126af4d5cc09b62dd6b8dde0c5e2ba38167b5 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 0000000000000000000000000000000000000000..5be881f087235be751228c66536ea9946146aabc 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 0000000000000000000000000000000000000000..a4bf0be12cee4053cae84424c41dba9675c5e328 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 0000000000000000000000000000000000000000..358257f5899bbd94f73a1151f984a21af2823e30 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 0000000000000000000000000000000000000000..2b256c3bf7deceacbb8ca3866a0f34468ae8ef0b 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 0000000000000000000000000000000000000000..35440f5cc45524b2353d11872bfa6e7b53065c37 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 0000000000000000000000000000000000000000..70f1d9023d313d74f738a076c9c1db36fe9ef280 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 0000000000000000000000000000000000000000..349a3198e8b6dadd640b47b11c9ceb2e8c6813dc 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 0000000000000000000000000000000000000000..729a91d9723da9f7d014d9ecb589b3920917b945 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 0000000000000000000000000000000000000000..3362a9ef736b53ae22dae82a086a4953b47754a0 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 0000000000000000000000000000000000000000..844e5f5a5244152dc0414fcd17d3ae1b24e39638 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 0000000000000000000000000000000000000000..1b987d2bd54b33a76a831f32ddf765d4fcf4fedb 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 0000000000000000000000000000000000000000..d21e8b091c97ced17d0e8ccdfbb509f62d9de06a 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 0000000000000000000000000000000000000000..57226f31e8b9306018ad248ee47a615201dc26fd 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 0000000000000000000000000000000000000000..9d555a428133cae97776bfcb7f5f5612f778c2ab 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 0000000000000000000000000000000000000000..b6f3432acb4148d9dfdb79e37a30d725a1e81762 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 0000000000000000000000000000000000000000..5d9bb3a25c3f2514f01381b205b12256c272e2b0 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 0000000000000000000000000000000000000000..8535274b01dafda0f839b9bb2313c04dcf35bdfe 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 0000000000000000000000000000000000000000..c7a4e886d7eb07a1b11ae5f179e487369ef72649 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 0000000000000000000000000000000000000000..e4558136a041a496630f6e64f6be72369a955599 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 0000000000000000000000000000000000000000..13fc1dc61560093b3207331d90d7a5389ea2c376 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 0000000000000000000000000000000000000000..04dbc55daf7ef01b1d6acd3a38ca07c85d58b30d 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 0000000000000000000000000000000000000000..352552fcbd846324d7984d6502ac44f411b9d658 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 0000000000000000000000000000000000000000..75e9b2cc90d14a709f00e692761d70a465b262c0 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 0000000000000000000000000000000000000000..91d17a46bf563d2c60e9347ea8db73fd35388444 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 0000000000000000000000000000000000000000..d6240d8b4a012024d00235b5752b994e968335e9 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 0000000000000000000000000000000000000000..43fbd2e25b02795fcdd6286067da72f93e0425ed 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 0000000000000000000000000000000000000000..7a068732a7ab3f6efd5d6d5afe5fae101e215dd3 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 0000000000000000000000000000000000000000..9d15a466f35b8cf3fb2d08d4e4943ea99f5887fd 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 0000000000000000000000000000000000000000..78927ede6e8abbd531ee26dcb9d97a6da67af6a0 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 0000000000000000000000000000000000000000..2056c09f1ba75e7e7080e1242d375fc8e04ef5d2 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 0000000000000000000000000000000000000000..df7a65636ac4cc93ea361e77417507116b49f0bc 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 0000000000000000000000000000000000000000..87ab080f22be9d71c827bde844bcdb8ddb8ec3fa 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 0000000000000000000000000000000000000000..5d41a4f477592c84f65464f0c857106c76146db0 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 0000000000000000000000000000000000000000..1c656a9e1d277434753e98ddb31d8d402608d160 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 0000000000000000000000000000000000000000..5c3d2f47d2310cf10cbb1382de79c869a0bd030f 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 0000000000000000000000000000000000000000..84a759a3388b65b519d994389c4b1bb017f5e7e9 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 0000000000000000000000000000000000000000..02621906ef2f5c88ad0ced45fc8fdc324d7d6eac 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 0000000000000000000000000000000000000000..b355386cbd8f75f3d239f0c81c5178a1b5247537 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 0000000000000000000000000000000000000000..fb48697771f495575e1e3b6042a009878e074cc5 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 0000000000000000000000000000000000000000..fb268306c2fe5129640a1f00fe98c287ea87d147 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 0000000000000000000000000000000000000000..859cbbadaa08bda326841247d16762394e679bcc 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 0000000000000000000000000000000000000000..b40f68ae6cca0616f91990f2a46578f5a7babf0c 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 0000000000000000000000000000000000000000..4749586ed975d1807d0fa03278f16c7808ee8a87 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 0000000000000000000000000000000000000000..9ed1d41fdfbcd6b6aacf4f49464bec96306ee00b 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 0000000000000000000000000000000000000000..b6a1f52f166576a65a3f846f2fad3df90520663f 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 0000000000000000000000000000000000000000..1979b354facde79401cf4e0bb6c8dbf3e64e95a8 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 0000000000000000000000000000000000000000..dfff078c7953b8985a8c20af9237a4eea64df8d5 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 0000000000000000000000000000000000000000..659142cb61e29b06343b63bf527546e371d825a7 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 0000000000000000000000000000000000000000..a8264df22246c913d22f91e08f819c7eb42e4507 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 0000000000000000000000000000000000000000..06e2b582dba1392025bb185bdd07d78ec0577431 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 0000000000000000000000000000000000000000..29821cf6b89126d9c9a1e7a909501da66b78a77c 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 0000000000000000000000000000000000000000..6a44bdf49687fb9c68a60d0b611ef68cadaa43f9 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 0000000000000000000000000000000000000000..9df0dcb61621c85c221077fa453d582329a146fd 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 0000000000000000000000000000000000000000..8bd5cfa3236a39be353c865496fa05274377689c 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 0000000000000000000000000000000000000000..67c8672d062ecb121edeed86a4419b2dea5499e7 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 0000000000000000000000000000000000000000..56850d900223517280c3915f10141e7a9c3b0aed 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 0000000000000000000000000000000000000000..98059d5ba3e1189a34a92d040819716a4441b043 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 0000000000000000000000000000000000000000..f1990259518be72016e1e74351a3bd1d88f8093d 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 0000000000000000000000000000000000000000..c7ce008a8874b752437b58d3768e019a7f4a6a1e 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 0000000000000000000000000000000000000000..61ae06f18e09dab39798c9880fc75fc7237c29a4 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 0000000000000000000000000000000000000000..779e8c1128ede652707e463b288dae754a98eb0f 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 0000000000000000000000000000000000000000..59b6a2dd75a1b068a80ff9906405f76c97646b10 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 0000000000000000000000000000000000000000..d7372f0d63db268606d342ec86197a56f5be724f 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 0000000000000000000000000000000000000000..4f4ed5f9a547928480af0735edc8047b5c75c66c 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 0000000000000000000000000000000000000000..2a0566e3159b4f67eaa97edb301970c4451cfc17 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 0000000000000000000000000000000000000000..1a0ba74ae16af650181198e237df9fd37ee70b6c 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 0000000000000000000000000000000000000000..3cc1efcb962b29e3b8a6cbef62fd93397bfb0e55 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 0000000000000000000000000000000000000000..f53d5ab20ed0187459c48f113d9fe56aedd042d1 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 0000000000000000000000000000000000000000..22e70eb289eea5e797033e7c7e34b51299735e2c 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 0000000000000000000000000000000000000000..5fe6d07d27c16ab14381bd0090707791e9627895 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 0000000000000000000000000000000000000000..261595e0e3d4b164b6cca741b2535bffc8509859 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 0000000000000000000000000000000000000000..85ff21c0a57270e208c8dee9200306afa9b8c8c1 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 0000000000000000000000000000000000000000..415dbb96459ff1fe7951f94de3ff1c90885b7b9c 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 0000000000000000000000000000000000000000..40059c502d858f78949573a54c41f73d4a125b05 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 0000000000000000000000000000000000000000..1d7d3c6f30c5fabd8195d4d859cb4fddb4bc482c 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 0000000000000000000000000000000000000000..bfef4eb49c7eecb6053d0dfdbe6b813d412205d6 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 0000000000000000000000000000000000000000..44b455b3cdb7201f719f2cf745ea19ebe15a20da 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 0000000000000000000000000000000000000000..9af9f22712df2a4ec2c578a11dd0bb7858e8744e 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 0000000000000000000000000000000000000000..30a00f77b000c79fc6b819b9cf14bb9b5e6f3449 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 0000000000000000000000000000000000000000..4a4722d3908c6ab854ac5e74a385e71a1b260f3c 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 0000000000000000000000000000000000000000..4a767513c64eb71876a74b7e43518a895375a4f8 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 0000000000000000000000000000000000000000..162958274217281444cb6f1099492ad73263178f 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 0000000000000000000000000000000000000000..e414691f09863b37510ec91d9d5e989230633a2d 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 0000000000000000000000000000000000000000..2cfa383161877d53762665d1affcd5d6b5ec45c5 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 0000000000000000000000000000000000000000..abc9c356db2c3ef60b7522466a3824ea291fdfbd 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 0000000000000000000000000000000000000000..23937dbe0fd4e6d69c9756ea0844a875cbe73776 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 0000000000000000000000000000000000000000..d3f6f2441caaf95354fce35275296631b944f82c 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 0000000000000000000000000000000000000000..6505c28164de7ae8566dc8cb2342164602bd6117 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 0000000000000000000000000000000000000000..36faf56f50d1c1ed4e42921f2fd9ab92e7cf30e0 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 0000000000000000000000000000000000000000..04534f00b937392ce1970a19510736dd212e95ab 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 0000000000000000000000000000000000000000..d29a7c5d9dbe17f9951c3110902b8351b3818cb0 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 0000000000000000000000000000000000000000..81aca1cf684056d75e757edc43eea3c13f1a017c 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 0000000000000000000000000000000000000000..58612afcb47e2b777f7826f0604a27546d951353 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 0000000000000000000000000000000000000000..64212f9177695290f4c6a8142b653eca386d9ac2 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 0000000000000000000000000000000000000000..9d1c5a48fb90bc5ead1625fb95a2399d69c53336 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 0000000000000000000000000000000000000000..202bbb6fc76d7641a90b4175aab073f3c0edfe47 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 0000000000000000000000000000000000000000..7e4730668b05f311539262301e4c298ff9a78ded 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 0000000000000000000000000000000000000000..0a957c51808cceae93b911e642e359d341a88be3 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 0000000000000000000000000000000000000000..bda048dabf4fe2d49737110e95201bbb6d05a204 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 0000000000000000000000000000000000000000..0f9009df3ca1c9e349ea164cc42829dedd9720a2 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 0000000000000000000000000000000000000000..dbcafc513d699126b0b13a45f197b3d2b76f7c6b 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 0000000000000000000000000000000000000000..3d8b0cf7af36f5eed616e477c8b2e73ce536643f 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 0000000000000000000000000000000000000000..fd4e75d91a3d52570ccbf67f229a4412dba2f7c0 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 0000000000000000000000000000000000000000..030da3622d85b81e3de75c1baab5f66718ad18e2 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 0000000000000000000000000000000000000000..db3dcd2620d308d3388076b289201e4563933ca2 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 0000000000000000000000000000000000000000..25e7f61848b0e539bc3939f8f42dabf1e55ca400 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 0000000000000000000000000000000000000000..1dbbb6ebf151e312a6dc2fe100a5c228effa30d9 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 0000000000000000000000000000000000000000..02ff019f01aa855099b277a1ec628f3cb9abf378 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 0000000000000000000000000000000000000000..405fab707714a4fbda0759dc0b019bbd32c82a3a 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 0000000000000000000000000000000000000000..cf0feba5d0dd6c7cf679c9a087cabaa7111dfc05 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 0000000000000000000000000000000000000000..96ed2e2716362a51bec69fe2a844ec373378e7cb 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 0000000000000000000000000000000000000000..7a16f61d960a014a873a368a0a65734cae0bd96e 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 0000000000000000000000000000000000000000..f5141321249b2a45418a1c0c39a013f130b8b5f0 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 0000000000000000000000000000000000000000..dea9c705a1eed8dc145a28202033cbb25d522deb 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 0000000000000000000000000000000000000000..90bb26533a178178d53640370a0f674ae09675c1 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 0000000000000000000000000000000000000000..9ce335bd2623a8b77819c82edd75a94d8eb8e3c6 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 0000000000000000000000000000000000000000..581f640dc617e9830446fb963c18e33a5acd4054 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 0000000000000000000000000000000000000000..b05b1bc91f79e6ee0b1c24ccf9ae302d0211b965 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 0000000000000000000000000000000000000000..0b35d5826ab4b87fe1d76bcfad396ad8a2d8ab98 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 0000000000000000000000000000000000000000..499da95a92cb690446aff57b10f123a02da31484 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 0000000000000000000000000000000000000000..4f6b814b0e7d43f2e16976a6be60e9b576c4e8c7 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 0000000000000000000000000000000000000000..da4018fc8e6866239d450f9b7baaed933bf4b7ad 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 0000000000000000000000000000000000000000..baa63f6dbc87cdbf5321c49e6134938a21975e03 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 0000000000000000000000000000000000000000..a470c31e931997ac0a725f2ee78247590bd57315 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 0000000000000000000000000000000000000000..35203edc7f186c5bf437b05dc7dbcf95fdd0f9f1 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 0000000000000000000000000000000000000000..d20198304c83f804525e1012d55953f5273cdfd5 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 0000000000000000000000000000000000000000..9182502769db2cc9ea8900801ceda82de6d3dcd7 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 0000000000000000000000000000000000000000..0285ca256aeacce6ae6f3a12948121c689754faf 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 0000000000000000000000000000000000000000..347b624a18ff86c5b003d4a5e82b09553d0c0416 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 0000000000000000000000000000000000000000..59ceb40822fb2b6fec365a82398da5528b6cd309 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 0000000000000000000000000000000000000000..2b94264220f49b5bb31d8638f37e171ff54cecdf 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 0000000000000000000000000000000000000000..f58971db0bab926053c388810e34580a61ced6f9 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 0000000000000000000000000000000000000000..56d84fe710bd8df2d9a5f73157c01088e6d3126c 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 0000000000000000000000000000000000000000..4597927ed0938ad3064febdf21fc67fe444334c6 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 0000000000000000000000000000000000000000..1ac96bd66f94c67aeee68a561395995c557fbbb4 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 0000000000000000000000000000000000000000..d5f54ca92c2272002a514ef8beb9cd67a5ee34de 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 0000000000000000000000000000000000000000..dba98fea29d5f64b7bd1ff988662d39735adb2fd 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 0000000000000000000000000000000000000000..f6fdf1baa84f897423372fcfb61e08a1c13bc9b7 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 0000000000000000000000000000000000000000..cfb201f6a672287c541573a842611a62ae5acfe9 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 0000000000000000000000000000000000000000..ff207074b75b8fdfb67fd6d82b400ffe672566a8 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 0000000000000000000000000000000000000000..663478cc5ddc34d8eb0f3ca0885d0894b0e8209e 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 0000000000000000000000000000000000000000..3dfa39c7af10ccbccea570f6f3c567834f7f1c6e 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 0000000000000000000000000000000000000000..827c426c157bbbf8aa5950758a061f66df932d98 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 0000000000000000000000000000000000000000..52a357469f842353240c64a6ecdb595a57398940 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 0000000000000000000000000000000000000000..881d0001f12d8703e72dd0e1e0d07fac93bb3a6d 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 0000000000000000000000000000000000000000..3d7dfc0f0891bd7b13304625cfde13b33c69a024 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 0000000000000000000000000000000000000000..0e0b354b4217cb7906a8a8ee69c8cc8ebf6059c3 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 0000000000000000000000000000000000000000..367819100f1034aa7c2a8381e63602ed24baead7 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 0000000000000000000000000000000000000000..96c3ba123c2a31dac63bb95041b36e067c0aa9e8 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 0000000000000000000000000000000000000000..f444cee9d0f80cced9f6b6771aec957b2537d6a2 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 0000000000000000000000000000000000000000..fdc743e64cabc495503a947eeae186720d766b46 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 0000000000000000000000000000000000000000..867f9d1ba1f9f44371db8fc3fa146a7210de1b70 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 0000000000000000000000000000000000000000..65c5ee6addc7ca4a7187b5822bb633aab0ad1451 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 0000000000000000000000000000000000000000..4775df9dd53e541b4c610377cd00e928146e9bb8 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 0000000000000000000000000000000000000000..fdd999e5129adf7c2c31f00d88ac04bee93dd4ad 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 0000000000000000000000000000000000000000..dc3e7efbe021f9d0b6ee87091464ddcf77a71a54 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 0000000000000000000000000000000000000000..82799295981720565653cdd9acf063743c2c2693 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 0000000000000000000000000000000000000000..e2a01e5b9daf18da84d64009fbddf00b79a55e6a 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 0000000000000000000000000000000000000000..7aa421bb5bac24094b38455284b6d28250acd1a3 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 0000000000000000000000000000000000000000..705053ee90bcb8bf87213675738ec24ebd72573e 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 0000000000000000000000000000000000000000..2e3debba1a90c4b4fc9f5c0cfcd0d58c8c181857 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 0000000000000000000000000000000000000000..9b5e4102b7c212ad7d4a1efae85e3b7d38564b2a 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 0000000000000000000000000000000000000000..2a0ead697cba4b5fbe6b4410cb7d070e64738dfc 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 0000000000000000000000000000000000000000..a6b7c06543ec646eb9038c9e0d0080e0a505bd82 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 0000000000000000000000000000000000000000..ac503873f23700c703b76eb7b565fea61aff3f4e 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 0000000000000000000000000000000000000000..e66097840a1216d946db7f0248d7df2f2dda203e 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 0000000000000000000000000000000000000000..1672e71420a4fd745844032708268fc9271fba01 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 0000000000000000000000000000000000000000..b50f09328b30519f3c88379f911e06c4dd4f4e90 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 0000000000000000000000000000000000000000..8d9b2c2a61db245ae489b9dff7ede1db084520b9 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 0000000000000000000000000000000000000000..13a8c23fb04f487abac546ea1c3bd9c5484ab169 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 0000000000000000000000000000000000000000..91e0fc436c8587d4ab2f6e4712c17337a9b27af4 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 0000000000000000000000000000000000000000..32777b5fc47e464d52a99656a794bc0c4d121a4d 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 0000000000000000000000000000000000000000..5a8da5c0772b4dbfa383be54f3448acb8e292798 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 0000000000000000000000000000000000000000..b4165216c51c9f3038fb25fbaf8f2082b92973b0 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 0000000000000000000000000000000000000000..a2feed9d291b3ab9dd2063f630f584188218ed56 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 0000000000000000000000000000000000000000..4c38eac56507f6334a05bfbea6e5a00d793393a2 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 0000000000000000000000000000000000000000..630d17558769572711999a362f9bffc02eba23fb 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 0000000000000000000000000000000000000000..51e591ed1073add8ef822832f5df544f32787410 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 0000000000000000000000000000000000000000..950d366a0d5ec16a02eeadd68f0101b28488e247 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 0000000000000000000000000000000000000000..f7d356c12a1ba24513f15eedb4ad187796cd379f 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 0000000000000000000000000000000000000000..034af73e0a65685b4a50310cdfad2d245e05d280 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 0000000000000000000000000000000000000000..3ca86216d96645228456934ac9ea6dc2e359fd94 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 0000000000000000000000000000000000000000..2d515148e547b14b81afc03bf79196488f7b880a 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 0000000000000000000000000000000000000000..e15da367d70610955356a0dfcb668dfd328a7ff1 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 0000000000000000000000000000000000000000..ab98d0388f7046dc9530ef661614aa77060d81d3 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 0000000000000000000000000000000000000000..69f49bd4a4fbc6ce37b7f44710ec46285efcd534 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 0000000000000000000000000000000000000000..0fc9a345e34a74796931d41dac6eaaf772df6061 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 0000000000000000000000000000000000000000..dd76d43ab5a19b8925852ed3959f9e3350e37d0c 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 0000000000000000000000000000000000000000..ef98a2a591695656a2ac2235940c3bcbf59821b0 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 0000000000000000000000000000000000000000..0883f1f5edec55558c1228d1476fb1f2b9ad4dab 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 0000000000000000000000000000000000000000..3cb5f1970c1435cb09ffda361e681bfa1d6e891e 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 0000000000000000000000000000000000000000..a866fa13e1d92927bb25c1671b1b16a3101e86b7 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 0000000000000000000000000000000000000000..05a30367137f45fc9aa524cd199e367c70afd459 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 0000000000000000000000000000000000000000..97845abf1bef556ff660a6d1ab481fdf6622cacf 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 0000000000000000000000000000000000000000..0e41d96f00281b662727110f9f89e222d68a0c89 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 0000000000000000000000000000000000000000..2be0d4c02b80ea7457aba273e7e8a7e192af3ad0 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 0000000000000000000000000000000000000000..f5e755bb469ec4303f6dfe4c82a3b43d094382c2 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 0000000000000000000000000000000000000000..272886e7d1dbe02a977a3c5a64f5ee5d6f948636 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 0000000000000000000000000000000000000000..4e645c68382446f09dc7f67a49f06a69050def82 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 0000000000000000000000000000000000000000..655408d251b5655741c23786392f6d927cad07ff 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 0000000000000000000000000000000000000000..9a5f505b4efd40a8659c7e30310a8eae4b04fedc 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 0000000000000000000000000000000000000000..18722e1e1d3cfbb5877017501c18940a6599f393 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 0000000000000000000000000000000000000000..be014bbcb0c944293c11414d249380f99b18c3fe 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 0000000000000000000000000000000000000000..9ff1e6002882f12e17f3e87a2e75c0c98188eaf2 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 0000000000000000000000000000000000000000..8dc7361371772827b5e1dc58926836c2b18fffd8 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 0000000000000000000000000000000000000000..d06b1ab49ce4114909485e7a33c7e955b53f2146 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 0000000000000000000000000000000000000000..4d762442321284ca88c592b4a23eaf7d0fd7b400 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 0000000000000000000000000000000000000000..7d286fd397b4d637f09d956baa7230112d668f96 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 0000000000000000000000000000000000000000..4f4a87de54ddb7728bace2a78089d193c086f0ee 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 0000000000000000000000000000000000000000..d6a36fca3dfc44e77bff9c00a15f450f23deabf7 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 0000000000000000000000000000000000000000..4ec53ba3173be700047c6900d18e852ee9fa8415 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 0000000000000000000000000000000000000000..d1c49ac6f024e2b2a49782c50f282782124a0cb3 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 0000000000000000000000000000000000000000..d38b83294fa402997ad90592660931346c6ae589 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 0000000000000000000000000000000000000000..c001cbceaed694ad7798c5467df8d5eb1ee7aac7 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 0000000000000000000000000000000000000000..f4fb2fa07c9c6b3fe25c6c5bccc39dc340c373d3 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 0000000000000000000000000000000000000000..109ecec27e7976a027c3e652b161245176e29d48 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 0000000000000000000000000000000000000000..5d42ecc95e09d4626682d040ec9406449f72007e 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 0000000000000000000000000000000000000000..bbac0c42843a7c2f9f0dfe508e5d044c1d9a9dc1 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 0000000000000000000000000000000000000000..f0a66d2246de8440f6835fc8c5e5b50f652fdec6 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 0000000000000000000000000000000000000000..d8f152f5426ffa3f1ebdaab0d9e896bae54a7320 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 0000000000000000000000000000000000000000..fe994c74b6fb74586fb2842619a5c5e2d5f7fbba 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 0000000000000000000000000000000000000000..b3b0201d8792b80c575b28aca9dfff823a1a15e7 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 0000000000000000000000000000000000000000..fd4e9f40eafe12bfd48cdad0a5fc3ec1a314606f 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 0000000000000000000000000000000000000000..d0303c3ee0cecd3a6e17f6e1026a54a32f76ed3b 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 0000000000000000000000000000000000000000..e9a28bffd5783d15a67bfd0548b3b785f9ea4b6f 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 0000000000000000000000000000000000000000..55ce07c35894a34e34df3fcebbefe124d8aba709 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 0000000000000000000000000000000000000000..cc1cac35b7730f9f6fb648f3b391fc00151bee0d 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 0000000000000000000000000000000000000000..e00067006c298c782cd514c47358630a0e0992fa 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 0000000000000000000000000000000000000000..e7fbf3c1b03cd5a47ac04e7471354f30735db640 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 0000000000000000000000000000000000000000..069573a67395c45eb2a3d9d53c2ec1e7e25e5540 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 0000000000000000000000000000000000000000..eff27b2e70b126f944e5702174e7b2a7112f612f 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 0000000000000000000000000000000000000000..8d03b4c9b5e6492c989616ffbe8f11d130c3c99b 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 0000000000000000000000000000000000000000..3fb8015784d547ba9f7aca29b4084869e85c5c8e 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 0000000000000000000000000000000000000000..b89916773b1ca5b3a58aa29fc077ce19ee4f047e 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 0000000000000000000000000000000000000000..0ae1184ef25d4bb157bb2915a238b1208dd3c5f5 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 0000000000000000000000000000000000000000..029a9f9ff70c5224316e25f0e04d1ab5eaefd84a 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 0000000000000000000000000000000000000000..448c1d03e7df8af08ada892a73c4c7313e11feb0 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 0000000000000000000000000000000000000000..2e50f81115e0204aa4ef95f23c073b7002a715c8 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 0000000000000000000000000000000000000000..9e6649720ee22f6240d9926b6d50dbfe20420e6c 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 0000000000000000000000000000000000000000..7768be00d3e68fa5990d861e0dabe780684a286a 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 0000000000000000000000000000000000000000..aeedecd97e967364623cd3bde991e97d14ee5898 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 0000000000000000000000000000000000000000..88c5bd16ab93aa6e4a5bec2918befaa1ce1c8f9e 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 0000000000000000000000000000000000000000..2cbd4a9daeb56ddc426666364d74b520d387e455 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 0000000000000000000000000000000000000000..430a60070927dfaed626df92e1fcb1767c2d1def 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 0000000000000000000000000000000000000000..81700c15c7e0211714a02e0a6a2c0922c52376e9 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 0000000000000000000000000000000000000000..96336ea4a8f686bee6e0c1d7117b1c2ec204407a 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 0000000000000000000000000000000000000000..e49d13fdc245dac3558faa452f4f9041f08aa80f 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 0000000000000000000000000000000000000000..792dda1b7e47e4f02b911d1243c2ba3473e2c926 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 0000000000000000000000000000000000000000..238f2a25bde52b7be8c9a94a4f1aaf50f95187e0 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 0000000000000000000000000000000000000000..619a55e8cf5469b2cda790fe544dc49ff0f1076a 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 0000000000000000000000000000000000000000..3f6ae6c2d62ad859f41f4af02e02640e7d33260e 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 0000000000000000000000000000000000000000..da3c8a6785486e7dc71689375248199779b32ae9 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 0000000000000000000000000000000000000000..317a1635ac7126a2bc8238971c4c341bc7005ed8 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 0000000000000000000000000000000000000000..b79b2b9a54e319b4d031f369f0043e9d0bdd3d0c 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 0000000000000000000000000000000000000000..75565a1d9f10849e801869753c11ed5fd542f424 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 0000000000000000000000000000000000000000..0899e37107c2ad646ca4466699f53f14ad00e2c5 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 0000000000000000000000000000000000000000..d3d5905532ede7ad299a88b96a2f0ed2e788e287 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 0000000000000000000000000000000000000000..f1e81378d7b5571655f8c7738089c6a2a3348f53 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 0000000000000000000000000000000000000000..5da333e31f0393ae6ea7792d68bfca6712ed990b 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 0000000000000000000000000000000000000000..93b98a1531814d0a27a699a6b4a98f823d000b43 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 0000000000000000000000000000000000000000..a8ae01ffb017690b02f7bb1b9cd6e5a4a3b2cfe6 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 0000000000000000000000000000000000000000..82ac6ed2c317e1c3532018614cdc417a6784eb5a 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 0000000000000000000000000000000000000000..f215a9a5b1b74c5329f0f5630b5ed54affc629e8 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 0000000000000000000000000000000000000000..9628f8f1494bd15d28096244afa29240bf7864df 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 0000000000000000000000000000000000000000..6b2ef625e06876b72f2ab00d9e86e4d5e4807261 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 0000000000000000000000000000000000000000..c3698b88169da1999d0b8973b7d222d68af25cb1 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 0000000000000000000000000000000000000000..3b7c8395f6d5c07e1dc3a4766529691b9cc0029b 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 0000000000000000000000000000000000000000..7d9b8020ae67cea40e58e364f0815c35fd47a84d 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 0000000000000000000000000000000000000000..9075840470852e7bc79902af15ccdf518a6a53eb 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 0000000000000000000000000000000000000000..11253249ecbb21131da3b973de8ef3c31a219bf3 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 0000000000000000000000000000000000000000..a25fa7af92cf08e5479c4feb5be4b6ee8770fee7 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 0000000000000000000000000000000000000000..424560c1284bfd97dc847b8816b476c52094e5e9 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 0000000000000000000000000000000000000000..332c20dafb356074988183c62c9abf7654ac0965 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 0000000000000000000000000000000000000000..b44bf13471947c2a71f1e102d8eab63d165bd494 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 0000000000000000000000000000000000000000..f67fc96f6f1f7885183bae65b728becd0fd19eb2 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 0000000000000000000000000000000000000000..ff85f59673e091e6f88e0270dcb49691a487392f 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 0000000000000000000000000000000000000000..c1511c960162ea46d5be89c1c367a1a348569eda 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 0000000000000000000000000000000000000000..db4fbae9efcf5c0db3196a72052c3d51f428896e 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 0000000000000000000000000000000000000000..62d0a1e5fc939477a0e6c9ed77a2a28b6b27a967 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 0000000000000000000000000000000000000000..1ccdb931f7b8fda178d46bb3574ca8492e002199 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 0000000000000000000000000000000000000000..b2747665a2083c61a033ff6466443f5a7df7dc27 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 0000000000000000000000000000000000000000..7c0d72d7a9335acd91bb81461859344af15c4507 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 0000000000000000000000000000000000000000..ff47e3bab3d60aa295a88286a02c9475c0c7b59a 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 0000000000000000000000000000000000000000..bfa4cdf95108a84fc197edbc22eb525eb14027e0 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 0000000000000000000000000000000000000000..4f3432441797beff78555d2f675580cac0e045a9 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 0000000000000000000000000000000000000000..74d868b3f025f653cc573154fe49663a3f24f69a 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 0000000000000000000000000000000000000000..3da3d1a0e705bca21e8dd1d52159478d3b8777ee 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 0000000000000000000000000000000000000000..9e1c193ffa65a997268bf778c1dcf00094f60305 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 0000000000000000000000000000000000000000..070118e705513efda3722791cfe6084145b48da4 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 0000000000000000000000000000000000000000..8f3cfb31e91c3cb4f7a23cd0afcdd76a942a773e 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 0000000000000000000000000000000000000000..8ce14d35da70e2c807f8526074ee5b0b0f0408dd 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 0000000000000000000000000000000000000000..5869e8ba31b66575392f533b61746d5638352d08 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 0000000000000000000000000000000000000000..00859f0d4ba82b74a887f5e543d7dea87ca80f45 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 0000000000000000000000000000000000000000..6c13b8161a18ea1645945d5444bd2a1e741cae31 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 0000000000000000000000000000000000000000..d16196d66fe80ff5dfdbf2f4640716c51916fdf3 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 0000000000000000000000000000000000000000..c20db5f647858ec53c2b4c96c7f855afe68abdbf 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 0000000000000000000000000000000000000000..2808d482e630b6afbeba37f629f877a0128bfeb1 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 0000000000000000000000000000000000000000..68fc35b9cc6994579f00513becaf29d316b6929f 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 0000000000000000000000000000000000000000..c049b479305dcef6a54121743e4e6fec6632b2a1 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 0000000000000000000000000000000000000000..ee849c327ce67297d7ca0ccd94bfa476a8440550 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 0000000000000000000000000000000000000000..92520aa4ada2a85dcc76fc0cd9bcdcffd0b5930d 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 0000000000000000000000000000000000000000..5ed21a96d118e8aa8b0465f3c012aad214768257 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 0000000000000000000000000000000000000000..48e9b6b0515c26c3d5c3a8d43bbe4ab2a8f0e6fa 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 0000000000000000000000000000000000000000..915485e50567ed02da8fa53cba52bd99ff845b10 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 0000000000000000000000000000000000000000..47c0b2435059edd8ef1cd3e52932ce5fa63b7764 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 0000000000000000000000000000000000000000..923816c8c1bcdd7cdb0816f6eea94b137900188e 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 0000000000000000000000000000000000000000..4f2327a2b84d813078d6785b0e2bbcbf54a74745 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 0000000000000000000000000000000000000000..9e2f45fa715077110d2d4d287227f3fa41f9efad 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 0000000000000000000000000000000000000000..7ac6837bd649f47ab3b5a94373ce9537ea6f9c94 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 0000000000000000000000000000000000000000..5645bf780541cc0a806c38588645fe3942ba123e 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 0000000000000000000000000000000000000000..299e25b70b355d839ec7a561b317be378ea3f200 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 0000000000000000000000000000000000000000..d1adee5e13c56bc9a11f930a54cf1b9fa3cc280d 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 0000000000000000000000000000000000000000..89db0899bf619f486bc3c28c5637d4f7da44e0fb 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 0000000000000000000000000000000000000000..3f2706b6806eb730feb23d2ff5d62ed53e9d7da3 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 0000000000000000000000000000000000000000..43867a1f795be44c2a4c935b66fcf199933babdc 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 0000000000000000000000000000000000000000..4e842374b96b3890a01d854247764efc2a6e0ba3 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 0000000000000000000000000000000000000000..a7fb0ccf4433250f89c9a2c97337b4e39dbf138d 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 0000000000000000000000000000000000000000..3786dbd5775f3322b8570df8e4a8f50ef2ee8d09 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 0000000000000000000000000000000000000000..044e8131c597388aea8da448cd64dfc5c3a5352d 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 0000000000000000000000000000000000000000..bf611eb345d99de0f958566e305f06d61c430982 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 0000000000000000000000000000000000000000..f4af29b9426df0bd44dd079fea020b9829475b47 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 0000000000000000000000000000000000000000..3f28b4a6d8869da7687bbe0a034cbcd38a417e87 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 0000000000000000000000000000000000000000..dc3efc3cbf0539ed49501c42d110afbe069e2429 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 0000000000000000000000000000000000000000..66a01c0656f8ae1c6347f84138450d8d7ada26eb 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 0000000000000000000000000000000000000000..0d5527f758c9dd15b1d7ed288792bd38bb17079c 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 0000000000000000000000000000000000000000..15c3f14241600a03d91ab29ede1109a96741f9a1 --- /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 0000000000000000000000000000000000000000..9e23d49b1795ce1b52a08ec599062f0fcf8a1f17 --- /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 0000000000000000000000000000000000000000..72ffe9002ce0bebfa2ff25c2cc311ff282ffc1e9 --- /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 0000000000000000000000000000000000000000..0aa142acfda64ac8f3f3c2d8992b6a673003320d 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 0000000000000000000000000000000000000000..d2c85962d3bea4b3fd78d9530820d4407a6158b3 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 0000000000000000000000000000000000000000..176cc70d5431425e5bb2fd6b8b03486b1a609fd1 --- /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 0000000000000000000000000000000000000000..42431a79a6e17271a47f58ae52433108a978a655 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 0000000000000000000000000000000000000000..52636f3be04ff4ce8677fbbbfa2284a237020b3b 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 0000000000000000000000000000000000000000..955d02f6a06fa520fa4ccfdf7ea427213ea24acf 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 0000000000000000000000000000000000000000..1fab46899c710a1c8083bb2f17bef963db84b285 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 0000000000000000000000000000000000000000..5ec9457f8601ef82edb067fc8c1b0c2fff09b07a --- /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 0000000000000000000000000000000000000000..a4529955af351150211ff82dd93ef05e192fc051 --- /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 0000000000000000000000000000000000000000..a0bbd322a67b1333fb4c6ab38ef7817a1d67d8da --- /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 0000000000000000000000000000000000000000..dd4b8243bd3fae43ca786672ab4fba2adcdc58cd 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 0000000000000000000000000000000000000000..6f2c98af68221f822b2f43560bb0b2f7a3ab1a23 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 0000000000000000000000000000000000000000..4881a157a231705b63efcfa4fe4467e2d16b22a8 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 0000000000000000000000000000000000000000..f2f165aee83b7816860e1cdba78a52945bd54728 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 0000000000000000000000000000000000000000..814747d6d95c27930641e156dd50096c7e6acfa7 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 0000000000000000000000000000000000000000..b74edc0d98ddfc272b42793349b1fc907ef4fd75 --- /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 0000000000000000000000000000000000000000..84d742bad9c8261f679c0f0fbb9c487d6045e761 --- /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 0000000000000000000000000000000000000000..b39027169924bcbd1a47f964f145d479df341f5c --- /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 0000000000000000000000000000000000000000..300032cf57db8f518678d5d096bcb97b48426c07 --- /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 0000000000000000000000000000000000000000..d8f2ff83443d8625f2c2183942d5cc8f3d5d6091 --- /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 0000000000000000000000000000000000000000..1d16c9a629cc107b6670daebba39183e519d282c --- /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 0000000000000000000000000000000000000000..41ddae038338868754866962d91c9dba73bfbf41 --- /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 0000000000000000000000000000000000000000..8ab3f64f67b4639abcb35f03278b70c6a7c2fcb2 --- /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 0000000000000000000000000000000000000000..1da5305109c52c22e28f82cacea77737087becc2 --- /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 0000000000000000000000000000000000000000..1e1389789f1633e5d9f4b66340e16648201a07c0 --- /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 0000000000000000000000000000000000000000..9bd682f03279fe626a212060f615855e0e6e1b66 --- /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 0000000000000000000000000000000000000000..ff8abcecf702038d94e477105b6b87941be97a46 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 0000000000000000000000000000000000000000..c09733281647bb73eca1928110fdb20f6544806b --- /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 0000000000000000000000000000000000000000..033cfbf5c8d2941f3be33bc6a7da193235b0dd58 --- /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 0000000000000000000000000000000000000000..cc79dafc280335fd6e1322e72c8c4152db1f072f --- /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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/docs/source/introduction.rst b/docs/source/introduction.rst index 21d039672d89c8865df11150fea9632a6dad309d..bbcf546a75203b373f51e714b30e28f42d32af20 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 0464b728214b80e25880ab30d1a7a3a6b80ad627..ddd4ff1011086dab06f4d04f929f91e44ab5dcda 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 d870e56d05bf09f37fb48f35aefc4ec6bd1337dd..cd0491089b017e413ee569870862b1c5dd9c02a0 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 0000000000000000000000000000000000000000..edbcf26d54a46ea117ae34f88c5f230937f848f4 --- /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 0000000000000000000000000000000000000000..112048db61a0066b2cb01544ee5bf1e7f6498f9a --- /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 0000000000000000000000000000000000000000..8548a085d64b17addd00774150481c54270416a5 --- /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 0000000000000000000000000000000000000000..863e0ffbfe9f01cd25e41175eef6f1ed54950f3f --- /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 0000000000000000000000000000000000000000..3549496876c9fbfdfcdd30241a6a3c0f2a9c938b --- /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 0000000000000000000000000000000000000000..773e8d35487d6648756266a34a488f6173b5ed0f --- /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 0000000000000000000000000000000000000000..5c41d8a75562904c3a801e4842aba86fec312fdf --- /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 6c7582463df16a8d3d286e1e4048631be5d44f3f..61ff4708bd53ea9efc719a73397c4f72f85eae77 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 0000000000000000000000000000000000000000..97b026921ed790a2436f890423c222f7c4ace0d3 --- /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 0000000000000000000000000000000000000000..a3522bf9d04ef378a7acfea53cd8b0b749602d4b --- /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 0000000000000000000000000000000000000000..7923bd3339ed267cc1de15e12028bdc015363c54 --- /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 0000000000000000000000000000000000000000..f386209c8ca01551076dd9702e3d1058133b57af --- /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 0000000000000000000000000000000000000000..09f536f34b9b99648763d43c374eb6da5fb1f6ec --- /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 5fda443dad0b40fbb6ba75376b301da76ead8120..ba85d54ef176857391541461214847d3da2dae77 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 c2aef5cb62af014e6950fdfab639789e0bc219e4..1a39b8ff64de5dc9e6ad6d7699958f578c574d68 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 0000000000000000000000000000000000000000..ddabfa508ed0d7b1d3638309b38d350cb5aeff7b --- /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 0000000000000000000000000000000000000000..02f543dcca1d641e3c511f86935e171cd579fb76 --- /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 0000000000000000000000000000000000000000..99922839ba9c208a13644c772e70aa23ec589416 --- /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 9847560cb854708a59b307ac0ae4ed59fb9e0aab..0000000000000000000000000000000000000000 --- 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 e134afb331eb5fb83474fd26a9e58b9ecebc92c0..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..7c8cd625d14da1aff586c08810048d694f6c2b78 --- /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 0000000000000000000000000000000000000000..1da57e5d253693c551dc2b4d088686a11695a6f7 --- /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 3939418e70f81196ee1ef989f23cb70c60d792b9..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..af0e72d9913b03dac1822a0d56b785b5db58b1a1 --- /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 0000000000000000000000000000000000000000..efa5932b98b02e53d10cbc4976e63d2893ab22ba --- /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 f2beea77d3075c4fb3d7c6dcd8f98aea5f21a0cc..a3d9f4a732f2f92909b97a994f6fcb8ecdb82baa 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 8fb5f3df78f188add5ef83414336aa124f6442b8..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..8098353c4788a14039899ef83d0ad7113a27f6b0 --- /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 68a4aee9969488dde57b93d974eab5e2498106a2..a9fd0b00c5838707fe14915aa317026ec277a3f8 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 eb0e5652d65de61922832f626ba0be4dbd69de76..00689b8aaaaedd0ad77b69177e06087484e5a7ae 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 0000000000000000000000000000000000000000..7650ee8a059efa42398284af36dbc34292178508 --- /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 270547cb5b4b377373bb630eb93850e8ddb1e4cb..788edb963bea8a6993c2b81f2ce0513ed38fc131 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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/docs_zh_CN/source/introduction.rst b/docs_zh_CN/source/introduction.rst index 9eba1a95e570d3fcf6661591ac613c8d12f07c6c..e203088dd91d95b571dd0b5c0af55e26ef6af94f 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 d870e56d05bf09f37fb48f35aefc4ec6bd1337dd..cd0491089b017e413ee569870862b1c5dd9c02a0 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 55285b3757217bb05b6e883886e25f946e866155..6425197073a38554435abfcf9cee1807771c5fc7 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 0000000000000000000000000000000000000000..b4c01aa72a06a59fba87773bfb61a73a0baa8498 --- /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 dc0d8ff166c446a81446728ea3c1309e93ec4a95..8274e03a3e9fbab0e0e833715b3fbb74e282fefa 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 ff5adc2bbd42a9d76d1257d6c9ac8ee190bdd487..c0db7c5d447772c272da94732d756c1615822e40 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 4c79d0ca5fcf74b01c38731d03e7b2011012daeb..b174674971aec648edf58a288d3165b4e7879548 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 40e421cb28bbe08f8a77e050700ee25e7bccb796..dac001b7eb4af2dbe4d94e8ff1f40ba14a5f1654 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 0000000000000000000000000000000000000000..fbb9a51114164bd9628428c37dc256740a13aff1 --- /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 caf396f9fbbdb93e1738c8b741d8b7ed363e89be..5df7015c9f1c08f4c35e0b733cb144fedc003cca 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 0000000000000000000000000000000000000000..22bab8aac64e8ac1608834435d06e3d10cb58f2b --- /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 0000000000000000000000000000000000000000..4e5afd2ab07faee29f950b42661463fc317a0162 --- /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 0000000000000000000000000000000000000000..b7a1b8c5c359b20aca125733d5d87deb5bbad19e --- /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 0000000000000000000000000000000000000000..c8d47c6a9c4a19c5bea0d7569798bf2ada78a320 --- /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 0000000000000000000000000000000000000000..b94732f86dec7235c8a5b937d01c460c7d643f91 --- /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 0000000000000000000000000000000000000000..9275fb3756874003bc0effcf2dde4f12ec2a7dec --- /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 2fef78c2a63ba327d0ddd28cd6827276a3d82a36..cb5947bfe90045258b5aed4c5800f83c9b573bb0 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 e81e31a4117df3682a4c5020e520357456ad8669..c961c2a1b6ba8900cccc3b7d45f6f59d266c61f7 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 0000000000000000000000000000000000000000..9472249d6f9d4c5afb4c849de918ffc6c799caa5 --- /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 0000000000000000000000000000000000000000..04ac75c1f610b1db68218e897c26dc3c85350f79 --- /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 0000000000000000000000000000000000000000..869cd452ee2bd153b6dae7196d3d678df58b3456 --- /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 0000000000000000000000000000000000000000..0c13fd8945985612484380e2c22687c5d5a8c988 --- /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 0000000000000000000000000000000000000000..a1be0b235eb31577d20a20ab32d00c50d1024171 --- /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 0000000000000000000000000000000000000000..aec92b950e10ea6e65ca25223d61e4a5fa95029b --- /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 0000000000000000000000000000000000000000..c7c4c7c170afcf0e9dc2823c3768ca85e5ec653d --- /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 e02d7b84be769595a056b4b903d3ad46927a62e1..d16085688bdbb900ef2338864991d5b9829d0eb4 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 4422905b951cfda57ba6891f68e8e770a7f9fdd5..b8a7dd4710da1c67c777065ac95342d3304d73a3 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 0000000000000000000000000000000000000000..13cdf47b28ecf105943188a73e49016bbacdb929 --- /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 e5c87153f51828be2b16cb4fdac7cd80fc067afc..89eb842c7193fd9946113d43db5c12b9b32c8d8e 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 0000000000000000000000000000000000000000..da7e0841d589d262a4fec2581df5d5e33b2bccfe --- /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 0000000000000000000000000000000000000000..5a99c11720f30d0f8b55ddfdcb63f822375a4fb1 --- /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 0000000000000000000000000000000000000000..0b37dc7c5413c82b0e9985aba9682487a7a2d3e4 --- /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 57ad53f3fd3201ba30e0866afc5261617acd5e02..88828713539ecab6aa2fbcceedb0e832ccf9d72f 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 fcb22bbcea890b09843bbf67ceeac0e8b4dd2928..b85e507fab8af0383769ea94b1d9e2674de01331 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 b8da1e7710a23a205c75a4500e079d398c8f029a..d6fea608ec2db06e95323710a2c7f73f398bd8e9 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 81800f5687ddb01bf5577ba30a1730e1d85f3a62..c87dc4d964ec25cfd6a20e85034304f9801fcb8b 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 45c51232f71fb083f4cccac2567ce8199d87f287..deb27c2f4dfa75b9b3fefd024f0e4d82e6bce1c7 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 342a18a939c508a83576662a2dfec4aa463db891..1e2d697ada1099dfdcedfd6b4a565d2175ef3869 100644 --- a/paddle_quantum/__init__.py +++ b/paddle_quantum/__init__.py @@ -34,7 +34,7 @@ from . import mbqc 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 qml 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 88e2dea087b437aeb06878d89cd350b37d085aad..4a4f490dc0388fdb0422787dce7ebf27c3bb1a19 100644 --- a/paddle_quantum/ansatz/circuit.py +++ b/paddle_quantum/ansatz/circuit.py @@ -31,7 +31,8 @@ from ..gate import RealBlockLayer, RealEntangledLayer, ComplexBlockLayer, Comple 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 @@ class Circuit(Sequential): 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 @@ class Circuit(Sequential): """ 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 0000000000000000000000000000000000000000..b9c7098ebe3185968eff32631b40d601448f7e68 --- /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 0000000000000000000000000000000000000000..e3881bf6ce04d65769dfb3f98fc1eb243fb6b590 --- /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 0000000000000000000000000000000000000000..31930f6cc52ccf189f79efee20fe1d59a39b3203 --- /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 0000000000000000000000000000000000000000..7d97c29e667747b780044f946bb0535b3d6df36b --- /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 0000000000000000000000000000000000000000..c38ae8866edaaaf3a94a4d5e3358a8dd835e60e0 --- /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 0000000000000000000000000000000000000000..e8a111b8971b822e7823f6cb3e2ea3c051882e97 --- /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 0000000000000000000000000000000000000000..fb4023b5c1b5ae38d0fca84323e255b931dae4f2 --- /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 f084fbf3126caa2a21394a01a62fd10988fc0370..9a900ec0b9452a547ebc692fc579f1eaf0dc7d88 100644 --- a/paddle_quantum/channel/__init__.py +++ b/paddle_quantum/channel/__init__.py @@ -18,17 +18,6 @@ The module of the quantum channels. """ 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 33bdcd207138c3b1448a5c23f8ee7c101013f1ca..fc0d304fd008a6d4c1a3d3f92d0f47542e549530 100644 --- a/paddle_quantum/channel/base.py +++ b/paddle_quantum/channel/base.py @@ -17,11 +17,14 @@ r""" 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 @@ class Channel(paddle_quantum.Operator): 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 3c9825a6e7bcbadc228a8eacbb8177a61d86c594..31bdb3a1b135c6eb8c05d255f35e18ad5bff529c 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 @@ class BitFlip(Channel): 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 @@ class PhaseFlip(Channel): 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 @@ class BitPhaseFlip(Channel): 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 @@ class AmplitudeDamping(Channel): 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 @@ class GeneralizedAmplitudeDamping(Channel): 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 @@ class PhaseDamping(Channel): 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 @@ class Depolarizing(Channel): 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 @@ class PauliChannel(Channel): 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 @@ class ResetChannel(Channel): 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 @@ class ThermalRelaxation(Channel): 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 @@ class MixedUnitaryChannel(Channel): 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 82421f202e3e4464dfc8697e265dd69d560c0621..0891ab9a3c9d382a8cd5746d2b35f1fe9e5d0a12 100644 --- a/paddle_quantum/channel/custom.py +++ b/paddle_quantum/channel/custom.py @@ -20,43 +20,122 @@ The source file of the classes for custom quantum channels. 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 @@ class KrausRepr(Channel): 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 633c3cc62b7ff1fb314d2a25af83c9f00c6164c0..63f3ccd8a2447d1a13fff4fc82c5e84b72b9ee2d 100644 --- a/paddle_quantum/channel/functional/__init__.py +++ b/paddle_quantum/channel/functional/__init__.py @@ -17,16 +17,6 @@ r""" 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 851b8d84aec724ed1d5050c322e659762741fc2e..ea42269e3013dd9db96e19912361fd00d8116868 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 choi_repr( 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 choi_repr( ) 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 stinespring_repr( ) 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 0000000000000000000000000000000000000000..0a1a9d8d524c92003795aa793e9c3dc804a0787a --- /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 0000000000000000000000000000000000000000..ad2649a2c896188ff179d7a85ecebad9bd5e24da --- /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 0000000000000000000000000000000000000000..0d68e5acd672238a634b4a85b6f7eed1166f36c1 --- /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 0000000000000000000000000000000000000000..4814205e859ab05b004f9b648233d01e28babe7e --- /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 0000000000000000000000000000000000000000..27292adc835884b81a3b986a77942af6f1e63668 --- /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 7f986d810aacc1c8524f6f4fefe6ca4418c76a3f..9df7509a5737c552c1d5bbe9f2546f59bdd27358 100644 --- a/paddle_quantum/gate/base.py +++ b/paddle_quantum/gate/base.py @@ -107,13 +107,13 @@ class ParamGate(Gate): 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 @@ class ParamGate(Gate): 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 4e3dd90d136fa6029099443acbfcca8b5980c073..931d8b88aea6f7f0d551c6bcfcbba6dc108b051f 100644 --- a/paddle_quantum/hamiltonian.py +++ b/paddle_quantum/hamiltonian.py @@ -24,6 +24,7 @@ import numpy as np from scipy import sparse import paddle import paddle_quantum +import openfermion class Hamiltonian: @@ -201,7 +202,7 @@ class Hamiltonian: 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 @@ class Hamiltonian: 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 @@ class Hamiltonian: 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 44e3f0089a7a034d0920a1418ae1fd616ee85841..a21442aeacf4c7eefddb679377088bdc0a422033 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 720850b5aa6e8a09dff217374ceb4fdcdf84b201..d46cbbeb525ad48ab067acfaffdf2ccd2d7a2903 100644 --- a/paddle_quantum/linalg.py +++ b/paddle_quantum/linalg.py @@ -21,9 +21,10 @@ import paddle 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 8ae1706c8d14fcf418f5a8f7675156d764d13f68..acb40e90919f263cd9df636f8bf99c7042a71c78 100644 --- a/paddle_quantum/loss/measure.py +++ b/paddle_quantum/loss/measure.py @@ -102,7 +102,8 @@ class ExpecVal(paddle_quantum.Operator): 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 0000000000000000000000000000000000000000..e9795e959a66b056cd0a3538ef2fca55e8ae1281 --- /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 1765692045ab88a7d7fd823b5e02ecdc4c06a8d9..592c61af136eaffd79b2e5d0738e29734befdde5 100644 --- a/paddle_quantum/qchem/__init__.py +++ b/paddle_quantum/qchem/__init__.py @@ -17,9 +17,10 @@ r""" 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 0000000000000000000000000000000000000000..8eb9dc32bd9729e77ae06599111ab9a2b1e3a47b --- /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 0000000000000000000000000000000000000000..52528b3016f8b074f11a22d9446f5fa0c099f390 --- /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 70566b52fbb1e85ad1518ea8382ce5d4edebd58b..0000000000000000000000000000000000000000 --- 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 431b7e434a766de5ab2cc4e30191263b2eaa3450..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..2ed8e7d395e0245164f693da39eee31bdc29a58a --- /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 0000000000000000000000000000000000000000..6a5d1a3e8279e7b829ece8ee5e554962df41d7a1 --- /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 8d24e831d38bbb0e5eb933b2fde7af093088e26a..0000000000000000000000000000000000000000 --- 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 0b9ce149f4d9d4abf7cbabc51629159124412490..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..24a6fbc8df76f8b9a18dbeddcdf4924199a57b39 --- /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 0000000000000000000000000000000000000000..b2e9a736a0c10f0f6728fea848730b55c125c0da --- /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 f15ed6f8383a14464b971b8e9332c2b4b2bc775b..0000000000000000000000000000000000000000 --- 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 51240765ad5a6cb57cf788a49113bd073151bdf1..0000000000000000000000000000000000000000 --- 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 f8cb957990f3508d5bf39e4c6a7193a6e6c0cbe4..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..89d00d217074106b1695a19764f32bb36cfb4bce --- /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 abe02bc27c2605d1166067feb1ebe7e258146c92..842fd7ef4d00f2ee054fdf2fa34f5640b84770d4 100644 --- a/paddle_quantum/qinfo.py +++ b/paddle_quantum/qinfo.py @@ -22,16 +22,22 @@ import warnings 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 0000000000000000000000000000000000000000..6742becd59e0ef633f0bbe38d740490a1a447827 --- /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 0000000000000000000000000000000000000000..3007cebf76f5ebdebaa4d423663affc9d3e364cd --- /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 0000000000000000000000000000000000000000..4d90712d4bab2e8522c4d397d53961b0ff346f85 --- /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 823e67e99945678073bb638bb53ad0a37ae08c78..7f3642cf46416edccc1eb7b1f0b32edf14b2ff69 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 @@ class VSQL(paddle.nn.Layer): _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 @@ class VSQL(paddle.nn.Layer): 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 28d27e631119541c7608107a1c28aece20662491..2594d009c7c38700cfa14e84d933f30007efee38 100644 --- a/paddle_quantum/qpp/laurent.py +++ b/paddle_quantum/qpp/laurent.py @@ -236,7 +236,7 @@ class Laurent(object): 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 f1e7d544fc76d6770791b459d29897bda09a3b26..86257212099429a38e67fb53c685b83dc3b2afca 100644 --- a/paddle_quantum/state/state.py +++ b/paddle_quantum/state/state.py @@ -32,6 +32,7 @@ from ..backend import state_vector, density_matrix, quleaf from ..hamiltonian import Hamiltonian from typing import Optional, Union, Iterable, Tuple + class State(object): r"""The quantum state class. @@ -55,7 +56,7 @@ class State(object): 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 @@ class State(object): 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 e4d6fe3fdee9d3a3a3a8a0d9299b1fd675b2f34a..940a8fbe3b52581e71d8fecc710cc056aac436a6 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 ccc38bb1460f9cc9bc2ab29b231a85313509721a..f97f2fcaf81e1c4f092980542e750e78db67e95b 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 6bbef53be2448000782776aa29b27c707787cf16..1d9d57e930a1ebbebe6d7e83b3bbe2e92ca12a56 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ with open("README.md", "r", encoding="utf-8") as fh: 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 @@ setuptools.setup( '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 @@ setuptools.setup( '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 @@ setuptools.setup( '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 @@ setuptools.setup( '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 2cedc0b8bc50c5ce398226f4c40ed20344fb8c3d..75d379958fcc246d8313d26de1d21d366d0b134c 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": "iVBORw0KGgoAAAANSUhEUgAAAXwAAAD4CAYAAADvsV2wAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAABhxklEQVR4nO2dd3hT1RuA35umewKFlrL33nuXPQUERcDBT1BEEEEQBJUpU5AhAgqCIrJRAdl7743sDYVSZkvbdCW5vz9CSkd2M9v7Pg8PkJzce5KbvPe73/nuOYIoikhISEhIZH9kju6AhISEhIR9kIQvISEhkUOQhC8hISGRQ5CELyEhIZFDkIQvISEhkUOQO7oDhnD3CBQ9fUId3Q0JCQkJlyE+5tpTURTz6nrOqYXv6RNK1UYLHN0NCQkJCZfh0Mbwu/qek1I6EhISEjkESfgSEhISOQRJ+BISEhI5BEn4EhISEjkESfgSEhISOQRJ+BISEhI5BEn4EhISEjkESfgSEhISOQRJ+BISEhI5BEn4EhISEjkESfgSEhISOQSnnktHQsJVcXN3N/s1qpQUG/REQuI1kvAlchSWiNheGOqbdDKQsAZWSekIgrBYEITHgiD8p+d5QRCEHwVBuCEIwnlBEKpbY78SEsZwc3dP98dVyfg+XP39SDgGa+XwfwfaGHi+LVDq1Z++wHwr7VdCIh05TYjSiUDCHKyS0hFFcb8gCEUNNOkE/CGKoggcFQQhSBCE/KIoRlpj/xI5G0lwmdH1maiUCahVyQiCN4IgOKBXEo7GXjn8AsD9NP+PePVYJuELgtAXzVUAnt4hdumchOshSd50UpJfcvPcLJ5F7gdBhrdvQUpUHkRgcJV07aRxguyPvYSvK5wQdTUURXEBsADAL6iMzjYSOQ9J8JZz+fi3ePkUoGaL1bjJfXkWuZ9Lx76mapNf8PYrmNpO51WBdBLIVtirDj8CKJTm/wWBh3bat4SLIuWks05czA0S4iIoXnEQcnd/BEFGcFg4+Qq1JfLOeqOvlz7/7IW9hL8B+OBVtU5dIEbK30voQpK8dUmMj8THvziC4Jbucd+AkiQqHpm8Hem4ZA+sktIRBGEFEA4EC4IQAYwB3AFEUfwZ2Ay0A24ACuBDa+xXwvXJLgIJKRZmtW1F3bbexa9fUClioy+hUipwk/ukPh795AR+gaUs2mbaYyalfFwLa1Xp9DDyvAgMsMa+JFwfZ5C8NQVtbXT1zdKTgJdPKMH5G3Pp+EiKlP0Id49cPI7YQsyzM5So/HlWuyrJ38WQ7rSVsDmOFLwzi90c9L0PU04EpaoN48GNNdz8byaqlHhy5atNlcbzcfcMtGofJfk7P5LwJayOJHj7Yej9ak8GguBGwVLdKViqu726JcnfSZGEL2E17C36nCZ3fSQnRPM08pTO53KHVsHLJzjT49YcJzCGJH/nQRK+RJaxl+glwesm6t5Bjm35glz5aqd7PPrJKao1G0vxSpkje1OuDGyBJH/HIglfwmJsLXpXFnz+wnnMfk3kvWcW7atAydZ4+4YSVqw7gXkqAxAXc534l9cpUraz2dvL+Lnb6gQgyd/+SMKXMBtbid7WgrdEwvZEX/+MnQhkbu6Uq/sZN8/+QWCe6QBE3FhK2Vqf4ObuleV+aY+LFPm7PpLwJUzGmqK3R/Tu7II3FVNOBEXLd+Hy0Z+IeXYeN7k3cTGXKVH5F6v2I+0xk+TvmkjClzCKNURvr/RMdpG8KWR8r/Xaj+DMniUIMm+rRff6sEfUD5L8rY0kfAm9ZEX0kuDtT9laPTi2dSrKlEQatF+D3MM7UxtLxwn0Ya+oH15/HyXxW44kfIl0ZDWaz+l5eEfi5uZOs26zSElR6JQ9WD5OYAr2TvlI4jcfSfgSgHOL3lkkH1bA1y77efgg3uLXFi3f0qLX6fqMs3ISsNdAryR985CEn8NxRtHbQvD2krU10NfXrJwILEF7HKwhfrCN/KVo3zwk4edQnEn0torgXUnypqDr/djjJGAN8YNto35J/KYhCT+H4SyilyRvHex5Ekh7zJw16pfSPIaRhJ9DcLToJcHbD3ucBKwd9YP15C9F+/qRhJ/NcZTobTnQ6kqSLxCiazln3TyIst0SzrY6CVhL/GD9lI8k/sxIws+mOOpmKVeN5M0Rs736YMsTAFj3JGAL8YN15C+leV4jCT8bYa2pD5xF9NaSvDPI3BLsfQKAzJ+5uScAa+X5tVhL/lK0r0ESfjYgO4k+p0veELrek72uAiyJ/K0Z9YN1Uj45XfyS8F0cV0/dWDNVkx0lbwx7XQU4m/izmurJqWkeSfguiiMnNMuq7CXJ2w5bXwU4i/ilaN8yJOG7GK4q+uwo+fxBSTbZbmS0p1W3Z4urgLTH05F5fkn85iEJ34VwtRJLZ8vH20rQ1iZjP219AoCsnQScIeqX0jymIQnfBciJos9pkjeErU8AYJ2rAEeLX4r2jSMJ38lxxJz0jhK9K0heFEUU8bEkKOJIVMSToIgjQRFPcGgBwgoWIz7uJdvXL0URH0dCQpzm+fg4mrbtRq0GLXlw7ybjh/ZMfV1KchLFS1fk/X7fUK1OuMXvz1ZXAa4qfmtE+5D9xC8J34mxt+wdIXpHSP7EoR3ExUanE3bRkuVo0KwjarWa7758lwRFPAkJr6XequN7vPfJSBTxsXSqH5Jpm+9/8jW9BowiMSGeuVO/BEAud8fb1x9vH18qVW8AgKenF/nyF8bbxxdvHz8EQca1S6dSt3P2xH4W/ziayjUbUblGIypWq4ePr7/Z799aJwBXFb81pA/ZL80jCd8GJCc+43HEVp3PBeapin+uCka3YansXSGqt5fkRVHk0rmj7N68Ck8vH/oOmQTApBH/Izbmebq2rTq+R4NmHZHJZDyMuI273B1vHz8Cw/Lg7eNH/oLFAPDy9uWToZPx9vHD28cXLx8/fHz8CSukeT5XnhAOnriEj48v7h4eGXoUTUhRXxYsXKizv1EJoExJRhRF1iyZxcpF05G5uVGqXFXGzVxFcEgBRFFEEIx/ftY+Abii+K01VUN2kr4gira/e89S/ILKiFUbLXB0N8wm/uUtzu7vTf5iXZHJXv/QHkdspUCJ7hQo3k3va7NrVG/PSP7uzcvs3LiCPVtW8+jhXTw8vWjRoSdDxswF4MaVc7i7e+D1KsoumlupQ86ORaFQsPfoZc6dPMDV/04yae463ORy5n0/jPMnD1K5ZkOq1GxExeoNCAwy/xhm9QSQlUFeS6dvyEqaxxrRvqtI/9DG8FOiKNbU9ZwkfBtx+cQ3BAZXJ6xYV0BzErh4dCg1mi3DTe6j8zWuENU7QvSmSD7q4V3yhhZCJpPx05QhbFi1gBp1m9Os3Tu82a4xvn5+We6Ho4lKCGLT2sXs2bKaS+ePkZyUCECNes2Z+stGABITFHh56/5+GcKSE4AjpA+Wiz+nSF8SvgOIi7nO5eMjqd5sGW5unlw5NQ6/wNIULNkjc9voq9y9toiYp2dx9wgif7GOFC79PoLMeMbNGaN6e0n+xbPH7N/xN7s3r+Li2aPMWLyDyjUb8vTxQ0L9FOTJE5zlfjgryUlJ7D95g/MnDyLIZPT8aBgA77Upi4eXN5VrNKJyjQZUrtmI4Hzmf0fMOQG4UrSfE6QvCd9BaKP8wDzV9Eb3CXH3uXB4IIXLfkTesGYkJURx6+IcfPyLUKrql3q37Yyih6zJ3tSB16ePH/LDmE85dXQXapWKoiUr0KzdO7z3Tkfy5ss8oOosKJVKFIp4FPFxKOLjiY+PJzk5iTJlK+Dnb3xg1hgPY/1Y88cszp88yH9nDqOIjwWg50fD6f35ONRqNU+iIgjJX9jkbTqz+CXp60YSvoPQRvl+QWXwz1VRZ3R/6+KPyOX+FC7zYepjypRYTu7uQa0WK/Dwyp3pNfaSvT1Eb4rkk5OTOHFoOynJSYS3fgtlSgqfvx9OjXrNadq2G/WqFLRo34YQRZGU5GTi4+NQKBSav1NFneExRTzxcfGav189lhwfTXx8PPGKBOLi44mPV5CYpPu9yuVyalatTO3GrajfKJxy5Sshk8my1H+lUsnhs3c5f/IApSvUoErNRty8ep5P3q5DaFgRTRXQqz/5CxQ1OhDsrOKXpJ8ZSfgO5PKJb3j54j9qNluRLrrX5uvPHRhIwRI9Ccqb/vicP9Sf4pUGEpincrrHzZW9M6ZvTJG8SqXi/MkD7N68igM71xEXG03ZijX5afkBAEK8o83a553bNzl0YC9xsS+Jj49/Ler4OBTxCpLjXmjErFAQr1AQF69AqVSatG2ZTIafrw++vr74+vjg5+uDn68vPj7e+Pn64uvjnf45H198fL3x8/FFEASOnTrN3kNHuHj5KgB5cueiToNwGjQKp37DcILz5jPrverjSkQSe7eu5dzJA1w4fYiYF08BmDx/A7UatOTp44ckxMdRsGgpnScAZ5U+WCb+7Cp9SfgOJFERSaIikqDg6qmPpR2cvXZmGu4euShculfqY8qUOE7u7k6tFsvx8NIIOztE9aaUUWpF88PY/mz5+ze8ffxo0Kwjzdq9Q5vwaribMbCtUCjYvuVf/l2zhOOnzqQ+7unhga+vzysBpxezn6+vzuf8Xj3mm7adjw++vj54eXqaVCppjMdPnrL/8FH2HjrCvkNHePb8BQAVypWhTqOWNGgUTrXqtaxSUSSKIicuPuLcyQM0a/cOfv6B/LlgCr//NI7cwaG079qbd3oP1TkA7Kzil6SvQRK+E5GxEkcRe5ez+z+lWPn+BIc1I0kRxa2Ls/HyCaV09RGAa0f1pkTz929fY/eW1ezesoqJP/1DwSIluXTuGI8f3adj6/p4m1F1Iooi58+e5p+1y9m6cR3xCgXFixahR9fOdGrXmpC8wWadNByFWq3mv8tX2XvwMHsPHubk2fMolUp8fLypX7sWtRq3pmHjphQuUsxq+zx/M4aTh3dxdP8WjuzdSEhYYT4d9j0Nm3fS2d4ZxS9JXxK+06Cv7PLl8/+4ffFnYp6ew90zkNAiHSlSrjcymdws2TvLoKwpko+LjWHL37+xe/Nqrl8+gyAIVK3VhL5DJtGwhvkSe/bsKRvXrWXDmqVcu3kLb28vOrZtTfcunahdvapVInBHEhsXx6FjJ9h38Ah7Dx3h7v0IAIoUKkjdRs2p3yicOnUbWq38dPuBC8yZ9AUly1ZhxKRFetuZW85pD/HndOlLwncCTKmxT5vSsEdUb4v0jSHZv4x5zvMnjyhasjyxL1/QrWlRipWuSLN27xDe+i0qFDGvflypVHL4wF42rv6NHXv3o1QqqVG1Mj26dqZj21b4+Tp2sXP/mPtmtY8NLGRy29t377H34BH2HjrMoWMnUCgSrD74q1QquffCA1+/AG5cOceeLat5t+8InVM9ZIdoP7tI3+bCFwShDTAbcAN+FUVxSobnw4H1wO1XD/0tiuJ4Y9vNDsK35GYqZ4rqsyr6xAQFR/ZtYvfm1Zw4uI3SFarz49K9ADx/+ohyhbxM2n5a7t29zT9rV7Dx75U8evyE4Dy5eatTB7q/2ZHSJUuYvT0t5graEIrEJGJi44hVJBD36o9SqaJZnWoArNt9iP9u3CEpOYXalcrSrHZVfL1ffxbmyD85OYUTZ85q0j8ZBn/rNmxKg0bh1GsYTnBwXovfz4/zFrNw5jfkyZeffkOnEN7mbYcN7ErSN4xNhS8IghtwDWgJRAAngB6iKF5K0yYc+FIUxQ7mbNvVhe+Msrdn+mb5r9NYvnAqiQnxBOcLo2mbt2nW7h2LUjYKhYKd2zby7+olHD15GplMRrPGDenRpRMtwhsZzMsbE7koiiQlpxCrSCBPoD8ymYzbDx5x/e4D4hQJxCoUqdL+stfbuLm5sXTjTjbuO5r6eKwigZQUJRf+1syT03fcTFZs2ZNuP7kC/Li3fTkA742cwvo9h5G7uaFUqfD0cOetlo35edSgTP0zR/5gu8HfPUevMGfSEK5fPkOVWo35bMQMipXKPC+UPdI8kvT1Y2vh1wPGiqLY+tX/RwKIojg5TZtwcpjwzZW9M6VwshLVX7lwgmKlKuLp5c32Dcv478xhmrV7h5YNK5qdXhBFkf/On+XvNcvZuvEf4uLjKVa4EN27duLtzm8Qms9wuaJW9HGKBP7ZfYgdR04xdfBH5M+bhz/+3cGURSs1so5PQKlSAXB94++EBudm0sLlTF60MtM2I3auINDPl5lL/2LN9v34+Xjj7+ON36s/c0YOQCaTsef4WW5FRGqe9/XB38ebAD8fqpTRXIEkJafgLndDpVZz6MxFNh84jq+3F2M+fR9RFHl3xGSqlStJh8Z1KVusUGo0ba78jQ3+1m7ShgaNwk0e/FWpVPy+7B8W/TiGzt370WvAKL1tnUn8OUn6thb+W0AbURQ/evX/94E6oih+lqZNOPAXmiuAh2jkf1HP9voCfQE8vUNq1Gy+Kkv9cwS2lL2zRvVxsTEs/nEM/65ewIefjaXnx8MB8+vlAZ4/e8rGDX+xYfVSrt64ibe3F2+0bkn3Lp2oU7O6wQFYreRFUeTUpess2bCdv3YcIFaRQKHQvKz/cTylChdg59HTrNm+XyNrX+9Ucfdo14wAXx/uRT4m8unzdDL39/XGXW77CWafx8Ty5uAxnL58A4BiBUJp16g2vTq2olzx9HfJmnsCsNbgb/SL57wkP55e3pw4tIMXT6No8UZPnSd1W6d5nFX62VX4bwOtMwi/tiiKA9O0CQDUoijGCYLQDpgtimIpY9t2xQjfmWRvj0FZURQ5uGs9P00ZwvMnj3izZ3+GfznY7GoRlUrF4YN72bT6N7bv2UdKipLqlSvRvWsnOrVrjb+R7aUVvSAIXLl9j1o9PsPHy5M3mzekV8eW1K1czu7VOvJHd01qpwwtkumxh4+fseXgcTYfOM7ek+dYPP5LOjWtz92HUZy+fJ3mdasT4Pt6oNtc+YP+wd83Or/NoC+/NjofUVRCEBO/6sWeLaupULUun42cSalyVTO1s3W0b6sUjytK3+EpHR2vuQPUFEXxqaFtu5LwnS2FY69B2d9+GseyBVMoUaYyX4yZS5NaJU3uI8D9u3f456+VbPx7BZFRj8mdK4i3O3XgnTc7Uba04W1pJa9Wqzlw+gJLNuzA19uLOSM1F5d/7ThAi3rVCfQz/cRnqqBtTcYTQGy8Ag93dzw93Jm59C9Gz12Ch7ucxjUq0a5hHdo1rk2BfK/lbIn8tYO/m3fsYumqv/Dx9qL/4BG807MXcgNXNmq1mj9Xb2LhjG+IiX5Kh7c/5sOBYwgIzDwtiC3FL0lfg62FL0czaNsceIBm0LZn2pSNIAihQJQoiqIgCLWBtUAR0cjOXUX42Tmq1yV6lUpFUqICH19/bl+/yPGD2+jf9wODUkhLQoKCnds3s3H1Eg4fP4lMJqNpw/p079qJluFN8PAw/HlqRR/55Bl/btrF0n93cvvBI4L8fenVsRUTBn6Y6TXOInJLyCh/pVLF0QuX2XzgOJv3H+NmRCSeHu7c274cHy9PnsW8JHeAf7qrGXNPANdv3WbUxO/Zf/go5UqXYviYqdSsXc/ga16+jOGHGXNYt/Jnvhz3C606vqu3rTOIP7tK3x5lme2AWWjKMheLojhREIR+AKIo/iwIwmfAp4ASSACGiKJ42Nh2XUH4rij7rET11y+fZeb4ARQsUoqvp/xuco5eFEUuXjjHP2tXsOXfv4mNi6NIoYJ076IZgA0LNTzLpVbyKUolbjIZMpmMr39czJzl62hcoxK9OrbijSZ18fZ6LRJXlrw+MspfFEWu3ongvxu3eatlYwCafzycyCfPaNewNu0b16FBtQp4pPmemip/URTZsmM3Y6ZM50HkI9p37MKQ4aPIFxJq8HUnLz2mQJGSyGQy9m5bS0j+wpSrXDtTO0n6tkG68cpGmCN7W6Zw7BHVJyji+WP+BP76cw4BgXkY8NU0undpZXRbL54/Y9OGv9mwZimXr13Hy9OT9q1a0OOtztStWd1o5Y5W9DfvP+SPf3ewbNNuFoweTLM61Xj4+BkJSUmUKJT+s82OoteFrrw/kFoyuuf4WRKSkgn08+WLD7oy9IO3MrU1Rf6KhAR+Wvgb8xctQS6X02/gl7z7QR+jZZ2R8QF81KUG925doc2bvejz+Xhy5clcWWUr8Ttjisce0peEbwNsJXtnjOqvXTrN+CE9efTwLu269ubrkcMIDAzSuw2VSsXRw/v5e80K9u7cSnJKClUrVaB7l050bt+GACNzv6eN5v/eeZAlG3Zw4PQFZDIZrevXZPiH3ahZoXS61zir5JUR90xqJy9o+hz1eveV4QSgSExiz/GzbD5wnIbVK9KjbVOePI+m9+gfaNeoNu0a1aZIWPorK0MngDv37jNmynR27NlPyeLFGD56CvUaNDbYp/i4OGbMnsdff87By9uX/w0YTcdufXHTkf6zRTVPTpS+JHwr4wyyt+cNVM+fPmLsF935ePAEWjSsaHA7ly6eZ8LXg7lw6TK5goLo+kY7erzVmXKlDRdlpb056smLGPLmCkSpVFGucx+8PT14/40WvNuuOWH5Xn9GtpK8qZK2JVk9AeiL/s9cucHHY2dy9Y7m865YsijtGtXm467tCA1OP8iqT/479x5g9ORp3Ll3n/atmjPomynkDzO8JsGJS1H8NHkIp4/u5seleyhfpa7OdrYq4XS2FI8tpS8J30o4S77eXjdQLVv4PeNmrUImk5mUq1/2xyK+nzia4Ny5+Wbo53Rs1xpPI5f9aUV/6eZdhkz/mVsRkVz6ZxFyuRt3H0ZRKDRvutSPJaJ3Bolbii3kf+PeQzYfOMbmg8c5ev4y59cuoHD+fDx+9oLcgQHI5W6AfuknJiXxy29Lmf2LZmK12fN+o36jcIP9EEWRfcevp+bzH0bcJqyg7hu+TBW/JP3MSMK3Eq4U2WdF9ndvXWFwr+b4+Qcyd8VBSoUav0P2z98XMnXiaNo0b8rMSeMIDDAtbQOa1MOURSuZs3wdAX4+DP+wG33ebIuXZ/qTRU4TfUZsFfW/eBlHrgDNfQ4dB47iftRT5n/7OXUrlwMMp3kiHkbyvwGDuR/xkD/XbKJ4ydJ622qJSgji5OGdfD2gM99+v5TGLd/U2c7a0rdFesfVhJ+1ddRyEDlF9lGR9xjxSQfkcnem/rLRJNkvW/IrUyeOpl3LZvwyc6pZso988ozaPQYwc+lfdG8bzqlV8xnQvZMkex1k9f3IH93V+TlqZS+KIn3fbo9KpaLrF+M4d/UmYHguooJh+fl97iy8vDz5vO+7RL94brQfId7RVK7ZiNLlqzNtVF/u3rqis52paxybijlpUHtgyVxbWUUSvgk44sBkxB6yj37+hBGfvIFCEceUnzdQrXTmG2cysuyPRUyZMIq2LZox/4cpRicx08oj+VV0Exqcm+Z1q7Nl3iTmfzuI4KCAdK/RJylDKCPuZTvZa7HGe9P3mQqCQIfGddkyfxKB/r50HjyW6/ceAMalv3jODB5FPWb4gA9ISU422gcPD0/G/LAcTy8fxn3Rnfi4lxa/H0vXU7YGlqxE50gk4RvBWfL2ppDVu2afPn5IYkI8E35cS/2qxtMHy/5YxJTvvjVZ9qCp4Jm/6l8qdvmYB4+fIggCs7/qT8PqmQeDs3tUH3srgthbERa91pbiL5AvmPU/amYvHzx1XurjhqRfo2plZkway5ETp5gwdiTGUsUh3tHkDS3It9OWEnHvBtNGfazzNaZG+aZ+900NnKz923QWbD8TlAvjLLI35UuaFdmr1WpkMhkly1ZhyaaLFApMMLqd5UsXM+W7b2nTvCnzf5ii9+7YtJI4c+UGg6bM48yVGzSvUw2VSq3zNa4mekulnfH1/sUNV7roQvu+s5Lflz+6mym/X6pwATb8OD7TFZd/zH29Of0327fl+s3bzJq/kOIlS9Grdz+D+w3xjqZqrcb0GzqFlBT9VwX5g5LMrtW3JyHFwizO5bu5u9v1Llxp0FYPriR7yNo0CZNG9KJwsbL06v8tYHyGyxV//sakcV/Tunk4v8z4Xqfs04peFEVGzl7E/NUbCQ4KYOoXH9G1RaNME5k5g+izKm9rYIn4tdhiYFepVDFz6V/069YB/1eTtemTvlqtpt+Qr9i0fRdzfl5Ck2Ytje4zKiEo9d8qpVJnjT6YNohr7QFcVxy8lQZtzcQZcvZge9mLosicSV+wb9tf+PhqBu6MyX7lst/Nkj1ocsNxikR6d27NqVXzeKtlY7vLXps+MfbHGXBkqkdXmufMlRtM/HU53YdPJDFJE4nrS+/IZDJmTR5PpfJl+eqLfly/pntANi3a79zZ4/vo3bkqjx7o/i5YexDXmrhKLl8SfgacZZUqW8seYMm879i4ZiHv9B7K270GmyT7iWNH0qpZE6Oyv/swineGTeDMFc2c7nNGDmDm8E8J8k8/zbE9BmWdReTmkpV+W0P8WmpVLMMvowaz/9QFPhw9HaVSZfC1Pt7e/DZ3Fn6+Pgzq+y7PnhmcFDeV4JACvHj+hHFDepCUaDytqAtr5/LtgT0DTEn4achJsv9n2Vz+/GUybd/8Hx8N+s6o7FctW8LEsSNp2bQxC2ZOMyj7ZZt2UbvnZ+w7eZ6b9zWXuroienuI3lVlryWr7yEr4k97fN5pE860IX3ZuO8on03+CbVabXAQN39IPn6fN4snz54zrP/7JCcZjs5DvKMpWKQkIyct5vrlM/w4cZDFg7jWrNoxJ/3qClG+JHw74YiKHEM/Dl//IBq36sLgUXMI9YkxuJ3Vy5cwYewIjexnGZb94nVb6ffdbGpVLMOJFXNTZ3BMiz1y9a4u+oxYQ/yWkPZY9evWga8/6sG63Ye4fk9zIjck/SoVKzB78nhOnD7LuFHDTKrcqRfenvc/+Zpt65eycc2vFvXZVJwpyrcX0qDtK1wpus9KRU583Et8/TSVF6IoGpf9ij/4bvRXtAhvzMLZ03ROlaD90e87eY4On42iVf2aLJs8wiE3T2U30evD3gO72sFcURS5GxlF0bD0UyQbuht35rwFTJszn8FffkOfTz7T205LZHwA3w7sSu7gEL4c97PuNlYawHWm6RasNXgrTa1ghJwi+4tnj/DtZ135dtpSatRrbjSNkyr7Jo1Y+ON0g7IHTcXPL2s30efNtnhmuAqwx6CsM/H0aqRZ7YPL5LdoP/YUf8YKnoVrNyOTCfTp0hbQL31RFOn/5Ug2bNnOzLmLaN6yrdF93Y/2wt3D0+CSlMakb82Knewi/Bxfh2/rARNnkf3t6xf5ZkAXAnPloXjpikZlv2blUpNlv2zTLsJrVaFAvmD6v9MxXRtXj+rNFXdW92Ou+GNvRVgsfXNr+NPW66vVarYfOcW2wycJ9PflrZaN9dboC4LAjIljuRfxgK+/HMCSFRsoW97wrKsenl4A3L99jdW/z2TQtz8iN/O3WiBEsGhRdF3kL5zH7EXQnZEcHeFbKntTo3tnGaR99OAug3o1BWD2kj1UKRlocBtrVi5l/KjhNG/ckF/n/GBQ9vNWbeCrmb/S7+0OTBvaN/V5ZxS9veRtDSyJ+LMS7YPp4tdKPyExic6Dx3D8wlVWT/+WlvVqAPoj/ajHT2j3zvsIgsCyv7YRnDfzYijp2icEsWvTSiaP/JAu731G/+HTMrWxZ2rH1lG+PSL8HDtoa2vZm4qtZR/78gVffdKe5MREpsz/16js1676k/GjhtOsUQOjkf3clev5auavvNGkLhM/f72OrLPJ/unVSJeSPVjWZ3tV9GiPr7eXJ6unj6J8icK8O2IyR85dAvQP5Ibky8uSebN5ER3N0H7vkWik9DLEO5rm7bvzZs/+/P3nT+zevCpTG2euzTcXe5Rn5kjh20P21qzKyUpFjq9fIA2adWTCT39Rt3IBg9v4a/Uyxn07jKaN6vPrnB/w8swcPWl/zD+tWM+IWYvo1LQ+SyYOT10z1dlKLV1N9BlxlPiNoT3OgX6+rJs1jgIheTl/7Vbq8/qkX7FcGX76fhKnz19g4oj+JlXufDJ0ChWq1WPWhM95GvXAjHeiwZTfjymBV3Yo0cxxKZ2snEUdkcqxVPbJSYlEv3hCvlDN5bWxnP3fa5Yz5uuhNG1Un0VzZhiUfVJyCs0++pIShcJYNG4o7q9uhbdE9qaSndI3b+07weOEJJSiiOrV789NECjk48WqJrX0vs7eA7vGUjxpB3ETEpNSF5DXzs0E+tM7cxYuZvKMOXw2eDifDPjC4H6iEoJ4cO8mH3etSfuuvRkw4odMbayR2skuaR2pSicNrpS3t3h+HKWS74a9y9X/TrFo3RmKBRu+M9Ic2atUKtzc3HjxMg5/H+/UlZHMkb2t0jc3nr/k7rXMP7IAdznF/Jyn5nrMmSskRybQU/b6u/KH+ilXPZNZ36yOwcoUsK/4zZE+wMEz/zF8xkL+mjGa/Hk170+X9EVRZNCIUazdsIkfflxIq7YdDO4nKiGI/84cpnSFGnh46Ja7Nap2soP0pSqdV7hS3j4r8+PM+m4gB3dtoP/waUZl/8+aFYz95kvCGxqX/fQlazh2/gp/Th6RumgGmC57Ww/Kdl+xE7VSTZDs9df6pVpFskxkd6sGZm3LFjw88hiASslyvhdf8h7B+AtuxIgqDolx+Ca7cfpFDDVyBxncjj0repQR9wxKP+NMm96entx+8IhOg8awdf5kcgf666zeEQSBad+N5s79CL4ZPpAChQpRoWIVvfsJ8Y6GavUBzb0karUK/4BcZr0Xa1btuCo5JsJ3lry9LWUP8OvsUaxcNJ2eH3/FyOGDDW7jn7UrGfP1EBrXr8tvc2calP2031Yz/pc/6daqCb+MHmx2ZG/rQVmAP2/d5+D1KL7m9TH7nkdUK56HPqV0L+9nLlppZ4U1CU/ZkxBNbcGP99yC+V31hDjU+AtuBJfw59PSutd51Yc9KnrMifT3nTxHly/GUbl0cf6d8x1+Pt56UztPnz2nXbf3UCqV/PnXNkJCDb+X+zHefPRmdcpVrs3Iyb9lej47RPm2jPBzxKCtK8k+K+z4dzkrF02nw9sfMWLYIINt1/21ijFfD6FRvTos/slwZD918UrG//In3duEs2CMebK356DsW0XCuEYSN8VEAO6KSVxAQY9irwerHx55nKU/1sBPcCM3cjaL0TwQk9kqxtBNlptnohIemF91Yo+BXWPHMO13oUnNKvw+YTinL9/g3RGTSUpO0TuIG5wnN0vmzyY2Lp4h/d4lIUFhcD8eHp40b9+DXZtWcnT/lkzPG6vaceTqWKZiy2qdbC98Z5G9qWQlum/Q7A16DxzHwK9nGcwDr/trFaNHfkGjenX4be5MvL28MrXR/kBnLv2LCQuW06NtU34eNQg3N/NkbyqWiD6j5Lzc3OhdqjCrBM26qquEF3xQvBA+crlVhZ1VGnkEcI0kSuHFSNV9Ggr+RIopnBTjaeIRYHFfLRmoNudzN0f6bzSpy09ff0beXIHIZJrvoj7plytdinnTJ3Ph0hXGD/sEtVr3wjigSe30/Hg4RUtWYNb4zyxaGtHYb8zagZkzVexk65SOvXL2jk7l/HfmMCXKVMHbR7MPQxU56/9ezagRg2lYtza/z5tlUPagmQt9+abdTBncx2TZ2yN9o49ElYp2O4/SS52HxbKnLPYvibfgfHHNfykKJsXe5yVqwnAnXlAzzK8AVd0zf0/C6hm+QUkXthzYNSe9I4oigiAQExdPgK8PgiDoTe/8/NsfjP9+Jv0+G8KAQcMM7mPf8et8/n447br2ZvCoOZmet0dqx1nTOjkypZNTZH/r2gWG923PLz+MBAzLft/uHSbLXjvnebWyJZk2tK9NZG+LmnptlD9T/YjO7rmcUvYAFd19WJqrNFP8CjPIP4wlQaV0yh6wKOK39GYzU46HOZG+IAi8eBlHeO8vmfXn34D+SP+T/71P9y6d+PmnGezfs9PgPspWqkXX9wby4N5NlDrk6GypHXO9Yqu0jnP+GiRM5vCejSQnJdKr/zdGa+23bFpHcJ7cetM4aWnZ9yv6jM5c7+wozJHXW0XCaOYRSEcv516I2k0QqOjhSwV3H+RGSjHtibXnJgry98Vd7saBUxcMthMEgSljvkEmk3Hm9HGj2+09aDzfL9hk9hw7ORlJ+C7OnZuXCC1QlFx5Qoy2vXv9EhXKlsHH29tgO1EUuXLnPrkD/a3VTbvi5ebGl34F8BZkKEQVN5WJxKiVju5WjkUQBORubri7G68Cd3OToVar8fAwHJAAuLt7GL1nwVWx1cLmkvCzgKPTOaCZBbNYyQpGX69Sqbhx6zalS+gv+dNeaj94/JQ4RQJlimZtMi5D2Ho6Y1EUWaZ4zAcvrjMtNoLe0TeYFfeQZFH/gKCE7UhRKlPvyDZEUrJmzVxPHVVjGbl78zJvNirA0X2bs9w/W5OVRc6tSbYUvrPcYGVrkhITuH/7KiXKVjba9kHEPRKTkihdsoTRtlfvaGRcpmj6wTVLJkVzFNuSojmUFMtPbkX4ya0oi92KEZ2SwiJFlKO7liNJUarwMCHCT3q1SLqHjkn7MpKQEE9szPMs9y0nkaPutM1ueHp5s3LnTZMua2/euAZAGZOEr4n0yxbTv4qRLrKyaLY1eXjkMRsTn9NHyEuwoDn5+wpufCoLoX/SHT7yCcU9m6YCnJX332hBoZC8Rtslv4rwPUyI8JMTE1+1zZyiNGVunZyIJHwXJ3dwqPFGwM3rVwEoZSClo6ViyaIM6N6J4FyGp1J2Zl6IKvLL0l/p5cYNEUgU1bgLbo7pWA5l6AdvmdROm9LRN19OWhITNTdpeRopQJB4jSR8F2bruj9IiI/lzXcHGK3QuXH9KvlDQwjwNz4Q27hGZRrXMJ4msheWlBeWl3tzWBVHZ+H1fCtnRQV5ZHL8nLRUMzvzMl6Bp7t7pqUvM/Ja+MZTOslJmvn0dUX4ErrJdt98Z8rf23rAdss/v7Nv+98m9eXu9UsmpXMAbtx7mFqHbwvssf7su955WSM+Z7n6GZfFBDaqo/lB/Yg+PiHZtrLDmanQuQ/fzsk8901GtDl8UwZtg/OF0aJDT3LlNp4qktCQ7YRvL6w5nYIlqNVqbl29YNKArTkVOk9exFCtWz9+XrPRan11BEXlXswIKEq0XMWvPOGyWyJjAgpR18M1S01dnRSlKnUOJkNoc/juJqR0ylaqxYhJiwgOMbywj8RrrJLSEQShDTAbcAN+FUVxSobnhVfPtwMUwP9EUTxtjX3nVB49uEOCIo4Spa1doaMRf8aSTFeq0NFSwM2Tz/1cq/Iqu2JyWWaS5mrWlAg/K9hzmuSkhOdE3tkAOnYZkKcivgHF7daXLEf4giC4AXOBtkB5oIcgCOUzNGsLlHr1py8wP6v7zencuHIOwKQI35wKnWt6SjKN4UwVOhLOR4pShbsJEX7SqxuOTMnhr/ptBm1rBpFoZIZNRxMXfZfrZ6YS8/QsL5/9l/rn9qWfef7oiF37Yo0IvzZwQxTFWwCCIKwEOgGX0rTpBPwhamZqOyoIQpAgCPlFUXSKdehcrf4e4PnTR3h5+VC0RMZza2bMqdC5euc+vt5eFAwJznIfrYEzL1UoYRoqlQpRFM2K8E0py0xKTCAlOQkPT+eu0smTvxqBwdUJyFOVkEJtAVDE3eNZ1EFCi3aya1+sIfwCQNrZkCKAOia0KQBk+jULgtAXzVUAnt7GpwtIiz1WfXcWOvf4lDfe/hg3udyqFTpX70RQukjB1DVJrY09BmyzG0eSXnIg+SVeJ5+ke1wQBPqWKUoxPx8H9cx0vvm4Jw2rVzTaLjlZG+GbIPykBNw9PG32XbUmRcr15tqpyeQr0BJBJufelV9RKRM4vft9CpX+gPzFOtulmMAawtfVy4zZKlPaaB4UxQXAAtBMj5y1rtkGZ5hSAcDNhIgJzKvQGdizM4mvKiUknIM41JxJiefDJz5pHlOxSP2YAeUMX7U9vRpp8VTJ1sLNzY0Rfbqb1DbZjKkVkhIVeLpISWZQcDU8fUJ4HLED/9wViHl2htot/yFR8YBrZyYiCG7kL9bR5v2wxqkxAkib8C0IZJw4wpQ2EiYSE/2MIR+25MyxvUbbmlOhA9C8TjXaN854gSbhSJp5BOIlyAhGTjNZAM1kAcSipkNYCAV9nF94KpWKiKgnxCckGm2bqE3pmJDDT0pMtNlNV6Ysc2guRcr15u7VX7l3dTFhxbshd/fFL7A0JasMJ+L6cqvvTxfWEP4JoJQgCMUEQfAAugMbMrTZAHwgaKgLxEj5e8u5dfUC508dRK02XitvToVO1LMX7Dt5DkVi+qsKV6zQyU64CQI9ffKyXNQsuBEnqthMNJ+UKWrwdQ8ViUw4d5Wmv26k24qdbL3hmHTa0+iXlOvUhxWbdxtta05Kp2qtxrTt8mGW+2cvgoKroVIqiH56ivxFu6Q+7h9YhoT4COyxGFWWhS+KohL4DNgGXAZWi6J4URCEfoIg9HvVbDNwC7gBLAT6Z3W/GclJ+fubVzUVOsVLVzLe1owKnZ1HT9Phs1FERD0x2tYemDtgm50rdJp5BPIMJefVCtaL0YSHBhuM7h8lJPLewVOIDxP5ND4X9aPkjNpynIUnr9ix1xpSlJqpqeUmzZapjfCNC795++78b8DorHXOznh65yOsaBfkaRa7iX56Gt/Akqk5fFtNjQxWqsMXRXEzGqmnfeznNP8WgQHW2JcE3Lhynjz58pMrj/Gl78yr0InAXS6neAHzcr6mlmTaY8A2UVSzJymGy0oFuWVyWnvmIr+b8fSAs6ON8pckPuORmMyKMjpXsEtlyY37NFb70UumuQu1pOBFSbUnww5f4N0qJfExYeZKa5Hy6q5tU8oyX0f4pqR0EpDL3U0ey7Imli5vWKxCP26cm4FvYEkCclfi5fML3PrvR0pUHmyDXmbG+Ye3sylZGbC9efV86g1X1q3QuU/JwmEm3RHpjMSrVQyNuc3BhBhKKz1JTFIzKOY2p1PiHN01q9DMI5B4udpodA9w7nkMdfBL91iY4EEemTs3X5i/8HdW0Eb4ppRlanP47iYIf2T/Tgz7uG3WOmdngsMaU6racB7eWs3pPe/z8NYqSlX9krwFwu2y/xw9eZol+XtrVehYiiiKhBUqRoWq9Uxqb06FztXb96lU2viVgLPyT+IzCokeDJWFpl4eV1f7MCcukkVBJZG5+Bw6boLA/HpVyGVkAjKAEC9PIhTJVBBenxgSRTVPVSnks/NAr9LMCN/Tw7SVrJITE/ALyGW0nbORJ7Q+eULrO2Tf2SLCz0n5e0EQGDdrNd3+94XRtuZU6CQmJXP7YZTZd9g6E8eT42gjC0wni2qCD2rggTp7lJoW8fUhwITve/cSBVkpPOemqKmMUYhqfhGe0KhwCCF+9hV+3txBTPq8NxVLGQ8mkpKT8PQ0LQWXmJiAl5HlOiXSk6MjfFdEFEWTb9Awp0JH7ubG7l+nkSfINpOL2SN/7yXIiBfVqXd9xIgqdqtjiBaV7E2KwU/2OsIsL/ehjNwyWahEkS1JL0jSUVUR4uZOQ48Ai7ZrTeoE52JQheJ8d+kWHoKMl6KS5kXCmNbW/iW3eXMFMrBnZ5PaJicnm5S/B830yNLUyOYhCd/FmD9tOOdO7Ofn1UeNit+cCh253I0a5UtlftxBJZmWVOg08wxkVcJzKok++AgyXogpLBKf0kIIIDZJSSyaXPJBMZY3vHJbLHwRWJbwhNJ4kZ/XcrooKgiSy51C+AAdC+WnbYEQ4kP8ye3tSW5vx6wCFZ+QyP1HTyicPx8+Xob7kJSUjKeJwnelG6+chRwrfFesvwfNpGkenl4IgmB0wNacCp29J85xP+oJ73doYVZ/nGXSNIBWnkHcVCbyUfJtqsi8iRBTCMKNYoIHnWS5AXgoJrNXpRG+pcgFgbe98vBfooKP3DRVMCmiyCeq2/TzduxdrRlxl8komduxJ6AT/13ljYGj2Dx3Io1qGC4lTkhMMln4nXv2p3CxMtboYo4hW+TwsyP6KnTc3T14HHmPBIXxOwF9/TTpmYNHjhtte/nWPfpP+JEVW/aY11EnQiYIDPDLz6zAYtTzDqSfXyjf+RfmL/ULkkQ1AKvVz+nolStdescS2nnl5jIJ3BY1x2mXGEMBuScV3J1/Xhtj+BcvaLyRicTGKxgzbwnenh4UK2h4Oc6TZ86xffdeypQqadK2e/QZRoNmtp+OIDuRY4WftjbWGdG3CPMHn37DsyePWPvHbKISggxu46133qNiubJ8NW4iz54/19kmNlAzSPtx13Y0rFaRwVPncenm6zSOMrSIwX3ICxY2+LwWcyVi7vwvYfVe35MQ5uZBM89Aqrj7UsrdmzLu3mwVY3goJnNcjKezV9YXr/ESZLztlYcV6mekiCKr1c95z1v3ykvJopqNic8Z9fIu3728z8HklxbdVZn2PZpCcJn8Zn+OphwnY8dc+51JSk6h51eTOXftFn9M+oqCrxYx137n0nL/wUN6DxxC/tAQRk780eD2L9yOZe6UoSQodJfbGlvA3Nhc+NacVsFcz9jypivIwcJ3NKYswKDri1uhaj0at3yTVb/NIO5ltMHXu7u7M27aPF7GxjJy/GSDkpHL3fh9wjD8fX147+spxMa/nmPcmtI3R/zmCiusXj6dUnzXOy9/qZ+zVP3MKtG9Fm2Uv0j9RG90rxJFxsTe52DCS1qqA6it9mVp/GMWKqJM3o++92UIS0RvTdkDDJo6j70nzzH364G0aVAL0C372Lg4en06iOTkZGYvWEau3PpPyJHxAUwb9Qlb/lnCi2eZ76w2JntrYM5NV85GthC+pWdFS6J8Uw62LSZeSsvHX0xiys8b8AsIMhrlly5Tjv6fD2Pjtp2s37xNZxvtjzAkTy5+/24YN+9HMnXxKrP6ZKr0wb7RPkBJuSbKP22l6F6LNsrfKEbrje6PpMQSr1IxTlaA+jJ/mssCmCorxM6kGB6qDJeKWip6W0T1YJ7sAfq82YbpQ/vybvvmgG7Zq1Qq+g8dyfVbt5k+ZxHFS2QuHEjLuuXzOHNsD/2HTyOskPkrRTlzdG8PBHtM2GMpfkFlxKqNFpjU1p6Ll1vz5qusTpGsUqlwc3MzOICrVCrp/U5b7ty9z55/15Ivr+7FTbQ1+VsOnqBxjUr4eqefidBYxY4lA7jmlmtaOr9OpCqZe6ok6lh5TdskUc3+5Je09AzS+fzc+EhCUuR0lqW/QegHVSRVffxo7an7xiFzRQ+WRfWmYo7sz1y5QbWy6fPwumQPMGbydBb+sYxR46bQrWcvg/s4/l8k/d6pR816LRj/45pMVWpZTeWAceFbOqWCKVgrnXNoY/gpURR1zr2RLSJ8Z8TWUT7AotmjGTOom9F2crmcsVPnokhIYPjYCXpTO9ofZduGtfD19iI+IZGrd14L2VqpnbTYK9rP7+ZhddkDeAoyvbIHCBDceCJm/iE/QUmAkLlIztmiejBP9ov+3kLj/w3h331HUx/TJ/s/Vq5h4R/LeK/XR0ZlH5UQxOyJg/Dx9WfI2Llmy94Usnt0D9lI+M6W1jEVS3P5AIG58nB0/2ZOHt5pNLVTvGRpBg4Zyfbd+1i7fqNJfev17fd0HjSaZzGv515xVelbEjFbgxaeQewRY7kiJgCaG+f2qF/yiBRqumeY68ZOoreV7NftPsQX036mdYOatGmgCTD1yX7/4aN8M2EqzRs35MuRYw3uQ/vdHjZ+AaOmLyNXHvNWwgPrLFpuy9y9rQdrtWSblA44X1oHbLvyVXJyEn06V8PD04ufVx/F3d3DYGpHpVLxcc8OXL1+g90b1hIWqvuHo03tnL58nZZ9v6JJjcqsnTE6dSk5U27GsrQ+39YpHkewJ+op485eJQ9uJIhq5O4ypteqSOkAP+Mv1oMtI3ot5sh+38lzdPliHNXKlmTDnO/w8fLUK/vrt27zRvcPCAsN5bdVm/EzMLFfVEIQEXeuU6BISb03GtojlQOmC9+SINKawpdSOkZw1Sjfw8OTAV9N5+7Ny/y11HApG2iWmhv7/TySU5QMGz3eaGqnerlSfP/Fx+w4eprpv69Jfd5YlA+WRfpg+2jfETQNCWZHy3p8W6sc0+tVYn2zOhbL3tbpG1NJ+x14Gv2Snl9NpkShMNb8MNqg7J+/iKbXp4Nwd3dn1oLlBmUPcO/2Vfr3aMDvc8frfN5eqZzsEN2DJHyXp26TdjRo9garf59FgiLeaGqncJFiDB72LXsOHGb52n/0ttP+YHu/2YZ3WjdhwsLl7Dl+NvV5W0vfluWbjsBdJqNa7kDKB/pbtFi1PdI3WuQFC5t17IKDApg9oj/rZo0jl4ETWXJyCh99PpTIR1HMmr+EAgUNT9R364mMMYO64eHhRfu3epvcn7RYI5VjDs6au9eSrYSflTOlrQ6UqQNBWcnlDxw5k5+WH8Dbx7TKoB7vfUiDOrUYN3UGEQ8Mv29BEJg9YgDdWjXOdKekqdKXov2sYcn7sqXotcf90dPnHD57CYC3WjYmLJ8mzakruhdFka/GTeToydOMmzyTqtVrGdxHZHwAU7/pw4P7Nxn1wzLyhWbeppTKMZ9sJXx744gbMHR9yYNDChBWsBiiKPLiWZTRKF8mkzFqylxEUWTIt+NQq9U622l/uL7eXvw6bihFw0IRRTF1fnMwTfpg3xRPdhG/vaN6U9Ae7+jYON4cPJaeIyYRp0hIfV5fKmfeoiWs+ns9/T4bQvuOXXS20RKVEMTyhVM5vGcjn345lSo1G2VqYw3Z50Qk4dsBa0b5hvjlhxH079GQBEWcUekXKFiIoSPHcvDocZasWKO3XdofsFKposdXkxg9b4lF/bM02rdEYq4ufWdM32hln5CYxDvDJnD1TgSLxw3F79WCKvpkv2XnHibN+JE32rTi04FDDe5D+72tWK0+Xd77jM49My9/ba27aR0d3TuCbCd8Z0zrWBt9X/iGzTvx5FEES3+eZNJ23nrnPZo0qMeEH2Zx5959ve20P2S53I0C+fIwZ/k61u85nPq8qVG+FinFox97Dsqacxy0x1ipVPHh6OkcOXeZhWO+oFmdaoB+2V+4dIXPhn9N1UoVGD3t59RKL30kJ2kWbKlauwn9h0+zaLzDWqkcW2PvdA5kQ+HbG1MjAHtE+RWr1adtlw9Zu/RHbl37z2iULwgC306eg9xNzhdfj9Gb2knLpM/7UKN8KfpP+JGb91+fIO0p/ew2oAvOPSib9tj+uWkXm/YfY9qQj+naUpNq0Sf7R48f87/+g8gVFMSMn5fh5WV47vpbT2T0796AtQYqzuyZyslu0T1kszp8LVld8tDcunxr1uSDaXX5oLs2Pyb6Gb07VaVgkZLM/H0X+X2NL1i97q9VjBoxmLFfDaXv/97T205bn38v8jENew2mYEhedi38Hu80i1pYsmCKvWr2sxO2zNOnJeOJXK1Ws/PoGVrVrwHol70iIYEu7/fhxu07/LHyX8qUq2BwP5HxAYwb0p0j+zbz/YLNVK3VOHMbE1I51ozuXXWwNsfV4TviUskU7BHlBwbloe+QSTy4d4tHD+4YjfIBOnXpRovwxkyZ9RM3bt/R20774y6cPx8Lxw7h8fNobj94ZHFftdgr2s8O2HpQNi3pIvuNu7gX+RiZTGZU9mq1msEjR3Ph0hWmzvjZqOy1g7SHdv9Lv6FTdMreFKTo3jjZUvhZxdyD6KjpUvVFPK06vsfv/55PnU3QlNTO1xN/xMvLk8EjRqNSqQy2B2hdvybn1v5C+RLpI0BzUzta7Fm+6YrYK32jJe1xXLllD59OmM3MpX+lPqZP9gDTfpzPxm07GfrVaMKbtzK4n6iEII7u28ySed/RokNP3nx3gM521krl5NTcvRZJ+E5KVqIVQRDw8w9EpVRy/OB2k16TN18II0ZP5vT5C8xf/Ifedml/6L7eXqhUKib/uoIL12+nPm6p9MF+A7quhD0GZdOS9vhtP3yKTyf8SOMalZg8qA9gWPZr129k9i+/0rVbTz7o/YlJ+3v25BFlKtbki9E/6RyktWdVDmTf6B4k4dsda0cYhn4M/yyfy9f9O3Hh1EGTUjvt3niTdi2bMX3OfK5cu6G3Xdof/IvYeBav28b7I6cQE/f6vSlDi2Qp2reE7JbicURUn/aYHbtwhfdGTqZCiSKs+P4bvDwNrzV77NQZvhw1ngZ1avHNmMlGK2y038n2b/Vm9h978NQxqGutvL2z4Oh0c7YVflY/WGdI65j6Rdb3o+jw9seEhBVm9oRBpKQkm5TaGf7dbPz8fBk0cjQpBj5DrfSDgwL4fcIw7kRGMWDinEzz80gpHstwpOi1TFywjPx5c/P3zDEE+GpW89IX3d+9H0GfgUMoGJafqT8twd3IQuSPFIF8/+3HHNy1HtDM85QRa8peiu41ZFvhOzP2yiN6efswcORM7ty8ZNLkagB58gTz7fhpXLh0mZ8W/mbSaxpUrcC4/r1Yv+cw81ZtyPS8o1I8rih+ew7KguFj8+eUkfw7ZwL58mgWadEn+5exsfT6dBBqtZrZC5cTGKR7URctmkHa79m+4U8ePTS/qstVcXR0D5LwDZIdonzt5GpLf57Eowd3TUrttGzTgU7tWjNz/kL+u3xVb7u0Avi8Z2c6NKnL+J//5MmLmExts5riye7RvqPTN1oOnb3IoKnzUCQmEeDrQ+H8mjn69cleqVTSb8gIbt29x/Q5iyharITB/UYlBHHswFZ+nzuO5u270/W9gTrbSdG9bci85E42QpWSkuWafFvx8EG8yXX5WaX/V9OZMOx9FPHGa/K1fDl2BkeON2HwyFGsX/Y7vr6ZF+gGjQj8Y+4jCALzv/2cm/cjyZsrEFEUdeZwlaFFLKrVB43cLKnZ14rUnnX79jjRWDOqv3HvITOWrmXpvzspFJqX3p1bU6WMRt76ZK9SqRg18Xv2HjzM2InTqVOvodF937lxiUkj/kfxMpX5YnTmlasg++XtnYlsLfzswoMo0aSbsSKjPXXejBWSvzBz/twHgCI+lhO3o6hV3vCqQUG5cjN64gwG9utF887dmDlxLPVq67yXI1X6Qf5+1CivWYS60+ejyR0YQLfWTWhRtxoeaU68jpA+uE60bwxriv5ZzEu6fTmB4xeu4OYmY9C7bzLyox6p6xnrk/3FK1cZNvo7zl64yP/6fErXbu8a3X9UQhDHD/6GXO7OuJmr8PLOHERYqyJHi7NE986QzoFseqdtRpz1zlsw/e5byNoduFrmTxvOhlUL+PCzMfTv+4HOwbK0nDpxlLEjPufOvfv0eb8HIwcPxMdH9y3y2jtxVSoVw2cuZO2OAzyPiSV3oD9dWzTiw86tqVSqWKbXWSp/sPwuXVfEGqJPTklh+5FTPH4eTe/ObRBFkR5fTaJu5XJ0a9UkdYpj0C17RUICM+ct4OfflpIrKJDh306kTftOBity9hy9SlxsDLUbtkKlVBIT/ZTcwaGZ2pkqe1dL5dhb9obutM0RwoecJ33QLf7nTx8xe8LnHNr9L+Wr1GHY+AXULG94LVWFQsHs6ZNYvnQRRQsXYubEsdSpWV1nW630QSOXXcfOsmrrXjYdOMakgb35+K12vIxXEPnkOWWKZo64JfmnxxqSF0WR05dvsGLzbtbs2M/zmFhKFSnAqZXzMonaUI39vkNHGTFuInfvR9Dl7Z4MGf6twQHauNhYpkybwYaVv1CqfDXmLj+YpTp7c9I4OVX2IAk/FUn6GkRRZPfmVfw0eQhJSQmMmLiItzs1M7qtE8cOM3bE59x/8JCP3u/JV4MH4ONtONrX8jJegUwQ8PPxZvG6rQyaMo9qZUvyTpsmvNWyMSF50osjJ4vf2hU3ExcuZ8qilXh6uNOhcV26tw2neZ1quMs1GV1Dkgd49uIF46b8wNoNmyhetAjfTphBrTr1Db7m780HmDNxMM+eRNK5x6d8OHAsPr6ZlzN0VFQP2VP2IAk/HTlR+qBb/M+eRDJ/2nD6fP4d+QsWRRRFQn0yV9ikRREfz8zpE1n5528UL1KYmZPHUataVYOvySj/qGcvWLN9P6u27uXs1ZvIZDKa1qrCSj039+QE+VtL8rHxCtbvOcKKLbsZ9cl71K1cjos37nDi4jU6N6tPkP/rJQiNiV4URdau38jYqTOIi4+jd9+BfPzp53h6ehl83c5DF/nify0oXroSQ8bMpWylzKtbOTKqh+wre5CEnwlJ+pkRRZEJw9+nQpW6fNKnp9F5y48fPcTYEZ8T8TCSvr3eZfigAXh7GRYBZJb/ldv3Wb1tHzfuP+CPiV8BMG/VBkoUCqNZ7aqpUagWa8rfUsk6A2lFr1Kp2HvyPCs272bD3iMkJCVTomB+pnzxEW0apJetMclruXPvPl+NnciBI8eoWa0K30ycTclSZfS2V6vVHDl3n5JlqyCKIvu2/0XDZp2QZ/itmTMo68gUDmSt/DJbCl8QhNzAKqAocAfoJoriCx3t7gCxgApQ6utMRpxV+GC+9CGz+A9vHE9cdGSmdvnCCtDu/ckmbdNc6YNu8ScmKPhu2Hsc27+FStUb8OX4X6hexvCJKj4ujhnff8fqFX9QvGgRZk0aR81qVUzuR0b5A6QolVR48yMinzwnT1AAb7VoxDttwqlZoXSm/G9W5O+KZIzmtQPiSckplOrwP0RRTdcWjejRrhm1K5ZJ/bxMlTzAg8hHrPx7PXMX/oa7uzuDvvyWt3u8bzAAuHH9Kt9+PYIbV87x+4bz5A3VXQ1li6geJNlnxJbC/x54LoriFEEQRgC5RFH8Ske7O0BNURSfmrN9WwkfnEP6a2a3JSlBRq58dVIfi35yArl7Au8M2Wn3aF8URbZv+JN53w9DmZLMR4O+o2/vHkaj/SOH9jNu5CAiox7zyf/eZ9jnn+LlaV55XcbB3h1HTrN62z42HzxOYlIy3332Pwa/10VvfX92lX9GyWvTYSu27CY2PoFza39BEATOX7tFmaKF8PTQfK/NkXzU4yds3L6T9Zu3cfLMOQDatWzGkNHTCQnVvyhLUlIis+YsYOWi6fj4BdDvy6m0fKNnpuNjK9GDJHtd2FL4V4FwURQjBUHID+wVRTHTdZ8zCh8cL/2I6wfY+kc/qjb6DUEmRxRVnDvQm1bvzaFQ6XDAtike0C3+J48imDFuADevnmfRutP4B+QixDva4HbiYmP5Yep41q76k5LFizF1zNfUrVXDoiXq0sr/ZbyCDXuO0KBaBYoVCGXzgeNM+2013Vo3oWuLhqm3/WvJDuLXNfh6+OwlfvhjLbuOnUalUlO9XEl6tG1Gny5t0qW9TBX9sxcv2Lx9F+s3b+PIiVOIokj5MqVo0b4rrdt1pHCRzOWzabn3wpNPu9fn3q0rtOjQk35fTiEod95M7STZ2x9bCj9aFMWgNP9/IYpipjotQRBuAy8AEfhFFEW9FhcEoS/QF8DTO6RGzearLO6fKTha+mtmtcEvsDH5CrXhccQOXj7fQbcvdqQTpbl35For2n/86D4h+QujTElh3/a/6N6lldFo//CBvYz75gseRj4iLH8orZs1oU3zptStWR13Cz7rjGmfrYdO8N0vf3L+2m3c3GQ0q1WVd9qE07VFI+Ty9PcUuJr804perVZz+OwlihfMT1i+PGzYe4ThMxbSvW043ds0pWyx12I3VfIxL2PZsnM3G7Zs58CRY6hUKkoUK0qrDl1p064jxUuWNrqNpKREotWaGvrlC7+nTMUa1KjXPFM7ZxA95DzZQxaFLwjCTiDzXRLwDbDEROGHiaL4UBCEfMAOYKAoivuNddzWEb4WU6SvUipQpsTh4ZUHQch8s5Kl0tdG+VUaLuL8oY/TRfdpsbX0Qf+g7s6NK5jydW+q1mrC0HE/U7VUkMHtxMfFsXP7Zg5uX8feQ0dITEwkMMCfFk0a06ZFOOEN6uudqkEfGcV/+dY9Vm3by+ptmjuI//t7ITKZjKt3IihRML/d5J+VieF0cf3eA1Zu2cPKLXu59+gx33zckxF9uqNSqRAEIfWEa6rk4+Lj2b57H+u3bGPvwcOkpCgpXLAArTp0oU27TpQuW96kqzBRFFm1bic/TxvO6B+WU6FqPb1tnUH25s5rlV1kD06Q0snwmrFAnCiK041t317CB/3SV6mSuHl+Fk8idiFz80Qm86BohX6EFGqZqa2l0l8zqw2JCjUeXupM0X1aHCV9URTZ8vfv/Dz9K9RqNX2HTKLPB28ZjfYBEhIUHD64j907tnJg9zZexMTg6eFBo/p1adM8nFZNmxCcJ7dZfUwrf7VazYPHzygUmpcUpZLSb3yIALzVsjHdWjehRvlSFqWV7I1arabdgG84dOZiaplqj7ZN6dCkrtFpDjKiSEhg176DbNiyjV37DpKYlET+0BBatutM2/adqVCpilmfycMH9xk1ajTHD2yldIXqfDnuF4qXrpipnS1Fr+mHJHtTsKXwpwHP0gza5hZFcXiGNr6ATBTF2Ff/3gGMF0Vxq7Ht21P4oFv6V09NRJmioESlL3D3CCT2xUWunBpL2ZqjCcqb/m5TS4QPoEq6xN9z2/Nm/w06o/u0WDLhmrXEHxV5jx/GfMrpo7vp8PbHDB71o9HcflqUSiVnTh1n986t7N2+kYiHkQiCQK3qVWnTPJw2zZtStLDpg42QXv5KpYoth06wettethw8QVJyCiUKhTHp8960a1TbpO2JoohKpSZZqSQ5JYWUFCW5A/1xc3PjyYsYop4+f/WckpQUJclKJU1qVEYud+PslZtcvHlH89yrNskpSoZ80BWAdbsPcfT85dTHk5Up5AkMSF1J6ps5v5E3V6BJUxxkJCk5mT0HDrFhy3a279mHQpFA3uA8tGjbiTbtOlK1ei2TTtAZWfDbKhbO+BoEgd4Dx9Kpx6eZpuNwFtGD/VI44JyyB9sKPw+wGigM3APeFkXxuSAIYcCvoii2EwShOPDPq5fIgeWiKE40Zfv2Fj6kl35KUjTHd7xDzWarkLu/Fu2jexuJfnKCCnUnZXq9pdIXldfIX6yuSZGXI6UviiKb/lpMybJVKFuxJsnJSRQMUJgdRYuiyLUrl9i9cyv7dvzLxVfTMJctVVIj/xZNqVS+nFnbTSv/6Ng4Nuw9wqqtexnZpwcNq1fk331HmbpoZSZhb5k3iZKFw5i3agMjZi3KtIjL1Q2/EZYvD1MWrWTiwuWZ9huxcwWBfr58O+c3Zi/7J9Pz0Yf+wc3NjeEzFrJs0y7c3eV4uMvxkMvx9vJk58LvCfRLf0xNkXxKSgoHjx5n/eZtbN21h5exceQKCqJ56w60ad+JmrXrGZ0rSR/aabRX/z6TcycP8Pk3swjJn/m+BUn2zod045WZaKUfH3OTS8dHUT18SbrnY19c4tZ/s6nebLHO11sqfVvdoJUWa+b2AWZ9N5DIiNsMGTuPysUDLOoTwIOI++zZuZUDOzZw9ORp1Go1YflDUyP/OjWqmTXoq6vGf/exM/y8ZpNGtu5y3OWav0f26UFYvjwcPX+ZnUdP4yGXp0rZ092dd9qE4+/rw5Xb97h6JwJPD3c85HI83N1xd5dTvVxJ3OVynka/JE6hwEPunk7qXp4eJp24TJG8SqXiyIlTrN+8jc07dvMiOpoAfz+atmxPm/YdqVOvkUWD46A5CR84dYsd/y6jYrX6hLd+C7VajSAIdi21BPMXCZJk/xpJ+Bbg5u6OSpnIsW1dqNJgPl6+ryV+7+pvpKTEULraML2vzynS/3f1Qn75YSQymYxPh31Pr56GZ040hRfPn7F/7650g75BgQE0b9zIokFfXfK3J+bUxOtCrVZz4sw51m/exqbtO3ny9Bk+Pt6EN29Dm/adaNAwHA8z73tIy67Dl9i/4x8O7lrP48j7yNzceOd/Q+gzaLzO9s4wKJvaFzvm68H5ZQ+S8C3Gzd2d+9dX8OjOBoqU/QRvv4I8izxA5J2/qdp4Pt5+hudXt4f0wfHij4y4w/Qxn3DuxH5qNWjF0LHzqFDUOou72GLQ1xUQRZGzFy6yYcs2NmzdQeSjKLw8PWkU3oI27TvTKLwZ3jrmkzeFBy99uXntPGUq1ABgSO9WXD5/nJr1WtCwRSfqhbcnIDDzZ+rKUT3kDNmDJPws4ebuzuOInTy89TfJic8IyF2JwmU+wMfftHlYLJU+uFa0r1ar2bDqF/78ZQpz/txH/oJFSU5KxMPTy6yBXUPYYtDXmRBFkYtXrmkkv2U79yIe4O4uJ7xhfZp3eIfwZq3w9fMzviEd3Hvhyakjuzi4az1H9m4iPv4la/bcJTAoDxF3b5A7OETnbJZabLkKlSVrPEuy148k/Cxi78nW0uJq0X5SYgKeXpopk/t3b4AoitRp3IbaDVtTpmJNwvxiLepfRmwx6Osort24yfot21m/eRu37tzFzc2NRvXq0LxDN5q1bENAQKBF29UOvB7es5FJI/5HYkI8fv5B1AtvT8MWnajVoBUeHoZF7mxRPUiyN4YkfCsgSV83+nL7oiiycvF0ju7fwuVzx1Cr1QTmCqbnR8Pp+v7rhautFf1be9BXFEWUSiVJyckkJSWRmKT5W/P/NI8lJ6X+PykpmcS0/9f52lfPJyeTmJTE8+cvuHX3HoIgUL92TZq1f4uWrduTK7d5x1zLjSiRI3s2cWDXelq+0ZPGLd/kYcRtVi3+gUYtOlOlVmPc3TNPQZ0RZxQ9SLI3BUn4VsKVpA/OIX6AlzHPOXloB8cObKV2w1Y0b9+Dp1EPmDD8A+o0bkOdRm0oVqqi0bn4TUXXoG9ggD/1atVEJhPSCTejnNNKW61WZ6kfcrkcTw8PPD098PT0xMtD87f2/zJPf7x9vKlTrxGt2nQgbz7D6wzr42GcP1v+/o0Du9Zz9vheVEoleUML8uGAMbTq9J7J2zF3PVl7DMpqMVf0kDNlD5LwrYokff0Ykn5Grl48xcxxn3HjylkA8oYUoHajNvT8aBghYZrpCqwR/SckKDhyaD+7d2zlwumjuMvlr4Xr5Y+nhyeenp54eHri6en16m9PPF49nu4xT0+8PL0yPebp6YWnh2eax7zw8PBAnmEuf2ty/tZL7t68Qu2GrRBFkd6dqqIW1TRq0ZmGzTtRpoJpE9dZsmi4M6dvtORU2YMkfKuTU6QPthf/08cPOXFwO8cObOH00T0sXn+W4HxhHDuwlQd3b1CncVsKFC5htdSPK3Pq8hMO7FrHgZ3ruHbxNL7+gfy19z5yd3diXjwlICiPzSSvxdmjesjZsgdJ+DYhK9LPivDBMumDfaN9ME/8AMqUlNQVkmZ99zkb1ywEoGCRUtRu1Jq6jdtRvW5TwHq5f2dGFEUeKQKRyWSsWDSNRbNHA1CmYo1XkXxnChYpadK2siJ5yBlRPbi+7EESvk1wZJQP9pc+WC5+MF/+AA/v3+LYga0cP7CNsyf2UaxUBeatOATA1nV/kC+0EOWr1KFI7mSL++VsJCUlsn3/eY7u28zR/ZsZ/t1CqtZuws2r5zl3Yj8NmnfUOcWBLrIqeXD+QVktkuxfIwnfhrhaekeLvaP9jJh7AkhMUPDkUQSFipVGpVTyZqMwFPGxuLt7ULZSLarUakyDpm9Qqnw1l4v+oxKCiH7+hJnjB3DqyG4SE+Lx8vKhRv3mdO/9JeUqmzbxG1hH8uA6ogdJ9hkxJHzbjSrlEFQpKVmSftTthxZLX/sjsUT82h+mueLXiiCr4s8oJmMnAC9vHwoV0yzQ4SaXs2LHDS6eOcK5k/s5e2IfyxdORS53p1T5atx6ImPNkllUqdmY8lXqUDiX+VcXutDWtYuiSFJiAkmJCtRqFbnyaKprrl48RcyLZyQmxKc+H5grmEYtOgPwx/yJPIl6QFKiIrVNrQYteeuDQfgH5OLRg7u0fONd6jVpR9XaTfDwNL4ovLUED5ZLHiTRuwpShG9FHJnXB9eO+DNi7hVAfNxLVColAYG5uXDqIEP7tEatVuPu7kG5yrWpUrMxZSrWQKVSkpigSBWyIJPRucenAKxbMZ8rF06+ErIiVdhjZqwAYNTAtzhzbA+JiYrU/ZYqV435qw4DmhvNrl06na5flao3YObvOwEY0LMhT6Me4OXti6eXD17ePhQvXYnBo+aY9V6dRfLgGNGDJHtDSBG+nchKtK/9AmdF/K4c8WfE3CsAX7/XM3VWqtGQvw885OLZI5w7sZ9zJ/ezbOEUipWqyM2r59O9zj8wd6rwb145z3+nD2mE7O2Nl7cv3j6vpzKo2aAFBYuWwsvLJ/X53MGvF4MbPHoOypTkVKF7enmne/3c5QfN/yBe4UySB0n0rooU4dsARw/oaslOEX9GzL0CiIuNIfr5ExIUcanC9vTy0fz71VQQzoSzCV6Lo0QPkuxNRYrw7Yz2S+XIaB8cG/GDbeVv7hWAn38gfv6WzUljL5xV8uDaET3kDNGbgiR8G+LIAd20OEL8oFs6tjoJmHsCsCfWFLk+rC14La4uepBknxZJ+DbGWaJ9cJz405JRTM5yArCHlK2NrSQPkuizK5Lw7YSzRPvgHOLXYq+rAFcUui6cLZLX4kyiB0n2+pCEb0ecKdoH5xJ/Wux1FeAKOKvgtUiidy0k4TsAZ4r2wXnFr8WeYwHOgC0kby3Ba3H0DVMZkURvGpLwHYQ1on1rSh+cX/xpyW5XAa4geXA+0YMke3OQhO9gHH2zli5cSfxaHHUVYMuBU3OxheC1SKLPHkjCdwKskeIB5xR/Rux5InAmGdsCWwpeizOKHiTZW4okfCchqykeSP8Dc5Ycf0Z0ScreVwOuij0ED84reZBEn1Uk4TsZWY32tdgi6rem+NPiDFcDzkpWJK9WKTl/cCGXjq9ErUymROV2VG8+CE+v1/MOWWPKA5BE7ypIwndCrBHta3El8WckJ14NWDOK37r0Y549vENYsXeRyb25e2UD18+2oVmPtbjJrXNfgiR610ISvhNjrWgfbJPuSRsd2lr+Wlz9asBeaZknDy4Qcf0g1Rr/gcxNI3f/oPJcOj6UiOtbKFKuc5a2L+XnXRNJ+E6ONaN9La4c9evDVlcD9hK0NUh7Ar51fh+BeWqmyh5AEXublKRYLh35iftXNqV7bZHynSlUpr3RfUiid20k4bsI1oz2tWRH8afFlWRtCYby795+oSTE3U33mJvcB0XsLYpX+gJ3d+3MoSI3/5tFkfKd9G5Lknz2QRK+C2GLaB9sn+5JizOcCFwVcwZYQ4o2QrVnPBE3VxJWrCuC4EZc9BXc5N4kJTwitHAHAF48PoanTy4Klm6XaRuS6LMfkvBdEFtE+1psVdOvxZC0pJPBa7JaPSOTyWny1h8c3zqMkztXIMjkePrkoVabaZzcPpKwYm8jdw/g/vUlVKw/GEGQAZLkszuS8F0UW0ofbC9+XeTkk4G1yiPT4htYiKbvrCQh7jFqVTI+AQUQBIHI2/uIvL0W/1wVUSkVuLtXlUSfQ5CE78LYKsWTFlvdzGUurnIysIW4s4q3Xz7g9bHMV+BtTu/+kOdRRyhStndqdG9NJMk7J5LwswH2ED84Juo3BWuOFzijsC1FX9Tu5RNKcIFwXj47T3CBplbdpyR650YSfjYi7Y8tu6V7LCE7ydsUzEnLlKg0kJTkWKtE95LkXQdJ+NmUnJTuyclYmnt3k/vgJvfJ0r4l0bseWTq9C4LwtiAIFwVBUAuCUNNAuzaCIFwVBOGGIAgjsrJPCfNQpaTY5YcZdfuhzW+zl3j9OTvy87bXd0rC+mQ1wv8P6AL8oq+BIAhuwFygJRABnBAEYYMoipeyuG8JM7B3ugekqN9aOMOJVBJ89iBLwhdF8TKAIBhcaKI2cEMUxVuv2q4EOgGS8B2EvQd5dSGdDPTjDILXIok+e2GPHH4B4H6a/0cAdfQ1FgShL9AXwNM7xLY9y+HYS/y60Ce1nHgicCbBgyT57IxR4QuCsBMI1fHUN6IorjdhH7rCf71LEYmiuABYAOAXVCZ7L1nkJDhS/BnJTlcFziZyfUiCzzkYFb4oii2yuI8IoFCa/xcEXOOXkMOwV57fUpzhZOAqEjeEJPiciz1SOieAUoIgFAMeAN2BnnbYr0QWcKao3xQsTRFlB4EbQxK8hJYsCV8QhDeBOUBeYJMgCGdFUWwtCEIY8Ksoiu1EUVQKgvAZsA1wAxaLongxyz2XsAuuJv6M5AShZ0QSvIQ+slql8w/wj47HHwLt0vx/M7A5K/uScCzOnu7JqUhylzAH6U5bCbNx9ajflZEEL5EVJOFLWIwkftsjCV7CmkjCl8gykvithyR4CVsiCV/Cakh5ftORxC7hCCThS9gEc4SWXU4OksQlnB1J+BIOx1xR2vMEIUlcIjshCV/C5bBEwmlPEpLEJXIqkvAlcgSS5CUksrgAioSEhISE6yAJX0JCQiKHIAlfQkJCIocgCV9CQkIihyCIovOuMSIIwhPgrp12Fww8tdO+XAnpc9GN9LnoRvpcMmPvz6SIKIp5dT3h1MK3J4IgnBRFsaaj++FsSJ+LbqTPRTfS55IZZ/pMpJSOhISERA5BEr6EhIREDkES/msWOLoDTor0uehG+lx0I30umXGaz0TK4UtISEjkEKQIX0JCQiKHIAlfQkJCIoeQY4UvCMLbgiBcFARBLQiC3pIpQRDaCIJwVRCEG4IgjLBnHx2BIAi5BUHYIQjC9Vd/59LT7o4gCBcEQTgrCMJJe/fTXhg7/oKGH189f14QhOqO6Kc9MeEzCRcEIebVd+OsIAijHdFPeyMIwmJBEB4LgvCfnucd/l3JscIH/gO6APv1NRAEwQ2YC7QFygM9BEEob5/uOYwRwC5RFEsBu179Xx9NRVGs6iw1xtbGxOPfFij16k9fYL5dO2lnzPhNHHj13agqiuJ4u3bScfwOtDHwvMO/KzlW+KIoXhZF8aqRZrWBG6Io3hJFMRlYCXSyfe8cSidgyat/LwE6O64rDseU498J+EPUcBQIEgQhv707akdy4m/CJERR3A88N9DE4d+VHCt8EykA3E/z/4hXj2VnQkRRjAR49Xc+Pe1EYLsgCKcEQehrt97ZF1OOf077jpj6fusJgnBOEIQtgiBUsE/XnB6Hf1ey9QIogiDsBEJ1PPWNKIrrTdmEjsdcvo7V0OdixmYaiKL4UBCEfMAOQRCuvIpwshOmHP9s+R0xgCnv9zSa+VziBEFoB6xDk8bI6Tj8u5KthS+KYossbiICKJTm/wWBh1ncpsMx9LkIghAlCEJ+URQjX11uPtazjYev/n4sCMI/aC71s5vwTTn+2fI7YgCj71cUxZdp/r1ZEIR5giAEi6KY0ydVc/h3RUrpGOYEUEoQhGKCIHgA3YENDu6TrdkA9Hr1715ApishQRB8BUHw1/4baIVmEDy7Ycrx3wB88KoCoy4Qo02JZVOMfiaCIIQKgiC8+ndtNJ55ZveeOh8O/65k6wjfEIIgvAnMAfICmwRBOCuKYmtBEMKAX0VRbCeKolIQhM+AbYAbsFgUxYsO7LY9mAKsFgShD3APeBsg7ecChAD/vPpNy4HloihudVB/bYa+4y8IQr9Xz/8MbAbaATcABfCho/prD0z8TN4CPhUEQQkkAN3FHHBLvyAIK4BwIFgQhAhgDOAOzvNdkaZWkJCQkMghSCkdCQkJiRyCJHwJCQmJHIIkfAkJCYkcgiR8CQkJiRyCJHwJCQmJHIIkfAkJCYkcgiR8CQkJiRzC/wH4z6IxUj3jawAAAABJRU5ErkJggg==\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXwAAAD4CAYAAADvsV2wAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAABhxklEQVR4nO2dd3hT1RuA35umewKFlrL33nuXPQUERcDBT1BEEEEQBJUpU5AhAgqCIrJRAdl7743sDYVSZkvbdCW5vz9CSkd2M9v7Pg8PkJzce5KbvPe73/nuOYIoikhISEhIZH9kju6AhISEhIR9kIQvISEhkUOQhC8hISGRQ5CELyEhIZFDkIQvISEhkUOQO7oDhnD3CBQ9fUId3Q0JCQkJlyE+5tpTURTz6nrOqYXv6RNK1UYLHN0NCQkJCZfh0Mbwu/qek1I6EhISEjkESfgSEhISOQRJ+BISEhI5BEn4EhISEjkESfgSEhISOQRJ+BISEhI5BEn4EhISEjkESfgSEhISOQRJ+BISEhI5BEn4EhISEjkESfgSEhISOQSnnktHQsJVcXN3N/s1qpQUG/REQuI1kvAlchSWiNheGOqbdDKQsAZWSekIgrBYEITHgiD8p+d5QRCEHwVBuCEIwnlBEKpbY78SEsZwc3dP98dVyfg+XP39SDgGa+XwfwfaGHi+LVDq1Z++wHwr7VdCIh05TYjSiUDCHKyS0hFFcb8gCEUNNOkE/CGKoggcFQQhSBCE/KIoRlpj/xI5G0lwmdH1maiUCahVyQiCN4IgOKBXEo7GXjn8AsD9NP+PePVYJuELgtAXzVUAnt4hdumchOshSd50UpJfcvPcLJ5F7gdBhrdvQUpUHkRgcJV07aRxguyPvYSvK5wQdTUURXEBsADAL6iMzjYSOQ9J8JZz+fi3ePkUoGaL1bjJfXkWuZ9Lx76mapNf8PYrmNpO51WBdBLIVtirDj8CKJTm/wWBh3bat4SLIuWks05czA0S4iIoXnEQcnd/BEFGcFg4+Qq1JfLOeqOvlz7/7IW9hL8B+OBVtU5dIEbK30voQpK8dUmMj8THvziC4Jbucd+AkiQqHpm8Hem4ZA+sktIRBGEFEA4EC4IQAYwB3AFEUfwZ2Ay0A24ACuBDa+xXwvXJLgIJKRZmtW1F3bbexa9fUClioy+hUipwk/ukPh795AR+gaUs2mbaYyalfFwLa1Xp9DDyvAgMsMa+JFwfZ5C8NQVtbXT1zdKTgJdPKMH5G3Pp+EiKlP0Id49cPI7YQsyzM5So/HlWuyrJ38WQ7rSVsDmOFLwzi90c9L0PU04EpaoN48GNNdz8byaqlHhy5atNlcbzcfcMtGofJfk7P5LwJayOJHj7Yej9ak8GguBGwVLdKViqu726JcnfSZGEL2E17C36nCZ3fSQnRPM08pTO53KHVsHLJzjT49YcJzCGJH/nQRK+RJaxl+glwesm6t5Bjm35glz5aqd7PPrJKao1G0vxSpkje1OuDGyBJH/HIglfwmJsLXpXFnz+wnnMfk3kvWcW7atAydZ4+4YSVqw7gXkqAxAXc534l9cpUraz2dvL+Lnb6gQgyd/+SMKXMBtbid7WgrdEwvZEX/+MnQhkbu6Uq/sZN8/+QWCe6QBE3FhK2Vqf4ObuleV+aY+LFPm7PpLwJUzGmqK3R/Tu7II3FVNOBEXLd+Hy0Z+IeXYeN7k3cTGXKVH5F6v2I+0xk+TvmkjClzCKNURvr/RMdpG8KWR8r/Xaj+DMniUIMm+rRff6sEfUD5L8rY0kfAm9ZEX0kuDtT9laPTi2dSrKlEQatF+D3MM7UxtLxwn0Ya+oH15/HyXxW44kfIl0ZDWaz+l5eEfi5uZOs26zSElR6JQ9WD5OYAr2TvlI4jcfSfgSgHOL3lkkH1bA1y77efgg3uLXFi3f0qLX6fqMs3ISsNdAryR985CEn8NxRtHbQvD2krU10NfXrJwILEF7HKwhfrCN/KVo3zwk4edQnEn0torgXUnypqDr/djjJGAN8YNto35J/KYhCT+H4SyilyRvHex5Ekh7zJw16pfSPIaRhJ9DcLToJcHbD3ucBKwd9YP15C9F+/qRhJ/NcZTobTnQ6kqSLxCiazln3TyIst0SzrY6CVhL/GD9lI8k/sxIws+mOOpmKVeN5M0Rs736YMsTAFj3JGAL8YN15C+leV4jCT8bYa2pD5xF9NaSvDPI3BLsfQKAzJ+5uScAa+X5tVhL/lK0r0ESfjYgO4k+p0veELrek72uAiyJ/K0Z9YN1Uj45XfyS8F0cV0/dWDNVkx0lbwx7XQU4m/izmurJqWkeSfguiiMnNMuq7CXJ2w5bXwU4i/ilaN8yJOG7GK4q+uwo+fxBSTbZbmS0p1W3Z4urgLTH05F5fkn85iEJ34VwtRJLZ8vH20rQ1iZjP219AoCsnQScIeqX0jymIQnfBciJos9pkjeErU8AYJ2rAEeLX4r2jSMJ38lxxJz0jhK9K0heFEUU8bEkKOJIVMSToIgjQRFPcGgBwgoWIz7uJdvXL0URH0dCQpzm+fg4mrbtRq0GLXlw7ybjh/ZMfV1KchLFS1fk/X7fUK1OuMXvz1ZXAa4qfmtE+5D9xC8J34mxt+wdIXpHSP7EoR3ExUanE3bRkuVo0KwjarWa7758lwRFPAkJr6XequN7vPfJSBTxsXSqH5Jpm+9/8jW9BowiMSGeuVO/BEAud8fb1x9vH18qVW8AgKenF/nyF8bbxxdvHz8EQca1S6dSt3P2xH4W/ziayjUbUblGIypWq4ePr7/Z799aJwBXFb81pA/ZL80jCd8GJCc+43HEVp3PBeapin+uCka3YansXSGqt5fkRVHk0rmj7N68Ck8vH/oOmQTApBH/Izbmebq2rTq+R4NmHZHJZDyMuI273B1vHz8Cw/Lg7eNH/oLFAPDy9uWToZPx9vHD28cXLx8/fHz8CSukeT5XnhAOnriEj48v7h4eGXoUTUhRXxYsXKizv1EJoExJRhRF1iyZxcpF05G5uVGqXFXGzVxFcEgBRFFEEIx/ftY+Abii+K01VUN2kr4gira/e89S/ILKiFUbLXB0N8wm/uUtzu7vTf5iXZHJXv/QHkdspUCJ7hQo3k3va7NrVG/PSP7uzcvs3LiCPVtW8+jhXTw8vWjRoSdDxswF4MaVc7i7e+D1KsoumlupQ86ORaFQsPfoZc6dPMDV/04yae463ORy5n0/jPMnD1K5ZkOq1GxExeoNCAwy/xhm9QSQlUFeS6dvyEqaxxrRvqtI/9DG8FOiKNbU9ZwkfBtx+cQ3BAZXJ6xYV0BzErh4dCg1mi3DTe6j8zWuENU7QvSmSD7q4V3yhhZCJpPx05QhbFi1gBp1m9Os3Tu82a4xvn5+We6Ho4lKCGLT2sXs2bKaS+ePkZyUCECNes2Z+stGABITFHh56/5+GcKSE4AjpA+Wiz+nSF8SvgOIi7nO5eMjqd5sGW5unlw5NQ6/wNIULNkjc9voq9y9toiYp2dx9wgif7GOFC79PoLMeMbNGaN6e0n+xbPH7N/xN7s3r+Li2aPMWLyDyjUb8vTxQ0L9FOTJE5zlfjgryUlJ7D95g/MnDyLIZPT8aBgA77Upi4eXN5VrNKJyjQZUrtmI4Hzmf0fMOQG4UrSfE6QvCd9BaKP8wDzV9Eb3CXH3uXB4IIXLfkTesGYkJURx6+IcfPyLUKrql3q37Yyih6zJ3tSB16ePH/LDmE85dXQXapWKoiUr0KzdO7z3Tkfy5ss8oOosKJVKFIp4FPFxKOLjiY+PJzk5iTJlK+Dnb3xg1hgPY/1Y88cszp88yH9nDqOIjwWg50fD6f35ONRqNU+iIgjJX9jkbTqz+CXp60YSvoPQRvl+QWXwz1VRZ3R/6+KPyOX+FC7zYepjypRYTu7uQa0WK/Dwyp3pNfaSvT1Eb4rkk5OTOHFoOynJSYS3fgtlSgqfvx9OjXrNadq2G/WqFLRo34YQRZGU5GTi4+NQKBSav1NFneExRTzxcfGav189lhwfTXx8PPGKBOLi44mPV5CYpPu9yuVyalatTO3GrajfKJxy5Sshk8my1H+lUsnhs3c5f/IApSvUoErNRty8ep5P3q5DaFgRTRXQqz/5CxQ1OhDsrOKXpJ8ZSfgO5PKJb3j54j9qNluRLrrX5uvPHRhIwRI9Ccqb/vicP9Sf4pUGEpincrrHzZW9M6ZvTJG8SqXi/MkD7N68igM71xEXG03ZijX5afkBAEK8o83a553bNzl0YC9xsS+Jj49/Ler4OBTxCpLjXmjErFAQr1AQF69AqVSatG2ZTIafrw++vr74+vjg5+uDn68vPj7e+Pn64uvjnf45H198fL3x8/FFEASOnTrN3kNHuHj5KgB5cueiToNwGjQKp37DcILz5jPrverjSkQSe7eu5dzJA1w4fYiYF08BmDx/A7UatOTp44ckxMdRsGgpnScAZ5U+WCb+7Cp9SfgOJFERSaIikqDg6qmPpR2cvXZmGu4euShculfqY8qUOE7u7k6tFsvx8NIIOztE9aaUUWpF88PY/mz5+ze8ffxo0Kwjzdq9Q5vwaribMbCtUCjYvuVf/l2zhOOnzqQ+7unhga+vzysBpxezn6+vzuf8Xj3mm7adjw++vj54eXqaVCppjMdPnrL/8FH2HjrCvkNHePb8BQAVypWhTqOWNGgUTrXqtaxSUSSKIicuPuLcyQM0a/cOfv6B/LlgCr//NI7cwaG079qbd3oP1TkA7Kzil6SvQRK+E5GxEkcRe5ez+z+lWPn+BIc1I0kRxa2Ls/HyCaV09RGAa0f1pkTz929fY/eW1ezesoqJP/1DwSIluXTuGI8f3adj6/p4m1F1Iooi58+e5p+1y9m6cR3xCgXFixahR9fOdGrXmpC8wWadNByFWq3mv8tX2XvwMHsPHubk2fMolUp8fLypX7sWtRq3pmHjphQuUsxq+zx/M4aTh3dxdP8WjuzdSEhYYT4d9j0Nm3fS2d4ZxS9JXxK+06Cv7PLl8/+4ffFnYp6ew90zkNAiHSlSrjcymdws2TvLoKwpko+LjWHL37+xe/Nqrl8+gyAIVK3VhL5DJtGwhvkSe/bsKRvXrWXDmqVcu3kLb28vOrZtTfcunahdvapVInBHEhsXx6FjJ9h38Ah7Dx3h7v0IAIoUKkjdRs2p3yicOnUbWq38dPuBC8yZ9AUly1ZhxKRFetuZW85pD/HndOlLwncCTKmxT5vSsEdUb4v0jSHZv4x5zvMnjyhasjyxL1/QrWlRipWuSLN27xDe+i0qFDGvflypVHL4wF42rv6NHXv3o1QqqVG1Mj26dqZj21b4+Tp2sXP/mPtmtY8NLGRy29t377H34BH2HjrMoWMnUCgSrD74q1QquffCA1+/AG5cOceeLat5t+8InVM9ZIdoP7tI3+bCFwShDTAbcAN+FUVxSobnw4H1wO1XD/0tiuJ4Y9vNDsK35GYqZ4rqsyr6xAQFR/ZtYvfm1Zw4uI3SFarz49K9ADx/+ohyhbxM2n5a7t29zT9rV7Dx75U8evyE4Dy5eatTB7q/2ZHSJUuYvT0t5graEIrEJGJi44hVJBD36o9SqaJZnWoArNt9iP9u3CEpOYXalcrSrHZVfL1ffxbmyD85OYUTZ85q0j8ZBn/rNmxKg0bh1GsYTnBwXovfz4/zFrNw5jfkyZeffkOnEN7mbYcN7ErSN4xNhS8IghtwDWgJRAAngB6iKF5K0yYc+FIUxQ7mbNvVhe+Msrdn+mb5r9NYvnAqiQnxBOcLo2mbt2nW7h2LUjYKhYKd2zby7+olHD15GplMRrPGDenRpRMtwhsZzMsbE7koiiQlpxCrSCBPoD8ymYzbDx5x/e4D4hQJxCoUqdL+stfbuLm5sXTjTjbuO5r6eKwigZQUJRf+1syT03fcTFZs2ZNuP7kC/Li3fTkA742cwvo9h5G7uaFUqfD0cOetlo35edSgTP0zR/5gu8HfPUevMGfSEK5fPkOVWo35bMQMipXKPC+UPdI8kvT1Y2vh1wPGiqLY+tX/RwKIojg5TZtwcpjwzZW9M6VwshLVX7lwgmKlKuLp5c32Dcv478xhmrV7h5YNK5qdXhBFkf/On+XvNcvZuvEf4uLjKVa4EN27duLtzm8Qms9wuaJW9HGKBP7ZfYgdR04xdfBH5M+bhz/+3cGURSs1so5PQKlSAXB94++EBudm0sLlTF60MtM2I3auINDPl5lL/2LN9v34+Xjj7+ON36s/c0YOQCaTsef4WW5FRGqe9/XB38ebAD8fqpTRXIEkJafgLndDpVZz6MxFNh84jq+3F2M+fR9RFHl3xGSqlStJh8Z1KVusUGo0ba78jQ3+1m7ShgaNwk0e/FWpVPy+7B8W/TiGzt370WvAKL1tnUn8OUn6thb+W0AbURQ/evX/94E6oih+lqZNOPAXmiuAh2jkf1HP9voCfQE8vUNq1Gy+Kkv9cwS2lL2zRvVxsTEs/nEM/65ewIefjaXnx8MB8+vlAZ4/e8rGDX+xYfVSrt64ibe3F2+0bkn3Lp2oU7O6wQFYreRFUeTUpess2bCdv3YcIFaRQKHQvKz/cTylChdg59HTrNm+XyNrX+9Ucfdo14wAXx/uRT4m8unzdDL39/XGXW77CWafx8Ty5uAxnL58A4BiBUJp16g2vTq2olzx9HfJmnsCsNbgb/SL57wkP55e3pw4tIMXT6No8UZPnSd1W6d5nFX62VX4bwOtMwi/tiiKA9O0CQDUoijGCYLQDpgtimIpY9t2xQjfmWRvj0FZURQ5uGs9P00ZwvMnj3izZ3+GfznY7GoRlUrF4YN72bT6N7bv2UdKipLqlSvRvWsnOrVrjb+R7aUVvSAIXLl9j1o9PsPHy5M3mzekV8eW1K1czu7VOvJHd01qpwwtkumxh4+fseXgcTYfOM7ek+dYPP5LOjWtz92HUZy+fJ3mdasT4Pt6oNtc+YP+wd83Or/NoC+/NjofUVRCEBO/6sWeLaupULUun42cSalyVTO1s3W0b6sUjytK3+EpHR2vuQPUFEXxqaFtu5LwnS2FY69B2d9+GseyBVMoUaYyX4yZS5NaJU3uI8D9u3f456+VbPx7BZFRj8mdK4i3O3XgnTc7Uba04W1pJa9Wqzlw+gJLNuzA19uLOSM1F5d/7ThAi3rVCfQz/cRnqqBtTcYTQGy8Ag93dzw93Jm59C9Gz12Ch7ucxjUq0a5hHdo1rk2BfK/lbIn8tYO/m3fsYumqv/Dx9qL/4BG807MXcgNXNmq1mj9Xb2LhjG+IiX5Kh7c/5sOBYwgIzDwtiC3FL0lfg62FL0czaNsceIBm0LZn2pSNIAihQJQoiqIgCLWBtUAR0cjOXUX42Tmq1yV6lUpFUqICH19/bl+/yPGD2+jf9wODUkhLQoKCnds3s3H1Eg4fP4lMJqNpw/p079qJluFN8PAw/HlqRR/55Bl/btrF0n93cvvBI4L8fenVsRUTBn6Y6TXOInJLyCh/pVLF0QuX2XzgOJv3H+NmRCSeHu7c274cHy9PnsW8JHeAf7qrGXNPANdv3WbUxO/Zf/go5UqXYviYqdSsXc/ga16+jOGHGXNYt/Jnvhz3C606vqu3rTOIP7tK3x5lme2AWWjKMheLojhREIR+AKIo/iwIwmfAp4ASSACGiKJ42Nh2XUH4rij7rET11y+fZeb4ARQsUoqvp/xuco5eFEUuXjjHP2tXsOXfv4mNi6NIoYJ076IZgA0LNTzLpVbyKUolbjIZMpmMr39czJzl62hcoxK9OrbijSZ18fZ6LRJXlrw+MspfFEWu3ongvxu3eatlYwCafzycyCfPaNewNu0b16FBtQp4pPmemip/URTZsmM3Y6ZM50HkI9p37MKQ4aPIFxJq8HUnLz2mQJGSyGQy9m5bS0j+wpSrXDtTO0n6tkG68cpGmCN7W6Zw7BHVJyji+WP+BP76cw4BgXkY8NU0undpZXRbL54/Y9OGv9mwZimXr13Hy9OT9q1a0OOtztStWd1o5Y5W9DfvP+SPf3ewbNNuFoweTLM61Xj4+BkJSUmUKJT+s82OoteFrrw/kFoyuuf4WRKSkgn08+WLD7oy9IO3MrU1Rf6KhAR+Wvgb8xctQS6X02/gl7z7QR+jZZ2R8QF81KUG925doc2bvejz+Xhy5clcWWUr8Ttjisce0peEbwNsJXtnjOqvXTrN+CE9efTwLu269ubrkcMIDAzSuw2VSsXRw/v5e80K9u7cSnJKClUrVaB7l050bt+GACNzv6eN5v/eeZAlG3Zw4PQFZDIZrevXZPiH3ahZoXS61zir5JUR90xqJy9o+hz1eveV4QSgSExiz/GzbD5wnIbVK9KjbVOePI+m9+gfaNeoNu0a1aZIWPorK0MngDv37jNmynR27NlPyeLFGD56CvUaNDbYp/i4OGbMnsdff87By9uX/w0YTcdufXHTkf6zRTVPTpS+JHwr4wyyt+cNVM+fPmLsF935ePAEWjSsaHA7ly6eZ8LXg7lw6TK5goLo+kY7erzVmXKlDRdlpb056smLGPLmCkSpVFGucx+8PT14/40WvNuuOWH5Xn9GtpK8qZK2JVk9AeiL/s9cucHHY2dy9Y7m865YsijtGtXm467tCA1OP8iqT/479x5g9ORp3Ll3n/atmjPomynkDzO8JsGJS1H8NHkIp4/u5seleyhfpa7OdrYq4XS2FI8tpS8J30o4S77eXjdQLVv4PeNmrUImk5mUq1/2xyK+nzia4Ny5+Wbo53Rs1xpPI5f9aUV/6eZdhkz/mVsRkVz6ZxFyuRt3H0ZRKDRvutSPJaJ3Bolbii3kf+PeQzYfOMbmg8c5ev4y59cuoHD+fDx+9oLcgQHI5W6AfuknJiXxy29Lmf2LZmK12fN+o36jcIP9EEWRfcevp+bzH0bcJqyg7hu+TBW/JP3MSMK3Eq4U2WdF9ndvXWFwr+b4+Qcyd8VBSoUav0P2z98XMnXiaNo0b8rMSeMIDDAtbQOa1MOURSuZs3wdAX4+DP+wG33ebIuXZ/qTRU4TfUZsFfW/eBlHrgDNfQ4dB47iftRT5n/7OXUrlwMMp3kiHkbyvwGDuR/xkD/XbKJ4ydJ622qJSgji5OGdfD2gM99+v5TGLd/U2c7a0rdFesfVhJ+1ddRyEDlF9lGR9xjxSQfkcnem/rLRJNkvW/IrUyeOpl3LZvwyc6pZso988ozaPQYwc+lfdG8bzqlV8xnQvZMkex1k9f3IH93V+TlqZS+KIn3fbo9KpaLrF+M4d/UmYHguooJh+fl97iy8vDz5vO+7RL94brQfId7RVK7ZiNLlqzNtVF/u3rqis52paxybijlpUHtgyVxbWUUSvgk44sBkxB6yj37+hBGfvIFCEceUnzdQrXTmG2cysuyPRUyZMIq2LZox/4cpRicx08oj+VV0Exqcm+Z1q7Nl3iTmfzuI4KCAdK/RJylDKCPuZTvZa7HGe9P3mQqCQIfGddkyfxKB/r50HjyW6/ceAMalv3jODB5FPWb4gA9ISU422gcPD0/G/LAcTy8fxn3Rnfi4lxa/H0vXU7YGlqxE50gk4RvBWfL2ppDVu2afPn5IYkI8E35cS/2qxtMHy/5YxJTvvjVZ9qCp4Jm/6l8qdvmYB4+fIggCs7/qT8PqmQeDs3tUH3srgthbERa91pbiL5AvmPU/amYvHzx1XurjhqRfo2plZkway5ETp5gwdiTGUsUh3tHkDS3It9OWEnHvBtNGfazzNaZG+aZ+900NnKz923QWbD8TlAvjLLI35UuaFdmr1WpkMhkly1ZhyaaLFApMMLqd5UsXM+W7b2nTvCnzf5ii9+7YtJI4c+UGg6bM48yVGzSvUw2VSq3zNa4mekulnfH1/sUNV7roQvu+s5Lflz+6mym/X6pwATb8OD7TFZd/zH29Of0327fl+s3bzJq/kOIlS9Grdz+D+w3xjqZqrcb0GzqFlBT9VwX5g5LMrtW3JyHFwizO5bu5u9v1Llxp0FYPriR7yNo0CZNG9KJwsbL06v8tYHyGyxV//sakcV/Tunk4v8z4Xqfs04peFEVGzl7E/NUbCQ4KYOoXH9G1RaNME5k5g+izKm9rYIn4tdhiYFepVDFz6V/069YB/1eTtemTvlqtpt+Qr9i0fRdzfl5Ck2Ytje4zKiEo9d8qpVJnjT6YNohr7QFcVxy8lQZtzcQZcvZge9mLosicSV+wb9tf+PhqBu6MyX7lst/Nkj1ocsNxikR6d27NqVXzeKtlY7vLXps+MfbHGXBkqkdXmufMlRtM/HU53YdPJDFJE4nrS+/IZDJmTR5PpfJl+eqLfly/pntANi3a79zZ4/vo3bkqjx7o/i5YexDXmrhKLl8SfgacZZUqW8seYMm879i4ZiHv9B7K270GmyT7iWNH0qpZE6Oyv/swineGTeDMFc2c7nNGDmDm8E8J8k8/zbE9BmWdReTmkpV+W0P8WmpVLMMvowaz/9QFPhw9HaVSZfC1Pt7e/DZ3Fn6+Pgzq+y7PnhmcFDeV4JACvHj+hHFDepCUaDytqAtr5/LtgT0DTEn4achJsv9n2Vz+/GUybd/8Hx8N+s6o7FctW8LEsSNp2bQxC2ZOMyj7ZZt2UbvnZ+w7eZ6b9zWXuroienuI3lVlryWr7yEr4k97fN5pE860IX3ZuO8on03+CbVabXAQN39IPn6fN4snz54zrP/7JCcZjs5DvKMpWKQkIyct5vrlM/w4cZDFg7jWrNoxJ/3qClG+JHw74YiKHEM/Dl//IBq36sLgUXMI9YkxuJ3Vy5cwYewIjexnGZb94nVb6ffdbGpVLMOJFXNTZ3BMiz1y9a4u+oxYQ/yWkPZY9evWga8/6sG63Ye4fk9zIjck/SoVKzB78nhOnD7LuFHDTKrcqRfenvc/+Zpt65eycc2vFvXZVJwpyrcX0qDtK1wpus9KRU583Et8/TSVF6IoGpf9ij/4bvRXtAhvzMLZ03ROlaD90e87eY4On42iVf2aLJs8wiE3T2U30evD3gO72sFcURS5GxlF0bD0UyQbuht35rwFTJszn8FffkOfTz7T205LZHwA3w7sSu7gEL4c97PuNlYawHWm6RasNXgrTa1ghJwi+4tnj/DtZ135dtpSatRrbjSNkyr7Jo1Y+ON0g7IHTcXPL2s30efNtnhmuAqwx6CsM/H0aqRZ7YPL5LdoP/YUf8YKnoVrNyOTCfTp0hbQL31RFOn/5Ug2bNnOzLmLaN6yrdF93Y/2wt3D0+CSlMakb82Knewi/Bxfh2/rARNnkf3t6xf5ZkAXAnPloXjpikZlv2blUpNlv2zTLsJrVaFAvmD6v9MxXRtXj+rNFXdW92Ou+GNvRVgsfXNr+NPW66vVarYfOcW2wycJ9PflrZaN9dboC4LAjIljuRfxgK+/HMCSFRsoW97wrKsenl4A3L99jdW/z2TQtz8iN/O3WiBEsGhRdF3kL5zH7EXQnZEcHeFbKntTo3tnGaR99OAug3o1BWD2kj1UKRlocBtrVi5l/KjhNG/ckF/n/GBQ9vNWbeCrmb/S7+0OTBvaN/V5ZxS9veRtDSyJ+LMS7YPp4tdKPyExic6Dx3D8wlVWT/+WlvVqAPoj/ajHT2j3zvsIgsCyv7YRnDfzYijp2icEsWvTSiaP/JAu731G/+HTMrWxZ2rH1lG+PSL8HDtoa2vZm4qtZR/78gVffdKe5MREpsz/16js1676k/GjhtOsUQOjkf3clev5auavvNGkLhM/f72OrLPJ/unVSJeSPVjWZ3tV9GiPr7eXJ6unj6J8icK8O2IyR85dAvQP5Ibky8uSebN5ER3N0H7vkWik9DLEO5rm7bvzZs/+/P3nT+zevCpTG2euzTcXe5Rn5kjh20P21qzKyUpFjq9fIA2adWTCT39Rt3IBg9v4a/Uyxn07jKaN6vPrnB/w8swcPWl/zD+tWM+IWYvo1LQ+SyYOT10z1dlKLV1N9BlxlPiNoT3OgX6+rJs1jgIheTl/7Vbq8/qkX7FcGX76fhKnz19g4oj+JlXufDJ0ChWq1WPWhM95GvXAjHeiwZTfjymBV3Yo0cxxKZ2snEUdkcqxVPbJSYlEv3hCvlDN5bWxnP3fa5Yz5uuhNG1Un0VzZhiUfVJyCs0++pIShcJYNG4o7q9uhbdE9qaSndI3b+07weOEJJSiiOrV789NECjk48WqJrX0vs7eA7vGUjxpB3ETEpNSF5DXzs0E+tM7cxYuZvKMOXw2eDifDPjC4H6iEoJ4cO8mH3etSfuuvRkw4odMbayR2skuaR2pSicNrpS3t3h+HKWS74a9y9X/TrFo3RmKBRu+M9Ic2atUKtzc3HjxMg5/H+/UlZHMkb2t0jc3nr/k7rXMP7IAdznF/Jyn5nrMmSskRybQU/b6u/KH+ilXPZNZ36yOwcoUsK/4zZE+wMEz/zF8xkL+mjGa/Hk170+X9EVRZNCIUazdsIkfflxIq7YdDO4nKiGI/84cpnSFGnh46Ja7Nap2soP0pSqdV7hS3j4r8+PM+m4gB3dtoP/waUZl/8+aFYz95kvCGxqX/fQlazh2/gp/Th6RumgGmC57Ww/Kdl+xE7VSTZDs9df6pVpFskxkd6sGZm3LFjw88hiASslyvhdf8h7B+AtuxIgqDolx+Ca7cfpFDDVyBxncjj0repQR9wxKP+NMm96entx+8IhOg8awdf5kcgf666zeEQSBad+N5s79CL4ZPpAChQpRoWIVvfsJ8Y6GavUBzb0karUK/4BcZr0Xa1btuCo5JsJ3lry9LWUP8OvsUaxcNJ2eH3/FyOGDDW7jn7UrGfP1EBrXr8tvc2calP2031Yz/pc/6daqCb+MHmx2ZG/rQVmAP2/d5+D1KL7m9TH7nkdUK56HPqV0L+9nLlppZ4U1CU/ZkxBNbcGP99yC+V31hDjU+AtuBJfw59PSutd51Yc9KnrMifT3nTxHly/GUbl0cf6d8x1+Pt56UztPnz2nXbf3UCqV/PnXNkJCDb+X+zHefPRmdcpVrs3Iyb9lej47RPm2jPBzxKCtK8k+K+z4dzkrF02nw9sfMWLYIINt1/21ijFfD6FRvTos/slwZD918UrG//In3duEs2CMebK356DsW0XCuEYSN8VEAO6KSVxAQY9irwerHx55nKU/1sBPcCM3cjaL0TwQk9kqxtBNlptnohIemF91Yo+BXWPHMO13oUnNKvw+YTinL9/g3RGTSUpO0TuIG5wnN0vmzyY2Lp4h/d4lIUFhcD8eHp40b9+DXZtWcnT/lkzPG6vaceTqWKZiy2qdbC98Z5G9qWQlum/Q7A16DxzHwK9nGcwDr/trFaNHfkGjenX4be5MvL28MrXR/kBnLv2LCQuW06NtU34eNQg3N/NkbyqWiD6j5Lzc3OhdqjCrBM26qquEF3xQvBA+crlVhZ1VGnkEcI0kSuHFSNV9Ggr+RIopnBTjaeIRYHFfLRmoNudzN0f6bzSpy09ff0beXIHIZJrvoj7plytdinnTJ3Ph0hXGD/sEtVr3wjigSe30/Hg4RUtWYNb4zyxaGtHYb8zagZkzVexk65SOvXL2jk7l/HfmMCXKVMHbR7MPQxU56/9ezagRg2lYtza/z5tlUPagmQt9+abdTBncx2TZ2yN9o49ElYp2O4/SS52HxbKnLPYvibfgfHHNfykKJsXe5yVqwnAnXlAzzK8AVd0zf0/C6hm+QUkXthzYNSe9I4oigiAQExdPgK8PgiDoTe/8/NsfjP9+Jv0+G8KAQcMM7mPf8et8/n447br2ZvCoOZmet0dqx1nTOjkypZNTZH/r2gWG923PLz+MBAzLft/uHSbLXjvnebWyJZk2tK9NZG+LmnptlD9T/YjO7rmcUvYAFd19WJqrNFP8CjPIP4wlQaV0yh6wKOK39GYzU46HOZG+IAi8eBlHeO8vmfXn34D+SP+T/71P9y6d+PmnGezfs9PgPspWqkXX9wby4N5NlDrk6GypHXO9Yqu0jnP+GiRM5vCejSQnJdKr/zdGa+23bFpHcJ7cetM4aWnZ9yv6jM5c7+wozJHXW0XCaOYRSEcv516I2k0QqOjhSwV3H+RGSjHtibXnJgry98Vd7saBUxcMthMEgSljvkEmk3Hm9HGj2+09aDzfL9hk9hw7ORlJ+C7OnZuXCC1QlFx5Qoy2vXv9EhXKlsHH29tgO1EUuXLnPrkD/a3VTbvi5ebGl34F8BZkKEQVN5WJxKiVju5WjkUQBORubri7G68Cd3OToVar8fAwHJAAuLt7GL1nwVWx1cLmkvCzgKPTOaCZBbNYyQpGX69Sqbhx6zalS+gv+dNeaj94/JQ4RQJlimZtMi5D2Ho6Y1EUWaZ4zAcvrjMtNoLe0TeYFfeQZFH/gKCE7UhRKlPvyDZEUrJmzVxPHVVjGbl78zJvNirA0X2bs9w/W5OVRc6tSbYUvrPcYGVrkhITuH/7KiXKVjba9kHEPRKTkihdsoTRtlfvaGRcpmj6wTVLJkVzFNuSojmUFMtPbkX4ya0oi92KEZ2SwiJFlKO7liNJUarwMCHCT3q1SLqHjkn7MpKQEE9szPMs9y0nkaPutM1ueHp5s3LnTZMua2/euAZAGZOEr4n0yxbTv4qRLrKyaLY1eXjkMRsTn9NHyEuwoDn5+wpufCoLoX/SHT7yCcU9m6YCnJX332hBoZC8Rtslv4rwPUyI8JMTE1+1zZyiNGVunZyIJHwXJ3dwqPFGwM3rVwEoZSClo6ViyaIM6N6J4FyGp1J2Zl6IKvLL0l/p5cYNEUgU1bgLbo7pWA5l6AdvmdROm9LRN19OWhITNTdpeRopQJB4jSR8F2bruj9IiI/lzXcHGK3QuXH9KvlDQwjwNz4Q27hGZRrXMJ4msheWlBeWl3tzWBVHZ+H1fCtnRQV5ZHL8nLRUMzvzMl6Bp7t7pqUvM/Ja+MZTOslJmvn0dUX4ErrJdt98Z8rf23rAdss/v7Nv+98m9eXu9UsmpXMAbtx7mFqHbwvssf7su955WSM+Z7n6GZfFBDaqo/lB/Yg+PiHZtrLDmanQuQ/fzsk8901GtDl8UwZtg/OF0aJDT3LlNp4qktCQ7YRvL6w5nYIlqNVqbl29YNKArTkVOk9exFCtWz9+XrPRan11BEXlXswIKEq0XMWvPOGyWyJjAgpR18M1S01dnRSlKnUOJkNoc/juJqR0ylaqxYhJiwgOMbywj8RrrJLSEQShDTAbcAN+FUVxSobnhVfPtwMUwP9EUTxtjX3nVB49uEOCIo4Spa1doaMRf8aSTFeq0NFSwM2Tz/1cq/Iqu2JyWWaS5mrWlAg/K9hzmuSkhOdE3tkAOnYZkKcivgHF7daXLEf4giC4AXOBtkB5oIcgCOUzNGsLlHr1py8wP6v7zencuHIOwKQI35wKnWt6SjKN4UwVOhLOR4pShbsJEX7SqxuOTMnhr/ptBm1rBpFoZIZNRxMXfZfrZ6YS8/QsL5/9l/rn9qWfef7oiF37Yo0IvzZwQxTFWwCCIKwEOgGX0rTpBPwhamZqOyoIQpAgCPlFUXSKdehcrf4e4PnTR3h5+VC0RMZza2bMqdC5euc+vt5eFAwJznIfrYEzL1UoYRoqlQpRFM2K8E0py0xKTCAlOQkPT+eu0smTvxqBwdUJyFOVkEJtAVDE3eNZ1EFCi3aya1+sIfwCQNrZkCKAOia0KQBk+jULgtAXzVUAnt7GpwtIiz1WfXcWOvf4lDfe/hg3udyqFTpX70RQukjB1DVJrY09BmyzG0eSXnIg+SVeJ5+ke1wQBPqWKUoxPx8H9cx0vvm4Jw2rVzTaLjlZG+GbIPykBNw9PG32XbUmRcr15tqpyeQr0BJBJufelV9RKRM4vft9CpX+gPzFOtulmMAawtfVy4zZKlPaaB4UxQXAAtBMj5y1rtkGZ5hSAcDNhIgJzKvQGdizM4mvKiUknIM41JxJiefDJz5pHlOxSP2YAeUMX7U9vRpp8VTJ1sLNzY0Rfbqb1DbZjKkVkhIVeLpISWZQcDU8fUJ4HLED/9wViHl2htot/yFR8YBrZyYiCG7kL9bR5v2wxqkxAkib8C0IZJw4wpQ2EiYSE/2MIR+25MyxvUbbmlOhA9C8TjXaN854gSbhSJp5BOIlyAhGTjNZAM1kAcSipkNYCAV9nF94KpWKiKgnxCckGm2bqE3pmJDDT0pMtNlNV6Ysc2guRcr15u7VX7l3dTFhxbshd/fFL7A0JasMJ+L6cqvvTxfWEP4JoJQgCMUEQfAAugMbMrTZAHwgaKgLxEj5e8u5dfUC508dRK02XitvToVO1LMX7Dt5DkVi+qsKV6zQyU64CQI9ffKyXNQsuBEnqthMNJ+UKWrwdQ8ViUw4d5Wmv26k24qdbL3hmHTa0+iXlOvUhxWbdxtta05Kp2qtxrTt8mGW+2cvgoKroVIqiH56ivxFu6Q+7h9YhoT4COyxGFWWhS+KohL4DNgGXAZWi6J4URCEfoIg9HvVbDNwC7gBLAT6Z3W/GclJ+fubVzUVOsVLVzLe1owKnZ1HT9Phs1FERD0x2tYemDtgm50rdJp5BPIMJefVCtaL0YSHBhuM7h8lJPLewVOIDxP5ND4X9aPkjNpynIUnr9ix1xpSlJqpqeUmzZapjfCNC795++78b8DorHXOznh65yOsaBfkaRa7iX56Gt/Akqk5fFtNjQxWqsMXRXEzGqmnfeznNP8WgQHW2JcE3Lhynjz58pMrj/Gl78yr0InAXS6neAHzcr6mlmTaY8A2UVSzJymGy0oFuWVyWnvmIr+b8fSAs6ON8pckPuORmMyKMjpXsEtlyY37NFb70UumuQu1pOBFSbUnww5f4N0qJfExYeZKa5Hy6q5tU8oyX0f4pqR0EpDL3U0ey7Imli5vWKxCP26cm4FvYEkCclfi5fML3PrvR0pUHmyDXmbG+Ye3sylZGbC9efV86g1X1q3QuU/JwmEm3RHpjMSrVQyNuc3BhBhKKz1JTFIzKOY2p1PiHN01q9DMI5B4udpodA9w7nkMdfBL91iY4EEemTs3X5i/8HdW0Eb4ppRlanP47iYIf2T/Tgz7uG3WOmdngsMaU6racB7eWs3pPe/z8NYqSlX9krwFwu2y/xw9eZol+XtrVehYiiiKhBUqRoWq9Uxqb06FztXb96lU2viVgLPyT+IzCokeDJWFpl4eV1f7MCcukkVBJZG5+Bw6boLA/HpVyGVkAjKAEC9PIhTJVBBenxgSRTVPVSnks/NAr9LMCN/Tw7SVrJITE/ALyGW0nbORJ7Q+eULrO2Tf2SLCz0n5e0EQGDdrNd3+94XRtuZU6CQmJXP7YZTZd9g6E8eT42gjC0wni2qCD2rggTp7lJoW8fUhwITve/cSBVkpPOemqKmMUYhqfhGe0KhwCCF+9hV+3txBTPq8NxVLGQ8mkpKT8PQ0LQWXmJiAl5HlOiXSk6MjfFdEFEWTb9Awp0JH7ubG7l+nkSfINpOL2SN/7yXIiBfVqXd9xIgqdqtjiBaV7E2KwU/2OsIsL/ehjNwyWahEkS1JL0jSUVUR4uZOQ48Ai7ZrTeoE52JQheJ8d+kWHoKMl6KS5kXCmNbW/iW3eXMFMrBnZ5PaJicnm5S/B830yNLUyOYhCd/FmD9tOOdO7Ofn1UeNit+cCh253I0a5UtlftxBJZmWVOg08wxkVcJzKok++AgyXogpLBKf0kIIIDZJSSyaXPJBMZY3vHJbLHwRWJbwhNJ4kZ/XcrooKgiSy51C+AAdC+WnbYEQ4kP8ye3tSW5vx6wCFZ+QyP1HTyicPx8+Xob7kJSUjKeJwnelG6+chRwrfFesvwfNpGkenl4IgmB0wNacCp29J85xP+oJ73doYVZ/nGXSNIBWnkHcVCbyUfJtqsi8iRBTCMKNYoIHnWS5AXgoJrNXpRG+pcgFgbe98vBfooKP3DRVMCmiyCeq2/TzduxdrRlxl8komduxJ6AT/13ljYGj2Dx3Io1qGC4lTkhMMln4nXv2p3CxMtboYo4hW+TwsyP6KnTc3T14HHmPBIXxOwF9/TTpmYNHjhtte/nWPfpP+JEVW/aY11EnQiYIDPDLz6zAYtTzDqSfXyjf+RfmL/ULkkQ1AKvVz+nolStdescS2nnl5jIJ3BY1x2mXGEMBuScV3J1/Xhtj+BcvaLyRicTGKxgzbwnenh4UK2h4Oc6TZ86xffdeypQqadK2e/QZRoNmtp+OIDuRY4WftjbWGdG3CPMHn37DsyePWPvHbKISggxu46133qNiubJ8NW4iz54/19kmNlAzSPtx13Y0rFaRwVPncenm6zSOMrSIwX3ICxY2+LwWcyVi7vwvYfVe35MQ5uZBM89Aqrj7UsrdmzLu3mwVY3goJnNcjKezV9YXr/ESZLztlYcV6mekiCKr1c95z1v3ykvJopqNic8Z9fIu3728z8HklxbdVZn2PZpCcJn8Zn+OphwnY8dc+51JSk6h51eTOXftFn9M+oqCrxYx137n0nL/wUN6DxxC/tAQRk780eD2L9yOZe6UoSQodJfbGlvA3Nhc+NacVsFcz9jypivIwcJ3NKYswKDri1uhaj0at3yTVb/NIO5ltMHXu7u7M27aPF7GxjJy/GSDkpHL3fh9wjD8fX147+spxMa/nmPcmtI3R/zmCiusXj6dUnzXOy9/qZ+zVP3MKtG9Fm2Uv0j9RG90rxJFxsTe52DCS1qqA6it9mVp/GMWKqJM3o++92UIS0RvTdkDDJo6j70nzzH364G0aVAL0C372Lg4en06iOTkZGYvWEau3PpPyJHxAUwb9Qlb/lnCi2eZ76w2JntrYM5NV85GthC+pWdFS6J8Uw62LSZeSsvHX0xiys8b8AsIMhrlly5Tjv6fD2Pjtp2s37xNZxvtjzAkTy5+/24YN+9HMnXxKrP6ZKr0wb7RPkBJuSbKP22l6F6LNsrfKEbrje6PpMQSr1IxTlaA+jJ/mssCmCorxM6kGB6qDJeKWip6W0T1YJ7sAfq82YbpQ/vybvvmgG7Zq1Qq+g8dyfVbt5k+ZxHFS2QuHEjLuuXzOHNsD/2HTyOskPkrRTlzdG8PBHtM2GMpfkFlxKqNFpjU1p6Ll1vz5qusTpGsUqlwc3MzOICrVCrp/U5b7ty9z55/15Ivr+7FTbQ1+VsOnqBxjUr4eqefidBYxY4lA7jmlmtaOr9OpCqZe6ok6lh5TdskUc3+5Je09AzS+fzc+EhCUuR0lqW/QegHVSRVffxo7an7xiFzRQ+WRfWmYo7sz1y5QbWy6fPwumQPMGbydBb+sYxR46bQrWcvg/s4/l8k/d6pR816LRj/45pMVWpZTeWAceFbOqWCKVgrnXNoY/gpURR1zr2RLSJ8Z8TWUT7AotmjGTOom9F2crmcsVPnokhIYPjYCXpTO9ofZduGtfD19iI+IZGrd14L2VqpnbTYK9rP7+ZhddkDeAoyvbIHCBDceCJm/iE/QUmAkLlIztmiejBP9ov+3kLj/w3h331HUx/TJ/s/Vq5h4R/LeK/XR0ZlH5UQxOyJg/Dx9WfI2Llmy94Usnt0D9lI+M6W1jEVS3P5AIG58nB0/2ZOHt5pNLVTvGRpBg4Zyfbd+1i7fqNJfev17fd0HjSaZzGv515xVelbEjFbgxaeQewRY7kiJgCaG+f2qF/yiBRqumeY68ZOoreV7NftPsQX036mdYOatGmgCTD1yX7/4aN8M2EqzRs35MuRYw3uQ/vdHjZ+AaOmLyNXHvNWwgPrLFpuy9y9rQdrtWSblA44X1oHbLvyVXJyEn06V8PD04ufVx/F3d3DYGpHpVLxcc8OXL1+g90b1hIWqvuHo03tnL58nZZ9v6JJjcqsnTE6dSk5U27GsrQ+39YpHkewJ+op485eJQ9uJIhq5O4ypteqSOkAP+Mv1oMtI3ot5sh+38lzdPliHNXKlmTDnO/w8fLUK/vrt27zRvcPCAsN5bdVm/EzMLFfVEIQEXeuU6BISb03GtojlQOmC9+SINKawpdSOkZw1Sjfw8OTAV9N5+7Ny/y11HApG2iWmhv7/TySU5QMGz3eaGqnerlSfP/Fx+w4eprpv69Jfd5YlA+WRfpg+2jfETQNCWZHy3p8W6sc0+tVYn2zOhbL3tbpG1NJ+x14Gv2Snl9NpkShMNb8MNqg7J+/iKbXp4Nwd3dn1oLlBmUPcO/2Vfr3aMDvc8frfN5eqZzsEN2DJHyXp26TdjRo9garf59FgiLeaGqncJFiDB72LXsOHGb52n/0ttP+YHu/2YZ3WjdhwsLl7Dl+NvV5W0vfluWbjsBdJqNa7kDKB/pbtFi1PdI3WuQFC5t17IKDApg9oj/rZo0jl4ETWXJyCh99PpTIR1HMmr+EAgUNT9R364mMMYO64eHhRfu3epvcn7RYI5VjDs6au9eSrYSflTOlrQ6UqQNBWcnlDxw5k5+WH8Dbx7TKoB7vfUiDOrUYN3UGEQ8Mv29BEJg9YgDdWjXOdKekqdKXov2sYcn7sqXotcf90dPnHD57CYC3WjYmLJ8mzakruhdFka/GTeToydOMmzyTqtVrGdxHZHwAU7/pw4P7Nxn1wzLyhWbeppTKMZ9sJXx744gbMHR9yYNDChBWsBiiKPLiWZTRKF8mkzFqylxEUWTIt+NQq9U622l/uL7eXvw6bihFw0IRRTF1fnMwTfpg3xRPdhG/vaN6U9Ae7+jYON4cPJaeIyYRp0hIfV5fKmfeoiWs+ns9/T4bQvuOXXS20RKVEMTyhVM5vGcjn345lSo1G2VqYw3Z50Qk4dsBa0b5hvjlhxH079GQBEWcUekXKFiIoSPHcvDocZasWKO3XdofsFKposdXkxg9b4lF/bM02rdEYq4ufWdM32hln5CYxDvDJnD1TgSLxw3F79WCKvpkv2XnHibN+JE32rTi04FDDe5D+72tWK0+Xd77jM49My9/ba27aR0d3TuCbCd8Z0zrWBt9X/iGzTvx5FEES3+eZNJ23nrnPZo0qMeEH2Zx5959ve20P2S53I0C+fIwZ/k61u85nPq8qVG+FinFox97Dsqacxy0x1ipVPHh6OkcOXeZhWO+oFmdaoB+2V+4dIXPhn9N1UoVGD3t59RKL30kJ2kWbKlauwn9h0+zaLzDWqkcW2PvdA5kQ+HbG1MjAHtE+RWr1adtlw9Zu/RHbl37z2iULwgC306eg9xNzhdfj9Gb2knLpM/7UKN8KfpP+JGb91+fIO0p/ew2oAvOPSib9tj+uWkXm/YfY9qQj+naUpNq0Sf7R48f87/+g8gVFMSMn5fh5WV47vpbT2T0796AtQYqzuyZyslu0T1kszp8LVld8tDcunxr1uSDaXX5oLs2Pyb6Gb07VaVgkZLM/H0X+X2NL1i97q9VjBoxmLFfDaXv/97T205bn38v8jENew2mYEhedi38Hu80i1pYsmCKvWr2sxO2zNOnJeOJXK1Ws/PoGVrVrwHol70iIYEu7/fhxu07/LHyX8qUq2BwP5HxAYwb0p0j+zbz/YLNVK3VOHMbE1I51ozuXXWwNsfV4TviUskU7BHlBwbloe+QSTy4d4tHD+4YjfIBOnXpRovwxkyZ9RM3bt/R20774y6cPx8Lxw7h8fNobj94ZHFftdgr2s8O2HpQNi3pIvuNu7gX+RiZTGZU9mq1msEjR3Ph0hWmzvjZqOy1g7SHdv9Lv6FTdMreFKTo3jjZUvhZxdyD6KjpUvVFPK06vsfv/55PnU3QlNTO1xN/xMvLk8EjRqNSqQy2B2hdvybn1v5C+RLpI0BzUzta7Fm+6YrYK32jJe1xXLllD59OmM3MpX+lPqZP9gDTfpzPxm07GfrVaMKbtzK4n6iEII7u28ySed/RokNP3nx3gM521krl5NTcvRZJ+E5KVqIVQRDw8w9EpVRy/OB2k16TN18II0ZP5vT5C8xf/Ifedml/6L7eXqhUKib/uoIL12+nPm6p9MF+A7quhD0GZdOS9vhtP3yKTyf8SOMalZg8qA9gWPZr129k9i+/0rVbTz7o/YlJ+3v25BFlKtbki9E/6RyktWdVDmTf6B4k4dsda0cYhn4M/yyfy9f9O3Hh1EGTUjvt3niTdi2bMX3OfK5cu6G3Xdof/IvYeBav28b7I6cQE/f6vSlDi2Qp2reE7JbicURUn/aYHbtwhfdGTqZCiSKs+P4bvDwNrzV77NQZvhw1ngZ1avHNmMlGK2y038n2b/Vm9h978NQxqGutvL2z4Oh0c7YVflY/WGdI65j6Rdb3o+jw9seEhBVm9oRBpKQkm5TaGf7dbPz8fBk0cjQpBj5DrfSDgwL4fcIw7kRGMWDinEzz80gpHstwpOi1TFywjPx5c/P3zDEE+GpW89IX3d+9H0GfgUMoGJafqT8twd3IQuSPFIF8/+3HHNy1HtDM85QRa8peiu41ZFvhOzP2yiN6efswcORM7ty8ZNLkagB58gTz7fhpXLh0mZ8W/mbSaxpUrcC4/r1Yv+cw81ZtyPS8o1I8rih+ew7KguFj8+eUkfw7ZwL58mgWadEn+5exsfT6dBBqtZrZC5cTGKR7URctmkHa79m+4U8ePTS/qstVcXR0D5LwDZIdonzt5GpLf57Eowd3TUrttGzTgU7tWjNz/kL+u3xVb7u0Avi8Z2c6NKnL+J//5MmLmExts5riye7RvqPTN1oOnb3IoKnzUCQmEeDrQ+H8mjn69cleqVTSb8gIbt29x/Q5iyharITB/UYlBHHswFZ+nzuO5u270/W9gTrbSdG9bci85E42QpWSkuWafFvx8EG8yXX5WaX/V9OZMOx9FPHGa/K1fDl2BkeON2HwyFGsX/Y7vr6ZF+gGjQj8Y+4jCALzv/2cm/cjyZsrEFEUdeZwlaFFLKrVB43cLKnZ14rUnnX79jjRWDOqv3HvITOWrmXpvzspFJqX3p1bU6WMRt76ZK9SqRg18Xv2HjzM2InTqVOvodF937lxiUkj/kfxMpX5YnTmlasg++XtnYlsLfzswoMo0aSbsSKjPXXejBWSvzBz/twHgCI+lhO3o6hV3vCqQUG5cjN64gwG9utF887dmDlxLPVq67yXI1X6Qf5+1CivWYS60+ejyR0YQLfWTWhRtxoeaU68jpA+uE60bwxriv5ZzEu6fTmB4xeu4OYmY9C7bzLyox6p6xnrk/3FK1cZNvo7zl64yP/6fErXbu8a3X9UQhDHD/6GXO7OuJmr8PLOHERYqyJHi7NE986QzoFseqdtRpz1zlsw/e5byNoduFrmTxvOhlUL+PCzMfTv+4HOwbK0nDpxlLEjPufOvfv0eb8HIwcPxMdH9y3y2jtxVSoVw2cuZO2OAzyPiSV3oD9dWzTiw86tqVSqWKbXWSp/sPwuXVfEGqJPTklh+5FTPH4eTe/ObRBFkR5fTaJu5XJ0a9UkdYpj0C17RUICM+ct4OfflpIrKJDh306kTftOBity9hy9SlxsDLUbtkKlVBIT/ZTcwaGZ2pkqe1dL5dhb9obutM0RwoecJ33QLf7nTx8xe8LnHNr9L+Wr1GHY+AXULG94LVWFQsHs6ZNYvnQRRQsXYubEsdSpWV1nW630QSOXXcfOsmrrXjYdOMakgb35+K12vIxXEPnkOWWKZo64JfmnxxqSF0WR05dvsGLzbtbs2M/zmFhKFSnAqZXzMonaUI39vkNHGTFuInfvR9Dl7Z4MGf6twQHauNhYpkybwYaVv1CqfDXmLj+YpTp7c9I4OVX2IAk/FUn6GkRRZPfmVfw0eQhJSQmMmLiItzs1M7qtE8cOM3bE59x/8JCP3u/JV4MH4ONtONrX8jJegUwQ8PPxZvG6rQyaMo9qZUvyTpsmvNWyMSF50osjJ4vf2hU3ExcuZ8qilXh6uNOhcV26tw2neZ1quMs1GV1Dkgd49uIF46b8wNoNmyhetAjfTphBrTr1Db7m780HmDNxMM+eRNK5x6d8OHAsPr6ZlzN0VFQP2VP2IAk/HTlR+qBb/M+eRDJ/2nD6fP4d+QsWRRRFQn0yV9ikRREfz8zpE1n5528UL1KYmZPHUataVYOvySj/qGcvWLN9P6u27uXs1ZvIZDKa1qrCSj039+QE+VtL8rHxCtbvOcKKLbsZ9cl71K1cjos37nDi4jU6N6tPkP/rJQiNiV4URdau38jYqTOIi4+jd9+BfPzp53h6ehl83c5DF/nify0oXroSQ8bMpWylzKtbOTKqh+wre5CEnwlJ+pkRRZEJw9+nQpW6fNKnp9F5y48fPcTYEZ8T8TCSvr3eZfigAXh7GRYBZJb/ldv3Wb1tHzfuP+CPiV8BMG/VBkoUCqNZ7aqpUagWa8rfUsk6A2lFr1Kp2HvyPCs272bD3iMkJCVTomB+pnzxEW0apJetMclruXPvPl+NnciBI8eoWa0K30ycTclSZfS2V6vVHDl3n5JlqyCKIvu2/0XDZp2QZ/itmTMo68gUDmSt/DJbCl8QhNzAKqAocAfoJoriCx3t7gCxgApQ6utMRpxV+GC+9CGz+A9vHE9cdGSmdvnCCtDu/ckmbdNc6YNu8ScmKPhu2Hsc27+FStUb8OX4X6hexvCJKj4ujhnff8fqFX9QvGgRZk0aR81qVUzuR0b5A6QolVR48yMinzwnT1AAb7VoxDttwqlZoXSm/G9W5O+KZIzmtQPiSckplOrwP0RRTdcWjejRrhm1K5ZJ/bxMlTzAg8hHrPx7PXMX/oa7uzuDvvyWt3u8bzAAuHH9Kt9+PYIbV87x+4bz5A3VXQ1li6geJNlnxJbC/x54LoriFEEQRgC5RFH8Ske7O0BNURSfmrN9WwkfnEP6a2a3JSlBRq58dVIfi35yArl7Au8M2Wn3aF8URbZv+JN53w9DmZLMR4O+o2/vHkaj/SOH9jNu5CAiox7zyf/eZ9jnn+LlaV55XcbB3h1HTrN62z42HzxOYlIy3332Pwa/10VvfX92lX9GyWvTYSu27CY2PoFza39BEATOX7tFmaKF8PTQfK/NkXzU4yds3L6T9Zu3cfLMOQDatWzGkNHTCQnVvyhLUlIis+YsYOWi6fj4BdDvy6m0fKNnpuNjK9GDJHtd2FL4V4FwURQjBUHID+wVRTHTdZ8zCh8cL/2I6wfY+kc/qjb6DUEmRxRVnDvQm1bvzaFQ6XDAtike0C3+J48imDFuADevnmfRutP4B+QixDva4HbiYmP5Yep41q76k5LFizF1zNfUrVXDoiXq0sr/ZbyCDXuO0KBaBYoVCGXzgeNM+2013Vo3oWuLhqm3/WvJDuLXNfh6+OwlfvhjLbuOnUalUlO9XEl6tG1Gny5t0qW9TBX9sxcv2Lx9F+s3b+PIiVOIokj5MqVo0b4rrdt1pHCRzOWzabn3wpNPu9fn3q0rtOjQk35fTiEod95M7STZ2x9bCj9aFMWgNP9/IYpipjotQRBuAy8AEfhFFEW9FhcEoS/QF8DTO6RGzearLO6fKTha+mtmtcEvsDH5CrXhccQOXj7fQbcvdqQTpbl35For2n/86D4h+QujTElh3/a/6N6lldFo//CBvYz75gseRj4iLH8orZs1oU3zptStWR13Cz7rjGmfrYdO8N0vf3L+2m3c3GQ0q1WVd9qE07VFI+Ty9PcUuJr804perVZz+OwlihfMT1i+PGzYe4ThMxbSvW043ds0pWyx12I3VfIxL2PZsnM3G7Zs58CRY6hUKkoUK0qrDl1p064jxUuWNrqNpKREotWaGvrlC7+nTMUa1KjXPFM7ZxA95DzZQxaFLwjCTiDzXRLwDbDEROGHiaL4UBCEfMAOYKAoivuNddzWEb4WU6SvUipQpsTh4ZUHQch8s5Kl0tdG+VUaLuL8oY/TRfdpsbX0Qf+g7s6NK5jydW+q1mrC0HE/U7VUkMHtxMfFsXP7Zg5uX8feQ0dITEwkMMCfFk0a06ZFOOEN6uudqkEfGcV/+dY9Vm3by+ptmjuI//t7ITKZjKt3IihRML/d5J+VieF0cf3eA1Zu2cPKLXu59+gx33zckxF9uqNSqRAEIfWEa6rk4+Lj2b57H+u3bGPvwcOkpCgpXLAArTp0oU27TpQuW96kqzBRFFm1bic/TxvO6B+WU6FqPb1tnUH25s5rlV1kD06Q0snwmrFAnCiK041t317CB/3SV6mSuHl+Fk8idiFz80Qm86BohX6EFGqZqa2l0l8zqw2JCjUeXupM0X1aHCV9URTZ8vfv/Dz9K9RqNX2HTKLPB28ZjfYBEhIUHD64j907tnJg9zZexMTg6eFBo/p1adM8nFZNmxCcJ7dZfUwrf7VazYPHzygUmpcUpZLSb3yIALzVsjHdWjehRvlSFqWV7I1arabdgG84dOZiaplqj7ZN6dCkrtFpDjKiSEhg176DbNiyjV37DpKYlET+0BBatutM2/adqVCpilmfycMH9xk1ajTHD2yldIXqfDnuF4qXrpipnS1Fr+mHJHtTsKXwpwHP0gza5hZFcXiGNr6ATBTF2Ff/3gGMF0Vxq7Ht21P4oFv6V09NRJmioESlL3D3CCT2xUWunBpL2ZqjCcqb/m5TS4QPoEq6xN9z2/Nm/w06o/u0WDLhmrXEHxV5jx/GfMrpo7vp8PbHDB71o9HcflqUSiVnTh1n986t7N2+kYiHkQiCQK3qVWnTPJw2zZtStLDpg42QXv5KpYoth06wettethw8QVJyCiUKhTHp8960a1TbpO2JoohKpSZZqSQ5JYWUFCW5A/1xc3PjyYsYop4+f/WckpQUJclKJU1qVEYud+PslZtcvHlH89yrNskpSoZ80BWAdbsPcfT85dTHk5Up5AkMSF1J6ps5v5E3V6BJUxxkJCk5mT0HDrFhy3a279mHQpFA3uA8tGjbiTbtOlK1ei2TTtAZWfDbKhbO+BoEgd4Dx9Kpx6eZpuNwFtGD/VI44JyyB9sKPw+wGigM3APeFkXxuSAIYcCvoii2EwShOPDPq5fIgeWiKE40Zfv2Fj6kl35KUjTHd7xDzWarkLu/Fu2jexuJfnKCCnUnZXq9pdIXldfIX6yuSZGXI6UviiKb/lpMybJVKFuxJsnJSRQMUJgdRYuiyLUrl9i9cyv7dvzLxVfTMJctVVIj/xZNqVS+nFnbTSv/6Ng4Nuw9wqqtexnZpwcNq1fk331HmbpoZSZhb5k3iZKFw5i3agMjZi3KtIjL1Q2/EZYvD1MWrWTiwuWZ9huxcwWBfr58O+c3Zi/7J9Pz0Yf+wc3NjeEzFrJs0y7c3eV4uMvxkMvx9vJk58LvCfRLf0xNkXxKSgoHjx5n/eZtbN21h5exceQKCqJ56w60ad+JmrXrGZ0rSR/aabRX/z6TcycP8Pk3swjJn/m+BUn2zod045WZaKUfH3OTS8dHUT18SbrnY19c4tZ/s6nebLHO11sqfVvdoJUWa+b2AWZ9N5DIiNsMGTuPysUDLOoTwIOI++zZuZUDOzZw9ORp1Go1YflDUyP/OjWqmTXoq6vGf/exM/y8ZpNGtu5y3OWav0f26UFYvjwcPX+ZnUdP4yGXp0rZ092dd9qE4+/rw5Xb97h6JwJPD3c85HI83N1xd5dTvVxJ3OVynka/JE6hwEPunk7qXp4eJp24TJG8SqXiyIlTrN+8jc07dvMiOpoAfz+atmxPm/YdqVOvkUWD46A5CR84dYsd/y6jYrX6hLd+C7VajSAIdi21BPMXCZJk/xpJ+Bbg5u6OSpnIsW1dqNJgPl6+ryV+7+pvpKTEULraML2vzynS/3f1Qn75YSQymYxPh31Pr56GZ040hRfPn7F/7650g75BgQE0b9zIokFfXfK3J+bUxOtCrVZz4sw51m/exqbtO3ny9Bk+Pt6EN29Dm/adaNAwHA8z73tIy67Dl9i/4x8O7lrP48j7yNzceOd/Q+gzaLzO9s4wKJvaFzvm68H5ZQ+S8C3Gzd2d+9dX8OjOBoqU/QRvv4I8izxA5J2/qdp4Pt5+hudXt4f0wfHij4y4w/Qxn3DuxH5qNWjF0LHzqFDUOou72GLQ1xUQRZGzFy6yYcs2NmzdQeSjKLw8PWkU3oI27TvTKLwZ3jrmkzeFBy99uXntPGUq1ABgSO9WXD5/nJr1WtCwRSfqhbcnIDDzZ+rKUT3kDNmDJPws4ebuzuOInTy89TfJic8IyF2JwmU+wMfftHlYLJU+uFa0r1ar2bDqF/78ZQpz/txH/oJFSU5KxMPTy6yBXUPYYtDXmRBFkYtXrmkkv2U79yIe4O4uJ7xhfZp3eIfwZq3w9fMzviEd3Hvhyakjuzi4az1H9m4iPv4la/bcJTAoDxF3b5A7OETnbJZabLkKlSVrPEuy148k/Cxi78nW0uJq0X5SYgKeXpopk/t3b4AoitRp3IbaDVtTpmJNwvxiLepfRmwx6Osort24yfot21m/eRu37tzFzc2NRvXq0LxDN5q1bENAQKBF29UOvB7es5FJI/5HYkI8fv5B1AtvT8MWnajVoBUeHoZF7mxRPUiyN4YkfCsgSV83+nL7oiiycvF0ju7fwuVzx1Cr1QTmCqbnR8Pp+v7rhautFf1be9BXFEWUSiVJyckkJSWRmKT5W/P/NI8lJ6X+PykpmcS0/9f52lfPJyeTmJTE8+cvuHX3HoIgUL92TZq1f4uWrduTK7d5x1zLjSiRI3s2cWDXelq+0ZPGLd/kYcRtVi3+gUYtOlOlVmPc3TNPQZ0RZxQ9SLI3BUn4VsKVpA/OIX6AlzHPOXloB8cObKV2w1Y0b9+Dp1EPmDD8A+o0bkOdRm0oVqqi0bn4TUXXoG9ggD/1atVEJhPSCTejnNNKW61WZ6kfcrkcTw8PPD098PT0xMtD87f2/zJPf7x9vKlTrxGt2nQgbz7D6wzr42GcP1v+/o0Du9Zz9vheVEoleUML8uGAMbTq9J7J2zF3PVl7DMpqMVf0kDNlD5LwrYokff0Ykn5Grl48xcxxn3HjylkA8oYUoHajNvT8aBghYZrpCqwR/SckKDhyaD+7d2zlwumjuMvlr4Xr5Y+nhyeenp54eHri6en16m9PPF49nu4xT0+8PL0yPebp6YWnh2eax7zw8PBAnmEuf2ty/tZL7t68Qu2GrRBFkd6dqqIW1TRq0ZmGzTtRpoJpE9dZsmi4M6dvtORU2YMkfKuTU6QPthf/08cPOXFwO8cObOH00T0sXn+W4HxhHDuwlQd3b1CncVsKFC5htdSPK3Pq8hMO7FrHgZ3ruHbxNL7+gfy19z5yd3diXjwlICiPzSSvxdmjesjZsgdJ+DYhK9LPivDBMumDfaN9ME/8AMqUlNQVkmZ99zkb1ywEoGCRUtRu1Jq6jdtRvW5TwHq5f2dGFEUeKQKRyWSsWDSNRbNHA1CmYo1XkXxnChYpadK2siJ5yBlRPbi+7EESvk1wZJQP9pc+WC5+MF/+AA/v3+LYga0cP7CNsyf2UaxUBeatOATA1nV/kC+0EOWr1KFI7mSL++VsJCUlsn3/eY7u28zR/ZsZ/t1CqtZuws2r5zl3Yj8NmnfUOcWBLrIqeXD+QVktkuxfIwnfhrhaekeLvaP9jJh7AkhMUPDkUQSFipVGpVTyZqMwFPGxuLt7ULZSLarUakyDpm9Qqnw1l4v+oxKCiH7+hJnjB3DqyG4SE+Lx8vKhRv3mdO/9JeUqmzbxG1hH8uA6ogdJ9hkxJHzbjSrlEFQpKVmSftTthxZLX/sjsUT82h+mueLXiiCr4s8oJmMnAC9vHwoV0yzQ4SaXs2LHDS6eOcK5k/s5e2IfyxdORS53p1T5atx6ImPNkllUqdmY8lXqUDiX+VcXutDWtYuiSFJiAkmJCtRqFbnyaKprrl48RcyLZyQmxKc+H5grmEYtOgPwx/yJPIl6QFKiIrVNrQYteeuDQfgH5OLRg7u0fONd6jVpR9XaTfDwNL4ovLUED5ZLHiTRuwpShG9FHJnXB9eO+DNi7hVAfNxLVColAYG5uXDqIEP7tEatVuPu7kG5yrWpUrMxZSrWQKVSkpigSBWyIJPRucenAKxbMZ8rF06+ErIiVdhjZqwAYNTAtzhzbA+JiYrU/ZYqV435qw4DmhvNrl06na5flao3YObvOwEY0LMhT6Me4OXti6eXD17ePhQvXYnBo+aY9V6dRfLgGNGDJHtDSBG+nchKtK/9AmdF/K4c8WfE3CsAX7/XM3VWqtGQvw885OLZI5w7sZ9zJ/ezbOEUipWqyM2r59O9zj8wd6rwb145z3+nD2mE7O2Nl7cv3j6vpzKo2aAFBYuWwsvLJ/X53MGvF4MbPHoOypTkVKF7enmne/3c5QfN/yBe4UySB0n0rooU4dsARw/oaslOEX9GzL0CiIuNIfr5ExIUcanC9vTy0fz71VQQzoSzCV6Lo0QPkuxNRYrw7Yz2S+XIaB8cG/GDbeVv7hWAn38gfv6WzUljL5xV8uDaET3kDNGbgiR8G+LIAd20OEL8oFs6tjoJmHsCsCfWFLk+rC14La4uepBknxZJ+DbGWaJ9cJz405JRTM5yArCHlK2NrSQPkuizK5Lw7YSzRPvgHOLXYq+rAFcUui6cLZLX4kyiB0n2+pCEb0ecKdoH5xJ/Wux1FeAKOKvgtUiidy0k4TsAZ4r2wXnFr8WeYwHOgC0kby3Ba3H0DVMZkURvGpLwHYQ1on1rSh+cX/xpyW5XAa4geXA+0YMke3OQhO9gHH2zli5cSfxaHHUVYMuBU3OxheC1SKLPHkjCdwKskeIB5xR/Rux5InAmGdsCWwpeizOKHiTZW4okfCchqykeSP8Dc5Ycf0Z0ScreVwOuij0ED84reZBEn1Uk4TsZWY32tdgi6rem+NPiDFcDzkpWJK9WKTl/cCGXjq9ErUymROV2VG8+CE+v1/MOWWPKA5BE7ypIwndCrBHta3El8WckJ14NWDOK37r0Y549vENYsXeRyb25e2UD18+2oVmPtbjJrXNfgiR610ISvhNjrWgfbJPuSRsd2lr+Wlz9asBeaZknDy4Qcf0g1Rr/gcxNI3f/oPJcOj6UiOtbKFKuc5a2L+XnXRNJ+E6ONaN9La4c9evDVlcD9hK0NUh7Ar51fh+BeWqmyh5AEXublKRYLh35iftXNqV7bZHynSlUpr3RfUiid20k4bsI1oz2tWRH8afFlWRtCYby795+oSTE3U33mJvcB0XsLYpX+gJ3d+3MoSI3/5tFkfKd9G5Lknz2QRK+C2GLaB9sn+5JizOcCFwVcwZYQ4o2QrVnPBE3VxJWrCuC4EZc9BXc5N4kJTwitHAHAF48PoanTy4Klm6XaRuS6LMfkvBdEFtE+1psVdOvxZC0pJPBa7JaPSOTyWny1h8c3zqMkztXIMjkePrkoVabaZzcPpKwYm8jdw/g/vUlVKw/GEGQAZLkszuS8F0UW0ofbC9+XeTkk4G1yiPT4htYiKbvrCQh7jFqVTI+AQUQBIHI2/uIvL0W/1wVUSkVuLtXlUSfQ5CE78LYKsWTFlvdzGUurnIysIW4s4q3Xz7g9bHMV+BtTu/+kOdRRyhStndqdG9NJMk7J5LwswH2ED84Juo3BWuOFzijsC1FX9Tu5RNKcIFwXj47T3CBplbdpyR650YSfjYi7Y8tu6V7LCE7ydsUzEnLlKg0kJTkWKtE95LkXQdJ+NmUnJTuyclYmnt3k/vgJvfJ0r4l0bseWTq9C4LwtiAIFwVBUAuCUNNAuzaCIFwVBOGGIAgjsrJPCfNQpaTY5YcZdfuhzW+zl3j9OTvy87bXd0rC+mQ1wv8P6AL8oq+BIAhuwFygJRABnBAEYYMoipeyuG8JM7B3ugekqN9aOMOJVBJ89iBLwhdF8TKAIBhcaKI2cEMUxVuv2q4EOgGS8B2EvQd5dSGdDPTjDILXIok+e2GPHH4B4H6a/0cAdfQ1FgShL9AXwNM7xLY9y+HYS/y60Ce1nHgicCbBgyT57IxR4QuCsBMI1fHUN6IorjdhH7rCf71LEYmiuABYAOAXVCZ7L1nkJDhS/BnJTlcFziZyfUiCzzkYFb4oii2yuI8IoFCa/xcEXOOXkMOwV57fUpzhZOAqEjeEJPiciz1SOieAUoIgFAMeAN2BnnbYr0QWcKao3xQsTRFlB4EbQxK8hJYsCV8QhDeBOUBeYJMgCGdFUWwtCEIY8Ksoiu1EUVQKgvAZsA1wAxaLongxyz2XsAuuJv6M5AShZ0QSvIQ+slql8w/wj47HHwLt0vx/M7A5K/uScCzOnu7JqUhylzAH6U5bCbNx9ajflZEEL5EVJOFLWIwkftsjCV7CmkjCl8gykvithyR4CVsiCV/Cakh5ftORxC7hCCThS9gEc4SWXU4OksQlnB1J+BIOx1xR2vMEIUlcIjshCV/C5bBEwmlPEpLEJXIqkvAlcgSS5CUksrgAioSEhISE6yAJX0JCQiKHIAlfQkJCIocgCV9CQkIihyCIovOuMSIIwhPgrp12Fww8tdO+XAnpc9GN9LnoRvpcMmPvz6SIKIp5dT3h1MK3J4IgnBRFsaaj++FsSJ+LbqTPRTfS55IZZ/pMpJSOhISERA5BEr6EhIREDkES/msWOLoDTor0uehG+lx0I30umXGaz0TK4UtISEjkEKQIX0JCQiKHIAlfQkJCIoeQY4UvCMLbgiBcFARBLQiC3pIpQRDaCIJwVRCEG4IgjLBnHx2BIAi5BUHYIQjC9Vd/59LT7o4gCBcEQTgrCMJJe/fTXhg7/oKGH189f14QhOqO6Kc9MeEzCRcEIebVd+OsIAijHdFPeyMIwmJBEB4LgvCfnucd/l3JscIH/gO6APv1NRAEwQ2YC7QFygM9BEEob5/uOYwRwC5RFEsBu179Xx9NRVGs6iw1xtbGxOPfFij16k9fYL5dO2lnzPhNHHj13agqiuJ4u3bScfwOtDHwvMO/KzlW+KIoXhZF8aqRZrWBG6Io3hJFMRlYCXSyfe8cSidgyat/LwE6O64rDseU498J+EPUcBQIEgQhv707akdy4m/CJERR3A88N9DE4d+VHCt8EykA3E/z/4hXj2VnQkRRjAR49Xc+Pe1EYLsgCKcEQehrt97ZF1OOf077jpj6fusJgnBOEIQtgiBUsE/XnB6Hf1ey9QIogiDsBEJ1PPWNKIrrTdmEjsdcvo7V0OdixmYaiKL4UBCEfMAOQRCuvIpwshOmHP9s+R0xgCnv9zSa+VziBEFoB6xDk8bI6Tj8u5KthS+KYossbiICKJTm/wWBh1ncpsMx9LkIghAlCEJ+URQjX11uPtazjYev/n4sCMI/aC71s5vwTTn+2fI7YgCj71cUxZdp/r1ZEIR5giAEi6KY0ydVc/h3RUrpGOYEUEoQhGKCIHgA3YENDu6TrdkA9Hr1715ApishQRB8BUHw1/4baIVmEDy7Ycrx3wB88KoCoy4Qo02JZVOMfiaCIIQKgiC8+ndtNJ55ZveeOh8O/65k6wjfEIIgvAnMAfICmwRBOCuKYmtBEMKAX0VRbCeKolIQhM+AbYAbsFgUxYsO7LY9mAKsFgShD3APeBsg7ecChAD/vPpNy4HloihudVB/bYa+4y8IQr9Xz/8MbAbaATcABfCho/prD0z8TN4CPhUEQQkkAN3FHHBLvyAIK4BwIFgQhAhgDOAOzvNdkaZWkJCQkMghSCkdCQkJiRyCJHwJCQmJHIIkfAkJCYkcgiR8CQkJiRyCJHwJCQmJHIIkfAkJCYkcgiR8CQkJiRzC/wH4z6IxUj3jawAAAABJRU5ErkJggg==", "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 26f04c75753b9035d39f03da9cb3e0d96c686061..db301b8fc562a6d57a3b80803f7a5e78fd91b4ef 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 3478c276c372b257d1c62415449c092543e4c3c0..f3eb5b4068b6dd9a28189e8a7484184b5b25734c 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 a1563b80dec43474c6501c6fdb7b5704f9670340..386bcaa20604dc5cad2fa78c1f3fc7cf5b8706b3 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 507964054c6910a4d6d7375bbda276ef39fe1088..eff7ae0224f8a8ebcbb1e28bb55bdcf60fae406a 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 349e1375f22ff3f07ac4bb7ebd239c309013295c..13bdf69d91286d2e9156ed7978a707f94e97a82e 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 9d20b852e49393f0a80899cf09e8261b74c77fc8..36b263d8d4323600b64a20b0131781f00f13e442 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 1de85fdc2f7ec114156608ceef378e5593c5b068..88a36f9b20a4a4452551c8b037b4742d0ba9faf9 100644 Binary files a/tutorials/quantum_simulation/output/summary_data.npz and b/tutorials/quantum_simulation/output/summary_data.npz differ