From 609f7276fc1fb484be34b232d35426f91ddb9b71 Mon Sep 17 00:00:00 2001 From: hachem Date: Sat, 6 Dec 2025 21:20:43 +0100 Subject: [add]: complete horizonal renderer --- libpsi-visualizer/src/cli/horizontal_cli.rs | 279 ++++++++++++++++++++++--- libpsi-visualizer/src/cli/mod.rs | 2 - libpsi-visualizer/src/cli/renderer.rs | 312 ---------------------------- libpsi-visualizer/src/cli/vertical_cli.rs | 26 --- libpsi-visualizer/src/lib.rs | 11 - 5 files changed, 246 insertions(+), 384 deletions(-) delete mode 100644 libpsi-visualizer/src/cli/renderer.rs (limited to 'libpsi-visualizer/src') diff --git a/libpsi-visualizer/src/cli/horizontal_cli.rs b/libpsi-visualizer/src/cli/horizontal_cli.rs index 3f02c59..b1b851b 100644 --- a/libpsi-visualizer/src/cli/horizontal_cli.rs +++ b/libpsi-visualizer/src/cli/horizontal_cli.rs @@ -1,44 +1,257 @@ -/* - NOTE(Hachem): This is what I would like the output to be like - - ┌───┐ ┌───────┐ ┌─────────┐ - |q0>: ─────┤ H ├────┤ C-NOT ├────┤ MEASURE ├───────────────────▓ - └───┘ └───┬───┘ └────┬────┘ - │ │ ┌─────────┐ - |q1>: ──────────────────■─────────────┼─────────┤ MEASURE ├────▓ - │ └────┬────┘ - │ │ - c0: ════════════════════════════════■══════════════╪═════════▓ - c1: ═══════════════════════════════════════════════■═════════▓ -*/ - use core::fmt; +use libpsi_core::{GateOp, QuantumCircuit}; -use crate::{renderer::*, Visualizer}; -use libpsi_core::QuantumCircuit; - -#[allow(unused)] -pub struct HorizontalCLIVisualizer<'a> { - circuit: &'a QuantumCircuit<'a>, - renderer: CLIRenderer, +pub struct HorizontalRenderer<'a> { + circuit: &'a QuantumCircuit, } -impl<'a> fmt::Display for HorizontalCLIVisualizer<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.renderer.to_string()) +impl<'a> HorizontalRenderer<'a> { + pub fn new(circuit: &'a QuantumCircuit) -> Self { + HorizontalRenderer { circuit } } } -impl<'a> Visualizer<'a> for HorizontalCLIVisualizer<'a> { - fn new(circuit: &'a QuantumCircuit) -> HorizontalCLIVisualizer<'a> { - let terminal_width = term_size::dimensions().unwrap().0; - HorizontalCLIVisualizer { - circuit, - renderer: CLIRenderer::new(terminal_width), +impl<'a> fmt::Display for HorizontalRenderer<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let nq = self.circuit.num_qubits(); + let nc = self.circuit.num_classical(); + let ops = self.circuit.operations(); + + let mut q_lines: Vec = (0..nq).map(|i| format!("q{}: ", i)).collect(); + let mut c_lines: Vec = (0..nc).map(|i| format!("c{}: ", i)).collect(); + + let max_label = q_lines + .iter() + .chain(c_lines.iter()) + .map(|s| s.len()) + .max() + .unwrap_or(3); + + for line in &mut q_lines { + while line.len() < max_label { + line.insert(0, ' '); + } + } + for line in &mut c_lines { + while line.len() < max_label { + line.insert(0, ' '); + } + } + let mut gap_line = " ".repeat(max_label); + + if ops.is_empty() { + for line in &q_lines { + writeln!(f, "{}───", line)?; + } + if nc > 0 { + writeln!(f, "{} ", gap_line)?; + for line in &c_lines { + writeln!(f, "{}═══", line)?; + } + } + return Ok(()); + } + + for op in ops { + let q_targets = op.quantum_targets(); + + let min_q = q_targets.iter().min().copied().unwrap_or(0); + let max_q = q_targets.iter().max().copied().unwrap_or(0); + + match op { + GateOp::H(t) => { + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *t { + line.push_str("─[H]─"); + } else { + line.push_str("─────"); + } + } + for line in c_lines.iter_mut() { + line.push_str("═════"); + } + gap_line.push_str(" "); + } + GateOp::X(t) => { + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *t { + line.push_str("─[X]─"); + } else { + line.push_str("─────"); + } + } + for line in c_lines.iter_mut() { + line.push_str("═════"); + } + gap_line.push_str(" "); + } + GateOp::Y(t) => { + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *t { + line.push_str("─[Y]─"); + } else { + line.push_str("─────"); + } + } + for line in c_lines.iter_mut() { + line.push_str("═════"); + } + gap_line.push_str(" "); + } + GateOp::Z(t) => { + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *t { + line.push_str("─[Z]─"); + } else { + line.push_str("─────"); + } + } + for line in c_lines.iter_mut() { + line.push_str("═════"); + } + gap_line.push_str(" "); + } + GateOp::S(t) => { + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *t { + line.push_str("─[S]─"); + } else { + line.push_str("─────"); + } + } + for line in c_lines.iter_mut() { + line.push_str("═════"); + } + gap_line.push_str(" "); + } + GateOp::T(t) => { + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *t { + line.push_str("─[T]─"); + } else { + line.push_str("─────"); + } + } + for line in c_lines.iter_mut() { + line.push_str("═════"); + } + gap_line.push_str(" "); + } + GateOp::CNOT(c, t) => { + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *c { + line.push_str("──●──"); + } else if i == *t { + line.push_str("──⊕──"); + } else if i > min_q && i < max_q { + line.push_str("──│──"); + } else { + line.push_str("─────"); + } + } + for line in c_lines.iter_mut() { + line.push_str("═════"); + } + gap_line.push_str(" "); + } + GateOp::CZ(c, t) => { + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *c || i == *t { + line.push_str("──●──"); + } else if i > min_q && i < max_q { + line.push_str("──│──"); + } else { + line.push_str("─────"); + } + } + for line in c_lines.iter_mut() { + line.push_str("═════"); + } + gap_line.push_str(" "); + } + GateOp::SWAP(a, b) => { + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *a || i == *b { + line.push_str("──╳──"); + } else if i > min_q && i < max_q { + line.push_str("──│──"); + } else { + line.push_str("─────"); + } + } + for line in c_lines.iter_mut() { + line.push_str("═════"); + } + gap_line.push_str(" "); + } + GateOp::CCNOT(c1, c2, t) => { + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *c1 || i == *c2 { + line.push_str("──●──"); + } else if i == *t { + line.push_str("──⊕──"); + } else if i > min_q && i < max_q { + line.push_str("──│──"); + } else { + line.push_str("─────"); + } + } + for line in c_lines.iter_mut() { + line.push_str("═════"); + } + gap_line.push_str(" "); + } + GateOp::CSWAP(c, t1, t2) => { + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *c { + line.push_str("──●──"); + } else if i == *t1 || i == *t2 { + line.push_str("──╳──"); + } else if i > min_q && i < max_q { + line.push_str("──│──"); + } else { + line.push_str("─────"); + } + } + for line in c_lines.iter_mut() { + line.push_str("═════"); + } + gap_line.push_str(" "); + } + GateOp::Measure(q, c) => { + for (i, line) in q_lines.iter_mut().enumerate() { + if i == *q { + line.push_str("─[M]─"); + } else if i > *q { + line.push_str("──║──"); + } else { + line.push_str("─────"); + } + } + for (i, line) in c_lines.iter_mut().enumerate() { + if i == *c { + line.push_str("══╩══"); + } else if i < *c { + line.push_str("══║══"); + } else { + line.push_str("═════"); + } + } + gap_line.push_str(" ║ "); + } + } + } + + for line in &q_lines { + writeln!(f, "{}", line)?; + } + if nc > 0 { + writeln!(f, "{}", gap_line)?; + for line in &c_lines { + writeln!(f, "{}", line)?; + } } - } - fn render(&mut self) { - println!("{}", self); + Ok(()) } } diff --git a/libpsi-visualizer/src/cli/mod.rs b/libpsi-visualizer/src/cli/mod.rs index e1c8736..fe10249 100644 --- a/libpsi-visualizer/src/cli/mod.rs +++ b/libpsi-visualizer/src/cli/mod.rs @@ -1,4 +1,2 @@ pub mod horizontal_cli; -pub mod renderer; - pub use horizontal_cli::*; diff --git a/libpsi-visualizer/src/cli/renderer.rs b/libpsi-visualizer/src/cli/renderer.rs deleted file mode 100644 index ba5a28b..0000000 --- a/libpsi-visualizer/src/cli/renderer.rs +++ /dev/null @@ -1,312 +0,0 @@ -#[derive(Clone, Copy)] -pub struct Quad { - pub x: usize, - pub y: usize, - pub width: usize, - pub height: usize, -} - -impl Quad { - pub fn new(x: usize, y: usize, width: usize, height: usize) -> Quad { - Quad { - x, - y, - width, - height, - } - } -} - -pub struct CLIRenderer { - buffer: Vec>, -} - -impl CLIRenderer { - pub fn new(width: usize) -> CLIRenderer { - CLIRenderer { - buffer: vec![vec![' '; width]; 1], - } - } - - fn expand(&mut self, x: usize, y: usize) { - if y >= self.buffer.len() { - self.buffer.resize( - y + 1, - vec![' '; self.buffer.get(0).map_or(0, |row| row.len())], - ); - } - - if x >= self.buffer[y].len() { - self.buffer[y].resize(x + 1, ' '); - } - } - - pub fn to_string(&self) -> String { - self.buffer - .clone() - .into_iter() - .map(|inner| inner.into_iter().collect::()) - .collect::>() - .join("\n") - } - - pub fn place(&mut self, x: usize, y: usize, character: char) { - self.expand(x, y); - self.buffer[y][x] = character; - } - - pub fn get(&mut self, x: usize, y: usize) -> char { - self.expand(x, y); - self.buffer[y][x] - } - - pub fn draw_text(&mut self, x: usize, y: usize, text: &'static str) { - self.expand(x + text.len(), y); - for i in 0..text.len() { - self.place(x + i, y, text.as_bytes()[i].into()); - } - } - - pub fn draw_vline(&mut self, x: usize, y: usize, height: usize, thick: bool) { - self.expand(x, y + height); - - for i in y..y + height { - if self.get(x, i) == '─' { - self.place(x, i, if thick { '╫' } else { '┼' }); - } else if self.get(x, i) == '═' { - self.place(x, i, if thick { '║' } else { '╫' }); - } else { - self.place(x, i, if thick { '║' } else { '│' }) - } - } - } - - pub fn draw_hline(&mut self, x: usize, y: usize, width: usize, thick: bool) { - self.expand(x + width, y); - - for i in x..x + width { - if self.get(i, y) == '│' { - self.place(i, y, if thick { '╪' } else { '┼' }); - } else if self.get(i, y) == '║' { - self.place(i, y, if thick { '║' } else { '╫' }); - } else { - self.place(i, y, if thick { '═' } else { '─' }) - } - } - } - - pub fn draw_quad(&mut self, quad: Quad, thick: bool) { - self.expand(quad.x + quad.width, quad.y + quad.height); - - for x in quad.x..quad.x + quad.width { - for y in quad.y..quad.y + quad.height { - if x == quad.x && y == quad.y { - self.place(x, y, if thick { '╔' } else { '┌' }); - } else if x == quad.x + quad.width - 1 && y == quad.y { - self.place(x, y, if thick { '╗' } else { '┐' }); - } else if x == quad.x && y == quad.y + quad.height - 1 { - self.place(x, y, if thick { '╚' } else { '└' }); - } else if x == quad.x + quad.width - 1 && y == quad.y + quad.height - 1 { - self.place(x, y, if thick { '╝' } else { '┘' }); - } else if y == quad.y || y == quad.y + quad.height - 1 { - self.draw_hline(x, y, 1, thick); - } else if x == quad.x || x == quad.x + quad.width - 1 { - self.draw_vline(x, y, 1, thick); - } - } - } - } - - pub fn connect(&mut self, quad: Quad, point: (usize, usize)) { - enum Directions { - Bottom, - BottomLeft, - BottomRight, - - Top, - TopLeft, - TopRight, - - Right, - Left, - None, - } - - let center_x = (((quad.x + quad.width) as f32 / 2.0).ceil()) as usize; - let center_y = (((quad.y + quad.height) as f32 / 2.0).ceil()) as usize; - - let point_x = point.0; - let point_y = point.1; - - let mut edge_midpoint_x = 0usize; - let mut edge_midpoint_y = 0usize; - - let line_midpoint_x = center_x; - let line_midpoint_y = point_y; - - let mut direction = Directions::None; - - if point_y > center_y && point_x == center_x { - edge_midpoint_x = center_x; - edge_midpoint_y = quad.y + quad.height - 1; - direction = Directions::Bottom; - } else if point_y > center_y && point_x < center_x { - edge_midpoint_x = center_x; - edge_midpoint_y = quad.y + quad.height - 1; - direction = Directions::BottomLeft; - } else if point_y > center_y && point_x > center_x { - edge_midpoint_x = center_x; - edge_midpoint_y = quad.y + quad.height - 1; - direction = Directions::BottomRight; - } else if point_y < center_y && point_x == center_x { - edge_midpoint_x = center_x; - edge_midpoint_y = quad.y; - direction = Directions::Top; - } else if point_y < center_y && point_x < center_x { - edge_midpoint_x = center_x; - edge_midpoint_y = quad.y; - direction = Directions::TopLeft; - } else if point_y < center_y && point_x > center_x { - edge_midpoint_x = center_x; - edge_midpoint_y = quad.y; - direction = Directions::TopRight; - } else if point_x > center_x && point_y == center_y { - edge_midpoint_x = quad.x + quad.width - 1; - edge_midpoint_y = center_y; - direction = Directions::Right; - } else if point_x < center_x && point_y == center_y { - edge_midpoint_x = quad.x; - edge_midpoint_y = center_y; - - direction = Directions::Left; - } - - match direction { - Directions::Bottom | Directions::BottomRight | Directions::BottomLeft => { - let character = self.get(edge_midpoint_x, edge_midpoint_y); - self.place( - edge_midpoint_x, - edge_midpoint_y, - if character == '─' { '┬' } else { '╤' }, - ); - } - - Directions::Top | Directions::TopRight | Directions::TopLeft => { - let character = self.get(edge_midpoint_x, edge_midpoint_y); - self.place( - edge_midpoint_x, - edge_midpoint_y, - if character == '─' { '┴' } else { '╧' }, - ); - } - - Directions::Right => { - let character = self.get(edge_midpoint_x, edge_midpoint_y); - self.place( - edge_midpoint_x, - edge_midpoint_y, - if character == '│' { '├' } else { '╟' }, - ); - } - - Directions::Left => { - let character = self.get(edge_midpoint_x, edge_midpoint_y); - self.place( - edge_midpoint_x, - edge_midpoint_y, - if character == '│' { '┤' } else { '╢' }, - ); - } - - Directions::None => {} - } - - match direction { - Directions::Top => { - self.draw_vline(edge_midpoint_x, point_y, edge_midpoint_y - point_y, false); - } - - Directions::Bottom => { - self.draw_vline( - edge_midpoint_x, - edge_midpoint_y + 1, - point_y - edge_midpoint_y - 1, - false, - ); - } - - Directions::Right => { - self.draw_hline( - edge_midpoint_x + 1, - edge_midpoint_y, - point_x - edge_midpoint_x - 1, - false, - ); - } - - Directions::Left => { - self.draw_hline( - point_x + 1, - edge_midpoint_y, - edge_midpoint_x - point_x - 1, - false, - ); - } - - Directions::BottomRight => { - self.place(line_midpoint_x, line_midpoint_y, '└'); - self.draw_hline( - line_midpoint_x + 1, - line_midpoint_y, - point_x - line_midpoint_x - 1, - false, - ); - self.draw_vline( - edge_midpoint_x, - edge_midpoint_y + 1, - point_y - edge_midpoint_y - 1, - false, - ); - } - Directions::BottomLeft => { - self.place(line_midpoint_x, line_midpoint_y, '┘'); - self.draw_hline(point_x, point_y, line_midpoint_x - point_x, false); - self.draw_vline( - edge_midpoint_x, - edge_midpoint_y + 1, - point_y - edge_midpoint_y - 1, - false, - ); - } - Directions::TopRight => { - self.place(line_midpoint_x, line_midpoint_y, '┌'); - self.draw_hline( - line_midpoint_x + 1, - line_midpoint_y, - point_x - line_midpoint_x - 1, - false, - ); - - self.draw_vline( - edge_midpoint_x, - point_y + 1, - edge_midpoint_y - point_y - 1, - false, - ); - } - Directions::TopLeft => { - self.place(line_midpoint_x, line_midpoint_y, '┐'); - self.draw_hline(point_x, point_y, line_midpoint_x - point_x, false); - self.draw_vline( - edge_midpoint_x, - point_y + 1, - edge_midpoint_y - point_y - 1, - false, - ); - } - _ => {} - } - - self.place(point_x, point_y, '■'); - } -} diff --git a/libpsi-visualizer/src/cli/vertical_cli.rs b/libpsi-visualizer/src/cli/vertical_cli.rs index cf69d0b..8b13789 100644 --- a/libpsi-visualizer/src/cli/vertical_cli.rs +++ b/libpsi-visualizer/src/cli/vertical_cli.rs @@ -1,27 +1 @@ -/* - NOTE(Hachem): This is what I would like the vertical output to be like - |q0>: |q0>: c0: c1: - │ │ ║ ║ - │ │ ║ ║ - ┌─┴─┐ │ ║ ║ - │ H │ │ ║ ║ - └─┬─┘ │ ║ ║ - │ │ ║ ║ - │ │ ║ ║ - ┌───┴───┐ │ ║ ║ - │ C-NOT ├────────■ ║ ║ - └───┬───┘ │ ║ ║ - │ │ ║ ║ - │ │ ║ ║ - ┌────┴────┐ │ ║ ║ - │ MEASURE ├───────┼───────■ ║ - └────┬────┘ │ ║ ║ - │ │ ║ ║ - │ ┌────┴────┐ ║ ║ - │ │ MEASURE ├──╫────■ - │ └────┬────┘ ║ ║ - │ │ ║ ║ - │ │ ║ ║ - ▓ ▓ ▓ ▓ -*/ diff --git a/libpsi-visualizer/src/lib.rs b/libpsi-visualizer/src/lib.rs index 11436c4..373ae6d 100644 --- a/libpsi-visualizer/src/lib.rs +++ b/libpsi-visualizer/src/lib.rs @@ -1,13 +1,2 @@ -use libpsi_core::*; -use std::fmt; - pub mod cli; pub use cli::*; - -pub trait Visualizer<'a> -where - Self: fmt::Display, -{ - fn new(circuit: &'a QuantumCircuit) -> Self; - fn render(&mut self); -} -- cgit v1.3