diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/visualizer/grid.c | 136 | ||||
| -rw-r--r-- | src/visualizer/grid.h | 29 | ||||
| -rw-r--r-- | src/visualizer/vertical_cli.c | 326 |
3 files changed, 491 insertions, 0 deletions
diff --git a/src/visualizer/grid.c b/src/visualizer/grid.c new file mode 100644 index 0000000..5f95efc --- /dev/null +++ b/src/visualizer/grid.c @@ -0,0 +1,136 @@ +#include "visualizer/grid.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +static size_t glyph_bytes(unsigned char lead) +{ + if (lead < 0x80) + return 1; + if ((lead >> 5) == 0x6) + return 2; + if ((lead >> 4) == 0xE) + return 3; + if ((lead >> 3) == 0x1E) + return 4; + + return 1; +} + +size_t psi_utf8_count(const char *s) +{ + size_t count = 0; + while (*s) + { + s += glyph_bytes((unsigned char)*s); + count++; + } + + return count; +} + +struct PsiStringBuilder psi_new_string_builder(void) +{ + return (struct PsiStringBuilder) + { + NULL, + 0, + 0, + }; +} + +void psi_string_builder_append(struct PsiStringBuilder *sb, const char *s) +{ + size_t n = strlen(s); + if (sb->len + n + 1 > sb->cap) + { + size_t new_cap = sb->cap == 0 ? 256 : sb->cap * 2; + while (new_cap < sb->len + n + 1) + new_cap *= 2; + + sb->data = realloc(sb->data, new_cap); + assert(sb->data != NULL); + sb->cap = new_cap; + } + + memcpy(sb->data + sb->len, s, n + 1); + sb->len += n; +} + +char *psi_string_builder_finish(struct PsiStringBuilder *sb) +{ + if (sb->data == NULL) + psi_string_builder_append(sb, ""); + + return sb->data; +} + +struct PsiGlyphRow psi_new_glyph_row(size_t width) +{ + char (*cells)[5] = malloc(width * sizeof(*cells)); + assert(cells != NULL || width == 0); + + for (size_t i = 0; i < width; i++) + memcpy(cells[i], " ", 2); + + return (struct PsiGlyphRow) + { + cells, + width, + }; +} + +void psi_free_glyph_row(struct PsiGlyphRow *row) +{ + free(row->cells); + row->cells = NULL; + row->width = 0; +} + +void psi_glyph_row_set(struct PsiGlyphRow *row, size_t index, const char *glyph) +{ + if (index >= row->width) + return; + + size_t n = glyph_bytes((unsigned char)glyph[0]); + memcpy(row->cells[index], glyph, n); + row->cells[index][n] = '\0'; +} + +size_t psi_glyph_row_place(struct PsiGlyphRow *row, size_t start, const char *utf8) +{ + size_t count = 0; + const char *p = utf8; + + while (*p) + { + size_t n = glyph_bytes((unsigned char)*p); + size_t index = start + count; + if (index < row->width) + { + memcpy(row->cells[index], p, n); + row->cells[index][n] = '\0'; + } + + p += n; + count++; + } + + return count; +} + +void psi_glyph_row_fill_space(struct PsiGlyphRow *row, size_t from, size_t to, const char *glyph) +{ + for (size_t i = from; i < to && i < row->width; i++) + if (strcmp(row->cells[i], " ") == 0) + psi_glyph_row_set(row, i, glyph); +} + +void psi_glyph_row_render(struct PsiGlyphRow row, struct PsiStringBuilder *sb) +{ + for (size_t i = 0; i < row.width; i++) + psi_string_builder_append(sb, row.cells[i]); + + psi_string_builder_append(sb, "\n"); +} diff --git a/src/visualizer/grid.h b/src/visualizer/grid.h new file mode 100644 index 0000000..f327df8 --- /dev/null +++ b/src/visualizer/grid.h @@ -0,0 +1,29 @@ +#pragma once + +#include <stddef.h> + +struct PsiStringBuilder +{ + char *data; + size_t len; + size_t cap; +}; + +struct PsiStringBuilder psi_new_string_builder(void); +void psi_string_builder_append(struct PsiStringBuilder *sb, const char *s); +char *psi_string_builder_finish(struct PsiStringBuilder *sb); + +struct PsiGlyphRow +{ + char (*cells)[5]; + size_t width; +}; + +struct PsiGlyphRow psi_new_glyph_row(size_t width); +void psi_free_glyph_row(struct PsiGlyphRow *row); +void psi_glyph_row_set(struct PsiGlyphRow *row, size_t index, const char *glyph); +size_t psi_glyph_row_place(struct PsiGlyphRow *row, size_t start, const char *utf8); +void psi_glyph_row_fill_space(struct PsiGlyphRow *row, size_t from, size_t to, const char *glyph); +void psi_glyph_row_render(struct PsiGlyphRow row, struct PsiStringBuilder *sb); + +size_t psi_utf8_count(const char *s); diff --git a/src/visualizer/vertical_cli.c b/src/visualizer/vertical_cli.c new file mode 100644 index 0000000..09d4456 --- /dev/null +++ b/src/visualizer/vertical_cli.c @@ -0,0 +1,326 @@ +#include "visualizer/renderer.h" + +#include <stdbool.h> +#include <stdio.h> + +#include "visualizer/grid.h" + +static void gate_label(struct PsiGateOp op, char *out, size_t cap) +{ + switch (op.kind) + { + case PSI_GATE_H: snprintf(out, cap, "[H]"); break; + case PSI_GATE_X: snprintf(out, cap, "[X]"); break; + case PSI_GATE_Y: snprintf(out, cap, "[Y]"); break; + case PSI_GATE_Z: snprintf(out, cap, "[Z]"); break; + case PSI_GATE_S: snprintf(out, cap, "[S]"); break; + case PSI_GATE_T: snprintf(out, cap, "[T]"); break; + case PSI_GATE_SDG: snprintf(out, cap, "[S†]"); break; + case PSI_GATE_TDG: snprintf(out, cap, "[T†]"); break; + case PSI_GATE_SX: snprintf(out, cap, "[√X]"); break; + case PSI_GATE_SXDG: snprintf(out, cap, "[√X†]"); break; + case PSI_GATE_RX: snprintf(out, cap, "[Rx(%.2f)]", op.params[0]); break; + case PSI_GATE_RY: snprintf(out, cap, "[Ry(%.2f)]", op.params[0]); break; + case PSI_GATE_RZ: snprintf(out, cap, "[Rz(%.2f)]", op.params[0]); break; + case PSI_GATE_P: snprintf(out, cap, "[P(%.2f)]", op.params[0]); break; + case PSI_GATE_U1: snprintf(out, cap, "[U1(%.2f)]", op.params[0]); break; + case PSI_GATE_U2: snprintf(out, cap, "[U2]"); break; + case PSI_GATE_U3: snprintf(out, cap, "[U3]"); break; + case PSI_GATE_CRX: snprintf(out, cap, "[CRx]"); break; + case PSI_GATE_CRY: snprintf(out, cap, "[CRy]"); break; + case PSI_GATE_CRZ: snprintf(out, cap, "[CRz]"); break; + case PSI_GATE_CP: snprintf(out, cap, "[CP]"); break; + case PSI_GATE_CNOT: snprintf(out, cap, "●"); break; + case PSI_GATE_CZ: snprintf(out, cap, "●"); break; + case PSI_GATE_SWAP: snprintf(out, cap, "╳"); break; + case PSI_GATE_CCNOT: snprintf(out, cap, "●"); break; + case PSI_GATE_CSWAP: snprintf(out, cap, "●"); break; + case PSI_GATE_MEASURE: snprintf(out, cap, "[M]"); break; + case PSI_GATE_CUSTOM: snprintf(out, cap, "[%s]", op.custom->name); break; + } +} + +static size_t calculate_col_width(const struct PsiQuantumCircuit *circuit) +{ + size_t max_label = 3; + + for (size_t i = 0; i < circuit->operation_count; i++) + { + char label[64]; + gate_label(circuit->operations[i], label, sizeof label); + size_t n = psi_utf8_count(label); + if (n > max_label) + max_label = n; + } + + size_t width = max_label + 2; + if (width % 2 == 0) + return width + 1; + + return width; +} + +static bool is_single_target(enum PsiGateKind kind) +{ + switch (kind) + { + case PSI_GATE_H: + case PSI_GATE_X: + case PSI_GATE_Y: + case PSI_GATE_Z: + case PSI_GATE_S: + case PSI_GATE_T: + case PSI_GATE_SDG: + case PSI_GATE_TDG: + case PSI_GATE_SX: + case PSI_GATE_SXDG: + case PSI_GATE_RX: + case PSI_GATE_RY: + case PSI_GATE_RZ: + case PSI_GATE_P: + case PSI_GATE_U1: + case PSI_GATE_U2: + case PSI_GATE_U3: + return true; + default: + return false; + } +} + +static bool is_param_controlled(enum PsiGateKind kind) +{ + switch (kind) + { + case PSI_GATE_CRX: + case PSI_GATE_CRY: + case PSI_GATE_CRZ: + case PSI_GATE_CP: + return true; + default: + return false; + } +} + +char *psi_render_circuit_vertical(const struct PsiQuantumCircuit *circuit) +{ + struct PsiStringBuilder sb = psi_new_string_builder(); + + size_t nq = circuit->num_qubits; + size_t nc = circuit->num_classical; + + if (nq == 0) + return psi_string_builder_finish(&sb); + + size_t col_width = calculate_col_width(circuit); + size_t gap_width = 3; + size_t stride = col_width + 1; + size_t half = col_width / 2; + + size_t q_total = nq * col_width + (nq - 1); + size_t c_total = nc > 0 ? nc * col_width + (nc - 1) : 0; + size_t total_width = q_total + gap_width + c_total; + + struct PsiGlyphRow header = psi_new_glyph_row(total_width); + for (size_t i = 0; i < nq; i++) + { + char label[16]; + snprintf(label, sizeof label, "q%zu", i); + size_t col_start = i * stride; + psi_glyph_row_place(&header, col_start + (col_width - psi_utf8_count(label)) / 2, label); + } + for (size_t i = 0; i < nc; i++) + { + char label[16]; + snprintf(label, sizeof label, "c%zu", i); + size_t col_start = q_total + gap_width + i * stride; + psi_glyph_row_place(&header, col_start + (col_width - psi_utf8_count(label)) / 2, label); + } + psi_glyph_row_render(header, &sb); + psi_free_glyph_row(&header); + + for (size_t op_index = 0; op_index <= circuit->operation_count; op_index++) + { + struct PsiGlyphRow wires = psi_new_glyph_row(total_width); + for (size_t i = 0; i < nq; i++) + psi_glyph_row_set(&wires, i * stride + half, "│"); + for (size_t i = 0; i < nc; i++) + psi_glyph_row_set(&wires, q_total + gap_width + i * stride + half, "║"); + psi_glyph_row_render(wires, &sb); + psi_free_glyph_row(&wires); + + if (op_index == circuit->operation_count) + break; + + struct PsiGateOp op = circuit->operations[op_index]; + + size_t target_count; + const size_t *targets = psi_gate_op_quantum_targets(&op, &target_count); + size_t min_q = targets[0]; + size_t max_q = targets[0]; + for (size_t i = 1; i < target_count; i++) + { + if (targets[i] < min_q) + min_q = targets[i]; + if (targets[i] > max_q) + max_q = targets[i]; + } + + char label[64]; + gate_label(op, label, sizeof label); + size_t label_len = psi_utf8_count(label); + + struct PsiGlyphRow row = psi_new_glyph_row(total_width); + + if (is_single_target(op.kind)) + { + size_t target = op.qubits[0]; + for (size_t i = 0; i < nq; i++) + { + size_t col_start = i * stride; + if (i == target) + psi_glyph_row_place(&row, col_start + (col_width - label_len) / 2, label); + else + psi_glyph_row_set(&row, col_start + half, "│"); + } + for (size_t i = 0; i < nc; i++) + psi_glyph_row_set(&row, q_total + gap_width + i * stride + half, "║"); + } + else if (op.kind == PSI_GATE_MEASURE) + { + size_t mq = op.qubits[0]; + size_t mc = op.classical; + for (size_t i = 0; i < nq; i++) + { + size_t col_start = i * stride; + if (i < mq) + psi_glyph_row_set(&row, col_start + half, "│"); + else if (i == mq) + psi_glyph_row_place(&row, col_start + (col_width - label_len) / 2, label); + } + + size_t mq_center = mq * stride + half; + size_t mc_start = q_total + gap_width; + size_t mc_center = mc_start + mc * stride + half; + + psi_glyph_row_fill_space(&row, mq_center + 2, mc_center + 1, "═"); + psi_glyph_row_set(&row, mc_center, "╣"); + + for (size_t i = 0; i < nc; i++) + if (i > mc) + psi_glyph_row_set(&row, mc_start + i * stride + half, "║"); + } + else if (op.kind == PSI_GATE_CCNOT || op.kind == PSI_GATE_CSWAP) + { + bool is_cswap = op.kind == PSI_GATE_CSWAP; + const char *sym_c = "●"; + const char *sym_t = is_cswap ? "╳" : "⊕"; + size_t c1 = op.qubits[0]; + size_t c2 = op.qubits[1]; + size_t t = op.qubits[2]; + + for (size_t i = 0; i < nq; i++) + { + size_t center = i * stride + half; + if (i < min_q || i > max_q) + psi_glyph_row_set(&row, center, "│"); + else if (i == c1) + psi_glyph_row_set(&row, center, sym_c); + else if (i == c2) + psi_glyph_row_set(&row, center, is_cswap ? sym_t : sym_c); + else if (i == t) + psi_glyph_row_set(&row, center, sym_t); + } + + psi_glyph_row_fill_space(&row, min_q * stride + half + 1, max_q * stride + half, "─"); + for (size_t i = 0; i < nc; i++) + psi_glyph_row_set(&row, q_total + gap_width + i * stride + half, "║"); + } + else if (op.kind == PSI_GATE_CUSTOM) + { + if (target_count == 1) + { + for (size_t i = 0; i < nq; i++) + { + size_t col_start = i * stride; + if (i == targets[0]) + psi_glyph_row_place(&row, col_start + (col_width - label_len) / 2, label); + else + psi_glyph_row_set(&row, col_start + half, "│"); + } + } + else + { + for (size_t i = 0; i < nq; i++) + { + size_t col_start = i * stride; + size_t center = col_start + half; + bool is_target = false; + for (size_t k = 0; k < target_count; k++) + if (targets[k] == i) + is_target = true; + + if (i < min_q || i > max_q) + psi_glyph_row_set(&row, center, "│"); + else if (i == targets[0]) + psi_glyph_row_place(&row, col_start + (col_width - label_len) / 2, label); + else if (is_target) + psi_glyph_row_set(&row, center, "□"); + } + + psi_glyph_row_fill_space(&row, min_q * stride + half + 1, max_q * stride + half, "─"); + } + + for (size_t i = 0; i < nc; i++) + psi_glyph_row_set(&row, q_total + gap_width + i * stride + half, "║"); + } + else + { + const char *sym1 = "●"; + const char *sym2 = "⊕"; + if (op.kind == PSI_GATE_CZ) + sym2 = "●"; + else if (op.kind == PSI_GATE_SWAP) + { + sym1 = "╳"; + sym2 = "╳"; + } + else if (is_param_controlled(op.kind)) + sym2 = "□"; + + size_t control = op.qubits[0]; + size_t target = op.qubits[1]; + + for (size_t i = 0; i < nq; i++) + { + size_t col_start = i * stride; + size_t center = col_start + half; + if (i < min_q || i > max_q) + psi_glyph_row_set(&row, center, "│"); + else if (i == control) + psi_glyph_row_set(&row, center, sym1); + else if (i == target) + { + if (is_param_controlled(op.kind)) + psi_glyph_row_place(&row, col_start + (col_width - label_len) / 2, label); + else + psi_glyph_row_set(&row, center, sym2); + } + } + + psi_glyph_row_fill_space(&row, min_q * stride + half + 1, max_q * stride + half, "─"); + for (size_t i = 0; i < nc; i++) + psi_glyph_row_set(&row, q_total + gap_width + i * stride + half, "║"); + } + + psi_glyph_row_render(row, &sb); + psi_free_glyph_row(&row); + } + + struct PsiGlyphRow end_row = psi_new_glyph_row(total_width); + for (size_t i = 0; i < total_width; i++) + psi_glyph_row_set(&end_row, i, "░"); + psi_glyph_row_render(end_row, &sb); + psi_free_glyph_row(&end_row); + + return psi_string_builder_finish(&sb); +} |
