Skip to content

The names of functions to come are refused by name - #2184

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
the-names-of-functions-to-come-are-refused
Oct 28, 2026
Merged

Rafael-SOWNet merged 1 commit into
masterfrom
the-names-of-functions-to-come-are-refused

Conversation

@Rafael-SOWNet

@Rafael-SOWNet Rafael-SOWNet commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

An unknown name followed by a bracket is read as a product, so zeta(2) was zeta * 2 and simplified to 2 * zeta, with nothing said. #2182 registers the functions the library is to have, named by what a mathematician calls them. Reworked with Happypig375 there, it is reserved here in two kinds.

Functions to come, refused until each is implemented, as trunc already is:

  • zeta, riemannzeta, hurwitzzeta. All three are reserved, since zeta(s) must not stay a product whichever name A register of function names, by what a mathematician calls each #2182 settles on.
  • lambertw and Mathematica's productlog.
  • mobius and moebiusmu, divisorsigma, primenu, primeomega, primepi.
  • harmonicnumber, catalannumber, multinomial.
  • fallingfactorial and factorialpower, risingfactorial.
  • forwarddifference, laplacian, jacobian, hessian.

Names that will not be functions, refused with the spelling to use, as NN is refused with ZZ+ and ZZ*:

Name Why The refusal says
heaviside its value at 0 is 0, 1/2 or 1 by source iverson(x > 0), iverson(x >= 0) or (1 + sgn(x))/2
unitstep iverson(x >= 0)
isprime n in PP is the statement n in PP
fractionalpart x mod 1
quotient floor(a / b)
doublefactorial, factorial2 the double factorial is to be the postfix n!! that, and that it is not implemented yet
pochhammer the rising factorial to some authors, the falling one to others risingfactorial or fallingfactorial

Each name without a bracket is still a variable. Short names that could be somebody's variable before a bracket are not reserved: beta, norm, grad, div, curl. Docs/Usage/Syntax.md's "Refused by name" paragraph lists both kinds, and so does the MCP server's syntax document. The parser is regenerated.

Input Was (2.6.0) Now
zeta(s) zeta * s throws UnrecognizedFunctionParseException
zeta(2), simplified 2 * zeta throws UnrecognizedFunctionParseException
isprime(7) isprime * 7 throws, with n in PP
heaviside(x) heaviside * x throws, with the three spellings
divisorsigma(k, n) throws UnhandledParseException throws UnrecognizedFunctionParseException

Measured, on the branch before its rebase onto #2186's merge, which shares only BREAKING-CHANGES.md with it, at a different entry, and none of its code:

  • Suite: 17,397 passed, 0 failed, 13 skipped. McpUnitTests 419 passed.
  • Intbench chain against master d44599f: identical on every family, 0 moves. Performance gate passed on all 19 gated benchmarks.
  • Harnesses: BoundCheck 1208 shapes with 0 disagreements, RootCheck 596/596, CasBench 120/121 with 0 wrong, SimpSweep 10463/10463, CrashCheck 0 crashed, PropCheck and RuleCheck clean, CanonCheck (2091) and Confluence (3) matching their baselines.

Part of #2182.

🤖 Generated with Claude Code

https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura

@Rafael-SOWNet Rafael-SOWNet moved this from Todo to In review in AngouriMath Oct 28, 2026
@codecov

codecov Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.74468% with 30 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.53%. Comparing base (059513d) to head (2af815c).

Files with missing lines Patch % Lines
...ources/AngouriMath/Core/Antlr/AngouriMathParser.cs 95.74% 29 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2184      +/-   ##
==========================================
+ Coverage   86.47%   86.53%   +0.06%     
==========================================
  Files         297      297              
  Lines       65603    66096     +493     
  Branches    19227    19227              
==========================================
+ Hits        56730    57197     +467     
- Misses       4810     4836      +26     
  Partials     4063     4063              
