|
| 1 | +using ITensorBase: Index, ITensor, dag, prime, tags |
| 2 | + |
| 3 | +op(::OpName, ::SiteType, ::Index...; kwargs...) = nothing |
| 4 | +function op( |
| 5 | + ::OpName, ::SiteType, ::SiteType, sitetypes_inds::Union{SiteType,Index}...; kwargs... |
| 6 | +) |
| 7 | + return nothing |
| 8 | +end |
| 9 | + |
| 10 | +_sitetypes(i::Index) = _sitetypes(tags(i)) |
| 11 | + |
| 12 | +function commontags(i::Index...) |
| 13 | + return union(tags.(i)...) |
| 14 | +end |
| 15 | + |
| 16 | +op(X::AbstractArray, s::Index...) = ITensor(X, (prime.(s)..., dag.(s)...)) |
| 17 | + |
| 18 | +# TODO: Delete these. |
| 19 | +op(opname, s::Vector{<:Index}; kwargs...) = op(opname, s...; kwargs...) |
| 20 | +op(s::Vector{<:Index}, opname; kwargs...) = op(opname, s...; kwargs...) |
| 21 | + |
| 22 | +function op(name::AbstractString, s::Index...; adjoint::Bool=false, kwargs...) |
| 23 | + name = strip(name) |
| 24 | + # TODO: filter out only commons tags |
| 25 | + # if there are multiple indices |
| 26 | + commontags_s = commontags(s...) |
| 27 | + # first we handle the + and - algebra, which requires a space between ops to avoid clashing |
| 28 | + name_split = nothing |
| 29 | + @ignore_derivatives name_split = String.(split(name, " ")) |
| 30 | + oplocs = findall(x -> x ∈ ("+", "-"), name_split) |
| 31 | + if !isempty(oplocs) |
| 32 | + @ignore_derivatives !isempty(kwargs) && |
| 33 | + error("Lazy algebra on parametric gates not allowed") |
| 34 | + # the string representation of algebra ops: ex ["+", "-", "+"] |
| 35 | + labels = name_split[oplocs] |
| 36 | + # assign coefficients to each term: ex [+1, -1, +1] |
| 37 | + coeffs = [1, [(-1)^Int(label == "-") for label in labels]...] |
| 38 | + # grad the name of each operator block separated by an algebra op, and do so by |
| 39 | + # making sure blank spaces between opnames are kept when building the new block. |
| 40 | + start, opnames = 0, String[] |
| 41 | + for oploc in oplocs |
| 42 | + finish = oploc |
| 43 | + opnames = vcat( |
| 44 | + opnames, [prod([name_split[k] * " " for k in (start + 1):(finish - 1)])] |
| 45 | + ) |
| 46 | + start = oploc |
| 47 | + end |
| 48 | + opnames = vcat( |
| 49 | + opnames, [prod([name_split[k] * " " for k in (start + 1):length(name_split)])] |
| 50 | + ) |
| 51 | + # build the vector of blocks and sum |
| 52 | + op_list = [ |
| 53 | + coeff * (op(opname, s...; kwargs...)) for (coeff, opname) in zip(coeffs, opnames) |
| 54 | + ] |
| 55 | + return sum(op_list) |
| 56 | + end |
| 57 | + # the the multiplication come after |
| 58 | + oploc = findfirst("*", name) |
| 59 | + if !isnothing(oploc) |
| 60 | + op1, op2 = nothing, nothing |
| 61 | + @ignore_derivatives begin |
| 62 | + op1 = name[1:prevind(name, oploc.start)] |
| 63 | + op2 = name[nextind(name, oploc.start):end] |
| 64 | + if !(op1[end] == ' ' && op2[1] == ' ') |
| 65 | + @warn "($op1*$op2) composite op definition `A*B` deprecated: please use `A * B` instead (with spaces)" |
| 66 | + end |
| 67 | + end |
| 68 | + return product(op(op1, s...; kwargs...), op(op2, s...; kwargs...)) |
| 69 | + end |
| 70 | + common_stypes = _sitetypes(commontags_s) |
| 71 | + @ignore_derivatives push!(common_stypes, SiteType("Generic")) |
| 72 | + opn = OpName(name) |
| 73 | + for st in common_stypes |
| 74 | + op_mat = op(opn, st; kwargs...) |
| 75 | + if !isnothing(op_mat) |
| 76 | + rs = reverse(s) |
| 77 | + res = ITensor(op_mat, (prime.(rs)..., dag.(rs)...)) |
| 78 | + adjoint && return swapprime(dag(res), 0 => 1) |
| 79 | + return res |
| 80 | + end |
| 81 | + end |
| 82 | + return throw( |
| 83 | + ArgumentError( |
| 84 | + "Overload of \"op\" or \"op!\" functions not found for operator name \"$name\" and Index tags: $(tags.(s)).", |
| 85 | + ), |
| 86 | + ) |
| 87 | +end |
| 88 | + |
| 89 | +function op(opname, s::Vector{<:Index}, ns::NTuple{N,Integer}; kwargs...) where {N} |
| 90 | + return op(opname, ntuple(n -> s[ns[n]], Val(N))...; kwargs...) |
| 91 | +end |
| 92 | + |
| 93 | +function op(opname, s::Vector{<:Index}, ns::Vararg{Integer}; kwargs...) |
| 94 | + return op(opname, s, ns; kwargs...) |
| 95 | +end |
| 96 | + |
| 97 | +function op(s::Vector{<:Index}, opname, ns::Tuple{Vararg{Integer}}; kwargs...) |
| 98 | + return op(opname, s, ns...; kwargs...) |
| 99 | +end |
| 100 | + |
| 101 | +function op(s::Vector{<:Index}, opname, ns::Integer...; kwargs...) |
| 102 | + return op(opname, s, ns; kwargs...) |
| 103 | +end |
| 104 | + |
| 105 | +function op(s::Vector{<:Index}, opname, ns::Tuple{Vararg{Integer}}, kwargs::NamedTuple) |
| 106 | + return op(opname, s, ns; kwargs...) |
| 107 | +end |
| 108 | + |
| 109 | +function op(s::Vector{<:Index}, opname, ns::Integer, kwargs::NamedTuple) |
| 110 | + return op(opname, s, (ns,); kwargs...) |
| 111 | +end |
| 112 | + |
| 113 | +op(s::Vector{<:Index}, o::Tuple) = op(s, o...) |
| 114 | + |
| 115 | +op(o::Tuple, s::Vector{<:Index}) = op(s, o...) |
| 116 | + |
| 117 | +function op( |
| 118 | + s::Vector{<:Index}, |
| 119 | + f::Function, |
| 120 | + opname::AbstractString, |
| 121 | + ns::Tuple{Vararg{Integer}}; |
| 122 | + kwargs..., |
| 123 | +) |
| 124 | + return f(op(opname, s, ns...; kwargs...)) |
| 125 | +end |
| 126 | + |
| 127 | +function op( |
| 128 | + s::Vector{<:Index}, f::Function, opname::AbstractString, ns::Integer...; kwargs... |
| 129 | +) |
| 130 | + return f(op(opname, s, ns; kwargs...)) |
| 131 | +end |
| 132 | + |
| 133 | +# Here, Ref is used to not broadcast over the vector of indices |
| 134 | +# TODO: consider overloading broadcast for `op` with the example |
| 135 | +# here: https://discourse.julialang.org/t/how-to-broadcast-over-only-certain-function-arguments/19274/5 |
| 136 | +# so that `Ref` isn't needed. |
| 137 | +ops(s::Vector{<:Index}, os::AbstractArray) = [op(oₙ, s) for oₙ in os] |
| 138 | +ops(os::AbstractVector, s::Vector{<:Index}) = [op(oₙ, s) for oₙ in os] |
0 commit comments