Integrate Class - python

So I have been trying to create an integrate class for an assignment and while I have gotten a simple skeletal structure for the functions within said class, I keep on getting a None result, which really bugs me. The code that I have written down though is written below.
What do I do to make this code work?
import math
class Integrator():
def __init__(self, xMin, xMax, N):
x = []
self.xMin = min(x)
self.xMax = max(x)
self.N = N
def I(self, x):
(x**2)*np.exp(-x)*np.sin(x)
def integrate(self):
y = list(np.arange(self.xMin, self.xMax, self.N))
tot = 0
i = 0
while i < self.xMax:
tot += y [i]
i += self.N
np.sum(tot)*(self.xMax-self.xMin)/self.N
examp = Integrator(1,3,20000)
examp.integrate()

You're missing a return statement on the integrate and I methods. That is why they both exclusively return None.
Beyond that, there are some other issues. For example, the min and max statements here will not work. These operators do not work on an empty sequence (which is what x is). Perhaps you meant self.xMin = xMin?
class Integrator():
def __init__(self, xMin, xMax, N):
x = []
self.xMin = min(x)
self.xMax = max(x)
Beyond that, there are some curiosities with the integrate method. For example, the while loop will only do one iteration, because i < self.xMax (which is 3 in your example), but every iteration i gets incremented by self.N (which is 20000 in your example).
np.sum(tot) is also illogical, as that only works when tot is "array-like", but tot is just a float (or int). No need to sum one of those.
Then, list(np.arange(self.xMin, self.xMax, self.N)) likely does not do what you're expecting. np.arange is given a start, stop and step parameter. That means that it starts at 1 (self.xMin), then sets a step of 20000 (self.N), and then because that is larger than stop of 3 (self.xMax), it will not include that one. So, y = [1]. Maybe you'd want y = list(np.arange(self.xMin, self.xMax, 1 / self.N)), so that the step is such that y has a length of 40000.
That said, I have no idea what you're expecting to get returned from this method. Perhaps it's along the lines of this though:
import math
import numpy as np
class Integrator():
def __init__(self, xMin, xMax, N):
x = []
self.xMin = xMin
self.xMax = xMax
self.N = N
def I(self, x):
return (x**2)*np.exp(-x)*np.sin(x)
def integrate(self):
y = list(np.arange(self.xMin, self.xMax, 1 / self.N))
tot = 0
i = 0
while i < len(y):
tot += y[i]
i += 1
return tot*(self.xMax-self.xMin)/self.N
examp = Integrator(1,3,20000)
print(examp.integrate())
Which returns 7.999900000008443.

Related

How to find root of a function accepting error on f(x) instead of x?

I have a function like e. g.:
amountOfCalls = 0
def myFunc(x):
global amountOfCalls
amountOfCalls+=1
y = x ** 3 + x -5
return y
and want to find the root of it (i. e.: find the value of x where f(x) == 0).
If I use one of scipy optimizers like ridder, I can set only the relative or absolute tolerance of x but not of f(x).
Is there any possibility/alternative, where I could tell the optimizer to stop when abs(f(x)) has become smaller than e.g. 0.2 (as an absolute tolerance)?
Remark: As my function is in reality a black-box function, I have to stick to an optimizing approach (so analytical alternatives unfortunately do not work).
You could use the scipy.optimize.root for achieving this as described here.
You could use any of the mentioned methods on the above-mentioned link. I am choosing broyden1. In this method, you can set fatol to 0.2 as it is the absolute tolerance for the residual.
from scipy.optimize import root
amountOfCalls = 0
def myFunc(x):
global amountOfCalls
amountOfCalls+=1
y = x ** 3 + x -5
return y
x0 = 3
res = root(myFunc, x0, method='broyden1', options={'disp':True, 'fatol':0.2})
print(res)
The above code will terminate when myFunc(x) < 0.2.
If you want your condition to be explicitly abs(f(x)) < 0.2, then you could use the callback function as it is called at each iteration. You can then check at each iteration, if the condition is met and raise an Error to stop the root-finding method as follows:
from scipy.optimize import root
class Error(Exception):
pass
class AlmostReachedZeroError(Error):
pass
class rootFinder():
def __init__(self):
self.amountOfCalls = 0
def fun(self, x0):
y = x0 ** 3 + x0 - 5
return y
def callback(self, x, f):
# callback to terminate if condition is met
self.amountOfCalls+=1
if abs(self.fun(x[0])) < 0.2:
print("Current x: ", x[0], "Current fval: ", f[0],
"Iterations: ", self.amountOfCalls)
raise AlmostReachedZeroError("Terminating optimization: limit reached")
def find_root(self):
x0 = 3
opt = root(self.fun, x0, method='broyden1', callback=self.callback,
options={'disp':True, 'ftol':0.2})
return opt
rf = rootFinder()
res = rf.find_root()
print(res)
The above code will terminate the root-finding method if abs(f(x)) < 0.2, and will produce an Error. Also, the current x value, the current fval, and the iterations that the method took will be printed.
I might be misunderstanding your question, but this should do the trick:
global amountOfCalls
amountOfCalls+=1
y = x ** 3 + x -5
if y == 0:
return x
return y

