Getting sympy to simplify infinite sums containing piecewise functions - sympy

I am running the following in my jupyter-notebook.
from sympy import *
B = IndexedBase('B')
x, L = symbols('x L', real=True)
n = Symbol('n', integer=True)
n = Idx(n, (0, oo))
Bn = Indexed('B', n)
m = Symbol('m', integer=True)
Sum(Piecewise((0, Ne(m, n)), (L, True))*B[n], (n, 0, oo)).doit()
The last expression should evaluate to $L B_n$. I have tried using simplify, doit, limit and evalf methods on it to without success. I also found more similar issues in github but couldn't taylor this to my specific problem.
I also tried fiddling around with the underlying assumptions for integer m but couldn't find anything suitable.
Is there any direct or indirect way to get sympy to simplify infinite sums containing piecewise functions?
Just as an aside this code below works:
p = Piecewise((1, n<5), (0, True))
Sum(p, (n, 1, oo)).evalf()

Related

Sympy expression simplification

I'm solving an eigenvalue problem when the matrix and the eigenvectors are time dependent. The matrix has dimension 8x8 and is hermitian. The time dependent matrix has the form:
import sympy as sp
t, lbd = sp.symbols(r't,\lambda', real=True)
Had = ...
print(repr(Had))
Matrix([[2*t,0, 0, 0, 0, 0, 0,0],
[ 0,-2*t, 2*t*(1 - t), 0, 0, 0,0,0],
[0, 2*t*(1 - t),0,0, 2 - 2*t, 0,0,0],
[0,0,0,0, 0, 2 - 2*t, 0,0],
[0,0,2 - 2*t,0,0,0,0,0],
[0,0,0, 2 - 2*t,0,0, 2*t*(1 - t),0],
[0,0,0,0,0, 2*t*(1 - t),-2*t,0],
[0,0,0,0,0,0,0,2*t]])
Now the characteristic polynomial has the following for:
P = p.simplify(sp.collect(sp.factor(Had.charpoly(lbd).as_expr()),lbd))
and get
Then I choose the second term and find the solution for lambda:
P_list = sp.factor_list(P)
a,b = P_list[1]
eq,exp = sp.simplify(b)
sol = sp.solve(eq)
With that I get the roots in a list:
r_list = []
for i in range(len(sol)):
a = list(sol[i].values())
r_list.append(a[0])
Solving the problem using sp.eigenvecs:
val_mult_vec = Had.eigenvects()
e_vals = []
mults = []
e_vecs = []
for i in range(len(val_mult_vec)):
val, mult, [vec_i, vec_j] = val_mult_vec[i]
e_vals.append(val)
e_vals.append(val)
mults.append(mult)
e_vecs.append(vec_i)
e_vecs.append(vec_j)
Solving the eigenvectors I get complicated expressions like this:
But I know that this complicated expression can be expressed in terms of the solution of the second term in the characteristic polynomial something like this:
Where r1 are one of the roots of that equation. With the solution to the characteristic polynomial how can I rewrite the eigenvectors in a simplified way like the last image using sympy? rewrite e_vec[i] in terms of r_list[j]
Seems like you want to obtain a compact version of the eigenvectors.
Recepy:
We can create as many symbols as the number of eigenvalues. Each symbol represents an eigenvalue.
Loop over the eigenvectors and for each of its elements substitute the long eigenvalue expression with the respective symbol.
r_symbols = symbols("r0:%s" % len(e_vals))
final_evecs = []
for vec, val, s in zip(e_vecs, e_vals, r_symbols):
final_evecs.append(
vec.applyfunc(lambda t: t.subs(val, s))
)
final_evecs is a list containing eigenvectors in a compact notation.
Let's test one output:
final_evecs[7]

Solving 1 equation based on input variable

