Skip to content

Commit eee360d

Browse files
committed
Improve inverse error function (erfInv) accuracy and performance
- Introduced Winitzki's approximation for the inverse error function as a seed for Newton's method, enhancing accuracy to ~2e-3 relative over (-1, 1). - Replaced the previous 6-term truncated Maclaurin series with a refined Newton's method approach, achieving full machine precision after 4 iterations. - Updated boundary checks for input values to handle edge cases more robustly. - Adjusted thresholds in fresnelS and fresnelC functions to improve numerical stability and accuracy for large inputs.
1 parent 70f6ba5 commit eee360d

66 files changed

Lines changed: 220807 additions & 29 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ CLAUDE.md
22
.claude/
33

44
doc/
5-
data/
65

76
build/
87
dist/

REVIEW.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,55 @@ were reproduced at runtime against the live engine, not just inferred from readi
169169

170170
---
171171

172+
## Known test failures (tracked for follow-up)
173+
174+
These suites fail on `main` **independently of this bug-fix initiative** — they
175+
were red before it started and none of the fixes above touch their code paths
176+
(verified by reverting suspect changes). They are catalogued here so they are
177+
not mistaken for regressions and can be investigated separately.
178+
179+
### Genuine, persistent
180+
181+
- **`collections.test.ts``Take`/`Drop`/`Slice` of a matrix (3 tests, all
182+
marked `// @fixme`).** Row-slicing a rank-2 list operates on the *flattened*
183+
element stream instead of whole rows. With
184+
`matrix = [[2,3,4],[6,7,9],[11,12,13]]`:
185+
- `Take(matrix, 1)``["List", 2]`; should be the first **row**
186+
`["List", 2, 3, 4]`.
187+
- `Slice(matrix, 2, 3)``["List", 6, 11]`; should be rows 2–3
188+
`[[6,7,9],[11,12,13]]`.
189+
- `Drop(matrix, 2)` similarly drops by element, not by row.
190+
191+
The committed inline snapshots are **stale** (e.g. `Take` expects
192+
`["List", 6]`, `Slice` expects `["List", 11, ["Error", "'missing'"]]`) — they
193+
match neither the current output nor the correct row-wise result, so the tests
194+
fail. **Do not** `-u` them: that would bake in the wrong output and hide the
195+
bug. Root cause is in the collection slice/take/drop handlers' indexing for
196+
indexed (rank≥2) collections (cf. the F3 `tensor.slice` off-by-one and the B4/
197+
B8 `Slice`/`Drop` `at` fixes — those addressed the rank-1 paths; the rank-2
198+
row semantics here are still wrong). **Follow-up:** make `Take`/`Drop`/`Slice`
199+
on a matrix select rows, then refresh the `@fixme` snapshots.
200+
201+
- **`ascii-math.test.ts``ARITHMETIC OPERATORS › should serialize Multiply`.**
202+
`check('(-2-3i) \\times -4')` expects `4(2 + 3i)` but yields `-4(-2 - 3i)`
203+
(both equal `8 + 12i`). The product of two negatives is not normalized so the
204+
leading sign is factored out; a canonicalization/serialization quality issue
205+
for `Multiply` of a negative real and a complex sum. Pre-existing and not
206+
touched by the fixes above. **Follow-up:** decide the canonical sign
207+
convention for `negativeReal · (complexSum)` and normalize.
208+
209+
### Flaky (not a correctness bug)
210+
211+
- **`compile-performance.test.ts`** can fail under parallel load (it asserts
212+
wall-clock timing budgets; it took ~25 s in a full `jest` run vs ~15 s in
213+
isolation). It passes when run alone. **Follow-up:** make the timing
214+
assertions load-tolerant (or mark them non-CI) so the suite is deterministic.
215+
216+
> Note: `rule-dispatch-regression.test.ts` was failing in earlier batches while
217+
> the in-tree Fungrim solve/rules WIP was uncommitted; it passes now.
218+
219+
---
220+
172221
## Executive Summary
173222

174223
The core engine (boxed-expression, canonicalization, arithmetic) is in good shape; most

data/fungrim/LICENSE

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# This data set is a translated derivative of Fungrim (https://fungrim.org); the upstream license follows verbatim.
2+
3+
MIT License
4+
5+
Copyright (c) 2019 Fredrik Johansson
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in all
15+
copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
SOFTWARE.

data/fungrim/MANIFEST.json

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
{
2+
"schemaVersion": 1,
3+
"generated": "2026-06-10",
4+
"generator": "grim2mathjson 0.1.0",
5+
"upstream": {
6+
"name": "fungrim",
7+
"snapshot": "/Users/arno/dev/fungrim-master (unversioned local snapshot)",
8+
"pin": {
9+
"sha256": "c7178f4f40cabc7dbc6b2b50da977520e8921806c0b65ea57690170cc499bebe",
10+
"method": "cd fungrim-master && find pygrim -name '*.py' -type f | LC_ALL=C sort | xargs cat | shasum -a 256",
11+
"fileCount": 70
12+
},
13+
"license": "MIT, Copyright (c) 2019 Fredrik Johansson (see LICENSE)"
14+
},
15+
"counts": {
16+
"totalEntries": 3130,
17+
"formulaEntries": 2764,
18+
"corpusEntries": 2551,
19+
"propertyEntries": 131,
20+
"skippedFormulaEntries": 82,
21+
"skippedRecords": 448,
22+
"symbolDefinitionEntries": 366,
23+
"shellDeclarations": 152,
24+
"existingCESymbols": 122,
25+
"topicFiles": 57
26+
},
27+
"classCounts": {
28+
"identity": 1226,
29+
"inequality": 242,
30+
"logical": 179,
31+
"other": 2,
32+
"representation": 459,
33+
"specific-value": 443
34+
},
35+
"guardLevelCounts": {
36+
"none": 681,
37+
"real-simple": 546,
38+
"complex-domain": 1283,
39+
"undischargeable": 41
40+
},
41+
"skipReasons": {
42+
"symbol-definition": 366,
43+
"tuple-indexing-set": 26,
44+
"where-def-tuple": 19,
45+
"formal-indeterminate": 13,
46+
"generator-list": 8,
47+
"repeat-splice": 6,
48+
"where-function-def": 4,
49+
"matrix-generator": 2,
50+
"where-recursive-def": 1,
51+
"ellipsis": 1,
52+
"step-splice": 1,
53+
"path-integral": 1
54+
},
55+
"files": {
56+
"corpus/<topic>.json": "57 per-topic files, 2551 annotated formula entries",
57+
"declarations.json": "152 symbol-shell declarations for heads CE does not define, plus an `existing` audit section",
58+
"properties.json": "131 analytic-property records (Poles, Zeros, BranchCuts, ...)",
59+
"skipped.json": "448 skip records (full ledger, incl. the 366 SymbolDefinition entries)",
60+
"LICENSE": "upstream MIT license (verbatim, with derivative-work header)",
61+
"README.md": "provenance, schema documentation, regeneration workflow"
62+
}
63+
}

