Skip to content

Fix Jasp quantum-quantum QuantumModulus multiplication in QuantumArray - #710

Open
alighazi288 wants to merge 1 commit into
eclipse-qrisp:mainfrom
alighazi288:fix/jasp-quantum-array-modulus-mul
Open

Fix Jasp quantum-quantum QuantumModulus multiplication in QuantumArray#710
alighazi288 wants to merge 1 commit into
eclipse-qrisp:mainfrom
alighazi288:fix/jasp-quantum-array-modulus-mul

Conversation

@alighazi288

Copy link
Copy Markdown
Contributor

Description

Fixes element-wise quantum-quantum multiplication of QuantumArrays of QuantumModulus in dynamic (Jasp) mode, which returned a value scaled by a power of two. e.g. 1 * 5 mod 7 gave 3 instead of 5.

Root cause: qq_montgomery_multiply_modulus computed the Montgomery reduction shift as smallest_power_of_two((N-1)**2 + 1) - n, whereas the static montgomery_mod_mul and create_output_qf use ceil(log2((N-1)**2) + 1) - n. Since ceil(log2(k²+1)) ≠ ceil(log2(k²)) + 1, the jasp result's res.m came out one too small for many moduli (7, 11, 13, …). A single QuantumModulus product returns that result directly and still decodes correctly, but the QuantumArray element-wise path preallocates the output's Montgomery shift via create_output_qf, so the injected result was decoded against a mismatching shift and came out off by . The fix aligns the jasp shift with the static formula (JAX/BigInteger-safe — the + 1 moves outside smallest_power_of_two).

Related Issues

Closes #640

Type of Change

  • Bug Fix

Breaking Change?

  • No

What was changed?

  • Corrected the Montgomery reduction shift in qq_montgomery_multiply_modulus (smallest_power_of_two((N-1)**2 + 1) - nsmallest_power_of_two((N-1)**2) + 1 - n) so it matches the static montgomery_mod_mul / create_output_qf convention, with a comment documenting the invariant.
  • Removed the pytest.skip guarding the QuantumModulus quantum-quantum cases in test_quantum_array_element_wise_ops_qm, so they now run.

How was it tested?

Test-ID Description Status
T-001 (Positive) a * b element-wise on QuantumArray[QuantumModulus] returns (A*B) % N - issue repro (1*5 mod 7 = 5) + random 2×2/3×2 arrays over N ∈ {7,11,13,15,23,31,47}
T-002 (Negative) Quantum-classical mul on a QuantumArray of non-QuantumModulus still raises NotImplementedError (unchanged)
T-003 (Boundary) Standard-form (m=0) and pre-shifted (m≠0) single-QuantumModulus products remain correct; smallest modulus exercised via scalar-RHS quantum case (N=3)
T-004 (Boundary) Chained (a*b)*c and non-square arrays across several moduli

Local suites green: test_jasp_quantum_array.py (113), test_jasp_modulus.py (56, incl. the qq standard-form / non-standard-m / traced-BigInteger regressions), test_montgomery.py. Also confirmed the corrected shift equals the static formula for every odd modulus 3–63.

Checklist

  • My code follows the project's coding standards
  • I have performed a self-review
  • I have added/updated tests (referencing issue Test-IDs)
  • All tests pass locally and in CI
  • I have updated the documentation
  • I have added a changelog entry
  • Breaking changes are documented with migration path

Reviewer Notes

The previously-skipped test_quantum_array_element_wise_ops_qm (mul, quantum RHS) is now un-skipped and serves as the regression guard (its N=7 array case reproduces the bug).

Design note: this aligns the jasp shift with the existing canonical formula, which makes it one larger for affected moduli so qq_montgomery_multiply_modulus uses ~1 extra ancilla qubit, matching what the static path already allocates. I chose this fix over the qubit-optimal alternative (keep the tighter jasp shift and instead have the injection carry the actual res.m), which would touch the well-tested create_output_qf/injection machinery. Happy to go the other route if you'd prefer.

Element-wise quantum-quantum multiplication of QuantumArrays of
QuantumModulus returned a value scaled by a power of two in dynamic
(Jasp) mode, e.g. 1 * 5 mod 7 gave 3 instead of 5 (see eclipse-qrisp#640).

qq_montgomery_multiply_modulus computed the Montgomery reduction shift as
smallest_power_of_two((N-1)^2 + 1) - n, whereas the static
montgomery_mod_mul and create_output_qf use ceil(log2((N-1)^2) + 1) - n.
Because ceil(log2(k^2 + 1)) differs from ceil(log2(k^2)) + 1, the jasp
result's res.m came out one too small for many moduli (e.g. 7, 11, 13).
A single QuantumModulus product returns that result directly and still
decodes correctly, but the QuantumArray element-wise path preallocates
the output's Montgomery shift via create_output_qf, so the injected
result was decoded against a mismatching shift and came out off by 2^1.

Align the jasp shift with the static formula (JAX/BigInteger-safe: the
"+ 1" moves outside smallest_power_of_two) and unskip the existing
QuantumModulus quantum-quantum multiplication test.

Closes eclipse-qrisp#640

Signed-off-by: Syed Ali Ghazi <alighazi0609@gmail.com>
@renezander90
renezander90 requested a review from diehoq July 7, 2026 09:28
@renezander90 renezander90 added this to the v0.10 milestone Jul 7, 2026
@renezander90
renezander90 self-requested a review July 7, 2026 10:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

QuantumArray: quatum-quantum multiplication for QuantumModuli

2 participants