1use crate::circuit::control_flow::{ClassicalControlOp, ControlBody, ForOp, IfOp};
27use crate::circuit::error::CircuitError;
28use crate::circuit::gate::Instruction;
29use crate::circuit::gate::classical_data::ClassicalDataOp;
30use crate::circuit::gate::directive::Directive;
31use crate::circuit::{Operation, Qubit};
32use std::borrow::Cow;
33use std::collections::{BTreeSet, HashMap};
34
35pub(crate) fn circuit_depth(
42 all_qubits: impl IntoIterator<Item = Qubit>,
43 operations: &[Operation],
44 recurse: bool,
45) -> Result<usize, CircuitError> {
46 if !recurse && contains_control_flow(operations) {
47 return Err(CircuitError::ControlFlowPresent);
48 }
49 let universe: Vec<Qubit> = all_qubits.into_iter().collect();
50 operations_depth(operations, &universe, recurse)
51}
52
53fn operations_depth(
59 operations: &[Operation],
60 qubit_universe: &[Qubit],
61 recurse: bool,
62) -> Result<usize, CircuitError> {
63 let mut qubit_depths: HashMap<Qubit, usize> = HashMap::new();
64 let mut max_depth = 0usize;
65 for operation in operations {
66 let (synced, local_depth) =
67 operation_sync_and_local_depth(operation, qubit_universe, recurse)?;
68 if local_depth == 0 {
69 continue;
71 }
72 let base = synced
73 .iter()
74 .map(|qubit| qubit_depths.get(qubit).copied().unwrap_or(0))
75 .max()
76 .unwrap_or(0);
77 let depth = base + local_depth;
78 for qubit in synced.iter() {
79 qubit_depths.insert(*qubit, depth);
80 }
81 if depth > max_depth {
82 max_depth = depth;
83 }
84 }
85 Ok(max_depth)
86}
87
88fn operation_sync_and_local_depth<'a>(
98 operation: &'a Operation,
99 qubit_universe: &[Qubit],
100 recurse: bool,
101) -> Result<(Cow<'a, [Qubit]>, usize), CircuitError> {
102 match &operation.instruction {
103 Instruction::Standard(_)
104 | Instruction::McGate(_)
105 | Instruction::UnitaryGate(_)
106 | Instruction::Delay
107 | Instruction::Directive(Directive::Measure | Directive::Reset)
108 | Instruction::ClassicalData(ClassicalDataOp::MeasureBit { .. })
109 | Instruction::ClassicalData(ClassicalDataOp::MeasureBits { .. }) => {
110 Ok((Cow::Borrowed(&operation.qubits[..]), 1))
111 }
112 Instruction::Directive(Directive::Barrier) => {
113 if operation.qubits.is_empty() {
114 Ok((Cow::Owned(qubit_universe.to_vec()), 1))
116 } else {
117 Ok((Cow::Borrowed(&operation.qubits[..]), 1))
118 }
119 }
120 Instruction::CircuitGate(_) => {
121 Ok((Cow::Borrowed(&operation.qubits[..]), 1))
123 }
124 Instruction::ClassicalData(ClassicalDataOp::Store { .. }) => {
125 Ok((Cow::Borrowed(&[][..]), 0))
127 }
128 Instruction::ClassicalControl(control) => {
129 if !recurse {
130 return Err(CircuitError::ControlFlowPresent);
133 }
134 control_flow_sync_and_depth(control, qubit_universe, recurse)
135 .map(|(qubits, depth)| (Cow::Owned(qubits), depth))
136 }
137 }
138}
139
140#[allow(clippy::type_complexity)]
147fn control_flow_sync_and_depth(
148 control: &ClassicalControlOp,
149 qubit_universe: &[Qubit],
150 recurse: bool,
151) -> Result<(Vec<Qubit>, usize), CircuitError> {
152 match control {
153 ClassicalControlOp::If(op) => {
154 let then_d = control_body_depth(op.then_body(), qubit_universe, recurse)?;
155 let else_d = match op.else_body() {
156 Some(body) => control_body_depth(body, qubit_universe, recurse)?,
157 None => 0,
158 };
159 let local_depth = 1 + then_d.max(else_d);
160 let synced = if_union_qubits(op);
161 Ok((synced, local_depth))
162 }
163 ClassicalControlOp::While(op) => {
164 let body_d = control_body_depth(op.body(), qubit_universe, recurse)?;
165 let local_depth = 1 + body_d;
166 let synced = used_qubits_recursive(op.body().operations());
167 Ok((synced.into_iter().collect(), local_depth))
168 }
169 ClassicalControlOp::For(op) => {
170 let body_d = control_body_depth(op.body(), qubit_universe, recurse)?;
171 let iterations = for_loop_iterations(op).unwrap_or(1);
172 let local_depth = 1 + iterations.saturating_mul(body_d);
173 let synced = used_qubits_recursive(op.body().operations());
174 Ok((synced.into_iter().collect(), local_depth))
175 }
176 ClassicalControlOp::Switch(op) => {
177 let mut max_depth = 0usize;
178 let mut synced = BTreeSet::new();
179 for case in op.cases() {
180 max_depth =
181 max_depth.max(control_body_depth(case.body(), qubit_universe, recurse)?);
182 synced.extend(used_qubits_recursive(case.body().operations()));
183 }
184 if let Some(default) = op.default() {
185 max_depth = max_depth.max(control_body_depth(default, qubit_universe, recurse)?);
186 synced.extend(used_qubits_recursive(default.operations()));
187 }
188 let local_depth = 1 + max_depth;
189 Ok((synced.into_iter().collect(), local_depth))
190 }
191 ClassicalControlOp::Break | ClassicalControlOp::Continue => {
192 Ok((Vec::new(), 0))
194 }
195 }
196}
197
198fn control_body_depth(
200 body: &ControlBody,
201 qubit_universe: &[Qubit],
202 recurse: bool,
203) -> Result<usize, CircuitError> {
204 operations_depth(body.operations(), qubit_universe, recurse)
205}
206
207fn if_union_qubits(op: &IfOp) -> Vec<Qubit> {
209 let mut qubits = used_qubits_recursive(op.then_body().operations());
210 if let Some(else_body) = op.else_body() {
211 qubits.extend(used_qubits_recursive(else_body.operations()));
212 }
213 qubits.into_iter().collect()
214}
215
216fn for_loop_iterations(op: &ForOp) -> Option<usize> {
220 usize::try_from(op.static_iteration_count()?).ok()
221}
222
223fn used_qubits_recursive(operations: &[Operation]) -> BTreeSet<Qubit> {
229 let mut qubits = BTreeSet::new();
230 for operation in operations {
231 qubits.extend(operation.qubits.iter().copied());
232 if let Instruction::ClassicalControl(control) = &operation.instruction {
233 for body in control.bodies() {
234 qubits.extend(used_qubits_recursive(body.operations()));
235 }
236 }
237 }
238 qubits
239}
240
241fn contains_control_flow(operations: &[Operation]) -> bool {
249 operations
250 .iter()
251 .any(|operation| matches!(operation.instruction, Instruction::ClassicalControl(_)))
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257 use crate::circuit::circuit_impl::Circuit;
258 use crate::circuit::gate::Instruction;
259 use crate::circuit::{
260 CircuitError, ClassicalControlOp, ClassicalExpr, ClassicalType, ClassicalVar, Operation,
261 Qubit,
262 };
263 use smallvec::smallvec;
264 use std::slice::from_ref;
265
266 fn q(n: u32) -> Qubit {
267 Qubit::new(n)
268 }
269
270 #[test]
271 fn test_depth_empty_circuit() {
272 let c = Circuit::new(3);
273 assert_eq!(c.depth(false).unwrap(), 0);
274 }
275
276 #[test]
277 fn test_depth_single_qubit_chain() {
278 let mut c = Circuit::new(1);
279 c.h(q(0)).unwrap();
280 c.x(q(0)).unwrap();
281 c.z(q(0)).unwrap();
282 assert_eq!(c.depth(false).unwrap(), 3);
283 }
284
285 #[test]
286 fn test_depth_parallel_single_qubit() {
287 let mut c = Circuit::new(3);
288 c.h(q(0)).unwrap();
289 c.h(q(1)).unwrap();
290 c.h(q(2)).unwrap();
291 assert_eq!(c.depth(false).unwrap(), 1);
292 }
293
294 #[test]
295 fn test_depth_cx_chain() {
296 let mut c = Circuit::new(3);
297 c.cx(q(0), q(1)).unwrap();
298 c.cx(q(1), q(2)).unwrap();
299 assert_eq!(c.depth(false).unwrap(), 2);
300 }
301
302 #[test]
303 fn test_depth_barrier_forces_serialization() {
304 let mut c = Circuit::new(2);
305 c.h(q(0)).unwrap();
306 c.h(q(1)).unwrap();
307 c.barrier(vec![q(0), q(1)]).unwrap();
308 c.h(q(0)).unwrap();
309 c.h(q(1)).unwrap();
310 assert_eq!(c.depth(false).unwrap(), 3);
312 }
313
314 #[test]
315 fn test_depth_global_barrier() {
316 let mut c = Circuit::new(2);
317 c.h(q(0)).unwrap();
318 c.barrier(Vec::new()).unwrap();
320 c.h(q(1)).unwrap();
321 assert_eq!(c.depth(false).unwrap(), 3);
323 }
324
325 #[test]
326 fn test_depth_measure_and_reset_count() {
327 let mut c = Circuit::new(1);
328 c.measure(q(0)).unwrap();
329 c.reset(q(0)).unwrap();
330 assert_eq!(c.depth(false).unwrap(), 2);
331 }
332
333 #[test]
334 fn test_depth_store_zero_qubits_noop() {
335 let mut c = Circuit::new(1);
336 c.h(q(0)).unwrap();
337 let target = c.var(ClassicalType::uint(8).unwrap());
339 c.store(target, ClassicalExpr::uint_literal(8, 0).unwrap())
340 .unwrap();
341 c.h(q(0)).unwrap();
342 assert_eq!(c.depth(false).unwrap(), 2);
343 }
344
345 #[test]
346 fn test_depth_recurse_false_raises_on_if() {
347 let mut c = Circuit::new(2);
348 c.if_(ClassicalExpr::bool_literal(true), |body| {
349 body.x(q(1))?;
350 Ok(())
351 })
352 .unwrap();
353 assert!(matches!(
354 c.depth(false),
355 Err(CircuitError::ControlFlowPresent)
356 ));
357 }
358
359 #[test]
360 fn test_depth_recurse_false_raises_on_nested_cf() {
361 let mut c = Circuit::new(1);
363 let counter = c.var(ClassicalType::uint(4).unwrap());
364 c.for_uint(
365 counter,
366 ClassicalExpr::uint_literal(4, 0).unwrap(),
367 ClassicalExpr::uint_literal(4, 2).unwrap(),
368 ClassicalExpr::uint_literal(4, 1).unwrap(),
369 |body, _| {
370 body.if_(ClassicalExpr::bool_literal(true), |inner| {
371 inner.x(q(0))?;
372 Ok(())
373 })?;
374 Ok(())
375 },
376 )
377 .unwrap();
378 assert!(matches!(
379 c.depth(false),
380 Err(CircuitError::ControlFlowPresent)
381 ));
382 }
383
384 #[test]
385 fn test_depth_recurse_true_if_else_max() {
386 let mut c = Circuit::new(1);
387 c.if_else(
388 ClassicalExpr::bool_literal(true),
389 |then_body| {
390 then_body.x(q(0))?;
391 then_body.z(q(0))?;
392 Ok(())
393 },
394 |else_body| {
395 else_body.y(q(0))?;
396 Ok(())
397 },
398 )
399 .unwrap();
400 assert_eq!(c.depth(true).unwrap(), 3);
402 }
403
404 #[test]
405 fn test_depth_recurse_true_while_body_once() {
406 let mut c = Circuit::new(1);
407 c.while_(ClassicalExpr::bool_literal(true), |body| {
408 body.h(q(0))?;
409 body.x(q(0))?;
410 body.break_loop()?;
411 Ok(())
412 })
413 .unwrap();
414 assert_eq!(c.depth(true).unwrap(), 3);
416 }
417
418 #[test]
419 fn test_depth_recurse_true_for_uint_unrolled() {
420 let mut c = Circuit::new(1);
421 let counter = c.var(ClassicalType::uint(8).unwrap());
422 c.for_uint(
423 counter,
424 ClassicalExpr::uint_literal(8, 0).unwrap(),
425 ClassicalExpr::uint_literal(8, 3).unwrap(),
426 ClassicalExpr::uint_literal(8, 1).unwrap(),
427 |body, _| {
428 body.h(q(0))?;
429 Ok(())
430 },
431 )
432 .unwrap();
433 assert_eq!(c.depth(true).unwrap(), 4);
435 }
436
437 #[test]
438 fn test_depth_recurse_true_for_var_falls_back_once() {
439 let mut c = Circuit::new(1);
440 let counter = c.var(ClassicalType::uint(8).unwrap());
441 let runtime_start = c.var(ClassicalType::uint(8).unwrap());
442 c.for_uint(
443 counter,
444 runtime_start.expr(),
445 ClassicalExpr::uint_literal(8, 3).unwrap(),
446 ClassicalExpr::uint_literal(8, 1).unwrap(),
447 |body, _| {
448 body.h(q(0))?;
449 Ok(())
450 },
451 )
452 .unwrap();
453 assert_eq!(c.depth(true).unwrap(), 2);
455 }
456
457 #[test]
458 fn test_depth_recurse_true_for_step_zero_falls_back_once() {
459 let mut c = Circuit::new(1);
460 let counter = c.var(ClassicalType::uint(8).unwrap());
461 c.for_uint(
462 counter,
463 ClassicalExpr::uint_literal(8, 0).unwrap(),
464 ClassicalExpr::uint_literal(8, 3).unwrap(),
465 ClassicalExpr::uint_literal(8, 0).unwrap(), |body, _| {
467 body.h(q(0))?;
468 Ok(())
469 },
470 )
471 .unwrap();
472 assert_eq!(c.depth(true).unwrap(), 2);
474 }
475
476 #[test]
477 fn test_depth_recurse_true_for_empty_range() {
478 let mut c = Circuit::new(1);
479 let counter = c.var(ClassicalType::uint(8).unwrap());
480 c.for_uint(
481 counter,
482 ClassicalExpr::uint_literal(8, 5).unwrap(),
483 ClassicalExpr::uint_literal(8, 2).unwrap(),
484 ClassicalExpr::uint_literal(8, 1).unwrap(),
485 |body, _| {
486 body.h(q(0))?;
487 Ok(())
488 },
489 )
490 .unwrap();
491 assert_eq!(c.depth(true).unwrap(), 1);
493 }
494
495 #[test]
496 fn test_depth_recurse_true_for_uneven_step() {
497 let mut c = Circuit::new(1);
498 let counter = c.var(ClassicalType::uint(8).unwrap());
499 c.for_uint(
500 counter,
501 ClassicalExpr::uint_literal(8, 0).unwrap(),
502 ClassicalExpr::uint_literal(8, 5).unwrap(),
503 ClassicalExpr::uint_literal(8, 2).unwrap(),
504 |body, _| {
505 body.h(q(0))?;
506 Ok(())
507 },
508 )
509 .unwrap();
510 assert_eq!(c.depth(true).unwrap(), 4);
512 }
513
514 #[test]
515 fn test_depth_recurse_true_switch_max() {
516 let mut c = Circuit::new(1);
517 let state = c.var(ClassicalType::uint(2).unwrap());
518 c.switch(state.expr(), |case| {
519 case.value(0, |body| {
520 body.x(q(0))?;
521 Ok(())
522 })?;
523 case.value(1, |body| {
524 body.x(q(0))?;
525 body.z(q(0))?;
526 Ok(())
527 })?;
528 case.default(|body| {
529 body.y(q(0))?;
530 Ok(())
531 })?;
532 Ok(())
533 })
534 .unwrap();
535 assert_eq!(c.depth(true).unwrap(), 3);
537 }
538
539 #[test]
540 fn test_depth_recurse_true_break_continue_zero() {
541 let mut c = Circuit::new(1);
542 c.while_(ClassicalExpr::bool_literal(true), |body| {
543 body.break_loop()?;
544 Ok(())
545 })
546 .unwrap();
547 assert_eq!(c.depth(true).unwrap(), 1);
549 }
550
551 #[test]
552 fn test_depth_recurse_true_nested_if_in_for() {
553 let mut c = Circuit::new(1);
554 let counter = c.var(ClassicalType::uint(8).unwrap());
555 c.for_uint(
556 counter,
557 ClassicalExpr::uint_literal(8, 0).unwrap(),
558 ClassicalExpr::uint_literal(8, 2).unwrap(),
559 ClassicalExpr::uint_literal(8, 1).unwrap(),
560 |body, _| {
561 body.if_(ClassicalExpr::bool_literal(true), |inner| {
562 inner.x(q(0))?;
563 inner.y(q(0))?;
564 Ok(())
565 })?;
566 Ok(())
567 },
568 )
569 .unwrap();
570 assert_eq!(c.depth(true).unwrap(), 7);
573 }
574
575 #[test]
576 fn test_depth_cf_synchronizes_union_qubits() {
577 let mut c = Circuit::new(4);
580 c.if_else(
581 ClassicalExpr::bool_literal(true),
582 |then_body| {
583 then_body.cx(q(0), q(1))?;
584 Ok(())
585 },
586 |else_body| {
587 else_body.cx(q(2), q(3))?;
588 Ok(())
589 },
590 )
591 .unwrap();
592 c.h(q(0)).unwrap();
593 assert_eq!(c.depth(true).unwrap(), 3);
595 }
596
597 #[test]
598 fn test_depth_for_loop_iterations_helper() {
599 let mk = |start: u128, stop: u128, step: u128| {
601 let var = ClassicalVar::new(
602 crate::circuit::CircuitId::new(),
603 0,
604 ClassicalType::uint(8).unwrap(),
605 );
606 ForOp::new(
607 var,
608 ClassicalExpr::uint_literal(8, start).unwrap(),
609 ClassicalExpr::uint_literal(8, stop).unwrap(),
610 ClassicalExpr::uint_literal(8, step).unwrap(),
611 crate::circuit::ControlBody::new(vec![]),
612 )
613 .unwrap()
614 };
615 assert_eq!(for_loop_iterations(&mk(0, 3, 1)), Some(3));
616 assert_eq!(for_loop_iterations(&mk(0, 5, 2)), Some(3)); assert_eq!(for_loop_iterations(&mk(5, 2, 1)), Some(0)); assert_eq!(for_loop_iterations(&mk(5, 0, 1)), Some(0)); assert_eq!(for_loop_iterations(&mk(0, 3, 0)), None); }
621
622 #[test]
623 fn test_depth_circuit_gate_opaque() {
624 let mut inner = Circuit::new(2);
627 inner.h(q(0)).unwrap();
628 inner.cx(q(0), q(1)).unwrap();
629 let gate_instruction = inner.to_gate("mygate").unwrap();
630
631 let mut c = Circuit::new(2);
632 c.append(gate_instruction, [q(0), q(1)], [], None).unwrap();
633 assert_eq!(c.depth(false).unwrap(), 1);
634 }
635
636 #[test]
637 fn test_contains_control_flow_helper() {
638 let mk_cf = |op: ClassicalControlOp| Operation {
639 instruction: Instruction::ClassicalControl(op),
640 qubits: smallvec![],
641 params: smallvec![],
642 label: None,
643 };
644 let plain = Operation {
645 instruction: crate::circuit::gate::Instruction::Standard(
646 crate::circuit::gate::StandardGate::H,
647 ),
648 qubits: smallvec![q(0)],
649 params: smallvec![],
650 label: None,
651 };
652 assert!(!contains_control_flow(from_ref(&plain)));
653
654 let if_op = IfOp::new(
655 ClassicalExpr::bool_literal(true),
656 crate::circuit::ControlBody::new(vec![]),
657 None,
658 )
659 .unwrap();
660 assert!(contains_control_flow(&[mk_cf(ClassicalControlOp::If(
661 if_op
662 ))]));
663 }
664}