|
| 1 | + |
| 2 | + |
| 3 | +function solve_sparse(T, eq, x, basis, radius; kwargs...) |
| 4 | + args = Dict(kwargs) |
| 5 | + abstol, opt, complex_plane, verbose = args[:abstol], args[:opt], args[:complex_plane], |
| 6 | + args[:verbose] |
| 7 | + n = length(basis) |
| 8 | + |
| 9 | + # A is an nxn matrix holding the values of the fragments at n random points |
| 10 | + A = zeros(Complex{T}, (n, n)) |
| 11 | + X = zeros(Complex{T}, n) |
| 12 | + |
| 13 | + init_basis_matrix!(T, A, X, x, eq, basis, radius, complex_plane; abstol) |
| 14 | + |
| 15 | + y₁, ϵ₁ = sparse_fit(T, A, x, basis, opt; abstol) |
| 16 | + if ϵ₁ < abstol |
| 17 | + return y₁, ϵ₁ |
| 18 | + end |
| 19 | + |
| 20 | + y₂, ϵ₂ = find_singlet(T, A, basis; abstol) |
| 21 | + if ϵ₂ < abstol |
| 22 | + return y₂, ϵ₂ |
| 23 | + end |
| 24 | + |
| 25 | + if n < 8 # here, 8 is arbitrary and signifies a small basis |
| 26 | + y₃, ϵ₃ = find_dense(T, A, basis; abstol) |
| 27 | + if ϵ₃ < abstol |
| 28 | + return y₃, ϵ₃ |
| 29 | + end |
| 30 | + end |
| 31 | + |
| 32 | + # moving toward the poles |
| 33 | + modify_basis_matrix!(T, A, X, x, eq, basis, radius; abstol) |
| 34 | + y₄, ϵ₄ = sparse_fit(T, A, x, basis, opt; abstol) |
| 35 | + |
| 36 | + if ϵ₄ < abstol || ϵ₄ < ϵ₁ |
| 37 | + return y₄, ϵ₄ |
| 38 | + else |
| 39 | + return y₁, ϵ₁ |
| 40 | + end |
| 41 | +end |
| 42 | + |
| 43 | +function init_basis_matrix!(T, A, X, x, eq, basis, radius, complex_plane; abstol = 1e-6) |
| 44 | + n, m = size(A) |
| 45 | + k = 1 |
| 46 | + i = 1 |
| 47 | + |
| 48 | + eq_fun = fun!(eq, x) |
| 49 | + Δbasis_fun = deriv_fun!.(basis, x) |
| 50 | + |
| 51 | + while k <= n |
| 52 | + try |
| 53 | + x₀ = test_point(complex_plane, radius) |
| 54 | + X[k] = x₀ # move_toward_roots_poles(x₀, x, eq) |
| 55 | + b₀ = eq_fun(X[k]) |
| 56 | + |
| 57 | + if is_proper(b₀) |
| 58 | + for j in 1:m |
| 59 | + A[k, j] = Δbasis_fun[j](X[k]) / b₀ |
| 60 | + end |
| 61 | + if all(is_proper, A[k, :]) |
| 62 | + k += 1 |
| 63 | + end |
| 64 | + end |
| 65 | + catch e |
| 66 | + println("Error from init_basis_matrix!: ", e) |
| 67 | + end |
| 68 | + end |
| 69 | +end |
| 70 | + |
| 71 | +function move_toward_roots_poles(z, x, eq; n = 1, max_r = 100.0) |
| 72 | + eq_fun = fun!(eq, x) |
| 73 | + Δeq_fun = deriv_fun!(eq, x) |
| 74 | + is_root = rand() < 0.5 |
| 75 | + z₀ = z |
| 76 | + for i in 1:n |
| 77 | + dz = eq_fun(z) / Δeq_fun(z) |
| 78 | + if is_root |
| 79 | + z -= dz |
| 80 | + else |
| 81 | + z += dz |
| 82 | + end |
| 83 | + if abs(z) > max_r |
| 84 | + return z₀ |
| 85 | + end |
| 86 | + end |
| 87 | + return z |
| 88 | +end |
| 89 | + |
| 90 | +function modify_basis_matrix!(T, A, X, x, eq, basis, radius; abstol = 1e-6) |
| 91 | + n, m = size(A) |
| 92 | + eq_fun = fun!(eq, x) |
| 93 | + Δeq_fun = deriv_fun!(eq, x) |
| 94 | + Δbasis_fun = deriv_fun!.(basis, x) |
| 95 | + |
| 96 | + for k in 1:n |
| 97 | + # One Newton iteration toward the poles |
| 98 | + # note the + sign instead of the usual - in Newton-Raphson's method. This is |
| 99 | + # because we are moving toward the poles and not zeros. |
| 100 | + |
| 101 | + X[k] += eq_fun(X[k]) / Δeq_fun(X[k]) |
| 102 | + b₀ = eq_fun(X[k]) |
| 103 | + for j in 1:m |
| 104 | + A[k, j] = Δbasis_fun[j](X[k]) / b₀ |
| 105 | + end |
| 106 | + end |
| 107 | +end |
| 108 | + |
| 109 | + |
| 110 | +function sparse_fit(T, A, x, basis, opt; abstol = 1e-6) |
| 111 | + n = length(basis) |
| 112 | + # find a linearly independent subset of the basis |
| 113 | + l = find_independent_subset(A; abstol) |
| 114 | + A, basis, n = A[l, l], basis[l], sum(l) |
| 115 | + |
| 116 | + try |
| 117 | + b = ones(n) |
| 118 | + # q₀ = A \ b |
| 119 | + q₀ = DataDrivenDiffEq.init(opt, A, b) |
| 120 | + @views sparse_regression!(q₀, A, permutedims(b)', opt, maxiter = 1000) |
| 121 | + ϵ = rms(A * q₀ - b) |
| 122 | + q = nice_parameter.(q₀) |
| 123 | + if sum(iscomplex.(q)) > 2 |
| 124 | + return nothing, Inf |
| 125 | + end # eliminating complex coefficients |
| 126 | + return sum(q[i] * expr(basis[i]) for i in 1:length(basis) if q[i] != 0; |
| 127 | + init = zero(x)), |
| 128 | + abs(ϵ) |
| 129 | + catch e |
| 130 | + println("Error from sparse_fit", e) |
| 131 | + return nothing, Inf |
| 132 | + end |
| 133 | +end |
| 134 | + |
| 135 | +function find_singlet(T, A, basis; abstol) |
| 136 | + σ = vec(std(A; dims = 1)) |
| 137 | + μ = vec(mean(A; dims = 1)) |
| 138 | + l = (σ .< abstol) .* (abs.(μ) .> abstol) |
| 139 | + if sum(l) == 1 |
| 140 | + k = findfirst(l) |
| 141 | + return nice_parameter(1 / μ[k]) * expr(basis[k]), σ[k] |
| 142 | + else |
| 143 | + return nothing, Inf |
| 144 | + end |
| 145 | +end |
| 146 | + |
| 147 | +function find_dense(T, A, basis; abstol = 1e-6) |
| 148 | + n = size(A, 1) |
| 149 | + b = ones(T, n) |
| 150 | + |
| 151 | + try |
| 152 | + q = A \ b |
| 153 | + if minimum(abs.(q)) > abstol |
| 154 | + ϵ = maximum(abs.(A * q .- b)) |
| 155 | + if ϵ < abstol |
| 156 | + y = sum(nice_parameter.(q) .* expr.(basis)) |
| 157 | + return y, ϵ |
| 158 | + end |
| 159 | + end |
| 160 | + catch e |
| 161 | + # |
| 162 | + end |
| 163 | + return nothing, Inf |
| 164 | +end |
0 commit comments