-
Notifications
You must be signed in to change notification settings - Fork 105
/
cubic.jl
155 lines (143 loc) · 4.74 KB
/
cubic.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
"""
CubicHermite
This type is purposely left undocumented since the interface is expected to
radically change in order to make it conform to the `AbstractInterpolation`
interface. Consider this API to be highly unstable and non-public, and that
breaking changes to this code could be made in a point release.
"""
struct CubicHermite
xs::Vector{Float64}
ys::Vector{Float64}
dydxs::Vector{Float64}
function CubicHermite(xs, ys, dydxs)
if length(xs) < 2
throw(DomainError(
"There must be at least two samples in the domain of the CubicHermite interpolator.",
))
end
x = xs[1]
for i = 2:length(xs)
if xs[i] ≤ x
throw(DomainError("The list of abscissas must be strictly increasing."))
end
x = xs[i]
end
if (length(xs) != length(ys) || length(ys) != length(dydxs))
throw(DomainError(
"There must be exactly the same number of abscissas as ordinates and derivatives.",
))
end
new(xs, ys, dydxs)
end
end
function (ch::CubicHermite)(x::Float64)::Float64
if x ≤ ch.xs[1]
if x == ch.xs[1]
return ch.ys[1]
end
throw(DomainError(
"Requested abscissas $x is less than the minimum value of the domain of the interpolator.",
))
end
if x ≥ last(ch.xs)
if x == last(ch.xs)
return last(ch.ys)
end
throw(DomainError(
"Requested abscissa $x is greater than the maximum value of the domain of the interpolator.",
))
end
# searchsorted is convenient but it is a little too general for this usecase.
# We have asserted that the xs's are strictly increasing.
# Ostensibly there is another function that could do this more cleanly.
urange = searchsorted(ch.xs, x)
# urange.start exceeds urange.stop in this case which again indicates that
# searchsorted is not quite the right tool for this job:
i = urange.stop
x1 = ch.xs[i]
x2 = ch.xs[i+1]
@assert x1 ≤ x ≤ x2
h = x2 - x1
y1 = ch.ys[i]
y2 = ch.ys[i+1]
dydx1 = ch.dydxs[i]
dydx2 = ch.dydxs[i+1]
# Corless, A Graduate Introduction to Numerical Methods, equation 14.10:
c1 = (2x - 3x1 + x2) * (x - x2) * (x - x2) / (h * h * h)
c2 = -(2x - 3x2 + x1) * (x - x1) * (x - x1) / (h * h * h)
d1 = (x - x1) * (x - x2) * (x - x2) / (h * h)
d2 = (x - x2) * (x - x1) * (x - x1) / (h * h)
c1 * y1 + c2 * y2 + d1 * dydx1 + d2 * dydx2
end
function gradient(ch::CubicHermite, x::Float64)::Float64
if x ≤ ch.xs[1]
if x == ch.xs[1]
return ch.dydxs[1]
end
throw(DomainError(
"Requested abscissas $x is less than the minimum value of the domain of the interpolator.",
))
end
if x ≥ last(ch.xs)
if x == last(ch.xs)
return last(ch.dydxs)
end
throw(DomainError(
"Requested abscissa $x is greater than the maximum value of the domain of the interpolator.",
))
end
urange = searchsorted(ch.xs, x)
i = urange.stop
x1 = ch.xs[i]
x2 = ch.xs[i+1]
@assert x1 ≤ x ≤ x2
h = x2 - x1
y1 = ch.ys[i]
y2 = ch.ys[i+1]
dydx1 = ch.dydxs[i]
dydx2 = ch.dydxs[i+1]
# Corless, A Graduate Introduction to Numerical Methods, equation 14.11:
c1 = 6 * (x - x2) * (x - x1) / (h * h * h)
d1 = (x - x2) * (3x - 2x1 - x2) / (h * h)
d2 = (x - x1) * (3x - 2x2 - x1) / (h * h)
c1 * (y1 - y2) + d1 * dydx1 + d2 * dydx2
end
function hessian(ch::CubicHermite, x::Float64)::Float64
if x < ch.xs[1]
throw(DomainError(
"Requested abscissas $x is less than the minimum value of the domain of the interpolator.",
))
end
if x ≥ last(ch.xs)
if x == last(ch.xs)
h = ch.xs[end] - ch.xs[end-1]
c1 = 6 / (h * h)
d1 = 2 / h
d2 = 4 / h
return c1 * (ch.ys[end-1] - ch.ys[end]) +
d1 * ch.dydxs[end-1] +
d2 * ch.dydxs[end]
end
throw(DomainError(
"Requested abscissa $x is greater than the maximum value of the domain of the interpolator.",
))
end
urange = searchsorted(ch.xs, x)
i = urange.stop
x1 = ch.xs[i]
x2 = ch.xs[i+1]
@assert x1 ≤ x ≤ x2
h = x2 - x1
y1 = ch.ys[i]
y2 = ch.ys[i+1]
dydx1 = ch.dydxs[i]
dydx2 = ch.dydxs[i+1]
# Corless, A Graduate Introduction to Numerical Methods, equation 14.12:
c1 = 6 * (2x - x1 - x2) / (h * h * h)
d1 = 2 * (3x - 2x2 - x1) / (h * h)
d2 = 2 * (3x - 2x1 - x2) / (h * h)
c1 * (y1 - y2) + d1 * dydx1 + d2 * dydx2
end
export CubicHermite
export gradient
export hessian