Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterative function generation in Python

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?

like image 492
user56643 Avatar asked Jan 21 '17 11:01

user56643


1 Answers

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.

like image 75
Right leg Avatar answered Sep 23 '22 03:09

Right leg