Python code for Lagrange interpolation - determining the equation of the polynomial - python

The following code takes in a single value, x, and a list of points, X, and determines the value of the Lagrange polynomial through the list of points at the given x value.
def chunkIt(seq, num):
avg = len(seq) / float(num)
out = []
last = 0.0
while last < len(seq):
out.append(seq[int(last):int(last + avg)])
last += avg
return out
def product(list):
p = 1
for i in list:
p *= i
return p
def Lagrange(x,X):
T = np.zeros((2,len(X)))
list = []
for i in range(len(X)):
for j in range(len(X)):
if i != j:
list.append((x-X[j][0])/(X[i][0]-X[j][0]))
p = []
for i in chunkIt(list,len(X)):
p.append(product(i))
for i in range(len(X)):
T[0][i] = p[i]
T[1][i] = X[i][1]
list2 = []
for i in range(len(X)):
list2.append(T[0][i]*T[1][i])
return sum(list2)
For example:
x, X = 3, [[0,0],[1,1],[2,0.5]]
gives a value of -1.5.
How do I modify this code to determine the equation of the polynomial through the list of points? i.e. if I put x = 'x' as the input, I want it to return -0.75x**2 + 1.75x [for the given example]

import numpy as np
from pypoly import Polynomial
x, X = 3, [[0, 0], [1, 1], [2, 0.5]]
order = len(X)
This is the order of the resulting Lagrange polynomial. For your example, order is 3.
equations = np.array([[point[0] ** i for i in range(order)] for point in X])
values = np.array([point[1] for point in X])
coefficients = np.linalg.solve(equations, values)
This sets up simultaneous equations by substituting the points into a general polynomial. For order 3, the general polynomial is:
a * x ** 2 + b * x ** 1 + c * x ** 0 = y
It solves the system of simultaneous equations to find coefficients. For order 3, we get the values of a, b, c.
print 'coefficients', list(coefficients)
coefficients [0.0, 1.75, -0.75]
p = Polynomial(*coefficients)
Here, the * operator splits the elements of the array-like into individual values to be passed as arguments to Polynomial().
print p
1.75 * X - 0.75 * X**2
print p(x)
-1.5
To install PyPolynomial with pip, use:
for Python 2:
pip install PyPolynomial
for Python 3:
pip3 install PyPolynomial

Related

Optimal way to convolute continuous functions in python

