Python Save a (Sparse) Matrix with a variable inside - python

I have some matrices of decent size (2000*2000) and I wish to have symbolic expressions in the elements of the matrices - i.e. .9**b + .8**b + .7**b ... is an example of an element. The matrices are quite sparse.
I am creating these matrices by adding up intermediate calculations. I would like to store them to disk to be read in later and evaluated with different values of b.
I have played around with sympy and it does exactly what I need it to do however it is mind-numbingly slow to do simple additions. From what I have read it seems theano or tensorflow might be able to do this with Tensors but I could not figure out how to put a symbol in a Tensor.
Can anyone point me in the right direction as to the best tool to use for this task? I'd prefer it to be in python but if something outside python would do the job that'd be nice too.

The problem is likely coming from the fact that you are taking a symbolic power. But, for whatever reason, SymPy tries to find an explicit form for a symbolic power. For example:
In [12]: x = Symbol('x')
In [13]: print(Matrix([[1, 2], [3, 4]])**x)
Matrix([[-2*(5/2 + sqrt(33)/2)**x*(-2/((-3/2 + sqrt(33)/2)*(-1/2 + sqrt(33)/6)**2*(sqrt(33)/4 + 11/4)) + 1/(-1/2 + sqrt(33)/6))/(-sqrt(33)/2 - 3/2) + 2*(-sqrt(33)/2 + 5/2)**x/((-3/2 + sqrt(33)/2)*(-1/2 + sqrt(33)/6)*(sqrt(33)/4 + 11/4)), -4*(5/2 + sqrt(33)/2)**x/((-3/2 + sqrt(33)/2)*(-1/2 + sqrt(33)/6)*(-sqrt(33)/2 - 3/2)*(sqrt(33)/4 + 11/4)) - 2*(-sqrt(33)/2 + 5/2)**x/((-3/2 + sqrt(33)/2)*(sqrt(33)/4 + 11/4))], [(5/2 + sqrt(33)/2)**x*(-2/((-3/2 + sqrt(33)/2)*(-1/2 + sqrt(33)/6)**2*(sqrt(33)/4 + 11/4)) + 1/(-1/2 + sqrt(33)/6)) - (-sqrt(33)/2 + 5/2)**x/((-1/2 + sqrt(33)/6)*(sqrt(33)/4 + 11/4)), 2*(5/2 + sqrt(33)/2)**x/((-3/2 + sqrt(33)/2)*(-1/2 + sqrt(33)/6)*(sqrt(33)/4 + 11/4)) + (-sqrt(33)/2 + 5/2)**x/(sqrt(33)/4 + 11/4)]])
Is this actually what you want to do? Do you know the value of b ahead of time? You can leave the expression unevaluated as a power by using MatPow(arr, b).

Related

How to order terms in polynomial expression (sympy, python) according to increasing degree (univariate case)?

In sympy (python) it seems that, by default, terms in univarate polynomials are ordered according to decreasing degrees: highest degree first, then second to highest, and so on. So, for example, a polynomial like
x + 1 + x^3 + 3x^6
will be printed out as 3x^6 + x^3 + x + 1.
I would like to reverse this order of polynomial terms in sympy to be increasing in the degrees. For the same example, the print-out should read 1 + x + x^3 + 3x^6. A solution that globally changes some parameter in program preamble is preferred but other options are also welcome.
Here is an MWE to play around with. It is different from the actual program I am working with. One part of the actual program (not the MWE) is printing out a list of recursively defined polynomials, e.g., P_n(x) = P_(n-1)(x) + a_n * x^n. It is easier for me to compare them when they are ordered by increasing degree. This is the motivation to change the order; doing it globally would probably just keep the code more readable (aesthetically pleasing). But the MWE is just for the same simple polynomial given in example above.
import sympy as sym
from sympy import *
x = sym.Symbol('x')
polynomial = x + 1 + x**3 + 3*x**6
print(polynomial)
Output of MWE:
>>> 3*x**6 + x**3 + x + 1
Desired output for MWE:
>>> 1 + x + x**3 + 3*x**6
You can get the leading term using sympy.polys.polytools.LT:
LT(3x ** 6 + x ** 3 + x + 1) == 3x**6
So at least you can churn out terms recursively and print it in your own way.
Unfortunately I’ve been trying to find some way to print the terms in some fix order for a long while and find no solution better than this
It's seems that there isn't an explicit way to do that and I found this approach to the problem:
to modify the print-representation of the object you can subclass its type and override the corresponding printing method (for LaTeX, MathML, ...) see documentation.
In this case _sympystr is used to "generates readable representations of SymPy expressions."
Here a basic implementation:
from sympy import Poly, symbols, latex
class UPoly(Poly):
"""Modified univariative polynomial"""
def _sympystr(self, printer) -> str:
"""increasing order of powers"""
if self.is_multivariate: # or: not self.is_univariate
raise Exception('Error, Polynomial is not univariative')
x = next(iter(expr.free_symbols))
poly_print = ""
for deg, coef in sorted(self.terms()):
term = coef * x**deg[0]
if coef.is_negative:
term = -term # fix sign
poly_print += " - "
else:
poly_print += " + "
poly_print += printer._print(term)
return poly_print.lstrip(" +-")
def _latex(self, printer):
return latex(self._sympystr(printer)) # keep the order
x = symbols('x')
expr = 2*x + 6 - x**5
up = UPoly(expr)
print(up)
#6 + 2*x - x**5
print(latex(up))
#6 + 2 x - x^{5}

