From a4bd550def064fba3281f1173167794ad6b86ed7 Mon Sep 17 00:00:00 2001 From: Jan Kosata <58295890+jkosata@users.noreply.github.com> Date: Thu, 21 Apr 2022 12:14:16 +0200 Subject: [PATCH] Progress bars (#20) * progress bars added * Symbolics updated --- Project.toml | 6 +++-- src/HarmonicBalance.jl | 1 + src/modules/LinearResponse.jl | 1 + .../LinearResponse/jacobian_spectrum.jl | 3 +++ src/modules/LinearResponse/linear_response.jl | 3 +++ src/solve_homotopy.jl | 18 +++++++-------- src/sorting.jl | 22 ++++++++++++------- 7 files changed, 35 insertions(+), 19 deletions(-) diff --git a/Project.toml b/Project.toml index 6ab75cfa2..5986b7d81 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "HarmonicBalance" uuid = "e13b9ff6-59c3-11ec-14b1-f3d2cc6c135e" authors = ["Jan Kosata <kosataj@phys.ethz.ch>", "Javier del Pino <jdelpino@phys.ethz.ch>"] -version = "0.4.3" +version = "0.4.4" [deps] BijectiveHilbert = "91e7fc40-53cd-4118-bd19-d7fcd1de2a54" @@ -18,6 +18,7 @@ Latexify = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Peaks = "18e31ff7-3703-566c-8e60-38913d67486b" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" +ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca" PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee" REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" @@ -37,10 +38,11 @@ HomotopyContinuation = "2.6.4" JLD2 = "0.4.19" Latexify = "0.15.9" Peaks = "0.4.0" +ProgressMeter = "1.7.2" PyCall = "1.93.0" PyPlot = "2.10.0" SymbolicUtils = "0.19.7" -Symbolics = "4.3.1" +Symbolics = "4.4.1" julia = "1.7.0" [extras] diff --git a/src/HarmonicBalance.jl b/src/HarmonicBalance.jl index 9cb5aaab0..c82a18bc3 100644 --- a/src/HarmonicBalance.jl +++ b/src/HarmonicBalance.jl @@ -8,6 +8,7 @@ module HarmonicBalance export d export plot using Symbolics + using ProgressMeter import SymbolicUtils: Term, Add, Div, Mul, Pow, Sym using DocStringExtensions diff --git a/src/modules/LinearResponse.jl b/src/modules/LinearResponse.jl index ee982082a..5c49779b6 100644 --- a/src/modules/LinearResponse.jl +++ b/src/modules/LinearResponse.jl @@ -7,6 +7,7 @@ module LinearResponse using ..HarmonicBalance using ..HC_wrapper using PyPlot + using ProgressMeter using DocStringExtensions using Latexify diff --git a/src/modules/LinearResponse/jacobian_spectrum.jl b/src/modules/LinearResponse/jacobian_spectrum.jl index 4cc7948b8..0e5942068 100644 --- a/src/modules/LinearResponse/jacobian_spectrum.jl +++ b/src/modules/LinearResponse/jacobian_spectrum.jl @@ -144,9 +144,12 @@ function plot_jacobian_spectrum(res::Result, nat_var::Num; Ω_range, branch::Int spectra = [JacobianSpectrum(res, branch=branch, index = i) for i in (1:length(res.solutions))[stability]] C = Array{Float64, 2}(undef, length(Ω_range)-1, length(X)-1) + bar = Progress(length(CartesianIndices(C)), 1, "Diagonalizing the Jacobian for each solution ... ", 50) for ij in CartesianIndices(C) C[ij] = abs(evaluate(spectra[ij[2]][nat_var], Ω_range[ij[1]] - offset[ij[2]])) + next!(bar) end + x_mat = x_scale * hcat([x*ones(length(Ω_range)) for x in X]...) y_mat = y_scale * hcat([Ω_range for j=1:length(X)]...) C = logscale ? log.(C) : C diff --git a/src/modules/LinearResponse/linear_response.jl b/src/modules/LinearResponse/linear_response.jl index add361858..6e839c091 100644 --- a/src/modules/LinearResponse/linear_response.jl +++ b/src/modules/LinearResponse/linear_response.jl @@ -79,7 +79,10 @@ function plot_response(res::Result, Ω_range; branch::Int, logscale=false) Y = Array{Float64, 2}(undef, length(Ω_range), length(X)) # this could be optimized by not grabbing the entire huge dictionary every time + + bar = Progress(length(Y), 1, "Solving the linear response ODE for each solution and input frequency ... ", 50) for j in 1:(size(Y)[2]) + next!(bar) s = get_single_solution(res, branch=branch, index=j) for i in 1:(size(Y)[1]) Y[i,j] = get_response(response, s, reverse(Ω_range)[i]) diff --git a/src/solve_homotopy.jl b/src/solve_homotopy.jl index b57f81177..1ff944c11 100644 --- a/src/solve_homotopy.jl +++ b/src/solve_homotopy.jl @@ -36,8 +36,7 @@ get_single_solution(res::Result, index) = [get_single_solution(res, index=index, fixed_parameters::ParameterList; random_warmup=false, threading=false, - sorting="hilbert", - show_progress=true) + sorting="hilbert") Solves `prob` over the ranges specified by `swept_parameters`, keeping `fixed_parameters` constant. `swept_parameters` accepts pairs mapping symbolic variables to arrays or `LinRange`. @@ -48,7 +47,6 @@ Keyword arguments This takes far longer but can be more reliable. - `threading`: If `true`, multithreaded support is activated. The number of available threads is set by the environment variable `JULIA_NUM_THREADS`. - `sorting`: the method used by `sort_solutions` to get continuous solutions branches. The current options are `"hilbert"` (1D sorting along a Hilbert curve), `"nearest"` (nearest-neighbor sorting) and `"none"`. -- `show_progress`: If `true` Indicate whether a progress bar should be displayed (see [HomotopyContinuation.jl docs](https://www.juliahomotopycontinuation.org/HomotopyContinuation.jl/stable/solve/) Example: solving a simple harmonic oscillator ``m \\ddot{x} + γ \\dot{x} + ω_0^2 x = F \\cos(ωt)`` to obtain the response as a function of ``ω`` @@ -86,7 +84,7 @@ A steady state result for 1000 parameter points ``` """ -function get_steady_states(prob::Problem, swept_parameters::ParameterRange, fixed_parameters::ParameterList; random_warmup=false, threading=false, sorting="nearest",show_progress=true) +function get_steady_states(prob::Problem, swept_parameters::ParameterRange, fixed_parameters::ParameterList; random_warmup=false, threading=false, sorting="nearest") # make sure the variables are in our namespace to make them accessible later declare_variable.(string.(cat(prob.parameters, prob.variables, dims=1))) @@ -95,7 +93,7 @@ function get_steady_states(prob::Problem, swept_parameters::ParameterRange, fixe unique_fixed = filter_duplicate_parameters(swept_parameters, fixed_parameters) input_array = _prepare_input_params(prob, swept_parameters, unique_fixed) # feed the array into HomotopyContinuation, get back an similar array of solutions - raw = _get_raw_solution(prob, input_array, sweep=swept_parameters, random_warmup=random_warmup, threading=threading,show_progress=show_progress) + raw = _get_raw_solution(prob, input_array, sweep=swept_parameters, random_warmup=random_warmup, threading=threading) # extract all the information we need from results #rounded_solutions = unique_points.(HomotopyContinuation.solutions.(getindex.(raw, 1)); metric = EuclideanNorm(), atol=1E-14, rtol=1E-8) @@ -228,20 +226,22 @@ end "Uses HomotopyContinuation to solve `problem` at specified `parameter_values`." -function _get_raw_solution(problem::Problem, parameter_values::Array{ParameterVector}; sweep=[],random_warmup=false, threading=false,show_progress= true) +function _get_raw_solution(problem::Problem, parameter_values::Array{ParameterVector}; sweep=[],random_warmup=false, threading=false) # HomotopyContinuation accepts 1D arrays of parameter sets params_1D = reshape(parameter_values, :, 1) if random_warmup complex_pert = [1E-2 * issubset(p, keys(sweep))*randn(ComplexF64) for p in problem.parameters] # complex perturbation of the warmup parameters warmup_parameters = params_1D[Int(round(length(params_1D)/2))] .* (ones(length(params_1D[1])) + complex_pert) - warmup_solution = HomotopyContinuation.solve(problem.system, target_parameters=warmup_parameters, threading=threading, show_progress=show_progress) - result_full = HomotopyContinuation.solve(problem.system, HomotopyContinuation.solutions(warmup_solution), start_parameters=warmup_parameters, target_parameters=parameter_values, threading=threading,show_progress= show_progress) + warmup_solution = HomotopyContinuation.solve(problem.system, target_parameters=warmup_parameters, threading=threading) + result_full = HomotopyContinuation.solve(problem.system, HomotopyContinuation.solutions(warmup_solution), start_parameters=warmup_parameters, target_parameters=parameter_values, threading=threading) else result_full = Array{Vector{Any}, 1}(undef, length(parameter_values)) + bar = Progress(length(parameter_values), 1, "Solving via total degree homotopy ...", 50) for i in 1:length(parameter_values) # do NOT thread this p = parameter_values[i] - result_full[i] = [HomotopyContinuation.solve(problem.system, start_system=:total_degree, target_parameters=p, threading=threading, show_progress=show_progress), p] + next!(bar) + result_full[i] = [HomotopyContinuation.solve(problem.system, start_system=:total_degree, target_parameters=p, threading=threading, show_progress=false), p] end end diff --git a/src/sorting.jl b/src/sorting.jl index 571b3f780..9a72b3e35 100644 --- a/src/sorting.jl +++ b/src/sorting.jl @@ -113,7 +113,10 @@ Go through a vector of solution and sort each according to Euclidean norm. function sort_1D(solns::Vector{Vector{SteadyState}}) sorted_solns = similar(solns) # preallocate sorted_solns[1] = sort(solns[1], by= x->abs.(imag(x))) # prefer real solution at first position + + bar = Progress(length(solns), 1, "Ordering solutions into branches ...") for (i, soln) in enumerate(solns[1:end-1]) + next!(bar) matched_indices = align_pair(sorted_solns[i], solns[i+1]) # pairs of matching indices next_indices = getindex.(matched_indices, 2) # indices of the next solution sorted_solns[i+1] = (solns[i+1])[next_indices] @@ -124,16 +127,16 @@ end function hilbert_indices(solns::Matrix{Vector{Vector{ComplexF64}}}) """Get mapping between 2D indexes (parameter space) and a 1D Hilbert curve""" Lx,Ly = size(solns) - mapping = [] #compute mapping between Hilbert indices and 2Ds - for j in 1:Ly #length of parameter sweep 1 - for i in 1:Lx #length of parameter sweep 2 + mapping = [] # compute mapping between Hilbert indices and 2Ds + for j in 1:Ly # length of parameter sweep 1 + for i in 1:Lx # length of parameter sweep 2 X = [i, j] h = encode_hilbert(Simple2D(Int), X) X .= 0 push!(mapping,(h=>decode_hilbert!(Simple2D(Int), X, h))) end end - idx_pairs = [el[2] for el in sort(mapping)] #sort along the Hilbert curve. Now we can iterate over these indexes + idx_pairs = [el[2] for el in sort(mapping)] # sort along the Hilbert curve. Now we can iterate over these indexes end function naive_indices(solns::Matrix{Vector{Vector{ComplexF64}}}) @@ -164,16 +167,19 @@ end function sort_2D(solns::Matrix{Vector{Vector{ComplexF64}}}; sorting="nearest") """match each 2D solution with all its surrounding neighbors, including the diagonal ones""" - #determine a trajectory in 2D space where nodes will be visited - if sorting=="hilbert" #propagating matching of solutions along a hilbert_curve in 2D + # determine a trajectory in 2D space where nodes will be visited + if sorting=="hilbert" # propagating matching of solutions along a hilbert_curve in 2D idx_pairs = hilbert_indices(solns) - elseif sorting=="nearest" #propagate matching of solutions along rows + elseif sorting=="nearest" # propagate matching of solutions along rows idx_pairs = naive_indices(solns) end - sorted_solns = Inf.*copy(solns) #infinite solutions are ignored by the align_pair function. This trick allows a consistent ordering "propagation" + sorted_solns = Inf.*copy(solns) # infinite solutions are ignored by the align_pair function. This trick allows a consistent ordering "propagation" sorted_solns[1,1] = sort(solns[1,1], by= x->abs.(imag(x))) # prefer real solution at first position + + bar = Progress(length(idx_pairs), 1, "Ordering solutions into branches ...") for i in 1:length(idx_pairs)-1 + next!(bar) neighbors = get_nn_2D(idx_pairs[i+1],size(solns,1),size(solns,2)) reference = [sorted_solns[ind...] for ind in neighbors] matched_indices = align_pair(reference, solns[idx_pairs[i+1]...]) # pairs of matching indices