1use rand::{Rng, RngExt};
30
31use super::error::{QuantumError, QuantumResult};
32use super::gates::{
33 apply_gate, Fredkin, Hadamard, Identity, PauliX, PauliY, PauliZ, PhaseS, PhaseSdg, PhaseShift,
34 PhaseT, PhaseTdg, QuantumGate, RotX, RotY, RotZ, Toffoli, Unitary1Q, CNOT, CU, CZ, SWAP,
35};
36use super::qubits::QubitRegister;
37
38pub struct GateOp {
44 gate: Box<dyn QuantumGate>,
46 qubits: Vec<usize>,
48}
49
50impl GateOp {
51 fn new(gate: impl QuantumGate + 'static, qubits: Vec<usize>) -> Self {
52 Self {
53 gate: Box::new(gate),
54 qubits,
55 }
56 }
57}
58
59impl std::fmt::Debug for GateOp {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 write!(f, "GateOp({}, qubits={:?})", self.gate.name(), self.qubits)
62 }
63}
64
65pub struct QuantumCircuit {
76 n_qubits: usize,
78 ops: Vec<GateOp>,
80}
81
82impl QuantumCircuit {
83 pub fn new(n_qubits: usize) -> Self {
87 Self {
88 n_qubits,
89 ops: Vec::new(),
90 }
91 }
92
93 pub fn n_qubits(&self) -> usize {
97 self.n_qubits
98 }
99
100 pub fn n_ops(&self) -> usize {
102 self.ops.len()
103 }
104
105 pub fn circuit_depth(&self) -> usize {
111 let mut layer_finish = vec![0usize; self.n_qubits];
113 let mut depth = 0usize;
114
115 for op in &self.ops {
116 let earliest = op
118 .qubits
119 .iter()
120 .map(|&q| layer_finish[q])
121 .max()
122 .unwrap_or(0);
123 let layer = earliest + 1;
124 for &q in &op.qubits {
125 layer_finish[q] = layer;
126 }
127 if layer > depth {
128 depth = layer;
129 }
130 }
131 depth
132 }
133
134 pub fn add_gate(
140 &mut self,
141 gate: impl QuantumGate + 'static,
142 qubits: &[usize],
143 ) -> QuantumResult<()> {
144 if qubits.len() != gate.n_qubits() {
145 return Err(QuantumError::GateArityMismatch {
146 gate_qubits: gate.n_qubits(),
147 supplied: qubits.len(),
148 });
149 }
150 for &q in qubits {
151 if q >= self.n_qubits {
152 return Err(QuantumError::QubitIndexOutOfRange {
153 index: q,
154 n_qubits: self.n_qubits,
155 });
156 }
157 }
158 for i in 0..qubits.len() {
160 for j in (i + 1)..qubits.len() {
161 if qubits[i] == qubits[j] {
162 return Err(QuantumError::DuplicateQubitIndex { index: qubits[i] });
163 }
164 }
165 }
166 self.ops.push(GateOp::new(gate, qubits.to_vec()));
167 Ok(())
168 }
169
170 pub fn run(&self, initial_state: QubitRegister) -> QuantumResult<QubitRegister> {
176 if initial_state.n_qubits() != self.n_qubits {
177 return Err(QuantumError::CircuitRegisterMismatch {
178 circuit_qubits: self.n_qubits,
179 register_qubits: initial_state.n_qubits(),
180 });
181 }
182 let mut state = initial_state;
183 for op in &self.ops {
184 apply_gate(&mut state, op.gate.as_ref(), &op.qubits)?;
185 }
186 Ok(state)
187 }
188
189 pub fn measure_all<R: Rng>(
193 &self,
194 state: &QubitRegister,
195 rng: &mut R,
196 ) -> QuantumResult<Vec<u8>> {
197 if state.n_qubits() != self.n_qubits {
198 return Err(QuantumError::CircuitRegisterMismatch {
199 circuit_qubits: self.n_qubits,
200 register_qubits: state.n_qubits(),
201 });
202 }
203 Ok(state.measure_all(rng))
204 }
205
206 pub fn sample<R: Rng>(
212 &self,
213 initial_state: &QubitRegister,
214 shots: usize,
215 rng: &mut R,
216 ) -> QuantumResult<Vec<Vec<u8>>> {
217 if initial_state.n_qubits() != self.n_qubits {
218 return Err(QuantumError::CircuitRegisterMismatch {
219 circuit_qubits: self.n_qubits,
220 register_qubits: initial_state.n_qubits(),
221 });
222 }
223 let final_state = self.run(initial_state.clone())?;
224 let results = (0..shots).map(|_| final_state.measure_all(rng)).collect();
225 Ok(results)
226 }
227
228 pub fn id(&mut self, q: usize) -> QuantumResult<()> {
232 self.add_gate(Identity, &[q])
233 }
234
235 pub fn x(&mut self, q: usize) -> QuantumResult<()> {
237 self.add_gate(PauliX, &[q])
238 }
239
240 pub fn y(&mut self, q: usize) -> QuantumResult<()> {
242 self.add_gate(PauliY, &[q])
243 }
244
245 pub fn z(&mut self, q: usize) -> QuantumResult<()> {
247 self.add_gate(PauliZ, &[q])
248 }
249
250 pub fn h(&mut self, q: usize) -> QuantumResult<()> {
252 self.add_gate(Hadamard, &[q])
253 }
254
255 pub fn s(&mut self, q: usize) -> QuantumResult<()> {
257 self.add_gate(PhaseS, &[q])
258 }
259
260 pub fn sdg(&mut self, q: usize) -> QuantumResult<()> {
262 self.add_gate(PhaseSdg, &[q])
263 }
264
265 pub fn t(&mut self, q: usize) -> QuantumResult<()> {
267 self.add_gate(PhaseT, &[q])
268 }
269
270 pub fn tdg(&mut self, q: usize) -> QuantumResult<()> {
272 self.add_gate(PhaseTdg, &[q])
273 }
274
275 pub fn rx(&mut self, theta: f64, q: usize) -> QuantumResult<()> {
277 self.add_gate(RotX { theta }, &[q])
278 }
279
280 pub fn ry(&mut self, theta: f64, q: usize) -> QuantumResult<()> {
282 self.add_gate(RotY { theta }, &[q])
283 }
284
285 pub fn rz(&mut self, theta: f64, q: usize) -> QuantumResult<()> {
287 self.add_gate(RotZ { theta }, &[q])
288 }
289
290 pub fn p(&mut self, lambda: f64, q: usize) -> QuantumResult<()> {
292 self.add_gate(PhaseShift { lambda }, &[q])
293 }
294
295 pub fn u(&mut self, theta: f64, phi: f64, lambda: f64, q: usize) -> QuantumResult<()> {
297 self.add_gate(Unitary1Q { theta, phi, lambda }, &[q])
298 }
299
300 pub fn cx(&mut self, control: usize, target: usize) -> QuantumResult<()> {
304 self.add_gate(CNOT, &[control, target])
305 }
306
307 pub fn cz(&mut self, control: usize, target: usize) -> QuantumResult<()> {
309 self.add_gate(CZ, &[control, target])
310 }
311
312 pub fn swap(&mut self, q0: usize, q1: usize) -> QuantumResult<()> {
314 self.add_gate(SWAP, &[q0, q1])
315 }
316
317 pub fn cu(
319 &mut self,
320 gate: impl QuantumGate + 'static,
321 control: usize,
322 target: usize,
323 ) -> QuantumResult<()> {
324 let cu_gate = CU::new(gate)?;
325 self.add_gate(cu_gate, &[control, target])
326 }
327
328 pub fn ccx(&mut self, c0: usize, c1: usize, target: usize) -> QuantumResult<()> {
332 self.add_gate(Toffoli, &[c0, c1, target])
333 }
334
335 pub fn cswap(&mut self, control: usize, t0: usize, t1: usize) -> QuantumResult<()> {
337 self.add_gate(Fredkin, &[control, t0, t1])
338 }
339
340 pub fn barrier(&mut self, qubits: &[usize]) -> QuantumResult<()> {
347 for &q in qubits {
348 self.add_gate(Identity, &[q])?;
349 }
350 Ok(())
351 }
352}
353
354impl std::fmt::Debug for QuantumCircuit {
355 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
356 write!(
357 f,
358 "QuantumCircuit({} qubits, {} ops)",
359 self.n_qubits,
360 self.ops.len()
361 )?;
362 for (i, op) in self.ops.iter().enumerate() {
363 write!(f, "\n [{i}] {} on {:?}", op.gate.name(), op.qubits)?;
364 }
365 Ok(())
366 }
367}
368
369pub fn bell_pair_circuit() -> QuantumCircuit {
378 let mut c = QuantumCircuit::new(2);
379 c.h(0)
380 .expect("h gate on qubit 0 is always valid for 2-qubit circuit");
381 c.cx(0, 1)
382 .expect("cx gate on qubits 0,1 is always valid for 2-qubit circuit");
383 c
384}
385
386pub fn ghz_circuit(n_qubits: usize) -> QuantumResult<QuantumCircuit> {
392 if n_qubits < 2 {
393 return Err(QuantumError::InvalidQubitCount(n_qubits));
394 }
395 let mut c = QuantumCircuit::new(n_qubits);
396 c.h(0)?;
397 for q in 1..n_qubits {
398 c.cx(0, q)?;
399 }
400 Ok(c)
401}
402
403pub fn qft_circuit(n_qubits: usize) -> QuantumResult<QuantumCircuit> {
415 if n_qubits == 0 {
416 return Err(QuantumError::InvalidQubitCount(n_qubits));
417 }
418 let mut c = QuantumCircuit::new(n_qubits);
419
420 for target in 0..n_qubits {
421 c.h(target)?;
423 for control in (target + 1)..n_qubits {
425 let k = (control - target + 1) as f64;
426 let lambda = std::f64::consts::PI / (2.0_f64.powi((k - 1.0) as i32));
427 c.cu(PhaseShift { lambda }, control, target)?;
428 }
429 }
430 Ok(c)
431}
432
433pub fn qft_circuit_with_swap(n_qubits: usize) -> QuantumResult<QuantumCircuit> {
438 if n_qubits == 0 {
439 return Err(QuantumError::InvalidQubitCount(n_qubits));
440 }
441 let mut c = qft_circuit(n_qubits)?;
442 for i in 0..(n_qubits / 2) {
444 c.swap(i, n_qubits - 1 - i)?;
445 }
446 Ok(c)
447}
448
449pub fn iqft_circuit(n_qubits: usize) -> QuantumResult<QuantumCircuit> {
451 if n_qubits == 0 {
452 return Err(QuantumError::InvalidQubitCount(n_qubits));
453 }
454 let mut c = QuantumCircuit::new(n_qubits);
455
456 for target in (0..n_qubits).rev() {
458 for control in (target + 1..n_qubits).rev() {
460 let k = (control - target + 1) as f64;
461 let lambda = -std::f64::consts::PI / (2.0_f64.powi((k - 1.0) as i32));
462 c.cu(PhaseShift { lambda }, control, target)?;
463 }
464 c.h(target)?;
466 }
467 Ok(c)
468}
469
470pub fn phase_estimation_circuit(
480 n_counting: usize,
481 n_target: usize,
482) -> QuantumResult<QuantumCircuit> {
483 if n_counting == 0 || n_target == 0 {
484 return Err(QuantumError::InvalidQubitCount(n_counting + n_target));
485 }
486 let n_total = n_counting + n_target;
487 let mut c = QuantumCircuit::new(n_total);
488
489 for q in 0..n_counting {
491 c.h(q)?;
492 }
493 let iqft = iqft_circuit(n_counting)?;
495 for op in &iqft.ops {
496 c.add_gate_raw(op.gate.as_ref(), &op.qubits)?;
497 }
498 Ok(c)
499}
500
501impl QuantumCircuit {
502 fn add_gate_raw(&mut self, gate: &dyn QuantumGate, qubits: &[usize]) -> QuantumResult<()> {
505 for &q in qubits {
507 if q >= self.n_qubits {
508 return Err(QuantumError::QubitIndexOutOfRange {
509 index: q,
510 n_qubits: self.n_qubits,
511 });
512 }
513 }
514 let mat_gate = MatrixGate {
515 matrix: gate.matrix(),
516 n_qubits: gate.n_qubits(),
517 name: gate.name().to_string(),
518 };
519 self.ops.push(GateOp::new(mat_gate, qubits.to_vec()));
520 Ok(())
521 }
522}
523
524struct MatrixGate {
526 matrix: ndarray::Array2<num_complex::Complex<f64>>,
527 n_qubits: usize,
528 name: String,
529}
530
531impl QuantumGate for MatrixGate {
532 fn matrix(&self) -> ndarray::Array2<num_complex::Complex<f64>> {
533 self.matrix.clone()
534 }
535 fn n_qubits(&self) -> usize {
536 self.n_qubits
537 }
538 fn name(&self) -> &str {
539 &self.name
540 }
541}
542
543#[cfg(test)]
548mod tests {
549 use super::*;
550 use num_complex::Complex;
551 use rand::SeedableRng;
552 use rand_chacha::ChaCha20Rng;
553
554 const TOL: f64 = 1e-10;
555
556 #[test]
557 fn test_bell_pair_probabilities() {
558 let circ = bell_pair_circuit();
559 let init = QubitRegister::new_zero_state(2).expect("valid");
560 let state = circ.run(init).expect("run ok");
561
562 let p00 = state.probability(0).expect("ok");
563 let p11 = state.probability(3).expect("ok");
564 let p01 = state.probability(1).expect("ok");
565 let p10 = state.probability(2).expect("ok");
566
567 assert!((p00 - 0.5).abs() < TOL, "p00={}", p00);
568 assert!((p11 - 0.5).abs() < TOL, "p11={}", p11);
569 assert!(p01.abs() < TOL, "p01={}", p01);
570 assert!(p10.abs() < TOL, "p10={}", p10);
571 }
572
573 #[test]
574 fn test_bell_pair_measurement_outcomes() {
575 let circ = bell_pair_circuit();
576 let init = QubitRegister::new_zero_state(2).expect("valid");
577 let state = circ.run(init).expect("run ok");
578 let mut rng = ChaCha20Rng::seed_from_u64(7);
579 for _ in 0..20 {
580 let bits = circ.measure_all(&state, &mut rng).expect("ok");
581 assert!(
582 bits == vec![0, 0] || bits == vec![1, 1],
583 "Bell pair must measure |00⟩ or |11⟩, got {:?}",
584 bits
585 );
586 }
587 }
588
589 #[test]
590 fn test_ghz_3qubits() {
591 let circ = ghz_circuit(3).expect("valid");
592 let init = QubitRegister::new_zero_state(3).expect("valid");
593 let state = circ.run(init).expect("run ok");
594
595 let p000 = state.probability(0).expect("ok"); let p111 = state.probability(7).expect("ok"); assert!((p000 - 0.5).abs() < TOL, "p000={}", p000);
599 assert!((p111 - 0.5).abs() < TOL, "p111={}", p111);
600 for k in [1usize, 2, 3, 4, 5, 6] {
602 let p = state.probability(k).expect("ok");
603 assert!(p.abs() < TOL, "p[{}]={}", k, p);
604 }
605 }
606
607 #[test]
608 fn test_circuit_depth_sequential() {
609 let mut c = QuantumCircuit::new(1);
611 c.x(0).expect("ok");
612 c.x(0).expect("ok");
613 assert_eq!(c.circuit_depth(), 2);
614 }
615
616 #[test]
617 fn test_circuit_depth_parallel() {
618 let mut c = QuantumCircuit::new(2);
620 c.h(0).expect("ok");
621 c.h(1).expect("ok");
622 assert_eq!(c.circuit_depth(), 1);
623 }
624
625 #[test]
626 fn test_circuit_depth_mixed() {
627 let mut c = QuantumCircuit::new(2);
629 c.h(0).expect("ok");
630 c.h(1).expect("ok");
631 c.cx(0, 1).expect("ok");
632 assert_eq!(c.circuit_depth(), 2);
633 }
634
635 #[test]
636 fn test_circuit_register_mismatch() {
637 let circ = bell_pair_circuit();
638 let wrong = QubitRegister::new_zero_state(3).expect("valid");
639 let err = circ.run(wrong);
640 assert!(matches!(
641 err,
642 Err(QuantumError::CircuitRegisterMismatch { .. })
643 ));
644 }
645
646 #[test]
647 fn test_qft_two_qubits_normalised() {
648 let circ = qft_circuit(2).expect("valid");
649 let init = QubitRegister::new_zero_state(2).expect("valid");
650 let state = circ.run(init).expect("run ok");
651 assert!(
652 state.is_normalised(1e-10),
653 "QFT output should be normalised"
654 );
655 }
656
657 #[test]
658 fn test_qft_iqft_roundtrip() {
659 let n = 3;
661 let qft = qft_circuit(n).expect("valid");
662 let iqft = iqft_circuit(n).expect("valid");
663
664 let init = QubitRegister::new_basis_state(n, 3).expect("valid");
665 let after_qft = qft.run(init.clone()).expect("qft ok");
666 let after_iqft = iqft.run(after_qft).expect("iqft ok");
667
668 let fidelity = init.fidelity(&after_iqft).expect("ok");
669 assert!(
670 (fidelity - 1.0).abs() < 1e-9,
671 "QFT·IQFT fidelity should be 1, got {}",
672 fidelity
673 );
674 }
675
676 #[test]
677 fn test_sample_returns_correct_shots() {
678 let circ = bell_pair_circuit();
679 let init = QubitRegister::new_zero_state(2).expect("valid");
680 let mut rng = ChaCha20Rng::seed_from_u64(99);
681 let shots = circ.sample(&init, 50, &mut rng).expect("ok");
682 assert_eq!(shots.len(), 50);
683 }
684
685 #[test]
686 fn test_toffoli_circuit() {
687 let mut c = QuantumCircuit::new(3);
692 c.ccx(0, 1, 2).expect("ok");
693 let init = QubitRegister::new_basis_state(3, 3).expect("valid");
694 let state = c.run(init).expect("run ok");
695 let p7 = state.probability(7).expect("ok");
696 assert!((p7 - 1.0).abs() < TOL, "Toffoli p7={}", p7);
697 }
698
699 #[test]
700 fn test_x_x_identity() {
701 let mut c = QuantumCircuit::new(1);
703 c.x(0).expect("ok");
704 c.x(0).expect("ok");
705 let init = QubitRegister::new_zero_state(1).expect("valid");
706 let state = c.run(init.clone()).expect("run ok");
707 let fidelity = init.fidelity(&state).expect("ok");
708 assert!((fidelity - 1.0).abs() < TOL);
709 }
710}