# quantum-base **Repository Path**: aurawing/quantum-base ## Basic Information - **Project Name**: quantum-base - **Description**: 量子计算模拟器QVecOpt - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-09-03 - **Last Updated**: 2026-07-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 本项目为基于块设备的高性能量子计算模拟器。其核心思想是利用大规模、低成本的块设备(如内存盘、NVMe SSD)来存储庞大的量子态向量,并通过优化的数据访问和计算策略来模拟量子线路的演化。 # 核心组件 1. `main/main.go`: 程序入口,负责解析配置文件 config.yaml,初始化并管理多个计算节点。它支持主从(Master-Slave)模式的分布式计算,主节点负责接收计算任务、分发任务并汇总结果,而子节点则执行具体的计算任务。 2. `quantum_base.go`: 定义了核心的数据结构和管理模块。 * Config: 定义了模拟器的所有配置参数,如设备数量、大小、数据类型、缓存大小、节点信息等。 * DeviceManager: 核心模块,负责管理底层的块设备。它将量子态向量分布在多个块设备上,并提供了读写接口。为了提升性能,它还实现了一个多级缓存机制。 3. `gate_parser.go`: 定义了各种量子门(单比特门和两比特门)的矩阵表示。它提供了一个 GateMap,可以将字符串形式的量子门名称(如 "x", "h", "cx")映射到其对应的矩阵实现。 4. `gate_opt.go`: 包含了一些辅助函数,例如用于打印量子态和验证计算结果的函数。 5. `utils.go`: 提供了一些工具函数,例如将门操作序列转换为 CombinedGate 结构,以及矩阵乘法等。 # 整体架构 该模拟器的架构可以概括为以下几层: 1. 任务接收与分发层: 主节点通过 HTTP 服务接收来自用户的计算任务(一个包含量子比特数、初始态和量子门序列的 JSON 对象)。主节点解析任务,并将计算任务分发给所有计算节点(包括自身)。 2. 分布式计算层: 多个计算节点并行执行计算。每个节点负责计算量子态向量的一部分。节点间的同步通过主节点的 HTTP 接口实现。每个节点在完成一轮(一个量子门)的计算后,会通知主节点,并等待所有其他节点都完成后再开始下一轮计算。 3. 量子态演化层: 这是模拟器的核心。每个计算节点根据分配到的量子态范围和当前的量子门,更新其负责的量子态。 4. 缓存管理层: 为了减少对慢速块设备的访问,DeviceManager 实现了一个内存缓存。在进行量子门计算时,它会根据门操作的特性,智能地将需要的数据从块设备读入缓存,计算后再写回。 5. 存储层: 量子态向量被存储在一个或多个块设备上。DeviceManager 通过直接的系统调用(pread, pwrite)来读写设备,以获得更高的 I/O 性能。 ## 计算任务提交方式 一个量子线路的计算任务可通过以下接口提交给计算引擎,其中`nqubits`为量子比特数,`initial_state`为初始量子态,`gates`为量子门描述列表: ```bash curl -X POST http://:/initialize -H "Content-Type: application/json" -d '{ "nqubits": 29, "initial_state": 8192, "gates": [ { "name": "h", "qubits": [27], "params": [] }, { "name": "x", "qubits": [27], "params": [] }, { "name": "cx", "qubits": [28, 27], "params": [] } ] }' ``` ## 架构图 ```Mermaid graph TD subgraph "用户" A[编译引擎] end subgraph "主节点 (Master Node)" B[HTTP Server] C[任务解析与分发] D[结果汇总] end subgraph "计算节点 (Worker Nodes)" E1[计算节点 1] E2[计算节点 2] E3[...] end subgraph "单个计算节点内部" F[量子态计算循环] G[DeviceManager] H[内存缓存] I[块设备] end A -- JSON任务 --> B B -- 解析后任务 --> C C -- 分发任务 --> E1 C -- 分发任务 --> E2 C -- 分发任务 --> E3 E1 -- 计算 --> F E2 -- 计算 --> F E3 -- 计算 --> F F -- 读写数据 --> G G -- 缓存未命中 --> I G -- 缓存命中 --> H H -- 读写 --> F I -- 读写 --> H E1 -- 完成通知 --> D E2 -- 完成通知 --> D E3 -- 完成通知 --> D D -- 最终结果 --> A ``` # 量子门矩阵计算与配对优化算法 ## 量子门矩阵计算原理 在量子计算模拟中,系统的状态由一个长度为 $2^n$ 的复数向量表示,其中 $n$ 为量子比特数。量子门的作用本质上是对该状态向量施加一个酉变换(Unitary Transformation),即对其进行矩阵乘法。 * 单比特门: 一个单比特门作用于第 q 个量子比特,其对应的 $2^n$ x $2^n$ 矩阵可以表示为: I ⊗ ... ⊗ I ⊗ U ⊗ I ⊗ ... ⊗ I 其中 U 是一个 2x2 的矩阵,作用在第 q 个位置,I 是 2x2 的单位矩阵,⊗ 表示克罗内克积。 * 两比特门: 一个两比特门作用于第 q0 和 q1 个量子比特,其对应的 $2^n$ x $2^n$ 矩阵构造更为复杂,但原理类似。 但由于该矩阵维度为 $2^n \times 2^n$,直接构造与存储不可行,因此采用“按需作用”方式,即通过识别量子态中受影响的子空间,对其对应部分进行局部更新。下边将分别介绍单比特门与两比特门的优化计算方法,重点在于配对态的识别与更新公式的推导。 ## 单比特量子门计算算法 设总量子比特数为 $n$,目标门 $U \in \mathbb{C}^{2\times2}$ 作用于第 $q$ 位($0 \le q < n$)量子比特。 ### 配对态分析 一个 $n$ 比特二进制状态 $x \in \{0,1\}^n$ 可表示为: $$ x = (x_{n-1}, ..., x_{q+1}, x_q, x_{q-1}, ..., x_0) $$ 我们将状态分为两组:$x_q = 0$ 和 $x_q = 1$。每一对只在第 $q$ 位不同的状态称为“配对态”。 对于某个基态 $|x\rangle$,若 $x_q = 0$,其配对态记为 $|x'\rangle$,其中 $x' = x \oplus (1 \ll q)$。该对的概率幅为 $\alpha = \psi[x], \beta = \psi[x']$。应用单比特门 $U$ 后,它们变为: $$ \begin{bmatrix} \psi'[x] \\ \psi'[x'] \end{bmatrix} = U \begin{bmatrix} \psi[x] \\ \psi[x'] \end{bmatrix} $$ 即: $$ \begin{aligned} \psi'[x] &= U_{00} \cdot \psi[x] + U_{01} \cdot \psi[x'] \\ \psi'[x'] &= U_{10} \cdot \psi[x] + U_{11} \cdot \psi[x'] \end{aligned} $$ ### 索引构造公式 设原始状态索引为 $i$,下标 $x$ 和 $x'$ 的整数索引需满足: $$ x' = x \oplus (1 \ll q) $$ 则两个配对状态的索引为: - $x = i$ - $x' = i + (1 \ll q)$ 只需遍历所有 $x$ 满足 $x_q = 0$,即 $x \& (1 \ll q) == 0$,即可获得所有不重复配对。 ## 两比特量子门计算算法 设 $U \in \mathbb{C}^{4\times4}$ 是一个作用于第 $q_0, q_1$ 位的双比特门($q_0 \ne q_1$,记 $q_0 > q_1$)。则它作用于由这两个比特控制的四个基态所张成的四维子空间。 ### 配对态分析 对于任意状态 $x \in \{0,1\}^n$,若其在 $q_0$ 与 $q_1$ 位上均为 0,则它与以下三个状态构成一个“4-态组”: - $x_{00} = x$($q_0=0, q_1=0$) - $x_{01} = x \oplus (1 \ll q_1)$($q_0=0, q_1=1$) - $x_{10} = x \oplus (1 \ll q_0)$($q_0=1, q_1=0$) - $x_{11} = x \oplus (1 \ll q_0) \oplus (1 \ll q_1)$($q_0=1, q_1=1$) 这四个状态构成一个计算单元,其概率幅分别为: $$ \begin{aligned} \alpha &= \psi[x_{00}] \\ \beta &= \psi[x_{01}] \\ \gamma &= \psi[x_{10}] \\ \delta &= \psi[x_{11}] \end{aligned} $$ $U$ 对这组状态的更新可表示为: $$ \begin{bmatrix} \psi'[x_{00}] \\ \psi'[x_{01}] \\ \psi'[x_{10}] \\ \psi'[x_{11}] \end{bmatrix} = U \begin{bmatrix} \alpha \\ \beta \\ \gamma \\ \delta \end{bmatrix} $$ 即更新为: $$ \begin{aligned} \psi'[x_{00}] &= U_{00} \alpha + U_{01} \beta + U_{02} \gamma + U_{03} \delta \\ \psi'[x_{01}] &= U_{10} \alpha + U_{11} \beta + U_{12} \gamma + U_{13} \delta \\ \psi'[x_{10}] &= U_{20} \alpha + U_{21} \beta + U_{22} \gamma + U_{23} \delta \\ \psi'[x_{11}] &= U_{30} \alpha + U_{31} \beta + U_{32} \gamma + U_{33} \delta \end{aligned} $$ ### 索引构造公式 设原始状态索引为 $i$,满足: $$ (i \& (1 \ll q_0)) == 0,\quad (i \& (1 \ll q_1)) == 0 $$ 则四个配对状态的索引为: - $x_{00} = i$ - $x_{01} = i + (1 \ll q_1)$ - $x_{10} = i + (1 \ll q_0)$ - $x_{11} = i + (1 \ll q_0) + (1 \ll q_1)$ 故只需对满足上述条件的 $i$ 进行遍历,即可更新所有四态组。 ## 量子门配对算法与缓存分配 该模拟器的核心优化在于其量子门配对算法以及与之配合的缓存分配策略。其目标是最小化对底层块设备的 I/O 操作。 关键思想: 在计算一个量子门时,将所有相关的量子态(即需要相互计算的配对态)一次性读入内存缓存中,在缓存中完成计算,然后再将结果写回块设备。 ### 1. 单比特门 (LoopCacheBlock) 对于一个作用在第 $q$ 个量子比特的单比特门,任意两个配对的量子态 |...0...⟩ 和 |...1...⟩ 的索引(即二进制表示的整数值)相差 $2^q$。 * 计算位数和量子态间隔的关系: * 量子态索引间隔 $d = 2^q$ * 其中 $q$ 是量子门作用的比特位。 * 缓存分配规则: 1. 判断合并: 首先判断 $2^{q+1} \times \text{blockSize}$ 是否大于 cacheBlockSize。blockSize 是存储一个复数所需的字节数(complex64 为 8,complex128 为 16)。$2^{q+1}$ 表示了包含所有配对状态的最小连续数据块的大小。 2. 情况一:可以合并 (`combine = true`): 如果间隔 $d$ 较小,使得一个缓存块(cacheBlockSize)可以同时容纳下所有需要计算的配对态,那么就使用一个完整的缓存块 dm.cache。 * 算法: 1. 从块设备中读取一个大小为 cacheBlockSize 的数据块到 dm.cache。 2. 在 dm.cache 中,遍历每一个索引 $i$,找到其配对索引 $j = i + d$。 3. 根据 $U$ 矩阵更新 $i$ 和 $j$ 位置的量子态振幅。 4. 将更新后的 dm.cache 写回块设备。 3. 情况二:不可合并 (`combine = false`): 如果间隔 $d$ 很大,一个缓存块无法同时容纳配对的两个状态,那么就需要将缓存分成两半(dm.cache1 和 dm.cache2),分别用于存储配对的两个数据块。 * 算法: 1. 从块设备中读取一个数据块到 dm.cache1。 2. 计算出配对数据块的位置,并将其读取到 dm.cache2。 3. 遍历 dm.cache1 中的每个索引 $i$,其对应的配对态在 dm.cache2 的相同偏移位置。 4. 根据 $U$ 矩阵更新 dm.cache1 和 dm.cache2 中的量子态振幅。 5. 将更新后的 dm.cache1 和 dm.cache2 分别写回它们在块设备中原来的位置。 ### 2. 两比特门 (LoopCacheBlock2) 对于一个作用在第 $q0$ 和 $q1$ 个量子比特的两比特门(假设 $q0 > q1$),会涉及到四个相互关联的量子态: * |...0...0...⟩ (索引 $i$) * |...0...1...⟩ (索引 $i + 2^{q1}$) * |...1...0...⟩ (索引 $i + 2^{q0}$) * |...1...1...⟩ (索引 $i + 2^{q0} + 2^{q1}$) * 计算位数和量子态间隔的关系: * 高位间隔 $d_{high} = 2^{q0}$ * 低位间隔 $d_{low} = 2^{q1}$ * 缓存分配规则: 该算法根据 $d_{high}$ 和 $d_{low}$ 与缓存大小的关系,分为三种情况: 1. 单缓存模式: 如果 $2^{q0+1} * blockSize \le cacheBlockSize$,说明所有四个相关的状态都可以在一个缓存块内找到。 * 算法: 类似于单比特门的合并情况,一次性读入一个大缓存块,在内部完成四个状态的更新,然后写回。 2. 双缓存模式: 如果 $2^{q0+1} * blockSize > cacheBlockSize$ 但 $2^{q1+1} * blockSize \le cacheBlockSize$,说明由低位 $q1$ 关联的两个状态(|...0...0...⟩ 和 |...0...1...⟩)可以在一个缓存块内,但由高位 $q0$ 关联的状态则在远端。 * 算法: 将缓存分为 dm.cache1 和 dm.cache2。dm.cache1 用于存储 |...0...x...⟩ 的数据块,dm.cache2 用于存储 |...1...x...⟩ 的数据块。计算时,需要同时访问 dm.cache1 和 dm.cache2 来获取四个配对态。 3. 四缓存模式: 如果 $2^{q1+1} * blockSize > cacheBlockSize$,说明即便是低位关联的状态也无法放在一个缓存块内。这是最复杂的情况。 * 算法: 将缓存分为四个部分 dm.cache21, dm.cache22, dm.cache23, dm.cache24,分别用于存储四个配对的数据块。计算时,需要从这四个缓存中分别获取 |...0...0...⟩, |...0...1...⟩, |...1...0...⟩, |...1...1...⟩ 的振幅,更新后再写回各自的缓存。 # 总结 该项目通过以下几个关键设计实现了一个高性能的量子计算模拟器: * 分布式计算: 利用多个计算节点并行处理庞大的量子态向量,提高了整体的计算速度。 * 基于块设备的存储: 使用低级的块设备I/O来存储量子态,避免了文件系统的开销,实现了高速的数据访问。 * 优化的量子门算法: 避免了构建和存储庞大的门矩阵,而是通过直接计算门对量子态的作用来更新状态。 * 智能缓存管理: 核心优化点。通过分析量子门的特性,预测数据访问模式,并实现了单缓存、双缓存、四缓存的自适应策略,最大限度地减少了对慢速块设备的访问,将计算密集部分保持在内存中进行。 这种设计使得该模拟器能够有效地模拟比传统内存模拟器更大规模的量子系统。