Browse Source

LA: MatrixStorage.Find/Find2; leverage Find2 in Equals #291

cuda
Christoph Ruegg 12 years ago
parent
commit
70dda56f85
  1. 35
      src/Numerics/Compatibility.cs
  2. 107
      src/Numerics/LinearAlgebra/Storage/DenseColumnMajorMatrixStorage.cs
  3. 2
      src/Numerics/LinearAlgebra/Storage/DenseVectorStorage.cs
  4. 171
      src/Numerics/LinearAlgebra/Storage/DiagonalMatrixStorage.cs
  5. 70
      src/Numerics/LinearAlgebra/Storage/MatrixStorage.cs
  6. 237
      src/Numerics/LinearAlgebra/Storage/SparseCompressedRowMatrixStorage.cs
  7. 2
      src/Numerics/LinearAlgebra/Storage/SparseVectorStorage.cs
  8. 2
      src/Numerics/LinearAlgebra/Storage/VectorStorage.cs
  9. 33
      src/UnitTests/LinearAlgebraTests/MatrixStructureTheory.cs

35
src/Numerics/Compatibility.cs

@ -233,6 +233,41 @@ namespace MathNet.Numerics
}
}
public class Tuple<T1, T2, T3, T4> : IComparable, IComparable<Tuple<T1, T2, T3, T4>>
{
public T1 Item1 { get; set; }
public T2 Item2 { get; set; }
public T3 Item3 { get; set; }
public T4 Item4 { get; set; }
public Tuple(T1 item1, T2 item2, T3 item3, T4 item4)
{
Item1 = item1;
Item2 = item2;
Item3 = item3;
Item4 = item4;
}
public int CompareTo(object obj)
{
if (obj == null) return 1;
var other = obj as Tuple<T1, T2, T3, T4>;
if (other == null) throw new ArgumentException();
return CompareTo(other);
}
public int CompareTo(Tuple<T1, T2, T3, T4> other)
{
if (other == null) return 1;
int a = ObjectComparer.Compare(Item1, other.Item1);
if (a != 0) return a;
int b = ObjectComparer.Compare(Item2, other.Item2);
if (b != 0) return b;
int c = ObjectComparer.Compare(Item3, other.Item3);
return c != 0 ? c : ObjectComparer.Compare(Item4, other.Item4);
}
}
public static class EnumerableExtensions
{
public static IEnumerable<T> Zip<TA, TB, T>(this IEnumerable<TA> seqA, IEnumerable<TB> seqB, Func<TA, TB, T> func)

107
src/Numerics/LinearAlgebra/Storage/DenseColumnMajorMatrixStorage.cs

@ -4,7 +4,7 @@
// http://github.com/mathnet/mathnet-numerics
// http://mathnetnumerics.codeplex.com
//
// Copyright (c) 2009-2014 Math.NET
// Copyright (c) 2009-2015 Math.NET
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
@ -108,6 +108,19 @@ namespace MathNet.Numerics.LinearAlgebra.Storage
Data[(column*RowCount) + row] = value;
}
/// <summary>
/// Evaluate the row and column at a specific data index.
/// </summary>
void RowColumnAtIndex(int index, out int row, out int column)
{
#if PORTABLE
row = index % RowCount;
column = index / RowCount;
#else
column = Math.DivRem(index, RowCount, out row);
#endif
}
// CLEARING
public override void Clear()
@ -647,6 +660,98 @@ namespace MathNet.Numerics.LinearAlgebra.Storage
}
}
// FIND
public override Tuple<int, int, T> Find(Func<T, bool> predicate, Zeros zeros)
{
for (int i = 0; i < Data.Length; i++)
{
if (predicate(Data[i]))
{
int row, column;
RowColumnAtIndex(i, out row, out column);
return new Tuple<int, int, T>(row, column, Data[i]);
}
}
return null;
}
internal override Tuple<int, int, T, TOther> Find2Unchecked<TOther>(MatrixStorage<TOther> other, Func<T, TOther, bool> predicate, Zeros zeros)
{
var denseOther = other as DenseColumnMajorMatrixStorage<TOther>;
if (denseOther != null)
{
TOther[] otherData = denseOther.Data;
for (int i = 0; i < Data.Length; i++)
{
if (predicate(Data[i], otherData[i]))
{
int row, column;
RowColumnAtIndex(i, out row, out column);
return new Tuple<int, int, T, TOther>(row, column, Data[i], otherData[i]);
}
}
return null;
}
var diagonalOther = other as DiagonalMatrixStorage<TOther>;
if (diagonalOther != null)
{
TOther[] otherData = diagonalOther.Data;
TOther otherZero = BuilderInstance<TOther>.Matrix.Zero;
int k = 0;
for (int j = 0; j < ColumnCount; j++)
{
for (int i = 0; i < RowCount; i++)
{
if (predicate(Data[k], i == j ? otherData[i] : otherZero))
{
return new Tuple<int, int, T, TOther>(i, j, Data[k], i == j ? otherData[i] : otherZero);
}
k++;
}
}
return null;
}
var sparseOther = other as SparseCompressedRowMatrixStorage<TOther>;
if (sparseOther != null)
{
int[] otherRowPointers = sparseOther.RowPointers;
int[] otherColumnIndices = sparseOther.ColumnIndices;
TOther[] otherValues = sparseOther.Values;
TOther otherZero = BuilderInstance<TOther>.Matrix.Zero;
int k = 0;
for (int row = 0; row < RowCount; row++)
{
for (int col = 0; col < ColumnCount; col++)
{
if (k < otherRowPointers[row + 1] && otherColumnIndices[k] == col)
{
if (predicate(Data[col*RowCount + row], otherValues[k]))
{
return new Tuple<int, int, T, TOther>(row, col, Data[col*RowCount + row], otherValues[k]);
}
k++;
}
else
{
if (predicate(Data[col*RowCount + row], otherZero))
{
return new Tuple<int, int, T, TOther>(row, col, Data[col*RowCount + row], otherValues[k]);
}
}
}
}
return null;
}
// FALL BACK
return base.Find2Unchecked(other, predicate, zeros);
}
// FUNCTIONAL COMBINATORS: MAP
public override void MapInplace(Func<T, T> f, Zeros zeros = Zeros.AllowSkip)

