View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.math4.legacy.field.linalg;
18  
19  import java.util.Arrays;
20  import java.util.List;
21  import java.util.ArrayList;
22  import org.apache.commons.numbers.field.Field;
23  import org.apache.commons.math4.legacy.linear.AnyMatrix;
24  
25  /**
26   * Square matrix whose elements define a {@link Field}.
27   *
28   * @param <T> Type of the field elements.
29   *
30   * @since 4.0
31   */
32  public final class FieldDenseMatrix<T>
33      implements AnyMatrix {
34      /** Field. */
35      private final Field<T> field;
36      /** Number of rows. */
37      private final int rows;
38      /** Number of columns. */
39      private final int columns;
40      /**
41       * Data storage (in row-major order).
42       *
43       * <p>Note: This is an Object[] that has been cast to T[] for convenience. It should not be
44       * exposed to avoid heap pollution. It is expected all entries stored in the array are
45       * instances of T.
46       */
47      private final T[] data;
48  
49      /**
50       * @param f Field.
51       * @param r Number of rows.
52       * @param c Number of columns.
53       * @throws IllegalArgumentException if {@code r <= 0} or {@code c <= 0}.
54       */
55      @SuppressWarnings("unchecked")
56      private FieldDenseMatrix(Field<T> f,
57                               int r,
58                               int c) {
59          if (r <= 0 ||
60              c <= 0) {
61              throw new IllegalArgumentException("Negative size");
62          }
63  
64          field = f;
65          rows = r;
66          columns = c;
67          data = (T[]) new Object[r * c];
68      }
69  
70      /**
71       * Factory method.
72       *
73       * @param <T> Type of the field elements.
74       * @param f Field.
75       * @param r Number of rows.
76       * @param c Number of columns.
77       * @return a new instance.
78       * @throws IllegalArgumentException if {@code r <= 0} or {@code c <= 0}.
79       */
80      public static <T> FieldDenseMatrix<T> create(Field<T> f,
81                                                   int r,
82                                                   int c) {
83          return new FieldDenseMatrix<>(f, r, c);
84      }
85  
86      /**
87       * Factory method.
88       *
89       * @param <T> Type of the field elements.
90       * @param f Field.
91       * @param r Number of rows.
92       * @param c Number of columns.
93       * @return a matrix with elements zet to {@link Field#zero() zero}.
94       * @throws IllegalArgumentException if {@code r <= 0} or {@code c <= 0}.
95       */
96      public static <T> FieldDenseMatrix<T> zero(Field<T> f,
97                                                 int r,
98                                                 int c) {
99          return create(f, r, c).fill(f.zero());
100     }
101 
102     /**
103      * Factory method.
104      *
105      * @param <T> Type of the field elements.
106      * @param f Field.
107      * @param n Dimension of the matrix.
108      * @throws IllegalArgumentException if {@code n <= 0}.
109      * @return the identity matrix.
110      */
111     public static <T> FieldDenseMatrix<T> identity(Field<T> f,
112                                                    int n) {
113         final FieldDenseMatrix<T> r = zero(f, n, n);
114 
115         for (int i = 0; i < n; i++) {
116             r.set(i, i, f.one());
117         }
118 
119         return r;
120     }
121 
122     /** {@inheritDoc} */
123     @Override
124     public boolean equals(Object other) {
125         if (this == other) {
126             return true;
127         } else {
128             if (other instanceof FieldDenseMatrix) {
129                 final FieldDenseMatrix<?> m = (FieldDenseMatrix<?>) other;
130                 return field.equals(m.field) &&
131                     rows == m.rows &&
132                     columns == m.columns &&
133                     Arrays.equals(data, m.data);
134             } else {
135                 return false;
136             }
137         }
138     }
139 
140     /**
141      * Copies this matrix.
142      *
143      * @return a new instance.
144      */
145     public FieldDenseMatrix<T> copy() {
146         final FieldDenseMatrix<T> r = create(field, rows, columns);
147         System.arraycopy(data, 0, r.data, 0, data.length);
148         return r;
149     }
150 
151     /**
152      * Transposes this matrix.
153      *
154      * @return a new instance.
155      */
156     public FieldDenseMatrix<T> transpose() {
157         final FieldDenseMatrix<T> r = create(field, columns, rows);
158 
159         for (int i = 0; i < rows; i++) {
160             for (int j = 0; j < columns; j++) {
161                 r.set(j, i, get(i, j));
162             }
163         }
164 
165         return r;
166     }
167 
168     /** {@inheritDoc} */
169     @Override
170     public int getRowDimension() {
171         return rows;
172     }
173 
174     /** {@inheritDoc} */
175     @Override
176     public int getColumnDimension() {
177         return columns;
178     }
179 
180     /**
181      * @return the field associated with the matrix entries.
182      */
183     public Field<T> getField() {
184         return field;
185     }
186 
187     /**
188      * Sets all elements to the given value.
189      *
190      * @param value Value of the elements of the matrix.
191      * @return {@code this}.
192      */
193     public FieldDenseMatrix<T> fill(T value) {
194         Arrays.fill(data, value);
195         return this;
196     }
197 
198     /**
199      * Gets an element.
200      *
201      * @param i Row.
202      * @param j Column.
203      * @return the element at (i, j).
204      */
205     public T get(int i,
206                  int j) {
207         return data[i * columns + j];
208     }
209 
210     /**
211      * Sets an element.
212      *
213      * @param i Row.
214      * @param j Column.
215      * @param value Value.
216      */
217     public void set(int i,
218                     int j,
219                     T value) {
220         data[i * columns + j] = value;
221     }
222 
223     /**
224      * Addition.
225      *
226      * @param other Matrix to add.
227      * @return a new instance with the result of the addition.
228      * @throws IllegalArgumentException if the dimensions do not match.
229      */
230     public FieldDenseMatrix<T> add(FieldDenseMatrix<T> other) {
231         checkAdd(other);
232         final FieldDenseMatrix<T> r = create(field, rows, columns);
233 
234         for (int i = 0; i < data.length; i++) {
235             r.data[i] = field.add(data[i], other.data[i]);
236         }
237 
238         return r;
239     }
240 
241     /**
242      * Subtraction.
243      *
244      * @param other Matrix to subtract.
245      * @return a new instance with the result of the subtraction.
246      * @throws IllegalArgumentException if the dimensions do not match.
247      */
248     public FieldDenseMatrix<T> subtract(FieldDenseMatrix<T> other) {
249         checkAdd(other);
250         final FieldDenseMatrix<T> r = create(field, rows, columns);
251 
252         for (int i = 0; i < data.length; i++) {
253             r.data[i] = field.subtract(data[i], other.data[i]);
254         }
255 
256         return r;
257     }
258 
259     /**
260      * Negate.
261      *
262      * @return a new instance with the opposite matrix.
263      */
264     public FieldDenseMatrix<T> negate() {
265         final FieldDenseMatrix<T> r = create(field, rows, columns);
266 
267         for (int i = 0; i < data.length; i++) {
268             r.data[i] = field.negate(data[i]);
269         }
270 
271         return r;
272     }
273 
274     /**
275      * Multiplication.
276      *
277      * @param other Matrix to multiply with.
278      * @return a new instance with the result of the multiplication.
279      * @throws IllegalArgumentException if the dimensions do not match.
280      */
281     public FieldDenseMatrix<T> multiply(FieldDenseMatrix<T> other) {
282         checkMultiply(other);
283         final FieldDenseMatrix<T> r = zero(field, rows, other.columns);
284 
285         for (int i = 0; i < rows; i++) {
286             final int o1 = i * columns;
287             final int o2 = i * r.columns;
288             for (int j = 0; j < other.columns; j++) {
289                 final int o3 = o2 + j;
290                 for (int k = 0; k < columns; k++) {
291                     r.data[o3] = field.add(r.data[o3],
292                                            field.multiply(data[o1 + k],
293                                                           other.data[k * other.columns + j]));
294                 }
295             }
296         }
297 
298         return r;
299     }
300 
301     /**
302      * Multiplies the matrix with itself {@code p} times.
303      *
304      * @param p Exponent.
305      * @return a new instance.
306      * @throws IllegalArgumentException if {@code p < 0}.
307      */
308     public FieldDenseMatrix<T> pow(int p) {
309         checkMultiply(this);
310 
311         if (p < 0) {
312             throw new IllegalArgumentException("Negative exponent: " + p);
313         }
314 
315         if (p == 0) {
316             return identity(field, rows);
317         }
318 
319         if (p == 1) {
320             return copy();
321         }
322 
323         final int power = p - 1;
324 
325         // Only log_2(p) operations are necessary by doing as follows:
326         //    5^214 = 5^128 * 5^64 * 5^16 * 5^4 * 5^2
327         // The same approach is used for A^p.
328 
329         final char[] binary = Integer.toBinaryString(power).toCharArray();
330         final ArrayList<Integer> nonZeroPositions = new ArrayList<>();
331 
332         for (int i = 0; i < binary.length; i++) {
333             if (binary[i] == '1') {
334                 final int pos = binary.length - i - 1;
335                 nonZeroPositions.add(pos);
336             }
337         }
338 
339         final List<FieldDenseMatrix<T>> results = new ArrayList<>(binary.length);
340         results.add(this);
341         for (int i = 1; i < binary.length; i++) {
342             final FieldDenseMatrix<T> s = results.get(i - 1);
343             final FieldDenseMatrix<T> r = s.multiply(s);
344             results.add(r);
345         }
346 
347         FieldDenseMatrix<T> r = this;
348         for (Integer i : nonZeroPositions) {
349             r = r.multiply(results.get(i));
350         }
351 
352         return r;
353     }
354 
355     /** {@inheritDoc} */
356     @Override
357     public int hashCode() {
358         assert false : "hashCode not designed";
359         return 42; // Any arbitrary constant will do.
360     }
361 }