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    /** The step-size of the function to compute a hash value for a matrix. */
37    private static final int MATRIX_HASH_STEPSIZE = 5;
38  
39    /** Serialization ID. */
40    private static final long serialVersionUID = 8686379106612322945L;
41  
42    /** Offset for the number of rows. */
43    static final long OFFSET_ROW = 1000L;
44  
45    /** Offset for the check-sum of the matrix. */
46    static final long OFFSET_HASH = 1000000L;
47  
48    /** Part of error messages. */
49    static final String ERR_MSG_COL_STORE = "Matrix operation error: Can't store to column with index ";
50  
51    /** Part of error messages. */
52    static final String ERR_MSG_SOURCE_MATRIX = "Matrix operation error: source matrix has ";
53  
54    /** Part of error messages. */
55    static final String ERR_MSG_CANNOT_READ = " cannot be read.";
56  
57    /** Error message for column errors. */
58    static final String ERR_MSG_COL = ", so column with index ";
59  
60    /** Label for rows. */
61    private String rowLabel = "";
62  
63    /** Label for column. */
64    private String columnLabel = "";
65  
66    /**
67     * Standard constructor.
68     * 
69     * @param values
70     *          matrix values
71     * @param rLabel
72     *          the row label
73     * @param cLabel
74     *          the column label
75     */
76    public Matrix2D(double[][] values, String rLabel, String cLabel) {
77      super(values);
78      this.rowLabel = rLabel;
79      this.columnLabel = cLabel;
80    }
81  
82    /**
83     * Constructor for bean compatibility.
84     */
85    public Matrix2D() {
86      super(new double[0][0]);
87    }
88  
89    /**
90     * Alternative constructor.
91     * 
92     * @param rows
93     *          number of rows
94     * @param columns
95     *          number of columns
96     */
97    public Matrix2D(int rows, int columns) {
98      super(rows, columns);
99    }
100 
101   /**
102    * Assigns column values.
103    * 
104    * @param index
105    *          index of the column to be filled
106    * @param values
107    *          values to be filled in
108    */
109   public void assignColumn(int index, double[] values) {
110 
111     if (index >= this.columns) {
112       throw new IllegalArgumentException(ERR_MSG_COL_STORE + index
113           + " - matrix only has " + this.columns + " columns");
114     }
115 
116     if (values.length != this.rows) {
117       throw new IllegalArgumentException("Matrix operation error: Can't store "
118           + values.length + " - value vector to matrix with " + this.rows
119           + " rows");
120     }
121 
122     for (int i = 0; i < this.rows; i++) {
123       this.setQuick(i, index, values[i]);
124     }
125   }
126 
127   /**
128    * Copies content from source column of source matrix to target column of this
129    * matrix.
130    * 
131    * @param indexSourceColumn
132    *          index of the column in the source matrix
133    * @param source
134    *          the source matrix
135    * @param indexTargetColumn
136    *          index of the column in the target matrix
137    */
138   public void assignColumn(int indexSourceColumn, Matrix2D source,
139       int indexTargetColumn) {
140 
141     if (source.rows != this.rows) {
142       throw new IllegalArgumentException(ERR_MSG_SOURCE_MATRIX + source.rows
143           + "rows, while this matrix has " + this.rows + ".");
144     }
145 
146     if (indexSourceColumn >= source.columns) {
147       throw new IllegalArgumentException(ERR_MSG_SOURCE_MATRIX + source.columns
148           + ERR_MSG_COL + indexSourceColumn + ERR_MSG_CANNOT_READ);
149     }
150 
151     if (indexTargetColumn >= this.columns) {
152       throw new IllegalArgumentException(
153           "Matrix operation error: target matrix has " + this.columns
154               + ERR_MSG_COL + indexTargetColumn + ERR_MSG_CANNOT_READ);
155     }
156 
157     for (int i = 0; i < this.rows; i++) {
158       this.setQuick(i, indexTargetColumn, source.getQuick(i, indexSourceColumn));
159     }
160   }
161 
162   @Override
163   public Matrix2D copy() {
164     return new Matrix2D(this.getContent().toArray(), this.getRowLabel(),
165         this.getColumnLabel());
166   }
167 
168   /**
169    * Calculates hash code of the value {@link Matrix2D}.
170    * 
171    * @param val
172    *          the matrix for which the hash code shall be computed
173    * @return the hash code of the matrix
174    */
175   public static long calculateHashCode(Matrix2D val) {
176     long result = 0;
177     result += OFFSET_ROW * val.rows() + val.columns();
178     for (int x = 1; x < val.rows(); x += MATRIX_HASH_STEPSIZE)
179       for (int y = 1; y < val.columns(); y += MATRIX_HASH_STEPSIZE)
180         result += (Double.doubleToLongBits(val.getQuick(x, y)) ^ Double
181             .doubleToLongBits(val.getQuick(x - 1, y - 1)));
182     return result;
183   }
184 
185   @Override
186   public boolean equals(Object o) {
187     if (!(o instanceof Matrix2D)) {
188       return false;
189     }
190 
191     Matrix2D val = (Matrix2D) o;
192     if (!rowLabel.equals(val.getRowLabel())
193         && columnLabel.equals(val.getColumnLabel())) {
194       return false;
195     }
196 
197     return super.equals(o);
198   }
199 
200   @Override
201   public int hashCode() {
202     return (int) calculateHashCode(this);
203   }
204 
205   /**
206    * Copies as much as possible from the source to the target.
207    * 
208    * @param source
209    *          the source matrix
210    * @param target
211    *          the target matrix
212    */
213   public static void subMatrix(Matrix2D source, Matrix2D target) {
214     for (int i = 0; i < Math.min(source.rows, target.rows); i++) {
215       for (int j = 0; j < Math.min(source.columns, target.columns); j++) {
216         target.setQuick(i, j, source.get(i, j));
217       }
218     }
219   }
220 
221   public String getColumnLabel() {
222     return columnLabel;
223   }
224 
225   public void setColumnLabel(String columnLabel) {
226     this.columnLabel = columnLabel;
227   }
228 
229   public String getRowLabel() {
230     return rowLabel;
231   }
232 
233   public void setRowLabel(String rowLabel) {
234     this.rowLabel = rowLabel;
235   }
236 
237   /**
238    * Adds a number of given matrices. All have to have the same dimensions
239    * 
240    * @param addList
241    *          the list of given matrices m_1, ..., m_n
242    * @return resulting matrix m_1 + ... + m_n
243    */
244   public static Matrix2D add(List<Matrix2D> addList) {
245     Matrix2D[] list = addList.toArray(new Matrix2D[0]);
246     Matrix2D result = list[0].copy();
247     for (int i = 0; i < result.rows(); i++) {
248       for (int j = 0; j < result.columns(); j++) {
249         double res = result.getQuick(i, j);
250         for (int k = 1; k < list.length; k++) {
251           res += list[k].getQuick(i, j);
252         }
253         result.setQuick(i, j, res);
254       }
255     }
256     return result;
257   }
258 
259   /**
260    * Sums up all rows of a matrix column-wise.
261    * 
262    * @param mat
263    *          the matrix whose columns should be summed up
264    * @return result matrix 1 x columns
265    */
266   public static Matrix2D sumRows(Matrix2D mat) {
267     return sumRows(mat, null, null);
268   }
269 
270   /**
271    * Sums up some rows of a matrix column-wise. Set null to lower/upper border
272    * to set it to default value (0/rows(), respectively).
273    * 
274    * @param mat
275    *          the matrix whose columns should be summed up
276    * @param lowerBorder
277    *          the lower border
278    * @param upperBorder
279    *          the upper border
280    * 
281    * @return result matrix 1 x columns
282    */
283   public static Matrix2D sumRows(Matrix2D mat, Integer lowerBorder,
284       Integer upperBorder) {
285     Matrix2D result = new Matrix2D(1, mat.columns);
286     int lowB = lowerBorder == null ? 0 : lowerBorder;
287     int upB = upperBorder == null ? mat.rows : upperBorder;
288     for (int i = 0; i < mat.columns(); i++) {
289       double res = 0.0;
290       for (int j = lowB; j < upB; j++) {
291         res += mat.getQuick(j, i);
292       }
293       result.setQuick(0, i, res);
294     }
295     return result;
296   }
297 
298   /**
299    * Subtract list of matrices, element-wise.
300    * 
301    * @param subList
302    *          the list of matrices to be subtracted
303    * 
304    * @return the result matrix
305    */
306   public Matrix2D sub(List<Matrix2D> subList) {
307     Matrix2D result = copy();
308     for (int i = 0; i < result.rows(); i++) {
309       for (int j = 0; j < result.columns(); j++) {
310         double sub = 0.0;
311         for (Matrix2D subMat : subList) {
312           sub += subMat.getQuick(i, j);
313         }
314         result.setQuick(i, j, result.getQuick(i, j) - sub);
315       }
316     }
317     return result;
318   }
319 
320   /**
321    * Create a matrix with differing dimensions. Unknown values are filled with
322    * zeros.
323    * 
324    * @param newRows
325    *          the new number of rows
326    * @param newCols
327    *          the new number of columns
328    * 
329    * @return a new matrix of the appropriate sizes
330    */
331   public Matrix2D getResizedMatrix(int newRows, int newCols) {
332     if (newRows <= 0 || newCols <= 0) {
333       SimSystem.report(Level.WARNING, "Resize to " + newRows + " x " + newCols
334           + " matrix not supported - matrix must not be empty.");
335       return copy();
336     }
337     Matrix2D result = new Matrix2D(newRows, newCols);
338     result.setRowLabel(rowLabel);
339     result.setColumnLabel(columnLabel);
340     int minRows = Math.min(rows, newRows);
341     int minCols = Math.min(columns, newCols);
342 
343     for (int row = 0; row < minRows; row++) {
344       for (int col = 0; col < minCols; col++) {
345         result.setQuick(row, col, getQuick(row, col));
346       }
347     }
348     return result;
349   }
350 
351 }