FieldDenseMatrix.java

  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. import java.util.Arrays;
  19. import java.util.List;
  20. import java.util.ArrayList;
  21. import org.apache.commons.numbers.field.Field;
  22. import org.apache.commons.math4.legacy.linear.AnyMatrix;

  23. /**
  24.  * Square matrix whose elements define a {@link Field}.
  25.  *
  26.  * @param <T> Type of the field elements.
  27.  *
  28.  * @since 4.0
  29.  */
  30. public final class FieldDenseMatrix<T>
  31.     implements AnyMatrix {
  32.     /** Field. */
  33.     private final Field<T> field;
  34.     /** Number of rows. */
  35.     private final int rows;
  36.     /** Number of columns. */
  37.     private final int columns;
  38.     /**
  39.      * Data storage (in row-major order).
  40.      *
  41.      * <p>Note: This is an Object[] that has been cast to T[] for convenience. It should not be
  42.      * exposed to avoid heap pollution. It is expected all entries stored in the array are
  43.      * instances of T.
  44.      */
  45.     private final T[] data;

  46.     /**
  47.      * @param f Field.
  48.      * @param r Number of rows.
  49.      * @param c Number of columns.
  50.      * @throws IllegalArgumentException if {@code r <= 0} or {@code c <= 0}.
  51.      */
  52.     @SuppressWarnings("unchecked")
  53.     private FieldDenseMatrix(Field<T> f,
  54.                              int r,
  55.                              int c) {
  56.         if (r <= 0 ||
  57.             c <= 0) {
  58.             throw new IllegalArgumentException("Negative size");
  59.         }

  60.         field = f;
  61.         rows = r;
  62.         columns = c;
  63.         data = (T[]) new Object[r * c];
  64.     }

  65.     /**
  66.      * Factory method.
  67.      *
  68.      * @param <T> Type of the field elements.
  69.      * @param f Field.
  70.      * @param r Number of rows.
  71.      * @param c Number of columns.
  72.      * @return a new instance.
  73.      * @throws IllegalArgumentException if {@code r <= 0} or {@code c <= 0}.
  74.      */
  75.     public static <T> FieldDenseMatrix<T> create(Field<T> f,
  76.                                                  int r,
  77.                                                  int c) {
  78.         return new FieldDenseMatrix<>(f, r, c);
  79.     }

  80.     /**
  81.      * Factory method.
  82.      *
  83.      * @param <T> Type of the field elements.
  84.      * @param f Field.
  85.      * @param r Number of rows.
  86.      * @param c Number of columns.
  87.      * @return a matrix with elements zet to {@link Field#zero() zero}.
  88.      * @throws IllegalArgumentException if {@code r <= 0} or {@code c <= 0}.
  89.      */
  90.     public static <T> FieldDenseMatrix<T> zero(Field<T> f,
  91.                                                int r,
  92.                                                int c) {
  93.         return create(f, r, c).fill(f.zero());
  94.     }

  95.     /**
  96.      * Factory method.
  97.      *
  98.      * @param <T> Type of the field elements.
  99.      * @param f Field.
  100.      * @param n Dimension of the matrix.
  101.      * @throws IllegalArgumentException if {@code n <= 0}.
  102.      * @return the identity matrix.
  103.      */
  104.     public static <T> FieldDenseMatrix<T> identity(Field<T> f,
  105.                                                    int n) {
  106.         final FieldDenseMatrix<T> r = zero(f, n, n);

  107.         for (int i = 0; i < n; i++) {
  108.             r.set(i, i, f.one());
  109.         }

  110.         return r;
  111.     }

  112.     /** {@inheritDoc} */
  113.     @Override
  114.     public boolean equals(Object other) {
  115.         if (this == other) {
  116.             return true;
  117.         } else {
  118.             if (other instanceof FieldDenseMatrix) {
  119.                 final FieldDenseMatrix<?> m = (FieldDenseMatrix<?>) other;
  120.                 return field.equals(m.field) &&
  121.                     rows == m.rows &&
  122.                     columns == m.columns &&
  123.                     Arrays.equals(data, m.data);
  124.             } else {
  125.                 return false;
  126.             }
  127.         }
  128.     }

  129.     /**
  130.      * Copies this matrix.
  131.      *
  132.      * @return a new instance.
  133.      */
  134.     public FieldDenseMatrix<T> copy() {
  135.         final FieldDenseMatrix<T> r = create(field, rows, columns);
  136.         System.arraycopy(data, 0, r.data, 0, data.length);
  137.         return r;
  138.     }

  139.     /**
  140.      * Transposes this matrix.
  141.      *
  142.      * @return a new instance.
  143.      */
  144.     public FieldDenseMatrix<T> transpose() {
  145.         final FieldDenseMatrix<T> r = create(field, columns, rows);

  146.         for (int i = 0; i < rows; i++) {
  147.             for (int j = 0; j < columns; j++) {
  148.                 r.set(j, i, get(i, j));
  149.             }
  150.         }

  151.         return r;
  152.     }

  153.     /** {@inheritDoc} */
  154.     @Override
  155.     public int getRowDimension() {
  156.         return rows;
  157.     }

  158.     /** {@inheritDoc} */
  159.     @Override
  160.     public int getColumnDimension() {
  161.         return columns;
  162.     }

  163.     /**
  164.      * @return the field associated with the matrix entries.
  165.      */
  166.     public Field<T> getField() {
  167.         return field;
  168.     }

  169.     /**
  170.      * Sets all elements to the given value.
  171.      *
  172.      * @param value Value of the elements of the matrix.
  173.      * @return {@code this}.
  174.      */
  175.     public FieldDenseMatrix<T> fill(T value) {
  176.         Arrays.fill(data, value);
  177.         return this;
  178.     }

  179.     /**
  180.      * Gets an element.
  181.      *
  182.      * @param i Row.
  183.      * @param j Column.
  184.      * @return the element at (i, j).
  185.      */
  186.     public T get(int i,
  187.                  int j) {
  188.         return data[i * columns + j];
  189.     }

  190.     /**
  191.      * Sets an element.
  192.      *
  193.      * @param i Row.
  194.      * @param j Column.
  195.      * @param value Value.
  196.      */
  197.     public void set(int i,
  198.                     int j,
  199.                     T value) {
  200.         data[i * columns + j] = value;
  201.     }

  202.     /**
  203.      * Addition.
  204.      *
  205.      * @param other Matrix to add.
  206.      * @return a new instance with the result of the addition.
  207.      * @throws IllegalArgumentException if the dimensions do not match.
  208.      */
  209.     public FieldDenseMatrix<T> add(FieldDenseMatrix<T> other) {
  210.         checkAdd(other);
  211.         final FieldDenseMatrix<T> r = create(field, rows, columns);

  212.         for (int i = 0; i < data.length; i++) {
  213.             r.data[i] = field.add(data[i], other.data[i]);
  214.         }

  215.         return r;
  216.     }

  217.     /**
  218.      * Subtraction.
  219.      *
  220.      * @param other Matrix to subtract.
  221.      * @return a new instance with the result of the subtraction.
  222.      * @throws IllegalArgumentException if the dimensions do not match.
  223.      */
  224.     public FieldDenseMatrix<T> subtract(FieldDenseMatrix<T> other) {
  225.         checkAdd(other);
  226.         final FieldDenseMatrix<T> r = create(field, rows, columns);

  227.         for (int i = 0; i < data.length; i++) {
  228.             r.data[i] = field.subtract(data[i], other.data[i]);
  229.         }

  230.         return r;
  231.     }

  232.     /**
  233.      * Negate.
  234.      *
  235.      * @return a new instance with the opposite matrix.
  236.      */
  237.     public FieldDenseMatrix<T> negate() {
  238.         final FieldDenseMatrix<T> r = create(field, rows, columns);

  239.         for (int i = 0; i < data.length; i++) {
  240.             r.data[i] = field.negate(data[i]);
  241.         }

  242.         return r;
  243.     }

  244.     /**
  245.      * Multiplication.
  246.      *
  247.      * @param other Matrix to multiply with.
  248.      * @return a new instance with the result of the multiplication.
  249.      * @throws IllegalArgumentException if the dimensions do not match.
  250.      */
  251.     public FieldDenseMatrix<T> multiply(FieldDenseMatrix<T> other) {
  252.         checkMultiply(other);
  253.         final FieldDenseMatrix<T> r = zero(field, rows, other.columns);

  254.         for (int i = 0; i < rows; i++) {
  255.             final int o1 = i * columns;
  256.             final int o2 = i * r.columns;
  257.             for (int j = 0; j < other.columns; j++) {
  258.                 final int o3 = o2 + j;
  259.                 for (int k = 0; k < columns; k++) {
  260.                     r.data[o3] = field.add(r.data[o3],
  261.                                            field.multiply(data[o1 + k],
  262.                                                           other.data[k * other.columns + j]));
  263.                 }
  264.             }
  265.         }

  266.         return r;
  267.     }

  268.     /**
  269.      * Multiplies the matrix with itself {@code p} times.
  270.      *
  271.      * @param p Exponent.
  272.      * @return a new instance.
  273.      * @throws IllegalArgumentException if {@code p < 0}.
  274.      */
  275.     public FieldDenseMatrix<T> pow(int p) {
  276.         checkMultiply(this);

  277.         if (p < 0) {
  278.             throw new IllegalArgumentException("Negative exponent: " + p);
  279.         }

  280.         if (p == 0) {
  281.             return identity(field, rows);
  282.         }

  283.         if (p == 1) {
  284.             return copy();
  285.         }

  286.         final int power = p - 1;

  287.         // Only log_2(p) operations are necessary by doing as follows:
  288.         //    5^214 = 5^128 * 5^64 * 5^16 * 5^4 * 5^2
  289.         // The same approach is used for A^p.

  290.         final char[] binary = Integer.toBinaryString(power).toCharArray();
  291.         final ArrayList<Integer> nonZeroPositions = new ArrayList<>();

  292.         for (int i = 0; i < binary.length; i++) {
  293.             if (binary[i] == '1') {
  294.                 final int pos = binary.length - i - 1;
  295.                 nonZeroPositions.add(pos);
  296.             }
  297.         }

  298.         final List<FieldDenseMatrix<T>> results = new ArrayList<>(binary.length);
  299.         results.add(this);
  300.         for (int i = 1; i < binary.length; i++) {
  301.             final FieldDenseMatrix<T> s = results.get(i - 1);
  302.             final FieldDenseMatrix<T> r = s.multiply(s);
  303.             results.add(r);
  304.         }

  305.         FieldDenseMatrix<T> r = this;
  306.         for (Integer i : nonZeroPositions) {
  307.             r = r.multiply(results.get(i));
  308.         }

  309.         return r;
  310.     }

  311.     /** {@inheritDoc} */
  312.     @Override
  313.     public int hashCode() {
  314.         assert false : "hashCode not designed";
  315.         return 42; // Any arbitrary constant will do.
  316.     }
  317. }