Thoughts and question when implementing animation plot of Collatz Conjecture (3X+1)

Thanks to Veritasium great video about the topic, I was planning to do a quick replication of the animation he showed in the video where the number bouncing up and down until hit the number 1, depending on the initial number.
Below I figured out a version of code to implement the animation. But I have a question and confusion while constructing the code.
I found that if I don't initialize the y-data as y=np.empty(100) instead with a empty list, it will throw an error list assignment index out of range
So I'm very confused why I can't start with a empty list, because I know, depending on the value of y_start the len(y) varies. If I can collect those calculated y value into a list (converting them into array later) then I don't have to go the whole night-yard setting plt.xlim(1,100) (instead, I could just set plt.xlim(0,len(y)) also due to the potential remaining 0.0 value in the final y-data, I have to add additional condition (2nd after and) in the if statement -> if y[i] % 2 == 0 and y[i] != 0: Otherwise, it goes haywire even after y reached the value 1....
In addition, I want to add y-data's value displaying on top of the each individual point, but I have no clue how to implement that in the code...It would be greatly appreciate if anyone can help on this issue to make the animation looks more "complete"!
Here is the code that I've tested
import numpy as np
from matplotlib.animation import FuncAnimation
from matplotlib import pyplot as plt
def odd(num):
return (num*3)+1
def even(num):
return num // 2
y_start = 33
x = np.linspace(0, 100, 100)
# y = []
def collatz():
y = np.empty(100)
y[0] = y_start
for i in range(0,100):
if y[i] % 2 == 0 and y[i] != 0:
y[i+1] = even(y[i])
else:
y[i+1] = odd(y[i])
if y[i+1] == 1:
break
return y
y = collatz()
fig = plt.figure()
plt.xlim(1,100)
plt.ylim(1,max(y))
draw, = plt.plot([],[])
def update(idx):
draw.set_data(x[:idx], y[:idx])
return draw,
a = FuncAnimation(fig, update, frames=len(x), interval=90)
plt.show()
So again, my questions are,
why starting with an empty list to contain calculated y fails?
Also, in the early version, inside the collatz function definition, my code was like:
def collatz():
y = np.empty(100)
y[0] = y_start
for i in range(0,100):
while y[i] != 1:
if y[i] % 2 == 0 and y[i] != 0:
y[i+1] = even(y[i])
else:
y[i+1] = odd(y[i])
if y[i+1] == 1:
break
return y
The compilation freezes, the code seems undergoes an infinite cycle...I don't know, is it the usage of the while statement ? what should I do/add to correct it?
I have rewritten your code (works on my machine), and will try to answer your questions
You cannot start from an empty list for y because the collatz() function needs a starting point. Hence, if y is empty, there is nothing to start from and the function fails. In the new code below I have added a parameter start to your function (in the code: 49). This is now the new starting point of your function. Note that if you want a random starting point instead of one defined by you, you can delete start, and replace y = [start] with y = [int(np.random.randint(1, 100, 1))] or another code that draws a random integer.
Now collatz uses a while loop: it works as long as y is larger than 1 (hence for y = 0 or 1 it will stop). Note that the -1 operator means 'the last element added to y'. For each number it does the even() or odd() function, and then it adds the number to the list using append. This ensures that the list is only as long as it needs to be. Note that in this case a while loop is the best option since you don't know how long the loop will last. When you have a fixed amount of iterations, a for loop should be chosen.
Finally, x is determined based on the length of y.
from matplotlib.animation import FuncAnimation
from matplotlib import pyplot as plt
def odd(num):
return (num*3)+1
def even(num):
return num // 2
def collatz(start):
y = [start]
while y[-1] > 1:
if y[-1] % 2 == 0:
y.append(even(y[-1]))
else:
y.append(odd(y[-1]))
return y
y = collatz(49)
x = list(range(len(y)))
fig = plt.figure()
plt.xlim(1,len(y))
plt.ylim(1,max(y))
draw, = plt.plot([],[])
def update(idx):
draw.set_data(x[:idx], y[:idx])
return draw
a = FuncAnimation(fig, update, frames=len(x), interval=90)
plt.show()

