Repository navigation
Speculative Combinatorics in CE
Speculative design. Open, not settled — thinking and proposals, not shipped behavior. See Roadmap for what's in flight.
Status: draft for review (2026-11-14). compute-engine's maintainer has agreed that the combinatorial collections, statistics and maps can live in compute-engine's repository as an optional package, beside Rubi and Fungrim (compute-engine#515). The proposal to him, with its seven questions, is compute-engine#531; it has no reply yet. Roadmap already gives the direction ("ours shrinks to the records and the glyphs") and Names the rule. This page is the part neither has: the split file by file, the order to do it in, and what changes on our side.
It assumes the verdict snapshot lands first (lane gxverd), so a build takes minutes and not hours. Nothing here repeats that work.
Names gives one test per side:
- compute-engine: would someone using compute-engine without us need it to evaluate?
- enumeratio: is it a fact about an object, or the address of one?
- notatio: is it about showing?
Applied to packages/symbols/combinatorics/combinatorics (@notatio/combinatorics), every
piece has one answer. Two pieces look like they could go either way, and §2.4 says why each
goes where it does.
About 5,260 tracked files: 4,619 under reference/ (the records) and about 640 of source,
scripts and tests. Paths below are relative to the package.
| Piece | Files today | Notes |
|---|---|---|
| Carrier and operation tables |
packages/structures/src/{carriers,operations,maps,recursion,restriction}.ts, and src/{carriers,carrier-list}.ts here |
A trimmed copy goes up, as #531 says. structures stays here for the libraries that have not moved (§5.1) |
Families: count, unrank, rank, valid as Epsil |
collections/src/families/ (epsil.ts, types.ts, and each area's src/families/) |
EpsilFamily objects; the definitions are MathJSON written in TypeScript, not .epsil text (question 4 of #531) |
| Fast kernels for families |
collections/src/families/{kernels*,exact-*,closed-forms,numeric-definitions}.ts, fast: true
|
Optional by rule: the Epsil is the meaning. They go with tests/fast-kernels.test.ts, which holds the two equal |
| Statistics |
<area>/src/statistics.ts in ten areas, src/statistics/{types,vocabulary,declare,core,all}.ts
|
Definition { head, on, expr, … }, one table entry per (carrier, name)
|
| Maps |
<area>/src/*maps.ts, src/{maps,map-helpers,laws,recursion,flat}.ts
|
CombinatorialMap { name, from, to, body, … } |
| The compile step |
scripts/{build-generated,compile-area,compile-maps,compile-families,family-checks,shards,verdicts,samples}.ts, src/statistics/generate-compiled.ts, src/compiled.ts
|
Compiles each definition to JavaScript and keeps it only where it agrees with the interpreter |
| The verdict snapshot | (lane gxverd) | With the compile step: §2.4 |
| FindStat identifiers | findstat/src/{findstat-data,findstat-maps-data}.ts |
The id-to-entry tables: CombinatorialStat(x, "St000018") needs them to evaluate. Identifiers and match records only, as #531 says |
| The tests of all of the above |
tests/, <area>/tests/, tests/readings/
|
The independent readings are the oracle each PR ships with (Contributing) |
The areas, by size of source: collections 1.2 MB, tableaux 1.26 MB, permutations 549 KB, partitions 460 KB, lattice paths 418 KB, trees 351 KB, set partitions 233 KB, words 220 KB, graphs 216 KB, compositions 118 KB, Cartan types 92 KB.
| Piece | Files today | Notes |
|---|---|---|
| The records |
reference/<Head>/: 2,107 index.md, 2,017 examples.tsv, about 475 examples.values.<system>.tsv (Wolfram 225, Sage 108, mathlib4 50, …) |
Never go upstream as records (§5.3) |
| FindStat's own data |
findstat/data/{statistics,maps}.json (1.1 MB: titles, descriptions, sample rows) |
A snapshot of someone else's database, with its own licence. Our crosswalk reads it; an evaluator does not |
| The FindStat lanes |
packages/reference/scripts/{find-findstat,find-findstat-maps,collect-findstat-*,findstat-incremental}.ts, findstat/scripts/check-findstat-maps.ts, set-partitions/scripts/check-findstat.ts
|
They decide which FindStat statistic each of ours is, by value. Their output (the id tables in §2.1) is sent upstream as data, regenerated by us |
| The other oracle lanes |
packages/oracle, packages/reference/scripts/oracle-*.ts, find-oeis.ts
|
Unchanged. They evaluate through whatever engine is loaded |
| The frontier lists |
<area>/src/statistics.frontier.ts, src/frontier-maps.ts
|
What the catalogue cannot do yet: a fact about our coverage, not a definition |
| Plausible, the census |
packages/plausible, packages/census; here collections/sampleable and src/maps-laws.generated.ts
|
The sampler stays. The laws themselves are §2.4 |
| Naming data |
src/statistics/naming-data.ts (built from the records) |
Record-derived, so it stays; the compile step must stop reading it (§4, step 1) |
Almost none of it is in the package now, which is the easy part of this split.
| Piece | Files today |
|---|---|
| Glyphs and frame layers | packages/frontend/src/{glyphs,figure-frames,path-frame,cell-frame,tree-frame,strand-frame,diagram-lowering}.ts |
| Tables and explorers |
packages/frontend/src/{collection-table,explorer,map-explorer,row-source,table-state}.ts, packages/components/src/notatio-collection-table.ts
|
| Terminal figures | packages/cli/src/figure.ts |
| The carriers' notation |
src/{notation,carrier-latex}.ts here (4 KB), the ./notation export |
docs/figure.md |
here; a presentation page filed with the math |
The notation is the one piece inside the package. Names says a library's notation is data
in that library, applied by notatio. Once the library is compute-engine's, that data has
nowhere to sit there (compute-engine is headless), so it stays in what is left of
@notatio/combinatorics (§5.1). docs/figure.md moves to packages/frontend/docs/.
The verdict snapshot goes with the math. A verdict says that one definition's compiled
JavaScript agrees with the interpreter, for a given checker. It is keyed by the definition, its
compiled code and the checking code (scripts/verdicts.ts, CHECKED_ON). It is not a fact
about a mathematical object; it is the proof that a build artefact is safe to ship, and
compute-engine needs it to build the fast path without a multi-hour sweep. So it moves with the
compile step, as committed data like fungrim-core-data.json, with a job on that side that
re-checks it (RECHECK_ALL=1 today). Until the compile step moves, it is committed here.
Map laws go up as tests, and stay here as Plausible's input. A law ({ inverse }, an
order isomorphism, a FindStat property) is a property of a definition, and upstream it is a
test of that definition. Plausible, which samples laws across every library we load, stays
ours and reads the laws from the package's public exports. One source, two readers.
collections/src/ also holds heads that are not collections: list-*.ts,
generating-functions.ts, infinite-products.ts, graphs*.ts, stats.ts. #531 already says
these are out. Each one goes the way Roadmap gives for its kind: a core pull request, a
patch in packages/ce-patches/, or it stays an enumeratio library. They need sorting before
collections/ can move, and that sort is its own piece of work (§4, step 4).
What the package imports from the workspace in shipped source (files, measured on main):
| Import | Files | What to do |
|---|---|---|
@notatio/engine |
62 | Mostly types (Engine, Expr) and operandsOf, integerAt, collectMessages; four use compileTyped from engine/compiled. Replace with compute-engine's public API. Whatever has no public equivalent is an ask (compute-engine#527) |
@notatio/structures |
50 | The carrier and operation tables. The trimmed copy that goes up replaces these |
@notatio/manifest |
5 | Build stamps and collect-declares. Stays behind: the upstream build uses compute-engine's own scripts |
@notatio/residues |
2 |
isPrime, factorInteger, nthPrime, primeCountUpTo. compute-engine has its own number theory; use it |
@notatio/entry |
2 | Record types in shipped code. Move those two reads to the catalogue side |
@notatio/ce-patches |
1 |
matrixRankCompiles. Lands upstream first, or that one use waits |
@notatio/boxes |
1 |
src/notation.ts only. Stays behind with the notation |
@notatio/groupalgebra |
0 shipped, 1 test |
extends lists it, but only a test imports it. Drop it from extends
|
Two more, outside the import list:
-
@notatio/statistics(probability distributions) and combinatorics read each other. Combinatorics' build reads../statistics/referencefor naming; statistics requires combinatorics. Neither read belongs in a package that leaves. -
Records are in one folder, definitions in ten.
reference/is not split by area, so "the records of the permutations area" is not a path today. The area is in each record's front matter, which is enough for an export (§5.3) and need not become a folder.
Each step leaves main working and can stop there.
Step 0. In flight. The verdict snapshot (gxverd). Answers on #531.
Step 1. Separate the three sides inside the package, without moving anything out. No compute-engine change, so it does not wait on #531.
- Lay the math out as compute-engine would hold it:
src/compute-engine/combinatorics/<area>/, aspackages/ce-patches/does for core. The pull request is then a copy. - Cut the seams in §3 for the first area. A test holds the layout to its allowed imports, as
tests/verdict-inputs.test.tsalready does for the checkers. - Give the catalogue side and the notation their own entry points, so
packages/reference, the site and the CLI can read records, FindStat data and notation without loading the math../findstatand./notationalready are; the frontier lists and naming data are not. - Make the compile step independent of the records (
naming-data.tsis built from them).
Step 2. The first slice upstream. #531 proposes the carrier machinery, permutations and integer partitions, with their statistics, maps and FindStat tables. A smaller slice would answer the same questions sooner:
The carrier tables and permutation statistics, interpreted only: no compiled kernels, no families, no maps.
It exercises the loader (loadCombinatorics), the public-API question, Epsil carried in a
package, the seam with core's Permutations and group-theory, FindStat ids, and our examples
as their tests. It leaves out the compile step and the verdicts, which are the parts most
likely to need his design call. Whether to send this or #531's slice is a question for Dean
(§6): #531 has already named the larger one.
Step 3. The switch on our side. declareCombinatorics declares an area from our tree only
when the loaded compute-engine does not provide it (loadCombinatorics returns a report). A
test fails when compute-engine ships an area we still declare, naming the files to delete:
the same contract as packages/ce-patches/tests/landed.test.ts, one level up (§5.2).
Step 4. The rest, one area per pull request. In #531's order: partitions' and permutations'
families and maps, then the compile step and the verdict snapshot (once, for the areas already
there), then set partitions, compositions, lattice paths, words, trees, tableaux, graphs,
Cartan types. collections/ goes last, after its non-collection heads are sorted (§2.5).
Step 5. Shrink. When the last area has landed and the pin has moved past it, what is left here is §5.1.
@notatio/combinatorics keeps its name and becomes the catalogue's side of the library:
-
reference/(the records),findstat/data/, the frontier lists, the notation data; -
declareCombinatorics, now a call to compute-engine's loader plus the notation; - no
build-generated, no compile steps, no verdicts. Its build iscollect-declaresandcollect-notation.
In its package.json:
-
enumeratio.layerstaysextension, since it is still what puts the heads into our engine and what the manifest reads them from. -
enumeratio.extendsdropsresiduesandgroupalgebra. It keepsstructuresonly while our own table is whatCombinatorialStatdispatches through (question A3). -
enumeratio.readsdrops../statistics/reference. -
generatedempties.
packages/manifest/tests/hierarchy.test.ts needs no new rule: the package imports less, not
differently. Names reserves @enumeratio/* for data packages and says there are none yet;
this would be the first candidate, and renaming it is a separate decision (question D5).
@notatio/structures stays. Residues, the algebras, braid and modular all build on it, and
they are not moving. The copy upstream and ours then drift unless one reads the other
(question A3).
packages/ce-patches/ is for a head or a fix that belongs in compute-engine's core: one
Patch per ask, applied at run time when fixed(ce) is false, deleted when landed.test.ts
says the release has it. It is laid out as core is.
The optional package is not core and does not go through ce-patches:
| A patch | An area of the package | |
|---|---|---|
| Unit | One head or fix | One area (carrier, statistics, maps) |
| Lives, before it lands | packages/ce-patches/src/patches/<slug>.ts |
Its area folder in @notatio/combinatorics, in compute-engine's layout |
| Applied |
applyPatches(ce), per patch |
declareCombinatorics(ce), per area, when the loader's report lacks it |
| Retired by | tests/landed.test.ts |
The same test, for areas (step 3) |
| After it lands | Deleted here | Deleted here; maintained in compute-engine's repository by us |
The difference that matters is the last row. A patch is finished when it lands. An area is not: new statistics and FindStat refreshes keep arriving, and after the move they are pull requests to compute-engine. A definition added there reaches our records only at the next release and pin bump (question D3).
What does go through ce-patches is what the package needs from core: matrixRankCompiles
today, and whatever #527 turns into.
AGENTS.md says a head's record lives in the package that declares it, and compute-engine's
own heads have theirs in packages/reference/entries/. Read literally, 2,107 records would
move there once compute-engine declares these heads. They should not: the rule becomes "an
optional compute-engine library's records live in the enumeratio package of the same name",
and the folder does not move.
What goes upstream from a record is a copy of some of its rows, one way:
- A script here exports, per area, the
examples.tsvrows a compute-engine user can run: the input, the expected value, and the pin (knownwith itssource) where there is one. No summaries, crosswalks, captions,boxesshapes orexamples.values.<system>.tsv. - compute-engine's tests read that file (question 6 of #531 asks where it sits). A freshness check on our side fails when the export is behind the records.
- The records remain the source. A fix to an expected value is made here and exported; nobody
edits the copy.
tests/known.test.tskeeps holdingknownrows on our side.
Our examples keep running against the installed compute-engine in the standard run, so every record is also a downstream test of the package.
tools/ce-downstream/run.ts is the slice of our tests compute-engine can run in its own CI.
Today it excludes @notatio/combinatorics and everything whose build reads it, by name,
because the cold build takes about 90 minutes.
- With the verdict snapshot, that reason goes, whether or not anything moves.
- After the move, the exclusion is deleted. The slice then covers the record examples for every area, evaluated through compute-engine's own package: the check that a core change did not break the library as its users see it. compute-engine's own tests of the package cover the definitions; ours cover the catalogue's claims.
-
ce-main.ymlneeds no change: the package ships in the same tarball as the engine. The switch in step 3 means amainthat has gained an area is picked up without an edit, and the retirement test reports it as news, alongsidelanded.test.tsand the version pin (NEWSinrun.ts). -
SMOKE_TESTSandsmoke-skip.json(23 files) go with the tests they skip.tools/ci/affected.tsloses its combinatorics special case.
These go through the CE upstream session, batched with #531, not posted from here. #531's own seven (name, size, core seam, Epsil sources, public API, test data, FindStat data) still stand.
- A1. Compiled kernels: built or committed? The compile step needs the interpreter cross-check. Committing the compiled JavaScript and the verdict snapshot keeps his build fast and puts the sweep in a nightly. Building them puts our build problem into his CI.
- A2. Who re-checks verdicts when core changes? A core change can invalidate a compiled kernel without failing a unit test. We propose a nightly on his side that we own.
-
A3. One carrier table or two. The package needs carriers and operations; our other
libraries use the same tables from
@notatio/structures. Would he take them as a public part of the package that our libraries then import, or prefer the package's copy private? - A4. Release cadence. After the move, a new statistic reaches users at his next release. Is a package-only change releasable on its own?
- D1. First slice. #531's (carriers, permutations and partitions in full) or the smaller one in step 2 (permutation statistics, interpreted only)?
- D2. Verdict snapshot's home. §2.4 sends it upstream with the compile step. The alternative keeps compiled kernels out of compute-engine altogether: the package ships interpreted Epsil, and the fast path stays a layer of ours. Smaller ask, slower for his users.
-
D3. The lag. Once an area is upstream, a new definition waits for a release before its
record can pass. Options: mark such examples
aspirationaluntil the bump; or allow definitions "ahead" in our tree under the step-3 switch, which keeps two homes alive. - D4. Step 1 before an answer on #531? It is useful either way (it is also what a per-package repository needs, Per-Package Repositories), but the layout it adopts guesses at question 1 of #531.
-
D5. The leftover package's name and scope. Keep
@notatio/combinatorics, or make it the first@enumeratio/*data package. - D6. FindStat data licence. #531 commits us to checking FindStat's terms before sending identifiers and match records. Not done yet.
- How many statistics and maps we define. The records (2,107 heads) and the FindStat ids
mentioned (1,997
St…, 336Mp…, of which 1,460 in the match tables) bound it; #531 says 2,100 heads and about 11k examples. - How much of the 62-file
@notatio/engineseam has a public equivalent in compute-engine. - The size of the first slice in compute-engine's bundle (question 2 of #531).
- The gxverd branch was not read: it is not on
originyet.