SciML / DifferentialEquations.jl

Multi-language suite for high-performance solvers of differential equations and scientific machine learning (SciML) components. Ordinary differential equations (ODEs), stochastic differential equations (SDEs), delay differential equations (DDEs), differential-algebraic equations (DAEs), and more in Julia.
https://docs.sciml.ai/DiffEqDocs/stable/
Other
2.84k stars 224 forks source link

Docs: Complex numbers and Autodiff Jacobians #462

Open rakhusur opened 5 years ago

rakhusur commented 5 years ago

Hi, I couldn't figure out from the docs how to use derivative-based methods for complex problems. Some things are mentioned in the news blog, others I still don't know. It would be nice if the following things were mentioned:

ChrisRackauckas commented 5 years ago

Yeah, this is more of an upstream issue with the differentiation libraries. No autodiff library currently does this, and we need a good check on the numerical diff libraries to make sure we have this right (I don't think they do: I think they currently do the directional derivative along the real, which assumes analytic).

That Jacobian is just used in the Newton iterations, so we'd have to work out what is correct for non-holomorphic. For implicit methods it's just a line search so it's okay if it's only approximate, but this will be an issue with Rosenbrock methods if it's not exactly correct. It should correspond to the same thing as if you redefined the system as the vector of real and imaginary parts, making complex numbers just a nice abstraction for the user. Given this, it might just be best to add hook ins to implement them this way, though then it would ignore any optimizations that can be had on analytic functions.

daviehh commented 5 years ago

It should correspond to the same thing as if you redefined the system as the vector of real and imaginary parts

I used to create a new array twice the size of the complex-valued one, storing the real/imaginary parts separately. Assuming the initial condition/solution is stored as a vector for now, would this be a good way when working with e.g. Rosenbrock methods?

Define

to_reals(x) = [real(vec(x)); imag(vec(x))]

function to_complexes(xri)
    rleng = Int(length(xri)//2)
    [xri[i] + xri[i+rleng]*im for i in 1:rleng]
end

then, convert the complex-valued initial condition with to_reals(), and use to_complexes() inside the ode function on u like uc = to_complexes(u), carry out the complex-valued equation on uc to get duc and return du = to_reals(duc)? Thanks!

ChrisRackauckas commented 5 years ago

Yes. That adds a few allocations to the solving though, so it might slow it down.

rakhusur commented 5 years ago

This is also what I do for now. I'm accessing the content of the real vector through these functions to avoid converting it:

getreal(v::Vector{<: Real}, i::Integer) = v[i]
getimag(v::Vector{<: Real}, i::Integer) = v[i + end÷2]
getcomplex(v::Vector{<: Real}, i::Integer) = complex(getreal(v, i), getimag(v, i))
setreal!(v::Vector{<: Real}, val::Real, i::Integer) = v[i] = val
setimag!(v::Vector{<: Real}, val::Real, i::Integer) = v[i + end÷2] = val
function setcomplex!(v::Vector{<: Real}, val::Number, i::Integer)
    setreal!(v, real(val), i)
    setimag!(v, imag(val), i)
    val
end

But perhaps it's still slow because real and imaginary part are not adjacent in memory (didn't really test this).