Skip to content

【代码贡献】Fix qubit_comparator eq and neq modes with unequal register widths - #93

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

mnn31 wants to merge 1 commit into
OriginQ:developfrom
mnn31:fix/qubit-comparator-eq-neq

Conversation

@mnn31

@mnn31 mnn31 commented Sep 21, 2026

Copy link
Copy Markdown

Problem

QCmp.qubit_comparator returns inverted results for function='eq' and
function='neq' whenever q_state_1 has more qubits than q_state_2. The
four inequality modes are not affected by this bug; only the two symmetric
modes break, and only when the first register is the wider one.

Repro, q_state_1 in uniform superposition over 0..7 and q_state_2 holding 2,
once with a 3-qubit second register and once with a 2-qubit one:

prog = QProg()
prog << H(0) << H(1) << H(2) << X(4)
prog << QCmp.qubit_comparator([0, 1, 2], [3, 4], [5, 6], function='eq')
function n1=3, n2=3 n1=3, n2=2 expected
g 0.625 0.625 0.625
geq 0.750 0.750 0.750
s 0.250 0.250 0.250
seq 0.375 0.375 0.375
eq 0.125 0.875 0.125
neq 0.875 0.125 0.875

Padding q_state_2 out to the width of q_state_1 gives the right answer, so
the two columns should agree. eq and neq are exactly swapped.

Root cause

pyqpanda-algorithm/pyqpanda_alg/QCmp/QCmp.py, qubit_comparator, lines
357-360 before this change.

The function normalises the two registers so that the shorter one is the one it
walks over. When len(q_state_1) > len(q_state_2) it swaps them and
compensates with a negation:

if len(q_state_1) > len(q_state_2):
    q_state = q_state_2
    q_ctr = q_state_1
    circuit << X(q_cmp)

Swapping the operands of a comparison turns a > b into b > a, which is the
complement of a >= b, so the negation plus the flag reassignment on the next
lines is what keeps g, geq, s and seq correct. But eq and neq are
symmetric: a == b and b == a are the same predicate, so nothing needs
compensating and the X(q_cmp) is a straight inversion of the answer. The
swap branch applies it unconditionally.

Fix

Skip the negation for the two symmetric modes:

if len(q_state_1) > len(q_state_2):
    q_state = q_state_2
    q_ctr = q_state_1
    # swapping the operands flips the inequality, but not eq/neq
    if function != 'eq' and function != 'neq':
        circuit << X(q_cmp)

Three lines, no gate-count change for any mode that was already correct, and one
gate fewer for eq/neq on the swap path. The non-swap branch is untouched, so
every equal-width and n1 < n2 call produces an identical circuit to
before, including the docstring example and demo14-comparator-qubit_comparator.ipynb
(both equal-width, function='g').

Verification

Exhaustive computational-basis sweep, asserting the comparison qubit against the
classical predicate for every (a, b) pair, over 6 modes and 7 register shapes
with both widths at least 2 (2400 pairs in total):

n1, n2 g geq s seq eq neq
2, 2 16/16 16/16 16/16 16/16 16/16 16/16
3, 3 64/64 64/64 64/64 64/64 64/64 64/64
2, 3 32/32 32/32 32/32 32/32 32/32 32/32
2, 4 64/64 64/64 64/64 64/64 64/64 64/64
3, 2 32/32 32/32 32/32 32/32 0/32 0/32
4, 2 64/64 64/64 64/64 64/64 0/64 0/64
4, 3 128/128 128/128 128/128 128/128 0/128 0/128

Before the fix: 1952/2400 pass, and the 448 failures are exactly the six
bolded cells, i.e. every eq/neq case with n1 > n2 fails, none of them
partially. After the fix: 2400/2400 pass, every cell full.

Out of scope here: when either register is a single qubit and the widths differ
(shapes like (1,2) or (3,1)), all six modes already return some wrong flags on
develop, because the loop over the narrow register never consumes the extra
high bits of the wide one. That is a separate defect with a different cause. This
change does not touch it, and the sweep above uses widths of 2 and up.

Tests

test/QAlgBase/Test_comparator_qubit_comparator.py existed but every line was
commented out, so nothing covered this function. Un-commented it, brought it up
to the current pyqpanda3 API, and extended it to 50 tests: the docstring
example, all six modes on the superposition case with exact expected
probabilities, the a == b boundary at equal and unequal width, and exhaustive
basis-pair sweeps at shapes (2,2), (3,3), (3,2), (2,3) and (4,2) for all six
modes, plus the NameError on an unknown function.

Against the unpatched code the new module reports 6 failed, 44 passed; the six
failures are the eq and neq parametrisations of the three first-register-wider
tests. Against the patched code all 50 pass.

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

One note on the test module: pyqpanda3 raises qubits_list arg error from
get_prob_dict when a qubit in the program was never acted on, which happens on
the short circuits some of these modes build, so the helper applies an I to
every qubit in the register before measuring.

#42 also touches this file but a different function (qft_qubit_comparator),
and the two branches merge cleanly with no conflict.

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