Flag Coverage Δ
cpp 37.77% <ø> (ø)
csharp 87.09% <95.74%> (+0.05%) ⬆️
fsharp 32.97% <ø> (ø)
interactive 40.72% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
Sources/AngouriMath/Core/Antlr/AngouriMathLexer.cs 99.20% <ø> (+0.17%) ⬆️
...ources/AngouriMath/Core/Antlr/AngouriMathParser.cs 87.28% <95.74%> (+0.39%) ⬆️

... and 6 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Rafael-SOWNet

Copy link
Copy Markdown
Member Author

Back to draft: the names it reserves wait for #2182's register to settle, which Happypig375 rightly found arbitrary. It will be redone from the settled table.

🤖 Generated with Claude Code

https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura

An unknown name followed by a bracket is read as a product, so zeta(2) was zeta * 2 and
simplified to 2 * zeta, silently. #2182 registers the functions the library is to have, named
by what a mathematician calls them, and their long names are now refused until each is
implemented, as trunc is: zeta, riemannzeta and hurwitzzeta, lambertw and productlog, mobius
and moebiusmu, divisorsigma, primenu, primeomega, primepi, harmonicnumber, catalannumber,
multinomial, fallingfactorial and factorialpower, risingfactorial, forwarddifference, laplacian,
jacobian and hessian.

Names that will not be functions are refused with the spelling to use, as NN is with ZZ+ and
ZZ*. heaviside's value at 0 is 0, 1/2 or 1 by source, so it points to iverson(x > 0),
iverson(x >= 0) and (1 + sgn(x))/2, and unitstep to iverson(x >= 0). isprime(n) is n in PP,
fractionalpart(x) is x mod 1, quotient(a, b) is floor(a / b), the double factorial is to be the
postfix n!!, and pochhammer is the rising factorial to some authors and the falling one to
others. Each name without a bracket is still a variable, and short names such as beta and norm
are still read as products.

Part of #2182.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
@Rafael-SOWNet
Rafael-SOWNet force-pushed the the-names-of-functions-to-come-are-refused branch from 97243eb to 2af815c Compare October 28, 2026 18:14
@Rafael-SOWNet
Rafael-SOWNet marked this pull request as ready for review October 28, 2026 18:14
@Rafael-SOWNet
Rafael-SOWNet merged commit 3dbb55b into master Oct 28, 2026
36 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in AngouriMath Oct 28, 2026
@Rafael-SOWNet
Rafael-SOWNet deleted the the-names-of-functions-to-come-are-refused branch October 28, 2026 19:21
Rafael-SOWNet added a commit that referenced this pull request Oct 28, 2026
laplacian, jacobian and hessian were reserved by name in #2184 and are now computed when they are
parsed, as det is. Each is made of derivative nodes and needs no node of its own.

laplacian(f, [x, y, z]) is the sum of the second derivatives of a scalar f. jacobian(F, [x, y]) is
the matrix of the first derivatives of F's components, with a row for each component and a column
for each variable; a scalar F is one row. hessian(f, [x, y]) is the matrix of the second
derivatives of a scalar f, the entry in row i and column j being the derivative in x_i of the
derivative in x_j, as the definition writes it.

The variables are a vector of distinct names written out, or a single name. A set is refused,
having no order to give the columns, and so are a constant, a repeated name, and a vector or matrix
as the function of a Laplacian or a Hessian. det(jacobian([r cos(t), r sin(t)], [r, t]))
simplifies to r, and the Laplacian of 1/r vanishes in space.

The syntax page and the MCP server's syntax document describe them, and the native smoke test
evaluates two of them.

Part of #2182.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
Rafael-SOWNet added a commit that referenced this pull request Oct 28, 2026
laplacian, jacobian and hessian were reserved by name in #2184 and are now computed when they are
parsed, as det is. Each is made of derivative nodes and needs no node of its own.

laplacian(f, [x, y, z]) is the sum of the second derivatives of a scalar f. jacobian(F, [x, y]) is
the matrix of the first derivatives of F's components, with a row for each component and a column
for each variable; a scalar F is one row. hessian(f, [x, y]) is the matrix of the second
derivatives of a scalar f, the entry in row i and column j being the derivative in x_i of the
derivative in x_j, as the definition writes it.

