Search code examples
pythonsympywolfram-mathematica

Re-write code from Mathematica to Python. Solve function with matrices


I am trying to rewrite one code from Mathematica to Python. In Mathematica I have:

Solve[(K - w*M) . a == 0]

where K and M are square matrices, a = {x1, x2, x3, x4, x5, x6, x7}; and w will give the values in this case of 7 frequencies of the normal modes of vibrations of a system. Then, for each value of w, I get the relative positions of each x_i.

EDIT: I am inserting my full code:

from sympy.solvers import solve
from sympy import *
import numpy as np

m=3.0
k=1.50


w,x1,x2,x3,x4,x5,x6,x7 = symbols("w,x1,x2,x3,x4,x5,x6,x7")

M = Matrix([[m,0,0,0,0,0,0], [0,m,0,0,0,0,0],[0,0,m,0,0,0,0],[0,0,0,m,0,0,0],[0,0,0,0,m,0,0],[0,0,0,0,0,m,0],[0,0,0,0,0,0,m]])
K = Matrix([[2*k,-k,0,0,0,0,0], [-k,2*k,-k,0,0,0,0],[0,-k,2*k,-k,0,0,0],[0,0,-k,2*k,-k,0,0],[0,0,0,-k,2*k,-k,0],[0,0,0,0,-k,2*k,-k],[0,0,0,0,0,-k,2*k]])
xn = Matrix([x1,x2,x3,x4,x5,x6,x7])

D1=K-w*M


print("omega squared")
A=solve(D1.det(), w)
print(A)

res =(np.array(A))**0.5


for i in range(7):
  omegan=float(res[i])
  wn=round(omegan**2,5)
  D1=K-wn*M
  print(D1*xn)
  print("\n** Positions x_i ", i+1, "for omega= ", np.sqrt(wn), " son; ",solve(D1*xn,xn))


I do get the same numerical values for w that I get in Mathematica, although for some numerical values in the matrices K and M, the calculation does not finish or after two minutes I do not get any response. For other numerical values, I do get the solution in less than 10 seconds.

Now, when I try to get the position of x_i for each numerical value of w with the following lines:

.
.
.
wn=omegan**2
D1=K-wn*M
solve(D1*xn,xn)

I get for all cases of wn {x1: 0.0, x2: 0.0, x3: 0.0, x4: 0.0, x5: 0.0, x6: 0.0, x7: 0.0}

But with Mathematica I do get for example x1 -> 0.541196 x2, x3 -> 1.30656 x2, x4 -> 1.41421 x2, x5 -> 1.30656 x2, x6 -> x2, x7 -> 0.541196 x2.

I guess I am using a method not advisable in Python.


