diff --git a/QuadTree.Tests/QuadTree.Tests.fsproj b/QuadTree.Tests/QuadTree.Tests.fsproj index 8697b9a..dd3c1ad 100644 --- a/QuadTree.Tests/QuadTree.Tests.fsproj +++ b/QuadTree.Tests/QuadTree.Tests.fsproj @@ -11,6 +11,7 @@ + diff --git a/QuadTree.Tests/Tests.LinearAlgebra.fs b/QuadTree.Tests/Tests.LinearAlgebra.fs index e285644..8e3dd45 100644 --- a/QuadTree.Tests/Tests.LinearAlgebra.fs +++ b/QuadTree.Tests/Tests.LinearAlgebra.fs @@ -17,59 +17,51 @@ N,N,3,N = 6,6,14,10 *) + +let op_add x y = + match (x, y) with + | Some(a), Some(b) -> Some(a + b) + | Some(a), _ + | _, Some(a) -> Some(a) + | _ -> None + +let op_mult x y = + match (x, y) with + | Some(a), Some(b) -> Some(a * b) + | _ -> None + +let leaf_v v = qtree.Leaf << UserValue <| Some v +let leaf_n () = qtree.Leaf << UserValue <| None +let leaf_d () = qtree.Leaf Dummy + +let vleaf_v v = + Vector.btree.Leaf << UserValue <| Some v + +let vleaf_n () = Vector.btree.Leaf << UserValue <| None +let vleaf_d () = Vector.btree.Leaf Dummy + [] let ``Simple vxm. All sizes are power of two.`` () = let m = let tree = Matrix.qtree.Node( - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(UserValue(Some(1))), - Matrix.qtree.Leaf(UserValue(Some(3))), - Matrix.qtree.Leaf(UserValue(Some(2))) - ), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(Some(1))), - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(UserValue(Some(2))), - Matrix.qtree.Leaf(UserValue(Some(3))) - ), - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(Some(1))), - Matrix.qtree.Leaf(UserValue(Some(2))), - Matrix.qtree.Leaf(UserValue(Some(3))), - Matrix.qtree.Leaf(UserValue(None)) - ) + Matrix.qtree.Node(leaf_n (), leaf_v 1, leaf_v 3, leaf_v 2), + Matrix.qtree.Node(leaf_v 1, leaf_n (), leaf_v 2, leaf_v 3), + leaf_n (), + Matrix.qtree.Node(leaf_v 1, leaf_v 2, leaf_v 3, leaf_n ()) ) let store = Matrix.Storage(4UL, tree) SparseMatrix(4UL, 4UL, 9UL, store) let v = - let tree = Vector.btree.Leaf(UserValue(Some(2))) + let tree = vleaf_v 2 let store = Vector.Storage(4UL, tree) SparseVector(4UL, 4UL, store) - let op_add x y = - match (x, y) with - | Some(a), Some(b) -> Some(a + b) - | Some(a), _ - | _, Some(a) -> Some(a) - | _ -> None - - let op_mult x y = - match (x, y) with - | Some(a), Some(b) -> Some(a * b) - | _ -> None - let expected = - let tree = - Vector.btree.Node( - Vector.btree.Leaf(UserValue(Some(6))), - Vector.btree.Node(Vector.btree.Leaf(UserValue(Some(14))), Vector.btree.Leaf(UserValue(Some(10)))) - ) + let tree = Vector.btree.Node(vleaf_v 6, Vector.btree.Node(vleaf_v 14, vleaf_v 10)) let store = Vector.Storage(4UL, tree) Result.Success(SparseVector(4UL, 4UL, store)) @@ -93,63 +85,23 @@ let ``Simple vxm. 3 * (3x4)`` () = let m = let tree = Matrix.qtree.Node( - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(UserValue(Some(1))), - Matrix.qtree.Leaf(UserValue(Some(3))), - Matrix.qtree.Leaf(UserValue(Some(2))) - ), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(Some(1))), - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(UserValue(Some(2))), - Matrix.qtree.Leaf(UserValue(Some(3))) - ), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(Dummy) - ), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(Some(1))), - Matrix.qtree.Leaf(UserValue(Some(2))), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(Dummy) - ) + Matrix.qtree.Node(leaf_n (), leaf_v 1, leaf_v 3, leaf_v 2), + Matrix.qtree.Node(leaf_v 1, leaf_n (), leaf_v 2, leaf_v 3), + Matrix.qtree.Node(leaf_n (), leaf_n (), leaf_d (), leaf_d ()), + Matrix.qtree.Node(leaf_v 1, leaf_v 2, leaf_d (), leaf_d ()) ) let store = Matrix.Storage(4UL, tree) SparseMatrix(3UL, 4UL, 8UL, store) let v = - let tree = - Vector.btree.Node( - Vector.btree.Leaf(UserValue(Some(2))), - Vector.btree.Node(Vector.btree.Leaf(UserValue(Some(2))), Vector.btree.Leaf(Dummy)) - ) + let tree = Vector.btree.Node(vleaf_v 2, Vector.btree.Node(vleaf_v 2, vleaf_d ())) let store = Vector.Storage(4UL, tree) SparseVector(3UL, 3UL, store) - let op_add x y = - match (x, y) with - | Some(a), Some(b) -> Some(a + b) - | Some(a), _ - | _, Some(a) -> Some(a) - | _ -> None - - let op_mult x y = - match (x, y) with - | Some(a), Some(b) -> Some(a * b) - | _ -> None - let expected = - let tree = - Vector.btree.Node( - Vector.btree.Leaf(UserValue(Some(6))), - Vector.btree.Node(Vector.btree.Leaf(UserValue(Some(8))), Vector.btree.Leaf(UserValue(Some(10)))) - ) + let tree = Vector.btree.Node(vleaf_v 6, Vector.btree.Node(vleaf_v 8, vleaf_v 10)) let store = Vector.Storage(4UL, tree) Result.Success(SparseVector(4UL, 4UL, store)) @@ -174,54 +126,24 @@ let ``Simple vxm. 4 * (4x3).`` () = let m = let tree = Matrix.qtree.Node( - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(UserValue(Some(1))), - Matrix.qtree.Leaf(UserValue(Some(3))), - Matrix.qtree.Leaf(UserValue(Some(2))) - ), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(Some(1))), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(UserValue(Some(2))), - Matrix.qtree.Leaf(Dummy) - ), - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(Some(1))), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(UserValue(Some(3))), - Matrix.qtree.Leaf(Dummy) - ) + Matrix.qtree.Node(leaf_n (), leaf_v 1, leaf_v 3, leaf_v 2), + Matrix.qtree.Node(leaf_v 1, leaf_d (), leaf_v 2, leaf_d ()), + leaf_n (), + Matrix.qtree.Node(leaf_v 1, leaf_d (), leaf_v 3, leaf_d ()) ) let store = Matrix.Storage(4UL, tree) SparseMatrix(4UL, 3UL, 7UL, store) let v = - let tree = Vector.btree.Leaf(UserValue(Some(2))) + let tree = vleaf_v 2 let store = Vector.Storage(4UL, tree) SparseVector(4UL, 4UL, store) - let op_add x y = - match (x, y) with - | Some(a), Some(b) -> Some(a + b) - | Some(a), _ - | _, Some(a) -> Some(a) - | _ -> None - - let op_mult x y = - match (x, y) with - | Some(a), Some(b) -> Some(a * b) - | _ -> None let expected = - let tree = - Vector.btree.Node( - Vector.btree.Leaf(UserValue(Some(6))), - Vector.btree.Node(Vector.btree.Leaf(UserValue(Some(14))), Vector.btree.Leaf(Dummy)) - ) + let tree = Vector.btree.Node(vleaf_v 6, Vector.btree.Node(vleaf_v 14, vleaf_d ())) let store = Vector.Storage(4UL, tree) Result.Success(SparseVector(3UL, 3UL, store)) @@ -251,87 +173,35 @@ let ``Simple vxm. 3 * (3x5)`` () = let tree = Matrix.qtree.Node( Matrix.qtree.Node( - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(UserValue(Some(1))), - Matrix.qtree.Leaf(UserValue(Some(3))), - Matrix.qtree.Leaf(UserValue(Some(2))) - ), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(Some(1))), - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(UserValue(Some(2))), - Matrix.qtree.Leaf(UserValue(Some(3))) - ), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(Dummy) - ), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(Some(1))), - Matrix.qtree.Leaf(UserValue(Some(2))), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(Dummy) - ) + Matrix.qtree.Node(leaf_n (), leaf_v 1, leaf_v 3, leaf_v 2), + Matrix.qtree.Node(leaf_v 1, leaf_n (), leaf_v 2, leaf_v 3), + Matrix.qtree.Node(leaf_n (), leaf_n (), leaf_d (), leaf_d ()), + Matrix.qtree.Node(leaf_v 1, leaf_v 2, leaf_d (), leaf_d ()) ), Matrix.qtree.Node( - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(UserValue(Some(1))), - Matrix.qtree.Leaf(Dummy) - ), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(Dummy) - ), - Matrix.qtree.Leaf(Dummy) + Matrix.qtree.Node(leaf_n (), leaf_d (), leaf_v 1, leaf_d ()), + leaf_d (), + Matrix.qtree.Node(leaf_n (), leaf_d (), leaf_d (), leaf_d ()), + leaf_d () ), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(Dummy) + leaf_d (), + leaf_d () ) let store = Matrix.Storage(8UL, tree) SparseMatrix(3UL, 5UL, 9UL, store) let v = - let tree = - Vector.btree.Node( - Vector.btree.Leaf(UserValue(Some(2))), - Vector.btree.Node(Vector.btree.Leaf(UserValue(Some(2))), Vector.btree.Leaf(Dummy)) - ) + let tree = Vector.btree.Node(vleaf_v 2, Vector.btree.Node(vleaf_v 2, vleaf_d ())) let store = Vector.Storage(4UL, tree) SparseVector(3UL, 3UL, store) - let op_add x y = - match (x, y) with - | Some(a), Some(b) -> Some(a + b) - | Some(a), _ - | _, Some(a) -> Some(a) - | _ -> None - - let op_mult x y = - match (x, y) with - | Some(a), Some(b) -> Some(a * b) - | _ -> None - let expected = let tree = Vector.btree.Node( - Vector.btree.Node( - Vector.btree.Leaf(UserValue(Some(6))), - Vector.btree.Node(Vector.btree.Leaf(UserValue(Some(8))), Vector.btree.Leaf(UserValue(Some(10)))) - ), - Vector.btree.Node( - Vector.btree.Node(Vector.btree.Leaf(UserValue(Some(2))), Vector.btree.Leaf(Dummy)), - Vector.btree.Leaf(Dummy) - ) + Vector.btree.Node(vleaf_v 6, Vector.btree.Node(vleaf_v 8, vleaf_v 10)), + Vector.btree.Node(Vector.btree.Node(vleaf_v 2, vleaf_d ()), vleaf_d ()) ) let store = Vector.Storage(8UL, tree) @@ -340,3 +210,171 @@ let ``Simple vxm. 3 * (3x5)`` () = let actual = LinearAlgebra.vxm op_add op_mult v m Assert.Equal(expected, actual) + +[] +let ``Simple mxm`` () = + // 222D + // 222D + // 222D + // DDDD + let tree = + qtree.Node( + leaf_v 2, + qtree.Node(leaf_v 2, leaf_d (), leaf_v 2, leaf_d ()), + qtree.Node(leaf_v 2, leaf_v 2, leaf_d (), leaf_d ()), + qtree.Node(leaf_v 2, leaf_d (), leaf_d (), leaf_d ()) + ) + + let tree_expected = + qtree.Node( + leaf_v 12, + qtree.Node(leaf_v 12, leaf_d (), leaf_v 12, leaf_d ()), + qtree.Node(leaf_v 12, leaf_v 12, leaf_d (), leaf_d ()), + qtree.Node(leaf_v 12, leaf_d (), leaf_d (), leaf_d ()) + ) + + let m1 = + SparseMatrix(3UL, 3UL, 9UL, Matrix.Storage(4UL, tree)) + + let m2 = m1 + + let expected = + SparseMatrix(3UL, 3UL, 9UL, Matrix.Storage(4UL, tree_expected)) + + let actual = + match LinearAlgebra.mxm op_add op_mult m1 m2 with + | Result.Success m -> m + | _ -> failwith "Unreachable" + + Assert.Equal(expected.storage.data, actual.storage.data) + +[] +let ``Sparse mxm`` () = + let m1 = + let d = + [ 0UL, 0UL, 1 + 1UL, 1UL, 2 + 2UL, 2UL, 3 ] + + let clist = Matrix.CoordinateList(3UL, 3UL, d) + Matrix.fromCoordinateList clist + + let m2 = + let d = + [ 0UL, 0UL, 3 + 1UL, 1UL, 2 + 2UL, 2UL, 1 ] + + let clist = Matrix.CoordinateList(3UL, 3UL, d) + Matrix.fromCoordinateList clist + + let expected = + let d = + [ 0UL, 0UL, 3 + 1UL, 1UL, 4 + 2UL, 2UL, 3 ] + + let clist = Matrix.CoordinateList(3UL, 3UL, d) + Matrix.fromCoordinateList clist + + let actual = + match LinearAlgebra.mxm op_add op_mult m1 m2 with + | Result.Success m -> m + | Result.Failure e -> failwith (e.ToString()) + + Assert.Equal(expected, actual) + +[] +let ``Shrinking mxm`` () = + // 2 x 3 + // 1 0 2 + // 0 3 0 + let m1 = + let d = + [ 0UL, 0UL, 1 + 0UL, 2UL, 2 + 1UL, 1UL, 3 ] + + let clist = Matrix.CoordinateList(2UL, 3UL, d) + Matrix.fromCoordinateList clist + + // 3 x 2 + // 0 4 + // 5 0 + // 6 0 + let m2 = + let d = + [ 0UL, 1UL, 4 + 1UL, 0UL, 5 + 2UL, 0UL, 6 ] + + let clist = Matrix.CoordinateList(3UL, 2UL, d) + Matrix.fromCoordinateList clist + + // 2 x 2 + // 12 4 + // 15 0 + let expected = + let d = + [ 0UL, 0UL, 12 + 0UL, 1UL, 4 + 1UL, 0UL, 15 ] + + let clist = Matrix.CoordinateList(2UL, 2UL, d) + Matrix.fromCoordinateList clist + + let actual = + match LinearAlgebra.mxm op_add op_mult m1 m2 with + | Result.Success m -> m + | Result.Failure e -> failwith (e.ToString()) + + Assert.Equal(expected, actual) + +[] +let ``Expanding mxm`` () = + // 3 x 2 + // 1 0 + // 0 2 + // 3 0 + let m1 = + let d = + [ 0UL, 0UL, 1 + 1UL, 1UL, 2 + 2UL, 0UL, 3 ] + + let clist = Matrix.CoordinateList(3UL, 2UL, d) + Matrix.fromCoordinateList clist + // 2 x 3 + // 4 5 6 + // 0 0 0 + let m2 = + let d = + [ 0UL, 0UL, 4 + 0UL, 1UL, 5 + 0UL, 2UL, 6 ] + + let clist = Matrix.CoordinateList(2UL, 3UL, d) + Matrix.fromCoordinateList clist + + // 3 x 3 + // 4 5 6 + // 0 0 0 + // 12 15 18 + let expected = + let d = + [ 0UL, 0UL, 4 + 0UL, 1UL, 5 + 0UL, 2UL, 6 + 2UL, 0UL, 12 + 2UL, 1UL, 15 + 2UL, 2UL, 18 ] + + let clist = Matrix.CoordinateList(3UL, 3UL, d) + Matrix.fromCoordinateList clist + + let actual = + match LinearAlgebra.mxm op_add op_mult m1 m2 with + | Result.Success m -> m + | Result.Failure e -> failwith (e.ToString()) + + Assert.Equal(expected, actual) diff --git a/QuadTree.Tests/Tests.Matrix.fs b/QuadTree.Tests/Tests.Matrix.fs index bb382a1..a7f4553 100644 --- a/QuadTree.Tests/Tests.Matrix.fs +++ b/QuadTree.Tests/Tests.Matrix.fs @@ -15,7 +15,21 @@ let printMatrix (matrix: SparseMatrix<_>) = printfn " size: %A" matrix.storage.size printfn " Data: %A" matrix.storage.data - +let leaf_v v = qtree.Leaf << UserValue <| Some v +let leaf_n () = qtree.Leaf << UserValue <| None +let leaf_d () = qtree.Leaf Dummy + +let op_add x y = + match (x, y) with + | Some(a), Some(b) -> Some(a + b) + | Some(a), _ + | _, Some(a) -> Some(a) + | _ -> None + +let op_mult x y = + match (x, y) with + | Some(a), Some(b) -> Some(a * b) + | _ -> None (* N,1,1,N 3,2,2,3 @@ -37,38 +51,17 @@ let ``Simple Matrix.map2. Square where number of cols and rows are power of two. let m1 = let tree = Matrix.qtree.Node( - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(UserValue(Some(1))), - Matrix.qtree.Leaf(UserValue(Some(3))), - Matrix.qtree.Leaf(UserValue(Some(2))) - ), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(Some(1))), - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(UserValue(Some(2))), - Matrix.qtree.Leaf(UserValue(Some(3))) - ), - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(Some(1))), - Matrix.qtree.Leaf(UserValue(Some(2))), - Matrix.qtree.Leaf(UserValue(Some(3))), - Matrix.qtree.Leaf(UserValue(None)) - ) + Matrix.qtree.Node(leaf_n (), leaf_v 1, leaf_v 3, leaf_v 2), + Matrix.qtree.Node(leaf_v 1, leaf_n (), leaf_v 2, leaf_v 3), + leaf_n (), + Matrix.qtree.Node(leaf_v 1, leaf_v 2, leaf_v 3, leaf_n ()) ) let store = Storage(4UL, tree) SparseMatrix(4UL, 4UL, 9UL, store) let m2 = - let tree = - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(Some(1))), - Matrix.qtree.Leaf(UserValue(Some(2))), - Matrix.qtree.Leaf(UserValue(Some(3))), - Matrix.qtree.Leaf(UserValue(None)) - ) + let tree = Matrix.qtree.Node(leaf_v 1, leaf_v 2, leaf_v 3, leaf_n ()) let store = Storage(4UL, tree) SparseMatrix(4UL, 4UL, 12UL, store) @@ -81,20 +74,10 @@ let ``Simple Matrix.map2. Square where number of cols and rows are power of two. let expected = let tree = Matrix.qtree.Node( - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(UserValue(Some(2))), - Matrix.qtree.Leaf(UserValue(Some(4))), - Matrix.qtree.Leaf(UserValue(Some(3))) - ), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(Some(3))), - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(UserValue(Some(4))), - Matrix.qtree.Leaf(UserValue(Some(5))) - ), - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(UserValue(None)) + Matrix.qtree.Node(leaf_n (), leaf_v 2, leaf_v 4, leaf_v 3), + Matrix.qtree.Node(leaf_v 3, leaf_n (), leaf_v 4, leaf_v 5), + leaf_n (), + leaf_n () ) let store = Storage(4UL, tree) @@ -125,30 +108,10 @@ let ``Simple Matrix.map2. Square where number of cols and rows are not power of let m1 = let tree = Matrix.qtree.Node( - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(UserValue(Some(1))), - Matrix.qtree.Leaf(UserValue(Some(3))), - Matrix.qtree.Leaf(UserValue(Some(2))) - ), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(Some(1))), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(UserValue(Some(2))), - Matrix.qtree.Leaf(Dummy) - ), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(Dummy) - ), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(Some(1))), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(Dummy) - ) + Matrix.qtree.Node(leaf_n (), leaf_v 1, leaf_v 3, leaf_v 2), + Matrix.qtree.Node(leaf_v 1, leaf_d (), leaf_v 2, leaf_d ()), + Matrix.qtree.Node(leaf_n (), leaf_n (), leaf_d (), leaf_d ()), + Matrix.qtree.Node(leaf_v 1, leaf_d (), leaf_d (), leaf_d ()) ) let store = Storage(4UL, tree) @@ -157,25 +120,10 @@ let ``Simple Matrix.map2. Square where number of cols and rows are not power of let m2 = let tree = Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(Some(1))), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(Some(2))), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(UserValue(Some(2))), - Matrix.qtree.Leaf(Dummy) - ), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(Some(3))), - Matrix.qtree.Leaf(UserValue(Some(3))), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(Dummy) - ), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(Dummy) - ) + leaf_v 1, + Matrix.qtree.Node(leaf_v 2, leaf_d (), leaf_v 2, leaf_d ()), + Matrix.qtree.Node(leaf_v 3, leaf_v 3, leaf_d (), leaf_d ()), + Matrix.qtree.Node(leaf_n (), leaf_d (), leaf_d (), leaf_d ()) ) let store = Storage(4UL, tree) @@ -189,30 +137,10 @@ let ``Simple Matrix.map2. Square where number of cols and rows are not power of let expected = let tree = Matrix.qtree.Node( - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(UserValue(Some(2))), - Matrix.qtree.Leaf(UserValue(Some(4))), - Matrix.qtree.Leaf(UserValue(Some(3))) - ), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(Some(3))), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(UserValue(Some(4))), - Matrix.qtree.Leaf(Dummy) - ), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(Dummy) - ), - Matrix.qtree.Node( - Matrix.qtree.Leaf(UserValue(None)), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(Dummy), - Matrix.qtree.Leaf(Dummy) - ) + Matrix.qtree.Node(leaf_n (), leaf_v 2, leaf_v 4, leaf_v 3), + Matrix.qtree.Node(leaf_v 3, leaf_d (), leaf_v 4, leaf_d ()), + Matrix.qtree.Node(leaf_n (), leaf_n (), leaf_d (), leaf_d ()), + Matrix.qtree.Node(leaf_n (), leaf_d (), leaf_d (), leaf_d ()) ) let store = Storage(4UL, tree) @@ -303,12 +231,7 @@ let ``Condensation of empty`` () = // DDDD // DDDD let tree = - qtree.Node( - qtree.Leaf <| UserValue None, - qtree.Node(qtree.Leaf <| UserValue None, qtree.Leaf Dummy, qtree.Leaf <| UserValue None, qtree.Leaf Dummy), - qtree.Leaf Dummy, - qtree.Leaf Dummy - ) + qtree.Node(leaf_n (), qtree.Node(leaf_n (), leaf_d (), leaf_n (), leaf_d ()), leaf_d (), leaf_d ()) let expected = SparseMatrix(2UL, 3UL, 0UL, Storage(4UL, tree)) @@ -329,23 +252,166 @@ let ``Condensation of sparse`` () = let tree = qtree.Node( - qtree.Leaf <| UserValue None, - qtree.Node( - qtree.Leaf << UserValue <| Some 2, - qtree.Leaf Dummy, - qtree.Leaf <| UserValue None, - qtree.Leaf Dummy - ), - qtree.Leaf <| UserValue None, - qtree.Node( - qtree.Leaf <| UserValue None, - qtree.Leaf Dummy, - qtree.Leaf << UserValue <| Some 4, - qtree.Leaf Dummy - ) + leaf_n (), + qtree.Node(leaf_v 2, leaf_d (), leaf_n (), leaf_d ()), + leaf_n (), + qtree.Node(leaf_n (), leaf_d (), leaf_v 4, leaf_d ()) ) let expected = SparseMatrix(4UL, 3UL, 0UL, Storage(4UL, tree)) Assert.Equal(expected.storage.data, actual.storage.data) + +[] +let ``fold -> sum`` () = + // 222D + // 222D + // 222D + // DDDD + let tree = + qtree.Node( + leaf_v 2, + qtree.Node(leaf_v 2, leaf_d (), leaf_v 2, leaf_d ()), + qtree.Node(leaf_v 2, leaf_v 2, leaf_d (), leaf_d ()), + qtree.Node(leaf_v 2, leaf_d (), leaf_d (), leaf_d ()) + ) + + let m1 = + SparseMatrix(3UL, 3UL, 9UL, Matrix.Storage(4UL, tree)) + + let expected = 18 + + let actual = Option.get <| Matrix.foldAssociative op_add None m1 + + Assert.Equal(expected, actual) + +[] +let ``4x4 lower triangle`` () = + // 2222 + // 2222 + // 2222 + // 2222 + let tree = leaf_v 2 + + // 2NNN + // 22NN + // 222N + // 2222 + let tree_expected = + qtree.Node( + qtree.Node(leaf_v 2, leaf_n (), leaf_v 2, leaf_v 2), + leaf_n (), + leaf_v 2, + qtree.Node(leaf_v 2, leaf_n (), leaf_v 2, leaf_v 2) + ) + + let m1 = + SparseMatrix(4UL, 4UL, 16UL, Matrix.Storage(4UL, tree)) + + let expected = + SparseMatrix(4UL, 4UL, 10UL, Matrix.Storage(4UL, tree_expected)) + + let actual = getLowerTriangle m1 + + Assert.Equal(expected, actual) + + +[] +let ``3x3 lower triangle`` () = + // 222 D + // N22 D + // NN2 D + + // DDD D + let tree = + qtree.Node( + qtree.Node(leaf_v 2, leaf_v 2, leaf_n (), leaf_v 2), + qtree.Node(leaf_v 2, leaf_d (), leaf_v 2, leaf_d ()), + qtree.Node(leaf_n (), leaf_n (), leaf_d (), leaf_d ()), + qtree.Node(leaf_v 2, leaf_d (), leaf_d (), leaf_d ()) + ) + + + // 2NN D + // N2N D + // NN2 D + + // DDD D + let tree_expected = + qtree.Node( + qtree.Node(leaf_v 2, leaf_n (), leaf_n (), leaf_v 2), + qtree.Node(leaf_n (), leaf_d (), leaf_n (), leaf_d ()), + qtree.Node(leaf_n (), leaf_n (), leaf_d (), leaf_d ()), + qtree.Node(leaf_v 2, leaf_d (), leaf_d (), leaf_d ()) + ) + + let m1 = + SparseMatrix(3UL, 3UL, 6UL, Matrix.Storage(4UL, tree)) + + let expected = + SparseMatrix(3UL, 3UL, 3UL, Matrix.Storage(4UL, tree_expected)) + + let actual = getLowerTriangle m1 + + Assert.Equal(expected, actual) + +[] +let ``2x3 transposition`` () = + // 2N2D + // N2ND + // DDDD + // DDDD + let tree = + qtree.Node( + qtree.Node(leaf_v 2, leaf_n (), leaf_n (), leaf_v 2), + qtree.Node(leaf_v 2, leaf_d (), leaf_n (), leaf_d ()), + leaf_d (), + leaf_d () + ) + + + // 2NDD + // N2DD + // 2NDD + // DDDD + let tree_expected = + qtree.Node( + qtree.Node(leaf_v 2, leaf_n (), leaf_n (), leaf_v 2), + leaf_d (), + qtree.Node(leaf_v 2, leaf_n (), leaf_d (), leaf_d ()), + leaf_d () + ) + + let m1 = + SparseMatrix(2UL, 3UL, 3UL, Matrix.Storage(4UL, tree)) + + let expected = + SparseMatrix(3UL, 2UL, 3UL, Matrix.Storage(4UL, tree_expected)) + + let actual = transpose m1 + + Assert.Equal(expected, actual) + +[] +let ``Fold sum`` () = + // 2N2D + // N2ND + // DDDD + // DDDD + let tree = + qtree.Node( + qtree.Node(leaf_v 2, leaf_n (), leaf_n (), leaf_v 2), + qtree.Node(leaf_v 2, leaf_d (), leaf_n (), leaf_d ()), + leaf_d (), + leaf_d () + ) + + let m1 = + SparseMatrix(2UL, 3UL, 3UL, Matrix.Storage(4UL, tree)) + + let expected = 6 + + let actual = foldAssociative op_add None m1 |> Option.get + + Assert.Equal(expected, actual) diff --git a/QuadTree.Tests/Tests.TriangleCount.fs b/QuadTree.Tests/Tests.TriangleCount.fs new file mode 100644 index 0000000..357be7c --- /dev/null +++ b/QuadTree.Tests/Tests.TriangleCount.fs @@ -0,0 +1,60 @@ +module Graph.TriangleCount.Tests + +open System +open Xunit + +open Common +open Matrix +open Graph.TriangleCount + +[] +let ``7V Triangle count`` () = + + // 0 1 2 3 4 5 6 + // + // 0 0 1 0 1 0 0 0 + // 1 1 0 0 1 1 0 1 + // 2 0 0 0 1 0 1 1 + // 3 1 1 1 0 0 1 1 + // 4 0 1 0 0 0 1 1 + // 5 0 0 1 1 1 0 0 + // 6 0 1 1 1 1 0 0 + + let g = + let d = + [ 1UL, 0UL, () + 3UL, 0UL, () + 3UL, 1UL, () + 3UL, 2UL, () + 4UL, 1UL, () + 5UL, 2UL, () + 5UL, 3UL, () + 5UL, 4UL, () + 6UL, 1UL, () + 6UL, 2UL, () + 6UL, 3UL, () + 6UL, 4UL, () + + 0UL, 1UL, () + 0UL, 3UL, () + 1UL, 3UL, () + 2UL, 3UL, () + 1UL, 4UL, () + 2UL, 5UL, () + 3UL, 5UL, () + 4UL, 5UL, () + 1UL, 6UL, () + 2UL, 6UL, () + 3UL, 6UL, () + 4UL, 6UL, () ] + + fromCoordinateList (CoordinateList(7UL, 7UL, d)) + + let expected = 5UL + + let actual = + match triangle_count g with + | Result.Success(Some x) -> x + | _ -> failwith "Unreachable" + + Assert.Equal(expected, actual) diff --git a/QuadTree/LinearAlgebra.fs b/QuadTree/LinearAlgebra.fs index 618ef52..16a3479 100644 --- a/QuadTree/LinearAlgebra.fs +++ b/QuadTree/LinearAlgebra.fs @@ -7,6 +7,7 @@ type Error<'value1, 'value2, 'value3> = | InconsistentSizeOfArguments of Vector.SparseVector<'value1> * Matrix.SparseMatrix<'value2> | VectorAdditionProblem of Vector.Error<'value3, 'value3> + let rec multScalar op_add (x: uint64) y = if x = 1UL then y @@ -99,3 +100,153 @@ let vxm op_add op_mult (vector: Vector.SparseVector<'a>) (matrix: Matrix.SparseM |> Result.Success else (Error.InconsistentSizeOfArguments(vector, matrix)) |> Result.Failure + + +type MXMError<'value1, 'value2, 'value3> = + | InconsistentSizeOfArguments of Matrix.SparseMatrix<'value1> * Matrix.SparseMatrix<'value2> + | MatrixAdditionProblem of Matrix.Error<'value3, 'value3> + + +let mxm + (op_add: 'c option -> 'c option -> 'c option) + (op_mult: 'a option -> 'b option -> 'c option) + (m1: Matrix.SparseMatrix<'a>) + (m2: Matrix.SparseMatrix<'b>) + = + + let rec shrink tree (size: uint64) = + match tree with + | Matrix.qtree.Node(nw, ne, sw, _) when ne = sw && ne = Matrix.qtree.Leaf Dummy -> shrink nw (size / 2UL) + | _ -> tree, size + + let rec expand tree expandRatio = + match expandRatio with + | 1UL -> tree + | _ -> + expand + (Matrix.mkNode tree (Matrix.qtree.Leaf Dummy) (Matrix.qtree.Leaf Dummy) (Matrix.qtree.Leaf Dummy)) + (expandRatio / 2UL) + + let rec multiply (size: uint64) m1 m2 = + let divided (nw1, ne1, sw1, se1) (nw2, ne2, sw2, se2) = + let halfSize = size / 2UL + + // Double check this code + let nw1_x_nw2, ne1_x_sw2, nw1_x_ne2, ne1_x_se2, sw1_x_nw2, se1_x_sw2, sw1_x_ne2, se1_x_se2 = + (multiply halfSize nw1 nw2), + (multiply halfSize ne1 sw2), + (multiply halfSize nw1 ne2), + (multiply halfSize ne1 se2), + (multiply halfSize sw1 nw2), + (multiply halfSize se1 sw2), + (multiply halfSize sw1 ne2), + (multiply halfSize se1 se2) + + match nw1_x_nw2, ne1_x_sw2, nw1_x_ne2, ne1_x_se2, sw1_x_nw2, se1_x_sw2, sw1_x_ne2, se1_x_se2 with + | Result.Success(tnw1_x_nw2, nvals_nw1_x_nw2), + Result.Success(tne1_x_sw2, nvals_ne1_x_sw2), + Result.Success(tnw1_x_ne2, nvals_nw1_x_ne2), + Result.Success(tne1_x_se2, nvals_ne1_x_se2), + Result.Success(tsw1_x_nw2, nvals_sw1_x_nw2), + Result.Success(tse1_x_sw2, nvals_se1_x_sw2), + Result.Success(tsw1_x_ne2, nvals_sw1_x_ne2), + Result.Success(tse1_x_se2, nvals_se1_x_se2) -> + let nrows = (uint64 halfSize) * 1UL + let ncols = (uint64 halfSize) * 1UL + let storageSize = halfSize + + let nw1_x_nw2 = + Matrix.SparseMatrix(nrows, ncols, nvals_nw1_x_nw2, Matrix.Storage(storageSize, tnw1_x_nw2)) + + let ne1_x_sw2 = + Matrix.SparseMatrix(nrows, ncols, nvals_ne1_x_sw2, Matrix.Storage(storageSize, tne1_x_sw2)) + + let nw1_x_ne2 = + Matrix.SparseMatrix(nrows, ncols, nvals_nw1_x_ne2, Matrix.Storage(storageSize, tnw1_x_ne2)) + + let ne1_x_se2 = + Matrix.SparseMatrix(nrows, ncols, nvals_ne1_x_se2, Matrix.Storage(storageSize, tne1_x_se2)) + + let sw1_x_nw2 = + Matrix.SparseMatrix(nrows, ncols, nvals_sw1_x_nw2, Matrix.Storage(storageSize, tsw1_x_nw2)) + + let se1_x_sw2 = + Matrix.SparseMatrix(nrows, ncols, nvals_se1_x_sw2, Matrix.Storage(storageSize, tse1_x_sw2)) + + let sw1_x_ne2 = + Matrix.SparseMatrix(nrows, ncols, nvals_sw1_x_ne2, Matrix.Storage(storageSize, tsw1_x_ne2)) + + let se1_x_se2 = + Matrix.SparseMatrix(nrows, ncols, nvals_se1_x_se2, Matrix.Storage(storageSize, tse1_x_se2)) + + let mAdd m1 (m2: Matrix.SparseMatrix<_>) = + match m2.storage.data with + | Matrix.qtree.Leaf Dummy -> Result.Success m1 + | _ -> Matrix.map2 m1 m2 op_add + + let rnw = mAdd nw1_x_nw2 ne1_x_sw2 + let rne = mAdd nw1_x_ne2 ne1_x_se2 + let rsw = mAdd sw1_x_nw2 se1_x_sw2 + let rse = mAdd sw1_x_ne2 se1_x_se2 + + match rnw, rne, rsw, rse with + | Result.Success(nw), Result.Success(ne), Result.Success(sw), Result.Success(se) -> + Result.Success( + Matrix.mkNode nw.storage.data ne.storage.data sw.storage.data se.storage.data, + nw.nvals + ne.nvals + sw.nvals + se.nvals + ) + | Result.Failure(e), _, _, _ + | _, Result.Failure(e), _, _ + | _, _, Result.Failure(e), _ + | _, _, _, Result.Failure(e) -> Result.Failure(MXMError.MatrixAdditionProblem(e)) + + | Result.Failure(e), _, _, _, _, _, _, _ + | _, Result.Failure(e), _, _, _, _, _, _ + | _, _, Result.Failure(e), _, _, _, _, _ + | _, _, _, Result.Failure(e), _, _, _, _ + | _, _, _, _, Result.Failure(e), _, _, _ + | _, _, _, _, _, Result.Failure(e), _, _ + | _, _, _, _, _, _, Result.Failure(e), _ + | _, _, _, _, _, _, _, Result.Failure(e) -> Result.Failure(e) + + match m1, m2 with + | Matrix.qtree.Leaf(UserValue v1), Matrix.qtree.Leaf(UserValue v2) -> + let res = multScalar op_add (uint64 size) (op_mult v1 v2) + + let nnz = + match res with + | None -> 0UL + | _ -> (uint64 <| size * size) * 1UL + + Result.Success(Matrix.qtree.Leaf(UserValue res), nnz) + | Matrix.qtree.Leaf(UserValue(_)), Matrix.qtree.Node(nw2, ne2, sw2, se2) -> + divided (m1, m1, m1, m1) (nw2, ne2, sw2, se2) + | Matrix.qtree.Node(nw1, ne1, sw1, se1), Matrix.qtree.Leaf(UserValue(_)) -> + divided (nw1, ne1, sw1, se1) (m2, m2, m2, m2) + | Matrix.qtree.Node(nw1, ne1, sw1, se1), Matrix.qtree.Node(nw2, ne2, sw2, se2) -> + divided (nw1, ne1, sw1, se1) (nw2, ne2, sw2, se2) + | Matrix.qtree.Leaf Dummy, _ + | _, Matrix.qtree.Leaf Dummy -> Result.Success(Matrix.qtree.Leaf Dummy, 0UL) + + if uint64 m1.ncols = uint64 m2.nrows then + let nrows = m1.nrows + let ncols = m2.ncols + let storageSize = max m1.storage.size m2.storage.size + + let m1_tree, m2_tree = + if m1.storage.size < m2.storage.size then + expand (m1.storage.data) (m2.storage.size / (uint64 m1.storage.size)), m2.storage.data + elif m1.storage.size > m2.storage.size then + m1.storage.data, expand (m2.storage.data) (m1.storage.size / (uint64 m2.storage.size)) + else + m1.storage.data, m2.storage.data + + match multiply storageSize m1_tree m2_tree with + | Result.Success(tree, nvals) -> + // in case the resulting storageSize can be smaller + // e.g. (2x3) * (3x2) matrices + let tree, storageSize = shrink tree storageSize + Result.Success(Matrix.SparseMatrix(nrows, ncols, nvals, Matrix.Storage(storageSize, tree))) + | Result.Failure(e) -> Result.Failure(e) + else + MXMError.InconsistentSizeOfArguments(m1, m2) |> Result.Failure diff --git a/QuadTree/Matrix.fs b/QuadTree/Matrix.fs index 467ee10..7697762 100644 --- a/QuadTree/Matrix.fs +++ b/QuadTree/Matrix.fs @@ -180,48 +180,102 @@ let map2 (matrix1: SparseMatrix<_>) (matrix2: SparseMatrix<_>) f = else (Error.InconsistentSizeOfArguments(matrix1, matrix2)) |> Result.Failure +let foldAssociative (folder: 'T option -> 'T option -> 'T option) (state: 'T option) (matrix: SparseMatrix<'T>) = + let rec traverse tree (size: uint64) (state: 'T option) = + match tree with + | Leaf Dummy -> state + | Leaf(UserValue v) -> + let area = (uint64 size) * (uint64 size) -(* -let rec map (op: 'TCell1 -> 'TCell2) (m1:Matrix<'TCell1>) = - match m1 with - | Leaf(s,v1) -> - Leaf (s, op v1) - | Node (s,x1,x2,x3,x4) -> - mkNode s (map op x1) (map op x2) (map op x3) (map op x4) + let rec foldValue size accum = + if size = 1UL then + accum + else + let halfSize = size / 2UL + foldValue halfSize (folder accum accum) -let multScalar opAdd (x:uint) y = - if x = 1u - then y - else - List.init (int x) (fun _ -> y) - |> List.reduce opAdd - -let compose opAdd (opMult: 'TCell1 -> 'TCell2 -> 'TCell3) (m1:Matrix<'TCell1>) (m2:Matrix<'TCell2>) = - - let rec go m1 m2 = - match (m1, m2) with - | Leaf(s,v1), Leaf(_,v2) -> - Leaf (s, multScalar opAdd s (opMult v1 v2)) - | Node (s,x1,x2,x3,x4), Node (_,y1,y2,y3,y4) -> - let z1 = map2 opAdd (go x1 y1) (go x2 y3) - let z2 = map2 opAdd (go x1 y2) (go x2 y4) - let z3 = map2 opAdd (go x3 y1) (go x4 y3) - let z4 = map2 opAdd (go x3 y2) (go x4 y4) - mkNode s z1 z2 z3 z4 - | Node (s,x1,x2,x3,x4), Leaf (_,v) -> - let l = Leaf(s / 2u,v) - let z1 = map2 opAdd (go x1 l) (go x2 l) - let z3 = map2 opAdd (go x3 l) (go x4 l) - mkNode s z1 z1 z3 z3 - | Leaf (_,v), Node (s,x1,x2,x3,x4) -> - let l = Leaf(s / 2u,v) - let z1 = map2 opAdd (go l x1) (go l x3) - let z2 = map2 opAdd (go l x2) (go l x4) - mkNode s z1 z2 z1 z2 - - if m1.Size = m2.Size - then go m1 m2 - else failwith $"Matrices should be of equals size, but m1.Size = {m1.Size} and m2.Size = {m2.Size}" + folder state (foldValue area v) + | Node(nw, ne, sw, se) -> + let halfSize = size / 2UL -*) + let nwState = traverse nw halfSize state + let neState = traverse ne halfSize nwState + let swState = traverse sw halfSize neState + let seState = traverse se halfSize swState + + seState + + let storageSize = matrix.storage.size + + let tree = matrix.storage.data + traverse tree storageSize state + + +let getLowerTriangle (matrix: SparseMatrix<_>) = + + // returns tree, removed_nvals + let rec makeNone tree (size: uint64) = + match tree with + | Leaf Dummy + | Leaf(UserValue None) -> tree, 0UL + | Leaf(UserValue(Some _)) -> Leaf(UserValue None), (uint64 <| size * size) * 1UL + | Node(nw, ne, sw, se) -> + let halfSize = size / 2UL + let nw_new, nw_removed = makeNone nw halfSize + let ne_new, ne_removed = makeNone ne halfSize + let sw_new, sw_removed = makeNone sw halfSize + let se_new, se_removed = makeNone se halfSize + + (mkNode nw_new ne_new sw_new se_new), nw_removed + ne_removed + sw_removed + se_removed + + let rec traverse tree size = + match tree with + | Leaf _ when size = 1UL -> tree, 0UL + | Leaf Dummy -> Leaf Dummy, 0UL + | Leaf _ -> + let halfSize = size / 2UL + + let nw, nw_removed = traverse tree halfSize + + let ne, ne_removed = + Leaf <| UserValue None, (uint64 <| halfSize * halfSize) * 1UL + + let sw, sw_removed = tree, 0UL + let se, se_removed = traverse tree halfSize + (mkNode nw ne sw se), nw_removed + ne_removed + sw_removed + se_removed + | Node(nw, ne, sw, se) -> + let halfSize = size / 2UL + + let nw_new, nw_removed = traverse nw halfSize + let ne_new, ne_removed = makeNone ne halfSize + let sw_new, sw_removed = sw, 0UL + let se_new, se_removed = traverse se halfSize + + (mkNode nw_new ne_new sw_new se_new), nw_removed + ne_removed + sw_removed + se_removed + + let storageSize = matrix.storage.size + let tree, nvals_removed = traverse matrix.storage.data storageSize + + SparseMatrix(matrix.nrows, matrix.ncols, matrix.nvals - nvals_removed, Storage(storageSize, tree)) + +let transpose (matrix: SparseMatrix<_>) = + let rec traverse tree = + match tree with + | Leaf _ -> tree + | Node(nw, ne, sw, se) -> + mkNode + (traverse nw) + (traverse sw) // ne -> sw + (traverse ne) // sw -> ne + (traverse se) + + let nrows = (uint64 matrix.ncols) * 1UL + let ncols = (uint64 matrix.nrows) * 1UL + + let tree = traverse matrix.storage.data + + SparseMatrix(nrows, ncols, matrix.nvals, Storage(matrix.storage.size, tree)) + +let mask (m1: SparseMatrix<'a>) (m2: SparseMatrix<'b>) f = + map2 m1 m2 (fun m1 m2 -> if f m2 then m1 else None) diff --git a/QuadTree/QuadTree.fsproj b/QuadTree/QuadTree.fsproj index a27fb3d..85fda43 100644 --- a/QuadTree/QuadTree.fsproj +++ b/QuadTree/QuadTree.fsproj @@ -12,6 +12,7 @@ + diff --git a/QuadTree/TriangleCount.fs b/QuadTree/TriangleCount.fs new file mode 100644 index 0000000..f1d41b6 --- /dev/null +++ b/QuadTree/TriangleCount.fs @@ -0,0 +1,42 @@ +module Graph.TriangleCount + +open Common + +type TriangleCountError<'value1, 'value2, 'value3> = + | MXMError of LinearAlgebra.MXMError<'value1, 'value2, 'value3> + | MaskingError of Matrix.Error<'value3, 'value2> + +// Assume non-oriented graph adjacency matrix +// Some _ -> edge, None -> no edge +// Computes triangle count +let triangle_count (graph: Matrix.SparseMatrix<_>) = + let graph = Matrix.getLowerTriangle graph + + let op_add o1 o2 = + match o1, o2 with + | Some x, Some y -> Some <| x + y + | Some x, None + | None, Some x -> Some x + | None, None -> None + + let op_mult o1 o2 = + match o1, o2 with + | Some _, Some _ -> Some 1UL + | _ -> None + + let C = LinearAlgebra.mxm op_add op_mult graph (Matrix.transpose graph) + + let CMasked = + match C with + | Result.Success matrix -> + match Matrix.mask matrix graph Option.isSome with + | Result.Success m -> Result.Success m + | Result.Failure e -> Result.Failure <| TriangleCountError.MaskingError e + | Result.Failure e -> Result.Failure <| TriangleCountError.MXMError e + + let result = + match CMasked with + | Result.Success matrix -> Result.Success(Matrix.foldAssociative op_add None matrix) + | Result.Failure e -> Result.Failure e + + result