quonic 0.2.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.
- quonic/__init__.py +39 -0
- quonic/algorithms/__init__.py +27 -0
- quonic/algorithms/grover.py +109 -0
- quonic/algorithms/hamiltonians.py +34 -0
- quonic/algorithms/oracle.py +42 -0
- quonic/algorithms/qaoa.py +93 -0
- quonic/algorithms/qpe.py +55 -0
- quonic/algorithms/quantum_counting.py +109 -0
- quonic/algorithms/shor.py +284 -0
- quonic/algorithms/vqe.py +79 -0
- quonic/backends/__init__.py +72 -0
- quonic/backends/base.py +25 -0
- quonic/backends/cirq.py +102 -0
- quonic/backends/native.py +107 -0
- quonic/backends/pennylane.py +114 -0
- quonic/backends/qi.py +141 -0
- quonic/backends/qiskit.py +122 -0
- quonic/compare.py +88 -0
- quonic/compiler.py +294 -0
- quonic/gates.py +83 -0
- quonic/ir.py +150 -0
- quonic/noise.py +52 -0
- quonic/qft.py +32 -0
- quonic/qgate.py +17 -0
- quonic/qif.py +312 -0
- quonic/qint.py +152 -0
- quonic/qshow.py +124 -0
- quonic/result.py +37 -0
- quonic/scheduler/__init__.py +91 -0
- quonic/scheduler/benchmark.py +342 -0
- quonic/scheduler/capabilities.py +85 -0
- quonic/scheduler/data/benchmarks.json +197 -0
- quonic/scheduler/features.py +76 -0
- quonic/scheduler/registry.py +180 -0
- quonic/simulator.py +86 -0
- quonic/simulators/__init__.py +21 -0
- quonic/simulators/_density.py +141 -0
- quonic/simulators/_gates.py +66 -0
- quonic/simulators/_mps.py +164 -0
- quonic/simulators/_stabilizer.py +179 -0
- quonic/simulators/_statevector.py +88 -0
- quonic/stack.py +48 -0
- quonic/topology.py +65 -0
- quonic/viz/__init__.py +73 -0
- quonic/viz/_mpl.py +62 -0
- quonic/viz/algorithm.py +264 -0
- quonic/viz/circuit.py +270 -0
- quonic/viz/gate.py +87 -0
- quonic/viz/noise.py +193 -0
- quonic/viz/result.py +53 -0
- quonic/viz/routing.py +75 -0
- quonic/viz/scheduler.py +370 -0
- quonic/viz/state.py +449 -0
- quonic-0.2.0.dist-info/METADATA +254 -0
- quonic-0.2.0.dist-info/RECORD +58 -0
- quonic-0.2.0.dist-info/WHEEL +5 -0
- quonic-0.2.0.dist-info/licenses/LICENSE +201 -0
- quonic-0.2.0.dist-info/top_level.txt +1 -0
quonic/__init__.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""QuoNic —— 量子编程,像写 Python 一样简单。"""
|
|
2
|
+
|
|
3
|
+
from . import gates
|
|
4
|
+
from .compare import qeq, qgt, qlt
|
|
5
|
+
from .compiler import RoutingError, decompose
|
|
6
|
+
from .noise import NoiseModel, depolarizing
|
|
7
|
+
from .qgate import qgate
|
|
8
|
+
from .qif import cif, controlled, creg, cwhile, qif
|
|
9
|
+
from .qint import QInt, mul
|
|
10
|
+
from .qshow import qshow
|
|
11
|
+
from .result import Result
|
|
12
|
+
from .stack import reset
|
|
13
|
+
from .topology import CouplingMap
|
|
14
|
+
|
|
15
|
+
__version__ = "0.2.0"
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"qgate",
|
|
19
|
+
"qif",
|
|
20
|
+
"cif",
|
|
21
|
+
"controlled",
|
|
22
|
+
"creg",
|
|
23
|
+
"cwhile",
|
|
24
|
+
"qlt",
|
|
25
|
+
"qeq",
|
|
26
|
+
"qgt",
|
|
27
|
+
"mul",
|
|
28
|
+
"qshow",
|
|
29
|
+
"reset",
|
|
30
|
+
"gates",
|
|
31
|
+
"QInt",
|
|
32
|
+
"Result",
|
|
33
|
+
"NoiseModel",
|
|
34
|
+
"depolarizing",
|
|
35
|
+
"CouplingMap",
|
|
36
|
+
"RoutingError",
|
|
37
|
+
"decompose",
|
|
38
|
+
"__version__",
|
|
39
|
+
]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""算法模板:Grover 搜索 / VQE / QAOA / QPE。
|
|
2
|
+
|
|
3
|
+
每个模板只依赖 numpy / scipy,不绑定具体后端;采样类算法
|
|
4
|
+
(Grover、QPE)可切换 qiskit / cirq / pennylane 任意后端。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .grover import diffusion, grover, mark_state
|
|
8
|
+
from .hamiltonians import from_qiskit_nature
|
|
9
|
+
from .oracle import oracle
|
|
10
|
+
from .qaoa import qaoa_maxcut
|
|
11
|
+
from .qpe import qpe
|
|
12
|
+
from .quantum_counting import quantum_counting
|
|
13
|
+
from .shor import shor
|
|
14
|
+
from .vqe import vqe
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"grover",
|
|
18
|
+
"mark_state",
|
|
19
|
+
"diffusion",
|
|
20
|
+
"oracle",
|
|
21
|
+
"quantum_counting",
|
|
22
|
+
"shor",
|
|
23
|
+
"vqe",
|
|
24
|
+
"qaoa_maxcut",
|
|
25
|
+
"from_qiskit_nature",
|
|
26
|
+
"qpe",
|
|
27
|
+
]
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Grover 搜索算法模板。
|
|
2
|
+
|
|
3
|
+
搜索某个计算基态,最简单的方式是直接传比特串(mark_state 自动生成神谕):
|
|
4
|
+
|
|
5
|
+
from quonic.algorithms import grover
|
|
6
|
+
result = grover("11", 2, shots=1024) # 在 2 比特中搜索 |11>
|
|
7
|
+
|
|
8
|
+
也可以提供自定义神谕(一个把目标态相位翻转的回调):
|
|
9
|
+
|
|
10
|
+
from quonic.algorithms import grover, mark_state
|
|
11
|
+
result = grover(mark_state("11"), 2, shots=1024)
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import math
|
|
15
|
+
|
|
16
|
+
from ..backends import get_backend
|
|
17
|
+
from ..ir import Circuit, GateOperation
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _add_diffusion(circuit, n):
|
|
21
|
+
for q in range(n):
|
|
22
|
+
circuit.add(GateOperation("h", (q,)))
|
|
23
|
+
for q in range(n):
|
|
24
|
+
circuit.add(GateOperation("x", (q,)))
|
|
25
|
+
_add_phase_flip_all_ones(circuit, n)
|
|
26
|
+
for q in range(n):
|
|
27
|
+
circuit.add(GateOperation("x", (q,)))
|
|
28
|
+
for q in range(n):
|
|
29
|
+
circuit.add(GateOperation("h", (q,)))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _add_phase_flip_all_ones(circuit, n):
|
|
33
|
+
# 对 |11...1> 施加 -1 相位(多控制 Z)
|
|
34
|
+
if n == 1:
|
|
35
|
+
circuit.add(GateOperation("z", (0,)))
|
|
36
|
+
else:
|
|
37
|
+
circuit.add(GateOperation("mcz", tuple(range(n))))
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def mark_state(bitstring):
|
|
41
|
+
"""返回一个神谕回调,标记计算基态 |bitstring>。
|
|
42
|
+
|
|
43
|
+
比特串最右位是 qubit 0(与 qshow 的比特串约定一致)。
|
|
44
|
+
例:mark_state("11") 标记 |11>;mark_state("10") 标记 |10>(qubit0=0, qubit1=1)。
|
|
45
|
+
"""
|
|
46
|
+
bitstring = str(bitstring)
|
|
47
|
+
if not bitstring or any(ch not in "01" for ch in bitstring):
|
|
48
|
+
raise ValueError(f"mark_state 需要只含 0/1 的比特串,收到 {bitstring!r}")
|
|
49
|
+
n = len(bitstring)
|
|
50
|
+
|
|
51
|
+
def oracle(circuit):
|
|
52
|
+
# 把目标态中的 0 位翻成 1,做全 1 相位翻转,再翻回来
|
|
53
|
+
for q in range(n):
|
|
54
|
+
if bitstring[n - 1 - q] == "0":
|
|
55
|
+
circuit.add(GateOperation("x", (q,)))
|
|
56
|
+
_add_phase_flip_all_ones(circuit, n)
|
|
57
|
+
for q in range(n):
|
|
58
|
+
if bitstring[n - 1 - q] == "0":
|
|
59
|
+
circuit.add(GateOperation("x", (q,)))
|
|
60
|
+
|
|
61
|
+
return oracle
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def grover(oracle, n_qubits, iterations=None, backend="auto", shots=1024):
|
|
65
|
+
"""运行 Grover 搜索。
|
|
66
|
+
|
|
67
|
+
参数:
|
|
68
|
+
oracle: 神谕,可以是回调函数 oracle(circuit),也可以直接传比特串
|
|
69
|
+
(如 "11",等价于 mark_state("11"))。
|
|
70
|
+
n_qubits: 量子比特数。
|
|
71
|
+
iterations: 迭代次数,默认 floor(π/4 · √(2^n))。
|
|
72
|
+
backend: 采样后端(qiskit / cirq / pennylane)。
|
|
73
|
+
shots: 采样次数。
|
|
74
|
+
|
|
75
|
+
返回:Result(kind="counts"),result.counts 为采样直方图。
|
|
76
|
+
"""
|
|
77
|
+
if isinstance(oracle, str):
|
|
78
|
+
if len(oracle) != n_qubits:
|
|
79
|
+
raise ValueError(
|
|
80
|
+
f"标记的比特串 '{oracle}' 长度 {len(oracle)} 与量子比特数 {n_qubits} 不一致"
|
|
81
|
+
)
|
|
82
|
+
oracle = mark_state(oracle)
|
|
83
|
+
|
|
84
|
+
circuit = Circuit()
|
|
85
|
+
for q in range(n_qubits):
|
|
86
|
+
circuit.add(GateOperation("h", (q,)))
|
|
87
|
+
|
|
88
|
+
if iterations is None:
|
|
89
|
+
iterations = int(math.pi / 4 * math.sqrt(2 ** n_qubits))
|
|
90
|
+
|
|
91
|
+
for _ in range(iterations):
|
|
92
|
+
oracle(circuit)
|
|
93
|
+
_add_diffusion(circuit, n_qubits)
|
|
94
|
+
|
|
95
|
+
return get_backend(backend).run(circuit, shots=shots)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def diffusion(n_qubits):
|
|
99
|
+
"""把 Grover 扩散算子(2|s><s| - I)追加到当前电路。
|
|
100
|
+
|
|
101
|
+
对 qubit 0..n_qubits-1 施加 H、X、多控制 Z、X、H 的序列,是振幅放大
|
|
102
|
+
(Grover 迭代)的核心一步,可与 qgate / mark_state 组合构建自定义搜索:
|
|
103
|
+
qgate(H, 0); qgate(H, 1); mark_state("11")(current_circuit()); diffusion(2)
|
|
104
|
+
"""
|
|
105
|
+
from ..stack import current_circuit
|
|
106
|
+
|
|
107
|
+
circ = current_circuit()
|
|
108
|
+
_add_diffusion(circ, n_qubits)
|
|
109
|
+
return circ
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""哈密顿量辅助:从外部量子化学库导入泡利哈密顿量。
|
|
2
|
+
|
|
3
|
+
QuoNic 不内置化学数据库;分子的电子结构哈密顿量需要用户用
|
|
4
|
+
Qiskit Nature / OpenFermion 等工具自行生成,再用本模块的适配器
|
|
5
|
+
转成 vqe() 所需的 [(系数, 泡利串), ...] 格式。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def from_qiskit_nature(op):
|
|
10
|
+
"""把 Qiskit Nature(或 Qiskit)的 SparsePauliOp 转成 [(coeff, pauli), ...]。
|
|
11
|
+
|
|
12
|
+
op 需具备 .coeffs 和 .paulis 属性(qiskit.quantum_info.SparsePauliOp 满足)。
|
|
13
|
+
|
|
14
|
+
例(示意):
|
|
15
|
+
from qiskit_nature.second_q.drivers import PySCFDriver
|
|
16
|
+
from qiskit_nature.second_q.mappers import JordanWignerMapper
|
|
17
|
+
# ... 用 PySCFDriver 得到 ElectronicStructureProblem,做 JW 映射 ...
|
|
18
|
+
qubit_op = problem.hamiltonian.second_q_op() # 经 mapper 映射后是 SparsePauliOp
|
|
19
|
+
terms = from_qiskit_nature(qubit_op)
|
|
20
|
+
vqe(terms, n_qubits)
|
|
21
|
+
|
|
22
|
+
说明:
|
|
23
|
+
- 泡利串序已对齐 QuoNic 约定(左起第一个字符 = qubit 0),与 Qiskit 一致,无需反转。
|
|
24
|
+
- 分子哈密顿量在 JW 映射下系数为实数;若出现不可忽略的虚部会报错。
|
|
25
|
+
"""
|
|
26
|
+
terms = []
|
|
27
|
+
for coeff, pauli in zip(op.coeffs, op.paulis):
|
|
28
|
+
if abs(coeff.imag) > 1e-8:
|
|
29
|
+
raise ValueError(
|
|
30
|
+
f"哈密顿量系数 {coeff} 含不可忽略的虚部,当前 VQE 仅支持实系数"
|
|
31
|
+
)
|
|
32
|
+
label = pauli.to_label() if hasattr(pauli, "to_label") else str(pauli)
|
|
33
|
+
terms.append((float(coeff.real), label))
|
|
34
|
+
return terms
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""@oracle 装饰器:把经典谓词编译成 Grover 相位神谕。
|
|
2
|
+
|
|
3
|
+
from quonic.algorithms import grover, oracle
|
|
4
|
+
|
|
5
|
+
@oracle(3)
|
|
6
|
+
def f(x):
|
|
7
|
+
return x == 5 # 标记 |101>(qubit0 在最低位)
|
|
8
|
+
|
|
9
|
+
result = grover(f, 3) # 搜索唯一满足 f 的状态
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from .grover import mark_state
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def oracle(n_qubits):
|
|
16
|
+
"""装饰器:把一个经典谓词 f(x) -> bool 变成 Grover 相位神谕。
|
|
17
|
+
|
|
18
|
+
f 接受整数 x(0 <= x < 2**n_qubits),返回 True 表示标记该状态。
|
|
19
|
+
所有满足 f(x)=True 的状态都会被施加 -1 相位。
|
|
20
|
+
|
|
21
|
+
返回的装饰器把 f 包装成 oracle(circuit) 回调,可直接传给 grover(),
|
|
22
|
+
也可传给 quantum_counting()(后者会读取其 .marked 属性)。
|
|
23
|
+
|
|
24
|
+
参数:
|
|
25
|
+
n_qubits: 谓词作用的量子比特数。
|
|
26
|
+
"""
|
|
27
|
+
if not isinstance(n_qubits, int) or n_qubits < 1:
|
|
28
|
+
raise ValueError(f"n_qubits 必须是正整数,收到 {n_qubits!r}")
|
|
29
|
+
|
|
30
|
+
def decorator(f):
|
|
31
|
+
marked = tuple(x for x in range(2 ** n_qubits) if f(x))
|
|
32
|
+
|
|
33
|
+
def phase_oracle(circuit):
|
|
34
|
+
for x in marked:
|
|
35
|
+
mark_state(format(x, f"0{n_qubits}b"))(circuit)
|
|
36
|
+
|
|
37
|
+
phase_oracle.marked = marked
|
|
38
|
+
phase_oracle.n_qubits = n_qubits
|
|
39
|
+
phase_oracle.__name__ = getattr(f, "__name__", "oracle")
|
|
40
|
+
return phase_oracle
|
|
41
|
+
|
|
42
|
+
return decorator
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""QAOA(量子近似优化算法)模板,求解 MaxCut 问题。
|
|
2
|
+
|
|
3
|
+
给定无向图(边列表),用 p=1 层 QAOA 变分求最大割。
|
|
4
|
+
|
|
5
|
+
示例:三角形图(3 个顶点、3 条边)
|
|
6
|
+
|
|
7
|
+
from quonic.algorithms import qaoa
|
|
8
|
+
|
|
9
|
+
edges = [(0, 1), (1, 2), (0, 2)]
|
|
10
|
+
result = qaoa.qaoa_maxcut(edges, 3)
|
|
11
|
+
print(result["cut"]) # 三角形最大割 = 2
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from ..result import Result
|
|
15
|
+
from ..simulator import StatevectorSimulator
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _pauli_z(i, j, n):
|
|
19
|
+
s = ["I"] * n
|
|
20
|
+
s[i] = "Z"
|
|
21
|
+
s[j] = "Z"
|
|
22
|
+
return "".join(s)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _qaoa_state(n, edges, p, params):
|
|
26
|
+
gammas = params[:p]
|
|
27
|
+
betas = params[p:]
|
|
28
|
+
sim = StatevectorSimulator(n)
|
|
29
|
+
for q in range(n):
|
|
30
|
+
sim.apply("h", (q,))
|
|
31
|
+
for layer in range(p):
|
|
32
|
+
# 代价层:exp(-i γ Z_i Z_j) = CX · Rz(2γ) · CX
|
|
33
|
+
for i, j in edges:
|
|
34
|
+
sim.apply("cx", (i, j))
|
|
35
|
+
sim.apply("rz", (j,), (2 * gammas[layer],))
|
|
36
|
+
sim.apply("cx", (i, j))
|
|
37
|
+
# 混合层:Rx(2β)
|
|
38
|
+
for q in range(n):
|
|
39
|
+
sim.apply("rx", (q,), (2 * betas[layer],))
|
|
40
|
+
return sim
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def qaoa_maxcut(edges, n_qubits, p=1, init_params=None, optimizer="COBYLA", maxiter=300,
|
|
44
|
+
record_history=False):
|
|
45
|
+
"""变分求解给定图的 MaxCut。
|
|
46
|
+
|
|
47
|
+
参数:
|
|
48
|
+
edges: 边列表 [(i, j), ...]。
|
|
49
|
+
n_qubits: 顶点数。
|
|
50
|
+
p: QAOA 层数(默认 1)。
|
|
51
|
+
init_params: 初始参数(长度 2p),默认全 0.1。
|
|
52
|
+
optimizer / maxiter: 传给 scipy.optimize.minimize。
|
|
53
|
+
record_history: 为 True 时把每步能量记录进 metadata["history"],
|
|
54
|
+
供 plot_energy_convergence 画收敛曲线(默认关闭)。
|
|
55
|
+
|
|
56
|
+
返回:Result(kind="value"),result.value 为近似最大割,
|
|
57
|
+
metadata 含 "params"(最优参数)、"energy"(Σ<ZiZj>)与可选 "history"。
|
|
58
|
+
"""
|
|
59
|
+
try:
|
|
60
|
+
from scipy.optimize import minimize
|
|
61
|
+
except ImportError as e:
|
|
62
|
+
raise ImportError(
|
|
63
|
+
"使用 QAOA 需要安装 scipy:\n"
|
|
64
|
+
" pip install 'quonic[algorithms]'\n"
|
|
65
|
+
"或: pip install scipy"
|
|
66
|
+
) from e
|
|
67
|
+
|
|
68
|
+
if init_params is None:
|
|
69
|
+
init_params = [0.1] * (2 * p)
|
|
70
|
+
|
|
71
|
+
def cost(params):
|
|
72
|
+
sim = _qaoa_state(n_qubits, edges, p, params)
|
|
73
|
+
return sum(sim.expectation(_pauli_z(i, j, n_qubits)) for i, j in edges)
|
|
74
|
+
|
|
75
|
+
history = []
|
|
76
|
+
callback = None
|
|
77
|
+
if record_history:
|
|
78
|
+
def callback(xk):
|
|
79
|
+
history.append(float(cost(xk)))
|
|
80
|
+
|
|
81
|
+
result = minimize(
|
|
82
|
+
cost,
|
|
83
|
+
init_params,
|
|
84
|
+
method=optimizer,
|
|
85
|
+
options={"maxiter": maxiter},
|
|
86
|
+
callback=callback,
|
|
87
|
+
)
|
|
88
|
+
energy = float(result.fun)
|
|
89
|
+
cut = (len(edges) - energy) / 2.0
|
|
90
|
+
metadata = {"params": [float(x) for x in result.x], "energy": energy}
|
|
91
|
+
if record_history:
|
|
92
|
+
metadata["history"] = history
|
|
93
|
+
return Result.from_value(cut, **metadata)
|
quonic/algorithms/qpe.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""量子相位估计(QPE)模板。
|
|
2
|
+
|
|
3
|
+
估计单比特门 U = Rz(θ) 作用在 |1> 上的本征相位。
|
|
4
|
+
|
|
5
|
+
Rz(θ)|1> = e^{iθ/2}|1>,所以本征相位 φ = θ/2。
|
|
6
|
+
QPE 用 n 个相位比特估计 j,满足 j/2^n ≈ φ/(2π) = θ/(4π)。
|
|
7
|
+
|
|
8
|
+
示例:
|
|
9
|
+
import math
|
|
10
|
+
from quonic.algorithms import qpe
|
|
11
|
+
|
|
12
|
+
result = qpe(math.pi, n_precision=3, shots=1024)
|
|
13
|
+
# Rz(π)|1> 相位 π/2,φ/(2π)=1/4,j≈2 -> 相位比特 "010"
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
from ..backends import get_backend
|
|
18
|
+
from ..ir import Circuit, GateOperation
|
|
19
|
+
from ..qft import add_iqft
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _add_crz(circuit, c, t, theta):
|
|
23
|
+
# 受控 Rz(theta)(控制 c,目标 t)
|
|
24
|
+
circuit.add(GateOperation("cx", (c, t)))
|
|
25
|
+
circuit.add(GateOperation("rz", (t,), (-theta / 2,)))
|
|
26
|
+
circuit.add(GateOperation("cx", (c, t)))
|
|
27
|
+
circuit.add(GateOperation("rz", (t,), (theta / 2,)))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _add_iqft(circuit, n):
|
|
31
|
+
# 逆量子傅里叶变换(qubit 0 = 最低位),委托给 qft 模块
|
|
32
|
+
add_iqft(circuit, list(range(n)))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def qpe(theta, n_precision, shots=1024, backend="auto"):
|
|
36
|
+
"""估计 Rz(theta) 作用在 |1> 上的本征相位。
|
|
37
|
+
|
|
38
|
+
参数:
|
|
39
|
+
theta: 单比特旋转角(弧度)。
|
|
40
|
+
n_precision: 相位估计的比特数。
|
|
41
|
+
shots / backend: 采样参数。
|
|
42
|
+
|
|
43
|
+
返回:Result(kind="counts")。比特串最右侧 n_precision 位是相位估计,
|
|
44
|
+
其整数值 j 满足 j/2^n ≈ theta/(4π)(n = n_precision)。
|
|
45
|
+
"""
|
|
46
|
+
n = n_precision
|
|
47
|
+
state_qubit = n
|
|
48
|
+
circuit = Circuit()
|
|
49
|
+
circuit.add(GateOperation("x", (state_qubit,)))
|
|
50
|
+
for j in range(n):
|
|
51
|
+
circuit.add(GateOperation("h", (j,)))
|
|
52
|
+
for j in range(n):
|
|
53
|
+
_add_crz(circuit, j, state_qubit, theta * (2 ** (n - 1 - j)))
|
|
54
|
+
_add_iqft(circuit, n)
|
|
55
|
+
return get_backend(backend).run(circuit, shots=shots)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""量子计数(quantum counting)模板。
|
|
2
|
+
|
|
3
|
+
用 Grover 迭代 + 量子相位估计(QPE)估计满足神谕的解的个数 M。
|
|
4
|
+
|
|
5
|
+
原理:Grover 算子 G 在 {|非解>, |解>} 子空间内旋转角度 2θ,其中
|
|
6
|
+
sin²θ = M / N(N = 2^n)。QPE 估计 G 的本征相位 θ/π,从而反解出 M。
|
|
7
|
+
|
|
8
|
+
示例:在 3 比特(N=8)中统计有多少个状态满足谓词
|
|
9
|
+
|
|
10
|
+
from quonic.algorithms import oracle, quantum_counting
|
|
11
|
+
|
|
12
|
+
@oracle(3)
|
|
13
|
+
def f(x):
|
|
14
|
+
return x & 1 == 0 # 偶数:共 4 个解
|
|
15
|
+
|
|
16
|
+
result = quantum_counting(f, 3)
|
|
17
|
+
print(result.value) # 接近 4
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import math
|
|
21
|
+
|
|
22
|
+
from ..backends import get_backend
|
|
23
|
+
from ..ir import Circuit, GateOperation
|
|
24
|
+
from ..result import Result
|
|
25
|
+
from .qpe import _add_iqft
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _marked_states(oracle, n_qubits):
|
|
29
|
+
if isinstance(oracle, str):
|
|
30
|
+
if len(oracle) != n_qubits:
|
|
31
|
+
raise ValueError(
|
|
32
|
+
f"标记比特串 '{oracle}' 长度 {len(oracle)} 与量子比特数 {n_qubits} 不一致"
|
|
33
|
+
)
|
|
34
|
+
return [int(oracle, 2)]
|
|
35
|
+
if hasattr(oracle, "marked"): # @oracle 装饰器产物
|
|
36
|
+
if oracle.n_qubits != n_qubits:
|
|
37
|
+
raise ValueError(
|
|
38
|
+
f"神谕的量子比特数 {oracle.n_qubits} 与 n_qubits={n_qubits} 不一致"
|
|
39
|
+
)
|
|
40
|
+
return list(oracle.marked)
|
|
41
|
+
if callable(oracle): # 裸谓词 f(x) -> bool
|
|
42
|
+
states = [x for x in range(2 ** n_qubits) if oracle(x)]
|
|
43
|
+
if not states:
|
|
44
|
+
raise ValueError("神谕没有标记任何状态,无法计数")
|
|
45
|
+
return states
|
|
46
|
+
raise TypeError("oracle 必须是标记比特串、@oracle 装饰器产物或谓词函数")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _add_controlled_oracle(circuit, control, search, marked):
|
|
50
|
+
n = len(search)
|
|
51
|
+
for x in marked:
|
|
52
|
+
bits = format(x, f"0{n}b")
|
|
53
|
+
for q in range(n):
|
|
54
|
+
if bits[n - 1 - q] == "0":
|
|
55
|
+
circuit.add(GateOperation("x", (search[q],)))
|
|
56
|
+
circuit.add(GateOperation("mcz", tuple([control] + search)))
|
|
57
|
+
for q in range(n):
|
|
58
|
+
if bits[n - 1 - q] == "0":
|
|
59
|
+
circuit.add(GateOperation("x", (search[q],)))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _add_controlled_diffusion(circuit, control, search):
|
|
63
|
+
for q in search:
|
|
64
|
+
circuit.add(GateOperation("h", (q,)))
|
|
65
|
+
for q in search:
|
|
66
|
+
circuit.add(GateOperation("x", (q,)))
|
|
67
|
+
circuit.add(GateOperation("mcz", tuple([control] + search)))
|
|
68
|
+
for q in search:
|
|
69
|
+
circuit.add(GateOperation("x", (q,)))
|
|
70
|
+
for q in search:
|
|
71
|
+
circuit.add(GateOperation("h", (q,)))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _add_controlled_grover(circuit, control, search, marked):
|
|
75
|
+
_add_controlled_oracle(circuit, control, search, marked)
|
|
76
|
+
_add_controlled_diffusion(circuit, control, search)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def quantum_counting(oracle, n_qubits, t=None, backend="auto", shots=1024):
|
|
80
|
+
"""估计满足神谕的解的个数 M。
|
|
81
|
+
|
|
82
|
+
参数:
|
|
83
|
+
oracle: 标记比特串、@oracle 装饰器产物或谓词 f(x)->bool。
|
|
84
|
+
n_qubits: 搜索空间量子比特数(N = 2**n_qubits)。
|
|
85
|
+
t: 计数量子比特数,默认 n_qubits + 1(越大估计越精确)。
|
|
86
|
+
backend / shots: 采样参数。
|
|
87
|
+
|
|
88
|
+
返回:Result(kind="value"),result.value 为 M 的估计值。
|
|
89
|
+
"""
|
|
90
|
+
marked = _marked_states(oracle, n_qubits)
|
|
91
|
+
if t is None:
|
|
92
|
+
t = n_qubits + 1
|
|
93
|
+
|
|
94
|
+
search = list(range(t, t + n_qubits))
|
|
95
|
+
circuit = Circuit()
|
|
96
|
+
for q in range(t + n_qubits):
|
|
97
|
+
circuit.add(GateOperation("h", (q,)))
|
|
98
|
+
|
|
99
|
+
for j in range(t):
|
|
100
|
+
for _ in range(2 ** (t - 1 - j)):
|
|
101
|
+
_add_controlled_grover(circuit, j, search, marked)
|
|
102
|
+
|
|
103
|
+
_add_iqft(circuit, t)
|
|
104
|
+
|
|
105
|
+
result = get_backend(backend).run(circuit, shots=shots)
|
|
106
|
+
best = max(result.counts, key=result.counts.get)
|
|
107
|
+
j = int(best[-t:], 2) # 最右侧 t 位是计数比特
|
|
108
|
+
m = 2 ** n_qubits * math.sin(math.pi * abs(j / 2 ** t - 0.5)) ** 2
|
|
109
|
+
return Result.from_value(m, j=j, t=t, counts=result.counts)
|