Sympy does not update series coefficients

aa = list(symbols('a0:2'))
q1= series(aa[0]/(1-x) + aa[1]/(1-x**2),x,n=6)
q1.subs(aa[0],1)
print(q1)
Output: x**2*(a0 + a1) + x**4*(a0 + a1) + a1 + a0 + a0*x + a0*x**3 + a0*x**5 + O(x**6)
But what I would like for all the a0's in the series to be substitued by the value of 1:
Output: x**2*(1 + a1) + x**4*(1 + a1) + a1 + 1 + 1*x + 1*x**3 + 1*x**5 + O(x**6)
My understanding is that:
q1.subs(aa[0],1)
would do exactly that. Is there any other way to do the same ? Thanks!
With the exception of mutable matrices, SymPy objects are immutable. Their methods do not modify them; a new object is returned instead. This object needs to be assigned to something (or printed, or returned):
q2 = q1.subs(...)
print(q1.subs(...))
return q1.subs(...)
all make sense; the lonely q1.subs(...) is useless.
This is covered in the "Gotchas and Pitfalls" article under Immutability of Expressions; I recommend reading the rest of that page too.

python sympy does not factor well polynomials of booleans

I am trying to factor a polynomial of booleans to get the minimal form of a logic net. My variables are a1, a2, a3 ... and the negative counterparts na1, na2, na3 ...
If would expect a function
f = a1*a2*b2*nb1 + a1*b1*na2*nb2 + a1*b1*na2 + a2*b2*na1*nb1
to be factored like this (at least) :
f = a1*b1*(b2*nb1 + na2*(nb2 + 1)) + a2*b2*na1*nb1
I run this script:
import sympy
a1,a2,b1,b2,b3,na1,na2,na3,nb1,nb2,nb3 = \
sympy.symbols("a1:3, b1:4, na1:4, nb1:4", bool=True)
f = "a1*na2*b1 + a1*a2*nb1*b2 + a1*na2*b1*nb2 + na1*a2*nb1*b2"
sympy.init_printing(use_unicode=True)
sympy.factor(f)
and this returns me the same function, not factored.
a1*a2*b2*nb1 + a1*b1*na2*nb2 + a1*b1*na2 + a2*b2*na1*nb1
What am I doing wrong ?
Your expected output
f = a1*b1*(b2*nb1 + na2*(nb2 + 1)) + a2*b2*na1*nb1
is not a factorization of f, so factor is not going to produce it. To factor something means to write it as a product, not "a product plus some other stuff".
If you give a polynomial that can actually be factored, say f = a1*na2*b1 + a1*a2*nb1*b2 + a1*na2*b1*nb2, then factor(f) has an effect.
What you are looking for is closer to collecting the terms with the same variable, which is done with collect.
f = a1*na2*b1 + a1*a2*nb1*b2 + a1*na2*b1*nb2 + na1*a2*nb1*b2
collect(f, a1)
outputs
a1*(a2*b2*nb1 + b1*na2*nb2 + b1*na2) + a2*b2*na1*nb1
The method coeff also works in that direction, e.g., f.coeff(a1) returns the contents of the parentheses in the previous formula.

