diff --git a/src/FSharp/LinearAlgebra.Double.Vector.fs b/src/FSharp/LinearAlgebra.Double.Vector.fs index 6b83fe25..7857b3a5 100644 --- a/src/FSharp/LinearAlgebra.Double.Vector.fs +++ b/src/FSharp/LinearAlgebra.Double.Vector.fs @@ -44,29 +44,58 @@ module Vector = /// In-place mutation by applying a function to every element of the vector. let inline mapInPlace (f: float -> float) (v: #Vector) = - for i=0 to v.Count-1 do - v.At(i, f (v.At i)) + v.MapInplace((fun x -> f x), true) () /// In-place mutation by applying a function to every element of the vector. let inline mapiInPlace (f: int -> float -> float) (v: #Vector) = - for i=0 to v.Count-1 do - v.At(i, f i (v.At i)) + v.MapIndexedInplace((fun i x -> f i x), true) () - /// In-place vector addition. - let inline addInPlace (v: #Vector) (w: #Vector) = v.Add(w, v) + /// In-place mutation by applying a function to every element of the vector. + /// Zero-values may be skipped (relevant mostly for sparse vectors). + let inline mapnzInPlace (f: float -> float) (v: #Vector) = + v.MapInplace((fun x -> f x), false) + () - /// In place vector subtraction. - let inline subInPlace (v: #Vector) (w: #Vector) = v.Subtract(w, v) + /// In-place mutation by applying a function to every element of the vector. + /// Zero-values may be skipped (relevant mostly for sparse vectors). + let inline mapinzInPlace (f: int -> float -> float) (v: #Vector) = + v.MapIndexedInplace((fun i x -> f i x), false) + () - /// Functional map operator for vectors. - /// + /// Maps a vector to a new vector by applying a function to every element. let inline map f (v: #Vector) = let w = v.Clone() - mapInPlace (fun x -> f x) w + w.MapInplace((fun x -> f x), true) + w + + /// Maps a vector to a new vector by applying a function to every element. + /// Zero-values may be skipped (relevant mostly for sparse vectors). + let inline mapnz f (v: #Vector) = + let w = v.Clone() + w.MapInplace((fun x -> f x), false) + w + + /// Maps a vector to a new vector by applying a function to every element. + let inline mapi (f: int -> float -> float) (v: #Vector) = + let w = v.Clone() + w.MapIndexedInplace((fun i x -> f i x), true) w + /// Maps a vector to a new vector by applying a function to every element. + /// Zero-values may be skipped (relevant mostly for sparse vectors). + let inline mapinz (f: int -> float -> float) (v: #Vector) = + let w = v.Clone() + w.MapIndexedInplace((fun i x -> f i x), false) + w + + /// In-place vector addition. + let inline addInPlace (v: #Vector) (w: #Vector) = v.Add(w, v) + + /// In place vector subtraction. + let inline subInPlace (v: #Vector) (w: #Vector) = v.Subtract(w, v) + /// Applies a function to all elements of the vector. let inline iter (f: float -> unit) (v: #Vector) = for i=0 to v.Count-1 do @@ -77,11 +106,6 @@ module Vector = for i=0 to v.Count-1 do f i (v.At i) - /// Maps a vector to a new vector by applying a function to every element. - let inline mapi (f: int -> float -> float) (v: #Vector) = - let w = v.Clone() - mapiInPlace f w - w /// Fold all entries of a vector. let inline fold (f: 'a -> float -> 'a) (acc0: 'a) (v: #Vector) = diff --git a/src/FSharpUnitTests/VectorTests.fs b/src/FSharpUnitTests/VectorTests.fs index d60a171b..569c2a6c 100644 --- a/src/FSharpUnitTests/VectorTests.fs +++ b/src/FSharpUnitTests/VectorTests.fs @@ -11,6 +11,9 @@ module VectorTests = /// A small uniform vector. let smallv = new DenseVector([|0.3;0.3;0.3;0.3;0.3|]) :> Vector + /// A small uniform vector. + let sparsev = SparseVector.OfIndexedEnumerable(5, [(1,0.3)]) :> Vector + /// A large vector with increasingly large entries let largev = new DenseVector(Array.init 100 (fun i -> float i / 100.0)) :> Vector @@ -49,16 +52,40 @@ module VectorTests = Vector.toList smallv |> should equal [0.3;0.3;0.3;0.3;0.3] [] - let ``Vector.mapInPlace`` () = + let ``Vector.mapInPlace.Dense`` () = let w = smallv.Clone() Vector.mapInPlace (fun x -> 2.0 * x) w w |> should equal (2.0 * smallv) [] - let ``Vector.mapiInPlace`` () = + let ``Vector.mapInPlace.Sparse`` () = + let w = sparsev.Clone() + Vector.mapInPlace (fun x -> 2.0 * x) w + w |> should equal (2.0 * sparsev) + + [] + let ``Vector.mapnzInPlace.Sparse`` () = + let w = sparsev.Clone() + Vector.mapnzInPlace (fun x -> 2.0 * x) w + w |> should equal (2.0 * sparsev) + + [] + let ``Vector.mapiInPlace.Dense`` () = let w = largev.Clone() Vector.mapiInPlace (fun i x -> float i / 100.0) w w |> should equal (largev) + + [] + let ``Vector.mapiInPlace.Sparse`` () = + let w = sparsev.Clone() + Vector.mapiInPlace (fun i x -> 2.0 * float i * x) w + w |> should equal (2.0 * sparsev) + + [] + let ``Vector.mapinzInPlace.Sparse`` () = + let w = sparsev.Clone() + Vector.mapinzInPlace (fun i x -> 2.0 * float i * x) w + w |> should equal (2.0 * sparsev) [] let ``Vector.addInPlace`` () = @@ -76,10 +103,18 @@ module VectorTests = let ``Vector.map`` () = Vector.map (fun x -> 2.0 * x) largev |> should equal (2.0 * largev) + [] + let ``Vector.mapnz`` () = + Vector.mapnz (fun x -> 2.0 * x) largev |> should equal (2.0 * largev) + [] let ``Vector.mapi`` () = Vector.mapi (fun i x -> float i / 100.0) largev |> should equal largev + [] + let ``Vector.mapinz`` () = + Vector.mapinz (fun i x -> float i / 100.0) largev |> should equal largev + [] let ``Vector.fold`` () = Vector.fold (fun a b -> a - b) 0.0 smallv |> should equal -1.5 diff --git a/src/Numerics/LinearAlgebra/Generic/Vector.cs b/src/Numerics/LinearAlgebra/Generic/Vector.cs index bea0f281..a32d1260 100644 --- a/src/Numerics/LinearAlgebra/Generic/Vector.cs +++ b/src/Numerics/LinearAlgebra/Generic/Vector.cs @@ -1291,5 +1291,26 @@ namespace MathNet.Numerics.LinearAlgebra.Generic { return Storage.EnumerateNonZero(); } + + /// + /// Applies a function to each value of this vector and replaces the value with its result. + /// If forceMapZero is not set to true, zero values may or may not be skipped depending + /// on the actual data storage implementation (relevant mostly for sparse vectors). + /// + public void MapInplace(Func f, bool forceMapZeros = false) + { + Storage.MapInplace(f, forceMapZeros); + } + + /// + /// Applies a function to each value of this vector and replaces the value with its result. + /// The index of each value (zero-based) is passed as first argument to the function. + /// If forceMapZero is not set to true, zero values may or may not be skipped depending + /// on the actual data storage implementation (relevant mostly for sparse vectors). + /// + public void MapIndexedInplace(Func f, bool forceMapZeros = false) + { + Storage.MapIndexedInplace(f, forceMapZeros); + } } } diff --git a/src/Numerics/LinearAlgebra/Storage/DenseVectorStorage.cs b/src/Numerics/LinearAlgebra/Storage/DenseVectorStorage.cs index a53b3312..23980e74 100644 --- a/src/Numerics/LinearAlgebra/Storage/DenseVectorStorage.cs +++ b/src/Numerics/LinearAlgebra/Storage/DenseVectorStorage.cs @@ -291,5 +291,23 @@ namespace MathNet.Numerics.LinearAlgebra.Storage target.At(ii, columnIndex, Data[i]); } } + + // FUNCTIONAL COMBINATORS + + public override void MapInplace(Func f, bool forceMapZeros = false) + { + for (int i = 0; i < Data.Length; i++) + { + Data[i] = f(Data[i]); + } + } + + public override void MapIndexedInplace(Func f, bool forceMapZeros = false) + { + for (int i = 0; i < Data.Length; i++) + { + Data[i] = f(i, Data[i]); + } + } } } diff --git a/src/Numerics/LinearAlgebra/Storage/SparseVectorStorage.cs b/src/Numerics/LinearAlgebra/Storage/SparseVectorStorage.cs index f93ff268..47d00ea1 100644 --- a/src/Numerics/LinearAlgebra/Storage/SparseVectorStorage.cs +++ b/src/Numerics/LinearAlgebra/Storage/SparseVectorStorage.cs @@ -590,5 +590,75 @@ namespace MathNet.Numerics.LinearAlgebra.Storage target.At(Indices[i] + offset, Values[i]); } } + + // FUNCTIONAL COMBINATORS + + public override void MapInplace(Func f, bool forceMapZeros = false) + { + var indices = new List(); + var values = new List(); + if (forceMapZeros || !Zero.Equals(f(Zero))) + { + int k = 0; + for (int i = 0; i < Length; i++) + { + var item = k < ValueCount && (Indices[k]) == i ? f(Values[k++]) : f(Zero); + if (!Zero.Equals(item)) + { + values.Add(item); + indices.Add(i); + } + } + } + else + { + for (int i = 0; i < Values.Length; i++) + { + var item = f(Values[i]); + if (!Zero.Equals(item)) + { + values.Add(item); + indices.Add(Indices[i]); + } + } + } + Indices = indices.ToArray(); + Values = values.ToArray(); + ValueCount = values.Count; + } + + public override void MapIndexedInplace(Func f, bool forceMapZeros = false) + { + var indices = new List(); + var values = new List(); + if (forceMapZeros || !Zero.Equals(f(0, Zero))) + { + int k = 0; + for (int i = 0; i < Length; i++) + { + var item = k < ValueCount && (Indices[k]) == i ? f(i, Values[k++]) : f(i, Zero); + if (!Zero.Equals(item)) + { + values.Add(item); + indices.Add(i); + } + } + } + else + { + for (int i = 0; i < Values.Length; i++) + { + var item = f(Indices[i], Values[i]); + if (!Zero.Equals(item)) + { + values.Add(item); + indices.Add(Indices[i]); + } + } + } + Indices = indices.ToArray(); + Values = values.ToArray(); + ValueCount = values.Count; + } } } diff --git a/src/Numerics/LinearAlgebra/Storage/VectorStorage.cs b/src/Numerics/LinearAlgebra/Storage/VectorStorage.cs index 659365ad..89caa3a1 100644 --- a/src/Numerics/LinearAlgebra/Storage/VectorStorage.cs +++ b/src/Numerics/LinearAlgebra/Storage/VectorStorage.cs @@ -391,5 +391,23 @@ namespace MathNet.Numerics.LinearAlgebra.Storage target.At(ii, columnIndex, At(i)); } } + + // FUNCTIONAL COMBINATORS + + public virtual void MapInplace(Func f, bool forceMapZeros = false) + { + for (int i = 0; i < Length; i++) + { + At(i, f(At(i))); + } + } + + public virtual void MapIndexedInplace(Func f, bool forceMapZeros = false) + { + for (int i = 0; i < Length; i++) + { + At(i, f(i, At(i))); + } + } } }