Skip to content

Commit 443e5f0

Browse files
committed
Generalize control gates to Qudits, introduce general spin site type
1 parent fac7984 commit 443e5f0

File tree

7 files changed

+121
-97
lines changed

7 files changed

+121
-97
lines changed

ext/QuantumOperatorDefinitionsITensorBaseExt/QuantumOperatorDefinitionsITensorBaseExt.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ using QuantumOperatorDefinitions:
55
QuantumOperatorDefinitions, OpName, SiteType, StateName, has_fermion_string
66

77
function QuantumOperatorDefinitions.SiteType(r::Index)
8-
return SiteType(gettag(r, "sitetype", "Qudit"); length=Int(length(r)))
8+
return SiteType(gettag(r, "sitetype", "Qudit"); dim=Int(length(r)))
99
end
1010

1111
function QuantumOperatorDefinitions.has_fermion_string(n::String, r::Index)

src/op.jl

Lines changed: 84 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1+
using LinearAlgebra: diag, qr
2+
13
struct OpName{Name,Params}
2-
function OpName{Name,Params}(params::NamedTuple) where {Name,Params}
3-
return new{Name,(; Params..., params...)}()
4+
params::Params
5+
function OpName{N}(params::NamedTuple) where {N}
6+
return new{N,typeof(params)}(params)
47
end
58
end
69
name(::OpName{Name}) where {Name} = Name
7-
params(::OpName{<:Any,Params}) where {Params} = Params
10+
params(n::OpName) = getfield(n, :params)
811

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

11-
OpName{Name,Params}(; kwargs...) where {Name,Params} = OpName{Name,Params}((; kwargs...))
12-
13-
OpName{N}(params::NamedTuple) where {N} = OpName{N,params}()
1414
OpName{N}(; kwargs...) where {N} = OpName{N}((; kwargs...))
1515

1616
# This compiles operator expressions, such as:
@@ -461,35 +461,84 @@ end
461461
alias(::OpName"√iSWAP") = (OpName"iSWAP"())
462462
@op_alias "√iSwap" "√iSWAP"
463463

464-
## TODO: Bring back these definitions.
465-
## function default_random_matrix(eltype::Type, s::Index...)
466-
## n = prod(dim.(s))
467-
## return randn(eltype, n, n)
468-
## end
469-
##
470-
## # Haar-random unitary
471-
## #
472-
## # Reference:
473-
## # Section 4.6
474-
## # http://math.mit.edu/~edelman/publications/random_matrix_theory.pdf
475-
## function op!(
476-
## o::ITensor,
477-
## ::OpName"RandomUnitary",
478-
## ::SiteType"Generic",
479-
## s1::Index,
480-
## sn::Index...;
481-
## eltype=ComplexF64,
482-
## random_matrix=default_random_matrix(eltype, s1, sn...),
483-
## )
484-
## s = (s1, sn...)
485-
## Q, _ = NDTensors.qr_positive(random_matrix)
486-
## t = itensor(Q, prime.(s)..., dag.(s)...)
487-
## return settensor!(o, tensor(t))
488-
## end
489-
##
490-
## function op!(o::ITensor, ::OpName"randU", st::SiteType"Generic", s::Index...; kwargs...)
491-
## return op!(o, OpName("RandomUnitary"), st, s...; kwargs...)
492-
## end
464+
# TODO: Generalize to more controlled operators, right now
465+
# there is only one controlled operator that is enabled when
466+
# all of the last values of each sites/Qudit is set. See:
467+
# https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.UCGate
468+
# https://arxiv.org/abs/2410.05122
469+
nsites(n::OpName"Controlled") = get(params(n), :ncontrol, 1) + nsites(n.arg)
470+
function (n::OpName"Controlled")(domain...)
471+
# Number of target sites.
472+
nt = nsites(n.arg)
473+
# Number of control sites.
474+
nc = get(params(n), :ncontrol, length(domain) - nt)
475+
@assert length(domain) == nc + nt
476+
d_control = prod(to_dim.(domain[1:nc]))
477+
return cat(I(d_control), n.arg(domain[(nc + 1):end]...); dims=(1, 2))
478+
end
479+
@op_alias "CNOT" "Controlled" op = OpName"X"()
480+
@op_alias "CX" "Controlled" op = OpName"X"()
481+
@op_alias "CY" "Controlled" op = OpName"Y"()
482+
@op_alias "CZ" "Controlled" op = OpName"Z"()
483+
function alias(n::OpName"CPhase")
484+
return controlled(OpName"Phase"(; params(n)...))
485+
end
486+
@op_alias "CPHASE" "CPhase"
487+
@op_alias "Cphase" "CPhase"
488+
function alias(n::OpName"CRx")
489+
return controlled(OpName"Rx"(; params(n)...))
490+
end
491+
@op_alias "CRX" "CRx"
492+
function alias(::OpName"CRy")
493+
return controlled(OpName"Ry"(; params(n)...))
494+
end
495+
@op_alias "CRY" "CRy"
496+
function alias(::OpName"CRz")
497+
return controlled(OpName"Rz"(; params(n)...))
498+
end
499+
@op_alias "CRZ" "CRz"
500+
function alias(::OpName"CRn")
501+
return controlled(; arg=OpName"Rn"(; params(n)...))
502+
end
503+
@op_alias "CRn̂" "CRn"
504+
505+
@op_alias "CCNOT" "Controlled" ncontrol = 2 op = OpName"X"()
506+
@op_alias "Toffoli" "CCNOT"
507+
@op_alias "CCX" "CCNOT"
508+
@op_alias "TOFF" "CCNOT"
509+
510+
@op_alias "CSWAP" "Controlled" ncontrol = 2 op = OpName"SWAP"()
511+
@op_alias "Fredkin" "CSWAP"
512+
@op_alias "CSwap" "CSWAP"
513+
@op_alias "CS" "CSWAP"
514+
515+
@op_alias "CCCNOT" "Controlled" ncontrol = 3 op = OpName"X"()
516+
517+
# Version of `sign` that returns one
518+
# if `x == 0`.
519+
function nonzero_sign(x)
520+
iszero(x) && return one(x)
521+
return sign(x)
522+
end
523+
524+
function qr_positive(M::AbstractMatrix)
525+
Q, R = qr(M)
526+
Q′ = typeof(R)(Q)
527+
signs = nonzero_sign.(diag(R))
528+
Q′ = Q′ * Diagonal(signs)
529+
R = Diagonal(conj.(signs)) * R
530+
return Q′, R
531+
end
532+
533+
# TODO: Store a random matrix or seed as a parameter
534+
# of the `OpName`?
535+
function (n::OpName"RandomUnitary")(domain...)
536+
d = prod(to_dim.(domain))
537+
elt = get(params(n), :eltype, Complex{Float64})
538+
Q, _ = qr_positive(randn(elt, (d, d)))
539+
return Q
540+
end
541+
@op_alias "randU" "RandomUnitary"
493542

