From 8ed6bb7f04fb72f93a20c8969a96290f325c4555 Mon Sep 17 00:00:00 2001 From: Daily Test Coverage Improver Date: Tue, 28 Oct 2025 03:24:57 +0000 Subject: [PATCH] Add comprehensive SIMD tests for Matrix vector operations - Added 23 comprehensive tests targeting SIMD paths in muliplyVector, addRowVector, and addColVector - Tests cover various matrix sizes (4, 5, 6, 8, 9, 12, 16, 20 columns) to trigger SIMD acceleration - Tests validate both SIMD chunks and scalar tail processing - Tests include both float and float32 types - Tests verify mathematical correctness across diverse scenarios These tests specifically target lines 496, 534, and 572 in Matrix.fs which are SIMD accumulation loops within inline functions. While coverage metrics won't reflect the improvement due to F# inline function limitations, these tests DO execute and validate the SIMD code paths, ensuring mathematical correctness and preventing regressions. All 1509 tests passing (up from 1486, +23 new tests). --- tests/FsMath.Tests/FsMath.Tests.fsproj | 1 + tests/FsMath.Tests/MatrixSIMDCoverageTests.fs | 307 ++++++++++++++++++ 2 files changed, 308 insertions(+) create mode 100644 tests/FsMath.Tests/MatrixSIMDCoverageTests.fs diff --git a/tests/FsMath.Tests/FsMath.Tests.fsproj b/tests/FsMath.Tests/FsMath.Tests.fsproj index 9ceaa30..d06b9d8 100644 --- a/tests/FsMath.Tests/FsMath.Tests.fsproj +++ b/tests/FsMath.Tests/FsMath.Tests.fsproj @@ -30,6 +30,7 @@ + diff --git a/tests/FsMath.Tests/MatrixSIMDCoverageTests.fs b/tests/FsMath.Tests/MatrixSIMDCoverageTests.fs new file mode 100644 index 0000000..f17de2d --- /dev/null +++ b/tests/FsMath.Tests/MatrixSIMDCoverageTests.fs @@ -0,0 +1,307 @@ +namespace FsMath.Tests.Matrix + +open Xunit +open FsMath + +/// +/// Comprehensive SIMD coverage tests for Matrix vector operations. +/// These tests specifically target SIMD code paths in muliplyVector, addRowVector, and addColVector +/// to improve coverage for lines 496, 534, and 572 in Matrix.fs. +/// +/// Key Requirements for SIMD Activation: +/// - Vector<float>.Count is typically 4 on most systems +/// - Matrix columns must be >= 4 for SIMD to activate +/// - Tests include both SIMD chunks and scalar tail processing +/// +module MatrixSIMDCoverageTests = + + // ========================================================================= + // Tests for Matrix.muliplyVector (line 496: SIMD accumulation loop) + // ========================================================================= + + [] + let ``muliplyVector: 2x4 matrix with float - triggers SIMD path`` () = + // 4 columns = exactly 1 SIMD chunk (Vector.Count = 4) + let mat = Matrix.create 2 4 [|1.0; 2.0; 3.0; 4.0; + 5.0; 6.0; 7.0; 8.0|] + let vec = [|1.0; 2.0; 3.0; 4.0|] + let result = Matrix.muliplyVector mat vec + + // Expected: [1*1 + 2*2 + 3*3 + 4*4; 5*1 + 6*2 + 7*3 + 8*4] + // = [30.0; 70.0] + Assert.Equal(30.0, result.[0]) + Assert.Equal(70.0, result.[1]) + + [] + let ``muliplyVector: 3x8 matrix with float - multiple SIMD chunks`` () = + // 8 columns = 2 SIMD chunks + let mat = Matrix.create 3 8 [|1.0; 1.0; 1.0; 1.0; 2.0; 2.0; 2.0; 2.0; + 3.0; 3.0; 3.0; 3.0; 4.0; 4.0; 4.0; 4.0; + 5.0; 5.0; 5.0; 5.0; 6.0; 6.0; 6.0; 6.0|] + let vec = [|1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0|] + let result = Matrix.muliplyVector mat vec + + // Row 0: 1*(1+2+3+4) + 2*(5+6+7+8) = 10 + 52 = 62 + // Row 1: 3*(1+2+3+4) + 4*(5+6+7+8) = 30 + 104 = 134 + // Row 2: 5*(1+2+3+4) + 6*(5+6+7+8) = 50 + 156 = 206 + Assert.Equal(62.0, result.[0]) + Assert.Equal(134.0, result.[1]) + Assert.Equal(206.0, result.[2]) + + [] + let ``muliplyVector: 2x5 matrix with float - SIMD + scalar tail`` () = + // 5 columns = 1 SIMD chunk (4 elements) + 1 scalar tail + let mat = Matrix.create 2 5 [|1.0; 2.0; 3.0; 4.0; 5.0; + 6.0; 7.0; 8.0; 9.0; 10.0|] + let vec = [|1.0; 1.0; 1.0; 1.0; 1.0|] + let result = Matrix.muliplyVector mat vec + + // Row 0: 1+2+3+4+5 = 15 + // Row 1: 6+7+8+9+10 = 40 + Assert.Equal(15.0, result.[0]) + Assert.Equal(40.0, result.[1]) + + [] + let ``muliplyVector: 4x12 matrix with float - extensive SIMD processing`` () = + // 12 columns = 3 SIMD chunks + let data = Array.init (4 * 12) (fun i -> float (i + 1)) + let mat = Matrix.create 4 12 data + let vec = Array.create 12 1.0 + let result = Matrix.muliplyVector mat vec + + // Each row is the sum of 12 consecutive numbers + Assert.Equal(78.0, result.[0]) // sum(1..12) + Assert.Equal(222.0, result.[1]) // sum(13..24) + Assert.Equal(366.0, result.[2]) // sum(25..36) + Assert.Equal(510.0, result.[3]) // sum(37..48) + + [] + let ``muliplyVector: 2x4 with float32 - SIMD with different type`` () = + // Vector.Count is typically 8, so 4 columns won't trigger SIMD + // But this tests the function with float32 type + let mat = Matrix.create 2 4 [|1.0f; 2.0f; 3.0f; 4.0f; + 5.0f; 6.0f; 7.0f; 8.0f|] + let vec = [|1.0f; 2.0f; 3.0f; 4.0f|] + let result = Matrix.muliplyVector mat vec + + Assert.Equal(30.0f, result.[0]) + Assert.Equal(70.0f, result.[1]) + + [] + let ``muliplyVector: 3x16 matrix with float32 - multiple SIMD chunks float32`` () = + // Vector.Count is typically 8, so 16 columns = 2 SIMD chunks + let data = Array.init (3 * 16) (fun i -> float32 (i % 4 + 1)) + let mat = Matrix.create 3 16 data + let vec = Array.create 16 1.0f + let result = Matrix.muliplyVector mat vec + + // Each row: 4 cycles of [1,2,3,4] = 4*(1+2+3+4) = 40 + Assert.Equal(40.0f, result.[0]) + Assert.Equal(40.0f, result.[1]) + Assert.Equal(40.0f, result.[2]) + + [] + let ``muliplyVector: identity matrix - SIMD path validation`` () = + let n = 8 + let mat = Matrix.identity n + let vec = [|1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0|] + let result = Matrix.muliplyVector mat vec + + // Identity matrix should return the same vector + Assert.Equal>(vec, result) + + [] + let ``muliplyVector: 4x6 zero matrix with SIMD`` () = + let mat = Matrix.zeroCreate 4 6 + let vec = [|1.0; 2.0; 3.0; 4.0; 5.0; 6.0|] + let result = Matrix.muliplyVector mat vec + + Assert.All(result, fun x -> Assert.Equal(0.0, x)) + + // ========================================================================= + // Tests for Matrix.addRowVector (line 534: SIMD addition loop) + // ========================================================================= + + [] + let ``addRowVector: 2x4 matrix - triggers SIMD path`` () = + // 4 columns = exactly 1 SIMD chunk + let mat = Matrix.create 2 4 [|1.0; 2.0; 3.0; 4.0; + 5.0; 6.0; 7.0; 8.0|] + let vec = [|10.0; 20.0; 30.0; 40.0|] + let result = Matrix.addRowVector mat vec + + let expected = [|11.0; 22.0; 33.0; 44.0; 15.0; 26.0; 37.0; 48.0|] + Assert.Equal>(expected, result.Data) + + [] + let ``addRowVector: 3x8 matrix - multiple SIMD chunks`` () = + // 8 columns = 2 SIMD chunks + let mat = Matrix.create 3 8 (Array.init 24 (fun i -> float (i + 1))) + let vec = Array.create 8 100.0 + let result = Matrix.addRowVector mat vec + + // Each element should be increased by 100 + for i = 0 to 23 do + Assert.Equal(float (i + 1) + 100.0, result.Data.[i]) + + [] + let ``addRowVector: 2x5 matrix - SIMD + scalar tail`` () = + // 5 columns = 1 SIMD chunk + 1 scalar tail + let mat = Matrix.create 2 5 [|1.0; 2.0; 3.0; 4.0; 5.0; + 6.0; 7.0; 8.0; 9.0; 10.0|] + let vec = [|1.0; 1.0; 1.0; 1.0; 1.0|] + let result = Matrix.addRowVector mat vec + + let expected = [|2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; 9.0; 10.0; 11.0|] + Assert.Equal>(expected, result.Data) + + [] + let ``addRowVector: 4x12 matrix - extensive SIMD processing`` () = + // 12 columns = 3 SIMD chunks + let mat = Matrix.ones 4 12 + let vec = Array.init 12 (fun i -> float i) + let result = Matrix.addRowVector mat vec + + // Each row should have [1+0, 1+1, 1+2, ..., 1+11] + for row = 0 to 3 do + for col = 0 to 11 do + Assert.Equal(1.0 + float col, result.[row, col]) + + [] + let ``addRowVector: 2x16 with float32 - SIMD with float32 type`` () = + // Vector.Count is typically 8, so 16 columns = 2 SIMD chunks + let mat = Matrix.create 2 16 (Array.init 32 (fun i -> float32 i)) + let vec = Array.create 16 10.0f + let result = Matrix.addRowVector mat vec + + for i = 0 to 31 do + Assert.Equal(float32 i + 10.0f, result.Data.[i]) + + [] + let ``addRowVector: 3x6 with negative values`` () = + let mat = Matrix.create 3 6 [|1.0; 2.0; 3.0; 4.0; 5.0; 6.0; + 7.0; 8.0; 9.0; 10.0; 11.0; 12.0; + 13.0; 14.0; 15.0; 16.0; 17.0; 18.0|] + let vec = [|-1.0; -2.0; -3.0; -4.0; -5.0; -6.0|] + let result = Matrix.addRowVector mat vec + + let expected = [|0.0; 0.0; 0.0; 0.0; 0.0; 0.0; + 6.0; 6.0; 6.0; 6.0; 6.0; 6.0; + 12.0; 12.0; 12.0; 12.0; 12.0; 12.0|] + Assert.Equal>(expected, result.Data) + + [] + let ``addRowVector: 5x9 large matrix - mixed SIMD and tail`` () = + // 9 columns = 2 SIMD chunks + 1 scalar tail (assuming Vector.Count = 4) + let mat = Matrix.zeroCreate 5 9 + let vec = [|1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; 9.0|] + let result = Matrix.addRowVector mat vec + + // Each row should equal vec + for row = 0 to 4 do + for col = 0 to 8 do + Assert.Equal(vec.[col], result.[row, col]) + + // ========================================================================= + // Tests for Matrix.addColVector (line 572: SIMD addition with broadcast) + // ========================================================================= + + [] + let ``addColVector: 2x4 matrix - triggers SIMD path`` () = + // 4 columns = exactly 1 SIMD chunk + let mat = Matrix.create 2 4 [|1.0; 2.0; 3.0; 4.0; + 5.0; 6.0; 7.0; 8.0|] + let vec = [|100.0; 200.0|] + let result = Matrix.addColVector mat vec + + let expected = [|101.0; 102.0; 103.0; 104.0; 205.0; 206.0; 207.0; 208.0|] + Assert.Equal>(expected, result.Data) + + [] + let ``addColVector: 3x8 matrix - multiple SIMD chunks`` () = + // 8 columns = 2 SIMD chunks + let mat = Matrix.create 3 8 (Array.init 24 (fun i -> float (i + 1))) + let vec = [|10.0; 20.0; 30.0|] + let result = Matrix.addColVector mat vec + + // Row 0: add 10 to first 8 elements + for i = 0 to 7 do + Assert.Equal(float (i + 1) + 10.0, result.Data.[i]) + // Row 1: add 20 to next 8 elements + for i = 8 to 15 do + Assert.Equal(float (i + 1) + 20.0, result.Data.[i]) + // Row 2: add 30 to last 8 elements + for i = 16 to 23 do + Assert.Equal(float (i + 1) + 30.0, result.Data.[i]) + + [] + let ``addColVector: 2x5 matrix - SIMD + scalar tail`` () = + // 5 columns = 1 SIMD chunk + 1 scalar tail + let mat = Matrix.create 2 5 [|1.0; 2.0; 3.0; 4.0; 5.0; + 6.0; 7.0; 8.0; 9.0; 10.0|] + let vec = [|10.0; 20.0|] + let result = Matrix.addColVector mat vec + + let expected = [|11.0; 12.0; 13.0; 14.0; 15.0; 26.0; 27.0; 28.0; 29.0; 30.0|] + Assert.Equal>(expected, result.Data) + + [] + let ``addColVector: 4x12 matrix - extensive SIMD processing`` () = + // 12 columns = 3 SIMD chunks + let mat = Matrix.ones 4 12 + let vec = [|0.0; 10.0; 20.0; 30.0|] + let result = Matrix.addColVector mat vec + + // Row 0: all 1.0, Row 1: all 11.0, Row 2: all 21.0, Row 3: all 31.0 + for col = 0 to 11 do + Assert.Equal(1.0, result.[0, col]) + Assert.Equal(11.0, result.[1, col]) + Assert.Equal(21.0, result.[2, col]) + Assert.Equal(31.0, result.[3, col]) + + [] + let ``addColVector: 3x16 with float32 - SIMD with float32 type`` () = + // Vector.Count is typically 8, so 16 columns = 2 SIMD chunks + let mat = Matrix.zeroCreate 3 16 + let vec = [|1.0f; 2.0f; 3.0f|] + let result = Matrix.addColVector mat vec + + for col = 0 to 15 do + Assert.Equal(1.0f, result.[0, col]) + Assert.Equal(2.0f, result.[1, col]) + Assert.Equal(3.0f, result.[2, col]) + + [] + let ``addColVector: 2x6 with negative column vector`` () = + let mat = Matrix.create 2 6 [|10.0; 20.0; 30.0; 40.0; 50.0; 60.0; + 70.0; 80.0; 90.0; 100.0; 110.0; 120.0|] + let vec = [|-5.0; -15.0|] + let result = Matrix.addColVector mat vec + + let expected = [|5.0; 15.0; 25.0; 35.0; 45.0; 55.0; + 55.0; 65.0; 75.0; 85.0; 95.0; 105.0|] + Assert.Equal>(expected, result.Data) + + [] + let ``addColVector: 5x9 large matrix - mixed SIMD and tail`` () = + // 9 columns = 2 SIMD chunks + 1 scalar tail (assuming Vector.Count = 4) + let mat = Matrix.zeroCreate 5 9 + let vec = [|1.0; 2.0; 3.0; 4.0; 5.0|] + let result = Matrix.addColVector mat vec + + for row = 0 to 4 do + for col = 0 to 8 do + Assert.Equal(vec.[row], result.[row, col]) + + [] + let ``addColVector: 6x20 very large matrix - stress test SIMD`` () = + // 20 columns = 5 SIMD chunks (assuming Vector.Count = 4) + let mat = Matrix.create 6 20 (Array.init 120 (fun i -> float i)) + let vec = [|100.0; 200.0; 300.0; 400.0; 500.0; 600.0|] + let result = Matrix.addColVector mat vec + + for row = 0 to 5 do + let expectedAddition = vec.[row] + for col = 0 to 19 do + let originalValue = float (row * 20 + col) + Assert.Equal(originalValue + expectedAddition, result.[row, col])