# Problems with complex values in optimization objective (JuMP)

**URL:** <https://discourse.julialang.org/t/problems-with-complex-values-in-optimization-objective-jump/135504>\
**Category:** Optimization (Mathematical)\
**Tags:** jump, optimization, complex-numbers\
**Created:** [February 6, 2026, 2:27pm UTC](https://discourse.julialang.org/t/problems-with-complex-values-in-optimization-objective-jump/135504 "2026-02-06T14:27:49Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![dnldlg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnldlg/32/209465_2.png) [@dnldlg](https://discourse.julialang.org/u/dnldlg)\
**Post date:** [February 6, 2026, 2:27pm UTC](https://discourse.julialang.org/t/problems-with-complex-values-in-optimization-objective-jump/135504/1 "2026-02-06T14:27:49Z")

</div>

When using complex valued numbers in the objective function, get an error that I struggle to understand. This minimal example illustrates it very well. Let’s say we want to find parameters of a parametrised unitay matrix such that it agrees with a given unitary.

```julia-auto
using LinearAlgebra
using JuMP
import HiGHS

const X = ComplexF64[0 1; 1 0]
const Y = ComplexF64[0 -im; im 0]
const Z = ComplexF64[1 0; 0 -1]

rot(op, angle) = exp(-im * angle / 2 * op)

function unitary(theta)
    return exp(-i * theta[3] * kron(Z, X)) + exp(-i * theta[2] * kron(Z, Y)) * exp(-i * theta[1] * kron(Z, X))
end

function opt(theta)
    u_target = exp(-im * pi / 4 * kron(Z, X))
    u = u_lstar(theta)
    fid = abs(tr(u_target' * u))^2 / 4
    return fid
end

model = Model(HiGHS.Optimizer)
@variable(model, -pi <= theta[1:3] <= pi)
@objective(model, Max, opt(theta))
JuMP.optimize!(model)
value(theta)

```

It gives the following error:

```julia-auto
ERROR: Cannot build `GenericNonlinearExpr` because a term is complex-valued: `(0)::GenericAffExpr{ComplexF64, VariableRef}`
Stacktrace:
  [1] error(s::String)
    @ Base .\error.jl:44
  [2] _throw_if_not_real(x::GenericAffExpr{ComplexF64, VariableRef})
    @ JuMP C:\Users\xyz\.julia\packages\JuMP\7eD71\src\nlp_expr.jl:344
  [3] /(x::GenericAffExpr{ComplexF64, VariableRef}, y::NonlinearExpr)
    @ JuMP C:\Users\xyz\.julia\packages\JuMP\7eD71\src\nlp_expr.jl:401
  [4] eigtype(T::Type)
    @ LinearAlgebra C:\Users\xyz\.julia\juliaup\julia-1.12.4+0.x64.w64.mingw32\share\julia\stdlib\v1.12\LinearAlgebra\src\eigen.jl:319
  [5] exp(A::Matrix{GenericAffExpr{ComplexF64, VariableRef}})
    @ LinearAlgebra C:\Users\xyz\.julia\juliaup\julia-1.12.4+0.x64.w64.mingw32\share\julia\stdlib\v1.12\LinearAlgebra\src\dense.jl:671
...

```

The value returned by the optimization objective (and everything else except for the matrix product) is real, so I struggle to understand what causes the problem. Is it not allowed to use complex numbers altogether?

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [February 6, 2026, 6:30pm UTC](https://discourse.julialang.org/t/problems-with-complex-values-in-optimization-objective-jump/135504/2 "2026-02-06T18:30:55Z")

</div>

Hi @dnldlg, complex numbers may not appear in nonlinear expressions. I’ll update the documentation and the error message to make this clearer.

I’m away from my computer for the weekend, but I’ll see if there’s a way to rewrite your problem next week.

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [February 8, 2026, 6:58pm UTC](https://discourse.julialang.org/t/problems-with-complex-values-in-optimization-objective-jump/135504/3 "2026-02-08T18:58:22Z")

</div>

Hi @dnldlg, what is `u_lstar`? Also `unitary` refers to `i` which isn’t defined.

Do you have a minimal reproducible example?

---

<div class="post-metadata">

**Author:** ![dnldlg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnldlg/32/209465_2.png) [@dnldlg](https://discourse.julialang.org/u/dnldlg)\
**Post date:** [February 8, 2026, 10:44pm UTC](https://discourse.julialang.org/t/problems-with-complex-values-in-optimization-objective-jump/135504/4 "2026-02-08T22:44:04Z")

</div>

Apologies, I was not using a clean REPL while writing this minimal example so I didn’t catch these errors. Here is a corrected version.

```julia-auto
using LinearAlgebra
using JuMP
import HiGHS

const X = ComplexF64[0 1; 1 0]
const Y = ComplexF64[0 -im; im 0]
const Z = ComplexF64[1 0; 0 -1]

function unitary(theta)
    return exp(-im * theta[3] * kron(Z, X)) + exp(-im * theta[2] * kron(Z, Y)) * exp(-im * theta[1] * kron(Z, X))
end

function opt(theta)
    u_target = exp(-im * pi / 4 * kron(Z, X))
    u = unitary(theta)
    fid = abs(tr(u_target' * u))^2 / 4
    return fid
end

model = Model(HiGHS.Optimizer)
@variable(model, -pi <= theta[1:3] <= pi)
@objective(model, Max, opt(theta))
JuMP.optimize!(model)
value(theta)

```

I think this package might not be suitable for me if no complex operations can be performed in the optimization. While one could rewrite this specific problem with orthogonal matrices, rewriting my “real” problem seems unfeasible — or at least too tedious.

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [February 9, 2026, 12:34am UTC](https://discourse.julialang.org/t/problems-with-complex-values-in-optimization-objective-jump/135504/5 "2026-02-09T00:34:47Z")

</div>

Yeah if you don’t want to rewrite, JuMP is the wrong tool for the job.

See [Should you use JuMP? · JuMP](https://jump.dev/JuMP.jl/stable/should_i_use/#You-want-to-optimize-a-complicated-Julia-function)

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [February 9, 2026, 12:35am UTC](https://discourse.julialang.org/t/problems-with-complex-values-in-optimization-objective-jump/135504/6 "2026-02-09T00:35:33Z")

</div>

If you just provide your own gradient it should be fine, I think. (Pass a `grad_f!` to `@operator` to construct a user-defined operator with a user-defined gradient. Then JuMP won’t “look inside” it and won’t care that you use complex numbers internally.)

(Not all the AD packages support differentiating through complex functions, but some of them do, e.g. Zygote.jl and Enzyme.jl. Of course, you can also just differentiate it manually. Regardless, you might want to learn a little about CR calculus to understand what is going on — for a gentle introduction, see e.g. chapter 15 of our [_Matrix Calculus_ course notes](https://www.dropbox.com/scl/fi/iq4plt8oqja845cuuosa4/Matrix-Calculus-latest.pdf?rlkey=nsnytdu28jje41nhh1bl2dbba&e=1&st=i6lfha0r&dl=0) and references therein.)

Of course, once you have the gradient you can easily call other optimization packages, such as Optimization.jl, NLopt.jl, Nonconvex.jl, and Ipopt.jl.

For a three-parameter function, it’s also not crazy to do derivative-free optimization, which is a lot simpler to use (though it doesn’t scale to lots of parameters).

> [@dnldlg](#):
>
> `@variable(model, -pi <= theta[1:3] <= pi)`

PS. Be careful with this. It may seem reasonable, but it can create artficial local minima at the periodic \theta boundary, where it e.g. gets stuck at \theta=\pi and can’t go downhill to some \theta = -\pi + \delta. You can just leave \theta unconstrained and take it mod 2π at the end, for example.
