testHopfMA.jl
``````using Test, PseudoArcLengthContinuation, LinearAlgebra, Plots, SparseArrays
const Cont = PseudoArcLengthContinuation

f1(u, v) = u^2*v
n = 101
a = 2.
b = 5.45

function F_bru(x, α, β; D1 = 0.008, D2 = 0.004, l = 1.0)
n = div(length(x), 2)
h = 1.0 / (n+1); h2 = h*h

u = @view x[1:n]
v = @view x[n+1:2n]

# output
f = similar(x)

f[1] = u[1] - α
f[n] = u[n] - α
for i=2:n-1
f[i] = D1/l^2 * (u[i-1] - 2u[i] + u[i+1]) / h2 - (β + 1) * u[i] + α + f1(u[i], v[i])
end

f[n+1] = v[1] - β / α
f[end] = v[n] - β / α;
for i=2:n-1
f[n+i] = D2/l^2 * (v[i-1] - 2v[i] + v[i+1]) / h2 + β * u[i] - f1(u[i], v[i])
end

return f
end

function Jac_sp(x, α, β; D1 = 0.008, D2 = 0.004, l = 1.0)
# compute the Jacobian using a sparse representation
n = div(length(x), 2)
h = 1.0 / (n+1); hh = h*h

diag  = zeros(2n)
diagp1 = zeros(2n-1)
diagm1 = zeros(2n-1)

diagpn = zeros(n)
diagmn = zeros(n)

diag[1] = 1.0
diag[n] = 1.0
diag[n + 1] = 1.0
diag[end] = 1.0

for i=2:n-1
diagm1[i-1] = D1 / hh/l^2
diag[i]   = -2D1 / hh/l^2 - (β + 1) + 2x[i] * x[i+n]
diagp1[i] = D1 / hh/l^2
diagpn[i] = x[i]^2
end

for i=n+2:2n-1
diagmn[i-n] = β - 2x[i-n] * x[i]
diagm1[i-1] = D2 / hh/l^2
diag[i]   = -2D2 / hh/l^2 - x[i-n]^2
diagp1[i] = D2 / hh/l^2
end
return spdiagm(0 => diag, 1 => diagp1, -1 => diagm1, n => diagpn, -n => diagmn)
end

sol0 = vcat(a * ones(n), b / a * ones(n))

opt_newton = Cont.NewtonPar(tol = 1e-11, verbose = true)
out, hist, flag = @time Cont.newton(
x -> F_bru(x, a, b),
x -> Jac_sp(x, a, b),
sol0,
opt_newton)

opts_br0 = ContinuationPar(dsmin = 0.001, dsmax = 0.01, ds= 0.0051, pMax = 1.8, theta = 0.01, detect_bifurcation = true, nev = 16)

br, u1 = @time Cont.continuation(
(x, p) ->  F_bru(x, a, b, l = p),
(x, p) -> Jac_sp(x, a, b, l = p),
out,
0.3,
opts_br0,
plot = false,
printsolution = x->norm(x, Inf64), verbosity = 0)
#################################################################################################### Continuation of the Hopf Point using Dense method
ind_hopf = 1
av = randn(Complex{Float64},2n); av = av./norm(av)
bv = randn(Complex{Float64},2n); bv = bv./norm(bv)
hopfpt = Cont.HopfPoint(br, ind_hopf)
# hopfpt[1:2n] .= rand(2n)
hopfvariable = β -> HopfProblemMinimallyAugmented(
(x, p) ->  F_bru(x, a, β, l = p),
(x, p) -> Jac_sp(x, a, β, l = p),
(x, p) -> transpose(Jac_sp(x, a, β, l = p)),
br.eig[br.bifpoint[ind_hopf][2]][2][:, br.bifpoint[ind_hopf][end]],
conj.(br.eig[br.bifpoint[ind_hopf][2]][2][:, br.bifpoint[ind_hopf][end]]),
# av,
# bv,
opts_br0.newtonOptions.linsolve)
hopfPb = (u, p)->hopfvariable(p)(u)

hopfvariable(b)(hopfpt)

# finite differences Jacobian
Jac_hopf_fdMA(u0, p) = Cont.finiteDifferences( u-> hopfPb(u, p), u0)
# ``analytical'' jacobian
Jac_hopf_MA(u0, pb::HopfProblemMinimallyAugmented) = (return (u0, pb))

