From 17598056a69a14e0390a07251d383f413ded9eea Mon Sep 17 00:00:00 2001 From: hachem Date: Fri, 18 Sep 2026 12:25:32 +0200 Subject: feat: add matrix, vector, and circuit display + fmt --- .clang-format | 2 +- STYLE.md | 274 ------------ include/core/circuit.h | 91 ++-- include/core/classical_components.h | 10 +- include/core/custom_gate.h | 56 +-- include/core/kernel.h | 58 +-- include/core/noise.h | 18 +- include/core/quantum_components.h | 18 +- include/core/runtime.h | 26 +- include/maths/complex.h | 4 +- include/maths/format.h | 6 + include/maths/matrix.h | 8 +- include/maths/simd.h | 8 +- include/maths/vector.h | 22 +- src/core/circuit.c | 460 +++++++++++--------- src/core/circuit.rs | 477 --------------------- src/core/classical_components.c | 32 +- src/core/custom_gate.c | 220 +++++----- src/core/gates.c | 244 +++++------ src/core/kernel.c | 820 ++++++++++++++++++------------------ src/core/noise.c | 610 +++++++++++++-------------- src/core/quantum_components.c | 346 +++++++-------- src/core/runtime.c | 490 ++++++++++----------- src/maths/complex.c | 122 +++--- src/maths/format.c | 293 +++++++------ src/maths/matrix.c | 192 ++++----- src/maths/matrix.rs | 310 -------------- src/maths/simd.c | 496 +++++++++++----------- src/maths/vector.c | 136 +++--- src/maths/vector.rs | 258 ------------ src/psi.c | 2 +- src/visualizer/grid.c | 160 +++---- src/visualizer/grid.h | 10 +- src/visualizer/horizontal_cli.c | 600 +++++++++++++------------- src/visualizer/vertical_cli.c | 594 +++++++++++++------------- tester/clifford.c | 108 ++--- tester/custom_gates.c | 54 +-- tester/kernels.c | 76 ++-- tester/main.c | 56 +-- tester/noise.c | 74 ++-- tester/non_clifford.c | 58 +-- tester/simd.c | 108 ++--- tester/test.c | 84 ++-- 43 files changed, 3447 insertions(+), 4644 deletions(-) delete mode 100644 STYLE.md delete mode 100644 src/core/circuit.rs delete mode 100644 src/maths/matrix.rs delete mode 100644 src/maths/vector.rs diff --git a/.clang-format b/.clang-format index 530613b..e7d632d 100644 --- a/.clang-format +++ b/.clang-format @@ -2,7 +2,7 @@ Language: Cpp BasedOnStyle: LLVM IndentWidth: 4 TabWidth: 4 -UseTab: AlignWithSpaces +UseTab: Never ContinuationIndentWidth: 8 ColumnLimit: 100 BreakBeforeBraces: Allman diff --git a/STYLE.md b/STYLE.md deleted file mode 100644 index 856287f..0000000 --- a/STYLE.md +++ /dev/null @@ -1,274 +0,0 @@ -# psi C style guide - -This document describes the conventions for the C implementation of `psi`. Formatting -is enforced by [`.clang-format`](.clang-format) and naming by [`.clang-tidy`](.clang-tidy); -everything a tool cannot check is described here and is expected in review. - -The guiding idea: **idiomatic, data-driven C that reads like the Rust original in naming**, -with Allman bracing as the one deliberate departure from typical C style. - ---- - -## 1. Language & build - -- **C17**, compiled with `-Wall -Wextra -Werror -pedantic`. Warnings are errors; keep every - build clean. -- Build system is **premake5** (`premake5 gmake && make config=release`). Public headers live - in `include/`, implementation in `src/`, the test runner in `tester/`. -- Portable across x86_64 and ARM64 (the only supported architectures). Platform-specific code - (SIMD intrinsics) is `#ifdef`-gated with a scalar fallback. - ---- - -## 2. Formatting (enforced by clang-format) - -Run before committing: - -```bash -find include src tester -name '*.c' -o -name '*.h' | xargs clang-format -i -``` - -CI check (no diffs allowed): - -```bash -find include src tester -name '*.c' -o -name '*.h' | xargs clang-format --dry-run -Werror -``` - -### Braces — Allman, everywhere - -Opening brace on its own line for functions, structs, enums, unions, and control blocks. - -```c -struct PsiComplex -{ - double real; - double imaginary; -}; - -double psi_abs_complex(struct PsiComplex z) -{ - return sqrt(psi_norm2_complex(z)); -} -``` - -**Exception (tool limitation):** compound literals keep the brace on the same line as the -type, because clang-format cannot break it: - -```c -return (struct PsiComplex){ - a.real + b.real, - a.imaginary + b.imaginary, -}; -``` - -### Single statements omit braces - -An `if`/`for`/`while` with a single-statement body has no braces; the statement goes on the -next line. - -```c -if (index >= row->width) - return; - -for (size_t i = 0; i < count; i++) - sum = psi_add_complex(sum, values[i]); -``` - -Use braces once a body has more than one statement. - -### Indentation — tabs - -Tabs for indentation (width 4). Alignment (e.g. wrapped arguments under an open paren) uses -spaces, so alignment survives any tab width. - -### Pointers bind to the type - -```c -struct PsiComplex* data; -void psi_free_vector(struct PsiVector* v); -const size_t* targets; -``` - -Not `struct PsiComplex *data`. - -### Line length & wrapping - -- 100-column limit. Long lines wrap; continuation indents two tabs, wrapped call/parameter - lists align under the opening paren. - -```c -static void execute_kernels(struct PsiVector* state, const struct PsiKernel* kernels, - size_t count, size_t num_qubits, struct PsiRuntimeConfig config) -{ - ... -} -``` - -### Includes - -Not sorted by the tool — order them yourself. Convention: system headers (`<...>`) first, a -blank line, then project headers (`"..."`). `include/psi.h` orders its headers by dependency -layer, not alphabetically. - ---- - -## 3. Naming (enforced by clang-tidy) - -| Kind | Convention | Example | -|------|-----------|---------| -| Structs | `Psi` + PascalCase | `struct PsiQuantumRegister` | -| Enums | `Psi` + PascalCase | `enum PsiGateType` | -| Enum constants | `UPPER_CASE` | `PSI_GATE_H`, `PSI_SIMD_NEON` | -| Functions | `snake_case` | `psi_add_complex`, `apply_kernel` | -| Parameters / variables | `snake_case` | `num_qubits`, `target_bit` | -| Macros / compile-time consts | `THIS_CASE` | `PSI_VERSION_MAJOR`, `INV_SQRT_2` | -| Function-like macros | `psi_` + `snake_case` | `psi_matrix`, `psi_column_vector` | - -### Library prefix - -Every **public** symbol is namespaced: - -- functions → `psi_...` -- structs → `Psi...` -- macros / constants → `PSI_...` - -File-local `static` helpers are **not** prefixed (`apply_pair`, `build_pairs`, `op_to_kernel`). - -### Data-driven, action-first (not OOP) - -The operation leads and the data is an argument — think free functions over data, not methods -bound to a type. `psi__(...)`: - -```c -psi_add_complex(a, b) /* not psi_complex_add */ -psi_new_quantum_gate(...) /* not gate_new(...) */ -psi_apply_gate(®, ...) /* verb first */ -``` - -*Not enforceable by tooling — maintained in review.* - ---- - -## 4. Types - -- **`double` only** for floating point. No `float`, no fixed-width float aliases (their sizes - are not guaranteed across platforms). -- **Fixed-width ``** types (`uint32_t`, `int64_t`, …) over `int`/`unsigned`/`long`. -- **`size_t`** for sizes, counts, and indices (the Rust `usize`). -- **No `typedef` on structs, enums, or unions** — always spell `struct PsiComplex`, - `enum PsiGateType`. - ---- - -## 5. Memory & ownership - -There is no garbage collector; ownership is explicit and follows a few rules. - -- Every type that owns a heap allocation has a matching `psi_new_*` / `psi_free_*` pair, and - `psi_free_*` takes a pointer and nulls the freed fields. - -```c -struct PsiVector v = psi_new_vector(4, PSI_COLUMN_VECTOR); -... -psi_free_vector(&v); -``` - -- **The caller frees.** Functions that allocate and return a value (`psi_add_vector`, - `psi_clone_matrix`, the gate factories, the renderers' returned strings) transfer ownership - to the caller. -- **Read-only args are passed by value**, which shares the underlying buffer and never frees - it. Mutators and destructors take a pointer. -- **Constructors that take ownership** consume what they are handed (e.g. a gate takes - ownership of its matrix; `psi_apply_custom` takes ownership of the custom gate). Builders - that take an existing value they should not consume make a copy (`psi_new_vector_from`, - `psi_new_quantum_register_from`). -- **Allocation idiom:** `sizeof *ptr`, not the repeated type. - -```c -struct PsiKernel* out = malloc(count * sizeof *out); -``` - -- Verify with sanitizers during development: - -```bash -clang -std=c17 -Iinclude -Isrc -Itester -fsanitize=address,undefined -g \ - src/psi.c src/**/*.c tester/*.c -o /tmp/psi -lm && /tmp/psi -``` - ---- - -## 6. Error handling - -- Preconditions and programmer errors (dimension mismatches, out-of-range indices) use - `assert`. The Rust original returned `Option`/panicked; in C these are `assert`s, since they - indicate caller bugs rather than recoverable conditions. -- `malloc`/`calloc`/`realloc` results are `assert`ed non-NULL (with `|| size == 0` where a - zero-size allocation is legal). - ---- - -## 7. Comments - -- **Avoid narration.** Code should read on its own; do not annotate ported steps or restate - what a line does. -- **Short math-formula comments are welcome** where they clarify an expression: - -```c -struct PsiComplex psi_mul_complex(struct PsiComplex a, struct PsiComplex b) -{ - // (a + bi)(c + di) = (ac - bd) + (ad + bc)i - return (struct PsiComplex){ - a.real * b.real - a.imaginary * b.imaginary, - a.real * b.imaginary + a.imaginary * b.real, - }; -} -``` - -*Not enforceable by tooling — maintained in review.* - ---- - -## 8. Project layout - -``` -include/ public API (mirrors src/ subtree) - psi.h umbrella header — includes the whole public API - maths/ complex, vector, matrix, format, simd - core/ quantum_components, gates, custom_gate, classical_components, - circuit, kernel, runtime, noise - visualizer/ renderer -src/ implementation, same subtree; may also hold private headers -tester/ assertion-based test suite -``` - -- Headers are `#pragma once` and included **root-relative**: `#include "maths/complex.h"`. -- `include/psi.h` is the umbrella: `#include ` pulls in the entire public API. Add each - new module's public header to it. -- Private, implementation-only headers live under `src/` (e.g. `src/visualizer/grid.h`). - ---- - -## 9. Testing - -- `tester/` is an assertion suite with subcommands - (`clifford`, `non-clifford`, `custom`, `kernels`, `simd`, `noise`, `all`, `help`). -- Tests assert against known states (Bell, GHZ, rotations, fusion, noise purity, …) and check - that all runtimes agree. The process exit code reflects pass/fail (CI-friendly). - -```bash -premake5 gmake && make config=release -./bin/release-/tester # all suites -./bin/release-/tester noise # one suite -``` - ---- - -## Tooling summary - -| Concern | Tool | Command | -|---------|------|---------| -| Formatting | clang-format | `... | xargs clang-format --dry-run -Werror` | -| Naming | clang-tidy | `clang-tidy -- -std=c17 -Iinclude -Isrc` | -| Build | premake5 + make | `premake5 gmake && make config=release` | -| Tests | tester | `./bin/release-/tester` | -| Leaks / UB | sanitizers | build with `-fsanitize=address,undefined` | diff --git a/include/core/circuit.h b/include/core/circuit.h index 7c0d371..ce36b1c 100644 --- a/include/core/circuit.h +++ b/include/core/circuit.h @@ -2,53 +2,54 @@ #include #include +#include #include "core/custom_gate.h" #include "maths/vector.h" enum PsiGateKind { - PSI_GATE_H, - PSI_GATE_X, - PSI_GATE_Y, - PSI_GATE_Z, - PSI_GATE_S, - PSI_GATE_T, - PSI_GATE_SDG, - PSI_GATE_TDG, - PSI_GATE_SX, - PSI_GATE_SXDG, - PSI_GATE_RX, - PSI_GATE_RY, - PSI_GATE_RZ, - PSI_GATE_P, - PSI_GATE_U1, - PSI_GATE_U2, - PSI_GATE_U3, - PSI_GATE_CNOT, - PSI_GATE_CZ, - PSI_GATE_SWAP, - PSI_GATE_CRX, - PSI_GATE_CRY, - PSI_GATE_CRZ, - PSI_GATE_CP, - PSI_GATE_CCNOT, - PSI_GATE_CSWAP, - PSI_GATE_MEASURE, - PSI_GATE_CUSTOM, + PSI_GATE_H, + PSI_GATE_X, + PSI_GATE_Y, + PSI_GATE_Z, + PSI_GATE_S, + PSI_GATE_T, + PSI_GATE_SDG, + PSI_GATE_TDG, + PSI_GATE_SX, + PSI_GATE_SXDG, + PSI_GATE_RX, + PSI_GATE_RY, + PSI_GATE_RZ, + PSI_GATE_P, + PSI_GATE_U1, + PSI_GATE_U2, + PSI_GATE_U3, + PSI_GATE_CNOT, + PSI_GATE_CZ, + PSI_GATE_SWAP, + PSI_GATE_CRX, + PSI_GATE_CRY, + PSI_GATE_CRZ, + PSI_GATE_CP, + PSI_GATE_CCNOT, + PSI_GATE_CSWAP, + PSI_GATE_MEASURE, + PSI_GATE_CUSTOM, }; struct PsiGateOp { - enum PsiGateKind kind; - size_t qubits[3]; - size_t qubit_count; - double params[3]; - size_t param_count; - size_t classical; - struct PsiCustomGate* custom; - size_t* custom_targets; - size_t custom_target_count; + enum PsiGateKind kind; + size_t qubits[3]; + size_t qubit_count; + double params[3]; + size_t param_count; + size_t classical; + struct PsiCustomGate* custom; + size_t* custom_targets; + size_t custom_target_count; }; const char* psi_gate_op_name(struct PsiGateOp op); @@ -60,13 +61,13 @@ bool psi_gate_op_is_non_clifford(struct PsiGateOp op); struct PsiQuantumCircuit { - size_t num_qubits; - size_t num_classical; - struct PsiGateOp* operations; - size_t operation_count; - size_t operation_capacity; - struct PsiVector computed_state; - bool is_computed; + size_t num_qubits; + size_t num_classical; + struct PsiGateOp* operations; + size_t operation_count; + size_t operation_capacity; + struct PsiVector computed_state; + bool is_computed; }; struct PsiQuantumCircuit psi_new_quantum_circuit(size_t num_qubits); @@ -111,3 +112,5 @@ void psi_measure_all(struct PsiQuantumCircuit* c); void psi_apply_custom(struct PsiQuantumCircuit* c, struct PsiCustomGate gate, const size_t* targets, size_t count); + +void psi_print_circuit(const struct PsiQuantumCircuit* circuit, FILE* out); diff --git a/include/core/classical_components.h b/include/core/classical_components.h index 53c00ef..7c58ae3 100644 --- a/include/core/classical_components.h +++ b/include/core/classical_components.h @@ -5,15 +5,15 @@ struct PsiClassicalBit { - const char* name; - bool state; + const char* name; + bool state; }; struct PsiClassicalRegister { - const char* name; - struct PsiClassicalBit* bits; - size_t num_bits; + const char* name; + struct PsiClassicalBit* bits; + size_t num_bits; }; struct PsiClassicalBit psi_new_classical_bit(const char* name, bool state); diff --git a/include/core/custom_gate.h b/include/core/custom_gate.h index efda36f..7003699 100644 --- a/include/core/custom_gate.h +++ b/include/core/custom_gate.h @@ -7,46 +7,46 @@ enum PsiCompositeOp { - PSI_OP_H, - PSI_OP_X, - PSI_OP_Y, - PSI_OP_Z, - PSI_OP_S, - PSI_OP_T, - PSI_OP_CNOT, - PSI_OP_CZ, - PSI_OP_SWAP, - PSI_OP_CCNOT, - PSI_OP_CSWAP, + PSI_OP_H, + PSI_OP_X, + PSI_OP_Y, + PSI_OP_Z, + PSI_OP_S, + PSI_OP_T, + PSI_OP_CNOT, + PSI_OP_CZ, + PSI_OP_SWAP, + PSI_OP_CCNOT, + PSI_OP_CSWAP, }; struct PsiCompositeGateOp { - enum PsiCompositeOp op; - size_t targets[3]; - size_t target_count; + enum PsiCompositeOp op; + size_t targets[3]; + size_t target_count; }; enum PsiCustomGateKind { - PSI_CUSTOM_GATE_MATRIX, - PSI_CUSTOM_GATE_COMPOSITE, + PSI_CUSTOM_GATE_MATRIX, + PSI_CUSTOM_GATE_COMPOSITE, }; struct PsiCustomGate { - const char* name; - size_t num_qubits; - enum PsiCustomGateKind kind; - union - { - struct PsiMatrix matrix; - struct - { - struct PsiCompositeGateOp* ops; - size_t count; - } composite; - } definition; + const char* name; + size_t num_qubits; + enum PsiCustomGateKind kind; + union + { + struct PsiMatrix matrix; + struct + { + struct PsiCompositeGateOp* ops; + size_t count; + } composite; + } definition; }; struct PsiCustomGate psi_new_custom_gate_from_matrix(const char* name, struct PsiMatrix matrix); diff --git a/include/core/kernel.h b/include/core/kernel.h index b8b19ea..f3a2b77 100644 --- a/include/core/kernel.h +++ b/include/core/kernel.h @@ -8,18 +8,18 @@ enum PsiGateType { - PSI_GATE_TYPE_DIAGONAL, - PSI_GATE_TYPE_NON_DIAGONAL, - PSI_GATE_TYPE_CONTROLLED, + PSI_GATE_TYPE_DIAGONAL, + PSI_GATE_TYPE_NON_DIAGONAL, + PSI_GATE_TYPE_CONTROLLED, }; struct PsiKernel { - struct PsiMatrix matrix; - size_t* targets; - size_t target_count; - char* name; - enum PsiGateType gate_type; + struct PsiMatrix matrix; + size_t* targets; + size_t target_count; + char* name; + enum PsiGateType gate_type; }; struct PsiKernel psi_new_kernel(const char* name, struct PsiMatrix matrix, const size_t* targets, @@ -34,10 +34,10 @@ bool psi_fuse_kernels(struct PsiKernel a, struct PsiKernel b, struct PsiKernel* struct PsiKernelBatch { - struct PsiKernel* kernels; - size_t count; - size_t capacity; - size_t num_qubits; + struct PsiKernel* kernels; + size_t count; + size_t capacity; + size_t num_qubits; }; struct PsiKernelBatch psi_new_kernel_batch(size_t num_qubits); @@ -49,31 +49,31 @@ void psi_apply_kernel(struct PsiVector* state, struct PsiKernel kernel, size_t n struct PsiExecutionLayer { - struct PsiKernel* kernels; - size_t count; - size_t capacity; + struct PsiKernel* kernels; + size_t count; + size_t capacity; }; struct PsiKernelStats { - size_t total_kernels; - size_t single_qubit; - size_t two_qubit; - size_t multi_qubit; - size_t diagonal; - size_t execution_layers; + size_t total_kernels; + size_t single_qubit; + size_t two_qubit; + size_t multi_qubit; + size_t diagonal; + size_t execution_layers; }; struct PsiStructureAwareBatch { - struct PsiKernel* kernels; - size_t count; - size_t capacity; - struct PsiExecutionLayer* layers; - size_t layer_count; - size_t layer_capacity; - size_t num_qubits; - bool optimised; + struct PsiKernel* kernels; + size_t count; + size_t capacity; + struct PsiExecutionLayer* layers; + size_t layer_count; + size_t layer_capacity; + size_t num_qubits; + bool optimised; }; struct PsiStructureAwareBatch psi_new_structure_aware_batch(size_t num_qubits); diff --git a/include/core/noise.h b/include/core/noise.h index 320a4ab..2bd65e1 100644 --- a/include/core/noise.h +++ b/include/core/noise.h @@ -8,8 +8,8 @@ struct PsiKrausOperator { - struct PsiMatrix matrix; - const char* name; + struct PsiMatrix matrix; + const char* name; }; struct PsiKrausOperator psi_new_kraus_operator(const char* name, struct PsiMatrix matrix); @@ -17,10 +17,10 @@ void psi_free_kraus_operator(struct PsiKrausOperator* op); struct PsiNoiseChannel { - const char* name; - struct PsiKrausOperator* operators; - size_t operator_count; - size_t num_qubits; + const char* name; + struct PsiKrausOperator* operators; + size_t operator_count; + size_t num_qubits; }; struct PsiNoiseChannel psi_new_noise_channel(const char* name, @@ -38,9 +38,9 @@ struct PsiNoiseChannel psi_generalised_amplitude_damping_channel(double p, doubl struct PsiDensityMatrix { - struct PsiComplex* data; - size_t dim; - size_t num_qubits; + struct PsiComplex* data; + size_t dim; + size_t num_qubits; }; struct PsiDensityMatrix psi_new_density_matrix(size_t num_qubits); diff --git a/include/core/quantum_components.h b/include/core/quantum_components.h index 63797cf..815e2d9 100644 --- a/include/core/quantum_components.h +++ b/include/core/quantum_components.h @@ -10,9 +10,9 @@ struct PsiVector psi_new_state_1(void); struct PsiQuantumGate { - const char* name; - struct PsiMatrix matrix; - size_t num_qubits; + const char* name; + struct PsiMatrix matrix; + size_t num_qubits; }; struct PsiQuantumGate psi_new_quantum_gate(const char* name, struct PsiMatrix matrix, @@ -22,8 +22,8 @@ void psi_free_quantum_gate(struct PsiQuantumGate* gate); struct PsiQuantumBit { - const char* name; - struct PsiVector state; + const char* name; + struct PsiVector state; }; struct PsiQuantumBit psi_new_quantum_bit(const char* name, struct PsiVector state); @@ -31,10 +31,10 @@ void psi_free_quantum_bit(struct PsiQuantumBit* bit); struct PsiQuantumRegister { - const char* name; - struct PsiVector state_vector; - struct PsiQuantumBit* qubits; - size_t num_qubits; + const char* name; + struct PsiVector state_vector; + struct PsiQuantumBit* qubits; + size_t num_qubits; }; struct PsiQuantumRegister psi_new_quantum_register(const char* name, const char** names, diff --git a/include/core/runtime.h b/include/core/runtime.h index 2a6f739..cf03371 100644 --- a/include/core/runtime.h +++ b/include/core/runtime.h @@ -10,23 +10,23 @@ struct PsiRuntimeConfig { - bool parallel; - bool simd; - bool batched; - bool structure_aware; - size_t parallel_threshold; + bool parallel; + bool simd; + bool batched; + bool structure_aware; + size_t parallel_threshold; }; enum PsiRuntime { - PSI_RUNTIME_BASIC, - PSI_RUNTIME_BASIC_MT, - PSI_RUNTIME_BATCHED, - PSI_RUNTIME_BATCHED_MT, - PSI_RUNTIME_SIMD, - PSI_RUNTIME_SIMD_MT, - PSI_RUNTIME_STRUCTURE_AWARE, - PSI_RUNTIME_STRUCTURE_AWARE_MT, + PSI_RUNTIME_BASIC, + PSI_RUNTIME_BASIC_MT, + PSI_RUNTIME_BATCHED, + PSI_RUNTIME_BATCHED_MT, + PSI_RUNTIME_SIMD, + PSI_RUNTIME_SIMD_MT, + PSI_RUNTIME_STRUCTURE_AWARE, + PSI_RUNTIME_STRUCTURE_AWARE_MT, }; struct PsiRuntimeConfig psi_new_runtime_config(void); diff --git a/include/maths/complex.h b/include/maths/complex.h index c94f64a..d2782eb 100644 --- a/include/maths/complex.h +++ b/include/maths/complex.h @@ -2,8 +2,8 @@ struct PsiComplex { - double real; - double imaginary; + double real; + double imaginary; }; struct PsiComplex psi_new_complex(double real, double imaginary); diff --git a/include/maths/format.h b/include/maths/format.h index a11e3be..3b746a5 100644 --- a/include/maths/format.h +++ b/include/maths/format.h @@ -1,8 +1,14 @@ #pragma once #include +#include #include "maths/complex.h" +#include "maths/matrix.h" +#include "maths/vector.h" char* psi_format_amplitude(struct PsiComplex c, char* out, size_t cap); char* psi_format_probability(double p, char* out, size_t cap); + +void psi_print_matrix(struct PsiMatrix m, FILE* out); +void psi_print_vector(struct PsiVector v, FILE* out); diff --git a/include/maths/matrix.h b/include/maths/matrix.h index ff996d6..87bdcee 100644 --- a/include/maths/matrix.h +++ b/include/maths/matrix.h @@ -7,9 +7,9 @@ struct PsiMatrix { - struct PsiComplex* data; - size_t rows; - size_t cols; + struct PsiComplex* data; + size_t rows; + size_t cols; }; struct PsiMatrix psi_new_matrix(size_t rows, size_t cols); @@ -19,7 +19,7 @@ struct PsiMatrix psi_clone_matrix(struct PsiMatrix m); void psi_free_matrix(struct PsiMatrix* m); #define psi_matrix(rows, cols, ...) \ - psi_new_matrix_from((struct PsiComplex[]){ __VA_ARGS__ }, rows, cols) + psi_new_matrix_from((struct PsiComplex[]){ __VA_ARGS__ }, rows, cols) struct PsiComplex psi_get_matrix(struct PsiMatrix m, size_t row, size_t col); void psi_set_matrix(struct PsiMatrix* m, size_t row, size_t col, struct PsiComplex value); diff --git a/include/maths/simd.h b/include/maths/simd.h index 38d0171..5533913 100644 --- a/include/maths/simd.h +++ b/include/maths/simd.h @@ -6,10 +6,10 @@ enum PsiSimdCapability { - PSI_SIMD_NONE, - PSI_SIMD_AVX2, - PSI_SIMD_AVX512, - PSI_SIMD_NEON, + PSI_SIMD_NONE, + PSI_SIMD_AVX2, + PSI_SIMD_AVX512, + PSI_SIMD_NEON, }; enum PsiSimdCapability psi_detect_simd(void); diff --git a/include/maths/vector.h b/include/maths/vector.h index 0136715..a9ddf1f 100644 --- a/include/maths/vector.h +++ b/include/maths/vector.h @@ -6,15 +6,15 @@ enum PsiVectorKind { - PSI_ROW_VECTOR, - PSI_COLUMN_VECTOR, + PSI_ROW_VECTOR, + PSI_COLUMN_VECTOR, }; struct PsiVector { - struct PsiComplex* data; - size_t size; - enum PsiVectorKind kind; + struct PsiComplex* data; + size_t size; + enum PsiVectorKind kind; }; struct PsiVector psi_new_vector(size_t size, enum PsiVectorKind kind); @@ -24,13 +24,13 @@ struct PsiVector psi_clone_vector(struct PsiVector v); void psi_free_vector(struct PsiVector* v); #define psi_row_vector(...) \ - psi_new_vector_from((struct PsiComplex[]){ __VA_ARGS__ }, \ - sizeof((struct PsiComplex[]){ __VA_ARGS__ }) / sizeof(struct PsiComplex), \ - PSI_ROW_VECTOR) + psi_new_vector_from((struct PsiComplex[]){ __VA_ARGS__ }, \ + sizeof((struct PsiComplex[]){ __VA_ARGS__ }) / sizeof(struct PsiComplex), \ + PSI_ROW_VECTOR) #define psi_column_vector(...) \ - psi_new_vector_from((struct PsiComplex[]){ __VA_ARGS__ }, \ - sizeof((struct PsiComplex[]){ __VA_ARGS__ }) / sizeof(struct PsiComplex), \ - PSI_COLUMN_VECTOR) + psi_new_vector_from((struct PsiComplex[]){ __VA_ARGS__ }, \ + sizeof((struct PsiComplex[]){ __VA_ARGS__ }) / sizeof(struct PsiComplex), \ + PSI_COLUMN_VECTOR) struct PsiComplex psi_get_vector(struct PsiVector v, size_t index); void psi_set_vector(struct PsiVector* v, size_t index, struct PsiComplex value); diff --git a/src/core/circuit.c b/src/core/circuit.c index 8009db5..8c56c33 100644 --- a/src/core/circuit.c +++ b/src/core/circuit.c @@ -1,382 +1,442 @@ #include "core/circuit.h" #include +#include #include #include +#include "maths/format.h" + const char* psi_gate_op_name(struct PsiGateOp op) { - switch (op.kind) - { - case PSI_GATE_H: return "H"; - case PSI_GATE_X: return "X"; - case PSI_GATE_Y: return "Y"; - case PSI_GATE_Z: return "Z"; - case PSI_GATE_S: return "S"; - case PSI_GATE_T: return "T"; - case PSI_GATE_SDG: return "S†"; - case PSI_GATE_TDG: return "T†"; - case PSI_GATE_SX: return "√X"; - case PSI_GATE_SXDG: return "√X†"; - case PSI_GATE_RX: return "Rx"; - case PSI_GATE_RY: return "Ry"; - case PSI_GATE_RZ: return "Rz"; - case PSI_GATE_P: return "P"; - case PSI_GATE_U1: return "U1"; - case PSI_GATE_U2: return "U2"; - case PSI_GATE_U3: return "U3"; - case PSI_GATE_CNOT: return "CNOT"; - case PSI_GATE_CZ: return "CZ"; - case PSI_GATE_SWAP: return "SWAP"; - case PSI_GATE_CRX: return "CRx"; - case PSI_GATE_CRY: return "CRy"; - case PSI_GATE_CRZ: return "CRz"; - case PSI_GATE_CP: return "CP"; - case PSI_GATE_CCNOT: return "CCNOT"; - case PSI_GATE_CSWAP: return "CSWAP"; - case PSI_GATE_MEASURE: return "M"; - case PSI_GATE_CUSTOM: return op.custom->name; - } - - return "?"; + switch (op.kind) + { + case PSI_GATE_H: return "H"; + case PSI_GATE_X: return "X"; + case PSI_GATE_Y: return "Y"; + case PSI_GATE_Z: return "Z"; + case PSI_GATE_S: return "S"; + case PSI_GATE_T: return "T"; + case PSI_GATE_SDG: return "S†"; + case PSI_GATE_TDG: return "T†"; + case PSI_GATE_SX: return "√X"; + case PSI_GATE_SXDG: return "√X†"; + case PSI_GATE_RX: return "Rx"; + case PSI_GATE_RY: return "Ry"; + case PSI_GATE_RZ: return "Rz"; + case PSI_GATE_P: return "P"; + case PSI_GATE_U1: return "U1"; + case PSI_GATE_U2: return "U2"; + case PSI_GATE_U3: return "U3"; + case PSI_GATE_CNOT: return "CNOT"; + case PSI_GATE_CZ: return "CZ"; + case PSI_GATE_SWAP: return "SWAP"; + case PSI_GATE_CRX: return "CRx"; + case PSI_GATE_CRY: return "CRy"; + case PSI_GATE_CRZ: return "CRz"; + case PSI_GATE_CP: return "CP"; + case PSI_GATE_CCNOT: return "CCNOT"; + case PSI_GATE_CSWAP: return "CSWAP"; + case PSI_GATE_MEASURE: return "M"; + case PSI_GATE_CUSTOM: return op.custom->name; + } + + return "?"; } const size_t* psi_gate_op_quantum_targets(const struct PsiGateOp* op, size_t* out_count) { - if (op->kind == PSI_GATE_CUSTOM) - { - *out_count = op->custom_target_count; - return op->custom_targets; - } + if (op->kind == PSI_GATE_CUSTOM) + { + *out_count = op->custom_target_count; + return op->custom_targets; + } - *out_count = op->qubit_count; - return op->qubits; + *out_count = op->qubit_count; + return op->qubits; } const size_t* psi_gate_op_classical_targets(const struct PsiGateOp* op, size_t* out_count) { - if (op->kind == PSI_GATE_MEASURE) - { - *out_count = 1; - return &op->classical; - } + if (op->kind == PSI_GATE_MEASURE) + { + *out_count = 1; + return &op->classical; + } - *out_count = 0; - return NULL; + *out_count = 0; + return NULL; } bool psi_gate_op_is_measurement(struct PsiGateOp op) { - return op.kind == PSI_GATE_MEASURE; + return op.kind == PSI_GATE_MEASURE; } bool psi_gate_op_is_custom(struct PsiGateOp op) { - return op.kind == PSI_GATE_CUSTOM; + return op.kind == PSI_GATE_CUSTOM; } bool psi_gate_op_is_non_clifford(struct PsiGateOp op) { - switch (op.kind) - { - case PSI_GATE_T: - 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: - case PSI_GATE_CRX: - case PSI_GATE_CRY: - case PSI_GATE_CRZ: - case PSI_GATE_CP: return true; - default: return false; - } + switch (op.kind) + { + case PSI_GATE_T: + 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: + case PSI_GATE_CRX: + case PSI_GATE_CRY: + case PSI_GATE_CRZ: + case PSI_GATE_CP: return true; + default: return false; + } } struct PsiQuantumCircuit psi_new_quantum_circuit(size_t num_qubits) { - return psi_new_quantum_circuit_with_classical(num_qubits, 0); + return psi_new_quantum_circuit_with_classical(num_qubits, 0); } struct PsiQuantumCircuit psi_new_quantum_circuit_with_classical(size_t num_qubits, size_t num_classical) { - struct PsiQuantumCircuit c; - c.num_qubits = num_qubits; - c.num_classical = num_classical; - c.operations = NULL; - c.operation_count = 0; - c.operation_capacity = 0; - c.computed_state = psi_new_vector(0, PSI_COLUMN_VECTOR); - c.is_computed = false; + struct PsiQuantumCircuit c; + c.num_qubits = num_qubits; + c.num_classical = num_classical; + c.operations = NULL; + c.operation_count = 0; + c.operation_capacity = 0; + c.computed_state = psi_new_vector(0, PSI_COLUMN_VECTOR); + c.is_computed = false; - return c; + return c; } static void free_operations(struct PsiQuantumCircuit* c) { - for (size_t i = 0; i < c->operation_count; i++) - { - struct PsiGateOp* op = &c->operations[i]; - if (op->kind != PSI_GATE_CUSTOM) - continue; + for (size_t i = 0; i < c->operation_count; i++) + { + struct PsiGateOp* op = &c->operations[i]; + if (op->kind != PSI_GATE_CUSTOM) + continue; - psi_free_custom_gate(op->custom); - free(op->custom); - free(op->custom_targets); - } + psi_free_custom_gate(op->custom); + free(op->custom); + free(op->custom_targets); + } } void psi_free_quantum_circuit(struct PsiQuantumCircuit* c) { - free_operations(c); - free(c->operations); - c->operations = NULL; - c->operation_count = 0; - c->operation_capacity = 0; - psi_free_vector(&c->computed_state); - c->is_computed = false; + free_operations(c); + free(c->operations); + c->operations = NULL; + c->operation_count = 0; + c->operation_capacity = 0; + psi_free_vector(&c->computed_state); + c->is_computed = false; } void psi_reset_circuit(struct PsiQuantumCircuit* c) { - free_operations(c); - c->operation_count = 0; - psi_free_vector(&c->computed_state); - c->computed_state = psi_new_vector(0, PSI_COLUMN_VECTOR); - c->is_computed = false; + free_operations(c); + c->operation_count = 0; + psi_free_vector(&c->computed_state); + c->computed_state = psi_new_vector(0, PSI_COLUMN_VECTOR); + c->is_computed = false; } static struct PsiGateOp* append_op(struct PsiQuantumCircuit* c) { - if (c->operation_count == c->operation_capacity) - { - size_t new_capacity = c->operation_capacity == 0 ? 8 : c->operation_capacity * 2; - c->operations = realloc(c->operations, new_capacity * sizeof(struct PsiGateOp)); - assert(c->operations != NULL); - c->operation_capacity = new_capacity; - } + if (c->operation_count == c->operation_capacity) + { + size_t new_capacity = c->operation_capacity == 0 ? 8 : c->operation_capacity * 2; + c->operations = realloc(c->operations, new_capacity * sizeof(struct PsiGateOp)); + assert(c->operations != NULL); + c->operation_capacity = new_capacity; + } - struct PsiGateOp* op = &c->operations[c->operation_count++]; - memset(op, 0, sizeof(*op)); - c->is_computed = false; + struct PsiGateOp* op = &c->operations[c->operation_count++]; + memset(op, 0, sizeof(*op)); + c->is_computed = false; - return op; + return op; } static void push_1q(struct PsiQuantumCircuit* c, enum PsiGateKind kind, size_t target) { - struct PsiGateOp* op = append_op(c); - op->kind = kind; - op->qubits[0] = target; - op->qubit_count = 1; + struct PsiGateOp* op = append_op(c); + op->kind = kind; + op->qubits[0] = target; + op->qubit_count = 1; } static void push_1q_1p(struct PsiQuantumCircuit* c, enum PsiGateKind kind, size_t target, double theta) { - struct PsiGateOp* op = append_op(c); - op->kind = kind; - op->qubits[0] = target; - op->qubit_count = 1; - op->params[0] = theta; - op->param_count = 1; + struct PsiGateOp* op = append_op(c); + op->kind = kind; + op->qubits[0] = target; + op->qubit_count = 1; + op->params[0] = theta; + op->param_count = 1; } static void push_2q(struct PsiQuantumCircuit* c, enum PsiGateKind kind, size_t a, size_t b) { - struct PsiGateOp* op = append_op(c); - op->kind = kind; - op->qubits[0] = a; - op->qubits[1] = b; - op->qubit_count = 2; + struct PsiGateOp* op = append_op(c); + op->kind = kind; + op->qubits[0] = a; + op->qubits[1] = b; + op->qubit_count = 2; } static void push_2q_1p(struct PsiQuantumCircuit* c, enum PsiGateKind kind, size_t control, size_t target, double theta) { - struct PsiGateOp* op = append_op(c); - op->kind = kind; - op->qubits[0] = control; - op->qubits[1] = target; - op->qubit_count = 2; - op->params[0] = theta; - op->param_count = 1; + struct PsiGateOp* op = append_op(c); + op->kind = kind; + op->qubits[0] = control; + op->qubits[1] = target; + op->qubit_count = 2; + op->params[0] = theta; + op->param_count = 1; } static void push_3q(struct PsiQuantumCircuit* c, enum PsiGateKind kind, size_t a, size_t b, size_t d) { - struct PsiGateOp* op = append_op(c); - op->kind = kind; - op->qubits[0] = a; - op->qubits[1] = b; - op->qubits[2] = d; - op->qubit_count = 3; + struct PsiGateOp* op = append_op(c); + op->kind = kind; + op->qubits[0] = a; + op->qubits[1] = b; + op->qubits[2] = d; + op->qubit_count = 3; } void psi_apply_h(struct PsiQuantumCircuit* c, size_t target) { - push_1q(c, PSI_GATE_H, target); + push_1q(c, PSI_GATE_H, target); } void psi_apply_x(struct PsiQuantumCircuit* c, size_t target) { - push_1q(c, PSI_GATE_X, target); + push_1q(c, PSI_GATE_X, target); } void psi_apply_y(struct PsiQuantumCircuit* c, size_t target) { - push_1q(c, PSI_GATE_Y, target); + push_1q(c, PSI_GATE_Y, target); } void psi_apply_z(struct PsiQuantumCircuit* c, size_t target) { - push_1q(c, PSI_GATE_Z, target); + push_1q(c, PSI_GATE_Z, target); } void psi_apply_s(struct PsiQuantumCircuit* c, size_t target) { - push_1q(c, PSI_GATE_S, target); + push_1q(c, PSI_GATE_S, target); } void psi_apply_t(struct PsiQuantumCircuit* c, size_t target) { - push_1q(c, PSI_GATE_T, target); + push_1q(c, PSI_GATE_T, target); } void psi_apply_sdg(struct PsiQuantumCircuit* c, size_t target) { - push_1q(c, PSI_GATE_SDG, target); + push_1q(c, PSI_GATE_SDG, target); } void psi_apply_tdg(struct PsiQuantumCircuit* c, size_t target) { - push_1q(c, PSI_GATE_TDG, target); + push_1q(c, PSI_GATE_TDG, target); } void psi_apply_sx(struct PsiQuantumCircuit* c, size_t target) { - push_1q(c, PSI_GATE_SX, target); + push_1q(c, PSI_GATE_SX, target); } void psi_apply_sxdg(struct PsiQuantumCircuit* c, size_t target) { - push_1q(c, PSI_GATE_SXDG, target); + push_1q(c, PSI_GATE_SXDG, target); } void psi_apply_rx(struct PsiQuantumCircuit* c, size_t target, double theta) { - push_1q_1p(c, PSI_GATE_RX, target, theta); + push_1q_1p(c, PSI_GATE_RX, target, theta); } void psi_apply_ry(struct PsiQuantumCircuit* c, size_t target, double theta) { - push_1q_1p(c, PSI_GATE_RY, target, theta); + push_1q_1p(c, PSI_GATE_RY, target, theta); } void psi_apply_rz(struct PsiQuantumCircuit* c, size_t target, double theta) { - push_1q_1p(c, PSI_GATE_RZ, target, theta); + push_1q_1p(c, PSI_GATE_RZ, target, theta); } void psi_apply_p(struct PsiQuantumCircuit* c, size_t target, double theta) { - push_1q_1p(c, PSI_GATE_P, target, theta); + push_1q_1p(c, PSI_GATE_P, target, theta); } void psi_apply_u1(struct PsiQuantumCircuit* c, size_t target, double lambda) { - push_1q_1p(c, PSI_GATE_U1, target, lambda); + push_1q_1p(c, PSI_GATE_U1, target, lambda); } void psi_apply_u2(struct PsiQuantumCircuit* c, size_t target, double phi, double lambda) { - struct PsiGateOp* op = append_op(c); - op->kind = PSI_GATE_U2; - op->qubits[0] = target; - op->qubit_count = 1; - op->params[0] = phi; - op->params[1] = lambda; - op->param_count = 2; + struct PsiGateOp* op = append_op(c); + op->kind = PSI_GATE_U2; + op->qubits[0] = target; + op->qubit_count = 1; + op->params[0] = phi; + op->params[1] = lambda; + op->param_count = 2; } void psi_apply_u3(struct PsiQuantumCircuit* c, size_t target, double theta, double phi, double lambda) { - struct PsiGateOp* op = append_op(c); - op->kind = PSI_GATE_U3; - op->qubits[0] = target; - op->qubit_count = 1; - op->params[0] = theta; - op->params[1] = phi; - op->params[2] = lambda; - op->param_count = 3; + struct PsiGateOp* op = append_op(c); + op->kind = PSI_GATE_U3; + op->qubits[0] = target; + op->qubit_count = 1; + op->params[0] = theta; + op->params[1] = phi; + op->params[2] = lambda; + op->param_count = 3; } void psi_apply_cnot(struct PsiQuantumCircuit* c, size_t control, size_t target) { - push_2q(c, PSI_GATE_CNOT, control, target); + push_2q(c, PSI_GATE_CNOT, control, target); } void psi_apply_cz(struct PsiQuantumCircuit* c, size_t control, size_t target) { - push_2q(c, PSI_GATE_CZ, control, target); + push_2q(c, PSI_GATE_CZ, control, target); } void psi_apply_swap(struct PsiQuantumCircuit* c, size_t qubit1, size_t qubit2) { - push_2q(c, PSI_GATE_SWAP, qubit1, qubit2); + push_2q(c, PSI_GATE_SWAP, qubit1, qubit2); } void psi_apply_crx(struct PsiQuantumCircuit* c, size_t control, size_t target, double theta) { - push_2q_1p(c, PSI_GATE_CRX, control, target, theta); + push_2q_1p(c, PSI_GATE_CRX, control, target, theta); } void psi_apply_cry(struct PsiQuantumCircuit* c, size_t control, size_t target, double theta) { - push_2q_1p(c, PSI_GATE_CRY, control, target, theta); + push_2q_1p(c, PSI_GATE_CRY, control, target, theta); } void psi_apply_crz(struct PsiQuantumCircuit* c, size_t control, size_t target, double theta) { - push_2q_1p(c, PSI_GATE_CRZ, control, target, theta); + push_2q_1p(c, PSI_GATE_CRZ, control, target, theta); } void psi_apply_cp(struct PsiQuantumCircuit* c, size_t control, size_t target, double theta) { - push_2q_1p(c, PSI_GATE_CP, control, target, theta); + push_2q_1p(c, PSI_GATE_CP, control, target, theta); } void psi_apply_ccnot(struct PsiQuantumCircuit* c, size_t control1, size_t control2, size_t target) { - push_3q(c, PSI_GATE_CCNOT, control1, control2, target); + push_3q(c, PSI_GATE_CCNOT, control1, control2, target); } void psi_apply_cswap(struct PsiQuantumCircuit* c, size_t control, size_t target1, size_t target2) { - push_3q(c, PSI_GATE_CSWAP, control, target1, target2); + push_3q(c, PSI_GATE_CSWAP, control, target1, target2); } void psi_measure(struct PsiQuantumCircuit* c, size_t qubit, size_t classical) { - if (classical >= c->num_classical) - c->num_classical = classical + 1; + if (classical >= c->num_classical) + c->num_classical = classical + 1; - struct PsiGateOp* op = append_op(c); - op->kind = PSI_GATE_MEASURE; - op->qubits[0] = qubit; - op->qubit_count = 1; - op->classical = classical; + struct PsiGateOp* op = append_op(c); + op->kind = PSI_GATE_MEASURE; + op->qubits[0] = qubit; + op->qubit_count = 1; + op->classical = classical; } void psi_measure_all(struct PsiQuantumCircuit* c) { - for (size_t i = 0; i < c->num_qubits; i++) - psi_measure(c, i, i); + for (size_t i = 0; i < c->num_qubits; i++) + psi_measure(c, i, i); } void psi_apply_custom(struct PsiQuantumCircuit* c, struct PsiCustomGate gate, const size_t* targets, size_t count) { - struct PsiCustomGate* owned = malloc(sizeof(struct PsiCustomGate)); - assert(owned != NULL); - *owned = gate; - - size_t* owned_targets = malloc(count * sizeof(size_t)); - assert(owned_targets != NULL || count == 0); - if (count > 0) - memcpy(owned_targets, targets, count * sizeof(size_t)); - - struct PsiGateOp* op = append_op(c); - op->kind = PSI_GATE_CUSTOM; - op->custom = owned; - op->custom_targets = owned_targets; - op->custom_target_count = count; + struct PsiCustomGate* owned = malloc(sizeof(struct PsiCustomGate)); + assert(owned != NULL); + *owned = gate; + + size_t* owned_targets = malloc(count * sizeof(size_t)); + assert(owned_targets != NULL || count == 0); + if (count > 0) + memcpy(owned_targets, targets, count * sizeof(size_t)); + + struct PsiGateOp* op = append_op(c); + op->kind = PSI_GATE_CUSTOM; + op->custom = owned; + op->custom_targets = owned_targets; + op->custom_target_count = count; +} + +void psi_print_circuit(const struct PsiQuantumCircuit* circuit, FILE* out) +{ + size_t nq = circuit->num_qubits; + fprintf(out, "QuantumCircuit (%zu qubits, %zu classical)\n", nq, circuit->num_classical); + fprintf(out, "Operations:\n"); + + for (size_t i = 0; i < circuit->operation_count; i++) + { + struct PsiGateOp op = circuit->operations[i]; + + if (op.kind == PSI_GATE_MEASURE) + { + fprintf(out, " %zu: %s q%zu → c%zu\n", i, psi_gate_op_name(op), op.qubits[0], + op.classical); + continue; + } + + if (op.kind == PSI_GATE_CUSTOM) + fprintf(out, " %zu: [%s] on [", i, op.custom->name); + else + fprintf(out, " %zu: %s on [", i, psi_gate_op_name(op)); + + size_t target_count; + const size_t* targets = psi_gate_op_quantum_targets(&op, &target_count); + for (size_t j = 0; j < target_count; j++) + { + fprintf(out, "%zu", targets[j]); + if (j != target_count - 1) + fputs(", ", out); + } + fputs("]\n", out); + } + + if (!circuit->is_computed) + { + fputs("State: (not computed)\n", out); + return; + } + + fputs("State:\n", out); + size_t dim = (size_t)1 << nq; + for (size_t i = 0; i < dim; i++) + { + struct PsiComplex amp = circuit->computed_state.data[i]; + if (fabs(amp.real) < 1e-10 && fabs(amp.imaginary) < 1e-10) + continue; + + char basis[65]; + for (size_t b = 0; b < nq; b++) + basis[b] = ((i >> (nq - 1 - b)) & 1) ? '1' : '0'; + basis[nq] = '\0'; + + char amp_buf[64]; + fprintf(out, " |%s⟩: %s\n", basis, psi_format_amplitude(amp, amp_buf, sizeof amp_buf)); + } } diff --git a/src/core/circuit.rs b/src/core/circuit.rs deleted file mode 100644 index 6647f58..0000000 --- a/src/core/circuit.rs +++ /dev/null @@ -1,477 +0,0 @@ -use super::{CustomGate, QuantumState, Runtime, RuntimeConfig}; -use crate::{format_amplitude, format_probability, Vector}; -use core::fmt; -use std::sync::Arc; - -#[derive(Clone)] -pub enum GateOp { - H(usize), - X(usize), - Y(usize), - Z(usize), - S(usize), - T(usize), - Sdg(usize), - Tdg(usize), - Sx(usize), - Sxdg(usize), - Rx(usize, f64), - Ry(usize, f64), - Rz(usize, f64), - P(usize, f64), - U1(usize, f64), - U2(usize, f64, f64), - U3(usize, f64, f64, f64), - CNOT(usize, usize), - CZ(usize, usize), - SWAP(usize, usize), - CRx(usize, usize, f64), - CRy(usize, usize, f64), - CRz(usize, usize, f64), - CP(usize, usize, f64), - CCNOT(usize, usize, usize), - CSWAP(usize, usize, usize), - Measure(usize, usize), - Custom(Arc, Vec), -} - -impl GateOp { - pub fn name(&self) -> &str { - match self { - GateOp::H(_) => "H", - GateOp::X(_) => "X", - GateOp::Y(_) => "Y", - GateOp::Z(_) => "Z", - GateOp::S(_) => "S", - GateOp::T(_) => "T", - GateOp::Sdg(_) => "S†", - GateOp::Tdg(_) => "T†", - GateOp::Sx(_) => "√X", - GateOp::Sxdg(_) => "√X†", - GateOp::Rx(_, _) => "Rx", - GateOp::Ry(_, _) => "Ry", - GateOp::Rz(_, _) => "Rz", - GateOp::P(_, _) => "P", - GateOp::U1(_, _) => "U1", - GateOp::U2(_, _, _) => "U2", - GateOp::U3(_, _, _, _) => "U3", - GateOp::CRx(_, _, _) => "CRx", - GateOp::CRy(_, _, _) => "CRy", - GateOp::CRz(_, _, _) => "CRz", - GateOp::CP(_, _, _) => "CP", - GateOp::CNOT(_, _) => "CNOT", - GateOp::CZ(_, _) => "CZ", - GateOp::SWAP(_, _) => "SWAP", - GateOp::CCNOT(_, _, _) => "CCNOT", - GateOp::CSWAP(_, _, _) => "CSWAP", - GateOp::Measure(_, _) => "M", - GateOp::Custom(gate, _) => &gate.name, - } - } - - pub fn quantum_targets(&self) -> Vec { - match self { - GateOp::H(t) - | GateOp::X(t) - | GateOp::Y(t) - | GateOp::Z(t) - | GateOp::S(t) - | GateOp::T(t) - | GateOp::Sdg(t) - | GateOp::Tdg(t) - | GateOp::Sx(t) - | GateOp::Sxdg(t) - | GateOp::Rx(t, _) - | GateOp::Ry(t, _) - | GateOp::Rz(t, _) - | GateOp::P(t, _) - | GateOp::U1(t, _) - | GateOp::U2(t, _, _) - | GateOp::U3(t, _, _, _) => vec![*t], - GateOp::CNOT(c, t) - | GateOp::CZ(c, t) - | GateOp::SWAP(c, t) - | GateOp::CRx(c, t, _) - | GateOp::CRy(c, t, _) - | GateOp::CRz(c, t, _) - | GateOp::CP(c, t, _) => vec![*c, *t], - GateOp::CCNOT(c1, c2, t) | GateOp::CSWAP(c1, c2, t) => vec![*c1, *c2, *t], - GateOp::Measure(q, _) => vec![*q], - GateOp::Custom(_, targets) => targets.clone(), - } - } - - pub fn classical_targets(&self) -> Vec { - match self { - GateOp::Measure(_, c) => vec![*c], - _ => vec![], - } - } - - pub fn is_measurement(&self) -> bool { - matches!(self, GateOp::Measure(_, _)) - } - - pub fn is_custom(&self) -> bool { - matches!(self, GateOp::Custom(_, _)) - } - - pub fn is_non_clifford(&self) -> bool { - matches!( - self, - GateOp::T(_) - | GateOp::Tdg(_) - | GateOp::Sx(_) - | GateOp::Sxdg(_) - | GateOp::Rx(_, _) - | GateOp::Ry(_, _) - | GateOp::Rz(_, _) - | GateOp::P(_, _) - | GateOp::U1(_, _) - | GateOp::U2(_, _, _) - | GateOp::U3(_, _, _, _) - | GateOp::CRx(_, _, _) - | GateOp::CRy(_, _, _) - | GateOp::CRz(_, _, _) - | GateOp::CP(_, _, _) - ) - } -} - -pub struct QuantumCircuit { - num_qubits: usize, - num_classical: usize, - operations: Vec, - computed_state: Option, -} - -impl QuantumCircuit { - pub fn new(num_qubits: usize) -> QuantumCircuit { - QuantumCircuit { - num_qubits, - num_classical: 0, - operations: Vec::new(), - computed_state: None, - } - } - - pub fn with_classical(num_qubits: usize, num_classical: usize) -> QuantumCircuit { - QuantumCircuit { - num_qubits, - num_classical, - operations: Vec::new(), - computed_state: None, - } - } - - pub fn num_qubits(&self) -> usize { - self.num_qubits - } - - pub fn num_classical(&self) -> usize { - self.num_classical - } - - pub fn operations(&self) -> &[GateOp] { - &self.operations - } - - pub fn is_computed(&self) -> bool { - self.computed_state.is_some() - } - - pub fn compute(&mut self) -> &QuantumState { - self.compute_with(Runtime::default()) - } - - pub fn compute_with(&mut self, runtime: Runtime) -> &QuantumState { - if self.computed_state.is_none() { - self.computed_state = Some(runtime.compute(self.num_qubits, &self.operations)); - } - self.computed_state.as_ref().unwrap() - } - - pub fn compute_with_config(&mut self, config: RuntimeConfig) -> &QuantumState { - if self.computed_state.is_none() { - self.computed_state = Some(config.compute(self.num_qubits, &self.operations)); - } - self.computed_state.as_ref().unwrap() - } - - pub fn state(&mut self) -> &QuantumState { - self.compute() - } - - pub fn state_with(&mut self, runtime: Runtime) -> &QuantumState { - self.compute_with(runtime) - } - - pub fn state_with_config(&mut self, config: RuntimeConfig) -> &QuantumState { - self.compute_with_config(config) - } - - pub fn h(&mut self, target: usize) -> &mut Self { - self.operations.push(GateOp::H(target)); - self.computed_state = None; - self - } - - pub fn x(&mut self, target: usize) -> &mut Self { - self.operations.push(GateOp::X(target)); - self.computed_state = None; - self - } - - pub fn y(&mut self, target: usize) -> &mut Self { - self.operations.push(GateOp::Y(target)); - self.computed_state = None; - self - } - - pub fn z(&mut self, target: usize) -> &mut Self { - self.operations.push(GateOp::Z(target)); - self.computed_state = None; - self - } - - pub fn s(&mut self, target: usize) -> &mut Self { - self.operations.push(GateOp::S(target)); - self.computed_state = None; - self - } - - pub fn t(&mut self, target: usize) -> &mut Self { - self.operations.push(GateOp::T(target)); - self.computed_state = None; - self - } - - pub fn sdg(&mut self, target: usize) -> &mut Self { - self.operations.push(GateOp::Sdg(target)); - self.computed_state = None; - self - } - - pub fn tdg(&mut self, target: usize) -> &mut Self { - self.operations.push(GateOp::Tdg(target)); - self.computed_state = None; - self - } - - pub fn sx(&mut self, target: usize) -> &mut Self { - self.operations.push(GateOp::Sx(target)); - self.computed_state = None; - self - } - - pub fn sxdg(&mut self, target: usize) -> &mut Self { - self.operations.push(GateOp::Sxdg(target)); - self.computed_state = None; - self - } - - pub fn rx(&mut self, target: usize, theta: f64) -> &mut Self { - self.operations.push(GateOp::Rx(target, theta)); - self.computed_state = None; - self - } - - pub fn ry(&mut self, target: usize, theta: f64) -> &mut Self { - self.operations.push(GateOp::Ry(target, theta)); - self.computed_state = None; - self - } - - pub fn rz(&mut self, target: usize, theta: f64) -> &mut Self { - self.operations.push(GateOp::Rz(target, theta)); - self.computed_state = None; - self - } - - pub fn p(&mut self, target: usize, theta: f64) -> &mut Self { - self.operations.push(GateOp::P(target, theta)); - self.computed_state = None; - self - } - - pub fn u1(&mut self, target: usize, lambda: f64) -> &mut Self { - self.operations.push(GateOp::U1(target, lambda)); - self.computed_state = None; - self - } - - pub fn u2(&mut self, target: usize, phi: f64, lambda: f64) -> &mut Self { - self.operations.push(GateOp::U2(target, phi, lambda)); - self.computed_state = None; - self - } - - pub fn u3(&mut self, target: usize, theta: f64, phi: f64, lambda: f64) -> &mut Self { - self.operations.push(GateOp::U3(target, theta, phi, lambda)); - self.computed_state = None; - self - } - - pub fn crx(&mut self, control: usize, target: usize, theta: f64) -> &mut Self { - self.operations.push(GateOp::CRx(control, target, theta)); - self.computed_state = None; - self - } - - pub fn cry(&mut self, control: usize, target: usize, theta: f64) -> &mut Self { - self.operations.push(GateOp::CRy(control, target, theta)); - self.computed_state = None; - self - } - - pub fn crz(&mut self, control: usize, target: usize, theta: f64) -> &mut Self { - self.operations.push(GateOp::CRz(control, target, theta)); - self.computed_state = None; - self - } - - pub fn cp(&mut self, control: usize, target: usize, theta: f64) -> &mut Self { - self.operations.push(GateOp::CP(control, target, theta)); - self.computed_state = None; - self - } - - pub fn cnot(&mut self, control: usize, target: usize) -> &mut Self { - self.operations.push(GateOp::CNOT(control, target)); - self.computed_state = None; - self - } - - pub fn cx(&mut self, control: usize, target: usize) -> &mut Self { - self.cnot(control, target) - } - - pub fn cz(&mut self, control: usize, target: usize) -> &mut Self { - self.operations.push(GateOp::CZ(control, target)); - self.computed_state = None; - self - } - - pub fn swap(&mut self, qubit1: usize, qubit2: usize) -> &mut Self { - self.operations.push(GateOp::SWAP(qubit1, qubit2)); - self.computed_state = None; - self - } - - pub fn ccnot(&mut self, control1: usize, control2: usize, target: usize) -> &mut Self { - self.operations - .push(GateOp::CCNOT(control1, control2, target)); - self.computed_state = None; - self - } - - pub fn toffoli(&mut self, control1: usize, control2: usize, target: usize) -> &mut Self { - self.ccnot(control1, control2, target) - } - - pub fn cswap(&mut self, control: usize, target1: usize, target2: usize) -> &mut Self { - self.operations - .push(GateOp::CSWAP(control, target1, target2)); - self.computed_state = None; - self - } - - pub fn fredkin(&mut self, control: usize, target1: usize, target2: usize) -> &mut Self { - self.cswap(control, target1, target2) - } - - pub fn measure(&mut self, qubit: usize, classical: usize) -> &mut Self { - if classical >= self.num_classical { - self.num_classical = classical + 1; - } - self.operations.push(GateOp::Measure(qubit, classical)); - self - } - - pub fn measure_all(&mut self) -> &mut Self { - for i in 0..self.num_qubits { - self.measure(i, i); - } - self - } - - pub fn custom(&mut self, gate: &Arc, targets: &[usize]) -> &mut Self { - self.operations - .push(GateOp::Custom(Arc::clone(gate), targets.to_vec())); - self.computed_state = None; - self - } - - pub fn apply_custom(&mut self, gate: CustomGate, targets: &[usize]) -> &mut Self { - self.operations - .push(GateOp::Custom(Arc::new(gate), targets.to_vec())); - self.computed_state = None; - self - } - - pub fn reset(&mut self) -> &mut Self { - self.operations.clear(); - self.computed_state = None; - self - } - - pub fn probability(&mut self, state_index: usize) -> f64 { - self.compute(); - let state = self.computed_state.as_ref().unwrap(); - let amp = state.get(state_index); - amp.norm2() - } - - pub fn probabilities(&mut self) -> Vec { - self.compute(); - let n = 1 << self.num_qubits; - let state = self.computed_state.as_ref().unwrap(); - (0..n).map(|i| state.get(i).norm2()).collect() - } - - pub fn print_probabilities(&mut self) { - let probs = self.probabilities(); - let n = self.num_qubits; - println!("Probabilities:"); - for (i, p) in probs.iter().enumerate() { - if *p > 1e-10 { - let basis: String = format!("{:0width$b}", i, width = n); - println!(" |{}⟩: {}", basis, format_probability(*p)); - } - } - } -} - -impl fmt::Display for QuantumCircuit { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!( - f, - "QuantumCircuit ({} qubits, {} classical)", - self.num_qubits, self.num_classical - )?; - writeln!(f, "Operations:")?; - for (i, op) in self.operations.iter().enumerate() { - match op { - GateOp::Measure(q, c) => writeln!(f, " {}: {} q{} → c{}", i, op.name(), q, c)?, - GateOp::Custom(gate, targets) => { - writeln!(f, " {}: [{}] on {:?}", i, gate.name, targets)? - } - _ => writeln!(f, " {}: {} on {:?}", i, op.name(), op.quantum_targets())?, - } - } - if let Some(state) = &self.computed_state { - writeln!(f, "State:")?; - let n = 1 << self.num_qubits; - for i in 0..n { - let amp = state.get(i); - if amp.real.abs() > 1e-10 || amp.imaginary.abs() > 1e-10 { - let basis: String = format!("{:0width$b}", i, width = self.num_qubits); - writeln!(f, " |{}⟩: {}", basis, format_amplitude(&))?; - } - } - } else { - writeln!(f, "State: (not computed)")?; - } - Ok(()) - } -} diff --git a/src/core/classical_components.c b/src/core/classical_components.c index 3ffeb88..3404c6b 100644 --- a/src/core/classical_components.c +++ b/src/core/classical_components.c @@ -5,31 +5,31 @@ struct PsiClassicalBit psi_new_classical_bit(const char* name, bool state) { - return (struct PsiClassicalBit){ - name, - state, - }; + return (struct PsiClassicalBit){ + name, + state, + }; } struct PsiClassicalRegister psi_new_classical_register(const char* name, const char** names, size_t count) { - struct PsiClassicalBit* bits = malloc(count * sizeof(struct PsiClassicalBit)); - assert(bits != NULL || count == 0); + struct PsiClassicalBit* bits = malloc(count * sizeof(struct PsiClassicalBit)); + assert(bits != NULL || count == 0); - for (size_t i = 0; i < count; i++) - bits[i] = psi_new_classical_bit(names[i], false); + for (size_t i = 0; i < count; i++) + bits[i] = psi_new_classical_bit(names[i], false); - return (struct PsiClassicalRegister){ - name, - bits, - count, - }; + return (struct PsiClassicalRegister){ + name, + bits, + count, + }; } void psi_free_classical_register(struct PsiClassicalRegister* reg) { - free(reg->bits); - reg->bits = NULL; - reg->num_bits = 0; + free(reg->bits); + reg->bits = NULL; + reg->num_bits = 0; } diff --git a/src/core/custom_gate.c b/src/core/custom_gate.c index dd41635..2dce185 100644 --- a/src/core/custom_gate.c +++ b/src/core/custom_gate.c @@ -9,157 +9,157 @@ struct PsiCustomGate psi_new_custom_gate_from_matrix(const char* name, struct PsiMatrix matrix) { - assert(matrix.rows == matrix.cols); + assert(matrix.rows == matrix.cols); - size_t dim = matrix.rows; - assert(dim > 0 && (dim & (dim - 1)) == 0); + size_t dim = matrix.rows; + assert(dim > 0 && (dim & (dim - 1)) == 0); - size_t num_qubits = 0; - while (((size_t)1 << num_qubits) < dim) - num_qubits++; + size_t num_qubits = 0; + while (((size_t)1 << num_qubits) < dim) + num_qubits++; - struct PsiCustomGate gate; - gate.name = name; - gate.num_qubits = num_qubits; - gate.kind = PSI_CUSTOM_GATE_MATRIX; - gate.definition.matrix = matrix; + struct PsiCustomGate gate; + gate.name = name; + gate.num_qubits = num_qubits; + gate.kind = PSI_CUSTOM_GATE_MATRIX; + gate.definition.matrix = matrix; - return gate; + return gate; } struct PsiCustomGate psi_new_custom_gate_from_composite(const char* name, size_t num_qubits, const struct PsiCompositeGateOp* ops, size_t op_count) { - struct PsiCompositeGateOp* owned = malloc(op_count * sizeof(struct PsiCompositeGateOp)); - assert(owned != NULL || op_count == 0); + struct PsiCompositeGateOp* owned = malloc(op_count * sizeof(struct PsiCompositeGateOp)); + assert(owned != NULL || op_count == 0); - if (op_count > 0) - memcpy(owned, ops, op_count * sizeof(struct PsiCompositeGateOp)); + if (op_count > 0) + memcpy(owned, ops, op_count * sizeof(struct PsiCompositeGateOp)); - struct PsiCustomGate gate; - gate.name = name; - gate.num_qubits = num_qubits; - gate.kind = PSI_CUSTOM_GATE_COMPOSITE; - gate.definition.composite.ops = owned; - gate.definition.composite.count = op_count; + struct PsiCustomGate gate; + gate.name = name; + gate.num_qubits = num_qubits; + gate.kind = PSI_CUSTOM_GATE_COMPOSITE; + gate.definition.composite.ops = owned; + gate.definition.composite.count = op_count; - return gate; + return gate; } void psi_free_custom_gate(struct PsiCustomGate* gate) { - if (gate->kind == PSI_CUSTOM_GATE_MATRIX) - { - psi_free_matrix(&gate->definition.matrix); - return; - } - - free(gate->definition.composite.ops); - gate->definition.composite.ops = NULL; - gate->definition.composite.count = 0; + if (gate->kind == PSI_CUSTOM_GATE_MATRIX) + { + psi_free_matrix(&gate->definition.matrix); + return; + } + + free(gate->definition.composite.ops); + gate->definition.composite.ops = NULL; + gate->definition.composite.count = 0; } static struct PsiQuantumGate op_gate(enum PsiCompositeOp op) { - switch (op) - { - case PSI_OP_H: return psi_hadamard_gate(); - case PSI_OP_X: return psi_pauli_x_gate(); - case PSI_OP_Y: return psi_pauli_y_gate(); - case PSI_OP_Z: return psi_pauli_z_gate(); - case PSI_OP_S: return psi_s_gate(); - case PSI_OP_T: return psi_t_gate(); - case PSI_OP_CNOT: return psi_cnot_gate(); - case PSI_OP_CZ: return psi_cz_gate(); - case PSI_OP_SWAP: return psi_swap_gate(); - case PSI_OP_CCNOT: return psi_toffoli_gate(); - case PSI_OP_CSWAP: return psi_fredkin_gate(); - } - - return psi_identity_gate(); + switch (op) + { + case PSI_OP_H: return psi_hadamard_gate(); + case PSI_OP_X: return psi_pauli_x_gate(); + case PSI_OP_Y: return psi_pauli_y_gate(); + case PSI_OP_Z: return psi_pauli_z_gate(); + case PSI_OP_S: return psi_s_gate(); + case PSI_OP_T: return psi_t_gate(); + case PSI_OP_CNOT: return psi_cnot_gate(); + case PSI_OP_CZ: return psi_cz_gate(); + case PSI_OP_SWAP: return psi_swap_gate(); + case PSI_OP_CCNOT: return psi_toffoli_gate(); + case PSI_OP_CSWAP: return psi_fredkin_gate(); + } + + return psi_identity_gate(); } static bool find_target(const size_t* targets, size_t count, size_t q, size_t* pos) { - for (size_t i = 0; i < count; i++) - if (targets[i] == q) - { - *pos = i; - return true; - } - - return false; + for (size_t i = 0; i < count; i++) + if (targets[i] == q) + { + *pos = i; + return true; + } + + return false; } static struct PsiMatrix build_full_operator(struct PsiMatrix gate_matrix, const size_t* targets, size_t num_gate_qubits, size_t total_qubits) { - size_t dim = (size_t)1 << total_qubits; - size_t gate_dim = gate_matrix.rows; - - struct PsiMatrix result = psi_new_matrix(dim, dim); - - for (size_t i = 0; i < dim; i++) - for (size_t j = 0; j < dim; j++) - { - size_t gate_i = 0; - size_t gate_j = 0; - bool match_non_targets = true; - - for (size_t q = 0; q < total_qubits; q++) - { - size_t bit_i = (i >> (total_qubits - 1 - q)) & 1; - size_t bit_j = (j >> (total_qubits - 1 - q)) & 1; - - size_t pos; - if (find_target(targets, num_gate_qubits, q, &pos)) - { - gate_i |= bit_i << (num_gate_qubits - 1 - pos); - gate_j |= bit_j << (num_gate_qubits - 1 - pos); - } - else if (bit_i != bit_j) - { - match_non_targets = false; - break; - } - } - - if (match_non_targets) - result.data[i * dim + j] = gate_matrix.data[gate_i * gate_dim + gate_j]; - } - - return result; + size_t dim = (size_t)1 << total_qubits; + size_t gate_dim = gate_matrix.rows; + + struct PsiMatrix result = psi_new_matrix(dim, dim); + + for (size_t i = 0; i < dim; i++) + for (size_t j = 0; j < dim; j++) + { + size_t gate_i = 0; + size_t gate_j = 0; + bool match_non_targets = true; + + for (size_t q = 0; q < total_qubits; q++) + { + size_t bit_i = (i >> (total_qubits - 1 - q)) & 1; + size_t bit_j = (j >> (total_qubits - 1 - q)) & 1; + + size_t pos; + if (find_target(targets, num_gate_qubits, q, &pos)) + { + gate_i |= bit_i << (num_gate_qubits - 1 - pos); + gate_j |= bit_j << (num_gate_qubits - 1 - pos); + } + else if (bit_i != bit_j) + { + match_non_targets = false; + break; + } + } + + if (match_non_targets) + result.data[i * dim + j] = gate_matrix.data[gate_i * gate_dim + gate_j]; + } + + return result; } static struct PsiMatrix compute_composite_matrix(struct PsiCustomGate gate) { - size_t dim = (size_t)1 << gate.num_qubits; - struct PsiMatrix result = psi_identity_matrix(dim); + size_t dim = (size_t)1 << gate.num_qubits; + struct PsiMatrix result = psi_identity_matrix(dim); - for (size_t i = 0; i < gate.definition.composite.count; i++) - { - struct PsiCompositeGateOp step = gate.definition.composite.ops[i]; - struct PsiQuantumGate g = op_gate(step.op); + for (size_t i = 0; i < gate.definition.composite.count; i++) + { + struct PsiCompositeGateOp step = gate.definition.composite.ops[i]; + struct PsiQuantumGate g = op_gate(step.op); - struct PsiMatrix full = - build_full_operator(g.matrix, step.targets, step.target_count, gate.num_qubits); - struct PsiMatrix next = psi_dot_matrix(full, result); + struct PsiMatrix full = + build_full_operator(g.matrix, step.targets, step.target_count, gate.num_qubits); + struct PsiMatrix next = psi_dot_matrix(full, result); - psi_free_matrix(&full); - psi_free_matrix(&result); - psi_free_quantum_gate(&g); - result = next; - } + psi_free_matrix(&full); + psi_free_matrix(&result); + psi_free_quantum_gate(&g); + result = next; + } - return result; + return result; } struct PsiQuantumGate psi_to_quantum_gate(struct PsiCustomGate gate) { - if (gate.kind == PSI_CUSTOM_GATE_MATRIX) - return psi_new_quantum_gate(gate.name, psi_clone_matrix(gate.definition.matrix), - gate.num_qubits); + if (gate.kind == PSI_CUSTOM_GATE_MATRIX) + return psi_new_quantum_gate(gate.name, psi_clone_matrix(gate.definition.matrix), + gate.num_qubits); - return psi_new_quantum_gate(gate.name, compute_composite_matrix(gate), gate.num_qubits); + return psi_new_quantum_gate(gate.name, compute_composite_matrix(gate), gate.num_qubits); } diff --git a/src/core/gates.c b/src/core/gates.c index 51605c9..57d2e97 100644 --- a/src/core/gates.c +++ b/src/core/gates.c @@ -6,266 +6,266 @@ static const double INV_SQRT_2 = 0.7071067811865476; struct PsiMatrix psi_rx_matrix(double theta) { - double cos_h = cos(theta / 2.0); - double sin_h = sin(theta / 2.0); + double cos_h = cos(theta / 2.0); + double sin_h = sin(theta / 2.0); - return psi_matrix(2, 2, psi_new_complex(cos_h, 0.0), psi_new_complex(0.0, -sin_h), - psi_new_complex(0.0, -sin_h), psi_new_complex(cos_h, 0.0)); + return psi_matrix(2, 2, psi_new_complex(cos_h, 0.0), psi_new_complex(0.0, -sin_h), + psi_new_complex(0.0, -sin_h), psi_new_complex(cos_h, 0.0)); } struct PsiMatrix psi_ry_matrix(double theta) { - double cos_h = cos(theta / 2.0); - double sin_h = sin(theta / 2.0); + double cos_h = cos(theta / 2.0); + double sin_h = sin(theta / 2.0); - return psi_matrix(2, 2, psi_new_complex(cos_h, 0.0), psi_new_complex(-sin_h, 0.0), - psi_new_complex(sin_h, 0.0), psi_new_complex(cos_h, 0.0)); + return psi_matrix(2, 2, psi_new_complex(cos_h, 0.0), psi_new_complex(-sin_h, 0.0), + psi_new_complex(sin_h, 0.0), psi_new_complex(cos_h, 0.0)); } struct PsiMatrix psi_rz_matrix(double theta) { - double half = theta / 2.0; + double half = theta / 2.0; - return psi_matrix(2, 2, psi_new_complex(cos(half), -sin(half)), psi_new_complex(0.0, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(cos(half), sin(half))); + return psi_matrix(2, 2, psi_new_complex(cos(half), -sin(half)), psi_new_complex(0.0, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(cos(half), sin(half))); } struct PsiMatrix psi_p_matrix(double theta) { - return psi_matrix(2, 2, psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(cos(theta), sin(theta))); + return psi_matrix(2, 2, psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(cos(theta), sin(theta))); } struct PsiMatrix psi_u1_matrix(double lambda) { - return psi_p_matrix(lambda); + return psi_p_matrix(lambda); } struct PsiMatrix psi_u2_matrix(double phi, double lambda) { - return psi_matrix( - 2, 2, psi_new_complex(INV_SQRT_2, 0.0), - psi_new_complex(-INV_SQRT_2 * cos(lambda), -INV_SQRT_2 * sin(lambda)), - psi_new_complex(INV_SQRT_2 * cos(phi), INV_SQRT_2 * sin(phi)), - psi_new_complex(cos(phi + lambda) * INV_SQRT_2, sin(phi + lambda) * INV_SQRT_2)); + return psi_matrix( + 2, 2, psi_new_complex(INV_SQRT_2, 0.0), + psi_new_complex(-INV_SQRT_2 * cos(lambda), -INV_SQRT_2 * sin(lambda)), + psi_new_complex(INV_SQRT_2 * cos(phi), INV_SQRT_2 * sin(phi)), + psi_new_complex(cos(phi + lambda) * INV_SQRT_2, sin(phi + lambda) * INV_SQRT_2)); } struct PsiMatrix psi_u3_matrix(double theta, double phi, double lambda) { - double cos_h = cos(theta / 2.0); - double sin_h = sin(theta / 2.0); + double cos_h = cos(theta / 2.0); + double sin_h = sin(theta / 2.0); - return psi_matrix(2, 2, psi_new_complex(cos_h, 0.0), - psi_new_complex(-sin_h * cos(lambda), -sin_h * sin(lambda)), - psi_new_complex(sin_h * cos(phi), sin_h * sin(phi)), - psi_new_complex(cos_h * cos(phi + lambda), cos_h * sin(phi + lambda))); + return psi_matrix(2, 2, psi_new_complex(cos_h, 0.0), + psi_new_complex(-sin_h * cos(lambda), -sin_h * sin(lambda)), + psi_new_complex(sin_h * cos(phi), sin_h * sin(phi)), + psi_new_complex(cos_h * cos(phi + lambda), cos_h * sin(phi + lambda))); } struct PsiMatrix psi_crx_matrix(double theta) { - double cos_h = cos(theta / 2.0); - double sin_h = sin(theta / 2.0); + double cos_h = cos(theta / 2.0); + double sin_h = sin(theta / 2.0); - struct PsiMatrix m = psi_identity_matrix(4); - psi_set_matrix(&m, 2, 2, psi_new_complex(cos_h, 0.0)); - psi_set_matrix(&m, 2, 3, psi_new_complex(0.0, -sin_h)); - psi_set_matrix(&m, 3, 2, psi_new_complex(0.0, -sin_h)); - psi_set_matrix(&m, 3, 3, psi_new_complex(cos_h, 0.0)); + struct PsiMatrix m = psi_identity_matrix(4); + psi_set_matrix(&m, 2, 2, psi_new_complex(cos_h, 0.0)); + psi_set_matrix(&m, 2, 3, psi_new_complex(0.0, -sin_h)); + psi_set_matrix(&m, 3, 2, psi_new_complex(0.0, -sin_h)); + psi_set_matrix(&m, 3, 3, psi_new_complex(cos_h, 0.0)); - return m; + return m; } struct PsiMatrix psi_cry_matrix(double theta) { - double cos_h = cos(theta / 2.0); - double sin_h = sin(theta / 2.0); + double cos_h = cos(theta / 2.0); + double sin_h = sin(theta / 2.0); - struct PsiMatrix m = psi_identity_matrix(4); - psi_set_matrix(&m, 2, 2, psi_new_complex(cos_h, 0.0)); - psi_set_matrix(&m, 2, 3, psi_new_complex(-sin_h, 0.0)); - psi_set_matrix(&m, 3, 2, psi_new_complex(sin_h, 0.0)); - psi_set_matrix(&m, 3, 3, psi_new_complex(cos_h, 0.0)); + struct PsiMatrix m = psi_identity_matrix(4); + psi_set_matrix(&m, 2, 2, psi_new_complex(cos_h, 0.0)); + psi_set_matrix(&m, 2, 3, psi_new_complex(-sin_h, 0.0)); + psi_set_matrix(&m, 3, 2, psi_new_complex(sin_h, 0.0)); + psi_set_matrix(&m, 3, 3, psi_new_complex(cos_h, 0.0)); - return m; + return m; } struct PsiMatrix psi_crz_matrix(double theta) { - double half = theta / 2.0; + double half = theta / 2.0; - struct PsiMatrix m = psi_identity_matrix(4); - psi_set_matrix(&m, 2, 2, psi_new_complex(cos(half), -sin(half))); - psi_set_matrix(&m, 3, 3, psi_new_complex(cos(half), sin(half))); + struct PsiMatrix m = psi_identity_matrix(4); + psi_set_matrix(&m, 2, 2, psi_new_complex(cos(half), -sin(half))); + psi_set_matrix(&m, 3, 3, psi_new_complex(cos(half), sin(half))); - return m; + return m; } struct PsiMatrix psi_cp_matrix(double theta) { - struct PsiMatrix m = psi_identity_matrix(4); - psi_set_matrix(&m, 3, 3, psi_new_complex(cos(theta), sin(theta))); + struct PsiMatrix m = psi_identity_matrix(4); + psi_set_matrix(&m, 3, 3, psi_new_complex(cos(theta), sin(theta))); - return m; + return m; } struct PsiQuantumGate psi_hadamard_gate(void) { - struct PsiMatrix m = - psi_matrix(2, 2, psi_new_complex(INV_SQRT_2, 0.0), psi_new_complex(INV_SQRT_2, 0.0), - psi_new_complex(INV_SQRT_2, 0.0), psi_new_complex(-INV_SQRT_2, 0.0)); + struct PsiMatrix m = + psi_matrix(2, 2, psi_new_complex(INV_SQRT_2, 0.0), psi_new_complex(INV_SQRT_2, 0.0), + psi_new_complex(INV_SQRT_2, 0.0), psi_new_complex(-INV_SQRT_2, 0.0)); - return psi_new_quantum_gate_from_matrix("H", m); + return psi_new_quantum_gate_from_matrix("H", m); } struct PsiQuantumGate psi_pauli_x_gate(void) { - struct PsiMatrix m = psi_matrix(2, 2, psi_new_complex(0.0, 0.0), psi_new_complex(1.0, 0.0), - psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0)); + struct PsiMatrix m = psi_matrix(2, 2, psi_new_complex(0.0, 0.0), psi_new_complex(1.0, 0.0), + psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0)); - return psi_new_quantum_gate_from_matrix("X", m); + return psi_new_quantum_gate_from_matrix("X", m); } struct PsiQuantumGate psi_pauli_y_gate(void) { - struct PsiMatrix m = psi_matrix(2, 2, psi_new_complex(0.0, 0.0), psi_new_complex(0.0, -1.0), - psi_new_complex(0.0, 1.0), psi_new_complex(0.0, 0.0)); + struct PsiMatrix m = psi_matrix(2, 2, psi_new_complex(0.0, 0.0), psi_new_complex(0.0, -1.0), + psi_new_complex(0.0, 1.0), psi_new_complex(0.0, 0.0)); - return psi_new_quantum_gate_from_matrix("Y", m); + return psi_new_quantum_gate_from_matrix("Y", m); } struct PsiQuantumGate psi_pauli_z_gate(void) { - struct PsiMatrix m = psi_matrix(2, 2, psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(-1.0, 0.0)); + struct PsiMatrix m = psi_matrix(2, 2, psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(-1.0, 0.0)); - return psi_new_quantum_gate_from_matrix("Z", m); + return psi_new_quantum_gate_from_matrix("Z", m); } struct PsiQuantumGate psi_s_gate(void) { - struct PsiMatrix m = psi_matrix(2, 2, psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 1.0)); + struct PsiMatrix m = psi_matrix(2, 2, psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 1.0)); - return psi_new_quantum_gate_from_matrix("S", m); + return psi_new_quantum_gate_from_matrix("S", m); } struct PsiQuantumGate psi_t_gate(void) { - struct PsiMatrix m = - psi_matrix(2, 2, psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(INV_SQRT_2, INV_SQRT_2)); + struct PsiMatrix m = + psi_matrix(2, 2, psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(INV_SQRT_2, INV_SQRT_2)); - return psi_new_quantum_gate_from_matrix("T", m); + return psi_new_quantum_gate_from_matrix("T", m); } struct PsiQuantumGate psi_sdg_gate(void) { - struct PsiMatrix m = psi_matrix(2, 2, psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(0.0, -1.0)); + struct PsiMatrix m = psi_matrix(2, 2, psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(0.0, -1.0)); - return psi_new_quantum_gate_from_matrix("S†", m); + return psi_new_quantum_gate_from_matrix("S†", m); } struct PsiQuantumGate psi_tdg_gate(void) { - struct PsiMatrix m = - psi_matrix(2, 2, psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(INV_SQRT_2, -INV_SQRT_2)); + struct PsiMatrix m = + psi_matrix(2, 2, psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(INV_SQRT_2, -INV_SQRT_2)); - return psi_new_quantum_gate_from_matrix("T†", m); + return psi_new_quantum_gate_from_matrix("T†", m); } struct PsiQuantumGate psi_sx_gate(void) { - struct PsiMatrix m = psi_matrix(2, 2, psi_new_complex(0.5, 0.5), psi_new_complex(0.5, -0.5), - psi_new_complex(0.5, -0.5), psi_new_complex(0.5, 0.5)); + struct PsiMatrix m = psi_matrix(2, 2, psi_new_complex(0.5, 0.5), psi_new_complex(0.5, -0.5), + psi_new_complex(0.5, -0.5), psi_new_complex(0.5, 0.5)); - return psi_new_quantum_gate_from_matrix("√X", m); + return psi_new_quantum_gate_from_matrix("√X", m); } struct PsiQuantumGate psi_sxdg_gate(void) { - struct PsiMatrix m = psi_matrix(2, 2, psi_new_complex(0.5, -0.5), psi_new_complex(0.5, 0.5), - psi_new_complex(0.5, 0.5), psi_new_complex(0.5, -0.5)); + struct PsiMatrix m = psi_matrix(2, 2, psi_new_complex(0.5, -0.5), psi_new_complex(0.5, 0.5), + psi_new_complex(0.5, 0.5), psi_new_complex(0.5, -0.5)); - return psi_new_quantum_gate_from_matrix("√X†", m); + return psi_new_quantum_gate_from_matrix("√X†", m); } struct PsiQuantumGate psi_identity_gate(void) { - return psi_new_quantum_gate_from_matrix("I", psi_identity_matrix(2)); + return psi_new_quantum_gate_from_matrix("I", psi_identity_matrix(2)); } struct PsiQuantumGate psi_cnot_gate(void) { - struct PsiMatrix m = psi_identity_matrix(4); - psi_set_matrix(&m, 2, 2, psi_new_complex(0.0, 0.0)); - psi_set_matrix(&m, 3, 3, psi_new_complex(0.0, 0.0)); - psi_set_matrix(&m, 2, 3, psi_new_complex(1.0, 0.0)); - psi_set_matrix(&m, 3, 2, psi_new_complex(1.0, 0.0)); + struct PsiMatrix m = psi_identity_matrix(4); + psi_set_matrix(&m, 2, 2, psi_new_complex(0.0, 0.0)); + psi_set_matrix(&m, 3, 3, psi_new_complex(0.0, 0.0)); + psi_set_matrix(&m, 2, 3, psi_new_complex(1.0, 0.0)); + psi_set_matrix(&m, 3, 2, psi_new_complex(1.0, 0.0)); - return psi_new_quantum_gate_from_matrix("CNOT", m); + return psi_new_quantum_gate_from_matrix("CNOT", m); } struct PsiQuantumGate psi_cz_gate(void) { - struct PsiMatrix m = psi_identity_matrix(4); - psi_set_matrix(&m, 3, 3, psi_new_complex(-1.0, 0.0)); + struct PsiMatrix m = psi_identity_matrix(4); + psi_set_matrix(&m, 3, 3, psi_new_complex(-1.0, 0.0)); - return psi_new_quantum_gate_from_matrix("CZ", m); + return psi_new_quantum_gate_from_matrix("CZ", m); } struct PsiQuantumGate psi_swap_gate(void) { - struct PsiMatrix m = psi_identity_matrix(4); - psi_set_matrix(&m, 1, 1, psi_new_complex(0.0, 0.0)); - psi_set_matrix(&m, 2, 2, psi_new_complex(0.0, 0.0)); - psi_set_matrix(&m, 1, 2, psi_new_complex(1.0, 0.0)); - psi_set_matrix(&m, 2, 1, psi_new_complex(1.0, 0.0)); + struct PsiMatrix m = psi_identity_matrix(4); + psi_set_matrix(&m, 1, 1, psi_new_complex(0.0, 0.0)); + psi_set_matrix(&m, 2, 2, psi_new_complex(0.0, 0.0)); + psi_set_matrix(&m, 1, 2, psi_new_complex(1.0, 0.0)); + psi_set_matrix(&m, 2, 1, psi_new_complex(1.0, 0.0)); - return psi_new_quantum_gate_from_matrix("SWAP", m); + return psi_new_quantum_gate_from_matrix("SWAP", m); } struct PsiQuantumGate psi_iswap_gate(void) { - struct PsiMatrix m = psi_identity_matrix(4); - psi_set_matrix(&m, 1, 1, psi_new_complex(0.0, 0.0)); - psi_set_matrix(&m, 2, 2, psi_new_complex(0.0, 0.0)); - psi_set_matrix(&m, 1, 2, psi_new_complex(0.0, 1.0)); - psi_set_matrix(&m, 2, 1, psi_new_complex(0.0, 1.0)); + struct PsiMatrix m = psi_identity_matrix(4); + psi_set_matrix(&m, 1, 1, psi_new_complex(0.0, 0.0)); + psi_set_matrix(&m, 2, 2, psi_new_complex(0.0, 0.0)); + psi_set_matrix(&m, 1, 2, psi_new_complex(0.0, 1.0)); + psi_set_matrix(&m, 2, 1, psi_new_complex(0.0, 1.0)); - return psi_new_quantum_gate_from_matrix("iSWAP", m); + return psi_new_quantum_gate_from_matrix("iSWAP", m); } struct PsiQuantumGate psi_sqrt_swap_gate(void) { - struct PsiMatrix m = psi_identity_matrix(4); - psi_set_matrix(&m, 1, 1, psi_new_complex(0.5, 0.5)); - psi_set_matrix(&m, 1, 2, psi_new_complex(0.5, -0.5)); - psi_set_matrix(&m, 2, 1, psi_new_complex(0.5, -0.5)); - psi_set_matrix(&m, 2, 2, psi_new_complex(0.5, 0.5)); + struct PsiMatrix m = psi_identity_matrix(4); + psi_set_matrix(&m, 1, 1, psi_new_complex(0.5, 0.5)); + psi_set_matrix(&m, 1, 2, psi_new_complex(0.5, -0.5)); + psi_set_matrix(&m, 2, 1, psi_new_complex(0.5, -0.5)); + psi_set_matrix(&m, 2, 2, psi_new_complex(0.5, 0.5)); - return psi_new_quantum_gate_from_matrix("√SWAP", m); + return psi_new_quantum_gate_from_matrix("√SWAP", m); } struct PsiQuantumGate psi_toffoli_gate(void) { - struct PsiMatrix m = psi_identity_matrix(8); - psi_set_matrix(&m, 6, 6, psi_new_complex(0.0, 0.0)); - psi_set_matrix(&m, 7, 7, psi_new_complex(0.0, 0.0)); - psi_set_matrix(&m, 6, 7, psi_new_complex(1.0, 0.0)); - psi_set_matrix(&m, 7, 6, psi_new_complex(1.0, 0.0)); + struct PsiMatrix m = psi_identity_matrix(8); + psi_set_matrix(&m, 6, 6, psi_new_complex(0.0, 0.0)); + psi_set_matrix(&m, 7, 7, psi_new_complex(0.0, 0.0)); + psi_set_matrix(&m, 6, 7, psi_new_complex(1.0, 0.0)); + psi_set_matrix(&m, 7, 6, psi_new_complex(1.0, 0.0)); - return psi_new_quantum_gate_from_matrix("CCNOT", m); + return psi_new_quantum_gate_from_matrix("CCNOT", m); } struct PsiQuantumGate psi_fredkin_gate(void) { - struct PsiMatrix m = psi_identity_matrix(8); - psi_set_matrix(&m, 5, 5, psi_new_complex(0.0, 0.0)); - psi_set_matrix(&m, 6, 6, psi_new_complex(0.0, 0.0)); - psi_set_matrix(&m, 5, 6, psi_new_complex(1.0, 0.0)); - psi_set_matrix(&m, 6, 5, psi_new_complex(1.0, 0.0)); + struct PsiMatrix m = psi_identity_matrix(8); + psi_set_matrix(&m, 5, 5, psi_new_complex(0.0, 0.0)); + psi_set_matrix(&m, 6, 6, psi_new_complex(0.0, 0.0)); + psi_set_matrix(&m, 5, 6, psi_new_complex(1.0, 0.0)); + psi_set_matrix(&m, 6, 5, psi_new_complex(1.0, 0.0)); - return psi_new_quantum_gate_from_matrix("CSWAP", m); + return psi_new_quantum_gate_from_matrix("CSWAP", m); } diff --git a/src/core/kernel.c b/src/core/kernel.c index def0782..377982f 100644 --- a/src/core/kernel.c +++ b/src/core/kernel.c @@ -8,598 +8,598 @@ static char* dup_string(const char* s) { - size_t n = strlen(s) + 1; - char* p = malloc(n); - assert(p != NULL); - memcpy(p, s, n); + size_t n = strlen(s) + 1; + char* p = malloc(n); + assert(p != NULL); + memcpy(p, s, n); - return p; + return p; } static size_t* dup_targets(const size_t* targets, size_t count) { - size_t* p = malloc(count * sizeof(size_t)); - assert(p != NULL || count == 0); + size_t* p = malloc(count * sizeof(size_t)); + assert(p != NULL || count == 0); - if (count > 0) - memcpy(p, targets, count * sizeof(size_t)); + if (count > 0) + memcpy(p, targets, count * sizeof(size_t)); - return p; + return p; } static bool starts_with(const char* s, const char* prefix) { - return strncmp(s, prefix, strlen(prefix)) == 0; + return strncmp(s, prefix, strlen(prefix)) == 0; } static enum PsiGateType detect_gate_type(const char* name, struct PsiMatrix matrix) { - static const char* diagonal[] = { "Z", "S", "T", "Sdg", "Tdg", "Rz", - "P", "U1", "CZ", "CP", "CRz" }; - for (size_t i = 0; i < sizeof(diagonal) / sizeof(diagonal[0]); i++) - if (starts_with(name, diagonal[i])) - return PSI_GATE_TYPE_DIAGONAL; + static const char* diagonal[] = { "Z", "S", "T", "Sdg", "Tdg", "Rz", + "P", "U1", "CZ", "CP", "CRz" }; + for (size_t i = 0; i < sizeof(diagonal) / sizeof(diagonal[0]); i++) + if (starts_with(name, diagonal[i])) + return PSI_GATE_TYPE_DIAGONAL; - static const char* controlled[] = { "CNOT", "CZ", "SWAP", "CRx", "CRy", - "CRz", "CP", "CCNOT", "CSWAP" }; - for (size_t i = 0; i < sizeof(controlled) / sizeof(controlled[0]); i++) - if (starts_with(name, controlled[i])) - return PSI_GATE_TYPE_CONTROLLED; + static const char* controlled[] = { "CNOT", "CZ", "SWAP", "CRx", "CRy", + "CRz", "CP", "CCNOT", "CSWAP" }; + for (size_t i = 0; i < sizeof(controlled) / sizeof(controlled[0]); i++) + if (starts_with(name, controlled[i])) + return PSI_GATE_TYPE_CONTROLLED; - if (matrix.rows == 2 && matrix.cols == 2) - { - bool is_diag = fabs(matrix.data[1].real) < 1e-10 && - fabs(matrix.data[1].imaginary) < 1e-10 && fabs(matrix.data[2].real) < 1e-10 && - fabs(matrix.data[2].imaginary) < 1e-10; - if (is_diag) - return PSI_GATE_TYPE_DIAGONAL; - } + if (matrix.rows == 2 && matrix.cols == 2) + { + bool is_diag = fabs(matrix.data[1].real) < 1e-10 && + fabs(matrix.data[1].imaginary) < 1e-10 && fabs(matrix.data[2].real) < 1e-10 && + fabs(matrix.data[2].imaginary) < 1e-10; + if (is_diag) + return PSI_GATE_TYPE_DIAGONAL; + } - return PSI_GATE_TYPE_NON_DIAGONAL; + return PSI_GATE_TYPE_NON_DIAGONAL; } struct PsiKernel psi_new_kernel(const char* name, struct PsiMatrix matrix, const size_t* targets, size_t target_count) { - struct PsiKernel kernel; - kernel.matrix = matrix; - kernel.targets = dup_targets(targets, target_count); - kernel.target_count = target_count; - kernel.name = dup_string(name); - kernel.gate_type = detect_gate_type(name, matrix); + struct PsiKernel kernel; + kernel.matrix = matrix; + kernel.targets = dup_targets(targets, target_count); + kernel.target_count = target_count; + kernel.name = dup_string(name); + kernel.gate_type = detect_gate_type(name, matrix); - return kernel; + return kernel; } struct PsiKernel psi_clone_kernel(struct PsiKernel kernel) { - struct PsiKernel copy; - copy.matrix = psi_clone_matrix(kernel.matrix); - copy.targets = dup_targets(kernel.targets, kernel.target_count); - copy.target_count = kernel.target_count; - copy.name = dup_string(kernel.name); - copy.gate_type = kernel.gate_type; + struct PsiKernel copy; + copy.matrix = psi_clone_matrix(kernel.matrix); + copy.targets = dup_targets(kernel.targets, kernel.target_count); + copy.target_count = kernel.target_count; + copy.name = dup_string(kernel.name); + copy.gate_type = kernel.gate_type; - return copy; + return copy; } void psi_free_kernel(struct PsiKernel* kernel) { - psi_free_matrix(&kernel->matrix); - free(kernel->targets); - free(kernel->name); - kernel->targets = NULL; - kernel->name = NULL; - kernel->target_count = 0; + psi_free_matrix(&kernel->matrix); + free(kernel->targets); + free(kernel->name); + kernel->targets = NULL; + kernel->name = NULL; + kernel->target_count = 0; } static bool targets_equal(struct PsiKernel a, struct PsiKernel b) { - if (a.target_count != b.target_count) - return false; + if (a.target_count != b.target_count) + return false; - for (size_t i = 0; i < a.target_count; i++) - if (a.targets[i] != b.targets[i]) - return false; + for (size_t i = 0; i < a.target_count; i++) + if (a.targets[i] != b.targets[i]) + return false; - return true; + return true; } bool psi_kernels_share_qubits(struct PsiKernel a, struct PsiKernel b) { - for (size_t i = 0; i < a.target_count; i++) - for (size_t j = 0; j < b.target_count; j++) - if (a.targets[i] == b.targets[j]) - return true; + for (size_t i = 0; i < a.target_count; i++) + for (size_t j = 0; j < b.target_count; j++) + if (a.targets[i] == b.targets[j]) + return true; - return false; + return false; } bool psi_kernels_commute(struct PsiKernel a, struct PsiKernel b) { - if (!psi_kernels_share_qubits(a, b)) - return true; + if (!psi_kernels_share_qubits(a, b)) + return true; - if (a.gate_type == PSI_GATE_TYPE_DIAGONAL && b.gate_type == PSI_GATE_TYPE_DIAGONAL && - targets_equal(a, b)) - return true; + if (a.gate_type == PSI_GATE_TYPE_DIAGONAL && b.gate_type == PSI_GATE_TYPE_DIAGONAL && + targets_equal(a, b)) + return true; - return false; + return false; } bool psi_kernels_can_fuse(struct PsiKernel a, struct PsiKernel b) { - if (a.target_count != 1 || b.target_count != 1) - return false; + if (a.target_count != 1 || b.target_count != 1) + return false; - return a.targets[0] == b.targets[0]; + return a.targets[0] == b.targets[0]; } bool psi_fuse_kernels(struct PsiKernel a, struct PsiKernel b, struct PsiKernel* out) { - if (!psi_kernels_can_fuse(a, b)) - return false; + if (!psi_kernels_can_fuse(a, b)) + return false; - enum PsiGateType new_type = - a.gate_type == PSI_GATE_TYPE_DIAGONAL && b.gate_type == PSI_GATE_TYPE_DIAGONAL - ? PSI_GATE_TYPE_DIAGONAL - : PSI_GATE_TYPE_NON_DIAGONAL; + enum PsiGateType new_type = + a.gate_type == PSI_GATE_TYPE_DIAGONAL && b.gate_type == PSI_GATE_TYPE_DIAGONAL + ? PSI_GATE_TYPE_DIAGONAL + : PSI_GATE_TYPE_NON_DIAGONAL; - size_t name_len = strlen(a.name) + strlen(b.name) + 2; - char* fused_name = malloc(name_len); - assert(fused_name != NULL); - snprintf(fused_name, name_len, "%s+%s", a.name, b.name); + size_t name_len = strlen(a.name) + strlen(b.name) + 2; + char* fused_name = malloc(name_len); + assert(fused_name != NULL); + snprintf(fused_name, name_len, "%s+%s", a.name, b.name); - out->matrix = psi_dot_matrix(b.matrix, a.matrix); - out->targets = dup_targets(a.targets, a.target_count); - out->target_count = a.target_count; - out->name = fused_name; - out->gate_type = new_type; + out->matrix = psi_dot_matrix(b.matrix, a.matrix); + out->targets = dup_targets(a.targets, a.target_count); + out->target_count = a.target_count; + out->name = fused_name; + out->gate_type = new_type; - return true; + return true; } static struct PsiComplex* apply_kernel(const struct PsiComplex* state, struct PsiKernel kernel, size_t num_qubits) { - size_t dim = (size_t)1 << num_qubits; - size_t g = kernel.target_count; - size_t gate_dim = (size_t)1 << g; + size_t dim = (size_t)1 << num_qubits; + size_t g = kernel.target_count; + size_t gate_dim = (size_t)1 << g; - size_t* target_bits = malloc(g * sizeof(size_t)); - assert(target_bits != NULL || g == 0); - for (size_t k = 0; k < g; k++) - target_bits[k] = num_qubits - 1 - kernel.targets[k]; + size_t* target_bits = malloc(g * sizeof(size_t)); + assert(target_bits != NULL || g == 0); + for (size_t k = 0; k < g; k++) + target_bits[k] = num_qubits - 1 - kernel.targets[k]; - size_t non_target_mask = dim - 1; - for (size_t k = 0; k < g; k++) - non_target_mask &= ~((size_t)1 << target_bits[k]); + size_t non_target_mask = dim - 1; + for (size_t k = 0; k < g; k++) + non_target_mask &= ~((size_t)1 << target_bits[k]); - struct PsiComplex* new_state = malloc(dim * sizeof(struct PsiComplex)); - assert(new_state != NULL); + struct PsiComplex* new_state = malloc(dim * sizeof(struct PsiComplex)); + assert(new_state != NULL); - for (size_t i = 0; i < dim; i++) - { - size_t target_idx = 0; - for (size_t k = 0; k < g; k++) - if ((i >> target_bits[k]) & 1) - target_idx |= (size_t)1 << (g - 1 - k); + for (size_t i = 0; i < dim; i++) + { + size_t target_idx = 0; + for (size_t k = 0; k < g; k++) + if ((i >> target_bits[k]) & 1) + target_idx |= (size_t)1 << (g - 1 - k); - struct PsiComplex sum = psi_new_complex(0.0, 0.0); - for (size_t j = 0; j < gate_dim; j++) - { - struct PsiComplex gate_elem = kernel.matrix.data[target_idx * gate_dim + j]; - if (fabs(gate_elem.real) < 1e-15 && fabs(gate_elem.imaginary) < 1e-15) - continue; + struct PsiComplex sum = psi_new_complex(0.0, 0.0); + for (size_t j = 0; j < gate_dim; j++) + { + struct PsiComplex gate_elem = kernel.matrix.data[target_idx * gate_dim + j]; + if (fabs(gate_elem.real) < 1e-15 && fabs(gate_elem.imaginary) < 1e-15) + continue; - size_t source_idx = i & non_target_mask; - for (size_t k = 0; k < g; k++) - if ((j >> (g - 1 - k)) & 1) - source_idx |= (size_t)1 << target_bits[k]; + size_t source_idx = i & non_target_mask; + for (size_t k = 0; k < g; k++) + if ((j >> (g - 1 - k)) & 1) + source_idx |= (size_t)1 << target_bits[k]; - sum = psi_add_complex(sum, psi_mul_complex(gate_elem, state[source_idx])); - } + sum = psi_add_complex(sum, psi_mul_complex(gate_elem, state[source_idx])); + } - new_state[i] = sum; - } + new_state[i] = sum; + } - free(target_bits); - return new_state; + free(target_bits); + return new_state; } struct PsiKernelBatch psi_new_kernel_batch(size_t num_qubits) { - struct PsiKernelBatch batch; - batch.kernels = NULL; - batch.count = 0; - batch.capacity = 0; - batch.num_qubits = num_qubits; + struct PsiKernelBatch batch; + batch.kernels = NULL; + batch.count = 0; + batch.capacity = 0; + batch.num_qubits = num_qubits; - return batch; + return batch; } void psi_free_kernel_batch(struct PsiKernelBatch* batch) { - for (size_t i = 0; i < batch->count; i++) - psi_free_kernel(&batch->kernels[i]); + for (size_t i = 0; i < batch->count; i++) + psi_free_kernel(&batch->kernels[i]); - free(batch->kernels); - batch->kernels = NULL; - batch->count = 0; - batch->capacity = 0; + free(batch->kernels); + batch->kernels = NULL; + batch->count = 0; + batch->capacity = 0; } void psi_add_kernel(struct PsiKernelBatch* batch, struct PsiKernel kernel) { - if (batch->count == batch->capacity) - { - size_t new_capacity = batch->capacity == 0 ? 8 : batch->capacity * 2; - batch->kernels = realloc(batch->kernels, new_capacity * sizeof(struct PsiKernel)); - assert(batch->kernels != NULL); - batch->capacity = new_capacity; - } + if (batch->count == batch->capacity) + { + size_t new_capacity = batch->capacity == 0 ? 8 : batch->capacity * 2; + batch->kernels = realloc(batch->kernels, new_capacity * sizeof(struct PsiKernel)); + assert(batch->kernels != NULL); + batch->capacity = new_capacity; + } - batch->kernels[batch->count++] = kernel; + batch->kernels[batch->count++] = kernel; } void psi_optimize_kernel_batch(struct PsiKernelBatch* batch) { - if (batch->count < 2) - return; - - size_t original = batch->count; - struct PsiKernel* out = malloc(original * sizeof(struct PsiKernel)); - assert(out != NULL); - size_t out_count = 0; - - size_t i = 0; - while (i < batch->count) - { - if (i + 1 < batch->count) - { - struct PsiKernel fused; - if (psi_fuse_kernels(batch->kernels[i], batch->kernels[i + 1], &fused)) - { - psi_free_kernel(&batch->kernels[i]); - psi_free_kernel(&batch->kernels[i + 1]); - out[out_count++] = fused; - i += 2; - continue; - } - } - - out[out_count++] = batch->kernels[i]; - i += 1; - } - - free(batch->kernels); - batch->kernels = out; - batch->count = out_count; - batch->capacity = original; + if (batch->count < 2) + return; + + size_t original = batch->count; + struct PsiKernel* out = malloc(original * sizeof(struct PsiKernel)); + assert(out != NULL); + size_t out_count = 0; + + size_t i = 0; + while (i < batch->count) + { + if (i + 1 < batch->count) + { + struct PsiKernel fused; + if (psi_fuse_kernels(batch->kernels[i], batch->kernels[i + 1], &fused)) + { + psi_free_kernel(&batch->kernels[i]); + psi_free_kernel(&batch->kernels[i + 1]); + out[out_count++] = fused; + i += 2; + continue; + } + } + + out[out_count++] = batch->kernels[i]; + i += 1; + } + + free(batch->kernels); + batch->kernels = out; + batch->count = out_count; + batch->capacity = original; } void psi_execute_kernel_batch(struct PsiKernelBatch batch, struct PsiVector* state) { - size_t dim = (size_t)1 << batch.num_qubits; - assert(state->size == dim); + size_t dim = (size_t)1 << batch.num_qubits; + assert(state->size == dim); - for (size_t i = 0; i < batch.count; i++) - { - struct PsiComplex* next = apply_kernel(state->data, batch.kernels[i], batch.num_qubits); - free(state->data); - state->data = next; - } + for (size_t i = 0; i < batch.count; i++) + { + struct PsiComplex* next = apply_kernel(state->data, batch.kernels[i], batch.num_qubits); + free(state->data); + state->data = next; + } } void psi_apply_kernel(struct PsiVector* state, struct PsiKernel kernel, size_t num_qubits) { - struct PsiComplex* next = apply_kernel(state->data, kernel, num_qubits); - free(state->data); - state->data = next; + struct PsiComplex* next = apply_kernel(state->data, kernel, num_qubits); + free(state->data); + state->data = next; } static void push_kernel(struct PsiKernel** kernels, size_t* count, size_t* capacity, struct PsiKernel kernel) { - if (*count == *capacity) - { - size_t new_capacity = *capacity == 0 ? 8 : *capacity * 2; - *kernels = realloc(*kernels, new_capacity * sizeof(struct PsiKernel)); - assert(*kernels != NULL); - *capacity = new_capacity; - } + if (*count == *capacity) + { + size_t new_capacity = *capacity == 0 ? 8 : *capacity * 2; + *kernels = realloc(*kernels, new_capacity * sizeof(struct PsiKernel)); + assert(*kernels != NULL); + *capacity = new_capacity; + } - (*kernels)[(*count)++] = kernel; + (*kernels)[(*count)++] = kernel; } static bool layer_can_add(struct PsiExecutionLayer layer, struct PsiKernel kernel) { - for (size_t i = 0; i < layer.count; i++) - if (psi_kernels_share_qubits(layer.kernels[i], kernel)) - return false; + for (size_t i = 0; i < layer.count; i++) + if (psi_kernels_share_qubits(layer.kernels[i], kernel)) + return false; - return true; + return true; } static void free_layer(struct PsiExecutionLayer* layer) { - for (size_t i = 0; i < layer->count; i++) - psi_free_kernel(&layer->kernels[i]); + for (size_t i = 0; i < layer->count; i++) + psi_free_kernel(&layer->kernels[i]); - free(layer->kernels); - layer->kernels = NULL; - layer->count = 0; - layer->capacity = 0; + free(layer->kernels); + layer->kernels = NULL; + layer->count = 0; + layer->capacity = 0; } struct PsiStructureAwareBatch psi_new_structure_aware_batch(size_t num_qubits) { - struct PsiStructureAwareBatch batch; - batch.kernels = NULL; - batch.count = 0; - batch.capacity = 0; - batch.layers = NULL; - batch.layer_count = 0; - batch.layer_capacity = 0; - batch.num_qubits = num_qubits; - batch.optimised = false; + struct PsiStructureAwareBatch batch; + batch.kernels = NULL; + batch.count = 0; + batch.capacity = 0; + batch.layers = NULL; + batch.layer_count = 0; + batch.layer_capacity = 0; + batch.num_qubits = num_qubits; + batch.optimised = false; - return batch; + return batch; } static void clear_layers(struct PsiStructureAwareBatch* batch) { - for (size_t i = 0; i < batch->layer_count; i++) - free_layer(&batch->layers[i]); + for (size_t i = 0; i < batch->layer_count; i++) + free_layer(&batch->layers[i]); - free(batch->layers); - batch->layers = NULL; - batch->layer_count = 0; - batch->layer_capacity = 0; + free(batch->layers); + batch->layers = NULL; + batch->layer_count = 0; + batch->layer_capacity = 0; } void psi_free_structure_aware_batch(struct PsiStructureAwareBatch* batch) { - for (size_t i = 0; i < batch->count; i++) - psi_free_kernel(&batch->kernels[i]); + for (size_t i = 0; i < batch->count; i++) + psi_free_kernel(&batch->kernels[i]); - free(batch->kernels); - batch->kernels = NULL; - batch->count = 0; - batch->capacity = 0; - clear_layers(batch); + free(batch->kernels); + batch->kernels = NULL; + batch->count = 0; + batch->capacity = 0; + clear_layers(batch); } void psi_add_structure_aware_kernel(struct PsiStructureAwareBatch* batch, struct PsiKernel kernel) { - push_kernel(&batch->kernels, &batch->count, &batch->capacity, kernel); - batch->optimised = false; + push_kernel(&batch->kernels, &batch->count, &batch->capacity, kernel); + batch->optimised = false; } static struct PsiKernel remove_kernel_at(struct PsiStructureAwareBatch* batch, size_t index) { - struct PsiKernel removed = batch->kernels[index]; - for (size_t i = index; i + 1 < batch->count; i++) - batch->kernels[i] = batch->kernels[i + 1]; + struct PsiKernel removed = batch->kernels[index]; + for (size_t i = index; i + 1 < batch->count; i++) + batch->kernels[i] = batch->kernels[i + 1]; - batch->count--; - return removed; + batch->count--; + return removed; } static void insert_kernel_at(struct PsiStructureAwareBatch* batch, size_t index, struct PsiKernel kernel) { - if (batch->count == batch->capacity) - { - size_t new_capacity = batch->capacity == 0 ? 8 : batch->capacity * 2; - batch->kernels = realloc(batch->kernels, new_capacity * sizeof(struct PsiKernel)); - assert(batch->kernels != NULL); - batch->capacity = new_capacity; - } + if (batch->count == batch->capacity) + { + size_t new_capacity = batch->capacity == 0 ? 8 : batch->capacity * 2; + batch->kernels = realloc(batch->kernels, new_capacity * sizeof(struct PsiKernel)); + assert(batch->kernels != NULL); + batch->capacity = new_capacity; + } - for (size_t i = batch->count; i > index; i--) - batch->kernels[i] = batch->kernels[i - 1]; + for (size_t i = batch->count; i > index; i--) + batch->kernels[i] = batch->kernels[i - 1]; - batch->kernels[index] = kernel; - batch->count++; + batch->kernels[index] = kernel; + batch->count++; } static void reorder_commuting_gates(struct PsiStructureAwareBatch* batch) { - bool changed = true; - size_t iterations = 0; - const size_t MAX_ITERATIONS = 100; - - while (changed && iterations < MAX_ITERATIONS) - { - changed = false; - iterations++; - - for (size_t i = 0; i + 1 < batch->count; i++) - { - struct PsiKernel current = batch->kernels[i]; - struct PsiKernel next = batch->kernels[i + 1]; - - if (current.target_count != 1 || next.target_count != 1 || - current.targets[0] == next.targets[0] || !psi_kernels_commute(current, next)) - continue; - - for (size_t j = i + 2; j < batch->count; j++) - { - struct PsiKernel candidate = batch->kernels[j]; - if (candidate.target_count != 1 || candidate.targets[0] != current.targets[0]) - continue; - - bool can_move = true; - for (size_t k = i + 1; k < j; k++) - { - struct PsiKernel between = batch->kernels[k]; - if (psi_kernels_share_qubits(between, current) && - !psi_kernels_commute(current, between)) - { - can_move = false; - break; - } - } - - if (can_move && psi_kernels_can_fuse(current, candidate)) - { - struct PsiKernel moved = remove_kernel_at(batch, j); - insert_kernel_at(batch, i + 1, moved); - changed = true; - break; - } - } - } - } + bool changed = true; + size_t iterations = 0; + const size_t MAX_ITERATIONS = 100; + + while (changed && iterations < MAX_ITERATIONS) + { + changed = false; + iterations++; + + for (size_t i = 0; i + 1 < batch->count; i++) + { + struct PsiKernel current = batch->kernels[i]; + struct PsiKernel next = batch->kernels[i + 1]; + + if (current.target_count != 1 || next.target_count != 1 || + current.targets[0] == next.targets[0] || !psi_kernels_commute(current, next)) + continue; + + for (size_t j = i + 2; j < batch->count; j++) + { + struct PsiKernel candidate = batch->kernels[j]; + if (candidate.target_count != 1 || candidate.targets[0] != current.targets[0]) + continue; + + bool can_move = true; + for (size_t k = i + 1; k < j; k++) + { + struct PsiKernel between = batch->kernels[k]; + if (psi_kernels_share_qubits(between, current) && + !psi_kernels_commute(current, between)) + { + can_move = false; + break; + } + } + + if (can_move && psi_kernels_can_fuse(current, candidate)) + { + struct PsiKernel moved = remove_kernel_at(batch, j); + insert_kernel_at(batch, i + 1, moved); + changed = true; + break; + } + } + } + } } static void multi_pass_fusion(struct PsiStructureAwareBatch* batch) { - bool changed = true; - size_t iterations = 0; - const size_t MAX_ITERATIONS = 50; - - while (changed && iterations < MAX_ITERATIONS) - { - changed = false; - iterations++; - - struct PsiKernel* new_kernels = NULL; - size_t new_count = 0; - size_t new_capacity = 0; - - size_t i = 0; - while (i < batch->count) - { - if (i + 1 < batch->count) - { - struct PsiKernel fused; - if (psi_fuse_kernels(batch->kernels[i], batch->kernels[i + 1], &fused)) - { - psi_free_kernel(&batch->kernels[i]); - psi_free_kernel(&batch->kernels[i + 1]); - push_kernel(&new_kernels, &new_count, &new_capacity, fused); - i += 2; - changed = true; - continue; - } - } - - push_kernel(&new_kernels, &new_count, &new_capacity, batch->kernels[i]); - i++; - } - - free(batch->kernels); - batch->kernels = new_kernels; - batch->count = new_count; - batch->capacity = new_capacity; - } + bool changed = true; + size_t iterations = 0; + const size_t MAX_ITERATIONS = 50; + + while (changed && iterations < MAX_ITERATIONS) + { + changed = false; + iterations++; + + struct PsiKernel* new_kernels = NULL; + size_t new_count = 0; + size_t new_capacity = 0; + + size_t i = 0; + while (i < batch->count) + { + if (i + 1 < batch->count) + { + struct PsiKernel fused; + if (psi_fuse_kernels(batch->kernels[i], batch->kernels[i + 1], &fused)) + { + psi_free_kernel(&batch->kernels[i]); + psi_free_kernel(&batch->kernels[i + 1]); + push_kernel(&new_kernels, &new_count, &new_capacity, fused); + i += 2; + changed = true; + continue; + } + } + + push_kernel(&new_kernels, &new_count, &new_capacity, batch->kernels[i]); + i++; + } + + free(batch->kernels); + batch->kernels = new_kernels; + batch->count = new_count; + batch->capacity = new_capacity; + } } static void build_execution_layers(struct PsiStructureAwareBatch* batch) { - clear_layers(batch); - - for (size_t i = 0; i < batch->count; i++) - { - struct PsiKernel kernel = batch->kernels[i]; - bool placed = false; - - for (size_t l = 0; l < batch->layer_count; l++) - if (layer_can_add(batch->layers[l], kernel)) - { - struct PsiExecutionLayer* layer = &batch->layers[l]; - push_kernel(&layer->kernels, &layer->count, &layer->capacity, - psi_clone_kernel(kernel)); - placed = true; - break; - } - - if (placed) - continue; - - if (batch->layer_count == batch->layer_capacity) - { - size_t new_capacity = batch->layer_capacity == 0 ? 4 : batch->layer_capacity * 2; - batch->layers = realloc(batch->layers, new_capacity * sizeof(struct PsiExecutionLayer)); - assert(batch->layers != NULL); - batch->layer_capacity = new_capacity; - } - - struct PsiExecutionLayer layer; - layer.kernels = NULL; - layer.count = 0; - layer.capacity = 0; - push_kernel(&layer.kernels, &layer.count, &layer.capacity, psi_clone_kernel(kernel)); - batch->layers[batch->layer_count++] = layer; - } + clear_layers(batch); + + for (size_t i = 0; i < batch->count; i++) + { + struct PsiKernel kernel = batch->kernels[i]; + bool placed = false; + + for (size_t l = 0; l < batch->layer_count; l++) + if (layer_can_add(batch->layers[l], kernel)) + { + struct PsiExecutionLayer* layer = &batch->layers[l]; + push_kernel(&layer->kernels, &layer->count, &layer->capacity, + psi_clone_kernel(kernel)); + placed = true; + break; + } + + if (placed) + continue; + + if (batch->layer_count == batch->layer_capacity) + { + size_t new_capacity = batch->layer_capacity == 0 ? 4 : batch->layer_capacity * 2; + batch->layers = realloc(batch->layers, new_capacity * sizeof(struct PsiExecutionLayer)); + assert(batch->layers != NULL); + batch->layer_capacity = new_capacity; + } + + struct PsiExecutionLayer layer; + layer.kernels = NULL; + layer.count = 0; + layer.capacity = 0; + push_kernel(&layer.kernels, &layer.count, &layer.capacity, psi_clone_kernel(kernel)); + batch->layers[batch->layer_count++] = layer; + } } void psi_optimize_structure_aware_batch(struct PsiStructureAwareBatch* batch) { - if (batch->optimised || batch->count < 2) - return; + if (batch->optimised || batch->count < 2) + return; - reorder_commuting_gates(batch); - multi_pass_fusion(batch); - build_execution_layers(batch); - batch->optimised = true; + reorder_commuting_gates(batch); + multi_pass_fusion(batch); + build_execution_layers(batch); + batch->optimised = true; } void psi_execute_structure_aware_batch(struct PsiStructureAwareBatch batch, struct PsiVector* state) { - size_t dim = (size_t)1 << batch.num_qubits; - assert(state->size == dim); + size_t dim = (size_t)1 << batch.num_qubits; + assert(state->size == dim); - for (size_t i = 0; i < batch.count; i++) - { - struct PsiComplex* next = apply_kernel(state->data, batch.kernels[i], batch.num_qubits); - free(state->data); - state->data = next; - } + for (size_t i = 0; i < batch.count; i++) + { + struct PsiComplex* next = apply_kernel(state->data, batch.kernels[i], batch.num_qubits); + free(state->data); + state->data = next; + } } void psi_execute_structure_aware_batch_layered(struct PsiStructureAwareBatch batch, struct PsiVector* state) { - size_t dim = (size_t)1 << batch.num_qubits; - assert(state->size == dim); + size_t dim = (size_t)1 << batch.num_qubits; + assert(state->size == dim); - for (size_t l = 0; l < batch.layer_count; l++) - for (size_t k = 0; k < batch.layers[l].count; k++) - { - struct PsiComplex* next = - apply_kernel(state->data, batch.layers[l].kernels[k], batch.num_qubits); - free(state->data); - state->data = next; - } + for (size_t l = 0; l < batch.layer_count; l++) + for (size_t k = 0; k < batch.layers[l].count; k++) + { + struct PsiComplex* next = + apply_kernel(state->data, batch.layers[l].kernels[k], batch.num_qubits); + free(state->data); + state->data = next; + } } struct PsiKernelStats psi_structure_aware_batch_stats(struct PsiStructureAwareBatch batch) { - struct PsiKernelStats stats; - stats.total_kernels = batch.count; - stats.single_qubit = 0; - stats.two_qubit = 0; - stats.multi_qubit = 0; - stats.diagonal = 0; - stats.execution_layers = batch.layer_count; - - for (size_t i = 0; i < batch.count; i++) - { - struct PsiKernel kernel = batch.kernels[i]; - - if (kernel.target_count == 1) - stats.single_qubit++; - else if (kernel.target_count == 2) - stats.two_qubit++; - else if (kernel.target_count > 2) - stats.multi_qubit++; - - if (kernel.gate_type == PSI_GATE_TYPE_DIAGONAL) - stats.diagonal++; - } - - return stats; + struct PsiKernelStats stats; + stats.total_kernels = batch.count; + stats.single_qubit = 0; + stats.two_qubit = 0; + stats.multi_qubit = 0; + stats.diagonal = 0; + stats.execution_layers = batch.layer_count; + + for (size_t i = 0; i < batch.count; i++) + { + struct PsiKernel kernel = batch.kernels[i]; + + if (kernel.target_count == 1) + stats.single_qubit++; + else if (kernel.target_count == 2) + stats.two_qubit++; + else if (kernel.target_count > 2) + stats.multi_qubit++; + + if (kernel.gate_type == PSI_GATE_TYPE_DIAGONAL) + stats.diagonal++; + } + + return stats; } diff --git a/src/core/noise.c b/src/core/noise.c index 5f141c8..0f73c48 100644 --- a/src/core/noise.c +++ b/src/core/noise.c @@ -7,415 +7,415 @@ struct PsiKrausOperator psi_new_kraus_operator(const char* name, struct PsiMatrix matrix) { - return (struct PsiKrausOperator){ - matrix, - name, - }; + return (struct PsiKrausOperator){ + matrix, + name, + }; } void psi_free_kraus_operator(struct PsiKrausOperator* op) { - psi_free_matrix(&op->matrix); + psi_free_matrix(&op->matrix); } struct PsiNoiseChannel psi_new_noise_channel(const char* name, const struct PsiKrausOperator* operators, size_t count, size_t num_qubits) { - struct PsiKrausOperator* owned = malloc(count * sizeof(struct PsiKrausOperator)); - assert(owned != NULL || count == 0); - - if (count > 0) - memcpy(owned, operators, count * sizeof(struct PsiKrausOperator)); - - return (struct PsiNoiseChannel){ - name, - owned, - count, - num_qubits, - }; + struct PsiKrausOperator* owned = malloc(count * sizeof(struct PsiKrausOperator)); + assert(owned != NULL || count == 0); + + if (count > 0) + memcpy(owned, operators, count * sizeof(struct PsiKrausOperator)); + + return (struct PsiNoiseChannel){ + name, + owned, + count, + num_qubits, + }; } void psi_free_noise_channel(struct PsiNoiseChannel* channel) { - for (size_t i = 0; i < channel->operator_count; i++) - psi_free_matrix(&channel->operators[i].matrix); + for (size_t i = 0; i < channel->operator_count; i++) + psi_free_matrix(&channel->operators[i].matrix); - free(channel->operators); - channel->operators = NULL; - channel->operator_count = 0; + free(channel->operators); + channel->operators = NULL; + channel->operator_count = 0; } struct PsiNoiseChannel psi_depolarising_channel(double p) { - double sqrt_1_p = sqrt(1.0 - p); - double sqrt_p3 = sqrt(p / 3.0); - - struct PsiKrausOperator ops[] = { - psi_new_kraus_operator("K0", - psi_matrix(2, 2, psi_new_complex(sqrt_1_p, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), - psi_new_complex(sqrt_1_p, 0.0))), - psi_new_kraus_operator( - "K1(X)", - psi_matrix(2, 2, psi_new_complex(0.0, 0.0), psi_new_complex(sqrt_p3, 0.0), - psi_new_complex(sqrt_p3, 0.0), psi_new_complex(0.0, 0.0))), - psi_new_kraus_operator( - "K2(Y)", - psi_matrix(2, 2, psi_new_complex(0.0, 0.0), psi_new_complex(0.0, -sqrt_p3), - psi_new_complex(0.0, sqrt_p3), psi_new_complex(0.0, 0.0))), - psi_new_kraus_operator("K3(Z)", - psi_matrix(2, 2, psi_new_complex(sqrt_p3, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), - psi_new_complex(-sqrt_p3, 0.0))), - }; - - return psi_new_noise_channel("Depolarising", ops, 4, 1); + double sqrt_1_p = sqrt(1.0 - p); + double sqrt_p3 = sqrt(p / 3.0); + + struct PsiKrausOperator ops[] = { + psi_new_kraus_operator("K0", + psi_matrix(2, 2, psi_new_complex(sqrt_1_p, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), + psi_new_complex(sqrt_1_p, 0.0))), + psi_new_kraus_operator( + "K1(X)", + psi_matrix(2, 2, psi_new_complex(0.0, 0.0), psi_new_complex(sqrt_p3, 0.0), + psi_new_complex(sqrt_p3, 0.0), psi_new_complex(0.0, 0.0))), + psi_new_kraus_operator( + "K2(Y)", + psi_matrix(2, 2, psi_new_complex(0.0, 0.0), psi_new_complex(0.0, -sqrt_p3), + psi_new_complex(0.0, sqrt_p3), psi_new_complex(0.0, 0.0))), + psi_new_kraus_operator("K3(Z)", + psi_matrix(2, 2, psi_new_complex(sqrt_p3, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), + psi_new_complex(-sqrt_p3, 0.0))), + }; + + return psi_new_noise_channel("Depolarising", ops, 4, 1); } struct PsiNoiseChannel psi_amplitude_damping_channel(double gamma) { - double sqrt_gamma = sqrt(gamma); - double sqrt_1_gamma = sqrt(1.0 - gamma); - - struct PsiKrausOperator ops[] = { - psi_new_kraus_operator("K0", - psi_matrix(2, 2, psi_new_complex(1.0, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), - psi_new_complex(sqrt_1_gamma, 0.0))), - psi_new_kraus_operator("K1", - psi_matrix(2, 2, psi_new_complex(0.0, 0.0), - psi_new_complex(sqrt_gamma, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0))), - }; - - return psi_new_noise_channel("AmplitudeDamping", ops, 2, 1); + double sqrt_gamma = sqrt(gamma); + double sqrt_1_gamma = sqrt(1.0 - gamma); + + struct PsiKrausOperator ops[] = { + psi_new_kraus_operator("K0", + psi_matrix(2, 2, psi_new_complex(1.0, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), + psi_new_complex(sqrt_1_gamma, 0.0))), + psi_new_kraus_operator("K1", + psi_matrix(2, 2, psi_new_complex(0.0, 0.0), + psi_new_complex(sqrt_gamma, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0))), + }; + + return psi_new_noise_channel("AmplitudeDamping", ops, 2, 1); } struct PsiNoiseChannel psi_phase_damping_channel(double gamma) { - double sqrt_gamma = sqrt(gamma); - double sqrt_1_gamma = sqrt(1.0 - gamma); - - struct PsiKrausOperator ops[] = { - psi_new_kraus_operator("K0", - psi_matrix(2, 2, psi_new_complex(1.0, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), - psi_new_complex(sqrt_1_gamma, 0.0))), - psi_new_kraus_operator("K1", - psi_matrix(2, 2, psi_new_complex(0.0, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), - psi_new_complex(sqrt_gamma, 0.0))), - }; - - return psi_new_noise_channel("PhaseDamping", ops, 2, 1); + double sqrt_gamma = sqrt(gamma); + double sqrt_1_gamma = sqrt(1.0 - gamma); + + struct PsiKrausOperator ops[] = { + psi_new_kraus_operator("K0", + psi_matrix(2, 2, psi_new_complex(1.0, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), + psi_new_complex(sqrt_1_gamma, 0.0))), + psi_new_kraus_operator("K1", + psi_matrix(2, 2, psi_new_complex(0.0, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), + psi_new_complex(sqrt_gamma, 0.0))), + }; + + return psi_new_noise_channel("PhaseDamping", ops, 2, 1); } struct PsiNoiseChannel psi_bit_flip_channel(double p) { - double sqrt_1_p = sqrt(1.0 - p); - double sqrt_p = sqrt(p); - - struct PsiKrausOperator ops[] = { - psi_new_kraus_operator("K0(I)", - psi_matrix(2, 2, psi_new_complex(sqrt_1_p, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), - psi_new_complex(sqrt_1_p, 0.0))), - psi_new_kraus_operator("K1(X)", - psi_matrix(2, 2, psi_new_complex(0.0, 0.0), - psi_new_complex(sqrt_p, 0.0), - psi_new_complex(sqrt_p, 0.0), psi_new_complex(0.0, 0.0))), - }; - - return psi_new_noise_channel("BitFlip", ops, 2, 1); + double sqrt_1_p = sqrt(1.0 - p); + double sqrt_p = sqrt(p); + + struct PsiKrausOperator ops[] = { + psi_new_kraus_operator("K0(I)", + psi_matrix(2, 2, psi_new_complex(sqrt_1_p, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), + psi_new_complex(sqrt_1_p, 0.0))), + psi_new_kraus_operator("K1(X)", + psi_matrix(2, 2, psi_new_complex(0.0, 0.0), + psi_new_complex(sqrt_p, 0.0), + psi_new_complex(sqrt_p, 0.0), psi_new_complex(0.0, 0.0))), + }; + + return psi_new_noise_channel("BitFlip", ops, 2, 1); } struct PsiNoiseChannel psi_phase_flip_channel(double p) { - double sqrt_1_p = sqrt(1.0 - p); - double sqrt_p = sqrt(p); - - struct PsiKrausOperator ops[] = { - psi_new_kraus_operator("K0(I)", - psi_matrix(2, 2, psi_new_complex(sqrt_1_p, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), - psi_new_complex(sqrt_1_p, 0.0))), - psi_new_kraus_operator("K1(Z)", - psi_matrix(2, 2, psi_new_complex(sqrt_p, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), - psi_new_complex(-sqrt_p, 0.0))), - }; - - return psi_new_noise_channel("PhaseFlip", ops, 2, 1); + double sqrt_1_p = sqrt(1.0 - p); + double sqrt_p = sqrt(p); + + struct PsiKrausOperator ops[] = { + psi_new_kraus_operator("K0(I)", + psi_matrix(2, 2, psi_new_complex(sqrt_1_p, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), + psi_new_complex(sqrt_1_p, 0.0))), + psi_new_kraus_operator("K1(Z)", + psi_matrix(2, 2, psi_new_complex(sqrt_p, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), + psi_new_complex(-sqrt_p, 0.0))), + }; + + return psi_new_noise_channel("PhaseFlip", ops, 2, 1); } struct PsiNoiseChannel psi_bit_phase_flip_channel(double p) { - double sqrt_1_p = sqrt(1.0 - p); - double sqrt_p = sqrt(p); - - struct PsiKrausOperator ops[] = { - psi_new_kraus_operator("K0(I)", - psi_matrix(2, 2, psi_new_complex(sqrt_1_p, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), - psi_new_complex(sqrt_1_p, 0.0))), - psi_new_kraus_operator("K1(Y)", - psi_matrix(2, 2, psi_new_complex(0.0, 0.0), - psi_new_complex(0.0, -sqrt_p), - psi_new_complex(0.0, sqrt_p), psi_new_complex(0.0, 0.0))), - }; - - return psi_new_noise_channel("BitPhaseFlip", ops, 2, 1); + double sqrt_1_p = sqrt(1.0 - p); + double sqrt_p = sqrt(p); + + struct PsiKrausOperator ops[] = { + psi_new_kraus_operator("K0(I)", + psi_matrix(2, 2, psi_new_complex(sqrt_1_p, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), + psi_new_complex(sqrt_1_p, 0.0))), + psi_new_kraus_operator("K1(Y)", + psi_matrix(2, 2, psi_new_complex(0.0, 0.0), + psi_new_complex(0.0, -sqrt_p), + psi_new_complex(0.0, sqrt_p), psi_new_complex(0.0, 0.0))), + }; + + return psi_new_noise_channel("BitPhaseFlip", ops, 2, 1); } struct PsiNoiseChannel psi_generalised_amplitude_damping_channel(double p, double gamma) { - double sqrt_p = sqrt(p); - double sqrt_1_p = sqrt(1.0 - p); - double sqrt_gamma = sqrt(gamma); - double sqrt_1_gamma = sqrt(1.0 - gamma); - - struct PsiKrausOperator ops[] = { - psi_new_kraus_operator("K0", - psi_matrix(2, 2, psi_new_complex(sqrt_p, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), - psi_new_complex(sqrt_p * sqrt_1_gamma, 0.0))), - psi_new_kraus_operator("K1", - psi_matrix(2, 2, psi_new_complex(0.0, 0.0), - psi_new_complex(sqrt_p * sqrt_gamma, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0))), - psi_new_kraus_operator("K2", - psi_matrix(2, 2, psi_new_complex(sqrt_1_p * sqrt_1_gamma, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), - psi_new_complex(sqrt_1_p, 0.0))), - psi_new_kraus_operator( - "K3", - psi_matrix(2, 2, psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), - psi_new_complex(sqrt_1_p * sqrt_gamma, 0.0), psi_new_complex(0.0, 0.0))), - }; - - return psi_new_noise_channel("GeneralisedAmplitudeDamping", ops, 4, 1); + double sqrt_p = sqrt(p); + double sqrt_1_p = sqrt(1.0 - p); + double sqrt_gamma = sqrt(gamma); + double sqrt_1_gamma = sqrt(1.0 - gamma); + + struct PsiKrausOperator ops[] = { + psi_new_kraus_operator("K0", + psi_matrix(2, 2, psi_new_complex(sqrt_p, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), + psi_new_complex(sqrt_p * sqrt_1_gamma, 0.0))), + psi_new_kraus_operator("K1", + psi_matrix(2, 2, psi_new_complex(0.0, 0.0), + psi_new_complex(sqrt_p * sqrt_gamma, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0))), + psi_new_kraus_operator("K2", + psi_matrix(2, 2, psi_new_complex(sqrt_1_p * sqrt_1_gamma, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), + psi_new_complex(sqrt_1_p, 0.0))), + psi_new_kraus_operator( + "K3", + psi_matrix(2, 2, psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), + psi_new_complex(sqrt_1_p * sqrt_gamma, 0.0), psi_new_complex(0.0, 0.0))), + }; + + return psi_new_noise_channel("GeneralisedAmplitudeDamping", ops, 4, 1); } struct PsiDensityMatrix psi_new_density_matrix(size_t num_qubits) { - size_t dim = (size_t)1 << num_qubits; - struct PsiComplex* data = calloc(dim * dim, sizeof(struct PsiComplex)); - assert(data != NULL); - data[0] = psi_new_complex(1.0, 0.0); - - return (struct PsiDensityMatrix){ - data, - dim, - num_qubits, - }; + size_t dim = (size_t)1 << num_qubits; + struct PsiComplex* data = calloc(dim * dim, sizeof(struct PsiComplex)); + assert(data != NULL); + data[0] = psi_new_complex(1.0, 0.0); + + return (struct PsiDensityMatrix){ + data, + dim, + num_qubits, + }; } struct PsiDensityMatrix psi_new_density_matrix_from_state(const struct PsiComplex* state, size_t len) { - size_t dim = len; - size_t num_qubits = 0; - while (((size_t)1 << num_qubits) < dim) - num_qubits++; - - struct PsiComplex* data = malloc(dim * dim * sizeof(struct PsiComplex)); - assert(data != NULL); - - for (size_t i = 0; i < dim; i++) - for (size_t j = 0; j < dim; j++) - data[i * dim + j] = psi_mul_complex(state[i], psi_conjugate_complex(state[j])); - - return (struct PsiDensityMatrix){ - data, - dim, - num_qubits, - }; + size_t dim = len; + size_t num_qubits = 0; + while (((size_t)1 << num_qubits) < dim) + num_qubits++; + + struct PsiComplex* data = malloc(dim * dim * sizeof(struct PsiComplex)); + assert(data != NULL); + + for (size_t i = 0; i < dim; i++) + for (size_t j = 0; j < dim; j++) + data[i * dim + j] = psi_mul_complex(state[i], psi_conjugate_complex(state[j])); + + return (struct PsiDensityMatrix){ + data, + dim, + num_qubits, + }; } void psi_free_density_matrix(struct PsiDensityMatrix* dm) { - free(dm->data); - dm->data = NULL; - dm->dim = 0; - dm->num_qubits = 0; + free(dm->data); + dm->data = NULL; + dm->dim = 0; + dm->num_qubits = 0; } struct PsiComplex psi_get_density_matrix(struct PsiDensityMatrix dm, size_t row, size_t col) { - assert(row < dm.dim && col < dm.dim); - return dm.data[row * dm.dim + col]; + assert(row < dm.dim && col < dm.dim); + return dm.data[row * dm.dim + col]; } void psi_set_density_matrix(struct PsiDensityMatrix* dm, size_t row, size_t col, struct PsiComplex value) { - assert(row < dm->dim && col < dm->dim); - dm->data[row * dm->dim + col] = value; + assert(row < dm->dim && col < dm->dim); + dm->data[row * dm->dim + col] = value; } struct PsiComplex psi_trace_density_matrix(struct PsiDensityMatrix dm) { - struct PsiComplex sum = psi_new_complex(0.0, 0.0); - for (size_t i = 0; i < dm.dim; i++) - sum = psi_add_complex(sum, dm.data[i * dm.dim + i]); + struct PsiComplex sum = psi_new_complex(0.0, 0.0); + for (size_t i = 0; i < dm.dim; i++) + sum = psi_add_complex(sum, dm.data[i * dm.dim + i]); - return sum; + return sum; } double psi_purity_density_matrix(struct PsiDensityMatrix dm) { - struct PsiComplex sum = psi_new_complex(0.0, 0.0); - for (size_t i = 0; i < dm.dim; i++) - for (size_t j = 0; j < dm.dim; j++) - sum = psi_add_complex( - sum, psi_mul_complex(dm.data[i * dm.dim + j], dm.data[j * dm.dim + i])); + struct PsiComplex sum = psi_new_complex(0.0, 0.0); + for (size_t i = 0; i < dm.dim; i++) + for (size_t j = 0; j < dm.dim; j++) + sum = psi_add_complex( + sum, psi_mul_complex(dm.data[i * dm.dim + j], dm.data[j * dm.dim + i])); - return sum.real; + return sum.real; } bool psi_is_pure_density_matrix(struct PsiDensityMatrix dm, double tolerance) { - return fabs(psi_purity_density_matrix(dm) - 1.0) < tolerance; + return fabs(psi_purity_density_matrix(dm) - 1.0) < tolerance; } void psi_density_matrix_probabilities(struct PsiDensityMatrix dm, double* out) { - for (size_t i = 0; i < dm.dim; i++) - out[i] = dm.data[i * dm.dim + i].real; + for (size_t i = 0; i < dm.dim; i++) + out[i] = dm.data[i * dm.dim + i].real; } void psi_apply_unitary_density_matrix(struct PsiDensityMatrix* dm, struct PsiMatrix gate, const size_t* targets, size_t target_count) { - size_t g = target_count; - size_t gate_dim = (size_t)1 << g; - size_t dim = dm->dim; - - size_t* target_bits = malloc(g * sizeof(size_t)); - assert(target_bits != NULL || g == 0); - for (size_t t = 0; t < g; t++) - target_bits[t] = dm->num_qubits - 1 - targets[t]; - - size_t non_target_mask = dim - 1; - for (size_t t = 0; t < g; t++) - non_target_mask &= ~((size_t)1 << target_bits[t]); - - struct PsiComplex* new_data = calloc(dim * dim, sizeof(struct PsiComplex)); - assert(new_data != NULL); - - for (size_t i = 0; i < dim; i++) - for (size_t j = 0; j < dim; j++) - { - struct PsiComplex sum = psi_new_complex(0.0, 0.0); - - for (size_t k = 0; k < gate_dim; k++) - for (size_t l = 0; l < gate_dim; l++) - { - size_t src_i = i & non_target_mask; - size_t src_j = j & non_target_mask; - - for (size_t idx = 0; idx < g; idx++) - { - if ((k >> (g - 1 - idx)) & 1) - src_i |= (size_t)1 << target_bits[idx]; - if ((l >> (g - 1 - idx)) & 1) - src_j |= (size_t)1 << target_bits[idx]; - } - - size_t tgt_i = 0; - size_t tgt_j = 0; - for (size_t idx = 0; idx < g; idx++) - { - if ((i >> target_bits[idx]) & 1) - tgt_i |= (size_t)1 << (g - 1 - idx); - if ((j >> target_bits[idx]) & 1) - tgt_j |= (size_t)1 << (g - 1 - idx); - } - - struct PsiComplex u_ik = gate.data[tgt_i * gate_dim + k]; - struct PsiComplex u_jl_dag = - psi_conjugate_complex(gate.data[tgt_j * gate_dim + l]); - struct PsiComplex rho_kl = dm->data[src_i * dim + src_j]; - - sum = psi_add_complex(sum, - psi_mul_complex(psi_mul_complex(u_ik, rho_kl), u_jl_dag)); - } - - new_data[i * dim + j] = sum; - } - - free(target_bits); - free(dm->data); - dm->data = new_data; + size_t g = target_count; + size_t gate_dim = (size_t)1 << g; + size_t dim = dm->dim; + + size_t* target_bits = malloc(g * sizeof(size_t)); + assert(target_bits != NULL || g == 0); + for (size_t t = 0; t < g; t++) + target_bits[t] = dm->num_qubits - 1 - targets[t]; + + size_t non_target_mask = dim - 1; + for (size_t t = 0; t < g; t++) + non_target_mask &= ~((size_t)1 << target_bits[t]); + + struct PsiComplex* new_data = calloc(dim * dim, sizeof(struct PsiComplex)); + assert(new_data != NULL); + + for (size_t i = 0; i < dim; i++) + for (size_t j = 0; j < dim; j++) + { + struct PsiComplex sum = psi_new_complex(0.0, 0.0); + + for (size_t k = 0; k < gate_dim; k++) + for (size_t l = 0; l < gate_dim; l++) + { + size_t src_i = i & non_target_mask; + size_t src_j = j & non_target_mask; + + for (size_t idx = 0; idx < g; idx++) + { + if ((k >> (g - 1 - idx)) & 1) + src_i |= (size_t)1 << target_bits[idx]; + if ((l >> (g - 1 - idx)) & 1) + src_j |= (size_t)1 << target_bits[idx]; + } + + size_t tgt_i = 0; + size_t tgt_j = 0; + for (size_t idx = 0; idx < g; idx++) + { + if ((i >> target_bits[idx]) & 1) + tgt_i |= (size_t)1 << (g - 1 - idx); + if ((j >> target_bits[idx]) & 1) + tgt_j |= (size_t)1 << (g - 1 - idx); + } + + struct PsiComplex u_ik = gate.data[tgt_i * gate_dim + k]; + struct PsiComplex u_jl_dag = + psi_conjugate_complex(gate.data[tgt_j * gate_dim + l]); + struct PsiComplex rho_kl = dm->data[src_i * dim + src_j]; + + sum = psi_add_complex(sum, + psi_mul_complex(psi_mul_complex(u_ik, rho_kl), u_jl_dag)); + } + + new_data[i * dim + j] = sum; + } + + free(target_bits); + free(dm->data); + dm->data = new_data; } void psi_apply_noise_channel(struct PsiDensityMatrix* dm, struct PsiNoiseChannel channel, size_t target) { - assert(channel.num_qubits == 1); - - size_t dim = dm->dim; - size_t target_bit = dm->num_qubits - 1 - target; - - struct PsiComplex* new_data = calloc(dim * dim, sizeof(struct PsiComplex)); - assert(new_data != NULL); - - for (size_t op = 0; op < channel.operator_count; op++) - { - struct PsiMatrix k = channel.operators[op].matrix; - - for (size_t i = 0; i < dim; i++) - for (size_t j = 0; j < dim; j++) - { - size_t i_target = (i >> target_bit) & 1; - size_t j_target = (j >> target_bit) & 1; - - for (size_t ki = 0; ki < 2; ki++) - for (size_t kj = 0; kj < 2; kj++) - { - size_t src_i = (i & ~((size_t)1 << target_bit)) | (ki << target_bit); - size_t src_j = (j & ~((size_t)1 << target_bit)) | (kj << target_bit); - - struct PsiComplex k_elem = k.data[i_target * 2 + ki]; - struct PsiComplex k_dag_elem = - psi_conjugate_complex(k.data[j_target * 2 + kj]); - struct PsiComplex rho_elem = dm->data[src_i * dim + src_j]; - - struct PsiComplex term = - psi_mul_complex(psi_mul_complex(k_elem, rho_elem), k_dag_elem); - new_data[i * dim + j] = psi_add_complex(new_data[i * dim + j], term); - } - } - } - - free(dm->data); - dm->data = new_data; + assert(channel.num_qubits == 1); + + size_t dim = dm->dim; + size_t target_bit = dm->num_qubits - 1 - target; + + struct PsiComplex* new_data = calloc(dim * dim, sizeof(struct PsiComplex)); + assert(new_data != NULL); + + for (size_t op = 0; op < channel.operator_count; op++) + { + struct PsiMatrix k = channel.operators[op].matrix; + + for (size_t i = 0; i < dim; i++) + for (size_t j = 0; j < dim; j++) + { + size_t i_target = (i >> target_bit) & 1; + size_t j_target = (j >> target_bit) & 1; + + for (size_t ki = 0; ki < 2; ki++) + for (size_t kj = 0; kj < 2; kj++) + { + size_t src_i = (i & ~((size_t)1 << target_bit)) | (ki << target_bit); + size_t src_j = (j & ~((size_t)1 << target_bit)) | (kj << target_bit); + + struct PsiComplex k_elem = k.data[i_target * 2 + ki]; + struct PsiComplex k_dag_elem = + psi_conjugate_complex(k.data[j_target * 2 + kj]); + struct PsiComplex rho_elem = dm->data[src_i * dim + src_j]; + + struct PsiComplex term = + psi_mul_complex(psi_mul_complex(k_elem, rho_elem), k_dag_elem); + new_data[i * dim + j] = psi_add_complex(new_data[i * dim + j], term); + } + } + } + + free(dm->data); + dm->data = new_data; } double psi_measure_probability_density_matrix(struct PsiDensityMatrix dm, size_t qubit, size_t outcome) { - size_t target_bit = dm.num_qubits - 1 - qubit; - double prob = 0.0; + size_t target_bit = dm.num_qubits - 1 - qubit; + double prob = 0.0; - for (size_t i = 0; i < dm.dim; i++) - if (((i >> target_bit) & 1) == outcome) - prob += dm.data[i * dm.dim + i].real; + for (size_t i = 0; i < dm.dim; i++) + if (((i >> target_bit) & 1) == outcome) + prob += dm.data[i * dm.dim + i].real; - return prob; + return prob; } double psi_fidelity_density_matrix(struct PsiDensityMatrix dm, const struct PsiComplex* state) { - struct PsiComplex sum = psi_new_complex(0.0, 0.0); + struct PsiComplex sum = psi_new_complex(0.0, 0.0); - for (size_t i = 0; i < dm.dim; i++) - for (size_t j = 0; j < dm.dim; j++) - sum = psi_add_complex(sum, - psi_mul_complex(psi_mul_complex(psi_conjugate_complex(state[i]), - dm.data[i * dm.dim + j]), - state[j])); + for (size_t i = 0; i < dm.dim; i++) + for (size_t j = 0; j < dm.dim; j++) + sum = psi_add_complex(sum, + psi_mul_complex(psi_mul_complex(psi_conjugate_complex(state[i]), + dm.data[i * dm.dim + j]), + state[j])); - return sum.real; + return sum.real; } diff --git a/src/core/quantum_components.c b/src/core/quantum_components.c index a62a911..5870906 100644 --- a/src/core/quantum_components.c +++ b/src/core/quantum_components.c @@ -6,262 +6,262 @@ struct PsiVector psi_new_state_0(void) { - return psi_column_vector(psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0)); + return psi_column_vector(psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0)); } struct PsiVector psi_new_state_1(void) { - return psi_column_vector(psi_new_complex(0.0, 0.0), psi_new_complex(1.0, 0.0)); + return psi_column_vector(psi_new_complex(0.0, 0.0), psi_new_complex(1.0, 0.0)); } struct PsiQuantumGate psi_new_quantum_gate(const char* name, struct PsiMatrix matrix, size_t num_qubits) { - size_t expected_dim = (size_t)1 << num_qubits; - assert(matrix.rows == expected_dim); - assert(matrix.cols == expected_dim); - - return (struct PsiQuantumGate){ - name, - matrix, - num_qubits, - }; + size_t expected_dim = (size_t)1 << num_qubits; + assert(matrix.rows == expected_dim); + assert(matrix.cols == expected_dim); + + return (struct PsiQuantumGate){ + name, + matrix, + num_qubits, + }; } struct PsiQuantumGate psi_new_quantum_gate_from_matrix(const char* name, struct PsiMatrix matrix) { - assert(matrix.rows == matrix.cols); + assert(matrix.rows == matrix.cols); - size_t dim = matrix.rows; - assert(dim > 0 && (dim & (dim - 1)) == 0); + size_t dim = matrix.rows; + assert(dim > 0 && (dim & (dim - 1)) == 0); - size_t num_qubits = 0; - while (((size_t)1 << num_qubits) < dim) - num_qubits++; + size_t num_qubits = 0; + while (((size_t)1 << num_qubits) < dim) + num_qubits++; - return (struct PsiQuantumGate){ - name, - matrix, - num_qubits, - }; + return (struct PsiQuantumGate){ + name, + matrix, + num_qubits, + }; } void psi_free_quantum_gate(struct PsiQuantumGate* gate) { - psi_free_matrix(&gate->matrix); + psi_free_matrix(&gate->matrix); } struct PsiQuantumBit psi_new_quantum_bit(const char* name, struct PsiVector state) { - return (struct PsiQuantumBit){ - name, - state, - }; + return (struct PsiQuantumBit){ + name, + state, + }; } void psi_free_quantum_bit(struct PsiQuantumBit* bit) { - psi_free_vector(&bit->state); + psi_free_vector(&bit->state); } static void update_register(struct PsiQuantumRegister* reg) { - psi_free_vector(®->state_vector); - - if (reg->num_qubits == 0) - { - reg->state_vector = psi_new_vector(0, PSI_COLUMN_VECTOR); - return; - } - - struct PsiMatrix result = psi_matrix_from_vector(reg->qubits[0].state); - for (size_t i = 1; i < reg->num_qubits; i++) - { - struct PsiMatrix part = psi_matrix_from_vector(reg->qubits[i].state); - struct PsiMatrix next = psi_kronecker_matrix(result, part); - psi_free_matrix(&result); - psi_free_matrix(&part); - result = next; - } - - reg->state_vector = psi_vector_from_matrix(result, PSI_COLUMN_VECTOR); - psi_free_matrix(&result); + psi_free_vector(®->state_vector); + + if (reg->num_qubits == 0) + { + reg->state_vector = psi_new_vector(0, PSI_COLUMN_VECTOR); + return; + } + + struct PsiMatrix result = psi_matrix_from_vector(reg->qubits[0].state); + for (size_t i = 1; i < reg->num_qubits; i++) + { + struct PsiMatrix part = psi_matrix_from_vector(reg->qubits[i].state); + struct PsiMatrix next = psi_kronecker_matrix(result, part); + psi_free_matrix(&result); + psi_free_matrix(&part); + result = next; + } + + reg->state_vector = psi_vector_from_matrix(result, PSI_COLUMN_VECTOR); + psi_free_matrix(&result); } struct PsiQuantumRegister psi_new_quantum_register(const char* name, const char** names, size_t count) { - struct PsiQuantumBit* qubits = malloc(count * sizeof(struct PsiQuantumBit)); - assert(qubits != NULL || count == 0); + struct PsiQuantumBit* qubits = malloc(count * sizeof(struct PsiQuantumBit)); + assert(qubits != NULL || count == 0); - for (size_t i = 0; i < count; i++) - qubits[i] = psi_new_quantum_bit(names[i], psi_new_state_0()); + for (size_t i = 0; i < count; i++) + qubits[i] = psi_new_quantum_bit(names[i], psi_new_state_0()); - struct PsiQuantumRegister reg = { - name, - psi_new_vector(0, PSI_COLUMN_VECTOR), - qubits, - count, - }; + struct PsiQuantumRegister reg = { + name, + psi_new_vector(0, PSI_COLUMN_VECTOR), + qubits, + count, + }; - update_register(®); - return reg; + update_register(®); + return reg; } struct PsiQuantumRegister psi_new_quantum_register_from(const char* name, const struct PsiQuantumBit* bits, size_t count) { - struct PsiQuantumBit* qubits = malloc(count * sizeof(struct PsiQuantumBit)); - assert(qubits != NULL || count == 0); + struct PsiQuantumBit* qubits = malloc(count * sizeof(struct PsiQuantumBit)); + assert(qubits != NULL || count == 0); - for (size_t i = 0; i < count; i++) - qubits[i] = psi_new_quantum_bit(bits[i].name, psi_clone_vector(bits[i].state)); + for (size_t i = 0; i < count; i++) + qubits[i] = psi_new_quantum_bit(bits[i].name, psi_clone_vector(bits[i].state)); - struct PsiQuantumRegister reg = { - name, - psi_new_vector(0, PSI_COLUMN_VECTOR), - qubits, - count, - }; + struct PsiQuantumRegister reg = { + name, + psi_new_vector(0, PSI_COLUMN_VECTOR), + qubits, + count, + }; - update_register(®); - return reg; + update_register(®); + return reg; } void psi_free_quantum_register(struct PsiQuantumRegister* reg) { - for (size_t i = 0; i < reg->num_qubits; i++) - psi_free_quantum_bit(®->qubits[i]); + for (size_t i = 0; i < reg->num_qubits; i++) + psi_free_quantum_bit(®->qubits[i]); - free(reg->qubits); - reg->qubits = NULL; - reg->num_qubits = 0; - psi_free_vector(®->state_vector); + free(reg->qubits); + reg->qubits = NULL; + reg->num_qubits = 0; + psi_free_vector(®->state_vector); } static bool targets_contain(const size_t* targets, size_t count, size_t value) { - for (size_t i = 0; i < count; i++) - if (targets[i] == value) - return true; + for (size_t i = 0; i < count; i++) + if (targets[i] == value) + return true; - return false; + return false; } static struct PsiMatrix build_contiguous_operator(struct PsiQuantumRegister reg, struct PsiQuantumGate gate, size_t start_idx) { - size_t n = reg.num_qubits; - size_t g = gate.num_qubits; + size_t n = reg.num_qubits; + size_t g = gate.num_qubits; - bool has_result = false; - struct PsiMatrix result = { 0 }; + bool has_result = false; + struct PsiMatrix result = { 0 }; - for (size_t i = 0; i < n; i++) - { - if (i > start_idx && i < start_idx + g) - continue; + for (size_t i = 0; i < n; i++) + { + if (i > start_idx && i < start_idx + g) + continue; - struct PsiMatrix part = - i == start_idx ? psi_clone_matrix(gate.matrix) : psi_identity_matrix(2); + struct PsiMatrix part = + i == start_idx ? psi_clone_matrix(gate.matrix) : psi_identity_matrix(2); - if (!has_result) - { - result = part; - has_result = true; - continue; - } + if (!has_result) + { + result = part; + has_result = true; + continue; + } - struct PsiMatrix next = psi_kronecker_matrix(result, part); - psi_free_matrix(&result); - psi_free_matrix(&part); - result = next; - } + struct PsiMatrix next = psi_kronecker_matrix(result, part); + psi_free_matrix(&result); + psi_free_matrix(&part); + result = next; + } - if (!has_result) - return psi_identity_matrix((size_t)1 << n); + if (!has_result) + return psi_identity_matrix((size_t)1 << n); - return result; + return result; } static struct PsiMatrix build_full_operator(struct PsiQuantumRegister reg, struct PsiQuantumGate gate, const size_t* targets, size_t target_count) { - size_t n = reg.num_qubits; - size_t g = gate.num_qubits; - size_t dim = (size_t)1 << n; - - bool contiguous = true; - for (size_t i = 1; i < target_count; i++) - if (targets[i] != targets[i - 1] + 1) - { - contiguous = false; - break; - } - - if (contiguous && g == n) - return psi_clone_matrix(gate.matrix); - - if (contiguous) - return build_contiguous_operator(reg, gate, targets[0]); - - struct PsiMatrix result = psi_new_matrix(dim, dim); - - for (size_t col = 0; col < dim; col++) - for (size_t row = 0; row < dim; row++) - { - size_t target_row_bits = 0; - size_t target_col_bits = 0; - - for (size_t i = 0; i < target_count; i++) - { - size_t qubit_pos = n - 1 - targets[i]; - if ((row >> qubit_pos) & 1) - target_row_bits |= (size_t)1 << (g - 1 - i); - if ((col >> qubit_pos) & 1) - target_col_bits |= (size_t)1 << (g - 1 - i); - } - - bool non_target_match = true; - for (size_t q = 0; q < n; q++) - { - if (targets_contain(targets, target_count, q)) - continue; - - size_t qubit_pos = n - 1 - q; - if (((row >> qubit_pos) & 1) != ((col >> qubit_pos) & 1)) - { - non_target_match = false; - break; - } - } - - if (non_target_match) - result.data[row * result.cols + col] = - psi_get_matrix(gate.matrix, target_row_bits, target_col_bits); - } - - return result; + size_t n = reg.num_qubits; + size_t g = gate.num_qubits; + size_t dim = (size_t)1 << n; + + bool contiguous = true; + for (size_t i = 1; i < target_count; i++) + if (targets[i] != targets[i - 1] + 1) + { + contiguous = false; + break; + } + + if (contiguous && g == n) + return psi_clone_matrix(gate.matrix); + + if (contiguous) + return build_contiguous_operator(reg, gate, targets[0]); + + struct PsiMatrix result = psi_new_matrix(dim, dim); + + for (size_t col = 0; col < dim; col++) + for (size_t row = 0; row < dim; row++) + { + size_t target_row_bits = 0; + size_t target_col_bits = 0; + + for (size_t i = 0; i < target_count; i++) + { + size_t qubit_pos = n - 1 - targets[i]; + if ((row >> qubit_pos) & 1) + target_row_bits |= (size_t)1 << (g - 1 - i); + if ((col >> qubit_pos) & 1) + target_col_bits |= (size_t)1 << (g - 1 - i); + } + + bool non_target_match = true; + for (size_t q = 0; q < n; q++) + { + if (targets_contain(targets, target_count, q)) + continue; + + size_t qubit_pos = n - 1 - q; + if (((row >> qubit_pos) & 1) != ((col >> qubit_pos) & 1)) + { + non_target_match = false; + break; + } + } + + if (non_target_match) + result.data[row * result.cols + col] = + psi_get_matrix(gate.matrix, target_row_bits, target_col_bits); + } + + return result; } void psi_apply_gate(struct PsiQuantumRegister* reg, struct PsiQuantumGate gate, const size_t* targets, size_t target_count) { - size_t n = reg->num_qubits; + size_t n = reg->num_qubits; - assert(gate.num_qubits == target_count); - for (size_t i = 0; i < target_count; i++) - assert(targets[i] < n); + assert(gate.num_qubits == target_count); + for (size_t i = 0; i < target_count; i++) + assert(targets[i] < n); - for (size_t i = 0; i < target_count; i++) - for (size_t j = i + 1; j < target_count; j++) - assert(targets[i] != targets[j]); + for (size_t i = 0; i < target_count; i++) + for (size_t j = i + 1; j < target_count; j++) + assert(targets[i] != targets[j]); - struct PsiMatrix full_operator = build_full_operator(*reg, gate, targets, target_count); - struct PsiVector new_state = psi_mul_vector_matrix(reg->state_vector, full_operator); + struct PsiMatrix full_operator = build_full_operator(*reg, gate, targets, target_count); + struct PsiVector new_state = psi_mul_vector_matrix(reg->state_vector, full_operator); - psi_free_vector(®->state_vector); - psi_free_matrix(&full_operator); - reg->state_vector = new_state; + psi_free_vector(®->state_vector); + psi_free_matrix(&full_operator); + reg->state_vector = new_state; } diff --git a/src/core/runtime.c b/src/core/runtime.c index 24cbe26..9c95214 100644 --- a/src/core/runtime.c +++ b/src/core/runtime.c @@ -8,304 +8,304 @@ struct PsiRuntimeConfig psi_new_runtime_config(void) { - struct PsiRuntimeConfig config; - config.parallel = false; - config.simd = false; - config.batched = false; - config.structure_aware = false; - config.parallel_threshold = PSI_PARALLEL_THRESHOLD; - - return config; + struct PsiRuntimeConfig config; + config.parallel = false; + config.simd = false; + config.batched = false; + config.structure_aware = false; + config.parallel_threshold = PSI_PARALLEL_THRESHOLD; + + return config; } struct PsiRuntimeConfig psi_optimal_runtime_config(void) { - struct PsiRuntimeConfig config = psi_new_runtime_config(); - config.structure_aware = true; - config.simd = true; - config.parallel = true; + struct PsiRuntimeConfig config = psi_new_runtime_config(); + config.structure_aware = true; + config.simd = true; + config.parallel = true; - return config; + return config; } struct PsiRuntimeConfig psi_runtime_to_config(enum PsiRuntime runtime) { - struct PsiRuntimeConfig config = psi_new_runtime_config(); - - switch (runtime) - { - case PSI_RUNTIME_BASIC: break; - case PSI_RUNTIME_BASIC_MT: config.parallel = true; break; - case PSI_RUNTIME_BATCHED: config.batched = true; break; - case PSI_RUNTIME_BATCHED_MT: - config.batched = true; - config.parallel = true; - break; - case PSI_RUNTIME_SIMD: - config.batched = true; - config.simd = true; - break; - case PSI_RUNTIME_SIMD_MT: - config.batched = true; - config.simd = true; - config.parallel = true; - break; - case PSI_RUNTIME_STRUCTURE_AWARE: - config.structure_aware = true; - config.simd = true; - break; - case PSI_RUNTIME_STRUCTURE_AWARE_MT: - config.structure_aware = true; - config.simd = true; - config.parallel = true; - break; - } - - return config; + struct PsiRuntimeConfig config = psi_new_runtime_config(); + + switch (runtime) + { + case PSI_RUNTIME_BASIC: break; + case PSI_RUNTIME_BASIC_MT: config.parallel = true; break; + case PSI_RUNTIME_BATCHED: config.batched = true; break; + case PSI_RUNTIME_BATCHED_MT: + config.batched = true; + config.parallel = true; + break; + case PSI_RUNTIME_SIMD: + config.batched = true; + config.simd = true; + break; + case PSI_RUNTIME_SIMD_MT: + config.batched = true; + config.simd = true; + config.parallel = true; + break; + case PSI_RUNTIME_STRUCTURE_AWARE: + config.structure_aware = true; + config.simd = true; + break; + case PSI_RUNTIME_STRUCTURE_AWARE_MT: + config.structure_aware = true; + config.simd = true; + config.parallel = true; + break; + } + + return config; } static bool op_to_kernel(struct PsiGateOp op, struct PsiKernel* out) { - struct PsiMatrix matrix; - const char* name; - - switch (op.kind) - { - case PSI_GATE_H: - matrix = psi_hadamard_gate().matrix; - name = "H"; - break; - case PSI_GATE_X: - matrix = psi_pauli_x_gate().matrix; - name = "X"; - break; - case PSI_GATE_Y: - matrix = psi_pauli_y_gate().matrix; - name = "Y"; - break; - case PSI_GATE_Z: - matrix = psi_pauli_z_gate().matrix; - name = "Z"; - break; - case PSI_GATE_S: - matrix = psi_s_gate().matrix; - name = "S"; - break; - case PSI_GATE_T: - matrix = psi_t_gate().matrix; - name = "T"; - break; - case PSI_GATE_SDG: - matrix = psi_sdg_gate().matrix; - name = "Sdg"; - break; - case PSI_GATE_TDG: - matrix = psi_tdg_gate().matrix; - name = "Tdg"; - break; - case PSI_GATE_SX: - matrix = psi_sx_gate().matrix; - name = "Sx"; - break; - case PSI_GATE_SXDG: - matrix = psi_sxdg_gate().matrix; - name = "Sxdg"; - break; - case PSI_GATE_RX: - matrix = psi_rx_matrix(op.params[0]); - name = "Rx"; - break; - case PSI_GATE_RY: - matrix = psi_ry_matrix(op.params[0]); - name = "Ry"; - break; - case PSI_GATE_RZ: - matrix = psi_rz_matrix(op.params[0]); - name = "Rz"; - break; - case PSI_GATE_P: - matrix = psi_p_matrix(op.params[0]); - name = "P"; - break; - case PSI_GATE_U1: - matrix = psi_u1_matrix(op.params[0]); - name = "U1"; - break; - case PSI_GATE_U2: - matrix = psi_u2_matrix(op.params[0], op.params[1]); - name = "U2"; - break; - case PSI_GATE_U3: - matrix = psi_u3_matrix(op.params[0], op.params[1], op.params[2]); - name = "U3"; - break; - case PSI_GATE_CNOT: - matrix = psi_cnot_gate().matrix; - name = "CNOT"; - break; - case PSI_GATE_CZ: - matrix = psi_cz_gate().matrix; - name = "CZ"; - break; - case PSI_GATE_SWAP: - matrix = psi_swap_gate().matrix; - name = "SWAP"; - break; - case PSI_GATE_CRX: - matrix = psi_crx_matrix(op.params[0]); - name = "CRx"; - break; - case PSI_GATE_CRY: - matrix = psi_cry_matrix(op.params[0]); - name = "CRy"; - break; - case PSI_GATE_CRZ: - matrix = psi_crz_matrix(op.params[0]); - name = "CRz"; - break; - case PSI_GATE_CP: - matrix = psi_cp_matrix(op.params[0]); - name = "CP"; - break; - case PSI_GATE_CCNOT: - matrix = psi_toffoli_gate().matrix; - name = "CCNOT"; - break; - case PSI_GATE_CSWAP: - matrix = psi_fredkin_gate().matrix; - name = "CSWAP"; - break; - case PSI_GATE_MEASURE: return false; - case PSI_GATE_CUSTOM: - matrix = psi_to_quantum_gate(*op.custom).matrix; - name = "Custom"; - break; - } - - size_t target_count; - const size_t* targets = psi_gate_op_quantum_targets(&op, &target_count); - *out = psi_new_kernel(name, matrix, targets, target_count); - - return true; + struct PsiMatrix matrix; + const char* name; + + switch (op.kind) + { + case PSI_GATE_H: + matrix = psi_hadamard_gate().matrix; + name = "H"; + break; + case PSI_GATE_X: + matrix = psi_pauli_x_gate().matrix; + name = "X"; + break; + case PSI_GATE_Y: + matrix = psi_pauli_y_gate().matrix; + name = "Y"; + break; + case PSI_GATE_Z: + matrix = psi_pauli_z_gate().matrix; + name = "Z"; + break; + case PSI_GATE_S: + matrix = psi_s_gate().matrix; + name = "S"; + break; + case PSI_GATE_T: + matrix = psi_t_gate().matrix; + name = "T"; + break; + case PSI_GATE_SDG: + matrix = psi_sdg_gate().matrix; + name = "Sdg"; + break; + case PSI_GATE_TDG: + matrix = psi_tdg_gate().matrix; + name = "Tdg"; + break; + case PSI_GATE_SX: + matrix = psi_sx_gate().matrix; + name = "Sx"; + break; + case PSI_GATE_SXDG: + matrix = psi_sxdg_gate().matrix; + name = "Sxdg"; + break; + case PSI_GATE_RX: + matrix = psi_rx_matrix(op.params[0]); + name = "Rx"; + break; + case PSI_GATE_RY: + matrix = psi_ry_matrix(op.params[0]); + name = "Ry"; + break; + case PSI_GATE_RZ: + matrix = psi_rz_matrix(op.params[0]); + name = "Rz"; + break; + case PSI_GATE_P: + matrix = psi_p_matrix(op.params[0]); + name = "P"; + break; + case PSI_GATE_U1: + matrix = psi_u1_matrix(op.params[0]); + name = "U1"; + break; + case PSI_GATE_U2: + matrix = psi_u2_matrix(op.params[0], op.params[1]); + name = "U2"; + break; + case PSI_GATE_U3: + matrix = psi_u3_matrix(op.params[0], op.params[1], op.params[2]); + name = "U3"; + break; + case PSI_GATE_CNOT: + matrix = psi_cnot_gate().matrix; + name = "CNOT"; + break; + case PSI_GATE_CZ: + matrix = psi_cz_gate().matrix; + name = "CZ"; + break; + case PSI_GATE_SWAP: + matrix = psi_swap_gate().matrix; + name = "SWAP"; + break; + case PSI_GATE_CRX: + matrix = psi_crx_matrix(op.params[0]); + name = "CRx"; + break; + case PSI_GATE_CRY: + matrix = psi_cry_matrix(op.params[0]); + name = "CRy"; + break; + case PSI_GATE_CRZ: + matrix = psi_crz_matrix(op.params[0]); + name = "CRz"; + break; + case PSI_GATE_CP: + matrix = psi_cp_matrix(op.params[0]); + name = "CP"; + break; + case PSI_GATE_CCNOT: + matrix = psi_toffoli_gate().matrix; + name = "CCNOT"; + break; + case PSI_GATE_CSWAP: + matrix = psi_fredkin_gate().matrix; + name = "CSWAP"; + break; + case PSI_GATE_MEASURE: return false; + case PSI_GATE_CUSTOM: + matrix = psi_to_quantum_gate(*op.custom).matrix; + name = "Custom"; + break; + } + + size_t target_count; + const size_t* targets = psi_gate_op_quantum_targets(&op, &target_count); + *out = psi_new_kernel(name, matrix, targets, target_count); + + return true; } static struct PsiVector new_zero_state(size_t num_qubits) { - size_t dim = (size_t)1 << num_qubits; - struct PsiVector state = psi_new_vector(dim, PSI_COLUMN_VECTOR); - state.data[0] = psi_new_complex(1.0, 0.0); + size_t dim = (size_t)1 << num_qubits; + struct PsiVector state = psi_new_vector(dim, PSI_COLUMN_VECTOR); + state.data[0] = psi_new_complex(1.0, 0.0); - return state; + return state; } static void execute_kernels(struct PsiVector* state, const struct PsiKernel* kernels, size_t count, size_t num_qubits, struct PsiRuntimeConfig config) { - bool use_parallel = config.parallel && num_qubits >= config.parallel_threshold; - - for (size_t i = 0; i < count; i++) - { - struct PsiKernel kernel = kernels[i]; - - if (config.simd && kernel.target_count == 1) - { - struct PsiComplex gate[2][2] = { - { kernel.matrix.data[0], kernel.matrix.data[1] }, - { kernel.matrix.data[2], kernel.matrix.data[3] }, - }; - - if (use_parallel) - psi_apply_single_qubit_gate_simd_parallel(state->data, gate, kernel.targets[0], - num_qubits); - else - psi_apply_single_qubit_gate_simd(state->data, gate, kernel.targets[0], num_qubits); - } - else - psi_apply_kernel(state, kernel, num_qubits); - } + bool use_parallel = config.parallel && num_qubits >= config.parallel_threshold; + + for (size_t i = 0; i < count; i++) + { + struct PsiKernel kernel = kernels[i]; + + if (config.simd && kernel.target_count == 1) + { + struct PsiComplex gate[2][2] = { + { kernel.matrix.data[0], kernel.matrix.data[1] }, + { kernel.matrix.data[2], kernel.matrix.data[3] }, + }; + + if (use_parallel) + psi_apply_single_qubit_gate_simd_parallel(state->data, gate, kernel.targets[0], + num_qubits); + else + psi_apply_single_qubit_gate_simd(state->data, gate, kernel.targets[0], num_qubits); + } + else + psi_apply_kernel(state, kernel, num_qubits); + } } struct PsiVector psi_compute_runtime_config(struct PsiRuntimeConfig config, size_t num_qubits, const struct PsiGateOp* operations, size_t op_count) { - struct PsiVector state = new_zero_state(num_qubits); - - if (config.structure_aware) - { - struct PsiStructureAwareBatch batch = psi_new_structure_aware_batch(num_qubits); - for (size_t i = 0; i < op_count; i++) - { - struct PsiKernel kernel; - if (op_to_kernel(operations[i], &kernel)) - psi_add_structure_aware_kernel(&batch, kernel); - } - - psi_optimize_structure_aware_batch(&batch); - execute_kernels(&state, batch.kernels, batch.count, num_qubits, config); - psi_free_structure_aware_batch(&batch); - - return state; - } - - struct PsiKernelBatch batch = psi_new_kernel_batch(num_qubits); - for (size_t i = 0; i < op_count; i++) - { - struct PsiKernel kernel; - if (op_to_kernel(operations[i], &kernel)) - psi_add_kernel(&batch, kernel); - } - - if (config.batched) - psi_optimize_kernel_batch(&batch); - - execute_kernels(&state, batch.kernels, batch.count, num_qubits, config); - psi_free_kernel_batch(&batch); - - return state; + struct PsiVector state = new_zero_state(num_qubits); + + if (config.structure_aware) + { + struct PsiStructureAwareBatch batch = psi_new_structure_aware_batch(num_qubits); + for (size_t i = 0; i < op_count; i++) + { + struct PsiKernel kernel; + if (op_to_kernel(operations[i], &kernel)) + psi_add_structure_aware_kernel(&batch, kernel); + } + + psi_optimize_structure_aware_batch(&batch); + execute_kernels(&state, batch.kernels, batch.count, num_qubits, config); + psi_free_structure_aware_batch(&batch); + + return state; + } + + struct PsiKernelBatch batch = psi_new_kernel_batch(num_qubits); + for (size_t i = 0; i < op_count; i++) + { + struct PsiKernel kernel; + if (op_to_kernel(operations[i], &kernel)) + psi_add_kernel(&batch, kernel); + } + + if (config.batched) + psi_optimize_kernel_batch(&batch); + + execute_kernels(&state, batch.kernels, batch.count, num_qubits, config); + psi_free_kernel_batch(&batch); + + return state; } struct PsiVector psi_compute_runtime(enum PsiRuntime runtime, size_t num_qubits, const struct PsiGateOp* operations, size_t op_count) { - return psi_compute_runtime_config(psi_runtime_to_config(runtime), num_qubits, operations, - op_count); + return psi_compute_runtime_config(psi_runtime_to_config(runtime), num_qubits, operations, + op_count); } const struct PsiVector* psi_compute_circuit_with_config(struct PsiQuantumCircuit* circuit, struct PsiRuntimeConfig config) { - if (!circuit->is_computed) - { - psi_free_vector(&circuit->computed_state); - circuit->computed_state = psi_compute_runtime_config( - config, circuit->num_qubits, circuit->operations, circuit->operation_count); - circuit->is_computed = true; - } - - return &circuit->computed_state; + if (!circuit->is_computed) + { + psi_free_vector(&circuit->computed_state); + circuit->computed_state = psi_compute_runtime_config( + config, circuit->num_qubits, circuit->operations, circuit->operation_count); + circuit->is_computed = true; + } + + return &circuit->computed_state; } const struct PsiVector* psi_compute_circuit_with(struct PsiQuantumCircuit* circuit, enum PsiRuntime runtime) { - return psi_compute_circuit_with_config(circuit, psi_runtime_to_config(runtime)); + return psi_compute_circuit_with_config(circuit, psi_runtime_to_config(runtime)); } const struct PsiVector* psi_compute_circuit(struct PsiQuantumCircuit* circuit) { - return psi_compute_circuit_with(circuit, PSI_RUNTIME_BASIC); + return psi_compute_circuit_with(circuit, PSI_RUNTIME_BASIC); } double psi_circuit_probability(struct PsiQuantumCircuit* circuit, size_t state_index) { - const struct PsiVector* state = psi_compute_circuit(circuit); - return psi_norm2_complex(state->data[state_index]); + const struct PsiVector* state = psi_compute_circuit(circuit); + return psi_norm2_complex(state->data[state_index]); } void psi_circuit_probabilities(struct PsiQuantumCircuit* circuit, double* out) { - const struct PsiVector* state = psi_compute_circuit(circuit); - size_t dim = (size_t)1 << circuit->num_qubits; - for (size_t i = 0; i < dim; i++) - out[i] = psi_norm2_complex(state->data[i]); + const struct PsiVector* state = psi_compute_circuit(circuit); + size_t dim = (size_t)1 << circuit->num_qubits; + for (size_t i = 0; i < dim; i++) + out[i] = psi_norm2_complex(state->data[i]); } diff --git a/src/maths/complex.c b/src/maths/complex.c index 44e255f..419d0b0 100644 --- a/src/maths/complex.c +++ b/src/maths/complex.c @@ -4,126 +4,126 @@ struct PsiComplex psi_new_complex(double real, double imaginary) { - return (struct PsiComplex){ - real, - imaginary, - }; + return (struct PsiComplex){ + real, + imaginary, + }; } struct PsiComplex psi_new_complex_from_real(double real) { - return (struct PsiComplex){ - real, - 0.0, - }; + return (struct PsiComplex){ + real, + 0.0, + }; } struct PsiComplex psi_conjugate_complex(struct PsiComplex z) { - return (struct PsiComplex){ - z.real, - -z.imaginary, - }; + return (struct PsiComplex){ + z.real, + -z.imaginary, + }; } struct PsiComplex psi_neg_complex(struct PsiComplex z) { - return (struct PsiComplex){ - -z.real, - -z.imaginary, - }; + return (struct PsiComplex){ + -z.real, + -z.imaginary, + }; } double psi_phase_complex(struct PsiComplex z) { - return atan2(z.imaginary, z.real); + return atan2(z.imaginary, z.real); } double psi_norm2_complex(struct PsiComplex z) { - return z.real * z.real + z.imaginary * z.imaginary; + return z.real * z.real + z.imaginary * z.imaginary; } double psi_abs_complex(struct PsiComplex z) { - return sqrt(psi_norm2_complex(z)); + return sqrt(psi_norm2_complex(z)); } struct PsiComplex psi_sqrt_complex(struct PsiComplex z) { - double r = psi_abs_complex(z); - double half_theta = psi_phase_complex(z) / 2.0; - double sqrt_r = sqrt(r); + double r = psi_abs_complex(z); + double half_theta = psi_phase_complex(z) / 2.0; + double sqrt_r = sqrt(r); - return (struct PsiComplex){ - sqrt_r * cos(half_theta), - sqrt_r * sin(half_theta), - }; + return (struct PsiComplex){ + sqrt_r * cos(half_theta), + sqrt_r * sin(half_theta), + }; } struct PsiComplex psi_add_complex(struct PsiComplex a, struct PsiComplex b) { - return (struct PsiComplex){ - a.real + b.real, - a.imaginary + b.imaginary, - }; + return (struct PsiComplex){ + a.real + b.real, + a.imaginary + b.imaginary, + }; } struct PsiComplex psi_sub_complex(struct PsiComplex a, struct PsiComplex b) { - return (struct PsiComplex){ - a.real - b.real, - a.imaginary - b.imaginary, - }; + return (struct PsiComplex){ + a.real - b.real, + a.imaginary - b.imaginary, + }; } struct PsiComplex psi_mul_complex(struct PsiComplex a, struct PsiComplex b) { - // (a + bi)(c + di) = (ac - bd) + (ad + bc)i - return (struct PsiComplex){ - a.real * b.real - a.imaginary * b.imaginary, - a.real * b.imaginary + a.imaginary * b.real, - }; + // (a + bi)(c + di) = (ac - bd) + (ad + bc)i + return (struct PsiComplex){ + a.real * b.real - a.imaginary * b.imaginary, + a.real * b.imaginary + a.imaginary * b.real, + }; } struct PsiComplex psi_div_complex(struct PsiComplex a, struct PsiComplex b) { - // (a + bi) / (c + di) = ((ac + bd) + (bc - ad)i) / (c² + d²) - double denom = b.real * b.real + b.imaginary * b.imaginary; - return (struct PsiComplex){ - (a.real * b.real + a.imaginary * b.imaginary) / denom, - (a.imaginary * b.real - a.real * b.imaginary) / denom, - }; + // (a + bi) / (c + di) = ((ac + bd) + (bc - ad)i) / (c² + d²) + double denom = b.real * b.real + b.imaginary * b.imaginary; + return (struct PsiComplex){ + (a.real * b.real + a.imaginary * b.imaginary) / denom, + (a.imaginary * b.real - a.real * b.imaginary) / denom, + }; } struct PsiComplex psi_add_complex_real(struct PsiComplex a, double b) { - return (struct PsiComplex){ - a.real + b, - a.imaginary, - }; + return (struct PsiComplex){ + a.real + b, + a.imaginary, + }; } struct PsiComplex psi_sub_complex_real(struct PsiComplex a, double b) { - return (struct PsiComplex){ - a.real - b, - a.imaginary, - }; + return (struct PsiComplex){ + a.real - b, + a.imaginary, + }; } struct PsiComplex psi_mul_complex_real(struct PsiComplex a, double b) { - return (struct PsiComplex){ - a.real * b, - a.imaginary * b, - }; + return (struct PsiComplex){ + a.real * b, + a.imaginary * b, + }; } struct PsiComplex psi_div_complex_real(struct PsiComplex a, double b) { - return (struct PsiComplex){ - a.real / b, - a.imaginary / b, - }; + return (struct PsiComplex){ + a.real / b, + a.imaginary / b, + }; } diff --git a/src/maths/format.c b/src/maths/format.c index f3db83d..52b2b5a 100644 --- a/src/maths/format.c +++ b/src/maths/format.c @@ -12,136 +12,189 @@ static const double INV_SQRT_32 = 0.1767766952966369; static bool approx_eq(double a, double b) { - return fabs(a - b) < EPSILON; + return fabs(a - b) < EPSILON; } static bool real_symbolic(double v, char* out, size_t cap) { - double abs_v = fabs(v); - const char* sign = v < 0.0 ? "-" : ""; - - if (approx_eq(abs_v, 0.0)) - { - snprintf(out, cap, "%s", "0"); - return true; - } - - const char* sym = NULL; - if (approx_eq(abs_v, 1.0)) - sym = "1"; - else if (approx_eq(abs_v, 0.5)) - sym = "½"; - else if (approx_eq(abs_v, 0.25)) - sym = "¼"; - else if (approx_eq(abs_v, 0.75)) - sym = "¾"; - else if (approx_eq(abs_v, 0.125)) - sym = "⅛"; - else if (approx_eq(abs_v, SQRT_2)) - sym = "√2"; - else if (approx_eq(abs_v, INV_SQRT_2)) - sym = "¹⁄√2"; - else if (approx_eq(abs_v, INV_SQRT_8)) - sym = "¹⁄√8"; - else if (approx_eq(abs_v, INV_SQRT_32)) - sym = "¹⁄√32"; - else if (approx_eq(abs_v, 2.0)) - sym = "2"; - else if (approx_eq(abs_v, 1.0 / 3.0)) - sym = "⅓"; - else if (approx_eq(abs_v, 2.0 / 3.0)) - sym = "⅔"; - else - return false; - - snprintf(out, cap, "%s%s", sign, sym); - return true; + double abs_v = fabs(v); + const char* sign = v < 0.0 ? "-" : ""; + + if (approx_eq(abs_v, 0.0)) + { + snprintf(out, cap, "%s", "0"); + return true; + } + + const char* sym = NULL; + if (approx_eq(abs_v, 1.0)) + sym = "1"; + else if (approx_eq(abs_v, 0.5)) + sym = "½"; + else if (approx_eq(abs_v, 0.25)) + sym = "¼"; + else if (approx_eq(abs_v, 0.75)) + sym = "¾"; + else if (approx_eq(abs_v, 0.125)) + sym = "⅛"; + else if (approx_eq(abs_v, SQRT_2)) + sym = "√2"; + else if (approx_eq(abs_v, INV_SQRT_2)) + sym = "¹⁄√2"; + else if (approx_eq(abs_v, INV_SQRT_8)) + sym = "¹⁄√8"; + else if (approx_eq(abs_v, INV_SQRT_32)) + sym = "¹⁄√32"; + else if (approx_eq(abs_v, 2.0)) + sym = "2"; + else if (approx_eq(abs_v, 1.0 / 3.0)) + sym = "⅓"; + else if (approx_eq(abs_v, 2.0 / 3.0)) + sym = "⅔"; + else + return false; + + snprintf(out, cap, "%s%s", sign, sym); + return true; } char* psi_format_amplitude(struct PsiComplex c, char* out, size_t cap) { - double re = c.real; - double im = c.imaginary; - - bool re_zero = approx_eq(fabs(re), 0.0); - bool im_zero = approx_eq(fabs(im), 0.0); - - if (re_zero && im_zero) - { - snprintf(out, cap, "%s", "0"); - return out; - } - - if (im_zero) - { - if (!real_symbolic(re, out, cap)) - snprintf(out, cap, "%.4f", re); - - return out; - } - - if (re_zero) - { - if (approx_eq(fabs(im), 1.0)) - { - snprintf(out, cap, "%s", im > 0.0 ? "i" : "-i"); - return out; - } - - char sym[32]; - if (real_symbolic(im, sym, sizeof sym)) - snprintf(out, cap, "%si", sym); - else - snprintf(out, cap, "%.4fi", im); - - return out; - } - - char re_str[32]; - if (!real_symbolic(re, re_str, sizeof re_str)) - snprintf(re_str, sizeof re_str, "%.4f", re); - - char im_str[32]; - if (approx_eq(fabs(im), 1.0)) - { - snprintf(im_str, sizeof im_str, "%s", im > 0.0 ? "+i" : "-i"); - } - else - { - const char* sign = im > 0.0 ? "+" : "-"; - char sym[32]; - if (real_symbolic(fabs(im), sym, sizeof sym)) - snprintf(im_str, sizeof im_str, "%s%si", sign, sym); - else - snprintf(im_str, sizeof im_str, "%s%.4fi", sign, fabs(im)); - } - - snprintf(out, cap, "%s%s", re_str, im_str); - return out; + double re = c.real; + double im = c.imaginary; + + bool re_zero = approx_eq(fabs(re), 0.0); + bool im_zero = approx_eq(fabs(im), 0.0); + + if (re_zero && im_zero) + { + snprintf(out, cap, "%s", "0"); + return out; + } + + if (im_zero) + { + if (!real_symbolic(re, out, cap)) + snprintf(out, cap, "%.4f", re); + + return out; + } + + if (re_zero) + { + if (approx_eq(fabs(im), 1.0)) + { + snprintf(out, cap, "%s", im > 0.0 ? "i" : "-i"); + return out; + } + + char sym[32]; + if (real_symbolic(im, sym, sizeof sym)) + snprintf(out, cap, "%si", sym); + else + snprintf(out, cap, "%.4fi", im); + + return out; + } + + char re_str[32]; + if (!real_symbolic(re, re_str, sizeof re_str)) + snprintf(re_str, sizeof re_str, "%.4f", re); + + char im_str[32]; + if (approx_eq(fabs(im), 1.0)) + { + snprintf(im_str, sizeof im_str, "%s", im > 0.0 ? "+i" : "-i"); + } + else + { + const char* sign = im > 0.0 ? "+" : "-"; + char sym[32]; + if (real_symbolic(fabs(im), sym, sizeof sym)) + snprintf(im_str, sizeof im_str, "%s%si", sign, sym); + else + snprintf(im_str, sizeof im_str, "%s%.4fi", sign, fabs(im)); + } + + snprintf(out, cap, "%s%s", re_str, im_str); + return out; } char* psi_format_probability(double p, char* out, size_t cap) { - if (approx_eq(p, 0.0)) - snprintf(out, cap, "%s", "0"); - else if (approx_eq(p, 1.0)) - snprintf(out, cap, "%s", "1"); - else if (approx_eq(p, 0.5)) - snprintf(out, cap, "%s", "½"); - else if (approx_eq(p, 0.25)) - snprintf(out, cap, "%s", "¼"); - else if (approx_eq(p, 0.75)) - snprintf(out, cap, "%s", "¾"); - else if (approx_eq(p, 0.125)) - snprintf(out, cap, "%s", "⅛"); - else if (approx_eq(p, 0.0625)) - snprintf(out, cap, "%s", "¹⁄₁₆"); - else if (approx_eq(p, 1.0 / 3.0)) - snprintf(out, cap, "%s", "⅓"); - else if (approx_eq(p, 2.0 / 3.0)) - snprintf(out, cap, "%s", "⅔"); - else - snprintf(out, cap, "%.4f", p); - - return out; + if (approx_eq(p, 0.0)) + snprintf(out, cap, "%s", "0"); + else if (approx_eq(p, 1.0)) + snprintf(out, cap, "%s", "1"); + else if (approx_eq(p, 0.5)) + snprintf(out, cap, "%s", "½"); + else if (approx_eq(p, 0.25)) + snprintf(out, cap, "%s", "¼"); + else if (approx_eq(p, 0.75)) + snprintf(out, cap, "%s", "¾"); + else if (approx_eq(p, 0.125)) + snprintf(out, cap, "%s", "⅛"); + else if (approx_eq(p, 0.0625)) + snprintf(out, cap, "%s", "¹⁄₁₆"); + else if (approx_eq(p, 1.0 / 3.0)) + snprintf(out, cap, "%s", "⅓"); + else if (approx_eq(p, 2.0 / 3.0)) + snprintf(out, cap, "%s", "⅔"); + else + snprintf(out, cap, "%.4f", p); + + return out; +} + +void psi_print_matrix(struct PsiMatrix m, FILE* out) +{ + int re_width = 0; + int im_width = 0; + for (size_t k = 0; k < m.rows * m.cols; k++) + { + int rl = snprintf(NULL, 0, "%.2f", m.data[k].real); + int il = snprintf(NULL, 0, "%.2f", fabs(m.data[k].imaginary)); + if (rl > re_width) + re_width = rl; + if (il > im_width) + im_width = il; + } + + for (size_t i = 0; i < m.rows; i++) + { + fputs(i == 0 ? "┌" : (i == m.rows - 1 ? "└" : "│"), out); + + for (size_t j = 0; j < m.cols; j++) + { + struct PsiComplex e = m.data[i * m.cols + j]; + const char* sign = e.imaginary > 0.0 ? "+" : "-"; + fprintf(out, "%*.2f %s %*.2fi", re_width, e.real, sign, im_width, fabs(e.imaginary)); + if (j != m.cols - 1) + fputs(", ", out); + } + + fputs(i == 0 ? "┐" : (i == m.rows - 1 ? "┘" : "│"), out); + if (i != m.rows - 1) + fputc('\n', out); + } +} + +void psi_print_vector(struct PsiVector v, FILE* out) +{ + if (v.kind == PSI_COLUMN_VECTOR) + { + struct PsiMatrix m = psi_matrix_from_vector(v); + psi_print_matrix(m, out); + psi_free_matrix(&m); + return; + } + + fputc('[', out); + for (size_t i = 0; i < v.size; i++) + { + fprintf(out, "%g + %gi", v.data[i].real, v.data[i].imaginary); + if (i != v.size - 1) + fputs(", ", out); + } + fputc(']', out); } diff --git a/src/maths/matrix.c b/src/maths/matrix.c index 2415395..8b41065 100644 --- a/src/maths/matrix.c +++ b/src/maths/matrix.c @@ -6,181 +6,181 @@ struct PsiMatrix psi_new_matrix(size_t rows, size_t cols) { - struct PsiComplex* data = calloc(rows * cols, sizeof(struct PsiComplex)); - assert(data != NULL || rows * cols == 0); + struct PsiComplex* data = calloc(rows * cols, sizeof(struct PsiComplex)); + assert(data != NULL || rows * cols == 0); - return (struct PsiMatrix){ - data, - rows, - cols, - }; + return (struct PsiMatrix){ + data, + rows, + cols, + }; } struct PsiMatrix psi_new_matrix_from(const struct PsiComplex* data, size_t rows, size_t cols) { - struct PsiMatrix m = psi_new_matrix(rows, cols); - memcpy(m.data, data, rows * cols * sizeof(struct PsiComplex)); + struct PsiMatrix m = psi_new_matrix(rows, cols); + memcpy(m.data, data, rows * cols * sizeof(struct PsiComplex)); - return m; + return m; } struct PsiMatrix psi_clone_matrix(struct PsiMatrix m) { - return psi_new_matrix_from(m.data, m.rows, m.cols); + return psi_new_matrix_from(m.data, m.rows, m.cols); } struct PsiMatrix psi_identity_matrix(size_t size) { - struct PsiMatrix m = psi_new_matrix(size, size); - for (size_t i = 0; i < size; i++) - m.data[i * size + i] = psi_new_complex(1.0, 0.0); + struct PsiMatrix m = psi_new_matrix(size, size); + for (size_t i = 0; i < size; i++) + m.data[i * size + i] = psi_new_complex(1.0, 0.0); - return m; + return m; } void psi_free_matrix(struct PsiMatrix* m) { - free(m->data); - m->data = NULL; - m->rows = 0; - m->cols = 0; + free(m->data); + m->data = NULL; + m->rows = 0; + m->cols = 0; } struct PsiComplex psi_get_matrix(struct PsiMatrix m, size_t row, size_t col) { - assert(row < m.rows && col < m.cols); - return m.data[row * m.cols + col]; + assert(row < m.rows && col < m.cols); + return m.data[row * m.cols + col]; } void psi_set_matrix(struct PsiMatrix* m, size_t row, size_t col, struct PsiComplex value) { - assert(row < m->rows && col < m->cols); - m->data[row * m->cols + col] = value; + assert(row < m->rows && col < m->cols); + m->data[row * m->cols + col] = value; } struct PsiMatrix psi_dot_matrix(struct PsiMatrix a, struct PsiMatrix b) { - assert(a.cols == b.rows); + assert(a.cols == b.rows); - struct PsiMatrix result = psi_new_matrix(a.rows, b.cols); - for (size_t i = 0; i < a.rows; i++) - for (size_t j = 0; j < b.cols; j++) - { - struct PsiComplex sum = psi_new_complex(0.0, 0.0); - for (size_t k = 0; k < a.cols; k++) - sum = psi_add_complex( - sum, psi_mul_complex(a.data[i * a.cols + k], b.data[k * b.cols + j])); + struct PsiMatrix result = psi_new_matrix(a.rows, b.cols); + for (size_t i = 0; i < a.rows; i++) + for (size_t j = 0; j < b.cols; j++) + { + struct PsiComplex sum = psi_new_complex(0.0, 0.0); + for (size_t k = 0; k < a.cols; k++) + sum = psi_add_complex( + sum, psi_mul_complex(a.data[i * a.cols + k], b.data[k * b.cols + j])); - result.data[i * result.cols + j] = sum; - } + result.data[i * result.cols + j] = sum; + } - return result; + return result; } struct PsiMatrix psi_kronecker_matrix(struct PsiMatrix a, struct PsiMatrix b) { - struct PsiMatrix result = psi_new_matrix(a.rows * b.rows, a.cols * b.cols); - for (size_t i = 0; i < a.rows; i++) - for (size_t j = 0; j < a.cols; j++) - { - struct PsiComplex a_val = a.data[i * a.cols + j]; - for (size_t k = 0; k < b.rows; k++) - for (size_t l = 0; l < b.cols; l++) - { - size_t row = i * b.rows + k; - size_t col = j * b.cols + l; - result.data[row * result.cols + col] = - psi_mul_complex(a_val, b.data[k * b.cols + l]); - } - } + struct PsiMatrix result = psi_new_matrix(a.rows * b.rows, a.cols * b.cols); + for (size_t i = 0; i < a.rows; i++) + for (size_t j = 0; j < a.cols; j++) + { + struct PsiComplex a_val = a.data[i * a.cols + j]; + for (size_t k = 0; k < b.rows; k++) + for (size_t l = 0; l < b.cols; l++) + { + size_t row = i * b.rows + k; + size_t col = j * b.cols + l; + result.data[row * result.cols + col] = + psi_mul_complex(a_val, b.data[k * b.cols + l]); + } + } - return result; + return result; } struct PsiMatrix psi_transpose_matrix(struct PsiMatrix m) { - struct PsiMatrix result = psi_new_matrix(m.cols, m.rows); - for (size_t i = 0; i < m.rows; i++) - for (size_t j = 0; j < m.cols; j++) - result.data[j * result.cols + i] = m.data[i * m.cols + j]; + struct PsiMatrix result = psi_new_matrix(m.cols, m.rows); + for (size_t i = 0; i < m.rows; i++) + for (size_t j = 0; j < m.cols; j++) + result.data[j * result.cols + i] = m.data[i * m.cols + j]; - return result; + return result; } struct PsiMatrix psi_add_matrix(struct PsiMatrix a, struct PsiMatrix b) { - assert(a.rows == b.rows && a.cols == b.cols); + assert(a.rows == b.rows && a.cols == b.cols); - struct PsiMatrix result = psi_new_matrix(a.rows, a.cols); - for (size_t i = 0; i < a.rows * a.cols; i++) - result.data[i] = psi_add_complex(a.data[i], b.data[i]); + struct PsiMatrix result = psi_new_matrix(a.rows, a.cols); + for (size_t i = 0; i < a.rows * a.cols; i++) + result.data[i] = psi_add_complex(a.data[i], b.data[i]); - return result; + return result; } struct PsiMatrix psi_sub_matrix(struct PsiMatrix a, struct PsiMatrix b) { - assert(a.rows == b.rows && a.cols == b.cols); + assert(a.rows == b.rows && a.cols == b.cols); - struct PsiMatrix result = psi_new_matrix(a.rows, a.cols); - for (size_t i = 0; i < a.rows * a.cols; i++) - result.data[i] = psi_sub_complex(a.data[i], b.data[i]); + struct PsiMatrix result = psi_new_matrix(a.rows, a.cols); + for (size_t i = 0; i < a.rows * a.cols; i++) + result.data[i] = psi_sub_complex(a.data[i], b.data[i]); - return result; + return result; } struct PsiMatrix psi_scale_matrix(struct PsiMatrix m, struct PsiComplex scalar) { - struct PsiMatrix result = psi_new_matrix(m.rows, m.cols); - for (size_t i = 0; i < m.rows * m.cols; i++) - result.data[i] = psi_mul_complex(m.data[i], scalar); + struct PsiMatrix result = psi_new_matrix(m.rows, m.cols); + for (size_t i = 0; i < m.rows * m.cols; i++) + result.data[i] = psi_mul_complex(m.data[i], scalar); - return result; + return result; } struct PsiMatrix psi_matrix_from_vector(struct PsiVector v) { - if (v.kind == PSI_ROW_VECTOR) - return psi_new_matrix_from(v.data, 1, v.size); + if (v.kind == PSI_ROW_VECTOR) + return psi_new_matrix_from(v.data, 1, v.size); - return psi_new_matrix_from(v.data, v.size, 1); + return psi_new_matrix_from(v.data, v.size, 1); } struct PsiVector psi_vector_from_matrix(struct PsiMatrix m, enum PsiVectorKind kind) { - return psi_new_vector_from(m.data, m.rows * m.cols, kind); + return psi_new_vector_from(m.data, m.rows * m.cols, kind); } struct PsiVector psi_mul_vector_matrix(struct PsiVector v, struct PsiMatrix m) { - if (v.kind == PSI_COLUMN_VECTOR) - { - assert(m.cols == v.size); + if (v.kind == PSI_COLUMN_VECTOR) + { + assert(m.cols == v.size); - struct PsiVector result = psi_new_vector(m.rows, PSI_COLUMN_VECTOR); - for (size_t i = 0; i < m.rows; i++) - { - struct PsiComplex sum = psi_new_complex(0.0, 0.0); - for (size_t j = 0; j < m.cols; j++) - sum = psi_add_complex(sum, psi_mul_complex(m.data[i * m.cols + j], v.data[j])); + struct PsiVector result = psi_new_vector(m.rows, PSI_COLUMN_VECTOR); + for (size_t i = 0; i < m.rows; i++) + { + struct PsiComplex sum = psi_new_complex(0.0, 0.0); + for (size_t j = 0; j < m.cols; j++) + sum = psi_add_complex(sum, psi_mul_complex(m.data[i * m.cols + j], v.data[j])); - result.data[i] = sum; - } + result.data[i] = sum; + } - return result; - } + return result; + } - assert(v.size == m.rows); + assert(v.size == m.rows); - struct PsiVector result = psi_new_vector(m.cols, PSI_ROW_VECTOR); - for (size_t j = 0; j < m.cols; j++) - { - struct PsiComplex sum = psi_new_complex(0.0, 0.0); - for (size_t i = 0; i < m.rows; i++) - sum = psi_add_complex(sum, psi_mul_complex(v.data[i], m.data[i * m.cols + j])); + struct PsiVector result = psi_new_vector(m.cols, PSI_ROW_VECTOR); + for (size_t j = 0; j < m.cols; j++) + { + struct PsiComplex sum = psi_new_complex(0.0, 0.0); + for (size_t i = 0; i < m.rows; i++) + sum = psi_add_complex(sum, psi_mul_complex(v.data[i], m.data[i * m.cols + j])); - result.data[j] = sum; - } + result.data[j] = sum; + } - return result; + return result; } diff --git a/src/maths/matrix.rs b/src/maths/matrix.rs deleted file mode 100644 index c9d96a4..0000000 --- a/src/maths/matrix.rs +++ /dev/null @@ -1,310 +0,0 @@ -use super::Float; -use core::{fmt, ops}; - -#[macro_export] -macro_rules! matrix { - ( $( $( $x:expr ),* );* ) => {{ - let mut data = Vec::new(); - let mut rows = 0; - let mut cols = 0; - - $( - let row_data = $( $x )*; - if cols == 0 { - cols = row_data.len(); - } - assert_eq!(cols, row_data.len(), "All rows must have the same number of columns."); - data.extend(row_data); - rows += 1; - )* - - $crate::Matrix::new(rows, cols, data) - }}; -} - -macro_rules! impl_matrix_ops { - ($($trait:ident, $method:ident, $other:ty, $output:ty, $scale_fn:ident),* $(,)?) => { - $( - impl core::ops::$trait<$other> for Matrix { - type Output = $output; - - fn $method(self, other: $other) -> Self::Output { - self.$scale_fn(other) - } - } - )* - }; - ($($trait:ident, $method:ident, $other:ty, $scale_fn:ident),* $(,)?) => { - $( - impl core::ops::$trait<$other> for Matrix { - fn $method(&mut self, other: $other) { - *self = self.$scale_fn(other); - } - } - )* - }; -} - -#[derive(Clone)] -pub struct Matrix { - pub data: Vec, - pub rows: usize, - pub cols: usize, -} - -impl Matrix { - pub fn new(rows: usize, cols: usize, data: Vec) -> Self { - Matrix { data, rows, cols } - } - - pub fn get(&self, row: usize, col: usize) -> T { - self.data[row * self.cols + col] - } - - pub fn set(&mut self, row: usize, col: usize, value: T) { - self.data[row * self.cols + col] = value; - } - - pub fn dot(&self, other: &Self) -> Option> { - if self.cols != other.rows { - return None; - } - - let mut result = Matrix::new( - self.rows, - other.cols, - vec![T::zero(); self.rows * other.cols], - ); - for i in 0..self.rows { - for j in 0..other.cols { - let mut sum = T::zero(); - for k in 0..self.cols { - sum += self.get(i, k) * other.get(k, j) ; - } - result.set(i, j, sum); - } - } - Some(result) - } - - pub fn kronecker(&self, other: &Self) -> Matrix { - let new_rows = self.rows * other.rows; - let new_cols = self.cols * other.cols; - - let mut result = Matrix::new(new_rows, new_cols, vec![T::zero(); new_rows * new_cols]); - - for i in 0..self.rows { - for j in 0..self.cols { - let self_val = self.get(i, j); - for k in 0..other.rows { - for l in 0..other.cols { - let result_row = i * other.rows + k; - let result_col = j * other.cols + l; - result.set(result_row, result_col, self_val * other.get(k, l)); - } - } - } - } - - result - } - - pub fn transpose(&self) -> Matrix { - let mut result = Matrix::new(self.cols, self.rows, vec![T::zero(); self.cols * self.rows]); - - for i in 0..self.rows { - for j in 0..self.cols { - let value = self.get(i, j); - result.set(j, i, value); - } - } - - result - } - - pub fn add_to(&self, other: &Self) -> Option> { - if self.rows != other.rows || self.cols != other.cols { - return None; - } - - let mut result = Matrix::new(self.rows, self.cols, vec![T::zero(); self.rows * self.cols]); - - for i in 0..self.rows { - for j in 0..self.cols { - let sum = self.get(i, j) + other.get(i, j); - result.set(i, j, sum); - } - } - Some(result) - } - - pub fn subtract(&self, other: &Self) -> Option> { - if self.rows != other.rows || self.cols != other.cols { - return None; - } - - let mut result = Matrix::new(self.rows, self.cols, vec![T::zero(); self.rows * self.cols]); - - for i in 0..self.rows { - for j in 0..self.cols { - let diff = self.get(i, j) - other.get(i, j); - result.set(i, j, diff); - } - } - Some(result) - } - - pub fn scale(&self, scalar: T) -> Matrix { - let mut result = Matrix::new(self.rows, self.cols, vec![T::zero(); self.rows * self.cols]); - - for i in 0..self.rows { - for j in 0..self.cols { - let scaled_value = self.get(i, j) * scalar; - result.set(i, j, scaled_value); - } - } - result - } -} - -impl ops::Index<(usize, usize)> for Matrix { - type Output = T; - - fn index(&self, index: (usize, usize)) -> &Self::Output { - &self.data[index.0 * self.cols + index.1] - } -} - -impl ops::IndexMut<(usize, usize)> for Matrix { - fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output { - &mut self.data[index.0 * self.cols + index.1] - } -} - -impl ops::AddAssign<&Matrix> for Matrix { - fn add_assign(&mut self, other: &Matrix) { - if let Some(result) = self.add_to(other) { - *self = result; - } - } -} - -impl ops::SubAssign<&Matrix> for Matrix { - fn sub_assign(&mut self, other: &Matrix) { - if let Some(result) = self.subtract(other) { - *self = result; - } - } -} - -impl_matrix_ops! { - Add, add, &Matrix, Option>, add_to, - Sub, sub, &Matrix, Option>, subtract, - Mul, mul, T, Matrix, scale, - Div, div, T, Matrix, scale, -} - -impl_matrix_ops! { - MulAssign, mul_assign, T, scale, - DivAssign, div_assign, T, scale, -} - -impl fmt::Debug for Matrix { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for i in 0..self.rows { - for j in 0..self.cols { - write!(f, "{:?} ", self.get(i, j))?; - } - writeln!(f)?; - } - Ok(()) - } -} - -impl fmt::Display for Matrix { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let elements: Vec = self.data.iter().map(ToString::to_string).collect(); - let is_complex = elements.iter().any(|element| element.contains("i")); - - let normalized: Vec<(f64, f64)> = self - .data - .iter() - .map(|element| { - let element_string = element.to_string(); - - if is_complex { - let element_string = element_string.trim_end_matches('i').trim(); - let element_split: Vec<&str> = element_string.split_whitespace().collect(); - let real = element_split[0].parse::().unwrap(); - let imaginary = element_split - .get(2) - .map_or(0.0, |&s| s.parse::().unwrap()); - (real, imaginary) - } else { - (element_string.parse::().unwrap(), 0.0) - } - }) - .collect(); - - let max_widths = normalized - .iter() - .fold((0, 0), |(max_0, max_1), &(real, imag)| { - let new_max_0 = max_0.max(format!("{:.2}", real).len()); - let new_max_1 = if is_complex { - max_1.max(format!("{:.2}", imag.abs()).len()) - } else { - max_1 - }; - (new_max_0, new_max_1) - }); - - let aligned: Vec = normalized - .iter() - .map(|&(real, imag)| { - if is_complex { - format!( - "{:>rewidth$.2} {} {:>imwidth$.2}i", - real, - if imag > 0.0 { "+" } else { "-" }, - imag.abs(), - rewidth = max_widths.0, - imwidth = max_widths.1, - ) - } else { - format!("{:>width$.2}", real, width = max_widths.0) - } - }) - .collect(); - - for i in 0..self.rows { - if i == 0 { - write!(f, "┌")?; - } else if i == self.rows - 1 { - write!(f, "└")?; - } else { - write!(f, "│")?; - } - - for j in 0..self.cols { - write!(f, "{}", aligned[i + j * self.rows])?; - if j != self.cols - 1 { - write!(f, ", ")?; - } - } - - if i == 0 { - write!(f, "┐")?; - } else if i == self.rows - 1 { - write!(f, "┘")?; - } else { - write!(f, "│")?; - } - - if i != self.rows - 1 { - writeln!(f)?; - } - } - - Ok(()) - } -} diff --git a/src/maths/simd.c b/src/maths/simd.c index 9e83dcc..fc25de8 100644 --- a/src/maths/simd.c +++ b/src/maths/simd.c @@ -12,144 +12,144 @@ enum PsiSimdCapability psi_detect_simd(void) { #if defined(__x86_64__) || defined(__i386__) - if (__builtin_cpu_supports("avx512f") && __builtin_cpu_supports("avx512dq")) - return PSI_SIMD_AVX512; - if (__builtin_cpu_supports("avx2") && __builtin_cpu_supports("fma")) - return PSI_SIMD_AVX2; + if (__builtin_cpu_supports("avx512f") && __builtin_cpu_supports("avx512dq")) + return PSI_SIMD_AVX512; + if (__builtin_cpu_supports("avx2") && __builtin_cpu_supports("fma")) + return PSI_SIMD_AVX2; - return PSI_SIMD_NONE; + return PSI_SIMD_NONE; #elif defined(__aarch64__) - return PSI_SIMD_NEON; + return PSI_SIMD_NEON; #else - return PSI_SIMD_NONE; + return PSI_SIMD_NONE; #endif } const char* psi_simd_name(enum PsiSimdCapability cap) { - switch (cap) - { - case PSI_SIMD_NONE: return "Scalar"; - case PSI_SIMD_AVX2: return "AVX2+FMA"; - case PSI_SIMD_AVX512: return "AVX-512"; - case PSI_SIMD_NEON: return "NEON"; - } - - return "Scalar"; + switch (cap) + { + case PSI_SIMD_NONE: return "Scalar"; + case PSI_SIMD_AVX2: return "AVX2+FMA"; + case PSI_SIMD_AVX512: return "AVX-512"; + case PSI_SIMD_NEON: return "NEON"; + } + + return "Scalar"; } static void apply_pair(struct PsiComplex* state, size_t i, size_t j, struct PsiComplex g00, struct PsiComplex g01, struct PsiComplex g10, struct PsiComplex g11) { - struct PsiComplex s0 = state[i]; - struct PsiComplex s1 = state[j]; - - state[i] = psi_new_complex(s0.real * g00.real - s0.imaginary * g00.imaginary + - s1.real * g01.real - s1.imaginary * g01.imaginary, - s0.real * g00.imaginary + s0.imaginary * g00.real + - s1.real * g01.imaginary + s1.imaginary * g01.real); - state[j] = psi_new_complex(s0.real * g10.real - s0.imaginary * g10.imaginary + - s1.real * g11.real - s1.imaginary * g11.imaginary, - s0.real * g10.imaginary + s0.imaginary * g10.real + - s1.real * g11.imaginary + s1.imaginary * g11.real); + struct PsiComplex s0 = state[i]; + struct PsiComplex s1 = state[j]; + + state[i] = psi_new_complex(s0.real * g00.real - s0.imaginary * g00.imaginary + + s1.real * g01.real - s1.imaginary * g01.imaginary, + s0.real * g00.imaginary + s0.imaginary * g00.real + + s1.real * g01.imaginary + s1.imaginary * g01.real); + state[j] = psi_new_complex(s0.real * g10.real - s0.imaginary * g10.imaginary + + s1.real * g11.real - s1.imaginary * g11.imaginary, + s0.real * g10.imaginary + s0.imaginary * g10.real + + s1.real * g11.imaginary + s1.imaginary * g11.real); } static void apply_scalar(struct PsiComplex* state, const struct PsiComplex gate[2][2], size_t target, size_t num_qubits) { - size_t target_bit = num_qubits - 1 - target; - size_t step = (size_t)1 << target_bit; - size_t dim = (size_t)1 << num_qubits; + size_t target_bit = num_qubits - 1 - target; + size_t step = (size_t)1 << target_bit; + size_t dim = (size_t)1 << num_qubits; - for (size_t i = 0; i < dim; i++) - { - if ((i >> target_bit) & 1) - continue; + for (size_t i = 0; i < dim; i++) + { + if ((i >> target_bit) & 1) + continue; - apply_pair(state, i, i | step, gate[0][0], gate[0][1], gate[1][0], gate[1][1]); - } + apply_pair(state, i, i | step, gate[0][0], gate[0][1], gate[1][0], gate[1][1]); + } } static size_t (*build_pairs(size_t dim, size_t target_bit, size_t step, size_t* out_count))[2] { - size_t (*pairs)[2] = malloc((dim / 2) * sizeof(*pairs)); - assert(pairs != NULL || dim == 0); - - size_t n = 0; - for (size_t i = 0; i < dim; i++) - if (((i >> target_bit) & 1) == 0) - { - pairs[n][0] = i; - pairs[n][1] = i | step; - n++; - } - - *out_count = n; - return pairs; + size_t (*pairs)[2] = malloc((dim / 2) * sizeof(*pairs)); + assert(pairs != NULL || dim == 0); + + size_t n = 0; + for (size_t i = 0; i < dim; i++) + if (((i >> target_bit) & 1) == 0) + { + pairs[n][0] = i; + pairs[n][1] = i | step; + n++; + } + + *out_count = n; + return pairs; } #if defined(__aarch64__) static void apply_neon(struct PsiComplex* state, const struct PsiComplex gate[2][2], size_t target, size_t num_qubits) { - size_t target_bit = num_qubits - 1 - target; - size_t step = (size_t)1 << target_bit; - size_t dim = (size_t)1 << num_qubits; - - struct PsiComplex g00 = gate[0][0]; - struct PsiComplex g01 = gate[0][1]; - struct PsiComplex g10 = gate[1][0]; - struct PsiComplex g11 = gate[1][1]; - - size_t np; - size_t (*pairs)[2] = build_pairs(dim, target_bit, step, &np); - size_t chunks = np / 2; - - float64x2_t g00_re = vdupq_n_f64(g00.real); - float64x2_t g00_im = vdupq_n_f64(g00.imaginary); - float64x2_t g01_re = vdupq_n_f64(g01.real); - float64x2_t g01_im = vdupq_n_f64(g01.imaginary); - float64x2_t g10_re = vdupq_n_f64(g10.real); - float64x2_t g10_im = vdupq_n_f64(g10.imaginary); - float64x2_t g11_re = vdupq_n_f64(g11.real); - float64x2_t g11_im = vdupq_n_f64(g11.imaginary); - - for (size_t chunk = 0; chunk < chunks; chunk++) - { - size_t i0 = pairs[chunk * 2][0]; - size_t j0 = pairs[chunk * 2][1]; - size_t i1 = pairs[chunk * 2 + 1][0]; - size_t j1 = pairs[chunk * 2 + 1][1]; - - double s0r[2] = { state[i0].real, state[i1].real }; - double s0i[2] = { state[i0].imaginary, state[i1].imaginary }; - double s1r[2] = { state[j0].real, state[j1].real }; - double s1i[2] = { state[j0].imaginary, state[j1].imaginary }; - - float64x2_t s0_re = vld1q_f64(s0r); - float64x2_t s0_im = vld1q_f64(s0i); - float64x2_t s1_re = vld1q_f64(s1r); - float64x2_t s1_im = vld1q_f64(s1i); - - float64x2_t new0_re = vaddq_f64(vfmsq_f64(vmulq_f64(s0_re, g00_re), s0_im, g00_im), - vfmsq_f64(vmulq_f64(s1_re, g01_re), s1_im, g01_im)); - float64x2_t new0_im = vaddq_f64(vfmaq_f64(vmulq_f64(s0_re, g00_im), s0_im, g00_re), - vfmaq_f64(vmulq_f64(s1_re, g01_im), s1_im, g01_re)); - float64x2_t new1_re = vaddq_f64(vfmsq_f64(vmulq_f64(s0_re, g10_re), s0_im, g10_im), - vfmsq_f64(vmulq_f64(s1_re, g11_re), s1_im, g11_im)); - float64x2_t new1_im = vaddq_f64(vfmaq_f64(vmulq_f64(s0_re, g10_im), s0_im, g10_re), - vfmaq_f64(vmulq_f64(s1_re, g11_im), s1_im, g11_re)); - - state[i0] = psi_new_complex(vgetq_lane_f64(new0_re, 0), vgetq_lane_f64(new0_im, 0)); - state[j0] = psi_new_complex(vgetq_lane_f64(new1_re, 0), vgetq_lane_f64(new1_im, 0)); - state[i1] = psi_new_complex(vgetq_lane_f64(new0_re, 1), vgetq_lane_f64(new0_im, 1)); - state[j1] = psi_new_complex(vgetq_lane_f64(new1_re, 1), vgetq_lane_f64(new1_im, 1)); - } - - for (size_t p = chunks * 2; p < np; p++) - apply_pair(state, pairs[p][0], pairs[p][1], g00, g01, g10, g11); - - free(pairs); + size_t target_bit = num_qubits - 1 - target; + size_t step = (size_t)1 << target_bit; + size_t dim = (size_t)1 << num_qubits; + + struct PsiComplex g00 = gate[0][0]; + struct PsiComplex g01 = gate[0][1]; + struct PsiComplex g10 = gate[1][0]; + struct PsiComplex g11 = gate[1][1]; + + size_t np; + size_t (*pairs)[2] = build_pairs(dim, target_bit, step, &np); + size_t chunks = np / 2; + + float64x2_t g00_re = vdupq_n_f64(g00.real); + float64x2_t g00_im = vdupq_n_f64(g00.imaginary); + float64x2_t g01_re = vdupq_n_f64(g01.real); + float64x2_t g01_im = vdupq_n_f64(g01.imaginary); + float64x2_t g10_re = vdupq_n_f64(g10.real); + float64x2_t g10_im = vdupq_n_f64(g10.imaginary); + float64x2_t g11_re = vdupq_n_f64(g11.real); + float64x2_t g11_im = vdupq_n_f64(g11.imaginary); + + for (size_t chunk = 0; chunk < chunks; chunk++) + { + size_t i0 = pairs[chunk * 2][0]; + size_t j0 = pairs[chunk * 2][1]; + size_t i1 = pairs[chunk * 2 + 1][0]; + size_t j1 = pairs[chunk * 2 + 1][1]; + + double s0r[2] = { state[i0].real, state[i1].real }; + double s0i[2] = { state[i0].imaginary, state[i1].imaginary }; + double s1r[2] = { state[j0].real, state[j1].real }; + double s1i[2] = { state[j0].imaginary, state[j1].imaginary }; + + float64x2_t s0_re = vld1q_f64(s0r); + float64x2_t s0_im = vld1q_f64(s0i); + float64x2_t s1_re = vld1q_f64(s1r); + float64x2_t s1_im = vld1q_f64(s1i); + + float64x2_t new0_re = vaddq_f64(vfmsq_f64(vmulq_f64(s0_re, g00_re), s0_im, g00_im), + vfmsq_f64(vmulq_f64(s1_re, g01_re), s1_im, g01_im)); + float64x2_t new0_im = vaddq_f64(vfmaq_f64(vmulq_f64(s0_re, g00_im), s0_im, g00_re), + vfmaq_f64(vmulq_f64(s1_re, g01_im), s1_im, g01_re)); + float64x2_t new1_re = vaddq_f64(vfmsq_f64(vmulq_f64(s0_re, g10_re), s0_im, g10_im), + vfmsq_f64(vmulq_f64(s1_re, g11_re), s1_im, g11_im)); + float64x2_t new1_im = vaddq_f64(vfmaq_f64(vmulq_f64(s0_re, g10_im), s0_im, g10_re), + vfmaq_f64(vmulq_f64(s1_re, g11_im), s1_im, g11_re)); + + state[i0] = psi_new_complex(vgetq_lane_f64(new0_re, 0), vgetq_lane_f64(new0_im, 0)); + state[j0] = psi_new_complex(vgetq_lane_f64(new1_re, 0), vgetq_lane_f64(new1_im, 0)); + state[i1] = psi_new_complex(vgetq_lane_f64(new0_re, 1), vgetq_lane_f64(new0_im, 1)); + state[j1] = psi_new_complex(vgetq_lane_f64(new1_re, 1), vgetq_lane_f64(new1_im, 1)); + } + + for (size_t p = chunks * 2; p < np; p++) + apply_pair(state, pairs[p][0], pairs[p][1], g00, g01, g10, g11); + + free(pairs); } #endif @@ -158,167 +158,167 @@ __attribute__((target("avx2,fma"))) static void apply_avx2(struct PsiComplex* st const struct PsiComplex gate[2][2], size_t target, size_t num_qubits) { - size_t target_bit = num_qubits - 1 - target; - size_t step = (size_t)1 << target_bit; - size_t dim = (size_t)1 << num_qubits; - - struct PsiComplex g00 = gate[0][0]; - struct PsiComplex g01 = gate[0][1]; - struct PsiComplex g10 = gate[1][0]; - struct PsiComplex g11 = gate[1][1]; - - size_t np; - size_t (*pairs)[2] = build_pairs(dim, target_bit, step, &np); - size_t chunks = np / 2; - - for (size_t chunk = 0; chunk < chunks; chunk++) - { - size_t i0 = pairs[chunk * 2][0]; - size_t j0 = pairs[chunk * 2][1]; - size_t i1 = pairs[chunk * 2 + 1][0]; - size_t j1 = pairs[chunk * 2 + 1][1]; - - __m256d s0_re = - _mm256_set_pd(state[j1].real, state[i1].real, state[j0].real, state[i0].real); - __m256d s0_im = _mm256_set_pd(state[j1].imaginary, state[i1].imaginary, state[j0].imaginary, - state[i0].imaginary); - - __m256d g_re_0 = _mm256_set_pd(g01.real, g00.real, g01.real, g00.real); - __m256d g_im_0 = _mm256_set_pd(g01.imaginary, g00.imaginary, g01.imaginary, g00.imaginary); - __m256d g_re_1 = _mm256_set_pd(g11.real, g10.real, g11.real, g10.real); - __m256d g_im_1 = _mm256_set_pd(g11.imaginary, g10.imaginary, g11.imaginary, g10.imaginary); - - __m256d prod0_re = _mm256_fmsub_pd(s0_re, g_re_0, _mm256_mul_pd(s0_im, g_im_0)); - __m256d prod0_im = _mm256_fmadd_pd(s0_re, g_im_0, _mm256_mul_pd(s0_im, g_re_0)); - __m256d prod1_re = _mm256_fmsub_pd(s0_re, g_re_1, _mm256_mul_pd(s0_im, g_im_1)); - __m256d prod1_im = _mm256_fmadd_pd(s0_re, g_im_1, _mm256_mul_pd(s0_im, g_re_1)); - - double res0_re[4]; - double res0_im[4]; - double res1_re[4]; - double res1_im[4]; - - _mm256_storeu_pd(res0_re, prod0_re); - _mm256_storeu_pd(res0_im, prod0_im); - _mm256_storeu_pd(res1_re, prod1_re); - _mm256_storeu_pd(res1_im, prod1_im); - - state[i0] = psi_new_complex(res0_re[0] + res0_re[1], res0_im[0] + res0_im[1]); - state[j0] = psi_new_complex(res1_re[0] + res1_re[1], res1_im[0] + res1_im[1]); - state[i1] = psi_new_complex(res0_re[2] + res0_re[3], res0_im[2] + res0_im[3]); - state[j1] = psi_new_complex(res1_re[2] + res1_re[3], res1_im[2] + res1_im[3]); - } - - for (size_t p = chunks * 2; p < np; p++) - apply_pair(state, pairs[p][0], pairs[p][1], g00, g01, g10, g11); - - free(pairs); + size_t target_bit = num_qubits - 1 - target; + size_t step = (size_t)1 << target_bit; + size_t dim = (size_t)1 << num_qubits; + + struct PsiComplex g00 = gate[0][0]; + struct PsiComplex g01 = gate[0][1]; + struct PsiComplex g10 = gate[1][0]; + struct PsiComplex g11 = gate[1][1]; + + size_t np; + size_t (*pairs)[2] = build_pairs(dim, target_bit, step, &np); + size_t chunks = np / 2; + + for (size_t chunk = 0; chunk < chunks; chunk++) + { + size_t i0 = pairs[chunk * 2][0]; + size_t j0 = pairs[chunk * 2][1]; + size_t i1 = pairs[chunk * 2 + 1][0]; + size_t j1 = pairs[chunk * 2 + 1][1]; + + __m256d s0_re = + _mm256_set_pd(state[j1].real, state[i1].real, state[j0].real, state[i0].real); + __m256d s0_im = _mm256_set_pd(state[j1].imaginary, state[i1].imaginary, state[j0].imaginary, + state[i0].imaginary); + + __m256d g_re_0 = _mm256_set_pd(g01.real, g00.real, g01.real, g00.real); + __m256d g_im_0 = _mm256_set_pd(g01.imaginary, g00.imaginary, g01.imaginary, g00.imaginary); + __m256d g_re_1 = _mm256_set_pd(g11.real, g10.real, g11.real, g10.real); + __m256d g_im_1 = _mm256_set_pd(g11.imaginary, g10.imaginary, g11.imaginary, g10.imaginary); + + __m256d prod0_re = _mm256_fmsub_pd(s0_re, g_re_0, _mm256_mul_pd(s0_im, g_im_0)); + __m256d prod0_im = _mm256_fmadd_pd(s0_re, g_im_0, _mm256_mul_pd(s0_im, g_re_0)); + __m256d prod1_re = _mm256_fmsub_pd(s0_re, g_re_1, _mm256_mul_pd(s0_im, g_im_1)); + __m256d prod1_im = _mm256_fmadd_pd(s0_re, g_im_1, _mm256_mul_pd(s0_im, g_re_1)); + + double res0_re[4]; + double res0_im[4]; + double res1_re[4]; + double res1_im[4]; + + _mm256_storeu_pd(res0_re, prod0_re); + _mm256_storeu_pd(res0_im, prod0_im); + _mm256_storeu_pd(res1_re, prod1_re); + _mm256_storeu_pd(res1_im, prod1_im); + + state[i0] = psi_new_complex(res0_re[0] + res0_re[1], res0_im[0] + res0_im[1]); + state[j0] = psi_new_complex(res1_re[0] + res1_re[1], res1_im[0] + res1_im[1]); + state[i1] = psi_new_complex(res0_re[2] + res0_re[3], res0_im[2] + res0_im[3]); + state[j1] = psi_new_complex(res1_re[2] + res1_re[3], res1_im[2] + res1_im[3]); + } + + for (size_t p = chunks * 2; p < np; p++) + apply_pair(state, pairs[p][0], pairs[p][1], g00, g01, g10, g11); + + free(pairs); } __attribute__((target("avx512f,avx512dq"))) static void apply_avx512(struct PsiComplex* state, const struct PsiComplex gate[2][2], size_t target, size_t num_qubits) { - size_t target_bit = num_qubits - 1 - target; - size_t step = (size_t)1 << target_bit; - size_t dim = (size_t)1 << num_qubits; - - struct PsiComplex g00 = gate[0][0]; - struct PsiComplex g01 = gate[0][1]; - struct PsiComplex g10 = gate[1][0]; - struct PsiComplex g11 = gate[1][1]; - - size_t np; - size_t (*pairs)[2] = build_pairs(dim, target_bit, step, &np); - size_t chunks = np / 4; - - for (size_t chunk = 0; chunk < chunks; chunk++) - { - size_t base = chunk * 4; - size_t i0 = pairs[base][0], j0 = pairs[base][1]; - size_t i1 = pairs[base + 1][0], j1 = pairs[base + 1][1]; - size_t i2 = pairs[base + 2][0], j2 = pairs[base + 2][1]; - size_t i3 = pairs[base + 3][0], j3 = pairs[base + 3][1]; - - __m512d s0_re = - _mm512_set_pd(state[j3].real, state[i3].real, state[j2].real, state[i2].real, - state[j1].real, state[i1].real, state[j0].real, state[i0].real); - __m512d s0_im = _mm512_set_pd(state[j3].imaginary, state[i3].imaginary, state[j2].imaginary, - state[i2].imaginary, state[j1].imaginary, state[i1].imaginary, - state[j0].imaginary, state[i0].imaginary); - - __m512d g_re_0 = _mm512_set_pd(g01.real, g00.real, g01.real, g00.real, g01.real, g00.real, - g01.real, g00.real); - __m512d g_im_0 = _mm512_set_pd(g01.imaginary, g00.imaginary, g01.imaginary, g00.imaginary, - g01.imaginary, g00.imaginary, g01.imaginary, g00.imaginary); - __m512d g_re_1 = _mm512_set_pd(g11.real, g10.real, g11.real, g10.real, g11.real, g10.real, - g11.real, g10.real); - __m512d g_im_1 = _mm512_set_pd(g11.imaginary, g10.imaginary, g11.imaginary, g10.imaginary, - g11.imaginary, g10.imaginary, g11.imaginary, g10.imaginary); - - __m512d prod0_re = _mm512_fmsub_pd(s0_re, g_re_0, _mm512_mul_pd(s0_im, g_im_0)); - __m512d prod0_im = _mm512_fmadd_pd(s0_re, g_im_0, _mm512_mul_pd(s0_im, g_re_0)); - __m512d prod1_re = _mm512_fmsub_pd(s0_re, g_re_1, _mm512_mul_pd(s0_im, g_im_1)); - __m512d prod1_im = _mm512_fmadd_pd(s0_re, g_im_1, _mm512_mul_pd(s0_im, g_re_1)); - - double res0_re[8]; - double res0_im[8]; - double res1_re[8]; - double res1_im[8]; - - _mm512_storeu_pd(res0_re, prod0_re); - _mm512_storeu_pd(res0_im, prod0_im); - _mm512_storeu_pd(res1_re, prod1_re); - _mm512_storeu_pd(res1_im, prod1_im); - - state[i0] = psi_new_complex(res0_re[0] + res0_re[1], res0_im[0] + res0_im[1]); - state[j0] = psi_new_complex(res1_re[0] + res1_re[1], res1_im[0] + res1_im[1]); - state[i1] = psi_new_complex(res0_re[2] + res0_re[3], res0_im[2] + res0_im[3]); - state[j1] = psi_new_complex(res1_re[2] + res1_re[3], res1_im[2] + res1_im[3]); - state[i2] = psi_new_complex(res0_re[4] + res0_re[5], res0_im[4] + res0_im[5]); - state[j2] = psi_new_complex(res1_re[4] + res1_re[5], res1_im[4] + res1_im[5]); - state[i3] = psi_new_complex(res0_re[6] + res0_re[7], res0_im[6] + res0_im[7]); - state[j3] = psi_new_complex(res1_re[6] + res1_re[7], res1_im[6] + res1_im[7]); - } - - for (size_t p = chunks * 4; p < np; p++) - apply_pair(state, pairs[p][0], pairs[p][1], g00, g01, g10, g11); - - free(pairs); + size_t target_bit = num_qubits - 1 - target; + size_t step = (size_t)1 << target_bit; + size_t dim = (size_t)1 << num_qubits; + + struct PsiComplex g00 = gate[0][0]; + struct PsiComplex g01 = gate[0][1]; + struct PsiComplex g10 = gate[1][0]; + struct PsiComplex g11 = gate[1][1]; + + size_t np; + size_t (*pairs)[2] = build_pairs(dim, target_bit, step, &np); + size_t chunks = np / 4; + + for (size_t chunk = 0; chunk < chunks; chunk++) + { + size_t base = chunk * 4; + size_t i0 = pairs[base][0], j0 = pairs[base][1]; + size_t i1 = pairs[base + 1][0], j1 = pairs[base + 1][1]; + size_t i2 = pairs[base + 2][0], j2 = pairs[base + 2][1]; + size_t i3 = pairs[base + 3][0], j3 = pairs[base + 3][1]; + + __m512d s0_re = + _mm512_set_pd(state[j3].real, state[i3].real, state[j2].real, state[i2].real, + state[j1].real, state[i1].real, state[j0].real, state[i0].real); + __m512d s0_im = _mm512_set_pd(state[j3].imaginary, state[i3].imaginary, state[j2].imaginary, + state[i2].imaginary, state[j1].imaginary, state[i1].imaginary, + state[j0].imaginary, state[i0].imaginary); + + __m512d g_re_0 = _mm512_set_pd(g01.real, g00.real, g01.real, g00.real, g01.real, g00.real, + g01.real, g00.real); + __m512d g_im_0 = _mm512_set_pd(g01.imaginary, g00.imaginary, g01.imaginary, g00.imaginary, + g01.imaginary, g00.imaginary, g01.imaginary, g00.imaginary); + __m512d g_re_1 = _mm512_set_pd(g11.real, g10.real, g11.real, g10.real, g11.real, g10.real, + g11.real, g10.real); + __m512d g_im_1 = _mm512_set_pd(g11.imaginary, g10.imaginary, g11.imaginary, g10.imaginary, + g11.imaginary, g10.imaginary, g11.imaginary, g10.imaginary); + + __m512d prod0_re = _mm512_fmsub_pd(s0_re, g_re_0, _mm512_mul_pd(s0_im, g_im_0)); + __m512d prod0_im = _mm512_fmadd_pd(s0_re, g_im_0, _mm512_mul_pd(s0_im, g_re_0)); + __m512d prod1_re = _mm512_fmsub_pd(s0_re, g_re_1, _mm512_mul_pd(s0_im, g_im_1)); + __m512d prod1_im = _mm512_fmadd_pd(s0_re, g_im_1, _mm512_mul_pd(s0_im, g_re_1)); + + double res0_re[8]; + double res0_im[8]; + double res1_re[8]; + double res1_im[8]; + + _mm512_storeu_pd(res0_re, prod0_re); + _mm512_storeu_pd(res0_im, prod0_im); + _mm512_storeu_pd(res1_re, prod1_re); + _mm512_storeu_pd(res1_im, prod1_im); + + state[i0] = psi_new_complex(res0_re[0] + res0_re[1], res0_im[0] + res0_im[1]); + state[j0] = psi_new_complex(res1_re[0] + res1_re[1], res1_im[0] + res1_im[1]); + state[i1] = psi_new_complex(res0_re[2] + res0_re[3], res0_im[2] + res0_im[3]); + state[j1] = psi_new_complex(res1_re[2] + res1_re[3], res1_im[2] + res1_im[3]); + state[i2] = psi_new_complex(res0_re[4] + res0_re[5], res0_im[4] + res0_im[5]); + state[j2] = psi_new_complex(res1_re[4] + res1_re[5], res1_im[4] + res1_im[5]); + state[i3] = psi_new_complex(res0_re[6] + res0_re[7], res0_im[6] + res0_im[7]); + state[j3] = psi_new_complex(res1_re[6] + res1_re[7], res1_im[6] + res1_im[7]); + } + + for (size_t p = chunks * 4; p < np; p++) + apply_pair(state, pairs[p][0], pairs[p][1], g00, g01, g10, g11); + + free(pairs); } #endif void psi_apply_single_qubit_gate_simd(struct PsiComplex* state, const struct PsiComplex gate[2][2], size_t target, size_t num_qubits) { - enum PsiSimdCapability cap = psi_detect_simd(); + enum PsiSimdCapability cap = psi_detect_simd(); #if defined(__x86_64__) || defined(__i386__) - if (cap == PSI_SIMD_AVX512) - { - apply_avx512(state, gate, target, num_qubits); - return; - } - if (cap == PSI_SIMD_AVX2) - { - apply_avx2(state, gate, target, num_qubits); - return; - } + if (cap == PSI_SIMD_AVX512) + { + apply_avx512(state, gate, target, num_qubits); + return; + } + if (cap == PSI_SIMD_AVX2) + { + apply_avx2(state, gate, target, num_qubits); + return; + } #elif defined(__aarch64__) - if (cap == PSI_SIMD_NEON) - { - apply_neon(state, gate, target, num_qubits); - return; - } + if (cap == PSI_SIMD_NEON) + { + apply_neon(state, gate, target, num_qubits); + return; + } #endif - (void)cap; - apply_scalar(state, gate, target, num_qubits); + (void)cap; + apply_scalar(state, gate, target, num_qubits); } void psi_apply_single_qubit_gate_simd_parallel(struct PsiComplex* state, const struct PsiComplex gate[2][2], size_t target, size_t num_qubits) { - apply_scalar(state, gate, target, num_qubits); + apply_scalar(state, gate, target, num_qubits); } diff --git a/src/maths/vector.c b/src/maths/vector.c index 1c5c4f9..5cd49e0 100644 --- a/src/maths/vector.c +++ b/src/maths/vector.c @@ -6,155 +6,155 @@ static enum PsiVectorKind flip_kind(enum PsiVectorKind kind) { - if (kind == PSI_ROW_VECTOR) - return PSI_COLUMN_VECTOR; + if (kind == PSI_ROW_VECTOR) + return PSI_COLUMN_VECTOR; - return PSI_ROW_VECTOR; + return PSI_ROW_VECTOR; } struct PsiVector psi_new_vector(size_t size, enum PsiVectorKind kind) { - struct PsiComplex* data = calloc(size, sizeof(struct PsiComplex)); - assert(data != NULL || size == 0); + struct PsiComplex* data = calloc(size, sizeof(struct PsiComplex)); + assert(data != NULL || size == 0); - return (struct PsiVector){ - data, - size, - kind, - }; + return (struct PsiVector){ + data, + size, + kind, + }; } struct PsiVector psi_new_vector_from(const struct PsiComplex* data, size_t size, enum PsiVectorKind kind) { - struct PsiVector v = psi_new_vector(size, kind); - memcpy(v.data, data, size * sizeof(struct PsiComplex)); + struct PsiVector v = psi_new_vector(size, kind); + memcpy(v.data, data, size * sizeof(struct PsiComplex)); - return v; + return v; } struct PsiVector psi_clone_vector(struct PsiVector v) { - return psi_new_vector_from(v.data, v.size, v.kind); + return psi_new_vector_from(v.data, v.size, v.kind); } void psi_free_vector(struct PsiVector* v) { - free(v->data); - v->data = NULL; - v->size = 0; + free(v->data); + v->data = NULL; + v->size = 0; } struct PsiComplex psi_get_vector(struct PsiVector v, size_t index) { - assert(index < v.size); - return v.data[index]; + assert(index < v.size); + return v.data[index]; } void psi_set_vector(struct PsiVector* v, size_t index, struct PsiComplex value) { - assert(index < v->size); - v->data[index] = value; + assert(index < v->size); + v->data[index] = value; } struct PsiComplex psi_dot_vector(struct PsiVector a, struct PsiVector b) { - assert(a.size == b.size); + assert(a.size == b.size); - struct PsiComplex sum = psi_new_complex(0.0, 0.0); - for (size_t i = 0; i < a.size; i++) - sum = psi_add_complex(sum, psi_mul_complex(a.data[i], b.data[i])); + struct PsiComplex sum = psi_new_complex(0.0, 0.0); + for (size_t i = 0; i < a.size; i++) + sum = psi_add_complex(sum, psi_mul_complex(a.data[i], b.data[i])); - return sum; + return sum; } struct PsiComplex psi_norm_vector(struct PsiVector v) { - struct PsiComplex sum = psi_new_complex(0.0, 0.0); - for (size_t i = 0; i < v.size; i++) - sum = psi_add_complex(sum, psi_mul_complex(v.data[i], v.data[i])); + struct PsiComplex sum = psi_new_complex(0.0, 0.0); + for (size_t i = 0; i < v.size; i++) + sum = psi_add_complex(sum, psi_mul_complex(v.data[i], v.data[i])); - return psi_sqrt_complex(sum); + return psi_sqrt_complex(sum); } struct PsiComplex psi_sum_vector(struct PsiVector v) { - struct PsiComplex sum = psi_new_complex(0.0, 0.0); - for (size_t i = 0; i < v.size; i++) - sum = psi_add_complex(sum, v.data[i]); + struct PsiComplex sum = psi_new_complex(0.0, 0.0); + for (size_t i = 0; i < v.size; i++) + sum = psi_add_complex(sum, v.data[i]); - return sum; + return sum; } static int less_than(struct PsiComplex a, struct PsiComplex b) { - if (a.real != b.real) - return a.real < b.real; + if (a.real != b.real) + return a.real < b.real; - return a.imaginary < b.imaginary; + return a.imaginary < b.imaginary; } struct PsiComplex psi_max_vector(struct PsiVector v) { - if (v.size == 0) - return psi_new_complex(0.0, 0.0); + if (v.size == 0) + return psi_new_complex(0.0, 0.0); - struct PsiComplex best = v.data[0]; - for (size_t i = 1; i < v.size; i++) - if (less_than(best, v.data[i])) - best = v.data[i]; + struct PsiComplex best = v.data[0]; + for (size_t i = 1; i < v.size; i++) + if (less_than(best, v.data[i])) + best = v.data[i]; - return best; + return best; } struct PsiComplex psi_min_vector(struct PsiVector v) { - if (v.size == 0) - return psi_new_complex(0.0, 0.0); + if (v.size == 0) + return psi_new_complex(0.0, 0.0); - struct PsiComplex best = v.data[0]; - for (size_t i = 1; i < v.size; i++) - if (less_than(v.data[i], best)) - best = v.data[i]; + struct PsiComplex best = v.data[0]; + for (size_t i = 1; i < v.size; i++) + if (less_than(v.data[i], best)) + best = v.data[i]; - return best; + return best; } struct PsiVector psi_add_vector(struct PsiVector a, struct PsiVector b) { - assert(a.size == b.size); + assert(a.size == b.size); - struct PsiVector result = psi_new_vector(a.size, a.kind); - for (size_t i = 0; i < a.size; i++) - result.data[i] = psi_add_complex(a.data[i], b.data[i]); + struct PsiVector result = psi_new_vector(a.size, a.kind); + for (size_t i = 0; i < a.size; i++) + result.data[i] = psi_add_complex(a.data[i], b.data[i]); - return result; + return result; } struct PsiVector psi_sub_vector(struct PsiVector a, struct PsiVector b) { - assert(a.size == b.size); + assert(a.size == b.size); - struct PsiVector result = psi_new_vector(a.size, a.kind); - for (size_t i = 0; i < a.size; i++) - result.data[i] = psi_sub_complex(a.data[i], b.data[i]); + struct PsiVector result = psi_new_vector(a.size, a.kind); + for (size_t i = 0; i < a.size; i++) + result.data[i] = psi_sub_complex(a.data[i], b.data[i]); - return result; + return result; } struct PsiVector psi_scale_vector(struct PsiVector v, struct PsiComplex scalar) { - struct PsiVector result = psi_new_vector(v.size, v.kind); - for (size_t i = 0; i < v.size; i++) - result.data[i] = psi_mul_complex(v.data[i], scalar); + struct PsiVector result = psi_new_vector(v.size, v.kind); + for (size_t i = 0; i < v.size; i++) + result.data[i] = psi_mul_complex(v.data[i], scalar); - return result; + return result; } struct PsiVector psi_transpose_vector(struct PsiVector v) { - struct PsiVector result = psi_clone_vector(v); - result.kind = flip_kind(v.kind); + struct PsiVector result = psi_clone_vector(v); + result.kind = flip_kind(v.kind); - return result; + return result; } diff --git a/src/maths/vector.rs b/src/maths/vector.rs deleted file mode 100644 index 11f3d29..0000000 --- a/src/maths/vector.rs +++ /dev/null @@ -1,258 +0,0 @@ -use super::{Float, Matrix}; -use core::{fmt, ops}; - -#[macro_export] -macro_rules! row_vector { - ($($x:expr),*) => { - RowVector::new(vec![$($x),*]) - }; - ($($x:expr,)*) => { - RowVector::new(vec![$($x),*]) - }; -} - -#[macro_export] -macro_rules! column_vector { - ($($x:expr),*) => { - ColumnVector::new(vec![$($x),*]) - }; - ($($x:expr,)*) => { - ColumnVector::new(vec![$($x),*]) - }; -} - -pub trait Vector { - fn new(data: Vec) -> Self; - fn get(&self, index: usize) -> T; - fn set(&mut self, index: usize, value: T); - fn size(&self) -> usize; - - fn dot(&self, other: &Self) -> T; - fn norm(&self) -> T; - - fn max(&self) -> T; - fn min(&self) -> T; - fn sum(&self) -> T; - - fn from_matrix(matrix: &Matrix) -> Self; -} - -pub trait VectorMatrix { - fn to_matrix(&self) -> Matrix; -} - -#[derive(Clone)] -pub struct VectorImpl(Vec); -pub type RowVector = VectorImpl; -pub type ColumnVector = VectorImpl; - -impl ColumnVector { - pub fn mul_matrix(&self, matrix: &Matrix) -> Option> { - if matrix.cols != self.size() { - return None; - } - - let mut result = ColumnVector::new(vec![T::zero(); matrix.rows]); - - for i in 0..matrix.rows { - let mut sum = T::zero(); - for j in 0..matrix.cols { - sum += matrix.get(i, j) * self.get(j) ; - } - result.set(i, sum); - } - - Some(result) - } - - pub fn transpose(&self) -> RowVector { - RowVector::new(self.0.clone()) - } -} - -impl RowVector { - pub fn mul_matrix(&self, matrix: &Matrix) -> Option> { - if self.size() != matrix.rows { - return None; - } - - let mut result = RowVector::new(vec![T::zero(); matrix.cols]); - - for j in 0..matrix.cols { - let mut sum = T::zero(); - for i in 0..matrix.rows { - sum += self.get(i) * matrix.get(i, j) ; - } - result.set(j, sum); - } - - Some(result) - } - - pub fn transpose(&self) -> ColumnVector { - ColumnVector::new(self.0.clone()) - } -} - -impl VectorMatrix for RowVector { - fn to_matrix(&self) -> Matrix { - Matrix::new(1, self.size(), self.0.clone()) - } -} - -impl VectorMatrix for ColumnVector { - fn to_matrix(&self) -> Matrix { - Matrix::new(self.size(), 1, self.0.clone()) - } -} - -impl Vector for VectorImpl { - fn from_matrix(matrix: &Matrix) -> Self { - Self::new(matrix.data.clone()) - } - - fn new(data: Vec) -> Self { - Self(data) - } - - fn get(&self, index: usize) -> T { - self.0[index] - } - - fn set(&mut self, index: usize, value: T) { - self.0[index] = value; - } - - fn size(&self) -> usize { - self.0.len() - } - - fn dot(&self, other: &Self) -> T { - self.0 - .iter() - .zip(other.0.iter()) - .map(|(a, b)| *a * *b) - .fold(T::zero(), |acc, x| acc + x) - } - - fn norm(&self) -> T { - self.0 - .iter() - .map(|x| *x * *x) - .fold(T::zero(), |acc, x| acc + x) - .sqrt() - } - - fn max(&self) -> T { - *self - .0 - .iter() - .max_by(|a, b| a.partial_cmp(b).unwrap()) - .unwrap_or(&T::zero()) - } - - fn min(&self) -> T { - *self - .0 - .iter() - .min_by(|a, b| a.partial_cmp(b).unwrap()) - .unwrap_or(&T::zero()) - } - - fn sum(&self) -> T { - self.0.iter().fold(T::zero(), |acc, x| acc + *x) - } -} - -impl VectorImpl { - pub fn add_to(&self, other: &Self) -> Option> { - if self.size() != other.size() { - return None; - } - - let mut result = VectorImpl::new(vec![T::zero(); ROWS * COLS]); - - for i in 0..self.size() { - let sum = self.get(i) + other.get(i); - result.set(i, sum); - } - - Some(result) - } - - pub fn subtract(&self, other: &Self) -> Option> { - if self.size() != other.size() { - return None; - } - - let mut result = VectorImpl::new(vec![T::zero(); ROWS * COLS]); - - for i in 0..self.size() { - let sum = self.get(i) - other.get(i); - result.set(i, sum); - } - - Some(result) - } - - pub fn scale(&self, scalar: T) -> VectorImpl { - let mut result = VectorImpl::new(vec![T::zero(); ROWS * COLS]); - - for i in 0..self.size() { - let product = self.get(i) * scalar; - result.set(i, product); - } - - result - } -} - -impl ops::Index - for VectorImpl -{ - type Output = T; - - fn index(&self, index: usize) -> &Self::Output { - &self.0[index] - } -} - -impl ops::IndexMut - for VectorImpl -{ - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.0[index] - } -} - -impl fmt::Debug for RowVector { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "RowVector({:?})", self.0) - } -} - -impl fmt::Debug for ColumnVector { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ColumnVector({:?})", self.0) - } -} - -impl fmt::Display for RowVector { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "[{}]", - self.0 - .iter() - .map(|x| x.to_string()) - .collect::>() - .join(", ") - ) - } -} - -impl fmt::Display for ColumnVector { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.to_matrix()) - } -} diff --git a/src/psi.c b/src/psi.c index 34940fe..f8a659a 100644 --- a/src/psi.c +++ b/src/psi.c @@ -2,5 +2,5 @@ const char* psi_version(void) { - return "0.1.0"; + return "0.1.0"; } diff --git a/src/visualizer/grid.c b/src/visualizer/grid.c index 32ade1b..29d1981 100644 --- a/src/visualizer/grid.c +++ b/src/visualizer/grid.c @@ -6,129 +6,129 @@ 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; + 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; + 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, - }; + 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; + 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, ""); + if (sb->data == NULL) + psi_string_builder_append(sb, ""); - return sb->data; + return sb->data; } struct PsiGlyphRow psi_new_glyph_row(size_t width) { - char (*cells)[5] = malloc(width * sizeof(*cells)); - assert(cells != NULL || width == 0); + char (*cells)[5] = malloc(width * sizeof(*cells)); + assert(cells != NULL || width == 0); - for (size_t i = 0; i < width; i++) - memcpy(cells[i], " ", 2); + for (size_t i = 0; i < width; i++) + memcpy(cells[i], " ", 2); - return (struct PsiGlyphRow){ - cells, - width, - }; + return (struct PsiGlyphRow){ + cells, + width, + }; } void psi_free_glyph_row(struct PsiGlyphRow* row) { - free(row->cells); - row->cells = NULL; - row->width = 0; + 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; + 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 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; + 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); + 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]); + for (size_t i = 0; i < row.width; i++) + psi_string_builder_append(sb, row.cells[i]); - psi_string_builder_append(sb, "\n"); + psi_string_builder_append(sb, "\n"); } diff --git a/src/visualizer/grid.h b/src/visualizer/grid.h index 9353433..f02941a 100644 --- a/src/visualizer/grid.h +++ b/src/visualizer/grid.h @@ -4,9 +4,9 @@ struct PsiStringBuilder { - char* data; - size_t len; - size_t cap; + char* data; + size_t len; + size_t cap; }; struct PsiStringBuilder psi_new_string_builder(void); @@ -15,8 +15,8 @@ char* psi_string_builder_finish(struct PsiStringBuilder* sb); struct PsiGlyphRow { - char (*cells)[5]; - size_t width; + char (*cells)[5]; + size_t width; }; struct PsiGlyphRow psi_new_glyph_row(size_t width); diff --git a/src/visualizer/horizontal_cli.c b/src/visualizer/horizontal_cli.c index 56a2e00..9f7620e 100644 --- a/src/visualizer/horizontal_cli.c +++ b/src/visualizer/horizontal_cli.c @@ -10,344 +10,344 @@ static void append_repeat(struct PsiStringBuilder* sb, const char* glyph, size_t n) { - for (size_t i = 0; i < n; i++) - psi_string_builder_append(sb, glyph); + for (size_t i = 0; i < n; i++) + psi_string_builder_append(sb, glyph); } 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; - } + 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 void single_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; - default: snprintf(out, cap, "[?]"); break; - } + 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; + default: snprintf(out, cap, "[?]"); break; + } } static void controlled_label(struct PsiGateOp op, char* out, size_t cap) { - switch (op.kind) - { - case PSI_GATE_CRX: snprintf(out, cap, "[CRx(%.2f)]", op.params[0]); break; - case PSI_GATE_CRY: snprintf(out, cap, "[CRy(%.2f)]", op.params[0]); break; - case PSI_GATE_CRZ: snprintf(out, cap, "[CRz(%.2f)]", op.params[0]); break; - case PSI_GATE_CP: snprintf(out, cap, "[CP(%.2f)]", op.params[0]); break; - default: snprintf(out, cap, "[?]"); break; - } + switch (op.kind) + { + case PSI_GATE_CRX: snprintf(out, cap, "[CRx(%.2f)]", op.params[0]); break; + case PSI_GATE_CRY: snprintf(out, cap, "[CRy(%.2f)]", op.params[0]); break; + case PSI_GATE_CRZ: snprintf(out, cap, "[CRz(%.2f)]", op.params[0]); break; + case PSI_GATE_CP: snprintf(out, cap, "[CP(%.2f)]", op.params[0]); break; + default: snprintf(out, cap, "[?]"); break; + } } 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; - } + 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_horizontal(const struct PsiQuantumCircuit* circuit) { - size_t nq = circuit->num_qubits; - size_t nc = circuit->num_classical; + size_t nq = circuit->num_qubits; + size_t nc = circuit->num_classical; - struct PsiStringBuilder* q = nq > 0 ? malloc(nq * sizeof(struct PsiStringBuilder)) : NULL; - struct PsiStringBuilder* c = nc > 0 ? malloc(nc * sizeof(struct PsiStringBuilder)) : NULL; - assert(q != NULL || nq == 0); - assert(c != NULL || nc == 0); + struct PsiStringBuilder* q = nq > 0 ? malloc(nq * sizeof(struct PsiStringBuilder)) : NULL; + struct PsiStringBuilder* c = nc > 0 ? malloc(nc * sizeof(struct PsiStringBuilder)) : NULL; + assert(q != NULL || nq == 0); + assert(c != NULL || nc == 0); - size_t max_label = 3; - for (size_t i = 0; i < nq; i++) - { - char lbl[16]; - snprintf(lbl, sizeof lbl, "q%zu: ", i); - if (strlen(lbl) > max_label) - max_label = strlen(lbl); - } - for (size_t i = 0; i < nc; i++) - { - char lbl[16]; - snprintf(lbl, sizeof lbl, "c%zu: ", i); - if (strlen(lbl) > max_label) - max_label = strlen(lbl); - } + size_t max_label = 3; + for (size_t i = 0; i < nq; i++) + { + char lbl[16]; + snprintf(lbl, sizeof lbl, "q%zu: ", i); + if (strlen(lbl) > max_label) + max_label = strlen(lbl); + } + for (size_t i = 0; i < nc; i++) + { + char lbl[16]; + snprintf(lbl, sizeof lbl, "c%zu: ", i); + if (strlen(lbl) > max_label) + max_label = strlen(lbl); + } - for (size_t i = 0; i < nq; i++) - { - q[i] = psi_new_string_builder(); - char lbl[16]; - snprintf(lbl, sizeof lbl, "q%zu: ", i); - append_repeat(&q[i], " ", max_label - strlen(lbl)); - psi_string_builder_append(&q[i], lbl); - } - for (size_t i = 0; i < nc; i++) - { - c[i] = psi_new_string_builder(); - char lbl[16]; - snprintf(lbl, sizeof lbl, "c%zu: ", i); - append_repeat(&c[i], " ", max_label - strlen(lbl)); - psi_string_builder_append(&c[i], lbl); - } + for (size_t i = 0; i < nq; i++) + { + q[i] = psi_new_string_builder(); + char lbl[16]; + snprintf(lbl, sizeof lbl, "q%zu: ", i); + append_repeat(&q[i], " ", max_label - strlen(lbl)); + psi_string_builder_append(&q[i], lbl); + } + for (size_t i = 0; i < nc; i++) + { + c[i] = psi_new_string_builder(); + char lbl[16]; + snprintf(lbl, sizeof lbl, "c%zu: ", i); + append_repeat(&c[i], " ", max_label - strlen(lbl)); + psi_string_builder_append(&c[i], lbl); + } - struct PsiStringBuilder gap = psi_new_string_builder(); - append_repeat(&gap, " ", max_label); + struct PsiStringBuilder gap = psi_new_string_builder(); + append_repeat(&gap, " ", max_label); - if (circuit->operation_count == 0) - { - for (size_t i = 0; i < nq; i++) - psi_string_builder_append(&q[i], "───"); - for (size_t i = 0; i < nc; i++) - psi_string_builder_append(&c[i], "═══"); - psi_string_builder_append(&gap, " "); - } + if (circuit->operation_count == 0) + { + for (size_t i = 0; i < nq; i++) + psi_string_builder_append(&q[i], "───"); + for (size_t i = 0; i < nc; i++) + psi_string_builder_append(&c[i], "═══"); + psi_string_builder_append(&gap, " "); + } - for (size_t oi = 0; oi < circuit->operation_count; oi++) - { - struct PsiGateOp op = circuit->operations[oi]; + for (size_t oi = 0; oi < circuit->operation_count; oi++) + { + struct PsiGateOp op = circuit->operations[oi]; - size_t tc; - const size_t* targets = psi_gate_op_quantum_targets(&op, &tc); - size_t min_q = targets[0]; - size_t max_q = targets[0]; - for (size_t k = 1; k < tc; k++) - { - if (targets[k] < min_q) - min_q = targets[k]; - if (targets[k] > max_q) - max_q = targets[k]; - } + size_t tc; + const size_t* targets = psi_gate_op_quantum_targets(&op, &tc); + size_t min_q = targets[0]; + size_t max_q = targets[0]; + for (size_t k = 1; k < tc; k++) + { + if (targets[k] < min_q) + min_q = targets[k]; + if (targets[k] > max_q) + max_q = targets[k]; + } - if (is_single_target(op.kind)) - { - char label[64]; - single_label(op, label, sizeof label); - size_t w = psi_utf8_count(label); - size_t t = op.qubits[0]; + if (is_single_target(op.kind)) + { + char label[64]; + single_label(op, label, sizeof label); + size_t w = psi_utf8_count(label); + size_t t = op.qubits[0]; - for (size_t i = 0; i < nq; i++) - { - if (i == t) - { - psi_string_builder_append(&q[i], "─"); - psi_string_builder_append(&q[i], label); - psi_string_builder_append(&q[i], "─"); - } - else - append_repeat(&q[i], "─", w + 2); - } - for (size_t i = 0; i < nc; i++) - append_repeat(&c[i], "═", w + 2); - append_repeat(&gap, " ", w + 2); - } - else if (is_param_controlled(op.kind)) - { - char label[64]; - controlled_label(op, label, sizeof label); - size_t w = psi_utf8_count(label); - size_t control = op.qubits[0]; - size_t target = op.qubits[1]; + for (size_t i = 0; i < nq; i++) + { + if (i == t) + { + psi_string_builder_append(&q[i], "─"); + psi_string_builder_append(&q[i], label); + psi_string_builder_append(&q[i], "─"); + } + else + append_repeat(&q[i], "─", w + 2); + } + for (size_t i = 0; i < nc; i++) + append_repeat(&c[i], "═", w + 2); + append_repeat(&gap, " ", w + 2); + } + else if (is_param_controlled(op.kind)) + { + char label[64]; + controlled_label(op, label, sizeof label); + size_t w = psi_utf8_count(label); + size_t control = op.qubits[0]; + size_t target = op.qubits[1]; - for (size_t i = 0; i < nq; i++) - { - psi_string_builder_append(&q[i], "─"); - if (i == control) - { - psi_string_builder_append(&q[i], "●"); - append_repeat(&q[i], "─", w - 1); - } - else if (i == target) - psi_string_builder_append(&q[i], label); - else if (i > min_q && i < max_q) - { - psi_string_builder_append(&q[i], "│"); - append_repeat(&q[i], "─", w - 1); - } - else - append_repeat(&q[i], "─", w); - psi_string_builder_append(&q[i], "─"); - } - for (size_t i = 0; i < nc; i++) - append_repeat(&c[i], "═", w + 2); - append_repeat(&gap, " ", w + 2); - } - else if (op.kind == PSI_GATE_CNOT || op.kind == PSI_GATE_CZ || op.kind == PSI_GATE_SWAP || - op.kind == PSI_GATE_CCNOT || op.kind == PSI_GATE_CSWAP) - { - for (size_t i = 0; i < nq; i++) - { - const char* seg = "─────"; + for (size_t i = 0; i < nq; i++) + { + psi_string_builder_append(&q[i], "─"); + if (i == control) + { + psi_string_builder_append(&q[i], "●"); + append_repeat(&q[i], "─", w - 1); + } + else if (i == target) + psi_string_builder_append(&q[i], label); + else if (i > min_q && i < max_q) + { + psi_string_builder_append(&q[i], "│"); + append_repeat(&q[i], "─", w - 1); + } + else + append_repeat(&q[i], "─", w); + psi_string_builder_append(&q[i], "─"); + } + for (size_t i = 0; i < nc; i++) + append_repeat(&c[i], "═", w + 2); + append_repeat(&gap, " ", w + 2); + } + else if (op.kind == PSI_GATE_CNOT || op.kind == PSI_GATE_CZ || op.kind == PSI_GATE_SWAP || + op.kind == PSI_GATE_CCNOT || op.kind == PSI_GATE_CSWAP) + { + for (size_t i = 0; i < nq; i++) + { + const char* seg = "─────"; - switch (op.kind) - { - case PSI_GATE_CNOT: - if (i == op.qubits[0]) - seg = "──●──"; - else if (i == op.qubits[1]) - seg = "──⊕──"; - else if (i > min_q && i < max_q) - seg = "──│──"; - break; - case PSI_GATE_CZ: - if (i == op.qubits[0] || i == op.qubits[1]) - seg = "──●──"; - else if (i > min_q && i < max_q) - seg = "──│──"; - break; - case PSI_GATE_SWAP: - if (i == op.qubits[0] || i == op.qubits[1]) - seg = "──╳──"; - else if (i > min_q && i < max_q) - seg = "──│──"; - break; - case PSI_GATE_CCNOT: - if (i == op.qubits[0] || i == op.qubits[1]) - seg = "──●──"; - else if (i == op.qubits[2]) - seg = "──⊕──"; - else if (i > min_q && i < max_q) - seg = "──│──"; - break; - case PSI_GATE_CSWAP: - if (i == op.qubits[0]) - seg = "──●──"; - else if (i == op.qubits[1] || i == op.qubits[2]) - seg = "──╳──"; - else if (i > min_q && i < max_q) - seg = "──│──"; - break; - default: break; - } + switch (op.kind) + { + case PSI_GATE_CNOT: + if (i == op.qubits[0]) + seg = "──●──"; + else if (i == op.qubits[1]) + seg = "──⊕──"; + else if (i > min_q && i < max_q) + seg = "──│──"; + break; + case PSI_GATE_CZ: + if (i == op.qubits[0] || i == op.qubits[1]) + seg = "──●──"; + else if (i > min_q && i < max_q) + seg = "──│──"; + break; + case PSI_GATE_SWAP: + if (i == op.qubits[0] || i == op.qubits[1]) + seg = "──╳──"; + else if (i > min_q && i < max_q) + seg = "──│──"; + break; + case PSI_GATE_CCNOT: + if (i == op.qubits[0] || i == op.qubits[1]) + seg = "──●──"; + else if (i == op.qubits[2]) + seg = "──⊕──"; + else if (i > min_q && i < max_q) + seg = "──│──"; + break; + case PSI_GATE_CSWAP: + if (i == op.qubits[0]) + seg = "──●──"; + else if (i == op.qubits[1] || i == op.qubits[2]) + seg = "──╳──"; + else if (i > min_q && i < max_q) + seg = "──│──"; + break; + default: break; + } - psi_string_builder_append(&q[i], seg); - } - for (size_t i = 0; i < nc; i++) - psi_string_builder_append(&c[i], "═════"); - psi_string_builder_append(&gap, " "); - } - else if (op.kind == PSI_GATE_MEASURE) - { - size_t mq = op.qubits[0]; - size_t mc = op.classical; + psi_string_builder_append(&q[i], seg); + } + for (size_t i = 0; i < nc; i++) + psi_string_builder_append(&c[i], "═════"); + psi_string_builder_append(&gap, " "); + } + 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++) - { - if (i == mq) - psi_string_builder_append(&q[i], "─[M]─"); - else if (i > mq) - psi_string_builder_append(&q[i], "──║──"); - else - psi_string_builder_append(&q[i], "─────"); - } - for (size_t i = 0; i < nc; i++) - { - if (i == mc) - psi_string_builder_append(&c[i], "══╩══"); - else if (i < mc) - psi_string_builder_append(&c[i], "══║══"); - else - psi_string_builder_append(&c[i], "═════"); - } - psi_string_builder_append(&gap, " ║ "); - } - else - { - char label[64]; - snprintf(label, sizeof label, "[%s]", op.custom->name); - size_t w = psi_utf8_count(label); + for (size_t i = 0; i < nq; i++) + { + if (i == mq) + psi_string_builder_append(&q[i], "─[M]─"); + else if (i > mq) + psi_string_builder_append(&q[i], "──║──"); + else + psi_string_builder_append(&q[i], "─────"); + } + for (size_t i = 0; i < nc; i++) + { + if (i == mc) + psi_string_builder_append(&c[i], "══╩══"); + else if (i < mc) + psi_string_builder_append(&c[i], "══║══"); + else + psi_string_builder_append(&c[i], "═════"); + } + psi_string_builder_append(&gap, " ║ "); + } + else + { + char label[64]; + snprintf(label, sizeof label, "[%s]", op.custom->name); + size_t w = psi_utf8_count(label); - for (size_t i = 0; i < nq; i++) - { - bool is_target = false; - for (size_t k = 0; k < tc; k++) - if (targets[k] == i) - is_target = true; + for (size_t i = 0; i < nq; i++) + { + bool is_target = false; + for (size_t k = 0; k < tc; k++) + if (targets[k] == i) + is_target = true; - psi_string_builder_append(&q[i], "─"); - if (is_target && i == targets[0]) - psi_string_builder_append(&q[i], label); - else if (is_target) - append_repeat(&q[i], "─", w); - else if (i > min_q && i < max_q) - { - psi_string_builder_append(&q[i], "│"); - append_repeat(&q[i], "─", w - 1); - } - else - append_repeat(&q[i], "─", w); - psi_string_builder_append(&q[i], "─"); - } - for (size_t i = 0; i < nc; i++) - append_repeat(&c[i], "═", w + 2); - append_repeat(&gap, " ", w + 2); - } - } + psi_string_builder_append(&q[i], "─"); + if (is_target && i == targets[0]) + psi_string_builder_append(&q[i], label); + else if (is_target) + append_repeat(&q[i], "─", w); + else if (i > min_q && i < max_q) + { + psi_string_builder_append(&q[i], "│"); + append_repeat(&q[i], "─", w - 1); + } + else + append_repeat(&q[i], "─", w); + psi_string_builder_append(&q[i], "─"); + } + for (size_t i = 0; i < nc; i++) + append_repeat(&c[i], "═", w + 2); + append_repeat(&gap, " ", w + 2); + } + } - struct PsiStringBuilder out = psi_new_string_builder(); + struct PsiStringBuilder out = psi_new_string_builder(); - for (size_t i = 0; i < nq; i++) - { - psi_string_builder_append(&out, psi_string_builder_finish(&q[i])); - psi_string_builder_append(&out, "░\n"); - free(q[i].data); - } + for (size_t i = 0; i < nq; i++) + { + psi_string_builder_append(&out, psi_string_builder_finish(&q[i])); + psi_string_builder_append(&out, "░\n"); + free(q[i].data); + } - if (nc > 0) - { - psi_string_builder_append(&out, psi_string_builder_finish(&gap)); - psi_string_builder_append(&out, "░\n"); - for (size_t i = 0; i < nc; i++) - { - psi_string_builder_append(&out, psi_string_builder_finish(&c[i])); - psi_string_builder_append(&out, "░\n"); - free(c[i].data); - } - } + if (nc > 0) + { + psi_string_builder_append(&out, psi_string_builder_finish(&gap)); + psi_string_builder_append(&out, "░\n"); + for (size_t i = 0; i < nc; i++) + { + psi_string_builder_append(&out, psi_string_builder_finish(&c[i])); + psi_string_builder_append(&out, "░\n"); + free(c[i].data); + } + } - free(gap.data); - free(q); - free(c); + free(gap.data); + free(q); + free(c); - return psi_string_builder_finish(&out); + return psi_string_builder_finish(&out); } diff --git a/src/visualizer/vertical_cli.c b/src/visualizer/vertical_cli.c index 4f21e49..340a2a5 100644 --- a/src/visualizer/vertical_cli.c +++ b/src/visualizer/vertical_cli.c @@ -7,317 +7,317 @@ 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; - } + 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; + 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; - } + 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; - } + 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); + 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); } diff --git a/tester/clifford.c b/tester/clifford.c index 3be9234..4bed17e 100644 --- a/tester/clifford.c +++ b/tester/clifford.c @@ -4,72 +4,72 @@ static const double R2 = 0.7071067811865476; static void build_bell(struct PsiQuantumCircuit* c) { - psi_apply_h(c, 0); - psi_apply_cnot(c, 0, 1); + psi_apply_h(c, 0); + psi_apply_cnot(c, 0, 1); } static void build_ghz(struct PsiQuantumCircuit* c) { - psi_apply_h(c, 0); - psi_apply_cnot(c, 0, 1); - psi_apply_cnot(c, 0, 2); + psi_apply_h(c, 0); + psi_apply_cnot(c, 0, 1); + psi_apply_cnot(c, 0, 2); } void run_clifford_tests(void) { - psi_test_section("Clifford gates"); + psi_test_section("Clifford gates"); - struct PsiQuantumCircuit x = psi_new_quantum_circuit(1); - psi_apply_x(&x, 0); - struct PsiComplex x_exp[] = { psi_new_complex(0.0, 0.0), psi_new_complex(1.0, 0.0) }; - psi_check_circuit("X|0> = |1>", &x, x_exp, 2); - psi_free_quantum_circuit(&x); + struct PsiQuantumCircuit x = psi_new_quantum_circuit(1); + psi_apply_x(&x, 0); + struct PsiComplex x_exp[] = { psi_new_complex(0.0, 0.0), psi_new_complex(1.0, 0.0) }; + psi_check_circuit("X|0> = |1>", &x, x_exp, 2); + psi_free_quantum_circuit(&x); - struct PsiQuantumCircuit bell = psi_new_quantum_circuit(2); - build_bell(&bell); - struct PsiComplex bell_exp[] = { - psi_new_complex(R2, 0.0), - psi_new_complex(0.0, 0.0), - psi_new_complex(0.0, 0.0), - psi_new_complex(R2, 0.0), - }; - psi_check_circuit("Bell = (|00>+|11>)/sqrt2", &bell, bell_exp, 4); - psi_free_quantum_circuit(&bell); + struct PsiQuantumCircuit bell = psi_new_quantum_circuit(2); + build_bell(&bell); + struct PsiComplex bell_exp[] = { + psi_new_complex(R2, 0.0), + psi_new_complex(0.0, 0.0), + psi_new_complex(0.0, 0.0), + psi_new_complex(R2, 0.0), + }; + psi_check_circuit("Bell = (|00>+|11>)/sqrt2", &bell, bell_exp, 4); + psi_free_quantum_circuit(&bell); - struct PsiQuantumCircuit ghz = psi_new_quantum_circuit(3); - build_ghz(&ghz); - struct PsiComplex ghz_exp[] = { - psi_new_complex(R2, 0.0), psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), - psi_new_complex(0.0, 0.0), psi_new_complex(R2, 0.0), - }; - psi_check_circuit("GHZ = (|000>+|111>)/sqrt2", &ghz, ghz_exp, 8); - psi_free_quantum_circuit(&ghz); + struct PsiQuantumCircuit ghz = psi_new_quantum_circuit(3); + build_ghz(&ghz); + struct PsiComplex ghz_exp[] = { + psi_new_complex(R2, 0.0), psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 0.0), + psi_new_complex(0.0, 0.0), psi_new_complex(R2, 0.0), + }; + psi_check_circuit("GHZ = (|000>+|111>)/sqrt2", &ghz, ghz_exp, 8); + psi_free_quantum_circuit(&ghz); - struct PsiQuantumCircuit swap = psi_new_quantum_circuit(2); - psi_apply_x(&swap, 0); - psi_apply_cnot(&swap, 0, 1); - psi_apply_cnot(&swap, 1, 0); - psi_apply_cnot(&swap, 0, 1); - struct PsiComplex swap_exp[] = { - psi_new_complex(0.0, 0.0), - psi_new_complex(1.0, 0.0), - psi_new_complex(0.0, 0.0), - psi_new_complex(0.0, 0.0), - }; - psi_check_circuit("SWAP via 3 CNOTs: |10> -> |01>", &swap, swap_exp, 4); - psi_free_quantum_circuit(&swap); + struct PsiQuantumCircuit swap = psi_new_quantum_circuit(2); + psi_apply_x(&swap, 0); + psi_apply_cnot(&swap, 0, 1); + psi_apply_cnot(&swap, 1, 0); + psi_apply_cnot(&swap, 0, 1); + struct PsiComplex swap_exp[] = { + psi_new_complex(0.0, 0.0), + psi_new_complex(1.0, 0.0), + psi_new_complex(0.0, 0.0), + psi_new_complex(0.0, 0.0), + }; + psi_check_circuit("SWAP via 3 CNOTs: |10> -> |01>", &swap, swap_exp, 4); + psi_free_quantum_circuit(&swap); - struct PsiQuantumCircuit toffoli = psi_new_quantum_circuit(3); - psi_apply_x(&toffoli, 0); - psi_apply_x(&toffoli, 1); - psi_apply_ccnot(&toffoli, 0, 1, 2); - struct PsiComplex toffoli_exp[8]; - for (size_t i = 0; i < 8; i++) - toffoli_exp[i] = psi_new_complex(0.0, 0.0); - toffoli_exp[7] = psi_new_complex(1.0, 0.0); - psi_check_circuit("Toffoli: |110> -> |111>", &toffoli, toffoli_exp, 8); - psi_free_quantum_circuit(&toffoli); + struct PsiQuantumCircuit toffoli = psi_new_quantum_circuit(3); + psi_apply_x(&toffoli, 0); + psi_apply_x(&toffoli, 1); + psi_apply_ccnot(&toffoli, 0, 1, 2); + struct PsiComplex toffoli_exp[8]; + for (size_t i = 0; i < 8; i++) + toffoli_exp[i] = psi_new_complex(0.0, 0.0); + toffoli_exp[7] = psi_new_complex(1.0, 0.0); + psi_check_circuit("Toffoli: |110> -> |111>", &toffoli, toffoli_exp, 8); + psi_free_quantum_circuit(&toffoli); - psi_check_runtimes_agree("Runtimes agree on GHZ", 3, build_ghz); + psi_check_runtimes_agree("Runtimes agree on GHZ", 3, build_ghz); } diff --git a/tester/custom_gates.c b/tester/custom_gates.c index df2d9ae..8831e8c 100644 --- a/tester/custom_gates.c +++ b/tester/custom_gates.c @@ -4,33 +4,33 @@ static const double R2 = 0.7071067811865476; void run_custom_tests(void) { - psi_test_section("Custom gates"); + psi_test_section("Custom gates"); - struct PsiCompositeGateOp bell_ops[] = { - { PSI_OP_H, { 0 }, 1 }, - { PSI_OP_CNOT, { 0, 1 }, 2 }, - }; - struct PsiCustomGate bell = psi_new_custom_gate_from_composite("BELL", 2, bell_ops, 2); - struct PsiQuantumCircuit c = psi_new_quantum_circuit(2); - size_t bell_targets[] = { 0, 1 }; - psi_apply_custom(&c, bell, bell_targets, 2); - struct PsiComplex bell_exp[] = { - psi_new_complex(R2, 0.0), - psi_new_complex(0.0, 0.0), - psi_new_complex(0.0, 0.0), - psi_new_complex(R2, 0.0), - }; - psi_check_circuit("composite BELL gate -> Bell state", &c, bell_exp, 4); - psi_free_quantum_circuit(&c); + struct PsiCompositeGateOp bell_ops[] = { + { PSI_OP_H, { 0 }, 1 }, + { PSI_OP_CNOT, { 0, 1 }, 2 }, + }; + struct PsiCustomGate bell = psi_new_custom_gate_from_composite("BELL", 2, bell_ops, 2); + struct PsiQuantumCircuit c = psi_new_quantum_circuit(2); + size_t bell_targets[] = { 0, 1 }; + psi_apply_custom(&c, bell, bell_targets, 2); + struct PsiComplex bell_exp[] = { + psi_new_complex(R2, 0.0), + psi_new_complex(0.0, 0.0), + psi_new_complex(0.0, 0.0), + psi_new_complex(R2, 0.0), + }; + psi_check_circuit("composite BELL gate -> Bell state", &c, bell_exp, 4); + psi_free_quantum_circuit(&c); - struct PsiCustomGate custom_x = psi_new_custom_gate_from_matrix( - "MYX", - psi_matrix(2, 2, psi_new_complex(0.0, 0.0), psi_new_complex(1.0, 0.0), - psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0))); - struct PsiQuantumCircuit c2 = psi_new_quantum_circuit(1); - size_t x_targets[] = { 0 }; - psi_apply_custom(&c2, custom_x, x_targets, 1); - struct PsiComplex x_exp[] = { psi_new_complex(0.0, 0.0), psi_new_complex(1.0, 0.0) }; - psi_check_circuit("from_matrix X custom gate: |0> -> |1>", &c2, x_exp, 2); - psi_free_quantum_circuit(&c2); + struct PsiCustomGate custom_x = psi_new_custom_gate_from_matrix( + "MYX", + psi_matrix(2, 2, psi_new_complex(0.0, 0.0), psi_new_complex(1.0, 0.0), + psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0))); + struct PsiQuantumCircuit c2 = psi_new_quantum_circuit(1); + size_t x_targets[] = { 0 }; + psi_apply_custom(&c2, custom_x, x_targets, 1); + struct PsiComplex x_exp[] = { psi_new_complex(0.0, 0.0), psi_new_complex(1.0, 0.0) }; + psi_check_circuit("from_matrix X custom gate: |0> -> |1>", &c2, x_exp, 2); + psi_free_quantum_circuit(&c2); } diff --git a/tester/kernels.c b/tester/kernels.c index 91a5cba..f3e4e46 100644 --- a/tester/kernels.c +++ b/tester/kernels.c @@ -4,50 +4,50 @@ static const double R2 = 0.7071067811865476; static void build_fusion(struct PsiQuantumCircuit* c) { - psi_apply_h(c, 0); - psi_apply_z(c, 0); - psi_apply_h(c, 0); - psi_apply_x(c, 1); - psi_apply_x(c, 1); + psi_apply_h(c, 0); + psi_apply_z(c, 0); + psi_apply_h(c, 0); + psi_apply_x(c, 1); + psi_apply_x(c, 1); } void run_kernel_tests(void) { - psi_test_section("Kernel batching and fusion"); + psi_test_section("Kernel batching and fusion"); - struct PsiQuantumCircuit hh = psi_new_quantum_circuit(1); - psi_apply_h(&hh, 0); - psi_apply_h(&hh, 0); - struct PsiComplex hh_exp[] = { psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0) }; - psi_check_circuit("H then H = identity", &hh, hh_exp, 2); - psi_free_quantum_circuit(&hh); + struct PsiQuantumCircuit hh = psi_new_quantum_circuit(1); + psi_apply_h(&hh, 0); + psi_apply_h(&hh, 0); + struct PsiComplex hh_exp[] = { psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0) }; + psi_check_circuit("H then H = identity", &hh, hh_exp, 2); + psi_free_quantum_circuit(&hh); - struct PsiKernel a = - psi_new_kernel("H", - psi_matrix(2, 2, psi_new_complex(R2, 0.0), psi_new_complex(R2, 0.0), - psi_new_complex(R2, 0.0), psi_new_complex(-R2, 0.0)), - (size_t[]){ 0 }, 1); - struct PsiKernel b = psi_clone_kernel(a); - psi_test_check(psi_kernels_can_fuse(a, b), "adjacent single-qubit kernels fuse"); - psi_free_kernel(&a); - psi_free_kernel(&b); + struct PsiKernel a = + psi_new_kernel("H", + psi_matrix(2, 2, psi_new_complex(R2, 0.0), psi_new_complex(R2, 0.0), + psi_new_complex(R2, 0.0), psi_new_complex(-R2, 0.0)), + (size_t[]){ 0 }, 1); + struct PsiKernel b = psi_clone_kernel(a); + psi_test_check(psi_kernels_can_fuse(a, b), "adjacent single-qubit kernels fuse"); + psi_free_kernel(&a); + psi_free_kernel(&b); - struct PsiKernelBatch batch = psi_new_kernel_batch(1); - psi_add_kernel( - &batch, - psi_new_kernel("H", - psi_matrix(2, 2, psi_new_complex(R2, 0.0), psi_new_complex(R2, 0.0), - psi_new_complex(R2, 0.0), psi_new_complex(-R2, 0.0)), - (size_t[]){ 0 }, 1)); - psi_add_kernel( - &batch, - psi_new_kernel("H", - psi_matrix(2, 2, psi_new_complex(R2, 0.0), psi_new_complex(R2, 0.0), - psi_new_complex(R2, 0.0), psi_new_complex(-R2, 0.0)), - (size_t[]){ 0 }, 1)); - psi_optimize_kernel_batch(&batch); - psi_test_check(batch.count == 1, "batch fuses two H kernels into one"); - psi_free_kernel_batch(&batch); + struct PsiKernelBatch batch = psi_new_kernel_batch(1); + psi_add_kernel( + &batch, + psi_new_kernel("H", + psi_matrix(2, 2, psi_new_complex(R2, 0.0), psi_new_complex(R2, 0.0), + psi_new_complex(R2, 0.0), psi_new_complex(-R2, 0.0)), + (size_t[]){ 0 }, 1)); + psi_add_kernel( + &batch, + psi_new_kernel("H", + psi_matrix(2, 2, psi_new_complex(R2, 0.0), psi_new_complex(R2, 0.0), + psi_new_complex(R2, 0.0), psi_new_complex(-R2, 0.0)), + (size_t[]){ 0 }, 1)); + psi_optimize_kernel_batch(&batch); + psi_test_check(batch.count == 1, "batch fuses two H kernels into one"); + psi_free_kernel_batch(&batch); - psi_check_runtimes_agree("Runtimes agree on fusion circuit", 2, build_fusion); + psi_check_runtimes_agree("Runtimes agree on fusion circuit", 2, build_fusion); } diff --git a/tester/main.c b/tester/main.c index c516568..a17e8f5 100644 --- a/tester/main.c +++ b/tester/main.c @@ -5,37 +5,37 @@ static bool has_arg(int argc, char** argv, const char* name) { - for (int i = 1; i < argc; i++) - if (strcmp(argv[i], name) == 0) - return true; + for (int i = 1; i < argc; i++) + if (strcmp(argv[i], name) == 0) + return true; - return false; + return false; } int main(int argc, char** argv) { - if (has_arg(argc, argv, "help") || has_arg(argc, argv, "--help") || has_arg(argc, argv, "-h")) - { - printf("Usage: tester [clifford|non-clifford|custom|kernels|simd|noise|all]\n"); - return 0; - } - - bool all = argc < 2 || has_arg(argc, argv, "all"); - - printf("psi %s test suite\n", psi_version()); - - if (all || has_arg(argc, argv, "clifford")) - run_clifford_tests(); - if (all || has_arg(argc, argv, "non-clifford")) - run_non_clifford_tests(); - if (all || has_arg(argc, argv, "custom")) - run_custom_tests(); - if (all || has_arg(argc, argv, "kernels")) - run_kernel_tests(); - if (all || has_arg(argc, argv, "simd")) - run_simd_tests(); - if (all || has_arg(argc, argv, "noise")) - run_noise_tests(); - - return psi_test_summary(); + if (has_arg(argc, argv, "help") || has_arg(argc, argv, "--help") || has_arg(argc, argv, "-h")) + { + printf("Usage: tester [clifford|non-clifford|custom|kernels|simd|noise|all]\n"); + return 0; + } + + bool all = argc < 2 || has_arg(argc, argv, "all"); + + printf("psi %s test suite\n", psi_version()); + + if (all || has_arg(argc, argv, "clifford")) + run_clifford_tests(); + if (all || has_arg(argc, argv, "non-clifford")) + run_non_clifford_tests(); + if (all || has_arg(argc, argv, "custom")) + run_custom_tests(); + if (all || has_arg(argc, argv, "kernels")) + run_kernel_tests(); + if (all || has_arg(argc, argv, "simd")) + run_simd_tests(); + if (all || has_arg(argc, argv, "noise")) + run_noise_tests(); + + return psi_test_summary(); } diff --git a/tester/noise.c b/tester/noise.c index df791ae..184537c 100644 --- a/tester/noise.c +++ b/tester/noise.c @@ -4,46 +4,46 @@ void run_noise_tests(void) { - psi_test_section("Noise channels"); + psi_test_section("Noise channels"); - struct PsiDensityMatrix pure = psi_new_density_matrix(1); - psi_test_check(psi_is_pure_density_matrix(pure, 1e-10), "fresh density matrix is pure"); - psi_test_check(fabs(psi_trace_density_matrix(pure).real - 1.0) < 1e-10, "trace = 1"); - psi_free_density_matrix(&pure); + struct PsiDensityMatrix pure = psi_new_density_matrix(1); + psi_test_check(psi_is_pure_density_matrix(pure, 1e-10), "fresh density matrix is pure"); + psi_test_check(fabs(psi_trace_density_matrix(pure).real - 1.0) < 1e-10, "trace = 1"); + psi_free_density_matrix(&pure); - struct PsiDensityMatrix bf = psi_new_density_matrix(1); - struct PsiNoiseChannel flip = psi_bit_flip_channel(0.25); - psi_apply_noise_channel(&bf, flip, 0); - double probs[2]; - psi_density_matrix_probabilities(bf, probs); - psi_test_check(fabs(probs[0] - 0.75) < 1e-10 && fabs(probs[1] - 0.25) < 1e-10, - "bit_flip(0.25) on |0>: p = [0.75, 0.25]"); - psi_test_check(fabs(psi_purity_density_matrix(bf) - 0.625) < 1e-10, - "bit_flip(0.25) purity = 0.625"); - psi_free_noise_channel(&flip); - psi_free_density_matrix(&bf); + struct PsiDensityMatrix bf = psi_new_density_matrix(1); + struct PsiNoiseChannel flip = psi_bit_flip_channel(0.25); + psi_apply_noise_channel(&bf, flip, 0); + double probs[2]; + psi_density_matrix_probabilities(bf, probs); + psi_test_check(fabs(probs[0] - 0.75) < 1e-10 && fabs(probs[1] - 0.25) < 1e-10, + "bit_flip(0.25) on |0>: p = [0.75, 0.25]"); + psi_test_check(fabs(psi_purity_density_matrix(bf) - 0.625) < 1e-10, + "bit_flip(0.25) purity = 0.625"); + psi_free_noise_channel(&flip); + psi_free_density_matrix(&bf); - struct PsiComplex one[2] = { psi_new_complex(0.0, 0.0), psi_new_complex(1.0, 0.0) }; - struct PsiDensityMatrix damp = psi_new_density_matrix_from_state(one, 2); - struct PsiNoiseChannel ad = psi_amplitude_damping_channel(1.0); - psi_apply_noise_channel(&damp, ad, 0); - double dprobs[2]; - psi_density_matrix_probabilities(damp, dprobs); - psi_test_check(fabs(dprobs[0] - 1.0) < 1e-10, "amplitude_damping(1.0) fully decays |1> -> |0>"); - psi_free_noise_channel(&ad); - psi_free_density_matrix(&damp); + struct PsiComplex one[2] = { psi_new_complex(0.0, 0.0), psi_new_complex(1.0, 0.0) }; + struct PsiDensityMatrix damp = psi_new_density_matrix_from_state(one, 2); + struct PsiNoiseChannel ad = psi_amplitude_damping_channel(1.0); + psi_apply_noise_channel(&damp, ad, 0); + double dprobs[2]; + psi_density_matrix_probabilities(damp, dprobs); + psi_test_check(fabs(dprobs[0] - 1.0) < 1e-10, "amplitude_damping(1.0) fully decays |1> -> |0>"); + psi_free_noise_channel(&ad); + psi_free_density_matrix(&damp); - struct PsiComplex zero[2] = { psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0) }; - struct PsiDensityMatrix fid = psi_new_density_matrix_from_state(zero, 2); - psi_test_check(fabs(psi_fidelity_density_matrix(fid, zero) - 1.0) < 1e-10, - "fidelity(|0><0|, |0>) = 1"); - psi_free_density_matrix(&fid); + struct PsiComplex zero[2] = { psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0) }; + struct PsiDensityMatrix fid = psi_new_density_matrix_from_state(zero, 2); + psi_test_check(fabs(psi_fidelity_density_matrix(fid, zero) - 1.0) < 1e-10, + "fidelity(|0><0|, |0>) = 1"); + psi_free_density_matrix(&fid); - struct PsiDensityMatrix depo = psi_new_density_matrix(1); - struct PsiNoiseChannel dc = psi_depolarising_channel(0.3); - psi_apply_noise_channel(&depo, dc, 0); - psi_test_check(fabs(psi_trace_density_matrix(depo).real - 1.0) < 1e-10, - "depolarising preserves trace"); - psi_free_noise_channel(&dc); - psi_free_density_matrix(&depo); + struct PsiDensityMatrix depo = psi_new_density_matrix(1); + struct PsiNoiseChannel dc = psi_depolarising_channel(0.3); + psi_apply_noise_channel(&depo, dc, 0); + psi_test_check(fabs(psi_trace_density_matrix(depo).real - 1.0) < 1e-10, + "depolarising preserves trace"); + psi_free_noise_channel(&dc); + psi_free_density_matrix(&depo); } diff --git a/tester/non_clifford.c b/tester/non_clifford.c index c361fb5..e82c86d 100644 --- a/tester/non_clifford.c +++ b/tester/non_clifford.c @@ -5,38 +5,38 @@ static const double PI = 3.141592653589793; void run_non_clifford_tests(void) { - psi_test_section("Non-Clifford gates"); + psi_test_section("Non-Clifford gates"); - struct PsiQuantumCircuit t = psi_new_quantum_circuit(1); - psi_apply_x(&t, 0); - psi_apply_t(&t, 0); - struct PsiComplex t_exp[] = { psi_new_complex(0.0, 0.0), psi_new_complex(R2, R2) }; - psi_check_circuit("T|1> = e^{i pi/4}|1>", &t, t_exp, 2); - psi_free_quantum_circuit(&t); + struct PsiQuantumCircuit t = psi_new_quantum_circuit(1); + psi_apply_x(&t, 0); + psi_apply_t(&t, 0); + struct PsiComplex t_exp[] = { psi_new_complex(0.0, 0.0), psi_new_complex(R2, R2) }; + psi_check_circuit("T|1> = e^{i pi/4}|1>", &t, t_exp, 2); + psi_free_quantum_circuit(&t); - struct PsiQuantumCircuit rx = psi_new_quantum_circuit(1); - psi_apply_rx(&rx, 0, PI); - struct PsiComplex rx_exp[] = { psi_new_complex(0.0, 0.0), psi_new_complex(0.0, -1.0) }; - psi_check_circuit("Rx(pi)|0> = -i|1>", &rx, rx_exp, 2); - psi_free_quantum_circuit(&rx); + struct PsiQuantumCircuit rx = psi_new_quantum_circuit(1); + psi_apply_rx(&rx, 0, PI); + struct PsiComplex rx_exp[] = { psi_new_complex(0.0, 0.0), psi_new_complex(0.0, -1.0) }; + psi_check_circuit("Rx(pi)|0> = -i|1>", &rx, rx_exp, 2); + psi_free_quantum_circuit(&rx); - struct PsiQuantumCircuit ry = psi_new_quantum_circuit(1); - psi_apply_ry(&ry, 0, PI); - struct PsiComplex ry_exp[] = { psi_new_complex(0.0, 0.0), psi_new_complex(1.0, 0.0) }; - psi_check_circuit("Ry(pi)|0> = |1>", &ry, ry_exp, 2); - psi_free_quantum_circuit(&ry); + struct PsiQuantumCircuit ry = psi_new_quantum_circuit(1); + psi_apply_ry(&ry, 0, PI); + struct PsiComplex ry_exp[] = { psi_new_complex(0.0, 0.0), psi_new_complex(1.0, 0.0) }; + psi_check_circuit("Ry(pi)|0> = |1>", &ry, ry_exp, 2); + psi_free_quantum_circuit(&ry); - struct PsiQuantumCircuit p = psi_new_quantum_circuit(1); - psi_apply_x(&p, 0); - psi_apply_p(&p, 0, PI / 2.0); - struct PsiComplex p_exp[] = { psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 1.0) }; - psi_check_circuit("P(pi/2)|1> = i|1>", &p, p_exp, 2); - psi_free_quantum_circuit(&p); + struct PsiQuantumCircuit p = psi_new_quantum_circuit(1); + psi_apply_x(&p, 0); + psi_apply_p(&p, 0, PI / 2.0); + struct PsiComplex p_exp[] = { psi_new_complex(0.0, 0.0), psi_new_complex(0.0, 1.0) }; + psi_check_circuit("P(pi/2)|1> = i|1>", &p, p_exp, 2); + psi_free_quantum_circuit(&p); - struct PsiQuantumCircuit sx = psi_new_quantum_circuit(1); - psi_apply_sx(&sx, 0); - psi_apply_sx(&sx, 0); - struct PsiComplex sx_exp[] = { psi_new_complex(0.0, 0.0), psi_new_complex(1.0, 0.0) }; - psi_check_circuit("sqrt(X) applied twice = X", &sx, sx_exp, 2); - psi_free_quantum_circuit(&sx); + struct PsiQuantumCircuit sx = psi_new_quantum_circuit(1); + psi_apply_sx(&sx, 0); + psi_apply_sx(&sx, 0); + struct PsiComplex sx_exp[] = { psi_new_complex(0.0, 0.0), psi_new_complex(1.0, 0.0) }; + psi_check_circuit("sqrt(X) applied twice = X", &sx, sx_exp, 2); + psi_free_quantum_circuit(&sx); } diff --git a/tester/simd.c b/tester/simd.c index bea2f06..cfc38e2 100644 --- a/tester/simd.c +++ b/tester/simd.c @@ -6,58 +6,58 @@ static const double R2 = 0.7071067811865476; void run_simd_tests(void) { - psi_test_section("SIMD single-qubit kernels"); - - printf(" detected: %s\n", psi_simd_name(psi_detect_simd())); - - struct PsiComplex h[2][2] = { - { psi_new_complex(R2, 0.0), psi_new_complex(R2, 0.0) }, - { psi_new_complex(R2, 0.0), psi_new_complex(-R2, 0.0) }, - }; - - struct PsiComplex two[4] = { - psi_new_complex(1.0, 0.0), - psi_new_complex(0.0, 0.0), - psi_new_complex(0.0, 0.0), - psi_new_complex(0.0, 0.0), - }; - psi_apply_single_qubit_gate_simd(two, h, 0, 2); - struct PsiComplex two_exp[] = { - psi_new_complex(R2, 0.0), - psi_new_complex(0.0, 0.0), - psi_new_complex(R2, 0.0), - psi_new_complex(0.0, 0.0), - }; - psi_test_check(psi_amps_match(two, two_exp, 4), "SIMD H on q0 of |00>"); - - struct PsiComplex three[8]; - for (size_t i = 0; i < 8; i++) - three[i] = psi_new_complex(0.0, 0.0); - three[0] = psi_new_complex(1.0, 0.0); - psi_apply_single_qubit_gate_simd(three, h, 0, 3); - struct PsiComplex three_exp[8]; - for (size_t i = 0; i < 8; i++) - three_exp[i] = psi_new_complex(0.0, 0.0); - three_exp[0] = psi_new_complex(R2, 0.0); - three_exp[4] = psi_new_complex(R2, 0.0); - psi_test_check(psi_amps_match(three, three_exp, 8), "SIMD H on q0 of |000> (chunked)"); - - struct PsiComplex x[2][2] = { - { psi_new_complex(0.0, 0.0), psi_new_complex(1.0, 0.0) }, - { psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0) }, - }; - struct PsiComplex flip[4] = { - psi_new_complex(1.0, 0.0), - psi_new_complex(0.0, 0.0), - psi_new_complex(0.0, 0.0), - psi_new_complex(0.0, 0.0), - }; - psi_apply_single_qubit_gate_simd(flip, x, 1, 2); - struct PsiComplex flip_exp[] = { - psi_new_complex(0.0, 0.0), - psi_new_complex(1.0, 0.0), - psi_new_complex(0.0, 0.0), - psi_new_complex(0.0, 0.0), - }; - psi_test_check(psi_amps_match(flip, flip_exp, 4), "SIMD X on q1 of |00> = |01>"); + psi_test_section("SIMD single-qubit kernels"); + + printf(" detected: %s\n", psi_simd_name(psi_detect_simd())); + + struct PsiComplex h[2][2] = { + { psi_new_complex(R2, 0.0), psi_new_complex(R2, 0.0) }, + { psi_new_complex(R2, 0.0), psi_new_complex(-R2, 0.0) }, + }; + + struct PsiComplex two[4] = { + psi_new_complex(1.0, 0.0), + psi_new_complex(0.0, 0.0), + psi_new_complex(0.0, 0.0), + psi_new_complex(0.0, 0.0), + }; + psi_apply_single_qubit_gate_simd(two, h, 0, 2); + struct PsiComplex two_exp[] = { + psi_new_complex(R2, 0.0), + psi_new_complex(0.0, 0.0), + psi_new_complex(R2, 0.0), + psi_new_complex(0.0, 0.0), + }; + psi_test_check(psi_amps_match(two, two_exp, 4), "SIMD H on q0 of |00>"); + + struct PsiComplex three[8]; + for (size_t i = 0; i < 8; i++) + three[i] = psi_new_complex(0.0, 0.0); + three[0] = psi_new_complex(1.0, 0.0); + psi_apply_single_qubit_gate_simd(three, h, 0, 3); + struct PsiComplex three_exp[8]; + for (size_t i = 0; i < 8; i++) + three_exp[i] = psi_new_complex(0.0, 0.0); + three_exp[0] = psi_new_complex(R2, 0.0); + three_exp[4] = psi_new_complex(R2, 0.0); + psi_test_check(psi_amps_match(three, three_exp, 8), "SIMD H on q0 of |000> (chunked)"); + + struct PsiComplex x[2][2] = { + { psi_new_complex(0.0, 0.0), psi_new_complex(1.0, 0.0) }, + { psi_new_complex(1.0, 0.0), psi_new_complex(0.0, 0.0) }, + }; + struct PsiComplex flip[4] = { + psi_new_complex(1.0, 0.0), + psi_new_complex(0.0, 0.0), + psi_new_complex(0.0, 0.0), + psi_new_complex(0.0, 0.0), + }; + psi_apply_single_qubit_gate_simd(flip, x, 1, 2); + struct PsiComplex flip_exp[] = { + psi_new_complex(0.0, 0.0), + psi_new_complex(1.0, 0.0), + psi_new_complex(0.0, 0.0), + psi_new_complex(0.0, 0.0), + }; + psi_test_check(psi_amps_match(flip, flip_exp, 4), "SIMD X on q1 of |00> = |01>"); } diff --git a/tester/test.c b/tester/test.c index 9e11d8c..2c3532f 100644 --- a/tester/test.c +++ b/tester/test.c @@ -8,74 +8,74 @@ static int tests_passed = 0; void psi_test_section(const char* name) { - printf("\n── %s ──\n", name); + printf("\n── %s ──\n", name); } void psi_test_check(bool ok, const char* name) { - tests_run++; - if (ok) - tests_passed++; + tests_run++; + if (ok) + tests_passed++; - printf(" [%s] %s\n", ok ? "PASS" : "FAIL", name); + printf(" [%s] %s\n", ok ? "PASS" : "FAIL", name); } bool psi_amps_match(const struct PsiComplex* actual, const struct PsiComplex* expected, size_t n) { - for (size_t i = 0; i < n; i++) - { - if (fabs(actual[i].real - expected[i].real) > 1e-9) - return false; - if (fabs(actual[i].imaginary - expected[i].imaginary) > 1e-9) - return false; - } + for (size_t i = 0; i < n; i++) + { + if (fabs(actual[i].real - expected[i].real) > 1e-9) + return false; + if (fabs(actual[i].imaginary - expected[i].imaginary) > 1e-9) + return false; + } - return true; + return true; } void psi_check_circuit(const char* name, struct PsiQuantumCircuit* circuit, const struct PsiComplex* expected, size_t n) { - const struct PsiVector* state = psi_compute_circuit(circuit); - bool ok = state->size == n && psi_amps_match(state->data, expected, n); - psi_test_check(ok, name); + const struct PsiVector* state = psi_compute_circuit(circuit); + bool ok = state->size == n && psi_amps_match(state->data, expected, n); + psi_test_check(ok, name); } void psi_check_runtimes_agree(const char* name, size_t num_qubits, void (*build)(struct PsiQuantumCircuit*)) { - enum PsiRuntime runtimes[] = { - PSI_RUNTIME_BASIC, - PSI_RUNTIME_BATCHED, - PSI_RUNTIME_SIMD, - PSI_RUNTIME_STRUCTURE_AWARE, - }; + enum PsiRuntime runtimes[] = { + PSI_RUNTIME_BASIC, + PSI_RUNTIME_BATCHED, + PSI_RUNTIME_SIMD, + PSI_RUNTIME_STRUCTURE_AWARE, + }; - struct PsiQuantumCircuit base = psi_new_quantum_circuit(num_qubits); - build(&base); - const struct PsiVector* base_state = psi_compute_circuit_with(&base, PSI_RUNTIME_BASIC); - struct PsiVector reference = psi_clone_vector(*base_state); + struct PsiQuantumCircuit base = psi_new_quantum_circuit(num_qubits); + build(&base); + const struct PsiVector* base_state = psi_compute_circuit_with(&base, PSI_RUNTIME_BASIC); + struct PsiVector reference = psi_clone_vector(*base_state); - bool ok = true; - for (size_t i = 1; i < sizeof runtimes / sizeof runtimes[0]; i++) - { - struct PsiQuantumCircuit circuit = psi_new_quantum_circuit(num_qubits); - build(&circuit); - const struct PsiVector* state = psi_compute_circuit_with(&circuit, runtimes[i]); - if (state->size != reference.size || - !psi_amps_match(state->data, reference.data, reference.size)) - ok = false; + bool ok = true; + for (size_t i = 1; i < sizeof runtimes / sizeof runtimes[0]; i++) + { + struct PsiQuantumCircuit circuit = psi_new_quantum_circuit(num_qubits); + build(&circuit); + const struct PsiVector* state = psi_compute_circuit_with(&circuit, runtimes[i]); + if (state->size != reference.size || + !psi_amps_match(state->data, reference.data, reference.size)) + ok = false; - psi_free_quantum_circuit(&circuit); - } + psi_free_quantum_circuit(&circuit); + } - psi_free_vector(&reference); - psi_free_quantum_circuit(&base); - psi_test_check(ok, name); + psi_free_vector(&reference); + psi_free_quantum_circuit(&base); + psi_test_check(ok, name); } int psi_test_summary(void) { - printf("\n%d/%d checks passed\n", tests_passed, tests_run); - return tests_passed == tests_run ? 0 : 1; + printf("\n%d/%d checks passed\n", tests_passed, tests_run); + return tests_passed == tests_run ? 0 : 1; } -- cgit v1.3