494543
# Expand the operator in a new basis.
495544
using LinearAlgebra:

src/sitetype.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
struct SiteType{T,Params}
22
params::Params
3+
function SiteType{N}(params::NamedTuple) where {N}
4+
return new{N,typeof(params)}(params)
5+
end
36
end
47
params(t::SiteType) = getfield(t, :params)
5-
68
Base.getproperty(t::SiteType, name::Symbol) = getfield(params(t), name)
79

8-
SiteType{N}(params) where {N} = SiteType{N,typeof(params)}(params)
910
SiteType{N}(; kwargs...) where {N} = SiteType{N}((; kwargs...))
1011

1112
SiteType(s::AbstractString; kwargs...) = SiteType{Symbol(s)}(; kwargs...)

src/sitetypes/qubit.jl

Lines changed: 6 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -86,62 +86,15 @@ or:
8686
alias(n::OpName"R") = OpName"Rn"(; θ=n.θ, ϕ=n.ϕ, λ=-n.ϕ)
8787
=#
8888
# https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.UGate
89-
function Base.AbstractArray(n::OpName"Rn", ::Tuple{SiteType"Qubit"})
89+
# TODO: Generalize to `"Qudit"`, see:
90+
# https://quantumcomputing.stackexchange.com/questions/16251/how-does-a-general-rotation-r-hatn-theta-related-to-u-3-gate
91+
# https://quantumcomputing.stackexchange.com/questions/4249/decomposition-of-an-arbitrary-1-qubit-gate-into-a-specific-gateset
92+
# https://en.wikipedia.org/wiki/List_of_quantum_logic_gates#Other_named_gates
93+
# https://en.wikipedia.org/wiki/Spin_(physics)#Higher_spins
94+
function (n::OpName"Rn")(::SiteType"Qubit")
9095
return [
9196
cos(n.θ / 2) -exp(im * n.λ)*sin(n.θ / 2)
9297
exp(im * n.ϕ)*sin(n.θ / 2) exp(im * (n.ϕ + n.λ))*cos(n.θ / 2)
9398
]
9499
end
95100
@op_alias "Rn̂" "Rn"
96-
97-
# TODO: Generalize to `"Qudit"` and other SiteTypes.
98-
# https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.UCGate
99-
nsites(n::OpName"Controlled") = get(params(n), :ncontrol, 1) + nsites(n.arg)
100-
function Base.AbstractArray(n::OpName"Controlled", ts::Tuple{Vararg{SiteType"Qubit"}})
101-
# Number of target qubits.
102-
nt = nsites(n.arg)
103-
# Number of control qubits.
104-
nc = get(params(n), :ncontrol, length(ts) - nt)
105-
@assert length(ts) == nc + nt
106-
return [
107-
I(2^nc) falses(2^nc, 2^nt)
108-
falses(2^nt, 2^nc) AbstractArray(n.arg, ts[(nc + 1):end])
109-
]
110-
end
111-
@op_alias "CNOT" "Controlled" op = OpName"X"()
112-
@op_alias "CX" "Controlled" op = OpName"X"()
113-
@op_alias "CY" "Controlled" op = OpName"Y"()
114-
@op_alias "CZ" "Controlled" op = OpName"Z"()
115-
function alias(n::OpName"CPhase")
116-
return controlled(OpName"Phase"(; params(n)...))
117-
end
118-
@op_alias "CPHASE" "CPhase"
119-
@op_alias "Cphase" "CPhase"
120-
function alias(n::OpName"CRx")
121-
return controlled(OpName"Rx"(; params(n)...))
122-
end
123-
@op_alias "CRX" "CRx"
124-
function alias(::OpName"CRy")
125-
return controlled(OpName"Ry"(; params(n)...))
126-
end
127-
@op_alias "CRY" "CRy"
128-
function alias(::OpName"CRz")
129-
return controlled(OpName"Rz"(; params(n)...))
130-
end
131-
@op_alias "CRZ" "CRz"
132-
function alias(::OpName"CRn")
133-
return controlled(; arg=OpName"Rn"(; params(n)...))
134-
end
135-
@op_alias "CRn̂" "CRn"
136-
137-
@op_alias "CCNOT" "Controlled" ncontrol = 2 op = OpName"X"()
138-
@op_alias "Toffoli" "CCNOT"
139-
@op_alias "CCX" "CCNOT"
140-
@op_alias "TOFF" "CCNOT"
141-
142-
@op_alias "CSWAP" "Controlled" ncontrol = 2 op = OpName"SWAP"()
143-
@op_alias "Fredkin" "CSWAP"
144-
@op_alias "CSwap" "CSWAP"
145-
@op_alias "CS" "CSWAP"
146-
147-
@op_alias "CCCNOT" "Controlled" ncontrol = 3 op = OpName"X"()