I am trying to numerically compute in python integrals of the form
To that aim, I first define two discrete sets of x and t values, let's say
x_samples = np.linspace(-10, 10, 100)
t_samples = np.linspace(0, 1, 100)
dx = x_samples[1]-x_samples[0]
dt = t_samples[1]-t_samples[0]
declare symbolically that the function g(x,t) is equal to 0 if t<0 and discretise the two functions to integrate as
discretG = g(x_samples[None, :], t_samples[:, None])
discretH = h(x_samples[None, :], t_samples[:, None])
I have then tried to run
discretF = signal.fftconvolve(discretG, discretH, mode='full') * dx * dt
Yet, on basic test functions such as
g(x,t) = lambda x,t: np.exp(-np.abs(x))+t
h(x,t) = lambda x,t: np.exp(-np.abs(x))-t
I don't find an agreement between the the numerical integration and the convolution using scipy and I would like to have a fairly fast way of computing these integrals, especially when I only have access to discretised representations of the functions rather than their symbolic one.
According to your code, I assume you want to conduct convolution on two function g and h that are non-zero only on [a, b]*[m,n].
Of course you can use signal.fftconvolve to compute the convolution. The key is don't forget the transformation between the indices inside discretF and the real coordinates. Here I use interpolation to compute for arbitrary (x,t).
import numpy as np
from scipy import signal, interpolate
a = -1
b = 2
m = -10
n = 15
samples_num = 1000
x_eval_index = 200
t_eval_index = 300
x_samples = np.linspace(a, b, samples_num)
t_samples = np.linspace(m, n, samples_num)
dx = x_samples[1]-x_samples[0]
dt = t_samples[1]-t_samples[0]
g = lambda x,t: np.exp(-np.abs(x))+t
h = lambda x,t: np.exp(-np.abs(x))-t
discretG = g(x_samples[None, :], t_samples[:, None])
discretH = h(x_samples[None, :], t_samples[:, None])
discretF = signal.fftconvolve(discretG, discretH, mode='full')
def compute_f(x, t):
if x < 2*a or x > 2*b or t < 2*m or t > 2*n:
return 0
# use interpolation t get data on new point
x_samples_for_conv = np.linspace(2*a, 2*b, 2*samples_num-1)
t_samples_for_conv = np.linspace(2*m, 2*n, 2*samples_num-1)
f = interpolate.RectBivariateSpline(x_samples_for_conv, t_samples_for_conv, discretF.T)
return f(x, t)[0, 0] * dx * dt
Note: you can extend my codes to compute convolution on a meshgrid defined by x and y, where x and y are 1D array. (In my code, x and y are float now)
You can use the following code to explore the "agreement" between "the numerical integration" and "the convolution using scipy" (and also, the correctness of compute_f function above):
# how the convolve work
# for 1D f[i]=sigma_{j} g[j]h[i-j]
sum = 0
for y_idx, y in enumerate(x_samples[0:]):
for s_idx, s in enumerate(t_samples[0:]):
if x_eval_index - y_idx < 0 or t_eval_index - s_idx < 0:
continue
if t_eval_index - s_idx >= len(x_samples[0:]) or x_eval_index - y_idx >= len(t_samples[0:]):
continue
sum += discretG[t_eval_index - s_idx, x_eval_index - y_idx] * discretH[s_idx, y_idx] * dx * dt
print("Do discrete convolution manually, I get: %f" % sum)
print("Do discrete convolution using scipy, I get: %f" % (discretF[t_eval_index, x_eval_index] * dx * dt))
# numerical integral
# the x_val and t_val
# take 1D convolution as example, function defined on [a, b], and index of your samples range from [0, samples_num-1]
# after convolution, function defined on [2a, 2b], index of your samples range from [0, 2*samples_num-2]
dx_prime = (b-a) / (samples_num-1)
dt_prime = (n-m) / (samples_num-1)
x_eval = 2*a + x_eval_index * dx_prime
t_eval = 2*m + t_eval_index * dt_prime
sum = 0
for y in x_samples[:]:
for s in t_samples[:]:
if x_eval - y < a or x_eval - y > b:
continue
if t_eval - s < m or t_eval - s > n:
continue
if y < a or y >= b:
continue
if s < m or s >= n:
continue
sum += g(x_eval - y, t_eval - s) * h(y, s) * dx * dt
print("Do numerical integration, I get: %f" % sum)
print("The convolution result of 'compute_f' is: %f" % compute_f(x_eval, t_eval))
Which gives:
Do discrete convolution manually, I get: -154.771369
Do discrete convolution using scipy, I get: -154.771369
Do numerical integration, I get: -154.771369
The convolution result of 'compute_f' is: -154.771369

Using a list of floats for a loop

I'm trying to run a Runge-Kutta algorithm to approximate a differential equation. I want to go through a list of values for a constant variable, A, in the function and have the algorithm loop go through for each item in the list and produce a graph. I keep getting an error saying "list indices must be integers or slices but not a float". I tried to convert the numbers in the list to being integer fractions of each other but that didn't work either. I'm mostly unsure on how to circumvent this error as some fixes I found didn't work, here is my code:
import numpy as np
import matplotlib.pyplot as plt
from math import pi
from numpy import arange
from matplotlib.pyplot import plot,show
wo = 1
w = 2 #defining wo, w, g1, Amplitude and steps
h = 0.001
g1 = 0.2
A = [0.1,0.25,0.5,0.7,0.75,0.85,0.95,1.00,1.02,1.031,1.033,1.035,1.05]
for item in list(A): #Converting list items into Float values
[float(i) for i in A]
xpoints = arange(0,100,h)
tpoints = []
zpoints = []
t=0
x = 0
z = pi/2
for i in A: #Calls for items in Amplitude list to run algorighm
def F(t, z, x): #Defining the differential equation
return -g1 * z - (wo ** 2 + 2 * A[i] * np.cos(w * t)) * np.sin(x)
for x in xpoints:
tpoints.append(t)
zpoints.append(z)
m1 = z*h
k1 = h*F(t,z,x) #setting up the runge-kutta algorithm
m2 = h*(z+(k1/2))
k2 = h*F(t+0.5*m1,z+0.5*m1,x+0.5*h)
m3 = h*(z+0.5*k2)
k3 = h*F(t+0.5*m2,z+0.5*m2,x+0.5*h)
m4 = h*(z+0.5*k3)
k4 = h*F(t+0.5*m3,z+0.5*m3,x+0.5*h)
t += (m1+2*m2+2*m3+m4)/6
z += (k1+2*k2+2*k3+k4)/6
A += 1
plot(xpoints,zpoints)
The problem isn't that the numbers themselves need to be converted. Note how you iterate with for i in A:. This means that i is the actual value and not the index. So where you use A[i], you're trying to go to the 0.1 index of A. Instead, just replace A[i] with i in the line at the bottom of this snippet.
A = [0.1,0.25,0.5,0.7,0.75,0.85,0.95,1.00,1.02,1.031,1.033,1.035,1.05]
...
for i in A:
def F(t, z, x):
return -g1 * z - (wo ** 2 + 2 * A[i] * np.cos(w * t)) * np.sin(x)
Because the value of i is an element of A. If you want to loop index by index in A list:
for i in range(len(A))
this works.
This time, you get an error in A + = 1. I think this place will be i + = 1.

