1  #region License Information


2  /* HeuristicLab


3  * Copyright (C) 20022019 Heuristic and Evolutionary Algorithms Laboratory (HEAL)


4  *


5  * This file is part of HeuristicLab.


6  *


7  * HeuristicLab is free software: you can redistribute it and/or modify


8  * it under the terms of the GNU General Public License as published by


9  * the Free Software Foundation, either version 3 of the License, or


10  * (at your option) any later version.


11  *


12  * HeuristicLab is distributed in the hope that it will be useful,


13  * but WITHOUT ANY WARRANTY; without even the implied warranty of


14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the


15  * GNU General Public License for more details.


16  *


17  * You should have received a copy of the GNU General Public License


18  * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.


19  */


20  #endregion


21 


22  using System;


23  using System.Collections;


24  using System.Collections.Generic;


25  using System.Linq;


26  using HeuristicLab.Common;


27  using HeuristicLab.PluginInfrastructure;


28 


29  namespace HeuristicLab.Algorithms.DataAnalysis {


30  [NonDiscoverableType]


31  internal class Matrix : IEnumerable<double>, IDeepCloneable {


32  // this type is immutable


33  private readonly IEnumerable<double> values;


34  public readonly int Rows;


35  public readonly int Columns;


36 


37  protected Matrix(Matrix original, Cloner cloner) {


38  this.values = original.values.ToArray();


39  this.Rows = original.Rows;


40  this.Columns = original.Columns;


41  cloner.RegisterClonedObject(original, this);


42  }


43  public Matrix(IEnumerable<double> vector) {


44  this.values = vector;


45  Rows = 1;


46  Columns = vector.Count();


47  }


48  public Matrix(IEnumerable<double> vector, int length) {


49  this.values = vector;


50  Rows = 1;


51  Columns = length;


52  }


53  public Matrix(double[,] matrix) {


54  this.values = GetOnlineValues(matrix);


55  Rows = matrix.GetLength(0);


56  Columns = matrix.GetLength(1);


57  }


58  public Matrix(IEnumerable<double> matrix, int rows, int columns) {


59  this.values = matrix;


60  Rows = rows;


61  Columns = columns;


62  }


63 


64  public object Clone() {


65  return Clone(new Cloner());


66  }


67  public IDeepCloneable Clone(Cloner cloner) {


68  return new Matrix(this, cloner);


69  }


70 


71  public Matrix Transpose() {


72  return new Matrix(Transpose(values, Columns, Rows), Columns, Rows);


73  }


74 


75  private IEnumerable<double> Transpose(IEnumerable<double> values, int rows, int columns) {


76  // vectors don't need to be transposed


77  if (rows == 1  columns == 1) {


78  foreach (var v in values) yield return v;


79  yield break;


80  }


81 


82  int skip = 0;


83  var iter = values.GetEnumerator();


84  if (!iter.MoveNext()) yield break;


85  while (skip < rows) {


86  for (int i = 0; i < skip; i++) iter.MoveNext();


87  yield return iter.Current;


88  for (int j = 0; j < columns  1; j++) {


89  for (int i = 0; i < rows; i++) iter.MoveNext();


90  yield return iter.Current;


91  }


92  skip++;


93  if (skip < rows) {


94  iter = values.GetEnumerator();


95  iter.MoveNext();


96  }


97  }


98  }


99 


100  public Matrix Add(Matrix other) {


101  return new Matrix(AddOnline(other), Rows, Columns);


102  }


103 


104  public void AddTo(double[,] matrix) {


105  if (Rows != matrix.GetLength(0)  Columns != matrix.GetLength(1)) throw new ArgumentException("unequal size", "matrix");


106  var iter = values.GetEnumerator();


107  for (int i = 0; i < Rows; i++)


108  for (int j = 0; j < Columns; j++) {


109  iter.MoveNext();


110  matrix[i, j] += iter.Current;


111  }


112  }


113 


114  public Matrix Subtract(Matrix other) {


115  return new Matrix(SubtractOnline(other), Rows, Columns);


116  }


117 


118  public Matrix Multiply(Matrix other) {


119  return new Matrix(MultiplyOnline(other), Rows, other.Columns);


120  }


121 


122  public Matrix Multiply(double value) {


123  return new Matrix(values.Select(x => x * value), Rows, Columns);


124  }


125 


126  public double EuclideanNorm() {


127  return Math.Sqrt(SumOfSquares());


128  }


129 


130  public double SumOfSquares() {


131  return values.Sum(x => x * x);


132  }


133 


134  public Matrix OuterProduct(Matrix other) {


135  if (Rows != 1  other.Rows != 1) throw new ArgumentException("OuterProduct can only be applied to vectors.");


136  return Transpose().Multiply(other);


137  }


138 


139  public IEnumerable<double> ColumnSums() {


140  return Transpose().RowSums();


141  }


142 


143  public IEnumerable<double> RowSums() {


144  var sum = 0.0;


145  int counter = 0;


146  foreach (var v in values) {


147  sum += v;


148  counter++;


149  if (counter == Rows) {


150  yield return sum;


151  sum = 0.0;


152  counter = 0;


153  }


154  }


155  }


156 


157  public Matrix Negate() {


158  return new Matrix(values.Select(x => x), Rows, Columns);


159  }


160 


161  public Matrix Apply() {


162  return new Matrix(values.ToArray(), Rows, Columns);


163  }


164 


165  public IEnumerator<double> GetEnumerator() { return values.GetEnumerator(); }


166  IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }


167 


168 


169  private IEnumerable<double> AddOnline(Matrix other) {


170  if (Rows != other.Rows  Columns != other.Columns) throw new ArgumentException("Number of rows and columns are not equal.");


171  var meIter = values.GetEnumerator();


172  var otherIter = other.GetEnumerator();


173  if (!meIter.MoveNext()) yield break;


174  if (!otherIter.MoveNext()) yield break;


175  for (int i = 0; i < Rows * Columns; i++) {


176  yield return meIter.Current + otherIter.Current;


177  meIter.MoveNext();


178  otherIter.MoveNext();


179  }


180  }


181 


182  private IEnumerable<double> SubtractOnline(Matrix other) {


183  if (Rows != other.Rows  Columns != other.Columns) throw new ArgumentException("Number of rows and columns are not equal.");


184  var meIter = values.GetEnumerator();


185  var otherIter = other.GetEnumerator();


186  if (!meIter.MoveNext()) yield break;


187  if (!otherIter.MoveNext()) yield break;


188  for (int i = 0; i < Rows * Columns; i++) {


189  yield return meIter.Current  otherIter.Current;


190  meIter.MoveNext();


191  otherIter.MoveNext();


192  }


193  }


194 


195  private IEnumerable<double> MultiplyOnline(Matrix other) {


196  if (Columns != other.Rows) throw new ArgumentException("Number of rows and columns are not equal.");


197  var meIter = values.GetEnumerator();


198  var otherByColumn = other.Transpose();


199  var otherIter = otherByColumn.GetEnumerator();


200  if (!meIter.MoveNext()) yield break;


201  if (!otherIter.MoveNext()) yield break;


202  for (int r = 0; r < Rows; r++) {


203  var row = new double[Columns];


204  for (int x = 0; x < Columns; x++) {


205  row[x] = meIter.Current;


206  meIter.MoveNext();


207  }


208  for (int c = 0; c < other.Columns; c++) {


209  var sum = 0.0;


210  for (int y = 0; y < other.Rows; y++) {


211  sum += row[y] * otherIter.Current;


212  otherIter.MoveNext();


213  }


214  yield return sum;


215  }


216  otherIter = otherByColumn.GetEnumerator();


217  otherIter.MoveNext();


218  }


219  }


220 


221  private IEnumerable<double> GetOnlineValues(double[,] matrix) {


222  for (int i = 0; i < matrix.GetLength(0); i++)


223  for (int j = 0; j < matrix.GetLength(1); j++) {


224  yield return matrix[i, j];


225  }


226  }


227  }


228  }

