Propagate calculation result - python

I'd like to initialize an array b similar to this, but faster:
a = [0.53, 0.66, 0.064, 0.94, 0.44]
b = [0.0]*5
x = 14.3
for i in range(5):
x = b[i] = a[i]*0.1 + x*0.9
Is there something in numpy for that purpose?

An ugly but vectorized numpy solution:
import numpy as np
a = np.array([0.53, 0.66, 0.064, 0.94, 0.44])
x = 14.3
idx = np.arange(a.size)
0.9 ** idx * (0.1 * (a * 0.9 ** (-idx)).cumsum() + x * 0.9)
# array([12.923 , 11.6967 , 10.53343 , 9.574087 , 8.6606783])
Result from for loop:
a = [0.53, 0.66, 0.064, 0.94, 0.44]
b = [0.0]*5
x = 14.3
for i in range(5):
x = b[i] = a[i]*0.1 + x*0.9
b
#[12.923000000000002,
# 11.696700000000003,
# 10.533430000000003,
# 9.574087000000002,
# 8.660678300000002]
This is because:
And components in the result can be vectorized correspondingly, note the sum of a terms in the inner parenthesis is vectorized as cumsum.

Maybe we can break down and find the correlation between each steps
***x0=x0***
***x1=a[0]*0.1 + x0*0.9***
***x2=a[1]*0.1 + x1*0.9=a[1]*0.1 + (a[0]*0.1 + x0*0.9)*0.9***
So xn=an*0.1+an-1*0.1*0.9+...+a0*0.1*0.9**n-1+x0*0.9**n
n=np.array(0.9)**np.arange(len(a))
sum(a[::-1]*n)*0.1+x*(0.9**(len(a)))
Out[338]: 8.6606783
Update output array
np.diag(np.fliplr(((a*n[::-1])*0.1).cumsum()/n[:,None]+x*(0.9**(np.arange(1,len(a)+1)))))[::-1]
Out[472]: array([12.923 , 11.6967 , 10.53343 , 9.574087 , 8.6606783])

Let's rewrite your loop to analyze it more closely:
for i in range(1, 5):
b[i] = a[i]*0.1 + b[i-1]*0.9
This makes it clear that the calculation is recursive. That is, the value of b[i] depends on the value of b[i-1]. This means that you cannot vectorize the calculation. Vectorizing requires that each element of the result vector is independent of all other elements.

Related

Can I turn this for loop into recursion or dynamic programming?

I am trying to make the code that I have into either recursion or dynamic programming.
import numpy as np
index_list = [1, 2, 0]
weights = [0.3, 0.8]
A_matrix = np.asarray([[0, 1, 2], [0, 1, 2], [0, 1, 2]])
initial_best_vector = A_matrix[:, 1]
# set best_vector_combinations to initial_best_vector
best_vector_combinations = initial_best_vector
for index, _ in enumerate(index_list[1:]):
best_vector_combinations = (
1 - weights[index]
) * best_vector_combinations + (
weights[index] * A_matrix[:, index_list[index + 1]]
)
Is it possible to do so? What I am doing is a nested linear combination of vectors, with the initial base being the initial_best_vector, which corresponds to the index_list.
In other words, let c_i be the columns of the matrix A, I want:
((1-0.3) * c_1 + 0.3 * c_2) * (1-0.8) + 0.8 * c_0
I hope to make this case more general to hold for any length of numbers.
Edit:
The code:
def calculate(vectors, weights):
if not (vectors or weights):
return 0
if not weights:
return vectors[0]
return vectors[0]*(1-weights[0]) + weights[0] * (calculate(vectors[1:], weights[1:]))
vectors = [1,2,3]
weights = [0.2, 0.3]
calculate(vectors, weights) = 1.26
but expected answer is 1.74 where i would expect first case to be 0.8 * 1 + 0.2 * 2 = 1.2, then second to be 1.2 * 0.7 + 3 * 0.3 = 1.74. Note I replaced your typo result to calculate but still unable to recover 1.74.
If you want a recursive implementation, if would be helpful to start with a simpler example and figure out the recurrence relation.
Let vectors = [8,5,2,1] (1D array for simplicity) and let weights = [0.5, 0.8, 0.1, 0.2].
First step of computation: (8 * 0.5) + (1-0.5)*(result of second step).
Second step: 5 * 0.8 + (1-0.8)*(result of third step).
You can work this out further, but the basic relation is
result(vectors, weights) =
(
vectors[0]*weights[0]) +
(1-weights[0]) * (result(vectors[1:], weights[1:]))
) if (vectors and weights) else 0
Implementation:
def calculate(vectors, weights):
if not (vectors or weights):
return 0
if not weights:
return vectors[0]
return vectors[0]*weights[0] + (1-weights[0]) * (calculate(vectors[1:], weights[1:]))
print(calculate([1,2,3], [0.2,0.3])) #left to right processing, 1.26
print(calculate([1,2,3][::-1], [0.2,0.3][::-1])) #right to left processing, 1.74

