Sympy with Cython produces singular matrix when attempting to find roots - python

I am trying to solve a set of nonlinear equations using Sympy. This is my code with a few numbers changed and only one input value (the real code runs through 170,000 lines of data):
import sympy as sp
K0 = 2.00*10**-4
x2, y2 = sp.symbols('x2, y2')
x0 = 500
y0 = 500
R1 = ((x2 - x0)**2 + (y2 - y0)**2)**0.5
R2 = K0 * R1
H2 = sp.atan(R2 * (x2 - x0)/R1)
V2 = sp.atan(R2 * (y2 - y0)/R1)
x, y = sp.symbols('x, y')
x0 = 1.0
y0 = 2.0
x = R1 * H2
y = R1 * V2
dat = sp.nsolve([x - x0, y - y0], [x2, y2], [512, 512]) # This line is the problem
print "dat = %f, %f" % (dat[0], dat[1])
Purely with Python, the code runs fine and produces a good output - but it's very slow. To speed up the process, I used Cython to compile a .pyx file with the exact same code (in a definition, def test()), yet during run-time I'm met with:
File "test.py", line 3, in <module>
demo.test()
File "demo.pyx", line 17, in demo.test
dat = sp.nsolve([x - x0, y - y0], [x2, y2], [512, 512])
File "C:\...\site-packages\sympy\utilities\decorator.py", line 91, in func_wrapper
return func(*args, **kwargs)
File "C:\...\site-packages\sympy\solvers\solvers.py", line 2847, in nsolve
x = findroot(f, x0, J=J, **kwargs)
File "C:\...\site-packages\mpmath\calculus\optimization.py", line 960, in findroot
for x, error in iterations:
File "C:\...\site-packages\mpmath\calculus\optimization.py", line 658, in __iter__
s = self.ctx.lu_solve(Jx, fxn)
File "C:\...\site-packages\mpmath\matrices\linalg.py", line 227, in lu_solve
A, p = ctx.LU_decomp(A)
File "C:\...\site-packages\mpmath\matrices\linalg.py", line 137, in LU_decomp
raise ZeroDivisionError('matrix is numerically singular')
ZeroDivisionError: matrix is numerically singular
I have narrowed down the problem to the x - x0 and y - y0 part. For some reason, the compiled code cannot handle finding the roots when they're not equal to 0. Can nsolve simply not be converted to C using Cython? Is there something I'm missing that I have to do with Cython?

You can use sympy.lambdify together with e.g. SciPy's solvers. If that is not fast enough you could use symengine.Lambdify.
Getting the function signatures right, and deriving the Jacobian, requires you to jump through hoops. If you want to use a library for that I have written pyneqsys:
>>> from pyneqsys.symbolic import SymbolicSys
>>> neqsys = SymbolicSys([x2, y2], [x - x0, y - y0])
>>> neqsys.solve([512, 512])
Out[4]:
(array([ 547.28609349, 594.58064617]),
fjac: array([[ 0.91320338, 0.4075041 ],
[-0.4075041 , 0.91320338]])
fun: array([ -1.37667655e-13, 1.52011737e-12])
message: 'The solution converged.'
nfev: 17
njev: 2
qtf: array([ 1.55620322e-10, 4.63225371e-10])
r: array([ 0.02751454, 0.023682 , 0.03261983])
status: 1
success: True
x: array([ 547.28609349, 594.58064617]))
If those 170 000 solves involves gradually changing parameters pyneqsys can exploit that (by propagating the solution as guess between solves). It can also use symengine.Lambdify automatically by setting the environment variable SYM_BACKEND=sympysymengine

Related

How to solve this time-dependent PDE in Python?

