11using BifurcationKit, ModelingToolkit, Test
22
3- # Checks pitchfork diagram and that there are the correct number of branches (a main one and two children)
4- let
3+ # Simple pitchfork diagram, compares solution to native BifurcationKit, checks they are identical.
4+ # Checks using `jac=false` option.
5+ let
6+ # Creaets model.
57 @variables t x (t) y (t)
68 @parameters μ α
79 eqs = [0 ~ μ * x - x^ 3 + α * y,
810 0 ~ - y]
911 @named nsys = NonlinearSystem (eqs, [x, y], [μ, α])
1012
13+ # Creates BifurcationProblem
1114 bif_par = μ
1215 p_start = [μ => - 1.0 , α => 1.0 ]
1316 u0_guess = [x => 1.0 , y => 1.0 ]
14- plot_var = x
15-
16- using BifurcationKit
17- bprob = BifurcationProblem (nsys,
18- u0_guess,
19- p_start,
20- bif_par;
21- plot_var = plot_var,
22- jac = false )
17+ plot_var = x;
18+ bprob = BifurcationProblem (nsys, u0_guess, p_start, bif_par; plot_var= plot_var, jac= false )
2319
20+ # Conputes bifurcation diagram.
2421 p_span = (- 4.0 , 6.0 )
2522 opt_newton = NewtonPar (tol = 1e-9 , max_iterations = 20 )
23+ opts_br = ContinuationPar (dsmin = 0.001 , dsmax = 0.05 , ds = 0.01 ,
24+ max_steps = 100 , nev = 2 , newton_options = opt_newton,
25+ p_min = p_span[1 ], p_max = p_span[2 ],
26+ detect_bifurcation = 3 , n_inversion = 4 , tol_bisection_eigenvalue = 1e-8 , dsmin_bisection = 1e-9 )
27+ bif_dia = bifurcationdiagram (bprob, PALC (), 2 , (args... ) -> opts_br; bothside= true )
28+
29+ # Computes bifurcation diagram using BifurcationKit directly (without going through MTK).
30+ function f_BK (u, p)
31+ x, y = u
32+ μ, α = p
33+ return [μ* x - x^ 3 + α* y, - y]
34+ end
35+ bprob_BK = BifurcationProblem (f_BK, [1.0 , 1.0 ], [- 1.0 , 1.0 ], (@lens _[1 ]); record_from_solution = (x, p) -> x[1 ])
36+ bif_dia_BK = bifurcationdiagram (bprob_BK, PALC (), 2 , (args... ) -> opts_br; bothside= true )
37+
38+ # Compares results.
39+ @test getfield .(bif_dia. γ. branch, :x ) ≈ getfield .(bif_dia_BK. γ. branch, :x )
40+ @test getfield .(bif_dia. γ. branch, :param ) ≈ getfield .(bif_dia_BK. γ. branch, :param )
41+ @test bif_dia. γ. specialpoint[1 ]. x == bif_dia_BK. γ. specialpoint[1 ]. x
42+ @test bif_dia. γ. specialpoint[1 ]. param == bif_dia_BK. γ. specialpoint[1 ]. param
43+ @test bif_dia. γ. specialpoint[1 ]. type == bif_dia_BK. γ. specialpoint[1 ]. type
44+ end
45+
46+ # Lotka–Volterra model, checks exact position of bifurcation variable and bifurcation points.
47+ # Checks using ODESystem input.
48+ let
49+ # Ceates a Lotka–Volterra model.
50+ @parameters α a b
51+ @variables t x (t) y (t) z (t)
52+ D = Differential (t)
53+ eqs = [D (x) ~ - x + a* y + x^ 2 * y,
54+ D (y) ~ b - a* y - x^ 2 * y]
55+ @named sys = ODESystem (eqs)
56+
57+ # Creates BifurcationProblem
58+ bprob = BifurcationProblem (sys, [x => 1.5 , y => 1.0 ], [a => 0.1 , b => 0.5 ], b; plot_var = x)
59+
60+ # Computes bifurcation diagram.
61+ p_span = (0.0 , 2.0 )
62+ opt_newton = NewtonPar (tol = 1e-9 , max_iterations = 2000 )
2663 opts_br = ContinuationPar (dsmin = 0.001 , dsmax = 0.05 , ds = 0.01 ,
2764 max_steps = 100 , nev = 2 , newton_options = opt_newton,
2865 p_min = p_span[1 ], p_max = p_span[2 ],
2966 detect_bifurcation = 3 , n_inversion = 4 , tol_bisection_eigenvalue = 1e-8 ,
3067 dsmin_bisection = 1e-9 )
68+ bif_dia = bifurcationdiagram (bprob, PALC (), 2 , (args... ) -> opts_br; bothside = true )
3169
32- bf = bifurcationdiagram (bprob, PALC (), 2 , (args... ) -> opts_br; bothside = true )
70+ # Tests that the diagram has the correct values (x = b)
71+ all ([b. x ≈ b. param for b in bif_dia. γ. branch])
3372
34- @test length (bf. child) == 2
73+ # Tests that we get two Hopf bifurcations at the correct positions.
74+ hopf_points = sort (getfield .(filter (sp -> sp. type == :hopf , bif_dia. γ. specialpoint), :x ); by= x-> x[1 ])
75+ @test length (hopf_points) == 2
76+ @test hopf_points[1 ] ≈ [0.41998733080424205 , 1.5195495712453098 ]
77+ @test hopf_points[2 ] ≈ [0.7899715592573977 , 1.0910379583813192 ]
3578end
79+
80+ # Simple fold bifurcation model, checks exact position of bifurcation variable and bifurcation points.
81+ # Checks that default parameter values are accounted for.
82+ # Checks that observables (that depend on other observables, as in this case) are accounted for.
83+ let
84+ # Creates model, and uses `structural_simplify` to generate observables.
85+ @parameters μ p= 2
86+ @variables t x (t) y (t) z (t)
87+ D = Differential (t)
88+ eqs = [0 ~ μ - x^ 3 + 2 x^ 2 ,
89+ 0 ~ p* μ - y,
90+ 0 ~ y - z]
91+ @named nsys = NonlinearSystem (eqs, [x, y, z], [μ, p])
92+ nsys = structural_simplify (nsys)
93+
94+ # Creates BifurcationProblem.
95+ bif_par = μ
96+ p_start = [μ => 1.0 ]
97+ u0_guess = [x => 1.0 , y => 0.1 , z => 0.1 ]
98+ plot_var = x;
99+ bprob = BifurcationProblem (nsys, u0_guess, p_start, bif_par; plot_var= plot_var)
100+
101+ # Computes bifurcation diagram.
102+ p_span = (- 4.3 , 12.0 )
103+ opt_newton = NewtonPar (tol = 1e-9 , max_iterations = 20 )
104+ opts_br = ContinuationPar (dsmin = 0.001 , dsmax = 0.05 , ds = 0.01 ,
105+ max_steps = 100 , nev = 2 , newton_options = opt_newton,
106+ p_min = p_span[1 ], p_max = p_span[2 ],
107+ detect_bifurcation = 3 , n_inversion = 4 , tol_bisection_eigenvalue = 1e-8 , dsmin_bisection = 1e-9 );
108+ bif_dia = bifurcationdiagram (bprob, PALC (), 2 , (args... ) -> opts_br; bothside= true )
109+
110+ # Tests that the diagram has the correct values (x = b)
111+ all ([b. x ≈ 2 * b. param for b in bif_dia. γ. branch])
112+
113+ # Tests that we get two fold bifurcations at the correct positions.
114+ fold_points = sort (getfield .(filter (sp -> sp. type == :bp , bif_dia. γ. specialpoint), :param ))
115+ @test length (fold_points) == 2
116+ @test fold_points ≈ [- 1.1851851706940317 , - 5.6734983580551894e-6 ] # test that they occur at the correct parameter values).
117+ end
0 commit comments