Skip to content

Commit 8b401ba

Browse files
committed
more rbf and test
1 parent 663438f commit 8b401ba

File tree

3 files changed

+50
-24
lines changed

3 files changed

+50
-24
lines changed

src/numericalnim/interpolate.nim

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import arraymancer, cdt/[dt, vectors, edges, types]
33
import
44
./utils,
55
./common/commonTypes,
6-
./private/arraymancerOverloads
6+
./private/arraymancerOverloads,
7+
./rbf
8+
9+
export rbf
710

811
type
912
InterpolatorType*[T] = ref object

src/numericalnim/rbf.nim

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,46 +3,56 @@ import arraymancer
33

44

55
type
6+
RbfFunc* = proc (r: Tensor[float], epsilon: float): Tensor[float]
67
RbfType*[T] = object
78
points*: Tensor[float] # (n_points, n_dim)
89
values*: Tensor[T] # (n_points, n_values)
910
coeffs*: Tensor[float] # (n_points, n_values)
1011
epsilon*: float
12+
f*: RbfFunc
1113

14+
# Idea: blocked distance matrix for better cache friendliness
1215
proc distanceMatrix(p1, p2: Tensor[float]): Tensor[float] =
1316
## Returns distance matrix of shape (n_points, n_points)
14-
let n_points = p1.shape[0]
17+
let n_points1 = p1.shape[0]
18+
let n_points2 = p2.shape[0]
1519
let n_dims = p1.shape[1]
16-
result = newTensor[float](n_points, n_points)
17-
for i in 0 ..< n_points:
18-
for j in 0 ..< n_points:
20+
result = newTensor[float](n_points2, n_points1)
21+
for i in 0 ..< n_points2:
22+
for j in 0 ..< n_points1:
1923
var r2 = 0.0
2024
for k in 0 ..< n_dims:
21-
let diff = p1[i,k] - p2[j,k]
25+
let diff = p2[i,k] - p1[j,k]
2226
r2 += diff * diff
2327
result[i, j] = sqrt(r2)
2428

2529
proc compactRbfFunc*(r: Tensor[float], epsilon: float): Tensor[float] =
2630
result = map_inline(r):
2731
(1 - x/epsilon) ^ 4 * (4*x/epsilon + 1) * float(x < epsilon)
2832

29-
proc newRbf*[T](points: Tensor[float], values: Tensor[T], rbfFunc: proc (r: Tensor[float], epsilon: float): Tensor[float] = compactRbfFunc, epsilon: float = 1): RbfType[T] =
33+
proc newRbf*[T](points: Tensor[float], values: Tensor[T], rbfFunc: RbfFunc = compactRbfFunc, epsilon: float = 1): RbfType[T] =
3034
assert points.shape[0] == values.shape[0]
3135
let dist = distanceMatrix(points, points)
3236
let A = rbfFunc(dist, epsilon)
3337
let coeffs = solve(A, values)
34-
result = RbfType[T](points: points, values: values, coeffs: coeffs, epsilon: epsilon)
35-
36-
let x1 = @[@[0.0, 0.0, 0.0], @[1.0, 1.0, 0.0], @[1.0, 2.0, 0.0]].toTensor
37-
let x2 = @[@[0.0, 0.0, 1.0], @[1.0, 1.0, 2.0], @[1.0, 2.0, 3.0]].toTensor
38-
echo distanceMatrix(x1, x1)
39-
let values = @[0.0, 1.0, 2.0].toTensor
40-
echo newRbf(x1, values, epsilon=10)
41-
42-
import benchy
43-
44-
let pos = randomTensor(5000, 3, 1.0)
45-
let vals = randomTensor(5000, 1, 1.0)
46-
timeIt "Rbf":
47-
keep newRbf(pos, vals)
38+
result = RbfType[T](points: points, values: values, coeffs: coeffs, epsilon: epsilon, f: rbfFunc)
39+
40+
proc eval*[T](rbf: RbfType[T], x: Tensor[float]): Tensor[T] =
41+
let dist = distanceMatrix(rbf.points, x)
42+
let A = rbf.f(dist, rbf.epsilon)
43+
result = A * rbf.coeffs
44+
45+
when isMainModule:
46+
let x1 = @[@[0.0, 0.0, 0.0], @[1.0, 1.0, 0.0], @[1.0, 2.0, 0.0]].toTensor
47+
let x2 = @[@[0.0, 0.0, 1.0], @[1.0, 1.0, 2.0], @[1.0, 2.0, 3.0]].toTensor
48+
echo distanceMatrix(x1, x1)
49+
let values = @[0.0, 1.0, 2.0].toTensor
50+
echo newRbf(x1, values, epsilon=10)
51+
52+
import benchy
53+
54+
let pos = randomTensor(5000, 3, 1.0)
55+
let vals = randomTensor(5000, 1, 1.0)
56+
timeIt "Rbf":
57+
keep newRbf(pos, vals)
4858

tests/test_interpolate.nim

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import unittest, math, sequtils
1+
import unittest, math, sequtils, random
22
import numericalnim
33
import arraymancer
44

5-
65
proc f(x: float): float = sin(x)
76
proc df(x: float): float = cos(x)
87
let t = linspace(0.0, 10.0, 100)
@@ -410,4 +409,18 @@ test "Trilinear f = x*y*z T: Tensor[float]":
410409
for k in z:
411410
check abs(spline.eval(i, j, k)[0] - i*j*k) < 1e-12
412411
check abs(spline.eval(i, j, k)[1] - i*j*k) < 1e-12
413-
check abs(spline.eval(i, j, k)[2] - 1) < 1e-16
412+
check abs(spline.eval(i, j, k)[2] - 1) < 1e-16
413+
414+
test "rbf f=x*y*z":
415+
randomize(1337)
416+
let pos = randomTensor[float](100, 3, 1.0)
417+
let vals = pos[_, 0] *. pos[_, 1] *. pos[_, 2]
418+
let rbfObj = newRbf(pos, vals)
419+
420+
# We want test points in the interior to avoid the edges
421+
let xTest = meshgrid(arraymancer.linspace(0.1, 0.9, 10), arraymancer.linspace(0.1, 0.9, 10), arraymancer.linspace(0.1, 0.9, 10))
422+
let yTest = rbfObj.eval(xTest)
423+
let yCorrect = xTest[_, 0] *. xTest[_, 1] *. xTest[_, 2]
424+
for x in abs(yCorrect - yTest):
425+
check x < 0.16
426+
check mean_squared_error(yTest, yCorrect) < 2e-4

0 commit comments

Comments
 (0)