How to compute the Mass distribution in Python?

Mass distribution is defined as follows.
f is the probability density function of a continuous variable.
Given a set of data values, which are saved in a list, how to approximate this function? Since the integrals in the numerator and the denominator are identical to the expected value of a distribution, can we use the sample mean based approach as follows?
def get_mass_distribution(values):
x = np.linspace(0, max(values), max(values))
mean = sum(values)/len(values)
mass = []
values.sort()
for i in range(len(values)):
mass.append(sum(values[0:i+1])/(mean*(i+1)))
return x, mass
You should use trapezoidal rule to approximate this integral.
def get_mass_distribution(data):
a = np.array(data)
ag = st.gaussian_kde(a)
denom_integral = trapezoidal(ag, 0, max(data), max(data)*10)
Fm = [0]
x = []
k = 0
while(k < max(data)):
x.append(k)
k = k+1
for i in x[1:]:
enum_integral = trapezoidal(ag, 0, i, i*10)
Fm.append(enum_integral/denom_integral)
return x, Fm
def trapezoidal(ag, a, b, n):
h = float(b - a) / n
s = 0.0
s += a*ag(a)[0]/2.0
for i in range(1, n):
s += (a + i*h)*ag(a + i*h)[0]
s += b*ag(b)[0]/2.0
return s * h

Is there a faster way of repeating a chunk of code x times and taking an average?

