Skip to content

Shape matchers: recognizing families in a parsed equation - #5

Open
roed-math wants to merge 1 commit into
split/03-registryfrom
split/04-matchers
Open

Shape matchers: recognizing families in a parsed equation#5
roed-math wants to merge 1 commit into
split/03-registryfrom
split/04-matchers

Conversation

@roed-math

@roed-math roed-math commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

The shape matchers: matchers.run(parsed) returns Match(slug, data) records.

This is the file where a human eye is most useful on the mathematics: the
degree/genus tests, the reductions to standard forms, and the data each match
extracts (D for Pell, a-invariants for Weierstrass, the regime of a
generalized Fermat equation, ...). Matchers never mutate the parsed equation.

API note: Match.transform is a CoordinateTransform (transforms.py),
not a prose string. It holds both directions of the map between the user's
variables and the family's standard coordinates, push_forward/pull_back,
and the structural roles a family assigns — which have moved out of
Match.data["roles"], since a solver needs them together with the map that
consumes them. A string cannot transport a solution: 5*x^2 - y^2 = 1 is a
Pell equation only after reading the user's y as the standard x, and the
answer has to come back. Operations that change the equation but no
coordinate (multiplying through by -1) are recorded separately, in
.operations.

Two recognition fixes worth a look: a parameter in a coefficient is no longer
dropped (a Term keeps its parameter factors apart from coeff, so A*2^n
and 2^n shared coeff == 1 and Pillai matched both), and orienting
D*x^2 - y^2 = N as a Pell equation swaps the variables, which negates the
right-hand side too — without that, 5*x^2 - y^2 = 1 was matched as the
different equation x^2 - 5*y^2 = 1.

The recognizers cover 44 families, most of which are not registered yet. That
is deliberate and safe: a recognizer whose family has not landed is inert,
because the classifier (next PR) ranks matches through the registry and drops
slugs it does not know. tests/test_registry.py checks the flags of the
registered ones; 99-polish tightens that to every emitted slug once the
registry is complete.

Part of the series that splits #1 into reviewable pieces:

  1. 01-bibliography — packaging, docs, annotated bibliography
  2. 02-parsing — equation strings to a term model
  3. 03-registry — the YAML family registry (3 seed families)
  4. 04-matchers — shape recognizers
  5. 05-classify — the classification pipeline
  6. 06-solvers — solver framework, two seed solvers, and the CLI
  7. 07..09-backbone — the 23 parent families of the DAG, by depth
  8. one PR per remaining family (38 of them, mutually independent)
  9. 99-polish — restore the full doctests and tighten the invariants

The shape matchers: `matchers.run(parsed)` returns `Match(slug, data)` records.

This is the file where a human eye is most useful on the mathematics: the
degree/genus tests, the reductions to standard forms, and the data each match
extracts (`D` for Pell, a-invariants for Weierstrass, the regime of a
generalized Fermat equation, ...).  Matchers never mutate the parsed equation.

**API note:** `Match.transform` is a `CoordinateTransform` (`transforms.py`),
not a prose string.  It holds both directions of the map between the user's
variables and the family's standard coordinates, `push_forward`/`pull_back`,
and the structural roles a family assigns — which have moved out of
`Match.data["roles"]`, since a solver needs them together with the map that
consumes them.  A string cannot transport a solution: `5*x^2 - y^2 = 1` is a
Pell equation only after reading the user's `y` as the standard `x`, and the
answer has to come back.  Operations that change the equation but no
coordinate (multiplying through by `-1`) are recorded separately, in
`.operations`.

Two recognition fixes worth a look: a parameter in a coefficient is no longer
dropped (a `Term` keeps its parameter factors apart from `coeff`, so `A*2^n`
and `2^n` shared `coeff == 1` and Pillai matched both), and orienting
`D*x^2 - y^2 = N` as a Pell equation swaps the variables, which negates the
right-hand side too — without that, `5*x^2 - y^2 = 1` was matched as the
different equation `x^2 - 5*y^2 = 1`.

The recognizers cover 44 families, most of which are not registered yet.  That
is deliberate and safe: a recognizer whose family has not landed is inert,
because the classifier (next PR) ranks matches through the registry and drops
slugs it does not know.  `tests/test_registry.py` checks the flags of the
registered ones; `99-polish` tightens that to *every* emitted slug once the
registry is complete.

Part of the series that splits #1 into reviewable pieces:

1. `01-bibliography` — packaging, docs, annotated bibliography
2. `02-parsing` — equation strings to a term model
3. `03-registry` — the YAML family registry (3 seed families)
4. `04-matchers` — shape recognizers
5. `05-classify` — the classification pipeline
6. `06-solvers` — solver framework, two seed solvers, and the CLI
7. `07..09-backbone` — the 23 parent families of the DAG, by depth
8. one PR per remaining family (38 of them, mutually independent)
9. `99-polish` — restore the full doctests and tighten the invariants
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants