THE FHERMA CLI

Scaffolding an implementation

An implementation answers a specification, and the skeleton for answering is generated from the same signature the testing bundle came from — so the two sides cannot disagree about a type.

fherma implementation init polynomial-multiplication/negacyclic@1.0.0
fherma implementation init polynomial-multiplication/negacyclic --lang cpp
fherma implementation init --signature ./sig.fhk --lang rust

--lang takes python, cpp or rust. Neither the C++ nor the Rust skeleton takes a dependency. For Python:

negacyclic-1.0.0-python/
├── fherma.py       generated — types and codec, derived from the signature
├── main.py         generated — the harness: reads cases, calls you, writes answers
├── solve.py        yours
├── fherma.toml     generated — spec, entry points, how it starts
└── README.md       generated

The three functions you write

init(p: Point)                  -> State     setup. Called once per point. Not measured.
run (state: State, inp: Inputs) -> Outputs   the answer. Measured, and only this.
free(state: State)                           optional.

init builds whatever depends on the parameters alone — tables, plans, device contexts. It receives no input data; the generated harness does not pass it any. run is timed from entry to exit with a monotonic clock; reads and writes happen in the harness, outside the window.

The timing code is in the generated main.py. Before building, the platform overwrites the generated files with its own copies, so local edits to them cannot affect how a submission is measured.

Run it against a bundle

The harness and the bundle speak the same on-disk protocol, so the whole chain runs on your machine:

python bundle/main.py make /tmp/p --point '{"N": 1024, "W": 2}' --seeds 0-7
python main.py /tmp/p                       # your solution answers every case
python bundle/main.py verify /tmp/p         # verdicts.json, one per case

An exception in one case is recorded as that case's status and the loop continues. out/results.json is rewritten after every case, so a process killed at its timeout still reports the completed cases.

Declaring what the code needs

Beside the code, an implementation declares its coverage — the region of the parameter space it claims — and its machine requirements: an image, a CUDA version, memory. Requirements determine both which runners can take it and which class its results are compared in.

Submitting

Push the directory to a repository and register the repository and commit on the implementation. The commit is what is measured; a branch reference would not identify the code. The platform plans points and seeds, a runner builds and measures, and results appear on the implementation's page. See Measurement and runners.