Skip to content

Commit 93ff2ac

Browse files
committed
Small optimisations to 1D caching
Exploit uniform spacing of samples to: * Avoid bisecting search for nearest index in sample array * Drop the array of sampled x points and calculate them analytically instead. This will reduce memory usage (useful for higher dimension caching objects).
1 parent e616dc6 commit 93ff2ac

2 files changed

Lines changed: 13 additions & 14 deletions

File tree

cherab/core/math/caching/caching1d.pxd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ cdef class Caching1D(Function1D):
2727
bint _no_boundary_error
2828
double _xmin, _xmax, _resolution
2929
unsigned char[::1] _sampled
30-
double[::1] _xsamples, _fsamples
30+
double[::1] _fsamples
3131
int _nsamples
3232

3333
cdef double _get_and_cache(self, int index) except? -1e999

cherab/core/math/caching/caching1d.pyx

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import numpy as np
2222

2323
cimport cython
24-
from raysect.core.math.cython.utility cimport find_index
2524
from raysect.core.math.cython.interpolation.cubic cimport calc_coefficients_1d, evaluate_cubic_1d
2625
from cherab.core.math.function cimport autowrap_function1d
2726

@@ -73,7 +72,6 @@ cdef class Caching1D(Function1D):
7372
"""
7473

7574
# The implementation works as follows:
76-
# - From space_area and resolution, generate an x grid of sampled points.
7775
# - Store a list of which points have already been evaluated, and their values.
7876
# - For each new value x to evaluate, check if the sample points either side
7977
# have already been evaluated.
@@ -84,6 +82,9 @@ cdef class Caching1D(Function1D):
8482
# - Use raysect.core.math.cython.interpolation.cubic's utilities to calculate
8583
# the polynomial coefficients for the 1D cubic and then evaluate the cubic
8684
# interpolation of the function at the position x.
85+
# - Note that the sampled points have uniform spacing: this enables a few
86+
# optimisations such as analytic calculations of the array index and x
87+
# values for the nearest samples to the requested x.
8788

8889
def __init__(self, function1d, space_area, resolution, no_boundary_error=False, function_boundaries=None):
8990
self._function = autowrap_function1d(function1d)
@@ -93,14 +94,9 @@ cdef class Caching1D(Function1D):
9394
if resolution <= 0:
9495
raise ValueError("Resolution must be greater than zero.")
9596
self._resolution = resolution
96-
nsamples = int((self._xmax - self._xmin) / resolution + 1)
97-
xsamples = np.linspace(self._xmin, self._xmax, nsamples)
98-
fsamples = np.empty_like(xsamples)
99-
sampled = np.full_like(fsamples, False, dtype='uint8')
100-
self._nsamples = nsamples
101-
self._xsamples = xsamples
102-
self._fsamples = fsamples
103-
self._sampled = sampled
97+
self._nsamples = int((self._xmax - self._xmin) / resolution + 1)
98+
self._fsamples = np.empty(self._nsamples)
99+
self._sampled = np.full(self._nsamples, False, dtype='uint8')
104100
self._no_boundary_error = no_boundary_error
105101
# TODO: normalise using function_boundaries to avoid numerical issues.
106102

@@ -111,7 +107,7 @@ cdef class Caching1D(Function1D):
111107
cdef double x, fsamp
112108

113109
if not self._sampled[index]:
114-
x = self._xsamples[index]
110+
x = self._xmin + index * self._resolution
115111
fsamp = self._function.evaluate(x)
116112
self._sampled[index] = True
117113
self._fsamples[index] = fsamp
@@ -135,7 +131,8 @@ cdef class Caching1D(Function1D):
135131
double dfdx[2] # Function derivative evaluations
136132

137133
if self._xmin <= x < self._xmax:
138-
lower_index = find_index(self._xsamples, x)
134+
# Sampling is uniform, so lower_index is determined analytically.
135+
lower_index = int((x - self._xmin) // self._resolution)
139136
f[0] = self._get_and_cache(lower_index)
140137
f[1] = self._get_and_cache(lower_index + 1)
141138
# Calculate derivatives. calc_coefficiets_1d requires derivatives
@@ -151,7 +148,9 @@ cdef class Caching1D(Function1D):
151148
fnext = self._get_and_cache(lower_index + 2)
152149
dfdx[1] = (fnext - f[0]) / 2
153150
calc_coefficients_1d(f, dfdx, a)
154-
xnorm = (x - self._xsamples[lower_index]) / self._resolution
151+
# lower_x = xmin + resolution * index
152+
# xnorm = (x - lower_x) / res = (x - xmin) / resolution - index
153+
xnorm = (x - self._xmin) / self._resolution - lower_index
155154
feval = evaluate_cubic_1d(a, xnorm)
156155
return feval
157156

0 commit comments

Comments
 (0)