Is there a way to numerically solve the following PDE in Python?
The second term on the RHS has a derivative with respect to time as well as space.
I tried using Py-PDE package in Python, it solves only the form dy(x,t)/dt = f(y(x,t)) so I tried to use a root finding algorithm similar to scipy fzero to get the solution to dy(x,t)/dt - f(y(x,t),dy(x,t)/dt) = 0 (solving for dy(x,t)/dt).
class nonlinearPDE(pde.PDEBase):
def __init__(self, bc={"derivative":0}):
self.bc = bc #boundary conditions for operators
def _make_pde_rhs_numba(self, state):
"""numba-compiled implementation of the PDE"""
laplace = state.grid.make_operator("laplace", bc=self.bc)
def findroot(f, df, x0, nmax):
"""Newton–Raphson method"""
for i in range(nmax):
x0 = x0 - f(x0)/df(x0)
return x0
#jit
def pde_rhs(y, t):
func = lambda dydt : dydt - a*laplace(y) - b*laplace(dydt)
dydt = findroot(func, lambda x : 1, 0, 1)
return dydt
return pde_rhs
However, when the program tries to solve the PDE it throws an error:
File "...\anaconda3\lib\site-packages\pde\solvers\controller.py", line 191, in run
t = stepper(state, t, t_break)
File "...\anaconda3\lib\site-packages\pde\solvers\scipy.py", line 82, in stepper
sol = integrate.solve_ivp(
File "...\anaconda3\lib\site-packages\scipy\integrate\_ivp\ivp.py", line 542, in solve_ivp
solver = method(fun, t0, y0, tf, vectorized=vectorized, **options)
File "...\anaconda3\lib\site-packages\scipy\integrate\_ivp\rk.py", line 94, in __init__
self.f = self.fun(self.t, self.y)
File "...\anaconda3\lib\site-packages\scipy\integrate\_ivp\base.py", line 138, in fun
return self.fun_single(t, y)
File "...\anaconda3\lib\site-packages\scipy\integrate\_ivp\base.py", line 20, in fun_wrapped
return np.asarray(fun(t, y), dtype=dtype)
File "...\anaconda3\lib\site-packages\pde\solvers\scipy.py", line 74, in rhs_helper
return rhs(state_flat.reshape(shape), t).flat # type: ignore
File "...\anaconda3\lib\site-packages\numba\core\dispatcher.py", line 420, in _compile_for_args
error_rewrite(e, 'typing')
File "...\anaconda3\lib\site-packages\numba\core\dispatcher.py", line 361, in error_rewrite
raise e.with_traceback(None)
TypingError: Cannot capture the non-constant value associated with variable 'y' in a function that will escape.
Since no one has posted an answer yet, I managed to get a minimal working example by using scipy odeint with a method of lines to solve the PDE, that is, by discretizing the Laplace operator, and then wrapping the differential equation inside fsolve to get dydt:
import numpy as np
from scipy.integrate import odeint
from scipy.optimize import fsolve
a=1
b=1
t = np.linspace(0,1,100)
y0 = np.asarray([1,0.5,0]) #starting values
N=len(y0)
def laplace(y):
""" Laplace operator in cartesian coordinate system """
d2y_0 = [2*(y[1] - y[0])] #at i=0
d2y_N = [2*(y[-2] - y[-1])] #at i=N
d2y_i = [y[i+2] - 2*y[i+1] + y[i] for i in range(N-2)] #elsewhere
return np.asarray(d2y_0 + d2y_i + d2y_N)
def rhs(y, dydt, t):
""" RHS of PDE including dydt term """
return a*laplace(y) + b*laplace(dydt)
def pde(y, t):
""" Function that solves for dydt using root finding """
return fsolve(lambda dydt : dydt - rhs(y, dydt, t),np.zeros(N))
#calculate
sol = odeint(pde, y0, t)
#plot odeint with fsolve
import matplotlib.pyplot as plt
plt.figure()
plt.plot(t,sol)
plt.grid(ls='--')
plt.xlabel('$t$')
plt.ylabel('$y_i$')
plt.title('odeint with fsolve')
plt.legend(["$i=$"+str(i) for i in range(N)])
#plot theoretical
u10=y0[0]
u20=y0[1]
u30=y0[2]
E1=np.exp(-2*a*t/(1+2*b)) #1st exponential term
E2=np.exp(-4*a*t/(1+4*b)) #2nd exponential term
u1 = 1/4*((1+2*E1+E2)*u10 - 2*(E2-1)*u20 + (1-2*E1+E2)*u30)
u2 = 1/4*((1-E2)*u10 + 2*(E2+1)*u20 + (1-E2)*u30)
u3 = 1/4*((1-2*E1+E2)*u10 - 2*(E2-1)*u20 + (1+2*E1+E2)*u30)
plt.figure()
plt.plot(t,u1,t,u2,t,u3)
plt.grid(ls='--')
plt.xlabel('$t$')
plt.ylabel('$y_i$')
plt.title('Theoretical')
plt.legend(["$i=$"+str(i) for i in range(N)])
Note that the same Laplace discretization method allows us to solve a system of ODEs to get the exact analytical solution with which we verify our numerical calculation (for a N=3 size system):
>>> np.allclose(sol[:,0],u1)
True
>>> np.allclose(sol[:,1],u2)
True
>>> np.allclose(sol[:,2],u3)
True
Seems it works as intended.

ValueError when coding quadratic formula

I am coding the quadratic formula in Python. The following function is supposed to solve x^2-x-20. If you do the math you get -4 and 5.
# Quadratic Formula
from math import sqrt
def findsolutions(a, b, c):
"""
Find solutions to quadratic equations.
"""
x1 = (-b + sqrt(b^2-4*a*c))/(2*a)
x2 = (-b - sqrt(b^2-4*a*c))/(2*a)
return x1, x2
x1, x2 = findsolutions(1,-1,-20)
print("The solutions to x^2-x-20 are", x1, "and", x2)
However, if you run it, you get:
Traceback (most recent call last):
File "C:/Users/Atharv 2020/Desktop/Python/1. Quadratic Formula.py", line 11, in <module>
x1, x2 = findsolutions(1,-1,-20)
File "C:/Users/Atharv 2020/Desktop/Python/1. Quadratic Formula.py", line 7, in findsolutions
x1 = (-b + sqrt(b^2-4*a*c))/(2*a)
ValueError: math domain error
So what is going on?
In Python, ^ is a Bitwise operator called XOR (Exclusive-OR)
I think you've mistook it for calculating power. In python you calculate power using **.
x ** y - x raised to the power y
So fix your code like this
x1 = (-b + sqrt(b**2-(4*a*c)))/(2*a)

Solving a system of differential equations with SymPy

I'm trying to solve the system of differential equations one gets from a tuned mass damper:
https://en.wikipedia.org/wiki/Tuned_mass_damper.
I first initialize all of the constants, variables, and functions that I'll need.
from sympy import *
m1, m2, c1, c2, k1, k2, F0, t, x10, x20 = symbols('m1 m2 c1 c2 k1 k2 F0 t x10 x20')
x1, x2 = symbols('x1 x2', cls=Function)
x1_ = x1(t).diff(t)
x1__ = x1_.diff(t)
x2_ = x2(t).diff(t)
x2__ = x2_.diff(t)
eq1 = Eq(m1*x1__ + c1*x1_ + k1*x1(t) - m2*x2__ - c2*x2_ - k2*x2(t) - F0)
eq2 = Eq(m2*x2__ + c2*x2_ + k1*x2(t))
pprint(eq1)
pprint(eq2)
which prints:
2
d d d
-F₀ + c₁⋅──(x₁(t)) - c₂⋅──(x₂(t)) + k₁⋅x₁(t) - k₂⋅x₂(t) + m₁⋅───(x₁(t)) - m₂⋅─
dt dt 2
dt
2
d
── (x₂(t)) = 0
2
dt
2
d d
c₂⋅──(x₂(t)) + k₁⋅x₂(t) + m₂⋅───(x₂(t)) = 0
dt 2
dt
Then I try passing the two equations to the dsolve sympy function with the appropriate initial conditions:
system = [eq1, eq2]
ics = {x1(0): x10, x2(0): x20}
solution = dsolve(system, (x1(t), x2(t)), ics=ics)
pprint(solution)
However, I'm met with an error:
Traceback (most recent call last):
File "/Users/wyatt_blair/...", line 22, in <module>
solution = dsolve(system, (x1(t), x2(t)), ics=ics)
File "/Users/wyatt_blair/anaconda3/lib/python3.6/site-packages/sympy/solvers/ode.py", line 584, in dsolve
match = classify_sysode(eq, func)
File "/Users/wyatt_blair/anaconda3/lib/python3.6/site-packages/sympy/solvers/ode.py", line 1458, in classify_sysode
type_of_equation = check_linear_2eq_order2(eq, funcs, func_coef)
File "/Users/wyatt_blair/anaconda3/lib/python3.6/site-packages/sympy/solvers/ode.py", line 1568, in check_linear_2eq_order2
x = func[0].func
AttributeError: 'list' object has no attribute 'func'
I tried printing the variable func in ode.py and found the it was: [[x1(t), x2(t)]].
It seems that ode.py was expecting func to look like this: [x1(t), x2(t)]. If I change the two problematic lines in ode.py that were failing to:
x = func[0][0].func
y = func[0][1].func
I get an ImplementationError, which is strange because this equation seems like something SymPy could handle as it isn't too crazy.
Am I passing in x1(t) and x2(t) incorrectly?

SymPy dsolve returns different results for mathematically equivalent differential equations

Here is the content of my script:
from sympy import *
x = symbols('x')
init_printing(use_unicode=True)
f = symbols('f', cls=Function)
diffeq = Eq(x**2 * f(x).diff(x, x) + x * f(x).diff(x) - f(x) , 1/((1+x**2)**(3)) )
print dsolve(diffeq, f(x))
This program returns the following output:
Eq(f(x), (C1*x**2 + C1 + C2*x**4 + C2*x**2 - 15*x**4*atan(x) - 15*x**3 - 18*x**2*atan(x) - 13*x - 3*atan(x))/(16*x*(x**2 + 1)))
But when I define the variable diffeq like this:
diffeq = Eq(f(x).diff(x, x) + f(x).diff(x)/x - f(x)/x**(2) , 1 / ((1+x**2)**(3) * x**(2)) )
then I receive the output:
Traceback (most recent call last):
File "/home/foo/odeSympyTrial01.py", line 12, in <module>
print dsolve(diffeq, f(x))
File "/usr/lib/python2.7/dist-packages/sympy/solvers/ode.py", line 625, in dsolve
x0=x0, n=n, **kwargs)
File "/usr/lib/python2.7/dist-packages/sympy/solvers/deutils.py", line 235, in _desolve
raise NotImplementedError(dummy + "solve" + ": Cannot solve " + str(eq))
NotImplementedError: solve: Cannot solve Derivative(f(x), x, x) + Derivative(f(x), x)/x - f(x)/x**2 - 1/(x**2*(x**2 + 1)**3)
And when I define the variable diffeq like this:
diffeq = Eq(f(x).diff(x, x) * x**(2) + f(x).diff(x) * x**(2) /x - f(x) * x**(2) /x**(2) , 1* x**(2)/((1+x**2)**(3) * x**(2)) )
then I receive the output:
Eq(f(x), (C1*x**2 + C1 + C2*x**4 + C2*x**2 - 15*x**4*atan(x) - 15*x**3 - 18*x**2*atan(x) - 13*x - 3*atan(x))/(16*x*(x**2 + 1)))
In every one of these cases, the differential equations diffeq are mathematically equal. Therefore in my opinion dsolve() should return the same output for each case. Somebody please help me to understand why dsolve() returns an error in the second case. How should the nonhomogeneous linear ordinary differential equation be expressed to ensure dsolve() does not return an error?
Short explanation: the logic of SymPy ODE module is often naive and sometimes incorrect.
As written originally, with
x**2 * f(x).diff(x, x) + x * f(x).diff(x) - f(x)
this matches the form of Cauchy–Euler equation (also known as Euler's equation): the power of x in each coefficient is the order of derivative. SymPy detects this structure and applies an appropriate method. But if you divide by x**2,
f(x).diff(x, x) + f(x).diff(x)/x - f(x)/x**(2)
this is no longer the case: the second derivative does not have the power of x**2 so the match fails. A more careful check could detect the latent Cauchy-Euler structure here, but that's not implemented, as one can see by looking at the source.
You can check that this is indeed what's going on with
classify_ode(diffeq, f(x))
which will return 'nth_linear_euler_eq_nonhomogeneous_variation_of_parameters' in the first case but not the second.
While looking at the source, one can also seen an example of wrong logic.
if coeff.is_Mul:
if coeff.has(f(x)):
return False
return x**order in coeff.args
For example, x**2*sin(x) will pass this check with order=2, which means that SymPy will mistake x**2*sin(x)*f(x).diff(x, x) - f(x) = 0 for Euler's equation. And indeed,
dsolve(x**2*sin(x)*f(x).diff(x, x) - f(x), f(x))
"solves" the equation incorrectly. Do not trust ODE solutions from SymPy.

curve_fit with ODE of unknown coefficients

I'm trying to solve the equation Iy'' + b|y'|y' + ky = 0 and fit the coefficients to data.
This is the code I have so far (ready to run):
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
import pandas as pd
from scipy.optimize import curve_fit
# Define derivatives of function
def f(y, t, I, b, k):
theta, omega = y
derivs = [omega, -b / I * omega * abs(omega) - k / I * theta]
return derivs
# integrate the function
def yint(t, I, b, k, y0, y1):
y = odeint(f, [y0, y1], t, args=(I, b, k))
return y.ravel()
# define t and y to fit
y0 = [.5, 0]
t = np.arange(0, 3, .01)
y = np.cos(t)*np.e**(-.01*t)
# fit
vals, cov = curve_fit(yint, t, y, p0=[0.002245, 1e-5, 0.2492, y0[0], y[1]])
However, when I run the function, I get the error:
Traceback (most recent call last):
File "---", line 24, in <module>
vals, cov = curve_fit(yint, t, y, p0=[0.002245, 1e-5, 0.2492, y0[0], y[1]])
File "---.py", line 578, in curve_fit
res = leastsq(func, p0, args=args, full_output=1, **kw)
File "---.py", line 371, in leastsq
shape, dtype = _check_func('leastsq', 'func', func, x0, args, n)
File "---.py", line 20, in _check_func
res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))
File "---.py", line 447, in _general_function
return function(xdata, *params) - ydata
ValueError: operands could not be broadcast together with shapes (600,) (300,)
Any thoughts on how to fix this?
The problem is that function yint returns an array of shape (600,) for the argument of shape (300,). Think again about yint: it solves a second-order differential equation by representing it as a system of two first-order equations. So the result of y = odeint(...) has two columns, one for the solution itself, the second for its derivative. Its shape is (300,2). Mashing the solution and its derivative together with ravel does not make sense. Instead, you should only take the actual solution, that's the thing you are fitting.
So, replace
return y.ravel()
with
return y[:, 0]

Categories

Resources