# # on construit la matrice a inverser pour calculer sigma1 et sigma2
# ω = hopfpt[end]
# # av = hopfvariable(b).a
# # bv = hopfvariable(b).b
# Jh = hopfvariable(b).J(hopfpt[1:end-2], hopfpt[end-1])
# A = Jh + Complex(0, ω ) * I
# A = Array(A)
# Atot = [A av ; (bv') 0.]
# res1 = Atot \ [zeros(2n)' Complex(1,0)]'
# res2 = Atot' \ [zeros(2n)' Complex(1,0)]'
# println("--> σ1 = ", res1[end], ",\n--> σ2 = ", res2[end])
#
# dot(res2, Atot*res1)
#
# v, σ1, _ = Cont.linearBorderedSolver(Jh + Complex(0, ω) * I, av, bv, 0., zeros(2n), 1.0, hopfvariable(b).linsolve)
#
# w, σ2, _ = Cont.linearBorderedSolver(Jh' - Complex(0, ω) * I, bv, av, 0., zeros(2n), 1.0, hopfvariable(b).linsolve)
#
# res1[1:end-1]-v |> norm
# res2[1:end-1]-w |> norm
#
# L1 = hcat(Jh, -ω*I, real.(av), -imag.(av))
# L2 = hcat(ω*I, Jh, imag.(av), real.(av))
# L3 = hcat(real.(bv)', imag.(bv)', 0, 0)
# L4 = hcat(-imag.(bv)', real.(bv)', 0, 0)
# Atot2 = vcat(L1, L2, L3, L4)
# res12 = Atot2 \ hcat(zeros(4n)',[1],[0])'
# res22 = Atot2' \ hcat(zeros(4n)',[1],[0])'
#
# @test res12[1:2n] - real(res1[1:end-1]) |> norm < 1e-10
# @test res12[2n+1:4n] - imag(res1[1:end-1]) |> norm < 1e-10
# @test res22[1:2n] - real(res2[1:end-1]) |> norm < 1e-10
# @test res22[2n+1:4n] - imag(res2[1:end-1]) |> norm < 1e-10
#
# @test v - res1[1:end-1] |> norm < 1e-10
# @test w - res2[1:end-1] |> norm < 1e-10
#
# println("--> version reelle σ1 = ", res12[end-1:end])
# println("--> version reelle σ2 = ", res22[end-1:end])
#
# # on calcule les derivees de sigma1 et sigma2
# jac_hopf_fd = Jac_hopf_fdMA(hopfpt, b)
# println("-->\n FD sigma_omega = ", jac_hopf_fd[end-1:end,end-1:end])
# dot(res22[1:2n], res12[1:2n])
# dot(res22[2n+1:4n], res12[2n+1:4n])
#
# newton convergence toward
outhopf, _, flag, _ = @time Cont.newton(u -> hopfvariable(b)(u),
u -> Jac_hopf_fdMA(u, b),
hopfpt, NewtonPar(verbose = true))
flag && printstyled(color=:red, "--> We found a Hopf Point at l = ", outhopf[end-1], ", ω = ", outhopf[end], " - ", hopfpt[end-1:end], "\n")

outhopf, _, flag, _ = @time Cont.newton(u -> hopfvariable(b)(u),
x -> Jac_hopf_MA(x, hopfvariable(b)),
hopfpt, NewtonPar(verbose = true, linsolve = HopfLinearSolveMinAug(), eigsolve = Default_eig()))
flag && printstyled(color=:red, "--> We found a Hopf Point at l = ", outhopf[end-1], ", ω = ", outhopf[end], " - ", hopfpt[end-1:end], "\n")

rhs = rand(length(hopfpt))
jac_hopf_fd = Jac_hopf_fdMA(hopfpt, b)
sol_fd = jac_hopf_fd \ rhs
# create a linear solver
hopflinsolve = HopfLinearSolveMinAug()
sol_ma, _, _, sigomMA  = hopflinsolve(Jac_hopf_MA(hopfpt, hopfvariable(b)), rhs, true)

println("--> test jacobian expression for Hopf Minimally Augmented")
@test sol_ma - sol_fd |> x->norm(x, Inf64) < 1e-3

@test (sol_ma - sol_fd)[1:end-2] |> x->norm(x, Inf64) < 1e-5