For example, Volume of a rectangular box can be calculated as V = L * W * H
Suppose we know V, L, W, then we can solve for H.
Suppose we know V, H, W, then we can solve for V.
Suppose we know L, W, H, then we can solve for V.
And e.t.c
Is there a way to solve in python (I am trying Sympy currently) to solve it based on the input given?
Sure I can use cases of ifs, but I will need to write 4 equations to solve and that is for a short equation.
Any suggestion is appreciated.
Kind regards,
Iwan
This answer to a similar question may help you. Basically you can define the general equation, get values for all but one variable, substitution them into the general equation then pass that expression to solve (or else just pass all equations to solve, which I show below):
from sympy import Eq, solve, Tuple, S
from sympy.abc import v,l,w,h
eq = Eq(v, l*w*h)
variables = eq.free_symbols
got = []
vs = ', '.join(([str(i) for i in variables]))
print('enter 3 known values of {%s} as equality, e.g. h=2' % vs)
for i in range(3):
if 0: # change to 1 to get real input
e = input()
else:
e = ['v=20','w=5','h=1'][i]
got.append(Eq(*[S(j) for j in e.split('=')]))
x = ({v,l,w,h} - Tuple(*got).free_symbols).pop()
ans = solve([eq]+got)
print('consistent values: %s' % ans)
print('%s = %s' % (x.name, ans[0][x])
gives
enter 3 known values of {v, h, w, l} as equality, e.g. h=2
consistent values: [{v: 20, h: 1, w: 5, l: 4}]
l = 4

Solving a matrix equation containing MatrixSymbols of symbolic size in Sympy?

As an introduction i want to point out that if one has a matrix A consisting of 4 submatrices in a 2x2 pattern, where the diagonal matrices are square, then if we denote its inverse as X, the submatrix X22 = (A22-A21(A11^-1)A12)^-1, which is quite easy to show by hand.
I was trying to do the same for a matrix of 4x4 submatrices, but its quite tedious by hand. So I thought Sympy would be of some help. But I cannot figure out how (I have started by just trying to reproduce the 2x2 result).
I've tried:
import sympy as s
def blockmatrix(name, sizes, names=None):
if names is None:
names = sizes
ll = []
for i, (s1, n1) in enumerate(zip(sizes, names)):
l = []
for j, (s2, n2) in enumerate(zip(sizes, names)):
l.append(s.MatrixSymbol(name+str(n1)+str(n2), s1, s2))
ll.append(l)
return ll
def eyes(*sizes):
ll = []
for i, s1 in enumerate(sizes):
l = []
for j, s2 in enumerate(sizes):
if i==j:
l.append(s.Identity(s1))
continue
l.append(s.ZeroMatrix(s1, s2))
ll.append(l)
return ll
n1, n2 = s.symbols("n1, n2", integer=True, positive=True, nonzero=True)
M = s.Matrix(blockmatrix("m", (n1, n2)))
X = s.Matrix(blockmatrix("x", (n1, n2)))
I = s.Matrix(eyes(n1, n2))
s.solve(M*X[:, 1:]-I[:, 1:], X[:, 1:])
but it just returns an empty list instead of the result.
I have also tried:
Using M*X==I but that just returns False (boolean, not an Expression)
Entering a list of equations
Using 'ordinary' symbols with commutative=False instead of MatrixSymbols -- this gives an exception with GeneratorsError: non-commutative generators: (x12, x22)
but all without luck.
Can you show how to derive a result with Sympy similar to the one I gave as an example for X22?
The most similar other questions on solving with MatrixSymbols seem to have been solved by working around doing exactly that, by using an array of the inner symbols or some such instead. But since I am dealing with symbolically sized MatrixSymbols, that is not an option for me.
Is this what you mean by a matrix of 2x2 matrices?
>>> a = [MatrixSymbol(i,2,2) for i in symbols('a1:5')]
>>> A = Matrix(2,2,a)
>>> X = A.inv()
>>> print(X[1,1]) # [1,1] instead of [2,2] because indexing starts at 0
a1*(a1*a3 - a3*a1)**(-1)
[You indicated not and pointed out that the above is not correct -- that appears to be an issue that should be resolved.]
I am not sure why this isn't implemented, but we can do the solving manually as follows:
>>> n = 2
>>> v = symbols('b:%s'%n**2,commutative=False)
>>> A = Matrix(n,n,symbols('a:%s'%n**2,commutative=False))
>>> B = Matrix(n,n,v)
>>> eqs = list(A*B - eye(n))
>>> for i in range(n**2):
... s = solve(eqs[i],v[i])[0]
... eqs[i+1:] = [e.subs(v[i],s) for e in eqs[i+1:]]
...
>>> s # solution for v[3] which is B22
(-a2*a0**(-1)*a1 + a3)**(-1)
You can change n to 3 and see a modestly complicated expression. Change it to 4 and check the result by hand to give a new definition to the word "tedious" ;-)
The special structure of the equations to be solved can allow for a faster solution, too: the variable of interest is the last factor in each term containing it:
>>> for i in range(n**2):
... c,d = eqs[i].expand().as_independent(v[i])
... assert all(j.args[-1]==v[i] for j in Add.make_args(d))
... s = 1/d.subs(v[i], 1)*-c
... eqs[i+1:] = [e.subs(v[i], s) for e in eqs[i+1:]]

How to simplify algebra equations represented as list of list

