TorchLean API

NN.Proofs.Gradients.Activation

Scalar Activation Derivatives #

Real calculus lemmas (HasDerivAt, etc.) for scalar activation functions, used as building blocks for TorchLean autograd correctness proofs. These identify derivatives of the real specifications, not the behavior of native kernels.

The operator references below give context for the formulas. Native evaluation, rounding, and implementation-specific branches are outside these exact-real theorems.

The ReLU, leaky-ReLU, and ELU theorems assume x ≠ 0. ReLU has a kink there; leaky ReLU and ELU are differentiable there when their slope parameter is 1, but these statements do not cover that case.

Correctness of the ReLU derivative spec away from the kink at 0.

PyTorch note: torch.relu uses a subgradient convention at 0; in this file we avoid that subtlety by assuming x ≠ 0.

theorem Proofs.leaky_relu_deriv_correct (x : ℝ) (h : x ≠ 0) (αₗ : ℝ) :

Correctness of the leaky-ReLU derivative spec away from the kink at 0.

PyTorch correspondence: torch.nn.functional.leaky_relu(x, negative_slope = αₗ) with αₗ > 0.

theorem Proofs.square_deriv_correct (x : ℝ) :
HasDerivAt (fun (y : ℝ) => y * y) (2 * x) x

Correctness of the square derivative spec: d/dx x^2 = 2x.

Correctness of the hyperbolic-sine derivative spec: sinh' = cosh.

Correctness of the hyperbolic-cosine derivative spec: cosh' = sinh.

The compensated exponential difference agrees with exp x - 1 over the reals.

theorem Proofs.elu_deriv_correct (x α : ℝ) (h : x ≠ 0) :

Correctness of the ELU derivative spec away from the kink at 0.

PyTorch correspondence: torch.nn.functional.elu(x, alpha = α). For arbitrary α, the left derivative at 0 is α and the right derivative is 1, so the clean reusable statement is the pointwise theorem away from 0.

Both evaluation branches of sigmoid define (1 + exp (-x))⁻¹ over the reals.

The negative branch multiplies numerator and denominator by exp x. This identity lets the calculus proofs use one smooth expression, including at the branch boundary x = 0.

The real sigmoid is nonnegative.

The real sigmoid is at most 1.

Sigmoid also equals exp x / (1 + exp x) over the reals.

This form is the negative-input evaluation branch and the derivative obtained by differentiating log (1 + exp x).

The decaying-exponential slope is the usual real sigmoid derivative.

Correctness of the sigmoid derivative spec.

PyTorch correspondence: torch.sigmoid.

Differentiating σ(x)(1 - σ(x)) gives σ'(x)(1 - 2σ(x)).

This is the scalar second derivative used when a sigmoid VJP is evaluated over dual numbers. The real identity holds at zero as well as on either evaluation branch.

Over the reals the logistic formula exp x / (exp x + 1) is the sigmoid, despite their different floating-point evaluation schedules.

Correctness of the derivative spec for Activation.Math.logisticSpec.

This is the scalar logistic formula exp x / (exp x + 1). It is not named softmax: a one-entry softmax is always 1, while TorchLean's actual axis-normalizing softmax is the tensor-level Activation.softmaxSpec in NN/Spec/Layers/Activation.lean. Over the reals it is the sigmoid (logisticSpec_eq_sigmoidSpec). Its output-form derivative equals the input-based sigmoid slope by sigmoid_deriv_spec_eq, so correctness follows from sigmoid_deriv_correct.

Real tanh is smooth at every order because its cosh denominator never vanishes.

tanh in terms of exp.

Mathlib states Real.sinh and Real.cosh in exponential form (Real.sinh_eq, Real.cosh_eq); the common factor 1 / 2 cancels in the quotient.

Squaring the scaled reciprocal cosh preserves the exact real tanh derivative.

Correctness of the tanh derivative spec.

PyTorch correspondence: torch.tanh.

theorem HasDerivAt.tanh {f : ℝ → ℝ} {f' x : ℝ} (hf : HasDerivAt f f' x) :
HasDerivAt (fun (y : ℝ) => Real.tanh (f y)) ((1 - Real.tanh (f x) * Real.tanh (f x)) * f') x

The real tanh chain rule, using the same derivative expression as the runtime.

Correctness of the tanh-approximate GELU derivative spec.

PyTorch correspondence: torch.nn.functional.gelu(x, approximate = "tanh"). The proof follows the product rule for x * (1 + tanh(c * (x + k*x^3))) / 2, plus the chain rule through the tanh inner polynomial.

The compensated formula agrees with log(1 + x) over the reals.

The branch-stable real softplus specification is extensionally log (1 + exp x).

Correctness of the softplus derivative spec.

PyTorch correspondence: torch.nn.functional.softplus.

The softplus derivative is sigmoid, so its derivative is σ(x)(1 - σ(x)) at every real input. This supplies the second-derivative identity without differentiating the branch test.

Splitting the negative exponential does not change the real SiLU function.

The split-exponential slope is the real product-rule derivative of SiLU.

Correctness of the SiLU derivative spec.

silu(x) = x * sigmoid(x), so the proof is just the product rule plus the already-proved sigmoid derivative. This is the scalar calculus fact used by the tensor-level VJP proof bridge.

Correctness of the safeLog derivative spec (a smooth log surrogate).

safeLog is not a standard PyTorch primitive; conceptually it is “log-like but always defined” using softplus to avoid a strict-positivity side condition.

Related PyTorch primitives: https://pytorch.org/docs/stable/generated/torch.log.html

theorem Proofs.smooth_abs_spec_eq_sqrt (x ε : ℝ) (hε : 0 < ε) :

Scaling before squaring preserves the real smooth-absolute-value function.

For positive ε, sqrt(x² + ε) is differentiable with derivative x / sqrt(x² + ε).