View Javadoc
1   package org.djunits.value.vdouble.matrix.base;
2   
3   import java.lang.reflect.Array;
4   
5   import org.djunits.unit.Unit;
6   import org.djunits.value.Absolute;
7   import org.djunits.value.AbstractIndexedValue;
8   import org.djunits.value.ValueRuntimeException;
9   import org.djunits.value.formatter.Format;
10  import org.djunits.value.storage.StorageType;
11  import org.djunits.value.util.ValueUtil;
12  import org.djunits.value.vdouble.function.DoubleFunction;
13  import org.djunits.value.vdouble.function.DoubleMathFunctions;
14  import org.djunits.value.vdouble.matrix.data.DoubleMatrixData;
15  import org.djunits.value.vdouble.scalar.base.AbstractDoubleScalar;
16  import org.djunits.value.vdouble.scalar.base.DoubleScalar;
17  import org.djunits.value.vdouble.vector.base.AbstractDoubleVector;
18  import org.djunits.value.vdouble.vector.data.DoubleVectorData;
19  import org.djutils.exceptions.Throw;
20  
21  /**
22   * The most basic abstract class for the DoubleMatrix.
23   * <p>
24   * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
25   * BSD-style license. See <a href="https://djunits.org/docs/license.html">DJUNITS License</a>.
26   * </p>
27   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
28   * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
29   * @param <U> the unit
30   * @param <S> the scalar with unit U
31   * @param <V> the vector type belonging to the matrix type
32   * @param <M> the generic matrix type
33   */
34  public abstract class AbstractDoubleMatrix<U extends Unit<U>, S extends AbstractDoubleScalar<U, S>,
35          V extends AbstractDoubleVector<U, S, V>, M extends AbstractDoubleMatrix<U, S, V, M>>
36          extends AbstractIndexedValue<U, S, M, DoubleMatrixData> implements DoubleMatrixInterface<U, S, V, M>
37  {
38      /** */
39      private static final long serialVersionUID = 20161015L;
40  
41      /** The stored data as an object, can be sparse or dense. */
42      @SuppressWarnings("checkstyle:visibilitymodifier")
43      protected DoubleMatrixData data;
44  
45      /**
46       * Construct a new DoubleMatrix.
47       * @param data DoubleMatrixData; an internal data object
48       * @param unit U; the unit
49       */
50      AbstractDoubleMatrix(final DoubleMatrixData data, final U unit)
51      {
52          super(unit);
53          Throw.whenNull(data, "data cannot be null");
54          this.data = data;
55      }
56  
57      /** {@inheritDoc} */
58      @Override
59      protected final DoubleMatrixData getData()
60      {
61          return this.data;
62      }
63  
64      /** {@inheritDoc} */
65      @Override
66      protected void setData(final DoubleMatrixData data)
67      {
68          this.data = data;
69      }
70  
71      /** {@inheritDoc} */
72      @Override
73      public double getSI(final int row, final int column) throws ValueRuntimeException
74      {
75          checkIndex(row, column);
76          return this.data.getSI(row, column);
77      }
78  
79      /** {@inheritDoc} */
80      @Override
81      public double getInUnit(final int row, final int column) throws ValueRuntimeException
82      {
83          checkIndex(row, column);
84          return ValueUtil.expressAsUnit(this.data.getSI(row, column), getDisplayUnit());
85      }
86  
87      /** {@inheritDoc} */
88      @Override
89      public double getInUnit(final int row, final int column, final U targetUnit) throws ValueRuntimeException
90      {
91          checkIndex(row, column);
92          return ValueUtil.expressAsUnit(this.data.getSI(row, column), targetUnit);
93      }
94  
95      /** {@inheritDoc} */
96      @Override
97      public void setSI(final int row, final int column, final double valueSI) throws ValueRuntimeException
98      {
99          checkIndex(row, column);
100         checkCopyOnWrite();
101         this.data.setSI(row, column, valueSI);
102     }
103 
104     /** {@inheritDoc} */
105     @Override
106     public void setInUnit(final int row, final int column, final double valueInUnit) throws ValueRuntimeException
107     {
108         setSI(row, column, ValueUtil.expressAsSIUnit(valueInUnit, getDisplayUnit()));
109     }
110 
111     /** {@inheritDoc} */
112     @Override
113     public void setInUnit(final int row, final int column, final double valueInUnit, final U valueUnit)
114             throws ValueRuntimeException
115     {
116         setSI(row, column, ValueUtil.expressAsSIUnit(valueInUnit, valueUnit));
117     }
118 
119     /** {@inheritDoc} */
120     @Override
121     public void set(final int row, final int column, final S value) throws ValueRuntimeException
122     {
123         setSI(row, column, value.si);
124     }
125 
126     /** {@inheritDoc} */
127     @Override
128     public double[] getRowSI(final int row) throws ValueRuntimeException
129     {
130         checkRowIndex(row);
131         double[] result = new double[this.data.cols()];
132         for (int col = 0; col < result.length; col++)
133         {
134             result[col] = this.data.getSI(row, col);
135         }
136         return result;
137     }
138 
139     /** {@inheritDoc} */
140     @Override
141     public double[] getColumnSI(final int column) throws ValueRuntimeException
142     {
143         checkColumnIndex(column);
144         double[] result = new double[this.data.rows()];
145         for (int row = 0; row < result.length; row++)
146         {
147             result[row] = this.data.getSI(row, column);
148         }
149         return result;
150     }
151 
152     /** {@inheritDoc} */
153     @Override
154     public double[] getDiagonalSI() throws ValueRuntimeException
155     {
156         checkSquare();
157         double[] result = new double[this.data.rows()];
158         for (int row = 0; row < result.length; row++)
159         {
160             result[row] = this.data.getSI(row, row);
161         }
162         return result;
163     }
164 
165     /** {@inheritDoc} */
166     @Override
167     public final double[][] getValuesSI()
168     {
169         return this.data.getDenseMatrixSI();
170     }
171 
172     /** {@inheritDoc} */
173     @Override
174     public final double[][] getValuesInUnit()
175     {
176         return getValuesInUnit(getDisplayUnit());
177     }
178 
179     /** {@inheritDoc} */
180     @Override
181     public final double[][] getValuesInUnit(final U targetUnit)
182     {
183         double[][] values = getValuesSI();
184         for (int i = values.length; --i >= 0;)
185         {
186             for (int j = values[i].length; --j >= 0;)
187             {
188                 values[i][j] = ValueUtil.expressAsUnit(values[i][j], targetUnit);
189             }
190         }
191         return values;
192     }
193 
194     /** {@inheritDoc} */
195     @Override
196     public int rows()
197     {
198         return this.data.rows();
199     }
200 
201     /** {@inheritDoc} */
202     @Override
203     public int cols()
204     {
205         return this.data.cols();
206     }
207 
208     /** {@inheritDoc} */
209     @SuppressWarnings("unchecked")
210     @Override
211     public S[][] getScalars()
212     {
213         S[][] array = (S[][]) Array.newInstance(getScalarClass(), rows(), cols());
214         for (int i = 0; i < rows(); i++)
215         {
216             S[] row = (S[]) Array.newInstance(getScalarClass(), cols());
217             array[i] = row;
218             for (int j = 0; j < cols(); j++)
219             {
220                 row[j] = get(i, j);
221             }
222         }
223         return array;
224     }
225 
226     /** {@inheritDoc} */
227     @Override
228     public S get(final int row, final int column) throws ValueRuntimeException
229     {
230         checkIndex(row, column);
231         return DoubleScalar.instantiateSI(getSI(row, column), getDisplayUnit());
232     }
233 
234     /** {@inheritDoc} */
235     @Override
236     public V getRow(final int row) throws ValueRuntimeException
237     {
238         checkRowIndex(row);
239         DoubleVectorData dvd =
240                 DoubleVectorData.instantiate(getRowSI(row), getDisplayUnit().getStandardUnit().getScale(), getStorageType());
241         return instantiateVector(dvd, getDisplayUnit());
242     }
243 
244     /** {@inheritDoc} */
245     @Override
246     public V getColumn(final int column) throws ValueRuntimeException
247     {
248         checkColumnIndex(column);
249         DoubleVectorData dvd = DoubleVectorData.instantiate(getColumnSI(column), getDisplayUnit().getStandardUnit().getScale(),
250                 getStorageType());
251         return instantiateVector(dvd, getDisplayUnit());
252     }
253 
254     /** {@inheritDoc} */
255     @Override
256     public V getDiagonal() throws ValueRuntimeException
257     {
258         checkSquare();
259         DoubleVectorData dvd =
260                 DoubleVectorData.instantiate(getDiagonalSI(), getDisplayUnit().getStandardUnit().getScale(), getStorageType());
261         return instantiateVector(dvd, getDisplayUnit());
262     }
263 
264     /** {@inheritDoc} */
265     @SuppressWarnings("unchecked")
266     @Override
267     public S[] getRowScalars(final int row) throws ValueRuntimeException
268     {
269         checkRowIndex(row);
270         S[] array = (S[]) Array.newInstance(getScalarClass(), cols());
271         for (int col = 0; col < cols(); col++)
272         {
273             array[col] = get(row, col);
274         }
275         return array;
276     }
277 
278     /** {@inheritDoc} */
279     @SuppressWarnings("unchecked")
280     @Override
281     public S[] getColumnScalars(final int col) throws ValueRuntimeException
282     {
283         checkColumnIndex(col);
284         S[] array = (S[]) Array.newInstance(getScalarClass(), rows());
285         for (int row = 0; row < rows(); row++)
286         {
287             array[row] = get(row, col);
288         }
289         return array;
290     }
291 
292     /** {@inheritDoc} */
293     @SuppressWarnings("unchecked")
294     @Override
295     public S[] getDiagonalScalars() throws ValueRuntimeException
296     {
297         checkSquare();
298         S[] array = (S[]) Array.newInstance(getScalarClass(), rows());
299         for (int row = 0; row < rows(); row++)
300         {
301             array[row] = get(row, row);
302         }
303         return array;
304     }
305 
306     /** {@inheritDoc} */
307     @SuppressWarnings("unchecked")
308     @Override
309     public M toSparse()
310     {
311         M result;
312         if (getStorageType().equals(StorageType.SPARSE))
313         {
314             result = (M) this;
315             result.setDisplayUnit(getDisplayUnit());
316         }
317         else
318         {
319             result = instantiateMatrix(this.data.toSparse(), getDisplayUnit());
320         }
321         result.setDisplayUnit(getDisplayUnit());
322         return result;
323     }
324 
325     /** {@inheritDoc} */
326     @SuppressWarnings("unchecked")
327     @Override
328     public M toDense()
329     {
330         M result;
331         if (getStorageType().equals(StorageType.DENSE))
332         {
333             result = (M) this;
334             result.setDisplayUnit(getDisplayUnit());
335         }
336         else
337         {
338             result = instantiateMatrix(this.data.toDense(), getDisplayUnit());
339         }
340         return result;
341     }
342 
343     /** {@inheritDoc} */
344     @SuppressWarnings("unchecked")
345     @Override
346     public final M assign(final DoubleFunction doubleFunction)
347     {
348         checkCopyOnWrite();
349         this.data.assign(doubleFunction);
350         return (M) this;
351     }
352 
353     /** {@inheritDoc} */
354     @Override
355     public final M abs()
356     {
357         return assign(DoubleMathFunctions.ABS);
358     }
359 
360     /** {@inheritDoc} */
361     @Override
362     public final M ceil()
363     {
364         return assign(DoubleMathFunctions.CEIL);
365     }
366 
367     /** {@inheritDoc} */
368     @Override
369     public final M floor()
370     {
371         return assign(DoubleMathFunctions.FLOOR);
372     }
373 
374     /** {@inheritDoc} */
375     @Override
376     public final M neg()
377     {
378         return assign(DoubleMathFunctions.NEG);
379     }
380 
381     /** {@inheritDoc} */
382     @Override
383     public final M rint()
384     {
385         return assign(DoubleMathFunctions.RINT);
386     }
387 
388     /** {@inheritDoc} */
389     @Override
390     public String toString()
391     {
392         return toString(getDisplayUnit(), false, true);
393     }
394 
395     /** {@inheritDoc} */
396     @Override
397     public String toString(final U displayUnit)
398     {
399         return toString(displayUnit, false, true);
400     }
401 
402     /** {@inheritDoc} */
403     @Override
404     public String toString(final boolean verbose, final boolean withUnit)
405     {
406         return toString(getDisplayUnit(), verbose, withUnit);
407     }
408 
409     /** {@inheritDoc} */
410     @Override
411     public String toString(final U displayUnit, final boolean verbose, final boolean withUnit)
412     {
413         StringBuffer buf = new StringBuffer();
414         if (verbose)
415         {
416             String ab = this instanceof Absolute ? "Abs " : "Rel ";
417             String ds = this.data.isDense() ? "Dense  " : this.data.isSparse() ? "Sparse " : "?????? ";
418             if (isMutable())
419             {
420                 buf.append("Mutable   " + ab + ds);
421             }
422             else
423             {
424                 buf.append("Immutable " + ab + ds);
425             }
426         }
427         for (int row = 0; row < rows(); row++)
428         {
429             buf.append("\r\n\t");
430             for (int col = 0; col < cols(); col++)
431             {
432                 try
433                 {
434                     double d = ValueUtil.expressAsUnit(getSI(row, col), displayUnit);
435                     buf.append(" " + Format.format(d));
436                 }
437                 catch (ValueRuntimeException ve)
438                 {
439                     buf.append(" " + "********************".substring(0, Format.DEFAULTSIZE));
440                 }
441             }
442         }
443         buf.append("\n");
444         if (withUnit)
445         {
446             buf.append(displayUnit.getLocalizedDisplayAbbreviation());
447         }
448         return buf.toString();
449     }
450 
451     /**
452      * Check that provided row and column indices are valid.
453      * @param row int; the row value to check
454      * @param col int; the column value to check
455      * @throws ValueRuntimeException when row or column is invalid
456      */
457     protected final void checkIndex(final int row, final int col) throws ValueRuntimeException
458     {
459         if (row < 0 || row >= rows() || col < 0 || col >= cols())
460         {
461             throw new ValueRuntimeException("index out of range (valid range is 0.." + (rows() - 1) + ", 0.." + (cols() - 1)
462                     + ", got " + row + ", " + col + ")");
463         }
464     }
465 
466     /**
467      * Check that provided row index is valid.
468      * @param row int; the row value to check
469      * @throws ValueRuntimeException when row is invalid
470      */
471     protected final void checkRowIndex(final int row) throws ValueRuntimeException
472     {
473         if (row < 0 || row >= rows())
474         {
475             throw new ValueRuntimeException("row index out of range (valid range is 0.." + (rows() - 1) + ", got " + row + ")");
476         }
477     }
478 
479     /**
480      * Check that provided column index is valid.
481      * @param col int; the column value to check
482      * @throws ValueRuntimeException when row is invalid
483      */
484     protected final void checkColumnIndex(final int col) throws ValueRuntimeException
485     {
486         if (col < 0 || col >= cols())
487         {
488             throw new ValueRuntimeException(
489                     "column index out of range (valid range is 0.." + (cols() - 1) + ", got " + col + ")");
490         }
491     }
492 
493     /**
494      * Check that the matrix is square.
495      * @throws ValueRuntimeException when matrix is not square
496      */
497     protected final void checkSquare() throws ValueRuntimeException
498     {
499         Throw.when(rows() != cols(), ValueRuntimeException.class, "Matrix is not square, rows=%d, cols=%d", rows(), cols());
500     }
501 
502     /** {@inheritDoc} */
503     @Override
504     public final double determinantSI() throws ValueRuntimeException
505     {
506         checkSquare();
507         return det(getValuesSI());
508     }
509 
510     /**
511      * Calculate the determinant of an n x n matrix.
512      * @param mat the matrix
513      * @return the determinant using the co-factor formula
514      */
515     private static double det(final double[][] mat)
516     {
517         if (mat.length == 1)
518         {
519             return mat[0][0];
520         }
521         // det(A) = sum(j=1:n) (-1)^(i+j).a_ij.A_ij where A_ij is the matrix with row i and column j removed
522         double det = 0.0;
523         // possible optimization: pick the row or column with most zeros; here: pick row 0
524         for (int col = 0; col < mat.length; col++)
525         {
526             double sgn = (col % 2 == 0) ? 1 : -1;
527             double aij = mat[0][col];
528             double[][] matAij = new double[mat.length - 1][];
529             int r = 0;
530             for (int row = 1; row < mat.length; row++)
531             {
532                 matAij[r] = new double[matAij.length];
533                 int c = 0;
534                 for (int j = 0; j < mat.length; j++)
535                 {
536                     if (j != col)
537                     {
538                         matAij[r][c++] = mat[row][j];
539                     }
540                 }
541                 r++;
542             }
543             det += sgn * aij * det(matAij);
544         }
545         return det;
546     }
547 
548     /** {@inheritDoc} */
549     @Override
550     @SuppressWarnings("checkstyle:designforextension")
551     public int hashCode()
552     {
553         final int prime = 31;
554         int result = getDisplayUnit().getStandardUnit().hashCode();
555         result = prime * result + ((this.data == null) ? 0 : this.data.hashCode());
556         return result;
557     }
558 
559     /** {@inheritDoc} */
560     @Override
561     @SuppressWarnings({"checkstyle:designforextension", "checkstyle:needbraces"})
562     public boolean equals(final Object obj)
563     {
564         if (this == obj)
565             return true;
566         if (obj == null)
567             return false;
568         if (getClass() != obj.getClass())
569             return false;
570         AbstractDoubleMatrix<?, ?, ?, ?> other = (AbstractDoubleMatrix<?, ?, ?, ?>) obj;
571         if (!getDisplayUnit().getStandardUnit().equals(other.getDisplayUnit().getStandardUnit()))
572             return false;
573         if (this.data == null)
574         {
575             if (other.data != null)
576                 return false;
577         }
578         else if (!this.data.equals(other.data))
579             return false;
580         return true;
581     }
582 }