Skip to content

Commit

Permalink
Add support for parsing operator expressions in op (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
mtfishman authored Jan 24, 2025
1 parent 3059932 commit ac8ca27
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 145 deletions.
4 changes: 3 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
name = "QuantumOperatorDefinitions"
uuid = "826dd319-6fd5-459a-a990-3a4f214664bf"
authors = ["ITensor developers <[email protected]> and contributors"]
version = "0.1.1"
version = "0.1.2"

[deps]
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[compat]
LinearAlgebra = "1.10"
Random = "1.10"
julia = "1.10"
246 changes: 125 additions & 121 deletions src/op.jl
Original file line number Diff line number Diff line change
@@ -1,26 +1,42 @@
using Random: randstring

struct OpName{Name,Params}
params::Params
function OpName{Name,Params}(params::NamedTuple) where {Name,Params}
return new{Name,(; Params..., params...)}()
end
end
params(n::OpName) = getfield(n, :params)
name(::OpName{Name}) where {Name} = Name
params(::OpName{<:Any,Params}) where {Params} = Params

Base.getproperty(n::OpName, name::Symbol) = getfield(params(n), name)

OpName{N}(params) where {N} = OpName{N,typeof(params)}(params)
OpName{Name,Params}(; kwargs...) where {Name,Params} = OpName{Name,Params}((; kwargs...))

OpName{N}(params::NamedTuple) where {N} = OpName{N,params}()
OpName{N}(; kwargs...) where {N} = OpName{N}((; kwargs...))

const DAGGER_STRING = randstring()
const UPARROW_STRING = randstring()
const DOWNARROW_STRING = randstring()
const OPEXPR_REPLACEMENTS = (
"" => DAGGER_STRING, "" => UPARROW_STRING, "" => DOWNARROW_STRING
)

# This compiles operator expressions, such as:
# ```julia
# opexpr("X + Y") == OpName("X") + OpName("Y")
# opexpr("Ry{θ=π/2}") == OpName("Ry"; θ=π/2)
# ```
function opexpr(n::String)
return opexpr(Meta.parse(n))
function opexpr(n::String; kwargs...)
n = replace(n, OPEXPR_REPLACEMENTS...)
return opexpr(Meta.parse(n); kwargs...)
end
opexpr(n::Number) = n
function opexpr(n::Symbol)
function opexpr(n::Symbol; kwargs...)
n === :im && return im
n === && return π
return OpName{n}()
n = Symbol(replace(String(n), reverse.(OPEXPR_REPLACEMENTS)...))
return OpName{n}(; kwargs...)
end
function opexpr(ex::Expr)
if Meta.isexpr(ex, :call)
Expand All @@ -41,12 +57,107 @@ function opexpr(ex::Expr)
return error("Can't parse expression $ex.")
end

# TODO: Should this parse the string?
OpName(s::AbstractString; kwargs...) = OpName{Symbol(s)}(; kwargs...)
OpName(s::Symbol; kwargs...) = OpName{s}(; kwargs...)
name(::OpName{N}) where {N} = N
# TODO: Should this parse the string?
macro OpName_str(s)
return OpName{Symbol(s)}
return :(OpName{$(Expr(:quote, Symbol(s)))})
end

# This version parses. Disabled for now until
# it is written better, there is a compelling
# use case, and the name is decided.
# TODO: Write this in terms of expressions, avoid
# `eval`.
# macro opexpr_str(s)
# return :(typeof(opexpr($s)))
# end

for f in (
:(Base.sqrt),
:(Base.real),
:(Base.imag),
:(Base.complex),
:(Base.exp),
:(Base.cis),
:(Base.cos),
:(Base.sin),
:(Base.adjoint),
:(Base.:+),
:(Base.:-),
)
@eval begin
$f(n::OpName) = OpName"f"(; f=$f, op=n)
end
end

# Unary operations
nsites(n::OpName"f") = nsites(n.op)
function Base.AbstractArray(n::OpName"f", domain_size::Tuple{Vararg{Int}})
return n.f(AbstractArray(n.op, domain_size))
end

nsites(n::OpName"^") = nsites(n.op)
function Base.AbstractArray(n::OpName"^", domain_size::Tuple{Vararg{Int}})
return AbstractArray(n.op, domain_size)^n.exponent
end
Base.:^(n::OpName, exponent) = OpName"^"(; op=n, exponent)

nsites(n::OpName"kron") = nsites(n.op1) + nsites(n.op2)
function Base.AbstractArray(n::OpName"kron", domain_size::Tuple{Vararg{Int}})
domain_size1 = domain_size[1:nsites(n.op1)]
domain_size2 = domain_size[(nsites(n.op1) + 1):end]
@assert length(domain_size2) == nsites(n.op2)
return kron(AbstractArray(n.op1, domain_size1), AbstractArray(n.op2, domain_size2))
end
Base.kron(n1::OpName, n2::OpName) = OpName"kron"(; op1=n1, op2=n2)
(n1::OpName, n2::OpName) = kron(n1, n2)

function nsites(n::OpName"+")
@assert nsites(n.op1) == nsites(n.op2)
return nsites(n.op1)
end
function Base.AbstractArray(n::OpName"+", domain_size::Tuple{Vararg{Int}})
return AbstractArray(n.op1, domain_size) + AbstractArray(n.op2, domain_size)
end
Base.:+(n1::OpName, n2::OpName) = OpName"+"(; op1=n1, op2=n2)
Base.:-(n1::OpName, n2::OpName) = n1 + (-n2)

function nsites(n::OpName"*")
@assert nsites(n.op1) == nsites(n.op2)
return nsites(n.op1)
end
function Base.AbstractArray(n::OpName"*", domain_size::Tuple{Vararg{Int}})
return AbstractArray(n.op1, domain_size) * AbstractArray(n.op2, domain_size)
end
Base.:*(n1::OpName, n2::OpName) = OpName"*"(; op1=n1, op2=n2)

nsites(n::OpName"scaled") = nsites(n.op)
function Base.AbstractArray(n::OpName"scaled", domain_size::Tuple{Vararg{Int}})
return AbstractArray(n.op, domain_size) * n.c
end
function Base.:*(c::Number, n::OpName)
return OpName"scaled"(; op=n, c)
end
function Base.:*(n::OpName, c::Number)
return OpName"scaled"(; op=n, c)
end
function Base.:/(n::OpName, c::Number)
return OpName"scaled"(; op=n, c=inv(c))
end

function Base.:*(c::Number, n::OpName"scaled")
return OpName"scaled"(; op=n.op, c=(c * n.c))
end
function Base.:*(n::OpName"scaled", c::Number)
return OpName"scaled"(; op=n.op, c=(n.c * c))
end
function Base.:/(n::OpName"scaled", c::Number)
return OpName"scaled"(; op=n.op, c=(n.c / c))
end

controlled(n::OpName; ncontrol=1) = OpName"Controlled"(; ncontrol, op=n)

function op_alias_expr(name1, name2, pars...)
return :(function alias(n::OpName{Symbol($name1)})
Expand Down Expand Up @@ -117,7 +228,7 @@ function (arrtype::Type{<:AbstractArray})(n::OpName)
end

function op(arrtype::Type{<:AbstractArray}, n::String, domain...; kwargs...)
return arrtype(OpName(n; kwargs...), domain...)
return arrtype(opexpr(n; kwargs...), domain...)
end
function op(elt::Type{<:Number}, n::String, domain...; kwargs...)
return op(AbstractArray{elt}, n, domain...; kwargs...)
Expand All @@ -136,26 +247,6 @@ function nsites(n::Union{StateName,OpName})
return nsites(n′)
end

## TODO: Delete.
## # Default implementations of op
## op(::OpName; kwargs...) = nothing
## op(::OpName, ::SiteType; kwargs...) = nothing

function _sitetypes(ts::Set)
return collect(SiteType, SiteType.(ts))
end

## TODO: Delete.
## op(name::AbstractString; kwargs...) = error("Must input indices when creating an `op`.")

# To ease calling of other op overloads,
# allow passing a string as the op name
## TODO: Bring this back?
## op(opname::AbstractString, t::SiteType; kwargs...) = op(OpName(opname), t; kwargs...)

# TODO: Bring this back?
# op(f::Function, args...; kwargs...) = f(op(args...; kwargs...))

using LinearAlgebra: Diagonal
function Base.AbstractArray(::OpName"Id", domain_size::Tuple{Int})
return Diagonal(trues(only(domain_size)))
Expand Down Expand Up @@ -214,15 +305,13 @@ function Base.AbstractArray(n::OpName"σ⁺", domain_size::Tuple{Int})
return [2 * δ(i + 1, j) * ((s + 1) * (i + j - 1) - i * j) for i in 1:d, j in 1:d]
end
alias(::OpName"S⁺") = OpName("σ⁺") / 2
@op_alias "S+" "S⁺"
@op_alias "Splus" "S+"
@op_alias "Sp" "S+"
@op_alias "Splus" "S⁺"
@op_alias "Sp" "S⁺"

alias(::OpName"σ⁻") = OpName"σ⁺"()'
alias(::OpName"S⁻") = OpName("σ⁻") / 2
@op_alias "S-" "S⁻"
@op_alias "Sminus" "S-"
@op_alias "Sm" "S-"
@op_alias "Sminus" "S⁻"
@op_alias "Sm" "S⁻"

alias(::OpName"X") = (OpName"σ⁺"() + OpName"σ⁻"()) / 2
@op_alias "σx" "X"
Expand Down Expand Up @@ -383,91 +472,6 @@ alias(::OpName"√iSWAP") = √(OpName"iSWAP"())
## return op!(o, OpName("RandomUnitary"), st, s...; kwargs...)
## end

# Unary operations
nsites(n::OpName"f") = nsites(n.op)
function Base.AbstractArray(n::OpName"f", domain_size::Tuple{Vararg{Int}})
return n.f(AbstractArray(n.op, domain_size))
end

for f in (
:(Base.sqrt),
:(Base.real),
:(Base.imag),
:(Base.complex),
:(Base.exp),
:(Base.cis),
:(Base.cos),
:(Base.sin),
:(Base.adjoint),
:(Base.:+),
:(Base.:-),
)
@eval begin
$f(n::OpName) = OpName"f"(; f=$f, op=n)
end
end

nsites(n::OpName"^") = nsites(n.op)
function Base.AbstractArray(n::OpName"^", domain_size::Tuple{Vararg{Int}})
return AbstractArray(n.op, domain_size)^n.exponent
end
Base.:^(n::OpName, exponent) = OpName"^"(; op=n, exponent)

nsites(n::OpName"kron") = nsites(n.op1) + nsites(n.op2)
function Base.AbstractArray(n::OpName"kron", domain_size::Tuple{Vararg{Int}})
domain_size1 = domain_size[1:nsites(n.op1)]
domain_size2 = domain_size[(nsites(n.op1) + 1):end]
@assert length(domain_size2) == nsites(n.op2)
return kron(AbstractArray(n.op1, domain_size1), AbstractArray(n.op2, domain_size2))
end
Base.kron(n1::OpName, n2::OpName) = OpName"kron"(; op1=n1, op2=n2)
(n1::OpName, n2::OpName) = kron(n1, n2)

function nsites(n::OpName"+")
@assert nsites(n.op1) == nsites(n.op2)
return nsites(n.op1)
end
function Base.AbstractArray(n::OpName"+", domain_size::Tuple{Vararg{Int}})
return AbstractArray(n.op1, domain_size) + AbstractArray(n.op2, domain_size)
end
Base.:+(n1::OpName, n2::OpName) = OpName"+"(; op1=n1, op2=n2)
Base.:-(n1::OpName, n2::OpName) = n1 + (-n2)

function nsites(n::OpName"*")
@assert nsites(n.op1) == nsites(n.op2)
return nsites(n.op1)
end
function Base.AbstractArray(n::OpName"*", domain_size::Tuple{Vararg{Int}})
return AbstractArray(n.op1, domain_size) * AbstractArray(n.op2, domain_size)
end
Base.:*(n1::OpName, n2::OpName) = OpName"*"(; op1=n1, op2=n2)

nsites(n::OpName"scaled") = nsites(n.op)
function Base.AbstractArray(n::OpName"scaled", domain_size::Tuple{Vararg{Int}})
return AbstractArray(n.op, domain_size) * n.c
end
function Base.:*(c::Number, n::OpName)
return OpName"scaled"(; op=n, c)
end
function Base.:*(n::OpName, c::Number)
return OpName"scaled"(; op=n, c)
end
function Base.:/(n::OpName, c::Number)
return OpName"scaled"(; op=n, c=inv(c))
end

function Base.:*(c::Number, n::OpName"scaled")
return OpName"scaled"(; op=n.op, c=(c * n.c))
end
function Base.:*(n::OpName"scaled", c::Number)
return OpName"scaled"(; op=n.op, c=(n.c * c))
end
function Base.:/(n::OpName"scaled", c::Number)
return OpName"scaled"(; op=n.op, c=(n.c / c))
end

controlled(n::OpName; ncontrol=1) = OpName"Controlled"(; ncontrol, op=n)

# Expand the operator in a new basis.
using LinearAlgebra:
function expand(v::AbstractArray, basis)
Expand Down
24 changes: 8 additions & 16 deletions src/sitetypes/electron.jl
Original file line number Diff line number Diff line change
Expand Up @@ -238,44 +238,36 @@ function Base.AbstractArray(::OpName"Sˣ", st::Tuple{SiteType"Electron"})
return AbstractArray(OpName("Sx"), st)
end

function Base.AbstractArray(::OpName"S+", ::Tuple{SiteType"Electron"})
# cat(falses(1, 1), Matrix(OpName("S+")), falses(1, 1); dims=(1, 2))
function Base.AbstractArray(::OpName"S", ::Tuple{SiteType"Electron"})
# cat(falses(1, 1), Matrix(OpName("S")), falses(1, 1); dims=(1, 2))
return [
0.0 0.0 0.0 0.0
0.0 0.0 1.0 0.0
0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0
]
end

function Base.AbstractArray(::OpName"S⁺", st::Tuple{SiteType"Electron"})
return AbstractArray(OpName("S+"), st)
end
function Base.AbstractArray(::OpName"Sp", st::Tuple{SiteType"Electron"})
return AbstractArray(OpName("S+"), st)
return AbstractArray(OpName("S"), st)
end
function Base.AbstractArray(::OpName"Splus", st::Tuple{SiteType"Electron"})
return AbstractArray(OpName("S+"), st)
return AbstractArray(OpName("S"), st)
end

function Base.AbstractArray(::OpName"S-", ::Tuple{SiteType"Electron"})
# cat(falses(1, 1), Matrix(OpName("S-")), falses(1, 1); dims=(1, 2))
function Base.AbstractArray(::OpName"S", ::Tuple{SiteType"Electron"})
# cat(falses(1, 1), Matrix(OpName("S")), falses(1, 1); dims=(1, 2))
return [
0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0
0.0 1.0 0.0 0.0
0.0 0.0 0.0 0.0
]
end

function Base.AbstractArray(::OpName"S⁻", st::Tuple{SiteType"Electron"})
return AbstractArray(OpName("S-"), st)
end
function Base.AbstractArray(::OpName"Sm", st::Tuple{SiteType"Electron"})
return AbstractArray(OpName("S-"), st)
return AbstractArray(OpName("S"), st)
end
function Base.AbstractArray(::OpName"Sminus", st::Tuple{SiteType"Electron"})
return AbstractArray(OpName("S-"), st)
return AbstractArray(OpName("S"), st)
end

@op_alias "a↑" "Aup"
Expand Down
13 changes: 6 additions & 7 deletions test/test_basics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,13 @@ const elts = (real_elts..., complex_elts...)
end
end
@testset "Parsing" begin
# TODO: Should this be called in the `OpName`/`op` constructor?
@test Matrix(opexpr("X * Y")) == op("X") * op("Y")
@test Matrix(opexpr("X * Y + Z")) == op("X") * op("Y") + op("Z")
@test Matrix(opexpr("X * Y + 2 * Z")) == op("X") * op("Y") + 2 * op("Z")
@test Matrix(opexpr("exp(im * (X * Y + 2 * Z))")) ==
exp(im * (op("X") * op("Y") + 2 * op("Z")))
@test Matrix(opexpr("exp(im * (X ⊗ Y + Z ⊗ Z))")) ==
@test op("X * Y") == op("X") * op("Y")
@test op("X * Y + Z") == op("X") * op("Y") + op("Z")
@test op("X * Y + 2 * Z") == op("X") * op("Y") + 2 * op("Z")
@test op("exp(im * (X * Y + 2 * Z))") == exp(im * (op("X") * op("Y") + 2 * op("Z")))
@test op("exp(im * (X ⊗ Y + Z ⊗ Z))") ==
exp(im * (kron(op("X"), op("Y")) + kron(op("Z"), op("Z"))))
@test Matrix(opexpr("Ry{θ=π/2}")) == op("Ry"; θ=π / 2)
@test op("Ry{θ=π/2}") == op("Ry"; θ=π / 2)
end
end

0 comments on commit ac8ca27

Please sign in to comment.