1use num_complex::Complex;
40use rand::{Rng, RngExt};
41use std::f64::consts::PI;
42
43use super::error::{QuantumError, QuantumResult};
44
45#[derive(Debug, Clone, PartialEq)]
49pub struct Qubit {
50 pub(crate) alpha: Complex<f64>,
52 pub(crate) beta: Complex<f64>,
54}
55
56impl Qubit {
57 pub fn new(alpha: Complex<f64>, beta: Complex<f64>) -> QuantumResult<Self> {
61 let norm_sq = alpha.norm_sqr() + beta.norm_sqr();
62 if norm_sq < 1e-15 {
63 return Err(QuantumError::ZeroStateVector);
64 }
65 let norm = norm_sq.sqrt();
66 Ok(Self {
67 alpha: alpha / norm,
68 beta: beta / norm,
69 })
70 }
71
72 pub fn new_zero() -> Self {
74 Self {
75 alpha: Complex::new(1.0, 0.0),
76 beta: Complex::new(0.0, 0.0),
77 }
78 }
79
80 pub fn new_one() -> Self {
82 Self {
83 alpha: Complex::new(0.0, 0.0),
84 beta: Complex::new(1.0, 0.0),
85 }
86 }
87
88 pub fn new_superposition(theta: f64, phi: f64) -> Self {
93 let half = theta / 2.0;
94 Self {
95 alpha: Complex::new(half.cos(), 0.0),
96 beta: Complex::from_polar(half.sin(), phi),
97 }
98 }
99
100 pub fn new_plus() -> Self {
102 let s = 1.0 / 2.0_f64.sqrt();
103 Self {
104 alpha: Complex::new(s, 0.0),
105 beta: Complex::new(s, 0.0),
106 }
107 }
108
109 pub fn new_minus() -> Self {
111 let s = 1.0 / 2.0_f64.sqrt();
112 Self {
113 alpha: Complex::new(s, 0.0),
114 beta: Complex::new(-s, 0.0),
115 }
116 }
117
118 pub fn alpha(&self) -> Complex<f64> {
120 self.alpha
121 }
122
123 pub fn beta(&self) -> Complex<f64> {
125 self.beta
126 }
127
128 pub fn prob_zero(&self) -> f64 {
130 self.alpha.norm_sqr()
131 }
132
133 pub fn prob_one(&self) -> f64 {
135 self.beta.norm_sqr()
136 }
137
138 pub fn is_normalised(&self, tol: f64) -> bool {
140 ((self.alpha.norm_sqr() + self.beta.norm_sqr()) - 1.0).abs() < tol
141 }
142
143 pub fn measure<R: Rng>(&self, rng: &mut R) -> (u8, Qubit) {
150 let p0 = self.prob_zero();
151 let sample: f64 = rng.random();
152 if sample < p0 {
153 (0, Qubit::new_zero())
154 } else {
155 (1, Qubit::new_one())
156 }
157 }
158
159 pub fn bloch_angles(&self) -> (f64, f64) {
163 let theta = 2.0 * self.alpha.norm().acos().min(PI);
164 let phi = {
165 let raw = self.beta.arg() - self.alpha.arg();
166 raw.rem_euclid(2.0 * PI)
167 };
168 (theta, phi)
169 }
170
171 pub fn to_register(&self) -> QubitRegister {
173 QubitRegister {
174 amplitudes: vec![self.alpha, self.beta],
175 n_qubits: 1,
176 }
177 }
178}
179
180impl Default for Qubit {
181 fn default() -> Self {
182 Self::new_zero()
183 }
184}
185
186impl std::fmt::Display for Qubit {
187 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188 write!(
189 f,
190 "({:.6} + {:.6}i)|0⟩ + ({:.6} + {:.6}i)|1⟩",
191 self.alpha.re, self.alpha.im, self.beta.re, self.beta.im
192 )
193 }
194}
195
196#[derive(Debug, Clone, PartialEq)]
205pub struct QubitRegister {
206 pub(crate) amplitudes: Vec<Complex<f64>>,
208 pub(crate) n_qubits: usize,
210}
211
212impl QubitRegister {
213 pub fn new(n_qubits: usize, amplitudes: Vec<Complex<f64>>) -> QuantumResult<Self> {
220 let expected_dim = 1usize
221 .checked_shl(n_qubits as u32)
222 .ok_or(QuantumError::TooManyQubits(n_qubits))?;
223 if amplitudes.len() != expected_dim {
224 return Err(QuantumError::DimensionMismatch {
225 expected: expected_dim,
226 actual: amplitudes.len(),
227 });
228 }
229 let norm_sq: f64 = amplitudes.iter().map(|a| a.norm_sqr()).sum();
230 if norm_sq < 1e-15 {
231 return Err(QuantumError::ZeroStateVector);
232 }
233 let norm = norm_sq.sqrt();
234 let normalised = amplitudes.iter().map(|a| a / norm).collect();
235 Ok(Self {
236 amplitudes: normalised,
237 n_qubits,
238 })
239 }
240
241 pub fn from_qubit(q: &Qubit) -> Self {
243 Self {
244 amplitudes: vec![q.alpha, q.beta],
245 n_qubits: 1,
246 }
247 }
248
249 pub fn new_zero_state(n_qubits: usize) -> QuantumResult<Self> {
251 if n_qubits == 0 {
252 return Err(QuantumError::InvalidQubitCount(n_qubits));
253 }
254 let dim = 1usize
255 .checked_shl(n_qubits as u32)
256 .ok_or(QuantumError::TooManyQubits(n_qubits))?;
257 let mut amps = vec![Complex::new(0.0, 0.0); dim];
258 amps[0] = Complex::new(1.0, 0.0);
259 Ok(Self {
260 amplitudes: amps,
261 n_qubits,
262 })
263 }
264
265 pub fn new_uniform_superposition(n_qubits: usize) -> QuantumResult<Self> {
269 if n_qubits == 0 {
270 return Err(QuantumError::InvalidQubitCount(n_qubits));
271 }
272 let dim = 1usize
273 .checked_shl(n_qubits as u32)
274 .ok_or(QuantumError::TooManyQubits(n_qubits))?;
275 let amp = Complex::new(1.0 / (dim as f64).sqrt(), 0.0);
276 Ok(Self {
277 amplitudes: vec![amp; dim],
278 n_qubits,
279 })
280 }
281
282 pub fn new_basis_state(n_qubits: usize, k: usize) -> QuantumResult<Self> {
284 if n_qubits == 0 {
285 return Err(QuantumError::InvalidQubitCount(n_qubits));
286 }
287 let dim = 1usize
288 .checked_shl(n_qubits as u32)
289 .ok_or(QuantumError::TooManyQubits(n_qubits))?;
290 if k >= dim {
291 return Err(QuantumError::BasisIndexOutOfRange { index: k, dim });
292 }
293 let mut amps = vec![Complex::new(0.0, 0.0); dim];
294 amps[k] = Complex::new(1.0, 0.0);
295 Ok(Self {
296 amplitudes: amps,
297 n_qubits,
298 })
299 }
300
301 pub fn n_qubits(&self) -> usize {
305 self.n_qubits
306 }
307
308 pub fn dim(&self) -> usize {
310 self.amplitudes.len()
311 }
312
313 pub fn amplitudes(&self) -> &[Complex<f64>] {
315 &self.amplitudes
316 }
317
318 pub fn amplitudes_mut(&mut self) -> &mut Vec<Complex<f64>> {
321 &mut self.amplitudes
322 }
323
324 pub fn amplitude(&self, k: usize) -> QuantumResult<Complex<f64>> {
326 self.amplitudes
327 .get(k)
328 .copied()
329 .ok_or(QuantumError::BasisIndexOutOfRange {
330 index: k,
331 dim: self.dim(),
332 })
333 }
334
335 pub fn probability(&self, k: usize) -> QuantumResult<f64> {
337 Ok(self.amplitude(k)?.norm_sqr())
338 }
339
340 pub fn probabilities(&self) -> Vec<f64> {
342 self.amplitudes.iter().map(|a| a.norm_sqr()).collect()
343 }
344
345 pub fn normalise(&mut self) -> QuantumResult<()> {
347 let norm_sq: f64 = self.amplitudes.iter().map(|a| a.norm_sqr()).sum();
348 if norm_sq < 1e-15 {
349 return Err(QuantumError::ZeroStateVector);
350 }
351 let norm = norm_sq.sqrt();
352 for a in &mut self.amplitudes {
353 *a /= norm;
354 }
355 Ok(())
356 }
357
358 pub fn is_normalised(&self, tol: f64) -> bool {
360 let norm_sq: f64 = self.amplitudes.iter().map(|a| a.norm_sqr()).sum();
361 (norm_sq - 1.0).abs() < tol
362 }
363
364 pub fn tensor_product(a: &QubitRegister, b: &QubitRegister) -> QubitRegister {
371 let n = a.n_qubits + b.n_qubits;
372 let mut amps = Vec::with_capacity(a.dim() * b.dim());
373 for &_a in &a.amplitudes {
374 for &_b in &b.amplitudes {
375 amps.push(amp_a * amp_b);
376 }
377 }
378 QubitRegister {
379 amplitudes: amps,
380 n_qubits: n,
381 }
382 }
383
384 pub fn measure_qubit<R: Rng>(
389 &self,
390 qubit_idx: usize,
391 rng: &mut R,
392 ) -> QuantumResult<(u8, QubitRegister)> {
393 if qubit_idx >= self.n_qubits {
394 return Err(QuantumError::QubitIndexOutOfRange {
395 index: qubit_idx,
396 n_qubits: self.n_qubits,
397 });
398 }
399
400 let mut prob_one: f64 = 0.0;
402 for (k, amp) in self.amplitudes.iter().enumerate() {
403 if (k >> qubit_idx) & 1 == 1 {
404 prob_one += amp.norm_sqr();
405 }
406 }
407 let sample: f64 = rng.random();
408 let outcome: u8 = if sample < prob_one { 1 } else { 0 };
409
410 let mut new_amps = self.amplitudes.clone();
412 for (k, amp) in new_amps.iter_mut().enumerate() {
413 let bit = ((k >> qubit_idx) & 1) as u8;
414 if bit != outcome {
415 *amp = Complex::new(0.0, 0.0);
416 }
417 }
418 let mut collapsed = QubitRegister {
419 amplitudes: new_amps,
420 n_qubits: self.n_qubits,
421 };
422 collapsed.normalise()?;
423 Ok((outcome, collapsed))
424 }
425
426 pub fn measure_all<R: Rng>(&self, rng: &mut R) -> Vec<u8> {
430 let probs = self.probabilities();
432 let sample: f64 = rng.random();
433 let mut cumulative = 0.0;
434 let mut outcome_index = probs.len().saturating_sub(1);
435 for (i, &p) in probs.iter().enumerate() {
436 cumulative += p;
437 if sample < cumulative {
438 outcome_index = i;
439 break;
440 }
441 }
442 (0..self.n_qubits)
444 .map(|q| ((outcome_index >> q) & 1) as u8)
445 .collect()
446 }
447
448 pub fn inner_product(&self, other: &QubitRegister) -> QuantumResult<Complex<f64>> {
450 if self.n_qubits != other.n_qubits {
451 return Err(QuantumError::DimensionMismatch {
452 expected: self.dim(),
453 actual: other.dim(),
454 });
455 }
456 let ip = self
457 .amplitudes
458 .iter()
459 .zip(other.amplitudes.iter())
460 .map(|(a, b)| b.conj() * a)
461 .sum();
462 Ok(ip)
463 }
464
465 pub fn fidelity(&self, other: &QubitRegister) -> QuantumResult<f64> {
467 let ip = self.inner_product(other)?;
468 Ok(ip.norm_sqr())
469 }
470
471 pub fn entropy(&self) -> f64 {
474 let probs = self.probabilities();
475 -probs
476 .iter()
477 .filter(|&&p| p > 1e-15)
478 .map(|&p| p * p.ln())
479 .sum::<f64>()
480 }
481}
482
483impl Default for QubitRegister {
484 fn default() -> Self {
485 Self {
487 amplitudes: vec![Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)],
488 n_qubits: 1,
489 }
490 }
491}
492
493impl std::fmt::Display for QubitRegister {
494 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
495 write!(f, "QubitRegister({} qubits) [", self.n_qubits)?;
496 for (i, amp) in self.amplitudes.iter().enumerate() {
497 if i > 0 {
498 write!(f, ", ")?;
499 }
500 if amp.im >= 0.0 {
501 write!(
502 f,
503 "|{:0>width$b}⟩: {:.4}+{:.4}i",
504 i,
505 amp.re,
506 amp.im,
507 width = self.n_qubits
508 )?;
509 } else {
510 write!(
511 f,
512 "|{:0>width$b}⟩: {:.4}{:.4}i",
513 i,
514 amp.re,
515 amp.im,
516 width = self.n_qubits
517 )?;
518 }
519 }
520 write!(f, "]")
521 }
522}
523
524#[cfg(test)]
529mod tests {
530 use super::*;
531 use rand::SeedableRng;
532 use rand_chacha::ChaCha20Rng;
533
534 const TOL: f64 = 1e-12;
535
536 #[test]
537 fn test_qubit_zero_normalised() {
538 let q = Qubit::new_zero();
539 assert!(q.is_normalised(TOL));
540 assert!((q.prob_zero() - 1.0).abs() < TOL);
541 assert!(q.prob_one().abs() < TOL);
542 }
543
544 #[test]
545 fn test_qubit_one_normalised() {
546 let q = Qubit::new_one();
547 assert!(q.is_normalised(TOL));
548 assert!(q.prob_zero().abs() < TOL);
549 assert!((q.prob_one() - 1.0).abs() < TOL);
550 }
551
552 #[test]
553 fn test_qubit_superposition() {
554 let q = Qubit::new_plus();
555 assert!(q.is_normalised(TOL));
556 assert!((q.prob_zero() - 0.5).abs() < TOL);
557 assert!((q.prob_one() - 0.5).abs() < TOL);
558 }
559
560 #[test]
561 fn test_bloch_sphere_zero() {
562 let q = Qubit::new_superposition(0.0, 0.0);
563 assert!((q.prob_zero() - 1.0).abs() < TOL);
565 }
566
567 #[test]
568 fn test_bloch_sphere_one() {
569 let q = Qubit::new_superposition(std::f64::consts::PI, 0.0);
570 assert!((q.prob_one() - 1.0).abs() < 1e-10);
572 }
573
574 #[test]
575 fn test_qubit_measure_deterministic_zero() {
576 let q = Qubit::new_zero();
577 let mut rng = ChaCha20Rng::seed_from_u64(0);
578 let (outcome, post) = q.measure(&mut rng);
579 assert_eq!(outcome, 0);
580 assert!(post.is_normalised(TOL));
581 assert!((post.prob_zero() - 1.0).abs() < TOL);
582 }
583
584 #[test]
585 fn test_qubit_measure_deterministic_one() {
586 let q = Qubit::new_one();
587 let mut rng = ChaCha20Rng::seed_from_u64(0);
588 let (outcome, post) = q.measure(&mut rng);
589 assert_eq!(outcome, 1);
590 assert!(post.is_normalised(TOL));
591 }
592
593 #[test]
594 fn test_register_zero_state() {
595 let reg = QubitRegister::new_zero_state(3).expect("valid");
596 assert_eq!(reg.n_qubits(), 3);
597 assert_eq!(reg.dim(), 8);
598 assert!((reg.probability(0).expect("ok") - 1.0).abs() < TOL);
599 }
600
601 #[test]
602 fn test_register_uniform_superposition() {
603 let reg = QubitRegister::new_uniform_superposition(2).expect("valid");
604 let p = reg.probability(0).expect("ok");
605 assert!((p - 0.25).abs() < TOL);
606 }
607
608 #[test]
609 fn test_tensor_product_dims() {
610 let r1 = QubitRegister::new_zero_state(2).expect("valid");
611 let r2 = QubitRegister::new_zero_state(3).expect("valid");
612 let combined = QubitRegister::tensor_product(&r1, &r2);
613 assert_eq!(combined.n_qubits(), 5);
614 assert_eq!(combined.dim(), 32);
615 }
616
617 #[test]
618 fn test_measure_all_basis_state() {
619 let reg = QubitRegister::new_basis_state(3, 5).expect("valid");
620 let mut rng = ChaCha20Rng::seed_from_u64(42);
621 let bits = reg.measure_all(&mut rng);
622 assert_eq!(bits, vec![1, 0, 1]);
624 }
625
626 #[test]
627 fn test_fidelity_same_state() {
628 let r = QubitRegister::new_zero_state(2).expect("valid");
629 let f = r.fidelity(&r).expect("ok");
630 assert!((f - 1.0).abs() < TOL);
631 }
632
633 #[test]
634 fn test_fidelity_orthogonal() {
635 let r0 = QubitRegister::new_basis_state(1, 0).expect("valid");
636 let r1 = QubitRegister::new_basis_state(1, 1).expect("valid");
637 let f = r0.fidelity(&r1).expect("ok");
638 assert!(f.abs() < TOL);
639 }
640}