SymPy - substitute sybolic entries in a matrix - python-2.7

I have a python function which generates a sympy.Matrix with symbolic entries. It works effectively like:
import sympy as sp
M = sp.Matrix([[1,0,2],[0,1,2],[1,2,0]])
def make_symbolic_matrix(M):
M_sym = sp.zeros(3)
syms = ['a0:3']
for i in xrange(3):
for j in xrange(3):
if M[i,j] == 1:
M_sym = syms[i]
elif M[i,j] == 2:
M_sym = 1 - syms[i]
return M_sym
This works just fine. I get a matrix out, which I can use for all the symbolical calculations I need.
My issue is that now I want to evaluate my matrix at specified parameter-value. Usually I would just use the .subs attribute. However, since the symbols, that are now used as entries in my matrix, were originally defined as temporary elements in a function, I don't know how to call them.
It seems as if it should be possible, since I'm able to perform symbolic calculations.
What I want to do would look something like (following the code above):
M_sym = make_matrix(M)
M_eval = M_sym.subs([(a0,.8),(a1,.3),(a2,.5)])
But all I get is "name 'a0' is not defined".
I'd be super happy if someone out there got a solution!
PS. I'm not just defining the symbols globally, because in the actual problem I don't know how many parameters I have from time to time.

In the general case, I assume you're looking for an n-by-m matrix of symbolic elements.
import sympy
def make_symbolic(n, m):
rows = []
for i in xrange(n):
col = []
for j in xrange(m):
col.append(sympy.Symbol('a%d%d' % (i,j)))
rows.append(col)
return sympy.Matrix(rows)
which could be used in the following way:
make_symbolic(3, 4)
to give:
Matrix([
[a00, a01, a02, a03],
[a10, a11, a12, a13],
[a20, a21, a22, a23]])
once you've got that matrix you can substitute in any values required.

Given that the answer from Andrew was helpful it seems like you might be interested in the MatrixSymbol.
In [1]: from sympy import *
In [2]: X = MatrixSymbol('X', 3, 4)
In [3]: X # completely symbolic
Out[3]: X
In [4]: Matrix(X) # Expand to explicit matrix
Out[4]:
⎡X₀₀ X₀₁ X₀₂ X₀₃⎤
⎢ ⎥
⎢X₁₀ X₁₁ X₁₂ X₁₃⎥
⎢ ⎥
⎣X₂₀ X₂₁ X₂₂ X₂₃⎦
But answering your original question, perhapcs you could get the symbols out of the matrix that you produce?
x12 = X[1, 2]

Symbols are defined by their name. Two symbols with the same name will considered to be the same thing by Sympy. So if you know the name of the symbols you want, just create them again using symbols.
You may also find good use of the zip function of Python.

Related

Conditional expression in Sympy

If I try to have an expression whose value depends on another expression
from sympy import *
x = symbols('x')
y1 = x/cos(x)
y2 = y2 if y1>0 else nan
but an exception is raised
File /usr/lib/python3.10/site-packages/sympy/core/relational.py:511, in Relational.__bool__(self)
510 def __bool__(self):
--> 511 raise TypeError("cannot determine truth value of Relational")
TypeError: cannot determine truth value of Relational
Is there a chance of having the effect that I'd want?
In case my problem is a X-Y problem, what I ultimately want to do is to plot x/cos(x) only where the function is positive.
UPDATE
I used the useful suggestion of #Oscar Benjamin (that completely answers my original question) but I have other issues when plotting the Piecewiswe function, that I'll expose in another question, as well as a wrong plot when I use the plot_implicit solution they suggested.
What you're looking for is Piecewise:
In [8]: p = Piecewise((x/cos(x), x/cos(x) > 0), (S.NaN, True))
In [9]: p
Out[9]:
⎧ x x
⎪────── for ────── > 0
⎨cos(x) cos(x)
⎪
⎩ nan otherwise
A more direct solution to your problem though would be something like
plot_implicit(Eq(y, x/cos(x)) & (x/cos(x) > 0))

how to use SymPy or other library to have a numerical solution