The QRsolve method of sympy never returns or throws "Could not normalize the vector" error

I've tried QRsolve and cholesky_solve on the matrices shown below (printed str repr). I get results from numpy with these. The functions never return with sympy, I guess this has something to do with sympy trying to solve these using a symbolic method? The reason I'm trying sympy is that as my system becomes more complex the matrices become ill-conditioned and I am attempting to increase the precision using sympy and mpmath.
from sympy.matrices import Matrix as sy_matrix
A = sy_matrix([[-1.73598602689344 - 0.555723094599341j, -1.73598602689344 - 0.555723094599341j, 0.232989179693563 - 0.308565151130628j, 0.232989179693563 - 0.308565151130628j, 0.785911137306334 + 0.373372141423308j, 0.785911137306334 + 0.373372141423308j, 0.436377021604638 + 0.329496457808818j, 0.436377021604638 + 0.329496457808818j],
[-2.47182252542744 - 3.12228363243637j, -8.23364083219883 - 10.4003267796456j, 0.752244101320904 + 0.206999511678148j, 2.50572510149994 + 0.689515373399912j, 8.05887958417013 + 10.8152044077855j, 26.8441278948708 + 36.0254458823337j, -0.534283343532919 + 1.94160599872119j, -1.77969781730816 + 6.46748958174029j],
[-2.44359008697499 - 4.49072303037516j, -13.8356070724522 - 25.4264737979839j, 1.0065385313486 + 0.721365705527217j, 5.69902116449569 + 4.08437262469506j, 15.1118001221454 + 29.8836019724734j, 85.5630122915864 + 169.200954368143j, -2.42747866727978 + 3.38711806497384j, -13.744384214138 + 19.1778624838817j],
[-3.12331363856586 - 6.06170033660146j, -24.9646459130564 - 48.4511707904544j, 1.44382306453606 + 1.1598998787916j, 11.5404777548365 + 9.27107973118107j, 24.2361910493061 + 51.4282308184496j, 193.719875057099 + 411.065848931859j, -4.63756924615993 + 5.77276501482576j, -37.0680909845555 + 46.1417107635014j],
[0.232989179693563 - 0.308565151130628j, 0.232989179693563 - 0.308565151130628j, -1.73598602689344 - 0.555723094599341j, -1.73598602689344 - 0.555723094599341j, 0.436377021604638 + 0.329496457808818j, 0.436377021604638 + 0.329496457808818j, 0.785911137306335 + 0.373372141423309j, 0.785911137306335 + 0.373372141423309j],
[0.752244101320904 + 0.206999511678148j, 2.50572510149994 + 0.689515373399912j, -2.47182252542744 - 3.12228363243637j, -8.23364083219883 - 10.4003267796456j, -0.534283343532919 + 1.94160599872119j, -1.77969781730816 + 6.46748958174029j, 8.05887958417013 + 10.8152044077855j, 26.8441278948708 + 36.0254458823337j],
[1.0065385313486 + 0.721365705527217j, 5.69902116449569 + 4.08437262469506j, -2.44359008697499 - 4.49072303037516j, -13.8356070724522 - 25.4264737979839j, -2.42747866727978 + 3.38711806497384j, -13.744384214138 + 19.1778624838817j, 15.1118001221454 + 29.8836019724734j, 85.5630122915864 + 169.200954368143j],
[1.44382306453606 + 1.1598998787916j, 11.5404777548365 + 9.27107973118107j, -3.12331363856586 - 6.06170033660146j, -24.9646459130564 - 48.4511707904544j, -4.63756924615993 + 5.77276501482576j, -37.0680909845555 + 46.1417107635014j, 24.2361910493061 + 51.4282308184496j, 193.7198750571 + 411.065848931859j]])
b = sy_matrix([[1.73598602689344 + 0.555723094599341j], [0.742066203971011 + 0.937341228590923j], [0.431577196569235 + 0.793133703704558j], [0.39075611642261 + 0.758376121181231j], [-0.232989179693563 + 0.308565151130628j], [-0.225831312314891 - 0.06214335385114j], [-0.177770846229001 - 0.127404751947585j], [-0.180635939514086 - 0.145114460001454j]])
x = A.QRsolve(b)
I pasted the code as suggested in the comment, constructing a simple repo script. The difference here is that I create the matrices on initialisation, rather than value-by-value. Now I don't get a block but the following error:
Traceback (most recent call last):
File "sympyTest.py", line 14, in
x = A.QRsolve(b)
File "C:\Python27\lib\site-packages\sympy\matrices\matrices.py", line 1633, in QRsolve
Q, R = self.as_mutable().QRdecomposition()
File "C:\Python27\lib\site-packages\sympy\matrices\matrices.py", line 1599, in QRdecomposition
"Could not normalize the vector %d." % j)
NotImplementedError: Could not normalize the vector 1.
As QRsolve documentation says,
This is mainly for educational purposes and symbolic matrices, for real (or complex) matrices use sympy.mpmath.qr_solve.
I suggest following this advice, or just use mpmath on its own.
Here is a much simpler repro of the issue qith QRsolve:
A = sy_matrix([[2+3j, 1], [1, 1]])
b = sy_matrix([[1], [1]])
A.QRsolve(b)
This throws "NotImplementedError: Could not normalize the vector 0." because the QRdecomposition method, on which QRsolve relies, does not even try to handle floating point errors. This is what it does:
R[j, j] = tmp.norm()
Q[:, j] = tmp / R[j, j]
if Q[:, j].norm() != 1:
raise NotImplementedError("Could not normalize the vector %d." % j)
Obviously, the vector obtained by dividing a column of floating-point numbers by its norm need not have norm exactly 1. In my example,
>>> (A[:,0]/A[:,0].norm()).norm() == 1
False

