Skip to main content

oxicuda_quantum/algorithm/
shor.rs

1//! Shor's period-finding subroutine.
2//!
3//! The quantum heart of Shor's factoring algorithm is *order finding*: given a
4//! base `a` coprime to `N`, find the multiplicative order `r` — the least
5//! positive integer with `a^r ≡ 1 (mod N)`. Quantum phase estimation on the
6//! modular-multiplication unitary `U_a|y⟩ = |a·y mod N⟩` yields a phase
7//! `φ ≈ k/r`, and the classical *continued-fraction* expansion of `φ` recovers
8//! the denominator `r`. Once `r` is known (and even, with `a^{r/2} ≢ −1`), the
9//! factors of `N` are `gcd(a^{r/2} ± 1, N)`.
10//!
11//! This module provides:
12//! * [`mod_exp`] — fast modular exponentiation by repeated squaring.
13//! * [`gcd`] — binary Euclidean greatest common divisor.
14//! * [`continued_fraction_convergents`] — the convergents `p_i/q_i` of a real.
15//! * [`order_finding`] — state-vector phase estimation of `U_a` plus classical
16//!   continued-fraction post-processing.
17//! * [`shor_factor`] — the end-to-end reduction from `r` to a non-trivial factor
18//!   of `N`.
19//!
20//! The counting register is **little-endian** (qubit `i` carries weight `2^i`),
21//! matching [`crate::fourier::qft`].
22
23use crate::error::{QuantumError, QuantumResult};
24use crate::fourier::qft::qft_inverse_inplace;
25use crate::gates::hadamard::gate_h;
26use crate::statevec::apply_1q::apply_1q_inplace;
27use crate::statevec::state::StateVector;
28
29/// Fast modular exponentiation `base^exp mod modulus` via repeated squaring.
30///
31/// Uses `u128` intermediates so products of two `u64`-range operands cannot
32/// overflow. Returns `0` for `modulus == 0` (degenerate) and handles
33/// `modulus == 1` (everything is `0`).
34#[must_use]
35pub fn mod_exp(base: u64, exp: u64, modulus: u64) -> u64 {
36    if modulus <= 1 {
37        return 0;
38    }
39    let mut result: u128 = 1;
40    let mut b = (base % modulus) as u128;
41    let m = modulus as u128;
42    let mut e = exp;
43    while e > 0 {
44        if e & 1 == 1 {
45            result = (result * b) % m;
46        }
47        b = (b * b) % m;
48        e >>= 1;
49    }
50    result as u64
51}
52
53/// Binary-Euclidean greatest common divisor of `a` and `b`.
54#[must_use]
55pub fn gcd(mut a: u64, mut b: u64) -> u64 {
56    if a == 0 {
57        return b;
58    }
59    if b == 0 {
60        return a;
61    }
62    let shift = (a | b).trailing_zeros();
63    a >>= a.trailing_zeros();
64    loop {
65        b >>= b.trailing_zeros();
66        if a > b {
67            std::mem::swap(&mut a, &mut b);
68        }
69        b -= a;
70        if b == 0 {
71            break;
72        }
73    }
74    a << shift
75}
76
77/// Compute the continued-fraction convergents `(p_i, q_i)` of `value ∈ [0, 1)`,
78/// stopping once a denominator exceeds `max_denominator` or the expansion
79/// terminates.
80///
81/// Each returned pair satisfies `p_i / q_i ≈ value`, with successively better
82/// rational approximations. The denominators are the candidate periods in
83/// Shor's post-processing.
84#[must_use]
85pub fn continued_fraction_convergents(value: f64, max_denominator: u64) -> Vec<(u64, u64)> {
86    let mut convergents = Vec::new();
87    let mut x = value;
88    // Convergent recurrence h_n = a_n·h_{n-1} + h_{n-2}, k_n = a_n·k_{n-1} + k_{n-2}
89    // seeded with h_{-1}=1, h_{-2}=0 and k_{-1}=0, k_{-2}=1.
90    let mut p_minus2: i128 = 0; // h_{-2}
91    let mut p_minus1: i128 = 1; // h_{-1}
92    let mut q_minus2: i128 = 1; // k_{-2}
93    let mut q_minus1: i128 = 0; // k_{-1}
94    for _ in 0..64 {
95        let a = x.floor();
96        if !a.is_finite() {
97            break;
98        }
99        let a_i = a as i128;
100        let p_next = a_i * p_minus1 + p_minus2;
101        let q_next = a_i * q_minus1 + q_minus2;
102        if q_next <= 0 || q_next as u64 > max_denominator {
103            break;
104        }
105        convergents.push((p_next.max(0) as u64, q_next as u64));
106        p_minus2 = p_minus1;
107        p_minus1 = p_next;
108        q_minus2 = q_minus1;
109        q_minus1 = q_next;
110        let frac = x - a;
111        if frac.abs() < 1e-12 {
112            break;
113        }
114        x = 1.0 / frac;
115    }
116    convergents
117}
118
119/// Outcome of [`order_finding`].
120#[derive(Debug, Clone)]
121pub struct OrderFindingResult {
122    /// The recovered period `r` such that `a^r ≡ 1 (mod N)` (or `0` if not found).
123    pub order: u64,
124    /// The raw phase `φ ≈ k/r ∈ [0, 1)` read from the counting register.
125    pub phase: f64,
126    /// The integer measured in the counting register (argmax readout).
127    pub measured: usize,
128    /// The number of counting qubits used.
129    pub counting_qubits: usize,
130}
131
132/// Brute-force the multiplicative order of `a` modulo `n` (classical reference).
133///
134/// Returns the least `r ≥ 1` with `a^r ≡ 1 (mod n)`, or `0` if `gcd(a, n) ≠ 1`
135/// or no order is found within `n` steps.
136#[must_use]
137pub fn classical_order(a: u64, n: u64) -> u64 {
138    if n <= 1 || gcd(a, n) != 1 {
139        return 0;
140    }
141    let mut cur = a % n;
142    let mut r = 1u64;
143    while cur != 1 {
144        cur = (cur as u128 * (a % n) as u128 % n as u128) as u64;
145        r += 1;
146        if r > n {
147            return 0;
148        }
149    }
150    r
151}
152
153/// Quantum order finding for the unitary `U_a|y⟩ = |a·y mod N⟩`.
154///
155/// Runs phase estimation with `counting_qubits` in the counting register and a
156/// `⌈log2 N⌉`-qubit work register initialised to `|1⟩`. The eigenphases of `U_a`
157/// are `k/r`, so the argmax readout `φ ≈ k/r` is fed through
158/// [`continued_fraction_convergents`] to extract the period `r` (verified by
159/// `a^r ≡ 1`). The modular-multiplication ladder is applied **exactly** on the
160/// amplitude array (controlled `U_a^{2^j}` = multiply by `a^{2^j} mod N`), so no
161/// gate-level modular-arithmetic synthesis is required.
162///
163/// # Errors
164/// * [`QuantumError::InvalidParameter`] if `modulus < 2`, `base % modulus` is
165///   not coprime to `modulus`, or the registers exceed 30 qubits combined.
166/// * [`QuantumError::InvalidQubitCount`] if `counting_qubits == 0`.
167pub fn order_finding(
168    base: u64,
169    modulus: u64,
170    counting_qubits: usize,
171) -> QuantumResult<OrderFindingResult> {
172    if modulus < 2 {
173        return Err(QuantumError::InvalidParameter {
174            name: format!("modulus {modulus} must be ≥ 2"),
175        });
176    }
177    if counting_qubits == 0 {
178        return Err(QuantumError::InvalidQubitCount { n: 0 });
179    }
180    let a = base % modulus;
181    if gcd(a, modulus) != 1 {
182        return Err(QuantumError::InvalidParameter {
183            name: format!("base {base} not coprime to modulus {modulus}"),
184        });
185    }
186    // Work register must index 0..modulus.
187    let work_qubits = {
188        let mut bits = 0usize;
189        let mut cap = 1u64;
190        while cap < modulus {
191            cap <<= 1;
192            bits += 1;
193        }
194        bits.max(1)
195    };
196    let total = counting_qubits + work_qubits;
197    if total > 30 {
198        return Err(QuantumError::InvalidParameter {
199            name: format!("total qubits {total} exceeds 30"),
200        });
201    }
202
203    let mut sv = StateVector::new_zero_state(total)?;
204    // Initialise work register (qubits counting..total) to |1⟩.
205    let work_one = 1usize << counting_qubits;
206    sv.amps[0] = num_complex::Complex::new(0.0, 0.0);
207    sv.amps[work_one] = num_complex::Complex::new(1.0, 0.0);
208
209    // Hadamard the counting register (qubits 0..counting_qubits).
210    for q in 0..counting_qubits {
211        apply_1q_inplace(&mut sv, q, &gate_h())?;
212    }
213
214    // Controlled-U_a^{2^j}: for each counting qubit j, if it is set, multiply the
215    // work register value by a^{2^j} mod N. Done exactly via amplitude permutation.
216    let count_mask = (1usize << counting_qubits) - 1;
217    for j in 0..counting_qubits {
218        let power = mod_exp(a, 1u64 << j, modulus);
219        if power == 1 {
220            continue; // identity permutation
221        }
222        let ctrl_bit = 1usize << j;
223        let dim = 1usize << total;
224        let mut new_amps = vec![num_complex::Complex::<f32>::new(0.0, 0.0); dim];
225        for (idx, amp) in sv.amps.iter().enumerate() {
226            if amp.norm_sqr() == 0.0 {
227                continue;
228            }
229            if idx & ctrl_bit == 0 {
230                new_amps[idx] += *amp;
231                continue;
232            }
233            let work_val = (idx >> counting_qubits) as u64;
234            let count_part = idx & count_mask;
235            let new_work = if work_val < modulus {
236                (work_val as u128 * power as u128 % modulus as u128) as usize
237            } else {
238                work_val as usize // values ≥ N are inert (never populated)
239            };
240            let target = count_part | (new_work << counting_qubits);
241            new_amps[target] += *amp;
242        }
243        sv.amps = new_amps;
244    }
245
246    // Inverse QFT on the counting register to read the phase.
247    let counting_indices: Vec<usize> = (0..counting_qubits).collect();
248    qft_inverse_inplace(&mut sv, &counting_indices)?;
249
250    // Marginalise the work register to obtain the counting-register distribution.
251    let dim_count = 1usize << counting_qubits;
252    let mut marg = vec![0.0f32; dim_count];
253    for (idx, amp) in sv.amps.iter().enumerate() {
254        marg[idx & count_mask] += amp.norm_sqr();
255    }
256
257    // The textbook readout argmax: the single most probable basis index.
258    let mut measured = 0usize;
259    let mut best = f32::NEG_INFINITY;
260    for (i, &p) in marg.iter().enumerate() {
261        if p > best {
262            best = p;
263            measured = i;
264        }
265    }
266    let phase = measured as f64 / dim_count as f64;
267
268    // Continued-fraction post-processing. A single QPE shot returns a uniformly
269    // random peak `≈ k·2ⁿ/r`; the useless `k = 0` peak (and any peak whose
270    // convergents fail the `a^q ≡ 1` check) carries no period information. To
271    // make the routine deterministic we scan the significant peaks in order of
272    // decreasing probability and accept the first whose denominator is a genuine
273    // order — exactly the value a repeated run would eventually sample.
274    let threshold = 0.5f32 / dim_count as f32; // ignore numerical dust
275    let mut peaks: Vec<(usize, f32)> = marg
276        .iter()
277        .enumerate()
278        .filter(|&(_, &p)| p > threshold)
279        .map(|(i, &p)| (i, p))
280        .collect();
281    peaks.sort_by(|a, b| {
282        b.1.partial_cmp(&a.1)
283            .unwrap_or(std::cmp::Ordering::Equal)
284            .then(a.0.cmp(&b.0))
285    });
286
287    let mut order = 0u64;
288    'scan: for &(idx, _) in &peaks {
289        if idx == 0 {
290            continue; // k = 0 ⇒ φ = 0 carries no period
291        }
292        let phi = idx as f64 / dim_count as f64;
293        for (_, q) in continued_fraction_convergents(phi, modulus) {
294            if q == 0 {
295                continue;
296            }
297            if mod_exp(a, q, modulus) == 1 {
298                order = q;
299                break 'scan;
300            }
301        }
302    }
303
304    Ok(OrderFindingResult {
305        order,
306        phase,
307        measured,
308        counting_qubits,
309    })
310}
311
312/// Outcome of [`shor_factor`].
313#[derive(Debug, Clone)]
314pub struct ShorResult {
315    /// A non-trivial factor of `N` (with `1 < factor < N`), if one was found.
316    pub factor: Option<u64>,
317    /// The base `a` that succeeded (or the last one tried).
318    pub base: u64,
319    /// The order `r` of `a` modulo `N` that produced the factor.
320    pub order: u64,
321}
322
323/// Classical reduction from a period `r` (order of `base` mod `n`) to a factor.
324///
325/// Implements the textbook step: if `r` is even and `a^{r/2} ≢ ±1 (mod n)`, then
326/// `gcd(a^{r/2} − 1, n)` and `gcd(a^{r/2} + 1, n)` are non-trivial factors.
327/// Returns the first non-trivial factor found, else `None`.
328#[must_use]
329pub fn factor_from_order(base: u64, n: u64, r: u64) -> Option<u64> {
330    if r == 0 || !r.is_multiple_of(2) || n < 2 {
331        return None;
332    }
333    let half = mod_exp(base, r / 2, n);
334    if half == 1 || half == n - 1 {
335        return None;
336    }
337    [gcd(half + 1, n), gcd(half.wrapping_sub(1), n)]
338        .into_iter()
339        .find(|&candidate| candidate > 1 && candidate < n)
340}
341
342/// End-to-end Shor factoring driver using quantum order finding.
343///
344/// Tries the supplied candidate bases in turn; for each coprime base it runs
345/// [`order_finding`] and attempts [`factor_from_order`]. Returns as soon as a
346/// non-trivial factor is found. Even `N` short-circuits to the factor `2`, and a
347/// base sharing a common factor with `N` yields that factor directly via `gcd`.
348///
349/// # Errors
350/// * [`QuantumError::InvalidParameter`] if `n < 2` or `bases` is empty.
351pub fn shor_factor(n: u64, bases: &[u64], counting_qubits: usize) -> QuantumResult<ShorResult> {
352    if n < 2 {
353        return Err(QuantumError::InvalidParameter {
354            name: format!("N {n} must be ≥ 2"),
355        });
356    }
357    if bases.is_empty() {
358        return Err(QuantumError::EmptyInput);
359    }
360    if n.is_multiple_of(2) {
361        return Ok(ShorResult {
362            factor: Some(2),
363            base: 2,
364            order: 0,
365        });
366    }
367    let mut last_base = bases[0];
368    for &a in bases {
369        let a = a % n;
370        if a < 2 {
371            continue;
372        }
373        last_base = a;
374        let g = gcd(a, n);
375        if g > 1 {
376            // Lucky: a shares a factor with N.
377            return Ok(ShorResult {
378                factor: Some(g),
379                base: a,
380                order: 0,
381            });
382        }
383        let of = order_finding(a, n, counting_qubits)?;
384        if of.order == 0 {
385            continue;
386        }
387        if let Some(factor) = factor_from_order(a, n, of.order) {
388            return Ok(ShorResult {
389                factor: Some(factor),
390                base: a,
391                order: of.order,
392            });
393        }
394    }
395    Ok(ShorResult {
396        factor: None,
397        base: last_base,
398        order: 0,
399    })
400}
401
402#[cfg(test)]
403mod tests {
404    use super::*;
405
406    #[test]
407    fn mod_exp_basic() {
408        assert_eq!(mod_exp(2, 10, 1000), 24); // 1024 mod 1000
409        assert_eq!(mod_exp(7, 0, 13), 1);
410        assert_eq!(mod_exp(3, 3, 7), 6); // 27 mod 7
411    }
412
413    #[test]
414    fn mod_exp_large_no_overflow() {
415        // 2^62 mod (2^61 - 1): exercise u128 path.
416        let m = (1u64 << 61) - 1;
417        let v = mod_exp(2, 62, m);
418        assert!(v < m);
419        // 2^62 = 2 * 2^61 ≡ 2 * 1 = 2 (mod 2^61 - 1).
420        assert_eq!(v, 2);
421    }
422
423    #[test]
424    fn gcd_basic() {
425        assert_eq!(gcd(54, 24), 6);
426        assert_eq!(gcd(17, 13), 1);
427        assert_eq!(gcd(0, 5), 5);
428        assert_eq!(gcd(8, 0), 8);
429    }
430
431    #[test]
432    fn continued_fraction_recovers_denominator() {
433        // φ = 1/4 ⇒ convergents include (1, 4).
434        let cs = continued_fraction_convergents(0.25, 100);
435        assert!(cs.iter().any(|&(_, q)| q == 4), "convergents={cs:?}");
436    }
437
438    #[test]
439    fn continued_fraction_for_third() {
440        let cs = continued_fraction_convergents(1.0 / 3.0, 100);
441        assert!(cs.iter().any(|&(p, q)| p == 1 && q == 3), "cs={cs:?}");
442    }
443
444    #[test]
445    fn classical_order_matches_definition() {
446        // order of 2 mod 15: 2,4,8,16≡1 ⇒ r=4.
447        assert_eq!(classical_order(2, 15), 4);
448        // order of 7 mod 15: 7,49≡4,28≡13,91≡1 ⇒ r=4.
449        assert_eq!(classical_order(7, 15), 4);
450        // not coprime.
451        assert_eq!(classical_order(6, 15), 0);
452    }
453
454    #[test]
455    fn order_finding_period_of_2_mod_15() {
456        // a=2, N=15, true order 4. 8 counting qubits resolves 1/4 exactly.
457        let res = order_finding(2, 15, 8)
458            .expect("a=2 coprime to N=15, 8 counting qubits within valid range");
459        assert_eq!(
460            res.order, 4,
461            "phase={}, measured={}",
462            res.phase, res.measured
463        );
464    }
465
466    #[test]
467    fn order_finding_period_of_4_mod_15() {
468        // a=4, N=15: 4,16≡1 ⇒ order 2.
469        let res = order_finding(4, 15, 8)
470            .expect("a=4 coprime to N=15, 8 counting qubits within valid range");
471        assert_eq!(res.order, 2);
472    }
473
474    #[test]
475    fn order_finding_period_of_2_mod_21() {
476        // order of 2 mod 21: 2,4,8,16,32≡11,22≡1 ⇒ r=6.
477        let res = order_finding(2, 21, 9)
478            .expect("a=2 coprime to N=21, 9 counting qubits within valid range");
479        // Phase estimation may land on a convergent of k/6; accept any divisor
480        // that genuinely satisfies a^r ≡ 1.
481        assert!(res.order == 6 || (res.order > 0 && mod_exp(2, res.order, 21) == 1));
482    }
483
484    #[test]
485    fn order_finding_rejects_non_coprime() {
486        assert!(order_finding(3, 15, 6).is_err()); // gcd(3,15)=3
487    }
488
489    #[test]
490    fn order_finding_rejects_bad_modulus() {
491        assert!(order_finding(2, 1, 6).is_err());
492        assert!(order_finding(2, 15, 0).is_err());
493    }
494
495    #[test]
496    fn factor_from_order_factors_15() {
497        // order of 7 mod 15 is 4; 7^2 = 49 ≡ 4 (mod 15); gcd(4±1,15) = {5,3}.
498        let f = factor_from_order(7, 15, 4)
499            .expect("order 4 of base 7 mod 15 is even and yields a non-trivial factor");
500        assert!(f == 3 || f == 5, "f={f}");
501    }
502
503    #[test]
504    fn factor_from_order_rejects_odd_order() {
505        assert_eq!(factor_from_order(2, 7, 3), None);
506    }
507
508    #[test]
509    fn shor_factor_even_shortcut() {
510        let res = shor_factor(8, &[3, 5, 7], 6)
511            .expect("N=8 is even so shor_factor short-circuits to factor 2");
512        assert_eq!(res.factor, Some(2));
513    }
514
515    #[test]
516    fn shor_factor_fifteen() {
517        // N=15 should factor into 3 or 5 via order finding.
518        let res = shor_factor(15, &[2, 7, 11, 13], 8)
519            .expect("N=15 with coprime bases and 8 counting qubits should succeed");
520        let f = res.factor.expect("should find a factor");
521        assert!(f == 3 || f == 5, "factor={f}");
522        assert_eq!(15 % f, 0);
523    }
524
525    #[test]
526    fn shor_factor_lucky_gcd() {
527        // base 5 shares factor with 15 ⇒ returns 5 immediately.
528        let res = shor_factor(15, &[5], 6)
529            .expect("base=5 shares factor 5 with N=15 so shor_factor succeeds immediately");
530        assert_eq!(res.factor, Some(5));
531    }
532
533    #[test]
534    fn shor_factor_empty_bases_errors() {
535        assert!(shor_factor(15, &[], 6).is_err());
536    }
537
538    #[test]
539    fn shor_factor_invalid_n_errors() {
540        assert!(shor_factor(1, &[2], 6).is_err());
541    }
542}