How to avoid double for loops? - python-3.x

I want to calculate part of 2 matrices (inner, outer) using data from 2 other matrices. They are all the same size. The code below works but it is too slow on big matrices. I used np.fromfunction in another case but was calculating the entire matrix not only a subset.
What's the fastest replacement for the double for loops?
F = np.random.rand(100,100)
S = 10*np.random.rand(100,100) + 1
L,C = F.shape
inner = np.zeros((L,C))
outer = np.zeros((L,C))
for l in range(5, L - 5):
for c in range(5, C - 5):
inner[l,c] = np.mean(F[l-5 : l+5 , c-5:c])
outer[l,c] = np.mean(F[l-5 : l+5 , c+5 : c+5+int(S[l,c])])

It looks like inner is the result of the convolution of 10x5 averaging filter on F. This is quite easy to rewrite as a convolution with scipy and it will be as fast as you can get from a CPU. However since you are leaving out 5 rows and columns on the borders of the matrices, you have to truncate the output inner and inner2 matrices accordingly to be able to compare them.
import numpy as np
from scipy.signal import convolve2d
F = np.random.rand(100,100)
S = 10*np.random.rand(100,100) + 1
L,C = F.shape
inner = np.zeros((L,C))
outer = np.zeros((L,C))
for l in range(5, L - 5):
for c in range(5, C - 5):
inner[l, c] = np.mean(F[l-5 : l+5 , c-5:c])
outer[l, c] = np.mean(F[l-5 : l+5 , c+5 : c+ 5 + int(S[l, c])])
# if inner[l, c] = np.mean(F[l-5 : l+5 , c-5:c+5]),
# then you should use a 10x10 filter
avg_filter = np.ones((10, 5)) / (10*5)
inner2 = convolve2d(F, avg_filter, mode='valid')
# should be very small (1.262e-13 for me)
print((inner2[:89, :89] - inner[5:94, 5:94]).sum())
The expression for outer is quite strange, because of this int(S[l, c]) offset that you add to your expression. I don't think you can represent this as a matrix computation.
So to replace your double for loop you can use from itertools import product which iterates over the cartesian product of two iterables, like this:
from itertools import product
for (l, c) in product(range(5, L - 5), range(5, C - 5)):
outer[l, c] = np.mean(F[l-5 : l+5 , c+5 : c+ 5 + int(S[l, c])])
From a signal processing standpoint, I'm not sure what is the outer matrix supposed to be. It would be easier to write faster code with the desired effect if you told us what you are trying to do.

Related

Function to Convert Square Matrix to Upper Hessenberg with Similarity Transformations

