Skip to content

tanh rule in v1.74.11 fails on symbolic inputs ("non-boolean (Num) used in boolean context") #871

Closed

Description

@StefanKarpinski

Since v1.74.11 (#864), the tanh rule picks between two formulas with a ternary:

@scalar_rule tanh(x) abs(real(x)) < 1 ? 1 - Ω ^ 2 : sech(x) ^ 2

The ternary needs a Bool. For a symbolic x (e.g. Symbolics.Num), abs(real(x)) < 1 is a symbolic expression, so frule / derivatives_given_output throws.

Reproducer

using ChainRules, ChainRulesCore, Symbolics
@variables x
frule((NoTangent(), true), tanh, x)
ERROR: TypeError: non-boolean (Symbolics.Num) used in boolean context
Stacktrace:
 [1] derivatives_given_output(Ω::Num, ::typeof(tanh), x::Num)
   @ ChainRules ~/.julia/packages/ChainRules/.../src/rulesets/Base/fastmath_able.jl:42
 [2] frule(::Tuple{NoTangent, Bool}, ::typeof(tanh), x::Num)
   @ ChainRules ~/.julia/packages/ChainRules/.../src/rulesets/Base/fastmath_able.jl:42

The stack trace is from a CI failure in which TaylorDiff.jl hit this while precompiling (log). The snippet above reconstructs that call and hasn't been run on its own.

Impact

TaylorDiff.jl (v0.3.5) evaluates ChainRules rules on Symbolics variables while it precompiles, so it now fails to precompile. That in turn breaks everything that depends on it, e.g. NonlinearSolveHomotopyContinuation and the ModelingToolkit HomotopyContinuation extension tests. We see this in downstream CI: v1.74.10 passes, v1.74.11 and later fail, and v1.74.13 still has the ternary.

Suggested fix

Use ifelse instead of the ternary. Symbolics and similar packages overload it for symbolic conditions:

@scalar_rule tanh(x) ifelse(abs(real(x)) < 1, 1 - Ω ^ 2, sech(x) ^ 2)

ifelse always computes both branches, which costs one extra sech for ordinary numbers, but gives the same result. The other rules changed in #864 may be worth checking for the same problem with non-Bool comparisons.

(Diagnosed with the help of Claude Code.)

Activity

  1. devmotion commented on Nov 9, 2026

    @devmotion
    Member

    The only point for using the ternary is to avoid the sech in cases that are less susceptible to numerical issues, so an ifelse would invalidate that purpose.

    Since ChainRules doesn't has any downstream tests for TaylorDiff and no policy that would prefer or recommend ifelse (it seems ternaries are rather common in rules in general) it seems almost a bit of a lucky coincidence that rules used by TaylorDiff had been working.

  2. devmotion commented on Nov 10, 2026

    @devmotion
    Member

    I'm not sure whether ChainRules is suitable for TaylorDiff in the way it's currently used, so I opened JuliaDiff/TaylorDiff.jl#114 as a possible fix.

  3. devmotion commented on Nov 12, 2026

    @devmotion
    Member

    The TaylorDiff PR was merged and is included in the latest release of TaylorDiff. This fixes the issue.

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

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions