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 }