The variables are a vector of distinct names written out, or a single name. A set is refused,
having no order to give the columns, and so are a constant, a repeated name, and a vector or matrix
as the function of a Laplacian or a Hessian. det(jacobian([r cos(t), r sin(t)], [r, t]))
simplifies to r, and the Laplacian of 1/r vanishes in space.

The syntax page and the MCP server's syntax document describe them, and the native smoke test
evaluates two of them.

Part of #2182.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
Rafael-SOWNet added a commit that referenced this pull request Oct 29, 2026
fallingfactorial(x, n), also factorialpower(x, n), Mathematica's name, and risingfactorial(x, n)
were reserved by name in #2184 and are now Entity.FallingFactorialf and Entity.RisingFactorialf,
built by MathS.FallingFactorial and MathS.RisingFactorial.

For a whole n >= 0 they are x (x - 1) ... (x - n + 1) and x (x + 1) ... (x + n - 1), x to the n
falling and rising, computed exactly for any number x. For a negative whole n they are the
reciprocals of (x + 1) ... (x - n) and (x - 1) ... (x + n), (2.52) in Graham, Knuth and
Patashnik's Concrete Mathematics, with no value where a factor vanishes. For any other n they are
Gamma(x + 1)/Gamma(x - n + 1) and Gamma(x + n)/Gamma(x), as Mathematica's FactorialPower and
Pochhammer are, evaluated numerically: no value at a pole of the numerator, and 0 at one of the
denominator. fallingfactorial(5, 1/2) agrees with mpmath to 1e-20. risingfactorial(x, n) is
fallingfactorial(x + n - 1, n), and binomial(n, k) is fallingfactorial(n, k)/k!.

They draw with Knuth's bars, x^{\underline{n}} and x^{\overline{n}}, and export to SymPy's ff and
rf. A conjugate as the whole exponent of a power is drawn in brackets,
x^{\left(\overline{n}\right)}, since x^{\overline{n}} is the rising factorial. pochhammer stays
refused, (x)_n being the rising factorial to some authors and the falling one to others.

Part of #2182.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
Rafael-SOWNet added a commit that referenced this pull request Oct 29, 2026
multinomial(k_1, ..., k_m) was reserved by name in #2184. It is now (k_1 + ... + k_m)! /
(k_1! ... k_m!), written so when it is parsed, as Happypig375 agreed on #2182.

The factorial is the gamma function off the whole numbers, so the coefficient is symmetric in its
arguments everywhere, and it has no value where an argument or their sum is a negative integer,
a pole of the gamma function. multinomial(2, 3, 4) is 1260, multinomial(1/2, 1/2) is 4 / pi, and
multinomial(1/2, 1/3) agrees with mpmath to 1e-25.

It is not built as the product of binomial coefficients the multinomial theorem factors it into.
binomial is 0 at a negative whole lower index, and that product depends on the order of the
arguments there: multinomial(-3, 1) would be -2 and multinomial(1, -3) would be 0.

Part of #2182.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
Rafael-SOWNet added a commit that referenced this pull request Oct 29, 2026
catalannumber(n) was reserved by name in #2184 and is now Entity.CatalanNumberf, built by
MathS.CatalanNumber.

For a whole n >= 0 it is binomial(2n, n) / (n + 1), computed exactly up to n = 1000, so 1, 1, 2, 5,
14 from n = 0. Elsewhere it is 4^n Gamma(n + 1/2) / (sqrt(pi) Gamma(n + 2)), as Mathematica's
CatalanNumber and SymPy's catalan are. That is -1/2 at -1, where the poles of the two gammas
cancel, and 0 at every whole number below it, where the reciprocal of Gamma(n + 2) vanishes. A
negative half-integer, a pole of Gamma(n + 1/2), has no value, and any other number is evaluated
numerically: catalannumber(1/2) agrees with SymPy's 8/(3 pi) to 1e-25. The recurrence
C(n + 1) = sum of C(i) C(n - i) holds.

It prints as catalannumber(n), draws as \operatorname{C}_{n} and exports to SymPy's catalan.

