|
| 1 | +module GraphBLAS.FSharp.Tests.Backend.Algorithms.PageRank |
| 2 | + |
| 3 | +open Expecto |
| 4 | +open GraphBLAS.FSharp |
| 5 | +open GraphBLAS.FSharp.Tests |
| 6 | +open GraphBLAS.FSharp.Tests.Context |
| 7 | +open GraphBLAS.FSharp.Objects.ClVectorExtensions |
| 8 | +open GraphBLAS.FSharp.Objects |
| 9 | + |
| 10 | +let private alpha = 0.85f |
| 11 | +let private accuracy = 0.00001f |
| 12 | + |
| 13 | +let prepareNaive (matrix: float32 [,]) = |
| 14 | + let result = Array2D.copy matrix |
| 15 | + let rowCount = Array2D.length1 matrix |
| 16 | + let outDegrees = Array.zeroCreate rowCount |
| 17 | + |
| 18 | + //Count degree |
| 19 | + Array2D.iteri (fun r c v -> outDegrees.[r] <- outDegrees.[r] + (if v <> 0f then 1f else 0f)) matrix |
| 20 | + |
| 21 | + //Set value |
| 22 | + Array2D.iteri |
| 23 | + (fun r c v -> |
| 24 | + result.[r, c] <- |
| 25 | + if v <> 0f then |
| 26 | + alpha / outDegrees.[r] |
| 27 | + else |
| 28 | + 0f) |
| 29 | + matrix |
| 30 | + |
| 31 | + //Transpose |
| 32 | + Array2D.iteri |
| 33 | + (fun r c _ -> |
| 34 | + if r > c then |
| 35 | + let temp = result.[r, c] |
| 36 | + result.[r, c] <- result.[c, r] |
| 37 | + result.[c, r] <- temp) |
| 38 | + matrix |
| 39 | + |
| 40 | + result |
| 41 | + |
| 42 | +let pageRankNaive (matrix: float32 [,]) = |
| 43 | + let rowCount = Array2D.length1 matrix |
| 44 | + let mutable result = Array.zeroCreate rowCount |
| 45 | + |
| 46 | + let mutable prev = |
| 47 | + Array.create rowCount (1f / (float32 rowCount)) |
| 48 | + |
| 49 | + let mutable error = accuracy + 1f |
| 50 | + let addConst = (1f - alpha) / (float32 rowCount) |
| 51 | + |
| 52 | + while (error > accuracy) do |
| 53 | + for r in 0 .. rowCount - 1 do |
| 54 | + result.[r] <- 0f |
| 55 | + |
| 56 | + for c in 0 .. rowCount - 1 do |
| 57 | + result.[r] <- result.[r] + matrix.[r, c] * prev.[c] |
| 58 | + |
| 59 | + result.[r] <- result.[r] + addConst |
| 60 | + |
| 61 | + error <- |
| 62 | + sqrt |
| 63 | + <| Array.fold2 (fun e x1 x2 -> e + (x1 - x2) * (x1 - x2)) 0f result prev |
| 64 | + |
| 65 | + let temp = result |
| 66 | + result <- prev |
| 67 | + prev <- temp |
| 68 | + |
| 69 | + prev |
| 70 | + |
| 71 | +let testFixtures (testContext: TestContext) = |
| 72 | + [ let config = Utils.undirectedAlgoConfig |
| 73 | + let context = testContext.ClContext |
| 74 | + let queue = testContext.Queue |
| 75 | + let workGroupSize = Utils.defaultWorkGroupSize |
| 76 | + |
| 77 | + let testName = |
| 78 | + sprintf "Test on %A" testContext.ClContext |
| 79 | + |
| 80 | + let pageRank = |
| 81 | + Algorithms.PageRank.run context workGroupSize |
| 82 | + |
| 83 | + testPropertyWithConfig config testName |
| 84 | + <| fun (matrix: float32 [,]) -> |
| 85 | + let matrixHost = |
| 86 | + Utils.createMatrixFromArray2D CSR matrix ((=) 0f) |
| 87 | + |
| 88 | + if matrixHost.NNZ > 0 then |
| 89 | + let preparedMatrixExpected = prepareNaive matrix |
| 90 | + |
| 91 | + let expected = pageRankNaive preparedMatrixExpected |
| 92 | + |
| 93 | + let matrix = matrixHost.ToDevice context |
| 94 | + |
| 95 | + let preparedMatrix = |
| 96 | + Algorithms.PageRank.prepareMatrix context workGroupSize queue matrix |
| 97 | + |
| 98 | + let res = pageRank queue preparedMatrix accuracy |
| 99 | + |
| 100 | + let resHost = res.ToHost queue |
| 101 | + |
| 102 | + preparedMatrix.Dispose queue |
| 103 | + matrix.Dispose queue |
| 104 | + res.Dispose queue |
| 105 | + |
| 106 | + match resHost with |
| 107 | + | Vector.Dense resHost -> |
| 108 | + let actual = resHost |> Utils.unwrapOptionArray 0f |
| 109 | + |
| 110 | + for i in 0 .. actual.Length - 1 do |
| 111 | + Expect.isTrue |
| 112 | + ((abs (actual.[i] - expected.[i])) < accuracy) |
| 113 | + (sprintf "Values should be equal. Expected %A, actual %A" expected.[i] actual.[i]) |
| 114 | + |
| 115 | + | _ -> failwith "Not implemented" ] |
| 116 | + |
| 117 | +let tests = |
| 118 | + TestCases.gpuTests "PageRank tests" testFixtures |
0 commit comments