Solution

  • Now that the question is updated with complete code let's first see what those matrices look like:

    In [2]: K
    Out[2]: 
    ⎡3.0   -1.5   0     0     0     0     0  ⎤
    ⎢                                        ⎥
    ⎢-1.5  3.0   -1.5   0     0     0     0  ⎥
    ⎢                                        ⎥
    ⎢ 0    -1.5  3.0   -1.5   0     0     0  ⎥
    ⎢                                        ⎥
    ⎢ 0     0    -1.5  3.0   -1.5   0     0  ⎥
    ⎢                                        ⎥
    ⎢ 0     0     0    -1.5  3.0   -1.5   0  ⎥
    ⎢                                        ⎥
    ⎢ 0     0     0     0    -1.5  3.0   -1.5⎥
    ⎢                                        ⎥
    ⎣ 0     0     0     0     0    -1.5  3.0 ⎦
    
    In [3]: M
    Out[3]: 
    ⎡3.0   0    0    0    0    0    0 ⎤
    ⎢                                 ⎥
    ⎢ 0   3.0   0    0    0    0    0 ⎥
    ⎢                                 ⎥
    ⎢ 0    0   3.0   0    0    0    0 ⎥
    ⎢                                 ⎥
    ⎢ 0    0    0   3.0   0    0    0 ⎥
    ⎢                                 ⎥
    ⎢ 0    0    0    0   3.0   0    0 ⎥
    ⎢                                 ⎥
    ⎢ 0    0    0    0    0   3.0   0 ⎥
    ⎢                                 ⎥
    ⎣ 0    0    0    0    0    0   3.0⎦
    

    It looks like you are trying to solve a generalised eigenvalue problem i.e. to find scalars w_i^2 and nonzero vectors x_i such that

    K x_i = w_i * M * x_i
    

    SymPy does not have a dedicated solver for generalized eigenvalue problems like this but SciPy's eig function can do it for matrices of floating point numbers:

    https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.eig.html

    In [5]: from scipy.linalg import eig
    
    In [11]: import numpy as np
    
    In [12]: Kn = np.array(K, dtype=float)
    
    In [13]: Mn = np.array(M, dtype=float)
    
    In [14]: type(Kn), type(Mn)
    Out[14]: (numpy.ndarray, numpy.ndarray)
    
    In [15]: Kn
    Out[15]: 
    array([[ 3. , -1.5,  0. ,  0. ,  0. ,  0. ,  0. ],
           [-1.5,  3. , -1.5,  0. ,  0. ,  0. ,  0. ],
           [ 0. , -1.5,  3. , -1.5,  0. ,  0. ,  0. ],
           [ 0. ,  0. , -1.5,  3. , -1.5,  0. ,  0. ],
           [ 0. ,  0. ,  0. , -1.5,  3. , -1.5,  0. ],
           [ 0. ,  0. ,  0. ,  0. , -1.5,  3. , -1.5],
           [ 0. ,  0. ,  0. ,  0. ,  0. , -1.5,  3. ]])
    
    In [16]: Mn
    Out[16]: 
    array([[3., 0., 0., 0., 0., 0., 0.],
           [0., 3., 0., 0., 0., 0., 0.],
           [0., 0., 3., 0., 0., 0., 0.],
           [0., 0., 0., 3., 0., 0., 0.],
           [0., 0., 0., 0., 3., 0., 0.],
           [0., 0., 0., 0., 0., 3., 0.],
           [0., 0., 0., 0., 0., 0., 3.]])
    
    In [17]: eig(Kn, Mn)
    Out[17]: 
    (array([0.07612047+0.j, 0.29289322+0.j, 0.61731657+0.j, 1.        +0.j,
            1.92387953+0.j, 1.70710678+0.j, 1.38268343+0.j]),
     array([[ 1.91341716e-01,  3.53553391e-01, -4.61939766e-01,
              5.00000000e-01, -1.91341716e-01,  3.53553391e-01,
              4.61939766e-01],
            [ 3.53553391e-01,  5.00000000e-01, -3.53553391e-01,
             -1.93816282e-16,  3.53553391e-01, -5.00000000e-01,
             -3.53553391e-01],
            [ 4.61939766e-01,  3.53553391e-01,  1.91341716e-01,
             -5.00000000e-01, -4.61939766e-01,  3.53553391e-01,
             -1.91341716e-01],
            [ 5.00000000e-01, -7.68527201e-16,  5.00000000e-01,
             -1.91221591e-16,  5.00000000e-01, -4.23414829e-16,
              5.00000000e-01],
            [ 4.61939766e-01, -3.53553391e-01,  1.91341716e-01,
              5.00000000e-01, -4.61939766e-01, -3.53553391e-01,
             -1.91341716e-01],
            [ 3.53553391e-01, -5.00000000e-01, -3.53553391e-01,
             -3.60198282e-16,  3.53553391e-01,  5.00000000e-01,
             -3.53553391e-01],
            [ 1.91341716e-01, -3.53553391e-01, -4.61939766e-01,
             -5.00000000e-01, -1.91341716e-01, -3.53553391e-01,
              4.61939766e-01]]))
    

    Alternatively since the matrix M is invertible we can trivially turn this into an ordinary eigenvalue problem:

    (M**-1 * K) x_i = w_i * x_i
    

    Then we can do it with SymPy:

    In [22]: evs = (M.inv() * K).eigenvects()
    
    In [23]: print(evs[0])
    (0.0761204674887132, 1, [Matrix([
    [-0.191341716182545],
    [-0.353553390593274],
    [-0.461939766255643],
    [              -0.5],
    [-0.461939766255643],
    [-0.353553390593274],
    [-0.191341716182545]])])
    

    That is just showing the first (evs[0]) eigenvector and eigenvalue and 1 there is the multiplicty of the eigenvalue.

    The advantage of using SymPy rather than SciPy in this calculation would be if you wanted to compute the results exactly. To do that first turn your floats into rationals:

    In [24]: Mr = M.applyfunc(nsimplify)
    
    In [25]: Kr = K.applyfunc(nsimplify)
    
    In [26]: Mr
    Out[26]: 
    ⎡3  0  0  0  0  0  0⎤
    ⎢                   ⎥
    ⎢0  3  0  0  0  0  0⎥
    ⎢                   ⎥
    ⎢0  0  3  0  0  0  0⎥
    ⎢                   ⎥
    ⎢0  0  0  3  0  0  0⎥
    ⎢                   ⎥
    ⎢0  0  0  0  3  0  0⎥
    ⎢                   ⎥
    ⎢0  0  0  0  0  3  0⎥
    ⎢                   ⎥
    ⎣0  0  0  0  0  0  3⎦
    
    In [27]: Kr
    Out[27]: 
    ⎡ 3    -3/2   0     0     0     0     0  ⎤
    ⎢                                        ⎥
    ⎢-3/2   3    -3/2   0     0     0     0  ⎥
    ⎢                                        ⎥
    ⎢ 0    -3/2   3    -3/2   0     0     0  ⎥
    ⎢                                        ⎥
    ⎢ 0     0    -3/2   3    -3/2   0     0  ⎥
    ⎢                                        ⎥
    ⎢ 0     0     0    -3/2   3    -3/2   0  ⎥
    ⎢                                        ⎥
    ⎢ 0     0     0     0    -3/2   3    -3/2⎥
    ⎢                                        ⎥
    ⎣ 0     0     0     0     0    -3/2   3  ⎦
    
    In [28]: evs = (Mr.inv() * Kr).eigenvects()
    
    In [29]: evs[0]
    Out[29]: 
    ⎛      ⎡⎡-1⎤⎤⎞
    ⎜      ⎢⎢  ⎥⎥⎟
    ⎜      ⎢⎢0 ⎥⎥⎟
    ⎜      ⎢⎢  ⎥⎥⎟
    ⎜      ⎢⎢1 ⎥⎥⎟
    ⎜      ⎢⎢  ⎥⎥⎟
    ⎜1, 1, ⎢⎢0 ⎥⎥⎟
    ⎜      ⎢⎢  ⎥⎥⎟
    ⎜      ⎢⎢-1⎥⎥⎟
    ⎜      ⎢⎢  ⎥⎥⎟
    ⎜      ⎢⎢0 ⎥⎥⎟
    ⎜      ⎢⎢  ⎥⎥⎟
    ⎝      ⎣⎣1 ⎦⎦⎠
    
    In [30]: evs[1]
    Out[30]: 
    ⎛           ⎡⎡-1 ⎤⎤⎞
    ⎜           ⎢⎢   ⎥⎥⎟
    ⎜           ⎢⎢-√2⎥⎥⎟
    ⎜           ⎢⎢   ⎥⎥⎟
    ⎜           ⎢⎢-1 ⎥⎥⎟
    ⎜    √2     ⎢⎢   ⎥⎥⎟
    ⎜1 - ──, 1, ⎢⎢ 0 ⎥⎥⎟
    ⎜    2      ⎢⎢   ⎥⎥⎟
    ⎜           ⎢⎢ 1 ⎥⎥⎟
    ⎜           ⎢⎢   ⎥⎥⎟
    ⎜           ⎢⎢√2 ⎥⎥⎟
    ⎜           ⎢⎢   ⎥⎥⎟
    ⎝           ⎣⎣ 1 ⎦⎦⎠
    
    In [31]: evs[2]
    Out[31]: 
    ⎛                   ⎡⎡                               1                                ⎤⎤⎞
    ⎜                   ⎢⎢                                                                ⎥⎥⎟
    ⎜                   ⎢⎢                             ________                           ⎥⎥⎟
    ⎜                   ⎢⎢                           ╲╱ 2 - √2                            ⎥⎥⎟
    ⎜                   ⎢⎢                                                                ⎥⎥⎟
    ⎜                   ⎢⎢                                   2                            ⎥⎥⎟
    ⎜                   ⎢⎢                   ⎛      ________⎞                             ⎥⎥⎟
    ⎜                   ⎢⎢                   ⎜    ╲╱ 2 - √2 ⎟        ________             ⎥⎥⎟
    ⎜                   ⎢⎢            -5 + 4⋅⎜1 - ──────────⎟  + 4⋅╲╱ 2 - √2              ⎥⎥⎟
    ⎜                   ⎢⎢                   ⎝        2     ⎠                             ⎥⎥⎟
    ⎜                   ⎢⎢                                                                ⎥⎥⎟
    ⎜      ________     ⎢⎢                        3                                      2⎥⎥⎟
    ⎜    ╲╱ 2 - √2      ⎢⎢        ⎛      ________⎞                       ⎛      ________⎞ ⎥⎥⎟
    ⎜1 - ──────────, 1, ⎢⎢        ⎜    ╲╱ 2 - √2 ⎟         ________      ⎜    ╲╱ 2 - √2 ⎟ ⎥⎥⎟
    ⎜        2          ⎢⎢-16 - 8⋅⎜1 - ──────────⎟  + 10⋅╲╱ 2 - √2  + 24⋅⎜1 - ──────────⎟ ⎥⎥⎟
    ⎜                   ⎢⎢        ⎝        2     ⎠                       ⎝        2     ⎠ ⎥⎥⎟
    ⎜                   ⎢⎢                                                                ⎥⎥⎟
    ⎜                   ⎢⎢                                   2                            ⎥⎥⎟
    ⎜                   ⎢⎢                   ⎛      ________⎞                             ⎥⎥⎟
    ⎜                   ⎢⎢                   ⎜    ╲╱ 2 - √2 ⎟        ________             ⎥⎥⎟
    ⎜                   ⎢⎢            -5 + 4⋅⎜1 - ──────────⎟  + 4⋅╲╱ 2 - √2              ⎥⎥⎟
    ⎜                   ⎢⎢                   ⎝        2     ⎠                             ⎥⎥⎟
    ⎜                   ⎢⎢                                                                ⎥⎥⎟
    ⎜                   ⎢⎢                             ________                           ⎥⎥⎟
    ⎜                   ⎢⎢                           ╲╱ 2 - √2                            ⎥⎥⎟
    ⎜                   ⎢⎢                                                                ⎥⎥⎟
    ⎝                   ⎣⎣                               1                                ⎦⎦⎠
    

    I won't show the rest because it is long but for completeness these are the approximate and exact eigenvalues as computed by SymPy:

    In [34]: (M.inv() * K).eigenvals()
    Out[34]: 
    {0.0761204674887132: 1, 0.292893218813452: 1, 0.61731656763491: 1, 1.0: 1, 1.38268343236509: 1, 1.7 ↪
    
    ↪ 0710678118655: 1, 1.92387953251129: 1}
    
    In [35]: (Mr.inv() * Kr).eigenvals()
    Out[35]: 
    ⎧                         ________             ________                    ________             ___ ↪
    ⎪          √2            ╱ 1   √2             ╱ √2   1      √2            ╱ 1   √2             ╱ √2 ↪
    ⎨1: 1, 1 - ──: 1, 1 -   ╱  ─ - ── : 1, 1 -   ╱  ── + ─ : 1, ── + 1: 1,   ╱  ─ - ──  + 1: 1,   ╱  ── ↪
    ⎪          2          ╲╱   2   4           ╲╱   4    2      2          ╲╱   2   4           ╲╱   4  ↪
    ⎩                                                                                                   ↪
    
    ↪ _____       ⎫
    ↪    1        ⎪
    ↪  + ─  + 1: 1⎬
    ↪    2        ⎪
    ↪             ⎭
    

    These are the eigenvalues as computed by SciPy:

    In [36]: from scipy.linalg import eigvals
    
    In [37]: eigvals(Kn, Mn)
    Out[37]: 
    array([0.07612047+0.j, 0.29289322+0.j, 0.61731657+0.j, 1.        +0.j,
           1.92387953+0.j, 1.70710678+0.j, 1.38268343+0.j])