Home > Back-end >  Solving System of Nonlinear Equations in 100 variables Computationally
Solving System of Nonlinear Equations in 100 variables Computationally

Time:10-05

I have been trying to solve a system of 10,000 non-linear equations with 100 variables using Sympy/Numpy in Python.

Attempt so far:

I tried both nsolve and solve from Sympy, solve in numpy.linalg, all of which were still running after a 5-6 hour wait (and I ended up force-stopping them as I ran out of RAM).

Generating the set of equations with Sympy itself took ~1 hour. I switched to SageMath (Windows native), which seemed to do equation generation itself way better (~3min), still, helpless with solving them.

Is there a way to optimize the running using any specific languages or tricks in SageMath/Python itself or should I look for a more powerful system to run the code?

My system is i7-11300H/16GB RAM.

Edit: linalg.solve was a mistake as I initially thought it is a linear system, later realised that it is not.

Edit:

from sympy import *
x,t=symbols('x,t')
a11, a12, a13, a14, a15, a16, a17, a18, a19, a21, a22, a23, a24 = symbols('a11, a12, a13, a14, a15, a16, a17, a18, a19, a21, a22, a23, a24')
Coeffs = [a11, a12, a13, a14, a15, a16, a17, a18, a19, a21, a22, a23, a24]
#E = expression in x,t,aij, 0<i<11,0<j<11 (nonlinear in aij)
## Sample
E = 1/2*(9*(8*t   1)*a11   3*(t**2   2*t   1)*a21   4*(t**3   3*t**2   3*t   1)*a12   5*(t**4   4*t**3)*a13   6*(t**5   5*t**4)*a14   7*(t**6   6*t**5   1)*a15   8*(t**7   7*t)*a16   2*a17*(t   1)   a18)*x**2   1/48*(13860*(252*(t**10   10*t)*a19   1260*(t**2   2*t)*a21   840*(t**3   3*t**2   3*t)*a22   630*(t**4)*a23   504*(t**5   10*t**2   5*t))*a24)
Eqs = [E.subs({x:1/k,t:1/m}) for k in range(1,100) for m in range(1,100)]
sol = solve(Eqs, Coeffs)

Also tried nsolve with array of 0s as initial values.

CodePudding user response:

After you have updated your question I think I understand what you are doing but I think that you are not approaching the problem in the right way.

Firstly the way you define the system of equations introduces floating point coefficients and polynomial equations with floats can be extremely ill-conditioned so make sure to use e.g. S(1)/2 or Rational(1, 2) rather than 1/2 like this:

from sympy import *
x,t=symbols('x,t')
Coeffs = symbols('a11, a12, a13, a14, a15, a16, a17, a18, a19, a21, a22, a23, a24')
a11, a12, a13, a14, a15, a16, a17, a18, a19, a21, a22, a23, a24 = Coeffs

#E = expression in x,t,aij, 0<i<11,0<j<11 (nonlinear in aij)
## Sample
E = (
      S(1)/2*(9*(8*t   1)*a11
      3*(t**2   2*t   1)*a21
      4*(t**3   3*t**2   3*t   1)*a12
      5*(t** 4   4*t**3)*a13
      6*(t**5   5*t**4)*a14
      7*(t**6   6*t**5   1)*a15
      8*(t**7   7*t)*a16
      2*a17*(t   1)   a18)*x**2
      S(1)/48*(13860*(
          252*(t**10   10*t)*a19
          1260*(t**2   2*t)*a21
          840*(t**3   3*t**2   3*t)*a22
          630*(t**4)*a23
          504*(t**5   10*t**2   5*t) )*a24
        )
    )

So you have E which looks like this:

In [2]: E
Out[2]: 
    ⎛          ⎛     10         ⎞             ⎛      2         ⎞             ⎛     3         2     
a₂₄⋅⎝13860⋅a₁₉⋅⎝252⋅t     2520⋅t⎠   13860⋅a₂₁⋅⎝1260⋅t    2520⋅t⎠   13860⋅a₂₂⋅⎝840⋅t    2520⋅t    25
───────────────────────────────────────────────────────────────────────────────────────────────────
                                                                                484            5             2             ⎞      ⎛                     ⎛   3   
20⋅t⎠   8731800⋅a₂₃⋅t    6985440⋅t    69854400⋅t    34927200⋅t⎠    2 ⎜a₁₁⋅(72⋅t   9)   a₁₂⋅⎝4⋅t    
───────────────────────────────────────────────────────────────   x ⋅⎜──────────────   ────────────
                                                                     ⎝      2                      

    2           ⎞       ⎛   4       3⎞       ⎛   5       4⎞       ⎛   6       5    ⎞       ⎛   7   
12⋅t    12⋅t   4⎠   a₁₃⋅⎝5⋅t    20⋅t ⎠   a₁₄⋅⎝6⋅t    30⋅t ⎠   a₁₅⋅⎝7⋅t    42⋅t    7⎠   a₁₆⋅⎝8⋅t    
─────────────────   ──────────────────   ──────────────────   ──────────────────────   ────────────
  2                         2                    2                      2                      2   

    ⎞                           ⎛   2          ⎞⎞
