Skip to content

Commit e9bab2e

Browse files
committed
progress on PU
1 parent 518fb76 commit e9bab2e

File tree

1 file changed

+87
-22
lines changed

1 file changed

+87
-22
lines changed

src/numericalnim/rbf.nim

Lines changed: 87 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import std / [math, algorithm, tables]
1+
import std / [math, algorithm, tables, sequtils]
22
import arraymancer
3+
import ./utils
34

45

56
type
@@ -20,21 +21,43 @@ type
2021

2122
RbfPUType*[T] = object
2223
limits*: tuple[upper: Tensor[float], lower: Tensor[float]]
23-
# Don't hard-code in the patches, this is just for lookup to construct the patches
24-
#
25-
grid*: seq[RbfType[T]]
24+
grid*: RbfGrid[RbfType[T]]
25+
nValues*: int
2626

2727
template km(point: Tensor[float], index: int, delta: float): int =
2828
int(ceil(point[0, index] / delta))
2929

3030
iterator neighbours*[T](grid: RbfGrid[T], k: int): int =
3131
discard
3232

33+
iterator neighboursIncludingCenter*[T](grid: RbfGrid[T], k: int): int =
34+
yield k
35+
for x in grid.neighbours(k):
36+
yield x
37+
3338
proc findIndex*[T](grid: RbfGrid[T], point: Tensor[float]): int =
3439
result = km(point, grid.gridDim - 1, grid.gridDelta) - 1
3540
for i in 0 ..< grid.gridDim - 1:
3641
result += (km(point, i, grid.gridDelta) - 1) * grid.gridSize ^ (grid.gridDim - i - 1)
3742

43+
proc constructMeshedPatches*[T](grid: RbfGrid[T]): Tensor[float] =
44+
meshgrid(@[arraymancer.linspace(0 + grid.gridDelta / 2, 1 - grid.gridDelta / 2, grid.gridSize)].cycle(grid.gridDim))
45+
46+
template dist2(p1, p2: Tensor[float]): float =
47+
var result = 0.0
48+
for i in 0 ..< p1.shape[1]:
49+
let diff = p1[0, i] - p2[0, i]
50+
result += diff * diff
51+
result
52+
53+
proc findAllWithin*[T](grid: RbfGrid[T], x: Tensor[float], rho: float): seq[int] =
54+
assert x.shape.len == 2 and x.shape[0] == 1
55+
let index = grid.findIndex(x)
56+
for k in grid.neighboursIncludingCenter(index):
57+
for i in grid.indices[k]:
58+
if dist2(x, grid.points[i, _]) <= rho*rho:
59+
result.add i
60+
3861
proc newRbfGrid*[T](points: Tensor[float], values: Tensor[T], gridSize: int = 0): RbfGrid[T] =
3962
let nPoints = points.shape[0]
4063
let nDims = points.shape[1]
@@ -51,7 +74,6 @@ proc newRbfGrid*[T](points: Tensor[float], values: Tensor[T], gridSize: int = 0)
5174
result.values = values
5275
result.points = points
5376

54-
5577
# Idea: blocked distance matrix for better cache friendliness
5678
proc distanceMatrix(p1, p2: Tensor[float]): Tensor[float] =
5779
## Returns distance matrix of shape (n_points, n_points)
@@ -67,6 +89,9 @@ proc distanceMatrix(p1, p2: Tensor[float]): Tensor[float] =
6789
r2 += diff * diff
6890
result[i, j] = sqrt(r2)
6991

