Suppose we have a function like the following below.
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
def f(x) :
return x * np.tanh(50.0*(x-0.5)) + 1.0
x = np.linspace(0,1,100)
plt.plot(x,f(x));
Some parts change slowly (or change uniformly), and some parts change quickly.
Question, how would you set the step size to get an accurate integral using the trapazoid method?
Question, how do you know where to put the variable widths?
Question, what if you want adaptive trapazoid widths for *any* function, even ones you don't know yet? * That is, for any function that a user might supply. * How can you make your code decide?
Answers
*All of these are pretty complicated, there is a simpler way*
def adapt_trap(f, a, b, tol) :
m = (a+b)/2 # midpoint
global store_m; store_m += [m] # store the midpoint for plotting below
I1 = (b-a)*(f(b)+f(a))/2 # 1 trap
I2 = (m-a)*(f(a)+f(m))/2 + (b-m)*(f(m)+f(b))/2 # 2 traps
if np.abs((I1-I2)/I2) <= tol : # done if I1, I2 are close
return I2
else : # recursive: integrate the 2 sub intervals
return adapt_trap(f,a,m,tol) + adapt_trap(f,m,b,tol)
store_m = [] # initialize the storage array for plotting
I = adapt_trap(f, 0, 1, 1.0E-2)
print(f'I_adpt = {I:.5f}')
I_adpt = 1.24953
x = np.array(store_m)
plt.plot(x,f(x),'o')
plt.vlines(x,0.4,2.0);