Is there a matrix (numpy) equivalent to these recursive equations?

I have the following recursive equations I want to implement in Python
(the background is financial markets):
This is easily done sequentially, using a loop:
def compute(x, y, χ, γ, a0, b0):
a = [a0]
b = [b0]
α = [a[0]]
β = [b[0]]
for i in range(len(x)):
a.append(-α[i]*x[i] + β[i]*y[i]*γ[i])
b.append(-β[i]*y[i] + α[i]*x[i]*χ[i])
α.append(α[-1] + a[-1])
β.append(β[-1] + b[-1])
return α, β
A small-arrays example:
x = np.array([0.6, 0.4, 0., 0., 0.9])
y = np.array([0., 0., 0.3, 0.9, 0.])
χ = np.arange(100., 105., 1.)
γ = 1. / (χ - 1.)
print(np.array(compute(x, y, χ, γ, 1., 0.)))
would produce
>>> [[ 1. 0.4 0.24 0.46621782 0.93661782 0.09366178]
[ 0. 60. 76.16 53.312 5.3312 92.99862812]]
is there a way to do it in NumPy (which I expect to significantly speed up the computation)?
In other words: to compute the whole a and b vectors without using a loop, just NumPy functions?
To be clear first: I have no idea how make this faster using numpy. So this is not an answer to your question.
But: you can achieve some speedup using numba:
from numba import jit
import numpy as np
N = 1000000
x = np.random.rand(N)
y = np.random.rand(N)
gamma = np.random.rand(N)
chi = np.random.rand(N)
a = [.2]
b = [.3]
alpha = [a[0]]
beta = [b[0]]
def compute_in_python():
for i in range(len(x)):
a.append(-alpha[i]*x[i] + beta[i]*y[i]*gamma[i])
b.append(-beta[i]*y[i] + alpha[i]*x[i]*chi[i])
alpha.append(alpha[-1]+a[-1])
beta.append(beta[-1]+b[-1])
#jit(nopython=True)
def compute_with_numba(x,y,gamma,chi,a0, b0, alpha0, beta0):
N = len(x)
a = np.empty(N+1)
b = np.empty(N+1)
alpha = np.empty(N+1)
beta = np.empty(N+1)
a[0] = a0
b[0] = b0
alpha[0] = alpha0
beta[0] = beta0
for i in range(N):
a[i+1] = -alpha[i] * x[i] + beta[i] * y[i] * gamma[i]
b[i+1] = -beta[i] * y[i] + alpha[i] * x[i] * chi[i]
alpha[i+1] = alpha[i]+a[i+1]
beta[i+1] = beta[i]+b[i+1]
return a,b,alpha,beta
Vanilla python loops:
In [23]: %time compute_in_python()
CPU times: user 1.6 s, sys: 24.8 ms, total: 1.63 s
Wall time: 1.63 s
Numba-jitted:
In [42]: %time res = compute_with_numba(x,y,gamma,chi,a[0], b[0], alpha[0], beta[0])
CPU times: user 13 ms, sys: 3.36 ms, total: 16.4 ms
Wall time: 16.4 ms
Note that the first call to compute_with_numba will trigger the jit, so you should measure the runtime of the second call.
Again, this is not an answer to your question, but it still is approximately 100 times faster.
you can do it with matrix multiplication if you rearrange the elements to get the following form
After that you can compute everything by simple matrix multiplications.
note that k, j, l, m of a 2x2 matrix are all available and the matrix they construct can be precomputed.
In this case they will be:
k = 1-x
l = y*γ
m = x*χ
n = 1-y
Also I do recommend pre-allocating any array that might be used as the size is always available (appending a list is very costly).
In any case though a for loop is inevitable. But I guess the following would make it neat.
from functools import reduce
import numpy as np
def compute(mat, inp):
return reduce(np.dot, mat) # inp

