ticit 0.1.2

Fast simulation of near-Clifford quantum circuits.
Documentation
# ticit — Rust port of SOFT/SymFT

## Goal

Port the SymFT reference implementation at `SOFT_ROOT` (exact C++/Python simulator
for noisy, adaptive Clifford-dominated quantum circuits, Stim-style input)
to pure idiomatic Rust, then out-perform the raw C++ version through
repeated profile → optimize cycles.

## Phasing (strict order)

1. **CPU port first.** Single-core, then multithreaded, then SIMD.
   Do not start GPU work until the Rust CPU version beats C++ on the
   benchmarks in `SOFT/benchmark/`.
   The CPU public API, CLI, and benchmarks expose batch sampling only;
   single-shot sampling remains an internal differential-validation hook.
2. **GPU port second.** CUDA backend, only after CPU goal is met.

## Reference source layout (C++)

- `SOFT/cpp/src/core/` — pauli, frames, symbolic Clifford–Pauli algebra
- `SOFT/cpp/src/circuit/` — circuit repr + lowering
- `SOFT/cpp/src/factored/` — planner, factored state, pending optimizer
- `SOFT/cpp/src/frontend/` — Stim parser, prepared sampler
- `SOFT/cpp/src/sampler/` — CPU sampling (single-core + multithreaded)
- `SOFT/cpp/src/simd/` — runtime-dispatched SIMD kernels
- `SOFT/cpp/src/cuda/` — CUDA backend
- `SOFT/benchmark/` — benchmark circuits, configs, methodology (ground truth
  for perf comparison)

## Benchmarking

Benchmark over a wide variety of circuits, never a narrow subset:

- All circuits in `SOFT/benchmark/circuit/`.
- All circuits in `testdata/circuits/ccz/`
  (`d05/d07/d09/d11` × `p0/p1e-3`, `.clifft` format).
- Fast profile/optimization loops may use CCZ d05/d07 and skip d09/d11;
  final validation and benchmark reporting still cover the full matrix.

Comparison baselines:

- **symft (SOFT):** `SOFT_ROOT`
- **clifft:** `CLIFFT_ROOT`
- CPU benchmarks report all three: ticit (Rust), symft, clifft.
- GPU benchmarks compare ticit vs symft (CUDA backend).

Known issues in SOFT's benchmark harness — do not reproduce them:

- [SOFT#8]https://github.com/haoliri0/SOFT/issues/8: harness mixes output
  conventions — Stim/Clifft paths reference-normalize detectors/observables
  against a noiseless reference, Tsim/SymFT use raw parity. Affects circuits
  with nonzero noiseless reference (`coherent_d3_r3`, `coherent_d5_r5`  postselection rejects different shots, throughput not comparable;
  `distillation`, `msc_d7` — logical classification differs). Use one
  convention for all tools (e.g. run clifft in raw-parity mode: omit
  `expected_detectors`/`expected_observables`).
- [SOFT#9]https://github.com/haoliri0/SOFT/issues/9: inconsistent
  logical-error metric — Stim/Tsim/SymFT count observable 0, Clifft path
  reports `logical_errors` (any observable set). Matters for `distillation`
  (5 observables). Use `observable_ones[0]` everywhere. Also note: Stim
  materializes full bit-packed detector output while Clifft/SymFT return
  aggregate counters — different output-contract work, caveat it in results.

## Toolchain choices

- Stable Rust only.
- CLI argument parsing: `clap` with derive macros.
- SIMD: `fearless_simd` (or comparable stable-toolchain SIMD crate).
- GPU: `cuda-oxide` or `cutile-rs` (NVlabs).
- CPU profiling: `perf` (see linux-perf skill).
- Tests: `cargo nextest run`; property tests vs reference with `quickcheck`;
  snapshots with `insta`.

## Machines

- **Local machine:** fast iteration and `perf` profiling are allowed.
- **CPU server:** `ssh riling` — benchmarking + perf profiling.
- **GPU server:** `ssh gpucluster` — multiple RTX 4090 + H200 via **slurm**.
- Sync local ↔ remote via git remotes (push/pull directly between repos),
  not rsync/scp.

## Agent roles

- **Claude Code:** the main agent uses Fable 5 as orchestrator, designer, and
  planner; Opus 5 subagents (`model: opus`) with high thinking effort write
  implementation code.
- **Codex:** the main agent uses its selected model for both coordination and
  implementation; spawning implementation subagents is not required.

## Working rules

- Correctness first: validate against C++ outputs / benchmark circuits
  before optimizing.
- Profile before optimizing; keep numbers from `perf` runs in the repo
  (e.g. `docs/impl-notes/perf/`) so regressions are visible.
- `docs/benchmark.md` carries the three-way benchmark tables (ticit / symft / clifft:
  throughput, compile time, peak RSS) stamped with the commit they were
  measured at. Whenever retained work moves any of those numbers meaningfully,
  re-measure and update the benchmark tables in the same session — stale
  results misrepresent the project to newcomers.
- In addition to algorithms and SIMD, test obvious release-build and hot-data
  layout improvements from the [Rust Performance Book]https://nnethercote.github.io/perf-book/title-page.html,
  retaining them only when representative benchmarks show a broad win.
- Do not use PGO for retained optimizations or benchmark baselines: crates.io
  packages installed with `cargo install` cannot ship the trained profile.
  Wins must come from source or ordinary Cargo/rustc build settings available
  to those users.
- Idiomatic Rust: `unsafe` is allowed only when profiling shows a measurable
  performance win. Keep the safe–unsafe boundary narrow, document and test its
  safety invariants, audit it carefully, and remove unsafe code that buys no
  measured improvement.