src/sitetypes/qudit.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
Base.length(t::SiteType"Qudit") = t.length
2-
alias(::SiteType"Boson") = SiteType"Qudit"()
1+
Base.length(t::SiteType"Qudit") = t.dim
2+
alias(t::SiteType"Boson") = SiteType"Qudit"(; dim=t.dim)
3+
alias(t::SiteType"S") = SiteType"Qudit"(; dim=Int(2t.spin + 1))

src/state.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ using Random: randstring
22

33
struct StateName{Name,Params}
44
params::Params
5+
function StateName{N}(params::NamedTuple) where {N}
6+
return new{N,typeof(params)}(params)
7+
end
58
end
69
params(n::StateName) = getfield(n, :params)
7-
810
Base.getproperty(n::StateName, name::Symbol) = getfield(params(n), name)
911

10-
StateName{N}(params) where {N} = StateName{N,typeof(params)}(params)
1112
StateName{N}(; kwargs...) where {N} = StateName{N}((; kwargs...))
1213

1314
StateName(s::AbstractString; kwargs...) = StateName{Symbol(s)}(; kwargs...)

test/test_basics.jl

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using QuantumOperatorDefinitions: OpName, SiteType, , expand, op, opexpr, state, nsites
2-
using LinearAlgebra: Diagonal
2+
using LinearAlgebra: Diagonal, I
33
using Test: @test, @testset
44

55
const real_elts = (Float32, Float64)
@@ -54,7 +54,7 @@ const elts = (real_elts..., complex_elts...)
5454
[0 1; 0 0],
5555
),
5656
(
57-
SiteType("Qudit"; length=3),
57+
SiteType("Qudit"; dim=3),
5858
3,
5959
2 * [0 1 0; 1 0 1; 0 1 0],
6060
2 * [0 -im 0; im 0 -im; 0 im 0],
@@ -161,6 +161,19 @@ const elts = (real_elts..., complex_elts...)
161161
@test op(name, SiteType("Electron")) == Matrix(OpName(name), SiteType("Electron"))
162162
end
163163
end
164+
@testset "Random unitary" begin
165+
U = op("RandomUnitary")
166+
@test eltype(U) === Complex{Float64}
167+
@test U * U' I(2)
168+
169+
U = op("RandomUnitary"; eltype=Float32)
170+
@test eltype(U) === Float32
171+
@test U * U' I(2)
172+
173+
U = op("RandomUnitary", 3)
174+
@test eltype(U) === Complex{Float64}
175+
@test U * U' I(3)
176+
end
164177
@testset "state" begin
165178
@test state(1) == [1, 0]
166179
@test state("0") == [1, 0]
@@ -176,4 +189,10 @@ const elts = (real_elts..., complex_elts...)
176189
@test state("|0⟩ + 2|+⟩") == state("0") + 2 * state("+")
177190
@test state("|0⟩ ⊗ |+⟩") == kron(state("0"), state("+"))
178191
end
192+
@testset "Boson and spin" begin
193+
for t in (SiteType("Qudit"; dim=3), SiteType("Boson"; dim=3), SiteType("S"; spin=1))
194+
@test length(t) == 3
195+
@test op("X", t) == op("X", 3)
196+
end
197+
end
179198
end

0 commit comments

Comments
 (0)