From 2815a498f82e70eba3ea81f3fc6cbd8982a7e5ed Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Fri, 9 Aug 2024 15:58:21 +0500 Subject: [PATCH 1/3] Mutual Information I(A,B), improving doctests and documentation for entanglementjl --- docs/src/references.bib | 12 +-- src/QuantumClifford.jl | 2 +- src/entanglement.jl | 154 ++++++++++++++++++++++++++++++++++++-- test/test_entanglement.jl | 14 ++++ 4 files changed, 167 insertions(+), 15 deletions(-) diff --git a/docs/src/references.bib b/docs/src/references.bib index 6a50a9194..062f2743b 100644 --- a/docs/src/references.bib +++ b/docs/src/references.bib @@ -111,13 +111,13 @@ @article{gullans2020dynamical publisher={APS} } -@article{hein2006entanglement, +@incollection{hein2006entanglement, title={Entanglement in graph states and its applications}, - author={Hein, Marc and D{\"u}r, Wolfgang and Eisert, Jens and Raussendorf, Robert and Nest, M and Briegel, H-J}, - journal={arXiv preprint quant-ph/0602096}, - url={https://arxiv.org/abs/quant-ph/0602096}, - doi={10.48550/arXiv.quant-ph/0602096}, - year={2006} + author={Hein, Marc and D{\"u}r, Wolfgang and Eisert, Jens and Raussendorf, Robert and Van den Nest, Maarten and Briegel, H-J}, + booktitle={Quantum computers, algorithms and chaos}, + pages={115--218}, + year={2006}, + publisher={IOS Press} } @article{wilde2009logical, diff --git a/src/QuantumClifford.jl b/src/QuantumClifford.jl index d54ec03b6..49cf26f08 100644 --- a/src/QuantumClifford.jl +++ b/src/QuantumClifford.jl @@ -77,7 +77,7 @@ export # Group theory tools groupify, minimal_generating_set, pauligroup, normalizer, centralizer, contractor, delete_columns, # Clipped Gauge - canonicalize_clip!, bigram, entanglement_entropy, + canonicalize_clip!, bigram, entanglement_entropy, mutual_information, # mctrajectories CircuitStatus, continue_stat, true_success_stat, false_success_stat, failure_stat, mctrajectory!, mctrajectories, applywstatus!, diff --git a/src/entanglement.jl b/src/entanglement.jl index f4fd12b71..a2d3e2cfe 100644 --- a/src/entanglement.jl +++ b/src/entanglement.jl @@ -132,15 +132,40 @@ end """ $TYPEDSIGNATURES -Get the bigram of a tableau. +The Bigram `B` of stabilizer endpoints represents the "span" of each stabilizer within a set of Pauli operators `๐’ข = {gโ‚,โ€ฆ,gโ‚™}`. -It is the list of endpoints of a tableau in the clipped gauge. +For each stabilizer `g`, the left endpoint `๐“(g)` is defined as the minimum site `x` where `g` acts non-trivially, while the +right endpoint `๐“‡(g)` is the maximum site where `g` acts non-trivially. + +The site `x` represent the position within the system, taking values from `{1,2,โ€ฆ,n}` where `n` is the number of qubits. + +The bigram set `B(๐’ข)` encodes these endpoints as pairs: + +`B(๐’ข) โ‰ก {(๐“(gโ‚),๐“‡(gโ‚)),โ€ฆ,(๐“(gโ‚™),๐“‡(gโ‚™))}` + +The clipped gauge `๐’ข` is a specific choice of stabilizer state where exactly two stabilizer endpoints exist at each site, +ensuring `ฯ๐“(x) + ฯ๐“‡(x) = 2` for all sites `x`. + +In the clipped gauge, entanglement entropy is determined only by the stabilizers' endpoints, regardless of their internal structure. If `clip=true` (the default) the tableau is converted to the clipped gauge in-place before calculating the bigram. Otherwise, the clip gauge conversion is skipped (for cases where the input is already known to be in the correct gauge). Introduced in [nahum2017quantum](@cite), with a more detailed explanation of the algorithm in [li2019measurement](@cite) and [gullans2021quantum](@cite). +```jldoctest +julia> s = ghz(3) ++ XXX ++ ZZ_ ++ _ZZ + +julia> bigram(s) +3ร—2 Matrix{Int64}: + 1 3 + 1 2 + 2 3 +``` + See also: [`canonicalize_clip!`](@ref) """ function bigram(state::AbstractStabilizer; clip::Bool=true)::Matrix{Int} # JET-XXX The ::Matrix{Int} should not be necessary, but they help with inference @@ -162,24 +187,52 @@ end """ $TYPEDSIGNATURES -Get bipartite entanglement entropy of a subsystem +Get bipartite entanglement entropy of a subsystem `๐’ถ`. -Defined as entropy of the reduced density matrix. +In a system of `n`-qubits divided into regions `๐’ถ` and `๐’ท`, the Rรฉnyi (or von Neumann) +entropy `S(ฯ๐’ถ)` where `ฯ๐’ถ` is reduced density matrix, quantifies the quantum information +in region `๐’ถ` after tracing out region `๐’ท`. -It can be calculated with multiple different algorithms, -the most performant one depending on the particular case. +The entropy is given by: `S(ฯ๐’ถ) = |๐’ถ| - logโ‚‚ |๐’ข๐’ถ|`, where `๐’ข๐’ถ` is a subgroup of stabilizers +acting non-trivially on `๐’ถ`. This entropy measures the number of independent stabilizers +on `๐’ถ`, reflecting the quantum correlations between regions. + +The stabilizer group `๐’ข`, viewed as a binary vector space `V`, can be decomposed such that: +`S๐’ถ = |๐’ท| - dim V๐’ท = dim (ฮ ๐’ถ V) - |๐’ถ|` where `ฮ ๐’ถ` and `ฮ ๐’ท` are truncation maps for regions +`๐’ถ` and `๐’ท`, respectively. + +Initially, each site has one stabilizer and zero entanglement, but as stabilizers enter +region `๐’ถ` over time, entanglement `S๐’ถ` increases by one bit for each new independent +operator in `๐’ถ`. + +It can be calculated with multiple different algorithms, the most performant one depending +on the particular case. Currently implemented are the `:clip` (clipped gauge), `:graph` (graph state), and `:rref` (Gaussian elimination) algorithms. Benchmark your particular case to choose the best one. + +See Appendix C of [nahum2017quantum](@cite). """ function entanglement_entropy end """ +$TYPEDSIGNATURES + Get bipartite entanglement entropy of a contiguous subsystem by passing through the clipped gauge. If `clip=false` is set the canonicalization step is skipped, useful if the input state is already in the clipped gauge. +```jldoctest +julia> s = ghz(3) ++ XXX ++ ZZ_ ++ _ZZ + +julia> entanglement_entropy(s, 1:3, Val(:clip)) +0 +``` + See also: [`bigram`](@ref), [`canonicalize_clip!`](@ref) """ function entanglement_entropy(state::AbstractStabilizer, subsystem_range::UnitRange, algorithm::Val{:clip}; clip::Bool=true) @@ -193,10 +246,25 @@ end """ +$TYPEDSIGNATURES + Get bipartite entanglement entropy by first converting the state to a graph and computing the rank of the adjacency matrix. -Based on "Entanglement in graph states and its applications". -""" # TODO you should use [hein2006entanglement](@cite) instead of "Entanglement in graph states and its applications", but Documenter is giving the weirdest error if you do so... +```jldoctest +julia> using Graphs # hide + +julia> s = Stabilizer(Graph(ghz(4))) ++ XZZZ ++ ZX__ ++ Z_X_ ++ Z__X + +julia> entanglement_entropy(s, [1,4], Val(:graph)) +1 +``` + +Based on [hein2006entanglement](@ref). +""" function entanglement_entropy(state::AbstractStabilizer, subsystem::AbstractVector, algorithm::Val{:graph}) graph = Graphs.Graph(state) adjmat = Graphs.adjacency_matrix(graph) @@ -207,10 +275,25 @@ end """ +$TYPEDSIGNATURES + Get bipartite entanglement entropy by converting to RREF form (i.e., partial trace form). The state will be partially canonicalized in an RREF form. +```jldoctest +julia> s = MixedDestabilizer(T"-IX -YX -ZZ -ZI",2) +๐’Ÿโ„ฏ๐“ˆ๐“‰๐’ถ๐’ท +- _X +- YX +๐’ฎ๐“‰๐’ถ๐’ท +- ZZ +- Z_ + +julia> entanglement_entropy(s, [1,2], Val(:rref)) +0 +``` + See also: [`canonicalize_rref!`](@ref), [`traceout!`](@ref). """ function entanglement_entropy(state::AbstractStabilizer, subsystem::AbstractVector, algorithm::Val{:rref}; pure::Bool=false) @@ -227,4 +310,59 @@ function entanglement_entropy(state::AbstractStabilizer, subsystem::AbstractVect return nb_of_qubits - rank_after_deletion - nb_of_deletions end +""" +$TYPEDSIGNATURES +""" entanglement_entropy(state::MixedDestabilizer, subsystem::AbstractVector, a::Val{:rref}) = entanglement_entropy(state, subsystem, a; pure=nqubits(state)==rank(state)) + +_to_unitrange(x) = isa(x, UnitRange) ? x : isa(x, AbstractVector) && !isempty(x) && all(diff(x) .== 1) ? UnitRange(first(x), last(x)) : error("Cannot convert to UnitRange: $x") + +""" +$TYPEDSIGNATURES + +The mutual information between subsystems `๐’ถ` and `๐’ท` in a stabilizer state is +given by `Iโฟ(๐’ถ, ๐’ท) = Sโฟ๐’ถ + Sโฟ๐’ท - Sโฟ๐’ถ๐’ท`, where the Rรฉnyi index `n` is dropped because, for +Clifford circuits, all Renyi entropies are equal due to the flat entanglement spectrum. + +```jldoctest +julia> mutual_information(ghz(3), 1:2, 3:5, Val(:clip)) +2 +``` + +See Eq. E6 of [li2019measurement](@cite). +""" +function mutual_information(state::AbstractStabilizer, A::UnitRange, B::UnitRange, algorithm::Val{:clip}; clip::Bool=true) + S๐’ถ = entanglement_entropy(state, A, algorithm; clip=clip) + S๐’ท = entanglement_entropy(state, B, algorithm; clip=clip) + S๐’ถ๐’ท = entanglement_entropy(state, _to_unitrange(union(A, B)), algorithm; clip=clip) + return S๐’ถ + S๐’ท - S๐’ถ๐’ท +end + +""" +$TYPEDSIGNATURES + +```jldoctest +julia> using Graphs # hide + +julia> s = Stabilizer(Graph(ghz(4))) ++ XZZZ ++ ZX__ ++ Z_X_ ++ Z__X + +julia> mutual_information(s, [1,2], [3, 4], Val(:graph)) +0 +``` + +""" +function mutual_information(state::AbstractStabilizer, A::AbstractVector, B::AbstractVector, algorithm::Union{Val{:rref}, Val{:graph}}; pure::Bool=false) + if algorithm == Val(:graph) + S๐’ถ = entanglement_entropy(state, A, algorithm) + S๐’ท = entanglement_entropy(state, B, algorithm) + S๐’ถ๐’ท = entanglement_entropy(state, union(A, B), algorithm) + else + S๐’ถ = entanglement_entropy(state, A, algorithm; pure=pure) + S๐’ท = entanglement_entropy(state, B, algorithm; pure=pure) + S๐’ถ๐’ท = entanglement_entropy(state, union(A, B), algorithm; pure=pure) + end +end diff --git a/test/test_entanglement.jl b/test/test_entanglement.jl index fa50fb369..35c4c7c16 100644 --- a/test/test_entanglement.jl +++ b/test/test_entanglement.jl @@ -50,4 +50,18 @@ @test entanglement_entropy(copy(s), subsystem, Val(:graph))==2 @test entanglement_entropy(copy(s), subsystem, Val(:rref))==2 end + + @testset "Mutual Information for Clifford Circuits" begin + for n in test_sizes + s = random_stabilizer(n) + endpointsA = sort(rand(1:n, 2)) + subsystem_rangeA = endpointsA[1]:endpointsA[2] + startB = rand(subsystem_rangeA) + endB = rand(startB:n) + subsystem_rangeB = startB:endB + # The mutual information `Iโฟ(๐’ถ, ๐’ท) = Sโฟ๐’ถ + Sโฟ๐’ท - Sโฟ๐’ถ๐’ท for Clifford circuits is non-negative since n is 1 [li2019measurement](@cite). + @test mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:clip)) & mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:rref)) >= 0 + @test mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:graph)) >= 0 + end + end end From b616cddf7ab19fb50af14f3d019852874f754622 Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Fri, 9 Aug 2024 16:51:28 +0500 Subject: [PATCH 2/3] minor fix and improving tests --- src/entanglement.jl | 10 ++++++---- test/test_entanglement.jl | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/entanglement.jl b/src/entanglement.jl index a2d3e2cfe..954295270 100644 --- a/src/entanglement.jl +++ b/src/entanglement.jl @@ -263,8 +263,8 @@ julia> entanglement_entropy(s, [1,4], Val(:graph)) 1 ``` -Based on [hein2006entanglement](@ref). -""" +Based on "Entanglement in graph states and its applications". +""" # TODO you should use [hein2006entanglement](@cite) instead of "Entanglement in graph states and its applications", but Documenter is giving the weirdest error if you do so... function entanglement_entropy(state::AbstractStabilizer, subsystem::AbstractVector, algorithm::Val{:graph}) graph = Graphs.Graph(state) adjmat = Graphs.adjacency_matrix(graph) @@ -325,7 +325,7 @@ given by `Iโฟ(๐’ถ, ๐’ท) = Sโฟ๐’ถ + Sโฟ๐’ท - Sโฟ๐’ถ๐’ท`, where the Rรฉn Clifford circuits, all Renyi entropies are equal due to the flat entanglement spectrum. ```jldoctest -julia> mutual_information(ghz(3), 1:2, 3:5, Val(:clip)) +julia> mutual_information(ghz(3), 1:2, 3:4, Val(:clip)) 2 ``` @@ -351,7 +351,7 @@ julia> s = Stabilizer(Graph(ghz(4))) + Z__X julia> mutual_information(s, [1,2], [3, 4], Val(:graph)) -0 +2 ``` """ @@ -360,9 +360,11 @@ function mutual_information(state::AbstractStabilizer, A::AbstractVector, B::Abs S๐’ถ = entanglement_entropy(state, A, algorithm) S๐’ท = entanglement_entropy(state, B, algorithm) S๐’ถ๐’ท = entanglement_entropy(state, union(A, B), algorithm) + return S๐’ถ + S๐’ท - S๐’ถ๐’ท else S๐’ถ = entanglement_entropy(state, A, algorithm; pure=pure) S๐’ท = entanglement_entropy(state, B, algorithm; pure=pure) S๐’ถ๐’ท = entanglement_entropy(state, union(A, B), algorithm; pure=pure) + return S๐’ถ + S๐’ท - S๐’ถ๐’ท end end diff --git a/test/test_entanglement.jl b/test/test_entanglement.jl index 35c4c7c16..98bdf8617 100644 --- a/test/test_entanglement.jl +++ b/test/test_entanglement.jl @@ -59,6 +59,7 @@ startB = rand(subsystem_rangeA) endB = rand(startB:n) subsystem_rangeB = startB:endB + @test mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:clip)) == mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:rref)) == mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:graph)) # The mutual information `Iโฟ(๐’ถ, ๐’ท) = Sโฟ๐’ถ + Sโฟ๐’ท - Sโฟ๐’ถ๐’ท for Clifford circuits is non-negative since n is 1 [li2019measurement](@cite). @test mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:clip)) & mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:rref)) >= 0 @test mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:graph)) >= 0 From 80676a69554a5958168506444f75da410f405195 Mon Sep 17 00:00:00 2001 From: Fe-r-oz Date: Wed, 14 Aug 2024 20:37:13 +0500 Subject: [PATCH 3/3] adding codereview suggestions --- docs/src/references.bib | 12 ++-- src/entanglement.jl | 130 ++++++++++++++------------------------ test/test_entanglement.jl | 17 +++-- 3 files changed, 66 insertions(+), 93 deletions(-) diff --git a/docs/src/references.bib b/docs/src/references.bib index 062f2743b..6a50a9194 100644 --- a/docs/src/references.bib +++ b/docs/src/references.bib @@ -111,13 +111,13 @@ @article{gullans2020dynamical publisher={APS} } -@incollection{hein2006entanglement, +@article{hein2006entanglement, title={Entanglement in graph states and its applications}, - author={Hein, Marc and D{\"u}r, Wolfgang and Eisert, Jens and Raussendorf, Robert and Van den Nest, Maarten and Briegel, H-J}, - booktitle={Quantum computers, algorithms and chaos}, - pages={115--218}, - year={2006}, - publisher={IOS Press} + author={Hein, Marc and D{\"u}r, Wolfgang and Eisert, Jens and Raussendorf, Robert and Nest, M and Briegel, H-J}, + journal={arXiv preprint quant-ph/0602096}, + url={https://arxiv.org/abs/quant-ph/0602096}, + doi={10.48550/arXiv.quant-ph/0602096}, + year={2006} } @article{wilde2009logical, diff --git a/src/entanglement.jl b/src/entanglement.jl index 954295270..6a8321213 100644 --- a/src/entanglement.jl +++ b/src/entanglement.jl @@ -144,7 +144,8 @@ The bigram set `B(๐’ข)` encodes these endpoints as pairs: `B(๐’ข) โ‰ก {(๐“(gโ‚),๐“‡(gโ‚)),โ€ฆ,(๐“(gโ‚™),๐“‡(gโ‚™))}` The clipped gauge `๐’ข` is a specific choice of stabilizer state where exactly two stabilizer endpoints exist at each site, -ensuring `ฯ๐“(x) + ฯ๐“‡(x) = 2` for all sites `x`. +ensuring `ฯโ‚—(x) + ฯแตฃ(x) = 2` for all sites `x` where `ฯ` represents the reduced density matrix for the subsystem under +consideration. In the clipped gauge, entanglement entropy is determined only by the stabilizers' endpoints, regardless of their internal structure. @@ -187,26 +188,12 @@ end """ $TYPEDSIGNATURES -Get bipartite entanglement entropy of a subsystem `๐’ถ`. +Get bipartite entanglement entropy of a subsystem -In a system of `n`-qubits divided into regions `๐’ถ` and `๐’ท`, the Rรฉnyi (or von Neumann) -entropy `S(ฯ๐’ถ)` where `ฯ๐’ถ` is reduced density matrix, quantifies the quantum information -in region `๐’ถ` after tracing out region `๐’ท`. +Defined as entropy of the reduced density matrix. -The entropy is given by: `S(ฯ๐’ถ) = |๐’ถ| - logโ‚‚ |๐’ข๐’ถ|`, where `๐’ข๐’ถ` is a subgroup of stabilizers -acting non-trivially on `๐’ถ`. This entropy measures the number of independent stabilizers -on `๐’ถ`, reflecting the quantum correlations between regions. - -The stabilizer group `๐’ข`, viewed as a binary vector space `V`, can be decomposed such that: -`S๐’ถ = |๐’ท| - dim V๐’ท = dim (ฮ ๐’ถ V) - |๐’ถ|` where `ฮ ๐’ถ` and `ฮ ๐’ท` are truncation maps for regions -`๐’ถ` and `๐’ท`, respectively. - -Initially, each site has one stabilizer and zero entanglement, but as stabilizers enter -region `๐’ถ` over time, entanglement `S๐’ถ` increases by one bit for each new independent -operator in `๐’ถ`. - -It can be calculated with multiple different algorithms, the most performant one depending -on the particular case. +It can be calculated with multiple different algorithms, +the most performant one depending on the particular case. Currently implemented are the `:clip` (clipped gauge), `:graph` (graph state), and `:rref` (Gaussian elimination) algorithms. Benchmark your particular case to choose the best one. @@ -224,6 +211,8 @@ Get bipartite entanglement entropy of a contiguous subsystem by passing through If `clip=false` is set the canonicalization step is skipped, useful if the input state is already in the clipped gauge. ```jldoctest +julia> using Graphs # hide + julia> s = ghz(3) + XXX + ZZ_ @@ -231,6 +220,15 @@ julia> s = ghz(3) julia> entanglement_entropy(s, 1:3, Val(:clip)) 0 + +julia> s = Stabilizer(Graph(ghz(4))) ++ XZZZ ++ ZX__ ++ Z_X_ ++ Z__X + +julia> entanglement_entropy(s, [1,4], Val(:graph)) +1 ``` See also: [`bigram`](@ref), [`canonicalize_clip!`](@ref) @@ -250,19 +248,6 @@ $TYPEDSIGNATURES Get bipartite entanglement entropy by first converting the state to a graph and computing the rank of the adjacency matrix. -```jldoctest -julia> using Graphs # hide - -julia> s = Stabilizer(Graph(ghz(4))) -+ XZZZ -+ ZX__ -+ Z_X_ -+ Z__X - -julia> entanglement_entropy(s, [1,4], Val(:graph)) -1 -``` - Based on "Entanglement in graph states and its applications". """ # TODO you should use [hein2006entanglement](@cite) instead of "Entanglement in graph states and its applications", but Documenter is giving the weirdest error if you do so... function entanglement_entropy(state::AbstractStabilizer, subsystem::AbstractVector, algorithm::Val{:graph}) @@ -281,20 +266,7 @@ Get bipartite entanglement entropy by converting to RREF form (i.e., partial tra The state will be partially canonicalized in an RREF form. -```jldoctest -julia> s = MixedDestabilizer(T"-IX -YX -ZZ -ZI",2) -๐’Ÿโ„ฏ๐“ˆ๐“‰๐’ถ๐’ท -- _X -- YX -๐’ฎ๐“‰๐’ถ๐’ท -- ZZ -- Z_ - -julia> entanglement_entropy(s, [1,2], Val(:rref)) -0 -``` - -See also: [`canonicalize_rref!`](@ref), [`traceout!`](@ref). +See also: [`canonicalize_rref!`](@ref), [`traceout!`](@ref), [`mutual_information`](@ref) """ function entanglement_entropy(state::AbstractStabilizer, subsystem::AbstractVector, algorithm::Val{:rref}; pure::Bool=false) nb_of_qubits = nqubits(state) @@ -310,39 +282,18 @@ function entanglement_entropy(state::AbstractStabilizer, subsystem::AbstractVect return nb_of_qubits - rank_after_deletion - nb_of_deletions end -""" -$TYPEDSIGNATURES -""" entanglement_entropy(state::MixedDestabilizer, subsystem::AbstractVector, a::Val{:rref}) = entanglement_entropy(state, subsystem, a; pure=nqubits(state)==rank(state)) -_to_unitrange(x) = isa(x, UnitRange) ? x : isa(x, AbstractVector) && !isempty(x) && all(diff(x) .== 1) ? UnitRange(first(x), last(x)) : error("Cannot convert to UnitRange: $x") - """ $TYPEDSIGNATURES -The mutual information between subsystems `๐’ถ` and `๐’ท` in a stabilizer state is -given by `Iโฟ(๐’ถ, ๐’ท) = Sโฟ๐’ถ + Sโฟ๐’ท - Sโฟ๐’ถ๐’ท`, where the Rรฉnyi index `n` is dropped because, for -Clifford circuits, all Renyi entropies are equal due to the flat entanglement spectrum. +The mutual information between subsystems `๐’ถ` and `๐’ท` in a stabilizer state is given by `I(๐’ถ, ๐’ท) = S๐’ถ + S๐’ท - S๐’ถ๐’ท`. ```jldoctest +julia> using Graphs # hide + julia> mutual_information(ghz(3), 1:2, 3:4, Val(:clip)) 2 -``` - -See Eq. E6 of [li2019measurement](@cite). -""" -function mutual_information(state::AbstractStabilizer, A::UnitRange, B::UnitRange, algorithm::Val{:clip}; clip::Bool=true) - S๐’ถ = entanglement_entropy(state, A, algorithm; clip=clip) - S๐’ท = entanglement_entropy(state, B, algorithm; clip=clip) - S๐’ถ๐’ท = entanglement_entropy(state, _to_unitrange(union(A, B)), algorithm; clip=clip) - return S๐’ถ + S๐’ท - S๐’ถ๐’ท -end - -""" -$TYPEDSIGNATURES - -```jldoctest -julia> using Graphs # hide julia> s = Stabilizer(Graph(ghz(4))) + XZZZ @@ -354,17 +305,34 @@ julia> mutual_information(s, [1,2], [3, 4], Val(:graph)) 2 ``` +See Eq. E6 of [li2019measurement](@cite). See also: [`entanglement_entropy`](@ref) """ -function mutual_information(state::AbstractStabilizer, A::AbstractVector, B::AbstractVector, algorithm::Union{Val{:rref}, Val{:graph}}; pure::Bool=false) - if algorithm == Val(:graph) - S๐’ถ = entanglement_entropy(state, A, algorithm) - S๐’ท = entanglement_entropy(state, B, algorithm) - S๐’ถ๐’ท = entanglement_entropy(state, union(A, B), algorithm) - return S๐’ถ + S๐’ท - S๐’ถ๐’ท - else - S๐’ถ = entanglement_entropy(state, A, algorithm; pure=pure) - S๐’ท = entanglement_entropy(state, B, algorithm; pure=pure) - S๐’ถ๐’ท = entanglement_entropy(state, union(A, B), algorithm; pure=pure) - return S๐’ถ + S๐’ท - S๐’ถ๐’ท +function mutual_information(state::AbstractStabilizer, A::UnitRange, B::UnitRange, algorithm::Val{:clip}; clip::Bool=true) + if !isempty(intersect(A, B)) + throw(ArgumentError("Ranges A and B must not overlap.")) + end + S๐’ถ = entanglement_entropy(state, A, algorithm; clip=clip) + S๐’ท = entanglement_entropy(state, B, algorithm; clip=clip) + S๐’ถ๐’ท = entanglement_entropy(state, UnitRange(first(union(A, B)), last(union(A, B))), algorithm; clip=clip) + return S๐’ถ + S๐’ท - S๐’ถ๐’ท +end + +function mutual_information(state::AbstractStabilizer, A::AbstractVector, B::AbstractVector, algorithm::Val{:rref}; pure::Bool=false) + if !isempty(intersect(A, B)) + throw(ArgumentError("Ranges A and B must not overlap.")) + end + S๐’ถ = entanglement_entropy(state, A, algorithm; pure=pure) + S๐’ท = entanglement_entropy(state, B, algorithm; pure=pure) + S๐’ถ๐’ท = entanglement_entropy(state, union(A, B), algorithm; pure=pure) + return S๐’ถ + S๐’ท - S๐’ถ๐’ท +end + +function mutual_information(state::AbstractStabilizer, A::AbstractVector, B::AbstractVector, algorithm::Val{:graph}) + if !isempty(intersect(A, B)) + throw(ArgumentError("Ranges A and B must not overlap.")) end + S๐’ถ = entanglement_entropy(state, A, algorithm) + S๐’ท = entanglement_entropy(state, B, algorithm) + S๐’ถ๐’ท = entanglement_entropy(state, union(A, B), algorithm) + return S๐’ถ + S๐’ท - S๐’ถ๐’ท end diff --git a/test/test_entanglement.jl b/test/test_entanglement.jl index 98bdf8617..ba5dab390 100644 --- a/test/test_entanglement.jl +++ b/test/test_entanglement.jl @@ -50,8 +50,8 @@ @test entanglement_entropy(copy(s), subsystem, Val(:graph))==2 @test entanglement_entropy(copy(s), subsystem, Val(:rref))==2 end - - @testset "Mutual Information for Clifford Circuits" begin + + @testset "Mutual information for Clifford circuits" begin for n in test_sizes s = random_stabilizer(n) endpointsA = sort(rand(1:n, 2)) @@ -59,10 +59,15 @@ startB = rand(subsystem_rangeA) endB = rand(startB:n) subsystem_rangeB = startB:endB - @test mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:clip)) == mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:rref)) == mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:graph)) - # The mutual information `Iโฟ(๐’ถ, ๐’ท) = Sโฟ๐’ถ + Sโฟ๐’ท - Sโฟ๐’ถ๐’ท for Clifford circuits is non-negative since n is 1 [li2019measurement](@cite). - @test mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:clip)) & mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:rref)) >= 0 - @test mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:graph)) >= 0 + if !isempty(intersect(subsystem_rangeA, subsystem_rangeB)) + @test_throws ArgumentError mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:clip)) + @test_throws ArgumentError mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:rref)) + @test_throws ArgumentError mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:graph)) + else + @test mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:clip)) == mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:rref)) == mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:graph)) + # The mutual information `I(๐’ถ, ๐’ท) = S๐’ถ + S๐’ท - S๐’ถ๐’ท for Clifford circuits is non-negative [li2019measurement](@cite). + @test mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:clip)) & mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:rref)) & mutual_information(copy(s), subsystem_rangeA, subsystem_rangeB, Val(:graph)) >= 0 + end end end end