Home | History | Annotate | Download | only in linear
      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 
     18 package org.apache.commons.math.linear;
     19 
     20 import java.io.Serializable;
     21 
     22 import org.apache.commons.math.util.OpenIntToDoubleHashMap;
     23 
     24 /**
     25  * Sparse matrix implementation based on an open addressed map.
     26  *
     27  * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $
     28  * @since 2.0
     29  */
     30 public class OpenMapRealMatrix extends AbstractRealMatrix implements SparseRealMatrix, Serializable {
     31 
     32     /** Serializable version identifier. */
     33     private static final long serialVersionUID = -5962461716457143437L;
     34 
     35     /** Number of rows of the matrix. */
     36     private final int rows;
     37 
     38     /** Number of columns of the matrix. */
     39     private final int columns;
     40 
     41     /** Storage for (sparse) matrix elements. */
     42     private final OpenIntToDoubleHashMap entries;
     43 
     44     /**
     45      * Build a sparse matrix with the supplied row and column dimensions.
     46      * @param rowDimension number of rows of the matrix
     47      * @param columnDimension number of columns of the matrix
     48      */
     49     public OpenMapRealMatrix(int rowDimension, int columnDimension) {
     50         super(rowDimension, columnDimension);
     51         this.rows    = rowDimension;
     52         this.columns = columnDimension;
     53         this.entries = new OpenIntToDoubleHashMap(0.0);
     54     }
     55 
     56     /**
     57      * Build a matrix by copying another one.
     58      * @param matrix matrix to copy
     59      */
     60     public OpenMapRealMatrix(OpenMapRealMatrix matrix) {
     61         this.rows    = matrix.rows;
     62         this.columns = matrix.columns;
     63         this.entries = new OpenIntToDoubleHashMap(matrix.entries);
     64     }
     65 
     66     /** {@inheritDoc} */
     67     @Override
     68     public OpenMapRealMatrix copy() {
     69         return new OpenMapRealMatrix(this);
     70     }
     71 
     72     /** {@inheritDoc} */
     73     @Override
     74     public OpenMapRealMatrix createMatrix(int rowDimension, int columnDimension)
     75             throws IllegalArgumentException {
     76         return new OpenMapRealMatrix(rowDimension, columnDimension);
     77     }
     78 
     79     /** {@inheritDoc} */
     80     @Override
     81     public int getColumnDimension() {
     82         return columns;
     83     }
     84 
     85     /** {@inheritDoc} */
     86     @Override
     87     public OpenMapRealMatrix add(final RealMatrix m)
     88         throws IllegalArgumentException {
     89         try {
     90             return add((OpenMapRealMatrix) m);
     91         } catch (ClassCastException cce) {
     92             return (OpenMapRealMatrix) super.add(m);
     93         }
     94     }
     95 
     96     /**
     97      * Compute the sum of this and <code>m</code>.
     98      *
     99      * @param m    matrix to be added
    100      * @return     this + m
    101      * @throws  IllegalArgumentException if m is not the same size as this
    102      */
    103     public OpenMapRealMatrix add(OpenMapRealMatrix m) throws IllegalArgumentException {
    104 
    105         // safety check
    106         MatrixUtils.checkAdditionCompatible(this, m);
    107 
    108         final OpenMapRealMatrix out = new OpenMapRealMatrix(this);
    109         for (OpenIntToDoubleHashMap.Iterator iterator = m.entries.iterator(); iterator.hasNext();) {
    110             iterator.advance();
    111             final int row = iterator.key() / columns;
    112             final int col = iterator.key() - row * columns;
    113             out.setEntry(row, col, getEntry(row, col) + iterator.value());
    114         }
    115 
    116         return out;
    117 
    118     }
    119 
    120     /** {@inheritDoc} */
    121     @Override
    122     public OpenMapRealMatrix subtract(final RealMatrix m)
    123         throws IllegalArgumentException {
    124         try {
    125             return subtract((OpenMapRealMatrix) m);
    126         } catch (ClassCastException cce) {
    127             return (OpenMapRealMatrix) super.subtract(m);
    128         }
    129     }
    130 
    131     /**
    132      * Compute this minus <code>m</code>.
    133      *
    134      * @param m    matrix to be subtracted
    135      * @return     this - m
    136      * @throws  IllegalArgumentException if m is not the same size as this
    137      */
    138     public OpenMapRealMatrix subtract(OpenMapRealMatrix m) throws IllegalArgumentException {
    139 
    140         // safety check
    141         MatrixUtils.checkAdditionCompatible(this, m);
    142 
    143         final OpenMapRealMatrix out = new OpenMapRealMatrix(this);
    144         for (OpenIntToDoubleHashMap.Iterator iterator = m.entries.iterator(); iterator.hasNext();) {
    145             iterator.advance();
    146             final int row = iterator.key() / columns;
    147             final int col = iterator.key() - row * columns;
    148             out.setEntry(row, col, getEntry(row, col) - iterator.value());
    149         }
    150 
    151         return out;
    152 
    153     }
    154 
    155     /** {@inheritDoc} */
    156     @Override
    157     public RealMatrix multiply(final RealMatrix m)
    158         throws IllegalArgumentException {
    159         try {
    160             return multiply((OpenMapRealMatrix) m);
    161         } catch (ClassCastException cce) {
    162 
    163             // safety check
    164             MatrixUtils.checkMultiplicationCompatible(this, m);
    165 
    166             final int outCols = m.getColumnDimension();
    167             final BlockRealMatrix out = new BlockRealMatrix(rows, outCols);
    168             for (OpenIntToDoubleHashMap.Iterator iterator = entries.iterator(); iterator.hasNext();) {
    169                 iterator.advance();
    170                 final double value = iterator.value();
    171                 final int key      = iterator.key();
    172                 final int i        = key / columns;
    173                 final int k        = key % columns;
    174                 for (int j = 0; j < outCols; ++j) {
    175                     out.addToEntry(i, j, value * m.getEntry(k, j));
    176                 }
    177             }
    178 
    179             return out;
    180 
    181         }
    182     }
    183 
    184     /**
    185      * Returns the result of postmultiplying this by m.
    186      *
    187      * @param m    matrix to postmultiply by
    188      * @return     this * m
    189      * @throws     IllegalArgumentException
    190      *             if columnDimension(this) != rowDimension(m)
    191      */
    192     public OpenMapRealMatrix multiply(OpenMapRealMatrix m) throws IllegalArgumentException {
    193 
    194         // safety check
    195         MatrixUtils.checkMultiplicationCompatible(this, m);
    196 
    197         final int outCols = m.getColumnDimension();
    198         OpenMapRealMatrix out = new OpenMapRealMatrix(rows, outCols);
    199         for (OpenIntToDoubleHashMap.Iterator iterator = entries.iterator(); iterator.hasNext();) {
    200             iterator.advance();
    201             final double value = iterator.value();
    202             final int key      = iterator.key();
    203             final int i        = key / columns;
    204             final int k        = key % columns;
    205             for (int j = 0; j < outCols; ++j) {
    206                 final int rightKey = m.computeKey(k, j);
    207                 if (m.entries.containsKey(rightKey)) {
    208                     final int outKey = out.computeKey(i, j);
    209                     final double outValue =
    210                         out.entries.get(outKey) + value * m.entries.get(rightKey);
    211                     if (outValue == 0.0) {
    212                         out.entries.remove(outKey);
    213                     } else {
    214                         out.entries.put(outKey, outValue);
    215                     }
    216                 }
    217             }
    218         }
    219 
    220         return out;
    221 
    222     }
    223 
    224     /** {@inheritDoc} */
    225     @Override
    226     public double getEntry(int row, int column) throws MatrixIndexException {
    227         MatrixUtils.checkRowIndex(this, row);
    228         MatrixUtils.checkColumnIndex(this, column);
    229         return entries.get(computeKey(row, column));
    230     }
    231 
    232     /** {@inheritDoc} */
    233     @Override
    234     public int getRowDimension() {
    235         return rows;
    236     }
    237 
    238     /** {@inheritDoc} */
    239     @Override
    240     public void setEntry(int row, int column, double value)
    241             throws MatrixIndexException {
    242         MatrixUtils.checkRowIndex(this, row);
    243         MatrixUtils.checkColumnIndex(this, column);
    244         if (value == 0.0) {
    245             entries.remove(computeKey(row, column));
    246         } else {
    247             entries.put(computeKey(row, column), value);
    248         }
    249     }
    250 
    251     /** {@inheritDoc} */
    252     @Override
    253     public void addToEntry(int row, int column, double increment)
    254             throws MatrixIndexException {
    255         MatrixUtils.checkRowIndex(this, row);
    256         MatrixUtils.checkColumnIndex(this, column);
    257         final int key = computeKey(row, column);
    258         final double value = entries.get(key) + increment;
    259         if (value == 0.0) {
    260             entries.remove(key);
    261         } else {
    262             entries.put(key, value);
    263         }
    264     }
    265 
    266     /** {@inheritDoc} */
    267     @Override
    268     public void multiplyEntry(int row, int column, double factor)
    269             throws MatrixIndexException {
    270         MatrixUtils.checkRowIndex(this, row);
    271         MatrixUtils.checkColumnIndex(this, column);
    272         final int key = computeKey(row, column);
    273         final double value = entries.get(key) * factor;
    274         if (value == 0.0) {
    275             entries.remove(key);
    276         } else {
    277             entries.put(key, value);
    278         }
    279     }
    280 
    281     /**
    282      * Compute the key to access a matrix element
    283      * @param row row index of the matrix element
    284      * @param column column index of the matrix element
    285      * @return key within the map to access the matrix element
    286      */
    287     private int computeKey(int row, int column) {
    288         return row * columns + column;
    289     }
    290 
    291 
    292 }
    293