# How to forward differentiate a complex function?

**URL:** <https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127>\
**Category:** General Usage\
**Tags:** question, forwarddiff, autodiff, complex-numbers\
**Created:** [June 23, 2025, 10:52am UTC](https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127 "2025-06-23T10:52:14Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![albertomercurio](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/albertomercurio/32/27051_2.png) [@albertomercurio](https://discourse.julialang.org/u/albertomercurio)\
**Post date:** [June 23, 2025, 10:52am UTC](https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127/1 "2025-06-23T10:52:15Z")

</div>

Hello,

I have a very basic question, but I haven’t found any solution. I want to differentiate a holomorphic complex function.

My function is

```julia-auto
f(z) = [z^3 + exp(im*z), exp(im*z^2) + 1]

```

And I differentiate it using DifferentiationInferface.jl

```julia-auto
f2(z) = f(z[1])

_, j1 = DifferentiationInterface.value_and_jacobian(f2, AutoFiniteDiff(), [1.0+2.0im])

j1

_, j2 = DifferentiationInterface.value_and_jacobian(f2, AutoForwardDiff(), [1.0+2.0im])

j2

```

But ForwardDiff.jl returns the error

```julia-auto
ERROR: ArgumentError: Cannot create a dual over scalar type ComplexF64. If the type behaves as a scalar, define ForwardDiff.can_dual(::Type{ComplexF64}) = true.

```

What does it do if I set `can_dual`? Why isn’t it already set? If I do it I then get

```julia-auto
ERROR: ArgumentError: Cannot create a dual over scalar type Any. If the type behaves as a scalar, define ForwardDiff.can_dual(::Type{Any}) = true.

```

If I also set this, I get

```julia-auto
ERROR: MethodError: no method matching signbit(::ComplexF64)
The function `signbit` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  signbit(::Float32)
   @ Base floatfuncs.jl:16
  signbit(::Bool)
   @ Base bool.jl:151
  signbit(::Float16)
   @ Base floatfuncs.jl:17

```

## Using Zygote

It works using Zygote.js

```julia-auto
function wirtinger(f, x)
  y, back = Zygote.pullback(f, x)

  du = [back([i == j ? 1 : 0 for i in 1:length(y)])[1] for j in 1:length(y)]
  dv = [back([i == j ? im : 0 for i in 1:length(y)])[1] for j in 1:length(y)]
  (conj.(du) + im*conj.(dv))/2, (du + im*dv)/2
end

wirtinger(f, 1+2im)[1]

```

---

<div class="post-metadata">

**Author:** ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)\
**Post date:** [June 23, 2025, 2:56pm UTC](https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127/2 "2025-06-23T14:56:32Z")

</div>

Complex numbers aren’t supported by ForwardDiff. It’s definitely a deliberate omission, although I’m not the one to explain why. I believe it relates to ambiguity/ill-posedness. Setting `can_dual` for Complex won’t work, in any case.

One way to input/output complex numbers is as pairs of real numbers. Usually something like _(untested, so maybe a small mistake here)_

```julia
DifferentiationInterface.value_and_jacobian(rc -> reinterpret(Float64, f2(ComplexF64(rc[1], rc[2]))), AutoFiniteDiff(), [1.0,2.0])

```

(Note that `exp(im*x)` can be written as `cis(x)`, although there isn’t much difference between the two when `x` is complex.)

---

<div class="post-metadata">

