1use crate::circuit::Qubit;
26use std::collections::BTreeSet;
27
28#[derive(Debug, Clone, PartialEq, Eq)]
32pub enum VisualOpStyle {
33 Gate,
35 Controlled {
37 num_controls: usize,
39 },
40 Cz,
42 Swap,
44 Barrier,
46 Measure,
48 Reset,
50 Delay,
52 ControlFlow {
54 kind: VisualControlFlowKind,
56 },
57}
58
59#[derive(Debug, Clone, PartialEq, Eq)]
61pub enum VisualControlFlowKind {
62 IfElseBlock {
64 has_false_branch: bool,
66 condition: VisualCondition,
68 },
69 WhileBlock {
71 condition: VisualCondition,
73 },
74 ForBlock {
76 range: VisualCondition,
78 },
79 SwitchBlock {
81 target: VisualCondition,
83 },
84 Break,
86 Continue,
88 IfStart,
90 ElseStart,
92 WhileStart,
94 ForStart,
96 SwitchStart,
98 CaseStart,
100 DefaultStart,
102 End,
104}
105
106#[derive(Debug, Clone, PartialEq, Eq)]
108pub struct VisualCondition {
109 pub label: String,
111}
112
113#[derive(Debug, Clone)]
117pub struct VisualOperation {
118 pub column: usize,
120 pub lanes: Vec<usize>,
122 pub covered_lanes: Vec<usize>,
124 pub label: String,
126 pub params: Vec<String>,
128 pub style: VisualOpStyle,
130 pub span_box: bool,
132 pub children: Option<VisualChildren>,
134 pub span_cols: usize,
136}
137
138#[derive(Debug, Clone)]
164pub struct VisualCircuit {
165 pub qubits: Vec<Qubit>,
167 pub operations: Vec<VisualOperation>,
169 pub num_columns: usize,
171}
172
173impl VisualCircuit {
174 pub fn num_qubits(&self) -> usize {
176 self.qubits.len()
177 }
178}
179
180#[derive(Debug, Clone)]
184pub enum VisualChildren {
185 IfElse {
187 then_circuit: Box<VisualCircuit>,
189 else_circuit: Option<Box<VisualCircuit>>,
191 },
192 While {
194 body_circuit: Box<VisualCircuit>,
196 },
197 For {
199 body_circuit: Box<VisualCircuit>,
201 },
202 Switch {
204 case_circuits: Vec<(String, Box<VisualCircuit>)>,
206 default_circuit: Option<Box<VisualCircuit>>,
208 },
209}
210
211pub(crate) fn flatten_control_flow_visual(visual: &VisualCircuit) -> VisualCircuit {
216 let mut flat_ops = Vec::new();
217 let mut next_cf_id = 0usize;
218 let root_scope_lanes: Vec<usize> = (0..visual.num_qubits()).collect();
219 flatten_ops_recursive(visual, &mut next_cf_id, &mut flat_ops, &root_scope_lanes);
220
221 let mut next_free = vec![0usize; visual.num_qubits()];
222 let mut num_columns = 0usize;
223 for op in &mut flat_ops {
224 let schedule_lanes = scheduling_covered_lanes(op, visual.num_qubits());
225 let column = schedule_lanes
226 .iter()
227 .filter_map(|lane| next_free.get(*lane).copied())
228 .max()
229 .unwrap_or(0);
230 op.column = column;
231 let span = op.span_cols.max(1);
232 for lane in schedule_lanes {
233 if let Some(slot) = next_free.get_mut(lane) {
234 *slot = column + span;
235 }
236 }
237 num_columns = num_columns.max(column + span);
238 }
239
240 VisualCircuit {
241 qubits: visual.qubits.clone(),
242 operations: flat_ops,
243 num_columns,
244 }
245}
246
247pub(crate) fn reverse_visual_lanes(mut visual: VisualCircuit) -> VisualCircuit {
249 let n = visual.num_qubits();
250 if n == 0 {
251 return visual;
252 }
253 visual.qubits.reverse();
254 for op in &mut visual.operations {
255 for lane in &mut op.lanes {
256 *lane = n - 1 - *lane;
257 }
258 for lane in &mut op.covered_lanes {
259 *lane = n - 1 - *lane;
260 }
261 }
262 visual
263}
264
265fn flatten_ops_recursive(
266 visual: &VisualCircuit,
267 next_cf_id: &mut usize,
268 out: &mut Vec<VisualOperation>,
269 scope_lanes: &[usize],
270) {
271 for op in &visual.operations {
272 match &op.style {
273 VisualOpStyle::ControlFlow {
274 kind: VisualControlFlowKind::IfElseBlock { condition, .. },
275 } => {
276 let cf_id = *next_cf_id;
277 *next_cf_id += 1;
278 let covered = control_flow_marker_lanes(op, visual.num_qubits(), scope_lanes);
279 let if_label = format!("If-{cf_id} {}", condition.label);
280 out.push(make_control_marker(
281 if_label,
282 VisualControlFlowKind::IfStart,
283 covered.clone(),
284 ));
285
286 if let Some(VisualChildren::IfElse {
287 then_circuit,
288 else_circuit,
289 }) = op.children.as_ref()
290 {
291 flatten_ops_recursive(then_circuit, next_cf_id, out, &covered);
292 if let Some(else_body) = else_circuit {
293 out.push(make_control_marker(
294 format!("Else-{cf_id}"),
295 VisualControlFlowKind::ElseStart,
296 covered.clone(),
297 ));
298 flatten_ops_recursive(else_body, next_cf_id, out, &covered);
299 }
300 }
301
302 out.push(make_control_marker(
303 format!("End-{cf_id}"),
304 VisualControlFlowKind::End,
305 covered,
306 ));
307 }
308 VisualOpStyle::ControlFlow {
309 kind: VisualControlFlowKind::WhileBlock { condition },
310 } => {
311 let cf_id = *next_cf_id;
312 *next_cf_id += 1;
313 let covered = control_flow_marker_lanes(op, visual.num_qubits(), scope_lanes);
314 let while_label = format!("While-{cf_id} {}", condition.label);
315 out.push(make_control_marker(
316 while_label,
317 VisualControlFlowKind::WhileStart,
318 covered.clone(),
319 ));
320 if let Some(VisualChildren::While { body_circuit }) = op.children.as_ref() {
321 flatten_ops_recursive(body_circuit, next_cf_id, out, &covered);
322 }
323 out.push(make_control_marker(
324 format!("End-{cf_id}"),
325 VisualControlFlowKind::End,
326 covered,
327 ));
328 }
329 VisualOpStyle::ControlFlow {
330 kind: VisualControlFlowKind::ForBlock { range },
331 } => {
332 let cf_id = *next_cf_id;
333 *next_cf_id += 1;
334 let covered = control_flow_marker_lanes(op, visual.num_qubits(), scope_lanes);
335 out.push(make_control_marker(
336 format!("For-{cf_id} {}", range.label),
337 VisualControlFlowKind::ForStart,
338 covered.clone(),
339 ));
340 if let Some(VisualChildren::For { body_circuit }) = op.children.as_ref() {
341 flatten_ops_recursive(body_circuit, next_cf_id, out, &covered);
342 }
343 out.push(make_control_marker(
344 format!("End-{cf_id}"),
345 VisualControlFlowKind::End,
346 covered,
347 ));
348 }
349 VisualOpStyle::ControlFlow {
350 kind: VisualControlFlowKind::SwitchBlock { target },
351 } => {
352 let cf_id = *next_cf_id;
353 *next_cf_id += 1;
354 let covered = control_flow_marker_lanes(op, visual.num_qubits(), scope_lanes);
355 out.push(make_control_marker(
356 format!("Switch-{cf_id} {}", target.label),
357 VisualControlFlowKind::SwitchStart,
358 covered.clone(),
359 ));
360 if let Some(VisualChildren::Switch {
361 case_circuits,
362 default_circuit,
363 }) = op.children.as_ref()
364 {
365 for (case_label, case_circuit) in case_circuits {
366 out.push(make_control_marker(
367 format!("Case-{cf_id} {case_label}"),
368 VisualControlFlowKind::CaseStart,
369 covered.clone(),
370 ));
371 flatten_ops_recursive(case_circuit, next_cf_id, out, &covered);
372 }
373 if let Some(default_body) = default_circuit {
374 out.push(make_control_marker(
375 format!("Default-{cf_id}"),
376 VisualControlFlowKind::DefaultStart,
377 covered.clone(),
378 ));
379 flatten_ops_recursive(default_body, next_cf_id, out, &covered);
380 }
381 }
382 out.push(make_control_marker(
383 format!("End-{cf_id}"),
384 VisualControlFlowKind::End,
385 covered,
386 ));
387 }
388 _ => {
389 let mut clone = op.clone();
390 clone.children = None;
391 clone.span_cols = 1;
392 clone.column = 0;
393 if matches!(clone.style, VisualOpStyle::ControlFlow { .. }) {
394 let covered = control_flow_marker_lanes(op, visual.num_qubits(), scope_lanes);
395 clone.lanes = covered.clone();
396 clone.covered_lanes = covered;
397 } else if clone.lanes.is_empty() {
398 clone.lanes = effective_covered_lanes(op, visual.num_qubits());
399 }
400 out.push(clone);
401 }
402 }
403 }
404}
405
406fn make_control_marker(
407 label: String,
408 kind: VisualControlFlowKind,
409 covered_lanes: Vec<usize>,
410) -> VisualOperation {
411 VisualOperation {
412 column: 0,
413 lanes: covered_lanes.clone(),
414 covered_lanes,
415 label,
416 params: Vec::new(),
417 style: VisualOpStyle::ControlFlow { kind },
418 span_box: false,
419 children: None,
420 span_cols: 1,
421 }
422}
423
424fn effective_covered_lanes(op: &VisualOperation, num_qubits: usize) -> Vec<usize> {
425 if !op.covered_lanes.is_empty() {
426 return op.covered_lanes.clone();
427 }
428 if !op.lanes.is_empty() {
429 return op.lanes.clone();
430 }
431 (0..num_qubits).collect()
432}
433
434fn scheduling_covered_lanes(op: &VisualOperation, num_qubits: usize) -> Vec<usize> {
435 if matches!(op.style, VisualOpStyle::ControlFlow { .. }) {
436 return (0..num_qubits).collect();
437 }
438 effective_covered_lanes(op, num_qubits)
439}
440
441fn control_flow_marker_lanes(
442 op: &VisualOperation,
443 num_qubits: usize,
444 scope_lanes: &[usize],
445) -> Vec<usize> {
446 let lanes = op
447 .children
448 .as_ref()
449 .and_then(collect_children_used_lanes)
450 .or_else(|| non_empty_lanes(&op.lanes))
451 .or_else(|| non_empty_lanes(scope_lanes))
452 .unwrap_or_else(|| effective_covered_lanes(op, num_qubits));
453
454 normalize_lane_span(&lanes)
455}
456
457fn collect_children_used_lanes(children: &VisualChildren) -> Option<Vec<usize>> {
458 let mut lanes = BTreeSet::new();
459 match children {
460 VisualChildren::IfElse {
461 then_circuit,
462 else_circuit,
463 } => {
464 collect_circuit_used_lanes(then_circuit, &mut lanes);
465 if let Some(else_circuit) = else_circuit {
466 collect_circuit_used_lanes(else_circuit, &mut lanes);
467 }
468 }
469 VisualChildren::While { body_circuit } | VisualChildren::For { body_circuit } => {
470 collect_circuit_used_lanes(body_circuit, &mut lanes);
471 }
472 VisualChildren::Switch {
473 case_circuits,
474 default_circuit,
475 } => {
476 for (_, case_circuit) in case_circuits {
477 collect_circuit_used_lanes(case_circuit, &mut lanes);
478 }
479 if let Some(default_circuit) = default_circuit {
480 collect_circuit_used_lanes(default_circuit, &mut lanes);
481 }
482 }
483 }
484
485 lanes_to_vec(lanes)
486}
487
488fn collect_circuit_used_lanes(visual: &VisualCircuit, lanes: &mut BTreeSet<usize>) {
489 for op in &visual.operations {
490 if let Some(children) = op.children.as_ref()
491 && let Some(child_lanes) = collect_children_used_lanes(children)
492 {
493 lanes.extend(child_lanes);
494 continue;
495 }
496
497 if !op.lanes.is_empty() {
498 lanes.extend(op.lanes.iter().copied());
499 } else if matches!(op.style, VisualOpStyle::Barrier) {
500 lanes.extend(effective_covered_lanes(op, visual.num_qubits()));
501 }
502 }
503}
504
505fn non_empty_lanes(lanes: &[usize]) -> Option<Vec<usize>> {
506 if lanes.is_empty() {
507 None
508 } else {
509 Some(lanes.to_vec())
510 }
511}
512
513fn lanes_to_vec(lanes: BTreeSet<usize>) -> Option<Vec<usize>> {
514 if lanes.is_empty() {
515 None
516 } else {
517 Some(lanes.into_iter().collect())
518 }
519}
520
521fn normalize_lane_span(lanes: &[usize]) -> Vec<usize> {
522 let Some(min_lane) = lanes.iter().copied().min() else {
523 return Vec::new();
524 };
525 let max_lane = lanes.iter().copied().max().unwrap_or(min_lane);
526 (min_lane..=max_lane).collect()
527}