aboutsummaryrefslogtreecommitdiff
path: root/libpsi-visualizer
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2025-12-06 21:20:43 +0100
committerhachem <im@hachem.wtf>2025-12-06 21:20:43 +0100
commit609f7276fc1fb484be34b232d35426f91ddb9b71 (patch)
tree8cfc9a68ac184cb01111ddbef1f3a77b0b62b91f /libpsi-visualizer
parent1fef726728a14cc923c7cbaf80d337b4eb35db3c (diff)
[add]: complete horizonal renderer
Diffstat (limited to 'libpsi-visualizer')
-rw-r--r--libpsi-visualizer/src/cli/horizontal_cli.rs279
-rw-r--r--libpsi-visualizer/src/cli/mod.rs2
-rw-r--r--libpsi-visualizer/src/cli/renderer.rs312
-rw-r--r--libpsi-visualizer/src/cli/vertical_cli.rs26
-rw-r--r--libpsi-visualizer/src/lib.rs11
5 files changed, 246 insertions, 384 deletions
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<String> = (0..nq).map(|i| format!("q{}: ", i)).collect();
+ let mut c_lines: Vec<String> = (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<Vec<char>>,
-}
-
-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::<String>())
- .collect::<Vec<String>>()
- .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);
-}