From ee14ad272e68d9363202d7f668e0b20302827209 Mon Sep 17 00:00:00 2001 From: hachem Date: Mon, 14 Sep 2026 12:20:52 +0200 Subject: feat: simd + testing + formatting --- STYLE.md | 274 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 274 insertions(+) create mode 100644 STYLE.md (limited to 'STYLE.md') diff --git a/STYLE.md b/STYLE.md new file mode 100644 index 0000000..856287f --- /dev/null +++ b/STYLE.md @@ -0,0 +1,274 @@ +# 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` | -- cgit v1.3