Skip to content

Commit ac8ca27

Browse files
authored
Add support for parsing operator expressions in op (#12)
1 parent 3059932 commit ac8ca27

File tree

4 files changed

+142
-145
lines changed

4 files changed

+142
-145
lines changed

Project.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
name = "QuantumOperatorDefinitions"
22
uuid = "826dd319-6fd5-459a-a990-3a4f214664bf"
33
authors = ["ITensor developers <[email protected]> and contributors"]
4-
version = "0.1.1"
4+
version = "0.1.2"
55

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

910
[compat]
1011
LinearAlgebra = "1.10"
12+
Random = "1.10"
1113
julia = "1.10"

src/op.jl

Lines changed: 125 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,42 @@
1+
using Random: randstring
2+
13
struct OpName{Name,Params}
2-
params::Params
4+
function OpName{Name,Params}(params::NamedTuple) where {Name,Params}
5+
return new{Name,(; Params..., params...)}()
6+
end
37
end
4-
params(n::OpName) = getfield(n, :params)
8+
name(::OpName{Name}) where {Name} = Name
9+
params(::OpName{<:Any,Params}) where {Params} = Params
510

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

8-
OpName{N}(params) where {N} = OpName{N,typeof(params)}(params)
13+
OpName{Name,Params}(; kwargs...) where {Name,Params} = OpName{Name,Params}((; kwargs...))
14+
15+
OpName{N}(params::NamedTuple) where {N} = OpName{N,params}()
916
OpName{N}(; kwargs...) where {N} = OpName{N}((; kwargs...))
1017

18+
const DAGGER_STRING = randstring()
19+
const UPARROW_STRING = randstring()
20+
const DOWNARROW_STRING = randstring()
21+
const OPEXPR_REPLACEMENTS = (
22+
"" => DAGGER_STRING, "" => UPARROW_STRING, "" => DOWNARROW_STRING
23+
)
24+
1125
# This compiles operator expressions, such as:
1226
# ```julia
1327
# opexpr("X + Y") == OpName("X") + OpName("Y")
1428
# opexpr("Ry{θ=π/2}") == OpName("Ry"; θ=π/2)
1529
# ```
16-
function opexpr(n::String)
17-
return opexpr(Meta.parse(n))
30+
function opexpr(n::String; kwargs...)
31+
n = replace(n, OPEXPR_REPLACEMENTS...)
32+
return opexpr(Meta.parse(n); kwargs...)
1833
end
1934
opexpr(n::Number) = n
20-
function opexpr(n::Symbol)
35+
function opexpr(n::Symbol; kwargs...)
2136
n === :im && return im
2237
n === && return π
23-
return OpName{n}()
38+
n = Symbol(replace(String(n), reverse.(OPEXPR_REPLACEMENTS)...))
39+
return OpName{n}(; kwargs...)
2440
end
2541
function opexpr(ex::Expr)
2642
if Meta.isexpr(ex, :call)
@@ -41,12 +57,107 @@ function opexpr(ex::Expr)
4157
return error("Can't parse expression $ex.")
4258
end
4359

60+
# TODO: Should this parse the string?
4461
OpName(s::AbstractString; kwargs...) = OpName{Symbol(s)}(; kwargs...)
4562
OpName(s::Symbol; kwargs...) = OpName{s}(; kwargs...)
46-
name(::OpName{N}) where {N} = N
63+
# TODO: Should this parse the string?
4764
macro OpName_str(s)
48-
return OpName{Symbol(s)}
65+
return :(OpName{$(Expr(:quote, Symbol(s)))})
66+
end
67+
68+
# This version parses. Disabled for now until
69+
# it is written better, there is a compelling
70+
# use case, and the name is decided.
71+
# TODO: Write this in terms of expressions, avoid
72+
# `eval`.
73+
# macro opexpr_str(s)
74+
# return :(typeof(opexpr($s)))
75+
# end
76+
77+
for f in (
78+
:(Base.sqrt),
79+
:(Base.real),
80+
:(Base.imag),
81+
:(Base.complex),
82+
:(Base.exp),
83+
:(Base.cis),
84+
:(Base.cos),
85+
:(Base.sin),
86+
:(Base.adjoint),
87+
:(Base.:+),
88+
:(Base.:-),
89+
)
90+
@eval begin
91+
$f(n::OpName) = OpName"f"(; f=$f, op=n)
92+
end
93+
end
94+
95+
# Unary operations
96+
nsites(n::OpName"f") = nsites(n.op)
97+
function Base.AbstractArray(n::OpName"f", domain_size::Tuple{Vararg{Int}})
98+
return n.f(AbstractArray(n.op, domain_size))
99+
end
100+
101+
nsites(n::OpName"^") = nsites(n.op)
102+
function Base.AbstractArray(n::OpName"^", domain_size::Tuple{Vararg{Int}})
103+
return AbstractArray(n.op, domain_size)^n.exponent
49104
end
105+
Base.:^(n::OpName, exponent) = OpName"^"(; op=n, exponent)
106+
107+
nsites(n::OpName"kron") = nsites(n.op1) + nsites(n.op2)
108+
function Base.AbstractArray(n::OpName"kron", domain_size::Tuple{Vararg{Int}})
109+
domain_size1 = domain_size[1:nsites(n.op1)]
110+
domain_size2 = domain_size[(nsites(n.op1) + 1):end]
111+
@assert length(domain_size2) == nsites(n.op2)
112+
return kron(AbstractArray(n.op1, domain_size1), AbstractArray(n.op2, domain_size2))
113+
end
114+
Base.kron(n1::OpName, n2::OpName) = OpName"kron"(; op1=n1, op2=n2)
115+
(n1::OpName, n2::OpName) = kron(n1, n2)
116+
117+
function nsites(n::OpName"+")
118+
@assert nsites(n.op1) == nsites(n.op2)
119+
return nsites(n.op1)
120+
end
121+
function Base.AbstractArray(n::OpName"+", domain_size::Tuple{Vararg{Int}})
122+
return AbstractArray(n.op1, domain_size) + AbstractArray(n.op2, domain_size)
123+
end
124+
Base.:+(n1::OpName, n2::OpName) = OpName"+"(; op1=n1, op2=n2)
125+
Base.:-(n1::OpName, n2::OpName) = n1 + (-n2)
126+
127+
function nsites(n::OpName"*")
128+
@assert nsites(n.op1) == nsites(n.op2)
129+
return nsites(n.op1)
130+
end
131+
function Base.AbstractArray(n::OpName"*", domain_size::Tuple{Vararg{Int}})
132+
return AbstractArray(n.op1, domain_size) * AbstractArray(n.op2, domain_size)
133+
end
134+
Base.:*(n1::OpName, n2::OpName) = OpName"*"(; op1=n1, op2=n2)
135+
136+
nsites(n::OpName"scaled") = nsites(n.op)
137+
function Base.AbstractArray(n::OpName"scaled", domain_size::Tuple{Vararg{Int}})
138+
return AbstractArray(n.op, domain_size) * n.c
139+
end
140+
function Base.:*(c::Number, n::OpName)
141+
return OpName"scaled"(; op=n, c)
142+
end
143+
function Base.:*(n::OpName, c::Number)
144+
return OpName"scaled"(; op=n, c)
145+
end
146+
function Base.:/(n::OpName, c::Number)
147+
return OpName"scaled"(; op=n, c=inv(c))
148+
end
149+
150+
function Base.:*(c::Number, n::OpName"scaled")
151+
return OpName"scaled"(; op=n.op, c=(c * n.c))
152+
end
153+
function Base.:*(n::OpName"scaled", c::Number)
154+
return OpName"scaled"(; op=n.op, c=(n.c * c))
155+
end
156+
function Base.:/(n::OpName"scaled", c::Number)
157+
return OpName"scaled"(; op=n.op, c=(n.c / c))
158+
end
159+
160+
controlled(n::OpName; ncontrol=1) = OpName"Controlled"(; ncontrol, op=n)
50161

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

119230
function op(arrtype::Type{<:AbstractArray}, n::String, domain...; kwargs...)
120-
return arrtype(OpName(n; kwargs...), domain...)
231+
return arrtype(opexpr(n; kwargs...), domain...)
121232
end
122233
function op(elt::Type{<:Number}, n::String, domain...; kwargs...)
123234
return op(AbstractArray{elt}, n, domain...; kwargs...)
@@ -136,26 +247,6 @@ function nsites(n::Union{StateName,OpName})
136247
return nsites(n′)
137248
end
138249

139-
## TODO: Delete.
140-
## # Default implementations of op
141-
## op(::OpName; kwargs...) = nothing
142-
## op(::OpName, ::SiteType; kwargs...) = nothing
143-
144-
function _sitetypes(ts::Set)
145-
return collect(SiteType, SiteType.(ts))
146-
end
147-
148-
## TODO: Delete.
149-
## op(name::AbstractString; kwargs...) = error("Must input indices when creating an `op`.")
150-
151-
# To ease calling of other op overloads,
152-
# allow passing a string as the op name
153-
## TODO: Bring this back?
154-
## op(opname::AbstractString, t::SiteType; kwargs...) = op(OpName(opname), t; kwargs...)
155-
156-
# TODO: Bring this back?
157-
# op(f::Function, args...; kwargs...) = f(op(args...; kwargs...))
158-
159250
using LinearAlgebra: Diagonal
160251
function Base.AbstractArray(::OpName"Id", domain_size::Tuple{Int})
161252
return Diagonal(trues(only(domain_size)))
@@ -214,15 +305,13 @@ function Base.AbstractArray(n::OpName"σ⁺", domain_size::Tuple{Int})
214305
return [2 * δ(i + 1, j) * ((s + 1) * (i + j - 1) - i * j) for i in 1:d, j in 1:d]
215306
end
216307
alias(::OpName"S⁺") = OpName("σ⁺") / 2
217-
@op_alias "S+" "S⁺"
218-
@op_alias "Splus" "S+"
219-
@op_alias "Sp" "S+"
308+
@op_alias "Splus" "S⁺"
309+
@op_alias "Sp" "S⁺"
220310

221311
alias(::OpName"σ⁻") = OpName"σ⁺"()'
222312
alias(::OpName"S⁻") = OpName("σ⁻") / 2
223-
@op_alias "S-" "S⁻"
224-
@op_alias "Sminus" "S-"
225-
@op_alias "Sm" "S-"
313+
@op_alias "Sminus" "S⁻"
314+
@op_alias "Sm" "S⁻"
226315

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

386-
# Unary operations
387-
nsites(n::OpName"f") = nsites(n.op)
388-
function Base.AbstractArray(n::OpName"f", domain_size::Tuple{Vararg{Int}})
389-
return n.f(AbstractArray(n.op, domain_size))
390-
end
391-
392-
for f in (
393-
:(Base.sqrt),
394-
:(Base.real),
395-
:(Base.imag),
396-
:(Base.complex),
397-
:(Base.exp),
398-
:(Base.cis),
399-
:(Base.cos),
400-
:(Base.sin),
401-
:(Base.adjoint),
402-
:(Base.:+),
403-
:(Base.:-),
404-
)
405-
@eval begin
406-
$f(n::OpName) = OpName"f"(; f=$f, op=n)
407-
end
408-
end
409-
410-
nsites(n::OpName"^") = nsites(n.op)
411-
function Base.AbstractArray(n::OpName"^", domain_size::Tuple{Vararg{Int}})
412-
return AbstractArray(n.op, domain_size)^n.exponent
413-
end
414-
Base.:^(n::OpName, exponent) = OpName"^"(; op=n, exponent)
415-
416-
nsites(n::OpName"kron") = nsites(n.op1) + nsites(n.op2)
417-
function Base.AbstractArray(n::OpName"kron", domain_size::Tuple{Vararg{Int}})
418-
domain_size1 = domain_size[1:nsites(n.op1)]
419-
domain_size2 = domain_size[(nsites(n.op1) + 1):end]
420-
@assert length(domain_size2) == nsites(n.op2)
421-
return kron(AbstractArray(n.op1, domain_size1), AbstractArray(n.op2, domain_size2))
422-
end
423-
Base.kron(n1::OpName, n2::OpName) = OpName"kron"(; op1=n1, op2=n2)
424-
(n1::OpName, n2::OpName) = kron(n1, n2)
425-
426-
function nsites(n::OpName"+")
427-
@assert nsites(n.op1) == nsites(n.op2)
428-
return nsites(n.op1)
429-
end
430-
function Base.AbstractArray(n::OpName"+", domain_size::Tuple{Vararg{Int}})
431-
return AbstractArray(n.op1, domain_size) + AbstractArray(n.op2, domain_size)
432-
end
433-
Base.:+(n1::OpName, n2::OpName) = OpName"+"(; op1=n1, op2=n2)
434-
Base.:-(n1::OpName, n2::OpName) = n1 + (-n2)
435-
436-
function nsites(n::OpName"*")
437-
@assert nsites(n.op1) == nsites(n.op2)
438-
return nsites(n.op1)
439-
end
440-
function Base.AbstractArray(n::OpName"*", domain_size::Tuple{Vararg{Int}})
441-
return AbstractArray(n.op1, domain_size) * AbstractArray(n.op2, domain_size)
442-
end
443-
Base.:*(n1::OpName, n2::OpName) = OpName"*"(; op1=n1, op2=n2)
444-
445-
nsites(n::OpName"scaled") = nsites(n.op)
446-
function Base.AbstractArray(n::OpName"scaled", domain_size::Tuple{Vararg{Int}})
447-
return AbstractArray(n.op, domain_size) * n.c
448-
end
449-
function Base.:*(c::Number, n::OpName)
450-
return OpName"scaled"(; op=n, c)
451-
end
452-
function Base.:*(n::OpName, c::Number)
453-
return OpName"scaled"(; op=n, c)
454-
end
455-
function Base.:/(n::OpName, c::Number)
456-
return OpName"scaled"(; op=n, c=inv(c))
457-
end
458-
459-
function Base.:*(c::Number, n::OpName"scaled")
460-
return OpName"scaled"(; op=n.op, c=(c * n.c))
461-
end
462-
function Base.:*(n::OpName"scaled", c::Number)
463-
return OpName"scaled"(; op=n.op, c=(n.c * c))
464-
end
465-
function Base.:/(n::OpName"scaled", c::Number)
466-
return OpName"scaled"(; op=n.op, c=(n.c / c))
467-
end
468-
469-
controlled(n::OpName; ncontrol=1) = OpName"Controlled"(; ncontrol, op=n)
470-
471475
# Expand the operator in a new basis.
472476
using LinearAlgebra:
473477
function expand(v::AbstractArray, basis)

src/sitetypes/electron.jl

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -238,44 +238,36 @@ function Base.AbstractArray(::OpName"Sˣ", st::Tuple{SiteType"Electron"})
238238
return AbstractArray(OpName("Sx"), st)
239239
end
240240

241-
function Base.AbstractArray(::OpName"S+", ::Tuple{SiteType"Electron"})
242-
# cat(falses(1, 1), Matrix(OpName("S+")), falses(1, 1); dims=(1, 2))
241+
function Base.AbstractArray(::OpName"S", ::Tuple{SiteType"Electron"})
242+
# cat(falses(1, 1), Matrix(OpName("S")), falses(1, 1); dims=(1, 2))
243243
return [
244244
0.0 0.0 0.0 0.0
245245
0.0 0.0 1.0 0.0
246246
0.0 0.0 0.0 0.0
247247
0.0 0.0 0.0 0.0
248248
]
249249
end
250-
251-
function Base.AbstractArray(::OpName"S⁺", st::Tuple{SiteType"Electron"})
252-
return AbstractArray(OpName("S+"), st)
253-
end
254250
function Base.AbstractArray(::OpName"Sp", st::Tuple{SiteType"Electron"})
255-
return AbstractArray(OpName("S+"), st)
251+
return AbstractArray(OpName("S"), st)
256252
end
257253
function Base.AbstractArray(::OpName"Splus", st::Tuple{SiteType"Electron"})
258-
return AbstractArray(OpName("S+"), st)
254+
return AbstractArray(OpName("S"), st)
259255
end
260256

261-
function Base.AbstractArray(::OpName"S-", ::Tuple{SiteType"Electron"})
262-
# cat(falses(1, 1), Matrix(OpName("S-")), falses(1, 1); dims=(1, 2))
257+
function Base.AbstractArray(::OpName"S", ::Tuple{SiteType"Electron"})
258+
# cat(falses(1, 1), Matrix(OpName("S")), falses(1, 1); dims=(1, 2))
263259
return [
264260
0.0 0.0 0.0 0.0
265261
0.0 0.0 0.0 0.0
266262
0.0 1.0 0.0 0.0
267263
0.0 0.0 0.0 0.0
268264
]
269265
end
270-
271-
function Base.AbstractArray(::OpName"S⁻", st::Tuple{SiteType"Electron"})
272-
return AbstractArray(OpName("S-"), st)
273-
end
274266
function Base.AbstractArray(::OpName"Sm", st::Tuple{SiteType"Electron"})
275-
return AbstractArray(OpName("S-"), st)
267+
return AbstractArray(OpName("S"), st)
276268
end
277269
function Base.AbstractArray(::OpName"Sminus", st::Tuple{SiteType"Electron"})
278-
return AbstractArray(OpName("S-"), st)
270+
return AbstractArray(OpName("S"), st)
279271
end
280272

281273
@op_alias "a↑" "Aup"

test/test_basics.jl

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,13 @@ const elts = (real_elts..., complex_elts...)
119119
end
120120
end
121121
@testset "Parsing" begin
122-
# TODO: Should this be called in the `OpName`/`op` constructor?
123122
@test Matrix(opexpr("X * Y")) == op("X") * op("Y")
124-
@test Matrix(opexpr("X * Y + Z")) == op("X") * op("Y") + op("Z")
125-
@test Matrix(opexpr("X * Y + 2 * Z")) == op("X") * op("Y") + 2 * op("Z")
126-
@test Matrix(opexpr("exp(im * (X * Y + 2 * Z))")) ==
127-
exp(im * (op("X") * op("Y") + 2 * op("Z")))
128-
@test Matrix(opexpr("exp(im * (X ⊗ Y + Z ⊗ Z))")) ==
123+
@test op("X * Y") == op("X") * op("Y")
124+
@test op("X * Y + Z") == op("X") * op("Y") + op("Z")
125+
@test op("X * Y + 2 * Z") == op("X") * op("Y") + 2 * op("Z")
126+
@test op("exp(im * (X * Y + 2 * Z))") == exp(im * (op("X") * op("Y") + 2 * op("Z")))
127+
@test op("exp(im * (X ⊗ Y + Z ⊗ Z))") ==
129128
exp(im * (kron(op("X"), op("Y")) + kron(op("Z"), op("Z"))))
130-
@test Matrix(opexpr("Ry{θ=π/2}")) == op("Ry"; θ=π / 2)
129+
@test op("Ry{θ=π/2}") == op("Ry"; θ=π / 2)
131130
end
132131
end

0 commit comments

Comments
 (0)