diff options
Diffstat (limited to 'STYLE.md')
| -rw-r--r-- | STYLE.md | 274 |
1 files changed, 0 insertions, 274 deletions
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_<operation>_<type>(...)`: - -```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 `<stdint.h>`** 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 <psi.h>` 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-<system>/tester # all suites -./bin/release-<system>/tester noise # one suite -``` - ---- - -## Tooling summary - -| Concern | Tool | Command | -|---------|------|---------| -| Formatting | clang-format | `... | xargs clang-format --dry-run -Werror` | -| Naming | clang-tidy | `clang-tidy <file> -- -std=c17 -Iinclude -Isrc` | -| Build | premake5 + make | `premake5 gmake && make config=release` | -| Tests | tester | `./bin/release-<system>/tester` | -| Leaks / UB | sanitizers | build with `-fsanitize=address,undefined` | |
