镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Differentiating a factorial or gamma answers NaN, which claims the derivative does not exist #958

Closed

Description

@Rafael-SOWNet

Entity.Differentiate answers NaN for the factorial and the gamma function, which claims their derivatives do not exist. They do — both are smooth away from the poles at non-positive integers.

Measured on master at a45a7256, .NET 10, default settings:

expression InnerSimplified Evaled
derivative(sin(x), x) cos(x) cos(x)
derivative(abs(x), x) sgn(x) provided not x = 0 same
derivative(sgn(x), x) 0 provided not x = 0 same
derivative(x!, x) NaN NaN
derivative(gamma(x), x) NaN NaN

The first three are the library working correctly, including the two that need a side condition. The last two are the defect.

Why it is a wrong answer rather than a missing feature

x! is not undefined and not non-differentiable — the library evaluates it at non-integers through the gamma function:

"(1/2)!".EvalNumerical()   // 0.886226925452758...

so it is a smooth function of a real argument, and d/dx x! exists everywhere except at the poles. At x = 3 it is Γ'(4) = 6(H₃ − γ) ≈ 3.966, a perfectly ordinary number.

AGENTS.md draws exactly this distinction:

Not answering is a legitimate answer; answering wrongly is not. Unevaluated means "I could not settle this", NaN means "this does not exist", and confusing them ships a wrong answer.

This is that confusion. The honest answer is the unevaluated derivative(x!, x), which says "I cannot settle this" and leaves the caller free to handle it. NaN says something false, and it propagates: anything multiplying or adding this becomes NaN too, so a single factorial in a larger expression can silently destroy an otherwise fine derivative.

Scope

This is not #171 ("Inverse and derivative for Factorial and Gamma"), which is the feature request to actually compute these and is deliberately marked Not now. This issue is only that the refusal is spelled wrongly. Fixing it does not require implementing digamma — it requires the derivative rule to decline rather than to assert non-existence, and it would remain correct once #171 is eventually done.

I have not checked how many other nodes return NaN from InnerDifferentiate for the same reason; that sweep is the obvious next step and I would expect this not to be the only one.

Activity

  1. Rafael-SOWNet commented on Aug 16, 2026

    @Rafael-SOWNet
    MemberAuthor

    Swept the whole node space, and I was wrong about the scope — in the good direction. I wrote above that I would expect this not to be the only one. It is.

    Enumerated every concrete Entity node type by reflection (65 of them), built each with a variable argument and differentiated it, on master at a45a7256:

    differentiated fine: 46      answered NaN: 1      not constructible this way: 18
    

    The one is Factorialf. And gamma(x) parses to Factorialf, so the two rows in the table above are one node type rather than two — the whole defect is a single rule.

    The 18 my constructor probe could not build are numbers (Integer, Rational, Real, Complex, Boolean), sets (Integers, Reals, FiniteSet, Interval, …), containers (Matrix, Piecewise) and the three calculus nodes, which take extra arguments. I checked those by hand rather than leave the gap:

    derivative(derivative(y, y), x) 0
    derivative(integral(y, y), x) 0
    derivative(limit(y, y, 0), x) 0
    derivative(piecewise(x provided x > 0, 0), x) piecewise(1 provided x > 0, 0 provided True)
    derivative([1, 2], x) [0, 0]
    derivative(apply(f, x), x) derivative(apply(f, x), x) — unevaluated
    derivative(x in RR, x) derivative(x in RR, x) — unevaluated

    So every other node either differentiates correctly, or refuses by staying unevaluated.

    That last point is the useful one for fixing this. apply(f, x) is the closest analogue — a function whose derivative the library genuinely cannot compute — and it already does the right thing: it hands back the unevaluated derivative(...) rather than asserting non-existence. Factorialf is the single node that does not follow that pattern, so the fix is to make it behave like its neighbour, and there is no sweep of other nodes owed afterwards.

  2. added theissue type on Sep 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions