Nonlinear Solver Lab
Demonstration of W4 method
- To demonstrate the W4 method, we show below how to solve the system of nonlinear equations by the Python version of the W4 method with the UL decomposition and the LH decomposition.
- In this example, a problem in the paper is solved. You can try other problems using the Python notebook.
Package Inclusion¶
- Sympy :: To define the set of nonlinear equations
- Numpy :: For iterative solver
- Scipy :: To use Linear solver
- Matplotlib :: For Basin figure
In [1]:
import sympy as sy;
import numpy as np;
from numpy import cos,sin,pi;
from scipy import linalg;
import matplotlib.pyplot as plt;
Parameter Setup¶
For the W4 method, we define
- dim : dimension of system
- dtau : $\Delta\tau$ for evolution
- errmax : Criterion of error to stop iteration
- itermax : Maximum iteration.
In [2]:
dim=2;
In [3]:
dtau=0.5;
errmax = pow(10,-4);
itermax = pow(10,3);
In [4]:
### Variables
### x[0], x[1], x[2], x[3], ... , x[dim-1]
###
x = []
for i in range(dim):
vname = 'x[' + str(i) +']';
x.append(sy.symbols(vname));
Definition of Source in the Sympy style¶
- F(x) : System of nonlinear equations
- |F(x)| : Absolute components
- J : Jacobian matrix $\partial$ F / $\partial$ x
In [5]:
### Source
### f[0], f[1], f[2], ... , f[dim-1]
f = [];
f.append( (x[0]**2 +x[1]**2 -4) );
f.append( (x[0]**2*x[1] -1) );
In [6]:
# input source
def calc_source(x,f):
### Summation of absolute values of all terms in each equation
### to compare the error with the typical value of each equation
fa = [];
for j in range(dim):
tmp = 0;
for i in range(len(f[j].args)):
tmp = tmp + abs(f[j].args[i]);
fa.append(tmp);
### Definition of variables, sources, absolute sources(x, F, |F|) as vector
v = sy.Matrix([x]).transpose();
F = sy.Matrix([f]).transpose();
Fa = sy.Matrix([fa]).transpose();
### Analytic calculation of Jacobian(dim x dim Matrix)
J = sy.Matrix();
for i in range(dim):
J = J.col_insert(i,F.diff(x[i]));
### From Sympy to Numpy
arg = v.transpose();
vn = sy.lambdify(arg, v, "numpy")
Fn = sy.lambdify(arg, F, "numpy")
Fan = sy.lambdify(arg, Fa, "numpy")
Jacn = sy.lambdify(arg, J, "numpy")
return vn,Fn,Fan,Jacn;
In [7]:
vn, Fn, Fan, Jacn = calc_source(x,f);
Problem we want to solve¶
- Find the points where two curves cross
- There are four solutions from the figure below.
In [8]:
fig = plt.figure(figsize=(8,8));
ax = fig.add_subplot(1,1,1);
theta = np.linspace(0,2.0*pi,100);
x1 = 2.0*cos(theta);
y1 = 2.0*sin(theta);
ax.plot(x1,y1);
x2 = np.linspace(-5.,5.,100);
y2 = 1./x2**2;
ax.plot(x2,y2);
ax.set_xlabel("$x$");
ax.set_ylabel("$y$");
ax.set_xlim(-5,5);
ax.set_ylim(-5,5);
ax.set_aspect('equal');
ax.set_title("Problem");
Definition :: Newton-Raphson method¶
In [9]:
### NR method
def nrlu(v,F,Fa,J,ini,dt,itermax,errmax):
### Initialization for x as v0
# p0 = np.zeros(dim);
v0 = v(*ini).transpose()[0];
### Main iteration Loop
for i in range(itermax):
J0=J(*v0);
F0=F(*v0).transpose()[0].transpose();
Fa0=Fa(*v0).transpose()[0].transpose();
### Criterion to stop the iteration
### Evaluation of Source F
err = 0.0;
for k in range(dim):
err = max(err,abs(F0[k]/Fa0[k]));
if err < errmax:
break;
### Decomposition of Jacobian into U and L
P, L, U = linalg.lu(J0);
srcx = -linalg.solve(U,linalg.solve(L,linalg.solve(P,F0)));
### Increment of x
v0 = v0 +srcx*dt;
return v0, i, err
Definition :: W4UL method¶
In [10]:
### W4 method with UL decomposition
def w4ul(v,F,Fa,J,ini,dt,itermax,errmax):
### Initialization for x and p as v0 and p0
p0 = np.zeros(dim);
v0 = v(*ini).transpose()[0];
### Main iteration Loop
for i in range(itermax):
J0=J(*v0);
F0=F(*v0).transpose()[0].transpose();
Fa0=Fa(*v0).transpose()[0].transpose();
### Criterion to stop the iteration
### Evaluation of Source F
err = 0.0;
for k in range(dim):
err = max(err,abs(F0[k]/Fa0[k]));
if err < errmax:
break;
### Decomposition of Jacobian into U and L
Jinv = linalg.inv(J0);
P, L, U = linalg.lu(Jinv.transpose());
srcp = -2*p0 -L.transpose().dot(P.dot(F0));
srcx = U.transpose().dot(p0);
### Increment of x and p
v0 = v0 +srcx*dt;
p0 = p0 +srcp*dt;
return v0, i, err
Definition :: W4LH method¶
In [11]:
### W4 method with LH decomposition
def w4lh(v,F,Fa,J,ini,dt,itermax,errmax):
### Initialization for x and p as v0 and p0
p0 = np.zeros(dim);
v0 = v(*ini).transpose()[0];
### Main Iteration Loop
for i in range(itermax):
J0=J(*v0);
F0=F(*v0).transpose()[0].transpose();
Fa0=Fa(*v0).transpose()[0].transpose();
### Criterion to stop the iteration
### Evaluation of Source F
err = 0.0;
for k in range(dim):
err = max(err,abs(F0[k]/Fa0[k]));
if err < errmax:
break;
### Decomposition of Jacobian into L and H
Q, R = linalg.qr(J0.transpose());
srcp = -2*p0 -linalg.solve(R.transpose(),F0);
srcx = Q.dot(p0);
v0 = v0 +srcx*dt;
p0 = p0 +srcp*dt;
return v0, i, err
Results by Newton-Raphson method¶
In [12]:
### Output data for Basin Plot by the W4UL
dtau=1.;
errmax = pow(10,-4);
itermax = pow(10,3);
### Number of Division(Resolution for Basin Plot)
ND=20
xd_nr = np.array([]);
yd_nr = np.array([]);
zd_nr = np.array([]);
wd_nr = np.array([]);
for i in range(ND):
for j in range(ND):
### Initial guess
vini = np.array([]);
x0 = (i+0.5)*10./ND -5.;
y0 = (j+0.5)*10./ND -5.;
vini = np.append(vini, x0);
vini = np.append(vini, y0);
### Nonlinear Solver by the Newton-Raphson method
vans, iter, err = nrlu(vn,Fn,Fan,Jacn,vini,dtau,itermax,errmax);
### Save data
wd_nr = np.append(wd_nr,iter);
xd_nr = np.append(xd_nr,x0);
yd_nr = np.append(yd_nr,y0);
if(iter>=(itermax-1)):
zd_nr = np.append(zd_nr,0);
else:
zd_nr = np.append(zd_nr,vans[0]);
Results by W4UL method¶
In [13]:
### Output data for Basin Plot by the W4UL
dtau=0.5;
errmax = pow(10,-4);
itermax = pow(10,3);
### Number of Division(Resolution for Basin Plot)
ND=20
xd_w4ul = np.array([]);
yd_w4ul = np.array([]);
zd_w4ul = np.array([]);
wd_w4ul = np.array([]);
for i in range(ND):
for j in range(ND):
### Initial guess
vini = np.array([]);
x0 = (i+0.5)*10./ND -5.;
y0 = (j+0.5)*10./ND -5.;
vini = np.append(vini, x0);
vini = np.append(vini, y0);
### Nonlinear Solver by the W4 method with the UL decomposition
vans, iter, err = w4ul(vn,Fn,Fan,Jacn,vini,dtau,itermax,errmax);
### Save data
wd_w4ul = np.append(wd_w4ul,iter);
xd_w4ul = np.append(xd_w4ul,x0);
yd_w4ul = np.append(yd_w4ul,y0);
if(iter>=(itermax-1)):
zd_w4ul = np.append(zd_w4ul,0);
else:
zd_w4ul = np.append(zd_w4ul,vans[0]);
Results by W4LH method¶
In [14]:
### Output data for Basin Plot by the W4UL
dtau=0.5;
errmax = pow(10,-4);
itermax = pow(10,3);
### Number of Division(Resolution for Basin Plot)
ND=20
xd_w4lh = np.array([]);
yd_w4lh = np.array([]);
zd_w4lh = np.array([]);
wd_w4lh = np.array([]);
for i in range(ND):
for j in range(ND):
### Initial guess
vini = np.array([]);
x0 = (i+0.5)*10./ND -5.;
y0 = (j+0.5)*10./ND -5.;
vini = np.append(vini, x0);
vini = np.append(vini, y0);
### Nonlinear Solver by the W4 method with the LH decomposition
vans, iter, err = w4lh(vn,Fn,Fan,Jacn,vini,dtau,itermax,errmax);
### Save data
wd_w4lh = np.append(wd_w4lh,iter);
xd_w4lh = np.append(xd_w4lh,x0);
yd_w4lh = np.append(yd_w4lh,y0);
if(iter>=(itermax-1)):
zd_w4lh = np.append(zd_w4lh,0);
else:
zd_w4lh = np.append(zd_w4lh,vans[0]);
In [15]:
### Plot Basins for Newton-Raphson, W4UL, and W4LH methods
### Four solutions for plots
solx = np.array([]);
soly = np.array([]);
x0=-5.0
y0= 0.5
vini = np.array([]);
vini = np.append(vini, x0);
vini = np.append(vini, y0);
vans, iter, err = w4ul(vn,Fn,Fan,Jacn,vini,dtau,itermax,errmax);
solx = np.append(solx, vans[0]);
soly = np.append(soly, vans[1]);
x0=-0.5
y0= 5.0
vini = np.array([]);
vini = np.append(vini, x0);
vini = np.append(vini, y0);
vans, iter, err = w4ul(vn,Fn,Fan,Jacn,vini,dtau,itermax,errmax);
solx = np.append(solx, vans[0]);
soly = np.append(soly, vans[1]);
x0=0.5
y0=5.0
vini = np.array([]);
vini = np.append(vini, x0);
vini = np.append(vini, y0);
vans, iter, err = w4ul(vn,Fn,Fan,Jacn,vini,dtau,itermax,errmax);
solx = np.append(solx, vans[0]);
soly = np.append(soly, vans[1]);
x0=5.0
y0=0.5
vini = np.array([]);
vini = np.append(vini, x0);
vini = np.append(vini, y0);
vans, iter, err = w4ul(vn,Fn,Fan,Jacn,vini,dtau,itermax,errmax);
solx = np.append(solx, vans[0]);
soly = np.append(soly, vans[1]);
### Basin Figures
fig = plt.figure(figsize=(12,5));
ax1 = fig.add_subplot(1,3,1);
ax1.scatter(xd_nr,yd_nr,s=120,c=zd_nr,marker='s',cmap='Accent');
ax1.set_xlabel("$x_{ini}$");
ax1.set_ylabel("$y_{ini}$");
ax1.set_xlim(-5,5);
ax1.set_ylim(-5,5);
ax1.set_aspect('equal');
ax1.scatter(solx,soly,s=120,c='black',marker='x');
ax1.set_title("NR");
ax2 = fig.add_subplot(1,3,2);
ax2.scatter(xd_w4ul,yd_w4ul,s=120,c=zd_w4ul,marker='s',cmap='Accent');
ax2.set_xlabel("$x_{ini}$");
ax2.set_ylabel("$y_{ini}$");
ax2.set_xlim(-5,5);
ax2.set_ylim(-5,5);
ax2.set_aspect('equal');
ax2.scatter(solx,soly,s=120,c='black',marker='x');
ax2.set_title("W4UL");
ax3 = fig.add_subplot(1,3,3);
ax3.scatter(xd_w4lh,yd_w4lh,s=120,c=zd_w4lh,marker='s',cmap='Accent');
ax3.set_xlabel("$x_{ini}$");
ax3.set_ylabel("$y_{ini}$");
ax3.set_xlim(-5,5);
ax3.set_ylim(-5,5);
ax3.set_aspect('equal');
ax3.scatter(solx,soly,s=120,c='black',marker='x');
ax3.set_title("W4LH");
fig.tight_layout();
Comments on Basin figures¶
How to see the figures¶
- $x_{ini}$, $y_{ini}$ :: x[0], x[1]
- Black Crosses :: Four solutions to the system of nonlinear equations
- Color difference :: Different solution which the method finds from the initial guess
- Blue color :: method fails to find any solution
What we can see¶
- Newton-Raphson method sometimes fails to find a solution as a well-known fact, while w4 method can do it for this problem. (Blue color disappears.)
- All methods have the local convergence. (Initial guess close to the solution reaches it.)
- Which solution we can obtain by the W4 method strongly depends on how to decompose the Jacobian matrix, W4UL or W4LH. (Note that the Python version of W4UL decomposition is different from the FORTRAN90 version in the original paper by the permutation matrix.)
In [ ]: