QuantumLegos.jl/test/runtests.jl

405 lines
16 KiB
Julia

# for language server completion
# must be removed for test
true || include("../src/QuantumLegos.jl")
using QuantumLegos
using Test
using Documenter
using Aqua
using JET
@testset "QuantumLegos.jl" begin
@testset "PauliOps" begin
include("pauliops.jl")
end
@testset "Lego" begin
stabilizers = pauliop.(["II", "XX"])
wrong_stabilizers = pauliop.(["I", "XX"]) # not SVector
@info stabilizers
@test_throws ArgumentError QuantumLegos.Lego(3, stabilizers)
@test_throws MethodError QuantumLegos.Lego(2, wrong_stabilizers)
end
@testset "CheckMatrix" begin
@test_throws ArgumentError QuantumLegos.CheckMatrix([true false; false true], 2, 2)
@test_throws ArgumentError QuantumLegos.CheckMatrix([true false; false true], 1, 1)
@test QuantumLegos.CheckMatrix([true false; false true]) ==
QuantumLegos.CheckMatrix(Bool[1 0; 0 1], 1, 2)
@test_throws ArgumentError QuantumLegos.CheckMatrix([true false true; false true true])
@test_throws MethodError QuantumLegos.checkmatrix(pauliop.(["I", "IX"]))
@test_throws ArgumentError QuantumLegos.checkmatrix(PauliOps.PauliOp[])
@test QuantumLegos.checkmatrix(pauliop.(["IIXX", "IZZI"])) ==
QuantumLegos.CheckMatrix(Bool[0 0 1 1 0 0 0 0; 0 0 0 0 0 1 1 0])
@test QuantumLegos.checkmatrix(pauliop.(["YIXX", "IZYI"])) ==
QuantumLegos.CheckMatrix(Bool[1 0 1 1 1 0 0 0; 0 0 1 0 0 1 1 0])
let
gens = pauliop.(["IIXX", "IZZI"])
cmat_1 = QuantumLegos.checkmatrix(gens)
@test QuantumLegos.xpart(cmat_1) == Bool[0 0 1 1; 0 0 0 0]
@test QuantumLegos.zpart(cmat_1) == Bool[0 0 0 0; 0 1 1 0]
@test generators(cmat_1) == gens
end
let
gens = pauliop.(["YIXX", "IZYI"])
cmat_2 = QuantumLegos.checkmatrix(gens)
@test QuantumLegos.xpart(cmat_2) == Bool[1 0 1 1; 0 0 1 0]
@test QuantumLegos.zpart(cmat_2) == Bool[1 0 0 0; 0 1 1 0]
@test generators(cmat_2) == gens
end
@testset "eliminate_column!" begin
let
cmat = QuantumLegos.CheckMatrix(Bool[
1 0 0 1
1 0 1 1
])
@test QuantumLegos.eliminate_column!(cmat, 1, Int64[]) == 1
@test cmat.cmat == Bool[
1 0 0 1
0 0 1 0
]
end
let
cmat = QuantumLegos.CheckMatrix(Bool[
1 0 0 1
1 0 1 1
])
@test QuantumLegos.eliminate_column!(cmat, 1, Int64[1]) == 2
@test cmat.cmat == Bool[
0 0 1 0
1 0 1 1
]
end
let
cmat = QuantumLegos.CheckMatrix(
Bool[
1 1 0 1 0 0 1 1
0 0 1 1 0 1 0 1
0 1 1 0 1 0 1 1
0 1 0 1 0 1 0 1
],
)
@test QuantumLegos.eliminate_column!(cmat, 2, [1]) == 3
@test cmat.cmat == Bool[
1 0 1 1 1 0 0 0
0 0 1 1 0 1 0 1
0 1 1 0 1 0 1 1
0 0 1 1 1 1 1 0
]
end
let
cmat = QuantumLegos.CheckMatrix(
Bool[
1 1 0 1 0 0 1 1
0 0 1 1 0 1 0 1
0 1 1 0 1 0 1 1
0 1 0 1 0 1 0 1
],
)
@test QuantumLegos.eliminate_column!(cmat, 2, [1, nothing]) == 3
@test cmat.cmat == Bool[
1 0 1 1 1 0 0 0
0 0 1 1 0 1 0 1
0 1 1 0 1 0 1 1
0 0 1 1 1 1 1 0
]
end
let
cmat = QuantumLegos.CheckMatrix(
Bool[
1 0 0 0 1 1 0 1
0 1 0 1 0 1 1 0
0 1 0 0 0 1 0 0
1 1 0 1 0 0 0 1
],
)
@test QuantumLegos.eliminate_column!(cmat, 3, Int[]) === nothing
end
let
# random test (robustness)
function random_test()::Bool
test_mat = rand(Bool, (8, 16))
cmat = QuantumLegos.CheckMatrix(copy(test_mat))
keep_indices = (1:8)[rand(1:8, rand(0:8))]
keep_index = rand(1:16)
try
QuantumLegos.eliminate_column!(cmat, keep_index, keep_indices)
return true
catch e
# unexpected error
@error "Error on eliminate_column! with keep_index: $(keep_index), avoid: $(keep_indices), matrix:" cmat
@error "error: " e
return false
end
end
for i in 1:100
# @info i
@test random_test()
end
end
@testset "ArgumentError" begin
cmat = CheckMatrix(rand(Bool, (6, 12)))
@test_throws ArgumentError QuantumLegos.eliminate_column!(cmat, 3, [1, 7])
end
end
@testset "swap_row!" begin
let
m = rand(Bool, (10, 10))
m_copy = copy(m)
QuantumLegos.swap_row!(m, 3, 7)
QuantumLegos.swap_row!(m, 3, 7)
@test m == m_copy
end
let
m = rand(Bool, (10, 10))
m = BitMatrix(m)
m_copy = copy(m)
QuantumLegos.swap_row!(m, 4, 2)
QuantumLegos.swap_row!(m, 4, 2)
@test m == m_copy
end
end
@testset "align_row!" begin
mat = Bool[
1 0 1 1 0 0 1 0
0 1 0 1 0 1 0 0
1 1 1 0 0 1 0 0
1 0 0 1 0 1 0 0
]
mat_1 = copy(mat)
@test 1 == QuantumLegos.align_row!(mat_1, 3, Int64[])
@test mat_1 == Bool[
1 1 1 0 0 1 0 0
0 1 0 1 0 1 0 0
1 0 1 1 0 0 1 0
1 0 0 1 0 1 0 0
]
mat_1 = copy(mat)
@test 1 == QuantumLegos.align_row!(mat_1, 3, [nothing])
@test mat_1 == Bool[
1 1 1 0 0 1 0 0
0 1 0 1 0 1 0 0
1 0 1 1 0 0 1 0
1 0 0 1 0 1 0 0
]
mat_2 = copy(mat)
@test 2 == QuantumLegos.align_row!(mat_2, 3, [1])
@test mat_2 == Bool[
1 0 1 1 0 0 1 0
1 1 1 0 0 1 0 0
0 1 0 1 0 1 0 0
1 0 0 1 0 1 0 0
]
mat_3 = copy(mat)
@test 3 == QuantumLegos.align_row!(mat_3, 3, [1, nothing, 3])
@test mat_3 == Bool[
1 0 1 1 0 0 1 0
0 1 0 1 0 1 0 0
1 1 1 0 0 1 0 0 # 3
1 0 0 1 0 1 0 0 # 4
]
mat_3_2 = copy(mat_3)
@test 4 == QuantumLegos.align_row!(mat_3, 4, [1, nothing, 3])
@test mat_3 == mat_3_2
@test nothing === QuantumLegos.align_row!(mat_3, nothing, [1, 2])
@test nothing === QuantumLegos.align_row!(mat_3, nothing, [1, 2, nothing])
@test nothing === QuantumLegos.align_row!(mat_3, nothing, [nothing])
end
@testset "ref!" begin
@testset "compare with AbstractAlgebra" begin
# test using existing package
using AbstractAlgebra
F₂ = GF(2) # finite field
"""
Compare self-implemented `ref!` with AbstractAlgebra's `rref!` or `rank`.
"""
function random_test(size::Tuple{T, T}) where {T <: Integer}
mat = rand(Bool, size)
cmat = CheckMatrix(mat)
S = matrix_space(F₂, size...)
cmat_aa = S(F₂.(mat))
# r_aa, A_aa = AbstractAlgebra.rref(cmat_aa)
# cmat_aa = A_aa.entries .|> ==(1) |> Matrix{Bool} |> CheckMatrix
r_aa = rank(cmat_aa)
r = QuantumLegos.ref!(cmat)
# @info "compare cmat" cmat cmat_aa
@test r == r_aa
end
for _ in 1:10
random_test((2, 4))
random_test((4, 8))
random_test((100, 200))
end
end
@testset "manual sample" begin
import LinearAlgebra
mat =
Bool[1 0 0 1 1 0 0 0; 0 1 0 0 1 1 0 1; 0 0 1 1 0 1 0 1; 0 1 0 0 1 0 0 1]
cmat = CheckMatrix(mat)
@test QuantumLegos.ref!(cmat) == LinearAlgebra.rank(mat)
@test cmat == CheckMatrix(
Bool[
1 0 0 1 1 0 0 0
0 1 0 0 1 1 0 1
0 0 1 1 0 1 0 1
0 0 0 0 0 1 0 0
],
4,
4,
)
let
mat = copy(mat)
dependent_row = reduce(.⊻, eachrow(mat)[[1, 2]])
mat[4, :] = dependent_row
cmat = CheckMatrix(mat)
@test QuantumLegos.ref!(cmat) == LinearAlgebra.rank(mat) == 3
end
let
mat = copy(mat)
dependent_row = reduce(.⊻, eachrow(mat)[[1, 2]])
mat = vcat(mat, dependent_row')
dependent_row = reduce(.⊻, eachrow(mat)[[1, 2, 3]])
mat = vcat(mat, dependent_row')
dependent_row = reduce(.⊻, eachrow(mat)[[2, 4]])
mat = vcat(mat, dependent_row')
cmat = CheckMatrix(mat)
@test QuantumLegos.ref!(cmat) == rank(mat) == 3
end
end
@testset "generated group is invariant under ref!" begin
for _ in 1:10
cmat = CheckMatrix(rand(Bool, (8, 12)))
before = cmat |> generators |> GeneratedPauliGroup |> Set
QuantumLegos.ref!(cmat)
@test cmat |> generators |> GeneratedPauliGroup |> Set |> ==(before)
end
end
end
@testset "eliminate_dependent_row!" begin
@testset "trivial" begin
cmat = CheckMatrix(
Bool[
1 0 0 1 0 1 0 1
0 1 1 0 0 1 1 0
0 0 0 1 1 0 1 0
0 0 0 0 0 0 0 0
],
)
@test QuantumLegos.eliminate_dependent_row!(cmat) == CheckMatrix(
Bool[1 0 0 1 0 1 0 1; 0 1 1 0 0 1 1 0; 0 0 0 1 1 0 1 0],
4,
3,
)
end
@testset "less trivial" begin
cmat = CheckMatrix(
Bool[
1 0 0 1 0 1 0 1
0 1 1 0 0 1 1 0
0 0 0 1 1 0 1 0
1 1 1 1 0 0 1 1
],
)
@test QuantumLegos.eliminate_dependent_row!(cmat) == CheckMatrix(
Bool[1 0 0 1 0 1 0 1; 0 1 1 0 0 1 1 0; 0 0 0 1 1 0 1 0],
4,
3,
)
end
# TODO: add more?
end
@testset "self_trace!" begin
cmat = CheckMatrix(rand(Bool, (8, 16)))
@test_throws ArgumentError QuantumLegos.self_trace!(cmat, 16, 17)
@test_throws ArgumentError QuantumLegos.self_trace!(cmat, 18, 17)
end
end
@testset "State" begin
@testset "LegoLeg" begin
@test LegoLeg(0, 1) < LegoLeg(1, 0)
@test LegoLeg(2, 1) > LegoLeg(1, 0)
@test LegoLeg(1, 0) < LegoLeg(1, 1)
@test LegoLeg(1, 1) == LegoLeg(1, 1)
@test sort(LegoLeg.([(1, 2), (2, 3), (0, 2), (0, 1)])) ==
LegoLeg[LegoLeg(0, 1), LegoLeg(0, 2), LegoLeg(1, 2), LegoLeg(2, 3)]
end
@testset "edge" begin
@test edge(1, 2, 3, 4) == (LegoLeg(1, 2), LegoLeg(3, 4))
@test edge((1, 2, 3, 4)) == (LegoLeg(1, 2), LegoLeg(3, 4))
@test edge(((1, 2), (3, 4))) == (LegoLeg(1, 2), LegoLeg(3, 4))
end
@testset "0 lego, 0 leg" begin
@test_throws ArgumentError QuantumLegos.State(Lego{6}[], Tuple{LegoLeg, LegoLeg}[])
end
@testset "1 lego, 0 leg" begin
stabgens = pauliop.(["IIXX", "XXII", "IZZI", "ZIIZ"])
lego = QuantumLegos.Lego(stabgens)
state = QuantumLegos.State([lego], Tuple{LegoLeg, LegoLeg}[])
cmat = QuantumLegos.checkmatrix(stabgens)
@test state == QuantumLegos.State([lego], Tuple{LegoLeg, LegoLeg}[])
@test state.legos == [lego]
@test state.edges == Tuple{LegoLeg, LegoLeg}[]
@test state.cmat == cmat
end
@testset "2+ legos, 0 leg" begin
stabgens = pauliop.(["IIXX", "XXII", "IZZI", "ZIIZ"])
lego = QuantumLegos.Lego(stabgens)
state_1 = QuantumLegos.State([lego], Tuple{LegoLeg, LegoLeg}[])
add_lego!(state_1, lego)
@test state_1.legos == [lego, lego]
@test state_1.edges == Tuple{LegoLeg, LegoLeg}[]
@test state_1.cmat.ngens == 8
@test state_1.cmat.nlegs == 8
state_2 = QuantumLegos.State([lego, lego], Tuple{LegoLeg, LegoLeg}[])
@test state_1 == state_2
@test all(state_2.cmat.cmat[5:8, 1:4] .== false) # block diagonal
end
@testset "2+ legos, 1+ legs" begin
stabgens = pauliop.(["IIXX", "XXII", "IZZI", "ZIIZ"])
lego = QuantumLegos.Lego(stabgens)
@test_throws ArgumentError State([lego, lego], edge.([(1, 1, 1, 1)]))
end
@testset "is_connected_to_firstlego" begin
stabilizers = pauliop.(["IIXXXX", "IIZZZZ", "ZIZIZI", "IZIZIZ", "IXIIXX", "XIXXII"])
lego = Lego(stabilizers)
let
state =
State(fill(lego, 6), edge.([(3, 2, 1, 5), (5, 2, 3, 3), (4, 1, 5, 1)]))
@test QuantumLegos.is_connected_to_firstlego(state) ==
BitVector([1, 0, 1, 1, 1, 0])
end
let
state = State(
fill(lego, 7),
edge.([(3, 2, 1, 5), (5, 2, 3, 3), (4, 1, 5, 1), (2, 2, 3, 1)]),
)
@test QuantumLegos.is_connected_to_firstlego(state) ==
BitVector([1, 1, 1, 1, 1, 0, 0])
end
end
end
@testset "Doctest" begin
DocMeta.setdocmeta!(
QuantumLegos,
:DocTestSetup,
:(using QuantumLegos; ENV["JULIA_DEBUG"] = "");
recursive = true,
)
doctest(QuantumLegos)
end
# @testset "Code quality (Aqua.jl)" begin
# Aqua.test_all(QuantumLegos)
# end
# @testset "Code linting (JET.jl)" begin
# JET.test_package(QuantumLegos; target_defined_modules = true)
# end
end