Starting with:
a,b=np.ogrid[0:n+1:1,0:n+1:1]
B=np.exp(1j*(np.pi/3)*np.abs(a-b))
B[z,b] = np.exp(1j * (np.pi/3) * np.abs(z - b +x))
B[a,z] = np.exp(1j * (np.pi/3) * np.abs(a - z +x))
B[diag,diag]=1-1j/np.sqrt(3)
this produces an n*n grid that acts as a matrix.
n is just a number chosen to represent the indices, i.e. an a*b matrix where a and b both go up to n.
Where z is a constant I choose to replace a row and column with the B[z,b] and B[a,z] formulas. (Essentially the same formula but with a small number added to the np.abs(a-b))
The diagonal of the matrix is given by the bottom line:
B[diag,diag]=1-1j/np.sqrt(3)
where,
diag=np.arange(n+1)
I would like to repeat this code 50 times where the only thing that changes is x so I will end up with 50 versions of the B np.ogrid. x is a randomly generated number between -0.8 and 0.8 each time.
x=np.random.uniform(-0.8,0.8)
I want to generate 50 versions of B with random values of x each time and take a geometric average of the 50 versions of B using the definition:
def geo_mean(y):
y = np.asarray(y)
return np.prod(y ** (1.0 / y.shape[0]), axis=-1)
I have tried to set B as a function of some index and then use a for _ in range(): loop, this doesn't work. Aside from copy and pasting the block 50 times and denoting each one as B1, B2, B3 etc; I can't think of another way of working this out.
EDIT:
I'm now using part of a given solution in order to show clearly what I am looking for:
#A matrix with 50 random values between -0.8 and 0.8 to be used in the loop
X=np.random.uniform(-0.8,0.8, (50,1))
#constructing the base array before modification by random x values in position z
a,b = np.ogrid[0:n+1:1,0:n+1:1]
B = np.exp(1j * ( np.pi / 3) * np.abs( a - b ))
B[diag,diag] = 1 - 1j / np.sqrt(3)
#list to store all modified arrays
randomarrays = []
for i in range( 0,50 ):
#copy array and modify it
Bnew = np.copy( B )
Bnew[z, b] = np.exp( 1j * ( np.pi / 3 ) * np.abs(z - b + X[i]))
Bnew[a, z] = np.exp( 1j * ( np.pi / 3 ) * np.abs(a - z + X[i]))
randomarrays.append(Bnew)
Bstack = np.dstack(randomarrays)
#calculate the geometric mean value along the axis that was the row in 2D arrays
B0 = geo_mean(Bstack)
From this example, every iteration of i uses the same value of X, I can't seem to get a way to get each new loop of i to use the next value in the matrix X. I am unsure of the ++ action in python, I know it does not work in python, I just don't know how to use the python equivalent. I want a loop to use a value of X, then the next loop to use the next value and so on and so forth so I can dstack all the matrices at the end and find a geo_mean for each element in the stacked matrices.
One pedestrian way would be to use a list comprehension or generator expression:
>>> def f(n, z, x):
... diag = np.arange(n+1)
... a,b=np.ogrid[0:n+1:1,0:n+1:1]
... B=np.exp(1j*(np.pi/3)*np.abs(a-b))
... B[z,b] = np.exp(1j * (np.pi/3) * np.abs(z - b +x))
... B[a,z] = np.exp(1j * (np.pi/3) * np.abs(a - z +x))
... B[diag,diag]=1-1j/np.sqrt(3)
... return B
...
>>> X = np.random.uniform(-0.8, 0.8, (10,))
>>> np.prod((*map(np.power, map(f, 10*(4,), 10*(2,), X), 10 * (1/10,)),), axis=0)
But in your concrete example we can do much better than that;
using the identity exp(a) x exp(b) = exp(a + b) we can convert the geometric mean after exponentiation to an arithmetic mean before exponentition. A bit of care is required because of the multivaluedness of the complex n-th root which occurs in the geometric mean. In the code below we normalize the angles occurring to range -pi, pi so as to always hit the same branch as the n-th root.
Please also note that the geo_mean function you provide is definitely wrong. It fails the basic sanity check that taking the average of copies of the same thing should return the same thing. I've provided a better version. It is still not perfect, but I think there actually is no perfect solution, because of the nonuniqueness of the complex root.
Because of this I recommend taking the average before exponentiating. As long as your random spread is less than pi this allows a well-defined averaging procedure with an average that is actually close to the samples
import numpy as np
def f(n, z, X, do_it_pps_way=True):
X = np.asanyarray(X)
diag = np.arange(n+1)
a,b=np.ogrid[0:n+1:1,0:n+1:1]
B=np.exp(1j*(np.pi/3)*np.abs(a-b))
X = X.reshape(-1,1,1)
if do_it_pps_way:
zbx = np.mean(np.abs(z-b+X), axis=0)
azx = np.mean(np.abs(a-z+X), axis=0)
else:
zbx = np.mean((np.abs(z-b+X)+3) % 6 - 3, axis=0)
azx = np.mean((np.abs(a-z+X)+3) % 6 - 3, axis=0)
B[z,b] = np.exp(1j * (np.pi/3) * zbx)
B[a,z] = np.exp(1j * (np.pi/3) * azx)
B[diag,diag]=1-1j/np.sqrt(3)
return B
def geo_mean(y):
y = np.asarray(y)
dim = len(y.shape)
y = np.atleast_2d(y)
v = np.prod(y, axis=0) ** (1.0 / y.shape[0])
return v[0] if dim == 1 else v
def geo_mean_correct(y):
y = np.asarray(y)
return np.prod(y ** (1.0 / y.shape[0]), axis=0)
# demo that orig geo_mean is wrong
B = np.exp(1j * np.random.random((5, 5)))
# the mean of four times the same thing should be the same thing:
if not np.allclose(B, geo_mean([B, B, B, B])):
print('geo_mean failed')
if np.allclose(B, geo_mean_correct([B, B, B, B])):
print('but geo_mean_correct works')
n, z, m = 10, 3, 50
X = np.random.uniform(-0.8, 0.8, (m,))
B0 = f(n, z, X, do_it_pps_way=False)
B1 = np.prod((*map(np.power, map(f, m*(n,), m*(z,), X), m * (1/m,)),), axis=0)
B2 = geo_mean_correct([f(n, z, x) for x in X])
# This is the recommended way:
B_recommended = f(n, z, X, do_it_pps_way=True)
print()
print(np.allclose(B1, B0))
print(np.allclose(B2, B1))
I think you should rely more on numpy functionality, when approaching your problem. Not a numpy expert myself, so there is surely room for improvement:
from scipy.stats import gmean
n = 2
z = 1
a = np.arange(n + 1).reshape(1, n + 1)
#constructing the base array before modification by random x values in position z
B = np.exp(1j * (np.pi / 3) * np.abs(a - a.T))
B[a, a] = 1 - 1j / np.sqrt(3)
#list to store all modified arrays
random_arrays = []
for _ in range(50):
#generate random x value
x=np.random.uniform(-0.8, 0.8)
#copy array and modify it
B_new = np.copy(B)
B_new[z, a] = np.exp(1j * (np.pi / 3) * np.abs(z - a + x))
B_new[a, z] = np.exp(1j * (np.pi / 3) * np.abs(a - z + x))
random_arrays.append(B_new)
#store all B arrays as a 3D array
B_stack = np.stack(random_arrays)
#calculate the geometric mean value along the axis that was the row in 2D arrays
geom_mean_for_rows = gmean(B_stack, axis = 2)
It uses the geometric mean function from scipy.stats module to have a vectorised approach for this calculation.

