How to efficiently pass function through?
Let's start by changing the code to output the current iteration:
_u = self.ufor t in range(0, self.T): print(t) lparams = np.random.randint(self.a, self.b, 6).reshape(3, 2).tolist() functions = [self._function_template(_u, *lparams[i]) for i in range(0, 3)] # evaluate functions pairs = list(itertools.combinations(functions, 2)) fval = [F(diff(*pairs[i]), self.a, self.b) for i in range(0, 3)] ind = np.sort(np.unique(np.random.randint(self.a, self.b, 10))) _u = _temp(ind, np.asarray(functions)[ind % 3])
Looking into the line causing the behaviour,
fval = [F(diff(*pairs[i]), self.a, self.b) for i in range(0, 3)]
functions of interest would be F
and diff
. The latter being straightforward, the former:
def F(f, a, b): try: brentq(f, a=a, b=b) except ValueError: pass
Hmm, swallowing exceptions, let's see what happens if we:
def F(f, a, b): brentq(f, a=a, b=b)
Immediately, for the first function and on the first iteration, an error is thrown:
ValueError: f(a) and f(b) must have different signs
Looking at the docs this is a prerequisite of the root finding function brentq
. Let's change the definition once more to monitor this condition on each iteration.
def F(f, a, b): try: brentq(f, a=a, b=b) except ValueError as e: print(e)
The output is
if(a) and f(b) must have different signsf(a) and f(b) must have different signsf(a) and f(b) must have different signs
for i
ranging from 0 to 57. Meaning, the first time the function F
ever does any real work is for i=58
. And it keeps doing so for higher values of i
.
Conclusion: it takes longer for these higher values, because:
- the root is never calculated for the lower values
- the number of calculations grows linear for
i>58
Your code is really far too complex to explain your problem - strive for something simpler. Sometimes you have to write code just to demonstrate the problem.
I'm taking a stab, based purely on your description rather than your code (although I ran the code and verified) . Here's your problem:
method eval: This is the core function to generate the blue, green and red versions every time. It takes three varying parameters each iteration: vfunction which is the dominating function from the previous step, m and s which are two parameters (flaots) affecting the shape of the resulting curve.
Your vfunction
parameter is more complex on each iteration. You are passing a nested function built up over previous iterations, which causes a recursive execution. Each iteration increases the depth of the recursive call.
How can you avoid this? There's no easy or built in way. The simplest answer is - assuming the inputs to these functions are consistent - to store the functional result (i.e. the numbers) rather than the function themselves. You can do this as long as you have a finite number of known inputs.
If the inputs to the underlying functions aren't consistent then there's no shortcut. You need to repeatedly evaluate those underlying functions. I see that you're doing some piecewise splicing of the underlying functions - you can test whether the cost of doing so exceeds the cost of simply taking the max
of each of the underlying functions.
The test that I ran (10 iterations) took a few seconds. I don't see that as a problem.