The solver layer, two seed solvers, and the CLI - #7
Open
roed-math wants to merge 1 commit into
Open
Conversation
Closed
roed-math
force-pushed
the
split/05-classify
branch
from
August 8, 2026 16:12
bb8a7b4 to
ebd9750
Compare
roed-math
force-pushed
the
split/06-solvers
branch
from
August 8, 2026 16:12
d02a9b4 to
f587dc2
Compare
roed-math
force-pushed
the
split/05-classify
branch
from
August 9, 2026 02:43
ebd9750 to
ee06c19
Compare
roed-math
force-pushed
the
split/06-solvers
branch
from
August 9, 2026 02:43
f587dc2 to
32e4147
Compare
roed-math
force-pushed
the
split/05-classify
branch
from
August 9, 2026 05:01
ee06c19 to
041cd3e
Compare
roed-math
force-pushed
the
split/06-solvers
branch
from
August 9, 2026 05:01
32e4147 to
c6f731d
Compare
roed-math
force-pushed
the
split/05-classify
branch
from
August 9, 2026 05:21
041cd3e to
174de58
Compare
roed-math
force-pushed
the
split/06-solvers
branch
from
August 9, 2026 05:21
c6f731d to
157c4f0
Compare
roed-math
force-pushed
the
split/05-classify
branch
from
August 9, 2026 08:23
174de58 to
72efc5a
Compare
roed-math
force-pushed
the
split/06-solvers
branch
from
August 9, 2026 08:23
157c4f0 to
f458a1f
Compare
The solver layer.
`SolutionSet` is the result type: a finite complete list, an empty set with a
reason, or an infinite family that streams (`iter(S)`, `S.first(n)`). When no
solver is wired up, `SolverUnavailable` carries the registry's software
pointers and filled code templates, so an equation page can always offer
runnable code.
**API notes:**
- A solver is `Callable[[Classification, Match], SolutionSet]` and works in its
family's standard coordinates. `solve()` dispatches over the matches that
were actually emitted, most specific first, handing each solver its own
match; it no longer walks `[slug] + lineage`, which could invoke an ancestor
solver with data belonging to another match.
- `SOLVER_DOMAINS` declares the domains each solver answers over. The integer
answer does not describe the rational solution set, so a `QQ` question is
declined rather than answered with it; `linear` gains a real `QQ` solver (a
complete affine parametrization, enumerated by rational height) and an `NN`
solver that is complete where it answers and declines otherwise.
- `_finalize_solution_set` wraps every solver: it pulls each tuple back through
the match's transform, orders it by the original equation's unknowns, and
checks the domain, the side conditions and the original equation exactly —
lazily, for infinite families. `solve("1/(x - 1) = 1/(y - 1)")` cannot emit
`(1, 1)`.
- `SolutionSet` validates its own kind on construction (an `infinite` set must
carry a stream, so iteration cannot stop at the stored witness) and owns its
serialization, `as_dict()`. Coordinates keep their exact values: the CLI used
to push every one through `int()`, turning the root `1/2` into `0`.
The two seed solvers (linear, univariate) are here; every other solver arrives
with its family, which is what makes those PRs independent. The module-level
enumeration bounds and the Sage imports are the union used by those solvers.
`cli.py` (`dioclassify 'x^2 - 61*y^2 = 1' --solve`) also lands here, since it
needs both halves: classification for `--json`, solving for `--solve`.
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
roed-math
force-pushed
the
split/05-classify
branch
from
August 9, 2026 20:35
72efc5a to
b90e5d6
Compare
roed-math
force-pushed
the
split/06-solvers
branch
from
August 9, 2026 20:35
f458a1f to
c686bc0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The solver layer.
SolutionSetis the result type: a finite complete list, an empty set with areason, or an infinite family that streams (
iter(S),S.first(n)). When nosolver is wired up,
SolverUnavailablecarries the registry's softwarepointers and filled code templates, so an equation page can always offer
runnable code.
API notes:
Callable[[Classification, Match], SolutionSet]and works in itsfamily's standard coordinates.
solve()dispatches over the matches thatwere actually emitted, most specific first, handing each solver its own
match; it no longer walks
[slug] + lineage, which could invoke an ancestorsolver with data belonging to another match.
SOLVER_DOMAINSdeclares the domains each solver answers over. The integeranswer does not describe the rational solution set, so a
QQquestion isdeclined rather than answered with it;
lineargains a realQQsolver (acomplete affine parametrization, enumerated by rational height) and an
NNsolver that is complete where it answers and declines otherwise.
_finalize_solution_setwraps every solver: it pulls each tuple back throughthe match's transform, orders it by the original equation's unknowns, and
checks the domain, the side conditions and the original equation exactly —
lazily, for infinite families.
solve("1/(x - 1) = 1/(y - 1)")cannot emit(1, 1).SolutionSetvalidates its own kind on construction (aninfiniteset mustcarry a stream, so iteration cannot stop at the stored witness) and owns its
serialization,
as_dict(). Coordinates keep their exact values: the CLI usedto push every one through
int(), turning the root1/2into0.The two seed solvers (linear, univariate) are here; every other solver arrives
with its family, which is what makes those PRs independent. The module-level
enumeration bounds and the Sage imports are the union used by those solvers.
cli.py(dioclassify 'x^2 - 61*y^2 = 1' --solve) also lands here, since itneeds both halves: classification for
--json, solving for--solve.Part of the series that splits #1 into reviewable pieces:
01-bibliography— packaging, docs, annotated bibliography02-parsing— equation strings to a term model03-registry— the YAML family registry (3 seed families)04-matchers— shape recognizers05-classify— the classification pipeline06-solvers— solver framework, two seed solvers, and the CLI07..09-backbone— the 23 parent families of the DAG, by depth99-polish— restore the full doctests and tighten the invariants