I am attempting to translate a MATLAB function to Python from Timothy Sauer,
Numerical Analysis Second Edition, page 546, Program 12.8. The original function
receives a square matrix and returns a matrix with the same eigenvalues but in
Upper Hessenberg form. The original function creates Householder reflectors to produce zeros in the
offdiagonals of the matrix and performs similarity transformations on the original matrix to
get it to upper hessenberg form.
My Python translation succeeds only in obtaining the eigenvalues for 3x3 matrices
but not for 4x4 matrices. Would anyone know the cause of the error? I pasted my code with success and failing cases below. Thank you.
import numpy as np
import math
norm = lambda v:math.sqrt(np.sum(v**2))
def upper_hessenberg(A):
'''
Translated from Timothy Sauer, Numerical Analysis Second Edition, page 546, Program 12.8
Input: Square Matrix, A
Output: B, a Similar Matrix with Same Eigenvalues as A except in Upper Hessenberg form
V, a matrix containing the reflectors used to produce zeros in the off diagonals
'''
rows, columns = A.shape
B = A[:,:].astype(np.float) #will store the similar matrix
V = np.zeros(shape=(rows,columns),dtype=float) #will store the reflectors
for column in range(columns-2): #start from the 1st column end at the third to last column
row = column
x = B[row+1: ,column] #decapitate the column
reflection_of_x = np.zeros(len(x)) #first entry is the norm, followed by 0s
if abs(norm(x)) <= np.finfo(float).eps: #if there are already 0s inthe offdiagonals skip this column
continue
reflection_of_x[0] = norm(x)
v = reflection_of_x - x # v, (the difference vector) represents the line connecting the original column to the reflection of the column (see Timothy Sauer Num Analysis 2nd Edition Figure 4.11 Householder reflector)
v = v/norm(v) #normalize to length of 1 (unit vector)
V[:len(v), column] = v #save the reflector in an upper triangular matrix called V
#verify with x-2*(x # v * v) should equal a vector with all zeros except the leading entry
column_projections = np.outer(v , v # B[row+1:, column:]) #project each col onto difference vector
B[row+1:, column:] = B[row+1:, column:] - (2 * column_projections)
row_projections = np.outer(v, B[row:, column + 1:] # v).T #project each row onto difference vector
B[row:, column + 1:] = B[row:, column + 1:] - (2 * row_projections)
return V, B
# Algorithm succeeds only with 3x3 matrices
eigvectors = np.array([
[1,3,2],
[4,5,6],
[7,8,9],
])
eigvalues = np.array([
[4,0,0],
[0,3,0],
[0,0,2]
])
M = eigvectors # eigvalues # np.linalg.inv(eigvectors)
print("The expected eigvals :", np.linalg.eigvals(M))
V,B = upper_hessenberg(M)
print("For 3x3 matrices, The function successfully produces these eigvals",np.linalg.eigvals(B))
#But with 4x4 matrices it fails
eigvectors = np.array([
[1,3,2,4],
[4,5,6,2],
[7,8,9,5],
[5,2,7,8]
])
eigvalues = np.array([
[4,0,0,0],
[0,3,0,0],
[0,0,2,0],
[0,0,0,1]
])
M = eigvectors # eigvalues # np.linalg.inv(eigvectors)
print("The expected eigvals :", np.linalg.eigvals(M))
V,B = upper_hessenberg(M)
print("For 4x4 matrices, The function fails to obtain correct eigvals",np.linalg.eigvals(B))
Your error is that you try to be too efficient. While the last rows are indeed increasingly reduced with leading zeros, this is not the case for the last columns. So in row_projections you need to remove the limiter row:, change to B[:, column + 1:].
You are using the unstable variant of the "improved" Householder reflector. The older version would use the larger of x_refl - x and x_refl + x by setting reflection_of_x[0] = -np.sign(x[0])*norm(x) (or remove all minus signs there).
The stable variant of the improved reflector would use the binomial trick in the normalization of x_refl - x if this difference becomes too small.
x_refl - x = [ norm(x) - x[0], - x[1:] ]
= [ norm(x[1:])^2/(norm(x) + x[0]), - x[1:] ]
(x_refl - x)/norm(x_refl - x)
[ norm(x[1:]), - (norm(x)+x[0])*(x[1:]/norm(x[1:])) ]
= -----------------------------------------------------
sqrt(2*norm(x)*(norm(x)+x[0]))
While the parts may have wildly different scales, no catastrophic cancellation happens for x[0]>0.
See the discussion about the same algorithm from Golub/van Loan 4th ed. in for further details and opinions and the code from that book.

Sympy -- Simplify and gather different variables for variable transformation

In a sympy expression I would like to gather all sub expressions of (x*y)
and replace it by z, wherever possible. In a very simple example, that means performing the map
x*a*y+ (x*y**2) -> a*(x*y) + (x*y)*y -> a*z + z*y
The full code is with another example is
from sympy import symbols,Function,Derivative
from sympy import simplify, exp, cos, sin,log
x,y,z = symbols('x y z')
a,b,c = symbols('a b c')
f,g = Function('f')(x),Function('g')(x)
# Simplify the expression such that all combinations of (x*y) can be replaced by c
expr_1 = ((x**2+y)*y*exp(-c+2*log(x*c*y)))/(x**3*c*y**2)
#simplify(expr_1) ?
In a final step, I would like to replace two functions f*g by h
expr_2 = f*g + f*Derivative(g,x) + Derivative(f*g, x) -> h + f*Derivative(g,x) + Derivative(h, x)
Sometimes an algebraic substitution will do what you want:
>>> eq
a*x*y + x*y**2
>>> eq.subs(x,z/y)
a*z + y*z
But you could just as well have done subs(y,z/x) but that would not have led to as simple of an expression. In such cases you can try both and take the simpler of the two:
>>> from sympy import ordered
>>> next(ordered([eq.subs(x,z/y),eq.subs(y,z/x)]))
a*z + y*z
For expr_1
>>> eq=((x**2+y)*y*exp(-c+2*log(x*c*y)))/(x**3*c*y**2)
>>> next(ordered([eq.subs(x,z/y),eq.subs(y,z/x)]))
c*z*(x**2 + z/x)*exp(-c)/x**2
>>> next(ordered([eq.subs(x,c/y),eq.subs(y,c/x)]))
y**2*(c**2/y**2 + y)*exp(-c)
>>> simplify(_)
(c**2 + y**3)*exp(-c)
The ordered strategy should also work for expr_2.

How to appending 1 value to array A to match the dimensions of array B?

The program I have here is simulating the velocity of a falling object.
The velocity is calculated by subtracting the y position from time_1 and time_2.
The problem that I have is that the dimensions of array v and array t don't match. Instead of shortening array t I would like to add 0 at the beginning of the v array. So that the graph will show v = 0 at t= 0. Yes, I know it is a small interval and that it does not really matter. But I want to know it for educational purpose.
I'm wondering if i can write the line v = (y[1:] - y[:-1])/0.1 in a from where i keep the dimension.
The ideal thing that would happen is that the array y will be substracted with an array y[:-1] and that this subtraction will happen at the end of the y array so the result will be an array of dimension 101 with a 0 as start value.
I would like to know your thoughts about this.
import matplotlib.pyplot as plt
t = linspace(0,10,101)
g = 9.80665
y = 0.5*g*t*t
v = (y[1:] - y[:-1])/0.1
plt.plot(t,v)
plt.show()
is there a function where i can add a certain value to the beginning of an array? np.append will add it to the end.
Maybe you could just pre-define the length of the result at the beginning and then fill up the values:
import numpy as np
dt = .1
g = 9.80665
t_end = 10
t = np.arange(0,t_end+dt,dt)
y = 0.5*g*t*t
v = np.zeros(t.shape[0])
v[1:] = (y[1:] - y[:-1])/dt
if you simply looking for the append at index function it would be this one:
np.insert([1,2,3,4,5,6], 2, 100)
>> array([ 1, 2, 100, 3, 4, 5, 6])
another possible solution to this would be to use np.append but inverse your order :
import numpy as np
v = np.random.rand(10)
value = 42 # value to append at the beginning of v
value_arr = np.array([value]) # dimensions should be adjust for multidimensional array
v = np.append(arr = value_arr, values = v, axis=0)
and the possible variants following the same idea, using np.concatenate or np.hstack ...
regarding your second question in comments, one solution may be :
t = np.arange(6)
condlist = [t <= 2, t >= 4]
choicelist = [1, 1]
t = np.select(condlist, choicelist, default=t)

Python partial derivative

I am trying to put numbers in a function that has partial derivatives but I can't find a correct way to do it,I have searched all the internet and I always get an error.Here is the code:
from sympy import symbols,diff
import sympy as sp
import numpy as np
from scipy.misc import derivative
a, b, c, d, e, g, h, x= symbols('a b c d e g h x', real=True)
da=0.1
db=0.2
dc=0.05
dd=0
de=0
dg=0
dh=0
f = 4*a*b+a*sp.sin(c)+a**3+c**8*b
x = sp.sqrt(pow(diff(f, a)*da, 2)+pow(diff(f, b)*db, 2)+pow(diff(f, c)*dc, 2))
def F(a, b, c):
return x
print(derivative(F(2 ,3 ,5)))
I get the following error: derivative() missing 1 required positional argument: 'x0'
I am new to python so maybe it's a stupid question but I would feel grateful if someone helped me.
You can find three partial derivatives of function foo by variables a, b and c at the point (2,3,5):
f = 4*a*b+a*sp.sin(c)+a**3+c**8*b
foo = sp.sqrt(pow(diff(f, a)*da, 2)+pow(diff(f, b)*db, 2)+pow(diff(f, c)*dc, 2))
foo_da = diff(foo, a)
foo_db = diff(foo, b)
foo_dc = diff(foo, c)
print(foo_da," = ", float(foo_da.subs({a:2, b:3, c:5})))
print(foo_db," = ", float(foo_db.subs({a:2, b:3, c:5})))
print(foo_dc," = ", float(foo_dc.subs({a:2, b:3, c:5})))
I have used a python package 'sympy' to perform the partial derivative. The point at which the partial derivative is to be evaluated is val. The argument 'val' can be passed as a list or tuple.
# Sympy implementation to return the derivative of a function in x,y
# Enter ginput as a string expression in x and y and val as 1x2 array
def partial_derivative_x_y(ginput,der_var,val):
import sympy as sp
x,y = sp.symbols('x y')
function = lambda x,y: ginput
derivative_x = sp.lambdify((x,y),sp.diff(function(x,y),x))
derivative_y = sp.lambdify((x,y),sp.diff(function(x,y),y))
if der_var == 'x' :
return derivative_x(val[0],val[1])
if der_var == 'y' :
return derivative_y(val[0],val[1])
input1 = 'x*y**2 + 5*log(x*y +x**7) + 99'
partial_derivative_x_y(input1,'y',(3,1))

Lambifying Nested Parametric Integrals

I have a loss function L(u1,...,un) that takes the form
L(u) = Integral( (I**2 + J**2) * h, (t,c,d) ) //h=h(t), I=I(t,u)
where
I = Integral( f, (x,a,b) ) // f=f(x,t,u)
J = Integral( g, (x,a,b) ) // g=g(x,t,u)
I want to numerically minimize L with scipy, hence I need to lambdify the expression.
However at this point in time lambdify does not natively support translating integrals. With some tricks one can get it to work with single parametric integrals, see Lambdify A Parametric Integral. However I don't see how the proposed solution could possibly be extended to this generalised case.
One idea that in principle should work is the following:
Take the computational graph defining L. Recursively, starting from the leaves, replace each symbolic operation with the corresponding numerical operation, expressed as a lambda function. However this would lead to an immense nesting of lambda function, which I suspect has a very bad influence on performance.
Ideally we would want to arrive at the same result as one would by hand crafting:
L = lambda u: quad(lambda t: (quad(lambda x: f,a,b)[0]**2
+ quad(lambda x: g,a,b)[0]**2)*h, c, d)[0]
MWE: (using code from old thread)
from sympy import *
from scipy.integrate import quad
import numpy as np
def integral_as_quad(function, limits):
x, a, b = limits
param = tuple(function.free_symbols - {x})
f = sp.lambdify((x, *param), function, modules=['numpy'])
return quad(f, a, b, args=param)[0]
x,a,b,t = sp.symbols('x a b t')
f = (x/t-a)**2
g = (x/t-b)**2
h = exp(-t)
I = Integral( f**2,(x,0,1))
J = Integral( g**2,(x,0,1))
K = Integral( (I**2 + J**2)*h,(t,1,+oo))
F = lambdify( (a,b), K, modules=['sympy',{"Integral": integral_as_quad}])
L = lambda a,b: quad(lambda t: (quad(lambda x: (x/t-a)**2,0,1)[0]**2
+ quad(lambda x: (x/t-b)**2,0,1)[0]**2)*np.exp(-t), 1, np.inf)[0]
display(F(1,1))
display(type(F(1,1)))
display(L(1,1))

Resources