From e591f86e2fa569626f27cdcaa09e4f1e924b7d6e Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sat, 21 Mar 2015 12:23:47 +0100 Subject: [PATCH] LA: Vector.Find/2, Exists/2, ForAll/2 --- src/Numerics/LinearAlgebra/Matrix.cs | 24 +++ .../Storage/DenseVectorStorage.cs | 67 +++++++ .../LinearAlgebra/Storage/MatrixStorage.cs | 38 ++++ .../SparseCompressedRowMatrixStorage.cs | 30 ++- .../Storage/SparseVectorStorage.cs | 185 ++++++++++++++---- .../LinearAlgebra/Storage/VectorStorage.cs | 80 ++++++-- src/Numerics/LinearAlgebra/Vector.cs | 57 ++++++ 7 files changed, 413 insertions(+), 68 deletions(-) diff --git a/src/Numerics/LinearAlgebra/Matrix.cs b/src/Numerics/LinearAlgebra/Matrix.cs index 223bcf96..4c67a260 100644 --- a/src/Numerics/LinearAlgebra/Matrix.cs +++ b/src/Numerics/LinearAlgebra/Matrix.cs @@ -1745,33 +1745,57 @@ namespace MathNet.Numerics.LinearAlgebra return Storage.Fold2(other.Storage, f, state, zeros); } + /// + /// Returns a tuple with the index and value of the first element satisfying a predicate, or null if none is found. + /// Zero elements may be skipped on sparse data structures if allowed (default). + /// public Tuple Find(Func predicate, Zeros zeros = Zeros.AllowSkip) { return Storage.Find(predicate, zeros); } + /// + /// Returns a tuple with the index and values of the first element pair of two matrices of the same size satisfying a predicate, or null if none is found. + /// Zero elements may be skipped on sparse data structures if allowed (default). + /// public Tuple Find2(Func predicate, Matrix other, Zeros zeros = Zeros.AllowSkip) where TOther : struct, IEquatable, IFormattable { return Storage.Find2(other.Storage, predicate, zeros); } + /// + /// Returns true if at least one element satisfies a predicate. + /// Zero elements may be skipped on sparse data structures if allowed (default). + /// public bool Exists(Func predicate, Zeros zeros = Zeros.AllowSkip) { return Storage.Find(predicate, zeros) != null; } + /// + /// Returns true if at least one element pairs of two matrices of the same size satisfies a predicate. + /// Zero elements may be skipped on sparse data structures if allowed (default). + /// public bool Exists2(Func predicate, Matrix other, Zeros zeros = Zeros.AllowSkip) where TOther : struct, IEquatable, IFormattable { return Storage.Find2(other.Storage, predicate, zeros) != null; } + /// + /// Returns true if all elements satisfy a predicate. + /// Zero elements may be skipped on sparse data structures if allowed (default). + /// public bool ForAll(Func predicate, Zeros zeros = Zeros.AllowSkip) { return Storage.Find(x => !predicate(x), zeros) == null; } + /// + /// Returns true if all element pairs of two matrices of the same size satisfy a predicate. + /// Zero elements may be skipped on sparse data structures if allowed (default). + /// public bool ForAll2(Func predicate, Matrix other, Zeros zeros = Zeros.AllowSkip) where TOther : struct, IEquatable, IFormattable { diff --git a/src/Numerics/LinearAlgebra/Storage/DenseVectorStorage.cs b/src/Numerics/LinearAlgebra/Storage/DenseVectorStorage.cs index 2cbaaee1..c52b0f82 100644 --- a/src/Numerics/LinearAlgebra/Storage/DenseVectorStorage.cs +++ b/src/Numerics/LinearAlgebra/Storage/DenseVectorStorage.cs @@ -90,6 +90,8 @@ namespace MathNet.Numerics.LinearAlgebra.Storage Data[index] = value; } + // CLEARING + public override void Clear() { Array.Clear(Data, 0, Data.Length); @@ -353,6 +355,71 @@ 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, Data[i]); + } + } + return null; + } + + internal override Tuple Find2Unchecked(VectorStorage other, Func predicate, Zeros zeros) + { + var denseOther = other as DenseVectorStorage; + if (denseOther != null) + { + TOther[] otherData = denseOther.Data; + for (int i = 0; i < Data.Length; i++) + { + if (predicate(Data[i], otherData[i])) + { + return new Tuple(i, Data[i], otherData[i]); + + } + } + return null; + } + + var sparseOther = other as SparseVectorStorage; + if (sparseOther != null) + { + int[] otherIndices = sparseOther.Indices; + TOther[] otherValues = sparseOther.Values; + int otherValueCount = sparseOther.ValueCount; + TOther otherZero = BuilderInstance.Matrix.Zero; + int k = 0; + for (int i = 0; i < Data.Length; i++) + { + if (k < otherValueCount && otherIndices[k] == i) + { + if (predicate(Data[i], otherValues[k])) + { + return new Tuple(i, Data[i], otherValues[k]); + } + k++; + } + else + { + if (predicate(Data[i], otherZero)) + { + return new Tuple(i, Data[i], otherZero); + } + } + } + return null; + } + + // FALLBACK + + return base.Find2Unchecked(other, predicate, zeros); + } + // FUNCTIONAL COMBINATORS internal override void MapToUnchecked(VectorStorage target, Func f, Zeros zeros, ExistingData existingData) diff --git a/src/Numerics/LinearAlgebra/Storage/MatrixStorage.cs b/src/Numerics/LinearAlgebra/Storage/MatrixStorage.cs index 778aca5e..3fcdbb45 100644 --- a/src/Numerics/LinearAlgebra/Storage/MatrixStorage.cs +++ b/src/Numerics/LinearAlgebra/Storage/MatrixStorage.cs @@ -794,6 +794,44 @@ namespace MathNet.Numerics.LinearAlgebra.Storage } } + public void Map2To(MatrixStorage target, MatrixStorage other, Func f, Zeros zeros, ExistingData existingData) + { + if (target == null) + { + throw new ArgumentNullException("target"); + } + + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (RowCount != target.RowCount || ColumnCount != target.ColumnCount) + { + var message = string.Format(Resources.ArgumentMatrixDimensions2, RowCount + "x" + ColumnCount, target.RowCount + "x" + target.ColumnCount); + throw new ArgumentException(message, "target"); + } + + 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"); + } + + Map2ToUnchecked(target, other, f, zeros, existingData); + } + + internal virtual void Map2ToUnchecked(MatrixStorage target, MatrixStorage other, Func f, Zeros zeros, ExistingData existingData) + { + for (int i = 0; i < RowCount; i++) + { + for (int j = 0; j < ColumnCount; j++) + { + target.At(i, j, f(At(i, j), other.At(i, j))); + } + } + } + // FUNCTIONAL COMBINATORS: FOLD /// The state array will not be modified, unless it is the same instance as the target array (which is allowed). diff --git a/src/Numerics/LinearAlgebra/Storage/SparseCompressedRowMatrixStorage.cs b/src/Numerics/LinearAlgebra/Storage/SparseCompressedRowMatrixStorage.cs index d839ebb7..5c42e85d 100644 --- a/src/Numerics/LinearAlgebra/Storage/SparseCompressedRowMatrixStorage.cs +++ b/src/Numerics/LinearAlgebra/Storage/SparseCompressedRowMatrixStorage.cs @@ -1367,7 +1367,8 @@ namespace MathNet.Numerics.LinearAlgebra.Storage TOther[] otherData = diagonalOther.Data; TOther otherZero = BuilderInstance.Matrix.Zero; - if (zeros == Zeros.Include) + // Full Scan + if (zeros == Zeros.Include && predicate(Zero, otherZero)) { int k = 0; for (int row = 0; row < RowCount; row++) @@ -1384,6 +1385,7 @@ namespace MathNet.Numerics.LinearAlgebra.Storage return null; } + // Sparse Scan for (int row = 0; row < RowCount; row++) { bool diagonal = false; @@ -1446,35 +1448,31 @@ namespace MathNet.Numerics.LinearAlgebra.Storage 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) + var k = RowPointers[row]; + var otherk = otherRowPointers[row]; + while (k < endIndex || otherk < otherEndIndex) { - if (j1 == endIndex || j2 < otherEndIndex && ColumnIndices[j1] > otherColumnIndices[j2]) + if (k == endIndex || otherk < otherEndIndex && ColumnIndices[k] > otherColumnIndices[otherk]) { - if (predicate(Zero, otherValues[j2++])) + if (predicate(Zero, otherValues[otherk++])) { - return new Tuple(row, otherColumnIndices[j2 - 1], Zero, otherValues[j2 - 1]); + return new Tuple(row, otherColumnIndices[otherk - 1], Zero, otherValues[otherk - 1]); } } - else if (j2 == otherEndIndex || ColumnIndices[j1] < otherColumnIndices[j2]) + else if (otherk == otherEndIndex || ColumnIndices[k] < otherColumnIndices[otherk]) { - if (predicate(Values[j1++], otherZero)) + if (predicate(Values[k++], otherZero)) { - return new Tuple(row, ColumnIndices[j1 - 1], Values[j1 - 1], otherZero); + return new Tuple(row, ColumnIndices[k - 1], Values[k - 1], otherZero); } } else { - if (predicate(Values[j1++], otherValues[j2++])) + if (predicate(Values[k++], otherValues[otherk++])) { - return new Tuple(row, ColumnIndices[j1 - 1], Values[j1 - 1], otherValues[j2 - 1]); + return new Tuple(row, ColumnIndices[k - 1], Values[k - 1], otherValues[otherk - 1]); } } } diff --git a/src/Numerics/LinearAlgebra/Storage/SparseVectorStorage.cs b/src/Numerics/LinearAlgebra/Storage/SparseVectorStorage.cs index 0f383cb3..8e78c3cd 100644 --- a/src/Numerics/LinearAlgebra/Storage/SparseVectorStorage.cs +++ b/src/Numerics/LinearAlgebra/Storage/SparseVectorStorage.cs @@ -183,42 +183,6 @@ namespace MathNet.Numerics.LinearAlgebra.Storage return delta; } - public override void Clear() - { - ValueCount = 0; - } - - public override void Clear(int index, int count) - { - if (index == 0 && count == Length) - { - Clear(); - return; - } - - var first = Array.BinarySearch(Indices, 0, ValueCount, index); - var last = Array.BinarySearch(Indices, 0, ValueCount, index + count - 1); - if (first < 0) first = ~first; - if (last < 0) last = ~last - 1; - int itemCount = last - first + 1; - - if (itemCount > 0) - { - Array.Copy(Values, first + count, Values, first, ValueCount - first - count); - Array.Copy(Indices, first + count, Indices, first, ValueCount - first - count); - - ValueCount -= count; - } - - // Check whether we need to shrink the arrays. This is reasonable to do if - // there are a lot of non-zero elements and storage is two times bigger - if ((ValueCount > 1024) && (ValueCount < Indices.Length / 2)) - { - Array.Resize(ref Values, ValueCount); - Array.Resize(ref Indices, ValueCount); - } - } - public override bool Equals(VectorStorage other) { // Reject equality when the argument is null or has a different shape. @@ -293,6 +257,44 @@ namespace MathNet.Numerics.LinearAlgebra.Storage return hash; } + // CLEARING + + public override void Clear() + { + ValueCount = 0; + } + + public override void Clear(int index, int count) + { + if (index == 0 && count == Length) + { + Clear(); + return; + } + + var first = Array.BinarySearch(Indices, 0, ValueCount, index); + var last = Array.BinarySearch(Indices, 0, ValueCount, index + count - 1); + if (first < 0) first = ~first; + if (last < 0) last = ~last - 1; + int itemCount = last - first + 1; + + if (itemCount > 0) + { + Array.Copy(Values, first + count, Values, first, ValueCount - first - count); + Array.Copy(Indices, first + count, Indices, first, ValueCount - first - count); + + ValueCount -= count; + } + + // Check whether we need to shrink the arrays. This is reasonable to do if + // there are a lot of non-zero elements and storage is two times bigger + if ((ValueCount > 1024) && (ValueCount < Indices.Length / 2)) + { + Array.Resize(ref Values, ValueCount); + Array.Resize(ref Indices, ValueCount); + } + } + // INITIALIZATION public static SparseVectorStorage OfVector(VectorStorage vector) @@ -645,6 +647,117 @@ namespace MathNet.Numerics.LinearAlgebra.Storage } } + // FIND + + public override Tuple Find(Func predicate, Zeros zeros) + { + for (int i = 0; i < ValueCount; i++) + { + if (predicate(Values[i])) + { + return new Tuple(Indices[i], Values[i]); + } + } + if (zeros == Zeros.Include && ValueCount < Length && predicate(Zero)) + { + for (int i = 0; i < Length; i++) + { + if (i >= ValueCount || Indices[i] != i) + { + return new Tuple(i, Zero); + } + } + } + return null; + } + + internal override Tuple Find2Unchecked(VectorStorage other, Func predicate, Zeros zeros) + { + var denseOther = other as DenseVectorStorage; + if (denseOther != null) + { + TOther[] otherData = denseOther.Data; + int k = 0; + for (int i = 0; i < otherData.Length; i++) + { + if (k < ValueCount && Indices[k] == i) + { + if (predicate(Values[k], otherData[i])) + { + return new Tuple(i, Values[k], otherData[i]); + } + k++; + } + else + { + if (predicate(Zero, otherData[i])) + { + return new Tuple(i, Zero, otherData[i]); + } + } + } + return null; + } + + var sparseOther = other as SparseVectorStorage; + if (sparseOther != null) + { + int[] otherIndices = sparseOther.Indices; + TOther[] otherValues = sparseOther.Values; + int otherValueCount = sparseOther.ValueCount; + TOther otherZero = BuilderInstance.Matrix.Zero; + + // Full Scan + int k = 0, otherk = 0; + if (zeros == Zeros.Include && ValueCount < Length && sparseOther.ValueCount < Length && predicate(Zero, otherZero)) + { + for (int i = 0; i < Length; i++) + { + var left = k < ValueCount && Indices[k] == i ? Values[k++] : Zero; + var right = otherk < otherValueCount && otherIndices[otherk] == i ? otherValues[otherk++] : otherZero; + if (predicate(left, right)) + { + return new Tuple(i, left, right); + } + } + return null; + } + + // Sparse Scan + k = 0; + otherk = 0; + while (k < ValueCount || otherk < otherValueCount) + { + if (k == ValueCount || otherk < otherValueCount && Indices[k] > otherIndices[otherk]) + { + if (predicate(Zero, otherValues[otherk++])) + { + return new Tuple(otherIndices[otherk - 1], Zero, otherValues[otherk - 1]); + } + } + else if (otherk == otherValueCount || Indices[k] < otherIndices[otherk]) + { + if (predicate(Values[k++], otherZero)) + { + return new Tuple(Indices[k - 1], Values[k - 1], otherZero); + } + } + else + { + if (predicate(Values[k++], otherValues[otherk++])) + { + return new Tuple(Indices[k - 1], Values[k - 1], otherValues[otherk - 1]); + } + } + } + return null; + } + + // FALL BACK + + return base.Find2Unchecked(other, predicate, zeros); + } + // FUNCTIONAL COMBINATORS internal override void MapToUnchecked(VectorStorage target, Func f, Zeros zeros, ExistingData existingData) diff --git a/src/Numerics/LinearAlgebra/Storage/VectorStorage.cs b/src/Numerics/LinearAlgebra/Storage/VectorStorage.cs index 04077836..48f68bb6 100644 --- a/src/Numerics/LinearAlgebra/Storage/VectorStorage.cs +++ b/src/Numerics/LinearAlgebra/Storage/VectorStorage.cs @@ -98,22 +98,6 @@ namespace MathNet.Numerics.LinearAlgebra.Storage /// WARNING: This method is not thread safe. Use "lock" with it and be sure to avoid deadlocks. public abstract void At(int index, T value); - public virtual void Clear() - { - for (var i = 0; i < Length; i++) - { - At(i, Zero); - } - } - - public virtual void Clear(int index, int count) - { - for (var i = index; i < index + count; i++) - { - At(i, Zero); - } - } - /// /// Indicates whether the current object is equal to another object of the same type. /// @@ -185,6 +169,24 @@ namespace MathNet.Numerics.LinearAlgebra.Storage return hash; } + // CLEARING + + public virtual void Clear() + { + for (var i = 0; i < Length; i++) + { + At(i, Zero); + } + } + + public virtual void Clear(int index, int count) + { + for (var i = index; i < index + count; i++) + { + At(i, Zero); + } + } + // VECTOR COPY public void CopyTo(VectorStorage target, ExistingData existingData = ExistingData.Clear) @@ -411,6 +413,52 @@ namespace MathNet.Numerics.LinearAlgebra.Storage } } + // FIND + + public virtual Tuple Find(Func predicate, Zeros zeros) + { + for (int i = 0; i < Length; i++) + { + var item = At(i); + if (predicate(item)) + { + return new Tuple(i, item); + } + } + return null; + } + + public Tuple Find2(VectorStorage other, Func predicate, Zeros zeros) + where TOther : struct, IEquatable, IFormattable + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Length != other.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); + } + + return Find2Unchecked(other, predicate, zeros); + } + + internal virtual Tuple Find2Unchecked(VectorStorage other, Func predicate, Zeros zeros) + where TOther : struct, IEquatable, IFormattable + { + for (int i = 0; i < Length; i++) + { + var item = At(i); + var otherItem = other.At(i); + if (predicate(item, otherItem)) + { + return new Tuple(i, item, otherItem); + } + } + return null; + } + // FUNCTIONAL COMBINATORS public void MapTo(VectorStorage target, Func f, diff --git a/src/Numerics/LinearAlgebra/Vector.cs b/src/Numerics/LinearAlgebra/Vector.cs index d01e8675..1988103a 100644 --- a/src/Numerics/LinearAlgebra/Vector.cs +++ b/src/Numerics/LinearAlgebra/Vector.cs @@ -484,5 +484,62 @@ namespace MathNet.Numerics.LinearAlgebra { return Storage.Fold2(other.Storage, f, state, zeros); } + + /// + /// Returns a tuple with the index and value of the first element satisfying a predicate, or null if none is found. + /// Zero elements may be skipped on sparse data structures if allowed (default). + /// + public Tuple Find(Func predicate, Zeros zeros = Zeros.AllowSkip) + { + return Storage.Find(predicate, zeros); + } + + /// + /// Returns a tuple with the index and values of the first element pair of two vectors of the same size satisfying a predicate, or null if none is found. + /// Zero elements may be skipped on sparse data structures if allowed (default). + /// + public Tuple Find2(Func predicate, Vector other, Zeros zeros = Zeros.AllowSkip) + where TOther : struct, IEquatable, IFormattable + { + return Storage.Find2(other.Storage, predicate, zeros); + } + + /// + /// Returns true if at least one element satisfies a predicate. + /// Zero elements may be skipped on sparse data structures if allowed (default). + /// + public bool Exists(Func predicate, Zeros zeros = Zeros.AllowSkip) + { + return Storage.Find(predicate, zeros) != null; + } + + /// + /// Returns true if at least one element pairs of two vectors of the same size satisfies a predicate. + /// Zero elements may be skipped on sparse data structures if allowed (default). + /// + public bool Exists2(Func predicate, Vector other, Zeros zeros = Zeros.AllowSkip) + where TOther : struct, IEquatable, IFormattable + { + return Storage.Find2(other.Storage, predicate, zeros) != null; + } + + /// + /// Returns true if all elements satisfy a predicate. + /// Zero elements may be skipped on sparse data structures if allowed (default). + /// + public bool ForAll(Func predicate, Zeros zeros = Zeros.AllowSkip) + { + return Storage.Find(x => !predicate(x), zeros) == null; + } + + /// + /// Returns true if all element pairs of two vectors of the same size satisfy a predicate. + /// Zero elements may be skipped on sparse data structures if allowed (default). + /// + public bool ForAll2(Func predicate, Vector other, Zeros zeros = Zeros.AllowSkip) + where TOther : struct, IEquatable, IFormattable + { + return Storage.Find2(other.Storage, (x, y) => !predicate(x, y), zeros) == null; + } } }