Skip to content

The extremum of an expression over a set, as a binder - #1222

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
extremum
Sep 8, 2026
Merged

Rafael-SOWNet merged 1 commit into
masterfrom
extremum

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Member

Part of #1212 — question I.6, and the extremum item of the plan there. The fourth of the agreed nodes, after divides (#1220) and card (#1221). Independent of both: cut from master, one mechanical conflict round on BREAKING-CHANGES.md and the public-API record with whichever lands first.

What it is

Four binders — Maximumf, Minimumf, Argmaxf, Argminf — reading max(f, t in S), min, argmax, argmin.

  • Grammar: max and min already took two values; when the second argument is variable in set (an Inf whose element is a variable) they now bind instead, and argmax/argmin are new and require that shape. max(a, b), max(x, y), max(x, 1 in S) all stay the two-value form. Printed the same words back; \max_{t \in S} f and \operatorname{argmax}_{t \in S} f in LaTeX; sympy.maximum(f, t, S) in the SymPy export (argmax has none there and declines). MathS.Maximum/Minimum/Argmax/Argmin in code. The variable is bound over the expression and the set.
  • The value (ExtremumOverSet) is answered over a finite set of numbers, by evaluating at each and comparing; and over a closed interval with numeric ends, by comparing the closed endpoints with the stationary points inside — the derivative's zeros, which the solver gives, with a periodic family c + p n enumerated where it lands in the interval.
  • Two guards, because a wrong maximum is worse than none. The expression has to be smooth on the reals (sums, products, whole non-negative powers, sines, cosines, exponentials with a positive base) so that every interior extremum is a stationary point — abs and 1/x are declined. And the best candidate is checked against the expression sampled along the interval: the solver's zero-list is not guaranteed complete, and a sample that beats the best candidate means one was missed, on which the question is left as written. A sample can miss a narrow peak, so this is a guard, not a proof — what it rules out is the confident wrong answer.
  • Left as written: an open endpoint (max(x, x in [0; 1)) has no maximum), a symbolic set or end, a pole, a kink, a set with a symbol in it, RR and the special sets.
  • Threaded through substitution (the variable is bound, renamed as a summation's index is), sort keys, JSON, FreeVariables (the variable is bound), the inverter (declined), the public-API record. Not buildable-as-a-pattern, like the other binders, so the node census is unchanged.

One thing the measurement caught: the stationary points are read by Vars, not FreeVariables, because FreeVariables counts the constants pi, e and i — which appear in the roots the solver writes (ln(...)/i for a trig zero) — and miscounting them as parameters of a periodic family sent every interval case down the wrong branch.

The cost

Measured on 27ed5b53: max(f(t), t in S) was the two-value max of f(t) and the boolean t in S, unevaluated; argmax(f, t in S) was a parse error. A Loud row in BREAKING-CHANGES.md, with the table on both builds.

Not in this PR

The two-argument functions still have their old behaviour for every shape but variable in set, so nothing that parsed before changes except that one shape. The ~/E/P distribution nodes and Sequence are the remaining agreed nodes.

Checks

ExtremumTest: the sheet's I.6, fourteen answered extrema over finite sets and intervals, seven point-sets, twelve left-as-written shapes, three round trips, LaTeX, the four code spellings, JSON, the bound variable, and the two-value max unchanged. The reflection-driven node tests pass with the four new nodes. Full suite in two chunks: 8395 and 1458 pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura

@Rafael-SOWNet
Rafael-SOWNet force-pushed the extremum branch 2 times, most recently from 4358bc4 to 73d4021 Compare September 8, 2026 16:00
An expression had no way to name its largest value over a set. max and
min took two values; max(f(t), t in S) parsed as the two-value max of
f(t) and the membership statement t in S, a category error that stayed
unevaluated, and argmax and argmin were not functions at all.

Maximumf, Minimumf, Argmaxf, Argminf read the second argument as
`variable in set` and bind the variable over the expression and the
set. Spelt max(f, t in S), min, argmax, argmin; printed \max_{t in S} f
and \operatorname{argmax}_{t in S} f; the two-value max(a, b) is
untouched, and a second argument that is not `variable in set` stays
the two-value form.

The value is answered (ExtremumOverSet) over a finite set of numbers,
by evaluating at each and comparing, and over a closed interval with
numeric ends for an expression whose extrema are all stationary -- sums,
products, whole non-negative powers, sines, cosines, exponentials with
a positive base -- by comparing the closed endpoints with the
derivative's zeros inside, a periodic family enumerated where it lands.
Two guards, because a wrong maximum is worse than none: the smoothness
check, so every interior extremum is a zero of the derivative; and the
best candidate checked against the expression sampled along the
interval, since the solver's list of zeros is not guaranteed complete
-- a sample that beats it leaves the question as written. An open
endpoint is not a candidate, so max(x, x in [0; 1)) has no maximum;
a symbolic set or end, a pole, a kink, and a set with a symbol in it
are left as written. argmax and argmin return the set of points.

The stationary points are read by Vars, not FreeVariables, so the
constants pi, e and i in a root the solver wrote are not miscounted as
parameters of a periodic family. Question I.6 of #1212 --
max(sin(t)^3 cos(t), t in [0; pi/2]) is 3 sqrt(3) / 16. BREAKING-CHANGES.md
has the rows, measured on both builds.

The fourth of the nodes agreed on #1212, after divides (#1220) and
card (#1221).

Part of #1212.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
@Rafael-SOWNet
Rafael-SOWNet merged commit 0bf049f into master Sep 8, 2026
32 checks passed
@Rafael-SOWNet
Rafael-SOWNet deleted the extremum branch September 8, 2026 16:56
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