From 44f24880129f107287454c43c29c824ebf2cf82a Mon Sep 17 00:00:00 2001 From: Ray Kimmerer Date: Thu, 22 Sep 2022 01:47:45 -0400 Subject: [PATCH 01/16] fix masking op to use replace --- src/abstractgbarray.jl | 14 +++++------- src/chainrules/ewiserules.jl | 4 ++-- src/chainrules/selectrules.jl | 8 +++---- src/operations/ewise.jl | 6 +++--- src/operations/extract.jl | 4 ++-- src/operations/map.jl | 34 +++--------------------------- src/operations/mul.jl | 2 +- src/operations/operationutils.jl | 1 - src/operations/reduce.jl | 2 +- src/operations/select.jl | 2 +- src/operations/sort.jl | 2 +- src/operations/transpose.jl | 32 ++++++++++++++++++++++++++-- test/chainrules/chainrulesutils.jl | 4 ++-- 13 files changed, 55 insertions(+), 60 deletions(-) diff --git a/src/abstractgbarray.jl b/src/abstractgbarray.jl index e7c6e637..ce5fea07 100644 --- a/src/abstractgbarray.jl +++ b/src/abstractgbarray.jl @@ -433,7 +433,7 @@ function subassign!( nj_sizecheck = J isa Colon ? size(C, 2) : length(J) I, ni = idx(I) J, nj = idx(J) - desc = _handledescriptor(desc; in1=A) + desc = _handledescriptor(desc; out=C, in1=A) mask = _handlemask!(desc, mask) I = decrement!(I) J = decrement!(J) @@ -464,7 +464,7 @@ function subassign!(C::AbstractGBArray{T}, x, I, J; J, nj = idx(J) I = decrement!(I) J = decrement!(J) - desc = _handledescriptor(desc) + desc = _handledescriptor(desc; out=C) mask = _handlemask!(desc, mask) _subassign(C, x, I, ni, J, nj, mask, _handleaccum(accum, eltype(C)), desc) increment!(I) @@ -533,7 +533,7 @@ function assign!( _canbeoutput(C) || throw(ShallowException()) I, ni = idx(I) J, nj = idx(J) - desc = _handledescriptor(desc; in1=A) + desc = _handledescriptor(desc; in1=A, out=C) mask = _handlemask!(desc, mask) I = decrement!(I) J = decrement!(J) @@ -552,7 +552,8 @@ function assign!(C::AbstractGBArray{T}, x, I, J; J, nj = idx(J) I = decrement!(I) J = decrement!(J) - desc = _handledescriptor(desc) + desc = _handledescriptor(desc; out=C) + mask = _handlemask!(desc, mask) _assign(C, x, I, ni, J, nj, mask, _handleaccum(accum, eltype(C)), desc) increment!(I) increment!(J) @@ -571,11 +572,6 @@ function Base.setindex!( subassign!(C, A, I, J; mask, accum, desc) end -#Help wanted: This isn't really centered for a lot of eltypes. -function Base.replace_in_print_matrix(A::AbstractGBMatrix, i::Integer, j::Integer, s::AbstractString) - Base.isstored(A, i, j) ? s : Base.replace_with_centered_mark(s) -end - # AbstractGBVector functions: ############################# function Base.size(v::AbstractGBVector) diff --git a/src/chainrules/ewiserules.jl b/src/chainrules/ewiserules.jl index bdddae21..e08bcf08 100644 --- a/src/chainrules/ewiserules.jl +++ b/src/chainrules/ewiserules.jl @@ -55,8 +55,8 @@ function rrule(::typeof(eadd), A::AbstractGBArray, B::AbstractGBArray, ::typeof( function pluspullback(ΔΩ) return ( NoTangent(), - mask(unthunk(ΔΩ), A; structural = true), - mask(unthunk(ΔΩ), B; structural = true), + mask(unthunk(ΔΩ), Structural(A)), + mask(unthunk(ΔΩ), Structural(B)), NoTangent() ) end diff --git a/src/chainrules/selectrules.jl b/src/chainrules/selectrules.jl index db2a7f25..bfd218bc 100644 --- a/src/chainrules/selectrules.jl +++ b/src/chainrules/selectrules.jl @@ -6,7 +6,7 @@ function frule( A::AbstractGBArray ) Ω = select(op, A) - ∂Ω = mask(unthunk(ΔA), Ω, structural = true) + ∂Ω = mask(unthunk(ΔA), Structural(Ω)) return Ω, ∂Ω end @@ -19,7 +19,7 @@ function frule( thunk::Union{GBScalar, Nothing, valid_union} ) Ω = select(op, A, thunk) - ∂Ω = mask(unthunk(ΔA), Ω, structural = true) + ∂Ω = mask(unthunk(ΔA), Structural(Ω)) return Ω, ∂Ω end @@ -30,7 +30,7 @@ function rrule( ) out = select(op, A) function selectback(ΔΩ) - ∂A = mask(unthunk(ΔΩ), out, structural = true) + ∂A = mask(unthunk(ΔΩ), Structural(out)) return NoTangent(), NoTangent(), ∂A end return out, selectback @@ -44,7 +44,7 @@ function rrule( ) out = select(op, A, thunk) function selectback(ΔΩ) - ∂A = mask(unthunk(ΔΩ), out, structural = true) + ∂A = mask(unthunk(ΔΩ), Structural(out)) return NoTangent(), NoTangent(), ∂A, NoTangent() end return out, selectback diff --git a/src/operations/ewise.jl b/src/operations/ewise.jl index 47ce5c55..706fe80b 100644 --- a/src/operations/ewise.jl +++ b/src/operations/ewise.jl @@ -31,7 +31,7 @@ function emul!( desc = nothing ) _canbeoutput(C) || throw(ShallowException()) - desc = _handledescriptor(desc; in1=A, in2=B) + desc = _handledescriptor(desc; out=C, in1=A, in2=B) mask = _handlemask!(desc, mask) size(C, 1) == size(A, 1) == size(B, 1) && size(C, 2) == size(A, 2) == size(B, 2) || throw(DimensionMismatch()) @@ -117,7 +117,7 @@ function eadd!( desc = nothing ) _canbeoutput(C) || throw(ShallowException()) - desc = _handledescriptor(desc; in1=A, in2 = B) + desc = _handledescriptor(desc; out=C, in1=A, in2 = B) mask = _handlemask!(desc, mask) size(C, 1) == size(A, 1) == size(B, 1) && size(C, 2) == size(A, 2) == size(B, 2) || throw(DimensionMismatch()) @@ -203,7 +203,7 @@ function eunion!( desc = nothing ) where {T, U} _canbeoutput(C) || throw(ShallowException()) - desc = _handledescriptor(desc; in1=A, in2 = B) + desc = _handledescriptor(desc; out=C, in1=A, in2 = B) mask = _handlemask!(desc, mask) size(C, 1) == size(A, 1) == size(B, 1) && size(C, 2) == size(A, 2) == size(B, 2) || throw(DimensionMismatch()) diff --git a/src/operations/extract.jl b/src/operations/extract.jl index 4e46ec37..2682dce4 100644 --- a/src/operations/extract.jl +++ b/src/operations/extract.jl @@ -65,7 +65,7 @@ function extract!( J, nj = idx(J) I isa Number && (I = UInt64[I]) J isa Number && (J = UInt64[J]) - desc = _handledescriptor(desc; in1 = A) + desc = _handledescriptor(desc; out=C, in1 = A) mask = _handlemask!(desc, mask) I = decrement!(I) J = decrement!(J) @@ -137,7 +137,7 @@ function extract!( _canbeoutput(C) || throw(ShallowException()) I, ni = idx(I) I = decrement!(I) - desc = _handledescriptor(desc) + desc = _handledescriptor(desc; out=w) mask = _handlemask!(desc, mask) @wraperror LibGraphBLAS.GrB_Matrix_extract(w, mask, _handleaccum(accum, eltype(w)), u, I, ni, UInt64[0], 1, desc) I isa AbstractVector && increment!(I) diff --git a/src/operations/map.jl b/src/operations/map.jl index 548e123b..9fe0d9c5 100644 --- a/src/operations/map.jl +++ b/src/operations/map.jl @@ -3,7 +3,7 @@ function apply!( mask = nothing, accum = nothing, desc = nothing ) where {T} _canbeoutput(C) || throw(ShallowException()) - desc = _handledescriptor(desc; in1=A) + desc = _handledescriptor(desc; out=C, in1=A) mask = _handlemask!(desc, mask) op = unaryop(op, eltype(A)) accum = _handleaccum(accum, eltype(C)) @@ -54,7 +54,7 @@ function apply!( mask = nothing, accum = nothing, desc = nothing ) where {T} _canbeoutput(C) || throw(ShallowException()) - desc = _handledescriptor(desc; in2=A) + desc = _handledescriptor(desc; out=C, in2=A) mask = _handlemask!(desc, mask) op = binaryop(op, eltype(A), typeof(x)) accum = _handleaccum(accum, eltype(C)) @@ -82,7 +82,7 @@ function apply!( mask = nothing, accum = nothing, desc = nothing ) where {T} _canbeoutput(C) || throw(ShallowException()) - desc = _handledescriptor(desc; in1=A) + desc = _handledescriptor(desc; out=C, in1=A) mask = _handlemask!(desc, mask) op = binaryop(op, eltype(A), typeof(x)) accum = _handleaccum(accum, eltype(C)) @@ -124,31 +124,3 @@ Base.:-(u::GBArrayOrTranspose) = apply(-, u) Base.real(A::GBArrayOrTranspose) = real.(A) Base.imag(A::GBArrayOrTranspose) = imag.(A) - -""" - mask!(C::GBArrayOrTranspose, A::GBArrayOrTranspose, mask::GBVecOrMat) - -Apply a mask to matrix `A`, storing the results in C. -""" -function mask!(C::GBVecOrMat, A::GBArrayOrTranspose, mask::GBVecOrMat; structural = false, complement = false) - _canbeoutput(C) || throw(ShallowException()) - desc = Descriptor() - structural && (desc.structural_mask=true) - complement && (desc.complement_mask=true) - mask = mask isa Transpose || mask isa Adjoint ? copy(mask) : mask - apply!(identity, C, A; mask, desc) - return C -end - -function mask!(A::GBArrayOrTranspose, mask::GBVecOrMat; structural = false, complement = false) - mask!(A, A, mask; structural, complement) -end - -""" - mask(A::GBArrayOrTranspose, mask::GBVecOrMat) - -Apply a mask to matrix `A`. -""" -function mask(A::GBArrayOrTranspose, mask::GBVecOrMat; structural = false, complement = false) - return mask!(similar(A), A, mask; structural, complement) -end diff --git a/src/operations/mul.jl b/src/operations/mul.jl index 57bd6ba1..c0cfd1bc 100644 --- a/src/operations/mul.jl +++ b/src/operations/mul.jl @@ -8,7 +8,7 @@ function LinearAlgebra.mul!( desc = nothing ) _canbeoutput(C) || throw(ShallowException()) - desc = _handledescriptor(desc; in1=A, in2=B) + desc = _handledescriptor(desc; out=C, in1=A, in2=B) mask = _handlemask!(desc, mask) size(A, 2) == size(B, 1) || throw(DimensionMismatch("size(A, 2) != size(B, 1)")) size(A, 1) == size(C, 1) || throw(DimensionMismatch("size(A, 1) != size(C, 1)")) diff --git a/src/operations/operationutils.jl b/src/operations/operationutils.jl index 4e29cd02..ead54fda 100644 --- a/src/operations/operationutils.jl +++ b/src/operations/operationutils.jl @@ -77,7 +77,6 @@ function _handlemask!(desc, mask) return mask end - _handleaccum(::Nothing, t) = C_NULL _handleaccum(::Ptr{Nothing}, t) = C_NULL _handleaccum(op::Function, t) = binaryop(op, t, t) diff --git a/src/operations/reduce.jl b/src/operations/reduce.jl index 30a27192..b65fa035 100644 --- a/src/operations/reduce.jl +++ b/src/operations/reduce.jl @@ -3,7 +3,7 @@ function reduce!( mask = nothing, accum = nothing, desc = nothing ) _canbeoutput(w) || throw(ShallowException()) - desc = _handledescriptor(desc; in1=A) + desc = _handledescriptor(desc; out=w, in1=A) mask = _handlemask!(desc, mask) op = typedmonoid(op, eltype(w)) diff --git a/src/operations/select.jl b/src/operations/select.jl index 2e849b17..aa6a1156 100644 --- a/src/operations/select.jl +++ b/src/operations/select.jl @@ -12,7 +12,7 @@ function select!( ) _canbeoutput(C) || throw(ShallowException()) op = SelectOp(op) - desc = _handledescriptor(desc; in1=A) + desc = _handledescriptor(desc; out=C, in1=A) mask = _handlemask!(desc, mask) thunk === nothing && (thunk = C_NULL) accum = _handleaccum(accum, eltype(C)) diff --git a/src/operations/sort.jl b/src/operations/sort.jl index 12119dc6..69e26c49 100644 --- a/src/operations/sort.jl +++ b/src/operations/sort.jl @@ -24,7 +24,7 @@ function Base.sort!( else throw(ArgumentError("dims must be either 1 (sort columns) or 2 (sort rows)")) end - desc = _handledescriptor(desc; in1=A) + desc = _handledescriptor(desc; out=C, in1=A) desc.transpose_input1 = transpose @wraperror LibGraphBLAS.GxB_Matrix_sort(C, P, op, parent(A), desc) return C diff --git a/src/operations/transpose.jl b/src/operations/transpose.jl index e79fb6de..28c5f133 100644 --- a/src/operations/transpose.jl +++ b/src/operations/transpose.jl @@ -19,7 +19,7 @@ function gbtranspose!( mask = nothing, accum = nothing, desc = nothing ) _canbeoutput(C) || throw(ShallowException()) - desc = _handledescriptor(desc; in1=A) + desc = _handledescriptor(desc; out=C, in1=A) mask = _handlemask!(desc, mask) accum = _handleaccum(accum, eltype(C)) @wraperror LibGraphBLAS.GrB_transpose(C, mask, accum, parent(A), desc) @@ -72,4 +72,32 @@ LinearAlgebra.adjoint(A::GBVecOrMat) = transpose(A) LinearAlgebra.transpose(::Nothing) = nothing Base.unsafe_convert(::Type{Ptr{T}}, A::LinearAlgebra.AdjOrTrans{<:Any, <:AbstractGBArray}) where {T} = -throw(ArgumentError("Cannot convert $(typeof(A)) directly to a pointer. Please use copy.")) \ No newline at end of file +throw(ArgumentError("Cannot convert $(typeof(A)) directly to a pointer. Please use copy.")) + +""" + mask!(C::GBArrayOrTranspose, A::GBArrayOrTranspose, mask::GBVecOrMat) + +Apply a mask to matrix `A`, storing the results in C. +""" +function mask!(C::GBVecOrMat, A::GBArrayOrTranspose, mask; desc = nothing, replace_output = true) + _canbeoutput(C) || throw(ShallowException()) + desc = _handledescriptor(desc; out=C, in1 = A) + desc.transpose_input1 = true # double transpose to cancel out transpose. + desc.replace_output = replace_output # we must replace + mask = _handlemask!(desc, mask) + gbtranspose!(C, A; mask, desc) + return C +end + +function mask!(A::GBArrayOrTranspose, mask; desc = nothing, replace_output = true) + mask!(A, A, mask; desc) +end + +""" + mask(A::GBArrayOrTranspose, mask::GBVecOrMat) + +Apply a mask to matrix `A`. +""" +function mask(A::GBArrayOrTranspose, mask; desc = nothing, replace_output = true) + return mask!(similar(A), A, mask; desc) +end \ No newline at end of file diff --git a/test/chainrules/chainrulesutils.jl b/test/chainrules/chainrulesutils.jl index c60de0b7..480e3239 100644 --- a/test/chainrules/chainrulesutils.jl +++ b/test/chainrules/chainrulesutils.jl @@ -3,7 +3,7 @@ function FiniteDifferences.to_vec(M::GBMatrix) x, back = FiniteDifferences.to_vec(Matrix(M)) function backtomat(xvec) M2 = GBMatrix(back(xvec)) - return mask(M2, M; structural=true) + return mask!(M2, Structural(M)) end return x, backtomat end @@ -12,7 +12,7 @@ function FiniteDifferences.to_vec(v::GBVector) x, back = FiniteDifferences.to_vec(Vector(v)) function backtovec(xvec) v2 = GBVector(back(xvec)) - return mask(v2, v; structural=true) + return mask!(v2, Structural(v)) end return x, backtovec end From 9a516c90b861716de95f60f6dbf9e788d7f2882b Mon Sep 17 00:00:00 2001 From: Ray Kimmerer Date: Thu, 22 Sep 2022 05:01:11 -0400 Subject: [PATCH 02/16] really gross method of solving cfunction error? Will likely remove with more sleep --- src/operations/map.jl | 2 +- src/types.jl | 34 +++++++++++++++++++++++++++------- test/chainrules/mulrules.jl | 9 +++++++++ test/chainrules/selectrules.jl | 9 ++++++++- test/runtests.jl | 2 +- 5 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/operations/map.jl b/src/operations/map.jl index 9fe0d9c5..9ed39d76 100644 --- a/src/operations/map.jl +++ b/src/operations/map.jl @@ -112,7 +112,7 @@ function Base.map!(f, C::GBVecOrMat, A::GBArrayOrTranspose{T}; mask = nothing, a apply!(f, C, A; mask, accum, desc) end function Base.map!(f, A::GBArrayOrTranspose{T}; mask = nothing, accum = nothing, desc = nothing) where {T} - apply!(f, C, A; mask, accum, desc) + apply!(f, A, A; mask, accum, desc) end Base.:*(x::V, u::GBArrayOrTranspose{T}; mask = nothing, accum = nothing, desc = nothing) where {T, V<:Union{<:valid_union, T, <:Number}} = diff --git a/src/types.jl b/src/types.jl index bdad94b1..0d2630b7 100644 --- a/src/types.jl +++ b/src/types.jl @@ -31,6 +31,15 @@ function TypedUnaryOperator(fn::F, ::Type{X}) where {F, X} return TypedUnaryOperator(fn, X, Base._return_type(fn, Tuple{X})) end +@generated function getcfunc(f::F, ::Type{Z}, ::Type{X}) where {X, Z, F} + pmodule = Symbol(parentmodule(F)) + name = Symbol(F.instance) + newname = Symbol(name, :(___)) + quote + return @cfunction($pmodule.$name, Cvoid, (Ptr{$Z}, Ref{$X})) + end +end + function Base.unsafe_convert(::Type{LibGraphBLAS.GrB_UnaryOp}, op::TypedUnaryOperator{F, X, Z}) where {F, X, Z} # We can lazily load the built-ins since they are already constants. # Could potentially do this with UDFs, but probably not worth the effort. @@ -38,16 +47,27 @@ function Base.unsafe_convert(::Type{LibGraphBLAS.GrB_UnaryOp}, op::TypedUnaryOpe if op.builtin op.p = load_global(op.typestr, LibGraphBLAS.GrB_UnaryOp) else - fn = op.fn - function unaryopfn(z, x) - unsafe_store!(z, fn(x)) - return nothing + # function unaryopfn(z, x) + # unsafe_store!(z, fn(x)) + # return nothing + # end + pmodule = Symbol(parentmodule(F)) + name = Symbol(F.instance) + newname = Symbol(name, :(___)) + println(newname) + eval( + quote + function $newname(z, x) + unsafe_store!(z, $pmodule.$name(x)) + return nothing + end end + ) opref = Ref{LibGraphBLAS.GrB_UnaryOp}() - unaryopfn_C = @cfunction($unaryopfn, Cvoid, (Ptr{Z}, Ref{X})) - op.keepalive = (unaryopfn, unaryopfn_C) + unaryopfn_C = getcfunc(eval(:($newname)), Z, X) + op.keepalive = unaryopfn_C # the "" below is a placeholder for C code in the future for JIT'ing. (And maybe compiled code as a ptr :pray:?) - LibGraphBLAS.GxB_UnaryOp_new(opref, unaryopfn_C, gbtype(Z), gbtype(X), string(fn), "") + LibGraphBLAS.GxB_UnaryOp_new(opref, unaryopfn_C, gbtype(Z), gbtype(X), string(op.fn), "") op.p = opref[] end op.loaded = true diff --git a/test/chainrules/mulrules.jl b/test/chainrules/mulrules.jl index 843eb625..365840ca 100644 --- a/test/chainrules/mulrules.jl +++ b/test/chainrules/mulrules.jl @@ -51,7 +51,16 @@ @testset "Sparse" begin M = GBMatrix(sprand(20, 10, 0.25)) Y = GBMatrix(sprand(10, 0.1)) + # there's some sort of hangup when we hit completely zero matrices. + # But I can't replicate outside of Test.jl... + # TODO: *really* fix this. + if nnz(Y) == 0 + Y[rand(1:10), 1] = rand(eltype(Y)) + end N = GBMatrix(sprand(10, 30, 0.05)) + if nnz(N) == 0 + N[rand(1:10), rand(1:30)] = rand(eltype(N)) + end @testset "+.*" begin test_frule(*, M, Y) test_frule(*, M, Y, (+, *)) diff --git a/test/chainrules/selectrules.jl b/test/chainrules/selectrules.jl index 16ea98a3..d703d1c7 100644 --- a/test/chainrules/selectrules.jl +++ b/test/chainrules/selectrules.jl @@ -17,21 +17,28 @@ test_frule(select, <=, X, 0.) test_rrule(select, <=, X, 0.) end + @testset "Sparse" begin - X = GBMatrix(sprand(50, 50, 0.15)) + X = GBMatrix(sprand(10, 10, 0.4)) + print(X) test_frule(select, diag, X) test_rrule(select, diag, X) + println("diag") test_frule(select, offdiag, X) test_rrule(select, offdiag, X) + println("offdiag") test_frule(select, tril, X) test_rrule(select, tril, X) test_frule(select, triu, X) test_rrule(select, triu, X) + println("tris") test_frule(select, nonzeros, X) test_rrule(select, nonzeros, X) + println("nonzeros") test_frule(select, >, X, 0.) test_rrule(select, >, X, 0.) test_frule(select, <=, X, 0.) test_rrule(select, <=, X, 0.) + println("equalities") end end diff --git a/test/runtests.jl b/test/runtests.jl index d5567506..c0eed5ca 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -6,7 +6,7 @@ using ChainRulesTestUtils using ChainRulesCore using FiniteDifferences using SuiteSparseGraphBLAS -using SuiteSparseGraphBLAS: pair, second, xtype, ytype, ztype +using SuiteSparseGraphBLAS: pair, second, xtype, ytype, ztype, Structural, Complement Random.seed!(1) function include_test(path) From 2bae6c697b536516ff3b065649244ace67ed679c Mon Sep 17 00:00:00 2001 From: Ray Kimmerer Date: Thu, 22 Sep 2022 06:10:38 -0400 Subject: [PATCH 03/16] try functionwrappers --- Project.toml | 6 +++--- src/types.jl | 47 +++++++++++++++++++++-------------------------- 2 files changed, 24 insertions(+), 29 deletions(-) diff --git a/Project.toml b/Project.toml index 698c6ac5..0a7ac68b 100644 --- a/Project.toml +++ b/Project.toml @@ -1,10 +1,11 @@ name = "SuiteSparseGraphBLAS" uuid = "c2e53296-7b14-11e9-1210-bddfa8111e1d" authors = ["Will Kimmerer "] -version = "0.8" +version = "0.8.0" [deps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" +FunctionWrappers = "069b7b12-0de2-55c6-9aab-29f3d0a68a2e" KLU = "ef3ab10e-7fda-4108-b977-705223b18434" Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" @@ -20,11 +21,10 @@ SuiteSparse = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" [compat] ChainRulesCore = "1" +KLU = "0.4" MacroTools = "0.5" Preferences = "1" SSGraphBLAS_jll = "7.2" SpecialFunctions = "2" StorageOrders = "0.2" julia = "1.7" -KLU = "0.4" - diff --git a/src/types.jl b/src/types.jl index 0d2630b7..7641dbcb 100644 --- a/src/types.jl +++ b/src/types.jl @@ -1,3 +1,4 @@ +using FunctionWrappers: FunctionWrapper, do_ccall abstract type AbstractSparsity end struct Dense <: AbstractSparsity end struct Bitmap <: AbstractSparsity end @@ -31,14 +32,14 @@ function TypedUnaryOperator(fn::F, ::Type{X}) where {F, X} return TypedUnaryOperator(fn, X, Base._return_type(fn, Tuple{X})) end -@generated function getcfunc(f::F, ::Type{Z}, ::Type{X}) where {X, Z, F} - pmodule = Symbol(parentmodule(F)) - name = Symbol(F.instance) - newname = Symbol(name, :(___)) - quote - return @cfunction($pmodule.$name, Cvoid, (Ptr{$Z}, Ref{$X})) - end -end +# @generated function getcfunc(f::F, ::Type{Z}, ::Type{X}) where {X, Z, F} +# pmodule = Symbol(parentmodule(F)) +# name = Symbol(F.instance) +# newname = Symbol(name, :(___)) +# quote +# return @cfunction($pmodule.$name, Cvoid, (Ptr{$Z}, Ref{$X})) +# end +# end function Base.unsafe_convert(::Type{LibGraphBLAS.GrB_UnaryOp}, op::TypedUnaryOperator{F, X, Z}) where {F, X, Z} # We can lazily load the built-ins since they are already constants. @@ -47,27 +48,21 @@ function Base.unsafe_convert(::Type{LibGraphBLAS.GrB_UnaryOp}, op::TypedUnaryOpe if op.builtin op.p = load_global(op.typestr, LibGraphBLAS.GrB_UnaryOp) else - # function unaryopfn(z, x) - # unsafe_store!(z, fn(x)) - # return nothing - # end - pmodule = Symbol(parentmodule(F)) - name = Symbol(F.instance) - newname = Symbol(name, :(___)) - println(newname) - eval( - quote - function $newname(z, x) - unsafe_store!(z, $pmodule.$name(x)) - return nothing - end + fn = op.fn + function unaryopfn(z, x) + o = fn(x) + println(unsafe_load(z)) + println(o) + unsafe_store!(z, fn(x)) + println(unsafe_load(z)) + return nothing end - ) + opref = Ref{LibGraphBLAS.GrB_UnaryOp}() - unaryopfn_C = getcfunc(eval(:($newname)), Z, X) - op.keepalive = unaryopfn_C + unaryopfn_C = FunctionWrapper{Cvoid, Tuple{Ptr{Z}, X}}(unaryopfn) + op.keepalive = (unaryopfn, unaryopfn_C) # the "" below is a placeholder for C code in the future for JIT'ing. (And maybe compiled code as a ptr :pray:?) - LibGraphBLAS.GxB_UnaryOp_new(opref, unaryopfn_C, gbtype(Z), gbtype(X), string(op.fn), "") + LibGraphBLAS.GxB_UnaryOp_new(opref, unaryopfn_C.ptr, gbtype(Z), gbtype(X), string(fn), "") op.p = opref[] end op.loaded = true From c9cdda74255afaeb8ed1a3073ef724389b7f9a90 Mon Sep 17 00:00:00 2001 From: Ray Kimmerer Date: Thu, 22 Sep 2022 15:04:03 -0400 Subject: [PATCH 04/16] use celrod trick --- Project.toml | 1 - src/types.jl | 27 +++++++++++++++++++-------- src/unpack.jl | 4 ++-- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/Project.toml b/Project.toml index 0a7ac68b..e7d4a434 100644 --- a/Project.toml +++ b/Project.toml @@ -5,7 +5,6 @@ version = "0.8.0" [deps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -FunctionWrappers = "069b7b12-0de2-55c6-9aab-29f3d0a68a2e" KLU = "ef3ab10e-7fda-4108-b977-705223b18434" Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" diff --git a/src/types.jl b/src/types.jl index 7641dbcb..3acbdc56 100644 --- a/src/types.jl +++ b/src/types.jl @@ -1,4 +1,3 @@ -using FunctionWrappers: FunctionWrapper, do_ccall abstract type AbstractSparsity end struct Dense <: AbstractSparsity end struct Bitmap <: AbstractSparsity end @@ -41,6 +40,14 @@ end # end # end +@generated function cunary(f::F, ::Type{X}, ::Type{Z}) where {F, X, Z} + if Base.issingletontype(F) + :(@cfunction($(F.instance), Cvoid, (Ptr{Z}, Ref{X}))) + else + throw("Unsupported function $f") + end +end + function Base.unsafe_convert(::Type{LibGraphBLAS.GrB_UnaryOp}, op::TypedUnaryOperator{F, X, Z}) where {F, X, Z} # We can lazily load the built-ins since they are already constants. # Could potentially do this with UDFs, but probably not worth the effort. @@ -50,19 +57,15 @@ function Base.unsafe_convert(::Type{LibGraphBLAS.GrB_UnaryOp}, op::TypedUnaryOpe else fn = op.fn function unaryopfn(z, x) - o = fn(x) - println(unsafe_load(z)) - println(o) unsafe_store!(z, fn(x)) - println(unsafe_load(z)) return nothing end opref = Ref{LibGraphBLAS.GrB_UnaryOp}() - unaryopfn_C = FunctionWrapper{Cvoid, Tuple{Ptr{Z}, X}}(unaryopfn) + unaryopfn_C = cunary(unaryopfn, X, Z) op.keepalive = (unaryopfn, unaryopfn_C) # the "" below is a placeholder for C code in the future for JIT'ing. (And maybe compiled code as a ptr :pray:?) - LibGraphBLAS.GxB_UnaryOp_new(opref, unaryopfn_C.ptr, gbtype(Z), gbtype(X), string(fn), "") + LibGraphBLAS.GxB_UnaryOp_new(opref, unaryopfn_C, gbtype(Z), gbtype(X), string(fn), "") op.p = opref[] end op.loaded = true @@ -101,6 +104,14 @@ function (op::TypedBinaryOperator{F, X, Y, Z})(::Type{T1}, ::Type{T2}) where {F, end (op::TypedBinaryOperator)(T) = op(T, T) +@generated function cbinary(f::F, ::Type{X}, ::Type{Y}, ::Type{Z}) where {F, X, Y, Z} + if Base.issingletontype(F) + :(@cfunction($(F.instance), Cvoid, (Ptr{Z}, Ref{X}, Ref{Y}))) + else + throw("Unsupported function $f") + end +end + function Base.unsafe_convert(::Type{LibGraphBLAS.GrB_BinaryOp}, op::TypedBinaryOperator{F, X, Y, Z}) where {F, X, Y, Z} if !op.loaded if op.builtin @@ -112,7 +123,7 @@ function Base.unsafe_convert(::Type{LibGraphBLAS.GrB_BinaryOp}, op::TypedBinaryO return nothing end opref = Ref{LibGraphBLAS.GrB_BinaryOp}() - binaryopfn_C = @cfunction($binaryopfn, Cvoid, (Ptr{Z}, Ref{X}, Ref{Y})) + binaryopfn_C = cbinary(binaryopfn, X, Y, Z) op.keepalive = (binaryopfn, binaryopfn_C) @wraperror LibGraphBLAS.GB_BinaryOp_new(opref, binaryopfn_C, gbtype(Z), gbtype(X), gbtype(Y), string(fn)) op.p = opref[] diff --git a/src/unpack.jl b/src/unpack.jl index a51e8baa..6bbf39ba 100644 --- a/src/unpack.jl +++ b/src/unpack.jl @@ -254,11 +254,11 @@ end # we will never attachfinalizer here because it is assumed that this is a temporary unpack. function tempunpack!(A::AbstractGBArray, sparsity::Dense; order = ColMajor(), incrementindices = false) shallowA = isshallow(A) - out = unpack!(A, sparsity; order, incrementindices) + out = unsafeunpack!(A, sparsity; order, incrementindices) function repack!(mat, shallow = shallowA; order = order, decrementindices = incrementindices) return unsafepack!(A, mat, shallow; order, decrementindices) end - return (out..., repack!) + return (out, repack!) end function tempunpack!(A::AbstractGBArray, sparsity::Sparse; order = ColMajor(), incrementindices = false) From 7db3e0c06562a8fa8cd0a5dd523eb7bb2aad7a79 Mon Sep 17 00:00:00 2001 From: Ray Kimmerer Date: Thu, 22 Sep 2022 17:18:46 -0400 Subject: [PATCH 05/16] begin adding other unpack types --- test/chainrules/selectrules.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/chainrules/selectrules.jl b/test/chainrules/selectrules.jl index d703d1c7..77a49bb5 100644 --- a/test/chainrules/selectrules.jl +++ b/test/chainrules/selectrules.jl @@ -19,7 +19,7 @@ end @testset "Sparse" begin - X = GBMatrix(sprand(10, 10, 0.4)) + X = GBMatrix(sprand(4, 4, 0.5)) print(X) test_frule(select, diag, X) test_rrule(select, diag, X) From 584fcef40f730ef592f0369e4938f22dec80d91f Mon Sep 17 00:00:00 2001 From: Ray Kimmerer Date: Thu, 22 Sep 2022 17:19:02 -0400 Subject: [PATCH 06/16] begin adding other unpack types --- src/chainrules/mulrules.jl | 20 +++++------ src/operations/broadcasts.jl | 3 ++ src/unpack.jl | 55 +++++++++++++++++++++++++++++- test/chainrules/chainrulesutils.jl | 6 ++-- 4 files changed, 71 insertions(+), 13 deletions(-) diff --git a/src/chainrules/mulrules.jl b/src/chainrules/mulrules.jl index 81d2e404..b55c76a1 100644 --- a/src/chainrules/mulrules.jl +++ b/src/chainrules/mulrules.jl @@ -27,8 +27,8 @@ function rrule( ::typeof((+, *)) ) function mulpullback(ΔΩ) - ∂A = *(unthunk(ΔΩ), B', (+, *); mask=A) - ∂B = *(A', unthunk(ΔΩ), (+, *); mask=B) + ∂A = *(unthunk(ΔΩ), B', (+, *); mask=Structural(A)) + ∂B = *(A', unthunk(ΔΩ), (+, *); mask=Structural(B)) return NoTangent(), ∂A, ∂B, NoTangent() end return *(A, B), mulpullback @@ -55,8 +55,8 @@ function rrule( ::typeof((+, /)) ) function mulpullback(ΔΩ) - ∂A = *(unthunk(ΔΩ), one(eltype(A)) ./ B', (+, *); mask=A) - ∂B = (zero(eltype(A)) .- *(A', unthunk(ΔΩ); mask=B)) ./ (B .^ 2.) + ∂A = *(unthunk(ΔΩ), one(eltype(A)) ./ B', (+, *); mask=Structural(A)) + ∂B = (zero(eltype(A)) .- *(A', unthunk(ΔΩ); mask=Structural(B))) ./ (B .^ 2.) return NoTangent(), ∂A, ∂B, NoTangent() end return *(A, B, (+, /)), mulpullback @@ -82,8 +82,8 @@ function rrule( ::typeof((+, +)) ) function mulpullback(ΔΩ) - ∂A = *(unthunk(ΔΩ), B', (+, first); mask=A) - ∂B = *(A', unthunk(ΔΩ), (+, second); mask=B) + ∂A = *(unthunk(ΔΩ), B', (+, first); mask=Structural(A)) + ∂B = *(A', unthunk(ΔΩ), (+, second); mask=Structural(B)) return NoTangent(), ∂A, ∂B, NoTangent() end return *(A, B, (+, +)), mulpullback @@ -109,8 +109,8 @@ function rrule( ::typeof((+, -)) ) function mulpullback(ΔΩ) - ∂A = *(unthunk(ΔΩ), B', (+, first); mask=A) - ∂B = *(A', zero(eltype(unthunk(ΔΩ))) .- unthunk(ΔΩ), (+, second); mask=B) + ∂A = *(unthunk(ΔΩ), B', (+, first); mask=Structural(A)) + ∂B = *(A', zero(eltype(unthunk(ΔΩ))) .- unthunk(ΔΩ), (+, second); mask=Structural(B)) return NoTangent(), ∂A, ∂B, NoTangent() end return *(A, B, (+, -)), mulpullback @@ -136,7 +136,7 @@ function rrule( ::typeof((+, first)) ) function mulpullback(ΔΩ) - ∂A = *(unthunk(ΔΩ), B', (+, first); mask=A) + ∂A = *(unthunk(ΔΩ), B', (+, first); mask=Structural(A)) ∂B = NoTangent() # perhaps this should be ZeroTangent(), not sure. return NoTangent(), ∂A, ∂B, NoTangent() end @@ -164,7 +164,7 @@ function rrule( ) function mulpullback(ΔΩ) ∂A = NoTangent() - ∂B = *(A', unthunk(ΔΩ), (+, second); mask=B) + ∂B = *(A', unthunk(ΔΩ), (+, second); mask=Structural(B)) return NoTangent(), ∂A, ∂B, NoTangent() end return *(A, B, (+, second)), mulpullback diff --git a/src/operations/broadcasts.jl b/src/operations/broadcasts.jl index ea77aa1e..dd7e5a95 100644 --- a/src/operations/broadcasts.jl +++ b/src/operations/broadcasts.jl @@ -315,6 +315,9 @@ end end end +# function Base.broadcasted(::Union{<:GBMatrixStyle}, ::Type{X}, A::AbstractGBMatrix) +# +# end ## Really ugly overloads to make A .= 3 work correctly # TODO go through the broadcast code and figuring out how this should be done. function Base.materialize!( diff --git a/src/unpack.jl b/src/unpack.jl index 6bbf39ba..f8fa0062 100644 --- a/src/unpack.jl +++ b/src/unpack.jl @@ -5,7 +5,7 @@ function _unpackdensematrix!( szA = size(A) desc = _handledescriptor(desc) Csize = Ref{LibGraphBLAS.GrB_Index}(length(A) * sizeof(T)) - values = Ref{Ptr{Cvoid}}(Ptr{T}()) + values = Ref{Ptr{Cvoid}}(C_NULL) isiso = Ref{Bool}(false) @wraperror LibGraphBLAS.GxB_Matrix_unpack_FullC( A, @@ -189,6 +189,59 @@ function _unpackcsrmatrix!( vals end +function _unpackbitmapmatrix!( + A::AbstractGBArray{T}; + desc = nothing, attachfinalizer = false +) + szA = size(A) + desc = _handledescriptor(desc) + Csize = Ref{LibGraphBLAS.GrB_Index}(length(A) * sizeof(T)) + Bsize = Ref{LibGraphBLAS.GrB_Index}(length(A) * sizeof(Bool)) + values = Ref{Ptr{Cvoid}}(C_NULL) + bytemap = Ref{Ptr{Bool}}(C_NULL) + isiso = Ref{Bool}(false) + nnz = Ref{LibGraphBLAS.GrB_Index}(nnz(A)) + @wraperror LibGraphBLAS.GxB_Matrix_unpack_BitmapC( + A, + bytemap, + values, + Bsize, + Csize, + isiso, + nnz, + desc + ) + v = unsafe_wrap(Array, Ptr{T}(values[]), szA...) + b = unsafe_wrap(Array, bytemap[], szA...) + if attachfinalizer + v = finalizer(v) do f + _jlfree(f) + end + b = finalizer(b) do f + _jlfree(f) + end + end + return v, b +end + +function _unpackbitmapmatrixR!( + A::AbstractGBArray{T}; + desc = nothing, attachfinalizer = false +) + +end + +function _unpackhypermatrix!( + A::AbstractGBArray{T}; + desc = nothing, incrementindices = false, attachfinalizer = false +) +end + +function _unpackhypermatrixR!( + A::AbstractGBArray{T}; + desc = nothing, incrementindices = false, attachfinalizer = false +) +end function unsafeunpack!( A::AbstractGBVector{T}, ::Dense; order = ColMajor(), attachfinalizer = false, incrementindices = false diff --git a/test/chainrules/chainrulesutils.jl b/test/chainrules/chainrulesutils.jl index 480e3239..4dec17e9 100644 --- a/test/chainrules/chainrulesutils.jl +++ b/test/chainrules/chainrulesutils.jl @@ -1,5 +1,5 @@ #Required for ChainRulesTestUtils -function FiniteDifferences.to_vec(M::GBMatrix) +function FiniteDifferences.to_vec(M::SuiteSparseGraphBLAS.AbstractGBMatrix) x, back = FiniteDifferences.to_vec(Matrix(M)) function backtomat(xvec) M2 = GBMatrix(back(xvec)) @@ -8,7 +8,7 @@ function FiniteDifferences.to_vec(M::GBMatrix) return x, backtomat end -function FiniteDifferences.to_vec(v::GBVector) +function FiniteDifferences.to_vec(v::SuiteSparseGraphBLAS.AbstractGBVector) x, back = FiniteDifferences.to_vec(Vector(v)) function backtovec(xvec) v2 = GBVector(back(xvec)) @@ -17,6 +17,8 @@ function FiniteDifferences.to_vec(v::GBVector) return x, backtovec end +FiniteDifferences.to_vec(P::SuiteSparseGraphBLAS.Structural) = FiniteDifferences.to_vec(parent(P)) + function test_to_vec(x::T; check_inferred=true) where {T} check_inferred && @inferred FiniteDifferences.to_vec(x) x_vec, back = FiniteDifferences.to_vec(x) From 05eac0f19c55b6b0c004dcd0c58866e5f3a72872 Mon Sep 17 00:00:00 2001 From: Ray Kimmerer Date: Mon, 26 Sep 2022 17:33:00 -0400 Subject: [PATCH 07/16] transfer wip to antarctic --- src/SuiteSparseGraphBLAS.jl | 8 +- src/convert.jl | 26 ++++ src/mem.jl | 2 +- src/options.jl | 4 +- src/pack.jl | 84 +++++++--- src/types.jl | 11 +- src/unpack.jl | 296 +++++++++++++++++++++++++++++------- 7 files changed, 339 insertions(+), 92 deletions(-) create mode 100644 src/convert.jl diff --git a/src/SuiteSparseGraphBLAS.jl b/src/SuiteSparseGraphBLAS.jl index e03ba5a0..e89986ee 100644 --- a/src/SuiteSparseGraphBLAS.jl +++ b/src/SuiteSparseGraphBLAS.jl @@ -61,6 +61,12 @@ include("scalar.jl") include("vector.jl") include("matrix.jl") include("abstractgbarray.jl") + +# EXPERIMENTAL array types: +include("shallowtypes.jl") +include("oriented.jl") + +include("convert.jl") include("random.jl") # Miscellaneous Operations include("print.jl") @@ -96,10 +102,8 @@ include("serialization.jl") include("misc.jl") include("mmread.jl") # include("iterator.jl") -include("oriented.jl") include("solvers/klu.jl") include("solvers/umfpack.jl") -include("shallowtypes.jl") export SparseArrayCompat export LibGraphBLAS diff --git a/src/convert.jl b/src/convert.jl new file mode 100644 index 00000000..03b3cba5 --- /dev/null +++ b/src/convert.jl @@ -0,0 +1,26 @@ +# First we'll just support element type conversions. +# This is crucial since we can't pass DataTypes to UDF handlers. + +# pass through for most cases +conform(M::AbstractGBArray) = M + +function Base.convert(::Type{M}, A::AbstractGBArray; fill = getfill(A)) where {T, M<:AbstractGBArray{T}} + isabstracttype(M) && throw(ArgumentError("$M is an abstract type, which cannot be constructed.")) + sparsity = sparsitystatus(A) + x = tempunpack_noformat!(A) + repack! = x[end] + values = x[end - 1] + indices = x[begin:end-2] + display(typeof.(indices)) + display(typeof(values)) + newvalues = unsafe_wrap(Array, _sizedjlmalloc(length(values), T), size(values)) + display(typeof(newvalues)) + copyto!(newvalues, values) + newindices = _copytoraw.(indices) + B = M(size(A); fill) + unsafepack!(B, newindices..., newvalues, false) +end + +# TODO: Implement this? No strong reason not to? +Base.convert(::Type{M}, ::AbstractGBArray; fill = nothing) where {M<:AbstractGBShallowArray} = + throw(ArgumentError("Cannot convert into a shallow array.")) diff --git a/src/mem.jl b/src/mem.jl index cf070911..fc833a70 100644 --- a/src/mem.jl +++ b/src/mem.jl @@ -18,7 +18,7 @@ function _copytoraw(A::DenseVecOrMat{T}) where {T} sz = sizeof(A) ptr = _jlmalloc(sz, T) unsafe_copyto!(ptr, pointer(A), length(A)) - return unsafe_wrap(Array, ptr, length(A)) + return unsafe_wrap(Array, ptr, size(A)) end function _copytoraw(A::SparseMatrixCSC) diff --git a/src/options.jl b/src/options.jl index 4d468d9c..233f3155 100644 --- a/src/options.jl +++ b/src/options.jl @@ -147,13 +147,13 @@ function setstorageorder!(A::AbstractGBArray, o::StorageOrders.StorageOrder) end shapetoconst(::Dense) = GBDENSE -shapetoconst(::Bitmap) = GBBITMAP +shapetoconst(::Bytemap) = GBBITMAP shapetoconst(::Sparse) = GBSPARSE shapetoconst(::Hypersparse) = GBHYPER function consttoshape(c) c == GBDENSE && (return Dense()) - c == GBBITMAP && (return Bitmap()) + c == GBBITMAP && (return Bytemap()) c == GBSPARSE && (return Sparse()) c == GBHYPER && (return Hypersparse()) end diff --git a/src/pack.jl b/src/pack.jl index f45bcdc7..4de5184d 100644 --- a/src/pack.jl +++ b/src/pack.jl @@ -1,35 +1,73 @@ function _packdensematrix!( A::AbstractGBArray{T}, M::VecOrMat{T}; - desc = nothing + desc = nothing, order = ColMajor() ) where {T} desc = _handledescriptor(desc) - Csize = length(A) * sizeof(T) + Csize = length(M) * sizeof(T) ptr = pointer(M) - @wraperror LibGraphBLAS.GxB_Matrix_pack_FullC( - A, - Ref{Ptr{Cvoid}}(ptr), - Csize, - false, #isuniform - desc - ) + isiso = length(M) == 1 && length(A) != 1 + if order === ColMajor() + @wraperror LibGraphBLAS.GxB_Matrix_pack_FullC( + A, + Ref{Ptr{Cvoid}}(ptr), + Csize, + isiso, #isuniform + desc + ) + elseif order === RowMajor() + @wraperror LibGraphBLAS.GxB_Matrix_pack_FullR( + A, + Ref{Ptr{Cvoid}}(ptr), + Csize, + isiso, #isuniform + desc + ) + else + throw(ArgumentError("order must be either RowMajor() or ColMajor()")) + end return A end -function _packdensematrixR!( - A::AbstractGBArray{T}, M::VecOrMat{T}; - desc = nothing -) where {T} +function _packbitmap!( + A::AbstractGBArray{T}, bytemap::VecOrMat{Int8}, values::VecOrMat{T}; + desc = nothing, order = ColMajor() +) where T desc = _handledescriptor(desc) - Csize = length(A) * sizeof(T) - ptr = pointer(M) - @wraperror LibGraphBLAS.GxB_Matrix_pack_FullR( - A, - Ref{Ptr{Cvoid}}(ptr), - Csize, - false, #isuniform - desc - ) - return A + valsize = length(A) * sizeof(T) + bytesize = length(A) * sizeof(eltype(bytemap)) + isiso = (length(values) == 1) && (length(A) != 1) + nvals = sum(bytemap) + bytepointer = pointer(bytemap) + valpointer = pointer(values) + bytepointer = Ref{Ptr{Int8}}(bytepointer) + valpointer = Ref{Ptr{Cvoid}}(valpointer) + + if order === ColMajor() + @wraperror LibGraphBLAS.GxB_Matrix_pack_BitmapC( + A, + bytepointer, + valpointer, + bytesize, + valsize, + isiso, + nvals, + desc + ) + elseif order === RowMajor() + @wraperror LibGraphBLAS.GxB_Matrix_pack_BitmapR( + A, + bytepointer, + valpointer, + bytesize, + valsize, + isiso, + nvals, + desc + ) + else + throw(ArgumentError("order must be either RowMajor() or ColMajor()")) + end + end function _packcscmatrix!( diff --git a/src/types.jl b/src/types.jl index 3acbdc56..b6663b13 100644 --- a/src/types.jl +++ b/src/types.jl @@ -1,6 +1,6 @@ abstract type AbstractSparsity end struct Dense <: AbstractSparsity end -struct Bitmap <: AbstractSparsity end +struct Bytemap <: AbstractSparsity end struct Sparse <: AbstractSparsity end struct Hypersparse <: AbstractSparsity end @@ -31,15 +31,6 @@ function TypedUnaryOperator(fn::F, ::Type{X}) where {F, X} return TypedUnaryOperator(fn, X, Base._return_type(fn, Tuple{X})) end -# @generated function getcfunc(f::F, ::Type{Z}, ::Type{X}) where {X, Z, F} -# pmodule = Symbol(parentmodule(F)) -# name = Symbol(F.instance) -# newname = Symbol(name, :(___)) -# quote -# return @cfunction($pmodule.$name, Cvoid, (Ptr{$Z}, Ref{$X})) -# end -# end - @generated function cunary(f::F, ::Type{X}, ::Type{Z}) where {F, X, Z} if Base.issingletontype(F) :(@cfunction($(F.instance), Cvoid, (Ptr{Z}, Ref{X}))) diff --git a/src/unpack.jl b/src/unpack.jl index f8fa0062..0928516c 100644 --- a/src/unpack.jl +++ b/src/unpack.jl @@ -1,12 +1,12 @@ function _unpackdensematrix!( A::AbstractGBVector{T}; - desc = nothing, attachfinalizer = false + desc = nothing, attachfinalizer = false, allowiso = false ) where {T} szA = size(A) desc = _handledescriptor(desc) Csize = Ref{LibGraphBLAS.GrB_Index}(length(A) * sizeof(T)) values = Ref{Ptr{Cvoid}}(C_NULL) - isiso = Ref{Bool}(false) + isiso = Ref{Bool}(allowiso ? true : C_NULL) @wraperror LibGraphBLAS.GxB_Matrix_unpack_FullC( A, values, @@ -14,7 +14,7 @@ function _unpackdensematrix!( isiso, desc ) - v = unsafe_wrap(Array, Ptr{T}(values[]), szA...) + v = unsafe_wrap(Array, Ptr{T}(values[]), szA) if attachfinalizer return finalizer(v) do x _jlfree(x) @@ -26,13 +26,13 @@ end function _unpackdensematrix!( A::AbstractGBMatrix{T}; - desc = nothing, attachfinalizer = false + desc = nothing, attachfinalizer = false, allowiso = false ) where {T} szA = size(A) desc = _handledescriptor(desc) Csize = Ref{LibGraphBLAS.GrB_Index}(length(A) * sizeof(T)) - values = Ref{Ptr{Cvoid}}(Ptr{T}()) - isiso = Ref{Bool}(false) + values = Ref{Ptr{Cvoid}}(C_NULL) + isiso = Ref{Bool}(allowiso ? true : C_NULL) @wraperror LibGraphBLAS.GxB_Matrix_unpack_FullC( A, values, @@ -55,13 +55,13 @@ end function _unpackdensematrixR!( A::AbstractGBArray{T}; - desc = nothing, attachfinalizer = false + desc = nothing, attachfinalizer = false, allowiso = false ) where {T} szA = size(A) desc = _handledescriptor(desc) Csize = Ref{LibGraphBLAS.GrB_Index}(length(A) * sizeof(T)) - values = Ref{Ptr{Cvoid}}(Ptr{T}()) - isiso = Ref{Bool}(false) + values = Ref{Ptr{Cvoid}}(C_NULL) + isiso = Ref{Bool}(allowiso ? true : C_NULL) @wraperror LibGraphBLAS.GxB_Matrix_unpack_FullR( A, values, @@ -79,16 +79,16 @@ end function _unpackcscmatrix!( A::AbstractGBArray{T}; - desc = nothing, incrementindices = true, attachfinalizer = false + desc = nothing, incrementindices = true, attachfinalizer = false, allowiso = false ) where {T} desc = _handledescriptor(desc) - colptr = Ref{Ptr{LibGraphBLAS.GrB_Index}}() - rowidx = Ref{Ptr{LibGraphBLAS.GrB_Index}}() - values = Ref{Ptr{Cvoid}}(Ptr{T}()) + colptr = Ref{Ptr{LibGraphBLAS.GrB_Index}}(C_NULL) + rowidx = Ref{Ptr{LibGraphBLAS.GrB_Index}}(C_NULL) + values = Ref{Ptr{Cvoid}}(C_NULL) colptrsize = Ref{LibGraphBLAS.GrB_Index}() rowidxsize = Ref{LibGraphBLAS.GrB_Index}() valsize = Ref{LibGraphBLAS.GrB_Index}() - isiso = Ref{Bool}(false) + isiso = Ref{Bool}(allowiso ? true : C_NULL) isjumbled = C_NULL nnonzeros = nnz(A) @wraperror LibGraphBLAS.GxB_Matrix_unpack_CSC( @@ -105,11 +105,9 @@ function _unpackcscmatrix!( ) colptr = unsafe_wrap(Array, Ptr{Int64}(colptr[]), size(A, 2) + 1) rowidx = unsafe_wrap(Array, Ptr{Int64}(rowidx[]), nnonzeros) - if isiso[] - vals = unsafe_wrap(Array, Ptr{T}(values[]), 1) - else - vals = unsafe_wrap(Array, Ptr{T}(values[]), nnonzeros) - end + nstored = isiso[] ? 1 : nnonzeros + vals = unsafe_wrap(Array, Ptr{T}(values[]), nstored) + if attachfinalizer colptr = finalizer(colptr) do x _jlfree(x) @@ -122,9 +120,6 @@ function _unpackcscmatrix!( end end - if isiso[] - vals = fill(vals[1], nnonzeros) - end if incrementindices increment!(colptr) increment!(rowidx) @@ -134,16 +129,16 @@ end function _unpackcsrmatrix!( A::AbstractGBArray{T}; - desc = nothing, incrementindices = true, attachfinalizer = false + desc = nothing, incrementindices = true, attachfinalizer = false, allowiso = false ) where {T} desc = _handledescriptor(desc) - rowptr = Ref{Ptr{LibGraphBLAS.GrB_Index}}() - colidx = Ref{Ptr{LibGraphBLAS.GrB_Index}}() - values = Ref{Ptr{Cvoid}}(Ptr{T}()) + rowptr = Ref{Ptr{LibGraphBLAS.GrB_Index}}(C_NULL) + colidx = Ref{Ptr{LibGraphBLAS.GrB_Index}}(C_NULL) + values = Ref{Ptr{Cvoid}}(C_NULL) rowptrsize = Ref{LibGraphBLAS.GrB_Index}() colidxsize = Ref{LibGraphBLAS.GrB_Index}() valsize = Ref{LibGraphBLAS.GrB_Index}() - isiso = Ref{Bool}(false) + isiso = Ref{Bool}(allowiso ? true : C_NULL) isjumbled = C_NULL nnonzeros = nnz(A) @wraperror LibGraphBLAS.GxB_Matrix_unpack_CSC( @@ -160,11 +155,8 @@ function _unpackcsrmatrix!( ) rowptr = unsafe_wrap(Array, Ptr{Int64}(rowptr[]), size(A, 1) + 1) colidx = unsafe_wrap(Array, Ptr{Int64}(rowidx[]), colidxsize[]) - if isiso[] - vals = unsafe_wrap(Array, Ptr{T}(values[]), 1) - else - vals = unsafe_wrap(Array, Ptr{T}(values[]), nnonzeros) - end + nstored = isiso[] ? 1 : nnonzeros + vals = unsafe_wrap(Array, Ptr{T}(values[]), nstored) if attachfinalizer rowptr = finalizer(rowptr) do x _jlfree(x) @@ -176,10 +168,6 @@ function _unpackcsrmatrix!( _jlfree(x) end end - - if isiso[] - vals = fill(vals[1], nnonzeros) - end if incrementindices increment!(rowptr) increment!(colidx) @@ -191,15 +179,15 @@ end function _unpackbitmapmatrix!( A::AbstractGBArray{T}; - desc = nothing, attachfinalizer = false -) + desc = nothing, attachfinalizer = false, allowiso = false +) where T szA = size(A) desc = _handledescriptor(desc) Csize = Ref{LibGraphBLAS.GrB_Index}(length(A) * sizeof(T)) Bsize = Ref{LibGraphBLAS.GrB_Index}(length(A) * sizeof(Bool)) values = Ref{Ptr{Cvoid}}(C_NULL) bytemap = Ref{Ptr{Bool}}(C_NULL) - isiso = Ref{Bool}(false) + isiso = Ref{Bool}(allowiso ? true : C_NULL) nnz = Ref{LibGraphBLAS.GrB_Index}(nnz(A)) @wraperror LibGraphBLAS.GxB_Matrix_unpack_BitmapC( A, @@ -211,8 +199,9 @@ function _unpackbitmapmatrix!( nnz, desc ) - v = unsafe_wrap(Array, Ptr{T}(values[]), szA...) - b = unsafe_wrap(Array, bytemap[], szA...) + nstored = isiso[] ? 1 : szA + v = unsafe_wrap(Array, Ptr{T}(values[]), nstored) + b = unsafe_wrap(Array, bytemap[], szA) if attachfinalizer v = finalizer(v) do f _jlfree(f) @@ -226,26 +215,169 @@ end function _unpackbitmapmatrixR!( A::AbstractGBArray{T}; - desc = nothing, attachfinalizer = false -) - + desc = nothing, attachfinalizer = false, allowiso = false +) where T + szA = size(A) + desc = _handledescriptor(desc) + Csize = Ref{LibGraphBLAS.GrB_Index}(length(A) * sizeof(T)) + Bsize = Ref{LibGraphBLAS.GrB_Index}(length(A) * sizeof(Int8)) + values = Ref{Ptr{Cvoid}}(C_NULL) + bytemap = Ref{Ptr{Int8}}(C_NULL) + isiso = Ref{Bool}(allowiso ? true : C_NULL) + nonzeros = Ref{LibGraphBLAS.GrB_Index}(0) + @wraperror LibGraphBLAS.GxB_Matrix_unpack_BitmapR( + A, + bytemap, + values, + Bsize, + Csize, + isiso, + nonzeros, + desc + ) + nstored = isiso[] ? 1 : szA + v = unsafe_wrap(Array, Ptr{T}(values[]), nstored) + b = unsafe_wrap(Array, bytemap[], szA) + if attachfinalizer + v = finalizer(v) do f + _jlfree(f) + end + b = finalizer(b) do f + _jlfree(f) + end + end + return v, b end function _unpackhypermatrix!( A::AbstractGBArray{T}; - desc = nothing, incrementindices = false, attachfinalizer = false -) + desc = nothing, incrementindices = false, attachfinalizer = false, allowiso = false +) where T + desc = _handledescriptor(desc) + colptr = Ref{Ptr{LibGraphBLAS.GrB_Index}}(C_NULL) + colidx = Ref{Ptr{LibGraphBLAS.GrB_Index}}(C_NULL) + rowidx = Ref{Ptr{LibGraphBLAS.GrB_Index}}(C_NULL) + values = Ref{Ptr{Cvoid}}(C_NULL) + colptrsize = Ref{LibGraphBLAS.GrB_Index}() + colidxsize = Ref{LibGraphBLAS.GrB_Index}() + rowidxsize = Ref{LibGraphBLAS.GrB_Index}() + valsize = Ref{LibGraphBLAS.GrB_Index}() + nvec = Ref{LibGraphBLAS.GrB_Index}() + isiso = Ref{Bool}(allowiso ? true : C_NULL) + isjumbled = C_NULL + nnonzeros = nnz(A) + + @wraperror LibGraphBLAS.GxB_Matrix_unpack_HyperCSC( + A, + colptr, + colidx, + rowidx, + values, + colptrsize, + colidxsize, + rowidxsize, + valsize, + isiso, + nvec, + isjumbled, + desc + ) + colptr = unsafe_wrap(Array, Ptr{Int64}(colptr[]), size(A, 2) + 1) + colidx = unsafe_wrap(Array, Ptr{Int64}(colidx), colidxsize[]) + rowidx = unsafe_wrap(Array, Ptr{Int64}(rowidx), nnonzeros) + nstored = isiso[] ? 1 : nnonzeros + vals = unsafe_wrap(Array, Ptr{T}(values[]), nstored) + + if attachfinalizer + colptr = finalizer(colptr) do x + _jlfree(x) + end + colidx = finalizer(colidx) do x + _jlfree(x) + end + rowidx = finalizer(rowidx) do x + _jlfree(x) + end + vals = finalizer(vals) do x + _jlfree(x) + end + end + + if incrementindices + increment!(colptr) + increment!(colidx) + increment!(rowidx) + end + return colptr, colidx, rowidx, vals end function _unpackhypermatrixR!( A::AbstractGBArray{T}; - desc = nothing, incrementindices = false, attachfinalizer = false -) + desc = nothing, incrementindices = false, attachfinalizer = false, allowiso = false +) where T +desc = _handledescriptor(desc) + rowptr = Ref{Ptr{LibGraphBLAS.GrB_Index}}(C_NULL) + rowidx = Ref{Ptr{LibGraphBLAS.GrB_Index}}(C_NULL) + colidx = Ref{Ptr{LibGraphBLAS.GrB_Index}}(C_NULL) + values = Ref{Ptr{Cvoid}}(C_NULL) + rowptrsize = Ref{LibGraphBLAS.GrB_Index}() + rowidxsize = Ref{LibGraphBLAS.GrB_Index}() + colidxsize = Ref{LibGraphBLAS.GrB_Index}() + valsize = Ref{LibGraphBLAS.GrB_Index}() + nvec = Ref{LibGraphBLAS.GrB_Index}() + isiso = Ref{Bool}(allowiso ? true : C_NULL) + isjumbled = C_NULL + nnonzeros = nnz(A) + + @wraperror LibGraphBLAS.GxB_Matrix_unpack_HyperCSC( + A, + rowptr, + rowidx, + colidx, + values, + rowptrsize, + rowidxsize, + colidxsize, + valsize, + isiso, + nvec, + isjumbled, + desc + ) + rowptr = unsafe_wrap(Array, Ptr{Int64}(rowptr[]), size(A, 2) + 1) + rowidx = unsafe_wrap(Array, Ptr{Int64}(rowidx[]), rowidxsize[]) + colidx = unsafe_wrap(Array, Ptr{Int64}(colidx[]), nnonzeros) + nstored = isiso[] ? 1 : nnonzeros + vals = unsafe_wrap(Array, Ptr{T}(values[]), nstored) + + if attachfinalizer + rowptr = finalizer(rowptr) do x + _jlfree(x) + end + rowidx = finalizer(rowidx) do x + _jlfree(x) + end + colidx = finalizer(colidx) do x + _jlfree(x) + end + vals = finalizer(vals) do x + _jlfree(x) + end + end + + if incrementindices + increment!(rowptr) + increment!(rowidx) + increment!(colidx) + end + return rowptr, rowidx, colidx, vals end + function unsafeunpack!( A::AbstractGBVector{T}, ::Dense; order = ColMajor(), attachfinalizer = false, incrementindices = false ) where {T} + incrementindices && throw(ArgumentError("Cannot increment indices for Dense unpack.")) wait(A) sparsity = sparsitystatus(A) sparsity === Dense() || (A .+= (similar(A) .= zero(T))) @@ -260,6 +392,7 @@ function unsafeunpack!( A::AbstractGBMatrix{T}, ::Dense; order = ColMajor(), attachfinalizer = false, incrementindices = false ) where {T} + incrementindices && throw(ArgumentError("Cannot increment indices for Dense unpack.")) wait(A) sparsity = sparsitystatus(A) sparsity === Dense() || (A .+= (similar(A) .= zero(T))) @@ -276,11 +409,25 @@ function unsafeunpack!( ) reshape(unsafeunpack!(A, Dense(); attachfinalizer, order, incrementindices), :)::Vector{T} end + unsafeunpack!( - A::AbstractGBArray, - ::Type{Matrix}; attachfinalizer = false, order = ColMajor(), incrementindices = false + A::AbstractGBArray, ::Type{Matrix}; + attachfinalizer = false, order = ColMajor(), incrementindices = false ) = unsafeunpack!(A, Dense(); order, incrementindices, attachfinalizer) +function unsafeunpack!( + A::AbstractGBArray, ::Bytemap; + attachfinalizer = false, order = ColMajor(), incrementindices = false +) + incrementindices && throw(ArgumentError("Cannot increment indices for Bytemap unpack.")) + wait(A) + if order === ColMajor() + return _unpackbitmapmatrix!(A; attachfinalizer) + else + return _unpackbitmapmatrixR!(A; attachfinalizer) + end +end + function unsafeunpack!( A::AbstractGBArray{T}, ::Sparse; order = ColMajor(), incrementindices = true, attachfinalizer = false @@ -304,23 +451,64 @@ function unsafeunpack!(A::AbstractGBArray; attachfinalizer = false, ) return unsafeunpack!(A, sparsity; order, attachfinalizer) end +function unsafeunpack!( + A::AbstractGBArray, ::Hypersparse; + attachfinalizer = false, order = ColMajor(), incrementindices = false +) + wait(A) + if order === ColMajor() + return _unpackhypermatrix!(A; incrementindices, attachfinalizer) + else + return _unpackhypermatrixR!(A; incrementindices, attachfinalizer) + end +end + + + # we will never attachfinalizer here because it is assumed that this is a temporary unpack. function tempunpack!(A::AbstractGBArray, sparsity::Dense; order = ColMajor(), incrementindices = false) shallowA = isshallow(A) out = unsafeunpack!(A, sparsity; order, incrementindices) - function repack!(mat, shallow = shallowA; order = order, decrementindices = incrementindices) + function repack!(mat = out, shallow = shallowA; order = order, decrementindices = incrementindices) return unsafepack!(A, mat, shallow; order, decrementindices) end return (out, repack!) end +# we will never attachfinalizer here because it is assumed that this is a temporary unpack. +function tempunpack!(A::AbstractGBArray, sparsity::Bytemap; order = ColMajor(), incrementindices = false) + shallowA = isshallow(A) + bytemap, values = unsafeunpack!(A, sparsity; order, incrementindices) + function repack!(bytemap = bytemap, values = values, shallow = shallowA; + order = order, decrementindices = incrementindices) + return unsafepack!(A, bytemap, values, shallow; order, decrementindices) + end + return (bytemap, values, repack!) +end + function tempunpack!(A::AbstractGBArray, sparsity::Sparse; order = ColMajor(), incrementindices = false) shallowA = isshallow(A) - out = unsafeunpack!(A, sparsity; order, incrementindices) - function repack!(ptr, idx, nzval, shallow = shallowA; order = order, decrementindices = incrementindices) + ptr, idx, nzval = unsafeunpack!(A, sparsity; order, incrementindices) + function repack!(ptr = ptr, idx = idx, nzval = nzval, shallow = shallowA; + order = order, decrementindices = incrementindices) return unsafepack!(A, ptr, idx, nzval, shallow; order, decrementindices) end - return (out..., repack!) + return (ptr, idx, nzval, repack!) +end + +function tempunpack!(A::AbstractGBArray, sparsity::Hypersparse; order = ColMajor(), incrementindices = false) + shallowA = isshallow(A) + ptr, idx1, idx2, nzval = unsafeunpack!(A, sparsity; order, incrementindices) + function repack!(ptr = ptr, idx1 = idx1, idx2 = idx2, nzval = nzval, shallow = shallowA; + order = order, decrementindices = incrementindices) + return unsafepack!(A, ptr, idx1, idx2, nzval, shallow; order, decrementindices) + end + return (ptr, idx1, idx2, nzval, repack!) +end + +function tempunpack_noformat!(A::AbstractGBArray, incrementindices = false) + sparsity, order = format(A) + return tempunpack!(A, sparsity; order, incrementindices) end # TODO: BITMAP && HYPER From e0c3e34c6c66ef4dd1bb4262a0a7ca2ca4afe1a0 Mon Sep 17 00:00:00 2001 From: Will Kimmerer Date: Sun, 2 Oct 2022 06:41:28 -0400 Subject: [PATCH 08/16] WIP fill generalization, container type generalization --- Project.toml | 1 + docs/src/arrays.md | 2 +- src/SuiteSparseGraphBLAS.jl | 4 +- src/abstractgbarray.jl | 75 +++-- src/abstracts.jl | 9 +- src/chainrules/mulrules.jl | 6 +- src/constants.jl | 9 +- src/convert.jl | 6 +- src/matrix.jl | 148 +-------- src/operations/ewise.jl | 25 +- src/operations/extract.jl | 4 +- src/operations/kronecker.jl | 8 +- src/operations/map.jl | 37 ++- src/operations/mul.jl | 10 +- src/operations/operationutils.jl | 37 ++- src/operations/reduce.jl | 8 +- src/operations/select.jl | 2 +- src/operations/sort.jl | 2 +- src/operations/transpose.jl | 4 +- src/operators/binaryops.jl | 31 +- src/operators/monoids.jl | 8 +- src/operators/operatorutils.jl | 8 +- src/operators/unaryops.jl | 4 +- src/options.jl | 56 +--- src/oriented.jl | 253 +++++++------- src/pack.jl | 5 +- src/shallowtypes.jl | 33 -- src/types.jl | 550 ++++++++++++++++++++++++++++++- src/unpack.jl | 1 - src/vector.jl | 155 ++++----- test/Manifest.toml | 215 ++++++++++++ test/asjulia.jl | 40 --- test/gbarray.jl | 12 +- test/issues.jl | 8 +- test/operations/kron.jl | 2 +- test/runtests.jl | 1 + test/types.jl | 26 ++ 37 files changed, 1190 insertions(+), 615 deletions(-) create mode 100644 test/Manifest.toml delete mode 100644 test/asjulia.jl create mode 100644 test/types.jl diff --git a/Project.toml b/Project.toml index e7d4a434..85407621 100644 --- a/Project.toml +++ b/Project.toml @@ -17,6 +17,7 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" StorageOrders = "e9177fbf-8fde-426c-9425-4eed0f22262a" SuiteSparse = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" +Tricks = "410a4b4d-49e4-4fbc-ab6d-cb71b17b3775" [compat] ChainRulesCore = "1" diff --git a/docs/src/arrays.md b/docs/src/arrays.md index a4932f3b..d42d1e20 100644 --- a/docs/src/arrays.md +++ b/docs/src/arrays.md @@ -3,7 +3,7 @@ There are two primary array types in SuiteSparseGraphBLAS.jl: [`GBVector`](@ref) and [`GBMatrix`](@ref), as well as a few specialized versions of those array types. The full type hierarchy is: ``` -AbstractGBArray{T, N, F} <: AbstractSparseArray{T, N} +AbstractGBArray{T, F, N} <: AbstractSparseArray{Union{T, F}, N} ├ N = 2 ─ AbstractGBMatrix{T, F} │ ├─ GBMatrix{T, F} │ └─ OrientedGBMatrix{T, F, O} diff --git a/src/SuiteSparseGraphBLAS.jl b/src/SuiteSparseGraphBLAS.jl index e89986ee..482bbe48 100644 --- a/src/SuiteSparseGraphBLAS.jl +++ b/src/SuiteSparseGraphBLAS.jl @@ -110,9 +110,9 @@ export LibGraphBLAS # export UnaryOps, BinaryOps, Monoids, Semirings #Submodules export unaryop, binaryop, Monoid, semiring #UDFs export Descriptor #Types -export gbset, gbget # global and object specific options. +export gbset, gbget, getfill, setfill # global and object specific options. # export xtype, ytype, ztype #Determine input/output types of operators -export GBScalar, GBVector, GBMatrix #arrays +export GBScalar, GBVector, GBMatrix, GBMatrixC, GBMatrixR #arrays export lgamma, gamma, erf, erfc #reexport of SpecialFunctions. # Function arguments not found elsewhere in Julia diff --git a/src/abstractgbarray.jl b/src/abstractgbarray.jl index ce5fea07..0fa4c621 100644 --- a/src/abstractgbarray.jl +++ b/src/abstractgbarray.jl @@ -1,18 +1,26 @@ # AbstractGBArray functions: -Base.unsafe_convert(::Type{LibGraphBLAS.GrB_Matrix}, A::AbstractGBArray) = A.p[] -Base.unsafe_convert(::Type{LibGraphBLAS.GrB_Vector}, A::AbstractGBVector) = LibGraphBLAS.GrB_Vector(A.p[]) +# AbstractGBArray "traits": -_newGrBRef() = finalizer(Ref{LibGraphBLAS.GrB_Matrix}()) do ref - @wraperror LibGraphBLAS.GrB_Matrix_free(ref) -end -function _copyGrBMat(r::Base.RefValue{LibGraphBLAS.GrB_Matrix}) - C = Ref{LibGraphBLAS.GrB_Matrix}() - LibGraphBLAS.GrB_Matrix_dup(C, r[]) - return C -end +# output checking -_canbeoutput(A::AbstractGBArray) = true +""" + _canbeoutput(A::AbstractGBArray)::Bool + +Whether `A` may be used as the output argument to a GraphBLAS function. +""" +_canbeoutput(::AbstractGBArray) = true +_canbeoutput(::AbstractGBShallowArray) = false + +""" + _hasconstantorder(A::AbstractGBArray)::Bool + +Whether `A` may have its storageorder changed. +""" +_hasconstantorder(::AbstractGBArray) = true +# GBMatrix is the only one which may, under ordinary circumstances +# have its order changed. +_hasconstantorder(::GBMatrix) = false function SparseArrays.nnz(A::GBArrayOrTranspose) nvals = Ref{LibGraphBLAS.GrB_Index}() @@ -20,7 +28,19 @@ function SparseArrays.nnz(A::GBArrayOrTranspose) return Int64(nvals[]) end -Base.eltype(::Type{GBArrayOrTranspose{T}}) where{T} = T +# Base functions: + +Base.IndexStyle(::AbstractGBArray) = IndexCartesian() +Base.eltype(::AbstractGBArray{T, F}) where {T, F} = Union{T, F} +Base.eltype(::Type{<:AbstractGBArray{T, F}}) where{T, F} = Union{T, F} + +storedeltype(x) = eltype(x) +storedeltype(::AbstractGBArray{T}) where T = T +# TODO: The transpose + +Base.unsafe_convert(::Type{LibGraphBLAS.GrB_Matrix}, A::AbstractGBArray) = A.p[] +Base.unsafe_convert(::Type{LibGraphBLAS.GrB_Vector}, A::AbstractGBVector) = + LibGraphBLAS.GrB_Vector(A.p[]) """ empty!(A::AbstractGBArray) @@ -275,14 +295,15 @@ for T ∈ valid_vec end function build!( - A::AbstractGBMatrix{T}, I::AbstractVector{<:Integer}, J::AbstractVector{<:Integer}, X::AbstractVector{T}; + A::AbstractGBMatrix{T}, I::AbstractVector{<:Integer}, J::AbstractVector{<:Integer}, X::AbstractVector; combine = + - ) where {T} + ) where T _canbeoutput(A) || throw(ShallowException()) combine = binaryop(combine, T) I isa Vector || (I = collect(I)) J isa Vector || (J = collect(J)) X isa Vector || (X = collect(X)) + X = convert(Vector{T}, X) nnz(A) == 0 || throw(OutputNotEmptyError("Cannot build matrix with existing elements")) length(X) == length(I) == length(J) || DimensionMismatch("I, J and X must have the same length") @@ -445,7 +466,7 @@ function subassign!( rereshape = true end @wraperror LibGraphBLAS.GxB_Matrix_subassign(C, mask, - _handleaccum(accum, eltype(C)), parent(A), I, ni, J, nj, desc) + _handleaccum(accum, storedeltype(C)), parent(A), I, ni, J, nj, desc) if rereshape # undo the reshape. Need size(A, 2) here @wraperror LibGraphBLAS.GxB_Matrix_reshape( parent(A), true, sz1, 1, C_NULL) @@ -466,7 +487,7 @@ function subassign!(C::AbstractGBArray{T}, x, I, J; J = decrement!(J) desc = _handledescriptor(desc; out=C) mask = _handlemask!(desc, mask) - _subassign(C, x, I, ni, J, nj, mask, _handleaccum(accum, eltype(C)), desc) + _subassign(C, x, I, ni, J, nj, mask, _handleaccum(accum, storedeltype(C)), desc) increment!(I) increment!(J) return x @@ -537,7 +558,7 @@ function assign!( mask = _handlemask!(desc, mask) I = decrement!(I) J = decrement!(J) - @wraperror LibGraphBLAS.GrB_Matrix_assign(C, mask, _handleaccum(accum, eltype(C)), parent(A), I, ni, J, nj, desc) + @wraperror LibGraphBLAS.GrB_Matrix_assign(C, mask, _handleaccum(accum, storedeltype(C)), parent(A), I, ni, J, nj, desc) increment!(I) increment!(J) return A @@ -554,7 +575,7 @@ function assign!(C::AbstractGBArray{T}, x, I, J; J = decrement!(J) desc = _handledescriptor(desc; out=C) mask = _handlemask!(desc, mask) - _assign(C, x, I, ni, J, nj, mask, _handleaccum(accum, eltype(C)), desc) + _assign(C, x, I, ni, J, nj, mask, _handleaccum(accum, storedeltype(C)), desc) increment!(I) increment!(J) return x @@ -580,8 +601,6 @@ function Base.size(v::AbstractGBVector) return (Int64(nrows[]),) end -Base.eltype(::Type{AbstractGBVector{T}}) where{T} = T - function Base.deleteat!(v::AbstractGBVector, i) @wraperror LibGraphBLAS.GrB_Matrix_removeElement(v, decrement!(i), 0) return v @@ -613,7 +632,7 @@ end # This does not conform to the normal definition with a lazy wrapper. function LinearAlgebra.Diagonal(v::AbstractGBVector, k::Integer=0; desc = nothing) s = size(v, 1) - C = GBMatrix{eltype(v)}(s, s; fill = v.fill) + C = GBMatrix{storedeltype(v)}(s, s; fill = v.fill) desc = _handledescriptor(desc) # Switch ptr to a Vector to trick GraphBLAS. # This is allowed since GrB_Vector is a GrB_Matrix internally. @@ -717,17 +736,17 @@ function Base.isstored(v::AbstractGBVector, i::Int) end # UDT versions of Vector functions, which just require one def of each. - -function build!(v::AbstractGBVector{T}, I::Vector{<:Integer}, X::Vector{T}; combine = +) where {T} +function build!(v::AbstractGBVector{T}, I::AbstractVector{<:Integer}, X::AbstractVector; combine = +) where T nnz(v) == 0 || throw(OutputNotEmptyError("Cannot build vector with existing elements")) _canbeoutput(v) || throw(ShallowException()) I isa Vector || (I = collect(I)) X isa Vector || (X = collect(X)) + X = convert(Vector{T}, X) length(X) == length(I) || DimensionMismatch("I and X must have the same length") combine = binaryop(combine, T) decrement!(I) @wraperror LibGraphBLAS.GrB_Matrix_build_UDT( - Ptr{LibGraphBLAS.GrB_Vector}(v.p[]), + v, I, # TODO, fix this ugliness by switching to the GBVector build internally. zeros(LibGraphBLAS.GrB_Index, length(I)), @@ -782,10 +801,10 @@ function SparseArrays.nonzeroinds(v::GBVector{T}) where {T} return increment!(I) end -function build!(v::GBVector{T}, I::Vector{<:Integer}, x::T) where {T} +function build!(v::GBVector{T}, I::Vector{<:Integer}, x::T2) where {T, T2} _canbeoutput(v) || throw(ShallowException()) nnz(v) == 0 || throw(OutputNotEmptyError("Cannot build vector with existing elements")) - x = GBScalar(x) + x = GBScalar(convert(T2, x)) decrement!(I) @wraperror LibGraphBLAS.GxB_Matrix_build_Scalar( v, @@ -861,7 +880,7 @@ function Base.getindex( end """ - setfill!(A::AbstractGBArray{T, N, F}, x::F) + setfill!(A::AbstractGBArray{T, F, N}, x::F) Modify the fill value of `A`. The fill type of `A` and the type of `x` must be the same. @@ -871,7 +890,7 @@ function setfill!(A::AbstractGBArray, x) end """ - setfill(A::AbstractGBArray{T, N, F}, x::F2) + setfill(A::AbstractGBArray{T, F, N}, x::F2) Create a new AbstractGBArray with the same underlying data but a new fill `x`. The fill type of `A` and the type of `x` may be different. diff --git a/src/abstracts.jl b/src/abstracts.jl index 331b8e71..5d781f93 100644 --- a/src/abstracts.jl +++ b/src/abstracts.jl @@ -5,7 +5,10 @@ abstract type AbstractSelectOp <: AbstractOp end abstract type AbstractMonoid <: AbstractOp end abstract type AbstractTypedOp{Z} end -abstract type AbstractGBArray{T, N, F} <: AbstractSparseArray{T, UInt64, N} end +abstract type AbstractGBArray{T, F, N} <: AbstractSparseArray{Union{T, F}, UInt64, N} end -const AbstractGBMatrix{T, F} = AbstractGBArray{T, 2, F} -const AbstractGBVector{T, F} = AbstractGBArray{T, 1, F} \ No newline at end of file +const AbstractGBMatrix{T, F} = AbstractGBArray{T, F, 2} +const AbstractGBVector{T, F} = AbstractGBArray{T, F, 1} + +# P = pointer vectors, B = bitmap storage, A = value storage +abstract type AbstractGBShallowArray{T, F, P, B, A, N} <: AbstractGBArray{T, F, N} end diff --git a/src/chainrules/mulrules.jl b/src/chainrules/mulrules.jl index b55c76a1..854ec5c5 100644 --- a/src/chainrules/mulrules.jl +++ b/src/chainrules/mulrules.jl @@ -55,8 +55,8 @@ function rrule( ::typeof((+, /)) ) function mulpullback(ΔΩ) - ∂A = *(unthunk(ΔΩ), one(eltype(A)) ./ B', (+, *); mask=Structural(A)) - ∂B = (zero(eltype(A)) .- *(A', unthunk(ΔΩ); mask=Structural(B))) ./ (B .^ 2.) + ∂A = *(unthunk(ΔΩ), one(storedeltype(A)) ./ B', (+, *); mask=Structural(A)) + ∂B = (zero(storedeltype(A)) .- *(A', unthunk(ΔΩ); mask=Structural(B))) ./ (B .^ 2.) return NoTangent(), ∂A, ∂B, NoTangent() end return *(A, B, (+, /)), mulpullback @@ -110,7 +110,7 @@ function rrule( ) function mulpullback(ΔΩ) ∂A = *(unthunk(ΔΩ), B', (+, first); mask=Structural(A)) - ∂B = *(A', zero(eltype(unthunk(ΔΩ))) .- unthunk(ΔΩ), (+, second); mask=Structural(B)) + ∂B = *(A', zero(storedeltype(unthunk(ΔΩ))) .- unthunk(ΔΩ), (+, second); mask=Structural(B)) return NoTangent(), ∂A, ∂B, NoTangent() end return *(A, B, (+, -)), mulpullback diff --git a/src/constants.jl b/src/constants.jl index 216c93ec..8049d5db 100644 --- a/src/constants.jl +++ b/src/constants.jl @@ -1,8 +1,7 @@ -const GBVecOrMat{T} = Union{<:AbstractGBVector{T}, <:AbstractGBMatrix{T}} -const GBMatrixOrTranspose{T} = Union{<:AbstractGBMatrix{T}, Transpose{T, <:AbstractGBMatrix{T}}} -const GBVectorOrTranspose{T} = Union{<:AbstractGBVector{T}, Transpose{T, <:AbstractGBVector{T}}} -const GBArrayOrTranspose{T} = Union{<:AbstractGBArray{T}, Transpose{T, <:AbstractGBArray{T}}} - +const GBVecOrMat{T, F} = Union{<:AbstractGBVector{T, F}, <:AbstractGBMatrix{T, F}} +const GBMatrixOrTranspose{T, F} = Union{<:AbstractGBMatrix{T, F}, <:Transpose{<:Any, <:AbstractGBMatrix{T, F}}} +const GBVectorOrTranspose{T, F} = Union{<:AbstractGBVector{T, F}, <:Transpose{<:Any, <:AbstractGBVector{T, F}}} +const GBArrayOrTranspose{T, F} = Union{<:AbstractGBArray{T, F}, <:Transpose{<:Any, <:AbstractGBArray{T, F}}} const VecMatOrTrans = Union{<:DenseVecOrMat, <:Transpose{<:Any, <:DenseVecOrMat}} const ptrtogbtype = IdDict{Ptr, GBType}() diff --git a/src/convert.jl b/src/convert.jl index 03b3cba5..cbb3c7d9 100644 --- a/src/convert.jl +++ b/src/convert.jl @@ -4,7 +4,7 @@ # pass through for most cases conform(M::AbstractGBArray) = M -function Base.convert(::Type{M}, A::AbstractGBArray; fill = getfill(A)) where {T, M<:AbstractGBArray{T}} +function Base.convert(::Type{M}, A::N; fill = getfill(A)) where {M<:AbstractGBArray, N<:AbstractGBArray} isabstracttype(M) && throw(ArgumentError("$M is an abstract type, which cannot be constructed.")) sparsity = sparsitystatus(A) x = tempunpack_noformat!(A) @@ -17,10 +17,14 @@ function Base.convert(::Type{M}, A::AbstractGBArray; fill = getfill(A)) where {T display(typeof(newvalues)) copyto!(newvalues, values) newindices = _copytoraw.(indices) + repack!() B = M(size(A); fill) unsafepack!(B, newindices..., newvalues, false) + end +Base.convert(::Type{M}, A::M; fill = nothing) where {M<:AbstractGBArray} = A + # TODO: Implement this? No strong reason not to? Base.convert(::Type{M}, ::AbstractGBArray; fill = nothing) where {M<:AbstractGBShallowArray} = throw(ArgumentError("Cannot convert into a shallow array.")) diff --git a/src/matrix.jl b/src/matrix.jl index dbe6d718..9af72c54 100644 --- a/src/matrix.jl +++ b/src/matrix.jl @@ -2,106 +2,10 @@ ############### # Empty constructors: -function GBMatrix{T}(p::Base.RefValue{LibGraphBLAS.GrB_Matrix}; fill::F = nothing) where {T, F} - return GBMatrix{T, F}(finalizer(p) do ref - @wraperror LibGraphBLAS.GrB_Matrix_free(ref) - end, fill) -end - -""" - GBMatrix{T}(nrows, ncols; fill = nothing) - -Create a GBMatrix of the specified size. -""" -function GBMatrix{T}(nrows::Integer, ncols::Integer; fill::F = nothing) where {T, F} - m = Ref{LibGraphBLAS.GrB_Matrix}() - @wraperror LibGraphBLAS.GrB_Matrix_new(m, gbtype(T), nrows, ncols) - return GBMatrix{T}(m; fill) -end - -GBMatrix{T}(dims::Dims{2}; fill = nothing) where {T} = GBMatrix{T}(dims...; fill) -GBMatrix{T}(dims::Tuple{<:Integer}; fill = nothing) where {T} = GBMatrix{T}(dims...; fill) -GBMatrix{T}(size::Tuple{Base.OneTo, Base.OneTo}; fill = nothing) where {T} = - GBMatrix{T}(size[1].stop, size[2].stop; fill) - -# Coordinate form constructors: -""" - GBMatrix(I, J, X; combine = +, nrows = maximum(I), ncols = maximum(J); fill = nothing) - -Create an nrows x ncols GBMatrix M such that M[I[k], J[k]] = X[k]. The combine function defaults -to `|` for booleans and `+` for nonbooleans. -""" -function GBMatrix( - I::AbstractVector, J::AbstractVector, X::AbstractVector{T}; - combine = +, nrows = maximum(I), ncols = maximum(J), fill = nothing -) where {T} - I isa Vector || (I = collect(I)) - J isa Vector || (J = collect(J)) - X isa Vector || (X = collect(X)) - A = GBMatrix{T}(nrows, ncols; fill) - build!(A, I, J, X; combine) - return A -end - -function GBMatrix{T}( - I::AbstractVector, J::AbstractVector, X::AbstractVector{Told}; - combine = +, nrows = maximum(I), ncols = maximum(J), fill = nothing -) where {T, Told} - return GBMatrix(I, J, T.(X); combine, ncols, nrows, fill) -end - -#iso constructors -""" - GBMatrix(I, J, x; nrows = maximum(I), ncols = maximum(J); fill = nothing) - -Create an nrows x ncols GBMatrix M such that M[I[k], J[k]] = x. -The resulting matrix is "iso-valued" such that it only stores `x` once rather than once for -each index. -""" -function GBMatrix(I::AbstractVector, J::AbstractVector, x::T; - nrows = maximum(I), ncols = maximum(J), fill = nothing) where {T} - A = GBMatrix{T}(nrows, ncols; fill) - build!(A, I, J, x) - return A -end - -function GBMatrix(dims::Dims{2}, x::T; fill = nothing) where {T} - A = GBMatrix{T}(dims; fill) - A .= x - return A -end - -GBMatrix(nrows, ncols, x; fill = nothing) = GBMatrix((nrows, ncols), x; fill) -GBMatrix(dims::Tuple{<:Integer}, x; fill = nothing) = GBMatrix(dims..., x; fill) -GBMatrix(size::Tuple{Base.OneTo, Base.OneTo}, x; fill = nothing) = -GBMatrix(size[1].stop, size[2].stop, x; fill) - -function GBMatrix(v::GBVector) +# TODO: match above, FIXME +function GBMatrix(v::GBVector{T, F}) where {T, F} # this copies, I think that's ideal, and I can implement @view or something at a later date. - return copy(GBMatrix{eltype(v), typeof(v.fill)}(v.p, v.fill)) -end - -GBMatrix{T, F}(::Number, ::Number; fill = nothing) where {T, F} = throw(ArgumentError("The F parameter is implicit and determined by the `fill` keyword argument to constructors. Users must not specify this manually.")) - -function GBMatrix(S::SparseMatrixCSC{T}; fill::F = nothing) where {T, F} - A = GBMatrix{T}(size(S)...; fill) - return unsafepack!(A, _copytoraw(S)..., false) -end - -function GBMatrix(M::Union{AbstractVector{T}, AbstractMatrix{T}}; fill::F = nothing) where {T, F} - if M isa AbstractVector && !(M isa Vector) - M = collect(M) - end - if M isa AbstractMatrix && !(M isa Matrix) - M = Matrix(M) - end - A = GBMatrix{T}(size(M, 1), size(M, 2); fill) - return unsafepack!(A, _copytoraw(M), false) -end - -function GBMatrix(v::SparseVector{T}; fill::F = nothing) where {T, F} - A = GBMatrix{T}(size(v, 1), 1; fill) - return unsafepack!(A, _copytoraw(v)..., false) + return copy(GBMatrix{T, F}(v.p, v.fill)) end # Some Base and basic SparseArrays/LinearAlgebra functions: @@ -111,52 +15,6 @@ function Base.copy(A::GBMatrix{T, F}) where {T, F} return GBMatrix{T, F}(_copyGrBMat(A.p), A.fill) end -# because of the fill kwarg we have to redo a lot of the Base.similar dispatch stack. -function Base.similar( - A::GBMatrixOrTranspose{T}, ::Type{TNew} = T, - dims::Tuple{Int64, Vararg{Int64, N}} = size(A); fill = parent(A).fill -) where {T, TNew, N} - if dims isa Dims{1} - x = GBVector{TNew}(dims...; fill) - else - x = GBMatrix{TNew}(dims...; fill) - end - setstorageorder!(x, storageorder(A)) - return x -end - -function Base.similar(A::GBMatrixOrTranspose{T}, dims::Tuple; fill = parent(A).fill) where {T} - return similar(A, T, dims; fill) -end - -function Base.similar( - A::GBMatrixOrTranspose{T}, ::Type{TNew}, - dims::Integer; fill = parent(A).fill -) where {T, TNew} - return similar(A, TNew, (dims,); fill) -end - -function Base.similar( - A::GBMatrixOrTranspose{T}, ::Type{TNew}, - dim1::Integer, dim2::Integer; fill = parent(A).fill -) where {T, TNew} - return similar(A, TNew, (dim1, dim2); fill) -end - -function Base.similar( - A::GBMatrixOrTranspose{T}, - dims::Integer; fill = parent(A).fill -) where {T} - return similar(A, (dims,); fill) -end - -function Base.similar( - A::GBMatrixOrTranspose{T}, - dim1::Integer, dim2::Integer; fill = parent(A).fill -) where {T} - return similar(A, (dim1, dim2); fill) -end - # TODO: FIXME # function LinearAlgebra.diagm(v::GBVector, k::Integer=0; desc = nothing) # return Diagonal(v, k; desc) diff --git a/src/operations/ewise.jl b/src/operations/ewise.jl index 706fe80b..29934402 100644 --- a/src/operations/ewise.jl +++ b/src/operations/ewise.jl @@ -35,8 +35,8 @@ function emul!( mask = _handlemask!(desc, mask) size(C, 1) == size(A, 1) == size(B, 1) && size(C, 2) == size(A, 2) == size(B, 2) || throw(DimensionMismatch()) - op = binaryop(op, eltype(A), eltype(B)) - accum = _handleaccum(accum, eltype(C)) + op = binaryop(op, A, B) + accum = _handleaccum(accum, storedeltype(C)) if op isa TypedBinaryOperator @wraperror LibGraphBLAS.GrB_Matrix_eWiseMult_BinaryOp(C, mask, accum, op, parent(A), parent(B), desc) return C @@ -77,8 +77,9 @@ function emul( accum = nothing, desc = nothing ) - t = inferbinarytype(eltype(A), eltype(B), op) - C = similar(A, t, _combinesizes(A, B); fill=_promotefill(parent(A).fill, parent(B).fill)) + t = inferbinarytype(parent(A), parent(B), op) + + C = similar(A, t, _combinesizes(A, B); fill=_promotefill(parent(A), parent(B), op)) return emul!(C, A, B, op; mask, accum, desc) end @@ -121,8 +122,8 @@ function eadd!( mask = _handlemask!(desc, mask) size(C, 1) == size(A, 1) == size(B, 1) && size(C, 2) == size(A, 2) == size(B, 2) || throw(DimensionMismatch()) - op = binaryop(op, eltype(A), eltype(B)) - accum = _handleaccum(accum, eltype(C)) + op = binaryop(op, A, B) + accum = _handleaccum(accum, storedeltype(C)) if op isa TypedBinaryOperator @wraperror LibGraphBLAS.GrB_Matrix_eWiseAdd_BinaryOp(C, mask, accum, op, parent(A), parent(B), desc) return C @@ -162,8 +163,8 @@ function eadd( accum = nothing, desc = nothing ) - t = inferbinarytype(eltype(A), eltype(B), op) - C = similar(A, t, _combinesizes(A, B); fill=_promotefill(parent(A).fill, parent(B).fill)) + t = inferbinarytype(parent(A), parent(B), op) + C = similar(A, t, _combinesizes(A, B); fill=_promotefill(parent(A), parent(B), op)) return eadd!(C, A, B, op; mask, accum, desc) end @@ -207,8 +208,8 @@ function eunion!( mask = _handlemask!(desc, mask) size(C, 1) == size(A, 1) == size(B, 1) && size(C, 2) == size(A, 2) == size(B, 2) || throw(DimensionMismatch()) - op = binaryop(op, eltype(A), eltype(B)) - accum = _handleaccum(accum, eltype(C)) + op = binaryop(op, A, B) + accum = _handleaccum(accum, storedeltype(C)) if op isa TypedBinaryOperator @wraperror LibGraphBLAS.GxB_Matrix_eWiseUnion(C, mask, accum, op, parent(A), GBScalar(α), parent(B), GBScalar(β), desc) return C @@ -248,8 +249,8 @@ function eunion( accum = nothing, desc = nothing ) where {T, U} - t = inferbinarytype(eltype(A), eltype(B), op) - C = similar(A, t, _combinesizes(A, B); fill=_promotefill(parent(A).fill, parent(B).fill)) + t = inferbinarytype(parent(A), parent(B), op) + C = similar(A, t, _combinesizes(A, B); fill=_promotefill(parent(A), parent(B), op)) return eunion!(C, A, α, B, β, op; mask, accum, desc) end diff --git a/src/operations/extract.jl b/src/operations/extract.jl index 2682dce4..40881354 100644 --- a/src/operations/extract.jl +++ b/src/operations/extract.jl @@ -69,7 +69,7 @@ function extract!( mask = _handlemask!(desc, mask) I = decrement!(I) J = decrement!(J) - @wraperror LibGraphBLAS.GrB_Matrix_extract(C, mask, _handleaccum(accum, eltype(C)), parent(A), I, ni, J, nj, desc) + @wraperror LibGraphBLAS.GrB_Matrix_extract(C, mask, _handleaccum(accum, storedeltype(C)), parent(A), I, ni, J, nj, desc) I isa AbstractVector && increment!(I) J isa AbstractVector && increment!(J) return C @@ -139,7 +139,7 @@ function extract!( I = decrement!(I) desc = _handledescriptor(desc; out=w) mask = _handlemask!(desc, mask) - @wraperror LibGraphBLAS.GrB_Matrix_extract(w, mask, _handleaccum(accum, eltype(w)), u, I, ni, UInt64[0], 1, desc) + @wraperror LibGraphBLAS.GrB_Matrix_extract(w, mask, _handleaccum(accum, storedeltype(w)), u, I, ni, UInt64[0], 1, desc) I isa AbstractVector && increment!(I) return w end diff --git a/src/operations/kronecker.jl b/src/operations/kronecker.jl index debc7b3f..98c1c699 100644 --- a/src/operations/kronecker.jl +++ b/src/operations/kronecker.jl @@ -15,8 +15,8 @@ function LinearAlgebra.kron!( _canbeoutput(C) || throw(ShallowException()) desc = _handledescriptor(desc; in1=A, in2=B) mask = _handlemask!(desc, mask) - op = binaryop(op, eltype(A), eltype(B)) - accum = _handleaccum(accum, eltype(C)) + op = binaryop(op, A, B) + accum = _handleaccum(accum, storedeltype(C)) @wraperror LibGraphBLAS.GxB_kron(C, mask, accum, op, parent(A), parent(B), desc) return C end @@ -47,8 +47,8 @@ function LinearAlgebra.kron( accum = nothing, desc = nothing ) - t = inferbinarytype(eltype(A), eltype(B), op) - C = similar(A, t, (size(A, 1) * size(B, 1), size(A, 2) * size(B, 2)); fill = _promotefill(parent(A).fill, parent(B).fill)) + t = inferbinarytype(parent(A), parent(B), op) + C = similar(A, t, (size(A, 1) * size(B, 1), size(A, 2) * size(B, 2)); fill = _promotefill(parent(A), parent(B), op)) kron!(C, A, B, op; mask, accum, desc) return C end diff --git a/src/operations/map.jl b/src/operations/map.jl index 9ed39d76..66f8ce33 100644 --- a/src/operations/map.jl +++ b/src/operations/map.jl @@ -5,8 +5,8 @@ function apply!( _canbeoutput(C) || throw(ShallowException()) desc = _handledescriptor(desc; out=C, in1=A) mask = _handlemask!(desc, mask) - op = unaryop(op, eltype(A)) - accum = _handleaccum(accum, eltype(C)) + op = unaryop(op, A) + accum = _handleaccum(accum, storedeltype(C)) @wraperror LibGraphBLAS.GrB_Matrix_apply(C, mask, accum, op, parent(A), desc) return C end @@ -45,7 +45,7 @@ function apply( op, A::GBArrayOrTranspose{T}; mask = nothing, accum = nothing, desc = nothing ) where {T} - t = inferunarytype(eltype(A), op) + t = inferunarytype(A, op) return apply!(op, similar(A, t), A; mask, accum, desc) end @@ -56,8 +56,8 @@ function apply!( _canbeoutput(C) || throw(ShallowException()) desc = _handledescriptor(desc; out=C, in2=A) mask = _handlemask!(desc, mask) - op = binaryop(op, eltype(A), typeof(x)) - accum = _handleaccum(accum, eltype(C)) + op = binaryop(op, A, typeof(x)) + accum = _handleaccum(accum, storedeltype(C)) @wraperror LibGraphBLAS.GxB_Matrix_apply_BinaryOp1st(C, mask, accum, op, GBScalar(x), parent(A), desc) return C end @@ -73,7 +73,7 @@ function apply( op, x, A::GBArrayOrTranspose{T}; mask = nothing, accum = nothing, desc = nothing ) where {T} - t = inferbinarytype(typeof(x), eltype(A), op) + t = inferbinarytype(typeof(x), parent(A), op) return apply!(op, similar(A, t), x, A; mask, accum, desc) end @@ -84,8 +84,8 @@ function apply!( _canbeoutput(C) || throw(ShallowException()) desc = _handledescriptor(desc; out=C, in1=A) mask = _handlemask!(desc, mask) - op = binaryop(op, eltype(A), typeof(x)) - accum = _handleaccum(accum, eltype(C)) + op = binaryop(op, A, typeof(x)) + accum = _handleaccum(accum, storedeltype(C)) @wraperror LibGraphBLAS.GxB_Matrix_apply_BinaryOp2nd(C, mask, accum, op, parent(A), GBScalar(x), desc) return C end @@ -98,26 +98,31 @@ function apply!( end function apply( - op, A::GBArrayOrTranspose{T}, x; + op, A::GBArrayOrTranspose, x; mask = nothing, accum = nothing, desc = nothing -) where {T} - t = inferbinarytype(eltype(A), typeof(x), op) +) + t = inferbinarytype(parent(A), typeof(x), op) return apply!(op, similar(A, t), A, x; mask, accum, desc) end -function Base.map(f, A::GBArrayOrTranspose{T}; mask = nothing, accum = nothing, desc = nothing) where {T} +function Base.map(f, A::GBArrayOrTranspose; mask = nothing, accum = nothing, desc = nothing) apply(f, A; mask, accum, desc) end -function Base.map!(f, C::GBVecOrMat, A::GBArrayOrTranspose{T}; mask = nothing, accum = nothing, desc = nothing) where {T} +function Base.map!(f, C::GBVecOrMat, A::GBArrayOrTranspose; mask = nothing, accum = nothing, desc = nothing) apply!(f, C, A; mask, accum, desc) end -function Base.map!(f, A::GBArrayOrTranspose{T}; mask = nothing, accum = nothing, desc = nothing) where {T} +function Base.map!(f, A::GBArrayOrTranspose; mask = nothing, accum = nothing, desc = nothing) apply!(f, A, A; mask, accum, desc) end -Base.:*(x::V, u::GBArrayOrTranspose{T}; mask = nothing, accum = nothing, desc = nothing) where {T, V<:Union{<:valid_union, T, <:Number}} = +Base.:*(x, u::GBArrayOrTranspose; mask = nothing, accum = nothing, desc = nothing) = + apply(*, x, u; mask, accum, desc) +Base.:*(u::GBArrayOrTranspose, x; mask = nothing, accum = nothing, desc = nothing) = + apply(*, u, x; mask, accum, desc) + +Base.:*(x::Number, u::GBArrayOrTranspose; mask = nothing, accum = nothing, desc = nothing) = apply(*, x, u; mask, accum, desc) -Base.:*(u::GBArrayOrTranspose{T}, x::V; mask = nothing, accum = nothing, desc = nothing) where {T, V<:Union{<:valid_union, T, <:Number}} = +Base.:*(u::GBArrayOrTranspose, x::Number; mask = nothing, accum = nothing, desc = nothing) = apply(*, u, x; mask, accum, desc) Base.:-(u::GBArrayOrTranspose) = apply(-, u) diff --git a/src/operations/mul.jl b/src/operations/mul.jl index c0cfd1bc..0d0adc1b 100644 --- a/src/operations/mul.jl +++ b/src/operations/mul.jl @@ -13,8 +13,8 @@ function LinearAlgebra.mul!( size(A, 2) == size(B, 1) || throw(DimensionMismatch("size(A, 2) != size(B, 1)")) size(A, 1) == size(C, 1) || throw(DimensionMismatch("size(A, 1) != size(C, 1)")) size(B, 2) == size(C, 2) || throw(DimensionMismatch("size(B, 2) != size(C, 2)")) - op = semiring(op, eltype(A), eltype(B)) - accum = _handleaccum(accum, eltype(C)) + op = semiring(op, storedeltype(A), storedeltype(B)) + accum = _handleaccum(accum, storedeltype(C)) op isa TypedSemiring || throw(ArgumentError("$op is not a valid TypedSemiring")) @wraperror LibGraphBLAS.GrB_mxm(C, mask, accum, op, parent(A), parent(B), desc) return C @@ -91,8 +91,8 @@ function Base.:*( accum = nothing, desc = nothing ) - t = inferbinarytype(eltype(A), eltype(B), op) - fill = _promotefill(parent(A).fill, parent(B).fill) + t = inferbinarytype(parent(A), parent(B), op) + fill = _promotefill(parent(A), parent(B), op) if A isa GBMatrixOrTranspose && B isa AbstractGBVector C = similar(A, t, size(A, 1); fill) elseif A isa AbstractGBVector && B isa GBMatrixOrTranspose @@ -171,7 +171,7 @@ function Base.:*( return @_densepack B (*(A, B, op; mask, accum, desc)) end -function Base.:*((⊕)::Union{<:Base.Callable, Monoid}, (⊗)::Function) +function Base.:*((⊕)::Union{<:Function, Monoid}, (⊗)::Function) return function(A::GBArrayOrTranspose, B::GBArrayOrTranspose; mask=nothing, accum=nothing, desc=nothing) *(A, B, (⊕, ⊗); mask, accum, desc) end diff --git a/src/operations/operationutils.jl b/src/operations/operationutils.jl index ead54fda..15f67be3 100644 --- a/src/operations/operationutils.jl +++ b/src/operations/operationutils.jl @@ -1,12 +1,18 @@ -inferunarytype(::Type{T}, f::F) where {T, F<:Base.Callable} = Base._return_type(f, Tuple{T}) +inferunarytype(::Type{T}, f::F) where {T, F} = Base._return_type(f, Tuple{T}) inferunarytype(::Type{X}, op::TypedUnaryOperator{F, X}) where {F, X} = ztype(op) -inferbinarytype(::Type{T}, ::Type{U}, f::F) where {T, U, F<:Base.Callable} = Base._return_type(f, Tuple{T, U}) +inferunarytype(::GBArrayOrTranspose{T}, op) where T = inferunarytype(T, op) + +inferbinarytype(::Type{T}, ::Type{U}, f) where {T, U} = Base._return_type(f, Tuple{T, U}) # manual overload for `any` which will give Union{} normally: inferbinarytype(::Type{T}, ::Type{U}, ::typeof(any)) where {T, U} = promote_type(T, U) # Overload for `first`, which will give Vector{T} normally: inferbinarytype(::Type{T}, ::Type{U}, f::typeof(first)) where {T, U} = T +inferbinarytype(::AbstractGBArray{T}, ::AbstractGBArray{U}, f) where {T, U} = inferbinarytype(T, U, f) +inferbinarytype(::AbstractGBArray{T}, ::Type{U}, f) where {T, U} = inferbinarytype(T, U, f) +inferbinarytype(::Type{T}, B::AbstractGBArray{U}, f) where {T, U} = inferbinarytype(T, U, f) + inferbinarytype(::Type{T}, ::Type{U}, op::AbstractMonoid) where {T, U} = inferbinarytype(T, U, op.fn) #semirings are technically binary so we'll just overload that inferbinarytype(::Type{T}, ::Type{U}, op::Tuple) where {T, U} = inferbinarytype(T, U, semiring(op, T, U)) @@ -104,15 +110,18 @@ Determine type of the output of a typed operator. """ function ztype end -_promotefill(::Nothing, ::Nothing) = nothing -_promotefill(::Nothing, x) = nothing -_promotefill(x, ::Nothing) = nothing -_promotefill(::Missing, ::Missing) = missing -_promotefill(::Missing, x) = missing -_promotefill(x, ::Missing) = missing -# I'd prefer that this be nothing on x != y. But for type inference reasons this seems better. -# It's not a serious issue for several reasons. -# The first is that GrB methods don't know anything about fill, they don't care. -# The second is that it's free to setfill(A, nothing). Methods that are sensitive to this can enforce that. -# And third a future GBGraph type can manage this for the user. -_promotefill(x::X, y::Y) where {X, Y} = x == y ? (return promote_type(X, Y)(x)) : (return zero(promote_type(X, Y))) \ No newline at end of file +_promotefill(::AbstractGBArray{<:Any, Nothing}, ::AbstractGBArray{<:Any, Nothing}, op) = nothing +_promotefill(::AbstractGBArray{<:Any, Nothing}, y, op) = nothing +_promotefill(x, ::AbstractGBArray{<:Any, Nothing}, op) = nothing +_promotefill(::AbstractGBArray{<:Any, Missing}, ::AbstractGBArray{<:Any, Missing}, op) = missing +_promotefill(::AbstractGBArray{<:Any, Missing}, y, op) = nothing +_promotefill(x, ::AbstractGBArray{<:Any, Missing}, op) = nothing +_promotefill(::AbstractGBArray{<:Any, Nothing}, ::AbstractGBArray{<:Any, Missing}, op) = nothing +_promotefill(::AbstractGBArray{<:Any, Missing}, ::AbstractGBArray{<:Any, Nothing}, op) = nothing + +_promotefill(x, op) = x + +function _promotefill(x::AbstractGBArray{<:Any, T}, y::AbstractGBArray{<:Any, U}, op) where {T, U} + getfill(x) ≈ getfill(y) && (return inferbinarytype(T, U, op)(getfill(x))) + return defaultfill(inferbinarytype(T, U, op)) # fallback to defaultfill. +end \ No newline at end of file diff --git a/src/operations/reduce.jl b/src/operations/reduce.jl index b65fa035..5f61e412 100644 --- a/src/operations/reduce.jl +++ b/src/operations/reduce.jl @@ -6,8 +6,8 @@ function reduce!( desc = _handledescriptor(desc; out=w, in1=A) mask = _handlemask!(desc, mask) - op = typedmonoid(op, eltype(w)) - accum = _handleaccum(accum, eltype(w)) + op = typedmonoid(op, storedeltype(w)) + accum = _handleaccum(accum, storedeltype(w)) @wraperror LibGraphBLAS.GrB_Matrix_reduce_Monoid( w, mask, accum, op, parent(A), desc ) @@ -27,9 +27,9 @@ function Base.reduce( desc = _handledescriptor(desc; in1=A) mask = _handlemask!(desc, mask) if typeout === nothing - typeout = inferbinarytype(eltype(A), eltype(A), op) + typeout = inferbinarytype(parent(A), parent(A), op) end - if typeout != eltype(A) + if typeout != storedeltype(A) throw(ArgumentError( "The SuiteSparse:GraphBLAS reduce function only supports monoids where T x T -> T. Please pass a function whose output type matches both input types.")) diff --git a/src/operations/select.jl b/src/operations/select.jl index aa6a1156..31b0c011 100644 --- a/src/operations/select.jl +++ b/src/operations/select.jl @@ -15,7 +15,7 @@ function select!( desc = _handledescriptor(desc; out=C, in1=A) mask = _handlemask!(desc, mask) thunk === nothing && (thunk = C_NULL) - accum = _handleaccum(accum, eltype(C)) + accum = _handleaccum(accum, storedeltype(C)) if thunk isa Number thunk = GBScalar(thunk) end diff --git a/src/operations/sort.jl b/src/operations/sort.jl index 69e26c49..c52d3384 100644 --- a/src/operations/sort.jl +++ b/src/operations/sort.jl @@ -16,7 +16,7 @@ function Base.sort!( C === nothing && (C = C_NULL) P === nothing && (P = C_NULL) C == C_NULL && P == C_NULL && throw(ArgumentError("One (or both) of C and P must not be nothing.")) - op = binaryop(lt, eltype(A)) + op = binaryop(lt, A, A) if dims == 1 transpose = true elseif dims == 2 diff --git a/src/operations/transpose.jl b/src/operations/transpose.jl index 28c5f133..82420c17 100644 --- a/src/operations/transpose.jl +++ b/src/operations/transpose.jl @@ -21,7 +21,7 @@ function gbtranspose!( _canbeoutput(C) || throw(ShallowException()) desc = _handledescriptor(desc; out=C, in1=A) mask = _handlemask!(desc, mask) - accum = _handleaccum(accum, eltype(C)) + accum = _handleaccum(accum, storedeltype(C)) @wraperror LibGraphBLAS.GrB_transpose(C, mask, accum, parent(A), desc) return C end @@ -58,7 +58,7 @@ function Base.copy!( end function Base.copy( - A::LinearAlgebra.Transpose{<:Any, <:GBVecOrMat}; + A::LinearAlgebra.Transpose{<:Any, <:AbstractGBArray}; mask = nothing, accum = nothing, desc = nothing ) return gbtranspose(parent(A); mask, accum, desc) diff --git a/src/operators/binaryops.jl b/src/operators/binaryops.jl index d3eadb7c..dcb37fcc 100644 --- a/src/operators/binaryops.jl +++ b/src/operators/binaryops.jl @@ -2,7 +2,7 @@ module BinaryOps import ..SuiteSparseGraphBLAS using ..SuiteSparseGraphBLAS: isGxB, isGrB, TypedBinaryOperator, GBType, valid_vec, juliaop, gbtype, symtotype, Itypes, Ftypes, Ztypes, FZtypes, Rtypes, optype, - Ntypes, Ttypes, suffix, valid_union + Ntypes, Ttypes, suffix, valid_union, GBArrayOrTranspose, binaryop using ..LibGraphBLAS export BinaryOp, binaryop @@ -10,11 +10,11 @@ export second, rminus, iseq, isne, isgt, islt, isge, isle, ∨, ∧, lxor, xnor, bxnor, bget, bset, bclr, firsti0, firsti, firstj0, firstj, secondi0, secondi, secondj0, secondj, pair -const BINARYOPS = IdDict{Tuple{<:Base.Callable, DataType, DataType}, TypedBinaryOperator}() +const BINARYOPS = IdDict{Tuple{<:Any, DataType, DataType}, TypedBinaryOperator}() function fallback_binaryop( f::F, ::Type{X}, ::Type{Y} -) where {F<:Base.Callable, X, Y} +) where {F, X, Y} return get!(BINARYOPS, (f, X, Y)) do TypedBinaryOperator(f, X, Y) end @@ -22,11 +22,11 @@ end # If we have the same type we know we must fallback, # more specific methods will be captured by dispatch. -binaryop(f::F, ::Type{X}, ::Type{X}) where {F<:Base.Callable, X} = fallback_binaryop(f, X, X) +SuiteSparseGraphBLAS.binaryop(f::F, ::Type{X}, ::Type{X}) where {F, X} = fallback_binaryop(f, X, X) -function binaryop( +function SuiteSparseGraphBLAS.binaryop( f::F, ::Type{X}, ::Type{Y} -) where {F<:Base.Callable, X, Y} +) where {F, X, Y} P = promote_type(X, Y) if isconcretetype(P) return binaryop(f, P, P) @@ -35,8 +35,13 @@ function binaryop( end end -binaryop(f, type) = binaryop(f, type, type) -binaryop(op::TypedBinaryOperator, x...) = op +SuiteSparseGraphBLAS.binaryop(f, ::GBArrayOrTranspose{T}, ::GBArrayOrTranspose{U}) where {T, U} = + binaryop(f, T, U) +SuiteSparseGraphBLAS.binaryop(f, ::GBArrayOrTranspose{T}, ::Type{U}) where {T, U} = binaryop(f, T, U) +SuiteSparseGraphBLAS.binaryop(f, ::Type{T}, ::GBArrayOrTranspose{U}) where {T, U} = binaryop(f, T, U) + +SuiteSparseGraphBLAS.binaryop(f, type) = binaryop(f, type, type) +SuiteSparseGraphBLAS.binaryop(op::TypedBinaryOperator, x...) = op SuiteSparseGraphBLAS.juliaop(op::TypedBinaryOperator) = op.fn @@ -68,9 +73,9 @@ function typedbinopconstexpr(jlfunc, builtin, namestr, xtype, ytype, outtype) constquote = :(const $(esc(namesym)) = TypedBinaryOperator($(esc(jlfunc)), $(esc(xsym)), $(esc(ysym)), $(esc(outsym)))) end dispatchquote = if xtype === :Any && ytype === :Any - :($(esc(:(SuiteSparseGraphBLAS.BinaryOps.binaryop)))(::$(esc(:typeof))($(esc(jlfunc))), ::Type, ::Type) = $(esc(namesym))) + :($(esc(:(SuiteSparseGraphBLAS.binaryop)))(::$(esc(:typeof))($(esc(jlfunc))), ::Type, ::Type) = $(esc(namesym))) else - :($(esc(:(SuiteSparseGraphBLAS.BinaryOps.binaryop)))(::$(esc(:typeof))($(esc(jlfunc))), ::Type{$xsym}, ::Type{$ysym}) = $(esc(namesym))) + :($(esc(:(SuiteSparseGraphBLAS.binaryop)))(::$(esc(:typeof))($(esc(jlfunc))), ::Type{$xsym}, ::Type{$ysym}) = $(esc(namesym))) end return quote $(constquote) @@ -177,8 +182,8 @@ function ∧(x::T, y::T) where T return (x != zero(T)) && (y != zero(T)) end @binop (∧) GxB_LAND R=>R -binaryop(::typeof(|), ::Type{Bool}, ::Type{Bool}) = LOR_BOOL -binaryop(::typeof(&), ::Type{Bool}, ::Type{Bool}) = LAND_BOOL +SuiteSparseGraphBLAS.binaryop(::typeof(|), ::Type{Bool}, ::Type{Bool}) = LOR_BOOL +SuiteSparseGraphBLAS.binaryop(::typeof(&), ::Type{Bool}, ::Type{Bool}) = LAND_BOOL lxor(x::T, y::T) where T = xor((x != zero(T)), (y != zero(T))) @binop lxor GxB_LXOR R=>R @@ -210,7 +215,7 @@ xnor(x::T, y::T) where T = !(lxor(x, y)) @binop (⊻) GrB_BXOR I=>I bxnor(x::T, y::T) where T = ~⊻(x, y) @binop bxnor GrB_BXNOR I=>I -binaryop(::typeof(⊻), ::Type{Bool}, ::Type{Bool}) = LXOR_BOOL +SuiteSparseGraphBLAS.binaryop(::typeof(⊻), ::Type{Bool}, ::Type{Bool}) = LXOR_BOOL # leaving these without any equivalent Julia functions # probably should only operate on Ints anyway. diff --git a/src/operators/monoids.jl b/src/operators/monoids.jl index f539b163..ef224843 100644 --- a/src/operators/monoids.jl +++ b/src/operators/monoids.jl @@ -33,7 +33,7 @@ function Monoid(fn, identity) return Monoid(fn, identity, nothing) end -function typedmonoid(m::Monoid{F, I, Term}, ::Type{T}) where {F<:Base.Callable, I, Term, T} +function typedmonoid(m::Monoid{F, I, Term}, ::Type{T}) where {F, I, Term, T} return (get!(MONOIDS, (m, T)) do TypedMonoid(binaryop(m.fn, T), m.identity, m.terminal) end) @@ -42,7 +42,7 @@ end typedmonoid(op::TypedMonoid, x...) = op # We default to no available monoid. -defaultmonoid(f::F, ::Type{T}) where {F<:Base.Callable, T} = throw( +defaultmonoid(f::F, ::Type{T}) where {F, T} = throw( ArgumentError("Function $f does not have a default monoid. You must either extend defaultmonoid(::$F, ::Type{T}) = Monoid($f, [, ]) or pass the struct @@ -50,9 +50,7 @@ defaultmonoid(f::F, ::Type{T}) where {F<:Base.Callable, T} = throw( ) # Use defaultmonoid when available. User should verify that this results in the correct monoid. -typedmonoid(f::F, ::Type{T}) where {F<:Base.Callable, T} = typedmonoid(defaultmonoid(f, T), T) - -BinaryOps.binaryop(op::TypedMonoid) = op.binaryop +typedmonoid(f::F, ::Type{T}) where {F, T} = typedmonoid(defaultmonoid(f, T), T) function typedmonoidconstexpr(jlfunc, builtin, namestr, type, identity, term) if type ∈ Ztypes && isGrB(namestr) diff --git a/src/operators/operatorutils.jl b/src/operators/operatorutils.jl index 21a14f26..14203d31 100644 --- a/src/operators/operatorutils.jl +++ b/src/operators/operatorutils.jl @@ -53,4 +53,10 @@ function symtotype(sym) end end -function juliaop end \ No newline at end of file +juliaop(op) = op +juliaop(op::Tuple) = throw(ArgumentError("You must request either the mulop or addop of a semiring before conversion to a julia operator.")) + +function binaryop end +function monoid end +# const mulop = binaryop +# const addop = monoid \ No newline at end of file diff --git a/src/operators/unaryops.jl b/src/operators/unaryops.jl index be786f36..dfd4e6eb 100644 --- a/src/operators/unaryops.jl +++ b/src/operators/unaryops.jl @@ -2,7 +2,8 @@ module UnaryOps import ..SuiteSparseGraphBLAS using ..SuiteSparseGraphBLAS: isGxB, isGrB, TypedUnaryOperator, GBType, - valid_vec, juliaop, gbtype, symtotype, Itypes, Ftypes, Ztypes, FZtypes, Rtypes, Ntypes, Ttypes, suffix + valid_vec, juliaop, gbtype, symtotype, Itypes, Ftypes, Ztypes, FZtypes, Rtypes, Ntypes, Ttypes, suffix, + GBArrayOrTranspose using ..LibGraphBLAS export unaryop, @unop @@ -18,6 +19,7 @@ function unaryop(f::F, ::Type{T}) where {F<:Base.Callable, T} end end +unaryop(f, ::GBArrayOrTranspose{T}) where T = unaryop(f, T) unaryop(op::TypedUnaryOperator, x...) = op SuiteSparseGraphBLAS.juliaop(op::TypedUnaryOperator) = op.fn diff --git a/src/options.jl b/src/options.jl index 233f3155..44315362 100644 --- a/src/options.jl +++ b/src/options.jl @@ -27,34 +27,19 @@ function GxB_Global_Option_get(field) T = Bool end v = Ref{T}() - ccall( - (:GxB_Global_Option_get, libgraphblas), - Cvoid, - (UInt32, Ptr{Cvoid}), - field, - v - ) + LibGraphBLAS.GxB_Global_Option_get(field, v) return v[] end function GxB_Global_Option_set(field, value) if field ∈ [GxB_HYPER_SWITCH, GxB_BITMAP_SWITCH, GxB_GLOBAL_CHUNK] - ccall( - (:GxB_Global_Option_set, libgraphblas), - Cvoid, - (UInt32, Cdouble), - field, - value - ) + value isa Cdouble || throw(ArgumentError("$field specifies a value of type Float64")) elseif field ∈ [GxB_GLOBAL_NTHREADS, GxB_BURBLE, GxB_PRINT_1BASED, GxB_FORMAT] - ccall( - (:GxB_Global_Option_set, libgraphblas), - Cvoid, - (UInt32, Cint), - field, - value - ) + value = Cint(value) + else + throw(ArgumentError("$field is not a valid Matrix option.")) end + LibGraphBLAS.GxB_Global_Option_set(field, value) end function GxB_Matrix_Option_get(A::AbstractGBArray, field) @@ -70,24 +55,16 @@ end function GxB_Matrix_Option_set(A::AbstractGBArray, field, value) if field ∈ [GxB_HYPER_SWITCH, GxB_BITMAP_SWITCH] - ccall( - (:GxB_Matrix_Option_set, libgraphblas), - Cvoid, - (LibGraphBLAS.GrB_Matrix, UInt32, Cdouble), - A, - field, - value - ) - elseif field ∈ [GxB_FORMAT, GxB_SPARSITY_CONTROL] - ccall( - (:GxB_Matrix_Option_set, libgraphblas), - Cvoid, - (LibGraphBLAS.GrB_Matrix, UInt32, UInt32), - A, - field, - value - ) + value isa Cdouble || throw(ArgumentError("$field specifies a value of type Float64")) + elseif field == GxB_FORMAT + value isa LibGraphBLAS.GxB_Format_Value || + throw(ArgumentError("$field specifies a value of type GxB_Format_Value")) + elseif field == GxB_SPARSITY_CONTROL + value isa Cint || throw(ArgumentError("$field specifies a value of type Cint")) + else + throw(ArgumentError("$field is not a valid Matrix option.")) end + LibGraphBLAS.GxB_Matrix_Option_set(A, field, value) end function gbset(option, value) @@ -104,6 +81,9 @@ end function gbset(A::AbstractGBArray, option, value) option = option_toconst(option) + if option == GxB_FORMAT && _hasconstantorder(A) + throw(ArgumentError("$(typeof(A)) may not have its storage order changed.")) + end value = option_toconst(value) GxB_Matrix_Option_set(A, option, value) return nothing diff --git a/src/oriented.jl b/src/oriented.jl index 2002981e..ea8b3f22 100644 --- a/src/oriented.jl +++ b/src/oriented.jl @@ -1,11 +1,4 @@ -mutable struct OrientedGBMatrix{T, O, F} <: AbstractGBMatrix{T, F} - p::Base.RefValue{LibGraphBLAS.GrB_Matrix} - fill::F -end - -const GBMatrixC{T, F} = OrientedGBMatrix{T, StorageOrders.ColMajor(), F} -const GBMatrixR{T, F} = OrientedGBMatrix{T, StorageOrders.RowMajor(), F} -StorageOrders.storageorder(::OrientedGBMatrix{T, O, F}) where {T, O, F} = O +StorageOrders.storageorder(::OrientedGBMatrix{T, F, O}) where {T, F, O} = O # Constructors: ############### @@ -14,129 +7,129 @@ StorageOrders.storageorder(::OrientedGBMatrix{T, O, F}) where {T, O, F} = O Create a GBMatrix of the specified size. """ -function OrientedGBMatrix{T, O, F}(nrows::Integer, ncols::Integer; fill::F = nothing) where {T, O, F} - m = Ref{LibGraphBLAS.GrB_Matrix}() - @wraperror LibGraphBLAS.GrB_Matrix_new(m, gbtype(T), nrows, ncols) - A = GBMatrix{T, F}(finalizer(m) do ref - @wraperror LibGraphBLAS.GrB_Matrix_free(ref) - end, fill) - gbset(A, :format, O === StorageOrders.ColMajor() ? :bycol : :byrow) - return OrientedGBMatrix{T, O, F}(A) -end -OrientedGBMatrix{T, O}(nrows::Integer, ncols::Integer; fill::F = nothing) where {T, O, F} = OrientedGBMatrix{T, O, F}(nrows, ncols; fill) -GBMatrixC{T}(nrows::Integer, ncols::Integer; fill::F = nothing) where {T, F} = GBMatrixC{T, F}(nrows, ncols; fill) -GBMatrixR{T}(nrows::Integer, ncols::Integer; fill::F = nothing) where {T, F} = GBMatrixR{T, F}(nrows, ncols; fill) - -OrientedGBMatrix{T, O, F}(dims::Dims{2}; fill::F = nothing) where {T, O, F} = OrientedGBMatrix{T, O}(dims...; fill) -OrientedGBMatrix{T, O, F}(dims::Tuple{<:Integer}; fill::F = nothing) where {T, O, F} = OrientedGBMatrix{T, O}(dims...; fill) -OrientedGBMatrix{T, O, F}(size::Tuple{Base.OneTo, Base.OneTo}; fill::F = nothing) where {T, O, F} = - OrientedGBMatrix{T, O}(size[1].stop, size[2].stop; fill) - -OrientedGBMatrix{T, O}(dims::Tuple; fill::F = nothing) where {T, O, F} = OrientedGBMatrix{T, O, F}(dims; fill) -GBMatrixC{T}(dims::Tuple; fill::F = nothing) where {T, F} = GBMatrixC{T, F}(dims; fill) -GBMatrixR{T}(dims::Tuple; fill::F = nothing) where {T, F} = GBMatrixR{T, F}(dims; fill) +# function OrientedGBMatrix{T, O, F}(nrows::Integer, ncols::Integer; fill::F = nothing) where {T, O, F} +# m = Ref{LibGraphBLAS.GrB_Matrix}() +# @wraperror LibGraphBLAS.GrB_Matrix_new(m, gbtype(T), nrows, ncols) +# A = GBMatrix{T, F}(finalizer(m) do ref +# @wraperror LibGraphBLAS.GrB_Matrix_free(ref) +# end, fill) +# gbset(A, :format, O === StorageOrders.ColMajor() ? :bycol : :byrow) +# return OrientedGBMatrix{T, O, F}(A) +# end +# OrientedGBMatrix{T, O}(nrows::Integer, ncols::Integer; fill::F = nothing) where {T, O, F} = OrientedGBMatrix{T, O, F}(nrows, ncols; fill) +# GBMatrixC{T}(nrows::Integer, ncols::Integer; fill::F = nothing) where {T, F} = GBMatrixC{T, F}(nrows, ncols; fill) +# GBMatrixR{T}(nrows::Integer, ncols::Integer; fill::F = nothing) where {T, F} = GBMatrixR{T, F}(nrows, ncols; fill) +# +# OrientedGBMatrix{T, O, F}(dims::Dims{2}; fill::F = nothing) where {T, O, F} = OrientedGBMatrix{T, O}(dims...; fill) +# OrientedGBMatrix{T, O, F}(dims::Tuple{<:Integer}; fill::F = nothing) where {T, O, F} = OrientedGBMatrix{T, O}(dims...; fill) +# OrientedGBMatrix{T, O, F}(size::Tuple{Base.OneTo, Base.OneTo}; fill::F = nothing) where {T, O, F} = +# OrientedGBMatrix{T, O}(size[1].stop, size[2].stop; fill) +# +# OrientedGBMatrix{T, O}(dims::Tuple; fill::F = nothing) where {T, O, F} = OrientedGBMatrix{T, O, F}(dims; fill) +# GBMatrixC{T}(dims::Tuple; fill::F = nothing) where {T, F} = GBMatrixC{T, F}(dims; fill) +# GBMatrixR{T}(dims::Tuple; fill::F = nothing) where {T, F} = GBMatrixR{T, F}(dims; fill) """ OrientedGBMatrix(I, J, X; combine = +, nrows = maximum(I), ncols = maximum(J)) Create an nrows x ncols GBMatrix M such that M[I[k], J[k]] = X[k]. The combine function defaults to `|` for booleans and `+` for nonbooleans. """ -function OrientedGBMatrix{T, O, F}( - I::AbstractVector, J::AbstractVector, X::AbstractVector{T}; - combine = +, nrows = maximum(I), ncols = maximum(J), fill::F = nothing -) where {T, O, F} - I isa Vector || (I = collect(I)) - J isa Vector || (J = collect(J)) - X isa Vector || (X = collect(X)) - A = OrientedGBMatrix{T, O}(nrows, ncols; fill) - build!(A, I, J, X; combine) - return A -end -function OrientedGBMatrix{O}( - I::AbstractVector, J::AbstractVector, X::AbstractVector{T}; - combine = +, nrows = maximum(I), ncols = maximum(J), fill::F = nothing -) where {T, O, F} - return OrientedGBMatrix{T, O, F}(I, J, X,; combine, nrows, ncols, fill) -end - - -GBMatrixC( - I::AbstractVector, J::AbstractVector, X::AbstractVector; - combine = +, nrows = maximum(I), ncols = maximum(J), fill = nothing -) = OrientedGBMatrix{ColMajor()}(I, J, X; combine, nrows, ncols, fill) -GBMatrixR( - I::AbstractVector, J::AbstractVector, X::AbstractVector; - combine = +, nrows = maximum(I), ncols = maximum(J), fill = nothing -) = OrientedGBMatrix{RowMajor()}(I, J, X; combine, nrows, ncols, fill) - - -#iso constructors -""" - OrientedGBMatrix(I, J, x; nrows = maximum(I), ncols = maximum(J)) - -Create an nrows x ncols GBMatrix M such that M[I[k], J[k]] = x. -The resulting matrix is "iso-valued" such that it only stores `x` once rather than once for -each index. -""" -function OrientedGBMatrix{T, O, F}(I::AbstractVector, J::AbstractVector, x::T; - nrows = maximum(I), ncols = maximum(J), fill::F = nothing) where {T, O, F} - A = OrientedGBMatrix{T, O, F}(nrows, ncols; fill) - build!(A, I, J, x) - return A -end -OrientedGBMatrix{O}(I::AbstractVector, J::AbstractVector, x::T; nrows = maximum(I), ncols = maximum(J), fill::F = nothing) where {T, O, F} = - OrientedGBMatrix{T, O, F}(I, J, x; nrows, ncols, fill) - - -function OrientedGBMatrix{T, O, F}(dims::Dims{2}, x::T; fill::F = nothing) where {T, O, F} - A = OrientedGBMatrix{T, O, F}(dims; fill) - A[:, :] = x - return A -end -OrientedGBMatrix{O}(dims::Dims{2}, x::T; fill::F = nothing) where {T, O, F} = OrientedGBMatrix{T, O, F}(dims, x; fill) - -OrientedGBMatrix{T, O, F}(nrows, ncols, x::T; fill::F = nothing) where {T, O, F} = OrientedGBMatrix{O}((nrows, ncols), x; fill) -OrientedGBMatrix{O}(nrows, ncols, x::T; fill::F = nothing) where {T, O, F} = OrientedGBMatrix{T, O, F}(nrows, ncols, x; fill) - -function OrientedGBMatrix{T, O, F}(v::GBVector) where {T, O, F} - O === ByRow() && throw(ArgumentError("Cannot wrap a GBVector in a ByRow matrix.")) - # this copies, I think that's ideal, and I can implement @view or something at a later date. - return copy(OrientedGBMatrix{T, O, F}(v.p, v.fill)) -end -function OrientedGBMatrix{O}(v::GBVector) where {O} - # this copies, I think that's ideal, and I can implement @view or something at a later date. - return OrientedGBMatrix{eltype(v), typeof(v.fill), O}(v) -end - -function OrientedGBMatrix{T, O, F}(A::AbstractGBMatrix) where {T, O, F} - storageorder(A) != O && throw(ArgumentError("Cannot wrap a GBMatrix in an OrientedGBMatrix with a different orientation.")) - # this copies, I think that's ideal, and I can implement @view or something at a later date. - return copy(OrientedGBMatrix{T, O, F}(A.p, A.fill)) -end -function OrientedGBMatrix{O}(A::AbstractGBMatrix) where {O} - # this copies, I think that's ideal, and I can implement @view or something at a later date. - return OrientedGBMatrix{eltype(A), typeof(A.fill), O}(A) -end - -GBMatrixR(A::AbstractGBMatrix) = OrientedGBMatrix{RowMajor()}(A) -GBMatrixC(A::AbstractGBMatrix) = OrientedGBMatrix{ColMajor()}(A) - -GBMatrixC( - I::AbstractVector, J::AbstractVector, X::T; - nrows = maximum(I), ncols = maximum(J), fill = nothing -) where {T} = OrientedGBMatrix{ColMajor()}(I, J, X; nrows, ncols, fill) -GBMatrixR( - I::AbstractVector, J::AbstractVector, X::T; - nrows = maximum(I), ncols = maximum(J), fill = nothing -) where {T} = OrientedGBMatrix{RowMajor()}(I, J, X; nrows, ncols, fill) - -Base.unsafe_convert(::Type{LibGraphBLAS.GrB_Matrix}, A::OrientedGBMatrix) = A.p[] - -function Base.copy(A::OrientedGBMatrix{T, O, F}) where {T, O, F} - C = Ref{LibGraphBLAS.GrB_Matrix}() - LibGraphBLAS.GrB_Matrix_dup(C, A) - return OrientedGBMatrix{T, O, F}(C, A.fill) # This should automatically be the same orientation. -end +# function OrientedGBMatrix{T, O, F}( +# I::AbstractVector, J::AbstractVector, X::AbstractVector{T}; +# combine = +, nrows = maximum(I), ncols = maximum(J), fill::F = nothing +# ) where {T, O, F} +# I isa Vector || (I = collect(I)) +# J isa Vector || (J = collect(J)) +# X isa Vector || (X = collect(X)) +# A = OrientedGBMatrix{T, O}(nrows, ncols; fill) +# build!(A, I, J, X; combine) +# return A +# end +# function OrientedGBMatrix{O}( +# I::AbstractVector, J::AbstractVector, X::AbstractVector{T}; +# combine = +, nrows = maximum(I), ncols = maximum(J), fill::F = nothing +# ) where {T, O, F} +# return OrientedGBMatrix{T, O, F}(I, J, X,; combine, nrows, ncols, fill) +# end +# +# +# GBMatrixC( +# I::AbstractVector, J::AbstractVector, X::AbstractVector; +# combine = +, nrows = maximum(I), ncols = maximum(J), fill = nothing +# ) = OrientedGBMatrix{ColMajor()}(I, J, X; combine, nrows, ncols, fill) +# GBMatrixR( +# I::AbstractVector, J::AbstractVector, X::AbstractVector; +# combine = +, nrows = maximum(I), ncols = maximum(J), fill = nothing +# ) = OrientedGBMatrix{RowMajor()}(I, J, X; combine, nrows, ncols, fill) +# +# +# #iso constructors +# """ +# OrientedGBMatrix(I, J, x; nrows = maximum(I), ncols = maximum(J)) +# +# Create an nrows x ncols GBMatrix M such that M[I[k], J[k]] = x. +# The resulting matrix is "iso-valued" such that it only stores `x` once rather than once for +# each index. +# """ +# function OrientedGBMatrix{T, O, F}(I::AbstractVector, J::AbstractVector, x::T; +# nrows = maximum(I), ncols = maximum(J), fill::F = nothing) where {T, O, F} +# A = OrientedGBMatrix{T, O, F}(nrows, ncols; fill) +# build!(A, I, J, x) +# return A +# end +# OrientedGBMatrix{O}(I::AbstractVector, J::AbstractVector, x::T; nrows = maximum(I), ncols = maximum(J), fill::F = nothing) where {T, O, F} = +# OrientedGBMatrix{T, O, F}(I, J, x; nrows, ncols, fill) +# +# +# function OrientedGBMatrix{T, O, F}(dims::Dims{2}, x::T; fill::F = nothing) where {T, O, F} +# A = OrientedGBMatrix{T, O, F}(dims; fill) +# A[:, :] = x +# return A +# end +# OrientedGBMatrix{O}(dims::Dims{2}, x::T; fill::F = nothing) where {T, O, F} = OrientedGBMatrix{T, O, F}(dims, x; fill) +# +# OrientedGBMatrix{T, O, F}(nrows, ncols, x::T; fill::F = nothing) where {T, O, F} = OrientedGBMatrix{O}((nrows, ncols), x; fill) +# OrientedGBMatrix{O}(nrows, ncols, x::T; fill::F = nothing) where {T, O, F} = OrientedGBMatrix{T, O, F}(nrows, ncols, x; fill) +# +# function OrientedGBMatrix{T, O, F}(v::GBVector) where {T, O, F} +# O === ByRow() && throw(ArgumentError("Cannot wrap a GBVector in a ByRow matrix.")) +# # this copies, I think that's ideal, and I can implement @view or something at a later date. +# return copy(OrientedGBMatrix{T, O, F}(v.p, v.fill)) +# end +# function OrientedGBMatrix{O}(v::GBVector) where {O} +# # this copies, I think that's ideal, and I can implement @view or something at a later date. +# return OrientedGBMatrix{eltype(v), typeof(v.fill), O}(v) +# end +# +# function OrientedGBMatrix{T, O, F}(A::AbstractGBMatrix) where {T, O, F} +# storageorder(A) != O && throw(ArgumentError("Cannot wrap a GBMatrix in an OrientedGBMatrix with a different orientation.")) +# # this copies, I think that's ideal, and I can implement @view or something at a later date. +# return copy(OrientedGBMatrix{T, O, F}(A.p, A.fill)) +# end +# function OrientedGBMatrix{O}(A::AbstractGBMatrix) where {O} +# # this copies, I think that's ideal, and I can implement @view or something at a later date. +# return OrientedGBMatrix{eltype(A), typeof(A.fill), O}(A) +# end +# +# GBMatrixR(A::AbstractGBMatrix) = OrientedGBMatrix{RowMajor()}(A) +# GBMatrixC(A::AbstractGBMatrix) = OrientedGBMatrix{ColMajor()}(A) +# +# GBMatrixC( +# I::AbstractVector, J::AbstractVector, X::T; +# nrows = maximum(I), ncols = maximum(J), fill = nothing +# ) where {T} = OrientedGBMatrix{ColMajor()}(I, J, X; nrows, ncols, fill) +# GBMatrixR( +# I::AbstractVector, J::AbstractVector, X::T; +# nrows = maximum(I), ncols = maximum(J), fill = nothing +# ) where {T} = OrientedGBMatrix{RowMajor()}(I, J, X; nrows, ncols, fill) +# +# Base.unsafe_convert(::Type{LibGraphBLAS.GrB_Matrix}, A::OrientedGBMatrix) = A.p[] +# +# function Base.copy(A::OrientedGBMatrix{T, O, F}) where {T, O, F} +# C = Ref{LibGraphBLAS.GrB_Matrix}() +# LibGraphBLAS.GrB_Matrix_dup(C, A) +# return OrientedGBMatrix{T, O, F}(C, A.fill) # This should automatically be the same orientation. +# end # because of the fill kwarg we have to redo a lot of the Base.similar dispatch stack. function Base.similar( @@ -180,14 +173,4 @@ function Base.similar( dim1::Integer, dim2::Integer; fill = parent(A).fill ) where {T} return similar(A, (dim1, dim2); fill) -end - -function gbset(A::OrientedGBMatrix, option, value) - if option === :format - throw(ArgumentError("Cannot change orientation of an OrientedGBMatrix")) - end - option = option_toconst(option) - value = option_toconst(value) - GxB_Matrix_Option_set(A, option, value) - return nothing end \ No newline at end of file diff --git a/src/pack.jl b/src/pack.jl index 4de5184d..3c0f61e4 100644 --- a/src/pack.jl +++ b/src/pack.jl @@ -154,6 +154,8 @@ function unsafepack!( A::AbstractGBArray, M::StridedVecOrMat, shallow::Bool = true; order = ColMajor(), decrementindices = false # we don't need this, but it avoids another method. ) + _hasconstantorder(A) && (storageorder(A) !== order) && + (throw(ArgumentError("Cannot change the storage order of $(typeof(A))"))) if order === ColMajor() _packdensematrix!(A, M) else @@ -167,7 +169,8 @@ function unsafepack!( A::AbstractGBArray, ptr, idx, values, shallow::Bool = true; order = ColMajor(), decrementindices = true ) - + _hasconstantorder(A) && (storageorder(A) !== order) && + (throw(ArgumentError("Cannot change the storage order of $(typeof(A))"))) if order === ColMajor() _packcscmatrix!(A, ptr, idx, values; decrementindices) else diff --git a/src/shallowtypes.jl b/src/shallowtypes.jl index a9a86729..04e60276 100644 --- a/src/shallowtypes.jl +++ b/src/shallowtypes.jl @@ -1,38 +1,7 @@ -abstract type AbstractGBShallowArray{T, P, B, A, N, F} <: AbstractGBArray{T, N, F} end - # fall back to nzval, this may need to change eventually, as it's currently not possible to know the storage order. # Either a new parameter or something else. StorageOrders.storageorder(A::AbstractGBShallowArray) = storageorder(A.nzval) - -mutable struct GBShallowVector{T, P, B, A, F} <: AbstractGBShallowArray{T, P, B, A, 1, F} - p::Base.RefValue{LibGraphBLAS.GrB_Matrix} - fill::F - # storage for sparse formats supported by SS:GraphBLAS - ptr::P #colptr / rowptr - idx::P # rowidx / colidx - h::P # hypersparse-only - bitmap::B # bitmap only - nzval::A # array storage for dense arrays, nonzero values storage for everyone else. -end - -function GBShallowVector{T}(p, fill::F, ptr::P, idx::P, h::P, bitmap::B, nzval::A) where {T, P, B, A, F} - GBShallowVector{T, P, B, A, F}(p, fill, ptr, idx, h, bitmap, nzval) -end -mutable struct GBShallowMatrix{T, P, B, A, F} <: AbstractGBShallowArray{T, P, B, A, 2, F} - p::Base.RefValue{LibGraphBLAS.GrB_Matrix} - fill::F - # storage for sparse formats supported by SS:GraphBLAS - ptr::P #colptr / rowptr - idx::P # rowidx / colidx - h::P # hypersparse-only - bitmap::B # bitmap only - nzval::A # array storage for dense arrays, nonzero values storage for everyone else. -end -function GBShallowMatrix{T}(p, fill::F, ptr::P, idx::P, h::P, bitmap::B, nzval::A) where {T, P, B, A, F} - GBShallowVector{T, P, B, A, F}(p, fill, ptr, idx, h, bitmap, nzval) -end - function GBShallowVector(v::DenseVector{T}; fill::F = nothing) where {T, F} m = _newGrBRef() @wraperror LibGraphBLAS.GrB_Matrix_new(m, gbtype(T), size(v, 1), size(v, 2)) @@ -116,8 +85,6 @@ end struct ShallowException <: Exception end Base.showerror(io::IO, ::ShallowException) = print(io, "An AbstractGBShallowArray has been passed to a function which may modify it.") -_canbeoutput(A::AbstractGBShallowArray) = false - Base.empty!(::AbstractGBShallowArray) = throw(ShallowException()) Base.copyto!(::AbstractGBShallowArray, A::GBArrayOrTranspose) = throw(ShallowException()) reshape!(::AbstractGBShallowArray, nrows, ncols; kwargs...) = throw(ShallowException()) diff --git a/src/types.jl b/src/types.jl index b6663b13..b74bde12 100644 --- a/src/types.jl +++ b/src/types.jl @@ -144,6 +144,8 @@ mutable struct TypedMonoid{F, Z, T} <: AbstractTypedOp{Z} end end +binaryop(m::TypedMonoid) = m.binaryop + function (op::TypedMonoid{F, Z, T})(::Type{X}) where {F, X, Z, T} return op end @@ -237,6 +239,10 @@ mutable struct TypedSemiring{FA, FM, X, Y, Z, T} <: AbstractTypedOp{Z} end end +binaryop(s::TypedSemiring) = s.mulop +monoid(s::TypedSemiring) = s.addop +binaryop(s::Tuple) = s[2] +monoid(s::Tuple) = s[1] function (op::TypedSemiring{FA, FM, X, Y, Z, T})(::Type{T1}, ::Type{T2}) where {FA, FM, X, Y, Z, T, T1, T2} return op @@ -275,6 +281,443 @@ mutable struct GBScalar{T} end end + +""" + _newGrBRef()::Ref{LibGraphBLAS.GrB_Matrix} + +Create a reference to a `GrB_Matrix` and attach a finalizer. +""" +_newGrBRef() = finalizer(Ref{LibGraphBLAS.GrB_Matrix}()) do ref + @wraperror LibGraphBLAS.GrB_Matrix_free(ref) +end + +""" + _copyGrBMat(r::RefValue{LibGraphBLAS.GrB_Matrix}) + +copy `r` to a new Ref. This copy shares nothing with `r`. +""" +function _copyGrBMat(r::Base.RefValue{LibGraphBLAS.GrB_Matrix}) + C = Ref{LibGraphBLAS.GrB_Matrix}() + LibGraphBLAS.GrB_Matrix_dup(C, r[]) + return C +end + + +""" + @gbmatrixtype + +Automatically define the basic AbstractGBMatrix interface constructors. +""" +macro gbmatrixtype(typename) + esc(quote + # Empty Constructors: + function $typename{T, F}(nrows::Integer, ncols::Integer; fill = defaultfill(F)) where {T, F} + m = _newGrBRef() + @wraperror LibGraphBLAS.GrB_Matrix_new(m, gbtype(T), nrows, ncols) + return $typename{T, F}(m; fill) + end + $typename{T}(nrows::Integer, ncols::Integer; fill::F = defaultfill(T)) where {T, F} = + $typename{T, F}(nrows, ncols; fill) + + $typename{T, F}(dims::D; fill = defaultfill(F)) where {T, F, D<:Union{Dims{2}, Tuple{<:Integer, <:Integer}}} = + $typename{T, F}(dims...; fill) + $typename{T}(dims::Dims{2}; fill::F = defaultfill(T)) where {T, F} = $typename{T, F}(dims...; fill) + $typename{T}(dims::Tuple{<:Integer, <:Integer}; fill::F = defaultfill(T)) where {T, F} = $typename{T, F}(dims...; fill) + + $typename{T, F}(size::Tuple{Base.OneTo, Base.OneTo}; fill = defaultfill(F)) where {T, F} = + $typename{T, F}(size[1].stop, size[2].stop; fill) + $typename{T}(size::Tuple{Base.OneTo, Base.OneTo}; fill::F = defaultfill(T)) where {T, F} = + $typename{T, F}(size; fill) + + # Coordinate Form Constructors: + function $typename{T, F}( + I::AbstractVector, J::AbstractVector, X::AbstractVector{T2}, nrows, ncols; + combine = +, fill = defaultfill(F) + ) where {T, F, T2} + I isa Vector || (I = collect(I)) + J isa Vector || (J = collect(J)) + (T2 == T && X isa DenseVector) || (X = convert(Vector{T2}, X)) + A = $typename{T, F}(nrows, ncols; fill) + build!(A, I, J, X; combine) + return A + end + $typename{T, F}( + I::AbstractVector, J::AbstractVector, X::AbstractVector{T2}; + combine = +, fill = defaultfill(F) + ) where {T, F, T2} = $typename{T, F}(I, J, X, maximum(I), maximum(J); combine, fill) + + function $typename{T}( + I::AbstractVector, J::AbstractVector, X::AbstractVector, + nrows, ncols; combine = +, fill::F = defaultfill(T) + ) where {T, F} + return $typename{T, F}(I, J, X, nrows, ncols; combine, fill) + end + $typename{T}( + I::AbstractVector, J::AbstractVector, X::AbstractVector; + combine = +, fill = defaultfill(T) + ) where {T} = $typename{T}(I, J, X, maximum(I), maximum(J); combine, fill) + + $typename( + I::AbstractVector, J::AbstractVector, X::AbstractVector{T}, nrows, ncols; + combine = +, fill = defaultfill(T) + ) where T = $typename{T}(I, J, X, nrows, ncols; combine, fill) + $typename( + I::AbstractVector, J::AbstractVector, X::AbstractVector{T}; + combine = +, fill = defaultfill(T) + ) where {T} = $typename{T}(I, J, X; combine, fill) + + # ISO constructors: + function $typename{T, F}( + I::AbstractVector, J::AbstractVector, x, + nrows, ncols; fill = defaultfill(F) + ) where {T, F} + A = $typename{T, F}(nrows, ncols; fill) + build!(A, I, J, convert(T, x)) + return A + end + $typename{T, F}( + I::AbstractVector, J::AbstractVector, x; + fill = defaultfill(F) + ) where {T, F} = $typename{T, F}(I, J, x, maximum(I), maximum(J); fill) + + function $typename{T}( + I::AbstractVector, J::AbstractVector, x, nrows, ncols; + fill::F = defaultfill(T) + ) where {T, F} + return $typename{T, F}(I, J, x, nrows, ncols; fill) + end + $typename{T}( + I::AbstractVector, J::AbstractVector, x; fill = defaultfill(T) + ) where {T} = $typename{T}(I, J, x, maximum(I), maximum(J); fill) + + function $typename( + I::AbstractVector, J::AbstractVector, x::T, nrows, ncols; + fill = defaultfill(T)) where {T} + $typename{T}(I, J, x, nrows, ncols; fill) + end + $typename(I::AbstractVector, J::AbstractVector, x::T; fill = defaultfill(T)) where T = + $typename{T}(I, J, x, maximum(I), maximum(J); fill) + + function $typename{T, F}(dims::Dims{2}, x; fill = defaultfill(F)) where {T, F} + A = $typename{T, F}(dims; fill) + A .= x + return A + end + $typename{T}(dims::Dims{2}, x; fill::F = defaultfill(T)) where {T, F} = + $typename{T, F}(dims, x; fill) + $typename(dims::Dims{2}, x::T; fill = defaultfill(T)) where T = + $typename{T}(dims, x, fill) + + $typename(nrows, ncols, x::T; fill = defaultfill(T)) where T = + $typename{T}((nrows, ncols), x; fill) + $typename(dims::Tuple{<:Integer}, x::T; fill = defaultfill(T)) where T = + $typename{T}(dims..., x; fill) + $typename(size::Tuple{Base.OneTo, Base.OneTo}, x::T; fill = defaultfill(T)) where T = + $typename{T}(size[1].stop, size[2].stop, x; fill) + + function $typename{T, F}(v::AbstractGBVector; fill = getfill(v)) where {T, F} + return convert($typename{T, F}, v; fill) + end + function $typename{T}(v::AbstractGBVector; fill::F = getfill(v)) where {T, F} + return $typename{T, F}(v; fill) + end + + # Pack based constructors: + function $typename{T, F}( + A::Union{<:AbstractVector, <:AbstractMatrix}; + fill = defaultfill(F) + ) where {T, F} + vpack = _sizedjlmalloc(length(A), T) + vpack = unsafe_wrap(Array, vpack, size(A)) + copyto!(vpack, A) + C = $typename{T, F}(size(A, 1), size(A, 2); fill) + return unsafepack!(C, vpack, false; order = storageorder(A)) + end + $typename{T}( + A::Union{<:AbstractVector, <:AbstractMatrix}; + fill::F = defaultfill(T) + ) where {T, F} = $typename{T, F}(A; fill) + $typename( + A::Union{<:AbstractVector{T}, <:AbstractMatrix{T}}; + fill::F = defaultfill(T) + ) where {T, F} = $typename{T, F}(A; fill) + + function $typename{T, F}( + A::SparseVector; + fill = defaultfill(F) + ) where {T, F} + C = $typename{T, F}(size(A, 1), 1; fill) + return unsafepack!(C, _copytoraw(A)..., false) + end + $typename{T}( + A::SparseVector; + fill::F = defaultfill(T) + ) where {T, F} = $typename{T, F}(A; fill) + $typename( + A::SparseVector{T}; + fill::F = defaultfill(T) + ) where {T, F} = $typename{T, F}(A; fill) + + function $typename{T, F}( + A::SparseMatrixCSC; + fill = defaultfill(F) + ) where {T, F} + C = $typename{T, F}(size(A)...; fill) + return unsafepack!(C, _copytoraw(A)..., false) + end + $typename{T}( + A::SparseMatrixCSC; + fill::F = defaultfill(T) + ) where {T, F} = $typename{T, F}(A; fill) + $typename( + A::SparseMatrixCSC{T}; + fill::F = defaultfill(T) + ) where {T, F} = $typename{T, F}(A; fill) + + # similar + function Base.similar( + A::$typename{T}, ::Type{TNew} = T, + dims::Tuple{Int64, Vararg{Int64, N}} = size(A); fill = getfill(A) + ) where {T, TNew, N} + if dims isa Dims{1} + # TODO: When new Vector types are added this will be incorrect. + x = GBVector{TNew}(dims...; fill) + else + x = $typename{TNew}(dims...; fill) + end + _hasconstantorder(x) || setstorageorder!(x, storageorder(A)) + return x + end + + function Base.similar(A::$typename{T}, dims::Tuple; fill = getfill(A)) where T + return similar(A, T, dims; fill) + end + + function Base.similar( + A::$typename, ::Type{TNew}, + dims::Integer; fill = getfill(A) + ) where TNew + return similar(A, TNew, (dims,); fill) + end + + function Base.similar( + A::$typename, ::Type{TNew}, + dim1::Integer, dim2::Integer; fill = getfill(A) + ) where TNew + return similar(A, TNew, (dim1, dim2); fill) + end + + function Base.similar( + A::$typename, + dims::Integer; fill = getfill(A) + ) + return similar(A, (dims,); fill) + end + + function Base.similar( + A::$typename, + dim1::Integer, dim2::Integer; fill = getfill(A) + ) + return similar(A, (dim1, dim2); fill) + end + end) +end + +macro gbvectortype(typename) + esc(quote + function $typename{T, F}(n::Integer; fill = defaultfill(F)) where {T, F} + m = _newGrBRef() + @wraperror LibGraphBLAS.GrB_Matrix_new(m, gbtype(T), n, 1) + return $typename{T, F}(m; fill) + end + $typename{T}(n::Integer; fill::F = defaultfill(T)) where {T, F} = + $typename{T, F}(n; fill) + + $typename{T, F}(dims::D; fill = defaultfill(F)) where {T, F, D<:Union{Dims{1}, Tuple{<:Integer}}} = + $typename{T, F}(dims...; fill) + $typename{T}(dims::Dims{1}; fill::F = defaultfill(T)) where {T, F} = $typename{T, F}(dims...; fill) + $typename{T}(dims::Tuple{<:Integer}; fill::F = defaultfill(T)) where {T, F} = $typename{T, F}(dims...; fill) + + $typename{T, F}(size::Tuple{Base.OneTo}; fill = defaultfill(F)) where {T, F} = + $typename{T, F}(size[1].stop; fill) + $typename{T}(size::Tuple{Base.OneTo}; fill::F = defaultfill(T)) where {T, F} = + $typename{T, F}(size; fill) + + function $typename{T, F}( + I::AbstractVector, X::AbstractVector{T2}, n; + combine = +, fill = defaultfill(F) + ) where {T, F, T2} + I isa Vector || (I = collect(I)) + (T2 == T && X isa DenseVector) || (X = convert(Vector{T2}, X)) + A = $typename{T, F}(n; fill) + build!(A, I, X; combine) + return A + end + $typename{T, F}( + I::AbstractVector, X::AbstractVector{T2}; + combine = +, fill = defaultfill(F) + ) where {T, F, T2} = $typename{T, F}(I, X, maximum(I); combine, fill) + + function $typename{T}( + I::AbstractVector, X::AbstractVector, + n; combine = +, fill::F = defaultfill(T) + ) where {T, F} + return $typename{T, F}(I, X, n; combine, fill) + end + $typename{T}( + I::AbstractVector, X::AbstractVector; + combine = +, fill = defaultfill(T) + ) where {T} = $typename{T}(I, X, maximum(I); combine, fill) + + $typename( + I::AbstractVector, X::AbstractVector{T}, n; + combine = +, fill = defaultfill(T) + ) where T = $typename{T}(I, X, n; combine, fill) + $typename( + I::AbstractVector, X::AbstractVector{T}; + combine = +, fill = defaultfill(T) + ) where {T} = $typename{T}(I, X; combine, fill) + + function $typename{T, F}( + I::AbstractVector, x, + n; fill = defaultfill(F) + ) where {T, F} + A = $typename{T, F}(n; fill) + build!(A, I, convert(T, x)) + return A + end + $typename{T, F}( + I::AbstractVector, x; + fill = defaultfill(F) + ) where {T, F} = $typename{T, F}(I, x, maximum(I); fill) + + function $typename{T}( + I::AbstractVector, x, n; + fill::F = defaultfill(T) + ) where {T, F} + return $typename{T, F}(I, x, n; fill) + end + $typename{T}( + I::AbstractVector, x; fill = defaultfill(T) + ) where {T} = $typename{T}(I, x, maximum(I); fill) + + function $typename( + I::AbstractVector, x::T, n; + fill = defaultfill(T)) where {T} + $typename{T}(I, J, x, n; fill) + end + $typename(I::AbstractVector, x::T; fill = defaultfill(T)) where T = + $typename{T}(I, x, maximum(I); fill) + + function $typename{T, F}(dims::Dims{1}, x; fill = defaultfill(F)) where {T, F} + A = $typename{T, F}(dims; fill) + A .= x + return A + end + $typename{T}(dims::Dims{1}, x; fill::F = defaultfill(T)) where {T, F} = + $typename{T, F}(dims, x; fill) + $typename(dims::Dims{1}, x::T; fill = defaultfill(T)) where T = + $typename{T}(dims, x, fill) + + $typename(nrows, ncols, x::T; fill = defaultfill(T)) where T = + $typename{T}((nrows, ncols), x; fill) + $typename(dims::Tuple{<:Integer}, x::T; fill = defaultfill(T)) where T = + $typename{T}(dims..., x; fill) + $typename(size::Tuple{Base.OneTo, Base.OneTo}, x::T; fill = defaultfill(T)) where T = + $typename{T}(size[1].stop, size[2].stop, x; fill) + + function $typename{T, F}(v::AbstractGBVector; fill = getfill(v)) where {T, F} + return convert($typename{T, F}, v; fill) + end + function $typename{T}(v::AbstractGBVector; fill::F = getfill(v)) where {T, F} + return $typename{T, F}(v; fill) + end + + # Pack based constructors: + function $typename{T, F}( + A::AbstractVector; + fill = defaultfill(F) + ) where {T, F} + vpack = _sizedjlmalloc(length(A), T) + vpack = unsafe_wrap(Array, vpack, size(A)) + copyto!(vpack, A) + C = $typename{T, F}(size(A); fill) + return unsafepack!(C, vpack, false; order = storageorder(A)) + end + $typename{T}( + A::AbstractVector; + fill::F = defaultfill(T) + ) where {T, F} = $typename{T, F}(A; fill) + $typename( + A::AbstractVector{T}; + fill::F = defaultfill(T) + ) where {T, F} = $typename{T, F}(A; fill) + + function $typename{T, F}( + A::SparseVector; + fill = defaultfill(F) + ) where {T, F} + C = $typename{T, F}(size(A, 1); fill) + return unsafepack!(C, _copytoraw(A)..., false) + end + $typename{T}( + A::SparseVector; + fill::F = defaultfill(T) + ) where {T, F} = $typename{T, F}(A; fill) + $typename( + A::SparseVector{T}; + fill::F = defaultfill(T) + ) where {T, F} = $typename{T, F}(A; fill) + + # similar + function Base.similar( + v::$typename{T}, ::Type{TNew} = T, + dims::Tuple{Int64, Vararg{Int64, N}} = size(v); fill = getfill(v) + ) where {T, TNew, N} + if dims isa Dims{1} + # TODO: Check this for correctness!!! + x = $typename{TNew}(dims...; fill) + else + x = $GBMatrix{TNew}(dims...; fill) + end + _hasconstantorder(x) || setstorageorder!(x, storageorder(v)) + return x + end + + function Base.similar(v::$typename{T}, dims::Tuple; fill = getfill(v)) where T + return similar(v, T, dims; fill) + end + + function Base.similar( + v::$typename, ::Type{TNew}, + dims::Integer; fill = getfill(v) + ) where TNew + return similar(v, TNew, (dims,); fill) + end + + function Base.similar( + v::$typename, ::Type{TNew}, + dim1::Integer, dim2::Integer; fill = getfill(v) + ) where TNew + return similar(v, TNew, (dim1, dim2); fill) + end + + function Base.similar( + v::$typename, + dims::Integer; fill = getfill(v) + ) + return similar(v, (dims,); fill) + end + + function Base.similar( + v::$typename, + dim1::Integer, dim2::Integer; fill = getfill(v) + ) + return similar(v, (dim1, dim2); fill) + end + end) +end + """ GBVector{T, F} <: AbstractSparseArray{T, UInt64, 1} @@ -290,6 +733,17 @@ mutable struct GBVector{T, F} <: AbstractGBVector{T, F} fill::F end +function GBVector{T, F}(p::Base.RefValue{LibGraphBLAS.GrB_Matrix}; fill = defaultfill(F)) where {T, F} + fill = convert(F, fill) # conversion to F happens at the last possible moment. + return GBVector{T, F}(p, fill) +end +GBVector{T}( + p::Base.RefValue{LibGraphBLAS.GrB_Matrix}; + fill::F = defaultfill(T) +) where {T, F} = return GBVector{T, F}(p; fill) + +# we call @gbvectortype GBVector below GBMatrix defn. + """ GBMatrix{T, F} <: AbstractSparseArray{T, UInt64, 2} @@ -310,7 +764,101 @@ mutable struct GBMatrix{T, F} <: AbstractGBMatrix{T, F} fill::F end +function GBMatrix{T, F}(p::Base.RefValue{LibGraphBLAS.GrB_Matrix}; fill = defaultfill(F)) where {T, F} + fill = convert(F, fill) # conversion to F happens at the last possible moment. + return GBMatrix{T, F}(p, fill) +end +GBMatrix{T}( + p::Base.RefValue{LibGraphBLAS.GrB_Matrix}; + fill::F = defaultfill(T) +) where {T, F} = return GBMatrix{T, F}(p; fill) + +@gbmatrixtype GBMatrix +@gbvectortype GBVector + +mutable struct OrientedGBMatrix{T, F, O} <: AbstractGBMatrix{T, F} + p::Base.RefValue{LibGraphBLAS.GrB_Matrix} + fill::F + function OrientedGBMatrix{T, F, O}( + p::Base.RefValue{LibGraphBLAS.GrB_Matrix}, + fill::F + ) where {T, F, O} + O isa StorageOrders.StorageOrder || throw(ArgumentError("$O is not a valid StorageOrder")) + A = new{T, F, O}(p, fill) + order = option_toconst(O) + LibGraphBLAS.GxB_Matrix_Option_set(A, LibGraphBLAS.GxB_FORMAT, order) + return A + end +end +function OrientedGBMatrix{T, F, O}( + p::Base.RefValue{LibGraphBLAS.GrB_Matrix}; + fill = defaultfill(F) +) where {T, F, O} + fill = convert(F, fill) + A = OrientedGBMatrix{T, F, O}(p, fill) + # we can't use `setstorageorder!` here since it's banned for OrientedGBMatrix + return A +end + +function OrientedGBMatrix{T, O}( + p::Base.RefValue{LibGraphBLAS.GrB_Matrix}; + fill::F = defaultfill(T) +) where {T, F, O} + return OrientedGBMatrix{T, F, O}(p; fill) +end + +const GBMatrixC{T, F} = OrientedGBMatrix{T, F, StorageOrders.ColMajor()} +const GBMatrixR{T, F} = OrientedGBMatrix{T, F, StorageOrders.RowMajor()} + +@gbmatrixtype GBMatrixC +@gbmatrixtype GBMatrixR + +#= + Shallow array types + +These types do not have the general constructors created by `@gbmatrixtype` since they +should *never* be constructed by a user directly. Only through the `pack` interface. +=# +mutable struct GBShallowVector{T, F, P, B, A} <: AbstractGBShallowArray{T, F, P, B, A, 1} + p::Base.RefValue{LibGraphBLAS.GrB_Matrix} + fill::F + # storage for sparse formats supported by SS:GraphBLAS + ptr::P #colptr / rowptr + idx::P # rowidx / colidx + h::P # hypersparse-only + bitmap::B # bitmap only + nzval::A # array storage for dense arrays, nonzero values storage for everyone else. +end +function GBShallowVector{T}(p, fill::F, ptr::P, idx::P, h::P, bitmap::B, nzval::A) where {T, F, P, B, A} + GBShallowVector{T, F, P, B, A}(p, fill, ptr, idx, h, bitmap, nzval) +end + +mutable struct GBShallowMatrix{T, F, P, B, A} <: AbstractGBShallowArray{T, F, P, B, A, 2} + p::Base.RefValue{LibGraphBLAS.GrB_Matrix} + fill::F + # storage for sparse formats supported by SS:GraphBLAS + ptr::P #colptr / rowptr + idx::P # rowidx / colidx + h::P # hypersparse-only + bitmap::B # bitmap only + nzval::A # array storage for dense arrays, nonzero values storage for everyone else. +end +function GBShallowMatrix{T}(p, fill::F, ptr::P, idx::P, h::P, bitmap::B, nzval::A) where {T, F, P, B, A} + GBShallowVector{T, F, P, B, A}(p, fill, ptr, idx, h, bitmap, nzval) +end + # We need to do this at runtime. This should perhaps be `RuntimeOrder`, but that trait should likely be removed. # This should ideally work out fine. a GBMatrix or GBVector won't have StorageOrders.storageorder(A::AbstractGBMatrix) = gbget(A, :format) == Integer(BYCOL) ? StorageOrders.ColMajor() : StorageOrders.RowMajor() -StorageOrders.storageorder(A::AbstractGBVector) = ColMajor() \ No newline at end of file +StorageOrders.storageorder(::AbstractGBVector) = ColMajor() + +defaultfill(::Type{T}) where T = zero(T) +defaultfill(::Type{Nothing}) = nothing +defaultfill(::Type{Missing}) = missing +# This is bold, I'm not sure if I like it... +# It boils down to whether we want numeric sparse arrays to be default +# or for graph sparse arrays to be default. +# I don't think it is onerous for graph algorithm writers to say `GBMatrix{Int64, Nothing}`, +# and provides better defaults for other users. +defaultfill(::Type{Union{T, Nothing}}) where T = T +defaultfill(::Type{Union{T, Missing}}) where T = T \ No newline at end of file diff --git a/src/unpack.jl b/src/unpack.jl index 0928516c..19acc354 100644 --- a/src/unpack.jl +++ b/src/unpack.jl @@ -46,7 +46,6 @@ function _unpackdensematrix!( _jlfree(x) end end - # eltype(M) == T || (M = copy(reinterpret(T, M))) if length(v) != length(A) resize!(v, length(A)) end diff --git a/src/vector.jl b/src/vector.jl index d93e9da1..6645bc59 100644 --- a/src/vector.jl +++ b/src/vector.jl @@ -3,90 +3,77 @@ """ GBVector{T}(n; fill = nothing) """ -function GBVector{T}(n; fill::F = nothing) where {T, F} - m = Ref{LibGraphBLAS.GrB_Matrix}() - @wraperror LibGraphBLAS.GrB_Matrix_new(m, gbtype(T), n, 1) - return GBVector{T}(m; fill) -end - -function GBVector{T}( - p::Base.RefValue{LibGraphBLAS.GrB_Matrix}; - fill::F = nothing -) where {T, F} - - v = GBVector{T, F}(finalizer(p) do ref - @wraperror LibGraphBLAS.GrB_Matrix_free(ref) - end, fill) - gbset(v, FORMAT, BYCOL) - return v -end - -GBVector{T}(dims::Dims{1}; fill = nothing) where {T} = GBVector{T}(dims...; fill) -GBVector{T}(nrows::Base.OneTo; fill = nothing) where {T} = - GBVector{T}(nrows.stop; fill) -GBVector{T}(nrows::Tuple{Base.OneTo,}; fill = nothing) where {T} = GBVector{T}(first(nrows); fill) - -""" - GBVector(I::AbstractVector, X::AbstractVector{T}; fill = nothing) - -Create a GBVector from a vector of indices `I` and a vector of values `X`. -""" -function GBVector(I::AbstractVector{U}, X::AbstractVector{T}; combine = +, nrows = maximum(I), fill = nothing) where {U<:Integer, T} - I isa Vector || (I = collect(I)) - X isa Vector || (X = collect(X)) - v = GBVector{T}(nrows; fill) - build!(v, I, X; combine) - return v -end - -function GBVector{T}( - I::AbstractVector, X::AbstractVector{Told}; - combine = +, nrows = maximum(I), fill = nothing -) where {T, U, Told} - return GBVector(I, T.(X); combine, nrows, fill) -end -#iso valued constructors. -""" - GBVector(I, x; nrows = maximum(I) fill = nothing) - -Create an GBVector `v` from coordinates `I` such that `M[I] = x` . -The resulting vector is "iso-valued" such that it only stores `x` once rather than once for -each index. -""" -function GBVector(I::AbstractVector{U}, x::T; - nrows = maximum(I), fill = nothing) where {U<:Integer, T} - A = GBVector{T}(nrows; fill) - build!(A, I, x) - return A -end - -""" - GBVector(n, x; fill = nothing) - -Create an `n` length dense GBVector `v` such that M[:] = x. -The resulting vector is "iso-valued" such that it only stores `x` once rather than once for -each index. -""" -function GBVector(n::Integer, x::T; fill = nothing) where {T} - v = GBVector{T}(n; fill) - v .= x - return v -end - -GBVector{T, F}(::Number) where {T, F} = throw(ArgumentError("The F parameter is implicit and determined by the `fill` keyword argument to constructors. Users must not specify this manually.")) - -function GBVector(v::AbstractVector{T}; fill::F = nothing) where {T, F} - if v isa AbstractVector && !(v isa Vector) - v = collect(v) - end - A = GBVector{T}(size(v, 1); fill) - return unsafepack!(A, _copytoraw(v), false) -end - -function GBVector(v::SparseVector{T}; fill::F = nothing) where {T, F} - A = GBVector{T}(size(v, 1); fill) - return unsafepack!(A, _copytoraw(v)..., false) -end +# function GBVector{T}(n; fill::F = nothing) where {T, F} +# m = Ref{LibGraphBLAS.GrB_Matrix}() +# @wraperror LibGraphBLAS.GrB_Matrix_new(m, gbtype(T), n, 1) +# return GBVector{T}(m; fill) +# end +# +# function GBVector{T}( +# p::Base.RefValue{LibGraphBLAS.GrB_Matrix}; +# fill::F = nothing +# ) where {T, F} +# +# v = GBVector{T, F}(finalizer(p) do ref +# @wraperror LibGraphBLAS.GrB_Matrix_free(ref) +# end, fill) +# gbset(v, FORMAT, BYCOL) +# return v +# end +# +# GBVector{T}(dims::Dims{1}; fill = nothing) where {T} = GBVector{T}(dims...; fill) +# GBVector{T}(nrows::Base.OneTo; fill = nothing) where {T} = +# GBVector{T}(nrows.stop; fill) +# GBVector{T}(nrows::Tuple{Base.OneTo,}; fill = nothing) where {T} = GBVector{T}(first(nrows); fill) +# +# """ +# GBVector(I::AbstractVector, X::AbstractVector{T}; fill = nothing) +# +# Create a GBVector from a vector of indices `I` and a vector of values `X`. +# """ +# function GBVector(I::AbstractVector{U}, X::AbstractVector{T}; combine = +, nrows = maximum(I), fill = nothing) where {U<:Integer, T} +# I isa Vector || (I = collect(I)) +# X isa Vector || (X = collect(X)) +# v = GBVector{T}(nrows; fill) +# build!(v, I, X; combine) +# return v +# end +# +# function GBVector{T}( +# I::AbstractVector, X::AbstractVector{Told}; +# combine = +, nrows = maximum(I), fill = nothing +# ) where {T, U, Told} +# return GBVector(I, T.(X); combine, nrows, fill) +# end +# #iso valued constructors. +# """ +# GBVector(I, x; nrows = maximum(I) fill = nothing) +# +# Create an GBVector `v` from coordinates `I` such that `M[I] = x` . +# The resulting vector is "iso-valued" such that it only stores `x` once rather than once for +# each index. +# """ +# function GBVector(I::AbstractVector{U}, x::T; +# nrows = maximum(I), fill = nothing) where {U<:Integer, T} +# A = GBVector{T}(nrows; fill) +# build!(A, I, x) +# return A +# end +# +# """ +# GBVector(n, x; fill = nothing) +# +# Create an `n` length dense GBVector `v` such that M[:] = x. +# The resulting vector is "iso-valued" such that it only stores `x` once rather than once for +# each index. +# """ +# function GBVector(n::Integer, x::T; fill = nothing) where {T} +# v = GBVector{T}(n; fill) +# v .= x +# return v +# end +# +# GBVector{T, F}(::Number) where {T, F} = throw(ArgumentError("The F parameter is implicit and determined by the `fill` keyword argument to constructors. Users must not specify this manually.")) # Some Base and basic SparseArrays/LinearAlgebra functions: ########################################################### diff --git a/test/Manifest.toml b/test/Manifest.toml new file mode 100644 index 00000000..c1f816b1 --- /dev/null +++ b/test/Manifest.toml @@ -0,0 +1,215 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.8.1" +manifest_format = "2.0" +project_hash = "0e610cf8435f78ad82784147cc6e89c101047f5f" + +[[deps.ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" +version = "1.1.1" + +[[deps.Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" + +[[deps.Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[deps.ChainRulesCore]] +deps = ["Compat", "LinearAlgebra", "SparseArrays"] +git-tree-sha1 = "dc4405cee4b2fe9e1108caec2d760b7ea758eca2" +uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" +version = "1.15.5" + +[[deps.ChainRulesTestUtils]] +deps = ["ChainRulesCore", "Compat", "FiniteDifferences", "LinearAlgebra", "Random", "Test"] +git-tree-sha1 = "426d44d79dbbb25e423ab6efe067f8e91350ff95" +uuid = "cdddcdb0-9152-4a09-a978-84456f9df70a" +version = "1.9.3" + +[[deps.Compat]] +deps = ["Dates", "LinearAlgebra", "UUIDs"] +git-tree-sha1 = "5856d3031cdb1f3b2b6340dfdc66b6d9a149a374" +uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" +version = "4.2.0" + +[[deps.CompilerSupportLibraries_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" +version = "0.5.2+0" + +[[deps.Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[deps.Downloads]] +deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" +version = "1.6.0" + +[[deps.FileWatching]] +uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" + +[[deps.FiniteDifferences]] +deps = ["ChainRulesCore", "LinearAlgebra", "Printf", "Random", "Richardson", "SparseArrays", "StaticArrays"] +git-tree-sha1 = "0ee1275eb003b6fc7325cb14301665d1072abda1" +uuid = "26cc04aa-876d-5657-8c51-4c34ba976000" +version = "0.12.24" + +[[deps.InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[deps.KLU]] +deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse_jll"] +git-tree-sha1 = "764164ed65c30738750965d55652db9c94c59bfe" +uuid = "ef3ab10e-7fda-4108-b977-705223b18434" +version = "0.4.0" + +[[deps.LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" +version = "0.6.3" + +[[deps.LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" +version = "7.84.0+0" + +[[deps.LibGit2]] +deps = ["Base64", "NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[deps.LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" +version = "1.10.2+0" + +[[deps.Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[deps.LinearAlgebra]] +deps = ["Libdl", "libblastrampoline_jll"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[deps.Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[deps.Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[deps.MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" +version = "2.28.0+0" + +[[deps.MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" +version = "2022.2.1" + +[[deps.NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" +version = "1.2.0" + +[[deps.OpenBLAS_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" +version = "0.3.20+0" + +[[deps.Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +version = "1.8.0" + +[[deps.Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[deps.REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[deps.Random]] +deps = ["SHA", "Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[deps.Richardson]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "e03ca566bec93f8a3aeb059c8ef102f268a38949" +uuid = "708f8203-808e-40c0-ba2d-98a6953ed40d" +version = "1.4.0" + +[[deps.SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +version = "0.7.0" + +[[deps.Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[deps.Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[deps.SparseArrays]] +deps = ["LinearAlgebra", "Random"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[deps.StaticArrays]] +deps = ["LinearAlgebra", "Random", "StaticArraysCore", "Statistics"] +git-tree-sha1 = "efa8acd030667776248eabb054b1836ac81d92f0" +uuid = "90137ffa-7385-5640-81b9-e52037218182" +version = "1.5.7" + +[[deps.StaticArraysCore]] +git-tree-sha1 = "ec2bd695e905a3c755b33026954b119ea17f2d22" +uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" +version = "1.3.0" + +[[deps.Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[deps.SuiteSparse_jll]] +deps = ["Artifacts", "Libdl", "Pkg", "libblastrampoline_jll"] +uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" +version = "5.10.1+0" + +[[deps.TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" +version = "1.0.0" + +[[deps.Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" +version = "1.10.0" + +[[deps.Test]] +deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[deps.UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[deps.Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" + +[[deps.Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" +version = "1.2.12+3" + +[[deps.libblastrampoline_jll]] +deps = ["Artifacts", "Libdl", "OpenBLAS_jll"] +uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" +version = "5.1.1+0" + +[[deps.nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" +version = "1.48.0+0" + +[[deps.p7zip_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" +version = "17.4.0+0" diff --git a/test/asjulia.jl b/test/asjulia.jl deleted file mode 100644 index e41ce795..00000000 --- a/test/asjulia.jl +++ /dev/null @@ -1,40 +0,0 @@ -@testset "asjulia" begin - A = [[1,2] [3,4]] - B = GBMatrix(A) - @test (A .== A) == SuiteSparseGraphBLAS.as((x) -> x .== A, Matrix, B) - SuiteSparseGraphBLAS.as(Matrix, B; dropzeros=true) do mat - mat[2, 2] = zero(eltype(mat)) - return nothing - end - - u = [1,3,5,7,9] - v = GBVector(u) - @test 25 == SuiteSparseGraphBLAS.as(Vector, v) do vec - sum(vec) - end - - A = spdiagm([1,3,5,7,9]) - B = GBMatrix(A) - @test SparseMatrixCSC(B) == A - @test 25 == SuiteSparseGraphBLAS.as(SparseMatrixCSC, B, freeunpacked=true) do mat - x = 0 - for i ∈ 1:5 - x += mat[i, i] - end - return x - end - - u = sparsevec([1,3,5,7,9], [1,2,3,4,5]) - v = GBVector(u) - @test SparseVector(v) == u - SuiteSparseGraphBLAS.as(SparseVector, v) do vec - for i ∈ 1:2:9 - vec[i] = vec[i] * 10 - end - return nothing - end - @test sum(v) == 150 - @test u .* 10 == SuiteSparseGraphBLAS.as(SparseVector, v; freeunpacked=true) do vec - copy(vec) - end -end \ No newline at end of file diff --git a/test/gbarray.jl b/test/gbarray.jl index 56358eb4..fb3c9c2c 100644 --- a/test/gbarray.jl +++ b/test/gbarray.jl @@ -17,12 +17,12 @@ x = sprand(Int64, 100, 100, 0.05) m = GBMatrix(x) deleteat!(m, 1, 2) - @test m[1, 2] === nothing - @test m[:, 2] == GBVector(x[:, 2]) + @test m[1, 2] === getfill(m) + @test m[:, 3] == GBVector(x[:, 3]) @test m[2, :] == GBVector(x[2, :]) @test m[:, :] == m - @test m[1:2:5, 1:2] == GBMatrix(x[1:2:5, 1:2]) - @test m[1:2:5, :] == GBMatrix(x[1:2:5, :]) + @test m[1:2:5, 3:5] == GBMatrix(x[1:2:5, 3:5]) + @test m[3:2:9, :] == GBMatrix(x[3:2:9, :]) A = GBMatrix([[1,2] [3,4]]) @test A[[1,2], [1,1]] == [[1,2] [1,2]] @@ -64,7 +64,9 @@ mask = GBMatrix([[true, true, false] [false, true, true] [true, false,true]]) m[8:10, 8:10, mask = mask, accum = *, desc = Descriptor(replace_output=true)] = fill(10, 3, 3) - @test m[9, 10] === nothing + @test m[9, 10] == zero(eltype(m)) + n = setfill(m, nothing) + @test n[9, 10] === nothing @test m[10, 10] == 90 #vectors diff --git a/test/issues.jl b/test/issues.jl index c1acc5ab..b64541a4 100644 --- a/test/issues.jl +++ b/test/issues.jl @@ -5,7 +5,7 @@ A = GBMatrix([1,1,2,2,3,4,4,5,6,7,7,7], [2,4,5,7,6,1,3,6,3,3,4,5], [1:12...]) @test A[:, 2] isa GBVector @test A[2, :] isa GBVector - @test emul(A[:, 2], B) == GBVector([1], [1], nrows = 7) + @test emul(A[:, 2], B) == GBVector([1], [1], 7) # test that GBMatrix GBVector is allowed # with correct sizes: @@ -38,12 +38,6 @@ @test *(Monoid((a, b)->a + b, zero), *)(A, A) == A * A end - @testset "#71" begin - # segfault with weird ctor - @test_throws ArgumentError GBMatrix{Int, Int}(10, 10) - @test_throws ArgumentError GBVector{Int, Int}(10) - end - @testset "#85" begin A = GBMatrix([1,1,2,2,3,4,4,5,6,7,7,7], [2,4,5,7,6,1,3,6,3,3,4,5], [1:12...]) B = GBVector([3, 4, 5, 6, 7], [3, 4, 5, 6, 7]) diff --git a/test/operations/kron.jl b/test/operations/kron.jl index d4ce7a0c..0708287f 100644 --- a/test/operations/kron.jl +++ b/test/operations/kron.jl @@ -9,6 +9,6 @@ mask[17:20, 5:8] = false #don't care value, using structural #mask out bottom chunk using structural complement o2 = kron(m1, n1; mask, desc=Descriptor(structural_mask=true, complement_mask=true)) - @test o2[20, 5] === nothing #We don't want values in masked out area + @test o2[20, 5] === getfill(o2) #We don't want values in masked out area @test o2[1:2:15, :] == o1[1:2:15, :] #The rest should match, test indexing too. end diff --git a/test/runtests.jl b/test/runtests.jl index c0eed5ca..5a6ce012 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -74,6 +74,7 @@ println("$(SuiteSparseGraphBLAS.get_lib())") include_test("operatorutils.jl") include_test("ops.jl") include_test("gbarray.jl") + include_test("types.jl") include_test("issues.jl") include_test("operations/ewise.jl") include_test("operations/kron.jl") diff --git a/test/types.jl b/test/types.jl new file mode 100644 index 00000000..eef803c8 --- /dev/null +++ b/test/types.jl @@ -0,0 +1,26 @@ +@testset "types.jl" begin +for type ∈ [GBMatrix, GBMatrixC, GBMatrixR] + # {T, F}(nrows, ncols; fill) + A = type{Int64, Float64}(3, 3) + @test getfill(A) === zero(Float64) + @test nnz(A) == 0 + type === GBMatrixC && (@test storageorder(A) === ColMajor()) + type === GBMatrixR && (@test storageorder(A) === RowMajor()) + A = type{Int64, Float64}(3, 3; fill = 3) + @test getfill(A) === 3.0 + type === GBMatrixC && (@test storageorder(A) === ColMajor()) + type === GBMatrixR && (@test storageorder(A) === RowMajor()) + @test_throws MethodError type{Int64, Nothing}(3, 3; fill = 3) + + # {T}(nrows, ncols; fill) + A = type{Int64}(3, 3) + @test getfill(A) === zero(Int64) + @test nnz(A) == 0 + type === GBMatrixC && (@test storageorder(A) === ColMajor()) + type === GBMatrixR && (@test storageorder(A) === RowMajor()) + B = setfill(A, nothing) + @test getfill(B) === nothing + + # {T, F}() +end +end \ No newline at end of file From 6aa5b0ebcc0036747de44b88e74bbd392c694c7d Mon Sep 17 00:00:00 2001 From: Will Kimmerer Date: Sun, 2 Oct 2022 22:23:18 -0400 Subject: [PATCH 09/16] passing tests locally --- src/SuiteSparseGraphBLAS.jl | 1 + src/abstractgbarray.jl | 40 +++++++ src/convert.jl | 24 +++-- src/operations/broadcasts.jl | 2 + src/operations/operationutils.jl | 4 + src/operators/unaryops.jl | 1 + src/oriented.jl | 177 +------------------------------ src/pack.jl | 23 ++-- src/types.jl | 22 ++-- src/unpack.jl | 4 +- src/vector.jl | 46 -------- test/chainrules/selectrules.jl | 6 -- test/operations/reduce.jl | 14 +-- test/operations/select.jl | 4 +- test/runtests.jl | 6 +- test/solvers/klu.jl | 2 +- test/solvers/umfpack.jl | 2 +- test/types.jl | 8 +- 18 files changed, 117 insertions(+), 269 deletions(-) diff --git a/src/SuiteSparseGraphBLAS.jl b/src/SuiteSparseGraphBLAS.jl index 482bbe48..d1410546 100644 --- a/src/SuiteSparseGraphBLAS.jl +++ b/src/SuiteSparseGraphBLAS.jl @@ -18,6 +18,7 @@ using SparseArrays using SparseArrays: nonzeroinds, getcolptr, getrowval, getnzval using MacroTools using LinearAlgebra +using LinearAlgebra: copy_similar using Random: randsubseq, default_rng, AbstractRNG, GLOBAL_RNG using SpecialFunctions: lgamma, gamma, erf, erfc using Base.Broadcast diff --git a/src/abstractgbarray.jl b/src/abstractgbarray.jl index 0fa4c621..78de7a48 100644 --- a/src/abstractgbarray.jl +++ b/src/abstractgbarray.jl @@ -42,6 +42,46 @@ Base.unsafe_convert(::Type{LibGraphBLAS.GrB_Matrix}, A::AbstractGBArray) = A.p[] Base.unsafe_convert(::Type{LibGraphBLAS.GrB_Vector}, A::AbstractGBVector) = LibGraphBLAS.GrB_Vector(A.p[]) +# similar for transpose of GBArrays: +function Base.similar( + A::Transpose{<:Any,<:AbstractGBArray{T}}, ::Type{TNew} = T, + dims::Tuple{Int64, Vararg{Int64, N}} = size(A); fill = getfill(A) +) where {T, TNew, N} + similar(parent(A), TNew, dims; fill) +end + +function Base.similar(A::Transpose{<:Any,<:AbstractGBArray{T}}, dims::Tuple; fill = getfill(A)) where T + return similar(A, T, dims; fill) +end + +function Base.similar( + A::Transpose{<:Any,<:AbstractGBArray}, ::Type{TNew}, + dims::Integer; fill = getfill(A) +) where TNew + return similar(A, TNew, (dims,); fill) +end + +function Base.similar( + A::Transpose{<:Any,<:AbstractGBArray}, ::Type{TNew}, + dim1::Integer, dim2::Integer; fill = getfill(A) +) where TNew + return similar(A, TNew, (dim1, dim2); fill) +end + +function Base.similar( + A::Transpose{<:Any,<:AbstractGBArray}, + dims::Integer; fill = getfill(A) +) + return similar(A, (dims,); fill) +end + +function Base.similar( + A::Transpose{<:Any,<:AbstractGBArray}, + dim1::Integer, dim2::Integer; fill = getfill(A) +) + return similar(A, (dim1, dim2); fill) +end + """ empty!(A::AbstractGBArray) diff --git a/src/convert.jl b/src/convert.jl index cbb3c7d9..ac25afd4 100644 --- a/src/convert.jl +++ b/src/convert.jl @@ -4,27 +4,37 @@ # pass through for most cases conform(M::AbstractGBArray) = M -function Base.convert(::Type{M}, A::N; fill = getfill(A)) where {M<:AbstractGBArray, N<:AbstractGBArray} +function Base.convert(::Type{M}, A::N; fill::F = getfill(A)) where {F, M<:AbstractGBArray, N<:AbstractGBArray} + !(F <: Union{Nothing, Missing}) && (fill = convert(eltype(M), fill)) isabstracttype(M) && throw(ArgumentError("$M is an abstract type, which cannot be constructed.")) - sparsity = sparsitystatus(A) x = tempunpack_noformat!(A) repack! = x[end] values = x[end - 1] indices = x[begin:end-2] - display(typeof.(indices)) - display(typeof(values)) newvalues = unsafe_wrap(Array, _sizedjlmalloc(length(values), T), size(values)) display(typeof(newvalues)) copyto!(newvalues, values) newindices = _copytoraw.(indices) repack!() B = M(size(A); fill) - unsafepack!(B, newindices..., newvalues, false) - + unsafepack!(B, newindices..., newvalues, false; decrementindices = false) end Base.convert(::Type{M}, A::M; fill = nothing) where {M<:AbstractGBArray} = A -# TODO: Implement this? No strong reason not to? +function LinearAlgebra.copy_similar(A::GBArrayOrTranspose, ::Type{T}) where T + order = storageorder(A) + C = similar(A, T, size(A)) + x = tempunpack_noformat!(A) + repack! = x[end] + values = x[end - 1] + indices = x[begin:end-2] + newvalues = unsafe_wrap(Array, _sizedjlmalloc(length(values), T), size(values)) + copyto!(newvalues, values) + newindices = _copytoraw.(indices) + repack!() + unsafepack!(C, newindices..., newvalues, false; order, decrementindices = false) +end +# TODO: Implement this? Base.convert(::Type{M}, ::AbstractGBArray; fill = nothing) where {M<:AbstractGBShallowArray} = throw(ArgumentError("Cannot convert into a shallow array.")) diff --git a/src/operations/broadcasts.jl b/src/operations/broadcasts.jl index dd7e5a95..5b27344d 100644 --- a/src/operations/broadcasts.jl +++ b/src/operations/broadcasts.jl @@ -330,3 +330,5 @@ function Base.materialize!( ) where {S, T} return setindex!(A, bc.args[begin], :) end + +Base.Broadcast.broadcasted(::Type{T}, A::AbstractGBArray) where T = copy_similar(A, T) \ No newline at end of file diff --git a/src/operations/operationutils.jl b/src/operations/operationutils.jl index 15f67be3..c325efc7 100644 --- a/src/operations/operationutils.jl +++ b/src/operations/operationutils.jl @@ -118,6 +118,10 @@ _promotefill(::AbstractGBArray{<:Any, Missing}, y, op) = nothing _promotefill(x, ::AbstractGBArray{<:Any, Missing}, op) = nothing _promotefill(::AbstractGBArray{<:Any, Nothing}, ::AbstractGBArray{<:Any, Missing}, op) = nothing _promotefill(::AbstractGBArray{<:Any, Missing}, ::AbstractGBArray{<:Any, Nothing}, op) = nothing +_promotefill(::AbstractGBArray{<:Any, Nothing}, ::AbstractGBArray{<:Any, <:Any}, op) = nothing +_promotefill(::AbstractGBArray{<:Any, <:Any}, ::AbstractGBArray{<:Any, Nothing}, op) = nothing +_promotefill(::AbstractGBArray{<:Any, Missing}, ::AbstractGBArray{<:Any, <:Any}, op) = missing +_promotefill(::AbstractGBArray{<:Any, <:Any}, ::AbstractGBArray{<:Any, Missing}, op) = missing _promotefill(x, op) = x diff --git a/src/operators/unaryops.jl b/src/operators/unaryops.jl index dfd4e6eb..6b131d3a 100644 --- a/src/operators/unaryops.jl +++ b/src/operators/unaryops.jl @@ -20,6 +20,7 @@ function unaryop(f::F, ::Type{T}) where {F<:Base.Callable, T} end unaryop(f, ::GBArrayOrTranspose{T}) where T = unaryop(f, T) +unaryop(op::TypedUnaryOperator, ::GBArrayOrTranspose{T}) where T = op unaryop(op::TypedUnaryOperator, x...) = op SuiteSparseGraphBLAS.juliaop(op::TypedUnaryOperator) = op.fn diff --git a/src/oriented.jl b/src/oriented.jl index ea8b3f22..08a45d5f 100644 --- a/src/oriented.jl +++ b/src/oriented.jl @@ -1,176 +1 @@ -StorageOrders.storageorder(::OrientedGBMatrix{T, F, O}) where {T, F, O} = O - -# Constructors: -############### -""" - OrientedGBMatrix{T}(nrows, ncols; fill = nothing) - -Create a GBMatrix of the specified size. -""" -# function OrientedGBMatrix{T, O, F}(nrows::Integer, ncols::Integer; fill::F = nothing) where {T, O, F} -# m = Ref{LibGraphBLAS.GrB_Matrix}() -# @wraperror LibGraphBLAS.GrB_Matrix_new(m, gbtype(T), nrows, ncols) -# A = GBMatrix{T, F}(finalizer(m) do ref -# @wraperror LibGraphBLAS.GrB_Matrix_free(ref) -# end, fill) -# gbset(A, :format, O === StorageOrders.ColMajor() ? :bycol : :byrow) -# return OrientedGBMatrix{T, O, F}(A) -# end -# OrientedGBMatrix{T, O}(nrows::Integer, ncols::Integer; fill::F = nothing) where {T, O, F} = OrientedGBMatrix{T, O, F}(nrows, ncols; fill) -# GBMatrixC{T}(nrows::Integer, ncols::Integer; fill::F = nothing) where {T, F} = GBMatrixC{T, F}(nrows, ncols; fill) -# GBMatrixR{T}(nrows::Integer, ncols::Integer; fill::F = nothing) where {T, F} = GBMatrixR{T, F}(nrows, ncols; fill) -# -# OrientedGBMatrix{T, O, F}(dims::Dims{2}; fill::F = nothing) where {T, O, F} = OrientedGBMatrix{T, O}(dims...; fill) -# OrientedGBMatrix{T, O, F}(dims::Tuple{<:Integer}; fill::F = nothing) where {T, O, F} = OrientedGBMatrix{T, O}(dims...; fill) -# OrientedGBMatrix{T, O, F}(size::Tuple{Base.OneTo, Base.OneTo}; fill::F = nothing) where {T, O, F} = -# OrientedGBMatrix{T, O}(size[1].stop, size[2].stop; fill) -# -# OrientedGBMatrix{T, O}(dims::Tuple; fill::F = nothing) where {T, O, F} = OrientedGBMatrix{T, O, F}(dims; fill) -# GBMatrixC{T}(dims::Tuple; fill::F = nothing) where {T, F} = GBMatrixC{T, F}(dims; fill) -# GBMatrixR{T}(dims::Tuple; fill::F = nothing) where {T, F} = GBMatrixR{T, F}(dims; fill) -""" - OrientedGBMatrix(I, J, X; combine = +, nrows = maximum(I), ncols = maximum(J)) - -Create an nrows x ncols GBMatrix M such that M[I[k], J[k]] = X[k]. The combine function defaults -to `|` for booleans and `+` for nonbooleans. -""" -# function OrientedGBMatrix{T, O, F}( -# I::AbstractVector, J::AbstractVector, X::AbstractVector{T}; -# combine = +, nrows = maximum(I), ncols = maximum(J), fill::F = nothing -# ) where {T, O, F} -# I isa Vector || (I = collect(I)) -# J isa Vector || (J = collect(J)) -# X isa Vector || (X = collect(X)) -# A = OrientedGBMatrix{T, O}(nrows, ncols; fill) -# build!(A, I, J, X; combine) -# return A -# end -# function OrientedGBMatrix{O}( -# I::AbstractVector, J::AbstractVector, X::AbstractVector{T}; -# combine = +, nrows = maximum(I), ncols = maximum(J), fill::F = nothing -# ) where {T, O, F} -# return OrientedGBMatrix{T, O, F}(I, J, X,; combine, nrows, ncols, fill) -# end -# -# -# GBMatrixC( -# I::AbstractVector, J::AbstractVector, X::AbstractVector; -# combine = +, nrows = maximum(I), ncols = maximum(J), fill = nothing -# ) = OrientedGBMatrix{ColMajor()}(I, J, X; combine, nrows, ncols, fill) -# GBMatrixR( -# I::AbstractVector, J::AbstractVector, X::AbstractVector; -# combine = +, nrows = maximum(I), ncols = maximum(J), fill = nothing -# ) = OrientedGBMatrix{RowMajor()}(I, J, X; combine, nrows, ncols, fill) -# -# -# #iso constructors -# """ -# OrientedGBMatrix(I, J, x; nrows = maximum(I), ncols = maximum(J)) -# -# Create an nrows x ncols GBMatrix M such that M[I[k], J[k]] = x. -# The resulting matrix is "iso-valued" such that it only stores `x` once rather than once for -# each index. -# """ -# function OrientedGBMatrix{T, O, F}(I::AbstractVector, J::AbstractVector, x::T; -# nrows = maximum(I), ncols = maximum(J), fill::F = nothing) where {T, O, F} -# A = OrientedGBMatrix{T, O, F}(nrows, ncols; fill) -# build!(A, I, J, x) -# return A -# end -# OrientedGBMatrix{O}(I::AbstractVector, J::AbstractVector, x::T; nrows = maximum(I), ncols = maximum(J), fill::F = nothing) where {T, O, F} = -# OrientedGBMatrix{T, O, F}(I, J, x; nrows, ncols, fill) -# -# -# function OrientedGBMatrix{T, O, F}(dims::Dims{2}, x::T; fill::F = nothing) where {T, O, F} -# A = OrientedGBMatrix{T, O, F}(dims; fill) -# A[:, :] = x -# return A -# end -# OrientedGBMatrix{O}(dims::Dims{2}, x::T; fill::F = nothing) where {T, O, F} = OrientedGBMatrix{T, O, F}(dims, x; fill) -# -# OrientedGBMatrix{T, O, F}(nrows, ncols, x::T; fill::F = nothing) where {T, O, F} = OrientedGBMatrix{O}((nrows, ncols), x; fill) -# OrientedGBMatrix{O}(nrows, ncols, x::T; fill::F = nothing) where {T, O, F} = OrientedGBMatrix{T, O, F}(nrows, ncols, x; fill) -# -# function OrientedGBMatrix{T, O, F}(v::GBVector) where {T, O, F} -# O === ByRow() && throw(ArgumentError("Cannot wrap a GBVector in a ByRow matrix.")) -# # this copies, I think that's ideal, and I can implement @view or something at a later date. -# return copy(OrientedGBMatrix{T, O, F}(v.p, v.fill)) -# end -# function OrientedGBMatrix{O}(v::GBVector) where {O} -# # this copies, I think that's ideal, and I can implement @view or something at a later date. -# return OrientedGBMatrix{eltype(v), typeof(v.fill), O}(v) -# end -# -# function OrientedGBMatrix{T, O, F}(A::AbstractGBMatrix) where {T, O, F} -# storageorder(A) != O && throw(ArgumentError("Cannot wrap a GBMatrix in an OrientedGBMatrix with a different orientation.")) -# # this copies, I think that's ideal, and I can implement @view or something at a later date. -# return copy(OrientedGBMatrix{T, O, F}(A.p, A.fill)) -# end -# function OrientedGBMatrix{O}(A::AbstractGBMatrix) where {O} -# # this copies, I think that's ideal, and I can implement @view or something at a later date. -# return OrientedGBMatrix{eltype(A), typeof(A.fill), O}(A) -# end -# -# GBMatrixR(A::AbstractGBMatrix) = OrientedGBMatrix{RowMajor()}(A) -# GBMatrixC(A::AbstractGBMatrix) = OrientedGBMatrix{ColMajor()}(A) -# -# GBMatrixC( -# I::AbstractVector, J::AbstractVector, X::T; -# nrows = maximum(I), ncols = maximum(J), fill = nothing -# ) where {T} = OrientedGBMatrix{ColMajor()}(I, J, X; nrows, ncols, fill) -# GBMatrixR( -# I::AbstractVector, J::AbstractVector, X::T; -# nrows = maximum(I), ncols = maximum(J), fill = nothing -# ) where {T} = OrientedGBMatrix{RowMajor()}(I, J, X; nrows, ncols, fill) -# -# Base.unsafe_convert(::Type{LibGraphBLAS.GrB_Matrix}, A::OrientedGBMatrix) = A.p[] -# -# function Base.copy(A::OrientedGBMatrix{T, O, F}) where {T, O, F} -# C = Ref{LibGraphBLAS.GrB_Matrix}() -# LibGraphBLAS.GrB_Matrix_dup(C, A) -# return OrientedGBMatrix{T, O, F}(C, A.fill) # This should automatically be the same orientation. -# end - -# because of the fill kwarg we have to redo a lot of the Base.similar dispatch stack. -function Base.similar( - A::OrientedGBMatrix{T, O, F}, ::Type{TNew} = T, - dims::Tuple{Int64, Vararg{Int64, N}} = size(A); fill = parent(A).fill -) where {T, TNew, N, F, O} - if dims isa Dims{1} - return GBVector{TNew}(dims...; fill) - else - A = OrientedGBMatrix{TNew, O}(dims...; fill) - end -end - -function Base.similar(A::OrientedGBMatrix{T}, dims::Tuple; fill = parent(A).fill) where {T} - return similar(A, T, dims; fill) -end - -function Base.similar( - A::OrientedGBMatrix{T}, ::Type{TNew}, - dims::Integer; fill = parent(A).fill -) where {T, TNew} - return similar(A, TNew, (dims,); fill) -end - -function Base.similar( - A::OrientedGBMatrix{T}, ::Type{TNew}, - dim1::Integer, dim2::Integer; fill = parent(A).fill -) where {T, TNew} - return similar(A, TNew, (dim1, dim2); fill) -end - -function Base.similar( - A::OrientedGBMatrix{T}, - dims::Integer; fill = parent(A).fill -) where {T} - return similar(A, (dims,); fill) -end - -function Base.similar( - A::OrientedGBMatrix{T}, - dim1::Integer, dim2::Integer; fill = parent(A).fill -) where {T} - return similar(A, (dim1, dim2); fill) -end \ No newline at end of file +StorageOrders.storageorder(::OrientedGBMatrix{T, F, O}) where {T, F, O} = O \ No newline at end of file diff --git a/src/pack.jl b/src/pack.jl index 3c0f61e4..0e9a73df 100644 --- a/src/pack.jl +++ b/src/pack.jl @@ -156,11 +156,18 @@ function unsafepack!( ) _hasconstantorder(A) && (storageorder(A) !== order) && (throw(ArgumentError("Cannot change the storage order of $(typeof(A))"))) - if order === ColMajor() - _packdensematrix!(A, M) - else - _packdensematrixR!(A, M) - end + _packdensematrix!(A, M; order) + shallow && makeshallow!(A) + return A +end + +function unsafepack!( + A::AbstractGBArray, M::DenseVecOrMat{Int8}, V::DenseVecOrMat, shallow::Bool = true; + order = ColMajor(), decrementindices = false +) + _hasconstantorder(A) && (storageorder(A) !== order) && + (throw(ArgumentError("Cannot change the storage order of $(typeof(A))"))) + _packbitmap!(A, M, V; order) shallow && makeshallow!(A) return A end @@ -217,17 +224,17 @@ unsafepack!( # end # These functions do not have the `!` since they will not modify A during packing (to decrement indices) -function pack(A::StridedVecOrMat; fill = nothing) +function pack(A::StridedVecOrMat; fill = defaultfill(eltype(A))) if A isa AbstractVector return GBShallowVector(A; fill) else GBShallowMatrix(A; fill) end end -function pack(A::Transpose{<:Any, <:StridedVecOrMat}; fill = nothing) +function pack(A::Transpose{<:Any, <:StridedVecOrMat}; fill = defaultfill(eltype(A))) return transpose(parent(A); fill) end -pack(A::Transpose{<:Any, <:DenseVecOrMat}; fill = nothing) = +pack(A::Transpose{<:Any, <:DenseVecOrMat}; fill = defaultfill(eltype(A))) = transpose(pack(parent(A); fill)) macro _densepack(xs...) diff --git a/src/types.jl b/src/types.jl index b74bde12..7e33e449 100644 --- a/src/types.jl +++ b/src/types.jl @@ -312,6 +312,8 @@ macro gbmatrixtype(typename) esc(quote # Empty Constructors: function $typename{T, F}(nrows::Integer, ncols::Integer; fill = defaultfill(F)) where {T, F} + ((F === Nothing) || (F === Missing) || (T === F)) || + throw(ArgumentError("Fill type $F must be <: Union{Nothing, Missing, $T}")) m = _newGrBRef() @wraperror LibGraphBLAS.GrB_Matrix_new(m, gbtype(T), nrows, ncols) return $typename{T, F}(m; fill) @@ -477,15 +479,16 @@ macro gbmatrixtype(typename) # similar function Base.similar( A::$typename{T}, ::Type{TNew} = T, - dims::Tuple{Int64, Vararg{Int64, N}} = size(A); fill = getfill(A) - ) where {T, TNew, N} + dims::Tuple{Int64, Vararg{Int64, N}} = size(A); fill::F = getfill(A) + ) where {T, TNew, N, F} + !(F <: Union{Nothing, Missing}) && (fill = convert(TNew, fill)) if dims isa Dims{1} # TODO: When new Vector types are added this will be incorrect. x = GBVector{TNew}(dims...; fill) else x = $typename{TNew}(dims...; fill) + _hasconstantorder(x) || setstorageorder!(x, storageorder(A)) end - _hasconstantorder(x) || setstorageorder!(x, storageorder(A)) return x end @@ -526,6 +529,8 @@ end macro gbvectortype(typename) esc(quote function $typename{T, F}(n::Integer; fill = defaultfill(F)) where {T, F} + ((F === Nothing) || (F === Missing) || (T === F)) || + throw(ArgumentError("Fill type $F must be <: Union{Nothing, Missing, $T}")) m = _newGrBRef() @wraperror LibGraphBLAS.GrB_Matrix_new(m, gbtype(T), n, 1) return $typename{T, F}(m; fill) @@ -672,15 +677,16 @@ macro gbvectortype(typename) # similar function Base.similar( v::$typename{T}, ::Type{TNew} = T, - dims::Tuple{Int64, Vararg{Int64, N}} = size(v); fill = getfill(v) - ) where {T, TNew, N} + dims::Tuple{Int64, Vararg{Int64, N}} = size(v); fill::F = getfill(v) + ) where {T, TNew, N, F} + !(F <: Union{Nothing, Missing}) && (fill = convert(TNew, fill)) if dims isa Dims{1} # TODO: Check this for correctness!!! x = $typename{TNew}(dims...; fill) else x = $GBMatrix{TNew}(dims...; fill) + _hasconstantorder(x) || setstorageorder!(x, storageorder(v)) end - _hasconstantorder(x) || setstorageorder!(x, storageorder(v)) return x end @@ -734,6 +740,8 @@ mutable struct GBVector{T, F} <: AbstractGBVector{T, F} end function GBVector{T, F}(p::Base.RefValue{LibGraphBLAS.GrB_Matrix}; fill = defaultfill(F)) where {T, F} + ((F === Nothing) || (F === Missing) || (T === F)) || + throw(ArgumentError("Fill type $F must be <: Union{Nothing, Missing, $T}")) fill = convert(F, fill) # conversion to F happens at the last possible moment. return GBVector{T, F}(p, fill) end @@ -765,6 +773,8 @@ mutable struct GBMatrix{T, F} <: AbstractGBMatrix{T, F} end function GBMatrix{T, F}(p::Base.RefValue{LibGraphBLAS.GrB_Matrix}; fill = defaultfill(F)) where {T, F} + ((F === Nothing) || (F === Missing) || (T === F)) || + throw(ArgumentError("Fill type $F must be <: Union{Nothing, Missing, $T}")) fill = convert(F, fill) # conversion to F happens at the last possible moment. return GBMatrix{T, F}(p, fill) end diff --git a/src/unpack.jl b/src/unpack.jl index 19acc354..a362573e 100644 --- a/src/unpack.jl +++ b/src/unpack.jl @@ -209,7 +209,7 @@ function _unpackbitmapmatrix!( _jlfree(f) end end - return v, b + return b, v end function _unpackbitmapmatrixR!( @@ -245,7 +245,7 @@ function _unpackbitmapmatrixR!( _jlfree(f) end end - return v, b + return b, v end function _unpackhypermatrix!( diff --git a/src/vector.jl b/src/vector.jl index 6645bc59..3ab00229 100644 --- a/src/vector.jl +++ b/src/vector.jl @@ -85,52 +85,6 @@ function Base.copy(A::GBVector{T, F}) where {T, F} return GBVector{T, F}(C, A.fill) end - -# because of the fill kwarg we have to redo a lot of the Base.similar dispatch stack. -function Base.similar( - v::GBVectorOrTranspose{T}, ::Type{TNew} = T, - dims::Tuple{Int64, Vararg{Int64, N}} = size(v); fill = parent(v).fill -) where {T, TNew, N} - if dims isa Dims{1} - x = GBVector{TNew}(dims...; fill) - else - x = GBMatrix{TNew}(dims...; fill) - end - setstorageorder!(x, storageorder(v)) - return x -end - -function Base.similar(v::GBVectorOrTranspose{T}, dims::Tuple; fill = v.fill) where {T} - return similar(v, T, dims; fill) -end - -function Base.similar( - v::GBVectorOrTranspose{T}, ::Type{TNew}, - dims::Integer; fill = parent(v).fill -) where {T, TNew} - return similar(v, TNew, (dims,); fill) -end - -function Base.similar( - v::GBVectorOrTranspose{T}, - dims::Integer; fill = parent(v).fill -) where {T} - return similar(v, (dims,); fill) -end - -function Base.similar( - v::GBVectorOrTranspose{T}, ::Type{TNew}, - dim1::Integer, dim2::Integer; fill = parent(v).fill -) where {T, TNew} - return similar(v, TNew, (dim1, dim2); fill) -end - -function Base.similar( - v::GBVectorOrTranspose{T}, dim1::Integer, dim2::Integer; fill = parent(v).fill -) where {T} - return similar(v, (dim1, dim2); fill) -end - #We need these until I can get a SparseArrays.nonzeros implementation # TODO: REMOVE function Base.show(io::IO, ::MIME"text/plain", v::GBVector) diff --git a/test/chainrules/selectrules.jl b/test/chainrules/selectrules.jl index 77a49bb5..b0667da3 100644 --- a/test/chainrules/selectrules.jl +++ b/test/chainrules/selectrules.jl @@ -20,25 +20,19 @@ @testset "Sparse" begin X = GBMatrix(sprand(4, 4, 0.5)) - print(X) test_frule(select, diag, X) test_rrule(select, diag, X) - println("diag") test_frule(select, offdiag, X) test_rrule(select, offdiag, X) - println("offdiag") test_frule(select, tril, X) test_rrule(select, tril, X) test_frule(select, triu, X) test_rrule(select, triu, X) - println("tris") test_frule(select, nonzeros, X) test_rrule(select, nonzeros, X) - println("nonzeros") test_frule(select, >, X, 0.) test_rrule(select, >, X, 0.) test_frule(select, <=, X, 0.) test_rrule(select, <=, X, 0.) - println("equalities") end end diff --git a/test/operations/reduce.jl b/test/operations/reduce.jl index 15021888..c767ce52 100644 --- a/test/operations/reduce.jl +++ b/test/operations/reduce.jl @@ -16,14 +16,14 @@ using SuiteSparseGraphBLAS: Structural @test reduce(+, M) == 10 @test reduce(+, M, dims=1) == GBVector([3,7]) @test reduce(+, M, dims=2) == GBVector([4, 6]) - @test reduce(+, M, dims=1; mask = GBVector([1], [true], nrows = 2)) == - GBVector([1], [3], nrows = 2) - @test reduce(+, M, dims=2; mask = ~GBVector([1], [true], nrows = 2)) == - GBVector([2], [6], nrows = 2) + @test reduce(+, M, dims=1; mask = GBVector([1], [true], 2)) == + GBVector([1], [3], 2) + @test reduce(+, M, dims=2; mask = ~GBVector([1], [true], 2)) == + GBVector([2], [6], 2) - @test reduce(+, M, dims=2; mask=Structural(GBVector([1], [false], nrows = 2))) == - GBVector([1], [4], nrows = 2) - @test reduce(+, M, dims=2; mask=GBVector([1], [false], nrows = 2)) == + @test reduce(+, M, dims=2; mask=Structural(GBVector([1], [false], 2))) == + GBVector([1], [4], 2) + @test reduce(+, M, dims=2; mask=GBVector([1], [false], 2)) == GBVector{Int64}(2) end end diff --git a/test/operations/select.jl b/test/operations/select.jl index 35c42479..e5d44d47 100644 --- a/test/operations/select.jl +++ b/test/operations/select.jl @@ -1,7 +1,7 @@ @testset "select" begin m = GBMatrix([[1,2,3] [4,5,6] [7,8,9]]) s = select(tril, m) - @test s[1,2] === nothing && s[3,1] == 3 + @test s[1,2] === getfill(s) && s[3,1] == 3 s = select(<, m, 6) - @test s[2,2] == 5 && s[3,3] === nothing + @test s[2,2] == 5 && s[3,3] === getfill(s) end diff --git a/test/runtests.jl b/test/runtests.jl index 5a6ce012..2e638cb4 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -21,7 +21,7 @@ function ChainRulesTestUtils.rand_tangent( n = nnz(x) v = rand(rng, -9:0.01:9, n) I, J, _ = findnz(x) - return GBMatrix(I, J, v; nrows = size(x, 1), ncols = size(x, 2)) + return GBMatrix(I, J, v, size(x, 1), size(x, 2)) end function ChainRulesTestUtils.rand_tangent( @@ -31,7 +31,7 @@ function ChainRulesTestUtils.rand_tangent( n = nnz(x) v = rand(rng, -9:0.01:9, n) I, _ = findnz(x) - return GBVector(I, v; nrows = size(x, 1)) + return GBVector(I, v, size(x, 1)) end # Inefficient, but doesn't matter, only doing small matrices @@ -93,5 +93,5 @@ println("$(SuiteSparseGraphBLAS.get_lib())") include_test("chainrules/constructorrules.jl") include_test("chainrules/maprules.jl") include_test("solvers/klu.jl") - #include_test("solvers/umfpack.jl") + # include_test("solvers/umfpack.jl") end diff --git a/test/solvers/klu.jl b/test/solvers/klu.jl index 35575304..a4ba331a 100644 --- a/test/solvers/klu.jl +++ b/test/solvers/klu.jl @@ -13,7 +13,7 @@ using SparseArrays A0 = GBMatrix(Ap, Ai, Ax) A1 = GBMatrix(increment!([0,4,1,1,2,2,0,1,2,3,4,4]), increment!([0,4,0,2,1,2,1,4,3,2,1,2]), - [2.,1.,3.,4.,-1.,-3.,3.,9.,2.,1.,4.,2.], nrows = 5, ncols = 5) + [2.,1.,3.,4.,-1.,-3.,3.,9.,2.,1.,4.,2.], 5, 5) @testset "Core functionality for $Tv elements" for Tv in (Float64, ComplexF64) A = Tv.(A0) # test the raw vector construction method. diff --git a/test/solvers/umfpack.jl b/test/solvers/umfpack.jl index d09750df..e38a97c6 100644 --- a/test/solvers/umfpack.jl +++ b/test/solvers/umfpack.jl @@ -169,7 +169,7 @@ end A0 = GBMatrix(increment!([0,4,1,1,2,2,0,1,2,3,4,4]), increment!([0,4,0,2,1,2,1,4,3,2,1,2]), - [2.,1.,3.,4.,-1.,-3.,3.,6.,2.,1.,4.,2.], nrows = 5, ncols = 5) + [2.,1.,3.,4.,-1.,-3.,3.,6.,2.,1.,4.,2.], 5, 5) @testset "Core functionality for $Tv elements" for Tv in (Float64, ComplexF64) # We might be able to support two index sizes one day diff --git a/test/types.jl b/test/types.jl index eef803c8..537d52fa 100644 --- a/test/types.jl +++ b/test/types.jl @@ -1,13 +1,13 @@ @testset "types.jl" begin for type ∈ [GBMatrix, GBMatrixC, GBMatrixR] # {T, F}(nrows, ncols; fill) - A = type{Int64, Float64}(3, 3) - @test getfill(A) === zero(Float64) + A = type{Int64, Int64}(3, 3) + @test getfill(A) === zero(Int64) @test nnz(A) == 0 type === GBMatrixC && (@test storageorder(A) === ColMajor()) type === GBMatrixR && (@test storageorder(A) === RowMajor()) - A = type{Int64, Float64}(3, 3; fill = 3) - @test getfill(A) === 3.0 + A = type{Int64, Int64}(3, 3; fill = 3) + @test getfill(A) === 3 type === GBMatrixC && (@test storageorder(A) === ColMajor()) type === GBMatrixR && (@test storageorder(A) === RowMajor()) @test_throws MethodError type{Int64, Nothing}(3, 3; fill = 3) From 40b5b4904ae8e396ddbfd9f47777b92cefa30f7e Mon Sep 17 00:00:00 2001 From: Will Kimmerer Date: Thu, 6 Oct 2022 10:27:14 -0400 Subject: [PATCH 10/16] fix type infer error --- src/constants.jl | 10 +++++----- src/types.jl | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/constants.jl b/src/constants.jl index 8049d5db..cfb94a7f 100644 --- a/src/constants.jl +++ b/src/constants.jl @@ -1,8 +1,8 @@ -const GBVecOrMat{T, F} = Union{<:AbstractGBVector{T, F}, <:AbstractGBMatrix{T, F}} -const GBMatrixOrTranspose{T, F} = Union{<:AbstractGBMatrix{T, F}, <:Transpose{<:Any, <:AbstractGBMatrix{T, F}}} -const GBVectorOrTranspose{T, F} = Union{<:AbstractGBVector{T, F}, <:Transpose{<:Any, <:AbstractGBVector{T, F}}} -const GBArrayOrTranspose{T, F} = Union{<:AbstractGBArray{T, F}, <:Transpose{<:Any, <:AbstractGBArray{T, F}}} -const VecMatOrTrans = Union{<:DenseVecOrMat, <:Transpose{<:Any, <:DenseVecOrMat}} +const GBVecOrMat{T, F} = Union{AbstractGBVector{T, F}, AbstractGBMatrix{T, F}} +const GBMatrixOrTranspose{T, F} = Union{AbstractGBMatrix{T, F}, Transpose{<:Any, <:AbstractGBMatrix{T, F}}} +const GBVectorOrTranspose{T, F} = Union{AbstractGBVector{T, F}, Transpose{<:Any, <:AbstractGBVector{T, F}}} +const GBArrayOrTranspose{T, F} = Union{AbstractGBArray{T, F}, Transpose{<:Any, <:AbstractGBArray{T, F}}} +const VecMatOrTrans = Union{DenseVecOrMat, Transpose{<:Any, <:DenseVecOrMat}} const ptrtogbtype = IdDict{Ptr, GBType}() const GrBOp = Union{ diff --git a/src/types.jl b/src/types.jl index 7e33e449..460e5c9b 100644 --- a/src/types.jl +++ b/src/types.jl @@ -750,6 +750,7 @@ GBVector{T}( fill::F = defaultfill(T) ) where {T, F} = return GBVector{T, F}(p; fill) +StorageOrders.storageorder(::GBVector) = ColMajor() # we call @gbvectortype GBVector below GBMatrix defn. """ From 4d3d707bb5170bea755ceb011f5882bb5200d186 Mon Sep 17 00:00:00 2001 From: Will Kimmerer Date: Thu, 6 Oct 2022 11:51:35 -0400 Subject: [PATCH 11/16] reorg, fix #84 --- src/SuiteSparseGraphBLAS.jl | 4 +- src/abstractgbarray.jl | 29 +++++++++ src/convert.jl | 9 ++- src/gbmatrix.jl | 45 ++++++++++++++ src/gbvector.jl | 16 +++++ src/matrix.jl | 29 --------- src/options.jl | 3 +- src/oriented.jl | 71 ++++++++++++++++++++- src/pack.jl | 37 +++-------- src/types.jl | 36 +---------- src/unpack.jl | 14 ++--- src/vector.jl | 121 ------------------------------------ test/types.jl | 4 ++ 13 files changed, 190 insertions(+), 228 deletions(-) create mode 100644 src/gbmatrix.jl create mode 100644 src/gbvector.jl delete mode 100644 src/matrix.jl delete mode 100644 src/vector.jl diff --git a/src/SuiteSparseGraphBLAS.jl b/src/SuiteSparseGraphBLAS.jl index d1410546..2313aaf7 100644 --- a/src/SuiteSparseGraphBLAS.jl +++ b/src/SuiteSparseGraphBLAS.jl @@ -59,8 +59,8 @@ include("indexutils.jl") # include("operations/extract.jl") include("scalar.jl") -include("vector.jl") -include("matrix.jl") +include("gbvector.jl") +include("gbmatrix.jl") include("abstractgbarray.jl") # EXPERIMENTAL array types: diff --git a/src/abstractgbarray.jl b/src/abstractgbarray.jl index 78de7a48..8a5f3224 100644 --- a/src/abstractgbarray.jl +++ b/src/abstractgbarray.jl @@ -102,6 +102,10 @@ function Base.copyto!(C::AbstractGBArray, A::GBArrayOrTranspose) return C end +function Base.copy(A::M) where {M<:AbstractGBArray} + M(_copyGrBMat(A.p), A.fill) +end + function Base.Matrix(A::GBArrayOrTranspose) format = sparsitystatus(A) if format === Dense() @@ -894,6 +898,12 @@ function Base.show(io::IO, ::MIME"text/plain", A::AbstractGBArray) #fallback pri gxbprint(io, A) end +function Base.show(io::IO, A::AbstractGBArray) + gxbprint(io, A) +end +function Base.show(io::IOContext, A::AbstractGBArray) + gxbprint(io, A) +end function Base.getindex( @@ -919,6 +929,25 @@ function Base.getindex( return x end +function Base.getindex( + u::AbstractGBVector, I; + mask = nothing, accum = nothing, desc = nothing +) + return extract(u, I; mask, accum, desc) +end + +function Base.getindex(u::AbstractGBVector, ::Colon; mask = nothing, accum = nothing, desc = nothing) + return extract(u, :) +end + +function Base.getindex( + u::AbstractGBVector, i::Union{Vector, UnitRange, StepRange}; + mask = nothing, accum = nothing, desc = nothing +) + return extract(u, i; mask, accum, desc) +end + + """ setfill!(A::AbstractGBArray{T, F, N}, x::F) diff --git a/src/convert.jl b/src/convert.jl index ac25afd4..8e7f77a7 100644 --- a/src/convert.jl +++ b/src/convert.jl @@ -7,16 +7,15 @@ conform(M::AbstractGBArray) = M function Base.convert(::Type{M}, A::N; fill::F = getfill(A)) where {F, M<:AbstractGBArray, N<:AbstractGBArray} !(F <: Union{Nothing, Missing}) && (fill = convert(eltype(M), fill)) isabstracttype(M) && throw(ArgumentError("$M is an abstract type, which cannot be constructed.")) - x = tempunpack_noformat!(A) + x = tempunpack!(A) repack! = x[end] values = x[end - 1] indices = x[begin:end-2] - newvalues = unsafe_wrap(Array, _sizedjlmalloc(length(values), T), size(values)) - display(typeof(newvalues)) + newvalues = unsafe_wrap(Array, _sizedjlmalloc(length(values), eltype(M)), size(values)) copyto!(newvalues, values) newindices = _copytoraw.(indices) repack!() - B = M(size(A); fill) + B = M(size(A, 1), size(A, 2); fill) unsafepack!(B, newindices..., newvalues, false; decrementindices = false) end @@ -25,7 +24,7 @@ Base.convert(::Type{M}, A::M; fill = nothing) where {M<:AbstractGBArray} = A function LinearAlgebra.copy_similar(A::GBArrayOrTranspose, ::Type{T}) where T order = storageorder(A) C = similar(A, T, size(A)) - x = tempunpack_noformat!(A) + x = tempunpack!(A) repack! = x[end] values = x[end - 1] indices = x[begin:end-2] diff --git a/src/gbmatrix.jl b/src/gbmatrix.jl new file mode 100644 index 00000000..2fa1d444 --- /dev/null +++ b/src/gbmatrix.jl @@ -0,0 +1,45 @@ +# TODO: FIXME +# function LinearAlgebra.diagm(v::GBVector, k::Integer=0; desc = nothing) +# return Diagonal(v, k; desc) +# end + +# Indexing functions +#################### + +# Linear indexing +function Base.getindex(A::GBMatrixOrTranspose, v::AbstractVector) + throw("Not implemented") +end + +# Pack based constructors: +function GBMatrix{T, F}( + A::SparseVector; + fill = defaultfill(F) +) where {T, F} + C = GBMatrix{T, F}(size(A, 1), 1; fill) + return unsafepack!(C, _copytoraw(A)..., false) +end +GBMatrix{T}( + A::SparseVector; + fill::F = defaultfill(T) +) where {T, F} = GBMatrix{T, F}(A; fill) +GBMatrix( + A::SparseVector{T}; + fill::F = defaultfill(T) +) where {T, F} = GBMatrix{T, F}(A; fill) + +function GBMatrix{T, F}( + A::SparseMatrixCSC; + fill = defaultfill(F) +) where {T, F} + C = GBMatrix{T, F}(size(A)...; fill) + return unsafepack!(C, _copytoraw(A)..., false) +end +GBMatrix{T}( + A::SparseMatrixCSC; + fill::F = defaultfill(T) +) where {T, F} = GBMatrix{T, F}(A; fill) +GBMatrix( + A::SparseMatrixCSC{T}; + fill::F = defaultfill(T) +) where {T, F} = GBMatrix{T, F}(A; fill) \ No newline at end of file diff --git a/src/gbvector.jl b/src/gbvector.jl new file mode 100644 index 00000000..787c8f1b --- /dev/null +++ b/src/gbvector.jl @@ -0,0 +1,16 @@ +# Constructors: +############### + +# Some Base and basic SparseArrays/LinearAlgebra functions: +########################################################### +Base.unsafe_convert(::Type{LibGraphBLAS.GrB_Matrix}, v::GBVector) = v.p[] + +function Base.copy(A::GBVector{T, F}) where {T, F} + C = Ref{LibGraphBLAS.GrB_Matrix}() + LibGraphBLAS.GrB_Matrix_dup(C, A) + return GBVector{T, F}(C, A.fill) +end + +# Indexing functions: +##################### + diff --git a/src/matrix.jl b/src/matrix.jl deleted file mode 100644 index 9af72c54..00000000 --- a/src/matrix.jl +++ /dev/null @@ -1,29 +0,0 @@ -# Constructors: -############### - -# Empty constructors: -# TODO: match above, FIXME -function GBMatrix(v::GBVector{T, F}) where {T, F} - # this copies, I think that's ideal, and I can implement @view or something at a later date. - return copy(GBMatrix{T, F}(v.p, v.fill)) -end - -# Some Base and basic SparseArrays/LinearAlgebra functions: -########################################################### - -function Base.copy(A::GBMatrix{T, F}) where {T, F} - return GBMatrix{T, F}(_copyGrBMat(A.p), A.fill) -end - -# TODO: FIXME -# function LinearAlgebra.diagm(v::GBVector, k::Integer=0; desc = nothing) -# return Diagonal(v, k; desc) -# end - -# Indexing functions -#################### - -# Linear indexing -function Base.getindex(A::GBMatrixOrTranspose, v::AbstractVector) - throw("Not implemented") -end diff --git a/src/options.jl b/src/options.jl index 44315362..5803813b 100644 --- a/src/options.jl +++ b/src/options.jl @@ -81,7 +81,8 @@ end function gbset(A::AbstractGBArray, option, value) option = option_toconst(option) - if option == GxB_FORMAT && _hasconstantorder(A) + if option == GxB_FORMAT && _hasconstantorder(A) && + (option_toconst(storageorder(A)) != option_toconst(storageorder(A))) throw(ArgumentError("$(typeof(A)) may not have its storage order changed.")) end value = option_toconst(value) diff --git a/src/oriented.jl b/src/oriented.jl index 08a45d5f..597582d8 100644 --- a/src/oriented.jl +++ b/src/oriented.jl @@ -1 +1,70 @@ -StorageOrders.storageorder(::OrientedGBMatrix{T, F, O}) where {T, F, O} = O \ No newline at end of file +StorageOrders.storageorder(::OrientedGBMatrix{T, F, O}) where {T, F, O} = O + +function GBMatrixC{T, F}( + A::SparseVector; + fill = defaultfill(F) +) where {T, F} + C = GBMatrixC{T, F}(size(A, 1), 1; fill) + return unsafepack!(C, _copytoraw(A)..., false) +end +GBMatrixC{T}( + A::SparseVector; + fill::F = defaultfill(T) +) where {T, F} = GBMatrixC{T, F}(A; fill) +GBMatrixC( + A::SparseVector{T}; + fill::F = defaultfill(T) +) where {T, F} = GBMatrixC{T, F}(A; fill) + +function GBMatrixC{T, F}( + A::SparseMatrixCSC; + fill = defaultfill(F) +) where {T, F} + C = GBMatrixC{T, F}(size(A)...; fill) + return unsafepack!(C, _copytoraw(A)..., false) +end +GBMatrixC{T}( + A::SparseMatrixCSC; + fill::F = defaultfill(T) +) where {T, F} = GBMatrixC{T, F}(A; fill) +GBMatrixC( + A::SparseMatrixCSC{T}; + fill::F = defaultfill(T) +) where {T, F} = GBMatrixC{T, F}(A; fill) + +# BYROW +function GBMatrixR{T, F}( + A::SparseVector; + fill = defaultfill(F) +) where {T, F} + C = GBMatrixR{T, F}(size(A, 1), 1; fill) + unsafepack!(C, _copytoraw(A)..., false) + LibGraphBLAS.GxB_Matrix_Option_set(C, LibGraphBLAS.GxB_FORMAT, option_toconst(RowMajor())) + return C +end +GBMatrixR{T}( + A::SparseVector; + fill::F = defaultfill(T) +) where {T, F} = GBMatrixR{T, F}(A; fill) +GBMatrixR( + A::SparseVector{T}; + fill::F = defaultfill(T) +) where {T, F} = GBMatrixR{T, F}(A; fill) + +function GBMatrixR{T, F}( + A::SparseMatrixCSC; + fill = defaultfill(F) +) where {T, F} + C = GBMatrixR{T, F}(size(A)...; fill) + unsafepack!(C, _copytoraw(A)..., false) + LibGraphBLAS.GxB_Matrix_Option_set(C, LibGraphBLAS.GxB_FORMAT, option_toconst(RowMajor())) + return C +end +GBMatrixR{T}( + A::SparseMatrixCSC; + fill::F = defaultfill(T) +) where {T, F} = GBMatrixR{T, F}(A; fill) +GBMatrixR( + A::SparseMatrixCSC{T}; + fill::F = defaultfill(T) +) where {T, F} = GBMatrixR{T, F}(A; fill) \ No newline at end of file diff --git a/src/pack.jl b/src/pack.jl index 0e9a73df..dcd4e799 100644 --- a/src/pack.jl +++ b/src/pack.jl @@ -29,15 +29,15 @@ function _packdensematrix!( end function _packbitmap!( - A::AbstractGBArray{T}, bytemap::VecOrMat{Int8}, values::VecOrMat{T}; + A::AbstractGBArray{T}, bytemap::VecOrMat{B}, values::VecOrMat{T}; desc = nothing, order = ColMajor() -) where T +) where {T, B<:Union{Int8, Bool}} desc = _handledescriptor(desc) valsize = length(A) * sizeof(T) bytesize = length(A) * sizeof(eltype(bytemap)) isiso = (length(values) == 1) && (length(A) != 1) nvals = sum(bytemap) - bytepointer = pointer(bytemap) + bytepointer = Ptr{Int8}(pointer(bytemap)) valpointer = pointer(values) bytepointer = Ref{Ptr{Int8}}(bytepointer) valpointer = Ref{Ptr{Cvoid}}(valpointer) @@ -154,21 +154,19 @@ function unsafepack!( A::AbstractGBArray, M::StridedVecOrMat, shallow::Bool = true; order = ColMajor(), decrementindices = false # we don't need this, but it avoids another method. ) - _hasconstantorder(A) && (storageorder(A) !== order) && - (throw(ArgumentError("Cannot change the storage order of $(typeof(A))"))) _packdensematrix!(A, M; order) shallow && makeshallow!(A) + LibGraphBLAS.GxB_Matrix_Option_set(A, LibGraphBLAS.GxB_FORMAT, option_toconst(storageorder(A))) return A end function unsafepack!( - A::AbstractGBArray, M::DenseVecOrMat{Int8}, V::DenseVecOrMat, shallow::Bool = true; + A::AbstractGBArray, M::DenseVecOrMat{T}, V::DenseVecOrMat, shallow::Bool = true; order = ColMajor(), decrementindices = false -) - _hasconstantorder(A) && (storageorder(A) !== order) && - (throw(ArgumentError("Cannot change the storage order of $(typeof(A))"))) +) where {T <: Union{Int8, Bool}} _packbitmap!(A, M, V; order) shallow && makeshallow!(A) + LibGraphBLAS.GxB_Matrix_Option_set(A, LibGraphBLAS.GxB_FORMAT, option_toconst(storageorder(A))) return A end @@ -176,14 +174,13 @@ function unsafepack!( A::AbstractGBArray, ptr, idx, values, shallow::Bool = true; order = ColMajor(), decrementindices = true ) - _hasconstantorder(A) && (storageorder(A) !== order) && - (throw(ArgumentError("Cannot change the storage order of $(typeof(A))"))) if order === ColMajor() _packcscmatrix!(A, ptr, idx, values; decrementindices) else _packcsrmatrix!(A, ptr, idx, values; decrementindices) end shallow && makeshallow!(A) + LibGraphBLAS.GxB_Matrix_Option_set(A, LibGraphBLAS.GxB_FORMAT, option_toconst(storageorder(A))) return A end @@ -205,24 +202,6 @@ unsafepack!( s::Transpose{<:Any, <:SparseVector}, shallow::Bool = true; decrementindices = true ) = transpose(unsafepack!(A, parent(s), shallow; decrementindices)) - -# if no GBArray is provided then we will always return a GBShallowArray -# We will also *always* decrementindices here. -# -# function unsafepack!(S::SparseMatrixCSC; fill = nothing) -# return GBShallowMatrix(S; fill) -# end -# function unsafepack!(S::Transpose{<:Any, <:SparseMatrixCSC}; fill = nothing) -# return transpose(unsafepack!(S; fill)) -# end -# -# function unsafepack!(s::SparseVector; fill = nothing) -# return GBShallowVector(s; fill) -# end -# function unsafepack!(S::Transpose{<:Any, <:SparseVector}; fill = nothing) -# return transpose(unsafepack!(parent(S); fill)) -# end - # These functions do not have the `!` since they will not modify A during packing (to decrement indices) function pack(A::StridedVecOrMat; fill = defaultfill(eltype(A))) if A isa AbstractVector diff --git a/src/types.jl b/src/types.jl index 460e5c9b..355d52a3 100644 --- a/src/types.jl +++ b/src/types.jl @@ -299,7 +299,9 @@ copy `r` to a new Ref. This copy shares nothing with `r`. function _copyGrBMat(r::Base.RefValue{LibGraphBLAS.GrB_Matrix}) C = Ref{LibGraphBLAS.GrB_Matrix}() LibGraphBLAS.GrB_Matrix_dup(C, r[]) - return C + return finalizer(C) do ref + @wraperror LibGraphBLAS.GrB_Matrix_free(ref) + end end @@ -444,38 +446,6 @@ macro gbmatrixtype(typename) fill::F = defaultfill(T) ) where {T, F} = $typename{T, F}(A; fill) - function $typename{T, F}( - A::SparseVector; - fill = defaultfill(F) - ) where {T, F} - C = $typename{T, F}(size(A, 1), 1; fill) - return unsafepack!(C, _copytoraw(A)..., false) - end - $typename{T}( - A::SparseVector; - fill::F = defaultfill(T) - ) where {T, F} = $typename{T, F}(A; fill) - $typename( - A::SparseVector{T}; - fill::F = defaultfill(T) - ) where {T, F} = $typename{T, F}(A; fill) - - function $typename{T, F}( - A::SparseMatrixCSC; - fill = defaultfill(F) - ) where {T, F} - C = $typename{T, F}(size(A)...; fill) - return unsafepack!(C, _copytoraw(A)..., false) - end - $typename{T}( - A::SparseMatrixCSC; - fill::F = defaultfill(T) - ) where {T, F} = $typename{T, F}(A; fill) - $typename( - A::SparseMatrixCSC{T}; - fill::F = defaultfill(T) - ) where {T, F} = $typename{T, F}(A; fill) - # similar function Base.similar( A::$typename{T}, ::Type{TNew} = T, diff --git a/src/unpack.jl b/src/unpack.jl index a362573e..f16e3fb9 100644 --- a/src/unpack.jl +++ b/src/unpack.jl @@ -140,7 +140,7 @@ function _unpackcsrmatrix!( isiso = Ref{Bool}(allowiso ? true : C_NULL) isjumbled = C_NULL nnonzeros = nnz(A) - @wraperror LibGraphBLAS.GxB_Matrix_unpack_CSC( + @wraperror LibGraphBLAS.GxB_Matrix_unpack_CSR( A, rowptr, colidx, @@ -153,7 +153,7 @@ function _unpackcsrmatrix!( desc ) rowptr = unsafe_wrap(Array, Ptr{Int64}(rowptr[]), size(A, 1) + 1) - colidx = unsafe_wrap(Array, Ptr{Int64}(rowidx[]), colidxsize[]) + colidx = unsafe_wrap(Array, Ptr{Int64}(colidx[]), nnonzeros) nstored = isiso[] ? 1 : nnonzeros vals = unsafe_wrap(Array, Ptr{T}(values[]), nstored) if attachfinalizer @@ -185,9 +185,9 @@ function _unpackbitmapmatrix!( Csize = Ref{LibGraphBLAS.GrB_Index}(length(A) * sizeof(T)) Bsize = Ref{LibGraphBLAS.GrB_Index}(length(A) * sizeof(Bool)) values = Ref{Ptr{Cvoid}}(C_NULL) - bytemap = Ref{Ptr{Bool}}(C_NULL) + bytemap = Ref{Ptr{Int8}}(C_NULL) isiso = Ref{Bool}(allowiso ? true : C_NULL) - nnz = Ref{LibGraphBLAS.GrB_Index}(nnz(A)) + nnonzeros = Ref{LibGraphBLAS.GrB_Index}(nnz(A)) @wraperror LibGraphBLAS.GxB_Matrix_unpack_BitmapC( A, bytemap, @@ -195,12 +195,12 @@ function _unpackbitmapmatrix!( Bsize, Csize, isiso, - nnz, + nnonzeros, desc ) nstored = isiso[] ? 1 : szA v = unsafe_wrap(Array, Ptr{T}(values[]), nstored) - b = unsafe_wrap(Array, bytemap[], szA) + b = unsafe_wrap(Array, Ptr{Bool}(bytemap[]), szA) if attachfinalizer v = finalizer(v) do f _jlfree(f) @@ -505,7 +505,7 @@ function tempunpack!(A::AbstractGBArray, sparsity::Hypersparse; order = ColMajor return (ptr, idx1, idx2, nzval, repack!) end -function tempunpack_noformat!(A::AbstractGBArray, incrementindices = false) +function tempunpack!(A::AbstractGBArray, incrementindices = false) sparsity, order = format(A) return tempunpack!(A, sparsity; order, incrementindices) end diff --git a/src/vector.jl b/src/vector.jl deleted file mode 100644 index 3ab00229..00000000 --- a/src/vector.jl +++ /dev/null @@ -1,121 +0,0 @@ -# Constructors: -############### -""" - GBVector{T}(n; fill = nothing) -""" -# function GBVector{T}(n; fill::F = nothing) where {T, F} -# m = Ref{LibGraphBLAS.GrB_Matrix}() -# @wraperror LibGraphBLAS.GrB_Matrix_new(m, gbtype(T), n, 1) -# return GBVector{T}(m; fill) -# end -# -# function GBVector{T}( -# p::Base.RefValue{LibGraphBLAS.GrB_Matrix}; -# fill::F = nothing -# ) where {T, F} -# -# v = GBVector{T, F}(finalizer(p) do ref -# @wraperror LibGraphBLAS.GrB_Matrix_free(ref) -# end, fill) -# gbset(v, FORMAT, BYCOL) -# return v -# end -# -# GBVector{T}(dims::Dims{1}; fill = nothing) where {T} = GBVector{T}(dims...; fill) -# GBVector{T}(nrows::Base.OneTo; fill = nothing) where {T} = -# GBVector{T}(nrows.stop; fill) -# GBVector{T}(nrows::Tuple{Base.OneTo,}; fill = nothing) where {T} = GBVector{T}(first(nrows); fill) -# -# """ -# GBVector(I::AbstractVector, X::AbstractVector{T}; fill = nothing) -# -# Create a GBVector from a vector of indices `I` and a vector of values `X`. -# """ -# function GBVector(I::AbstractVector{U}, X::AbstractVector{T}; combine = +, nrows = maximum(I), fill = nothing) where {U<:Integer, T} -# I isa Vector || (I = collect(I)) -# X isa Vector || (X = collect(X)) -# v = GBVector{T}(nrows; fill) -# build!(v, I, X; combine) -# return v -# end -# -# function GBVector{T}( -# I::AbstractVector, X::AbstractVector{Told}; -# combine = +, nrows = maximum(I), fill = nothing -# ) where {T, U, Told} -# return GBVector(I, T.(X); combine, nrows, fill) -# end -# #iso valued constructors. -# """ -# GBVector(I, x; nrows = maximum(I) fill = nothing) -# -# Create an GBVector `v` from coordinates `I` such that `M[I] = x` . -# The resulting vector is "iso-valued" such that it only stores `x` once rather than once for -# each index. -# """ -# function GBVector(I::AbstractVector{U}, x::T; -# nrows = maximum(I), fill = nothing) where {U<:Integer, T} -# A = GBVector{T}(nrows; fill) -# build!(A, I, x) -# return A -# end -# -# """ -# GBVector(n, x; fill = nothing) -# -# Create an `n` length dense GBVector `v` such that M[:] = x. -# The resulting vector is "iso-valued" such that it only stores `x` once rather than once for -# each index. -# """ -# function GBVector(n::Integer, x::T; fill = nothing) where {T} -# v = GBVector{T}(n; fill) -# v .= x -# return v -# end -# -# GBVector{T, F}(::Number) where {T, F} = throw(ArgumentError("The F parameter is implicit and determined by the `fill` keyword argument to constructors. Users must not specify this manually.")) - -# Some Base and basic SparseArrays/LinearAlgebra functions: -########################################################### -Base.unsafe_convert(::Type{LibGraphBLAS.GrB_Matrix}, v::GBVector) = v.p[] - -function Base.copy(A::GBVector{T, F}) where {T, F} - C = Ref{LibGraphBLAS.GrB_Matrix}() - LibGraphBLAS.GrB_Matrix_dup(C, A) - return GBVector{T, F}(C, A.fill) -end - -#We need these until I can get a SparseArrays.nonzeros implementation -# TODO: REMOVE -function Base.show(io::IO, ::MIME"text/plain", v::GBVector) - gxbprint(io, v) -end - -function Base.show(io::IO, v::GBVector) - gxbprint(io, v) -end - -function Base.show(io::IOContext, v::GBVector) - gxbprint(io, v) -end - -# Indexing functions: -##################### - -function Base.getindex( - u::GBVector, I; - mask = nothing, accum = nothing, desc = nothing -) - return extract(u, I; mask, accum, desc) -end - -function Base.getindex(u::GBVector, ::Colon; mask = nothing, accum = nothing, desc = nothing) - return extract(u, :) -end - -function Base.getindex( - u::GBVector, i::Union{Vector, UnitRange, StepRange}; - mask = nothing, accum = nothing, desc = nothing -) - return extract(u, i; mask, accum, desc) -end diff --git a/test/types.jl b/test/types.jl index 537d52fa..3dedffbb 100644 --- a/test/types.jl +++ b/test/types.jl @@ -1,16 +1,20 @@ @testset "types.jl" begin for type ∈ [GBMatrix, GBMatrixC, GBMatrixR] # {T, F}(nrows, ncols; fill) + @test type{Int64, Int64}(3, 3) isa type A = type{Int64, Int64}(3, 3) @test getfill(A) === zero(Int64) @test nnz(A) == 0 type === GBMatrixC && (@test storageorder(A) === ColMajor()) type === GBMatrixR && (@test storageorder(A) === RowMajor()) + + @test type{Int64, Int64}(3, 3,; fill = Int32(3)) isa type A = type{Int64, Int64}(3, 3; fill = 3) @test getfill(A) === 3 type === GBMatrixC && (@test storageorder(A) === ColMajor()) type === GBMatrixR && (@test storageorder(A) === RowMajor()) @test_throws MethodError type{Int64, Nothing}(3, 3; fill = 3) + @test type{Int64, Nothing}(3, 3; fill = nothing) isa type # {T}(nrows, ncols; fill) A = type{Int64}(3, 3) From a78e6d74e3ff2bfa8ecbf8117b875fe74311de2f Mon Sep 17 00:00:00 2001 From: Will Kimmerer Date: Thu, 6 Oct 2022 13:36:12 -0400 Subject: [PATCH 12/16] add some tests, fix binaryops between valid_union and UDTs --- src/abstractgbarray.jl | 53 ++++++++++++++---------- src/operators/binaryops.jl | 2 +- src/shallowtypes.jl | 2 +- test/abstractgbarray.jl | 82 ++++++++++++++++++++++++++++++++++++++ test/gbarray.jl | 45 +++++++++++++++++++++ test/runtests.jl | 1 + 6 files changed, 162 insertions(+), 23 deletions(-) create mode 100644 test/abstractgbarray.jl diff --git a/src/abstractgbarray.jl b/src/abstractgbarray.jl index 8a5f3224..fd452e70 100644 --- a/src/abstractgbarray.jl +++ b/src/abstractgbarray.jl @@ -36,7 +36,6 @@ Base.eltype(::Type{<:AbstractGBArray{T, F}}) where{T, F} = Union{T, F} storedeltype(x) = eltype(x) storedeltype(::AbstractGBArray{T}) where T = T -# TODO: The transpose Base.unsafe_convert(::Type{LibGraphBLAS.GrB_Matrix}, A::AbstractGBArray) = A.p[] Base.unsafe_convert(::Type{LibGraphBLAS.GrB_Vector}, A::AbstractGBVector) = @@ -175,26 +174,28 @@ reshape!(A::AbstractGBMatrix, dims...; bycol = true) = reshape!(A::AbstractGBMatrix, n; bycol = true) = reshape!(A, n, 1; bycol) function Base.reshape( - A::AbstractGBMatrix, nrows, ncols; + A::AbstractGBMatrix, nrows::Int, ncols::Int; bycol = true, desc = nothing) desc = _handledescriptor(desc) - lenA = length(A) - nrows isa Colon && ncols isa Colon && throw( - ArgumentError("nrows and ncols may not both be Colon")) - nrows isa Colon && (nrows = lenA ÷ ncols) - ncols isa Colon && (ncols = lenA ÷ nrows) C = Ref{LibGraphBLAS.GrB_Matrix}() @wraperror LibGraphBLAS.GxB_Matrix_reshapeDup( C, A, bycol, nrows, ncols, desc ) - # TODO, do better. This is ugly and allocates twice. out = similar(A) out.p = finalizer(C) do ref @wraperror LibGraphBLAS.GrB_Matrix_free(ref) end return out end + +Base.reshape(A::AbstractGBMatrix, ::Colon, ncols::Int; bycol = true) = + reshape(A, length(A) ÷ ncols, ncols; bycol) +Base.reshape(A::AbstractGBMatrix, nrows::Int, ::Colon; bycol = true) = + reshape(A, nrows, length(A) ÷ nrows; bycol) +Base.reshape(::AbstractGBMatrix, ::Colon, ::Colon; bycol = true) = + throw(ArgumentError("nrows and ncols may not both be Colon")) + Base.reshape(A::AbstractGBMatrix, dims::Tuple{Vararg{Int64, N}}; bycol = true) where N = reshape(A, dims...; bycol) Base.reshape(A::AbstractGBMatrix, dims::Tuple{Vararg{Union{Colon, Int64}}}; bycol = true) = @@ -205,10 +206,10 @@ Base.reshape( bycol = true ) = reshape(A, dims...; bycol) -Base.reshape(A::AbstractGBMatrix, n; bycol = true) = reshape(A, n, 1; bycol) +Base.reshape(A::AbstractGBMatrix, n::Union{Int, Colon}; bycol = true) = reshape(A, n, 1; bycol) function build!(A::AbstractGBMatrix{T}, I::AbstractVector, J::AbstractVector, x::T) where {T} - nnz(A) == 0 || throw(OutputNotEmptyError("Cannot build matrix with existing elements")) + nnz(A) == 0 || throw(OutputNotEmptyError()) length(I) == length(J) || DimensionMismatch("I, J and X must have the same length") x = GBScalar(x) @@ -260,7 +261,7 @@ for T ∈ valid_vec I isa Vector || (I = collect(I)) J isa Vector || (J = collect(J)) X isa Vector || (X = collect(X)) - nnz(A) == 0 || throw(OutputNotEmptyError("Cannot build matrix with existing elements")) + nnz(A) == 0 || throw(OutputNotEmptyError()) length(X) == length(I) == length(J) || DimensionMismatch("I, J and X must have the same length") decrement!(I) @@ -275,6 +276,7 @@ for T ∈ valid_vec ) increment!(I) increment!(J) + return A end end # Setindex functions @@ -348,7 +350,7 @@ function build!( J isa Vector || (J = collect(J)) X isa Vector || (X = collect(X)) X = convert(Vector{T}, X) - nnz(A) == 0 || throw(OutputNotEmptyError("Cannot build matrix with existing elements")) + nnz(A) == 0 || throw(OutputNotEmptyError()) length(X) == length(I) == length(J) || DimensionMismatch("I, J and X must have the same length") decrement!(I) @@ -363,6 +365,7 @@ function build!( ) increment!(I) increment!(J) + return A end function Base.setindex!(A::AbstractGBMatrix{T}, x, i::Integer, j::Integer) where {T} @@ -384,7 +387,7 @@ function Base.getindex(A::AbstractGBMatrix{T}, i::Integer, j::Integer) where {T} end end # Fix ambiguity -function Base.getindex(A::Transpose{T, <:AbstractGBMatrix{T}}, i::Integer, j::Integer) where {T} +function Base.getindex(A::Transpose{T, <:AbstractGBMatrix{T}}, i::Int, j::Int) where T return getindex(parent(A), j, i) end @@ -439,7 +442,7 @@ for T ∈ valid_vec end end -function Base.isstored(A::AbstractGBMatrix, i::Int, j::Int) +function Base.isstored(A::AbstractGBArray, i::Int, j::Int = 1) result = LibGraphBLAS.GxB_Matrix_isStoredElement(A, decrement!(i), decrement!(j)) if result == LibGraphBLAS.GrB_SUCCESS true @@ -504,6 +507,9 @@ function subassign!( J = decrement!(J) rereshape = false sz1 = size(A, 1) + if !(eltype(A) <: valid_union) || !(eltype(C) <: valid_union) + A = copy_similar(A, eltype(C)) + end # reshape A: nx1 -> 1xn if A isa GBVector && (ni_sizecheck == size(A, 2) && nj_sizecheck == sz1) @wraperror LibGraphBLAS.GxB_Matrix_reshape(parent(A), true, 1, sz1, C_NULL) @@ -537,20 +543,20 @@ function subassign!(C::AbstractGBArray{T}, x, I, J; return x end -function subassign!(C::AbstractGBArray, x::AbstractMatrix, I, J; - mask = nothing, accum = nothing, desc = nothing) +function subassign!(C::AbstractGBArray{T}, x::AbstractMatrix, I, J; + mask = nothing, accum = nothing, desc = nothing) where T _canbeoutput(C) || throw(ShallowException()) - array = x isa VecOrMat ? x : collect(x) + array = x isa Matrix{T} ? x : copyto!(Matrix{T}(undef, size(x)...), x) array = pack(array) subassign!(C, array, I, J; mask, accum, desc) unsafeunpack!(array) return x end -function subassign!(C::AbstractGBArray, x::AbstractVector, I, J; - mask = nothing, accum = nothing, desc = nothing) +function subassign!(C::AbstractGBArray{T}, x::AbstractVector, I, J; + mask = nothing, accum = nothing, desc = nothing) where T _canbeoutput(C) || throw(ShallowException()) - array = x isa VecOrMat ? x : collect(x) + array = x isa Vector{T} ? x : copyto!(Vector{T}(undef, size(x)...), x) array = pack(array) subassign!(C, array, I, J; mask, accum, desc) unsafeunpack!(array) @@ -602,6 +608,9 @@ function assign!( mask = _handlemask!(desc, mask) I = decrement!(I) J = decrement!(J) + if !(eltype(A) <: valid_union) || !(eltype(C) <: valid_union) + A = copy_similar(A, eltype(C)) + end @wraperror LibGraphBLAS.GrB_Matrix_assign(C, mask, _handleaccum(accum, storedeltype(C)), parent(A), I, ni, J, nj, desc) increment!(I) increment!(J) @@ -712,6 +721,7 @@ for T ∈ valid_vec combine ) increment!(I) + return v end end # Setindex functions @@ -799,6 +809,7 @@ function build!(v::AbstractGBVector{T}, I::AbstractVector{<:Integer}, X::Abstrac combine ) increment!(I) + return v end function Base.setindex!(v::AbstractGBVector{T}, x, i::Integer) where {T} @@ -845,7 +856,7 @@ function SparseArrays.nonzeroinds(v::GBVector{T}) where {T} return increment!(I) end -function build!(v::GBVector{T}, I::Vector{<:Integer}, x::T2) where {T, T2} +function build!(v::AbstractGBVector{T}, I::Vector{<:Integer}, x::T2) where {T, T2} _canbeoutput(v) || throw(ShallowException()) nnz(v) == 0 || throw(OutputNotEmptyError("Cannot build vector with existing elements")) x = GBScalar(convert(T2, x)) diff --git a/src/operators/binaryops.jl b/src/operators/binaryops.jl index dcb37fcc..0ebfc780 100644 --- a/src/operators/binaryops.jl +++ b/src/operators/binaryops.jl @@ -28,7 +28,7 @@ function SuiteSparseGraphBLAS.binaryop( f::F, ::Type{X}, ::Type{Y} ) where {F, X, Y} P = promote_type(X, Y) - if isconcretetype(P) + if isconcretetype(P) && (X <: valid_union && Y <: valid_union) return binaryop(f, P, P) else return fallback_binaryop(f, X, Y) diff --git a/src/shallowtypes.jl b/src/shallowtypes.jl index 04e60276..63a34f81 100644 --- a/src/shallowtypes.jl +++ b/src/shallowtypes.jl @@ -71,7 +71,7 @@ function Base.similar( A::GBShallowVector{T}, ::Type{TNew}, dims::Tuple{Int64} = size(A); fill = A.fill) where {T, TNew} - return GBVector{TNew}(dims..., fill) + return GBVector{TNew}(dims...; fill) end function Base.similar( diff --git a/test/abstractgbarray.jl b/test/abstractgbarray.jl new file mode 100644 index 00000000..67f71f33 --- /dev/null +++ b/test/abstractgbarray.jl @@ -0,0 +1,82 @@ +@testset "abstractgbarray.jl" begin + A = GBMatrix([[1,2] [3,4]]) + @testset "_canbeoutput" begin + B = SuiteSparseGraphBLAS.pack([[1,2] [3,4]]) + @test SuiteSparseGraphBLAS._canbeoutput(A) + @test !SuiteSparseGraphBLAS._canbeoutput(B) + end + + @testset "_hasconstantorder" begin + @test !SuiteSparseGraphBLAS._hasconstantorder(A) + @test SuiteSparseGraphBLAS._hasconstantorder(GBMatrixC(A)) + end + @testset "Base Functions" begin + @test Base.IndexStyle(A) === IndexCartesian() + + @testset "similar of Transpose" begin + @test similar(A, Float64, 5) isa GBVector{Float64} + @test similar(A, 5) isa GBVector{eltype(A)} + end + + C = GBMatrix{Int64}(2, 2) + @test copyto!(C, A) == A + @test C !== A + + @testset "reshape and resize!" begin + @test size(reshape(A, 1, 4)) == (1, 4) + @test size(reshape(A, 1, :)) == (1, 4) + @test size(reshape(A, :, 1)) == (4, 1) + @test size(reshape(A, :, 2)) == (2, 2) + @test size(reshape(A, 2, :)) == (2, 2) + @test size(reshape(A, (:, 2))) == (2, 2) + @test size(reshape(A, (2, 2))) == (2, 2) + @test size(reshape(A, :)) == (4, 1) + B = copy(A) + @test resize!(B, 1, 4) == GBMatrix([1, 1], [1, 2], [1, 3], 1, 4) + @test resize!(B, 1, 1) === B + end + @tesetset "isstored" begin + @test deleteat!(A, 1, 1) isa SuiteSparseGraphBLAS.AbstractGBArray + @test !Base.isstored(A, 1, 1) + @test Base.isstored(A, 2, 2) + A[1, 1] = 1 + end + end + + @testset "SparseArrays.jl Functions" begin + @test SparseMatrixCSC(A) == A + end + + @testset "build!" begin + # UDTs + X = GBMatrix{Int128}(2, 2) + @test SuiteSparseGraphBLAS.build!(X, [1,1,2,2], [1,2,1,2], Int128[1,2,3,4]) == + Int128[[1,3] [2,4]] + X = GBMatrix{Int128}(2, 2) + @test SuiteSparseGraphBLAS.build!(X, [1,1,2,2], [1,2,1,2], Int128(5)) == + Int128[[5,5] [5,5]] + + # Built-in + X = GBMatrix{Float64}(2, 2) + @test SuiteSparseGraphBLAS.build!(X, [1,1,2,2], [1,2,1,2], Float64[1,2,3,4]) == + Float64[[1,3] [2,4]] + X = GBMatrix{Float64}(2, 2) + @test SuiteSparseGraphBLAS.build!(X, [1,1,2,2], [1,2,1,2], Float64(5)) == + Float64[[5,5] [5,5]] + end + + @testset "findnz" begin + # UDTs + X = GBMatrix{Int128}([[1,2] [3,4]]) + @test eltype(X) === Int128 + @test findnz(X) == ([1,2,1,2], [1,1,2,2], [1,2,3,4]) + @test nonzeros(X) == [1,2,3,4] + @test nonzeroinds(X) == ([1,2,1,2], [1,1,2,2]) + + X = GBMatrix{Float64, Nothing}([[1,2] [3,4]]) + @test eltype(X) == Union{Float64, Nothing} + @test findnz(X) == ([1,2,1,2], [1,1,2,2], [1,2,3,4]) + @test nonzeros(X) == [1,2,3,4] + @test nonzeroinds(X) == ([1,2,1,2], [1,1,2,2]) + end +end \ No newline at end of file diff --git a/test/gbarray.jl b/test/gbarray.jl index fb3c9c2c..ca8cafdc 100644 --- a/test/gbarray.jl +++ b/test/gbarray.jl @@ -27,6 +27,14 @@ A = GBMatrix([[1,2] [3,4]]) @test A[[1,2], [1,1]] == [[1,2] [1,2]] + A = GBMatrix(Int128[[1,2] [3,4]]) + @test A[1, 1] == Int128(1) + @test (A[1, 1] = 10) == 10 + @test A[1, 1] == Int128(10) + @test A[1, 1] isa Int128 + @test A'[1, 2] == 2 + deleteat!(A, 1, 1) + @test A[1, 1] == getfill(A) end @testset "sparse" begin #Construction and indexing correct for sparse matrices @@ -83,5 +91,42 @@ v[10:10:100, mask = GBVector([true, true, true, false, false,false, true, false, true, true]), accum = SuiteSparseGraphBLAS.iseq] = collect(1:10) @test v[10] == 1 && v[60] == 6 && v[100] == 1 + + ##### UDT: + x = sprand(Int128, 10, 10, 0.1) + m = GBMatrix(x) + empty!(m) + @test nnz(m) == 0 + #steprange and scalar + m[1:2:10, 1] = [1, 2, 3, 4, 5] + @test m[1:2:10, 1] == GBMatrix([1,2,3,4,5]) + + #range and range + m[8:10, 8:10] = GBMatrix([[1,2,3] [4,5,6] [7,8,9]]) + @test Matrix(m[8:10, 8:10]) == [[1,2,3] [4,5,6] [7,8,9]] + + #range, range, accum, and mask + mask = GBMatrix([[true, true, false] [false, true, true] [true, false,true]]) + m[8:10, 8:10, mask = mask, accum = *, desc = Descriptor(replace_output=true)] = + fill(10, 3, 3) + @test m[9, 10] == zero(eltype(m)) + n = setfill(m, nothing) + @test n[9, 10] === nothing + @test m[10, 10] == 90 + + #vectors + x = sprand(Float32, 100, 0.35) + v = GBVector(x) + empty!(v) + @test nnz(v) == 0 + + #steprange + v[10:10:100] = collect(1:10) + @test v[100] == 10 + + #steprange, mask, accum + v[10:10:100, mask = GBVector([true, true, true, false, false,false, true, false, true, true]), accum = SuiteSparseGraphBLAS.iseq] = + collect(1:10) + @test v[10] == 1 && v[60] == 6 && v[100] == 1 end end diff --git a/test/runtests.jl b/test/runtests.jl index 2e638cb4..daefb6dd 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -73,6 +73,7 @@ println("$(SuiteSparseGraphBLAS.get_lib())") include_test("libutils.jl") include_test("operatorutils.jl") include_test("ops.jl") + include_test("abstractgbarray.jl") include_test("gbarray.jl") include_test("types.jl") include_test("issues.jl") From 855d7571e6ed33faef76741fb5d30b3a52772efc Mon Sep 17 00:00:00 2001 From: Will Kimmerer Date: Thu, 6 Oct 2022 13:44:44 -0400 Subject: [PATCH 13/16] missing qualifiers --- src/abstractgbarray.jl | 4 ++-- src/operations/broadcasts.jl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/abstractgbarray.jl b/src/abstractgbarray.jl index fd452e70..cfd6311e 100644 --- a/src/abstractgbarray.jl +++ b/src/abstractgbarray.jl @@ -508,7 +508,7 @@ function subassign!( rereshape = false sz1 = size(A, 1) if !(eltype(A) <: valid_union) || !(eltype(C) <: valid_union) - A = copy_similar(A, eltype(C)) + A = LinearAlgebra.copy_similar(A, eltype(C)) end # reshape A: nx1 -> 1xn if A isa GBVector && (ni_sizecheck == size(A, 2) && nj_sizecheck == sz1) @@ -609,7 +609,7 @@ function assign!( I = decrement!(I) J = decrement!(J) if !(eltype(A) <: valid_union) || !(eltype(C) <: valid_union) - A = copy_similar(A, eltype(C)) + A = LinearAlgebra.copy_similar(A, eltype(C)) end @wraperror LibGraphBLAS.GrB_Matrix_assign(C, mask, _handleaccum(accum, storedeltype(C)), parent(A), I, ni, J, nj, desc) increment!(I) diff --git a/src/operations/broadcasts.jl b/src/operations/broadcasts.jl index 5b27344d..a30165f1 100644 --- a/src/operations/broadcasts.jl +++ b/src/operations/broadcasts.jl @@ -331,4 +331,4 @@ function Base.materialize!( return setindex!(A, bc.args[begin], :) end -Base.Broadcast.broadcasted(::Type{T}, A::AbstractGBArray) where T = copy_similar(A, T) \ No newline at end of file +Base.Broadcast.broadcasted(::Type{T}, A::AbstractGBArray) where T = LinearAlgebra.copy_similar(A, T) \ No newline at end of file From 4d70f611ca29751e3aa4891b5661c9c56eb5a0b3 Mon Sep 17 00:00:00 2001 From: Will Kimmerer Date: Thu, 6 Oct 2022 14:01:22 -0400 Subject: [PATCH 14/16] fix typo --- src/types.jl | 2 +- test/abstractgbarray.jl | 2 +- test/gbarray.jl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/types.jl b/src/types.jl index 355d52a3..bc6a9637 100644 --- a/src/types.jl +++ b/src/types.jl @@ -825,7 +825,7 @@ mutable struct GBShallowMatrix{T, F, P, B, A} <: AbstractGBShallowArray{T, F, P, nzval::A # array storage for dense arrays, nonzero values storage for everyone else. end function GBShallowMatrix{T}(p, fill::F, ptr::P, idx::P, h::P, bitmap::B, nzval::A) where {T, F, P, B, A} - GBShallowVector{T, F, P, B, A}(p, fill, ptr, idx, h, bitmap, nzval) + GBShallowMatrix{T, F, P, B, A}(p, fill, ptr, idx, h, bitmap, nzval) end # We need to do this at runtime. This should perhaps be `RuntimeOrder`, but that trait should likely be removed. diff --git a/test/abstractgbarray.jl b/test/abstractgbarray.jl index 67f71f33..d219ec66 100644 --- a/test/abstractgbarray.jl +++ b/test/abstractgbarray.jl @@ -35,7 +35,7 @@ @test resize!(B, 1, 4) == GBMatrix([1, 1], [1, 2], [1, 3], 1, 4) @test resize!(B, 1, 1) === B end - @tesetset "isstored" begin + @testset "isstored" begin @test deleteat!(A, 1, 1) isa SuiteSparseGraphBLAS.AbstractGBArray @test !Base.isstored(A, 1, 1) @test Base.isstored(A, 2, 2) diff --git a/test/gbarray.jl b/test/gbarray.jl index ca8cafdc..3b6276e9 100644 --- a/test/gbarray.jl +++ b/test/gbarray.jl @@ -115,7 +115,7 @@ @test m[10, 10] == 90 #vectors - x = sprand(Float32, 100, 0.35) + x = sprand(ComplexF16, 100, 0.35) v = GBVector(x) empty!(v) @test nnz(v) == 0 From 03585de32d2f29a6428d9c698cce0fd990474208 Mon Sep 17 00:00:00 2001 From: Will Kimmerer Date: Thu, 6 Oct 2022 14:08:02 -0400 Subject: [PATCH 15/16] similar->oftype --- src/SuiteSparseGraphBLAS.jl | 2 +- src/abstractgbarray.jl | 4 ++-- src/operations/broadcasts.jl | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/SuiteSparseGraphBLAS.jl b/src/SuiteSparseGraphBLAS.jl index 2313aaf7..44b405c5 100644 --- a/src/SuiteSparseGraphBLAS.jl +++ b/src/SuiteSparseGraphBLAS.jl @@ -18,7 +18,7 @@ using SparseArrays using SparseArrays: nonzeroinds, getcolptr, getrowval, getnzval using MacroTools using LinearAlgebra -using LinearAlgebra: copy_similar +using LinearAlgebra: copy_oftype using Random: randsubseq, default_rng, AbstractRNG, GLOBAL_RNG using SpecialFunctions: lgamma, gamma, erf, erfc using Base.Broadcast diff --git a/src/abstractgbarray.jl b/src/abstractgbarray.jl index cfd6311e..a1176e73 100644 --- a/src/abstractgbarray.jl +++ b/src/abstractgbarray.jl @@ -508,7 +508,7 @@ function subassign!( rereshape = false sz1 = size(A, 1) if !(eltype(A) <: valid_union) || !(eltype(C) <: valid_union) - A = LinearAlgebra.copy_similar(A, eltype(C)) + A = LinearAlgebra.copy_oftype(A, eltype(C)) end # reshape A: nx1 -> 1xn if A isa GBVector && (ni_sizecheck == size(A, 2) && nj_sizecheck == sz1) @@ -609,7 +609,7 @@ function assign!( I = decrement!(I) J = decrement!(J) if !(eltype(A) <: valid_union) || !(eltype(C) <: valid_union) - A = LinearAlgebra.copy_similar(A, eltype(C)) + A = LinearAlgebra.copy_oftype(A, eltype(C)) end @wraperror LibGraphBLAS.GrB_Matrix_assign(C, mask, _handleaccum(accum, storedeltype(C)), parent(A), I, ni, J, nj, desc) increment!(I) diff --git a/src/operations/broadcasts.jl b/src/operations/broadcasts.jl index a30165f1..a5cfe27f 100644 --- a/src/operations/broadcasts.jl +++ b/src/operations/broadcasts.jl @@ -331,4 +331,4 @@ function Base.materialize!( return setindex!(A, bc.args[begin], :) end -Base.Broadcast.broadcasted(::Type{T}, A::AbstractGBArray) where T = LinearAlgebra.copy_similar(A, T) \ No newline at end of file +Base.Broadcast.broadcasted(::Type{T}, A::AbstractGBArray) where T = LinearAlgebra.copy_oftype(A, T) \ No newline at end of file From 51d45e983bd453f2e5a00fb3370ef6a4cfc76df8 Mon Sep 17 00:00:00 2001 From: Will Kimmerer Date: Thu, 6 Oct 2022 14:17:36 -0400 Subject: [PATCH 16/16] refix typoe --- src/convert.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/convert.jl b/src/convert.jl index 8e7f77a7..be8d230e 100644 --- a/src/convert.jl +++ b/src/convert.jl @@ -21,7 +21,7 @@ end Base.convert(::Type{M}, A::M; fill = nothing) where {M<:AbstractGBArray} = A -function LinearAlgebra.copy_similar(A::GBArrayOrTranspose, ::Type{T}) where T +function LinearAlgebra.copy_oftype(A::GBArrayOrTranspose, ::Type{T}) where T order = storageorder(A) C = similar(A, T, size(A)) x = tempunpack!(A)