diff --git a/NDTensors/Project.toml b/NDTensors/Project.toml index efdaf9b2f3..f4e3920db5 100644 --- a/NDTensors/Project.toml +++ b/NDTensors/Project.toml @@ -10,6 +10,7 @@ ArrayLayouts = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" Compat = "34da2185-b29b-5c13-b0c7-acf172513d20" Dictionaries = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4" +FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" EllipsisNotation = "da5c29d0-fa7d-589e-88eb-ea29b0a81949" FLoops = "cc61a311-1640-44b5-9fba-1b764f453329" Folds = "41a02a25-b8f0-4f67-bc48-60067656b558" diff --git a/NDTensors/src/NDTensors.jl b/NDTensors/src/NDTensors.jl index 052927677f..cd91a265bd 100644 --- a/NDTensors/src/NDTensors.jl +++ b/NDTensors/src/NDTensors.jl @@ -23,6 +23,7 @@ for lib in [ :AlgorithmSelection, :AllocateData, :BaseExtensions, + :UnspecifiedTypes, :SetParameters, :BroadcastMapConversion, :Unwrap, @@ -37,6 +38,7 @@ for lib in [ :SmallVectors, :SortedSets, :TagSets, + :UnallocatedArrays, ] include("lib/$(lib)/src/$(lib).jl") @eval using .$lib: $lib diff --git a/NDTensors/src/lib/SetParameters/Project.toml b/NDTensors/src/lib/SetParameters/Project.toml new file mode 100644 index 0000000000..d4b3d82f02 --- /dev/null +++ b/NDTensors/src/lib/SetParameters/Project.toml @@ -0,0 +1,8 @@ +[deps] +PackageExtensionCompat = "65ce6f38-6b18-4e1d-a461-8949797d7930" + +[extensions] +SetParametersFillArraysExt = "FillArrays" + +[compat] +PackageExtensionCompat = "1" \ No newline at end of file diff --git a/NDTensors/src/lib/SetParameters/TODO.md b/NDTensors/src/lib/SetParameters/TODO.md index 264165241c..f20ba6b57d 100644 --- a/NDTensors/src/lib/SetParameters/TODO.md +++ b/NDTensors/src/lib/SetParameters/TODO.md @@ -27,3 +27,12 @@ default_parameter(::Type{<:AbstractArray}, ::Position{2}) = 1 nparameters(::Type{<:AbstractArray}) = Val(2) ``` + +https://github.com/ITensor/ITensors.jl/pull/1213/files#r1431708585 + +# Create generic set_eltype and set_ndims functions which can be defined on +# Array structures and use the `set_parameter` +```julia +set_eltype(T::Type, elt::Type) = Error("set_eltype is not defined for datatype $T") +set_eltype(T::Type{<:Array}, elt::Type) = set_parameter(T, Position{1}(), elt) +``` \ No newline at end of file diff --git a/NDTensors/src/lib/SetParameters/ext/SetParametersFillArraysExt/SetParametersFillArraysExt.jl b/NDTensors/src/lib/SetParameters/ext/SetParametersFillArraysExt/SetParametersFillArraysExt.jl new file mode 100644 index 0000000000..35f122dc05 --- /dev/null +++ b/NDTensors/src/lib/SetParameters/ext/SetParametersFillArraysExt/SetParametersFillArraysExt.jl @@ -0,0 +1,4 @@ +module SetParametersFillArraysExt +include("set_types.jl") + +end diff --git a/NDTensors/src/lib/SetParameters/ext/SetParametersFillArraysExt/set_types.jl b/NDTensors/src/lib/SetParameters/ext/SetParametersFillArraysExt/set_types.jl new file mode 100644 index 0000000000..8e53665e05 --- /dev/null +++ b/NDTensors/src/lib/SetParameters/ext/SetParametersFillArraysExt/set_types.jl @@ -0,0 +1,77 @@ +using FillArrays: AbstractFill, Fill, Zeros +using NDTensors.SetParameters: SetParameters, Position +using NDTensors.UnspecifiedTypes: UnspecifiedZero + +# `SetParameters.jl` overloads. +SetParameters.get_parameter(::Type{<:AbstractFill{P1}}, ::Position{1}) where {P1} = P1 +SetParameters.get_parameter(::Type{<:AbstractFill{<:Any,P2}}, ::Position{2}) where {P2} = P2 +function SetParameters.get_parameter( + ::Type{<:AbstractFill{<:Any,<:Any,P3}}, ::Position{3} +) where {P3} + return P3 +end + +## Setting paramaters +# Set parameter 1 +function SetParameters.set_parameter(T::Type{<:AbstractFill}, ::Position{1}, P1) + return unspecify_parameters(T){P1} +end +function SetParameters.set_parameter( + T::Type{<:AbstractFill{<:Any,P2}}, ::Position{1}, P1 +) where {P2} + return unspecify_parameters(T){P1,P2} +end +function SetParameters.set_parameter( + T::Type{<:AbstractFill{<:Any,P2,P3}}, ::Position{1}, P1 +) where {P2,P3} + return unspecify_parameters(T){P1,P2,P3} +end + +# Set parameter 2 +function SetParameters.set_parameter( + T::Type{<:AbstractFill{P1}}, ::Position{2}, P2 +) where {P1} + return unspecify_parameters(T){P1,P2} +end +function SetParameters.set_parameter( + T::Type{<:AbstractFill{P1,P}}, ::Position{2}, P2 +) where {P1,P} + return unspecify_parameters(T){P1,P2} +end +function SetParameters.set_parameter( + T::Type{<:AbstractFill{P1,P,P3}}, ::Position{2}, P2 +) where {P1,P,P3} + return unspecify_parameters(T){P1,P2,P3} +end + +# Set parameter 3 +function SetParameters.set_parameter( + T::Type{<:AbstractFill{P1}}, ::Position{3}, P3 +) where {P1} + return unspecify_parameters(T){P1,<:Any,P3} +end +function SetParameters.set_parameter( + T::Type{<:AbstractFill{P1,P2}}, ::Position{3}, P3 +) where {P1,P2} + return unspecify_parameters(T){P1,P2,P3} +end +function SetParameters.set_parameter( + T::Type{<:AbstractFill{P1,P2,P}}, ::Position{3}, P3 +) where {P1,P2,P} + return unspecify_parameters(T){P1,P2,P3} +end + +## default parameters +function SetParameters.default_parameter(::Type{<:AbstractFill}, ::Position{1}) + return UnspecifiedZero +end +SetParameters.default_parameter(::Type{<:AbstractFill}, ::Position{2}) = 0 +SetParameters.default_parameter(::Type{<:AbstractFill}, ::Position{3}) = Tuple{} + +SetParameters.nparameters(::Type{<:AbstractFill}) = Val(3) + +## These helper functions take a UnallocatedArray type and +## remove all the parameters, this way all parameters can be set +## at once in the `set_parameter` functions above. +unspecify_parameters(::Type{<:Fill}) = Fill +unspecify_parameters(::Type{<:Zeros}) = Zeros diff --git a/NDTensors/src/lib/SetParameters/src/SetParameters.jl b/NDTensors/src/lib/SetParameters/src/SetParameters.jl index 66f295d899..723e178bb1 100644 --- a/NDTensors/src/lib/SetParameters/src/SetParameters.jl +++ b/NDTensors/src/lib/SetParameters/src/SetParameters.jl @@ -12,6 +12,15 @@ include("Base/val.jl") include("Base/array.jl") include("Base/subarray.jl") +## TODO when this is a full package utilize this function to +# # enable extensions +# using PackageExtensionCompat +# function __init__() +# @require_extensions +# end + +include("../ext/SetParametersFillArraysExt/SetParametersFillArraysExt.jl") + export DefaultParameter, DefaultParameters, Position, diff --git a/NDTensors/src/lib/TensorAlgebra/test/runtests.jl b/NDTensors/src/lib/TensorAlgebra/test/runtests.jl index 1f643a58ad..5e2cf60a66 100644 --- a/NDTensors/src/lib/TensorAlgebra/test/runtests.jl +++ b/NDTensors/src/lib/TensorAlgebra/test/runtests.jl @@ -163,9 +163,10 @@ end a_dest_tensoroperations = TensorOperations.tensorcontract( labels_dest, a1, labels1, a2, labels2 ) - @test a_dest ≈ α * a_dest_tensoroperations + β * a_dest_init rtol = default_rtol( - elt_dest - ) + ## Here we loosened the tolerance because of some floating point roundoff issue. + ## with Float32 numbers + @test a_dest ≈ α * a_dest_tensoroperations + β * a_dest_init rtol = + 10 * default_rtol(elt_dest) end end @testset "qr (eltype=$elt)" for elt in elts diff --git a/NDTensors/src/lib/UnallocatedArrays/README.md b/NDTensors/src/lib/UnallocatedArrays/README.md new file mode 100644 index 0000000000..f0e962a928 --- /dev/null +++ b/NDTensors/src/lib/UnallocatedArrays/README.md @@ -0,0 +1,4 @@ +# UnallocatedArrays + +A module defining a set of unallocated immutable lazy arrays which will be used to quickly construct +tensors and allocating as little data as possible. diff --git a/NDTensors/src/lib/UnallocatedArrays/src/UnallocatedArrays.jl b/NDTensors/src/lib/UnallocatedArrays/src/UnallocatedArrays.jl new file mode 100644 index 0000000000..fb95f4ec21 --- /dev/null +++ b/NDTensors/src/lib/UnallocatedArrays/src/UnallocatedArrays.jl @@ -0,0 +1,11 @@ +module UnallocatedArrays +include("abstractfill/abstractfill.jl") + +include("unallocatedfill.jl") +include("unallocatedzeros.jl") +include("broadcast.jl") +include("abstractunallocatedarray.jl") +include("set_types.jl") + +export UnallocatedFill, UnallocatedZeros, alloctype, set_alloctype, allocate +end diff --git a/NDTensors/src/lib/UnallocatedArrays/src/abstractfill/abstractfill.jl b/NDTensors/src/lib/UnallocatedArrays/src/abstractfill/abstractfill.jl new file mode 100644 index 0000000000..5b1dd7ae72 --- /dev/null +++ b/NDTensors/src/lib/UnallocatedArrays/src/abstractfill/abstractfill.jl @@ -0,0 +1,17 @@ +using FillArrays: AbstractFill +using NDTensors.SetParameters: Position, get_parameter, set_parameters +## Here are functions specifically defined for UnallocatedArrays +## not implemented by FillArrays +## TODO this might need a more generic name maybe like compute unit +function alloctype(A::AbstractFill) + return A.alloc +end + +## TODO this fails if the parameter is a type +function alloctype(Atype::Type{<:AbstractFill}) + return get_parameter(Atype, Position{4}()) +end + +set_eltype(T::Type{<:AbstractFill}, elt::Type) = set_parameters(T, Position{1}(), elt) +set_ndims(T::Type{<:AbstractFill}, n) = set_parameters(T, Position{2}(), n) +set_axestype(T::Type{<:AbstractFill}, ax::Type) = set_parameters(T, Position{3}(), ax) diff --git a/NDTensors/src/lib/UnallocatedArrays/src/abstractunallocatedarray.jl b/NDTensors/src/lib/UnallocatedArrays/src/abstractunallocatedarray.jl new file mode 100644 index 0000000000..ec887f55ca --- /dev/null +++ b/NDTensors/src/lib/UnallocatedArrays/src/abstractunallocatedarray.jl @@ -0,0 +1,65 @@ +using FillArrays: FillArrays, getindex_value +using NDTensors.SetParameters: set_parameters +using Adapt: adapt + +const UnallocatedArray{ElT,N,AxesT,AllocT} = Union{ + UnallocatedFill{ElT,N,AxesT,AllocT},UnallocatedZeros{ElT,N,AxesT,AllocT} +} + +@inline Base.axes(A::UnallocatedArray) = axes(parent(A)) +Base.size(A::UnallocatedArray) = size(parent(A)) +function FillArrays.getindex_value(A::UnallocatedArray) + return getindex_value(parent(A)) +end + +function Base.complex(A::UnallocatedArray) + return complex(eltype(A)).(A) +end + +function Base.transpose(a::UnallocatedArray) + return set_alloctype(transpose(parent(a)), alloctype(a)) +end + +function Base.adjoint(a::UnallocatedArray) + return set_alloctype(adjoint(parent(a)), alloctype(a)) +end + +function set_alloctype(T::Type{<:UnallocatedArray}, alloc::Type{<:AbstractArray}) + return set_parameters(T, Position{4}(), alloc) +end + +## This overloads the definition defined in `FillArrays.jl` +for STYPE in (:AbstractArray, :AbstractFill) + @eval begin + @inline $STYPE{T}(F::UnallocatedArray{T}) where {T} = F + @inline $STYPE{T,N}(F::UnallocatedArray{T,N}) where {T,N} = F + end +end + +function allocate(f::UnallocatedArray) + a = similar(f) + fill!(a, getindex_value(f)) + return a +end + +function allocate(arraytype::Type{<:AbstractArray}, elt::Type, axes) + ## TODO rewrite this using set_eltype and set_ndims functions + ## currently these functions are defined in `NDTensors` + ## In the future they should be defined in `SetParameters` + ArrayT = set_parameters(arraytype, Position{1}(), elt) + ArrayT = set_parameters(ArrayT, Position{2}(), length(axes)) + return similar(ArrayT, axes) +end + +function Base.similar(f::UnallocatedArray, elt::Type, axes::Tuple{Int64,Vararg{Int64}}) + return allocate(alloctype(f), elt, axes) +end + +## TODO fix this because reshape loses alloctype +#FillArrays.reshape(a::Union{<:UnallocatedFill, <:UnallocatedZeros}, dims) = set_alloctype(reshape(parent(a), dims), allocate(a)) + +# function Adapt.adapt_storage(to::Type{<:AbstractArray}, x::Union{<:UnallocatedFill, <:UnallocatedZeros}) +# return set_alloctype(parent(x), to) +# end + +# function Adapt.adapt_storage(to::Type{<:Number}, x::) diff --git a/NDTensors/src/lib/UnallocatedArrays/src/broadcast.jl b/NDTensors/src/lib/UnallocatedArrays/src/broadcast.jl new file mode 100644 index 0000000000..c5e98e71d4 --- /dev/null +++ b/NDTensors/src/lib/UnallocatedArrays/src/broadcast.jl @@ -0,0 +1,28 @@ +using FillArrays: broadcasted_fill, broadcasted_zeros, getindex_value + +abstract type ZeroPreserving end +struct IsZeroPreserving <: ZeroPreserving end +struct NotZeroPreserving <: ZeroPreserving end + +# Assume operations don't preserve zeros for safety +ZeroPreserving(x) = NotZeroPreserving() +ZeroPreserving(::typeof(Complex)) = IsZeroPreserving() +ZeroPreserving(::typeof(Real)) = IsZeroPreserving() + +function Broadcast.broadcasted(style::Broadcast.DefaultArrayStyle, f, a::UnallocatedZeros) + return _broadcasted(style, f, ZeroPreserving(f), a) +end + +function _broadcasted( + style::Broadcast.DefaultArrayStyle, f, ::IsZeroPreserving, a::UnallocatedZeros +) + z = f.(parent(a)) + return broadcasted_zeros(f, a, eltype(z), axes(z)) +end + +function _broadcasted( + style::Broadcast.DefaultArrayStyle, f, ::NotZeroPreserving, a::UnallocatedZeros +) + z = f.(parent(a)) + return broadcasted_fill(f, a, getindex_value(z), axes(z)) +end diff --git a/NDTensors/src/lib/UnallocatedArrays/src/set_types.jl b/NDTensors/src/lib/UnallocatedArrays/src/set_types.jl new file mode 100644 index 0000000000..d2ec636cb3 --- /dev/null +++ b/NDTensors/src/lib/UnallocatedArrays/src/set_types.jl @@ -0,0 +1,45 @@ +using NDTensors.SetParameters: SetParameters, Position +using NDTensors.UnspecifiedTypes: UnspecifiedArray, UnspecifiedNumber, UnspecifiedZero +# ## TODO make unit tests for all of these functions +## TODO All I need to do is overload AbstractFill functions with 4 parameters +# `SetParameters.jl` overloads. +function SetParameters.get_parameter( + ::Type{<:UnallocatedArray{<:Any,<:Any,<:Any,P4}}, ::Position{4} +) where {P4} + return P4 +end + +# ## Setting paramaters +function SetParameters.set_parameter( + T::Type{<:UnallocatedArray{P,P2,P3,P4}}, ::Position{1}, P1 +) where {P,P2,P3,P4} + return unspecify_parameters(T){P1,P2,P3,P4} +end + +function SetParameters.set_parameter( + T::Type{<:UnallocatedArray{P1,P,P3,P4}}, ::Position{2}, P2 +) where {P1,P,P3,P4} + return unspecify_parameters(T){P1,P2,P3,P4} +end + +function SetParameters.set_parameter( + T::Type{<:UnallocatedArray{P1,P2,P,P4}}, ::Position{3}, P3 +) where {P1,P2,P,P4} + return unspecify_parameters(T){P1,P2,P3,P4} +end + +function SetParameters.set_parameter( + T::Type{<:UnallocatedArray{P1,P2,P3}}, ::Position{4}, P4 +) where {P1,P2,P3} + return unspecify_parameters(T){P1,P2,P3,P4} +end + +# ## default parameters +function SetParameters.default_parameter(::Type{<:UnallocatedArray}, ::Position{4}) + return UnspecifiedArray{UnspecifiedNumber{UnspecifiedZero},0} +end + +SetParameters.nparameters(::Type{<:UnallocatedArray}) = Val(4) + +unspecify_parameters(::Type{<:UnallocatedFill}) = UnallocatedFill +unspecify_parameters(::Type{<:UnallocatedZeros}) = UnallocatedZeros diff --git a/NDTensors/src/lib/UnallocatedArrays/src/unallocatedfill.jl b/NDTensors/src/lib/UnallocatedArrays/src/unallocatedfill.jl new file mode 100644 index 0000000000..08aa7a4bc3 --- /dev/null +++ b/NDTensors/src/lib/UnallocatedArrays/src/unallocatedfill.jl @@ -0,0 +1,68 @@ +using FillArrays: + FillArrays, AbstractFill, Fill, broadcasted_fill, getindex_value, kron_fill, mult_fill +using NDTensors.SetParameters: Position, set_parameters + +struct UnallocatedFill{ElT,N,Axes,Alloc} <: AbstractFill{ElT,N,Axes} + f::Fill{ElT,N,Axes} + alloc::Alloc +end + +function UnallocatedFill{ElT,N,Axes}(f::Fill, alloc::Type) where {ElT,N,Axes} + return new{ElT,N,Axes,Type{alloc}}(f, alloc) +end + +function UnallocatedFill{ElT,N}(f::Fill, alloc) where {ElT,N} + return UnallocatedFill{ElT,N,typeof(axes(f))}(f, alloc) +end + +function UnallocatedFill{ElT}(f::Fill, alloc) where {ElT} + return UnallocatedFill{ElT,ndims(f)}(f, alloc) +end + +set_alloctype(f::Fill, alloc::Type) = UnallocatedFill(f, alloc) + +Base.parent(F::UnallocatedFill) = F.f + +Base.convert(::Type{<:UnallocatedFill}, A::UnallocatedFill) = A + +############################################# +# Arithmatic + +# mult_fill(a, b, val, ax) = Fill(val, ax) +function FillArrays.mult_fill(a::UnallocatedFill, b, val, ax) + return UnallocatedFill(Fill(val, ax), alloctype(a)) +end +FillArrays.mult_fill(a, b::UnallocatedFill, val, ax) = mult_fill(b, a, val, ax) +function FillArrays.mult_fill(a::UnallocatedFill, b::UnallocatedFill, val, ax) + @assert alloctype(a) == alloctype(b) + return UnallocatedFill(Fill(val, ax), alloctype(a)) +end + +function FillArrays.broadcasted_fill(f, a::UnallocatedFill, val, ax) + return UnallocatedFill(Fill(val, ax), alloctype(a)) +end +function FillArrays.broadcasted_fill(f, a::UnallocatedFill, b::UnallocatedFill, val, ax) + @assert alloctype(a) == alloctype(b) + return UnallocatedFill(Fill(val, ax), alloctype(a)) +end + +function FillArrays.broadcasted_fill(f, a::UnallocatedFill, b, val, ax) + return UnallocatedFill(Fill(val, ax), alloctype(a)) +end +function FillArrays.broadcasted_fill(f, a, b::UnallocatedFill, val, ax) + return broadcasted_fill(f, b, a, val, ax) +end + +function FillArrays.kron_fill(a::UnallocatedFill, b::UnallocatedFill, val, ax) + @assert alloctype(a) == alloctype(b) + return UnallocatedFill(Fill(val, ax), alloctype(a)) +end + +Base.:+(A::UnallocatedFill, B::UnallocatedFill) = A .+ B + +function Base.Broadcast.broadcasted( + ::Base.Broadcast.DefaultArrayStyle, op, r::UnallocatedFill +) + f = op.(parent(r)) + return broadcasted_fill(op, r, getindex_value(f), axes(f)) +end diff --git a/NDTensors/src/lib/UnallocatedArrays/src/unallocatedzeros.jl b/NDTensors/src/lib/UnallocatedArrays/src/unallocatedzeros.jl new file mode 100644 index 0000000000..66f9876e19 --- /dev/null +++ b/NDTensors/src/lib/UnallocatedArrays/src/unallocatedzeros.jl @@ -0,0 +1,88 @@ +using FillArrays: + FillArrays, + AbstractZeros, + Fill, + Zeros, + broadcasted_fill, + broadcasted_zeros, + kron_fill, + kron_zeros, + mult_zeros +using NDTensors.SetParameters: Position, set_parameters +## TODO Should Alloc also be of ElT and N or should there be +## More freedom there? +struct UnallocatedZeros{ElT,N,Axes,Alloc} <: AbstractZeros{ElT,N,Axes} + z::Zeros{ElT,N,Axes} + alloc::Alloc +end + +function UnallocatedZeros{ElT,N,Axes}(z::Zeros, alloc::Type) where {ElT,N,Axes} + return new{ElT,N,Axes,Type{alloc}}(z, alloc) +end + +function UnallocatedZeros{ElT,N}(z::Zeros, alloc) where {ElT,N} + return UnallocatedZeros{ElT,N,typeof(axes(z))}(z, alloc) +end + +function UnallocatedZeros{ElT}(z::Zeros, alloc) where {ElT} + return UnallocatedZeros{ElT,ndims(z)}(z, alloc) +end + +set_alloctype(f::Zeros, alloc::Type) = UnallocatedZeros(f, alloc) + +Base.parent(Z::UnallocatedZeros) = Z.z + +Base.convert(::Type{<:UnallocatedZeros}, A::UnallocatedZeros) = A + +############################################# +# Arithmatic + +function FillArrays.mult_zeros(a::UnallocatedZeros, b, elt, ax) + return UnallocatedZeros(Zeros{elt}(ax), alloctype(a)) +end +FillArrays.mult_zeros(a, b::UnallocatedZeros, elt, ax) = mult_zeros(b, a, elt, ax) +function FillArrays.mult_zeros(a::UnallocatedZeros, b::UnallocatedZeros, elt, ax) + @assert alloctype(a) == alloctype(b) + return UnallocatedZeros(Zeros{elt}(ax), alloctype(a)) +end + +function FillArrays.broadcasted_zeros(f, a::UnallocatedZeros, elt, ax) + return UnallocatedZeros(Zeros{elt}(ax), alloctype(a)) +end +function FillArrays.broadcasted_zeros(f, a::UnallocatedZeros, b::UnallocatedZeros, elt, ax) + @assert alloctype(a) == alloctype(b) + return UnallocatedZeros(Zeros{elt}(ax), alloctype(a)) +end + +function FillArrays.broadcasted_zeros(f, a::UnallocatedZeros, b, elt, ax) + return UnallocatedZeros(Zeros{elt}(ax), alloctype(a)) +end +function FillArrays.broadcasted_zeros(f, a, b::UnallocatedZeros, elt, ax) + return broadcasted_zeros(f, b, a, elt, ax) +end + +function FillArrays.broadcasted_fill(f, a::UnallocatedZeros, val, ax) + return UnallocatedFill(Fill(val, ax), alloctype(a)) +end +function FillArrays.broadcasted_fill(f, a::UnallocatedZeros, b, val, ax) + return UnallocatedFill(Fill(val, ax), alloctype(a)) +end + +function FillArrays.broadcasted_fill(f, a, b::UnallocatedZeros, val, ax) + return broadcasted_fill(f, b, a, val, ax) +end + +function FillArrays.kron_zeros(a::UnallocatedZeros, b::UnallocatedZeros, elt, ax) + @assert alloctype(a) == alloctype(b) + return UnallocatedZeros(Zeros{elt}(ax), alloctype(a)) +end + +function FillArrays.kron_fill(a::UnallocatedZeros, b::UnallocatedFill, val, ax) + @assert alloctype(a) == alloctype(b) + elt = typeof(val) + return UnallocatedZeros(Zeros{elt}(ax), alloctype(a)) +end + +function FillArrays.kron_fill(a::UnallocatedFill, b::UnallocatedZeros, val, ax) + return kron_fill(b, a, val, ax) +end diff --git a/NDTensors/src/lib/UnallocatedArrays/test/runtests.jl b/NDTensors/src/lib/UnallocatedArrays/test/runtests.jl new file mode 100644 index 0000000000..131d509ef6 --- /dev/null +++ b/NDTensors/src/lib/UnallocatedArrays/test/runtests.jl @@ -0,0 +1,320 @@ +@eval module $(gensym()) +using FillArrays: FillArrays, AbstractFill, Fill, Zeros +using NDTensors: NDTensors +using NDTensors.UnallocatedArrays +using LinearAlgebra: norm +using Test: @test, @testset, @test_broken + +include(joinpath(pkgdir(NDTensors), "test", "NDTensorsTestUtils", "NDTensorsTestUtils.jl")) +using .NDTensorsTestUtils: devices_list + +@testset "Testing UnallocatedArrays on $dev with eltype $elt" for dev in devices_list(ARGS), + elt in (Float64, Float32, ComplexF64, ComplexF32) + + @testset "Basic funcitonality" begin + z = Zeros{elt}((2, 3)) + Z = UnallocatedZeros(z, dev(Matrix{elt})) + + @test Z isa AbstractFill + @test size(Z) == (2, 3) + @test length(Z) == 6 + @test iszero(sum(Z)) + @test iszero(norm(Z)) + @test iszero(Z[2, 3]) + @test allocate(Z) isa dev(Matrix{elt}) + Zp = set_alloctype(z, dev(Matrix{elt})) + @test Zp == Z + Zc = copy(Z) + @test Zc == Z + Zc = complex(Z) + @test eltype(Zc) == complex(eltype(z)) + @test iszero(Zc[1, 2]) + @test Zc isa UnallocatedZeros + @test alloctype(Zc) == alloctype(Z) + + Zs = similar(Z) + @test Zs isa alloctype(Z) + + Z = UnallocatedZeros(z, dev(Array)) + Za = allocate(Z) + @test Za isa dev(Array{elt,2}) + @test Za[1, 3] == zero(elt) + + ######################################### + # UnallocatedFill + f = Fill{elt}(3, (2, 3, 4)) + F = UnallocatedFill(f, Array{elt,ndims(f)}) + @test F isa AbstractFill + @test size(F) == (2, 3, 4) + @test length(F) == 24 + @test sum(F) ≈ elt(3) * 24 + @test norm(F) ≈ sqrt(elt(3)^2 * 24) + @test F[2, 3, 1] == elt(3) + @test allocate(F) isa Array{elt,3} + Fp = allocate(F) + @test norm(Fp) ≈ norm(F) + Fs = similar(F) + @test Fs isa alloctype(F) + @test length(Fs) == 2 * 3 * 4 + Fs[1, 1, 1] = elt(10) + @test Fs[1, 1, 1] == elt(10) + + Fp = set_alloctype(f, dev(Array{elt,ndims(f)})) + @test allocate(Fp) isa dev(Array{elt,ndims(f)}) + @test Fp == F + Fc = copy(F) + @test Fc == F + Fc = allocate(complex(F)) + @test eltype(Fc) == complex(eltype(F)) + ## Here we no longer require the eltype of the alloctype to + ## Be the same as the eltype of the `UnallocatedArray`. It will be + ## replaced when the array is allocated + # @test_broken typeof(Fc) == alloctype(complex(F)) + Fc[2, 3, 4] = elt(0) + @test iszero(Fc[2, 3, 4]) + + F = UnallocatedFill(f, dev(Array)) + Fa = allocate(F) + @test Fa isa dev(Array{elt,3}) + @test Fa[2, 1, 4] == elt(3) + + F = UnallocatedFill(f, dev(Vector)) + Fa = allocate(F) + @test ndims(Fa) == 3 + @test Fa isa dev(Array) + end + + @testset "Multiplication" begin + z = Zeros{elt}((2, 3)) + Z = UnallocatedZeros(z, dev(Matrix{elt})) + + R = Z * Z' + @test R isa UnallocatedZeros + @test alloctype(R) == alloctype(Z) + @test size(R) == (2, 2) + M = rand(elt, (3, 4)) + R = Z * M + @test R isa UnallocatedZeros + @test alloctype(R) == alloctype(Z) + @test size(R) == (2, 4) + R = M' * Z' + @test R isa UnallocatedZeros + @test alloctype(R) == alloctype(Z) + @test size(R) == (4, 2) + R = transpose(M) * transpose(Z) + @test R isa UnallocatedZeros + @test alloctype(R) == alloctype(Z) + @test size(R) == (4, 2) + + ################################### + ## UnallocatedFill + f = Fill{elt}(3, (2, 12)) + F = UnallocatedFill(f, dev(Matrix{elt})) + p = Fill{elt}(4, (12, 5)) + P = UnallocatedFill(p, dev(Array{elt,ndims(p)})) + R = F * P + @test F isa UnallocatedFill + @test R[1, 1] == elt(144) + @test alloctype(R) == alloctype(F) + @test size(R) == (2, 5) + + R = F * F' + @test R isa UnallocatedFill + @test R[1, 2] == elt(108) + @test alloctype(R) == alloctype(F) + @test size(R) == (2, 2) + + R = transpose(F) * F + @test R isa UnallocatedFill + @test R[12, 3] == elt(18) + @test alloctype(R) == alloctype(F) + @test size(R) == (12, 12) + + R = transpose(Z) * F + @test R isa UnallocatedZeros + @test alloctype(R) == alloctype(Z) + @test size(R) == (3, 12) + end + + @testset "Broadcast" begin + z = Zeros{elt}((2, 3)) + Z = UnallocatedZeros(z, dev(Matrix{elt})) + R = elt(2) .* Z + @test R isa UnallocatedZeros + @test alloctype(R) == alloctype(Z) + R = Z .* elt(2) + @test R isa UnallocatedZeros + @test alloctype(R) == alloctype(Z) + + R = Z .* Z + @test R isa UnallocatedZeros + @test alloctype(R) == alloctype(Z) + + Z = UnallocatedZeros(Zeros{elt}((2, 3)), dev(Matrix{elt})) + R = Z + Z + @test R isa UnallocatedZeros + @test alloctype(R) == alloctype(Z) + + R = Z .+ elt(2) + @test R isa UnallocatedFill + @test alloctype(R) == alloctype(Z) + + R = (x -> x .+ 1).(Z) + @test R isa UnallocatedFill + @test alloctype(R) == alloctype(Z) + @test R[1, 1] == elt(1) + + Z .*= 1.0 + @test Z isa UnallocatedZeros + @test alloctype(R) == dev(Matrix{elt}) + @test Z[2, 1] == zero(elt) + ######################## + # UnallocatedFill + f = Fill(elt(3), (2, 3, 4)) + F = UnallocatedFill(f, Array{elt,ndims(f)}) + F2 = F .* 2 + @test F2 isa UnallocatedFill + @test F2[1, 1, 1] == elt(6) + @test alloctype(F2) == alloctype(F) + + F2 = F2 .+ elt(2) + @test F2 isa UnallocatedFill + @test F2[1, 1, 1] == elt(8) + @test alloctype(F2) == alloctype(F) + + F = UnallocatedFill(Fill(elt(2), (2, 3)), dev(Matrix{elt})) + R = Z + F + @test R isa UnallocatedFill + @test alloctype(R) == alloctype(Z) + + R = F + Z + @test R isa UnallocatedFill + @test alloctype(R) == alloctype(Z) + + F = UnallocatedFill(Fill(elt(3), (2, 12)), dev(Matrix{elt})) + R = F .* F + @test R isa UnallocatedFill + @test R[2, 9] == elt(9) + @test alloctype(R) == alloctype(F) + @test size(R) == (2, 12) + + P = UnallocatedFill(Fill(elt(4), (2, 3)), dev(Matrix{elt})) + R = Z .* P + @test R isa UnallocatedZeros + @test alloctype(R) == alloctype(P) + @test size(R) == (2, 3) + + F = UnallocatedFill(Fill(elt(2), (2, 3)), dev(Matrix{elt})) + R = F + F + @test R isa UnallocatedFill + @test R[1, 3] == elt(4) + + R = (x -> x .+ 1).(F) + @test R isa UnallocatedFill + @test R[2, 1] == elt(3) + @test alloctype(R) == alloctype(F) + end + + ## TODO make other kron tests + @testset "Kron" begin + A = UnallocatedZeros(Zeros{elt}(2), dev(Vector{elt})) + B = UnallocatedZeros(Zeros{elt}(2), dev(Vector{elt})) + C = kron(A, B) + @test C isa UnallocatedZeros + @test alloctype(C) == alloctype(B) + + B = UnallocatedFill(Fill(elt(2), (2)), dev(Vector{elt})) + C = kron(A, B) + @test C isa UnallocatedZeros + @test alloctype(C) == alloctype(B) + + C = kron(B, A) + @test C isa UnallocatedZeros + @test alloctype(C) == alloctype(B) + + A = UnallocatedFill(Fill(elt(3), (2)), dev(Vector{elt})) + C = kron(A, B) + @test C isa UnallocatedFill + @test alloctype(C) == alloctype(B) + @test C[1] == elt(6) + end +end +end + +using FillArrays: Fill, Zeros +using NDTensors.UnallocatedArrays +using NDTensors.SetParameters: + Position, default_parameter, nparameters, get_parameter, set_parameters +using Test: @test, @testset + +@testset "SetParameters" begin + @testset "Tetsing $typ" for (typ) in (:Fill, :Zeros) + @eval begin + t1 = default_parameter($typ, Position{1}()) + t2 = default_parameter($typ, Position{2}()) + t3 = default_parameter($typ, Position{3}()) + t4 = Any + ft1 = $typ{t1} + ft2 = $typ{t1,t2} + ft3 = $typ{t1,t2,t3} + + ## check 1 parameter specified + ftn1 = set_parameters(ft1, Position{1}(), t4) + ftn2 = set_parameters(ft1, Position{2}(), t4) + ftn3 = set_parameters(ft1, Position{3}(), t4) + @test ftn1 == $typ{t4} + @test ftn2 == $typ{t1,t4} + @test ftn3 == $typ{t1,<:Any,t4} + + ## check 2 parameters specified + ftn1 = set_parameters(ft2, Position{1}(), t4) + ftn2 = set_parameters(ft2, Position{2}(), t4) + ftn3 = set_parameters(ft2, Position{3}(), t4) + @test ftn1 == $typ{t4,t2} + @test ftn2 == $typ{t1,t4} + @test ftn3 == $typ{t1,t2,t4} + + ## check 3 parameters specified + ftn1 = set_parameters(ft3, Position{1}(), t4) + ftn2 = set_parameters(ft3, Position{2}(), t4) + ftn3 = set_parameters(ft3, Position{3}(), t4) + @test ftn1 == $typ{t4,t2,t3} + @test ftn2 == $typ{t1,t4,t3} + @test ftn3 == $typ{t1,t2,t4} + + @test get_parameter(ft3, Position{1}()) == t1 + @test get_parameter(ft3, Position{2}()) == t2 + @test get_parameter(ft3, Position{3}()) == t3 + + @test nparameters(ft3) == Val(3) + end + end + + @testset "Tetsing $typ" for (typ) in (:UnallocatedFill, :UnallocatedZeros) + @eval begin + t1 = default_parameter($typ, Position{1}()) + t2 = default_parameter($typ, Position{2}()) + t3 = default_parameter($typ, Position{3}()) + t4 = default_parameter($typ, Position{4}()) + t5 = Any + ft = $typ{t1,t2,t3,t4} + + ## check 4 parameters specified + ftn1 = set_parameters(ft, Position{1}(), t5) + ftn2 = set_parameters(ft, Position{2}(), t5) + ftn3 = set_parameters(ft, Position{3}(), t5) + ftn4 = set_parameters(ft, Position{4}(), t5) + @test ftn1 == $typ{t5,t2,t3,t4} + @test ftn2 == $typ{t1,t5,t3,t4} + @test ftn3 == $typ{t1,t2,t5,t4} + @test ftn4 == $typ{t1,t2,t3,t5} + + @test get_parameter(ft, Position{1}()) == t1 + @test get_parameter(ft, Position{2}()) == t2 + @test get_parameter(ft, Position{3}()) == t3 + @test get_parameter(ft, Position{4}()) == t4 + + @test nparameters(ft) == Val(4) + end + end +end diff --git a/NDTensors/src/lib/UnspecifiedTypes/README.md b/NDTensors/src/lib/UnspecifiedTypes/README.md new file mode 100644 index 0000000000..15ad85367d --- /dev/null +++ b/NDTensors/src/lib/UnspecifiedTypes/README.md @@ -0,0 +1,3 @@ +# UnspecifiedTypes + +A module defining a set of basic types which are place holders for allocated bit-wise representable types. diff --git a/NDTensors/src/lib/UnspecifiedTypes/TODO.md b/NDTensors/src/lib/UnspecifiedTypes/TODO.md new file mode 100644 index 0000000000..f836b420e3 --- /dev/null +++ b/NDTensors/src/lib/UnspecifiedTypes/TODO.md @@ -0,0 +1,6 @@ +## TODO +Currently this code is relatively unimplemented things we need to do are +1. Fully implement all of the types +2. Make sure these types work properly with infrastructure like `UnallocatedArrays`... +3. Make sure type promotion works as expected for each `UnallocatedType` +4. Make a test suite for the module \ No newline at end of file diff --git a/NDTensors/src/lib/UnspecifiedTypes/src/UnspecifiedTypes.jl b/NDTensors/src/lib/UnspecifiedTypes/src/UnspecifiedTypes.jl new file mode 100644 index 0000000000..f94a63a14c --- /dev/null +++ b/NDTensors/src/lib/UnspecifiedTypes/src/UnspecifiedTypes.jl @@ -0,0 +1,11 @@ +module UnspecifiedTypes + +using LinearAlgebra + +include("unspecifiednumber.jl") +include("unspecifiedzero.jl") + +include("unspecifiedarray.jl") + +export UnspecifiedArray, UnspecifiedNumber, UnspecifiedZero +end diff --git a/NDTensors/src/lib/UnspecifiedTypes/src/unspecifiedarray.jl b/NDTensors/src/lib/UnspecifiedTypes/src/unspecifiedarray.jl new file mode 100644 index 0000000000..e47e9832e5 --- /dev/null +++ b/NDTensors/src/lib/UnspecifiedTypes/src/unspecifiedarray.jl @@ -0,0 +1 @@ +struct UnspecifiedArray{ElT,N} <: AbstractArray{ElT,N} end diff --git a/NDTensors/src/lib/UnspecifiedTypes/src/unspecifiednumber.jl b/NDTensors/src/lib/UnspecifiedTypes/src/unspecifiednumber.jl new file mode 100644 index 0000000000..c44587b376 --- /dev/null +++ b/NDTensors/src/lib/UnspecifiedTypes/src/unspecifiednumber.jl @@ -0,0 +1,5 @@ +abstract type AbstractUnspecifiedNumber <: Number end + +struct UnspecifiedNumber{T} <: AbstractUnspecifiedNumber + value::T +end diff --git a/NDTensors/src/lib/UnspecifiedTypes/src/unspecifiedzero.jl b/NDTensors/src/lib/UnspecifiedTypes/src/unspecifiedzero.jl new file mode 100644 index 0000000000..4ca1858218 --- /dev/null +++ b/NDTensors/src/lib/UnspecifiedTypes/src/unspecifiedzero.jl @@ -0,0 +1,39 @@ +struct UnspecifiedZero <: AbstractUnspecifiedNumber end + +# Base.Complex{UnspecifiedZero}() = complex(UnspecifiedZero()) +# function Base.Complex{UnspecifiedZero}(z::Real) +# return (iszero(z) ? complex(UnspecifiedZero()) : throw(ErrorException)) +# end + +zero(::Type{UnspecifiedZero}) = UnspecifiedZero() +zero(n::UnspecifiedZero) = zero(typeof(n)) + +# This helps handle a lot of basic algebra, like: +# UnspecifiedZero() + 2.3 == 2.3 +convert(::Type{T}, x::UnspecifiedZero) where {T<:Number} = T(zero(T)) + +#convert(::Type{Complex{UnspecifiedZero}}, x::UnspecifiedZero) = complex(x) + +# TODO: Should this be implemented? +#Complex(x::Real, ::UnspecifiedZero) = x + +# This is to help define `float(::UnspecifiedZero) = 0.0`. +# This helps with defining `norm` of `UnallocatedZeros{UnspecifiedZero}`. +AbstractFloat(::UnspecifiedZero) = zero(AbstractFloat) + +# Basic arithmetic +(::UnspecifiedZero + ::UnspecifiedZero) = UnspecifiedZero() +(::UnspecifiedZero - ::UnspecifiedZero) = UnspecifiedZero() +(::Number * ::UnspecifiedZero) = UnspecifiedZero() +(::UnspecifiedZero * ::Number) = UnspecifiedZero() +(::UnspecifiedZero * ::UnspecifiedZero) = UnspecifiedZero() +(::UnspecifiedZero / ::Number) = UnspecifiedZero() +(::Number / ::UnspecifiedZero) = throw(DivideError()) +(::UnspecifiedZero / ::UnspecifiedZero) = throw(DivideError()) +-(::UnspecifiedZero) = UnspecifiedZero() + +Base.promote_type(z::Type{<:UnspecifiedZero}, ElT::Type) = Base.promote_type(ElT, z) + +Base.promote_type(ElT::Type, ::Type{<:UnspecifiedZero}) = ElT +Base.promote_type(::Type{<:UnspecifiedZero}, ::Type{<:UnspecifiedZero}) = UnspecifiedZero +Base.promote_type(ElT::Type, ::Type{<:Complex{<:UnspecifiedZero}}) = Complex{real(ElT)} diff --git a/NDTensors/src/lib/UnspecifiedTypes/test/runtests.jl b/NDTensors/src/lib/UnspecifiedTypes/test/runtests.jl new file mode 100644 index 0000000000..e9649c3c50 --- /dev/null +++ b/NDTensors/src/lib/UnspecifiedTypes/test/runtests.jl @@ -0,0 +1,6 @@ +@eval module $(gensym()) +using NDTensors.UnspecifiedTypes +using Test: @test, @testset + +@testset "Testing UnspecifiedTypes" begin end +end diff --git a/NDTensors/test/Project.toml b/NDTensors/test/Project.toml index edfcea72ee..0655c1519b 100644 --- a/NDTensors/test/Project.toml +++ b/NDTensors/test/Project.toml @@ -8,6 +8,7 @@ Dictionaries = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4" EllipsisNotation = "da5c29d0-fa7d-589e-88eb-ea29b0a81949" GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" ITensors = "9136182c-28ba-11e9-034c-db9fb085ebd5" +FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" NDTensors = "23ae76d9-e61a-49c4-8f12-3f1a16adf9cf" Octavian = "6fd5a793-0b7e-452c-907f-f8bfe9c57db4" diff --git a/NDTensors/test/lib/Project.toml b/NDTensors/test/lib/Project.toml index b86a01c764..41bb43fa99 100644 --- a/NDTensors/test/lib/Project.toml +++ b/NDTensors/test/lib/Project.toml @@ -4,6 +4,7 @@ BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" Compat = "34da2185-b29b-5c13-b0c7-acf172513d20" GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" +FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" NDTensors = "23ae76d9-e61a-49c4-8f12-3f1a16adf9cf" SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" TensorOperations = "6aa20fa7-93e2-5fca-9bc0-fbd0db3c71a2" diff --git a/NDTensors/test/lib/runtests.jl b/NDTensors/test/lib/runtests.jl index d01ccfe126..14c42ee3fc 100644 --- a/NDTensors/test/lib/runtests.jl +++ b/NDTensors/test/lib/runtests.jl @@ -15,6 +15,8 @@ using Test: @testset "SparseArrayDOKs", "TagSets", "TensorAlgebra", + "UnallocatedArrays", + "UnspecifiedTypes", "Unwrap", ] using NDTensors: NDTensors diff --git a/jenkins/Jenkinsfile b/jenkins/Jenkinsfile index fb14fca47f..cc9f211f88 100644 --- a/jenkins/Jenkinsfile +++ b/jenkins/Jenkinsfile @@ -34,7 +34,7 @@ pipeline { ''' } } - stage('julia-1.9') { + stage('julia-1.10') { options { timeout(time: 45, unit: 'MINUTES') } @@ -43,7 +43,7 @@ pipeline { label 'gpu&&v100' filename 'Dockerfile' dir 'jenkins' - additionalBuildArgs '--build-arg JULIA=1.9' + additionalBuildArgs '--build-arg JULIA=1.10' args '--gpus "device=1"' } }