001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.commons.math.linear; 019 020 import java.io.Serializable; 021 022 import org.apache.commons.math.util.OpenIntToDoubleHashMap; 023 024 /** 025 * Sparse matrix implementation based on an open addressed map. 026 * 027 * @version $Revision: 811685 $ $Date: 2009-09-05 19:36:48 +0200 (sam. 05 sept. 2009) $ 028 * @since 2.0 029 */ 030 public class OpenMapRealMatrix extends AbstractRealMatrix implements SparseRealMatrix, Serializable { 031 032 /** Serializable version identifier. */ 033 private static final long serialVersionUID = -5962461716457143437L; 034 035 /** Number of rows of the matrix. */ 036 private final int rows; 037 038 /** Number of columns of the matrix. */ 039 private final int columns; 040 041 /** Storage for (sparse) matrix elements. */ 042 private final OpenIntToDoubleHashMap entries; 043 044 /** 045 * Build a sparse matrix with the supplied row and column dimensions. 046 * @param rowDimension number of rows of the matrix 047 * @param columnDimension number of columns of the matrix 048 */ 049 public OpenMapRealMatrix(int rowDimension, int columnDimension) { 050 super(rowDimension, columnDimension); 051 this.rows = rowDimension; 052 this.columns = columnDimension; 053 this.entries = new OpenIntToDoubleHashMap(0.0); 054 } 055 056 /** 057 * Build a matrix by copying another one. 058 * @param matrix matrix to copy 059 */ 060 public OpenMapRealMatrix(OpenMapRealMatrix matrix) { 061 this.rows = matrix.rows; 062 this.columns = matrix.columns; 063 this.entries = new OpenIntToDoubleHashMap(matrix.entries); 064 } 065 066 /** {@inheritDoc} */ 067 @Override 068 public OpenMapRealMatrix copy() { 069 return new OpenMapRealMatrix(this); 070 } 071 072 /** {@inheritDoc} */ 073 @Override 074 public OpenMapRealMatrix createMatrix(int rowDimension, int columnDimension) 075 throws IllegalArgumentException { 076 return new OpenMapRealMatrix(rowDimension, columnDimension); 077 } 078 079 /** {@inheritDoc} */ 080 @Override 081 public int getColumnDimension() { 082 return columns; 083 } 084 085 /** {@inheritDoc} */ 086 @Override 087 public OpenMapRealMatrix add(final RealMatrix m) 088 throws IllegalArgumentException { 089 try { 090 return add((OpenMapRealMatrix) m); 091 } catch (ClassCastException cce) { 092 return (OpenMapRealMatrix) super.add(m); 093 } 094 } 095 096 /** 097 * Compute the sum of this and <code>m</code>. 098 * 099 * @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 }