In numerical linear algebra, the method of successive over-relaxation (SOR) is a variant of the Gauss–Seidel method for solving a linear system of equations, resulting in faster convergence. A similar method can be used for any slowly converging iterative process.
It was devised simultaneously by David M. Young Jr. and by Stanley P. Frankel in 1950 for the purpose of automatically solving linear systems on digital computers. Over-relaxation methods had been used before the work of Young and Frankel. An example is the method of Lewis Fry Richardson, and the methods developed by R. V. Southwell. However, these methods were designed for computation by human calculators, requiring some expertise to ensure convergence to the solution which made them inapplicable for programming on digital computers. These aspects are discussed in the thesis of David M. Young Jr.
Given a square system of n linear equations with unknown x:
Ax=b
where:
A=\begin{bmatrix}a11&a12& … &a1n\ a21&a22& … &a2n\ \vdots&\vdots&\ddots&\vdots\\an1&an2& … &ann\end{bmatrix}, x=\begin{bmatrix}x1\ x2\ \vdots\ xn\end{bmatrix}, b=\begin{bmatrix}b1\ b2\ \vdots\ bn\end{bmatrix}.
Then A can be decomposed into a diagonal component D, and strictly lower and upper triangular components L and U:
A=D+L+U,
D=\begin{bmatrix}a11&0& … &0\ 0&a22& … &0\ \vdots&\vdots&\ddots&\vdots\\0&0& … &ann\end{bmatrix}, L=\begin{bmatrix}0&0& … &0\ a21&0& … &0\ \vdots&\vdots&\ddots&\vdots\\an1&an2& … &0\end{bmatrix}, U=\begin{bmatrix}0&a12& … &a1n\ 0&0& … &a2n\ \vdots&\vdots&\ddots&\vdots\\0&0& … &0\end{bmatrix}.
The system of linear equations may be rewritten as:
(D+\omegaL)x=\omegab-[\omegaU+(\omega-1)D]x
for a constant ω > 1, called the relaxation factor.
The method of successive over-relaxation is an iterative technique that solves the left hand side of this expression for x, using the previous value for x on the right hand side. Analytically, this may be written as:
x(k+1)=(D+\omegaL)-1(\omegab-[\omegaU+(\omega-1)D]x(k))=L\omegax(k)+c,
where
x(k)
x
x(k+1)
x
(k+1) | |
x | |
i |
=
(k) | |
(1-\omega)x | |
i |
+
\omega | |
aii |
\left(bi-\sumj<iaij
(k+1) | |
x | |
j |
-\sumj>iaij
(k) | |
x | |
j |
\right), i=1,2,\ldots,n.
This can again be written analytically in matrix-vector form without the need of inverting the matrix
(D+\omegaL)
x(k+1)=(1-\omega)x(k)+\omegaD-1(b-Lx(k+1)-Ux(k)).
The choice of relaxation factor ω is not necessarily easy, and depends upon the properties of the coefficient matrix. In 1947, Ostrowski proved that if
A
\rho(L\omega)<1
0<\omega<2
The convergence rate for the SOR method can be analytically derived.One needs to assume the following[2] [3]
\omega\in(0,2)
CJac:=I-D-1A
\mu:=\rho(CJac)<1
A=D+L+U
\operatorname{det}(λD+zL+\tfrac{1}{z}U)=\operatorname{det}(λD+L+U)
z\inC\setminus\{0\}
λ\inC
\rho(C\omega)=\begin{cases}
1 | |
4 |
\left(\omega\mu+\sqrt{\omega2\mu2-4(\omega-1)}\right)2, &0<\omega\leq\omegaopt \\ \omega-1, &\omegaopt<\omega<2 \end{cases}
\omegaopt:=1+\left(
\mu | |
1+\sqrt{1-\mu2 |
\omega=1
2 | |
\rho(C | |
Jac) |
\omega
\rho(C | ||||
|
The last assumption is satisfied for tridiagonal matrices since
Z(λD+L+U)Z-1=λD+zL+\tfrac{1}{z}U
Z
Zii=zi-1
\operatorname{det}(λD+L+U)=\operatorname{det}(Z(λD+L+U)Z-1)
Since elements can be overwritten as they are computed in this algorithm, only one storage vector is needed, and vector indexing is omitted. The algorithm goes as follows:
Inputs:,, Output: Choose an initial guess to the solution repeat until convergence for from 1 until do set to 0 for from 1 until do if ≠ then set to end if end (-loop) set to end (-loop) check if convergence is reached end (repeat)
(1-\omega)\phii+
\omega | |
aii |
(bi-\sigma)
\phii+\omega\left(
bi-\sigma | |
aii |
-\phii\right)
We are presented the linear system
\begin{align} 4x1-x2-6x3+0x4&=2,\\ -5x1-4x2+10x3+8x4&=21,\\ 0x1+9x2+4x3-2x4&=-12,\\ 1x1+0x2-7x3+5x4&=-6. \end{align}
To solve the equations, we choose a relaxation factor
\omega=0.5
\phi=(0,0,0,0)
Iteration | x1 | x2 | x3 | x4 | |
---|---|---|---|---|---|
1 | 0.25 | -2.78125 | 1.6289062 | 0.5152344 | |
2 | 1.2490234 | -2.2448974 | 1.9687712 | 0.9108547 | |
3 | 2.070478 | -1.6696789 | 1.5904881 | 0.76172125 | |
... | ... | ... | ... | ... | |
37 | 2.9999998 | -2.0 | 2.0 | 1.0 | |
38 | 3.0 | -2.0 | 2.0 | 1.0 |
A simple implementation of the algorithm in Common Lisp is offered below.
(defparameter +MAXIMUM-NUMBER-OF-ITERATIONS+ 100 "The number of iterations beyond which the algorithm should cease its operation, regardless of its current solution. A higher number of iterations might provide a more accurate result, but imposes higher performance requirements.")
(declaim (type (integer 0 *) +MAXIMUM-NUMBER-OF-ITERATIONS+))
(defun get-errors (computed-solution exact-solution) "For each component of the COMPUTED-SOLUTION vector, retrieves its error with respect to the expected EXACT-SOLUTION vector, returning a vector of error values. --- While both input vectors should be equal in size, this condition is not checked and the shortest of the twain determines the output vector's number of elements. --- The established formula is the following: Let resultVectorSize = min(computedSolution.length, exactSolution.length) Let resultVector = new vector of resultVectorSize For i from 0 to (resultVectorSize - 1) resultVector[i] = exactSolution[i] - computedSolution[i] Return resultVector" (declare (type (vector number *) computed-solution)) (declare (type (vector number *) exact-solution)) (map '(vector number *) #'- exact-solution computed-solution))
(defun is-convergent (errors &key (error-tolerance 0.001)) "Checks whether the convergence is reached with respect to the ERRORS vector which registers the discrepancy betwixt the computed and the exact solution vector. --- The convergence is fulfilled if and only if each absolute error component is less than or equal to the ERROR-TOLERANCE, that is: For all e in ERRORS, it holds: abs(e) <= errorTolerance." (declare (type (vector number *) errors)) (declare (type number error-tolerance)) (flet ((error-is-acceptable (error) (declare (type number error)) (<= (abs error) error-tolerance))) (every #'error-is-acceptable errors)))
(defun make-zero-vector (size) "Creates and returns a vector of the SIZE with all elements set to 0." (declare (type (integer 0 *) size)) (make-array size :initial-element 0.0 :element-type 'number))
(defun successive-over-relaxation (A b omega &key (phi (make-zero-vector (length b))) (convergence-check #'(lambda (iteration phi) (declare (ignore phi)) (>= iteration +MAXIMUM-NUMBER-OF-ITERATIONS+)))) "Implements the successive over-relaxation (SOR) method, applied upon the linear equations defined by the matrix A and the right-hand side vector B, employing the relaxation factor OMEGA, returning the calculated solution vector. --- The first algorithm step, the choice of an initial guess PHI, is represented by the optional keyword parameter PHI, which defaults to a zero-vector of the same structure as B. If supplied, this vector will be destructively modified. In any case, the PHI vector constitutes the function's result value. --- The terminating condition is implemented by the CONVERGENCE-CHECK, an optional predicate lambda(iteration phi) => generalized-boolean which returns T, signifying the immediate termination, upon achieving convergence, or NIL, signaling continuant operation, otherwise. In its default configuration, the CONVERGENCE-CHECK simply abides the iteration's ascension to the ``+MAXIMUM-NUMBER-OF-ITERATIONS+, ignoring the achieved accuracy of the vector PHI." (declare (type (array number (* *)) A)) (declare (type (vector number *) b)) (declare (type number omega)) (declare (type (vector number *) phi)) (declare (type (function ((integer 1 *) (vector number *)) *) convergence-check)) (let ((n (array-dimension A 0))) (declare (type (integer 0 *) n)) (loop for iteration from 1 by 1 do (loop for i from 0 below n by 1 do (let ((rho 0)) (declare (type number rho)) (loop for j from 0 below n by 1 do (when (/= j i) (let ((a[ij] (aref A i j)) (phi[j] (aref phi j))) (incf rho (* a[ij] phi[j]))))) (setf (aref phi i) (+ (* (- 1 omega) (aref phi i)) (* (/ omega (aref A i i)) (- (aref b i) rho)))))) (format T "~&~d. solution = ~a" iteration phi) ;; Check if convergence is reached. (when (funcall convergence-check iteration phi) (return)))) (the (vector number *) phi))
A simple Python implementation of the pseudo-code provided above.
def sor_solver(A, b, omega, initial_guess, convergence_criteria): """ This is an implementation of the pseudo-code provided in the Wikipedia article. Arguments: A: nxn numpy matrix. b: n dimensional numpy vector. omega: relaxation factor. initial_guess: An initial solution guess for the solver to start with. convergence_criteria: The maximum discrepancy acceptable to regard the current solution as fitting. Returns: phi: solution vector of dimension n. """ step = 0 phi = initial_guess[:] residual = linalg.norm(A @ phi - b) # Initial residual while residual > convergence_criteria: for i in range(A.shape[0]): sigma = 0 for j in range(A.shape[1]): if j != i: sigma += A[i, j] * phi[j] phi[i] = (1 - omega) * phi[i] + (omega / A[i, i]) * (b[i] - sigma) residual = linalg.norm(A @ phi - b) step += 1 print("Step Residual: ".format(step, residual)) return phi
residual_convergence = 1e-8omega = 0.5 # Relaxation factor
A = np.array(4, -1, -6, 0, [-5, -4, 10, 8], [0, 9, 4, -2], [1, 0, -7, 5]])
b = np.array([2, 21, -12, -6])
initial_guess = np.zeros(4)
phi = sor_solver(A, b, omega, initial_guess, residual_convergence)print(phi)
The version for symmetric matrices A, in which
U=LT,
is referred to as Symmetric Successive Over-Relaxation, or (SSOR), in which
P=\left( | D | +L\right) |
\omega |
\omega | |
2-\omega |
D-1\left(
D | |
\omega |
+U\right),
and the iterative method is
xk+1=xk-\gammakP-1(Axk-b), k\ge0.
The SOR and SSOR methods are credited to David M. Young Jr.
See main article: Richardson extrapolation. A similar technique can be used for any iterative method. If the original iteration had the form
xn+1=f(xn)
then the modified version would use
SOR | |
x | |
n+1 |
SOR | |
=(1-\omega)x | |
n+\omega |
SOR | |
f(x | |
n). |
However, the formulation presented above, used for solving systems of linear equations, is not a special case of this formulation if is considered to be the complete vector. If this formulation is used instead, the equation for calculating the next vector will look like
x(k+1)=(1-\omega)x(k)+\omega
-1 | |
L | |
* |
(b-Ux(k)),
where
L*=L+D
\omega>1
\omega<1
There are various methods that adaptively set the relaxation parameter
\omega