From 15f415e705e44a160186c3819d8a491c6a37cd9e Mon Sep 17 00:00:00 2001
From: kirillgarbar <st087492@student.spbu.ru>
Date: Tue, 24 Oct 2023 13:17:30 +0300
Subject: [PATCH] PageRank test property

---
 .../Backend/Algorithms/PageRank.fs            | 123 ++++++++++++------
 1 file changed, 86 insertions(+), 37 deletions(-)

diff --git a/tests/GraphBLAS-sharp.Tests/Backend/Algorithms/PageRank.fs b/tests/GraphBLAS-sharp.Tests/Backend/Algorithms/PageRank.fs
index 7e6c394f..b88c9383 100644
--- a/tests/GraphBLAS-sharp.Tests/Backend/Algorithms/PageRank.fs
+++ b/tests/GraphBLAS-sharp.Tests/Backend/Algorithms/PageRank.fs
@@ -2,15 +2,75 @@
 
 open Expecto
 open GraphBLAS.FSharp
-open GraphBLAS.FSharp.Backend.Quotes
 open GraphBLAS.FSharp.Tests
 open GraphBLAS.FSharp.Tests.Context
 open GraphBLAS.FSharp.Objects.ClVectorExtensions
 open GraphBLAS.FSharp.Objects
-open GraphBLAS.FSharp.Objects.MatrixExtensions
+
+let private alpha = 0.85f
+let private accuracy = 0.00001f
+
+let prepareNaive (matrix: float32 [,]) =
+    let result = Array2D.copy matrix
+    let rowCount = Array2D.length1 matrix
+    let outDegrees = Array.zeroCreate rowCount
+
+    //Count degree
+    Array2D.iteri (fun r c v -> outDegrees.[r] <- outDegrees.[r] + (if v <> 0f then 1f else 0f)) matrix
+
+    //Set value
+    Array2D.iteri
+        (fun r c v ->
+            result.[r, c] <-
+                if v <> 0f then
+                    alpha / outDegrees.[r]
+                else
+                    0f)
+        matrix
+
+    //Transpose
+    Array2D.iteri
+        (fun r c _ ->
+            if r > c then
+                let temp = result.[r, c]
+                result.[r, c] <- result.[c, r]
+                result.[c, r] <- temp)
+        matrix
+
+    result
+
+let pageRankNaive (matrix: float32 [,]) =
+    let rowCount = Array2D.length1 matrix
+    let mutable result = Array.zeroCreate rowCount
+
+    let mutable prev =
+        Array.create rowCount (1f / (float32 rowCount))
+
+    let mutable error = accuracy + 1f
+    let addConst = (1f - alpha) / (float32 rowCount)
+
+    while (error > accuracy) do
+        for r in 0 .. rowCount - 1 do
+            result [ r ] <- 0f
+
+            for c in 0 .. rowCount - 1 do
+                result.[r] <- result.[r] + matrix.[r, c] * prev.[c]
+
+            result.[r] <- result.[r] + addConst
+
+        error <-
+            sqrt
+            <| Array.fold2 (fun e x1 x2 -> e + (x1 - x2) * (x1 - x2)) 0f result prev
+
+        let temp = result
+        result <- prev
+        prev <- temp
+
+    prev
 
 let testFixtures (testContext: TestContext) =
-    [ let context = testContext.ClContext
+    [ let config = Utils.undirectedAlgoConfig
+      let context = testContext.ClContext
       let queue = testContext.Queue
       let workGroupSize = Utils.defaultWorkGroupSize
 
@@ -20,50 +80,39 @@ let testFixtures (testContext: TestContext) =
       let pageRank =
           Algorithms.PageRank.run context workGroupSize
 
-      testCase testName
-      <| fun () ->
-
-          let matrix = Array2D.zeroCreate 4 4
-
-          matrix.[0, 1] <- 1f
-          matrix.[0, 2] <- 1f
-          matrix.[0, 3] <- 1f
-          matrix.[1, 2] <- 1f
-          matrix.[1, 3] <- 1f
-          matrix.[2, 0] <- 1f
-          matrix.[3, 2] <- 1f
-          matrix.[3, 0] <- 1f
-
+      testPropertyWithConfig config testName
+      <| fun (matrix: float32 [,]) ->
           let matrixHost =
               Utils.createMatrixFromArray2D CSR matrix ((=) 0f)
 
-          let matrix = matrixHost.ToDevice context
+          if matrixHost.NNZ > 0 then
+              let preparedMatrixExpected = prepareNaive matrix
+
+              let expected = pageRankNaive preparedMatrixExpected
 
-          let preparedMatrix =
-              Algorithms.PageRank.prepareMatrix context workGroupSize queue matrix
+              let matrix = matrixHost.ToDevice context
 
-          let res = pageRank queue preparedMatrix
+              let preparedMatrix =
+                  Algorithms.PageRank.prepareMatrix context workGroupSize queue matrix
 
-          let resHost = res.ToHost queue
+              let res = pageRank queue preparedMatrix accuracy
 
-          preparedMatrix.Dispose queue
-          matrix.Dispose queue
-          res.Dispose queue
+              let resHost = res.ToHost queue
 
-          let expected =
-              [| 0.3681506515f
-                 0.1418093443f
-                 0.2879616022f
-                 0.2020783126f |]
+              preparedMatrix.Dispose queue
+              matrix.Dispose queue
+              res.Dispose queue
 
-          match resHost with
-          | Vector.Dense resHost ->
-              let actual = resHost |> Utils.unwrapOptionArray 0f
+              match resHost with
+              | Vector.Dense resHost ->
+                  let actual = resHost |> Utils.unwrapOptionArray 0f
 
-              for i in 0 .. actual.Length - 1 do
-                  Expect.isTrue (Utils.float32IsEqual actual.[i] expected.[i]) "Values should be equal"
+                  for i in 0 .. actual.Length - 1 do
+                      Expect.isTrue
+                          ((abs (actual.[i] - expected.[i])) < accuracy)
+                          (sprintf "Values should be equal. Expected %A, actual %A" expected.[i] actual.[i])
 
-          | _ -> failwith "Not implemented" ]
+              | _ -> failwith "Not implemented" ]
 
 let tests =
-    TestCases.gpuTests "Bfs tests" testFixtures
+    TestCases.gpuTests "PageRank tests" testFixtures