passing a function as an argument to a class - python

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__.

Related

Faster exhaustive research using numpy

I'm trying to maximize the minimum between two function using exhaustive research, this solution work but loop in python consumes a lot of computing time. is there an efficient way to use numpy (mesh grid or vectorize) to solve this problem?
Code :
Functions below are used in the exhaustive research method
import numpy as np
def F1(x):
return (x/11)**10
def F2(x,y,z):
return z+x/y
def F3(x,y,z,a,b,c):
return ((x+y)**z)/((a-b)**c)
Exhaustive research method take 6 parameter (scalar or 1D array). for the moment I just want to compute my code on scalar, then I can use another function to browse those parameter if they are 1D array.
def B_F(P1, P2, P3,P4, P5, P6) :
# initializing my optimal parameters
a_Opt, b_opt, c_opt, obj_opt = 0, 0, 0, 0
# feasible set
a = np.linspace(0.0,1.0,10)
b = np.linspace(0.0,100.0,100)
c = np.linspace(0.0,100.0,100)
for i in a:
for j in b:
for k in c:
#if constraint is respected
if P1*k+P2*j+2*(i*k*j) <= F1(P3):
# calculate the max min of the two function
f_1 = F2(i,k,P6)
f_2 = F3(i,k,j,10,P4,P5)
min_f = np.minimum(f_1, f_2)
# extract optimal parameters and objective function
if obj_opt <= min_f :
a_Opt = i
b_opt = j
c_opt = k
obj_opt = min_f
exhaustive_research = np.array([[obj_opt, a_Opt, b_opt, c_opt]])
return exhaustive_research
You can do it this way:
A,B,C = np.meshgrid(a,b,c)
mask = P1*C+P2*B+2*(A*B*C) <= F1(P3)
A = A[mask]
B = B[mask]
C = C[mask]
f_1 = F2(A,C,P6)
f_2 = F3(A,C,B,10,P4,P5)
min_f = np.minimum(f_1, f_2)
ind = np.argmax(min_f)
obj_opt, a_Opt, b_opt, c_opt = min_f[ind], A[ind], B[ind], C[ind]

Appending structured data to a class attribute in Python

I have a couple of objects I'm using for running numerical simulations. A minimal example is shown below where there are two objects: 1) an Environment object which has two states (x and y) that it simulates stochastically through time; and 2) a Simulation object which manages the simulaton and saves the state of the Environment throughout the simulation.
Within the Simulation object, I want to save the state of the Environment both 1) through time, and 2) across multiple simulations. Through time I can use a defaultdict to save the state variables within a single simulation but across simulations it's not clear to me the best way to save the defaultdicts that have been generated. If I append to a list (without using copy) then the list returns all identical defaultdicts due to the mutability of lists. In the example below I use copy.copy, as the answer here suggests.
Are there approaches that are more "Pythonic"? Would it be better to use an immutable type to store the defaultdicts for each simulation?
import copy
from collections import defaultdict
import numpy as np, pandas as pd
from matplotlib import pyplot as plt
class Environment(object):
"""
Class representing a random walk of two variables x and y
Methods
-------
start_simulation: draw values from state variables from priors
step: add random noise to state variables
current_state: return current state of x and y in a dict
"""
def __init__(self, mu1, sigma1, mu2, sigma2):
self.mu1 = mu1
self.mu2 = mu2
self.sigma1 = sigma1
self.sigma2 = sigma2
def start_simulation(self):
self.x = self.mu1 + self.sigma1 * np.random.randn()
self.y = self.mu2 + self.sigma2 * np.random.randn()
def step(self):
self.x += self.sigma1 * np.random.randn()
self.y += self.sigma2 * np.random.randn()
def current_state(self):
return({"x": self.x, "y": self.y})
class Simulation(object):
"""
Class representing a simulation object for handling the Environment object
and storing data
Methods
-------
start_simulation: start the simulation; initialise state of the environment
simulate: generate n_simulations simulations of n_timesteps time steps each
save_state:
"""
def __init__(self, env, n_timesteps):
self.env = env
self.n_timesteps = n_timesteps
self.data_all = []
self.data_states = defaultdict(list)
def start_simulation(self):
self.timestep = 0
self.env.start_simulation()
# Append current data (if non empty)
if self.data_states:
self.data_all.append(copy.copy(self.data_states)) # <---------- this step
# without copy.copy this will return all elements of the list data_all to be the
# same default dict at the end of all simulations - lists are mutable
# Reset data_current
self.data_states = defaultdict(list)
def simulate(self, n_simulations):
"""
Run simulation for n_simulations and n_timesteps timesteps
"""
self.start_simulation()
for self.simulation in range(n_simulations):
self.timestep = 0
while(self.timestep < self.n_timesteps):
self.env.step()
self.save_state(self.env.current_state())
self.timestep += 1
self.start_simulation()
def save_state(self, state):
"""
Save results to a default dict
"""
for key, value in state.items():
self.data_states[key].append(value)
if __name__ == "__main__":
# Run 7 simulations, each for for 20 time steps
N_TIME = 20
N_SIM = 7
e = Environment(
mu1 = 1.4, sigma1 = 0.1,
mu2 = 2.6, sigma2 = 0.05)
s = Simulation(env = e, n_timesteps = N_TIME)
s.simulate(N_SIM)
# Plot output
fig, ax = plt.subplots()
for var, c in zip(["x", "y"], ["#D55E00", "#009E73"]):
[ax.plot(pd.DataFrame(d)[var], label = var, color = c) for d in s.data_all]
ax.set_xlabel("Time")
ax.set_ylabel("Value")
plt.show()

