SparseFieldMatrix.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.  *      https://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. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.linear;

  22. import org.hipparchus.Field;
  23. import org.hipparchus.FieldElement;
  24. import org.hipparchus.exception.MathIllegalArgumentException;
  25. import org.hipparchus.util.OpenIntToFieldHashMap;

  26. /**
  27.  * Sparse matrix implementation based on an open addressed map.
  28.  *
  29.  * <p>
  30.  *  Caveat: This implementation assumes that, for any {@code x},
  31.  *  the equality {@code x * 0d == 0d} holds. But it is is not true for
  32.  *  {@code NaN}. Moreover, zero entries will lose their sign.
  33.  *  Some operations (that involve {@code NaN} and/or infinities) may
  34.  *  thus give incorrect results.
  35.  * </p>
  36.  * @param <T> the type of the field elements
  37.  */
  38. public class SparseFieldMatrix<T extends FieldElement<T>> extends AbstractFieldMatrix<T> {

  39.     /** Storage for (sparse) matrix elements. */
  40.     private final OpenIntToFieldHashMap<T> entries;
  41.     /** Row dimension. */
  42.     private final int rows;
  43.     /** Column dimension. */
  44.     private final int columns;

  45.     /**
  46.      * Create a matrix with no data.
  47.      *
  48.      * @param field Field to which the elements belong.
  49.      */
  50.     public SparseFieldMatrix(final Field<T> field) {
  51.         super(field);
  52.         rows = 0;
  53.         columns= 0;
  54.         entries = new OpenIntToFieldHashMap<>(field);
  55.     }

  56.     /**
  57.      * Create a new {@link SparseFieldMatrix} with the supplied row and column
  58.      * dimensions.
  59.      *
  60.      * @param field Field to which the elements belong.
  61.      * @param rowDimension Number of rows in the new matrix.
  62.      * @param columnDimension Number of columns in the new matrix.
  63.      * @throws org.hipparchus.exception.MathIllegalArgumentException
  64.      * if row or column dimension is not positive.
  65.      */
  66.     public SparseFieldMatrix(final Field<T> field,
  67.                              final int rowDimension, final int columnDimension) {
  68.         super(field, rowDimension, columnDimension);
  69.         this.rows = rowDimension;
  70.         this.columns = columnDimension;
  71.         entries = new OpenIntToFieldHashMap<>(field);
  72.     }

  73.     /**
  74.      * Copy constructor.
  75.      *
  76.      * @param other Instance to copy.
  77.      */
  78.     public SparseFieldMatrix(SparseFieldMatrix<T> other) {
  79.         super(other.getField(), other.getRowDimension(), other.getColumnDimension());
  80.         rows = other.getRowDimension();
  81.         columns = other.getColumnDimension();
  82.         entries = new OpenIntToFieldHashMap<>(other.entries);
  83.     }

  84.     /**
  85.      * Generic copy constructor.
  86.      *
  87.      * @param other Instance to copy.
  88.      */
  89.     public SparseFieldMatrix(FieldMatrix<T> other){
  90.         super(other.getField(), other.getRowDimension(), other.getColumnDimension());
  91.         rows = other.getRowDimension();
  92.         columns = other.getColumnDimension();
  93.         entries = new OpenIntToFieldHashMap<>(getField());
  94.         for (int i = 0; i < rows; i++) {
  95.             for (int j = 0; j < columns; j++) {
  96.                 setEntry(i, j, other.getEntry(i, j));
  97.             }
  98.         }
  99.     }

  100.     /** {@inheritDoc} */
  101.     @Override
  102.     public void addToEntry(int row, int column, T increment) {
  103.         checkRowIndex(row);
  104.         checkColumnIndex(column);
  105.         final int key = computeKey(row, column);
  106.         final T value = entries.get(key).add(increment);
  107.         if (getField().getZero().equals(value)) {
  108.             entries.remove(key);
  109.         } else {
  110.             entries.put(key, value);
  111.         }
  112.     }

  113.     /** {@inheritDoc} */
  114.     @Override
  115.     public FieldMatrix<T> copy() {
  116.         return new SparseFieldMatrix<>(this);
  117.     }

  118.     /** {@inheritDoc} */
  119.     @Override
  120.     public FieldMatrix<T> createMatrix(int rowDimension, int columnDimension) {
  121.         return new SparseFieldMatrix<>(getField(), rowDimension, columnDimension);
  122.     }

  123.     /** {@inheritDoc} */
  124.     @Override
  125.     public int getColumnDimension() {
  126.         return columns;
  127.     }

  128.     /** {@inheritDoc} */
  129.     @Override
  130.     public T getEntry(int row, int column) {
  131.         checkRowIndex(row);
  132.         checkColumnIndex(column);
  133.         return entries.get(computeKey(row, column));
  134.     }

  135.     /** {@inheritDoc} */
  136.     @Override
  137.     public int getRowDimension() {
  138.         return rows;
  139.     }

  140.     /** {@inheritDoc} */
  141.     @Override
  142.     public void multiplyEntry(int row, int column, T factor) {
  143.         checkRowIndex(row);
  144.         checkColumnIndex(column);
  145.         final int key = computeKey(row, column);
  146.         final T value = entries.get(key).multiply(factor);
  147.         if (getField().getZero().equals(value)) {
  148.             entries.remove(key);
  149.         } else {
  150.             entries.put(key, value);
  151.         }

  152.     }

  153.     /** {@inheritDoc} */
  154.     @Override
  155.     public void setEntry(int row, int column, T value) {
  156.         checkRowIndex(row);
  157.         checkColumnIndex(column);
  158.         if (getField().getZero().equals(value)) {
  159.             entries.remove(computeKey(row, column));
  160.         } else {
  161.             entries.put(computeKey(row, column), value);
  162.         }
  163.     }

  164.     /**
  165.      * {@inheritDoc}
  166.      *
  167.      * @throws MathIllegalArgumentException if {@code m} is an
  168.      * {@code OpenMapRealMatrix}, and the total number of entries of the product
  169.      * is larger than {@code Integer.MAX_VALUE}.
  170.      */
  171.     @Override
  172.     public FieldMatrix<T> multiplyTransposed(final FieldMatrix<T> m)
  173.         throws MathIllegalArgumentException {

  174.         MatrixUtils.checkSameColumnDimension(this, m);

  175.         final int outCols = m.getRowDimension();
  176.         final FieldMatrix<T> out = m.createMatrix(rows, outCols);
  177.         for (OpenIntToFieldHashMap<T>.Iterator iterator = entries.iterator(); iterator.hasNext();) {
  178.             iterator.advance();
  179.             final T   value    = iterator.value();
  180.             final int key      = iterator.key();
  181.             final int i        = key / columns;
  182.             final int k        = key % columns;
  183.             for (int j = 0; j < outCols; ++j) {
  184.                 out.addToEntry(i, j, value.multiply(m.getEntry(j, k)));
  185.             }
  186.         }

  187.         return out;

  188.     }

  189.     /**
  190.      * {@inheritDoc}
  191.      *
  192.      * @throws MathIllegalArgumentException if {@code m} is an
  193.      * {@code OpenMapRealMatrix}, and the total number of entries of the product
  194.      * is larger than {@code Integer.MAX_VALUE}.
  195.      */
  196.     @Override
  197.     public FieldMatrix<T> transposeMultiply(final FieldMatrix<T> m)
  198.         throws MathIllegalArgumentException {

  199.         MatrixUtils.checkSameRowDimension(this, m);

  200.         final int outCols = m.getColumnDimension();
  201.         final FieldMatrix<T> out = m.createMatrix(columns, outCols);
  202.         for (OpenIntToFieldHashMap<T>.Iterator iterator = entries.iterator(); iterator.hasNext();) {
  203.             iterator.advance();
  204.             final T   value = iterator.value();
  205.             final int key   = iterator.key();
  206.             final int k     = key / columns;
  207.             final int i     = key % columns;
  208.             for (int j = 0; j < outCols; ++j) {
  209.                 out.addToEntry(i, j, value.multiply(m.getEntry(k, j)));
  210.             }
  211.         }

  212.         return out;

  213.     }

  214.     /**
  215.      * Compute the key to access a matrix element.
  216.      *
  217.      * @param row Row index of the matrix element.
  218.      * @param column Column index of the matrix element.
  219.      * @return the key within the map to access the matrix element.
  220.      */
  221.     private int computeKey(int row, int column) {
  222.         return row * columns + column;
  223.     }
  224. }