2
src/Numerics/LinearAlgebra/Storage/DenseVectorStorage.cs

@ -4,7 +4,7 @@
// http://github.com/mathnet/mathnet-numerics
// http://mathnetnumerics.codeplex.com
//
// Copyright (c) 2009-2013 Math.NET
// Copyright (c) 2009-2015 Math.NET
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation

171
src/Numerics/LinearAlgebra/Storage/DiagonalMatrixStorage.cs

@ -4,7 +4,7 @@
// http://github.com/mathnet/mathnet-numerics
// http://mathnetnumerics.codeplex.com
//
// Copyright (c) 2009-2014 Math.NET
// Copyright (c) 2009-2015 Math.NET
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
@ -115,44 +115,6 @@ namespace MathNet.Numerics.LinearAlgebra.Storage
}
}
/// <summary>
/// Indicates whether the current object is equal to another object of the same type.
/// </summary>
/// <param name="other">
/// An object to compare with this object.
/// </param>
/// <returns>
/// <c>true</c> if the current object is equal to the <paramref name="other"/> parameter; otherwise, <c>false</c>.
/// </returns>
public override bool Equals(MatrixStorage<T> other)
{
var diagonal = other as DiagonalMatrixStorage<T>;
if (diagonal == null)
{
return base.Equals(other);
}
// Reject equality when the argument is null or has a different shape.
if (ColumnCount != other.ColumnCount || RowCount != other.RowCount)
{
return false;
}
// Accept if the argument is the same object as this.
if (ReferenceEquals(this, other))
{
return true;
}
if (diagonal.Data.Length != Data.Length)
{
return false;
}
// If all else fails, perform element wise comparison.
return !Data.Where((t, i) => !t.Equals(diagonal.Data[i])).Any();
}
/// <summary>
/// Returns a hash code for this instance.
/// </summary>
@ -635,6 +597,137 @@ namespace MathNet.Numerics.LinearAlgebra.Storage
}
}
// FIND
public override Tuple<int, int, T> Find(Func<T, bool> predicate, Zeros zeros)
{
for (int i = 0; i < Data.Length; i++)
{
if (predicate(Data[i]))
{
return new Tuple<int, int, T>(i, i, Data[i]);
}
}
if (zeros == Zeros.Include && (RowCount > 1 || ColumnCount > 1))
{
if (predicate(Zero))
{
return new Tuple<int, int, T>(RowCount > 1 ? 1 : 0, RowCount > 1 ? 0 : 1, Zero);
}
}
return null;
}
internal override Tuple<int, int, T, TOther> Find2Unchecked<TOther>(MatrixStorage<TOther> other, Func<T, TOther, bool> predicate, Zeros zeros)
{
var denseOther = other as DenseColumnMajorMatrixStorage<TOther>;
if (denseOther != null)
{
TOther[] otherData = denseOther.Data;
int k = 0;
for (int j = 0; j < ColumnCount; j++)
{
for (int i = 0; i < RowCount; i++)
{
if (predicate(i == j ? Data[i] : Zero, otherData[k]))
{
return new Tuple<int, int, T, TOther>(i, j, i == j ? Data[i] : Zero, otherData[k]);
}
k++;
}
}
return null;
}
var diagonalOther = other as DiagonalMatrixStorage<TOther>;
if (diagonalOther != null)
{
TOther[] otherData = diagonalOther.Data;
for (int i = 0; i < Data.Length; i++)
{
if (predicate(Data[i], otherData[i]))
{
return new Tuple<int, int, T, TOther>(i, i, Data[i], otherData[i]);
}
}
if (zeros == Zeros.Include && (RowCount > 1 || ColumnCount > 1))
{
TOther otherZero = BuilderInstance<TOther>.Matrix.Zero;
if (predicate(Zero, otherZero))
{
return new Tuple<int, int, T, TOther>(RowCount > 1 ? 1 : 0, RowCount > 1 ? 0 : 1, Zero, otherZero);
}
}
return null;
}
var sparseOther = other as SparseCompressedRowMatrixStorage<TOther>;
if (sparseOther != null)
{
int[] otherRowPointers = sparseOther.RowPointers;
int[] otherColumnIndices = sparseOther.ColumnIndices;
TOther[] otherValues = sparseOther.Values;
TOther otherZero = BuilderInstance<TOther>.Matrix.Zero;
for (int row = 0; row < RowCount; row++)
{
bool diagonal = false;
var startIndex = otherRowPointers[row];
var endIndex = otherRowPointers[row + 1];
for (var j = startIndex; j < endIndex; j++)
{
if (otherColumnIndices[j] == row)
{
diagonal = true;
if (predicate(Data[row], otherValues[j]))
{
return new Tuple<int, int, T, TOther>(row, row, Data[row], otherValues[j]);
}
}
else
{
if (predicate(Zero, otherValues[j]))
{
return new Tuple<int, int, T, TOther>(row, otherColumnIndices[j], Zero, otherValues[j]);
}
}
}
if (!diagonal && row < ColumnCount)
{
if (predicate(Data[row], otherZero))
{
return new Tuple<int, int, T, TOther>(row, row, Data[row], otherZero);
}
}
}
if (zeros == Zeros.Include && sparseOther.ValueCount < (RowCount * ColumnCount))
{
if (predicate(Zero, otherZero))
{
int k = 0;
for (int row = 0; row < RowCount; row++)
{
for (int col = 0; col < ColumnCount; col++)
{
if (k < otherRowPointers[row + 1] && otherColumnIndices[k] == col)
{
k++;
}
else if (row != col)
{
return new Tuple<int, int, T, TOther>(row, col, Zero, otherZero);
}
}
}
}
}
return null;
}
// FALL BACK
return base.Find2Unchecked(other, predicate, zeros);
}
// FUNCTIONAL COMBINATORS: MAP
public override void MapInplace(Func<T, T> f, Zeros zeros = Zeros.AllowSkip)

