/
collocation.jl
238 lines (204 loc) · 6.61 KB
/
collocation.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
"""
A wrapper for the interpolation methods of DataInterpolations.jl.
$(SIGNATURES)
Wraps the methods in such a way that they are callable as `f(u,t)` to
create and return an interpolation of `u` over `t`.
The first argument of the constructor always defines the interpolation method,
all following arguments will be used in the interpolation.
The additional keyword `crop = false` indicates to discard the first and last element of the time series.
# Example
```julia
# Create the wrapper struct
itp_method = InterpolationMethod(QuadraticSpline)
# Create a callable interpolation
itp = itp_method(u,t)
# Return u[2]
itp(t[2])
```
"""
struct InterpolationMethod{T} <: AbstractInterpolationMethod
itp::T
args::Any
function InterpolationMethod(itp, args...)
return new{typeof(itp)}(itp, args)
end
end
(x::InterpolationMethod)(u, t) = x.itp(u, t, x.args...)
# TODO Wrap all types
# Wrap the common itps
InterpolationMethod() = InterpolationMethod(LinearInterpolation)
# Taken from DiffEqFlux
# https://github.com/SciML/DiffEqFlux.jl/blob/master/src/collocation.jl
# On 3-11-2021
struct EpanechnikovKernel <: CollocationKernel end
struct UniformKernel <: CollocationKernel end
struct TriangularKernel <: CollocationKernel end
struct QuarticKernel <: CollocationKernel end
struct TriweightKernel <: CollocationKernel end
struct TricubeKernel <: CollocationKernel end
struct GaussianKernel <: CollocationKernel end
struct CosineKernel <: CollocationKernel end
struct LogisticKernel <: CollocationKernel end
struct SigmoidKernel <: CollocationKernel end
struct SilvermanKernel <: CollocationKernel end
function calckernel(::EpanechnikovKernel, t)
if abs(t) > 1
return 0
else
return 0.75 * (1 - t^2)
end
end
function calckernel(::UniformKernel, t)
if abs(t) > 1
return 0
else
return 0.5
end
end
function calckernel(::TriangularKernel, t)
if abs(t) > 1
return 0
else
return (1 - abs(t))
end
end
function calckernel(::QuarticKernel, t)
if abs(t) > 0
return 0
else
return (15 * (1 - t^2)^2) / 16
end
end
function calckernel(::TriweightKernel, t)
if abs(t) > 0
return 0
else
return (35 * (1 - t^2)^3) / 32
end
end
function calckernel(::TricubeKernel, t)
if abs(t) > 0
return 0
else
return (70 * (1 - abs(t)^3)^3) / 80
end
end
function calckernel(::GaussianKernel, t)
exp(-0.5 * t^2) / (sqrt(2 * π))
end
function calckernel(::CosineKernel, t)
if abs(t) > 0
return 0
else
return (π * cos(π * t / 2)) / 4
end
end
function calckernel(::LogisticKernel, t)
1 / (exp(t) + 2 + exp(-t))
end
function calckernel(::SigmoidKernel, t)
2 / (π * (exp(t) + exp(-t)))
end
function calckernel(::SilvermanKernel, t)
sin(abs(t) / 2 + π / 4) * 0.5 * exp(-abs(t) / sqrt(2))
end
function construct_t1(t, tpoints)
hcat(ones(eltype(tpoints), length(tpoints)), tpoints .- t)
end
function construct_t2(t, tpoints)
hcat(ones(eltype(tpoints), length(tpoints)), tpoints .- t, (tpoints .- t) .^ 2)
end
function construct_w(t, tpoints, h, kernel)
W = @. calckernel((kernel,), (tpoints - t) / h) / h
Diagonal(W)
end
"""
$(SIGNATURES)
Unified interface for collocation techniques. The input can either be
a `CollocationKernel` (see list below) or a wrapped `InterpolationMethod` from
[DataInterpolations.jl](https://github.com/PumasAI/DataInterpolations.jl).
Computes a non-parametrically smoothed estimate of `u'` and `u`
given the `data`, where each column is a snapshot of the timeseries at
`tpoints[i]`.
# Examples
```julia
u′,u,t = collocate_data(data,tpoints,kernel=SigmoidKernel())
u′,u,t = collocate_data(data,tpoints,tpoints_sample,interp,args...)
u′,u,t = collocate_data(data,tpoints,interp)
```
# Collocation Kernels
See [this paper](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2631937/) for more information.
+ EpanechnikovKernel
+ UniformKernel
+ TriangularKernel
+ QuarticKernel
+ TriweightKernel
+ TricubeKernel
+ GaussianKernel
+ CosineKernel
+ LogisticKernel
+ SigmoidKernel
+ SilvermanKernel
# Interpolation Methods
See [DataInterpolations.jl](https://github.com/PumasAI/DataInterpolations.jl) for more information.
+ ConstantInterpolation
+ LinearInterpolation
+ QuadraticInterpolation
+ LagrangeInterpolation
+ QuadraticSpline
+ CubicSpline
+ BSplineInterpolation
+ BSplineApprox
+ Curvefit
"""
function collocate_data(data, tpoints, kernel = TriangularKernel(); crop = false, kwargs...)
_one = oneunit(first(data))
_zero = zero(first(data))
e1 = [_one; _zero]
e2 = [_zero; _one; _zero]
n = length(tpoints)
h = (n^(-1 / 5)) * (n^(-3 / 35)) * ((log(n))^(-1 / 16))
Wd = similar(data, n, size(data, 1))
WT1 = similar(data, n, 2)
WT2 = similar(data, n, 3)
x = map(tpoints) do _t
T1 = construct_t1(_t, tpoints)
T2 = construct_t2(_t, tpoints)
W = construct_w(_t, tpoints, h, kernel)
mul!(Wd, W, data')
mul!(WT1, W, T1)
mul!(WT2, W, T2)
(e2' * ((T2' * WT2) \ T2')) * Wd, (e1' * ((T1' * WT1) \ T1')) * Wd
end
estimated_derivative = reduce(hcat, transpose.(first.(x)))
estimated_solution = reduce(hcat, transpose.(last.(x)))
crop &&
return estimated_derivative[:, 2:(end - 1)], estimated_derivative[:, 2:(end - 1)],
tpoints[2:(end - 1)]
estimated_derivative, estimated_solution, tpoints
end
# Adapted to dispatch on InterpolationMethod
function collocate_data(data, tpoints, interp::InterpolationMethod; kwargs...)
collocate_data(data, tpoints, tpoints, interp; kwargs...)
end
function collocate_data(data::AbstractVector, tpoints::AbstractVector,
tpoints_sample::AbstractVector,
interp::InterpolationMethod; kwargs...)
u, du, tpoints = collocate_data(reshape(data, 1, :), tpoints, tpoints_sample, interp;
kwargs...)
return du[1, :], u[1, :], tpoints
end
# Adapted to dispatch on InterpolationMethod
function collocate_data(data::AbstractMatrix{T}, tpoints::AbstractVector{T},
tpoints_sample::AbstractVector{T}, interp::InterpolationMethod;
crop = false, kwargs...) where {T}
u = zeros(T, size(data, 1), length(tpoints_sample))
du = zeros(T, size(data, 1), length(tpoints_sample))
for d1 in 1:size(data, 1)
interpolation = interp(data[d1, :], tpoints)
u[d1, :] .= interpolation.(tpoints_sample)
du[d1, :] .= DataInterpolations.derivative.((interpolation,), tpoints_sample)
end
crop && return du[:, 2:(end - 1)], u[:, 2:(end - 1)], tpoints[2:(end - 1)]
return du, u, tpoints
end