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 13:36:48 -0400 (Sat, 05 Sep 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 }