How to Integrate Using Simpsons Rule in Python? - python

I am trying to integrate this function: x^4 - 2x + 1 from 0 to 2
I wrote this program:
def f(x):
return (x**4)-(2*x)+1
N=10
a=0.0
b=2.0
h=(b-a)/N
s=f(a)+f(b)
for k in range(1,N/2):
s+=4*f(a+(2*k-1)*h)
for k in range(1,N/(2-1)):
s1 +=f(a+(2*k*h)
M=(s)+(2*s1)
print((1/3.0)*h)*(3)
But I got this error:
File "<ipython-input-29-6107592420b6>", line 17
M=(s)+(2*s1):
^
SyntaxError: invalid syntax
I tried writing it in different forms but I always get an error in M

You forgot a closing parentheses in your second for loop here: s1 += f(a+(2*k*h). It should be:
s1 += f(a + (2 * k * h)) # <<< here it is

For future reference you might also think about using scipy.integrate.
Look here for some methods which might have better accuracy depending on the nature and resolution of your data set.
A code might look like this:
import scipy.integrate as int
x = [ ii/10. for ii in range(21)]
y = [ xi**4 - 2*xi + 1 for xi in x]
tahdah = int.simps(y,x,even='avg')
print(tahdah)
Which yields and answer of 4.4 that you can confirm with pencil and paper.

Have you seen the code example of Simpsons Rule on Wikipedia (written in python)? I will repost it here for the benefit of future readers.
#!/usr/bin/env python3
from __future__ import division # Python 2 compatibility
def simpson(f, a, b, n):
"""Approximates the definite integral of f from a to b by the
composite Simpson's rule, using n subintervals (with n even)"""
if n % 2:
raise ValueError("n must be even (received n=%d)" % n)
h = (b - a) / n
s = f(a) + f(b)
for i in range(1, n, 2):
s += 4 * f(a + i * h)
for i in range(2, n-1, 2):
s += 2 * f(a + i * h)
return s * h / 3
# Demonstrate that the method is exact for polynomials up to 3rd order
print(simpson(lambda x:x**3, 0.0, 10.0, 2)) # 2500.0
print(simpson(lambda x:x**3, 0.0, 10.0, 100000)) # 2500.0
print(simpson(lambda x:x**4, 0.0, 10.0, 2)) # 20833.3333333
print(simpson(lambda x:x**4, 0.0, 10.0, 100000)) # 20000.0

Related

Approximating sin using the Taylor series

I'm trying to calculate sin(x) using Taylor series without using factorials.
import math, time
import matplotlib.pyplot as plot
def sin3(x, i=30):
x %= 2 * math.pi
n = 0
dn = x**2 / 2
for c in range(4, 2 * i + 4, 2):
n += dn
dn *= -x**2 / ((c + 1) * (c + 2))
return x - n
def draw_graph(start = -800, end = 800):
y = [sin3(i/100) for i in range(start, end)]
x = [i/100 for i in range(start, end)]
y2 = [math.sin(i/100) for i in range(start, end)]
x2 = [i/100 for i in range(start, end)]
plot.fill_between(x, y, facecolor="none", edgecolor="red", lw=0.7)
plot.fill_between(x2, y2, facecolor="none", edgecolor="blue", lw=0.7)
plot.show()
When you run the draw_graph function it uses matplotlib to draw a graph, the redline is the output from my sin3 function, and the blue line is the correct output from the math.sin method.
As you can see the curve is not quite right, it's not high or low enough (seems to peak at 0.5), and also has strange behavior where it generates a small peak around 0.25 then drops down again. How can I adjust my function to match the correct output of math.sin?
You have the wrong equation for sin(x), and you also have a messed up loop invariant.
The formula for sin(x) is x/1! - x^3/3! + x^5/5! - x^7/7!..., so I really don't know why you're initializing dn to something involving x^2.
You also want to ask yourself: What is my loop invariant? What is the value of dn when I reach the start of my loop. It is clear from the way you update dn that you expect it to be something involving x^i / i!. Yet on the very first iteration of the loop, i=4, yet dn involves x^2.
Here is what you meant to write:
def sin3(x, i=30):
x %= 2 * math.pi
n = 0
dn = x
for c in range(1, 2 * i + 4, 2):
n += dn
dn *= -x**2 / ((c + 1) * (c + 2))
return n

Graphing Intergration in Python

In the following code I have implemented Simpsons Rule in Python. I have attempted to plot the absolute error as a function of n for a suitable range of integer values n. I know that the exact result should be 1-cos(pi/2). However my graph doesn't seem to be correct. How can I fix my code to get the correct output? there were two loops and I don't think I implemented my graph coding correctly
def simpson(f, a, b, n):
"""Approximates the definite integral of f from a to b by the composite Simpson's rule, using n subintervals (with n even)"""
h = (b - a) / (n)
s = f(a) + f(b)
diffs = {}
for i in range(1, n, 2):
s += 4 * f(a + i * h)
for i in range(2, n-1, 2):
s += 2 * f(a + i * h)
r = s
exact = 1 - cos(pi/2)
diff = abs(r - exact)
diffs[n] = diff
ordered = sorted(diffs.items())
x,y = zip(*ordered)
plt.autoscale()
plt.loglog(x,y)
plt.xlabel("Intervals")
plt.ylabel("Error")
plt.show()
return s * h / 3
simpson(lambda x: sin(x), 0.0, pi/2, 100)
Your simpson method should just calculate the integral for a single value of n (as it does), but creating the plot for many values of n should be outside that method. as:
from math import pi, cos, sin
from matplotlib import pyplot as plt
def simpson(f, a, b, n):
"""Approximates the definite integral of f from a to b by the composite Simpson's rule, using 2n subintervals """
h = (b - a) / (2*n)
s = f(a) + f(b)
for i in range(1, 2*n, 2):
s += 4 * f(a + i * h)
for i in range(2, 2*n-1, 2):
s += 2 * f(a + i * h)
return s * h / 3
diffs = {}
exact = 1 - cos(pi/2)
for n in range(1, 100):
result = simpson(lambda x: sin(x), 0.0, pi/2, n)
diffs[2*n] = abs(exact - result) # use 2*n or n here, your choice.
ordered = sorted(diffs.items())
x,y = zip(*ordered)
plt.autoscale()
plt.loglog(x,y)
plt.xlabel("Intervals")
plt.ylabel("Error")
plt.show()

Handle summation of big binomials in Python

I need to compute this formula:
It is an approximation of this integral
but it doesn't matter, actually I just want to compute the value of Figure 1 with PYTHON, that's what the topic concerns.
K, alpha and sigma are fixed values within a single computation, usually:
0 <= k <= 99;
alpha = 3;
sigma = 2.
Below is how I am trying to compute such summation in python:
import decimal
from scipy.special import binom
def residual_time_mean(alpha, sigma=2, k=1):
prev_prec = decimal.getcontext().prec
D = decimal.Decimal
decimal.getcontext().prec = 128
a = float(alpha)
s = float(sigma)
sum1 = 0.0
sum2 = 0.0
sumD1 = D(0.0)
sumD2 = D(0.0)
for i in range(1, k + 1):
sum1 += binom(k, i) * ((-1) ** (i + 1)) * (s / ((a - 1) * i - 1.0))
sum2 += binom(k, i) * ((-1) ** (i + 1)) * s / ((a - 1) * i - 1.0)
sumD1 += D(binom(k, i)) * (D(-1.0) ** (D(i) + D(1.0))) * (D(s) / ((D(a) - D(1.0)) * D(i) - D(1.0)))
sumD2 += D(binom(k, i)) * (D(-1.0) ** (D(i) + D(1.0))) * D(s) / ((D(a) - D(1.0)) * D(i) - D(1.0))
decimal.getcontext().prec = prev_prec
return sum1, sum2, float(sumD1), float(sumD2)
Running
for k in [0, 1, 2, 4, 8, 20, 50, 99]:
print("k={} -> {}".format(k, residual_time_mean(3, 2, k)))
the outcome is:
k=0 -> (0.0, 0.0, 0.0, 0.0)
k=1 -> (2.0, 2.0, 2.0, 2.0)
k=2 -> (3.3333333333333335, 3.3333333333333335, 3.3333333333333335, 3.3333333333333335)
k=4 -> (5.314285714285714, 5.314285714285714, 5.314285714285714, 5.314285714285714)
k=8 -> (8.184304584304588, 8.184304584304583, 8.184304584304584, 8.184304584304584)
k=20 -> (13.952692275798238, 13.952692275795965, 13.95269227579524, 13.95269227579524)
k=50 -> (23.134878809207617, 23.13390225415814, 23.134078892910786, 23.134078892910786)
k=99 -> (265412075330.96634, 179529505602.9507, 17667813427.20196, 17667813427.20196)
You can see that starting from k=8 the results are different.
Making a multiplication before a division leads results of sum1 and sum2 to diverge a lot for k=99 for instance.
sum1 += binom(k, i) * ((-1) ** (i + 1)) * (s / ((a - 1) * i - 1.0))
sum2 += binom(k, i) * ((-1) ** (i + 1)) * s / ((a - 1) * i - 1.0)
With decimal this problem doesn't occur but the result is not correct at all.
Computing the summation on WolframAlpha
for k = 99
(Here is the link for the computation on WolframAlpha). It gives 33.3159488(...) while for my python function it is 17667813427.20196. I trust WolframAlpha since it makes something like symbolic computation, indeed it also returns the real value in form of a fraction.
for other k
Approximation problems (e.g. the value computed by Wolfram is different from the one computed in python by an order of magnitude of 10^0 or more) starts occurring from k~=60.
Moreover computing the integral (Figure 2) with scipy.integrate leads to similar approximation errors.
The question:
Do you have any suggestion to handle this computation? Increasing decimal precision doesn't seem to be helpful.
I've discovered the problem by myself:
Executing scipy.special.binom(99,50) gives
5.044567227278209e+28
while calculating binomial (99,50) on WolframAlpha gives
5.0445672272782096667406248628e+28
There is an absolute difference with an order of magnitude of 10^12.
That's why, for sure, results of python function are unreliable for high values of k. So I need to change how the binomial is computed.
I don't understand why you involve a numpy function here, and why you are converting to float objects. Really, for this formula, if your inputs are always integers, then simply stick with int and fractions.Fraction and your answers will always be exact. It is easy enough to implement your own binom function:
In [8]: def binom(n, k):
...: return (
...: factorial(n)
...: // (factorial(k)*factorial(n-k))
...: )
...:
Note, I used integer division: //. And finally, your summation:
In [9]: from fractions import Fraction
...: def F(k, a, s):
...: result = Fraction(0, 1)
...: for i in range(1, k+1):
...: b = binom(k, i)*pow(-1, i+1)
...: x = Fraction(s, (a-1)*i - 1)
...: result += b*x
...: return result
...:
And the results:
In [10]: F(99, 3, 2)
Out[10]: Fraction(47372953498165579239913571130715220654368322523193013011418, 1421930192463933435386372127473055337225260516259631545875)
Which seems correct based on wolfram-alpha...
Note, if say, alpha can be a non-integer, you could use decimal.Decimal for arbitary-precision floating point operations:
In [17]: from decimal import Decimal
...: def F(k, a, s):
...: result = Decimal('0')
...: for i in range(1, k+1):
...: b = binom(k, i)*pow(-1, i+1)
...: x = Decimal(s) / Decimal((a-1)*i - 1)
...: result += b*x
...: return result
...:
In [18]: F(99, 3, 2)
Out[18]: Decimal('33.72169506311642881389682714')
Let's up the precision:
In [20]: import decimal
In [21]: decimal.getcontext().prec
Out[21]: 28
In [22]: decimal.getcontext().prec = 100
In [23]: F(99, 3, 2)
Out[23]: Decimal('33.31594880623309576443774363783112352607484321721989160481537847749994248174570647797323789728798446')

Integrate a function by the trapezoidal rule- Python

Here is the homework assignment I'm trying to solve:
A further improvement of the approximate integration method from the last question is to divide the area under the f(x) curve into n equally-spaced trapezoids.
Based on this idea, the following formula can be derived for approximating the integral:
!(https://www.dropbox.com/s/q84mx8r5ml1q7n1/Screenshot%202017-10-01%2016.09.32.png?dl=0)!
where h is the width of the trapezoids, h=(b−a)/n, and xi=a+ih,i∈0,...,n, are the coordinates of the sides of the trapezoids. The figure above visualizes the idea of the trapezoidal rule.
Implement this formula in a Python function trapezint( f,a,b,n ). You may need to check and see if b > a, otherwise you may need to swap the variables.
For instance, the result of trapezint( math.sin,0,0.5*math.pi,10 ) should be 0.9979 (with some numerical error). The result of trapezint( abs,-1,1,10 ) should be 2.0
This is my code but It doesn't seem to return the right values.
For print ((trapezint( math.sin,0,0.5*math.pi,10)))
I get 0.012286334153465965, when I am suppose to get 0.9979
For print (trapezint(abs, -1, 1, 10))
I get 0.18000000000000002, when I am suppose to get 1.0.
import math
def trapezint(f,a,b,n):
g = 0
if b>a:
h = (b-a)/float(n)
for i in range (0,n):
k = 0.5*h*(f(a+i*h) + f(a + (i+1)*h))
g = g + k
return g
else:
a,b=b,a
h = (b-a)/float(n)
for i in range(0,n):
k = 0.5*h*(f(a + i*h) + f(a + (i + 1)*h))
g = g + k
return g
print ((trapezint( math.sin,0,0.5*math.pi,10)))
print (trapezint(abs, -1, 1, 10))
Essentially, your return g statement was indented, when it should not have been.
Also, I removed your duplicated code, so it would adhere to "DRY" "Don't Repeat Yourself" principle, which prevents errors, and keeps code simplified and more readable.
import math
def trapezint(f, a, b, n):
g = 0
if b > a:
h = (b-a)/float(n)
else:
h = (a-b)/float(n)
for i in range (0, n):
k = 0.5 * h * ( f(a + i*h) + f(a + (i+1)*h) )
g = g + k
return g
print ( trapezint( math.sin, 0, 0.5*math.pi, 10) )
print ( trapezint(abs, -1, 1, 10) )
0.9979429863543573
1.0000000000000002
This variation reduces the complexity of branches and reduces number of operations. The summation in last step is reduced to single operation on an array.
from math import pi, sin
def trapezoid(f, a, b, n):
if b < a:
a,b = b, a
h = (b - a)/float(n)
g = [(0.5 * h * (f(a + (i * h)) + f(a + ((i + 1) * h)))) for i in range(0, n)]
return sum(g)
assert trapezoid(sin, 0, 0.5*pi, 10) == 0.9979429863543573
assert trapezoid(abs, -1, 1, 10) == 1.0000000000000002

Simpson's Rule returning 0

I coded a function for Simpson's Rule of numerical integration. For values of n more than or equal to 34, the function returns 0.
Here, n is the number of intervals, a is the start point, and b is the end point.
import math
def simpsons(f, a,b,n):
x = []
h = (b-a)/n
for i in range(n+1):
x.append(a+i*h)
I=0
for i in range(1,(n/2)+1):
I+=f(x[2*i-2])+4*f(x[2*i-1])+f(x[2*i])
return I*(h/3)
def func(x):
return (x**(3/2))/(math.cosh(x))
x = []
print(simpsons(func,0,100,34))
I am not sure why this is happening. I also coded a function for the Trapezoidal Method and that does not return 0 even when n = 50. What is going on here?
Wikipedia has the code for Simpson's rule in Python :
from __future__ import division # Python 2 compatibility
import math
def simpson(f, a, b, n):
"""Approximates the definite integral of f from a to b by the
composite Simpson's rule, using n subintervals (with n even)"""
if n % 2:
raise ValueError("n must be even (received n=%d)" % n)
h = (b - a) / n
s = f(a) + f(b)
for i in range(1, n, 2):
s += 4 * f(a + i * h)
for i in range(2, n-1, 2):
s += 2 * f(a + i * h)
return s * h / 3
def func(x):
return (x**(3/2))/(math.cosh(x))

Categories

Resources