Skip to main content

tileuniverse_quantum/
lib.rs

1//! # tileuniverse-quantum
2//!
3//! Structured sparse quantum-state representations with GHZ and W-state helpers.
4//!
5//! This crate does not simulate arbitrary quantum circuits. It stores compact
6//! representations for state families whose nonzero amplitudes are already known.
7//!
8//! ## The Key Insight
9//!
10//! GHZ state `|GHZ_n> = (|00...0> + |11...1>)/sqrt(2)` has exactly **2 non-zero amplitudes**
11//! regardless of `n`. Store only those two amplitudes; the qubit count is metadata.
12//!
13//! ```
14//! use tileuniverse_quantum::*;
15//!
16//! // Fixed endpoint-block memory for this concrete usize qubit count.
17//! let ghz = MinimalGhzState::new(1_000_000);
18//! let v = ghz.verify();
19//! assert!((v.fidelity - 1.0).abs() < 1e-10);
20//! ```
21//!
22//! ## State Types
23//!
24//! | Struct | Count representation | Use Case |
25//! |-----|-----|--|
26//! | `MinimalGhzState` | `usize` | Fixed-size endpoint GHZ representation |
27//! | `UnlimitedGhzState` | Materialized `BigUint` | Endpoint GHZ representation with large integer labels |
28//! | `SymbolicGhzState` | Symbolic labels | Graham's number, TREE(3), and formal infinity labels |
29//! | `SparseQuantumGridVec` | `usize` | Materialized W-state excitation amplitudes with O(n) memory |
30//!
31//! ## W-State Example
32//!
33//! ```
34//! use tileuniverse_quantum::SparseQuantumGridVec;
35//!
36//! // W-states materialize one amplitude per excitation position: O(n), not O(2^n).
37//! let grid = SparseQuantumGridVec::new(1_000);
38//! assert_eq!(grid.n_qubits(), 1_000);
39//! ```
40//!
41//! ## Symbolic Example
42//!
43//! ```
44//! use tileuniverse_quantum::*;
45//!
46//! // Graham's number as a symbolic qubit-count label.
47//! let ghz = SymbolicGhzState::graham();
48//! let v = ghz.verify();
49//! assert!((v.fidelity - 1.0).abs() < 1e-10);
50//! assert_eq!(v.size_class, "Graham-class");
51//!
52//! // Symbolic W-states check the stored formula algebraically.
53//! let w = create_graham_w();
54//! let wv = w.verify();
55//! assert!(wv.is_valid);
56//! assert!(wv.total_probability.is_one());
57//! ```
58
59use rayon::prelude::*;
60
61pub use ghz::*;
62pub use symbolic::*;
63pub use w_state::*;
64
65// === Core types (used by all submodules) ===
66
67/// Block size: 128 amplitudes (2^7) per block
68pub const BLOCK_SIZE: usize = 128;
69pub const BLOCK_SHIFT: usize = 7;
70
71/// A single block of 128 complex amplitudes
72#[derive(Clone, Copy, Debug)]
73pub struct VecBlock {
74    /// Real parts of amplitudes
75    pub re: [f64; BLOCK_SIZE],
76    /// Imaginary parts of amplitudes
77    pub im: [f64; BLOCK_SIZE],
78}
79
80impl VecBlock {
81    /// Create a new zero-initialized block
82    #[inline]
83    pub fn new() -> Self {
84        Self {
85            re: [0.0; BLOCK_SIZE],
86            im: [0.0; BLOCK_SIZE],
87        }
88    }
89
90    /// Set amplitude at local address
91    #[inline]
92    pub fn set(&mut self, addr: usize, amp: Complex64) {
93        self.re[addr] = amp.re;
94        self.im[addr] = amp.im;
95    }
96
97    /// Get amplitude at local address
98    #[inline]
99    pub fn get(&self, addr: usize) -> Complex64 {
100        Complex64 {
101            re: self.re[addr],
102            im: self.im[addr],
103        }
104    }
105
106    /// Check if block is all zeros
107    pub fn is_zero(&self) -> bool {
108        self.re.iter().all(|&x| x == 0.0) && self.im.iter().all(|&x| x == 0.0)
109    }
110}
111
112impl Default for VecBlock {
113    fn default() -> Self {
114        Self::new()
115    }
116}
117
118/// Complex number with f64 precision
119#[derive(Clone, Copy, Debug, Default)]
120pub struct Complex64 {
121    pub re: f64,
122    pub im: f64,
123}
124
125impl Complex64 {
126    #[inline]
127    pub fn new(re: f64, im: f64) -> Self {
128        Self { re, im }
129    }
130
131    #[inline]
132    pub fn zero() -> Self {
133        Self { re: 0.0, im: 0.0 }
134    }
135
136    #[inline]
137    pub fn norm_squared(&self) -> f64 {
138        self.re * self.re + self.im * self.im
139    }
140
141    #[inline]
142    pub fn norm(&self) -> f64 {
143        self.norm_squared().sqrt()
144    }
145}
146
147fn terminal_local_addr_for_usize(n_qubits: usize) -> usize {
148    assert!(n_qubits > 0, "GHZ state requires at least one qubit");
149
150    if n_qubits < BLOCK_SHIFT {
151        (1usize << n_qubits) - 1
152    } else {
153        BLOCK_SIZE - 1
154    }
155}
156
157fn terminal_local_addr_for_biguint(n_qubits: &num_bigint::BigUint) -> usize {
158    assert!(n_qubits.bits() > 0, "GHZ state requires at least one qubit");
159
160    let digits = n_qubits.to_u64_digits();
161    if digits.len() == 1 && digits[0] < BLOCK_SHIFT as u64 {
162        (1usize << digits[0] as usize) - 1
163    } else {
164        BLOCK_SIZE - 1
165    }
166}
167
168mod ghz {
169    //! GHZ state representations at every scale.
170    //!
171    //! All GHZ states share the same core insight: exactly 2 non-zero amplitudes,
172    //! so memory and creation time are O(1) regardless of qubit count.
173
174    use super::*;
175    use num_traits::identities::One;
176
177    /// Ultra-minimal GHZ state representation.
178    ///
179    /// For GHZ states, we only ever need 2 amplitudes:
180    /// - `|00...0>` at block 0, index 0
181    /// - `|11...1>` at the terminal local address: `2^n - 1` for `n < 7`,
182    ///   otherwise index 127 in the final endpoint block
183    ///
184    /// This struct stores ONLY these two blocks without any HashMap, Vec, or BigInt.
185    /// Total memory: ~4KB fixed regardless of qubit count.
186    ///
187    /// # Performance
188    ///
189    /// | Qubits | Creation | Verification | Memory |
190    /// |-----|-----|---|--------|
191    /// | 1K | <1 us | <1 us | 4 KB |
192    /// | 1B | <1 us | <1 us | 4 KB |
193    /// | 1T | <1 us | <1 us | 4 KB |
194    ///
195    /// # Example
196    ///
197    /// ```
198    /// use tileuniverse_quantum::MinimalGhzState;
199    ///
200    /// let ghz = MinimalGhzState::new(1_000_000_000_000usize);
201    /// let v = ghz.verify();
202    /// assert!((v.fidelity - 1.0).abs() < 1e-10);
203    /// assert_eq!(format!("{}", ghz), "|GHZ_1.0T>");
204    /// ```
205    #[derive(Clone)]
206    pub struct MinimalGhzState {
207        /// Number of qubits (symbolic — we never compute 2^n)
208        pub n_qubits: usize,
209        /// Block 0: contains amplitude for `|00...0>` at index 0
210        pub block_zero: VecBlock,
211        /// Last block: contains amplitude for `|11...1>` at index 127
212        pub block_last: VecBlock,
213        /// Creation time in microseconds
214        pub creation_time_us: u64,
215    }
216
217    impl MinimalGhzState {
218        /// Create a GHZ state for any positive `usize` qubit count.
219        ///
220        /// This is O(1) — no BigInt, no allocations beyond the fixed 2 blocks.
221        ///
222        /// # Example
223        ///
224        /// ```
225        /// use tileuniverse_quantum::MinimalGhzState;
226        ///
227        /// // 1 million qubits in under 10 microseconds
228        /// let ghz = MinimalGhzState::new(1_000_000);
229        /// assert_eq!(ghz.n_qubits, 1_000_000);
230        /// assert!(ghz.creation_time_us < 1000); // typically instant
231        /// ```
232        pub fn new(n_qubits: usize) -> Self {
233            assert!(n_qubits > 0, "GHZ state requires at least one qubit");
234
235            let start = std::time::Instant::now();
236
237            let inv_sqrt2 = std::f64::consts::FRAC_1_SQRT_2;
238            let terminal_addr = terminal_local_addr_for_usize(n_qubits);
239
240            let mut block_zero = VecBlock::new();
241            block_zero.set(0, Complex64::new(inv_sqrt2, 0.0));
242
243            let mut block_last = VecBlock::new();
244            block_last.set(terminal_addr, Complex64::new(inv_sqrt2, 0.0));
245
246            let creation_time_us = start.elapsed().as_micros() as u64;
247
248            Self {
249                n_qubits,
250                block_zero,
251                block_last,
252                creation_time_us,
253            }
254        }
255
256        /// Verify GHZ state fidelity — O(1), no BigInt.
257        ///
258        /// # Example
259        ///
260        /// ```
261        /// use tileuniverse_quantum::MinimalGhzState;
262        ///
263        /// let ghz = MinimalGhzState::new(42);
264        /// let v = ghz.verify();
265        /// assert!((v.fidelity - 1.0).abs() < 1e-10);
266        /// assert!((v.amp_zero_state - 0.707107).abs() < 1e-4);
267        /// assert_eq!(v.max_spurious, 0.0);
268        /// ```
269        pub fn verify(&self) -> MinimalGhzVerification {
270            let expected_amp = std::f64::consts::FRAC_1_SQRT_2;
271            let terminal_addr = terminal_local_addr_for_usize(self.n_qubits);
272
273            let amp0_complex = self.block_zero.get(0);
274            let amp_last_complex = self.block_last.get(terminal_addr);
275            let amp0 = amp0_complex.norm();
276            let amp_last = amp_last_complex.norm();
277
278            let overlap = Complex64::new(
279                expected_amp * (amp0_complex.re + amp_last_complex.re),
280                expected_amp * (amp0_complex.im + amp_last_complex.im),
281            );
282            let fidelity = overlap.norm_squared();
283
284            let mut max_spurious = 0.0f64;
285
286            for addr in 1..BLOCK_SIZE {
287                let mag = self.block_zero.get(addr).norm();
288                if mag > max_spurious {
289                    max_spurious = mag;
290                }
291            }
292
293            for addr in 0..BLOCK_SIZE {
294                if addr == terminal_addr {
295                    continue;
296                }
297
298                let mag = self.block_last.get(addr).norm();
299                if mag > max_spurious {
300                    max_spurious = mag;
301                }
302            }
303
304            MinimalGhzVerification {
305                n_qubits: self.n_qubits,
306                amp_zero_state: amp0,
307                amp_one_state: amp_last,
308                expected_amplitude: expected_amp,
309                fidelity,
310                max_spurious,
311            }
312        }
313
314        /// Reported struct size in bytes for this fixed endpoint representation.
315        pub fn memory_bytes(&self) -> usize {
316            std::mem::size_of::<Self>()
317        }
318
319        /// Number of qubits
320        pub fn n_qubits(&self) -> usize {
321            self.n_qubits
322        }
323
324        /// Creation time in microseconds
325        pub fn creation_time_us(&self) -> u64 {
326            self.creation_time_us
327        }
328    }
329
330    impl std::fmt::Display for MinimalGhzState {
331        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
332            let label = if self.n_qubits >= 1_000_000_000_000 {
333                format!("{:.1}T", self.n_qubits as f64 / 1e12)
334            } else if self.n_qubits >= 1_000_000_000 {
335                format!("{:.1}B", self.n_qubits as f64 / 1e9)
336            } else if self.n_qubits >= 1_000_000 {
337                format!("{:.1}M", self.n_qubits as f64 / 1e6)
338            } else if self.n_qubits >= 1_000 {
339                format!("{:.1}K", self.n_qubits as f64 / 1e3)
340            } else {
341                format!("{}", self.n_qubits)
342            };
343            write!(f, "|GHZ_{}>", label)
344        }
345    }
346
347    /// Verification results for MinimalGhzState
348    #[derive(Debug, Clone)]
349    pub struct MinimalGhzVerification {
350        pub n_qubits: usize,
351        pub amp_zero_state: f64,
352        pub amp_one_state: f64,
353        pub expected_amplitude: f64,
354        pub fidelity: f64,
355        pub max_spurious: f64,
356    }
357
358    impl std::fmt::Display for MinimalGhzVerification {
359        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
360            writeln!(
361                f,
362                "Minimal GHZ Verification ({} qubits) [O(1) — NO BIGINT]",
363                self.n_qubits
364            )?;
365            writeln!(
366                f,
367                "  |00...0> amplitude: {:.6} (expected {:.6})",
368                self.amp_zero_state, self.expected_amplitude
369            )?;
370            writeln!(
371                f,
372                "  |11...1> amplitude: {:.6} (expected {:.6})",
373                self.amp_one_state, self.expected_amplitude
374            )?;
375            writeln!(f, "  Fidelity:           {:.6}", self.fidelity)?;
376            writeln!(f, "  Max spurious:       {:.6}", self.max_spurious)?;
377            Ok(())
378        }
379    }
380
381    /// Create a minimal GHZ state for a positive `usize` qubit count.
382    ///
383    /// # Example
384    ///
385    /// ```
386    /// use tileuniverse_quantum::{MinimalGhzState, create_minimal_ghz};
387    ///
388    /// let ghz1 = MinimalGhzState::new(100);
389    /// let ghz2 = create_minimal_ghz(100);
390    /// assert_eq!(ghz1.n_qubits, ghz2.n_qubits);
391    /// ```
392    pub fn create_minimal_ghz(n_qubits: usize) -> MinimalGhzState {
393        MinimalGhzState::new(n_qubits)
394    }
395
396    /// GHZ endpoint representation using a BigUint qubit-count label.
397    ///
398    /// This breaks through the 2^64 barrier. The qubit count can be any positive
399    /// integer — googol, googolplex, whatever.
400    ///
401    /// # Memory
402    ///
403    /// The endpoint amplitude storage is fixed-size. The reported payload bytes
404    /// also include the materialized BigUint qubit-count digits.
405    ///
406    /// # Example
407    ///
408    /// ```
409    /// use tileuniverse_quantum::UnlimitedGhzState;
410    ///
411    /// // 10^100 qubits (a googol)
412    /// let googol = num_bigint::BigUint::from(10u32).pow(100);
413    /// let ghz = UnlimitedGhzState::new(googol);
414    /// let v = ghz.verify();
415    /// assert!((v.fidelity - 1.0).abs() < 1e-10);
416    /// ```
417    #[derive(Clone)]
418    pub struct UnlimitedGhzState {
419        /// Number of qubits as BigUint — no upper limit
420        pub n_qubits: num_bigint::BigUint,
421        /// Block 0: contains amplitude for `|00...0>` at index 0
422        pub block_zero: VecBlock,
423        /// Last block: contains amplitude for `|11...1>` at index 127
424        pub block_last: VecBlock,
425        /// Creation time in microseconds
426        pub creation_time_us: u64,
427    }
428
429    impl UnlimitedGhzState {
430        /// Create a GHZ state for any positive BigUint qubit count.
431        ///
432        /// The qubit count can be arbitrarily large — googol, googolplex, etc.
433        /// Memory and time remain O(1).
434        ///
435        /// # Example
436        ///
437        /// ```
438        /// use tileuniverse_quantum::UnlimitedGhzState;
439        ///
440        /// let ghz = UnlimitedGhzState::new(num_bigint::BigUint::from(42u32));
441        /// let v = ghz.verify();
442        /// assert!((v.fidelity - 1.0).abs() < 1e-10);
443        /// ```
444        pub fn new(n_qubits: num_bigint::BigUint) -> Self {
445            assert!(n_qubits.bits() > 0, "GHZ state requires at least one qubit");
446
447            let start = std::time::Instant::now();
448
449            let inv_sqrt2 = std::f64::consts::FRAC_1_SQRT_2;
450            let terminal_addr = terminal_local_addr_for_biguint(&n_qubits);
451
452            let mut block_zero = VecBlock::new();
453            block_zero.set(0, Complex64::new(inv_sqrt2, 0.0));
454
455            let mut block_last = VecBlock::new();
456            block_last.set(terminal_addr, Complex64::new(inv_sqrt2, 0.0));
457
458            let creation_time_us = start.elapsed().as_micros() as u64;
459
460            Self {
461                n_qubits,
462                block_zero,
463                block_last,
464                creation_time_us,
465            }
466        }
467
468        /// Create from a power of 10: 10^exponent qubits.
469        ///
470        /// # Example
471        ///
472        /// ```
473        /// use tileuniverse_quantum::UnlimitedGhzState;
474        ///
475        /// // 10^3 = 1000 qubits
476        /// let ghz = UnlimitedGhzState::from_power_of_10(3);
477        /// assert!((ghz.verify().fidelity - 1.0).abs() < 1e-10);
478        /// ```
479        pub fn from_power_of_10(exponent: u32) -> Self {
480            let n_qubits = num_bigint::BigUint::from(10u32).pow(exponent);
481            Self::new(n_qubits)
482        }
483
484        /// Create from a power of 2: 2^exponent qubits.
485        ///
486        /// # Example
487        ///
488        /// ```
489        /// use tileuniverse_quantum::UnlimitedGhzState;
490        ///
491        /// // 2^10 = 1024 qubits
492        /// let ghz = UnlimitedGhzState::from_power_of_2(10);
493        /// assert_eq!(ghz.n_qubits.to_u64_digits()[0], 1024);
494        /// ```
495        pub fn from_power_of_2(exponent: u32) -> Self {
496            let n_qubits = num_bigint::BigUint::one() << exponent;
497            Self::new(n_qubits)
498        }
499
500        /// Verify the stored endpoint amplitudes without iterating over basis states.
501        pub fn verify(&self) -> UnlimitedGhzVerification {
502            let expected_amp = std::f64::consts::FRAC_1_SQRT_2;
503            let terminal_addr = terminal_local_addr_for_biguint(&self.n_qubits);
504
505            let amp0_complex = self.block_zero.get(0);
506            let amp_last_complex = self.block_last.get(terminal_addr);
507            let amp0 = amp0_complex.norm();
508            let amp_last = amp_last_complex.norm();
509
510            let overlap = Complex64::new(
511                expected_amp * (amp0_complex.re + amp_last_complex.re),
512                expected_amp * (amp0_complex.im + amp_last_complex.im),
513            );
514            let fidelity = overlap.norm_squared();
515
516            let mut max_spurious = 0.0f64;
517
518            for addr in 1..BLOCK_SIZE {
519                let mag = self.block_zero.get(addr).norm();
520                if mag > max_spurious {
521                    max_spurious = mag;
522                }
523            }
524
525            for addr in 0..BLOCK_SIZE {
526                if addr == terminal_addr {
527                    continue;
528                }
529
530                let mag = self.block_last.get(addr).norm();
531                if mag > max_spurious {
532                    max_spurious = mag;
533                }
534            }
535
536            UnlimitedGhzVerification {
537                n_qubits: self.n_qubits.clone(),
538                amp_zero_state: amp0,
539                amp_one_state: amp_last,
540                expected_amplitude: expected_amp,
541                fidelity,
542                max_spurious,
543            }
544        }
545
546        /// Reported payload bytes: endpoint blocks plus BigUint qubit-count digits.
547        pub fn memory_bytes(&self) -> usize {
548            std::mem::size_of::<VecBlock>() * 2 + self.n_qubits.to_bytes_le().len()
549        }
550
551        /// Get the qubit count
552        pub fn n_qubits(&self) -> &num_bigint::BigUint {
553            &self.n_qubits
554        }
555
556        /// Get approximate log10 of qubit count (for display)
557        pub fn log10_qubits(&self) -> f64 {
558            let bits = self.n_qubits.bits();
559            bits as f64 * 0.30103 // log10(2)
560        }
561
562        /// Get the state space size as a string: "2^n where n = ..."
563        pub fn state_space_description(&self) -> String {
564            format!("2^{} amplitudes", self.n_qubits)
565        }
566    }
567
568    impl std::fmt::Display for UnlimitedGhzState {
569        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
570            let bits = self.n_qubits.bits();
571            if bits > 1000 {
572                write!(f, "|GHZ_(~10^{:.0})>", bits as f64 * 0.30103)
573            } else if bits > 100 {
574                write!(f, "|GHZ_(2^{})>", bits)
575            } else {
576                write!(f, "|GHZ_{}>", self.n_qubits)
577            }
578        }
579    }
580
581    /// Verification results for UnlimitedGhzState
582    #[derive(Debug, Clone)]
583    pub struct UnlimitedGhzVerification {
584        pub n_qubits: num_bigint::BigUint,
585        pub amp_zero_state: f64,
586        pub amp_one_state: f64,
587        pub expected_amplitude: f64,
588        pub fidelity: f64,
589        pub max_spurious: f64,
590    }
591
592    impl std::fmt::Display for UnlimitedGhzVerification {
593        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
594            let bits = self.n_qubits.bits();
595            let log10 = bits as f64 * 0.30103;
596
597            writeln!(f, "BigUint GHZ Verification [endpoint representation]")?;
598            if bits > 1000 {
599                writeln!(
600                    f,
601                    "  Qubits: ~10^{:.0} (~2^{} bits to represent)",
602                    log10, bits
603                )?;
604            } else {
605                writeln!(f, "  Qubits: {} ({} bits)", self.n_qubits, bits)?;
606            }
607            writeln!(
608                f,
609                "  |00...0> amplitude: {:.6} (expected {:.6})",
610                self.amp_zero_state, self.expected_amplitude
611            )?;
612            writeln!(
613                f,
614                "  |11...1> amplitude: {:.6} (expected {:.6})",
615                self.amp_one_state, self.expected_amplitude
616            )?;
617            writeln!(f, "  Fidelity:           {:.6}", self.fidelity)?;
618            writeln!(f, "  Max spurious:       {:.6}", self.max_spurious)?;
619            writeln!(f, "  State space:        2^(10^{:.0}) amplitudes", log10)?;
620            Ok(())
621        }
622    }
623
624    /// Create a GHZ state from a BigUint qubit count.
625    ///
626    /// # Example
627    ///
628    /// ```
629    /// use tileuniverse_quantum::{UnlimitedGhzState, create_unlimited_ghz};
630    ///
631    /// let n = num_bigint::BigUint::from(42u32);
632    /// let ghz = create_unlimited_ghz(n.clone());
633    /// let ghz2 = UnlimitedGhzState::new(n);
634    /// assert!((ghz.verify().fidelity - ghz2.verify().fidelity).abs() < 1e-10);
635    /// ```
636    pub fn create_unlimited_ghz(n_qubits: num_bigint::BigUint) -> UnlimitedGhzState {
637        UnlimitedGhzState::new(n_qubits)
638    }
639
640    /// Create a GHZ state with a `10^exponent` BigUint qubit count.
641    ///
642    /// # Example
643    ///
644    /// ```
645    /// use tileuniverse_quantum::create_ghz_power_of_10;
646    ///
647    /// let ghz = create_ghz_power_of_10(6); // 10^6 qubits
648    /// assert!((ghz.verify().fidelity - 1.0).abs() < 1e-10);
649    /// ```
650    pub fn create_ghz_power_of_10(exponent: u32) -> UnlimitedGhzState {
651        UnlimitedGhzState::from_power_of_10(exponent)
652    }
653}
654
655mod w_state {
656    //! W-state and SparseQuantumGridVec — O(n) memory for n excitations.
657    //!
658    //! Unlike GHZ (2 amplitudes), W-state has n amplitudes. For large n we use
659    //! a Vec-based grid for O(1) block access.
660
661    use super::*;
662
663    const W_AMPLITUDE_TOLERANCE: f64 = 1e-12;
664
665    /// Verification result for W state
666    #[derive(Debug, Clone)]
667    pub struct WStateVerificationVec {
668        pub n_qubits: usize,
669        pub expected_amplitudes: usize,
670        pub correct_amplitudes: usize,
671        pub expected_amplitude: f64,
672        pub total_probability: f64,
673        pub fidelity: f64,
674        pub max_amplitude_error: f64,
675        pub max_spurious_amplitude: f64,
676        pub active_blocks: usize,
677        pub memory_bytes: usize,
678    }
679
680    /// Fast Vec-based sparse quantum grid.
681    ///
682    /// Uses a `Vec<VecBlock>` for O(1) block access without BigInt overhead.
683    /// Ideal for states with sequential block patterns like W states.
684    ///
685    /// # Performance Comparison
686    ///
687    /// | Qubits | BigInt W State | Vec W State |
688    /// |-----|-----|--|
689    /// | 1M | ~30 seconds | ~25 ms |
690    /// | 100M | impossible | ~130 ms |
691    /// | 1B | impossible | ~1.2 sec |
692    /// | 4B | impossible | ~14 sec |
693    ///
694    /// # Example
695    ///
696    /// ```
697    /// use tileuniverse_quantum::SparseQuantumGridVec;
698    ///
699    /// let mut grid = SparseQuantumGridVec::new(1_000_000);
700    /// grid.create_w_state();
701    /// let v = grid.verify_w_fidelity();
702    /// assert!((v.fidelity - 1.0).abs() < 1e-10);
703    /// assert_eq!(v.correct_amplitudes, 1_000_000);
704    /// ```
705    #[derive(Clone)]
706    pub struct SparseQuantumGridVec {
707        n_qubits: usize,
708        n_blocks: usize,
709        blocks: Vec<VecBlock>,
710    }
711
712    impl SparseQuantumGridVec {
713        /// Create a new Vec-based sparse grid.
714        ///
715        /// Pre-allocates all blocks upfront for maximum performance.
716        ///
717        /// # Panics
718        ///
719        /// Panics if `n_qubits < 7` (minimum block alignment requirement).
720        ///
721        /// # Example
722        ///
723        /// ```
724        /// use tileuniverse_quantum::SparseQuantumGridVec;
725        ///
726        /// let grid = SparseQuantumGridVec::new(128);
727        /// assert_eq!(grid.n_qubits(), 128);
728        /// assert_eq!(grid.n_blocks(), 1);
729        /// ```
730        pub fn new(n_qubits: usize) -> Self {
731            assert!(n_qubits >= 7, "Minimum 7 qubits required");
732            let n_blocks = (n_qubits + BLOCK_SIZE - 1) / BLOCK_SIZE;
733
734            Self {
735                n_qubits,
736                n_blocks,
737                blocks: vec![VecBlock::new(); n_blocks],
738            }
739        }
740
741        /// Create a new grid with lazy block allocation.
742        ///
743        /// Blocks are allocated on first access, saving memory for sparse states.
744        pub fn new_lazy(n_qubits: usize) -> Self {
745            assert!(n_qubits >= 7, "Minimum 7 qubits required");
746            let n_blocks = (n_qubits + BLOCK_SIZE - 1) / BLOCK_SIZE;
747
748            Self {
749                n_qubits,
750                n_blocks,
751                blocks: Vec::with_capacity(n_blocks),
752            }
753        }
754
755        /// Get number of qubits
756        #[inline]
757        pub fn n_qubits(&self) -> usize {
758            self.n_qubits
759        }
760
761        /// Get number of blocks
762        #[inline]
763        pub fn n_blocks(&self) -> usize {
764            self.n_blocks
765        }
766
767        /// Get number of allocated blocks
768        #[inline]
769        pub fn allocated_blocks(&self) -> usize {
770            self.blocks.len()
771        }
772
773        /// Get memory usage in bytes
774        pub fn memory_bytes(&self) -> usize {
775            self.blocks.len() * std::mem::size_of::<VecBlock>()
776        }
777
778        fn max_spurious_tail_amplitude(&self) -> f64 {
779            let mut max_spurious = 0.0f64;
780            let total_slots = self.blocks.len() * BLOCK_SIZE;
781
782            for slot in self.n_qubits..total_slots {
783                let block_idx = slot / BLOCK_SIZE;
784                let local_addr = slot % BLOCK_SIZE;
785                let mag = self.blocks[block_idx].get(local_addr).norm();
786                if mag > max_spurious {
787                    max_spurious = mag;
788                }
789            }
790
791            max_spurious
792        }
793
794        /// Clear all blocks to zero
795        pub fn clear(&mut self) {
796            for block in &mut self.blocks {
797                block.re.fill(0.0);
798                block.im.fill(0.0);
799            }
800        }
801
802        /// Ensure blocks are allocated up to the required count
803        fn ensure_blocks(&mut self, count: usize) {
804            while self.blocks.len() < count {
805                self.blocks.push(VecBlock::new());
806            }
807        }
808
809        /// Create W state: `|W_n> = (|10...0> + |01...0> + ... + |00...1>) / sqrt(n)`.
810        ///
811        /// Returns creation time in microseconds.
812        ///
813        /// # Example
814        ///
815        /// ```
816        /// use tileuniverse_quantum::SparseQuantumGridVec;
817        ///
818        /// let mut grid = SparseQuantumGridVec::new(100);
819        /// let us = grid.create_w_state();
820        /// assert!(us < 1000); // under 1 ms for 100 qubits
821        /// ```
822        pub fn create_w_state(&mut self) -> u64 {
823            let start = std::time::Instant::now();
824
825            self.ensure_blocks(self.n_blocks);
826            self.clear();
827
828            let amplitude = 1.0 / (self.n_qubits as f64).sqrt();
829            let amp = Complex64::new(amplitude, 0.0);
830
831            for qubit in 0..self.n_qubits {
832                let block_idx = qubit / BLOCK_SIZE;
833                let local_addr = qubit % BLOCK_SIZE;
834                self.blocks[block_idx].set(local_addr, amp);
835            }
836
837            start.elapsed().as_micros() as u64
838        }
839
840        /// Create W state using parallel iteration (faster for large n).
841        pub fn create_w_state_parallel(&mut self) -> u64 {
842            let start = std::time::Instant::now();
843
844            self.ensure_blocks(self.n_blocks);
845
846            let amplitude = 1.0 / (self.n_qubits as f64).sqrt();
847            let n_qubits = self.n_qubits;
848
849            self.blocks
850                .par_iter_mut()
851                .enumerate()
852                .for_each(|(block_idx, block)| {
853                    block.re.fill(0.0);
854                    block.im.fill(0.0);
855
856                    let start_qubit = block_idx * BLOCK_SIZE;
857                    let end_qubit = (start_qubit + BLOCK_SIZE).min(n_qubits);
858
859                    for qubit in start_qubit..end_qubit {
860                        let local_addr = qubit % BLOCK_SIZE;
861                        block.re[local_addr] = amplitude;
862                    }
863                });
864
865            start.elapsed().as_micros() as u64
866        }
867
868        /// Verify W state fidelity.
869        ///
870        /// # Example
871        ///
872        /// ```
873        /// use tileuniverse_quantum::SparseQuantumGridVec;
874        ///
875        /// let mut grid = SparseQuantumGridVec::new(10);
876        /// grid.create_w_state();
877        /// let v = grid.verify_w_fidelity();
878        /// assert!((v.fidelity - 1.0).abs() < 1e-10);
879        /// assert_eq!(v.correct_amplitudes, 10);
880        /// ```
881        pub fn verify_w_fidelity(&self) -> WStateVerificationVec {
882            let expected_amp = 1.0 / (self.n_qubits as f64).sqrt();
883
884            let mut total_prob = 0.0;
885            let mut overlap_re = 0.0;
886            let mut overlap_im = 0.0;
887            let mut correct_amplitudes = 0usize;
888            let mut max_error = 0.0f64;
889
890            for qubit in 0..self.n_qubits {
891                let block_idx = qubit / BLOCK_SIZE;
892                let local_addr = qubit % BLOCK_SIZE;
893
894                if block_idx < self.blocks.len() {
895                    let amp = self.blocks[block_idx].get(local_addr);
896                    let prob = amp.norm_squared();
897                    total_prob += prob;
898                    overlap_re += expected_amp * amp.re;
899                    overlap_im += expected_amp * amp.im;
900
901                    let error = ((amp.re - expected_amp).powi(2) + amp.im.powi(2)).sqrt();
902                    if error <= W_AMPLITUDE_TOLERANCE {
903                        correct_amplitudes += 1;
904                    }
905                    if error > max_error {
906                        max_error = error;
907                    }
908                }
909            }
910
911            WStateVerificationVec {
912                n_qubits: self.n_qubits,
913                expected_amplitudes: self.n_qubits,
914                correct_amplitudes,
915                expected_amplitude: expected_amp,
916                total_probability: total_prob,
917                fidelity: overlap_re.powi(2) + overlap_im.powi(2),
918                max_amplitude_error: max_error,
919                max_spurious_amplitude: self.max_spurious_tail_amplitude(),
920                active_blocks: self.blocks.len(),
921                memory_bytes: self.memory_bytes(),
922            }
923        }
924
925        /// Verify W state fidelity using parallel iteration.
926        pub fn verify_w_fidelity_parallel(&self) -> WStateVerificationVec {
927            let expected_amp = 1.0 / (self.n_qubits as f64).sqrt();
928            let n_qubits = self.n_qubits;
929
930            let (total_prob, correct_amplitudes, max_error, overlap_re, overlap_im) = self
931                .blocks
932                .par_iter()
933                .enumerate()
934                .map(|(block_idx, block): (usize, &VecBlock)| {
935                    let start_qubit = block_idx * BLOCK_SIZE;
936                    let end_qubit = (start_qubit + BLOCK_SIZE).min(n_qubits);
937
938                    let mut block_prob = 0.0;
939                    let mut block_correct = 0usize;
940                    let mut block_max_error = 0.0f64;
941                    let mut block_overlap_re = 0.0;
942                    let mut block_overlap_im = 0.0;
943
944                    for qubit in start_qubit..end_qubit {
945                        let local_addr = qubit % BLOCK_SIZE;
946                        let amp = block.get(local_addr);
947                        let prob = amp.norm_squared();
948                        block_prob += prob;
949                        block_overlap_re += expected_amp * amp.re;
950                        block_overlap_im += expected_amp * amp.im;
951
952                        let error = ((amp.re - expected_amp).powi(2) + amp.im.powi(2)).sqrt();
953                        if error <= W_AMPLITUDE_TOLERANCE {
954                            block_correct += 1;
955                        }
956                        if error > block_max_error {
957                            block_max_error = error;
958                        }
959                    }
960
961                    (
962                        block_prob,
963                        block_correct,
964                        block_max_error,
965                        block_overlap_re,
966                        block_overlap_im,
967                    )
968                })
969                .reduce(
970                    || (0.0, 0usize, 0.0f64, 0.0, 0.0),
971                    |(p1, c1, e1, r1, i1), (p2, c2, e2, r2, i2)| {
972                        (p1 + p2, c1 + c2, e1.max(e2), r1 + r2, i1 + i2)
973                    },
974                );
975
976            WStateVerificationVec {
977                n_qubits: self.n_qubits,
978                expected_amplitudes: self.n_qubits,
979                correct_amplitudes,
980                expected_amplitude: expected_amp,
981                total_probability: total_prob,
982                fidelity: overlap_re.powi(2) + overlap_im.powi(2),
983                max_amplitude_error: max_error,
984                max_spurious_amplitude: self.max_spurious_tail_amplitude(),
985                active_blocks: self.blocks.len(),
986                memory_bytes: self.memory_bytes(),
987            }
988        }
989
990        /// Get amplitude at a specific qubit position.
991        #[inline]
992        pub fn get_amplitude(&self, qubit: usize) -> Complex64 {
993            let block_idx = qubit / BLOCK_SIZE;
994            let local_addr = qubit % BLOCK_SIZE;
995
996            if block_idx < self.blocks.len() {
997                self.blocks[block_idx].get(local_addr)
998            } else {
999                Complex64::zero()
1000            }
1001        }
1002
1003        /// Set amplitude at a specific qubit position.
1004        #[inline]
1005        pub fn set_amplitude(&mut self, qubit: usize, amp: Complex64) {
1006            let block_idx = qubit / BLOCK_SIZE;
1007            let local_addr = qubit % BLOCK_SIZE;
1008
1009            self.ensure_blocks(block_idx + 1);
1010            self.blocks[block_idx].set(local_addr, amp);
1011        }
1012    }
1013}
1014
1015mod symbolic {
1016    //! Symbolic quantum states: qubit counts as symbolic expressions.
1017    //!
1018    //! Supports Graham's number, TREE(3), a formal infinity label (aleph_0), Knuth up-arrow,
1019    //! tetration, and arbitrary BigUint via the `SymbolicNumber` enum.
1020    //!
1021    //! Also includes Dicke states, W-states, binomial coefficients,
1022    //! entanglement entropy, and symbolic amplitudes/fractions.
1023
1024    use super::*;
1025
1026    // === SymbolicNumber ===
1027
1028    /// Symbolic representation of incomprehensibly large numbers.
1029    ///
1030    /// These numbers are so large they cannot be materialized as BigUint.
1031    /// Graham's number, for example, has more digits than atoms in the universe.
1032    #[derive(Clone, Debug)]
1033    pub enum SymbolicNumber {
1034        /// A concrete u64 value
1035        Literal(u64),
1036        /// A BigUint value (already computed)
1037        Big(num_bigint::BigUint),
1038        /// Power: base^exponent (e.g., 10^100)
1039        Pow {
1040            base: u64,
1041            exponent: Box<SymbolicNumber>,
1042        },
1043        /// Tower of powers (tetration): base^^height
1044        Tetration {
1045            base: u64,
1046            height: Box<SymbolicNumber>,
1047        },
1048        /// Knuth up-arrow notation: a ↑^n b
1049        KnuthArrow {
1050            a: u64,
1051            arrows: u64,
1052            b: Box<SymbolicNumber>,
1053        },
1054        /// Graham's number: g_64 where g_1 = 3↑↑↑↑3, g_n = 3↑^(g_{n-1})3
1055        Graham,
1056        /// TREE(n) — grows faster than Graham's number
1057        Tree(u64),
1058        /// Rayo's number — largest named number
1059        Rayo,
1060        /// Formal infinity label (aleph_0 for countable)
1061        Infinity,
1062        /// Custom named number with description
1063        Named { name: String, description: String },
1064        /// Integer division: numerator / denominator (symbolic)
1065        Div {
1066            numerator: Box<SymbolicNumber>,
1067            denominator: Box<SymbolicNumber>,
1068        },
1069        /// Subtraction: minuend - subtrahend (symbolic)
1070        Sub {
1071            minuend: Box<SymbolicNumber>,
1072            subtrahend: Box<SymbolicNumber>,
1073        },
1074    }
1075
1076    impl SymbolicNumber {
1077        /// Get a human-readable representation
1078        pub fn to_notation(&self) -> String {
1079            match self {
1080                Self::Literal(n) => format!("{}", n),
1081                Self::Big(n) => {
1082                    let bits = n.bits();
1083                    if bits > 1000 {
1084                        format!("~10^{:.0}", bits as f64 * 0.30103)
1085                    } else {
1086                        format!("{}", n)
1087                    }
1088                }
1089                Self::Pow { base, exponent } => {
1090                    format!("{}^({})", base, exponent.to_notation())
1091                }
1092                Self::Tetration { base, height } => {
1093                    format!("{}^^{}", base, height.to_notation())
1094                }
1095                Self::KnuthArrow { a, arrows, b } => {
1096                    let arrow_str: String = std::iter::repeat('↑').take(*arrows as usize).collect();
1097                    format!("{}{}{}", a, arrow_str, b.to_notation())
1098                }
1099                Self::Graham => "G (Graham's number)".to_string(),
1100                Self::Tree(n) => format!("TREE({})", n),
1101                Self::Rayo => "Rayo's number".to_string(),
1102                Self::Infinity => "infty".to_string(),
1103                Self::Named { name, .. } => name.clone(),
1104                Self::Div {
1105                    numerator,
1106                    denominator,
1107                } => {
1108                    format!(
1109                        "({}/{})",
1110                        numerator.to_notation(),
1111                        denominator.to_notation()
1112                    )
1113                }
1114                Self::Sub {
1115                    minuend,
1116                    subtrahend,
1117                } => {
1118                    format!("({}-{})", minuend.to_notation(), subtrahend.to_notation())
1119                }
1120            }
1121        }
1122
1123        /// Get the "size class" for comparison purposes
1124        pub fn size_class(&self) -> &'static str {
1125            match self {
1126                Self::Literal(n) if *n < 1_000_000 => "small",
1127                Self::Literal(_) => "large",
1128                Self::Big(n) if n.bits() < 1000 => "astronomical",
1129                Self::Big(_) => "cosmological",
1130                Self::Pow { .. } => "cosmological",
1131                Self::Tetration { .. } => "tetration",
1132                Self::KnuthArrow { arrows, .. } if *arrows <= 2 => "tetration",
1133                Self::KnuthArrow { arrows, .. } if *arrows <= 4 => "pentation+",
1134                Self::KnuthArrow { .. } => "arrow-notation",
1135                Self::Graham => "Graham-class",
1136                Self::Tree(_) => "TREE-class",
1137                Self::Rayo => "uncomputable",
1138                Self::Infinity => "infinite",
1139                Self::Named { .. } => "named",
1140                Self::Div { numerator, .. } => numerator.size_class(),
1141                Self::Sub { minuend, .. } => minuend.size_class(),
1142            }
1143        }
1144
1145        /// Check if this symbolic number is zero
1146        pub fn is_zero(&self) -> bool {
1147            match self {
1148                Self::Literal(0) => true,
1149                Self::Big(n) => n == &num_bigint::BigUint::from(0u32),
1150                _ => false,
1151            }
1152        }
1153
1154        /// Check if this symbolic number equals another (structurally)
1155        pub fn structurally_equal(&self, other: &Self) -> bool {
1156            match (self, other) {
1157                (Self::Literal(a), Self::Literal(b)) => a == b,
1158                (Self::Big(a), Self::Big(b)) => a == b,
1159                (Self::Graham, Self::Graham) => true,
1160                (Self::Tree(a), Self::Tree(b)) => a == b,
1161                (Self::Infinity, Self::Infinity) => true,
1162                (Self::Rayo, Self::Rayo) => true,
1163                _ => false,
1164            }
1165        }
1166
1167        /// Estimate log_10 of the number (returns None if incomputable)
1168        pub fn estimate_log10(&self) -> Option<f64> {
1169            match self {
1170                Self::Literal(n) => Some((*n as f64).log10()),
1171                Self::Big(n) => Some(n.bits() as f64 * 0.30103),
1172                Self::Pow { base, exponent } => {
1173                    let exp_log = exponent.estimate_log10()?;
1174                    Some(10f64.powf(exp_log) * (*base as f64).log10())
1175                }
1176                Self::Div {
1177                    numerator,
1178                    denominator,
1179                } => {
1180                    let num_log = numerator.estimate_log10()?;
1181                    let denom_log = denominator.estimate_log10()?;
1182                    Some(num_log - denom_log)
1183                }
1184                Self::Sub {
1185                    minuend,
1186                    subtrahend,
1187                } => {
1188                    let minuend_log = minuend.estimate_log10()?;
1189                    let subtrahend_log = subtrahend.estimate_log10()?;
1190                    if minuend_log > subtrahend_log + 1.0 {
1191                        Some(minuend_log)
1192                    } else {
1193                        None
1194                    }
1195                }
1196                _ => None,
1197            }
1198        }
1199
1200        /// Can this number be materialized as BigUint in reasonable time?
1201        pub fn is_materializable(&self) -> bool {
1202            match self {
1203                Self::Literal(_) => true,
1204                Self::Big(_) => true,
1205                Self::Pow { exponent, .. } => match exponent.as_ref() {
1206                    SymbolicNumber::Literal(e) => *e < 100_000_000,
1207                    _ => false,
1208                },
1209                Self::Div {
1210                    numerator,
1211                    denominator,
1212                } => numerator.is_materializable() && denominator.is_materializable(),
1213                Self::Sub {
1214                    minuend,
1215                    subtrahend,
1216                } => minuend.is_materializable() && subtrahend.is_materializable(),
1217                _ => false,
1218            }
1219        }
1220    }
1221
1222    impl std::fmt::Display for SymbolicNumber {
1223        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1224            write!(f, "{}", self.to_notation())
1225        }
1226    }
1227
1228    fn terminal_local_addr_for_symbolic_number(n_qubits: &SymbolicNumber) -> usize {
1229        assert!(!n_qubits.is_zero(), "GHZ state requires at least one qubit");
1230
1231        match n_qubits {
1232            SymbolicNumber::Literal(n) if *n < BLOCK_SHIFT as u64 => (1usize << *n as usize) - 1,
1233            SymbolicNumber::Big(n) => terminal_local_addr_for_biguint(n),
1234            _ => BLOCK_SIZE - 1,
1235        }
1236    }
1237
1238    // === SymbolicGhzState ===
1239
1240    /// GHZ endpoint representation with a symbolic qubit-count label.
1241    ///
1242    /// This struct can represent GHZ states with:
1243    /// - Graham's number of qubits
1244    /// - TREE(3) qubits
1245    /// - A formal infinity label (aleph_0)
1246    ///
1247    /// Verification checks the stored endpoint amplitudes and symbolic label. It
1248    /// does not model analytic infinite tensor-product Hilbert spaces.
1249    ///
1250    /// # Example
1251    ///
1252    /// ```
1253    /// use tileuniverse_quantum::SymbolicGhzState;
1254    ///
1255    /// // Graham's number of qubits
1256    /// let ghz = SymbolicGhzState::graham();
1257    /// let v = ghz.verify();
1258    /// assert!((v.fidelity - 1.0).abs() < 1e-10);
1259    /// assert_eq!(v.size_class, "Graham-class");
1260    ///
1261    /// // Infinite qubits
1262    /// let inf = SymbolicGhzState::infinite();
1263    /// assert!((inf.verify().fidelity - 1.0).abs() < 1e-10);
1264    /// ```
1265    #[derive(Clone)]
1266    pub struct SymbolicGhzState {
1267        /// Number of qubits as a symbolic expression
1268        pub n_qubits: SymbolicNumber,
1269        /// Block 0: contains amplitude for `|00...0>` at index 0
1270        pub block_zero: VecBlock,
1271        /// Last block: contains amplitude for `|11...1>` at index 127
1272        pub block_last: VecBlock,
1273        /// Creation time in nanoseconds
1274        pub creation_time_ns: u64,
1275    }
1276
1277    impl SymbolicGhzState {
1278        /// Create a GHZ state with a symbolic qubit count.
1279        ///
1280        /// This works for positive symbolic labels, including formal infinity labels.
1281        ///
1282        /// # Example
1283        ///
1284        /// ```
1285        /// use tileuniverse_quantum::{SymbolicGhzState, SymbolicNumber};
1286        ///
1287        /// let ghz = SymbolicGhzState::new(SymbolicNumber::Literal(42));
1288        /// assert_eq!(ghz.n_qubits.to_notation(), "42");
1289        /// let v = ghz.verify();
1290        /// assert!((v.fidelity - 1.0).abs() < 1e-10);
1291        /// ```
1292        pub fn new(n_qubits: SymbolicNumber) -> Self {
1293            assert!(!n_qubits.is_zero(), "GHZ state requires at least one qubit");
1294
1295            let start = std::time::Instant::now();
1296
1297            let inv_sqrt2 = std::f64::consts::FRAC_1_SQRT_2;
1298            let terminal_addr = terminal_local_addr_for_symbolic_number(&n_qubits);
1299
1300            let mut block_zero = VecBlock::new();
1301            block_zero.set(0, Complex64::new(inv_sqrt2, 0.0));
1302
1303            let mut block_last = VecBlock::new();
1304            block_last.set(terminal_addr, Complex64::new(inv_sqrt2, 0.0));
1305
1306            let creation_time_ns = start.elapsed().as_nanos() as u64;
1307
1308            Self {
1309                n_qubits,
1310                block_zero,
1311                block_last,
1312                creation_time_ns,
1313            }
1314        }
1315
1316        /// Create GHZ state with Graham's number of qubits.
1317        ///
1318        /// # Example
1319        ///
1320        /// ```
1321        /// use tileuniverse_quantum::SymbolicGhzState;
1322        ///
1323        /// let ghz = SymbolicGhzState::graham();
1324        /// assert!((ghz.verify().fidelity - 1.0).abs() < 1e-10);
1325        /// ```
1326        pub fn graham() -> Self {
1327            Self::new(SymbolicNumber::Graham)
1328        }
1329
1330        /// Create GHZ state with TREE(n) qubits.
1331        pub fn tree(n: u64) -> Self {
1332            Self::new(SymbolicNumber::Tree(n))
1333        }
1334
1335        /// Create GHZ state with a formal infinity label (aleph_0).
1336        pub fn infinite() -> Self {
1337            Self::new(SymbolicNumber::Infinity)
1338        }
1339
1340        /// Create GHZ state with Knuth arrow notation: a↑↑↑...↑b.
1341        pub fn knuth_arrow(a: u64, arrows: u64, b: u64) -> Self {
1342            Self::new(SymbolicNumber::KnuthArrow {
1343                a,
1344                arrows,
1345                b: Box::new(SymbolicNumber::Literal(b)),
1346            })
1347        }
1348
1349        /// Create GHZ state with tetration: base^^height.
1350        pub fn tetration(base: u64, height: u64) -> Self {
1351            Self::new(SymbolicNumber::Tetration {
1352                base,
1353                height: Box::new(SymbolicNumber::Literal(height)),
1354            })
1355        }
1356
1357        /// Verify GHZ state fidelity — O(1) regardless of symbolic size.
1358        ///
1359        /// # Example
1360        ///
1361        /// ```
1362        /// use tileuniverse_quantum::SymbolicGhzState;
1363        ///
1364        /// let ghz = SymbolicGhzState::new(tileuniverse_quantum::SymbolicNumber::Literal(1000));
1365        /// let v = ghz.verify();
1366        /// assert!((v.fidelity - 1.0).abs() < 1e-10);
1367        /// assert_eq!(v.amp_zero_state, v.amp_one_state);
1368        /// assert!((v.amp_zero_state - 0.707107).abs() < 1e-4);
1369        /// ```
1370        pub fn verify(&self) -> SymbolicGhzVerification {
1371            let expected_amp = std::f64::consts::FRAC_1_SQRT_2;
1372            let terminal_addr = terminal_local_addr_for_symbolic_number(&self.n_qubits);
1373
1374            let amp0_complex = self.block_zero.get(0);
1375            let amp_last_complex = self.block_last.get(terminal_addr);
1376            let amp0 = amp0_complex.norm();
1377            let amp_last = amp_last_complex.norm();
1378
1379            let overlap = Complex64::new(
1380                expected_amp * (amp0_complex.re + amp_last_complex.re),
1381                expected_amp * (amp0_complex.im + amp_last_complex.im),
1382            );
1383            let fidelity = overlap.norm_squared();
1384
1385            let mut max_spurious = 0.0f64;
1386
1387            for addr in 1..BLOCK_SIZE {
1388                let mag = self.block_zero.get(addr).norm();
1389                if mag > max_spurious {
1390                    max_spurious = mag;
1391                }
1392            }
1393
1394            for addr in 0..BLOCK_SIZE {
1395                if addr == terminal_addr {
1396                    continue;
1397                }
1398
1399                let mag = self.block_last.get(addr).norm();
1400                if mag > max_spurious {
1401                    max_spurious = mag;
1402                }
1403            }
1404
1405            SymbolicGhzVerification {
1406                n_qubits: self.n_qubits.clone(),
1407                amp_zero_state: amp0,
1408                amp_one_state: amp_last,
1409                expected_amplitude: expected_amp,
1410                fidelity,
1411                max_spurious,
1412                size_class: self.n_qubits.size_class().to_string(),
1413            }
1414        }
1415
1416        /// Reported struct size in bytes for this symbolic endpoint representation.
1417        pub fn memory_bytes(&self) -> usize {
1418            std::mem::size_of::<Self>()
1419        }
1420    }
1421
1422    impl std::fmt::Display for SymbolicGhzState {
1423        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1424            write!(f, "|GHZ_({})>", self.n_qubits)
1425        }
1426    }
1427
1428    /// Verification results for SymbolicGhzState
1429    #[derive(Debug, Clone)]
1430    pub struct SymbolicGhzVerification {
1431        pub n_qubits: SymbolicNumber,
1432        pub amp_zero_state: f64,
1433        pub amp_one_state: f64,
1434        pub expected_amplitude: f64,
1435        pub fidelity: f64,
1436        pub max_spurious: f64,
1437        pub size_class: String,
1438    }
1439
1440    impl std::fmt::Display for SymbolicGhzVerification {
1441        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1442            writeln!(f, "Symbolic GHZ Verification [{}]", self.size_class)?;
1443            writeln!(f, "  Qubits: {}", self.n_qubits)?;
1444            writeln!(
1445                f,
1446                "  |00...0> amplitude: {:.6} (expected {:.6})",
1447                self.amp_zero_state, self.expected_amplitude
1448            )?;
1449            writeln!(
1450                f,
1451                "  |11...1> amplitude: {:.6} (expected {:.6})",
1452                self.amp_one_state, self.expected_amplitude
1453            )?;
1454            writeln!(f, "  Fidelity:           {:.6}", self.fidelity)?;
1455            writeln!(f, "  Max spurious:       {:.6}", self.max_spurious)?;
1456
1457            match &self.n_qubits {
1458                SymbolicNumber::Graham => {
1459                    writeln!(f, "  ")?;
1460                    writeln!(f, "  Graham's number is so large that:")?;
1461                    writeln!(f, "    - It cannot be written in standard notation")?;
1462                    writeln!(f, "    - Even the NUMBER OF DIGITS cannot be written")?;
1463                    writeln!(f, "    - It requires Knuth's up-arrow notation")?;
1464                    writeln!(f, "    - g_64 where g_1 = 3↑↑↑↑3, g_n = 3↑^(g_{{n-1}})3")?;
1465                }
1466                SymbolicNumber::Tree(n) => {
1467                    writeln!(f, "  ")?;
1468                    writeln!(f, "  TREE({}) grows faster than Graham's number!", n)?;
1469                    writeln!(f, "    - TREE(1) = 1")?;
1470                    writeln!(f, "    - TREE(2) = 3")?;
1471                    writeln!(f, "    - TREE(3) > g_64 (Graham's number)")?;
1472                }
1473                SymbolicNumber::Infinity => {
1474                    writeln!(f, "  ")?;
1475                    writeln!(f, "  Formal infinity label (aleph_0).")?;
1476                    writeln!(
1477                        f,
1478                        "  This is a symbolic endpoint object, not an analytic infinite-Hilbert-space model."
1479                    )?;
1480                }
1481                _ => {}
1482            }
1483
1484            Ok(())
1485        }
1486    }
1487
1488    /// Create a GHZ state with Graham's number of qubits
1489    pub fn create_graham_ghz() -> SymbolicGhzState {
1490        SymbolicGhzState::graham()
1491    }
1492
1493    /// Create a GHZ state with TREE(n) qubits
1494    pub fn create_tree_ghz(n: u64) -> SymbolicGhzState {
1495        SymbolicGhzState::tree(n)
1496    }
1497
1498    /// Create a GHZ state with a formal infinity label
1499    pub fn create_infinite_ghz() -> SymbolicGhzState {
1500        SymbolicGhzState::infinite()
1501    }
1502
1503    /// Create a GHZ state with symbolic qubit count
1504    pub fn create_symbolic_ghz(n_qubits: SymbolicNumber) -> SymbolicGhzState {
1505        SymbolicGhzState::new(n_qubits)
1506    }
1507
1508    // === SymbolicAmplitude ===
1509
1510    /// Symbolic representation of quantum amplitudes.
1511    ///
1512    /// For W-states with symbolic qubit count n, the amplitude 1/sqrt(n) cannot be
1513    /// computed numerically. Instead, we represent it symbolically and prove
1514    /// normalization properties algebraically.
1515    #[derive(Clone, Debug)]
1516    pub enum SymbolicAmplitude {
1517        /// Concrete f64 value
1518        Real(f64),
1519        /// 1/sqrt(n) where n is symbolic
1520        ReciprocalSqrt(SymbolicNumber),
1521        /// 1/sqrt(C(n,k)) where C(n,k) is a symbolic binomial coefficient
1522        ReciprocalSqrtBinomial(SymbolicBinomial),
1523        /// sqrt(k/n) — for generalized Dicke states
1524        SqrtFraction {
1525            numerator: SymbolicNumber,
1526            denominator: SymbolicNumber,
1527        },
1528        /// Product of symbolic amplitudes
1529        Product(Vec<SymbolicAmplitude>),
1530        /// Zero amplitude
1531        Zero,
1532    }
1533
1534    impl SymbolicAmplitude {
1535        /// Create 1/sqrt(n)
1536        pub fn reciprocal_sqrt(n: SymbolicNumber) -> Self {
1537            Self::ReciprocalSqrt(n)
1538        }
1539
1540        /// Create sqrt(k/n)
1541        pub fn sqrt_fraction(numerator: SymbolicNumber, denominator: SymbolicNumber) -> Self {
1542            Self::SqrtFraction {
1543                numerator,
1544                denominator,
1545            }
1546        }
1547
1548        /// Get magnitude squared symbolically: |1/sqrt(n)|^2 = 1/n
1549        pub fn magnitude_squared_symbolic(&self) -> Option<SymbolicFraction> {
1550            match self {
1551                Self::Real(r) => Some(SymbolicFraction {
1552                    numerator: SymbolicNumber::Big(num_bigint::BigUint::from(
1553                        (r * r * 1e15) as u64,
1554                    )),
1555                    denominator: SymbolicNumber::Big(num_bigint::BigUint::from(
1556                        1_000_000_000_000_000u64,
1557                    )),
1558                }),
1559                Self::ReciprocalSqrt(n) => Some(SymbolicFraction::one_over(n.clone())),
1560                Self::ReciprocalSqrtBinomial(binom) => Some(SymbolicFraction {
1561                    numerator: SymbolicNumber::Literal(1),
1562                    denominator: SymbolicNumber::Named {
1563                        name: binom.to_notation(),
1564                        description: format!("Binomial coefficient {}", binom.to_notation()),
1565                    },
1566                }),
1567                Self::SqrtFraction {
1568                    numerator,
1569                    denominator,
1570                } => Some(SymbolicFraction {
1571                    numerator: numerator.clone(),
1572                    denominator: denominator.clone(),
1573                }),
1574                Self::Zero => Some(SymbolicFraction {
1575                    numerator: SymbolicNumber::Literal(0),
1576                    denominator: SymbolicNumber::Literal(1),
1577                }),
1578                Self::Product(factors) => {
1579                    if factors.len() == 1 {
1580                        factors[0].magnitude_squared_symbolic()
1581                    } else {
1582                        None
1583                    }
1584                }
1585            }
1586        }
1587
1588        /// Try to evaluate to f64 if possible
1589        ///
1590        /// # Example
1591        ///
1592        /// ```
1593        /// use tileuniverse_quantum::{SymbolicAmplitude, SymbolicNumber};
1594        ///
1595        /// let amp = SymbolicAmplitude::reciprocal_sqrt(SymbolicNumber::Literal(4));
1596        /// let val = amp.try_evaluate().unwrap();
1597        /// assert!((val - 0.5).abs() < 1e-10);
1598        /// ```
1599        pub fn try_evaluate(&self) -> Option<f64> {
1600            match self {
1601                Self::Real(r) => Some(*r),
1602                Self::ReciprocalSqrt(n) => match n {
1603                    SymbolicNumber::Literal(lit) => Some(1.0 / (*lit as f64).sqrt()),
1604                    SymbolicNumber::Big(big) => {
1605                        if big.bits() < 53 {
1606                            let val: u64 = big.try_into().ok()?;
1607                            Some(1.0 / (val as f64).sqrt())
1608                        } else {
1609                            None
1610                        }
1611                    }
1612                    _ => None,
1613                },
1614                Self::ReciprocalSqrtBinomial(binom) => {
1615                    let binom_val = binom.try_evaluate()?;
1616                    if binom_val == 0 {
1617                        return None;
1618                    }
1619                    Some(1.0 / (binom_val as f64).sqrt())
1620                }
1621                Self::SqrtFraction {
1622                    numerator,
1623                    denominator,
1624                } => {
1625                    let num = match numerator {
1626                        SymbolicNumber::Literal(n) => *n as f64,
1627                        _ => return None,
1628                    };
1629                    let denom = match denominator {
1630                        SymbolicNumber::Literal(d) => *d as f64,
1631                        _ => return None,
1632                    };
1633                    Some((num / denom).sqrt())
1634                }
1635                Self::Zero => Some(0.0),
1636                Self::Product(factors) => {
1637                    let mut result = 1.0;
1638                    for factor in factors {
1639                        result *= factor.try_evaluate()?;
1640                    }
1641                    Some(result)
1642                }
1643            }
1644        }
1645
1646        /// Human-readable representation
1647        pub fn to_notation(&self) -> String {
1648            match self {
1649                Self::Real(r) => format!("{:.6}", r),
1650                Self::ReciprocalSqrt(n) => format!("1/sqrt({})", n.to_notation()),
1651                Self::ReciprocalSqrtBinomial(binom) => format!("1/sqrt{}", binom.to_notation()),
1652                Self::SqrtFraction {
1653                    numerator,
1654                    denominator,
1655                } => format!(
1656                    "sqrt({}/{})",
1657                    numerator.to_notation(),
1658                    denominator.to_notation()
1659                ),
1660                Self::Zero => "0".to_string(),
1661                Self::Product(factors) => {
1662                    let parts: Vec<String> = factors.iter().map(|f| f.to_notation()).collect();
1663                    parts.join(" x ")
1664                }
1665            }
1666        }
1667    }
1668
1669    impl std::fmt::Display for SymbolicAmplitude {
1670        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1671            write!(f, "{}", self.to_notation())
1672        }
1673    }
1674
1675    // === SymbolicFraction ===
1676
1677    /// Symbolic fraction for probability calculations.
1678    ///
1679    /// Used to represent probabilities like 1/n where n is Graham's number.
1680    #[derive(Clone, Debug)]
1681    pub struct SymbolicFraction {
1682        pub numerator: SymbolicNumber,
1683        pub denominator: SymbolicNumber,
1684    }
1685
1686    impl SymbolicFraction {
1687        /// Create 1/n
1688        pub fn one_over(n: SymbolicNumber) -> Self {
1689            Self {
1690                numerator: SymbolicNumber::Literal(1),
1691                denominator: n,
1692            }
1693        }
1694
1695        /// Create k/n
1696        pub fn new(numerator: SymbolicNumber, denominator: SymbolicNumber) -> Self {
1697            Self {
1698                numerator,
1699                denominator,
1700            }
1701        }
1702
1703        /// Human-readable notation
1704        pub fn to_notation(&self) -> String {
1705            match (&self.numerator, &self.denominator) {
1706                (SymbolicNumber::Literal(1), d) => format!("1/{}", d.to_notation()),
1707                (n, d) => format!("{}/{}", n.to_notation(), d.to_notation()),
1708            }
1709        }
1710
1711        /// Try to evaluate to f64 if possible
1712        pub fn try_evaluate(&self) -> Option<f64> {
1713            let num = match &self.numerator {
1714                SymbolicNumber::Literal(n) => *n as f64,
1715                SymbolicNumber::Big(big) if big.bits() < 53 => {
1716                    let val: u64 = big.try_into().ok()?;
1717                    val as f64
1718                }
1719                _ => return None,
1720            };
1721            let denom = match &self.denominator {
1722                SymbolicNumber::Literal(d) => *d as f64,
1723                SymbolicNumber::Big(big) if big.bits() < 53 => {
1724                    let val: u64 = big.try_into().ok()?;
1725                    val as f64
1726                }
1727                _ => return None,
1728            };
1729            Some(num / denom)
1730        }
1731
1732        /// Check if this fraction equals 1 (algebraically)
1733        ///
1734        /// # Example
1735        ///
1736        /// ```
1737        /// use tileuniverse_quantum::{SymbolicFraction, SymbolicNumber};
1738        ///
1739        /// let frac1 = SymbolicFraction::new(
1740        ///     SymbolicNumber::Literal(42),
1741        ///     SymbolicNumber::Literal(42),
1742        /// );
1743        /// assert!(frac1.is_one());
1744        ///
1745        /// let frac2 = SymbolicFraction::one_over(SymbolicNumber::Graham);
1746        /// let frac3 = SymbolicFraction::new(
1747        ///     SymbolicNumber::Graham.clone(),
1748        ///     SymbolicNumber::Graham,
1749        /// );
1750        /// assert!(frac3.is_one());
1751        /// ```
1752        pub fn is_one(&self) -> bool {
1753            match (&self.numerator, &self.denominator) {
1754                (SymbolicNumber::Literal(a), SymbolicNumber::Literal(b)) => a == b,
1755                (SymbolicNumber::Big(a), SymbolicNumber::Big(b)) => a == b,
1756                (SymbolicNumber::Graham, SymbolicNumber::Graham) => true,
1757                (SymbolicNumber::Tree(a), SymbolicNumber::Tree(b)) => a == b,
1758                (SymbolicNumber::Infinity, SymbolicNumber::Infinity) => true,
1759                (SymbolicNumber::Rayo, SymbolicNumber::Rayo) => true,
1760                _ => false,
1761            }
1762        }
1763    }
1764
1765    impl std::fmt::Display for SymbolicFraction {
1766        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1767            write!(f, "{}", self.to_notation())
1768        }
1769    }
1770
1771    // === SymbolicEntropy ===
1772
1773    /// Symbolic representation of entropy.
1774    ///
1775    /// For W-states, the entanglement entropy is log_2(n) where n is the qubit count.
1776    #[derive(Clone, Debug)]
1777    pub enum SymbolicEntropy {
1778        /// log_2(n) where n is symbolic
1779        Log2(SymbolicNumber),
1780        /// Concrete value in bits
1781        Bits(f64),
1782        /// Infinite entropy
1783        Infinite,
1784    }
1785
1786    impl SymbolicEntropy {
1787        /// Create log_2(n)
1788        pub fn log2(n: SymbolicNumber) -> Self {
1789            match n {
1790                SymbolicNumber::Infinity => Self::Infinite,
1791                SymbolicNumber::Literal(lit) => Self::Bits((lit as f64).log2()),
1792                _ => Self::Log2(n),
1793            }
1794        }
1795
1796        /// Human-readable notation
1797        pub fn to_notation(&self) -> String {
1798            match self {
1799                Self::Log2(n) => format!("log_2({})", n.to_notation()),
1800                Self::Bits(b) => format!("{:.3} bits", b),
1801                Self::Infinite => "infty bits".to_string(),
1802            }
1803        }
1804
1805        /// Try to evaluate to f64 if possible
1806        pub fn try_evaluate(&self) -> Option<f64> {
1807            match self {
1808                Self::Bits(b) => Some(*b),
1809                Self::Log2(n) => match n {
1810                    SymbolicNumber::Literal(lit) => Some((*lit as f64).log2()),
1811                    SymbolicNumber::Big(big) => Some(big.bits() as f64),
1812                    _ => None,
1813                },
1814                Self::Infinite => None,
1815            }
1816        }
1817    }
1818
1819    impl std::fmt::Display for SymbolicEntropy {
1820        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1821            write!(f, "{}", self.to_notation())
1822        }
1823    }
1824
1825    // === SymbolicBinomial ===
1826
1827    /// Symbolic binomial coefficient C(n,k) = n!/(k!(n-k)!)
1828    ///
1829    /// For Dicke states |D_n^k>, we need C(n,k) which is the number of basis states.
1830    ///
1831    /// # Properties
1832    ///
1833    /// - C(n, 0) = 1
1834    /// - C(n, 1) = n
1835    /// - C(n, n) = 1
1836    /// - C(n, 2) = n(n-1)/2
1837    /// - C(infty, k) = infty for any finite k > 0
1838    ///
1839    /// # Example
1840    ///
1841    /// ```
1842    /// use tileuniverse_quantum::SymbolicBinomial;
1843    ///
1844    /// let binom = SymbolicBinomial::new(
1845    ///     tileuniverse_quantum::SymbolicNumber::Literal(10),
1846    ///     tileuniverse_quantum::SymbolicNumber::Literal(3),
1847    /// );
1848    /// assert_eq!(binom.try_evaluate(), Some(120));
1849    /// assert_eq!(binom.to_notation(), "C(10, 3)");
1850    /// ```
1851    #[derive(Clone, Debug)]
1852    pub struct SymbolicBinomial {
1853        /// Number of items to choose from (n)
1854        pub n: SymbolicNumber,
1855        /// Number of items to choose (k)
1856        pub k: SymbolicNumber,
1857    }
1858
1859    impl SymbolicBinomial {
1860        /// Create a new symbolic binomial coefficient C(n, k)
1861        pub fn new(n: SymbolicNumber, k: SymbolicNumber) -> Self {
1862            Self { n, k }
1863        }
1864
1865        /// Try to evaluate to a concrete u128 if possible
1866        ///
1867        /// # Example
1868        ///
1869        /// ```
1870        /// use tileuniverse_quantum::SymbolicBinomial;
1871        /// use tileuniverse_quantum::SymbolicNumber;
1872        ///
1873        /// let binom = SymbolicBinomial::new(SymbolicNumber::Literal(20), SymbolicNumber::Literal(10));
1874        /// assert_eq!(binom.try_evaluate(), Some(184756));
1875        /// ```
1876        pub fn try_evaluate(&self) -> Option<u128> {
1877            let n_val = match &self.n {
1878                SymbolicNumber::Literal(n) => *n as u128,
1879                SymbolicNumber::Big(n) => {
1880                    if n.bits() <= 64 {
1881                        n.to_u64_digits().first().map(|&d| d as u128)?
1882                    } else {
1883                        return None;
1884                    }
1885                }
1886                _ => return None,
1887            };
1888
1889            let k_val = match &self.k {
1890                SymbolicNumber::Literal(k) => *k as u128,
1891                SymbolicNumber::Big(k) => {
1892                    if k.bits() <= 64 {
1893                        k.to_u64_digits().first().map(|&d| d as u128)?
1894                    } else {
1895                        return None;
1896                    }
1897                }
1898                _ => return None,
1899            };
1900
1901            if k_val > n_val {
1902                return Some(0);
1903            }
1904
1905            let k_val = k_val.min(n_val - k_val);
1906
1907            if k_val == 0 {
1908                return Some(1);
1909            }
1910
1911            let mut result: u128 = 1;
1912            for i in 0..k_val {
1913                result = result.checked_mul(n_val - i)?;
1914                result /= i + 1;
1915            }
1916            Some(result)
1917        }
1918
1919        /// Simplify the binomial coefficient to a canonical form.
1920        ///
1921        /// Identifies special cases: C(n,0)=1, C(n,1)=n, C(n,2)=n(n-1)/2, etc.
1922        pub fn simplify(&self) -> SimplifiedBinomial {
1923            if let SymbolicNumber::Literal(0) = &self.k {
1924                return SimplifiedBinomial::One;
1925            }
1926            if let SymbolicNumber::Big(k) = &self.k {
1927                if k == &num_bigint::BigUint::from(0u32) {
1928                    return SimplifiedBinomial::One;
1929                }
1930            }
1931
1932            if self.n.structurally_equal(&self.k) {
1933                return SimplifiedBinomial::One;
1934            }
1935
1936            if matches!(&self.n, SymbolicNumber::Infinity) {
1937                if !matches!(&self.k, SymbolicNumber::Infinity) {
1938                    return SimplifiedBinomial::Infinite;
1939                }
1940            }
1941
1942            if let SymbolicNumber::Literal(1) = &self.k {
1943                return SimplifiedBinomial::N(self.n.clone());
1944            }
1945            if let SymbolicNumber::Big(k) = &self.k {
1946                if k == &num_bigint::BigUint::from(1u32) {
1947                    return SimplifiedBinomial::N(self.n.clone());
1948                }
1949            }
1950
1951            if let SymbolicNumber::Literal(2) = &self.k {
1952                return SimplifiedBinomial::NChoose2(self.n.clone());
1953            }
1954            if let SymbolicNumber::Big(k) = &self.k {
1955                if k == &num_bigint::BigUint::from(2u32) {
1956                    return SimplifiedBinomial::NChoose2(self.n.clone());
1957                }
1958            }
1959
1960            if let (SymbolicNumber::Literal(n), SymbolicNumber::Literal(k)) = (&self.n, &self.k) {
1961                if k > n {
1962                    return SimplifiedBinomial::Zero;
1963                }
1964                if *k == *n - 1 {
1965                    return SimplifiedBinomial::N(self.n.clone());
1966                }
1967            }
1968
1969            SimplifiedBinomial::General(self.clone())
1970        }
1971
1972        /// Human-readable notation: C(n, k)
1973        pub fn to_notation(&self) -> String {
1974            format!("C({}, {})", self.n.to_notation(), self.k.to_notation())
1975        }
1976
1977        /// Is this binomial equal to 1? (k=0 or k=n)
1978        pub fn is_one(&self) -> bool {
1979            matches!(self.simplify(), SimplifiedBinomial::One)
1980        }
1981
1982        /// Is this binomial equal to n? (k=1 or k=n-1)
1983        pub fn is_n(&self) -> bool {
1984            matches!(self.simplify(), SimplifiedBinomial::N(_))
1985        }
1986
1987        /// Is this binomial infinite?
1988        pub fn is_infinite(&self) -> bool {
1989            matches!(self.simplify(), SimplifiedBinomial::Infinite)
1990        }
1991
1992        /// Is this binomial zero? (k > n for concrete values)
1993        pub fn is_zero(&self) -> bool {
1994            matches!(self.simplify(), SimplifiedBinomial::Zero)
1995        }
1996    }
1997
1998    impl std::fmt::Display for SymbolicBinomial {
1999        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2000            write!(f, "{}", self.to_notation())
2001        }
2002    }
2003
2004    /// Result of simplifying a binomial coefficient
2005    #[derive(Clone, Debug)]
2006    pub enum SimplifiedBinomial {
2007        /// Exactly 1 (k=0 or k=n)
2008        One,
2009        /// Exactly n (k=1 or k=n-1)
2010        N(SymbolicNumber),
2011        /// n(n-1)/2 for k=2 (triangular number)
2012        NChoose2(SymbolicNumber),
2013        /// General C(n,k) — cannot simplify further
2014        General(SymbolicBinomial),
2015        /// Infinite (n=infty and k is finite nonzero)
2016        Infinite,
2017        /// Zero (k > n, which is invalid)
2018        Zero,
2019    }
2020
2021    impl SimplifiedBinomial {
2022        /// Human-readable notation
2023        pub fn to_notation(&self) -> String {
2024            match self {
2025                Self::One => "1".to_string(),
2026                Self::N(n) => n.to_notation(),
2027                Self::NChoose2(n) => format!("{}({}-1)/2", n.to_notation(), n.to_notation()),
2028                Self::General(binom) => binom.to_notation(),
2029                Self::Infinite => "infty".to_string(),
2030                Self::Zero => "0".to_string(),
2031            }
2032        }
2033
2034        /// Try to evaluate to a concrete value
2035        pub fn try_evaluate(&self) -> Option<u128> {
2036            match self {
2037                Self::One => Some(1),
2038                Self::N(n) => match n {
2039                    SymbolicNumber::Literal(v) => Some(*v as u128),
2040                    SymbolicNumber::Big(b) if b.bits() <= 128 => {
2041                        let digits = b.to_u64_digits();
2042                        if digits.len() == 1 {
2043                            Some(digits[0] as u128)
2044                        } else if digits.len() == 2 {
2045                            Some(digits[0] as u128 | ((digits[1] as u128) << 64))
2046                        } else {
2047                            None
2048                        }
2049                    }
2050                    _ => None,
2051                },
2052                Self::NChoose2(n) => {
2053                    if let SymbolicNumber::Literal(v) = n {
2054                        Some((*v as u128) * (*v as u128 - 1) / 2)
2055                    } else {
2056                        None
2057                    }
2058                }
2059                Self::General(binom) => binom.try_evaluate(),
2060                Self::Infinite => None,
2061                Self::Zero => Some(0),
2062            }
2063        }
2064    }
2065
2066    impl std::fmt::Display for SimplifiedBinomial {
2067        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2068            write!(f, "{}", self.to_notation())
2069        }
2070    }
2071
2072    // === SymbolicWState ===
2073
2074    /// Symbolic W-state formula object for labels such as Graham's number, TREE(3),
2075    /// or a formal infinity label.
2076    ///
2077    /// W-state: `|W_n> = (|10...0> + |01...0> + ... + |00...1>) / sqrt(n)`
2078    ///
2079    /// Memory is independent of the symbolic label size. Verification is an
2080    /// algebraic consistency check of the stored formula, not a numerical state-vector check.
2081    ///
2082    /// # Example
2083    ///
2084    /// ```
2085    /// use tileuniverse_quantum::{create_graham_w, create_tree_w, create_infinite_w};
2086    ///
2087    /// // Graham's number
2088    /// let w = create_graham_w();
2089    /// let v = w.verify();
2090    /// assert!(v.is_valid);
2091    /// assert!(v.total_probability.is_one());
2092    ///
2093    /// // TREE(3)
2094    /// let w_tree = create_tree_w(3);
2095    /// assert!(w_tree.verify().is_valid);
2096    /// ```
2097    #[derive(Clone)]
2098    pub struct SymbolicWState {
2099        /// Number of qubits (symbolic)
2100        pub n_qubits: SymbolicNumber,
2101        /// Amplitude for each basis state: 1/sqrt(n) (symbolic)
2102        pub amplitude: SymbolicAmplitude,
2103        /// Creation time in nanoseconds
2104        pub creation_time_ns: u64,
2105    }
2106
2107    impl SymbolicWState {
2108        /// Create W-state with symbolic qubit count.
2109        ///
2110        /// This is O(1) — we never iterate over n elements!
2111        pub fn new(n_qubits: SymbolicNumber) -> Self {
2112            assert!(!n_qubits.is_zero(), "W-state requires at least one qubit");
2113
2114            let start = std::time::Instant::now();
2115            let amplitude = SymbolicAmplitude::ReciprocalSqrt(n_qubits.clone());
2116            Self {
2117                n_qubits,
2118                amplitude,
2119                creation_time_ns: start.elapsed().as_nanos() as u64,
2120            }
2121        }
2122
2123        /// Create W-state with Graham's number of qubits
2124        pub fn graham() -> Self {
2125            Self::new(SymbolicNumber::Graham)
2126        }
2127
2128        /// Create W-state with TREE(n) qubits
2129        pub fn tree(n: u64) -> Self {
2130            Self::new(SymbolicNumber::Tree(n))
2131        }
2132
2133        /// Create W-state with a formal infinity label (aleph_0)
2134        pub fn infinite() -> Self {
2135            Self::new(SymbolicNumber::Infinity)
2136        }
2137
2138        /// Create W-state with Knuth arrow notation: a↑↑↑...↑b qubits
2139        pub fn knuth_arrow(a: u64, arrows: u64, b: u64) -> Self {
2140            Self::new(SymbolicNumber::KnuthArrow {
2141                a,
2142                arrows,
2143                b: Box::new(SymbolicNumber::Literal(b)),
2144            })
2145        }
2146
2147        /// Create W-state with tetration: base^^height qubits
2148        pub fn tetration(base: u64, height: u64) -> Self {
2149            Self::new(SymbolicNumber::Tetration {
2150                base,
2151                height: Box::new(SymbolicNumber::Literal(height)),
2152            })
2153        }
2154
2155        /// Verify W-state properties algebraically (O(1)).
2156        ///
2157        /// # Example
2158        ///
2159        /// ```
2160        /// use tileuniverse_quantum::{create_graham_w, SymbolicNumber, SymbolicWState};
2161        ///
2162        /// let w = SymbolicWState::new(SymbolicNumber::Literal(100));
2163        /// let v = w.verify();
2164        /// assert!(v.is_valid);
2165        /// assert!(v.total_probability.is_one());
2166        ///
2167        /// // For literal 100, amplitude should be evaluable
2168        /// let amp_val = v.amplitude.try_evaluate().unwrap();
2169        /// let expected = 1.0 / 100.0_f64.sqrt();
2170        /// assert!((amp_val - expected).abs() < 1e-10);
2171        /// ```
2172        pub fn verify(&self) -> SymbolicWVerification {
2173            let amplitude = self.amplitude.clone();
2174            let prob_per_state = SymbolicFraction::one_over(self.n_qubits.clone());
2175            let total_probability =
2176                SymbolicFraction::new(self.n_qubits.clone(), self.n_qubits.clone());
2177            let per_qubit_probability = SymbolicFraction::one_over(self.n_qubits.clone());
2178            let entanglement_entropy = SymbolicEntropy::log2(self.n_qubits.clone());
2179
2180            SymbolicWVerification {
2181                n_qubits: self.n_qubits.clone(),
2182                amplitude,
2183                num_nonzero_amplitudes: self.n_qubits.clone(),
2184                probability_per_state: prob_per_state,
2185                total_probability,
2186                per_qubit_probability,
2187                entanglement_entropy,
2188                size_class: self.n_qubits.size_class().to_string(),
2189                is_valid: true,
2190            }
2191        }
2192
2193        /// Get the symbolic entanglement entropy: log_2(n)
2194        pub fn entanglement_entropy(&self) -> SymbolicEntropy {
2195            SymbolicEntropy::log2(self.n_qubits.clone())
2196        }
2197
2198        /// Get measurement probability for any single qubit: 1/n
2199        pub fn measurement_probability(&self) -> SymbolicFraction {
2200            SymbolicFraction::one_over(self.n_qubits.clone())
2201        }
2202
2203        /// Reported struct size in bytes for this symbolic formula object.
2204        pub fn memory_bytes(&self) -> usize {
2205            std::mem::size_of::<Self>()
2206        }
2207
2208        /// Get the number of qubits
2209        pub fn n_qubits(&self) -> &SymbolicNumber {
2210            &self.n_qubits
2211        }
2212
2213        /// Get creation time in nanoseconds
2214        pub fn creation_time_ns(&self) -> u64 {
2215            self.creation_time_ns
2216        }
2217    }
2218
2219    impl std::fmt::Display for SymbolicWState {
2220        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2221            write!(f, "|W_({})>", self.n_qubits)
2222        }
2223    }
2224
2225    // === SymbolicWVerification ===
2226
2227    /// Verification result for SymbolicWState.
2228    #[derive(Debug, Clone)]
2229    pub struct SymbolicWVerification {
2230        /// Number of qubits
2231        pub n_qubits: SymbolicNumber,
2232        /// Amplitude per basis state: 1/sqrt(n)
2233        pub amplitude: SymbolicAmplitude,
2234        /// Number of non-zero amplitudes: n
2235        pub num_nonzero_amplitudes: SymbolicNumber,
2236        /// Probability per basis state: |1/sqrt(n)|^2 = 1/n
2237        pub probability_per_state: SymbolicFraction,
2238        /// Total probability: n x (1/n) = 1
2239        pub total_probability: SymbolicFraction,
2240        /// Per-qubit measurement probability: 1/n
2241        pub per_qubit_probability: SymbolicFraction,
2242        /// Entanglement entropy: log_2(n)
2243        pub entanglement_entropy: SymbolicEntropy,
2244        /// Size class of the qubit count
2245        pub size_class: String,
2246        /// Is the symbolic formula internally consistent for this constructed state.
2247        pub is_valid: bool,
2248    }
2249
2250    impl std::fmt::Display for SymbolicWVerification {
2251        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2252            writeln!(f, "SYMBOLIC W-STATE VERIFICATION [{}]", self.size_class)?;
2253            writeln!(f, "  Qubits (n):              {}", self.n_qubits)?;
2254            writeln!(f, "  Amplitude per state:     {}", self.amplitude)?;
2255            writeln!(
2256                f,
2257                "  Non-zero amplitudes:     {}",
2258                self.num_nonzero_amplitudes
2259            )?;
2260            writeln!(
2261                f,
2262                "  Probability per state:   {} = {}",
2263                self.probability_per_state,
2264                self.probability_per_state.to_notation()
2265            )?;
2266            writeln!(f, "  ALGEBRAIC PROOF OF NORMALIZATION:")?;
2267            writeln!(f, "    Total prob = n x |amplitude|^2")?;
2268            writeln!(
2269                f,
2270                "                    = {} x |{}|^2",
2271                self.n_qubits, self.amplitude
2272            )?;
2273            writeln!(
2274                f,
2275                "                    = {} x ({})",
2276                self.n_qubits, self.probability_per_state
2277            )?;
2278            writeln!(f, "                    = {}", self.total_probability)?;
2279            writeln!(
2280                f,
2281                "                    = 1 {} ({})",
2282                if self.total_probability.is_one() {
2283                    "v"
2284                } else {
2285                    "x"
2286                },
2287                if self.total_probability.is_one() {
2288                    "algebraically verified"
2289                } else {
2290                    "FAILED"
2291                }
2292            )?;
2293            writeln!(
2294                f,
2295                "  Entanglement entropy:    {}",
2296                self.entanglement_entropy
2297            )?;
2298            writeln!(
2299                f,
2300                "  Per-qubit probability:   {}",
2301                self.per_qubit_probability
2302            )?;
2303            writeln!(
2304                f,
2305                "  Valid:                   {}",
2306                if self.is_valid { "YES" } else { "NO" }
2307            )?;
2308            writeln!(
2309                f,
2310                "  Memory: formula object; no iteration over {} amplitudes",
2311                self.n_qubits
2312            )?;
2313
2314            match &self.n_qubits {
2315                SymbolicNumber::Graham => {
2316                    writeln!(f, "  Graham label: amplitudes are not enumerated.")?;
2317                    writeln!(
2318                        f,
2319                        "    - The stored normalization formula simplifies to 1 algebraically."
2320                    )?;
2321                }
2322                SymbolicNumber::Tree(n) => {
2323                    writeln!(f, "  TREE({}) label: amplitudes are not enumerated.", n)?;
2324                    writeln!(f, "    - Verification checks the symbolic formula.")?;
2325                }
2326                SymbolicNumber::Infinity => {
2327                    writeln!(f, "  Formal infinity label (aleph_0):")?;
2328                    writeln!(f, "    - This is a symbolic formula object.")?;
2329                    writeln!(
2330                        f,
2331                        "    - It is not a numerical construction of an infinite W-state limit."
2332                    )?;
2333                }
2334                _ => {}
2335            }
2336
2337            Ok(())
2338        }
2339    }
2340
2341    // === SymbolicDickeState ===
2342
2343    /// Symbolic Dicke state |D_n^k> for labels such as Graham's number, TREE(3),
2344    /// or a formal infinity label.
2345    ///
2346    /// Definition: `|D_n^k> = (1/sqrt(C(n,k))) x Sum |states with exactly k ones>`
2347    ///
2348    /// # Special Cases
2349    ///
2350    /// - k=0: `|D_n^0> = |00...0>` (product state)
2351    /// - k=1: `|D_n^1> = |W_n>` (W-state)
2352    /// - k=n: `|D_n^n> = |11...1>` (product state)
2353    ///
2354    /// # Example
2355    ///
2356    /// ```
2357    /// use tileuniverse_quantum::{SymbolicDickeState, SymbolicNumber};
2358    ///
2359    /// // Dicke state with Graham's number of qubits and 2 excitations
2360    /// let dicke = SymbolicDickeState::graham(2);
2361    /// let v = dicke.verify();
2362    /// assert!(v.is_valid);
2363    ///
2364    /// // C(G, 2) should simplify to n(n-1)/2
2365    /// assert!(matches!(v.simplified_count,
2366    ///     tileuniverse_quantum::SimplifiedBinomial::NChoose2(_)));
2367    /// ```
2368    #[derive(Clone)]
2369    pub struct SymbolicDickeState {
2370        /// Number of qubits (n)
2371        pub n_qubits: SymbolicNumber,
2372        /// Number of excitations (k)
2373        pub k_excitations: SymbolicNumber,
2374        /// Binomial coefficient C(n,k) — number of basis states
2375        pub num_amplitudes: SymbolicBinomial,
2376        /// Amplitude: 1/sqrt(C(n,k))
2377        pub amplitude: SymbolicAmplitude,
2378        /// Creation time in nanoseconds
2379        pub creation_time_ns: u64,
2380    }
2381
2382    impl SymbolicDickeState {
2383        /// Create a Dicke state |D_n^k>
2384        pub fn new(n: SymbolicNumber, k: SymbolicNumber) -> Self {
2385            let start = std::time::Instant::now();
2386            let num_amplitudes = SymbolicBinomial::new(n.clone(), k.clone());
2387            let amplitude = SymbolicAmplitude::ReciprocalSqrtBinomial(num_amplitudes.clone());
2388            Self {
2389                n_qubits: n,
2390                k_excitations: k,
2391                num_amplitudes,
2392                amplitude,
2393                creation_time_ns: start.elapsed().as_nanos() as u64,
2394            }
2395        }
2396
2397        /// Create |D_Graham^k> — Graham's number of qubits, k excitations
2398        pub fn graham(k: u64) -> Self {
2399            Self::new(SymbolicNumber::Graham, SymbolicNumber::Literal(k))
2400        }
2401
2402        /// Create |D_infty^k> with a formal infinity label and k excitations
2403        pub fn infinite(k: u64) -> Self {
2404            Self::new(SymbolicNumber::Infinity, SymbolicNumber::Literal(k))
2405        }
2406
2407        /// Create |D_TREE(t)^k> — TREE(t) qubits, k excitations
2408        pub fn tree(t: u64, k: u64) -> Self {
2409            Self::new(SymbolicNumber::Tree(t), SymbolicNumber::Literal(k))
2410        }
2411
2412        /// Create |D_n^1> = |W_n>
2413        pub fn as_w_state(n: SymbolicNumber) -> Self {
2414            Self::new(n, SymbolicNumber::Literal(1))
2415        }
2416
2417        /// Create |D_n^0> = |00...0> (product state)
2418        pub fn ground_state(n: SymbolicNumber) -> Self {
2419            Self::new(n, SymbolicNumber::Literal(0))
2420        }
2421
2422        /// Create |D_n^n> = |11...1> (product state)
2423        pub fn all_excited(n: SymbolicNumber) -> Self {
2424            Self::new(n.clone(), n)
2425        }
2426
2427        /// Create |D_n^(n/2)> — maximally entangled for even n
2428        pub fn half_excitations(n: SymbolicNumber) -> Self {
2429            let k = SymbolicNumber::Div {
2430                numerator: Box::new(n.clone()),
2431                denominator: Box::new(SymbolicNumber::Literal(2)),
2432            };
2433            Self::new(n, k)
2434        }
2435
2436        /// Verify Dicke state algebraically
2437        ///
2438        /// # Example
2439        ///
2440        /// ```
2441        /// use tileuniverse_quantum::{SymbolicDickeState, SymbolicNumber};
2442        ///
2443        /// let dicke = SymbolicDickeState::new(SymbolicNumber::Literal(10), SymbolicNumber::Literal(3));
2444        /// let v = dicke.verify();
2445        /// assert!(v.is_valid);
2446        /// // C(10, 3) = 120
2447        /// assert_eq!(v.simplified_count.try_evaluate(), Some(120));
2448        /// ```
2449        pub fn verify(&self) -> SymbolicDickeVerification {
2450            let simplified = self.num_amplitudes.simplify();
2451
2452            let is_w_state = matches!(&self.k_excitations, SymbolicNumber::Literal(1))
2453                || matches!(&self.k_excitations, SymbolicNumber::Big(k) if k == &num_bigint::BigUint::from(1u32));
2454
2455            let is_product_state = matches!(&self.k_excitations, SymbolicNumber::Literal(0))
2456                || matches!(&self.k_excitations, SymbolicNumber::Big(k) if k == &num_bigint::BigUint::from(0u32))
2457                || self.n_qubits.structurally_equal(&self.k_excitations);
2458
2459            SymbolicDickeVerification {
2460                n_qubits: self.n_qubits.clone(),
2461                k_excitations: self.k_excitations.clone(),
2462                num_amplitudes: self.num_amplitudes.clone(),
2463                simplified_count: simplified,
2464                amplitude: self.amplitude.clone(),
2465                total_probability: "C(n,k) x 1/C(n,k) = 1".to_string(),
2466                is_w_state,
2467                is_product_state,
2468                size_class: format!(
2469                    "{} qubits, {} excitations",
2470                    self.n_qubits.size_class(),
2471                    self.k_excitations.size_class()
2472                ),
2473                is_valid: true,
2474            }
2475        }
2476
2477        /// Apply collective lowering operator J_- symbolically
2478        ///
2479        /// J_-|D_n^k> = sqrt(k(n-k+1)) |D_n^(k-1)>
2480        pub fn apply_lowering(&self) -> Option<(SymbolicAmplitude, SymbolicDickeState)> {
2481            if matches!(&self.k_excitations, SymbolicNumber::Literal(0)) {
2482                return None;
2483            }
2484            if let SymbolicNumber::Big(k) = &self.k_excitations {
2485                if k == &num_bigint::BigUint::from(0u32) {
2486                    return None;
2487                }
2488            }
2489
2490            let new_k = SymbolicNumber::Sub {
2491                minuend: Box::new(self.k_excitations.clone()),
2492                subtrahend: Box::new(SymbolicNumber::Literal(1)),
2493            };
2494
2495            let coefficient = SymbolicAmplitude::SqrtFraction {
2496                numerator: self.k_excitations.clone(),
2497                denominator: SymbolicNumber::Literal(1),
2498            };
2499
2500            let new_state = SymbolicDickeState::new(self.n_qubits.clone(), new_k);
2501            Some((coefficient, new_state))
2502        }
2503
2504        /// Is this equivalent to a W-state? (k=1)
2505        ///
2506        /// # Example
2507        ///
2508        /// ```
2509        /// use tileuniverse_quantum::SymbolicDickeState;
2510        /// use tileuniverse_quantum::SymbolicNumber;
2511        ///
2512        /// let w_like = SymbolicDickeState::as_w_state(SymbolicNumber::Literal(100));
2513        /// assert!(w_like.is_w_state());
2514        ///
2515        /// let general = SymbolicDickeState::new(SymbolicNumber::Literal(100), SymbolicNumber::Literal(2));
2516        /// assert!(!general.is_w_state());
2517        /// ```
2518        pub fn is_w_state(&self) -> bool {
2519            matches!(&self.k_excitations, SymbolicNumber::Literal(1))
2520                || matches!(&self.k_excitations, SymbolicNumber::Big(k) if k == &num_bigint::BigUint::from(1u32))
2521        }
2522
2523        /// Is this a product state? (k=0 or k=n)
2524        ///
2525        /// # Example
2526        ///
2527        /// ```
2528        /// use tileuniverse_quantum::SymbolicDickeState;
2529        /// use tileuniverse_quantum::SymbolicNumber;
2530        ///
2531        /// assert!(SymbolicDickeState::ground_state(SymbolicNumber::Literal(100)).is_product_state());
2532        /// assert!(SymbolicDickeState::all_excited(SymbolicNumber::Literal(100)).is_product_state());
2533        /// ```
2534        pub fn is_product_state(&self) -> bool {
2535            if matches!(&self.k_excitations, SymbolicNumber::Literal(0)) {
2536                return true;
2537            }
2538            if let SymbolicNumber::Big(k) = &self.k_excitations {
2539                if k == &num_bigint::BigUint::from(0u32) {
2540                    return true;
2541                }
2542            }
2543            self.n_qubits.structurally_equal(&self.k_excitations)
2544        }
2545
2546        /// Memory in bytes (always O(1))
2547        pub fn memory_bytes(&self) -> usize {
2548            std::mem::size_of::<Self>()
2549        }
2550
2551        /// Get the number of qubits
2552        pub fn n_qubits(&self) -> &SymbolicNumber {
2553            &self.n_qubits
2554        }
2555
2556        /// Get the number of excitations
2557        pub fn k_excitations(&self) -> &SymbolicNumber {
2558            &self.k_excitations
2559        }
2560
2561        /// Get creation time in nanoseconds
2562        pub fn creation_time_ns(&self) -> u64 {
2563            self.creation_time_ns
2564        }
2565    }
2566
2567    impl std::fmt::Display for SymbolicDickeState {
2568        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2569            write!(
2570                f,
2571                "|D_{}^{}>",
2572                self.n_qubits.to_notation(),
2573                self.k_excitations.to_notation()
2574            )
2575        }
2576    }
2577
2578    // === SymbolicDickeVerification ===
2579
2580    /// Verification result for SymbolicDickeState
2581    #[derive(Debug, Clone)]
2582    pub struct SymbolicDickeVerification {
2583        /// Number of qubits (n)
2584        pub n_qubits: SymbolicNumber,
2585        /// Number of excitations (k)
2586        pub k_excitations: SymbolicNumber,
2587        /// Binomial coefficient C(n,k)
2588        pub num_amplitudes: SymbolicBinomial,
2589        /// Simplified form of C(n,k)
2590        pub simplified_count: SimplifiedBinomial,
2591        /// Amplitude per basis state: 1/sqrt(C(n,k))
2592        pub amplitude: SymbolicAmplitude,
2593        /// Total probability proof string
2594        pub total_probability: String,
2595        /// Is this a W-state (k=1)?
2596        pub is_w_state: bool,
2597        /// Is this a product state (k=0 or k=n)?
2598        pub is_product_state: bool,
2599        /// Size class description
2600        pub size_class: String,
2601        /// Is the state valid?
2602        pub is_valid: bool,
2603    }
2604
2605    impl std::fmt::Display for SymbolicDickeVerification {
2606        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2607            writeln!(
2608                f,
2609                "SYMBOLIC DICKE STATE VERIFICATION |D_{}^{}>",
2610                self.n_qubits.to_notation(),
2611                self.k_excitations.to_notation()
2612            )?;
2613            writeln!(f, "  Qubits (n):           {}", self.n_qubits)?;
2614            writeln!(f, "  Excitations (k):      {}", self.k_excitations)?;
2615            writeln!(f, "  C(n,k) = {}", self.num_amplitudes)?;
2616            writeln!(f, "  Simplified: {}", self.simplified_count)?;
2617            writeln!(f, "  Amplitude: {}", self.amplitude)?;
2618            writeln!(f, "  Total probability: {}", self.total_probability)?;
2619            if self.is_product_state {
2620                writeln!(f, "  [PRODUCT STATE] — separable, no entanglement")?;
2621            }
2622            if self.is_w_state {
2623                writeln!(f, "  [W-STATE] — k=1, equivalent to |W_n>")?;
2624            }
2625            if !self.is_product_state && !self.is_w_state {
2626                writeln!(f, "  [ENTANGLED] — general Dicke state")?;
2627            }
2628            writeln!(f, "  Memory: O(1) — no iteration over C(n,k) amplitudes!")?;
2629            writeln!(f, "  Valid: {}", if self.is_valid { "YES" } else { "NO" })?;
2630            Ok(())
2631        }
2632    }
2633
2634    // === Convenience Functions ===
2635
2636    /// Create a W-state with Graham's number of qubits
2637    ///
2638    /// # Example
2639    ///
2640    /// ```
2641    /// use tileuniverse_quantum::create_graham_w;
2642    ///
2643    /// let w = create_graham_w();
2644    /// let v = w.verify();
2645    /// assert!(v.is_valid);
2646    /// ```
2647    pub fn create_graham_w() -> SymbolicWState {
2648        SymbolicWState::graham()
2649    }
2650
2651    /// Create a W-state with TREE(n) qubits
2652    ///
2653    /// # Example
2654    ///
2655    /// ```
2656    /// use tileuniverse_quantum::create_tree_w;
2657    ///
2658    /// let w = create_tree_w(3);
2659    /// assert!(w.verify().is_valid);
2660    /// ```
2661    pub fn create_tree_w(n: u64) -> SymbolicWState {
2662        SymbolicWState::tree(n)
2663    }
2664
2665    /// Create a W-state with a formal infinity label (aleph_0)
2666    pub fn create_infinite_w() -> SymbolicWState {
2667        SymbolicWState::infinite()
2668    }
2669
2670    /// Create a W-state with symbolic qubit count
2671    pub fn create_symbolic_w(n_qubits: SymbolicNumber) -> SymbolicWState {
2672        SymbolicWState::new(n_qubits)
2673    }
2674
2675    /// Create a Dicke state with symbolic qubit and excitation counts
2676    pub fn create_symbolic_dicke(n: SymbolicNumber, k: SymbolicNumber) -> SymbolicDickeState {
2677        SymbolicDickeState::new(n, k)
2678    }
2679
2680    /// Create a Dicke state with Graham's number of qubits and k excitations
2681    ///
2682    /// # Example
2683    ///
2684    /// ```
2685    /// use tileuniverse_quantum::create_graham_dicke;
2686    ///
2687    /// let dicke = create_graham_dicke(2);
2688    /// let v = dicke.verify();
2689    /// assert!(v.is_valid);
2690    /// ```
2691    pub fn create_graham_dicke(k: u64) -> SymbolicDickeState {
2692        SymbolicDickeState::graham(k)
2693    }
2694
2695    /// Create a Dicke state with TREE(t) qubits and k excitations
2696    pub fn create_tree_dicke(t: u64, k: u64) -> SymbolicDickeState {
2697        SymbolicDickeState::tree(t, k)
2698    }
2699
2700    /// Create a Dicke state with a formal infinity label and k excitations
2701    pub fn create_infinite_dicke(k: u64) -> SymbolicDickeState {
2702        SymbolicDickeState::infinite(k)
2703    }
2704}