OpenMapRealMatrix.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 java.io.Serializable;

  23. import org.hipparchus.exception.LocalizedCoreFormats;
  24. import org.hipparchus.exception.MathIllegalArgumentException;
  25. import org.hipparchus.util.OpenIntToDoubleHashMap;

  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.  */
  37. public class OpenMapRealMatrix extends AbstractRealMatrix
  38.     implements SparseRealMatrix, Serializable {
  39.     /** Serializable version identifier. */
  40.     private static final long serialVersionUID = -5962461716457143437L;
  41.     /** Number of rows of the matrix. */
  42.     private final int rows;
  43.     /** Number of columns of the matrix. */
  44.     private final int columns;
  45.     /** Storage for (sparse) matrix elements. */
  46.     private final OpenIntToDoubleHashMap entries;

  47.     /**
  48.      * Build a sparse matrix with the supplied row and column dimensions.
  49.      *
  50.      * @param rowDimension Number of rows of the matrix.
  51.      * @param columnDimension Number of columns of the matrix.
  52.      * @throws MathIllegalArgumentException if row or column dimension is not
  53.      * positive.
  54.      * @throws MathIllegalArgumentException if the total number of entries of the
  55.      * matrix is larger than {@code Integer.MAX_VALUE}.
  56.      */
  57.     public OpenMapRealMatrix(int rowDimension, int columnDimension)
  58.         throws MathIllegalArgumentException {
  59.         super(rowDimension, columnDimension);
  60.         long lRow = rowDimension;
  61.         long lCol = columnDimension;
  62.         if (lRow * lCol >= Integer.MAX_VALUE) {
  63.             throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_LARGE_BOUND_EXCLUDED,
  64.                                                    lRow * lCol, Integer.MAX_VALUE);
  65.         }
  66.         this.rows = rowDimension;
  67.         this.columns = columnDimension;
  68.         this.entries = new OpenIntToDoubleHashMap(0.0);
  69.     }

  70.     /**
  71.      * Build a matrix by copying another one.
  72.      *
  73.      * @param matrix matrix to copy.
  74.      */
  75.     public OpenMapRealMatrix(OpenMapRealMatrix matrix) {
  76.         this.rows = matrix.rows;
  77.         this.columns = matrix.columns;
  78.         this.entries = new OpenIntToDoubleHashMap(matrix.entries);
  79.     }

  80.     /** {@inheritDoc} */
  81.     @Override
  82.     public OpenMapRealMatrix copy() {
  83.         return new OpenMapRealMatrix(this);
  84.     }

  85.     /**
  86.      * {@inheritDoc}
  87.      *
  88.      * @throws MathIllegalArgumentException if the total number of entries of the
  89.      * matrix is larger than {@code Integer.MAX_VALUE}.
  90.      */
  91.     @Override
  92.     public OpenMapRealMatrix createMatrix(int rowDimension, int columnDimension)
  93.         throws MathIllegalArgumentException {
  94.         return new OpenMapRealMatrix(rowDimension, columnDimension);
  95.     }

  96.     /** {@inheritDoc} */
  97.     @Override
  98.     public int getColumnDimension() {
  99.         return columns;
  100.     }

  101.     /**
  102.      * Compute the sum of this matrix and {@code m}.
  103.      *
  104.      * @param m Matrix to be added.
  105.      * @return {@code this} + {@code m}.
  106.      * @throws MathIllegalArgumentException if {@code m} is not the same
  107.      * size as {@code this}.
  108.      */
  109.     public OpenMapRealMatrix add(OpenMapRealMatrix m)
  110.         throws MathIllegalArgumentException {

  111.         MatrixUtils.checkAdditionCompatible(this, m);

  112.         final OpenMapRealMatrix out = new OpenMapRealMatrix(this);
  113.         for (OpenIntToDoubleHashMap.Iterator iterator = m.entries.iterator(); iterator.hasNext();) {
  114.             iterator.advance();
  115.             final int row = iterator.key() / columns;
  116.             final int col = iterator.key() - row * columns;
  117.             out.setEntry(row, col, getEntry(row, col) + iterator.value());
  118.         }

  119.         return out;

  120.     }

  121.     /** {@inheritDoc} */
  122.     @Override
  123.     public OpenMapRealMatrix subtract(final RealMatrix m)
  124.         throws MathIllegalArgumentException {
  125.         if (m instanceof OpenMapRealMatrix) {
  126.             return subtract((OpenMapRealMatrix) m);
  127.         } else {
  128.             return (OpenMapRealMatrix) super.subtract(m);
  129.         }
  130.     }

  131.     /**
  132.      * Subtract {@code m} from this matrix.
  133.      *
  134.      * @param m Matrix to be subtracted.
  135.      * @return {@code this} - {@code m}.
  136.      * @throws MathIllegalArgumentException if {@code m} is not the same
  137.      * size as {@code this}.
  138.      */
  139.     public OpenMapRealMatrix subtract(OpenMapRealMatrix m)
  140.         throws MathIllegalArgumentException {
  141.         MatrixUtils.checkAdditionCompatible(this, m);

  142.         final OpenMapRealMatrix out = new OpenMapRealMatrix(this);
  143.         for (OpenIntToDoubleHashMap.Iterator iterator = m.entries.iterator(); iterator.hasNext();) {
  144.             iterator.advance();
  145.             final int row = iterator.key() / columns;
  146.             final int col = iterator.key() - row * columns;
  147.             out.setEntry(row, col, getEntry(row, col) - iterator.value());
  148.         }

  149.         return out;
  150.     }

  151.     /**
  152.      * {@inheritDoc}
  153.      *
  154.      * @throws MathIllegalArgumentException if {@code m} is an
  155.      * {@code OpenMapRealMatrix}, and the total number of entries of the product
  156.      * is larger than {@code Integer.MAX_VALUE}.
  157.      */
  158.     @Override
  159.     public RealMatrix multiply(final RealMatrix m)
  160.         throws MathIllegalArgumentException {

  161.         MatrixUtils.checkMultiplicationCompatible(this, m);

  162.         final int outCols = m.getColumnDimension();
  163.         final RealMatrix out = m.createMatrix(rows, outCols);
  164.         for (OpenIntToDoubleHashMap.Iterator iterator = entries.iterator(); iterator.hasNext();) {
  165.             iterator.advance();
  166.             final double value = iterator.value();
  167.             final int key      = iterator.key();
  168.             final int i        = key / columns;
  169.             final int k        = key % columns;
  170.             for (int j = 0; j < outCols; ++j) {
  171.                 out.addToEntry(i, j, value * m.getEntry(k, j));
  172.             }
  173.         }

  174.         return out;

  175.     }

  176.     /**
  177.      * {@inheritDoc}
  178.      *
  179.      * @throws MathIllegalArgumentException if {@code m} is an
  180.      * {@code OpenMapRealMatrix}, and the total number of entries of the product
  181.      * is larger than {@code Integer.MAX_VALUE}.
  182.      */
  183.     @Override
  184.     public RealMatrix multiplyTransposed(final RealMatrix m)
  185.         throws MathIllegalArgumentException {

  186.         MatrixUtils.checkSameColumnDimension(this, m);

  187.         final int outCols = m.getRowDimension();
  188.         final RealMatrix out = m.createMatrix(rows, outCols);
  189.         for (OpenIntToDoubleHashMap.Iterator iterator = entries.iterator(); iterator.hasNext();) {
  190.             iterator.advance();
  191.             final double value = iterator.value();
  192.             final int key      = iterator.key();
  193.             final int i        = key / columns;
  194.             final int k        = key % columns;
  195.             for (int j = 0; j < outCols; ++j) {
  196.                 out.addToEntry(i, j, value * m.getEntry(j, k));
  197.             }
  198.         }

  199.         return out;

  200.     }

  201.     /**
  202.      * {@inheritDoc}
  203.      *
  204.      * @throws MathIllegalArgumentException if {@code m} is an
  205.      * {@code OpenMapRealMatrix}, and the total number of entries of the product
  206.      * is larger than {@code Integer.MAX_VALUE}.
  207.      */
  208.     @Override
  209.     public RealMatrix transposeMultiply(final RealMatrix m)
  210.         throws MathIllegalArgumentException {

  211.         MatrixUtils.checkSameRowDimension(this, m);

  212.         final int outCols = m.getColumnDimension();
  213.         final RealMatrix out = m.createMatrix(columns, outCols);
  214.         for (OpenIntToDoubleHashMap.Iterator iterator = entries.iterator(); iterator.hasNext();) {
  215.             iterator.advance();
  216.             final double value = iterator.value();
  217.             final int key      = iterator.key();
  218.             final int k        = key / columns;
  219.             final int i        = key % columns;
  220.             for (int j = 0; j < outCols; ++j) {
  221.                 out.addToEntry(i, j, value * m.getEntry(k, j));
  222.             }
  223.         }

  224.         return out;

  225.     }

  226.     /**
  227.      * Postmultiply this matrix by {@code m}.
  228.      *
  229.      * @param m Matrix to postmultiply by.
  230.      * @return {@code this} * {@code m}.
  231.      * @throws MathIllegalArgumentException if the number of rows of {@code m}
  232.      * differ from the number of columns of {@code this} matrix.
  233.      * @throws MathIllegalArgumentException if the total number of entries of the
  234.      * product is larger than {@code Integer.MAX_VALUE}.
  235.      */
  236.     public OpenMapRealMatrix multiply(OpenMapRealMatrix m)
  237.         throws MathIllegalArgumentException {
  238.         // Safety check.
  239.         MatrixUtils.checkMultiplicationCompatible(this, m);

  240.         final int outCols = m.getColumnDimension();
  241.         OpenMapRealMatrix out = new OpenMapRealMatrix(rows, outCols);
  242.         for (OpenIntToDoubleHashMap.Iterator iterator = entries.iterator(); iterator.hasNext();) {
  243.             iterator.advance();
  244.             final double value = iterator.value();
  245.             final int key      = iterator.key();
  246.             final int i        = key / columns;
  247.             final int k        = key % columns;
  248.             for (int j = 0; j < outCols; ++j) {
  249.                 final int rightKey = m.computeKey(k, j);
  250.                 if (m.entries.containsKey(rightKey)) {
  251.                     final int outKey = out.computeKey(i, j);
  252.                     final double outValue =
  253.                         out.entries.get(outKey) + value * m.entries.get(rightKey);
  254.                     if (outValue == 0.0) {
  255.                         out.entries.remove(outKey);
  256.                     } else {
  257.                         out.entries.put(outKey, outValue);
  258.                     }
  259.                 }
  260.             }
  261.         }

  262.         return out;
  263.     }

  264.     /** {@inheritDoc} */
  265.     @Override
  266.     public double getEntry(int row, int column) throws MathIllegalArgumentException {
  267.         MatrixUtils.checkRowIndex(this, row);
  268.         MatrixUtils.checkColumnIndex(this, column);
  269.         return entries.get(computeKey(row, column));
  270.     }

  271.     /** {@inheritDoc} */
  272.     @Override
  273.     public int getRowDimension() {
  274.         return rows;
  275.     }

  276.     /** {@inheritDoc} */
  277.     @Override
  278.     public void setEntry(int row, int column, double value)
  279.         throws MathIllegalArgumentException {
  280.         MatrixUtils.checkRowIndex(this, row);
  281.         MatrixUtils.checkColumnIndex(this, column);
  282.         if (value == 0.0) {
  283.             entries.remove(computeKey(row, column));
  284.         } else {
  285.             entries.put(computeKey(row, column), value);
  286.         }
  287.     }

  288.     /** {@inheritDoc} */
  289.     @Override
  290.     public void addToEntry(int row, int column, double increment)
  291.         throws MathIllegalArgumentException {
  292.         MatrixUtils.checkRowIndex(this, row);
  293.         MatrixUtils.checkColumnIndex(this, column);
  294.         final int key = computeKey(row, column);
  295.         final double value = entries.get(key) + increment;
  296.         if (value == 0.0) {
  297.             entries.remove(key);
  298.         } else {
  299.             entries.put(key, value);
  300.         }
  301.     }

  302.     /** {@inheritDoc} */
  303.     @Override
  304.     public void multiplyEntry(int row, int column, double factor)
  305.         throws MathIllegalArgumentException {
  306.         MatrixUtils.checkRowIndex(this, row);
  307.         MatrixUtils.checkColumnIndex(this, column);
  308.         final int key = computeKey(row, column);
  309.         final double value = entries.get(key) * factor;
  310.         if (value == 0.0) {
  311.             entries.remove(key);
  312.         } else {
  313.             entries.put(key, value);
  314.         }
  315.     }

  316.     /**
  317.      * Compute the key to access a matrix element
  318.      * @param row row index of the matrix element
  319.      * @param column column index of the matrix element
  320.      * @return key within the map to access the matrix element
  321.      */
  322.     private int computeKey(int row, int column) {
  323.         return row * columns + column;
  324.     }


  325. }