**Author:** ![araujoms](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/araujoms/32/217734_2.png) [@araujoms](https://discourse.julialang.org/u/araujoms)\
**Post date:** [June 23, 2025, 5:42pm UTC](https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127/3 "2025-06-23T17:42:40Z")

</div>

I’m not involved with ForwardDiff, but the reason is that differentiation over complex variables is seldom useful. Pretty much all optimization algorithms deal with a vector of real variables. So it doesn’t matter what you have, real numbers, complex numbers, real matrices, complex matrices, you have to write it as a vector of real numbers.

Another reason is that the derivative you get from considering a complex function as a function over a vector of real numbers has little to do with its complex derivative.

---

<div class="post-metadata">

**Author:** ![goerz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goerz/32/3269_2.png) [@goerz](https://discourse.julialang.org/u/goerz)\
**Post date:** [June 23, 2025, 6:16pm UTC](https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127/4 "2025-06-23T18:16:31Z")

</div>

> [@araujoms](#):
>
> differentiation over complex variables is seldom useful

Except in [quantum control](https://juliaquantumcontrol.github.io/QuantumControl.jl/stable/), which is entirely based on complex vectors and matrices.

You can still rewrite everything in real and imaginary parts if you absolutely have to, but it’s a pain, and numerically inefficient. The lack of support for complex numbers in “classical” optimization / AD has been a major stumbling block for using packages from that domain “out of the box” for quantum control.

(Just mentioning that because both @albertomercurio and myself work in quantum control, and that’s probably the motivation for the question in the first place)

---

<div class="post-metadata">

**Author:** ![albertomercurio](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/albertomercurio/32/27051_2.png) [@albertomercurio](https://discourse.julialang.org/u/albertomercurio)\
**Post date:** [June 23, 2025, 6:45pm UTC](https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127/5 "2025-06-23T18:45:42Z")

</div>

Yes my example is the oversimplification of a more complex case, involving the forward differentiation of the evolution of an open quantum system.

As @goerz were mentioning, quantum systems are described by complex vectors and matrices.

So, is there a way to forward-differentiate a complex function like in the example?

---

<div class="post-metadata">

**Author:** ![araujoms](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/araujoms/32/217734_2.png) [@araujoms](https://discourse.julialang.org/u/araujoms)\
**Post date:** [June 23, 2025, 6:52pm UTC](https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127/6 "2025-06-23T18:52:25Z")

</div>

I work in quantum information, and I do need to differentiate complex matrices all the time. The way to do it is rewrite them as a real vector; specifically a vector containing the scaled elements of the upper triangular of a Hermitian matrix.

I am curious about for which application it would be useful to have the complex derivative.

---

<div class="post-metadata">

**Author:** ![danielwe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielwe/32/35657_2.png) [@danielwe](https://discourse.julialang.org/u/danielwe)\
**Post date:** [June 23, 2025, 6:53pm UTC](https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127/7 "2025-06-23T18:53:54Z")

</div>

> [@albertomercurio](#):
>
> I want to differentiate a holomorphic complex function.

There’s no way the autodiff engine can divine that your function is holomorphic, and the notion of derivative you need in the general case (a 2N by 2N Jacobian) is very different from the one you want for a holomorphic function (an N by N Jacobian). Hence, this has to be handled by some kind of wrapper function or mode that you manually select when you know the function is holomorphic. I suppose such wrappers might be in scope for DifferentiationInterface if someone wants to implement them—@gdalle?

ForwardDiff is built from the ground up around dual numbers both _wrapping_ reals and themselves _being_ reals, so it’s not surprising that just adding a method to `can_dual` is not enough to make this work transparently. See [ForwardDiff.jl/src/dual.jl at f00147ebd3615497b24bafb71c1f037857ca9408 · JuliaDiff/ForwardDiff.jl · GitHub](https://github.com/JuliaDiff/ForwardDiff.jl/blob/f00147ebd3615497b24bafb71c1f037857ca9408/src/dual.jl#L11-L14)

```julia
can_dual(::Type{<:Real}) = true
can_dual(::Type) = false

struct Dual{T,V,N} <: Real
    [...]

```

FWIW, Enzyme.jl has built-in support for reverse-mode holomorphic differentiation via the `ReverseHolomorphic` mode, see [FAQ · Enzyme.jl](https://enzyme.mit.edu/julia/stable/faq/#Complex-numbers). But for forward mode, it seems like you currently have to make the wrappers yourself, like you did for (reverse mode) Zygote.jl.

Here’s a simple wrapper for the scalar case, which should be straightforward to generalize to higher-dimensional cases. The basic idea is that, in pseudo-Julia, `holomorphicderivative(f, z) = conj(gradient(real ∘ f, z))`, where `gradient` is a simple “structural” gradient wrt. the `Complex` struct (equivalently, the \partial/\partial \bar{z} Wirtinger derivative).

```julia-repl
julia> using ForwardDiff

julia> function holomorphicderivative(f, z)
           x, y = reim(z)
           dx = ForwardDiff.derivative(x_ -> real(f(complex(x_, y))), x)
           dy = ForwardDiff.derivative(y_ -> real(f(complex(x, y_))), y)
           return complex(dx, -dy)
       end
holomorphicderivative (generic function with 1 method)

julia> holomorphicderivative(log, 1.0 + 1.0im)
0.5 - 0.5im

```

---

<div class="post-metadata">

**Author:** ![mattsignorelli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mattsignorelli/32/221502_2.png) [@mattsignorelli](https://discourse.julialang.org/u/mattsignorelli)\
**Post date:** [June 23, 2025, 7:04pm UTC](https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127/8 "2025-06-23T19:04:36Z")

</div>

The GTPSA.jl backend supports complex numbers and works just fine with this:

```julia
using GTPSA

f(z) = [z^3 + exp(im*z), exp(im*z^2) + 1]
f2(z) = f(z[1])
_, j1 = DifferentiationInterface.value_and_jacobian(f2, AutoFiniteDiff(), [1.0+2.0im])
_, jg = DifferentiationInterface.value_and_jacobian(f2, AutoGTPSA(), [1.0+2.0im])

```

---

<div class="post-metadata">

**Author:** ![goerz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goerz/32/3269_2.png) [@goerz](https://discourse.julialang.org/u/goerz)\
**Post date:** [June 23, 2025, 7:21pm UTC](https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127/9 "2025-06-23T19:21:51Z")

</div>

> [@araujoms](#):
>
> I am curious about for which application it would be useful to have the complex derivative.

To put the quantum control problem as briefly as possible, you have your Schrödinger or Liouville equation of the form

i \frac{\partial}{\partial t} \vert{\Psi(t)}\rangle = \hat{H}(\epsilon(t)) \vert\Psi(t)\rangle

where \vert{\Psi(t)}\rangle is a complex vector, \hat{H} is a complex matrix, and \epsilon(t) is a real-valued control field (you know this, but just to keep things clear for the general audience). You have some real-valued functional J(\vert\Psi(\epsilon(t), t=T\rangle) and you want to find the \epsilon(t) that minimizes J. To calculate the appropriate gradient \partial J/\partial \epsilon(t), you’ll need the complex matrix-calculus derivative \partial J/\partial \langle \Psi(T) \vert (derivative of real scalar w.r.t. to a complex vector). See [Quantum Optimal Control via Semi-Automatic Differentiation – Quantum](https://quantum-journal.org/papers/q-2022-12-07-871/) for the full details.

If you want to get the gradient fully with AD, i.e., by simulating the dynamics under the Schrödinger equation with `DifferentialEquations` or something similar and then plugging the resulting \vert \Psi(T)\rangle into the functional J, you are sad if your AD framework does not support complex numbers (`Zygote` does, with some bugs due to lack of testing the complex case, but others don’t).

In both cases, you _can_ rewrite the complex vector \vert\Psi\rangle and the complex matrix \hat{H} to objects of twice the dimension (concatenating real and imaginary part), but having to write the conversion routines is annoying, and more importantly, the extended formulation contains redundant information and slows down the simulation pretty significantly. When you’re doing thousands or millions of iterations of gradient descent based on that, with runtimes of multiple days, that’s not ideal, and you really want everything to just work with the straightforward complex objects.

---

<div class="post-metadata">

**Author:** ![gdalle](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdalle/32/27854_2.png) [@gdalle](https://discourse.julialang.org/u/gdalle)\
**Post date:** [June 23, 2025, 7:23pm UTC](https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127/10 "2025-06-23T19:23:58Z")

</div>

DI supports holomorphic Jacobians with FiniteDiff, and we could possibly add other backends if they work, but ForwardDiff certainly doesn’t (as I realized in [feat: allow and test holomorphic derivatives by gdalle · Pull Request #687 · JuliaDiff/DifferentiationInterface.jl · GitHub](https://github.com/JuliaDiff/DifferentiationInterface.jl/pull/687)).  
As for non-holomorphic complex functions, they aren’t really in scope at the moment (although I think I added the case of real-valued functions for Optim.jl).

---

<div class="post-metadata">

**Author:** ![gdalle](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdalle/32/27854_2.png) [@gdalle](https://discourse.julialang.org/u/gdalle)\
**Post date:** [June 23, 2025, 7:35pm UTC](https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127/11 "2025-06-23T19:35:17Z")

</div>

To be more precise, DI will assume that your function is holomorphic, because it is the only case where conventions are not ambiguous. This is specified in the “Limitations” docs: [Limitations · DifferentiationInterface.jl](https://juliadiff.org/DifferentiationInterface.jl/DifferentiationInterface/stable/faq/limitations/#Complex-numbers)

I have test scenarios for holomorphic functions, which I only run with FiniteDiff at the moment. We could definitely add more backends to the tests if needed (whether the functionality is supported depends more on the backend itself than it does on DI, except when the operator in question only exists in DI).

---

<div class="post-metadata">

**Author:** ![araujoms](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/araujoms/32/217734_2.png) [@araujoms](https://discourse.julialang.org/u/araujoms)\
**Post date:** [June 23, 2025, 7:37pm UTC](https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127/12 "2025-06-23T19:37:05Z")

</div>

Thanks for the explanation. So you don’t in fact want the complex derivative, you want to picture complex numbers as pairs of real numbers and take the derivative over them. That makes perfect sense and one could add support to it in ForwardDiff.

---

<div class="post-metadata">

**Author:** ![gdalle](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdalle/32/27854_2.png) [@gdalle](https://discourse.julialang.org/u/gdalle)\
**Post date:** [June 23, 2025, 7:42pm UTC](https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127/13 "2025-06-23T19:42:40Z")

</div>

Well that’s only the beginning of the story, because the representation of the derivative in the non holomorphic case has several variants, none of which is unambiguously the right one.

> [@Taking Complex Autodiff Seriously in ChainRules](https://discourse.julialang.org/t/taking-complex-autodiff-seriously-in-chainrules/39317):
>
> So these conversations have been starting up recently again on the Slack #autodiff channel and in places like [https://github.com/JuliaDiff/ChainRulesCore.jl/issues/159](https://github.com/JuliaDiff/ChainRulesCore.jl/issues/159). I think there’s a lot of misconceptions out there about the derivatives of functions of complex numbers, caused in part by confusing notation and in part by many people’s education about complex numbers focusing too heavily on the holomorphic case. However, in an AD system where we want to deal with general code, we can not limi…

---

<div class="post-metadata">

**Author:** ![danielwe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielwe/32/35657_2.png) [@danielwe](https://discourse.julialang.org/u/danielwe)\
**Post date:** [June 23, 2025, 9:50pm UTC](https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127/14 "2025-06-23T21:50:09Z")

</div>

> [@goerz](#):
>
> To calculate the appropriate gradient \partial J/\partial \epsilon(t), you’ll need the complex matrix-calculus derivative \partial J/\partial \langle \Psi(T) \vert (derivative of real scalar w.r.t. to a complex vector)

What you’re describing here is just a structural gradient of the objective with respect to the complex (dual) vector. You probably want to use reverse mode for that, and any reverse-mode autodiff engine should just give you what you want without any extra fuss, no? It’s been a while since I touched Zygote, but Enzyme certainly will. And if your end-to-end function is actually real-to-real (\epsilon(t) \rightarrow J), AD systems should be able to deal transparently with the intermediate complex numbers—they’re no different from any other struct for chain rule purposes.

Also, since a complex-to-real map can never be holomorphic, this problem is actually orthogonal to the holomorphic derivative desired in the OP.

The doubling-up boilerplate, ambiguities, and overlapping cases only enter the fray when you have non-holomorphic complex-to-complex functions.

> [@gdalle](#):
>
> DI will assume that your function is holomorphic, because it is the only case where conventions are not ambiguous

I think the structural gradient of complex-to-real functions described by @goerz is equally unambiguous. This and the holomorphic derivative are the two “nice” cases you may encounter when working with derivatives of complex numbers.

---

<div class="post-metadata">

**Author:** ![gdalle](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdalle/32/27854_2.png) [@gdalle](https://discourse.julialang.org/u/gdalle)\
**Post date:** [June 24, 2025, 4:49am UTC](https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127/15 "2025-06-24T04:49:26Z")

</div>

> [@danielwe](#):
>
> I think the structural gradient of complex-to-real functions described by @goerz is equally unambiguous.

One would hope, but even for that there are two conventions. I picked the one that makes most sense for optimization when I sought feature parity for Optim.jl to switch to DI. Again, it is only tested on a limited set of backends though, and it could use a second pair of eyes.

> **[A tutorial on automatic differentiation with complex numbers](https://arxiv.org/abs/2409.06752)**
>
> Automatic differentiation is everywhere, but there exists only minimal documentation of how it works in complex arithmetic beyond stating "derivatives in $\\mathbb{C}^d$" $\\cong$ "derivatives in $\\mathbb{R}^{2d}$" and, at best, shallow references to...

---

<div class="post-metadata">

**Author:** ![albertomercurio](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/albertomercurio/32/27051_2.png) [@albertomercurio](https://discourse.julialang.org/u/albertomercurio)\
**Post date:** [June 24, 2025, 7:25am UTC](https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127/16 "2025-06-24T07:25:17Z")

</div>

Thanks everyone for the comments!

Aside from the discussion on complex derivatives, here I write a brief recap on what I understood about Forward differentiation of complex function in Julia.

## ForwardDiff.jl

This package, although being the most used one, it seems to not fully support complex numbers. One way is

> [@danielwe](#):
>
> ```julia-auto
> julia> using ForwardDiff
> 
> julia> function holomorphicderivative(f, z)
> x, y = reim(z)
> dx = ForwardDiff.derivative(x_ -> real(f(complex(x_, y))), x)
> dy = ForwardDiff.derivative(y_ -> real(f(complex(x, y_))), y)
> return complex(dx, -dy)
> end
> holomorphicderivative (generic function with 1 method)
> 
> julia> holomorphicderivative(log, 1.0 + 1.0im)
> 0.5 - 0.5im
> 
> ```

It seems to work for scalar functions, but what if the function returns a complex vector? How would I extend it to return the Jacobian?

## GTPSA.jl

> [@mattsignorelli](#):
>
> The [GTPSA.jl](https://juliaregistries.github.io/General/packages/redirect_to_repo/GTPSA) backend supports complex numbers and works just fine with this:

And indeed it works well. However, for what I’m understanding, this method expands the function in series, truncating it up to some order. So it is not _exact_, right?

## Enzyme.jl

This package supports Forward differentiation, and it is also the fastest. The [Documentation FAQ](https://enzymead.github.io/Enzyme.jl/stable/faq/#Complex-numbers) is very detailed on this, and the following example seems to work.

```julia-auto
Enzyme.jacobian(Forward, f2, [1.0 + 2.0im])

```

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [June 24, 2025, 8:32am UTC](https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127/17 "2025-06-24T08:32:36Z")

</div>

> [@araujoms](#):
>
> So you don’t in fact want the complex derivative, you want to picture complex numbers as pairs of real numbers and take the derivative over them.

What you’re calling “the” complex derivative is just a constraint on the jacobian of a measure-zero subset of complex → complex functions such that you treat it as single-variable function.

The complex numbers fundamentally are a two-dimensional vector space, and one does not need to necessarily treat them as pairs of real numbers to talk about the full richness of that space. The Wirtinger representation for instance talks about the pair of derivatives \partial / \partial z and \partial / \partial z^\* and one can talk about the complex jacobian using that 2D complex basis as well (or any other basis one feels like taking on the complex plane).

While important, I think people wildly over-focus on holomorphic functions to a point where it seems to become their very definition of what a complex function is.

---

<div class="post-metadata">

**Author:** ![araujoms](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/araujoms/32/217734_2.png) [@araujoms](https://discourse.julialang.org/u/araujoms)\
**Post date:** [June 24, 2025, 8:59am UTC](https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127/18 "2025-06-24T08:59:14Z")

</div>

> [@Mason](#):
>
> What you’re calling “the” complex derivative is just

What I’m calling the complex derivative is what everybody calls the complex derivative, including OP. It’s important to use standard nomenclature in order to communicate clearly.

> [@Mason](#):
>
> The complex numbers fundamentally are a two-dimensional vector space, and one does not need to necessarily treat them as pairs of real numbers to talk about the full richness of that space

The complex numbers are a two-dimensional _real_ vector space. So yes, you can use different bases to represent them, but it always boils down to representing them as a pair of real numbers.

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [June 24, 2025, 9:36am UTC](https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127/19 "2025-06-24T09:36:27Z")

</div>

> [@araujoms](#):
>
> What I’m calling the complex derivative is what everybody calls the complex derivative, including OP. It’s important to use standard nomenclature in order to communicate clearly.

The OP clearly communicated wanting a _holomorphic_ derivative. You’re the only one in this thread that is using the misleading convention of just calling a holomorphic derivative “the” derivative.

---

<div class="post-metadata">

**Author:** ![mattsignorelli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mattsignorelli/32/221502_2.png) [@mattsignorelli](https://discourse.julialang.org/u/mattsignorelli)\
**Post date:** [June 24, 2025, 10:13am UTC](https://discourse.julialang.org/t/how-to-forward-differentiate-a-complex-function/130127/20 "2025-06-24T10:13:02Z")

</div>

> [@albertomercurio](#):
>
> And indeed it works well. However, for what I’m understanding, this method expands the function in series, truncating it up to some order. So it is not _exact_, right?

No, the derivatives are exact. TPSA is forward-mode autodifferentiation. The `TPS` number type is basically a `Dual` number but optimized to make high order AD with many variables much, much faster than you would get by nesting Dual numbers.