70
src/Numerics/LinearAlgebra/Storage/MatrixStorage.cs

@ -137,7 +137,7 @@ namespace MathNet.Numerics.LinearAlgebra.Storage
/// <returns>
/// <c>true</c> if the current object is equal to the <paramref name="other"/> parameter; otherwise, <c>false</c>.
/// </returns>
public virtual bool Equals(MatrixStorage<T> other)
public bool Equals(MatrixStorage<T> other)
{
// Reject equality when the argument is null or has a different shape.
if (other == null)
@ -155,19 +155,8 @@ namespace MathNet.Numerics.LinearAlgebra.Storage
return true;
}
// If all else fails, perform element wise comparison.
for (var row = 0; row < RowCount; row++)
{
for (var column = 0; column < ColumnCount; column++)
{
if (!At(row, column).Equals(other.At(row, column)))
{
return false;
}
}
}
return true;
// Perform element wise comparison.
return Find2Unchecked(other, (a, b) => !a.Equals(b), Zeros.AllowSkip) == null;
}
/// <summary>
@ -627,6 +616,59 @@ namespace MathNet.Numerics.LinearAlgebra.Storage
}
}
// FIND
public virtual Tuple<int, int, T> Find(Func<T, bool> predicate, Zeros zeros)
{
for (int i = 0; i < RowCount; i++)
{
for (int j = 0; j < ColumnCount; j++)
{
var item = At(i, j);
if (predicate(item))
{
return new Tuple<int, int, T>(i, j, item);
}
}
}
return null;
}
public Tuple<int, int, T, TOther> Find2<TOther>(MatrixStorage<TOther> other, Func<T, TOther, bool> predicate, Zeros zeros)
where TOther : struct, IEquatable<TOther>, IFormattable
{
if (other == null)
{
throw new ArgumentNullException("other");
}
if (RowCount != other.RowCount || ColumnCount != other.ColumnCount)
{
var message = string.Format(Resources.ArgumentMatrixDimensions2, RowCount + "x" + ColumnCount, other.RowCount + "x" + other.ColumnCount);
throw new ArgumentException(message, "other");
}
return Find2Unchecked(other, predicate, zeros);
}
internal virtual Tuple<int, int, T, TOther> Find2Unchecked<TOther>(MatrixStorage<TOther> other, Func<T, TOther, bool> predicate, Zeros zeros)
where TOther : struct, IEquatable<TOther>, IFormattable
{
for (int i = 0; i < RowCount; i++)
{
for (int j = 0; j < ColumnCount; j++)
{
var item = At(i, j);
var otherItem = other.At(i, j);
if (predicate(item, otherItem))
{
return new Tuple<int, int, T, TOther>(i, j, item, otherItem);
}
}
}
return null;
}
// FUNCTIONAL COMBINATORS: MAP
public virtual void MapInplace(Func<T, T> f, Zeros zeros = Zeros.AllowSkip)

