1use crate::simulator::Simulator; #[cfg(feature = "python")]
9use pyo3::exceptions::PyValueError;
10#[cfg(feature = "python")]
11use pyo3::PyResult;
12use quantrs2_circuit::builder::Circuit;
13use quantrs2_circuit::builder::Simulator as CircuitSimulator; #[cfg(feature = "python")]
15use quantrs2_core::gate::multi::{CRX, CRY, CRZ};
16#[cfg(feature = "python")]
17use quantrs2_core::gate::single::{RotationX, RotationY, RotationZ};
18use quantrs2_core::{
19 error::{QuantRS2Error, QuantRS2Result},
20 gate::GateOp,
21};
22use scirs2_core::Complex64;
23
24#[allow(unused_imports)]
26use crate::simulator::SimulatorResult;
27use crate::statevector::StateVectorSimulator;
28#[allow(unused_imports)]
29use quantrs2_core::qubit::QubitId;
30#[allow(unused_imports)]
31use std::collections::HashMap;
32
33#[cfg(all(feature = "gpu", not(target_os = "macos")))]
34use crate::gpu::GpuStateVectorSimulator;
35
36pub enum DynamicCircuit {
59 Q2(Circuit<2>),
61 Q3(Circuit<3>),
63 Q4(Circuit<4>),
65 Q5(Circuit<5>),
67 Q6(Circuit<6>),
69 Q7(Circuit<7>),
71 Q8(Circuit<8>),
73 Q9(Circuit<9>),
75 Q10(Circuit<10>),
77 Q12(Circuit<12>),
79 Q16(Circuit<16>),
81 Q20(Circuit<20>),
83 Q24(Circuit<24>),
85 Q32(Circuit<32>),
87}
88
89impl DynamicCircuit {
90 pub fn new(n_qubits: usize) -> QuantRS2Result<Self> {
92 match n_qubits {
93 2 => Ok(Self::Q2(Circuit::<2>::new())),
94 3 => Ok(Self::Q3(Circuit::<3>::new())),
95 4 => Ok(Self::Q4(Circuit::<4>::new())),
96 5 => Ok(Self::Q5(Circuit::<5>::new())),
97 6 => Ok(Self::Q6(Circuit::<6>::new())),
98 7 => Ok(Self::Q7(Circuit::<7>::new())),
99 8 => Ok(Self::Q8(Circuit::<8>::new())),
100 9 => Ok(Self::Q9(Circuit::<9>::new())),
101 10 => Ok(Self::Q10(Circuit::<10>::new())),
102 12 => Ok(Self::Q12(Circuit::<12>::new())),
103 16 => Ok(Self::Q16(Circuit::<16>::new())),
104 20 => Ok(Self::Q20(Circuit::<20>::new())),
105 24 => Ok(Self::Q24(Circuit::<24>::new())),
106 32 => Ok(Self::Q32(Circuit::<32>::new())),
107 _ => Err(QuantRS2Error::UnsupportedQubits(
108 n_qubits,
109 "Supported qubit counts are 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 16, 20, 24, and 32."
110 .to_string(),
111 )),
112 }
113 }
114
115 #[must_use]
117 pub fn gates(&self) -> Vec<String> {
118 self.get_gate_names()
119 }
120
121 #[must_use]
133 pub const fn num_qubits(&self) -> usize {
134 match self {
135 Self::Q2(_) => 2,
136 Self::Q3(_) => 3,
137 Self::Q4(_) => 4,
138 Self::Q5(_) => 5,
139 Self::Q6(_) => 6,
140 Self::Q7(_) => 7,
141 Self::Q8(_) => 8,
142 Self::Q9(_) => 9,
143 Self::Q10(_) => 10,
144 Self::Q12(_) => 12,
145 Self::Q16(_) => 16,
146 Self::Q20(_) => 20,
147 Self::Q24(_) => 24,
148 Self::Q32(_) => 32,
149 }
150 }
151
152 #[must_use]
154 pub fn get_gate_names(&self) -> Vec<String> {
155 match self {
156 Self::Q2(c) => c
157 .gates()
158 .iter()
159 .map(|gate| gate.name().to_string())
160 .collect(),
161 Self::Q3(c) => c
162 .gates()
163 .iter()
164 .map(|gate| gate.name().to_string())
165 .collect(),
166 Self::Q4(c) => c
167 .gates()
168 .iter()
169 .map(|gate| gate.name().to_string())
170 .collect(),
171 Self::Q5(c) => c
172 .gates()
173 .iter()
174 .map(|gate| gate.name().to_string())
175 .collect(),
176 Self::Q6(c) => c
177 .gates()
178 .iter()
179 .map(|gate| gate.name().to_string())
180 .collect(),
181 Self::Q7(c) => c
182 .gates()
183 .iter()
184 .map(|gate| gate.name().to_string())
185 .collect(),
186 Self::Q8(c) => c
187 .gates()
188 .iter()
189 .map(|gate| gate.name().to_string())
190 .collect(),
191 Self::Q9(c) => c
192 .gates()
193 .iter()
194 .map(|gate| gate.name().to_string())
195 .collect(),
196 Self::Q10(c) => c
197 .gates()
198 .iter()
199 .map(|gate| gate.name().to_string())
200 .collect(),
201 Self::Q12(c) => c
202 .gates()
203 .iter()
204 .map(|gate| gate.name().to_string())
205 .collect(),
206 Self::Q16(c) => c
207 .gates()
208 .iter()
209 .map(|gate| gate.name().to_string())
210 .collect(),
211 Self::Q20(c) => c
212 .gates()
213 .iter()
214 .map(|gate| gate.name().to_string())
215 .collect(),
216 Self::Q24(c) => c
217 .gates()
218 .iter()
219 .map(|gate| gate.name().to_string())
220 .collect(),
221 Self::Q32(c) => c
222 .gates()
223 .iter()
224 .map(|gate| gate.name().to_string())
225 .collect(),
226 }
227 }
228
229 #[cfg(feature = "python")]
234 fn get_gate_by_flat_index(&self, flat_index: usize) -> Option<&(dyn GateOp + Send + Sync)> {
235 match self {
236 Self::Q2(c) => c.gates().get(flat_index).map(|g| g.as_ref()),
237 Self::Q3(c) => c.gates().get(flat_index).map(|g| g.as_ref()),
238 Self::Q4(c) => c.gates().get(flat_index).map(|g| g.as_ref()),
239 Self::Q5(c) => c.gates().get(flat_index).map(|g| g.as_ref()),
240 Self::Q6(c) => c.gates().get(flat_index).map(|g| g.as_ref()),
241 Self::Q7(c) => c.gates().get(flat_index).map(|g| g.as_ref()),
242 Self::Q8(c) => c.gates().get(flat_index).map(|g| g.as_ref()),
243 Self::Q9(c) => c.gates().get(flat_index).map(|g| g.as_ref()),
244 Self::Q10(c) => c.gates().get(flat_index).map(|g| g.as_ref()),
245 Self::Q12(c) => c.gates().get(flat_index).map(|g| g.as_ref()),
246 Self::Q16(c) => c.gates().get(flat_index).map(|g| g.as_ref()),
247 Self::Q20(c) => c.gates().get(flat_index).map(|g| g.as_ref()),
248 Self::Q24(c) => c.gates().get(flat_index).map(|g| g.as_ref()),
249 Self::Q32(c) => c.gates().get(flat_index).map(|g| g.as_ref()),
250 }
251 }
252
253 #[cfg(feature = "python")]
256 fn find_nth_gate(
257 &self,
258 gate_type: &str,
259 index: usize,
260 ) -> PyResult<&(dyn GateOp + Send + Sync)> {
261 let gates = self.get_gate_names();
262 let mut count = 0;
263 for (i, name) in gates.iter().enumerate() {
264 if name == gate_type {
265 if count == index {
266 return self.get_gate_by_flat_index(i).ok_or_else(|| {
267 PyValueError::new_err(format!(
268 "Gate {gate_type} at index {index} vanished while reading it"
269 ))
270 });
271 }
272 count += 1;
273 }
274 }
275 Err(PyValueError::new_err(format!(
276 "Gate {gate_type} at index {index} not found"
277 )))
278 }
279
280 #[cfg(feature = "python")]
284 pub fn get_single_qubit_for_gate(&self, gate_type: &str, index: usize) -> PyResult<u32> {
285 let gate = self.find_nth_gate(gate_type, index)?;
286 let qubits = gate.qubits();
287 if qubits.len() != 1 {
288 return Err(PyValueError::new_err(format!(
289 "Gate {gate_type} at index {index} acts on {} qubit(s), expected 1",
290 qubits.len()
291 )));
292 }
293 Ok(qubits[0].id())
294 }
295
296 #[cfg(feature = "python")]
301 pub fn get_rotation_params_for_gate(
302 &self,
303 gate_type: &str,
304 index: usize,
305 ) -> PyResult<(u32, f64)> {
306 let gate = self.find_nth_gate(gate_type, index)?;
307 let qubits = gate.qubits();
308 if qubits.is_empty() {
309 return Err(PyValueError::new_err(format!(
310 "Gate {gate_type} at index {index} has no qubits"
311 )));
312 }
313
314 let theta = if let Some(g) = gate.as_any().downcast_ref::<RotationX>() {
315 g.theta
316 } else if let Some(g) = gate.as_any().downcast_ref::<RotationY>() {
317 g.theta
318 } else if let Some(g) = gate.as_any().downcast_ref::<RotationZ>() {
319 g.theta
320 } else {
321 return Err(PyValueError::new_err(format!(
322 "Gate {gate_type} at index {index} is not a recognized single-qubit rotation \
323 gate (RX/RY/RZ); cannot extract its rotation angle"
324 )));
325 };
326
327 Ok((qubits[0].id(), theta))
328 }
329
330 #[cfg(feature = "python")]
333 pub fn get_two_qubit_params_for_gate(
334 &self,
335 gate_type: &str,
336 index: usize,
337 ) -> PyResult<(u32, u32)> {
338 let gate = self.find_nth_gate(gate_type, index)?;
339 let qubits = gate.qubits();
340 if qubits.len() != 2 {
341 return Err(PyValueError::new_err(format!(
342 "Gate {gate_type} at index {index} acts on {} qubit(s), expected 2",
343 qubits.len()
344 )));
345 }
346 Ok((qubits[0].id(), qubits[1].id()))
347 }
348
349 #[cfg(feature = "python")]
354 pub fn get_controlled_rotation_params_for_gate(
355 &self,
356 gate_type: &str,
357 index: usize,
358 ) -> PyResult<(u32, u32, f64)> {
359 let gate = self.find_nth_gate(gate_type, index)?;
360 let qubits = gate.qubits();
361 if qubits.len() != 2 {
362 return Err(PyValueError::new_err(format!(
363 "Gate {gate_type} at index {index} acts on {} qubit(s), expected 2 (control, target)",
364 qubits.len()
365 )));
366 }
367
368 let theta = if let Some(g) = gate.as_any().downcast_ref::<CRX>() {
369 g.theta
370 } else if let Some(g) = gate.as_any().downcast_ref::<CRY>() {
371 g.theta
372 } else if let Some(g) = gate.as_any().downcast_ref::<CRZ>() {
373 g.theta
374 } else {
375 return Err(PyValueError::new_err(format!(
376 "Gate {gate_type} at index {index} is not a recognized controlled rotation gate \
377 (CRX/CRY/CRZ); cannot extract its rotation angle"
378 )));
379 };
380
381 Ok((qubits[0].id(), qubits[1].id(), theta))
382 }
383
384 #[cfg(feature = "python")]
387 pub fn get_three_qubit_params_for_gate(
388 &self,
389 gate_type: &str,
390 index: usize,
391 ) -> PyResult<(u32, u32, u32)> {
392 let gate = self.find_nth_gate(gate_type, index)?;
393 let qubits = gate.qubits();
394 if qubits.len() != 3 {
395 return Err(PyValueError::new_err(format!(
396 "Gate {gate_type} at index {index} acts on {} qubit(s), expected 3",
397 qubits.len()
398 )));
399 }
400 Ok((qubits[0].id(), qubits[1].id(), qubits[2].id()))
401 }
402
403 pub fn apply_gate<G: GateOp + Clone + Send + Sync + 'static>(
405 &mut self,
406 gate: G,
407 ) -> QuantRS2Result<()> {
408 match self {
409 Self::Q2(c) => c.add_gate(gate).map(|_| ()),
410 Self::Q3(c) => c.add_gate(gate).map(|_| ()),
411 Self::Q4(c) => c.add_gate(gate).map(|_| ()),
412 Self::Q5(c) => c.add_gate(gate).map(|_| ()),
413 Self::Q6(c) => c.add_gate(gate).map(|_| ()),
414 Self::Q7(c) => c.add_gate(gate).map(|_| ()),
415 Self::Q8(c) => c.add_gate(gate).map(|_| ()),
416 Self::Q9(c) => c.add_gate(gate).map(|_| ()),
417 Self::Q10(c) => c.add_gate(gate).map(|_| ()),
418 Self::Q12(c) => c.add_gate(gate).map(|_| ()),
419 Self::Q16(c) => c.add_gate(gate).map(|_| ()),
420 Self::Q20(c) => c.add_gate(gate).map(|_| ()),
421 Self::Q24(c) => c.add_gate(gate).map(|_| ()),
422 Self::Q32(c) => c.add_gate(gate).map(|_| ()),
423 }
424 }
425
426 pub fn run(&self, simulator: &StateVectorSimulator) -> QuantRS2Result<DynamicResult> {
428 match self {
429 Self::Q2(c) => {
430 let result = simulator.run(c)?;
431 Ok(DynamicResult {
432 amplitudes: result.amplitudes().to_vec(),
433 num_qubits: 2,
434 })
435 }
436 Self::Q3(c) => {
437 let result = simulator.run(c)?;
438 Ok(DynamicResult {
439 amplitudes: result.amplitudes().to_vec(),
440 num_qubits: 3,
441 })
442 }
443 Self::Q4(c) => {
444 let result = simulator.run(c)?;
445 Ok(DynamicResult {
446 amplitudes: result.amplitudes().to_vec(),
447 num_qubits: 4,
448 })
449 }
450 Self::Q5(c) => {
451 let result = simulator.run(c)?;
452 Ok(DynamicResult {
453 amplitudes: result.amplitudes().to_vec(),
454 num_qubits: 5,
455 })
456 }
457 Self::Q6(c) => {
458 let result = simulator.run(c)?;
459 Ok(DynamicResult {
460 amplitudes: result.amplitudes().to_vec(),
461 num_qubits: 6,
462 })
463 }
464 Self::Q7(c) => {
465 let result = simulator.run(c)?;
466 Ok(DynamicResult {
467 amplitudes: result.amplitudes().to_vec(),
468 num_qubits: 7,
469 })
470 }
471 Self::Q8(c) => {
472 let result = simulator.run(c)?;
473 Ok(DynamicResult {
474 amplitudes: result.amplitudes().to_vec(),
475 num_qubits: 8,
476 })
477 }
478 Self::Q9(c) => {
479 let result = simulator.run(c)?;
480 Ok(DynamicResult {
481 amplitudes: result.amplitudes().to_vec(),
482 num_qubits: 9,
483 })
484 }
485 Self::Q10(c) => {
486 let result = simulator.run(c)?;
487 Ok(DynamicResult {
488 amplitudes: result.amplitudes().to_vec(),
489 num_qubits: 10,
490 })
491 }
492 Self::Q12(c) => {
493 let result = simulator.run(c)?;
494 Ok(DynamicResult {
495 amplitudes: result.amplitudes().to_vec(),
496 num_qubits: 12,
497 })
498 }
499 Self::Q16(c) => {
500 let result = simulator.run(c)?;
501 Ok(DynamicResult {
502 amplitudes: result.amplitudes().to_vec(),
503 num_qubits: 16,
504 })
505 }
506 Self::Q20(c) => {
507 let result = simulator.run(c)?;
508 Ok(DynamicResult {
509 amplitudes: result.amplitudes().to_vec(),
510 num_qubits: 20,
511 })
512 }
513 Self::Q24(c) => {
514 let result = simulator.run(c)?;
515 Ok(DynamicResult {
516 amplitudes: result.amplitudes().to_vec(),
517 num_qubits: 24,
518 })
519 }
520 Self::Q32(c) => {
521 let result = simulator.run(c)?;
522 Ok(DynamicResult {
523 amplitudes: result.amplitudes().to_vec(),
524 num_qubits: 32,
525 })
526 }
527 }
528 }
529
530 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
532 pub fn is_gpu_available() -> bool {
533 GpuStateVectorSimulator::is_available()
534 }
535
536 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
538 pub fn run_gpu(&self) -> QuantRS2Result<DynamicResult> {
539 let mut gpu_simulator = match GpuStateVectorSimulator::new_blocking() {
541 Ok(sim) => sim,
542 Err(e) => {
543 return Err(QuantRS2Error::BackendExecutionFailed(format!(
544 "Failed to create GPU simulator: {}",
545 e
546 )))
547 }
548 };
549
550 match self {
552 DynamicCircuit::Q2(c) => {
553 let result = gpu_simulator.run(c).map_err(|e| {
554 QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
555 })?;
556 Ok(DynamicResult {
557 amplitudes: result.amplitudes.clone(),
558 num_qubits: 2,
559 })
560 }
561 DynamicCircuit::Q3(c) => {
562 let result = gpu_simulator.run(c).map_err(|e| {
563 QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
564 })?;
565 Ok(DynamicResult {
566 amplitudes: result.amplitudes.clone(),
567 num_qubits: 3,
568 })
569 }
570 DynamicCircuit::Q4(c) => {
571 let result = gpu_simulator.run(c).map_err(|e| {
572 QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
573 })?;
574 Ok(DynamicResult {
575 amplitudes: result.amplitudes.clone(),
576 num_qubits: 4,
577 })
578 }
579 DynamicCircuit::Q5(c) => {
580 let result = gpu_simulator.run(c).map_err(|e| {
581 QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
582 })?;
583 Ok(DynamicResult {
584 amplitudes: result.amplitudes.clone(),
585 num_qubits: 5,
586 })
587 }
588 DynamicCircuit::Q6(c) => {
589 let result = gpu_simulator.run(c).map_err(|e| {
590 QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
591 })?;
592 Ok(DynamicResult {
593 amplitudes: result.amplitudes.clone(),
594 num_qubits: 6,
595 })
596 }
597 DynamicCircuit::Q7(c) => {
598 let result = gpu_simulator.run(c).map_err(|e| {
599 QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
600 })?;
601 Ok(DynamicResult {
602 amplitudes: result.amplitudes.clone(),
603 num_qubits: 7,
604 })
605 }
606 DynamicCircuit::Q8(c) => {
607 let result = gpu_simulator.run(c).map_err(|e| {
608 QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
609 })?;
610 Ok(DynamicResult {
611 amplitudes: result.amplitudes.clone(),
612 num_qubits: 8,
613 })
614 }
615 DynamicCircuit::Q9(c) => {
616 let result = gpu_simulator.run(c).map_err(|e| {
617 QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
618 })?;
619 Ok(DynamicResult {
620 amplitudes: result.amplitudes.clone(),
621 num_qubits: 9,
622 })
623 }
624 DynamicCircuit::Q10(c) => {
625 let result = gpu_simulator.run(c).map_err(|e| {
626 QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
627 })?;
628 Ok(DynamicResult {
629 amplitudes: result.amplitudes.clone(),
630 num_qubits: 10,
631 })
632 }
633 DynamicCircuit::Q12(c) => {
634 let result = gpu_simulator.run(c).map_err(|e| {
635 QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
636 })?;
637 Ok(DynamicResult {
638 amplitudes: result.amplitudes.clone(),
639 num_qubits: 12,
640 })
641 }
642 DynamicCircuit::Q16(c) => {
643 let result = gpu_simulator.run(c).map_err(|e| {
644 QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
645 })?;
646 Ok(DynamicResult {
647 amplitudes: result.amplitudes.clone(),
648 num_qubits: 16,
649 })
650 }
651 DynamicCircuit::Q20(c) => {
652 let result = gpu_simulator.run(c).map_err(|e| {
653 QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
654 })?;
655 Ok(DynamicResult {
656 amplitudes: result.amplitudes.clone(),
657 num_qubits: 20,
658 })
659 }
660 DynamicCircuit::Q24(c) => {
661 let result = gpu_simulator.run(c).map_err(|e| {
662 QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
663 })?;
664 Ok(DynamicResult {
665 amplitudes: result.amplitudes.clone(),
666 num_qubits: 24,
667 })
668 }
669 DynamicCircuit::Q32(c) => {
670 let result = gpu_simulator.run(c).map_err(|e| {
671 QuantRS2Error::BackendExecutionFailed(format!("GPU simulation failed: {}", e))
672 })?;
673 Ok(DynamicResult {
674 amplitudes: result.amplitudes.clone(),
675 num_qubits: 32,
676 })
677 }
678 }
679 }
680
681 #[cfg(not(all(feature = "gpu", not(target_os = "macos"))))]
683 #[must_use]
684 pub const fn is_gpu_available() -> bool {
685 false
686 }
687
688 #[cfg(not(all(feature = "gpu", not(target_os = "macos"))))]
690 pub fn run_gpu(&self) -> QuantRS2Result<DynamicResult> {
691 Err(QuantRS2Error::BackendExecutionFailed(
692 "GPU acceleration is not available on this platform".to_string(),
693 ))
694 }
695
696 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
698 pub fn run_best(&self) -> QuantRS2Result<DynamicResult> {
699 if Self::is_gpu_available() && self.num_qubits() >= 4 {
700 self.run_gpu()
701 } else {
702 let simulator = StateVectorSimulator::new();
703 self.run(&simulator)
704 }
705 }
706
707 #[cfg(all(feature = "gpu", target_os = "macos"))]
709 pub fn run_best(&self) -> QuantRS2Result<DynamicResult> {
710 let simulator = StateVectorSimulator::new();
711 self.run(&simulator)
712 }
713
714 #[cfg(not(feature = "gpu"))]
716 pub fn run_best(&self) -> QuantRS2Result<DynamicResult> {
717 let simulator = StateVectorSimulator::new();
718 self.run(&simulator)
719 }
720}
721
722pub struct DynamicResult {
724 pub amplitudes: Vec<Complex64>,
726 pub num_qubits: usize,
728}
729
730impl DynamicResult {
731 #[must_use]
733 pub fn amplitudes(&self) -> &[Complex64] {
734 &self.amplitudes
735 }
736
737 #[must_use]
739 pub fn probabilities(&self) -> Vec<f64> {
740 self.amplitudes
741 .iter()
742 .map(scirs2_core::Complex::norm_sqr)
743 .collect()
744 }
745
746 #[must_use]
748 pub const fn num_qubits(&self) -> usize {
749 self.num_qubits
750 }
751}
752
753#[cfg(all(test, feature = "python"))]
754mod python_introspection_tests {
755 use super::*;
756 use quantrs2_core::gate::multi::CRY;
757 use quantrs2_core::gate::single::RotationY;
758 use quantrs2_core::qubit::QubitId;
759
760 #[test]
766 fn test_single_qubit_and_rotation_params_for_q3_circuit() {
767 let mut dc = DynamicCircuit::new(3).expect("3 qubits supported");
768 dc.apply_gate(RotationY {
771 target: QubitId::new(2),
772 theta: 1.2345,
773 })
774 .expect("RY gate applied");
775
776 let qubit = dc
777 .get_single_qubit_for_gate("RY", 0)
778 .expect("real qubit for RY gate");
779 assert_eq!(
780 qubit, 2,
781 "expected the gate's real target qubit (2), not a hardcoded 0"
782 );
783
784 let (qubit, theta) = dc
785 .get_rotation_params_for_gate("RY", 0)
786 .expect("real rotation params for RY gate");
787 assert_eq!(qubit, 2);
788 assert!(
789 (theta - 1.2345).abs() < 1e-12,
790 "expected the gate's real angle (1.2345), not a hardcoded 0.0, got {theta}"
791 );
792 }
793
794 #[test]
798 fn test_controlled_rotation_params_for_q4_circuit() {
799 let mut dc = DynamicCircuit::new(4).expect("4 qubits supported");
800 dc.apply_gate(CRY {
801 control: QubitId::new(3),
802 target: QubitId::new(1),
803 theta: 0.4321,
804 })
805 .expect("CRY gate applied");
806
807 let (control, target, theta) = dc
808 .get_controlled_rotation_params_for_gate("CRY", 0)
809 .expect("real controlled-rotation params for CRY gate");
810 assert_eq!(
811 control, 3,
812 "expected the gate's real control qubit (3), not a hardcoded 0"
813 );
814 assert_eq!(
815 target, 1,
816 "expected the gate's real target qubit (1), not a hardcoded 1-by-coincidence"
817 );
818 assert!(
819 (theta - 0.4321).abs() < 1e-12,
820 "expected the gate's real angle (0.4321), not a hardcoded 0.0, got {theta}"
821 );
822 }
823
824 #[test]
827 fn test_missing_gate_returns_honest_error() {
828 let dc = DynamicCircuit::new(3).expect("3 qubits supported");
829 let result = dc.get_single_qubit_for_gate("RY", 0);
830 assert!(
831 result.is_err(),
832 "expected an honest error for a nonexistent gate"
833 );
834 }
835}