Making python program for calculating integral

I have a Python program for calculating the integral of a function in just one point. But this is simply not effective as I want to calculate more x values at the same time, not just one value. Anyone have any idea?
from scitools.std import *
def trapezoidal(f,a,x,n):
h = (x-a)/float(n)
I = 0.5*f(a)
for i in iseq(1, n-1):
I += f(a+i*h)
I += 0.5*f(x)
I *= h
return I
class Integral:
def __init__(self, f, a, n=100):
self.f, self.a, self.n = f,a,n
def __call__(self,x):
return trapezoidal(self.f,self.a,x,self.n)

passing a function as an argument to a class

I have a function is given by :
import scipy.special
def p(z):
z0=1./3.;eta=1.0
value=eta*(z**2)*numpy.exp(-1*(z/z0)**eta)/scipy.special.gamma(3./eta)/z0**3
return value
I want to pass this function to the following class which is in the file called redshift_probability.py as an argument p:
import pylab
import numpy
import pylab
import numpy
class GeneralRandom:
"""This class enables us to generate random numbers with an arbitrary
distribution."""
def __init__(self, x = pylab.arange(-1.0, 1.0, .01), p = None, Nrl = 1000):
"""Initialize the lookup table (with default values if necessary)
Inputs:
x = random number values
p = probability density profile at that point
Nrl = number of reverse look up values between 0 and 1"""
if p == None:
p = pylab.exp(-10*x**2.0)
self.set_pdf(x, p, Nrl)
def set_pdf(self, x, p, Nrl = 1000):
"""Generate the lookup tables.
x is the value of the random variate
pdf is its probability density
cdf is the cumulative pdf
inversecdf is the inverse look up table
"""
self.x = x
self.pdf = p/p.sum() #normalize it
self.cdf = self.pdf.cumsum()
self.inversecdfbins = Nrl
self.Nrl = Nrl
y = pylab.arange(Nrl)/float(Nrl)
delta = 1.0/Nrl
self.inversecdf = pylab.zeros(Nrl)
self.inversecdf[0] = self.x[0]
cdf_idx = 0
for n in xrange(1,self.inversecdfbins):
while self.cdf[cdf_idx] < y[n] and cdf_idx < Nrl:
cdf_idx += 1
self.inversecdf[n] = self.x[cdf_idx-1] + (self.x[cdf_idx] - self.x[cdf_idx-1]) * (y[n] - self.cdf[cdf_idx-1])/(self.cdf[cdf_idx] - self.cdf[cdf_idx-1])
if cdf_idx >= Nrl:
break
self.delta_inversecdf = pylab.concatenate((pylab.diff(self.inversecdf), [0]))
def random(self, N = 1000):
"""Give us N random numbers with the requested distribution"""
idx_f = numpy.random.uniform(size = N, high = self.Nrl-1)
idx = pylab.array([idx_f],'i')
y = self.inversecdf[idx] + (idx_f - idx)*self.delta_inversecdf[idx]
return y
I don't know how to pass input argument x as an input parameter to function p(z) when I call the class
from redshift_probability import GeneralRandom
z_pdf=GeneralRandom()
If I do as following I get error:
z_pdf.set_pdf( x=numpy.arange(0, 1.5, .001),p(x),N=1000000)
How do I modify it?
I think you want to change GeneralRandom.__init__ to look like this:
def __init__(self, x = pylab.arange(-1.0, 1.0, .01), p_func=None, Nrl = 1000):
"""Initialize the lookup table (with default values if necessary)
Inputs:
x = random number values
p_func = function to compute probability density profile at that point
Nrl = number of reverse look up values between 0 and 1"""
if p_func is None:
self.p_val = pylab.exp(-10*x**2.0)
else:
self.p_val = p_func(x)
Then call it like this:
GeneralRandom(p_func=p)
That way, if you provide p_func it will be called with x as an argument, but if it's not provided, it gets set the same default as before. There's no need to call set_pdf explicitly, because it's called at the end of __init__.

