{"data":{"kind":"file","path":"README.md","version_id":"bikjonmxmr8i97o5wtcn6pbu","entry":{"name":"README.md","path":"README.md","is_directory":false,"size":4060,"modified_at":"2026-09-06T21:42:59.783000","content_hash":"f80c82661f2506d6a4a6d80a4712b8af7c85e85184d044286232624a8bfedc2f"},"entries":[],"content":"# gafitas-numpy\n\nWrite numpy and scipy. Running it on real arrays decides whether you got it\nright.\n\nFour single-turn tasks of rising difficulty. The model is given a spec and one\nempty module, and writes it. Grading runs the result on real arrays and checks\nit against numpy and scipy themselves. Nothing is string-matched, and no\nreference solution ships with the package.\n\n| task | | what it measures |\n|---|---|---|\n| `np01` | easy | broadcasting, normalisation that doesn't produce NaN on a zero row, per-row top-k |\n| `np02` | medium | least-squares fit, interpolation with clamped edges rather than NaN |\n| `np03` | medium | rolling windows, including window = 1 and window = len |\n| `np04` | hard | **vectorisation budget** — no Python-level iteration, at all |\n\n## Why np04 exists\n\nThe other three ask whether the answer is right. `np04` asks whether it was\nwritten the way someone who knows numpy writes it.\n\nA pairwise distance matrix built from two nested loops is **correct**. It is\nalso the commonest way numpy gets written by people who haven't internalised\nit, it is one to three orders of magnitude slower, and no test that only\nchecks the numbers will ever say so.\n\nSo the rule is stated in the objective and enforced on the submitted source:\nno `for`, no `while`, no comprehension, and none of the helpers that are loops\nin a numpy coat — `vectorize`, `apply_along_axis`, `fromiter`, `map`,\n`filter`.\n\nThat was measured before publishing, not assumed. A correct nested-loop\nsolution run through this grader passed every correctness test and failed\nexactly one: the loop check. So the environment carries a `correct_but_naive`\nsignal (weight 0) separating *wrong* from *right but written as C*.\n\n`np04` also carries a deliberate accuracy trap. The expansion\n`|a-b|² = |a|² - 2ab + |b|²` is the natural vectorised route and produces\nsmall negative values through floating-point error, which become NaN under a\nsquare root. The suite checks that a self-distance diagonal is exactly zero —\nso the fast route has to be taken *and* taken carefully.\n\n## What makes the reward mean something\n\n**The grader cannot be edited.** Every acceptance file is rewritten from the\nfrozen payload immediately before pytest runs. Verified per task: an empty\nmodule that *also* replaces the suite with trivially-passing tests scores 0.\n\n**No answer key ships.** References live only in the build script, and the\nemitter refuses to write a payload containing any distinctive line of its own\nreference — so a task cannot be published carrying its own solution even by\naccident.\n\n**A broken harness is not a failed solution.** `suite_usable` separates\n\"pytest ran and tests failed\" from \"pytest could not collect at all\".\n\n**The tasks start unsolved and are solvable.** Each was verified to FAIL with\nthe module empty and PASS with a reference, each control run twice.\n\n## Verified as published\n\nThe controls are checked against the *installed package*, loaded the way the\nhub loads it. That distinction is not pedantry — a sibling package once\nshipped without declaring `pytest`, so in a clean venv every task scored 0.0\n**including the reference**: a total silent failure indistinguishable from a\nmodel being bad at the task.\n\n```\nnp01 reference implemented=1.0   junk=0.0   empty=0.0\nnp02 reference implemented=1.0   junk=0.0   empty=0.0\nnp03 reference implemented=1.0   junk=0.0   empty=0.0\nnp04 reference implemented=1.0   junk=0.0   empty=0.0\nnp04 looping   implemented=0.0   correct_but_naive=1.0\n```\n\n## Safety\n\nThis environment **executes model-generated Python**. Grading writes into a\nfresh temporary directory and runs pytest there as a subprocess under a\ntimeout, writing nothing outside it — but untrusted code still runs. Sandbox\nit, as you would any code-execution environment.\n\n## Usage\n\n```python\nfrom gafitas_numpy import load_environment\n\nenv = load_environment()                      # all four\nenv = load_environment(tasks=(\"np04\",))       # just the vectorisation task\n```\n\nRequires numpy, scipy and pytest, all declared.\n\nMIT.\n","encoding":"utf-8","truncated":false,"total_bytes":4060},"status":null}