diff --git a/.JuliaFormatter.toml b/.JuliaFormatter.toml deleted file mode 100644 index 9613e0542..000000000 --- a/.JuliaFormatter.toml +++ /dev/null @@ -1 +0,0 @@ -style = "yas" \ No newline at end of file diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index e7be5d8e6..a402fbac1 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -15,7 +15,7 @@ concurrency: jobs: test: - name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} + name: Julia ${{ matrix.version }} - ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml index 82540cbdf..2efa357ca 100644 --- a/.github/workflows/Documentation.yml +++ b/.github/workflows/Documentation.yml @@ -11,7 +11,7 @@ on: workflow_dispatch: jobs: - build: + Documentation: runs-on: ${{ matrix.os }} strategy: matrix: diff --git a/.github/workflows/FormatCheck.yml b/.github/workflows/FormatCheck.yml index e112b3028..383c502e7 100644 --- a/.github/workflows/FormatCheck.yml +++ b/.github/workflows/FormatCheck.yml @@ -1,44 +1,15 @@ -name: FormatCheck +name: 'Format' on: - push: - branches: - - 'main' - - 'master' - - 'release-' - tags: '*' - pull_request: + pull_request_target: + paths: ['**/*.jl'] + types: [opened, synchronize, reopened, ready_for_review] -jobs: - build: - runs-on: ${{ matrix.os }} - strategy: - matrix: - version: - - '1' # automatically expands to the latest stable 1.x release of Julia - os: - - ubuntu-latest - steps: - - uses: julia-actions/setup-julia@latest - with: - version: ${{ matrix.version }} +permissions: + contents: read + actions: write + pull-requests: write - - uses: actions/checkout@v5 - - name: Install JuliaFormatter and format - # This will use the latest version by default but you can set the version like so: - # - # julia -e 'using Pkg; Pkg.add(PackageSpec(name="JuliaFormatter", version="0.13.0"))' - run: | - julia -e 'using Pkg; Pkg.add(PackageSpec(name="JuliaFormatter", version="1"))' - julia -e 'using JuliaFormatter; format(".", verbose=true)' - - name: Format check - run: | - julia -e ' - out = Cmd(`git diff --name-only`) |> read |> String - if out == "" - exit(0) - else - @error "Some files have not been formatted !!!" - write(stdout, out) - exit(1) - end' +jobs: + formatcheck: + uses: "QuantumKitHub/QuantumKitHubActions/.github/workflows/FormatCheck.yml@main" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..3e2823c36 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,5 @@ +repos: + - repo: https://github.com/fredrikekre/runic-pre-commit + rev: v2.0.1 + hooks: + - id: runic diff --git a/benchmark/TensorKitBenchmarks/TensorKitBenchmarks.jl b/benchmark/TensorKitBenchmarks/TensorKitBenchmarks.jl index f93fd9996..bbf308851 100644 --- a/benchmark/TensorKitBenchmarks/TensorKitBenchmarks.jl +++ b/benchmark/TensorKitBenchmarks/TensorKitBenchmarks.jl @@ -11,13 +11,15 @@ BenchmarkTools.DEFAULT_PARAMETERS.memory_tolerance = 0.01 const PARAMS_PATH = joinpath(@__DIR__, "etc", "params.json") const SUITE = BenchmarkGroup() -const MODULES = Dict("linalg" => :LinalgBenchmarks, - "indexmanipulations" => :IndexManipulationBenchmarks, - "tensornetworks" => :TensorNetworkBenchmarks) +const MODULES = Dict( + "linalg" => :LinalgBenchmarks, + "indexmanipulations" => :IndexManipulationBenchmarks, + "tensornetworks" => :TensorNetworkBenchmarks +) load!(id::AbstractString; kwargs...) = load!(SUITE, id; kwargs...) -function load!(group::BenchmarkGroup, id::AbstractString; tune::Bool=false) +function load!(group::BenchmarkGroup, id::AbstractString; tune::Bool = false) modsym = MODULES[id] modpath = joinpath(dirname(@__FILE__), id, "$(modsym).jl") Core.eval(@__MODULE__, :(include($modpath))) @@ -33,14 +35,14 @@ end loadall!(; kwargs...) = loadall!(SUITE; kwargs...) -function loadall!(group::BenchmarkGroup; verbose::Bool=true, tune::Bool=false) +function loadall!(group::BenchmarkGroup; verbose::Bool = true, tune::Bool = false) for id in keys(MODULES) if verbose print("loading group $(repr(id))... ") - time = @elapsed load!(group, id, tune=false) + time = @elapsed load!(group, id, tune = false) println("done (took $time seconds)") else - load!(group, id; tune=false) + load!(group, id; tune = false) end end if tune diff --git a/benchmark/TensorKitBenchmarks/indexmanipulations/IndexManipulationBenchmarks.jl b/benchmark/TensorKitBenchmarks/indexmanipulations/IndexManipulationBenchmarks.jl index 243fbce05..1529db44e 100644 --- a/benchmark/TensorKitBenchmarks/indexmanipulations/IndexManipulationBenchmarks.jl +++ b/benchmark/TensorKitBenchmarks/indexmanipulations/IndexManipulationBenchmarks.jl @@ -25,15 +25,15 @@ function benchmark_permute!(benchgroup, params::Dict) end return nothing end -function benchmark_permute!(bench; sigmas=nothing, T="Float64", I="Trivial", dims, p) +function benchmark_permute!(bench; sigmas = nothing, T = "Float64", I = "Trivial", dims, p) T_ = parse_type(T) I_ = parse_type(I) p_ = (Tuple(p[1]), Tuple(p[2])) Vs = generate_space.(I_, dims, sigmas) - codomain = mapreduce(Base.Fix1(getindex, Vs), ⊗, p_[1]; init=one(eltype(Vs))) - domain = mapreduce(Base.Fix1(getindex, Vs), ⊗, p_[2]; init=one(eltype(Vs))) + codomain = mapreduce(Base.Fix1(getindex, Vs), ⊗, p_[1]; init = one(eltype(Vs))) + domain = mapreduce(Base.Fix1(getindex, Vs), ⊗, p_[2]; init = one(eltype(Vs))) init() = init_permute_tensors(T_, codomain ← domain, p_) bench[T, I, dims, sigmas, p] = @benchmarkable permute!(C, A, $p_) setup = ((C, A) = $init()) diff --git a/benchmark/TensorKitBenchmarks/linalg/LinalgBenchmarks.jl b/benchmark/TensorKitBenchmarks/linalg/LinalgBenchmarks.jl index 75ad34a29..ea9d873ec 100644 --- a/benchmark/TensorKitBenchmarks/linalg/LinalgBenchmarks.jl +++ b/benchmark/TensorKitBenchmarks/linalg/LinalgBenchmarks.jl @@ -28,7 +28,7 @@ function benchmark_mul!(benchgroup, params::Dict) return nothing end -function benchmark_mul!(bench; sigmas=nothing, T="Float64", I="Trivial", dims) +function benchmark_mul!(bench; sigmas = nothing, T = "Float64", I = "Trivial", dims) T_ = parse_type(T) I_ = parse_type(I) @@ -62,7 +62,7 @@ function benchmark_svd!(benchgroup, params::Dict) end return nothing end -function benchmark_svd!(bench; sigmas=nothing, T="Float64", I="Trivial", dims) +function benchmark_svd!(bench; sigmas = nothing, T = "Float64", I = "Trivial", dims) T_ = parse_type(T) I_ = parse_type(I) Vs = generate_space.(I_, dims, sigmas) diff --git a/benchmark/TensorKitBenchmarks/tensornetworks/TensorNetworkBenchmarks.jl b/benchmark/TensorKitBenchmarks/tensornetworks/TensorNetworkBenchmarks.jl index 4fcd27b22..749ea709b 100644 --- a/benchmark/TensorKitBenchmarks/tensornetworks/TensorNetworkBenchmarks.jl +++ b/benchmark/TensorKitBenchmarks/tensornetworks/TensorNetworkBenchmarks.jl @@ -32,7 +32,7 @@ function benchmark_mpo!(benchgroup, params::Dict) end return nothing end -function benchmark_mpo!(bench; sigmas=nothing, T="Float64", I="Trivial", dims) +function benchmark_mpo!(bench; sigmas = nothing, T = "Float64", I = "Trivial", dims) T_ = parse_type(T) I_ = parse_type(I) @@ -64,8 +64,8 @@ end function benchmark_pepo(A, P, FL, FD, FR, FU) return @tensor FL[18, 7, 4, 2, 1] * FU[1, 3, 6, 9, 10] * A[2, 17, 5, 3, 11] * - P[4, 16, 8, 5, 6, 12] * conj(A[7, 15, 8, 9, 13]) * - FR[10, 11, 12, 13, 14] * FD[14, 15, 16, 17, 18] + P[4, 16, 8, 5, 6, 12] * conj(A[7, 15, 8, 9, 13]) * + FR[10, 11, 12, 13, 14] * FD[14, 15, 16, 17, 18] end function benchmark_pepo!(benchgroup, params::Dict) @@ -76,7 +76,7 @@ function benchmark_pepo!(benchgroup, params::Dict) end return nothing end -function benchmark_pepo!(bench; sigmas=nothing, T="Float64", I="Trivial", dims) +function benchmark_pepo!(bench; sigmas = nothing, T = "Float64", I = "Trivial", dims) T_ = parse_type(T) I_ = parse_type(I) @@ -105,11 +105,18 @@ function init_mera_tensors(T, V) end function benchmark_mera(u, w, ρ, h) - return @tensor (((((((h[9, 3, 4, 5, 1, 2] * u[1, 2, 7, 12]) * conj(u[3, 4, 11, 13])) * - (u[8, 5, 15, 6] * w[6, 7, 19])) * - (conj(u[8, 9, 17, 10]) * conj(w[10, 11, 22]))) * - ((w[12, 14, 20] * conj(w[13, 14, 23])) * ρ[18, 19, 20, 21, 22, 23])) * - w[16, 15, 18]) * conj(w[16, 17, 21])) + return @tensor ( + ( + ( + ( + ( + ((h[9, 3, 4, 5, 1, 2] * u[1, 2, 7, 12]) * conj(u[3, 4, 11, 13])) * + (u[8, 5, 15, 6] * w[6, 7, 19]) + ) * (conj(u[8, 9, 17, 10]) * conj(w[10, 11, 22])) + ) * ((w[12, 14, 20] * conj(w[13, 14, 23])) * ρ[18, 19, 20, 21, 22, 23]) + ) * w[16, 15, 18] + ) * conj(w[16, 17, 21]) + ) end function benchmark_mera!(benchgroup, params::Dict) @@ -121,7 +128,7 @@ function benchmark_mera!(benchgroup, params::Dict) return nothing end -function benchmark_mera!(bench; sigmas=nothing, T="Float64", I="Trivial", dims) +function benchmark_mera!(bench; sigmas = nothing, T = "Float64", I = "Trivial", dims) T_ = parse_type(T) I_ = parse_type(I) diff --git a/benchmark/TensorKitBenchmarks/utils/BenchUtils.jl b/benchmark/TensorKitBenchmarks/utils/BenchUtils.jl index ce22c763a..8f1d59159 100644 --- a/benchmark/TensorKitBenchmarks/utils/BenchUtils.jl +++ b/benchmark/TensorKitBenchmarks/utils/BenchUtils.jl @@ -8,8 +8,9 @@ using TensorKit parse_type(x::String) = eval(Meta.parse(x)) function expand_kwargs(params::Dict) - const_params = NamedTuple(Symbol(key) => value - for (key, value) in params if !(value isa Vector)) + const_params = NamedTuple( + Symbol(key) => value for (key, value) in params if !(value isa Vector) + ) nonconst_keys = Tuple(Symbol(key) for (key, value) in params if value isa Vector) nonconst_vals = (value for value in values(params) if value isa Vector) return Iterators.map(Iterators.product(nonconst_vals...)) do expanded_vals @@ -22,15 +23,15 @@ end Creates a (graded) vector space with sectortype `I` and total dimension `D`, where the distribution of charges is controlled through a spread parameter `sigma`. """ -function generate_space(::Type{Trivial}, D::Int, sigma::Nothing=nothing) +function generate_space(::Type{Trivial}, D::Int, sigma::Nothing = nothing) return ComplexSpace(round(Int, D)) end -function generate_space(::Type{Z2Irrep}, D::Int, sigma::Real=0.5) +function generate_space(::Type{Z2Irrep}, D::Int, sigma::Real = 0.5) D_even = ceil(Int, sigma * D) D_odd = D - D_even return Z2Space(0 => D_even, 1 => D_odd) end -function generate_space(::Type{U1Irrep}, D::Int, sigma::Real=0.5) +function generate_space(::Type{U1Irrep}, D::Int, sigma::Real = 0.5) # use ceil here to avoid getting stuck normal_pdf = let D = D x -> ceil(Int, D * exp(-0.5 * (x / sigma)^2) / (sigma * sqrt(2π))) @@ -54,7 +55,7 @@ function generate_space(::Type{U1Irrep}, D::Int, sigma::Real=0.5) return U1Space((s => d for (s, d) in zip(sectors, dims))...) end -function generate_space(::Type{SU2Irrep}, D::Int, sigma::Real=0.5) +function generate_space(::Type{SU2Irrep}, D::Int, sigma::Real = 0.5) normal_pdf = let D = D x -> D * exp(-0.5 * (x / sigma)^2) / (sigma * sqrt(2π)) end diff --git a/benchmark/comparisons/benchtools.jl b/benchmark/comparisons/benchtools.jl index 7504b85aa..322069711 100644 --- a/benchmark/comparisons/benchtools.jl +++ b/benchmark/comparisons/benchtools.jl @@ -1,12 +1,12 @@ -function distributeZ2(D; p=0.5) +function distributeZ2(D; p = 0.5) D0 = ceil(Int, p * D) D1 = D - D0 return [(0, D0), (1, D1)] end -distributeU1(D; p=0.25) = distributeU1_poisson(D; p=p) +distributeU1(D; p = 0.25) = distributeU1_poisson(D; p = p) -function distributeU1_exponential(D; p=0.25) +function distributeU1_exponential(D; p = 0.25) λ = (1 - p) / (1 + p) D0 = ceil(Int, p * D) if isodd(D - D0) @@ -22,10 +22,10 @@ function distributeU1_exponential(D; p=0.25) Drem -= 2 * Dn n += 1 end - return sort!(sectors; by=first) + return sort!(sectors; by = first) end -function distributeU1_poisson(D; p=0.25) +function distributeU1_poisson(D; p = 0.25) λ = log((1 / p + 1) / 2) D0 = ceil(Int, p * D) if isodd(D - D0) @@ -41,42 +41,42 @@ function distributeU1_poisson(D; p=0.25) Drem -= 2 * Dn n += 1 end - return sort!(sectors; by=first) + return sort!(sectors; by = first) end module Timers -struct Timer{F,D<:Tuple} - f::F - argsref::Base.RefValue{D} - Timer(f, args...) = new{typeof(f),typeof(args)}(f, Ref(args)) -end + struct Timer{F, D <: Tuple} + f::F + argsref::Base.RefValue{D} + Timer(f, args...) = new{typeof(f), typeof(args)}(f, Ref(args)) + end -@noinline donothing(arg) = arg -function (t::Timer)(; inner=1, outer=1) - args = t.argsref[] - f = t.f - f(args...) # run once to compile - times = zeros(Float64, (outer,)) - gctimes = zeros(Float64, (outer,)) - @inbounds for i in 1:outer - if inner == 1 - gcstart = Base.gc_num() - start = Base.time_ns() - donothing(f(args...)) - stop = Base.time_ns() - gcstop = Base.gc_num() - else - gcstart = Base.gc_num() - start = Base.time_ns() - for _ in 1:inner + @noinline donothing(arg) = arg + function (t::Timer)(; inner = 1, outer = 1) + args = t.argsref[] + f = t.f + f(args...) # run once to compile + times = zeros(Float64, (outer,)) + gctimes = zeros(Float64, (outer,)) + @inbounds for i in 1:outer + if inner == 1 + gcstart = Base.gc_num() + start = Base.time_ns() donothing(f(args...)) + stop = Base.time_ns() + gcstop = Base.gc_num() + else + gcstart = Base.gc_num() + start = Base.time_ns() + for _ in 1:inner + donothing(f(args...)) + end + stop = Base.time_ns() + gcstop = Base.gc_num() end - stop = Base.time_ns() - gcstop = Base.gc_num() + times[i] = (stop - start) / (1.0e9) / inner + gctimes[i] = Base.GC_Diff(gcstop, gcstart).total_time / 1.0e9 / inner end - times[i] = (stop - start) / (1e9) / inner - gctimes[i] = Base.GC_Diff(gcstop, gcstart).total_time / 1e9 / inner + return times, gctimes end - return times, gctimes -end end diff --git a/benchmark/comparisons/bonds.txt b/benchmark/comparisons/bonds.txt index f9ed2e5a8..3fcaaddcd 100644 --- a/benchmark/comparisons/bonds.txt +++ b/benchmark/comparisons/bonds.txt @@ -1,32 +1,44 @@ # Warning: it's good to have about 32 GB of RAM or even more (64 GB) # Warning: this should be both valid Python and valid Julia code -mpodims_triv = ([ 10, 40, 160, 640, 2560, 100, 200, 300], # mps - [ 4, 4, 4, 4, 4, 10, 10, 20], # mpo - [ 3, 3, 3, 3, 3, 10, 10, 20]) # phys +mpodims_triv = ( + [10, 40, 160, 640, 2560, 100, 200, 300], # mps + [4, 4, 4, 4, 4, 10, 10, 20], # mpo + [3, 3, 3, 3, 3, 10, 10, 20], +) # phys -mpodims_z2 = ( [ 10, 40, 160, 640, 2560, 100, 200, 300], # mps - [ 4, 4, 4, 4, 4, 10, 10, 20], # mpo - [ 4, 4, 4, 4, 4, 10, 10, 20]) # phys +mpodims_z2 = ( + [10, 40, 160, 640, 2560, 100, 200, 300], # mps + [4, 4, 4, 4, 4, 10, 10, 20], # mpo + [4, 4, 4, 4, 4, 10, 10, 20], +) # phys -mpodims_u1 = ( [ 40, 160, 640, 2560, 6120, 200, 400, 400], # mps - [ 5, 5, 5, 5, 5, 20, 20, 40], # mpo - [ 3, 3, 3, 3, 3, 20, 20, 40]) # phys +mpodims_u1 = ( + [40, 160, 640, 2560, 6120, 200, 400, 400], # mps + [5, 5, 5, 5, 5, 20, 20, 40], # mpo + [3, 3, 3, 3, 3, 20, 20, 40], +) # phys -pepodims_triv = ( [ 3, 3, 4, 4, 5, 5, 6, 6], # peps - [ 2, 3, 2, 3, 2, 2, 2, 3], # pepo - [ 2, 3, 2, 3, 2, 3, 2, 2], # phys - [ 50, 100, 50, 100, 50, 100, 50, 100]) # env +pepodims_triv = ( + [3, 3, 4, 4, 5, 5, 6, 6], # peps + [2, 3, 2, 3, 2, 2, 2, 3], # pepo + [2, 3, 2, 3, 2, 3, 2, 2], # phys + [50, 100, 50, 100, 50, 100, 50, 100], +) # env -pepodims_z2 = ( [ 4, 4, 5, 5, 6, 6, 8, 8], # peps - [ 2, 4, 2, 3, 2, 2, 2, 3], # pepo - [ 2, 4, 2, 4, 2, 4, 2, 2], # phys - [ 50, 100, 50, 100, 50, 100, 50, 100]) # env +pepodims_z2 = ( + [4, 4, 5, 5, 6, 6, 8, 8], # peps + [2, 4, 2, 3, 2, 2, 2, 3], # pepo + [2, 4, 2, 4, 2, 4, 2, 2], # phys + [50, 100, 50, 100, 50, 100, 50, 100], +) # env -pepodims_u1 = ( [ 4, 4, 6, 6, 8, 8, 10, 10], # peps - [ 2, 4, 2, 3, 2, 2, 2, 3], # pepo - [ 2, 4, 2, 4, 2, 4, 2, 2], # phys - [100, 200, 100, 200, 100, 200, 50, 100]) # env +pepodims_u1 = ( + [4, 4, 6, 6, 8, 8, 10, 10], # peps + [2, 4, 2, 3, 2, 2, 2, 3], # pepo + [2, 4, 2, 4, 2, 4, 2, 2], # phys + [100, 200, 100, 200, 100, 200, 50, 100], +) # env meradims_triv = ([2, 3, 4, 8, 12, 16],) meradims_z2 = ([2, 4, 8, 12, 16, 20],) diff --git a/benchmark/comparisons/itensors_timers.jl b/benchmark/comparisons/itensors_timers.jl index 96bebe5b7..368908cf7 100644 --- a/benchmark/comparisons/itensors_timers.jl +++ b/benchmark/comparisons/itensors_timers.jl @@ -1,86 +1,84 @@ include("benchtools.jl") module ITensorsTimers -using ITensors -using ..Timers: Timers + using ITensors + using ..Timers: Timers -function add_indexlabels_as_tags(tensor, labels) - new_inds = map(x -> settags(x[2], "l$(x[1])"), zip(labels, tensor.inds)) - return ITensor(tensor.store, new_inds) -end + function add_indexlabels_as_tags(tensor, labels) + new_inds = map(x -> settags(x[2], "l$(x[1])"), zip(labels, tensor.inds)) + return ITensor(tensor.store, new_inds) + end -function mpo_timer(T=Float64; Vmpo, Vmps, Vphys) - A = randomITensor(T, Vmps, Vphys, dag(Vmps)) - M = randomITensor(T, Vmpo, Vphys, dag(Vphys), dag(Vmpo)) - FL = randomITensor(T, Vmps, dag(Vmpo), dag(Vmps)) - FR = randomITensor(T, Vmps, Vmpo, dag(Vmps)) + function mpo_timer(T = Float64; Vmpo, Vmps, Vphys) + A = randomITensor(T, Vmps, Vphys, dag(Vmps)) + M = randomITensor(T, Vmpo, Vphys, dag(Vphys), dag(Vmpo)) + FL = randomITensor(T, Vmps, dag(Vmpo), dag(Vmps)) + FR = randomITensor(T, Vmps, Vmpo, dag(Vmps)) - FL1 = add_indexlabels_as_tags(FL, (4, 2, 1)) - A1 = add_indexlabels_as_tags(A, (1, 3, 6)) - M1 = add_indexlabels_as_tags(M, (2, 5, 3, 7)) - A2 = add_indexlabels_as_tags(A, (4, 5, 8)) - FR1 = add_indexlabels_as_tags(FR, (6, 7, 8)) + FL1 = add_indexlabels_as_tags(FL, (4, 2, 1)) + A1 = add_indexlabels_as_tags(A, (1, 3, 6)) + M1 = add_indexlabels_as_tags(M, (2, 5, 3, 7)) + A2 = add_indexlabels_as_tags(A, (4, 5, 8)) + FR1 = add_indexlabels_as_tags(FR, (6, 7, 8)) - return Timers.Timer(A1, A2, M1, FL1, FR1) do A1, A2, M1, FL1, FR1 - C = ((((FL1 * A1) * M1) * dag(A2)) * FR1) - return C[] + return Timers.Timer(A1, A2, M1, FL1, FR1) do A1, A2, M1, FL1, FR1 + C = ((((FL1 * A1) * M1) * dag(A2)) * FR1) + return C[] + end end -end -function pepo_timer(T=Float64; Vpepo, Vpeps, Venv, Vphys) - Vpeps′ = dag(Vpeps) - Vpepo′ = dag(Vpepo) - Venv′ = dag(Venv) - Vphys′ = dag(Vphys) + function pepo_timer(T = Float64; Vpepo, Vpeps, Venv, Vphys) + Vpeps′ = dag(Vpeps) + Vpepo′ = dag(Vpepo) + Venv′ = dag(Venv) + Vphys′ = dag(Vphys) - A = randomITensor(T, Vpeps, Vpeps, Vphys, Vpeps′, Vpeps′) - P = randomITensor(T, Vpepo, Vpepo, Vphys, Vphys′, Vpepo′, Vpepo′) - FL = randomITensor(T, Venv, Vpeps, Vpepo′, Vpeps′, Venv′) - FD = randomITensor(T, Venv, Vpeps, Vpepo′, Vpeps′, Venv′) - FR = randomITensor(T, Venv, Vpeps, Vpepo, Vpeps′, Venv′) - FU = randomITensor(T, Venv, Vpeps, Vpepo, Vpeps′, Venv′) + A = randomITensor(T, Vpeps, Vpeps, Vphys, Vpeps′, Vpeps′) + P = randomITensor(T, Vpepo, Vpepo, Vphys, Vphys′, Vpepo′, Vpepo′) + FL = randomITensor(T, Venv, Vpeps, Vpepo′, Vpeps′, Venv′) + FD = randomITensor(T, Venv, Vpeps, Vpepo′, Vpeps′, Venv′) + FR = randomITensor(T, Venv, Vpeps, Vpepo, Vpeps′, Venv′) + FU = randomITensor(T, Venv, Vpeps, Vpepo, Vpeps′, Venv′) - FL1 = add_indexlabels_as_tags(FL, (18, 7, 4, 2, 1)) - FU1 = add_indexlabels_as_tags(FU, (1, 3, 6, 9, 10)) - A1 = add_indexlabels_as_tags(A, (2, 17, 5, 3, 11)) - P1 = add_indexlabels_as_tags(P, (4, 16, 8, 5, 6, 12)) - A2 = add_indexlabels_as_tags(A, (7, 15, 8, 9, 13)) - FR1 = add_indexlabels_as_tags(FR, (10, 11, 12, 13, 14)) - FD1 = add_indexlabels_as_tags(FD, (14, 15, 16, 17, 18)) + FL1 = add_indexlabels_as_tags(FL, (18, 7, 4, 2, 1)) + FU1 = add_indexlabels_as_tags(FU, (1, 3, 6, 9, 10)) + A1 = add_indexlabels_as_tags(A, (2, 17, 5, 3, 11)) + P1 = add_indexlabels_as_tags(P, (4, 16, 8, 5, 6, 12)) + A2 = add_indexlabels_as_tags(A, (7, 15, 8, 9, 13)) + FR1 = add_indexlabels_as_tags(FR, (10, 11, 12, 13, 14)) + FD1 = add_indexlabels_as_tags(FD, (14, 15, 16, 17, 18)) - return Timers.Timer(A1, A2, P1, FL1, FD1, FR1, FU1) do A1, A2, P1, FL1, FD1, FR1, FU1 - C = (((((FL1 * FU1) * A1) * P1) * dag(A2)) * FR1) * FD1 - return C[] + return Timers.Timer(A1, A2, P1, FL1, FD1, FR1, FU1) do A1, A2, P1, FL1, FD1, FR1, FU1 + C = (((((FL1 * FU1) * A1) * P1) * dag(A2)) * FR1) * FD1 + return C[] + end end -end -function mera_timer(T=Float64; Vmera) - Vmera′ = dag(Vmera) + function mera_timer(T = Float64; Vmera) + Vmera′ = dag(Vmera) - u = randomITensor(T, Vmera, Vmera, Vmera′, Vmera′) - w = randomITensor(T, Vmera, Vmera, Vmera′) - ρ = randomITensor(T, Vmera, Vmera, Vmera, Vmera′, Vmera′, Vmera′) - h = randomITensor(T, Vmera, Vmera, Vmera, Vmera′, Vmera′, Vmera′) + u = randomITensor(T, Vmera, Vmera, Vmera′, Vmera′) + w = randomITensor(T, Vmera, Vmera, Vmera′) + ρ = randomITensor(T, Vmera, Vmera, Vmera, Vmera′, Vmera′, Vmera′) + h = randomITensor(T, Vmera, Vmera, Vmera, Vmera′, Vmera′, Vmera′) - h1 = add_indexlabels_as_tags(h, (9, 3, 4, 5, 1, 2)) - u1 = add_indexlabels_as_tags(u, (1, 2, 7, 12)) - u2 = add_indexlabels_as_tags(u, (3, 4, 11, 13)) - u3 = add_indexlabels_as_tags(u, (8, 5, 15, 6)) - w1 = add_indexlabels_as_tags(w, (6, 7, 19)) - u4 = add_indexlabels_as_tags(u, (8, 9, 17, 10)) - w2 = add_indexlabels_as_tags(w, (10, 11, 22)) - w3 = add_indexlabels_as_tags(w, (12, 14, 20)) - w4 = add_indexlabels_as_tags(w, (13, 14, 23)) - ρ1 = add_indexlabels_as_tags(ρ, (18, 19, 20, 21, 22, 23)) - w5 = add_indexlabels_as_tags(w, (16, 15, 18)) - w6 = add_indexlabels_as_tags(w, (16, 17, 21)) + h1 = add_indexlabels_as_tags(h, (9, 3, 4, 5, 1, 2)) + u1 = add_indexlabels_as_tags(u, (1, 2, 7, 12)) + u2 = add_indexlabels_as_tags(u, (3, 4, 11, 13)) + u3 = add_indexlabels_as_tags(u, (8, 5, 15, 6)) + w1 = add_indexlabels_as_tags(w, (6, 7, 19)) + u4 = add_indexlabels_as_tags(u, (8, 9, 17, 10)) + w2 = add_indexlabels_as_tags(w, (10, 11, 22)) + w3 = add_indexlabels_as_tags(w, (12, 14, 20)) + w4 = add_indexlabels_as_tags(w, (13, 14, 23)) + ρ1 = add_indexlabels_as_tags(ρ, (18, 19, 20, 21, 22, 23)) + w5 = add_indexlabels_as_tags(w, (16, 15, 18)) + w6 = add_indexlabels_as_tags(w, (16, 17, 21)) - return Timers.Timer(u1, u2, u3, u4, w1, w2, w3, w4, w5, w6, ρ1, - h1) do u1, u2, u3, u4, w1, w2, w3, w4, w5, w6, ρ1, h1 - C = (((((((h1 * u1) * dag(u2)) * - (u3 * w1)) * - (dag(u4) * dag(w2))) * - ((w3 * dag(w4)) * ρ1)) * w5) * dag(w6)) - return C + return Timers.Timer( + u1, u2, u3, u4, w1, w2, w3, w4, w5, w6, ρ1, h1 + ) do u1, u2, u3, u4, w1, w2, w3, w4, w5, w6, ρ1, h1 + C = (((((((h1 * u1) * dag(u2)) * (u3 * w1)) * (dag(u4) * dag(w2))) * ((w3 * dag(w4)) * ρ1)) * w5) * dag(w6)) + return C + end end end -end diff --git a/benchmark/comparisons/plotmera.jl b/benchmark/comparisons/plotmera.jl index 8d1d517c2..1ed6c3fea 100644 --- a/benchmark/comparisons/plotmera.jl +++ b/benchmark/comparisons/plotmera.jl @@ -25,7 +25,7 @@ for j in 1:3 ymax = maximum(data_tensorkit) data_itensors = try readdlm("results/itensors" * s) - - readdlm("results/itensors" * s[1:(end - 4)] * "_gc.txt") + readdlm("results/itensors" * s[1:(end - 4)] * "_gc.txt") catch nothing end @@ -57,31 +57,36 @@ for j in 1:3 for i in 1:numplots Dmera = dims[j][1][i] - p = boxplot(data_tensorkit[:, i]; label="TensorKit.jl", markersize=M) + p = boxplot(data_tensorkit[:, i]; label = "TensorKit.jl", markersize = M) !isnothing(data_itensors) && - boxplot!(p, data_itensors[:, i]; label="ITensors.jl", markersize=M) + boxplot!(p, data_itensors[:, i]; label = "ITensors.jl", markersize = M) !isnothing(data_tenpy) && - boxplot!(p, data_tenpy[:, i]; label="Tenpy", markersize=M) + boxplot!(p, data_tenpy[:, i]; label = "Tenpy", markersize = M) !isnothing(data_tensornetwork) && - boxplot!(p, data_tensornetwork[:, i]; label="TensorNetwork", markersize=M) - plot!(p; yscale=:log10, yminorticks=true, xticks=[], xgrid=false, xshowaxis=false, - ylim=(ymin, ymax)) + boxplot!(p, data_tensornetwork[:, i]; label = "TensorNetwork", markersize = M) + plot!( + p; yscale = :log10, yminorticks = true, xticks = [], xgrid = false, xshowaxis = false, + ylim = (ymin, ymax) + ) if i == 1 - plot!(p; yguide=name[j], yguide_position=:left, yguidefontsize=10, - ytickfontsize=8, left_margin=6mm) - plot!(p; title="χ = $Dmera", titlefontsize=10) + plot!( + p; yguide = name[j], yguide_position = :left, yguidefontsize = 10, + ytickfontsize = 8, left_margin = 6mm + ) + plot!(p; title = "χ = $Dmera", titlefontsize = 10) else - plot!(p; title="$Dmera", titlefontsize=10) - plot!(p; left_margin=-5mm, yformatter=_ -> "", yshowaxis=false) + plot!(p; title = "$Dmera", titlefontsize = 10) + plot!(p; left_margin = -5mm, yformatter = _ -> "", yshowaxis = false) end if i == numplots && j == 3 - plot!(p; legendfontsize=10, legend=:bottomright) + plot!(p; legendfontsize = 10, legend = :bottomright) else - plot!(p; legend=false) + plot!(p; legend = false) end meraplots[i, j] = p end end -plotgrid = plot(meraplots...; layout=grid(3, numplots), link=:y, thickness_scaling=1, - size=(1500, 800)) +plotgrid = plot( + meraplots...; layout = grid(3, numplots), link = :y, thickness_scaling = 1, size = (1500, 800) +) savefig(plotgrid, "meraresults.pdf") diff --git a/benchmark/comparisons/plotmpo.jl b/benchmark/comparisons/plotmpo.jl index ed7a94319..239672cc1 100644 --- a/benchmark/comparisons/plotmpo.jl +++ b/benchmark/comparisons/plotmpo.jl @@ -57,31 +57,36 @@ for j in 1:3 Dmpo = dims[j][2][i] Dphys = dims[j][3][i] - p = boxplot(data_tensorkit[:, i]; label="TensorKit.jl", markersize=M) + p = boxplot(data_tensorkit[:, i]; label = "TensorKit.jl", markersize = M) !isnothing(data_itensors) && - boxplot!(p, data_itensors[:, i]; label="ITensors.jl", markersize=M) + boxplot!(p, data_itensors[:, i]; label = "ITensors.jl", markersize = M) !isnothing(data_tenpy) && - boxplot!(p, data_tenpy[:, i]; label="Tenpy", markersize=M) + boxplot!(p, data_tenpy[:, i]; label = "Tenpy", markersize = M) !isnothing(data_tensornetwork) && - boxplot!(p, data_tensornetwork[:, i]; label="TensorNetwork", markersize=M) - plot!(p; yscale=:log10, yminorticks=true, xticks=[], xgrid=false, xshowaxis=false, - ylim=(ymin, ymax)) + boxplot!(p, data_tensornetwork[:, i]; label = "TensorNetwork", markersize = M) + plot!( + p; yscale = :log10, yminorticks = true, xticks = [], xgrid = false, xshowaxis = false, + ylim = (ymin, ymax) + ) if i == 1 - plot!(p; title="(D, M, d) = ($Dmps, $Dmpo, $Dphys)", titlefontsize=10) - plot!(p; yguide=name[j], yguide_position=:left, yguidefontsize=10, - ytickfontsize=8, left_margin=6mm) + plot!(p; title = "(D, M, d) = ($Dmps, $Dmpo, $Dphys)", titlefontsize = 10) + plot!( + p; yguide = name[j], yguide_position = :left, yguidefontsize = 10, + ytickfontsize = 8, left_margin = 6mm + ) else - plot!(p; title="($Dmps, $Dmpo, $Dphys)", titlefontsize=10) - plot!(p; left_margin=-5mm, yformatter=_ -> "", yshowaxis=false) + plot!(p; title = "($Dmps, $Dmpo, $Dphys)", titlefontsize = 10) + plot!(p; left_margin = -5mm, yformatter = _ -> "", yshowaxis = false) end if i == numplots && j == 3 - plot!(p; legendfontsize=10, legend=:bottomright) + plot!(p; legendfontsize = 10, legend = :bottomright) else - plot!(p; legend=false) + plot!(p; legend = false) end mpoplots[i, j] = p end end -plotgrid = plot(mpoplots...; layout=grid(3, numplots), link=:y, thickness_scaling=1, - size=(1500, 800)) +plotgrid = plot( + mpoplots...; layout = grid(3, numplots), link = :y, thickness_scaling = 1, size = (1500, 800) +) savefig(plotgrid, "mporesults.pdf") diff --git a/benchmark/comparisons/plotpepo.jl b/benchmark/comparisons/plotpepo.jl index 40ce51161..ac73bd0ec 100644 --- a/benchmark/comparisons/plotpepo.jl +++ b/benchmark/comparisons/plotpepo.jl @@ -57,32 +57,39 @@ for j in 1:3 Dphys = dims[j][3][i] Denv = dims[j][4][i] - p = boxplot(data_tensorkit[:, i]; label="TensorKit.jl", markersize=M) + p = boxplot(data_tensorkit[:, i]; label = "TensorKit.jl", markersize = M) !isnothing(data_itensors) && - boxplot!(p, data_itensors[:, i]; label="ITensors.jl", markersize=M) + boxplot!(p, data_itensors[:, i]; label = "ITensors.jl", markersize = M) !isnothing(data_tenpy) && - boxplot!(p, data_tenpy[:, i]; label="Tenpy", markersize=M) + boxplot!(p, data_tenpy[:, i]; label = "Tenpy", markersize = M) !isnothing(data_tensornetwork) && - boxplot!(p, data_tensornetwork[:, i]; label="TensorNetwork", markersize=M) - plot!(p; yscale=:log10, yminorticks=true, xticks=[], xgrid=false, xshowaxis=false, - ylim=(ymin, ymax)) + boxplot!(p, data_tensornetwork[:, i]; label = "TensorNetwork", markersize = M) + plot!( + p; yscale = :log10, yminorticks = true, xticks = [], xgrid = false, xshowaxis = false, + ylim = (ymin, ymax) + ) if i == 1 - plot!(p; title="(D, M, d, χ) = ($Dpeps, $Dpepo, $Dphys, $Denv)", - titlefontsize=10) - plot!(p; yguide=name[j], yguide_position=:left, yguidefontsize=10, - ytickfontsize=8, left_margin=6mm) + plot!( + p; title = "(D, M, d, χ) = ($Dpeps, $Dpepo, $Dphys, $Denv)", + titlefontsize = 10 + ) + plot!( + p; yguide = name[j], yguide_position = :left, yguidefontsize = 10, + ytickfontsize = 8, left_margin = 6mm + ) else - plot!(p; title="($Dpeps, $Dpepo, $Dphys, $Denv)", titlefontsize=10) - plot!(p; left_margin=-5mm, yformatter=_ -> "", yshowaxis=false) + plot!(p; title = "($Dpeps, $Dpepo, $Dphys, $Denv)", titlefontsize = 10) + plot!(p; left_margin = -5mm, yformatter = _ -> "", yshowaxis = false) end if i == numplots && j == 3 - plot!(p; legendfontsize=10, legend=:bottomright) + plot!(p; legendfontsize = 10, legend = :bottomright) else - plot!(p; legend=false) + plot!(p; legend = false) end pepoplots[i, j] = p end end -plotgrid = plot(pepoplots...; layout=grid(3, numplots), link=:y, thickness_scaling=1, - size=(1500, 800)) +plotgrid = plot( + pepoplots...; layout = grid(3, numplots), link = :y, thickness_scaling = 1, size = (1500, 800) +) savefig(plotgrid, "peporesults.pdf") diff --git a/benchmark/comparisons/run_itensors.jl b/benchmark/comparisons/run_itensors.jl index 740325d88..d13f7095b 100644 --- a/benchmark/comparisons/run_itensors.jl +++ b/benchmark/comparisons/run_itensors.jl @@ -27,9 +27,9 @@ for i in 1:N Vmpo = Index(Dmpo) Vphys = Index(Dphys) - mpo_timer = ITensorsTimers.mpo_timer(; Vmps=Vmps, Vmpo=Vmpo, Vphys=Vphys) + mpo_timer = ITensorsTimers.mpo_timer(; Vmps = Vmps, Vmpo = Vmpo, Vphys = Vphys) - times, times_gc = mpo_timer(; outer=K) + times, times_gc = mpo_timer(; outer = K) mpo_triv_times[:, i] = times mpo_triv_times_gc[:, i] = times_gc @@ -54,9 +54,9 @@ for i in 1:N Vmpo = Index((QN(a, 2) => b for (a, b) in Dmpo)...) Vphys = Index((QN(a, 2) => b for (a, b) in Dphys)...) - mpo_timer = ITensorsTimers.mpo_timer(; Vmps=Vmps, Vmpo=Vmpo, Vphys=Vphys) + mpo_timer = ITensorsTimers.mpo_timer(; Vmps = Vmps, Vmpo = Vmpo, Vphys = Vphys) - times, times_gc = mpo_timer(; outer=K) + times, times_gc = mpo_timer(; outer = K) mpo_z2_times[:, i] = times mpo_z2_times_gc[:, i] = times_gc @@ -81,9 +81,9 @@ for i in 1:N Vmpo = Index((QN(a) => b for (a, b) in Dmpo)...) Vphys = Index((QN(a) => b for (a, b) in Dphys)...) - mpo_timer = ITensorsTimers.mpo_timer(; Vmps=Vmps, Vmpo=Vmpo, Vphys=Vphys) + mpo_timer = ITensorsTimers.mpo_timer(; Vmps = Vmps, Vmpo = Vmpo, Vphys = Vphys) - times, times_gc = mpo_timer(; outer=K) + times, times_gc = mpo_timer(; outer = K) mpo_u1_times[:, i] = times mpo_u1_times_gc[:, i] = times_gc @@ -112,10 +112,9 @@ for i in 1:N Vphys = Index(Dphys) Venv = Index(Denv) - pepo_timer = ITensorsTimers.pepo_timer(; Vpeps=Vpeps, Vpepo=Vpepo, Vphys=Vphys, - Venv=Venv) + pepo_timer = ITensorsTimers.pepo_timer(; Vpeps, Vpepo, Vphys, Venv) - times, times_gc = pepo_timer(; outer=K) + times, times_gc = pepo_timer(; outer = K) pepo_triv_times[:, i] = times pepo_triv_times_gc[:, i] = times_gc @@ -142,10 +141,9 @@ for i in 1:N Vphys = Index((QN(a, 2) => b for (a, b) in Dphys)...) Venv = Index((QN(a, 2) => b for (a, b) in Denv)...) - pepo_timer = ITensorsTimers.pepo_timer(; Vpeps=Vpeps, Vpepo=Vpepo, Vphys=Vphys, - Venv=Venv) + pepo_timer = ITensorsTimers.pepo_timer(; Vpeps, Vpepo, Vphys, Venv) - times, times_gc = pepo_timer(; outer=K) + times, times_gc = pepo_timer(; outer = K) pepo_z2_times[:, i] = times pepo_z2_times_gc[:, i] = times_gc @@ -172,10 +170,9 @@ for i in 1:N Vphys = Index((QN(a) => b for (a, b) in Dphys)...) Venv = Index((QN(a) => b for (a, b) in Denv)...) - pepo_timer = ITensorsTimers.pepo_timer(; Vpeps=Vpeps, Vpepo=Vpepo, Vphys=Vphys, - Venv=Venv) + pepo_timer = ITensorsTimers.pepo_timer(; Vpeps, Vpepo, Vphys, Venv) - times, times_gc = pepo_timer(; outer=K) + times, times_gc = pepo_timer(; outer = K) pepo_u1_times[:, i] = times pepo_u1_times_gc[:, i] = times_gc @@ -198,9 +195,9 @@ for i in 1:N Vmera = Index(Dmera) - mera_timer = ITensorsTimers.mera_timer(; Vmera=Vmera) + mera_timer = ITensorsTimers.mera_timer(; Vmera = Vmera) - times, times_gc = mera_timer(; outer=K) + times, times_gc = mera_timer(; outer = K) mera_triv_times[:, i] = times mera_triv_times_gc[:, i] = times_gc @@ -222,9 +219,9 @@ for i in 1:N Vmera = Index((QN(a, 2) => b for (a, b) in Dmera)...) - mera_timer = ITensorsTimers.mera_timer(; Vmera=Vmera) + mera_timer = ITensorsTimers.mera_timer(; Vmera = Vmera) - times, times_gc = mera_timer(; outer=K) + times, times_gc = mera_timer(; outer = K) mera_z2_times[:, i] = times mera_z2_times_gc[:, i] = times_gc @@ -246,9 +243,9 @@ for i in 1:N Vmera = Index((QN(a) => b for (a, b) in Dmera)...) - mera_timer = ITensorsTimers.mera_timer(; Vmera=Vmera) + mera_timer = ITensorsTimers.mera_timer(; Vmera = Vmera) - times, times_gc = mera_timer(; outer=K) + times, times_gc = mera_timer(; outer = K) mera_u1_times[:, i] = times mera_u1_times_gc[:, i] = times_gc diff --git a/benchmark/comparisons/run_tensorkit.jl b/benchmark/comparisons/run_tensorkit.jl index 430e2e926..8e13fb80b 100644 --- a/benchmark/comparisons/run_tensorkit.jl +++ b/benchmark/comparisons/run_tensorkit.jl @@ -5,7 +5,7 @@ using LinearAlgebra: LinearAlgebra LinearAlgebra.BLAS.set_num_threads(1) using TensorOperations: TensorOperations -TensorOperations.enable_cache(; maxrelsize=0.7) +TensorOperations.enable_cache(; maxrelsize = 0.7) using TensorKit: TensorKit using TensorKit: ℂ, Z2Space, U1Space using DelimitedFiles @@ -27,9 +27,9 @@ for i in 1:N Vmpo = ℂ^Dmpo Vphys = ℂ^Dphys - mpo_timer = TensorKitTimers.mpo_timer(; Vmps=Vmps, Vmpo=Vmpo, Vphys=Vphys) + mpo_timer = TensorKitTimers.mpo_timer(; Vmps = Vmps, Vmpo = Vmpo, Vphys = Vphys) - times, times_gc = mpo_timer(; outer=K) + times, times_gc = mpo_timer(; outer = K) mpo_triv_times[:, i] = times mpo_triv_times_gc[:, i] = times_gc empty!(TensorOperations.cache) @@ -55,9 +55,9 @@ for i in 1:N Vmpo = Z2Space((a => b for (a, b) in Dmpo)...) Vphys = Z2Space((a => b for (a, b) in Dphys)...) - mpo_timer = TensorKitTimers.mpo_timer(; Vmps=Vmps, Vmpo=Vmpo, Vphys=Vphys) + mpo_timer = TensorKitTimers.mpo_timer(; Vmps = Vmps, Vmpo = Vmpo, Vphys = Vphys) - times, times_gc = mpo_timer(; outer=K) + times, times_gc = mpo_timer(; outer = K) mpo_z2_times[:, i] = times mpo_z2_times_gc[:, i] = times_gc empty!(TensorOperations.cache) @@ -83,9 +83,9 @@ for i in 1:N Vmpo = U1Space((a => b for (a, b) in Dmpo)...) Vphys = U1Space((a => b for (a, b) in Dphys)...) - mpo_timer = TensorKitTimers.mpo_timer(; Vmps=Vmps, Vmpo=Vmpo, Vphys=Vphys) + mpo_timer = TensorKitTimers.mpo_timer(; Vmps = Vmps, Vmpo = Vmpo, Vphys = Vphys) - times, times_gc = mpo_timer(; outer=K) + times, times_gc = mpo_timer(; outer = K) mpo_u1_times[:, i] = times mpo_u1_times_gc[:, i] = times_gc empty!(TensorOperations.cache) @@ -115,10 +115,9 @@ for i in 1:N Vphys = ℂ^Dphys Venv = ℂ^Denv - pepo_timer = TensorKitTimers.pepo_timer(; Vpeps=Vpeps, Vpepo=Vpepo, Vphys=Vphys, - Venv=Venv) + pepo_timer = TensorKitTimers.pepo_timer(; Vpeps, Vpepo, Vphys, Venv) - times, times_gc = pepo_timer(; outer=K) + times, times_gc = pepo_timer(; outer = K) pepo_triv_times[:, i] = times pepo_triv_times_gc[:, i] = times_gc empty!(TensorOperations.cache) @@ -146,10 +145,9 @@ for i in 1:N Vphys = Z2Space((a => b for (a, b) in Dphys)...) Venv = Z2Space((a => b for (a, b) in Denv)...) - pepo_timer = TensorKitTimers.pepo_timer(; Vpeps=Vpeps, Vpepo=Vpepo, Vphys=Vphys, - Venv=Venv) + pepo_timer = TensorKitTimers.pepo_timer(; Vpeps, Vpepo, Vphys, Venv) - times, times_gc = pepo_timer(; outer=K) + times, times_gc = pepo_timer(; outer = K) pepo_z2_times[:, i] = times pepo_z2_times_gc[:, i] = times_gc empty!(TensorOperations.cache) @@ -177,10 +175,9 @@ for i in 1:N Vphys = U1Space((a => b for (a, b) in Dphys)...) Venv = U1Space((a => b for (a, b) in Denv)...) - pepo_timer = TensorKitTimers.pepo_timer(; Vpeps=Vpeps, Vpepo=Vpepo, Vphys=Vphys, - Venv=Venv) + pepo_timer = TensorKitTimers.pepo_timer(; Vpeps, Vpepo, Vphys, Venv) - times, times_gc = pepo_timer(; outer=K) + times, times_gc = pepo_timer(; outer = K) pepo_u1_times[:, i] = times pepo_u1_times_gc[:, i] = times_gc empty!(TensorOperations.cache) @@ -204,9 +201,9 @@ for i in 1:N Vmera = ℂ^Dmera - mera_timer = TensorKitTimers.mera_timer(; Vmera=Vmera) + mera_timer = TensorKitTimers.mera_timer(; Vmera = Vmera) - times, times_gc = mera_timer(; outer=K) + times, times_gc = mera_timer(; outer = K) mera_triv_times[:, i] = times mera_triv_times_gc[:, i] = times_gc @@ -229,9 +226,9 @@ for i in 1:N Vmera = Z2Space((a => b for (a, b) in Dmera)...) - mera_timer = TensorKitTimers.mera_timer(; Vmera=Vmera) + mera_timer = TensorKitTimers.mera_timer(; Vmera = Vmera) - times, times_gc = mera_timer(; outer=K) + times, times_gc = mera_timer(; outer = K) mera_z2_times[:, i] = times mera_z2_times_gc[:, i] = times_gc @@ -254,9 +251,9 @@ for i in 1:N Vmera = U1Space((a => b for (a, b) in Dmera)...) - mera_timer = TensorKitTimers.mera_timer(; Vmera=Vmera) + mera_timer = TensorKitTimers.mera_timer(; Vmera = Vmera) - times, times_gc = mera_timer(; outer=K) + times, times_gc = mera_timer(; outer = K) mera_u1_times[:, i] = times mera_u1_times_gc[:, i] = times_gc diff --git a/benchmark/comparisons/tensorkit_timers.jl b/benchmark/comparisons/tensorkit_timers.jl index 020783c31..161df1842 100644 --- a/benchmark/comparisons/tensorkit_timers.jl +++ b/benchmark/comparisons/tensorkit_timers.jl @@ -1,48 +1,53 @@ include("benchtools.jl") module TensorKitTimers -using TensorKit -using ..Timers: Timers + using TensorKit + using ..Timers: Timers -function mpo_timer(f=randn, T=Float64; Vmpo, Vmps, Vphys) - A = Tensor(f, T, Vmps ⊗ Vphys ⊗ Vmps') - M = Tensor(f, T, Vmpo ⊗ Vphys ⊗ Vphys' ⊗ Vmpo') - FL = Tensor(f, T, Vmps ⊗ Vmpo' ⊗ Vmps') - FR = Tensor(f, T, Vmps ⊗ Vmpo ⊗ Vmps') + function mpo_timer(f = randn, T = Float64; Vmpo, Vmps, Vphys) + A = Tensor(f, T, Vmps ⊗ Vphys ⊗ Vmps') + M = Tensor(f, T, Vmpo ⊗ Vphys ⊗ Vphys' ⊗ Vmpo') + FL = Tensor(f, T, Vmps ⊗ Vmpo' ⊗ Vmps') + FR = Tensor(f, T, Vmps ⊗ Vmpo ⊗ Vmps') - return Timers.Timer(A, M, FL, FR) do A, M, FL, FR - @tensor C = FL[4, 2, 1] * A[1, 3, 6] * M[2, 5, 3, 7] * conj(A[4, 5, 8]) * - FR[6, 7, 8] - return C + return Timers.Timer(A, M, FL, FR) do A, M, FL, FR + @tensor C = FL[4, 2, 1] * A[1, 3, 6] * M[2, 5, 3, 7] * conj(A[4, 5, 8]) * + FR[6, 7, 8] + return C + end end -end -function pepo_timer(f=randn, T=Float64; Vpepo, Vpeps, Venv, Vphys) - A = Tensor(f, T, Vpeps ⊗ Vpeps ⊗ Vphys ⊗ Vpeps' ⊗ Vpeps') - P = Tensor(f, T, Vpepo ⊗ Vpepo ⊗ Vphys ⊗ Vphys' ⊗ Vpepo' ⊗ Vpepo') - FL = Tensor(f, T, Venv ⊗ Vpeps ⊗ Vpepo' ⊗ Vpeps' ⊗ Venv') - FD = Tensor(f, T, Venv ⊗ Vpeps ⊗ Vpepo' ⊗ Vpeps' ⊗ Venv') - FR = Tensor(f, T, Venv ⊗ Vpeps ⊗ Vpepo ⊗ Vpeps' ⊗ Venv') - FU = Tensor(f, T, Venv ⊗ Vpeps ⊗ Vpepo ⊗ Vpeps' ⊗ Venv') - return Timers.Timer(A, P, FL, FD, FR, FU) do A, P, FL, FD, FR, FU - @tensor C = FL[18, 7, 4, 2, 1] * FU[1, 3, 6, 9, 10] * - A[2, 17, 5, 3, 11] * P[4, 16, 8, 5, 6, 12] * conj(A[7, 15, 8, 9, 13]) * - FR[10, 11, 12, 13, 14] * FD[14, 15, 16, 17, 18] - return C + function pepo_timer(f = randn, T = Float64; Vpepo, Vpeps, Venv, Vphys) + A = Tensor(f, T, Vpeps ⊗ Vpeps ⊗ Vphys ⊗ Vpeps' ⊗ Vpeps') + P = Tensor(f, T, Vpepo ⊗ Vpepo ⊗ Vphys ⊗ Vphys' ⊗ Vpepo' ⊗ Vpepo') + FL = Tensor(f, T, Venv ⊗ Vpeps ⊗ Vpepo' ⊗ Vpeps' ⊗ Venv') + FD = Tensor(f, T, Venv ⊗ Vpeps ⊗ Vpepo' ⊗ Vpeps' ⊗ Venv') + FR = Tensor(f, T, Venv ⊗ Vpeps ⊗ Vpepo ⊗ Vpeps' ⊗ Venv') + FU = Tensor(f, T, Venv ⊗ Vpeps ⊗ Vpepo ⊗ Vpeps' ⊗ Venv') + return Timers.Timer(A, P, FL, FD, FR, FU) do A, P, FL, FD, FR, FU + @tensor C = FL[18, 7, 4, 2, 1] * FU[1, 3, 6, 9, 10] * + A[2, 17, 5, 3, 11] * P[4, 16, 8, 5, 6, 12] * conj(A[7, 15, 8, 9, 13]) * + FR[10, 11, 12, 13, 14] * FD[14, 15, 16, 17, 18] + return C + end end -end -function mera_timer(f=randn, T=Float64; Vmera) - u = Tensor(f, T, Vmera ⊗ Vmera ⊗ Vmera' ⊗ Vmera') - w = Tensor(f, T, Vmera ⊗ Vmera ⊗ Vmera') - ρ = Tensor(f, T, Vmera ⊗ Vmera ⊗ Vmera ⊗ Vmera' ⊗ Vmera' ⊗ Vmera') - h = Tensor(f, T, Vmera ⊗ Vmera ⊗ Vmera ⊗ Vmera' ⊗ Vmera' ⊗ Vmera') - return Timers.Timer(u, w, ρ, h) do u, w, ρ, h - @tensor C = (((((((h[9, 3, 4, 5, 1, 2] * u[1, 2, 7, 12]) * conj(u[3, 4, 11, 13])) * - (u[8, 5, 15, 6] * w[6, 7, 19])) * - (conj(u[8, 9, 17, 10]) * conj(w[10, 11, 22]))) * - ((w[12, 14, 20] * conj(w[13, 14, 23])) * ρ[18, 19, 20, 21, 22, 23])) * - w[16, 15, 18]) * conj(w[16, 17, 21])) - return C + function mera_timer(f = randn, T = Float64; Vmera) + u = Tensor(f, T, Vmera ⊗ Vmera ⊗ Vmera' ⊗ Vmera') + w = Tensor(f, T, Vmera ⊗ Vmera ⊗ Vmera') + ρ = Tensor(f, T, Vmera ⊗ Vmera ⊗ Vmera ⊗ Vmera' ⊗ Vmera' ⊗ Vmera') + h = Tensor(f, T, Vmera ⊗ Vmera ⊗ Vmera ⊗ Vmera' ⊗ Vmera' ⊗ Vmera') + return Timers.Timer(u, w, ρ, h) do u, w, ρ, h + @tensor C = ( + ( + ( + ( + (((h[9, 3, 4, 5, 1, 2] * u[1, 2, 7, 12]) * conj(u[3, 4, 11, 13])) * (u[8, 5, 15, 6] * w[6, 7, 19])) * + (conj(u[8, 9, 17, 10]) * conj(w[10, 11, 22])) + ) * ((w[12, 14, 20] * conj(w[13, 14, 23])) * ρ[18, 19, 20, 21, 22, 23]) + ) * w[16, 15, 18] + ) * conj(w[16, 17, 21]) + ) + return C + end end end -end diff --git a/docs/make.jl b/docs/make.jl index 1ad55321d..5a7f2e8db 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -5,23 +5,32 @@ using TensorKit.TensorKitSectors using TensorKit.MatrixAlgebraKit using DocumenterInterLinks -links = InterLinks("MatrixAlgebraKit" => "https://quantumkithub.github.io/MatrixAlgebraKit.jl/stable/", - "TensorOperations" => "https://quantumkithub.github.io/TensorOperations.jl/stable/") +links = InterLinks( + "MatrixAlgebraKit" => "https://quantumkithub.github.io/MatrixAlgebraKit.jl/stable/", + "TensorOperations" => "https://quantumkithub.github.io/TensorOperations.jl/stable/" +) -pages = ["Home" => "index.md", - "Manual" => ["man/intro.md", "man/tutorial.md", "man/categories.md", - "man/spaces.md", "man/sectors.md", "man/tensors.md"], - "Library" => ["lib/sectors.md", "lib/spaces.md", "lib/tensors.md"], - "Index" => ["index/index.md"]] +pages = [ + "Home" => "index.md", + "Manual" => [ + "man/intro.md", "man/tutorial.md", "man/categories.md", + "man/spaces.md", "man/sectors.md", "man/tensors.md", + ], + "Library" => ["lib/sectors.md", "lib/spaces.md", "lib/tensors.md"], + "Index" => ["index/index.md"], +] -makedocs(; modules=[TensorKit, TensorKitSectors], - sitename="TensorKit.jl", - authors="Jutho Haegeman", - warnonly=[:missing_docs, :cross_references], - format=Documenter.HTML(; prettyurls=true, mathengine=MathJax(), - assets=["assets/custom.css"]), - pages=pages, - pagesonly=true, - plugins=[links]) +makedocs(; + modules = [TensorKit, TensorKitSectors], + sitename = "TensorKit.jl", + authors = "Jutho Haegeman", + warnonly = [:missing_docs, :cross_references], + format = Documenter.HTML(; + prettyurls = true, mathengine = MathJax(), assets = ["assets/custom.css"] + ), + pages = pages, + pagesonly = true, + plugins = [links] +) -deploydocs(; repo="github.com/QuantumKitHub/TensorKit.jl.git", push_preview=true) +deploydocs(; repo = "github.com/QuantumKitHub/TensorKit.jl.git", push_preview = true) diff --git a/ext/TensorKitChainRulesCoreExt/constructors.jl b/ext/TensorKitChainRulesCoreExt/constructors.jl index f5a8c007c..100c255d2 100644 --- a/ext/TensorKitChainRulesCoreExt/constructors.jl +++ b/ext/TensorKitChainRulesCoreExt/constructors.jl @@ -16,8 +16,7 @@ end # -- as a result, requires quantum dimensions to keep inner product the same: # ⟨Δdata, ∂data⟩ = ⟨Δtensor, ∂tensor⟩ = ∑_c d_c ⟨Δtensor_c, ∂tensor_c⟩ # ⟹ Δdata = d_c Δtensor_c -function ChainRulesCore.rrule(::Type{TensorMap{T}}, data::DenseVector, - V::TensorMapSpace) where {T} +function ChainRulesCore.rrule(::Type{TensorMap{T}}, data::DenseVector, V::TensorMapSpace) where {T} t = TensorMap{T}(data, V) P = ProjectTo(data) function TensorMap_pullback(Δt_) @@ -31,8 +30,7 @@ function ChainRulesCore.rrule(::Type{TensorMap{T}}, data::DenseVector, return t, TensorMap_pullback end -function ChainRulesCore.rrule(::Type{<:DiagonalTensorMap}, data::DenseVector, args...; - kwargs...) +function ChainRulesCore.rrule(::Type{<:DiagonalTensorMap}, data::DenseVector, args...; kwargs...) D = DiagonalTensorMap(data, args...; kwargs...) P = ProjectTo(data) function DiagonalTensorMap_pullback(Δt_) @@ -77,8 +75,7 @@ function ChainRulesCore.rrule(::typeof(Base.getproperty), t::TensorMap, prop::Sy end end -function ChainRulesCore.rrule(::typeof(Base.getproperty), t::DiagonalTensorMap, - prop::Symbol) +function ChainRulesCore.rrule(::typeof(Base.getproperty), t::DiagonalTensorMap, prop::Symbol) if prop === :data function getdata_pullback(Δdata) # unclear if we're allowed to modify/take ownership of the input @@ -101,30 +98,31 @@ function ChainRulesCore.rrule(::typeof(Base.copy), t::AbstractTensorMap) return copy(t), copy_pullback end -function ChainRulesCore.rrule(::typeof(TensorKit.copy_oftype), t::AbstractTensorMap, - T::Type{<:Number}) +function ChainRulesCore.rrule( + ::typeof(TensorKit.copy_oftype), t::AbstractTensorMap, T::Type{<:Number} + ) project = ProjectTo(t) copy_oftype_pullback(Δt) = NoTangent(), project(unthunk(Δt)), NoTangent() return TensorKit.copy_oftype(t, T), copy_oftype_pullback end -function ChainRulesCore.rrule(::typeof(TensorKit.permutedcopy_oftype), t::AbstractTensorMap, - T::Type{<:Number}, p::Index2Tuple) +function ChainRulesCore.rrule( + ::typeof(TensorKit.permutedcopy_oftype), t::AbstractTensorMap, T::Type{<:Number}, p::Index2Tuple + ) project = ProjectTo(t) function permutedcopy_oftype_pullback(Δt) invp = TensorKit._canonicalize(TupleTools.invperm(linearize(p)), t) return NoTangent(), project(TensorKit.permute(unthunk(Δt), invp)), NoTangent(), - NoTangent() + NoTangent() end return TensorKit.permutedcopy_oftype(t, T, p), permutedcopy_oftype_pullback end -function ChainRulesCore.rrule(::typeof(Base.convert), T::Type{<:Array}, - t::AbstractTensorMap) +function ChainRulesCore.rrule(::typeof(Base.convert), T::Type{<:Array}, t::AbstractTensorMap) A = convert(T, t) function convert_pullback(ΔA) # use constructor to (unconditionally) project back onto symmetric subspace - ∂t = TensorMap(unthunk(ΔA), codomain(t), domain(t); tol=Inf) + ∂t = TensorMap(unthunk(ΔA), codomain(t), domain(t); tol = Inf) return NoTangent(), NoTangent(), ∂t end return A, convert_pullback @@ -145,8 +143,7 @@ function ChainRulesCore.rrule(::typeof(Base.convert), ::Type{Dict}, t::AbstractT end return out, convert_pullback end -function ChainRulesCore.rrule(::typeof(Base.convert), ::Type{TensorMap}, - t::Dict{Symbol,Any}) +function ChainRulesCore.rrule(::typeof(Base.convert), ::Type{TensorMap}, t::Dict{Symbol, Any}) return convert(TensorMap, t), v -> (NoTangent(), NoTangent(), convert(Dict, v)) end diff --git a/ext/TensorKitChainRulesCoreExt/linalg.jl b/ext/TensorKitChainRulesCoreExt/linalg.jl index e52daf5a5..af0c7f386 100644 --- a/ext/TensorKitChainRulesCoreExt/linalg.jl +++ b/ext/TensorKitChainRulesCoreExt/linalg.jl @@ -34,9 +34,10 @@ function ChainRulesCore.rrule(::typeof(⊗), A::AbstractTensorMap, B::AbstractTe # TODO: this rule is probably better written in terms of inner products, # using planarcontract and adjoint tensormaps would remove the twists. ΔC = unthunk(ΔC_) - pΔC = ((codomainind(A)..., (domainind(A) .+ numout(B))...), - ((codomainind(B) .+ numout(A))..., - (domainind(B) .+ (numin(A) + numout(A)))...)) + pΔC = ( + (codomainind(A)..., (domainind(A) .+ numout(B))...), + ((codomainind(B) .+ numout(A))..., (domainind(B) .+ (numin(A) + numout(A)))...), + ) dA_ = @thunk let ipA = (codomainind(A), domainind(A)) pB = (allind(B), ()) @@ -58,13 +59,14 @@ function ChainRulesCore.rrule(::typeof(⊗), A::AbstractTensorMap, B::AbstractTe return C, otimes_pullback end -function ChainRulesCore.rrule(::typeof(permute), tsrc::AbstractTensorMap, p::Index2Tuple; - copy::Bool=false) +function ChainRulesCore.rrule( + ::typeof(permute), tsrc::AbstractTensorMap, p::Index2Tuple; copy::Bool = false + ) function permute_pullback(Δtdst) invp = TensorKit._canonicalize(TupleTools.invperm(linearize(p)), tsrc) - return NoTangent(), permute(unthunk(Δtdst), invp; copy=true), NoTangent() + return NoTangent(), permute(unthunk(Δtdst), invp; copy = true), NoTangent() end - return permute(tsrc, p; copy=true), permute_pullback + return permute(tsrc, p; copy = true), permute_pullback end function ChainRulesCore.rrule(::typeof(tr), A::AbstractTensorMap) @@ -77,15 +79,15 @@ function ChainRulesCore.rrule(::typeof(adjoint), A::AbstractTensorMap) return adjoint(A), adjoint_pullback end -function ChainRulesCore.rrule(::typeof(twist), A::AbstractTensorMap, is; inv::Bool=false) +function ChainRulesCore.rrule(::typeof(twist), A::AbstractTensorMap, is; inv::Bool = false) tA = twist(A, is; inv) - twist_pullback(ΔA) = NoTangent(), twist(unthunk(ΔA), is; inv=!inv), NoTangent() + twist_pullback(ΔA) = NoTangent(), twist(unthunk(ΔA), is; inv = !inv), NoTangent() return tA, twist_pullback end -function ChainRulesCore.rrule(::typeof(flip), A::AbstractTensorMap, is; inv::Bool=false) +function ChainRulesCore.rrule(::typeof(flip), A::AbstractTensorMap, is; inv::Bool = false) tA = flip(A, is; inv) - flip_pullback(ΔA) = NoTangent(), flip(unthunk(ΔA), is; inv=!inv), NoTangent() + flip_pullback(ΔA) = NoTangent(), flip(unthunk(ΔA), is; inv = !inv), NoTangent() return tA, flip_pullback end @@ -94,7 +96,7 @@ function ChainRulesCore.rrule(::typeof(dot), a::AbstractTensorMap, b::AbstractTe return dot(a, b), dot_pullback end -function ChainRulesCore.rrule(::typeof(norm), a::AbstractTensorMap, p::Real=2) +function ChainRulesCore.rrule(::typeof(norm), a::AbstractTensorMap, p::Real = 2) p == 2 || error("currently only implemented for p = 2") n = norm(a, p) function norm_pullback(Δn) @@ -122,13 +124,14 @@ function ChainRulesCore.rrule(::typeof(imag), a::AbstractTensorMap) function imag_pullback(Δa) Δa′ = unthunk(Δa) return NoTangent(), - eltype(a) <: Real ? ZeroTangent() : complex(zerovector(Δa′), Δa′) + eltype(a) <: Real ? ZeroTangent() : complex(zerovector(Δa′), Δa′) end return a_imag, imag_pullback end -function ChainRulesCore.rrule(cfg::RuleConfig{>:HasReverseMode}, ::typeof(exp), - A::AbstractTensorMap) +function ChainRulesCore.rrule( + cfg::RuleConfig{>:HasReverseMode}, ::typeof(exp), A::AbstractTensorMap + ) domain(A) == codomain(A) || error("Exponential of a tensor only exist when domain == codomain.") P_A = ProjectTo(A) @@ -151,11 +154,14 @@ end # define rrules for matrix functions for DiagonalTensorMap, since they access data directly. for f in - (:exp, :cos, :sin, :tan, :cot, :cosh, :sinh, :tanh, :coth, :atan, :acot, :asinh, :sqrt, - :log, :asin, :acos, :acosh, :atanh, :acoth) + ( + :exp, :cos, :sin, :tan, :cot, :cosh, :sinh, :tanh, :coth, :atan, :acot, :asinh, :sqrt, + :log, :asin, :acos, :acosh, :atanh, :acoth, + ) f_pullback = Symbol(f, :_pullback) - @eval function ChainRulesCore.rrule(cfg::RuleConfig{>:HasReverseMode}, ::typeof($f), - t::DiagonalTensorMap) + @eval function ChainRulesCore.rrule( + cfg::RuleConfig{>:HasReverseMode}, ::typeof($f), t::DiagonalTensorMap + ) P = ProjectTo(t) # unsure if this is necessary, should already be in pullback d, pullback = rrule_via_ad(cfg, broadcast, $f, t.data) function $f_pullback(Δd_) diff --git a/ext/TensorKitChainRulesCoreExt/tensoroperations.jl b/ext/TensorKitChainRulesCoreExt/tensoroperations.jl index f3dac3c5b..3809e7fbf 100644 --- a/ext/TensorKitChainRulesCoreExt/tensoroperations.jl +++ b/ext/TensorKitChainRulesCoreExt/tensoroperations.jl @@ -1,7 +1,9 @@ -function ChainRulesCore.rrule(::typeof(TensorOperations.tensoradd!), - C::AbstractTensorMap, - A::AbstractTensorMap, pA::Index2Tuple, conjA::Bool, - α::Number, β::Number, ba...) +function ChainRulesCore.rrule( + ::typeof(TensorOperations.tensoradd!), + C::AbstractTensorMap, + A::AbstractTensorMap, pA::Index2Tuple, conjA::Bool, + α::Number, β::Number, ba... + ) C′ = tensoradd!(copy(C), A, pA, conjA, α, β, ba...) projectA = ProjectTo(A) @@ -27,10 +29,13 @@ function ChainRulesCore.rrule(::typeof(TensorOperations.tensoradd!), # but for symmetric tensors an intermediate object will anyways be created # and then it might be more efficient to use an addition and inner product tΔC = twist(ΔC, filter(x -> isdual(space(ΔC, x)), allind(ΔC))) - _dα = tensorscalar(tensorcontract(A, ((), linearize(pA)), - !conjA, tΔC, - (trivtuple(TO.numind(pA)), ()), false, - ((), ()), One(), ba...)) + _dα = tensorscalar( + tensorcontract( + A, ((), linearize(pA)), !conjA, + tΔC, (trivtuple(TO.numind(pA)), ()), false, + ((), ()), One(), ba... + ) + ) return projectα(_dα) end dβ = @thunk projectβ(inner(C, ΔC)) @@ -41,12 +46,14 @@ function ChainRulesCore.rrule(::typeof(TensorOperations.tensoradd!), return C′, pullback end -function ChainRulesCore.rrule(::typeof(TensorOperations.tensorcontract!), - C::AbstractTensorMap, - A::AbstractTensorMap, pA::Index2Tuple, conjA::Bool, - B::AbstractTensorMap, pB::Index2Tuple, conjB::Bool, - pAB::Index2Tuple, - α::Number, β::Number, ba...) +function ChainRulesCore.rrule( + ::typeof(TensorOperations.tensorcontract!), + C::AbstractTensorMap, + A::AbstractTensorMap, pA::Index2Tuple, conjA::Bool, + B::AbstractTensorMap, pB::Index2Tuple, conjB::Bool, + pAB::Index2Tuple, + α::Number, β::Number, ba... + ) C′ = tensorcontract!(copy(C), A, pA, conjA, B, pB, conjB, pAB, α, β, ba...) projectA = ProjectTo(A) @@ -67,15 +74,23 @@ function ChainRulesCore.rrule(::typeof(TensorOperations.tensorcontract!), conjB′ = conjA ? conjB : !conjB TA = promote_contract(scalartype(ΔC), scalartype(B), scalartype(α)) # TODO: allocator - tB = twist(B, - TupleTools.vcat(filter(x -> !isdual(space(B, x)), pB[1]), - filter(x -> isdual(space(B, x)), pB[2]))) - _dA = tensoralloc_contract(TA, ΔC, pΔC, conjΔC, tB, reverse(pB), conjB′, ipA, - Val(false)) - _dA = tensorcontract!(_dA, - ΔC, pΔC, conjΔC, - tB, reverse(pB), conjB′, ipA, - conjA ? α : conj(α), Zero(), ba...) + tB = twist( + B, + TupleTools.vcat( + filter(x -> !isdual(space(B, x)), pB[1]), + filter(x -> isdual(space(B, x)), pB[2]) + ) + ) + _dA = tensoralloc_contract( + TA, ΔC, pΔC, conjΔC, tB, reverse(pB), conjB′, ipA, Val(false) + ) + _dA = tensorcontract!( + _dA, + ΔC, pΔC, conjΔC, + tB, reverse(pB), conjB′, + ipA, + conjA ? α : conj(α), Zero(), ba... + ) return projectA(_dA) end dB = @thunk let @@ -84,15 +99,23 @@ function ChainRulesCore.rrule(::typeof(TensorOperations.tensorcontract!), conjA′ = conjB ? conjA : !conjA TB = promote_contract(scalartype(ΔC), scalartype(A), scalartype(α)) # TODO: allocator - tA = twist(A, - TupleTools.vcat(filter(x -> isdual(space(A, x)), pA[1]), - filter(x -> !isdual(space(A, x)), pA[2]))) - _dB = tensoralloc_contract(TB, tA, reverse(pA), conjA′, ΔC, pΔC, conjΔC, ipB, - Val(false)) - _dB = tensorcontract!(_dB, - tA, reverse(pA), conjA′, - ΔC, pΔC, conjΔC, ipB, - conjB ? α : conj(α), Zero(), ba...) + tA = twist( + A, + TupleTools.vcat( + filter(x -> isdual(space(A, x)), pA[1]), + filter(x -> !isdual(space(A, x)), pA[2]) + ) + ) + _dB = tensoralloc_contract( + TB, tA, reverse(pA), conjA′, ΔC, pΔC, conjΔC, ipB, Val(false) + ) + _dB = tensorcontract!( + _dB, + tA, reverse(pA), conjA′, + ΔC, pΔC, conjΔC, + ipB, + conjB ? α : conj(α), Zero(), ba... + ) return projectB(_dB) end dα = @thunk let @@ -103,18 +126,20 @@ function ChainRulesCore.rrule(::typeof(TensorOperations.tensorcontract!), dβ = @thunk projectβ(inner(C, ΔC)) dba = map(_ -> NoTangent(), ba) return NoTangent(), dC, - dA, NoTangent(), NoTangent(), - dB, NoTangent(), NoTangent(), - NoTangent(), - dα, dβ, dba... + dA, NoTangent(), NoTangent(), + dB, NoTangent(), NoTangent(), + NoTangent(), + dα, dβ, dba... end return C′, pullback end -function ChainRulesCore.rrule(::typeof(TensorOperations.tensortrace!), - C::AbstractTensorMap, A::AbstractTensorMap, - p::Index2Tuple, q::Index2Tuple, conjA::Bool, - α::Number, β::Number, ba...) +function ChainRulesCore.rrule( + ::typeof(TensorOperations.tensortrace!), + C::AbstractTensorMap, + A::AbstractTensorMap, p::Index2Tuple, q::Index2Tuple, conjA::Bool, + α::Number, β::Number, ba... + ) C′ = tensortrace!(copy(C), A, p, q, conjA, α, β, ba...) projectA = ProjectTo(A) @@ -135,8 +160,9 @@ function ChainRulesCore.rrule(::typeof(TensorOperations.tensortrace!), TA = promote_scale(ΔC, α) # TODO: allocator _dA = tensoralloc_contract(TA, ΔC, pΔC, conjA, E, pE, conjA, pdA, Val(false)) - _dA = tensorproduct!(_dA, ΔC, pΔC, conjA, E, pE, conjA, pdA, - conjA ? α : conj(α), Zero(), ba...) + _dA = tensorproduct!( + _dA, ΔC, pΔC, conjA, E, pE, conjA, pdA, conjA ? α : conj(α), Zero(), ba... + ) return projectA(_dA) end dα = @thunk let diff --git a/ext/TensorKitChainRulesCoreExt/utility.jl b/ext/TensorKitChainRulesCoreExt/utility.jl index 2a9f11d57..85a444422 100644 --- a/ext/TensorKitChainRulesCoreExt/utility.jl +++ b/ext/TensorKitChainRulesCoreExt/utility.jl @@ -6,24 +6,24 @@ Base.@constprop :aggressive function _repartition(p::IndexTuple, N₁::Int) length(p) >= N₁ || throw(ArgumentError("cannot repartition $(typeof(p)) to $N₁, $(length(p) - N₁)")) return TupleTools.getindices(p, trivtuple(N₁)), - TupleTools.getindices(p, trivtuple(length(p) - N₁) .+ N₁) + TupleTools.getindices(p, trivtuple(length(p) - N₁) .+ N₁) end Base.@constprop :aggressive function _repartition(p::Index2Tuple, N₁::Int) return _repartition(linearize(p), N₁) end -function _repartition(p::Union{IndexTuple,Index2Tuple}, ::Index2Tuple{N₁}) where {N₁} +function _repartition(p::Union{IndexTuple, Index2Tuple}, ::Index2Tuple{N₁}) where {N₁} return _repartition(p, N₁) end -function _repartition(p::Union{IndexTuple,Index2Tuple}, - t::AbstractTensorMap) +function _repartition(p::Union{IndexTuple, Index2Tuple}, t::AbstractTensorMap) return _repartition(p, TensorKit.numout(t)) end TensorKit.block(t::ZeroTangent, c::Sector) = t -ChainRulesCore.ProjectTo(::T) where {T<:AbstractTensorMap} = ProjectTo{T}() -function (::ProjectTo{T1})(x::T2) where {S,N1,N2,T1<:AbstractTensorMap{<:Any,S,N1,N2}, - T2<:AbstractTensorMap{<:Any,S,N1,N2}} +ChainRulesCore.ProjectTo(::T) where {T <: AbstractTensorMap} = ProjectTo{T}() +function (::ProjectTo{T1})(x::T2) where { + S, N1, N2, T1 <: AbstractTensorMap{<:Any, S, N1, N2}, T2 <: AbstractTensorMap{<:Any, S, N1, N2}, + } T1 === T2 && return x y = similar(x, scalartype(T1)) for (c, b) in blocks(y) @@ -33,11 +33,11 @@ function (::ProjectTo{T1})(x::T2) where {S,N1,N2,T1<:AbstractTensorMap{<:Any,S,N return y end -function (::ProjectTo{DiagonalTensorMap{T,S,A}})(x::AbstractTensorMap) where {T,S,A} - x isa DiagonalTensorMap{T,S,A} && return x +function (::ProjectTo{DiagonalTensorMap{T, S, A}})(x::AbstractTensorMap) where {T, S, A} + x isa DiagonalTensorMap{T, S, A} && return x V = space(x, 1) space(x) == (V ← V) || throw(SpaceMismatch()) - y = DiagonalTensorMap{T,S,A}(undef, V) + y = DiagonalTensorMap{T, S, A}(undef, V) for (c, b) in blocks(y) p = ProjectTo(b) b .= p(block(x, c)) diff --git a/src/TensorKit.jl b/src/TensorKit.jl index ce6c7697b..e80de65f5 100644 --- a/src/TensorKit.jl +++ b/src/TensorKit.jl @@ -10,7 +10,7 @@ module TensorKit # Types: export Sector, AbstractIrrep, Irrep export FusionStyle, UniqueFusion, MultipleFusion, MultiplicityFreeFusion, - SimpleFusion, GenericFusion + SimpleFusion, GenericFusion export BraidingStyle, SymmetricBraiding, Bosonic, Fermionic, Anyonic, NoBraiding export Trivial, Z2Irrep, Z3Irrep, Z4Irrep, ZNIrrep, U1Irrep, SU2Irrep, CU1Irrep export ProductSector @@ -32,14 +32,14 @@ export SpaceMismatch, SectorMismatch, IndexError # error types # general vector space methods export space, field, dual, dim, reduceddim, dims, fuse, flip, isdual, oplus, ominus, - insertleftunit, insertrightunit, removeunit + insertleftunit, insertrightunit, removeunit # partial order for vector spaces export infimum, supremum, isisomorphic, ismonomorphic, isepimorphic # methods for sectors and properties thereof export sectortype, sectors, hassector, Nsymbol, Fsymbol, Rsymbol, Bsymbol, - frobeniusschur, twist, otimes, sectorscalartype, deligneproduct + frobeniusschur, twist, otimes, sectorscalartype, deligneproduct export fusiontrees, braid, permute, transpose export ZNSpace, SU2Irrep, U1Irrep, CU1Irrep # other fusion tree manipulations, should not be exported: @@ -62,7 +62,7 @@ export randisometry, randisometry!, rand, rand!, randn, randn! # special purpose constructors export zero, one, one!, id, id!, isomorphism, isomorphism!, unitary, unitary!, isometry, - isometry! + isometry! # reexport most of VectorInterface and some more tensor algebra export zerovector, zerovector!, zerovector!!, scale, scale!, scale!!, add, add!, add!! @@ -71,19 +71,19 @@ export inner, dot, norm, normalize, normalize!, tr # factorizations export mul!, lmul!, rmul!, adjoint!, pinv, axpy!, axpby! export left_orth, right_orth, left_null, right_null, - left_orth!, right_orth!, left_null!, right_null!, - left_polar, left_polar!, right_polar, right_polar!, - qr_full, qr_compact, qr_null, lq_full, lq_compact, lq_null, - qr_full!, qr_compact!, qr_null!, lq_full!, lq_compact!, lq_null!, - svd_compact!, svd_full!, svd_trunc!, svd_compact, svd_full, svd_trunc, - exp, exp!, - eigh_full!, eigh_full, eigh_trunc!, eigh_trunc, eig_full!, eig_full, eig_trunc!, - eig_trunc, - eigh_vals!, eigh_vals, eig_vals!, eig_vals, - isposdef, isposdef!, ishermitian, isisometry, isunitary, sylvester, rank, cond + left_orth!, right_orth!, left_null!, right_null!, + left_polar, left_polar!, right_polar, right_polar!, + qr_full, qr_compact, qr_null, lq_full, lq_compact, lq_null, + qr_full!, qr_compact!, qr_null!, lq_full!, lq_compact!, lq_null!, + svd_compact!, svd_full!, svd_trunc!, svd_compact, svd_full, svd_trunc, + exp, exp!, + eigh_full!, eigh_full, eigh_trunc!, eigh_trunc, eig_full!, eig_full, eig_trunc!, + eig_trunc, + eigh_vals!, eigh_vals, eig_vals!, eig_vals, + isposdef, isposdef!, ishermitian, isisometry, isunitary, sylvester, rank, cond export braid, braid!, permute, permute!, transpose, transpose!, twist, twist!, repartition, - repartition! + repartition! export catdomain, catcodomain, absorb, absorb! # tensor operations @@ -121,19 +121,19 @@ import TensorKitSectors: dual, type_repr import TensorKitSectors: twist using Base: @boundscheck, @propagate_inbounds, @constprop, - OneTo, tail, front, - tuple_type_head, tuple_type_tail, tuple_type_cons, - SizeUnknown, HasLength, HasShape, IsInfinite, EltypeUnknown, HasEltype + OneTo, tail, front, + tuple_type_head, tuple_type_tail, tuple_type_cons, + SizeUnknown, HasLength, HasShape, IsInfinite, EltypeUnknown, HasEltype using Base.Iterators: product, filter using LinearAlgebra: LinearAlgebra, BlasFloat using LinearAlgebra: norm, dot, normalize, normalize!, tr, - axpy!, axpby!, lmul!, rmul!, mul!, ldiv!, rdiv!, - adjoint, adjoint!, transpose, transpose!, - lu, pinv, sylvester, - eigen, eigen!, svd, svd!, - isposdef, isposdef!, ishermitian, rank, cond, - Diagonal, Hermitian + axpy!, axpby!, lmul!, rmul!, mul!, ldiv!, rdiv!, + adjoint, adjoint!, transpose, transpose!, + lu, pinv, sylvester, + eigen, eigen!, svd, svd!, + isposdef, isposdef!, ishermitian, rank, cond, + Diagonal, Hermitian using MatrixAlgebraKit import Base.Meta @@ -152,8 +152,8 @@ include("auxiliary/random.jl") #-------------------------------------------------------------------- # experiment with different dictionaries -const SectorDict{K,V} = SortedVectorDict{K,V} -const FusionTreeDict{K,V} = Dict{K,V} +const SectorDict{K, V} = SortedVectorDict{K, V} +const FusionTreeDict{K, V} = Dict{K, V} #-------------------------------------------------------------------- # Exception types: @@ -161,7 +161,7 @@ const FusionTreeDict{K,V} = Dict{K,V} abstract type TensorException <: Exception end # Exception type for all errors related to sector mismatch -struct SectorMismatch{S<:Union{Nothing,AbstractString}} <: TensorException +struct SectorMismatch{S <: Union{Nothing, AbstractString}} <: TensorException message::S end SectorMismatch() = SectorMismatch{Nothing}(nothing) @@ -169,7 +169,7 @@ Base.showerror(io::IO, ::SectorMismatch{Nothing}) = print(io, "SectorMismatch()" Base.showerror(io::IO, e::SectorMismatch) = print(io, "SectorMismatch(\"", e.message, "\")") # Exception type for all errors related to vector space mismatch -struct SpaceMismatch{S<:Union{Nothing,AbstractString}} <: TensorException +struct SpaceMismatch{S <: Union{Nothing, AbstractString}} <: TensorException message::S end SpaceMismatch() = SpaceMismatch{Nothing}(nothing) @@ -177,7 +177,7 @@ Base.showerror(io::IO, ::SpaceMismatch{Nothing}) = print(io, "SpaceMismatch()") Base.showerror(io::IO, e::SpaceMismatch) = print(io, "SpaceMismatch(\"", e.message, "\")") # Exception type for all errors related to invalid tensor index specification. -struct IndexError{S<:Union{Nothing,AbstractString}} <: TensorException +struct IndexError{S <: Union{Nothing, AbstractString}} <: TensorException message::S end IndexError() = IndexError{Nothing}(nothing) @@ -243,7 +243,7 @@ include("auxiliary/deprecate.jl") # Extensions # ---------- function __init__() - @require_extensions + return @require_extensions end end diff --git a/src/auxiliary/auxiliary.jl b/src/auxiliary/auxiliary.jl index cb90589e0..4b97603ca 100644 --- a/src/auxiliary/auxiliary.jl +++ b/src/auxiliary/auxiliary.jl @@ -1,5 +1,6 @@ -function linearizepermutation(p1::NTuple{N₁,Int}, p2::NTuple{N₂}, - n₁::Int, n₂::Int) where {N₁,N₂} +function linearizepermutation( + p1::NTuple{N₁, Int}, p2::NTuple{N₂}, n₁::Int, n₂::Int + ) where {N₁, N₂} p1′ = ntuple(Val(N₁)) do n return p1[n] > n₁ ? n₂ + 2n₁ + 1 - p1[n] : p1[n] end @@ -44,7 +45,7 @@ end @noinline _boundserror(P, i) = throw(BoundsError(P, i)) @noinline _nontrivialspaceerror(P, i) = throw(ArgumentError(lazy"Attempting to remove a non-trivial space $(P[i])")) -const VecOrNumber{T<:Number} = Union{DenseVector{T},T} +const VecOrNumber{T <: Number} = Union{DenseVector{T}, T} """ _interleave(a::NTuple{N}, b::NTuple{N}) -> NTuple{2N} @@ -58,7 +59,7 @@ end # Low-overhead implementation of `copyto!` for specific case of `stride(B, 1) < stride(B, 2)` # used in indexmanipulations: avoids the overhead of Strided.jl -function _copyto!(A::StridedView{<:Any,1}, B::StridedView{<:Any,2}) +function _copyto!(A::StridedView{<:Any, 1}, B::StridedView{<:Any, 2}) length(A) == length(B) || throw(DimensionMismatch()) Adata = parent(A) diff --git a/src/auxiliary/caches.jl b/src/auxiliary/caches.jl index e829c1517..03daf6997 100644 --- a/src/auxiliary/caches.jl +++ b/src/auxiliary/caches.jl @@ -1,18 +1,19 @@ -const GLOBAL_CACHES = Pair{Symbol,Any}[] +const GLOBAL_CACHES = Pair{Symbol, Any}[] function empty_globalcaches!() foreach(empty! ∘ last, GLOBAL_CACHES) return nothing end -function global_cache_info(io::IO=stdout) +function global_cache_info(io::IO = stdout) for (name, cache) in GLOBAL_CACHES println(io, name, ":\t", LRUCache.cache_info(cache)) end + return end abstract type CacheStyle end struct NoCache <: CacheStyle end -struct TaskLocalCache{D<:AbstractDict} <: CacheStyle end +struct TaskLocalCache{D <: AbstractDict} <: CacheStyle end struct GlobalLRUCache <: CacheStyle end const DEFAULT_GLOBALCACHE_SIZE = Ref(10^4) @@ -66,11 +67,12 @@ macro cached(ex) newfcall = Expr(:where, newfcall, params...) end cachestylevar = gensym(:cachestyle) - cachestyleex = Expr(:(=), cachestylevar, - Expr(:call, :CacheStyle, fname, fargnames...)) - newfbody = Expr(:block, - cachestyleex, - Expr(:call, fname, fargnames..., cachestylevar)) + cachestyleex = Expr( + :(=), cachestylevar, Expr(:call, :CacheStyle, fname, fargnames...) + ) + newfbody = Expr( + :block, cachestyleex, Expr(:call, fname, fargnames..., cachestylevar) + ) newfex = Expr(:function, newfcall, newfbody) # nocache implementation @@ -95,34 +97,42 @@ macro cached(ex) end localcachename = Symbol(:_tasklocal_, fname, :_cache) cachevar = gensym(:cache) - getlocalcacheex = :($cachevar::$Dvar = get!(task_local_storage(), $localcachename) do - return $Dvar() - end) + getlocalcacheex = :( + $cachevar::$Dvar = get!(task_local_storage(), $localcachename) do + return $Dvar() + end + ) valvar = gensym(:val) if length(fargnames) == 1 key = fargnames[1] else key = Expr(:tuple, fargnames...) end - getvalex = :(get!($cachevar, $key) do - return $_fname($(fargnames...)) - end) + getvalex = :( + get!($cachevar, $key) do + return $_fname($(fargnames...)) + end + ) if typed T = gensym(:T) - flocalcachebody = Expr(:block, - getlocalcacheex, - Expr(:(=), T, typeex), - Expr(:(=), Expr(:(::), valvar, T), getvalex), - Expr(:return, valvar)) + flocalcachebody = Expr( + :block, + getlocalcacheex, + Expr(:(=), T, typeex), + Expr(:(=), Expr(:(::), valvar, T), getvalex), + Expr(:return, valvar) + ) else - flocalcachebody = Expr(:block, - getlocalcacheex, - Expr(:(=), valvar, getvalex), - Expr(:return, valvar)) + flocalcachebody = Expr( + :block, + getlocalcacheex, + Expr(:(=), valvar, getvalex), + Expr(:return, valvar) + ) end flocalcacheex = Expr(:function, flocalcachecall, flocalcachebody) - # # global cache implementation + # # global cache implementation fglobalcachecall = Expr(:call, fname, fargs..., :(::GlobalLRUCache)) if hasparams fglobalcachecall = Expr(:where, fglobalcachecall, params...) @@ -131,25 +141,35 @@ macro cached(ex) getglobalcachex = Expr(:(=), cachevar, globalcachename) if typed T = gensym(:T) - fglobalcachebody = Expr(:block, - getglobalcachex, - Expr(:(=), T, typeex), - Expr(:(=), Expr(:(::), valvar, T), getvalex), - Expr(:return, valvar)) + fglobalcachebody = Expr( + :block, + getglobalcachex, + Expr(:(=), T, typeex), + Expr(:(=), Expr(:(::), valvar, T), getvalex), + Expr(:return, valvar) + ) else - fglobalcachebody = Expr(:block, - getglobalcachex, - Expr(:(=), valvar, getvalex), - Expr(:return, valvar)) + fglobalcachebody = Expr( + :block, + getglobalcachex, + Expr(:(=), valvar, getvalex), + Expr(:return, valvar) + ) end fglobalcacheex = Expr(:function, fglobalcachecall, fglobalcachebody) - fglobalcachedef = Expr(:const, - Expr(:(=), globalcachename, - :(LRU{Any,Any}(; maxsize=DEFAULT_GLOBALCACHE_SIZE[])))) - fglobalcacheregister = Expr(:call, :push!, :GLOBAL_CACHES, - :($(QuoteNode(globalcachename)) => $globalcachename)) + fglobalcachedef = Expr( + :const, + Expr(:(=), globalcachename, :(LRU{Any, Any}(; maxsize = DEFAULT_GLOBALCACHE_SIZE[]))) + ) + fglobalcacheregister = Expr( + :call, :push!, :GLOBAL_CACHES, :($(QuoteNode(globalcachename)) => $globalcachename) + ) # # total expression - return esc(Expr(:block, _fex, newfex, fnocacheex, flocalcacheex, - fglobalcachedef, fglobalcacheregister, fglobalcacheex)) + return esc( + Expr( + :block, _fex, newfex, fnocacheex, flocalcacheex, + fglobalcachedef, fglobalcacheregister, fglobalcacheex + ) + ) end diff --git a/src/auxiliary/dicts.jl b/src/auxiliary/dicts.jl index cb5a169ee..f43838010 100644 --- a/src/auxiliary/dicts.jl +++ b/src/auxiliary/dicts.jl @@ -1,9 +1,9 @@ -struct SingletonDict{K,V} <: AbstractDict{K,V} +struct SingletonDict{K, V} <: AbstractDict{K, V} key::K value::V - SingletonDict{K,V}(p::Pair{K,V}) where {K,V} = new{K,V}(p.first, p.second) + SingletonDict{K, V}(p::Pair{K, V}) where {K, V} = new{K, V}(p.first, p.second) end -SingletonDict(p::Pair{K,V}) where {K,V} = SingletonDict{K,V}(p) +SingletonDict(p::Pair{K, V}) where {K, V} = SingletonDict{K, V}(p) function SingletonDict(g::Base.Generator) s = iterate(g) @assert s !== nothing @@ -19,16 +19,16 @@ Base.haskey(d::SingletonDict, key) = isequal(d.key, key) Base.getindex(d::SingletonDict, key) = isequal(d.key, key) ? d.value : throw(KeyError(key)) Base.get(d::SingletonDict, key, default) = isequal(d.key, key) ? d.value : default -Base.iterate(d::SingletonDict, s=true) = s ? ((d.key => d.value), false) : nothing +Base.iterate(d::SingletonDict, s = true) = s ? ((d.key => d.value), false) : nothing -struct VectorDict{K,V} <: AbstractDict{K,V} +struct VectorDict{K, V} <: AbstractDict{K, V} keys::Vector{K} values::Vector{V} end -VectorDict{K,V}() where {K,V} = VectorDict{K,V}(Vector{K}(), Vector{V}()) -VectorDict() = VectorDict{Any,Any}() +VectorDict{K, V}() where {K, V} = VectorDict{K, V}(Vector{K}(), Vector{V}()) +VectorDict() = VectorDict{Any, Any}() -function VectorDict{K,V}(kvs) where {K,V} +function VectorDict{K, V}(kvs) where {K, V} keys = Vector{K}() values = Vector{V}() if Base.IteratorSize(kv) !== SizeUnknown() @@ -39,9 +39,9 @@ function VectorDict{K,V}(kvs) where {K,V} push!(keys, k) push!(values, v) end - return VectorDict{K,V}(keys, values) + return VectorDict{K, V}(keys, values) end -VectorDict(kv1::Pair{K,V}, kvs::Pair{K,V}...) where {K,V} = VectorDict{K,V}((kv1, kvs...)) +VectorDict(kv1::Pair{K, V}, kvs::Pair{K, V}...) where {K, V} = VectorDict{K, V}((kv1, kvs...)) VectorDict(g::Base.Generator) = VectorDict(g...) Base.length(d::VectorDict) = length(d.keys) @@ -52,7 +52,7 @@ end @propagate_inbounds getpair(d::VectorDict, i::Integer) = d.keys[i] => d.values[i] Base.copy(d::VectorDict) = VectorDict(copy(d.keys), copy(d.values)) -Base.empty(::VectorDict, ::Type{K}, ::Type{V}) where {K,V} = VectorDict{K,V}() +Base.empty(::VectorDict, ::Type{K}, ::Type{V}) where {K, V} = VectorDict{K, V}() Base.empty!(d::VectorDict) = (empty!(d.keys); empty!(d.values); return d) function Base.delete!(d::VectorDict, key) @@ -91,7 +91,7 @@ function Base.get(d::VectorDict, key, default) end end -function Base.iterate(d::VectorDict, s=1) +function Base.iterate(d::VectorDict, s = 1) @inbounds if s > length(d) return nothing else @@ -99,22 +99,22 @@ function Base.iterate(d::VectorDict, s=1) end end -struct SortedVectorDict{K,V} <: AbstractDict{K,V} +struct SortedVectorDict{K, V} <: AbstractDict{K, V} keys::Vector{K} values::Vector{V} - function SortedVectorDict{K,V}(pairs::Vector{Pair{K,V}}) where {K,V} - pairs = sort!(pairs; by=first) - return new{K,V}(first.(pairs), last.(pairs)) + function SortedVectorDict{K, V}(pairs::Vector{Pair{K, V}}) where {K, V} + pairs = sort!(pairs; by = first) + return new{K, V}(first.(pairs), last.(pairs)) end - function SortedVectorDict{K,V}(keys::Vector{K}, values::Vector{V}) where {K,V} + function SortedVectorDict{K, V}(keys::Vector{K}, values::Vector{V}) where {K, V} @assert issorted(keys) - return new{K,V}(keys, values) + return new{K, V}(keys, values) end - SortedVectorDict{K,V}() where {K,V} = new{K,V}(Vector{K}(undef, 0), Vector{V}(undef, 0)) + SortedVectorDict{K, V}() where {K, V} = new{K, V}(Vector{K}(undef, 0), Vector{V}(undef, 0)) end -SortedVectorDict{K,V}(kv::Pair{K,V}...) where {K,V} = SortedVectorDict{K,V}(kv) -function SortedVectorDict{K,V}(kv) where {K,V} - d = SortedVectorDict{K,V}() +SortedVectorDict{K, V}(kv::Pair{K, V}...) where {K, V} = SortedVectorDict{K, V}(kv) +function SortedVectorDict{K, V}(kv) where {K, V} + d = SortedVectorDict{K, V}() if Base.IteratorSize(kv) !== SizeUnknown() sizehint!(d, length(kv)) end @@ -123,22 +123,22 @@ function SortedVectorDict{K,V}(kv) where {K,V} end return d end -SortedVectorDict(pairs::Vector{Pair{K,V}}) where {K,V} = SortedVectorDict{K,V}(pairs) +SortedVectorDict(pairs::Vector{Pair{K, V}}) where {K, V} = SortedVectorDict{K, V}(pairs) @noinline function _no_pair_error() msg = "SortedVectorDict(kv): kv needs to be an iterator of pairs" throw(ArgumentError(msg)) end function SortedVectorDict(pairs::Vector) all(p -> isa(p, Pair), pairs) || _no_pair_error() - pairs = sort!(pairs; by=first) + pairs = sort!(pairs; by = first) keys = map(first, pairs) values = map(last, pairs) - return SortedVectorDict{eltype(keys),eltype(values)}(keys, values) + return SortedVectorDict{eltype(keys), eltype(values)}(keys, values) end -SortedVectorDict(kv::Pair{K,V}...) where {K,V} = SortedVectorDict{K,V}(kv) +SortedVectorDict(kv::Pair{K, V}...) where {K, V} = SortedVectorDict{K, V}(kv) -_getKV(::Type{Pair{K,V}}) where {K,V} = (K, V) +_getKV(::Type{Pair{K, V}}) where {K, V} = (K, V) function SortedVectorDict(kv) if Base.IteratorEltype(kv) === Base.HasEltype() P = eltype(kv) @@ -149,22 +149,22 @@ function SortedVectorDict(kv) end if P <: Pair && Base.isconcretetype(P) K, V = _getKV(P) - return SortedVectorDict{K,V}(kv) + return SortedVectorDict{K, V}(kv) else return SortedVectorDict(collect(kv)) end end -SortedVectorDict() = SortedVectorDict{Any,Any}() +SortedVectorDict() = SortedVectorDict{Any, Any}() Base.length(d::SortedVectorDict) = length(d.keys) function Base.sizehint!(d::SortedVectorDict, newsz) (sizehint!(d.keys, newsz); sizehint!(d.values, newsz); return d) end -function Base.copy(d::SortedVectorDict{K,V}) where {K,V} - return SortedVectorDict{K,V}(copy(d.keys), copy(d.values)) +function Base.copy(d::SortedVectorDict{K, V}) where {K, V} + return SortedVectorDict{K, V}(copy(d.keys), copy(d.values)) end -Base.empty(::SortedVectorDict, ::Type{K}, ::Type{V}) where {K,V} = SortedVectorDict{K,V}() +Base.empty(::SortedVectorDict, ::Type{K}, ::Type{V}) where {K, V} = SortedVectorDict{K, V}() Base.empty!(d::SortedVectorDict) = (empty!(d.keys); empty!(d.values); return d) # _searchsortedfirst(v::Vector, k) = searchsortedfirst(v, k) @@ -236,7 +236,7 @@ function Base.get(d::SortedVectorDict{K}, k, default) where {K} return (i <= length(d) && isequal(d.keys[i], key)) ? d.values[i] : default end end -function Base.get(f::Union{Function,Type}, d::SortedVectorDict{K}, k) where {K} +function Base.get(f::Union{Function, Type}, d::SortedVectorDict{K}, k) where {K} key = convert(K, k) if !isequal(k, key) return f() @@ -246,7 +246,7 @@ function Base.get(f::Union{Function,Type}, d::SortedVectorDict{K}, k) where {K} return (i <= length(d) && isequal(d.keys[i], key)) ? d.values[i] : f() end end -function Base.iterate(d::SortedVectorDict, i=1) +function Base.iterate(d::SortedVectorDict, i = 1) @inbounds if i > length(d) return nothing else diff --git a/src/auxiliary/iterators.jl b/src/auxiliary/iterators.jl index fdf6c34a9..211c0dd93 100644 --- a/src/auxiliary/iterators.jl +++ b/src/auxiliary/iterators.jl @@ -3,7 +3,7 @@ struct OneOrNoneIterator{T} first::T end -function Base.iterate(it::OneOrNoneIterator, state=true) +function Base.iterate(it::OneOrNoneIterator, state = true) if state && it.cond return (it.first, false) else diff --git a/src/auxiliary/random.jl b/src/auxiliary/random.jl index fe57f585e..022809518 100644 --- a/src/auxiliary/random.jl +++ b/src/auxiliary/random.jl @@ -7,11 +7,10 @@ Create a random isometry of size `dims`, uniformly distributed according to the See also [`randuniform`](@ref) and [`randnormal`](@ref). """ randisometry(dims::Base.Dims{2}) = randisometry(Float64, dims) -function randisometry(::Type{T}, dims::Base.Dims{2}) where {T<:Number} +function randisometry(::Type{T}, dims::Base.Dims{2}) where {T <: Number} return randisometry(Random.default_rng(), T, dims) end -function randisometry(rng::Random.AbstractRNG, ::Type{T}, - dims::Base.Dims{2}) where {T<:Number} +function randisometry(rng::Random.AbstractRNG, ::Type{T}, dims::Base.Dims{2}) where {T <: Number} return randisometry!(rng, Matrix{T}(undef, dims)) end @@ -20,6 +19,6 @@ function randisometry!(rng::Random.AbstractRNG, A::AbstractMatrix) dims = size(A) dims[1] >= dims[2] || throw(DimensionMismatch("cannot create isometric matrix with dimensions $dims; isometry needs to be tall or square")) - Q, = qr_compact!(Random.randn!(rng, A); positive=true) + Q, = qr_compact!(Random.randn!(rng, A); positive = true) return copy!(A, Q) end diff --git a/src/factorizations/adjoint.jl b/src/factorizations/adjoint.jl index b4c148788..30a68c138 100644 --- a/src/factorizations/adjoint.jl +++ b/src/factorizations/adjoint.jl @@ -10,12 +10,13 @@ _adjoint(alg::MAK.PolarViaSVD) = MAK.PolarViaSVD(_adjoint(alg.svdalg)) _adjoint(alg::AbstractAlgorithm) = alg # 1-arg functions -function MAK.initialize_output(::typeof(left_null!), t::AdjointTensorMap, - alg::AbstractAlgorithm) +function MAK.initialize_output(::typeof(left_null!), t::AdjointTensorMap, alg::AbstractAlgorithm) return adjoint(MAK.initialize_output(right_null!, adjoint(t), _adjoint(alg))) end -function MAK.initialize_output(::typeof(right_null!), t::AdjointTensorMap, - alg::AbstractAlgorithm) +function MAK.initialize_output( + ::typeof(right_null!), t::AdjointTensorMap, + alg::AbstractAlgorithm + ) return adjoint(MAK.initialize_output(left_null!, adjoint(t), _adjoint(alg))) end @@ -36,8 +37,10 @@ function MAK.is_right_isometry(t::AdjointTensorMap; kwargs...) end # 2-arg functions -for (left_f!, right_f!) in zip((:qr_full!, :qr_compact!, :left_polar!, :left_orth!), - (:lq_full!, :lq_compact!, :right_polar!, :right_orth!)) +for (left_f!, right_f!) in zip( + (:qr_full!, :qr_compact!, :left_polar!, :left_orth!), + (:lq_full!, :lq_compact!, :right_polar!, :right_orth!) + ) @eval function MAK.copy_input(::typeof($left_f!), t::AdjointTensorMap) return adjoint(MAK.copy_input($right_f!, adjoint(t))) end @@ -45,12 +48,14 @@ for (left_f!, right_f!) in zip((:qr_full!, :qr_compact!, :left_polar!, :left_ort return adjoint(MAK.copy_input($left_f!, adjoint(t))) end - @eval function MAK.initialize_output(::typeof($left_f!), t::AdjointTensorMap, - alg::AbstractAlgorithm) + @eval function MAK.initialize_output( + ::typeof($left_f!), t::AdjointTensorMap, alg::AbstractAlgorithm + ) return reverse(adjoint.(MAK.initialize_output($right_f!, adjoint(t), _adjoint(alg)))) end - @eval function MAK.initialize_output(::typeof($right_f!), t::AdjointTensorMap, - alg::AbstractAlgorithm) + @eval function MAK.initialize_output( + ::typeof($right_f!), t::AdjointTensorMap, alg::AbstractAlgorithm + ) return reverse(adjoint.(MAK.initialize_output($left_f!, adjoint(t), _adjoint(alg)))) end @@ -70,8 +75,9 @@ for f! in (:svd_full!, :svd_compact!, :svd_trunc!) return adjoint(MAK.copy_input($f!, adjoint(t))) end - @eval function MAK.initialize_output(::typeof($f!), t::AdjointTensorMap, - alg::AbstractAlgorithm) + @eval function MAK.initialize_output( + ::typeof($f!), t::AdjointTensorMap, alg::AbstractAlgorithm + ) return reverse(adjoint.(MAK.initialize_output($f!, adjoint(t), _adjoint(alg)))) end @eval function MAK.$f!(t::AdjointTensorMap, F, alg::AbstractAlgorithm) @@ -80,14 +86,16 @@ for f! in (:svd_full!, :svd_compact!, :svd_trunc!) end # disambiguate by prohibition - @eval function MAK.initialize_output(::typeof($f!), t::AdjointTensorMap, - alg::DiagonalAlgorithm) + @eval function MAK.initialize_output( + ::typeof($f!), t::AdjointTensorMap, alg::DiagonalAlgorithm + ) throw(MethodError($f!, (t, alg))) end end # avoid amgiguity -function MAK.initialize_output(::typeof(svd_trunc!), t::AdjointTensorMap, - alg::TruncatedAlgorithm) +function MAK.initialize_output( + ::typeof(svd_trunc!), t::AdjointTensorMap, alg::TruncatedAlgorithm + ) return MAK.initialize_output(svd_compact!, t, alg.alg) end # to fix ambiguity diff --git a/src/factorizations/diagonal.jl b/src/factorizations/diagonal.jl index 8fa33fd20..4de99f973 100644 --- a/src/factorizations/diagonal.jl +++ b/src/factorizations/diagonal.jl @@ -2,22 +2,26 @@ # ----------------- _repack_diagonal(d::DiagonalTensorMap) = Diagonal(d.data) -for f in (:svd_compact, :svd_full, :svd_trunc, :svd_vals, :qr_compact, :qr_full, :qr_null, - :lq_compact, :lq_full, :lq_null, :eig_full, :eig_trunc, :eig_vals, :eigh_full, - :eigh_trunc, :eigh_vals, :left_polar, :right_polar) +for f in ( + :svd_compact, :svd_full, :svd_trunc, :svd_vals, :qr_compact, :qr_full, :qr_null, + :lq_compact, :lq_full, :lq_null, :eig_full, :eig_trunc, :eig_vals, :eigh_full, + :eigh_trunc, :eigh_vals, :left_polar, :right_polar, + ) @eval MAK.copy_input(::typeof($f), d::DiagonalTensorMap) = copy(d) end for f! in (:eig_full!, :eig_trunc!) - @eval function MAK.initialize_output(::typeof($f!), d::AbstractTensorMap, - ::DiagonalAlgorithm) + @eval function MAK.initialize_output( + ::typeof($f!), d::AbstractTensorMap, ::DiagonalAlgorithm + ) return d, similar(d) end end for f! in (:eigh_full!, :eigh_trunc!) - @eval function MAK.initialize_output(::typeof($f!), d::AbstractTensorMap, - ::DiagonalAlgorithm) + @eval function MAK.initialize_output( + ::typeof($f!), d::AbstractTensorMap, ::DiagonalAlgorithm + ) if scalartype(d) <: Real return d, similar(d) else @@ -27,24 +31,28 @@ for f! in (:eigh_full!, :eigh_trunc!) end for f! in (:qr_full!, :qr_compact!) - @eval function MAK.initialize_output(::typeof($f!), d::AbstractTensorMap, - ::DiagonalAlgorithm) + @eval function MAK.initialize_output( + ::typeof($f!), d::AbstractTensorMap, ::DiagonalAlgorithm + ) return d, similar(d) end # to avoid ambiguities - @eval function MAK.initialize_output(::typeof($f!), d::AdjointTensorMap, - ::DiagonalAlgorithm) + @eval function MAK.initialize_output( + ::typeof($f!), d::AdjointTensorMap, ::DiagonalAlgorithm + ) return d, similar(d) end end for f! in (:lq_full!, :lq_compact!) - @eval function MAK.initialize_output(::typeof($f!), d::AbstractTensorMap, - ::DiagonalAlgorithm) + @eval function MAK.initialize_output( + ::typeof($f!), d::AbstractTensorMap, ::DiagonalAlgorithm + ) return similar(d), d end # to avoid ambiguities - @eval function MAK.initialize_output(::typeof($f!), d::AdjointTensorMap, - ::DiagonalAlgorithm) + @eval function MAK.initialize_output( + ::typeof($f!), d::AdjointTensorMap, ::DiagonalAlgorithm + ) return similar(d), d end end @@ -56,8 +64,9 @@ function MAK.initialize_output(::typeof(right_orth!), d::DiagonalTensorMap) return similar(d), d end -function MAK.initialize_output(::typeof(svd_full!), t::AbstractTensorMap, - ::DiagonalAlgorithm) +function MAK.initialize_output( + ::typeof(svd_full!), t::AbstractTensorMap, ::DiagonalAlgorithm + ) V_cod = fuse(codomain(t)) V_dom = fuse(domain(t)) U = similar(t, codomain(t) ← V_cod) @@ -67,8 +76,10 @@ function MAK.initialize_output(::typeof(svd_full!), t::AbstractTensorMap, end for f! in - (:qr_full!, :qr_compact!, :lq_full!, :lq_compact!, :eig_full!, :eig_trunc!, :eigh_full!, - :eigh_trunc!, :right_orth!, :left_orth!) + ( + :qr_full!, :qr_compact!, :lq_full!, :lq_compact!, :eig_full!, :eig_trunc!, :eigh_full!, + :eigh_trunc!, :right_orth!, :left_orth!, + ) @eval function MAK.$f!(d::DiagonalTensorMap, F, alg::DiagonalAlgorithm) MAK.check_input($f!, d, F, alg) $f!(_repack_diagonal(d), _repack_diagonal.(F), alg) @@ -77,8 +88,9 @@ for f! in end for f! in (:qr_full!, :qr_compact!) - @eval function MAK.check_input(::typeof($f!), d::AbstractTensorMap, QR, - ::DiagonalAlgorithm) + @eval function MAK.check_input( + ::typeof($f!), d::AbstractTensorMap, QR, ::DiagonalAlgorithm + ) Q, R = QR @assert d isa DiagonalTensorMap @assert Q isa DiagonalTensorMap && R isa DiagonalTensorMap @@ -92,8 +104,9 @@ for f! in (:qr_full!, :qr_compact!) end for f! in (:lq_full!, :lq_compact!) - @eval function MAK.check_input(::typeof($f!), d::AbstractTensorMap, LQ, - ::DiagonalAlgorithm) + @eval function MAK.check_input( + ::typeof($f!), d::AbstractTensorMap, LQ, ::DiagonalAlgorithm + ) L, Q = LQ @assert d isa DiagonalTensorMap @assert Q isa DiagonalTensorMap && L isa DiagonalTensorMap @@ -120,8 +133,9 @@ for f! in (:eig_vals!, :eigh_vals!, :svd_vals!) $f!(_repack_diagonal(d), diagview(_repack_diagonal(V)), alg) return V end - @eval function MAK.initialize_output(::typeof($f!), d::DiagonalTensorMap, - alg::DiagonalAlgorithm) + @eval function MAK.initialize_output( + ::typeof($f!), d::DiagonalTensorMap, alg::DiagonalAlgorithm + ) data = MAK.initialize_output($f!, _repack_diagonal(d), alg) return DiagonalTensorMap(data, d.domain) end @@ -147,8 +161,7 @@ function MAK.check_input(::typeof(eig_full!), t::AbstractTensorMap, DV, ::Diagon return nothing end -function MAK.check_input(::typeof(eigh_full!), t::AbstractTensorMap, DV, - ::DiagonalAlgorithm) +function MAK.check_input(::typeof(eigh_full!), t::AbstractTensorMap, DV, ::DiagonalAlgorithm) domain(t) == codomain(t) || throw(ArgumentError("Eigenvalue decomposition requires square input tensor")) diff --git a/src/factorizations/factorizations.jl b/src/factorizations/factorizations.jl index 310bf10a2..29d2c16a6 100644 --- a/src/factorizations/factorizations.jl +++ b/src/factorizations/factorizations.jl @@ -9,7 +9,7 @@ using ..TensorKit using ..TensorKit: AdjointTensorMap, SectorDict, blocktype, foreachblock, one! using LinearAlgebra: LinearAlgebra, BlasFloat, Diagonal, svdvals, svdvals!, eigen, eigen!, - isposdef, isposdef!, ishermitian + isposdef, isposdef!, ishermitian using TensorOperations: Index2Tuple @@ -17,10 +17,9 @@ using MatrixAlgebraKit import MatrixAlgebraKit as MAK using MatrixAlgebraKit: AbstractAlgorithm, TruncatedAlgorithm, DiagonalAlgorithm using MatrixAlgebraKit: TruncationStrategy, NoTruncation, TruncationByValue, - TruncationByError, TruncationIntersection, TruncationByFilter, - TruncationByOrder + TruncationByError, TruncationIntersection, TruncationByFilter, TruncationByOrder using MatrixAlgebraKit: left_orth_polar!, right_orth_polar!, left_orth_svd!, - right_orth_svd!, left_null_svd!, right_null_svd!, diagview + right_orth_svd!, left_null_svd!, right_null_svd!, diagview include("utility.jl") include("matrixalgebrakit.jl") @@ -32,7 +31,7 @@ include("pullbacks.jl") TensorKit.one!(A::AbstractMatrix) = MatrixAlgebraKit.one!(A) function MatrixAlgebraKit.isisometry(t::AbstractTensorMap, (p₁, p₂)::Index2Tuple) - t = permute(t, (p₁, p₂); copy=false) + t = permute(t, (p₁, p₂); copy = false) return isisometry(t) end diff --git a/src/factorizations/matrixalgebrakit.jl b/src/factorizations/matrixalgebrakit.jl index e67290603..fc5f84502 100644 --- a/src/factorizations/matrixalgebrakit.jl +++ b/src/factorizations/matrixalgebrakit.jl @@ -1,12 +1,13 @@ # Algorithm selection # ------------------- for f in - [:svd_compact, :svd_full, :svd_trunc, :svd_vals, :qr_compact, :qr_full, :qr_null, - :lq_compact, :lq_full, :lq_null, :eig_full, :eig_trunc, :eig_vals, :eigh_full, - :eigh_trunc, :eigh_vals, :left_polar, :right_polar] + [ + :svd_compact, :svd_full, :svd_trunc, :svd_vals, :qr_compact, :qr_full, :qr_null, + :lq_compact, :lq_full, :lq_null, :eig_full, :eig_trunc, :eig_vals, :eigh_full, + :eigh_trunc, :eigh_vals, :left_polar, :right_polar, + ] f! = Symbol(f, :!) - @eval function MAK.default_algorithm(::typeof($f!), ::Type{T}; - kwargs...) where {T<:AbstractTensorMap} + @eval function MAK.default_algorithm(::typeof($f!), ::Type{T}; kwargs...) where {T <: AbstractTensorMap} return MAK.default_algorithm($f!, blocktype(T); kwargs...) end @eval function MAK.copy_input(::typeof($f), t::AbstractTensorMap) @@ -21,13 +22,12 @@ end # Generic Implementations # ----------------------- -for f! in (:qr_compact!, :qr_full!, - :lq_compact!, :lq_full!, - :eig_full!, :eigh_full!, - :svd_compact!, :svd_full!, - :left_polar!, :left_orth_polar!, - :right_polar!, :right_orth_polar!, - :left_orth!, :right_orth!) +for f! in ( + :qr_compact!, :qr_full!, :lq_compact!, :lq_full!, + :eig_full!, :eigh_full!, :svd_compact!, :svd_full!, + :left_polar!, :left_orth_polar!, :right_polar!, :right_orth_polar!, + :left_orth!, :right_orth!, + ) @eval function MAK.$f!(t::AbstractTensorMap, F, alg::AbstractAlgorithm) MAK.check_input($f!, t, F, alg) @@ -79,8 +79,7 @@ end # Singular value decomposition # ---------------------------- -function MAK.check_input(::typeof(svd_full!), t::AbstractTensorMap, USVᴴ, - ::AbstractAlgorithm) +function MAK.check_input(::typeof(svd_full!), t::AbstractTensorMap, USVᴴ, ::AbstractAlgorithm) U, S, Vᴴ = USVᴴ # type checks @@ -103,8 +102,7 @@ function MAK.check_input(::typeof(svd_full!), t::AbstractTensorMap, USVᴴ, return nothing end -function MAK.check_input(::typeof(svd_compact!), t::AbstractTensorMap, USVᴴ, - ::AbstractAlgorithm) +function MAK.check_input(::typeof(svd_compact!), t::AbstractTensorMap, USVᴴ, ::AbstractAlgorithm) U, S, Vᴴ = USVᴴ # type checks @@ -134,8 +132,7 @@ function MAK.check_input(::typeof(svd_vals!), t::AbstractTensorMap, D, ::Abstrac return nothing end -function MAK.initialize_output(::typeof(svd_full!), t::AbstractTensorMap, - ::AbstractAlgorithm) +function MAK.initialize_output(::typeof(svd_full!), t::AbstractTensorMap, ::AbstractAlgorithm) V_cod = fuse(codomain(t)) V_dom = fuse(domain(t)) U = similar(t, codomain(t) ← V_cod) @@ -144,8 +141,7 @@ function MAK.initialize_output(::typeof(svd_full!), t::AbstractTensorMap, return U, S, Vᴴ end -function MAK.initialize_output(::typeof(svd_compact!), t::AbstractTensorMap, - ::AbstractAlgorithm) +function MAK.initialize_output(::typeof(svd_compact!), t::AbstractTensorMap, ::AbstractAlgorithm) V_cod = V_dom = infimum(fuse(codomain(t)), fuse(domain(t))) U = similar(t, codomain(t) ← V_cod) S = DiagonalTensorMap{real(scalartype(t))}(undef, V_cod) @@ -154,21 +150,18 @@ function MAK.initialize_output(::typeof(svd_compact!), t::AbstractTensorMap, end # TODO: remove this once `AbstractMatrix` specialization is removed in MatrixAlgebraKit -function MAK.initialize_output(::typeof(svd_trunc!), t::AbstractTensorMap, - alg::TruncatedAlgorithm) +function MAK.initialize_output(::typeof(svd_trunc!), t::AbstractTensorMap, alg::TruncatedAlgorithm) return MAK.initialize_output(svd_compact!, t, alg.alg) end -function MAK.initialize_output(::typeof(svd_vals!), t::AbstractTensorMap, - alg::AbstractAlgorithm) +function MAK.initialize_output(::typeof(svd_vals!), t::AbstractTensorMap, alg::AbstractAlgorithm) V_cod = infimum(fuse(codomain(t)), fuse(domain(t))) return DiagonalTensorMap{real(scalartype(t))}(undef, V_cod) end # Eigenvalue decomposition # ------------------------ -function MAK.check_input(::typeof(eigh_full!), t::AbstractTensorMap, DV, - ::AbstractAlgorithm) +function MAK.check_input(::typeof(eigh_full!), t::AbstractTensorMap, DV, ::AbstractAlgorithm) domain(t) == codomain(t) || throw(ArgumentError("Eigenvalue decomposition requires square input tensor")) @@ -228,8 +221,7 @@ function MAK.check_input(::typeof(eig_vals!), t::AbstractTensorMap, D, ::Abstrac return nothing end -function MAK.initialize_output(::typeof(eigh_full!), t::AbstractTensorMap, - ::AbstractAlgorithm) +function MAK.initialize_output(::typeof(eigh_full!), t::AbstractTensorMap, ::AbstractAlgorithm) V_D = fuse(domain(t)) T = real(scalartype(t)) D = DiagonalTensorMap{T}(undef, V_D) @@ -237,8 +229,7 @@ function MAK.initialize_output(::typeof(eigh_full!), t::AbstractTensorMap, return D, V end -function MAK.initialize_output(::typeof(eig_full!), t::AbstractTensorMap, - ::AbstractAlgorithm) +function MAK.initialize_output(::typeof(eig_full!), t::AbstractTensorMap, ::AbstractAlgorithm) V_D = fuse(domain(t)) Tc = complex(scalartype(t)) D = DiagonalTensorMap{Tc}(undef, V_D) @@ -246,15 +237,13 @@ function MAK.initialize_output(::typeof(eig_full!), t::AbstractTensorMap, return D, V end -function MAK.initialize_output(::typeof(eigh_vals!), t::AbstractTensorMap, - alg::AbstractAlgorithm) +function MAK.initialize_output(::typeof(eigh_vals!), t::AbstractTensorMap, alg::AbstractAlgorithm) V_D = fuse(domain(t)) T = real(scalartype(t)) return D = DiagonalTensorMap{Tc}(undef, V_D) end -function MAK.initialize_output(::typeof(eig_vals!), t::AbstractTensorMap, - alg::AbstractAlgorithm) +function MAK.initialize_output(::typeof(eig_vals!), t::AbstractTensorMap, alg::AbstractAlgorithm) V_D = fuse(domain(t)) Tc = complex(scalartype(t)) return D = DiagonalTensorMap{Tc}(undef, V_D) @@ -281,8 +270,7 @@ function MAK.check_input(::typeof(qr_full!), t::AbstractTensorMap, QR, ::Abstrac return nothing end -function MAK.check_input(::typeof(qr_compact!), t::AbstractTensorMap, QR, - ::AbstractAlgorithm) +function MAK.check_input(::typeof(qr_compact!), t::AbstractTensorMap, QR, ::AbstractAlgorithm) Q, R = QR # type checks @@ -313,24 +301,21 @@ function MAK.check_input(::typeof(qr_null!), t::AbstractTensorMap, N, ::Abstract return nothing end -function MAK.initialize_output(::typeof(qr_full!), t::AbstractTensorMap, - ::AbstractAlgorithm) +function MAK.initialize_output(::typeof(qr_full!), t::AbstractTensorMap, ::AbstractAlgorithm) V_Q = fuse(codomain(t)) Q = similar(t, codomain(t) ← V_Q) R = similar(t, V_Q ← domain(t)) return Q, R end -function MAK.initialize_output(::typeof(qr_compact!), t::AbstractTensorMap, - ::AbstractAlgorithm) +function MAK.initialize_output(::typeof(qr_compact!), t::AbstractTensorMap, ::AbstractAlgorithm) V_Q = infimum(fuse(codomain(t)), fuse(domain(t))) Q = similar(t, codomain(t) ← V_Q) R = similar(t, V_Q ← domain(t)) return Q, R end -function MAK.initialize_output(::typeof(qr_null!), t::AbstractTensorMap, - ::AbstractAlgorithm) +function MAK.initialize_output(::typeof(qr_null!), t::AbstractTensorMap, ::AbstractAlgorithm) V_Q = infimum(fuse(codomain(t)), fuse(domain(t))) V_N = ⊖(fuse(codomain(t)), V_Q) N = similar(t, codomain(t) ← V_N) @@ -358,8 +343,7 @@ function MAK.check_input(::typeof(lq_full!), t::AbstractTensorMap, LQ, ::Abstrac return nothing end -function MAK.check_input(::typeof(lq_compact!), t::AbstractTensorMap, LQ, - ::AbstractAlgorithm) +function MAK.check_input(::typeof(lq_compact!), t::AbstractTensorMap, LQ, ::AbstractAlgorithm) L, Q = LQ # type checks @@ -390,24 +374,21 @@ function MAK.check_input(::typeof(lq_null!), t::AbstractTensorMap, N, ::Abstract return nothing end -function MAK.initialize_output(::typeof(lq_full!), t::AbstractTensorMap, - ::AbstractAlgorithm) +function MAK.initialize_output(::typeof(lq_full!), t::AbstractTensorMap, ::AbstractAlgorithm) V_Q = fuse(domain(t)) L = similar(t, codomain(t) ← V_Q) Q = similar(t, V_Q ← domain(t)) return L, Q end -function MAK.initialize_output(::typeof(lq_compact!), t::AbstractTensorMap, - ::AbstractAlgorithm) +function MAK.initialize_output(::typeof(lq_compact!), t::AbstractTensorMap, ::AbstractAlgorithm) V_Q = infimum(fuse(codomain(t)), fuse(domain(t))) L = similar(t, codomain(t) ← V_Q) Q = similar(t, V_Q ← domain(t)) return L, Q end -function MAK.initialize_output(::typeof(lq_null!), t::AbstractTensorMap, - ::AbstractAlgorithm) +function MAK.initialize_output(::typeof(lq_null!), t::AbstractTensorMap, ::AbstractAlgorithm) V_Q = infimum(fuse(codomain(t)), fuse(domain(t))) V_N = ⊖(fuse(domain(t)), V_Q) N = similar(t, V_N ← domain(t)) @@ -416,8 +397,7 @@ end # Polar decomposition # ------------------- -function MAK.check_input(::typeof(left_polar!), t::AbstractTensorMap, WP, - ::AbstractAlgorithm) +function MAK.check_input(::typeof(left_polar!), t::AbstractTensorMap, WP, ::AbstractAlgorithm) codomain(t) ≿ domain(t) || throw(ArgumentError("Polar decomposition requires `codomain(t) ≿ domain(t)`")) @@ -436,8 +416,7 @@ function MAK.check_input(::typeof(left_polar!), t::AbstractTensorMap, WP, return nothing end -function MAK.check_input(::typeof(left_orth_polar!), t::AbstractTensorMap, WP, - ::AbstractAlgorithm) +function MAK.check_input(::typeof(left_orth_polar!), t::AbstractTensorMap, WP, ::AbstractAlgorithm) codomain(t) ≿ domain(t) || throw(ArgumentError("Polar decomposition requires `codomain(t) ≿ domain(t)`")) @@ -457,15 +436,13 @@ function MAK.check_input(::typeof(left_orth_polar!), t::AbstractTensorMap, WP, return nothing end -function MAK.initialize_output(::typeof(left_polar!), t::AbstractTensorMap, - ::AbstractAlgorithm) +function MAK.initialize_output(::typeof(left_polar!), t::AbstractTensorMap, ::AbstractAlgorithm) W = similar(t, space(t)) P = similar(t, domain(t) ← domain(t)) return W, P end -function MAK.check_input(::typeof(right_polar!), t::AbstractTensorMap, PWᴴ, - ::AbstractAlgorithm) +function MAK.check_input(::typeof(right_polar!), t::AbstractTensorMap, PWᴴ, ::AbstractAlgorithm) codomain(t) ≾ domain(t) || throw(ArgumentError("Polar decomposition requires `domain(t) ≿ codomain(t)`")) @@ -484,8 +461,7 @@ function MAK.check_input(::typeof(right_polar!), t::AbstractTensorMap, PWᴴ, return nothing end -function MAK.check_input(::typeof(right_orth_polar!), t::AbstractTensorMap, PWᴴ, - ::AbstractAlgorithm) +function MAK.check_input(::typeof(right_orth_polar!), t::AbstractTensorMap, PWᴴ, ::AbstractAlgorithm) codomain(t) ≾ domain(t) || throw(ArgumentError("Polar decomposition requires `domain(t) ≿ codomain(t)`")) @@ -505,8 +481,7 @@ function MAK.check_input(::typeof(right_orth_polar!), t::AbstractTensorMap, PW return nothing end -function MAK.initialize_output(::typeof(right_polar!), t::AbstractTensorMap, - ::AbstractAlgorithm) +function MAK.initialize_output(::typeof(right_polar!), t::AbstractTensorMap, ::AbstractAlgorithm) P = similar(t, codomain(t) ← codomain(t)) Wᴴ = similar(t, space(t)) return P, Wᴴ @@ -514,8 +489,7 @@ end # Orthogonalization # ----------------- -function MAK.check_input(::typeof(left_orth!), t::AbstractTensorMap, VC, - ::AbstractAlgorithm) +function MAK.check_input(::typeof(left_orth!), t::AbstractTensorMap, VC, ::AbstractAlgorithm) V, C = VC # scalartype checks @@ -530,8 +504,7 @@ function MAK.check_input(::typeof(left_orth!), t::AbstractTensorMap, VC, return nothing end -function MAK.check_input(::typeof(right_orth!), t::AbstractTensorMap, CVᴴ, - ::AbstractAlgorithm) +function MAK.check_input(::typeof(right_orth!), t::AbstractTensorMap, CVᴴ, ::AbstractAlgorithm) C, Vᴴ = CVᴴ # scalartype checks @@ -565,46 +538,50 @@ end # providing output arguments for left_ and right_orth. # This is mainly because polar decompositions have different shapes, and SVD for Diagonal # also does -function MAK.left_orth!(t::AbstractTensorMap; - trunc::TruncationStrategy=notrunc(), - kind=trunc == notrunc() ? :qr : :svd, - alg_qr=(; positive=true), alg_polar=(;), alg_svd=(;)) +function MAK.left_orth!( + t::AbstractTensorMap; + trunc::TruncationStrategy = notrunc(), + kind = trunc == notrunc() ? :qr : :svd, + alg_qr = (; positive = true), alg_polar = (;), alg_svd = (;) + ) trunc == notrunc() || kind === :svd || throw(ArgumentError("truncation not supported for left_orth with kind = $kind")) return if kind === :qr - alg_qr isa NamedTuple ? qr_compact!(t; alg_qr...) : qr_compact!(t; alg=alg_qr) + alg_qr isa NamedTuple ? qr_compact!(t; alg_qr...) : qr_compact!(t; alg = alg_qr) elseif kind === :polar alg_polar isa NamedTuple ? left_orth_polar!(t; alg_polar...) : - left_orth_polar!(t; alg=alg_polar) + left_orth_polar!(t; alg = alg_polar) elseif kind === :svd alg_svd isa NamedTuple ? left_orth_svd!(t; trunc, alg_svd...) : - left_orth_svd!(t; trunc, alg=alg_svd) + left_orth_svd!(t; trunc, alg = alg_svd) else throw(ArgumentError(lazy"`left_orth!` received unknown value `kind = $kind`")) end end -function MAK.right_orth!(t::AbstractTensorMap; - trunc::TruncationStrategy=notrunc(), - kind=trunc == notrunc() ? :lq : :svd, - alg_lq=(; positive=true), alg_polar=(;), alg_svd=(;)) +function MAK.right_orth!( + t::AbstractTensorMap; + trunc::TruncationStrategy = notrunc(), + kind = trunc == notrunc() ? :lq : :svd, + alg_lq = (; positive = true), alg_polar = (;), alg_svd = (;) + ) trunc == notrunc() || kind === :svd || throw(ArgumentError("truncation not supported for right_orth with kind = $kind")) return if kind === :lq - alg_lq isa NamedTuple ? lq_compact!(t; alg_lq...) : lq_compact!(t; alg=alg_lq) + alg_lq isa NamedTuple ? lq_compact!(t; alg_lq...) : lq_compact!(t; alg = alg_lq) elseif kind === :polar alg_polar isa NamedTuple ? right_orth_polar!(t; alg_polar...) : - right_orth_polar!(t; alg=alg_polar) + right_orth_polar!(t; alg = alg_polar) elseif kind === :svd alg_svd isa NamedTuple ? right_orth_svd!(t; trunc, alg_svd...) : - right_orth_svd!(t; trunc, alg=alg_svd) + right_orth_svd!(t; trunc, alg = alg_svd) else throw(ArgumentError(lazy"`right_orth!` received unknown value `kind = $kind`")) end end -function MAK.left_orth_polar!(t::AbstractTensorMap; alg=nothing, kwargs...) +function MAK.left_orth_polar!(t::AbstractTensorMap; alg = nothing, kwargs...) alg′ = MAK.select_algorithm(left_polar!, t, alg; kwargs...) VC = MAK.initialize_output(left_orth!, t) return left_orth_polar!(t, VC, alg′) @@ -613,7 +590,7 @@ function MAK.left_orth_polar!(t::AbstractTensorMap, VC, alg) alg′ = MAK.select_algorithm(left_polar!, t, alg) return left_orth_polar!(t, VC, alg′) end -function MAK.right_orth_polar!(t::AbstractTensorMap; alg=nothing, kwargs...) +function MAK.right_orth_polar!(t::AbstractTensorMap; alg = nothing, kwargs...) alg′ = MAK.select_algorithm(right_polar!, t, alg; kwargs...) CVᴴ = MAK.initialize_output(right_orth!, t) return right_orth_polar!(t, CVᴴ, alg′) @@ -623,14 +600,14 @@ function MAK.right_orth_polar!(t::AbstractTensorMap, CVᴴ, alg) return right_orth_polar!(t, CVᴴ, alg′) end -function MAK.left_orth_svd!(t::AbstractTensorMap; trunc=notrunc(), kwargs...) +function MAK.left_orth_svd!(t::AbstractTensorMap; trunc = notrunc(), kwargs...) U, S, Vᴴ = trunc == notrunc() ? svd_compact!(t; kwargs...) : - svd_trunc!(t; trunc, kwargs...) + svd_trunc!(t; trunc, kwargs...) return U, lmul!(S, Vᴴ) end -function MAK.right_orth_svd!(t::AbstractTensorMap; trunc=notrunc(), kwargs...) +function MAK.right_orth_svd!(t::AbstractTensorMap; trunc = notrunc(), kwargs...) U, S, Vᴴ = trunc == notrunc() ? svd_compact!(t; kwargs...) : - svd_trunc!(t; trunc, kwargs...) + svd_trunc!(t; trunc, kwargs...) return rmul!(U, S), Vᴴ end @@ -648,8 +625,7 @@ function MAK.check_input(::typeof(left_null!), t::AbstractTensorMap, N, ::Abstra return nothing end -function MAK.check_input(::typeof(right_null!), t::AbstractTensorMap, N, - ::AbstractAlgorithm) +function MAK.check_input(::typeof(right_null!), t::AbstractTensorMap, N, ::AbstractAlgorithm) @check_scalar N t # space checks @@ -675,7 +651,7 @@ function MAK.initialize_output(::typeof(right_null!), t::AbstractTensorMap) end for (f!, f_svd!) in zip((:left_null!, :right_null!), (:left_null_svd!, :right_null_svd!)) - @eval function MAK.$f_svd!(t::AbstractTensorMap, N, alg, ::Nothing=nothing) - return $f!(t, N; alg_svd=alg) + @eval function MAK.$f_svd!(t::AbstractTensorMap, N, alg, ::Nothing = nothing) + return $f!(t, N; alg_svd = alg) end end diff --git a/src/factorizations/pullbacks.jl b/src/factorizations/pullbacks.jl index 392304bd5..eeeec597f 100644 --- a/src/factorizations/pullbacks.jl +++ b/src/factorizations/pullbacks.jl @@ -1,7 +1,9 @@ -for pullback! in (:qr_pullback!, :lq_pullback!, - :left_polar_pullback!, :right_polar_pullback!) - @eval function MAK.$pullback!(Δt::AbstractTensorMap, t::AbstractTensorMap, - F, ΔF; kwargs...) +for pullback! in ( + :qr_pullback!, :lq_pullback!, :left_polar_pullback!, :right_polar_pullback!, + ) + @eval function MAK.$pullback!( + Δt::AbstractTensorMap, t::AbstractTensorMap, F, ΔF; kwargs... + ) foreachblock(Δt, t) do c, (Δb, b) Fc = block.(F, Ref(c)) ΔFc = block.(ΔF, Ref(c)) @@ -11,8 +13,9 @@ for pullback! in (:qr_pullback!, :lq_pullback!, end end for pullback! in (:qr_null_pullback!, :lq_null_pullback!) - @eval function MAK.$pullback!(Δt::AbstractTensorMap, t::AbstractTensorMap, - F, ΔF; kwargs...) + @eval function MAK.$pullback!( + Δt::AbstractTensorMap, t::AbstractTensorMap, F, ΔF; kwargs... + ) foreachblock(Δt, t) do c, (Δb, b) Fc = block(F, c) ΔFc = block(ΔF, c) @@ -25,9 +28,10 @@ end _notrunc_ind(t) = SectorDict(c => Colon() for c in blocksectors(t)) for pullback! in (:svd_pullback!, :eig_pullback!, :eigh_pullback!) - @eval function MAK.$pullback!(Δt::AbstractTensorMap, t::AbstractTensorMap, - F, ΔF, inds=_notrunc_ind(t); - kwargs...) + @eval function MAK.$pullback!( + Δt::AbstractTensorMap, t::AbstractTensorMap, F, ΔF, inds = _notrunc_ind(t); + kwargs... + ) for (c, ind) in inds Δb = block(Δt, c) b = block(t, c) @@ -40,9 +44,9 @@ for pullback! in (:svd_pullback!, :eig_pullback!, :eigh_pullback!) end for pullback_trunc! in (:svd_trunc_pullback!, :eig_trunc_pullback!, :eigh_trunc_pullback!) - @eval function MAK.$pullback_trunc!(Δt::AbstractTensorMap, - t::AbstractTensorMap, - F, ΔF; kwargs...) + @eval function MAK.$pullback_trunc!( + Δt::AbstractTensorMap, t::AbstractTensorMap, F, ΔF; kwargs... + ) foreachblock(Δt, t) do c, (Δb, b) Fc = block.(F, Ref(c)) ΔFc = block.(ΔF, Ref(c)) diff --git a/src/factorizations/truncation.jl b/src/factorizations/truncation.jl index 35ae0df44..a2faaf50a 100644 --- a/src/factorizations/truncation.jl +++ b/src/factorizations/truncation.jl @@ -8,7 +8,7 @@ such that the resulting vector space is no greater than `V`. See also [`truncspace`](@ref). """ -struct TruncationSpace{S<:ElementarySpace,F} <: TruncationStrategy +struct TruncationSpace{S <: ElementarySpace, F} <: TruncationStrategy space::S by::F rev::Bool @@ -20,7 +20,7 @@ end Truncation strategy to keep the first values for each sector when sorted according to `by` and `rev`, such that the resulting vector space is no greater than `V`. """ -function truncspace(space::ElementarySpace; by=abs, rev::Bool=true) +function truncspace(space::ElementarySpace; by = abs, rev::Bool = true) isdual(space) && throw(ArgumentError("resulting vector space is never dual")) return TruncationSpace(space, by, rev) end @@ -58,8 +58,10 @@ function truncate_diagonal!(Ddst::DiagonalTensorMap, Dsrc::DiagonalTensorMap, in return Ddst end -function MAK.truncate(::typeof(svd_trunc!), (U, S, Vᴴ)::NTuple{3,AbstractTensorMap}, - strategy::TruncationStrategy) +function MAK.truncate( + ::typeof(svd_trunc!), (U, S, Vᴴ)::NTuple{3, AbstractTensorMap}, + strategy::TruncationStrategy + ) ind = MAK.findtruncated_svd(diagview(S), strategy) V_truncated = truncate_space(space(S, 1), ind) @@ -73,12 +75,15 @@ function MAK.truncate(::typeof(svd_trunc!), (U, S, Vᴴ)::NTuple{3,AbstractTenso return (Ũ, S̃, Ṽᴴ), ind end -function MAK.truncate(::typeof(left_null!), - (U, S)::Tuple{AbstractTensorMap,AbstractTensorMap}, - strategy::MatrixAlgebraKit.TruncationStrategy) - extended_S = SectorDict(c => vcat(diagview(b), - zeros(eltype(b), max(0, size(b, 2) - size(b, 1)))) - for (c, b) in blocks(S)) +function MAK.truncate( + ::typeof(left_null!), + (U, S)::Tuple{AbstractTensorMap, AbstractTensorMap}, + strategy::MatrixAlgebraKit.TruncationStrategy + ) + extended_S = SectorDict( + c => vcat(diagview(b), zeros(eltype(b), max(0, size(b, 2) - size(b, 1)))) + for (c, b) in blocks(S) + ) ind = MAK.findtruncated(extended_S, strategy) V_truncated = truncate_space(space(S, 1), ind) Ũ = similar(U, codomain(U) ← V_truncated) @@ -87,9 +92,11 @@ function MAK.truncate(::typeof(left_null!), end for f! in (:eig_trunc!, :eigh_trunc!) - @eval function MAK.truncate(::typeof($f!), - (D, V)::Tuple{DiagonalTensorMap,AbstractTensorMap}, - strategy::TruncationStrategy) + @eval function MAK.truncate( + ::typeof($f!), + (D, V)::Tuple{DiagonalTensorMap, AbstractTensorMap}, + strategy::TruncationStrategy + ) ind = MAK.findtruncated(diagview(D), strategy) V_truncated = truncate_space(space(D, 1), ind) @@ -108,16 +115,18 @@ end # auxiliary functions rtol_to_atol(S, p, atol, rtol) = rtol > 0 ? max(atol, TensorKit._norm(S, p) * rtol) : atol -function _compute_truncerr(Σdata, truncdim, p=2) +function _compute_truncerr(Σdata, truncdim, p = 2) I = keytype(Σdata) S = scalartype(valtype(Σdata)) - return TensorKit._norm((c => @view(v[(get(truncdim, c, 0) + 1):end]) - for (c, v) in Σdata), - p, zero(S)) + return TensorKit._norm( + (c => @view(v[(get(truncdim, c, 0) + 1):end]) for (c, v) in Σdata), + p, zero(S) + ) end -function _findnexttruncvalue(S, truncdim::SectorDict{I,Int}; by=identity, - rev::Bool=true) where {I<:Sector} +function _findnexttruncvalue( + S, truncdim::SectorDict{I, Int}; by = identity, rev::Bool = true + ) where {I <: Sector} # early return (isempty(S) || all(iszero, values(truncdim))) && return nothing if rev @@ -154,8 +163,8 @@ function MAK.findtruncated(values::SectorDict, strategy::TruncationByOrder) end function MAK.findtruncated_svd(values::SectorDict, strategy::TruncationByOrder) I = keytype(values) - truncdim = SectorDict{I,Int}(c => length(d) for (c, d) in values) - totaldim = sum(dim(c) * d for (c, d) in truncdim; init=0) + truncdim = SectorDict{I, Int}(c => length(d) for (c, d) in values) + totaldim = sum(dim(c) * d for (c, d) in truncdim; init = 0) while true next = _findnexttruncvalue(values, truncdim; strategy.by, strategy.rev) isnothing(next) && break @@ -184,14 +193,14 @@ function MAK.findtruncated_svd(values::SectorDict, strategy::TruncationByValue) end function MAK.findtruncated(values::SectorDict, strategy::TruncationByError) - perms = SectorDict(c => sortperm(d; by=abs, rev=true) for (c, d) in values) + perms = SectorDict(c => sortperm(d; by = abs, rev = true) for (c, d) in values) values_sorted = SectorDict(c => d[perms[c]] for (c, d) in Sd) inds = MAK.findtruncated_svd(values_sorted, truncrank(strategy.howmany)) return SectorDict(c => perms[c][I] for (c, I) in inds) end function MAK.findtruncated_svd(values::SectorDict, strategy::TruncationByError) I = keytype(values) - truncdim = SectorDict{I,Int}(c => length(d) for (c, d) in values) + truncdim = SectorDict{I, Int}(c => length(d) for (c, d) in values) by(c, v) = abs(v)^strategy.p * dim(c) Nᵖ = sum(((c, v),) -> sum(Base.Fix1(by, c), v), values) ϵᵖ = max(strategy.atol^strategy.p, strategy.rtol^strategy.p * Nᵖ) @@ -204,7 +213,7 @@ function MAK.findtruncated_svd(values::SectorDict, strategy::TruncationByError) (truncdim[cmin] -= 1) == 0 && delete!(truncdim, cmin) next = _findnexttruncvalue(values, truncdim) end - return SectorDict{I,Base.OneTo{Int}}(c => Base.OneTo(d) for (c, d) in truncdim) + return SectorDict{I, Base.OneTo{Int}}(c => Base.OneTo(d) for (c, d) in truncdim) end function MAK.findtruncated(values::SectorDict, strategy::TruncationSpace) @@ -218,13 +227,19 @@ end function MAK.findtruncated(values::SectorDict, strategy::TruncationIntersection) inds = map(Base.Fix1(MAK.findtruncated, values), strategy) - return SectorDict(c => mapreduce(Base.Fix2(getindex, c), _ind_intersect, inds; - init=trues(length(values[c]))) - for c in intersect(map(keys, inds)...)) + return SectorDict( + c => mapreduce( + Base.Fix2(getindex, c), _ind_intersect, inds; + init = trues(length(values[c])) + ) for c in intersect(map(keys, inds)...) + ) end function MAK.findtruncated_svd(Sd::SectorDict, strategy::TruncationIntersection) inds = map(Base.Fix1(MAK.findtruncated_svd, Sd), strategy) - return SectorDict(c => mapreduce(Base.Fix2(getindex, c), _ind_intersect, inds; - init=trues(length(values[c]))) - for c in intersect(map(keys, inds)...)) + return SectorDict( + c => mapreduce( + Base.Fix2(getindex, c), _ind_intersect, inds; + init = trues(length(values[c])) + ) for c in intersect(map(keys, inds)...) + ) end diff --git a/src/factorizations/utility.jl b/src/factorizations/utility.jl index 46e88c8a9..0d2c3575b 100644 --- a/src/factorizations/utility.jl +++ b/src/factorizations/utility.jl @@ -2,7 +2,7 @@ macro check_space(x, V) return esc(:($MatrixAlgebraKit.@check_size($x, $V, $space))) end -macro check_scalar(x, y, op=:identity, eltype=:scalartype) +macro check_scalar(x, y, op = :identity, eltype = :scalartype) return esc(:($MatrixAlgebraKit.@check_scalar($x, $y, $op, $eltype))) end @@ -16,7 +16,7 @@ function copy_oftype(t::AbstractTensorMap, T::Type{<:Number}) return copy!(similar(t, T, space(t)), t) end -function _reverse!(t::AbstractTensorMap; dims=:) +function _reverse!(t::AbstractTensorMap; dims = :) for (c, b) in blocks(t) reverse!(b; dims) end diff --git a/src/fusiontrees/fusiontrees.jl b/src/fusiontrees/fusiontrees.jl index 3e694e523..419a01c92 100644 --- a/src/fusiontrees/fusiontrees.jl +++ b/src/fusiontrees/fusiontrees.jl @@ -19,18 +19,20 @@ is a more common term. splitting tree. If `FusionStyle(I) isa MultiplicityFreeFusion`, then `vertices` is simply equal to the constant value `ntuple(n->1, L)`. """ -struct FusionTree{I<:Sector,N,M,L} - uncoupled::NTuple{N,I} +struct FusionTree{I <: Sector, N, M, L} + uncoupled::NTuple{N, I} coupled::I - isdual::NTuple{N,Bool} - innerlines::NTuple{M,I} # M = N-2 - vertices::NTuple{L,Int} # L = N-1 - function FusionTree{I,N,M,L}(uncoupled::NTuple{N,I}, - coupled::I, - isdual::NTuple{N,Bool}, - innerlines::NTuple{M,I}, - vertices::NTuple{L,Int}) where - {I<:Sector,N,M,L} + isdual::NTuple{N, Bool} + innerlines::NTuple{M, I} # M = N-2 + vertices::NTuple{L, Int} # L = N-1 + function FusionTree{I, N, M, L}( + uncoupled::NTuple{N, I}, + coupled::I, + isdual::NTuple{N, Bool}, + innerlines::NTuple{M, I}, + vertices::NTuple{L, Int} + ) where + {I <: Sector, N, M, L} # if N == 0 # @assert coupled == one(coupled) # elseif N == 1 @@ -44,30 +46,38 @@ struct FusionTree{I<:Sector,N,M,L} # end # @assert coupled ∈ ⊗(innerlines[N-2], uncoupled[N]) # end - return new{I,N,M,L}(uncoupled, coupled, isdual, innerlines, vertices) + return new{I, N, M, L}(uncoupled, coupled, isdual, innerlines, vertices) end end -function FusionTree{I}(uncoupled::NTuple{N,Any}, coupled, - isdual::NTuple{N,Bool}, innerlines, - vertices=ntuple(n -> 1, max(0, N - 1))) where {I<:Sector,N} - if FusionStyle(I) isa GenericFusion - fusiontreetype(I, N)(map(s -> convert(I, s), uncoupled), - convert(I, coupled), isdual, - map(s -> convert(I, s), innerlines), vertices) +function FusionTree{I}( + uncoupled::NTuple{N, Any}, coupled, + isdual::NTuple{N, Bool}, innerlines, + vertices = ntuple(n -> 1, max(0, N - 1)) + ) where {I <: Sector, N} + return if FusionStyle(I) isa GenericFusion + fusiontreetype(I, N)( + map(s -> convert(I, s), uncoupled), + convert(I, coupled), isdual, + map(s -> convert(I, s), innerlines), vertices + ) else if all(isone, vertices) - fusiontreetype(I, N)(map(s -> convert(I, s), uncoupled), - convert(I, coupled), isdual, - map(s -> convert(I, s), innerlines), vertices) + fusiontreetype(I, N)( + map(s -> convert(I, s), uncoupled), + convert(I, coupled), isdual, + map(s -> convert(I, s), innerlines), vertices + ) else throw(ArgumentError("Incorrect fusion vertices")) end end end -function FusionTree(uncoupled::NTuple{N,I}, coupled::I, - isdual::NTuple{N,Bool}, innerlines, - vertices=ntuple(n -> 1, max(0, N - 1))) where {I<:Sector,N} - if FusionStyle(I) isa GenericFusion +function FusionTree( + uncoupled::NTuple{N, I}, coupled::I, + isdual::NTuple{N, Bool}, innerlines, + vertices = ntuple(n -> 1, max(0, N - 1)) + ) where {I <: Sector, N} + return if FusionStyle(I) isa GenericFusion fusiontreetype(I, N)(uncoupled, coupled, isdual, innerlines, vertices) else if all(isone, vertices) @@ -78,25 +88,28 @@ function FusionTree(uncoupled::NTuple{N,I}, coupled::I, end end -function FusionTree{I}(uncoupled::NTuple{N}, coupled=one(I), - isdual=ntuple(n -> false, N)) where {I<:Sector,N} +function FusionTree{I}( + uncoupled::NTuple{N}, coupled = one(I), isdual = ntuple(n -> false, N) + ) where {I <: Sector, N} FusionStyle(I) isa UniqueFusion || error("fusion tree requires inner lines if `FusionStyle(I) <: MultipleFusion`") - return FusionTree{I}(map(s -> convert(I, s), uncoupled), convert(I, coupled), isdual, - _abelianinner(map(s -> convert(I, s), - (uncoupled..., dual(coupled))))) + return FusionTree{I}( + map(s -> convert(I, s), uncoupled), convert(I, coupled), isdual, + _abelianinner(map(s -> convert(I, s), (uncoupled..., dual(coupled)))) + ) end -function FusionTree(uncoupled::NTuple{N,I}, coupled::I, - isdual=ntuple(n -> false, length(uncoupled))) where {N,I<:Sector} +function FusionTree( + uncoupled::NTuple{N, I}, coupled::I, isdual = ntuple(n -> false, length(uncoupled)) + ) where {N, I <: Sector} return FusionTree{I}(uncoupled, coupled, isdual) end -FusionTree(uncoupled::Tuple{I,Vararg{I}}) where {I<:Sector} = FusionTree(uncoupled, one(I)) +FusionTree(uncoupled::Tuple{I, Vararg{I}}) where {I <: Sector} = FusionTree(uncoupled, one(I)) # Properties -sectortype(::Type{<:FusionTree{I}}) where {I<:Sector} = I -FusionStyle(::Type{<:FusionTree{I}}) where {I<:Sector} = FusionStyle(I) -BraidingStyle(::Type{<:FusionTree{I}}) where {I<:Sector} = BraidingStyle(I) -Base.length(::Type{<:FusionTree{<:Sector,N}}) where {N} = N +sectortype(::Type{<:FusionTree{I}}) where {I <: Sector} = I +FusionStyle(::Type{<:FusionTree{I}}) where {I <: Sector} = FusionStyle(I) +BraidingStyle(::Type{<:FusionTree{I}}) where {I <: Sector} = BraidingStyle(I) +Base.length(::Type{<:FusionTree{<:Sector, N}}) where {N} = N FusionStyle(f::FusionTree) = FusionStyle(typeof(f)) BraidingStyle(f::FusionTree) = BraidingStyle(typeof(f)) @@ -113,7 +126,7 @@ function Base.hash(f::FusionTree{I}, h::UInt) where {I} end return h end -function Base.:(==)(f₁::FusionTree{I,N}, f₂::FusionTree{I,N}) where {I<:Sector,N} +function Base.:(==)(f₁::FusionTree{I, N}, f₂::FusionTree{I, N}) where {I <: Sector, N} f₁.coupled == f₂.coupled || return false @inbounds for i in 1:N f₁.uncoupled[i] == f₂.uncoupled[i] || return false @@ -134,22 +147,22 @@ end Base.:(==)(f₁::FusionTree, f₂::FusionTree) = false # Facilitate getting correct fusion tree types -function fusiontreetype(::Type{I}, N::Int) where {I<:Sector} - if N === 0 - FusionTree{I,0,0,0} +function fusiontreetype(::Type{I}, N::Int) where {I <: Sector} + return if N === 0 + FusionTree{I, 0, 0, 0} elseif N === 1 - FusionTree{I,1,0,0} + FusionTree{I, 1, 0, 0} else - FusionTree{I,N,N - 2,N - 1} + FusionTree{I, N, N - 2, N - 1} end end # converting to actual array -function Base.convert(A::Type{<:AbstractArray}, f::FusionTree{I,0}) where {I} +function Base.convert(A::Type{<:AbstractArray}, f::FusionTree{I, 0}) where {I} X = convert(A, fusiontensor(one(I), one(I), one(I)))[1, 1, :] return X end -function Base.convert(A::Type{<:AbstractArray}, f::FusionTree{I,1}) where {I} +function Base.convert(A::Type{<:AbstractArray}, f::FusionTree{I, 1}) where {I} c = f.coupled if f.isdual[1] sqrtdc = sqrtdim(c) @@ -161,7 +174,7 @@ function Base.convert(A::Type{<:AbstractArray}, f::FusionTree{I,1}) where {I} return X end -function Base.convert(A::Type{<:AbstractArray}, f::FusionTree{I,2}) where {I} +function Base.convert(A::Type{<:AbstractArray}, f::FusionTree{I, 2}) where {I} a, b = f.uncoupled isduala, isdualb = f.isdual c = f.coupled @@ -179,28 +192,32 @@ function Base.convert(A::Type{<:AbstractArray}, f::FusionTree{I,2}) where {I} return X end -function Base.convert(A::Type{<:AbstractArray}, f::FusionTree{I,N}) where {I,N} +function Base.convert(A::Type{<:AbstractArray}, f::FusionTree{I, N}) where {I, N} tailout = (f.innerlines[1], TupleTools.tail2(f.uncoupled)...) isdualout = (false, TupleTools.tail2(f.isdual)...) - ftail = FusionTree(tailout, f.coupled, isdualout, - Base.tail(f.innerlines), Base.tail(f.vertices)) + ftail = FusionTree(tailout, f.coupled, isdualout, Base.tail(f.innerlines), Base.tail(f.vertices)) Ctail = convert(A, ftail) - f₁ = FusionTree((f.uncoupled[1], f.uncoupled[2]), f.innerlines[1], - (f.isdual[1], f.isdual[2]), (), (f.vertices[1],)) + f₁ = FusionTree( + (f.uncoupled[1], f.uncoupled[2]), f.innerlines[1], + (f.isdual[1], f.isdual[2]), (), (f.vertices[1],) + ) C1 = convert(A, f₁) dtail = size(Ctail) d1 = size(C1) X = similar(C1, (d1[1], d1[2], Base.tail(dtail)...)) trivialtuple = ntuple(identity, Val(N)) - return TO.tensorcontract!(X, - C1, ((1, 2), (3,)), false, - Ctail, ((1,), Base.tail(trivialtuple)), false, - ((trivialtuple..., N + 1), ())) + return TO.tensorcontract!( + X, + C1, ((1, 2), (3,)), false, + Ctail, ((1,), Base.tail(trivialtuple)), false, + ((trivialtuple..., N + 1), ()) + ) end # TODO: is this piracy? -function Base.convert(A::Type{<:AbstractArray}, - (f₁, f₂)::Tuple{FusionTree{I},FusionTree{I}}) where {I} +function Base.convert( + A::Type{<:AbstractArray}, (f₁, f₂)::Tuple{FusionTree{I}, FusionTree{I}} + ) where {I} F₁ = convert(A, f₁) F₂ = convert(A, f₂) sz1 = size(F₁) @@ -208,19 +225,25 @@ function Base.convert(A::Type{<:AbstractArray}, d1 = TupleTools.front(sz1) d2 = TupleTools.front(sz2) - return reshape(reshape(F₁, TupleTools.prod(d1), sz1[end]) * - reshape(F₂, TupleTools.prod(d2), sz2[end])', (d1..., d2...)) + return reshape( + reshape(F₁, TupleTools.prod(d1), sz1[end]) * + reshape(F₂, TupleTools.prod(d2), sz2[end])', (d1..., d2...) + ) end # Show methods -function Base.show(io::IO, t::FusionTree{I}) where {I<:Sector} +function Base.show(io::IO, t::FusionTree{I}) where {I <: Sector} if FusionStyle(I) isa GenericFusion - return print(IOContext(io, :typeinfo => I), "FusionTree{", type_repr(I), "}(", - t.uncoupled, ", ", t.coupled, ", ", t.isdual, ", ", t.innerlines, ", ", - t.vertices, ")") + return print( + IOContext(io, :typeinfo => I), "FusionTree{", type_repr(I), "}(", + t.uncoupled, ", ", t.coupled, ", ", t.isdual, ", ", t.innerlines, ", ", + t.vertices, ")" + ) else - return print(IOContext(io, :typeinfo => I), "FusionTree{", type_repr(I), "}(", - t.uncoupled, ", ", t.coupled, ", ", t.isdual, ", ", t.innerlines, ")") + return print( + IOContext(io, :typeinfo => I), "FusionTree{", type_repr(I), "}(", + t.uncoupled, ", ", t.coupled, ", ", t.isdual, ", ", t.innerlines, ")" + ) end end @@ -233,16 +256,16 @@ include("iterator.jl") # auxiliary routines # _abelianinner: generate the inner indices for given outer indices in the abelian case _abelianinner(outer::Tuple{}) = () -function _abelianinner(outer::Tuple{I}) where {I<:Sector} +function _abelianinner(outer::Tuple{I}) where {I <: Sector} return isone(outer[1]) ? () : throw(SectorMismatch()) end -function _abelianinner(outer::Tuple{I,I}) where {I<:Sector} +function _abelianinner(outer::Tuple{I, I}) where {I <: Sector} return outer[1] == dual(outer[2]) ? () : throw(SectorMismatch()) end -function _abelianinner(outer::Tuple{I,I,I}) where {I<:Sector} +function _abelianinner(outer::Tuple{I, I, I}) where {I <: Sector} return isone(first(⊗(outer...))) ? () : throw(SectorMismatch()) end -function _abelianinner(outer::Tuple{I,I,I,I,Vararg{I}}) where {I<:Sector} +function _abelianinner(outer::Tuple{I, I, I, I, Vararg{I}}) where {I <: Sector} c = first(outer[1] ⊗ outer[2]) return (c, _abelianinner((c, TupleTools.tail2(outer)...))...) end diff --git a/src/fusiontrees/iterator.jl b/src/fusiontrees/iterator.jl index 7e7106daf..4284b78e3 100644 --- a/src/fusiontrees/iterator.jl +++ b/src/fusiontrees/iterator.jl @@ -6,16 +6,17 @@ Return an iterator over all fusion trees with a given coupled sector label `coupled` and uncoupled sector labels and isomorphisms `uncoupled` and `isdual` respectively. """ -function fusiontrees(uncoupled::NTuple{N,I}, coupled::I, - isdual::NTuple{N,Bool}) where {N,I<:Sector} +function fusiontrees( + uncoupled::NTuple{N, I}, coupled::I, isdual::NTuple{N, Bool} + ) where {N, I <: Sector} uncouplediterators = map(tuple, uncoupled) return FusionTreeIterator(uncouplediterators, coupled, isdual) end -function fusiontrees(uncoupled::Tuple{Vararg{I}}, coupled::I) where {I<:Sector} +function fusiontrees(uncoupled::Tuple{Vararg{I}}, coupled::I) where {I <: Sector} isdual = ntuple(n -> false, length(uncoupled)) return fusiontrees(uncoupled, coupled, isdual) end -function fusiontrees(uncoupled::Tuple{I,Vararg{I}}) where {I<:Sector} +function fusiontrees(uncoupled::Tuple{I, Vararg{I}}) where {I <: Sector} coupled = one(I) isdual = ntuple(n -> false, length(uncoupled)) return fusiontrees(uncoupled, coupled, isdual) @@ -26,27 +27,27 @@ end # Base.iterate(s::Sector, ::Any) = nothing # TODO: reconsider whether this is desirable; currently it conflicts with the iteration of `ProductSector` -struct FusionTreeIterator{I<:Sector,N,T<:NTuple{N}} +struct FusionTreeIterator{I <: Sector, N, T <: NTuple{N}} uncouplediterators::T # iterators over uncoupled sectors coupled::I - isdual::NTuple{N,Bool} + isdual::NTuple{N, Bool} end Base.IteratorSize(::FusionTreeIterator) = Base.SizeUnknown() Base.IteratorEltype(::FusionTreeIterator) = Base.HasEltype() -Base.eltype(::Type{<:FusionTreeIterator{I,N}}) where {I<:Sector,N} = fusiontreetype(I, N) +Base.eltype(::Type{<:FusionTreeIterator{I, N}}) where {I <: Sector, N} = fusiontreetype(I, N) Base.length(iter::FusionTreeIterator) = _fusiondim(iter.uncouplediterators, iter.coupled) -_fusiondim(::Tuple{}, c::I) where {I<:Sector} = Int(isone(c)) -_fusiondim(iters::NTuple{1}, c::I) where {I<:Sector} = Int(c ∈ iters[1]) -function _fusiondim(iters::NTuple{2}, c::I) where {I<:Sector} +_fusiondim(::Tuple{}, c::I) where {I <: Sector} = Int(isone(c)) +_fusiondim(iters::NTuple{1}, c::I) where {I <: Sector} = Int(c ∈ iters[1]) +function _fusiondim(iters::NTuple{2}, c::I) where {I <: Sector} d = 0 for a in iters[1], b in iters[2] d += Int(Nsymbol(a, b, c)) end return d end -function _fusiondim(iters, c::I) where {I<:Sector} +function _fusiondim(iters, c::I) where {I <: Sector} d = 0 N = length(iters) for b in iters[N] @@ -59,22 +60,22 @@ end # * Iterator methods: # Start with special cases: -function Base.iterate(it::FusionTreeIterator{I,0}, - state=!isone(it.coupled)) where {I<:Sector} +function Base.iterate(it::FusionTreeIterator{I, 0}, state = !isone(it.coupled)) where {I <: Sector} state && return nothing tree = FusionTree{I}((), it.coupled, (), (), ()) return tree, true end -function Base.iterate(it::FusionTreeIterator{I,1}, - state=!(it.coupled ∈ it.uncouplediterators[1])) where {I<:Sector} +function Base.iterate( + it::FusionTreeIterator{I, 1}, state = !(it.coupled ∈ it.uncouplediterators[1]) + ) where {I <: Sector} state && return nothing tree = FusionTree{I}((it.coupled,), it.coupled, it.isdual, (), ()) return tree, true end # General case: -function Base.iterate(it::FusionTreeIterator{I}) where {I<:Sector} +function Base.iterate(it::FusionTreeIterator{I}) where {I <: Sector} coupled = it.coupled next = _fusiontree_iterate(it.uncouplediterators, coupled) next === nothing && return nothing @@ -82,8 +83,7 @@ function Base.iterate(it::FusionTreeIterator{I}) where {I<:Sector} f = FusionTree{I}(out, coupled, it.isdual, lines, vertices) return f, (out, lines, vertices, states) end -function Base.iterate(it::FusionTreeIterator{I}, - (out, lines, vertices, states)) where {I<:Sector} +function Base.iterate(it::FusionTreeIterator{I}, (out, lines, vertices, states)) where {I <: Sector} coupled = it.coupled next = _fusiontree_iterate(it.uncouplediterators, coupled, out, lines, vertices, states) next === nothing && return nothing @@ -92,7 +92,7 @@ function Base.iterate(it::FusionTreeIterator{I}, return f, (out, lines, vertices, states) end -function _fusiontree_iterate(uncoupledsectors::NTuple{2}, c::I) where {I<:Sector} +function _fusiontree_iterate(uncoupledsectors::NTuple{2}, c::I) where {I <: Sector} outiter1 = uncoupledsectors[1] outiter2 = uncoupledsectors[2] nextout2 = iterate(outiter2) @@ -115,8 +115,7 @@ function _fusiontree_iterate(uncoupledsectors::NTuple{2}, c::I) where {I<:Sector return (a, b), (), (n,), (outstate1, outstate2) end -function _fusiontree_iterate(uncoupledsectors::NTuple{2}, c::I, out, lines, - vertices, states) where {I<:Sector} +function _fusiontree_iterate(uncoupledsectors::NTuple{2}, c::I, out, lines, vertices, states) where {I <: Sector} a, b = out n = vertices[1] n < Nsymbol(a, b, c) && return out, lines, (n + 1,), states @@ -145,8 +144,7 @@ function _fusiontree_iterate(uncoupledsectors::NTuple{2}, c::I, out, lines, return (a, b), (), (n,), (outstate1, outstate2) end -function _fusiontree_iterate(uncoupledsectors::NTuple{N}, - coupled::I) where {N,I<:Sector} +function _fusiontree_iterate(uncoupledsectors::NTuple{N}, coupled::I) where {N, I <: Sector} outiterN = uncoupledsectors[N] nextout = iterate(outiterN) nextout === nothing && return nothing @@ -183,8 +181,7 @@ function _fusiontree_iterate(uncoupledsectors::NTuple{N}, return out, lines, vertices, states end -function _fusiontree_iterate(uncoupledsectors::NTuple{N}, coupled::I, out, lines, - vertices, states) where {N,I<:Sector} +function _fusiontree_iterate(uncoupledsectors::NTuple{N}, coupled::I, out, lines, vertices, states) where {N, I <: Sector} a = lines[end] b = out[end] c = coupled @@ -192,8 +189,9 @@ function _fusiontree_iterate(uncoupledsectors::NTuple{N}, coupled::I, out, lines restlines = Base.front(lines) restvertices = Base.front(vertices) reststates = Base.front(Base.front(states)) - rest = _fusiontree_iterate(Base.front(uncoupledsectors), a, restout, restlines, - restvertices, reststates) + rest = _fusiontree_iterate( + Base.front(uncoupledsectors), a, restout, restlines, restvertices, reststates + ) outiterN = uncoupledsectors[N] vertexiterN = c ⊗ dual(b) outstateN = states[end] diff --git a/src/fusiontrees/manipulations.jl b/src/fusiontrees/manipulations.jl index f19bb06ed..6455e9d1a 100644 --- a/src/fusiontrees/manipulations.jl +++ b/src/fusiontrees/manipulations.jl @@ -13,7 +13,7 @@ Attach a fusion tree `f₂` to the uncoupled leg `i` of the fusion tree `f₁` a into a linear combination of fusion trees in standard form. This requires that `f₂.coupled == f₁.uncoupled[i]` and `f₁.isdual[i] == false`. """ -function insertat(f₁::FusionTree{I}, i::Int, f₂::FusionTree{I,0}) where {I} +function insertat(f₁::FusionTree{I}, i::Int, f₂::FusionTree{I, 0}) where {I} # this actually removes uncoupled line i, which should be trivial (f₁.uncoupled[i] == f₂.coupled && !f₁.isdual[i]) || throw(SectorMismatch("cannot connect $(f₂.uncoupled) to $(f₁.uncoupled[i])")) @@ -35,7 +35,7 @@ function insertat(f₁::FusionTree{I}, i::Int, f₂::FusionTree{I,0}) where {I} f = FusionTree(uncoupled, coupled, isdual, inner, vertices) return fusiontreedict(I)(f => coeff) end -function insertat(f₁::FusionTree{I}, i, f₂::FusionTree{I,1}) where {I} +function insertat(f₁::FusionTree{I}, i, f₂::FusionTree{I, 1}) where {I} # identity operation (f₁.uncoupled[i] == f₂.coupled && !f₁.isdual[i]) || throw(SectorMismatch("cannot connect $(f₂.uncoupled) to $(f₁.uncoupled[i])")) @@ -44,7 +44,7 @@ function insertat(f₁::FusionTree{I}, i, f₂::FusionTree{I,1}) where {I} f = FusionTree{I}(f₁.uncoupled, f₁.coupled, isdual′, f₁.innerlines, f₁.vertices) return fusiontreedict(I)(f => coeff) end -function insertat(f₁::FusionTree{I}, i, f₂::FusionTree{I,2}) where {I} +function insertat(f₁::FusionTree{I}, i, f₂::FusionTree{I, 2}) where {I} # elementary building block, (f₁.uncoupled[i] == f₂.coupled && !f₁.isdual[i]) || throw(SectorMismatch("cannot connect $(f₂.uncoupled) to $(f₁.uncoupled[i])")) @@ -106,14 +106,14 @@ function insertat(f₁::FusionTree{I}, i, f₂::FusionTree{I,2}) where {I} return newtrees end end -function insertat(f₁::FusionTree{I,N₁}, i, f₂::FusionTree{I,N₂}) where {I,N₁,N₂} +function insertat(f₁::FusionTree{I, N₁}, i, f₂::FusionTree{I, N₂}) where {I, N₁, N₂} F = fusiontreetype(I, N₁ + N₂ - 1) (f₁.uncoupled[i] == f₂.coupled && !f₁.isdual[i]) || throw(SectorMismatch("cannot connect $(f₂.uncoupled) to $(f₁.uncoupled[i])")) T = sectorscalartype(I) coeff = one(T) if length(f₁) == 1 - return fusiontreedict(I){F,T}(f₂ => coeff) + return fusiontreedict(I){F, T}(f₂ => coeff) end if i == 1 uncoupled = (f₂.uncoupled..., tail(f₁.uncoupled)...) @@ -122,18 +122,18 @@ function insertat(f₁::FusionTree{I,N₁}, i, f₂::FusionTree{I,N₂}) where { vertices = (f₂.vertices..., f₁.vertices...) coupled = f₁.coupled f′ = FusionTree(uncoupled, coupled, isdual, inner, vertices) - return fusiontreedict(I){F,T}(f′ => coeff) + return fusiontreedict(I){F, T}(f′ => coeff) else # recursive definition N2 = length(f₂) f₂′, f₂′′ = split(f₂, N2 - 1) - local newtrees::fusiontreedict(I){F,T} + local newtrees::fusiontreedict(I){F, T} for (f, coeff) in insertat(f₁, i, f₂′′) for (f′, coeff′) in insertat(f, i, f₂′) if @isdefined newtrees coeff′′ = coeff * coeff′ newtrees[f′] = get(newtrees, f′, zero(coeff′′)) + coeff′′ else - newtrees = fusiontreedict(I){F,T}(f′ => coeff * coeff′) + newtrees = fusiontreedict(I){F, T}(f′ => coeff * coeff′) end end end @@ -153,7 +153,7 @@ remaining `N-M` uncoupled sectors of `f`. It couples to the same sector as `f`. operation is the inverse of `insertat` in the sense that if `f₁, f₂ = split(t, M) ⇒ f == insertat(f₂, 1, f₁)`. """ -@inline function split(f::FusionTree{I,N}, M::Int) where {I,N} +@inline function split(f::FusionTree{I, N}, M::Int) where {I, N} if M > N || M < 0 throw(ArgumentError("M should be between 0 and N = $N")) elseif M === N @@ -210,15 +210,13 @@ sectors are those of `f₁` followed by those of `f₂`, and where the two coupl `FusionStyle(I) == GenericFusion()`, also a degeneracy label `μ` for the fusion of the coupled sectors of `f₁` and `f₂` to `c` needs to be specified. """ -function merge(f₁::FusionTree{I,N₁}, f₂::FusionTree{I,N₂}, - c::I) where {I,N₁,N₂} +function merge(f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}, c::I) where {I, N₁, N₂} if FusionStyle(I) isa GenericFusion throw(ArgumentError("vertex label for merging required")) end return merge(f₁, f₂, c, 1) end -function merge(f₁::FusionTree{I,N₁}, f₂::FusionTree{I,N₂}, - c::I, μ) where {I,N₁,N₂} +function merge(f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}, c::I, μ) where {I, N₁, N₂} if !(c in f₁.coupled ⊗ f₂.coupled) throw(SectorMismatch("cannot fuse sectors $(f₁.coupled) and $(f₂.coupled) to $c")) end @@ -230,7 +228,7 @@ function merge(f₁::FusionTree{I,N₁}, f₂::FusionTree{I,N₂}, @assert coeff == one(coeff) return insertat(f, N₁ + 1, f₂) end -function merge(f₁::FusionTree{I,0}, f₂::FusionTree{I,0}, c::I, μ) where {I} +function merge(f₁::FusionTree{I, 0}, f₂::FusionTree{I, 0}, c::I, μ) where {I} Nsymbol(f₁.coupled, f₂.coupled, c) == μ == 1 || throw(SectorMismatch("cannot fuse sectors $(f₁.coupled) and $(f₂.coupled) to $c")) return fusiontreedict(I)(f₁ => Fsymbol(c, c, c, c, c, c)[1, 1, 1, 1]) @@ -244,8 +242,7 @@ end # -> A-move (foldleft, foldright) is complicated, needs to be reexpressed in standard form # flip a duality flag of a fusion tree -function flip(f₁::FusionTree{I,N₁}, f₂::FusionTree{I,N₂}, i::Int; - inv::Bool=false) where {I<:Sector,N₁,N₂} +function flip(f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}, i::Int; inv::Bool = false) where {I <: Sector, N₁, N₂} @assert 0 < i ≤ N₁ + N₂ if i ≤ N₁ a = f₁.uncoupled[i] @@ -274,8 +271,7 @@ function flip(f₁::FusionTree{I,N₁}, f₂::FusionTree{I,N₂}, i::Int; return SingletonDict((f₁, f₂′) => factor) end end -function flip(f₁::FusionTree{I,N₁}, f₂::FusionTree{I,N₂}, ind; - inv::Bool=false) where {I<:Sector,N₁,N₂} +function flip(f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}, ind; inv::Bool = false) where {I <: Sector, N₁, N₂} f₁′, f₂′ = f₁, f₂ factor = one(sectorscalartype(I)) for i in ind @@ -286,7 +282,7 @@ function flip(f₁::FusionTree{I,N₁}, f₂::FusionTree{I,N₂}, ind; end # change to N₁ - 1, N₂ + 1 -function bendright(f₁::FusionTree{I,N₁}, f₂::FusionTree{I,N₂}) where {I<:Sector,N₁,N₂} +function bendright(f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}) where {I <: Sector, N₁, N₂} # map final splitting vertex (a, b)<-c to fusion vertex a<-(c, dual(b)) @assert N₁ > 0 c = f₁.coupled @@ -334,13 +330,13 @@ end # change to N₁ + 1, N₂ - 1 function bendleft(f₁::FusionTree{I}, f₂::FusionTree{I}) where {I} # map final fusion vertex c<-(a, b) to splitting vertex (c, dual(b))<-a - return fusiontreedict(I)((f₁′, f₂′) => conj(coeff) - for - ((f₂′, f₁′), coeff) in bendright(f₂, f₁)) + return fusiontreedict(I)( + (f₁′, f₂′) => conj(coeff) for ((f₂′, f₁′), coeff) in bendright(f₂, f₁) + ) end # change to N₁ - 1, N₂ + 1 -function foldright(f₁::FusionTree{I,N₁}, f₂::FusionTree{I,N₂}) where {I<:Sector,N₁,N₂} +function foldright(f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}) where {I <: Sector, N₁, N₂} # map first splitting vertex (a, b)<-c to fusion vertex b<-(dual(a), c) @assert N₁ > 0 a = f₁.uncoupled[1] @@ -384,7 +380,7 @@ function foldright(f₁::FusionTree{I,N₁}, f₂::FusionTree{I,N₂}) where {I< coeff = factor * coeff1 * conj(coeff2) if (@isdefined newtrees) newtrees[(fl, fr)] = get(newtrees, (fl, fr), zero(coeff)) + - coeff + coeff else newtrees = fusiontreedict(I)((fl, fr) => coeff) end @@ -398,9 +394,9 @@ end # change to N₁ + 1, N₂ - 1 function foldleft(f₁::FusionTree{I}, f₂::FusionTree{I}) where {I} # map first fusion vertex c<-(a, b) to splitting vertex (dual(a), c)<-b - return fusiontreedict(I)((f₁′, f₂′) => conj(coeff) - for - ((f₂′, f₁′), coeff) in foldright(f₂, f₁)) + return fusiontreedict(I)( + (f₁′, f₂′) => conj(coeff) for ((f₂′, f₁′), coeff) in foldright(f₂, f₁) + ) end # COMPOSITE DUALITY MANIPULATIONS PART 1: Repartition and transpose @@ -423,7 +419,7 @@ function iscyclicpermutation(v1, v2) end # clockwise cyclic permutation while preserving (N₁, N₂): foldright & bendleft -function cycleclockwise(f₁::FusionTree{I}, f₂::FusionTree{I}) where {I<:Sector} +function cycleclockwise(f₁::FusionTree{I}, f₂::FusionTree{I}) where {I <: Sector} local newtrees if length(f₁) > 0 for ((f1a, f2a), coeffa) in foldright(f₁, f₂) @@ -452,7 +448,7 @@ function cycleclockwise(f₁::FusionTree{I}, f₂::FusionTree{I}) where {I<:Sect end # anticlockwise cyclic permutation while preserving (N₁, N₂): foldleft & bendright -function cycleanticlockwise(f₁::FusionTree{I}, f₂::FusionTree{I}) where {I<:Sector} +function cycleanticlockwise(f₁::FusionTree{I}, f₂::FusionTree{I}) where {I <: Sector} local newtrees if length(f₂) > 0 for ((f1a, f2a), coeffa) in foldleft(f₁, f₂) @@ -492,17 +488,17 @@ outgoing (`f₁`) and incoming sectors (`f₂`) respectively (with identical cou repartitioning the tree by bending incoming to outgoing sectors (or vice versa) in order to have `N` outgoing sectors. """ -@inline function repartition(f₁::FusionTree{I,N₁}, - f₂::FusionTree{I,N₂}, - N::Int) where {I<:Sector,N₁,N₂} +@inline function repartition( + f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}, N::Int + ) where {I <: Sector, N₁, N₂} f₁.coupled == f₂.coupled || throw(SectorMismatch()) @assert 0 <= N <= N₁ + N₂ return _recursive_repartition(f₁, f₂, Val(N)) end -function _recursive_repartition(f₁::FusionTree{I,N₁}, - f₂::FusionTree{I,N₂}, - ::Val{N}) where {I<:Sector,N₁,N₂,N} +function _recursive_repartition( + f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}, ::Val{N} + ) where {I <: Sector, N₁, N₂, N} # recursive definition is only way to get correct number of loops for # GenericFusion, but is too complex for type inference to handle, so we # precompute the parameters of the return type @@ -511,16 +507,17 @@ function _recursive_repartition(f₁::FusionTree{I,N₁}, T = sectorscalartype(I) coeff = one(T) if N == N₁ - return fusiontreedict(I){Tuple{F₁,F₂},T}((f₁, f₂) => coeff) + return fusiontreedict(I){Tuple{F₁, F₂}, T}((f₁, f₂) => coeff) else - local newtrees::fusiontreedict(I){Tuple{F₁,F₂},T} + local newtrees::fusiontreedict(I){Tuple{F₁, F₂}, T} for ((f₁′, f₂′), coeff1) in (N < N₁ ? bendright(f₁, f₂) : bendleft(f₁, f₂)) for ((f₁′′, f₂′′), coeff2) in _recursive_repartition(f₁′, f₂′, Val(N)) if (@isdefined newtrees) push!(newtrees, (f₁′′, f₂′′) => coeff1 * coeff2) else - newtrees = fusiontreedict(I){Tuple{F₁,F₂},T}((f₁′′, f₂′′) => coeff1 * - coeff2) + newtrees = fusiontreedict(I){Tuple{F₁, F₂}, T}( + (f₁′′, f₂′′) => coeff1 * coeff2 + ) end end end @@ -540,8 +537,9 @@ outgoing (`t1`) and incoming sectors (`t2`) respectively (with identical coupled repartitioning and permuting the tree such that sectors `p1` become outgoing and sectors `p2` become incoming. """ -function Base.transpose(f₁::FusionTree{I}, f₂::FusionTree{I}, - p1::IndexTuple{N₁}, p2::IndexTuple{N₂}) where {I<:Sector,N₁,N₂} +function Base.transpose( + f₁::FusionTree{I}, f₂::FusionTree{I}, p1::IndexTuple{N₁}, p2::IndexTuple{N₂} + ) where {I <: Sector, N₁, N₂} N = N₁ + N₂ @assert length(f₁) + length(f₂) == N p = linearizepermutation(p1, p2, length(f₁), length(f₂)) @@ -549,20 +547,19 @@ function Base.transpose(f₁::FusionTree{I}, f₂::FusionTree{I}, return fstranspose((f₁, f₂, p1, p2)) end -const FSTransposeKey{I<:Sector,N₁,N₂} = Tuple{<:FusionTree{I},<:FusionTree{I}, - IndexTuple{N₁},IndexTuple{N₂}} +const FSTransposeKey{I <: Sector, N₁, N₂} = Tuple{ + <:FusionTree{I}, <:FusionTree{I}, + IndexTuple{N₁}, IndexTuple{N₂}, +} function _fsdicttype(I, N₁, N₂) F₁ = fusiontreetype(I, N₁) F₂ = fusiontreetype(I, N₂) T = sectorscalartype(I) - return fusiontreedict(I){Tuple{F₁,F₂},T} + return fusiontreedict(I){Tuple{F₁, F₂}, T} end -@cached function fstranspose(key::FSTransposeKey{I,N₁,N₂})::_fsdicttype(I, N₁, - N₂) where {I<:Sector, - N₁, - N₂} +@cached function fstranspose(key::FSTransposeKey{I, N₁, N₂})::_fsdicttype(I, N₁, N₂) where {I <: Sector, N₁, N₂} f₁, f₂, p1, p2 = key N = N₁ + N₂ p = linearizepermutation(p1, p2, length(f₁), length(f₂)) @@ -605,7 +602,7 @@ end return newtrees end -function CacheStyle(::typeof(fstranspose), k::FSTransposeKey{I}) where {I<:Sector} +function CacheStyle(::typeof(fstranspose), k::FSTransposeKey{I}) where {I <: Sector} if FusionStyle(I) isa UniqueFusion return NoCache() else @@ -618,29 +615,35 @@ end # -> composite manipulations that depend on the duality (rigidity) and pivotal structure # -> planar manipulations that do not require braiding, everything is in Fsymbol (A/Bsymbol) -function planar_trace(f₁::FusionTree{I}, f₂::FusionTree{I}, - p1::IndexTuple{N₁}, p2::IndexTuple{N₂}, - q1::IndexTuple{N₃}, q2::IndexTuple{N₃}) where {I<:Sector,N₁,N₂,N₃} +function planar_trace( + f₁::FusionTree{I}, f₂::FusionTree{I}, + p1::IndexTuple{N₁}, p2::IndexTuple{N₂}, + q1::IndexTuple{N₃}, q2::IndexTuple{N₃} + ) where {I <: Sector, N₁, N₂, N₃} N = N₁ + N₂ + 2N₃ @assert length(f₁) + length(f₂) == N if N₃ == 0 return transpose(f₁, f₂, p1, p2) end - linearindex = (ntuple(identity, Val(length(f₁)))..., - reverse(length(f₁) .+ ntuple(identity, Val(length(f₂))))...) + linearindex = ( + ntuple(identity, Val(length(f₁)))..., + reverse(length(f₁) .+ ntuple(identity, Val(length(f₂))))..., + ) q1′ = TupleTools.getindices(linearindex, q1) q2′ = TupleTools.getindices(linearindex, q2) p1′, p2′ = let q′ = (q1′..., q2′...) - (map(l -> l - count(l .> q′), TupleTools.getindices(linearindex, p1)), - map(l -> l - count(l .> q′), TupleTools.getindices(linearindex, p2))) + ( + map(l -> l - count(l .> q′), TupleTools.getindices(linearindex, p1)), + map(l -> l - count(l .> q′), TupleTools.getindices(linearindex, p2)), + ) end T = sectorscalartype(I) F₁ = fusiontreetype(I, N₁) F₂ = fusiontreetype(I, N₂) - newtrees = FusionTreeDict{Tuple{F₁,F₂},T}() + newtrees = FusionTreeDict{Tuple{F₁, F₂}, T}() for ((f₁′, f₂′), coeff′) in repartition(f₁, f₂, N) for (f₁′′, coeff′′) in planar_trace(f₁′, q1′, q2′) for (f12′′′, coeff′′′) in transpose(f₁′′, f₂′, p1′, p2′) @@ -662,11 +665,12 @@ Perform a planar trace of the uncoupled indices of the fusion tree `f` at `q1` w `q2`, where `q1[i]` is connected to `q2[i]` for all `i`. The result is returned as a dictionary of output trees and corresponding coefficients. """ -function planar_trace(f::FusionTree{I,N}, - q1::IndexTuple{N₃}, q2::IndexTuple{N₃}) where {I<:Sector,N,N₃} +function planar_trace( + f::FusionTree{I, N}, q1::IndexTuple{N₃}, q2::IndexTuple{N₃} + ) where {I <: Sector, N, N₃} T = sectorscalartype(I) F = fusiontreetype(I, N - 2 * N₃) - newtrees = FusionTreeDict{F,T}() + newtrees = FusionTreeDict{F, T}() N₃ === 0 && return push!(newtrees, f => one(T)) for (i, j) in zip(q1, q2) @@ -715,7 +719,7 @@ Perform an elementary trace of neighbouring uncoupled indices `i` and `i+1` on a fusion tree `f`, and returns the result as a dictionary of output trees and corresponding coefficients. """ -function elementary_trace(f::FusionTree{I,N}, i) where {I<:Sector,N} +function elementary_trace(f::FusionTree{I, N}, i) where {I <: Sector, N} (N > 1 && 1 <= i <= N) || throw(ArgumentError("Cannot trace outputs i=$i and i+1 out of only $N outputs")) i < N || isone(f.coupled) || @@ -723,7 +727,7 @@ function elementary_trace(f::FusionTree{I,N}, i) where {I<:Sector,N} T = sectorscalartype(I) F = fusiontreetype(I, N - 2) - newtrees = FusionTreeDict{F,T}() + newtrees = FusionTreeDict{F, T}() j = mod1(i + 1, N) b = f.uncoupled[i] @@ -731,8 +735,7 @@ function elementary_trace(f::FusionTree{I,N}, i) where {I<:Sector,N} # if trace is zero, return empty dict (b == dual(b′) && f.isdual[i] != f.isdual[j]) || return newtrees if i < N - inner_extended = (leftone(f.uncoupled[1]), f.uncoupled[1], f.innerlines..., - f.coupled) + inner_extended = (leftone(f.uncoupled[1]), f.uncoupled[1], f.innerlines..., f.coupled) a = inner_extended[i] d = inner_extended[i + 2] a == d || return newtrees @@ -743,13 +746,13 @@ function elementary_trace(f::FusionTree{I,N}, i) where {I<:Sector,N} inner′ = () else inner′ = i <= 2 ? Base.tail(Base.tail(f.innerlines)) : - TupleTools.deleteat(TupleTools.deleteat(f.innerlines, i - 1), i - 2) + TupleTools.deleteat(TupleTools.deleteat(f.innerlines, i - 1), i - 2) end if N <= 3 vertices′ = () else vertices′ = i <= 2 ? Base.tail(Base.tail(f.vertices)) : - TupleTools.deleteat(TupleTools.deleteat(f.vertices, i), i - 1) + TupleTools.deleteat(TupleTools.deleteat(f.vertices, i), i - 1) end f′ = FusionTree{I}(uncoupled′, coupled′, isdual′, inner′, vertices′) coeff = sqrtdim(b) @@ -820,7 +823,7 @@ applying `artin_braid(f′, i; inv = true)` to all the outputs `f′` of tree with non-zero coefficient, namely `f` with coefficient `1`. This keyword has no effect if `BraidingStyle(sectortype(f)) isa SymmetricBraiding`. """ -function artin_braid(f::FusionTree{I,N}, i; inv::Bool=false) where {I<:Sector,N} +function artin_braid(f::FusionTree{I, N}, i; inv::Bool = false) where {I <: Sector, N} 1 <= i < N || throw(ArgumentError("Cannot swap outputs i=$i and i+1 out of only $N outputs")) uncoupled = f.uncoupled @@ -840,9 +843,11 @@ function artin_braid(f::FusionTree{I,N}, i; inv::Bool=false) where {I<:Sector,N} inner′ = inner vertices′ = vertices if i > 1 # we also need to alter innerlines and vertices - inner′ = TupleTools.setindex(inner, - inner_extended[isone(a) ? (i + 1) : (i - 1)], - i - 1) + inner′ = TupleTools.setindex( + inner, + inner_extended[isone(a) ? (i + 1) : (i - 1)], + i - 1 + ) vertices′ = TupleTools.setindex(vertices′, vertices[i], i - 1) vertices′ = TupleTools.setindex(vertices′, vertices[i - 1], i) end @@ -885,28 +890,28 @@ function artin_braid(f::FusionTree{I,N}, i; inv::Bool=false) where {I<:Sector,N} e = inner_extended[i + 1] if FusionStyle(I) isa UniqueFusion c′ = first(a ⊗ d) - coeff = oftype(oneT, - if inv - conj(Rsymbol(d, c, e) * Fsymbol(d, a, b, e, c′, c)) * - Rsymbol(d, a, c′) - else - Rsymbol(c, d, e) * - conj(Fsymbol(d, a, b, e, c′, c) * Rsymbol(a, d, c′)) - end) + coeff = oftype( + oneT, + if inv + conj(Rsymbol(d, c, e) * Fsymbol(d, a, b, e, c′, c)) * Rsymbol(d, a, c′) + else + Rsymbol(c, d, e) * conj(Fsymbol(d, a, b, e, c′, c) * Rsymbol(a, d, c′)) + end + ) inner′ = TupleTools.setindex(inner, c′, i - 1) f′ = FusionTree{I}(uncoupled′, coupled′, isdual′, inner′) return fusiontreedict(I)(f′ => coeff) elseif FusionStyle(I) isa SimpleFusion local newtrees for c′ in intersect(a ⊗ d, e ⊗ conj(b)) - coeff = oftype(oneT, - if inv - conj(Rsymbol(d, c, e) * Fsymbol(d, a, b, e, c′, c)) * - Rsymbol(d, a, c′) - else - Rsymbol(c, d, e) * - conj(Fsymbol(d, a, b, e, c′, c) * Rsymbol(a, d, c′)) - end) + coeff = oftype( + oneT, + if inv + conj(Rsymbol(d, c, e) * Fsymbol(d, a, b, e, c′, c)) * Rsymbol(d, a, c′) + else + Rsymbol(c, d, e) * conj(Fsymbol(d, a, b, e, c′, c) * Rsymbol(a, d, c′)) + end + ) iszero(coeff) && continue inner′ = TupleTools.setindex(inner, c′, i - 1) f′ = FusionTree{I}(uncoupled′, coupled′, isdual′, inner′) @@ -963,9 +968,9 @@ that if `i` and `j` cross, ``τ_{i,j}`` is applied if `levels[i] < levels[j]` an ``τ_{j,i}^{-1}`` if `levels[i] > levels[j]`. This does not allow to encode the most general braid, but a general braid can be obtained by combining such operations. """ -function braid(f::FusionTree{I,N}, - levels::NTuple{N,Int}, - p::NTuple{N,Int}) where {I<:Sector,N} +function braid( + f::FusionTree{I, N}, levels::NTuple{N, Int}, p::NTuple{N, Int} + ) where {I <: Sector, N} TupleTools.isperm(p) || throw(ArgumentError("not a valid permutation: $p")) if FusionStyle(I) isa UniqueFusion && BraidingStyle(I) isa SymmetricBraiding coeff = one(sectorscalartype(I)) @@ -990,7 +995,7 @@ function braid(f::FusionTree{I,N}, for s in permutation2swaps(p) inv = levels[s] > levels[s + 1] for (f, c) in trees - for (f′, c′) in artin_braid(f, s; inv=inv) + for (f′, c′) in artin_braid(f, s; inv) newtrees[f′] = get(newtrees, f′, zero(coeff)) + c * c′ end end @@ -1012,7 +1017,7 @@ Perform a permutation of the uncoupled indices of the fusion tree `f` and return as a `<:AbstractDict` of output trees and corresponding coefficients; this requires that `BraidingStyle(sectortype(f)) isa SymmetricBraiding`. """ -function permute(f::FusionTree{I,N}, p::NTuple{N,Int}) where {I<:Sector,N} +function permute(f::FusionTree{I, N}, p::NTuple{N, Int}) where {I <: Sector, N} @assert BraidingStyle(I) isa SymmetricBraiding return braid(f, ntuple(identity, Val(N)), p) end @@ -1036,20 +1041,23 @@ respectively, which determines how indices braid. In particular, if `i` and `j` levels[j]`. This does not allow to encode the most general braid, but a general braid can be obtained by combining such operations. """ -function braid(f₁::FusionTree{I}, f₂::FusionTree{I}, - levels1::IndexTuple, levels2::IndexTuple, - p1::IndexTuple{N₁}, p2::IndexTuple{N₂}) where {I<:Sector,N₁,N₂} +function braid( + f₁::FusionTree{I}, f₂::FusionTree{I}, + levels1::IndexTuple, levels2::IndexTuple, + p1::IndexTuple{N₁}, p2::IndexTuple{N₂} + ) where {I <: Sector, N₁, N₂} @assert length(f₁) + length(f₂) == N₁ + N₂ @assert length(f₁) == length(levels1) && length(f₂) == length(levels2) @assert TupleTools.isperm((p1..., p2...)) return fsbraid((f₁, f₂, levels1, levels2, p1, p2)) end -const FSBraidKey{I<:Sector,N₁,N₂} = Tuple{<:FusionTree{I},<:FusionTree{I}, - IndexTuple,IndexTuple, - IndexTuple{N₁},IndexTuple{N₂}} +const FSBraidKey{I <: Sector, N₁, N₂} = Tuple{ + <:FusionTree{I}, <:FusionTree{I}, + IndexTuple, IndexTuple, + IndexTuple{N₁}, IndexTuple{N₂}, +} -@cached function fsbraid(key::FSBraidKey{I,N₁,N₂})::_fsdicttype(I, N₁, - N₂) where {I<:Sector,N₁,N₂} +@cached function fsbraid(key::FSBraidKey{I, N₁, N₂})::_fsdicttype(I, N₁, N₂) where {I <: Sector, N₁, N₂} (f₁, f₂, l1, l2, p1, p2) = key p = linearizepermutation(p1, p2, length(f₁), length(f₂)) levels = (l1..., reverse(l2)...) @@ -1059,7 +1067,7 @@ const FSBraidKey{I<:Sector,N₁,N₂} = Tuple{<:FusionTree{I},<:FusionTree{I}, for ((f₁′, f₂′), coeff3) in repartition(f′, f0, N₁) if @isdefined newtrees newtrees[(f₁′, f₂′)] = get(newtrees, (f₁′, f₂′), zero(coeff3)) + - coeff1 * coeff2 * coeff3 + coeff1 * coeff2 * coeff3 else newtrees = fusiontreedict(I)((f₁′, f₂′) => coeff1 * coeff2 * coeff3) end @@ -1069,7 +1077,7 @@ const FSBraidKey{I<:Sector,N₁,N₂} = Tuple{<:FusionTree{I},<:FusionTree{I}, return newtrees end -function CacheStyle(::typeof(fsbraid), k::FSBraidKey{I}) where {I<:Sector} +function CacheStyle(::typeof(fsbraid), k::FSBraidKey{I}) where {I <: Sector} if FusionStyle(I) isa UniqueFusion return NoCache() else @@ -1089,8 +1097,10 @@ outgoing (`t1`) and incoming sectors (`t2`) respectively (with identical coupled repartitioning and permuting the tree such that sectors `p1` become outgoing and sectors `p2` become incoming. """ -function permute(f₁::FusionTree{I}, f₂::FusionTree{I}, - p1::IndexTuple{N₁}, p2::IndexTuple{N₂}) where {I<:Sector,N₁,N₂} +function permute( + f₁::FusionTree{I}, f₂::FusionTree{I}, + p1::IndexTuple{N₁}, p2::IndexTuple{N₂} + ) where {I <: Sector, N₁, N₂} @assert BraidingStyle(I) isa SymmetricBraiding levels1 = ntuple(identity, length(f₁)) levels2 = length(f₁) .+ ntuple(identity, length(f₂)) diff --git a/src/planar/analyzers.jl b/src/planar/analyzers.jl index 4d22cf0ab..41585e8c1 100644 --- a/src/planar/analyzers.jl +++ b/src/planar/analyzers.jl @@ -80,8 +80,10 @@ function possible_planar_complements(ind1, ind2) # general case: j1 = findfirst(in(ind2), ind1) if j1 === nothing # disconnected diagrams, can be made planar in various ways - return Any[(circshift(ind1, i - 1), circshift(ind2, j - 1), Any[], Any[]) - for i in eachindex(ind1), j in eachindex(ind2)] + return Any[ + (circshift(ind1, i - 1), circshift(ind2, j - 1), Any[], Any[]) + for i in eachindex(ind1), j in eachindex(ind2) + ] else # genuine contraction N1, N2 = length(ind1), length(ind2) j2 = findfirst(==(ind1[j1]), ind2) @@ -104,11 +106,11 @@ function possible_planar_complements(ind1, ind2) jmin2 -= N2 end indo1 = jmin1 < 1 ? ind1[(jmax1 + 1):mod1(jmin1 - 1, N1)] : - vcat(ind1[(jmax1 + 1):N1], ind1[1:(jmin1 - 1)]) + vcat(ind1[(jmax1 + 1):N1], ind1[1:(jmin1 - 1)]) cind1 = jmin1 < 1 ? vcat(ind1[mod1(jmin1, N1):N1], ind1[1:jmax1]) : - ind1[jmin1:jmax1] + ind1[jmin1:jmax1] indo2 = jmin2 < 1 ? ind2[(jmax2 + 1):mod1(jmin2 - 1, N2)] : - vcat(ind2[(jmax2 + 1):N2], ind2[1:(jmin2 - 1)]) + vcat(ind2[(jmax2 + 1):N2], ind2[1:(jmin2 - 1)]) cind2 = reverse(cind1) return isempty(intersect(indo1, indo2)) ? Any[(indo1, indo2, cind1, cind2)] : Any[] end diff --git a/src/planar/macros.jl b/src/planar/macros.jl index e60167572..c2afcb596 100644 --- a/src/planar/macros.jl +++ b/src/planar/macros.jl @@ -50,8 +50,9 @@ function planarparser(planarexpr, kwargs...) isexpr(val, :tuple) || throw(ArgumentError("Invalid use of `order`, should be `order=(...,)`")) indexorder = map(normalizeindex, val.args) - parser.contractiontreebuilder = network -> TO.indexordertree(network, - indexorder) + parser.contractiontreebuilder = network -> TO.indexordertree( + network, indexorder + ) elseif name == :contractcheck val isa Bool || @@ -79,8 +80,10 @@ function planarparser(planarexpr, kwargs...) treebuilder = parser.contractiontreebuilder treesorter = parser.contractiontreesorter costcheck = parser.contractioncostcheck - push!(parser.preprocessors, - ex -> TO.processcontractions(ex, treebuilder, treesorter, costcheck)) + push!( + parser.preprocessors, + ex -> TO.processcontractions(ex, treebuilder, treesorter, costcheck) + ) parser.contractioncostcheck = nothing push!(parser.preprocessors, ex -> _check_planarity(ex)) push!(parser.preprocessors, ex -> _decompose_planar_contractions(ex, temporaries)) @@ -119,9 +122,10 @@ function _plansor(expr, kwargs...) tensorex = tparser(expr) planarex = pparser(expr) - push!(args, - Expr(:if, :(BraidingStyle(sectortype($targetsym)) isa Bosonic), tensorex, - planarex)) + push!( + args, + Expr(:if, :(BraidingStyle(sectortype($targetsym)) isa Bosonic), tensorex, planarex) + ) if !isa(targetobj, Symbol) && targetobj ∈ newtensors push!(args, :($targetobj = $targetsym)) end diff --git a/src/planar/planaroperations.jl b/src/planar/planaroperations.jl index 15fce65d0..a06f4431d 100644 --- a/src/planar/planaroperations.jl +++ b/src/planar/planaroperations.jl @@ -26,11 +26,12 @@ function planaradd!(C, A, p::Index2Tuple, α::Number, β::Number, backend, alloc end end # implementation -function planaradd!(C::AbstractTensorMap, - A::AbstractTensorMap, - p::Index2Tuple, - α::Number, β::Number, - backend, allocator) +function planaradd!( + C::AbstractTensorMap, + A::AbstractTensorMap, p::Index2Tuple, + α::Number, β::Number, + backend, allocator + ) return add_transpose!(C, A, p, α, β, backend) end @@ -43,8 +44,9 @@ function planartrace!(C, A, p::Index2Tuple, q::Index2Tuple, α::Number, β::Numb return planartrace!(C, A, p, q, α, β, backend, TO.DefaultAllocator()) end # replace default backend with select_backend mechanism -function planartrace!(C, A, p::Index2Tuple, q::Index2Tuple, α::Number, β::Number, - backend, allocator) +function planartrace!( + C, A, p::Index2Tuple, q::Index2Tuple, α::Number, β::Number, backend, allocator + ) if backend isa TO.DefaultBackend backend = TO.select_backend(planartrace!, C, A) return planartrace!(C, A, p, q, α, β, backend, allocator) @@ -61,10 +63,12 @@ function planartrace!(C, A, p::Index2Tuple, q::Index2Tuple, α::Number, β::Numb end end # implementation -function planartrace!(C::AbstractTensorMap, - A::AbstractTensorMap, (p₁, p₂)::Index2Tuple, (q₁, q₂)::Index2Tuple, - α::Number, β::Number, - backend, allocator) +function planartrace!( + C::AbstractTensorMap, + A::AbstractTensorMap, (p₁, p₂)::Index2Tuple, (q₁, q₂)::Index2Tuple, + α::Number, β::Number, + backend, allocator + ) (S = spacetype(C)) == spacetype(A) || throw(SpaceMismatch("incompatible spacetypes")) if BraidingStyle(sectortype(S)) == Bosonic() @@ -96,27 +100,34 @@ function planartrace!(C::AbstractTensorMap, β′ = One() for (f₁, f₂) in fusiontrees(A) for ((f₁′, f₂′), coeff) in planar_trace(f₁, f₂, p₁, p₂, q₁, q₂) - TO.tensortrace!(C[f₁′, f₂′], A[f₁, f₂], (p₁, p₂), (q₁, q₂), false, α * coeff, - β′, - backend, allocator) + TO.tensortrace!( + C[f₁′, f₂′], A[f₁, f₂], (p₁, p₂), (q₁, q₂), false, α * coeff, β′, + backend, allocator + ) end end return C end # insert default backend -function planarcontract!(C, A, pA::Index2Tuple, B, pB::Index2Tuple, pAB::Index2Tuple, - α::Number, β::Number) +function planarcontract!( + C, A, pA::Index2Tuple, B, pB::Index2Tuple, pAB::Index2Tuple, + α::Number, β::Number + ) return planarcontract!(C, A, pA, B, pB, pAB, α, β, TO.DefaultBackend()) end # insert default allocator -function planarcontract!(C, A, pA::Index2Tuple, B, pB::Index2Tuple, pAB::Index2Tuple, - α::Number, β::Number, backend) +function planarcontract!( + C, A, pA::Index2Tuple, B, pB::Index2Tuple, pAB::Index2Tuple, + α::Number, β::Number, backend + ) return planarcontract!(C, A, pA, B, pB, pAB, α, β, backend, TO.DefaultAllocator()) end # replace default backend with select_backend mechanism -function planarcontract!(C, A, pA::Index2Tuple, B, pB::Index2Tuple, pAB::Index2Tuple, - α::Number, β::Number, backend, allocator) +function planarcontract!( + C, A, pA::Index2Tuple, B, pB::Index2Tuple, pAB::Index2Tuple, + α::Number, β::Number, backend, allocator + ) if backend isa TO.DefaultBackend backend = TO.select_backend(planarcontract!, C, A, B) return planarcontract!(C, A, pA, B, pB, pAB, α, β, backend, allocator) @@ -135,12 +146,14 @@ function planarcontract!(C, A, pA::Index2Tuple, B, pB::Index2Tuple, pAB::Index2T end end # implementation -function planarcontract!(C::AbstractTensorMap, - A::AbstractTensorMap, pA::Index2Tuple, - B::AbstractTensorMap, pB::Index2Tuple, - pAB::Index2Tuple, - α::Number, β::Number, - backend, allocator) +function planarcontract!( + C::AbstractTensorMap, + A::AbstractTensorMap, pA::Index2Tuple, + B::AbstractTensorMap, pB::Index2Tuple, + pAB::Index2Tuple, + α::Number, β::Number, + backend, allocator + ) if BraidingStyle(sectortype(C)) == Bosonic() return contract!(C, A, pA, B, pB, pAB, α, β, backend, allocator) end @@ -149,22 +162,25 @@ function planarcontract!(C::AbstractTensorMap, codB, domB = codomainind(B), domainind(B) oindA, cindA = pA cindB, oindB = pB - oindA, cindA, oindB, cindB = reorder_indices(codA, domA, codB, domB, oindA, cindA, - oindB, cindB, pAB...) + oindA, cindA, oindB, cindB = reorder_indices( + codA, domA, codB, domB, oindA, cindA, oindB, cindB, pAB... + ) if oindA == codA && cindA == domA A′ = A else - A′ = TO.tensoralloc_add(scalartype(A), A, (oindA, cindA), false, Val(true), - allocator) + A′ = TO.tensoralloc_add( + scalartype(A), A, (oindA, cindA), false, Val(true), allocator + ) add_transpose!(A′, A, (oindA, cindA), One(), Zero(), backend) end if cindB == codB && oindB == domB B′ = B else - B′ = TensorOperations.tensoralloc_add(scalartype(B), B, (cindB, oindB), false, - Val(true), allocator) + B′ = TensorOperations.tensoralloc_add( + scalartype(B), B, (cindB, oindB), false, Val(true), allocator + ) add_transpose!(B′, B, (cindB, oindB), One(), Zero(), backend) end mul!(C, A′, B′, α, β) @@ -210,8 +226,9 @@ function reorder_indices(codA, domA, codB, domB, oindA, oindB, p1, p2) end function reorder_indices(codA, domA, codB, domB, oindA, cindA, oindB, cindB, p1, p2) - oindA2, cindA2, oindB2, cindB2 = reorder_indices(codA, domA, codB, domB, oindA, oindB, - p1, p2) + oindA2, cindA2, oindB2, cindB2 = reorder_indices( + codA, domA, codB, domB, oindA, oindB, p1, p2 + ) #if oindA or oindB are empty, then reorder indices can only order it correctly up to a cyclic permutation! if isempty(oindA2) && !isempty(cindA) diff --git a/src/planar/postprocessors.jl b/src/planar/postprocessors.jl index 37eff61d7..7ae527383 100644 --- a/src/planar/postprocessors.jl +++ b/src/planar/postprocessors.jl @@ -5,8 +5,8 @@ # to correct for this by adding the `istemp = true` flag. function _annotate_temporaries(ex, temporaries) if isexpr(ex, :(=)) && isexpr(ex.args[2], :call) && - ex.args[2].args[1] ∈ - (GlobalRef(TO, :tensoralloc_add), GlobalRef(TO, :tensoralloc_contract)) + ex.args[2].args[1] ∈ + (GlobalRef(TO, :tensoralloc_add), GlobalRef(TO, :tensoralloc_contract)) lhs = ex.args[1] i = findfirst(==(lhs), temporaries) if i !== nothing @@ -59,22 +59,30 @@ function _insert_planar_operations(ex) if ex.args[1] == GlobalRef(TensorOperations, :tensoradd!) conjA = popat!(ex.args, 5) @assert !conjA "conj flag should be disabled" - return Expr(ex.head, GlobalRef(TensorKit, Symbol(:planaradd!)), - map(_insert_planar_operations, ex.args[2:end])...) + return Expr( + ex.head, GlobalRef(TensorKit, Symbol(:planaradd!)), + map(_insert_planar_operations, ex.args[2:end])... + ) elseif ex.args[1] == GlobalRef(TensorOperations, :tensorcontract!) conjB = popat!(ex.args, 8) conjA = popat!(ex.args, 5) @assert !conjA && !conjB "conj flags should be disabled ($conjA), ($conjB)" - return Expr(ex.head, GlobalRef(TensorKit, Symbol(:planarcontract!)), - map(_insert_planar_operations, ex.args[2:end])...) + return Expr( + ex.head, GlobalRef(TensorKit, Symbol(:planarcontract!)), + map(_insert_planar_operations, ex.args[2:end])... + ) elseif ex.args[1] == GlobalRef(TensorOperations, :tensortrace!) conjA = popat!(ex.args, 6) @assert !conjA "conj flag should be disabled" - return Expr(ex.head, GlobalRef(TensorKit, Symbol(:planartrace!)), - map(_insert_planar_operations, ex.args[2:end])...) + return Expr( + ex.head, GlobalRef(TensorKit, Symbol(:planartrace!)), + map(_insert_planar_operations, ex.args[2:end])... + ) elseif ex.args[1] in TensorOperations.tensoroperationsfunctions - return Expr(ex.head, GlobalRef(TensorOperations, ex.args[1]), - map(_insert_planar_operations, ex.args[2:end])...) + return Expr( + ex.head, GlobalRef(TensorOperations, ex.args[1]), + map(_insert_planar_operations, ex.args[2:end])... + ) end elseif isa(ex, Expr) return Expr(ex.head, (_insert_planar_operations(e) for e in ex.args)...) diff --git a/src/planar/preprocessors.jl b/src/planar/preprocessors.jl index 031190816..d30406750 100644 --- a/src/planar/preprocessors.jl +++ b/src/planar/preprocessors.jl @@ -5,8 +5,10 @@ function _conj_to_adjoint(ex) if isexpr(ex, :call) && ex.args[1] == :conj && TO.istensor(ex.args[2]) obj, leftind, rightind = TO.decomposetensor(ex.args[2]) - return Expr(:typed_vcat, Expr(TO.prime, obj), - Expr(:tuple, rightind...), Expr(:tuple, leftind...)) + return Expr( + :typed_vcat, Expr(TO.prime, obj), + Expr(:tuple, rightind...), Expr(:tuple, leftind...) + ) elseif ex isa Expr return Expr(ex.head, [_conj_to_adjoint(a) for a in ex.args]...) else @@ -27,10 +29,12 @@ function _extract_tensormap_objects(ex) @assert !any(_is_adjoint, newtensors) existingtensors = unique!(vcat(inputtensors, outputtensors)) alltensors = unique!(vcat(existingtensors, newtensors)) - tensordict = Dict{Any,Any}(a => gensym(string(a)) - for a in alltensors if !(a isa Symbol)) - pre = Expr(:block, - [Expr(:(=), tensordict[a], a) for a in existingtensors if !(a isa Symbol)]...) + tensordict = Dict{Any, Any}( + a => gensym(string(a)) for a in alltensors if !(a isa Symbol) + ) + pre = Expr( + :block, [Expr(:(=), tensordict[a], a) for a in existingtensors if !(a isa Symbol)]... + ) pre2 = Expr(:block) ex = TO.replacetensorobjects(ex) do obj, leftind, rightind _is_adj = _is_adjoint(obj) @@ -43,9 +47,11 @@ function _extract_tensormap_objects(ex) nl = length(leftind) nr = length(rightind) tensor_inds = :((numout($newobj)), (numin($newobj))) - errorstr = Expr(:string, - "Incorrect number of input-output indices for $obj: ($nl, $nr) instead of (", - tensor_inds, ").") + errorstr = Expr( + :string, + "Incorrect number of input-output indices for $obj: ($nl, $nr) instead of (", + tensor_inds, ")." + ) checksize = quote (numout($newobj) == $nl && numin($newobj) == $nr) || throw(IndexError($errorstr)) @@ -54,21 +60,26 @@ function _extract_tensormap_objects(ex) end return _is_adj ? _add_adjoint(newobj) : newobj end - post = Expr(:block, - [Expr(:(=), a, tensordict[a]) for a in newtensors if !(a isa Symbol)]...) - pre = Expr(:macrocall, Symbol("@notensor"), - LineNumberNode(@__LINE__, Symbol(@__FILE__)), pre) - pre2 = Expr(:macrocall, Symbol("@notensor"), - LineNumberNode(@__LINE__, Symbol(@__FILE__)), pre2) - post = Expr(:macrocall, Symbol("@notensor"), - LineNumberNode(@__LINE__, Symbol(@__FILE__)), post) + post = Expr( + :block, + [Expr(:(=), a, tensordict[a]) for a in newtensors if !(a isa Symbol)]... + ) + pre = Expr( + :macrocall, Symbol("@notensor"), LineNumberNode(@__LINE__, Symbol(@__FILE__)), pre + ) + pre2 = Expr( + :macrocall, Symbol("@notensor"), LineNumberNode(@__LINE__, Symbol(@__FILE__)), pre2 + ) + post = Expr( + :macrocall, Symbol("@notensor"), LineNumberNode(@__LINE__, Symbol(@__FILE__)), post + ) return Expr(:block, pre, pre2, ex, post) end _is_adjoint(ex) = isexpr(ex, TO.prime) _remove_adjoint(ex) = _is_adjoint(ex) ? ex.args[1] : ex _add_adjoint(ex) = Expr(TO.prime, ex) -# used by `@planar`: identify braiding tensors (corresponding to name τ) and discover their +# used by `@planar`: identify braiding tensors (corresponding to name τ) and discover their # spaces from the rest of the expression. Construct the explicit BraidingTensor objects and # insert them in the expression. function _construct_braidingtensors(ex) @@ -81,31 +92,36 @@ function _construct_braidingtensors(ex) return ex end preargs = Vector{Any}() - indexmap = Dict{Any,Any}() + indexmap = Dict{Any, Any}() if TO.isassignment(ex) && TO.istensor(lhs) obj, leftind, rightind = TO.decomposetensor(lhs) for (i, l) in enumerate(leftind) indexmap[l] = Expr(:call, :dual, Expr(:call, :space, obj, i)) end for (i, l) in enumerate(rightind) - indexmap[l] = Expr(:call, :dual, - Expr(:call, :space, obj, length(leftind) + i)) + indexmap[l] = Expr( + :call, :dual, Expr(:call, :space, obj, length(leftind) + i) + ) end end newrhs, success = _construct_braidingtensors!(rhs, preargs, indexmap) success || throw(ArgumentError("cannot determine the spaces of all braiding tensors in $ex")) - pre = Expr(:macrocall, Symbol("@notensor"), - LineNumberNode(@__LINE__, Symbol(@__FILE__)), Expr(:block, preargs...)) + pre = Expr( + :macrocall, Symbol("@notensor"), + LineNumberNode(@__LINE__, Symbol(@__FILE__)), Expr(:block, preargs...) + ) return Expr(:block, pre, Expr(ex.head, lhs, newrhs)) elseif TO.istensorexpr(ex) preargs = Vector{Any}() - indexmap = Dict{Any,Any}() + indexmap = Dict{Any, Any}() newex, success = _construct_braidingtensors!(ex, preargs, indexmap) success || throw(ArgumentError("cannot determine the spaces of all braiding tensors in $ex")) - pre = Expr(:macrocall, Symbol("@notensor"), - LineNumberNode(@__LINE__, Symbol(@__FILE__)), Expr(:block, preargs...)) + pre = Expr( + :macrocall, Symbol("@notensor"), + LineNumberNode(@__LINE__, Symbol(@__FILE__)), Expr(:block, preargs...) + ) return Expr(:block, pre, newex) else return Expr(ex.head, map(_construct_braidingtensors, ex.args)...) @@ -154,8 +170,9 @@ function _construct_braidingtensors!(ex, preargs, indexmap) # ex is guaranteed t else success = false end - newex = Expr(:typed_vcat, obj, Expr(:tuple, leftind...), - Expr(:tuple, rightind...)) + newex = Expr( + :typed_vcat, obj, Expr(:tuple, leftind...), Expr(:tuple, rightind...) + ) else newex = ex success = true @@ -194,8 +211,9 @@ function _construct_braidingtensors!(ex, preargs, indexmap) # ex is guaranteed t while !all(successes) for i in 2:length(ex.args) successes[i] && continue - newargs[i], successa = _construct_braidingtensors!(args[i], preargs, - indexmap) + newargs[i], successa = _construct_braidingtensors!( + args[i], preargs, indexmap + ) successes[i] = successa end if numsuccess == count(successes) @@ -245,7 +263,7 @@ function _remove_braidingtensors(ex) if !TO.istensorexpr(rhs) return ex end - indexmap = Dict{Any,Any}() + indexmap = Dict{Any, Any}() if TO.istensor(lhs) obj, leftind, rightind = TO.decomposetensor(lhs) end @@ -254,7 +272,7 @@ function _remove_braidingtensors(ex) throw(ArgumentError("cannot determine the spaces of all braiding tensors in $ex")) return Expr(ex.head, lhs, newrhs) elseif TO.istensorexpr(ex) - indexmap = Dict{Any,Any}() + indexmap = Dict{Any, Any}() newex, unchanged = _remove_braidingtensors!(ex, indexmap) isempty(indexmap) || @@ -330,8 +348,9 @@ function _remove_braidingtensors!(ex, indexmap) # ex is guaranteed to be a singl delete!(indexmap, l′) end end - return Expr(:typed_vcat, obj, Expr(:tuple, leftind...), - Expr(:tuple, rightind...)), unchanged + return Expr( + :typed_vcat, obj, Expr(:tuple, leftind...), Expr(:tuple, rightind...) + ), unchanged end elseif TO.isgeneraltensor(ex) args = ex.args @@ -428,8 +447,9 @@ function _decompose_planar_contractions(ex::Expr, temporaries) return Expr(:block, pre..., rhs) end if isexpr(ex, :block) - return Expr(ex.head, - [_decompose_planar_contractions(a, temporaries) for a in ex.args]...) + return Expr( + ex.head, [_decompose_planar_contractions(a, temporaries) for a in ex.args]... + ) end return ex end @@ -479,24 +499,29 @@ function _extract_contraction_pairs(rhs, lhs, pre, temporaries) end ind1, ind2, oind1, oind2, cind1, cind2 = only(rhs_inds) # inds_rhs should hold exactly one match if all(in(leftind), oind2) || all(in(rightind), oind1) # reverse order - a1 = _extract_contraction_pairs(rhs.args[3], (oind2, reverse(cind2)), pre, - temporaries) - a2 = _extract_contraction_pairs(rhs.args[2], (cind1, reverse(oind1)), pre, - temporaries) + a1 = _extract_contraction_pairs( + rhs.args[3], (oind2, reverse(cind2)), pre, temporaries + ) + a2 = _extract_contraction_pairs( + rhs.args[2], (cind1, reverse(oind1)), pre, temporaries + ) oind1, oind2 = oind2, oind1 cind1, cind2 = cind2, cind1 else - a1 = _extract_contraction_pairs(rhs.args[2], (oind1, reverse(cind1)), pre, - temporaries) - a2 = _extract_contraction_pairs(rhs.args[3], (cind2, reverse(oind2)), pre, - temporaries) + a1 = _extract_contraction_pairs( + rhs.args[2], (oind1, reverse(cind1)), pre, temporaries + ) + a2 = _extract_contraction_pairs( + rhs.args[3], (cind2, reverse(oind2)), pre, temporaries + ) end if TO.isscalarexpr(a1) || TO.isscalarexpr(a2) rhs = Expr(:call, :*, a1, a2) s = gensym() - newlhs = Expr(:typed_vcat, s, Expr(:tuple, oind1...), - Expr(:tuple, reverse(oind2)...)) + newlhs = Expr( + :typed_vcat, s, Expr(:tuple, oind1...), Expr(:tuple, reverse(oind2)...) + ) push!(temporaries, s) push!(pre, Expr(:(:=), newlhs, rhs)) return newlhs @@ -513,8 +538,9 @@ function _extract_contraction_pairs(rhs, lhs, pre, temporaries) if lhs isa Tuple rhs = Expr(:call, :*, a1, a2) s = gensym() - newlhs = Expr(:typed_vcat, s, Expr(:tuple, oind1...), - Expr(:tuple, reverse(oind2)...)) + newlhs = Expr( + :typed_vcat, s, Expr(:tuple, oind1...), Expr(:tuple, reverse(oind2)...) + ) push!(temporaries, s) push!(pre, Expr(:(:=), newlhs, rhs)) return newlhs @@ -525,17 +551,16 @@ function _extract_contraction_pairs(rhs, lhs, pre, temporaries) else rhs = Expr(:call, :*, a1, a2) s = gensym() - newlhs = Expr(:typed_vcat, s, Expr(:tuple, oind1...), - Expr(:tuple, reverse(oind2)...)) + newlhs = Expr( + :typed_vcat, s, Expr(:tuple, oind1...), Expr(:tuple, reverse(oind2)...) + ) push!(temporaries, s) push!(pre, Expr(:(:=), newlhs, rhs)) return newlhs end end elseif isexpr(rhs, :call) && rhs.args[1] ∈ (:+, :-) - args = [_extract_contraction_pairs(a, lhs, pre, temporaries) - for - a in rhs.args[2:end]] + args = [_extract_contraction_pairs(a, lhs, pre, temporaries) for a in rhs.args[2:end]] return Expr(rhs.head, rhs.args[1], args...) elseif isexpr(rhs, :call) && rhs.args[1] == :/ newarg = _extract_contraction_pairs(rhs.args[2], lhs, pre, temporaries) diff --git a/src/spaces/cartesianspace.jl b/src/spaces/cartesianspace.jl index fe12f3dc6..778fa4982 100644 --- a/src/spaces/cartesianspace.jl +++ b/src/spaces/cartesianspace.jl @@ -8,10 +8,10 @@ vector space that is implicitly assumed in most of matrix algebra. struct CartesianSpace <: ElementarySpace d::Int end -CartesianSpace(d::Integer=0; dual=false) = CartesianSpace(Int(d)) -function CartesianSpace(dim::Pair; dual=false) +CartesianSpace(d::Integer = 0; dual = false) = CartesianSpace(Int(d)) +function CartesianSpace(dim::Pair; dual = false) if dim.first === Trivial() - return CartesianSpace(dim.second; dual=dual) + return CartesianSpace(dim.second; dual = dual) else msg = "$(dim) is not a valid dimension for CartesianSpace" throw(SectorMismatch(msg)) @@ -41,8 +41,8 @@ Base.:^(::RealNumbers, d::Int) = CartesianSpace(d) # Corresponding methods: #------------------------ -dim(V::CartesianSpace, ::Trivial=Trivial()) = V.d -Base.axes(V::CartesianSpace, ::Trivial=Trivial()) = Base.OneTo(dim(V)) +dim(V::CartesianSpace, ::Trivial = Trivial()) = V.d +Base.axes(V::CartesianSpace, ::Trivial = Trivial()) = Base.OneTo(dim(V)) hassector(V::CartesianSpace, ::Trivial) = dim(V) != 0 sectors(V::CartesianSpace) = OneOrNoneIterator(dim(V) != 0, Trivial()) sectortype(::Type{CartesianSpace}) = Trivial diff --git a/src/spaces/complexspace.jl b/src/spaces/complexspace.jl index 1031db614..00e4f2a3a 100644 --- a/src/spaces/complexspace.jl +++ b/src/spaces/complexspace.jl @@ -9,10 +9,10 @@ struct ComplexSpace <: ElementarySpace d::Int dual::Bool end -ComplexSpace(d::Integer=0; dual=false) = ComplexSpace(Int(d), dual) -function ComplexSpace(dim::Pair; dual=false) +ComplexSpace(d::Integer = 0; dual = false) = ComplexSpace(Int(d), dual) +function ComplexSpace(dim::Pair; dual = false) if dim.first === Trivial() - return ComplexSpace(dim.second; dual=dual) + return ComplexSpace(dim.second; dual = dual) else msg = "$(dim) is not a valid dimension for ComplexSpace" throw(SectorMismatch(msg)) @@ -39,9 +39,9 @@ Base.:^(::ComplexNumbers, d::Int) = ComplexSpace(d) # Corresponding methods: #------------------------ -dim(V::ComplexSpace, s::Trivial=Trivial()) = V.d +dim(V::ComplexSpace, s::Trivial = Trivial()) = V.d isdual(V::ComplexSpace) = V.dual -Base.axes(V::ComplexSpace, ::Trivial=Trivial()) = Base.OneTo(dim(V)) +Base.axes(V::ComplexSpace, ::Trivial = Trivial()) = Base.OneTo(dim(V)) hassector(V::ComplexSpace, ::Trivial) = dim(V) != 0 sectors(V::ComplexSpace) = OneOrNoneIterator(dim(V) != 0, Trivial()) sectortype(::Type{ComplexSpace}) = Trivial @@ -53,8 +53,8 @@ Base.zero(::Type{ComplexSpace}) = ComplexSpace(0) function ⊕(V₁::ComplexSpace, V₂::ComplexSpace) return isdual(V₁) == isdual(V₂) ? - ComplexSpace(dim(V₁) + dim(V₂), isdual(V₁)) : - throw(SpaceMismatch("Direct sum of a vector space and its dual does not exist")) + ComplexSpace(dim(V₁) + dim(V₂), isdual(V₁)) : + throw(SpaceMismatch("Direct sum of a vector space and its dual does not exist")) end function ⊖(V::ComplexSpace, W::ComplexSpace) (V ≿ W && isdual(V) == isdual(W)) || @@ -67,13 +67,13 @@ flip(V::ComplexSpace) = dual(V) function infimum(V₁::ComplexSpace, V₂::ComplexSpace) return isdual(V₁) == isdual(V₂) ? - ComplexSpace(min(dim(V₁), dim(V₂)), isdual(V₁)) : - throw(SpaceMismatch("Infimum of space and dual space does not exist")) + ComplexSpace(min(dim(V₁), dim(V₂)), isdual(V₁)) : + throw(SpaceMismatch("Infimum of space and dual space does not exist")) end function supremum(V₁::ComplexSpace, V₂::ComplexSpace) return isdual(V₁) == isdual(V₂) ? - ComplexSpace(max(dim(V₁), dim(V₂)), isdual(V₁)) : - throw(SpaceMismatch("Supremum of space and dual space does not exist")) + ComplexSpace(max(dim(V₁), dim(V₂)), isdual(V₁)) : + throw(SpaceMismatch("Supremum of space and dual space does not exist")) end Base.show(io::IO, V::ComplexSpace) = print(io, isdual(V) ? "(ℂ^$(V.d))'" : "ℂ^$(V.d)") diff --git a/src/spaces/deligne.jl b/src/spaces/deligne.jl index f9cc51c8e..926d59e40 100644 --- a/src/spaces/deligne.jl +++ b/src/spaces/deligne.jl @@ -19,57 +19,61 @@ end # define deligne products with empty tensor product: just add a trivial sector of the type # of the empty space to each of the sectors in the non-empty space -function ⊠(V::GradedSpace, P₀::ProductSpace{<:ElementarySpace,0}) +function ⊠(V::GradedSpace, P₀::ProductSpace{<:ElementarySpace, 0}) field(V) == field(P₀) || throw_incompatible_fields(V, P₀) I₁ = sectortype(V) I₂ = sectortype(P₀) - return Vect[I₁ ⊠ I₂](ifelse(isdual(V), dual(c), c) ⊠ one(I₂) => dim(V, c) - for c in sectors(V); dual=isdual(V)) + return Vect[I₁ ⊠ I₂]( + ifelse(isdual(V), dual(c), c) ⊠ one(I₂) => dim(V, c) + for c in sectors(V); dual = isdual(V) + ) end -function ⊠(P₀::ProductSpace{<:ElementarySpace,0}, V::GradedSpace) +function ⊠(P₀::ProductSpace{<:ElementarySpace, 0}, V::GradedSpace) field(P₀) == field(V) || throw_incompatible_fields(P₀, V) I₁ = sectortype(P₀) I₂ = sectortype(V) - return Vect[I₁ ⊠ I₂](one(I₁) ⊠ ifelse(isdual(V), dual(c), c) => dim(V, c) - for c in sectors(V); dual=isdual(V)) + return Vect[I₁ ⊠ I₂]( + one(I₁) ⊠ ifelse(isdual(V), dual(c), c) => dim(V, c) + for c in sectors(V); dual = isdual(V) + ) end -function ⊠(V::ComplexSpace, P₀::ProductSpace{<:ElementarySpace,0}) +function ⊠(V::ComplexSpace, P₀::ProductSpace{<:ElementarySpace, 0}) field(V) == field(P₀) || throw_incompatible_fields(V, P₀) I₂ = sectortype(P₀) - return Vect[I₂](one(I₂) => dim(V); dual=isdual(V)) + return Vect[I₂](one(I₂) => dim(V); dual = isdual(V)) end -function ⊠(P₀::ProductSpace{<:ElementarySpace,0}, V::ComplexSpace) +function ⊠(P₀::ProductSpace{<:ElementarySpace, 0}, V::ComplexSpace) field(P₀) == field(V) || throw_incompatible_fields(P₀, V) I₁ = sectortype(P₀) - return Vect[I₁](one(I₁) => dim(V); dual=isdual(V)) + return Vect[I₁](one(I₁) => dim(V); dual = isdual(V)) end -function ⊠(P::ProductSpace{<:ElementarySpace,0}, P₀::ProductSpace{<:ElementarySpace,0}) +function ⊠(P::ProductSpace{<:ElementarySpace, 0}, P₀::ProductSpace{<:ElementarySpace, 0}) field(P) == field(P₀) || throw_incompatible_fields(P, P₀) I₁ = sectortype(P) I₂ = sectortype(P₀) return one(Vect[I₁ ⊠ I₂]) end -function ⊠(P::ProductSpace{<:ElementarySpace}, P₀::ProductSpace{<:ElementarySpace,0}) +function ⊠(P::ProductSpace{<:ElementarySpace}, P₀::ProductSpace{<:ElementarySpace, 0}) field(P) == field(P₀) || throw_incompatible_fields(P, P₀) I₁ = sectortype(P) I₂ = sectortype(P₀) S = Vect[I₁ ⊠ I₂] N = length(P) - return ProductSpace{S,N}(map(V -> V ⊠ P₀, tuple(P...))) + return ProductSpace{S, N}(map(V -> V ⊠ P₀, tuple(P...))) end -function ⊠(P₀::ProductSpace{<:ElementarySpace,0}, P::ProductSpace{<:ElementarySpace}) +function ⊠(P₀::ProductSpace{<:ElementarySpace, 0}, P::ProductSpace{<:ElementarySpace}) field(P₀) == field(P) || throw_incompatible_fields(P₀, P) I₁ = sectortype(P₀) I₂ = sectortype(P) S = Vect[I₁ ⊠ I₂] N = length(P) - return ProductSpace{S,N}(map(V -> P₀ ⊠ V, tuple(P...))) + return ProductSpace{S, N}(map(V -> P₀ ⊠ V, tuple(P...))) end @noinline function throw_incompatible_fields(P₁, P₂) diff --git a/src/spaces/generalspace.jl b/src/spaces/generalspace.jl index c72b55e70..fa9db1baf 100644 --- a/src/spaces/generalspace.jl +++ b/src/spaces/generalspace.jl @@ -12,22 +12,22 @@ struct GeneralSpace{𝔽} <: ElementarySpace function GeneralSpace{𝔽}(d::Int, dual::Bool, conj::Bool) where {𝔽} d >= 0 || throw(ArgumentError("Dimension of a vector space should be bigger than zero")) - if 𝔽 isa Field + return if 𝔽 isa Field new{𝔽}(Int(d), dual, (𝔽 ⊆ ℝ) ? false : conj) else throw(ArgumentError("Unrecognised scalar field: $𝔽")) end end end -function GeneralSpace{𝔽}(d::Int=0; dual::Bool=false, conj::Bool=false) where {𝔽} +function GeneralSpace{𝔽}(d::Int = 0; dual::Bool = false, conj::Bool = false) where {𝔽} return GeneralSpace{𝔽}(d, dual, conj) end -dim(V::GeneralSpace, s::Trivial=Trivial()) = V.d +dim(V::GeneralSpace, s::Trivial = Trivial()) = V.d isdual(V::GeneralSpace) = V.dual isconj(V::GeneralSpace) = V.conj -Base.axes(V::GeneralSpace, ::Trivial=Trivial()) = Base.OneTo(dim(V)) +Base.axes(V::GeneralSpace, ::Trivial = Trivial()) = Base.OneTo(dim(V)) hassector(V::GeneralSpace, ::Trivial) = dim(V) != 0 sectors(V::GeneralSpace) = OneOrNoneIterator(dim(V) != 0, Trivial()) sectortype(::Type{<:GeneralSpace}) = Trivial @@ -42,14 +42,9 @@ dual(V::GeneralSpace{𝔽}) where {𝔽} = GeneralSpace{𝔽}(dim(V), !isdual(V) Base.conj(V::GeneralSpace{𝔽}) where {𝔽} = GeneralSpace{𝔽}(dim(V), isdual(V), !isconj(V)) function Base.show(io::IO, V::GeneralSpace{𝔽}) where {𝔽} - if isconj(V) - print(io, "conj(") - end + isconj(V) && print(io, "conj(") print(io, "GeneralSpace{", 𝔽, "}(", dim(V), ")") - if isdual(V) - print(io, "'") - end - if isconj(V) - print(io, ")") - end + isdual(V) && print(io, "'") + isconj(V) && print(io, ")") + return nothing end diff --git a/src/spaces/gradedspace.jl b/src/spaces/gradedspace.jl index 568b13475..d55a71d68 100644 --- a/src/spaces/gradedspace.jl +++ b/src/spaces/gradedspace.jl @@ -24,13 +24,13 @@ and should typically be of no concern. The concrete type `GradedSpace{I,D}` with correct `D` can be obtained as `Vect[I]`, or if `I == Irrep[G]` for some `G<:Group`, as `Rep[G]`. """ -struct GradedSpace{I<:Sector,D} <: ElementarySpace +struct GradedSpace{I <: Sector, D} <: ElementarySpace dims::D dual::Bool end -sectortype(::Type{<:GradedSpace{I}}) where {I<:Sector} = I +sectortype(::Type{<:GradedSpace{I}}) where {I <: Sector} = I -function GradedSpace{I,NTuple{N,Int}}(dims; dual::Bool=false) where {I,N} +function GradedSpace{I, NTuple{N, Int}}(dims; dual::Bool = false) where {I, N} d = ntuple(n -> 0, N) isset = ntuple(n -> false, N) for (c, dc) in dims @@ -41,48 +41,49 @@ function GradedSpace{I,NTuple{N,Int}}(dims; dual::Bool=false) where {I,N} isset = TupleTools.setindex(isset, true, i) d = TupleTools.setindex(d, dc, i) end - return GradedSpace{I,NTuple{N,Int}}(d, dual) + return GradedSpace{I, NTuple{N, Int}}(d, dual) end -function GradedSpace{I,NTuple{N,Int}}(dims::Pair; dual::Bool=false) where {I,N} - return GradedSpace{I,NTuple{N,Int}}((dims,); dual=dual) +function GradedSpace{I, NTuple{N, Int}}(dims::Pair; dual::Bool = false) where {I, N} + return GradedSpace{I, NTuple{N, Int}}((dims,); dual = dual) end -function GradedSpace{I,SectorDict{I,Int}}(dims; dual::Bool=false) where {I<:Sector} - d = SectorDict{I,Int}() +function GradedSpace{I, SectorDict{I, Int}}(dims; dual::Bool = false) where {I <: Sector} + d = SectorDict{I, Int}() for (c, dc) in dims k = convert(I, c) haskey(d, k) && throw(ArgumentError("Sector $k appears multiple times")) dc < 0 && throw(ArgumentError("Sector $k has negative dimension $dc")) !iszero(dc) && push!(d, k => dc) end - return GradedSpace{I,SectorDict{I,Int}}(d, dual) + return GradedSpace{I, SectorDict{I, Int}}(d, dual) end -function GradedSpace{I,SectorDict{I,Int}}(dims::Pair; dual::Bool=false) where {I<:Sector} - return GradedSpace{I,SectorDict{I,Int}}((dims,); dual=dual) +function GradedSpace{I, SectorDict{I, Int}}(dims::Pair; dual::Bool = false) where {I <: Sector} + return GradedSpace{I, SectorDict{I, Int}}((dims,); dual = dual) end -GradedSpace{I,D}(; kwargs...) where {I<:Sector,D} = GradedSpace{I,D}((); kwargs...) -function GradedSpace{I,D}(d1::Pair, d2::Pair, dims::Vararg{Pair}; - kwargs...) where {I<:Sector,D} - return GradedSpace{I,D}((d1, d2, dims...); kwargs...) +GradedSpace{I, D}(; kwargs...) where {I <: Sector, D} = GradedSpace{I, D}((); kwargs...) +function GradedSpace{I, D}(d1::Pair, d2::Pair, dims::Vararg{Pair}; kwargs...) where {I <: Sector, D} + return GradedSpace{I, D}((d1, d2, dims...); kwargs...) end -GradedSpace{I}(args...; kwargs...) where {I<:Sector} = Vect[I](args..., kwargs...) +GradedSpace{I}(args...; kwargs...) where {I <: Sector} = Vect[I](args..., kwargs...) -function GradedSpace(dims::Tuple{Pair{I,<:Integer},Vararg{Pair{I,<:Integer}}}; - dual::Bool=false) where {I<:Sector} - return Vect[I](dims; dual=dual) +function GradedSpace( + dims::Tuple{Pair{I, <:Integer}, Vararg{Pair{I, <:Integer}}}; dual::Bool = false + ) where {I <: Sector} + return Vect[I](dims; dual = dual) end -function GradedSpace(dim1::Pair{I,<:Integer}, rdims::Vararg{Pair{I,<:Integer}}; - dual::Bool=false) where {I<:Sector} - return Vect[I]((dim1, rdims...); dual=dual) +function GradedSpace( + dim1::Pair{I, <:Integer}, rdims::Vararg{Pair{I, <:Integer}}; dual::Bool = false + ) where {I <: Sector} + return Vect[I]((dim1, rdims...); dual = dual) end -function GradedSpace(dims::AbstractDict{I,<:Integer}; dual::Bool=false) where {I<:Sector} - return Vect[I](dims; dual=dual) +function GradedSpace(dims::AbstractDict{I, <:Integer}; dual::Bool = false) where {I <: Sector} + return Vect[I](dims; dual = dual) end # not inferrable -GradedSpace(g::Base.Generator; dual::Bool=false) = GradedSpace(g...; dual=dual) -GradedSpace(g::AbstractDict; dual::Bool=false) = GradedSpace(g...; dual=dual) +GradedSpace(g::Base.Generator; dual::Bool = false) = GradedSpace(g...; dual = dual) +GradedSpace(g::AbstractDict; dual::Bool = false) = GradedSpace(g...; dual = dual) Base.hash(V::GradedSpace, h::UInt) = hash(V.dual, hash(V.dims, h)) @@ -91,26 +92,28 @@ Base.hash(V::GradedSpace, h::UInt) = hash(V.dual, hash(V.dims, h)) field(::Type{<:GradedSpace}) = ℂ InnerProductStyle(::Type{<:GradedSpace}) = EuclideanInnerProduct() function dim(V::GradedSpace) - return reduce(+, dim(V, c) * dim(c) for c in sectors(V); - init=zero(dim(one(sectortype(V))))) + return reduce( + +, dim(V, c) * dim(c) for c in sectors(V); + init = zero(dim(one(sectortype(V)))) + ) end -function dim(V::GradedSpace{I,<:AbstractDict}, c::I) where {I<:Sector} +function dim(V::GradedSpace{I, <:AbstractDict}, c::I) where {I <: Sector} return get(V.dims, isdual(V) ? dual(c) : c, 0) end -function dim(V::GradedSpace{I,<:Tuple}, c::I) where {I<:Sector} +function dim(V::GradedSpace{I, <:Tuple}, c::I) where {I <: Sector} return V.dims[findindex(values(I), isdual(V) ? dual(c) : c)] end -function sectors(V::GradedSpace{I,<:AbstractDict}) where {I<:Sector} +function sectors(V::GradedSpace{I, <:AbstractDict}) where {I <: Sector} return SectorSet{I}(s -> isdual(V) ? dual(s) : s, keys(V.dims)) end -function sectors(V::GradedSpace{I,NTuple{N,Int}}) where {I<:Sector,N} - SectorSet{I}(Iterators.filter(n -> V.dims[n] != 0, 1:N)) do n +function sectors(V::GradedSpace{I, NTuple{N, Int}}) where {I <: Sector, N} + return SectorSet{I}(Iterators.filter(n -> V.dims[n] != 0, 1:N)) do n return isdual(V) ? dual(values(I)[n]) : values(I)[n] end end -hassector(V::GradedSpace{I}, s::I) where {I<:Sector} = dim(V, s) != 0 +hassector(V::GradedSpace{I}, s::I) where {I <: Sector} = dim(V, s) != 0 Base.conj(V::GradedSpace) = typeof(V)(V.dims, !V.dual) isdual(V::GradedSpace) = V.dual @@ -122,7 +125,7 @@ end # axes Base.axes(V::GradedSpace) = Base.OneTo(dim(V)) -function Base.axes(V::GradedSpace{I}, c::I) where {I<:Sector} +function Base.axes(V::GradedSpace{I}, c::I) where {I <: Sector} offset = 0 for c′ in sectors(V) c′ == c && break @@ -131,41 +134,41 @@ function Base.axes(V::GradedSpace{I}, c::I) where {I<:Sector} return (offset + 1):(offset + dim(c) * dim(V, c)) end -Base.oneunit(S::Type{<:GradedSpace{I}}) where {I<:Sector} = S(one(I) => 1) -Base.zero(S::Type{<:GradedSpace{I}}) where {I<:Sector} = S(one(I) => 0) +Base.oneunit(S::Type{<:GradedSpace{I}}) where {I <: Sector} = S(one(I) => 1) +Base.zero(S::Type{<:GradedSpace{I}}) where {I <: Sector} = S(one(I) => 0) # TODO: the following methods can probably be implemented more efficiently for # `FiniteGradedSpace`, but we don't expect them to be used often in hot loops, so # these generic definitions (which are still quite efficient) are good for now. -function ⊕(V₁::GradedSpace{I}, V₂::GradedSpace{I}) where {I<:Sector} +function ⊕(V₁::GradedSpace{I}, V₂::GradedSpace{I}) where {I <: Sector} dual1 = isdual(V₁) dual1 == isdual(V₂) || throw(SpaceMismatch("Direct sum of a vector space and a dual space does not exist")) - dims = SectorDict{I,Int}() + dims = SectorDict{I, Int}() for c in union(sectors(V₁), sectors(V₂)) cout = ifelse(dual1, dual(c), c) dims[cout] = dim(V₁, c) + dim(V₂, c) end - return typeof(V₁)(dims; dual=dual1) + return typeof(V₁)(dims; dual = dual1) end -function ⊖(V::GradedSpace{I}, W::GradedSpace{I}) where {I<:Sector} +function ⊖(V::GradedSpace{I}, W::GradedSpace{I}) where {I <: Sector} dual = isdual(V) V ≿ W && dual == isdual(W) || throw(SpaceMismatch("$(W) is not a subspace of $(V)")) return typeof(V)(c => dim(V, c) - dim(W, c) for c in sectors(V); dual) end -function flip(V::GradedSpace{I}) where {I<:Sector} - if isdual(V) +function flip(V::GradedSpace{I}) where {I <: Sector} + return if isdual(V) typeof(V)(c => dim(V, c) for c in sectors(V)) else typeof(V)(dual(c) => dim(V, c) for c in sectors(V))' end end -function fuse(V₁::GradedSpace{I}, V₂::GradedSpace{I}) where {I<:Sector} - dims = SectorDict{I,Int}() +function fuse(V₁::GradedSpace{I}, V₂::GradedSpace{I}) where {I <: Sector} + dims = SectorDict{I, Int}() for a in sectors(V₁), b in sectors(V₂) for c in a ⊗ b dims[c] = get(dims, c, 0) + Nsymbol(a, b, c) * dim(V₁, a) * dim(V₂, b) @@ -174,23 +177,27 @@ function fuse(V₁::GradedSpace{I}, V₂::GradedSpace{I}) where {I<:Sector} return typeof(V₁)(dims) end -function infimum(V₁::GradedSpace{I}, V₂::GradedSpace{I}) where {I<:Sector} +function infimum(V₁::GradedSpace{I}, V₂::GradedSpace{I}) where {I <: Sector} Visdual = isdual(V₁) Visdual == isdual(V₂) || throw(SpaceMismatch("Infimum of space and dual space does not exist")) - return typeof(V₁)((Visdual ? dual(c) : c) => min(dim(V₁, c), dim(V₂, c)) - for c in intersect(sectors(V₁), sectors(V₂)); dual=Visdual) + return typeof(V₁)( + (Visdual ? dual(c) : c) => min(dim(V₁, c), dim(V₂, c)) + for c in intersect(sectors(V₁), sectors(V₂)); dual = Visdual + ) end -function supremum(V₁::GradedSpace{I}, V₂::GradedSpace{I}) where {I<:Sector} +function supremum(V₁::GradedSpace{I}, V₂::GradedSpace{I}) where {I <: Sector} Visdual = isdual(V₁) Visdual == isdual(V₂) || throw(SpaceMismatch("Supremum of space and dual space does not exist")) - return typeof(V₁)((Visdual ? dual(c) : c) => max(dim(V₁, c), dim(V₂, c)) - for c in union(sectors(V₁), sectors(V₂)); dual=Visdual) + return typeof(V₁)( + (Visdual ? dual(c) : c) => max(dim(V₁, c), dim(V₂, c)) + for c in union(sectors(V₁), sectors(V₂)); dual = Visdual + ) end -function Base.show(io::IO, V::GradedSpace{I}) where {I<:Sector} +function Base.show(io::IO, V::GradedSpace{I}) where {I <: Sector} print(io, type_repr(typeof(V)), "(") separator = "" comma = ", " @@ -220,11 +227,11 @@ const Vect = SpaceTable() Base.getindex(::SpaceTable) = ComplexSpace Base.getindex(::SpaceTable, ::Type{Trivial}) = ComplexSpace function Base.getindex(::SpaceTable, I::Type{<:Sector}) - if Base.IteratorSize(values(I)) isa Union{HasLength,HasShape} + if Base.IteratorSize(values(I)) isa Union{HasLength, HasShape} N = length(values(I)) - return GradedSpace{I,NTuple{N,Int}} + return GradedSpace{I, NTuple{N, Int}} else - return GradedSpace{I,SectorDict{I,Int}} + return GradedSpace{I, SectorDict{I, Int}} end end @@ -242,12 +249,12 @@ See also [`Irrep`](@ref) and [`Vect`](@ref). const Rep = RepTable() Base.getindex(::RepTable, G::Type{<:Group}) = Vect[Irrep[G]] -type_repr(::Type{<:GradedSpace{I}}) where {I<:Sector} = "Vect[" * type_repr(I) * "]" -function type_repr(::Type{<:GradedSpace{<:AbstractIrrep{G}}}) where {G<:Group} +type_repr(::Type{<:GradedSpace{I}}) where {I <: Sector} = "Vect[" * type_repr(I) * "]" +function type_repr(::Type{<:GradedSpace{<:AbstractIrrep{G}}}) where {G <: Group} return "Rep[" * type_repr(G) * "]" end function type_repr(::Type{<:GradedSpace{ProductSector{T}}}) where - {T<:Tuple{Vararg{AbstractIrrep}}} + {T <: Tuple{Vararg{AbstractIrrep}}} sectors = T.parameters s = "Rep[" for i in 1:length(sectors) @@ -261,11 +268,11 @@ function type_repr(::Type{<:GradedSpace{ProductSector{T}}}) where end # Specific constructors for Z_N -const ZNSpace{N} = GradedSpace{ZNIrrep{N},NTuple{N,Int}} -ZNSpace{N}(dims::NTuple{N,Int}; dual::Bool=false) where {N} = ZNSpace{N}(dims, dual) -ZNSpace{N}(dims::Vararg{Int,N}; dual::Bool=false) where {N} = ZNSpace{N}(dims, dual) -ZNSpace(dims::NTuple{N,Int}; dual::Bool=false) where {N} = ZNSpace{N}(dims, dual) -ZNSpace(dims::Vararg{Int,N}; dual::Bool=false) where {N} = ZNSpace{N}(dims, dual) +const ZNSpace{N} = GradedSpace{ZNIrrep{N}, NTuple{N, Int}} +ZNSpace{N}(dims::NTuple{N, Int}; dual::Bool = false) where {N} = ZNSpace{N}(dims, dual) +ZNSpace{N}(dims::Vararg{Int, N}; dual::Bool = false) where {N} = ZNSpace{N}(dims, dual) +ZNSpace(dims::NTuple{N, Int}; dual::Bool = false) where {N} = ZNSpace{N}(dims, dual) +ZNSpace(dims::Vararg{Int, N}; dual::Bool = false) where {N} = ZNSpace{N}(dims, dual) # TODO: Do we still need all of those # ASCII type aliases diff --git a/src/spaces/homspace.jl b/src/spaces/homspace.jl index 92188b9ef..3958030b1 100644 --- a/src/spaces/homspace.jl +++ b/src/spaces/homspace.jl @@ -8,18 +8,18 @@ Represents the linear space of morphisms with codomain of type `P1` and domain o Note that HomSpace is not a subtype of VectorSpace, i.e. we restrict the latter to denote certain categories and their objects, and keep HomSpace distinct. """ -struct HomSpace{S<:ElementarySpace,P1<:CompositeSpace{S},P2<:CompositeSpace{S}} +struct HomSpace{S <: ElementarySpace, P1 <: CompositeSpace{S}, P2 <: CompositeSpace{S}} codomain::P1 domain::P2 end -function HomSpace(codomain::S, domain::CompositeSpace{S}) where {S<:ElementarySpace} +function HomSpace(codomain::S, domain::CompositeSpace{S}) where {S <: ElementarySpace} return HomSpace(⊗(codomain), domain) end -function HomSpace(codomain::CompositeSpace{S}, domain::S) where {S<:ElementarySpace} +function HomSpace(codomain::CompositeSpace{S}, domain::S) where {S <: ElementarySpace} return HomSpace(codomain, ⊗(domain)) end -function HomSpace(codomain::S, domain::S) where {S<:ElementarySpace} +function HomSpace(codomain::S, domain::S) where {S <: ElementarySpace} return HomSpace(⊗(codomain), ⊗(domain)) end HomSpace(codomain::VectorSpace) = HomSpace(codomain, zero(codomain)) @@ -45,18 +45,20 @@ numout(W::HomSpace) = length(codomain(W)) numin(W::HomSpace) = length(domain(W)) numind(W::HomSpace) = numin(W) + numout(W) -const TensorSpace{S<:ElementarySpace} = Union{S,ProductSpace{S}} -const TensorMapSpace{S<:ElementarySpace,N₁,N₂} = HomSpace{S,ProductSpace{S,N₁}, - ProductSpace{S,N₂}} +const TensorSpace{S <: ElementarySpace} = Union{S, ProductSpace{S}} +const TensorMapSpace{S <: ElementarySpace, N₁, N₂} = HomSpace{ + S, ProductSpace{S, N₁}, + ProductSpace{S, N₂}, +} -function Base.getindex(W::TensorMapSpace{<:IndexSpace,N₁,N₂}, i) where {N₁,N₂} +function Base.getindex(W::TensorMapSpace{<:IndexSpace, N₁, N₂}, i) where {N₁, N₂} return i <= N₁ ? codomain(W)[i] : dual(domain(W)[i - N₁]) end -function ←(codom::ProductSpace{S}, dom::ProductSpace{S}) where {S<:ElementarySpace} +function ←(codom::ProductSpace{S}, dom::ProductSpace{S}) where {S <: ElementarySpace} return HomSpace(codom, dom) end -function ←(codom::S, dom::S) where {S<:ElementarySpace} +function ←(codom::S, dom::S) where {S <: ElementarySpace} return HomSpace(ProductSpace(codom), ProductSpace(dom)) end ←(codom::VectorSpace, dom::VectorSpace) = ←(promote(codom, dom)...) @@ -69,7 +71,7 @@ function Base.show(io::IO, W::HomSpace) print(io, W.codomain) end print(io, " ← ") - if length(W.domain) == 1 + return if length(W.domain) == 1 print(io, W.domain[1]) else print(io, W.domain) @@ -140,7 +142,7 @@ fusiontrees(W::HomSpace) = fusionblockstructure(W).fusiontreelist Return the `HomSpace` obtained by permuting the indices of the domain and codomain of `W` according to the permutation `p₁` and `p₂` respectively. """ -function permute(W::HomSpace{S}, (p₁, p₂)::Index2Tuple{N₁,N₂}) where {S,N₁,N₂} +function permute(W::HomSpace{S}, (p₁, p₂)::Index2Tuple{N₁, N₂}) where {S, N₁, N₂} p = (p₁..., p₂...) TupleTools.isperm(p) && length(p) == numind(W) || throw(ArgumentError("$((p₁, p₂)) is not a valid permutation for $(W)")) @@ -153,9 +155,9 @@ end Return the `HomSpace` obtained by a selection from the domain and codomain of `W` according to the indices in `p₁` and `p₂` respectively. """ -function select(W::HomSpace{S}, (p₁, p₂)::Index2Tuple{N₁,N₂}) where {S,N₁,N₂} - cod = ProductSpace{S,N₁}(map(n -> W[n], p₁)) - dom = ProductSpace{S,N₂}(map(n -> dual(W[n]), p₂)) +function select(W::HomSpace{S}, (p₁, p₂)::Index2Tuple{N₁, N₂}) where {S, N₁, N₂} + cod = ProductSpace{S, N₁}(map(n -> W[n], p₁)) + dom = ProductSpace{S, N₂}(map(n -> dual(W[n]), p₂)) return cod ← dom end @@ -196,8 +198,9 @@ More specifically, adds a left monoidal unit or its dual. See also [`insertrightunit`](@ref insertrightunit(::HomSpace, ::Val{i}) where {i}), [`removeunit`](@ref removeunit(::HomSpace, ::Val{i}) where {i}). """ -function insertleftunit(W::HomSpace, ::Val{i}=Val(numind(W) + 1); - conj::Bool=false, dual::Bool=false) where {i} +function insertleftunit( + W::HomSpace, ::Val{i} = Val(numind(W) + 1); conj::Bool = false, dual::Bool = false + ) where {i} if i ≤ numout(W) return insertleftunit(codomain(W), Val(i); conj, dual) ← domain(W) else @@ -215,8 +218,9 @@ More specifically, adds a right monoidal unit or its dual. See also [`insertleftunit`](@ref insertleftunit(::HomSpace, ::Val{i}) where {i}), [`removeunit`](@ref removeunit(::HomSpace, ::Val{i}) where {i}). """ -function insertrightunit(W::HomSpace, ::Val{i}=Val(numind(W)); - conj::Bool=false, dual::Bool=false) where {i} +function insertrightunit( + W::HomSpace, ::Val{i} = Val(numind(W)); conj::Bool = false, dual::Bool = false + ) where {i} if i ≤ numout(W) return insertrightunit(codomain(W), Val(i); conj, dual) ← domain(W) else @@ -246,14 +250,14 @@ end #-------------------------------------------------------------------------- # sizes, strides, offset -const StridedStructure{N} = Tuple{NTuple{N,Int},NTuple{N,Int},Int} +const StridedStructure{N} = Tuple{NTuple{N, Int}, NTuple{N, Int}, Int} -struct FusionBlockStructure{I,N,F₁,F₂} +struct FusionBlockStructure{I, N, F₁, F₂} totaldim::Int - blockstructure::SectorDict{I,Tuple{Tuple{Int,Int},UnitRange{Int}}} - fusiontreelist::Vector{Tuple{F₁,F₂}} + blockstructure::SectorDict{I, Tuple{Tuple{Int, Int}, UnitRange{Int}}} + fusiontreelist::Vector{Tuple{F₁, F₂}} fusiontreestructure::Vector{StridedStructure{N}} - fusiontreeindices::FusionTreeDict{Tuple{F₁,F₂},Int} + fusiontreeindices::FusionTreeDict{Tuple{F₁, F₂}, Int} end function fusionblockstructuretype(W::HomSpace) @@ -263,7 +267,7 @@ function fusionblockstructuretype(W::HomSpace) I = sectortype(W) F₁ = fusiontreetype(I, N₁) F₂ = fusiontreetype(I, N₂) - return FusionBlockStructure{I,N,F₁,F₂} + return FusionBlockStructure{I, N, F₁, F₂} end @cached function fusionblockstructure(W::HomSpace)::fusionblockstructuretype(W) @@ -276,13 +280,13 @@ end F₂ = fusiontreetype(I, N₂) # output structure - blockstructure = SectorDict{I,Tuple{Tuple{Int,Int},UnitRange{Int}}}() # size, range - fusiontreelist = Vector{Tuple{F₁,F₂}}() - fusiontreestructure = Vector{Tuple{NTuple{N₁ + N₂,Int},NTuple{N₁ + N₂,Int},Int}}() # size, strides, offset + blockstructure = SectorDict{I, Tuple{Tuple{Int, Int}, UnitRange{Int}}}() # size, range + fusiontreelist = Vector{Tuple{F₁, F₂}}() + fusiontreestructure = Vector{Tuple{NTuple{N₁ + N₂, Int}, NTuple{N₁ + N₂, Int}, Int}}() # size, strides, offset # temporary data structures splittingtrees = Vector{F₁}() - splittingstructure = Vector{Tuple{Int,Int}}() + splittingstructure = Vector{Tuple{Int, Int}}() # main computational routine blockoffset = 0 @@ -322,15 +326,16 @@ end blockstructure[c] = (blocksize, blockrange) end - fusiontreeindices = sizehint!(FusionTreeDict{Tuple{F₁,F₂},Int}(), - length(fusiontreelist)) + fusiontreeindices = sizehint!( + FusionTreeDict{Tuple{F₁, F₂}, Int}(), length(fusiontreelist) + ) for (i, f₁₂) in enumerate(fusiontreelist) fusiontreeindices[f₁₂] = i end totaldim = blockoffset - structure = FusionBlockStructure(totaldim, blockstructure, - fusiontreelist, fusiontreestructure, - fusiontreeindices) + structure = FusionBlockStructure( + totaldim, blockstructure, fusiontreelist, fusiontreestructure, fusiontreeindices + ) return structure end @@ -352,7 +357,7 @@ end function diagonalblockstructure(W::HomSpace) ((numin(W) == numout(W) == 1) && domain(W) == codomain(W)) || throw(SpaceMismatch("Diagonal only support on V←V with a single space V")) - structure = SectorDict{sectortype(W),UnitRange{Int}}() # range + structure = SectorDict{sectortype(W), UnitRange{Int}}() # range offset = 0 dom = domain(W)[1] for c in blocksectors(W) diff --git a/src/spaces/productspace.jl b/src/spaces/productspace.jl index cda85381e..0911a5f53 100644 --- a/src/spaces/productspace.jl +++ b/src/spaces/productspace.jl @@ -5,22 +5,22 @@ A `ProductSpace` is a tensor product space of `N` vector spaces of type `S<:ElementarySpace`. Only tensor products between [`ElementarySpace`](@ref) objects of the same type are allowed. """ -struct ProductSpace{S<:ElementarySpace,N} <: CompositeSpace{S} - spaces::NTuple{N,S} - ProductSpace{S,N}(spaces::NTuple{N,S}) where {S<:ElementarySpace,N} = new{S,N}(spaces) +struct ProductSpace{S <: ElementarySpace, N} <: CompositeSpace{S} + spaces::NTuple{N, S} + ProductSpace{S, N}(spaces::NTuple{N, S}) where {S <: ElementarySpace, N} = new{S, N}(spaces) end -function ProductSpace{S,N}(spaces::Vararg{S,N}) where {S<:ElementarySpace,N} - return ProductSpace{S,N}(spaces) +function ProductSpace{S, N}(spaces::Vararg{S, N}) where {S <: ElementarySpace, N} + return ProductSpace{S, N}(spaces) end -function ProductSpace{S}(spaces::Tuple{Vararg{S}}) where {S<:ElementarySpace} - return ProductSpace{S,length(spaces)}(spaces) +function ProductSpace{S}(spaces::Tuple{Vararg{S}}) where {S <: ElementarySpace} + return ProductSpace{S, length(spaces)}(spaces) end -ProductSpace{S}(spaces::Vararg{S}) where {S<:ElementarySpace} = ProductSpace{S}(spaces) +ProductSpace{S}(spaces::Vararg{S}) where {S <: ElementarySpace} = ProductSpace{S}(spaces) -function ProductSpace(spaces::Tuple{S,Vararg{S}}) where {S<:ElementarySpace} - return ProductSpace{S,length(spaces)}(spaces) +function ProductSpace(spaces::Tuple{S, Vararg{S}}) where {S <: ElementarySpace} + return ProductSpace{S, length(spaces)}(spaces) end function ProductSpace(space1::ElementarySpace, rspaces::Vararg{ElementarySpace}) return ProductSpace((space1, rspaces...)) @@ -29,20 +29,20 @@ end ProductSpace(P::ProductSpace) = P # constructors with conversion behaviour -function ProductSpace{S,N}(V::Vararg{ElementarySpace,N}) where {S<:ElementarySpace,N} - return ProductSpace{S,N}(V) +function ProductSpace{S, N}(V::Vararg{ElementarySpace, N}) where {S <: ElementarySpace, N} + return ProductSpace{S, N}(V) end -function ProductSpace{S}(V::Vararg{ElementarySpace}) where {S<:ElementarySpace} +function ProductSpace{S}(V::Vararg{ElementarySpace}) where {S <: ElementarySpace} return ProductSpace{S}(V) end -function ProductSpace{S,N}(V::Tuple{Vararg{ElementarySpace,N}}) where {S<:ElementarySpace,N} +function ProductSpace{S, N}(V::Tuple{Vararg{ElementarySpace, N}}) where {S <: ElementarySpace, N} return ProductSpace{S}(convert.(S, V)) end -function ProductSpace{S}(V::Tuple{Vararg{ElementarySpace}}) where {S<:ElementarySpace} +function ProductSpace{S}(V::Tuple{Vararg{ElementarySpace}}) where {S <: ElementarySpace} return ProductSpace{S}(convert.(S, V)) end -function ProductSpace(V::Tuple{ElementarySpace,Vararg{ElementarySpace}}) +function ProductSpace(V::Tuple{ElementarySpace, Vararg{ElementarySpace}}) return ProductSpace(promote(V...)) end @@ -60,12 +60,12 @@ dim(P::ProductSpace) = prod(dims(P)) Base.axes(P::ProductSpace) = map(axes, P.spaces) Base.axes(P::ProductSpace, n::Int) = axes(P.spaces[n]) -dual(P::ProductSpace{<:ElementarySpace,0}) = P +dual(P::ProductSpace{<:ElementarySpace, 0}) = P dual(P::ProductSpace) = ProductSpace(map(dual, reverse(P.spaces))) # Base.conj(P::ProductSpace) = ProductSpace(map(conj, P.spaces)) -function Base.show(io::IO, P::ProductSpace{S}) where {S<:ElementarySpace} +function Base.show(io::IO, P::ProductSpace{S}) where {S <: ElementarySpace} spaces = P.spaces if length(spaces) == 0 print(io, "ProductSpace{", S, ", 0}") @@ -89,10 +89,10 @@ Return an iterator over all possible combinations of sectors (represented as an `NTuple{N, sectortype(S)}`) that can appear within the tensor product space `P`. """ sectors(P::ProductSpace) = _sectors(P, sectortype(P)) -function _sectors(P::ProductSpace{<:ElementarySpace,N}, ::Type{Trivial}) where {N} +function _sectors(P::ProductSpace{<:ElementarySpace, N}, ::Type{Trivial}) where {N} return OneOrNoneIterator(dim(P) != 0, ntuple(n -> Trivial(), N)) end -function _sectors(P::ProductSpace{<:ElementarySpace,N}, ::Type{<:Sector}) where {N} +function _sectors(P::ProductSpace{<:ElementarySpace, N}, ::Type{<:Sector}) where {N} return product(map(sectors, P.spaces)...) end @@ -103,8 +103,8 @@ end Query whether `P` has a non-zero degeneracy of sector `s`, representing a combination of sectors on the individual tensor indices. """ -function hassector(V::ProductSpace{<:ElementarySpace,N}, s::NTuple{N}) where {N} - return reduce(&, map(hassector, V.spaces, s); init=true) +function hassector(V::ProductSpace{<:ElementarySpace, N}, s::NTuple{N}) where {N} + return reduce(&, map(hassector, V.spaces, s); init = true) end """ @@ -114,7 +114,7 @@ end Return the degeneracy dimensions corresponding to a tuple of sectors `s` for each of the spaces in the tensor product `P`. """ -function dims(P::ProductSpace{<:ElementarySpace,N}, sector::NTuple{N,<:Sector}) where {N} +function dims(P::ProductSpace{<:ElementarySpace, N}, sector::NTuple{N, <:Sector}) where {N} return map(dim, P.spaces, sector) end @@ -125,12 +125,11 @@ end Return the total degeneracy dimension corresponding to a tuple of sectors for each of the spaces in the tensor product, obtained as `prod(dims(P, s))``. """ -function dim(P::ProductSpace{<:ElementarySpace,N}, sector::NTuple{N,<:Sector}) where {N} - return reduce(*, dims(P, sector); init=1) +function dim(P::ProductSpace{<:ElementarySpace, N}, sector::NTuple{N, <:Sector}) where {N} + return reduce(*, dims(P, sector); init = 1) end -function Base.axes(P::ProductSpace{<:ElementarySpace,N}, - sectors::NTuple{N,<:Sector}) where {N} +function Base.axes(P::ProductSpace{<:ElementarySpace, N}, sectors::NTuple{N, <:Sector}) where {N} return map(axes, P.spaces, sectors) end @@ -141,7 +140,7 @@ Return an iterator over the different unique coupled sector labels, i.e. the dif fusion outputs that can be obtained by fusing the sectors present in the different spaces that make up the `ProductSpace` instance. """ -function blocksectors(P::ProductSpace{S,N}) where {S,N} +function blocksectors(P::ProductSpace{S, N}) where {S, N} I = sectortype(S) if I == Trivial return OneOrNoneIterator(dim(P) != 0, Trivial()) @@ -172,7 +171,7 @@ Return an iterator over all fusion trees that can be formed by fusing the sector in the different spaces that make up the `ProductSpace` instance into the coupled sector `blocksector`. """ -function fusiontrees(P::ProductSpace{S,N}, blocksector::I) where {S,N,I} +function fusiontrees(P::ProductSpace{S, N}, blocksector::I) where {S, N, I} I == sectortype(S) || throw(SectorMismatch()) uncoupled = map(sectors, P.spaces) isdualflags = map(isdual, P.spaces) @@ -207,8 +206,7 @@ function blockdim(P::ProductSpace, c::Sector) return d end -function Base.:(==)(P1::ProductSpace{S,N}, - P2::ProductSpace{S,N}) where {S<:ElementarySpace,N} +function Base.:(==)(P1::ProductSpace{S, N}, P2::ProductSpace{S, N}) where {S <: ElementarySpace, N} return (P1.spaces == P2.spaces) end Base.:(==)(P1::ProductSpace, P2::ProductSpace) = false @@ -220,7 +218,7 @@ Base.hash(P::ProductSpace{S}, h::UInt) where {S} = hash(P.spaces, hash(S, h)) #--------------------------------------------- ⊗(V::ElementarySpace, Vrest::ElementarySpace...) = ProductSpace(V, Vrest...) ⊗(P::ProductSpace) = P -function ⊗(P1::ProductSpace{S}, P2::ProductSpace{S}) where {S<:ElementarySpace} +function ⊗(P1::ProductSpace{S}, P2::ProductSpace{S}) where {S <: ElementarySpace} return ProductSpace{S}(tuple(P1.spaces..., P2.spaces...)) end @@ -233,17 +231,17 @@ Return a tensor product of zero spaces of type `S`, i.e. this is the unit object tensor product operation, such that `V ⊗ one(V) == V`. """ Base.one(V::VectorSpace) = one(typeof(V)) -Base.one(::Type{<:ProductSpace{S}}) where {S<:ElementarySpace} = ProductSpace{S,0}(()) -Base.one(::Type{S}) where {S<:ElementarySpace} = ProductSpace{S,0}(()) +Base.one(::Type{<:ProductSpace{S}}) where {S <: ElementarySpace} = ProductSpace{S, 0}(()) +Base.one(::Type{S}) where {S <: ElementarySpace} = ProductSpace{S, 0}(()) -Base.:^(V::ElementarySpace, N::Int) = ProductSpace{typeof(V),N}(ntuple(n -> V, N)) +Base.:^(V::ElementarySpace, N::Int) = ProductSpace{typeof(V), N}(ntuple(n -> V, N)) Base.:^(V::ProductSpace, N::Int) = ⊗(ntuple(n -> V, N)...) function Base.literal_pow(::typeof(^), V::ElementarySpace, p::Val{N}) where {N} - return ProductSpace{typeof(V),N}(ntuple(n -> V, p)) + return ProductSpace{typeof(V), N}(ntuple(n -> V, p)) end -fuse(P::ProductSpace{S,0}) where {S<:ElementarySpace} = oneunit(S) -fuse(P::ProductSpace{S}) where {S<:ElementarySpace} = fuse(P.spaces...) +fuse(P::ProductSpace{S, 0}) where {S <: ElementarySpace} = oneunit(S) +fuse(P::ProductSpace{S}) where {S <: ElementarySpace} = fuse(P.spaces...) """ insertleftunit(P::ProductSpace, i::Int=length(P) + 1; conj=false, dual=false) @@ -254,8 +252,9 @@ More specifically, adds a left monoidal unit or its dual. See also [`insertrightunit`](@ref insertrightunit(::ProductSpace, ::Val{i}) where {i}), [`removeunit`](@ref removeunit(::ProductSpace, ::Val{i}) where {i}). """ -function insertleftunit(P::ProductSpace, ::Val{i}=Val(length(P) + 1); - conj::Bool=false, dual::Bool=false) where {i} +function insertleftunit( + P::ProductSpace, ::Val{i} = Val(length(P) + 1); conj::Bool = false, dual::Bool = false + ) where {i} u = oneunit(spacetype(P)) if dual u = TensorKit.dual(u) @@ -275,8 +274,9 @@ More specifically, adds a right monoidal unit or its dual. See also [`insertleftunit`](@ref insertleftunit(::ProductSpace, ::Val{i}) where {i}), [`removeunit`](@ref removeunit(::ProductSpace, ::Val{i}) where {i}). """ -function insertrightunit(P::ProductSpace, ::Val{i}=Val(length(P)); - conj::Bool=false, dual::Bool=false) where {i} +function insertrightunit( + P::ProductSpace, ::Val{i} = Val(length(P)); conj::Bool = false, dual::Bool = false + ) where {i} u = oneunit(spacetype(P)) if dual u = TensorKit.dual(u) @@ -311,7 +311,7 @@ Base.getindex(P::ProductSpace, n::Integer) = P.spaces[n] Base.iterate(P::ProductSpace, args...) = Base.iterate(P.spaces, args...) Base.indexed_iterate(P::ProductSpace, args...) = Base.indexed_iterate(P.spaces, args...) -Base.eltype(::Type{<:ProductSpace{S}}) where {S<:ElementarySpace} = S +Base.eltype(::Type{<:ProductSpace{S}}) where {S <: ElementarySpace} = S Base.eltype(P::ProductSpace) = eltype(typeof(P)) Base.IteratorEltype(::Type{<:ProductSpace}) = Base.HasEltype() @@ -321,13 +321,13 @@ Base.reverse(P::ProductSpace) = ProductSpace(reverse(P.spaces)) # Promotion and conversion # ------------------------ -function Base.promote_rule(::Type{S}, ::Type{<:ProductSpace{S}}) where {S<:ElementarySpace} +function Base.promote_rule(::Type{S}, ::Type{<:ProductSpace{S}}) where {S <: ElementarySpace} return ProductSpace{S} end # ProductSpace to ElementarySpace -Base.convert(::Type{S}, P::ProductSpace{S,0}) where {S<:ElementarySpace} = oneunit(S) -Base.convert(::Type{S}, P::ProductSpace{S}) where {S<:ElementarySpace} = fuse(P.spaces...) +Base.convert(::Type{S}, P::ProductSpace{S, 0}) where {S <: ElementarySpace} = oneunit(S) +Base.convert(::Type{S}, P::ProductSpace{S}) where {S <: ElementarySpace} = fuse(P.spaces...) # ElementarySpace to ProductSpace -Base.convert(::Type{<:ProductSpace}, V::S) where {S<:ElementarySpace} = ⊗(V) +Base.convert(::Type{<:ProductSpace}, V::S) where {S <: ElementarySpace} = ⊗(V) diff --git a/src/spaces/vectorspaces.jl b/src/spaces/vectorspaces.jl index 847182536..b15216aab 100644 --- a/src/spaces/vectorspaces.jl +++ b/src/spaces/vectorspaces.jl @@ -117,7 +117,7 @@ Return the degeneracy dimension corresponding to the sector `s` of the vector sp Return the sum of all degeneracy dimensions of the vector space `V`. """ -reduceddim(V::ElementarySpace) = sum(Base.Fix1(dim, V), sectors(V); init=0) +reduceddim(V::ElementarySpace) = sum(Base.Fix1(dim, V), sectors(V); init = 0) """ oneunit(V::S) where {S<:ElementarySpace} -> S @@ -157,7 +157,7 @@ const oplus = ⊕ Return a space that is equivalent to the orthogonal complement of `W` in `V`, i.e. an instance `X::ElementarySpace` such that `V = W ⊕ X`. """ -⊖(V₁::S, V₂::S) where {S<:ElementarySpace} +⊖(V₁::S, V₂::S) where {S <: ElementarySpace} ⊖(V₁::VectorSpace, V₂::VectorSpace) = ⊖(promote(V₁, V₂)...) const ominus = ⊖ @@ -260,7 +260,7 @@ defined. Also works in type domain. """ sectortype(x) = sectortype(typeof(x)) sectortype(::Type{T}) where {T} = sectortype(spacetype(T)) -sectortype(::Type{S}) where {S<:Sector} = S +sectortype(::Type{S}) where {S <: Sector} = S """ hassector(V::VectorSpace, a::Sector) -> Bool @@ -285,7 +285,7 @@ function sectors end Abstract type for composite spaces that are defined in terms of a number of elementary vector spaces of a homogeneous type `S<:ElementarySpace`. """ -abstract type CompositeSpace{S<:ElementarySpace} <: VectorSpace end +abstract type CompositeSpace{S <: ElementarySpace} <: VectorSpace end InnerProductStyle(::Type{<:CompositeSpace{S}}) where {S} = InnerProductStyle(S) @@ -300,8 +300,8 @@ spacetype(x) = spacetype(typeof(x)) function spacetype(::Type{T}) where {T} throw(MethodError(spacetype, (T,))) end -spacetype(::Type{E}) where {E<:ElementarySpace} = E -spacetype(::Type{S}) where {E,S<:CompositeSpace{E}} = E +spacetype(::Type{E}) where {E <: ElementarySpace} = E +spacetype(::Type{S}) where {E, S <: CompositeSpace{E}} = E # make ElementarySpace instances behave similar to ProductSpace instances blocksectors(V::ElementarySpace) = collect(sectors(V)) @@ -403,7 +403,7 @@ such that `V ≾ V₁`, `V ≾ V₂`, ... and no other `W ≻ V` has this proper that all arguments have the same value of `isdual( )`, and also the return value `V` will have the same value. """ -infimum(V₁::S, V₂::S, V₃::S...) where {S<:ElementarySpace} = infimum(infimum(V₁, V₂), V₃...) +infimum(V₁::S, V₂::S, V₃::S...) where {S <: ElementarySpace} = infimum(infimum(V₁, V₂), V₃...) """ supremum(V₁::ElementarySpace, V₂::ElementarySpace, V₃::ElementarySpace...) @@ -413,6 +413,6 @@ such that `V ≿ V₁`, `V ≿ V₂`, ... and no other `W ≺ V` has this proper that all arguments have the same value of `isdual( )`, and also the return value `V` will have the same value. """ -function supremum(V₁::S, V₂::S, V₃::S...) where {S<:ElementarySpace} +function supremum(V₁::S, V₂::S, V₃::S...) where {S <: ElementarySpace} return supremum(supremum(V₁, V₂), V₃...) end diff --git a/src/tensors/abstracttensor.jl b/src/tensors/abstracttensor.jl index d59ddb5cd..9d6bae666 100644 --- a/src/tensors/abstracttensor.jl +++ b/src/tensors/abstracttensor.jl @@ -9,7 +9,7 @@ Abstract supertype of all tensor maps, i.e. linear maps between tensor products spaces of type `S<:IndexSpace`, with element type `T`. An `AbstractTensorMap` maps from an input space of type `ProductSpace{S, N₂}` to an output space of type `ProductSpace{S, N₁}`. """ -abstract type AbstractTensorMap{T<:Number,S<:IndexSpace,N₁,N₂} end +abstract type AbstractTensorMap{T <: Number, S <: IndexSpace, N₁, N₂} end """ AbstractTensor{T,S,N} = AbstractTensorMap{T,S,N,0} @@ -20,7 +20,7 @@ Abstract supertype of all tensors, i.e. elements in the tensor product space of An `AbstractTensor{T, S, N}` is actually a special case `AbstractTensorMap{T, S, N, 0}`, i.e. a tensor map with only non-trivial output spaces. """ -const AbstractTensor{T,S,N} = AbstractTensorMap{T,S,N,0} +const AbstractTensor{T, S, N} = AbstractTensorMap{T, S, N, 0} # tensor characteristics: type information #------------------------------------------ @@ -32,9 +32,9 @@ Return the scalar or element type `T` of a tensor. """ Base.eltype(::Type{<:AbstractTensorMap{T}}) where {T} = T -spacetype(::Type{<:AbstractTensorMap{<:Any,S}}) where {S} = S +spacetype(::Type{<:AbstractTensorMap{<:Any, S}}) where {S} = S -function InnerProductStyle(::Type{TT}) where {TT<:AbstractTensorMap} +function InnerProductStyle(::Type{TT}) where {TT <: AbstractTensorMap} return InnerProductStyle(spacetype(TT)) end @@ -48,7 +48,7 @@ Return the type of vector that stores the data of a tensor. similarstoragetype(TT::Type{<:AbstractTensorMap}) = similarstoragetype(TT, scalartype(TT)) function similarstoragetype(TT::Type{<:AbstractTensorMap}, ::Type{T}) where {T} - return Core.Compiler.return_type(similar, Tuple{storagetype(TT),Type{T}}) + return Core.Compiler.return_type(similar, Tuple{storagetype(TT), Type{T}}) end # tensor characteristics: space and index information @@ -96,7 +96,7 @@ Return the number of output spaces of a tensor. This is equivalent to the number See also [`numin`](@ref) and [`numind`](@ref). """ -numout(::Type{<:AbstractTensorMap{T,S,N₁}}) where {T,S,N₁} = N₁ +numout(::Type{<:AbstractTensorMap{T, S, N₁}}) where {T, S, N₁} = N₁ """ numin(::Union{TT,Type{TT}}) where {TT<:AbstractTensorMap} -> Int @@ -105,7 +105,7 @@ Return the number of input spaces of a tensor. This is equivalent to the number See also [`numout`](@ref) and [`numind`](@ref). """ -numin(::Type{<:AbstractTensorMap{T,S,N₁,N₂}}) where {T,S,N₁,N₂} = N₂ +numin(::Type{<:AbstractTensorMap{T, S, N₁, N₂}}) where {T, S, N₁, N₂} = N₂ """ numind(::Union{T,Type{T}}) where {T<:AbstractTensorMap} -> Int @@ -115,7 +115,7 @@ total number of spaces in the domain and codomain of that tensor. See also [`numout`](@ref) and [`numin`](@ref). """ -numind(::Type{TT}) where {TT<:AbstractTensorMap} = numin(TT) + numout(TT) +numind(::Type{TT}) where {TT <: AbstractTensorMap} = numin(TT) + numout(TT) const order = numind """ @@ -125,7 +125,7 @@ Return all indices of the codomain of a tensor. See also [`domainind`](@ref) and [`allind`](@ref). """ -function codomainind(::Type{TT}) where {TT<:AbstractTensorMap} +function codomainind(::Type{TT}) where {TT <: AbstractTensorMap} return ntuple(identity, numout(TT)) end codomainind(t::AbstractTensorMap) = codomainind(typeof(t)) @@ -137,7 +137,7 @@ Return all indices of the domain of a tensor. See also [`codomainind`](@ref) and [`allind`](@ref). """ -function domainind(::Type{TT}) where {TT<:AbstractTensorMap} +function domainind(::Type{TT}) where {TT <: AbstractTensorMap} return ntuple(n -> numout(TT) + n, numin(TT)) end domainind(t::AbstractTensorMap) = domainind(typeof(t)) @@ -149,7 +149,7 @@ Return all indices of a tensor, i.e. the indices of its domain and codomain. See also [`codomainind`](@ref) and [`domainind`](@ref). """ -function allind(::Type{TT}) where {TT<:AbstractTensorMap} +function allind(::Type{TT}) where {TT <: AbstractTensorMap} return ntuple(identity, numind(TT)) end allind(t::AbstractTensorMap) = allind(typeof(t)) @@ -171,7 +171,7 @@ end InnerProductStyle(t::AbstractTensorMap) = InnerProductStyle(typeof(t)) storagetype(t::AbstractTensorMap) = storagetype(typeof(t)) blocktype(t::AbstractTensorMap) = blocktype(typeof(t)) -similarstoragetype(t::AbstractTensorMap, T=scalartype(t)) = similarstoragetype(typeof(t), T) +similarstoragetype(t::AbstractTensorMap, T = scalartype(t)) = similarstoragetype(typeof(t), T) numout(t::AbstractTensorMap) = numout(typeof(t)) numin(t::AbstractTensorMap) = numin(typeof(t)) @@ -236,7 +236,7 @@ hasblock(t::AbstractTensorMap, c::Sector) = c ∈ blocksectors(t) # """ # blockrange(t::AbstractTensorMap, c::Sector) -> UnitRange{Int} -# Return the range at which to find the matrix block of a tensor corresponding to a +# Return the range at which to find the matrix block of a tensor corresponding to a # coupled sector `c`, within the total data vector of length `dim(t)`. # """ # function blockrange(t::AbstractTensorMap, c::Sector) @@ -291,8 +291,8 @@ See also [`blocks`](@ref), [`blocksectors`](@ref), [`blockdim`](@ref) and [`hasb Return the type of the matrix blocks of a tensor. """ blocktype -function blocktype(::Type{T}) where {T<:AbstractTensorMap} - return Core.Compiler.return_type(block, Tuple{T,sectortype(T)}) +function blocktype(::Type{T}) where {T <: AbstractTensorMap} + return Core.Compiler.return_type(block, Tuple{T, sectortype(T)}) end # Derived indexing behavior for tensors with trivial symmetry @@ -362,13 +362,15 @@ By default, this will result in `TensorMap{T}(undef, V)` when custom objects do specialize this method. """ Base.similar(::AbstractTensorMap, args...) -function Base.similar(t::AbstractTensorMap, ::Type{T}, codomain::TensorSpace{S}, - domain::TensorSpace{S}) where {T,S} +function Base.similar( + t::AbstractTensorMap, ::Type{T}, codomain::TensorSpace{S}, domain::TensorSpace{S} + ) where {T, S} return similar(t, T, codomain ← domain) end # 3 arguments -function Base.similar(t::AbstractTensorMap, codomain::TensorSpace{S}, - domain::TensorSpace{S}) where {S} +function Base.similar( + t::AbstractTensorMap, codomain::TensorSpace{S}, domain::TensorSpace{S} + ) where {S} return similar(t, similarstoragetype(t), codomain ← domain) end function Base.similar(t::AbstractTensorMap, ::Type{T}, codomain::TensorSpace) where {T} @@ -384,8 +386,7 @@ Base.similar(t::AbstractTensorMap, ::Type{T}) where {T} = similar(t, T, space(t) Base.similar(t::AbstractTensorMap) = similar(t, similarstoragetype(t), space(t)) # generic implementation for AbstractTensorMap -> returns `TensorMap` -function Base.similar(t::AbstractTensorMap, ::Type{TorA}, - P::TensorMapSpace{S}) where {TorA,S} +function Base.similar(t::AbstractTensorMap, ::Type{TorA}, P::TensorMapSpace{S}) where {TorA, S} if TorA <: Number T = TorA A = similarstoragetype(t, T) @@ -398,15 +399,16 @@ function Base.similar(t::AbstractTensorMap, ::Type{TorA}, N₁ = length(codomain(P)) N₂ = length(domain(P)) - return TensorMap{T,S,N₁,N₂,A}(undef, P) + return TensorMap{T, S, N₁, N₂, A}(undef, P) end # implementation in type-domain -function Base.similar(::Type{TT}, P::TensorMapSpace) where {TT<:AbstractTensorMap} +function Base.similar(::Type{TT}, P::TensorMapSpace) where {TT <: AbstractTensorMap} return TensorMap{scalartype(TT)}(undef, P) end -function Base.similar(::Type{TT}, cod::TensorSpace{S}, - dom::TensorSpace{S}) where {TT<:AbstractTensorMap,S} +function Base.similar( + ::Type{TT}, cod::TensorSpace{S}, dom::TensorSpace{S} + ) where {TT <: AbstractTensorMap, S} return TensorMap{scalartype(TT)}(undef, cod, dom) end @@ -428,9 +430,10 @@ function Base.hash(t::AbstractTensorMap, h::UInt) return h end -function Base.isapprox(t1::AbstractTensorMap, t2::AbstractTensorMap; - atol::Real=0, - rtol::Real=Base.rtoldefault(scalartype(t1), scalartype(t2), atol)) +function Base.isapprox( + t1::AbstractTensorMap, t2::AbstractTensorMap; + atol::Real = 0, rtol::Real = Base.rtoldefault(scalartype(t1), scalartype(t2), atol) + ) d = norm(t1 - t2) if isfinite(d) return d <= max(atol, rtol * max(norm(t1), norm(t2))) diff --git a/src/tensors/adjoint.jl b/src/tensors/adjoint.jl index b47c7f2d3..a016e0b48 100644 --- a/src/tensors/adjoint.jl +++ b/src/tensors/adjoint.jl @@ -6,8 +6,8 @@ Specific subtype of [`AbstractTensorMap`](@ref) that is a lazy wrapper for representing the adjoint of an instance of [`AbstractTensorMap`](@ref). """ -struct AdjointTensorMap{T,S,N₁,N₂,TT<:AbstractTensorMap{T,S,N₂,N₁}} <: - AbstractTensorMap{T,S,N₁,N₂} +struct AdjointTensorMap{T, S, N₁, N₂, TT <: AbstractTensorMap{T, S, N₂, N₁}} <: + AbstractTensorMap{T, S, N₁, N₂} parent::TT end Base.parent(t::AdjointTensorMap) = t.parent @@ -19,7 +19,7 @@ Base.adjoint(t::AbstractTensorMap) = AdjointTensorMap(t) # Properties space(t::AdjointTensorMap) = adjoint(space(parent(t))) dim(t::AdjointTensorMap) = dim(parent(t)) -storagetype(::Type{AdjointTensorMap{T,S,N₁,N₂,TT}}) where {T,S,N₁,N₂,TT} = storagetype(TT) +storagetype(::Type{AdjointTensorMap{T, S, N₁, N₂, TT}}) where {T, S, N₁, N₂, TT} = storagetype(TT) # Blocks and subblocks #---------------------- @@ -27,7 +27,7 @@ block(t::AdjointTensorMap, s::Sector) = block(parent(t), s)' blocks(t::AdjointTensorMap) = BlockIterator(t, blocks(parent(t))) -function blocktype(::Type{AdjointTensorMap{T,S,N₁,N₂,TT}}) where {T,S,N₁,N₂,TT} +function blocktype(::Type{AdjointTensorMap{T, S, N₁, N₂, TT}}) where {T, S, N₁, N₂, TT} return Base.promote_op(adjoint, blocktype(TT)) end @@ -42,15 +42,16 @@ function Base.getindex(iter::BlockIterator{<:AdjointTensorMap}, c::Sector) return adjoint(Base.getindex(iter.structure, c)) end -function Base.getindex(t::AdjointTensorMap{T,S,N₁,N₂}, - f₁::FusionTree{I,N₁}, f₂::FusionTree{I,N₂}) where {T,S,N₁,N₂,I} +function Base.getindex( + t::AdjointTensorMap{T, S, N₁, N₂}, f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂} + ) where {T, S, N₁, N₂, I} tp = parent(t) subblock = getindex(tp, f₂, f₁) return permutedims(conj(subblock), (domainind(tp)..., codomainind(tp)...)) end -function Base.setindex!(t::AdjointTensorMap{T,S,N₁,N₂}, v, - f₁::FusionTree{I,N₁}, - f₂::FusionTree{I,N₂}) where {T,S,N₁,N₂,I} +function Base.setindex!( + t::AdjointTensorMap{T, S, N₁, N₂}, v, f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂} + ) where {T, S, N₁, N₂, I} return copy!(getindex(t, f₁, f₂), v) end @@ -81,4 +82,5 @@ function Base.show(io::IO, t::AdjointTensorMap) println(io) end end + return nothing end diff --git a/src/tensors/backends.jl b/src/tensors/backends.jl index 0fc8f99f6..9a4b99020 100644 --- a/src/tensors/backends.jl +++ b/src/tensors/backends.jl @@ -1,6 +1,6 @@ # Scheduler implementation # ------------------------ -function select_scheduler(scheduler=OhMyThreads.Implementation.NotGiven(); kwargs...) +function select_scheduler(scheduler = OhMyThreads.Implementation.NotGiven(); kwargs...) return if scheduler == OhMyThreads.Implementation.NotGiven() && isempty(kwargs) Threads.nthreads() == 1 ? SerialScheduler() : DynamicScheduler() else @@ -22,11 +22,12 @@ const blockscheduler = ScopedValue{Scheduler}(SerialScheduler()) Run `f` in a scope where the `blockscheduler` is determined by `scheduler' and `kwargs...`. """ -@inline function with_blockscheduler(f, scheduler=OhMyThreads.Implementation.NotGiven(); - kwargs...) - @with blockscheduler => select_scheduler(scheduler; kwargs...) f() +@inline function with_blockscheduler( + f, scheduler = OhMyThreads.Implementation.NotGiven(); kwargs... + ) + return @with blockscheduler => select_scheduler(scheduler; kwargs...) f() end # TODO: disable for trivial symmetry or small tensors? default_blockscheduler(t::AbstractTensorMap) = default_blockscheduler(typeof(t)) -default_blockscheduler(::Type{T}) where {T<:AbstractTensorMap} = blockscheduler[] +default_blockscheduler(::Type{T}) where {T <: AbstractTensorMap} = blockscheduler[] diff --git a/src/tensors/blockiterator.jl b/src/tensors/blockiterator.jl index edbaa27c8..409e9e630 100644 --- a/src/tensors/blockiterator.jl +++ b/src/tensors/blockiterator.jl @@ -3,14 +3,14 @@ Iterator over the blocks of type `T`, possibly holding some pre-computed data of type `S` """ -struct BlockIterator{T<:AbstractTensorMap,S} +struct BlockIterator{T <: AbstractTensorMap, S} t::T structure::S end Base.IteratorSize(::BlockIterator) = Base.HasLength() Base.IteratorEltype(::BlockIterator) = Base.HasEltype() -Base.eltype(::Type{<:BlockIterator{T}}) where {T} = Pair{sectortype(T),blocktype(T)} +Base.eltype(::Type{<:BlockIterator{T}}) where {T} = Pair{sectortype(T), blocktype(T)} Base.length(iter::BlockIterator) = length(iter.structure) Base.isdone(iter::BlockIterator, state...) = Base.isdone(iter.structure, state...) @@ -30,7 +30,7 @@ for c in union(blocksectors.(ts)...) end ``` """ -function foreachblock(f, t::AbstractTensorMap, ts::AbstractTensorMap...; scheduler=nothing) +function foreachblock(f, t::AbstractTensorMap, ts::AbstractTensorMap...; scheduler = nothing) tensors = (t, ts...) allsectors = union(blocksectors.(tensors)...) foreach(allsectors) do c @@ -38,7 +38,7 @@ function foreachblock(f, t::AbstractTensorMap, ts::AbstractTensorMap...; schedul end return nothing end -function foreachblock(f, t::AbstractTensorMap; scheduler=nothing) +function foreachblock(f, t::AbstractTensorMap; scheduler = nothing) foreach(blocks(t)) do (c, b) return f(c, (b,)) end diff --git a/src/tensors/braidingtensor.jl b/src/tensors/braidingtensor.jl index a4a184e5f..44b1673ca 100644 --- a/src/tensors/braidingtensor.jl +++ b/src/tensors/braidingtensor.jl @@ -11,11 +11,11 @@ braids the first input over the second input; its inverse can be obtained as the It holds that `domain(BraidingTensor(V1, V2)) == V1 ⊗ V2` and `codomain(BraidingTensor(V1, V2)) == V2 ⊗ V1`. """ -struct BraidingTensor{T,S} <: AbstractTensorMap{T,S,2,2} +struct BraidingTensor{T, S} <: AbstractTensorMap{T, S, 2, 2} V1::S V2::S adjoint::Bool - function BraidingTensor{T,S}(V1::S, V2::S, adjoint::Bool=false) where {T,S<:IndexSpace} + function BraidingTensor{T, S}(V1::S, V2::S, adjoint::Bool = false) where {T, S <: IndexSpace} for a in sectors(V1) for b in sectors(V2) for c in (a ⊗ b) @@ -24,42 +24,42 @@ struct BraidingTensor{T,S} <: AbstractTensorMap{T,S,2,2} end end end - return new{T,S}(V1, V2, adjoint) + return new{T, S}(V1, V2, adjoint) # partial construction: only construct rowr and colr when needed end end -function BraidingTensor{T}(V1::S, V2::S, adjoint::Bool=false) where {T,S<:IndexSpace} - return BraidingTensor{T,S}(V1, V2, adjoint) +function BraidingTensor{T}(V1::S, V2::S, adjoint::Bool = false) where {T, S <: IndexSpace} + return BraidingTensor{T, S}(V1, V2, adjoint) end -function BraidingTensor{T}(V1::IndexSpace, V2::IndexSpace, adjoint::Bool=false) where {T} +function BraidingTensor{T}(V1::IndexSpace, V2::IndexSpace, adjoint::Bool = false) where {T} return BraidingTensor{T}(promote(V1, V2)..., adjoint) end -function BraidingTensor(V1::IndexSpace, V2::IndexSpace, adjoint::Bool=false) +function BraidingTensor(V1::IndexSpace, V2::IndexSpace, adjoint::Bool = false) return BraidingTensor(promote(V1, V2)..., adjoint) end -function BraidingTensor(V1::S, V2::S, adjoint::Bool=false) where {S<:IndexSpace} +function BraidingTensor(V1::S, V2::S, adjoint::Bool = false) where {S <: IndexSpace} T = BraidingStyle(sectortype(S)) isa SymmetricBraiding ? Float64 : ComplexF64 - return BraidingTensor{T,S}(V1, V2, adjoint) + return BraidingTensor{T, S}(V1, V2, adjoint) end -function BraidingTensor(V::HomSpace, adjoint::Bool=false) +function BraidingTensor(V::HomSpace, adjoint::Bool = false) domain(V) == reverse(codomain(V)) || throw(SpaceMismatch("Cannot define a braiding on $V")) return BraidingTensor(V[2], V[1], adjoint) end -function BraidingTensor{T}(V::HomSpace, adjoint::Bool=false) where {T} +function BraidingTensor{T}(V::HomSpace, adjoint::Bool = false) where {T} domain(V) == reverse(codomain(V)) || throw(SpaceMismatch("Cannot define a braiding on $V")) return BraidingTensor{T}(V[2], V[1], adjoint) end -function Base.adjoint(b::BraidingTensor{T,S}) where {T,S} - return BraidingTensor{T,S}(b.V1, b.V2, !b.adjoint) +function Base.adjoint(b::BraidingTensor{T, S}) where {T, S} + return BraidingTensor{T, S}(b.V1, b.V2, !b.adjoint) end space(b::BraidingTensor) = b.adjoint ? b.V1 ⊗ b.V2 ← b.V2 ⊗ b.V1 : b.V2 ⊗ b.V1 ← b.V1 ⊗ b.V2 # TODO: this will probably give issues with GPUs, so we should try to avoid # calling this method alltogether -storagetype(::Type{BraidingTensor{T,S}}) where {T,S} = Vector{T} +storagetype(::Type{BraidingTensor{T, S}}) where {T, S} = Vector{T} function Base.getindex(b::BraidingTensor) sectortype(b) === Trivial || throw(SectorMismatch()) @@ -68,8 +68,9 @@ function Base.getindex(b::BraidingTensor) return sreshape(StridedView(block(b, Trivial())), d) end -@inline function Base.getindex(b::BraidingTensor, f₁::FusionTree{I,2}, - f₂::FusionTree{I,2}) where {I<:Sector} +@inline function Base.getindex( + b::BraidingTensor, f₁::FusionTree{I, 2}, f₂::FusionTree{I, 2} + ) where {I <: Sector} I == sectortype(b) || throw(SectorMismatch()) c = f₁.coupled V1, V2 = domain(b) @@ -87,7 +88,7 @@ end data = sreshape(StridedView(Matrix{eltype(b)}(undef, n1, n2)), d) fill!(data, zero(eltype(b))) if f₁.uncoupled == reverse(f₂.uncoupled) - braiddict = artin_braid(f₂, 1; inv=b.adjoint) + braiddict = artin_braid(f₂, 1; inv = b.adjoint) r = get(braiddict, f₁, zero(valtype(braiddict))) @inbounds for i in axes(data, 1), j in axes(data, 2) data[i, j, j, i] = r @@ -143,7 +144,7 @@ function block(b::BraidingTensor, s::Sector) continue end - braiddict = artin_braid(f2, 1; inv=b.adjoint) + braiddict = artin_braid(f2, 1; inv = b.adjoint) haskey(braiddict, f1) || continue r = braiddict[f1] @@ -160,15 +161,16 @@ end # Index manipulations # ------------------- has_shared_permute(t::BraidingTensor, ::Index2Tuple) = false -function add_transform!(tdst::AbstractTensorMap, - tsrc::BraidingTensor, - (p₁, p₂)::Index2Tuple, - fusiontreetransform, - α::Number, - β::Number, - backend::AbstractBackend...) - return add_transform!(tdst, TensorMap(tsrc), (p₁, p₂), fusiontreetransform, α, β, - backend...) +function add_transform!( + tdst::AbstractTensorMap, + tsrc::BraidingTensor, (p₁, p₂)::Index2Tuple, + fusiontreetransform, + α::Number, β::Number, backend::AbstractBackend... + ) + return add_transform!( + tdst, TensorMap(tsrc), (p₁, p₂), fusiontreetransform, α, β, + backend... + ) end # VectorInterface @@ -179,10 +181,11 @@ end # ---------------- # TODO: implement specialized methods -function TO.tensoradd!(C::AbstractTensorMap, - A::BraidingTensor, pA::Index2Tuple, conjA::Symbol, - α::Number, β::Number, backend=TO.DefaultBackend(), - allocator=TO.DefaultAllocator()) +function TO.tensoradd!( + C::AbstractTensorMap, + A::BraidingTensor, pA::Index2Tuple, conjA::Symbol, + α::Number, β::Number, backend = TO.DefaultBackend(), allocator = TO.DefaultAllocator() + ) return TO.tensoradd!(C, TensorMap(A), pA, conjA, α, β, backend, allocator) end @@ -190,33 +193,39 @@ end # ----------------- # TODO: implement specialized methods -function planaradd!(C::AbstractTensorMap, - A::BraidingTensor, p::Index2Tuple, - α::Number, β::Number, - backend, allocator) +function planaradd!( + C::AbstractTensorMap, + A::BraidingTensor, p::Index2Tuple, + α::Number, β::Number, backend, allocator + ) return planaradd!(C, TensorMap(A), p, α, β, backend, allocator) end -function planarcontract!(C::AbstractTensorMap, - A::BraidingTensor, - (oindA, cindA)::Index2Tuple, - B::AbstractTensorMap, - (cindB, oindB)::Index2Tuple, - (p1, p2)::Index2Tuple, - α::Number, β::Number, - backend, allocator) +function planarcontract!( + C::AbstractTensorMap, + A::BraidingTensor, + (oindA, cindA)::Index2Tuple, + B::AbstractTensorMap, + (cindB, oindB)::Index2Tuple, + (p1, p2)::Index2Tuple, + α::Number, β::Number, + backend, allocator + ) # special case only defined for contracting 2 indices length(oindA) == length(cindA) == 2 || - return planarcontract!(C, TensorMap(A), (oindA, cindA), B, (cindB, oindB), (p1, p2), - α, β, backend, allocator) + return planarcontract!( + C, TensorMap(A), (oindA, cindA), B, (cindB, oindB), (p1, p2), + α, β, backend, allocator + ) codA, domA = codomainind(A), domainind(A) codB, domB = codomainind(B), domainind(B) - oindA, cindA, oindB, cindB = reorder_indices(codA, domA, codB, domB, oindA, cindA, - oindB, cindB, p1, p2) + oindA, cindA, oindB, cindB = reorder_indices( + codA, domA, codB, domB, oindA, cindA, oindB, cindB, p1, p2 + ) if space(B, cindB[1]) != space(A, cindA[1])' || - space(B, cindB[2]) != space(A, cindA[2])' + space(B, cindB[2]) != space(A, cindA[2])' throw(SpaceMismatch("$(space(C)) ≠ permute($(space(A))[$oindA, $cindA] * $(space(B))[$cindB, $oindB], ($p1, $p2)")) end @@ -231,7 +240,7 @@ function planarcontract!(C::AbstractTensorMap, for (f₁, f₂) in fusiontrees(B) local newtrees for ((f₁′, f₂′), coeff′) in transpose(f₁, f₂, cindB, oindB) - for (f₁′′, coeff′′) in artin_braid(f₁′, 1; inv=inv_braid) + for (f₁′′, coeff′′) in artin_braid(f₁′, 1; inv = inv_braid) f12 = (f₁′′, f₂′) coeff = coeff′ * coeff′′ if @isdefined newtrees @@ -242,32 +251,36 @@ function planarcontract!(C::AbstractTensorMap, end end for ((f₁′, f₂′), coeff) in newtrees - TO.tensoradd!(C[f₁′, f₂′], B[f₁, f₂], (reverse(cindB), oindB), false, α * coeff, - One(), backend, allocator) + TO.tensoradd!( + C[f₁′, f₂′], B[f₁, f₂], (reverse(cindB), oindB), false, + α * coeff, One(), backend, allocator + ) end end return C end -function planarcontract!(C::AbstractTensorMap, - A::AbstractTensorMap, - (oindA, cindA)::Index2Tuple, - B::BraidingTensor, - (cindB, oindB)::Index2Tuple, - (p1, p2)::Index2Tuple, - α::Number, β::Number, - backend, allocator) +function planarcontract!( + C::AbstractTensorMap, + A::AbstractTensorMap, (oindA, cindA)::Index2Tuple, + B::BraidingTensor, (cindB, oindB)::Index2Tuple, + (p1, p2)::Index2Tuple, + α::Number, β::Number, + backend, allocator + ) # special case only defined for contracting 2 indices length(oindB) == length(cindB) == 2 || - return planarcontract!(C, A, (oindA, cindA), TensorMap(B), (cindB, oindB), (p1, p2), - α, β, backend, allocator) + return planarcontract!( + C, A, (oindA, cindA), TensorMap(B), (cindB, oindB), (p1, p2), + α, β, backend, allocator + ) codA, domA = codomainind(A), domainind(A) codB, domB = codomainind(B), domainind(B) - oindA, cindA, oindB, cindB = reorder_indices(codA, domA, codB, domB, oindA, cindA, - oindB, cindB, p1, p2) + oindA, cindA, oindB, cindB = reorder_indices( + codA, domA, codB, domB, oindA, cindA, oindB, cindB, p1, p2 + ) - if space(B, cindB[1]) != space(A, cindA[1])' || - space(B, cindB[2]) != space(A, cindA[2])' + if space(B, cindB[1]) != space(A, cindA[1])' || space(B, cindB[2]) != space(A, cindA[2])' throw(SpaceMismatch("$(space(C)) ≠ permute($(space(A))[$oindA, $cindA] * $(space(B))[$cindB, $oindB], ($p1, $p2)")) end @@ -282,7 +295,7 @@ function planarcontract!(C::AbstractTensorMap, for (f₁, f₂) in fusiontrees(A) local newtrees for ((f₁′, f₂′), coeff′) in transpose(f₁, f₂, oindA, cindA) - for (f₂′′, coeff′′) in artin_braid(f₂′, 1; inv=inv_braid) + for (f₂′′, coeff′′) in artin_braid(f₂′, 1; inv = inv_braid) f12 = (f₁′, f₂′′) coeff = coeff′ * conj(coeff′′) if @isdefined newtrees @@ -293,28 +306,35 @@ function planarcontract!(C::AbstractTensorMap, end end for ((f₁′, f₂′), coeff) in newtrees - TO.tensoradd!(C[f₁′, f₂′], A[f₁, f₂], (oindA, reverse(cindA)), false, α * coeff, - One(), backend, allocator) + TO.tensoradd!( + C[f₁′, f₂′], A[f₁, f₂], (oindA, reverse(cindA)), false, α * coeff, + One(), backend, allocator + ) end end return C end # ambiguity fix: -function planarcontract!(C::AbstractTensorMap, A::BraidingTensor, pA::Index2Tuple, - B::BraidingTensor, pB::Index2Tuple, pAB::Index2Tuple, - α::Number, β::Number, backend, - allocator) - return planarcontract!(C, TensorMap(A), pA, TensorMap(B), pB, pAB, α, β, backend, - allocator) +function planarcontract!( + C::AbstractTensorMap, + A::BraidingTensor, pA::Index2Tuple, + B::BraidingTensor, pB::Index2Tuple, + pAB::Index2Tuple, + α::Number, β::Number, backend, allocator + ) + return planarcontract!( + C, TensorMap(A), pA, TensorMap(B), pB, pAB, α, β, backend, allocator + ) end -function planartrace!(C::AbstractTensorMap, - A::BraidingTensor, - p::Index2Tuple, q::Index2Tuple, - α::Number, β::Number, - backend, - allocator) +function planartrace!( + C::AbstractTensorMap, + A::BraidingTensor, + p::Index2Tuple, q::Index2Tuple, + α::Number, β::Number, + backend, allocator + ) return planartrace!(C, TensorMap(A), p, q, α, β, backend, allocator) end diff --git a/src/tensors/diagonal.jl b/src/tensors/diagonal.jl index 7ab300ca0..df04cb9b6 100644 --- a/src/tensors/diagonal.jl +++ b/src/tensors/diagonal.jl @@ -1,23 +1,25 @@ # DiagonalTensorMap #==========================================================# -struct DiagonalTensorMap{T,S<:IndexSpace,A<:DenseVector{T}} <: AbstractTensorMap{T,S,1,1} +struct DiagonalTensorMap{T, S <: IndexSpace, A <: DenseVector{T}} <: AbstractTensorMap{T, S, 1, 1} data::A domain::S # equals codomain # uninitialized constructors - function DiagonalTensorMap{T,S,A}(::UndefInitializer, - dom::S) where {T,S<:IndexSpace,A<:DenseVector{T}} + function DiagonalTensorMap{T, S, A}( + ::UndefInitializer, dom::S + ) where {T, S <: IndexSpace, A <: DenseVector{T}} data = A(undef, reduceddim(dom)) if !isbitstype(T) zerovector!(data) end - return DiagonalTensorMap{T,S,A}(data, dom) + return DiagonalTensorMap{T, S, A}(data, dom) end # constructors from data - function DiagonalTensorMap{T,S,A}(data::A, - dom::S) where {T,S<:IndexSpace,A<:DenseVector{T}} + function DiagonalTensorMap{T, S, A}( + data::A, dom::S + ) where {T, S <: IndexSpace, A <: DenseVector{T}} T ⊆ field(S) || @warn("scalartype(data) = $T ⊈ $(field(S)))", maxlog = 1) - return new{T,S,A}(data, dom) + return new{T, S, A}(data, dom) end end @@ -25,7 +27,7 @@ end #-------------------------------------------- space(d::DiagonalTensorMap) = d.domain ← d.domain -storagetype(::Type{<:DiagonalTensorMap{T,S,A}}) where {T,S,A<:DenseVector{T}} = A +storagetype(::Type{<:DiagonalTensorMap{T, S, A}}) where {T, S, A <: DenseVector{T}} = A # DiagonalTensorMap constructors #-------------------------------- @@ -47,27 +49,27 @@ function DiagonalTensorMap{T}(::UndefInitializer, V::ProductSpace) where {T} throw(ArgumentError("DiagonalTensorMap requires `numin(d) == numout(d) == 1`")) return DiagonalTensorMap{T}(undef, only(V)) end -function DiagonalTensorMap{T}(::UndefInitializer, V::S) where {T,S<:IndexSpace} - return DiagonalTensorMap{T,S,Vector{T}}(undef, V) +function DiagonalTensorMap{T}(::UndefInitializer, V::S) where {T, S <: IndexSpace} + return DiagonalTensorMap{T, S, Vector{T}}(undef, V) end DiagonalTensorMap(::UndefInitializer, V::IndexSpace) = DiagonalTensorMap{Float64}(undef, V) -function DiagonalTensorMap{T}(data::A, V::S) where {T,S<:IndexSpace,A<:DenseVector{T}} +function DiagonalTensorMap{T}(data::A, V::S) where {T, S <: IndexSpace, A <: DenseVector{T}} length(data) == reduceddim(V) || throw(DimensionMismatch("length(data) = $(length(data)) is not compatible with the space $V")) - return DiagonalTensorMap{T,S,A}(data, V) + return DiagonalTensorMap{T, S, A}(data, V) end function DiagonalTensorMap(data::DenseVector{T}, V::IndexSpace) where {T} return DiagonalTensorMap{T}(data, V) end -function DiagonalTensorMap(t::AbstractTensorMap{T,S,1,1}) where {T,S} +function DiagonalTensorMap(t::AbstractTensorMap{T, S, 1, 1}) where {T, S} isa(t, DiagonalTensorMap) && return t domain(t) == codomain(t) || throw(SpaceMismatch("DiagonalTensorMap requires equal domain and codomain")) A = storagetype(t) - d = DiagonalTensorMap{T,S,A}(undef, space(t, 1)) + d = DiagonalTensorMap{T, S, A}(undef, space(t, 1)) for (c, b) in blocks(d) bt = block(t, c) # TODO: rewrite in terms of `diagview` from MatrixAlgebraKit.jl @@ -77,7 +79,7 @@ function DiagonalTensorMap(t::AbstractTensorMap{T,S,1,1}) where {T,S} end Base.similar(d::DiagonalTensorMap) = DiagonalTensorMap(similar(d.data), d.domain) -function Base.similar(d::DiagonalTensorMap, ::Type{T}) where {T<:Number} +function Base.similar(d::DiagonalTensorMap, ::Type{T}) where {T <: Number} return DiagonalTensorMap(similar(d.data, T), d.domain) end @@ -111,7 +113,7 @@ function Base.convert(::Type{DiagonalTensorMap}, t::AbstractTensorMap) throw(ArgumentError("DiagonalTensorMap requires input tensor that is diagonal")) return DiagonalTensorMap(t) end -function Base.convert(::Type{DiagonalTensorMap}, d::Dict{Symbol,Any}) +function Base.convert(::Type{DiagonalTensorMap}, d::Dict{Symbol, Any}) return convert(DiagonalTensorMap, convert(TensorMap, d)) end @@ -145,8 +147,8 @@ function block(d::DiagonalTensorMap, s::Sector) end blocks(t::DiagonalTensorMap) = BlockIterator(t, diagonalblockstructure(space(t))) -function blocktype(::Type{DiagonalTensorMap{T,S,A}}) where {T,S,A} - return Diagonal{T,SubArray{T,1,A,Tuple{UnitRange{Int}},true}} +function blocktype(::Type{DiagonalTensorMap{T, S, A}}) where {T, S, A} + return Diagonal{T, SubArray{T, 1, A, Tuple{UnitRange{Int}}, true}} end function Base.iterate(iter::BlockIterator{<:DiagonalTensorMap}, state...) @@ -164,19 +166,18 @@ end # Indexing and getting and setting the data at the subblock level #----------------------------------------------------------------- -@inline function Base.getindex(d::DiagonalTensorMap, - f₁::FusionTree{I,1}, - f₂::FusionTree{I,1}) where {I<:Sector} +@inline function Base.getindex( + d::DiagonalTensorMap, f₁::FusionTree{I, 1}, f₂::FusionTree{I, 1} + ) where {I <: Sector} s = f₁.uncoupled[1] s == f₁.coupled == f₂.uncoupled[1] == f₂.coupled || throw(SectorMismatch()) return block(d, s) # TODO: do we want a StridedView here? Then we need to allocate a new matrix. end -function Base.setindex!(d::DiagonalTensorMap, - v, - f₁::FusionTree{I,1}, - f₂::FusionTree{I,1}) where {I<:Sector} +function Base.setindex!( + d::DiagonalTensorMap, v, f₁::FusionTree{I, 1}, f₂::FusionTree{I, 1} + ) where {I <: Sector} return copy!(getindex(d, f₁, f₂), v) end @@ -197,8 +198,9 @@ function has_shared_permute(d::DiagonalTensorMap, (p₁, p₂)::Index2Tuple) end end -function permute(d::DiagonalTensorMap, (p₁, p₂)::Index2Tuple{1,1}; - copy::Bool=false) +function permute( + d::DiagonalTensorMap, (p₁, p₂)::Index2Tuple{1, 1}; copy::Bool = false + ) if p₁ === (1,) && p₂ === (2,) return copy ? Base.copy(d) : d elseif p₁ === (2,) && p₂ === (1,) # transpose @@ -220,11 +222,10 @@ end # VectorInterface # --------------- -function VectorInterface.zerovector(d::DiagonalTensorMap, ::Type{S}) where {S<:Number} +function VectorInterface.zerovector(d::DiagonalTensorMap, ::Type{S}) where {S <: Number} return DiagonalTensorMap(zerovector(d.data, S), d.domain) end -function VectorInterface.add(ty::DiagonalTensorMap, tx::DiagonalTensorMap, - α::Number, β::Number) +function VectorInterface.add(ty::DiagonalTensorMap, tx::DiagonalTensorMap, α::Number, β::Number) domain(ty) == domain(tx) || throw(SpaceMismatch("$(space(ty)) ≠ $(space(tx))")) T = VectorInterface.promote_add(ty, tx, α, β) return add!(scale!(zerovector(ty, T), ty, β), tx, α) # zerovector instead of similar preserves diagonal structure @@ -232,30 +233,32 @@ end # TensorOperations # ---------------- -function TO.tensoradd_type(TC, A::DiagonalTensorMap, ::Index2Tuple{1,1}, ::Bool) +function TO.tensoradd_type(TC, A::DiagonalTensorMap, ::Index2Tuple{1, 1}, ::Bool) M = similarstoragetype(A, TC) - return DiagonalTensorMap{TC,spacetype(A),M} + return DiagonalTensorMap{TC, spacetype(A), M} end -function TO.tensorcontract_type(TC, A::DiagonalTensorMap, ::Index2Tuple{1,1}, ::Bool, - B::DiagonalTensorMap, ::Index2Tuple{1,1}, ::Bool, - ::Index2Tuple{1,1}) +function TO.tensorcontract_type( + TC, A::DiagonalTensorMap, ::Index2Tuple{1, 1}, ::Bool, + B::DiagonalTensorMap, ::Index2Tuple{1, 1}, ::Bool, + ::Index2Tuple{1, 1} + ) M = similarstoragetype(A, TC) M == similarstoragetype(B, TC) || throw(ArgumentError("incompatible storage types:\n$(M) ≠ $(similarstoragetype(B, TC))")) spacetype(A) == spacetype(B) || throw(SpaceMismatch("incompatible space types")) - return DiagonalTensorMap{TC,spacetype(A),M} + return DiagonalTensorMap{TC, spacetype(A), M} end -function TO.tensoralloc(::Type{DiagonalTensorMap{T,S,M}}, - structure::TensorMapSpace{S,1,1}, - istemp::Val, - allocator=TO.DefaultAllocator()) where {T,S,M} +function TO.tensoralloc( + ::Type{DiagonalTensorMap{T, S, M}}, structure::TensorMapSpace{S, 1, 1}, + istemp::Val, allocator = TO.DefaultAllocator() + ) where {T, S, M} domain(structure) == codomain(structure) || throw(ArgumentError("domain ≠ codomain")) V = only(domain(structure)) dim = reduceddim(V) data = TO.tensoralloc(M, dim, istemp, allocator) - return DiagonalTensorMap{T,S,M}(data, V) + return DiagonalTensorMap{T, S, M}(data, V) end # Linear Algebra and factorizations @@ -271,10 +274,9 @@ function Base.zero(d::DiagonalTensorMap) return DiagonalTensorMap(zero.(d.data), d.domain) end -function LinearAlgebra.mul!(dC::DiagonalTensorMap, - dA::DiagonalTensorMap, - dB::DiagonalTensorMap, - α::Number, β::Number) +function LinearAlgebra.mul!( + dC::DiagonalTensorMap, dA::DiagonalTensorMap, dB::DiagonalTensorMap, α::Number, β::Number + ) dC.domain == dA.domain == dB.domain || throw(SpaceMismatch()) mul!(Diagonal(dC.data), Diagonal(dA.data), Diagonal(dB.data), α, β) return dC @@ -324,8 +326,10 @@ end # matrix functions for f in - (:exp, :cos, :sin, :tan, :cot, :cosh, :sinh, :tanh, :coth, :atan, :acot, :asinh, :sqrt, - :log, :asin, :acos, :acosh, :atanh, :acoth) + ( + :exp, :cos, :sin, :tan, :cot, :cosh, :sinh, :tanh, :coth, :atan, :acot, :asinh, :sqrt, + :log, :asin, :acos, :acosh, :atanh, :acoth, + ) @eval Base.$f(d::DiagonalTensorMap) = DiagonalTensorMap($f.(d.data), d.domain) end diff --git a/src/tensors/indexmanipulations.jl b/src/tensors/indexmanipulations.jl index c4e2b9228..7465a7bb0 100644 --- a/src/tensors/indexmanipulations.jl +++ b/src/tensors/indexmanipulations.jl @@ -13,7 +13,7 @@ Return a new tensor that is isomorphic to `t` but where the arrows on the indice general. To obtain the original tensor, one can use the `inv` keyword, i.e. it holds that `flip(flip(t, I), I; inv=true) == t`. """ -function flip(t::AbstractTensorMap, I; inv::Bool=false) +function flip(t::AbstractTensorMap, I; inv::Bool = false) P = flip(space(t), I) t′ = similar(t, P) for (f₁, f₂) in fusiontrees(t) @@ -32,9 +32,9 @@ The codomain and domain of `tdst` correspond to the indices in `p₁` and `p₂` See [`permute`](@ref) for creating a new tensor and [`add_permute!`](@ref) for a more general version. """ -@propagate_inbounds function Base.permute!(tdst::AbstractTensorMap, - tsrc::AbstractTensorMap, - p::Index2Tuple) +@propagate_inbounds function Base.permute!( + tdst::AbstractTensorMap, tsrc::AbstractTensorMap, p::Index2Tuple + ) return add_permute!(tdst, tsrc, p, One(), Zero()) end @@ -50,8 +50,9 @@ If `copy=false`, `tdst` might share data with `tsrc` whenever possible. Otherwis To permute into an existing destination, see [permute!](@ref) and [`add_permute!`](@ref) """ -function permute(t::AbstractTensorMap, (p₁, p₂)::Index2Tuple{N₁,N₂}; - copy::Bool=false) where {N₁,N₂} +function permute( + t::AbstractTensorMap, (p₁, p₂)::Index2Tuple{N₁, N₂}; copy::Bool = false + ) where {N₁, N₂} space′ = permute(space(t), (p₁, p₂)) # share data if possible if !copy && p₁ === codomainind(t) && p₂ === domainind(t) @@ -62,7 +63,7 @@ function permute(t::AbstractTensorMap, (p₁, p₂)::Index2Tuple{N₁,N₂}; return permute!(similar(t, space′), t, (p₁, p₂)) end end -function permute(t::TensorMap, (p₁, p₂)::Index2Tuple{N₁,N₂}; copy::Bool=false) where {N₁,N₂} +function permute(t::TensorMap, (p₁, p₂)::Index2Tuple{N₁, N₂}; copy::Bool = false) where {N₁, N₂} space′ = permute(space(t), (p₁, p₂)) # share data if possible if !copy @@ -77,12 +78,12 @@ function permute(t::TensorMap, (p₁, p₂)::Index2Tuple{N₁,N₂}; copy::Bool= return permute!(similar(t, space′), t, (p₁, p₂)) end end -function permute(t::AdjointTensorMap, (p₁, p₂)::Index2Tuple; copy::Bool=false) +function permute(t::AdjointTensorMap, (p₁, p₂)::Index2Tuple; copy::Bool = false) p₁′ = adjointtensorindices(t, p₂) p₂′ = adjointtensorindices(t, p₁) return adjoint(permute(adjoint(t), (p₁′, p₂′); copy)) end -function permute(t::AbstractTensorMap, p::IndexTuple; copy::Bool=false) +function permute(t::AbstractTensorMap, p::IndexTuple; copy::Bool = false) return permute(t, (p, ()); copy) end @@ -121,10 +122,9 @@ which determines whether they will braid over or under any other index with whic See [`braid`](@ref) for creating a new tensor and [`add_braid!`](@ref) for a more general version. """ -@propagate_inbounds function braid!(tdst::AbstractTensorMap, - tsrc::AbstractTensorMap, - p::Index2Tuple, - levels::IndexTuple) +@propagate_inbounds function braid!( + tdst::AbstractTensorMap, tsrc::AbstractTensorMap, p::Index2Tuple, levels::IndexTuple + ) return add_braid!(tdst, tsrc, p, levels, One(), Zero()) end @@ -142,11 +142,12 @@ If `copy=false`, `tdst` might share data with `tsrc` whenever possible. Otherwis To braid into an existing destination, see [braid!](@ref) and [`add_braid!`](@ref) """ -function braid(t::AbstractTensorMap, (p₁, p₂)::Index2Tuple, levels::IndexTuple; - copy::Bool=false) +function braid( + t::AbstractTensorMap, (p₁, p₂)::Index2Tuple, levels::IndexTuple; copy::Bool = false + ) @assert length(levels) == numind(t) if BraidingStyle(sectortype(t)) isa SymmetricBraiding - return permute(t, (p₁, p₂); copy=copy) + return permute(t, (p₁, p₂); copy = copy) end if !copy && p₁ == codomainind(t) && p₂ == domainind(t) return t @@ -174,9 +175,9 @@ the permutation `(p₁..., reverse(p₂)...)` should constitute a cyclic permuta See [`transpose`](@ref) for creating a new tensor and [`add_transpose!`](@ref) for a more general version. """ -function LinearAlgebra.transpose!(tdst::AbstractTensorMap, - tsrc::AbstractTensorMap, - (p₁, p₂)::Index2Tuple=_transpose_indices(t)) +function LinearAlgebra.transpose!( + tdst::AbstractTensorMap, tsrc::AbstractTensorMap, (p₁, p₂)::Index2Tuple = _transpose_indices(t) + ) return add_transpose!(tdst, tsrc, (p₁, p₂), One(), Zero()) end @@ -194,11 +195,12 @@ If `copy=false`, `tdst` might share data with `tsrc` whenever possible. Otherwis To permute into an existing destination, see [permute!](@ref) and [`add_permute!`](@ref) """ -function LinearAlgebra.transpose(t::AbstractTensorMap, - (p₁, p₂)::Index2Tuple=_transpose_indices(t); - copy::Bool=false) +function LinearAlgebra.transpose( + t::AbstractTensorMap, (p₁, p₂)::Index2Tuple = _transpose_indices(t); + copy::Bool = false + ) if sectortype(t) === Trivial - return permute(t, (p₁, p₂); copy=copy) + return permute(t, (p₁, p₂); copy = copy) end if !copy && p₁ == codomainind(t) && p₂ == domainind(t) return t @@ -210,12 +212,13 @@ function LinearAlgebra.transpose(t::AbstractTensorMap, end end -function LinearAlgebra.transpose(t::AdjointTensorMap, - (p₁, p₂)::Index2Tuple=_transpose_indices(t); - copy::Bool=false) +function LinearAlgebra.transpose( + t::AdjointTensorMap, (p₁, p₂)::Index2Tuple = _transpose_indices(t); + copy::Bool = false + ) p₁′ = map(n -> adjointtensorindex(t, n), p₂) p₂′ = map(n -> adjointtensorindex(t, n), p₁) - return adjoint(transpose(adjoint(t), (p₁′, p₂′); copy=copy)) + return adjoint(transpose(adjoint(t), (p₁′, p₂′); copy = copy)) end """ @@ -247,9 +250,9 @@ If `copy=false`, `tdst` might share data with `tsrc` whenever possible. Otherwis To repartition into an existing destination, see [repartition!](@ref). """ -@constprop :aggressive function repartition(t::AbstractTensorMap, N₁::Int, - N₂::Int=numind(t) - N₁; - copy::Bool=false) +@constprop :aggressive function repartition( + t::AbstractTensorMap, N₁::Int, N₂::Int = numind(t) - N₁; copy::Bool = false + ) N₁ + N₂ == numind(t) || throw(ArgumentError("Invalid repartition: $(numind(t)) to ($N₁, $N₂)")) all_inds = (codomainind(t)..., reverse(domainind(t))...) @@ -268,7 +271,7 @@ If `inv=true`, use the inverse twist. See [`twist`](@ref) for creating a new tensor. """ -function twist!(t::AbstractTensorMap, is; inv::Bool=false) +function twist!(t::AbstractTensorMap, is; inv::Bool = false) if !all(in(allind(t)), is) msg = "Can't twist indices $is of a tensor with only $(numind(t)) indices." throw(ArgumentError(msg)) @@ -299,7 +302,7 @@ If `inv=true`, use the inverse twist. See [`twist!`](@ref) for storing the result in place. """ -twist(t::AbstractTensorMap, i; inv::Bool=false) = twist!(copy(t), i; inv) +twist(t::AbstractTensorMap, i; inv::Bool = false) = twist!(copy(t), i; inv) # Methods which change the number of indices, implement using `Val(i)` for type inference """ @@ -315,8 +318,10 @@ If `copy=false`, `tdst` might share data with `tsrc` whenever possible. Otherwis See also [`insertrightunit`](@ref insertrightunit(::AbstractTensorMap, ::Val{i}) where {i}), [`removeunit`](@ref removeunit(::AbstractTensorMap, ::Val{i}) where {i}). """ -function insertleftunit(t::AbstractTensorMap, ::Val{i}=Val(numind(t) + 1); - copy::Bool=false, conj::Bool=false, dual::Bool=false) where {i} +function insertleftunit( + t::AbstractTensorMap, ::Val{i} = Val(numind(t) + 1); + copy::Bool = false, conj::Bool = false, dual::Bool = false + ) where {i} W = insertleftunit(space(t), Val(i); conj, dual) if t isa TensorMap return TensorMap{scalartype(t)}(copy ? Base.copy(t.data) : t.data, W) @@ -342,8 +347,10 @@ If `copy=false`, `tdst` might share data with `tsrc` whenever possible. Otherwis See also [`insertleftunit`](@ref insertleftunit(::AbstractTensorMap, ::Val{i}) where {i}), [`removeunit`](@ref removeunit(::AbstractTensorMap, ::Val{i}) where {i}). """ -function insertrightunit(t::AbstractTensorMap, ::Val{i}=Val(numind(t)); - copy::Bool=false, conj::Bool=false, dual::Bool=false) where {i} +function insertrightunit( + t::AbstractTensorMap, ::Val{i} = Val(numind(t)); + copy::Bool = false, conj::Bool = false, dual::Bool = false + ) where {i} W = insertrightunit(space(t), Val(i); conj, dual) if t isa TensorMap return TensorMap{scalartype(t)}(copy ? Base.copy(t.data) : t.data, W) @@ -368,7 +375,7 @@ If `copy=false`, `tdst` might share data with `tsrc` whenever possible. Otherwis This operation undoes the work of [`insertleftunit`](@ref insertleftunit(::AbstractTensorMap, ::Val{i}) where {i}) and [`insertrightunit`](@ref insertrightunit(::AbstractTensorMap, ::Val{i}) where {i}). """ -function removeunit(t::AbstractTensorMap, ::Val{i}; copy::Bool=false) where {i} +function removeunit(t::AbstractTensorMap, ::Val{i}; copy::Bool = false) where {i} W = removeunit(space(t), Val(i)) if t isa TensorMap return TensorMap{scalartype(t)}(copy ? Base.copy(t.data) : t.data, W) @@ -396,12 +403,10 @@ the indices of `tsrc` according to `(p₁, p₂)`. See also [`permute`](@ref), [`permute!`](@ref), [`add_braid!`](@ref), [`add_transpose!`](@ref). """ -@propagate_inbounds function add_permute!(tdst::AbstractTensorMap, - tsrc::AbstractTensorMap, - p::Index2Tuple, - α::Number, - β::Number, - backend::AbstractBackend...) +@propagate_inbounds function add_permute!( + tdst::AbstractTensorMap, tsrc::AbstractTensorMap, p::Index2Tuple, + α::Number, β::Number, backend::AbstractBackend... + ) transformer = treepermuter(tdst, tsrc, p) return add_transform!(tdst, tsrc, p, transformer, α, β, backend...) end @@ -415,13 +420,10 @@ the indices of `tsrc` according to `(p₁, p₂)` and `levels`. See also [`braid`](@ref), [`braid!`](@ref), [`add_permute!`](@ref), [`add_transpose!`](@ref). """ -@propagate_inbounds function add_braid!(tdst::AbstractTensorMap, - tsrc::AbstractTensorMap, - p::Index2Tuple, - levels::IndexTuple, - α::Number, - β::Number, - backend::AbstractBackend...) +@propagate_inbounds function add_braid!( + tdst::AbstractTensorMap, tsrc::AbstractTensorMap, p::Index2Tuple, levels::IndexTuple, + α::Number, β::Number, backend::AbstractBackend... + ) length(levels) == numind(tsrc) || throw(ArgumentError("incorrect levels $levels for tensor map $(codomain(tsrc)) ← $(domain(tsrc))")) @@ -441,23 +443,18 @@ the indices of `tsrc` according to `(p₁, p₂)`. See also [`transpose`](@ref), [`transpose!`](@ref), [`add_permute!`](@ref), [`add_braid!`](@ref). """ -@propagate_inbounds function add_transpose!(tdst::AbstractTensorMap, - tsrc::AbstractTensorMap, - p::Index2Tuple, - α::Number, - β::Number, - backend::AbstractBackend...) +@propagate_inbounds function add_transpose!( + tdst::AbstractTensorMap, tsrc::AbstractTensorMap, p::Index2Tuple, + α::Number, β::Number, backend::AbstractBackend... + ) transformer = treetransposer(tdst, tsrc, p) return add_transform!(tdst, tsrc, p, transformer, α, β, backend...) end -function add_transform!(tdst::AbstractTensorMap, - tsrc::AbstractTensorMap, - p::Index2Tuple, - transformer, - α::Number, - β::Number, - backend::AbstractBackend...) +function add_transform!( + tdst::AbstractTensorMap, tsrc::AbstractTensorMap, p::Index2Tuple, transformer, + α::Number, β::Number, backend::AbstractBackend... + ) @boundscheck begin permute(space(tsrc), p) == space(tdst) || throw(SpaceMismatch("source = $(codomain(tsrc))←$(domain(tsrc)), @@ -474,15 +471,17 @@ function add_transform!(tdst::AbstractTensorMap, if use_threaded_transform(tdst, transformer) _add_abelian_kernel_threaded!(tdst, tsrc, p, transformer, α, β, backend...) else - _add_abelian_kernel_nonthreaded!(tdst, tsrc, p, transformer, α, β, - backend...) + _add_abelian_kernel_nonthreaded!( + tdst, tsrc, p, transformer, α, β, backend... + ) end else if use_threaded_transform(tdst, transformer) _add_general_kernel_threaded!(tdst, tsrc, p, transformer, α, β, backend...) else - _add_general_kernel_nonthreaded!(tdst, tsrc, p, transformer, α, β, - backend...) + _add_general_kernel_nonthreaded!( + tdst, tsrc, p, transformer, α, β, backend... + ) end end end @@ -506,18 +505,20 @@ end # Abelian implementations # ----------------------- -function _add_abelian_kernel_nonthreaded!(tdst, tsrc, p, - transformer::AbelianTreeTransformer, - α, β, backend...) +function _add_abelian_kernel_nonthreaded!( + tdst, tsrc, p, transformer::AbelianTreeTransformer, α, β, backend... + ) for subtransformer in transformer.data _add_transform_single!(tdst, tsrc, p, subtransformer, α, β, backend...) end return nothing end -function _add_abelian_kernel_threaded!(tdst, tsrc, p, transformer::AbelianTreeTransformer, - α, β, backend...; - ntasks::Int=get_num_transformer_threads()) +function _add_abelian_kernel_threaded!( + tdst, tsrc, p, transformer::AbelianTreeTransformer, + α, β, backend...; + ntasks::Int = get_num_transformer_threads() + ) nblocks = length(transformer.data) counter = Threads.Atomic{Int}(1) Threads.@sync for _ in 1:min(ntasks, nblocks) @@ -533,9 +534,10 @@ function _add_abelian_kernel_threaded!(tdst, tsrc, p, transformer::AbelianTreeTr return nothing end -function _add_transform_single!(tdst, tsrc, p, - (coeff, struct_dst, struct_src)::_AbelianTransformerData, - α, β, backend...) +function _add_transform_single!( + tdst, tsrc, p, (coeff, struct_dst, struct_src)::_AbelianTransformerData, + α, β, backend... + ) subblock_dst = StridedView(tdst.data, struct_dst...) subblock_src = StridedView(tsrc.data, struct_src...) TO.tensoradd!(subblock_dst, subblock_src, p, false, α * coeff, β, backend...) @@ -551,24 +553,22 @@ end function _add_abelian_kernel_threaded!(tdst, tsrc, p, transformer, α, β, backend...) Threads.@sync for (f₁, f₂) in fusiontrees(tsrc) - Threads.@spawn _add_abelian_block!(tdst, tsrc, p, transformer, f₁, f₂, α, β, - backend...) + Threads.@spawn _add_abelian_block!(tdst, tsrc, p, transformer, f₁, f₂, α, β, backend...) end return nothing end function _add_abelian_block!(tdst, tsrc, p, transformer, f₁, f₂, α, β, backend...) (f₁′, f₂′), coeff = first(transformer(f₁, f₂)) - @inbounds TO.tensoradd!(tdst[f₁′, f₂′], tsrc[f₁, f₂], p, false, α * coeff, β, - backend...) + @inbounds TO.tensoradd!(tdst[f₁′, f₂′], tsrc[f₁, f₂], p, false, α * coeff, β, backend...) return nothing end # Non-abelian implementations # --------------------------- -function _add_general_kernel_nonthreaded!(tdst, tsrc, p, - transformer::GenericTreeTransformer, - α, β, backend...) +function _add_general_kernel_nonthreaded!( + tdst, tsrc, p, transformer::GenericTreeTransformer, α, β, backend... + ) # preallocate buffers buffers = allocate_buffers(tdst, tsrc, transformer) @@ -583,9 +583,10 @@ function _add_general_kernel_nonthreaded!(tdst, tsrc, p, return nothing end -function _add_general_kernel_threaded!(tdst, tsrc, p, transformer::GenericTreeTransformer, - α, β, backend...; - ntasks::Int=get_num_transformer_threads()) +function _add_general_kernel_threaded!( + tdst, tsrc, p, transformer::GenericTreeTransformer, α, β, backend...; + ntasks::Int = get_num_transformer_threads() + ) nblocks = length(transformer.data) counter = Threads.Atomic{Int}(1) @@ -601,8 +602,7 @@ function _add_general_kernel_threaded!(tdst, tsrc, p, transformer::GenericTreeTr if length(subtransformer[1]) == 1 _add_transform_single!(tdst, tsrc, p, subtransformer, α, β, backend...) else - _add_transform_multi!(tdst, tsrc, p, subtransformer, buffers, - α, β, backend...) + _add_transform_multi!(tdst, tsrc, p, subtransformer, buffers, α, β, backend...) end end end @@ -619,17 +619,16 @@ function _add_general_kernel_nonthreaded!(tdst, tsrc, p, transformer, α, β, ba end for (f₁, f₂) in fusiontrees(tsrc) for ((f₁′, f₂′), coeff) in transformer(f₁, f₂) - @inbounds TO.tensoradd!(tdst[f₁′, f₂′], tsrc[f₁, f₂], p, false, α * coeff, - One(), backend...) + @inbounds TO.tensoradd!(tdst[f₁′, f₂′], tsrc[f₁, f₂], p, false, α * coeff, One(), backend...) end end return nothing end -function _add_transform_single!(tdst, tsrc, p, - (basistransform, structs_dst, - structs_src)::_GenericTransformerData, - α, β, backend...) +function _add_transform_single!( + tdst, tsrc, p, (basistransform, structs_dst, structs_src)::_GenericTransformerData, + α, β, backend... + ) struct_dst = (structs_dst[1], only(structs_dst[2])...) struct_src = (structs_src[1], only(structs_src[2])...) coeff = only(basistransform) @@ -637,14 +636,16 @@ function _add_transform_single!(tdst, tsrc, p, return nothing end -function _add_transform_multi!(tdst, tsrc, p, - (basistransform, (sz_dst, structs_dst), - (sz_src, structs_src)), - (buffer1, buffer2), α, β, backend...) +function _add_transform_multi!( + tdst, tsrc, p, (basistransform, (sz_dst, structs_dst), (sz_src, structs_src)), + (buffer1, buffer2), α, β, backend... + ) rows, cols = size(basistransform) blocksize = prod(sz_src) - matsize = (prod(TupleTools.getindices(sz_src, codomainind(tsrc))), - prod(TupleTools.getindices(sz_src, domainind(tsrc)))) + matsize = ( + prod(TupleTools.getindices(sz_src, codomainind(tsrc))), + prod(TupleTools.getindices(sz_src, domainind(tsrc))), + ) # Filling up a buffer with contiguous data buffer_src = StridedView(buffer2, (blocksize, cols), (1, blocksize), 0) @@ -674,8 +675,7 @@ function _add_general_kernel_threaded!(tdst, tsrc, p, transformer, α, β, backe tdst = scale!(tdst, β) end Threads.@sync for s₁ in sectors(codomain(tsrc)), s₂ in sectors(domain(tsrc)) - Threads.@spawn _add_nonabelian_sector!(tdst, tsrc, p, transformer, s₁, s₂, α, - backend...) + Threads.@spawn _add_nonabelian_sector!(tdst, tsrc, p, transformer, s₁, s₂, α, backend...) end return nothing end @@ -684,8 +684,7 @@ function _add_nonabelian_sector!(tdst, tsrc, p, fusiontreetransform, s₁, s₂, for (f₁, f₂) in fusiontrees(tsrc) (f₁.uncoupled == s₁ && f₂.uncoupled == s₂) || continue for ((f₁′, f₂′), coeff) in fusiontreetransform(f₁, f₂) - @inbounds TO.tensoradd!(tdst[f₁′, f₂′], tsrc[f₁, f₂], p, false, α * coeff, - One(), backend...) + @inbounds TO.tensoradd!(tdst[f₁′, f₂′], tsrc[f₁, f₂], p, false, α * coeff, One(), backend...) end end return nothing diff --git a/src/tensors/linalg.jl b/src/tensors/linalg.jl index 7ab6c2988..437589515 100644 --- a/src/tensors/linalg.jl +++ b/src/tensors/linalg.jl @@ -15,8 +15,8 @@ Base.:*(α::Number, t::AbstractTensorMap) = VectorInterface.scale(t, α) Base.:/(t::AbstractTensorMap, α::Number) = *(t, one(scalartype(t)) / α) Base.:\(α::Number, t::AbstractTensorMap) = *(t, one(scalartype(t)) / α) -LinearAlgebra.normalize!(t::AbstractTensorMap, p::Real=2) = scale!(t, inv(norm(t, p))) -LinearAlgebra.normalize(t::AbstractTensorMap, p::Real=2) = scale(t, inv(norm(t, p))) +LinearAlgebra.normalize!(t::AbstractTensorMap, p::Real = 2) = scale!(t, inv(norm(t, p))) +LinearAlgebra.normalize(t::AbstractTensorMap, p::Real = 2) = scale(t, inv(norm(t, p))) # destination allocation for matrix multiplication function compose_dest(A::AbstractTensorMap, B::AbstractTensorMap) @@ -24,10 +24,12 @@ function compose_dest(A::AbstractTensorMap, B::AbstractTensorMap) pA = (codomainind(A), domainind(A)) pB = (codomainind(B), domainind(B)) pAB = (codomainind(A), ntuple(i -> i + numout(A), numin(B))) - return TO.tensoralloc_contract(TC, - A, pA, false, - B, pB, false, - pAB, Val(false)) + return TO.tensoralloc_contract( + TC, + A, pA, false, + B, pB, false, + pAB, Val(false) + ) end """ @@ -169,7 +171,7 @@ for morphism in (:isomorphism, :unitary, :isometry) function $morphism(A::Type, codomain::TensorSpace, domain::TensorSpace) return $morphism(A, codomain ← domain) end - function $morphism(A::Type, V::TensorMapSpace{S,N₁,N₂}) where {S,N₁,N₂} + function $morphism(A::Type, V::TensorMapSpace{S, N₁, N₂}) where {S, N₁, N₂} t = tensormaptype(S, N₁, N₂, A)(undef, V) return $morphism!(t) end @@ -183,9 +185,12 @@ function LinearAlgebra.diag(t::AbstractTensorMap) return SectorDict(c => LinearAlgebra.diag(b) for (c, b) in blocks(t)) end function LinearAlgebra.diagm(codom::VectorSpace, dom::VectorSpace, v::SectorDict) - return TensorMap(SectorDict(c => LinearAlgebra.diagm(blockdim(codom, c), - blockdim(dom, c), b) - for (c, b) in v), codom ← dom) + return TensorMap( + SectorDict( + c => LinearAlgebra.diagm(blockdim(codom, c), blockdim(dom, c), b) + for (c, b) in v + ), codom ← dom + ) end LinearAlgebra.isdiag(t::AbstractTensorMap) = all(LinearAlgebra.isdiag ∘ last, blocks(t)) @@ -217,8 +222,7 @@ function Base.fill!(t::TensorMap, value::Number) fill!(t.data, value) return t end -function LinearAlgebra.adjoint!(tdst::AbstractTensorMap, - tsrc::AbstractTensorMap) +function LinearAlgebra.adjoint!(tdst::AbstractTensorMap, tsrc::AbstractTensorMap) InnerProductStyle(tdst) === EuclideanInnerProduct() || throw_invalid_innerproduct(:adjoint!) space(tdst) == adjoint(space(tsrc)) || @@ -248,34 +252,35 @@ end function LinearAlgebra.axpy!(α::Number, t1::AbstractTensorMap, t2::AbstractTensorMap) return VectorInterface.add!(t2, t1, α) end -function LinearAlgebra.axpby!(α::Number, t1::AbstractTensorMap, β::Number, - t2::AbstractTensorMap) +function LinearAlgebra.axpby!( + α::Number, t1::AbstractTensorMap, β::Number, t2::AbstractTensorMap + ) return VectorInterface.add!(t2, t1, α, β) end # inner product and norm only valid for spaces with Euclidean inner product LinearAlgebra.dot(t1::AbstractTensorMap, t2::AbstractTensorMap) = inner(t1, t2) -function LinearAlgebra.norm(t::AbstractTensorMap, p::Real=2) +function LinearAlgebra.norm(t::AbstractTensorMap, p::Real = 2) InnerProductStyle(t) === EuclideanInnerProduct() || throw_invalid_innerproduct(:norm) return _norm(blocks(t), p, float(zero(real(scalartype(t))))) end function _norm(blockiter, p::Real, init::Real) if p == Inf - return mapreduce(max, blockiter; init=init) do (c, b) + return mapreduce(max, blockiter; init = init) do (c, b) return isempty(b) ? init : oftype(init, LinearAlgebra.normInf(b)) end elseif p == 2 - n² = mapreduce(+, blockiter; init=init) do (c, b) + n² = mapreduce(+, blockiter; init = init) do (c, b) return isempty(b) ? init : oftype(init, dim(c) * LinearAlgebra.norm2(b)^2) end return sqrt(n²) elseif p == 1 - return mapreduce(+, blockiter; init=init) do (c, b) + return mapreduce(+, blockiter; init = init) do (c, b) return isempty(b) ? init : oftype(init, dim(c) * sum(abs, b)) end elseif p > 0 - nᵖ = mapreduce(+, blockiter; init=init) do (c, b) + nᵖ = mapreduce(+, blockiter; init = init) do (c, b) return isempty(b) ? init : oftype(init, dim(c) * LinearAlgebra.normp(b, p)^p) end return (nᵖ)^inv(oftype(nᵖ, p)) @@ -287,8 +292,10 @@ end _default_rtol(t) = eps(real(float(scalartype(t)))) * min(dim(domain(t)), dim(codomain(t))) -function LinearAlgebra.rank(t::AbstractTensorMap; - atol::Real=0, rtol::Real=atol > 0 ? 0 : _default_rtol(t)) +function LinearAlgebra.rank( + t::AbstractTensorMap; + atol::Real = 0, rtol::Real = atol > 0 ? 0 : _default_rtol(t) + ) r = dim(one(sectortype(t))) * 0 dim(t) == 0 && return r S = LinearAlgebra.svdvals(t) @@ -302,7 +309,7 @@ function LinearAlgebra.rank(t::AbstractTensorMap; # return sum(((c, b),) -> dim(c) * count(>(tol), b), S; init) end -function LinearAlgebra.cond(t::AbstractTensorMap, p::Real=2) +function LinearAlgebra.cond(t::AbstractTensorMap, p::Real = 2) if p == 2 if dim(t) == 0 domain(t) == codomain(t) || @@ -330,9 +337,9 @@ function LinearAlgebra.tr(t::AbstractTensorMap) end # TensorMap multiplication -function LinearAlgebra.mul!(tC::AbstractTensorMap, - tA::AbstractTensorMap, - tB::AbstractTensorMap, α=true, β=false) +function LinearAlgebra.mul!( + tC::AbstractTensorMap, tA::AbstractTensorMap, tB::AbstractTensorMap, α = true, β = false + ) compose(space(tA), space(tB)) == space(tC) || throw(SpaceMismatch(lazy"$(space(tC)) ≠ $(space(tA)) * $(space(tB))")) @@ -430,9 +437,7 @@ function exp!(t::TensorMap) end # Sylvester equation with TensorMap objects: -function LinearAlgebra.sylvester(A::AbstractTensorMap, - B::AbstractTensorMap, - C::AbstractTensorMap) +function LinearAlgebra.sylvester(A::AbstractTensorMap, B::AbstractTensorMap, C::AbstractTensorMap) (codomain(A) == domain(A) == codomain(C) && codomain(B) == domain(B) == domain(C)) || throw(SpaceMismatch()) cod = domain(A) @@ -481,10 +486,11 @@ for f in (:sqrt, :log, :asin, :acos, :acosh, :atanh, :acoth) end # concatenate tensors -function catdomain(t1::TT, t2::TT) where {S,N₁,TT<:AbstractTensorMap{<:Any,S,N₁,1}} +function catdomain(t1::TT, t2::TT) where {S, N₁, TT <: AbstractTensorMap{<:Any, S, N₁, 1}} codomain(t1) == codomain(t2) || - throw(SpaceMismatch("codomains of tensors to concatenate must match:\n" * - "$(codomain(t1)) ≠ $(codomain(t2))")) + throw( + SpaceMismatch("codomains of tensors to concatenate must match:\n$(codomain(t1)) ≠ $(codomain(t2))") + ) V1, = domain(t1) V2, = domain(t2) isdual(V1) == isdual(V2) || @@ -499,10 +505,9 @@ function catdomain(t1::TT, t2::TT) where {S,N₁,TT<:AbstractTensorMap{<:Any,S,N end return t end -function catcodomain(t1::TT, t2::TT) where {S,N₂,TT<:AbstractTensorMap{<:Any,S,1,N₂}} +function catcodomain(t1::TT, t2::TT) where {S, N₂, TT <: AbstractTensorMap{<:Any, S, 1, N₂}} domain(t1) == domain(t2) || - throw(SpaceMismatch("domains of tensors to concatenate must match:\n" * - "$(domain(t1)) ≠ $(domain(t2))")) + throw(SpaceMismatch("domains of tensors to concatenate must match:\n$(domain(t1)) ≠ $(domain(t2))")) V1, = codomain(t1) V2, = codomain(t2) isdual(V1) == isdual(V2) || @@ -539,8 +544,8 @@ function absorb!(tdst::AbstractTensorMap, tsrc::AbstractTensorMap) throw(DimensionError("Incompatible number of indices for source and destination")) S = spacetype(tdst) S == spacetype(tsrc) || throw(SpaceMismatch("incompatible spacetypes")) - dom = mapreduce(infimum, ⊗, domain(tdst), domain(tsrc); init=one(S)) - cod = mapreduce(infimum, ⊗, codomain(tdst), codomain(tsrc); init=one(S)) + dom = mapreduce(infimum, ⊗, domain(tdst), domain(tsrc); init = one(S)) + cod = mapreduce(infimum, ⊗, codomain(tdst), codomain(tsrc); init = one(S)) for (f1, f2) in fusiontrees(cod ← dom) @inbounds data_dst = tdst[f1, f2] @inbounds data_src = tsrc[f1, f2] diff --git a/src/tensors/tensor.jl b/src/tensors/tensor.jl index a918478c3..accada77e 100644 --- a/src/tensors/tensor.jl +++ b/src/tensors/tensor.jl @@ -7,34 +7,31 @@ Specific subtype of [`AbstractTensorMap`](@ref) for representing tensor maps (morphisms in a tensor category), where the data is stored in a dense vector. """ -struct TensorMap{T,S<:IndexSpace,N₁,N₂,A<:DenseVector{T}} <: AbstractTensorMap{T,S,N₁,N₂} +struct TensorMap{T, S <: IndexSpace, N₁, N₂, A <: DenseVector{T}} <: AbstractTensorMap{T, S, N₁, N₂} data::A - space::TensorMapSpace{S,N₁,N₂} + space::TensorMapSpace{S, N₁, N₂} # uninitialized constructors - function TensorMap{T,S,N₁,N₂,A}(::UndefInitializer, - space::TensorMapSpace{S,N₁,N₂}) where {T,S<:IndexSpace, - N₁,N₂, - A<:DenseVector{T}} + function TensorMap{T, S, N₁, N₂, A}( + ::UndefInitializer, space::TensorMapSpace{S, N₁, N₂} + ) where {T, S <: IndexSpace, N₁, N₂, A <: DenseVector{T}} d = fusionblockstructure(space).totaldim data = A(undef, d) if !isbitstype(T) zerovector!(data) end - return TensorMap{T,S,N₁,N₂,A}(data, space) + return TensorMap{T, S, N₁, N₂, A}(data, space) end # constructors from data - function TensorMap{T,S,N₁,N₂,A}(data::A, - space::TensorMapSpace{S,N₁,N₂}) where {T,S<:IndexSpace, - N₁,N₂, - A<:DenseVector{T}} + function TensorMap{T, S, N₁, N₂, A}( + data::A, space::TensorMapSpace{S, N₁, N₂} + ) where {T, S <: IndexSpace, N₁, N₂, A <: DenseVector{T}} T ⊆ field(S) || @warn("scalartype(data) = $T ⊈ $(field(S)))", maxlog = 1) I = sectortype(S) T <: Real && !(sectorscalartype(I) <: Real) && - @warn("Tensors with real data might be incompatible with sector type $I", - maxlog = 1) - return new{T,S,N₁,N₂,A}(data, space) + @warn("Tensors with real data might be incompatible with sector type $I", maxlog = 1) + return new{T, S, N₁, N₂, A}(data, space) end end @@ -47,13 +44,13 @@ in a dense vector. A `Tensor{T, S, N, A}` is actually a special case `TensorMap{T, S, N, 0, A}`, i.e. a tensor map with only a non-trivial output space. """ -const Tensor{T,S,N,A} = TensorMap{T,S,N,0,A} +const Tensor{T, S, N, A} = TensorMap{T, S, N, 0, A} function tensormaptype(S::Type{<:IndexSpace}, N₁, N₂, TorA::Type) if TorA <: Number - return TensorMap{TorA,S,N₁,N₂,Vector{TorA}} + return TensorMap{TorA, S, N₁, N₂, Vector{TorA}} elseif TorA <: DenseVector - return TensorMap{scalartype(TorA),S,N₁,N₂,TorA} + return TensorMap{scalartype(TorA), S, N₁, N₂, TorA} else throw(ArgumentError("argument $TorA should specify a scalar type (`<:Number`) or a storage type `<:DenseVector{<:Number}`")) end @@ -68,7 +65,7 @@ space(t::TensorMap) = t.space Return the type of the storage `A` of the tensor map. """ -storagetype(::Type{<:TensorMap{T,S,N₁,N₂,A}}) where {T,S,N₁,N₂,A<:DenseVector{T}} = A +storagetype(::Type{<:TensorMap{T, S, N₁, N₂, A}}) where {T, S, N₁, N₂, A <: DenseVector{T}} = A dim(t::TensorMap) = length(t.data) @@ -86,26 +83,29 @@ dim(t::TensorMap) = length(t.data) Construct a `TensorMap` with uninitialized data. """ -function TensorMap{T}(::UndefInitializer, V::TensorMapSpace{S,N₁,N₂}) where {T,S,N₁,N₂} - return TensorMap{T,S,N₁,N₂,Vector{T}}(undef, V) +function TensorMap{T}(::UndefInitializer, V::TensorMapSpace{S, N₁, N₂}) where {T, S, N₁, N₂} + return TensorMap{T, S, N₁, N₂, Vector{T}}(undef, V) end -function TensorMap{T}(::UndefInitializer, codomain::TensorSpace{S}, - domain::TensorSpace{S}) where {T,S} +function TensorMap{T}( + ::UndefInitializer, codomain::TensorSpace{S}, domain::TensorSpace{S} + ) where {T, S} return TensorMap{T}(undef, codomain ← domain) end -function Tensor{T}(::UndefInitializer, V::TensorSpace{S}) where {T,S} +function Tensor{T}(::UndefInitializer, V::TensorSpace{S}) where {T, S} return TensorMap{T}(undef, V ← one(V)) end # constructor starting from vector = independent data (N₁ + N₂ = 1 is special cased below) # documentation is captured by the case where `data` is a general array # here, we force the `T` argument to distinguish it from the more general constructor below -function TensorMap{T}(data::A, - V::TensorMapSpace{S,N₁,N₂}) where {T,S,N₁,N₂,A<:DenseVector{T}} - return TensorMap{T,S,N₁,N₂,A}(data, V) +function TensorMap{T}( + data::A, V::TensorMapSpace{S, N₁, N₂} + ) where {T, S, N₁, N₂, A <: DenseVector{T}} + return TensorMap{T, S, N₁, N₂, A}(data, V) end -function TensorMap{T}(data::DenseVector{T}, codomain::TensorSpace{S}, - domain::TensorSpace{S}) where {T,S} +function TensorMap{T}( + data::DenseVector{T}, codomain::TensorSpace{S}, domain::TensorSpace{S} + ) where {T, S} return TensorMap(data, codomain ← domain) end @@ -129,8 +129,9 @@ Construct a `TensorMap` by explicitly specifying its block data. Alternatively, the domain and codomain can be specified by passing a [`HomSpace`](@ref) using the syntax `codomain ← domain` or `domain → codomain`. """ -function TensorMap(data::AbstractDict{<:Sector,<:AbstractMatrix}, - V::TensorMapSpace{S,N₁,N₂}) where {S,N₁,N₂} +function TensorMap( + data::AbstractDict{<:Sector, <:AbstractMatrix}, V::TensorMapSpace{S, N₁, N₂} + ) where {S, N₁, N₂} T = eltype(valtype(data)) t = TensorMap{T}(undef, V) for (c, b) in blocks(t) @@ -146,8 +147,9 @@ function TensorMap(data::AbstractDict{<:Sector,<:AbstractMatrix}, end return t end -function TensorMap(data::AbstractDict{<:Sector,<:AbstractMatrix}, codom::TensorSpace{S}, - dom::TensorSpace{S}) where {S} +function TensorMap( + data::AbstractDict{<:Sector, <:AbstractMatrix}, codom::TensorSpace{S}, dom::TensorSpace{S} + ) where {S} return TensorMap(data, codom ← dom) end @@ -169,12 +171,14 @@ Base.ones(::Type, ::HomSpace) for (fname, felt) in ((:zeros, :zero), (:ones, :one)) @eval begin - function Base.$fname(codomain::TensorSpace{S}, - domain::TensorSpace{S}=one(codomain)) where {S<:IndexSpace} + function Base.$fname( + codomain::TensorSpace{S}, domain::TensorSpace{S} = one(codomain) + ) where {S <: IndexSpace} return Base.$fname(codomain ← domain) end - function Base.$fname(::Type{T}, codomain::TensorSpace{S}, - domain::TensorSpace{S}=one(codomain)) where {T,S<:IndexSpace} + function Base.$fname( + ::Type{T}, codomain::TensorSpace{S}, domain::TensorSpace{S} = one(codomain) + ) where {T, S <: IndexSpace} return Base.$fname(T, codomain ← domain) end Base.$fname(V::TensorMapSpace) = Base.$fname(Float64, V) @@ -217,17 +221,17 @@ for randf in (:rand, :randn, :randexp, :randisometry) @doc $_docstr! $randfun!(::Type, ::HomSpace) # converting `codomain` and `domain` into `HomSpace` - function $randfun(codomain::TensorSpace{S}, - domain::TensorSpace{S}) where {S<:IndexSpace} + function $randfun(codomain::TensorSpace{S}, domain::TensorSpace{S}) where {S <: IndexSpace} return $randfun(codomain ← domain) end - function $randfun(::Type{T}, codomain::TensorSpace{S}, - domain::TensorSpace{S}) where {T,S<:IndexSpace} + function $randfun( + ::Type{T}, codomain::TensorSpace{S}, domain::TensorSpace{S} + ) where {T, S <: IndexSpace} return $randfun(T, codomain ← domain) end - function $randfun(rng::Random.AbstractRNG, ::Type{T}, - codomain::TensorSpace{S}, - domain::TensorSpace{S}) where {T,S<:IndexSpace} + function $randfun( + rng::Random.AbstractRNG, ::Type{T}, codomain::TensorSpace{S}, domain::TensorSpace{S} + ) where {T, S <: IndexSpace} return $randfun(rng, T, codomain ← domain) end @@ -236,8 +240,9 @@ for randf in (:rand, :randn, :randexp, :randisometry) function $randfun(::Type{T}, codomain::TensorSpace) where {T} return $randfun(T, codomain ← one(codomain)) end - function $randfun(rng::Random.AbstractRNG, ::Type{T}, - codomain::TensorSpace) where {T} + function $randfun( + rng::Random.AbstractRNG, ::Type{T}, codomain::TensorSpace + ) where {T} return $randfun(rng, T, codomain ← one(domain)) end @@ -254,8 +259,9 @@ for randf in (:rand, :randn, :randexp, :randisometry) $randfun!(t::AbstractTensorMap) = $randfun!(Random.default_rng(), t) # implementation - function $randfun(rng::Random.AbstractRNG, ::Type{T}, - V::TensorMapSpace) where {T} + function $randfun( + rng::Random.AbstractRNG, ::Type{T}, V::TensorMapSpace + ) where {T} t = TensorMap{T}(undef, V) $randfun!(rng, t) return t @@ -311,8 +317,10 @@ cases. to a plain array is possible, and only in the case where the `data` actually respects the specified symmetry structure, up to a tolerance `tol`. """ -function TensorMap(data::AbstractArray, V::TensorMapSpace{S,N₁,N₂}; - tol=sqrt(eps(real(float(eltype(data)))))) where {S<:IndexSpace,N₁,N₂} +function TensorMap( + data::AbstractArray, V::TensorMapSpace{S, N₁, N₂}; + tol = sqrt(eps(real(float(eltype(data))))) + ) where {S <: IndexSpace, N₁, N₂} T = eltype(data) if ndims(data) == 1 && length(data) == dim(V) if data isa DenseVector # refer to specific data-capturing constructor @@ -339,13 +347,14 @@ function TensorMap(data::AbstractArray, V::TensorMapSpace{S,N₁,N₂}; t = TensorMap{T}(undef, codom, dom) arraydata = reshape(collect(data), arraysize) t = project_symmetric!(t, arraydata) - if !isapprox(arraydata, convert(Array, t); atol=tol) + if !isapprox(arraydata, convert(Array, t); atol = tol) throw(ArgumentError("Data has non-zero elements at incompatible positions")) end return t end -function TensorMap(data::AbstractArray, codom::TensorSpace{S}, - dom::TensorSpace{S}; kwargs...) where {S} +function TensorMap( + data::AbstractArray, codom::TensorSpace{S}, dom::TensorSpace{S}; kwargs... + ) where {S} return TensorMap(data, codom ← dom; kwargs...) end function Tensor(data::AbstractArray, codom::TensorSpace, ; kwargs...) @@ -365,8 +374,9 @@ function project_symmetric!(t::TensorMap, data::DenseArray) else for (f₁, f₂) in fusiontrees(t) F = convert(Array, (f₁, f₂)) - dataslice = sview(data, axes(codomain(t), f₁.uncoupled)..., - axes(domain(t), f₂.uncoupled)...) + dataslice = sview( + data, axes(codomain(t), f₁.uncoupled)..., axes(domain(t), f₂.uncoupled)... + ) if FusionStyle(I) === UniqueFusion() Fscalar = first(F) # contains a single element scale!(t[f₁, f₂], dataslice, conj(Fscalar)) @@ -376,12 +386,14 @@ function project_symmetric!(t::TensorMap, data::DenseArray) indset1 = ntuple(identity, numind(t)) indset2 = 2 .* indset1 indset3 = indset2 .- 1 - TensorOperations.tensorcontract!(subblock, - F, ((), indset1), true, - sreshape(dataslice, szbF), - (indset3, indset2), false, - (indset1, ()), - inv(dim(f₁.coupled)), false) + TensorOperations.tensorcontract!( + subblock, + F, ((), indset1), true, + sreshape(dataslice, szbF), + (indset3, indset2), false, + (indset1, ()), + inv(dim(f₁.coupled)), false + ) end end end @@ -395,17 +407,17 @@ Base.copy(t::TensorMap) = typeof(t)(copy(t.data), t.space) # Conversion between TensorMap and Dict, for read and write purpose #------------------------------------------------------------------ function Base.convert(::Type{Dict}, t::AbstractTensorMap) - d = Dict{Symbol,Any}() + d = Dict{Symbol, Any}() d[:codomain] = repr(codomain(t)) d[:domain] = repr(domain(t)) - data = Dict{String,Any}() + data = Dict{String, Any}() for (c, b) in blocks(t) data[repr(c)] = Array(b) end d[:data] = data return d end -function Base.convert(::Type{TensorMap}, d::Dict{Symbol,Any}) +function Base.convert(::Type{TensorMap}, d::Dict{Symbol, Any}) try codomain = eval(Meta.parse(d[:codomain])) domain = eval(Meta.parse(d[:domain])) @@ -425,10 +437,10 @@ block(t::TensorMap, c::Sector) = blocks(t)[c] blocks(t::TensorMap) = BlockIterator(t, fusionblockstructure(t).blockstructure) -function blocktype(::Type{TT}) where {TT<:TensorMap} +function blocktype(::Type{TT}) where {TT <: TensorMap} A = storagetype(TT) T = eltype(A) - return Base.ReshapedArray{T,2,SubArray{T,1,A,Tuple{UnitRange{Int}},true},Tuple{}} + return Base.ReshapedArray{T, 2, SubArray{T, 1, A, Tuple{UnitRange{Int}}, true}, Tuple{}} end function Base.iterate(iter::BlockIterator{<:TensorMap}, state...) @@ -470,9 +482,9 @@ column indices correspond to `f₂.uncoupled`. See also [`Base.setindex!(::TensorMap{T,S,N₁,N₂}, ::Any, ::FusionTree{I,N₁}, ::FusionTree{I,N₂}) where {T,S,N₁,N₂,I<:Sector}`](@ref) """ -@inline function Base.getindex(t::TensorMap{T,S,N₁,N₂}, - f₁::FusionTree{I,N₁}, - f₂::FusionTree{I,N₂}) where {T,S,N₁,N₂,I<:Sector} +@inline function Base.getindex( + t::TensorMap{T, S, N₁, N₂}, f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂} + ) where {T, S, N₁, N₂, I <: Sector} structure = fusionblockstructure(t) @boundscheck begin haskey(structure.fusiontreeindices, (f₁, f₂)) || throw(SectorMismatch()) @@ -484,9 +496,9 @@ See also [`Base.setindex!(::TensorMap{T,S,N₁,N₂}, ::Any, ::FusionTree{I,N₁ end end # The following is probably worth special casing for trivial tensors -@inline function Base.getindex(t::TensorMap{T,S,N₁,N₂}, - f₁::FusionTree{Trivial,N₁}, - f₂::FusionTree{Trivial,N₂}) where {T,S,N₁,N₂} +@inline function Base.getindex( + t::TensorMap{T, S, N₁, N₂}, f₁::FusionTree{Trivial, N₁}, f₂::FusionTree{Trivial, N₂} + ) where {T, S, N₁, N₂} @boundscheck begin sectortype(t) == Trivial || throw(SectorMismatch()) end @@ -507,11 +519,9 @@ of size `(dims(codomain(t), f₁.uncoupled)..., dims(domain(t), f₂.uncoupled)) See also [`Base.getindex(::TensorMap{T,S,N₁,N₂}, ::FusionTree{I,N₁}, ::FusionTree{I,N₂}) where {T,S,N₁,N₂,I<:Sector}`](@ref) """ -@propagate_inbounds function Base.setindex!(t::TensorMap{T,S,N₁,N₂}, - v, - f₁::FusionTree{I,N₁}, - f₂::FusionTree{I,N₂}) where {T,S,N₁,N₂, - I<:Sector} +@propagate_inbounds function Base.setindex!( + t::TensorMap{T, S, N₁, N₂}, v, f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂} + ) where {T, S, N₁, N₂, I <: Sector} return copy!(getindex(t, f₁, f₂), v) end @@ -530,7 +540,7 @@ respectively, then a `StridedViews.StridedView` of size This method is only available for the case where `FusionStyle(I) isa UniqueFusion`, since it assumes a uniquely defined coupled charge. """ -@inline function Base.getindex(t::TensorMap, sectors::Tuple{I,Vararg{I}}) where {I<:Sector} +@inline function Base.getindex(t::TensorMap, sectors::Tuple{I, Vararg{I}}) where {I <: Sector} I === sectortype(t) || throw(SectorMismatch("Not a valid sectortype for this tensor.")) FusionStyle(I) isa UniqueFusion || throw(SectorMismatch("Indexing with sectors only possible if unique fusion")) @@ -581,6 +591,7 @@ function Base.show(io::IO, t::TensorMap) println(io) end end + return nothing end # Complex, real and imaginary parts @@ -600,8 +611,9 @@ function Base.convert(::Type{TensorMap}, t::AbstractTensorMap) return copy!(TensorMap{scalartype(t)}(undef, space(t)), t) end -function Base.convert(TT::Type{TensorMap{T,S,N₁,N₂,A}}, - t::AbstractTensorMap{<:Any,S,N₁,N₂}) where {T,S,N₁,N₂,A} +function Base.convert( + TT::Type{TensorMap{T, S, N₁, N₂, A}}, t::AbstractTensorMap{<:Any, S, N₁, N₂} + ) where {T, S, N₁, N₂, A} if typeof(t) === TT return t else @@ -610,11 +622,10 @@ function Base.convert(TT::Type{TensorMap{T,S,N₁,N₂,A}}, end end -function Base.promote_rule(::Type{<:TT₁}, - ::Type{<:TT₂}) where {S,N₁,N₂, - TT₁<:TensorMap{<:Any,S,N₁,N₂}, - TT₂<:TensorMap{<:Any,S,N₁,N₂}} +function Base.promote_rule( + ::Type{<:TT₁}, ::Type{<:TT₂} + ) where {S, N₁, N₂, TT₁ <: TensorMap{<:Any, S, N₁, N₂}, TT₂ <: TensorMap{<:Any, S, N₁, N₂}} A = VectorInterface.promote_add(storagetype(TT₁), storagetype(TT₂)) T = scalartype(A) - return TensorMap{T,S,N₁,N₂,A} + return TensorMap{T, S, N₁, N₂, A} end diff --git a/src/tensors/tensoroperations.jl b/src/tensors/tensoroperations.jl index 6110093b6..b75b9d636 100644 --- a/src/tensors/tensoroperations.jl +++ b/src/tensors/tensoroperations.jl @@ -5,11 +5,10 @@ function TO.tensorstructure(t::AbstractTensorMap, iA::Int, conjA::Bool) return !conjA ? space(t, iA) : conj(space(t, iA)) end -function TO.tensoralloc(::Type{TT}, - structure::TensorMapSpace{S,N₁,N₂}, - istemp::Val, - allocator=TO.DefaultAllocator()) where - {T,S,N₁,N₂,TT<:AbstractTensorMap{T,S,N₁,N₂}} +function TO.tensoralloc( + ::Type{TT}, structure::TensorMapSpace{S, N₁, N₂}, + istemp::Val, allocator = TO.DefaultAllocator() + ) where {T, S, N₁, N₂, TT <: AbstractTensorMap{T, S, N₁, N₂}} A = storagetype(TT) dim = fusionblockstructure(structure).totaldim data = TO.tensoralloc(A, dim, istemp, allocator) @@ -17,15 +16,16 @@ function TO.tensoralloc(::Type{TT}, return TensorMap{T}(data, structure) end -function TO.tensorfree!(t::TensorMap, allocator=TO.DefaultAllocator()) +function TO.tensorfree!(t::TensorMap, allocator = TO.DefaultAllocator()) TO.tensorfree!(t.data, allocator) return nothing end TO.tensorscalar(t::AbstractTensorMap) = scalar(t) -function _canonicalize(p::Index2Tuple{N₁,N₂}, - ::AbstractTensorMap{<:IndexSpace,N₁,N₂}) where {N₁,N₂} +function _canonicalize( + p::Index2Tuple{N₁, N₂}, ::AbstractTensorMap{<:IndexSpace, N₁, N₂} + ) where {N₁, N₂} return p end _canonicalize(p::Index2Tuple, t::AbstractTensorMap) = _canonicalize(linearize(p), t) @@ -36,10 +36,12 @@ function _canonicalize(p::IndexTuple, t::AbstractTensorMap) end # tensoradd! -function TO.tensoradd!(C::AbstractTensorMap, - A::AbstractTensorMap, pA::Index2Tuple, conjA::Bool, - α::Number, β::Number, - backend, allocator) +function TO.tensoradd!( + C::AbstractTensorMap, + A::AbstractTensorMap, pA::Index2Tuple, conjA::Bool, + α::Number, β::Number, + backend, allocator + ) if conjA A′ = adjoint(A) pA′ = adjointtensorindices(A, _canonicalize(pA, C)) @@ -50,15 +52,17 @@ function TO.tensoradd!(C::AbstractTensorMap, return C end -function TO.tensoradd_type(TC, A::AbstractTensorMap, ::Index2Tuple{N₁,N₂}, - ::Bool) where {N₁,N₂} +function TO.tensoradd_type( + TC, A::AbstractTensorMap, ::Index2Tuple{N₁, N₂}, ::Bool + ) where {N₁, N₂} I = sectortype(A) M = similarstoragetype(A, sectorscalartype(I) <: Real ? TC : complex(TC)) return tensormaptype(spacetype(A), N₁, N₂, M) end -function TO.tensoradd_structure(A::AbstractTensorMap, pA::Index2Tuple{N₁,N₂}, - conjA::Bool) where {N₁,N₂} +function TO.tensoradd_structure( + A::AbstractTensorMap, pA::Index2Tuple{N₁, N₂}, conjA::Bool + ) where {N₁, N₂} if !conjA # don't use `permute` as this is also used when indices are traced return select(space(A), pA) @@ -68,10 +72,12 @@ function TO.tensoradd_structure(A::AbstractTensorMap, pA::Index2Tuple{N₁,N₂} end # tensortrace! -function TO.tensortrace!(C::AbstractTensorMap, - A::AbstractTensorMap, p::Index2Tuple, q::Index2Tuple, - conjA::Bool, - α::Number, β::Number, backend, allocator) +function TO.tensortrace!( + C::AbstractTensorMap, + A::AbstractTensorMap, p::Index2Tuple, q::Index2Tuple, + conjA::Bool, + α::Number, β::Number, backend, allocator + ) if conjA A′ = adjoint(A) p′ = adjointtensorindices(A, _canonicalize(p, C)) @@ -84,11 +90,13 @@ function TO.tensortrace!(C::AbstractTensorMap, end # tensorcontract! -function TO.tensorcontract!(C::AbstractTensorMap, - A::AbstractTensorMap, pA::Index2Tuple, conjA::Bool, - B::AbstractTensorMap, pB::Index2Tuple, conjB::Bool, - pAB::Index2Tuple, α::Number, β::Number, - backend, allocator) +function TO.tensorcontract!( + C::AbstractTensorMap, + A::AbstractTensorMap, pA::Index2Tuple, conjA::Bool, + B::AbstractTensorMap, pB::Index2Tuple, conjB::Bool, + pAB::Index2Tuple, α::Number, β::Number, + backend, allocator + ) pAB′ = _canonicalize(pAB, C) if conjA && conjB A′ = A' @@ -110,10 +118,12 @@ function TO.tensorcontract!(C::AbstractTensorMap, return C end -function TO.tensorcontract_type(TC, - A::AbstractTensorMap, ::Index2Tuple, ::Bool, - B::AbstractTensorMap, ::Index2Tuple, ::Bool, - ::Index2Tuple{N₁,N₂}) where {N₁,N₂} +function TO.tensorcontract_type( + TC, + A::AbstractTensorMap, ::Index2Tuple, ::Bool, + B::AbstractTensorMap, ::Index2Tuple, ::Bool, + ::Index2Tuple{N₁, N₂} + ) where {N₁, N₂} spacetype(A) == spacetype(B) || throw(SpaceMismatch("incompatible space types")) I = sectortype(A) M = similarstoragetype(A, sectorscalartype(I) <: Real ? TC : complex(TC)) @@ -122,17 +132,21 @@ function TO.tensorcontract_type(TC, return tensormaptype(spacetype(A), N₁, N₂, M) end -function TO.tensorcontract_structure(A::AbstractTensorMap, pA::Index2Tuple, conjA::Bool, - B::AbstractTensorMap, pB::Index2Tuple, conjB::Bool, - pAB::Index2Tuple{N₁,N₂}) where {N₁,N₂} +function TO.tensorcontract_structure( + A::AbstractTensorMap, pA::Index2Tuple, conjA::Bool, + B::AbstractTensorMap, pB::Index2Tuple, conjB::Bool, + pAB::Index2Tuple{N₁, N₂} + ) where {N₁, N₂} sA = TO.tensoradd_structure(A, pA, conjA) sB = TO.tensoradd_structure(B, pB, conjB) return permute(compose(sA, sB), pAB) end -function TO.checkcontractible(tA::AbstractTensorMap, iA::Int, conjA::Bool, - tB::AbstractTensorMap, iB::Int, conjB::Bool, - label) +function TO.checkcontractible( + tA::AbstractTensorMap, iA::Int, conjA::Bool, + tB::AbstractTensorMap, iB::Int, conjB::Bool, + label + ) sA = TO.tensorstructure(tA, iA, conjA)' sB = TO.tensorstructure(tB, iB, conjB) sA == sB || @@ -156,13 +170,15 @@ TO.tensorcost(t::AbstractTensorMap, i::Int) = dim(space(t, i)) Return the updated `tdst`, which is the result of adding `α * tsrc` to `tdst` after permuting the indices of `tsrc` according to `(p₁, p₂)` and furthermore tracing the indices in `q₁` and `q₂`. """ -function trace_permute!(tdst::AbstractTensorMap, - tsrc::AbstractTensorMap, - (p₁, p₂)::Index2Tuple, - (q₁, q₂)::Index2Tuple, - α::Number, - β::Number, - backend=TO.DefaultBackend()) +function trace_permute!( + tdst::AbstractTensorMap, + tsrc::AbstractTensorMap, + (p₁, p₂)::Index2Tuple, + (q₁, q₂)::Index2Tuple, + α::Number, + β::Number, + backend = TO.DefaultBackend() + ) # some input checks (S = spacetype(tdst)) == spacetype(tsrc) || throw(SpaceMismatch("incompatible spacetypes")) @@ -235,12 +251,14 @@ end Return the updated `C`, which is the result of adding `α * A * B` to `C` after permuting the indices of `A` and `B` according to `(oindA, cindA)` and `(cindB, oindB)` respectively. """ -function contract!(C::AbstractTensorMap, - A::AbstractTensorMap, (oindA, cindA)::Index2Tuple, - B::AbstractTensorMap, (cindB, oindB)::Index2Tuple, - (p₁, p₂)::Index2Tuple, - α::Number, β::Number, - backend, allocator) +function contract!( + C::AbstractTensorMap, + A::AbstractTensorMap, (oindA, cindA)::Index2Tuple, + B::AbstractTensorMap, (cindB, oindB)::Index2Tuple, + (p₁, p₂)::Index2Tuple, + α::Number, β::Number, + backend, allocator + ) length(cindA) == length(cindB) || throw(IndexError("number of contracted indices does not match")) N₁, N₂ = length(oindA), length(oindB) @@ -263,19 +281,15 @@ function contract!(C::AbstractTensorMap, # keep order A en B, check possibilities for cind memcost1 = memcost2 = dC * (!hsp(C, (oindAinC, oindBinC))) - memcost1 += dA * (!hsp(A, (oindA, cindA′))) + - dB * (!hsp(B, (cindB′, oindB))) - memcost2 += dA * (!hsp(A, (oindA, cindA′′))) + - dB * (!hsp(B, (cindB′′, oindB))) + memcost1 += dA * (!hsp(A, (oindA, cindA′))) + dB * (!hsp(B, (cindB′, oindB))) + memcost2 += dA * (!hsp(A, (oindA, cindA′′))) + dB * (!hsp(B, (cindB′′, oindB))) # reverse order A en B, check possibilities for cind memcost3 = memcost4 = dC * (!hsp(C, (oindBinC, oindAinC))) - memcost3 += dB * (!hsp(B, (oindB, cindB′))) + - dA * (!hsp(A, (cindA′, oindA))) - memcost4 += dB * (!hsp(B, (oindB, cindB′′))) + - dA * (!hsp(A, (cindA′′, oindA))) + memcost3 += dB * (!hsp(B, (oindB, cindB′))) + dA * (!hsp(A, (cindA′, oindA))) + memcost4 += dB * (!hsp(B, (oindB, cindB′′))) + dA * (!hsp(A, (cindA′′, oindA))) - if min(memcost1, memcost2) <= min(memcost3, memcost4) + return if min(memcost1, memcost2) <= min(memcost3, memcost4) if memcost1 <= memcost2 return _contract!(α, A, B, β, C, oindA, cindA′, oindB, cindB′, p₁, p₂) else @@ -293,11 +307,13 @@ function contract!(C::AbstractTensorMap, end # TODO: also transform _contract! into new interface, and add backend support -function _contract!(α, A::AbstractTensorMap, B::AbstractTensorMap, - β, C::AbstractTensorMap, - oindA::IndexTuple, cindA::IndexTuple, - oindB::IndexTuple, cindB::IndexTuple, - p₁::IndexTuple, p₂::IndexTuple) +function _contract!( + α, A::AbstractTensorMap, B::AbstractTensorMap, + β, C::AbstractTensorMap, + oindA::IndexTuple, cindA::IndexTuple, + oindB::IndexTuple, cindB::IndexTuple, + p₁::IndexTuple, p₂::IndexTuple + ) if !(BraidingStyle(sectortype(C)) isa SymmetricBraiding) throw(SectorMismatch("only tensors with symmetric braiding rules can be contracted; try `@planar` instead")) end @@ -310,7 +326,7 @@ function _contract!(α, A::AbstractTensorMap, B::AbstractTensorMap, end end end - A′ = permute(A, (oindA, cindA); copy=copyA) + A′ = permute(A, (oindA, cindA); copy = copyA) B′ = permute(B, (cindB, oindB)) if BraidingStyle(sectortype(A)) isa Fermionic for i in domainind(A′) @@ -339,5 +355,5 @@ end function scalar(t::AbstractTensorMap) # TODO: should scalar only work if N₁ == N₂ == 0? return dim(codomain(t)) == dim(domain(t)) == 1 ? - first(blocks(t))[2][1, 1] : throw(DimensionMismatch()) + first(blocks(t))[2][1, 1] : throw(DimensionMismatch()) end diff --git a/src/tensors/treetransformers.jl b/src/tensors/treetransformers.jl index 046af5edc..36cd3926d 100644 --- a/src/tensors/treetransformers.jl +++ b/src/tensors/treetransformers.jl @@ -7,10 +7,10 @@ abstract type TreeTransformer end struct TrivialTreeTransformer <: TreeTransformer end -const _AbelianTransformerData{T,N} = Tuple{T,StridedStructure{N},StridedStructure{N}} +const _AbelianTransformerData{T, N} = Tuple{T, StridedStructure{N}, StridedStructure{N}} -struct AbelianTreeTransformer{T,N} <: TreeTransformer - data::Vector{_AbelianTransformerData{T,N}} +struct AbelianTreeTransformer{T, N} <: TreeTransformer + data::Vector{_AbelianTransformerData{T, N}} end function AbelianTreeTransformer(transform, p, Vdst, Vsrc) @@ -22,7 +22,7 @@ function AbelianTreeTransformer(transform, p, Vdst, Vsrc) L = length(structure_src.fusiontreelist) T = sectorscalartype(sectortype(Vdst)) N = numind(Vsrc) - data = Vector{Tuple{T,StridedStructure{N},StridedStructure{N}}}(undef, L) + data = Vector{Tuple{T, StridedStructure{N}, StridedStructure{N}}}(undef, L) for i in 1:L f₁, f₂ = structure_src.fusiontreelist[i] @@ -45,14 +45,14 @@ function AbelianTreeTransformer(transform, p, Vdst, Vsrc) return transformer end -const _GenericTransformerData{T,N} = Tuple{Matrix{T}, - Tuple{NTuple{N,Int}, - Vector{Tuple{NTuple{N,Int},Int}}}, - Tuple{NTuple{N,Int}, - Vector{Tuple{NTuple{N,Int},Int}}}} +const _GenericTransformerData{T, N} = Tuple{ + Matrix{T}, + Tuple{NTuple{N, Int}, Vector{Tuple{NTuple{N, Int}, Int}}}, + Tuple{NTuple{N, Int}, Vector{Tuple{NTuple{N, Int}, Int}}}, +} -struct GenericTreeTransformer{T,N} <: TreeTransformer - data::Vector{_GenericTransformerData{T,N}} +struct GenericTreeTransformer{T, N} <: TreeTransformer + data::Vector{_GenericTransformerData{T, N}} end function GenericTreeTransformer(transform, p, Vdst, Vsrc) @@ -76,7 +76,7 @@ function GenericTreeTransformer(transform, p, Vdst, Vsrc) T = sectorscalartype(I) N = numind(Vdst) L = length(uncoupleds_src_unique) - data = Vector{_GenericTransformerData{T,N}}(undef, L) + data = Vector{_GenericTransformerData{T, N}}(undef, L) # TODO: this can be multithreaded for (i, uncoupled) in enumerate(uncoupleds_src_unique) @@ -101,24 +101,28 @@ function GenericTreeTransformer(transform, p, Vdst, Vsrc) sz_src, newstructs_src = repack_transformer_structure(fusionstructure_src, inds_src) sz_dst, newstructs_dst = repack_transformer_structure(fusionstructure_dst, inds_dst) - @debug("Created recoupling block for uncoupled: $uncoupled", - sz = size(matrix), sparsity = count(!iszero, matrix) / length(matrix)) + @debug( + "Created recoupling block for uncoupled: $uncoupled", + sz = size(matrix), sparsity = count(!iszero, matrix) / length(matrix) + ) data[i] = (matrix, (sz_dst, newstructs_dst), (sz_src, newstructs_src)) end - transformer = GenericTreeTransformer{T,N}(data) + transformer = GenericTreeTransformer{T, N}(data) # sort by (approximate) weight to facilitate multi-threading strategies sort!(transformer) Δt = Base.time() - t₀ - @debug("TreeTransformer for $Vsrc to $Vdst via $p", - nblocks = length(data), - sz_median = size(data[cld(end, 2)][1], 1), - sz_max = size(data[1][1], 1), - Δt) + @debug( + "TreeTransformer for $Vsrc to $Vdst via $p", + nblocks = length(data), + sz_median = size(data[cld(end, 2)][1], 1), + sz_max = size(data[1][1], 1), + Δt + ) return transformer end @@ -130,13 +134,14 @@ function repack_transformer_structure(structures, ids) end function buffersize(transformer::GenericTreeTransformer) - return maximum(transformer.data; init=0) do (basistransform, structures_dst, _) + return maximum(transformer.data; init = 0) do (basistransform, structures_dst, _) return prod(structures_dst[1]) * size(basistransform, 1) end end -function allocate_buffers(tdst::TensorMap, tsrc::TensorMap, - transformer::GenericTreeTransformer) +function allocate_buffers( + tdst::TensorMap, tsrc::TensorMap, transformer::GenericTreeTransformer + ) sz = buffersize(transformer) return similar(tdst.data, sz), similar(tsrc.data, sz) end @@ -147,12 +152,12 @@ function treetransformertype(Vdst, Vsrc) T = sectorscalartype(I) N = numind(Vdst) - return FusionStyle(I) == UniqueFusion() ? AbelianTreeTransformer{T,N} : - GenericTreeTransformer{T,N} + return FusionStyle(I) == UniqueFusion() ? AbelianTreeTransformer{T, N} : GenericTreeTransformer{T, N} end -function TreeTransformer(transform::Function, p, Vdst::HomSpace{S}, - Vsrc::HomSpace{S}) where {S} +function TreeTransformer( + transform::Function, p, Vdst::HomSpace{S}, Vsrc::HomSpace{S} + ) where {S} permute(Vsrc, p) == Vdst || throw(SpaceMismatch("Incompatible spaces for permuting")) @@ -160,8 +165,8 @@ function TreeTransformer(transform::Function, p, Vdst::HomSpace{S}, I === Trivial && return TrivialTreeTransformer() return FusionStyle(I) == UniqueFusion() ? - AbelianTreeTransformer(transform, p, Vdst, Vsrc) : - GenericTreeTransformer(transform, p, Vdst, Vsrc) + AbelianTreeTransformer(transform, p, Vdst, Vsrc) : + GenericTreeTransformer(transform, p, Vdst, Vsrc) end # braid is special because it has levels @@ -171,8 +176,9 @@ end function treebraider(tdst::TensorMap, tsrc::TensorMap, p::Index2Tuple, levels) return treebraider(space(tdst), space(tsrc), p, levels) end -@cached function treebraider(Vdst::TensorMapSpace, Vsrc::TensorMapSpace, p::Index2Tuple, - levels)::treetransformertype(Vdst, Vsrc) +@cached function treebraider( + Vdst::TensorMapSpace, Vsrc::TensorMapSpace, p::Index2Tuple, levels + )::treetransformertype(Vdst, Vsrc) fusiontreebraider(f1, f2) = braid(f1, f2, levels..., p...) return TreeTransformer(fusiontreebraider, p, Vdst, Vsrc) end @@ -186,8 +192,9 @@ for (transform, treetransformer) in function $treetransformer(tdst::TensorMap, tsrc::TensorMap, p::Index2Tuple) return $treetransformer(space(tdst), space(tsrc), p) end - @cached function $treetransformer(Vdst::TensorMapSpace, Vsrc::TensorMapSpace, - p::Index2Tuple)::treetransformertype(Vdst, Vsrc) + @cached function $treetransformer( + Vdst::TensorMapSpace, Vsrc::TensorMapSpace, p::Index2Tuple + )::treetransformertype(Vdst, Vsrc) fusiontreetransform(f1, f2) = $transform(f1, f2, p...) return TreeTransformer(fusiontreetransform, p, Vdst, Vsrc) end @@ -198,8 +205,10 @@ end # Sorting based on cost model # --------------------------- -function Base.sort!(transformer::Union{AbelianTreeTransformer,GenericTreeTransformer}; - by=_transformer_weight, rev::Bool=true) +function Base.sort!( + transformer::Union{AbelianTreeTransformer, GenericTreeTransformer}; + by = _transformer_weight, rev::Bool = true + ) sort!(transformer.data; by, rev) return transformer end diff --git a/src/tensors/vectorinterface.jl b/src/tensors/vectorinterface.jl index f9eb0b6d1..49947b998 100644 --- a/src/tensors/vectorinterface.jl +++ b/src/tensors/vectorinterface.jl @@ -1,10 +1,10 @@ # scalartype #------------ -VectorInterface.scalartype(::Type{TT}) where {T,TT<:AbstractTensorMap{T}} = scalartype(T) +VectorInterface.scalartype(::Type{TT}) where {T, TT <: AbstractTensorMap{T}} = scalartype(T) # zerovector & zerovector!! #--------------------------- -function VectorInterface.zerovector(t::AbstractTensorMap, ::Type{S}) where {S<:Number} +function VectorInterface.zerovector(t::AbstractTensorMap, ::Type{S}) where {S <: Number} return zerovector!(similar(t, S)) end function VectorInterface.zerovector!(t::AbstractTensorMap) @@ -64,28 +64,32 @@ end # add, add! & add!! #------------------- # TODO: remove VectorInterface from calls to `add!` when `TensorKit.add!` is renamed -function VectorInterface.add(ty::AbstractTensorMap, tx::AbstractTensorMap, - α::Number, β::Number) +function VectorInterface.add( + ty::AbstractTensorMap, tx::AbstractTensorMap, α::Number, β::Number + ) space(ty) == space(tx) || throw(SpaceMismatch("$(space(ty)) ≠ $(space(tx))")) T = VectorInterface.promote_add(ty, tx, α, β) return add!(scale!(zerovector(ty, T), ty, β), tx, α) end -function VectorInterface.add!(ty::AbstractTensorMap, tx::AbstractTensorMap, - α::Number, β::Number) +function VectorInterface.add!( + ty::AbstractTensorMap, tx::AbstractTensorMap, α::Number, β::Number + ) space(ty) == space(tx) || throw(SpaceMismatch("$(space(ty)) ≠ $(space(tx))")) for ((cy, by), (cx, bx)) in zip(blocks(ty), blocks(tx)) add!(by, bx, α, β) end return ty end -function VectorInterface.add!(ty::TensorMap, tx::TensorMap, - α::Number, β::Number) +function VectorInterface.add!( + ty::TensorMap, tx::TensorMap, α::Number, β::Number + ) space(ty) == space(tx) || throw(SpaceMismatch("$(space(ty)) ≠ $(space(tx))")) add!(ty.data, tx.data, α, β) return ty end -function VectorInterface.add!!(ty::AbstractTensorMap, tx::AbstractTensorMap, - α::Number, β::Number) +function VectorInterface.add!!( + ty::AbstractTensorMap, tx::AbstractTensorMap, α::Number, β::Number + ) # spacecheck is done in add(!) T = VectorInterface.promote_add(ty, tx, α, β) if T <: scalartype(ty) diff --git a/test/ad.jl b/test/ad.jl index ddd419db1..39bc9b161 100644 --- a/test/ad.jl +++ b/test/ad.jl @@ -23,8 +23,9 @@ function ChainRulesTestUtils.rand_tangent(rng::AbstractRNG, x::DiagonalTensorMap return DiagonalTensorMap(randn(eltype(x), reduceddim(V)), V) end ChainRulesTestUtils.rand_tangent(::AbstractRNG, ::VectorSpace) = NoTangent() -function ChainRulesTestUtils.test_approx(actual::AbstractTensorMap, - expected::AbstractTensorMap, msg=""; kwargs...) +function ChainRulesTestUtils.test_approx( + actual::AbstractTensorMap, expected::AbstractTensorMap, msg = ""; kwargs... + ) for (c, b) in blocks(actual) ChainRulesTestUtils.@test_msg msg isapprox(b, block(expected, c); kwargs...) end @@ -34,7 +35,7 @@ end # make sure that norms are computed correctly: function FiniteDifferences.to_vec(t::TensorKit.SectorDict) T = scalartype(valtype(t)) - vec = mapreduce(vcat, t; init=T[]) do (c, b) + vec = mapreduce(vcat, t; init = T[]) do (c, b) return reshape(b, :) .* sqrt(dim(c)) end vec_real = T <: Real ? vec : collect(reinterpret(real(T), vec)) @@ -42,29 +43,33 @@ function FiniteDifferences.to_vec(t::TensorKit.SectorDict) function from_vec(x_real) x = T <: Real ? x_real : reinterpret(T, x_real) ctr = 0 - return TensorKit.SectorDict(c => (n = length(b); - b′ = reshape(view(x, ctr .+ (1:n)), size(b)) ./ - sqrt(dim(c)); - ctr += n; - b′) - for (c, b) in t) + return TensorKit.SectorDict( + c => ( + n = length(b); + b′ = reshape(view(x, ctr .+ (1:n)), size(b)) ./ sqrt(dim(c)); + ctr += n; + b′ + ) for (c, b) in t + ) end return vec_real, from_vec end # Float32 and finite differences don't mix well -precision(::Type{<:Union{Float32,Complex{Float32}}}) = 1.0e-2 -precision(::Type{<:Union{Float64,Complex{Float64}}}) = 1.0e-6 +precision(::Type{<:Union{Float32, Complex{Float32}}}) = 1.0e-2 +precision(::Type{<:Union{Float64, Complex{Float64}}}) = 1.0e-6 -function randindextuple(N::Int, k::Int=rand(0:N)) +function randindextuple(N::Int, k::Int = rand(0:N)) @assert 0 ≤ k ≤ N _p = randperm(N) return (tuple(_p[1:k]...), tuple(_p[(k + 1):end]...)) end -function test_ad_rrule(f, args...; check_inferred=false, kwargs...) - test_rrule(Zygote.ZygoteRuleConfig(), f, args...; - rrule_f=rrule_via_ad, check_inferred, kwargs...) +function test_ad_rrule(f, args...; check_inferred = false, kwargs...) + test_rrule( + Zygote.ZygoteRuleConfig(), f, args...; + rrule_f = rrule_via_ad, check_inferred, kwargs... + ) return nothing end @@ -93,8 +98,9 @@ function remove_lqgauge_dependence!(ΔQ, t, Q) end return ΔQ end -function remove_eiggauge_dependence!(ΔV, D, V; - degeneracy_atol=MatrixAlgebraKit.default_pullback_gaugetol(D)) +function remove_eiggauge_dependence!( + ΔV, D, V; degeneracy_atol = MatrixAlgebraKit.default_pullback_gaugetol(D) + ) gaugepart = V' * ΔV for (c, b) in blocks(gaugepart) Dc = diagview(block(D, c)) @@ -108,8 +114,9 @@ function remove_eiggauge_dependence!(ΔV, D, V; mul!(ΔV, V / (V' * V), gaugepart, -1, 1) return ΔV end -function remove_eighgauge_dependence!(ΔV, D, V; - degeneracy_atol=MatrixAlgebraKit.default_pullback_gaugetol(D)) +function remove_eighgauge_dependence!( + ΔV, D, V; degeneracy_atol = MatrixAlgebraKit.default_pullback_gaugetol(D) + ) gaugepart = V' * ΔV gaugepart = (gaugepart - gaugepart') / 2 for (c, b) in blocks(gaugepart) @@ -124,8 +131,9 @@ function remove_eighgauge_dependence!(ΔV, D, V; mul!(ΔV, V / (V' * V), gaugepart, -1, 1) return ΔV end -function remove_svdgauge_dependence!(ΔU, ΔVᴴ, U, S, Vᴴ; - degeneracy_atol=MatrixAlgebraKit.default_pullback_gaugetol(S)) +function remove_svdgauge_dependence!( + ΔU, ΔVᴴ, U, S, Vᴴ; degeneracy_atol = MatrixAlgebraKit.default_pullback_gaugetol(S) + ) gaugepart = U' * ΔU + Vᴴ * ΔVᴴ' gaugepart = (gaugepart - gaugepart') / 2 for (c, b) in blocks(gaugepart) @@ -148,32 +156,44 @@ project_hermitian(A) = (A + A') / 2 ChainRulesTestUtils.test_method_tables() -spacelist = ((ℂ^2, (ℂ^3)', ℂ^3, ℂ^2, (ℂ^2)'), - (Vect[Z2Irrep](0 => 1, 1 => 1), - Vect[Z2Irrep](0 => 1, 1 => 2)', - Vect[Z2Irrep](0 => 2, 1 => 2)', - Vect[Z2Irrep](0 => 2, 1 => 3), - Vect[Z2Irrep](0 => 2, 1 => 2)), - (Vect[FermionParity](0 => 1, 1 => 1), - Vect[FermionParity](0 => 1, 1 => 2)', - Vect[FermionParity](0 => 2, 1 => 1)', - Vect[FermionParity](0 => 2, 1 => 3), - Vect[FermionParity](0 => 2, 1 => 2)), - (Vect[U1Irrep](0 => 2, 1 => 1, -1 => 1), - Vect[U1Irrep](0 => 2, 1 => 1, -1 => 1), - Vect[U1Irrep](0 => 2, 1 => 2, -1 => 1)', - Vect[U1Irrep](0 => 1, 1 => 1, -1 => 2), - Vect[U1Irrep](0 => 1, 1 => 2, -1 => 1)'), - (Vect[SU2Irrep](0 => 2, 1 // 2 => 1), - Vect[SU2Irrep](0 => 1, 1 => 1), - Vect[SU2Irrep](1 // 2 => 1, 1 => 1)', - Vect[SU2Irrep](1 // 2 => 2), - Vect[SU2Irrep](0 => 1, 1 // 2 => 1, 3 // 2 => 1)'), - (Vect[FibonacciAnyon](:I => 1, :τ => 1), - Vect[FibonacciAnyon](:I => 1, :τ => 2)', - Vect[FibonacciAnyon](:I => 2, :τ => 2)', - Vect[FibonacciAnyon](:I => 2, :τ => 3), - Vect[FibonacciAnyon](:I => 2, :τ => 2))) +spacelist = ( + (ℂ^2, (ℂ^3)', ℂ^3, ℂ^2, (ℂ^2)'), + ( + Vect[Z2Irrep](0 => 1, 1 => 1), + Vect[Z2Irrep](0 => 1, 1 => 2)', + Vect[Z2Irrep](0 => 2, 1 => 2)', + Vect[Z2Irrep](0 => 2, 1 => 3), + Vect[Z2Irrep](0 => 2, 1 => 2), + ), + ( + Vect[FermionParity](0 => 1, 1 => 1), + Vect[FermionParity](0 => 1, 1 => 2)', + Vect[FermionParity](0 => 2, 1 => 1)', + Vect[FermionParity](0 => 2, 1 => 3), + Vect[FermionParity](0 => 2, 1 => 2), + ), + ( + Vect[U1Irrep](0 => 2, 1 => 1, -1 => 1), + Vect[U1Irrep](0 => 2, 1 => 1, -1 => 1), + Vect[U1Irrep](0 => 2, 1 => 2, -1 => 1)', + Vect[U1Irrep](0 => 1, 1 => 1, -1 => 2), + Vect[U1Irrep](0 => 1, 1 => 2, -1 => 1)', + ), + ( + Vect[SU2Irrep](0 => 2, 1 // 2 => 1), + Vect[SU2Irrep](0 => 1, 1 => 1), + Vect[SU2Irrep](1 // 2 => 1, 1 => 1)', + Vect[SU2Irrep](1 // 2 => 2), + Vect[SU2Irrep](0 => 1, 1 // 2 => 1, 3 // 2 => 1)', + ), + ( + Vect[FibonacciAnyon](:I => 1, :τ => 1), + Vect[FibonacciAnyon](:I => 1, :τ => 2)', + Vect[FibonacciAnyon](:I => 2, :τ => 2)', + Vect[FibonacciAnyon](:I => 2, :τ => 3), + Vect[FibonacciAnyon](:I => 2, :τ => 2), + ), +) for V in spacelist I = sectortype(eltype(V)) @@ -198,12 +218,13 @@ for V in spacelist test_rrule(copy, T2) test_rrule(TensorKit.copy_oftype, T1, ComplexF64) if symmetricbraiding - test_rrule(TensorKit.permutedcopy_oftype, T1, ComplexF64, - ((3, 1), (2, 4))) + test_rrule(TensorKit.permutedcopy_oftype, T1, ComplexF64, ((3, 1), (2, 4))) test_rrule(convert, Array, T1) - test_rrule(TensorMap, convert(Array, T1), codomain(T1), domain(T1); - fkwargs=(; tol=Inf)) + test_rrule( + TensorMap, convert(Array, T1), codomain(T1), domain(T1); + fkwargs = (; tol = Inf) + ) end test_rrule(Base.getproperty, T1, :data) @@ -284,7 +305,7 @@ for V in spacelist for i in 1:3 E = randn(T, ⊗(V[1:i]...) ← ⊗(V[1:i]...)) test_rrule(LinearAlgebra.tr, E) - test_rrule(exp, E; check_inferred=false) + test_rrule(exp, E; check_inferred = false) test_rrule(inv, E) end @@ -318,120 +339,118 @@ for V in spacelist randn!(d3.data) end - test_rrule(f, t1; rrule_f=Zygote.rrule_via_ad, check_inferred) - test_rrule(f, t2; rrule_f=Zygote.rrule_via_ad, check_inferred) - test_rrule(f, d ⊢ d2; check_inferred, output_tangent=d3) + test_rrule(f, t1; rrule_f = Zygote.rrule_via_ad, check_inferred) + test_rrule(f, t2; rrule_f = Zygote.rrule_via_ad, check_inferred) + test_rrule(f, d ⊢ d2; check_inferred, output_tangent = d3) end end symmetricbraiding && @timedtestset "TensorOperations with scalartype $T" for T in eltypes - atol = precision(T) - rtol = precision(T) - - @timedtestset "tensortrace!" begin - for _ in 1:5 - k1 = rand(0:2) - k2 = rand(1:2) - V1 = map(v -> rand(Bool) ? v' : v, rand(V, k1)) - V2 = map(v -> rand(Bool) ? v' : v, rand(V, k2)) - - (_p, _q) = randindextuple(k1 + 2 * k2, k1) - p = _repartition(_p, rand(0:k1)) - q = _repartition(_q, k2) - ip = _repartition(invperm(linearize((_p, _q))), - rand(0:(k1 + 2 * k2))) - A = randn(T, permute(prod(V1) ⊗ prod(V2) ← prod(V2), ip)) - - α = randn(T) - β = randn(T) - for conjA in (false, true) - C = randn!(TensorOperations.tensoralloc_add(T, A, p, conjA, - Val(false))) - test_rrule(tensortrace!, C, A, p, q, conjA, α, β; atol, rtol) - end - end - end + atol = precision(T) + rtol = precision(T) + + @timedtestset "tensortrace!" begin + for _ in 1:5 + k1 = rand(0:2) + k2 = rand(1:2) + V1 = map(v -> rand(Bool) ? v' : v, rand(V, k1)) + V2 = map(v -> rand(Bool) ? v' : v, rand(V, k2)) + + (_p, _q) = randindextuple(k1 + 2 * k2, k1) + p = _repartition(_p, rand(0:k1)) + q = _repartition(_q, k2) + ip = _repartition(invperm(linearize((_p, _q))), rand(0:(k1 + 2 * k2))) + A = randn(T, permute(prod(V1) ⊗ prod(V2) ← prod(V2), ip)) - @timedtestset "tensoradd!" begin - A = randn(T, V[1] ⊗ V[2] ← V[4] ⊗ V[5]) α = randn(T) β = randn(T) + for conjA in (false, true) + C = randn!(TensorOperations.tensoralloc_add(T, A, p, conjA, Val(false))) + test_rrule(tensortrace!, C, A, p, q, conjA, α, β; atol, rtol) + end + end + end - # repeat a couple times to get some distribution of arrows - for _ in 1:5 - p = randindextuple(numind(A)) + @timedtestset "tensoradd!" begin + A = randn(T, V[1] ⊗ V[2] ← V[4] ⊗ V[5]) + α = randn(T) + β = randn(T) - C1 = randn!(TensorOperations.tensoralloc_add(T, A, p, false, - Val(false))) - test_rrule(tensoradd!, C1, A, p, false, α, β; atol, rtol) + # repeat a couple times to get some distribution of arrows + for _ in 1:5 + p = randindextuple(numind(A)) - C2 = randn!(TensorOperations.tensoralloc_add(T, A, p, true, - Val(false))) - test_rrule(tensoradd!, C2, A, p, true, α, β; atol, rtol) + C1 = randn!(TensorOperations.tensoralloc_add(T, A, p, false, Val(false))) + test_rrule(tensoradd!, C1, A, p, false, α, β; atol, rtol) - A = rand(Bool) ? C1 : C2 - end + C2 = randn!(TensorOperations.tensoralloc_add(T, A, p, true, Val(false))) + test_rrule(tensoradd!, C2, A, p, true, α, β; atol, rtol) + + A = rand(Bool) ? C1 : C2 end + end - @timedtestset "tensorcontract!" begin - for _ in 1:5 - d = 0 - local V1, V2, V3 - # retry a couple times to make sure there are at least some nonzero elements - for _ in 1:10 - k1 = rand(0:3) - k2 = rand(0:2) - k3 = rand(0:2) - V1 = prod(v -> rand(Bool) ? v' : v, rand(V, k1); init=one(V[1])) - V2 = prod(v -> rand(Bool) ? v' : v, rand(V, k2); init=one(V[1])) - V3 = prod(v -> rand(Bool) ? v' : v, rand(V, k3); init=one(V[1])) - d = min(dim(V1 ← V2), dim(V1' ← V2), dim(V2 ← V3), - dim(V2' ← V3)) - d > 0 && break - end - ipA = randindextuple(length(V1) + length(V2)) - pA = _repartition(invperm(linearize(ipA)), length(V1)) - ipB = randindextuple(length(V2) + length(V3)) - pB = _repartition(invperm(linearize(ipB)), length(V2)) - pAB = randindextuple(length(V1) + length(V3)) - - α = randn(T) - β = randn(T) - V2_conj = prod(conj, V2; init=one(V[1])) - - for conjA in (false, true), conjB in (false, true) - A = randn(T, permute(V1 ← (conjA ? V2_conj : V2), ipA)) - B = randn(T, permute((conjB ? V2_conj : V2) ← V3, ipB)) - C = randn!(TensorOperations.tensoralloc_contract(T, A, pA, - conjA, - B, pB, conjB, - pAB, - Val(false))) - test_rrule(tensorcontract!, C, - A, pA, conjA, B, pB, conjB, pAB, - α, β; atol, rtol) - end + @timedtestset "tensorcontract!" begin + for _ in 1:5 + d = 0 + local V1, V2, V3 + # retry a couple times to make sure there are at least some nonzero elements + for _ in 1:10 + k1 = rand(0:3) + k2 = rand(0:2) + k3 = rand(0:2) + V1 = prod(v -> rand(Bool) ? v' : v, rand(V, k1); init = one(V[1])) + V2 = prod(v -> rand(Bool) ? v' : v, rand(V, k2); init = one(V[1])) + V3 = prod(v -> rand(Bool) ? v' : v, rand(V, k3); init = one(V[1])) + d = min(dim(V1 ← V2), dim(V1' ← V2), dim(V2 ← V3), dim(V2' ← V3)) + d > 0 && break end - end + ipA = randindextuple(length(V1) + length(V2)) + pA = _repartition(invperm(linearize(ipA)), length(V1)) + ipB = randindextuple(length(V2) + length(V3)) + pB = _repartition(invperm(linearize(ipB)), length(V2)) + pAB = randindextuple(length(V1) + length(V3)) - @timedtestset "tensorscalar" begin - A = randn(T, ProductSpace{typeof(V[1]),0}()) - test_rrule(tensorscalar, A) + α = randn(T) + β = randn(T) + V2_conj = prod(conj, V2; init = one(V[1])) + + for conjA in (false, true), conjB in (false, true) + A = randn(T, permute(V1 ← (conjA ? V2_conj : V2), ipA)) + B = randn(T, permute((conjB ? V2_conj : V2) ← V3, ipB)) + C = randn!( + TensorOperations.tensoralloc_contract( + T, A, pA, conjA, B, pB, conjB, pAB, Val(false) + ) + ) + test_rrule( + tensorcontract!, C, A, pA, conjA, B, pB, conjB, pAB, α, β; + atol, rtol + ) + end end end + @timedtestset "tensorscalar" begin + A = randn(T, ProductSpace{typeof(V[1]), 0}()) + test_rrule(tensorscalar, A) + end + end + @timedtestset "Factorizations" begin W = V[1] ⊗ V[2] @testset "QR" begin for T in eltypes, - t in (randn(T, W, W), randn(T, W, W)', - randn(T, W, V[1]), randn(T, V[1], W), - randn(T, W, V[1])', randn(T, V[1], W)', - DiagonalTensorMap(randn(T, reduceddim(V[1])), V[1])) + t in ( + randn(T, W, W), randn(T, W, W)', + randn(T, W, V[1]), randn(T, V[1], W), + randn(T, W, V[1])', randn(T, V[1], W)', + DiagonalTensorMap(randn(T, reduceddim(V[1])), V[1]), + ) atol = rtol = precision(T) * dim(space(t)) - fkwargs = (; positive=true) # make FiniteDifferences happy + fkwargs = (; positive = true) # make FiniteDifferences happy test_ad_rrule(qr_compact, t; fkwargs, atol, rtol) test_ad_rrule(first ∘ qr_compact, t; fkwargs, atol, rtol) @@ -449,10 +468,12 @@ for V in spacelist remove_qrgauge_dependence!(ΔQ, t, Q) - test_ad_rrule(qr_full, t; fkwargs, atol, rtol, output_tangent=(ΔQ, ΔR)) - test_ad_rrule(first ∘ qr_full, t; fkwargs, atol, rtol, - output_tangent=ΔQ) - test_ad_rrule(last ∘ qr_full, t; fkwargs, atol, rtol, output_tangent=ΔR) + test_ad_rrule(qr_full, t; fkwargs, atol, rtol, output_tangent = (ΔQ, ΔR)) + test_ad_rrule( + first ∘ qr_full, t; + fkwargs, atol, rtol, output_tangent = ΔQ + ) + test_ad_rrule(last ∘ qr_full, t; fkwargs, atol, rtol, output_tangent = ΔR) # TODO: figure out the following: # N = qr_null(t) @@ -468,13 +489,15 @@ for V in spacelist @testset "LQ" begin for T in eltypes, - t in (randn(T, W, W), randn(T, W, W)', - randn(T, W, V[1]), randn(T, V[1], W), - randn(T, W, V[1])', randn(T, V[1], W)', - DiagonalTensorMap(randn(T, reduceddim(V[1])), V[1])) + t in ( + randn(T, W, W), randn(T, W, W)', + randn(T, W, V[1]), randn(T, V[1], W), + randn(T, W, V[1])', randn(T, V[1], W)', + DiagonalTensorMap(randn(T, reduceddim(V[1])), V[1]), + ) atol = rtol = precision(T) * dim(space(t)) - fkwargs = (; positive=true) # make FiniteDifferences happy + fkwargs = (; positive = true) # make FiniteDifferences happy test_ad_rrule(lq_compact, t; fkwargs, atol, rtol) test_ad_rrule(first ∘ lq_compact, t; fkwargs, atol, rtol) @@ -493,10 +516,12 @@ for V in spacelist remove_lqgauge_dependence!(ΔQ, t, Q) - test_ad_rrule(lq_full, t; fkwargs, atol, rtol, output_tangent=(ΔL, ΔQ)) - test_ad_rrule(first ∘ lq_full, t; fkwargs, atol, rtol, - output_tangent=ΔL) - test_ad_rrule(last ∘ lq_full, t; fkwargs, atol, rtol, output_tangent=ΔQ) + test_ad_rrule(lq_full, t; fkwargs, atol, rtol, output_tangent = (ΔL, ΔQ)) + test_ad_rrule( + first ∘ lq_full, t; + fkwargs, atol, rtol, output_tangent = ΔL + ) + test_ad_rrule(last ∘ lq_full, t; fkwargs, atol, rtol, output_tangent = ΔQ) # TODO: figure out the following # Nᴴ = lq_null(t) @@ -513,8 +538,10 @@ for V in spacelist @testset "Eigenvalue decomposition" begin for T in eltypes, - t in (rand(T, V[1], V[1]), rand(T, W, W), rand(T, W, W)', - DiagonalTensorMap(rand(T, reduceddim(V[1])), V[1])) + t in ( + rand(T, V[1], V[1]), rand(T, W, W), rand(T, W, W)', + DiagonalTensorMap(rand(T, reduceddim(V[1])), V[1]), + ) atol = rtol = precision(T) * dim(space(t)) @@ -524,10 +551,10 @@ for V in spacelist Δd2 = randn!(similar(d, space(d))) remove_eiggauge_dependence!(Δv, d, v) - test_ad_rrule(eig_full, t; output_tangent=(Δd, Δv), atol, rtol) - test_ad_rrule(first ∘ eig_full, t; output_tangent=Δd, atol, rtol) - test_ad_rrule(last ∘ eig_full, t; output_tangent=Δv, atol, rtol) - test_ad_rrule(eig_full, t; output_tangent=(Δd2, Δv), atol, rtol) + test_ad_rrule(eig_full, t; output_tangent = (Δd, Δv), atol, rtol) + test_ad_rrule(first ∘ eig_full, t; output_tangent = Δd, atol, rtol) + test_ad_rrule(last ∘ eig_full, t; output_tangent = Δv, atol, rtol) + test_ad_rrule(eig_full, t; output_tangent = (Δd2, Δv), atol, rtol) t += t' d, v = eigh_full(t) @@ -539,16 +566,16 @@ for V in spacelist # necessary for FiniteDifferences to not complain eigh_full′ = eigh_full ∘ project_hermitian - test_ad_rrule(eigh_full′, t; output_tangent=(Δd, Δv), atol, rtol) - test_ad_rrule(first ∘ eigh_full′, t; output_tangent=Δd, atol, rtol) - test_ad_rrule(last ∘ eigh_full′, t; output_tangent=Δv, atol, rtol) - test_ad_rrule(eigh_full′, t; output_tangent=(Δd2, Δv), atol, rtol) + test_ad_rrule(eigh_full′, t; output_tangent = (Δd, Δv), atol, rtol) + test_ad_rrule(first ∘ eigh_full′, t; output_tangent = Δd, atol, rtol) + test_ad_rrule(last ∘ eigh_full′, t; output_tangent = Δv, atol, rtol) + test_ad_rrule(eigh_full′, t; output_tangent = (Δd2, Δv), atol, rtol) end end @testset "Singular value decomposition" begin for T in eltypes, - t in (randn(T, V[1], V[1]), randn(T, W, W), randn(T, W, W)) + t in (randn(T, V[1], V[1]), randn(T, W, W), randn(T, W, W)) # TODO: fix diagonaltensormap case # DiagonalTensorMap(rand(T, reduceddim(V1)), V1)) @@ -558,10 +585,10 @@ for V in spacelist ΔS2 = randn!(similar(ΔS, space(ΔS))) ΔU, ΔVᴴ = remove_svdgauge_dependence!(ΔU, ΔVᴴ, USVᴴ...; degeneracy_atol) - test_ad_rrule(svd_full, t; output_tangent=(ΔU, ΔS, ΔVᴴ), atol, rtol) - test_ad_rrule(svd_full, t; output_tangent=(ΔU, ΔS2, ΔVᴴ), atol, rtol) - test_ad_rrule(svd_compact, t; output_tangent=(ΔU, ΔS, ΔVᴴ), atol, rtol) - test_ad_rrule(svd_compact, t; output_tangent=(ΔU, ΔS2, ΔVᴴ), atol, rtol) + test_ad_rrule(svd_full, t; output_tangent = (ΔU, ΔS, ΔVᴴ), atol, rtol) + test_ad_rrule(svd_full, t; output_tangent = (ΔU, ΔS2, ΔVᴴ), atol, rtol) + test_ad_rrule(svd_compact, t; output_tangent = (ΔU, ΔS, ΔVᴴ), atol, rtol) + test_ad_rrule(svd_compact, t; output_tangent = (ΔU, ΔS2, ΔVᴴ), atol, rtol) # TODO: I'm not sure how to properly test with spaces that might change # with the finite-difference methods, as then the jacobian is ill-defined. @@ -569,20 +596,22 @@ for V in spacelist trunc = truncrank(round(Int, min(dim(domain(t)), dim(codomain(t))) ÷ 2)) USVᴴ_trunc = svd_trunc(t; trunc) ΔUSVᴴ_trunc = rand_tangent.(USVᴴ_trunc) - remove_svdgauge_dependence!(ΔUSVᴴ_trunc[1], ΔUSVᴴ_trunc[3], - USVᴴ_trunc...; - degeneracy_atol) + remove_svdgauge_dependence!( + ΔUSVᴴ_trunc[1], ΔUSVᴴ_trunc[3], USVᴴ_trunc...; degeneracy_atol + ) # test_ad_rrule(svd_trunc, t; # fkwargs=(; trunc), output_tangent=ΔUSVᴴ_trunc, atol, rtol) trunc = truncspace(space(USVᴴ_trunc[2], 1)) USVᴴ_trunc = svd_trunc(t; trunc) ΔUSVᴴ_trunc = rand_tangent.(USVᴴ_trunc) - remove_svdgauge_dependence!(ΔUSVᴴ_trunc[1], ΔUSVᴴ_trunc[3], - USVᴴ_trunc...; - degeneracy_atol) - test_ad_rrule(svd_trunc, t; - fkwargs=(; trunc), output_tangent=ΔUSVᴴ_trunc, atol, rtol) + remove_svdgauge_dependence!( + ΔUSVᴴ_trunc[1], ΔUSVᴴ_trunc[3], USVᴴ_trunc...; degeneracy_atol + ) + test_ad_rrule( + svd_trunc, t; + fkwargs = (; trunc), output_tangent = ΔUSVᴴ_trunc, atol, rtol + ) # ϵ = norm(*(USVᴴ_trunc...) - t) # trunc = truncerror(; atol=ϵ) @@ -594,14 +623,16 @@ for V in spacelist # fkwargs=(; trunc), output_tangent=ΔUSVᴴ_trunc, atol, rtol) tol = minimum(((c, b),) -> minimum(diagview(b)), blocks(USVᴴ_trunc[2])) - trunc = trunctol(; atol=10 * tol) + trunc = trunctol(; atol = 10 * tol) USVᴴ_trunc = svd_trunc(t; trunc) ΔUSVᴴ_trunc = rand_tangent.(USVᴴ_trunc) - remove_svdgauge_dependence!(ΔUSVᴴ_trunc[1], ΔUSVᴴ_trunc[3], - USVᴴ_trunc...; - degeneracy_atol) - test_ad_rrule(svd_trunc, t; - fkwargs=(; trunc), output_tangent=ΔUSVᴴ_trunc, atol, rtol) + remove_svdgauge_dependence!( + ΔUSVᴴ_trunc[1], ΔUSVᴴ_trunc[3], USVᴴ_trunc...; degeneracy_atol + ) + test_ad_rrule( + svd_trunc, t; + fkwargs = (; trunc), output_tangent = ΔUSVᴴ_trunc, atol, rtol + ) end end diff --git a/test/bugfixes.jl b/test/bugfixes.jl index 3b93f998a..b65ae6159 100644 --- a/test/bugfixes.jl +++ b/test/bugfixes.jl @@ -1,23 +1,19 @@ @timedtestset "Bugfixes" verbose = true begin @testset "BugfixConvert" begin - v = randn(ComplexF64, - (Vect[(Irrep[U₁] ⊠ Irrep[SU₂] ⊠ FermionParity)]((-3, 1 / 2, 1) => 3, - (-5, 1 / 2, 1) => 10, - (-7, 1 / 2, 1) => 13, - (-9, 1 / 2, 1) => 9, - (-11, 1 / 2, 1) => 1, - (-5, 3 / 2, 1) => 3, - (-7, 3 / 2, 1) => 3, - (-9, 3 / 2, 1) => 1) ⊗ - Vect[(Irrep[U₁] ⊠ Irrep[SU₂] ⊠ FermionParity)]((1, 1 / 2, 1) => 1)') ← - Vect[(Irrep[U₁] ⊠ Irrep[SU₂] ⊠ FermionParity)]((-3, 1 / 2, 1) => 3, - (-5, 1 / 2, 1) => 10, - (-7, 1 / 2, 1) => 13, - (-9, 1 / 2, 1) => 9, - (-11, 1 / 2, 1) => 1, - (-5, 3 / 2, 1) => 3, - (-7, 3 / 2, 1) => 3, - (-9, 3 / 2, 1) => 1)) + v = randn( + ComplexF64, + ( + Vect[(Irrep[U₁] ⊠ Irrep[SU₂] ⊠ FermionParity)]( + (-3, 1 / 2, 1) => 3, (-5, 1 / 2, 1) => 10, (-7, 1 / 2, 1) => 13, + (-9, 1 / 2, 1) => 9, (-11, 1 / 2, 1) => 1, (-5, 3 / 2, 1) => 3, + (-7, 3 / 2, 1) => 3, (-9, 3 / 2, 1) => 1 + ) ⊗ Vect[(Irrep[U₁] ⊠ Irrep[SU₂] ⊠ FermionParity)]((1, 1 / 2, 1) => 1)' + ) ← Vect[(Irrep[U₁] ⊠ Irrep[SU₂] ⊠ FermionParity)]( + (-3, 1 / 2, 1) => 3, (-5, 1 / 2, 1) => 10, (-7, 1 / 2, 1) => 13, + (-9, 1 / 2, 1) => 9, (-11, 1 / 2, 1) => 1, (-5, 3 / 2, 1) => 3, + (-7, 3 / 2, 1) => 3, (-9, 3 / 2, 1) => 1 + ) + ) w = convert(typeof(real(v)), v) @test w == v @test scalartype(w) == Float64 @@ -33,8 +29,7 @@ # https://github.com/quantumkithub/TensorKit.jl/issues/194 @testset "Issue #194" begin t1 = rand(ℂ^4 ← ℂ^4) - t2 = tensoralloc(typeof(t1), space(t1), Val(true), - TensorOperations.ManualAllocator()) + t2 = tensoralloc(typeof(t1), space(t1), Val(true), TensorOperations.ManualAllocator()) t3 = similar(t2, ComplexF64, space(t1)) @test storagetype(t3) == Vector{ComplexF64} t4 = similar(t2, domain(t1)) diff --git a/test/diagonal.jl b/test/diagonal.jl index 8d20c6ca0..74214949b 100644 --- a/test/diagonal.jl +++ b/test/diagonal.jl @@ -1,6 +1,8 @@ -diagspacelist = ((ℂ^4)', ℂ[Z2Irrep](0 => 2, 1 => 3), - ℂ[FermionNumber](0 => 2, 1 => 2, -1 => 1), - ℂ[SU2Irrep](0 => 2, 1 => 1)', ℂ[FibonacciAnyon](:I => 2, :τ => 2)) +diagspacelist = ( + (ℂ^4)', Vect[Z2Irrep](0 => 2, 1 => 3), + Vect[FermionNumber](0 => 2, 1 => 2, -1 => 1), + Vect[SU2Irrep](0 => 2, 1 => 1)', Vect[FibonacciAnyon](:I => 2, :τ => 2), +) @testset "DiagonalTensor with domain $V" for V in diagspacelist @timedtestset "Basic properties and algebra" begin @@ -29,7 +31,7 @@ diagspacelist = ((ℂ^4)', ℂ[Z2Irrep](0 => 2, 1 => 3), next = @constinferred Nothing iterate(bs, state) b2 = @constinferred block(t, first(blocksectors(t))) @test b1 == b2 - @test eltype(bs) === Pair{typeof(c),typeof(b1)} + @test eltype(bs) === Pair{typeof(c), typeof(b1)} @test typeof(b1) === TensorKit.blocktype(t) # basic linear algebra @test isa(@constinferred(norm(t)), real(T)) @@ -73,8 +75,7 @@ diagspacelist = ((ℂ^4)', ℂ[Z2Irrep](0 => 2, 1 => 3), α = rand(T) @test convert(TensorMap, α * t1) ≈ α * convert(TensorMap, t1) @test convert(TensorMap, t1') ≈ convert(TensorMap, t1)' - @test convert(TensorMap, t1 + t2) ≈ - convert(TensorMap, t1) + convert(TensorMap, t2) + @test convert(TensorMap, t1 + t2) ≈ convert(TensorMap, t1) + convert(TensorMap, t2) end end @timedtestset "Real and imaginary parts" begin @@ -138,8 +139,9 @@ diagspacelist = ((ℂ^4)', ℂ[Z2Irrep](0 => 2, 1 => 3), @test TensorMap(@constinferred t1 / t2) ≈ TensorMap(t1) / TensorMap(t2) @test TensorMap(@constinferred inv(t1)) ≈ inv(TensorMap(t1)) @test TensorMap(@constinferred pinv(t1)) ≈ pinv(TensorMap(t1)) - @test all(Base.Fix2(isa, DiagonalTensorMap), - (t1 * t2, t1 \ t2, t1 / t2, inv(t1), pinv(t1))) + @test all( + Base.Fix2(isa, DiagonalTensorMap), (t1 * t2, t1 \ t2, t1 / t2, inv(t1), pinv(t1)) + ) u = randn(Float64, V * V' * V, V) @test u * t1 ≈ u * TensorMap(t1) diff --git a/test/factorizations.jl b/test/factorizations.jl index 4022ff3e6..15e4845d0 100644 --- a/test/factorizations.jl +++ b/test/factorizations.jl @@ -29,8 +29,10 @@ for V in spacelist @testset "QR decomposition" begin for T in eltypes, - t in (rand(T, W, W), rand(T, W, W)', rand(T, W, V1), rand(T, V1, W)', - DiagonalTensorMap(rand(T, reduceddim(V1)), V1)) + t in ( + rand(T, W, W), rand(T, W, W)', rand(T, W, V1), rand(T, V1, W)', + DiagonalTensorMap(rand(T, reduceddim(V1)), V1), + ) Q, R = @constinferred qr_full(t) @test Q * R ≈ t @@ -40,7 +42,7 @@ for V in spacelist @test Q * R ≈ t @test isisometry(Q) - Q, R = @constinferred left_orth(t; kind=:qr) + Q, R = @constinferred left_orth(t; kind = :qr) @test Q * R ≈ t @test isisometry(Q) @@ -48,7 +50,7 @@ for V in spacelist @test isisometry(N) @test norm(N' * t) ≈ 0 atol = 100 * eps(norm(t)) - N = @constinferred left_null(t; kind=:qr) + N = @constinferred left_null(t; kind = :qr) @test isisometry(N) @test norm(N' * t) ≈ 0 atol = 100 * eps(norm(t)) end @@ -67,7 +69,7 @@ for V in spacelist @test isisometry(Q) @test dim(Q) == dim(R) == dim(t) - Q, R = @constinferred left_orth(t; kind=:qr) + Q, R = @constinferred left_orth(t; kind = :qr) @test Q * R ≈ t @test isisometry(Q) @test dim(Q) == dim(R) == dim(t) @@ -80,8 +82,10 @@ for V in spacelist @testset "LQ decomposition" begin for T in eltypes, - t in (rand(T, W, W), rand(T, W, W)', rand(T, W, V1), rand(T, V1, W)', - DiagonalTensorMap(rand(T, reduceddim(V1)), V1)) + t in ( + rand(T, W, W), rand(T, W, W)', rand(T, W, V1), rand(T, V1, W)', + DiagonalTensorMap(rand(T, reduceddim(V1)), V1), + ) L, Q = @constinferred lq_full(t) @test L * Q ≈ t @@ -89,14 +93,14 @@ for V in spacelist L, Q = @constinferred lq_compact(t) @test L * Q ≈ t - @test isisometry(Q; side=:right) + @test isisometry(Q; side = :right) - L, Q = @constinferred right_orth(t; kind=:lq) + L, Q = @constinferred right_orth(t; kind = :lq) @test L * Q ≈ t - @test isisometry(Q; side=:right) + @test isisometry(Q; side = :right) Nᴴ = @constinferred lq_null(t) - @test isisometry(Nᴴ; side=:right) + @test isisometry(Nᴴ; side = :right) @test norm(t * Nᴴ') ≈ 0 atol = 100 * eps(norm(t)) end @@ -111,12 +115,12 @@ for V in spacelist L, Q = @constinferred lq_compact(t) @test L * Q ≈ t - @test isisometry(Q; side=:right) + @test isisometry(Q; side = :right) @test dim(Q) == dim(L) == dim(t) - L, Q = @constinferred right_orth(t; kind=:lq) + L, Q = @constinferred right_orth(t; kind = :lq) @test L * Q ≈ t - @test isisometry(Q; side=:right) + @test isisometry(Q; side = :right) @test dim(Q) == dim(L) == dim(t) Nᴴ = @constinferred lq_null(t) @@ -127,8 +131,10 @@ for V in spacelist @testset "Polar decomposition" begin for T in eltypes, - t in (rand(T, W, W), rand(T, W, W)', rand(T, W, V1), rand(T, V1, W)', - DiagonalTensorMap(rand(T, reduceddim(V1)), V1)) + t in ( + rand(T, W, W), rand(T, W, W)', rand(T, W, V1), rand(T, V1, W)', + DiagonalTensorMap(rand(T, reduceddim(V1)), V1), + ) @assert domain(t) ≾ codomain(t) w, p = @constinferred left_polar(t) @@ -136,32 +142,34 @@ for V in spacelist @test isisometry(w) @test isposdef(p) - w, p = @constinferred left_orth(t; kind=:polar) + w, p = @constinferred left_orth(t; kind = :polar) @test w * p ≈ t @test isisometry(w) end for T in eltypes, - t in (rand(T, W, W), rand(T, W, W)', rand(T, V1, W), rand(T, W, V1)') + t in (rand(T, W, W), rand(T, W, W)', rand(T, V1, W), rand(T, W, V1)') @assert codomain(t) ≾ domain(t) p, wᴴ = @constinferred right_polar(t) @test p * wᴴ ≈ t - @test isisometry(wᴴ; side=:right) + @test isisometry(wᴴ; side = :right) @test isposdef(p) - p, wᴴ = @constinferred right_orth(t; kind=:polar) + p, wᴴ = @constinferred right_orth(t; kind = :polar) @test p * wᴴ ≈ t - @test isisometry(wᴴ; side=:right) + @test isisometry(wᴴ; side = :right) end end @testset "SVD" begin for T in eltypes, - t in (rand(T, W, W), rand(T, W, W)', - rand(T, W, V1), rand(T, V1, W), - rand(T, W, V1)', rand(T, V1, W)', - DiagonalTensorMap(rand(T, reduceddim(V1)), V1)) + t in ( + rand(T, W, W), rand(T, W, W)', + rand(T, W, V1), rand(T, V1, W), + rand(T, W, V1)', rand(T, V1, W)', + DiagonalTensorMap(rand(T, reduceddim(V1)), V1), + ) u, s, vᴴ = @constinferred svd_full(t) @test u * s * vᴴ ≈ t @@ -172,23 +180,23 @@ for V in spacelist @test u * s * vᴴ ≈ t @test isisometry(u) @test isposdef(s) - @test isisometry(vᴴ; side=:right) + @test isisometry(vᴴ; side = :right) s′ = LinearAlgebra.diag(s) for (c, b) in LinearAlgebra.svdvals(t) @test b ≈ s′[c] end - v, c = @constinferred left_orth(t; kind=:svd) + v, c = @constinferred left_orth(t; kind = :svd) @test v * c ≈ t @test isisometry(v) - N = @constinferred left_null(t; kind=:svd) + N = @constinferred left_null(t; kind = :svd) @test isisometry(N) @test norm(N' * t) ≈ 0 atol = 100 * eps(norm(t)) - Nᴴ = @constinferred right_null(t; kind=:svd) - @test isisometry(Nᴴ; side=:right) + Nᴴ = @constinferred right_null(t; kind = :svd) + @test isisometry(Nᴴ; side = :right) @test norm(t * Nᴴ') ≈ 0 atol = 100 * eps(norm(t)) end @@ -207,31 +215,33 @@ for V in spacelist @testset "truncated SVD" begin for T in eltypes, - t in (randn(T, W, W), randn(T, W, W)', - randn(T, W, V1), randn(T, V1, W), - randn(T, W, V1)', randn(T, V1, W)', - DiagonalTensorMap(randn(T, reduceddim(V1)), V1)) + t in ( + randn(T, W, W), randn(T, W, W)', + randn(T, W, V1), randn(T, V1, W), + randn(T, W, V1)', randn(T, V1, W)', + DiagonalTensorMap(randn(T, reduceddim(V1)), V1), + ) @constinferred normalize!(t) - U, S, Vᴴ = @constinferred svd_trunc(t; trunc=notrunc()) + U, S, Vᴴ = @constinferred svd_trunc(t; trunc = notrunc()) @test U * S * Vᴴ ≈ t @test isisometry(U) - @test isisometry(Vᴴ; side=:right) + @test isisometry(Vᴴ; side = :right) trunc = truncrank(dim(domain(S)) ÷ 2) U1, S1, Vᴴ1 = @constinferred svd_trunc(t; trunc) @test t * Vᴴ1' ≈ U1 * S1 @test isisometry(U1) - @test isisometry(Vᴴ1; side=:right) + @test isisometry(Vᴴ1; side = :right) @test dim(domain(S1)) <= trunc.howmany λ = minimum(minimum, values(LinearAlgebra.diag(S1))) - trunc = trunctol(; atol=λ - 10eps(λ)) + trunc = trunctol(; atol = λ - 10eps(λ)) U2, S2, Vᴴ2 = @constinferred svd_trunc(t; trunc) @test t * Vᴴ2' ≈ U2 * S2 @test isisometry(U2) - @test isisometry(Vᴴ2; side=:right) + @test isisometry(Vᴴ2; side = :right) @test minimum(minimum, values(LinearAlgebra.diag(S1))) >= λ @test U2 ≈ U1 @test S2 ≈ S1 @@ -241,29 +251,31 @@ for V in spacelist U3, S3, Vᴴ3 = @constinferred svd_trunc(t; trunc) @test t * Vᴴ3' ≈ U3 * S3 @test isisometry(U3) - @test isisometry(Vᴴ3; side=:right) + @test isisometry(Vᴴ3; side = :right) @test space(S3, 1) ≾ space(S2, 1) - trunc = truncerror(; atol=0.5) + trunc = truncerror(; atol = 0.5) U4, S4, Vᴴ4 = @constinferred svd_trunc(t; trunc) @test t * Vᴴ4' ≈ U4 * S4 @test isisometry(U4) - @test isisometry(Vᴴ4; side=:right) + @test isisometry(Vᴴ4; side = :right) @test norm(t - U4 * S4 * Vᴴ4) <= 0.5 end end @testset "Eigenvalue decomposition" begin for T in eltypes, - t in (rand(T, V1, V1), rand(T, W, W), rand(T, W, W)', - DiagonalTensorMap(rand(T, reduceddim(V1)), V1)) + t in ( + rand(T, V1, V1), rand(T, W, W), rand(T, W, W)', + DiagonalTensorMap(rand(T, reduceddim(V1)), V1), + ) d, v = @constinferred eig_full(t) @test t * v ≈ v * d d′ = LinearAlgebra.diag(d) for (c, b) in LinearAlgebra.eigvals(t) - @test sort(b; by=abs) ≈ sort(d′[c]; by=abs) + @test sort(b; by = abs) ≈ sort(d′[c]; by = abs) end vdv = v' * v @@ -271,7 +283,7 @@ for V in spacelist @test @constinferred isposdef(vdv) t isa DiagonalTensorMap || @test !isposdef(t) # unlikely for non-hermitian map - d, v = @constinferred eig_trunc(t; trunc=truncrank(dim(domain(t)) ÷ 2)) + d, v = @constinferred eig_trunc(t; trunc = truncrank(dim(domain(t)) ÷ 2)) @test t * v ≈ v * d @test dim(domain(d)) ≤ dim(domain(t)) ÷ 2 @@ -281,8 +293,10 @@ for V in spacelist D̃, Ṽ = @constinferred eigh_full(t2) @test D ≈ D̃ @test V ≈ Ṽ - λ = minimum(minimum(real(LinearAlgebra.diag(b))) - for (c, b) in blocks(D)) + λ = minimum( + minimum(real(LinearAlgebra.diag(b))) + for (c, b) in blocks(D) + ) @test cond(Ṽ) ≈ one(real(T)) @test isposdef(t2) == isposdef(λ) @test isposdef(t2 - λ * one(t2) + 0.1 * one(t2)) @@ -300,7 +314,7 @@ for V in spacelist @test isposdef(t - λ * one(t) + 0.1 * one(t)) @test !isposdef(t - λ * one(t) - 0.1 * one(t)) - d, v = @constinferred eigh_trunc(t; trunc=truncrank(dim(domain(t)) ÷ 2)) + d, v = @constinferred eigh_trunc(t; trunc = truncrank(dim(domain(t)) ÷ 2)) @test t * v ≈ v * d @test dim(domain(d)) ≤ dim(domain(t)) ÷ 2 end @@ -308,10 +322,12 @@ for V in spacelist @testset "Condition number and rank" begin for T in eltypes, - t in (rand(T, W, W), rand(T, W, W)', - rand(T, W, V1), rand(T, V1, W), - rand(T, W, V1)', rand(T, V1, W)', - DiagonalTensorMap(rand(T, reduceddim(V1)), V1)) + t in ( + rand(T, W, W), rand(T, W, W)', + rand(T, W, V1), rand(T, V1, W), + rand(T, W, V1)', rand(T, V1, W)', + DiagonalTensorMap(rand(T, reduceddim(V1)), V1), + ) d1, d2 = dim(codomain(t)), dim(domain(t)) @test rank(t) == min(d1, d2) diff --git a/test/fusiontrees.jl b/test/fusiontrees.jl index d758f9a83..0e0920ebd 100644 --- a/test/fusiontrees.jl +++ b/test/fusiontrees.jl @@ -3,7 +3,7 @@ println("Fusion Trees") println("------------------------------------") ti = time() @timedtestset "Fusion trees for $(TensorKit.type_repr(I))" verbose = true for I in - sectorlist + sectorlist Istr = TensorKit.type_repr(I) N = 5 out = ntuple(n -> randsector(I), N) @@ -31,13 +31,16 @@ ti = time() @constinferred FusionTree((u,), u, (false,), (), ()) @constinferred FusionTree((u, u), u, (false, false), (), (1,)) @constinferred FusionTree((u, u, u), u, (false, false, false), (u,), (1, 1)) - @constinferred FusionTree((u, u, u, u), u, (false, false, false, false), (u, u), - (1, 1, 1)) + @constinferred FusionTree( + (u, u, u, u), u, (false, false, false, false), (u, u), (1, 1, 1) + ) @test_throws MethodError FusionTree((u, u, u), u, (false, false), (u,), (1, 1)) - @test_throws MethodError FusionTree((u, u, u), u, (false, false, false), (u, u), - (1, 1)) - @test_throws MethodError FusionTree((u, u, u), u, (false, false, false), (u,), - (1, 1, 1)) + @test_throws MethodError FusionTree( + (u, u, u), u, (false, false, false), (u, u), (1, 1) + ) + @test_throws MethodError FusionTree( + (u, u, u), u, (false, false, false), (u,), (1, 1, 1) + ) @test_throws MethodError FusionTree((u, u, u), u, (false, false, false), (), (1,)) f = FusionTree((u, u, u), u, (false, false, false), (u,), (1, 1)) @@ -101,20 +104,22 @@ ti = time() end for (t, coeff) in trees3 coeff′ = get(trees, t, zero(coeff)) - @test isapprox(coeff′, coeff; atol=1e-12, rtol=1e-12) + @test isapprox(coeff′, coeff; atol = 1.0e-12, rtol = 1.0e-12) end if (BraidingStyle(I) isa Bosonic) && hasfusiontensor(I) Af1 = convert(Array, f1) Af2 = convert(Array, f2) - Af = tensorcontract(1:(2N), Af1, - [1:(i - 1); -1; N - 1 .+ ((i + 1):(N + 1))], - Af2, [i - 1 .+ (1:N); -1]) + Af = tensorcontract( + 1:(2N), Af1, + [1:(i - 1); -1; N - 1 .+ ((i + 1):(N + 1))], + Af2, [i - 1 .+ (1:N); -1] + ) Af′ = zero(Af) for (f, coeff) in trees Af′ .+= coeff .* convert(Array, f) end - @test isapprox(Af, Af′; atol=1e-12, rtol=1e-12) + @test isapprox(Af, Af′; atol = 1.0e-12, rtol = 1.0e-12) end end end @@ -139,7 +144,7 @@ ti = time() for (f′, coeff) in d bf′ .+= coeff .* convert(Array, f′) end - @test bf ≈ bf′ atol = 1e-12 + @test bf ≈ bf′ atol = 1.0e-12 end d2 = @constinferred TK.planar_trace(f, (1, 3), (2, 4)) @@ -149,7 +154,7 @@ ti = time() for (f2′, coeff) in d2 bf2′ .+= coeff .* convert(Array, f2′) end - @test bf2 ≈ bf2′ atol = 1e-12 + @test bf2 ≈ bf2′ atol = 1.0e-12 d2 = @constinferred TK.planar_trace(f, (5, 6), (2, 1)) oind2 = (3, 4, 7) @@ -158,7 +163,7 @@ ti = time() for (f2′, coeff) in d2 bf2′ .+= coeff .* convert(Array, f2′) end - @test bf2 ≈ bf2′ atol = 1e-12 + @test bf2 ≈ bf2′ atol = 1.0e-12 d2 = @constinferred TK.planar_trace(f, (1, 4), (6, 3)) bf2 = tensortrace(af, (:a, :b, :c, :c, :d, :a, :e)) @@ -166,7 +171,7 @@ ti = time() for (f2′, coeff) in d2 bf2′ .+= coeff .* convert(Array, f2′) end - @test bf2 ≈ bf2′ atol = 1e-12 + @test bf2 ≈ bf2′ atol = 1.0e-12 q1 = (1, 3, 5) q2 = (2, 4, 6) @@ -176,7 +181,7 @@ ti = time() for (f3′, coeff) in d3 bf3′ .+= coeff .* convert(Array, f3′) end - @test bf3 ≈ bf3′ atol = 1e-12 + @test bf3 ≈ bf3′ atol = 1.0e-12 q1 = (1, 3, 5) q2 = (6, 2, 4) @@ -186,7 +191,7 @@ ti = time() for (f3′, coeff) in d3 bf3′ .+= coeff .* convert(Array, f3′) end - @test bf3 ≈ bf3′ atol = 1e-12 + @test bf3 ≈ bf3′ atol = 1.0e-12 q1 = (1, 2, 3) q2 = (6, 5, 4) @@ -196,7 +201,7 @@ ti = time() for (f3′, coeff) in d3 bf3′ .+= coeff .* convert(Array, f3′) end - @test bf3 ≈ bf3′ atol = 1e-12 + @test bf3 ≈ bf3′ atol = 1.0e-12 q1 = (1, 2, 4) q2 = (6, 3, 5) @@ -206,7 +211,7 @@ ti = time() for (f3′, coeff) in d3 bf3′ .+= coeff .* convert(Array, f3′) end - @test bf3 ≈ bf3′ atol = 1e-12 + @test bf3 ≈ bf3′ atol = 1.0e-12 end end end @@ -221,7 +226,7 @@ ti = time() @test norm(values(d1)) ≈ 1 d2 = empty(d1) for (f1, coeff1) in d1 - for (f2, coeff2) in TK.artin_braid(f1, i; inv=true) + for (f2, coeff2) in TK.artin_braid(f1, i; inv = true) d2[f2] = get(d2, f2, zero(coeff1)) + coeff2 * coeff1 end end @@ -229,7 +234,7 @@ ti = time() if f2 == f @test coeff2 ≈ 1 else - @test isapprox(coeff2, 0; atol=1e-12, rtol=1e-12) + @test isapprox(coeff2, 0; atol = 1.0e-12, rtol = 1.0e-12) end end end @@ -247,14 +252,14 @@ ti = time() d1 = d2 d2 = empty(d1) for (f1, coeff1) in d1 - for (f2, coeff2) in TK.artin_braid(f1, 3; inv=true) + for (f2, coeff2) in TK.artin_braid(f1, 3; inv = true) d2[f2] = get(d2, f2, zero(coeff1)) + coeff2 * coeff1 end end d1 = d2 d2 = empty(d1) for (f1, coeff1) in d1 - for (f2, coeff2) in TK.artin_braid(f1, 2; inv=true) + for (f2, coeff2) in TK.artin_braid(f1, 2; inv = true) d2[f2] = get(d2, f2, zero(coeff1)) + coeff2 * coeff1 end end @@ -263,7 +268,7 @@ ti = time() if f1 == f @test coeff1 ≈ 1 else - @test isapprox(coeff1, 0; atol=1e-12, rtol=1e-12) + @test isapprox(coeff1, 0; atol = 1.0e-12, rtol = 1.0e-12) end end end @@ -274,7 +279,7 @@ ti = time() levels = ntuple(identity, N) d = @constinferred braid(f, levels, p) - d2 = Dict{typeof(f),valtype(d)}() + d2 = Dict{typeof(f), valtype(d)}() levels2 = p for (f2, coeff) in d for (f1, coeff2) in braid(f2, levels2, ip) @@ -285,7 +290,7 @@ ti = time() if f1 == f @test coeff2 ≈ 1 else - @test isapprox(coeff2, 0; atol=1e-12, rtol=1e-12) + @test isapprox(coeff2, 0; atol = 1.0e-12, rtol = 1.0e-12) end end @@ -314,9 +319,11 @@ ti = time() @constinferred TK.merge(f1, f2, first(in1 ⊗ in2), 1) @constinferred TK.merge(f1, f2, first(in1 ⊗ in2)) end - @test dim(in1) * dim(in2) ≈ sum(abs2(coeff) * dim(c) for c in in1 ⊗ in2 - for μ in 1:Nsymbol(in1, in2, c) - for (f, coeff) in TK.merge(f1, f2, c, μ)) + @test dim(in1) * dim(in2) ≈ sum( + abs2(coeff) * dim(c) for c in in1 ⊗ in2 + for μ in 1:Nsymbol(in1, in2, c) + for (f, coeff) in TK.merge(f1, f2, c, μ) + ) for c in in1 ⊗ in2 R = Rsymbol(in1, in2, c) @@ -324,8 +331,8 @@ ti = time() trees1 = TK.merge(f1, f2, c, μ) # test merge and braid interplay - trees2 = Dict{keytype(trees1),complex(valtype(trees1))}() - trees3 = Dict{keytype(trees1),complex(valtype(trees1))}() + trees2 = Dict{keytype(trees1), complex(valtype(trees1))}() + trees3 = Dict{keytype(trees1), complex(valtype(trees1))}() for ν in 1:Nsymbol(in2, in1, c) for (t, coeff) in TK.merge(f2, f1, c, ν) trees2[t] = get(trees2, t, zero(valtype(trees2))) + coeff * R[μ, ν] @@ -340,20 +347,23 @@ ti = time() end for (t, coeff) in trees3 coeff′ = get(trees2, t, zero(coeff)) - @test isapprox(coeff, coeff′; atol=1e-12, rtol=1e-12) + @test isapprox(coeff, coeff′; atol = 1.0e-12, rtol = 1.0e-12) end # test via conversion if (BraidingStyle(I) isa Bosonic) && hasfusiontensor(I) Af1 = convert(Array, f1) Af2 = convert(Array, f2) - Af0 = convert(Array, - FusionTree((f1.coupled, f2.coupled), c, (false, false), - (), (μ,))) - _Af = TensorOperations.tensorcontract(1:(N + 2), Af1, [1:N; -1], - Af0, [-1; N + 1; N + 2]) - Af = TensorOperations.tensorcontract(1:(2N + 1), Af2, [N .+ (1:N); -1], - _Af, [1:N; -1; 2N + 1]) + Af0 = convert( + Array, + FusionTree((f1.coupled, f2.coupled), c, (false, false), (), (μ,)) + ) + _Af = TensorOperations.tensorcontract( + 1:(N + 2), Af1, [1:N; -1], Af0, [-1; N + 1; N + 2] + ) + Af = TensorOperations.tensorcontract( + 1:(2N + 1), Af2, [N .+ (1:N); -1], _Af, [1:N; -1; 2N + 1] + ) Af′ = zero(Af) for (f, coeff) in trees1 Af′ .+= coeff .* convert(Array, f) @@ -383,8 +393,8 @@ ti = time() for n in 0:(2 * N) d = @constinferred TK.repartition(f1, f2, $n) @test dim(incoming) ≈ - sum(abs2(coef) * dim(f1.coupled) for ((f1, f2), coef) in d) - d2 = Dict{typeof((f1, f2)),valtype(d)}() + sum(abs2(coef) * dim(f1.coupled) for ((f1, f2), coef) in d) + d2 = Dict{typeof((f1, f2)), valtype(d)}() for ((f1′, f2′), coeff) in d for ((f1′′, f2′′), coeff2) in TK.repartition(f1′, f2′, N) d2[(f1′′, f2′′)] = get(d2, (f1′′, f2′′), zero(coeff)) + coeff2 * coeff @@ -394,7 +404,7 @@ ti = time() if f1 == f1′ && f2 == f2′ @test coeff2 ≈ 1 else - @test isapprox(coeff2, 0; atol=1e-12, rtol=1e-12) + @test isapprox(coeff2, 0; atol = 1.0e-12, rtol = 1.0e-12) end end if (BraidingStyle(I) isa Bosonic) && hasfusiontensor(I) @@ -405,8 +415,10 @@ ti = time() d1 = prod(sz1[1:(end - 1)]) d2 = prod(sz2[1:(end - 1)]) dc = sz1[end] - A = reshape(reshape(Af1, (d1, dc)) * reshape(Af2, (d2, dc))', - (sz1[1:(end - 1)]..., sz2[1:(end - 1)]...)) + A = reshape( + reshape(Af1, (d1, dc)) * reshape(Af2, (d2, dc))', + (sz1[1:(end - 1)]..., sz2[1:(end - 1)]...) + ) A2 = zero(A) for ((f1′, f2′), coeff) in d Af1′ = convert(Array, f1′) @@ -417,8 +429,10 @@ ti = time() d2′ = prod(sz2′[1:(end - 1)]) dc′ = sz1′[end] A2 += coeff * - reshape(reshape(Af1′, (d1′, dc′)) * reshape(Af2′, (d2′, dc′))', - (sz1′[1:(end - 1)]..., sz2′[1:(end - 1)]...)) + reshape( + reshape(Af1′, (d1′, dc′)) * reshape(Af2′, (d2′, dc′))', + (sz1′[1:(end - 1)]..., sz2′[1:(end - 1)]...) + ) end @test A ≈ A2 end @@ -434,20 +448,20 @@ ti = time() d = @constinferred TensorKit.permute(f1, f2, p1, p2) @test dim(incoming) ≈ - sum(abs2(coef) * dim(f1.coupled) for ((f1, f2), coef) in d) - d2 = Dict{typeof((f1, f2)),valtype(d)}() + sum(abs2(coef) * dim(f1.coupled) for ((f1, f2), coef) in d) + d2 = Dict{typeof((f1, f2)), valtype(d)}() for ((f1′, f2′), coeff) in d d′ = TensorKit.permute(f1′, f2′, ip1, ip2) for ((f1′′, f2′′), coeff2) in d′ d2[(f1′′, f2′′)] = get(d2, (f1′′, f2′′), zero(coeff)) + - coeff2 * coeff + coeff2 * coeff end end for ((f1′, f2′), coeff2) in d2 if f1 == f1′ && f2 == f2′ @test coeff2 ≈ 1 else - @test abs(coeff2) < 1e-12 + @test abs(coeff2) < 1.0e-12 end end @@ -459,8 +473,10 @@ ti = time() d1 = prod(sz1[1:(end - 1)]) d2 = prod(sz2[1:(end - 1)]) dc = sz1[end] - A = reshape(reshape(Af1, (d1, dc)) * reshape(Af2, (d2, dc))', - (sz1[1:(end - 1)]..., sz2[1:(end - 1)]...)) + A = reshape( + reshape(Af1, (d1, dc)) * reshape(Af2, (d2, dc))', + (sz1[1:(end - 1)]..., sz2[1:(end - 1)]...) + ) Ap = permutedims(A, (p1..., p2...)) A2 = zero(Ap) for ((f1′, f2′), coeff) in d @@ -471,9 +487,11 @@ ti = time() d1′ = prod(sz1′[1:(end - 1)]) d2′ = prod(sz2′[1:(end - 1)]) dc′ = sz1′[end] - A2 += coeff * reshape(reshape(Af1′, (d1′, dc′)) * - reshape(Af2′, (d2′, dc′))', - (sz1′[1:(end - 1)]..., sz2′[1:(end - 1)]...)) + A2 += coeff * reshape( + reshape(Af1′, (d1′, dc′)) * + reshape(Af2′, (d2′, dc′))', + (sz1′[1:(end - 1)]..., sz2′[1:(end - 1)]...) + ) end @test Ap ≈ A2 end @@ -492,8 +510,8 @@ ti = time() d = @constinferred transpose(f1, f2, p1, p2) @test dim(incoming) ≈ - sum(abs2(coef) * dim(f1.coupled) for ((f1, f2), coef) in d) - d2 = Dict{typeof((f1, f2)),valtype(d)}() + sum(abs2(coef) * dim(f1.coupled) for ((f1, f2), coef) in d) + d2 = Dict{typeof((f1, f2)), valtype(d)}() for ((f1′, f2′), coeff) in d d′ = transpose(f1′, f2′, ip1, ip2) for ((f1′′, f2′′), coeff2) in d′ @@ -504,7 +522,7 @@ ti = time() if f1 == f1′ && f2 == f2′ @test coeff2 ≈ 1 else - @test abs(coeff2) < 1e-12 + @test abs(coeff2) < 1.0e-12 end end @@ -513,7 +531,7 @@ ti = time() for (f1′, f2′) in union(keys(d), keys(d3)) coeff1 = get(d, (f1′, f2′), zero(valtype(d))) coeff3 = get(d3, (f1′, f2′), zero(valtype(d3))) - @test isapprox(coeff1, coeff3; atol=1e-12) + @test isapprox(coeff1, coeff3; atol = 1.0e-12) end end @@ -525,8 +543,10 @@ ti = time() d1 = prod(sz1[1:(end - 1)]) d2 = prod(sz2[1:(end - 1)]) dc = sz1[end] - A = reshape(reshape(Af1, (d1, dc)) * reshape(Af2, (d2, dc))', - (sz1[1:(end - 1)]..., sz2[1:(end - 1)]...)) + A = reshape( + reshape(Af1, (d1, dc)) * reshape(Af2, (d2, dc))', + (sz1[1:(end - 1)]..., sz2[1:(end - 1)]...) + ) Ap = permutedims(A, (p1..., p2...)) A2 = zero(Ap) for ((f1′, f2′), coeff) in d @@ -537,9 +557,11 @@ ti = time() d1′ = prod(sz1′[1:(end - 1)]) d2′ = prod(sz2′[1:(end - 1)]) dc′ = sz1′[end] - A2 += coeff * reshape(reshape(Af1′, (d1′, dc′)) * - reshape(Af2′, (d2′, dc′))', - (sz1′[1:(end - 1)]..., sz2′[1:(end - 1)]...)) + A2 += coeff * reshape( + reshape(Af1′, (d1′, dc′)) * + reshape(Af2′, (d2′, dc′))', + (sz1′[1:(end - 1)]..., sz2′[1:(end - 1)]...) + ) end @test Ap ≈ A2 end @@ -549,11 +571,13 @@ ti = time() d1 = transpose(f1, f1, (N + 1, 1:N..., ((2N):-1:(N + 3))...), (N + 2,)) f1front, = TK.split(f1, N - 1) T = typeof(Fsymbol(one(I), one(I), one(I), one(I), one(I), one(I))[1, 1, 1, 1]) - d2 = Dict{typeof((f1front, f1front)),T}() + d2 = Dict{typeof((f1front, f1front)), T}() for ((f1′, f2′), coeff′) in d1 for ((f1′′, f2′′), coeff′′) in - TK.planar_trace(f1′, f2′, (2:N...,), (1, ((2N):-1:(N + 3))...), (N + 1,), - (N + 2,)) + TK.planar_trace( + f1′, f2′, (2:N...,), (1, ((2N):-1:(N + 3))...), (N + 1,), + (N + 2,) + ) coeff = coeff′ * coeff′′ d2[(f1′′, f2′′)] = get(d2, (f1′′, f2′′), zero(coeff)) + coeff end @@ -562,14 +586,16 @@ ti = time() if (f1_, f2_) == (f1front, f1front) @test coeff ≈ dim(f1.coupled) / dim(f1front.coupled) else - @test abs(coeff) < 1e-12 + @test abs(coeff) < 1.0e-12 end end end TensorKit.empty_globalcaches!() end tf = time() -printstyled("Finished fusion tree tests in ", - string(round(tf - ti; sigdigits=3)), - " seconds."; bold=true, color=Base.info_color()) +printstyled( + "Finished fusion tree tests in ", + string(round(tf - ti; sigdigits = 3)), + " seconds."; bold = true, color = Base.info_color() +) println() diff --git a/test/planar.jl b/test/planar.jl index 453a39c09..470f69aed 100644 --- a/test/planar.jl +++ b/test/planar.jl @@ -13,48 +13,62 @@ function force_planar(V::GradedSpace) return GradedSpace((c ⊠ PlanarTrivial() => dim(V, c) for c in sectors(V))..., isdual(V)) end force_planar(V::ProductSpace) = mapreduce(force_planar, ⊗, V) -function force_planar(tsrc::TensorMap{<:Any,ComplexSpace}) - tdst = TensorMap{scalartype(tsrc)}(undef, - force_planar(codomain(tsrc)) ← - force_planar(domain(tsrc))) +function force_planar(tsrc::TensorMap{<:Any, ComplexSpace}) + tdst = TensorMap{scalartype(tsrc)}( + undef, + force_planar(codomain(tsrc)) ← + force_planar(domain(tsrc)) + ) copyto!(block(tdst, PlanarTrivial()), block(tsrc, Trivial())) return tdst end -function force_planar(tsrc::TensorMap{<:Any,<:GradedSpace}) - tdst = TensorMap{scalartype(tsrc)}(undef, - force_planar(codomain(tsrc)) ← - force_planar(domain(tsrc))) +function force_planar(tsrc::TensorMap{<:Any, <:GradedSpace}) + tdst = TensorMap{scalartype(tsrc)}( + undef, + force_planar(codomain(tsrc)) ← + force_planar(domain(tsrc)) + ) for (c, b) in blocks(tsrc) copyto!(block(tdst, c ⊠ PlanarTrivial()), b) end return tdst end -Vtr = (ℂ^3, - (ℂ^2)', - ℂ^5, - ℂ^6, - (ℂ^7)') -VU₁ = (ℂ[U1Irrep](0 => 1, 1 => 2, -1 => 2), - ℂ[U1Irrep](0 => 3, 1 => 1, -1 => 1), - ℂ[U1Irrep](0 => 2, 1 => 2, -1 => 1)', - ℂ[U1Irrep](0 => 1, 1 => 2, -1 => 3), - ℂ[U1Irrep](0 => 1, 1 => 3, -1 => 3)') -VfU₁ = (ℂ[FermionNumber](0 => 1, 1 => 2, -1 => 2), - ℂ[FermionNumber](0 => 3, 1 => 1, -1 => 1), - ℂ[FermionNumber](0 => 2, 1 => 2, -1 => 1)', - ℂ[FermionNumber](0 => 1, 1 => 2, -1 => 3), - ℂ[FermionNumber](0 => 1, 1 => 3, -1 => 3)') -VfSU₂ = (ℂ[FermionSpin](0 => 3, 1 // 2 => 1), - ℂ[FermionSpin](0 => 2, 1 => 1), - ℂ[FermionSpin](1 // 2 => 1, 1 => 1)', - ℂ[FermionSpin](0 => 2, 1 // 2 => 2), - ℂ[FermionSpin](0 => 1, 1 // 2 => 1, 3 // 2 => 1)') -Vfib = (Vect[FibonacciAnyon](:I => 1, :τ => 2), - Vect[FibonacciAnyon](:I => 2, :τ => 1), - Vect[FibonacciAnyon](:I => 1, :τ => 1), - Vect[FibonacciAnyon](:I => 1, :τ => 1), - Vect[FibonacciAnyon](:I => 1, :τ => 1)) +Vtr = ( + ℂ^3, + (ℂ^2)', + ℂ^5, + ℂ^6, + (ℂ^7)', +) +VU₁ = ( + Vect[U1Irrep](0 => 1, 1 => 2, -1 => 2), + Vect[U1Irrep](0 => 3, 1 => 1, -1 => 1), + Vect[U1Irrep](0 => 2, 1 => 2, -1 => 1)', + Vect[U1Irrep](0 => 1, 1 => 2, -1 => 3), + Vect[U1Irrep](0 => 1, 1 => 3, -1 => 3)', +) +VfU₁ = ( + Vect[FermionNumber](0 => 1, 1 => 2, -1 => 2), + Vect[FermionNumber](0 => 3, 1 => 1, -1 => 1), + Vect[FermionNumber](0 => 2, 1 => 2, -1 => 1)', + Vect[FermionNumber](0 => 1, 1 => 2, -1 => 3), + Vect[FermionNumber](0 => 1, 1 => 3, -1 => 3)', +) +VfSU₂ = ( + Vect[FermionSpin](0 => 3, 1 // 2 => 1), + Vect[FermionSpin](0 => 2, 1 => 1), + Vect[FermionSpin](1 // 2 => 1, 1 => 1)', + Vect[FermionSpin](0 => 2, 1 // 2 => 2), + Vect[FermionSpin](0 => 1, 1 // 2 => 1, 3 // 2 => 1)', +) +Vfib = ( + Vect[FibonacciAnyon](:I => 1, :τ => 2), + Vect[FibonacciAnyon](:I => 2, :τ => 1), + Vect[FibonacciAnyon](:I => 1, :τ => 1), + Vect[FibonacciAnyon](:I => 1, :τ => 1), + Vect[FibonacciAnyon](:I => 1, :τ => 1), +) @testset "Braiding tensor" begin for V in (Vtr, VU₁, VfU₁, VfSU₂, Vfib) W = V[1] ⊗ V[2] ← V[2] ⊗ V[1] @@ -96,7 +110,7 @@ end p = ((4, 3), (5, 2, 1)) @test force_planar(tensoradd!(C, A, p, false, true, true)) ≈ - planaradd!(C′, A′, p, true, true) + planaradd!(C′, A′, p, true, true) end @testset "planartrace" begin @@ -108,7 +122,7 @@ end q = ((1,), (3,)) @test force_planar(tensortrace!(C, A, p, q, false, true, true)) ≈ - planartrace!(C′, A′, p, q, true, true) + planartrace!(C′, A′, p, q, true, true) end @testset "planarcontract" begin @@ -125,7 +139,7 @@ end pAB = ((3, 2, 1), (4, 5)) @test force_planar(tensorcontract!(C, A, pA, false, B, pB, false, pAB, true, true)) ≈ - planarcontract!(C′, A′, pA, B′, pB, pAB, true, true) + planarcontract!(C′, A′, pA, B′, pB, pAB, true, true) end end @@ -174,9 +188,9 @@ end for alloc in (TensorOperations.DefaultAllocator(), TensorOperations.ManualAllocator()) @tensor allocator = alloc y[-1 -2; -3] := GL[-1 2; 1] * x[1 3; 4] * - O[2 -2; 3 5] * GR[4 5; -3] + O[2 -2; 3 5] * GR[4 5; -3] @planar allocator = alloc y′[-1 -2; -3] := GL′[-1 2; 1] * x′[1 3; 4] * - O′[2 -2; 3 5] * GR′[4 5; -3] + O′[2 -2; 3 5] * GR′[4 5; -3] @test force_planar(y) ≈ y′ end @@ -185,10 +199,10 @@ end x2 = randn(T, Vmps ⊗ P ← Vmps ⊗ P') x2′ = force_planar(x2) @tensor contractcheck = true y2[-1 -2; -3 -4] := GL[-1 7; 6] * x2[6 5; 1 3] * - O[7 -2; 5 4] * O[4 -4; 3 2] * - GR[1 2; -3] + O[7 -2; 5 4] * O[4 -4; 3 2] * + GR[1 2; -3] @planar y2′[-1 -2; -3 -4] := GL′[-1 7; 6] * x2′[6 5; 1 3] * O′[7 -2; 5 4] * - O′[4 -4; 3 2] * GR′[1 2; -3] + O′[4 -4; 3 2] * GR′[1 2; -3] @test force_planar(y2) ≈ y2′ # transfer matrix @@ -202,7 +216,7 @@ end @tensor ρ2[-1 -2; -3] := GL[1 -2; 3] * x[3 2; -3] * conj(x[1 2; -1]) @plansor ρ3[-1 -2; -3] := GL[1 2; 4] * x[4 5; -3] * τ[2 3; 5 -2] * conj(x[1 3; -1]) @planar ρ2′[-1 -2; -3] := GL′[1 2; 4] * x′[4 5; -3] * τ[2 3; 5 -2] * - conj(x′[1 3; -1]) + conj(x′[1 3; -1]) @test force_planar(ρ2) ≈ ρ2′ @test ρ2 ≈ ρ3 @@ -213,13 +227,13 @@ end f1′ = force_planar(f1) f2′ = force_planar(f2) @tensor O_periodic1[-1 -2; -3 -4] := O[1 -2; -3 2] * f1[-1; 1 3 4] * - conj(f2[-4; 2 3 4]) + conj(f2[-4; 2 3 4]) @plansor O_periodic2[-1 -2; -3 -4] := O[1 2; -3 6] * f1[-1; 1 3 5] * - conj(f2[-4; 6 7 8]) * τ[2 3; 7 4] * - τ[4 5; 8 -2] + conj(f2[-4; 6 7 8]) * τ[2 3; 7 4] * + τ[4 5; 8 -2] @planar O_periodic′[-1 -2; -3 -4] := O′[1 2; -3 6] * f1′[-1; 1 3 5] * - conj(f2′[-4; 6 7 8]) * τ[2 3; 7 4] * - τ[4 5; 8 -2] + conj(f2′[-4; 6 7 8]) * τ[2 3; 7 4] * + τ[4 5; 8 -2] @test O_periodic1 ≈ O_periodic2 @test force_planar(O_periodic1) ≈ O_periodic′ end @@ -240,18 +254,38 @@ end for alloc in (TensorOperations.DefaultAllocator(), TensorOperations.ManualAllocator()) @tensor allocator = alloc begin - C = (((((((h[9 3 4; 5 1 2] * u[1 2; 7 12]) * conj(u[3 4; 11 13])) * - (u[8 5; 15 6] * w[6 7; 19])) * - (conj(u[8 9; 17 10]) * conj(w[10 11; 22]))) * - ((w[12 14; 20] * conj(w[13 14; 23])) * ρ[18 19 20; 21 22 23])) * - w[16 15; 18]) * conj(w[16 17; 21])) + C = ( + ( + ( + ( + ( + ((h[9 3 4; 5 1 2] * u[1 2; 7 12]) * conj(u[3 4; 11 13])) * + (u[8 5; 15 6] * w[6 7; 19]) + ) * + (conj(u[8 9; 17 10]) * conj(w[10 11; 22])) + ) * + ((w[12 14; 20] * conj(w[13 14; 23])) * ρ[18 19 20; 21 22 23]) + ) * + w[16 15; 18] + ) * conj(w[16 17; 21]) + ) end @planar allocator = alloc begin - C′ = (((((((h′[9 3 4; 5 1 2] * u′[1 2; 7 12]) * conj(u′[3 4; 11 13])) * - (u′[8 5; 15 6] * w′[6 7; 19])) * - (conj(u′[8 9; 17 10]) * conj(w′[10 11; 22]))) * - ((w′[12 14; 20] * conj(w′[13 14; 23])) * ρ′[18 19 20; 21 22 23])) * - w′[16 15; 18]) * conj(w′[16 17; 21])) + C′ = ( + ( + ( + ( + ( + ((h′[9 3 4; 5 1 2] * u′[1 2; 7 12]) * conj(u′[3 4; 11 13])) * + (u′[8 5; 15 6] * w′[6 7; 19]) + ) * + (conj(u′[8 9; 17 10]) * conj(w′[10 11; 22])) + ) * + ((w′[12 14; 20] * conj(w′[13 14; 23])) * ρ′[18 19 20; 21 22 23]) + ) * + w′[16 15; 18] + ) * conj(w′[16 17; 21]) + ) end @test C ≈ C′ end diff --git a/test/runtests.jl b/test/runtests.jl index f9829c237..8fa1600af 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -19,18 +19,18 @@ Random.seed!(1234) # specific ones is_buildkite = get(ENV, "BUILDKITE", "false") == "true" -smallset(::Type{I}) where {I<:Sector} = take(values(I), 5) -function smallset(::Type{ProductSector{Tuple{I1,I2}}}) where {I1,I2} +smallset(::Type{I}) where {I <: Sector} = take(values(I), 5) +function smallset(::Type{ProductSector{Tuple{I1, I2}}}) where {I1, I2} iter = product(smallset(I1), smallset(I2)) s = collect(i ⊠ j for (i, j) in iter if dim(i) * dim(j) <= 6) return length(s) > 6 ? rand(s, 6) : s end -function smallset(::Type{ProductSector{Tuple{I1,I2,I3}}}) where {I1,I2,I3} +function smallset(::Type{ProductSector{Tuple{I1, I2, I3}}}) where {I1, I2, I3} iter = product(smallset(I1), smallset(I2), smallset(I3)) s = collect(i ⊠ j ⊠ k for (i, j, k) in iter if dim(i) * dim(j) * dim(k) <= 6) return length(s) > 6 ? rand(s, 6) : s end -function randsector(::Type{I}) where {I<:Sector} +function randsector(::Type{I}) where {I <: Sector} s = collect(smallset(I)) a = rand(s) while a == one(a) # don't use trivial label @@ -51,66 +51,90 @@ function hasfusiontensor(I::Type{<:Sector}) end end -sectorlist = (Z2Irrep, Z3Irrep, Z4Irrep, Z3Irrep ⊠ Z4Irrep, - U1Irrep, CU1Irrep, SU2Irrep, - FermionParity, FermionParity ⊠ FermionParity, - FermionParity ⊠ U1Irrep ⊠ SU2Irrep, FermionParity ⊠ SU2Irrep ⊠ SU2Irrep, # Hubbard-like - FibonacciAnyon, IsingAnyon, - Z2Irrep ⊠ FibonacciAnyon ⊠ FibonacciAnyon) +sectorlist = ( + Z2Irrep, Z3Irrep, Z4Irrep, Z3Irrep ⊠ Z4Irrep, + U1Irrep, CU1Irrep, SU2Irrep, + FermionParity, FermionParity ⊠ FermionParity, + FermionParity ⊠ U1Irrep ⊠ SU2Irrep, FermionParity ⊠ SU2Irrep ⊠ SU2Irrep, # Hubbard-like + FibonacciAnyon, IsingAnyon, + Z2Irrep ⊠ FibonacciAnyon ⊠ FibonacciAnyon, +) # spaces Vtr = (ℂ^2, (ℂ^3)', ℂ^4, ℂ^3, (ℂ^2)') -Vℤ₂ = (ℂ[Z2Irrep](0 => 1, 1 => 1), - ℂ[Z2Irrep](0 => 1, 1 => 2)', - ℂ[Z2Irrep](0 => 3, 1 => 2)', - ℂ[Z2Irrep](0 => 2, 1 => 3), - ℂ[Z2Irrep](0 => 2, 1 => 5)) -Vfℤ₂ = (ℂ[FermionParity](0 => 1, 1 => 1), - ℂ[FermionParity](0 => 1, 1 => 2)', - ℂ[FermionParity](0 => 2, 1 => 1)', - ℂ[FermionParity](0 => 2, 1 => 3), - ℂ[FermionParity](0 => 2, 1 => 5)) -Vℤ₃ = (ℂ[Z3Irrep](0 => 1, 1 => 2, 2 => 1), - ℂ[Z3Irrep](0 => 2, 1 => 1, 2 => 1), - ℂ[Z3Irrep](0 => 1, 1 => 2, 2 => 1)', - ℂ[Z3Irrep](0 => 1, 1 => 2, 2 => 3), - ℂ[Z3Irrep](0 => 1, 1 => 3, 2 => 3)') -VU₁ = (ℂ[U1Irrep](0 => 1, 1 => 2, -1 => 2), - ℂ[U1Irrep](0 => 3, 1 => 1, -1 => 1), - ℂ[U1Irrep](0 => 2, 1 => 2, -1 => 1)', - ℂ[U1Irrep](0 => 1, 1 => 2, -1 => 3), - ℂ[U1Irrep](0 => 1, 1 => 3, -1 => 3)') -VfU₁ = (ℂ[FermionNumber](0 => 1, 1 => 2, -1 => 2), - ℂ[FermionNumber](0 => 3, 1 => 1, -1 => 1), - ℂ[FermionNumber](0 => 2, 1 => 2, -1 => 1)', - ℂ[FermionNumber](0 => 1, 1 => 2, -1 => 3), - ℂ[FermionNumber](0 => 1, 1 => 3, -1 => 3)') -VCU₁ = (ℂ[CU1Irrep]((0, 0) => 1, (0, 1) => 2, 1 => 1), - ℂ[CU1Irrep]((0, 0) => 3, (0, 1) => 0, 1 => 1), - ℂ[CU1Irrep]((0, 0) => 1, (0, 1) => 0, 1 => 2)', - ℂ[CU1Irrep]((0, 0) => 2, (0, 1) => 2, 1 => 1), - ℂ[CU1Irrep]((0, 0) => 2, (0, 1) => 1, 1 => 2)') -VSU₂ = (ℂ[SU2Irrep](0 => 3, 1 // 2 => 1), - ℂ[SU2Irrep](0 => 2, 1 => 1), - ℂ[SU2Irrep](1 // 2 => 1, 1 => 1)', - ℂ[SU2Irrep](0 => 2, 1 // 2 => 2), - ℂ[SU2Irrep](0 => 1, 1 // 2 => 1, 3 // 2 => 1)') -VfSU₂ = (ℂ[FermionSpin](0 => 3, 1 // 2 => 1), - ℂ[FermionSpin](0 => 2, 1 => 1), - ℂ[FermionSpin](1 // 2 => 1, 1 => 1)', - ℂ[FermionSpin](0 => 2, 1 // 2 => 2), - ℂ[FermionSpin](0 => 1, 1 // 2 => 1, 3 // 2 => 1)') -VSU₂U₁ = (Vect[SU2Irrep ⊠ U1Irrep]((0, 0) => 1, (1 // 2, -1) => 1), - Vect[SU2Irrep ⊠ U1Irrep]((0, 0) => 2, (0, 2) => 1, (1, 0) => 1, (1, -2) => 1, - (1 // 2, -1) => 1), - Vect[SU2Irrep ⊠ U1Irrep]((1 // 2, 1) => 1, (1, -2) => 1)', - Vect[SU2Irrep ⊠ U1Irrep]((0, 0) => 2, (0, 2) => 1, (1 // 2, 1) => 1), - Vect[SU2Irrep ⊠ U1Irrep]((0, 0) => 1, (1 // 2, 1) => 1)') -Vfib = (Vect[FibonacciAnyon](:I => 1, :τ => 1), - Vect[FibonacciAnyon](:I => 1, :τ => 2)', - Vect[FibonacciAnyon](:I => 3, :τ => 2)', - Vect[FibonacciAnyon](:I => 2, :τ => 3), - Vect[FibonacciAnyon](:I => 2, :τ => 2)) +Vℤ₂ = ( + Vect[Z2Irrep](0 => 1, 1 => 1), + Vect[Z2Irrep](0 => 1, 1 => 2)', + Vect[Z2Irrep](0 => 3, 1 => 2)', + Vect[Z2Irrep](0 => 2, 1 => 3), + Vect[Z2Irrep](0 => 2, 1 => 5), +) +Vfℤ₂ = ( + Vect[FermionParity](0 => 1, 1 => 1), + Vect[FermionParity](0 => 1, 1 => 2)', + Vect[FermionParity](0 => 2, 1 => 1)', + Vect[FermionParity](0 => 2, 1 => 3), + Vect[FermionParity](0 => 2, 1 => 5), +) +Vℤ₃ = ( + Vect[Z3Irrep](0 => 1, 1 => 2, 2 => 1), + Vect[Z3Irrep](0 => 2, 1 => 1, 2 => 1), + Vect[Z3Irrep](0 => 1, 1 => 2, 2 => 1)', + Vect[Z3Irrep](0 => 1, 1 => 2, 2 => 3), + Vect[Z3Irrep](0 => 1, 1 => 3, 2 => 3)', +) +VU₁ = ( + Vect[U1Irrep](0 => 1, 1 => 2, -1 => 2), + Vect[U1Irrep](0 => 3, 1 => 1, -1 => 1), + Vect[U1Irrep](0 => 2, 1 => 2, -1 => 1)', + Vect[U1Irrep](0 => 1, 1 => 2, -1 => 3), + Vect[U1Irrep](0 => 1, 1 => 3, -1 => 3)', +) +VfU₁ = ( + Vect[FermionNumber](0 => 1, 1 => 2, -1 => 2), + Vect[FermionNumber](0 => 3, 1 => 1, -1 => 1), + Vect[FermionNumber](0 => 2, 1 => 2, -1 => 1)', + Vect[FermionNumber](0 => 1, 1 => 2, -1 => 3), + Vect[FermionNumber](0 => 1, 1 => 3, -1 => 3)', +) +VCU₁ = ( + Vect[CU1Irrep]((0, 0) => 1, (0, 1) => 2, 1 => 1), + Vect[CU1Irrep]((0, 0) => 3, (0, 1) => 0, 1 => 1), + Vect[CU1Irrep]((0, 0) => 1, (0, 1) => 0, 1 => 2)', + Vect[CU1Irrep]((0, 0) => 2, (0, 1) => 2, 1 => 1), + Vect[CU1Irrep]((0, 0) => 2, (0, 1) => 1, 1 => 2)', +) +VSU₂ = ( + Vect[SU2Irrep](0 => 3, 1 // 2 => 1), + Vect[SU2Irrep](0 => 2, 1 => 1), + Vect[SU2Irrep](1 // 2 => 1, 1 => 1)', + Vect[SU2Irrep](0 => 2, 1 // 2 => 2), + Vect[SU2Irrep](0 => 1, 1 // 2 => 1, 3 // 2 => 1)', +) +VfSU₂ = ( + Vect[FermionSpin](0 => 3, 1 // 2 => 1), + Vect[FermionSpin](0 => 2, 1 => 1), + Vect[FermionSpin](1 // 2 => 1, 1 => 1)', + Vect[FermionSpin](0 => 2, 1 // 2 => 2), + Vect[FermionSpin](0 => 1, 1 // 2 => 1, 3 // 2 => 1)', +) +VSU₂U₁ = ( + Vect[SU2Irrep ⊠ U1Irrep]((0, 0) => 1, (1 // 2, -1) => 1), + Vect[SU2Irrep ⊠ U1Irrep]( + (0, 0) => 2, (0, 2) => 1, (1, 0) => 1, (1, -2) => 1, + (1 // 2, -1) => 1 + ), + Vect[SU2Irrep ⊠ U1Irrep]((1 // 2, 1) => 1, (1, -2) => 1)', + Vect[SU2Irrep ⊠ U1Irrep]((0, 0) => 2, (0, 2) => 1, (1 // 2, 1) => 1), + Vect[SU2Irrep ⊠ U1Irrep]((0, 0) => 1, (1 // 2, 1) => 1)', +) +Vfib = ( + Vect[FibonacciAnyon](:I => 1, :τ => 1), + Vect[FibonacciAnyon](:I => 1, :τ => 2)', + Vect[FibonacciAnyon](:I => 3, :τ => 2)', + Vect[FibonacciAnyon](:I => 2, :τ => 3), + Vect[FibonacciAnyon](:I => 2, :τ => 2), +) if !is_buildkite Ti = time() @@ -125,9 +149,11 @@ if !is_buildkite end @time include("bugfixes.jl") Tf = time() - printstyled("Finished all tests in ", - string(round((Tf - Ti) / 60; sigdigits=3)), - " minutes."; bold=true, color=Base.info_color()) + printstyled( + "Finished all tests in ", + string(round((Tf - Ti) / 60; sigdigits = 3)), + " minutes."; bold = true, color = Base.info_color() + ) println() @testset "Aqua" verbose = true begin using Aqua @@ -142,8 +168,10 @@ else if AMDGPU.functional() end=# Tf = time() - printstyled("Finished all GPU tests in ", - string(round((Tf - Ti) / 60; sigdigits=3)), - " minutes."; bold=true, color=Base.info_color()) + printstyled( + "Finished all GPU tests in ", + string(round((Tf - Ti) / 60; sigdigits = 3)), + " minutes."; bold = true, color = Base.info_color() + ) println() end diff --git a/test/spaces.jl b/test/spaces.jl index 90d27d74e..1ca8eb700 100644 --- a/test/spaces.jl +++ b/test/spaces.jl @@ -58,8 +58,7 @@ println("------------------------------------") @test !isdual(V') @test V == CartesianSpace(Trivial() => d) == CartesianSpace(Dict(Trivial() => d)) @test @constinferred(hash(V)) == hash(deepcopy(V)) - @test V == @constinferred(dual(V)) == @constinferred(conj(V)) == - @constinferred(adjoint(V)) + @test V == @constinferred(dual(V)) == @constinferred(conj(V)) == @constinferred(adjoint(V)) @test field(V) == ℝ @test @constinferred(sectortype(V)) == Trivial @test ((@constinferred sectors(V))...,) == (Trivial(),) @@ -104,8 +103,7 @@ println("------------------------------------") @test isdual(V') @test V == ComplexSpace(Trivial() => d) == ComplexSpace(Dict(Trivial() => d)) @test @constinferred(hash(V)) == hash(deepcopy(V)) != hash(V') - @test @constinferred(dual(V)) == @constinferred(conj(V)) == - @constinferred(adjoint(V)) != V + @test @constinferred(dual(V)) == @constinferred(conj(V)) == @constinferred(adjoint(V)) != V @test @constinferred(field(V)) == ℂ @test @constinferred(sectortype(V)) == Trivial @test @constinferred(sectortype(V)) == Trivial @@ -181,23 +179,23 @@ println("------------------------------------") @test eval(Meta.parse(TensorKit.type_repr(typeof(V)))) == typeof(V) @test eval(Meta.parse(sprint(show, V))) == V @test eval(Meta.parse(sprint(show, V'))) == V' - @test V' == GradedSpace(gen; dual=true) + @test V' == GradedSpace(gen; dual = true) @test V == @constinferred GradedSpace(gen...) - @test V' == @constinferred GradedSpace(gen...; dual=true) + @test V' == @constinferred GradedSpace(gen...; dual = true) @test V == @constinferred GradedSpace(tuple(gen...)) - @test V' == @constinferred GradedSpace(tuple(gen...); dual=true) + @test V' == @constinferred GradedSpace(tuple(gen...); dual = true) @test V == @constinferred GradedSpace(Dict(gen)) - @test V' == @constinferred GradedSpace(Dict(gen); dual=true) + @test V' == @constinferred GradedSpace(Dict(gen); dual = true) @test V == @inferred Vect[I](gen) - @test V' == @constinferred Vect[I](gen; dual=true) + @test V' == @constinferred Vect[I](gen; dual = true) @test V == @constinferred Vect[I](gen...) - @test V' == @constinferred Vect[I](gen...; dual=true) + @test V' == @constinferred Vect[I](gen...; dual = true) @test V == @constinferred Vect[I](Dict(gen)) - @test V' == @constinferred Vect[I](Dict(gen); dual=true) + @test V' == @constinferred Vect[I](Dict(gen); dual = true) @test V == @constinferred typeof(V)(c => dim(V, c) for c in sectors(V)) if I isa ZNIrrep @test V == @constinferred typeof(V)(V.dims) - @test V' == @constinferred typeof(V)(V.dims; dual=true) + @test V' == @constinferred typeof(V)(V.dims; dual = true) end @test @constinferred(hash(V)) == hash(deepcopy(V)) != hash(V') @test V == GradedSpace(reverse(collect(gen))...) @@ -219,8 +217,7 @@ println("------------------------------------") @test isa(InnerProductStyle(V), EuclideanInnerProduct) @test isa(V, GradedSpace) @test isa(V, GradedSpace{I}) - @test @constinferred(dual(V)) == @constinferred(conj(V)) == - @constinferred(adjoint(V)) != V + @test @constinferred(dual(V)) == @constinferred(conj(V)) == @constinferred(adjoint(V)) != V @test @constinferred(field(V)) == ℂ @test @constinferred(sectortype(V)) == I slist = @constinferred sectors(V) @@ -234,18 +231,16 @@ println("------------------------------------") @test @constinferred(⊕(V, zero(V))) == V @test @constinferred(⊕(V, V)) == Vect[I](c => 2dim(V, c) for c in sectors(V)) @test @constinferred(⊕(V, V, V, V)) == Vect[I](c => 4dim(V, c) for c in sectors(V)) - @test @constinferred(⊕(V, oneunit(V))) == - Vect[I](c => isone(c) + dim(V, c) for c in sectors(V)) + @test @constinferred(⊕(V, oneunit(V))) == Vect[I](c => isone(c) + dim(V, c) for c in sectors(V)) @test @constinferred(fuse(V, oneunit(V))) == V - d = Dict{I,Int}() + d = Dict{I, Int}() for a in sectors(V), b in sectors(V) for c in a ⊗ b d[c] = get(d, c, 0) + dim(V, a) * dim(V, b) * Nsymbol(a, b, c) end end @test @constinferred(fuse(V, V)) == GradedSpace(d) - @test @constinferred(flip(V)) == - Vect[I](conj(c) => dim(V, c) for c in sectors(V))' + @test @constinferred(flip(V)) == Vect[I](conj(c) => dim(V, c) for c in sectors(V))' @test flip(V) ≅ V @test flip(V) ≾ V @test flip(V) ≿ V @@ -279,8 +274,8 @@ println("------------------------------------") @test @constinferred(⊗(V1, V2, V3 ⊗ V4)) == P @test @constinferred(⊗(V1, V2 ⊗ V3, V4)) == P @test V1 * V2 * oneunit(V1) * V3 * V4 == - @constinferred(insertleftunit(P, 3)) == - @constinferred(insertrightunit(P, 2)) + @constinferred(insertleftunit(P, 3)) == + @constinferred(insertrightunit(P, 2)) @test @constinferred(removeunit(V1 * V2 * oneunit(V1)' * V3 * V4, 3)) == P @test fuse(V1, V2', V3) ≅ V1 ⊗ V2' ⊗ V3 @test fuse(V1, V2', V3) ≾ V1 ⊗ V2' ⊗ V3 @@ -290,8 +285,8 @@ println("------------------------------------") @test @constinferred(⊗(V1)) == ProductSpace(V1) @test eval(Meta.parse(sprint(show, ⊗(V1)))) == ⊗(V1) @test @constinferred(one(V1)) == @constinferred(one(typeof(V1))) == - @constinferred(one(P)) == @constinferred(one(typeof(P))) == - ProductSpace{ComplexSpace}(()) + @constinferred(one(P)) == @constinferred(one(typeof(P))) == + ProductSpace{ComplexSpace}(()) @test eval(Meta.parse(sprint(show, one(P)))) == one(P) @test @constinferred(⊗(one(P), P)) == P @test @constinferred(⊗(P, one(P))) == P @@ -301,8 +296,7 @@ println("------------------------------------") @test @constinferred(dim(P)) == prod(dim, (V1, V2, V3, V4)) @test @constinferred(dim(P, 2)) == dim(V2) @test @constinferred(dim(one(P))) == 1 - @test first(@constinferred(sectors(P))) == - (Trivial(), Trivial(), Trivial(), Trivial()) + @test first(@constinferred(sectors(P))) == (Trivial(), Trivial(), Trivial(), Trivial()) @test first(@constinferred(sectors(one(P)))) == () cube(x) = x^3 @test @constinferred(cube(V1)) == V1 ⊗ V1 ⊗ V1 @@ -316,10 +310,8 @@ println("------------------------------------") @test @constinferred(blockdim(P, first(blocksectors(P)))) == dim(P) @test @constinferred(blockdim(P, Trivial())) == dim(P) @test @constinferred(blockdim(one(P), Trivial())) == 1 - @test Base.IteratorEltype(P) == Base.IteratorEltype(typeof(P)) == - Base.IteratorEltype(P.spaces) - @test Base.IteratorSize(P) == Base.IteratorSize(typeof(P)) == - Base.IteratorSize(P.spaces) + @test Base.IteratorEltype(P) == Base.IteratorEltype(typeof(P)) == Base.IteratorEltype(P.spaces) + @test Base.IteratorSize(P) == Base.IteratorSize(typeof(P)) == Base.IteratorSize(P.spaces) @test Base.eltype(P) == Base.eltype(typeof(P)) == typeof(V1) @test eltype(collect(P)) == typeof(V1) @test collect(P) == [V1, V2, V3, V4] @@ -328,7 +320,7 @@ println("------------------------------------") @timedtestset "ProductSpace{SU₂Space}" begin V1, V2, V3 = SU₂Space(0 => 3, 1 // 2 => 1), SU₂Space(0 => 2, 1 => 1), - SU₂Space(1 // 2 => 1, 1 => 1)' + SU₂Space(1 // 2 => 1, 1 => 1)' P = @constinferred ProductSpace(V1, V2, V3) @test eval(Meta.parse(sprint(show, P))) == P @test eval(Meta.parse(sprint(show, typeof(P)))) == typeof(P) @@ -343,8 +335,8 @@ println("------------------------------------") @test @constinferred(⊗(V1, V2, V3)) == P @test @constinferred(adjoint(P)) == dual(P) == V3' ⊗ V2' ⊗ V1' @test V1 * V2 * oneunit(V1)' * V3 == - @constinferred(insertleftunit(P, 3; conj=true)) == - @constinferred(insertrightunit(P, 2; conj=true)) + @constinferred(insertleftunit(P, 3; conj = true)) == + @constinferred(insertrightunit(P, 2; conj = true)) @test P == @constinferred(removeunit(insertleftunit(P, 3), 3)) @test fuse(V1, V2', V3) ≅ V1 ⊗ V2' ⊗ V3 @test fuse(V1, V2', V3) ≾ V1 ⊗ V2' ⊗ V3 ≾ fuse(V1 ⊗ V2' ⊗ V3) @@ -354,7 +346,7 @@ println("------------------------------------") @test fuse(flip(V1) ⊗ V2) ⊗ flip(V3) ≅ V1 ⊗ V2 ⊗ V3 @test @constinferred(⊗(V1)) == ProductSpace(V1) @test @constinferred(one(V1)) == @constinferred(one(typeof(V1))) == - @constinferred(one(P)) == @constinferred(one(typeof(P))) + @constinferred(one(P)) == @constinferred(one(typeof(P))) @test @constinferred(dims(P)) == map(dim, (V1, V2, V3)) @test @constinferred(dim(P)) == prod(dim, (V1, V2, V3)) @test @constinferred(dim(one(P))) == 1 @@ -364,8 +356,7 @@ println("------------------------------------") @test hassector(P, s) @test @constinferred(dims(P, s)) == dim.((V1, V2, V3), s) end - @test sum(dim(c) * blockdim(P, c) for c in @constinferred(blocksectors(P))) == - dim(P) + @test sum(dim(c) * blockdim(P, c) for c in @constinferred(blocksectors(P))) == dim(P) end @timedtestset "Deligne tensor product of spaces" begin @@ -380,17 +371,17 @@ println("------------------------------------") for W4 in (V1, V2, V3, V4) Ws = @constinferred(W1 ⊠ W2 ⊠ W3 ⊠ W4) @test Ws == @constinferred((W1 ⊠ W2) ⊠ (W3 ⊠ W4)) == - @constinferred(((W1 ⊠ W2) ⊠ W3) ⊠ W4) == - @constinferred((W1 ⊠ (W2 ⊠ W3)) ⊠ W4) == - @constinferred(W1 ⊠ ((W2 ⊠ W3)) ⊠ W4) == - @constinferred(W1 ⊠ (W2 ⊠ (W3 ⊠ W4))) + @constinferred(((W1 ⊠ W2) ⊠ W3) ⊠ W4) == + @constinferred((W1 ⊠ (W2 ⊠ W3)) ⊠ W4) == + @constinferred(W1 ⊠ ((W2 ⊠ W3)) ⊠ W4) == + @constinferred(W1 ⊠ (W2 ⊠ (W3 ⊠ W4))) I1, I2, I3, I4 = map(sectortype, (W1, W2, W3, W4)) I = sectortype(Ws) @test I == @constinferred((I1 ⊠ I2) ⊠ (I3 ⊠ I4)) == - @constinferred(((I1 ⊠ I2) ⊠ I3) ⊠ I4) == - @constinferred((I1 ⊠ (I2 ⊠ I3)) ⊠ I4) == - @constinferred(I1 ⊠ ((I2 ⊠ I3)) ⊠ I4) == - @constinferred(I1 ⊠ (I2 ⊠ (I3 ⊠ I4))) + @constinferred(((I1 ⊠ I2) ⊠ I3) ⊠ I4) == + @constinferred((I1 ⊠ (I2 ⊠ I3)) ⊠ I4) == + @constinferred(I1 ⊠ ((I2 ⊠ I3)) ⊠ I4) == + @constinferred(I1 ⊠ (I2 ⊠ (I3 ⊠ I4))) @test dim(Ws) == dim(W1) * dim(W2) * dim(W3) * dim(W4) end end @@ -425,19 +416,19 @@ println("------------------------------------") @test permute(W, ((2, 4, 5), (3, 1))) == (V2 ⊗ V4' ⊗ V5' ← V3 ⊗ V1') @test (V1 ⊗ V2 ← V1 ⊗ V2) == @constinferred TensorKit.compose(W, W') @test (V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5 ⊗ oneunit(V5)) == - @constinferred(insertleftunit(W)) == - @constinferred(insertrightunit(W)) + @constinferred(insertleftunit(W)) == + @constinferred(insertrightunit(W)) @test @constinferred(removeunit(insertleftunit(W), $(numind(W) + 1))) == W @test (V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5 ⊗ oneunit(V5)') == - @constinferred(insertleftunit(W; conj=true)) == - @constinferred(insertrightunit(W; conj=true)) + @constinferred(insertleftunit(W; conj = true)) == + @constinferred(insertrightunit(W; conj = true)) @test (oneunit(V1) ⊗ V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5) == - @constinferred(insertleftunit(W, 1)) == - @constinferred(insertrightunit(W, 0)) + @constinferred(insertleftunit(W, 1)) == + @constinferred(insertrightunit(W, 0)) @test (V1 ⊗ V2 ⊗ oneunit(V1) ← V3 ⊗ V4 ⊗ V5) == - @constinferred(insertrightunit(W, 2)) + @constinferred(insertrightunit(W, 2)) @test (V1 ⊗ V2 ← oneunit(V1) ⊗ V3 ⊗ V4 ⊗ V5) == - @constinferred(insertleftunit(W, 3)) + @constinferred(insertleftunit(W, 3)) @test @constinferred(removeunit(insertleftunit(W, 3), 3)) == W @test @constinferred(insertrightunit(one(V1) ← V1, 0)) == (oneunit(V1) ← V1) @test_throws BoundsError insertleftunit(one(V1) ← V1, 0) diff --git a/test/tensors.jl b/test/tensors.jl index f60de0d1b..73462306b 100644 --- a/test/tensors.jl +++ b/test/tensors.jl @@ -4,15 +4,15 @@ spacelist = try if Sys.iswindows() (Vtr, Vℤ₂, Vfℤ₂, Vℤ₃, VU₁, VfU₁, VCU₁, VSU₂) elseif Sys.isapple() - (Vtr, Vℤ₂, Vfℤ₂, Vℤ₃, VfU₁, VfSU₂, VSU₂U₁)#, VSU₃) + (Vtr, Vℤ₂, Vfℤ₂, Vℤ₃, VfU₁, VfSU₂, VSU₂U₁) #, VSU₃) else - (Vtr, Vℤ₂, Vfℤ₂, VU₁, VCU₁, VSU₂, VfSU₂, VSU₂U₁)#, VSU₃) + (Vtr, Vℤ₂, Vfℤ₂, VU₁, VCU₁, VSU₂, VfSU₂, VSU₂U₁) #, VSU₃) end else - (Vtr, Vℤ₂, Vfℤ₂, Vℤ₃, VU₁, VfU₁, VCU₁, VSU₂, VfSU₂, VSU₂U₁)#, VSU₃) + (Vtr, Vℤ₂, Vfℤ₂, Vℤ₃, VU₁, VfU₁, VCU₁, VSU₂, VfSU₂, VSU₂U₁) #, VSU₃) end catch - (Vtr, Vℤ₂, Vfℤ₂, Vℤ₃, VU₁, VfU₁, VCU₁, VSU₂, VfSU₂, VSU₂U₁)#, VSU₃) + (Vtr, Vℤ₂, Vfℤ₂, Vℤ₃, VU₁, VfU₁, VCU₁, VSU₂, VfSU₂, VSU₂U₁) #, VSU₃) end for V in spacelist @@ -33,7 +33,7 @@ for V in spacelist @test codomain(t) == W @test space(t) == (W ← one(W)) @test domain(t) == one(W) - @test typeof(t) == TensorMap{T,spacetype(t),5,0,Vector{T}} + @test typeof(t) == TensorMap{T, spacetype(t), 5, 0, Vector{T}} # blocks bs = @constinferred blocks(t) (c, b1), state = @constinferred Nothing iterate(bs) @@ -41,7 +41,7 @@ for V in spacelist next = @constinferred Nothing iterate(bs, state) b2 = @constinferred block(t, first(blocksectors(t))) @test b1 == b2 - @test eltype(bs) === Pair{typeof(c),typeof(b1)} + @test eltype(bs) === Pair{typeof(c), typeof(b1)} @test typeof(b1) === TensorKit.blocktype(t) @test typeof(c) === sectortype(t) end @@ -103,7 +103,7 @@ for V in spacelist next = @constinferred Nothing iterate(bs, state) b2 = @constinferred block(t', first(blocksectors(t'))) @test b1 == b2 - @test eltype(bs) === Pair{typeof(c),typeof(b1)} + @test eltype(bs) === Pair{typeof(c), typeof(b1)} @test typeof(b1) === TensorKit.blocktype(t') @test typeof(c) === sectortype(t) # linear algebra @@ -130,8 +130,7 @@ for V in spacelist @test i1 * i2 == @constinferred(id(T, V1 ⊗ V2)) @test i2 * i1 == @constinferred(id(Vector{T}, V2 ⊗ V1)) - w = @constinferred(isometry(T, V1 ⊗ (oneunit(V1) ⊕ oneunit(V1)), - V1)) + w = @constinferred(isometry(T, V1 ⊗ (oneunit(V1) ⊕ oneunit(V1)), V1)) @test dim(w) == 2 * dim(V1 ← V1) @test w' * w == id(Vector{T}, V1) @test w * w' == (w * w')^2 @@ -148,20 +147,20 @@ for V in spacelist @test scalartype(t2) === T @test t.data === t2.data @test @constinferred(removeunit(t2, $(numind(t2)))) == t - t3 = @constinferred insertleftunit(t; copy=true) - @test t3 == @constinferred insertrightunit(t; copy=true) + t3 = @constinferred insertleftunit(t; copy = true) + @test t3 == @constinferred insertrightunit(t; copy = true) @test t.data !== t3.data for (c, b) in blocks(t) @test b == block(t3, c) end @test @constinferred(removeunit(t3, $(numind(t3)))) == t - t4 = @constinferred insertrightunit(t, 3; dual=true) + t4 = @constinferred insertrightunit(t, 3; dual = true) @test numin(t4) == numin(t) && numout(t4) == numout(t) + 1 for (c, b) in blocks(t) @test b == block(t4, c) end @test @constinferred(removeunit(t4, 4)) == t - t5 = @constinferred insertleftunit(t, 4; dual=true) + t5 = @constinferred insertleftunit(t, 4; dual = true) @test numin(t5) == numin(t) + 1 && numout(t5) == numout(t) for (c, b) in blocks(t) @test b == block(t5, c) @@ -229,8 +228,7 @@ for V in spacelist @test dot(t2′, t2) ≈ dot(t′, t) ≈ dot(transpose(t2′), transpose(t2)) end - t3 = VERSION < v"1.7" ? repartition(t, k) : - @constinferred repartition(t, $k) + t3 = VERSION < v"1.7" ? repartition(t, k) : @constinferred repartition(t, $k) @test norm(t3) ≈ norm(t) t3′ = @constinferred repartition!(similar(t3), t′) @test norm(t3′) ≈ norm(t′) @@ -250,14 +248,14 @@ for V in spacelist a2 = convert(Array, t2) @test a2 ≈ permutedims(a, (p1..., p2...)) @test convert(Array, transpose(t2)) ≈ - permutedims(a2, (5, 4, 3, 2, 1)) + permutedims(a2, (5, 4, 3, 2, 1)) end t3 = repartition(t, k) a3 = convert(Array, t3) - @test a3 ≈ permutedims(a, - (ntuple(identity, k)..., - reverse(ntuple(i -> i + k, 5 - k))...)) + @test a3 ≈ permutedims( + a, (ntuple(identity, k)..., reverse(ntuple(i -> i + k, 5 - k))...) + ) end end end @@ -310,14 +308,11 @@ for V in spacelist rhoR = randn(ComplexF64, V5, V5)' # test adjoint tensor H = randn(ComplexF64, V2 * V4, V2 * V4) @tensor HrA12[a, s1, s2, c] := rhoL[a, a'] * conj(A1[a', t1, b]) * - A2[b, t2, c'] * rhoR[c', c] * - H[s1, s2, t1, t2] + A2[b, t2, c'] * rhoR[c', c] * H[s1, s2, t1, t2] @tensor HrA12array[a, s1, s2, c] := convert(Array, rhoL)[a, a'] * - conj(convert(Array, A1)[a', t1, b]) * - convert(Array, A2)[b, t2, c'] * - convert(Array, rhoR)[c', c] * - convert(Array, H)[s1, s2, t1, t2] + conj(convert(Array, A1)[a', t1, b]) * convert(Array, A2)[b, t2, c'] * + convert(Array, rhoR)[c', c] * convert(Array, H)[s1, s2, t1, t2] @test HrA12array ≈ convert(Array, HrA12) end @@ -325,8 +320,8 @@ for V in spacelist @timedtestset "Index flipping: test flipping inverse" begin t = rand(ComplexF64, V1 ⊗ V1' ← V1' ⊗ V1) for i in 1:4 - @test t ≈ flip(flip(t, i), i; inv=true) - @test t ≈ flip(flip(t, i; inv=true), i) + @test t ≈ flip(flip(t, i), i; inv = true) + @test t ≈ flip(flip(t, i; inv = true), i) end end @timedtestset "Index flipping: test via explicit flip" begin @@ -348,13 +343,10 @@ for V in spacelist @tensor ta[a, b] := t1[x, y, a, z] * t2[y, b, z, x] @tensor tb[a, b] := flip(t1, 1)[x, y, a, z] * flip(t2, 4)[y, b, z, x] @test ta ≈ tb - @tensor tb[a, b] := flip(t1, (2, 4))[x, y, a, z] * - flip(t2, (1, 3))[y, b, z, x] + @tensor tb[a, b] := flip(t1, (2, 4))[x, y, a, z] * flip(t2, (1, 3))[y, b, z, x] @test ta ≈ tb - @tensor tb[a, b] := flip(t1, (1, 2, 4))[x, y, a, z] * - flip(t2, (1, 3, 4))[y, b, z, x] - @tensor tb[a, b] := flip(t1, (1, 3))[x, y, a, z] * - flip(t2, (2, 4))[y, b, z, x] + @tensor tb[a, b] := flip(t1, (1, 2, 4))[x, y, a, z] * flip(t2, (1, 3, 4))[y, b, z, x] + @tensor tb[a, b] := flip(t1, (1, 3))[x, y, a, z] * flip(t2, (2, 4))[y, b, z, x] @test flip(ta, (1, 2)) ≈ tb end @timedtestset "Multiplication of isometries: test properties" begin @@ -440,15 +432,15 @@ for V in spacelist s = dim(W) expt = @constinferred exp(t) @test reshape(convert(Array, expt), (s, s)) ≈ - exp(reshape(convert(Array, t), (s, s))) + exp(reshape(convert(Array, t), (s, s))) @test (@constinferred sqrt(t))^2 ≈ t @test reshape(convert(Array, sqrt(t^2)), (s, s)) ≈ - sqrt(reshape(convert(Array, t^2), (s, s))) + sqrt(reshape(convert(Array, t^2), (s, s))) @test exp(@constinferred log(expt)) ≈ expt @test reshape(convert(Array, log(expt)), (s, s)) ≈ - log(reshape(convert(Array, expt), (s, s))) + log(reshape(convert(Array, expt), (s, s))) @test (@constinferred cos(t))^2 + (@constinferred sin(t))^2 ≈ id(W) @test (@constinferred tan(t)) ≈ sin(t) / cos(t) @@ -475,8 +467,10 @@ for V in spacelist @test coth(@constinferred acoth(t8)) ≈ t8 t = randn(T, W, V1) # not square for f in - (cos, sin, tan, cot, cosh, sinh, tanh, coth, atan, acot, asinh, - sqrt, log, asin, acos, acosh, atanh, acoth) + ( + cos, sin, tan, cot, cosh, sinh, tanh, coth, atan, acot, asinh, + sqrt, log, asin, acos, acosh, atanh, acoth, + ) @test_throws SpaceMismatch f(t) end end @@ -493,7 +487,7 @@ for V in spacelist @test codomain(t) == V1 ⊗ V3 @test domain(t) == V2 ⊗ V4 @test norm(tA * t + t * tB + tC) < - (norm(tA) + norm(tB) + norm(tC)) * eps(real(T))^(2 / 3) + (norm(tA) + norm(tB) + norm(tC)) * eps(real(T))^(2 / 3) if BraidingStyle(I) isa Bosonic && hasfusiontensor(I) matrix(x) = reshape(convert(Array, x), dim(codomain(x)), dim(domain(x))) @test matrix(t) ≈ sylvester(matrix(tA), matrix(tB), matrix(tC)) @@ -520,8 +514,8 @@ for V in spacelist d4 = dim(domain(t2)) At = convert(Array, t) @test reshape(At, (d1, d2, d3, d4)) ≈ - reshape(convert(Array, t1), (d1, 1, d3, 1)) .* - reshape(convert(Array, t2), (1, d2, 1, d4)) + reshape(convert(Array, t1), (d1, 1, d3, 1)) .* + reshape(convert(Array, t2), (1, d2, 1, d4)) end end end @@ -573,8 +567,8 @@ end d4 = dim(domain(t2)) At = convert(Array, t) @test reshape(At, (d1, d2, d3, d4)) ≈ - reshape(convert(Array, t1), (d1, 1, d3, 1)) .* - reshape(convert(Array, t2), (1, d2, 1, d4)) + reshape(convert(Array, t1), (d1, 1, d3, 1)) .* + reshape(convert(Array, t2), (1, d2, 1, d4)) end end end