92+
template compactRbfFuncScalar*(r: float, epsilon: float): float =
93+
(1 - r/epsilon) ^ 4 * (4*r/epsilon + 1) * float(r < epsilon)
94+
7095
proc compactRbfFunc*(r: Tensor[float], epsilon: float): Tensor[float] =
7196
result = map_inline(r):
7297
(1 - x/epsilon) ^ 4 * (4*x/epsilon + 1) * float(x < epsilon)
@@ -80,39 +105,79 @@ proc newRbf*[T](points: Tensor[float], values: Tensor[T], rbfFunc: RbfFunc = com
80105

81106
proc eval*[T](rbf: RbfType[T], x: Tensor[float]): Tensor[T] =
82107
let dist = distanceMatrix(rbf.points, x)
108+
echo dist
83109
let A = rbf.f(dist, rbf.epsilon)
110+
echo A
111+
echo "---------------"
84112
result = A * rbf.coeffs
85113

86114
proc scalePoint*(x: Tensor[float], limits: tuple[upper: Tensor[float], lower: Tensor[float]]): Tensor[float] =
87-
(x -. limits.lower) /. (limits.upper - limits.lower)
115+
let lower = limits.lower -. 0.01
116+
let upper = limits.upper +. 0.01
117+
(x -. lower) /. (upper - lower)
88118

89-
proc gridIndex*(limits: tuple[upper: Tensor[float], lower: Tensor[float]], gridSide: int): int =
90-
discard
91-
92-
proc newRbfPu*[T](points: Tensor[float], values: Tensor[T], gridSide: int, rbfFunc: RbfFunc = compactRbfFunc, epsilon: float = 1): RbfType[T] =
119+
proc newRbfPu*[T](points: Tensor[float], values: Tensor[T], gridSize: int = 0, rbfFunc: RbfFunc = compactRbfFunc, epsilon: float = 1): RbfPUType[T] =
120+
assert points.shape[0] == values.shape[0]
121+
assert points.shape.len == 2 and values.shape.len == 2
93122
let upperLimit = max(points, 0)
94123
let lowerLimit = min(points, 0)
95-
let limits = (upper: upperLimit, lower: lowerLimit)
124+
let limits = (upper: upperLimit, lower: lowerLimit) # move this buff to scalePoint
96125
let scaledPoints = points.scalePoint(limits)
97-
echo scaledPoints
98-
echo limits
126+
let dataGrid = newRbfGrid(scaledPoints, values, gridSize)
127+
let patchPoints = dataGrid.constructMeshedPatches()
128+
let nPatches = patchPoints.shape[0]
129+
var patchRbfs: seq[RbfType[T]] #= newTensor[RbfType[T]](nPatches, 1)
130+
var patchIndices: seq[int]
131+
for i in 0 ..< nPatches:
132+
let indices = dataGrid.findAllWithin(patchPoints[i, _], dataGrid.gridDelta)
133+
if indices.len > 0:
134+
patchRbfs.add newRbf(dataGrid.points[indices,_], values[indices, _], epsilon=dataGrid.gridDelta)
135+
patchIndices.add i
136+
137+
let patchGrid = newRbfGrid(patchPoints[patchIndices, _], patchRbfs.toTensor.unsqueeze(1), gridSize)
138+
result = RbfPUType[T](limits: limits, grid: patchGrid, nValues: values.shape[1])
139+
140+
proc eval*[T](rbf: RbfPUType[T], x: Tensor[float]): Tensor[T] =
141+
assert x.shape.len == 2
142+
assert (not ((x <=. rbf.limits.upper) and (x >=. rbf.limits.lower))).astype(int).sum() == 0, "Some of your points are outside the allowed limits"
143+
let nPoints = x.shape[0]
144+
let x = x.scalePoint(rbf.limits)
145+
result = newTensor[T](nPoints, rbf.nValues)
146+
for row in 0 ..< nPoints:
147+
let p = x[row, _]
148+
let indices = rbf.grid.findAllWithin(p, rbf.grid.gridDelta)
149+
if indices.len > 0:
150+
var c = 0.0
151+
for i in indices:
152+
let center = rbf.grid.points[i, _]
153+
let r = sqrt(dist2(p, center))
154+
let ci = compactRbfFuncScalar(r, rbf.grid.gridDelta)
155+
c += ci
156+
let val = rbf.grid.values[i, 0].eval(p)
157+
result[row, _] = result[row, _] + ci * val
158+
result[row, _] = result[row, _] / c
159+
else:
160+
result[row, _] = T(Nan) # allow to pass default value to newRbfPU?
99161

100162
when isMainModule:
101-
let x1 = @[@[0.0, 0.0, 0.0], @[1.0, 1.0, 0.0], @[1.0, 2.0, 4.0]].toTensor
163+
let x1 = @[@[0.01, 0.01, 0.01], @[1.0, 1.0, 0.0], @[1.0, 2.0, 4.0]].toTensor
102164
let x2 = @[@[0.0, 0.0, 1.0], @[1.0, 1.0, 2.0], @[1.0, 2.0, 3.0]].toTensor
103-
echo distanceMatrix(x1, x1)
104-
let values = @[0.0, 1.0, 2.0].toTensor
105-
echo newRbf(x1, values, epsilon=10)
165+
#echo distanceMatrix(x1, x1)
166+
let values = @[0.1, 1.0, 2.0].toTensor.unsqueeze(1)
167+
#echo newRbf(x1, values, epsilon=10)
106168

107169
import benchy
108170

109-
let pos = randomTensor(5000, 3, 1.0)
110-
let vals = randomTensor(5000, 1, 1.0)
171+
#let pos = randomTensor(5000, 3, 1.0)
172+
#let vals = randomTensor(5000, 1, 1.0)
111173
# timeIt "Rbf":
112174
# keep newRbf(pos, vals)
113175
let rbfPu = newRbfPu(x1, values, 3)
176+
echo rbfPu.grid.values[1, 0]
177+
#echo rbfPu.eval(x1[[2, 1, 0], _])
114178

179+
echo rbfPu.eval(sqrt x1)
115180
echo "----------------"
116-
let xGrid = [[0.1, 0.1], [0.2, 0.3], [0.9, 0.9], [0.4, 0.4]].toTensor
117-
let valuesGrid = @[0, 1, 9, 5].toTensor.reshape(4, 1)
118-
echo newRbfGrid(xGrid, valuesGrid, 3)
181+
#let xGrid = [[0.1, 0.1], [0.2, 0.3], [0.9, 0.9], [0.4, 0.4]].toTensor
182+
#let valuesGrid = @[0, 1, 9, 5].toTensor.reshape(4, 1)
183+
#echo newRbfGrid(xGrid, valuesGrid, 3)

0 commit comments

Comments
 (0)