What went wrong with my Kruskal-Wallis class?

I was trying to build a class that could perform the Kruskal-Wallis test. The class uses the following formula to compute H:
However, it yields a different H-value than the kruskal function of scipy. Does anyone know why this is the case?
import numpy as np
from scipy.stats import rankdata
from scipy.stats import kruskal
class Kruskal_Wallis():
def __init__(self):
pass
def fit(self, groups):
"""
Performs Kruskal-Wallis test.
:param groups: list containing 1D group arrays
Adds the following attributes:
- n: size of total population
- n_groups: number of groups (n_groups = len(n_i) = len(r_i))
- n_i: array containing group sizes
- df: degrees of freedom
- r2_i: array containing the square of the sum of ranks for each group
- h: kruskal-wallis statistic
"""
def sum_ranks_per_group(groups):
n_groups = len(groups)
n_i = np.array([group.shape[0] for group in groups])
data = np.array([])
for group in groups:
data = np.concatenate((data, group), axis=0)
ranked_data = rankdata(data, method="average")
ranked_groups = ranked_data.reshape((n_groups, n_i[0])) #works only if groups have equal size
summed_ranks = ranked_groups.sum(axis=1)
return summed_ranks
def get_h(n, r2_i, n_i):
summed_r2_i_per_n_i = (r2_i/n_i).sum()
h = (12/(n*(n-1)) * summed_r2_i_per_n_i) - 3*(n+1)
return h
n_groups = len(groups)
n_i = np.array([group.shape[0] for group in groups])
n = sum(n_i)
df = n_groups - 1
r2_i = sum_ranks_per_group(groups)**2
h = get_h(n, r2_i, n_i)
self.n_groups = n_groups
self.n_i = n_i
self.n = n
self.df = df
self.r2_i = r2_i
self.h = h
## Compare results yielded by scipy.stats.kruskal and Kruskal_Wallis class
groups = [np.arange(1,3),
np.arange(3,5)]
res = kruskal(groups[0], groups[1])
kruskal_wallis = Kruskal_Wallis()
kruskal_wallis.fit(groups)
print(res)
print(kruskal_wallis.h)
the difference between the answers might be caused by the way python handles float type in the division operations.
Instead of using pythonic division (/) try using numpy's true division

Python: Numerov's method plotting error

I'm trying to solve the Schrödinger equation with the Numerov's method. Here is my code:
from pylab import *
from scipy.optimize import brentq
import numpy as np
l = float(input("Angular momentum l:"))
L = float(input("Width of the potential:"))
Vo = float(input("Value of the potential:"))
N = int(input("Number of steps (~10000):"))
h = float(3*L/N)
psi = np.zeros(N) #wave function
psi[0] = 0
psi[1] = h
def V(x,E):
"""
Effective potential function.
"""
if x > L:
return -2*E+l*(l+1)/x**2
else:
return -2*(Vo+E)+l*(l+1)/x**2
def Wavefunction(energy):
"""
Calculates wave function psi for the given value
of energy E and returns value at point xmax
"""
global psi
global E
E=energy
for i in range(2,N):
psi[i]=(2*(1+5*(h**2)*V(i*h,E)/12)*psi[i-1]-(1-(h**2)*V((i-1)*h,E)/12)*psi[i-2])/(1-(h**2)*V((i+1)*h,E)/12)
return psi[-1]
def find_energy_levels(x,y):
"""
Gives all zeroes in y = psi_max, x=en
"""
zeroes = []
s = np.sign(y)
for i in range(len(y)-1):
if s[i]+s[i+1] == 0: #sign change
zero = brentq(Wavefunction, x[i], x[i+1])
zeroes.append(zero)
return zeroes
def main():
energies = np.linspace(-Vo,0,int(10*Vo)) # vector of energies where we look for the stable states
psi_max = [] # vector of wave function at x = 3L for all of the energies in energies
for energy in energies:
psi_max.append(Wavefunction(energy)) # for each energy find the the psi_max at xmax
E_levels = find_energy_levels(energies,psi_max) # now find the energies where psi_max = 0
print ("Energies for the bound states are: ")
for E in E_levels:
print ("%.2f" %E)
# Plot the wavefunctions for first 4 eigenstates
x = np.linspace(0, 3*L, N)
figure()
for E in E_levels:
Wavefunction(E)
plot(x, psi, label="E = %.2f"%E)
legend(loc="upper right")
xlabel('r')
ylabel('$u(r)$', fontsize = 10)
grid()
savefig('numerov.pdf', bbox_inches='tight')
if __name__ == "__main__":
main()
Everything was working really well, this is a plot for Vo=35, l=1, but when I try whit a value of Vo=85, l=0 (is the same for Vo>50), the plot is not what I expected (the end of the plot blows up). For l=1, the error vanish. I am a novice in Python, so I do not know what would be the error. Thanks for the help.

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