diff --git a/Project.toml b/Project.toml index 4f88083b..d0c784f2 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "TensorAlgebra" uuid = "68bd88dc-f39d-4e12-b2ca-f046b68fcc6a" -version = "0.17.7" +version = "0.17.8" authors = ["ITensor developers and contributors"] [workspace] diff --git a/ext/TensorAlgebraTensorKitExt.jl b/ext/TensorAlgebraTensorKitExt.jl index 36497751..c3c073b3 100644 --- a/ext/TensorAlgebraTensorKitExt.jl +++ b/ext/TensorAlgebraTensorKitExt.jl @@ -37,6 +37,10 @@ TensorAlgebra.scalar(t::AbstractTensorMap) = TensorKit.scalar(t) TensorAlgebra.data(t::TensorMap) = TensorAlgebra.data(t.data) TensorAlgebra.data(t::DiagonalTensorMap) = TensorAlgebra.data(t.data) +# Duality queries on a single index space route to TensorKit's own space duality. +TensorAlgebra.isdual(V::ElementarySpace) = TensorKit.isdual(V) +TensorAlgebra.dual(V::ElementarySpace) = TensorKit.dual(V) + # The trivial length-1 axis of a space is its unit space (`oneunit`), the trivial-sector # one-dimensional space; the length-`n` form is the direct sum of `n` unit spaces. TensorAlgebra.trivialrange(V::ElementarySpace) = oneunit(V) diff --git a/src/TensorAlgebra.jl b/src/TensorAlgebra.jl index ef3e19e3..df3f74a4 100644 --- a/src/TensorAlgebra.jl +++ b/src/TensorAlgebra.jl @@ -1,7 +1,7 @@ module TensorAlgebra -export contract, contract!, eig_full, eig_trunc, eig_vals, eigh_full, eigh_trunc, - eigh_vals, gram_eigh_full, gram_eigh_full_with_pinv, invsqrth_safe, left_null, +export contract, contract!, dual, eig_full, eig_trunc, eig_vals, eigh_full, eigh_trunc, + eigh_vals, gram_eigh_full, gram_eigh_full_with_pinv, invsqrth_safe, isdual, left_null, left_orth, left_polar, lq_compact, lq_full, project_hermitian, qr_compact, qr_full, right_null, right_orth, right_polar, sqrth_invsqrth_safe, sqrth_safe, svd_compact, svd_full, svd_trunc, svd_vals @@ -24,6 +24,7 @@ include("matricize.jl") include("concatenate.jl") include("directsum.jl") include("diagonal.jl") +include("dual.jl") include("to_range.jl") include("contract/contractalgorithm.jl") include("contract/contract.jl") diff --git a/src/dual.jl b/src/dual.jl new file mode 100644 index 00000000..eb3cca15 --- /dev/null +++ b/src/dual.jl @@ -0,0 +1,21 @@ +""" + isdual(a) -> Bool + +Returns `true` or `false` depending on if the axis `a` is dual. Falls back to `false` +for `AbstractUnitRange`. + +See also [`dual`](@ref). +""" +function isdual end +isdual(::AbstractUnitRange) = false + +""" + dual(a) + +Returns the dual of the axis `a`. Falls back to returning `a` unchanged for +`AbstractUnitRange`. + +See also [`isdual`](@ref). +""" +function dual end +dual(a::AbstractUnitRange) = a diff --git a/test/test_dual.jl b/test/test_dual.jl new file mode 100644 index 00000000..6afa5456 --- /dev/null +++ b/test/test_dual.jl @@ -0,0 +1,14 @@ +using TensorAlgebra: TensorAlgebra, dual, isdual +using Test: @test, @test_throws, @testset + +@testset "dual/isdual fallbacks on ranges" begin + # An ordinary range has no arrow: never dual, and its own dual. + for r in (Base.OneTo(4), 2:5) + @test isdual(r) == false + @test dual(r) === r + end + # No universal fallback: a type with no duality concept errors instead of + # silently returning a default. + @test_throws MethodError isdual(3) + @test_throws MethodError dual(3) +end diff --git a/test/test_exports.jl b/test/test_exports.jl index b45c40cf..5bab6ad0 100644 --- a/test/test_exports.jl +++ b/test/test_exports.jl @@ -6,6 +6,7 @@ using Test: @test, @testset :TensorAlgebra, :contract, :contract!, + :dual, :eig_full, :eig_trunc, :eig_vals, @@ -15,6 +16,7 @@ using Test: @test, @testset :gram_eigh_full, :gram_eigh_full_with_pinv, :invsqrth_safe, + :isdual, :left_null, :left_orth, :left_polar, diff --git a/test/test_tensorkitext.jl b/test/test_tensorkitext.jl index 4d9260a0..31460877 100644 --- a/test/test_tensorkitext.jl +++ b/test/test_tensorkitext.jl @@ -330,3 +330,10 @@ using Test: @test, @test_throws, @testset LinearAlgebra.tr(t) end end + +@testset "dual/isdual on a TensorKit space" begin + V = Rep[U₁](0 => 2, 1 => 1) + @test TensorAlgebra.isdual(V) == false + @test TensorAlgebra.isdual(dual(V)) == true + @test TensorAlgebra.dual(V) == dual(V) +end