6

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?

1 Answer 1

4

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Good answer. It is worth mentioning lexical closures.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.