ITensor / ITensors.jl

A Julia library for efficient tensor computations and tensor network calculations
https://itensor.org
Apache License 2.0
520 stars 120 forks source link

[TensorAlgebra] [Feature request] TensorAlgebra.contract to accept biperm #1507

Open ogauthe opened 2 months ago

ogauthe commented 2 months ago

For a FusionTensor, a permutation is not enough to specify the structure, a biperm is needed. Currently, TensorAlgebra.contract does not allow this.

using NDTensors.FusionTensors: FusionTensor
using NDTensors.GradedAxes
using NDTensors.Sectors: U1

  g1 = GradedAxes.gradedrange([U1(0) => 1])
  ft1 = FusionTensor(Float64, (GradedAxes.dual(g1),), (g1,))
  ft2 = FusionTensor(Float64, (GradedAxes.dual(g1),), (g1,g1))

  ft3, legs = TensorAlgebra.contract(ft1, (1,2), ft2, (2, 3, 4))  # Ok
  ft4 = TensorAlgebra.contract((1,4, 3), ft1, (1,2), ft2, (2, 3, 4))  # Ok
  ft5 = TensorAlgebra.contract(((1,4),(3,)), ft1, (1,2), ft2, (2, 3, 4))   # MethodError
  ERROR: MethodError: no method matching blockedperm(::Tuple{Nothing}, ::Tuple{Nothing, Nothing})

Closest candidates are:
  blockedperm(::NDTensors.NamedDimsArrays.AbstractNamedDimsArray, ::Tuple...)
   @ NDTensors ~/Documents/itensor/ITensors.jl/NDTensors/src/lib/NamedDimsArrays/ext/NamedDimsArraysTensorAlgebraExt/src/fusedims.jl:4

Stacktrace:
 [1] blockedperms(::typeof(NDTensors.TensorAlgebra.contract), dimnames_dest::Tuple{…}, dimnames1::Tuple{…}, dimnames2::Tuple{…})
   @ NDTensors.TensorAlgebra ~/Documents/itensor/ITensors.jl/NDTensors/src/lib/TensorAlgebra/src/contract/blockedperms.jl:26
 [2] contract(alg::NDTensors.BackendSelection.Algorithm{…}, labels_dest::Tuple{…}, a1::FusionTensor{…}, labels1::Tuple{…}, a2::FusionTensor{…}, labels2::Tuple{…}, α::Bool; kwargs::@Kwargs{})
   @ NDTensors.TensorAlgebra ~/Documents/itensor/ITensors.jl/NDTensors/src/lib/TensorAlgebra/src/contract/contract.jl:87
 [3] contract
   @ ~/Documents/itensor/ITensors.jl/NDTensors/src/lib/TensorAlgebra/src/contract/contract.jl:77 [inlined]
 [4] #contract#33
   @ ~/Documents/itensor/ITensors.jl/NDTensors/src/lib/TensorAlgebra/src/contract/contract.jl:58 [inlined]
 [5] contract
   @ ~/Documents/itensor/ITensors.jl/NDTensors/src/lib/TensorAlgebra/src/contract/contract.jl:48 [inlined]
 [6] contract(labels_dest::Tuple{…}, a1::FusionTensor{…}, labels1::Tuple{…}, a2::FusionTensor{…}, labels2::Tuple{…})
   @ NDTensors.TensorAlgebra ~/Documents/itensor/ITensors.jl/NDTensors/src/lib/TensorAlgebra/src/contract/contract.jl:48
 [7] top-level scope
   @ REPL[65]:1
Some type information was truncated. Use `show(err)` to see complete types.

Note that it is not possible to construct a BlockedPermutation from ((1,4), (3,)):

  julia> TensorAlgebra.blockedperm((1,4), (3,))
ERROR: AssertionError: isperm(blockedperm)
Stacktrace:
 [1] blockedperm
   @ ~/Documents/itensor/ITensors.jl/NDTensors/src/lib/TensorAlgebra/src/blockedpermutation.jl:166 [inlined]
 [2] blockedperm
   @ ~/Documents/itensor/ITensors.jl/NDTensors/src/lib/TensorAlgebra/src/blockedpermutation.jl:110 [inlined]
 [3] #blockedperm#13
   @ ~/Documents/itensor/ITensors.jl/NDTensors/src/lib/TensorAlgebra/src/blockedpermutation.jl:106 [inlined]
 [4] blockedperm(::Tuple{Int64, Int64}, ::Tuple{Int64})
   @ NDTensors.TensorAlgebra ~/Documents/itensor/ITensors.jl/NDTensors/src/lib/TensorAlgebra/src/blockedpermutation.jl:105
 [5] top-level scope
   @ REPL[66]:1

but even a correct BlockedPermutation fails:

  biperm = TensorAlgebra.blockedperm((1,3),(2,))
  ft5 = TensorAlgebra.contract(biperm, ft1, (1,4), ft2, (4, 2, 3))   # MethodError
ERROR: MethodError: no method matching contract(::NDTensors.TensorAlgebra.BlockedPermutation{…}, ::FusionTensor{…}, ::Tuple{…}, ::FusionTensor{…}, ::Tuple{…})

Closest candidates are:
  contract(::NDTensors.BackendSelection.Algorithm, ::AbstractArray, ::Tuple, ::AbstractArray, ::Tuple; ...)
   @ NDTensors ~/Documents/itensor/ITensors.jl/NDTensors/src/lib/TensorAlgebra/src/contract/contract.jl:35
  contract(::NDTensors.BackendSelection.Algorithm, ::AbstractArray, ::Tuple, ::AbstractArray, ::Tuple, ::Number; kwargs...)
   @ NDTensors ~/Documents/itensor/ITensors.jl/NDTensors/src/lib/TensorAlgebra/src/contract/contract.jl:35
  contract(::Tuple, ::AbstractArray, ::Tuple, ::AbstractArray, ::Tuple; ...)
   @ NDTensors ~/Documents/itensor/ITensors.jl/NDTensors/src/lib/TensorAlgebra/src/contract/contract.jl:48
  ...

Stacktrace:
 [1] top-level scope
   @ REPL[84]:1
Some type information was truncated. Use `show(err)` to see complete types.
mtfishman commented 2 months ago

I agree it makes sense to support the syntax:

contract(((1, 4), (3,)), ft1, (1, 2), ft2, (2, 3, 4))

to specify which dimensions will become the codomain and domain of the output tensor, good suggestion.

However, I think in terms of terminology you might be conflating the concept of labels and permutations, I don't think that supporting blockedperm((1, 4), (3,)) makes sense, but I also don't think it is needed to implement this feature. In syntax like contract(((1, 4), (3,)), ft1, (1, 2), ft2, (2, 3, 4)), to be consistent with the conventions of the current interface, those should be interpreted as labels, and then those would be compiled down to blocked permutations. So contract should be thought of as having two layers/interfaces, one based around dimension labels and one based around permutations/bipartitioned permutations (and those shouldn't be mixed in a single function call).

I believe the permutation version of that contraction would be something like:

contract(blockedperm((1, 3), (2,)), ft1, blockedperm((1,), (2,)), ft2, blockedperm((1,), (2, 3)))

I'm not sure if I got that exactly right but hopefully you get the idea. I'm not sure if that syntax is supported right now but if it isn't we should add it as well.