diff --git a/Project.toml b/Project.toml index 9cdea500..cc7c4743 100644 --- a/Project.toml +++ b/Project.toml @@ -26,6 +26,8 @@ Dagger = "d58978e5-989f-55fb-8d15-ea34adc7bf54" FiniteDifferences = "26cc04aa-876d-5657-8c51-4c34ba976000" GraphMakie = "1ecd5474-83a3-4783-bb4f-06765db800d2" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" +ITensorNetworks = "2919e153-833c-4bdc-8836-1ea460a35fc7" +ITensors = "9136182c-28ba-11e9-034c-db9fb085ebd5" KrylovKit = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d" @@ -41,6 +43,8 @@ TenetChainRulesTestUtilsExt = ["ChainRulesCore", "ChainRulesTestUtils"] TenetDaggerExt = "Dagger" TenetFiniteDifferencesExt = "FiniteDifferences" TenetGraphMakieExt = ["GraphMakie", "Makie"] +TenetITensorNetworksExt = "ITensorNetworks" +TenetITensorsExt = "ITensors" TenetKrylovKitExt = ["KrylovKit"] TenetPythonCallExt = "PythonCall" TenetQuacExt = "Quac" @@ -60,8 +64,10 @@ EinExprs = "0.5, 0.6" FiniteDifferences = "0.12" GraphMakie = "0.4,0.5" Graphs = "1.7" +ITensorNetworks = "0.11" +ITensors = "0.6" KeywordDispatch = "0.3" -KrylovKit = "0.8.1" +KrylovKit = "0.7, 0.8" LinearAlgebra = "1.9" Makie = "0.18,0.19,0.20, 0.21" OMEinsum = "0.7, 0.8" diff --git a/ext/TenetITensorNetworksExt.jl b/ext/TenetITensorNetworksExt.jl new file mode 100644 index 00000000..48dcd855 --- /dev/null +++ b/ext/TenetITensorNetworksExt.jl @@ -0,0 +1,42 @@ +module TenetITensorNetworksExt + +using Tenet +using ITensorNetworks: ITensorNetworks, ITensorNetwork, ITensor, Index, siteinds, plev, vertices, rename_vertices +const ITensors = ITensorNetworks.ITensors +const DataGraphs = ITensorNetworks.DataGraphs +const TenetITensorsExt = Base.get_extension(Tenet, :TenetITensorsExt) + +Base.convert(::Type{TensorNetwork}, tn::ITensorNetwork) = TensorNetwork([convert(Tensor, tn[v]) for v in vertices(tn)]) + +function Base.convert(::Type{ITensorNetwork}, tn::Tenet.AbstractTensorNetwork; inds=Dict{Symbol,Index}()) + return ITensorNetwork(convert(Vector{ITensor}, tn; inds)) +end + +function Base.convert(::Type{Quantum}, tn::ITensorNetwork) + sitedict = Dict( + map(pairs(DataGraphs.vertex_data(siteinds(tn)))) do (loc, index) + index = only(index) + primelevel = plev(index) + @assert primelevel ∈ (0, 1) + + # NOTE ITensors' Index's tag only has space for 16 characters + tag = ITensors.id(index) + Site(loc; dual=Bool(primelevel)) => TenetITensorsExt.symbolize(index) + end, + ) + return Quantum(convert(TensorNetwork, tn), sitedict) +end + +function Base.convert(::Type{ITensorNetwork}, tn::Tenet.AbstractQuantum) + itn = @invoke convert(ITensorNetwork, tn::Tenet.AbstractTensorNetwork) + + return rename_vertices(itn) do v + itensor = itn[v] + indices = map(x -> Symbol(replace(x, "\"" => "")), string.(ITensors.tags.(ITensors.inds(itensor)))) + tensor = only(tensors(tn; contains=indices)) + physical_index = only(inds(tn; set=:physical) ∩ inds(tensor)) + return sites(tn; at=physical_index).id + end +end + +end diff --git a/ext/TenetITensorsExt.jl b/ext/TenetITensorsExt.jl new file mode 100644 index 00000000..8cca8af1 --- /dev/null +++ b/ext/TenetITensorsExt.jl @@ -0,0 +1,47 @@ +module TenetITensorsExt + +using Tenet +using ITensors: ITensors, ITensor, Index + +function symbolize(index::Index) + tag = string(ITensors.id(index)) + + # NOTE ITensors' Index's tag only has space for 16 characters + return Symbol(length(tag) > 16 ? tag[(end - 16 + 1):end] : tag) +end + +function tagize(index::Symbol) + tag = string(index) + + # NOTE ITensors' Index's tag only has space for 16 characters + return length(tag) > 16 ? tag[(end - 16 + 1):end] : tag +end + +# TODO customize index names +function Base.convert(::Type{Tensor}, tensor::ITensor) + array = ITensors.array(tensor) + is = map(symbolize, ITensors.inds(tensor)) + return Tensor(array, is) +end + +function Base.convert(::Type{ITensor}, tensor::Tensor; inds=Dict{Symbol,Index}()) + indices = map(Tenet.inds(tensor)) do i + haskey(inds, i) ? inds[i] : Index(size(tensor, i), tagize(i)) + end + return ITensor(parent(tensor), indices) +end + +Base.convert(::Type{TensorNetwork}, tn::Vector{ITensor}) = TensorNetwork(map(t -> convert(Tensor, t), tn)) + +function Base.convert(::Type{Vector{ITensor}}, tn::Tenet.AbstractTensorNetwork; inds=Dict{Symbol,Index}()) + indices = merge(inds, Dict( + map(Iterators.filter(!Base.Fix1(haskey, inds), Tenet.inds(tn))) do i + i => Index(size(tn, i), tagize(i)) + end, + )) + return map(tensors(tn)) do tensor + ITensor(parent(tensor), map(i -> indices[i], Tenet.inds(tensor))) + end +end + +end diff --git a/src/Quantum.jl b/src/Quantum.jl index 98bcddc6..23d38975 100644 --- a/src/Quantum.jl +++ b/src/Quantum.jl @@ -242,6 +242,11 @@ end end end +@kwmethod function sites(tn::AbstractQuantum; at::Symbol) + tn = Quantum(tn) + return findfirst(==(at), tn.sites) +end + @deprecate Base.getindex(q::Quantum, site::Site) inds(q; at=site) false function Base.replace!(tn::Quantum, old_new::Base.AbstractVecOrTuple{Pair{Symbol,Symbol}}) diff --git a/test/Project.toml b/test/Project.toml index f3c933c3..f8588675 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -8,12 +8,14 @@ Dagger = "d58978e5-989f-55fb-8d15-ea34adc7bf54" DeltaArrays = "10b0fc19-5ccc-4427-889b-d75dd6306188" Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" GraphMakie = "1ecd5474-83a3-4783-bb4f-06765db800d2" +ITensors = "9136182c-28ba-11e9-034c-db9fb085ebd5" KrylovKit = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" NetworkLayout = "46757867-2c16-5918-afeb-47bfcb05e46a" OMEinsum = "ebe7aa44-baf0-506c-a96f-8464559b3922" Permutations = "2ae35dd2-176d-5d53-8349-f30d82d94d4f" +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Quac = "b9105292-1415-45cf-bff1-d6ccf71e6143" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/test/integration/ITensorNetworks_test.jl b/test/integration/ITensorNetworks_test.jl new file mode 100644 index 00000000..b66a38a8 --- /dev/null +++ b/test/integration/ITensorNetworks_test.jl @@ -0,0 +1,29 @@ +# breaks in instantiation on Julia 1.9 +using Pkg +Pkg.add("ITensorNetworks") + +@testset "ITensorNetworks" begin + using ITensors: ITensors, ITensor, Index, array + using ITensorNetworks: ITensorNetwork, vertices + + i = Index(2, "i") + j = Index(3, "j") + k = Index(4, "k") + l = Index(5, "l") + m = Index(6, "m") + + a = ITensor(rand(2, 3), i, j) + b = ITensor(rand(3, 4, 5), j, k, l) + c = ITensor(rand(5, 6), l, m) + itn = ITensorNetwork([a, b, c]) + + tn = convert(TensorNetwork, itn) + @test tn isa TensorNetwork + @test issetequal(arrays(tn), array.([a, b, c])) + + itn = convert(ITensorNetwork, tn) + @test itn isa ITensorNetwork + @test issetequal(map(v -> array(itn[v]), vertices(itn)), array.([a, b, c])) + + # TODO test Quantum +end diff --git a/test/integration/ITensors_test.jl b/test/integration/ITensors_test.jl new file mode 100644 index 00000000..b2e80f3b --- /dev/null +++ b/test/integration/ITensors_test.jl @@ -0,0 +1,32 @@ +@testset "ITensors" begin + using ITensors: ITensors, ITensor, Index, array + + i = Index(2, "i") + j = Index(3, "j") + k = Index(4, "k") + + itensor = ITensor(rand(2, 3, 4), i, j, k) + + tensor = convert(Tensor, itensor) + @test tensor isa Tensor + @test size(tensor) == (2, 3, 4) + @test parent(tensor) == array(itensor) + + tensor = Tensor(rand(2, 3, 4), (:i, :j, :k)) + itensor = convert(ITensor, tensor) + @test itensor isa ITensor + @test size(itensor) == (2, 3, 4) + @test array(itensor) == parent(tensor) + @test all( + splat(==), + zip(map(x -> replace(x, "\"" => ""), string.(ITensors.tags.(ITensors.inds(itensor)))), ["i", "j", "k"]), + ) + + tn = rand(TensorNetwork, 4, 3) + itensors = convert(Vector{ITensor}, tn) + @test itensors isa Vector{ITensor} + + tnr = convert(TensorNetwork, itensors) + @test tnr isa TensorNetwork + @test issetequal(arrays(tn), arrays(tnr)) +end diff --git a/test/runtests.jl b/test/runtests.jl index db1262ff..6a9bc8e7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -23,6 +23,8 @@ if VERSION >= v"1.10" include("integration/Makie_test.jl") include("integration/KrylovKit_test.jl") include("integration/Quac_test.jl") + include("integration/ITensors_test.jl") + include("integration/ITensorNetworks_test.jl") end end