Skip to content

【代码贡献】Fix qft_qubit_comparator geq and s modes returning wrong results - #42

Open
mnn31 wants to merge 1 commit into
OriginQ:developfrom
mnn31:fix/qft-qubit-comparator
Open

【代码贡献】Fix qft_qubit_comparator geq and s modes returning wrong results#42
mnn31 wants to merge 1 commit into
OriginQ:developfrom
mnn31:fix/qft-qubit-comparator

Conversation

@mnn31

@mnn31 mnn31 commented Aug 9, 2026

Copy link
Copy Markdown

Problem

QCmp.qft_qubit_comparator builds the same circuit for function='geq' as for
'g', and the same circuit for 's' as for 'seq'. The two strict/non-strict
pairs are indistinguishable, so two of the four documented modes are wrong
whenever the two registers can be equal.

Repro, q_state_1 in uniform superposition over 0..3 and q_state_2 = |2>:

prog = QProg()
prog << H(0) << H(1) << X(3)
prog << QCmp.qft_qubit_comparator([0, 1], [2, 3], [4], function='geq')
function qft_qubit_comparator qubit_comparator qft_comparator expected
g 0.25 0.25 0.25 0.25
geq 0.25 0.50 0.50 0.50
s 0.75 0.50 0.50 0.50
seq 0.75 0.75 0.75 0.75

qubit_comparator and qft_comparator agree with each other on the same case;
qft_qubit_comparator disagrees on geq and s.

Root cause

pyqpanda-algorithm/pyqpanda_alg/QCmp/QCmp.py, qft_qubit_comparator
(lines 512-592 before this change).

Both QFT comparators work the same way: subtract the compared value from the
q_state + q_cmp register, then add it back into q_state alone, which leaves
q_cmp holding the borrow bit and restores the state register. The borrow bit
is a strict comparison, so the non-strict mode has to shift the subtracted value
by one.

qft_comparator does that at line 480:

if function == 'seq' or function == 'g':
    value += 1

qft_qubit_comparator has no equivalent. Its subtracted value comes from
controlled U1 phases with q_state_1 as controls, so there is nowhere
for a value += 1 to go, and the constant term was simply never added. The
borrow bit therefore always evaluates q_state_1 > q_state_2, and the only
thing function still controls is the final X(q_cmp), giving g == geq and
s == seq.

Fix

Add the constant 1 to the same QFT-adder phase construction, for the two modes
that need it. In the q_state_2 + q_cmp block it goes in with -factor_all, in
the q_state_2 restore block with +factor_remain, so the state register still
comes back unchanged:

if function == 'geq' or function == 's':
    offset = 1
else:
    offset = 0
...
if offset:
    for j, qj in enumerate(qlist):
        cir << U1(qj, -factor_all * 2 ** j * offset)
...
if offset:
    for j, qj in enumerate(q_state_2):
        cir << U1(qj, factor_remain * 2 ** j * offset)

Uncontrolled gates, so the cost is 2n + 1 extra U1s and nothing at
all for g and seq. g and seq produce byte-identical circuits to before,
so the docstring example and demo16-comparator-qft_qubit_comparator.ipynb
(both function='g') are unaffected.

Verification

The repro table above now reads 0.25 / 0.50 / 0.50 / 0.75, matching
qubit_comparator and qft_comparator.

Exhaustive check over computational-basis pairs, asserting the comparison qubit
against the classical predicate for every (a, b):

  • 2-qubit registers, 4 modes x 16 pairs: 64/64 pass (8 failed before the fix,
    all of them a == b under geq or s)
  • 3-qubit registers, 4 modes x 64 pairs: 256/256 pass
  • 4-qubit registers, 4 modes x 256 pairs: 1024/1024 pass

Also checked that q_state_1 and q_state_2 are returned unchanged: feeding a
superposition through the geq circuit leaves the joint distribution over the
state qubits identical to the input.

Tests

test/QAlgBase/Test_comparator_qft_qubit_comparator.py existed but every line
was commented out, so nothing covered this. Un-commented it, brought it up to
the current pyqpanda3 API, and extended it to 19 tests: the docstring example,
all four modes on the superposition case with exact expected probabilities, the
a == b boundary that separates g from geq and s from seq, exhaustive
basis-pair sweeps at 2 and 3 qubits, register restoration, and the NameError
on an unknown function.

cd test && python -m pytest -o addopts="" -q
# 36 passed  (17 before, 19 new)

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