Using 'If' to run the loop again?

I am producing 2 arrays of random numbers that range from -3 to 3 (this would provide to be my x and y coordinates per say). However I want to produce a new set of numbers if the respective x and y is greater than my radius (which is 3) by using an if loop? What do I put in after my if loop to achieve this?
from physics import *
import random
N=3
x= zeros(N,float)
y= zeros(N,float)
for i in range (0, N):
x[i] = uniform(6)-3
y[i] = uniform(6)-3
if (x[n]*2+y[n]**2)**0.5 > 3:
Thanks
Most random library modules provide a routine to return a floating point number from a uniform distribution over the interval [0,1). To choose randomly from a uniform distribution over the interval [a,b), where b > a, you can just multiply by b-a, and then add a.
I'm not certain which modules you're importing in your example, so I'll give examples using the standard random module, as well as the numpy.random
random
import random
def rand_range(a, b):
return a + random.uniform() * (b - a)
x = [rand_range(-3, 3) for i in range(3)]
y = [rand_range(-3, 3) for i in range(3)]
numpy.random
Numpy allows one to vectorize operations for speed and clarity.
import numpy as np
def rand_range(a, b, size):
return a + np.random.random(size) * (b - a)
x = rand_range(-3, 3, (3))
y = rand_range(-3, 3, (3))
or, create an 2D array of 2 rows of 3 elements, and use unpacking
x, y = rand_range(-3, 3, (2, 3))
bounds checking
In order to ensure the points generated meet the criteria of being with 3 units of x, y = (0, 0), you can do something like the following:
x = []
y = []
for i in range(3):
xi, yi = 3, 3 #initally set candidates to fail check
while xi ** 2 + yi ** 2 > 9:
xi = rand_range(-3, 3)
yi = rand_range(-3, 3)
x.append(xi)
y.append(yi)
Given the underlying random method returns a value on the interval [0,1), these arrays should all pass the following assertion:
for xi, yi in zip(x,y):
assert xi**2 + yi**2 <= 9
Can't you just create a new range and then reset your index?
from physics import *
import random
N=3
x= zeros(N,float)
y= zeros(N,float)
for i in range (0, N):
x[i] = uniform(6)-3
y[i] = uniform(6)-3
if (x[n]*2+y[n]**2)**0.5 <= 3:
x = random.sample(range(-3, 3), n)
y = random.sample(range(-3, 3), n)
i = 0
#mtadd
I am answering my question because I found a way to solve it. However, I'm curious whether the way you solved it is more efficient than my process mtadd.
In my physics import is where the random and numpy module are stored, hence why it isn't used in my code.
from physics import *
N=3 #Number of point charges
x= zeros(N,float) #grid
y= zeros(N,float)
i=0
for i in range (0, N): #Loop to ensure two values are <= 3
while i < N:
x[i] = uniform(6)-3
y[i] = uniform(6)-3
if x[i] ** 2 + y[i] ** 2 <= 9:
i+=1
else:
i=0
print x,y
Thanks for the help guys

Categories

Resources