From b626370063256c7faac3089878f1a3cfbaed2d28 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Wed, 6 Aug 2025 21:38:53 +0530 Subject: [PATCH 01/70] Entry point and function created, returns info about instructions in circuit --- crates/circuit/src/circuit_data.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/crates/circuit/src/circuit_data.rs b/crates/circuit/src/circuit_data.rs index fa2cc64f97b4..cf44679fe41f 100644 --- a/crates/circuit/src/circuit_data.rs +++ b/crates/circuit/src/circuit_data.rs @@ -1581,6 +1581,13 @@ impl CircuitData { Ok(()) } + // draw quantum circuit as an ASCII string art + #[pyo3(name = "draw")] + fn py_drawer(&self, py: Python) -> PyResult> { + let ret = self.circuit_draw(); + Ok(PyString::new(py, &ret).unbind()) + } + /// Add a captured variable to the circuit. /// /// Args: @@ -2872,6 +2879,24 @@ impl CircuitData { Ok(var_idx) } + pub fn circuit_draw(&self) -> String { + //"12346 vewfdvch.".to_string() + let mut res = String::new(); + res.push_str(& format!("{} Qubits and {} Instructions\n", + self.num_qubits(), self.data.len())); + for (i, inst) in self.data.iter().enumerate() { + let qubits = self.qargs_interner.get(inst.qubits); + + // Print qubit indices as numbers + let qubit_indices: Vec = qubits.iter() + .map(|qubit| qubit.0.to_string()) // Qubit wraps a u32 index + .collect(); + + res.push_str(&format!("{}: {} {:?} {:?}\n", i, inst.op.name(), qubits, inst.clbits)); + } + return res; + } + /// Return a variable given its unique [Var] index in the circuit or /// None if `var` is not a valid var index for this circuit. pub fn get_var(&self, var: Var) -> Option<&expr::Var> { From 8f0eab7cc3b5d9445bbbb6632983866457aa0c0f Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Thu, 7 Aug 2025 01:31:50 +0530 Subject: [PATCH 02/70] printing the qubits and clbits names --- crates/circuit/src/circuit_data.rs | 48 ++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/crates/circuit/src/circuit_data.rs b/crates/circuit/src/circuit_data.rs index cf44679fe41f..dfeb8d217808 100644 --- a/crates/circuit/src/circuit_data.rs +++ b/crates/circuit/src/circuit_data.rs @@ -2880,21 +2880,45 @@ impl CircuitData { } pub fn circuit_draw(&self) -> String { - //"12346 vewfdvch.".to_string() - let mut res = String::new(); - res.push_str(& format!("{} Qubits and {} Instructions\n", - self.num_qubits(), self.data.len())); - for (i, inst) in self.data.iter().enumerate() { - let qubits = self.qargs_interner.get(inst.qubits); + // let mut res = String::new(); + // res.push_str(& format!("{} Qubits and {} Instructions\n", + // self.num_qubits(), self.data.len())); + // for (i, inst) in self.data.iter().enumerate() { + // let qubits = self.qargs_interner.get(inst.qubits); + + // // Print qubit indices as numbers + // let qubit_indices: Vec = qubits.iter() + // .map(|qubit| qubit.0.to_string()) + // .collect(); + + // res.push_str(&format!("{}: {} {:?} {:?}\n", i, inst.op.name(), qubits, inst.clbits)); + // } + // return res; + let ct_qubits = self.qubits.len(); + let ct_clbits = self.clbits.len(); + // vector of strings with capacity (qubits + clbit) x 3 + let mut res = vec![" ".to_string(); (ct_qubits + ct_clbits) * 3]; - // Print qubit indices as numbers - let qubit_indices: Vec = qubits.iter() - .map(|qubit| qubit.0.to_string()) // Qubit wraps a u32 index - .collect(); + // concatinating all strings into one string with \n as separator + for i in 0..ct_qubits { + if (i-1) % 3 == 0 { + res[i].push_str(format!("q_{}", i).as_str()); + } else { + res[i].push_str(" "); + } + } - res.push_str(&format!("{}: {} {:?} {:?}\n", i, inst.op.name(), qubits, inst.clbits)); + for i in 0..ct_clbits { + if (i-1) % 3 == 0 { + res[ct_qubits + i].push_str(format!("c_{}", i).as_str()); + } else { + res[ct_qubits + i].push_str(" "); + } } - return res; + + //collect all strings and return + res.iter().map(|s| s.as_str()).collect::>().join("\n") + } /// Return a variable given its unique [Var] index in the circuit or From 64d4a3acfca88bff7fe0b910698a63aded0e46af Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Mon, 11 Aug 2025 12:42:49 +0530 Subject: [PATCH 03/70] fixed entry point for draw function --- crates/circuit/src/circuit_data.rs | 72 ++++++++++++------- .../circuit/circuit_visualization.py | 1 + 2 files changed, 49 insertions(+), 24 deletions(-) diff --git a/crates/circuit/src/circuit_data.rs b/crates/circuit/src/circuit_data.rs index dfeb8d217808..e2396006ee07 100644 --- a/crates/circuit/src/circuit_data.rs +++ b/crates/circuit/src/circuit_data.rs @@ -1582,10 +1582,23 @@ impl CircuitData { } // draw quantum circuit as an ASCII string art + + #[staticmethod] #[pyo3(name = "draw")] - fn py_drawer(&self, py: Python) -> PyResult> { - let ret = self.circuit_draw(); - Ok(PyString::new(py, &ret).unbind()) + fn py_drawer(py: Python, quantum_circuit: &Bound) -> PyResult> { + + if !quantum_circuit.is_instance(QUANTUM_CIRCUIT.get_bound(py))? { + return Err(PyTypeError::new_err( + "Expected a QuantumCircuit instance" + )); + } + + let circuit_data: CircuitData = quantum_circuit.getattr("_data")?.extract()?; + + // Execute the drawing logic on the extracted CircuitData + let drawing = circuit_data.circuit_draw(); + + Ok(PyString::new(py, &drawing).unbind()) } /// Add a captured variable to the circuit. @@ -2880,29 +2893,19 @@ impl CircuitData { } pub fn circuit_draw(&self) -> String { - // let mut res = String::new(); - // res.push_str(& format!("{} Qubits and {} Instructions\n", - // self.num_qubits(), self.data.len())); - // for (i, inst) in self.data.iter().enumerate() { - // let qubits = self.qargs_interner.get(inst.qubits); - - // // Print qubit indices as numbers - // let qubit_indices: Vec = qubits.iter() - // .map(|qubit| qubit.0.to_string()) - // .collect(); - - // res.push_str(&format!("{}: {} {:?} {:?}\n", i, inst.op.name(), qubits, inst.clbits)); - // } - // return res; + let ct_qubits = self.qubits.len(); let ct_clbits = self.clbits.len(); - // vector of strings with capacity (qubits + clbit) x 3 + println!("ct_qubits: {}, ct_clbits: {}", ct_qubits, ct_clbits); + let q_wires_rep: usize = ct_qubits * 3; + let c_wires_rep = ct_clbits * 3; + println!("q_wires: {}, c_wires: {}", q_wires_rep, c_wires_rep); + let mut res = vec![" ".to_string(); (ct_qubits + ct_clbits) * 3]; - // concatinating all strings into one string with \n as separator - for i in 0..ct_qubits { - if (i-1) % 3 == 0 { - res[i].push_str(format!("q_{}", i).as_str()); + for i in 1..q_wires_rep { + if (i-1) % 3 == 0 && (i-1) % 3 <= ct_qubits as usize { + res[i].push_str(format!("q_{}", (i-1)/3).as_str()); } else { res[i].push_str(" "); } @@ -2915,10 +2918,31 @@ impl CircuitData { res[ct_qubits + i].push_str(" "); } } - + + for i in self.data().iter() { + println!("op: {}, qubits: {:?}, clbits: {:?}", i.op.name(), i.qubits, i.clbits); + } + + for (i, inst) in self.data.iter().enumerate(){ + let name = inst.op.name().to_ascii_uppercase(); + let qubits = self.qargs_interner.get(inst.qubits); + let clbits = self.cargs_interner.get(inst.clbits); + for (j, qubit) in qubits.iter().enumerate() { + let index: usize = (qubit.0 * 3 + 1) as usize; + for k in 0..q_wires_rep { + if k == index { + res[index].push_str(&format!("-{}-", name)); + } else if (k - 1)%3 == 0{ + res[k].push_str("---"); + } else { + res[k].push_str(" "); + } + } + } + } //collect all strings and return res.iter().map(|s| s.as_str()).collect::>().join("\n") - + } /// Return a variable given its unique [Var] index in the circuit or diff --git a/qiskit/visualization/circuit/circuit_visualization.py b/qiskit/visualization/circuit/circuit_visualization.py index 917f83d50c6d..0ebde23ba25e 100644 --- a/qiskit/visualization/circuit/circuit_visualization.py +++ b/qiskit/visualization/circuit/circuit_visualization.py @@ -37,6 +37,7 @@ from qiskit import user_config from qiskit.circuit import ControlFlowOp, Measure from qiskit.utils import optionals as _optionals +from qiskit._accelerate.circuit import CircuitData from ..exceptions import VisualizationError from ..utils import _trim as trim_image From ed422d3e66b170f8f589ca87aebf13cafbd456b0 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Tue, 12 Aug 2025 23:52:34 +0530 Subject: [PATCH 04/70] addition to draw function, getting layers from DAG circuit --- crates/circuit/src/circuit_data.rs | 162 ++++++++++++++++++++--------- 1 file changed, 115 insertions(+), 47 deletions(-) diff --git a/crates/circuit/src/circuit_data.rs b/crates/circuit/src/circuit_data.rs index e2396006ee07..c490d806c8d5 100644 --- a/crates/circuit/src/circuit_data.rs +++ b/crates/circuit/src/circuit_data.rs @@ -1585,7 +1585,7 @@ impl CircuitData { #[staticmethod] #[pyo3(name = "draw")] - fn py_drawer(py: Python, quantum_circuit: &Bound) -> PyResult> { + fn py_drawer(py: Python, quantum_circuit: &Bound) -> PyResult<()> { if !quantum_circuit.is_instance(QUANTUM_CIRCUIT.get_bound(py))? { return Err(PyTypeError::new_err( @@ -1595,10 +1595,9 @@ impl CircuitData { let circuit_data: CircuitData = quantum_circuit.getattr("_data")?.extract()?; - // Execute the drawing logic on the extracted CircuitData - let drawing = circuit_data.circuit_draw(); + circuit_data.circuit_draw(); - Ok(PyString::new(py, &drawing).unbind()) + Ok(()) } /// Add a captured variable to the circuit. @@ -2892,57 +2891,126 @@ impl CircuitData { Ok(var_idx) } - pub fn circuit_draw(&self) -> String { + // pub fn circuit_draw(&self) -> String { - let ct_qubits = self.qubits.len(); - let ct_clbits = self.clbits.len(); - println!("ct_qubits: {}, ct_clbits: {}", ct_qubits, ct_clbits); - let q_wires_rep: usize = ct_qubits * 3; - let c_wires_rep = ct_clbits * 3; - println!("q_wires: {}, c_wires: {}", q_wires_rep, c_wires_rep); + // let ct_qubits = self.qubits.len(); + // let ct_clbits = self.clbits.len(); + // println!("ct_qubits: {}, ct_clbits: {}", ct_qubits, ct_clbits); + // let q_wires_rep: usize = ct_qubits * 3; + // let c_wires_rep = ct_clbits * 3; + // println!("q_wires: {}, c_wires: {}", q_wires_rep, c_wires_rep); - let mut res = vec![" ".to_string(); (ct_qubits + ct_clbits) * 3]; + // let mut res = vec![" ".to_string(); (ct_qubits + ct_clbits) * 3]; - for i in 1..q_wires_rep { - if (i-1) % 3 == 0 && (i-1) % 3 <= ct_qubits as usize { - res[i].push_str(format!("q_{}", (i-1)/3).as_str()); - } else { - res[i].push_str(" "); - } - } - - for i in 0..ct_clbits { - if (i-1) % 3 == 0 { - res[ct_qubits + i].push_str(format!("c_{}", i).as_str()); - } else { - res[ct_qubits + i].push_str(" "); - } - } + // for i in 1..q_wires_rep { + // if (i-1) % 3 == 0 && (i-1) % 3 <= ct_qubits as usize { + // res[i].push_str(format!("q_{}", (i-1)/3).as_str()); + // } else { + // res[i].push_str(" "); + // } + // } + + // for i in 0..ct_clbits { + // if (i-1) % 3 == 0 { + // res[ct_qubits + i].push_str(format!("c_{}", i).as_str()); + // } else { + // res[ct_qubits + i].push_str(" "); + // } + // } + + // for i in self.data().iter() { + // println!("op: {}, qubits: {:?}, clbits: {:?}", i.op.name(), i.qubits, i.clbits); + // } + + // for (i, inst) in self.data.iter().enumerate(){ + // let name = inst.op.name().to_ascii_uppercase(); + // let qubits = self.qargs_interner.get(inst.qubits); + // let clbits = self.cargs_interner.get(inst.clbits); + // for (j, qubit) in qubits.iter().enumerate() { + // let index: usize = (qubit.0 * 3 + 1) as usize; + // for k in 0..q_wires_rep { + // if k == index { + // res[index].push_str(&format!("-{}-", name)); + // } else if (k - 1)%3 == 0{ + // res[k].push_str("---"); + // } else { + // res[k].push_str(" "); + // } + // } + // } + // } + // //collect all strings and return + // res.iter().map(|s| s.as_str()).collect::>().join("\n") + + // } + + pub fn circuit_draw(&self) { + use crate::converters::circuit_to_dag; + use crate::converters::QuantumCircuitData; + use crate::dag_circuit::NodeType; + + let quantum_circuit_data = QuantumCircuitData { + data: self.clone(), + name: None, + metadata: None, + }; + let dag_circuit = circuit_to_dag(quantum_circuit_data, true, None, None) + .expect("Failed to convert circuit data to DAGCircuit"); + + let mut output = String::new(); + output.push_str("DAG Circuit Operations:\n"); + output.push_str(&format!("Number of qubits: {}\n", dag_circuit.num_qubits())); + output.push_str(&format!("Number of operations: {}\n", dag_circuit.num_ops())); + output.push_str("Operations:\n"); + + let layer_iterator = dag_circuit.multigraph_layers(); + for (layer_index, layer) in layer_iterator.enumerate() { + output.push_str(&format!("Layer {}:\n", layer_index)); + + // Filter for operation nodes only + let operations: Vec<_> = layer + .into_iter() + .filter_map(|node_index| { + match &dag_circuit.dag()[node_index] { + NodeType::Operation(instruction) => Some((node_index, instruction)), + _ => None, // Skip input/output nodes + } + }) + .collect(); - for i in self.data().iter() { - println!("op: {}, qubits: {:?}, clbits: {:?}", i.op.name(), i.qubits, i.clbits); + if operations.is_empty() { + continue; // Skip layers with no operations } - - for (i, inst) in self.data.iter().enumerate(){ - let name = inst.op.name().to_ascii_uppercase(); - let qubits = self.qargs_interner.get(inst.qubits); - let clbits = self.cargs_interner.get(inst.clbits); - for (j, qubit) in qubits.iter().enumerate() { - let index: usize = (qubit.0 * 3 + 1) as usize; - for k in 0..q_wires_rep { - if k == index { - res[index].push_str(&format!("-{}-", name)); - } else if (k - 1)%3 == 0{ - res[k].push_str("---"); - } else { - res[k].push_str(" "); - } - } + + for (node_index, instruction) in operations { + let op_name = instruction.op.name(); + let qubits = dag_circuit.qargs_interner().get(instruction.qubits); + let clbits = dag_circuit.cargs_interner().get(instruction.clbits); + + let qubit_str = qubits.iter() + .map(|q| q.0.to_string()) + .collect::>() + .join(","); + + let clbit_str = clbits.iter() + .map(|c| c.0.to_string()) + .collect::>() + .join(","); + + output.push_str(&format!( + " Node {}: {} qubits=[{}] clbits=[{}]\n", + node_index.index(), op_name, qubit_str, clbit_str + )); + + // Print parameters if any + let params = instruction.params_view(); + if !params.is_empty() { + output.push_str(&format!(" params: {:?}\n", params)); } } - //collect all strings and return - res.iter().map(|s| s.as_str()).collect::>().join("\n") + } + println!("{}",output); } /// Return a variable given its unique [Var] index in the circuit or From 1e9da89452282f7ad032b109cbb9a11a46fbabda Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Wed, 13 Aug 2025 14:40:27 +0530 Subject: [PATCH 05/70] code moved to circuit_drawer.rs and added as module for import, no longer needed to import qiskit._accelerate, can simply call qc.draw(text2) --- crates/circuit/src/circuit_data.rs | 141 ----------------------- crates/circuit/src/circuit_drawer.rs | 163 +++++++++++++++++++++++++++ crates/circuit/src/lib.rs | 2 + 3 files changed, 165 insertions(+), 141 deletions(-) create mode 100644 crates/circuit/src/circuit_drawer.rs diff --git a/crates/circuit/src/circuit_data.rs b/crates/circuit/src/circuit_data.rs index c490d806c8d5..fa2cc64f97b4 100644 --- a/crates/circuit/src/circuit_data.rs +++ b/crates/circuit/src/circuit_data.rs @@ -1581,25 +1581,6 @@ impl CircuitData { Ok(()) } - // draw quantum circuit as an ASCII string art - - #[staticmethod] - #[pyo3(name = "draw")] - fn py_drawer(py: Python, quantum_circuit: &Bound) -> PyResult<()> { - - if !quantum_circuit.is_instance(QUANTUM_CIRCUIT.get_bound(py))? { - return Err(PyTypeError::new_err( - "Expected a QuantumCircuit instance" - )); - } - - let circuit_data: CircuitData = quantum_circuit.getattr("_data")?.extract()?; - - circuit_data.circuit_draw(); - - Ok(()) - } - /// Add a captured variable to the circuit. /// /// Args: @@ -2891,128 +2872,6 @@ impl CircuitData { Ok(var_idx) } - // pub fn circuit_draw(&self) -> String { - - // let ct_qubits = self.qubits.len(); - // let ct_clbits = self.clbits.len(); - // println!("ct_qubits: {}, ct_clbits: {}", ct_qubits, ct_clbits); - // let q_wires_rep: usize = ct_qubits * 3; - // let c_wires_rep = ct_clbits * 3; - // println!("q_wires: {}, c_wires: {}", q_wires_rep, c_wires_rep); - - // let mut res = vec![" ".to_string(); (ct_qubits + ct_clbits) * 3]; - - // for i in 1..q_wires_rep { - // if (i-1) % 3 == 0 && (i-1) % 3 <= ct_qubits as usize { - // res[i].push_str(format!("q_{}", (i-1)/3).as_str()); - // } else { - // res[i].push_str(" "); - // } - // } - - // for i in 0..ct_clbits { - // if (i-1) % 3 == 0 { - // res[ct_qubits + i].push_str(format!("c_{}", i).as_str()); - // } else { - // res[ct_qubits + i].push_str(" "); - // } - // } - - // for i in self.data().iter() { - // println!("op: {}, qubits: {:?}, clbits: {:?}", i.op.name(), i.qubits, i.clbits); - // } - - // for (i, inst) in self.data.iter().enumerate(){ - // let name = inst.op.name().to_ascii_uppercase(); - // let qubits = self.qargs_interner.get(inst.qubits); - // let clbits = self.cargs_interner.get(inst.clbits); - // for (j, qubit) in qubits.iter().enumerate() { - // let index: usize = (qubit.0 * 3 + 1) as usize; - // for k in 0..q_wires_rep { - // if k == index { - // res[index].push_str(&format!("-{}-", name)); - // } else if (k - 1)%3 == 0{ - // res[k].push_str("---"); - // } else { - // res[k].push_str(" "); - // } - // } - // } - // } - // //collect all strings and return - // res.iter().map(|s| s.as_str()).collect::>().join("\n") - - // } - - pub fn circuit_draw(&self) { - use crate::converters::circuit_to_dag; - use crate::converters::QuantumCircuitData; - use crate::dag_circuit::NodeType; - - let quantum_circuit_data = QuantumCircuitData { - data: self.clone(), - name: None, - metadata: None, - }; - let dag_circuit = circuit_to_dag(quantum_circuit_data, true, None, None) - .expect("Failed to convert circuit data to DAGCircuit"); - - let mut output = String::new(); - output.push_str("DAG Circuit Operations:\n"); - output.push_str(&format!("Number of qubits: {}\n", dag_circuit.num_qubits())); - output.push_str(&format!("Number of operations: {}\n", dag_circuit.num_ops())); - output.push_str("Operations:\n"); - - let layer_iterator = dag_circuit.multigraph_layers(); - for (layer_index, layer) in layer_iterator.enumerate() { - output.push_str(&format!("Layer {}:\n", layer_index)); - - // Filter for operation nodes only - let operations: Vec<_> = layer - .into_iter() - .filter_map(|node_index| { - match &dag_circuit.dag()[node_index] { - NodeType::Operation(instruction) => Some((node_index, instruction)), - _ => None, // Skip input/output nodes - } - }) - .collect(); - - if operations.is_empty() { - continue; // Skip layers with no operations - } - - for (node_index, instruction) in operations { - let op_name = instruction.op.name(); - let qubits = dag_circuit.qargs_interner().get(instruction.qubits); - let clbits = dag_circuit.cargs_interner().get(instruction.clbits); - - let qubit_str = qubits.iter() - .map(|q| q.0.to_string()) - .collect::>() - .join(","); - - let clbit_str = clbits.iter() - .map(|c| c.0.to_string()) - .collect::>() - .join(","); - - output.push_str(&format!( - " Node {}: {} qubits=[{}] clbits=[{}]\n", - node_index.index(), op_name, qubit_str, clbit_str - )); - - // Print parameters if any - let params = instruction.params_view(); - if !params.is_empty() { - output.push_str(&format!(" params: {:?}\n", params)); - } - } - } - - println!("{}",output); - } - /// Return a variable given its unique [Var] index in the circuit or /// None if `var` is not a valid var index for this circuit. pub fn get_var(&self, var: Var) -> Option<&expr::Var> { diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs new file mode 100644 index 000000000000..be60d621be39 --- /dev/null +++ b/crates/circuit/src/circuit_drawer.rs @@ -0,0 +1,163 @@ +// This code is part of Qiskit. +// +// (C) Copyright IBM 2023, 2024 +// +// This code is licensed under the Apache License, Version 2.0. You may +// obtain a copy of this license in the LICENSE.txt file in the root directory +// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. + +use std::fmt::Debug; +use std::hash::{Hash, RandomState}; +#[cfg(feature = "cache_pygates")] +use std::sync::OnceLock; + +use crate::bit::{ + BitLocations, ClassicalRegister, PyBit, QuantumRegister, Register, ShareableClbit, + ShareableQubit, +}; +use crate::bit_locator::BitLocator; +use crate::circuit_instruction::{CircuitInstruction, OperationFromPython}; +use crate::classical::expr; +use crate::dag_circuit::{add_global_phase, DAGStretchType, DAGVarType}; +use crate::imports::{ANNOTATED_OPERATION, QUANTUM_CIRCUIT}; +use crate::interner::{Interned, Interner}; +use crate::object_registry::ObjectRegistry; +use crate::operations::{Operation, OperationRef, Param, PythonOperation, StandardGate}; +use crate::packed_instruction::{PackedInstruction, PackedOperation}; +use crate::parameter_table::{ParameterTable, ParameterTableError, ParameterUse, ParameterUuid}; +use crate::register_data::RegisterData; +use crate::slice::{PySequenceIndex, SequenceIndex}; +use crate::{Clbit, Qubit, Stretch, Var, VarsMode}; + +use numpy::PyReadonlyArray1; +use pyo3::exceptions::*; +use pyo3::prelude::*; +use pyo3::{import_exception}; + + +use crate::converters::circuit_to_dag; +use crate::converters::QuantumCircuitData; +use crate::dag_circuit::NodeType; +use crate::circuit_data::CircuitData; + +import_exception!(qiskit.circuit.exceptions, CircuitError); + +#[pyclass(sequence, module = "qiskit._accelerate.circuit")] +pub struct CircuitDrawer; + +#[pymethods] +impl CircuitDrawer{ + + #[staticmethod] + #[pyo3(name = "draw")] + fn py_drawer(py: Python, quantum_circuit: &Bound) -> PyResult<()> { + if !quantum_circuit.is_instance(QUANTUM_CIRCUIT.get_bound(py))? { + return Err(PyTypeError::new_err( + "Expected a QuantumCircuit instance" + )); + } + println!("FUNCTION IS BEING CALLED FROM circuit_drawer.rs FILE"); + let circ_data: CircuitData = quantum_circuit.getattr("_data")?.extract()?; + circuit_draw(&circ_data); + Ok(()) + } +} + + +pub fn circuit_draw(circ_data: &CircuitData) { + + let quantum_circuit_data = QuantumCircuitData { + data: circ_data.clone(), + name: None, + metadata: None, + }; + + let dag_circuit = circuit_to_dag(quantum_circuit_data, true, None, None) + .expect("Failed to convert circuit data to DAGCircuit"); + + let mut output = String::new(); + output.push_str("DAG Circuit Operations:\n"); + output.push_str(&format!("Number of qubits: {}\n", dag_circuit.num_qubits())); + output.push_str(&format!("Number of operations: {}\n", dag_circuit.num_ops())); + output.push_str("Operations:\n"); + + //creating representation where each wire is represented by 3 strings + let mut circuit_rep: Vec = vec![String::new(); (dag_circuit.num_qubits() + 1) * 3]; + + // Fill the first column with qubit labels + for (i, qubit) in dag_circuit.qubits().objects().iter().enumerate() { + let qubit_index = i * 3 + 1; + let qubit_name = format!("q_{}: ", i); + circuit_rep[qubit_index].push_str(&qubit_name); + circuit_rep[qubit_index - 1].push_str(" ".repeat((&qubit_name).len()).as_str()); + circuit_rep[qubit_index + 1].push_str(" ".repeat((&qubit_name).len()).as_str()); + } + + // Print the circuit representation + for i in circuit_rep { + println!("{}", i); + } + //getting qubits and clbit information + for (index, qubit) in dag_circuit.qubits().objects().iter().enumerate() { + println!("Qubit {}: {:?}", index, qubit); + }; + + // Iterate through clbits with their indices + for (index, clbit) in dag_circuit.clbits().objects().iter().enumerate() { + println!("Clbit {}: {:?}", index, clbit); + }; + + let layer_iterator = dag_circuit.multigraph_layers(); + for (layer_index, layer) in layer_iterator.enumerate() { + output.push_str(&format!("Layer {}:\n", layer_index)); + + // Filter for operation nodes only + let operations: Vec<_> = layer + .into_iter() + .filter_map(|node_index| { + match &dag_circuit.dag()[node_index] { + NodeType::Operation(instruction) => Some((node_index, instruction)), + _ => None, // Skip input/output nodes + } + }) + .collect(); + + if operations.is_empty() { + continue; // Skip layers with no operations + } + + for (node_index, instruction) in operations { + let op_name = instruction.op.name(); + let qubits = dag_circuit.qargs_interner().get(instruction.qubits); + let clbits = dag_circuit.cargs_interner().get(instruction.clbits); + + let qubit_str = qubits.iter() + .map(|q| q.0.to_string()) + .collect::>() + .join(","); + + let clbit_str = clbits.iter() + .map(|c| c.0.to_string()) + .collect::>() + .join(","); + + output.push_str(&format!( + " Node {}: {} qubits=[{}] clbits=[{}]\n", + node_index.index(), op_name, qubit_str, clbit_str + )); + + // Print parameters if any + let params = instruction.params_view(); + if !params.is_empty() { + output.push_str(&format!(" params: {:?}\n", params)); + } + } + } + + println!("{}",output); +} + diff --git a/crates/circuit/src/lib.rs b/crates/circuit/src/lib.rs index 38ab85d79949..4cc9e6cb556a 100644 --- a/crates/circuit/src/lib.rs +++ b/crates/circuit/src/lib.rs @@ -37,6 +37,7 @@ pub mod register_data; pub mod slice; pub mod util; pub mod vf2; +pub mod circuit_drawer; mod variable_mapper; @@ -256,6 +257,7 @@ pub fn circuit(m: &Bound) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + m.add_class::()?; let classical_mod = PyModule::new(m.py(), "classical")?; classical::register_python(&classical_mod)?; m.add_submodule(&classical_mod)?; From 8d585029c6923f8c2daf90fe376f731800cd1dbd Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Sun, 17 Aug 2025 18:43:22 +0530 Subject: [PATCH 06/70] refactored code, wire and circuit class --- crates/circuit/src/circuit_drawer.rs | 307 ++++++++++++++++++++------- 1 file changed, 234 insertions(+), 73 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index be60d621be39..4543ff7a69d5 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -22,8 +22,9 @@ use crate::bit::{ use crate::bit_locator::BitLocator; use crate::circuit_instruction::{CircuitInstruction, OperationFromPython}; use crate::classical::expr; -use crate::dag_circuit::{add_global_phase, DAGStretchType, DAGVarType}; +use crate::dag_circuit::{self, add_global_phase, DAGCircuit, DAGStretchType, DAGVarType}; use crate::imports::{ANNOTATED_OPERATION, QUANTUM_CIRCUIT}; +use rustworkx_core::petgraph::stable_graph::{EdgeReference, NodeIndex}; use crate::interner::{Interned, Interner}; use crate::object_registry::ObjectRegistry; use crate::operations::{Operation, OperationRef, Param, PythonOperation, StandardGate}; @@ -67,6 +68,155 @@ impl CircuitDrawer{ } } +pub const q_wire: &str = "─"; +pub const c_wire: char = '═'; +pub const top_con: char = '┴'; +pub const bot_con: char = '┬'; +pub const left_con: char = '┤'; +pub const right_con: char = '├'; +pub const top_left_con: char = '┌'; +pub const top_right_con: char = '┐'; +pub const bot_left_con: char = '└'; +pub const bot_right_con: char = '┘'; + +#[derive(Clone)] +pub struct qubit_wire { + pub top: String, + pub mid: String, + pub bot: String, + pub wire_len: u64 +} + +impl qubit_wire { + pub fn new() -> Self { + qubit_wire { + top: String::new(), + mid: String::new(), + bot: String::new(), + wire_len: 0 + } + } + + pub fn update_wire_len(&mut self) { + let top_len = self.top.len(); + let mid_len = self.mid.len(); + let bot_len = self.bot.len(); + if top_len == mid_len && mid_len == bot_len { + self.wire_len = top_len as u64; + } else { + panic!("The lengths of the wire components are not equal"); + } + } + + // setting qubit name + pub fn qubit_name(&mut self, qubit_name: &str) { + let name_len = qubit_name.len(); + self.top.push_str(" ".repeat(name_len).as_str()); + self.mid.push_str(&format!("{}", qubit_name)); + self.bot.push_str(" ".repeat(name_len).as_str()); + self.update_wire_len(); + } + + // concatenate full wire representation and send for printing + pub fn get_wire_rep(&self) -> String { + let mut wire_rep = String::new(); + wire_rep.push_str(&self.top); + wire_rep.push('\n'); + wire_rep.push_str(&self.mid); + wire_rep.push('\n'); + wire_rep.push_str(&self.bot); + wire_rep.push('\n'); + wire_rep + } + + pub fn fix_len(&mut self, num: u64) { + self.wire_len = self.wire_len + num; + self.top.push_str(" ".repeat(num as usize).as_str()); + self.mid.push_str(q_wire.repeat(num as usize).as_str()); + self.bot.push_str(" ".repeat(num as usize).as_str()); + } +} + +pub struct circuit_rep { + q_wires: Vec::, + dag_circ: DAGCircuit +} + +impl circuit_rep { + pub fn new(dag_circ: DAGCircuit) -> Self { + + //number of qubits in dag_circuit + let qubit = dag_circ.num_qubits(); + + circuit_rep { + q_wires: vec!(qubit_wire::new(); qubit as usize), + dag_circ: dag_circ + } + } + + pub fn circuit_string(&self) -> String { + let mut output = String::new(); + for wires in self.q_wires.iter() { + output.push_str(&wires.get_wire_rep()); + } + output + } + + pub fn fix_len(&mut self) { + let mut num = 0; + for wire in self.q_wires.iter() { + if wire.wire_len > num { + num = wire.wire_len; + } + } + + for wire in self.q_wires.iter_mut() { + wire.fix_len(num - wire.wire_len); + } + } + + pub fn set_qubit_name(&mut self) { + for (i, qubit) in self.dag_circ.qubits().objects().iter().enumerate() { + let qubit_name = if let Some(locations) = self.dag_circ.qubit_locations().get(qubit) { + if let Some((register, reg_index)) = locations.registers().first() { + format!("{}[{}]", register.name(), reg_index) + } else { + format!("q_{}", i) + } + } else { + format!("q_{}", i) + }; + self.q_wires[i].qubit_name(&qubit_name); + } + self.fix_len(); + } + + pub fn build_layer(&mut self, layer: Vec<&PackedInstruction>){ + println!("{:?}",layer); + } + + pub fn build_layers(&mut self) { + let binding = self.dag_circ.clone(); + let layer_iterator = binding.multigraph_layers(); + for (layer_index, layer) in layer_iterator.enumerate() { + //create a vector of packed operations using the vectors of + + let operations: Vec<&PackedInstruction> = layer + .into_iter() + .filter_map(|node_index| { + match &binding.dag()[node_index] { + NodeType::Operation(instruction) => Some(instruction), + _ => None, // Skip input/output nodes + } + }) + .collect(); + + self.build_layer(operations); + + self.fix_len(); + } + } +} pub fn circuit_draw(circ_data: &CircuitData) { @@ -80,84 +230,95 @@ pub fn circuit_draw(circ_data: &CircuitData) { .expect("Failed to convert circuit data to DAGCircuit"); let mut output = String::new(); - output.push_str("DAG Circuit Operations:\n"); - output.push_str(&format!("Number of qubits: {}\n", dag_circuit.num_qubits())); - output.push_str(&format!("Number of operations: {}\n", dag_circuit.num_ops())); - output.push_str("Operations:\n"); - - //creating representation where each wire is represented by 3 strings - let mut circuit_rep: Vec = vec![String::new(); (dag_circuit.num_qubits() + 1) * 3]; - - // Fill the first column with qubit labels - for (i, qubit) in dag_circuit.qubits().objects().iter().enumerate() { - let qubit_index = i * 3 + 1; - let qubit_name = format!("q_{}: ", i); - circuit_rep[qubit_index].push_str(&qubit_name); - circuit_rep[qubit_index - 1].push_str(" ".repeat((&qubit_name).len()).as_str()); - circuit_rep[qubit_index + 1].push_str(" ".repeat((&qubit_name).len()).as_str()); - } + // Create a circuit representation + let mut circuit_rep = circuit_rep::new(dag_circuit.clone()); + circuit_rep.set_qubit_name(); + output.push_str(&circuit_rep.circuit_string()); + circuit_rep.build_layers(); // Print the circuit representation - for i in circuit_rep { - println!("{}", i); - } - //getting qubits and clbit information - for (index, qubit) in dag_circuit.qubits().objects().iter().enumerate() { - println!("Qubit {}: {:?}", index, qubit); - }; - - // Iterate through clbits with their indices - for (index, clbit) in dag_circuit.clbits().objects().iter().enumerate() { - println!("Clbit {}: {:?}", index, clbit); - }; + println!("{}", output); - let layer_iterator = dag_circuit.multigraph_layers(); - for (layer_index, layer) in layer_iterator.enumerate() { - output.push_str(&format!("Layer {}:\n", layer_index)); + // output.push_str("DAG Circuit Operations:\n"); + // output.push_str(&format!("Number of qubits: {}\n", dag_circuit.num_qubits())); + // output.push_str(&format!("Number of operations: {}\n", dag_circuit.num_ops())); + // output.push_str("Operations:\n"); - // Filter for operation nodes only - let operations: Vec<_> = layer - .into_iter() - .filter_map(|node_index| { - match &dag_circuit.dag()[node_index] { - NodeType::Operation(instruction) => Some((node_index, instruction)), - _ => None, // Skip input/output nodes - } - }) - .collect(); + // // creating representation where each wire is represented by 3 strings + // let mut circuit_rep: Vec = vec![String::new(); (dag_circuit.num_qubits() + 1) * 3]; + + // // Fill the first column with qubit labels + // for (i, qubit) in dag_circuit.qubits().objects().iter().enumerate() { + // let qubit_index = i * 3 + 1; + // let qubit_name = format!("q_{}: ", i); + // circuit_rep[qubit_index].push_str(&qubit_name); + // circuit_rep[qubit_index - 1].push_str(" ".repeat((&qubit_name).len()).as_str()); + // circuit_rep[qubit_index + 1].push_str(" ".repeat((&qubit_name).len()).as_str()); + // } + + // // Print the circuit representation + // for i in circuit_rep { + // println!("{}", i); + // } + // //getting qubits and clbit information + // for (index, qubit) in dag_circuit.qubits().objects().iter().enumerate() { + // println!("Qubit {}: {:?}", index, qubit); + // }; + + // // Iterate through clbits with their indices + // for (index, clbit) in dag_circuit.clbits().objects().iter().enumerate() { + // println!("Clbit {}: {:?}", index, clbit); + // }; + + // let layer_iterator = dag_circuit.multigraph_layers(); + // for (layer_index, layer) in layer_iterator.enumerate() { + // output.push_str(&format!("Layer {}:\n", layer_index)); - if operations.is_empty() { - continue; // Skip layers with no operations - } + // // Filter for operation nodes only + // let operations: Vec<_> = layer + // .into_iter() + // .filter_map(|node_index| { + // match &dag_circuit.dag()[node_index] { + // NodeType::Operation(instruction) => Some((node_index, instruction)), + // _ => None, // Skip input/output nodes + // } + // }) + // .collect(); - for (node_index, instruction) in operations { - let op_name = instruction.op.name(); - let qubits = dag_circuit.qargs_interner().get(instruction.qubits); - let clbits = dag_circuit.cargs_interner().get(instruction.clbits); - - let qubit_str = qubits.iter() - .map(|q| q.0.to_string()) - .collect::>() - .join(","); - - let clbit_str = clbits.iter() - .map(|c| c.0.to_string()) - .collect::>() - .join(","); - - output.push_str(&format!( - " Node {}: {} qubits=[{}] clbits=[{}]\n", - node_index.index(), op_name, qubit_str, clbit_str - )); + // if operations.is_empty() { + // return; + // } + + // for (node_index, instruction) in operations { + // let standard_gate = StandardGate::try_from(&instruction.op).unwrap(); + // let op_name = instruction.op.name(); + // let qubits = self.dag_circ.qargs_interner().get(instruction.qubits); + // let clbits = self.dag_circ.cargs_interner().get(instruction.clbits); + + // let qubit_str = qubits.iter() + // .map(|q| q.0.to_string()) + // .collect::>() + // .join(","); - // Print parameters if any - let params = instruction.params_view(); - if !params.is_empty() { - output.push_str(&format!(" params: {:?}\n", params)); - } - } - } + // let clbit_str = clbits.iter() + // .map(|c| c.0.to_string()) + // .collect::>() + // .join(","); + + // // Here, you might want to store or process the operation info as needed. + // // For now, just print or log as a placeholder. + // println!( + // " Node {}: {} qubits=[{}] clbits=[{}]", + // node_index.index(), op_name, qubit_str, clbit_str + // ); + + // // Print parameters if any + // let params = instruction.params_view(); + // if !params.is_empty() { + // println!(" params: {:?}", params); + // } + // } - println!("{}",output); + // println!("{}",output); + // } } - From 9898fb78a382a117fa19ce89a6f28f18460798ae Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Mon, 18 Aug 2025 17:37:57 +0530 Subject: [PATCH 07/70] minor qubit rep fixes --- crates/circuit/src/circuit_drawer.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 4543ff7a69d5..b752335ca913 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -108,7 +108,7 @@ impl qubit_wire { } } - // setting qubit name + // setting qubit names pub fn qubit_name(&mut self, qubit_name: &str) { let name_len = qubit_name.len(); self.top.push_str(" ".repeat(name_len).as_str()); @@ -129,10 +129,10 @@ impl qubit_wire { wire_rep } - pub fn fix_len(&mut self, num: u64) { + pub fn fix_len(&mut self, num: u64, chr: &str) { self.wire_len = self.wire_len + num; self.top.push_str(" ".repeat(num as usize).as_str()); - self.mid.push_str(q_wire.repeat(num as usize).as_str()); + self.mid.push_str(chr.repeat(num as usize).as_str()); self.bot.push_str(" ".repeat(num as usize).as_str()); } } @@ -162,7 +162,7 @@ impl circuit_rep { output } - pub fn fix_len(&mut self) { + pub fn fix_len(&mut self, chr: &str) { let mut num = 0; for wire in self.q_wires.iter() { if wire.wire_len > num { @@ -171,7 +171,7 @@ impl circuit_rep { } for wire in self.q_wires.iter_mut() { - wire.fix_len(num - wire.wire_len); + wire.fix_len(num - wire.wire_len, chr); } } @@ -179,7 +179,7 @@ impl circuit_rep { for (i, qubit) in self.dag_circ.qubits().objects().iter().enumerate() { let qubit_name = if let Some(locations) = self.dag_circ.qubit_locations().get(qubit) { if let Some((register, reg_index)) = locations.registers().first() { - format!("{}[{}]", register.name(), reg_index) + format!("{}_{}", register.name(), reg_index) } else { format!("q_{}", i) } @@ -188,7 +188,7 @@ impl circuit_rep { }; self.q_wires[i].qubit_name(&qubit_name); } - self.fix_len(); + self.fix_len(" "); } pub fn build_layer(&mut self, layer: Vec<&PackedInstruction>){ @@ -213,7 +213,7 @@ impl circuit_rep { self.build_layer(operations); - self.fix_len(); + self.fix_len(q_wire); } } } From f4fc501a61d78a88b4f88531fd7ca87f5b47bed7 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Mon, 25 Aug 2025 12:08:17 +0530 Subject: [PATCH 08/70] changed from pymodule to pyfunction --- crates/circuit/src/circuit_drawer.rs | 31 ++++++++++++---------------- crates/circuit/src/lib.rs | 3 ++- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index b752335ca913..17b5969794d2 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -47,27 +47,22 @@ use crate::circuit_data::CircuitData; import_exception!(qiskit.circuit.exceptions, CircuitError); -#[pyclass(sequence, module = "qiskit._accelerate.circuit")] -pub struct CircuitDrawer; - -#[pymethods] -impl CircuitDrawer{ - - #[staticmethod] - #[pyo3(name = "draw")] - fn py_drawer(py: Python, quantum_circuit: &Bound) -> PyResult<()> { - if !quantum_circuit.is_instance(QUANTUM_CIRCUIT.get_bound(py))? { - return Err(PyTypeError::new_err( - "Expected a QuantumCircuit instance" - )); - } - println!("FUNCTION IS BEING CALLED FROM circuit_drawer.rs FILE"); - let circ_data: CircuitData = quantum_circuit.getattr("_data")?.extract()?; - circuit_draw(&circ_data); - Ok(()) + +#[pyfunction] +#[pyo3(name = "draw")] +pub fn py_drawer(py: Python, quantum_circuit: &Bound) -> PyResult<()> { + if !quantum_circuit.is_instance(QUANTUM_CIRCUIT.get_bound(py))? { + return Err(PyTypeError::new_err( + "Expected a QuantumCircuit instance" + )); } + println!("FUNCTION IS BEING CALLED FROM circuit_drawer.rs FILE"); + let circ_data: CircuitData = quantum_circuit.getattr("_data")?.extract()?; + circuit_draw(&circ_data); + Ok(()) } + pub const q_wire: &str = "─"; pub const c_wire: char = '═'; pub const top_con: char = '┴'; diff --git a/crates/circuit/src/lib.rs b/crates/circuit/src/lib.rs index 4cc9e6cb556a..9f11c1bb9c94 100644 --- a/crates/circuit/src/lib.rs +++ b/crates/circuit/src/lib.rs @@ -257,7 +257,8 @@ pub fn circuit(m: &Bound) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; - m.add_class::()?; + // m.add_class::()?; + m.add_function(wrap_pyfunction!(circuit_drawer::py_drawer, m)?)?; let classical_mod = PyModule::new(m.py(), "classical")?; classical::register_python(&classical_mod)?; m.add_submodule(&classical_mod)?; From b8d29b5790deea97c3949a601dfd8cfa4fee6f6b Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Mon, 25 Aug 2025 15:47:44 +0530 Subject: [PATCH 09/70] circuit to dag conversion moved to python --- crates/circuit/src/circuit_drawer.rs | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 17b5969794d2..6ad4d9549b9c 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -50,15 +50,10 @@ import_exception!(qiskit.circuit.exceptions, CircuitError); #[pyfunction] #[pyo3(name = "draw")] -pub fn py_drawer(py: Python, quantum_circuit: &Bound) -> PyResult<()> { - if !quantum_circuit.is_instance(QUANTUM_CIRCUIT.get_bound(py))? { - return Err(PyTypeError::new_err( - "Expected a QuantumCircuit instance" - )); - } - println!("FUNCTION IS BEING CALLED FROM circuit_drawer.rs FILE"); - let circ_data: CircuitData = quantum_circuit.getattr("_data")?.extract()?; - circuit_draw(&circ_data); +pub fn py_drawer(py: Python, dag_circ: &Bound) -> PyResult<()> { + let dag_circ = dag_circ.extract::()?; + println!("function is being called from circuit_drawer.rs FILE"); + circuit_draw(&dag_circ); Ok(()) } @@ -213,16 +208,9 @@ impl circuit_rep { } } -pub fn circuit_draw(circ_data: &CircuitData) { - - let quantum_circuit_data = QuantumCircuitData { - data: circ_data.clone(), - name: None, - metadata: None, - }; +pub fn circuit_draw(dag_circ: &DAGCircuit) { - let dag_circuit = circuit_to_dag(quantum_circuit_data, true, None, None) - .expect("Failed to convert circuit data to DAGCircuit"); + let dag_circuit = dag_circ.clone(); let mut output = String::new(); From f13fb3e6ed988dc87d997b81416131e50b7ec151 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Wed, 20 Aug 2025 18:49:58 +0530 Subject: [PATCH 10/70] boilerplate code for layer stripping --- crates/circuit/src/circuit_drawer.rs | 98 ++++++++++++++++++++++++---- 1 file changed, 84 insertions(+), 14 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 6ad4d9549b9c..91a05dd027d7 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -14,7 +14,8 @@ use std::fmt::Debug; use std::hash::{Hash, RandomState}; #[cfg(feature = "cache_pygates")] use std::sync::OnceLock; - +use std::thread::current; +use hashbrown::HashSet; use crate::bit::{ BitLocations, ClassicalRegister, PyBit, QuantumRegister, Register, ShareableClbit, ShareableQubit, @@ -127,6 +128,7 @@ impl qubit_wire { } } + pub struct circuit_rep { q_wires: Vec::, dag_circ: DAGCircuit @@ -188,23 +190,91 @@ impl circuit_rep { pub fn build_layers(&mut self) { let binding = self.dag_circ.clone(); let layer_iterator = binding.multigraph_layers(); - for (layer_index, layer) in layer_iterator.enumerate() { - //create a vector of packed operations using the vectors of - - let operations: Vec<&PackedInstruction> = layer - .into_iter() - .filter_map(|node_index| { - match &binding.dag()[node_index] { - NodeType::Operation(instruction) => Some(instruction), - _ => None, // Skip input/output nodes + + let mut final_layers:Vec> = Vec::new(); + + println!("Building layers for the circuit..."); + + for (i,layer) in layer_iterator.enumerate(){ + + println!("Processing layer {}", i); + + // NodeIndex is being pushed into each sublayer + let mut sublayers: Vec> = vec![Vec::new(); layer.len()]; + + for node_index in layer { + if let NodeType::Operation(instruction_to_insert) = &binding.dag()[node_index] { + for sublayer in sublayers.iter_mut() { + if sublayer.is_empty() { + sublayer.push(node_index); + } else { + let mut flag = false; + for &sub_node_index in sublayer.iter() { + if let NodeType::Operation(instruction) = &binding.dag()[sub_node_index]{ + let subnode_qubits = binding.qargs_interner().get(instruction.qubits); + let subnode_clbits = binding.cargs_interner().get(instruction.clbits); + let node_qubits = binding.qargs_interner().get(instruction_to_insert.qubits); + let node_clbits = binding.cargs_interner().get(instruction_to_insert.clbits); + + // index can be 0 as well so to unwrap_or with default 0 might not be the best idea + let subnode_min_qubit = subnode_qubits.iter().map(|q| q.0).min().unwrap_or(0); + let subnode_max_qubit = subnode_qubits.iter().map(|q| q.0).max().unwrap_or(0); + let subnode_min_clbit = subnode_clbits.iter().map(|c| c.0).min().unwrap_or(0); + let subnode_max_clbit = subnode_clbits.iter().map(|c| c.0).max().unwrap_or(0); + let node_min_qubit = node_qubits.iter().map(|q| q.0).min().unwrap_or(0); + let node_max_qubit = node_qubits.iter().map(|q| q.0).max().unwrap_or(0); + let node_min_clbit = node_clbits.iter().map(|c| c.0).min().unwrap_or(0); + let node_max_clbit = node_clbits.iter().map(|c| c.0).max().unwrap_or(0); + + if subnode_max_qubit > node_min_qubit || subnode_min_qubit < node_max_qubit { + flag = true; + println!("Conflict detected between subnode {:?} and node {:?}", sub_node_index, node_index); + println!("Subnode qubits: {:?}, Node qubits: {:?}", subnode_qubits, node_qubits); + println!("Subnode clbits: {:?}, Node clbits: {:?}", subnode_clbits, node_clbits); + println!("Subnode min/max qubits: {}, {}, Node min/max qubits: {}, {}", + subnode_min_qubit, subnode_max_qubit, node_min_qubit, node_max_qubit); + println!("Subnode min/max clbits: {}, {}, Node min/max clbits: {}, {}", + subnode_min_clbit, subnode_max_clbit, node_min_clbit, node_max_clbit); + break; + } + + if subnode_max_clbit > node_min_clbit || subnode_min_clbit < node_max_clbit { + flag = true; + println!("Conflict detected between subnode {:?} and node {:?}", sub_node_index, node_index); + println!("Subnode qubits: {:?}, Node qubits: {:?}", subnode_qubits, node_qubits); + println!("Subnode clbits: {:?}, Node clbits: {:?}", subnode_clbits, node_clbits); + println!("Subnode min/max qubits: {}, {}, Node min/max qubits: {}, {}", + subnode_min_qubit, subnode_max_qubit, node_min_qubit, node_max_qubit); + break; + } + } + } + if !flag { + sublayer.push(node_index); + break; + } + } } - }) - .collect(); + println!("sublayer analyzed: {:?}, instruction: {:?}", sublayers, instruction_to_insert); + } + } - self.build_layer(operations); + let mut ct = 0; + for j in sublayers { + if j.is_empty() { + continue; + } else { + final_layers.push(j); + ct += 1; + } + } + println!("Layer {} has {} sublayers", i, ct); + } - self.fix_len(q_wire); + for (i, layer) in final_layers.iter().enumerate() { + println!("Layer {}: {:?}", i, layer); } + } } From 49e530deb090561836093e27cf1fe4a8695eb063 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Mon, 25 Aug 2025 11:49:50 +0530 Subject: [PATCH 11/70] creating visualisation layers --- crates/circuit/src/circuit_drawer.rs | 29 +++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 91a05dd027d7..d557fd3d973d 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -200,7 +200,9 @@ impl circuit_rep { println!("Processing layer {}", i); // NodeIndex is being pushed into each sublayer - let mut sublayers: Vec> = vec![Vec::new(); layer.len()]; + let mut sublayers: Vec> = vec![Vec::new()]; + // let mut sublayers: Vec> = vec![Vec::new(); layer.len()]; + for node_index in layer { if let NodeType::Operation(instruction_to_insert) = &binding.dag()[node_index] { @@ -208,7 +210,7 @@ impl circuit_rep { if sublayer.is_empty() { sublayer.push(node_index); } else { - let mut flag = false; + let mut overlap = false; for &sub_node_index in sublayer.iter() { if let NodeType::Operation(instruction) = &binding.dag()[sub_node_index]{ let subnode_qubits = binding.qargs_interner().get(instruction.qubits); @@ -226,8 +228,13 @@ impl circuit_rep { let node_min_clbit = node_clbits.iter().map(|c| c.0).min().unwrap_or(0); let node_max_clbit = node_clbits.iter().map(|c| c.0).max().unwrap_or(0); - if subnode_max_qubit > node_min_qubit || subnode_min_qubit < node_max_qubit { - flag = true; + // the issue now is that when unwrap_or sets the default to 0 for all instructions without 0 + // there will always be overlap + // need to use option to minimise overlap + + if (subnode_min_qubit >= node_min_qubit && subnode_min_qubit <= node_max_qubit) || + (subnode_max_qubit >= node_min_qubit && subnode_max_qubit <= node_max_qubit){ + overlap = true; println!("Conflict detected between subnode {:?} and node {:?}", sub_node_index, node_index); println!("Subnode qubits: {:?}, Node qubits: {:?}", subnode_qubits, node_qubits); println!("Subnode clbits: {:?}, Node clbits: {:?}", subnode_clbits, node_clbits); @@ -238,20 +245,28 @@ impl circuit_rep { break; } - if subnode_max_clbit > node_min_clbit || subnode_min_clbit < node_max_clbit { - flag = true; + if (subnode_min_clbit >= node_min_clbit && subnode_min_clbit <= node_max_clbit) || + (subnode_max_clbit >= node_min_clbit && subnode_max_clbit <= node_max_clbit) { + overlap = true; println!("Conflict detected between subnode {:?} and node {:?}", sub_node_index, node_index); println!("Subnode qubits: {:?}, Node qubits: {:?}", subnode_qubits, node_qubits); println!("Subnode clbits: {:?}, Node clbits: {:?}", subnode_clbits, node_clbits); println!("Subnode min/max qubits: {}, {}, Node min/max qubits: {}, {}", subnode_min_qubit, subnode_max_qubit, node_min_qubit, node_max_qubit); + println!("Subnode min/max clbits: {}, {}, Node min/max clbits: {}, {}", + subnode_min_clbit, subnode_max_clbit, node_min_clbit, node_max_clbit); break; } } } - if !flag { + if !overlap { sublayer.push(node_index); break; + } else { + // If there is a conflict, create a new sublayer + let new_sublayer = vec![node_index]; + sublayers.push(new_sublayer); + break; } } } From feae1a2aa9d0662b75a62400f0623dbecb151c46 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Tue, 26 Aug 2025 14:10:24 +0530 Subject: [PATCH 12/70] quadratic complexity building of sublayers --- crates/circuit/src/circuit_drawer.rs | 124 +++++++++++++++++++-------- 1 file changed, 86 insertions(+), 38 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index d557fd3d973d..10790fd71657 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -193,6 +193,9 @@ impl circuit_rep { let mut final_layers:Vec> = Vec::new(); + let total_qubits = binding.num_qubits() as u32; + let total_clbits = binding.num_clbits() as u32; + println!("Building layers for the circuit..."); for (i,layer) in layer_iterator.enumerate(){ @@ -219,43 +222,81 @@ impl circuit_rep { let node_clbits = binding.cargs_interner().get(instruction_to_insert.clbits); // index can be 0 as well so to unwrap_or with default 0 might not be the best idea - let subnode_min_qubit = subnode_qubits.iter().map(|q| q.0).min().unwrap_or(0); - let subnode_max_qubit = subnode_qubits.iter().map(|q| q.0).max().unwrap_or(0); - let subnode_min_clbit = subnode_clbits.iter().map(|c| c.0).min().unwrap_or(0); - let subnode_max_clbit = subnode_clbits.iter().map(|c| c.0).max().unwrap_or(0); - let node_min_qubit = node_qubits.iter().map(|q| q.0).min().unwrap_or(0); - let node_max_qubit = node_qubits.iter().map(|q| q.0).max().unwrap_or(0); - let node_min_clbit = node_clbits.iter().map(|c| c.0).min().unwrap_or(0); - let node_max_clbit = node_clbits.iter().map(|c| c.0).max().unwrap_or(0); - - // the issue now is that when unwrap_or sets the default to 0 for all instructions without 0 - // there will always be overlap - // need to use option to minimise overlap - - if (subnode_min_qubit >= node_min_qubit && subnode_min_qubit <= node_max_qubit) || - (subnode_max_qubit >= node_min_qubit && subnode_max_qubit <= node_max_qubit){ - overlap = true; - println!("Conflict detected between subnode {:?} and node {:?}", sub_node_index, node_index); - println!("Subnode qubits: {:?}, Node qubits: {:?}", subnode_qubits, node_qubits); - println!("Subnode clbits: {:?}, Node clbits: {:?}", subnode_clbits, node_clbits); - println!("Subnode min/max qubits: {}, {}, Node min/max qubits: {}, {}", - subnode_min_qubit, subnode_max_qubit, node_min_qubit, node_max_qubit); - println!("Subnode min/max clbits: {}, {}, Node min/max clbits: {}, {}", - subnode_min_clbit, subnode_max_clbit, node_min_clbit, node_max_clbit); - break; - } - - if (subnode_min_clbit >= node_min_clbit && subnode_min_clbit <= node_max_clbit) || - (subnode_max_clbit >= node_min_clbit && subnode_max_clbit <= node_max_clbit) { + let subnode_min_qubit = subnode_qubits.iter().map(|q| q.0).min(); + let subnode_max_qubit = subnode_qubits.iter().map(|q| q.0).max(); + let subnode_min_clbit = subnode_clbits.iter().map(|c| c.0).min(); + let subnode_max_clbit = subnode_clbits.iter().map(|c| c.0).max(); + let node_min_qubit = node_qubits.iter().map(|q| q.0).min(); + let node_max_qubit = node_qubits.iter().map(|q| q.0).max(); + let node_min_clbit = node_clbits.iter().map(|c| c.0).min(); + let node_max_clbit = node_clbits.iter().map(|c| c.0).max(); + + let node_min = match node_min_qubit { + Some(val) => val, + None => match node_max_qubit { + Some(cval) => cval, + None => match node_min_clbit { + Some(cval) => cval + total_qubits, + None => match node_max_clbit { + Some(qval) => qval + total_qubits, + None => continue, // No qubits or clbits, skip this node + }, + }, // No qubits or clbits, skip this node + }, + }; + + let node_max = match node_max_clbit { + Some(val) => val + total_qubits, + None => match node_min_clbit { + Some(cval) => cval + total_qubits, + None => match node_max_qubit { + Some(cval) => cval, + None => match node_min_qubit { + Some(qval) => qval, + None => continue, // No qubits or clbits, skip this node + }, + }, // No qubits or clbits, skip this node + }, + }; + + let subnode_min = match subnode_min_qubit { + Some(val) => val, + None => match subnode_max_qubit { + Some(cval) => cval, + None => match subnode_min_clbit { + Some(cval) => cval + total_qubits, + None => match subnode_max_clbit { + Some(qval) => qval + total_qubits, + None => continue, // No qubits or clbits, skip this node + }, + }, // No qubits or clbits, skip this node + }, + }; + + let subnode_max = match subnode_max_clbit { + Some(val) => val + total_qubits, + None => match subnode_min_clbit { + Some(cval) => cval + total_qubits, + None => match subnode_max_qubit { + Some(cval) => cval, + None => match subnode_min_qubit { + Some(qval) => qval, + None => continue, // No qubits or clbits, skip this node + }, + }, // No qubits or clbits, skip this node + }, + }; + + if (subnode_min <= node_min && subnode_max >= node_min) || (subnode_min <= node_max && subnode_max >= node_max) { overlap = true; - println!("Conflict detected between subnode {:?} and node {:?}", sub_node_index, node_index); - println!("Subnode qubits: {:?}, Node qubits: {:?}", subnode_qubits, node_qubits); - println!("Subnode clbits: {:?}, Node clbits: {:?}", subnode_clbits, node_clbits); - println!("Subnode min/max qubits: {}, {}, Node min/max qubits: {}, {}", - subnode_min_qubit, subnode_max_qubit, node_min_qubit, node_max_qubit); - println!("Subnode min/max clbits: {}, {}, Node min/max clbits: {}, {}", - subnode_min_clbit, subnode_max_clbit, node_min_clbit, node_max_clbit); - break; + // println!("Conflict detected between subnode {:?} and node {:?}", sub_node_index, node_index); + // println!("Subnode qubits: {:?}, Node qubits: {:?}", subnode_qubits, node_qubits); + // println!("Subnode clbits: {:?}, Node clbits: {:?}", subnode_clbits, node_clbits); + // println!("Subnode min/max qubits: {:?}, {:?}, Node min/max qubits: {:?}, {:?}", + // subnode_min_qubit, subnode_max_qubit, node_min_qubit, node_max_qubit); + // println!("Subnode min/max clbits: {:?}, {:?}, Node min/max clbits: {:?}, {:?}", + // subnode_min_clbit, subnode_max_clbit, node_min_clbit, node_max_clbit); + // break; } } } @@ -270,7 +311,7 @@ impl circuit_rep { } } } - println!("sublayer analyzed: {:?}, instruction: {:?}", sublayers, instruction_to_insert); + // println!("sublayer analyzed: {:?}, instruction: {:?}", sublayers, instruction_to_insert); } } @@ -287,7 +328,14 @@ impl circuit_rep { } for (i, layer) in final_layers.iter().enumerate() { - println!("Layer {}: {:?}", i, layer); + for nodeind in layer { + if let NodeType::Operation(instruction) = &binding.dag()[*nodeind] { + let qubits = binding.qargs_interner().get(instruction.qubits); + let clbits = binding.cargs_interner().get(instruction.clbits); + println!("Layer {}: Instruction: {} Qubits: {:?} Clbits: {:?}", i, instruction.op.name(), qubits, clbits); + } + } + println!("-----------------------------------"); } } From d0bc7dd42ecb4dae3a6af6e9240bf4f6842b80d5 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Tue, 26 Aug 2025 14:36:45 +0530 Subject: [PATCH 13/70] few minor fixes --- crates/circuit/src/circuit_drawer.rs | 106 ++++----------------------- 1 file changed, 13 insertions(+), 93 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 10790fd71657..3e807bb4eda6 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -49,16 +49,20 @@ use crate::circuit_data::CircuitData; import_exception!(qiskit.circuit.exceptions, CircuitError); -#[pyfunction] -#[pyo3(name = "draw")] -pub fn py_drawer(py: Python, dag_circ: &Bound) -> PyResult<()> { - let dag_circ = dag_circ.extract::()?; - println!("function is being called from circuit_drawer.rs FILE"); - circuit_draw(&dag_circ); +#[pyfunction(name = "draw")] +pub fn py_drawer(py: Python, quantum_circuit: &Bound) -> PyResult<()> { + if !quantum_circuit.is_instance(QUANTUM_CIRCUIT.get_bound(py))? { + return Err(PyTypeError::new_err( + "Expected a QuantumCircuit instance" + )); + } + println!("FUNCTION IS BEING CALLED FROM circuit_drawer.rs FILE"); + let circ_data: QuantumCircuitData = quantum_circuit.extract()?; + let dag_circuit = circuit_to_dag(circ_data, true, None, None)?; + circuit_draw(&dag_circuit); Ok(()) } - pub const q_wire: &str = "─"; pub const c_wire: char = '═'; pub const top_con: char = '┴'; @@ -341,100 +345,16 @@ impl circuit_rep { } } -pub fn circuit_draw(dag_circ: &DAGCircuit) { - let dag_circuit = dag_circ.clone(); +pub fn circuit_draw(dag_circ: &DAGCircuit) { let mut output = String::new(); // Create a circuit representation - let mut circuit_rep = circuit_rep::new(dag_circuit.clone()); + let mut circuit_rep = circuit_rep::new(dag_circ.clone()); circuit_rep.set_qubit_name(); output.push_str(&circuit_rep.circuit_string()); circuit_rep.build_layers(); // Print the circuit representation println!("{}", output); - - // output.push_str("DAG Circuit Operations:\n"); - // output.push_str(&format!("Number of qubits: {}\n", dag_circuit.num_qubits())); - // output.push_str(&format!("Number of operations: {}\n", dag_circuit.num_ops())); - // output.push_str("Operations:\n"); - - // // creating representation where each wire is represented by 3 strings - // let mut circuit_rep: Vec = vec![String::new(); (dag_circuit.num_qubits() + 1) * 3]; - - // // Fill the first column with qubit labels - // for (i, qubit) in dag_circuit.qubits().objects().iter().enumerate() { - // let qubit_index = i * 3 + 1; - // let qubit_name = format!("q_{}: ", i); - // circuit_rep[qubit_index].push_str(&qubit_name); - // circuit_rep[qubit_index - 1].push_str(" ".repeat((&qubit_name).len()).as_str()); - // circuit_rep[qubit_index + 1].push_str(" ".repeat((&qubit_name).len()).as_str()); - // } - - // // Print the circuit representation - // for i in circuit_rep { - // println!("{}", i); - // } - // //getting qubits and clbit information - // for (index, qubit) in dag_circuit.qubits().objects().iter().enumerate() { - // println!("Qubit {}: {:?}", index, qubit); - // }; - - // // Iterate through clbits with their indices - // for (index, clbit) in dag_circuit.clbits().objects().iter().enumerate() { - // println!("Clbit {}: {:?}", index, clbit); - // }; - - // let layer_iterator = dag_circuit.multigraph_layers(); - // for (layer_index, layer) in layer_iterator.enumerate() { - // output.push_str(&format!("Layer {}:\n", layer_index)); - - // // Filter for operation nodes only - // let operations: Vec<_> = layer - // .into_iter() - // .filter_map(|node_index| { - // match &dag_circuit.dag()[node_index] { - // NodeType::Operation(instruction) => Some((node_index, instruction)), - // _ => None, // Skip input/output nodes - // } - // }) - // .collect(); - - // if operations.is_empty() { - // return; - // } - - // for (node_index, instruction) in operations { - // let standard_gate = StandardGate::try_from(&instruction.op).unwrap(); - // let op_name = instruction.op.name(); - // let qubits = self.dag_circ.qargs_interner().get(instruction.qubits); - // let clbits = self.dag_circ.cargs_interner().get(instruction.clbits); - - // let qubit_str = qubits.iter() - // .map(|q| q.0.to_string()) - // .collect::>() - // .join(","); - - // let clbit_str = clbits.iter() - // .map(|c| c.0.to_string()) - // .collect::>() - // .join(","); - - // // Here, you might want to store or process the operation info as needed. - // // For now, just print or log as a placeholder. - // println!( - // " Node {}: {} qubits=[{}] clbits=[{}]", - // node_index.index(), op_name, qubit_str, clbit_str - // ); - - // // Print parameters if any - // let params = instruction.params_view(); - // if !params.is_empty() { - // println!(" params: {:?}", params); - // } - // } - - // println!("{}",output); - // } } From f667fdd4a100aa1a9ee9ce453f5cc7292836b290 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Thu, 28 Aug 2025 11:12:59 +0530 Subject: [PATCH 14/70] get_layers implementation completed --- crates/circuit/src/circuit_drawer.rs | 196 ++++++++++++--------------- 1 file changed, 87 insertions(+), 109 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 3e807bb4eda6..613f4617b492 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -56,7 +56,6 @@ pub fn py_drawer(py: Python, quantum_circuit: &Bound) -> PyResult<()> { "Expected a QuantumCircuit instance" )); } - println!("FUNCTION IS BEING CALLED FROM circuit_drawer.rs FILE"); let circ_data: QuantumCircuitData = quantum_circuit.extract()?; let dag_circuit = circuit_to_dag(circ_data, true, None, None)?; circuit_draw(&dag_circuit); @@ -200,125 +199,105 @@ impl circuit_rep { let total_qubits = binding.num_qubits() as u32; let total_clbits = binding.num_clbits() as u32; - println!("Building layers for the circuit..."); - for (i,layer) in layer_iterator.enumerate(){ - - println!("Processing layer {}", i); - - // NodeIndex is being pushed into each sublayer let mut sublayers: Vec> = vec![Vec::new()]; - // let mut sublayers: Vec> = vec![Vec::new(); layer.len()]; - for node_index in layer { - if let NodeType::Operation(instruction_to_insert) = &binding.dag()[node_index] { - for sublayer in sublayers.iter_mut() { - if sublayer.is_empty() { - sublayer.push(node_index); - } else { - let mut overlap = false; - for &sub_node_index in sublayer.iter() { - if let NodeType::Operation(instruction) = &binding.dag()[sub_node_index]{ - let subnode_qubits = binding.qargs_interner().get(instruction.qubits); - let subnode_clbits = binding.cargs_interner().get(instruction.clbits); - let node_qubits = binding.qargs_interner().get(instruction_to_insert.qubits); - let node_clbits = binding.cargs_interner().get(instruction_to_insert.clbits); - - // index can be 0 as well so to unwrap_or with default 0 might not be the best idea - let subnode_min_qubit = subnode_qubits.iter().map(|q| q.0).min(); - let subnode_max_qubit = subnode_qubits.iter().map(|q| q.0).max(); - let subnode_min_clbit = subnode_clbits.iter().map(|c| c.0).min(); - let subnode_max_clbit = subnode_clbits.iter().map(|c| c.0).max(); - let node_min_qubit = node_qubits.iter().map(|q| q.0).min(); - let node_max_qubit = node_qubits.iter().map(|q| q.0).max(); - let node_min_clbit = node_clbits.iter().map(|c| c.0).min(); - let node_max_clbit = node_clbits.iter().map(|c| c.0).max(); - - let node_min = match node_min_qubit { - Some(val) => val, - None => match node_max_qubit { - Some(cval) => cval, - None => match node_min_clbit { - Some(cval) => cval + total_qubits, - None => match node_max_clbit { - Some(qval) => qval + total_qubits, - None => continue, // No qubits or clbits, skip this node - }, - }, // No qubits or clbits, skip this node - }, - }; - - let node_max = match node_max_clbit { + + if let NodeType::Operation(instruction_to_insert) = &binding.dag()[node_index]{ + + if sublayers.is_empty() { + sublayers.push(vec![node_index]); + continue; + } + + let node_qubits = binding.qargs_interner().get(instruction_to_insert.qubits); + let node_clbits = binding.cargs_interner().get(instruction_to_insert.clbits); + + let node_min_qubit = node_qubits.iter().map(|q| q.0).min(); + let node_max_qubit = node_qubits.iter().map(|q| q.0).max(); + let node_min_clbit = node_clbits.iter().map(|c| c.0).min(); + let node_max_clbit = node_clbits.iter().map(|c| c.0).max(); + + let node_min = match node_min_qubit { + Some(val) => val, + None => match node_max_qubit { + Some(val) => val, + None => match node_min_clbit { + Some(val) => val + total_qubits, + None => match node_max_clbit { + Some(val) => val + total_qubits, + None => continue, // No qubits or clbits, skip this node + }, + }, // No qubits or clbits, skip this node + }, + }; + + let node_max = match node_max_clbit { + Some(val) => val + total_qubits, + None => match node_min_clbit { + Some(val) => val + total_qubits, + None => match node_max_qubit { + Some(val) => val, + None => match node_min_qubit { + Some(val) => val, + None => continue, // No qubits or clbits, skip this node + }, + }, // No qubits or clbits, skip this node + }, + }; + + let mut sublayer = sublayers.last_mut().unwrap(); + let mut overlap = false; + for &subnode in sublayer.iter() { + if let NodeType::Operation(instruction) = &binding.dag()[subnode]{ + let subnode_qubits = binding.qargs_interner().get(instruction.qubits); + let subnode_clbits = binding.cargs_interner().get(instruction.clbits); + let subnode_min_qubit = subnode_qubits.iter().map(|q| q.0).min(); + let subnode_max_qubit = subnode_qubits.iter().map(|q| q.0).max(); + let subnode_min_clbit = subnode_clbits.iter().map(|c| c.0).min(); + let subnode_max_clbit = subnode_clbits.iter().map(|c| c.0).max(); + let subnode_min = match subnode_min_qubit { + Some(val) => val, + None => match subnode_max_qubit { + Some(val) => val, + None => match subnode_min_clbit { Some(val) => val + total_qubits, - None => match node_min_clbit { - Some(cval) => cval + total_qubits, - None => match node_max_qubit { - Some(cval) => cval, - None => match node_min_qubit { - Some(qval) => qval, - None => continue, // No qubits or clbits, skip this node - }, - }, // No qubits or clbits, skip this node - }, - }; - - let subnode_min = match subnode_min_qubit { + None => match subnode_max_clbit { + Some(val) => val + total_qubits, + None => continue, // No qubits or clbits, skip this node + }, + }, // No qubits or clbits, skip this node + }, + }; + let subnode_max = match subnode_max_clbit { + Some(val) => val + total_qubits, + None => match subnode_min_clbit { + Some(val) => val + total_qubits, + None => match subnode_max_qubit { Some(val) => val, - None => match subnode_max_qubit { - Some(cval) => cval, - None => match subnode_min_clbit { - Some(cval) => cval + total_qubits, - None => match subnode_max_clbit { - Some(qval) => qval + total_qubits, - None => continue, // No qubits or clbits, skip this node - }, - }, // No qubits or clbits, skip this node - }, - }; - - let subnode_max = match subnode_max_clbit { - Some(val) => val + total_qubits, - None => match subnode_min_clbit { - Some(cval) => cval + total_qubits, - None => match subnode_max_qubit { - Some(cval) => cval, - None => match subnode_min_qubit { - Some(qval) => qval, - None => continue, // No qubits or clbits, skip this node - }, - }, // No qubits or clbits, skip this node - }, - }; - - if (subnode_min <= node_min && subnode_max >= node_min) || (subnode_min <= node_max && subnode_max >= node_max) { - overlap = true; - // println!("Conflict detected between subnode {:?} and node {:?}", sub_node_index, node_index); - // println!("Subnode qubits: {:?}, Node qubits: {:?}", subnode_qubits, node_qubits); - // println!("Subnode clbits: {:?}, Node clbits: {:?}", subnode_clbits, node_clbits); - // println!("Subnode min/max qubits: {:?}, {:?}, Node min/max qubits: {:?}, {:?}", - // subnode_min_qubit, subnode_max_qubit, node_min_qubit, node_max_qubit); - // println!("Subnode min/max clbits: {:?}, {:?}, Node min/max clbits: {:?}, {:?}", - // subnode_min_clbit, subnode_max_clbit, node_min_clbit, node_max_clbit); - // break; - } - } - } - if !overlap { - sublayer.push(node_index); - break; - } else { - // If there is a conflict, create a new sublayer - let new_sublayer = vec![node_index]; - sublayers.push(new_sublayer); + None => match subnode_min_qubit { + Some(val) => val, + None => continue, // No qubits or clbits, skip this node + }, + }, // No qubits or clbits, skip this node + }, + }; + if (subnode_min <= node_min && subnode_max >= node_min) || (subnode_min <= node_max && subnode_max >= node_max) { + overlap = true; break; } } } - // println!("sublayer analyzed: {:?}, instruction: {:?}", sublayers, instruction_to_insert); + + if overlap { + sublayers.push(vec![node_index]); + } else { + sublayer.push(node_index); + } } } - + let mut ct = 0; for j in sublayers { if j.is_empty() { @@ -328,7 +307,6 @@ impl circuit_rep { ct += 1; } } - println!("Layer {} has {} sublayers", i, ct); } for (i, layer) in final_layers.iter().enumerate() { From 73c606a2d8be264ec786fecfe39244d600af4f5b Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Mon, 8 Sep 2025 16:00:38 +0530 Subject: [PATCH 15/70] datastruct for drawing rep --- crates/circuit/src/circuit_drawer.rs | 219 ++++++++++++++++++++++++--- 1 file changed, 195 insertions(+), 24 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 613f4617b492..ed42c037819e 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -28,7 +28,7 @@ use crate::imports::{ANNOTATED_OPERATION, QUANTUM_CIRCUIT}; use rustworkx_core::petgraph::stable_graph::{EdgeReference, NodeIndex}; use crate::interner::{Interned, Interner}; use crate::object_registry::ObjectRegistry; -use crate::operations::{Operation, OperationRef, Param, PythonOperation, StandardGate}; +use crate::operations::{Operation, OperationRef, Param, PythonOperation, StandardGate, StandardInstruction}; use crate::packed_instruction::{PackedInstruction, PackedOperation}; use crate::parameter_table::{ParameterTable, ParameterTableError, ParameterUse, ParameterUuid}; use crate::register_data::RegisterData; @@ -73,21 +73,48 @@ pub const top_right_con: char = '┐'; pub const bot_left_con: char = '└'; pub const bot_right_con: char = '┘'; -#[derive(Clone)] -pub struct qubit_wire { - pub top: String, - pub mid: String, - pub bot: String, - pub wire_len: u64 +#[derive(Clone, Debug, PartialEq, Eq)] +enum wire_type { + qubit, + clbit, } -impl qubit_wire { - pub fn new() -> Self { - qubit_wire { +impl From<&wire_type> for &str { + fn from(wire_type: &wire_type) -> Self { + match wire_type { + wire_type::qubit => "─", + wire_type::clbit => "═", + } + } +} + +#[derive(Clone, Debug)] +enum control_type{ + open, + closed, +} + +#[derive(Clone, Debug)] +pub struct input{ + +} +#[derive(Clone, Debug)] +pub struct wire { + top: String, + mid: String, + bot: String, + wire_len: u64, + type_: wire_type, +} + +impl wire { + pub fn new(type_: wire_type) -> Self { + wire { top: String::new(), mid: String::new(), bot: String::new(), - wire_len: 0 + wire_len: 0, + type_: type_, } } @@ -101,7 +128,6 @@ impl qubit_wire { panic!("The lengths of the wire components are not equal"); } } - // setting qubit names pub fn qubit_name(&mut self, qubit_name: &str) { let name_len = qubit_name.len(); @@ -123,17 +149,53 @@ impl qubit_wire { wire_rep } - pub fn fix_len(&mut self, num: u64, chr: &str) { + pub fn fix_len(&mut self, num: u64) { self.wire_len = self.wire_len + num; self.top.push_str(" ".repeat(num as usize).as_str()); - self.mid.push_str(chr.repeat(num as usize).as_str()); + self.mid.push_str(<&str>::from(&self.type_).repeat(num as usize).as_str()); self.bot.push_str(" ".repeat(num as usize).as_str()); } } +#[derive(Clone, Debug)] +pub struct enclosed{ + qubits: Vec, + clbits: Vec, + control: Vec, + control_types: Vec, + wire: wire_type, + name: String, + label: Option, +} + +impl enclosed { + fn from_instruction(instruction: &PackedInstruction, dag_circ: &DAGCircuit) -> Self { + let instruction_qubits = dag_circ.qargs_interner().get(instruction.qubits).iter().map(|q| q.0).collect(); + let instruction_clbits = dag_circ.cargs_interner().get(instruction.clbits).iter().map(|c| c.0).collect(); + let instruction_name = instruction.op.name(); + let instruction_label = instruction.label(); + // handle case for when the control state is different + + println!("name: {}", instruction_name); + println!("label: {:?}", instruction_label); + println!("qubits: {:?}", instruction_qubits); + println!("clbits: {:?}", instruction_clbits); + + enclosed { + qubits: instruction_qubits, + clbits: instruction_clbits, + control: vec![], + control_types: vec![], + wire: wire_type::qubit, + name: instruction_name.to_string(), + label: instruction_label.map(|s| s.to_string()), + } + + } +} pub struct circuit_rep { - q_wires: Vec::, + q_wires: Vec::, dag_circ: DAGCircuit } @@ -144,7 +206,7 @@ impl circuit_rep { let qubit = dag_circ.num_qubits(); circuit_rep { - q_wires: vec!(qubit_wire::new(); qubit as usize), + q_wires: vec!(wire::new(wire_type::qubit); qubit as usize), dag_circ: dag_circ } } @@ -166,7 +228,7 @@ impl circuit_rep { } for wire in self.q_wires.iter_mut() { - wire.fix_len(num - wire.wire_len, chr); + wire.fix_len(num - wire.wire_len); } } @@ -187,7 +249,113 @@ impl circuit_rep { } pub fn build_layer(&mut self, layer: Vec<&PackedInstruction>){ - println!("{:?}",layer); + for instruction in layer{ + let enclosed_inst = enclosed::from_instruction(instruction, &self.dag_circ); + // println!("{:?}", enclosed_inst); + // if instruction.op.control_flow() { + // panic!("Control flow operations not supported yet: {}", instruction.op.name()); + // } + + // // Check for standard instructions (barrier, measure, reset, delay) + // if let Some(standard_instruction) = instruction.op.try_standard_instruction() { + // match standard_instruction { + // StandardInstruction::Measure => { + // // This is a measurement operation + // // Handle: MeasureFrom element on qubit, MeasureTo element on clbit + // // Access qubits: instruction.qubits (interned slice) + // // Access clbits: instruction.clbits (interned slice) + // // Create MeasureFrom element for the qubit + // // Create MeasureTo element for the clbit + // // Handle classical register bundling if needed + // } + // StandardInstruction::Barrier(num_qubits) => { + // // This is a barrier directive + // // Handle: Barrier elements across specified qubits + + // //get label of barrier + + // // Number of qubits affected: num_qubits + // // Access affected qubits: instruction.qubits + // // Check if barriers should be plotted with plotbarriers flag + // // Place barrier symbol on each affected qubit wire + // // Add label on topmost qubit if present: instruction.label() + // let mut temp:bool = false; + // for wire in self.q_wires.iter_mut() { + // if wire.type_ == wire_type::qubit { + // wire.top.push_str("░"); + // wire.mid.push_str("░"); + // wire.bot.push_str("░"); + // } + // } + // } + // StandardInstruction::Reset => { + // // PANIC - Reset not supported yet + // panic!("Reset operations not supported yet"); + // } + // StandardInstruction::Delay(_unit) => { + // // PANIC - Delay not supported yet + // panic!("Delay operations not supported yet"); + // } + // } + // } + + // // Check for standard gates + // else if let Some(standard_gate) = instruction.op.try_standard_gate() { + // // This is a standard gate operation + // let num_qubits = instruction.op.num_qubits(); + + // if num_qubits == 1 { + // // Single qubit standard gate + // // Handle: BoxOnQuWire element + // // Gate name: instruction.op.name() + // // Parameters: instruction.params_view() - format for display + // // Label: instruction.label() + // // Check for conditional operation (if condition exists) + // // Place single qubit box element on the target wire + // } else { + // // Multi-qubit standard gate + // // Access target qubits: instruction.qubits + // // Gate name: instruction.op.name() + // // Parameters: instruction.params_view() + // // Label: instruction.label() + + // // Special handling for specific multi-qubit gates: + // match standard_gate { + // // SwapGate: Create Ex elements with connections + // // RZZGate: Create Bullet elements with ZZ connection label + // // CXGate, CZGate, etc: Handle as controlled gates + // // Other multi-qubit gates: Use multi-qubit box spanning all qubits + // _ => { + // // Generic multi-qubit gate handling + // // Create box elements spanning all target qubits + // // Handle top/middle/bottom box parts for multi-qubit span + // } + // } + // } + // } + + // // Everything else - PANIC for now + // else { + // match instruction.op.view() { + // OperationRef::Gate(_) => { + // panic!("Python gates not supported yet: {}", instruction.op.name()); + // } + // OperationRef::Instruction(_) => { + // panic!("Python instructions not supported yet: {}", instruction.op.name()); + // } + // OperationRef::Operation(_) => { + // panic!("Python operations not supported yet: {}", instruction.op.name()); + // } + // OperationRef::Unitary(_) => { + // panic!("Unitary gates not supported yet: {}", instruction.op.name()); + // } + // _ => { + // panic!("Unknown operation type: {}", instruction.op.name()); + // } + // } + // } + // } + } } pub fn build_layers(&mut self) { @@ -309,15 +477,18 @@ impl circuit_rep { } } - for (i, layer) in final_layers.iter().enumerate() { + + let mut packedin_layers: Vec> = Vec::new(); + + for (id,layer) in final_layers.iter().enumerate() { + let mut packedin_layer: Vec<&PackedInstruction> = Vec::new(); for nodeind in layer { if let NodeType::Operation(instruction) = &binding.dag()[*nodeind] { - let qubits = binding.qargs_interner().get(instruction.qubits); - let clbits = binding.cargs_interner().get(instruction.clbits); - println!("Layer {}: Instruction: {} Qubits: {:?} Clbits: {:?}", i, instruction.op.name(), qubits, clbits); - } + packedin_layer.push(instruction); + } } - println!("-----------------------------------"); + println!("Layer {}", id); + self.build_layer(packedin_layer.clone()); } } From c9b9deaa2299fa4297adea720ff151a8c42902fa Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Mon, 15 Sep 2025 17:04:24 +0530 Subject: [PATCH 16/70] barrier implementation added --- crates/circuit/src/circuit_drawer.rs | 729 +++++++++++++++++++++------ 1 file changed, 568 insertions(+), 161 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index ed42c037819e..13d5e30d2ed3 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -10,12 +10,14 @@ // copyright notice, and modified files need to carry a notice indicating // that they have been altered from the originals. -use std::fmt::Debug; +use std::fmt::{format, Debug}; use std::hash::{Hash, RandomState}; +use std::sync::Barrier; #[cfg(feature = "cache_pygates")] use std::sync::OnceLock; use std::thread::current; use hashbrown::HashSet; +use hashbrown::HashMap; use crate::bit::{ BitLocations, ClassicalRegister, PyBit, QuantumRegister, Register, ShareableClbit, ShareableQubit, @@ -72,18 +74,19 @@ pub const top_left_con: char = '┌'; pub const top_right_con: char = '┐'; pub const bot_left_con: char = '└'; pub const bot_right_con: char = '┘'; +pub const barrier_char: char = '░'; #[derive(Clone, Debug, PartialEq, Eq)] -enum wire_type { +enum Wire_Type { qubit, clbit, } -impl From<&wire_type> for &str { - fn from(wire_type: &wire_type) -> Self { - match wire_type { - wire_type::qubit => "─", - wire_type::clbit => "═", +impl From<&Wire_Type> for &str { + fn from(Wire_Type: &Wire_Type) -> Self { + match Wire_Type { + Wire_Type::qubit => "─", + Wire_Type::clbit => "═", } } } @@ -103,29 +106,41 @@ pub struct wire { top: String, mid: String, bot: String, - wire_len: u64, - type_: wire_type, + type_: Wire_Type, } impl wire { - pub fn new(type_: wire_type) -> Self { + pub fn new(type_: Wire_Type) -> Self { wire { top: String::new(), mid: String::new(), bot: String::new(), - wire_len: 0, type_: type_, } } + pub fn get_len(&mut self) -> usize { + let top_len = self.top.len(); + let mid_len = self.mid.len(); + let bot_len = self.bot.len(); + if top_len == mid_len && mid_len == bot_len { + return mid_len; + } else { + let max_len = top_len.max(mid_len).max(bot_len); + self.fix_len(max_len); + } + self.mid.len() + } + pub fn update_wire_len(&mut self) { let top_len = self.top.len(); let mid_len = self.mid.len(); let bot_len = self.bot.len(); if top_len == mid_len && mid_len == bot_len { - self.wire_len = top_len as u64; + } else { - panic!("The lengths of the wire components are not equal"); + let max_len = top_len.max(mid_len).max(bot_len); + self.fix_len(max_len); } } // setting qubit names @@ -149,64 +164,549 @@ impl wire { wire_rep } - pub fn fix_len(&mut self, num: u64) { - self.wire_len = self.wire_len + num; - self.top.push_str(" ".repeat(num as usize).as_str()); - self.mid.push_str(<&str>::from(&self.type_).repeat(num as usize).as_str()); - self.bot.push_str(" ".repeat(num as usize).as_str()); + pub fn fix_len(&mut self, num: usize) { + self.top.push_str(" ".repeat(num).as_str()); + self.mid.push_str(<&str>::from(&self.type_).repeat(num).as_str()); + self.bot.push_str(" ".repeat(num).as_str()); + } + + pub fn add_wire_component(&mut self, component: &wire) { + &self.top.push_str(&component.top); + &self.mid.push_str(&component.mid); + &self.bot.push_str(&component.bot); + &self.update_wire_len(); } } -#[derive(Clone, Debug)] -pub struct enclosed{ - qubits: Vec, - clbits: Vec, - control: Vec, - control_types: Vec, - wire: wire_type, + +pub trait DrawElement{ + + fn get_width(&self) -> u64{ + 1 + } + + fn component_dict(&self) -> HashMap{ + HashMap::::new() + } + +} + +pub trait DirectOnWire{ + +} + +#[derive(Clone, Debug, PartialEq)] +enum VisualType{ + Boxed, + BetweenWire(String), + DirectOnWire(String), + WholeWire, +} + +pub struct Enclosed<'a>{ + packedinst: &'a PackedInstruction, + dag_circ: &'a DAGCircuit, + wire: Wire_Type, name: String, label: Option, + visual_type: VisualType, } -impl enclosed { - fn from_instruction(instruction: &PackedInstruction, dag_circ: &DAGCircuit) -> Self { - let instruction_qubits = dag_circ.qargs_interner().get(instruction.qubits).iter().map(|q| q.0).collect(); - let instruction_clbits = dag_circ.cargs_interner().get(instruction.clbits).iter().map(|c| c.0).collect(); - let instruction_name = instruction.op.name(); - let instruction_label = instruction.label(); - // handle case for when the control state is different - - println!("name: {}", instruction_name); - println!("label: {:?}", instruction_label); - println!("qubits: {:?}", instruction_qubits); - println!("clbits: {:?}", instruction_clbits); - - enclosed { - qubits: instruction_qubits, - clbits: instruction_clbits, - control: vec![], - control_types: vec![], - wire: wire_type::qubit, - name: instruction_name.to_string(), - label: instruction_label.map(|s| s.to_string()), +impl<'a> Enclosed<'a> { + pub fn new(packedinst: &'a PackedInstruction, dag_circ: &'a DAGCircuit ,wire: Wire_Type, name: String, label: Option, visual_type: VisualType) -> Enclosed<'a>{ + Enclosed { + packedinst, + dag_circ, + wire, + name, + label, + visual_type, } + } + + pub fn get_wire_indices(&self) -> Vec{ + let mut qubit_indices: Vec = self.dag_circ.qargs_interner().get(self.packedinst.qubits) + .iter() + .map(|qubit| qubit.0) + .collect(); + + let total_qubits:u32 = self.dag_circ.num_qubits() as u32; + + let clbit_indices: Vec = self.dag_circ.cargs_interner().get(self.packedinst.clbits) + .iter() + .map(|clbit| clbit.0 + total_qubits) + .collect(); + qubit_indices.extend(clbit_indices.into_iter()); + + qubit_indices + } + + pub fn wire_component(&self, ind: u32) -> wire{ + let mut component = wire::new(self.wire.clone()); + let wire_indices = self.get_wire_indices(); + let min_index = *wire_indices.iter().min().unwrap(); + let max_index = *wire_indices.iter().max().unwrap(); + + // barrier handling + if self.visual_type == VisualType::DirectOnWire(barrier_char.to_string()) { + let mut label = barrier_char.to_string(); + if let Some(l) = &self.label{ + label = l.to_string(); + } else { + label = barrier_char.to_string(); + } + let label_len:usize = label.len(); + + let mut left_pad_len = 0; + let mut right_pad_len = 0; + if label_len == 0 { + left_pad_len = 0; + right_pad_len = 0; + } + else if label_len % 2 == 0{ + left_pad_len = (label_len / 2) - 1; + right_pad_len = label_len / 2; + } + else { + left_pad_len = label_len / 2; + right_pad_len = label_len / 2; + } + let component_wire = format!("{}{}{}", + <&str>::from(&self.wire).repeat(left_pad_len), + barrier_char, + <&str>::from(&self.wire).repeat(right_pad_len) + ); + + let between_wire = format!("{}{}{}", + " ".repeat(left_pad_len), + barrier_char, + " ".repeat(right_pad_len) + ); + + if ind == 0 { + component.top.push_str(&label[..]); + } else { + component.top.push_str(&component_wire); + } + component.mid.push_str(&between_wire); + component.bot.push_str( &between_wire); + return component; + } + + match &self.visual_type { + VisualType::Boxed => { + if let Some(label) = &self.label { + let label_len = label.len() as usize; + component.top.push_str(&format!("{}{}{}", top_left_con, "─".repeat(label_len as usize), top_right_con)); + component.mid.push_str(&format!("{}{}{}", left_con, label, right_con)); + component.bot.push_str(&format!("{}{}{}", bot_left_con, "─".repeat(label_len as usize), bot_right_con)); + } else { + panic!("Boxed visual has no label"); + } + } + VisualType::BetweenWire(label) => { + let label_len = label.len() as u64; + component.top.push_str(&format!("{}{}{}", top_con, "─".repeat(label_len as usize), top_con)); + component.mid.push_str(&format!(" {} ", label)); + component.bot.push_str(&format!("{}{}{}", bot_con, "─".repeat(label_len as usize), bot_con)); + } + VisualType::DirectOnWire(label) => { + component.top.push_str(" ".repeat(label.len()).as_str()); + component.mid.push_str(label); + component.bot.push_str(" ".repeat(label.len()).as_str()); + } + VisualType::WholeWire => { + let wire_len = 1; // single character for whole wire + component.top.push_str("░".repeat(wire_len).as_str()); + component.mid.push_str("░".repeat(wire_len).as_str()); + component.bot.push_str("░. ".repeat(wire_len).as_str()); + } + } + component.update_wire_len(); + component } } +impl<'a> DrawElement for Enclosed<'a>{ + fn get_width(&self) -> u64 { + let type_ = match &self.visual_type { + VisualType::Boxed => { + if let Some(label) = &self.label { + label.len() as u64 + 2 // for the box edges + } else { + panic!("Boxed visual has no label"); + } + } + VisualType::BetweenWire(label) => { + label.len() as u64 + 1 // for the spaces around the label + } + VisualType::DirectOnWire(label) => { + 1 as u64// no extra space needed + } + VisualType::WholeWire => { + 0 as u64// just a single character for the whole wire + } + }; + + return type_; + } + + fn component_dict(&self) -> HashMap{ + // map of qubit index to wire component + let mut components = HashMap::::new(); + let indices = self.get_wire_indices(); + // for each qubit in packedinstruction, call wire component + for idx in indices{ + components.insert(idx, self.wire_component(idx)); + } + components + } +} + + pub struct circuit_rep { q_wires: Vec::, dag_circ: DAGCircuit } -impl circuit_rep { +impl<'a> circuit_rep { + pub fn from_instruction(&'a self, instruction: &'a PackedInstruction) -> impl DrawElement + 'a { + // println!("{:?}", instruction.label()); + if let Some(standard_gate) = instruction.op.try_standard_gate() { + let instruction_name = instruction.op.name(); + let instruction_param =format!("{:?}",instruction.params_view()); + let mut instruction_label: Option = match standard_gate { + StandardGate::GlobalPhase => { + // Global phase gate - affects overall circuit phase + + None + } + StandardGate::H => { + // Hadamard gate - creates superposition + Some("H".to_string()) + } + StandardGate::I => { + // Identity gate - no operation + Some("I".to_string()) + } + StandardGate::X => { + // Pauli-X gate (NOT gate) + Some("X".to_string()) + } + StandardGate::Y => { + // Pauli-Y gate + Some("Y".to_string()) + } + StandardGate::Z => { + // Pauli-Z gate + Some("Z".to_string()) + } + StandardGate::Phase => { + // Phase gate (parameterized) + Some(format!("P({})", instruction_param)) + } + StandardGate::R => { + // R gate (rotation about axis in XY plane) + Some(format!("R({})", instruction_param)) + } + StandardGate::RX => { + // Rotation about X axis + Some(format!("RX({})", instruction_param)) + } + StandardGate::RY => { + // Rotation about Y axis + Some(format!("RY({})", instruction_param)) + } + StandardGate::RZ => { + // Rotation about Z axis + Some(format!("RZ({})", instruction_param)) + } + StandardGate::S => { + // S gate (phase π/2) + Some("S".to_string()) + } + StandardGate::Sdg => { + // S dagger gate (phase -π/2) + Some("S†".to_string()) + } + StandardGate::SX => { + // Square root of X gate + Some("√X".to_string()) + } + StandardGate::SXdg => { + // Square root of X dagger gate + Some("√X†".to_string()) + } + StandardGate::T => { + // T gate (phase π/4) + Some("T".to_string()) + } + StandardGate::Tdg => { + // T dagger gate (phase -π/4) + Some("T†".to_string()) + } + StandardGate::U => { + // Universal single-qubit gate (3 parameters) + Some(format!("U({})", instruction_param)) + } + StandardGate::U1 => { + // U1 gate (1 parameter - phase) + Some(format!("U1({})", instruction_param)) + } + StandardGate::U2 => { + // U2 gate (2 parameters) + Some(format!("U2({})", instruction_param)) + } + StandardGate::U3 => { + // U3 gate (3 parameters - equivalent to U) + Some(format!("U3({})", instruction_param)) + } + StandardGate::CH => { + // Controlled Hadamard gate + Some("H".to_string()) + } + StandardGate::CX => { + // Controlled-X gate (CNOT) + Some("X".to_string()) + } + StandardGate::CY => { + // Controlled-Y gate + Some("Y".to_string()) + } + StandardGate::CZ => { + // Controlled-Z gate + Some("Z".to_string()) + } + StandardGate::DCX => { + // Double CNOT gate + Some("DCX".to_string()) + } + StandardGate::ECR => { + // Echoed cross-resonance gate + Some("ECR".to_string()) + } + StandardGate::Swap => { + // Swap gate + None + } + StandardGate::ISwap => { + // i-Swap gate + None + } + StandardGate::CPhase => { + // Controlled phase gate + + Some(format!("P({})", instruction_param)) + } + StandardGate::CRX => { + // Controlled rotation about X + Some(format!("RX({})", instruction_param)) + } + StandardGate::CRY => { + // Controlled rotation about Y + Some(format!("RY({})", instruction_param)) + } + StandardGate::CRZ => { + // Controlled rotation about Z + Some(format!("RZ({})", instruction_param)) + } + StandardGate::CS => { + // Controlled S gate + Some("S".to_string()) + } + StandardGate::CSdg => { + // Controlled S dagger gate + Some("S†".to_string()) + } + StandardGate::CSX => { + // Controlled square root of X gate + Some("√X".to_string()) + } + StandardGate::CU => { + // Controlled U gate (4 parameters) + Some(format!("U({})", instruction_param)) + } + StandardGate::CU1 => { + // Controlled U1 gate + Some(format!("U1({})", instruction_param)) + } + StandardGate::CU3 => { + // Controlled U3 gate + Some(format!("U3({})", instruction_param)) + } + StandardGate::RXX => { + // Two-qubit XX rotation + Some(format!("RXX({})", instruction_param)) + } + StandardGate::RYY => { + // Two-qubit YY rotation + Some(format!("RYY({})", instruction_param)) + } + StandardGate::RZZ => { + // Two-qubit ZZ rotation + Some(format!("RZZ({})", instruction_param)) + } + StandardGate::RZX => { + // Two-qubit ZX rotation + Some(format!("RZX({})", instruction_param)) + } + StandardGate::XXMinusYY => { + // XX-YY gate + Some(format!("XX-YY({})", instruction_param)) + } + StandardGate::XXPlusYY => { + // XX+YY gate + Some(format!("XX+YY({})", instruction_param)) + } + StandardGate::CCX => { + // Toffoli gate (controlled-controlled-X) + Some("X".to_string()) + } + StandardGate::CCZ => { + // Controlled-controlled-Z gate + Some("Z".to_string()) + } + StandardGate::CSwap => { + // Controlled swap gate (Fredkin gate) + None + } + StandardGate::RCCX => { + // Relative-phase Toffoli gate + Some(format!("RX({})", instruction_param)) + } + StandardGate::C3X => { + // 3-controlled X gate (4-qubit controlled X) + Some("X".to_string()) + } + StandardGate::C3SX => { + // 3-controlled square root of X gate + Some("√X".to_string()) + } + StandardGate::RC3X => { + // Relative-phase 3-controlled X gate + Some(format!("RX({})", instruction_param)) + } + }; + + // let label = instruction.label(); + // instruction_label = match label { + // Some(l) => { + // if l != "" { + // Some(l) + // } else { + // instruction_label + // } + // } + // None => instruction_label, + // }; + let visual_type = match standard_gate { + StandardGate::GlobalPhase => VisualType::BetweenWire(instruction.params_view().get(0).map_or("".to_string(), |p| format!("{:?}", p))), + StandardGate::Swap => VisualType::DirectOnWire("X".to_string()), + StandardGate::CPhase => VisualType::BetweenWire(instruction_label.clone().unwrap()), + _ => VisualType::Boxed, + }; + // // handle case for when the control state is different + // println!("name: {}", instruction_name); + // println!("label: {:?}", instruction_label); + // println!("params: {:?}", instruction_param); + + // fix so that enclosed only has a refernce to the original packed instruction + + Enclosed { + packedinst: &instruction, + wire: Wire_Type::qubit, + dag_circ: &self.dag_circ, + name: instruction_name.to_string(), + label: instruction_label, + visual_type: visual_type, + } + } + else if let Some(standard_instruction) = instruction.op.try_standard_instruction() { + match standard_instruction { + StandardInstruction::Measure =>{ + Enclosed{ + packedinst: &instruction, + wire: Wire_Type::qubit, + dag_circ: &self.dag_circ, + name: "Measure".to_string(), + label: Some("M".to_string()), + visual_type: VisualType::Boxed, + } + }, + StandardInstruction::Barrier(x) => { + + let label = instruction.label(); + let inst_label = match label{ + None => barrier_char.to_string(), + Some("") => barrier_char.to_string(), + Some(label) => label.to_string(), + }; + + println!("barrier label: {}", inst_label); + + Enclosed{ + packedinst: &instruction, + wire: Wire_Type::qubit, + dag_circ: &self.dag_circ, + name: "Barrier".to_string(), + label: Some(inst_label.clone()), + visual_type: VisualType::DirectOnWire(barrier_char.to_string()), + } + }, + StandardInstruction::Reset => { + Enclosed{ + packedinst: &instruction, + wire: Wire_Type::qubit, + dag_circ: &self.dag_circ, + name: "Reset".to_string(), + label: Some("|0>".to_string()), + visual_type: VisualType::DirectOnWire("|0>".to_string()), + } + }, + StandardInstruction::Delay(_unit) => { + + let unit = format!("{:?}", _unit).to_lowercase(); + + let label = if let Some(dur) = instruction.params_view().get(0){ + format!("Delay({:?}[{:?}])", dur, unit) + } else { + "Delay".to_string() + }; + + Enclosed{ + packedinst: &instruction, + wire: Wire_Type::qubit, + dag_circ: &self.dag_circ, + name: "Delay".to_string(), + label: Some(label.clone()), + visual_type: VisualType::Boxed, + } + } + } + } + else { + // print all operation details + println!("Unsupported operation details:"); + //get operator discriminant + // let PackedOperation(discriminant) = instruction.op; + // println!("{064b}", discriminant); + println!("Name: {}", instruction.op.name()); + println!("Parameters: {:?}", instruction.params_view()); + println!("Qubits: {:?}", self.dag_circ.qargs_interner().get(instruction.qubits)); + println!("Clbits: {:?}", self.dag_circ.cargs_interner().get(instruction.clbits)); + panic!("Unsupported operation: {}", instruction.op.name()); + + } + } + pub fn new(dag_circ: DAGCircuit) -> Self { //number of qubits in dag_circuit let qubit = dag_circ.num_qubits(); circuit_rep { - q_wires: vec!(wire::new(wire_type::qubit); qubit as usize), + q_wires: vec!(wire::new(Wire_Type::qubit); qubit as usize), dag_circ: dag_circ } } @@ -219,16 +719,18 @@ impl circuit_rep { output } - pub fn fix_len(&mut self, chr: &str) { + pub fn fix_len(&mut self) { let mut num = 0; - for wire in self.q_wires.iter() { - if wire.wire_len > num { - num = wire.wire_len; + for wire in self.q_wires.iter_mut() { + let length = wire.get_len(); + if length > num { + num = length; } } for wire in self.q_wires.iter_mut() { - wire.fix_len(num - wire.wire_len); + let length = wire.get_len(); + wire.fix_len(num - length); } } @@ -245,117 +747,22 @@ impl circuit_rep { }; self.q_wires[i].qubit_name(&qubit_name); } - self.fix_len(" "); + self.fix_len(); } pub fn build_layer(&mut self, layer: Vec<&PackedInstruction>){ for instruction in layer{ - let enclosed_inst = enclosed::from_instruction(instruction, &self.dag_circ); - // println!("{:?}", enclosed_inst); - // if instruction.op.control_flow() { - // panic!("Control flow operations not supported yet: {}", instruction.op.name()); - // } - - // // Check for standard instructions (barrier, measure, reset, delay) - // if let Some(standard_instruction) = instruction.op.try_standard_instruction() { - // match standard_instruction { - // StandardInstruction::Measure => { - // // This is a measurement operation - // // Handle: MeasureFrom element on qubit, MeasureTo element on clbit - // // Access qubits: instruction.qubits (interned slice) - // // Access clbits: instruction.clbits (interned slice) - // // Create MeasureFrom element for the qubit - // // Create MeasureTo element for the clbit - // // Handle classical register bundling if needed - // } - // StandardInstruction::Barrier(num_qubits) => { - // // This is a barrier directive - // // Handle: Barrier elements across specified qubits - - // //get label of barrier - - // // Number of qubits affected: num_qubits - // // Access affected qubits: instruction.qubits - // // Check if barriers should be plotted with plotbarriers flag - // // Place barrier symbol on each affected qubit wire - // // Add label on topmost qubit if present: instruction.label() - // let mut temp:bool = false; - // for wire in self.q_wires.iter_mut() { - // if wire.type_ == wire_type::qubit { - // wire.top.push_str("░"); - // wire.mid.push_str("░"); - // wire.bot.push_str("░"); - // } - // } - // } - // StandardInstruction::Reset => { - // // PANIC - Reset not supported yet - // panic!("Reset operations not supported yet"); - // } - // StandardInstruction::Delay(_unit) => { - // // PANIC - Delay not supported yet - // panic!("Delay operations not supported yet"); - // } - // } - // } - - // // Check for standard gates - // else if let Some(standard_gate) = instruction.op.try_standard_gate() { - // // This is a standard gate operation - // let num_qubits = instruction.op.num_qubits(); - - // if num_qubits == 1 { - // // Single qubit standard gate - // // Handle: BoxOnQuWire element - // // Gate name: instruction.op.name() - // // Parameters: instruction.params_view() - format for display - // // Label: instruction.label() - // // Check for conditional operation (if condition exists) - // // Place single qubit box element on the target wire - // } else { - // // Multi-qubit standard gate - // // Access target qubits: instruction.qubits - // // Gate name: instruction.op.name() - // // Parameters: instruction.params_view() - // // Label: instruction.label() - - // // Special handling for specific multi-qubit gates: - // match standard_gate { - // // SwapGate: Create Ex elements with connections - // // RZZGate: Create Bullet elements with ZZ connection label - // // CXGate, CZGate, etc: Handle as controlled gates - // // Other multi-qubit gates: Use multi-qubit box spanning all qubits - // _ => { - // // Generic multi-qubit gate handling - // // Create box elements spanning all target qubits - // // Handle top/middle/bottom box parts for multi-qubit span - // } - // } - // } - // } - - // // Everything else - PANIC for now - // else { - // match instruction.op.view() { - // OperationRef::Gate(_) => { - // panic!("Python gates not supported yet: {}", instruction.op.name()); - // } - // OperationRef::Instruction(_) => { - // panic!("Python instructions not supported yet: {}", instruction.op.name()); - // } - // OperationRef::Operation(_) => { - // panic!("Python operations not supported yet: {}", instruction.op.name()); - // } - // OperationRef::Unitary(_) => { - // panic!("Unitary gates not supported yet: {}", instruction.op.name()); - // } - // _ => { - // panic!("Unknown operation type: {}", instruction.op.name()); - // } - // } - // } - // } + let components = { + let enclosed = self.from_instruction(instruction); + enclosed.component_dict() + }; + println!("components: {:?}", components); + for (idx, component) in components{ + self.q_wires[idx as usize].add_wire_component(&component); + } } + // normalise all wire widths + // self.fix_len(); } pub fn build_layers(&mut self) { @@ -487,7 +894,7 @@ impl circuit_rep { packedin_layer.push(instruction); } } - println!("Layer {}", id); + // println!("Layer {}", id); self.build_layer(packedin_layer.clone()); } @@ -502,8 +909,8 @@ pub fn circuit_draw(dag_circ: &DAGCircuit) { // Create a circuit representation let mut circuit_rep = circuit_rep::new(dag_circ.clone()); circuit_rep.set_qubit_name(); - output.push_str(&circuit_rep.circuit_string()); circuit_rep.build_layers(); + output.push_str(&circuit_rep.circuit_string()); // Print the circuit representation println!("{}", output); } From d4793e5836f65dae8994d4e130235867318d3592 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Fri, 19 Sep 2025 18:30:04 +0530 Subject: [PATCH 17/70] Visualization Matrix structure completed --- crates/circuit/src/circuit_drawer.rs | 554 +++++++++++++++++++-------- 1 file changed, 387 insertions(+), 167 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 13d5e30d2ed3..3d0f8a463300 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -10,6 +10,7 @@ // copyright notice, and modified files need to carry a notice indicating // that they have been altered from the originals. +use core::panic; use std::fmt::{format, Debug}; use std::hash::{Hash, RandomState}; use std::sync::Barrier; @@ -64,37 +65,39 @@ pub fn py_drawer(py: Python, quantum_circuit: &Bound) -> PyResult<()> { Ok(()) } -pub const q_wire: &str = "─"; -pub const c_wire: char = '═'; -pub const top_con: char = '┴'; -pub const bot_con: char = '┬'; -pub const left_con: char = '┤'; -pub const right_con: char = '├'; -pub const top_left_con: char = '┌'; -pub const top_right_con: char = '┐'; -pub const bot_left_con: char = '└'; -pub const bot_right_con: char = '┘'; -pub const barrier_char: char = '░'; +pub const Q_WIRE: &str = "─"; +pub const C_WIRE: char = '═'; +pub const TOP_CON: char = '┴'; +pub const BOT_CON: char = '┬'; +pub const LEFT_CON: char = '┤'; +pub const RIGHT_CON: char = '├'; +pub const TOP_LEFT_CON: char = '┌'; +pub const TOP_RIGHT_CON: char = '┐'; +pub const BOT_LEFT_CON: char = '└'; +pub const BOT_RIGHT_CON: char = '┘'; +pub const BARRIER_CHAR: char = '░'; +pub const BULLET:char = '■'; + #[derive(Clone, Debug, PartialEq, Eq)] -enum Wire_Type { - qubit, - clbit, +enum WireType { + Qubit, + Clbit, } -impl From<&Wire_Type> for &str { - fn from(Wire_Type: &Wire_Type) -> Self { - match Wire_Type { - Wire_Type::qubit => "─", - Wire_Type::clbit => "═", +impl From<&WireType> for &str { + fn from(wire_type: &WireType) -> Self { + match wire_type { + WireType::Qubit => "─", + WireType::Clbit => "═", } } } #[derive(Clone, Debug)] -enum control_type{ - open, - closed, +enum ControlType{ + Open, + Closed } #[derive(Clone, Debug)] @@ -106,11 +109,201 @@ pub struct wire { top: String, mid: String, bot: String, - type_: Wire_Type, + type_: WireType, +} + +#[derive(Clone, Debug)] +pub struct Enclosed<'a>{ + packedinst: &'a PackedInstruction, + dag_circ: &'a DAGCircuit, + wire: WireType, + name: String, + label: Option, + visual_type: VisualType, +} + +struct Visualizer<'a>{ + visualization_matrix: VisualizationMatrix<'a>, + circuit_rep: CircuitRep +} + +struct VisualizationMatrix<'a>{ + visualization_layers: Vec>, + packedinst_layers: Vec>, + circuit_rep: &'a CircuitRep +} + +struct VisualizationLayer<'a>{ + elements: Vec>, + width: u32, + circuit_rep: &'a CircuitRep +} + +#[derive(Clone, Debug)] +pub struct VisualizationElement<'a>{ + element: Enclosed<'a>, + ascii_string: String, + ind: u32, + circuit_rep: &'a CircuitRep +} + +// pub struct VisualizationElement<'a>{ +// element: Option<&'a PackedInstruction>, +// ascii_string: String, +// ind: u32, +// circuit_rep: &'a CircuitRep +// } + +impl<'a> VisualizationMatrix<'a>{ + + pub fn new(circuit_rep: &'a CircuitRep, packedinst_layers: Vec>) -> Self{ + let mut visualization_layers: Vec = Vec::with_capacity(packedinst_layers.len()); + for layer in packedinst_layers.iter(){ + let vis_layer = VisualizationLayer::new(circuit_rep, layer.to_vec()); + visualization_layers.push(vis_layer); + } + + VisualizationMatrix{ + visualization_layers, + packedinst_layers, + circuit_rep + } + } + + pub fn print(&self){ + let mut output: Vec = Vec::new(); + for layer in &self.visualization_layers{ + let layer_col = layer.get_layer_col(); + if output.is_empty(){ + output = layer_col; + } else { + // append strings in ith index to output string in ith index + for i in 0..output.len(){ + output[i].push_str(&layer_col[i]); + } + } + } + for i in output{ + println!("{}", i); + } + } +} + +impl<'a> VisualizationLayer<'a>{ + + pub fn new(circuit_rep: &'a CircuitRep, packedinst_layer: Vec<&'a PackedInstruction>) -> Self{ + + let dummy_element: VisualizationElement = VisualizationElement{ + element: Enclosed{ + packedinst: &packedinst_layer[0], + dag_circ: &circuit_rep.dag_circ, + wire: WireType::Qubit, + name: String::new(), + label: None, + visual_type: VisualType::Boxed + }, + ascii_string: String::new(), + ind: 0, + circuit_rep + }; + + + //println!("{}",packedinst_layer.len()); + let mut vis_layer:Vec = vec![dummy_element;circuit_rep.get_indices() as usize]; + let mut enclosed_elements = { + let mut enclosed_layer:Vec = Vec::new(); + for &inst in packedinst_layer.iter(){ + let enclosed = circuit_rep.from_instruction(inst); + let indices = enclosed.get_wire_indices(); + // println!("enclosed indices: {:?}", indices); + // println!("{:?}", vis_layer); + for ind in indices{ + let vis_element = VisualizationElement::new(enclosed.clone(), circuit_rep, ind); + vis_layer[ind as usize] = vis_element; + } + } + enclosed_layer + }; + + let mut visualization_layer = VisualizationLayer{ + elements: vis_layer, + width: 0, + circuit_rep + }; + visualization_layer.width = visualization_layer.set_width(); + visualization_layer + + } + + pub fn get_enclosed(&self, inst: &'a PackedInstruction) -> Enclosed{ + self.circuit_rep.from_instruction(inst) + } + + pub fn set_width(&mut self) -> u32{ + let mut max_width:u32 = 0; + + for element in self.elements.iter(){ + let temp = element.get_length(); + if temp > max_width { + max_width = temp; + } + } + + max_width + } + + pub fn get_layer_col(&self) -> Vec{ + let mut layer_col: Vec = Vec::new(); + for element in &self.elements{ + layer_col.push(format!("{}{}",element.get_string(), " ".repeat((self.width - element.get_length()) as usize))); + } + layer_col + } +} + + + +impl<'a> VisualizationElement<'a>{ + + pub fn new(element: Enclosed<'a>, circuit_rep: &'a CircuitRep,ind: u32) -> Self{ + let mut vis_element = VisualizationElement{ + element, + ascii_string: String::new(), + ind, + circuit_rep + }; + vis_element.ascii_string = vis_element.set_string(); + return vis_element; + } + + pub fn get_string(&self) -> String{ + self.ascii_string.clone() + } + + pub fn get_length(&self) -> u32{ + self.ascii_string.len() as u32 + } + + pub fn set_string(&mut self) -> String{ + let enclosed = &self.element; + let mut ret: String = " ".to_string(); + + let indices = enclosed.get_wire_indices(); + + if self.ind == indices[0] { + ret = enclosed.get_name(); + }else if indices.contains(&self.ind) { + ret = format!("{}({})", BULLET,indices[0]); + }else { + ret = <&str>::from(&enclosed.wire).to_string(); + } + + format!("{}",ret) + } } impl wire { - pub fn new(type_: Wire_Type) -> Self { + pub fn new(type_: WireType) -> Self { wire { top: String::new(), mid: String::new(), @@ -171,10 +364,10 @@ impl wire { } pub fn add_wire_component(&mut self, component: &wire) { - &self.top.push_str(&component.top); - &self.mid.push_str(&component.mid); - &self.bot.push_str(&component.bot); - &self.update_wire_len(); + self.top.push_str(&component.top); + self.mid.push_str(&component.mid); + self.bot.push_str(&component.bot); + self.update_wire_len(); } } @@ -200,20 +393,11 @@ enum VisualType{ Boxed, BetweenWire(String), DirectOnWire(String), - WholeWire, -} - -pub struct Enclosed<'a>{ - packedinst: &'a PackedInstruction, - dag_circ: &'a DAGCircuit, - wire: Wire_Type, - name: String, - label: Option, - visual_type: VisualType, + WholeWire } impl<'a> Enclosed<'a> { - pub fn new(packedinst: &'a PackedInstruction, dag_circ: &'a DAGCircuit ,wire: Wire_Type, name: String, label: Option, visual_type: VisualType) -> Enclosed<'a>{ + pub fn new(packedinst: &'a PackedInstruction, dag_circ: &'a DAGCircuit ,wire: WireType, name: String, label: Option, visual_type: VisualType) -> Enclosed<'a>{ Enclosed { packedinst, dag_circ, @@ -224,6 +408,8 @@ impl<'a> Enclosed<'a> { } } + + pub fn get_wire_indices(&self) -> Vec{ let mut qubit_indices: Vec = self.dag_circ.qargs_interner().get(self.packedinst.qubits) .iter() @@ -249,12 +435,12 @@ impl<'a> Enclosed<'a> { let max_index = *wire_indices.iter().max().unwrap(); // barrier handling - if self.visual_type == VisualType::DirectOnWire(barrier_char.to_string()) { - let mut label = barrier_char.to_string(); + if self.visual_type == VisualType::DirectOnWire(BARRIER_CHAR.to_string()) { + let mut label = BARRIER_CHAR.to_string(); if let Some(l) = &self.label{ label = l.to_string(); } else { - label = barrier_char.to_string(); + label = BARRIER_CHAR.to_string(); } let label_len:usize = label.len(); @@ -274,13 +460,13 @@ impl<'a> Enclosed<'a> { } let component_wire = format!("{}{}{}", <&str>::from(&self.wire).repeat(left_pad_len), - barrier_char, + BARRIER_CHAR, <&str>::from(&self.wire).repeat(right_pad_len) ); let between_wire = format!("{}{}{}", " ".repeat(left_pad_len), - barrier_char, + BARRIER_CHAR, " ".repeat(right_pad_len) ); @@ -298,18 +484,18 @@ impl<'a> Enclosed<'a> { VisualType::Boxed => { if let Some(label) = &self.label { let label_len = label.len() as usize; - component.top.push_str(&format!("{}{}{}", top_left_con, "─".repeat(label_len as usize), top_right_con)); - component.mid.push_str(&format!("{}{}{}", left_con, label, right_con)); - component.bot.push_str(&format!("{}{}{}", bot_left_con, "─".repeat(label_len as usize), bot_right_con)); + component.top.push_str(&format!("{}{}{}", TOP_LEFT_CON, "─".repeat(label_len as usize), TOP_RIGHT_CON)); + component.mid.push_str(&format!("{}{}{}", LEFT_CON, label, RIGHT_CON)); + component.bot.push_str(&format!("{}{}{}", BOT_LEFT_CON, "─".repeat(label_len as usize), BOT_RIGHT_CON)); } else { panic!("Boxed visual has no label"); } } VisualType::BetweenWire(label) => { let label_len = label.len() as u64; - component.top.push_str(&format!("{}{}{}", top_con, "─".repeat(label_len as usize), top_con)); + component.top.push_str(&format!("{}{}{}", TOP_CON, "─".repeat(label_len as usize), TOP_CON)); component.mid.push_str(&format!(" {} ", label)); - component.bot.push_str(&format!("{}{}{}", bot_con, "─".repeat(label_len as usize), bot_con)); + component.bot.push_str(&format!("{}{}{}", BOT_CON, "─".repeat(label_len as usize), BOT_CON)); } VisualType::DirectOnWire(label) => { component.top.push_str(" ".repeat(label.len()).as_str()); @@ -326,6 +512,10 @@ impl<'a> Enclosed<'a> { component.update_wire_len(); component } + + pub fn get_name(&self) -> String{ + self.name.clone() + } } impl<'a> DrawElement for Enclosed<'a>{ @@ -364,14 +554,14 @@ impl<'a> DrawElement for Enclosed<'a>{ } } - -pub struct circuit_rep { +#[derive(Clone, Debug)] +pub struct CircuitRep { q_wires: Vec::, dag_circ: DAGCircuit } -impl<'a> circuit_rep { - pub fn from_instruction(&'a self, instruction: &'a PackedInstruction) -> impl DrawElement + 'a { +impl<'a> CircuitRep { + pub fn from_instruction(&'a self, instruction: &'a PackedInstruction) -> Enclosed<'a>{ // println!("{:?}", instruction.label()); if let Some(standard_gate) = instruction.op.try_standard_gate() { let instruction_name = instruction.op.name(); @@ -589,33 +779,27 @@ impl<'a> circuit_rep { } }; - // let label = instruction.label(); - // instruction_label = match label { - // Some(l) => { - // if l != "" { - // Some(l) - // } else { - // instruction_label - // } - // } - // None => instruction_label, - // }; + let label = instruction.label(); + instruction_label = match label { + Some(l) => { + if l != "" { + Some(l.to_string()) + } else { + instruction_label + } + } + None => instruction_label, + }; let visual_type = match standard_gate { StandardGate::GlobalPhase => VisualType::BetweenWire(instruction.params_view().get(0).map_or("".to_string(), |p| format!("{:?}", p))), StandardGate::Swap => VisualType::DirectOnWire("X".to_string()), StandardGate::CPhase => VisualType::BetweenWire(instruction_label.clone().unwrap()), _ => VisualType::Boxed, }; - // // handle case for when the control state is different - // println!("name: {}", instruction_name); - // println!("label: {:?}", instruction_label); - // println!("params: {:?}", instruction_param); - - // fix so that enclosed only has a refernce to the original packed instruction Enclosed { packedinst: &instruction, - wire: Wire_Type::qubit, + wire: WireType::Qubit, dag_circ: &self.dag_circ, name: instruction_name.to_string(), label: instruction_label, @@ -627,7 +811,7 @@ impl<'a> circuit_rep { StandardInstruction::Measure =>{ Enclosed{ packedinst: &instruction, - wire: Wire_Type::qubit, + wire: WireType::Qubit, dag_circ: &self.dag_circ, name: "Measure".to_string(), label: Some("M".to_string()), @@ -638,8 +822,8 @@ impl<'a> circuit_rep { let label = instruction.label(); let inst_label = match label{ - None => barrier_char.to_string(), - Some("") => barrier_char.to_string(), + None => BARRIER_CHAR.to_string(), + Some("") => BARRIER_CHAR.to_string(), Some(label) => label.to_string(), }; @@ -647,17 +831,17 @@ impl<'a> circuit_rep { Enclosed{ packedinst: &instruction, - wire: Wire_Type::qubit, + wire: WireType::Qubit, dag_circ: &self.dag_circ, name: "Barrier".to_string(), label: Some(inst_label.clone()), - visual_type: VisualType::DirectOnWire(barrier_char.to_string()), + visual_type: VisualType::DirectOnWire(BARRIER_CHAR.to_string()), } }, StandardInstruction::Reset => { Enclosed{ packedinst: &instruction, - wire: Wire_Type::qubit, + wire: WireType::Qubit, dag_circ: &self.dag_circ, name: "Reset".to_string(), label: Some("|0>".to_string()), @@ -676,7 +860,7 @@ impl<'a> circuit_rep { Enclosed{ packedinst: &instruction, - wire: Wire_Type::qubit, + wire: WireType::Qubit, dag_circ: &self.dag_circ, name: "Delay".to_string(), label: Some(label.clone()), @@ -705,8 +889,8 @@ impl<'a> circuit_rep { //number of qubits in dag_circuit let qubit = dag_circ.num_qubits(); - circuit_rep { - q_wires: vec!(wire::new(Wire_Type::qubit); qubit as usize), + CircuitRep { + q_wires: vec!(wire::new(WireType::Qubit); qubit as usize), dag_circ: dag_circ } } @@ -719,6 +903,12 @@ impl<'a> circuit_rep { output } + pub fn get_indices(&self) -> u32{ + let total_qubits = self.dag_circ.num_qubits() as u32; + let total_clbits = self.dag_circ.num_clbits() as u32; + total_qubits + total_clbits + } + pub fn fix_len(&mut self) { let mut num = 0; for wire in self.q_wires.iter_mut() { @@ -750,6 +940,66 @@ impl<'a> circuit_rep { self.fix_len(); } + pub fn get_instruction_range(&self, instruction: &PackedInstruction) -> (u32,u32){ + let node_qubits = self.dag_circ.qargs_interner().get(instruction.qubits); + let node_clbits = self.dag_circ.cargs_interner().get(instruction.clbits); + let total_qubits = self.dag_circ.num_qubits() as u32; + let node_min_qubit = node_qubits.iter().map(|q| q.0).min(); + let node_max_qubit = node_qubits.iter().map(|q| q.0).max(); + let node_min_clbit = node_clbits.iter().map(|c| c.0).min(); + let node_max_clbit = node_clbits.iter().map(|c| c.0).max(); + + let node_min = match node_min_qubit { + Some(val) => val, + None => match node_max_qubit { + Some(val) => val, + None => match node_min_clbit { + Some(val) => val + total_qubits, + None => match node_max_clbit { + Some(val) => val + total_qubits, + None => panic!("Non-empty node with no qubits/clbits"), // No qubits or clbits, skip this node + }, + }, // No qubits or clbits, skip this node + }, + }; + + // let node_min = node_min_qubit.unwrap_or( + // node_max_qubit.unwrap_or( + // node_min_clbit.map(|val| val + total_qubits).unwrap_or( + // node_max_clbit.map(|val| val + total_qubits).unwrap_or( + // panic!("Non-empty node with no qubits/clbits \n {:?}", instruction) + // ) + // ) + // ) + // ); + + let node_max = match node_max_clbit { + Some(val) => val + total_qubits, + None => match node_min_clbit { + Some(val) => val + total_qubits, + None => match node_max_qubit { + Some(val) => val, + None => match node_min_qubit { + Some(val) => val, + None => panic!("Non-empty node with no qubits/clbits"), // No qubits or clbits, skip this node + }, + }, // No qubits or clbits, skip this node + }, + }; + + // let node_max = node_max_clbit.map(|val| val + total_qubits).unwrap_or( + // node_min_clbit.map(|val| val + total_qubits).unwrap_or( + // node_max_qubit.unwrap_or( + // node_min_qubit.unwrap_or( + // panic!("Non-empty node with no qubits/clbits \n {:?}", instruction) + // ) + // ) + // ) + // ); + + return (node_min, node_max); + } + pub fn build_layer(&mut self, layer: Vec<&PackedInstruction>){ for instruction in layer{ let components = { @@ -765,106 +1015,54 @@ impl<'a> circuit_rep { // self.fix_len(); } - pub fn build_layers(&mut self) { - let binding = self.dag_circ.clone(); - let layer_iterator = binding.multigraph_layers(); + pub fn build_layers(&mut self) -> Vec> { + let layer_iterator = self.dag_circ.multigraph_layers(); let mut final_layers:Vec> = Vec::new(); - let total_qubits = binding.num_qubits() as u32; - let total_clbits = binding.num_clbits() as u32; - for (i,layer) in layer_iterator.enumerate(){ let mut sublayers: Vec> = vec![Vec::new()]; + let mut set: HashSet = hashbrown::HashSet::new(); for node_index in layer { - - if let NodeType::Operation(instruction_to_insert) = &binding.dag()[node_index]{ + if let NodeType::Operation(instruction_to_insert) = &self.dag_circ.dag()[node_index]{ if sublayers.is_empty() { sublayers.push(vec![node_index]); continue; } - let node_qubits = binding.qargs_interner().get(instruction_to_insert.qubits); - let node_clbits = binding.cargs_interner().get(instruction_to_insert.clbits); - - let node_min_qubit = node_qubits.iter().map(|q| q.0).min(); - let node_max_qubit = node_qubits.iter().map(|q| q.0).max(); - let node_min_clbit = node_clbits.iter().map(|c| c.0).min(); - let node_max_clbit = node_clbits.iter().map(|c| c.0).max(); - - let node_min = match node_min_qubit { - Some(val) => val, - None => match node_max_qubit { - Some(val) => val, - None => match node_min_clbit { - Some(val) => val + total_qubits, - None => match node_max_clbit { - Some(val) => val + total_qubits, - None => continue, // No qubits or clbits, skip this node - }, - }, // No qubits or clbits, skip this node - }, + if self.dag_circ.qargs_interner().get(instruction_to_insert.qubits).is_empty() && + self.dag_circ.cargs_interner().get(instruction_to_insert.clbits).is_empty() { + continue; }; - let node_max = match node_max_clbit { - Some(val) => val + total_qubits, - None => match node_min_clbit { - Some(val) => val + total_qubits, - None => match node_max_qubit { - Some(val) => val, - None => match node_min_qubit { - Some(val) => val, - None => continue, // No qubits or clbits, skip this node - }, - }, // No qubits or clbits, skip this node - }, - }; + let (node_min,node_max) = self.get_instruction_range(instruction_to_insert); let mut sublayer = sublayers.last_mut().unwrap(); let mut overlap = false; - for &subnode in sublayer.iter() { - if let NodeType::Operation(instruction) = &binding.dag()[subnode]{ - let subnode_qubits = binding.qargs_interner().get(instruction.qubits); - let subnode_clbits = binding.cargs_interner().get(instruction.clbits); - let subnode_min_qubit = subnode_qubits.iter().map(|q| q.0).min(); - let subnode_max_qubit = subnode_qubits.iter().map(|q| q.0).max(); - let subnode_min_clbit = subnode_clbits.iter().map(|c| c.0).min(); - let subnode_max_clbit = subnode_clbits.iter().map(|c| c.0).max(); - let subnode_min = match subnode_min_qubit { - Some(val) => val, - None => match subnode_max_qubit { - Some(val) => val, - None => match subnode_min_clbit { - Some(val) => val + total_qubits, - None => match subnode_max_clbit { - Some(val) => val + total_qubits, - None => continue, // No qubits or clbits, skip this node - }, - }, // No qubits or clbits, skip this node - }, - }; - let subnode_max = match subnode_max_clbit { - Some(val) => val + total_qubits, - None => match subnode_min_clbit { - Some(val) => val + total_qubits, - None => match subnode_max_qubit { - Some(val) => val, - None => match subnode_min_qubit { - Some(val) => val, - None => continue, // No qubits or clbits, skip this node - }, - }, // No qubits or clbits, skip this node - }, - }; - if (subnode_min <= node_min && subnode_max >= node_min) || (subnode_min <= node_max && subnode_max >= node_max) { - overlap = true; - break; - } + + if set.contains(&node_min) || set.contains(&node_max){ + overlap = true; + set = hashbrown::HashSet::new(); + } else { + for i in (node_min..node_max){ + set.insert(i); } } + // for &subnode in sublayer.iter() { + // if let NodeType::Operation(instruction) = &self.dag_circ.dag()[subnode]{ + + // let (subnode_min,subnode_max) = self.get_instruction_range(instruction); + + // if (subnode_min <= node_min && subnode_max >= node_min) || (subnode_min <= node_max && subnode_max >= node_max) { + // overlap = true; + // break; + // } + // } + // } + if overlap { sublayers.push(vec![node_index]); } else { @@ -890,14 +1088,23 @@ impl<'a> circuit_rep { for (id,layer) in final_layers.iter().enumerate() { let mut packedin_layer: Vec<&PackedInstruction> = Vec::new(); for nodeind in layer { - if let NodeType::Operation(instruction) = &binding.dag()[*nodeind] { - packedin_layer.push(instruction); + if let NodeType::Operation(instruction) = &self.dag_circ.dag()[*nodeind] { + packedin_layer.push(&instruction); } } + packedin_layers.push(packedin_layer); + // self.build_layer(packedin_layer); // println!("Layer {}", id); - self.build_layer(packedin_layer.clone()); } + for (i,packedin_layer )in packedin_layers.iter().enumerate() { + println!("layer:{}",i); + for &inst in packedin_layer.iter(){ + println!("{:?}", inst.op.name()); + } + } + + packedin_layers } } @@ -907,10 +1114,23 @@ pub fn circuit_draw(dag_circ: &DAGCircuit) { let mut output = String::new(); // Create a circuit representation - let mut circuit_rep = circuit_rep::new(dag_circ.clone()); + let mut circuit_rep = CircuitRep::new(dag_circ.clone()); circuit_rep.set_qubit_name(); - circuit_rep.build_layers(); - output.push_str(&circuit_rep.circuit_string()); - // Print the circuit representation - println!("{}", output); + // circuit_rep.build_layers(); + // output.push_str(&circuit_rep.circuit_string()); + + //print using visualisation matrix + let packedinst_layers = circuit_rep.build_layers(); + let mut circuit_rep2 = CircuitRep::new(dag_circ.clone()); + circuit_rep2.set_qubit_name(); + let vis_mat:VisualizationMatrix = VisualizationMatrix::new(&circuit_rep2, packedinst_layers); + + println!("======================"); + vis_mat.print(); + + + //println!("{}", output); } + + + From 55df1d741da4ba99169da38dd5abec4db915f3ec Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Sun, 21 Sep 2025 14:12:20 +0300 Subject: [PATCH 18/70] Make build_layers a standalone function --- crates/circuit/src/circuit_drawer.rs | 293 +++++++-------------------- 1 file changed, 70 insertions(+), 223 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 3d0f8a463300..bb24944c7b97 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -11,40 +11,17 @@ // that they have been altered from the originals. use core::panic; -use std::fmt::{format, Debug}; -use std::hash::{Hash, RandomState}; -use std::sync::Barrier; -#[cfg(feature = "cache_pygates")] -use std::sync::OnceLock; -use std::thread::current; -use hashbrown::HashSet; -use hashbrown::HashMap; -use crate::bit::{ - BitLocations, ClassicalRegister, PyBit, QuantumRegister, Register, ShareableClbit, - ShareableQubit, -}; -use crate::bit_locator::BitLocator; -use crate::circuit_instruction::{CircuitInstruction, OperationFromPython}; -use crate::classical::expr; -use crate::dag_circuit::{self, add_global_phase, DAGCircuit, DAGStretchType, DAGVarType}; -use crate::imports::{ANNOTATED_OPERATION, QUANTUM_CIRCUIT}; -use rustworkx_core::petgraph::stable_graph::{EdgeReference, NodeIndex}; -use crate::interner::{Interned, Interner}; -use crate::object_registry::ObjectRegistry; -use crate::operations::{Operation, OperationRef, Param, PythonOperation, StandardGate, StandardInstruction}; -use crate::packed_instruction::{PackedInstruction, PackedOperation}; -use crate::parameter_table::{ParameterTable, ParameterTableError, ParameterUse, ParameterUuid}; -use crate::register_data::RegisterData; -use crate::slice::{PySequenceIndex, SequenceIndex}; -use crate::{Clbit, Qubit, Stretch, Var, VarsMode}; - -use numpy::PyReadonlyArray1; -use pyo3::exceptions::*; +use std::fmt::Debug; +use hashbrown::{HashSet, HashMap}; +use crate::dag_circuit::DAGCircuit; +use crate::operations::{Operation, StandardGate, StandardInstruction}; +use crate::packed_instruction::PackedInstruction; +use itertools::{Itertools, MinMaxResult}; + use pyo3::prelude::*; use pyo3::{import_exception}; -use crate::converters::circuit_to_dag; use crate::converters::QuantumCircuitData; use crate::dag_circuit::NodeType; use crate::circuit_data::CircuitData; @@ -53,15 +30,8 @@ import_exception!(qiskit.circuit.exceptions, CircuitError); #[pyfunction(name = "draw")] -pub fn py_drawer(py: Python, quantum_circuit: &Bound) -> PyResult<()> { - if !quantum_circuit.is_instance(QUANTUM_CIRCUIT.get_bound(py))? { - return Err(PyTypeError::new_err( - "Expected a QuantumCircuit instance" - )); - } - let circ_data: QuantumCircuitData = quantum_circuit.extract()?; - let dag_circuit = circuit_to_dag(circ_data, true, None, None)?; - circuit_draw(&dag_circuit); +pub fn py_drawer(circuit: QuantumCircuitData) -> PyResult<()> { + draw_circuit(&circuit.data)?; Ok(()) } @@ -289,7 +259,7 @@ impl<'a> VisualizationElement<'a>{ let mut ret: String = " ".to_string(); let indices = enclosed.get_wire_indices(); - + if self.ind == indices[0] { ret = enclosed.get_name(); }else if indices.contains(&self.ind) { @@ -330,7 +300,7 @@ impl wire { let mid_len = self.mid.len(); let bot_len = self.bot.len(); if top_len == mid_len && mid_len == bot_len { - + } else { let max_len = top_len.max(mid_len).max(bot_len); self.fix_len(max_len); @@ -409,7 +379,7 @@ impl<'a> Enclosed<'a> { } - + pub fn get_wire_indices(&self) -> Vec{ let mut qubit_indices: Vec = self.dag_circ.qargs_interner().get(self.packedinst.qubits) .iter() @@ -479,7 +449,7 @@ impl<'a> Enclosed<'a> { component.bot.push_str( &between_wire); return component; } - + match &self.visual_type { VisualType::Boxed => { if let Some(label) = &self.label { @@ -541,7 +511,7 @@ impl<'a> DrawElement for Enclosed<'a>{ return type_; } - + fn component_dict(&self) -> HashMap{ // map of qubit index to wire component let mut components = HashMap::::new(); @@ -569,7 +539,7 @@ impl<'a> CircuitRep { let mut instruction_label: Option = match standard_gate { StandardGate::GlobalPhase => { // Global phase gate - affects overall circuit phase - + None } StandardGate::H => { @@ -778,7 +748,7 @@ impl<'a> CircuitRep { Some(format!("RX({})", instruction_param)) } }; - + let label = instruction.label(); instruction_label = match label { Some(l) => { @@ -805,7 +775,7 @@ impl<'a> CircuitRep { label: instruction_label, visual_type: visual_type, } - } + } else if let Some(standard_instruction) = instruction.op.try_standard_instruction() { match standard_instruction { StandardInstruction::Measure =>{ @@ -819,7 +789,7 @@ impl<'a> CircuitRep { } }, StandardInstruction::Barrier(x) => { - + let label = instruction.label(); let inst_label = match label{ None => BARRIER_CHAR.to_string(), @@ -868,7 +838,7 @@ impl<'a> CircuitRep { } } } - } + } else { // print all operation details println!("Unsupported operation details:"); @@ -939,198 +909,75 @@ impl<'a> CircuitRep { } self.fix_len(); } +} - pub fn get_instruction_range(&self, instruction: &PackedInstruction) -> (u32,u32){ - let node_qubits = self.dag_circ.qargs_interner().get(instruction.qubits); - let node_clbits = self.dag_circ.cargs_interner().get(instruction.clbits); - let total_qubits = self.dag_circ.num_qubits() as u32; - let node_min_qubit = node_qubits.iter().map(|q| q.0).min(); - let node_max_qubit = node_qubits.iter().map(|q| q.0).max(); - let node_min_clbit = node_clbits.iter().map(|c| c.0).min(); - let node_max_clbit = node_clbits.iter().map(|c| c.0).max(); - - let node_min = match node_min_qubit { - Some(val) => val, - None => match node_max_qubit { - Some(val) => val, - None => match node_min_clbit { - Some(val) => val + total_qubits, - None => match node_max_clbit { - Some(val) => val + total_qubits, - None => panic!("Non-empty node with no qubits/clbits"), // No qubits or clbits, skip this node - }, - }, // No qubits or clbits, skip this node - }, - }; - - // let node_min = node_min_qubit.unwrap_or( - // node_max_qubit.unwrap_or( - // node_min_clbit.map(|val| val + total_qubits).unwrap_or( - // node_max_clbit.map(|val| val + total_qubits).unwrap_or( - // panic!("Non-empty node with no qubits/clbits \n {:?}", instruction) - // ) - // ) - // ) - // ); - - let node_max = match node_max_clbit { - Some(val) => val + total_qubits, - None => match node_min_clbit { - Some(val) => val + total_qubits, - None => match node_max_qubit { - Some(val) => val, - None => match node_min_qubit { - Some(val) => val, - None => panic!("Non-empty node with no qubits/clbits"), // No qubits or clbits, skip this node - }, - }, // No qubits or clbits, skip this node - }, - }; +/// Calculate the range (inclusive) of the given instruction qubits/clbits over the wire indices. +/// The assumption is that clbits always appear after the qubits in the visualization, hence the clbit indices +/// are offset by the number of instruction qubits when calculating the range. + fn get_instruction_range(dag: &DAGCircuit, instruction: &PackedInstruction) -> (usize, usize) { + let node_qubits = dag.get_qargs(instruction.qubits); + let node_clbits = dag.get_cargs(instruction.clbits); - // let node_max = node_max_clbit.map(|val| val + total_qubits).unwrap_or( - // node_min_clbit.map(|val| val + total_qubits).unwrap_or( - // node_max_qubit.unwrap_or( - // node_min_qubit.unwrap_or( - // panic!("Non-empty node with no qubits/clbits \n {:?}", instruction) - // ) - // ) - // ) - // ); - - return (node_min, node_max); - } + let indices = node_qubits.iter().map(|q| q.index()).chain( + node_clbits.iter().map(|c| c.index() + dag.num_qubits())); - pub fn build_layer(&mut self, layer: Vec<&PackedInstruction>){ - for instruction in layer{ - let components = { - let enclosed = self.from_instruction(instruction); - enclosed.component_dict() - }; - println!("components: {:?}", components); - for (idx, component) in components{ - self.q_wires[idx as usize].add_wire_component(&component); - } - } - // normalise all wire widths - // self.fix_len(); + match indices.minmax() { + MinMaxResult::MinMax(min, max) => (min, max), + MinMaxResult::OneElement(idx) => (idx, idx), + MinMaxResult::NoElements => panic!("Encountered an instruction without qubits and clbits") } +} - pub fn build_layers(&mut self) -> Vec> { - let layer_iterator = self.dag_circ.multigraph_layers(); - - let mut final_layers:Vec> = Vec::new(); - - for (i,layer) in layer_iterator.enumerate(){ - let mut sublayers: Vec> = vec![Vec::new()]; - let mut set: HashSet = hashbrown::HashSet::new(); - - for node_index in layer { - if let NodeType::Operation(instruction_to_insert) = &self.dag_circ.dag()[node_index]{ - - if sublayers.is_empty() { - sublayers.push(vec![node_index]); - continue; - } - - if self.dag_circ.qargs_interner().get(instruction_to_insert.qubits).is_empty() && - self.dag_circ.cargs_interner().get(instruction_to_insert.clbits).is_empty() { - continue; - }; - - let (node_min,node_max) = self.get_instruction_range(instruction_to_insert); - - let mut sublayer = sublayers.last_mut().unwrap(); - let mut overlap = false; - - if set.contains(&node_min) || set.contains(&node_max){ - overlap = true; - set = hashbrown::HashSet::new(); - } else { - for i in (node_min..node_max){ - set.insert(i); - } - } +/// Return a list of PackedInstruction layers such that each layer contain instruction +/// whose qubits/clbits indices do not overlap. The instructions are packed into each layer +/// as long as there is no qubit/clbit overlap +fn build_layers(dag: &DAGCircuit) -> Vec> { + let mut layers:Vec> = Vec::new(); + let mut current_layer: Option<&mut Vec::<&PackedInstruction>> = None; + let mut used_wires = HashSet::::new(); - // for &subnode in sublayer.iter() { - // if let NodeType::Operation(instruction) = &self.dag_circ.dag()[subnode]{ - - // let (subnode_min,subnode_max) = self.get_instruction_range(instruction); + for layer in dag.multigraph_layers() { + for node_index in layer.into_iter().sorted() { - // if (subnode_min <= node_min && subnode_max >= node_min) || (subnode_min <= node_max && subnode_max >= node_max) { - // overlap = true; - // break; - // } - // } - // } + if let NodeType::Operation(instruction_to_insert) = &dag.dag()[node_index] { + let (node_min, node_max) = get_instruction_range(dag, instruction_to_insert); - if overlap { - sublayers.push(vec![node_index]); - } else { - sublayer.push(node_index); - } + // Check for instruction range overlap + if (node_min..=node_max).any(|idx| used_wires.contains(&idx)) { + current_layer = None; // Indication for starting a new layer + used_wires.clear(); } - } - - let mut ct = 0; - for j in sublayers { - if j.is_empty() { - continue; - } else { - final_layers.push(j); - ct += 1; - } - } - } - + used_wires.extend(node_min..=node_max); - let mut packedin_layers: Vec> = Vec::new(); + if current_layer.is_none() { + layers.push(Vec::new()); + current_layer = layers.last_mut(); + } - for (id,layer) in final_layers.iter().enumerate() { - let mut packedin_layer: Vec<&PackedInstruction> = Vec::new(); - for nodeind in layer { - if let NodeType::Operation(instruction) = &self.dag_circ.dag()[*nodeind] { - packedin_layer.push(&instruction); - } + current_layer.as_mut().unwrap().push(instruction_to_insert); } - packedin_layers.push(packedin_layer); - // self.build_layer(packedin_layer); - // println!("Layer {}", id); } - - for (i,packedin_layer )in packedin_layers.iter().enumerate() { - println!("layer:{}",i); - for &inst in packedin_layer.iter(){ - println!("{:?}", inst.op.name()); - } - } - - packedin_layers } -} + layers +} -pub fn circuit_draw(dag_circ: &DAGCircuit) { +pub fn draw_circuit(circuit: &CircuitData) -> PyResult<()> { + let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; - let mut output = String::new(); + // let mut circuit_rep = CircuitRep::new(dag.clone()); + let packedinst_layers = build_layers(&dag); + for (i, layer) in packedinst_layers.iter().enumerate() { + println!("==== LAYER {} ====", i); + for inst in layer { + println!("{:?}", inst.op.name()); + } + } - // Create a circuit representation - let mut circuit_rep = CircuitRep::new(dag_circ.clone()); - circuit_rep.set_qubit_name(); - // circuit_rep.build_layers(); - // output.push_str(&circuit_rep.circuit_string()); + // circuit_rep2.set_qubit_name(); + // let vis_mat:VisualizationMatrix = VisualizationMatrix::new(&circuit_rep, packedinst_layers); - //print using visualisation matrix - let packedinst_layers = circuit_rep.build_layers(); - let mut circuit_rep2 = CircuitRep::new(dag_circ.clone()); - circuit_rep2.set_qubit_name(); - let vis_mat:VisualizationMatrix = VisualizationMatrix::new(&circuit_rep2, packedinst_layers); - println!("======================"); - vis_mat.print(); - - - //println!("{}", output); + // vis_mat.print(); + Ok(()) } - - - From f772f6a8b8a5d6fb57cb864e8aaa75cc820f396e Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Tue, 30 Sep 2025 13:26:03 +0530 Subject: [PATCH 19/70] drawing logic for box and multibox, renamed Enclosed to Drawable --- crates/circuit/src/circuit_drawer.rs | 421 +++++++++++++++------------ 1 file changed, 237 insertions(+), 184 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index bb24944c7b97..c31108a26149 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -47,7 +47,14 @@ pub const BOT_LEFT_CON: char = '└'; pub const BOT_RIGHT_CON: char = '┘'; pub const BARRIER_CHAR: char = '░'; pub const BULLET:char = '■'; +pub const CONNECTING_WIRE:char = '│'; +pub const CROSSED_WIRE:char = '┼'; +#[derive(Clone, Debug, Copy)] +pub struct CircuitRep { + q_wires: Vec::, + dag_circ: DAGCircuit +} #[derive(Clone, Debug, PartialEq, Eq)] enum WireType { @@ -83,7 +90,7 @@ pub struct wire { } #[derive(Clone, Debug)] -pub struct Enclosed<'a>{ +pub struct Drawable<'a>{ packedinst: &'a PackedInstruction, dag_circ: &'a DAGCircuit, wire: WireType, @@ -91,28 +98,37 @@ pub struct Enclosed<'a>{ label: Option, visual_type: VisualType, } +// pub enum DrawElementType<'a>{ +// Controlled(&'a PackedInstruction), +// Multi(&'a PackedInstruction), +// Single(&'a PackedInstruction), +// Custom(&'a Drawable<'a>) +// } -struct Visualizer<'a>{ - visualization_matrix: VisualizationMatrix<'a>, - circuit_rep: CircuitRep -} +// struct Visualizer<'a>{ +// visualization_matrix: VisualizationMatrix<'a>, +// circuit_rep: CircuitRep +// } +#[derive(Clone, Debug)] struct VisualizationMatrix<'a>{ visualization_layers: Vec>, packedinst_layers: Vec>, - circuit_rep: &'a CircuitRep + circuit_rep: CircuitRep } +#[derive(Clone, Debug)] struct VisualizationLayer<'a>{ - elements: Vec>, + elements: Vec>>, + drawables: Vec>, width: u32, - circuit_rep: &'a CircuitRep + // circuit_rep: &'a CircuitRep + //parent: &'a VisualizationMatrix<'a> } #[derive(Clone, Debug)] pub struct VisualizationElement<'a>{ - element: Enclosed<'a>, - ascii_string: String, + layer_element_index: usize, ind: u32, circuit_rep: &'a CircuitRep } @@ -126,17 +142,17 @@ pub struct VisualizationElement<'a>{ impl<'a> VisualizationMatrix<'a>{ - pub fn new(circuit_rep: &'a CircuitRep, packedinst_layers: Vec>) -> Self{ - let mut visualization_layers: Vec = Vec::with_capacity(packedinst_layers.len()); - for layer in packedinst_layers.iter(){ - let vis_layer = VisualizationLayer::new(circuit_rep, layer.to_vec()); - visualization_layers.push(vis_layer); + pub fn new(circuit_rep: CircuitRep, packedinst_layers: Vec>) -> Self { + let mut temp: Vec = Vec::with_capacity(packedinst_layers.len()); + for layer in packedinst_layers.iter() { + let vis_layer = VisualizationLayer::new(&circuit_rep, layer.to_vec()); + temp.push(vis_layer); } - VisualizationMatrix{ - visualization_layers, + VisualizationMatrix { + visualization_layers: temp, packedinst_layers, - circuit_rep + circuit_rep, // Now we own this, so no borrowing issues } } @@ -157,65 +173,89 @@ impl<'a> VisualizationMatrix<'a>{ println!("{}", i); } } + + // add wires to the circuit representation and return the full circuit representation + pub fn draw(&mut self){ + for layer in &self.visualization_layers{ + let wires = layer.draw_layer(&self.circuit_rep); + for (i, wire) in wires.iter().enumerate(){ + self.circuit_rep.q_wires[i].add_wire_component(wire); + } + } + + // self.circuit_rep.fix_len(); + println!("{}", self.circuit_rep.circuit_string()); + } } impl<'a> VisualizationLayer<'a>{ pub fn new(circuit_rep: &'a CircuitRep, packedinst_layer: Vec<&'a PackedInstruction>) -> Self{ - let dummy_element: VisualizationElement = VisualizationElement{ - element: Enclosed{ - packedinst: &packedinst_layer[0], - dag_circ: &circuit_rep.dag_circ, - wire: WireType::Qubit, - name: String::new(), - label: None, - visual_type: VisualType::Boxed - }, - ascii_string: String::new(), - ind: 0, - circuit_rep - }; - - //println!("{}",packedinst_layer.len()); - let mut vis_layer:Vec = vec![dummy_element;circuit_rep.get_indices() as usize]; - let mut enclosed_elements = { - let mut enclosed_layer:Vec = Vec::new(); + let mut vis_layer:Vec> = vec![None;circuit_rep.get_indices() as usize]; + let mut drawable_elements = { + let mut drawable_layer:Vec = Vec::new(); + let mut drawable_ind:usize = 0; for &inst in packedinst_layer.iter(){ - let enclosed = circuit_rep.from_instruction(inst); - let indices = enclosed.get_wire_indices(); - // println!("enclosed indices: {:?}", indices); + let drawable = circuit_rep.from_instruction(inst); + let indices = drawable.get_wire_indices(); + // println!("drawable indices: {:?}", indices); // println!("{:?}", vis_layer); for ind in indices{ - let vis_element = VisualizationElement::new(enclosed.clone(), circuit_rep, ind); - vis_layer[ind as usize] = vis_element; + let vis_element = VisualizationElement::new(drawable_ind, &circuit_rep, ind); + vis_layer[ind as usize] = Option::Some(vis_element); } + drawable_ind += 1; } - enclosed_layer + drawable_layer }; let mut visualization_layer = VisualizationLayer{ elements: vis_layer, - width: 0, - circuit_rep + drawables: drawable_elements, + width: 0 }; visualization_layer.width = visualization_layer.set_width(); visualization_layer } - pub fn get_enclosed(&self, inst: &'a PackedInstruction) -> Enclosed{ - self.circuit_rep.from_instruction(inst) + pub fn draw_layer(&self, circuit_rep: &'a CircuitRep) -> Vec{ + let mut wires: Vec = Vec::new(); + let mut ct: usize = 0; + for element in self.elements.iter(){ + + if let Some(vis_element) = element{ + let wire_component = vis_element.draw(&self); + wires.push(wire_component); + } else { + // if index greater than number of qubits then wiretype is clbit + if ct >= circuit_rep.dag_circ.num_qubits() as usize{ + let wire_component = wire::new(WireType::Clbit); + wires.push(wire_component); + } else { + let wire_component = wire::new(WireType::Qubit); + wires.push(wire_component); + } + } + } + wires } pub fn set_width(&mut self) -> u32{ let mut max_width:u32 = 0; for element in self.elements.iter(){ - let temp = element.get_length(); - if temp > max_width { - max_width = temp; + if let Some(vis_element) = element{ + let length = vis_element.get_length(&self); + if length > max_width{ + max_width = length; + } + } else { + if 1 > max_width{ + max_width = 1; + } } } @@ -225,51 +265,56 @@ impl<'a> VisualizationLayer<'a>{ pub fn get_layer_col(&self) -> Vec{ let mut layer_col: Vec = Vec::new(); for element in &self.elements{ - layer_col.push(format!("{}{}",element.get_string(), " ".repeat((self.width - element.get_length()) as usize))); + if let Some(vis_element) = element{ + layer_col.push(format!("{}{}",vis_element.get_string(&self), " ".repeat((self.width - vis_element.get_length(&self)) as usize))); + } else { + layer_col.push(" ".repeat(self.width as usize)); + } } layer_col } } - - impl<'a> VisualizationElement<'a>{ - pub fn new(element: Enclosed<'a>, circuit_rep: &'a CircuitRep,ind: u32) -> Self{ - let mut vis_element = VisualizationElement{ - element, - ascii_string: String::new(), + pub fn new(layer_element_index: usize, circuit_rep: &'a CircuitRep,ind: u32) -> Self{ + let vis_element = VisualizationElement{ + layer_element_index, ind, circuit_rep }; - vis_element.ascii_string = vis_element.set_string(); return vis_element; } - pub fn get_string(&self) -> String{ - self.ascii_string.clone() - } + // pub fn get_string(&self, layer:&VisualizationLayer) -> String{ + // layer.drawables[self.layer_element_index].get_name() + // } - pub fn get_length(&self) -> u32{ - self.ascii_string.len() as u32 + pub fn get_length(&self, layer:&VisualizationLayer) -> u32{ + self.get_string(layer).len() as u32 } - pub fn set_string(&mut self) -> String{ - let enclosed = &self.element; + pub fn get_string(&self, layer:&VisualizationLayer) -> String{ + let drawable = &layer.drawables[self.layer_element_index]; let mut ret: String = " ".to_string(); - let indices = enclosed.get_wire_indices(); + let indices = drawable.get_wire_indices(); if self.ind == indices[0] { - ret = enclosed.get_name(); + ret = drawable.get_name(); }else if indices.contains(&self.ind) { ret = format!("{}({})", BULLET,indices[0]); }else { - ret = <&str>::from(&enclosed.wire).to_string(); + ret = <&str>::from(&drawable.wire).to_string(); } format!("{}",ret) } + + pub fn draw(&self, layer:&VisualizationLayer) -> wire{ + let drawable = &layer.drawables[self.layer_element_index]; + drawable.draw(self.ind) + } } impl wire { @@ -348,10 +393,6 @@ pub trait DrawElement{ 1 } - fn component_dict(&self) -> HashMap{ - HashMap::::new() - } - } pub trait DirectOnWire{ @@ -361,14 +402,15 @@ pub trait DirectOnWire{ #[derive(Clone, Debug, PartialEq)] enum VisualType{ Boxed, + MultiBoxed, BetweenWire(String), DirectOnWire(String), WholeWire } -impl<'a> Enclosed<'a> { - pub fn new(packedinst: &'a PackedInstruction, dag_circ: &'a DAGCircuit ,wire: WireType, name: String, label: Option, visual_type: VisualType) -> Enclosed<'a>{ - Enclosed { +impl<'a> Drawable<'a> { + pub fn new(packedinst: &'a PackedInstruction, dag_circ: &'a DAGCircuit ,wire: WireType, name: String, label: Option, visual_type: VisualType) -> Drawable<'a>{ + Drawable { packedinst, dag_circ, wire, @@ -378,8 +420,108 @@ impl<'a> Enclosed<'a> { } } + pub fn draw(&self, ind: u32) -> wire{ + let mut wire_component = wire::new(self.wire.clone()); + let indices = self.get_wire_indices(); + let indices_min = indices.iter().min().unwrap().to_owned(); + let indices_max = indices.iter().max().unwrap().to_owned(); + if indices.contains(&ind){ + match &self.visual_type { + VisualType::Boxed => { + if &ind == &indices[0] { + if let Some(label) = &self.label { + let box_len = label.len() + 2; // for the box edges + wire_component.top.push_str(&format!("{}{}{}", TOP_LEFT_CON, Q_WIRE.repeat(box_len), TOP_RIGHT_CON)); + wire_component.mid.push_str(&format!("{}{}{}", LEFT_CON, label, RIGHT_CON)); + wire_component.bot.push_str(&format!("{}{}{}", BOT_LEFT_CON, Q_WIRE.repeat(box_len), BOT_RIGHT_CON)); + } else { + panic!("Boxed visual has no label"); + } + } else if ind == indices_max { + wire_component.top.push_str(&CONNECTING_WIRE.to_string()); + wire_component.mid.push_str(&BULLET.to_string()); + wire_component.bot.push_str(" "); + } + else if ind == indices_min { + wire_component.top.push_str(" "); + wire_component.mid.push_str(&BULLET.to_string()); + wire_component.bot.push_str(&CONNECTING_WIRE.to_string()); + } else { + wire_component.top.push_str(&CONNECTING_WIRE.to_string()); + wire_component.mid.push_str(&CROSSED_WIRE.to_string()); + wire_component.bot.push_str(&CONNECTING_WIRE.to_string()); + } + } + VisualType::MultiBoxed => { + let mid = (indices_min + indices_max) / 2; + let index_in_array = indices.iter().position(|&r| r == ind); + let index = match index_in_array { + Some(i) => i.to_string(), + None => " ".to_string(), + }; + if let Some(label) = &self.label{ + let box_len = label.len(); // for the box edges + if ind == indices_min { + wire_component.top.push_str(&format!("{} {}{}", TOP_LEFT_CON," ".repeat(box_len) , TOP_RIGHT_CON)); + wire_component.mid.push_str(&format!("{}{}{}{}", LEFT_CON,index,label, RIGHT_CON)); + wire_component.bot.push_str(&format!("{} {}{}", CONNECTING_WIRE, " ".repeat(box_len), CONNECTING_WIRE)); + } else if ind == indices_max { + wire_component.top.push_str(&format!("{} {}{}", CONNECTING_WIRE," ".repeat(box_len) ,CONNECTING_WIRE)); + wire_component.mid.push_str(&format!("{}{}{}{}", LEFT_CON, index ," ".repeat(box_len), RIGHT_CON)); + wire_component.bot.push_str(&format!("{} {}{}", BOT_LEFT_CON, " ".repeat(box_len), BOT_RIGHT_CON)); + } else if ind == mid{ + wire_component.top.push_str(&format!("{} {}{}", CONNECTING_WIRE," ".repeat(box_len) , CONNECTING_WIRE)); + wire_component.mid.push_str(&format!("{}{}{}{}", LEFT_CON, index, label, RIGHT_CON)); + wire_component.bot.push_str(&format!("{} {}{}",CONNECTING_WIRE, " ".repeat(box_len), CONNECTING_WIRE)); + } else { + wire_component.top.push_str(&format!("{} {}{}", CONNECTING_WIRE," ".repeat(box_len) , CONNECTING_WIRE)); + wire_component.mid.push_str(&format!("{}{}{}{}", LEFT_CON, index," ".repeat(box_len), RIGHT_CON)); + wire_component.bot.push_str(&format!("{} {}{}",CONNECTING_WIRE, " ".repeat(box_len), CONNECTING_WIRE)); + } + } else { + panic!("Boxed visual has no label"); + } + } + VisualType::BetweenWire(label) => { + if let Some(inst_label) = &self.label{ + if ind == indices_min { + + } else if ind == indices_max { + + } else if indices.contains(&ind){ + } else { + } + } else { + panic!("BetweenWire visual has no label"); + } + } + VisualType::DirectOnWire(label) => { + if ind == indices_min { + + } else if ind == indices_max { + + } else if indices.contains(&ind){ + + } else { + + } + } + VisualType::WholeWire => { + + } + } + wire_component.update_wire_len(); + wire_component + } else { + panic!("Draw being called on an index which is not affected by the PackedInstruction"); + } + } + // this function returns the indices of wires than an instruction is acting upon. + // Since rust currently only has StandardInstruction and StandardGates, it always returns + // the qubit indices first, where in usually the first index is of the control qubit and the rest are of + // the target. However once ControlFlows are introduced, this handling needs to be more nuanced. pub fn get_wire_indices(&self) -> Vec{ let mut qubit_indices: Vec = self.dag_circ.qargs_interner().get(self.packedinst.qubits) .iter() @@ -398,106 +540,22 @@ impl<'a> Enclosed<'a> { qubit_indices } - pub fn wire_component(&self, ind: u32) -> wire{ - let mut component = wire::new(self.wire.clone()); - let wire_indices = self.get_wire_indices(); - let min_index = *wire_indices.iter().min().unwrap(); - let max_index = *wire_indices.iter().max().unwrap(); - - // barrier handling - if self.visual_type == VisualType::DirectOnWire(BARRIER_CHAR.to_string()) { - let mut label = BARRIER_CHAR.to_string(); - if let Some(l) = &self.label{ - label = l.to_string(); - } else { - label = BARRIER_CHAR.to_string(); - } - let label_len:usize = label.len(); - - let mut left_pad_len = 0; - let mut right_pad_len = 0; - if label_len == 0 { - left_pad_len = 0; - right_pad_len = 0; - } - else if label_len % 2 == 0{ - left_pad_len = (label_len / 2) - 1; - right_pad_len = label_len / 2; - } - else { - left_pad_len = label_len / 2; - right_pad_len = label_len / 2; - } - let component_wire = format!("{}{}{}", - <&str>::from(&self.wire).repeat(left_pad_len), - BARRIER_CHAR, - <&str>::from(&self.wire).repeat(right_pad_len) - ); - - let between_wire = format!("{}{}{}", - " ".repeat(left_pad_len), - BARRIER_CHAR, - " ".repeat(right_pad_len) - ); - - if ind == 0 { - component.top.push_str(&label[..]); - } else { - component.top.push_str(&component_wire); - } - component.mid.push_str(&between_wire); - component.bot.push_str( &between_wire); - return component; - } - - match &self.visual_type { - VisualType::Boxed => { - if let Some(label) = &self.label { - let label_len = label.len() as usize; - component.top.push_str(&format!("{}{}{}", TOP_LEFT_CON, "─".repeat(label_len as usize), TOP_RIGHT_CON)); - component.mid.push_str(&format!("{}{}{}", LEFT_CON, label, RIGHT_CON)); - component.bot.push_str(&format!("{}{}{}", BOT_LEFT_CON, "─".repeat(label_len as usize), BOT_RIGHT_CON)); - } else { - panic!("Boxed visual has no label"); - } - } - VisualType::BetweenWire(label) => { - let label_len = label.len() as u64; - component.top.push_str(&format!("{}{}{}", TOP_CON, "─".repeat(label_len as usize), TOP_CON)); - component.mid.push_str(&format!(" {} ", label)); - component.bot.push_str(&format!("{}{}{}", BOT_CON, "─".repeat(label_len as usize), BOT_CON)); - } - VisualType::DirectOnWire(label) => { - component.top.push_str(" ".repeat(label.len()).as_str()); - component.mid.push_str(label); - component.bot.push_str(" ".repeat(label.len()).as_str()); - } - VisualType::WholeWire => { - let wire_len = 1; // single character for whole wire - component.top.push_str("░".repeat(wire_len).as_str()); - component.mid.push_str("░".repeat(wire_len).as_str()); - component.bot.push_str("░. ".repeat(wire_len).as_str()); - } - } - component.update_wire_len(); - component - } - pub fn get_name(&self) -> String{ self.name.clone() } } -impl<'a> DrawElement for Enclosed<'a>{ +impl<'a> DrawElement for Drawable<'a>{ fn get_width(&self) -> u64 { let type_ = match &self.visual_type { - VisualType::Boxed => { + VisualType::Boxed | VisualType::MultiBoxed => { if let Some(label) = &self.label { label.len() as u64 + 2 // for the box edges } else { panic!("Boxed visual has no label"); } } + VisualType::BetweenWire(label) => { label.len() as u64 + 1 // for the spaces around the label } @@ -512,26 +570,12 @@ impl<'a> DrawElement for Enclosed<'a>{ return type_; } - fn component_dict(&self) -> HashMap{ - // map of qubit index to wire component - let mut components = HashMap::::new(); - let indices = self.get_wire_indices(); - // for each qubit in packedinstruction, call wire component - for idx in indices{ - components.insert(idx, self.wire_component(idx)); - } - components - } } -#[derive(Clone, Debug)] -pub struct CircuitRep { - q_wires: Vec::, - dag_circ: DAGCircuit -} + impl<'a> CircuitRep { - pub fn from_instruction(&'a self, instruction: &'a PackedInstruction) -> Enclosed<'a>{ + pub fn from_instruction(&'a self, instruction: &'a PackedInstruction) -> Drawable<'a>{ // println!("{:?}", instruction.label()); if let Some(standard_gate) = instruction.op.try_standard_gate() { let instruction_name = instruction.op.name(); @@ -539,7 +583,6 @@ impl<'a> CircuitRep { let mut instruction_label: Option = match standard_gate { StandardGate::GlobalPhase => { // Global phase gate - affects overall circuit phase - None } StandardGate::H => { @@ -767,7 +810,9 @@ impl<'a> CircuitRep { _ => VisualType::Boxed, }; - Enclosed { + // for label debugging + println!("{}{}",instruction_label.clone().unwrap_or("".to_string()), instruction_name); + Drawable { packedinst: &instruction, wire: WireType::Qubit, dag_circ: &self.dag_circ, @@ -779,7 +824,7 @@ impl<'a> CircuitRep { else if let Some(standard_instruction) = instruction.op.try_standard_instruction() { match standard_instruction { StandardInstruction::Measure =>{ - Enclosed{ + Drawable{ packedinst: &instruction, wire: WireType::Qubit, dag_circ: &self.dag_circ, @@ -799,7 +844,7 @@ impl<'a> CircuitRep { println!("barrier label: {}", inst_label); - Enclosed{ + Drawable{ packedinst: &instruction, wire: WireType::Qubit, dag_circ: &self.dag_circ, @@ -809,7 +854,7 @@ impl<'a> CircuitRep { } }, StandardInstruction::Reset => { - Enclosed{ + Drawable{ packedinst: &instruction, wire: WireType::Qubit, dag_circ: &self.dag_circ, @@ -828,7 +873,7 @@ impl<'a> CircuitRep { "Delay".to_string() }; - Enclosed{ + Drawable{ packedinst: &instruction, wire: WireType::Qubit, dag_circ: &self.dag_circ, @@ -965,7 +1010,7 @@ fn build_layers(dag: &DAGCircuit) -> Vec> { pub fn draw_circuit(circuit: &CircuitData) -> PyResult<()> { let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; - // let mut circuit_rep = CircuitRep::new(dag.clone()); + let packedinst_layers = build_layers(&dag); for (i, layer) in packedinst_layers.iter().enumerate() { println!("==== LAYER {} ====", i); @@ -978,6 +1023,14 @@ pub fn draw_circuit(circuit: &CircuitData) -> PyResult<()> { // let vis_mat:VisualizationMatrix = VisualizationMatrix::new(&circuit_rep, packedinst_layers); println!("======================"); + + //using circuit rep to draw circuit + let mut circuit_rep = CircuitRep::new(dag.clone()); + let vis_mat2:VisualizationMatrix = VisualizationMatrix::new(circuit_rep, packedinst_layers); + vis_mat2.print(); + println!("{}", circuit_rep.circuit_string()); + println!("======================"); + // vis_mat.print(); Ok(()) } From c39f6937b17d3a62aa709a776f31170e3aebd374 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Mon, 6 Oct 2025 12:43:46 +0530 Subject: [PATCH 20/70] printing logic complete, normalisation left --- crates/circuit/src/circuit_drawer.rs | 779 +++++++++++++++------------ 1 file changed, 421 insertions(+), 358 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index c31108a26149..438151133bd8 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -50,7 +50,7 @@ pub const BULLET:char = '■'; pub const CONNECTING_WIRE:char = '│'; pub const CROSSED_WIRE:char = '┼'; -#[derive(Clone, Debug, Copy)] +#[derive(Clone, Debug)] pub struct CircuitRep { q_wires: Vec::, dag_circ: DAGCircuit @@ -114,7 +114,7 @@ pub struct Drawable<'a>{ struct VisualizationMatrix<'a>{ visualization_layers: Vec>, packedinst_layers: Vec>, - circuit_rep: CircuitRep + dag_circ: &'a DAGCircuit } #[derive(Clone, Debug)] @@ -130,7 +130,7 @@ struct VisualizationLayer<'a>{ pub struct VisualizationElement<'a>{ layer_element_index: usize, ind: u32, - circuit_rep: &'a CircuitRep + dag_circ: &'a DAGCircuit } // pub struct VisualizationElement<'a>{ @@ -142,21 +142,24 @@ pub struct VisualizationElement<'a>{ impl<'a> VisualizationMatrix<'a>{ - pub fn new(circuit_rep: CircuitRep, packedinst_layers: Vec>) -> Self { + pub fn new(dag_circ: &'a DAGCircuit, packedinst_layers: Vec>) -> Self { + println!("Creating VisualizationMatrix..."); let mut temp: Vec = Vec::with_capacity(packedinst_layers.len()); for layer in packedinst_layers.iter() { - let vis_layer = VisualizationLayer::new(&circuit_rep, layer.to_vec()); + let vis_layer = VisualizationLayer::new(dag_circ, layer.to_vec()); temp.push(vis_layer); } + println!("Visualization layers created: {:?}", temp); VisualizationMatrix { visualization_layers: temp, packedinst_layers, - circuit_rep, // Now we own this, so no borrowing issues + dag_circ, // Now we own this, so no borrowing issues } } pub fn print(&self){ + println!("Printing VisualizationMatrix..."); let mut output: Vec = Vec::new(); for layer in &self.visualization_layers{ let layer_col = layer.get_layer_col(); @@ -175,42 +178,47 @@ impl<'a> VisualizationMatrix<'a>{ } // add wires to the circuit representation and return the full circuit representation - pub fn draw(&mut self){ + pub fn draw(&self, circuit_rep: &mut CircuitRep){ + println!("Drawing VisualizationMatrix..."); for layer in &self.visualization_layers{ - let wires = layer.draw_layer(&self.circuit_rep); + let wires = layer.draw_layer(circuit_rep); for (i, wire) in wires.iter().enumerate(){ - self.circuit_rep.q_wires[i].add_wire_component(wire); + circuit_rep.q_wires[i].add_wire_component(wire); } } // self.circuit_rep.fix_len(); - println!("{}", self.circuit_rep.circuit_string()); + println!("{}", circuit_rep.circuit_string()); } } impl<'a> VisualizationLayer<'a>{ - pub fn new(circuit_rep: &'a CircuitRep, packedinst_layer: Vec<&'a PackedInstruction>) -> Self{ - + pub fn new(dag_circ: &'a DAGCircuit, packedinst_layer: Vec<&'a PackedInstruction>) -> Self{ + println!("Creating VisualizationLayer..."); //println!("{}",packedinst_layer.len()); - let mut vis_layer:Vec> = vec![None;circuit_rep.get_indices() as usize]; + let mut vis_layer:Vec> = vec![None;get_indices(dag_circ) as usize]; let mut drawable_elements = { let mut drawable_layer:Vec = Vec::new(); let mut drawable_ind:usize = 0; for &inst in packedinst_layer.iter(){ - let drawable = circuit_rep.from_instruction(inst); + let drawable = from_instruction(dag_circ,inst); let indices = drawable.get_wire_indices(); // println!("drawable indices: {:?}", indices); // println!("{:?}", vis_layer); for ind in indices{ - let vis_element = VisualizationElement::new(drawable_ind, &circuit_rep, ind); + let vis_element = VisualizationElement::new(drawable_ind, dag_circ, ind); vis_layer[ind as usize] = Option::Some(vis_element); } drawable_ind += 1; + drawable_layer.push(drawable); } drawable_layer }; + println!("Drawable elements: {:?}", drawable_elements); + // println!("Vis layer: {:?}", vis_layer); + let mut visualization_layer = VisualizationLayer{ elements: vis_layer, drawables: drawable_elements, @@ -222,6 +230,7 @@ impl<'a> VisualizationLayer<'a>{ } pub fn draw_layer(&self, circuit_rep: &'a CircuitRep) -> Vec{ + println!("Drawing VisualizationLayer..."); let mut wires: Vec = Vec::new(); let mut ct: usize = 0; for element in self.elements.iter(){ @@ -277,11 +286,11 @@ impl<'a> VisualizationLayer<'a>{ impl<'a> VisualizationElement<'a>{ - pub fn new(layer_element_index: usize, circuit_rep: &'a CircuitRep,ind: u32) -> Self{ + pub fn new(layer_element_index: usize, dag_circ: &'a DAGCircuit,ind: u32) -> Self{ let vis_element = VisualizationElement{ layer_element_index, ind, - circuit_rep + dag_circ }; return vis_element; } @@ -395,9 +404,9 @@ pub trait DrawElement{ } -pub trait DirectOnWire{ +// pub trait DirectOnWire{ -} +// } #[derive(Clone, Debug, PartialEq)] enum VisualType{ @@ -405,7 +414,7 @@ enum VisualType{ MultiBoxed, BetweenWire(String), DirectOnWire(String), - WholeWire + AllWires(String) } impl<'a> Drawable<'a> { @@ -485,31 +494,63 @@ impl<'a> Drawable<'a> { VisualType::BetweenWire(label) => { if let Some(inst_label) = &self.label{ if ind == indices_min { - + wire_component.top.push_str(&format!("{}{}", " "," ".repeat(inst_label.len()))); + wire_component.mid.push_str(&format!("{}{}",label,<&str>::from(&self.wire))); + wire_component.bot.push_str(&format!("{}{}", CONNECTING_WIRE, inst_label)); } else if ind == indices_max { - + wire_component.top.push_str(&format!("{}{}", CONNECTING_WIRE," ".repeat(inst_label.len()))); + wire_component.mid.push_str(&format!("{}{}", label, <&str>::from(&self.wire))); + wire_component.bot.push_str(&format!("{}{}", " " ," ".repeat(inst_label.len()))); } else if indices.contains(&ind){ - + wire_component.top.push_str(&format!("{}{}", CONNECTING_WIRE," ".repeat(inst_label.len()))); + wire_component.mid.push_str(&format!("{}{}", label, <&str>::from(&self.wire))); + wire_component.bot.push_str(&format!("{}{}", CONNECTING_WIRE, " ".repeat(inst_label.len()))); } else { - + wire_component.top.push_str(&format!("{}{}", CONNECTING_WIRE," ".repeat(inst_label.len()))); + wire_component.mid.push_str(&format!("{}{}", CROSSED_WIRE, " ".repeat(inst_label.len()))); + wire_component.bot.push_str(&format!("{}{}", CONNECTING_WIRE ," ".repeat(inst_label.len()))); } } else { - panic!("BetweenWire visual has no label"); + panic!("BetweenWire visual has no label"); } } VisualType::DirectOnWire(label) => { + let inst_label = match &self.label { + Some(l) => l, + None => label, + }; if ind == indices_min { - + wire_component.top.push_str(&format!("{}{}", " "," ".repeat(inst_label.len()))); + wire_component.mid.push_str(&format!("{}{}",label,<&str>::from(&self.wire).repeat(inst_label.len()))); + wire_component.bot.push_str(&format!("{}{}", CONNECTING_WIRE, inst_label)); } else if ind == indices_max { - + wire_component.top.push_str(&format!("{}{}", CONNECTING_WIRE," ".repeat(inst_label.len()))); + wire_component.mid.push_str(&format!("{}{}", label, <&str>::from(&self.wire).repeat(inst_label.len()))); + wire_component.bot.push_str(&format!("{}{}", " " ," ".repeat(inst_label.len()))); } else if indices.contains(&ind){ - + wire_component.top.push_str(&format!("{}{}", CONNECTING_WIRE," ".repeat(inst_label.len()))); + wire_component.mid.push_str(&format!("{}{}", label, <&str>::from(&self.wire).repeat(inst_label.len()))); + wire_component.bot.push_str(&format!("{}{}", CONNECTING_WIRE, " ".repeat(inst_label.len()))); } else { - + wire_component.top.push_str(&format!("{}{}", CONNECTING_WIRE," ".repeat(inst_label.len()))); + wire_component.mid.push_str(&format!("{}{}", CROSSED_WIRE, " ".repeat(inst_label.len()))); + wire_component.bot.push_str(&format!("{}{}", CONNECTING_WIRE ," ".repeat(inst_label.len()))); } } - VisualType::WholeWire => { - + VisualType::AllWires(label) => { + let mut inst_label = match &self.label { + Some(l) => l, + None => label, + }; + if ind == 0 { + wire_component.top.push_str(&format!("{}{}", inst_label," ".to_string())); + wire_component.mid.push_str(&format!("{}{}",label,<&str>::from(&self.wire).repeat(inst_label.len()))); + wire_component.bot.push_str(&format!("{}{}", label, " ".to_string().repeat(inst_label.len()))); + } else if indices.contains(&ind) { + wire_component.top.push_str(&format!("{}{}", label," ".repeat(inst_label.len()))); + wire_component.mid.push_str(&format!("{}{}", label,<&str>::from(&self.wire).repeat(inst_label.len()))); + wire_component.bot.push_str(&format!("{}{}", label," ".repeat(inst_label.len()))); + } } } wire_component.update_wire_len(); @@ -562,7 +603,7 @@ impl<'a> DrawElement for Drawable<'a>{ VisualType::DirectOnWire(label) => { 1 as u64// no extra space needed } - VisualType::WholeWire => { + VisualType::AllWires(label) => { 0 as u64// just a single character for the whole wire } }; @@ -575,329 +616,6 @@ impl<'a> DrawElement for Drawable<'a>{ impl<'a> CircuitRep { - pub fn from_instruction(&'a self, instruction: &'a PackedInstruction) -> Drawable<'a>{ - // println!("{:?}", instruction.label()); - if let Some(standard_gate) = instruction.op.try_standard_gate() { - let instruction_name = instruction.op.name(); - let instruction_param =format!("{:?}",instruction.params_view()); - let mut instruction_label: Option = match standard_gate { - StandardGate::GlobalPhase => { - // Global phase gate - affects overall circuit phase - None - } - StandardGate::H => { - // Hadamard gate - creates superposition - Some("H".to_string()) - } - StandardGate::I => { - // Identity gate - no operation - Some("I".to_string()) - } - StandardGate::X => { - // Pauli-X gate (NOT gate) - Some("X".to_string()) - } - StandardGate::Y => { - // Pauli-Y gate - Some("Y".to_string()) - } - StandardGate::Z => { - // Pauli-Z gate - Some("Z".to_string()) - } - StandardGate::Phase => { - // Phase gate (parameterized) - Some(format!("P({})", instruction_param)) - } - StandardGate::R => { - // R gate (rotation about axis in XY plane) - Some(format!("R({})", instruction_param)) - } - StandardGate::RX => { - // Rotation about X axis - Some(format!("RX({})", instruction_param)) - } - StandardGate::RY => { - // Rotation about Y axis - Some(format!("RY({})", instruction_param)) - } - StandardGate::RZ => { - // Rotation about Z axis - Some(format!("RZ({})", instruction_param)) - } - StandardGate::S => { - // S gate (phase π/2) - Some("S".to_string()) - } - StandardGate::Sdg => { - // S dagger gate (phase -π/2) - Some("S†".to_string()) - } - StandardGate::SX => { - // Square root of X gate - Some("√X".to_string()) - } - StandardGate::SXdg => { - // Square root of X dagger gate - Some("√X†".to_string()) - } - StandardGate::T => { - // T gate (phase π/4) - Some("T".to_string()) - } - StandardGate::Tdg => { - // T dagger gate (phase -π/4) - Some("T†".to_string()) - } - StandardGate::U => { - // Universal single-qubit gate (3 parameters) - Some(format!("U({})", instruction_param)) - } - StandardGate::U1 => { - // U1 gate (1 parameter - phase) - Some(format!("U1({})", instruction_param)) - } - StandardGate::U2 => { - // U2 gate (2 parameters) - Some(format!("U2({})", instruction_param)) - } - StandardGate::U3 => { - // U3 gate (3 parameters - equivalent to U) - Some(format!("U3({})", instruction_param)) - } - StandardGate::CH => { - // Controlled Hadamard gate - Some("H".to_string()) - } - StandardGate::CX => { - // Controlled-X gate (CNOT) - Some("X".to_string()) - } - StandardGate::CY => { - // Controlled-Y gate - Some("Y".to_string()) - } - StandardGate::CZ => { - // Controlled-Z gate - Some("Z".to_string()) - } - StandardGate::DCX => { - // Double CNOT gate - Some("DCX".to_string()) - } - StandardGate::ECR => { - // Echoed cross-resonance gate - Some("ECR".to_string()) - } - StandardGate::Swap => { - // Swap gate - None - } - StandardGate::ISwap => { - // i-Swap gate - None - } - StandardGate::CPhase => { - // Controlled phase gate - - Some(format!("P({})", instruction_param)) - } - StandardGate::CRX => { - // Controlled rotation about X - Some(format!("RX({})", instruction_param)) - } - StandardGate::CRY => { - // Controlled rotation about Y - Some(format!("RY({})", instruction_param)) - } - StandardGate::CRZ => { - // Controlled rotation about Z - Some(format!("RZ({})", instruction_param)) - } - StandardGate::CS => { - // Controlled S gate - Some("S".to_string()) - } - StandardGate::CSdg => { - // Controlled S dagger gate - Some("S†".to_string()) - } - StandardGate::CSX => { - // Controlled square root of X gate - Some("√X".to_string()) - } - StandardGate::CU => { - // Controlled U gate (4 parameters) - Some(format!("U({})", instruction_param)) - } - StandardGate::CU1 => { - // Controlled U1 gate - Some(format!("U1({})", instruction_param)) - } - StandardGate::CU3 => { - // Controlled U3 gate - Some(format!("U3({})", instruction_param)) - } - StandardGate::RXX => { - // Two-qubit XX rotation - Some(format!("RXX({})", instruction_param)) - } - StandardGate::RYY => { - // Two-qubit YY rotation - Some(format!("RYY({})", instruction_param)) - } - StandardGate::RZZ => { - // Two-qubit ZZ rotation - Some(format!("RZZ({})", instruction_param)) - } - StandardGate::RZX => { - // Two-qubit ZX rotation - Some(format!("RZX({})", instruction_param)) - } - StandardGate::XXMinusYY => { - // XX-YY gate - Some(format!("XX-YY({})", instruction_param)) - } - StandardGate::XXPlusYY => { - // XX+YY gate - Some(format!("XX+YY({})", instruction_param)) - } - StandardGate::CCX => { - // Toffoli gate (controlled-controlled-X) - Some("X".to_string()) - } - StandardGate::CCZ => { - // Controlled-controlled-Z gate - Some("Z".to_string()) - } - StandardGate::CSwap => { - // Controlled swap gate (Fredkin gate) - None - } - StandardGate::RCCX => { - // Relative-phase Toffoli gate - Some(format!("RX({})", instruction_param)) - } - StandardGate::C3X => { - // 3-controlled X gate (4-qubit controlled X) - Some("X".to_string()) - } - StandardGate::C3SX => { - // 3-controlled square root of X gate - Some("√X".to_string()) - } - StandardGate::RC3X => { - // Relative-phase 3-controlled X gate - Some(format!("RX({})", instruction_param)) - } - }; - - let label = instruction.label(); - instruction_label = match label { - Some(l) => { - if l != "" { - Some(l.to_string()) - } else { - instruction_label - } - } - None => instruction_label, - }; - let visual_type = match standard_gate { - StandardGate::GlobalPhase => VisualType::BetweenWire(instruction.params_view().get(0).map_or("".to_string(), |p| format!("{:?}", p))), - StandardGate::Swap => VisualType::DirectOnWire("X".to_string()), - StandardGate::CPhase => VisualType::BetweenWire(instruction_label.clone().unwrap()), - _ => VisualType::Boxed, - }; - - // for label debugging - println!("{}{}",instruction_label.clone().unwrap_or("".to_string()), instruction_name); - Drawable { - packedinst: &instruction, - wire: WireType::Qubit, - dag_circ: &self.dag_circ, - name: instruction_name.to_string(), - label: instruction_label, - visual_type: visual_type, - } - } - else if let Some(standard_instruction) = instruction.op.try_standard_instruction() { - match standard_instruction { - StandardInstruction::Measure =>{ - Drawable{ - packedinst: &instruction, - wire: WireType::Qubit, - dag_circ: &self.dag_circ, - name: "Measure".to_string(), - label: Some("M".to_string()), - visual_type: VisualType::Boxed, - } - }, - StandardInstruction::Barrier(x) => { - - let label = instruction.label(); - let inst_label = match label{ - None => BARRIER_CHAR.to_string(), - Some("") => BARRIER_CHAR.to_string(), - Some(label) => label.to_string(), - }; - - println!("barrier label: {}", inst_label); - - Drawable{ - packedinst: &instruction, - wire: WireType::Qubit, - dag_circ: &self.dag_circ, - name: "Barrier".to_string(), - label: Some(inst_label.clone()), - visual_type: VisualType::DirectOnWire(BARRIER_CHAR.to_string()), - } - }, - StandardInstruction::Reset => { - Drawable{ - packedinst: &instruction, - wire: WireType::Qubit, - dag_circ: &self.dag_circ, - name: "Reset".to_string(), - label: Some("|0>".to_string()), - visual_type: VisualType::DirectOnWire("|0>".to_string()), - } - }, - StandardInstruction::Delay(_unit) => { - - let unit = format!("{:?}", _unit).to_lowercase(); - - let label = if let Some(dur) = instruction.params_view().get(0){ - format!("Delay({:?}[{:?}])", dur, unit) - } else { - "Delay".to_string() - }; - - Drawable{ - packedinst: &instruction, - wire: WireType::Qubit, - dag_circ: &self.dag_circ, - name: "Delay".to_string(), - label: Some(label.clone()), - visual_type: VisualType::Boxed, - } - } - } - } - else { - // print all operation details - println!("Unsupported operation details:"); - //get operator discriminant - // let PackedOperation(discriminant) = instruction.op; - // println!("{064b}", discriminant); - println!("Name: {}", instruction.op.name()); - println!("Parameters: {:?}", instruction.params_view()); - println!("Qubits: {:?}", self.dag_circ.qargs_interner().get(instruction.qubits)); - println!("Clbits: {:?}", self.dag_circ.cargs_interner().get(instruction.clbits)); - panic!("Unsupported operation: {}", instruction.op.name()); - - } - } pub fn new(dag_circ: DAGCircuit) -> Self { @@ -1007,9 +725,347 @@ fn build_layers(dag: &DAGCircuit) -> Vec> { layers } +pub fn from_instruction<'a>(dag_circ: &'a DAGCircuit, instruction: &'a PackedInstruction) -> Drawable<'a>{ + // println!("{:?}", instruction.label()); + if let Some(standard_gate) = instruction.op.try_standard_gate() { + let instruction_name = instruction.op.name(); + let instruction_param =format!("{:?}",instruction.params_view()); + let mut instruction_label: Option = match standard_gate { + StandardGate::GlobalPhase => { + // Global phase gate - affects overall circuit phase + None + } + StandardGate::H => { + // Hadamard gate - creates superposition + Some("H".to_string()) + } + StandardGate::I => { + // Identity gate - no operation + Some("I".to_string()) + } + StandardGate::X => { + // Pauli-X gate (NOT gate) + Some("X".to_string()) + } + StandardGate::Y => { + // Pauli-Y gate + Some("Y".to_string()) + } + StandardGate::Z => { + // Pauli-Z gate + Some("Z".to_string()) + } + StandardGate::Phase => { + // Phase gate (parameterized) + Some(format!("P({})", instruction_param)) + } + StandardGate::R => { + // R gate (rotation about axis in XY plane) + Some(format!("R({})", instruction_param)) + } + StandardGate::RX => { + // Rotation about X axis + Some(format!("RX({})", instruction_param)) + } + StandardGate::RY => { + // Rotation about Y axis + Some(format!("RY({})", instruction_param)) + } + StandardGate::RZ => { + // Rotation about Z axis + Some(format!("RZ({})", instruction_param)) + } + StandardGate::S => { + // S gate (phase π/2) + Some("S".to_string()) + } + StandardGate::Sdg => { + // S dagger gate (phase -π/2) + Some("S†".to_string()) + } + StandardGate::SX => { + // Square root of X gate + Some("√X".to_string()) + } + StandardGate::SXdg => { + // Square root of X dagger gate + Some("√X†".to_string()) + } + StandardGate::T => { + // T gate (phase π/4) + Some("T".to_string()) + } + StandardGate::Tdg => { + // T dagger gate (phase -π/4) + Some("T†".to_string()) + } + StandardGate::U => { + // Universal single-qubit gate (3 parameters) + Some(format!("U({})", instruction_param)) + } + StandardGate::U1 => { + // U1 gate (1 parameter - phase) + Some(format!("U1({})", instruction_param)) + } + StandardGate::U2 => { + // U2 gate (2 parameters) + Some(format!("U2({})", instruction_param)) + } + StandardGate::U3 => { + // U3 gate (3 parameters - equivalent to U) + Some(format!("U3({})", instruction_param)) + } + StandardGate::CH => { + // Controlled Hadamard gate + Some("H".to_string()) + } + StandardGate::CX => { + // Controlled-X gate (CNOT) + Some("X".to_string()) + } + StandardGate::CY => { + // Controlled-Y gate + Some("Y".to_string()) + } + StandardGate::CZ => { + // Controlled-Z gate + Some("Z".to_string()) + } + StandardGate::DCX => { + // Double CNOT gate + Some("DCX".to_string()) + } + StandardGate::ECR => { + // Echoed cross-resonance gate + Some("ECR".to_string()) + } + StandardGate::Swap => { + // Swap gate + None + } + StandardGate::ISwap => { + // i-Swap gate + None + } + StandardGate::CPhase => { + // Controlled phase gate + + Some(format!("P({})", instruction_param)) + } + StandardGate::CRX => { + // Controlled rotation about X + Some(format!("RX({})", instruction_param)) + } + StandardGate::CRY => { + // Controlled rotation about Y + Some(format!("RY({})", instruction_param)) + } + StandardGate::CRZ => { + // Controlled rotation about Z + Some(format!("RZ({})", instruction_param)) + } + StandardGate::CS => { + // Controlled S gate + Some("S".to_string()) + } + StandardGate::CSdg => { + // Controlled S dagger gate + Some("S†".to_string()) + } + StandardGate::CSX => { + // Controlled square root of X gate + Some("√X".to_string()) + } + StandardGate::CU => { + // Controlled U gate (4 parameters) + Some(format!("U({})", instruction_param)) + } + StandardGate::CU1 => { + // Controlled U1 gate + Some(format!("U1({})", instruction_param)) + } + StandardGate::CU3 => { + // Controlled U3 gate + Some(format!("U3({})", instruction_param)) + } + StandardGate::RXX => { + // Two-qubit XX rotation + Some(format!("RXX({})", instruction_param)) + } + StandardGate::RYY => { + // Two-qubit YY rotation + Some(format!("RYY({})", instruction_param)) + } + StandardGate::RZZ => { + // Two-qubit ZZ rotation + Some(format!("RZZ({})", instruction_param)) + } + StandardGate::RZX => { + // Two-qubit ZX rotation + Some(format!("RZX({})", instruction_param)) + } + StandardGate::XXMinusYY => { + // XX-YY gate + Some(format!("XX-YY({})", instruction_param)) + } + StandardGate::XXPlusYY => { + // XX+YY gate + Some(format!("XX+YY({})", instruction_param)) + } + StandardGate::CCX => { + // Toffoli gate (controlled-controlled-X) + Some("X".to_string()) + } + StandardGate::CCZ => { + // Controlled-controlled-Z gate + Some("Z".to_string()) + } + StandardGate::CSwap => { + // Controlled swap gate (Fredkin gate) + None + } + StandardGate::RCCX => { + // Relative-phase Toffoli gate + Some(format!("RX({})", instruction_param)) + } + StandardGate::C3X => { + // 3-controlled X gate (4-qubit controlled X) + Some("X".to_string()) + } + StandardGate::C3SX => { + // 3-controlled square root of X gate + Some("√X".to_string()) + } + StandardGate::RC3X => { + // Relative-phase 3-controlled X gate + Some(format!("RX({})", instruction_param)) + } + }; + + let label = instruction.label(); + instruction_label = match label { + Some(l) => { + if l != "" { + Some(l.to_string()) + } else { + instruction_label + } + } + None => instruction_label, + }; + + // TO DO:🥀 + // + // assign the correct visual type to all gates and instructions + // + // + + let visual_type = match standard_gate { + StandardGate::GlobalPhase => VisualType::BetweenWire(instruction.params_view().get(0).map_or("".to_string(), |p| format!("{:?}", p))), + StandardGate::Swap | StandardGate::CSwap => VisualType::DirectOnWire("X".to_string()), + StandardGate::CPhase => VisualType::BetweenWire(instruction_label.clone().unwrap()), + _ => VisualType::Boxed, + }; + + // for label debugging + println!("{}{}",instruction_label.clone().unwrap_or("".to_string()), instruction_name); + Drawable { + packedinst: &instruction, + wire: WireType::Qubit, + dag_circ: &dag_circ, + name: instruction_name.to_string(), + label: instruction_label, + visual_type: visual_type, + } + } + else if let Some(standard_instruction) = instruction.op.try_standard_instruction() { + match standard_instruction { + StandardInstruction::Measure =>{ + Drawable{ + packedinst: &instruction, + wire: WireType::Qubit, + dag_circ: &dag_circ, + name: "Measure".to_string(), + label: Some("M".to_string()), + visual_type: VisualType::Boxed, + } + }, + StandardInstruction::Barrier(x) => { + + let label = instruction.label(); + let inst_label = match label{ + None => BARRIER_CHAR.to_string(), + Some("") => BARRIER_CHAR.to_string(), + Some(label) => label.to_string(), + }; + + println!("barrier label: {}", inst_label); + + Drawable{ + packedinst: &instruction, + wire: WireType::Qubit, + dag_circ: &dag_circ, + name: "Barrier".to_string(), + label: Some(inst_label.clone()), + visual_type: VisualType::AllWires(BARRIER_CHAR.to_string()), + } + }, + StandardInstruction::Reset => { + Drawable{ + packedinst: &instruction, + wire: WireType::Qubit, + dag_circ: &dag_circ, + name: "Reset".to_string(), + label: Some("|0>".to_string()), + visual_type: VisualType::DirectOnWire("|0>".to_string()), + } + }, + StandardInstruction::Delay(_unit) => { + + let unit = format!("{:?}", _unit).to_lowercase(); + + let label = if let Some(dur) = instruction.params_view().get(0){ + format!("Delay({:?}[{:?}])", dur, unit) + } else { + "Delay".to_string() + }; + + Drawable{ + packedinst: &instruction, + wire: WireType::Qubit, + dag_circ: &dag_circ, + name: "Delay".to_string(), + label: Some(label.clone()), + visual_type: VisualType::Boxed, + } + } + } + } + else { + // print all operation details + println!("Unsupported operation details:"); + //get operator discriminant + // let PackedOperation(discriminant) = instruction.op; + // println!("{064b}", discriminant); + println!("Name: {}", instruction.op.name()); + println!("Parameters: {:?}", instruction.params_view()); + println!("Qubits: {:?}", dag_circ.qargs_interner().get(instruction.qubits)); + println!("Clbits: {:?}", dag_circ.cargs_interner().get(instruction.clbits)); + panic!("Unsupported operation: {}", instruction.op.name()); + + } +} + +pub fn get_indices(dag_circ: &DAGCircuit) -> u32{ + let total_qubits = dag_circ.num_qubits() as u32; + let total_clbits = dag_circ.num_clbits() as u32; + total_qubits + total_clbits +} + pub fn draw_circuit(circuit: &CircuitData) -> PyResult<()> { let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; + println!("Creating Packed Instruction layers..."); let packedinst_layers = build_layers(&dag); for (i, layer) in packedinst_layers.iter().enumerate() { @@ -1026,11 +1082,18 @@ pub fn draw_circuit(circuit: &CircuitData) -> PyResult<()> { //using circuit rep to draw circuit let mut circuit_rep = CircuitRep::new(dag.clone()); - let vis_mat2:VisualizationMatrix = VisualizationMatrix::new(circuit_rep, packedinst_layers); - vis_mat2.print(); - println!("{}", circuit_rep.circuit_string()); + let vis_mat2:VisualizationMatrix = VisualizationMatrix::new(&dag, packedinst_layers); + circuit_rep.set_qubit_name(); + vis_mat2.draw(&mut circuit_rep); + // vis_mat2.print(); + //println!("{}", &circuit_rep.circuit_string()); println!("======================"); + println!("Drawing circuit..."); + // let mut circuit_rep2 = CircuitRep::new(dag); + // vis_mat2.draw(&mut circuit_rep2); + + println!("finished circuit"); // vis_mat.print(); Ok(()) } From 55f90a1d2a49a2536ce23be55c2157e42eaf4f41 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Tue, 7 Oct 2025 17:04:46 +0300 Subject: [PATCH 21/70] Lay groundwork for VisualizationMatrix --- crates/circuit/src/circuit_drawer.rs | 183 +++++++++++++++++++++------ 1 file changed, 146 insertions(+), 37 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 438151133bd8..23eb3bf097f6 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -14,9 +14,10 @@ use core::panic; use std::fmt::Debug; use hashbrown::{HashSet, HashMap}; use crate::dag_circuit::DAGCircuit; -use crate::operations::{Operation, StandardGate, StandardInstruction}; +use crate::operations::{Operation, OperationRef, StandardGate, StandardInstruction}; use crate::packed_instruction::PackedInstruction; use itertools::{Itertools, MinMaxResult}; +use std::ops::Index; use pyo3::prelude::*; use pyo3::{import_exception}; @@ -112,15 +113,15 @@ pub struct Drawable<'a>{ #[derive(Clone, Debug)] struct VisualizationMatrix<'a>{ - visualization_layers: Vec>, + pub visualization_layers: Vec>, packedinst_layers: Vec>, dag_circ: &'a DAGCircuit } #[derive(Clone, Debug)] struct VisualizationLayer<'a>{ - elements: Vec>>, - drawables: Vec>, + pub elements: Vec>>, + pub drawables: Vec>, width: u32, // circuit_rep: &'a CircuitRep //parent: &'a VisualizationMatrix<'a> @@ -182,6 +183,7 @@ impl<'a> VisualizationMatrix<'a>{ println!("Drawing VisualizationMatrix..."); for layer in &self.visualization_layers{ let wires = layer.draw_layer(circuit_rep); + println!("wires {:?}", wires); for (i, wire) in wires.iter().enumerate(){ circuit_rep.q_wires[i].add_wire_component(wire); } @@ -295,7 +297,7 @@ impl<'a> VisualizationElement<'a>{ return vis_element; } - // pub fn get_string(&self, layer:&VisualizationLayer) -> String{ + // pub fn get_string(&self, layer:&VisualizationLayer) -> String{ // layer.drawables[self.layer_element_index].get_name() // } @@ -489,7 +491,7 @@ impl<'a> Drawable<'a> { } } else { panic!("Boxed visual has no label"); - } + } } VisualType::BetweenWire(label) => { if let Some(inst_label) = &self.label{ @@ -511,8 +513,8 @@ impl<'a> Drawable<'a> { wire_component.bot.push_str(&format!("{}{}", CONNECTING_WIRE ," ".repeat(inst_label.len()))); } } else { - panic!("BetweenWire visual has no label"); - } + panic!("BetweenWire visual has no label"); + } } VisualType::DirectOnWire(label) => { let inst_label = match &self.label { @@ -561,7 +563,7 @@ impl<'a> Drawable<'a> { } // this function returns the indices of wires than an instruction is acting upon. // Since rust currently only has StandardInstruction and StandardGates, it always returns - // the qubit indices first, where in usually the first index is of the control qubit and the rest are of + // the qubit indices first, where in usually the first index is of the control qubit and the rest are of // the target. However once ControlFlows are introduced, this handling needs to be more nuanced. pub fn get_wire_indices(&self) -> Vec{ let mut qubit_indices: Vec = self.dag_circ.qargs_interner().get(self.packedinst.qubits) @@ -618,12 +620,8 @@ impl<'a> DrawElement for Drawable<'a>{ impl<'a> CircuitRep { pub fn new(dag_circ: DAGCircuit) -> Self { - - //number of qubits in dag_circuit - let qubit = dag_circ.num_qubits(); - CircuitRep { - q_wires: vec!(wire::new(WireType::Qubit); qubit as usize), + q_wires: vec!(wire::new(WireType::Qubit); dag_circ.num_qubits()), dag_circ: dag_circ } } @@ -1062,38 +1060,149 @@ pub fn get_indices(dag_circ: &DAGCircuit) -> u32{ total_qubits + total_clbits } -pub fn draw_circuit(circuit: &CircuitData) -> PyResult<()> { - let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; - - println!("Creating Packed Instruction layers..."); - - let packedinst_layers = build_layers(&dag); - for (i, layer) in packedinst_layers.iter().enumerate() { - println!("==== LAYER {} ====", i); - for inst in layer { - println!("{:?}", inst.op.name()); + + +/// Enum for representing the elements stored in a visualization matrix. The elements +/// do not directly implement visualization capabilities, but rather carry enough information +/// to enable visualization later on by the actual drawer. +#[derive(Default, Clone, Debug)] +enum VisualizationElement2 { + #[default] + Empty, // Marker for no element + Input, // TODO: should be enum for qubit/clbits + Control, // TODO: should be an enum for the control symbols, e.g. closed, open + VerticalLine, // TODO: should be an enum for the various types, e.g. single, double + Operation, // TODO: should be an enum for the various fine-grained types: standard gates, instruction, etc.. +} + +/// A representation of a single column (called here a layer) of a visualization matrix +#[derive(Clone, Debug)] +struct VisualizationLayer2(Vec); + +impl VisualizationLayer2 { + fn len(&self) -> usize { + self.0.len() + } + + /// Adds the required visualization elements to represent the given instruction + fn add_instruction(&mut self, inst: &PackedInstruction, dag: &DAGCircuit) { + match inst.op.view() { + OperationRef::StandardGate(gate) => self.add_standard_gate(gate, inst, dag), + _ => unimplemented!("{}", format!("Visualization is not implemented for instruction of type {:?}", inst.op)), + } + } + + fn add_standard_gate(&mut self, gate: StandardGate, inst: &PackedInstruction, dag: &DAGCircuit) { + match gate { + StandardGate::CX | StandardGate::CCX => self.add_controlled_gate(inst, dag), + _ => unimplemented!("{}", format!("Visualization is not implemented for standard gate of type {:?}", gate)), + } + } + + fn add_controlled_gate(&mut self, inst: &PackedInstruction, dag: &DAGCircuit) { + let qargs = dag.get_qargs(inst.qubits); + + for control in 0..qargs.len() - 1 { + self.0[qargs[control].index()] = VisualizationElement2::Control; + } + self.0[qargs.last().unwrap().index()] = VisualizationElement2::Operation; + } +} + +impl Index for VisualizationLayer2 { + type Output = VisualizationElement2; + + fn index(&self, index: usize) -> &Self::Output { + &self.0[index] + } +} + +/// A Plain, logical 2D representation of a circuit. +/// +/// A dense representation of the circuit of size N * (M + 1), where the first +/// layer(column) represents the qubits and clbits inputs in the circuits, and +/// M is the number of operation layers. +#[derive(Debug)] +struct VisualizationMatrix2 { + layers: Vec, +} + + +impl VisualizationMatrix2 { + fn from_circuit(circuit: &CircuitData) -> PyResult { + let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; + let inst_layers = build_layers(&dag); + + let num_wires = circuit.num_qubits() + circuit.num_clbits(); + let mut layers = vec![VisualizationLayer2(vec![VisualizationElement2::default(); num_wires]); inst_layers.len() + 1]; // Add 1 to account for the inputs layer + + // TODO: add the qubit/clbit inputs here to layer #0 + + + for (i, layer) in inst_layers.iter().enumerate() { + for inst in layer { + layers[i + 1].add_instruction(inst, &dag); + } } + + Ok(VisualizationMatrix2{ + layers, + }) + } + + fn num_wires(&self) -> usize { + self.layers.first().map_or(0, |layer| layer.len()) + } + + fn num_layers(&self) -> usize { + self.layers.len() + } +} + +impl Index for VisualizationMatrix2 { + type Output = VisualizationLayer2; + + fn index(&self, index: usize) -> &Self::Output { + &self.layers[index] } +} + +pub fn draw_circuit(circuit: &CircuitData) -> PyResult<()> { + + let vis_mat2 = VisualizationMatrix2::from_circuit(circuit)?; + println!("======================"); + + println!("num wires {}, num layers {}", vis_mat2.num_wires(), vis_mat2.num_layers()); + + for i in 0..vis_mat2.num_wires() { + for j in 0..vis_mat2.num_layers() { + print!("{:^15}", format!("{:?}", vis_mat2[j][i])); + } + println!(""); + } // circuit_rep2.set_qubit_name(); // let vis_mat:VisualizationMatrix = VisualizationMatrix::new(&circuit_rep, packedinst_layers); println!("======================"); - //using circuit rep to draw circuit - let mut circuit_rep = CircuitRep::new(dag.clone()); - let vis_mat2:VisualizationMatrix = VisualizationMatrix::new(&dag, packedinst_layers); - circuit_rep.set_qubit_name(); - vis_mat2.draw(&mut circuit_rep); - // vis_mat2.print(); - //println!("{}", &circuit_rep.circuit_string()); - println!("======================"); + // //using circuit rep to draw circuit + // let mut circuit_rep = CircuitRep::new(dag.clone()); + // // println!("circuit_rep {:?}", circuit_rep); + // let vis_mat2:VisualizationMatrix = VisualizationMatrix::new(&dag, packedinst_layers); + + // return Ok(()); + // circuit_rep.set_qubit_name(); + // vis_mat2.draw(&mut circuit_rep); + // // vis_mat2.print(); + // //println!("{}", &circuit_rep.circuit_string()); + // println!("======================"); - println!("Drawing circuit..."); - // let mut circuit_rep2 = CircuitRep::new(dag); - // vis_mat2.draw(&mut circuit_rep2); + // println!("Drawing circuit..."); + // // let mut circuit_rep2 = CircuitRep::new(dag); + // // vis_mat2.draw(&mut circuit_rep2); - println!("finished circuit"); - // vis_mat.print(); + // println!("finished circuit"); + // // vis_mat.print(); Ok(()) } From ec2bda5de0212de7c8c396c625512db1db5a5feb Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Mon, 6 Oct 2025 16:10:55 +0530 Subject: [PATCH 22/70] fixed a counter in draw_layer, removed debug print statement to make code run --- crates/circuit/src/circuit_drawer.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 23eb3bf097f6..072750ade481 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -144,17 +144,28 @@ pub struct VisualizationElement<'a>{ impl<'a> VisualizationMatrix<'a>{ pub fn new(dag_circ: &'a DAGCircuit, packedinst_layers: Vec>) -> Self { + // println!("Creating VisualizationMatrix..."); + // let mut temp: Vec = Vec::with_capacity(packedinst_layers.len()); + // for layer in packedinst_layers.iter() { + // let vis_layer = VisualizationLayer::new(dag_circ, layer.to_vec()); + // temp.push(vis_layer); + // } + // println!("Visualization layers created: {:?}", temp); + + // VisualizationMatrix { + // visualization_layers: temp, + // packedinst_layers, + // dag_circ, // Now we own this, so no borrowing issues + // } println!("Creating VisualizationMatrix..."); let mut temp: Vec = Vec::with_capacity(packedinst_layers.len()); for layer in packedinst_layers.iter() { - let vis_layer = VisualizationLayer::new(dag_circ, layer.to_vec()); + let vis_layer = VisualizationLayer::new(dag_circ, layer.clone()); temp.push(vis_layer); } - println!("Visualization layers created: {:?}", temp); - VisualizationMatrix { visualization_layers: temp, - packedinst_layers, + packedinst_layers: packedinst_layers, dag_circ, // Now we own this, so no borrowing issues } } @@ -236,7 +247,6 @@ impl<'a> VisualizationLayer<'a>{ let mut wires: Vec = Vec::new(); let mut ct: usize = 0; for element in self.elements.iter(){ - if let Some(vis_element) = element{ let wire_component = vis_element.draw(&self); wires.push(wire_component); @@ -250,6 +260,7 @@ impl<'a> VisualizationLayer<'a>{ wires.push(wire_component); } } + ct += 1; } wires } From 51c836eb2c06a5b54635d0b4f19a0e29dfb6aa8e Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Mon, 13 Oct 2025 12:31:36 +0530 Subject: [PATCH 23/70] general handling of standard gate and instructions added, inputs also handled --- crates/circuit/src/circuit_drawer.rs | 727 ++++++++++++++++++--------- 1 file changed, 480 insertions(+), 247 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 072750ade481..b39589f6a94f 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -11,11 +11,14 @@ // that they have been altered from the originals. use core::panic; +use std::boxed; use std::fmt::Debug; -use hashbrown::{HashSet, HashMap}; -use crate::dag_circuit::DAGCircuit; +use std::io::ErrorKind; +use hashbrown::HashSet; +use crate::bit::{ShareableClbit, ShareableQubit}; +use crate::dag_circuit::{DAGCircuit}; use crate::operations::{Operation, OperationRef, StandardGate, StandardInstruction}; -use crate::packed_instruction::PackedInstruction; +use crate::packed_instruction::{PackedInstruction,PackedOperation}; use itertools::{Itertools, MinMaxResult}; use std::ops::Index; @@ -734,222 +737,239 @@ fn build_layers(dag: &DAGCircuit) -> Vec> { layers } +pub fn get_label(instruction: &PackedInstruction) -> Option{ + let label = instruction.label(); + let instruction_param =format!("{:?}",instruction.params_view()); + let instruction_label = match label { + Some(l) => { + Some(l.to_string()) + } + None => { + if let Some(standard_gate) = instruction.op.try_standard_gate() { + match standard_gate { + StandardGate::GlobalPhase => { + // Global phase gate - affects overall circuit phase + None + } + StandardGate::H => { + // Hadamard gate - creates superposition + Some("H".to_string()) + } + StandardGate::I => { + // Identity gate - no operation + Some("I".to_string()) + } + StandardGate::X => { + // Pauli-X gate (NOT gate) + Some("X".to_string()) + } + StandardGate::Y => { + // Pauli-Y gate + Some("Y".to_string()) + } + StandardGate::Z => { + // Pauli-Z gate + Some("Z".to_string()) + } + StandardGate::Phase => { + // Phase gate (parameterized) + Some(format!("P({})", instruction_param)) + } + StandardGate::R => { + // R gate (rotation about axis in XY plane) + Some(format!("R({})", instruction_param)) + } + StandardGate::RX => { + // Rotation about X axis + Some(format!("RX({})", instruction_param)) + } + StandardGate::RY => { + // Rotation about Y axis + Some(format!("RY({})", instruction_param)) + } + StandardGate::RZ => { + // Rotation about Z axis + Some(format!("RZ({})", instruction_param)) + } + StandardGate::S => { + // S gate (phase π/2) + Some("S".to_string()) + } + StandardGate::Sdg => { + // S dagger gate (phase -π/2) + Some("S†".to_string()) + } + StandardGate::SX => { + // Square root of X gate + Some("√X".to_string()) + } + StandardGate::SXdg => { + // Square root of X dagger gate + Some("√X†".to_string()) + } + StandardGate::T => { + // T gate (phase π/4) + Some("T".to_string()) + } + StandardGate::Tdg => { + // T dagger gate (phase -π/4) + Some("T†".to_string()) + } + StandardGate::U => { + // Universal single-qubit gate (3 parameters) + Some(format!("U({})", instruction_param)) + } + StandardGate::U1 => { + // U1 gate (1 parameter - phase) + Some(format!("U1({})", instruction_param)) + } + StandardGate::U2 => { + // U2 gate (2 parameters) + Some(format!("U2({})", instruction_param)) + } + StandardGate::U3 => { + // U3 gate (3 parameters - equivalent to U) + Some(format!("U3({})", instruction_param)) + } + StandardGate::CH => { + // Controlled Hadamard gate + Some("H".to_string()) + } + StandardGate::CX => { + // Controlled-X gate (CNOT) + Some("X".to_string()) + } + StandardGate::CY => { + // Controlled-Y gate + Some("Y".to_string()) + } + StandardGate::CZ => { + // Controlled-Z gate + Some("Z".to_string()) + } + StandardGate::DCX => { + // Double CNOT gate + Some("DCX".to_string()) + } + StandardGate::ECR => { + // Echoed cross-resonance gate + Some("ECR".to_string()) + } + StandardGate::Swap => { + // Swap gate + None + } + StandardGate::ISwap => { + // i-Swap gate + None + } + StandardGate::CPhase => { + // Controlled phase gate + + Some(format!("P({})", instruction_param)) + } + StandardGate::CRX => { + // Controlled rotation about X + Some(format!("RX({})", instruction_param)) + } + StandardGate::CRY => { + // Controlled rotation about Y + Some(format!("RY({})", instruction_param)) + } + StandardGate::CRZ => { + // Controlled rotation about Z + Some(format!("RZ({})", instruction_param)) + } + StandardGate::CS => { + // Controlled S gate + Some("S".to_string()) + } + StandardGate::CSdg => { + // Controlled S dagger gate + Some("S†".to_string()) + } + StandardGate::CSX => { + // Controlled square root of X gate + Some("√X".to_string()) + } + StandardGate::CU => { + // Controlled U gate (4 parameters) + Some(format!("U({})", instruction_param)) + } + StandardGate::CU1 => { + // Controlled U1 gate + Some(format!("U1({})", instruction_param)) + } + StandardGate::CU3 => { + // Controlled U3 gate + Some(format!("U3({})", instruction_param)) + } + StandardGate::RXX => { + // Two-qubit XX rotation + Some(format!("RXX({})", instruction_param)) + } + StandardGate::RYY => { + // Two-qubit YY rotation + Some(format!("RYY({})", instruction_param)) + } + StandardGate::RZZ => { + // Two-qubit ZZ rotation + Some(format!("RZZ({})", instruction_param)) + } + StandardGate::RZX => { + // Two-qubit ZX rotation + Some(format!("RZX({})", instruction_param)) + } + StandardGate::XXMinusYY => { + // XX-YY gate + Some(format!("XX-YY({})", instruction_param)) + } + StandardGate::XXPlusYY => { + // XX+YY gate + Some(format!("XX+YY({})", instruction_param)) + } + StandardGate::CCX => { + // Toffoli gate (controlled-controlled-X) + Some("X".to_string()) + } + StandardGate::CCZ => { + // Controlled-controlled-Z gate + Some("Z".to_string()) + } + StandardGate::CSwap => { + // Controlled swap gate (Fredkin gate) + None + } + StandardGate::RCCX => { + // Relative-phase Toffoli gate + Some(format!("RX({})", instruction_param)) + } + StandardGate::C3X => { + // 3-controlled X gate (4-qubit controlled X) + Some("X".to_string()) + } + StandardGate::C3SX => { + // 3-controlled square root of X gate + Some("√X".to_string()) + } + StandardGate::RC3X => { + // Relative-phase 3-controlled X gate + Some(format!("RX({})", instruction_param)) + } + } + } else { + Some(instruction.op.name().to_string()) + } + }, + }; + instruction_label +} + pub fn from_instruction<'a>(dag_circ: &'a DAGCircuit, instruction: &'a PackedInstruction) -> Drawable<'a>{ // println!("{:?}", instruction.label()); + let instruction_name = instruction.op.name(); if let Some(standard_gate) = instruction.op.try_standard_gate() { - let instruction_name = instruction.op.name(); - let instruction_param =format!("{:?}",instruction.params_view()); - let mut instruction_label: Option = match standard_gate { - StandardGate::GlobalPhase => { - // Global phase gate - affects overall circuit phase - None - } - StandardGate::H => { - // Hadamard gate - creates superposition - Some("H".to_string()) - } - StandardGate::I => { - // Identity gate - no operation - Some("I".to_string()) - } - StandardGate::X => { - // Pauli-X gate (NOT gate) - Some("X".to_string()) - } - StandardGate::Y => { - // Pauli-Y gate - Some("Y".to_string()) - } - StandardGate::Z => { - // Pauli-Z gate - Some("Z".to_string()) - } - StandardGate::Phase => { - // Phase gate (parameterized) - Some(format!("P({})", instruction_param)) - } - StandardGate::R => { - // R gate (rotation about axis in XY plane) - Some(format!("R({})", instruction_param)) - } - StandardGate::RX => { - // Rotation about X axis - Some(format!("RX({})", instruction_param)) - } - StandardGate::RY => { - // Rotation about Y axis - Some(format!("RY({})", instruction_param)) - } - StandardGate::RZ => { - // Rotation about Z axis - Some(format!("RZ({})", instruction_param)) - } - StandardGate::S => { - // S gate (phase π/2) - Some("S".to_string()) - } - StandardGate::Sdg => { - // S dagger gate (phase -π/2) - Some("S†".to_string()) - } - StandardGate::SX => { - // Square root of X gate - Some("√X".to_string()) - } - StandardGate::SXdg => { - // Square root of X dagger gate - Some("√X†".to_string()) - } - StandardGate::T => { - // T gate (phase π/4) - Some("T".to_string()) - } - StandardGate::Tdg => { - // T dagger gate (phase -π/4) - Some("T†".to_string()) - } - StandardGate::U => { - // Universal single-qubit gate (3 parameters) - Some(format!("U({})", instruction_param)) - } - StandardGate::U1 => { - // U1 gate (1 parameter - phase) - Some(format!("U1({})", instruction_param)) - } - StandardGate::U2 => { - // U2 gate (2 parameters) - Some(format!("U2({})", instruction_param)) - } - StandardGate::U3 => { - // U3 gate (3 parameters - equivalent to U) - Some(format!("U3({})", instruction_param)) - } - StandardGate::CH => { - // Controlled Hadamard gate - Some("H".to_string()) - } - StandardGate::CX => { - // Controlled-X gate (CNOT) - Some("X".to_string()) - } - StandardGate::CY => { - // Controlled-Y gate - Some("Y".to_string()) - } - StandardGate::CZ => { - // Controlled-Z gate - Some("Z".to_string()) - } - StandardGate::DCX => { - // Double CNOT gate - Some("DCX".to_string()) - } - StandardGate::ECR => { - // Echoed cross-resonance gate - Some("ECR".to_string()) - } - StandardGate::Swap => { - // Swap gate - None - } - StandardGate::ISwap => { - // i-Swap gate - None - } - StandardGate::CPhase => { - // Controlled phase gate - - Some(format!("P({})", instruction_param)) - } - StandardGate::CRX => { - // Controlled rotation about X - Some(format!("RX({})", instruction_param)) - } - StandardGate::CRY => { - // Controlled rotation about Y - Some(format!("RY({})", instruction_param)) - } - StandardGate::CRZ => { - // Controlled rotation about Z - Some(format!("RZ({})", instruction_param)) - } - StandardGate::CS => { - // Controlled S gate - Some("S".to_string()) - } - StandardGate::CSdg => { - // Controlled S dagger gate - Some("S†".to_string()) - } - StandardGate::CSX => { - // Controlled square root of X gate - Some("√X".to_string()) - } - StandardGate::CU => { - // Controlled U gate (4 parameters) - Some(format!("U({})", instruction_param)) - } - StandardGate::CU1 => { - // Controlled U1 gate - Some(format!("U1({})", instruction_param)) - } - StandardGate::CU3 => { - // Controlled U3 gate - Some(format!("U3({})", instruction_param)) - } - StandardGate::RXX => { - // Two-qubit XX rotation - Some(format!("RXX({})", instruction_param)) - } - StandardGate::RYY => { - // Two-qubit YY rotation - Some(format!("RYY({})", instruction_param)) - } - StandardGate::RZZ => { - // Two-qubit ZZ rotation - Some(format!("RZZ({})", instruction_param)) - } - StandardGate::RZX => { - // Two-qubit ZX rotation - Some(format!("RZX({})", instruction_param)) - } - StandardGate::XXMinusYY => { - // XX-YY gate - Some(format!("XX-YY({})", instruction_param)) - } - StandardGate::XXPlusYY => { - // XX+YY gate - Some(format!("XX+YY({})", instruction_param)) - } - StandardGate::CCX => { - // Toffoli gate (controlled-controlled-X) - Some("X".to_string()) - } - StandardGate::CCZ => { - // Controlled-controlled-Z gate - Some("Z".to_string()) - } - StandardGate::CSwap => { - // Controlled swap gate (Fredkin gate) - None - } - StandardGate::RCCX => { - // Relative-phase Toffoli gate - Some(format!("RX({})", instruction_param)) - } - StandardGate::C3X => { - // 3-controlled X gate (4-qubit controlled X) - Some("X".to_string()) - } - StandardGate::C3SX => { - // 3-controlled square root of X gate - Some("√X".to_string()) - } - StandardGate::RC3X => { - // Relative-phase 3-controlled X gate - Some(format!("RX({})", instruction_param)) - } - }; + let mut instruction_label: Option = get_label(instruction); let label = instruction.label(); instruction_label = match label { @@ -977,7 +997,7 @@ pub fn from_instruction<'a>(dag_circ: &'a DAGCircuit, instruction: &'a PackedIns }; // for label debugging - println!("{}{}",instruction_label.clone().unwrap_or("".to_string()), instruction_name); + // println!("{}{}",instruction_label.clone().unwrap_or("".to_string()), instruction_name); Drawable { packedinst: &instruction, wire: WireType::Qubit, @@ -1073,55 +1093,247 @@ pub fn get_indices(dag_circ: &DAGCircuit) -> u32{ +#[derive(Clone)] +enum WireInput<'a> { + Qubit(&'a ShareableQubit), + Clbit(&'a ShareableClbit), +} + +impl<'a> Debug for WireInput<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let name = match self { + WireInput::Qubit(&ref qubit) => "Qubit", + WireInput::Clbit(&ref clbit) => "Clbit", + }; + + write!(f, "{}", name) + } +} + +impl<'a> Debug for Boxed<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Boxed::Single(label) => write!(f, "Boxed({})", label), + Boxed::Multi(Some(inst)) => write!(f, "MultiBox({})", inst.op.name()), + Boxed::Multi(None) => write!(f, "MultiBox(None)"), + } + } +} + +/// Input Wires. +/// The Option is for optional labels and can be used when the registers have different names. This allows us to use +/// the same enum for both Input and VerticalLine types. +#[derive(Clone, Debug)] +enum InputType{ + Qubit(Option), + Clbit(Option) +} + +/// Enum for representing elements that can appear directly on a wire. +#[derive(Clone, Debug)] +enum ElementOnWire{ + Control, + Swap +} + +/// Enum for representing elements that appear directly on a wire and how they're connected. +#[derive(Clone, Debug)] +enum OnWire{ + Top(ElementOnWire), + Mid(ElementOnWire), + Bot(ElementOnWire), + Barrier, + Reset, +} + +// /// Enum for telling if an qubit in the index of a MultiBox Operation is being acted on or not. If yes, the u32 tells which index qubit it is. +// #[derive(Clone, Debug)] +// enum Effect{ +// Affected(u32), +// Unaffected +// } + +// /// Enum for representing where the element appears in the representation of a multi-box operation. +// #[derive(Clone, Debug)] +// enum MultiBoxElement{ +// Top(Effect), +// Mid(Effect), +// Bot(Effect), +// Label(String,Effect) +// } + +/// Enum for representing elements that appear in a boxed operation. +#[derive(Clone)] +enum Boxed<'a>{ + Single(String), + // Multi(MultiBoxElement) + Multi(Option<&'a PackedInstruction>), +} + /// Enum for representing the elements stored in a visualization matrix. The elements /// do not directly implement visualization capabilities, but rather carry enough information /// to enable visualization later on by the actual drawer. #[derive(Default, Clone, Debug)] -enum VisualizationElement2 { +enum VisualizationElement2<'a>{ #[default] Empty, // Marker for no element - Input, // TODO: should be enum for qubit/clbits - Control, // TODO: should be an enum for the control symbols, e.g. closed, open - VerticalLine, // TODO: should be an enum for the various types, e.g. single, double + Boxed(Boxed<'a>), + Input(WireInput<'a>), // TODO: should be enum for qubit/clbits + DirectOnWire(OnWire), // TODO: should be an enum for the control symbols, e.g. closed, open + VerticalLine(InputType), // TODO: should be an enum for the various types, e.g. single, double Operation, // TODO: should be an enum for the various fine-grained types: standard gates, instruction, etc.. } /// A representation of a single column (called here a layer) of a visualization matrix #[derive(Clone, Debug)] -struct VisualizationLayer2(Vec); +struct VisualizationLayer2<'a>(Vec>); -impl VisualizationLayer2 { +impl<'a> VisualizationLayer2<'a> { fn len(&self) -> usize { self.0.len() } + fn add_input(&mut self, input: WireInput<'a>, idx: usize) { + self.0[idx] = VisualizationElement2::Input(input); + } + /// Adds the required visualization elements to represent the given instruction - fn add_instruction(&mut self, inst: &PackedInstruction, dag: &DAGCircuit) { - match inst.op.view() { - OperationRef::StandardGate(gate) => self.add_standard_gate(gate, inst, dag), - _ => unimplemented!("{}", format!("Visualization is not implemented for instruction of type {:?}", inst.op)), + fn add_instruction(&mut self, inst: &'a PackedInstruction, dag: &DAGCircuit) { + // match inst.op.view() { + // OperationRef::StandardGate(gate) | OperationRef::StandardInstruction() => self.add_gate(inst, dag), + // _ => unimplemented!("{}", format!("Visualization is not implemented for instruction of type {:?}", inst.op)), + // } + self.add_gate(inst, dag); + } + + fn add_standard_gate(&mut self, gate: StandardGate, inst: &'a PackedInstruction, dag: &DAGCircuit) { + self.add_gate(inst, dag); + } + + fn get_controls(&self, inst: &PackedInstruction, dag: &DAGCircuit) -> Vec { + let has_control = vec![StandardGate::CX, StandardGate::CCX, StandardGate::CY, StandardGate::CZ, + StandardGate::CRX, StandardGate::CRY, StandardGate::CRZ, + StandardGate::CPhase, StandardGate::CS, StandardGate::CSdg, + StandardGate::CSX, StandardGate::CU, StandardGate::CU1, + StandardGate::CU3, StandardGate::CH, StandardGate::C3SX, StandardGate::C3X, + StandardGate::RC3X, StandardGate::RCCX]; + + let mut controls = Vec::new(); + + // check if gate is standard gate + if let std_gate = inst.op.try_standard_gate().unwrap() { + if has_control.contains(&std_gate) { + let qargs = dag.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); + let target = qargs.last().unwrap(); + let (minima,maxima) = get_instruction_range(dag, inst); + + for control in minima..=maxima { + if control != *target && qargs.contains(&control) { + controls.push(control); + } + } + controls + } else { + controls + } + } else { + controls + } + } + + fn add_controls(&mut self, controls: &Vec, range: (usize, usize)) { + for control in controls { + if *control == range.0 { + self.0[*control] = VisualizationElement2::DirectOnWire(OnWire::Top(ElementOnWire::Control)); + } else if *control == range.1 { + self.0[*control] = VisualizationElement2::DirectOnWire(OnWire::Bot(ElementOnWire::Control)); + } else { + self.0[*control] = VisualizationElement2::DirectOnWire(OnWire::Mid(ElementOnWire::Control)); + } } } - fn add_standard_gate(&mut self, gate: StandardGate, inst: &PackedInstruction, dag: &DAGCircuit) { - match gate { - StandardGate::CX | StandardGate::CCX => self.add_controlled_gate(inst, dag), - _ => unimplemented!("{}", format!("Visualization is not implemented for standard gate of type {:?}", gate)), + fn get_boxed_indices(&mut self, inst: & PackedInstruction, dag: &DAGCircuit) -> Vec{ + let single_box = vec![StandardGate::H, StandardGate::X, StandardGate::Y, StandardGate::Z, + StandardGate::RX, StandardGate::RY, StandardGate::RZ, StandardGate::U, StandardGate::U1, + StandardGate::U2, StandardGate::U3, StandardGate::S, StandardGate::Sdg, + StandardGate::T, StandardGate::Tdg, StandardGate::Phase, StandardGate::R, StandardGate::SX, + StandardGate::SXdg, StandardGate::CCX, StandardGate::CCZ, StandardGate::CX, StandardGate::CY, + StandardGate::CZ, StandardGate::CPhase, StandardGate::CRX, StandardGate::CRY, + StandardGate::CRZ, StandardGate::CS, StandardGate::CSdg, StandardGate::CSX, + StandardGate::CU, StandardGate::CU1, StandardGate::CU3, StandardGate::C3X, StandardGate::C3SX, StandardGate::RC3X]; + + let single_box_instrusctions = vec![StandardInstruction::Measure, StandardInstruction::Reset]; + + let multi_box = vec![StandardGate::ISwap, StandardGate::DCX, + StandardGate::ECR, StandardGate::RXX, StandardGate::RYY, StandardGate::RZZ, + StandardGate::RZX, StandardGate::XXMinusYY, StandardGate::XXPlusYY]; + + let qargs = dag.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); + let target = qargs.last().unwrap(); + let range = get_instruction_range(dag, inst); + + if let Some(std_gate) = inst.op.try_standard_gate(){ + if single_box.contains(&std_gate){ + vec![*target] + } else if multi_box.contains(&std_gate){ + (range.0..=range.1).collect() + } else { + vec![] + } + } else if let Some(std_instruction) = inst.op.try_standard_instruction(){ + if single_box_instrusctions.contains(&std_instruction){ + vec![*target] + } else if let StandardInstruction::Barrier(_) = std_instruction { + (range.0..=range.1).collect() + } else { + vec![] + } + } else { + vec![] } + } - fn add_controlled_gate(&mut self, inst: &PackedInstruction, dag: &DAGCircuit) { - let qargs = dag.get_qargs(inst.qubits); + fn add_boxed(&mut self, inst: &'a PackedInstruction, dag: &DAGCircuit, boxed_indices: &Vec) { + if boxed_indices.len() == 1 { + let qargs = dag.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); + let target = qargs.last().unwrap(); + let label = get_label(inst).unwrap_or_else(|| inst.op.name().to_string()); + self.0[*target] = VisualizationElement2::Boxed(Boxed::Single(label)); + } else if boxed_indices.len() > 1 { + for idx in boxed_indices { + self.0[*idx] = VisualizationElement2::Boxed(Boxed::Multi(Some(inst))); + } + } + } - for control in 0..qargs.len() - 1 { - self.0[qargs[control].index()] = VisualizationElement2::Control; + fn add_gate(&mut self, inst: &'a PackedInstruction, dag: &DAGCircuit) { + let qargs = dag.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); + let (minima,maxima) = get_instruction_range(dag, inst); + let controls = self.get_controls(inst, dag); + let boxed_elements = self.get_boxed_indices(inst, dag); + println!("Adding gate {:?}\n on qubits {:?} \n with controls {:?} \n and boxed elements {:?}\n ================", inst.op.name(), qargs, controls, boxed_elements); + let vert_lines = (minima..=maxima) + .filter(|idx| !controls.contains(idx) && !boxed_elements.contains(idx)) + .collect_vec(); + self.add_controls(&controls, (minima, maxima)); + self.add_boxed(inst, dag, &boxed_elements); + for vline in vert_lines { + let input_type = if vline < dag.num_qubits() { + InputType::Qubit(None) + } else { + InputType::Clbit(None) + }; + self.0[vline] = VisualizationElement2::VerticalLine(input_type); } - self.0[qargs.last().unwrap().index()] = VisualizationElement2::Operation; + + //self.0[qargs.last().unwrap().index()] = VisualizationElement2::Operation; } } -impl Index for VisualizationLayer2 { - type Output = VisualizationElement2; +impl<'a> Index for VisualizationLayer2<'a> { + type Output = VisualizationElement2<'a>; fn index(&self, index: usize) -> &Self::Output { &self.0[index] @@ -1134,14 +1346,14 @@ impl Index for VisualizationLayer2 { /// layer(column) represents the qubits and clbits inputs in the circuits, and /// M is the number of operation layers. #[derive(Debug)] -struct VisualizationMatrix2 { - layers: Vec, +struct VisualizationMatrix2<'a> { + layers: Vec>, } -impl VisualizationMatrix2 { - fn from_circuit(circuit: &CircuitData) -> PyResult { - let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; +impl<'a> VisualizationMatrix2<'a> { + + fn from_circuit(circuit: &'a CircuitData, dag: &'a DAGCircuit) -> PyResult { let inst_layers = build_layers(&dag); let num_wires = circuit.num_qubits() + circuit.num_clbits(); @@ -1149,6 +1361,17 @@ impl VisualizationMatrix2 { // TODO: add the qubit/clbit inputs here to layer #0 + let input_layer = layers.first_mut().unwrap(); + let mut input_idx = 0; + for qubit in circuit.qubits().objects() { + input_layer.add_input(WireInput::Qubit(qubit), input_idx); + input_idx += 1; + } + + for clbit in circuit.clbits().objects() { + input_layer.add_input(WireInput::Clbit(clbit), input_idx); + input_idx += 1; + } for (i, layer) in inst_layers.iter().enumerate() { for inst in layer { @@ -1170,8 +1393,8 @@ impl VisualizationMatrix2 { } } -impl Index for VisualizationMatrix2 { - type Output = VisualizationLayer2; +impl<'a> Index for VisualizationMatrix2<'a> { + type Output = VisualizationLayer2<'a>; fn index(&self, index: usize) -> &Self::Output { &self.layers[index] @@ -1180,7 +1403,9 @@ impl Index for VisualizationMatrix2 { pub fn draw_circuit(circuit: &CircuitData) -> PyResult<()> { - let vis_mat2 = VisualizationMatrix2::from_circuit(circuit)?; + let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; + + let vis_mat2 = VisualizationMatrix2::from_circuit(circuit, &dag)?; println!("======================"); @@ -1188,7 +1413,7 @@ pub fn draw_circuit(circuit: &CircuitData) -> PyResult<()> { for i in 0..vis_mat2.num_wires() { for j in 0..vis_mat2.num_layers() { - print!("{:^15}", format!("{:?}", vis_mat2[j][i])); + print!("{:^30}", format!("{:?}", vis_mat2[j][i])); } println!(""); } @@ -1197,6 +1422,14 @@ pub fn draw_circuit(circuit: &CircuitData) -> PyResult<()> { println!("======================"); + for i in 0..vis_mat2.num_wires() { + if let VisualizationElement2::Input(wire_input) = &vis_mat2[0][i] { + match wire_input { + WireInput::Qubit(qubit) => println!("QUBIT: {:?}", qubit), + WireInput::Clbit(clbit) => println!("CLBIT: {:?}", clbit), + } + } + } // //using circuit rep to draw circuit // let mut circuit_rep = CircuitRep::new(dag.clone()); // // println!("circuit_rep {:?}", circuit_rep); From 09a5ef4b7e6c296c84ca32372ef3a58b696cda1a Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Mon, 13 Oct 2025 13:49:46 +0530 Subject: [PATCH 24/70] minor changes in print statements --- crates/circuit/src/circuit_drawer.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index b39589f6a94f..172988bf3238 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -1313,7 +1313,7 @@ impl<'a> VisualizationLayer2<'a> { let (minima,maxima) = get_instruction_range(dag, inst); let controls = self.get_controls(inst, dag); let boxed_elements = self.get_boxed_indices(inst, dag); - println!("Adding gate {:?}\n on qubits {:?} \n with controls {:?} \n and boxed elements {:?}\n ================", inst.op.name(), qargs, controls, boxed_elements); + // println!("Adding gate {:?}\n on qubits {:?} \n with controls {:?} \n and boxed elements {:?}\n ================", inst.op.name(), qargs, controls, boxed_elements); let vert_lines = (minima..=maxima) .filter(|idx| !controls.contains(idx) && !boxed_elements.contains(idx)) .collect_vec(); @@ -1422,14 +1422,14 @@ pub fn draw_circuit(circuit: &CircuitData) -> PyResult<()> { println!("======================"); - for i in 0..vis_mat2.num_wires() { - if let VisualizationElement2::Input(wire_input) = &vis_mat2[0][i] { - match wire_input { - WireInput::Qubit(qubit) => println!("QUBIT: {:?}", qubit), - WireInput::Clbit(clbit) => println!("CLBIT: {:?}", clbit), - } - } - } + // for i in 0..vis_mat2.num_wires() { + // if let VisualizationElement2::Input(wire_input) = &vis_mat2[0][i] { + // match wire_input { + // WireInput::Qubit(qubit) => println!("QUBIT: {:?}", qubit), + // WireInput::Clbit(clbit) => println!("CLBIT: {:?}", clbit), + // } + // } + // } // //using circuit rep to draw circuit // let mut circuit_rep = CircuitRep::new(dag.clone()); // // println!("circuit_rep {:?}", circuit_rep); From 492c70aa13ee0eeabbcbdc405f8307f0555a9558 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Tue, 14 Oct 2025 14:07:27 +0530 Subject: [PATCH 25/70] Visualization Matrix implementation complete --- crates/circuit/src/circuit_drawer.rs | 152 ++++++++++++++++----------- 1 file changed, 93 insertions(+), 59 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 172988bf3238..0d0240b5a34e 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -1114,8 +1114,7 @@ impl<'a> Debug for Boxed<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Boxed::Single(label) => write!(f, "Boxed({})", label), - Boxed::Multi(Some(inst)) => write!(f, "MultiBox({})", inst.op.name()), - Boxed::Multi(None) => write!(f, "MultiBox(None)"), + Boxed::Multi(inst) => write!(f, "MultiBox({})", inst.op.name()), } } } @@ -1146,28 +1145,12 @@ enum OnWire{ Reset, } -// /// Enum for telling if an qubit in the index of a MultiBox Operation is being acted on or not. If yes, the u32 tells which index qubit it is. -// #[derive(Clone, Debug)] -// enum Effect{ -// Affected(u32), -// Unaffected -// } - -// /// Enum for representing where the element appears in the representation of a multi-box operation. -// #[derive(Clone, Debug)] -// enum MultiBoxElement{ -// Top(Effect), -// Mid(Effect), -// Bot(Effect), -// Label(String,Effect) -// } - /// Enum for representing elements that appear in a boxed operation. #[derive(Clone)] enum Boxed<'a>{ Single(String), // Multi(MultiBoxElement) - Multi(Option<&'a PackedInstruction>), + Multi(&'a PackedInstruction), } /// Enum for representing the elements stored in a visualization matrix. The elements @@ -1199,46 +1182,63 @@ impl<'a> VisualizationLayer2<'a> { /// Adds the required visualization elements to represent the given instruction fn add_instruction(&mut self, inst: &'a PackedInstruction, dag: &DAGCircuit) { - // match inst.op.view() { - // OperationRef::StandardGate(gate) | OperationRef::StandardInstruction() => self.add_gate(inst, dag), - // _ => unimplemented!("{}", format!("Visualization is not implemented for instruction of type {:?}", inst.op)), - // } - self.add_gate(inst, dag); + match inst.op.view() { + OperationRef::StandardGate(_gate) => self.add_standard(inst, dag), + OperationRef::StandardInstruction(_instruction) => self.add_standard(inst, dag), + _ => unimplemented!("{}", format!("Visualization is not implemented for instruction of type {:?}", inst.op)), + } } - fn add_standard_gate(&mut self, gate: StandardGate, inst: &'a PackedInstruction, dag: &DAGCircuit) { - self.add_gate(inst, dag); - } fn get_controls(&self, inst: &PackedInstruction, dag: &DAGCircuit) -> Vec { - let has_control = vec![StandardGate::CX, StandardGate::CCX, StandardGate::CY, StandardGate::CZ, + let gate_has_control = vec![StandardGate::CX, StandardGate::CCX, StandardGate::CY, StandardGate::CZ, StandardGate::CRX, StandardGate::CRY, StandardGate::CRZ, StandardGate::CPhase, StandardGate::CS, StandardGate::CSdg, StandardGate::CSX, StandardGate::CU, StandardGate::CU1, StandardGate::CU3, StandardGate::CH, StandardGate::C3SX, StandardGate::C3X, StandardGate::RC3X, StandardGate::RCCX]; - let mut controls = Vec::new(); - - // check if gate is standard gate - if let std_gate = inst.op.try_standard_gate().unwrap() { - if has_control.contains(&std_gate) { - let qargs = dag.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); - let target = qargs.last().unwrap(); - let (minima,maxima) = get_instruction_range(dag, inst); - - for control in minima..=maxima { - if control != *target && qargs.contains(&control) { - controls.push(control); + let inst_has_controls = vec![StandardInstruction::Measure]; + + let mut controls = vec![]; + + let std_op = inst.op.view(); + + match std_op { + OperationRef::StandardGate(gate) => { + if gate_has_control.contains(&gate) { + let qargs = dag.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); + let target = qargs.last().unwrap(); + let (minima,maxima) = get_instruction_range(dag, inst); + + for control in minima..=maxima { + if control != *target && qargs.contains(&control) { + controls.push(control); + } } - } - controls - } else { controls + } else { + vec![] + } } - } else { - controls - } + OperationRef::StandardInstruction(instruction) => { + if inst_has_controls.contains(&instruction) { + let qargs = dag.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); + let target = qargs.last().unwrap(); + let (minima,maxima) = get_instruction_range(dag, inst); + + for control in minima..=maxima { + if control != *target && qargs.contains(&control) { + controls.push(control); + } + } + controls + } else { + vec![] + } + }, + _ => vec![] + } } fn add_controls(&mut self, controls: &Vec, range: (usize, usize)) { @@ -1269,6 +1269,10 @@ impl<'a> VisualizationLayer2<'a> { StandardGate::ECR, StandardGate::RXX, StandardGate::RYY, StandardGate::RZZ, StandardGate::RZX, StandardGate::XXMinusYY, StandardGate::XXPlusYY]; + let direct_on_wire = vec![StandardGate::Swap, StandardGate::CSwap]; + + let special_cases = vec![ StandardGate::GlobalPhase, StandardGate::CPhase]; + let qargs = dag.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); let target = qargs.last().unwrap(); let range = get_instruction_range(dag, inst); @@ -1289,6 +1293,8 @@ impl<'a> VisualizationLayer2<'a> { } else { vec![] } + + // Handle special cases and direct on wire } else { vec![] } @@ -1296,6 +1302,7 @@ impl<'a> VisualizationLayer2<'a> { } fn add_boxed(&mut self, inst: &'a PackedInstruction, dag: &DAGCircuit, boxed_indices: &Vec) { + // The case of delay needs to be handled where it is multiple single qubit gates but shown as a box if boxed_indices.len() == 1 { let qargs = dag.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); let target = qargs.last().unwrap(); @@ -1303,13 +1310,29 @@ impl<'a> VisualizationLayer2<'a> { self.0[*target] = VisualizationElement2::Boxed(Boxed::Single(label)); } else if boxed_indices.len() > 1 { for idx in boxed_indices { - self.0[*idx] = VisualizationElement2::Boxed(Boxed::Multi(Some(inst))); + self.0[*idx] = VisualizationElement2::Boxed(Boxed::Multi(inst)); } } } - fn add_gate(&mut self, inst: &'a PackedInstruction, dag: &DAGCircuit) { - let qargs = dag.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); + fn add_vertical_lines(&mut self, inst: &'a PackedInstruction, dag: &DAGCircuit, vertical_lines: &Vec) { + let double_lines = vec![StandardInstruction::Measure]; + let input_type: InputType = if let Some(std_instruction) = inst.op.try_standard_instruction(){ + if double_lines.contains(&std_instruction){ + InputType::Qubit(Some("||".to_string())) + } else { + InputType::Qubit(None) + } + } else { + InputType::Qubit(None) + }; + for vline in vertical_lines { + self.0[*vline] = VisualizationElement2::VerticalLine(input_type.clone() ); + } + } + + // function to add standard gates and instructions + fn add_standard(&mut self, inst: &'a PackedInstruction, dag: &DAGCircuit) { let (minima,maxima) = get_instruction_range(dag, inst); let controls = self.get_controls(inst, dag); let boxed_elements = self.get_boxed_indices(inst, dag); @@ -1319,16 +1342,7 @@ impl<'a> VisualizationLayer2<'a> { .collect_vec(); self.add_controls(&controls, (minima, maxima)); self.add_boxed(inst, dag, &boxed_elements); - for vline in vert_lines { - let input_type = if vline < dag.num_qubits() { - InputType::Qubit(None) - } else { - InputType::Clbit(None) - }; - self.0[vline] = VisualizationElement2::VerticalLine(input_type); - } - - //self.0[qargs.last().unwrap().index()] = VisualizationElement2::Operation; + self.add_vertical_lines(inst, dag, &vert_lines); } } @@ -1401,6 +1415,26 @@ impl<'a> Index for VisualizationMatrix2<'a> { } } +struct TextDrawer{ + +} + +impl TextDrawer { + fn new() -> Self { + TextDrawer{ + + } + } + + fn create_vismat(circuit: &CircuitData, dag: &DAGCircuit) -> PyResult{ + VisualizationMatrix2::from_circuit(circuit, dag) + } + + fn get_element_width(element: &VisualizationElement2) -> u64 { + + } +} + pub fn draw_circuit(circuit: &CircuitData) -> PyResult<()> { let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; From 0474bbf6ce63775cabbf233082b407a6aef02937 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Tue, 14 Oct 2025 14:07:58 +0530 Subject: [PATCH 26/70] Visualization Matrix implementation complete --- crates/circuit/src/circuit_drawer.rs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 0d0240b5a34e..b84d819dd6f2 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -1415,25 +1415,6 @@ impl<'a> Index for VisualizationMatrix2<'a> { } } -struct TextDrawer{ - -} - -impl TextDrawer { - fn new() -> Self { - TextDrawer{ - - } - } - - fn create_vismat(circuit: &CircuitData, dag: &DAGCircuit) -> PyResult{ - VisualizationMatrix2::from_circuit(circuit, dag) - } - - fn get_element_width(element: &VisualizationElement2) -> u64 { - - } -} pub fn draw_circuit(circuit: &CircuitData) -> PyResult<()> { From b2374a988441f433e7dc88fd7c1a983cf1ca41f6 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Thu, 16 Oct 2025 17:24:38 +0530 Subject: [PATCH 27/70] text drawer, logic complete. TO DO: fix decorations --- crates/circuit/src/circuit_drawer.rs | 1776 ++++++++++---------------- 1 file changed, 706 insertions(+), 1070 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index b84d819dd6f2..4d9203e4b7a6 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -14,6 +14,7 @@ use core::panic; use std::boxed; use std::fmt::Debug; use std::io::ErrorKind; +use std::thread::current; use hashbrown::HashSet; use crate::bit::{ShareableClbit, ShareableQubit}; use crate::dag_circuit::{DAGCircuit}; @@ -39,653 +40,6 @@ pub fn py_drawer(circuit: QuantumCircuitData) -> PyResult<()> { Ok(()) } -pub const Q_WIRE: &str = "─"; -pub const C_WIRE: char = '═'; -pub const TOP_CON: char = '┴'; -pub const BOT_CON: char = '┬'; -pub const LEFT_CON: char = '┤'; -pub const RIGHT_CON: char = '├'; -pub const TOP_LEFT_CON: char = '┌'; -pub const TOP_RIGHT_CON: char = '┐'; -pub const BOT_LEFT_CON: char = '└'; -pub const BOT_RIGHT_CON: char = '┘'; -pub const BARRIER_CHAR: char = '░'; -pub const BULLET:char = '■'; -pub const CONNECTING_WIRE:char = '│'; -pub const CROSSED_WIRE:char = '┼'; - -#[derive(Clone, Debug)] -pub struct CircuitRep { - q_wires: Vec::, - dag_circ: DAGCircuit -} - -#[derive(Clone, Debug, PartialEq, Eq)] -enum WireType { - Qubit, - Clbit, -} - -impl From<&WireType> for &str { - fn from(wire_type: &WireType) -> Self { - match wire_type { - WireType::Qubit => "─", - WireType::Clbit => "═", - } - } -} - -#[derive(Clone, Debug)] -enum ControlType{ - Open, - Closed -} - -#[derive(Clone, Debug)] -pub struct input{ - -} -#[derive(Clone, Debug)] -pub struct wire { - top: String, - mid: String, - bot: String, - type_: WireType, -} - -#[derive(Clone, Debug)] -pub struct Drawable<'a>{ - packedinst: &'a PackedInstruction, - dag_circ: &'a DAGCircuit, - wire: WireType, - name: String, - label: Option, - visual_type: VisualType, -} -// pub enum DrawElementType<'a>{ -// Controlled(&'a PackedInstruction), -// Multi(&'a PackedInstruction), -// Single(&'a PackedInstruction), -// Custom(&'a Drawable<'a>) -// } - -// struct Visualizer<'a>{ -// visualization_matrix: VisualizationMatrix<'a>, -// circuit_rep: CircuitRep -// } - -#[derive(Clone, Debug)] -struct VisualizationMatrix<'a>{ - pub visualization_layers: Vec>, - packedinst_layers: Vec>, - dag_circ: &'a DAGCircuit -} - -#[derive(Clone, Debug)] -struct VisualizationLayer<'a>{ - pub elements: Vec>>, - pub drawables: Vec>, - width: u32, - // circuit_rep: &'a CircuitRep - //parent: &'a VisualizationMatrix<'a> -} - -#[derive(Clone, Debug)] -pub struct VisualizationElement<'a>{ - layer_element_index: usize, - ind: u32, - dag_circ: &'a DAGCircuit -} - -// pub struct VisualizationElement<'a>{ -// element: Option<&'a PackedInstruction>, -// ascii_string: String, -// ind: u32, -// circuit_rep: &'a CircuitRep -// } - -impl<'a> VisualizationMatrix<'a>{ - - pub fn new(dag_circ: &'a DAGCircuit, packedinst_layers: Vec>) -> Self { - // println!("Creating VisualizationMatrix..."); - // let mut temp: Vec = Vec::with_capacity(packedinst_layers.len()); - // for layer in packedinst_layers.iter() { - // let vis_layer = VisualizationLayer::new(dag_circ, layer.to_vec()); - // temp.push(vis_layer); - // } - // println!("Visualization layers created: {:?}", temp); - - // VisualizationMatrix { - // visualization_layers: temp, - // packedinst_layers, - // dag_circ, // Now we own this, so no borrowing issues - // } - println!("Creating VisualizationMatrix..."); - let mut temp: Vec = Vec::with_capacity(packedinst_layers.len()); - for layer in packedinst_layers.iter() { - let vis_layer = VisualizationLayer::new(dag_circ, layer.clone()); - temp.push(vis_layer); - } - VisualizationMatrix { - visualization_layers: temp, - packedinst_layers: packedinst_layers, - dag_circ, // Now we own this, so no borrowing issues - } - } - - pub fn print(&self){ - println!("Printing VisualizationMatrix..."); - let mut output: Vec = Vec::new(); - for layer in &self.visualization_layers{ - let layer_col = layer.get_layer_col(); - if output.is_empty(){ - output = layer_col; - } else { - // append strings in ith index to output string in ith index - for i in 0..output.len(){ - output[i].push_str(&layer_col[i]); - } - } - } - for i in output{ - println!("{}", i); - } - } - - // add wires to the circuit representation and return the full circuit representation - pub fn draw(&self, circuit_rep: &mut CircuitRep){ - println!("Drawing VisualizationMatrix..."); - for layer in &self.visualization_layers{ - let wires = layer.draw_layer(circuit_rep); - println!("wires {:?}", wires); - for (i, wire) in wires.iter().enumerate(){ - circuit_rep.q_wires[i].add_wire_component(wire); - } - } - - // self.circuit_rep.fix_len(); - println!("{}", circuit_rep.circuit_string()); - } -} - -impl<'a> VisualizationLayer<'a>{ - - pub fn new(dag_circ: &'a DAGCircuit, packedinst_layer: Vec<&'a PackedInstruction>) -> Self{ - println!("Creating VisualizationLayer..."); - //println!("{}",packedinst_layer.len()); - let mut vis_layer:Vec> = vec![None;get_indices(dag_circ) as usize]; - let mut drawable_elements = { - let mut drawable_layer:Vec = Vec::new(); - let mut drawable_ind:usize = 0; - for &inst in packedinst_layer.iter(){ - let drawable = from_instruction(dag_circ,inst); - let indices = drawable.get_wire_indices(); - // println!("drawable indices: {:?}", indices); - // println!("{:?}", vis_layer); - for ind in indices{ - let vis_element = VisualizationElement::new(drawable_ind, dag_circ, ind); - vis_layer[ind as usize] = Option::Some(vis_element); - } - drawable_ind += 1; - drawable_layer.push(drawable); - } - drawable_layer - }; - - println!("Drawable elements: {:?}", drawable_elements); - // println!("Vis layer: {:?}", vis_layer); - - let mut visualization_layer = VisualizationLayer{ - elements: vis_layer, - drawables: drawable_elements, - width: 0 - }; - visualization_layer.width = visualization_layer.set_width(); - visualization_layer - - } - - pub fn draw_layer(&self, circuit_rep: &'a CircuitRep) -> Vec{ - println!("Drawing VisualizationLayer..."); - let mut wires: Vec = Vec::new(); - let mut ct: usize = 0; - for element in self.elements.iter(){ - if let Some(vis_element) = element{ - let wire_component = vis_element.draw(&self); - wires.push(wire_component); - } else { - // if index greater than number of qubits then wiretype is clbit - if ct >= circuit_rep.dag_circ.num_qubits() as usize{ - let wire_component = wire::new(WireType::Clbit); - wires.push(wire_component); - } else { - let wire_component = wire::new(WireType::Qubit); - wires.push(wire_component); - } - } - ct += 1; - } - wires - } - - pub fn set_width(&mut self) -> u32{ - let mut max_width:u32 = 0; - - for element in self.elements.iter(){ - if let Some(vis_element) = element{ - let length = vis_element.get_length(&self); - if length > max_width{ - max_width = length; - } - } else { - if 1 > max_width{ - max_width = 1; - } - } - } - - max_width - } - - pub fn get_layer_col(&self) -> Vec{ - let mut layer_col: Vec = Vec::new(); - for element in &self.elements{ - if let Some(vis_element) = element{ - layer_col.push(format!("{}{}",vis_element.get_string(&self), " ".repeat((self.width - vis_element.get_length(&self)) as usize))); - } else { - layer_col.push(" ".repeat(self.width as usize)); - } - } - layer_col - } -} - -impl<'a> VisualizationElement<'a>{ - - pub fn new(layer_element_index: usize, dag_circ: &'a DAGCircuit,ind: u32) -> Self{ - let vis_element = VisualizationElement{ - layer_element_index, - ind, - dag_circ - }; - return vis_element; - } - - // pub fn get_string(&self, layer:&VisualizationLayer) -> String{ - // layer.drawables[self.layer_element_index].get_name() - // } - - pub fn get_length(&self, layer:&VisualizationLayer) -> u32{ - self.get_string(layer).len() as u32 - } - - pub fn get_string(&self, layer:&VisualizationLayer) -> String{ - let drawable = &layer.drawables[self.layer_element_index]; - let mut ret: String = " ".to_string(); - - let indices = drawable.get_wire_indices(); - - if self.ind == indices[0] { - ret = drawable.get_name(); - }else if indices.contains(&self.ind) { - ret = format!("{}({})", BULLET,indices[0]); - }else { - ret = <&str>::from(&drawable.wire).to_string(); - } - - format!("{}",ret) - } - - pub fn draw(&self, layer:&VisualizationLayer) -> wire{ - let drawable = &layer.drawables[self.layer_element_index]; - drawable.draw(self.ind) - } -} - -impl wire { - pub fn new(type_: WireType) -> Self { - wire { - top: String::new(), - mid: String::new(), - bot: String::new(), - type_: type_, - } - } - - pub fn get_len(&mut self) -> usize { - let top_len = self.top.len(); - let mid_len = self.mid.len(); - let bot_len = self.bot.len(); - if top_len == mid_len && mid_len == bot_len { - return mid_len; - } else { - let max_len = top_len.max(mid_len).max(bot_len); - self.fix_len(max_len); - } - self.mid.len() - } - - pub fn update_wire_len(&mut self) { - let top_len = self.top.len(); - let mid_len = self.mid.len(); - let bot_len = self.bot.len(); - if top_len == mid_len && mid_len == bot_len { - - } else { - let max_len = top_len.max(mid_len).max(bot_len); - self.fix_len(max_len); - } - } - // setting qubit names - pub fn qubit_name(&mut self, qubit_name: &str) { - let name_len = qubit_name.len(); - self.top.push_str(" ".repeat(name_len).as_str()); - self.mid.push_str(&format!("{}", qubit_name)); - self.bot.push_str(" ".repeat(name_len).as_str()); - self.update_wire_len(); - } - - // concatenate full wire representation and send for printing - pub fn get_wire_rep(&self) -> String { - let mut wire_rep = String::new(); - wire_rep.push_str(&self.top); - wire_rep.push('\n'); - wire_rep.push_str(&self.mid); - wire_rep.push('\n'); - wire_rep.push_str(&self.bot); - wire_rep.push('\n'); - wire_rep - } - - pub fn fix_len(&mut self, num: usize) { - self.top.push_str(" ".repeat(num).as_str()); - self.mid.push_str(<&str>::from(&self.type_).repeat(num).as_str()); - self.bot.push_str(" ".repeat(num).as_str()); - } - - pub fn add_wire_component(&mut self, component: &wire) { - self.top.push_str(&component.top); - self.mid.push_str(&component.mid); - self.bot.push_str(&component.bot); - self.update_wire_len(); - } -} - - -pub trait DrawElement{ - - fn get_width(&self) -> u64{ - 1 - } - -} - -// pub trait DirectOnWire{ - -// } - -#[derive(Clone, Debug, PartialEq)] -enum VisualType{ - Boxed, - MultiBoxed, - BetweenWire(String), - DirectOnWire(String), - AllWires(String) -} - -impl<'a> Drawable<'a> { - pub fn new(packedinst: &'a PackedInstruction, dag_circ: &'a DAGCircuit ,wire: WireType, name: String, label: Option, visual_type: VisualType) -> Drawable<'a>{ - Drawable { - packedinst, - dag_circ, - wire, - name, - label, - visual_type, - } - } - - pub fn draw(&self, ind: u32) -> wire{ - let mut wire_component = wire::new(self.wire.clone()); - let indices = self.get_wire_indices(); - let indices_min = indices.iter().min().unwrap().to_owned(); - let indices_max = indices.iter().max().unwrap().to_owned(); - if indices.contains(&ind){ - match &self.visual_type { - VisualType::Boxed => { - if &ind == &indices[0] { - if let Some(label) = &self.label { - let box_len = label.len() + 2; // for the box edges - wire_component.top.push_str(&format!("{}{}{}", TOP_LEFT_CON, Q_WIRE.repeat(box_len), TOP_RIGHT_CON)); - wire_component.mid.push_str(&format!("{}{}{}", LEFT_CON, label, RIGHT_CON)); - wire_component.bot.push_str(&format!("{}{}{}", BOT_LEFT_CON, Q_WIRE.repeat(box_len), BOT_RIGHT_CON)); - } else { - panic!("Boxed visual has no label"); - } - } else if ind == indices_max { - wire_component.top.push_str(&CONNECTING_WIRE.to_string()); - wire_component.mid.push_str(&BULLET.to_string()); - wire_component.bot.push_str(" "); - } - else if ind == indices_min { - wire_component.top.push_str(" "); - wire_component.mid.push_str(&BULLET.to_string()); - wire_component.bot.push_str(&CONNECTING_WIRE.to_string()); - } else { - wire_component.top.push_str(&CONNECTING_WIRE.to_string()); - wire_component.mid.push_str(&CROSSED_WIRE.to_string()); - wire_component.bot.push_str(&CONNECTING_WIRE.to_string()); - } - } - VisualType::MultiBoxed => { - let mid = (indices_min + indices_max) / 2; - let index_in_array = indices.iter().position(|&r| r == ind); - let index = match index_in_array { - Some(i) => i.to_string(), - None => " ".to_string(), - }; - if let Some(label) = &self.label{ - let box_len = label.len(); // for the box edges - if ind == indices_min { - wire_component.top.push_str(&format!("{} {}{}", TOP_LEFT_CON," ".repeat(box_len) , TOP_RIGHT_CON)); - wire_component.mid.push_str(&format!("{}{}{}{}", LEFT_CON,index,label, RIGHT_CON)); - wire_component.bot.push_str(&format!("{} {}{}", CONNECTING_WIRE, " ".repeat(box_len), CONNECTING_WIRE)); - } else if ind == indices_max { - wire_component.top.push_str(&format!("{} {}{}", CONNECTING_WIRE," ".repeat(box_len) ,CONNECTING_WIRE)); - wire_component.mid.push_str(&format!("{}{}{}{}", LEFT_CON, index ," ".repeat(box_len), RIGHT_CON)); - wire_component.bot.push_str(&format!("{} {}{}", BOT_LEFT_CON, " ".repeat(box_len), BOT_RIGHT_CON)); - } else if ind == mid{ - wire_component.top.push_str(&format!("{} {}{}", CONNECTING_WIRE," ".repeat(box_len) , CONNECTING_WIRE)); - wire_component.mid.push_str(&format!("{}{}{}{}", LEFT_CON, index, label, RIGHT_CON)); - wire_component.bot.push_str(&format!("{} {}{}",CONNECTING_WIRE, " ".repeat(box_len), CONNECTING_WIRE)); - } else { - wire_component.top.push_str(&format!("{} {}{}", CONNECTING_WIRE," ".repeat(box_len) , CONNECTING_WIRE)); - wire_component.mid.push_str(&format!("{}{}{}{}", LEFT_CON, index," ".repeat(box_len), RIGHT_CON)); - wire_component.bot.push_str(&format!("{} {}{}",CONNECTING_WIRE, " ".repeat(box_len), CONNECTING_WIRE)); - } - } else { - panic!("Boxed visual has no label"); - } - } - VisualType::BetweenWire(label) => { - if let Some(inst_label) = &self.label{ - if ind == indices_min { - wire_component.top.push_str(&format!("{}{}", " "," ".repeat(inst_label.len()))); - wire_component.mid.push_str(&format!("{}{}",label,<&str>::from(&self.wire))); - wire_component.bot.push_str(&format!("{}{}", CONNECTING_WIRE, inst_label)); - } else if ind == indices_max { - wire_component.top.push_str(&format!("{}{}", CONNECTING_WIRE," ".repeat(inst_label.len()))); - wire_component.mid.push_str(&format!("{}{}", label, <&str>::from(&self.wire))); - wire_component.bot.push_str(&format!("{}{}", " " ," ".repeat(inst_label.len()))); - } else if indices.contains(&ind){ - wire_component.top.push_str(&format!("{}{}", CONNECTING_WIRE," ".repeat(inst_label.len()))); - wire_component.mid.push_str(&format!("{}{}", label, <&str>::from(&self.wire))); - wire_component.bot.push_str(&format!("{}{}", CONNECTING_WIRE, " ".repeat(inst_label.len()))); - } else { - wire_component.top.push_str(&format!("{}{}", CONNECTING_WIRE," ".repeat(inst_label.len()))); - wire_component.mid.push_str(&format!("{}{}", CROSSED_WIRE, " ".repeat(inst_label.len()))); - wire_component.bot.push_str(&format!("{}{}", CONNECTING_WIRE ," ".repeat(inst_label.len()))); - } - } else { - panic!("BetweenWire visual has no label"); - } - } - VisualType::DirectOnWire(label) => { - let inst_label = match &self.label { - Some(l) => l, - None => label, - }; - if ind == indices_min { - wire_component.top.push_str(&format!("{}{}", " "," ".repeat(inst_label.len()))); - wire_component.mid.push_str(&format!("{}{}",label,<&str>::from(&self.wire).repeat(inst_label.len()))); - wire_component.bot.push_str(&format!("{}{}", CONNECTING_WIRE, inst_label)); - } else if ind == indices_max { - wire_component.top.push_str(&format!("{}{}", CONNECTING_WIRE," ".repeat(inst_label.len()))); - wire_component.mid.push_str(&format!("{}{}", label, <&str>::from(&self.wire).repeat(inst_label.len()))); - wire_component.bot.push_str(&format!("{}{}", " " ," ".repeat(inst_label.len()))); - } else if indices.contains(&ind){ - wire_component.top.push_str(&format!("{}{}", CONNECTING_WIRE," ".repeat(inst_label.len()))); - wire_component.mid.push_str(&format!("{}{}", label, <&str>::from(&self.wire).repeat(inst_label.len()))); - wire_component.bot.push_str(&format!("{}{}", CONNECTING_WIRE, " ".repeat(inst_label.len()))); - } else { - wire_component.top.push_str(&format!("{}{}", CONNECTING_WIRE," ".repeat(inst_label.len()))); - wire_component.mid.push_str(&format!("{}{}", CROSSED_WIRE, " ".repeat(inst_label.len()))); - wire_component.bot.push_str(&format!("{}{}", CONNECTING_WIRE ," ".repeat(inst_label.len()))); - } - } - VisualType::AllWires(label) => { - let mut inst_label = match &self.label { - Some(l) => l, - None => label, - }; - if ind == 0 { - wire_component.top.push_str(&format!("{}{}", inst_label," ".to_string())); - wire_component.mid.push_str(&format!("{}{}",label,<&str>::from(&self.wire).repeat(inst_label.len()))); - wire_component.bot.push_str(&format!("{}{}", label, " ".to_string().repeat(inst_label.len()))); - } else if indices.contains(&ind) { - wire_component.top.push_str(&format!("{}{}", label," ".repeat(inst_label.len()))); - wire_component.mid.push_str(&format!("{}{}", label,<&str>::from(&self.wire).repeat(inst_label.len()))); - wire_component.bot.push_str(&format!("{}{}", label," ".repeat(inst_label.len()))); - } - } - } - wire_component.update_wire_len(); - wire_component - } else { - panic!("Draw being called on an index which is not affected by the PackedInstruction"); - } - } - // this function returns the indices of wires than an instruction is acting upon. - // Since rust currently only has StandardInstruction and StandardGates, it always returns - // the qubit indices first, where in usually the first index is of the control qubit and the rest are of - // the target. However once ControlFlows are introduced, this handling needs to be more nuanced. - pub fn get_wire_indices(&self) -> Vec{ - let mut qubit_indices: Vec = self.dag_circ.qargs_interner().get(self.packedinst.qubits) - .iter() - .map(|qubit| qubit.0) - .collect(); - - let total_qubits:u32 = self.dag_circ.num_qubits() as u32; - - let clbit_indices: Vec = self.dag_circ.cargs_interner().get(self.packedinst.clbits) - .iter() - .map(|clbit| clbit.0 + total_qubits) - .collect(); - - qubit_indices.extend(clbit_indices.into_iter()); - - qubit_indices - } - - pub fn get_name(&self) -> String{ - self.name.clone() - } -} - -impl<'a> DrawElement for Drawable<'a>{ - fn get_width(&self) -> u64 { - let type_ = match &self.visual_type { - VisualType::Boxed | VisualType::MultiBoxed => { - if let Some(label) = &self.label { - label.len() as u64 + 2 // for the box edges - } else { - panic!("Boxed visual has no label"); - } - } - - VisualType::BetweenWire(label) => { - label.len() as u64 + 1 // for the spaces around the label - } - VisualType::DirectOnWire(label) => { - 1 as u64// no extra space needed - } - VisualType::AllWires(label) => { - 0 as u64// just a single character for the whole wire - } - }; - - return type_; - } - -} - - - -impl<'a> CircuitRep { - - pub fn new(dag_circ: DAGCircuit) -> Self { - CircuitRep { - q_wires: vec!(wire::new(WireType::Qubit); dag_circ.num_qubits()), - dag_circ: dag_circ - } - } - - pub fn circuit_string(&self) -> String { - let mut output = String::new(); - for wires in self.q_wires.iter() { - output.push_str(&wires.get_wire_rep()); - } - output - } - - pub fn get_indices(&self) -> u32{ - let total_qubits = self.dag_circ.num_qubits() as u32; - let total_clbits = self.dag_circ.num_clbits() as u32; - total_qubits + total_clbits - } - - pub fn fix_len(&mut self) { - let mut num = 0; - for wire in self.q_wires.iter_mut() { - let length = wire.get_len(); - if length > num { - num = length; - } - } - - for wire in self.q_wires.iter_mut() { - let length = wire.get_len(); - wire.fix_len(num - length); - } - } - - pub fn set_qubit_name(&mut self) { - for (i, qubit) in self.dag_circ.qubits().objects().iter().enumerate() { - let qubit_name = if let Some(locations) = self.dag_circ.qubit_locations().get(qubit) { - if let Some((register, reg_index)) = locations.registers().first() { - format!("{}_{}", register.name(), reg_index) - } else { - format!("q_{}", i) - } - } else { - format!("q_{}", i) - }; - self.q_wires[i].qubit_name(&qubit_name); - } - self.fix_len(); - } -} - /// Calculate the range (inclusive) of the given instruction qubits/clbits over the wire indices. /// The assumption is that clbits always appear after the qubits in the visualization, hence the clbit indices /// are offset by the number of instruction qubits when calculating the range. @@ -737,362 +91,12 @@ fn build_layers(dag: &DAGCircuit) -> Vec> { layers } -pub fn get_label(instruction: &PackedInstruction) -> Option{ - let label = instruction.label(); - let instruction_param =format!("{:?}",instruction.params_view()); - let instruction_label = match label { - Some(l) => { - Some(l.to_string()) - } - None => { - if let Some(standard_gate) = instruction.op.try_standard_gate() { - match standard_gate { - StandardGate::GlobalPhase => { - // Global phase gate - affects overall circuit phase - None - } - StandardGate::H => { - // Hadamard gate - creates superposition - Some("H".to_string()) - } - StandardGate::I => { - // Identity gate - no operation - Some("I".to_string()) - } - StandardGate::X => { - // Pauli-X gate (NOT gate) - Some("X".to_string()) - } - StandardGate::Y => { - // Pauli-Y gate - Some("Y".to_string()) - } - StandardGate::Z => { - // Pauli-Z gate - Some("Z".to_string()) - } - StandardGate::Phase => { - // Phase gate (parameterized) - Some(format!("P({})", instruction_param)) - } - StandardGate::R => { - // R gate (rotation about axis in XY plane) - Some(format!("R({})", instruction_param)) - } - StandardGate::RX => { - // Rotation about X axis - Some(format!("RX({})", instruction_param)) - } - StandardGate::RY => { - // Rotation about Y axis - Some(format!("RY({})", instruction_param)) - } - StandardGate::RZ => { - // Rotation about Z axis - Some(format!("RZ({})", instruction_param)) - } - StandardGate::S => { - // S gate (phase π/2) - Some("S".to_string()) - } - StandardGate::Sdg => { - // S dagger gate (phase -π/2) - Some("S†".to_string()) - } - StandardGate::SX => { - // Square root of X gate - Some("√X".to_string()) - } - StandardGate::SXdg => { - // Square root of X dagger gate - Some("√X†".to_string()) - } - StandardGate::T => { - // T gate (phase π/4) - Some("T".to_string()) - } - StandardGate::Tdg => { - // T dagger gate (phase -π/4) - Some("T†".to_string()) - } - StandardGate::U => { - // Universal single-qubit gate (3 parameters) - Some(format!("U({})", instruction_param)) - } - StandardGate::U1 => { - // U1 gate (1 parameter - phase) - Some(format!("U1({})", instruction_param)) - } - StandardGate::U2 => { - // U2 gate (2 parameters) - Some(format!("U2({})", instruction_param)) - } - StandardGate::U3 => { - // U3 gate (3 parameters - equivalent to U) - Some(format!("U3({})", instruction_param)) - } - StandardGate::CH => { - // Controlled Hadamard gate - Some("H".to_string()) - } - StandardGate::CX => { - // Controlled-X gate (CNOT) - Some("X".to_string()) - } - StandardGate::CY => { - // Controlled-Y gate - Some("Y".to_string()) - } - StandardGate::CZ => { - // Controlled-Z gate - Some("Z".to_string()) - } - StandardGate::DCX => { - // Double CNOT gate - Some("DCX".to_string()) - } - StandardGate::ECR => { - // Echoed cross-resonance gate - Some("ECR".to_string()) - } - StandardGate::Swap => { - // Swap gate - None - } - StandardGate::ISwap => { - // i-Swap gate - None - } - StandardGate::CPhase => { - // Controlled phase gate - - Some(format!("P({})", instruction_param)) - } - StandardGate::CRX => { - // Controlled rotation about X - Some(format!("RX({})", instruction_param)) - } - StandardGate::CRY => { - // Controlled rotation about Y - Some(format!("RY({})", instruction_param)) - } - StandardGate::CRZ => { - // Controlled rotation about Z - Some(format!("RZ({})", instruction_param)) - } - StandardGate::CS => { - // Controlled S gate - Some("S".to_string()) - } - StandardGate::CSdg => { - // Controlled S dagger gate - Some("S†".to_string()) - } - StandardGate::CSX => { - // Controlled square root of X gate - Some("√X".to_string()) - } - StandardGate::CU => { - // Controlled U gate (4 parameters) - Some(format!("U({})", instruction_param)) - } - StandardGate::CU1 => { - // Controlled U1 gate - Some(format!("U1({})", instruction_param)) - } - StandardGate::CU3 => { - // Controlled U3 gate - Some(format!("U3({})", instruction_param)) - } - StandardGate::RXX => { - // Two-qubit XX rotation - Some(format!("RXX({})", instruction_param)) - } - StandardGate::RYY => { - // Two-qubit YY rotation - Some(format!("RYY({})", instruction_param)) - } - StandardGate::RZZ => { - // Two-qubit ZZ rotation - Some(format!("RZZ({})", instruction_param)) - } - StandardGate::RZX => { - // Two-qubit ZX rotation - Some(format!("RZX({})", instruction_param)) - } - StandardGate::XXMinusYY => { - // XX-YY gate - Some(format!("XX-YY({})", instruction_param)) - } - StandardGate::XXPlusYY => { - // XX+YY gate - Some(format!("XX+YY({})", instruction_param)) - } - StandardGate::CCX => { - // Toffoli gate (controlled-controlled-X) - Some("X".to_string()) - } - StandardGate::CCZ => { - // Controlled-controlled-Z gate - Some("Z".to_string()) - } - StandardGate::CSwap => { - // Controlled swap gate (Fredkin gate) - None - } - StandardGate::RCCX => { - // Relative-phase Toffoli gate - Some(format!("RX({})", instruction_param)) - } - StandardGate::C3X => { - // 3-controlled X gate (4-qubit controlled X) - Some("X".to_string()) - } - StandardGate::C3SX => { - // 3-controlled square root of X gate - Some("√X".to_string()) - } - StandardGate::RC3X => { - // Relative-phase 3-controlled X gate - Some(format!("RX({})", instruction_param)) - } - } - } else { - Some(instruction.op.name().to_string()) - } - }, - }; - instruction_label -} - -pub fn from_instruction<'a>(dag_circ: &'a DAGCircuit, instruction: &'a PackedInstruction) -> Drawable<'a>{ - // println!("{:?}", instruction.label()); - let instruction_name = instruction.op.name(); - if let Some(standard_gate) = instruction.op.try_standard_gate() { - let mut instruction_label: Option = get_label(instruction); - - let label = instruction.label(); - instruction_label = match label { - Some(l) => { - if l != "" { - Some(l.to_string()) - } else { - instruction_label - } - } - None => instruction_label, - }; - - // TO DO:🥀 - // - // assign the correct visual type to all gates and instructions - // - // - - let visual_type = match standard_gate { - StandardGate::GlobalPhase => VisualType::BetweenWire(instruction.params_view().get(0).map_or("".to_string(), |p| format!("{:?}", p))), - StandardGate::Swap | StandardGate::CSwap => VisualType::DirectOnWire("X".to_string()), - StandardGate::CPhase => VisualType::BetweenWire(instruction_label.clone().unwrap()), - _ => VisualType::Boxed, - }; - - // for label debugging - // println!("{}{}",instruction_label.clone().unwrap_or("".to_string()), instruction_name); - Drawable { - packedinst: &instruction, - wire: WireType::Qubit, - dag_circ: &dag_circ, - name: instruction_name.to_string(), - label: instruction_label, - visual_type: visual_type, - } - } - else if let Some(standard_instruction) = instruction.op.try_standard_instruction() { - match standard_instruction { - StandardInstruction::Measure =>{ - Drawable{ - packedinst: &instruction, - wire: WireType::Qubit, - dag_circ: &dag_circ, - name: "Measure".to_string(), - label: Some("M".to_string()), - visual_type: VisualType::Boxed, - } - }, - StandardInstruction::Barrier(x) => { - - let label = instruction.label(); - let inst_label = match label{ - None => BARRIER_CHAR.to_string(), - Some("") => BARRIER_CHAR.to_string(), - Some(label) => label.to_string(), - }; - - println!("barrier label: {}", inst_label); - - Drawable{ - packedinst: &instruction, - wire: WireType::Qubit, - dag_circ: &dag_circ, - name: "Barrier".to_string(), - label: Some(inst_label.clone()), - visual_type: VisualType::AllWires(BARRIER_CHAR.to_string()), - } - }, - StandardInstruction::Reset => { - Drawable{ - packedinst: &instruction, - wire: WireType::Qubit, - dag_circ: &dag_circ, - name: "Reset".to_string(), - label: Some("|0>".to_string()), - visual_type: VisualType::DirectOnWire("|0>".to_string()), - } - }, - StandardInstruction::Delay(_unit) => { - - let unit = format!("{:?}", _unit).to_lowercase(); - - let label = if let Some(dur) = instruction.params_view().get(0){ - format!("Delay({:?}[{:?}])", dur, unit) - } else { - "Delay".to_string() - }; - - Drawable{ - packedinst: &instruction, - wire: WireType::Qubit, - dag_circ: &dag_circ, - name: "Delay".to_string(), - label: Some(label.clone()), - visual_type: VisualType::Boxed, - } - } - } - } - else { - // print all operation details - println!("Unsupported operation details:"); - //get operator discriminant - // let PackedOperation(discriminant) = instruction.op; - // println!("{064b}", discriminant); - println!("Name: {}", instruction.op.name()); - println!("Parameters: {:?}", instruction.params_view()); - println!("Qubits: {:?}", dag_circ.qargs_interner().get(instruction.qubits)); - println!("Clbits: {:?}", dag_circ.cargs_interner().get(instruction.clbits)); - panic!("Unsupported operation: {}", instruction.op.name()); - - } -} - pub fn get_indices(dag_circ: &DAGCircuit) -> u32{ let total_qubits = dag_circ.num_qubits() as u32; let total_clbits = dag_circ.num_clbits() as u32; total_qubits + total_clbits } - - #[derive(Clone)] enum WireInput<'a> { Qubit(&'a ShareableQubit), @@ -1110,15 +114,6 @@ impl<'a> Debug for WireInput<'a> { } } -impl<'a> Debug for Boxed<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Boxed::Single(label) => write!(f, "Boxed({})", label), - Boxed::Multi(inst) => write!(f, "MultiBox({})", inst.op.name()), - } - } -} - /// Input Wires. /// The Option is for optional labels and can be used when the registers have different names. This allows us to use /// the same enum for both Input and VerticalLine types. @@ -1129,18 +124,18 @@ enum InputType{ } /// Enum for representing elements that can appear directly on a wire. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Copy)] enum ElementOnWire{ - Control, - Swap + Top, + Mid, + Bot, } /// Enum for representing elements that appear directly on a wire and how they're connected. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Copy)] enum OnWire{ - Top(ElementOnWire), - Mid(ElementOnWire), - Bot(ElementOnWire), + Control(ElementOnWire), + Swap(ElementOnWire), Barrier, Reset, } @@ -1148,36 +143,46 @@ enum OnWire{ /// Enum for representing elements that appear in a boxed operation. #[derive(Clone)] enum Boxed<'a>{ - Single(String), + Single(&'a PackedInstruction), // Multi(MultiBoxElement) Multi(&'a PackedInstruction), } +impl<'a> Debug for Boxed<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Boxed::Single(inst) => write!(f, "Boxed({})", inst.op.name()), + Boxed::Multi(inst) => write!(f, "MultiBox({})", inst.op.name()), + } + } +} + + /// Enum for representing the elements stored in a visualization matrix. The elements /// do not directly implement visualization capabilities, but rather carry enough information /// to enable visualization later on by the actual drawer. #[derive(Default, Clone, Debug)] -enum VisualizationElement2<'a>{ +enum VisualizationElement<'a>{ #[default] Empty, // Marker for no element Boxed(Boxed<'a>), - Input(WireInput<'a>), // TODO: should be enum for qubit/clbits - DirectOnWire(OnWire), // TODO: should be an enum for the control symbols, e.g. closed, open - VerticalLine(InputType), // TODO: should be an enum for the various types, e.g. single, double - Operation, // TODO: should be an enum for the various fine-grained types: standard gates, instruction, etc.. + Input(WireInput<'a>), + DirectOnWire(OnWire), + VerticalLine(InputType), + // Operation, } /// A representation of a single column (called here a layer) of a visualization matrix #[derive(Clone, Debug)] -struct VisualizationLayer2<'a>(Vec>); +struct VisualizationLayer<'a>(Vec>); -impl<'a> VisualizationLayer2<'a> { +impl<'a> VisualizationLayer<'a> { fn len(&self) -> usize { self.0.len() } fn add_input(&mut self, input: WireInput<'a>, idx: usize) { - self.0[idx] = VisualizationElement2::Input(input); + self.0[idx] = VisualizationElement::Input(input); } /// Adds the required visualization elements to represent the given instruction @@ -1244,11 +249,11 @@ impl<'a> VisualizationLayer2<'a> { fn add_controls(&mut self, controls: &Vec, range: (usize, usize)) { for control in controls { if *control == range.0 { - self.0[*control] = VisualizationElement2::DirectOnWire(OnWire::Top(ElementOnWire::Control)); + self.0[*control] = VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Top)); } else if *control == range.1 { - self.0[*control] = VisualizationElement2::DirectOnWire(OnWire::Bot(ElementOnWire::Control)); + self.0[*control] = VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Bot)); } else { - self.0[*control] = VisualizationElement2::DirectOnWire(OnWire::Mid(ElementOnWire::Control)); + self.0[*control] = VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Mid)); } } } @@ -1306,11 +311,10 @@ impl<'a> VisualizationLayer2<'a> { if boxed_indices.len() == 1 { let qargs = dag.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); let target = qargs.last().unwrap(); - let label = get_label(inst).unwrap_or_else(|| inst.op.name().to_string()); - self.0[*target] = VisualizationElement2::Boxed(Boxed::Single(label)); + self.0[*target] = VisualizationElement::Boxed(Boxed::Single(inst)); } else if boxed_indices.len() > 1 { for idx in boxed_indices { - self.0[*idx] = VisualizationElement2::Boxed(Boxed::Multi(inst)); + self.0[*idx] = VisualizationElement::Boxed(Boxed::Multi(inst)); } } } @@ -1327,7 +331,7 @@ impl<'a> VisualizationLayer2<'a> { InputType::Qubit(None) }; for vline in vertical_lines { - self.0[*vline] = VisualizationElement2::VerticalLine(input_type.clone() ); + self.0[*vline] = VisualizationElement::VerticalLine(input_type.clone() ); } } @@ -1346,8 +350,8 @@ impl<'a> VisualizationLayer2<'a> { } } -impl<'a> Index for VisualizationLayer2<'a> { - type Output = VisualizationElement2<'a>; +impl<'a> Index for VisualizationLayer<'a> { + type Output = VisualizationElement<'a>; fn index(&self, index: usize) -> &Self::Output { &self.0[index] @@ -1360,18 +364,17 @@ impl<'a> Index for VisualizationLayer2<'a> { /// layer(column) represents the qubits and clbits inputs in the circuits, and /// M is the number of operation layers. #[derive(Debug)] -struct VisualizationMatrix2<'a> { - layers: Vec>, +struct VisualizationMatrix<'a> { + layers: Vec>, } - -impl<'a> VisualizationMatrix2<'a> { +impl<'a> VisualizationMatrix<'a> { fn from_circuit(circuit: &'a CircuitData, dag: &'a DAGCircuit) -> PyResult { let inst_layers = build_layers(&dag); let num_wires = circuit.num_qubits() + circuit.num_clbits(); - let mut layers = vec![VisualizationLayer2(vec![VisualizationElement2::default(); num_wires]); inst_layers.len() + 1]; // Add 1 to account for the inputs layer + let mut layers = vec![VisualizationLayer(vec![VisualizationElement::default(); num_wires]); inst_layers.len() + 1]; // Add 1 to account for the inputs layer // TODO: add the qubit/clbit inputs here to layer #0 @@ -1393,7 +396,7 @@ impl<'a> VisualizationMatrix2<'a> { } } - Ok(VisualizationMatrix2{ + Ok(VisualizationMatrix{ layers, }) } @@ -1407,20 +410,681 @@ impl<'a> VisualizationMatrix2<'a> { } } -impl<'a> Index for VisualizationMatrix2<'a> { - type Output = VisualizationLayer2<'a>; +impl<'a> Index for VisualizationMatrix<'a> { + type Output = VisualizationLayer<'a>; fn index(&self, index: usize) -> &Self::Output { &self.layers[index] } } +#[derive(Clone,Debug)] +struct Wire{ + top:String, + mid:String, + bot:String, +} + +impl Wire{ + fn width(&self) -> usize{ + // return the max of all strigns + // self.top.len().max(self.mid.len()).max(self.bot.len()) + let top = self.top.chars().count(); + let mid = self.mid.chars().count(); + let bot = self.bot.chars().count(); + // println!("top:{}, mid:{}, bot:{}", top, mid, bot); + // println!("{}", top.max(mid).max(bot)); + let max = { + if top >= mid && top >= bot { + top + } else if mid >= top && mid >= bot { + mid + } else { + bot + } + }; + // println!("max: {}", max); + // println!("{}\n{}\n{}", self.top, self.mid, self.bot); + max + } + + fn left_pad_string(s: &mut String, pad_char: char, width: usize){ + let current_width = s.len(); + if current_width < width{ + let pad_size = width - current_width; + let pad_str = pad_char.to_string().repeat(pad_size); + let new_str = format!("{}{}", pad_str, s); + *s = new_str; + } + } + + fn right_pad_string(s: &mut String, pad_char: char, width: usize){ + let current_width = s.len(); + if current_width < width{ + let pad_size = width - current_width; + let pad_str = pad_char.to_string().repeat(pad_size); + let new_str = format!("{}{}", s, pad_str); + *s = new_str; + } + } + + fn pad_string(s: &mut String, pad_char: char, width: usize){ + let current_width = s.chars().count(); + if current_width < width{ + let pad_size = width - current_width; + let left_pad = pad_size / 2; + let right_pad = pad_size - left_pad; + let left_pad_str = pad_char.to_string().repeat(left_pad); + let right_pad_str = pad_char.to_string().repeat(right_pad); + let new_str = format!("{}{}{}", left_pad_str, s, right_pad_str); + *s = new_str; + } + } + + fn pad_wire(&mut self, mid_char: char, width: usize){ + let current_width = self.width(); + if current_width < width{ + // let pad_size = width - current_width; + // let left_pad = pad_size / 2 - 1; + // let right_pad = pad_size - left_pad - 1; + // let left_pad_str = mid_char.to_string().repeat(left_pad); + // let right_pad_str = mid_char.to_string().repeat(right_pad); + // self.top = format!("{}{}{}", " ".to_string().repeat(left_pad), self.top, " ".to_string().repeat(right_pad)); + // self.mid = format!("{}{}{}", left_pad_str, self.mid, right_pad_str); + Self::pad_string(&mut self.top, ' ', width); + Self::pad_string(&mut self.mid, mid_char, width); + Self::pad_string(&mut self.bot, ' ', width); + } + //println!("layer width:{}, object width:{} : \n{}\n{}\n{}", width, current_width, self.top, self.mid, self.bot); + } +} + +struct CircuitRep{ + wires: Vec, +} + +impl CircuitRep{ + fn print(&self){ + for wire in &self.wires{ + println!("{}\n{}\n{}", wire.top, wire.mid, wire.bot); + } + } + + fn add_wire(&mut self, wire: &Wire, ind: usize){ + self.wires[ind].top.push_str(&wire.top); + self.wires[ind].mid.push_str(&wire.mid); + self.wires[ind].bot.push_str(&wire.bot); + // self.wires[ind].top.push_str(&format!("{}{}",&wire.top,"$")); + // self.wires[ind].mid.push_str(&format!("{}{}",&wire.mid,"$")); + // self.wires[ind].bot.push_str(&format!("{}{}",&wire.bot,"$")); + } +} + +impl Index for CircuitRep{ + type Output = Wire; + + fn index(&self, index: usize) -> &Self::Output { + &self.wires[index] + } +} + +pub const Q_WIRE: char = '─'; +pub const C_WIRE: char = '═'; +pub const TOP_CON: char = '┴'; +pub const BOT_CON: char = '┬'; +pub const Q_LEFT_CON: char = '┤'; +pub const Q_RIGHT_CON: char = '├'; +pub const CL_LEFT_CON: char = '╡'; +pub const CL_RIGHT_CON: char = '╞'; +pub const TOP_LEFT_BOX: char = '┌'; +pub const TOP_RIGHT_BOX: char = '┐'; +pub const BOT_LEFT_BOX: char = '└'; +pub const BOT_RIGHT_BOX: char = '┘'; +pub const BARRIER: char = '░'; +pub const BULLET:char = '■'; +pub const CONNECTING_WIRE:char = '│'; +pub const CROSSED_WIRE:char = '┼'; +pub const CL_CROSSED_WIRE:char = '╪'; + +struct TextDrawer; + +impl TextDrawer{ + + fn get_label(instruction: &PackedInstruction) -> Option{ + let label = instruction.label(); + let instruction_param =format!("{:?}",instruction.params_view()); + let instruction_label = match label { + Some(l) => { + Some(format!("{}{}{}"," ",l.to_string()," ")) + } + None => { + if let Some(standard_gate) = instruction.op.try_standard_gate() { + match standard_gate { + StandardGate::GlobalPhase => { + // Global phase gate - affects overall circuit phase + None + } + StandardGate::H => { + // Hadamard gate - creates superposition + Some(" H ".to_string()) + } + StandardGate::I => { + // Identity gate - no operation + Some(" I ".to_string()) + } + StandardGate::X => { + // Pauli-X gate (NOT gate) + Some(" X ".to_string()) + } + StandardGate::Y => { + // Pauli-Y gate + Some(" Y ".to_string()) + } + StandardGate::Z => { + // Pauli-Z gate + Some(" Z ".to_string()) + } + StandardGate::Phase => { + // Phase gate (parameterized) + Some(format!(" P({}) ", instruction_param)) + } + StandardGate::R => { + // R gate (rotation about axis in XY plane) + Some(format!(" R({}) ", instruction_param)) + } + StandardGate::RX => { + // Rotation about X axis + Some(format!(" RX({}) ", instruction_param)) + } + StandardGate::RY => { + // Rotation about Y axis + Some(format!(" RY({}) ", instruction_param)) + } + StandardGate::RZ => { + // Rotation about Z axis + Some(format!(" RZ({}) ", instruction_param)) + } + StandardGate::S => { + // S gate (phase π/2) + Some(" S ".to_string()) + } + StandardGate::Sdg => { + // S dagger gate (phase -π/2) + Some(" Sdg ".to_string()) + } + StandardGate::SX => { + // Square root of X gate + Some(" √X ".to_string()) + } + StandardGate::SXdg => { + // Square root of X dagger gate + Some(" √Xdg ".to_string()) + } + StandardGate::T => { + // T gate (phase π/4) + Some(" T ".to_string()) + } + StandardGate::Tdg => { + // T dagger gate (phase -π/4) + Some(" T† ".to_string()) + } + StandardGate::U => { + // Universal single-qubit gate (3 parameters) + Some(format!(" U({}) ", instruction_param)) + } + StandardGate::U1 => { + // U1 gate (1 parameter - phase) + Some(format!(" U1({}) ", instruction_param)) + } + StandardGate::U2 => { + // U2 gate (2 parameters) + Some(format!(" U2({}) ", instruction_param)) + } + StandardGate::U3 => { + // U3 gate (3 parameters - equivalent to U) + Some(format!(" U3({}) ", instruction_param)) + } + StandardGate::CH => { + // Controlled Hadamard gate + Some(" H ".to_string()) + } + StandardGate::CX => { + // Controlled-X gate (CNOT) + Some(" X ".to_string()) + } + StandardGate::CY => { + // Controlled-Y gate + Some(" Y ".to_string()) + } + StandardGate::CZ => { + // Controlled-Z gate + Some(" Z ".to_string()) + } + StandardGate::DCX => { + // Double CNOT gate + Some(" DCX ".to_string()) + } + StandardGate::ECR => { + // Echoed cross-resonance gate + Some(" ECR ".to_string()) + } + StandardGate::Swap => { + // Swap gate + None + } + StandardGate::ISwap => { + // i-Swap gate + Some(" iSWAP ".to_string()) + } + StandardGate::CPhase => { + // Controlled phase gate + + Some(format!(" P({}) ", instruction_param)) + } + StandardGate::CRX => { + // Controlled rotation about X + Some(format!(" RX({}) ", instruction_param)) + } + StandardGate::CRY => { + // Controlled rotation about Y + Some(format!(" RY({}) ", instruction_param)) + } + StandardGate::CRZ => { + // Controlled rotation about Z + Some(format!(" RZ({}) ", instruction_param)) + } + StandardGate::CS => { + // Controlled S gate + Some(" S ".to_string()) + } + StandardGate::CSdg => { + // Controlled S dagger gate + Some(" Sdg ".to_string()) + } + StandardGate::CSX => { + // Controlled square root of X gate + Some(" √X ".to_string()) + } + StandardGate::CU => { + // Controlled U gate (4 parameters) + Some(format!(" U({}) ", instruction_param)) + } + StandardGate::CU1 => { + // Controlled U1 gate + Some(format!(" U1({}) ", instruction_param)) + } + StandardGate::CU3 => { + // Controlled U3 gate + Some(format!(" U3({}) ", instruction_param)) + } + StandardGate::RXX => { + // Two-qubit XX rotation + Some(format!(" RXX({}) ", instruction_param)) + } + StandardGate::RYY => { + // Two-qubit YY rotation + Some(format!(" RYY({}) ", instruction_param)) + } + StandardGate::RZZ => { + // Two-qubit ZZ rotation + Some(format!(" RZZ({}) ", instruction_param)) + } + StandardGate::RZX => { + // Two-qubit ZX rotation + Some(format!(" RZX({}) ", instruction_param)) + } + StandardGate::XXMinusYY => { + // XX-YY gate + Some(format!(" XX-YY({}) ", instruction_param)) + } + StandardGate::XXPlusYY => { + // XX+YY gate + Some(format!(" XX+YY({}) ", instruction_param)) + } + StandardGate::CCX => { + // Toffoli gate (controlled-controlled-X) + Some(" X ".to_string()) + } + StandardGate::CCZ => { + // Controlled-controlled-Z gate + Some(" Z ".to_string()) + } + StandardGate::CSwap => { + // Controlled swap gate (Fredkin gate) + None + } + StandardGate::RCCX => { + // Relative-phase Toffoli gate + Some(format!(" RX({}) ", instruction_param)) + } + StandardGate::C3X => { + // 3-controlled X gate (4-qubit controlled X) + Some(" X ".to_string()) + } + StandardGate::C3SX => { + // 3-controlled square root of X gate + Some(" √X ".to_string()) + } + StandardGate::RC3X => { + // Relative-phase 3-controlled X gate + Some(format!(" RX({}) ", instruction_param)) + } + } + } else if let Some(std_instruction) = instruction.op.try_standard_instruction(){ + if std_instruction == StandardInstruction::Measure{ + Some(" M ".to_string()) + } else if std_instruction == StandardInstruction::Reset{ + Some("|0>".to_string()) + } else if let StandardInstruction::Barrier(_) = std_instruction { + Some(" ░ ".to_string()) + } else { + // Fallback for non-standard instructions + Some(format!("{}{}{}"," ",instruction.op.name().to_string()," ")) + } + } else { + // Fallback for non-standard operations + Some(format!("{}{}{}"," ",instruction.op.name().to_string()," ")) + } + }, + }; + instruction_label + } + + fn draw_matrix(vis_mat: &VisualizationMatrix, circ_data: &CircuitData) -> CircuitRep{ + let mut wires: Vec = vec![]; + for _ in 0..vis_mat.num_wires(){ + wires.push(Wire{ + top: String::new(), + mid: String::new(), + bot: String::new(), + }); + } + + let mut circuit_rep = CircuitRep{ + wires + }; + + let mut ct = 0; + for layer in &vis_mat.layers{ + let layer_wires = Self::draw_layer(layer, circ_data, ct); + ct += 1; + for (i, wire) in layer_wires.iter().enumerate(){ + circuit_rep.add_wire(wire, i); + } + } + + circuit_rep + } + + // fn get_layer_width(&self, layer: &VisualizationLayer, circ_data: &CircuitData) -> usize{ + // let mut width:usize = 0; + // for element in layer.0.iter(){ + // let ele_width = Self::get_element_width(self, &element, circ_data); + // if ele_width > width{ + // width = ele_width; + // } + // } + // width + // } + + // fn get_element_width(&self, element: &VisualizationElement, circ_data: &CircuitData) -> usize{ + // Self::draw_element(element.clone(), circ_data, 0).width() + // } + + fn draw_layer(layer: &VisualizationLayer, circ_data: &CircuitData, layer_ind: usize) -> Vec{ + let mut wires: Vec = vec![]; + for (i,element) in layer.0.iter().enumerate(){ + let wire = Self::draw_element(element.clone(), circ_data,i); + wires.push(wire); + } + + let num_qubits = circ_data.num_qubits(); + + //let layer_width = wires.iter().map(|w| w.width()).max().unwrap_or(0); + let mut layer_width = 0; + for wire in wires.iter(){ + let w = wire.width(); + if w > layer_width{ + layer_width = w; + } + } + + for (i,wire) in wires.iter_mut().enumerate(){ + if layer_ind == 0{ + wire.pad_wire(' ', layer_width); + } else if i < num_qubits{ + wire.pad_wire(Q_WIRE, layer_width); + // wire.pad_wire('$', layer_width); + } else { + wire.pad_wire(C_WIRE, layer_width); + // wire.pad_wire('$', layer_width); + } + } + + wires + } + + pub fn draw_element(vis_ele: VisualizationElement, circ_data: &CircuitData, ind: usize) -> Wire { + match vis_ele { + VisualizationElement::Boxed(sub_type) => { + // implement for cases where the box is on classical wires. The left and right connectors will change + // from single wired to double wired. + + match sub_type { + Boxed::Single(inst) => { + let label = Self::get_label(inst).unwrap_or(" ".to_string()); + Wire{ + top: format!("{}{}{}", TOP_LEFT_BOX, Q_WIRE.to_string().repeat(label.len()), TOP_RIGHT_BOX), + mid: format!("{}{}{}", Q_LEFT_CON , label, Q_RIGHT_CON), + bot: format!("{}{}{}", BOT_LEFT_BOX, Q_WIRE.to_string().repeat(label.len()), BOT_RIGHT_BOX), + } + }, + Boxed::Multi(inst) => { + let label = Self::get_label(inst).unwrap_or(" ".to_string()); + // get all the indices affected by this multi-box + let qargs = circ_data.qargs_interner().get(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); + let cargs = circ_data.cargs_interner().get(inst.clbits).into_iter().map(|c| c.index() + circ_data.num_qubits()).collect_vec(); + let minmax = qargs.iter().chain(cargs.iter()).minmax(); + let range = match minmax { + MinMaxResult::MinMax(min, max) => (*min, *max), + MinMaxResult::OneElement(idx) => (*idx, *idx), + MinMaxResult::NoElements => panic!("Encountered an multi-qubit without qubits and clbits") + }; + let mid = (range.0 + range.1) / 2; + + let num_affected = { + if qargs.contains(&ind) || cargs.contains(&ind) { + // Once the packed instruction can handle custom gates, we need to first check the number of controls + // and then give an index to the qubit in the multibox an index based on whats left. For example, if the + // qubits being affected are [0,1,2,3,4,5] and num controls is 2, then the qubits will be indexed as [C,C,T,T,T,T] + // so for qubits [2,3,4,5] the indexes inside the box will be 0,1,2,3 respectively. + + // get index of ind inside qargs or cargs + let temp: String = { + if qargs.contains(&ind) { + let idx = qargs.iter().position(|&x| x == ind).unwrap(); + format!("{:^width$}", idx, width = qargs.len()) + } + else { + " ".to_string() + } + }; + temp + } else { + " ".to_string() + } + }; + + let mid_section = if ind == mid { + format!("{:^total_q$} {:^label_len$}", num_affected, label, total_q = qargs.len(), label_len=label.len()) + } else { + format!("{:^total_q$} {:^label_len$}", num_affected, " ", total_q = qargs.len(), label_len=label.len()) + }; + + if ind == range.0 { + Wire{ + top: format!("{}{}{}", TOP_LEFT_BOX, Q_WIRE.to_string().repeat(mid_section.len()), TOP_RIGHT_BOX), + mid: format!("{}{}{}", Q_LEFT_CON, mid_section, Q_RIGHT_CON), + bot: format!("{}{}{}", CONNECTING_WIRE, " ".repeat(mid_section.len()), CONNECTING_WIRE), + } + } else if ind == range.1 { + Wire{ + top: format!("{}{}{}", CONNECTING_WIRE," ".to_string().repeat(mid_section.len()), CONNECTING_WIRE), + mid: format!("{}{}{}", Q_LEFT_CON, mid_section, Q_RIGHT_CON), + bot: format!("{}{}{}", BOT_LEFT_BOX, Q_WIRE.to_string().repeat(mid_section.len()), BOT_RIGHT_BOX), + } + } else { + Wire{ + top: format!("{}{}{}", CONNECTING_WIRE, " ".repeat(mid_section.len()), CONNECTING_WIRE), + mid: format!("{}{}{}", Q_LEFT_CON, mid_section, Q_RIGHT_CON), + bot: format!("{}{}{}", CONNECTING_WIRE, " ".repeat(mid_section.len()), CONNECTING_WIRE), + } + } + } + } + }, + VisualizationElement::DirectOnWire(on_wire) => { + let wire_char = match on_wire { + OnWire::Control(position) => { + BULLET + }, + OnWire::Swap(position) => { + 'X' + }, + OnWire::Barrier => BARRIER, + OnWire::Reset => '0', + }; + + let top:String = match on_wire{ + OnWire::Control(position) => { + match position { + ElementOnWire::Top => " ".to_string(), + ElementOnWire::Mid => format!("{}", CONNECTING_WIRE), + ElementOnWire::Bot => format!("{}", CONNECTING_WIRE), + } + }, + OnWire::Swap(position) => { + match position { + ElementOnWire::Top => " ".to_string(), + ElementOnWire::Mid => format!("{}", CONNECTING_WIRE), + ElementOnWire::Bot => format!("{}", CONNECTING_WIRE), + } + }, + OnWire::Barrier => { + format!("{}", BARRIER) + }, + OnWire::Reset => { + " ".to_string() + }, + }; + + let bot: String = match on_wire{ + OnWire::Control(position) => { + match position { + ElementOnWire::Top => format!("{}", CONNECTING_WIRE), + ElementOnWire::Mid => format!("{}", CONNECTING_WIRE), + ElementOnWire::Bot => " ".to_string(), + } + }, + OnWire::Swap(position) => { + match position { + ElementOnWire::Top => format!("{}", CONNECTING_WIRE), + ElementOnWire::Mid => format!("{}", CONNECTING_WIRE), + ElementOnWire::Bot => " ".to_string(), + } + }, + OnWire::Barrier => { + format!("{}", BARRIER) + }, + OnWire::Reset => { + " ".to_string() + }, + }; + + Wire{ + top, + mid: format!("{}", wire_char), + bot, + } + + }, + VisualizationElement::Input(wire_input) => { + match wire_input { + WireInput::Qubit(qubit) => { + let qubit_name = if let Some(bit_info) = circ_data.qubit_indices().get(qubit) { + if let Some((register, index)) = bit_info.registers().first() { + format!("{}_{}:", register.name(), index) + } else { + format!("q_{}:", ind) + } + } else { + format!("q_{}:", ind) + }; + Wire{ + top: format!("{}", " ".repeat(qubit_name.len())), + mid: format!("{}", qubit_name), + bot: format!("{}", " ".repeat(qubit_name.len())), + } + } + WireInput::Clbit(clbit) => { + let clbit_name = if let Some(bit_info) = circ_data.clbit_indices().get(clbit) { + if let Some((register, index)) = bit_info.registers().first() { + format!("{}_{}:", register.name(), index) + } else { + format!("c_{}:", ind) + } + } else { + format!("c_{}:", ind) + }; + Wire{ + top: format!("{}", " ".repeat(clbit_name.len())), + mid: format!("{}", clbit_name), + bot: format!("{}", " ".repeat(clbit_name.len())), + } + }, + } + }, + // VisualizationElement::Operation => { + // Wire{ + // top: format!(" "), + // mid: format!(" "), + // bot: format!(" "), + // } + // }, + VisualizationElement::VerticalLine(input_type) => { + match input_type { + InputType::Qubit(label) => { + Wire{ + top: format!("{}", CONNECTING_WIRE), + mid: format!("{}", CROSSED_WIRE), + bot: format!("{}", CONNECTING_WIRE), + } + }, + InputType::Clbit(label) => { + Wire{ + top: format!("{}", CONNECTING_WIRE), + mid: format!("{}", CL_CROSSED_WIRE), + bot: format!("{}", CONNECTING_WIRE), + } + }, + } + }, + VisualizationElement::Empty => { + Wire{ + top: format!(" "), + mid: format!("{}", Q_WIRE), + bot: format!(" "), + } + }, + } + } +} pub fn draw_circuit(circuit: &CircuitData) -> PyResult<()> { let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; - let vis_mat2 = VisualizationMatrix2::from_circuit(circuit, &dag)?; + let vis_mat2 = VisualizationMatrix::from_circuit(circuit, &dag)?; println!("======================"); @@ -1432,36 +1096,8 @@ pub fn draw_circuit(circuit: &CircuitData) -> PyResult<()> { } println!(""); } - // circuit_rep2.set_qubit_name(); - // let vis_mat:VisualizationMatrix = VisualizationMatrix::new(&circuit_rep, packedinst_layers); - - println!("======================"); - // for i in 0..vis_mat2.num_wires() { - // if let VisualizationElement2::Input(wire_input) = &vis_mat2[0][i] { - // match wire_input { - // WireInput::Qubit(qubit) => println!("QUBIT: {:?}", qubit), - // WireInput::Clbit(clbit) => println!("CLBIT: {:?}", clbit), - // } - // } - // } - // //using circuit rep to draw circuit - // let mut circuit_rep = CircuitRep::new(dag.clone()); - // // println!("circuit_rep {:?}", circuit_rep); - // let vis_mat2:VisualizationMatrix = VisualizationMatrix::new(&dag, packedinst_layers); - - // return Ok(()); - // circuit_rep.set_qubit_name(); - // vis_mat2.draw(&mut circuit_rep); - // // vis_mat2.print(); - // //println!("{}", &circuit_rep.circuit_string()); - // println!("======================"); - - // println!("Drawing circuit..."); - // // let mut circuit_rep2 = CircuitRep::new(dag); - // // vis_mat2.draw(&mut circuit_rep2); - - // println!("finished circuit"); - // // vis_mat.print(); + let circuit_rep = TextDrawer::draw_matrix(&vis_mat2, circuit); + circuit_rep.print(); Ok(()) } From 42e007cee8d55da6f121913411bf1b79e68eb1e5 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Sat, 25 Oct 2025 09:04:01 +0530 Subject: [PATCH 28/70] added swap gate support, removed circuit rep --- crates/circuit/src/circuit_drawer.rs | 467 +++++++++++++++++---------- 1 file changed, 299 insertions(+), 168 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 4d9203e4b7a6..13fa16fa9fa1 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -19,7 +19,7 @@ use hashbrown::HashSet; use crate::bit::{ShareableClbit, ShareableQubit}; use crate::dag_circuit::{DAGCircuit}; use crate::operations::{Operation, OperationRef, StandardGate, StandardInstruction}; -use crate::packed_instruction::{PackedInstruction,PackedOperation}; +use crate::packed_instruction::{self, PackedInstruction, PackedOperation}; use itertools::{Itertools, MinMaxResult}; use std::ops::Index; @@ -33,6 +33,8 @@ use crate::circuit_data::CircuitData; import_exception!(qiskit.circuit.exceptions, CircuitError); +// [ qubit indice] +// int: n #[pyfunction(name = "draw")] pub fn py_drawer(circuit: QuantumCircuitData) -> PyResult<()> { @@ -43,12 +45,13 @@ pub fn py_drawer(circuit: QuantumCircuitData) -> PyResult<()> { /// Calculate the range (inclusive) of the given instruction qubits/clbits over the wire indices. /// The assumption is that clbits always appear after the qubits in the visualization, hence the clbit indices /// are offset by the number of instruction qubits when calculating the range. - fn get_instruction_range(dag: &DAGCircuit, instruction: &PackedInstruction) -> (usize, usize) { - let node_qubits = dag.get_qargs(instruction.qubits); - let node_clbits = dag.get_cargs(instruction.clbits); +fn get_instruction_range(circuit: &CircuitData, instruction: &PackedInstruction) -> (usize, usize) { + let node_qubits = circuit.get_qargs(instruction.qubits); + let node_clbits = circuit.get_cargs(instruction.clbits); + let indices = node_qubits.iter().map(|q| q.index()).chain( - node_clbits.iter().map(|c| c.index() + dag.num_qubits())); + node_clbits.iter().map(|c| c.index() + circuit.num_qubits())); match indices.minmax() { MinMaxResult::MinMax(min, max) => (min, max), @@ -60,7 +63,7 @@ pub fn py_drawer(circuit: QuantumCircuitData) -> PyResult<()> { /// Return a list of PackedInstruction layers such that each layer contain instruction /// whose qubits/clbits indices do not overlap. The instructions are packed into each layer /// as long as there is no qubit/clbit overlap -fn build_layers(dag: &DAGCircuit) -> Vec> { +fn build_layers<'a>(dag: &'a DAGCircuit, circuit: &'a CircuitData) -> Vec> { let mut layers:Vec> = Vec::new(); let mut current_layer: Option<&mut Vec::<&PackedInstruction>> = None; let mut used_wires = HashSet::::new(); @@ -69,7 +72,7 @@ fn build_layers(dag: &DAGCircuit) -> Vec> { for node_index in layer.into_iter().sorted() { if let NodeType::Operation(instruction_to_insert) = &dag.dag()[node_index] { - let (node_min, node_max) = get_instruction_range(dag, instruction_to_insert); + let (node_min, node_max) = get_instruction_range(circuit, instruction_to_insert); // Check for instruction range overlap if (node_min..=node_max).any(|idx| used_wires.contains(&idx)) { @@ -98,23 +101,23 @@ pub fn get_indices(dag_circ: &DAGCircuit) -> u32{ } #[derive(Clone)] -enum WireInput<'a> { +enum ElementWireInput<'a> { Qubit(&'a ShareableQubit), Clbit(&'a ShareableClbit), } -impl<'a> Debug for WireInput<'a> { +impl<'a> Debug for ElementWireInput<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let name = match self { - WireInput::Qubit(&ref qubit) => "Qubit", - WireInput::Clbit(&ref clbit) => "Clbit", + ElementWireInput::Qubit(&ref qubit) => "Qubit", + ElementWireInput::Clbit(&ref clbit) => "Clbit", }; write!(f, "{}", name) } } -/// Input Wires. +/// Input ElementWires. /// The Option is for optional labels and can be used when the registers have different names. This allows us to use /// the same enum for both Input and VerticalLine types. #[derive(Clone, Debug)] @@ -131,9 +134,10 @@ enum ElementOnWire{ Bot, } + /// Enum for representing elements that appear directly on a wire and how they're connected. #[derive(Clone, Debug, Copy)] -enum OnWire{ +enum OnElementWire{ Control(ElementOnWire), Swap(ElementOnWire), Barrier, @@ -161,17 +165,23 @@ impl<'a> Debug for Boxed<'a> { /// Enum for representing the elements stored in a visualization matrix. The elements /// do not directly implement visualization capabilities, but rather carry enough information /// to enable visualization later on by the actual drawer. + +struct Op<'a>{ + instruction: &'a PackedInstruction, + +} + #[derive(Default, Clone, Debug)] enum VisualizationElement<'a>{ #[default] Empty, // Marker for no element Boxed(Boxed<'a>), - Input(WireInput<'a>), - DirectOnWire(OnWire), + Input(ElementWireInput<'a>), + DirectOnElementWire(OnElementWire), VerticalLine(InputType), - // Operation, } + /// A representation of a single column (called here a layer) of a visualization matrix #[derive(Clone, Debug)] struct VisualizationLayer<'a>(Vec>); @@ -181,27 +191,37 @@ impl<'a> VisualizationLayer<'a> { self.0.len() } - fn add_input(&mut self, input: WireInput<'a>, idx: usize) { + fn add_input(&mut self, input: ElementWireInput<'a>, idx: usize) { self.0[idx] = VisualizationElement::Input(input); } /// Adds the required visualization elements to represent the given instruction - fn add_instruction(&mut self, inst: &'a PackedInstruction, dag: &DAGCircuit) { + fn add_instruction(&mut self, inst: &'a PackedInstruction, circuit: &CircuitData) { match inst.op.view() { - OperationRef::StandardGate(_gate) => self.add_standard(inst, dag), - OperationRef::StandardInstruction(_instruction) => self.add_standard(inst, dag), + OperationRef::StandardGate(_gate) => self.add_standard(inst, circuit), + OperationRef::StandardInstruction(_instruction) => { + if let StandardInstruction::Barrier(_) = _instruction { + let barrier_indices = self.get_boxed_indices(inst, circuit); + self.add_barrier(inst, circuit, &barrier_indices); + } else if let StandardInstruction::Reset = _instruction { + let reset_indices = self.get_boxed_indices(inst, circuit); + self.add_reset(inst, circuit, &reset_indices); + } else { + self.add_standard(inst, circuit) + } + }, _ => unimplemented!("{}", format!("Visualization is not implemented for instruction of type {:?}", inst.op)), } } - fn get_controls(&self, inst: &PackedInstruction, dag: &DAGCircuit) -> Vec { + fn get_controls(&self, inst: &PackedInstruction, circuit: &CircuitData) -> Vec { let gate_has_control = vec![StandardGate::CX, StandardGate::CCX, StandardGate::CY, StandardGate::CZ, StandardGate::CRX, StandardGate::CRY, StandardGate::CRZ, StandardGate::CPhase, StandardGate::CS, StandardGate::CSdg, StandardGate::CSX, StandardGate::CU, StandardGate::CU1, StandardGate::CU3, StandardGate::CH, StandardGate::C3SX, StandardGate::C3X, - StandardGate::RC3X, StandardGate::RCCX]; + StandardGate::RC3X, StandardGate::RCCX, StandardGate::CSwap]; let inst_has_controls = vec![StandardInstruction::Measure]; @@ -211,10 +231,17 @@ impl<'a> VisualizationLayer<'a> { match std_op { OperationRef::StandardGate(gate) => { - if gate_has_control.contains(&gate) { - let qargs = dag.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); + if gate == StandardGate::CSwap { + let qargs = circuit.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); + let control = qargs[0]; + controls.push(control); + controls + } else if gate_has_control.contains(&gate) { + // let qargs = dag.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); + let qargs = circuit.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); + let target = qargs.last().unwrap(); - let (minima,maxima) = get_instruction_range(dag, inst); + let (minima,maxima) = get_instruction_range(circuit, inst); for control in minima..=maxima { if control != *target && qargs.contains(&control) { @@ -228,12 +255,13 @@ impl<'a> VisualizationLayer<'a> { } OperationRef::StandardInstruction(instruction) => { if inst_has_controls.contains(&instruction) { - let qargs = dag.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); + let qargs = circuit.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); let target = qargs.last().unwrap(); - let (minima,maxima) = get_instruction_range(dag, inst); + let cargs = circuit.get_cargs(inst.clbits).into_iter().map(|c| c.index() + circuit.num_qubits()).collect_vec(); + let (minima,maxima) = get_instruction_range(circuit, inst); for control in minima..=maxima { - if control != *target && qargs.contains(&control) { + if control != *target && cargs.contains(&control) { controls.push(control); } } @@ -249,16 +277,16 @@ impl<'a> VisualizationLayer<'a> { fn add_controls(&mut self, controls: &Vec, range: (usize, usize)) { for control in controls { if *control == range.0 { - self.0[*control] = VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Top)); + self.0[*control] = VisualizationElement::DirectOnElementWire(OnElementWire::Control(ElementOnWire::Top)); } else if *control == range.1 { - self.0[*control] = VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Bot)); + self.0[*control] = VisualizationElement::DirectOnElementWire(OnElementWire::Control(ElementOnWire::Bot)); } else { - self.0[*control] = VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Mid)); + self.0[*control] = VisualizationElement::DirectOnElementWire(OnElementWire::Control(ElementOnWire::Mid)); } } } - fn get_boxed_indices(&mut self, inst: & PackedInstruction, dag: &DAGCircuit) -> Vec{ + fn get_boxed_indices(&mut self, inst: & PackedInstruction, circuit: &CircuitData) -> Vec{ let single_box = vec![StandardGate::H, StandardGate::X, StandardGate::Y, StandardGate::Z, StandardGate::RX, StandardGate::RY, StandardGate::RZ, StandardGate::U, StandardGate::U1, StandardGate::U2, StandardGate::U3, StandardGate::S, StandardGate::Sdg, @@ -268,8 +296,8 @@ impl<'a> VisualizationLayer<'a> { StandardGate::CRZ, StandardGate::CS, StandardGate::CSdg, StandardGate::CSX, StandardGate::CU, StandardGate::CU1, StandardGate::CU3, StandardGate::C3X, StandardGate::C3SX, StandardGate::RC3X]; - let single_box_instrusctions = vec![StandardInstruction::Measure, StandardInstruction::Reset]; - + let single_box_instrusctions = vec![StandardInstruction::Measure]; + let multi_box = vec![StandardGate::ISwap, StandardGate::DCX, StandardGate::ECR, StandardGate::RXX, StandardGate::RYY, StandardGate::RZZ, StandardGate::RZX, StandardGate::XXMinusYY, StandardGate::XXPlusYY]; @@ -278,15 +306,17 @@ impl<'a> VisualizationLayer<'a> { let special_cases = vec![ StandardGate::GlobalPhase, StandardGate::CPhase]; - let qargs = dag.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); + let qargs = circuit.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); let target = qargs.last().unwrap(); - let range = get_instruction_range(dag, inst); + let range = get_instruction_range(circuit, inst); if let Some(std_gate) = inst.op.try_standard_gate(){ if single_box.contains(&std_gate){ vec![*target] } else if multi_box.contains(&std_gate){ (range.0..=range.1).collect() + } else if direct_on_wire.contains(&std_gate){ + qargs.iter().rev().take(2).cloned().collect_vec() } else { vec![] } @@ -298,32 +328,83 @@ impl<'a> VisualizationLayer<'a> { } else { vec![] } - - // Handle special cases and direct on wire } else { vec![] } } - fn add_boxed(&mut self, inst: &'a PackedInstruction, dag: &DAGCircuit, boxed_indices: &Vec) { + fn add_barrier(&mut self, inst: &'a PackedInstruction, circuit: &CircuitData, barrier_indices: &Vec) { + for bline in barrier_indices { + self.0[*bline] = VisualizationElement::DirectOnElementWire(OnElementWire::Barrier); + } + } + + fn add_reset(&mut self, inst: &'a PackedInstruction, circuit: &CircuitData, reset_indices: &Vec) { + for rline in reset_indices { + self.0[*rline] = VisualizationElement::DirectOnElementWire(OnElementWire::Reset); + } + } + + // [] + + fn add_boxed(&mut self, inst: &'a PackedInstruction, circuit: &CircuitData, boxed_indices: &Vec) { // The case of delay needs to be handled where it is multiple single qubit gates but shown as a box + // check if packed instruction is swap or cswap + // let qargs = circuit.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); + let special_cases = vec![StandardGate::Swap, StandardGate::CSwap]; + // if let Some(std_gate) = inst.op.try_standard_gate(){ + // if special_cases.contains(&std_gate){ + // let range= get_instruction_range(circuit, inst); + // let targets = qargs.iter().rev().take(2).collect_vec(); + // for target in targets { + // if *target == range.0 { + // self.0[*target] = VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Top)); + // } else if *target == range.1 { + // self.0[*target] = VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Bot)); + // } else { + // self.0[*target] = VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Mid)); + // } + // } + // } + // } if boxed_indices.len() == 1 { - let qargs = dag.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); + let qargs = circuit.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); let target = qargs.last().unwrap(); self.0[*target] = VisualizationElement::Boxed(Boxed::Single(inst)); } else if boxed_indices.len() > 1 { - for idx in boxed_indices { - self.0[*idx] = VisualizationElement::Boxed(Boxed::Multi(inst)); + if let Some(std_gate) = inst.op.try_standard_gate(){ + if special_cases.contains(&std_gate){ + let range= get_instruction_range(circuit, inst); + let targets = boxed_indices.iter().rev().take(2).collect_vec(); + for target in targets { + if *target == range.0 { + self.0[*target] = VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Top)); + } else if *target == range.1 { + self.0[*target] = VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Bot)); + } else { + self.0[*target] = VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Mid)); + } + } + return; + } else { + for idx in boxed_indices { + self.0[*idx] = VisualizationElement::Boxed(Boxed::Multi(inst)); + } + } + } else{ + for idx in boxed_indices { + self.0[*idx] = VisualizationElement::Boxed(Boxed::Multi(inst)); + } } } } - fn add_vertical_lines(&mut self, inst: &'a PackedInstruction, dag: &DAGCircuit, vertical_lines: &Vec) { + fn add_vertical_lines(&mut self, inst: &'a PackedInstruction, circuit: &CircuitData, vertical_lines: &Vec) { let double_lines = vec![StandardInstruction::Measure]; let input_type: InputType = if let Some(std_instruction) = inst.op.try_standard_instruction(){ if double_lines.contains(&std_instruction){ - InputType::Qubit(Some("||".to_string())) + InputType::Clbit(None) } else { InputType::Qubit(None) } @@ -336,17 +417,17 @@ impl<'a> VisualizationLayer<'a> { } // function to add standard gates and instructions - fn add_standard(&mut self, inst: &'a PackedInstruction, dag: &DAGCircuit) { - let (minima,maxima) = get_instruction_range(dag, inst); - let controls = self.get_controls(inst, dag); - let boxed_elements = self.get_boxed_indices(inst, dag); - // println!("Adding gate {:?}\n on qubits {:?} \n with controls {:?} \n and boxed elements {:?}\n ================", inst.op.name(), qargs, controls, boxed_elements); + fn add_standard(&mut self, inst: &'a PackedInstruction, circuit: &CircuitData) { + let (minima,maxima) = get_instruction_range(circuit, inst); + let controls = self.get_controls(inst, circuit); + let boxed_elements = self.get_boxed_indices(inst, circuit); let vert_lines = (minima..=maxima) .filter(|idx| !controls.contains(idx) && !boxed_elements.contains(idx)) .collect_vec(); self.add_controls(&controls, (minima, maxima)); - self.add_boxed(inst, dag, &boxed_elements); - self.add_vertical_lines(inst, dag, &vert_lines); + self.add_boxed(inst, circuit, &boxed_elements); + self.add_vertical_lines(inst, circuit, &vert_lines); + } } @@ -366,12 +447,14 @@ impl<'a> Index for VisualizationLayer<'a> { #[derive(Debug)] struct VisualizationMatrix<'a> { layers: Vec>, + circuit: &'a CircuitData, } impl<'a> VisualizationMatrix<'a> { - fn from_circuit(circuit: &'a CircuitData, dag: &'a DAGCircuit) -> PyResult { - let inst_layers = build_layers(&dag); + fn from_circuit(dag: &'a DAGCircuit,circuit: &'a CircuitData) -> PyResult { + // let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; + let inst_layers = build_layers(&dag, circuit); let num_wires = circuit.num_qubits() + circuit.num_clbits(); let mut layers = vec![VisualizationLayer(vec![VisualizationElement::default(); num_wires]); inst_layers.len() + 1]; // Add 1 to account for the inputs layer @@ -381,23 +464,24 @@ impl<'a> VisualizationMatrix<'a> { let input_layer = layers.first_mut().unwrap(); let mut input_idx = 0; for qubit in circuit.qubits().objects() { - input_layer.add_input(WireInput::Qubit(qubit), input_idx); + input_layer.add_input(ElementWireInput::Qubit(qubit), input_idx); input_idx += 1; } for clbit in circuit.clbits().objects() { - input_layer.add_input(WireInput::Clbit(clbit), input_idx); + input_layer.add_input(ElementWireInput::Clbit(clbit), input_idx); input_idx += 1; } for (i, layer) in inst_layers.iter().enumerate() { for inst in layer { - layers[i + 1].add_instruction(inst, &dag); + layers[i + 1].add_instruction(inst, &circuit); } } Ok(VisualizationMatrix{ layers, + circuit, }) } @@ -418,14 +502,25 @@ impl<'a> Index for VisualizationMatrix<'a> { } } -#[derive(Clone,Debug)] -struct Wire{ + // [control control boxed boxed] + // [swap swap] + // [control swaps swaps], + +// better name for the struct +#[derive(Clone)] +struct ElementWire{ top:String, mid:String, bot:String, } -impl Wire{ +impl Debug for ElementWire{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}\n{}\n{}", self.top, self.mid, self.bot) + } +} + +impl ElementWire{ fn width(&self) -> usize{ // return the max of all strigns // self.top.len().max(self.mid.len()).max(self.bot.len()) @@ -499,34 +594,6 @@ impl Wire{ } } -struct CircuitRep{ - wires: Vec, -} - -impl CircuitRep{ - fn print(&self){ - for wire in &self.wires{ - println!("{}\n{}\n{}", wire.top, wire.mid, wire.bot); - } - } - - fn add_wire(&mut self, wire: &Wire, ind: usize){ - self.wires[ind].top.push_str(&wire.top); - self.wires[ind].mid.push_str(&wire.mid); - self.wires[ind].bot.push_str(&wire.bot); - // self.wires[ind].top.push_str(&format!("{}{}",&wire.top,"$")); - // self.wires[ind].mid.push_str(&format!("{}{}",&wire.mid,"$")); - // self.wires[ind].bot.push_str(&format!("{}{}",&wire.bot,"$")); - } -} - -impl Index for CircuitRep{ - type Output = Wire; - - fn index(&self, index: usize) -> &Self::Output { - &self.wires[index] - } -} pub const Q_WIRE: char = '─'; pub const C_WIRE: char = '═'; @@ -542,11 +609,25 @@ pub const BOT_LEFT_BOX: char = '└'; pub const BOT_RIGHT_BOX: char = '┘'; pub const BARRIER: char = '░'; pub const BULLET:char = '■'; +pub const C_BULLET:char = '╩'; pub const CONNECTING_WIRE:char = '│'; -pub const CROSSED_WIRE:char = '┼'; -pub const CL_CROSSED_WIRE:char = '╪'; +pub const CL_CONNECTING_WIRE:char = '║'; +pub const Q_Q_CROSSED_WIRE:char = '┼'; +pub const Q_CL_CROSSED_WIRE:char = '╪'; +pub const CL_CL_CROSSED_WIRE:char = '╬'; +pub const CL_Q_CROSSED_WIRE:char = '╫'; + +struct TextDrawer{ + wires: Vec>, +} + +impl Index for TextDrawer{ + type Output = Vec; -struct TextDrawer; + fn index(&self, index: usize) -> &Self::Output { + &self.wires[index] + } +} impl TextDrawer{ @@ -772,11 +853,11 @@ impl TextDrawer{ } } else if let Some(std_instruction) = instruction.op.try_standard_instruction(){ if std_instruction == StandardInstruction::Measure{ - Some(" M ".to_string()) + Some("M".to_string()) } else if std_instruction == StandardInstruction::Reset{ Some("|0>".to_string()) } else if let StandardInstruction::Barrier(_) = std_instruction { - Some(" ░ ".to_string()) + Some("░".to_string()) } else { // Fallback for non-standard instructions Some(format!("{}{}{}"," ",instruction.op.name().to_string()," ")) @@ -790,30 +871,33 @@ impl TextDrawer{ instruction_label } - fn draw_matrix(vis_mat: &VisualizationMatrix, circ_data: &CircuitData) -> CircuitRep{ - let mut wires: Vec = vec![]; - for _ in 0..vis_mat.num_wires(){ - wires.push(Wire{ + fn from_visualization_matrix(vis_mat: &VisualizationMatrix) -> Self{ + let mut wires: Vec> = vec![]; + for _ in 0..vis_mat.num_layers(){ + wires.push(vec![]); + } + for i in 0..vis_mat.num_wires(){ + wires[i].push(ElementWire{ top: String::new(), mid: String::new(), bot: String::new(), }); } - let mut circuit_rep = CircuitRep{ + let mut text_drawer = TextDrawer{ wires }; let mut ct = 0; for layer in &vis_mat.layers{ - let layer_wires = Self::draw_layer(layer, circ_data, ct); + let layer_wires = Self::draw_layer(layer, vis_mat.circuit, ct); ct += 1; for (i, wire) in layer_wires.iter().enumerate(){ - circuit_rep.add_wire(wire, i); + text_drawer.wires[i].push(wire.clone()); } } - circuit_rep + text_drawer } // fn get_layer_width(&self, layer: &VisualizationLayer, circ_data: &CircuitData) -> usize{ @@ -831,8 +915,8 @@ impl TextDrawer{ // Self::draw_element(element.clone(), circ_data, 0).width() // } - fn draw_layer(layer: &VisualizationLayer, circ_data: &CircuitData, layer_ind: usize) -> Vec{ - let mut wires: Vec = vec![]; + fn draw_layer(layer: &VisualizationLayer, circ_data: &CircuitData, layer_ind: usize) -> Vec{ + let mut wires: Vec = vec![]; for (i,element) in layer.0.iter().enumerate(){ let wire = Self::draw_element(element.clone(), circ_data,i); wires.push(wire); @@ -864,7 +948,7 @@ impl TextDrawer{ wires } - pub fn draw_element(vis_ele: VisualizationElement, circ_data: &CircuitData, ind: usize) -> Wire { + pub fn draw_element(vis_ele: VisualizationElement, circ_data: &CircuitData, ind: usize) -> ElementWire { match vis_ele { VisualizationElement::Boxed(sub_type) => { // implement for cases where the box is on classical wires. The left and right connectors will change @@ -873,7 +957,7 @@ impl TextDrawer{ match sub_type { Boxed::Single(inst) => { let label = Self::get_label(inst).unwrap_or(" ".to_string()); - Wire{ + ElementWire{ top: format!("{}{}{}", TOP_LEFT_BOX, Q_WIRE.to_string().repeat(label.len()), TOP_RIGHT_BOX), mid: format!("{}{}{}", Q_LEFT_CON , label, Q_RIGHT_CON), bot: format!("{}{}{}", BOT_LEFT_BOX, Q_WIRE.to_string().repeat(label.len()), BOT_RIGHT_BOX), @@ -922,19 +1006,19 @@ impl TextDrawer{ }; if ind == range.0 { - Wire{ + ElementWire{ top: format!("{}{}{}", TOP_LEFT_BOX, Q_WIRE.to_string().repeat(mid_section.len()), TOP_RIGHT_BOX), mid: format!("{}{}{}", Q_LEFT_CON, mid_section, Q_RIGHT_CON), bot: format!("{}{}{}", CONNECTING_WIRE, " ".repeat(mid_section.len()), CONNECTING_WIRE), } } else if ind == range.1 { - Wire{ + ElementWire{ top: format!("{}{}{}", CONNECTING_WIRE," ".to_string().repeat(mid_section.len()), CONNECTING_WIRE), mid: format!("{}{}{}", Q_LEFT_CON, mid_section, Q_RIGHT_CON), bot: format!("{}{}{}", BOT_LEFT_BOX, Q_WIRE.to_string().repeat(mid_section.len()), BOT_RIGHT_BOX), } } else { - Wire{ + ElementWire{ top: format!("{}{}{}", CONNECTING_WIRE, " ".repeat(mid_section.len()), CONNECTING_WIRE), mid: format!("{}{}{}", Q_LEFT_CON, mid_section, Q_RIGHT_CON), bot: format!("{}{}{}", CONNECTING_WIRE, " ".repeat(mid_section.len()), CONNECTING_WIRE), @@ -943,74 +1027,91 @@ impl TextDrawer{ } } }, - VisualizationElement::DirectOnWire(on_wire) => { - let wire_char = match on_wire { - OnWire::Control(position) => { - BULLET + VisualizationElement::DirectOnElementWire(on_wire) => { + + let connecting_wire = if ind < circ_data.num_qubits() { + CONNECTING_WIRE + } else { + CL_CONNECTING_WIRE + }; + + let wire_char: String = match on_wire { + OnElementWire::Control(position) => { + if ind < circ_data.num_qubits() { + BULLET.to_string() + } else { + C_BULLET.to_string() + } }, - OnWire::Swap(position) => { - 'X' + OnElementWire::Swap(position) => { + "X".to_string() }, - OnWire::Barrier => BARRIER, - OnWire::Reset => '0', + OnElementWire::Barrier => BARRIER.to_string(), + OnElementWire::Reset => "|0>".to_string(), }; let top:String = match on_wire{ - OnWire::Control(position) => { + OnElementWire::Control(position) => { match position { ElementOnWire::Top => " ".to_string(), - ElementOnWire::Mid => format!("{}", CONNECTING_WIRE), - ElementOnWire::Bot => format!("{}", CONNECTING_WIRE), + ElementOnWire::Mid => format!("{}", connecting_wire), + ElementOnWire::Bot => format!("{}", connecting_wire), } }, - OnWire::Swap(position) => { + OnElementWire::Swap(position) => { match position { ElementOnWire::Top => " ".to_string(), - ElementOnWire::Mid => format!("{}", CONNECTING_WIRE), - ElementOnWire::Bot => format!("{}", CONNECTING_WIRE), + ElementOnWire::Mid => format!("{}", connecting_wire), + ElementOnWire::Bot => format!("{}", connecting_wire), } }, - OnWire::Barrier => { + OnElementWire::Barrier => { format!("{}", BARRIER) }, - OnWire::Reset => { - " ".to_string() + OnElementWire::Reset => { + " ".to_string() }, }; let bot: String = match on_wire{ - OnWire::Control(position) => { + OnElementWire::Control(position) => { match position { - ElementOnWire::Top => format!("{}", CONNECTING_WIRE), - ElementOnWire::Mid => format!("{}", CONNECTING_WIRE), + ElementOnWire::Top => format!("{}", connecting_wire), + ElementOnWire::Mid => format!("{}", connecting_wire), ElementOnWire::Bot => " ".to_string(), } }, - OnWire::Swap(position) => { + OnElementWire::Swap(position) => { match position { - ElementOnWire::Top => format!("{}", CONNECTING_WIRE), - ElementOnWire::Mid => format!("{}", CONNECTING_WIRE), + ElementOnWire::Top => format!("{}", connecting_wire), + ElementOnWire::Mid => format!("{}", connecting_wire), ElementOnWire::Bot => " ".to_string(), } }, - OnWire::Barrier => { + OnElementWire::Barrier => { format!("{}", BARRIER) }, - OnWire::Reset => { - " ".to_string() + OnElementWire::Reset => { + " ".to_string() }, }; - Wire{ - top, - mid: format!("{}", wire_char), - bot, + let wire = if ind < circ_data.num_qubits() { + Q_WIRE + } else { + C_WIRE + }; + + ElementWire{ + top: format!("{}{}{}"," ",top," "), + mid: format!("{}{}{}", wire, wire_char, wire), + bot: format!("{}{}{}"," ",bot," "), } }, VisualizationElement::Input(wire_input) => { match wire_input { - WireInput::Qubit(qubit) => { + ElementWireInput::Qubit(qubit) => { let qubit_name = if let Some(bit_info) = circ_data.qubit_indices().get(qubit) { if let Some((register, index)) = bit_info.registers().first() { format!("{}_{}:", register.name(), index) @@ -1020,13 +1121,13 @@ impl TextDrawer{ } else { format!("q_{}:", ind) }; - Wire{ + ElementWire{ top: format!("{}", " ".repeat(qubit_name.len())), mid: format!("{}", qubit_name), bot: format!("{}", " ".repeat(qubit_name.len())), } } - WireInput::Clbit(clbit) => { + ElementWireInput::Clbit(clbit) => { let clbit_name = if let Some(bit_info) = circ_data.clbit_indices().get(clbit) { if let Some((register, index)) = bit_info.registers().first() { format!("{}_{}:", register.name(), index) @@ -1036,7 +1137,7 @@ impl TextDrawer{ } else { format!("c_{}:", ind) }; - Wire{ + ElementWire{ top: format!("{}", " ".repeat(clbit_name.len())), mid: format!("{}", clbit_name), bot: format!("{}", " ".repeat(clbit_name.len())), @@ -1044,47 +1145,77 @@ impl TextDrawer{ }, } }, - // VisualizationElement::Operation => { - // Wire{ - // top: format!(" "), - // mid: format!(" "), - // bot: format!(" "), - // } - // }, VisualizationElement::VerticalLine(input_type) => { - match input_type { - InputType::Qubit(label) => { - Wire{ - top: format!("{}", CONNECTING_WIRE), - mid: format!("{}", CROSSED_WIRE), - bot: format!("{}", CONNECTING_WIRE), - } - }, - InputType::Clbit(label) => { - Wire{ - top: format!("{}", CONNECTING_WIRE), - mid: format!("{}", CL_CROSSED_WIRE), - bot: format!("{}", CONNECTING_WIRE), - } - }, + let crossed = { + match &input_type { + InputType::Qubit(label) => { + if ind < circ_data.num_qubits() { + Q_Q_CROSSED_WIRE + } else { + Q_CL_CROSSED_WIRE + } + }, + InputType::Clbit(label) => { + if ind < circ_data.num_qubits() { + CL_Q_CROSSED_WIRE + } else { + CL_CL_CROSSED_WIRE + } + }, + } + }; + let connector = match &input_type { + InputType::Qubit(_) => CONNECTING_WIRE, + InputType::Clbit(_) => CL_CONNECTING_WIRE, + }; + ElementWire{ + top: format!("{}", connector), + mid: format!("{}", crossed), + bot: format!("{}", connector), } }, VisualizationElement::Empty => { - Wire{ + let wire = { + if ind < circ_data.num_qubits() { + Q_WIRE + } else { + C_WIRE + } + }; + ElementWire{ top: format!(" "), - mid: format!("{}", Q_WIRE), + mid: format!("{}", wire), bot: format!(" "), } }, } } + + fn print(&self){ + let mut output = String::new(); + for i in self.wires.iter(){ + let top_line: String = i.iter().map(|wire| wire.top.clone()).collect::>().join(""); + let mid_line: String = i.iter().map(|wire| wire.mid.clone()).collect::>().join(""); + let bot_line: String = i.iter().map(|wire| wire.bot.clone()).collect::>().join(""); + output.push_str(&format!("{}\n{}\n{}\n", top_line, mid_line, bot_line)); + } + println!("{}", output); + } + + // fn add_wire(&mut self, wire: &ElementWire, ind: usize){ + // self.wires[ind].top.push_str(&wire.top); + // self.wires[ind].mid.push_str(&wire.mid); + // self.wires[ind].bot.push_str(&wire.bot); + // // self.wires[ind].top.push_str(&format!("{}{}",&wire.top,"$")); + // // self.wires[ind].mid.push_str(&format!("{}{}",&wire.mid,"$")); + // // self.wires[ind].bot.push_str(&format!("{}{}",&wire.bot,"$")); + // } } pub fn draw_circuit(circuit: &CircuitData) -> PyResult<()> { - let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; - let vis_mat2 = VisualizationMatrix::from_circuit(circuit, &dag)?; + let vis_mat2 = VisualizationMatrix::from_circuit(&dag,circuit)?; println!("======================"); @@ -1097,7 +1228,7 @@ pub fn draw_circuit(circuit: &CircuitData) -> PyResult<()> { println!(""); } - let circuit_rep = TextDrawer::draw_matrix(&vis_mat2, circuit); + let circuit_rep = TextDrawer::from_visualization_matrix(&vis_mat2); circuit_rep.print(); Ok(()) } From 1e3cac12b1b4f45833f797d8fba8a7b0fa984a13 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Sat, 25 Oct 2025 23:40:59 +0530 Subject: [PATCH 29/70] added top and bottom connect support, fixed parameter labels --- crates/circuit/src/circuit_drawer.rs | 236 ++++++++++++++++++--------- 1 file changed, 158 insertions(+), 78 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 13fa16fa9fa1..fd2d1a5ca315 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -18,7 +18,7 @@ use std::thread::current; use hashbrown::HashSet; use crate::bit::{ShareableClbit, ShareableQubit}; use crate::dag_circuit::{DAGCircuit}; -use crate::operations::{Operation, OperationRef, StandardGate, StandardInstruction}; +use crate::operations::{Operation, OperationRef, StandardGate, StandardInstruction, Param}; use crate::packed_instruction::{self, PackedInstruction, PackedOperation}; use itertools::{Itertools, MinMaxResult}; use std::ops::Index; @@ -100,7 +100,7 @@ pub fn get_indices(dag_circ: &DAGCircuit) -> u32{ total_qubits + total_clbits } -#[derive(Clone)] +#[derive(Clone, PartialEq)] enum ElementWireInput<'a> { Qubit(&'a ShareableQubit), Clbit(&'a ShareableClbit), @@ -120,14 +120,14 @@ impl<'a> Debug for ElementWireInput<'a> { /// Input ElementWires. /// The Option is for optional labels and can be used when the registers have different names. This allows us to use /// the same enum for both Input and VerticalLine types. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] enum InputType{ Qubit(Option), Clbit(Option) } /// Enum for representing elements that can appear directly on a wire. -#[derive(Clone, Debug, Copy)] +#[derive(Clone, Debug, Copy, PartialEq)] enum ElementOnWire{ Top, Mid, @@ -136,7 +136,7 @@ enum ElementOnWire{ /// Enum for representing elements that appear directly on a wire and how they're connected. -#[derive(Clone, Debug, Copy)] +#[derive(Clone, Debug, Copy, PartialEq)] enum OnElementWire{ Control(ElementOnWire), Swap(ElementOnWire), @@ -152,6 +152,20 @@ enum Boxed<'a>{ Multi(&'a PackedInstruction), } +// This is a temporary work around for a derive issue +// however the behaviour of this PartialEq is not ideal as it only checks the variant type +// because PackedInstructions cannot be compared directly + +impl PartialEq for Boxed<'_> { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Boxed::Single(inst1), Boxed::Single(inst2)) => true, + (Boxed::Multi(inst1), Boxed::Multi(inst2)) => true, + _ => false, + } + } +} + impl<'a> Debug for Boxed<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -171,7 +185,7 @@ struct Op<'a>{ } -#[derive(Default, Clone, Debug)] +#[derive(Default, Clone, Debug, PartialEq)] enum VisualizationElement<'a>{ #[default] Empty, // Marker for no element @@ -186,6 +200,14 @@ enum VisualizationElement<'a>{ #[derive(Clone, Debug)] struct VisualizationLayer<'a>(Vec>); +impl<'a> Index for VisualizationLayer<'a> { + type Output = VisualizationElement<'a>; + + fn index(&self, index: usize) -> &Self::Output { + &self.0[index] + } +} + impl<'a> VisualizationLayer<'a> { fn len(&self) -> usize { self.0.len() @@ -325,6 +347,8 @@ impl<'a> VisualizationLayer<'a> { vec![*target] } else if let StandardInstruction::Barrier(_) = std_instruction { (range.0..=range.1).collect() + } else if let StandardInstruction::Reset = std_instruction{ + qargs } else { vec![] } @@ -349,25 +373,7 @@ impl<'a> VisualizationLayer<'a> { // [] fn add_boxed(&mut self, inst: &'a PackedInstruction, circuit: &CircuitData, boxed_indices: &Vec) { - // The case of delay needs to be handled where it is multiple single qubit gates but shown as a box - // check if packed instruction is swap or cswap - // let qargs = circuit.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); let special_cases = vec![StandardGate::Swap, StandardGate::CSwap]; - // if let Some(std_gate) = inst.op.try_standard_gate(){ - // if special_cases.contains(&std_gate){ - // let range= get_instruction_range(circuit, inst); - // let targets = qargs.iter().rev().take(2).collect_vec(); - // for target in targets { - // if *target == range.0 { - // self.0[*target] = VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Top)); - // } else if *target == range.1 { - // self.0[*target] = VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Bot)); - // } else { - // self.0[*target] = VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Mid)); - // } - // } - // } - // } if boxed_indices.len() == 1 { let qargs = circuit.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); let target = qargs.last().unwrap(); @@ -431,14 +437,6 @@ impl<'a> VisualizationLayer<'a> { } } -impl<'a> Index for VisualizationLayer<'a> { - type Output = VisualizationElement<'a>; - - fn index(&self, index: usize) -> &Self::Output { - &self.0[index] - } -} - /// A Plain, logical 2D representation of a circuit. /// /// A dense representation of the circuit of size N * (M + 1), where the first @@ -599,6 +597,8 @@ pub const Q_WIRE: char = '─'; pub const C_WIRE: char = '═'; pub const TOP_CON: char = '┴'; pub const BOT_CON: char = '┬'; +pub const C_WIRE_CON_TOP:char = '╩'; +pub const C_BOT_CON:char = '╥'; pub const Q_LEFT_CON: char = '┤'; pub const Q_RIGHT_CON: char = '├'; pub const CL_LEFT_CON: char = '╡'; @@ -609,7 +609,6 @@ pub const BOT_LEFT_BOX: char = '└'; pub const BOT_RIGHT_BOX: char = '┘'; pub const BARRIER: char = '░'; pub const BULLET:char = '■'; -pub const C_BULLET:char = '╩'; pub const CONNECTING_WIRE:char = '│'; pub const CL_CONNECTING_WIRE:char = '║'; pub const Q_Q_CROSSED_WIRE:char = '┼'; @@ -633,7 +632,19 @@ impl TextDrawer{ fn get_label(instruction: &PackedInstruction) -> Option{ let label = instruction.label(); - let instruction_param =format!("{:?}",instruction.params_view()); + let mut instruction_param = String::new(); + for param in instruction.params_view(){ + let param_sub_str = match param { + Param::Float(f) => format!("{:.2}", f), + _ => format!("{:?}", param), + }; + if instruction_param.is_empty(){ + instruction_param = param_sub_str; + } else { + instruction_param = format!("{}, {}", instruction_param, param_sub_str); + } + } + // let instruction_param =format!("{:?}",instruction.params_view()); let instruction_label = match label { Some(l) => { Some(format!("{}{}{}"," ",l.to_string()," ")) @@ -873,16 +884,9 @@ impl TextDrawer{ fn from_visualization_matrix(vis_mat: &VisualizationMatrix) -> Self{ let mut wires: Vec> = vec![]; - for _ in 0..vis_mat.num_layers(){ + for _ in 0..vis_mat.num_wires(){ wires.push(vec![]); } - for i in 0..vis_mat.num_wires(){ - wires[i].push(ElementWire{ - top: String::new(), - mid: String::new(), - bot: String::new(), - }); - } let mut text_drawer = TextDrawer{ wires @@ -890,7 +894,7 @@ impl TextDrawer{ let mut ct = 0; for layer in &vis_mat.layers{ - let layer_wires = Self::draw_layer(layer, vis_mat.circuit, ct); + let layer_wires = Self::draw_layer(layer, vis_mat, ct); ct += 1; for (i, wire) in layer_wires.iter().enumerate(){ text_drawer.wires[i].push(wire.clone()); @@ -900,29 +904,14 @@ impl TextDrawer{ text_drawer } - // fn get_layer_width(&self, layer: &VisualizationLayer, circ_data: &CircuitData) -> usize{ - // let mut width:usize = 0; - // for element in layer.0.iter(){ - // let ele_width = Self::get_element_width(self, &element, circ_data); - // if ele_width > width{ - // width = ele_width; - // } - // } - // width - // } - - // fn get_element_width(&self, element: &VisualizationElement, circ_data: &CircuitData) -> usize{ - // Self::draw_element(element.clone(), circ_data, 0).width() - // } - - fn draw_layer(layer: &VisualizationLayer, circ_data: &CircuitData, layer_ind: usize) -> Vec{ + fn draw_layer(layer: &VisualizationLayer, vis_mat: &VisualizationMatrix, layer_ind: usize) -> Vec{ let mut wires: Vec = vec![]; for (i,element) in layer.0.iter().enumerate(){ - let wire = Self::draw_element(element.clone(), circ_data,i); + let wire = Self::draw_element(element.clone(), layer, vis_mat.circuit, i); wires.push(wire); } - let num_qubits = circ_data.num_qubits(); + let num_qubits = vis_mat.circuit.num_qubits(); //let layer_width = wires.iter().map(|w| w.width()).max().unwrap_or(0); let mut layer_width = 0; @@ -948,26 +937,104 @@ impl TextDrawer{ wires } - pub fn draw_element(vis_ele: VisualizationElement, circ_data: &CircuitData, ind: usize) -> ElementWire { + pub fn draw_element(vis_ele: VisualizationElement, vis_layer: &VisualizationLayer, circuit: &CircuitData, ind: usize) -> ElementWire { match vis_ele { VisualizationElement::Boxed(sub_type) => { // implement for cases where the box is on classical wires. The left and right connectors will change // from single wired to double wired. + let top_cases: Vec = vec![ + VisualizationElement::DirectOnElementWire(OnElementWire::Control(ElementOnWire::Top)), + VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Top)), + VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Mid)), + VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Mid)), + VisualizationElement::VerticalLine(InputType::Qubit(None)), + VisualizationElement::VerticalLine(InputType::Clbit(None)), + ]; + + let bot_cases: Vec = vec![ + VisualizationElement::DirectOnElementWire(OnElementWire::Control(ElementOnWire::Bot)), + VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Bot)), + VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Mid)), + VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Mid)), + VisualizationElement::VerticalLine(InputType::Qubit(None)), + VisualizationElement::VerticalLine(InputType::Clbit(None)), + ]; + + // if subtype is measurement then classical connectors + let is_measure = match &sub_type { + Boxed::Single(inst) => { + if let Some(std_instruction) = inst.op.try_standard_instruction(){ + if std_instruction == StandardInstruction::Measure{ + true + } else { + false + } + } else { + false + } + }, + _ => false, + }; + + let top_con = { + if ind >= 1 { + if top_cases.contains(&vis_layer.0[ind - 1]) { + if is_measure{ + C_WIRE_CON_TOP + } else { + TOP_CON + } + } else { + Q_WIRE + } + } else { + Q_WIRE + } + }; + + let bot_con = { + if ind + 1 < vis_layer.0.len() { + if bot_cases.contains(&vis_layer.0[ind + 1]) { + if is_measure { + C_BOT_CON + } else { + BOT_CON + } + } else { + Q_WIRE + } + } else { + Q_WIRE + } + }; + match sub_type { Boxed::Single(inst) => { let label = Self::get_label(inst).unwrap_or(" ".to_string()); + let left_len = (label.len() - 1) / 2; + let right_len = label.len() - left_len - 1; ElementWire{ - top: format!("{}{}{}", TOP_LEFT_BOX, Q_WIRE.to_string().repeat(label.len()), TOP_RIGHT_BOX), - mid: format!("{}{}{}", Q_LEFT_CON , label, Q_RIGHT_CON), - bot: format!("{}{}{}", BOT_LEFT_BOX, Q_WIRE.to_string().repeat(label.len()), BOT_RIGHT_BOX), + top: format!("{}{}{}{}{}", + TOP_LEFT_BOX, + Q_WIRE.to_string().repeat(left_len), + top_con, + Q_WIRE.to_string().repeat(right_len), + TOP_RIGHT_BOX), + mid: format!("{}{}{}", Q_LEFT_CON, label, Q_RIGHT_CON), + bot: format!("{}{}{}{}{}", + BOT_LEFT_BOX, + Q_WIRE.to_string().repeat(left_len), + bot_con, + Q_WIRE.to_string().repeat(right_len), + BOT_RIGHT_BOX), } }, Boxed::Multi(inst) => { let label = Self::get_label(inst).unwrap_or(" ".to_string()); // get all the indices affected by this multi-box - let qargs = circ_data.qargs_interner().get(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); - let cargs = circ_data.cargs_interner().get(inst.clbits).into_iter().map(|c| c.index() + circ_data.num_qubits()).collect_vec(); + let qargs = circuit.qargs_interner().get(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); + let cargs = circuit.cargs_interner().get(inst.clbits).into_iter().map(|c| c.index() + circuit.num_qubits()).collect_vec(); let minmax = qargs.iter().chain(cargs.iter()).minmax(); let range = match minmax { MinMaxResult::MinMax(min, max) => (*min, *max), @@ -1005,9 +1072,17 @@ impl TextDrawer{ format!("{:^total_q$} {:^label_len$}", num_affected, " ", total_q = qargs.len(), label_len=label.len()) }; + let left_len = (mid_section.len() - 1) / 2; + let right_len = mid_section.len() - left_len - 1; + if ind == range.0 { ElementWire{ - top: format!("{}{}{}", TOP_LEFT_BOX, Q_WIRE.to_string().repeat(mid_section.len()), TOP_RIGHT_BOX), + top: format!("{}{}{}{}{}", + TOP_LEFT_BOX, + Q_WIRE.to_string().repeat(left_len), + top_con, + Q_WIRE.to_string().repeat(right_len), + TOP_RIGHT_BOX), mid: format!("{}{}{}", Q_LEFT_CON, mid_section, Q_RIGHT_CON), bot: format!("{}{}{}", CONNECTING_WIRE, " ".repeat(mid_section.len()), CONNECTING_WIRE), } @@ -1015,7 +1090,12 @@ impl TextDrawer{ ElementWire{ top: format!("{}{}{}", CONNECTING_WIRE," ".to_string().repeat(mid_section.len()), CONNECTING_WIRE), mid: format!("{}{}{}", Q_LEFT_CON, mid_section, Q_RIGHT_CON), - bot: format!("{}{}{}", BOT_LEFT_BOX, Q_WIRE.to_string().repeat(mid_section.len()), BOT_RIGHT_BOX), + bot: format!("{}{}{}{}{}", + BOT_LEFT_BOX, + Q_WIRE.to_string().repeat(left_len), + bot_con, + Q_WIRE.to_string().repeat(right_len), + BOT_RIGHT_BOX), } } else { ElementWire{ @@ -1029,7 +1109,7 @@ impl TextDrawer{ }, VisualizationElement::DirectOnElementWire(on_wire) => { - let connecting_wire = if ind < circ_data.num_qubits() { + let connecting_wire = if ind < circuit.num_qubits() { CONNECTING_WIRE } else { CL_CONNECTING_WIRE @@ -1037,10 +1117,10 @@ impl TextDrawer{ let wire_char: String = match on_wire { OnElementWire::Control(position) => { - if ind < circ_data.num_qubits() { + if ind < circuit.num_qubits() { BULLET.to_string() } else { - C_BULLET.to_string() + C_WIRE_CON_TOP.to_string() } }, OnElementWire::Swap(position) => { @@ -1096,7 +1176,7 @@ impl TextDrawer{ }, }; - let wire = if ind < circ_data.num_qubits() { + let wire = if ind < circuit.num_qubits() { Q_WIRE } else { C_WIRE @@ -1112,7 +1192,7 @@ impl TextDrawer{ VisualizationElement::Input(wire_input) => { match wire_input { ElementWireInput::Qubit(qubit) => { - let qubit_name = if let Some(bit_info) = circ_data.qubit_indices().get(qubit) { + let qubit_name = if let Some(bit_info) = circuit.qubit_indices().get(qubit) { if let Some((register, index)) = bit_info.registers().first() { format!("{}_{}:", register.name(), index) } else { @@ -1128,7 +1208,7 @@ impl TextDrawer{ } } ElementWireInput::Clbit(clbit) => { - let clbit_name = if let Some(bit_info) = circ_data.clbit_indices().get(clbit) { + let clbit_name = if let Some(bit_info) = circuit.clbit_indices().get(clbit) { if let Some((register, index)) = bit_info.registers().first() { format!("{}_{}:", register.name(), index) } else { @@ -1149,14 +1229,14 @@ impl TextDrawer{ let crossed = { match &input_type { InputType::Qubit(label) => { - if ind < circ_data.num_qubits() { + if ind < circuit.num_qubits() { Q_Q_CROSSED_WIRE } else { Q_CL_CROSSED_WIRE } }, InputType::Clbit(label) => { - if ind < circ_data.num_qubits() { + if ind < circuit.num_qubits() { CL_Q_CROSSED_WIRE } else { CL_CL_CROSSED_WIRE @@ -1176,7 +1256,7 @@ impl TextDrawer{ }, VisualizationElement::Empty => { let wire = { - if ind < circ_data.num_qubits() { + if ind < circuit.num_qubits() { Q_WIRE } else { C_WIRE From 8e47ad6e4d80bf630ebf95a332de64c2c455b9ba Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Mon, 27 Oct 2025 09:44:17 +0530 Subject: [PATCH 30/70] merge wires added --- crates/circuit/src/circuit_drawer.rs | 90 +++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 8 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index fd2d1a5ca315..a955a374e40c 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -1272,16 +1272,91 @@ impl TextDrawer{ } fn print(&self){ - let mut output = String::new(); - for i in self.wires.iter(){ - let top_line: String = i.iter().map(|wire| wire.top.clone()).collect::>().join(""); - let mid_line: String = i.iter().map(|wire| wire.mid.clone()).collect::>().join(""); - let bot_line: String = i.iter().map(|wire| wire.bot.clone()).collect::>().join(""); - output.push_str(&format!("{}\n{}\n{}\n", top_line, mid_line, bot_line)); + // let mut output = String::new(); + // for i in self.wires.iter(){ + // let top_line: String = i.iter().map(|wire| wire.top.clone()).collect::>().join(""); + // let mid_line: String = i.iter().map(|wire| wire.mid.clone()).collect::>().join(""); + // let bot_line: String = i.iter().map(|wire| wire.bot.clone()).collect::>().join(""); + // output.push_str(&format!("{}\n{}\n{}\n", top_line, mid_line, bot_line)); + // } + // println!("{}", output); + + // print using merge lines + let num_wires = self.wires.len(); + for i in 0..num_wires - 1 { + if i == 0 { + let top_line = self.wires[i].iter().map(|wire| wire.top.clone()).collect::>().join(""); + let mid_line = self.wires[i].iter().map(|wire| wire.mid.clone()).collect::>().join(""); + println!("{}", top_line); + println!("{}", mid_line); + } + let bot_line = self.wires[i].iter().map(|wire| wire.bot.clone()).collect::>().join(""); + let top_line_next = self.wires[i + 1].iter().map(|wire| wire.top.clone()).collect::>().join(""); + let merged_line = Self::merge_lines(&bot_line, &top_line_next, "top"); + println!("{}", merged_line); + let mid_line_next = self.wires[i + 1].iter().map(|wire| wire.mid.clone()).collect::>().join(""); + println!("{}", mid_line_next); } - println!("{}", output); + let last_index = num_wires - 1; + let bot_line = self.wires[last_index].iter().map(|wire| wire.bot.clone()).collect::>().join(""); + println!("{}", bot_line); + } + pub fn merge_lines(top: &str, bot: &str, icod: &str) -> String { + let mut ret = String::new(); + + for (topc, botc) in top.chars().zip(bot.chars()) { + if topc == botc { + ret.push(topc); + } else if "┼╪".contains(topc) && botc == ' ' { + ret.push('│'); + } else if topc == ' ' { + ret.push(botc); + } else if "┬╥".contains(topc) && " ║│".contains(botc) && icod == "top" { + ret.push(topc); + } else if topc == '┬' && botc == ' ' && icod == "bot" { + ret.push('│'); + } else if topc == '╥' && botc == ' ' && icod == "bot" { + ret.push('║'); + } else if "┬│".contains(topc) && botc == '═' { + ret.push('╪'); + } else if "┬│".contains(topc) && botc == '─' { + ret.push('┼'); + } else if "└┘║│░".contains(topc) && botc == ' ' && icod == "top" { + ret.push(topc); + } else if "─═".contains(topc) && botc == ' ' && icod == "top" { + ret.push(topc); + } else if "─═".contains(topc) && botc == ' ' && icod == "bot" { + ret.push(botc); + } else if "║╥".contains(topc) && botc == '═' { + ret.push('╬'); + } else if "║╥".contains(topc) && botc == '─' { + ret.push('╫'); + } else if "║╫╬".contains(topc) && botc == ' ' { + ret.push('║'); + } else if "│┼╪".contains(topc) && botc == ' ' { + ret.push('│'); + } else if topc == '└' && botc == '┌' && icod == "top" { + ret.push('├'); + } else if topc == '┘' && botc == '┐' && icod == "top" { + ret.push('┤'); + } else if "┐┌".contains(botc) && icod == "top" { + ret.push('┬'); + } else if "┘└".contains(topc) && botc == '─' && icod == "top" { + ret.push('┴'); + } else if botc == ' ' && icod == "top" { + ret.push(topc); + } else { + ret.push(botc); + } + } + + ret + } +} + + // fn add_wire(&mut self, wire: &ElementWire, ind: usize){ // self.wires[ind].top.push_str(&wire.top); // self.wires[ind].mid.push_str(&wire.mid); @@ -1290,7 +1365,6 @@ impl TextDrawer{ // // self.wires[ind].mid.push_str(&format!("{}{}",&wire.mid,"$")); // // self.wires[ind].bot.push_str(&format!("{}{}",&wire.bot,"$")); // } -} pub fn draw_circuit(circuit: &CircuitData) -> PyResult<()> { let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; From 0dbff3d73e025ed3107b5c5230070439bbe5bf77 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Mon, 27 Oct 2025 14:38:12 +0530 Subject: [PATCH 31/70] added cregbundle functionality --- crates/circuit/src/circuit_drawer.rs | 172 ++++++++++++++++++--------- 1 file changed, 119 insertions(+), 53 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index a955a374e40c..defdadecb943 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -12,6 +12,7 @@ use core::panic; use std::boxed; +use std::cmp::Ordering; use std::fmt::Debug; use std::io::ErrorKind; use std::thread::current; @@ -100,12 +101,29 @@ pub fn get_indices(dag_circ: &DAGCircuit) -> u32{ total_qubits + total_clbits } -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Eq)] enum ElementWireInput<'a> { Qubit(&'a ShareableQubit), Clbit(&'a ShareableClbit), } +impl PartialOrd for ElementWireInput<'_> { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for ElementWireInput<'_> { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + (ElementWireInput::Qubit(q1), ElementWireInput::Qubit(q2)) => Ordering::Equal, + (ElementWireInput::Clbit(c1), ElementWireInput::Clbit(c2)) => Ordering::Equal, + (ElementWireInput::Qubit(_), ElementWireInput::Clbit(_)) => Ordering::Less, + (ElementWireInput::Clbit(_), ElementWireInput::Qubit(_)) => Ordering::Greater, + } + } +} + impl<'a> Debug for ElementWireInput<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let name = match self { @@ -120,14 +138,14 @@ impl<'a> Debug for ElementWireInput<'a> { /// Input ElementWires. /// The Option is for optional labels and can be used when the registers have different names. This allows us to use /// the same enum for both Input and VerticalLine types. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] enum InputType{ Qubit(Option), Clbit(Option) } /// Enum for representing elements that can appear directly on a wire. -#[derive(Clone, Debug, Copy, PartialEq)] +#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord)] enum ElementOnWire{ Top, Mid, @@ -136,8 +154,8 @@ enum ElementOnWire{ /// Enum for representing elements that appear directly on a wire and how they're connected. -#[derive(Clone, Debug, Copy, PartialEq)] -enum OnElementWire{ +#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord)] +enum OnWire{ Control(ElementOnWire), Swap(ElementOnWire), Barrier, @@ -156,6 +174,12 @@ enum Boxed<'a>{ // however the behaviour of this PartialEq is not ideal as it only checks the variant type // because PackedInstructions cannot be compared directly +impl PartialOrd for Boxed<'_> { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + impl PartialEq for Boxed<'_> { fn eq(&self, other: &Self) -> bool { match (self, other) { @@ -166,6 +190,20 @@ impl PartialEq for Boxed<'_> { } } +impl Eq for Boxed<'_> {} + +impl Ord for Boxed<'_> { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + match (self, other) { + (Boxed::Single(_), Boxed::Single(_)) => std::cmp::Ordering::Equal, + (Boxed::Multi(_), Boxed::Multi(_)) => std::cmp::Ordering::Equal, + (Boxed::Single(_), Boxed::Multi(_)) => std::cmp::Ordering::Less, + (Boxed::Multi(_), Boxed::Single(_)) => std::cmp::Ordering::Greater, + } + } +} + + impl<'a> Debug for Boxed<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -185,14 +223,14 @@ struct Op<'a>{ } -#[derive(Default, Clone, Debug, PartialEq)] +#[derive(Default, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] enum VisualizationElement<'a>{ #[default] Empty, // Marker for no element - Boxed(Boxed<'a>), - Input(ElementWireInput<'a>), - DirectOnElementWire(OnElementWire), VerticalLine(InputType), + Input(ElementWireInput<'a>), + DirectOnWire(OnWire), + Boxed(Boxed<'a>), } @@ -213,6 +251,14 @@ impl<'a> VisualizationLayer<'a> { self.0.len() } + fn push(&mut self, element: VisualizationElement<'a>) { + self.0.push(element); + } + + fn drain(&mut self, range: std::ops::Range) -> impl Iterator> + '_ { + self.0.drain(range) + } + fn add_input(&mut self, input: ElementWireInput<'a>, idx: usize) { self.0[idx] = VisualizationElement::Input(input); } @@ -299,11 +345,11 @@ impl<'a> VisualizationLayer<'a> { fn add_controls(&mut self, controls: &Vec, range: (usize, usize)) { for control in controls { if *control == range.0 { - self.0[*control] = VisualizationElement::DirectOnElementWire(OnElementWire::Control(ElementOnWire::Top)); + self.0[*control] = VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Top)); } else if *control == range.1 { - self.0[*control] = VisualizationElement::DirectOnElementWire(OnElementWire::Control(ElementOnWire::Bot)); + self.0[*control] = VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Bot)); } else { - self.0[*control] = VisualizationElement::DirectOnElementWire(OnElementWire::Control(ElementOnWire::Mid)); + self.0[*control] = VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Mid)); } } } @@ -360,13 +406,13 @@ impl<'a> VisualizationLayer<'a> { fn add_barrier(&mut self, inst: &'a PackedInstruction, circuit: &CircuitData, barrier_indices: &Vec) { for bline in barrier_indices { - self.0[*bline] = VisualizationElement::DirectOnElementWire(OnElementWire::Barrier); + self.0[*bline] = VisualizationElement::DirectOnWire(OnWire::Barrier); } } fn add_reset(&mut self, inst: &'a PackedInstruction, circuit: &CircuitData, reset_indices: &Vec) { for rline in reset_indices { - self.0[*rline] = VisualizationElement::DirectOnElementWire(OnElementWire::Reset); + self.0[*rline] = VisualizationElement::DirectOnWire(OnWire::Reset); } } @@ -385,11 +431,11 @@ impl<'a> VisualizationLayer<'a> { let targets = boxed_indices.iter().rev().take(2).collect_vec(); for target in targets { if *target == range.0 { - self.0[*target] = VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Top)); + self.0[*target] = VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Top)); } else if *target == range.1 { - self.0[*target] = VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Bot)); + self.0[*target] = VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Bot)); } else { - self.0[*target] = VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Mid)); + self.0[*target] = VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Mid)); } } return; @@ -442,7 +488,7 @@ impl<'a> VisualizationLayer<'a> { /// A dense representation of the circuit of size N * (M + 1), where the first /// layer(column) represents the qubits and clbits inputs in the circuits, and /// M is the number of operation layers. -#[derive(Debug)] +#[derive(Debug, Clone)] struct VisualizationMatrix<'a> { layers: Vec>, circuit: &'a CircuitData, @@ -457,8 +503,6 @@ impl<'a> VisualizationMatrix<'a> { let num_wires = circuit.num_qubits() + circuit.num_clbits(); let mut layers = vec![VisualizationLayer(vec![VisualizationElement::default(); num_wires]); inst_layers.len() + 1]; // Add 1 to account for the inputs layer - // TODO: add the qubit/clbit inputs here to layer #0 - let input_layer = layers.first_mut().unwrap(); let mut input_idx = 0; for qubit in circuit.qubits().objects() { @@ -574,16 +618,19 @@ impl ElementWire{ } } + fn pad_wire_left(&mut self, mid_char: char, width: usize){ + let current_width = self.width(); + if current_width < width{ + Self::left_pad_string(&mut self.top, ' ', width); + Self::left_pad_string(&mut self.mid, mid_char, width); + Self::left_pad_string(&mut self.bot, ' ', width); + } + //println!("layer width:{}, object width:{} : \n{}\n{}\n{}", width, current_width, self.top, self.mid, self.bot); + } + fn pad_wire(&mut self, mid_char: char, width: usize){ let current_width = self.width(); if current_width < width{ - // let pad_size = width - current_width; - // let left_pad = pad_size / 2 - 1; - // let right_pad = pad_size - left_pad - 1; - // let left_pad_str = mid_char.to_string().repeat(left_pad); - // let right_pad_str = mid_char.to_string().repeat(right_pad); - // self.top = format!("{}{}{}", " ".to_string().repeat(left_pad), self.top, " ".to_string().repeat(right_pad)); - // self.mid = format!("{}{}{}", left_pad_str, self.mid, right_pad_str); Self::pad_string(&mut self.top, ' ', width); Self::pad_string(&mut self.mid, mid_char, width); Self::pad_string(&mut self.bot, ' ', width); @@ -766,7 +813,7 @@ impl TextDrawer{ } StandardGate::ISwap => { // i-Swap gate - Some(" iSWAP ".to_string()) + Some(" Iswap ".to_string()) } StandardGate::CPhase => { // Controlled phase gate @@ -882,7 +929,7 @@ impl TextDrawer{ instruction_label } - fn from_visualization_matrix(vis_mat: &VisualizationMatrix) -> Self{ + fn from_visualization_matrix<'a>(vis_mat: &'a VisualizationMatrix) -> Self{ let mut wires: Vec> = vec![]; for _ in 0..vis_mat.num_wires(){ wires.push(vec![]); @@ -892,8 +939,28 @@ impl TextDrawer{ wires }; + let cregbundle = true; + + let post_processed_vis_mat = { + if !cregbundle { + None + } else { + let classical_bits_range = (vis_mat.circuit.num_qubits(), vis_mat.circuit.num_qubits() + vis_mat.circuit.num_clbits()); + let mut temp_vis_mat: VisualizationMatrix<'a> = vis_mat.clone(); + // compare the last classical elements and replace with max in the vector + for layer in temp_vis_mat.layers.iter_mut(){ + if let Some(max_element) = layer.drain(classical_bits_range.0..classical_bits_range.1).max(){ + layer.push(max_element.clone()); + } else { + layer.push(VisualizationElement::default()); + } + } + Some(temp_vis_mat) + } + }.unwrap_or(vis_mat.clone()); + let mut ct = 0; - for layer in &vis_mat.layers{ + for layer in &post_processed_vis_mat.layers{ let layer_wires = Self::draw_layer(layer, vis_mat, ct); ct += 1; for (i, wire) in layer_wires.iter().enumerate(){ @@ -924,7 +991,7 @@ impl TextDrawer{ for (i,wire) in wires.iter_mut().enumerate(){ if layer_ind == 0{ - wire.pad_wire(' ', layer_width); + wire.pad_wire_left(' ', layer_width); } else if i < num_qubits{ wire.pad_wire(Q_WIRE, layer_width); // wire.pad_wire('$', layer_width); @@ -944,19 +1011,19 @@ impl TextDrawer{ // from single wired to double wired. let top_cases: Vec = vec![ - VisualizationElement::DirectOnElementWire(OnElementWire::Control(ElementOnWire::Top)), - VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Top)), - VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Mid)), - VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Mid)), + VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Top)), + VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Top)), + VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Mid)), + VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Mid)), VisualizationElement::VerticalLine(InputType::Qubit(None)), VisualizationElement::VerticalLine(InputType::Clbit(None)), ]; let bot_cases: Vec = vec![ - VisualizationElement::DirectOnElementWire(OnElementWire::Control(ElementOnWire::Bot)), - VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Bot)), - VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Mid)), - VisualizationElement::DirectOnElementWire(OnElementWire::Swap(ElementOnWire::Mid)), + VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Bot)), + VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Bot)), + VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Mid)), + VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Mid)), VisualizationElement::VerticalLine(InputType::Qubit(None)), VisualizationElement::VerticalLine(InputType::Clbit(None)), ]; @@ -1050,7 +1117,6 @@ impl TextDrawer{ // qubits being affected are [0,1,2,3,4,5] and num controls is 2, then the qubits will be indexed as [C,C,T,T,T,T] // so for qubits [2,3,4,5] the indexes inside the box will be 0,1,2,3 respectively. - // get index of ind inside qargs or cargs let temp: String = { if qargs.contains(&ind) { let idx = qargs.iter().position(|&x| x == ind).unwrap(); @@ -1107,7 +1173,7 @@ impl TextDrawer{ } } }, - VisualizationElement::DirectOnElementWire(on_wire) => { + VisualizationElement::DirectOnWire(on_wire) => { let connecting_wire = if ind < circuit.num_qubits() { CONNECTING_WIRE @@ -1116,62 +1182,62 @@ impl TextDrawer{ }; let wire_char: String = match on_wire { - OnElementWire::Control(position) => { + OnWire::Control(position) => { if ind < circuit.num_qubits() { BULLET.to_string() } else { C_WIRE_CON_TOP.to_string() } }, - OnElementWire::Swap(position) => { + OnWire::Swap(position) => { "X".to_string() }, - OnElementWire::Barrier => BARRIER.to_string(), - OnElementWire::Reset => "|0>".to_string(), + OnWire::Barrier => BARRIER.to_string(), + OnWire::Reset => "|0>".to_string(), }; let top:String = match on_wire{ - OnElementWire::Control(position) => { + OnWire::Control(position) => { match position { ElementOnWire::Top => " ".to_string(), ElementOnWire::Mid => format!("{}", connecting_wire), ElementOnWire::Bot => format!("{}", connecting_wire), } }, - OnElementWire::Swap(position) => { + OnWire::Swap(position) => { match position { ElementOnWire::Top => " ".to_string(), ElementOnWire::Mid => format!("{}", connecting_wire), ElementOnWire::Bot => format!("{}", connecting_wire), } }, - OnElementWire::Barrier => { + OnWire::Barrier => { format!("{}", BARRIER) }, - OnElementWire::Reset => { + OnWire::Reset => { " ".to_string() }, }; let bot: String = match on_wire{ - OnElementWire::Control(position) => { + OnWire::Control(position) => { match position { ElementOnWire::Top => format!("{}", connecting_wire), ElementOnWire::Mid => format!("{}", connecting_wire), ElementOnWire::Bot => " ".to_string(), } }, - OnElementWire::Swap(position) => { + OnWire::Swap(position) => { match position { ElementOnWire::Top => format!("{}", connecting_wire), ElementOnWire::Mid => format!("{}", connecting_wire), ElementOnWire::Bot => " ".to_string(), } }, - OnElementWire::Barrier => { + OnWire::Barrier => { format!("{}", BARRIER) }, - OnElementWire::Reset => { + OnWire::Reset => { " ".to_string() }, }; From 16214958e8bf0402f293fde0a1543a9c0fb399da Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Sun, 2 Nov 2025 22:08:29 +0200 Subject: [PATCH 32/70] Clean up various `VisualizationMatrix` construction flow --- crates/circuit/src/circuit_drawer.rs | 960 ++++++++++++++------------- crates/circuit/src/lib.rs | 2 +- 2 files changed, 485 insertions(+), 477 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index defdadecb943..2e53f2af5a87 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -10,70 +10,83 @@ // copyright notice, and modified files need to carry a notice indicating // that they have been altered from the originals. +use crate::bit::{ShareableClbit, ShareableQubit}; +use crate::dag_circuit::DAGCircuit; +use crate::operations::{Operation, OperationRef, Param, StandardGate, StandardInstruction}; +use crate::packed_instruction::PackedInstruction; +use crate::{Clbit, Qubit}; use core::panic; -use std::boxed; +use hashbrown::{HashMap, HashSet}; +use itertools::{Itertools, MinMaxResult}; +use rustworkx_core::petgraph::csr::IndexType; +use rustworkx_core::petgraph::stable_graph::NodeIndex; use std::cmp::Ordering; use std::fmt::Debug; -use std::io::ErrorKind; -use std::thread::current; -use hashbrown::HashSet; -use crate::bit::{ShareableClbit, ShareableQubit}; -use crate::dag_circuit::{DAGCircuit}; -use crate::operations::{Operation, OperationRef, StandardGate, StandardInstruction, Param}; -use crate::packed_instruction::{self, PackedInstruction, PackedOperation}; -use itertools::{Itertools, MinMaxResult}; use std::ops::Index; +use pyo3::import_exception; use pyo3::prelude::*; -use pyo3::{import_exception}; - +use crate::circuit_data::CircuitData; use crate::converters::QuantumCircuitData; use crate::dag_circuit::NodeType; -use crate::circuit_data::CircuitData; import_exception!(qiskit.circuit.exceptions, CircuitError); -// [ qubit indice] -// int: n - +// TODO: remove when dev is done, since this is only for manual testing #[pyfunction(name = "draw")] pub fn py_drawer(circuit: QuantumCircuitData) -> PyResult<()> { draw_circuit(&circuit.data)?; Ok(()) } -/// Calculate the range (inclusive) of the given instruction qubits/clbits over the wire indices. -/// The assumption is that clbits always appear after the qubits in the visualization, hence the clbit indices -/// are offset by the number of instruction qubits when calculating the range. -fn get_instruction_range(circuit: &CircuitData, instruction: &PackedInstruction) -> (usize, usize) { - let node_qubits = circuit.get_qargs(instruction.qubits); - let node_clbits = circuit.get_cargs(instruction.clbits); +pub fn draw_circuit(circuit: &CircuitData) -> PyResult<()> { + let vis_mat2 = VisualizationMatrix::from_circuit(circuit)?; + + for inst in circuit.data() { + println!( + "INST {:?} QARGS: {:?}", + inst, + circuit.get_qargs(inst.qubits) + ); + } + println!("======================"); - let indices = node_qubits.iter().map(|q| q.index()).chain( - node_clbits.iter().map(|c| c.index() + circuit.num_qubits())); + println!( + "num wires {}, num layers {}", + vis_mat2.num_wires(), + vis_mat2.num_layers() + ); - match indices.minmax() { - MinMaxResult::MinMax(min, max) => (min, max), - MinMaxResult::OneElement(idx) => (idx, idx), - MinMaxResult::NoElements => panic!("Encountered an instruction without qubits and clbits") + for i in 0..vis_mat2.num_wires() { + for j in 0..vis_mat2.num_layers() { + print!("{:^30}", format!("{:?}", vis_mat2[j][i])); + } + println!(""); } + + let circuit_rep = TextDrawer::from_visualization_matrix(&vis_mat2); + circuit_rep.print(); + Ok(()) } -/// Return a list of PackedInstruction layers such that each layer contain instruction -/// whose qubits/clbits indices do not overlap. The instructions are packed into each layer -/// as long as there is no qubit/clbit overlap -fn build_layers<'a>(dag: &'a DAGCircuit, circuit: &'a CircuitData) -> Vec> { - let mut layers:Vec> = Vec::new(); - let mut current_layer: Option<&mut Vec::<&PackedInstruction>> = None; +/// Return a list of layers such that each layer contains a list of op node indices, representing instructions +/// whose qubits/clbits indices do not overlap. The instruction are packed into each layer as long as there +/// is no qubit/clbit overlap. +fn build_layers(dag: &DAGCircuit) -> Vec> { + let mut layers: Vec> = Vec::new(); + let mut current_layer: Option<&mut Vec> = None; let mut used_wires = HashSet::::new(); for layer in dag.multigraph_layers() { for node_index in layer.into_iter().sorted() { - if let NodeType::Operation(instruction_to_insert) = &dag.dag()[node_index] { - let (node_min, node_max) = get_instruction_range(circuit, instruction_to_insert); + let (node_min, node_max) = get_instruction_range( + dag.get_qargs(instruction_to_insert.qubits), + dag.get_cargs(instruction_to_insert.clbits), + dag.num_qubits(), + ); // Check for instruction range overlap if (node_min..=node_max).any(|idx| used_wires.contains(&idx)) { @@ -87,7 +100,7 @@ fn build_layers<'a>(dag: &'a DAGCircuit, circuit: &'a CircuitData) -> Vec(dag: &'a DAGCircuit, circuit: &'a CircuitData) -> Vec u32{ - let total_qubits = dag_circ.num_qubits() as u32; - let total_clbits = dag_circ.num_clbits() as u32; - total_qubits + total_clbits +/// Calculate the range (inclusive) of the given instruction qubits/clbits over the wire indices. +/// The assumption is that clbits always appear after the qubits in the visualization, hence the clbit indices +/// are offset by the number of instruction qubits when calculating the range. +fn get_instruction_range( + node_qubits: &[Qubit], + node_clbits: &[Clbit], + num_qubits: usize, +) -> (usize, usize) { + let indices = node_qubits + .iter() + .map(|q| q.index()) + .chain(node_clbits.iter().map(|c| c.index() + num_qubits)); + + match indices.minmax() { + MinMaxResult::MinMax(min, max) => (min, max), + MinMaxResult::OneElement(idx) => (idx, idx), + MinMaxResult::NoElements => panic!("Encountered an instruction without qubits and clbits"), + } } #[derive(Clone, PartialEq, Eq)] @@ -135,27 +162,26 @@ impl<'a> Debug for ElementWireInput<'a> { } } -/// Input ElementWires. +/// Input ElementWires. /// The Option is for optional labels and can be used when the registers have different names. This allows us to use /// the same enum for both Input and VerticalLine types. #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -enum InputType{ +enum InputType { Qubit(Option), - Clbit(Option) + Clbit(Option), } /// Enum for representing elements that can appear directly on a wire. #[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord)] -enum ElementOnWire{ +enum ElementOnWire { Top, Mid, Bot, } - /// Enum for representing elements that appear directly on a wire and how they're connected. #[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord)] -enum OnWire{ +enum OnWire { Control(ElementOnWire), Swap(ElementOnWire), Barrier, @@ -164,7 +190,7 @@ enum OnWire{ /// Enum for representing elements that appear in a boxed operation. #[derive(Clone)] -enum Boxed<'a>{ +enum Boxed<'a> { Single(&'a PackedInstruction), // Multi(MultiBoxElement) Multi(&'a PackedInstruction), @@ -203,7 +229,6 @@ impl Ord for Boxed<'_> { } } - impl<'a> Debug for Boxed<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -213,18 +238,16 @@ impl<'a> Debug for Boxed<'a> { } } - /// Enum for representing the elements stored in a visualization matrix. The elements /// do not directly implement visualization capabilities, but rather carry enough information /// to enable visualization later on by the actual drawer. -struct Op<'a>{ +struct Op<'a> { instruction: &'a PackedInstruction, - } #[derive(Default, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -enum VisualizationElement<'a>{ +enum VisualizationElement<'a> { #[default] Empty, // Marker for no element VerticalLine(InputType), @@ -233,7 +256,6 @@ enum VisualizationElement<'a>{ Boxed(Boxed<'a>), } - /// A representation of a single column (called here a layer) of a visualization matrix #[derive(Clone, Debug)] struct VisualizationLayer<'a>(Vec>); @@ -255,7 +277,10 @@ impl<'a> VisualizationLayer<'a> { self.0.push(element); } - fn drain(&mut self, range: std::ops::Range) -> impl Iterator> + '_ { + fn drain( + &mut self, + range: std::ops::Range, + ) -> impl Iterator> + '_ { self.0.drain(range) } @@ -264,223 +289,155 @@ impl<'a> VisualizationLayer<'a> { } /// Adds the required visualization elements to represent the given instruction - fn add_instruction(&mut self, inst: &'a PackedInstruction, circuit: &CircuitData) { - match inst.op.view() { - OperationRef::StandardGate(_gate) => self.add_standard(inst, circuit), - OperationRef::StandardInstruction(_instruction) => { - if let StandardInstruction::Barrier(_) = _instruction { - let barrier_indices = self.get_boxed_indices(inst, circuit); - self.add_barrier(inst, circuit, &barrier_indices); - } else if let StandardInstruction::Reset = _instruction { - let reset_indices = self.get_boxed_indices(inst, circuit); - self.add_reset(inst, circuit, &reset_indices); - } else { - self.add_standard(inst, circuit) - } - }, - _ => unimplemented!("{}", format!("Visualization is not implemented for instruction of type {:?}", inst.op)), - } - } - - - fn get_controls(&self, inst: &PackedInstruction, circuit: &CircuitData) -> Vec { - let gate_has_control = vec![StandardGate::CX, StandardGate::CCX, StandardGate::CY, StandardGate::CZ, - StandardGate::CRX, StandardGate::CRY, StandardGate::CRZ, - StandardGate::CPhase, StandardGate::CS, StandardGate::CSdg, - StandardGate::CSX, StandardGate::CU, StandardGate::CU1, - StandardGate::CU3, StandardGate::CH, StandardGate::C3SX, StandardGate::C3X, - StandardGate::RC3X, StandardGate::RCCX, StandardGate::CSwap]; - - let inst_has_controls = vec![StandardInstruction::Measure]; - - let mut controls = vec![]; - - let std_op = inst.op.view(); - - match std_op { - OperationRef::StandardGate(gate) => { - if gate == StandardGate::CSwap { - let qargs = circuit.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); - let control = qargs[0]; - controls.push(control); - controls - } else if gate_has_control.contains(&gate) { - // let qargs = dag.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); - let qargs = circuit.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); - - let target = qargs.last().unwrap(); - let (minima,maxima) = get_instruction_range(circuit, inst); - - for control in minima..=maxima { - if control != *target && qargs.contains(&control) { - controls.push(control); - } - } - controls - } else { - vec![] - } + fn add_instruction(&mut self, packed_inst: &'a PackedInstruction, circuit: &CircuitData) { + match packed_inst.op.view() { + OperationRef::StandardGate(gate) => self.add_standard_gate(gate, packed_inst, circuit), + OperationRef::StandardInstruction(std_inst) => { + self.add_standard_instruction(std_inst, packed_inst, circuit) } - OperationRef::StandardInstruction(instruction) => { - if inst_has_controls.contains(&instruction) { - let qargs = circuit.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); - let target = qargs.last().unwrap(); - let cargs = circuit.get_cargs(inst.clbits).into_iter().map(|c| c.index() + circuit.num_qubits()).collect_vec(); - let (minima,maxima) = get_instruction_range(circuit, inst); - - for control in minima..=maxima { - if control != *target && cargs.contains(&control) { - controls.push(control); - } - } - controls - } else { - vec![] - } - }, - _ => vec![] + _ => unimplemented!( + "{}", + format!( + "Visualization is not implemented for instruction of type {:?}", + packed_inst.op + ) + ), } } - fn add_controls(&mut self, controls: &Vec, range: (usize, usize)) { + fn add_controls(&mut self, controls: &HashSet, range: (usize, usize)) { for control in controls { if *control == range.0 { - self.0[*control] = VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Top)); + self.0[*control] = + VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Top)); } else if *control == range.1 { - self.0[*control] = VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Bot)); + self.0[*control] = + VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Bot)); } else { - self.0[*control] = VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Mid)); + self.0[*control] = + VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Mid)); } } } - fn get_boxed_indices(&mut self, inst: & PackedInstruction, circuit: &CircuitData) -> Vec{ - let single_box = vec![StandardGate::H, StandardGate::X, StandardGate::Y, StandardGate::Z, - StandardGate::RX, StandardGate::RY, StandardGate::RZ, StandardGate::U, StandardGate::U1, - StandardGate::U2, StandardGate::U3, StandardGate::S, StandardGate::Sdg, - StandardGate::T, StandardGate::Tdg, StandardGate::Phase, StandardGate::R, StandardGate::SX, - StandardGate::SXdg, StandardGate::CCX, StandardGate::CCZ, StandardGate::CX, StandardGate::CY, - StandardGate::CZ, StandardGate::CPhase, StandardGate::CRX, StandardGate::CRY, - StandardGate::CRZ, StandardGate::CS, StandardGate::CSdg, StandardGate::CSX, - StandardGate::CU, StandardGate::CU1, StandardGate::CU3, StandardGate::C3X, StandardGate::C3SX, StandardGate::RC3X]; - - let single_box_instrusctions = vec![StandardInstruction::Measure]; - - let multi_box = vec![StandardGate::ISwap, StandardGate::DCX, - StandardGate::ECR, StandardGate::RXX, StandardGate::RYY, StandardGate::RZZ, - StandardGate::RZX, StandardGate::XXMinusYY, StandardGate::XXPlusYY]; - - let direct_on_wire = vec![StandardGate::Swap, StandardGate::CSwap]; - - let special_cases = vec![ StandardGate::GlobalPhase, StandardGate::CPhase]; - - let qargs = circuit.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); - let target = qargs.last().unwrap(); - let range = get_instruction_range(circuit, inst); - - if let Some(std_gate) = inst.op.try_standard_gate(){ - if single_box.contains(&std_gate){ - vec![*target] - } else if multi_box.contains(&std_gate){ - (range.0..=range.1).collect() - } else if direct_on_wire.contains(&std_gate){ - qargs.iter().rev().take(2).cloned().collect_vec() - } else { - vec![] - } - } else if let Some(std_instruction) = inst.op.try_standard_instruction(){ - if single_box_instrusctions.contains(&std_instruction){ - vec![*target] - } else if let StandardInstruction::Barrier(_) = std_instruction { - (range.0..=range.1).collect() - } else if let StandardInstruction::Reset = std_instruction{ - qargs + fn add_vertical_lines(&mut self, inst: &'a PackedInstruction, vertical_lines: I) + where + I: Iterator, + { + let double_lines = vec![StandardInstruction::Measure]; + let input_type: InputType = + if let Some(std_instruction) = inst.op.try_standard_instruction() { + if double_lines.contains(&std_instruction) { + InputType::Clbit(None) + } else { + InputType::Qubit(None) + } } else { - vec![] - } - } else { - vec![] + InputType::Qubit(None) + }; + for vline in vertical_lines { + self.0[vline] = VisualizationElement::VerticalLine(input_type.clone()); } - } - fn add_barrier(&mut self, inst: &'a PackedInstruction, circuit: &CircuitData, barrier_indices: &Vec) { - for bline in barrier_indices { - self.0[*bline] = VisualizationElement::DirectOnWire(OnWire::Barrier); - } - } + fn add_standard_gate( + &mut self, + gate: StandardGate, + inst: &'a PackedInstruction, + circuit: &CircuitData, + ) { + let qargs = circuit.get_qargs(inst.qubits); + let (minima, maxima) = get_instruction_range( + qargs, + circuit.get_cargs(inst.clbits), + circuit.num_qubits(), + ); + + match gate { + StandardGate::ISwap + | StandardGate::DCX + | StandardGate::ECR + | StandardGate::RXX + | StandardGate::RYY + | StandardGate::RZZ + | StandardGate::RZX + | StandardGate::XXMinusYY + | StandardGate::XXPlusYY + | StandardGate::RCCX + | StandardGate::RC3X => { + for q in minima..=maxima { + self.0[q.index()] = VisualizationElement::Boxed(Boxed::Multi(inst)); + } + } + // StandardGate::Swap | StandardGate::CSwap => { + // // qargs[qargs.len() - 1..].iter().map(|q| q.index()).collect() + // // for target in targets { + // // if *target == range.0 { + // // self.0[*target] = VisualizationElement::DirectOnWire(OnWire::Swap( + // // ElementOnWire::Top, + // // )); + // // } else if *target == range.1 { + // // self.0[*target] = VisualizationElement::DirectOnWire(OnWire::Swap( + // // ElementOnWire::Bot, + // // )); + // // } else { + // // self.0[*target] = VisualizationElement::DirectOnWire(OnWire::Swap( + // // ElementOnWire::Mid, + // // )); + // // } + // }, + StandardGate::H | StandardGate::RX | StandardGate::RZ => { + self.0[qargs[0].index()] = VisualizationElement::Boxed(Boxed::Single(inst)); + }, + StandardGate::CX | StandardGate::CCX => { + self.0[qargs.last().unwrap().index()] = VisualizationElement::Boxed(Boxed::Single(inst)); + let mut control_indices: HashSet = HashSet::new(); + if gate.num_ctrl_qubits() > 0 { + control_indices.extend(qargs.iter().take(qargs.len() - 1).map(|q| q.index())); + self.add_controls(&control_indices, (minima, maxima)); + } - fn add_reset(&mut self, inst: &'a PackedInstruction, circuit: &CircuitData, reset_indices: &Vec) { - for rline in reset_indices { - self.0[*rline] = VisualizationElement::DirectOnWire(OnWire::Reset); + } + _ => unimplemented!("{}", format!("{:?} is not supported yet", gate)) } + + // let vert_lines = (minima..=maxima) + // .filter(|idx| !control_indices.contains(idx) && !box_indices.contains(idx)); + // self.add_vertical_lines(inst, vert_lines); } - // [] - - fn add_boxed(&mut self, inst: &'a PackedInstruction, circuit: &CircuitData, boxed_indices: &Vec) { - let special_cases = vec![StandardGate::Swap, StandardGate::CSwap]; - if boxed_indices.len() == 1 { - let qargs = circuit.get_qargs(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); - let target = qargs.last().unwrap(); - self.0[*target] = VisualizationElement::Boxed(Boxed::Single(inst)); - } else if boxed_indices.len() > 1 { - if let Some(std_gate) = inst.op.try_standard_gate(){ - if special_cases.contains(&std_gate){ - let range= get_instruction_range(circuit, inst); - let targets = boxed_indices.iter().rev().take(2).collect_vec(); - for target in targets { - if *target == range.0 { - self.0[*target] = VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Top)); - } else if *target == range.1 { - self.0[*target] = VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Bot)); - } else { - self.0[*target] = VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Mid)); - } - } - return; - } else { - for idx in boxed_indices { - self.0[*idx] = VisualizationElement::Boxed(Boxed::Multi(inst)); - } + fn add_standard_instruction( + &mut self, + std_inst: StandardInstruction, + inst: &'a PackedInstruction, + circuit: &CircuitData, + ) { + let qargs = circuit.get_qargs(inst.qubits); + let (minima, maxima) = + get_instruction_range(qargs, circuit.get_cargs(inst.clbits), circuit.num_qubits()); + + match std_inst { + StandardInstruction::Barrier(_) => { + for q in qargs { + self.0[q.index()] = VisualizationElement::DirectOnWire(OnWire::Barrier); } - } else{ - for idx in boxed_indices { - self.0[*idx] = VisualizationElement::Boxed(Boxed::Multi(inst)); + } + StandardInstruction::Reset => { + for q in qargs { + self.0[q.index()] = VisualizationElement::DirectOnWire(OnWire::Reset); } } - } - } - - fn add_vertical_lines(&mut self, inst: &'a PackedInstruction, circuit: &CircuitData, vertical_lines: &Vec) { - let double_lines = vec![StandardInstruction::Measure]; - let input_type: InputType = if let Some(std_instruction) = inst.op.try_standard_instruction(){ - if double_lines.contains(&std_instruction){ - InputType::Clbit(None) - } else { - InputType::Qubit(None) + StandardInstruction::Measure => { + self.0[qargs.last().unwrap().index()] = VisualizationElement::Boxed(Boxed::Single(inst)); + self.add_vertical_lines(inst, minima + 1..=maxima); + } + StandardInstruction::Delay(_) => { + for q in qargs { + self.0[q.index()] = VisualizationElement::Boxed(Boxed::Single(inst)); + } } - } else { - InputType::Qubit(None) - }; - for vline in vertical_lines { - self.0[*vline] = VisualizationElement::VerticalLine(input_type.clone() ); } } - - // function to add standard gates and instructions - fn add_standard(&mut self, inst: &'a PackedInstruction, circuit: &CircuitData) { - let (minima,maxima) = get_instruction_range(circuit, inst); - let controls = self.get_controls(inst, circuit); - let boxed_elements = self.get_boxed_indices(inst, circuit); - let vert_lines = (minima..=maxima) - .filter(|idx| !controls.contains(idx) && !boxed_elements.contains(idx)) - .collect_vec(); - self.add_controls(&controls, (minima, maxima)); - self.add_boxed(inst, circuit, &boxed_elements); - self.add_vertical_lines(inst, circuit, &vert_lines); - - } } /// A Plain, logical 2D representation of a circuit. @@ -495,13 +452,22 @@ struct VisualizationMatrix<'a> { } impl<'a> VisualizationMatrix<'a> { + fn from_circuit(circuit: &'a CircuitData) -> PyResult { + let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; - fn from_circuit(dag: &'a DAGCircuit,circuit: &'a CircuitData) -> PyResult { - // let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; - let inst_layers = build_layers(&dag, circuit); + let mut node_index_to_inst: HashMap = + HashMap::with_capacity(circuit.data().len()); + for (idx, node_index) in dag.op_node_indices(true).enumerate() { + node_index_to_inst.insert(node_index, &circuit.data()[idx]); + } + + let inst_layers = build_layers(&dag); let num_wires = circuit.num_qubits() + circuit.num_clbits(); - let mut layers = vec![VisualizationLayer(vec![VisualizationElement::default(); num_wires]); inst_layers.len() + 1]; // Add 1 to account for the inputs layer + let mut layers = vec![ + VisualizationLayer(vec![VisualizationElement::default(); num_wires]); + inst_layers.len() + 1 + ]; // Add 1 to account for the inputs layer let input_layer = layers.first_mut().unwrap(); let mut input_idx = 0; @@ -516,15 +482,12 @@ impl<'a> VisualizationMatrix<'a> { } for (i, layer) in inst_layers.iter().enumerate() { - for inst in layer { - layers[i + 1].add_instruction(inst, &circuit); + for node_index in layer { + layers[i + 1].add_instruction(node_index_to_inst.get(node_index).unwrap(), circuit); } } - Ok(VisualizationMatrix{ - layers, - circuit, - }) + Ok(VisualizationMatrix { layers, circuit }) } fn num_wires(&self) -> usize { @@ -544,26 +507,26 @@ impl<'a> Index for VisualizationMatrix<'a> { } } - // [control control boxed boxed] - // [swap swap] - // [control swaps swaps], +// [control control boxed boxed] +// [swap swap] +// [control swaps swaps], // better name for the struct #[derive(Clone)] -struct ElementWire{ - top:String, - mid:String, - bot:String, +struct ElementWire { + top: String, + mid: String, + bot: String, } -impl Debug for ElementWire{ +impl Debug for ElementWire { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}\n{}\n{}", self.top, self.mid, self.bot) } } -impl ElementWire{ - fn width(&self) -> usize{ +impl ElementWire { + fn width(&self) -> usize { // return the max of all strigns // self.top.len().max(self.mid.len()).max(self.bot.len()) let top = self.top.chars().count(); @@ -585,9 +548,9 @@ impl ElementWire{ max } - fn left_pad_string(s: &mut String, pad_char: char, width: usize){ + fn left_pad_string(s: &mut String, pad_char: char, width: usize) { let current_width = s.len(); - if current_width < width{ + if current_width < width { let pad_size = width - current_width; let pad_str = pad_char.to_string().repeat(pad_size); let new_str = format!("{}{}", pad_str, s); @@ -595,9 +558,9 @@ impl ElementWire{ } } - fn right_pad_string(s: &mut String, pad_char: char, width: usize){ + fn right_pad_string(s: &mut String, pad_char: char, width: usize) { let current_width = s.len(); - if current_width < width{ + if current_width < width { let pad_size = width - current_width; let pad_str = pad_char.to_string().repeat(pad_size); let new_str = format!("{}{}", s, pad_str); @@ -605,9 +568,9 @@ impl ElementWire{ } } - fn pad_string(s: &mut String, pad_char: char, width: usize){ + fn pad_string(s: &mut String, pad_char: char, width: usize) { let current_width = s.chars().count(); - if current_width < width{ + if current_width < width { let pad_size = width - current_width; let left_pad = pad_size / 2; let right_pad = pad_size - left_pad; @@ -618,9 +581,9 @@ impl ElementWire{ } } - fn pad_wire_left(&mut self, mid_char: char, width: usize){ + fn pad_wire_left(&mut self, mid_char: char, width: usize) { let current_width = self.width(); - if current_width < width{ + if current_width < width { Self::left_pad_string(&mut self.top, ' ', width); Self::left_pad_string(&mut self.mid, mid_char, width); Self::left_pad_string(&mut self.bot, ' ', width); @@ -628,9 +591,9 @@ impl ElementWire{ //println!("layer width:{}, object width:{} : \n{}\n{}\n{}", width, current_width, self.top, self.mid, self.bot); } - fn pad_wire(&mut self, mid_char: char, width: usize){ + fn pad_wire(&mut self, mid_char: char, width: usize) { let current_width = self.width(); - if current_width < width{ + if current_width < width { Self::pad_string(&mut self.top, ' ', width); Self::pad_string(&mut self.mid, mid_char, width); Self::pad_string(&mut self.bot, ' ', width); @@ -639,13 +602,12 @@ impl ElementWire{ } } - pub const Q_WIRE: char = '─'; pub const C_WIRE: char = '═'; pub const TOP_CON: char = '┴'; pub const BOT_CON: char = '┬'; -pub const C_WIRE_CON_TOP:char = '╩'; -pub const C_BOT_CON:char = '╥'; +pub const C_WIRE_CON_TOP: char = '╩'; +pub const C_BOT_CON: char = '╥'; pub const Q_LEFT_CON: char = '┤'; pub const Q_RIGHT_CON: char = '├'; pub const CL_LEFT_CON: char = '╡'; @@ -655,19 +617,19 @@ pub const TOP_RIGHT_BOX: char = '┐'; pub const BOT_LEFT_BOX: char = '└'; pub const BOT_RIGHT_BOX: char = '┘'; pub const BARRIER: char = '░'; -pub const BULLET:char = '■'; -pub const CONNECTING_WIRE:char = '│'; -pub const CL_CONNECTING_WIRE:char = '║'; -pub const Q_Q_CROSSED_WIRE:char = '┼'; -pub const Q_CL_CROSSED_WIRE:char = '╪'; -pub const CL_CL_CROSSED_WIRE:char = '╬'; -pub const CL_Q_CROSSED_WIRE:char = '╫'; - -struct TextDrawer{ +pub const BULLET: char = '■'; +pub const CONNECTING_WIRE: char = '│'; +pub const CL_CONNECTING_WIRE: char = '║'; +pub const Q_Q_CROSSED_WIRE: char = '┼'; +pub const Q_CL_CROSSED_WIRE: char = '╪'; +pub const CL_CL_CROSSED_WIRE: char = '╬'; +pub const CL_Q_CROSSED_WIRE: char = '╫'; + +struct TextDrawer { wires: Vec>, } -impl Index for TextDrawer{ +impl Index for TextDrawer { type Output = Vec; fn index(&self, index: usize) -> &Self::Output { @@ -675,17 +637,16 @@ impl Index for TextDrawer{ } } -impl TextDrawer{ - - fn get_label(instruction: &PackedInstruction) -> Option{ +impl TextDrawer { + fn get_label(instruction: &PackedInstruction) -> Option { let label = instruction.label(); let mut instruction_param = String::new(); - for param in instruction.params_view(){ + for param in instruction.params_view() { let param_sub_str = match param { Param::Float(f) => format!("{:.2}", f), _ => format!("{:?}", param), }; - if instruction_param.is_empty(){ + if instruction_param.is_empty() { instruction_param = param_sub_str; } else { instruction_param = format!("{}, {}", instruction_param, param_sub_str); @@ -693,9 +654,7 @@ impl TextDrawer{ } // let instruction_param =format!("{:?}",instruction.params_view()); let instruction_label = match label { - Some(l) => { - Some(format!("{}{}{}"," ",l.to_string()," ")) - } + Some(l) => Some(format!("{}{}{}", " ", l.to_string(), " ")), None => { if let Some(standard_gate) = instruction.op.try_standard_gate() { match standard_gate { @@ -909,35 +868,43 @@ impl TextDrawer{ Some(format!(" RX({}) ", instruction_param)) } } - } else if let Some(std_instruction) = instruction.op.try_standard_instruction(){ - if std_instruction == StandardInstruction::Measure{ + } else if let Some(std_instruction) = instruction.op.try_standard_instruction() { + if std_instruction == StandardInstruction::Measure { Some("M".to_string()) - } else if std_instruction == StandardInstruction::Reset{ + } else if std_instruction == StandardInstruction::Reset { Some("|0>".to_string()) } else if let StandardInstruction::Barrier(_) = std_instruction { Some("░".to_string()) } else { // Fallback for non-standard instructions - Some(format!("{}{}{}"," ",instruction.op.name().to_string()," ")) + Some(format!( + "{}{}{}", + " ", + instruction.op.name().to_string(), + " " + )) } } else { // Fallback for non-standard operations - Some(format!("{}{}{}"," ",instruction.op.name().to_string()," ")) + Some(format!( + "{}{}{}", + " ", + instruction.op.name().to_string(), + " " + )) } - }, + } }; instruction_label } - fn from_visualization_matrix<'a>(vis_mat: &'a VisualizationMatrix) -> Self{ + fn from_visualization_matrix<'a>(vis_mat: &'a VisualizationMatrix) -> Self { let mut wires: Vec> = vec![]; - for _ in 0..vis_mat.num_wires(){ + for _ in 0..vis_mat.num_wires() { wires.push(vec![]); } - let mut text_drawer = TextDrawer{ - wires - }; + let mut text_drawer = TextDrawer { wires }; let cregbundle = true; @@ -945,11 +912,17 @@ impl TextDrawer{ if !cregbundle { None } else { - let classical_bits_range = (vis_mat.circuit.num_qubits(), vis_mat.circuit.num_qubits() + vis_mat.circuit.num_clbits()); + let classical_bits_range = ( + vis_mat.circuit.num_qubits(), + vis_mat.circuit.num_qubits() + vis_mat.circuit.num_clbits(), + ); let mut temp_vis_mat: VisualizationMatrix<'a> = vis_mat.clone(); // compare the last classical elements and replace with max in the vector - for layer in temp_vis_mat.layers.iter_mut(){ - if let Some(max_element) = layer.drain(classical_bits_range.0..classical_bits_range.1).max(){ + for layer in temp_vis_mat.layers.iter_mut() { + if let Some(max_element) = layer + .drain(classical_bits_range.0..classical_bits_range.1) + .max() + { layer.push(max_element.clone()); } else { layer.push(VisualizationElement::default()); @@ -957,13 +930,14 @@ impl TextDrawer{ } Some(temp_vis_mat) } - }.unwrap_or(vis_mat.clone()); - + } + .unwrap_or(vis_mat.clone()); + let mut ct = 0; - for layer in &post_processed_vis_mat.layers{ + for layer in &post_processed_vis_mat.layers { let layer_wires = Self::draw_layer(layer, vis_mat, ct); ct += 1; - for (i, wire) in layer_wires.iter().enumerate(){ + for (i, wire) in layer_wires.iter().enumerate() { text_drawer.wires[i].push(wire.clone()); } } @@ -971,9 +945,13 @@ impl TextDrawer{ text_drawer } - fn draw_layer(layer: &VisualizationLayer, vis_mat: &VisualizationMatrix, layer_ind: usize) -> Vec{ + fn draw_layer( + layer: &VisualizationLayer, + vis_mat: &VisualizationMatrix, + layer_ind: usize, + ) -> Vec { let mut wires: Vec = vec![]; - for (i,element) in layer.0.iter().enumerate(){ + for (i, element) in layer.0.iter().enumerate() { let wire = Self::draw_element(element.clone(), layer, vis_mat.circuit, i); wires.push(wire); } @@ -982,17 +960,17 @@ impl TextDrawer{ //let layer_width = wires.iter().map(|w| w.width()).max().unwrap_or(0); let mut layer_width = 0; - for wire in wires.iter(){ + for wire in wires.iter() { let w = wire.width(); - if w > layer_width{ + if w > layer_width { layer_width = w; } } - for (i,wire) in wires.iter_mut().enumerate(){ - if layer_ind == 0{ + for (i, wire) in wires.iter_mut().enumerate() { + if layer_ind == 0 { wire.pad_wire_left(' ', layer_width); - } else if i < num_qubits{ + } else if i < num_qubits { wire.pad_wire(Q_WIRE, layer_width); // wire.pad_wire('$', layer_width); } else { @@ -1004,7 +982,12 @@ impl TextDrawer{ wires } - pub fn draw_element(vis_ele: VisualizationElement, vis_layer: &VisualizationLayer, circuit: &CircuitData, ind: usize) -> ElementWire { + pub fn draw_element( + vis_ele: VisualizationElement, + vis_layer: &VisualizationLayer, + circuit: &CircuitData, + ind: usize, + ) -> ElementWire { match vis_ele { VisualizationElement::Boxed(sub_type) => { // implement for cases where the box is on classical wires. The left and right connectors will change @@ -1031,8 +1014,8 @@ impl TextDrawer{ // if subtype is measurement then classical connectors let is_measure = match &sub_type { Boxed::Single(inst) => { - if let Some(std_instruction) = inst.op.try_standard_instruction(){ - if std_instruction == StandardInstruction::Measure{ + if let Some(std_instruction) = inst.op.try_standard_instruction() { + if std_instruction == StandardInstruction::Measure { true } else { false @@ -1040,14 +1023,14 @@ impl TextDrawer{ } else { false } - }, + } _ => false, }; - let top_con = { + let top_con = { if ind >= 1 { if top_cases.contains(&vis_layer.0[ind - 1]) { - if is_measure{ + if is_measure { C_WIRE_CON_TOP } else { TOP_CON @@ -1074,42 +1057,58 @@ impl TextDrawer{ } else { Q_WIRE } - }; + }; match sub_type { Boxed::Single(inst) => { let label = Self::get_label(inst).unwrap_or(" ".to_string()); let left_len = (label.len() - 1) / 2; let right_len = label.len() - left_len - 1; - ElementWire{ - top: format!("{}{}{}{}{}", - TOP_LEFT_BOX, + ElementWire { + top: format!( + "{}{}{}{}{}", + TOP_LEFT_BOX, Q_WIRE.to_string().repeat(left_len), top_con, - Q_WIRE.to_string().repeat(right_len), - TOP_RIGHT_BOX), + Q_WIRE.to_string().repeat(right_len), + TOP_RIGHT_BOX + ), mid: format!("{}{}{}", Q_LEFT_CON, label, Q_RIGHT_CON), - bot: format!("{}{}{}{}{}", + bot: format!( + "{}{}{}{}{}", BOT_LEFT_BOX, Q_WIRE.to_string().repeat(left_len), bot_con, Q_WIRE.to_string().repeat(right_len), - BOT_RIGHT_BOX), + BOT_RIGHT_BOX + ), } - }, + } Boxed::Multi(inst) => { let label = Self::get_label(inst).unwrap_or(" ".to_string()); // get all the indices affected by this multi-box - let qargs = circuit.qargs_interner().get(inst.qubits).into_iter().map(|q| q.index()).collect_vec(); - let cargs = circuit.cargs_interner().get(inst.clbits).into_iter().map(|c| c.index() + circuit.num_qubits()).collect_vec(); + let qargs = circuit + .qargs_interner() + .get(inst.qubits) + .into_iter() + .map(|q| q.index()) + .collect_vec(); + let cargs = circuit + .cargs_interner() + .get(inst.clbits) + .into_iter() + .map(|c| c.index() + circuit.num_qubits()) + .collect_vec(); let minmax = qargs.iter().chain(cargs.iter()).minmax(); let range = match minmax { MinMaxResult::MinMax(min, max) => (*min, *max), MinMaxResult::OneElement(idx) => (*idx, *idx), - MinMaxResult::NoElements => panic!("Encountered an multi-qubit without qubits and clbits") + MinMaxResult::NoElements => { + panic!("Encountered an multi-qubit without qubits and clbits") + } }; let mid = (range.0 + range.1) / 2; - + let num_affected = { if qargs.contains(&ind) || cargs.contains(&ind) { // Once the packed instruction can handle custom gates, we need to first check the number of controls @@ -1121,60 +1120,94 @@ impl TextDrawer{ if qargs.contains(&ind) { let idx = qargs.iter().position(|&x| x == ind).unwrap(); format!("{:^width$}", idx, width = qargs.len()) - } - else { + } else { " ".to_string() } }; temp } else { " ".to_string() - } + } }; let mid_section = if ind == mid { - format!("{:^total_q$} {:^label_len$}", num_affected, label, total_q = qargs.len(), label_len=label.len()) + format!( + "{:^total_q$} {:^label_len$}", + num_affected, + label, + total_q = qargs.len(), + label_len = label.len() + ) } else { - format!("{:^total_q$} {:^label_len$}", num_affected, " ", total_q = qargs.len(), label_len=label.len()) + format!( + "{:^total_q$} {:^label_len$}", + num_affected, + " ", + total_q = qargs.len(), + label_len = label.len() + ) }; let left_len = (mid_section.len() - 1) / 2; let right_len = mid_section.len() - left_len - 1; if ind == range.0 { - ElementWire{ - top: format!("{}{}{}{}{}", - TOP_LEFT_BOX, + ElementWire { + top: format!( + "{}{}{}{}{}", + TOP_LEFT_BOX, Q_WIRE.to_string().repeat(left_len), top_con, - Q_WIRE.to_string().repeat(right_len), - TOP_RIGHT_BOX), + Q_WIRE.to_string().repeat(right_len), + TOP_RIGHT_BOX + ), mid: format!("{}{}{}", Q_LEFT_CON, mid_section, Q_RIGHT_CON), - bot: format!("{}{}{}", CONNECTING_WIRE, " ".repeat(mid_section.len()), CONNECTING_WIRE), + bot: format!( + "{}{}{}", + CONNECTING_WIRE, + " ".repeat(mid_section.len()), + CONNECTING_WIRE + ), } } else if ind == range.1 { - ElementWire{ - top: format!("{}{}{}", CONNECTING_WIRE," ".to_string().repeat(mid_section.len()), CONNECTING_WIRE), + ElementWire { + top: format!( + "{}{}{}", + CONNECTING_WIRE, + " ".to_string().repeat(mid_section.len()), + CONNECTING_WIRE + ), mid: format!("{}{}{}", Q_LEFT_CON, mid_section, Q_RIGHT_CON), - bot: format!("{}{}{}{}{}", + bot: format!( + "{}{}{}{}{}", BOT_LEFT_BOX, Q_WIRE.to_string().repeat(left_len), bot_con, Q_WIRE.to_string().repeat(right_len), - BOT_RIGHT_BOX), + BOT_RIGHT_BOX + ), } } else { - ElementWire{ - top: format!("{}{}{}", CONNECTING_WIRE, " ".repeat(mid_section.len()), CONNECTING_WIRE), + ElementWire { + top: format!( + "{}{}{}", + CONNECTING_WIRE, + " ".repeat(mid_section.len()), + CONNECTING_WIRE + ), mid: format!("{}{}{}", Q_LEFT_CON, mid_section, Q_RIGHT_CON), - bot: format!("{}{}{}", CONNECTING_WIRE, " ".repeat(mid_section.len()), CONNECTING_WIRE), + bot: format!( + "{}{}{}", + CONNECTING_WIRE, + " ".repeat(mid_section.len()), + CONNECTING_WIRE + ), } } } } - }, + } VisualizationElement::DirectOnWire(on_wire) => { - let connecting_wire = if ind < circuit.num_qubits() { CONNECTING_WIRE } else { @@ -1188,58 +1221,44 @@ impl TextDrawer{ } else { C_WIRE_CON_TOP.to_string() } - }, - OnWire::Swap(position) => { - "X".to_string() - }, + } + OnWire::Swap(position) => "X".to_string(), OnWire::Barrier => BARRIER.to_string(), OnWire::Reset => "|0>".to_string(), }; - let top:String = match on_wire{ - OnWire::Control(position) => { - match position { - ElementOnWire::Top => " ".to_string(), - ElementOnWire::Mid => format!("{}", connecting_wire), - ElementOnWire::Bot => format!("{}", connecting_wire), - } + let top: String = match on_wire { + OnWire::Control(position) => match position { + ElementOnWire::Top => " ".to_string(), + ElementOnWire::Mid => format!("{}", connecting_wire), + ElementOnWire::Bot => format!("{}", connecting_wire), }, - OnWire::Swap(position) => { - match position { - ElementOnWire::Top => " ".to_string(), - ElementOnWire::Mid => format!("{}", connecting_wire), - ElementOnWire::Bot => format!("{}", connecting_wire), - } + OnWire::Swap(position) => match position { + ElementOnWire::Top => " ".to_string(), + ElementOnWire::Mid => format!("{}", connecting_wire), + ElementOnWire::Bot => format!("{}", connecting_wire), }, OnWire::Barrier => { format!("{}", BARRIER) - }, - OnWire::Reset => { - " ".to_string() - }, + } + OnWire::Reset => " ".to_string(), }; - let bot: String = match on_wire{ - OnWire::Control(position) => { - match position { - ElementOnWire::Top => format!("{}", connecting_wire), - ElementOnWire::Mid => format!("{}", connecting_wire), - ElementOnWire::Bot => " ".to_string(), - } + let bot: String = match on_wire { + OnWire::Control(position) => match position { + ElementOnWire::Top => format!("{}", connecting_wire), + ElementOnWire::Mid => format!("{}", connecting_wire), + ElementOnWire::Bot => " ".to_string(), }, - OnWire::Swap(position) => { - match position { - ElementOnWire::Top => format!("{}", connecting_wire), - ElementOnWire::Mid => format!("{}", connecting_wire), - ElementOnWire::Bot => " ".to_string(), - } + OnWire::Swap(position) => match position { + ElementOnWire::Top => format!("{}", connecting_wire), + ElementOnWire::Mid => format!("{}", connecting_wire), + ElementOnWire::Bot => " ".to_string(), }, OnWire::Barrier => { format!("{}", BARRIER) - }, - OnWire::Reset => { - " ".to_string() - }, + } + OnWire::Reset => " ".to_string(), }; let wire = if ind < circuit.num_qubits() { @@ -1248,47 +1267,44 @@ impl TextDrawer{ C_WIRE }; - ElementWire{ - top: format!("{}{}{}"," ",top," "), + ElementWire { + top: format!("{}{}{}", " ", top, " "), mid: format!("{}{}{}", wire, wire_char, wire), - bot: format!("{}{}{}"," ",bot," "), + bot: format!("{}{}{}", " ", bot, " "), } - - }, - VisualizationElement::Input(wire_input) => { - match wire_input { - ElementWireInput::Qubit(qubit) => { - let qubit_name = if let Some(bit_info) = circuit.qubit_indices().get(qubit) { - if let Some((register, index)) = bit_info.registers().first() { - format!("{}_{}:", register.name(), index) - } else { - format!("q_{}:", ind) - } + } + VisualizationElement::Input(wire_input) => match wire_input { + ElementWireInput::Qubit(qubit) => { + let qubit_name = if let Some(bit_info) = circuit.qubit_indices().get(qubit) { + if let Some((register, index)) = bit_info.registers().first() { + format!("{}_{}:", register.name(), index) } else { format!("q_{}:", ind) - }; - ElementWire{ - top: format!("{}", " ".repeat(qubit_name.len())), - mid: format!("{}", qubit_name), - bot: format!("{}", " ".repeat(qubit_name.len())), } + } else { + format!("q_{}:", ind) + }; + ElementWire { + top: format!("{}", " ".repeat(qubit_name.len())), + mid: format!("{}", qubit_name), + bot: format!("{}", " ".repeat(qubit_name.len())), } - ElementWireInput::Clbit(clbit) => { - let clbit_name = if let Some(bit_info) = circuit.clbit_indices().get(clbit) { - if let Some((register, index)) = bit_info.registers().first() { - format!("{}_{}:", register.name(), index) - } else { - format!("c_{}:", ind) - } + } + ElementWireInput::Clbit(clbit) => { + let clbit_name = if let Some(bit_info) = circuit.clbit_indices().get(clbit) { + if let Some((register, index)) = bit_info.registers().first() { + format!("{}_{}:", register.name(), index) } else { format!("c_{}:", ind) - }; - ElementWire{ - top: format!("{}", " ".repeat(clbit_name.len())), - mid: format!("{}", clbit_name), - bot: format!("{}", " ".repeat(clbit_name.len())), } - }, + } else { + format!("c_{}:", ind) + }; + ElementWire { + top: format!("{}", " ".repeat(clbit_name.len())), + mid: format!("{}", clbit_name), + bot: format!("{}", " ".repeat(clbit_name.len())), + } } }, VisualizationElement::VerticalLine(input_type) => { @@ -1300,26 +1316,26 @@ impl TextDrawer{ } else { Q_CL_CROSSED_WIRE } - }, + } InputType::Clbit(label) => { if ind < circuit.num_qubits() { CL_Q_CROSSED_WIRE } else { CL_CL_CROSSED_WIRE } - }, + } } }; let connector = match &input_type { InputType::Qubit(_) => CONNECTING_WIRE, InputType::Clbit(_) => CL_CONNECTING_WIRE, }; - ElementWire{ + ElementWire { top: format!("{}", connector), mid: format!("{}", crossed), bot: format!("{}", connector), } - }, + } VisualizationElement::Empty => { let wire = { if ind < circuit.num_qubits() { @@ -1328,16 +1344,16 @@ impl TextDrawer{ C_WIRE } }; - ElementWire{ + ElementWire { top: format!(" "), mid: format!("{}", wire), bot: format!(" "), } - }, + } } } - fn print(&self){ + fn print(&self) { // let mut output = String::new(); // for i in self.wires.iter(){ // let top_line: String = i.iter().map(|wire| wire.top.clone()).collect::>().join(""); @@ -1347,26 +1363,49 @@ impl TextDrawer{ // } // println!("{}", output); - // print using merge lines + // print using merge lines let num_wires = self.wires.len(); for i in 0..num_wires - 1 { if i == 0 { - let top_line = self.wires[i].iter().map(|wire| wire.top.clone()).collect::>().join(""); - let mid_line = self.wires[i].iter().map(|wire| wire.mid.clone()).collect::>().join(""); + let top_line = self.wires[i] + .iter() + .map(|wire| wire.top.clone()) + .collect::>() + .join(""); + let mid_line = self.wires[i] + .iter() + .map(|wire| wire.mid.clone()) + .collect::>() + .join(""); println!("{}", top_line); println!("{}", mid_line); } - let bot_line = self.wires[i].iter().map(|wire| wire.bot.clone()).collect::>().join(""); - let top_line_next = self.wires[i + 1].iter().map(|wire| wire.top.clone()).collect::>().join(""); + let bot_line = self.wires[i] + .iter() + .map(|wire| wire.bot.clone()) + .collect::>() + .join(""); + let top_line_next = self.wires[i + 1] + .iter() + .map(|wire| wire.top.clone()) + .collect::>() + .join(""); let merged_line = Self::merge_lines(&bot_line, &top_line_next, "top"); println!("{}", merged_line); - let mid_line_next = self.wires[i + 1].iter().map(|wire| wire.mid.clone()).collect::>().join(""); + let mid_line_next = self.wires[i + 1] + .iter() + .map(|wire| wire.mid.clone()) + .collect::>() + .join(""); println!("{}", mid_line_next); } let last_index = num_wires - 1; - let bot_line = self.wires[last_index].iter().map(|wire| wire.bot.clone()).collect::>().join(""); + let bot_line = self.wires[last_index] + .iter() + .map(|wire| wire.bot.clone()) + .collect::>() + .join(""); println!("{}", bot_line); - } pub fn merge_lines(top: &str, bot: &str, icod: &str) -> String { @@ -1421,34 +1460,3 @@ impl TextDrawer{ ret } } - - - // fn add_wire(&mut self, wire: &ElementWire, ind: usize){ - // self.wires[ind].top.push_str(&wire.top); - // self.wires[ind].mid.push_str(&wire.mid); - // self.wires[ind].bot.push_str(&wire.bot); - // // self.wires[ind].top.push_str(&format!("{}{}",&wire.top,"$")); - // // self.wires[ind].mid.push_str(&format!("{}{}",&wire.mid,"$")); - // // self.wires[ind].bot.push_str(&format!("{}{}",&wire.bot,"$")); - // } - -pub fn draw_circuit(circuit: &CircuitData) -> PyResult<()> { - let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; - - let vis_mat2 = VisualizationMatrix::from_circuit(&dag,circuit)?; - - println!("======================"); - - println!("num wires {}, num layers {}", vis_mat2.num_wires(), vis_mat2.num_layers()); - - for i in 0..vis_mat2.num_wires() { - for j in 0..vis_mat2.num_layers() { - print!("{:^30}", format!("{:?}", vis_mat2[j][i])); - } - println!(""); - } - - let circuit_rep = TextDrawer::from_visualization_matrix(&vis_mat2); - circuit_rep.print(); - Ok(()) -} diff --git a/crates/circuit/src/lib.rs b/crates/circuit/src/lib.rs index 9f11c1bb9c94..e6a8cbcd62eb 100644 --- a/crates/circuit/src/lib.rs +++ b/crates/circuit/src/lib.rs @@ -16,6 +16,7 @@ pub mod annotation; pub mod bit; pub mod bit_locator; pub mod circuit_data; +pub mod circuit_drawer; pub mod circuit_instruction; pub mod classical; pub mod converters; @@ -37,7 +38,6 @@ pub mod register_data; pub mod slice; pub mod util; pub mod vf2; -pub mod circuit_drawer; mod variable_mapper; From 6f92f1556c32754271163f013f60f63574d96172 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Thu, 30 Oct 2025 00:52:20 +0530 Subject: [PATCH 33/70] added draw capabilities --- crates/circuit/src/circuit_drawer.rs | 13 +++++-------- crates/circuit/src/lib.rs | 1 + .../visualization/circuit/circuit_visualization.py | 3 ++- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 2e53f2af5a87..25eb910c84cf 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -154,8 +154,8 @@ impl Ord for ElementWireInput<'_> { impl<'a> Debug for ElementWireInput<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let name = match self { - ElementWireInput::Qubit(&ref qubit) => "Qubit", - ElementWireInput::Clbit(&ref clbit) => "Clbit", + ElementWireInput::Qubit(qubit) => "Qubit", + ElementWireInput::Clbit(clbit) => "Clbit", }; write!(f, "{}", name) @@ -238,16 +238,13 @@ impl<'a> Debug for Boxed<'a> { } } -/// Enum for representing the elements stored in a visualization matrix. The elements + +/// Enum for representing the elements stored in a visualization matrix. The elements /// do not directly implement visualization capabilities, but rather carry enough information /// to enable visualization later on by the actual drawer. -struct Op<'a> { - instruction: &'a PackedInstruction, -} - #[derive(Default, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -enum VisualizationElement<'a> { +enum VisualizationElement<'a>{ #[default] Empty, // Marker for no element VerticalLine(InputType), diff --git a/crates/circuit/src/lib.rs b/crates/circuit/src/lib.rs index e6a8cbcd62eb..a1e3c6e96e34 100644 --- a/crates/circuit/src/lib.rs +++ b/crates/circuit/src/lib.rs @@ -38,6 +38,7 @@ pub mod register_data; pub mod slice; pub mod util; pub mod vf2; +pub mod circuit_drawer; mod variable_mapper; diff --git a/qiskit/visualization/circuit/circuit_visualization.py b/qiskit/visualization/circuit/circuit_visualization.py index 0ebde23ba25e..9406c2c7c878 100644 --- a/qiskit/visualization/circuit/circuit_visualization.py +++ b/qiskit/visualization/circuit/circuit_visualization.py @@ -299,7 +299,7 @@ def check_clbit_in_inst(circuit, cregbundle): return True cregbundle = check_clbit_in_inst(circuit, cregbundle) - + if output == "text": return _text_circuit_drawer( circuit, @@ -317,6 +317,7 @@ def check_clbit_in_inst(circuit, cregbundle): expr_len=expr_len, measure_arrows=measure_arrows, ) + elif output == "latex": image = _latex_circuit_drawer( circuit, From f8c409d1bdf27d26cfd1eff7ff8e18548ca066e1 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Thu, 30 Oct 2025 14:44:35 +0530 Subject: [PATCH 34/70] linting for lib.rs --- crates/circuit/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/circuit/src/lib.rs b/crates/circuit/src/lib.rs index a1e3c6e96e34..abcdc71258b8 100644 --- a/crates/circuit/src/lib.rs +++ b/crates/circuit/src/lib.rs @@ -38,8 +38,6 @@ pub mod register_data; pub mod slice; pub mod util; pub mod vf2; -pub mod circuit_drawer; - mod variable_mapper; use pyo3::PyTypeInfo; From 234bd02d1e341a4d0ad2b47c88aeb23bf75675b0 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Sun, 2 Nov 2025 22:26:50 +0530 Subject: [PATCH 35/70] added cregbundle flag --- crates/circuit/src/circuit_drawer.rs | 71 ++++++++++++++-------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 25eb910c84cf..adeaeedf7881 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -35,39 +35,9 @@ import_exception!(qiskit.circuit.exceptions, CircuitError); // TODO: remove when dev is done, since this is only for manual testing #[pyfunction(name = "draw")] -pub fn py_drawer(circuit: QuantumCircuitData) -> PyResult<()> { - draw_circuit(&circuit.data)?; - Ok(()) -} - -pub fn draw_circuit(circuit: &CircuitData) -> PyResult<()> { - let vis_mat2 = VisualizationMatrix::from_circuit(circuit)?; - - for inst in circuit.data() { - println!( - "INST {:?} QARGS: {:?}", - inst, - circuit.get_qargs(inst.qubits) - ); - } - - println!("======================"); - - println!( - "num wires {}, num layers {}", - vis_mat2.num_wires(), - vis_mat2.num_layers() - ); - - for i in 0..vis_mat2.num_wires() { - for j in 0..vis_mat2.num_layers() { - print!("{:^30}", format!("{:?}", vis_mat2[j][i])); - } - println!(""); - } - - let circuit_rep = TextDrawer::from_visualization_matrix(&vis_mat2); - circuit_rep.print(); +#[pyo3(signature = (circuit, cregbundle=true))] +pub fn py_drawer(circuit: QuantumCircuitData, cregbundle: bool) -> PyResult<()> { + draw_circuit(&circuit.data, &cregbundle)?; Ok(()) } @@ -895,7 +865,7 @@ impl TextDrawer { instruction_label } - fn from_visualization_matrix<'a>(vis_mat: &'a VisualizationMatrix) -> Self { + fn from_visualization_matrix<'a>(vis_mat: &'a VisualizationMatrix, cregbundle: &bool) -> Self{ let mut wires: Vec> = vec![]; for _ in 0..vis_mat.num_wires() { wires.push(vec![]); @@ -903,8 +873,6 @@ impl TextDrawer { let mut text_drawer = TextDrawer { wires }; - let cregbundle = true; - let post_processed_vis_mat = { if !cregbundle { None @@ -1457,3 +1425,34 @@ impl TextDrawer { ret } } + + + // fn add_wire(&mut self, wire: &ElementWire, ind: usize){ + // self.wires[ind].top.push_str(&wire.top); + // self.wires[ind].mid.push_str(&wire.mid); + // self.wires[ind].bot.push_str(&wire.bot); + // // self.wires[ind].top.push_str(&format!("{}{}",&wire.top,"$")); + // // self.wires[ind].mid.push_str(&format!("{}{}",&wire.mid,"$")); + // // self.wires[ind].bot.push_str(&format!("{}{}",&wire.bot,"$")); + // } + +pub fn draw_circuit(circuit: &CircuitData, cregbundle: &bool) -> PyResult<()> { + let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; + + let vis_mat2 = VisualizationMatrix::from_circuit(circuit)?; + + println!("======================"); + + println!("num wires {}, num layers {}", vis_mat2.num_wires(), vis_mat2.num_layers()); + + for i in 0..vis_mat2.num_wires() { + for j in 0..vis_mat2.num_layers() { + print!("{:^30}", format!("{:?}", vis_mat2[j][i])); + } + println!(""); + } + + let circuit_rep = TextDrawer::from_visualization_matrix(&vis_mat2, cregbundle); + circuit_rep.print(); + Ok(()) +} From f9b82f8936cc492ea4d63473812f5cd9fd2c97cd Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Sun, 2 Nov 2025 22:27:38 +0530 Subject: [PATCH 36/70] removed visualization matrix printing --- crates/circuit/src/circuit_drawer.rs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index adeaeedf7881..73f4a02b4e81 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -1426,32 +1426,11 @@ impl TextDrawer { } } - - // fn add_wire(&mut self, wire: &ElementWire, ind: usize){ - // self.wires[ind].top.push_str(&wire.top); - // self.wires[ind].mid.push_str(&wire.mid); - // self.wires[ind].bot.push_str(&wire.bot); - // // self.wires[ind].top.push_str(&format!("{}{}",&wire.top,"$")); - // // self.wires[ind].mid.push_str(&format!("{}{}",&wire.mid,"$")); - // // self.wires[ind].bot.push_str(&format!("{}{}",&wire.bot,"$")); - // } - pub fn draw_circuit(circuit: &CircuitData, cregbundle: &bool) -> PyResult<()> { let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; let vis_mat2 = VisualizationMatrix::from_circuit(circuit)?; - println!("======================"); - - println!("num wires {}, num layers {}", vis_mat2.num_wires(), vis_mat2.num_layers()); - - for i in 0..vis_mat2.num_wires() { - for j in 0..vis_mat2.num_layers() { - print!("{:^30}", format!("{:?}", vis_mat2[j][i])); - } - println!(""); - } - let circuit_rep = TextDrawer::from_visualization_matrix(&vis_mat2, cregbundle); circuit_rep.print(); Ok(()) From 18311603ce2905b2466a0bf00b0e0edffa2e7e10 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Sun, 2 Nov 2025 22:34:32 +0530 Subject: [PATCH 37/70] mergewires flag added --- crates/circuit/src/circuit_drawer.rs | 90 +++++++++++----------------- 1 file changed, 34 insertions(+), 56 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 73f4a02b4e81..c7757efd84e1 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -35,9 +35,9 @@ import_exception!(qiskit.circuit.exceptions, CircuitError); // TODO: remove when dev is done, since this is only for manual testing #[pyfunction(name = "draw")] -#[pyo3(signature = (circuit, cregbundle=true))] -pub fn py_drawer(circuit: QuantumCircuitData, cregbundle: bool) -> PyResult<()> { - draw_circuit(&circuit.data, &cregbundle)?; +#[pyo3(signature = (circuit, cregbundle=true, mergewires=true))] +pub fn py_drawer(circuit: QuantumCircuitData, cregbundle: bool, mergewires: bool) -> PyResult<()> { + draw_circuit(&circuit.data, &cregbundle, &mergewires)?; Ok(()) } @@ -1318,59 +1318,37 @@ impl TextDrawer { } } - fn print(&self) { - // let mut output = String::new(); - // for i in self.wires.iter(){ - // let top_line: String = i.iter().map(|wire| wire.top.clone()).collect::>().join(""); - // let mid_line: String = i.iter().map(|wire| wire.mid.clone()).collect::>().join(""); - // let bot_line: String = i.iter().map(|wire| wire.bot.clone()).collect::>().join(""); - // output.push_str(&format!("{}\n{}\n{}\n", top_line, mid_line, bot_line)); - // } - // println!("{}", output); - - // print using merge lines - let num_wires = self.wires.len(); - for i in 0..num_wires - 1 { - if i == 0 { - let top_line = self.wires[i] - .iter() - .map(|wire| wire.top.clone()) - .collect::>() - .join(""); - let mid_line = self.wires[i] - .iter() - .map(|wire| wire.mid.clone()) - .collect::>() - .join(""); - println!("{}", top_line); - println!("{}", mid_line); + fn print(&self, mergewires: &bool){ + + if !*mergewires { + let mut output = String::new(); + for i in self.wires.iter(){ + let top_line: String = i.iter().map(|wire| wire.top.clone()).collect::>().join(""); + let mid_line: String = i.iter().map(|wire| wire.mid.clone()).collect::>().join(""); + let bot_line: String = i.iter().map(|wire| wire.bot.clone()).collect::>().join(""); + output.push_str(&format!("{}\n{}\n{}\n", top_line, mid_line, bot_line)); + } + println!("{}", output); + } else { + let num_wires = self.wires.len(); + for i in 0..num_wires - 1 { + if i == 0 { + let top_line = self.wires[i].iter().map(|wire| wire.top.clone()).collect::>().join(""); + let mid_line = self.wires[i].iter().map(|wire| wire.mid.clone()).collect::>().join(""); + println!("{}", top_line); + println!("{}", mid_line); + } + let bot_line = self.wires[i].iter().map(|wire| wire.bot.clone()).collect::>().join(""); + let top_line_next = self.wires[i + 1].iter().map(|wire| wire.top.clone()).collect::>().join(""); + let merged_line = Self::merge_lines(&bot_line, &top_line_next, "top"); + println!("{}", merged_line); + let mid_line_next = self.wires[i + 1].iter().map(|wire| wire.mid.clone()).collect::>().join(""); + println!("{}", mid_line_next); } - let bot_line = self.wires[i] - .iter() - .map(|wire| wire.bot.clone()) - .collect::>() - .join(""); - let top_line_next = self.wires[i + 1] - .iter() - .map(|wire| wire.top.clone()) - .collect::>() - .join(""); - let merged_line = Self::merge_lines(&bot_line, &top_line_next, "top"); - println!("{}", merged_line); - let mid_line_next = self.wires[i + 1] - .iter() - .map(|wire| wire.mid.clone()) - .collect::>() - .join(""); - println!("{}", mid_line_next); + let last_index = num_wires - 1; + let bot_line = self.wires[last_index].iter().map(|wire| wire.bot.clone()).collect::>().join(""); + println!("{}", bot_line); } - let last_index = num_wires - 1; - let bot_line = self.wires[last_index] - .iter() - .map(|wire| wire.bot.clone()) - .collect::>() - .join(""); - println!("{}", bot_line); } pub fn merge_lines(top: &str, bot: &str, icod: &str) -> String { @@ -1426,12 +1404,12 @@ impl TextDrawer { } } -pub fn draw_circuit(circuit: &CircuitData, cregbundle: &bool) -> PyResult<()> { +pub fn draw_circuit(circuit: &CircuitData, cregbundle: &bool, mergewires: &bool) -> PyResult<()> { let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; let vis_mat2 = VisualizationMatrix::from_circuit(circuit)?; let circuit_rep = TextDrawer::from_visualization_matrix(&vis_mat2, cregbundle); - circuit_rep.print(); + circuit_rep.print(mergewires); Ok(()) } From 031b254afca4a63a764537f6bd9dc6d8a036a184 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Sun, 2 Nov 2025 23:58:46 +0200 Subject: [PATCH 38/70] Run cargo fmt --- crates/circuit/src/circuit_drawer.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index c7757efd84e1..ec4378c2a644 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -256,17 +256,19 @@ impl<'a> VisualizationLayer<'a> { } /// Adds the required visualization elements to represent the given instruction - fn add_instruction(&mut self, packed_inst: &'a PackedInstruction, circuit: &CircuitData) { - match packed_inst.op.view() { - OperationRef::StandardGate(gate) => self.add_standard_gate(gate, packed_inst, circuit), + fn add_instruction(&mut self, inst: &'a PackedInstruction, circuit: &CircuitData) { + match inst.op.view() { + OperationRef::StandardGate(gate) => { + self.add_standard_gate(gate, inst, circuit); + } OperationRef::StandardInstruction(std_inst) => { - self.add_standard_instruction(std_inst, packed_inst, circuit) + self.add_standard_instruction(std_inst, inst, circuit); } _ => unimplemented!( "{}", format!( "Visualization is not implemented for instruction of type {:?}", - packed_inst.op + inst.op ) ), } @@ -360,8 +362,14 @@ impl<'a> VisualizationLayer<'a> { self.0[qargs.last().unwrap().index()] = VisualizationElement::Boxed(Boxed::Single(inst)); let mut control_indices: HashSet = HashSet::new(); if gate.num_ctrl_qubits() > 0 { - control_indices.extend(qargs.iter().take(qargs.len() - 1).map(|q| q.index())); - self.add_controls(&control_indices, (minima, maxima)); + self.add_controls( + &qargs + .iter() + .take(qargs.len() - 1) + .map(|q| q.index()) + .collect(), + (minima, maxima), + ); } } From 8831e3445f11fe75737a5c63c6f82888cc0f7c22 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Tue, 4 Nov 2025 17:21:17 +0530 Subject: [PATCH 39/70] fixed a few labels --- crates/circuit/src/circuit_drawer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index ec4378c2a644..847ff21ed670 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -828,7 +828,7 @@ impl TextDrawer { } StandardGate::RCCX => { // Relative-phase Toffoli gate - Some(format!(" RX({}) ", instruction_param)) + Some(format!(" RCCX({}) ", instruction_param)) } StandardGate::C3X => { // 3-controlled X gate (4-qubit controlled X) @@ -840,7 +840,7 @@ impl TextDrawer { } StandardGate::RC3X => { // Relative-phase 3-controlled X gate - Some(format!(" RX({}) ", instruction_param)) + Some(format!(" RC3X({}) ", instruction_param)) } } } else if let Some(std_instruction) = instruction.op.try_standard_instruction() { From 74ddaa7e0da4dc01d62e4fef2468cd420d947c78 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Wed, 5 Nov 2025 10:38:34 +0530 Subject: [PATCH 40/70] added vertical lines support for controlled gates --- crates/circuit/src/circuit_drawer.rs | 38 ++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 847ff21ed670..ac5aef9b3923 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -355,12 +355,29 @@ impl<'a> VisualizationLayer<'a> { // // )); // // } // }, - StandardGate::H | StandardGate::RX | StandardGate::RZ => { + StandardGate::H + | StandardGate::RX + | StandardGate::RZ + | StandardGate::RY + | StandardGate::X + | StandardGate::SX + | StandardGate::Z + | StandardGate::Y + | StandardGate::S + | StandardGate::T => { self.0[qargs[0].index()] = VisualizationElement::Boxed(Boxed::Single(inst)); - }, - StandardGate::CX | StandardGate::CCX => { - self.0[qargs.last().unwrap().index()] = VisualizationElement::Boxed(Boxed::Single(inst)); - let mut control_indices: HashSet = HashSet::new(); + } + StandardGate::CX + | StandardGate::CCX + | StandardGate::CSdg + | StandardGate::CCZ + | StandardGate::CU + | StandardGate::CU1 + | StandardGate::CU3 + | StandardGate::CY + | StandardGate::CZ => { + self.0[qargs.last().unwrap().index()] = + VisualizationElement::Boxed(Boxed::Single(inst)); if gate.num_ctrl_qubits() > 0 { self.add_controls( &qargs @@ -372,13 +389,12 @@ impl<'a> VisualizationLayer<'a> { ); } + let vert_lines = (minima..=maxima) + .filter(|idx| !(qargs.iter().map(|q| q.0 as usize)).contains(idx)); + self.add_vertical_lines(inst, vert_lines); } - _ => unimplemented!("{}", format!("{:?} is not supported yet", gate)) + _ => unimplemented!("") } - - // let vert_lines = (minima..=maxima) - // .filter(|idx| !control_indices.contains(idx) && !box_indices.contains(idx)); - // self.add_vertical_lines(inst, vert_lines); } fn add_standard_instruction( @@ -850,6 +866,8 @@ impl TextDrawer { Some("|0>".to_string()) } else if let StandardInstruction::Barrier(_) = std_instruction { Some("░".to_string()) + } else if let StandardInstruction::Delay(_) = std_instruction { + Some(format!("Delay({})", instruction_param)) } else { // Fallback for non-standard instructions Some(format!( From 7a95e5fd6325aedaadad5eb59154fd9cb555edd6 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Wed, 5 Nov 2025 11:55:34 +0530 Subject: [PATCH 41/70] added standard gate visualisation support barring special cases --- crates/circuit/src/circuit_drawer.rs | 80 +++++++++++++++++++++------- 1 file changed, 62 insertions(+), 18 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index ac5aef9b3923..c52f61c9be12 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -326,6 +326,7 @@ impl<'a> VisualizationLayer<'a> { StandardGate::ISwap | StandardGate::DCX | StandardGate::ECR + | StandardGate::ISwap | StandardGate::RXX | StandardGate::RYY | StandardGate::RZZ @@ -356,26 +357,46 @@ impl<'a> VisualizationLayer<'a> { // // } // }, StandardGate::H - | StandardGate::RX - | StandardGate::RZ + | StandardGate::I + | StandardGate::X + | StandardGate::Y + | StandardGate::Z + | StandardGate::Phase + | StandardGate::R + | StandardGate::RX | StandardGate::RY - | StandardGate::X + | StandardGate::RZ + | StandardGate::S + | StandardGate::Sdg | StandardGate::SX - | StandardGate::Z - | StandardGate::Y - | StandardGate::S - | StandardGate::T => { + | StandardGate::SXdg + | StandardGate::T + | StandardGate::Tdg + | StandardGate::U + | StandardGate::U1 + | StandardGate::U2 + | StandardGate::U3 => { self.0[qargs[0].index()] = VisualizationElement::Boxed(Boxed::Single(inst)); } - StandardGate::CX - | StandardGate::CCX - | StandardGate::CSdg - | StandardGate::CCZ - | StandardGate::CU - | StandardGate::CU1 - | StandardGate::CU3 + StandardGate::CH + | StandardGate::CX | StandardGate::CY - | StandardGate::CZ => { + | StandardGate::CZ + | StandardGate::CRX + | StandardGate::CRY + | StandardGate::CRZ + | StandardGate::CS + | StandardGate::CSdg + | StandardGate::CSX + | StandardGate::CU + | StandardGate::CU1 + | StandardGate::CU3 + | StandardGate::CCX + | StandardGate::CCZ + | StandardGate::C3X + | StandardGate::C3SX + | StandardGate::CPhase + => { self.0[qargs.last().unwrap().index()] = VisualizationElement::Boxed(Boxed::Single(inst)); if gate.num_ctrl_qubits() > 0 { @@ -393,7 +414,30 @@ impl<'a> VisualizationLayer<'a> { .filter(|idx| !(qargs.iter().map(|q| q.0 as usize)).contains(idx)); self.add_vertical_lines(inst, vert_lines); } - _ => unimplemented!("") + StandardGate::GlobalPhase => { + + } + StandardGate::Swap + | StandardGate::CSwap => { + // taking the last 2 elements of qargs + let swap_qubits = qargs.iter().map(|q| q.0 as usize).rev().take(2); + for qubit in swap_qubits { + if qubit == minima { + self.0[qubit] = + VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Top)); + } else if qubit == maxima { + self.0[qubit] = + VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Bot)); + } else { + self.0[qubit] = + VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Mid)); + } + } + + let vert_lines = (minima..=maxima) + .filter(|idx| !(qargs.iter().map(|q| q.0 as usize)).contains(idx)); + self.add_vertical_lines(inst, vert_lines); + } } } @@ -1123,7 +1167,7 @@ impl TextDrawer { let mid_section = if ind == mid { format!( - "{:^total_q$} {:^label_len$}", + "{:^total_q$}{:^label_len$}", num_affected, label, total_q = qargs.len(), @@ -1131,7 +1175,7 @@ impl TextDrawer { ) } else { format!( - "{:^total_q$} {:^label_len$}", + "{:^total_q$}{:^label_len$}", num_affected, " ", total_q = qargs.len(), From eca1ad83d409084f6ac5230d478675c98e9a19f0 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Wed, 5 Nov 2025 14:30:38 +0530 Subject: [PATCH 42/70] fixed classical wire connection for measurement --- crates/circuit/src/circuit_drawer.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index c52f61c9be12..781145d550a1 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -463,8 +463,10 @@ impl<'a> VisualizationLayer<'a> { } } StandardInstruction::Measure => { - self.0[qargs.last().unwrap().index()] = VisualizationElement::Boxed(Boxed::Single(inst)); - self.add_vertical_lines(inst, minima + 1..=maxima); + self.0[qargs.last().unwrap().index()] = + VisualizationElement::Boxed(Boxed::Single(inst)); + self.add_vertical_lines(inst, minima + 1..maxima); + self.0[maxima] = VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Bot)) } StandardInstruction::Delay(_) => { for q in qargs { From 37acecea83bb515089aff60d25d16ed0e28430a4 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Thu, 6 Nov 2025 12:58:46 +0530 Subject: [PATCH 43/70] added fold functionality --- crates/circuit/src/circuit_drawer.rs | 210 +++++++++++++++++++++------ 1 file changed, 163 insertions(+), 47 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 781145d550a1..893191991711 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -15,7 +15,7 @@ use crate::dag_circuit::DAGCircuit; use crate::operations::{Operation, OperationRef, Param, StandardGate, StandardInstruction}; use crate::packed_instruction::PackedInstruction; use crate::{Clbit, Qubit}; -use core::panic; +use core::{num, panic}; use hashbrown::{HashMap, HashSet}; use itertools::{Itertools, MinMaxResult}; use rustworkx_core::petgraph::csr::IndexType; @@ -35,9 +35,21 @@ import_exception!(qiskit.circuit.exceptions, CircuitError); // TODO: remove when dev is done, since this is only for manual testing #[pyfunction(name = "draw")] -#[pyo3(signature = (circuit, cregbundle=true, mergewires=true))] -pub fn py_drawer(circuit: QuantumCircuitData, cregbundle: bool, mergewires: bool) -> PyResult<()> { - draw_circuit(&circuit.data, &cregbundle, &mergewires)?; +#[pyo3(signature = (circuit, cregbundle=true, mergewires=true, fold=None))] +pub fn py_drawer(circuit: QuantumCircuitData, cregbundle: bool, mergewires: bool, fold: Option) -> PyResult<()> { + draw_circuit(&circuit.data, cregbundle, mergewires, fold)?; + Ok(()) +} + +pub fn draw_circuit(circuit: &CircuitData, cregbundle: bool, mergewires: bool, fold: Option) -> PyResult<()> { + let vis_mat = VisualizationMatrix::from_circuit(circuit)?; + + let circuit_rep = TextDrawer::from_visualization_matrix(&vis_mat, &cregbundle); + // for i in 0..circuit_rep.wires.len() { + // println!("{:?}", circuit_rep.wires[i]); + // } + circuit_rep.print(mergewires, fold); + Ok(()) } @@ -482,6 +494,8 @@ impl<'a> VisualizationLayer<'a> { /// A dense representation of the circuit of size N * (M + 1), where the first /// layer(column) represents the qubits and clbits inputs in the circuits, and /// M is the number of operation layers. +/// +/// This structure follows a column-major order, where each layer represents a column of the circuit, #[derive(Debug, Clone)] struct VisualizationMatrix<'a> { layers: Vec>, @@ -569,8 +583,6 @@ impl ElementWire { let top = self.top.chars().count(); let mid = self.mid.chars().count(); let bot = self.bot.chars().count(); - // println!("top:{}, mid:{}, bot:{}", top, mid, bot); - // println!("{}", top.max(mid).max(bot)); let max = { if top >= mid && top >= bot { top @@ -580,8 +592,6 @@ impl ElementWire { bot } }; - // println!("max: {}", max); - // println!("{}\n{}\n{}", self.top, self.mid, self.bot); max } @@ -674,6 +684,18 @@ impl Index for TextDrawer { } } +impl Debug for TextDrawer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for layer in &self.wires { + for element in layer { + writeln!(f, "{:?}", element)?; + } + writeln!(f, "-----------------")?; + } + Ok(()) + } +} + impl TextDrawer { fn get_label(instruction: &PackedInstruction) -> Option { let label = instruction.label(); @@ -912,8 +934,8 @@ impl TextDrawer { Some("|0>".to_string()) } else if let StandardInstruction::Barrier(_) = std_instruction { Some("░".to_string()) - } else if let StandardInstruction::Delay(_) = std_instruction { - Some(format!("Delay({})", instruction_param)) + } else if let StandardInstruction::Delay(delay_unit) = std_instruction { + Some(format!("Delay({:?}[{}])", instruction.params, delay_unit)) } else { // Fallback for non-standard instructions Some(format!( @@ -939,7 +961,14 @@ impl TextDrawer { fn from_visualization_matrix<'a>(vis_mat: &'a VisualizationMatrix, cregbundle: &bool) -> Self{ let mut wires: Vec> = vec![]; - for _ in 0..vis_mat.num_wires() { + let num_wires = { + if *cregbundle { + vis_mat.circuit.num_qubits() + 1 + } else { + vis_mat.num_wires() + } + }; + for _ in 0..num_wires { wires.push(vec![]); } @@ -979,9 +1008,25 @@ impl TextDrawer { } } + // // print final visualization matrix + // for i in 0..post_processed_vis_mat.num_wires() { + // for j in 0..post_processed_vis_mat.layers.len() { + // print!("{:?}", post_processed_vis_mat[j][i]); + // } + // println!(); + // } + text_drawer } + fn get_layer_width(&self, ind: usize) -> usize { + self.wires + .iter() + .map(|wire| wire[ind].width()) + .max() + .unwrap_or(0) + } + fn draw_layer( layer: &VisualizationLayer, vis_mat: &VisualizationMatrix, @@ -1390,37 +1435,117 @@ impl TextDrawer { } } - fn print(&self, mergewires: &bool){ + fn print(&self, mergewires: bool, fold: Option) { + + let ranges: Vec<(usize,usize)> = match fold { + Some(f) => { + let mut temp_ranges = vec![]; + let mut layer_counter:usize = 1; + let total_layers = self.wires[0].len(); + while layer_counter < self.wires[0].len(){ + let mut total_width: usize = 0; + total_width += self.get_layer_width(0); + + let start = layer_counter; + while total_width <= f && layer_counter < self.wires[0].len(){ + total_width += self.get_layer_width(layer_counter); + layer_counter += 1; + } + let end = layer_counter; + temp_ranges.push((start, end)); + } + temp_ranges + }, + None => vec![(0, self.wires[0].len())], + }; - if !*mergewires { - let mut output = String::new(); - for i in self.wires.iter(){ - let top_line: String = i.iter().map(|wire| wire.top.clone()).collect::>().join(""); - let mid_line: String = i.iter().map(|wire| wire.mid.clone()).collect::>().join(""); - let bot_line: String = i.iter().map(|wire| wire.bot.clone()).collect::>().join(""); - output.push_str(&format!("{}\n{}\n{}\n", top_line, mid_line, bot_line)); - } - println!("{}", output); - } else { - let num_wires = self.wires.len(); - for i in 0..num_wires - 1 { - if i == 0 { - let top_line = self.wires[i].iter().map(|wire| wire.top.clone()).collect::>().join(""); - let mid_line = self.wires[i].iter().map(|wire| wire.mid.clone()).collect::>().join(""); - println!("{}", top_line); - println!("{}", mid_line); + println!("{:?}", ranges); + for (start,end) in ranges { + if !mergewires { + let mut output = String::new(); + for i in self.wires.iter() { + let top_line: String = i + .iter() + .enumerate() + .filter(|(idx, _)| (*idx >= start && *idx < end) || *idx == 0) // Fix here + .map(|(_, wire)| wire.top.clone()) // And here + .collect::>() + .join(""); + let mid_line: String = i + .iter() + .enumerate() + .filter(|(idx, _)| (*idx >= start && *idx < end) || *idx == 0) // Fix here too + .map(|(_, wire)| wire.mid.clone()) // And here + .collect::>() + .join(""); + let bot_line: String = i + .iter() + .enumerate() + .filter(|(idx, _)| (*idx >= start && *idx < end) || *idx == 0) // And here + .map(|(_, wire)| wire.bot.clone()) // And here + .collect::>() + .join(""); + output.push_str(&format!("{}\n{}\n{}\n", top_line, mid_line, bot_line)); } - let bot_line = self.wires[i].iter().map(|wire| wire.bot.clone()).collect::>().join(""); - let top_line_next = self.wires[i + 1].iter().map(|wire| wire.top.clone()).collect::>().join(""); - let merged_line = Self::merge_lines(&bot_line, &top_line_next, "top"); - println!("{}", merged_line); - let mid_line_next = self.wires[i + 1].iter().map(|wire| wire.mid.clone()).collect::>().join(""); - println!("{}", mid_line_next); + println!("{}", output); + } else { + let num_wires = self.wires.len(); + for i in 0..num_wires - 1 { + if i == 0 { + let top_line = self.wires[i] + .iter() + .enumerate() + .filter(|(idx, _)| (*idx >= start && *idx < end) || *idx == 0) // Fix here + .map(|(_, wire)| wire.top.clone()) // And here + .collect::>() + .join(""); + let mid_line = self.wires[i] + .iter() + .enumerate() + .filter(|(idx, _)| (*idx >= start && *idx < end) || *idx == 0) // Fix here + .map(|(_, wire)| wire.mid.clone()) // And here + .collect::>() + .join(""); + println!("{}", top_line); + println!("{}", mid_line); + } + let bot_line = self.wires[i] + .iter() + .enumerate() + .filter(|(idx, _)| (*idx >= start && *idx < end) || *idx == 0) // Fix here + .map(|(_, wire)| wire.bot.clone()) // And here + .collect::>() + .join(""); + let top_line_next = self.wires[i + 1] + .iter() + .enumerate() + .filter(|(idx, _)| (*idx >= start && *idx < end) || *idx == 0) // Fix here + .map(|(_, wire)| wire.top.clone()) // And here + .collect::>() + .join(""); + let merged_line = Self::merge_lines(&bot_line, &top_line_next, "top"); + println!("{}", merged_line); + let mid_line_next = self.wires[i + 1] + .iter() + .enumerate() + .filter(|(idx, _)| (*idx >= start && *idx < end) || *idx == 0) // Fix here + .map(|(_, wire)| wire.mid.clone()) // And here + .collect::>() + .join(""); + println!("{}", mid_line_next); + } + let last_index = num_wires - 1; + let bot_line = self.wires[last_index] + .iter() + .enumerate() + .filter(|(idx, _)| (*idx >= start && *idx < end) || *idx == 0) // Fix here + .map(|(_, wire)| wire.bot.clone()) // And here + .collect::>() + .join(""); + println!("{}", bot_line); } - let last_index = num_wires - 1; - let bot_line = self.wires[last_index].iter().map(|wire| wire.bot.clone()).collect::>().join(""); - println!("{}", bot_line); } + } pub fn merge_lines(top: &str, bot: &str, icod: &str) -> String { @@ -1476,12 +1601,3 @@ impl TextDrawer { } } -pub fn draw_circuit(circuit: &CircuitData, cregbundle: &bool, mergewires: &bool) -> PyResult<()> { - let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; - - let vis_mat2 = VisualizationMatrix::from_circuit(circuit)?; - - let circuit_rep = TextDrawer::from_visualization_matrix(&vis_mat2, cregbundle); - circuit_rep.print(mergewires); - Ok(()) -} From 4d418767da122153650782c863b5ee5302ab96af Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Thu, 6 Nov 2025 15:29:38 +0530 Subject: [PATCH 44/70] cosmetic addition for folding of quantum circuits --- crates/circuit/src/circuit_drawer.rs | 136 +++++++++++++++------------ 1 file changed, 75 insertions(+), 61 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 893191991711..6688dfef9731 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -36,12 +36,22 @@ import_exception!(qiskit.circuit.exceptions, CircuitError); // TODO: remove when dev is done, since this is only for manual testing #[pyfunction(name = "draw")] #[pyo3(signature = (circuit, cregbundle=true, mergewires=true, fold=None))] -pub fn py_drawer(circuit: QuantumCircuitData, cregbundle: bool, mergewires: bool, fold: Option) -> PyResult<()> { +pub fn py_drawer( + circuit: QuantumCircuitData, + cregbundle: bool, + mergewires: bool, + fold: Option, +) -> PyResult<()> { draw_circuit(&circuit.data, cregbundle, mergewires, fold)?; Ok(()) } -pub fn draw_circuit(circuit: &CircuitData, cregbundle: bool, mergewires: bool, fold: Option) -> PyResult<()> { +pub fn draw_circuit( + circuit: &CircuitData, + cregbundle: bool, + mergewires: bool, + fold: Option, +) -> PyResult<()> { let vis_mat = VisualizationMatrix::from_circuit(circuit)?; let circuit_rep = TextDrawer::from_visualization_matrix(&vis_mat, &cregbundle); @@ -49,7 +59,7 @@ pub fn draw_circuit(circuit: &CircuitData, cregbundle: bool, mergewires: bool, f // println!("{:?}", circuit_rep.wires[i]); // } circuit_rep.print(mergewires, fold); - + Ok(()) } @@ -368,12 +378,12 @@ impl<'a> VisualizationLayer<'a> { // // )); // // } // }, - StandardGate::H - | StandardGate::I - | StandardGate::X - | StandardGate::Y - | StandardGate::Z - | StandardGate::Phase + StandardGate::H + | StandardGate::I + | StandardGate::X + | StandardGate::Y + | StandardGate::Z + | StandardGate::Phase | StandardGate::R | StandardGate::RX | StandardGate::RY @@ -391,9 +401,9 @@ impl<'a> VisualizationLayer<'a> { self.0[qargs[0].index()] = VisualizationElement::Boxed(Boxed::Single(inst)); } StandardGate::CH - | StandardGate::CX + | StandardGate::CX | StandardGate::CY - | StandardGate::CZ + | StandardGate::CZ | StandardGate::CRX | StandardGate::CRY | StandardGate::CRZ @@ -407,8 +417,7 @@ impl<'a> VisualizationLayer<'a> { | StandardGate::CCZ | StandardGate::C3X | StandardGate::C3SX - | StandardGate::CPhase - => { + | StandardGate::CPhase => { self.0[qargs.last().unwrap().index()] = VisualizationElement::Boxed(Boxed::Single(inst)); if gate.num_ctrl_qubits() > 0 { @@ -423,14 +432,11 @@ impl<'a> VisualizationLayer<'a> { } let vert_lines = (minima..=maxima) - .filter(|idx| !(qargs.iter().map(|q| q.0 as usize)).contains(idx)); + .filter(|idx| !(qargs.iter().map(|q| q.0 as usize)).contains(idx)); self.add_vertical_lines(inst, vert_lines); } - StandardGate::GlobalPhase => { - - } - StandardGate::Swap - | StandardGate::CSwap => { + StandardGate::GlobalPhase => {} + StandardGate::Swap | StandardGate::CSwap => { // taking the last 2 elements of qargs let swap_qubits = qargs.iter().map(|q| q.0 as usize).rev().take(2); for qubit in swap_qubits { @@ -447,7 +453,7 @@ impl<'a> VisualizationLayer<'a> { } let vert_lines = (minima..=maxima) - .filter(|idx| !(qargs.iter().map(|q| q.0 as usize)).contains(idx)); + .filter(|idx| !(qargs.iter().map(|q| q.0 as usize)).contains(idx)); self.add_vertical_lines(inst, vert_lines); } } @@ -478,7 +484,8 @@ impl<'a> VisualizationLayer<'a> { self.0[qargs.last().unwrap().index()] = VisualizationElement::Boxed(Boxed::Single(inst)); self.add_vertical_lines(inst, minima + 1..maxima); - self.0[maxima] = VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Bot)) + self.0[maxima] = + VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Bot)) } StandardInstruction::Delay(_) => { for q in qargs { @@ -494,7 +501,7 @@ impl<'a> VisualizationLayer<'a> { /// A dense representation of the circuit of size N * (M + 1), where the first /// layer(column) represents the qubits and clbits inputs in the circuits, and /// M is the number of operation layers. -/// +/// /// This structure follows a column-major order, where each layer represents a column of the circuit, #[derive(Debug, Clone)] struct VisualizationMatrix<'a> { @@ -1436,18 +1443,17 @@ impl TextDrawer { } fn print(&self, mergewires: bool, fold: Option) { - - let ranges: Vec<(usize,usize)> = match fold { + let ranges: Vec<(usize, usize)> = match fold { Some(f) => { let mut temp_ranges = vec![]; - let mut layer_counter:usize = 1; + let mut layer_counter: usize = 1; let total_layers = self.wires[0].len(); - while layer_counter < self.wires[0].len(){ + while layer_counter < self.wires[0].len() { let mut total_width: usize = 0; total_width += self.get_layer_width(0); let start = layer_counter; - while total_width <= f && layer_counter < self.wires[0].len(){ + while total_width <= f && layer_counter < self.wires[0].len() { total_width += self.get_layer_width(layer_counter); layer_counter += 1; } @@ -1455,36 +1461,38 @@ impl TextDrawer { temp_ranges.push((start, end)); } temp_ranges - }, + } None => vec![(0, self.wires[0].len())], }; - println!("{:?}", ranges); - for (start,end) in ranges { + for (j, (start, end)) in ranges.iter().enumerate() { if !mergewires { let mut output = String::new(); - for i in self.wires.iter() { - let top_line: String = i + for (i, element) in self.wires.iter().enumerate() { + let mut top_line: String = element .iter() .enumerate() - .filter(|(idx, _)| (*idx >= start && *idx < end) || *idx == 0) // Fix here - .map(|(_, wire)| wire.top.clone()) // And here + .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here + .map(|(_, wire)| wire.top.clone()) // And here .collect::>() .join(""); - let mid_line: String = i + let mut mid_line: String = element .iter() .enumerate() - .filter(|(idx, _)| (*idx >= start && *idx < end) || *idx == 0) // Fix here too - .map(|(_, wire)| wire.mid.clone()) // And here + .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here too + .map(|(_, wire)| wire.mid.clone()) // And here .collect::>() .join(""); - let bot_line: String = i + let mut bot_line: String = element .iter() .enumerate() - .filter(|(idx, _)| (*idx >= start && *idx < end) || *idx == 0) // And here - .map(|(_, wire)| wire.bot.clone()) // And here + .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // And here + .map(|(_, wire)| wire.bot.clone()) // And here .collect::>() .join(""); + top_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); + mid_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); + bot_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); output.push_str(&format!("{}\n{}\n{}\n", top_line, mid_line, bot_line)); } println!("{}", output); @@ -1492,60 +1500,67 @@ impl TextDrawer { let num_wires = self.wires.len(); for i in 0..num_wires - 1 { if i == 0 { - let top_line = self.wires[i] + let mut top_line = self.wires[i] .iter() .enumerate() - .filter(|(idx, _)| (*idx >= start && *idx < end) || *idx == 0) // Fix here - .map(|(_, wire)| wire.top.clone()) // And here + .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here + .map(|(_, wire)| wire.top.clone()) // And here .collect::>() .join(""); - let mid_line = self.wires[i] + top_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); + let mut mid_line = self.wires[i] .iter() .enumerate() - .filter(|(idx, _)| (*idx >= start && *idx < end) || *idx == 0) // Fix here - .map(|(_, wire)| wire.mid.clone()) // And here + .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here + .map(|(_, wire)| wire.mid.clone()) // And here .collect::>() .join(""); + mid_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); println!("{}", top_line); println!("{}", mid_line); } - let bot_line = self.wires[i] + let mut bot_line = self.wires[i] .iter() .enumerate() - .filter(|(idx, _)| (*idx >= start && *idx < end) || *idx == 0) // Fix here - .map(|(_, wire)| wire.bot.clone()) // And here + .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here + .map(|(_, wire)| wire.bot.clone()) // And here .collect::>() .join(""); - let top_line_next = self.wires[i + 1] + + bot_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); + + let mut top_line_next = self.wires[i + 1] .iter() .enumerate() - .filter(|(idx, _)| (*idx >= start && *idx < end) || *idx == 0) // Fix here - .map(|(_, wire)| wire.top.clone()) // And here + .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here + .map(|(_, wire)| wire.top.clone()) // And here .collect::>() .join(""); - let merged_line = Self::merge_lines(&bot_line, &top_line_next, "top"); + top_line_next.push_str(if j == ranges.len() - 1 { "" } else { "»" }); + let mut merged_line = Self::merge_lines(&bot_line, &top_line_next, "top"); println!("{}", merged_line); - let mid_line_next = self.wires[i + 1] + let mut mid_line_next = self.wires[i + 1] .iter() .enumerate() - .filter(|(idx, _)| (*idx >= start && *idx < end) || *idx == 0) // Fix here - .map(|(_, wire)| wire.mid.clone()) // And here + .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here + .map(|(_, wire)| wire.mid.clone()) // And here .collect::>() .join(""); + mid_line_next.push_str(if j == ranges.len() - 1 { "" } else { "»" }); println!("{}", mid_line_next); } let last_index = num_wires - 1; - let bot_line = self.wires[last_index] + let mut bot_line = self.wires[last_index] .iter() .enumerate() - .filter(|(idx, _)| (*idx >= start && *idx < end) || *idx == 0) // Fix here - .map(|(_, wire)| wire.bot.clone()) // And here + .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here + .map(|(_, wire)| wire.bot.clone()) // And here .collect::>() .join(""); + bot_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); println!("{}", bot_line); } } - } pub fn merge_lines(top: &str, bot: &str, icod: &str) -> String { @@ -1600,4 +1615,3 @@ impl TextDrawer { ret } } - From 5ba5d7539bdf1cffa95d6b45952a850fe4c4e342 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Mon, 10 Nov 2025 12:27:02 +0200 Subject: [PATCH 45/70] Fix `cregbundle` behavior The intention of the `cregbundle` parameter is to bundle the bits of each classical register separately. This commit fixes a bug where all classical register bits were bundled into a single classical register. --- crates/circuit/src/circuit_drawer.rs | 318 ++++++--------------------- 1 file changed, 62 insertions(+), 256 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 6688dfef9731..ec4378c2a644 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -15,7 +15,7 @@ use crate::dag_circuit::DAGCircuit; use crate::operations::{Operation, OperationRef, Param, StandardGate, StandardInstruction}; use crate::packed_instruction::PackedInstruction; use crate::{Clbit, Qubit}; -use core::{num, panic}; +use core::panic; use hashbrown::{HashMap, HashSet}; use itertools::{Itertools, MinMaxResult}; use rustworkx_core::petgraph::csr::IndexType; @@ -35,31 +35,9 @@ import_exception!(qiskit.circuit.exceptions, CircuitError); // TODO: remove when dev is done, since this is only for manual testing #[pyfunction(name = "draw")] -#[pyo3(signature = (circuit, cregbundle=true, mergewires=true, fold=None))] -pub fn py_drawer( - circuit: QuantumCircuitData, - cregbundle: bool, - mergewires: bool, - fold: Option, -) -> PyResult<()> { - draw_circuit(&circuit.data, cregbundle, mergewires, fold)?; - Ok(()) -} - -pub fn draw_circuit( - circuit: &CircuitData, - cregbundle: bool, - mergewires: bool, - fold: Option, -) -> PyResult<()> { - let vis_mat = VisualizationMatrix::from_circuit(circuit)?; - - let circuit_rep = TextDrawer::from_visualization_matrix(&vis_mat, &cregbundle); - // for i in 0..circuit_rep.wires.len() { - // println!("{:?}", circuit_rep.wires[i]); - // } - circuit_rep.print(mergewires, fold); - +#[pyo3(signature = (circuit, cregbundle=true, mergewires=true))] +pub fn py_drawer(circuit: QuantumCircuitData, cregbundle: bool, mergewires: bool) -> PyResult<()> { + draw_circuit(&circuit.data, &cregbundle, &mergewires)?; Ok(()) } @@ -348,7 +326,6 @@ impl<'a> VisualizationLayer<'a> { StandardGate::ISwap | StandardGate::DCX | StandardGate::ECR - | StandardGate::ISwap | StandardGate::RXX | StandardGate::RYY | StandardGate::RZZ @@ -378,48 +355,12 @@ impl<'a> VisualizationLayer<'a> { // // )); // // } // }, - StandardGate::H - | StandardGate::I - | StandardGate::X - | StandardGate::Y - | StandardGate::Z - | StandardGate::Phase - | StandardGate::R - | StandardGate::RX - | StandardGate::RY - | StandardGate::RZ - | StandardGate::S - | StandardGate::Sdg - | StandardGate::SX - | StandardGate::SXdg - | StandardGate::T - | StandardGate::Tdg - | StandardGate::U - | StandardGate::U1 - | StandardGate::U2 - | StandardGate::U3 => { + StandardGate::H | StandardGate::RX | StandardGate::RZ => { self.0[qargs[0].index()] = VisualizationElement::Boxed(Boxed::Single(inst)); - } - StandardGate::CH - | StandardGate::CX - | StandardGate::CY - | StandardGate::CZ - | StandardGate::CRX - | StandardGate::CRY - | StandardGate::CRZ - | StandardGate::CS - | StandardGate::CSdg - | StandardGate::CSX - | StandardGate::CU - | StandardGate::CU1 - | StandardGate::CU3 - | StandardGate::CCX - | StandardGate::CCZ - | StandardGate::C3X - | StandardGate::C3SX - | StandardGate::CPhase => { - self.0[qargs.last().unwrap().index()] = - VisualizationElement::Boxed(Boxed::Single(inst)); + }, + StandardGate::CX | StandardGate::CCX => { + self.0[qargs.last().unwrap().index()] = VisualizationElement::Boxed(Boxed::Single(inst)); + let mut control_indices: HashSet = HashSet::new(); if gate.num_ctrl_qubits() > 0 { self.add_controls( &qargs @@ -431,32 +372,13 @@ impl<'a> VisualizationLayer<'a> { ); } - let vert_lines = (minima..=maxima) - .filter(|idx| !(qargs.iter().map(|q| q.0 as usize)).contains(idx)); - self.add_vertical_lines(inst, vert_lines); - } - StandardGate::GlobalPhase => {} - StandardGate::Swap | StandardGate::CSwap => { - // taking the last 2 elements of qargs - let swap_qubits = qargs.iter().map(|q| q.0 as usize).rev().take(2); - for qubit in swap_qubits { - if qubit == minima { - self.0[qubit] = - VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Top)); - } else if qubit == maxima { - self.0[qubit] = - VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Bot)); - } else { - self.0[qubit] = - VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Mid)); - } - } - - let vert_lines = (minima..=maxima) - .filter(|idx| !(qargs.iter().map(|q| q.0 as usize)).contains(idx)); - self.add_vertical_lines(inst, vert_lines); } + _ => unimplemented!("{}", format!("{:?} is not supported yet", gate)) } + + // let vert_lines = (minima..=maxima) + // .filter(|idx| !control_indices.contains(idx) && !box_indices.contains(idx)); + // self.add_vertical_lines(inst, vert_lines); } fn add_standard_instruction( @@ -481,11 +403,8 @@ impl<'a> VisualizationLayer<'a> { } } StandardInstruction::Measure => { - self.0[qargs.last().unwrap().index()] = - VisualizationElement::Boxed(Boxed::Single(inst)); - self.add_vertical_lines(inst, minima + 1..maxima); - self.0[maxima] = - VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Bot)) + self.0[qargs.last().unwrap().index()] = VisualizationElement::Boxed(Boxed::Single(inst)); + self.add_vertical_lines(inst, minima + 1..=maxima); } StandardInstruction::Delay(_) => { for q in qargs { @@ -501,8 +420,6 @@ impl<'a> VisualizationLayer<'a> { /// A dense representation of the circuit of size N * (M + 1), where the first /// layer(column) represents the qubits and clbits inputs in the circuits, and /// M is the number of operation layers. -/// -/// This structure follows a column-major order, where each layer represents a column of the circuit, #[derive(Debug, Clone)] struct VisualizationMatrix<'a> { layers: Vec>, @@ -590,6 +507,8 @@ impl ElementWire { let top = self.top.chars().count(); let mid = self.mid.chars().count(); let bot = self.bot.chars().count(); + // println!("top:{}, mid:{}, bot:{}", top, mid, bot); + // println!("{}", top.max(mid).max(bot)); let max = { if top >= mid && top >= bot { top @@ -599,6 +518,8 @@ impl ElementWire { bot } }; + // println!("max: {}", max); + // println!("{}\n{}\n{}", self.top, self.mid, self.bot); max } @@ -691,18 +612,6 @@ impl Index for TextDrawer { } } -impl Debug for TextDrawer { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for layer in &self.wires { - for element in layer { - writeln!(f, "{:?}", element)?; - } - writeln!(f, "-----------------")?; - } - Ok(()) - } -} - impl TextDrawer { fn get_label(instruction: &PackedInstruction) -> Option { let label = instruction.label(); @@ -919,7 +828,7 @@ impl TextDrawer { } StandardGate::RCCX => { // Relative-phase Toffoli gate - Some(format!(" RCCX({}) ", instruction_param)) + Some(format!(" RX({}) ", instruction_param)) } StandardGate::C3X => { // 3-controlled X gate (4-qubit controlled X) @@ -931,7 +840,7 @@ impl TextDrawer { } StandardGate::RC3X => { // Relative-phase 3-controlled X gate - Some(format!(" RC3X({}) ", instruction_param)) + Some(format!(" RX({}) ", instruction_param)) } } } else if let Some(std_instruction) = instruction.op.try_standard_instruction() { @@ -941,8 +850,6 @@ impl TextDrawer { Some("|0>".to_string()) } else if let StandardInstruction::Barrier(_) = std_instruction { Some("░".to_string()) - } else if let StandardInstruction::Delay(delay_unit) = std_instruction { - Some(format!("Delay({:?}[{}])", instruction.params, delay_unit)) } else { // Fallback for non-standard instructions Some(format!( @@ -968,14 +875,7 @@ impl TextDrawer { fn from_visualization_matrix<'a>(vis_mat: &'a VisualizationMatrix, cregbundle: &bool) -> Self{ let mut wires: Vec> = vec![]; - let num_wires = { - if *cregbundle { - vis_mat.circuit.num_qubits() + 1 - } else { - vis_mat.num_wires() - } - }; - for _ in 0..num_wires { + for _ in 0..vis_mat.num_wires() { wires.push(vec![]); } @@ -1015,25 +915,9 @@ impl TextDrawer { } } - // // print final visualization matrix - // for i in 0..post_processed_vis_mat.num_wires() { - // for j in 0..post_processed_vis_mat.layers.len() { - // print!("{:?}", post_processed_vis_mat[j][i]); - // } - // println!(); - // } - text_drawer } - fn get_layer_width(&self, ind: usize) -> usize { - self.wires - .iter() - .map(|wire| wire[ind].width()) - .max() - .unwrap_or(0) - } - fn draw_layer( layer: &VisualizationLayer, vis_mat: &VisualizationMatrix, @@ -1221,7 +1105,7 @@ impl TextDrawer { let mid_section = if ind == mid { format!( - "{:^total_q$}{:^label_len$}", + "{:^total_q$} {:^label_len$}", num_affected, label, total_q = qargs.len(), @@ -1229,7 +1113,7 @@ impl TextDrawer { ) } else { format!( - "{:^total_q$}{:^label_len$}", + "{:^total_q$} {:^label_len$}", num_affected, " ", total_q = qargs.len(), @@ -1442,124 +1326,36 @@ impl TextDrawer { } } - fn print(&self, mergewires: bool, fold: Option) { - let ranges: Vec<(usize, usize)> = match fold { - Some(f) => { - let mut temp_ranges = vec![]; - let mut layer_counter: usize = 1; - let total_layers = self.wires[0].len(); - while layer_counter < self.wires[0].len() { - let mut total_width: usize = 0; - total_width += self.get_layer_width(0); - - let start = layer_counter; - while total_width <= f && layer_counter < self.wires[0].len() { - total_width += self.get_layer_width(layer_counter); - layer_counter += 1; - } - let end = layer_counter; - temp_ranges.push((start, end)); - } - temp_ranges - } - None => vec![(0, self.wires[0].len())], - }; + fn print(&self, mergewires: &bool){ - for (j, (start, end)) in ranges.iter().enumerate() { - if !mergewires { - let mut output = String::new(); - for (i, element) in self.wires.iter().enumerate() { - let mut top_line: String = element - .iter() - .enumerate() - .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here - .map(|(_, wire)| wire.top.clone()) // And here - .collect::>() - .join(""); - let mut mid_line: String = element - .iter() - .enumerate() - .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here too - .map(|(_, wire)| wire.mid.clone()) // And here - .collect::>() - .join(""); - let mut bot_line: String = element - .iter() - .enumerate() - .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // And here - .map(|(_, wire)| wire.bot.clone()) // And here - .collect::>() - .join(""); - top_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); - mid_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); - bot_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); - output.push_str(&format!("{}\n{}\n{}\n", top_line, mid_line, bot_line)); - } - println!("{}", output); - } else { - let num_wires = self.wires.len(); - for i in 0..num_wires - 1 { - if i == 0 { - let mut top_line = self.wires[i] - .iter() - .enumerate() - .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here - .map(|(_, wire)| wire.top.clone()) // And here - .collect::>() - .join(""); - top_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); - let mut mid_line = self.wires[i] - .iter() - .enumerate() - .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here - .map(|(_, wire)| wire.mid.clone()) // And here - .collect::>() - .join(""); - mid_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); - println!("{}", top_line); - println!("{}", mid_line); - } - let mut bot_line = self.wires[i] - .iter() - .enumerate() - .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here - .map(|(_, wire)| wire.bot.clone()) // And here - .collect::>() - .join(""); - - bot_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); - - let mut top_line_next = self.wires[i + 1] - .iter() - .enumerate() - .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here - .map(|(_, wire)| wire.top.clone()) // And here - .collect::>() - .join(""); - top_line_next.push_str(if j == ranges.len() - 1 { "" } else { "»" }); - let mut merged_line = Self::merge_lines(&bot_line, &top_line_next, "top"); - println!("{}", merged_line); - let mut mid_line_next = self.wires[i + 1] - .iter() - .enumerate() - .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here - .map(|(_, wire)| wire.mid.clone()) // And here - .collect::>() - .join(""); - mid_line_next.push_str(if j == ranges.len() - 1 { "" } else { "»" }); - println!("{}", mid_line_next); + if !*mergewires { + let mut output = String::new(); + for i in self.wires.iter(){ + let top_line: String = i.iter().map(|wire| wire.top.clone()).collect::>().join(""); + let mid_line: String = i.iter().map(|wire| wire.mid.clone()).collect::>().join(""); + let bot_line: String = i.iter().map(|wire| wire.bot.clone()).collect::>().join(""); + output.push_str(&format!("{}\n{}\n{}\n", top_line, mid_line, bot_line)); + } + println!("{}", output); + } else { + let num_wires = self.wires.len(); + for i in 0..num_wires - 1 { + if i == 0 { + let top_line = self.wires[i].iter().map(|wire| wire.top.clone()).collect::>().join(""); + let mid_line = self.wires[i].iter().map(|wire| wire.mid.clone()).collect::>().join(""); + println!("{}", top_line); + println!("{}", mid_line); } - let last_index = num_wires - 1; - let mut bot_line = self.wires[last_index] - .iter() - .enumerate() - .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here - .map(|(_, wire)| wire.bot.clone()) // And here - .collect::>() - .join(""); - bot_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); - println!("{}", bot_line); + let bot_line = self.wires[i].iter().map(|wire| wire.bot.clone()).collect::>().join(""); + let top_line_next = self.wires[i + 1].iter().map(|wire| wire.top.clone()).collect::>().join(""); + let merged_line = Self::merge_lines(&bot_line, &top_line_next, "top"); + println!("{}", merged_line); + let mid_line_next = self.wires[i + 1].iter().map(|wire| wire.mid.clone()).collect::>().join(""); + println!("{}", mid_line_next); } + let last_index = num_wires - 1; + let bot_line = self.wires[last_index].iter().map(|wire| wire.bot.clone()).collect::>().join(""); + println!("{}", bot_line); } } @@ -1615,3 +1411,13 @@ impl TextDrawer { ret } } + +pub fn draw_circuit(circuit: &CircuitData, cregbundle: &bool, mergewires: &bool) -> PyResult<()> { + let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; + + let vis_mat2 = VisualizationMatrix::from_circuit(circuit)?; + + let circuit_rep = TextDrawer::from_visualization_matrix(&vis_mat2, cregbundle); + circuit_rep.print(mergewires); + Ok(()) +} From 7d7ade067de8cbfdae7ce0c52aa5a7410fff6357 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Thu, 13 Nov 2025 12:46:14 +0530 Subject: [PATCH 46/70] added aspect ratio based folding for printing, index out of range bug for creg bundling cases --- Cargo.lock | 190 +++++++++++++++++++++++ crates/circuit/Cargo.toml | 1 + crates/circuit/src/circuit_drawer.rs | 223 +++++++++++++++++++++------ 3 files changed, 369 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3a239209797f..c9e40b6e87b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -300,6 +300,15 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "convert_case" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "countme" version = "3.0.1" @@ -377,6 +386,33 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crossterm" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" +dependencies = [ + "bitflags 2.9.4", + "crossterm_winapi", + "derive_more", + "document-features", + "mio", + "parking_lot", + "rustix", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + [[package]] name = "crunchy" version = "0.2.4" @@ -399,6 +435,27 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "930c7171c8df9fb1782bdf9b918ed9ed2d33d1d22300abb754f9085bc48bf8e8" +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "digest" version = "0.10.7" @@ -409,6 +466,15 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + [[package]] name = "drop_bomb" version = "0.1.5" @@ -937,6 +1003,21 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.28" @@ -990,6 +1071,18 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mio" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +dependencies = [ + "libc", + "log", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.61.1", +] + [[package]] name = "nalgebra" version = "0.33.2" @@ -1319,6 +1412,29 @@ dependencies = [ "xshell", ] +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link 0.2.0", +] + [[package]] name = "paste" version = "1.0.15" @@ -1677,6 +1793,7 @@ dependencies = [ "approx", "bitfield-struct", "bytemuck", + "crossterm", "hashbrown 0.15.5", "indexmap", "itertools 0.14.0", @@ -1998,6 +2115,15 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03251193000f4bd3b042892be858ee50e8b3719f2b08e5833ac4353724632430" +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.9.4", +] + [[package]] name = "regex" version = "1.12.2" @@ -2144,6 +2270,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "seq-macro" version = "0.3.6" @@ -2228,6 +2360,36 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +dependencies = [ + "libc", +] + [[package]] name = "simba" version = "0.9.1" @@ -2509,6 +2671,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-width" version = "0.1.14" @@ -2599,6 +2767,22 @@ dependencies = [ "safe_arch", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.11" @@ -2608,6 +2792,12 @@ dependencies = [ "windows-sys 0.61.1", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows" version = "0.61.3" diff --git a/crates/circuit/Cargo.toml b/crates/circuit/Cargo.toml index 305ec615fbfe..a292721f11fd 100644 --- a/crates/circuit/Cargo.toml +++ b/crates/circuit/Cargo.toml @@ -27,6 +27,7 @@ nom.workspace = true nom-unicode.workspace = true nom-language.workspace = true num-bigint.workspace = true +crossterm = "0.29.0" [dependencies.pyo3] workspace = true diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index ec4378c2a644..d70e4a38e29a 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -10,12 +10,13 @@ // copyright notice, and modified files need to carry a notice indicating // that they have been altered from the originals. -use crate::bit::{ShareableClbit, ShareableQubit}; +use crate::bit::{ShareableClbit, ShareableQubit, ClassicalRegister}; use crate::dag_circuit::DAGCircuit; use crate::operations::{Operation, OperationRef, Param, StandardGate, StandardInstruction}; use crate::packed_instruction::PackedInstruction; use crate::{Clbit, Qubit}; use core::panic; +use crossterm::terminal::size; use hashbrown::{HashMap, HashSet}; use itertools::{Itertools, MinMaxResult}; use rustworkx_core::petgraph::csr::IndexType; @@ -35,10 +36,37 @@ import_exception!(qiskit.circuit.exceptions, CircuitError); // TODO: remove when dev is done, since this is only for manual testing #[pyfunction(name = "draw")] -#[pyo3(signature = (circuit, cregbundle=true, mergewires=true))] -pub fn py_drawer(circuit: QuantumCircuitData, cregbundle: bool, mergewires: bool) -> PyResult<()> { - draw_circuit(&circuit.data, &cregbundle, &mergewires)?; - Ok(()) +#[pyo3(signature = (circuit, cregbundle=true, mergewires=true, fold=None))] +pub fn py_drawer( + circuit: QuantumCircuitData, + cregbundle: bool, + mergewires: bool, + fold: Option, +) -> PyResult { + draw_circuit(&circuit.data, cregbundle, mergewires, fold) +} + +pub fn draw_circuit( + circuit: &CircuitData, + cregbundle: bool, + mergewires: bool, + fold: Option, +) -> PyResult { + let vis_mat = VisualizationMatrix::from_circuit(circuit, cregbundle)?; + + let circuit_rep = TextDrawer::from_visualization_matrix(&vis_mat, &cregbundle); + + let fold = match fold { + Some(f) => f, + None => { + let (term_width, _) = size().unwrap_or((80, 24)); + term_width as usize + } + }; + + let output = circuit_rep.print(mergewires, fold); + + Ok(output) } /// Return a list of layers such that each layer contains a list of op node indices, representing instructions @@ -102,6 +130,7 @@ fn get_instruction_range( enum ElementWireInput<'a> { Qubit(&'a ShareableQubit), Clbit(&'a ShareableClbit), + Creg(&'a ClassicalRegister) } impl PartialOrd for ElementWireInput<'_> { @@ -117,6 +146,8 @@ impl Ord for ElementWireInput<'_> { (ElementWireInput::Clbit(c1), ElementWireInput::Clbit(c2)) => Ordering::Equal, (ElementWireInput::Qubit(_), ElementWireInput::Clbit(_)) => Ordering::Less, (ElementWireInput::Clbit(_), ElementWireInput::Qubit(_)) => Ordering::Greater, + (ElementWireInput::Creg(_), _) => Ordering::Greater, + (_, ElementWireInput::Creg(_)) => Ordering::Less, } } } @@ -126,6 +157,7 @@ impl<'a> Debug for ElementWireInput<'a> { let name = match self { ElementWireInput::Qubit(qubit) => "Qubit", ElementWireInput::Clbit(clbit) => "Clbit", + ElementWireInput::Creg(_) => "Creg" }; write!(f, "{}", name) @@ -256,13 +288,18 @@ impl<'a> VisualizationLayer<'a> { } /// Adds the required visualization elements to represent the given instruction - fn add_instruction(&mut self, inst: &'a PackedInstruction, circuit: &CircuitData) { + fn add_instruction( + &mut self, + cregbundle: bool, + inst: &'a PackedInstruction, + circuit: &CircuitData, + ) { match inst.op.view() { OperationRef::StandardGate(gate) => { self.add_standard_gate(gate, inst, circuit); } OperationRef::StandardInstruction(std_inst) => { - self.add_standard_instruction(std_inst, inst, circuit); + self.add_standard_instruction(cregbundle, std_inst, inst, circuit); } _ => unimplemented!( "{}", @@ -383,6 +420,7 @@ impl<'a> VisualizationLayer<'a> { fn add_standard_instruction( &mut self, + cregbundle: bool, std_inst: StandardInstruction, inst: &'a PackedInstruction, circuit: &CircuitData, @@ -427,7 +465,7 @@ struct VisualizationMatrix<'a> { } impl<'a> VisualizationMatrix<'a> { - fn from_circuit(circuit: &'a CircuitData) -> PyResult { + fn from_circuit(circuit: &'a CircuitData, bundle_cregs: bool) -> PyResult { let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; let mut node_index_to_inst: HashMap = @@ -458,7 +496,11 @@ impl<'a> VisualizationMatrix<'a> { for (i, layer) in inst_layers.iter().enumerate() { for node_index in layer { - layers[i + 1].add_instruction(node_index_to_inst.get(node_index).unwrap(), circuit); + layers[i + 1].add_instruction( + bundle_cregs, + node_index_to_inst.get(node_index).unwrap(), + circuit, + ); } } @@ -918,6 +960,14 @@ impl TextDrawer { text_drawer } + fn get_layer_width(&self, ind: usize) -> usize { + self.wires + .iter() + .map(|wire| wire[ind].width()) + .max() + .unwrap_or(0) + } + fn draw_layer( layer: &VisualizationLayer, vis_mat: &VisualizationMatrix, @@ -1279,6 +1329,15 @@ impl TextDrawer { bot: format!("{}", " ".repeat(clbit_name.len())), } } + ElementWireInput::Creg(creg) => { + let wire_name = format!("{}: {}/", creg.name(), creg.len()); + + ElementWire { + top: format!("{}", " ".repeat(wire_name.len())), + mid: format!("{}", wire_name), + bot: format!("{}", " ".repeat(wire_name.len())), + } + } }, VisualizationElement::VerticalLine(input_type) => { let crossed = { @@ -1326,37 +1385,121 @@ impl TextDrawer { } } - fn print(&self, mergewires: &bool){ - - if !*mergewires { - let mut output = String::new(); - for i in self.wires.iter(){ - let top_line: String = i.iter().map(|wire| wire.top.clone()).collect::>().join(""); - let mid_line: String = i.iter().map(|wire| wire.mid.clone()).collect::>().join(""); - let bot_line: String = i.iter().map(|wire| wire.bot.clone()).collect::>().join(""); - output.push_str(&format!("{}\n{}\n{}\n", top_line, mid_line, bot_line)); + fn print(&self, mergewires: bool, fold: usize) -> String { + let ranges: Vec<(usize, usize)> = { + let mut temp_ranges = vec![]; + let mut layer_counter: usize = 1; + while layer_counter < self.wires[0].len() { + let mut total_width: usize = 0; + total_width += self.get_layer_width(0); + + let start = layer_counter; + while total_width <= fold && layer_counter < self.wires[0].len() { + total_width += self.get_layer_width(layer_counter); + layer_counter += 1; + } + let end = layer_counter; + temp_ranges.push((start, end)); } - println!("{}", output); - } else { - let num_wires = self.wires.len(); - for i in 0..num_wires - 1 { - if i == 0 { - let top_line = self.wires[i].iter().map(|wire| wire.top.clone()).collect::>().join(""); - let mid_line = self.wires[i].iter().map(|wire| wire.mid.clone()).collect::>().join(""); - println!("{}", top_line); - println!("{}", mid_line); + temp_ranges + }; + + let mut output = String::new(); + + for (j, (start, end)) in ranges.iter().enumerate() { + if !mergewires { + for (i, element) in self.wires.iter().enumerate() { + let mut top_line: String = element + .iter() + .enumerate() + .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here + .map(|(_, wire)| wire.top.clone()) // And here + .collect::>() + .join(""); + let mut mid_line: String = element + .iter() + .enumerate() + .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here too + .map(|(_, wire)| wire.mid.clone()) // And here + .collect::>() + .join(""); + let mut bot_line: String = element + .iter() + .enumerate() + .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // And here + .map(|(_, wire)| wire.bot.clone()) // And here + .collect::>() + .join(""); + top_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); + mid_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); + bot_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); + output.push_str(&format!("{}\n{}\n{}\n", top_line, mid_line, bot_line)); + } + } else { + let num_wires = self.wires.len(); + for i in 0..num_wires - 1 { + if i == 0 { + let mut top_line = self.wires[i] + .iter() + .enumerate() + .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here + .map(|(_, wire)| wire.top.clone()) // And here + .collect::>() + .join(""); + top_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); + let mut mid_line = self.wires[i] + .iter() + .enumerate() + .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here + .map(|(_, wire)| wire.mid.clone()) // And here + .collect::>() + .join(""); + mid_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); + output.push_str(&format!("{}\n{}\n", top_line, mid_line)); + } + let mut bot_line = self.wires[i] + .iter() + .enumerate() + .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here + .map(|(_, wire)| wire.bot.clone()) // And here + .collect::>() + .join(""); + + bot_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); + + let mut top_line_next = self.wires[i + 1] + .iter() + .enumerate() + .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here + .map(|(_, wire)| wire.top.clone()) // And here + .collect::>() + .join(""); + + top_line_next.push_str(if j == ranges.len() - 1 { "" } else { "»" }); + let mut merged_line = Self::merge_lines(&bot_line, &top_line_next, "top"); + output.push_str(&format!("{}\n", merged_line)); + let mut mid_line_next = self.wires[i + 1] + .iter() + .enumerate() + .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here + .map(|(_, wire)| wire.mid.clone()) // And here + .collect::>() + .join(""); + mid_line_next.push_str(if j == ranges.len() - 1 { "" } else { "»" }); + output.push_str(&format!("{}\n", mid_line_next)); } - let bot_line = self.wires[i].iter().map(|wire| wire.bot.clone()).collect::>().join(""); - let top_line_next = self.wires[i + 1].iter().map(|wire| wire.top.clone()).collect::>().join(""); + let last_index = num_wires - 1; + let bot_line = self.wires[last_index].iter().map(|wire| wire.bot.clone()).collect::>().join(""); + let top_line_next = self.wires[last_index + 1].iter().map(|wire| wire.top.clone()).collect::>().join(""); let merged_line = Self::merge_lines(&bot_line, &top_line_next, "top"); - println!("{}", merged_line); - let mid_line_next = self.wires[i + 1].iter().map(|wire| wire.mid.clone()).collect::>().join(""); - println!("{}", mid_line_next); + output.push_str(&format!("{}\n", merged_line)); + let mid_line_next = self.wires[last_index + 1].iter().map(|wire| wire.mid.clone()).collect::>().join(""); + output.push_str(&format!("{}\n", mid_line_next)); + let bot_line = self.wires[last_index].iter().map(|wire| wire.bot.clone()).collect::>().join(""); + output.push_str(&format!("{}\n", bot_line)) } - let last_index = num_wires - 1; - let bot_line = self.wires[last_index].iter().map(|wire| wire.bot.clone()).collect::>().join(""); - println!("{}", bot_line); } + output } pub fn merge_lines(top: &str, bot: &str, icod: &str) -> String { @@ -1411,13 +1554,3 @@ impl TextDrawer { ret } } - -pub fn draw_circuit(circuit: &CircuitData, cregbundle: &bool, mergewires: &bool) -> PyResult<()> { - let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; - - let vis_mat2 = VisualizationMatrix::from_circuit(circuit)?; - - let circuit_rep = TextDrawer::from_visualization_matrix(&vis_mat2, cregbundle); - circuit_rep.print(mergewires); - Ok(()) -} From 16b98a89276132a3adb8cd442b34cf3b10b368d8 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Sun, 16 Nov 2025 21:54:06 +0200 Subject: [PATCH 47/70] Simplify get_label This commits refactors the `TextDrawer::get_label` method to be more Rust idiomatic. --- crates/circuit/src/circuit_drawer.rs | 339 +++++++-------------------- 1 file changed, 83 insertions(+), 256 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index d70e4a38e29a..87eba6529790 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -655,264 +655,91 @@ impl Index for TextDrawer { } impl TextDrawer { - fn get_label(instruction: &PackedInstruction) -> Option { - let label = instruction.label(); - let mut instruction_param = String::new(); - for param in instruction.params_view() { - let param_sub_str = match param { - Param::Float(f) => format!("{:.2}", f), - _ => format!("{:?}", param), + fn get_label(instruction: &PackedInstruction) -> String { + if let Some(label) = instruction.label() { + return format!(" {} ", label.to_string()); + } + + if let Some(std_instruction) = instruction.op.try_standard_instruction() { + return match std_instruction { + StandardInstruction::Measure => "M".to_string(), + StandardInstruction::Reset => "|0>".to_string(), + StandardInstruction::Barrier(_) => "░".to_string(), + StandardInstruction::Delay(delay_unit) => format!("Delay({:?}[{}])", instruction.params, delay_unit), }; - if instruction_param.is_empty() { - instruction_param = param_sub_str; - } else { - instruction_param = format!("{}, {}", instruction_param, param_sub_str); - } } - // let instruction_param =format!("{:?}",instruction.params_view()); - let instruction_label = match label { - Some(l) => Some(format!("{}{}{}", " ", l.to_string(), " ")), - None => { - if let Some(standard_gate) = instruction.op.try_standard_gate() { - match standard_gate { - StandardGate::GlobalPhase => { - // Global phase gate - affects overall circuit phase - None - } - StandardGate::H => { - // Hadamard gate - creates superposition - Some(" H ".to_string()) - } - StandardGate::I => { - // Identity gate - no operation - Some(" I ".to_string()) - } - StandardGate::X => { - // Pauli-X gate (NOT gate) - Some(" X ".to_string()) - } - StandardGate::Y => { - // Pauli-Y gate - Some(" Y ".to_string()) - } - StandardGate::Z => { - // Pauli-Z gate - Some(" Z ".to_string()) - } - StandardGate::Phase => { - // Phase gate (parameterized) - Some(format!(" P({}) ", instruction_param)) - } - StandardGate::R => { - // R gate (rotation about axis in XY plane) - Some(format!(" R({}) ", instruction_param)) - } - StandardGate::RX => { - // Rotation about X axis - Some(format!(" RX({}) ", instruction_param)) - } - StandardGate::RY => { - // Rotation about Y axis - Some(format!(" RY({}) ", instruction_param)) - } - StandardGate::RZ => { - // Rotation about Z axis - Some(format!(" RZ({}) ", instruction_param)) - } - StandardGate::S => { - // S gate (phase π/2) - Some(" S ".to_string()) - } - StandardGate::Sdg => { - // S dagger gate (phase -π/2) - Some(" Sdg ".to_string()) - } - StandardGate::SX => { - // Square root of X gate - Some(" √X ".to_string()) - } - StandardGate::SXdg => { - // Square root of X dagger gate - Some(" √Xdg ".to_string()) - } - StandardGate::T => { - // T gate (phase π/4) - Some(" T ".to_string()) - } - StandardGate::Tdg => { - // T dagger gate (phase -π/4) - Some(" T† ".to_string()) - } - StandardGate::U => { - // Universal single-qubit gate (3 parameters) - Some(format!(" U({}) ", instruction_param)) - } - StandardGate::U1 => { - // U1 gate (1 parameter - phase) - Some(format!(" U1({}) ", instruction_param)) - } - StandardGate::U2 => { - // U2 gate (2 parameters) - Some(format!(" U2({}) ", instruction_param)) - } - StandardGate::U3 => { - // U3 gate (3 parameters - equivalent to U) - Some(format!(" U3({}) ", instruction_param)) - } - StandardGate::CH => { - // Controlled Hadamard gate - Some(" H ".to_string()) - } - StandardGate::CX => { - // Controlled-X gate (CNOT) - Some(" X ".to_string()) - } - StandardGate::CY => { - // Controlled-Y gate - Some(" Y ".to_string()) - } - StandardGate::CZ => { - // Controlled-Z gate - Some(" Z ".to_string()) - } - StandardGate::DCX => { - // Double CNOT gate - Some(" DCX ".to_string()) - } - StandardGate::ECR => { - // Echoed cross-resonance gate - Some(" ECR ".to_string()) - } - StandardGate::Swap => { - // Swap gate - None - } - StandardGate::ISwap => { - // i-Swap gate - Some(" Iswap ".to_string()) - } - StandardGate::CPhase => { - // Controlled phase gate - Some(format!(" P({}) ", instruction_param)) - } - StandardGate::CRX => { - // Controlled rotation about X - Some(format!(" RX({}) ", instruction_param)) - } - StandardGate::CRY => { - // Controlled rotation about Y - Some(format!(" RY({}) ", instruction_param)) - } - StandardGate::CRZ => { - // Controlled rotation about Z - Some(format!(" RZ({}) ", instruction_param)) - } - StandardGate::CS => { - // Controlled S gate - Some(" S ".to_string()) - } - StandardGate::CSdg => { - // Controlled S dagger gate - Some(" Sdg ".to_string()) - } - StandardGate::CSX => { - // Controlled square root of X gate - Some(" √X ".to_string()) - } - StandardGate::CU => { - // Controlled U gate (4 parameters) - Some(format!(" U({}) ", instruction_param)) - } - StandardGate::CU1 => { - // Controlled U1 gate - Some(format!(" U1({}) ", instruction_param)) - } - StandardGate::CU3 => { - // Controlled U3 gate - Some(format!(" U3({}) ", instruction_param)) - } - StandardGate::RXX => { - // Two-qubit XX rotation - Some(format!(" RXX({}) ", instruction_param)) - } - StandardGate::RYY => { - // Two-qubit YY rotation - Some(format!(" RYY({}) ", instruction_param)) - } - StandardGate::RZZ => { - // Two-qubit ZZ rotation - Some(format!(" RZZ({}) ", instruction_param)) - } - StandardGate::RZX => { - // Two-qubit ZX rotation - Some(format!(" RZX({}) ", instruction_param)) - } - StandardGate::XXMinusYY => { - // XX-YY gate - Some(format!(" XX-YY({}) ", instruction_param)) - } - StandardGate::XXPlusYY => { - // XX+YY gate - Some(format!(" XX+YY({}) ", instruction_param)) - } - StandardGate::CCX => { - // Toffoli gate (controlled-controlled-X) - Some(" X ".to_string()) - } - StandardGate::CCZ => { - // Controlled-controlled-Z gate - Some(" Z ".to_string()) - } - StandardGate::CSwap => { - // Controlled swap gate (Fredkin gate) - None - } - StandardGate::RCCX => { - // Relative-phase Toffoli gate - Some(format!(" RX({}) ", instruction_param)) - } - StandardGate::C3X => { - // 3-controlled X gate (4-qubit controlled X) - Some(" X ".to_string()) - } - StandardGate::C3SX => { - // 3-controlled square root of X gate - Some(" √X ".to_string()) - } - StandardGate::RC3X => { - // Relative-phase 3-controlled X gate - Some(format!(" RX({}) ", instruction_param)) - } - } - } else if let Some(std_instruction) = instruction.op.try_standard_instruction() { - if std_instruction == StandardInstruction::Measure { - Some("M".to_string()) - } else if std_instruction == StandardInstruction::Reset { - Some("|0>".to_string()) - } else if let StandardInstruction::Barrier(_) = std_instruction { - Some("░".to_string()) - } else { - // Fallback for non-standard instructions - Some(format!( - "{}{}{}", - " ", - instruction.op.name().to_string(), - " " - )) - } - } else { - // Fallback for non-standard operations - Some(format!( - "{}{}{}", - " ", - instruction.op.name().to_string(), - " " - )) - } + if let Some(standard_gate) = instruction.op.try_standard_gate() { + let mut label = match standard_gate { + StandardGate::GlobalPhase => "", + StandardGate::H => "H", + StandardGate::I => "I", + StandardGate::X => "X", + StandardGate::Y => "Y", + StandardGate::Z => "Z", + StandardGate::Phase => "P", + StandardGate::R => "R", + StandardGate::RX => "RX", + StandardGate::RY => "RY", + StandardGate::RZ => "RZ", + StandardGate::S => "S", + StandardGate::Sdg => "Sdg", + StandardGate::SX => "√X", + StandardGate::SXdg => "√Xdg", + StandardGate::T => "T", + StandardGate::Tdg => "T†", + StandardGate::U => "U", + StandardGate::U1 => "U1", + StandardGate::U2 => "U2", + StandardGate::U3 => "U3", + StandardGate::CH => "H", + StandardGate::CX => "X", + StandardGate::CY => "Y", + StandardGate::CZ => "Z", + StandardGate::DCX => "DCX", + StandardGate::ECR => "ECR", + StandardGate::Swap => "", + StandardGate::ISwap => "Iswap", + StandardGate::CPhase => "P", + StandardGate::CRX => "RX", + StandardGate::CRY => "RY", + StandardGate::CRZ => "RZ", + StandardGate::CS => "S", + StandardGate::CSdg => "Sdg", + StandardGate::CSX => "√X", + StandardGate::CU => "U", + StandardGate::CU1 => "U1", + StandardGate::CU3 => "U3", + StandardGate::RXX => "RXX", + StandardGate::RYY => "RYY", + StandardGate::RZZ => "RZZ", + StandardGate::RZX => "RZX", + StandardGate::XXMinusYY => "XX-YY", + StandardGate::XXPlusYY => "XX+YY", + StandardGate::CCX => "X", + StandardGate::CCZ => "Z", + StandardGate::CSwap => "", + StandardGate::RCCX => "RCCX", + StandardGate::C3X => "X", + StandardGate::C3SX => "√X", + StandardGate::RC3X => "RC3X", } - }; - instruction_label + .to_string(); + + if standard_gate.num_params() > 0 { + let params = instruction.params_view().iter().map(|param| match param { + Param::Float(f) => format!("{}", f), + _ => format!("{:?}", param), + }) + .join(","); + label = format!("{}({})", label, params); + } + + return format!(" {} ", label); + } + + // Fallback for non-standard operations + format!(" {} ", instruction.op.name().to_string()) } fn from_visualization_matrix<'a>(vis_mat: &'a VisualizationMatrix, cregbundle: &bool) -> Self{ @@ -1084,7 +911,7 @@ impl TextDrawer { match sub_type { Boxed::Single(inst) => { - let label = Self::get_label(inst).unwrap_or(" ".to_string()); + let label = Self::get_label(inst); let left_len = (label.len() - 1) / 2; let right_len = label.len() - left_len - 1; ElementWire { @@ -1108,7 +935,7 @@ impl TextDrawer { } } Boxed::Multi(inst) => { - let label = Self::get_label(inst).unwrap_or(" ".to_string()); + let label = Self::get_label(inst); // get all the indices affected by this multi-box let qargs = circuit .qargs_interner() From 664bae1f933d69a6b44bd975998c6d06cb848144 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Sun, 16 Nov 2025 20:31:33 +0530 Subject: [PATCH 48/70] preliminary changes to structure, added PackedInstruction to control and swap, a function that tells the position of the direct on wire element and changed the code to use this function when deciding to use top and bottom connectors for boxed instructions. --- crates/circuit/src/circuit_drawer.rs | 251 +++++++++++++++++++-------- 1 file changed, 178 insertions(+), 73 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 87eba6529790..59f1da1ee371 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -54,7 +54,7 @@ pub fn draw_circuit( ) -> PyResult { let vis_mat = VisualizationMatrix::from_circuit(circuit, cregbundle)?; - let circuit_rep = TextDrawer::from_visualization_matrix(&vis_mat, &cregbundle); + let circuit_rep = TextDrawer::from_visualization_matrix(&vis_mat, cregbundle); let fold = match fold { Some(f) => f, @@ -173,23 +173,81 @@ enum InputType { Clbit(Option), } -/// Enum for representing elements that can appear directly on a wire. -#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord)] -enum ElementOnWire { +/// Enum for representing elements that appear directly on a wire and how they're connected. +#[derive(Clone, Debug, Copy)] +enum OnWire<'a> { + Control(&'a PackedInstruction), + Swap(&'a PackedInstruction), + Barrier, + Reset, +} + +enum PosOnWire{ Top, Mid, - Bot, + Bot } -/// Enum for representing elements that appear directly on a wire and how they're connected. -#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord)] -enum OnWire { - Control(ElementOnWire), - Swap(ElementOnWire), - Barrier, - Reset, +impl OnWire<'_> { + fn is_pos(&self, circuit: &CircuitData, ind: usize) -> PosOnWire { + match self { + OnWire::Control(inst) + | OnWire::Swap(inst)=> { + let node_qubits = circuit.get_qargs(inst.qubits); + let node_clbits = circuit.get_cargs(inst.clbits); + let num_qubits = circuit.num_qubits(); + let range = get_instruction_range(node_qubits, node_clbits, num_qubits); + if ind == range.0 { + PosOnWire::Top + } else if ind == range.1 { + PosOnWire::Bot + } else { + PosOnWire::Mid + } + }, + OnWire::Reset | OnWire::Barrier => PosOnWire::Mid, + } + } +} + +impl PartialEq for OnWire<'_> { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (OnWire::Control(_), OnWire::Control(_)) => true, + (OnWire::Swap(_), OnWire::Swap(_)) => true, + (OnWire::Barrier, OnWire::Barrier) => true, + (OnWire::Reset, OnWire::Reset) => true, + _ => false, + } + } } +impl Eq for OnWire<'_> {} + +impl PartialOrd for OnWire<'_> { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for OnWire<'_> { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + match (self, other) { + (OnWire::Control(_), OnWire::Control(_)) => std::cmp::Ordering::Equal, + (OnWire::Swap(_), OnWire::Swap(_)) => std::cmp::Ordering::Equal, + (OnWire::Barrier, OnWire::Barrier) => std::cmp::Ordering::Equal, + (OnWire::Reset, OnWire::Reset) => std::cmp::Ordering::Equal, + (OnWire::Control(_), _) => std::cmp::Ordering::Less, + (OnWire::Swap(_), OnWire::Control(_)) => std::cmp::Ordering::Greater, + (OnWire::Swap(_), _) => std::cmp::Ordering::Less, + (OnWire::Barrier, OnWire::Reset) => std::cmp::Ordering::Less, + (OnWire::Barrier, _) => std::cmp::Ordering::Greater, + (OnWire::Reset, _) => std::cmp::Ordering::Greater, + } + } +} + + /// Enum for representing elements that appear in a boxed operation. #[derive(Clone)] enum Boxed<'a> { @@ -251,7 +309,7 @@ enum VisualizationElement<'a>{ Empty, // Marker for no element VerticalLine(InputType), Input(ElementWireInput<'a>), - DirectOnWire(OnWire), + DirectOnWire(OnWire<'a>), Boxed(Boxed<'a>), } @@ -311,18 +369,9 @@ impl<'a> VisualizationLayer<'a> { } } - fn add_controls(&mut self, controls: &HashSet, range: (usize, usize)) { + fn add_controls(&mut self, inst: &'a PackedInstruction, controls: &Vec, range: (usize, usize)) { for control in controls { - if *control == range.0 { - self.0[*control] = - VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Top)); - } else if *control == range.1 { - self.0[*control] = - VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Bot)); - } else { - self.0[*control] = - VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Mid)); - } + self.0[*control] = VisualizationElement::DirectOnWire(OnWire::Control(inst)); } } @@ -399,7 +448,7 @@ impl<'a> VisualizationLayer<'a> { self.0[qargs.last().unwrap().index()] = VisualizationElement::Boxed(Boxed::Single(inst)); let mut control_indices: HashSet = HashSet::new(); if gate.num_ctrl_qubits() > 0 { - self.add_controls( + self.add_controls(inst, &qargs .iter() .take(qargs.len() - 1) @@ -409,6 +458,25 @@ impl<'a> VisualizationLayer<'a> { ); } + let vert_lines = (minima..=maxima) + .filter(|idx| !(qargs.iter().map(|q| q.0 as usize)).contains(idx)); + self.add_vertical_lines(inst, vert_lines); + } + StandardGate::GlobalPhase => {} + StandardGate::Swap | StandardGate::CSwap => { + // taking the last 2 elements of qargs + if gate == StandardGate::CSwap { + let control = vec![qargs[0].0 as usize]; + self.add_controls(inst,&control, (minima, maxima)); + } + let swap_qubits = qargs.iter().map(|q| q.0 as usize).rev().take(2); + for qubit in swap_qubits { + self.0[qubit] = VisualizationElement::DirectOnWire(OnWire::Swap(inst)); + } + + let vert_lines = (minima..=maxima) + .filter(|idx| !(qargs.iter().map(|q| q.0 as usize)).contains(idx)); + self.add_vertical_lines(inst, vert_lines); } _ => unimplemented!("{}", format!("{:?} is not supported yet", gate)) } @@ -441,8 +509,27 @@ impl<'a> VisualizationLayer<'a> { } } StandardInstruction::Measure => { - self.0[qargs.last().unwrap().index()] = VisualizationElement::Boxed(Boxed::Single(inst)); - self.add_vertical_lines(inst, minima + 1..=maxima); + self.0[qargs.last().unwrap().index()] = + VisualizationElement::Boxed(Boxed::Single(inst)); + let maxima = { + if !cregbundle { + maxima + } else { + let shareable_clbit = circuit + .clbits() + .get(circuit.get_cargs(inst.clbits)[0]) + .unwrap(); + let creg = circuit + .cregs() + .iter() + .position(|r| r.contains(shareable_clbit)) + .unwrap(); + circuit.num_qubits() + creg + } + }; + self.add_vertical_lines(inst, minima + 1..maxima); + self.0[maxima] = + VisualizationElement::DirectOnWire(OnWire::Control(inst)) } StandardInstruction::Delay(_) => { for q in qargs { @@ -742,7 +829,7 @@ impl TextDrawer { format!(" {} ", instruction.op.name().to_string()) } - fn from_visualization_matrix<'a>(vis_mat: &'a VisualizationMatrix, cregbundle: &bool) -> Self{ + fn from_visualization_matrix<'a>(vis_mat: &'a VisualizationMatrix, cregbundle: bool) -> Self{ let mut wires: Vec> = vec![]; for _ in 0..vis_mat.num_wires() { wires.push(vec![]); @@ -777,7 +864,7 @@ impl TextDrawer { let mut ct = 0; for layer in &post_processed_vis_mat.layers { - let layer_wires = Self::draw_layer(layer, vis_mat, ct); + let layer_wires = Self::draw_layer(layer, vis_mat, cregbundle,ct); ct += 1; for (i, wire) in layer_wires.iter().enumerate() { text_drawer.wires[i].push(wire.clone()); @@ -798,11 +885,12 @@ impl TextDrawer { fn draw_layer( layer: &VisualizationLayer, vis_mat: &VisualizationMatrix, + cregbundle: bool, layer_ind: usize, ) -> Vec { let mut wires: Vec = vec![]; for (i, element) in layer.0.iter().enumerate() { - let wire = Self::draw_element(element.clone(), layer, vis_mat.circuit, i); + let wire = Self::draw_element(element.clone(), layer, vis_mat.circuit, cregbundle,i); wires.push(wire); } @@ -828,7 +916,6 @@ impl TextDrawer { // wire.pad_wire('$', layer_width); } } - wires } @@ -836,6 +923,7 @@ impl TextDrawer { vis_ele: VisualizationElement, vis_layer: &VisualizationLayer, circuit: &CircuitData, + cregbundle: bool, ind: usize, ) -> ElementWire { match vis_ele { @@ -843,23 +931,29 @@ impl TextDrawer { // implement for cases where the box is on classical wires. The left and right connectors will change // from single wired to double wired. - let top_cases: Vec = vec![ - VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Top)), - VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Top)), - VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Mid)), - VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Mid)), - VisualizationElement::VerticalLine(InputType::Qubit(None)), - VisualizationElement::VerticalLine(InputType::Clbit(None)), - ]; - - let bot_cases: Vec = vec![ - VisualizationElement::DirectOnWire(OnWire::Control(ElementOnWire::Bot)), - VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Bot)), - VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Mid)), - VisualizationElement::DirectOnWire(OnWire::Swap(ElementOnWire::Mid)), - VisualizationElement::VerticalLine(InputType::Qubit(None)), - VisualizationElement::VerticalLine(InputType::Clbit(None)), - ]; + // decide whether the boxed element needs a top connector or not + + let is_top_case = |ve: &VisualizationElement| match ve { + VisualizationElement::VerticalLine(InputType::Qubit(None)) + | VisualizationElement::VerticalLine(InputType::Clbit(None)) => true, + VisualizationElement::DirectOnWire(onwire) => match onwire.is_pos(circuit, ind){ + PosOnWire::Bot => false, + _ => true + }, + _ => false, + }; + + // decide whether the boxed element needs a bottom connector or not + + let is_bot_case = |ve: &VisualizationElement| match ve { + VisualizationElement::VerticalLine(InputType::Qubit(None)) + | VisualizationElement::VerticalLine(InputType::Clbit(None)) => true, + VisualizationElement::DirectOnWire(onwire) => match onwire.is_pos(circuit, ind){ + PosOnWire::Top => false, + _ => true + }, + _ => false, + }; // if subtype is measurement then classical connectors let is_measure = match &sub_type { @@ -879,7 +973,7 @@ impl TextDrawer { let top_con = { if ind >= 1 { - if top_cases.contains(&vis_layer.0[ind - 1]) { + if is_top_case(&vis_layer.0[ind - 1]) { if is_measure { C_WIRE_CON_TOP } else { @@ -894,13 +988,9 @@ impl TextDrawer { }; let bot_con = { - if ind + 1 < vis_layer.0.len() { - if bot_cases.contains(&vis_layer.0[ind + 1]) { - if is_measure { - C_BOT_CON - } else { - BOT_CON - } + if ind + 1 <= vis_layer.0.len() { + if is_bot_case(&vis_layer.0[ind + 1]) { + if is_measure { C_BOT_CON } else { BOT_CON } } else { Q_WIRE } @@ -909,6 +999,8 @@ impl TextDrawer { } }; + println!("ind: {}, len: {}, bot char: {}", ind, vis_layer.0.len(), bot_con); + match sub_type { Boxed::Single(inst) => { let label = Self::get_label(inst); @@ -1078,15 +1170,11 @@ impl TextDrawer { }; let top: String = match on_wire { - OnWire::Control(position) => match position { - ElementOnWire::Top => " ".to_string(), - ElementOnWire::Mid => format!("{}", connecting_wire), - ElementOnWire::Bot => format!("{}", connecting_wire), - }, - OnWire::Swap(position) => match position { - ElementOnWire::Top => " ".to_string(), - ElementOnWire::Mid => format!("{}", connecting_wire), - ElementOnWire::Bot => format!("{}", connecting_wire), + OnWire::Control(position) + | OnWire::Swap(position) => match on_wire.is_pos(circuit, ind) { + PosOnWire::Top => " ".to_string(), + PosOnWire::Mid => format!("{}", connecting_wire), + PosOnWire::Bot => format!("{}", connecting_wire), }, OnWire::Barrier => { format!("{}", BARRIER) @@ -1095,15 +1183,32 @@ impl TextDrawer { }; let bot: String = match on_wire { - OnWire::Control(position) => match position { - ElementOnWire::Top => format!("{}", connecting_wire), - ElementOnWire::Mid => format!("{}", connecting_wire), - ElementOnWire::Bot => " ".to_string(), + OnWire::Control(inst) => match on_wire.is_pos(circuit, ind) { + PosOnWire::Top => format!("{}", connecting_wire), + PosOnWire::Mid => format!("{}", connecting_wire), + PosOnWire::Bot => { + if cregbundle && ind >= circuit.num_qubits() { + // we assume there will only be a single classical bit for now since the current implementation + // only covers measurements as an instruction that operates both on classical and quantum bits. + let clbit = circuit.cargs_interner().get(inst.clbits).first().unwrap(); + // getting shareable clbit + let shareable_clbit = circuit.clbits().get(*clbit).unwrap(); + let bit_info = circuit.clbit_indices().get(shareable_clbit).unwrap(); + let clbit_ind = if let Some((_, index_in_creg)) = bit_info.registers().first() { + format!("{}", index_in_creg) + } else { + format!("{}", ind - circuit.num_qubits()) + }; + format!("{}", clbit_ind) + } else { + " ".to_string() + } + } }, - OnWire::Swap(position) => match position { - ElementOnWire::Top => format!("{}", connecting_wire), - ElementOnWire::Mid => format!("{}", connecting_wire), - ElementOnWire::Bot => " ".to_string(), + OnWire::Swap(_) => match on_wire.is_pos(circuit, ind){ + PosOnWire::Top => format!("{}", connecting_wire), + PosOnWire::Mid => format!("{}", connecting_wire), + PosOnWire::Bot => " ".to_string(), }, OnWire::Barrier => { format!("{}", BARRIER) From d65604973594127898bdffcd116a298c7a1d79c0 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Sun, 16 Nov 2025 23:45:59 +0530 Subject: [PATCH 49/70] bug fix for top and bottom con for boxed elements --- crates/circuit/src/circuit_drawer.rs | 32 ++++++++++++++++------------ 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 59f1da1ee371..06a4d2166072 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -933,24 +933,34 @@ impl TextDrawer { // decide whether the boxed element needs a top connector or not - let is_top_case = |ve: &VisualizationElement| match ve { + let is_top_case = |ve: &VisualizationElement, ind: usize| match ve { VisualizationElement::VerticalLine(InputType::Qubit(None)) | VisualizationElement::VerticalLine(InputType::Clbit(None)) => true, VisualizationElement::DirectOnWire(onwire) => match onwire.is_pos(circuit, ind){ PosOnWire::Bot => false, - _ => true + PosOnWire::Mid => match onwire { + OnWire::Swap(_) + | OnWire::Control(_)=> true, + _ => false, + }, + PosOnWire::Top => true, }, _ => false, }; // decide whether the boxed element needs a bottom connector or not - let is_bot_case = |ve: &VisualizationElement| match ve { + let is_bot_case = |ve: &VisualizationElement, ind: usize| match ve { VisualizationElement::VerticalLine(InputType::Qubit(None)) | VisualizationElement::VerticalLine(InputType::Clbit(None)) => true, VisualizationElement::DirectOnWire(onwire) => match onwire.is_pos(circuit, ind){ PosOnWire::Top => false, - _ => true + PosOnWire::Mid => match onwire { + OnWire::Swap(_) + | OnWire::Control(_)=> true, + _ => false, + }, + PosOnWire::Bot => true, }, _ => false, }; @@ -973,12 +983,8 @@ impl TextDrawer { let top_con = { if ind >= 1 { - if is_top_case(&vis_layer.0[ind - 1]) { - if is_measure { - C_WIRE_CON_TOP - } else { - TOP_CON - } + if is_top_case(&vis_layer.0[ind - 1], ind - 1) { + if is_measure { C_WIRE_CON_TOP } else { TOP_CON } } else { Q_WIRE } @@ -988,8 +994,8 @@ impl TextDrawer { }; let bot_con = { - if ind + 1 <= vis_layer.0.len() { - if is_bot_case(&vis_layer.0[ind + 1]) { + if ind + 1 < vis_layer.0.len() { + if is_bot_case(&vis_layer.0[ind + 1], ind + 1) { if is_measure { C_BOT_CON } else { BOT_CON } } else { Q_WIRE @@ -999,8 +1005,6 @@ impl TextDrawer { } }; - println!("ind: {}, len: {}, bot char: {}", ind, vis_layer.0.len(), bot_con); - match sub_type { Boxed::Single(inst) => { let label = Self::get_label(inst); From 85e2c53f6711ff2d7e32b31f415c23f888883a64 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Mon, 17 Nov 2025 16:43:33 +0530 Subject: [PATCH 50/70] temporary commit to save changes before merging with text_circuit_drawer --- crates/circuit/src/circuit_drawer.rs | 42 +++++++++++++++------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 06a4d2166072..d80b4f3c98d6 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -189,7 +189,7 @@ enum PosOnWire{ } impl OnWire<'_> { - fn is_pos(&self, circuit: &CircuitData, ind: usize) -> PosOnWire { + fn get_position(&self, circuit: &CircuitData, ind: usize) -> PosOnWire { match self { OnWire::Control(inst) | OnWire::Swap(inst)=> { @@ -936,7 +936,7 @@ impl TextDrawer { let is_top_case = |ve: &VisualizationElement, ind: usize| match ve { VisualizationElement::VerticalLine(InputType::Qubit(None)) | VisualizationElement::VerticalLine(InputType::Clbit(None)) => true, - VisualizationElement::DirectOnWire(onwire) => match onwire.is_pos(circuit, ind){ + VisualizationElement::DirectOnWire(onwire) => match onwire.get_position(circuit, ind){ PosOnWire::Bot => false, PosOnWire::Mid => match onwire { OnWire::Swap(_) @@ -953,7 +953,7 @@ impl TextDrawer { let is_bot_case = |ve: &VisualizationElement, ind: usize| match ve { VisualizationElement::VerticalLine(InputType::Qubit(None)) | VisualizationElement::VerticalLine(InputType::Clbit(None)) => true, - VisualizationElement::DirectOnWire(onwire) => match onwire.is_pos(circuit, ind){ + VisualizationElement::DirectOnWire(onwire) => match onwire.get_position(circuit, ind){ PosOnWire::Top => false, PosOnWire::Mid => match onwire { OnWire::Swap(_) @@ -1175,7 +1175,7 @@ impl TextDrawer { let top: String = match on_wire { OnWire::Control(position) - | OnWire::Swap(position) => match on_wire.is_pos(circuit, ind) { + | OnWire::Swap(position) => match on_wire.get_position(circuit, ind) { PosOnWire::Top => " ".to_string(), PosOnWire::Mid => format!("{}", connecting_wire), PosOnWire::Bot => format!("{}", connecting_wire), @@ -1187,29 +1187,31 @@ impl TextDrawer { }; let bot: String = match on_wire { - OnWire::Control(inst) => match on_wire.is_pos(circuit, ind) { + OnWire::Control(inst) => match on_wire.get_position(circuit, ind) { PosOnWire::Top => format!("{}", connecting_wire), PosOnWire::Mid => format!("{}", connecting_wire), PosOnWire::Bot => { + println!("num qubits:{}, index:{}", circuit.num_qubits(), ind); if cregbundle && ind >= circuit.num_qubits() { - // we assume there will only be a single classical bit for now since the current implementation - // only covers measurements as an instruction that operates both on classical and quantum bits. - let clbit = circuit.cargs_interner().get(inst.clbits).first().unwrap(); - // getting shareable clbit - let shareable_clbit = circuit.clbits().get(*clbit).unwrap(); - let bit_info = circuit.clbit_indices().get(shareable_clbit).unwrap(); - let clbit_ind = if let Some((_, index_in_creg)) = bit_info.registers().first() { - format!("{}", index_in_creg) + println!("creg is true, index greater than number of qubits"); + let clbits = circuit.cargs_interner().get(inst.clbits); + let classical_bit = clbits[0]; + let shareable_clbit = circuit.clbits().get(classical_bit).unwrap(); + let bit_register_info = circuit.clbit_indices().get(shareable_clbit).unwrap(); + let index_in_creg = if let Some((_, index)) = bit_register_info.registers().first() { + *index } else { - format!("{}", ind - circuit.num_qubits()) + classical_bit.index() - circuit.num_qubits() }; - format!("{}", clbit_ind) + println!("index_in_creg: {}", index_in_creg); + format!("{}", index_in_creg) } else { + println!("else block being executed"); " ".to_string() } } }, - OnWire::Swap(_) => match on_wire.is_pos(circuit, ind){ + OnWire::Swap(_) => match on_wire.get_position(circuit, ind){ PosOnWire::Top => format!("{}", connecting_wire), PosOnWire::Mid => format!("{}", connecting_wire), PosOnWire::Bot => " ".to_string(), @@ -1225,12 +1227,14 @@ impl TextDrawer { } else { C_WIRE }; - - ElementWire { + println!("num qubits:{}, index:{}", circuit.num_qubits(), ind); + let ret = ElementWire { top: format!("{}{}{}", " ", top, " "), mid: format!("{}{}{}", wire, wire_char, wire), bot: format!("{}{}{}", " ", bot, " "), - } + }; + println!("ElementWire: {:?}", ret); + ret } VisualizationElement::Input(wire_input) => match wire_input { ElementWireInput::Qubit(qubit) => { From 64f4d5b1b23abbce400e2dc7745709a1eaaee1b6 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Mon, 17 Nov 2025 15:13:43 +0200 Subject: [PATCH 51/70] Implement Debug for VisualizationMatrix --- crates/circuit/src/circuit_drawer.rs | 34 +++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index d80b4f3c98d6..8b46d20cfb23 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -611,9 +611,37 @@ impl<'a> Index for VisualizationMatrix<'a> { } } -// [control control boxed boxed] -// [swap swap] -// [control swaps swaps], +impl<'a> Debug for VisualizationMatrix<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for w in 0..self.num_wires() { + for l in 0..self.num_layers() { + let element = &self[l][w]; + let label = match &element { + VisualizationElement::Empty => "~", + VisualizationElement::VerticalLine(input_type) => match input_type { + InputType::Clbit(_) => "║", + InputType::Qubit(_) => "|", + }, + VisualizationElement::Input(input) => match input { + ElementWireInput::Qubit(_) => "QR", + ElementWireInput::Clbit(_) => "CR", + ElementWireInput::Creg(_) => "C/", + }, + VisualizationElement::DirectOnWire(on_wire) => match on_wire { + OnWire::Barrier => "░", + OnWire::Control(_) => "■", + OnWire::Reset => "|0>", + OnWire::Swap(_) => "x", + }, + VisualizationElement::Boxed(_) => "[ ]", + }; + write!(f, "{:^5}", label)?; + } + writeln!(f, "")?; + } + Ok(()) + } +} // better name for the struct #[derive(Clone)] From 8393f7e9730d1fc84c7c18af828330c20a4d6bc5 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Mon, 17 Nov 2025 15:35:31 +0200 Subject: [PATCH 52/70] Simplify VisualizationElement::VerticalLine --- crates/circuit/src/circuit_drawer.rs | 255 ++++++++++++--------------- 1 file changed, 115 insertions(+), 140 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 8b46d20cfb23..dbb81e69b324 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -54,6 +54,7 @@ pub fn draw_circuit( ) -> PyResult { let vis_mat = VisualizationMatrix::from_circuit(circuit, cregbundle)?; + println!("{:?}", vis_mat); let circuit_rep = TextDrawer::from_visualization_matrix(&vis_mat, cregbundle); let fold = match fold { @@ -164,15 +165,6 @@ impl<'a> Debug for ElementWireInput<'a> { } } -/// Input ElementWires. -/// The Option is for optional labels and can be used when the registers have different names. This allows us to use -/// the same enum for both Input and VerticalLine types. -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -enum InputType { - Qubit(Option), - Clbit(Option), -} - /// Enum for representing elements that appear directly on a wire and how they're connected. #[derive(Clone, Debug, Copy)] enum OnWire<'a> { @@ -182,17 +174,16 @@ enum OnWire<'a> { Reset, } -enum PosOnWire{ +enum PosOnWire { Top, Mid, - Bot + Bot, } impl OnWire<'_> { fn get_position(&self, circuit: &CircuitData, ind: usize) -> PosOnWire { match self { - OnWire::Control(inst) - | OnWire::Swap(inst)=> { + OnWire::Control(inst) | OnWire::Swap(inst) => { let node_qubits = circuit.get_qargs(inst.qubits); let node_clbits = circuit.get_cargs(inst.clbits); let num_qubits = circuit.num_qubits(); @@ -204,7 +195,7 @@ impl OnWire<'_> { } else { PosOnWire::Mid } - }, + } OnWire::Reset | OnWire::Barrier => PosOnWire::Mid, } } @@ -247,7 +238,6 @@ impl Ord for OnWire<'_> { } } - /// Enum for representing elements that appear in a boxed operation. #[derive(Clone)] enum Boxed<'a> { @@ -306,8 +296,10 @@ impl<'a> Debug for Boxed<'a> { #[derive(Default, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] enum VisualizationElement<'a>{ #[default] - Empty, // Marker for no element - VerticalLine(InputType), + Empty, + /// Vertical line (e.g for control of measure). + /// The bool indicates whether this is a single (false) or double (true) line + VerticalLine(bool), Input(ElementWireInput<'a>), DirectOnWire(OnWire<'a>), Boxed(Boxed<'a>), @@ -369,29 +361,18 @@ impl<'a> VisualizationLayer<'a> { } } - fn add_controls(&mut self, inst: &'a PackedInstruction, controls: &Vec, range: (usize, usize)) { + fn add_controls(&mut self, inst: &'a PackedInstruction, controls: &Vec) { for control in controls { self.0[*control] = VisualizationElement::DirectOnWire(OnWire::Control(inst)); } } - fn add_vertical_lines(&mut self, inst: &'a PackedInstruction, vertical_lines: I) + fn add_vertical_lines(&mut self, vertical_lines: I, double_line: bool) where I: Iterator, { - let double_lines = vec![StandardInstruction::Measure]; - let input_type: InputType = - if let Some(std_instruction) = inst.op.try_standard_instruction() { - if double_lines.contains(&std_instruction) { - InputType::Clbit(None) - } else { - InputType::Qubit(None) - } - } else { - InputType::Qubit(None) - }; for vline in vertical_lines { - self.0[vline] = VisualizationElement::VerticalLine(input_type.clone()); + self.0[vline] = VisualizationElement::VerticalLine(double_line); } } @@ -448,26 +429,26 @@ impl<'a> VisualizationLayer<'a> { self.0[qargs.last().unwrap().index()] = VisualizationElement::Boxed(Boxed::Single(inst)); let mut control_indices: HashSet = HashSet::new(); if gate.num_ctrl_qubits() > 0 { - self.add_controls(inst, + self.add_controls( + inst, &qargs .iter() .take(qargs.len() - 1) .map(|q| q.index()) .collect(), - (minima, maxima), ); } let vert_lines = (minima..=maxima) .filter(|idx| !(qargs.iter().map(|q| q.0 as usize)).contains(idx)); - self.add_vertical_lines(inst, vert_lines); + self.add_vertical_lines(vert_lines, false); } StandardGate::GlobalPhase => {} StandardGate::Swap | StandardGate::CSwap => { // taking the last 2 elements of qargs if gate == StandardGate::CSwap { let control = vec![qargs[0].0 as usize]; - self.add_controls(inst,&control, (minima, maxima)); + self.add_controls(inst, &control); } let swap_qubits = qargs.iter().map(|q| q.0 as usize).rev().take(2); for qubit in swap_qubits { @@ -476,7 +457,7 @@ impl<'a> VisualizationLayer<'a> { let vert_lines = (minima..=maxima) .filter(|idx| !(qargs.iter().map(|q| q.0 as usize)).contains(idx)); - self.add_vertical_lines(inst, vert_lines); + self.add_vertical_lines(vert_lines, false); } _ => unimplemented!("{}", format!("{:?} is not supported yet", gate)) } @@ -527,9 +508,8 @@ impl<'a> VisualizationLayer<'a> { circuit.num_qubits() + creg } }; - self.add_vertical_lines(inst, minima + 1..maxima); - self.0[maxima] = - VisualizationElement::DirectOnWire(OnWire::Control(inst)) + self.add_vertical_lines(minima + 1..maxima, true); + self.0[maxima] = VisualizationElement::DirectOnWire(OnWire::Control(inst)) } StandardInstruction::Delay(_) => { for q in qargs { @@ -545,7 +525,9 @@ impl<'a> VisualizationLayer<'a> { /// A dense representation of the circuit of size N * (M + 1), where the first /// layer(column) represents the qubits and clbits inputs in the circuits, and /// M is the number of operation layers. -#[derive(Debug, Clone)] +/// +/// This structure follows a column-major order, where each layer represents a column of the circuit, +#[derive(Clone)] struct VisualizationMatrix<'a> { layers: Vec>, circuit: &'a CircuitData, @@ -618,10 +600,8 @@ impl<'a> Debug for VisualizationMatrix<'a> { let element = &self[l][w]; let label = match &element { VisualizationElement::Empty => "~", - VisualizationElement::VerticalLine(input_type) => match input_type { - InputType::Clbit(_) => "║", - InputType::Qubit(_) => "|", - }, + VisualizationElement::VerticalLine(true) => "║", + VisualizationElement::VerticalLine(false) => "|", VisualizationElement::Input(input) => match input { ElementWireInput::Qubit(_) => "QR", ElementWireInput::Clbit(_) => "CR", @@ -659,7 +639,7 @@ impl Debug for ElementWire { impl ElementWire { fn width(&self) -> usize { - // return the max of all strigns + // return the max of all strings // self.top.len().max(self.mid.len()).max(self.bot.len()) let top = self.top.chars().count(); let mid = self.mid.chars().count(); @@ -780,7 +760,9 @@ impl TextDrawer { StandardInstruction::Measure => "M".to_string(), StandardInstruction::Reset => "|0>".to_string(), StandardInstruction::Barrier(_) => "░".to_string(), - StandardInstruction::Delay(delay_unit) => format!("Delay({:?}[{}])", instruction.params, delay_unit), + StandardInstruction::Delay(delay_unit) => { + format!("Delay({:?}[{}])", instruction.params, delay_unit) + } }; } @@ -842,11 +824,14 @@ impl TextDrawer { .to_string(); if standard_gate.num_params() > 0 { - let params = instruction.params_view().iter().map(|param| match param { - Param::Float(f) => format!("{}", f), - _ => format!("{:?}", param), - }) - .join(","); + let params = instruction + .params_view() + .iter() + .map(|param| match param { + Param::Float(f) => format!("{}", f), + _ => format!("{:?}", param), + }) + .join(","); label = format!("{}({})", label, params); } @@ -962,34 +947,34 @@ impl TextDrawer { // decide whether the boxed element needs a top connector or not let is_top_case = |ve: &VisualizationElement, ind: usize| match ve { - VisualizationElement::VerticalLine(InputType::Qubit(None)) - | VisualizationElement::VerticalLine(InputType::Clbit(None)) => true, - VisualizationElement::DirectOnWire(onwire) => match onwire.get_position(circuit, ind){ - PosOnWire::Bot => false, - PosOnWire::Mid => match onwire { - OnWire::Swap(_) - | OnWire::Control(_)=> true, - _ => false, - }, - PosOnWire::Top => true, - }, + VisualizationElement::VerticalLine(_) => true, + VisualizationElement::DirectOnWire(onwire) => { + match onwire.get_position(circuit, ind) { + PosOnWire::Bot => false, + PosOnWire::Mid => match onwire { + OnWire::Swap(_) | OnWire::Control(_) => true, + _ => false, + }, + PosOnWire::Top => true, + } + } _ => false, }; // decide whether the boxed element needs a bottom connector or not let is_bot_case = |ve: &VisualizationElement, ind: usize| match ve { - VisualizationElement::VerticalLine(InputType::Qubit(None)) - | VisualizationElement::VerticalLine(InputType::Clbit(None)) => true, - VisualizationElement::DirectOnWire(onwire) => match onwire.get_position(circuit, ind){ - PosOnWire::Top => false, - PosOnWire::Mid => match onwire { - OnWire::Swap(_) - | OnWire::Control(_)=> true, - _ => false, - }, - PosOnWire::Bot => true, - }, + VisualizationElement::VerticalLine(_) => true, + VisualizationElement::DirectOnWire(onwire) => { + match onwire.get_position(circuit, ind) { + PosOnWire::Top => false, + PosOnWire::Mid => match onwire { + OnWire::Swap(_) | OnWire::Control(_) => true, + _ => false, + }, + PosOnWire::Bot => true, + } + } _ => false, }; @@ -1022,7 +1007,7 @@ impl TextDrawer { }; let bot_con = { - if ind + 1 < vis_layer.0.len() { + if ind + 1 < vis_layer.0.len() { if is_bot_case(&vis_layer.0[ind + 1], ind + 1) { if is_measure { C_BOT_CON } else { BOT_CON } } else { @@ -1202,12 +1187,13 @@ impl TextDrawer { }; let top: String = match on_wire { - OnWire::Control(position) - | OnWire::Swap(position) => match on_wire.get_position(circuit, ind) { - PosOnWire::Top => " ".to_string(), - PosOnWire::Mid => format!("{}", connecting_wire), - PosOnWire::Bot => format!("{}", connecting_wire), - }, + OnWire::Control(position) | OnWire::Swap(position) => { + match on_wire.get_position(circuit, ind) { + PosOnWire::Top => " ".to_string(), + PosOnWire::Mid => format!("{}", connecting_wire), + PosOnWire::Bot => format!("{}", connecting_wire), + } + } OnWire::Barrier => { format!("{}", BARRIER) } @@ -1225,8 +1211,11 @@ impl TextDrawer { let clbits = circuit.cargs_interner().get(inst.clbits); let classical_bit = clbits[0]; let shareable_clbit = circuit.clbits().get(classical_bit).unwrap(); - let bit_register_info = circuit.clbit_indices().get(shareable_clbit).unwrap(); - let index_in_creg = if let Some((_, index)) = bit_register_info.registers().first() { + let bit_register_info = + circuit.clbit_indices().get(shareable_clbit).unwrap(); + let index_in_creg = if let Some((_, index)) = + bit_register_info.registers().first() + { *index } else { classical_bit.index() - circuit.num_qubits() @@ -1239,7 +1228,7 @@ impl TextDrawer { } } }, - OnWire::Swap(_) => match on_wire.get_position(circuit, ind){ + OnWire::Swap(_) => match on_wire.get_position(circuit, ind) { PosOnWire::Top => format!("{}", connecting_wire), PosOnWire::Mid => format!("{}", connecting_wire), PosOnWire::Bot => " ".to_string(), @@ -1264,71 +1253,57 @@ impl TextDrawer { println!("ElementWire: {:?}", ret); ret } - VisualizationElement::Input(wire_input) => match wire_input { - ElementWireInput::Qubit(qubit) => { - let qubit_name = if let Some(bit_info) = circuit.qubit_indices().get(qubit) { - if let Some((register, index)) = bit_info.registers().first() { - format!("{}_{}:", register.name(), index) + VisualizationElement::Input(wire_input) => { + let wire_name = match wire_input { + ElementWireInput::Qubit(qubit) => { + if let Some(bit_info) = circuit.qubit_indices().get(qubit) { + let (register, index) = bit_info + .registers() + .first() + .expect("Register cannot be empty"); + format!("{}_{}: ", register.name(), index) } else { format!("q_{}:", ind) } - } else { - format!("q_{}:", ind) - }; - ElementWire { - top: format!("{}", " ".repeat(qubit_name.len())), - mid: format!("{}", qubit_name), - bot: format!("{}", " ".repeat(qubit_name.len())), } - } - ElementWireInput::Clbit(clbit) => { - let clbit_name = if let Some(bit_info) = circuit.clbit_indices().get(clbit) { - if let Some((register, index)) = bit_info.registers().first() { - format!("{}_{}:", register.name(), index) + ElementWireInput::Clbit(clbit) => { + if let Some(bit_info) = circuit.clbit_indices().get(clbit) { + let (register, index) = bit_info + .registers() + .first() + .expect("Register cannot be empty"); + format!("{}_{}: ", register.name(), index) } else { - format!("c_{}:", ind) - } - } else { - format!("c_{}:", ind) - }; - ElementWire { - top: format!("{}", " ".repeat(clbit_name.len())), - mid: format!("{}", clbit_name), - bot: format!("{}", " ".repeat(clbit_name.len())), - } - } - ElementWireInput::Creg(creg) => { - let wire_name = format!("{}: {}/", creg.name(), creg.len()); - - ElementWire { - top: format!("{}", " ".repeat(wire_name.len())), - mid: format!("{}", wire_name), - bot: format!("{}", " ".repeat(wire_name.len())), - } - } - }, - VisualizationElement::VerticalLine(input_type) => { - let crossed = { - match &input_type { - InputType::Qubit(label) => { - if ind < circuit.num_qubits() { - Q_Q_CROSSED_WIRE - } else { - Q_CL_CROSSED_WIRE - } - } - InputType::Clbit(label) => { - if ind < circuit.num_qubits() { - CL_Q_CROSSED_WIRE - } else { - CL_CL_CROSSED_WIRE - } + format!("q_{}: ", ind) } } + ElementWireInput::Creg(creg) => format!("{}: {}/", creg.name(), creg.len()), }; - let connector = match &input_type { - InputType::Qubit(_) => CONNECTING_WIRE, - InputType::Clbit(_) => CL_CONNECTING_WIRE, + ElementWire { + top: format!("{}", " ".repeat(wire_name.len())), + mid: format!("{}", wire_name), + bot: format!("{}", " ".repeat(wire_name.len())), + } + } + VisualizationElement::VerticalLine(is_double) => { + let (connector, crossed) = if is_double { + ( + if ind < circuit.num_qubits() { + CL_Q_CROSSED_WIRE + } else { + CL_CL_CROSSED_WIRE + }, + CL_CONNECTING_WIRE, + ) + } else { + ( + if ind < circuit.num_qubits() { + Q_Q_CROSSED_WIRE + } else { + Q_CL_CROSSED_WIRE + }, + CONNECTING_WIRE, + ) }; ElementWire { top: format!("{}", connector), From f137da584f84c6931095f8da50f4f506a3f08322 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Mon, 17 Nov 2025 16:01:41 +0200 Subject: [PATCH 53/70] Add get_name to ElementWireInput --- crates/circuit/src/circuit_drawer.rs | 67 +++++++++++++--------------- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index dbb81e69b324..49dd6fbe8a95 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -127,7 +127,7 @@ fn get_instruction_range( } } -#[derive(Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] enum ElementWireInput<'a> { Qubit(&'a ShareableQubit), Clbit(&'a ShareableClbit), @@ -153,15 +153,33 @@ impl Ord for ElementWireInput<'_> { } } -impl<'a> Debug for ElementWireInput<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let name = match self { - ElementWireInput::Qubit(qubit) => "Qubit", - ElementWireInput::Clbit(clbit) => "Clbit", - ElementWireInput::Creg(_) => "Creg" - }; - - write!(f, "{}", name) +impl<'a> ElementWireInput<'a> { + fn get_name(&self, circuit: &CircuitData) -> Option { + match self { + Self::Qubit(qubit) => { + if let Some(bit_info) = circuit.qubit_indices().get(qubit) { + let (register, index) = bit_info + .registers() + .first() + .expect("Register cannot be empty"); + Some(format!("{}_{}: ", register.name(), index)) + } else { + None + } + } + ElementWireInput::Clbit(clbit) => { + if let Some(bit_info) = circuit.clbit_indices().get(clbit) { + let (register, index) = bit_info + .registers() + .first() + .expect("Register cannot be empty"); + Some(format!("{}_{}: ", register.name(), index)) + } else { + None + } + } + ElementWireInput::Creg(creg) => Some(format!("{}: {}/", creg.name(), creg.len())), + } } } @@ -300,13 +318,14 @@ enum VisualizationElement<'a>{ /// Vertical line (e.g for control of measure). /// The bool indicates whether this is a single (false) or double (true) line VerticalLine(bool), + /// Circuit input wires (qubit, clbit, creg) Input(ElementWireInput<'a>), DirectOnWire(OnWire<'a>), Boxed(Boxed<'a>), } /// A representation of a single column (called here a layer) of a visualization matrix -#[derive(Clone, Debug)] +#[derive(Clone)] struct VisualizationLayer<'a>(Vec>); impl<'a> Index for VisualizationLayer<'a> { @@ -1254,31 +1273,7 @@ impl TextDrawer { ret } VisualizationElement::Input(wire_input) => { - let wire_name = match wire_input { - ElementWireInput::Qubit(qubit) => { - if let Some(bit_info) = circuit.qubit_indices().get(qubit) { - let (register, index) = bit_info - .registers() - .first() - .expect("Register cannot be empty"); - format!("{}_{}: ", register.name(), index) - } else { - format!("q_{}:", ind) - } - } - ElementWireInput::Clbit(clbit) => { - if let Some(bit_info) = circuit.clbit_indices().get(clbit) { - let (register, index) = bit_info - .registers() - .first() - .expect("Register cannot be empty"); - format!("{}_{}: ", register.name(), index) - } else { - format!("q_{}: ", ind) - } - } - ElementWireInput::Creg(creg) => format!("{}: {}/", creg.name(), creg.len()), - }; + let wire_name = wire_input.get_name(circuit).unwrap(); ElementWire { top: format!("{}", " ".repeat(wire_name.len())), mid: format!("{}", wire_name), From 33db39fcad6b86919cd873c6e339b5ee1238b537 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Mon, 17 Nov 2025 18:01:01 +0200 Subject: [PATCH 54/70] Simplify DirectOnWire case --- crates/circuit/src/circuit_drawer.rs | 150 +++++++++++---------------- 1 file changed, 62 insertions(+), 88 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 49dd6fbe8a95..8aac7e3792e5 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -314,12 +314,14 @@ impl<'a> Debug for Boxed<'a> { #[derive(Default, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] enum VisualizationElement<'a>{ #[default] + /// A wire element without any associated information. Empty, - /// Vertical line (e.g for control of measure). + /// Vertical line (e.g for control or measure) element. /// The bool indicates whether this is a single (false) or double (true) line VerticalLine(bool), - /// Circuit input wires (qubit, clbit, creg) + /// Circuit input element (qubit, clbit, creg). Input(ElementWireInput<'a>), + /// Element which is drawn without a surrounding box. Used only on qubit wire elements DirectOnWire(OnWire<'a>), Boxed(Boxed<'a>), } @@ -527,8 +529,7 @@ impl<'a> VisualizationLayer<'a> { circuit.num_qubits() + creg } }; - self.add_vertical_lines(minima + 1..maxima, true); - self.0[maxima] = VisualizationElement::DirectOnWire(OnWire::Control(inst)) + self.add_vertical_lines(minima + 1..=maxima, true); } StandardInstruction::Delay(_) => { for q in qargs { @@ -958,6 +959,24 @@ impl TextDrawer { cregbundle: bool, ind: usize, ) -> ElementWire { + let is_start = |ind: usize, inst: &PackedInstruction| { + let (minima, _) = get_instruction_range( + circuit.get_qargs(inst.qubits), + circuit.get_cargs(inst.clbits), + circuit.num_qubits(), + ); + ind == minima + }; + + let is_end = |ind: usize, inst: &PackedInstruction| { + let (_, maxima) = get_instruction_range( + circuit.get_qargs(inst.qubits), + circuit.get_cargs(inst.clbits), + circuit.num_qubits(), + ); + ind == maxima + }; + match vis_ele { VisualizationElement::Boxed(sub_type) => { // implement for cases where the box is on classical wires. The left and right connectors will change @@ -1186,91 +1205,46 @@ impl TextDrawer { } } VisualizationElement::DirectOnWire(on_wire) => { - let connecting_wire = if ind < circuit.num_qubits() { - CONNECTING_WIRE - } else { - CL_CONNECTING_WIRE - }; - - let wire_char: String = match on_wire { - OnWire::Control(position) => { - if ind < circuit.num_qubits() { - BULLET.to_string() + let (top, wire_symbol, bot) = match on_wire { + OnWire::Control(inst) => ( + if is_start(ind, inst) { + "".to_string() } else { - C_WIRE_CON_TOP.to_string() - } - } - OnWire::Swap(position) => "X".to_string(), - OnWire::Barrier => BARRIER.to_string(), - OnWire::Reset => "|0>".to_string(), - }; - - let top: String = match on_wire { - OnWire::Control(position) | OnWire::Swap(position) => { - match on_wire.get_position(circuit, ind) { - PosOnWire::Top => " ".to_string(), - PosOnWire::Mid => format!("{}", connecting_wire), - PosOnWire::Bot => format!("{}", connecting_wire), - } - } - OnWire::Barrier => { - format!("{}", BARRIER) - } - OnWire::Reset => " ".to_string(), - }; - - let bot: String = match on_wire { - OnWire::Control(inst) => match on_wire.get_position(circuit, ind) { - PosOnWire::Top => format!("{}", connecting_wire), - PosOnWire::Mid => format!("{}", connecting_wire), - PosOnWire::Bot => { - println!("num qubits:{}, index:{}", circuit.num_qubits(), ind); - if cregbundle && ind >= circuit.num_qubits() { - println!("creg is true, index greater than number of qubits"); - let clbits = circuit.cargs_interner().get(inst.clbits); - let classical_bit = clbits[0]; - let shareable_clbit = circuit.clbits().get(classical_bit).unwrap(); - let bit_register_info = - circuit.clbit_indices().get(shareable_clbit).unwrap(); - let index_in_creg = if let Some((_, index)) = - bit_register_info.registers().first() - { - *index - } else { - classical_bit.index() - circuit.num_qubits() - }; - println!("index_in_creg: {}", index_in_creg); - format!("{}", index_in_creg) - } else { - println!("else block being executed"); - " ".to_string() - } - } - }, - OnWire::Swap(_) => match on_wire.get_position(circuit, ind) { - PosOnWire::Top => format!("{}", connecting_wire), - PosOnWire::Mid => format!("{}", connecting_wire), - PosOnWire::Bot => " ".to_string(), - }, - OnWire::Barrier => { - format!("{}", BARRIER) - } - OnWire::Reset => " ".to_string(), + CONNECTING_WIRE.to_string() + }, + BULLET.to_string(), + if is_end(ind, inst) { + "".to_string() + } else { + CONNECTING_WIRE.to_string() + }, + ), + OnWire::Swap(inst) => ( + if is_start(ind, inst) { + "".to_string() + } else { + CONNECTING_WIRE.to_string() + }, + "X".to_string(), + if is_end(ind, inst) { + "".to_string() + } else { + CONNECTING_WIRE.to_string() + }, + ), + OnWire::Barrier => ( + BARRIER.to_string(), + BARRIER.to_string(), + BARRIER.to_string(), + ), + OnWire::Reset => (" ".to_string(), "|0>".to_string(), " ".to_string()), }; - let wire = if ind < circuit.num_qubits() { - Q_WIRE - } else { - C_WIRE - }; - println!("num qubits:{}, index:{}", circuit.num_qubits(), ind); - let ret = ElementWire { - top: format!("{}{}{}", " ", top, " "), - mid: format!("{}{}{}", wire, wire_char, wire), - bot: format!("{}{}{}", " ", bot, " "), - }; - println!("ElementWire: {:?}", ret); - ret + ElementWire { + top: format!(" {} ", top), + mid: format!("{}{}{}", Q_WIRE, wire_symbol, Q_WIRE), + bot: format!(" {} ", bot), + } } VisualizationElement::Input(wire_input) => { let wire_name = wire_input.get_name(circuit).unwrap(); @@ -1283,21 +1257,21 @@ impl TextDrawer { VisualizationElement::VerticalLine(is_double) => { let (connector, crossed) = if is_double { ( + CL_CONNECTING_WIRE, if ind < circuit.num_qubits() { CL_Q_CROSSED_WIRE } else { CL_CL_CROSSED_WIRE }, - CL_CONNECTING_WIRE, ) } else { ( + CONNECTING_WIRE, if ind < circuit.num_qubits() { Q_Q_CROSSED_WIRE } else { Q_CL_CROSSED_WIRE }, - CONNECTING_WIRE, ) }; ElementWire { From 87896a98ea67b1e65903aa60668c264247c6dabb Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Tue, 18 Nov 2025 09:44:28 +0200 Subject: [PATCH 55/70] Simplify the Boxed::Single(inst) case --- crates/circuit/src/circuit_drawer.rs | 95 ++++++---------------------- 1 file changed, 19 insertions(+), 76 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 8aac7e3792e5..51a9f6dc29e2 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -981,83 +981,26 @@ impl TextDrawer { VisualizationElement::Boxed(sub_type) => { // implement for cases where the box is on classical wires. The left and right connectors will change // from single wired to double wired. - - // decide whether the boxed element needs a top connector or not - - let is_top_case = |ve: &VisualizationElement, ind: usize| match ve { - VisualizationElement::VerticalLine(_) => true, - VisualizationElement::DirectOnWire(onwire) => { - match onwire.get_position(circuit, ind) { - PosOnWire::Bot => false, - PosOnWire::Mid => match onwire { - OnWire::Swap(_) | OnWire::Control(_) => true, - _ => false, - }, - PosOnWire::Top => true, - } - } - _ => false, - }; - - // decide whether the boxed element needs a bottom connector or not - - let is_bot_case = |ve: &VisualizationElement, ind: usize| match ve { - VisualizationElement::VerticalLine(_) => true, - VisualizationElement::DirectOnWire(onwire) => { - match onwire.get_position(circuit, ind) { - PosOnWire::Top => false, - PosOnWire::Mid => match onwire { - OnWire::Swap(_) | OnWire::Control(_) => true, - _ => false, - }, - PosOnWire::Bot => true, - } - } - _ => false, - }; - - // if subtype is measurement then classical connectors - let is_measure = match &sub_type { + match sub_type { Boxed::Single(inst) => { - if let Some(std_instruction) = inst.op.try_standard_instruction() { - if std_instruction == StandardInstruction::Measure { - true - } else { - false + let mut top_con = Q_WIRE; + let mut bot_con = Q_WIRE; + if let Some(gate) = inst.op.try_standard_gate() { + if gate.is_controlled_gate() { + let qargs = circuit.get_qargs(inst.qubits); + let (minima, maxima) = get_instruction_range(qargs, &[], 0); + if qargs.last().unwrap().index() > minima { + top_con = TOP_CON; + } + if qargs.last().unwrap().index() < maxima { + bot_con = BOT_CON; + } + } + } else if let Some(std_inst) = inst.op.try_standard_instruction() { + if std_inst == StandardInstruction::Measure { + bot_con = C_BOT_CON; } - } else { - false - } - } - _ => false, - }; - - let top_con = { - if ind >= 1 { - if is_top_case(&vis_layer.0[ind - 1], ind - 1) { - if is_measure { C_WIRE_CON_TOP } else { TOP_CON } - } else { - Q_WIRE - } - } else { - Q_WIRE - } - }; - - let bot_con = { - if ind + 1 < vis_layer.0.len() { - if is_bot_case(&vis_layer.0[ind + 1], ind + 1) { - if is_measure { C_BOT_CON } else { BOT_CON } - } else { - Q_WIRE } - } else { - Q_WIRE - } - }; - - match sub_type { - Boxed::Single(inst) => { let label = Self::get_label(inst); let left_len = (label.len() - 1) / 2; let right_len = label.len() - left_len - 1; @@ -1154,7 +1097,7 @@ impl TextDrawer { "{}{}{}{}{}", TOP_LEFT_BOX, Q_WIRE.to_string().repeat(left_len), - top_con, + Q_WIRE, Q_WIRE.to_string().repeat(right_len), TOP_RIGHT_BOX ), @@ -1179,7 +1122,7 @@ impl TextDrawer { "{}{}{}{}{}", BOT_LEFT_BOX, Q_WIRE.to_string().repeat(left_len), - bot_con, + Q_WIRE, Q_WIRE.to_string().repeat(right_len), BOT_RIGHT_BOX ), From 759a4c99e5a644998200d6e8faae322002dd648a Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Tue, 18 Nov 2025 11:03:46 +0200 Subject: [PATCH 56/70] Simplify the Boxed::Multi case --- crates/circuit/src/circuit_drawer.rs | 279 ++++++++------------------- 1 file changed, 82 insertions(+), 197 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 51a9f6dc29e2..468cd4e1a073 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -219,48 +219,10 @@ impl OnWire<'_> { } } -impl PartialEq for OnWire<'_> { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (OnWire::Control(_), OnWire::Control(_)) => true, - (OnWire::Swap(_), OnWire::Swap(_)) => true, - (OnWire::Barrier, OnWire::Barrier) => true, - (OnWire::Reset, OnWire::Reset) => true, - _ => false, - } - } -} - -impl Eq for OnWire<'_> {} - -impl PartialOrd for OnWire<'_> { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for OnWire<'_> { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - match (self, other) { - (OnWire::Control(_), OnWire::Control(_)) => std::cmp::Ordering::Equal, - (OnWire::Swap(_), OnWire::Swap(_)) => std::cmp::Ordering::Equal, - (OnWire::Barrier, OnWire::Barrier) => std::cmp::Ordering::Equal, - (OnWire::Reset, OnWire::Reset) => std::cmp::Ordering::Equal, - (OnWire::Control(_), _) => std::cmp::Ordering::Less, - (OnWire::Swap(_), OnWire::Control(_)) => std::cmp::Ordering::Greater, - (OnWire::Swap(_), _) => std::cmp::Ordering::Less, - (OnWire::Barrier, OnWire::Reset) => std::cmp::Ordering::Less, - (OnWire::Barrier, _) => std::cmp::Ordering::Greater, - (OnWire::Reset, _) => std::cmp::Ordering::Greater, - } - } -} - -/// Enum for representing elements that appear in a boxed operation. +/// Represent elements that appear in a boxed operation. #[derive(Clone)] enum Boxed<'a> { Single(&'a PackedInstruction), - // Multi(MultiBoxElement) Multi(&'a PackedInstruction), } @@ -311,7 +273,7 @@ impl<'a> Debug for Boxed<'a> { /// do not directly implement visualization capabilities, but rather carry enough information /// to enable visualization later on by the actual drawer. -#[derive(Default, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Default, Clone, Debug)] enum VisualizationElement<'a>{ #[default] /// A wire element without any associated information. @@ -321,8 +283,9 @@ enum VisualizationElement<'a>{ VerticalLine(bool), /// Circuit input element (qubit, clbit, creg). Input(ElementWireInput<'a>), - /// Element which is drawn without a surrounding box. Used only on qubit wire elements + /// Element which is drawn without a surrounding box. Used only on qubit wires DirectOnWire(OnWire<'a>), + // Boxed element which can span one or more wires. Used only on qubit wires Boxed(Boxed<'a>), } @@ -426,24 +389,26 @@ impl<'a> VisualizationLayer<'a> { self.0[q.index()] = VisualizationElement::Boxed(Boxed::Multi(inst)); } } - // StandardGate::Swap | StandardGate::CSwap => { - // // qargs[qargs.len() - 1..].iter().map(|q| q.index()).collect() - // // for target in targets { - // // if *target == range.0 { - // // self.0[*target] = VisualizationElement::DirectOnWire(OnWire::Swap( - // // ElementOnWire::Top, - // // )); - // // } else if *target == range.1 { - // // self.0[*target] = VisualizationElement::DirectOnWire(OnWire::Swap( - // // ElementOnWire::Bot, - // // )); - // // } else { - // // self.0[*target] = VisualizationElement::DirectOnWire(OnWire::Swap( - // // ElementOnWire::Mid, - // // )); - // // } - // }, - StandardGate::H | StandardGate::RX | StandardGate::RZ => { + StandardGate::H + | StandardGate::I + | StandardGate::X + | StandardGate::Y + | StandardGate::Z + | StandardGate::Phase + | StandardGate::R + | StandardGate::RX + | StandardGate::RY + | StandardGate::RZ + | StandardGate::S + | StandardGate::Sdg + | StandardGate::SX + | StandardGate::SXdg + | StandardGate::T + | StandardGate::Tdg + | StandardGate::U + | StandardGate::U1 + | StandardGate::U2 + | StandardGate::U3 => { self.0[qargs[0].index()] = VisualizationElement::Boxed(Boxed::Single(inst)); }, StandardGate::CX | StandardGate::CCX => { @@ -771,10 +736,7 @@ impl Index for TextDrawer { impl TextDrawer { fn get_label(instruction: &PackedInstruction) -> String { - if let Some(label) = instruction.label() { - return format!(" {} ", label.to_string()); - } - + if let Some(std_instruction) = instruction.op.try_standard_instruction() { return match std_instruction { StandardInstruction::Measure => "M".to_string(), @@ -785,7 +747,8 @@ impl TextDrawer { } }; } - + + let custom_label = instruction.label(); if let Some(standard_gate) = instruction.op.try_standard_gate() { let mut label = match standard_gate { StandardGate::GlobalPhase => "", @@ -843,6 +806,9 @@ impl TextDrawer { } .to_string(); + if custom_label.is_some() && custom_label.unwrap() != label { + label = custom_label.unwrap().to_string(); + } if standard_gate.num_params() > 0 { let params = instruction .params_view() @@ -862,48 +828,17 @@ impl TextDrawer { format!(" {} ", instruction.op.name().to_string()) } - fn from_visualization_matrix<'a>(vis_mat: &'a VisualizationMatrix, cregbundle: bool) -> Self{ - let mut wires: Vec> = vec![]; - for _ in 0..vis_mat.num_wires() { - wires.push(vec![]); - } - - let mut text_drawer = TextDrawer { wires }; + fn from_visualization_matrix(vis_mat: &VisualizationMatrix, cregbundle: bool) -> Self { + let mut text_drawer = TextDrawer { + wires: vec![Vec::new(); vis_mat.num_wires()], + }; - let post_processed_vis_mat = { - if !cregbundle { - None - } else { - let classical_bits_range = ( - vis_mat.circuit.num_qubits(), - vis_mat.circuit.num_qubits() + vis_mat.circuit.num_clbits(), - ); - let mut temp_vis_mat: VisualizationMatrix<'a> = vis_mat.clone(); - // compare the last classical elements and replace with max in the vector - for layer in temp_vis_mat.layers.iter_mut() { - if let Some(max_element) = layer - .drain(classical_bits_range.0..classical_bits_range.1) - .max() - { - layer.push(max_element.clone()); - } else { - layer.push(VisualizationElement::default()); - } - } - Some(temp_vis_mat) - } - } - .unwrap_or(vis_mat.clone()); - - let mut ct = 0; - for layer in &post_processed_vis_mat.layers { - let layer_wires = Self::draw_layer(layer, vis_mat, cregbundle,ct); - ct += 1; - for (i, wire) in layer_wires.iter().enumerate() { - text_drawer.wires[i].push(wire.clone()); + for (i, layer) in vis_mat.layers.iter().enumerate() { + let layer_wires = Self::draw_layer(layer, vis_mat, cregbundle, i); + for (j, wire) in layer_wires.iter().enumerate() { + text_drawer.wires[j].push(wire.clone()); } } - text_drawer } @@ -1026,49 +961,16 @@ impl TextDrawer { } Boxed::Multi(inst) => { let label = Self::get_label(inst); - // get all the indices affected by this multi-box - let qargs = circuit - .qargs_interner() - .get(inst.qubits) - .into_iter() - .map(|q| q.index()) - .collect_vec(); - let cargs = circuit - .cargs_interner() - .get(inst.clbits) - .into_iter() - .map(|c| c.index() + circuit.num_qubits()) - .collect_vec(); - let minmax = qargs.iter().chain(cargs.iter()).minmax(); - let range = match minmax { - MinMaxResult::MinMax(min, max) => (*min, *max), - MinMaxResult::OneElement(idx) => (*idx, *idx), - MinMaxResult::NoElements => { - panic!("Encountered an multi-qubit without qubits and clbits") - } - }; - let mid = (range.0 + range.1) / 2; - - let num_affected = { - if qargs.contains(&ind) || cargs.contains(&ind) { - // Once the packed instruction can handle custom gates, we need to first check the number of controls - // and then give an index to the qubit in the multibox an index based on whats left. For example, if the - // qubits being affected are [0,1,2,3,4,5] and num controls is 2, then the qubits will be indexed as [C,C,T,T,T,T] - // so for qubits [2,3,4,5] the indexes inside the box will be 0,1,2,3 respectively. - - let temp: String = { - if qargs.contains(&ind) { - let idx = qargs.iter().position(|&x| x == ind).unwrap(); - format!("{:^width$}", idx, width = qargs.len()) - } else { - " ".to_string() - } - }; - temp + let qargs = circuit.get_qargs(inst.qubits); + let (minima, maxima) = get_instruction_range(qargs, &[], 0); + let mid = (minima + maxima) / 2; + + let num_affected = + if let Some(idx) = qargs.iter().position(|&x| x.index() == ind) { + format!("{:^width$}", idx, width = qargs.len()) } else { " ".to_string() - } - }; + }; let mid_section = if ind == mid { format!( @@ -1090,60 +992,43 @@ impl TextDrawer { let left_len = (mid_section.len() - 1) / 2; let right_len = mid_section.len() - left_len - 1; - - if ind == range.0 { - ElementWire { - top: format!( - "{}{}{}{}{}", - TOP_LEFT_BOX, - Q_WIRE.to_string().repeat(left_len), - Q_WIRE, - Q_WIRE.to_string().repeat(right_len), - TOP_RIGHT_BOX - ), - mid: format!("{}{}{}", Q_LEFT_CON, mid_section, Q_RIGHT_CON), - bot: format!( - "{}{}{}", - CONNECTING_WIRE, - " ".repeat(mid_section.len()), - CONNECTING_WIRE - ), - } - } else if ind == range.1 { - ElementWire { - top: format!( - "{}{}{}", - CONNECTING_WIRE, - " ".to_string().repeat(mid_section.len()), - CONNECTING_WIRE - ), - mid: format!("{}{}{}", Q_LEFT_CON, mid_section, Q_RIGHT_CON), - bot: format!( - "{}{}{}{}{}", - BOT_LEFT_BOX, - Q_WIRE.to_string().repeat(left_len), - Q_WIRE, - Q_WIRE.to_string().repeat(right_len), - BOT_RIGHT_BOX - ), - } + let top = if ind == minima { + format!( + "{}{}{}{}{}", + TOP_LEFT_BOX, + Q_WIRE.to_string().repeat(left_len), + Q_WIRE, + Q_WIRE.to_string().repeat(right_len), + TOP_RIGHT_BOX + ) } else { - ElementWire { - top: format!( - "{}{}{}", - CONNECTING_WIRE, - " ".repeat(mid_section.len()), - CONNECTING_WIRE - ), - mid: format!("{}{}{}", Q_LEFT_CON, mid_section, Q_RIGHT_CON), - bot: format!( - "{}{}{}", - CONNECTING_WIRE, - " ".repeat(mid_section.len()), - CONNECTING_WIRE - ), - } - } + format!( + "{}{}{}", + CONNECTING_WIRE, + " ".repeat(mid_section.len()), + CONNECTING_WIRE + ) + }; + let mid = format!("{}{}{}", Q_LEFT_CON, mid_section, Q_RIGHT_CON); + let bot = if ind == maxima { + format!( + "{}{}{}{}{}", + BOT_LEFT_BOX, + Q_WIRE.to_string().repeat(left_len), + Q_WIRE, + Q_WIRE.to_string().repeat(right_len), + BOT_RIGHT_BOX + ) + } else { + format!( + "{}{}{}", + CONNECTING_WIRE, + " ".repeat(mid_section.len()), + CONNECTING_WIRE + ) + }; + + ElementWire { top, mid, bot } } } } From b2212f24b9c024708f3373a25f89d7311564dda9 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Tue, 18 Nov 2025 13:20:35 +0200 Subject: [PATCH 57/70] Fix clippy issues, refactor and clean up a bit --- crates/circuit/src/circuit_drawer.rs | 520 +++++++++++---------------- 1 file changed, 208 insertions(+), 312 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 468cd4e1a073..056e581b8b61 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -15,11 +15,9 @@ use crate::dag_circuit::DAGCircuit; use crate::operations::{Operation, OperationRef, Param, StandardGate, StandardInstruction}; use crate::packed_instruction::PackedInstruction; use crate::{Clbit, Qubit}; -use core::panic; use crossterm::terminal::size; use hashbrown::{HashMap, HashSet}; use itertools::{Itertools, MinMaxResult}; -use rustworkx_core::petgraph::csr::IndexType; use rustworkx_core::petgraph::stable_graph::NodeIndex; use std::cmp::Ordering; use std::fmt::Debug; @@ -34,18 +32,18 @@ use crate::dag_circuit::NodeType; import_exception!(qiskit.circuit.exceptions, CircuitError); -// TODO: remove when dev is done, since this is only for manual testing -#[pyfunction(name = "draw")] -#[pyo3(signature = (circuit, cregbundle=true, mergewires=true, fold=None))] -pub fn py_drawer( - circuit: QuantumCircuitData, - cregbundle: bool, - mergewires: bool, - fold: Option, -) -> PyResult { - draw_circuit(&circuit.data, cregbundle, mergewires, fold) -} +/// Draw the [CircuitData] object as string. +/// +/// # Arguments: +/// +/// * circuit: The CircuitData to draw. +/// * cregbundle: If true, classical bits of classical registers are bundled into one wire. +/// * mergewires: If true, adjacent wires are merged when rendered. +/// * fold: If not None, applies line wrapping using the specified amount. +/// +/// # Returns: +/// The String representation of the circuit. pub fn draw_circuit( circuit: &CircuitData, cregbundle: bool, @@ -54,8 +52,7 @@ pub fn draw_circuit( ) -> PyResult { let vis_mat = VisualizationMatrix::from_circuit(circuit, cregbundle)?; - println!("{:?}", vis_mat); - let circuit_rep = TextDrawer::from_visualization_matrix(&vis_mat, cregbundle); + let text_drawer = TextDrawer::from_visualization_matrix(&vis_mat, cregbundle); let fold = match fold { Some(f) => f, @@ -65,11 +62,10 @@ pub fn draw_circuit( } }; - let output = circuit_rep.print(mergewires, fold); - - Ok(output) + Ok(text_drawer.draw(mergewires, fold)) } + /// Return a list of layers such that each layer contains a list of op node indices, representing instructions /// whose qubits/clbits indices do not overlap. The instruction are packed into each layer as long as there /// is no qubit/clbit overlap. @@ -127,33 +123,14 @@ fn get_instruction_range( } } -#[derive(Debug, Clone, PartialEq, Eq)] -enum ElementWireInput<'a> { +#[derive(Clone, PartialEq, Eq)] +enum WireInputElement<'a> { Qubit(&'a ShareableQubit), Clbit(&'a ShareableClbit), Creg(&'a ClassicalRegister) } -impl PartialOrd for ElementWireInput<'_> { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for ElementWireInput<'_> { - fn cmp(&self, other: &Self) -> Ordering { - match (self, other) { - (ElementWireInput::Qubit(q1), ElementWireInput::Qubit(q2)) => Ordering::Equal, - (ElementWireInput::Clbit(c1), ElementWireInput::Clbit(c2)) => Ordering::Equal, - (ElementWireInput::Qubit(_), ElementWireInput::Clbit(_)) => Ordering::Less, - (ElementWireInput::Clbit(_), ElementWireInput::Qubit(_)) => Ordering::Greater, - (ElementWireInput::Creg(_), _) => Ordering::Greater, - (_, ElementWireInput::Creg(_)) => Ordering::Less, - } - } -} - -impl<'a> ElementWireInput<'a> { +impl WireInputElement<'_> { fn get_name(&self, circuit: &CircuitData) -> Option { match self { Self::Qubit(qubit) => { @@ -167,7 +144,7 @@ impl<'a> ElementWireInput<'a> { None } } - ElementWireInput::Clbit(clbit) => { + WireInputElement::Clbit(clbit) => { if let Some(bit_info) = circuit.clbit_indices().get(clbit) { let (register, index) = bit_info .registers() @@ -178,102 +155,34 @@ impl<'a> ElementWireInput<'a> { None } } - ElementWireInput::Creg(creg) => Some(format!("{}: {}/", creg.name(), creg.len())), + WireInputElement::Creg(creg) => Some(format!("{}: {}/", creg.name(), creg.len())), } } } /// Enum for representing elements that appear directly on a wire and how they're connected. #[derive(Clone, Debug, Copy)] -enum OnWire<'a> { +enum OnWireElement<'a> { Control(&'a PackedInstruction), Swap(&'a PackedInstruction), Barrier, Reset, } -enum PosOnWire { - Top, - Mid, - Bot, -} - -impl OnWire<'_> { - fn get_position(&self, circuit: &CircuitData, ind: usize) -> PosOnWire { - match self { - OnWire::Control(inst) | OnWire::Swap(inst) => { - let node_qubits = circuit.get_qargs(inst.qubits); - let node_clbits = circuit.get_cargs(inst.clbits); - let num_qubits = circuit.num_qubits(); - let range = get_instruction_range(node_qubits, node_clbits, num_qubits); - if ind == range.0 { - PosOnWire::Top - } else if ind == range.1 { - PosOnWire::Bot - } else { - PosOnWire::Mid - } - } - OnWire::Reset | OnWire::Barrier => PosOnWire::Mid, - } - } -} - /// Represent elements that appear in a boxed operation. #[derive(Clone)] -enum Boxed<'a> { +enum BoxedElement<'a> { Single(&'a PackedInstruction), Multi(&'a PackedInstruction), } -// This is a temporary work around for a derive issue -// however the behaviour of this PartialEq is not ideal as it only checks the variant type -// because PackedInstructions cannot be compared directly - -impl PartialOrd for Boxed<'_> { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl PartialEq for Boxed<'_> { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (Boxed::Single(inst1), Boxed::Single(inst2)) => true, - (Boxed::Multi(inst1), Boxed::Multi(inst2)) => true, - _ => false, - } - } -} - -impl Eq for Boxed<'_> {} - -impl Ord for Boxed<'_> { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - match (self, other) { - (Boxed::Single(_), Boxed::Single(_)) => std::cmp::Ordering::Equal, - (Boxed::Multi(_), Boxed::Multi(_)) => std::cmp::Ordering::Equal, - (Boxed::Single(_), Boxed::Multi(_)) => std::cmp::Ordering::Less, - (Boxed::Multi(_), Boxed::Single(_)) => std::cmp::Ordering::Greater, - } - } -} - -impl<'a> Debug for Boxed<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Boxed::Single(inst) => write!(f, "Boxed({})", inst.op.name()), - Boxed::Multi(inst) => write!(f, "MultiBox({})", inst.op.name()), - } - } -} /// Enum for representing the elements stored in a visualization matrix. The elements /// do not directly implement visualization capabilities, but rather carry enough information /// to enable visualization later on by the actual drawer. -#[derive(Default, Clone, Debug)] +#[derive(Default, Clone)] enum VisualizationElement<'a>{ #[default] /// A wire element without any associated information. @@ -282,11 +191,11 @@ enum VisualizationElement<'a>{ /// The bool indicates whether this is a single (false) or double (true) line VerticalLine(bool), /// Circuit input element (qubit, clbit, creg). - Input(ElementWireInput<'a>), + Input(WireInputElement<'a>), /// Element which is drawn without a surrounding box. Used only on qubit wires - DirectOnWire(OnWire<'a>), + DirectOnWire(OnWireElement<'a>), // Boxed element which can span one or more wires. Used only on qubit wires - Boxed(Boxed<'a>), + Boxed(BoxedElement<'a>), } /// A representation of a single column (called here a layer) of a visualization matrix @@ -306,18 +215,7 @@ impl<'a> VisualizationLayer<'a> { self.0.len() } - fn push(&mut self, element: VisualizationElement<'a>) { - self.0.push(element); - } - - fn drain( - &mut self, - range: std::ops::Range, - ) -> impl Iterator> + '_ { - self.0.drain(range) - } - - fn add_input(&mut self, input: ElementWireInput<'a>, idx: usize) { + fn add_input(&mut self, input: WireInputElement<'a>, idx: usize) { self.0[idx] = VisualizationElement::Input(input); } @@ -347,7 +245,7 @@ impl<'a> VisualizationLayer<'a> { fn add_controls(&mut self, inst: &'a PackedInstruction, controls: &Vec) { for control in controls { - self.0[*control] = VisualizationElement::DirectOnWire(OnWire::Control(inst)); + self.0[*control] = VisualizationElement::DirectOnWire(OnWireElement::Control(inst)); } } @@ -367,11 +265,8 @@ impl<'a> VisualizationLayer<'a> { circuit: &CircuitData, ) { let qargs = circuit.get_qargs(inst.qubits); - let (minima, maxima) = get_instruction_range( - qargs, - circuit.get_cargs(inst.clbits), - circuit.num_qubits(), - ); + let (minima, maxima) = + get_instruction_range(qargs, &[], 0); match gate { StandardGate::ISwap @@ -386,7 +281,7 @@ impl<'a> VisualizationLayer<'a> { | StandardGate::RCCX | StandardGate::RC3X => { for q in minima..=maxima { - self.0[q.index()] = VisualizationElement::Boxed(Boxed::Multi(inst)); + self.0[q] = VisualizationElement::Boxed(BoxedElement::Multi(inst)); } } StandardGate::H @@ -409,11 +304,28 @@ impl<'a> VisualizationLayer<'a> { | StandardGate::U1 | StandardGate::U2 | StandardGate::U3 => { - self.0[qargs[0].index()] = VisualizationElement::Boxed(Boxed::Single(inst)); - }, - StandardGate::CX | StandardGate::CCX => { - self.0[qargs.last().unwrap().index()] = VisualizationElement::Boxed(Boxed::Single(inst)); - let mut control_indices: HashSet = HashSet::new(); + self.0[qargs[0].index()] = VisualizationElement::Boxed(BoxedElement::Single(inst)); + } + StandardGate::CH + | StandardGate::CX + | StandardGate::CY + | StandardGate::CZ + | StandardGate::CRX + | StandardGate::CRY + | StandardGate::CRZ + | StandardGate::CS + | StandardGate::CSdg + | StandardGate::CSX + | StandardGate::CU + | StandardGate::CU1 + | StandardGate::CU3 + | StandardGate::CCX + | StandardGate::CCZ + | StandardGate::C3X + | StandardGate::C3SX + | StandardGate::CPhase => { + self.0[qargs.last().unwrap().index()] = + VisualizationElement::Boxed(BoxedElement::Single(inst)); if gate.num_ctrl_qubits() > 0 { self.add_controls( inst, @@ -438,7 +350,7 @@ impl<'a> VisualizationLayer<'a> { } let swap_qubits = qargs.iter().map(|q| q.0 as usize).rev().take(2); for qubit in swap_qubits { - self.0[qubit] = VisualizationElement::DirectOnWire(OnWire::Swap(inst)); + self.0[qubit] = VisualizationElement::DirectOnWire(OnWireElement::Swap(inst)); } let vert_lines = (minima..=maxima) @@ -462,22 +374,22 @@ impl<'a> VisualizationLayer<'a> { ) { let qargs = circuit.get_qargs(inst.qubits); let (minima, maxima) = - get_instruction_range(qargs, circuit.get_cargs(inst.clbits), circuit.num_qubits()); + get_instruction_range(qargs, &[], 0); match std_inst { StandardInstruction::Barrier(_) => { for q in qargs { - self.0[q.index()] = VisualizationElement::DirectOnWire(OnWire::Barrier); + self.0[q.index()] = VisualizationElement::DirectOnWire(OnWireElement::Barrier); } } StandardInstruction::Reset => { for q in qargs { - self.0[q.index()] = VisualizationElement::DirectOnWire(OnWire::Reset); + self.0[q.index()] = VisualizationElement::DirectOnWire(OnWireElement::Reset); } } StandardInstruction::Measure => { self.0[qargs.last().unwrap().index()] = - VisualizationElement::Boxed(Boxed::Single(inst)); + VisualizationElement::Boxed(BoxedElement::Single(inst)); let maxima = { if !cregbundle { maxima @@ -498,7 +410,7 @@ impl<'a> VisualizationLayer<'a> { } StandardInstruction::Delay(_) => { for q in qargs { - self.0[q.index()] = VisualizationElement::Boxed(Boxed::Single(inst)); + self.0[q.index()] = VisualizationElement::Boxed(BoxedElement::Single(inst)); } } } @@ -514,12 +426,14 @@ impl<'a> VisualizationLayer<'a> { /// This structure follows a column-major order, where each layer represents a column of the circuit, #[derive(Clone)] struct VisualizationMatrix<'a> { + /// Layers stored in the matrix. layers: Vec>, + /// A reference to the circuit this matrix was constructed from. circuit: &'a CircuitData, } impl<'a> VisualizationMatrix<'a> { - fn from_circuit(circuit: &'a CircuitData, bundle_cregs: bool) -> PyResult { + fn from_circuit(circuit: &'a CircuitData, bundle_cregs: bool) -> PyResult{ let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; let mut node_index_to_inst: HashMap = @@ -539,13 +453,20 @@ impl<'a> VisualizationMatrix<'a> { let input_layer = layers.first_mut().unwrap(); let mut input_idx = 0; for qubit in circuit.qubits().objects() { - input_layer.add_input(ElementWireInput::Qubit(qubit), input_idx); + input_layer.add_input(WireInputElement::Qubit(qubit), input_idx); input_idx += 1; } - for clbit in circuit.clbits().objects() { - input_layer.add_input(ElementWireInput::Clbit(clbit), input_idx); - input_idx += 1; + if bundle_cregs { + for creg in circuit.cregs() { + input_layer.add_input(WireInputElement::Creg(creg), input_idx); + input_idx += 1; + } + } else { + for clbit in circuit.clbits().objects() { + input_layer.add_input(WireInputElement::Clbit(clbit), input_idx); + input_idx += 1; + } } for (i, layer) in inst_layers.iter().enumerate() { @@ -578,7 +499,7 @@ impl<'a> Index for VisualizationMatrix<'a> { } } -impl<'a> Debug for VisualizationMatrix<'a> { +impl Debug for VisualizationMatrix<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { for w in 0..self.num_wires() { for l in 0..self.num_layers() { @@ -588,21 +509,21 @@ impl<'a> Debug for VisualizationMatrix<'a> { VisualizationElement::VerticalLine(true) => "║", VisualizationElement::VerticalLine(false) => "|", VisualizationElement::Input(input) => match input { - ElementWireInput::Qubit(_) => "QR", - ElementWireInput::Clbit(_) => "CR", - ElementWireInput::Creg(_) => "C/", + WireInputElement::Qubit(_) => "QR", + WireInputElement::Clbit(_) => "CR", + WireInputElement::Creg(_) => "C/", }, VisualizationElement::DirectOnWire(on_wire) => match on_wire { - OnWire::Barrier => "░", - OnWire::Control(_) => "■", - OnWire::Reset => "|0>", - OnWire::Swap(_) => "x", + OnWireElement::Barrier => "░", + OnWireElement::Control(_) => "■", + OnWireElement::Reset => "|0>", + OnWireElement::Swap(_) => "x", }, VisualizationElement::Boxed(_) => "[ ]", }; write!(f, "{:^5}", label)?; } - writeln!(f, "")?; + writeln!(f)?; } Ok(()) } @@ -610,39 +531,32 @@ impl<'a> Debug for VisualizationMatrix<'a> { // better name for the struct #[derive(Clone)] -struct ElementWire { +struct TextWireElement { top: String, mid: String, bot: String, } -impl Debug for ElementWire { +impl Debug for TextWireElement { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}\n{}\n{}", self.top, self.mid, self.bot) } } -impl ElementWire { +impl TextWireElement { fn width(&self) -> usize { // return the max of all strings // self.top.len().max(self.mid.len()).max(self.bot.len()) let top = self.top.chars().count(); let mid = self.mid.chars().count(); let bot = self.bot.chars().count(); - // println!("top:{}, mid:{}, bot:{}", top, mid, bot); - // println!("{}", top.max(mid).max(bot)); - let max = { - if top >= mid && top >= bot { - top - } else if mid >= top && mid >= bot { - mid - } else { - bot - } - }; - // println!("max: {}", max); - // println!("{}\n{}\n{}", self.top, self.mid, self.bot); - max + if top >= mid && top >= bot { + top + } else if mid >= top && mid >= bot { + mid + } else { + bot + } } fn left_pad_string(s: &mut String, pad_char: char, width: usize) { @@ -655,16 +569,6 @@ impl ElementWire { } } - fn right_pad_string(s: &mut String, pad_char: char, width: usize) { - let current_width = s.len(); - if current_width < width { - let pad_size = width - current_width; - let pad_str = pad_char.to_string().repeat(pad_size); - let new_str = format!("{}{}", s, pad_str); - *s = new_str; - } - } - fn pad_string(s: &mut String, pad_char: char, width: usize) { let current_width = s.chars().count(); if current_width < width { @@ -685,7 +589,6 @@ impl ElementWire { Self::left_pad_string(&mut self.mid, mid_char, width); Self::left_pad_string(&mut self.bot, ' ', width); } - //println!("layer width:{}, object width:{} : \n{}\n{}\n{}", width, current_width, self.top, self.mid, self.bot); } fn pad_wire(&mut self, mid_char: char, width: usize) { @@ -695,7 +598,6 @@ impl ElementWire { Self::pad_string(&mut self.mid, mid_char, width); Self::pad_string(&mut self.bot, ' ', width); } - //println!("layer width:{}, object width:{} : \n{}\n{}\n{}", width, current_width, self.top, self.mid, self.bot); } } @@ -722,12 +624,14 @@ pub const Q_CL_CROSSED_WIRE: char = '╪'; pub const CL_CL_CROSSED_WIRE: char = '╬'; pub const CL_Q_CROSSED_WIRE: char = '╫'; +/// Textual representation of the circuit struct TextDrawer { - wires: Vec>, + /// The array of textural wire elements corresponding to the visualization elements + wires: Vec>, } impl Index for TextDrawer { - type Output = Vec; + type Output = Vec; fn index(&self, index: usize) -> &Self::Output { &self.wires[index] @@ -735,8 +639,8 @@ impl Index for TextDrawer { } impl TextDrawer { + fn get_label(instruction: &PackedInstruction) -> String { - if let Some(std_instruction) = instruction.op.try_standard_instruction() { return match std_instruction { StandardInstruction::Measure => "M".to_string(), @@ -747,7 +651,7 @@ impl TextDrawer { } }; } - + let custom_label = instruction.label(); if let Some(standard_gate) = instruction.op.try_standard_gate() { let mut label = match standard_gate { @@ -855,16 +759,15 @@ impl TextDrawer { vis_mat: &VisualizationMatrix, cregbundle: bool, layer_ind: usize, - ) -> Vec { - let mut wires: Vec = vec![]; + ) -> Vec { + let mut wires: Vec = vec![]; for (i, element) in layer.0.iter().enumerate() { - let wire = Self::draw_element(element.clone(), layer, vis_mat.circuit, cregbundle,i); + let wire = Self::draw_element(element.clone(), vis_mat.circuit, cregbundle,i); wires.push(wire); } let num_qubits = vis_mat.circuit.num_qubits(); - //let layer_width = wires.iter().map(|w| w.width()).max().unwrap_or(0); let mut layer_width = 0; for wire in wires.iter() { let w = wire.width(); @@ -878,10 +781,8 @@ impl TextDrawer { wire.pad_wire_left(' ', layer_width); } else if i < num_qubits { wire.pad_wire(Q_WIRE, layer_width); - // wire.pad_wire('$', layer_width); } else { wire.pad_wire(C_WIRE, layer_width); - // wire.pad_wire('$', layer_width); } } wires @@ -889,35 +790,17 @@ impl TextDrawer { pub fn draw_element( vis_ele: VisualizationElement, - vis_layer: &VisualizationLayer, circuit: &CircuitData, cregbundle: bool, ind: usize, - ) -> ElementWire { - let is_start = |ind: usize, inst: &PackedInstruction| { - let (minima, _) = get_instruction_range( - circuit.get_qargs(inst.qubits), - circuit.get_cargs(inst.clbits), - circuit.num_qubits(), - ); - ind == minima - }; - - let is_end = |ind: usize, inst: &PackedInstruction| { - let (_, maxima) = get_instruction_range( - circuit.get_qargs(inst.qubits), - circuit.get_cargs(inst.clbits), - circuit.num_qubits(), - ); - ind == maxima - }; - + ) -> TextWireElement { + let (top, mid, bot); match vis_ele { VisualizationElement::Boxed(sub_type) => { // implement for cases where the box is on classical wires. The left and right connectors will change // from single wired to double wired. match sub_type { - Boxed::Single(inst) => { + BoxedElement::Single(inst) => { let mut top_con = Q_WIRE; let mut bot_con = Q_WIRE; if let Some(gate) = inst.op.try_standard_gate() { @@ -939,31 +822,29 @@ impl TextDrawer { let label = Self::get_label(inst); let left_len = (label.len() - 1) / 2; let right_len = label.len() - left_len - 1; - ElementWire { - top: format!( - "{}{}{}{}{}", - TOP_LEFT_BOX, - Q_WIRE.to_string().repeat(left_len), - top_con, - Q_WIRE.to_string().repeat(right_len), - TOP_RIGHT_BOX - ), - mid: format!("{}{}{}", Q_LEFT_CON, label, Q_RIGHT_CON), - bot: format!( - "{}{}{}{}{}", - BOT_LEFT_BOX, - Q_WIRE.to_string().repeat(left_len), - bot_con, - Q_WIRE.to_string().repeat(right_len), - BOT_RIGHT_BOX - ), - } + top = format!( + "{}{}{}{}{}", + TOP_LEFT_BOX, + Q_WIRE.to_string().repeat(left_len), + top_con, + Q_WIRE.to_string().repeat(right_len), + TOP_RIGHT_BOX + ); + mid = format!("{}{}{}", Q_LEFT_CON, label, Q_RIGHT_CON); + bot = format!( + "{}{}{}{}{}", + BOT_LEFT_BOX, + Q_WIRE.to_string().repeat(left_len), + bot_con, + Q_WIRE.to_string().repeat(right_len), + BOT_RIGHT_BOX + ); } - Boxed::Multi(inst) => { + BoxedElement::Multi(inst) => { let label = Self::get_label(inst); let qargs = circuit.get_qargs(inst.qubits); let (minima, maxima) = get_instruction_range(qargs, &[], 0); - let mid = (minima + maxima) / 2; + let mid_idx = (minima + maxima) / 2; let num_affected = if let Some(idx) = qargs.iter().position(|&x| x.index() == ind) { @@ -972,7 +853,7 @@ impl TextDrawer { " ".to_string() }; - let mid_section = if ind == mid { + let mid_section = if ind == mid_idx { format!( "{:^total_q$} {:^label_len$}", num_affected, @@ -992,7 +873,7 @@ impl TextDrawer { let left_len = (mid_section.len() - 1) / 2; let right_len = mid_section.len() - left_len - 1; - let top = if ind == minima { + top = if ind == minima { format!( "{}{}{}{}{}", TOP_LEFT_BOX, @@ -1009,8 +890,8 @@ impl TextDrawer { CONNECTING_WIRE ) }; - let mid = format!("{}{}{}", Q_LEFT_CON, mid_section, Q_RIGHT_CON); - let bot = if ind == maxima { + mid = format!("{}{}{}", Q_LEFT_CON, mid_section, Q_RIGHT_CON); + bot = if ind == maxima { format!( "{}{}{}{}{}", BOT_LEFT_BOX, @@ -1027,105 +908,109 @@ impl TextDrawer { CONNECTING_WIRE ) }; - - ElementWire { top, mid, bot } } } } VisualizationElement::DirectOnWire(on_wire) => { - let (top, wire_symbol, bot) = match on_wire { - OnWire::Control(inst) => ( - if is_start(ind, inst) { - "".to_string() - } else { - CONNECTING_WIRE.to_string() - }, - BULLET.to_string(), - if is_end(ind, inst) { - "".to_string() - } else { - CONNECTING_WIRE.to_string() - }, - ), - OnWire::Swap(inst) => ( - if is_start(ind, inst) { - "".to_string() - } else { - CONNECTING_WIRE.to_string() - }, - "X".to_string(), - if is_end(ind, inst) { - "".to_string() - } else { - CONNECTING_WIRE.to_string() - }, - ), - OnWire::Barrier => ( + let (wire_top, wire_symbol, wire_bot) = match on_wire { + OnWireElement::Control(inst) => { + let (minima, maxima) = + get_instruction_range(circuit.get_qargs(inst.qubits), &[], 0); + ( + if ind == minima { + "".to_string() + } else { + CONNECTING_WIRE.to_string() + }, + BULLET.to_string(), + if ind == maxima { + "".to_string() + } else { + CONNECTING_WIRE.to_string() + }, + ) + } + OnWireElement::Swap(inst) => { + let (minima, maxima) = + get_instruction_range(circuit.get_qargs(inst.qubits), &[], 0); + ( + if ind == minima { + "".to_string() + } else { + CONNECTING_WIRE.to_string() + }, + "X".to_string(), + if ind == maxima { + "".to_string() + } else { + CONNECTING_WIRE.to_string() + }, + ) + } + OnWireElement::Barrier => ( BARRIER.to_string(), BARRIER.to_string(), BARRIER.to_string(), ), - OnWire::Reset => (" ".to_string(), "|0>".to_string(), " ".to_string()), + OnWireElement::Reset => (" ".to_string(), "|0>".to_string(), " ".to_string()), }; - ElementWire { - top: format!(" {} ", top), - mid: format!("{}{}{}", Q_WIRE, wire_symbol, Q_WIRE), - bot: format!(" {} ", bot), - } + top = format!(" {} ", wire_top); + mid = format!("{}{}{}", Q_WIRE, wire_symbol, Q_WIRE); + bot = format!(" {} ", wire_bot); } - VisualizationElement::Input(wire_input) => { - let wire_name = wire_input.get_name(circuit).unwrap(); - ElementWire { - top: format!("{}", " ".repeat(wire_name.len())), - mid: format!("{}", wire_name), - bot: format!("{}", " ".repeat(wire_name.len())), - } + VisualizationElement::Input(input) => { + let input_name = input.get_name(circuit).unwrap_or_else(|| match input { + WireInputElement::Clbit(_) => format!("q_{}: ", ind), + WireInputElement::Qubit(_) => format!("c_{}: ", ind), + WireInputElement::Creg(_) => unreachable!(), + }); + top = " ".repeat(input_name.len()); + bot = " ".repeat(input_name.len()); + mid = input_name; } VisualizationElement::VerticalLine(is_double) => { - let (connector, crossed) = if is_double { - ( - CL_CONNECTING_WIRE, + if is_double { + top = CL_CONNECTING_WIRE.to_string(); + bot = CL_CONNECTING_WIRE.to_string(); + mid = { if ind < circuit.num_qubits() { CL_Q_CROSSED_WIRE } else { CL_CL_CROSSED_WIRE - }, - ) + } + } + .to_string(); } else { - ( - CONNECTING_WIRE, + top = CONNECTING_WIRE.to_string(); + bot = CONNECTING_WIRE.to_string(); + mid = { if ind < circuit.num_qubits() { Q_Q_CROSSED_WIRE } else { Q_CL_CROSSED_WIRE - }, - ) - }; - ElementWire { - top: format!("{}", connector), - mid: format!("{}", crossed), - bot: format!("{}", connector), + } + } + .to_string(); } } VisualizationElement::Empty => { - let wire = { + top = " ".to_string(); + bot = " ".to_string(); + mid = { if ind < circuit.num_qubits() { Q_WIRE } else { C_WIRE } - }; - ElementWire { - top: format!(" "), - mid: format!("{}", wire), - bot: format!(" "), } + .to_string(); } - } + }; + TextWireElement { top, mid, bot } } - fn print(&self, mergewires: bool, fold: usize) -> String { + fn draw(&self, mergewires: bool, fold: usize) -> String { let ranges: Vec<(usize, usize)> = { let mut temp_ranges = vec![]; let mut layer_counter: usize = 1; @@ -1148,7 +1033,7 @@ impl TextDrawer { for (j, (start, end)) in ranges.iter().enumerate() { if !mergewires { - for (i, element) in self.wires.iter().enumerate() { + for element in self.wires.iter() { let mut top_line: String = element .iter() .enumerate() @@ -1216,7 +1101,7 @@ impl TextDrawer { .join(""); top_line_next.push_str(if j == ranges.len() - 1 { "" } else { "»" }); - let mut merged_line = Self::merge_lines(&bot_line, &top_line_next, "top"); + let merged_line = Self::merge_lines(&bot_line, &top_line_next, "top"); output.push_str(&format!("{}\n", merged_line)); let mut mid_line_next = self.wires[i + 1] .iter() @@ -1294,3 +1179,14 @@ impl TextDrawer { ret } } + +#[pyfunction(name = "draw")] +#[pyo3(signature = (circuit, cregbundle=true, mergewires=true, fold=None))] +pub fn py_drawer( + circuit: QuantumCircuitData, + cregbundle: bool, + mergewires: bool, + fold: Option, +) -> PyResult { + draw_circuit(&circuit.data, cregbundle, mergewires, fold) +} From b9c5ff5683a01e791299d04aff685f53c9134739 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Tue, 18 Nov 2025 13:49:30 +0200 Subject: [PATCH 58/70] Propagate "top" to `merge_wires` --- crates/circuit/src/circuit_drawer.rs | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 056e581b8b61..8ad032cbec12 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -1101,7 +1101,7 @@ impl TextDrawer { .join(""); top_line_next.push_str(if j == ranges.len() - 1 { "" } else { "»" }); - let merged_line = Self::merge_lines(&bot_line, &top_line_next, "top"); + let merged_line = Self::merge_lines(&bot_line, &top_line_next); output.push_str(&format!("{}\n", merged_line)); let mut mid_line_next = self.wires[i + 1] .iter() @@ -1127,7 +1127,7 @@ impl TextDrawer { output } - pub fn merge_lines(top: &str, bot: &str, icod: &str) -> String { + pub fn merge_lines(top: &str, bot: &str) -> String { let mut ret = String::new(); for (topc, botc) in top.chars().zip(bot.chars()) { @@ -1137,22 +1137,16 @@ impl TextDrawer { ret.push('│'); } else if topc == ' ' { ret.push(botc); - } else if "┬╥".contains(topc) && " ║│".contains(botc) && icod == "top" { + } else if "┬╥".contains(topc) && " ║│".contains(botc) { ret.push(topc); - } else if topc == '┬' && botc == ' ' && icod == "bot" { - ret.push('│'); - } else if topc == '╥' && botc == ' ' && icod == "bot" { - ret.push('║'); } else if "┬│".contains(topc) && botc == '═' { ret.push('╪'); } else if "┬│".contains(topc) && botc == '─' { ret.push('┼'); - } else if "└┘║│░".contains(topc) && botc == ' ' && icod == "top" { + } else if "└┘║│░".contains(topc) && botc == ' ' { ret.push(topc); - } else if "─═".contains(topc) && botc == ' ' && icod == "top" { + } else if "─═".contains(topc) && botc == ' ' { ret.push(topc); - } else if "─═".contains(topc) && botc == ' ' && icod == "bot" { - ret.push(botc); } else if "║╥".contains(topc) && botc == '═' { ret.push('╬'); } else if "║╥".contains(topc) && botc == '─' { @@ -1161,15 +1155,15 @@ impl TextDrawer { ret.push('║'); } else if "│┼╪".contains(topc) && botc == ' ' { ret.push('│'); - } else if topc == '└' && botc == '┌' && icod == "top" { + } else if topc == '└' && botc == '┌' { ret.push('├'); - } else if topc == '┘' && botc == '┐' && icod == "top" { + } else if topc == '┘' && botc == '┐' { ret.push('┤'); - } else if "┐┌".contains(botc) && icod == "top" { + } else if "┐┌".contains(botc) { ret.push('┬'); - } else if "┘└".contains(topc) && botc == '─' && icod == "top" { + } else if "┘└".contains(topc) && botc == '─' { ret.push('┴'); - } else if botc == ' ' && icod == "top" { + } else if botc == ' ' { ret.push(topc); } else { ret.push(botc); From d55dd973151b78716f1b4d18144ecb1860ab1673 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Tue, 18 Nov 2025 15:18:53 +0200 Subject: [PATCH 59/70] Apply miscellaneous fixes * Change the VisualizationElement::VerticalLine to hold a PackedInstruction reference * Fix capitalization of labels * Fix clippy * Correct drawing vertical line of a measure --- crates/circuit/src/circuit_drawer.rs | 120 ++++++++++++++------------- 1 file changed, 63 insertions(+), 57 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 8ad032cbec12..aca5e874c601 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -1,6 +1,6 @@ // This code is part of Qiskit. // -// (C) Copyright IBM 2023, 2024 +// (C) Copyright IBM 2025 // // This code is licensed under the Apache License, Version 2.0. You may // obtain a copy of this license in the LICENSE.txt file in the root directory @@ -15,6 +15,9 @@ use crate::dag_circuit::DAGCircuit; use crate::operations::{Operation, OperationRef, Param, StandardGate, StandardInstruction}; use crate::packed_instruction::PackedInstruction; use crate::{Clbit, Qubit}; +use crate::circuit_data::CircuitData; +use crate::converters::QuantumCircuitData; +use crate::dag_circuit::NodeType; use crossterm::terminal::size; use hashbrown::{HashMap, HashSet}; use itertools::{Itertools, MinMaxResult}; @@ -22,28 +25,21 @@ use rustworkx_core::petgraph::stable_graph::NodeIndex; use std::cmp::Ordering; use std::fmt::Debug; use std::ops::Index; - -use pyo3::import_exception; use pyo3::prelude::*; -use crate::circuit_data::CircuitData; -use crate::converters::QuantumCircuitData; -use crate::dag_circuit::NodeType; - -import_exception!(qiskit.circuit.exceptions, CircuitError); - /// Draw the [CircuitData] object as string. -/// +/// /// # Arguments: -/// +/// /// * circuit: The CircuitData to draw. /// * cregbundle: If true, classical bits of classical registers are bundled into one wire. -/// * mergewires: If true, adjacent wires are merged when rendered. -/// * fold: If not None, applies line wrapping using the specified amount. -/// +/// * mergewires: If true, adjacent wires are merged when rendered. +/// * fold: If not None, applies line wrapping using the specified amount. +/// /// # Returns: -/// The String representation of the circuit. +/// +/// The String representation of the circuit. pub fn draw_circuit( circuit: &CircuitData, cregbundle: bool, @@ -65,7 +61,6 @@ pub fn draw_circuit( Ok(text_drawer.draw(mergewires, fold)) } - /// Return a list of layers such that each layer contains a list of op node indices, representing instructions /// whose qubits/clbits indices do not overlap. The instruction are packed into each layer as long as there /// is no qubit/clbit overlap. @@ -187,14 +182,13 @@ enum VisualizationElement<'a>{ #[default] /// A wire element without any associated information. Empty, - /// Vertical line (e.g for control or measure) element. - /// The bool indicates whether this is a single (false) or double (true) line - VerticalLine(bool), - /// Circuit input element (qubit, clbit, creg). + /// A Vertical line element, belonging to an instruction (e.g of a controlled gate or a measure). + VerticalLine(&'a PackedInstruction), + /// A circuit input element (qubit, clbit, creg). Input(WireInputElement<'a>), - /// Element which is drawn without a surrounding box. Used only on qubit wires + /// An element which is drawn without a surrounding box. Used only on qubit wires. DirectOnWire(OnWireElement<'a>), - // Boxed element which can span one or more wires. Used only on qubit wires + // A boxed element which can span one or more wires. Used only on qubit wires. Boxed(BoxedElement<'a>), } @@ -249,12 +243,12 @@ impl<'a> VisualizationLayer<'a> { } } - fn add_vertical_lines(&mut self, vertical_lines: I, double_line: bool) + fn add_vertical_lines(&mut self, vertical_lines: I, inst: &'a PackedInstruction) where I: Iterator, { for vline in vertical_lines { - self.0[vline] = VisualizationElement::VerticalLine(double_line); + self.0[vline] = VisualizationElement::VerticalLine(inst); } } @@ -265,8 +259,7 @@ impl<'a> VisualizationLayer<'a> { circuit: &CircuitData, ) { let qargs = circuit.get_qargs(inst.qubits); - let (minima, maxima) = - get_instruction_range(qargs, &[], 0); + let (minima, maxima) = get_instruction_range(qargs, &[], 0); match gate { StandardGate::ISwap @@ -339,7 +332,7 @@ impl<'a> VisualizationLayer<'a> { let vert_lines = (minima..=maxima) .filter(|idx| !(qargs.iter().map(|q| q.0 as usize)).contains(idx)); - self.add_vertical_lines(vert_lines, false); + self.add_vertical_lines(vert_lines, inst); } StandardGate::GlobalPhase => {} StandardGate::Swap | StandardGate::CSwap => { @@ -355,7 +348,7 @@ impl<'a> VisualizationLayer<'a> { let vert_lines = (minima..=maxima) .filter(|idx| !(qargs.iter().map(|q| q.0 as usize)).contains(idx)); - self.add_vertical_lines(vert_lines, false); + self.add_vertical_lines(vert_lines, inst); } _ => unimplemented!("{}", format!("{:?} is not supported yet", gate)) } @@ -374,7 +367,7 @@ impl<'a> VisualizationLayer<'a> { ) { let qargs = circuit.get_qargs(inst.qubits); let (minima, maxima) = - get_instruction_range(qargs, &[], 0); + get_instruction_range(qargs, circuit.get_cargs(inst.clbits), circuit.num_qubits()); match std_inst { StandardInstruction::Barrier(_) => { @@ -406,7 +399,7 @@ impl<'a> VisualizationLayer<'a> { circuit.num_qubits() + creg } }; - self.add_vertical_lines(minima + 1..=maxima, true); + self.add_vertical_lines(minima + 1..=maxima, inst); } StandardInstruction::Delay(_) => { for q in qargs { @@ -433,7 +426,7 @@ struct VisualizationMatrix<'a> { } impl<'a> VisualizationMatrix<'a> { - fn from_circuit(circuit: &'a CircuitData, bundle_cregs: bool) -> PyResult{ + fn from_circuit(circuit: &'a CircuitData, bundle_cregs: bool) -> PyResult { let dag = DAGCircuit::from_circuit_data(circuit, false, None, None, None, None)?; let mut node_index_to_inst: HashMap = @@ -506,8 +499,15 @@ impl Debug for VisualizationMatrix<'_> { let element = &self[l][w]; let label = match &element { VisualizationElement::Empty => "~", - VisualizationElement::VerticalLine(true) => "║", - VisualizationElement::VerticalLine(false) => "|", + VisualizationElement::VerticalLine(inst) => { + let mut line = "|"; + if let Some(std_inst) = inst.op.try_standard_instruction() { + if std_inst == StandardInstruction::Measure { + line = "║"; + } + } + line + } VisualizationElement::Input(input) => match input { WireInputElement::Qubit(_) => "QR", WireInputElement::Clbit(_) => "CR", @@ -663,9 +663,9 @@ impl TextDrawer { StandardGate::Z => "Z", StandardGate::Phase => "P", StandardGate::R => "R", - StandardGate::RX => "RX", - StandardGate::RY => "RY", - StandardGate::RZ => "RZ", + StandardGate::RX => "Rx", + StandardGate::RY => "Ry", + StandardGate::RZ => "Rz", StandardGate::S => "S", StandardGate::Sdg => "Sdg", StandardGate::SX => "√X", @@ -680,33 +680,33 @@ impl TextDrawer { StandardGate::CX => "X", StandardGate::CY => "Y", StandardGate::CZ => "Z", - StandardGate::DCX => "DCX", - StandardGate::ECR => "ECR", + StandardGate::DCX => "Dcx", + StandardGate::ECR => "Ecr", StandardGate::Swap => "", StandardGate::ISwap => "Iswap", StandardGate::CPhase => "P", - StandardGate::CRX => "RX", - StandardGate::CRY => "RY", - StandardGate::CRZ => "RZ", + StandardGate::CRX => "Rx", + StandardGate::CRY => "Rt", + StandardGate::CRZ => "Rz", StandardGate::CS => "S", StandardGate::CSdg => "Sdg", StandardGate::CSX => "√X", StandardGate::CU => "U", StandardGate::CU1 => "U1", StandardGate::CU3 => "U3", - StandardGate::RXX => "RXX", - StandardGate::RYY => "RYY", - StandardGate::RZZ => "RZZ", - StandardGate::RZX => "RZX", + StandardGate::RXX => "Rxx", + StandardGate::RYY => "Ryy", + StandardGate::RZZ => "Rzz", + StandardGate::RZX => "Rzx", StandardGate::XXMinusYY => "XX-YY", StandardGate::XXPlusYY => "XX+YY", StandardGate::CCX => "X", StandardGate::CCZ => "Z", StandardGate::CSwap => "", - StandardGate::RCCX => "RCCX", + StandardGate::RCCX => "Rccx", StandardGate::C3X => "X", StandardGate::C3SX => "√X", - StandardGate::RC3X => "RC3X", + StandardGate::RC3X => "Rcccx", } .to_string(); @@ -918,13 +918,13 @@ impl TextDrawer { get_instruction_range(circuit.get_qargs(inst.qubits), &[], 0); ( if ind == minima { - "".to_string() + " ".to_string() } else { CONNECTING_WIRE.to_string() }, BULLET.to_string(), if ind == maxima { - "".to_string() + " ".to_string() } else { CONNECTING_WIRE.to_string() }, @@ -935,13 +935,13 @@ impl TextDrawer { get_instruction_range(circuit.get_qargs(inst.qubits), &[], 0); ( if ind == minima { - "".to_string() + " ".to_string() } else { CONNECTING_WIRE.to_string() }, "X".to_string(), if ind == maxima { - "".to_string() + " ".to_string() } else { CONNECTING_WIRE.to_string() }, @@ -952,7 +952,9 @@ impl TextDrawer { BARRIER.to_string(), BARRIER.to_string(), ), - OnWireElement::Reset => (" ".to_string(), "|0>".to_string(), " ".to_string()), + OnWireElement::Reset => { + (" ".to_string(), "|0>".to_string(), " ".to_string()) + } }; top = format!(" {} ", wire_top); @@ -969,8 +971,14 @@ impl TextDrawer { bot = " ".repeat(input_name.len()); mid = input_name; } - VisualizationElement::VerticalLine(is_double) => { - if is_double { + VisualizationElement::VerticalLine(inst) => { + let is_double_line = if let Some(std_inst) = inst.op.try_standard_instruction() { + std_inst == StandardInstruction::Measure + } else { + false + }; + + if is_double_line { top = CL_CONNECTING_WIRE.to_string(); bot = CL_CONNECTING_WIRE.to_string(); mid = { @@ -1143,9 +1151,7 @@ impl TextDrawer { ret.push('╪'); } else if "┬│".contains(topc) && botc == '─' { ret.push('┼'); - } else if "└┘║│░".contains(topc) && botc == ' ' { - ret.push(topc); - } else if "─═".contains(topc) && botc == ' ' { + } else if "└┘║│░─═".contains(topc) && botc == ' ' { ret.push(topc); } else if "║╥".contains(topc) && botc == '═' { ret.push('╬'); From d3c2c37db82e7548883dbc3935c27ca138269a94 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Tue, 18 Nov 2025 19:08:19 +0200 Subject: [PATCH 60/70] Fix minor visualization issues This commit fixes several issues discovered via manual testing. Specifically: * Single bit wires - avoid printing `_1` for bits of length 1. For example `my_qr` instead of `my_qr_1` if `my_qr` is of length 1 * Changed some standard gate names to match those in the Python drawer * Count number of chars in label to account for Unicode chars properly when computing label length. --- crates/circuit/src/circuit_drawer.rs | 31 +++++++++++++++++++--------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index aca5e874c601..f7a39b77f18a 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -134,7 +134,11 @@ impl WireInputElement<'_> { .registers() .first() .expect("Register cannot be empty"); - Some(format!("{}_{}: ", register.name(), index)) + if register.len() > 1 { + Some(format!("{}_{}: ", register.name(), index)) + } else { + Some(format!("{}: ", register.name())) + } } else { None } @@ -145,7 +149,12 @@ impl WireInputElement<'_> { .registers() .first() .expect("Register cannot be empty"); - Some(format!("{}_{}: ", register.name(), index)) + if register.len() > 1 { + Some(format!("{}_{}: ", register.name(), index)) + } + else { + Some(format!("{}: ", register.name())) + } } else { None } @@ -671,7 +680,7 @@ impl TextDrawer { StandardGate::SX => "√X", StandardGate::SXdg => "√Xdg", StandardGate::T => "T", - StandardGate::Tdg => "T†", + StandardGate::Tdg => "Tdg", StandardGate::U => "U", StandardGate::U1 => "U1", StandardGate::U2 => "U2", @@ -686,11 +695,11 @@ impl TextDrawer { StandardGate::ISwap => "Iswap", StandardGate::CPhase => "P", StandardGate::CRX => "Rx", - StandardGate::CRY => "Rt", + StandardGate::CRY => "Ry", StandardGate::CRZ => "Rz", StandardGate::CS => "S", StandardGate::CSdg => "Sdg", - StandardGate::CSX => "√X", + StandardGate::CSX => "Sx", StandardGate::CU => "U", StandardGate::CU1 => "U1", StandardGate::CU3 => "U3", @@ -705,7 +714,7 @@ impl TextDrawer { StandardGate::CSwap => "", StandardGate::RCCX => "Rccx", StandardGate::C3X => "X", - StandardGate::C3SX => "√X", + StandardGate::C3SX => "Sx", StandardGate::RC3X => "Rcccx", } .to_string(); @@ -820,8 +829,9 @@ impl TextDrawer { } } let label = Self::get_label(inst); - let left_len = (label.len() - 1) / 2; - let right_len = label.len() - left_len - 1; + let label_len = label.chars().count(); // To count unicode chars properly (e.g. in √X) + let left_len = (label_len - 1) / 2; + let right_len = label_len - left_len - 1; top = format!( "{}{}{}{}{}", TOP_LEFT_BOX, @@ -842,6 +852,7 @@ impl TextDrawer { } BoxedElement::Multi(inst) => { let label = Self::get_label(inst); + let label_len = label.chars().count(); // To count unicode chars properly (e.g. in √X) let qargs = circuit.get_qargs(inst.qubits); let (minima, maxima) = get_instruction_range(qargs, &[], 0); let mid_idx = (minima + maxima) / 2; @@ -859,7 +870,7 @@ impl TextDrawer { num_affected, label, total_q = qargs.len(), - label_len = label.len() + label_len = label_len ) } else { format!( @@ -867,7 +878,7 @@ impl TextDrawer { num_affected, " ", total_q = qargs.len(), - label_len = label.len() + label_len = label_len ) }; From 59056ece347fe324431eb71c974ea7e8d399777f Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Wed, 19 Nov 2025 16:41:44 +0200 Subject: [PATCH 61/70] Fix bugs related to input wires handling * Changed default names for qubit and clbit wires * Added support for anonymous bits * Fixed a bug with mapping clbit indices to `VisualizationMatrix` wires when `cregbunde=true` --- crates/circuit/src/circuit_drawer.rs | 126 +++++++++++++++++---------- 1 file changed, 82 insertions(+), 44 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index f7a39b77f18a..4b349a508f0d 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -10,23 +10,22 @@ // copyright notice, and modified files need to carry a notice indicating // that they have been altered from the originals. -use crate::bit::{ShareableClbit, ShareableQubit, ClassicalRegister}; +use crate::bit::{ClassicalRegister, ShareableClbit, ShareableQubit}; +use crate::circuit_data::CircuitData; +use crate::converters::QuantumCircuitData; use crate::dag_circuit::DAGCircuit; +use crate::dag_circuit::NodeType; use crate::operations::{Operation, OperationRef, Param, StandardGate, StandardInstruction}; use crate::packed_instruction::PackedInstruction; use crate::{Clbit, Qubit}; -use crate::circuit_data::CircuitData; -use crate::converters::QuantumCircuitData; -use crate::dag_circuit::NodeType; use crossterm::terminal::size; use hashbrown::{HashMap, HashSet}; use itertools::{Itertools, MinMaxResult}; +use pyo3::prelude::*; use rustworkx_core::petgraph::stable_graph::NodeIndex; use std::cmp::Ordering; use std::fmt::Debug; use std::ops::Index; -use pyo3::prelude::*; - /// Draw the [CircuitData] object as string. /// @@ -38,7 +37,7 @@ use pyo3::prelude::*; /// * fold: If not None, applies line wrapping using the specified amount. /// /// # Returns: -/// +/// /// The String representation of the circuit. pub fn draw_circuit( circuit: &CircuitData, @@ -129,12 +128,16 @@ impl WireInputElement<'_> { fn get_name(&self, circuit: &CircuitData) -> Option { match self { Self::Qubit(qubit) => { - if let Some(bit_info) = circuit.qubit_indices().get(qubit) { + let bit_info = circuit + .qubit_indices() + .get(qubit) + .expect("Bit should have location info"); + if !bit_info.registers().is_empty() { let (register, index) = bit_info .registers() .first() .expect("Register cannot be empty"); - if register.len() > 1 { + if !register.is_empty() { Some(format!("{}_{}: ", register.name(), index)) } else { Some(format!("{}: ", register.name())) @@ -144,15 +147,19 @@ impl WireInputElement<'_> { } } WireInputElement::Clbit(clbit) => { - if let Some(bit_info) = circuit.clbit_indices().get(clbit) { + let bit_info = circuit + .clbit_indices() + .get(clbit) + .expect("Bit should have location info"); + + if !bit_info.registers().is_empty() { let (register, index) = bit_info .registers() .first() .expect("Register cannot be empty"); - if register.len() > 1 { + if !register.is_empty() { Some(format!("{}_{}: ", register.name(), index)) - } - else { + } else { Some(format!("{}: ", register.name())) } } else { @@ -228,13 +235,14 @@ impl<'a> VisualizationLayer<'a> { cregbundle: bool, inst: &'a PackedInstruction, circuit: &CircuitData, + clbit_map: &[usize], ) { match inst.op.view() { OperationRef::StandardGate(gate) => { self.add_standard_gate(gate, inst, circuit); } OperationRef::StandardInstruction(std_inst) => { - self.add_standard_instruction(cregbundle, std_inst, inst, circuit); + self.add_standard_instruction(cregbundle, std_inst, inst, circuit, clbit_map); } _ => unimplemented!( "{}", @@ -373,9 +381,10 @@ impl<'a> VisualizationLayer<'a> { std_inst: StandardInstruction, inst: &'a PackedInstruction, circuit: &CircuitData, + clbit_map: &[usize], ) { let qargs = circuit.get_qargs(inst.qubits); - let (minima, maxima) = + let (minima, mut maxima) = get_instruction_range(qargs, circuit.get_cargs(inst.clbits), circuit.num_qubits()); match std_inst { @@ -392,22 +401,15 @@ impl<'a> VisualizationLayer<'a> { StandardInstruction::Measure => { self.0[qargs.last().unwrap().index()] = VisualizationElement::Boxed(BoxedElement::Single(inst)); - let maxima = { - if !cregbundle { - maxima - } else { - let shareable_clbit = circuit - .clbits() - .get(circuit.get_cargs(inst.clbits)[0]) - .unwrap(); - let creg = circuit - .cregs() - .iter() - .position(|r| r.contains(shareable_clbit)) - .unwrap(); - circuit.num_qubits() + creg - } - }; + + // Some bits may be bundled, so we need to map the Clbit index to the proper wire index + if cregbundle { + maxima = clbit_map[circuit + .get_cargs(inst.clbits) + .first() + .expect("Measure should have a clbit arg") + .index()]; + } self.add_vertical_lines(minima + 1..=maxima, inst); } StandardInstruction::Delay(_) => { @@ -446,7 +448,20 @@ impl<'a> VisualizationMatrix<'a> { let inst_layers = build_layers(&dag); - let num_wires = circuit.num_qubits() + circuit.num_clbits(); + let num_wires = circuit.num_qubits() + + if !bundle_cregs { + circuit.num_clbits() + } else { + // Anonymous bits are not bundled so need to be counted explicitly + circuit.cregs_data().len() + circuit.num_clbits() + - circuit + .cregs_data() + .registers() + .iter() + .map(|r| r.len()) + .sum::() + }; + let mut layers = vec![ VisualizationLayer(vec![VisualizationElement::default(); num_wires]); inst_layers.len() + 1 @@ -459,16 +474,38 @@ impl<'a> VisualizationMatrix<'a> { input_idx += 1; } - if bundle_cregs { - for creg in circuit.cregs() { - input_layer.add_input(WireInputElement::Creg(creg), input_idx); - input_idx += 1; - } - } else { - for clbit in circuit.clbits().objects() { - input_layer.add_input(WireInputElement::Clbit(clbit), input_idx); - input_idx += 1; + // A mapping from instruction's Clbit indices to the visualization matrix wires, to be + // used when mapping clbits to creg bundled bits + let mut clbit_map: Vec = Vec::new(); + let mut visited_cregs: HashSet<&ClassicalRegister> = HashSet::new(); + for clbit in circuit.clbits().objects() { + if bundle_cregs { + let bit_location = circuit + .clbit_indices() + .get(clbit) + .expect("Bit should have bit info"); + if !bit_location.registers().is_empty() { + let creg = &bit_location + .registers() + .first() + .expect("Registers should not be empty") + .0; + + if visited_cregs.contains(creg) { + clbit_map.push(input_idx - 1); + } else { + input_layer.add_input(WireInputElement::Creg(creg), input_idx); + visited_cregs.insert(creg); + clbit_map.push(input_idx); + input_idx += 1; + } + continue; + } } + + input_layer.add_input(WireInputElement::Clbit(clbit), input_idx); + clbit_map.push(input_idx); + input_idx += 1; } for (i, layer) in inst_layers.iter().enumerate() { @@ -477,6 +514,7 @@ impl<'a> VisualizationMatrix<'a> { bundle_cregs, node_index_to_inst.get(node_index).unwrap(), circuit, + &clbit_map, ); } } @@ -974,8 +1012,8 @@ impl TextDrawer { } VisualizationElement::Input(input) => { let input_name = input.get_name(circuit).unwrap_or_else(|| match input { - WireInputElement::Clbit(_) => format!("q_{}: ", ind), - WireInputElement::Qubit(_) => format!("c_{}: ", ind), + WireInputElement::Qubit(_) => format!("q_{}: ", ind), + WireInputElement::Clbit(_) => format!("c_{}: ", ind - circuit.num_qubits()), WireInputElement::Creg(_) => unreachable!(), }); top = " ".repeat(input_name.len()); @@ -1135,7 +1173,7 @@ impl TextDrawer { let last_index = num_wires - 1; let bot_line = self.wires[last_index].iter().map(|wire| wire.bot.clone()).collect::>().join(""); let top_line_next = self.wires[last_index + 1].iter().map(|wire| wire.top.clone()).collect::>().join(""); - let merged_line = Self::merge_lines(&bot_line, &top_line_next, "top"); + let merged_line = Self::merge_lines(&bot_line, &top_line_next); output.push_str(&format!("{}\n", merged_line)); let mid_line_next = self.wires[last_index + 1].iter().map(|wire| wire.mid.clone()).collect::>().join(""); output.push_str(&format!("{}\n", mid_line_next)); From 9837f3a95a6dd54f2181f665c084eb945f6139a9 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Wed, 19 Nov 2025 18:08:05 +0200 Subject: [PATCH 62/70] Add clbit_map as a field in VisualizationMatrix --- crates/circuit/src/circuit_drawer.rs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 4b349a508f0d..257350a76bcc 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -434,6 +434,9 @@ struct VisualizationMatrix<'a> { layers: Vec>, /// A reference to the circuit this matrix was constructed from. circuit: &'a CircuitData, + // A mapping from instruction's Clbit indices to the visualization matrix wires, + // to be used when mapping clbits to creg bundled bits + clbit_map: Vec, } impl<'a> VisualizationMatrix<'a> { @@ -474,10 +477,8 @@ impl<'a> VisualizationMatrix<'a> { input_idx += 1; } - // A mapping from instruction's Clbit indices to the visualization matrix wires, to be - // used when mapping clbits to creg bundled bits - let mut clbit_map: Vec = Vec::new(); let mut visited_cregs: HashSet<&ClassicalRegister> = HashSet::new(); + let mut clbit_map: Vec = Vec::new(); for clbit in circuit.clbits().objects() { if bundle_cregs { let bit_location = circuit @@ -519,7 +520,7 @@ impl<'a> VisualizationMatrix<'a> { } } - Ok(VisualizationMatrix { layers, circuit }) + Ok(VisualizationMatrix { layers, circuit, clbit_map }) } fn num_wires(&self) -> usize { @@ -809,7 +810,7 @@ impl TextDrawer { ) -> Vec { let mut wires: Vec = vec![]; for (i, element) in layer.0.iter().enumerate() { - let wire = Self::draw_element(element.clone(), vis_mat.circuit, cregbundle,i); + let wire = Self::draw_element(&element, vis_mat, i); wires.push(wire); } @@ -836,11 +837,11 @@ impl TextDrawer { } pub fn draw_element( - vis_ele: VisualizationElement, - circuit: &CircuitData, - cregbundle: bool, + vis_ele: &VisualizationElement, + vis_mat: &VisualizationMatrix, ind: usize, ) -> TextWireElement { + let circuit = vis_mat.circuit; let (top, mid, bot); match vis_ele { VisualizationElement::Boxed(sub_type) => { @@ -1027,6 +1028,14 @@ impl TextDrawer { false }; + println!("CLBIT MAP IS {:?}",vis_mat.clbit_map); + if is_double_line { + let clbit = circuit.get_cargs(inst.clbits).first().expect(""); + if vis_mat.clbit_map[clbit.index()] == ind { + println!("BOOOM"); + } + } + if is_double_line { top = CL_CONNECTING_WIRE.to_string(); bot = CL_CONNECTING_WIRE.to_string(); From 8b683c20d646c156ad9ab95a01e048a5a6b71b78 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Wed, 19 Nov 2025 23:31:59 +0530 Subject: [PATCH 63/70] added indexing for classical bits when bundling --- crates/circuit/src/circuit_drawer.rs | 74 ++++++++++++++++++---------- test.py | 31 ++++++++++++ 2 files changed, 80 insertions(+), 25 deletions(-) create mode 100644 test.py diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 257350a76bcc..f166486d480a 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -520,6 +520,8 @@ impl<'a> VisualizationMatrix<'a> { } } + println!("{:?}", clbit_map); + Ok(VisualizationMatrix { layers, circuit, clbit_map }) } @@ -810,7 +812,7 @@ impl TextDrawer { ) -> Vec { let mut wires: Vec = vec![]; for (i, element) in layer.0.iter().enumerate() { - let wire = Self::draw_element(&element, vis_mat, i); + let wire = Self::draw_element(&element, vis_mat, cregbundle,i); wires.push(wire); } @@ -839,6 +841,7 @@ impl TextDrawer { pub fn draw_element( vis_ele: &VisualizationElement, vis_mat: &VisualizationMatrix, + cregbundle: bool, ind: usize, ) -> TextWireElement { let circuit = vis_mat.circuit; @@ -1028,36 +1031,57 @@ impl TextDrawer { false }; - println!("CLBIT MAP IS {:?}",vis_mat.clbit_map); - if is_double_line { - let clbit = circuit.get_cargs(inst.clbits).first().expect(""); - if vis_mat.clbit_map[clbit.index()] == ind { - println!("BOOOM"); - } + if is_double_line{ + let clbit = circuit.get_cargs(inst.clbits); + println!("{},{}", ind, vis_mat.clbit_map[clbit.first().unwrap().index()]); } - - if is_double_line { + let clbit = circuit.get_cargs(inst.clbits); + if ind == vis_mat.clbit_map[clbit.first().unwrap().index()] { top = CL_CONNECTING_WIRE.to_string(); - bot = CL_CONNECTING_WIRE.to_string(); - mid = { - if ind < circuit.num_qubits() { - CL_Q_CROSSED_WIRE + mid = C_WIRE_CON_TOP.to_string(); + + // TO DO, if someone adds > 99 clbits + // the visualisation will have an extra whitespace shift which + // needs to be fixed + + bot = if cregbundle { + let clbits = circuit.cargs_interner().get(inst.clbits); + let classical_bit = clbits[0]; + let shareable_clbit = circuit.clbits().get(classical_bit).unwrap(); + let bit_register_info = circuit.clbit_indices().get(shareable_clbit).unwrap(); + let index_in_creg = if let Some((_, index)) = bit_register_info.registers().first() { + *index } else { - CL_CL_CROSSED_WIRE - } - } - .to_string(); + classical_bit.index() - circuit.num_qubits() + }; + index_in_creg.to_string() + } else { + " ".to_string() + }; } else { - top = CONNECTING_WIRE.to_string(); - bot = CONNECTING_WIRE.to_string(); - mid = { - if ind < circuit.num_qubits() { - Q_Q_CROSSED_WIRE - } else { - Q_CL_CROSSED_WIRE + if is_double_line { + top = CL_CONNECTING_WIRE.to_string(); + bot = CL_CONNECTING_WIRE.to_string(); + mid = { + if ind < circuit.num_qubits() { + CL_Q_CROSSED_WIRE + } else { + CL_CL_CROSSED_WIRE + } + } + .to_string(); + } else { + top = CONNECTING_WIRE.to_string(); + bot = CONNECTING_WIRE.to_string(); + mid = { + if ind < circuit.num_qubits() { + Q_Q_CROSSED_WIRE + } else { + Q_CL_CROSSED_WIRE + } } + .to_string(); } - .to_string(); } } VisualizationElement::Empty => { diff --git a/test.py b/test.py new file mode 100644 index 000000000000..45cac3cb8f02 --- /dev/null +++ b/test.py @@ -0,0 +1,31 @@ +from qiskit.circuit import * +import numpy as np +from qiskit._accelerate.circuit import draw + +# wire for each clbit +# [10, 11, 11, 12, 12] + +# clbit indices +# [0, 1, 2, 3, 4] + +# index being passed +# [12, 12, 11] + +# ind - clbit_map[clbit.first().index()] + +# required output: +# 0, 1, 1 + +creg1 = ClassicalRegister(1, 'c1') +creg2 = ClassicalRegister(2, 'c2') +creg3 = ClassicalRegister(101, 'c3') +qreg1 = QuantumRegister(10, 'q1') +qc = QuantumCircuit(qreg1, creg1, creg2, creg3) +# qc.iswap(qreg1[9], qreg1[8]) +qc.measure(qreg1[0], creg3[0]) +qc.measure(qreg1[1], creg3[99]) +qc.measure(qreg1[2], creg2[1]) +creg = ClassicalRegister(4, 'creg') +qc.add_register(creg) +qc.measure(qreg1[3], creg[0]) +print(draw(qc, cregbundle=True, mergewires =False)) \ No newline at end of file From 8b74972202c8015f6d0c52f4f5c1475b4797add8 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Wed, 19 Nov 2025 23:33:43 +0530 Subject: [PATCH 64/70] cargo fmt --- crates/circuit/src/circuit_drawer.rs | 59 ++++++++++++++++------------ 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index f166486d480a..84b7b81da630 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -434,7 +434,7 @@ struct VisualizationMatrix<'a> { layers: Vec>, /// A reference to the circuit this matrix was constructed from. circuit: &'a CircuitData, - // A mapping from instruction's Clbit indices to the visualization matrix wires, + // A mapping from instruction's Clbit indices to the visualization matrix wires, // to be used when mapping clbits to creg bundled bits clbit_map: Vec, } @@ -522,7 +522,11 @@ impl<'a> VisualizationMatrix<'a> { println!("{:?}", clbit_map); - Ok(VisualizationMatrix { layers, circuit, clbit_map }) + Ok(VisualizationMatrix { + layers, + circuit, + clbit_map, + }) } fn num_wires(&self) -> usize { @@ -689,6 +693,19 @@ impl Index for TextDrawer { } impl TextDrawer { + fn from_visualization_matrix(vis_mat: &VisualizationMatrix, cregbundle: bool) -> Self { + let mut text_drawer = TextDrawer { + wires: vec![Vec::new(); vis_mat.num_wires()], + }; + + for (i, layer) in vis_mat.layers.iter().enumerate() { + let layer_wires = Self::draw_layer(layer, vis_mat, cregbundle, i); + for (j, wire) in layer_wires.iter().enumerate() { + text_drawer.wires[j].push(wire.clone()); + } + } + text_drawer + } fn get_label(instruction: &PackedInstruction) -> String { if let Some(std_instruction) = instruction.op.try_standard_instruction() { @@ -782,20 +799,6 @@ impl TextDrawer { format!(" {} ", instruction.op.name().to_string()) } - fn from_visualization_matrix(vis_mat: &VisualizationMatrix, cregbundle: bool) -> Self { - let mut text_drawer = TextDrawer { - wires: vec![Vec::new(); vis_mat.num_wires()], - }; - - for (i, layer) in vis_mat.layers.iter().enumerate() { - let layer_wires = Self::draw_layer(layer, vis_mat, cregbundle, i); - for (j, wire) in layer_wires.iter().enumerate() { - text_drawer.wires[j].push(wire.clone()); - } - } - text_drawer - } - fn get_layer_width(&self, ind: usize) -> usize { self.wires .iter() @@ -812,7 +815,7 @@ impl TextDrawer { ) -> Vec { let mut wires: Vec = vec![]; for (i, element) in layer.0.iter().enumerate() { - let wire = Self::draw_element(&element, vis_mat, cregbundle,i); + let wire = Self::draw_element(&element, vis_mat, cregbundle, i); wires.push(wire); } @@ -1031,9 +1034,13 @@ impl TextDrawer { false }; - if is_double_line{ + if is_double_line { let clbit = circuit.get_cargs(inst.clbits); - println!("{},{}", ind, vis_mat.clbit_map[clbit.first().unwrap().index()]); + println!( + "{},{}", + ind, + vis_mat.clbit_map[clbit.first().unwrap().index()] + ); } let clbit = circuit.get_cargs(inst.clbits); if ind == vis_mat.clbit_map[clbit.first().unwrap().index()] { @@ -1048,12 +1055,14 @@ impl TextDrawer { let clbits = circuit.cargs_interner().get(inst.clbits); let classical_bit = clbits[0]; let shareable_clbit = circuit.clbits().get(classical_bit).unwrap(); - let bit_register_info = circuit.clbit_indices().get(shareable_clbit).unwrap(); - let index_in_creg = if let Some((_, index)) = bit_register_info.registers().first() { - *index - } else { - classical_bit.index() - circuit.num_qubits() - }; + let bit_register_info = + circuit.clbit_indices().get(shareable_clbit).unwrap(); + let index_in_creg = + if let Some((_, index)) = bit_register_info.registers().first() { + *index + } else { + classical_bit.index() - circuit.num_qubits() + }; index_in_creg.to_string() } else { " ".to_string() From 4a96ffe6e1c7a0586e48bbb4ba39f35e440aaf27 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Wed, 19 Nov 2025 21:32:03 +0200 Subject: [PATCH 65/70] Fix a panic when measuring anonymous clbits with cregbundle --- crates/circuit/src/circuit_drawer.rs | 84 +++++++++++----------------- 1 file changed, 34 insertions(+), 50 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 84b7b81da630..3e1dcdd5eb6d 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -434,8 +434,8 @@ struct VisualizationMatrix<'a> { layers: Vec>, /// A reference to the circuit this matrix was constructed from. circuit: &'a CircuitData, - // A mapping from instruction's Clbit indices to the visualization matrix wires, - // to be used when mapping clbits to creg bundled bits + /// A mapping from instruction's Clbit indices to the visualization matrix wires, + /// to be used when mapping clbits to bit of bundled cregs clbit_map: Vec, } @@ -520,8 +520,6 @@ impl<'a> VisualizationMatrix<'a> { } } - println!("{:?}", clbit_map); - Ok(VisualizationMatrix { layers, circuit, @@ -815,7 +813,7 @@ impl TextDrawer { ) -> Vec { let mut wires: Vec = vec![]; for (i, element) in layer.0.iter().enumerate() { - let wire = Self::draw_element(&element, vis_mat, cregbundle, i); + let wire = Self::draw_element(element, vis_mat, cregbundle, i); wires.push(wire); } @@ -1028,48 +1026,34 @@ impl TextDrawer { mid = input_name; } VisualizationElement::VerticalLine(inst) => { - let is_double_line = if let Some(std_inst) = inst.op.try_standard_instruction() { + let is_measure = if let Some(std_inst) = inst.op.try_standard_instruction() { std_inst == StandardInstruction::Measure } else { false }; - if is_double_line { - let clbit = circuit.get_cargs(inst.clbits); - println!( - "{},{}", - ind, - vis_mat.clbit_map[clbit.first().unwrap().index()] - ); - } - let clbit = circuit.get_cargs(inst.clbits); - if ind == vis_mat.clbit_map[clbit.first().unwrap().index()] { + if is_measure { top = CL_CONNECTING_WIRE.to_string(); - mid = C_WIRE_CON_TOP.to_string(); - - // TO DO, if someone adds > 99 clbits - // the visualisation will have an extra whitespace shift which - // needs to be fixed - - bot = if cregbundle { - let clbits = circuit.cargs_interner().get(inst.clbits); - let classical_bit = clbits[0]; - let shareable_clbit = circuit.clbits().get(classical_bit).unwrap(); - let bit_register_info = - circuit.clbit_indices().get(shareable_clbit).unwrap(); - let index_in_creg = - if let Some((_, index)) = bit_register_info.registers().first() { - *index - } else { - classical_bit.index() - circuit.num_qubits() - }; - index_in_creg.to_string() + + let clbit = circuit.get_cargs(inst.clbits).first().unwrap(); + if ind == vis_mat.clbit_map[clbit.index()] { + mid = C_WIRE_CON_TOP.to_string(); + + let shareable_clbit = circuit.clbits().get(*clbit).unwrap(); + let registers = circuit + .clbit_indices() + .get(shareable_clbit) + .unwrap() + .registers(); + // TODO: if someone adds > 99 clbits + // the visualization will have an extra whitespace shift which + // needs to be fixed + bot = if cregbundle && !registers.is_empty() { + format!("{}", registers.first().unwrap().1) + } else { + " ".to_string() + } } else { - " ".to_string() - }; - } else { - if is_double_line { - top = CL_CONNECTING_WIRE.to_string(); bot = CL_CONNECTING_WIRE.to_string(); mid = { if ind < circuit.num_qubits() { @@ -1079,18 +1063,18 @@ impl TextDrawer { } } .to_string(); - } else { - top = CONNECTING_WIRE.to_string(); - bot = CONNECTING_WIRE.to_string(); - mid = { - if ind < circuit.num_qubits() { - Q_Q_CROSSED_WIRE - } else { - Q_CL_CROSSED_WIRE - } + } + } else { + top = CONNECTING_WIRE.to_string(); + bot = CONNECTING_WIRE.to_string(); + mid = { + if ind < circuit.num_qubits() { + Q_Q_CROSSED_WIRE + } else { + Q_CL_CROSSED_WIRE } - .to_string(); } + .to_string(); } } VisualizationElement::Empty => { From a221c4a202ceab081a08397932e79c209779fdd3 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Wed, 19 Nov 2025 23:58:46 +0200 Subject: [PATCH 66/70] Add unitary gate support --- crates/circuit/src/circuit_drawer.rs | 195 +++++++++++++++------------ 1 file changed, 109 insertions(+), 86 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 3e1dcdd5eb6d..c1acd12d6a3b 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -244,6 +244,9 @@ impl<'a> VisualizationLayer<'a> { OperationRef::StandardInstruction(std_inst) => { self.add_standard_instruction(cregbundle, std_inst, inst, circuit, clbit_map); } + OperationRef::Unitary(_) => { + self.add_unitary_gate(inst, circuit); + } _ => unimplemented!( "{}", format!( @@ -419,6 +422,25 @@ impl<'a> VisualizationLayer<'a> { } } } + + fn add_unitary_gate( + &mut self, + inst: &'a PackedInstruction, + circuit: &CircuitData, + ) { + let qargs = circuit.get_qargs(inst.qubits); + if qargs.len() == 1 { + self.0[qargs.first().unwrap().index()] = VisualizationElement::Boxed(BoxedElement::Single(inst)); + } + else { + let (minima, maxima) = + get_instruction_range(qargs, &[], 0); + + for q in minima..=maxima { + self.0[q] = VisualizationElement::Boxed(BoxedElement::Multi(inst)); + } + } + } } /// A Plain, logical 2D representation of a circuit. @@ -706,95 +728,96 @@ impl TextDrawer { } fn get_label(instruction: &PackedInstruction) -> String { - if let Some(std_instruction) = instruction.op.try_standard_instruction() { - return match std_instruction { - StandardInstruction::Measure => "M".to_string(), - StandardInstruction::Reset => "|0>".to_string(), - StandardInstruction::Barrier(_) => "░".to_string(), - StandardInstruction::Delay(delay_unit) => { - format!("Delay({:?}[{}])", instruction.params, delay_unit) + match instruction.op.view() { + OperationRef::StandardInstruction(std_instruction) => { + match std_instruction { + StandardInstruction::Measure => "M".to_string(), + StandardInstruction::Reset => "|0>".to_string(), + StandardInstruction::Barrier(_) => "░".to_string(), + StandardInstruction::Delay(delay_unit) => + format!("Delay({:?}[{}])", instruction.params, delay_unit), } - }; - } - - let custom_label = instruction.label(); - if let Some(standard_gate) = instruction.op.try_standard_gate() { - let mut label = match standard_gate { - StandardGate::GlobalPhase => "", - StandardGate::H => "H", - StandardGate::I => "I", - StandardGate::X => "X", - StandardGate::Y => "Y", - StandardGate::Z => "Z", - StandardGate::Phase => "P", - StandardGate::R => "R", - StandardGate::RX => "Rx", - StandardGate::RY => "Ry", - StandardGate::RZ => "Rz", - StandardGate::S => "S", - StandardGate::Sdg => "Sdg", - StandardGate::SX => "√X", - StandardGate::SXdg => "√Xdg", - StandardGate::T => "T", - StandardGate::Tdg => "Tdg", - StandardGate::U => "U", - StandardGate::U1 => "U1", - StandardGate::U2 => "U2", - StandardGate::U3 => "U3", - StandardGate::CH => "H", - StandardGate::CX => "X", - StandardGate::CY => "Y", - StandardGate::CZ => "Z", - StandardGate::DCX => "Dcx", - StandardGate::ECR => "Ecr", - StandardGate::Swap => "", - StandardGate::ISwap => "Iswap", - StandardGate::CPhase => "P", - StandardGate::CRX => "Rx", - StandardGate::CRY => "Ry", - StandardGate::CRZ => "Rz", - StandardGate::CS => "S", - StandardGate::CSdg => "Sdg", - StandardGate::CSX => "Sx", - StandardGate::CU => "U", - StandardGate::CU1 => "U1", - StandardGate::CU3 => "U3", - StandardGate::RXX => "Rxx", - StandardGate::RYY => "Ryy", - StandardGate::RZZ => "Rzz", - StandardGate::RZX => "Rzx", - StandardGate::XXMinusYY => "XX-YY", - StandardGate::XXPlusYY => "XX+YY", - StandardGate::CCX => "X", - StandardGate::CCZ => "Z", - StandardGate::CSwap => "", - StandardGate::RCCX => "Rccx", - StandardGate::C3X => "X", - StandardGate::C3SX => "Sx", - StandardGate::RC3X => "Rcccx", - } - .to_string(); - - if custom_label.is_some() && custom_label.unwrap() != label { - label = custom_label.unwrap().to_string(); - } - if standard_gate.num_params() > 0 { - let params = instruction - .params_view() - .iter() - .map(|param| match param { - Param::Float(f) => format!("{}", f), - _ => format!("{:?}", param), - }) - .join(","); - label = format!("{}({})", label, params); - } + }, + OperationRef::StandardGate(standard_gate) => { + let mut label = match standard_gate { + StandardGate::GlobalPhase => "", + StandardGate::H => "H", + StandardGate::I => "I", + StandardGate::X => "X", + StandardGate::Y => "Y", + StandardGate::Z => "Z", + StandardGate::Phase => "P", + StandardGate::R => "R", + StandardGate::RX => "Rx", + StandardGate::RY => "Ry", + StandardGate::RZ => "Rz", + StandardGate::S => "S", + StandardGate::Sdg => "Sdg", + StandardGate::SX => "√X", + StandardGate::SXdg => "√Xdg", + StandardGate::T => "T", + StandardGate::Tdg => "Tdg", + StandardGate::U => "U", + StandardGate::U1 => "U1", + StandardGate::U2 => "U2", + StandardGate::U3 => "U3", + StandardGate::CH => "H", + StandardGate::CX => "X", + StandardGate::CY => "Y", + StandardGate::CZ => "Z", + StandardGate::DCX => "Dcx", + StandardGate::ECR => "Ecr", + StandardGate::Swap => "", + StandardGate::ISwap => "Iswap", + StandardGate::CPhase => "P", + StandardGate::CRX => "Rx", + StandardGate::CRY => "Ry", + StandardGate::CRZ => "Rz", + StandardGate::CS => "S", + StandardGate::CSdg => "Sdg", + StandardGate::CSX => "Sx", + StandardGate::CU => "U", + StandardGate::CU1 => "U1", + StandardGate::CU3 => "U3", + StandardGate::RXX => "Rxx", + StandardGate::RYY => "Ryy", + StandardGate::RZZ => "Rzz", + StandardGate::RZX => "Rzx", + StandardGate::XXMinusYY => "XX-YY", + StandardGate::XXPlusYY => "XX+YY", + StandardGate::CCX => "X", + StandardGate::CCZ => "Z", + StandardGate::CSwap => "", + StandardGate::RCCX => "Rccx", + StandardGate::C3X => "X", + StandardGate::C3SX => "Sx", + StandardGate::RC3X => "Rcccx", + } + .to_string(); - return format!(" {} ", label); + let custom_label = instruction.label(); + if custom_label.is_some() && custom_label.unwrap() != label { + label = custom_label.unwrap().to_string(); + } + if standard_gate.num_params() > 0 { + let params = instruction + .params_view() + .iter() + .map(|param| match param { + Param::Float(f) => format!("{}", f), + _ => format!("{:?}", param), + }) + .join(","); + label = format!("{}({})", label, params); + } + label + }, + OperationRef::Unitary(_) => { + instruction.label().unwrap_or(" Unitary ").to_string() + }, + // Fallback for non-standard operations + _ => format!(" {} ", instruction.op.name()) } - - // Fallback for non-standard operations - format!(" {} ", instruction.op.name().to_string()) } fn get_layer_width(&self, ind: usize) -> usize { From 7b329d2ae19d5df9113ffe720f7bde8caaf93a15 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Thu, 20 Nov 2025 20:27:35 +0530 Subject: [PATCH 67/70] linting and final checks --- crates/circuit/src/circuit_drawer.rs | 76 ++++++++++--------- crates/circuit/src/lib.rs | 2 +- .../circuit/circuit_visualization.py | 2 +- test.py | 31 -------- 4 files changed, 43 insertions(+), 68 deletions(-) delete mode 100644 test.py diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index c1acd12d6a3b..dc635d996088 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -121,7 +121,7 @@ fn get_instruction_range( enum WireInputElement<'a> { Qubit(&'a ShareableQubit), Clbit(&'a ShareableClbit), - Creg(&'a ClassicalRegister) + Creg(&'a ClassicalRegister), } impl WireInputElement<'_> { @@ -187,14 +187,12 @@ enum BoxedElement<'a> { Multi(&'a PackedInstruction), } - - /// Enum for representing the elements stored in a visualization matrix. The elements /// do not directly implement visualization capabilities, but rather carry enough information /// to enable visualization later on by the actual drawer. #[derive(Default, Clone)] -enum VisualizationElement<'a>{ +enum VisualizationElement<'a> { #[default] /// A wire element without any associated information. Empty, @@ -244,7 +242,7 @@ impl<'a> VisualizationLayer<'a> { OperationRef::StandardInstruction(std_inst) => { self.add_standard_instruction(cregbundle, std_inst, inst, circuit, clbit_map); } - OperationRef::Unitary(_) => { + OperationRef::Unitary(_) => { self.add_unitary_gate(inst, circuit); } _ => unimplemented!( @@ -370,12 +368,12 @@ impl<'a> VisualizationLayer<'a> { .filter(|idx| !(qargs.iter().map(|q| q.0 as usize)).contains(idx)); self.add_vertical_lines(vert_lines, inst); } - _ => unimplemented!("{}", format!("{:?} is not supported yet", gate)) + _ => unimplemented!("{}", format!("{:?} is not supported yet", gate)), } - // let vert_lines = (minima..=maxima) - // .filter(|idx| !control_indices.contains(idx) && !box_indices.contains(idx)); - // self.add_vertical_lines(inst, vert_lines); + // let vert_lines = (minima..=maxima) + // .filter(|idx| !control_indices.contains(idx) && !box_indices.contains(idx)); + // self.add_vertical_lines(inst, vert_lines); } fn add_standard_instruction( @@ -423,18 +421,13 @@ impl<'a> VisualizationLayer<'a> { } } - fn add_unitary_gate( - &mut self, - inst: &'a PackedInstruction, - circuit: &CircuitData, - ) { + fn add_unitary_gate(&mut self, inst: &'a PackedInstruction, circuit: &CircuitData) { let qargs = circuit.get_qargs(inst.qubits); if qargs.len() == 1 { - self.0[qargs.first().unwrap().index()] = VisualizationElement::Boxed(BoxedElement::Single(inst)); - } - else { - let (minima, maxima) = - get_instruction_range(qargs, &[], 0); + self.0[qargs.first().unwrap().index()] = + VisualizationElement::Boxed(BoxedElement::Single(inst)); + } else { + let (minima, maxima) = get_instruction_range(qargs, &[], 0); for q in minima..=maxima { self.0[q] = VisualizationElement::Boxed(BoxedElement::Multi(inst)); @@ -729,13 +722,12 @@ impl TextDrawer { fn get_label(instruction: &PackedInstruction) -> String { match instruction.op.view() { - OperationRef::StandardInstruction(std_instruction) => { - match std_instruction { - StandardInstruction::Measure => "M".to_string(), - StandardInstruction::Reset => "|0>".to_string(), - StandardInstruction::Barrier(_) => "░".to_string(), - StandardInstruction::Delay(delay_unit) => - format!("Delay({:?}[{}])", instruction.params, delay_unit), + OperationRef::StandardInstruction(std_instruction) => match std_instruction { + StandardInstruction::Measure => "M".to_string(), + StandardInstruction::Reset => "|0>".to_string(), + StandardInstruction::Barrier(_) => "░".to_string(), + StandardInstruction::Delay(delay_unit) => { + format!("Delay({:?}[{}])", instruction.params, delay_unit) } }, OperationRef::StandardGate(standard_gate) => { @@ -811,12 +803,10 @@ impl TextDrawer { label = format!("{}({})", label, params); } label - }, - OperationRef::Unitary(_) => { - instruction.label().unwrap_or(" Unitary ").to_string() - }, + } + OperationRef::Unitary(_) => instruction.label().unwrap_or(" Unitary ").to_string(), // Fallback for non-standard operations - _ => format!(" {} ", instruction.op.name()) + _ => format!(" {} ", instruction.op.name()), } } @@ -1220,13 +1210,29 @@ impl TextDrawer { output.push_str(&format!("{}\n", mid_line_next)); } let last_index = num_wires - 1; - let bot_line = self.wires[last_index].iter().map(|wire| wire.bot.clone()).collect::>().join(""); - let top_line_next = self.wires[last_index + 1].iter().map(|wire| wire.top.clone()).collect::>().join(""); + let bot_line = self.wires[last_index] + .iter() + .map(|wire| wire.bot.clone()) + .collect::>() + .join(""); + let top_line_next = self.wires[last_index + 1] + .iter() + .map(|wire| wire.top.clone()) + .collect::>() + .join(""); let merged_line = Self::merge_lines(&bot_line, &top_line_next); output.push_str(&format!("{}\n", merged_line)); - let mid_line_next = self.wires[last_index + 1].iter().map(|wire| wire.mid.clone()).collect::>().join(""); + let mid_line_next = self.wires[last_index + 1] + .iter() + .map(|wire| wire.mid.clone()) + .collect::>() + .join(""); output.push_str(&format!("{}\n", mid_line_next)); - let bot_line = self.wires[last_index].iter().map(|wire| wire.bot.clone()).collect::>().join(""); + let bot_line = self.wires[last_index] + .iter() + .map(|wire| wire.bot.clone()) + .collect::>() + .join(""); output.push_str(&format!("{}\n", bot_line)) } } diff --git a/crates/circuit/src/lib.rs b/crates/circuit/src/lib.rs index abcdc71258b8..5afae87d0934 100644 --- a/crates/circuit/src/lib.rs +++ b/crates/circuit/src/lib.rs @@ -37,8 +37,8 @@ pub mod parameter_table; pub mod register_data; pub mod slice; pub mod util; -pub mod vf2; mod variable_mapper; +pub mod vf2; use pyo3::PyTypeInfo; use pyo3::exceptions::PyValueError; diff --git a/qiskit/visualization/circuit/circuit_visualization.py b/qiskit/visualization/circuit/circuit_visualization.py index 9406c2c7c878..08f487f0e0dc 100644 --- a/qiskit/visualization/circuit/circuit_visualization.py +++ b/qiskit/visualization/circuit/circuit_visualization.py @@ -299,7 +299,7 @@ def check_clbit_in_inst(circuit, cregbundle): return True cregbundle = check_clbit_in_inst(circuit, cregbundle) - + if output == "text": return _text_circuit_drawer( circuit, diff --git a/test.py b/test.py deleted file mode 100644 index 45cac3cb8f02..000000000000 --- a/test.py +++ /dev/null @@ -1,31 +0,0 @@ -from qiskit.circuit import * -import numpy as np -from qiskit._accelerate.circuit import draw - -# wire for each clbit -# [10, 11, 11, 12, 12] - -# clbit indices -# [0, 1, 2, 3, 4] - -# index being passed -# [12, 12, 11] - -# ind - clbit_map[clbit.first().index()] - -# required output: -# 0, 1, 1 - -creg1 = ClassicalRegister(1, 'c1') -creg2 = ClassicalRegister(2, 'c2') -creg3 = ClassicalRegister(101, 'c3') -qreg1 = QuantumRegister(10, 'q1') -qc = QuantumCircuit(qreg1, creg1, creg2, creg3) -# qc.iswap(qreg1[9], qreg1[8]) -qc.measure(qreg1[0], creg3[0]) -qc.measure(qreg1[1], creg3[99]) -qc.measure(qreg1[2], creg2[1]) -creg = ClassicalRegister(4, 'creg') -qc.add_register(creg) -qc.measure(qreg1[3], creg[0]) -print(draw(qc, cregbundle=True, mergewires =False)) \ No newline at end of file From 13e78c11df52092d74231c2d80fc1f55faf5ef39 Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Thu, 20 Nov 2025 21:01:53 +0530 Subject: [PATCH 68/70] cleanups --- crates/circuit/src/circuit_drawer.rs | 6 ------ crates/circuit/src/lib.rs | 1 - qiskit/visualization/circuit/circuit_visualization.py | 2 -- 3 files changed, 9 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index dc635d996088..76c3ad98649c 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -23,7 +23,6 @@ use hashbrown::{HashMap, HashSet}; use itertools::{Itertools, MinMaxResult}; use pyo3::prelude::*; use rustworkx_core::petgraph::stable_graph::NodeIndex; -use std::cmp::Ordering; use std::fmt::Debug; use std::ops::Index; @@ -368,12 +367,7 @@ impl<'a> VisualizationLayer<'a> { .filter(|idx| !(qargs.iter().map(|q| q.0 as usize)).contains(idx)); self.add_vertical_lines(vert_lines, inst); } - _ => unimplemented!("{}", format!("{:?} is not supported yet", gate)), } - - // let vert_lines = (minima..=maxima) - // .filter(|idx| !control_indices.contains(idx) && !box_indices.contains(idx)); - // self.add_vertical_lines(inst, vert_lines); } fn add_standard_instruction( diff --git a/crates/circuit/src/lib.rs b/crates/circuit/src/lib.rs index 5afae87d0934..d1a5cff7d0d2 100644 --- a/crates/circuit/src/lib.rs +++ b/crates/circuit/src/lib.rs @@ -256,7 +256,6 @@ pub fn circuit(m: &Bound) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; - // m.add_class::()?; m.add_function(wrap_pyfunction!(circuit_drawer::py_drawer, m)?)?; let classical_mod = PyModule::new(m.py(), "classical")?; classical::register_python(&classical_mod)?; diff --git a/qiskit/visualization/circuit/circuit_visualization.py b/qiskit/visualization/circuit/circuit_visualization.py index 08f487f0e0dc..917f83d50c6d 100644 --- a/qiskit/visualization/circuit/circuit_visualization.py +++ b/qiskit/visualization/circuit/circuit_visualization.py @@ -37,7 +37,6 @@ from qiskit import user_config from qiskit.circuit import ControlFlowOp, Measure from qiskit.utils import optionals as _optionals -from qiskit._accelerate.circuit import CircuitData from ..exceptions import VisualizationError from ..utils import _trim as trim_image @@ -317,7 +316,6 @@ def check_clbit_in_inst(circuit, cregbundle): expr_len=expr_len, measure_arrows=measure_arrows, ) - elif output == "latex": image = _latex_circuit_drawer( circuit, From 57184e03ca959adaf82055dae09af034c636d3cd Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Fri, 21 Nov 2025 00:19:11 +0200 Subject: [PATCH 69/70] Fix panic with mergewires=true and improve Delay drawing --- crates/circuit/src/circuit_drawer.rs | 206 +++++++++++---------------- 1 file changed, 80 insertions(+), 126 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 76c3ad98649c..321c23084dbc 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -186,10 +186,9 @@ enum BoxedElement<'a> { Multi(&'a PackedInstruction), } -/// Enum for representing the elements stored in a visualization matrix. The elements +/// Enum for representing the elements stored in the visualization matrix. The elements /// do not directly implement visualization capabilities, but rather carry enough information /// to enable visualization later on by the actual drawer. - #[derive(Default, Clone)] enum VisualizationElement<'a> { #[default] @@ -716,14 +715,24 @@ impl TextDrawer { fn get_label(instruction: &PackedInstruction) -> String { match instruction.op.view() { - OperationRef::StandardInstruction(std_instruction) => match std_instruction { - StandardInstruction::Measure => "M".to_string(), - StandardInstruction::Reset => "|0>".to_string(), - StandardInstruction::Barrier(_) => "░".to_string(), - StandardInstruction::Delay(delay_unit) => { - format!("Delay({:?}[{}])", instruction.params, delay_unit) + OperationRef::StandardInstruction(std_instruction) => { + match std_instruction { + StandardInstruction::Measure => "M".to_string(), + StandardInstruction::Reset => "|0>".to_string(), + StandardInstruction::Barrier(_) => "░".to_string(), + StandardInstruction::Delay(delay_unit) => { + match instruction.params_view().first().unwrap() { + Param::Float(duration) => { + format!("Delay({}[{}])", duration, delay_unit) + } + Param::ParameterExpression(expr) => { + format!("Delay({}[{}])", expr, delay_unit) + } + Param::Obj(obj) => format!("Delay({:?}[{}])", obj, delay_unit), // TODO: extract the int + } + } } - }, + } OperationRef::StandardGate(standard_gate) => { let mut label = match standard_gate { StandardGate::GlobalPhase => "", @@ -878,7 +887,7 @@ impl TextDrawer { bot_con = C_BOT_CON; } } - let label = Self::get_label(inst); + let label = format!(" {} ", Self::get_label(inst)); let label_len = label.chars().count(); // To count unicode chars properly (e.g. in √X) let left_len = (label_len - 1) / 2; let right_len = label_len - left_len - 1; @@ -901,7 +910,7 @@ impl TextDrawer { ); } BoxedElement::Multi(inst) => { - let label = Self::get_label(inst); + let label = format!(" {} ", Self::get_label(inst)); let label_len = label.chars().count(); // To count unicode chars properly (e.g. in √X) let qargs = circuit.get_qargs(inst.qubits); let (minima, maxima) = get_instruction_range(qargs, &[], 0); @@ -916,7 +925,7 @@ impl TextDrawer { let mid_section = if ind == mid_idx { format!( - "{:^total_q$} {:^label_len$}", + "{:^total_q$}{:^label_len$}", num_affected, label, total_q = qargs.len(), @@ -924,7 +933,7 @@ impl TextDrawer { ) } else { format!( - "{:^total_q$} {:^label_len$}", + "{:^total_q$}{:^label_len$}", num_affected, " ", total_q = qargs.len(), @@ -1101,135 +1110,80 @@ impl TextDrawer { } fn draw(&self, mergewires: bool, fold: usize) -> String { - let ranges: Vec<(usize, usize)> = { - let mut temp_ranges = vec![]; - let mut layer_counter: usize = 1; - while layer_counter < self.wires[0].len() { - let mut total_width: usize = 0; - total_width += self.get_layer_width(0); - - let start = layer_counter; - while total_width <= fold && layer_counter < self.wires[0].len() { - total_width += self.get_layer_width(layer_counter); - layer_counter += 1; - } - let end = layer_counter; - temp_ranges.push((start, end)); + // Calculate the layer ranges for each fold of the circuit + let num_layers = self.wires[0].len(); + // We skip the first (inputs) layer since it's printed for each fold, regardless + // of screen width limit + let layer_widths = (1..num_layers).map(|layer| self.get_layer_width(layer)); + let (mut start, mut current_fold) = (1usize, 0usize); + let mut ranges: Vec<(usize, usize)> = Vec::new(); + + for (i, layer_width) in layer_widths.enumerate() { + current_fold += layer_width; + if current_fold > fold { + ranges.push((start, i + 1)); + start = i + 1; + current_fold = layer_width; } - temp_ranges - }; + } + ranges.push((start, num_layers)); let mut output = String::new(); - for (j, (start, end)) in ranges.iter().enumerate() { - if !mergewires { - for element in self.wires.iter() { - let mut top_line: String = element - .iter() - .enumerate() - .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here - .map(|(_, wire)| wire.top.clone()) // And here - .collect::>() - .join(""); - let mut mid_line: String = element - .iter() - .enumerate() - .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here too - .map(|(_, wire)| wire.mid.clone()) // And here - .collect::>() - .join(""); - let mut bot_line: String = element - .iter() - .enumerate() - .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // And here - .map(|(_, wire)| wire.bot.clone()) // And here - .collect::>() - .join(""); - top_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); - mid_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); - bot_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); - output.push_str(&format!("{}\n{}\n{}\n", top_line, mid_line, bot_line)); - } - } else { - let num_wires = self.wires.len(); - for i in 0..num_wires - 1 { - if i == 0 { - let mut top_line = self.wires[i] - .iter() - .enumerate() - .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here - .map(|(_, wire)| wire.top.clone()) // And here - .collect::>() - .join(""); - top_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); - let mut mid_line = self.wires[i] - .iter() - .enumerate() - .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here - .map(|(_, wire)| wire.mid.clone()) // And here - .collect::>() - .join(""); - mid_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); - output.push_str(&format!("{}\n{}\n", top_line, mid_line)); - } - let mut bot_line = self.wires[i] - .iter() - .enumerate() - .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here - .map(|(_, wire)| wire.bot.clone()) // And here - .collect::>() - .join(""); - - bot_line.push_str(if j == ranges.len() - 1 { "" } else { "»" }); + for (start, end) in ranges { + let mut wire_strings: Vec = Vec::new(); - let mut top_line_next = self.wires[i + 1] - .iter() - .enumerate() - .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here - .map(|(_, wire)| wire.top.clone()) // And here - .collect::>() - .join(""); - - top_line_next.push_str(if j == ranges.len() - 1 { "" } else { "»" }); - let merged_line = Self::merge_lines(&bot_line, &top_line_next); - output.push_str(&format!("{}\n", merged_line)); - let mut mid_line_next = self.wires[i + 1] - .iter() - .enumerate() - .filter(|(idx, _)| (*idx >= *start && *idx < *end) || *idx == 0) // Fix here - .map(|(_, wire)| wire.mid.clone()) // And here - .collect::>() - .join(""); - mid_line_next.push_str(if j == ranges.len() - 1 { "" } else { "»" }); - output.push_str(&format!("{}\n", mid_line_next)); - } - let last_index = num_wires - 1; - let bot_line = self.wires[last_index] + for wire in &self.wires { + let top_line: String = wire[start..end] .iter() - .map(|wire| wire.bot.clone()) + .map(|elem| elem.top.clone()) .collect::>() .join(""); - let top_line_next = self.wires[last_index + 1] + let mid_line: String = wire[start..end] .iter() - .map(|wire| wire.top.clone()) + .map(|elem| elem.mid.clone()) .collect::>() .join(""); - let merged_line = Self::merge_lines(&bot_line, &top_line_next); - output.push_str(&format!("{}\n", merged_line)); - let mid_line_next = self.wires[last_index + 1] + let bot_line: String = wire[start..end] .iter() - .map(|wire| wire.mid.clone()) + .map(|elem| elem.bot.clone()) .collect::>() .join(""); - output.push_str(&format!("{}\n", mid_line_next)); - let bot_line = self.wires[last_index] - .iter() - .map(|wire| wire.bot.clone()) - .collect::>() - .join(""); - output.push_str(&format!("{}\n", bot_line)) + wire_strings.push(format!( + "{}{}{}{}", + if start > 1 { "«" } else { "" }, + wire[0].top, + top_line, + if end < num_layers - 1 { "»" } else { "" } + )); + wire_strings.push(format!( + "{}{}{}{}", + if start > 1 { "«" } else { "" }, + wire[0].mid, + mid_line, + if end < num_layers - 1 { "»" } else { "" } + )); + wire_strings.push(format!( + "{}{}{}{}", + if start > 1 { "«" } else { "" }, + wire[0].bot, + bot_line, + if end < num_layers - 1 { "»" } else { "" } + )); + } + for wire_idx in 0..wire_strings.len() { + if mergewires && wire_idx % 3 == 2 && wire_idx < wire_strings.len() - 3 { + // Merge the bot_line of the this wire with the top_line of the next wire + let merged_line = + Self::merge_lines(&wire_strings[wire_idx], &wire_strings[wire_idx + 1]); + output.push_str(&format!("{}\n", merged_line)); + } else if !mergewires || wire_idx % 3 != 0 || wire_idx == 0 { + // if mergewires, skip all top_line strings but the very first one + output.push_str(&format!("{}\n", wire_strings[wire_idx])); + } } } + output } From 5a27774169ca5678ace18da722207a2bcb3fb57a Mon Sep 17 00:00:00 2001 From: Shobhit Pandey Date: Fri, 28 Nov 2025 11:44:02 +0530 Subject: [PATCH 70/70] fixed label extraction for packed instruction --- crates/circuit/src/circuit_drawer.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/circuit/src/circuit_drawer.rs b/crates/circuit/src/circuit_drawer.rs index 321c23084dbc..1c2272abd16a 100644 --- a/crates/circuit/src/circuit_drawer.rs +++ b/crates/circuit/src/circuit_drawer.rs @@ -790,9 +790,9 @@ impl TextDrawer { } .to_string(); - let custom_label = instruction.label(); - if custom_label.is_some() && custom_label.unwrap() != label { - label = custom_label.unwrap().to_string(); + let custom_label = instruction.label.clone(); + if custom_label.is_some() && *custom_label.unwrap() != label { + label = *instruction.label.clone().unwrap() } if standard_gate.num_params() > 0 { let params = instruction @@ -807,7 +807,10 @@ impl TextDrawer { } label } - OperationRef::Unitary(_) => instruction.label().unwrap_or(" Unitary ").to_string(), + OperationRef::Unitary(_) => *instruction + .label + .clone() + .unwrap_or(Box::new(" Unitary ".to_string())), // Fallback for non-standard operations _ => format!(" {} ", instruction.op.name()), }