Skip to content

Give the cost model a name, so a caller can pick one instead of writing one (#746 v2.0) - #949

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
feat/cost-models
Aug 15, 2026
Merged

Give the cost model a name, so a caller can pick one instead of writing one (#746 v2.0)#949
Rafael-SOWNet merged 1 commit into
masterfrom
feat/cost-models

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Collaborator

#746 v2.0 asks for "a cost model that is comparable across domains and is data, so callers can supply their own (smallest tree, fewest radicals, numerically stablest, most readable to a student)".

Half of it already existed

MathS.Settings.ComplexityCriteria has always been a Setting<Func<Entity, double>>, and its own documentation carries a worked example of ranking by fewest divisions. Measured, it does exactly what it says:

a / b + b / c                 default
(a * c + b ^ 2) / (b * c)     ranking by fewest divisions

Worth stating plainly before adding anything, because the roadmap item reads as though none of it were built.

What was missing: a criterion could not be named

Two callers wanting the smallest tree each wrote the same lambda, neither could record which one produced an answer, and nothing could list the alternatives.

using var _ = MathS.Settings.ComplexityCriteria.Set(CostModel.FewestDivisions.Cost);

CostModel is a name, a description and the function, with Default, SmallestTree, FewestDivisions and FewestRadicals in All.

CostModel.Default is the function the setting already used — moved, not copied, with ComplexityCriteria now initialising from it. A copy would have passed every test here and drifted later, so the test asserts the rate is equal, not merely that the same candidate wins.

Every model counts nodes a little, even the ones about something else

A criterion counting only its own feature ties constantly, and a tie goes to whichever candidate the search generated first — an accident rather than a preference. That is the same trap #205 hit, where two forms of 1 / (sqrt(3) + 5) rated equal. The node term is small enough not to overturn the feature and large enough to order what the feature cannot separate.

Two things measured rather than assumed — both of which I had wrong first

  • The default prefers the rationalised form of 1 / (sqrt(3) + 5), 17 against 19. But a hand-written (sqrt(3) - 5) / (-22) costs 21, worse than either, because the negative denominator is itself weighted. The form to compare against is the one Simplify actually produces.
  • The models' disagreement is on a / b + b / c: default 24 against 33 for the combined form, fewest-divisions 2.007 against 1.011 the other way. The radical pair's node counts tie at 7, so that pair demonstrates a weighting difference, not a disagreement — it gets its own test saying so.

Measured

Suite 7225 passed / 0 failed, 16 new. No behaviour changes — the default is the same function and the suite's answers are unmoved. PublicApi.txt records the type; the new file gets a per-file header year in .editorconfig, following the convention documented there.

🤖 Generated with Claude Code

…ng one (#746 v2.0)

#746 v2.0 asks for "a cost model that is comparable across domains and is *data*, so callers can
supply their own (smallest tree, fewest radicals, numerically stablest, most readable to a student)".

Half of that already existed and is worth saying plainly before adding anything: ComplexityCriteria
has always been a Setting<Func<Entity, double>>, and its documentation carries a worked example of
ranking by fewest divisions. Measured, it does exactly what it says -- a / b + b / c stays split by
default and becomes (a * c + b ^ 2) / (b * c) under that criterion.

What was missing is that a criterion could not be *named*. Two callers wanting the smallest tree each
wrote the same lambda, neither could record which one produced an answer, and nothing could list the
alternatives. CostModel is that: a name, a description, and the function, with Default, SmallestTree,
FewestDivisions and FewestRadicals in All.

CostModel.Default is the function the setting already used, moved rather than copied, and
ComplexityCriteria now initialises from it. A copy would have passed every test here and drifted
later; the test asserts the rate is equal, not merely that the same candidate wins.

Every model counts nodes a little, even the ones that are about something else. A criterion counting
only its own feature ties constantly, and a tie goes to whichever candidate the search generated
first -- an accident rather than a preference, which is the same trap #205 hit when two forms of
1 / (sqrt(3) + 5) rated equal. The node term is small enough not to overturn the feature and large
enough to order what the feature cannot separate.

Two things measured rather than assumed, both of which I had wrong at first. The default prefers the
rationalised form of 1 / (sqrt(3) + 5) at 17 against 19 -- but a hand-written (sqrt(3) - 5) / (-22)
costs 21, worse than either, because the negative denominator is itself weighted; the form to compare
against is the one Simplify produces. And the models' disagreement is on a / b + b / c: default 24
against 33 for the combined form, fewest-divisions 2.007 against 1.011 the other way. The node counts
of the radical pair tie at 7, so that pair shows a weighting difference and not a disagreement.

No behaviour changes: the default is the same function, and the suite's answers are unmoved.

Measured: suite 7225 passed / 0 failed, 16 of them new. PublicApi.txt records the type.

#746

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant