View Javadoc

1   /*
2    * Copyright 2006 - 2012 Christina Bohk and Roland Ewald
3    *  
4    * Licensed under the Apache License, Version 2.0 (the "License"); 
5    * you may not use this file except in compliance with the License. 
6    * You may obtain a copy of the License at 
7    *  
8    *  http://www.apache.org/licenses/LICENSE-2.0
9    *  
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License. 
15   */
16  package p3j.misc.math;
17  
18  import james.SimSystem;
19  
20  import java.util.List;
21  import java.util.logging.Level;
22  
23  import cern.colt.matrix.impl.DenseDoubleMatrix2D;
24  
25  /**
26   * Wrapper class for matrices.
27   * 
28   * Created on July 04, 2006
29   * 
30   * @author Christina Bohk
31   * @author Roland Ewald
32   * 
33   */
34  public class Matrix2D extends DenseDoubleMatrix2D {
35  
36  	/** Serialization ID. */
37  	private static final long serialVersionUID = 8686379106612322945L;
38  
39  	/** Offset for the number of rows. */
40  	static final long OFFSET_ROW = 1000L;
41  
42  	/** Offset for the check-sum of the matrix. */
43  	static final long OFFSET_HASH = 1000000L;
44  
45  	/** Part of error messages. */
46  	static final String ERR_MSG_COL_STORE = "Matrix operation error: Can't store to column with index ";
47  
48  	/** Part of error messages. */
49  	static final String ERR_MSG_SOURCE_MATRIX = "Matrix operation error: source matrix has ";
50  
51  	/** Part of error messages. */
52  	static final String ERR_MSG_CANNOT_READ = " cannot be read.";
53  
54  	/** Error message for column errors. */
55  	static final String ERR_MSG_COL = ", so column with index ";
56  
57  	/** Label for rows. */
58  	private String rowLabel = "";
59  
60  	/** Label for column. */
61  	private String columnLabel = "";
62  
63  	/**
64  	 * Standard constructor.
65  	 * 
66  	 * @param values
67  	 *          matrix values
68  	 * @param rLabel
69  	 *          the row label
70  	 * @param cLabel
71  	 *          the column label
72  	 */
73  	public Matrix2D(double[][] values, String rLabel, String cLabel) {
74  		super(values);
75  		this.rowLabel = rLabel;
76  		this.columnLabel = cLabel;
77  	}
78  
79  	/**
80  	 * Constructor for bean compatibility.
81  	 */
82  	public Matrix2D() {
83  		super(new double[0][0]);
84  	}
85  
86  	/**
87  	 * Alternative constructor.
88  	 * 
89  	 * @param rows
90  	 *          number of rows
91  	 * @param columns
92  	 *          number of columns
93  	 */
94  	public Matrix2D(int rows, int columns) {
95  		super(rows, columns);
96  	}
97  
98  	/**
99  	 * Assigns column values.
100 	 * 
101 	 * @param index
102 	 *          index of the column to be filled
103 	 * @param values
104 	 *          values to be filled in
105 	 */
106 	public void assignColumn(int index, double[] values) {
107 
108 		if (index >= this.columns) {
109 			throw new IllegalArgumentException(ERR_MSG_COL_STORE + index
110 			    + " - matrix only has " + this.columns + " columns");
111 		}
112 
113 		if (values.length != this.rows) {
114 			throw new IllegalArgumentException("Matrix operation error: Can't store "
115 			    + values.length + " - value vector to matrix with " + this.rows
116 			    + " rows");
117 		}
118 
119 		for (int i = 0; i < this.rows; i++) {
120 			this.setQuick(i, index, values[i]);
121 		}
122 	}
123 
124 	/**
125 	 * Copies content from source column of source matrix to target column of this
126 	 * matrix.
127 	 * 
128 	 * @param indexSourceColumn
129 	 *          index of the column in the source matrix
130 	 * @param source
131 	 *          the source matrix
132 	 * @param indexTargetColumn
133 	 *          index of the column in the target matrix
134 	 */
135 	public void assignColumn(int indexSourceColumn, Matrix2D source,
136 	    int indexTargetColumn) {
137 
138 		if (source.rows != this.rows) {
139 			throw new IllegalArgumentException(ERR_MSG_SOURCE_MATRIX + source.rows
140 			    + "rows, while this matrix has " + this.rows + ".");
141 		}
142 
143 		if (indexSourceColumn >= source.columns) {
144 			throw new IllegalArgumentException(ERR_MSG_SOURCE_MATRIX + source.columns
145 			    + ERR_MSG_COL + indexSourceColumn + ERR_MSG_CANNOT_READ);
146 		}
147 
148 		if (indexTargetColumn >= this.columns) {
149 			throw new IllegalArgumentException(
150 			    "Matrix operation error: target matrix has " + this.columns
151 			        + ERR_MSG_COL + indexTargetColumn + ERR_MSG_CANNOT_READ);
152 		}
153 
154 		for (int i = 0; i < this.rows; i++) {
155 			this.setQuick(i, indexTargetColumn, source.getQuick(i, indexSourceColumn));
156 		}
157 	}
158 
159 	@Override
160 	public Matrix2D copy() {
161 		return new Matrix2D(this.getContent().toArray(), this.getRowLabel(),
162 		    this.getColumnLabel());
163 	}
164 
165 	/**
166 	 * Calculates hash code of the value {@link Matrix2D}.
167 	 * 
168 	 * @param val
169 	 *          the matrix for which the hash code shall be computed
170 	 * @return the hash code of the matrix
171 	 */
172 	public static long calculateHashCode(Matrix2D val) {
173 		// TODO: Improve hash function, e.g. via summing up certain elements.
174 		long result = 0;
175 		result *= OFFSET_HASH;
176 		result += OFFSET_ROW * val.rows() + val.columns();
177 		return result;
178 	}
179 
180 	@Override
181 	public boolean equals(Object o) {
182 		if (!(o instanceof Matrix2D)) {
183 			return false;
184 		}
185 		if (!super.equals(o)) {
186 			return false;
187 		}
188 		Matrix2D val = (Matrix2D) o;
189 		return rowLabel.equals(val.getRowLabel())
190 		    && columnLabel.equals(val.getColumnLabel());
191 	}
192 
193 	@Override
194 	public int hashCode() {
195 		return (int) calculateHashCode(this);
196 	}
197 
198 	/**
199 	 * Copies as much as possible from the source to the target.
200 	 * 
201 	 * @param source
202 	 *          the source matrix
203 	 * @param target
204 	 *          the target matrix
205 	 */
206 	public static void subMatrix(Matrix2D source, Matrix2D target) {
207 		for (int i = 0; i < Math.min(source.rows, target.rows); i++) {
208 			for (int j = 0; j < Math.min(source.columns, target.columns); j++) {
209 				target.setQuick(i, j, source.get(i, j));
210 			}
211 		}
212 	}
213 
214 	public String getColumnLabel() {
215 		return columnLabel;
216 	}
217 
218 	public void setColumnLabel(String columnLabel) {
219 		this.columnLabel = columnLabel;
220 	}
221 
222 	public String getRowLabel() {
223 		return rowLabel;
224 	}
225 
226 	public void setRowLabel(String rowLabel) {
227 		this.rowLabel = rowLabel;
228 	}
229 
230 	/**
231 	 * Adds a number of given matrices. All have to have the same dimensions
232 	 * 
233 	 * @param addList
234 	 *          the list of given matrices m_1, ..., m_n
235 	 * @return resulting matrix m_1 + ... + m_n
236 	 */
237 	public static Matrix2D add(List<Matrix2D> addList) {
238 		Matrix2D[] list = addList.toArray(new Matrix2D[0]);
239 		Matrix2D result = list[0].copy();
240 		for (int i = 0; i < result.rows(); i++) {
241 			for (int j = 0; j < result.columns(); j++) {
242 				double res = result.getQuick(i, j);
243 				for (int k = 1; k < list.length; k++) {
244 					res += list[k].getQuick(i, j);
245 				}
246 				result.setQuick(i, j, res);
247 			}
248 		}
249 		return result;
250 	}
251 
252 	/**
253 	 * Sums up all rows of a matrix column-wise.
254 	 * 
255 	 * @param mat
256 	 *          the matrix whose columns should be summed up
257 	 * @return result matrix 1 x columns
258 	 */
259 	public static Matrix2D sumRows(Matrix2D mat) {
260 		return sumRows(mat, null, null);
261 	}
262 
263 	/**
264 	 * Sums up some rows of a matrix column-wise. Set null to lower/upper border
265 	 * to set it to default value (0/rows(), respectively).
266 	 * 
267 	 * @param mat
268 	 *          the matrix whose columns should be summed up
269 	 * @param lowerBorder
270 	 *          the lower border
271 	 * @param upperBorder
272 	 *          the upper border
273 	 * 
274 	 * @return result matrix 1 x columns
275 	 */
276 	public static Matrix2D sumRows(Matrix2D mat, Integer lowerBorder,
277 	    Integer upperBorder) {
278 		Matrix2D result = new Matrix2D(1, mat.columns);
279 		int lowB = lowerBorder == null ? 0 : lowerBorder;
280 		int upB = upperBorder == null ? mat.rows : upperBorder;
281 		for (int i = 0; i < mat.columns(); i++) {
282 			double res = 0.0;
283 			for (int j = lowB; j < upB; j++) {
284 				res += mat.getQuick(j, i);
285 			}
286 			result.setQuick(0, i, res);
287 		}
288 		return result;
289 	}
290 
291 	/**
292 	 * Subtract list of matrices, element-wise.
293 	 * 
294 	 * @param subList
295 	 *          the list of matrices to be subtracted
296 	 * 
297 	 * @return the result matrix
298 	 */
299 	public Matrix2D sub(List<Matrix2D> subList) {
300 		Matrix2D result = copy();
301 		for (int i = 0; i < result.rows(); i++) {
302 			for (int j = 0; j < result.columns(); j++) {
303 				double sub = 0.0;
304 				for (Matrix2D subMat : subList) {
305 					sub += subMat.getQuick(i, j);
306 				}
307 				result.setQuick(i, j, result.getQuick(i, j) - sub);
308 			}
309 		}
310 		return result;
311 	}
312 
313 	/**
314 	 * Create a matrix with differing dimensions. Unknown values are filled with
315 	 * zeros.
316 	 * 
317 	 * @param newRows
318 	 *          the new number of rows
319 	 * @param newCols
320 	 *          the new number of columns
321 	 * 
322 	 * @return a new matrix of the appropriate sizes
323 	 */
324 	public Matrix2D getResizedMatrix(int newRows, int newCols) {
325 		if (newRows <= 0 || newCols <= 0) {
326 			SimSystem.report(Level.WARNING, "Resize to " + newRows + " x " + newCols
327 			    + " matrix not supported - matrix must not be empty.");
328 			return copy();
329 		}
330 		Matrix2D result = new Matrix2D(newRows, newCols);
331 		result.setRowLabel(rowLabel);
332 		result.setColumnLabel(columnLabel);
333 		int minRows = Math.min(rows, newRows);
334 		int minCols = Math.min(columns, newCols);
335 
336 		for (int row = 0; row < minRows; row++) {
337 			for (int col = 0; col < minCols; col++) {
338 				result.setQuick(row, col, getQuick(row, col));
339 			}
340 		}
341 		return result;
342 	}
343 
344 }