FieldDenseMatrix.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math4.legacy.field.linalg;
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
import org.apache.commons.numbers.field.Field;
import org.apache.commons.math4.legacy.linear.AnyMatrix;
/**
* Square matrix whose elements define a {@link Field}.
*
* @param <T> Type of the field elements.
*
* @since 4.0
*/
public final class FieldDenseMatrix<T>
implements AnyMatrix {
/** Field. */
private final Field<T> field;
/** Number of rows. */
private final int rows;
/** Number of columns. */
private final int columns;
/**
* Data storage (in row-major order).
*
* <p>Note: This is an Object[] that has been cast to T[] for convenience. It should not be
* exposed to avoid heap pollution. It is expected all entries stored in the array are
* instances of T.
*/
private final T[] data;
/**
* @param f Field.
* @param r Number of rows.
* @param c Number of columns.
* @throws IllegalArgumentException if {@code r <= 0} or {@code c <= 0}.
*/
@SuppressWarnings("unchecked")
private FieldDenseMatrix(Field<T> f,
int r,
int c) {
if (r <= 0 ||
c <= 0) {
throw new IllegalArgumentException("Negative size");
}
field = f;
rows = r;
columns = c;
data = (T[]) new Object[r * c];
}
/**
* Factory method.
*
* @param <T> Type of the field elements.
* @param f Field.
* @param r Number of rows.
* @param c Number of columns.
* @return a new instance.
* @throws IllegalArgumentException if {@code r <= 0} or {@code c <= 0}.
*/
public static <T> FieldDenseMatrix<T> create(Field<T> f,
int r,
int c) {
return new FieldDenseMatrix<>(f, r, c);
}
/**
* Factory method.
*
* @param <T> Type of the field elements.
* @param f Field.
* @param r Number of rows.
* @param c Number of columns.
* @return a matrix with elements zet to {@link Field#zero() zero}.
* @throws IllegalArgumentException if {@code r <= 0} or {@code c <= 0}.
*/
public static <T> FieldDenseMatrix<T> zero(Field<T> f,
int r,
int c) {
return create(f, r, c).fill(f.zero());
}
/**
* Factory method.
*
* @param <T> Type of the field elements.
* @param f Field.
* @param n Dimension of the matrix.
* @throws IllegalArgumentException if {@code n <= 0}.
* @return the identity matrix.
*/
public static <T> FieldDenseMatrix<T> identity(Field<T> f,
int n) {
final FieldDenseMatrix<T> r = zero(f, n, n);
for (int i = 0; i < n; i++) {
r.set(i, i, f.one());
}
return r;
}
/** {@inheritDoc} */
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
} else {
if (other instanceof FieldDenseMatrix) {
final FieldDenseMatrix<?> m = (FieldDenseMatrix<?>) other;
return field.equals(m.field) &&
rows == m.rows &&
columns == m.columns &&
Arrays.equals(data, m.data);
} else {
return false;
}
}
}
/**
* Copies this matrix.
*
* @return a new instance.
*/
public FieldDenseMatrix<T> copy() {
final FieldDenseMatrix<T> r = create(field, rows, columns);
System.arraycopy(data, 0, r.data, 0, data.length);
return r;
}
/**
* Transposes this matrix.
*
* @return a new instance.
*/
public FieldDenseMatrix<T> transpose() {
final FieldDenseMatrix<T> r = create(field, columns, rows);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
r.set(j, i, get(i, j));
}
}
return r;
}
/** {@inheritDoc} */
@Override
public int getRowDimension() {
return rows;
}
/** {@inheritDoc} */
@Override
public int getColumnDimension() {
return columns;
}
/**
* @return the field associated with the matrix entries.
*/
public Field<T> getField() {
return field;
}
/**
* Sets all elements to the given value.
*
* @param value Value of the elements of the matrix.
* @return {@code this}.
*/
public FieldDenseMatrix<T> fill(T value) {
Arrays.fill(data, value);
return this;
}
/**
* Gets an element.
*
* @param i Row.
* @param j Column.
* @return the element at (i, j).
*/
public T get(int i,
int j) {
return data[i * columns + j];
}
/**
* Sets an element.
*
* @param i Row.
* @param j Column.
* @param value Value.
*/
public void set(int i,
int j,
T value) {
data[i * columns + j] = value;
}
/**
* Addition.
*
* @param other Matrix to add.
* @return a new instance with the result of the addition.
* @throws IllegalArgumentException if the dimensions do not match.
*/
public FieldDenseMatrix<T> add(FieldDenseMatrix<T> other) {
checkAdd(other);
final FieldDenseMatrix<T> r = create(field, rows, columns);
for (int i = 0; i < data.length; i++) {
r.data[i] = field.add(data[i], other.data[i]);
}
return r;
}
/**
* Subtraction.
*
* @param other Matrix to subtract.
* @return a new instance with the result of the subtraction.
* @throws IllegalArgumentException if the dimensions do not match.
*/
public FieldDenseMatrix<T> subtract(FieldDenseMatrix<T> other) {
checkAdd(other);
final FieldDenseMatrix<T> r = create(field, rows, columns);
for (int i = 0; i < data.length; i++) {
r.data[i] = field.subtract(data[i], other.data[i]);
}
return r;
}
/**
* Negate.
*
* @return a new instance with the opposite matrix.
*/
public FieldDenseMatrix<T> negate() {
final FieldDenseMatrix<T> r = create(field, rows, columns);
for (int i = 0; i < data.length; i++) {
r.data[i] = field.negate(data[i]);
}
return r;
}
/**
* Multiplication.
*
* @param other Matrix to multiply with.
* @return a new instance with the result of the multiplication.
* @throws IllegalArgumentException if the dimensions do not match.
*/
public FieldDenseMatrix<T> multiply(FieldDenseMatrix<T> other) {
checkMultiply(other);
final FieldDenseMatrix<T> r = zero(field, rows, other.columns);
for (int i = 0; i < rows; i++) {
final int o1 = i * columns;
final int o2 = i * r.columns;
for (int j = 0; j < other.columns; j++) {
final int o3 = o2 + j;
for (int k = 0; k < columns; k++) {
r.data[o3] = field.add(r.data[o3],
field.multiply(data[o1 + k],
other.data[k * other.columns + j]));
}
}
}
return r;
}
/**
* Multiplies the matrix with itself {@code p} times.
*
* @param p Exponent.
* @return a new instance.
* @throws IllegalArgumentException if {@code p < 0}.
*/
public FieldDenseMatrix<T> pow(int p) {
checkMultiply(this);
if (p < 0) {
throw new IllegalArgumentException("Negative exponent: " + p);
}
if (p == 0) {
return identity(field, rows);
}
if (p == 1) {
return copy();
}
final int power = p - 1;
// Only log_2(p) operations are necessary by doing as follows:
// 5^214 = 5^128 * 5^64 * 5^16 * 5^4 * 5^2
// The same approach is used for A^p.
final char[] binary = Integer.toBinaryString(power).toCharArray();
final ArrayList<Integer> nonZeroPositions = new ArrayList<>();
for (int i = 0; i < binary.length; i++) {
if (binary[i] == '1') {
final int pos = binary.length - i - 1;
nonZeroPositions.add(pos);
}
}
final List<FieldDenseMatrix<T>> results = new ArrayList<>(binary.length);
results.add(this);
for (int i = 1; i < binary.length; i++) {
final FieldDenseMatrix<T> s = results.get(i - 1);
final FieldDenseMatrix<T> r = s.multiply(s);
results.add(r);
}
FieldDenseMatrix<T> r = this;
for (Integer i : nonZeroPositions) {
r = r.multiply(results.get(i));
}
return r;
}
/** {@inheritDoc} */
@Override
public int hashCode() {
assert false : "hashCode not designed";
return 42; // Any arbitrary constant will do.
}
}