-
Notifications
You must be signed in to change notification settings - Fork 4
/
compact_radial_basis.py
218 lines (196 loc) · 7.99 KB
/
compact_radial_basis.py
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
#!/usr/bin/env python
u"""
compact_radial_basis.py
Written by Tyler Sutterley (05/2022)
Interpolates data using compactly supported radial basis functions
of minimal degree (Wendland functions) and sparse matrix algebra
Wendland functions have the form
p(r) if 0 <= r <= 1
0 if r > 1
where p represents a univariate polynomial
CALLING SEQUENCE:
ZI = compact_radial_basis(xs, ys, zs, XI, YI, dimension, order,
smooth=smooth, radius=radius, method='wendland')
INPUTS:
xs: scaled input X data
ys: scaled input Y data
zs: input data
XI: scaled grid X for output ZI
YI: scaled grid Y for output ZI
dimension: spatial dimension of Wendland function (d)
order: smoothness order of Wendland function (k)
OUTPUTS:
ZI: interpolated data grid
OPTIONS:
smooth: smoothing weights
radius: scaling factor for the basis function (the radius of the
support of the function)
method: compactly supported radial basis function
buhmann (not yet implemented)
wendland (default)
wu (not yet implemented)
PYTHON DEPENDENCIES:
numpy: Scientific Computing Tools For Python
https://numpy.org
scipy: Scientific Tools for Python
https://docs.scipy.org/doc/
REFERENCES:
Holger Wendland, "Piecewise polynomial, positive definite and compactly
supported radial functions of minimal degree." Advances in
Computational Mathematics, 1995.
Holger Wendland, "Scattered Data Approximation", Cambridge Monographs on
Applied and Computational Mathematics, 2005.
Martin Buhmann, "Radial Basis Functions", Cambridge Monographs on
Applied and Computational Mathematics, 2003.
UPDATE HISTORY:
Updated 05/2022: updated docstrings to numpy documentation format
Updated 01/2022: added function docstrings
Updated 02/2019: compatibility updates for python3
Updated 09/2017: using rcond=-1 in numpy least-squares algorithms
Updated 08/2016: using format text within ValueError, edit constant vector
removed 3 dimensional option of radial basis (spherical)
changed hierarchical_radial_basis to compact_radial_basis using
compactly-supported radial basis functions and sparse matrices
added low-order polynomial option (previously used default constant)
Updated 01/2016: new hierarchical_radial_basis function
that first reduces to points within distance. added cutoff option
Updated 10/2014: added third dimension (spherical)
Written 08/2014
"""
from __future__ import print_function, division
import numpy as np
import scipy.sparse
import scipy.sparse.linalg
import scipy.spatial
def compact_radial_basis(xs, ys, zs, XI, YI, dimension, order, smooth=0.,
radius=None, method='wendland'):
"""
Interpolates a sparse grid using compactly supported radial basis
functions of minimal degree and sparse matrix algebra
Parameters
----------
xs: float
scaled input x-coordinates
ys: float
scaled input y-coordinates
zs: float
input data
XI: float
scaled output x-coordinates for data grid
YI: float
scaled output y-coordinates for data grid
dimension: int
spatial dimension of Wendland function (d)
order: int
smoothness order of Wendland function (k)
smooth: float, default 0.0
smoothing weights
radius: float or NoneType, default None
scaling factor for the basis function
method: str, default `wendland`
compactly supported radial basis function
* ``'wendland'``
Returns
-------
ZI: float
interpolated data grid
References
----------
.. [Buhmann2003] M. Buhmann, "Radial Basis Functions",
*Cambridge Monographs on Applied and Computational
Mathematics*, (2003).
.. [Wendland1995] H. Wendland, "Piecewise polynomial,
positive definite and compactly supported radial
functions of minimal degree," *Advances in
Computational Mathematics*, 4, 389--396, (1995).
`doi: 10.1007/BF02123482 <https://doi.org/10.1007/BF02123482>`_
.. [Wendland2005] H. Wendland, "Scattered Data Approximation",
*Cambridge Monographs on Applied and Computational Mathematics*,
(2005).
"""
# remove singleton dimensions
xs = np.squeeze(xs)
ys = np.squeeze(ys)
zs = np.squeeze(zs)
XI = np.squeeze(XI)
YI = np.squeeze(YI)
# size of new matrix
if (np.ndim(XI) == 1):
nx = len(XI)
else:
nx, ny = np.shape(XI)
# Check to make sure sizes of input arguments are correct and consistent
if (len(zs) != len(xs)) | (len(zs) != len(ys)):
raise Exception('Length of input arrays must be equal')
if (np.shape(XI) != np.shape(YI)):
raise Exception('Shape of output arrays must be equal')
# create python dictionary of compact radial basis function formulas
radial_basis_functions = {}
# radial_basis_functions['buhmann'] = buhmann
radial_basis_functions['wendland'] = wendland
# radial_basis_functions['wu'] = wu
# check if formula name is listed
if method in radial_basis_functions.keys():
cRBF = radial_basis_functions[method]
else:
raise ValueError(f"Method {method} not implemented")
# construct kd-tree for Data points
kdtree = scipy.spatial.cKDTree(np.c_[xs, ys])
if radius is None:
# quick nearest-neighbor lookup to calculate mean radius
ds, _ = kdtree.query(np.c_[xs, ys], k=2)
radius = 2.0*np.mean(ds[:, 1])
# Creation of data-data distance sparse matrix in COOrdinate format
Rd = kdtree.sparse_distance_matrix(kdtree, radius,
output_type='coo_matrix')
# calculate ratio between data-data distance and radius
# replace cases where the data-data distance is greater than the radius
r0 = np.where(Rd.data < radius, Rd.data/radius, radius/radius)
# calculation of model PHI
PHI = cRBF(r0, dimension, order)
# construct sparse radial matrix
PHI = scipy.sparse.coo_matrix((PHI, (Rd.row, Rd.col)), shape=Rd.shape)
# Augmentation of the PHI Matrix with a smoothing factor
if (smooth != 0):
# calculate eigenvalues of distance matrix
eig = scipy.sparse.linalg.eigsh(Rd, k=1, which="LA", maxiter=1000,
return_eigenvectors=False)[0]
PHI += scipy.sparse.identity(len(xs), format='coo') * smooth * eig
# Computation of the Weights
w = scipy.sparse.linalg.spsolve(PHI, zs)
# construct kd-tree for Mesh points
# Data to Mesh Points
mkdtree = scipy.spatial.cKDTree(np.c_[XI.flatten(), YI.flatten()])
# Creation of data-mesh distance sparse matrix in COOrdinate format
Re = kdtree.sparse_distance_matrix(mkdtree, radius,
output_type='coo_matrix')
# calculate ratio between data-mesh distance and radius
# replace cases where the data-mesh distance is greater than the radius
R0 = np.where(Re.data < radius, Re.data/radius, radius/radius)
# calculation of the Evaluation Matrix
E = cRBF(R0, dimension, order)
# construct sparse radial matrix
E = scipy.sparse.coo_matrix((E, (Re.row, Re.col)), shape=Re.shape)
# calculate output interpolated array (or matrix)
if (np.ndim(XI) == 1):
ZI = E.transpose().dot(w[:, np.newaxis])
else:
ZI = np.zeros((nx, ny))
ZI[:, :] = E.transpose().dot(w[:, np.newaxis]).reshape(nx, ny)
# return the interpolated array (or matrix)
return ZI
# define compactly supported radial basis function formulas
def wendland(r, d, k):
# Wendland functions of dimension d and order k
# can replace with recursive method of Wendland for generalized case
L = (d//2) + k + 1
if (k == 0):
f = (1. - r)**L
elif (k == 1):
f = (1. - r)**(L + 1)*((L + 1.)*r + 1.)
elif (k == 2):
f = (1. - r)**(L + 2)*((L**2 + 4.*L + 3.)*r**2 + (3.*L + 6.)*r + 3.)
elif (k == 3):
f = (1. - r)**(L + 3)*((L**3 + 9.*L**2 + 23.*L + 15.)*r**3 +
(6.*L**2 + 36.*L + 45.)*r**2 + (15.*L + 45.)*r + 15.)
return f