Skip to content

Render signed PHIR registers as two's-complement bits instead of a sign prefix#339

Merged
ciaranra merged 16 commits into
devfrom
binary-str-sign-fix
Jul 10, 2026
Merged

Render signed PHIR registers as two's-complement bits instead of a sign prefix#339
ciaranra merged 16 commits into
devfrom
binary-str-sign-fix

Conversation

@ciaranra

@ciaranra ciaranra commented Jul 7, 2026

Copy link
Copy Markdown
Member

Problem

When a HybridEngine + PHIR program produced a negative value in a signed
register (a sign bit that flipped), the returned bit string used Python's
sign-and-magnitude form — a leading "-" for negatives, "" for positives.
So a full-width signed register printed a "-" plus magnitude bits instead of
a fixed-width two's-complement string.

eng = HybridEngine(qsim="stabilizer")
results = eng.run(program=phir, shots=2)   # i32 register holding 0 - 1
print(results["out"][0])
# before: -00000000000000000000000000000001
# after:  11111111111111111111111111111111

Fix

Render the raw two's-complement bit pattern in the results() binary-string
path: mask the value to size bits and format that. The sign bit now shows up
as a "1"/"0" like every other bit — never a "-" prefix — and the width is
stable regardless of sign.

Applied consistently to both interpreters:

  • phir_classical_interpreter.py (results()) — the default Python path
  • classical_interpreter.rs (results()) — the Rust interpreter twin, kept in parity

Width stays tied to the declared register size. Negatives only occur when
size == the backing type width (e.g. i32 size-32, i64 size-64), so that
case already prints the full 32/64 two's-complement bits with the sign bit as
the MSB, while a narrow signed register (e.g. an i64 size-1 measurement bit)
still prints exactly one bit.

The general-purpose dtype __format__ in dtypes.rs is intentionally left
alone — it mimics Python int sign-magnitude semantics for direct
format(i32(-1), 'b') calls and other callers rely on it. This change only
touches the register result-string path.

Verification

  • Reproduced the - bug end-to-end, then confirmed fixed across i8/i32/i64/u32,
    narrow and full-width registers.
  • Python and Rust interpreters produce identical strings (parity).
  • Added a regression test on each side.
  • Updated test_phir_64_bit.py, which had been asserting the old
    sign-magnitude round-trip.
  • Full local sweep: 717 Python passed, 86 Rust passed; lint + clippy clean.

@ciaranra ciaranra force-pushed the binary-str-sign-fix branch from 397a057 to b70a28c Compare July 10, 2026 01:11
@ciaranra ciaranra merged commit b92041b into dev Jul 10, 2026
112 of 113 checks passed
@ciaranra ciaranra deleted the binary-str-sign-fix branch July 10, 2026 05:06
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