How can I check to see the number of iterations Newton's method takes to run?

So basically I want to grab the number of iterations it takes my newton's method to find the root, and then take that number and apply it to my color scheme to make the longer the amount of iterations, the darker the color, and the fewer, the more full the color.
so here's my code
from numpy import *
import pylab as pl
def myffp(x):
return x**3 - 1, 3*(x**2)
def newton( ffp, x, nits):
for i in range(nits):
#print i,x
f,fp = ffp(x)
x = x - f/fp
return x
q = sqrt(3)/2
def leggo(xmin=-1,xmax=1,jmin=-1,jmax=1,pts=1000,nits=30):
x = linspace(xmin, xmax, pts)
y = linspace(jmin, jmax, pts)*complex(0,1)
x1,y1 = meshgrid(x,y)
n = newton(myffp,x1+y1,nits) #**here is where i wanna see the number of iterations newton's method takes to find my root**
r1 = complex(1,0)
r2 = complex(-.5, q)
r3 = complex(-.5,-q)
data = zeros((pts,pts,3))
data[:,:,0] = abs(n-r1) #**and apply it here**
data[:,:,2] = abs(n-r2)
data[:,:,1] = abs(n-r3)
pl.show(pl.imshow(data))
leggo()
The main problem is finding the number of iterations, I can then figure out how to apply that to darkening the color, but for now it's just finding the number of iterations it takes for each value ran through newton's method.
Perhaps the simplest way is to just refactor your newton function so that it keeps track of the total iterations and then returns it (along with the result, of course), e.g.,
def newton( ffp, x, nits):
c = 0 # initialize iteration counter
for i in range(nits):
c += 1 # increment counter for each iteration
f, fp = ffp(x)
x = x - f/fp
return x, c # return the counter when the function is called
so in the main body of your code, change your call to newton, like so:
res, tot_iter = newton(myffp, x, nits)
the number of iterations in the last call to newton is stored in tot_iter
As aside, your implementation of Newton's Method seems to be incomplete.
for instance, it's missing a test against some convergence criterion.
Here's a simple implementation in python that works:
def newtons_method(x_init, fn, max_iter=100):
"""
returns: approx. val of root of the function passed in, fn;
pass in: x_init, initial value for the root;
max_iter, total iteration count not exceeded;
fn, a function of the form:
def f(x): return x**3 - 2*x
"""
x = x_init
eps = .0001
# set initial value different from x_init so at lesat 1 loop
x_old = x + 10 * eps
step = .1
c = 0
# (x - x_old) is convergence criterion
while (abs(x - x_old) > eps) and (c < max_iter):
c += 1
fval = fn(x)
dfdx = (fn(x + step)) - fn(x) / step
x_old = x
x = x_old - fval / dfdx
return x, c
The code you're currently using for newton() has a fixed number of iterations (nits - which is being passed in as 30), so the results would be kind of trivial and uninteresting.
It looks like you're trying to generate a Newton fractal -- the method you're trying to use is incorrect; the typical coloring mode is based on the output of the function, not the number of iterations. See the Wikipedia article for a full explanation.

Categories