gpuqviz 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gpuqviz/__init__.py +45 -0
- gpuqviz/adapters/__init__.py +176 -0
- gpuqviz/algorithms/__init__.py +118 -0
- gpuqviz/algorithms/_bernstein_vazirani.py +71 -0
- gpuqviz/algorithms/_deutsch_jozsa.py +90 -0
- gpuqviz/algorithms/_engine.py +95 -0
- gpuqviz/algorithms/_grover.py +187 -0
- gpuqviz/algorithms/_primitive.py +106 -0
- gpuqviz/algorithms/_qft.py +110 -0
- gpuqviz/algorithms/_qpe.py +79 -0
- gpuqviz/algorithms/_quantum_walk.py +123 -0
- gpuqviz/algorithms/_simon.py +84 -0
- gpuqviz/algorithms/_superdense.py +90 -0
- gpuqviz/algorithms/_teleportation.py +100 -0
- gpuqviz/analysis/__init__.py +27 -0
- gpuqviz/analysis/entanglement.py +164 -0
- gpuqviz/analysis/measurement.py +223 -0
- gpuqviz/analysis/metrics.py +210 -0
- gpuqviz/api.py +968 -0
- gpuqviz/assets/font_msyh_64.bin +0 -0
- gpuqviz/assets/font_msyh_64.json +1 -0
- gpuqviz/assets/vendor/three.min.js +7 -0
- gpuqviz/assets/vendor/three.module.min.js +6 -0
- gpuqviz/assets/viewer.js +486 -0
- gpuqviz/assets/viewer_template.html +116 -0
- gpuqviz/backends/__init__.py +66 -0
- gpuqviz/backends/cpu.py +715 -0
- gpuqviz/circuits.py +383 -0
- gpuqviz/cli.py +354 -0
- gpuqviz/encode.py +276 -0
- gpuqviz/env.py +123 -0
- gpuqviz/evolve.py +194 -0
- gpuqviz/export_html.py +243 -0
- gpuqviz/interpolate.py +106 -0
- gpuqviz/jupyter.py +408 -0
- gpuqviz/mps.py +311 -0
- gpuqviz/noise.py +245 -0
- gpuqviz/parameters.py +181 -0
- gpuqviz/pipeline.py +85 -0
- gpuqviz/presets.py +52 -0
- gpuqviz/preview.py +118 -0
- gpuqviz/pro.py +202 -0
- gpuqviz/render/__init__.py +5 -0
- gpuqviz/render/bloch.py +254 -0
- gpuqviz/render/colorconvert.py +68 -0
- gpuqviz/render/compositor.py +26 -0
- gpuqviz/render/context.py +171 -0
- gpuqviz/render/density.py +177 -0
- gpuqviz/render/entanglement.py +173 -0
- gpuqviz/render/heatmap.py +180 -0
- gpuqviz/render/histogram.py +182 -0
- gpuqviz/render/phasesphere.py +86 -0
- gpuqviz/render/text.py +140 -0
- gpuqviz/scene.py +239 -0
- gpuqviz/state.py +332 -0
- gpuqviz-0.5.0.dist-info/METADATA +359 -0
- gpuqviz-0.5.0.dist-info/RECORD +60 -0
- gpuqviz-0.5.0.dist-info/WHEEL +4 -0
- gpuqviz-0.5.0.dist-info/entry_points.txt +2 -0
- gpuqviz-0.5.0.dist-info/licenses/LICENSE +201 -0
gpuqviz/__init__.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""gpuqviz: GPU-accelerated quantum state evolution visualization and video rendering."""
|
|
2
|
+
|
|
3
|
+
__version__ = "0.5.0"
|
|
4
|
+
|
|
5
|
+
from .api import render, render_bloch_video, render_frame, render_heatmap_video # noqa: E401
|
|
6
|
+
from .env import report_env # noqa: E401
|
|
7
|
+
from .export_html import export_html # noqa: E401
|
|
8
|
+
from .presets import ( # noqa: E401
|
|
9
|
+
PRESETS, RES_1080P, RES_1440P, RES_4K, RES_480P, RES_720P, RES_8K,
|
|
10
|
+
RES_SQUARE_1080, RES_SQUARE_2160, RES_VERTICAL_1080, RES_VERTICAL_720,
|
|
11
|
+
)
|
|
12
|
+
from .scene import TextOverlay, TextPosition # noqa: E401
|
|
13
|
+
from .scene import (Scene, BlochTrack, BlochVectorsTrack, # noqa: E401
|
|
14
|
+
DensityMatrixTrack, EntanglementTrack, HeatmapTrack,
|
|
15
|
+
HistogramTrack, Camera)
|
|
16
|
+
from .state import DensityMatrix, Statevector # noqa: E401
|
|
17
|
+
|
|
18
|
+
# 分析层子模块(专业轨道:测量统计 / 度量 / 纠缠,不依赖渲染)
|
|
19
|
+
from . import analysis # noqa: E401
|
|
20
|
+
from .pro import ProReport, ProVisualizer # noqa: E401
|
|
21
|
+
|
|
22
|
+
# 算法库子模块(惰性加载,不依赖 qiskit/pyqpanda)
|
|
23
|
+
from . import algorithms # noqa: E401
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def show(*args, **kwargs):
|
|
27
|
+
"""Jupyter 交互集成:notebook 中一行代码内嵌 3D 播放器。
|
|
28
|
+
|
|
29
|
+
惰性 import jupyter 模块,避免未装 IPython 时 import gpuqviz 失败。
|
|
30
|
+
"""
|
|
31
|
+
from .jupyter import show as _show
|
|
32
|
+
|
|
33
|
+
return _show(*args, **kwargs)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
__all__ = ["__version__", "report_env", "render_bloch_video", "render_heatmap_video",
|
|
37
|
+
"render", "render_frame", "export_html", "show", "algorithms", "analysis",
|
|
38
|
+
"Statevector", "DensityMatrix",
|
|
39
|
+
"PRESETS", "RES_480P", "RES_720P", "RES_1080P", "RES_1440P",
|
|
40
|
+
"RES_4K", "RES_8K", "RES_SQUARE_1080", "RES_SQUARE_2160",
|
|
41
|
+
"RES_VERTICAL_720", "RES_VERTICAL_1080",
|
|
42
|
+
"TextOverlay", "TextPosition",
|
|
43
|
+
"Scene", "BlochTrack", "HeatmapTrack", "HistogramTrack",
|
|
44
|
+
"EntanglementTrack", "DensityMatrixTrack", "BlochVectorsTrack",
|
|
45
|
+
"Camera", "ProVisualizer", "ProReport"]
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""模拟器适配器:把第三方量子编程框架的电路接入 gpuqviz。
|
|
2
|
+
|
|
3
|
+
- qiskit:evolve.sample_circuit(原生支持);qiskit_to_gates 提供框架无关
|
|
4
|
+
的门级翻译(含 transpile 兜底),供 numpy 模拟器路径使用
|
|
5
|
+
- pyqpanda:sample_pyqpanda(经 ORIGINIR 转换 + numpy 演化)
|
|
6
|
+
|
|
7
|
+
pyqpanda 注意:transform_qprog_to_originir 必须使用与 prog 相同的 machine
|
|
8
|
+
实例,跨 machine 转换会在原生层段错误(pyqpanda 2.x/3.x 已知行为),
|
|
9
|
+
因此 machine 是必传参数,适配器绝不自建虚拟机。
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
# 命名门直译集合(其余门走矩阵路径 / definition 递归 / transpile 兜底)
|
|
17
|
+
_NAMED_1Q = {"h", "x", "y", "z", "s", "sdg", "t", "tdg", "id", "i"}
|
|
18
|
+
_NAMED_P = {"p", "u1", "u2", "u3", "u"}
|
|
19
|
+
_NAMED_ROT = {"rx", "ry", "rz"}
|
|
20
|
+
_TRANSPILE_BASIS = [
|
|
21
|
+
"id", "h", "x", "y", "z", "s", "sdg", "t", "tdg",
|
|
22
|
+
"rx", "ry", "rz", "u", "cx", "cy", "cz", "swap",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class _UnsupportedGate(Exception):
|
|
27
|
+
"""门无法直译也无法取矩阵/展开时抛出,由上层触发 transpile 兜底。"""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _translate_op(op, qidx: list[int], depth: int = 0) -> list:
|
|
31
|
+
"""单条 qiskit 指令 → Gate 列表。
|
|
32
|
+
|
|
33
|
+
优先级:命名直译 > to_matrix/Operator 矩阵 > definition 递归。
|
|
34
|
+
全局相位不影响 Bloch 向量与概率,统一忽略。
|
|
35
|
+
"""
|
|
36
|
+
from ..circuits import Gate
|
|
37
|
+
|
|
38
|
+
if depth > 32:
|
|
39
|
+
raise _UnsupportedGate(f"definition nesting too deep: {op.name}")
|
|
40
|
+
name = op.name
|
|
41
|
+
|
|
42
|
+
# 命名门直译(保持 gate 语义可读,且覆盖 circuits.py 的命名分发路径)
|
|
43
|
+
if name in _NAMED_1Q:
|
|
44
|
+
return [Gate(name.upper(), targets=qidx)]
|
|
45
|
+
if name in _NAMED_ROT or name in _NAMED_P:
|
|
46
|
+
return [Gate(name.upper(), targets=qidx, params=list(op.params))]
|
|
47
|
+
if name in ("cx", "cnot"):
|
|
48
|
+
return [Gate("CX", targets=[qidx[1]], controls=[qidx[0]])]
|
|
49
|
+
if name in ("cy", "cz", "ch"):
|
|
50
|
+
return [Gate(name.upper(), targets=[qidx[1]], controls=[qidx[0]])]
|
|
51
|
+
if name == "swap":
|
|
52
|
+
return [Gate("SWAP", targets=qidx)]
|
|
53
|
+
if name == "iswap":
|
|
54
|
+
return [Gate("ISWAP", targets=qidx)]
|
|
55
|
+
if name == "ccx":
|
|
56
|
+
return [Gate("CCX", targets=[qidx[2]], controls=qidx[:2])]
|
|
57
|
+
|
|
58
|
+
# 受控门(CRX/MCX/CU/…):直接取整门矩阵,qargs 顺序为 controls+targets
|
|
59
|
+
if getattr(op, "num_ctrl_qubits", 0):
|
|
60
|
+
try:
|
|
61
|
+
return [Gate("UNITARY", targets=qidx, matrix=_op_matrix(op))]
|
|
62
|
+
except Exception:
|
|
63
|
+
pass # 落到 definition 递归
|
|
64
|
+
|
|
65
|
+
# 任意酉门(UnitaryGate、自定义 Gate、库门):整门矩阵路径
|
|
66
|
+
try:
|
|
67
|
+
return [Gate("UNITARY", targets=qidx, matrix=_op_matrix(op))]
|
|
68
|
+
except Exception:
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
# 复合门:递归展开 definition(子电路 qubit i 对应 qidx[i])
|
|
72
|
+
defn = getattr(op, "definition", None)
|
|
73
|
+
if defn is not None and len(defn.data) > 0:
|
|
74
|
+
out = []
|
|
75
|
+
for sub in defn.data:
|
|
76
|
+
sub_q = [qidx[defn.find_bit(q).index] for q in sub.qubits]
|
|
77
|
+
out.extend(_translate_op(sub.operation, sub_q, depth + 1))
|
|
78
|
+
return out
|
|
79
|
+
|
|
80
|
+
raise _UnsupportedGate(f"cannot translate qiskit instruction: {name}")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _op_matrix(op) -> np.ndarray:
|
|
84
|
+
"""qiskit Gate/Instruction → 2^k×2^k 酉矩阵(LSB = op 自身 qubit 0)。"""
|
|
85
|
+
try:
|
|
86
|
+
m = op.to_matrix()
|
|
87
|
+
except Exception:
|
|
88
|
+
from qiskit.quantum_info import Operator
|
|
89
|
+
|
|
90
|
+
m = Operator(op).to_matrix()
|
|
91
|
+
return np.asarray(m, dtype=np.complex128)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _translate_circuit(circuit) -> tuple[int, list]:
|
|
95
|
+
from ..circuits import Gate
|
|
96
|
+
|
|
97
|
+
n = circuit.num_qubits
|
|
98
|
+
gates: list = []
|
|
99
|
+
warned_measure = False
|
|
100
|
+
for inst in circuit.data:
|
|
101
|
+
op = inst.operation
|
|
102
|
+
qidx = [circuit.find_bit(q).index for q in inst.qubits]
|
|
103
|
+
if op.name in ("measure", "reset"):
|
|
104
|
+
# 态矢量演化不涉及测量/重置:按层边界处理,保证与可视化语义一致
|
|
105
|
+
if not warned_measure:
|
|
106
|
+
print("[gpuqviz] measure/reset 不会改变态矢量演化,按层边界处理")
|
|
107
|
+
warned_measure = True
|
|
108
|
+
gates.append(Gate("BARRIER"))
|
|
109
|
+
continue
|
|
110
|
+
if op.name == "delay":
|
|
111
|
+
continue
|
|
112
|
+
if op.name == "barrier":
|
|
113
|
+
gates.append(Gate("BARRIER"))
|
|
114
|
+
continue
|
|
115
|
+
gates.extend(_translate_op(op, qidx))
|
|
116
|
+
return n, gates
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def qiskit_to_gates(circuit) -> tuple[int, list]:
|
|
120
|
+
"""qiskit QuantumCircuit → (n_qubits, Gate 列表),框架无关路径的入口。
|
|
121
|
+
|
|
122
|
+
兜底策略:出现无法翻译的指令时,transpile 到基础门集后重试一次。
|
|
123
|
+
电路的末尾测量自动剔除;中途 measure/reset 按层边界处理。
|
|
124
|
+
"""
|
|
125
|
+
circuit = circuit.remove_final_measurements(inplace=False)
|
|
126
|
+
try:
|
|
127
|
+
return _translate_circuit(circuit)
|
|
128
|
+
except _UnsupportedGate as e:
|
|
129
|
+
print(f"[gpuqviz] 直译失败({e}),transpile 到基础门集后重试")
|
|
130
|
+
from qiskit import transpile
|
|
131
|
+
|
|
132
|
+
t = transpile(circuit, basis_gates=_TRANSPILE_BASIS,
|
|
133
|
+
optimization_level=0)
|
|
134
|
+
return _translate_circuit(t)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def sample_pyqpanda(prog, steps: int, machine) -> list[np.ndarray]:
|
|
138
|
+
"""pyqpanda QProg → steps 个关键帧态矢量。
|
|
139
|
+
|
|
140
|
+
machine:创建 prog 时所用的量子虚拟机实例(必传,见模块 docstring)。
|
|
141
|
+
pyqpanda 为可选依赖,未安装时报错提示。
|
|
142
|
+
"""
|
|
143
|
+
if machine is None:
|
|
144
|
+
raise TypeError(
|
|
145
|
+
"sample_pyqpanda requires the QVM instance that `prog` was built on "
|
|
146
|
+
"(machine=qm). Cross-machine ORIGINIR transforms segfault in pyqpanda.")
|
|
147
|
+
try:
|
|
148
|
+
from pyqpanda import transform_qprog_to_originir
|
|
149
|
+
except ImportError as e: # pragma: no cover
|
|
150
|
+
raise ImportError(
|
|
151
|
+
"pyqpanda is required for pyqpanda circuits: pip install gpuqviz[pyqpanda]"
|
|
152
|
+
) from e
|
|
153
|
+
|
|
154
|
+
from ..circuits import sample_originir
|
|
155
|
+
|
|
156
|
+
originir = transform_qprog_to_originir(prog, machine)
|
|
157
|
+
return sample_originir(originir, steps)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def to_key_states(circuit, steps: int, machine=None) -> list[np.ndarray]:
|
|
161
|
+
"""自动识别电路类型并采样关键帧。
|
|
162
|
+
|
|
163
|
+
qiskit QuantumCircuit 直接识别;pyqpanda QProg 需要同时传入
|
|
164
|
+
machine=(创建 prog 的虚拟机实例)。
|
|
165
|
+
"""
|
|
166
|
+
# qiskit QuantumCircuit:num_qubits 属性 + data 指令列表
|
|
167
|
+
if hasattr(circuit, "num_qubits") and hasattr(circuit, "data"):
|
|
168
|
+
from ..evolve import sample_circuit
|
|
169
|
+
|
|
170
|
+
return sample_circuit(circuit, steps)
|
|
171
|
+
if machine is not None:
|
|
172
|
+
return sample_pyqpanda(circuit, steps, machine)
|
|
173
|
+
raise TypeError(
|
|
174
|
+
f"unsupported circuit type {type(circuit).__name__}: expected a qiskit "
|
|
175
|
+
"QuantumCircuit, or a pyqpanda QProg together with machine=<QVM instance>"
|
|
176
|
+
)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""gpuqviz 内置量子算法库。
|
|
2
|
+
|
|
3
|
+
每个算法函数按 ``engine`` 参数返回对应类型的电路对象:
|
|
4
|
+
- ``"qiskit"``(默认):返回 ``qiskit.QuantumCircuit``
|
|
5
|
+
- ``"pyqpanda"``:返回 ``pyqpanda.QProg``(需配合 machine)
|
|
6
|
+
- ``"numpy"``:返回 ``list[Gate]``(框架无关)
|
|
7
|
+
|
|
8
|
+
返回的电路对象可直接传入 ``render_bloch_video`` / ``export_html`` / ``show``
|
|
9
|
+
等可视化 API,或经 ``evolve_gates`` 做纯 numpy 演化。
|
|
10
|
+
|
|
11
|
+
用法::
|
|
12
|
+
|
|
13
|
+
from gpuqviz.algorithms import grover, bell, qft
|
|
14
|
+
|
|
15
|
+
qc = grover(n=3, marked=0b101, iterations=2)
|
|
16
|
+
gpuqviz.export_html(circuit=qc, out="out/grover.html")
|
|
17
|
+
|
|
18
|
+
# numpy 路径(无 qiskit 依赖)
|
|
19
|
+
gates = grover(n=3, engine="numpy")
|
|
20
|
+
from gpuqviz.circuits import evolve_gates
|
|
21
|
+
states = evolve_gates(3, gates)
|
|
22
|
+
|
|
23
|
+
CLI 一行命令::
|
|
24
|
+
|
|
25
|
+
gpuqviz demo --algo grover
|
|
26
|
+
gpuqviz demo --algo qft --format mp4
|
|
27
|
+
gpuqviz demo --list
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
from dataclasses import dataclass
|
|
33
|
+
from typing import Any, Callable
|
|
34
|
+
|
|
35
|
+
from ._primitive import bell, ghz, superposition
|
|
36
|
+
from ._grover import grover
|
|
37
|
+
from ._qft import qft
|
|
38
|
+
from ._qpe import phase_estimation
|
|
39
|
+
from ._deutsch_jozsa import deutsch_jozsa
|
|
40
|
+
from ._bernstein_vazirani import bernstein_vazirani
|
|
41
|
+
from ._teleportation import teleportation
|
|
42
|
+
from ._superdense import superdense
|
|
43
|
+
from ._simon import simon
|
|
44
|
+
from ._quantum_walk import quantum_walk
|
|
45
|
+
|
|
46
|
+
__all__ = [
|
|
47
|
+
"bell", "ghz", "superposition", "grover", "qft", "phase_estimation",
|
|
48
|
+
"deutsch_jozsa", "bernstein_vazirani", "teleportation", "superdense",
|
|
49
|
+
"simon", "quantum_walk",
|
|
50
|
+
"ALGORITHM_REGISTRY", "AlgorithmSpec", "get_algorithm", "list_algorithms",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass
|
|
55
|
+
class AlgorithmSpec:
|
|
56
|
+
"""算法注册表条目。"""
|
|
57
|
+
name: str
|
|
58
|
+
description: str
|
|
59
|
+
builder: Callable[..., Any]
|
|
60
|
+
default_n_qubits: int
|
|
61
|
+
category: str
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# 算法注册表:name → spec
|
|
65
|
+
ALGORITHM_REGISTRY: dict[str, AlgorithmSpec] = {
|
|
66
|
+
"bell": AlgorithmSpec(
|
|
67
|
+
"bell", "Bell 态 |Φ+⟩ = (|00⟩+|11⟩)/√2 制备", bell, 2, "基础态"),
|
|
68
|
+
"ghz": AlgorithmSpec(
|
|
69
|
+
"ghz", "n 量子比特 GHZ 纠缠态 (|0…0⟩+|1…1⟩)/√2", ghz, 3, "基础态"),
|
|
70
|
+
"superposition": AlgorithmSpec(
|
|
71
|
+
"superposition", "均匀叠加态 H^n |0…0⟩ = |+⟩^n", superposition, 3, "基础态"),
|
|
72
|
+
"grover": AlgorithmSpec(
|
|
73
|
+
"grover", "Grover 搜索算法(振幅放大)", grover, 3, "搜索"),
|
|
74
|
+
"qft": AlgorithmSpec(
|
|
75
|
+
"qft", "量子傅里叶变换", qft, 3, "变换"),
|
|
76
|
+
"phase_estimation": AlgorithmSpec(
|
|
77
|
+
"phase_estimation", "量子相位估计(QPE)", phase_estimation, 4, "估计"),
|
|
78
|
+
"deutsch_jozsa": AlgorithmSpec(
|
|
79
|
+
"deutsch_jozsa", "Deutsch-Jozsa 算法(常数 vs 平衡判定)",
|
|
80
|
+
deutsch_jozsa, 4, "查询复杂度"),
|
|
81
|
+
"bernstein_vazirani": AlgorithmSpec(
|
|
82
|
+
"bernstein_vazirani", "Bernstein-Vazirani 算法(恢复隐藏字符串)",
|
|
83
|
+
bernstein_vazirani, 3, "查询复杂度"),
|
|
84
|
+
"teleportation": AlgorithmSpec(
|
|
85
|
+
"teleportation", "量子隐形传态(3 qubit Bell pair + 经典校正)",
|
|
86
|
+
teleportation, 3, "通信"),
|
|
87
|
+
"superdense": AlgorithmSpec(
|
|
88
|
+
"superdense", "超密编码(1 qubit 传 2 经典比特)",
|
|
89
|
+
superdense, 2, "通信"),
|
|
90
|
+
"simon": AlgorithmSpec(
|
|
91
|
+
"simon", "Simon 算法(寻找 2-to-1 函数的隐藏周期)",
|
|
92
|
+
simon, 4, "查询复杂度"),
|
|
93
|
+
"quantum_walk": AlgorithmSpec(
|
|
94
|
+
"quantum_walk", "离散时间量子随机游走(硬币 + 条件移位)",
|
|
95
|
+
quantum_walk, 3, "游走"),
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def get_algorithm(name: str) -> AlgorithmSpec:
|
|
100
|
+
"""按名称获取算法注册表条目。不存在时抛 KeyError。"""
|
|
101
|
+
if name not in ALGORITHM_REGISTRY:
|
|
102
|
+
raise KeyError(
|
|
103
|
+
f"unknown algorithm {name!r}; available: {sorted(ALGORITHM_REGISTRY)}"
|
|
104
|
+
)
|
|
105
|
+
return ALGORITHM_REGISTRY[name]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def list_algorithms() -> str:
|
|
109
|
+
"""返回格式化的算法列表字符串(供 CLI --list 输出)。"""
|
|
110
|
+
lines = [
|
|
111
|
+
f"{'名称':<25} {'类别':<12} {'默认qubit':<10} {'说明'}",
|
|
112
|
+
f"{'─' * 25} {'─' * 12} {'─' * 10} {'─' * 40}",
|
|
113
|
+
]
|
|
114
|
+
for spec in ALGORITHM_REGISTRY.values():
|
|
115
|
+
lines.append(
|
|
116
|
+
f"{spec.name:<25} {spec.category:<12} {spec.default_n_qubits:<10} {spec.description}"
|
|
117
|
+
)
|
|
118
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Bernstein-Vazirani 算法电路构建器。
|
|
2
|
+
|
|
3
|
+
给定一个隐藏字符串 s ∈ {0,1}^n 和函数 f(x) = s · x (mod 2),
|
|
4
|
+
BV 算法用一次量子查询恢复 s。
|
|
5
|
+
|
|
6
|
+
电路结构:
|
|
7
|
+
1. 辅助 qubit |1⟩ → H
|
|
8
|
+
2. 输入寄存器 H(all)
|
|
9
|
+
3. Oracle Uf:对每个 s_i = 1 的 qubit 做 CX(i, aux)
|
|
10
|
+
4. 输入寄存器 H(all)
|
|
11
|
+
5. 测量输入寄存器 → 得到 |s⟩
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from ._engine import (
|
|
17
|
+
_check_engine,
|
|
18
|
+
_numpy_gates_to_circuit,
|
|
19
|
+
_qiskit_circuit,
|
|
20
|
+
)
|
|
21
|
+
from ..circuits import Gate
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def bernstein_vazirani(secret: str = "101", engine: str = "qiskit"):
|
|
25
|
+
"""Bernstein-Vazirani 算法电路。
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
secret : str
|
|
30
|
+
隐藏比特串,如 "101"(长度 = n)。从左到右对应 q0, q1, …, q_{n-1}。
|
|
31
|
+
engine : str
|
|
32
|
+
"qiskit" / "numpy"。
|
|
33
|
+
"""
|
|
34
|
+
engine = _check_engine(engine)
|
|
35
|
+
# secret 字符串:"101" → s[0]='1', s[1]='0', s[2]='1'
|
|
36
|
+
# qubit i 对应 secret[i]
|
|
37
|
+
n = len(secret)
|
|
38
|
+
if n < 1:
|
|
39
|
+
raise ValueError(f"secret must be non-empty, got {secret!r}")
|
|
40
|
+
bits = [int(c) for c in secret if c in "01"]
|
|
41
|
+
if len(bits) != n:
|
|
42
|
+
raise ValueError(f"secret must be binary string, got {secret!r}")
|
|
43
|
+
|
|
44
|
+
n_total = n + 1
|
|
45
|
+
aux = n
|
|
46
|
+
|
|
47
|
+
if engine == "qiskit":
|
|
48
|
+
qc = _qiskit_circuit(n_total)
|
|
49
|
+
qc.x(aux)
|
|
50
|
+
qc.h(range(n_total))
|
|
51
|
+
# Oracle:s_i = 1 时 CX(i, aux)
|
|
52
|
+
for i in range(n):
|
|
53
|
+
if bits[i]:
|
|
54
|
+
qc.cx(i, aux)
|
|
55
|
+
qc.h(range(n))
|
|
56
|
+
return qc
|
|
57
|
+
|
|
58
|
+
if engine == "pyqpanda":
|
|
59
|
+
raise NotImplementedError("use bernstein_vazirani_pyqpanda(qubits, machine, ...)")
|
|
60
|
+
|
|
61
|
+
# numpy 路径
|
|
62
|
+
gates: list[Gate] = []
|
|
63
|
+
gates.append(Gate("X", targets=[aux]))
|
|
64
|
+
for i in range(n_total):
|
|
65
|
+
gates.append(Gate("H", targets=[i]))
|
|
66
|
+
for i in range(n):
|
|
67
|
+
if bits[i]:
|
|
68
|
+
gates.append(Gate("CX", targets=[aux], controls=[i]))
|
|
69
|
+
for i in range(n):
|
|
70
|
+
gates.append(Gate("H", targets=[i]))
|
|
71
|
+
return _numpy_gates_to_circuit(gates)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Deutsch-Jozsa 算法电路构建器。
|
|
2
|
+
|
|
3
|
+
判断函数 f: {0,1}^n → {0,1} 是常数(所有输出相同)还是平衡(一半 0 一半 1)。
|
|
4
|
+
|
|
5
|
+
电路结构:
|
|
6
|
+
1. 辅助 qubit |1⟩ → H
|
|
7
|
+
2. 输入寄存器 H(all)
|
|
8
|
+
3. Oracle Uf(常数/平衡两种)
|
|
9
|
+
4. 输入寄存器 H(all)
|
|
10
|
+
5. 测量输入寄存器:全 0 = 常数,否则 = 平衡
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from ._engine import (
|
|
16
|
+
_check_engine,
|
|
17
|
+
_numpy_gates_to_circuit,
|
|
18
|
+
_qiskit_circuit,
|
|
19
|
+
)
|
|
20
|
+
from ..circuits import Gate
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _build_balanced_oracle_qiskit(qc, n: int):
|
|
24
|
+
"""平衡 oracle:f(x) = x_0(第一个 qubit 的值)。
|
|
25
|
+
用 CX(0, aux) 实现:aux ^= x_0。
|
|
26
|
+
"""
|
|
27
|
+
aux = n # 辅助 qubit 索引
|
|
28
|
+
qc.cx(0, aux)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _build_constant_oracle_qiskit(qc, n: int):
|
|
32
|
+
"""常数 oracle:f(x) = 0(不做任何操作)或 f(x) = 1(翻转 aux)。
|
|
33
|
+
这里用 f(x) = 1:X(aux)。
|
|
34
|
+
"""
|
|
35
|
+
aux = n
|
|
36
|
+
qc.x(aux)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def deutsch_jozsa(oracle_type: str = "balanced", n: int = 3,
|
|
40
|
+
engine: str = "qiskit"):
|
|
41
|
+
"""Deutsch-Jozsa 算法电路。
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
oracle_type : str
|
|
46
|
+
"balanced" 或 "constant"。
|
|
47
|
+
n : int
|
|
48
|
+
输入寄存器量子比特数(总 qubit = n + 1 辅助)。
|
|
49
|
+
engine : str
|
|
50
|
+
"qiskit" / "numpy"。
|
|
51
|
+
"""
|
|
52
|
+
engine = _check_engine(engine)
|
|
53
|
+
if oracle_type not in ("balanced", "constant"):
|
|
54
|
+
raise ValueError(f"oracle_type must be 'balanced' or 'constant', got {oracle_type!r}")
|
|
55
|
+
if n < 1:
|
|
56
|
+
raise ValueError(f"n must be >= 1, got {n}")
|
|
57
|
+
|
|
58
|
+
n_total = n + 1
|
|
59
|
+
aux = n
|
|
60
|
+
|
|
61
|
+
if engine == "qiskit":
|
|
62
|
+
qc = _qiskit_circuit(n_total)
|
|
63
|
+
# 辅助 qubit |1⟩
|
|
64
|
+
qc.x(aux)
|
|
65
|
+
# 全部 H
|
|
66
|
+
qc.h(range(n_total))
|
|
67
|
+
# Oracle
|
|
68
|
+
if oracle_type == "balanced":
|
|
69
|
+
_build_balanced_oracle_qiskit(qc, n)
|
|
70
|
+
else:
|
|
71
|
+
_build_constant_oracle_qiskit(qc, n)
|
|
72
|
+
# 输入寄存器 H
|
|
73
|
+
qc.h(range(n))
|
|
74
|
+
return qc
|
|
75
|
+
|
|
76
|
+
if engine == "pyqpanda":
|
|
77
|
+
raise NotImplementedError("use deutsch_jozsa_pyqpanda(qubits, machine, ...)")
|
|
78
|
+
|
|
79
|
+
# numpy 路径
|
|
80
|
+
gates: list[Gate] = []
|
|
81
|
+
gates.append(Gate("X", targets=[aux]))
|
|
82
|
+
for i in range(n_total):
|
|
83
|
+
gates.append(Gate("H", targets=[i]))
|
|
84
|
+
if oracle_type == "balanced":
|
|
85
|
+
gates.append(Gate("CX", targets=[aux], controls=[0]))
|
|
86
|
+
else:
|
|
87
|
+
gates.append(Gate("X", targets=[aux]))
|
|
88
|
+
for i in range(n):
|
|
89
|
+
gates.append(Gate("H", targets=[i]))
|
|
90
|
+
return _numpy_gates_to_circuit(gates)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""引擎抽象层:算法函数按 engine 参数返回对应类型的电路对象。
|
|
2
|
+
|
|
3
|
+
三种引擎:
|
|
4
|
+
- qiskit(默认):返回 qiskit.QuantumCircuit
|
|
5
|
+
- pyqpanda:返回 pyqpanda.QProg(需调用方传入 machine)
|
|
6
|
+
- numpy:返回 list[Gate](框架无关,纯 numpy 演化)
|
|
7
|
+
|
|
8
|
+
每个算法构建函数在内部按 engine 分发,算法逻辑不变。
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
from ..circuits import Gate
|
|
16
|
+
|
|
17
|
+
ENGINE_QISKIT = "qiskit"
|
|
18
|
+
ENGINE_PYQPANDA = "pyqpanda"
|
|
19
|
+
ENGINE_NUMPY = "numpy"
|
|
20
|
+
_VALID_ENGINES = {ENGINE_QISKIT, ENGINE_PYQPANDA, ENGINE_NUMPY}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _check_engine(engine: str) -> str:
|
|
24
|
+
if engine not in _VALID_ENGINES:
|
|
25
|
+
raise ValueError(
|
|
26
|
+
f"unsupported engine {engine!r}; expected one of {sorted(_VALID_ENGINES)}"
|
|
27
|
+
)
|
|
28
|
+
return engine
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# --------------------------------------------------------------------------- #
|
|
32
|
+
# qiskit 构建辅助
|
|
33
|
+
# --------------------------------------------------------------------------- #
|
|
34
|
+
|
|
35
|
+
def _qiskit_circuit(n_qubits: int):
|
|
36
|
+
"""创建 qiskit QuantumCircuit(惰性 import qiskit)。"""
|
|
37
|
+
from qiskit import QuantumCircuit
|
|
38
|
+
|
|
39
|
+
return QuantumCircuit(n_qubits)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# --------------------------------------------------------------------------- #
|
|
43
|
+
# pyqpanda 构建辅助
|
|
44
|
+
# --------------------------------------------------------------------------- #
|
|
45
|
+
|
|
46
|
+
def _pyqpanda_prog():
|
|
47
|
+
"""创建 pyqpanda QProg(惰性 import)。"""
|
|
48
|
+
from pyqpanda import QProg
|
|
49
|
+
|
|
50
|
+
return QProg()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _pyqpanda_gate(prog, name: str, qubits: list, params: list[float] | None = None,
|
|
54
|
+
controls: list | None = None):
|
|
55
|
+
"""向 QProg 追加一个门。qubits 是已分配的 pyqpanda qubit 引用列表。"""
|
|
56
|
+
import pyqpanda as pq
|
|
57
|
+
|
|
58
|
+
name = name.upper()
|
|
59
|
+
if name == "H":
|
|
60
|
+
prog << pq.H(qubits[0])
|
|
61
|
+
elif name == "X":
|
|
62
|
+
prog << pq.X(qubits[0])
|
|
63
|
+
elif name == "Y":
|
|
64
|
+
prog << pq.Y(qubits[0])
|
|
65
|
+
elif name == "Z":
|
|
66
|
+
prog << pq.Z(qubits[0])
|
|
67
|
+
elif name == "S":
|
|
68
|
+
prog << pq.S(qubits[0])
|
|
69
|
+
elif name == "T":
|
|
70
|
+
prog << pq.T(qubits[0])
|
|
71
|
+
elif name == "RX":
|
|
72
|
+
prog << pq.RX(qubits[0], params[0])
|
|
73
|
+
elif name == "RY":
|
|
74
|
+
prog << pq.RY(qubits[0], params[0])
|
|
75
|
+
elif name == "RZ":
|
|
76
|
+
prog << pq.RZ(qubits[0], params[0])
|
|
77
|
+
elif name in ("CX", "CNOT"):
|
|
78
|
+
prog << pq.CNOT(qubits[0], qubits[1])
|
|
79
|
+
elif name == "CZ":
|
|
80
|
+
prog << pq.CZ(qubits[0], qubits[1])
|
|
81
|
+
elif name == "SWAP":
|
|
82
|
+
prog << pq.SWAP(qubits[0], qubits[1])
|
|
83
|
+
elif name in ("CCX", "TOFFOLI"):
|
|
84
|
+
prog << pq.Toffoli(qubits[0], qubits[1], qubits[2])
|
|
85
|
+
else:
|
|
86
|
+
raise ValueError(f"pyqpanda does not support gate {name}")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# --------------------------------------------------------------------------- #
|
|
90
|
+
# numpy 构建辅助
|
|
91
|
+
# --------------------------------------------------------------------------- #
|
|
92
|
+
|
|
93
|
+
def _numpy_gates_to_circuit(gates: list[Gate]) -> list[Gate]:
|
|
94
|
+
"""numpy 路径直接返回 Gate 列表。"""
|
|
95
|
+
return list(gates)
|