From 4d50a4dcdabb8d0f80dc96d6e63f03fd0d331da5 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 26 Mar 2024 17:37:12 +0100 Subject: [PATCH] [Tests] Add tests for tensor math extensions C# extensions in TensorMathExt_*.cs files do not handle sub windows properly. --- .../Aardvark.Base.FSharp.Tests.fsproj | 1 + .../TensorMathTests.fs | 324 ++++++++++++++++++ 2 files changed, 325 insertions(+) create mode 100644 src/Tests/Aardvark.Base.FSharp.Tests/TensorMathTests.fs diff --git a/src/Tests/Aardvark.Base.FSharp.Tests/Aardvark.Base.FSharp.Tests.fsproj b/src/Tests/Aardvark.Base.FSharp.Tests/Aardvark.Base.FSharp.Tests.fsproj index df0fcfc6..c3fb3939 100644 --- a/src/Tests/Aardvark.Base.FSharp.Tests/Aardvark.Base.FSharp.Tests.fsproj +++ b/src/Tests/Aardvark.Base.FSharp.Tests/Aardvark.Base.FSharp.Tests.fsproj @@ -20,6 +20,7 @@ + diff --git a/src/Tests/Aardvark.Base.FSharp.Tests/TensorMathTests.fs b/src/Tests/Aardvark.Base.FSharp.Tests/TensorMathTests.fs new file mode 100644 index 00000000..aaae12d1 --- /dev/null +++ b/src/Tests/Aardvark.Base.FSharp.Tests/TensorMathTests.fs @@ -0,0 +1,324 @@ +namespace Aardvark.Base.FSharp.Tests + +open Aardvark.Base + +open FsUnit +open NUnit.Framework + +module ``Tensor Math Tests`` = + + type Tensor = + | Full = 0 + | Sub = 1 + | Window = 2 + + let mutable rnd = Unchecked.defaultof + let equalApprox exp = equalWithin 0.0001 exp + + module Vector = + + module Ref = + + let inline dot (a: Vector< ^T>) (b: Vector< ^T>) = + a.InnerProduct(b, (*), LanguagePrimitives.GenericZero< ^T>, (+)) + + let inline approxEqual (a: Vector<'T>) (b: Vector<'T>) = + if a.S <> b.S then + invalidArg "size" "Mismatching size" + + (a.Elements, b.Elements) + ||> Seq.iter2 (fun a b -> a |> should equalApprox b) + + module Matrix = + + let inline approxEqual (a: Matrix<'T>) (b: Matrix<'T>) = + if a.S <> b.S then + invalidArg "size" "Mismatching size" + + (a.Elements, b.Elements) + ||> Seq.iter2 (fun a b -> a |> should equalApprox b) + + [] + let iterations = 100 + + [] + module Generation = + let inline getVectorOfSize (t: Tensor) (n: ^Int) = + let n = int n + + match t with + | Tensor.Full -> + Vector.Create <| Array.init n (ignore >> rnd.UniformDouble) + + | Tensor.Sub -> + let arr = Array.init (n + 512) (ignore >> rnd.UniformDouble) + let d = 1 + rnd.UniformInt 4 + let f = rnd.UniformInt (arr.Length - (n - 1) * d) + Vector.Create(arr).SubVector(f, n, d) + + | _ -> + let arr = Array.init (n + 512) (ignore >> rnd.UniformDouble) + let d = 1 + rnd.UniformInt 4 + let f = rnd.UniformInt ((arr.Length - (n - 1) * d) / d) + Vector.Create(arr).SubVectorWindow(f, n, d) + + let getVector (t: Tensor) = + getVectorOfSize t (32 + rnd.UniformInt 128) + + let inline getMatrixOfSize (t: Tensor) (s: ^V2i) = + let s = v2i s + + match t with + | Tensor.Full -> + let arr = Array.init (s.X * s.Y) (ignore >> rnd.UniformDouble) + Matrix(arr, s) + + | Tensor.Sub -> + let S = s + 256 + let arr = Array.init (S.X * S.Y) (ignore >> rnd.UniformDouble) + let d = 1 + rnd.UniformV2i 4 + let f = rnd.UniformV2i (S - (s - 1) * d) + Matrix(arr, S).SubMatrix(f, s, V2i(d.X, d.Y * S.X)) + + | _ -> + let S = s + 256 + let arr = Array.init (S.X * S.Y) (ignore >> rnd.UniformDouble) + let d = 1 + rnd.UniformV2i 4 + let f = rnd.UniformV2i ((S - (s - 1) * d) / d) + Matrix(arr, S).SubMatrixWindow(f, s, V2i(d.X, d.Y * S.X)) + + let getMatrix (t: Tensor) = + getMatrixOfSize t (16 + rnd.UniformV2i 64) + + let inline getMatrixOfHeight (t: Tensor) (h: ^Int) = + getMatrixOfSize t (V2i(16 + rnd.UniformInt 64, int h)) + + [] + let setup() = + rnd <- RandomSystem(0) + + [] + let ``[Vector] Dot product`` (tensor: Tensor) = + for _ = 1 to iterations do + let a = getVector tensor + let b = getVectorOfSize tensor a.Size + a.DotProduct(b) |> should equalApprox (Vector.Ref.dot a b) + + [] + let ``[Vector] Norm2 squared`` (tensor: Tensor) = + let inline ref (v: Vector< ^T>) = + v.Norm(sqr, LanguagePrimitives.GenericZero< ^T>, (+)) + + for _ = 1 to iterations do + let v = getVector tensor + v.NormSquared() |> should equalApprox (ref v) + + [] + let ``[Vector] Dist1`` (tensor: Tensor) = + let inline ref (a: Vector< ^T>) (b: Vector< ^T>) = + a.InnerProduct(b, (fun x y -> abs (x - y)), LanguagePrimitives.GenericZero< ^T>, (+)) + + for _ = 1 to iterations do + let a = getVector tensor + let b = getVectorOfSize tensor a.Size + a.Dist1(b) |> should equalApprox (ref a b) + + [] + let ``[Vector] Dist2`` (tensor: Tensor) = + let inline ref (a: Vector< ^T>) (b: Vector< ^T>) = + sqrt <| a.InnerProduct(b, (fun x y -> sqr (x - y)), LanguagePrimitives.GenericZero< ^T>, (+)) + + for _ = 1 to iterations do + let a = getVector tensor + let b = getVectorOfSize tensor a.Size + a.Dist2(b) |> should equalApprox (ref a b) + + [] + let ``[Vector] Dist2 squared`` (tensor: Tensor) = + let inline ref (a: Vector< ^T>) (b: Vector< ^T>) = + a.InnerProduct(b, (fun x y -> sqr (x - y)), LanguagePrimitives.GenericZero< ^T>, (+)) + + for _ = 1 to iterations do + let a = getVector tensor + let b = getVectorOfSize tensor a.Size + a.Dist2Squared(b) |> should equalApprox (ref a b) + + [] + let ``[Vector] DistMax`` (tensor: Tensor) = + let inline ref (a: Vector< ^T>) (b: Vector< ^T>) = + a.InnerProduct(b, (fun x y -> abs (x - y)), LanguagePrimitives.GenericZero< ^T>, max) + + for _ = 1 to iterations do + let a = getVector tensor + let b = getVectorOfSize tensor a.Size + a.DistMax(b) |> should equalApprox (ref a b) + + [] + let ``[Vector] Multiply`` (tensor: Tensor) = + let inline ref (a: Vector< ^T>) (b: Vector< ^T>) = + let a = Array.ofSeq a.Elements + let b = Array.ofSeq b.Elements + (a, b) ||> Array.map2 (*) |> Vector.Create + + for _ = 1 to iterations do + let a = getVector tensor + let b = getVectorOfSize tensor a.Size + Vector.approxEqual (a.Multiply(b)) (ref a b) + + [] + let ``[Vector] Multiply transposed`` (tensor: Tensor) = + let inline ref (a: Vector< ^T>) (b: Vector< ^T>) = + let a = Array.ofSeq a.Elements + let b = Array.ofSeq b.Elements + let r = Array.zeroCreate (a.Length * b.Length) + + let mutable i = 0 + for y = 0 to a.Length - 1 do + for x = 0 to b.Length - 1 do + r.[i] <- a.[y] * b.[x] + inc &i + + Matrix.Create(r, b.Length, a.Length) + + for _ = 1 to iterations do + let a = getVector tensor + let b = getVectorOfSize tensor a.Size + Matrix.approxEqual (a.MultiplyTransposed(b)) (ref a b) + + [] + let ``[Vector] Multiply scalar`` (tensor: Tensor) = + let inline ref (a: Vector< ^T>) (b: ^T) = + a.Elements |> Array.ofSeq |> Array.map ((*) b) |> Vector.Create + + for _ = 1 to iterations do + let a = getVector tensor + let b = rnd.UniformDouble() * 100.0 + Vector.approxEqual (a.Multiply(b)) (ref a b) + + [] + let ``[Vector] Subtract`` (tensor: Tensor) = + let inline ref (a: Vector< ^T>) (b: Vector< ^T>) = + let a = Array.ofSeq a.Elements + let b = Array.ofSeq b.Elements + (a, b) ||> Array.map2 (-) |> Vector.Create + + for _ = 1 to iterations do + let a = getVector tensor + let b = getVectorOfSize tensor a.Size + Vector.approxEqual (a.Subtract(b)) (ref a b) + + [] + let ``[Vector] Subtract scalar`` (tensor: Tensor) = + let inline ref (a: Vector< ^T>) (b: ^T) = + a.Elements |> Array.ofSeq |> Array.map (fun x -> x - b) |> Vector.Create + + for _ = 1 to iterations do + let a = getVector tensor + let b = rnd.UniformDouble() * 100.0 + Vector.approxEqual (a.Subtract(b)) (ref a b) + + [] + let ``[Vector] Add`` (tensor: Tensor) = + let inline ref (a: Vector< ^T>) (b: Vector< ^T>) = + let a = Array.ofSeq a.Elements + let b = Array.ofSeq b.Elements + (a, b) ||> Array.map2 (+) |> Vector.Create + + for _ = 1 to iterations do + let a = getVector tensor + let b = getVectorOfSize tensor a.Size + Vector.approxEqual (a.Add(b)) (ref a b) + + [] + let ``[Vector] Add scalar`` (tensor: Tensor) = + let inline ref (a: Vector< ^T>) (b: ^T) = + a.Elements |> Array.ofSeq |> Array.map ((+) b) |> Vector.Create + + for _ = 1 to iterations do + let a = getVector tensor + let b = rnd.UniformDouble() * 100.0 + Vector.approxEqual (a.Add(b)) (ref a b) + + [] + let ``[Matrix] Transform`` (tensor: Tensor) = + let inline ref (a: Matrix< ^T>) (b: Vector< ^T>) = + if a.SX <> b.S then + invalidArg "size" "Size mismatch" + + Array.init (int a.SY) (fun i -> + Vector.Ref.dot (a.Row (int64 i)) b + ) + |> Vector.Create + + for _ = 1 to iterations do + let a = getMatrix tensor + let b = getVectorOfSize tensor a.SX + Vector.approxEqual (a.Multiply(b)) (ref a b) + + [] + let ``[Matrix] Multiply`` (tensor: Tensor) = + let inline ref (a: Matrix< ^T>) (b: Matrix< ^T>) = + if a.SX <> b.SY then + invalidArg "size" "Size mismatch" + + let r = Array.zeroCreate (int (b.SX * a.SY)) + for y = 0 to int a.SY - 1 do + for x = 0 to int b.SX - 1 do + r.[y * int b.SX + x] <- Vector.Ref.dot (a.Row(int64 y)) (b.Col(int64 x)) + + Matrix(r, V2l(b.SX, a.SY)) + + for _ = 1 to iterations do + let a = getMatrix tensor + let b = getMatrixOfHeight tensor a.SX + Matrix.approxEqual (a.Multiply(b)) (ref a b) + + [] + let ``[Matrix] Subtract`` (tensor: Tensor) = + let inline ref (a: Matrix< ^T>) (b: Matrix< ^T>) = + if a.S <> b.S then + invalidArg "size" "Size mismatch" + + let r = (a.Elements, b.Elements) ||> Seq.map2 (-) |> Array.ofSeq + Matrix(r, a.S) + + for _ = 1 to iterations do + let a = getMatrix tensor + let b = getMatrixOfSize tensor a.S + Matrix.approxEqual (a.Subtract(b)) (ref a b) + + [] + let ``[Matrix] Subtract scalar`` (tensor: Tensor) = + let inline ref (a: Matrix< ^T>) (b: ^T) = + let r = a.Elements |> Seq.map (fun x -> x - b) |> Array.ofSeq + Matrix(r, a.S) + + for _ = 1 to iterations do + let a = getMatrix tensor + let b = rnd.UniformDouble() * 100.0 + Matrix.approxEqual (a.Subtract(b)) (ref a b) + + [] + let ``[Matrix] Add`` (tensor: Tensor) = + let inline ref (a: Matrix< ^T>) (b: Matrix< ^T>) = + if a.S <> b.S then + invalidArg "size" "Size mismatch" + + let r = (a.Elements, b.Elements) ||> Seq.map2 (+) |> Array.ofSeq + Matrix(r, a.S) + + for _ = 1 to iterations do + let a = getMatrix tensor + let b = getMatrixOfSize tensor a.S + Matrix.approxEqual (a.Add(b)) (ref a b) + + [] + let ``[Matrix] Add scalar`` (tensor: Tensor) = + let inline ref (a: Matrix< ^T>) (b: ^T) = + let r = a.Elements |> Seq.map ((+) b) |> Array.ofSeq + Matrix(r, a.S) + + for _ = 1 to iterations do + let a = getMatrix tensor + let b = rnd.UniformDouble() * 100.0 + Matrix.approxEqual (a.Add(b)) (ref a b) \ No newline at end of file