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.
Files changed (60) hide show
  1. gpuqviz/__init__.py +45 -0
  2. gpuqviz/adapters/__init__.py +176 -0
  3. gpuqviz/algorithms/__init__.py +118 -0
  4. gpuqviz/algorithms/_bernstein_vazirani.py +71 -0
  5. gpuqviz/algorithms/_deutsch_jozsa.py +90 -0
  6. gpuqviz/algorithms/_engine.py +95 -0
  7. gpuqviz/algorithms/_grover.py +187 -0
  8. gpuqviz/algorithms/_primitive.py +106 -0
  9. gpuqviz/algorithms/_qft.py +110 -0
  10. gpuqviz/algorithms/_qpe.py +79 -0
  11. gpuqviz/algorithms/_quantum_walk.py +123 -0
  12. gpuqviz/algorithms/_simon.py +84 -0
  13. gpuqviz/algorithms/_superdense.py +90 -0
  14. gpuqviz/algorithms/_teleportation.py +100 -0
  15. gpuqviz/analysis/__init__.py +27 -0
  16. gpuqviz/analysis/entanglement.py +164 -0
  17. gpuqviz/analysis/measurement.py +223 -0
  18. gpuqviz/analysis/metrics.py +210 -0
  19. gpuqviz/api.py +968 -0
  20. gpuqviz/assets/font_msyh_64.bin +0 -0
  21. gpuqviz/assets/font_msyh_64.json +1 -0
  22. gpuqviz/assets/vendor/three.min.js +7 -0
  23. gpuqviz/assets/vendor/three.module.min.js +6 -0
  24. gpuqviz/assets/viewer.js +486 -0
  25. gpuqviz/assets/viewer_template.html +116 -0
  26. gpuqviz/backends/__init__.py +66 -0
  27. gpuqviz/backends/cpu.py +715 -0
  28. gpuqviz/circuits.py +383 -0
  29. gpuqviz/cli.py +354 -0
  30. gpuqviz/encode.py +276 -0
  31. gpuqviz/env.py +123 -0
  32. gpuqviz/evolve.py +194 -0
  33. gpuqviz/export_html.py +243 -0
  34. gpuqviz/interpolate.py +106 -0
  35. gpuqviz/jupyter.py +408 -0
  36. gpuqviz/mps.py +311 -0
  37. gpuqviz/noise.py +245 -0
  38. gpuqviz/parameters.py +181 -0
  39. gpuqviz/pipeline.py +85 -0
  40. gpuqviz/presets.py +52 -0
  41. gpuqviz/preview.py +118 -0
  42. gpuqviz/pro.py +202 -0
  43. gpuqviz/render/__init__.py +5 -0
  44. gpuqviz/render/bloch.py +254 -0
  45. gpuqviz/render/colorconvert.py +68 -0
  46. gpuqviz/render/compositor.py +26 -0
  47. gpuqviz/render/context.py +171 -0
  48. gpuqviz/render/density.py +177 -0
  49. gpuqviz/render/entanglement.py +173 -0
  50. gpuqviz/render/heatmap.py +180 -0
  51. gpuqviz/render/histogram.py +182 -0
  52. gpuqviz/render/phasesphere.py +86 -0
  53. gpuqviz/render/text.py +140 -0
  54. gpuqviz/scene.py +239 -0
  55. gpuqviz/state.py +332 -0
  56. gpuqviz-0.5.0.dist-info/METADATA +359 -0
  57. gpuqviz-0.5.0.dist-info/RECORD +60 -0
  58. gpuqviz-0.5.0.dist-info/WHEEL +4 -0
  59. gpuqviz-0.5.0.dist-info/entry_points.txt +2 -0
  60. 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)