# dF = jac_hopf_fd[:,end-1]
# sig_vec_re = jac_hopf_fd[end-1,1:end-2]
# sig_vec_im = jac_hopf_fd[end,1:end-2]
#
# println("-->\n FD sigma_omega = ", jac_hopf_fd[end-1:end,end-1:end]')
#
# norm(sig_vec_re - real.(sigomMA[1]), Inf64)
# norm(sig_vec_im - imag.(sigomMA[1]), Inf64)
# norm(dF[1:end-2] - sigomMA[end], Inf64)
#
# # on essaie de resoudre jac_hopf_fd \ rhs
# Jh = jac_hopf_fd[1:end-2,1:end-2]
# sig1 = jac_hopf_fd[end-1,1:end-2]
# sig2 = jac_hopf_fd[end,1:end-2]
# sig1p = jac_hopf_fd[end-1,end-1]
# sig2p = jac_hopf_fd[end,end-1]
# sig1o = jac_hopf_fd[end-1,end]
# sig2o = jac_hopf_fd[end,end]
#
# sigma = hcat(sig1,sig2)
#
# X1 = Jh \ rhs[1:end-2]
# X2 = Jh \ dF[1:end-2]#jac_hopf_fd[1:end-2,end-2]
# X2m = hcat(X2, 0*X2)
# C = jac_hopf_fd[end-1:end,end-1:end]
# println("--> dp, dom = ",(C - sigma' * X2m) \ (rhs[end-1:end] - sigma' * X1))
# println("--> dp, dom FD = ",sol_fd[end-1:end])
outhopf, hist, flag = @time Cont.newtonHopf((x, p) ->  F_bru(x, a, b, l = p),
(x, p) -> Jac_sp(x, a, b, l = p),
br, ind_hopf,
NewtonPar(verbose = true))
flag && printstyled(color=:red, "--> We found a Hopf Point at l = ", outhopf[end-1], ", ω = ", outhopf[end], ", from l = ",hopfpt[end-1],"\n")

br_hopf, u1_hopf = @time Cont.continuationHopf(
(x, p, β) ->   F_bru(x, a, β, l = p),
(x, p, β) ->  Jac_sp(x, a, β, l = p),
br, ind_hopf,
b,
ContinuationPar(dsmin = 0.001, dsmax = 0.05, ds= 0.01, pMax = 6.5, pMin = 0.0, a = 2., theta = 0.4, maxSteps = 3, newtonOptions = NewtonPar(verbose=false)), verbosity = 0, plot = false)
#################################################################################################### Continuation of Periodic Orbit
ind_hopf = 2
hopfpt = Cont.HopfPoint(br, ind_hopf)

l_hopf = hopfpt[end-1]
ωH     = hopfpt[end] |> abs
M = 30

orbitguess = zeros(2n, M)
vec_hopf = br.eig[br.bifpoint[ind_hopf][2]][2][:, br.bifpoint[ind_hopf][end]-1]
for ii=1:M
t = (ii-1)/(M-1)
orbitguess[:, ii] .= real.(hopfpt[1:2n] +
26*0.1 * vec_hopf * exp(2pi * complex(0, 1) * (t - 0.2790)))
end

orbitguess_f = vcat(vec(orbitguess), 2pi/ωH) |> vec

poTrap = l-> PeriodicOrbitTrap(
x-> F_bru(x, a, b, l = l),
x-> Jac_sp(x, a, b, l = l),
real.(vec_hopf),
hopfpt[1:2n],
M,
opt_newton.linsolve)

jac_PO_fd = Cont.finiteDifferences(x->poTrap(l_hopf + 0.01)(x), orbitguess_f)
jac_PO_sp =  poTrap(l_hopf + 0.01)(orbitguess_f, :jacsparse)

# test of the Jacobian for PeriodicOrbit via Finite differences VS the FD associated jacobian
println("--> test jacobian expression for Periodic Orbit solve problem")
@test norm(jac_PO_fd - jac_PO_sp, Inf64) < 1e-5

# newton to find Periodic orbit
opt_po = Cont.NewtonPar(tol = 1e-8, verbose = true, maxIter = 50)
outpo_f, hist, flag = @time Cont.newton(
x ->  poTrap(l_hopf + 0.01)(x),
x ->  poTrap(l_hopf + 0.01)(x, :jacsparse),
orbitguess_f,
opt_po)
println("--> T = ", outpo_f[end])

# continuation of periodic orbits
opts_po_cont = ContinuationPar(dsmin = 0.0001, dsmax = 0.05, ds= 0.001, pMax = 2.3, maxSteps = 3, secant = true, theta = 0.1, plot_every_n_steps = 3, newtonOptions = NewtonPar(verbose = false))
br_pok2, upo , _= @time Cont.continuation(
(x, p) ->  poTrap(p)(x),
(x, p) ->  poTrap(p)(x, :jacsparse),
outpo_f, l_hopf + 0.01,
opts_po_cont,
plot = false,
plotsolution = (x;kwargs...)->heatmap!(reshape(x[1:end-1], 2*n, M)', subplot=4, ylabel="time"),
printsolution = u -> u[end], verbosity = 0)
``````