Part of #2182.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
Rafael-SOWNet added a commit that referenced this pull request Oct 29, 2026
harmonicnumber(n) and harmonicnumber(n, m) were reserved by name in #2184 and are now
Entity.HarmonicNumberf, built by MathS.HarmonicNumber.

For a whole n >= 0 they are 1 + 1/2^m + ... + 1/n^m, summed exactly for a rational order:
harmonicnumber(4) is 25/12 and harmonicnumber(4, 2) is 205/144. Elsewhere they are
zeta(m) - zeta(m, n + 1), as Mathematica's HarmonicNumber is, which is evaluated numerically for a
whole m >= 1 through the polygamma function: psi(n + 1) - psi(1) at m = 1, and
(-1)^m / (m - 1)! (psi^(m - 1)(1) - psi^(m - 1)(n + 1)) above it. harmonicnumber(1/2) is 2 - 2 ln 2
and agrees with mpmath to 1e-25, as do H(5/2), H(-1/2) and H(1/2, 2). A negative integer n is a pole
of the Hurwitz zeta function, and has no value but for a whole order m <= 0, where the harmonic
number is a polynomial in n. What is not computed is left as the node.

They print as harmonicnumber(n) and harmonicnumber(n, m), draw as \operatorname{H}_{n} and
\operatorname{H}_{n}^{(m)}, and export to SymPy's harmonic.

Part of #2182.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
Rafael-SOWNet added a commit that referenced this pull request Oct 29, 2026
forwarddifference(f, k) was reserved by name in #2184. It is now f(k + 1) - f(k), the forward
difference with a step of 1, as Mathematica's DifferenceDelta[f, k] is. It is written that way when
parsed, as laplacian is, and needs no node: forwarddifference(k^2, k) simplifies to 1 + 2 k, and
sum(forwarddifference(k^3, k), k, 1, 5) is 215, which is 6^3 - 1. A derivative in f is taken at
k + 1, as #2189 lets it be, so the difference of derivative(k^3, k) is 6 k + 3. It is taken in a
variable, and a number or a constant in that place is refused.

Part of #2182.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
Rafael-SOWNet added a commit that referenced this pull request Oct 29, 2026
lambertw(z) and lambertw(k, z), also productlog, Mathematica's name, were reserved by name in #2184
and are now Entity.LambertWf, built by MathS.LambertW: the solution w of w e^w = z on the branch k,
a whole number, and the principal branch without one.

The branches are those of Corless, Gonnet, Hare, Jeffrey and Knuth (1996), as Mathematica's
ProductLog and mpmath's lambertw have them: W_0 is real on [-1/e, +oo) and W_(-1) on [-1/e, 0), both
are -1 at -1/e, and a value on a cut is the one approached from above. It is exact at 0, which is 0 on
the principal branch and -oo off it as ln 0 is, and where z is c e^c for a real number c the branch
takes: lambertw(e) is 1, lambertw(-1/e) is -1 and lambertw(-1, -2 e^(-2)) is -2.

Elsewhere it is evaluated at the working precision, in Number.LambertW. The branch is found in double
precision: from the series in p = sqrt(2 (e z + 1)) about the branch point, from ln(1 + z) on the
principal branch short of |z| = 3, and from ln z + 2 pi i k - ln(ln z + 2 pi i k) otherwise, each
taken to a root by Halley's iteration. That root is refined by the same iteration on decimal pairs
at the working precision, which triples the digits with each step and does not leave the branch.
Near the branch point, where double precision cannot resolve z + 1/e, the series is evaluated at the
working precision instead. Forty-five points on branches -2 to 2, and 700 random ones on -3 to 3 on
and about the cuts, agree with mpmath to 1e-25.

Its derivative is e^(-W(z)) / (1 + W(z)), which is 1 at 0 where W(z) / (z (1 + W(z))) is 0/0. It prints
as lambertw(z) and lambertw(k, z), draws as \operatorname{W}_{k}, and exports to SymPy's LambertW.

Part of #2182.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant