Is there a way in Sympy to simplify Matrix algebra? Say with woodbury identity - sympy

I want to specifya N x M matrix where N >> M and use the woodbury,
and for Sympy to simplify the matrix algebra.
Is this possible or something similar?

Is there a way in Sympy to simplify Matrix algebra? Say with woodbury identity
Yes, it's possible to apply Woodbury's identity. In the code below, the identity is applied on (I + Phi^T * Phi)**(-1).
The particular case used was (I+UV)^-1 = I - U*(I+VU)^-1*V
from sympy.simplify.simplify import nc_simplify
from sympy import *
N,M,sigma = symbols("N M \sigma")
Phi = MatrixSymbol("Phi", N, M)
In,Im = Identity(N), Identity(M)
f = (Im + Phi.transpose()#Phi).inverse()
f = f # Phi.transpose()
f = Phi # f
f = In - sigma**(-2)*f
f = sigma**(-2)*Phi.transpose()#f
f = f# Phi
def apply_woodburry_1(e,U,V):
# The Woodbury identity
#
# (I+UV)^-1 = I - U * (I+VU)^-1 * V
#
# Below, P will be LHS and Q will be RHS
#
UV_dim = (U * V).shape
I1 = Identity(UV_dim[0])
VU_dim = (V * U).shape
I2 = Identity(VU_dim[0])
P = (I1+U*V)**(-1)
Q = I1 - U * ( (I2+V*U)**(-1) ) * V
return e.replace(P,Q)
display(f)
f = apply_woodburry_1(f,Phi.transpose(),Phi)
display(f)
f = nc_simplify(f.expand())
display(f)
Output:

Related

Substitute compound expression in SymPy

In sympy how can I make a substitution of a compound expression for a single variable as in the following example that only works for one of the instances of the common factor?
from sympy import *
x, y, z = symbols('x y z')
eq = Eq(2*(x+y) + 3*(x+y)**2, 0)
print(eq)
eq1 = Eq(z, x+y)
print(eq1)
eq2 = eq.subs(eq1.rhs, eq1.lhs)
print(eq2)
Output
Eq(2*x + 2*y + 3*(x + y)**2, 0)
Eq(z, x + y)
Eq(2*x + 2*y + 3*z**2, 0)
Desired output for last line
Eq(2*z + 3*z**2, 0)
Thanks to Oscar Benjamin's comment. I've solved the case I was actually interested in:
from sympy import *
t, L, C0, R, a, w0, h = symbols('t L C_0 R alpha omega_0 h')
Q = Function('Q')
ex0 = L*Q(t).diff(t, t) + R*Q(t).diff(t) + Q(t)*(1/(C0/(1+h*cos(a*t))))
print(ex0)
ex1 = ex0/L
ex1 = ex1.collect(Q(t)).expand()
print(ex1)
# substitute the following compound expression
ex2 = Eq(w0*w0, 1/(L*C0))
print(ex2)
ex3 = ex1.subs(L*C0, 1/(w0*w0))
ex4 = ex3.collect(Q(t))
print(ex4)
Output:
L*Derivative(Q(t), (t, 2)) + R*Derivative(Q(t), t) + (h*cos(alpha*t) + 1)*Q(t)/C_0
Derivative(Q(t), (t, 2)) + R*Derivative(Q(t), t)/L + h*Q(t)*cos(alpha*t)/(C_0*L) + Q(t)/(C_0*L)
Eq(omega_0**2, 1/(C_0*L))
(h*omega_0**2*cos(alpha*t) + omega_0**2)*Q(t) + Derivative(Q(t), (t, 2)) + R*Derivative(Q(t), t)/L
The substitution fails because subs does not encounter any argument x + y in the (sub)expression 2*(x + y): that expression automatically expands to 2*x + 2*y. So one solution is to do as Oscar suggested: make an algebraic substitution. I often follow this up with a restoration step to handle anything that didn't change as I expected. The other thing you can do is to use a helper function that groups together terms that are in the multi-term old object that you desire to replace:
def mvsubs(eq, old, new):
from sympy.core.exprtools import factor_terms
if not old.is_Add:
return eq.subs(old, new)
Add = old.func
free = old.free_symbols
for i in eq.atoms(Add):
reps = {}
for i in i.args:
if not all(i.has(x) for x in free):
reps.setdefault(i, Dummy())
eq = eq.subs(reps).subs(Add(*reps.values()),
factor_terms(Add(*reps.keys()))).subs(
old, new).xreplace({v:k for k,v in reps.items()})
return eq
>>> mvsubs(eq, x+y, z)
Eq(3*z**2 + 2*z, 0)

How do I draw graph in sympy?

I'm high school student, and I'm writing a report about profile velocity.
I don't know much about differential equations and Python, but I have to use both of them.
I'm trying to induce the velocity from (ma = mg - kv), and caculate a and s from v.
I caculated v successfully, but I have few questions.
import sympy
init_printing()
%matplotlib inline
(m, g, k, t) = symbols('m g k t')
v = Function('v')
deq = Eq( m*v(t).diff(t), m*g - k*v(t) )
eq = dsolve( deq, v(t) )
C1 = Symbol('C1')
C1_ic = solve( eq.rhs.subs( {t:0}), C1)[0]
r = expand(eq.subs({C1:C1_ic}))
the simple way to caculate C1 doesn't work
v(0) = 0
so I write
eq = dsolve( deq, ics={v(0):0})
but it has same result with
eq = dsolve( deq, v(t) )
how to caculate acc and draw a graph?
I try this code, but it doesn't work
a = diff(r, t)
r = dsolve( a, v(t))
r.subs({m:1, g:9.8, k:1})
plot( r , (t,0,100))
I don't get the same result from eq = dsolve( deq, ics={v(0):0}). Also you should declare m, g and k with positive=True.
In [50]: m, g, k = symbols('m g k', positive=True)
In [51]: t = Symbol('t')
In [52]: v = Function('v')
In [53]: deq = Eq( m*v(t).diff(t), m*g - k*v(t) )
In [54]: deq
Out[54]:
d
m⋅──(v(t)) = g⋅m - k⋅v(t)
dt
In [55]: dsolve(deq, v(t))
Out[55]:
k⋅(C₁ - t)
──────────
m
g⋅m + ℯ
v(t) = ─────────────────
k
In [56]: dsolve(deq, v(t), ics={v(0):0})
Out[56]:
⎛ m⋅log(g) m⋅log(m) ⅈ⋅π⋅m⎞
k⋅⎜-t + ──────── + ──────── + ─────⎟
⎝ k k k ⎠
────────────────────────────────────
m
g⋅m + ℯ
v(t) = ───────────────────────────────────────────
k
In [57]: sol = dsolve(deq, v(t), ics={v(0):0}).rhs
In [58]: sol.expand()
Out[58]:
-k⋅t
─────
m
g⋅m g⋅m⋅ℯ
─── - ──────────
k k
In [59]: factor_terms(sol.expand())
Out[59]:
⎛ -k⋅t ⎞
⎜ ─────⎟
⎜ m ⎟
g⋅m⋅⎝1 - ℯ ⎠
────────────────
k
You can compute and plot the acceleration like
In [62]: sol = factor_terms(sol.expand())
In [64]: a = sol.diff(t)
In [65]: a = sol.diff(t).subs({m:1, g:9.8, k:1})
In [66]: a
Out[66]:
-t
9.8⋅ℯ
In [67]: plot(a, (t, 0, 100))

generate N random numbers from a skew normal distribution using numpy

I need a function in python to return N random numbers from a skew normal distribution. The skew needs to be taken as a parameter.
e.g. my current use is
x = numpy.random.randn(1000)
and the ideal function would be e.g.
x = randn_skew(1000, skew=0.7)
Solution needs to conform with: python version 2.7, numpy v.1.9
A similar answer is here: skew normal distribution in scipy However this generates a PDF not the random numbers.
I start by generating the PDF curves for reference:
NUM_SAMPLES = 100000
SKEW_PARAMS = [-3, 0]
def skew_norm_pdf(x,e=0,w=1,a=0):
# adapated from:
# http://stackoverflow.com/questions/5884768/skew-normal-distribution-in-scipy
t = (x-e) / w
return 2.0 * w * stats.norm.pdf(t) * stats.norm.cdf(a*t)
# generate the skew normal PDF for reference:
location = 0.0
scale = 1.0
x = np.linspace(-5,5,100)
plt.subplots(figsize=(12,4))
for alpha_skew in SKEW_PARAMS:
p = skew_norm_pdf(x,location,scale,alpha_skew)
# n.b. note that alpha is a parameter that controls skew, but the 'skewness'
# as measured will be different. see the wikipedia page:
# https://en.wikipedia.org/wiki/Skew_normal_distribution
plt.plot(x,p)
Next I found a VB implementation of sampling random numbers from the skew normal distribution and converted it to python:
# literal adaption from:
# http://stackoverflow.com/questions/4643285/how-to-generate-random-numbers-that-follow-skew-normal-distribution-in-matlab
# original at:
# http://www.ozgrid.com/forum/showthread.php?t=108175
def rand_skew_norm(fAlpha, fLocation, fScale):
sigma = fAlpha / np.sqrt(1.0 + fAlpha**2)
afRN = np.random.randn(2)
u0 = afRN[0]
v = afRN[1]
u1 = sigma*u0 + np.sqrt(1.0 -sigma**2) * v
if u0 >= 0:
return u1*fScale + fLocation
return (-u1)*fScale + fLocation
def randn_skew(N, skew=0.0):
return [rand_skew_norm(skew, 0, 1) for x in range(N)]
# lets check they at least visually match the PDF:
plt.subplots(figsize=(12,4))
for alpha_skew in SKEW_PARAMS:
p = randn_skew(NUM_SAMPLES, alpha_skew)
sns.distplot(p)
And then wrote a quick version which (without extensive testing) appears to be correct:
def randn_skew_fast(N, alpha=0.0, loc=0.0, scale=1.0):
sigma = alpha / np.sqrt(1.0 + alpha**2)
u0 = np.random.randn(N)
v = np.random.randn(N)
u1 = (sigma*u0 + np.sqrt(1.0 - sigma**2)*v) * scale
u1[u0 < 0] *= -1
u1 = u1 + loc
return u1
# lets check again
plt.subplots(figsize=(12,4))
for alpha_skew in SKEW_PARAMS:
p = randn_skew_fast(NUM_SAMPLES, alpha_skew)
sns.distplot(p)
from scipy.stats import skewnorm
a=10
data= skewnorm.rvs(a, size=1000)
Here, a is a parameter which you can refer to:
https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.skewnorm.html
Adapted from rsnorm function from fGarch R package
def random_snorm(n, mean = 0, sd = 1, xi = 1.5):
def random_snorm_aux(n, xi):
weight = xi/(xi + 1/xi)
z = numpy.random.uniform(-weight,1-weight,n)
xi_ = xi**numpy.sign(z)
random = -numpy.absolute(numpy.random.normal(0,1,n))/xi_ * numpy.sign(z)
m1 = 2/numpy.sqrt(2 * numpy.pi)
mu = m1 * (xi - 1/xi)
sigma = numpy.sqrt((1 - m1**2) * (xi**2 + 1/xi**2) + 2 * m1**2 - 1)
return (random - mu)/sigma
return random_snorm_aux(n, xi) * sd + mean

Why does SymPy ignore my initial condition?

import sympy as sp
t = sp.Symbol("t")
y = sp.Function("y")
v = sp.Function("v")
sol = sp.dsolve([
y(t).diff(t) - v(t),
v(t).diff(t) + 2*y(t)
],
ics={y(0):2, v(0):0.45})
sol
gives:
[y(t) == C1*sin(sqrt(2)*t) + C2*cos(sqrt(2)*t),
v(t) == sqrt(2)*C1*cos(sqrt(2)*t) - sqrt(2)*C2*sin(sqrt(2)*t)]
Why are C1 and C2 not calculated?

I've done the same code on both MATLAB and Python, but ifft2 returns different values?

I've been trying to implement an homomorphic filter in frequency domain on both MATLAB and Python using OpenCV2 and NumPy, the MATLAB code gives the expected answer but the Python does not, the resulting image is very weird. I've tested all variables and came to the conclusion the only point there is a difference is the IFFT. On MATLAB, the results can be applied normally to the exp function and return the filtered original image expected, but the values of Python ifft are very different. I happened to see other posts with similar problems, but no satisfactory answer (perhaps i'm just bad at searching too...).
The MATLAB code
function [ img_r ] = homomorphic( img, D0, n )
[N, M] = size(img);
img_bk = double(img);
img_bk = log(img_bk+1);
img_freq = fftshift(fft2(img_bk));
magA = uint8(10*log(1+abs(img_freq)));
cu = M/2;
cv = N/2;
Hf = zeros(N,M);
for v = 1:N
dv = v - cv;
for u = 1:M
du = u - cu;
D = sqrt(du*du + dv*dv);
num = 1;
if D > 0
den = 1+((D0/D)^(2*n));
else
den = 0; %to replace +inf
end
if den ~= 0
H = num/den;
else
H = 0;
end
img_freq(v,u) = H*img_freq(v,u);
end
end
magB = uint8(10*log(1+abs(img_freq)));
img_r = (ifft2(ifftshift(img_freq)));
img_r = exp(img_r);
img_r = uint8(img_r);
and the Python code (might have some bugs but overall works)
import numpy as np
import cv2
def homomorphic(img, D0, n=2):
[N,M] = img.shape
img_bk = np.log(1 + np.float64(img))
img_freq = np.fft.fftshift(np.fft.fft2(img_bk))
cu = M/2.0
cv = N/2.0
for v in range(N):
dv = v - cv
for u in range(M):
du = u - cu
D = np.sqrt(du*du + dv*dv)
if D != 0:
a = 1.0 + (D0/D)**(2*n)
H = 1/a
else:
print D
H = 0
img_freq[v][u] = H*img_freq[v][u]
img_r = np.abs(np.fft.ifft2(np.fft.ifftshift(img_freq)))
eimg = np.exp(img_r)
eimg = np.uint8(eimg)
return eimg
I really don't get it, why the results are so different? Does anyone have any idea?