Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jobs:
uses: "QuantumKitHub/QuantumKitHubActions/.github/workflows/TestGroups.yml@main"
with:
fast: ${{ github.event.pull_request.draft == true }}
exclude: '["testsuite"]'
nthreads: 4
timeout-minutes: 90
# Enzyme plays a large role here, so also test macOS and Windows on PRs, once ubuntu passes
Expand Down
36 changes: 3 additions & 33 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,56 +42,26 @@ TensorOperationsJLArraysExt = "JLArrays"

[compat]
AMDGPU = "2"
Aqua = "0.6, 0.7, 0.8"
Adapt = "4"
Bumper = "0.6, 0.7"
CUDACore = "6"
ChainRulesCore = "1"
ChainRulesTestUtils = "1"
DynamicPolynomials = "0.5, 0.6"
Enzyme = "0.13.183"
EnzymeTestUtils = "0.2"
GPUArrays = "11"
JLArrays = "0.3"
LRUCache = "1"
LinearAlgebra = "1.6"
Logging = "1.6"
Mooncake = "0.5"
PackageExtensionCompat = "1"
ParallelTestRunner = "2"
PrecompileTools = "1.1"
Preferences = "1.4"
PtrArrays = "1.2"
Random = "1"
Strided = "2.6"
Strided = "2.6.7"
StridedViews = "0.5"
TBLIS = "0.3"
Test = "1"
TupleTools = "1.6"
VectorInterface = "0.4.1, 0.5, 0.6, 0.7"
cuRAND = "6"
cuTENSOR = "6"
julia = "1.10"

[extras]
Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
Bumper = "8ce10254-0962-460f-a3d8-1f77fea1446e"
CUDACore = "bd0ed864-bdfe-4181-a5ed-ce625a5fdea2"
ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
ChainRulesTestUtils = "cdddcdb0-9152-4a09-a978-84456f9df70a"
cuRAND = "20fd9a0b-12d5-4c2f-a8af-7c34e9e60431"
DynamicPolynomials = "7c1d4256-1411-5781-91ec-d7bc3513ac07"
Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9"
EnzymeTestUtils = "12d8515a-0907-448a-8884-5fe00fdf1c5a"
JLArrays = "27aeb0d3-9eb9-45fb-866b-73c2ecf80fcb"
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6"
ParallelTestRunner = "d3525ed8-44d0-4b2c-a655-542cee43accc"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
TBLIS = "48530278-0828-4a49-9772-0f3830dfa1e9"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
cuTENSOR = "011b41b2-24ef-40a8-b3eb-fa098493e9e1"

[targets]
test = ["Test", "Random", "DynamicPolynomials", "ChainRulesTestUtils", "ChainRulesCore", "cuRAND", "CUDACore", "cuTENSOR", "Aqua", "Logging", "Bumper", "Mooncake", "Enzyme", "EnzymeTestUtils", "Adapt", "JLArrays", "AMDGPU", "TBLIS", "ParallelTestRunner"]
[workspace]
projects = ["test"]
2 changes: 2 additions & 0 deletions ext/TensorOperationsTBLISExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ function TO.tensortrace!(
throw(ArgumentError("output tensor must not be aliased with input tensor"))

T = eltype(C)
# TBLIS does not handle traces over dimensions of size zero, which only scale `C`
any(i -> iszero(size(A, i)), q[1]) && return TO._scale!(C, β)
einA, einC = trace_labels(p, q)
Av, Cv = SV(A), SV(C)
lenA, strideA = tblis_dims(Av)
Expand Down
26 changes: 21 additions & 5 deletions ext/TensorOperationscuTENSORExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,17 @@ end
#-------------------------------------------------------------------------------------------
# A Base wrapper over `CuArray` will first pass via the `select_backend` methods for
# `AbstractArray` and be converted into a `StridedView` if it satisfies `isstrided`. Hence,
# we only need to capture `CuStridedView` here.
function TO.select_backend(::typeof(TO.tensoradd!), ::CuStridedView, ::CuStridedView)
# we only need to capture `CuStridedView` here. cuTENSOR requires all arguments to have the
# same element type; other combinations fall back to the `StridedView` methods.
function TO.select_backend(::typeof(TO.tensoradd!), ::CuStridedView{T}, ::CuStridedView{T}) where {T}
return cuTENSORBackend()
end
function TO.select_backend(::typeof(TO.tensortrace!), ::CuStridedView, ::CuStridedView)
function TO.select_backend(::typeof(TO.tensortrace!), ::CuStridedView{T}, ::CuStridedView{T}) where {T}
return cuTENSORBackend()
end
function TO.select_backend(
::typeof(TO.tensorcontract!), ::CuStridedView, ::CuStridedView, ::CuStridedView
)
::typeof(TO.tensorcontract!), ::CuStridedView{T}, ::CuStridedView{T}, ::CuStridedView{T}
) where {T}
return cuTENSORBackend()
end

