numerics
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1/// @file main.cpp
2/// @brief Quantum Circuit Demo -- interactive statevector simulator
3///
4/// Visualises five canonical quantum circuits with step-by-step gate animation
5/// and a live probability histogram.
6///
7/// Controls:
8/// [-> / L] Apply next gate
9/// [<- / H] Undo last gate (step back)
10/// [1-5] Load preset circuit
11/// [SPACE] Auto-step (one gate per 0.5 s)
12/// [R] Reset to |0...0>
13///
14/// Presets:
15/// [1] Bell State |Phi+> = (|00>+|11>)/sqrt2
16/// [2] GHZ State (|000>+|111>)/sqrt2
17/// [3] Grover Search 2-qubit, finds |11> in one iteration
18/// [4] Quantum Teleportation q0 -> q2
19/// [5] QFT_3 3-qubit Quantum Fourier Transform on |001>
20
21#include "quantum/circuit.hpp"
23#include <raylib.h>
24#include <cmath>
25#include <cstdio>
26#include <string>
27#include <vector>
28
29using namespace num;
30
31// -- Layout constants ----------------------------------------------------------
32static constexpr int WIN_W = 1280;
33static constexpr int WIN_H = 720;
34
35// Circuit panel (left)
36static constexpr int CIR_X = 10;
37static constexpr int CIR_Y = 80;
38static constexpr int CIR_W = 720;
39static constexpr int CIR_H = 500;
40
41// Histogram panel (right)
42static constexpr int HIS_X = 750;
43static constexpr int HIS_Y = 80;
44static constexpr int HIS_W = 510;
45static constexpr int HIS_H = 500;
46
47// Gate cell sizes (pixels)
48static constexpr int GATE_W = 64; // column pitch
49static constexpr int GATE_H = 36; // gate box height
50static constexpr int WIRE_PITCH = 80; // vertical distance between qubit wires
51
52// -- UCSB-dark palette ---------------------------------------------------------
53static constexpr Color COL_BG = {0, 26, 48, 255}; // #001a30
54static constexpr Color COL_PANEL = {0, 30, 56, 255}; // slightly lighter
55static constexpr Color COL_WIRE = {80, 120, 160, 255};
56static constexpr Color COL_GOLD = {254, 188, 17, 255}; // UCSB gold
57static constexpr Color COL_TEXT = {232, 238, 244, 255};
58static constexpr Color COL_DIM = {120, 150, 180, 255};
59static constexpr Color COL_ACTIVE = {255, 255, 255, 255}; // active gate border
60
61// Gate category colours
62static Color gate_color(int kind, const std::string& label) {
63 if (kind == 2) return {180, 60, 180, 255}; // SWAP: purple
64 if (kind == 3) return { 40, 160, 160, 255}; // Toffoli/Fredkin: teal
65 if (kind == 1) return { 40, 160, 160, 255}; // controlled: teal
66 // Single-qubit by name
67 if (label == "H") return {254, 188, 17, 255}; // gold
68 if (label == "X" || label == "(+)") return {210, 60, 60, 255}; // red
69 if (label == "Y") return { 60, 180, 60, 255}; // green
70 if (label == "Z") return { 60, 100, 210, 255}; // blue
71 if (label == "S" || label == "S†") return {200, 120, 40, 255}; // orange
72 if (label == "T" || label == "T†") return {200, 120, 40, 255}; // orange
73 if (label[0] == 'R') return {140, 60, 200, 255}; // purple
74 return {100, 140, 180, 255}; // default
75}
76
77// -- Preset circuits -----------------------------------------------------------
78struct Preset { std::string name; Circuit circuit; };
79
80static std::vector<Preset> make_presets() {
81 const real pi = 3.141592653589793;
82 std::vector<Preset> p;
83
84 // 1. Bell State
85 {
86 Circuit c(2);
87 c.h(0).cx(0, 1);
88 p.push_back({"Bell State |Phi+>", std::move(c)});
89 }
90 // 2. GHZ State
91 {
92 Circuit c(3);
93 c.h(0).cx(0, 1).cx(0, 2);
94 p.push_back({"GHZ State", std::move(c)});
95 }
96 // 3. Grover Search -- 2 qubits, oracle marks |11>
97 {
98 Circuit c(2);
99 c.h(0).h(1) // uniform superposition
100 .cz(0, 1) // oracle: phase flip |11>
101 .h(0).h(1).x(0).x(1).cz(0,1).x(0).x(1).h(0).h(1); // diffusion
102 p.push_back({"Grover Search |11>", std::move(c)});
103 }
104 // 4. Quantum Teleportation -- teleports RY(pi/3)|0> from q0 to q2
105 {
106 Circuit c(3);
107 c.ry(pi / 3, 0) // prepare state on q0
108 .h(1).cx(1, 2) // Bell pair between q1 and q2
109 .cx(0, 1).h(0) // Alice's encoding
110 .cx(1, 2).cz(0, 2); // Bob's corrections
111 p.push_back({"Quantum Teleportation", std::move(c)});
112 }
113 // 5. QFT_3 on |001>
114 {
115 Circuit c(3);
116 c.x(0) // prepare |001> (q0=1)
117 .h(2).cp(pi/2, 1, 2).cp(pi/4, 0, 2)
118 .h(1).cp(pi/2, 0, 1)
119 .h(0)
120 .swap(0, 2);
121 p.push_back({"QFT\xe2\x82\x83 on |001\xe2\x9f\xa9", std::move(c)});
122 }
123
124 return p;
125}
126
127// -- Drawing helpers -----------------------------------------------------------
128
129static void draw_panel(int x, int y, int w, int h, const char* title) {
130 DrawRectangle(x, y, w, h, COL_PANEL);
131 DrawRectangleLinesEx({(float)x,(float)y,(float)w,(float)h}, 1, COL_WIRE);
132 if (title && title[0])
133 DrawText(title, x + 8, y + 6, 16, COL_DIM);
134}
135
136static void draw_gate_box(int cx, int cy, int w, int h,
137 Color fill, const char* label, bool active) {
138 int x = cx - w/2, y = cy - h/2;
139 DrawRectangle(x, y, w, h, fill);
140 if (active)
141 DrawRectangleLinesEx({(float)x-1,(float)y-1,(float)(w+2),(float)(h+2)}, 2, COL_ACTIVE);
142 else
143 DrawRectangleLinesEx({(float)x,(float)y,(float)w,(float)h}, 1, {0,0,0,80});
144
145 int font = (int(strlen(label)) > 2) ? 12 : 16;
146 int tw = MeasureText(label, font);
147 DrawText(label, cx - tw/2, cy - font/2, font, COL_TEXT);
148}
149
150// Draw the circuit for the given gate views up to step `current_step`.
151// active_gate is highlighted with a white border.
152static void draw_circuit(const std::vector<GateView>& views, int n_qubits,
153 int current_step, int active_gate,
154 int origin_x, int origin_y, int panel_w) {
155 if (views.empty()) return;
156
157 // Compute total columns
158 int total_cols = 0;
159 for (const auto& v : views) total_cols = std::max(total_cols, v.col + 1);
160
161 // Wire y-positions
162 auto wire_y = [&](int q) { return origin_y + 40 + q * WIRE_PITCH; };
163 // Gate x-position
164 auto gate_x = [&](int col) { return origin_x + 50 + col * GATE_W; };
165
166 // Draw qubit labels and wires
167 for (int q = 0; q < n_qubits; ++q) {
168 int wy = wire_y(q);
169 char label[16]; std::snprintf(label, sizeof(label), "q[%d]", q);
170 DrawText(label, origin_x + 4, wy - 8, 16, COL_DIM);
171 // Wire extends across all columns
172 int wire_end_x = gate_x(total_cols);
173 DrawLine(origin_x + 46, wy, std::min(wire_end_x + 10, origin_x + panel_w - 10), wy, COL_WIRE);
174 }
175
176 // Draw gates
177 for (int i = 0; i < (int)views.size(); ++i) {
178 const auto& v = views[i];
179 bool applied = (i < current_step);
180 bool active = (i == active_gate);
181 Color base = gate_color(v.kind, v.label);
182 if (!applied) {
183 // Dim unplayed gates
184 base.r = (unsigned char)(base.r * 0.4f);
185 base.g = (unsigned char)(base.g * 0.4f);
186 base.b = (unsigned char)(base.b * 0.4f);
187 }
188 int gx = gate_x(v.col);
189
190 if (v.kind == 0) {
191 // Single-qubit gate box
192 draw_gate_box(gx, wire_y(v.q0), GATE_W - 8, GATE_H, base, v.label.c_str(), active);
193 } else if (v.kind == 1) {
194 // Controlled gate: control dot + vertical line + target symbol
195 int cy_ctrl = wire_y(v.q0);
196 int cy_tgt = wire_y(v.q1);
197 // Vertical line
198 DrawLine(gx, std::min(cy_ctrl, cy_tgt), gx, std::max(cy_ctrl, cy_tgt),
199 applied ? COL_WIRE : COL_DIM);
200 // Control dot
201 DrawCircle(gx, cy_ctrl, 7, applied ? base : COL_DIM);
202 if (active) DrawCircleLines(gx, cy_ctrl, 9, COL_ACTIVE);
203 // Target symbol ((+) for CX, box for CZ/CP/CY)
204 const char* tgt_sym = (v.label == "CX") ? "\xe2\x8a\x95" : // (+)
205 (v.label == "CZ") ? "Z" :
206 (v.label == "CY") ? "Y" : "P";
207 draw_gate_box(gx, cy_tgt, GATE_W - 8, GATE_H, base, tgt_sym, active);
208 } else if (v.kind == 2) {
209 // SWAP: two x symbols + vertical line
210 int cy0 = wire_y(v.q0), cy1 = wire_y(v.q1);
211 DrawLine(gx, cy0, gx, cy1, applied ? COL_WIRE : COL_DIM);
212 draw_gate_box(gx, cy0, GATE_W - 8, GATE_H, base, "\xc3\x97", active); // x
213 draw_gate_box(gx, cy1, GATE_W - 8, GATE_H, base, "\xc3\x97", active);
214 } else {
215 // 3-qubit gate (Toffoli / Fredkin)
216 int c0 = wire_y(v.q0), c1 = wire_y(v.q1), c2 = wire_y(v.q2);
217 int y_lo = std::min({c0,c1,c2}), y_hi = std::max({c0,c1,c2});
218 DrawLine(gx, y_lo, gx, y_hi, applied ? COL_WIRE : COL_DIM);
219 DrawCircle(gx, c0, 7, applied ? base : COL_DIM);
220 DrawCircle(gx, c1, 7, applied ? base : COL_DIM);
221 draw_gate_box(gx, c2, GATE_W - 8, GATE_H, base, "\xe2\x8a\x95", active);
222 if (active) {
223 DrawCircleLines(gx, c0, 9, COL_ACTIVE);
224 DrawCircleLines(gx, c1, 9, COL_ACTIVE);
225 }
226 }
227 }
228}
229
230// Draw probability histogram
231static void draw_histogram(const quantum::Statevector& sv, int n_qubits,
232 int ox, int oy, int panel_w, int panel_h) {
233 int N = 1 << n_qubits;
234 auto probs = quantum::probabilities(sv);
235
236 // Usable area
237 int ax = ox + 10, aw = panel_w - 20;
238 int ay = oy + 30, ah = panel_h - 80;
239
240 int bar_w = std::max(4, (aw - N * 2) / N);
241 int spacing = aw / N;
242
243 for (int k = 0; k < N; ++k) {
244 float p = (float)probs[k];
245 int bar_h = (int)(p * ah);
246 int bx = ax + k * spacing;
247 int by = ay + ah - bar_h;
248
249 // Bar colour: gold if dominant, teal otherwise
250 Color col = (p > 0.4f) ? COL_GOLD : Color{40,160,160,255};
251 if (p < 0.001f) col = {40, 60, 80, 255};
252
253 DrawRectangle(bx, by, bar_w, bar_h, col);
254 DrawRectangleLinesEx({(float)bx,(float)(ay),(float)bar_w,(float)ah}, 1, {60,80,100,120});
255
256 // Label below
257 std::string lbl(n_qubits, '0');
258 for (int b = 0; b < n_qubits; ++b)
259 lbl[n_qubits-1-b] = ((k >> b) & 1) ? '1' : '0';
260 int font = (n_qubits <= 3) ? 14 : 11;
261 DrawText(("|" + lbl + "\xe2\x9f\xa9").c_str(), bx, ay + ah + 4, font, COL_DIM);
262
263 // Probability text above bar
264 if (p > 0.02f) {
265 char pct[8]; std::snprintf(pct, sizeof(pct), "%3.0f%%", p*100.f);
266 DrawText(pct, bx, by - 18, 12, COL_TEXT);
267 }
268 }
269}
270
271// -- main ---------------------------------------------------------------------
272
273int main() {
274 SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_WINDOW_HIGHDPI);
275 InitWindow(WIN_W, WIN_H, "Quantum Circuit Demo -- numerics");
276 SetTargetFPS(60);
277
278 auto presets = make_presets();
279 int preset_idx = 0;
280 int step = 0; // gates applied so far
281 bool auto_step = false;
282 double auto_timer = 0.0;
283 const double AUTO_INTERVAL = 0.55;
284
285 auto load_preset = [&](int idx) {
286 preset_idx = idx;
287 step = 0;
288 auto_step = false;
289 };
290
291 load_preset(0);
292
293 while (!WindowShouldClose()) {
294 // -- Input ------------------------------------------------------------
295 const Circuit& circ = presets[preset_idx].circuit;
296 int n_gates = circ.n_gates();
297 int n_qubits = circ.n_qubits();
298
299 if (IsKeyPressed(KEY_RIGHT) || IsKeyPressed(KEY_L))
300 step = std::min(step + 1, n_gates);
301 if (IsKeyPressed(KEY_LEFT) || IsKeyPressed(KEY_H))
302 step = std::max(step - 1, 0);
303 if (IsKeyPressed(KEY_R)) step = 0;
304 if (IsKeyPressed(KEY_SPACE)) auto_step = !auto_step;
305
306 for (int i = 0; i < 5; ++i)
307 if (IsKeyPressed(KEY_ONE + i)) load_preset(i);
308
309 if (auto_step) {
310 auto_timer += GetFrameTime();
311 if (auto_timer >= AUTO_INTERVAL) {
312 auto_timer = 0.0;
313 if (step < n_gates) ++step;
314 else auto_step = false;
315 }
316 }
317
318 // -- State -------------------------------------------------------------
319 auto sv = circ.statevector_at(step);
320 auto views = circ.views();
321 int active = (step > 0 && step <= n_gates) ? step - 1 : -1;
322
323 // -- Draw --------------------------------------------------------------
324 BeginDrawing();
325 ClearBackground(COL_BG);
326
327 // Title bar
328 DrawText("QUANTUM CIRCUIT DEMO", 12, 12, 22, COL_GOLD);
329 DrawText(presets[preset_idx].name.c_str(), 12, 40, 18, COL_TEXT);
330
331 // Step counter (top right)
332 char step_buf[40];
333 std::snprintf(step_buf, sizeof(step_buf), "STEP %d / %d", step, n_gates);
334 int sw = MeasureText(step_buf, 18);
335 DrawText(step_buf, WIN_W - sw - 12, 40, 18,
336 (step == n_gates) ? COL_GOLD : COL_DIM);
337
338 // Auto indicator
339 if (auto_step) DrawText("[AUTO]", WIN_W - 90, 12, 16, {100,210,100,255});
340
341 // Circuit panel
342 draw_panel(CIR_X, CIR_Y, CIR_W, CIR_H, "Circuit");
343 draw_circuit(views, n_qubits, step, active,
344 CIR_X + 4, CIR_Y + 20, CIR_W - 8);
345
346 // Histogram panel
347 draw_panel(HIS_X, HIS_Y, HIS_W, HIS_H, "Measurement Probabilities");
348 draw_histogram(sv, n_qubits, HIS_X + 4, HIS_Y + 20, HIS_W - 8, HIS_H - 30);
349
350 // Stats strip (below circuit panel)
351 int sy = CIR_Y + CIR_H + 10;
352 DrawRectangle(CIR_X, sy, CIR_W, 80, COL_PANEL);
353 DrawRectangleLinesEx({(float)CIR_X,(float)sy,(float)CIR_W,80}, 1, COL_WIRE);
354
355 // Most likely state
356 auto probs = quantum::probabilities(sv);
357 int best_k = (int)(std::max_element(probs.begin(), probs.end()) - probs.begin());
358 std::string best_lbl(n_qubits, '0');
359 for (int b = 0; b < n_qubits; ++b)
360 best_lbl[n_qubits-1-b] = ((best_k >> b) & 1) ? '1' : '0';
361 char stat_buf[64];
362 std::snprintf(stat_buf, sizeof(stat_buf),
363 "Most likely: |%s\xe2\x9f\xa9 (%.1f%%)", // >
364 best_lbl.c_str(), probs[best_k]*100.0);
365 DrawText(stat_buf, CIR_X + 10, sy + 8, 16, COL_TEXT);
366
367 // Entanglement entropy of qubit 0
368 real S = quantum::entanglement_entropy(sv, n_qubits, 0);
369 char ent_buf[48];
370 std::snprintf(ent_buf, sizeof(ent_buf), "S(q[0]) = %.3f ebit%s",
371 S, S > 0.999 ? " (max entangled)" : "");
372 DrawText(ent_buf, CIR_X + 10, sy + 30, 16,
373 S > 0.5 ? COL_GOLD : COL_DIM);
374
375 // Norm (sanity check)
376 real nrm = quantum::norm(sv);
377 char nrm_buf[32];
378 std::snprintf(nrm_buf, sizeof(nrm_buf), "|\u03c8| = %.6f", nrm);
379 DrawText(nrm_buf, CIR_X + 10, sy + 52, 14, COL_DIM);
380
381 // Stats strip (below histogram panel)
382 DrawRectangle(HIS_X, sy, HIS_W, 80, COL_PANEL);
383 DrawRectangleLinesEx({(float)HIS_X,(float)sy,(float)HIS_W,80}, 1, COL_WIRE);
384 DrawText("All amplitudes", HIS_X + 10, sy + 8, 14, COL_DIM);
385
386 int N = 1 << n_qubits;
387 int row = 0;
388 for (int k = 0; k < N && row < 3; ++k) {
389 if (std::abs(sv[k]) < 0.005) continue;
390 std::string lbl(n_qubits, '0');
391 for (int b = 0; b < n_qubits; ++b)
392 lbl[n_qubits-1-b] = ((k >> b) & 1) ? '1' : '0';
393 char amp[64];
394 std::snprintf(amp, sizeof(amp), "|%s\xe2\x9f\xa9 %+.3f%+.3fi",
395 lbl.c_str(), (float)sv[k].real(), (float)sv[k].imag());
396 DrawText(amp, HIS_X + 10, sy + 24 + row * 18, 14, COL_TEXT);
397 ++row;
398 }
399 if (row == 3 && N > 4)
400 DrawText("...", HIS_X + 10, sy + 62, 14, COL_DIM);
401
402 // Controls bar (bottom)
403 int ctrl_y = WIN_H - 38;
404 DrawRectangle(0, ctrl_y, WIN_W, 38, {0, 14, 28, 255});
405 DrawLine(0, ctrl_y, WIN_W, ctrl_y, COL_WIRE);
406 DrawText("[<-/->] Step [SPACE] Auto [R] Reset [1-5] Load Circuit",
407 12, ctrl_y + 10, 16, COL_DIM);
408
409 // Preset selector highlights
410 for (int i = 0; i < 5; ++i) {
411 char key[4]; std::snprintf(key, sizeof(key), "[%d]", i+1);
412 int kx = WIN_W - 300 + i * 56;
413 DrawText(key, kx, ctrl_y + 10, 16,
414 (i == preset_idx) ? COL_GOLD : COL_DIM);
415 }
416
417 EndDrawing();
418 }
419
420 CloseWindow();
421 return 0;
422}
Fluent quantum circuit builder and statevector simulator.
Dense vector with optional GPU storage, templated over scalar type T.
Definition vector.hpp:23
Chainable quantum circuit builder.
Definition circuit.hpp:78
std::vector< GateView > views() const
Gate descriptions with layout columns – for renderers.
Definition circuit.cpp:217
int n_qubits() const
Definition circuit.hpp:139
quantum::Statevector statevector_at(int n) const
Apply first n gates only (0 = bare |0...0>). Useful for stepping through the circuit in a visualiser.
Definition circuit.cpp:159
int n_gates() const
Definition circuit.hpp:140
real norm(const Statevector &sv)
L2 norm of the statevector (should be 1 for a valid quantum state)
Gate gate_x()
Pauli-X (NOT)
std::vector< real > probabilities(const Statevector &sv)
Measurement probability for each basis state: p_i = |a_i|^2.
real entanglement_entropy(const Statevector &sv, int n_qubits, int qubit)
Von Neumann entropy of a single qubit's reduced density matrix.
double real
Definition types.hpp:10
std::size_t idx
Definition types.hpp:11
constexpr real pi
Definition math.hpp:40
int main()
Definition main.cpp:273
Statevector-based quantum circuit simulator.
std::string name
Definition main.cpp:78
Circuit circuit
Definition main.cpp:78