View Javadoc
1   /*
2    * Copyright (C) 2015 Alberto Irurueta Carro (alberto@irurueta.com)
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.irurueta.algebra;
17  
18  /**
19   * Computes Gauss-Jordan elimination for provided matrix using full pivoting,
20   * which provides greater stability.
21   * Gauss-Jordan elimination can be used to compute matrix inversion or to
22   * solve linear systems of equations of the form A * x = b, where Gauss-Jordan
23   * elimination both inverts matrix A and finds solution x at the same time.
24   */
25  public class GaussJordanElimination {
26  
27      /**
28       * Constructor.
29       * Prevents instantiation.
30       */
31      private GaussJordanElimination() {
32      }
33  
34      /**
35       * Computes Gauss-Jordan elimination by attempting to solve linear system
36       * of equations a * x = b. This method computes inverse of matrix "a", and
37       * modifies provided matrix so that its inverse is stored in it after
38       * execution of this method. Likewise, this method modifies b so that
39       * solution x is stored on it after execution of this method.
40       * This method can only be used on squared a matrices.
41       *
42       * @param a linear system of equations matrix. Will contain its inverse
43       *          after execution of this method.
44       * @param b linear system of equations parameters. Will contain the solution
45       *          after execution of this method. If null is provided, solution is not
46       *          stored but matrix inverse is computed anyway. Each column of b is
47       *          considered a new linear system of equations and its solution x is
48       *          computed on the corresponding column of b.
49       * @throws SingularMatrixException if provided matrix "a" is found to be
50       *                                 singular.
51       * @throws WrongSizeException      if provided matrix "a" is not square.
52       */
53      public static void process(final Matrix a, final Matrix b) throws SingularMatrixException, WrongSizeException {
54          if (a.getRows() != a.getColumns()) {
55              throw new WrongSizeException();
56          }
57          if (b != null && b.getRows() != a.getRows()) {
58              throw new WrongSizeException();
59          }
60  
61          int i;
62          var icol = 0;
63          var irow = 0;
64          int j;
65          int k;
66          int l;
67          int ll;
68          final var n = a.getRows();
69          final var m = b != null ? b.getColumns() : 0;
70  
71          double big;
72          double dum;
73          double pivinv;
74          double value;
75          final var indxr = new int[n];
76          final var indxc = new int[n];
77          final var ipiv = new int[n];
78  
79          for (j = 0; j < n; j++) {
80              ipiv[j] = 0;
81          }
82          for (i = 0; i < n; i++) {
83              big = 0.0;
84              for (j = 0; j < n; j++) {
85                  if (ipiv[j] != 1) {
86                      for (k = 0; k < n; k++) {
87                          if (ipiv[k] == 0) {
88                              value = Math.abs(a.getElementAt(j, k));
89                              if (value >= big) {
90                                  big = value;
91                                  irow = j;
92                                  icol = k;
93                              }
94                          }
95                      }
96                  }
97              }
98              ++(ipiv[icol]);
99              if (irow != icol) {
100                 for (l = 0; l < n; l++) {
101                     swap(a.getBuffer(), a.getBuffer(), a.getIndex(irow, l), a.getIndex(icol, l));
102                 }
103                 for (l = 0; l < m; l++) {
104                     swap(b.getBuffer(), b.getBuffer(), b.getIndex(irow, l), b.getIndex(icol, l));
105                 }
106             }
107             indxr[i] = irow;
108             indxc[i] = icol;
109             value = a.getElementAt(icol, icol);
110             if (value == 0.0) {
111                 throw new SingularMatrixException();
112             }
113             pivinv = 1.0 / value;
114             a.setElementAt(icol, icol, 1.0);
115             for (l = 0; l < n; l++) {
116                 a.setElementAt(icol, l, a.getElementAt(icol, l) * pivinv);
117             }
118             for (l = 0; l < m; l++) {
119                 b.setElementAt(icol, l, b.getElementAt(icol, l) * pivinv);
120             }
121             for (ll = 0; ll < n; ll++) {
122                 if (ll != icol) {
123                     dum = a.getElementAt(ll, icol);
124                     a.setElementAt(ll, icol, 0.0);
125                     for (l = 0; l < n; l++) {
126                         a.setElementAt(ll, l, a.getElementAt(ll, l) - a.getElementAt(icol, l) * dum);
127                     }
128                     for (l = 0; l < m; l++) {
129                         b.setElementAt(ll, l, b.getElementAt(ll, l) - b.getElementAt(icol, l) * dum);
130                     }
131                 }
132             }
133         }
134         for (l = n - 1; l >= 0; l--) {
135             if (indxr[l] != indxc[l]) {
136                 for (k = 0; k < n; k++) {
137                     swap(a.getBuffer(), a.getBuffer(), a.getIndex(k, indxr[l]), a.getIndex(k, indxc[l]));
138                 }
139             }
140         }
141     }
142 
143     /**
144      * Computes Gauss-Jordan elimination by attempting to solve linear system
145      * of equations a * x = b. This method computes inverse of matrix "a", and
146      * modifies provided matrix so that its inverse is stored in it after
147      * execution of this method. Likewise, this method modifies b so that
148      * solution x is stored on it after execution of this method.
149      * This method can only be used on squared a matrices.
150      *
151      * @param a linear system of equations matrix. Will contain its inverse
152      *          after execution of this method.
153      * @param b linear system of equations parameters. Will contain the solution
154      *          after execution of this method. If null is provided, solution is not
155      *          stored but matrix inverse is computed anyway.
156      * @throws SingularMatrixException if provided matrix "a" is found to be
157      *                                 singular.
158      * @throws WrongSizeException      if provided matrix "a" is not square.
159      */
160     public static void process(final Matrix a, final double[] b) throws SingularMatrixException, WrongSizeException {
161         final var mb = b != null ? Matrix.newFromArray(b) : null;
162         process(a, mb);
163         if (mb != null) {
164             final var buffer = mb.getBuffer();
165             System.arraycopy(buffer, 0, b, 0, b.length);
166         }
167     }
168 
169     /**
170      * Computes inverse of matrix "a". No solution of a linear system of equations
171      * is computed. This method modifies provided matrix storing the inverse
172      * on it after execution of this method
173      *
174      * @param a matrix to be inverted.
175      * @throws SingularMatrixException if provided matrix is found to be
176      *                                 singular
177      * @throws WrongSizeException      if provided matrix is not square
178      */
179     public static void inverse(final Matrix a) throws SingularMatrixException, WrongSizeException {
180         process(a, (Matrix) null);
181     }
182 
183     /**
184      * Swaps values in arrays at provided positions
185      *
186      * @param array1 1st array
187      * @param array2 2nd array
188      * @param pos1   1st position to be swapped
189      * @param pos2   2nd position to be swapped
190      */
191     private static void swap(final double[] array1, final double[] array2,
192                              final int pos1, final int pos2) {
193         final var value1 = array1[pos1];
194         final var value2 = array2[pos2];
195 
196         array1[pos1] = value2;
197         array2[pos1] = value1;
198     }
199 }