Expand All @@ -83,6 +84,9 @@ function TO.tensoradd!(
α::Number, β::Number,
backend::cuTENSORBackend, allocator = CUDAAllocator()
)
TO.argcheck_tensoradd(C, A, pA)
TO.dimcheck_tensoradd(C, A, pA)
isempty(C) && return C
C_cuda, isview = _custrided(C, allocator)
A_cuda, = _custrided(A, allocator)
tensoradd!(C_cuda, A_cuda, pA, conjA, α, β, backend, allocator)
Expand All @@ -97,6 +101,11 @@ function TO.tensorcontract!(
α::Number, β::Number,
backend::cuTENSORBackend, allocator = CUDAAllocator()
)
TO.argcheck_tensorcontract(C, A, pA, B, pB, pAB)
TO.dimcheck_tensorcontract(C, A, pA, B, pB, pAB)
isempty(C) && return C
# cuTENSOR does not accept dimensions of size zero; contracting over them only scales `C`
any(i -> iszero(size(A, i)), pA[2]) && return TO._scale!(C, β)
C_cuda, isview = _custrided(C, allocator)
A_cuda, = _custrided(A, allocator)
B_cuda, = _custrided(B, allocator)
Expand All @@ -112,6 +121,13 @@ function TO.tensortrace!(
α::Number, β::Number,
backend::cuTENSORBackend, allocator = CUDAAllocator()
)
TO.argcheck_tensortrace(C, A, p, q)
TO.dimcheck_tensortrace(C, A, p, q)
isempty(C) && return C
# cuTENSOR does not accept dimensions of size zero; tracing over them only scales `C`
any(i -> iszero(size(A, i)), q[1]) && return TO._scale!(C, β)
# a trace without traced indices is a permutation, which cuTENSOR cannot plan as a reduction
isempty(q[1]) && return tensoradd!(C, A, p, conjA, α, β, backend, allocator)
C_cuda, isview = _custrided(C, allocator)
A_cuda, = _custrided(A, allocator)
tensortrace!(C_cuda, A_cuda, p, q, conjA, α, β, backend, allocator)
Expand Down
4 changes: 4 additions & 0 deletions src/implementation/base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ function tensortrace!(
szA = size(A)
so = TupleTools.getindices(szA, linearize(p))
st = prod(TupleTools.getindices(szA, q[1]))
# traced dimensions of size zero: there is nothing to add to `β * C`
iszero(st) && return _scale!(C, β)
à = reshape(
PermutedDimsArray(A, (linearize(p)..., linearize(q)...)), (prod(so), st, st)
)
Expand Down Expand Up @@ -141,6 +143,8 @@ function tensortrace!(
szA = size(A)
so = TupleTools.getindices(szA, linearize(p))
st = prod(TupleTools.getindices(szA, q[1]))
# traced dimensions of size zero: there is nothing to add to `β * C`
iszero(st) && return _scale!(C, β)
perm = (linearize(p)..., linearize(q)...)
Atemp = tensoralloc_add(eltype(A), A, (perm, ()), conjA, Val(true), allocator)
à = reshape(permutedims!(Atemp, A, perm), (prod(so), st, st))
Expand Down
3 changes: 3 additions & 0 deletions src/implementation/strided.jl
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ function tensorcontract!(
)
@nospecialize backend allocator

(Base.mightalias(C, A) || Base.mightalias(C, B)) &&
throw(ArgumentError("output tensor must not be aliased with input tensor"))

# standardize input types for compilation time
α′ = standardize_scalartype(C, α)
β′ = standardize_scalartype(C, β)
Expand Down
4 changes: 4 additions & 0 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ function _kron(Es::NTuple{N, Any}, ba) where {N}
)
return tensorproduct(E1, ((1, 2), ()), false, E2, p2, false, p, One(), ba...)
end

# `C = β * C`, for operations without anything to add to `C`, such as traces or contractions
# over dimensions of size zero; `β = 0` overwrites `C`, which may contain `NaN`s
_scale!(C::AbstractArray, β::Number) = iszero(β) ? fill!(C, zero(eltype(C))) : (C .= β .* C)
57 changes: 57 additions & 0 deletions test/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name = "TensorOperationsTests"

[deps]
AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e"
Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
Bumper = "8ce10254-0962-460f-a3d8-1f77fea1446e"
CUDACore = "bd0ed864-bdfe-4181-a5ed-ce625a5fdea2"
ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
ChainRulesTestUtils = "cdddcdb0-9152-4a09-a978-84456f9df70a"
DynamicPolynomials = "7c1d4256-1411-5781-91ec-d7bc3513ac07"
Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9"
EnzymeTestUtils = "12d8515a-0907-448a-8884-5fe00fdf1c5a"
JLArrays = "27aeb0d3-9eb9-45fb-866b-73c2ecf80fcb"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6"
ParallelTestRunner = "d3525ed8-44d0-4b2c-a655-542cee43accc"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Strided = "5e0ebb24-38b0-5f93-81fe-25c709ecae67"
TBLIS = "48530278-0828-4a49-9772-0f3830dfa1e9"
TensorOperations = "6aa20fa7-93e2-5fca-9bc0-fbd0db3c71a2"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
TupleTools = "9d95972d-f1c8-5527-a6e0-b4b365fa01f6"
VectorInterface = "409d34a3-91d5-4945-b6ec-7529ddf182d8"
cuBLAS = "182d3088-87b7-4494-8cad-fc6afaa545bc"
cuRAND = "20fd9a0b-12d5-4c2f-a8af-7c34e9e60431"
cuTENSOR = "011b41b2-24ef-40a8-b3eb-fa098493e9e1"

[sources]
TensorOperations = {path = ".."}

[compat]
AMDGPU = "2"
Adapt = "4"
Aqua = "0.6, 0.7, 0.8"
Bumper = "0.6, 0.7"
CUDACore = "6"
ChainRulesCore = "1"
ChainRulesTestUtils = "1"
DynamicPolynomials = "0.5, 0.6"
Enzyme = "0.13.183"
EnzymeTestUtils = "0.2"
JLArrays = "0.3"
LinearAlgebra = "1.6"
Logging = "1.6"
Mooncake = "0.5"
ParallelTestRunner = "2"
Random = "1"
Strided = "2.6.7"
TBLIS = "0.3"
Test = "1"
TupleTools = "1.6"
VectorInterface = "0.4.1, 0.5, 0.6, 0.7"
cuBLAS = "6"
cuRAND = "6"
cuTENSOR = "6"
15 changes: 15 additions & 0 deletions test/core/backends/basecopy.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using TensorOperations
using TensorOperations: BaseCopy
using Test

@isdefined(TestSuite) || include("../../testsuite/TestSuite.jl")

# `BaseCopy` is the reference for all other backends, and is itself checked against loops
@testset "BaseCopy" begin
TestSuite.test_backend(BaseCopy(); reference = TestSuite.LoopReference(), diagonal = true)
end
@testset "BaseCopy with generic element types" begin
TestSuite.test_backend(
BaseCopy(); reference = TestSuite.LoopReference(), eltypes = (BigFloat, Complex{BigFloat})
)
end
9 changes: 9 additions & 0 deletions test/core/backends/baseview.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using TensorOperations
using TensorOperations: BaseView
using Test

@isdefined(TestSuite) || include("../../testsuite/TestSuite.jl")

@testset "BaseView" begin
TestSuite.test_backend(BaseView(); diagonal = true)
end
9 changes: 9 additions & 0 deletions test/core/backends/default.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using TensorOperations
using TensorOperations: DefaultBackend
using Test

@isdefined(TestSuite) || include("../../testsuite/TestSuite.jl")

@testset "DefaultBackend" begin
TestSuite.test_backend(DefaultBackend(); diagonal = true)
end
20 changes: 20 additions & 0 deletions test/core/backends/strided.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using TensorOperations
using TensorOperations: StridedBLAS, StridedNative, ManualAllocator, BufferAllocator
using Test

@isdefined(TestSuite) || include("../../testsuite/TestSuite.jl")

@testset "StridedNative" begin
TestSuite.test_backend(StridedNative(); diagonal = true, aliasing = true)
end
@testset "StridedBLAS" begin
TestSuite.test_backend(StridedBLAS(); aliasing = true)
end
@testset "$backend with generic element types" for backend in (StridedNative(), StridedBLAS())
TestSuite.test_backend(backend; eltypes = (BigFloat, Complex{BigFloat}))
end
@testset "$backend with allocator $allocator" for backend in (StridedNative(), StridedBLAS()),
allocator in (ManualAllocator(), BufferAllocator())

TestSuite.test_tensorcontract(backend; allocator, eltypes = (Float64, ComplexF64))
end
Loading
Loading