Consider the following idea: I want to generate a sequence of functions f_k
,
for k = 1,...,50
and store them in a Python dictionary.
To give a concrete example, lets say
f_k(x) = f_{k-1}(x) * sqrt(x)
This is just an example, the problem I have is more complicated, but this does not matter for my question.
Because in my real problem f_{k-1}
is very noisy and contains rounding errors, I do not want to build f_k
directly from f_{k-1}
, but instead
I first approximate f_{k-1}
through a spline approximation, and then determine f_k
from that spline approximation. Strangely, this results in an error message saying that maximum recursion depth is exceeded. Here is an example of the code:
import numpy as np
from scipy.interpolate import interp1d
n = 50 # number of functions I want to create
args = np.linspace(1,4,20) # where to evaluate for spline approximation
fdict = dict() # dictionary that stores all the functions
fdict[0] = lambda x: x**2 # the first function
# generate function f_k as follows: First, take function f_{k-1} and
# approximate it through a spline. Multiply that spline approximation
# by sqrt(x) and store this as function f_k.
for k in range(1,n+1):
spline_approx = lambda x: interp1d( args,fdict[k-1](args) )(x)
fdict[k] = lambda x: spline_approx(x) * np.sqrt(x)
print('test evalutation: ', fdict[n](3))
This results in the error
RecursionError: maximum recursion depth exceeded
My problem must be very Python specific. It must have something to do with the interpolation through interp1d. For instance. If I replace the line
spline_approx = lambda x: interp1d( args,fdict[k-1](args) )(x)
by a polyfit
coefs = np.polyfit(args,fdict[k-1](args),10) # polyfit coefficients
spline_approx = lambda x: np.polyval(coefs,x) # approximation of f_{k-1}
the code runs fine. I suspect that the problem arises because fdict[k-1]
is not directly evaluated but only passed on as a reference. But how can I solve this issue?
The line that raises the RecursionError
is indeed:
spline_approx = lambda x: interp1d( args,fdict[k-1](args) )(x)
This line means that spline_approx
is the function that, given x
, returns interp1d(args, fdict[k-1](args)
evaluated in x
.
Since interp1d
returns the function that you want to put into spline_approx
, this line can be simplified into:
spline_approx = interp1d( args,fdict[k-1](args) )
which will stop throwing RecursionError
.
Why does your original code throw a RecursionError
?
In the original line, interp1d(args, fdict[k-1](args))
is not evaluated, because it is inside of a lambda
expression. This evaluation is postponed to the call of that lambda
expression.
In other words, every time you call a function from fdict
, all the previous functions must evaluate interp1d(args, fdict[k-1](args))
. The problem is that args
is a sequence, so fdict[k-1]
is called as many times as args
has elements.
The number of calls is of course exponential, since every function must evaluate the precedent function len(args)
times. Which results in a RecursionError
.
On the other hand, the new expression does evaluate interp1d(args, fdict[k-1](args))
. After this evaluation, a call to fdict[k]
will not trigger a call to fdict[k-1]
anymore.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With