With Prolog I want to simplify algebra expression represented as as list of list:
algebra equation
f = 3x+2
list of list
[[3,1],[2,0]]
3 and 2 are coefficients
1 and 0 are exponents
That should be obvious.
I am looking for some tips or suggestions on how to code the simplifications for this example:
f = 3x+2x+1+2
[[3,1],[2,1],[1,0],[2,0]]
simplified:
f = 5x+3
[[5,1],[3,0]]
I have tried some built in functions but did not get the proper idea about how to use them.
One liner, similar to what's proposed by joel76:
simplify(I,O) :-
bagof([S,E],L^(bagof(C,member([C,E],I),L),sum_list(L,S)),O).
The inner bagof collects C (coefficients) given E (exponents), the resulting list L is summed into S, and paired with E becomes [S,E], an element (monomial) of O.
If you omit the universal quantification specifier (that is L^) you get single monomials on backtracking.
You can solve your problem in this way:
simplify(_,_,S,S,[]):- !.
simplify(L,I,Sum,NTot,[[I,S]|T]):-
Sum =< NTot,
findall(X,member([X,I],L),LO),
length(LO,N),
S1 is Sum + N,
sum_list(LO,S),
I1 is I+1,
simplify(L,I1,S1,NTot,T).
write_function([]).
write_function([[D,V]|T]):-
write(' + '),write(V),write('x^'),write(D),
write_function(T).
test:-
L = [[3,1],[2,1],[1,0],[2,0]],
length(L,N),
simplify(L,0,0,N,LO),
LO = [[D,V]|T],
write('f='),write(V),write('x^'),write(D),
write_function(T).
The main predicate is simplify/5 which uses findall/3 to find all the coefficients with the same degree and then sums them using sum_list/2. Then you can write the result in a fancy way using write_function/1.
In SWI-Prolog You can use aggregate :
pred(>, [_,X], [_,Y]) :- X > Y.
pred(<, [_,X], [_,Y]) :- X < Y.
pred(=, [_,X], [_,X]).
simplify(In, Out) :-
aggregate(set([S,X]), aggregate(sum(P), member([P,X], In), S), Temp),
predsort(pred, Temp, Out).
For example :
?- simplify([[3,1],[2,1],[1,0],[2,0]], Out).
Out = [[5, 1], [3, 0]] ;
false.

Summing two Poisson distributions with SymPy

I'm new to SymPy and I'm trying to use it to sum two Poisson distributions
Here's what I have so far (using jupyter notebook)
from sympy import *
from sympy.stats import *
init_printing(use_latex='mathjax')
lamda_1, lamda_2 = symbols('lamda_1, lamda_2')
n_1 = Symbol('n_1')
n_2 = Symbol('n_2')
n = Symbol('n')
#setting up distributions
N_1 = density(Poisson('N_1', lamda_1))(n_1)
N_2 = density(Poisson('N_2', lamda_2))(n_2)
display(N_1)
display(N_2)
print('setting N_2 in terms of N and N_1')
N_2 = N_2.subs(n_2,n-n_1)
display(N_2)
print("N_1 * N_2")
N = N_1 * N_2
#display(N)
Sum(N,(n_1,0,n))
#summation(N,(n_1,0,n))
Everything works fine until I try and run the summation. No errors just doesn't do anything and jupyter says it's running. I've let it run for 10 mins and nothing...
When declaring symbols, include their properties: being positive, integer, nonnegative, etc. This helps SymPy decide whether some transformations are legitimate.
lamda_1, lamda_2 = symbols('lamda_1, lamda_2', positive=True)
n_1, n_2, n = symbols('n_1 n_2 n', nonnegative=True, integer=True)
Unfortunately, summation still fails because SymPy cannot come up with the key trick: multiplying and dividing by factorial(n). It seems one has to tell it to do that.
s = summation(N*factorial(n), (n_1, 0, n))/factorial(n)
print(s.simplify())
This prints
Piecewise(((lamda_1 + lamda_2)**n*exp(-lamda_1 - lamda_2)/factorial(n), ((-n >= 0) & (lamda_1/lamda_2 <= 1)) | ((-n < 0) & (lamda_1/lamda_2 <= 1))), (lamda_2**n*exp(-lamda_1 - lamda_2)*Sum(lamda_1**n_1*lamda_2**(-n_1)/(factorial(n_1)*factorial(n - n_1)), (n_1, 0, n)), True))
which is a piecewise formula full of unnecessary conditions... but if we ignore those conditions (they are just artifacts of how SymPy performed the summation), the correct result
((lamda_1 + lamda_2)**n*exp(-lamda_1 - lamda_2)/factorial(n)
is there.
Aside: avoid doing import * from both sympy and sympy.stats, there are notational clashes such as E being 2.718... versus expected value. from sympy.stats import density, Poisson would be better. Also, N is a built-in SymPy function and is best avoided as a variable name.