56⋅t⎠                 a₁₈   a₂₁⋅⎝3⋅t    6⋅t   3⎠⎟
─────   a₁₇⋅(t   1)   ───   ────────────────────⎟
                       2             2

The first term of E with a24 makes this nonlinear but only mildly so since it is quadratic and polynomial rather than say transcendental or something (you previously only described your equations as nonlinear which can mean anything).

You are substituting 100 different values for each of x and t and then trying to solve the 10000 equations to make E zero for each of those values. It is almost guaranteed that the only possible solutions for these equations are those that make all of the coefficents of the x,t polynomial zero. I guess what you are actually trying to do is find the values of the ai symbols that make E zero for all x,t but we don't need 10000 equations to do that. We can just extract the coefficients and solve those as equations:

In [3]: eqs = Poly(E, [x, t]).coeffs()

In [4]: eqs
Out[4]: 
⎡       7⋅a₁₅                  5⋅a₁₃                                   3⋅a₂₁                       
⎢4⋅a₁₆, ─────, 3⋅a₁₄   21⋅a₁₅, ─────   15⋅a₁₄, 2⋅a₁₂   10⋅a₁₃, 6⋅a₁₂   ─────, 36⋅a₁₁   6⋅a₁₂   28⋅a
⎣         2                      2                                       2                         

                  9⋅a₁₁           7⋅a₁₅         a₁₈   3⋅a₂₁                             363825⋅a₂₃⋅
₁₆   a₁₇   3⋅a₂₁, ─────   2⋅a₁₂   ─────   a₁₇   ───   ─────, 72765⋅a₁₉⋅a₂₄, 145530⋅a₂₄, ───────────
                    2               2            2      2                                     2    

a₂₄                                                                                                
───, 242550⋅a₂₂⋅a₂₄, 363825⋅a₂₁⋅a₂₄   727650⋅a₂₂⋅a₂₄   1455300⋅a₂₄, 727650⋅a₁₉⋅a₂₄   727650⋅a₂₁⋅a₂₄
                                                                                                   

                              ⎤
   727650⋅a₂₂⋅a₂₄   727650⋅a₂₄⎥
                              ⎦

In [5]: %time [sol] = solve(eqs, Coeffs, dict=True)
CPU times: user 236 ms, sys: 8 ms, total: 244 ms
Wall time: 244 ms

In [6]: sol
Out[6]: 
⎧     a₁₈                                               -4⋅a₁₈                 ⎫
⎨a₁₁: ───, a₁₂: 0, a₁₃: 0, a₁₄: 0, a₁₅: 0, a₁₆: 0, a₁₇: ───────, a₂₁: 0, a₂₄: 0⎬
⎩      63                                                  7                   ⎭

In [7]: checksol(eqs, sol)
Out[7]: True

This shows that the system of equations was underdetermined so e.g. a18 can be arbitrary provided a11 = a18/63 etc. If one of the unknowns is not listed in the solution dict then it can take any arbitrary value independently of the others.

I added a %time in there to show that it takes 244 milliseconds to solve this on my not very fast computer (on the current sympy master branch -- it took ~0.5 seconds with sympy 1.8). Note that I do have gmpy2 installed which can make various things faster in SymPy. You might get a speed up from installing sympy 1.9rc1 and also gmpy2 (pip install --pre --upgrade sympy and pip install gmpy2). It's also worth noting that some bugs for systems of this type were recently fixed in sympy so for other examples 1.9 might give more accurate results:

https://github.com/sympy/sympy/pull/21883

CodePudding user response:

You can use threading for your purposes.

Here an explanation from Corey Schäfer --> https://youtu.be/IEEhzQoKtQU

The core idea is to run code in paralellel, meaning your code doesn't go like "...compute first code block ... (finished).... compute second code block ... finished" and so on. With threading your code will run multiple code blocks at once.

A good python library is the multiprocessing library.

But first make sure how many processors you can utilize for your machine.

import multiprocessing as mp
print("Number of processors: ", mp.cpu_count())

--> Number of processors:  4

For example my PC only has 4 processors available

And here an example of threading within python:

import requests
from multiprocessing.dummy import Pool as ThreadPool 

urls = ['https://www.python.org',
        'https://www.python.org/about/']

def get_status(url):
    r = requests.get(url)
    return r.status_code

if __name__ == "__main__":
    pool = ThreadPool(4)  # Make the Pool of workers
    results = pool.map(get_status, urls) #Open the urls in their own threads
    pool.close() #close the pool and wait for the work to finish 
    pool.join() 

This code above will loop through a list of URLs and outputs the status code of the URLs. Without threading the code would do this in a iterative way. First URL --> Status 200 ... NEXT ... Second URL --> Status 200 ...NEXT ... and so on.

I hope I could help you out a bit.

  • Related