Skip to content

Type promotion between MixedDestabilizer, Stabilizer and others, also used in ⊗ #354

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Sep 27, 2024
22 changes: 22 additions & 0 deletions src/QuantumClifford.jl
Original file line number Diff line number Diff line change
Expand Up @@ -989,6 +989,28 @@ function _apply!(stab::AbstractStabilizer, p::PauliOperator, indices; phases::Va
stab
end

##############################
# Conversion and promotion
##############################

Base.promote_rule(::Type{<:MixedStabilizer{T}} , ::Type{<:Stabilizer{T}} ) where {T<:Tableau} = Stabilizer{T}
Base.promote_rule(::Type{<:MixedStabilizer{T}} , ::Type{<:MixedStabilizer{T}}) where {T<:Tableau} = Stabilizer{T}
Base.promote_rule(::Type{<:MixedStabilizer{T}} , ::Type{<:Destabilizer{T}} ) where {T<:Tableau} = MixedDestabilizer{T}
Base.promote_rule(::Type{<:Stabilizer{T}} , ::Type{<:Destabilizer{T}} ) where {T<:Tableau} = MixedDestabilizer{T}
Base.promote_rule(::Type{<:Destabilizer{T}} , ::Type{<:Destabilizer{T}} ) where {T<:Tableau} = MixedDestabilizer{T}
Base.promote_rule(::Type{<:MixedDestabilizer{T}}, ::Type{<:Stabilizer{T}} ) where {T<:Tableau} = MixedDestabilizer{T}
Base.promote_rule(::Type{<:MixedDestabilizer{T}}, ::Type{<:Destabilizer{T}} ) where {T<:Tableau} = MixedDestabilizer{T}
Base.promote_rule(::Type{<:MixedDestabilizer{T}}, ::Type{<:MixedStabilizer{T}}) where {T<:Tableau} = MixedDestabilizer{T}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not agree with many of these rules. A MixedStabilizer can not be converted to Stabilizer.

From special to general the conversions would be (missing a lot of type parameterizations):

Stabilizer, T<:Union{MixedStabilizer, Destabilizer, MixedDestabilizer} -> T
Destabilizer, MixedDestabilizer -> MixedDestabilizer
MixedStabilizer, MixedDestabilizer -> MixedDestabilizer

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, I agree with you. I will remove the nonsensical rules. Also, incorporate your last comment about not missing the type parameterizations.

tensor(MixedStab(...), S"X") causes stack overflow error if a user wants to cause it, so most of the nonsensical rules were to avoid the stack overflow errors. But yeah, they result in nonsense objects which I completely overlooked. Sorry about that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much for special to general conversions #354 (comment), I have added them as instructed.


Base.convert(::Type{<:Stabilizer{T}} , x::MixedStabilizer{T} ) where {T<:Tableau} = Stabilizer(tab(x))
Base.convert(::Type{<:MixedStabilizer{T}} , x::MixedStabilizer{T} ) where {T<:Tableau} = Stabilizer(tab(x))
Base.convert(::Type{<:Stabilizer{T}} , x::Destabilizer{T} ) where {T<:Tableau} = MixedDestabilizer(x)
Base.convert(::Type{<:Destabilizer{T}} , x::Destabilizer{T} ) where {T<:Tableau} = MixedDestabilizer(x)
Base.convert(::Type{<:MixedDestabilizer{T}} , x::Stabilizer{T} ) where {T<:Tableau} = MixedDestabilizer(x)
Base.convert(::Type{<:MixedDestabilizer{T}} , x::Destabilizer{T} ) where {T<:Tableau} = MixedDestabilizer(x)
Base.convert(::Type{<:Destabilizer{T}} , x::MixedStabilizer{T} ) where {T<:Tableau} = MixedDestabilizer(Stabilizer(tab(x)))
Base.convert(::Type{<:MixedDestabilizer{T}} , x::MixedStabilizer{T} ) where {T<:Tableau} = MixedDestabilizer(Stabilizer(tab(x)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these converts can be implemented as something much simpler, e.g. convert(::T, x::AbstractStabilizer) where {T<:Type{S}, S<:AbstractStabilizer} = S(x) because we already have the constructors implemented. I probable screwed up some of the type syntax or names of types, but this is the rough idea.


##############################
# Helpers for binary codes
##############################
Expand Down
117 changes: 115 additions & 2 deletions src/linalg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,124 @@ julia> tensor(s, s)
- ____Z_
```

Tensor product between [`MixedDestabilizer`](@ref) and [`Stabilizer`](@ref):

```jldoctest promote
julia> md = MixedDestabilizer(T"Z//X", 1)
𝒟ℯ𝓈𝓉𝒶𝒷
+ Z
𝒮𝓉𝒶𝒷
+ X

julia> tensor(S"X", md)
𝒟ℯ𝓈𝓉𝒶𝒷
+ Z_
+ _Z
𝒮𝓉𝒶𝒷
+ X_
+ _X
```

Tensor product between [`MixedDestabilizer`](@ref) and [`Destabilizer`](@ref):

```jldoctest promote
julia> d = Destabilizer(T"-Y//-X")
𝒟ℯ𝓈𝓉𝒶𝒷
- Y
𝒮𝓉𝒶𝒷
- X

julia> tensor(d, md)
𝒟ℯ𝓈𝓉𝒶𝒷
- Y_
+ _Z
𝒮𝓉𝒶𝒷
- X_
+ _X
```

Tensor product between [`MixedStabilizer`](@ref) and [`Stabilizer`](@ref):

```jldoctest promote
julia> ms = MixedStabilizer(S"-XZ//-ZX")
- XZ
- ZX

julia> tensor(S"X", ms)
+ X__
- _XZ
- _ZX
```

Tensor product between [`Stabilizer`](@ref) and [`Destabilizer`](@ref):

```jldoctest promote
julia> tensor(S"X", d)
𝒟ℯ𝓈𝓉𝒶𝒷
+ Z_
- _Y
𝒮𝓉𝒶𝒷
+ X_
- _X
```

Tensor product between [`MixedStabilizer`](@ref) and [`MixedDestabilizer`](@ref):

```jldoctest promote
julia> tensor(md, ms)
𝒟ℯ𝓈𝓉𝒶𝒷
+ Z__
+ _Z_
+ __Z
𝒮𝓉𝒶𝒷━
+ X__
- _XZ
- _ZX
```

Tensor product between [`MixedStabilizer`](@ref) and [`Destabilizer`](@ref):

```jldoctest promote
julia> tensor(d, ms)
𝒟ℯ𝓈𝓉𝒶𝒷
- Y__
+ _Z_
+ __Z
𝒮𝓉𝒶𝒷━
- X__
- _XZ
- _ZX
```

Tensor product between [`Destabilizer`](@ref) and [`Destabilizer`](@ref):

```jldoctest promote
julia> tensor(d, d)
𝒟ℯ𝓈𝓉𝒶𝒷
- Y_
- _Y
𝒮𝓉𝒶𝒷
- X_
- _X
```

Tensor product between [`MixedStabilizer`](@ref) and [`MixedStabilizer`](@ref):

```jldoctest promote
julia> tensor(ms, ms)
- XZ__
- ZX__
- __XZ
- __ZX
```

See also [`tensor_pow`](@ref)."""
function tensor end

function tensor(ops::AbstractStabilizer...) # TODO optimize this by doing conversion to common type to enable preallocation
foldl(⊗, ops[2:end], init=ops[1])
function tensor(ops::AbstractStabilizer...)
ct = promote_type(map(typeof, ops)...)
conv_ops = map(x -> convert(ct, x), ops)
return foldl(⊗, conv_ops)
end

"""Repeated tensor product of an operators or a tableau.
Expand Down
22 changes: 22 additions & 0 deletions test/test_stabs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,28 @@
stabs = [s[1:i] for s in [random_stabilizer(n) for n in [32,16,16,64,63,65,129,128,127]] for i in rand(1:10)];
mdstabs = MixedDestabilizer.(stabs);
@test canonicalize!(⊗(stabs...)) == canonicalize!(stabilizerview(⊗(mdstabs...)))
md = MixedDestabilizer(random_destabilizer(n))
s = random_stabilizer(n)
mds = md⊗s
@test mixed_destab_looks_good(mds)
estab = stabilizerview(md)⊗s
@test canonicalize!(copy(stabilizerview(mds))) == canonicalize!(estab)
d = random_destabilizer(n)
dmd = d⊗md
@test mixed_destab_looks_good(dmd)
ms = MixedStabilizer(random_stabilizer(n))
mss = s⊗ms
@test mixed_stab_looks_good(mss)
sd = random_stabilizer(n)⊗random_destabilizer(n)
@test mixed_destab_looks_good(sd)
mdms = md⊗ms
@test mixed_destab_looks_good(mdms)
dms = d⊗ms
@test mixed_destab_looks_good(dms)
dd = d⊗d
@test destab_looks_good(dd)
msms = ms⊗ms
@test mixed_stab_looks_good(msms)
end
end

Expand Down
Loading