Skip to content

Tangent Propagation

Forward mode automatic differentiation computes derivatives by propagating tangent values alongside ordinary values. The ordinary value is called the primal. The derivative...

Forward mode automatic differentiation computes derivatives by propagating tangent values alongside ordinary values. The ordinary value is called the primal. The derivative value is called the tangent.

For every program variable vv, forward mode tracks:

(v,v˙) (v,\dot v)

The dot notation means “the change in vv induced by a chosen change in the input.”

If the input changes in direction x˙\dot x, then every later value changes according to the chain rule.

Directional Derivatives

Let:

f:RnRm f:\mathbb{R}^n\to\mathbb{R}^m

At input xx, choose a direction:

x˙Rn \dot x\in\mathbb{R}^n

Forward mode computes:

y˙=Jf(x)x˙ \dot y = J_f(x)\dot x

where Jf(x)J_f(x) is the Jacobian.

Thus forward mode computes a directional derivative. It tells us how the output changes when the input moves infinitesimally in direction x˙\dot x.

Primal and Tangent Execution

Consider:

f(x)=x2+3x f(x)=x^2+3x

A program evaluates:

v1 = x * x
v2 = 3 * x
v3 = v1 + v2
return v3

Forward mode evaluates a paired program:

v1     = x * x
dot_v1 = x * dot_x + x * dot_x

v2     = 3 * x
dot_v2 = 3 * dot_x

v3     = v1 + v2
dot_v3 = dot_v1 + dot_v2

return v3, dot_v3

If x˙=1\dot x=1, then:

v˙3=2x+3 \dot v_3=2x+3

which is the ordinary derivative.

Local Tangent Rules

Each primitive operation has a tangent rule.

OperationPrimalTangent
additionz=x+yz=x+yz˙=x˙+y˙\dot z=\dot x+\dot y
subtractionz=xyz=x-yz˙=x˙y˙\dot z=\dot x-\dot y
multiplicationz=xyz=xyz˙=yx˙+xy˙\dot z=y\dot x+x\dot y
divisionz=x/yz=x/yz˙=(yx˙xy˙)/y2\dot z=(y\dot x-x\dot y)/y^2
sinez=sinxz=\sin xz˙=cos(x)x˙\dot z=\cos(x)\dot x
exponentialz=exz=e^xz˙=exx˙\dot z=e^x\dot x
logarithmz=logxz=\log xz˙=x˙/x\dot z=\dot x/x

These rules are applied in the same order as the program.

Tangent Seeding

The initial tangent determines the derivative being computed.

For one scalar input:

x˙=1 \dot x=1

computes:

dfdx \frac{df}{dx}

For vector input x=(x1,,xn)x=(x_1,\dots,x_n), choosing:

x˙=ei \dot x=e_i

computes the ii-th column of the Jacobian.

Example:

f(x1,x2)=[x1x2sin(x1)+x2] f(x_1,x_2)= \begin{bmatrix} x_1x_2\\ \sin(x_1)+x_2 \end{bmatrix}

Seed:

x˙=[10] \dot x= \begin{bmatrix} 1\\ 0 \end{bmatrix}

Then forward mode computes the first Jacobian column:

y˙=[x2cos(x1)] \dot y= \begin{bmatrix} x_2\\ \cos(x_1) \end{bmatrix}

Seed:

x˙=[01] \dot x= \begin{bmatrix} 0\\ 1 \end{bmatrix}

Then it computes the second column:

y˙=[x11] \dot y= \begin{bmatrix} x_1\\ 1 \end{bmatrix}

Tangents Through Control Flow

Forward mode follows the same control flow as the primal program.

Example:

if x > 0:
    y = x * x
else:
    y = -x

If x>0x>0:

y˙=2xx˙ \dot y=2x\dot x

If x<0x<0:

y˙=x˙ \dot y=-\dot x

At x=0x=0, the function has a corner. Forward mode returns the tangent of the executed branch. It does not infer a symbolic piecewise derivative.

This is important for programs with:

  • thresholds
  • comparisons
  • clipping
  • indexing
  • data-dependent branches

Tangents Through Loops

Forward mode handles loops directly because tangent propagation follows primal execution.

Example:

y = x
for i in range(n):
    y = y * y

The tangent program is:

y = x
dot_y = dot_x

for i in range(n):
    old_y = y
    old_dot_y = dot_y

    y = old_y * old_y
    dot_y = old_y * old_dot_y + old_y * old_dot_y

Each iteration propagates the tangent through the loop body.

For fixed nn, this computes the derivative of the function represented by the loop.

Tangents for Structured Values

Real programs use arrays, tuples, structs, and nested containers.

Forward mode assigns tangents only to differentiable components.

Example:

State {
    position: Float
    velocity: Float
    step: Int
}

A tangent state may contain:

StateTangent {
    position: Float
    velocity: Float
}

The integer step has no tangent because discrete values do not vary infinitesimally.

This distinction becomes important in differentiable programming languages and compiler IRs.

Multiple Tangent Directions

Forward mode can carry several tangent directions at once.

Instead of:

(v,v˙) (v,\dot v)

use:

(v,v˙1,v˙2,,v˙k) (v,\dot v_1,\dot v_2,\dots,\dot v_k)

This computes:

Jf(x)S J_f(x)S

where SS is a matrix of seed directions.

Multi-direction forward mode is useful for:

  • computing several Jacobian columns
  • exploiting SIMD and vector hardware
  • reducing interpreter overhead
  • sparse Jacobian recovery

The cost grows roughly linearly with the number of tangent directions, but batching can improve constants.

Memory Behavior

Forward mode has simple memory behavior.

At each point, it needs only:

  • current primal values
  • current tangent values

It does not need to store a full tape for a later backward pass.

Thus forward mode is attractive for:

  • streaming computations
  • long simulations
  • online sensitivity analysis
  • low-memory systems

Its weakness is dimensional scaling. A full gradient of a scalar function with nn inputs requires nn forward passes.

Tangent Propagation as Pushforward

In differential geometry, tangent propagation is a pushforward.

A function:

f:XY f:X\to Y

maps points in XX to points in YY.

Its derivative maps tangent vectors at xx to tangent vectors at f(x)f(x):

Dfx:TxXTf(x)Y Df_x:T_xX\to T_{f(x)}Y

Forward mode computes this map operationally.

In coordinates, this is exactly:

y˙=Jf(x)x˙ \dot y=J_f(x)\dot x

This geometric view clarifies why forward mode moves in the same direction as computation.

Summary

Tangent propagation is the core mechanism of forward mode.

It evaluates each variable as:

(v,v˙) (v,\dot v)

and applies local tangent rules in program order.

Forward mode computes Jacobian-vector products, supports ordinary control flow naturally, and uses little memory. Its cost is proportional to the number of tangent directions being propagated.