237
src/Numerics/LinearAlgebra/Storage/SparseCompressedRowMatrixStorage.cs

@ -4,7 +4,7 @@
// http://github.com/mathnet/mathnet-numerics
// http://mathnetnumerics.codeplex.com
//
// Copyright (c) 2009-2014 Math.NET
// Copyright (c) 2009-2015 Math.NET
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
@ -287,54 +287,6 @@ namespace MathNet.Numerics.LinearAlgebra.Storage
MapInplace(x => x, Zeros.AllowSkip);
}
/// <summary>
/// Indicates whether the current object is equal to another object of the same type.
/// </summary>
/// <param name="other">
/// An object to compare with this object.
/// </param>
/// <returns>
/// <c>true</c> if the current object is equal to the <paramref name="other"/> parameter; otherwise, <c>false</c>.
/// </returns>
public override bool Equals(MatrixStorage<T> other)
{
// Reject equality when the argument is null or has a different shape.
if (other == null || ColumnCount != other.ColumnCount || RowCount != other.RowCount)
{
return false;
}
// Accept if the argument is the same object as this.
if (ReferenceEquals(this, other))
{
return true;
}
var sparse = other as SparseCompressedRowMatrixStorage<T>;
if (sparse == null)
{
return base.Equals(other);
}
if (ValueCount != sparse.ValueCount)
{
// TODO: this is only correct if normalized
return false;
}
// If all else fails, perform element wise comparison.
for (var index = 0; index < ValueCount; index++)
{
// TODO: AlmostEquals
if (!Values[index].Equals(sparse.Values[index]) || ColumnIndices[index] != sparse.ColumnIndices[index])
{
return false;
}
}
return true;
}
/// <summary>
/// Returns a hash code for this instance.
/// </summary>
@ -1329,6 +1281,193 @@ namespace MathNet.Numerics.LinearAlgebra.Storage
}
}
// FIND
public override Tuple<int, int, T> Find(Func<T, bool> predicate, Zeros zeros)
{
for (int row = 0; row < RowCount; row++)
{
var startIndex = RowPointers[row];
var endIndex = RowPointers[row + 1];
for (var j = startIndex; j < endIndex; j++)
{
if (predicate(Values[j]))
{
return new Tuple<int, int, T>(row, ColumnIndices[j], Values[j]);
}
}
}
if (zeros == Zeros.Include && ValueCount < (RowCount * ColumnCount))
{
if (predicate(Zero))
{
int k = 0;
for (int row = 0; row < RowCount; row++)
{
for (int col = 0; col < ColumnCount; col++)
{
if (k < RowPointers[row + 1] && ColumnIndices[k] == col)
{
k++;
}
else
{
return new Tuple<int, int, T>(row, col, Zero);
}
}
}
}
}
return null;
}
internal override Tuple<int, int, T, TOther> Find2Unchecked<TOther>(MatrixStorage<TOther> other, Func<T, TOther, bool> predicate, Zeros zeros)
{
var denseOther = other as DenseColumnMajorMatrixStorage<TOther>;
if (denseOther != null)
{
TOther[] otherData = denseOther.Data;
int k = 0;
for (int row = 0; row < RowCount; row++)
{
for (int col = 0; col < ColumnCount; col++)
{
bool available = k < RowPointers[row + 1] && ColumnIndices[k] == col;
if (predicate(available ? Values[k++] : Zero, otherData[col*RowCount + row]))
{
return new Tuple<int, int, T, TOther>(row, col, available ? Values[k - 1] : Zero, otherData[col*RowCount + row]);
}
}
}
return null;
}
var diagonalOther = other as DiagonalMatrixStorage<TOther>;
if (diagonalOther != null)
{
TOther[] otherData = diagonalOther.Data;
TOther otherZero = BuilderInstance<TOther>.Matrix.Zero;
if (zeros == Zeros.Include)
{
int k = 0;
for (int row = 0; row < RowCount; row++)
{
for (int col = 0; col < ColumnCount; col++)
{
bool available = k < RowPointers[row + 1] && ColumnIndices[k] == col;
if (predicate(available ? Values[k++] : Zero, row == col ? otherData[row] : otherZero))
{
return new Tuple<int, int, T, TOther>(row, col, available ? Values[k - 1] : Zero, row == col ? otherData[row] : otherZero);
}
}
}
return null;
}
for (int row = 0; row < RowCount; row++)
{
bool diagonal = false;
var startIndex = RowPointers[row];
var endIndex = RowPointers[row + 1];
for (var j = startIndex; j < endIndex; j++)
{
if (ColumnIndices[j] == row)
{
diagonal = true;
if (predicate(Values[j], otherData[row]))
{
return new Tuple<int, int, T, TOther>(row, row, Values[j], otherData[row]);
}
}
else
{
if (predicate(Values[j], otherZero))
{
return new Tuple<int, int, T, TOther>(row, ColumnIndices[j], Values[j], otherZero);
}
}
}
if (!diagonal && row < ColumnCount)
{
if (predicate(Zero, otherData[row]))
{
return new Tuple<int, int, T, TOther>(row, row, Zero, otherData[row]);
}
}
}
return null;
}
var sparseOther = other as SparseCompressedRowMatrixStorage<TOther>;
if (sparseOther != null)
{
int[] otherRowPointers = sparseOther.RowPointers;
int[] otherColumnIndices = sparseOther.ColumnIndices;
TOther[] otherValues = sparseOther.Values;
TOther otherZero = BuilderInstance<TOther>.Matrix.Zero;
if (zeros == Zeros.Include)
{
int k = 0, otherk = 0;
for (int row = 0; row < RowCount; row++)
{
for (int col = 0; col < ColumnCount; col++)
{
bool available = k < RowPointers[row + 1] && ColumnIndices[k] == col;
bool otherAvailable = otherk < otherRowPointers[row + 1] && otherColumnIndices[otherk] == col;
if (predicate(available ? Values[k++] : Zero, otherAvailable ? otherValues[otherk++] : otherZero))
{
return new Tuple<int, int, T, TOther>(row, col, available ? Values[k - 1] : Zero, otherAvailable ? otherValues[otherk - 1] : otherZero);
}
}
}
return null;
}
for (int row = 0; row < RowCount; row++)
{
var startIndex = RowPointers[row];
var endIndex = RowPointers[row + 1];
var otherStartIndex = otherRowPointers[row];
var otherEndIndex = otherRowPointers[row + 1];
var j1 = startIndex;
var j2 = otherStartIndex;
while (j1 < endIndex || j2 < otherEndIndex)
{
if (j1 == endIndex || j2 < otherEndIndex && ColumnIndices[j1] > otherColumnIndices[j2])
{
if (predicate(Zero, otherValues[j2++]))
{
return new Tuple<int, int, T, TOther>(row, otherColumnIndices[j2 - 1], Zero, otherValues[j2 - 1]);
}
}
else if (j2 == otherEndIndex || ColumnIndices[j1] < otherColumnIndices[j2])
{
if (predicate(Values[j1++], otherZero))
{
return new Tuple<int, int, T, TOther>(row, ColumnIndices[j1 - 1], Values[j1 - 1], otherZero);
}
}
else
{
if (predicate(Values[j1++], otherValues[j2++]))
{
return new Tuple<int, int, T, TOther>(row, ColumnIndices[j1 - 1], Values[j1 - 1], otherValues[j2 - 1]);
}
}
}
}
return null;
}
// FALL BACK
return base.Find2Unchecked(other, predicate, zeros);
}
// FUNCTIONAL COMBINATORS: MAP
public override void MapInplace(Func<T, T> f, Zeros zeros = Zeros.AllowSkip)

2
src/Numerics/LinearAlgebra/Storage/SparseVectorStorage.cs

@ -4,7 +4,7 @@
// http://github.com/mathnet/mathnet-numerics
// http://mathnetnumerics.codeplex.com
//
// Copyright (c) 2009-2013 Math.NET
// Copyright (c) 2009-2015 Math.NET
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation

2
src/Numerics/LinearAlgebra/Storage/VectorStorage.cs

@ -4,7 +4,7 @@
// http://github.com/mathnet/mathnet-numerics
// http://mathnetnumerics.codeplex.com
//
// Copyright (c) 2009-2014 Math.NET
// Copyright (c) 2009-2015 Math.NET
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation

33
src/UnitTests/LinearAlgebraTests/MatrixStructureTheory.cs

@ -29,6 +29,7 @@
// </copyright>
using System;
using System.Linq;
using MathNet.Numerics.LinearAlgebra;
using NUnit.Framework;
@ -80,6 +81,38 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests
Assert.IsFalse((object)left == right);
}
[Theory]
public void IsNotEqualToPermutation(Matrix<T> matrix)
{
if (!matrix.Storage.IsFullyMutable)
{
return;
}
Matrix<T> permutation;
if (matrix.RowCount >= 2 && matrix.Row(1).Any(x => !Zero.Equals(x)))
{
matrix.ClearRow(0);
permutation = matrix.Clone();
permutation.ClearRow(1);
permutation.SetRow(0, matrix.Row(1));
}
else if (matrix.ColumnCount >= 2 && matrix.Column(1).Any(x => !Zero.Equals(x)))
{
matrix.ClearColumn(0);
permutation = matrix.Clone();
permutation.ClearColumn(1);
permutation.SetColumn(0, matrix.Column(1));
}
else
{
return;
}
Assert.That(matrix, Is.Not.EqualTo(permutation));
Assert.IsFalse(matrix.Equals(permutation));
}
[Theory]
public void IsNotEqualToNonMatrixType(Matrix<T> matrix)
{

Loading…
Cancel
Save