{"data":{"kind":"file","path":"README.md","version_id":"ftymq3ieoimzo04z7wa4u8vf","entry":{"name":"README.md","path":"README.md","is_directory":false,"size":6977,"modified_at":"2026-08-07T06:30:03.677000","content_hash":"d9db791b825e0c0b5e8ef6fc5c48740e499ab123e1a362e460af8eb91539f67b"},"entries":[],"content":"# strang-env\r\n\r\nRL environment for **exact linear algebra** on the topics of *Introduction to Linear Algebra*\r\n(Strang): elimination and RREF, the four fundamental subspaces, determinants, LU and\r\nGram-Schmidt, eigenvalues and eigenvectors, projection and least squares.\r\n\r\n- **Tasks:** 35 across 6 categories\r\n- **Dataset:** [`strang-tasks-v1`](https://huggingface.co/datasets/eltociear/strang-tasks-v1)\r\n- **Reward:** binary, from an exact comparison. No LLM judge, no network, no clock.\r\n- **Dependencies in the sandbox:** none, and numpy is deliberately absent.\r\n\r\n```bash\r\nprime env install eltociear/strang-env\r\n```\r\n\r\n\r\n35 exact linear-algebra tasks for the\r\n[`strang-env`](https://app.primeintellect.ai/dashboard/environments/eltociear/strang-env) RL\r\nenvironment, on the topics of *Introduction to Linear Algebra* (Strang): elimination and RREF,\r\nthe four fundamental subspaces, determinants, LU and Gram-Schmidt, eigenvalues and\r\neigenvectors, projection and least squares.\r\n\r\n| field | meaning |\r\n|---|---|\r\n| `task_id` | `st-000` … `st-034` |\r\n| `category` | elimination / subspaces / determinants / factorisation / eigen / projection |\r\n| `prompt` | the question, the normalisation convention, and the exact shape of the answer |\r\n| `api_description` | the fixed integer matrices, plus the exact-arithmetic and normalisation rules |\r\n| `expected_output` | JSON `{\"rows\": [...]}`, **computed by executing a reference solution** |\r\n\r\nCategories: elimination 7, subspaces 6, determinants 5, factorisation 5, eigen 6, projection 6.\r\nNo dependencies — `fractions` and `itertools` are standard library, and **numpy is deliberately\r\nnot installed**.\r\n\r\n## Original matrices, not the book's exercises\r\n\r\nThe matrices are invented here. That matters more in this subject than most: the worked\r\nexamples in a famous linear algebra text are reproduced in thousands of lecture notes, so their\r\nRREFs and eigenvalues are memorisable. These are not.\r\n\r\n## The trap: most linear-algebra answers are not unique\r\n\r\n- A **basis** of the nullspace is not unique — any independent spanning set is correct. An\r\n  environment that asks for \"a basis\" and compares vectors marks correct work wrong.\r\n- An **eigenvector** is defined only up to scale, and up to any rotation inside an eigenspace\r\n  of dimension greater than one.\r\n- **LU** is not unique without saying which factor carries the unit diagonal.\r\n- **Gram-Schmidt** is unique only up to sign.\r\n\r\nWhat *is* unique: the RREF, rank, nullity, determinant, trace, the characteristic polynomial,\r\nthe projection matrix, and the least-squares solution of a full-rank system. This dataset asks\r\nfor those, and where it wants a non-unique object it **states the normalisation** — special\r\nsolutions with the free variable set to 1 and ordered by column index, eigenvectors scaled so\r\nthe first non-zero entry is 1, L with a unit diagonal.\r\n\r\nGram-Schmidt is deliberately *not* normalised to unit length: that would introduce square roots\r\nand leave nothing exact to grade. Scaling each vector by its first non-zero entry keeps\r\neverything rational **and** removes the sign ambiguity, which is the real obstacle to grading\r\nit.\r\n\r\n## No floats, anywhere\r\n\r\nEntries are integers or exact rationals as `[numerator, denominator]`; a vector of rationals is\r\nflattened, so `(1/2, 3)` is the row `[1, 2, 3, 1]`. This is not fastidiousness: floating-point\r\nelimination on a nearly-singular matrix is the classic way to compute a **confidently wrong\r\nrank** — a pivot that should be exactly zero comes out as `1e-17` and the rank is one too\r\nlarge. An environment that graded floats would be teaching precisely the wrong lesson, so a\r\nfloat never passes for an integer even when equal in value.\r\n\r\n## The helpers are cross-checked, not trusted\r\n\r\nBefore any answer key is built, the exact-arithmetic helpers are checked against independent\r\nidentities: `det(AB) = det(A)det(B)`, `det(Aᵀ) = det(A)`, `A·A⁻¹ = I`, `rank + nullity = n`, the\r\ncharacteristic polynomial's trace and determinant terms, and every special solution actually\r\nlying in the nullspace. Individual tasks assert more: that `L·U` reconstructs the matrix, that a\r\nclaimed eigenvector really satisfies `Cv = λv`, that the least-squares residual is orthogonal to\r\nboth columns, and that the projection matrix satisfies `P² = P = Pᵀ`.\r\n\r\nVerify with `python environments/strang_env/build_tasks.py --verify` (35/35).\r\nSource: <https://github.com/eltociear/my-molt-agent/tree/main/environments/strang_env>\r\n\r\n## Verify it yourself\r\n\r\n```bash\r\npython environments/strang_env/build_tasks.py --verify   # 35/35\r\n```\r\n\r\n## Environment arguments\r\n\r\n`load_environment()` deliberately exposes very little: the program's guidance is that an\r\nenvironment should have one correct way to be run, so nothing about the prompts, the parsing or\r\nthe grading is configurable.\r\n\r\n| Argument | Default | Meaning |\r\n|---|---|---|\r\n| `split` | `'train'` | Dataset split to load. |\r\n| `dataset_name` | `'eltociear/strang-tasks-v1'` | Hugging Face dataset of tasks. Change only to point at a fork. |\r\n| `max_turns` | `5` | Tool-use turns the model gets before the rollout ends. |\r\n| `**kwargs` | — | Passed through to the underlying `SandboxEnv`. |\r\n\r\n## Reward rubric\r\n\r\n| Reward function | Weight | What it returns |\r\n|---|---|---|\r\n| `correctness` | 1.0 | Binary: 1.0 when the answer matches the reference, else 0.0. |\r\n\r\nThere is no LLM judge and no partial credit. The score is computed on the host in\r\n`post_rollout` and read back by the rubric, so the reward is a deterministic function of the\r\nvalues the model left in `result`. A **harness** failure (dead sandbox, unreadable read-back)\r\nalso scores 0.0 but additionally sets `state[\"scoring_error\"]`, so an eval run can tell a\r\nbroken harness from a wrong answer instead of blaming the model.\r\n\r\n## Dependencies\r\n\r\n`datasets>=4.1.0`, `verifiers>=0.1.8`\r\n\r\n## Sample `vf-eval` usage\r\n\r\n```bash\r\nuv run vf-install strang-env\r\nuv run vf-eval -s strang-env -m gpt-4.1 -n 5 -r 3\r\nuv run vf-tui                      # inspect the outputs/ folder it writes\r\n```\r\n\r\n## Known limitation\r\n\r\nThe Docker **sandbox transport** has not been executed — `docker run`, `pip install`, and the\r\nmodel's code running inside the container — because that needs a Docker runtime and an\r\ninference provider key, neither available where this was authored.\r\n\r\nEverything else is exercised. `environments/test_scoring_path.py` runs this environment's\r\n`post_rollout` and `Rubric` with the sandbox mocked out, asserting that a correct answer scores\r\n1.0, a well-formed wrong answer scores 0.0, and a dead sandbox scores 0.0 *and* sets\r\n`scoring_error` so a harness failure is never mistaken for a bad model. It also checks that\r\nwhat `build_tasks.py` emits is exactly what the scorer expects. The environment mirrors the\r\nstructure of `polars_env` (already accepted into the Environments Program) and imports cleanly\r\nagainst `verifiers` 0.2.1.\r\n","encoding":"utf-8","truncated":false,"total_bytes":6977},"status":null}