1- import std / [math, algorithm, tables]
1+ import std / [math, algorithm, tables, sequtils ]
22import arraymancer
3+ import ./ utils
34
45
56type
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
2727template km (point: Tensor [float ], index: int , delta: float ): int =
2828 int (ceil (point[0 , index] / delta))
2929
3030iterator 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+
3338proc 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+
3861proc 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
5678proc 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+
7095proc 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
81106proc 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
86114proc 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
100162when 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