data/fungrim/README.md

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# Fungrim Corpus for the Compute Engine
2+
3+
A machine-translated snapshot of the [Fungrim](https://fungrim.org) (Mathematical
4+
Functions Grimoire) formula collection, expressed as MathJSON, annotated for
5+
Compute Engine consumption. This is the Phase-0 deliverable of the Fungrim
6+
integration plan (`FUNGRIM-PLAN-1-TRANSLATOR.md` at the repository root).
7+
8+
## Provenance
9+
10+
- **Upstream:** Fungrim by Fredrik Johansson, MIT licensed (see `LICENSE`
11+
the upstream notice is reproduced verbatim; this data set is a *translated
12+
derivative*, not a copy, of the upstream Python sources).
13+
- **Snapshot pin:** the upstream is an unversioned local snapshot at
14+
`fungrim-master/`. `MANIFEST.json` records a content hash as the pin:
15+
SHA-256 over the concatenation of all `pygrim/**/*.py` files sorted by path
16+
(`find pygrim -name '*.py' -type f | LC_ALL=C sort | xargs cat | shasum -a 256`).
17+
If the fork is ever pushed to a git host, replace the hash with the commit id.
18+
- **Translator:** `grim2mathjson` (lives in the fungrim fork as a sibling
19+
package to `pygrim`). Every emitted file embeds a
20+
`"generator": "grim2mathjson <version>"` field.
21+
- **Determinism:** the translator emits stable key order, entries sorted by id
22+
within topic, and `\n` line endings — regeneration produces byte-identical
23+
files when nothing changed, so the diff is the review artifact.
24+
25+
## Files
26+
27+
| File | Contents |
28+
|---|---|
29+
| `MANIFEST.json` | schema version, generation date, upstream pin, counts |
30+
| `corpus/<topic>.json` | 57 per-topic files, 2,551 annotated formula entries |
31+
| `declarations.json` | 152 symbol-shell declarations for heads CE does not define |
32+
| `properties.json` | 131 analytic-property records (Poles, Zeros, BranchCuts, …) |
33+
| `skipped.json` | full skip ledger: 448 records with machine-readable reason codes |
34+
| `LICENSE` | upstream MIT license |
35+
36+
## Entry schema (`corpus/<topic>.json`)
37+
38+
Each topic file is `{ "topic", "title", "source", "generator", "entries": [...] }`.
39+
Each entry:
40+
41+
```json
42+
{
43+
"id": "d4b0b6", // Fungrim entry id (stable, 6 hex chars)
44+
"formula": ["Equal", ...], // non-canonical MathJSON (source form)
45+
"variables": ["z"], // free variables, from Expr.free_variables()
46+
"assumptions": ["Element", ...], // MathJSON; null when the entry has none
47+
"assumptionAlternatives": [...], // OPTIONAL (27 entries): alternative assumption sets, see below
48+
"class": "identity", // taxonomy, see below
49+
"subclass": null, // for class=representation: series|integral|product|limit
50+
"heads": ["Sin", "Arctan"], // named function heads (post-mapping), the rule-dispatch index
51+
"guardLevel": "complex-domain", // assumption-discharge difficulty, see below
52+
"flavor": null, // typed limit/derivative variant: real|complex|left|right|sequence|meromorphic
53+
"references": null, // upstream literature references, when present
54+
"topics": ["atan"], // all topics referencing the entry (first = defining module)
55+
"directedInfinity": true, // OPTIONAL (26 entries): formula contains c·∞; CE evaluates
56+
// Multiply(c, PositiveInfinity) to NaN — exclude from numeric checks
57+
"indexedFamilies": ["a_"] // OPTIONAL (5 entries): trailing-underscore family heads used
58+
// in call form ["a_", k] to preserve binder scoping
59+
}
60+
```
61+
62+
### `assumptionAlternatives`
63+
64+
Upstream `Assumptions(expr, alt_expr, ...)` with multiple args states
65+
**alternative** assumption sets — the formula holds under each set
66+
independently (pygrim renders args past the first as "Alternative
67+
assumptions"). They are NOT conjoined: 16 of the 27 affected entries would
68+
otherwise produce genuinely contradictory conjunctions (e.g. `sqrt/0d8e03`:
69+
`b ∈ (0,∞)` AND `b ∈ ℂ∖(−∞,0]`). The first set is the entry's primary
70+
`assumptions`; the remaining sets are emitted under the optional
71+
`assumptionAlternatives` array (same translation pipeline per alternative).
72+
`guardLevel` is computed from the primary set only.
73+
74+
### `class` taxonomy
75+
76+
- `specific-value``Equal` with zero free variables (e.g. ζ(2) = π²/6)
77+
- `identity``Equal` between closed forms with variables
78+
- `representation``Equal` whose side is a `Sum`/`Product`/`Integrate`/`Limit`
79+
(sub-tagged via `subclass`)
80+
- `inequality` — top head `Less`/`LessEqual`/`Greater`/`GreaterEqual`/`AsymptoticTo`-like
81+
- `logical` — top head `Implies`/`Equivalent`/`Element`/quantifier shapes
82+
- `other` — residual (2 entries)
83+
84+
### `guardLevel` semantics
85+
86+
Difficulty of discharging the entry's assumptions, the max over the flattened
87+
`And` conjuncts (`none` < `real-simple` < `complex-domain` < `undischargeable`):
88+
89+
- `none` — no assumptions
90+
- `real-simple` — memberships in integer/rational/real sets or intervals over
91+
bare variables, simple real inequalities, parity, divisibility
92+
- `complex-domain` — membership in ℂ/ℍ, predicates over `Re/Im/Abs/Arg`,
93+
complex set algebra (`SetMinus`, `Union`, …), and plain-symbol membership
94+
in inert shell sets (`DirichletGroup(q)`, `SL2Z`, lattices, symbolic sets,
95+
…) — dischargeable via CE stored-membership facts (Track 3; verified by
96+
the `scripts/fungrim/guard-census.ts` measurement)
97+
- `undischargeable` — quantifiers, `Where` in assumptions, holomorphy
98+
predicates, Riemann-hypothesis atoms, indexed-family memberships, and
99+
structural objects in *term* position (`Matrix(...) ∈ SL2Z`, lattice
100+
bounds inside `Infimum`, `DirichletCharacter` comparisons, …)
101+
102+
## `declarations.json`
103+
104+
`{ "generator", "declarations": { <name>: {...} }, "existing": { <name>: {...} } }`.
105+
Each declaration record carries `fungrimId`, `description`, `arity`
106+
(number or `[min, max]`), `signature` (CE type syntax, feeds `ce.declare()`
107+
directly), `signatureSource`/`signatureInferred`, `domainTable`, `wikidata`.
108+
The `existing` section audits heads CE already defines (not declared by the
109+
harness), including the **LambertW note**: CE's `LambertW` is 1-arg (principal
110+
branch) while the corpus emits 2-arg `["LambertW", z, k]` for non-principal
111+
branches — consumers must re-declare `LambertW: (complex, integer?) -> complex`
112+
in a child scope before boxing (the validation harness does this).
113+
114+
## `skipped.json` reason codes
115+
116+
Translation is total: every upstream entry lands in the corpus, in
117+
`properties.json`, or here. Codes:
118+
119+
| code | meaning |
120+
|---|---|
121+
| `symbol-definition` | entry defines a symbol (no `Formula`) — feeds `declarations.json` |
122+
| `tuple-indexing-set` | `ForElement(Tuple(...), S)` index — no CE encoding |
123+
| `where-def-tuple` | `Where` with `Def(Tuple(...), <non-literal>)` destructuring |
124+
| `where-function-def` | `Where` local function def that could not be beta-reduced |
125+
| `where-recursive-def` | `Where` with a self-referential function def |
126+
| `formal-indeterminate` | formal power-series indeterminates (`XX`, `SerX`, …) |
127+
| `generator-list` | list/tuple-generator syntax with no MathJSON encoding |
128+
| `matrix-generator` | matrix built from a generator expression |
129+
| `repeat-splice` / `step-splice` | `Repeat`/`Step` argument splices (Carlson topics) |
130+
| `ellipsis` | literal `Ellipsis`/`EqualQSeriesEllipsis` |
131+
| `path-integral` | integral over a path object |
132+
133+
## Validation
134+
135+
The CE-side harness lives at `scripts/fungrim/` (repository root):
136+
137+
```sh
138+
# Stage 1 (always): shell-declare, type-declare variables, box every entry
139+
npx tsx scripts/fungrim/validate.ts --corpus data/fungrim
140+
141+
# Stage 2: seeded numeric spot checks on the none/real-simple guard slice
142+
npx tsx scripts/fungrim/validate.ts --corpus data/fungrim --numeric --seed 42
143+
```
144+
145+
Reports are written to `scripts/fungrim/validation-report.json` (and
146+
`numeric-failures.json` for Stage-2 `False` instances, which are triage input,
147+
not build failures).
148+
149+
## Regeneration workflow
150+
151+
From a clean state:
152+
153+
```sh
154+
# 1. Re-run the translator over the pinned fungrim snapshot
155+
cd /Users/arno/dev/fungrim-master
156+
python3 -m grim2mathjson --strict --out grim2mathjson/out
157+
158+
# 2. Copy the artifacts into this directory
159+
cd /Users/arno/dev/compute-engine
160+
cp -R /Users/arno/dev/fungrim-master/grim2mathjson/out/corpus data/fungrim/corpus
161+
cp /Users/arno/dev/fungrim-master/grim2mathjson/out/{declarations,properties,skipped}.json data/fungrim/
162+
163+
# 3. Recompute the upstream pin and update MANIFEST.json if pygrim changed
164+
(cd /Users/arno/dev/fungrim-master && find pygrim -name '*.py' -type f | LC_ALL=C sort | xargs cat | shasum -a 256)
165+
166+
# 4. Validate
167+
npx tsx scripts/fungrim/validate.ts --corpus data/fungrim
168+
```
169+
170+
The output is deterministic; review the git diff of `data/fungrim/` as the
171+
change artifact. This directory is intentionally **not** part of the npm
172+
package (`package.json` `files` is `["/dist"]`); revisit as
173+
`@cortex-js/fungrim-data` when Phase 1 ships runtime loading.

0 commit comments

Comments
 (0)