# Using LsqFit on complex data

I’m trying to use LsqFit.jl to fit data that is complex (in the form x + im*y). For purposes of showing the problem, I’m using the example provided on GitHub for LsqFit but I’m changing the model such that it has an imaginary term in the exponential, rather than a real one.

``````using LsqFit
@. model(x, p) = p * exp(im*x * p)

xdata = range(0, stop=10, length=20)
ydata = model(xdata, [1.0 2.0]) + 0.01 * randn(length(xdata))

p0 = [0.5, 0.5]
fit = curve_fit(model, xdata, ydata, p0)
``````

I get the error message:

``````LoadError: InexactError: Float64(-0.9292044089784778 + 3.141189448336519im)
Stacktrace:
 Real
@ ./complex.jl:44 [inlined]
 convert
@ ./number.jl:7 [inlined]
``````

I’ve tried the same thing in Matlab ( ) using `lsqcurvefit()` with complex data and it works fine and extracts the correct parameters. I’m aware that a fitting procedure that involves complex data is going to be inherently different to simply fitting real data, but I cannot figure this out.

1 Like

Even if I force all arguments to be of type (or `eltype`) `ComplexF64` like

``````using LsqFit
@. model(x, p) = p * exp(im*x * p)

xdata = range(0+0im, stop=10, length=20)
ydata = model(xdata, ComplexF64[1.0 2.0]) + 0.01 * randn(length(xdata))

p0 = [0.5+0im, 0.5+0im]
fit = curve_fit(model, xdata, ydata, p0)
``````

I get

``````ERROR: MethodError: no method matching isless(::Float64, ::ComplexF64)
Closest candidates are:
isless(::T, ::T) where T<:Union{Float16, Float32, Float64} at float.jl:424
isless(::AbstractFloat, ::AbstractFloat) at operators.jl:177
isless(::Real, ::AbstractFloat) at operators.jl:178
...
Stacktrace:
 max(x::ComplexF64, y::Float64)
@ Base .\operators.jl:467
 levenberg_marquardt(df::NLSolversBase.OnceDifferentiable{Vector{ComplexF64}, Matrix{ComplexF64}, Vector{ComplexF64}}, initial_x::Vector{ComplexF64}; x_tol::Float64, g_tol::Float64, maxIter::Int64, lambda::ComplexF64, tau::ComplexF64, lambda_increase::Float64, lambda_decrease::Float64, min_step_quality::Float64, good_step_quality::Float64, show_trace::Bool, lower::Vector{ComplexF64}, upper::Vector{ComplexF64}, avv!::Nothing)
``````

This looks like a problem in `LsqFit` to me.

1 Like

Yes, it looks like its `levenberg_marquardt` function assumes that the model results have the same type as the parameters (see the array allocations here), which is wrong in this case. They need to be a bit more careful in propagating the data types.

I would file an issue (and/or work on a PR).

2 Likes

@stevengj: I tried to fix the problem. Extended the original tests in `curve_fit.jl` and `curve_fit_inplace.jl` to check with `Float32`, `Float64`, `ComplexF32`, `ComplexF64`. Only open problem for now is I’m running into problems with complex AD in `ForwardDiff` here

``````        for ad in (T<:Complex ? (:finite,) : (:finite, :forward, :forwarddiff))
fit = curve_fit(model, xdata, ydata, p0; autodiff = ad)
@show fit.param
@assert norm(fit.param - [1.0, 2.0]) < 0.05
@test fit.converged

# can also get error estimates on the fit parameters
errors = margin_error(fit, 0.1)
@assert norm(errors - [0.017, 0.075]) < 0.01
end
``````

What else to check?

Thanks very much for your help, it’s super great to know that you guys are willing to help fix the problem! This has been my first question I’ve posted during my PhD after switching from Matlab to Julia, and it’s great to know there is help out there. I look forward to seeing the progress on the resolution of this issue!

Ahh okay- I look forward to seeing the progress of the resolution of this issue! Many thanks for your help on this!

I don’t think ForwardDiff supports this (Support for real-valued function with complex arguments · Issue #498 · JuliaDiff/ForwardDiff.jl · GitHub). I think Zygote does?

(Or you can use finite differences here; that’s what Matlab’s `lsqcurvefit` does IIRC.)