Numpy: efficiently apply function that takes surrounding cells

I have a numpy array (Potential) and I would like to compute the electromagnetic field. Right now it is the bottleneck of my program.
I have an array V dimension n+2, m+2. I want to create an Array E dimension n,m. The calculation of each cell is to do cell is ~:
sqrt((Cell_left-Cell_right)^2+(Cell_top-Cell_bottom)^2)
I would like to know if there is a way to apply a function to the whole array to avoid the expensive computation of "for loop" :)
right now my code is :
def set_e(self):
pass
for i in range(0, n):
for j in range(0, m):
self.E[i, j] = self.get_local_e(i, j)
def get_local_e(self, i, j):
return (
((self.solution[i + 2, j + 1] - self.solution[i, j + 1]) / unt_y) ** 2
+ ((self.solution[i + 1, j + 2] - self.solution[i + 1, j]) / unt_x) ** 2
) ** 0.5
Thanks
For the people that are interested in this issue, It is possible to do array calculation that way :
def set_e(self):
y_tmp = np.power((self.solution[:-2, 1:-1] - self.solution[2:, 1:-1]) / unt_y, 2)
x_tmp = np.power((self.solution[1:-1, :-2] - self.solution[1:-1, 2:]) / unt_x, 2)
self.E = np.power(x_tmp + y_tmp, 0.5)
It solved my issue
Let's work on this here.
There is something strange about your equation, as only computes the gradient along one row, see y_tmp.
The gradient function calculates along all rows and columns, that's what the shape of the input is the same as with the output.
import numpy as np
solution = np.array([[1.0, 2.0, 3.0, 4.0],
[3.0, 5.0, 7.0, 9.0],
[5.0, 8.0, 11.0, 14.0],
[7.0, 11.0, 15.0, 19.0]])
unt_y = 1
unt_x = 1
g = np.gradient(solution, unt_y, unt_x)
print(g)
a,b = g
c = np.power(a+b, 2.0)
print(c)
def set_e():
y_tmp = np.power((solution[:-2, 1:-1] - solution[2:, 1:-1]) / unt_y, 2)
print('y_tmp', y_tmp)
x_tmp = np.power((solution[1:-1, :-2] - solution[1:-1, 2:]) / unt_x, 2)
E = np.power(x_tmp + y_tmp, 0.5)
print(E)
set_e()

scipy fsolve fails for few values in input, and how to improve solver convergence

I am using scipy.optimize fsolve for finding roots of two equations. fsolve works well for some range of b values (0.1 to 0.6), but fails for values like 0.9 or 0.99.
I have tried moving to least_squares or minimize, but get a tuple error whilst providing initial conditions.
Including edits from below findings be self:
from scipy.optimize import fsolve
import scipy.stats as st
from numpy import *
import numpy as np
def rod(var1, var2, mu, sigma):
return (st.lognorm.ppf(var1, s = sigma, scale = np.exp(mu), loc = sigma))/(st.lognorm.ppf(var2, s = sigma, scale = np.exp(mu), loc = sigma))
def fs_spfs(var1, mu, sigma):
return (st.lognorm.ppf(var1, s = sigma, scale = np.exp(mu), loc = sigma))
a = 44.0
b = 0.5 #fsolve works for 0.5, 0.9, 0.99 but not for 0.95, incidentally works for 0.950001
c = 1.26
def f(x):
y = np.zeros(2)
y[0] = ((fs_spfs((1-b), x[0], x[1]) - a))
y[1] = (((fs_spfs(0.9, x[0], x[1])/fs_spfs(0.1, x[0], x[1])) - c))
print(y)
return y
x0 = np.array([1., 0.01])
solution = fsolve(f, x0)
print( "(x, y) = (" + str(solution[0]) + ", " + str(solution[1]) + ")")
Results with b = 0.5
b = 0.5
(x, y) = (3.7821340072441982, 0.09035467410258388)
fs_spfs((1-b), solution[0], solution[1]) # expected answer = 44.
43.99999999999982
rod(0.9, 0.1, solution[0], solution[1]) # exptected answer = 1.26
1.2599999999999958
Results with b = 0.9
b = 0.9
(x, y) = (3.8979025451494755, 0.09033430819655046)
fs_spfs((1-b), solution[0], solution[1]) # expected answer = 44.
43.999999999989164
rod(0.9, 0.1, solution[0], solution[1]) # exptected answer = 1.26
1.2600000000001814
Works for b = 0.99 as well, but fails for b = 0.95. Incidentally works for b = 0.950001
Following initial condition seems to be working for majority of common cases:
x0 = np.array([0.000001, 0.0000001])
Works for values till 0.999, still fails for 0.9999.

