Skip to content

【代码贡献】Add quantum counting (BHT) built on the existing Grover operator - #47

Open
mnn31 wants to merge 1 commit into
OriginQ:developfrom
mnn31:feature/quantum-counting
Open

【代码贡献】Add quantum counting (BHT) built on the existing Grover operator#47
mnn31 wants to merge 1 commit into
OriginQ:developfrom
mnn31:feature/quantum-counting

Conversation

@mnn31

@mnn31 mnn31 commented Aug 10, 2026

Copy link
Copy Markdown

Adds the Brassard-Hoyer-Tapp quantum counting algorithm as a new module,
pyqpanda_alg.QCounting. It estimates how many items of a search space of
N = 2 ** qnumber are marked by an oracle.

The docstring of Grover.amp_operator already says the operator "Can be part of
Grover/Quantum Count/QAE and other amplitude amplification related algorithm",
and the Grover and QAE parts are in the library while the counting part is not.
This fills that slot and reuses what is already there: the amplitude
amplification operator comes from Grover.amp_operator, the oracle from
Grover.mark_data_reflection when the marked states are known, and the readout
from plugin.QFT. Nothing is duplicated.

How it works. The counting register is put in superposition, the controlled
powers G ** (2 ** k) of the Grover operator are applied, and the inverse QFT
reads the eigenphase back. The eigenphases theta of G satisfy
sin(theta / 2) ** 2 = M / N, so the measured counting value y gives
M = N * sin(pi * y / 2 ** t) ** 2. G has the two eigenphases theta and
2 * pi - theta, and that formula is the same for y and 2 ** t - y, so the two
readout peaks give the same count and the peak does not need to be
disambiguated.

One implementation detail worth flagging: amp_operator returns the product of
the two reflections without the overall minus sign of the Grover operator. The
sign is unobservable in Grover search but not in phase estimation, where the
operator is controlled, so the module appends RX(q, 2 * pi) to restore it.
That is the same trick QAE._Q_cir already uses.

API:

QCounting(qnumber, flip_operator=None, mark_data=None,
          counting_qubits=None, ancilla_qubits=0)
    .run()                              # -> float, the estimated count
    .cir(q_search, q_count, q_ancilla)  # -> QCircuit, the full counting circuit
    .theta                              # estimated eigenphase after run()

The oracle is given either as mark_data, the marked bit strings, or as a phase
flip circuit through flip_operator, with ancilla_qubits workspace qubits if
it needs them. counting_qubits defaults to qnumber + 2, which is the smallest
register for which the error bound pi * N / 2 ** (counting_qubits + 1) equals
pi / 8 and therefore stays below 1/2, so rounding the result returns M. More
counting qubits sharpen the unrounded estimate.

Validation. Every count from 0 to N was checked for qnumber = 2 and 3, plus
spot checks at qnumber = 4, all at the default resolution:

qnumber M true counting qubits estimate rounded
2 0 4 0.0000 0
2 1 4 1.2346 1
2 2 4 2.0000 2
2 3 4 2.7654 3
2 4 4 4.0000 4
3 0 5 0.0000 0
3 1 5 1.1716 1
3 2 5 1.7777 2
3 3 5 3.2196 3
3 4 5 4.0000 4
3 5 5 4.7804 5
3 6 5 6.2223 6
3 7 5 6.8284 7
3 8 5 8.0000 8
4 1 6 0.9446 1
4 5 6 4.9385 5
4 11 6 11.0615 11
4 16 6 16.0000 16

A wider sweep of 150 cases (qnumber 2 to 4, every M, several random subsets per
M, counting registers of qnumber + 2 and qnumber + 3) rounded to the true count
in every case. M = 0 and M = N are not degenerate here: they are the exact
eigenvalues +1 and -1 of G, the counting register lands on y = 0 and
y = 2 ** (t - 1), and the estimates come out as exactly 0 and exactly N.

Tests in Test_QCounting_QCounting.py: 17 tests covering the sweeps above, the
scattered (non contiguous) marked sets, the exact M = 0 and M = N cases, a
flip_operator oracle with a workspace qubit, the relation between theta and
the returned count, the resolution behaviour, and input validation. They are
deterministic because the result is read from get_prob_dict. The whole test/
suite is green (35 passed, 17 of them new). There are doctests on the class,
cir and run, and they run clean.

example/QAlgBase/testeg_QCounting.py runs end to end in about 1.4 s and shows
both oracle styles plus the effect of the counting register size.

One limitation: runtime is dominated by the 2 ** counting_qubits - 1 copies of
the Grover operator, so this is meant for small search spaces, same as the other
textbook algorithms in the library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant