diff --git a/src/Numerics/Compatibility.cs b/src/Numerics/Compatibility.cs index c36e8bac..b84b2a59 100644 --- a/src/Numerics/Compatibility.cs +++ b/src/Numerics/Compatibility.cs @@ -233,6 +233,41 @@ namespace MathNet.Numerics } } + public class Tuple : IComparable, IComparable> + { + 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; + if (other == null) throw new ArgumentException(); + return CompareTo(other); + } + + public int CompareTo(Tuple 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 Zip(this IEnumerable seqA, IEnumerable seqB, Func func) diff --git a/src/Numerics/LinearAlgebra/Storage/DenseColumnMajorMatrixStorage.cs b/src/Numerics/LinearAlgebra/Storage/DenseColumnMajorMatrixStorage.cs index e2a43032..381fe52a 100644 --- a/src/Numerics/LinearAlgebra/Storage/DenseColumnMajorMatrixStorage.cs +++ b/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; } + /// + /// Evaluate the row and column at a specific data index. + /// + 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 Find(Func 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(row, column, Data[i]); + } + } + return null; + } + + internal override Tuple Find2Unchecked(MatrixStorage other, Func predicate, Zeros zeros) + { + var denseOther = other as DenseColumnMajorMatrixStorage; + 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(row, column, Data[i], otherData[i]); + + } + } + return null; + } + + var diagonalOther = other as DiagonalMatrixStorage; + if (diagonalOther != null) + { + TOther[] otherData = diagonalOther.Data; + TOther otherZero = BuilderInstance.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(i, j, Data[k], i == j ? otherData[i] : otherZero); + } + k++; + } + } + return null; + } + + var sparseOther = other as SparseCompressedRowMatrixStorage; + if (sparseOther != null) + { + int[] otherRowPointers = sparseOther.RowPointers; + int[] otherColumnIndices = sparseOther.ColumnIndices; + TOther[] otherValues = sparseOther.Values; + TOther otherZero = BuilderInstance.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(row, col, Data[col*RowCount + row], otherValues[k]); + } + k++; + } + else + { + if (predicate(Data[col*RowCount + row], otherZero)) + { + return new Tuple(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 f, Zeros zeros = Zeros.AllowSkip) diff --git a/src/Numerics/LinearAlgebra/Storage/DenseVectorStorage.cs b/src/Numerics/LinearAlgebra/Storage/DenseVectorStorage.cs index d472ca6b..59913410 100644 --- a/src/Numerics/LinearAlgebra/Storage/DenseVectorStorage.cs +++ b/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 diff --git a/src/Numerics/LinearAlgebra/Storage/DiagonalMatrixStorage.cs b/src/Numerics/LinearAlgebra/Storage/DiagonalMatrixStorage.cs index 0242a783..09a182bd 100644 --- a/src/Numerics/LinearAlgebra/Storage/DiagonalMatrixStorage.cs +++ b/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 } } - /// - /// Indicates whether the current object is equal to another object of the same type. - /// - /// - /// An object to compare with this object. - /// - /// - /// true if the current object is equal to the parameter; otherwise, false. - /// - public override bool Equals(MatrixStorage other) - { - var diagonal = other as DiagonalMatrixStorage; - 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(); - } - /// /// Returns a hash code for this instance. /// @@ -635,6 +597,137 @@ namespace MathNet.Numerics.LinearAlgebra.Storage } } + // FIND + + public override Tuple Find(Func predicate, Zeros zeros) + { + for (int i = 0; i < Data.Length; i++) + { + if (predicate(Data[i])) + { + return new Tuple(i, i, Data[i]); + } + } + if (zeros == Zeros.Include && (RowCount > 1 || ColumnCount > 1)) + { + if (predicate(Zero)) + { + return new Tuple(RowCount > 1 ? 1 : 0, RowCount > 1 ? 0 : 1, Zero); + } + } + return null; + } + + internal override Tuple Find2Unchecked(MatrixStorage other, Func predicate, Zeros zeros) + { + var denseOther = other as DenseColumnMajorMatrixStorage; + 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(i, j, i == j ? Data[i] : Zero, otherData[k]); + } + k++; + } + } + return null; + } + + var diagonalOther = other as DiagonalMatrixStorage; + if (diagonalOther != null) + { + TOther[] otherData = diagonalOther.Data; + for (int i = 0; i < Data.Length; i++) + { + if (predicate(Data[i], otherData[i])) + { + return new Tuple(i, i, Data[i], otherData[i]); + } + } + if (zeros == Zeros.Include && (RowCount > 1 || ColumnCount > 1)) + { + TOther otherZero = BuilderInstance.Matrix.Zero; + if (predicate(Zero, otherZero)) + { + return new Tuple(RowCount > 1 ? 1 : 0, RowCount > 1 ? 0 : 1, Zero, otherZero); + } + } + return null; + } + + var sparseOther = other as SparseCompressedRowMatrixStorage; + if (sparseOther != null) + { + int[] otherRowPointers = sparseOther.RowPointers; + int[] otherColumnIndices = sparseOther.ColumnIndices; + TOther[] otherValues = sparseOther.Values; + TOther otherZero = BuilderInstance.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(row, row, Data[row], otherValues[j]); + } + } + else + { + if (predicate(Zero, otherValues[j])) + { + return new Tuple(row, otherColumnIndices[j], Zero, otherValues[j]); + } + } + } + if (!diagonal && row < ColumnCount) + { + if (predicate(Data[row], otherZero)) + { + return new Tuple(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(row, col, Zero, otherZero); + } + } + } + } + } + return null; + } + + // FALL BACK + + return base.Find2Unchecked(other, predicate, zeros); + } + // FUNCTIONAL COMBINATORS: MAP public override void MapInplace(Func f, Zeros zeros = Zeros.AllowSkip) diff --git a/src/Numerics/LinearAlgebra/Storage/MatrixStorage.cs b/src/Numerics/LinearAlgebra/Storage/MatrixStorage.cs index 19f3846e..e2799e54 100644 --- a/src/Numerics/LinearAlgebra/Storage/MatrixStorage.cs +++ b/src/Numerics/LinearAlgebra/Storage/MatrixStorage.cs @@ -137,7 +137,7 @@ namespace MathNet.Numerics.LinearAlgebra.Storage /// /// true if the current object is equal to the parameter; otherwise, false. /// - public virtual bool Equals(MatrixStorage other) + public bool Equals(MatrixStorage 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; } /// @@ -627,6 +616,59 @@ namespace MathNet.Numerics.LinearAlgebra.Storage } } + // FIND + + public virtual Tuple Find(Func 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(i, j, item); + } + } + } + return null; + } + + public Tuple Find2(MatrixStorage other, Func predicate, Zeros zeros) + where TOther : struct, IEquatable, 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 Find2Unchecked(MatrixStorage other, Func predicate, Zeros zeros) + where TOther : struct, IEquatable, 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(i, j, item, otherItem); + } + } + } + return null; + } + // FUNCTIONAL COMBINATORS: MAP public virtual void MapInplace(Func f, Zeros zeros = Zeros.AllowSkip) diff --git a/src/Numerics/LinearAlgebra/Storage/SparseCompressedRowMatrixStorage.cs b/src/Numerics/LinearAlgebra/Storage/SparseCompressedRowMatrixStorage.cs index b87a217b..2f6f280c 100644 --- a/src/Numerics/LinearAlgebra/Storage/SparseCompressedRowMatrixStorage.cs +++ b/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); } - /// - /// Indicates whether the current object is equal to another object of the same type. - /// - /// - /// An object to compare with this object. - /// - /// - /// true if the current object is equal to the parameter; otherwise, false. - /// - public override bool Equals(MatrixStorage 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; - 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; - } - /// /// Returns a hash code for this instance. /// @@ -1329,6 +1281,193 @@ namespace MathNet.Numerics.LinearAlgebra.Storage } } + // FIND + + public override Tuple Find(Func 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(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(row, col, Zero); + } + } + } + } + } + return null; + } + + internal override Tuple Find2Unchecked(MatrixStorage other, Func predicate, Zeros zeros) + { + var denseOther = other as DenseColumnMajorMatrixStorage; + 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(row, col, available ? Values[k - 1] : Zero, otherData[col*RowCount + row]); + } + } + } + return null; + } + + var diagonalOther = other as DiagonalMatrixStorage; + if (diagonalOther != null) + { + TOther[] otherData = diagonalOther.Data; + TOther otherZero = BuilderInstance.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(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(row, row, Values[j], otherData[row]); + } + } + else + { + if (predicate(Values[j], otherZero)) + { + return new Tuple(row, ColumnIndices[j], Values[j], otherZero); + } + } + } + if (!diagonal && row < ColumnCount) + { + if (predicate(Zero, otherData[row])) + { + return new Tuple(row, row, Zero, otherData[row]); + } + } + } + return null; + } + + var sparseOther = other as SparseCompressedRowMatrixStorage; + if (sparseOther != null) + { + int[] otherRowPointers = sparseOther.RowPointers; + int[] otherColumnIndices = sparseOther.ColumnIndices; + TOther[] otherValues = sparseOther.Values; + TOther otherZero = BuilderInstance.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(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(row, otherColumnIndices[j2 - 1], Zero, otherValues[j2 - 1]); + } + } + else if (j2 == otherEndIndex || ColumnIndices[j1] < otherColumnIndices[j2]) + { + if (predicate(Values[j1++], otherZero)) + { + return new Tuple(row, ColumnIndices[j1 - 1], Values[j1 - 1], otherZero); + } + } + else + { + if (predicate(Values[j1++], otherValues[j2++])) + { + return new Tuple(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 f, Zeros zeros = Zeros.AllowSkip) diff --git a/src/Numerics/LinearAlgebra/Storage/SparseVectorStorage.cs b/src/Numerics/LinearAlgebra/Storage/SparseVectorStorage.cs index 8f90a1eb..94b13f68 100644 --- a/src/Numerics/LinearAlgebra/Storage/SparseVectorStorage.cs +++ b/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 diff --git a/src/Numerics/LinearAlgebra/Storage/VectorStorage.cs b/src/Numerics/LinearAlgebra/Storage/VectorStorage.cs index 7b4b60d4..87993f59 100644 --- a/src/Numerics/LinearAlgebra/Storage/VectorStorage.cs +++ b/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 diff --git a/src/UnitTests/LinearAlgebraTests/MatrixStructureTheory.cs b/src/UnitTests/LinearAlgebraTests/MatrixStructureTheory.cs index 1d3b1f67..7c5e6282 100644 --- a/src/UnitTests/LinearAlgebraTests/MatrixStructureTheory.cs +++ b/src/UnitTests/LinearAlgebraTests/MatrixStructureTheory.cs @@ -29,6 +29,7 @@ // 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 matrix) + { + if (!matrix.Storage.IsFullyMutable) + { + return; + } + + Matrix 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 matrix) {