Factor/collect expression in Sympy

I have an equation like:
R₂⋅V₁ + R₃⋅V₁ - R₃⋅V₂
i₁ = ─────────────────────
R₁⋅R₂ + R₁⋅R₃ + R₂⋅R₃
defined and I'd like to split it into factors that include only single variable - in this case V1 and V2.
So as a result I'd expect
-R₃ (R₂ + R₃)
i₁ = V₂⋅───────────────────── + V₁⋅─────────────────────
R₁⋅R₂ + R₁⋅R₃ + R₂⋅R₃ R₁⋅R₂ + R₁⋅R₃ + R₂⋅R₃
But the best I could get so far is
-R₃⋅V₂ + V₁⋅(R₂ + R₃)
i₁ = ─────────────────────
R₁⋅R₂ + R₁⋅R₃ + R₂⋅R₃
using equation.factor(V1,V2). Is there some other option to factor or another method to separate the variables even further?
If it was possible to exclude something from the factor algorithm (the denominator in this case) it would have been easy. I don't know a way to do this, so here is a manual solution:
In [1]: a
Out[1]:
r₁⋅v₁ + r₂⋅v₂ + r₃⋅v₂
─────────────────────
r₁⋅r₂ + r₁⋅r₃ + r₂⋅r₃
In [2]: b,c = factor(a,v2).as_numer_denom()
In [3]: b.args[0]/c + b.args[1]/c
Out[3]:
r₁⋅v₁ v₂⋅(r₂ + r₃)
───────────────────── + ─────────────────────
r₁⋅r₂ + r₁⋅r₃ + r₂⋅r₃ r₁⋅r₂ + r₁⋅r₃ + r₂⋅r₃
You may also look at the evaluate=False options in Add and Mul, to build those expressions manually. I don't know of a nice general solution.
In[3] can be a list comprehension if you have many terms.
You may also check if it is possible to treat this as multivariate polynomial in v1 and v2. It may give a better solution.
Here I have sympy 0.7.2 installed and the sympy.collect() works for this purpose:
import sympy
i1 = (r2*v1 + r3*v1 - r3*v2)/(r1*r2 + r1*r3 + r2*r3)
sympy.pretty_print(sympy.collect(i1, (v1, v2)))
# -r3*v2 + v1*(r2 + r3)
# ---------------------
# r1*r2 + r1*r3 + r2*r3

Categories