aboutsummaryrefslogtreecommitdiff
path: root/STYLE.md
diff options
context:
space:
mode:
Diffstat (limited to 'STYLE.md')
-rw-r--r--STYLE.md274
1 files changed, 274 insertions, 0 deletions
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_<operation>_<type>(...)`:
+
+```c
+psi_add_complex(a, b) /* not psi_complex_add */
+psi_new_quantum_gate(...) /* not gate_new(...) */
+psi_apply_gate(&reg, ...) /* 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` |