scipy.signal.fftconvolve doesn't give the required results

I have a question regarding python's fftconvolve. In my current research I've been required to calculate some convolution between two functions. To do so I'm calculating it using fourier transform (which I used numpy.fft and normalize it) . The thing is that if I want to compare it using fftconvolve package, it fails to give the correct results. Here is my code:
#!/usr/bin/python
import numpy as np
from scipy.signal import fftconvolve , convolve
def FFT(array , sign):
if sign==1:
return np.fft.fftshift(np.fft.fft(np.fft.fftshift(array))) * dw / (2.0 * np.pi)
elif sign==-1:
return np.fft.fftshift(np.fft.ifft(np.fft.fftshift(array))) * dt * len(array)
def convolve_arrays(array1,array2,sign):
sign = int(sign)
temp1 = FFT(array1 , sign,)
temp2 = FFT(array2 , sign,)
temp3 = np.multiply(temp1 , temp2)
return FFT(temp3 , -1 * sign) / (2. * np.pi)
""" EXAMPLE """
dt = .1
N = 2**17
t_max = N * dt / 2
time = dt * np.arange(-N / 2 , N / 2 , 1)
dw = 2. * np.pi / (N * dt)
w_max = N * dw / 2.
w = dw * np.arange(-N / 2 , N / 2 , 1)
eta_fourier = 1e-10
Gamma = 1.
epsilon = .5
omega = .5
G = zeros(N , complex)
G[:] = 1. / (w[:] - epsilon + 1j * eta_fourier)
D = zeros(N , complex)
D[:] = 1. / (w[:] - omega + 1j * eta_fourier) - 1. / (w[:] + omega + 1j * eta_fourier)
H = convolve_arrays(D , G , 1)
J = fftconvolve(D , G , mode = 'same') * np.pi / (2. * N)
If you plot the real/imaginary part of H, J you'll see a shift in the w axes and also I had to multiply the J's results in order to get somehow close (but still not) to the correct results.
Any suggestions?
Thanks!
Boundary conditions are important when you compute convolutions.
When you convolve two signals, the edges of the result depend on what values you assume outside the edges of the inputs. fftconvolve computes convolution assuming zero-padded boundaries.
Take a look at the source code of fftconvolve. Notice the shenanigans they go through to achieve zero-padded boundary conditions, in particular, these lines:
size = s1 + s2 - 1
...
fsize = 2 ** np.ceil(np.log2(size)).astype(int) #For speed; often suboptimal!
fslice = tuple([slice(0, int(sz)) for sz in size])
...
ret = ifftn(fftn(in1, fsize) * fftn(in2, fsize))[fslice].copy()
...
return _centered(ret, s1) #strips off padding
This is good stuff! It's probably worth reading fftconvolve's code carefully, and a good education if you want to understand Fourier-based convolution.
Brief sketch
The forward FFT zero-pads each signal to prevent periodic boundary conditions:
a = np.array([3, 4, 5])
b = np.fft.ifftn(np.fft.fftn(a, (5,))).real
print b #[ 3. 4. 5. 0. 0.]
the inverse FFT of the product of the forward FFTs gives a padded result:
a = np.array([3, 4, 5])
b = np.array([0., 0.9, 0.1])
b = np.fft.ifftn(np.fft.fftn(a, (5,))*
np.fft.fftn(b, (5,))
).real
print b #[ 0. 2.7 3.9 4.9 0.5]
and the _centered function strips off the extra padding pixels at the end (assuming you use the mode='same' option).

Categories

Resources