I was trying to solve two equations for two unknown symbols 'Diff' and 'OHs'. the equations are shown below
x = (8.67839580228369e-26*Diff + 7.245e-10*OHs**3 +
1.24402291559836e-10*OHs**2 + OHs*(-2.38073807380738e-19*Diff -
2.8607855978291e-18) - 1.01141409254177e-29)
J= (-0.00435840284294195*Diff**0.666666666666667*(1 +
3.64525434266056e-7/OHs) - 1)
solution = sym.nsolve ((x, J), (OHs, Diff), (0.000001, 0.000001))
print (solution)
is this the correct way to solve for the two unknowns?
Thanks for your help :)
Note: I edited your equation per Vialfont's comments.
I would say it is a possible way but you could do better by noticing that the J equation can be solved easily for OHs and substituted into the x equation. This will then be much easier for nsolve to solve:
>>> osol = solve(J, OHs)[0] # there is only one solution
>>> eq = x.subs(OHs,osol)
>>> dsol = nsolve(eq, 1e-5)
>>> eq.subs(Diff,dsol) # verify
4.20389539297445e-45
>>> osol.subs(Diff,dsol), dsol
(-2.08893263437131e-12, 4.76768525775849e-5)
But this is still pretty ill behaved in terms of scaling...proceed with caution. And I would suggest writing Diff**Rational(2,3) instead of Diff**0.666666666666667. Or better, then let Diff be y**3 so you are working with a polynomial in y.
>>> y = var('y', postive=True)
>>> yx=x.subs(Diff,y**3)
>>> yJ=J.subs(Diff,y**3)
>>> yosol=solve(yJ,OHs)[0]
>>> yeq = yx.subs(OHs, yosol)
Now, the solutions of eq will be where its numerator is zero so find the real roots of that:
>>> ysol = real_roots(yeq.as_numer_denom()[0])
>>> len(ysol)
1
>>> ysol[0].n()
0.0362606728976173
>>> yosol.subs(y,_)
-2.08893263437131e-12
That is consistent with our previous solution, and this time the solutions in ysol were exact (given the limitations of the coefficients). So if your OHs solution should be positive, check your numbers and equations.
Your expressions do not meet Sympy requirements, including the exponential expressions. May be it is easier to start with a simpler system to solve with two unknowns and only a square such as:
from sympy.abc import a,b,x, y
from sympy import solve,exp
eq1= a*x**2 + b*y+ exp(0)
eq2= x + y + 2
sol=solve((eq1, eq2),(x,y),dict=True)
sol includes your answers and you have access to solutions with sol[0][x] and sol[0][y]. Giving values to the parameters is done with the .sub() method:
sol[0][x].subs({a:1, b:2}) #gives -1
sol[0][y].subs({a:1, b:2}) #gives -1

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:]]

Solving constraint satisfaction problems in Sympy

I'm attempting to solve some simple Boolean satisfiability problems in Sympy. Here, I tried to solve a constraint that contains the Or logic operator:
from sympy import *
a,b = symbols("a b")
print(solve(Or(Eq(3, b*2), Eq(3, b*3))))
# In other words: (3 equals b*2) or (3 equals b*3)
# [1,3/2] was the answer that I expected
Surprisingly, this leads to an error instead:
TypeError: unsupported operand type(s) for -: 'Or' and 'int'
I can work around this problem using Piecewise, but this is much more verbose:
from sympy import *
a,b = symbols("a b")
print(solve(Piecewise((Eq(3, b*2),Eq(3, b*2)), (Eq(3, b*3),Eq(3, b*3)))))
#prints [1,3/2], as expected
Unfortunately, this work-around fails when I try to solve for two variables instead of one:
from sympy import *
a,b = symbols("a b")
print(solve([Eq(a,3+b),Piecewise((Eq(b,3),Eq(b,3)), (Eq(b,4),Eq(b,4)))]))
#AttributeError: 'BooleanTrue' object has no attribute 'n'
Is there a more reliable way to solve constraints like this one in Sympy?
To expand on zaq's answer, SymPy doesn't recognize logical operators in solve, but you can use the fact that
a*b = 0
is equivalent to
a = 0 OR b = 0
That is, multiply the two equations
solve((3 - 2*b)*(3 - 3*b), b)
As an additional note, if you wanted to use AND instead of OR, you can solve for a system. That is,
solve([eq1, eq2])
is equivalent to solving
eq1 = 0 AND eq2 = 0
Every equation can be expressed as something being equated to 0. For example, 3-2*b = 0 instead of 3 = 2*b. (In Sympy, you don't even have to write the =0 part, it's assumed.) Then you can simply multiply equations to express the OR logic:
>>> from sympy import *
>>> a,b = symbols("a b")
>>> solve((3-b*2)*(3-b*3))
[1, 3/2]
>>> solve([a-3-b, (3-b*2)*(3-b*3)])
[{b: 1, a: 4}, {b: 3/2, a: 9/2}]

ValueError: object of too small depth for desired array

I've searched and find out this may be a problem concerning types. But I tried to force the array to float using astype didn't work out. This must be a simple error, however im a beginner.
About the problem: im trying to form the spatial correlation matrix bewteen the signals of all mics.
R_a[k][l] = np.correlate(self.mic_list[k].delayed_signal,self.mic_list[l].delayed_signal)
where this class has a mic_list which is a list of mic, which is another class that has this method
def add_delayed_signal (self, delayed_signal):
self.delayed_signal = delayed_signal
Thanks you in advanced.
I'm guessing R_a is a 2-dimensional array. What np.correlate does is to compute the cross-correlation between two signals, and gives you a vector as a result (not a scalar).
What you're looking for is probably np.cov or np.corrcoef. These are also vectorized approaches to getting the result you want.
For example:
>>> x = np.random.randn(10)
>>> y = np.random.randn(10)
>>> X = np.vstack((x, y))
>>> X
array([[ 1.45841294, -0.16430013, -0.20782822, 0.08979425, -1.38337166,
0.36488053, -2.57135737, 0.81215918, -0.54081983, 0.30421112],
[-0.79416305, 1.14511318, -0.4962483 , -0.42647021, -0.59925241,
-0.45612051, -0.02566026, -1.7668091 , -1.63098627, 0.3761437 ]])
>>> np.cov(X)
array([[ 1.28563113, -0.20563105],
[-0.20563105, 0.74178773]])
Is this what you're looking for?