Skip to content

Commit e550019

Browse files
committed
init: graph state Pauli measurement
1 parent 1912c8c commit e550019

File tree

4 files changed

+184
-2
lines changed

4 files changed

+184
-2
lines changed

src/graphs/cphase.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ Perform local complementation on pure graph
4545
"""
4646
function local_comp!(g, id::Int)
4747
# inneighbors or outneighbors shouldn't really matter because the graph is undirected
48+
# NOTE: Here we don't need to copy nghbr even though it returns a reference because local complementation guarantees not changing it
4849
nghbr = inneighbors(g, id)
4950
for i1 in eachindex(nghbr)
5051
for i2 in i1+1:length(nghbr)

src/graphs/graphs.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export graphstate, graphstate!, graph_gatesequence, graph_gate
55
import Graphs: Graphs, nv, AbstractGraph
66
using QuantumClifford: AbstractStabilizer, AbstractSingleQubitOperator,
77
sId1, sInvPhase, sSQRTX, CliffordOperator, SingleQubitOperator,
8-
Stabilizer, sZ, sPhase, sX, sY, sHadamard, sInvSQRTX, @S_str, stabilizerview,
8+
Stabilizer, sZ, sPhase, sX, sY, sHadamard, sInvSQRTX, sInvSQRTY, sSQRTY, @S_str, stabilizerview,
99
canonicalize_gott!, phases, sCPHASE, affectedqubits, canonicalize!, tab
1010
import QuantumClifford: nqubits, apply!
1111

@@ -282,5 +282,6 @@ end
282282

283283
include("./two_qubits_table.jl")
284284
include("./cphase.jl")
285+
include("./project.jl")
285286

286-
end #module
287+
end #module

src/graphs/project.jl

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# Some documentation for myself:
2+
# In line with the existing definition
3+
# - `projectrand!(gs::GraphState, m::sMX)` doesn't care about the register/classical bit
4+
# - `apply!(state::Register, m::sMX)` does, and it depends on `projectrand!`
5+
# And these should be the only public facing APIs for now
6+
7+
function remove_neighbors!(g, qubit::Int)
8+
for nghbr in copy(inneighbors(g, qubit))
9+
rem_edge!(g, qubit, nghbr)
10+
end
11+
end
12+
13+
"""
14+
Project a pure graph state (all VOPs are identity) onto eigenspaces of the Z observable.
15+
"""
16+
function _projectZ_pure_graph!(state::GraphState, qubit::Int, res::Bool)
17+
# For a = qubit to measure, b ∈ nghbrs(a)
18+
# Cₐ -> Cₐ * Xₐ^res * Hₐ
19+
# Cᵦ -> Cᵦ * Zᵦ^res
20+
# And remove all edeges between a and its neighbors
21+
g = graph(state)
22+
v = vops(state)
23+
24+
# Tweak VOPs
25+
if res
26+
_apply_vop_right!(v, sX(qubit))
27+
end
28+
_apply_vop_right!(v, sHadamard(qubit))
29+
for nghbr in inneighbors(g, qubit)
30+
if res
31+
_apply_vop_right!(v, sZ(nghbr))
32+
end
33+
end
34+
35+
# Modify graph
36+
remove_neighbors!(g, qubit)
37+
38+
return state
39+
end
40+
41+
"""
42+
Project a pure graph state onto eigenspaces of the Y observable.
43+
"""
44+
function _projectY_pure_graph!(state::GraphState, qubit::Int, res::Bool)
45+
g = graph(state)
46+
v = vops(state)
47+
48+
function tweak_vop(q)
49+
if res
50+
_apply_vop_right!(v, sInvPhase(q))
51+
else
52+
_apply_vop_right!(v, sPhase(q))
53+
end
54+
end
55+
56+
# Tweak VOPs of target qubit and its neighbors
57+
tweak_vop(qubit)
58+
for b in inneighbors(g, qubit)
59+
tweak_vop(b)
60+
end
61+
62+
# perform local complementation on edge
63+
local_comp!(g, qubit)
64+
65+
# remove the neighbors of the target qubit, this step is missing in the paper
66+
remove_neighbors!(g, qubit)
67+
68+
return state
69+
end
70+
71+
"""
72+
Project a pure graph state onto eigenspaces of X observable.
73+
"""
74+
function _projectX_pure_graph!(state::GraphState, qubit::Int, res::Bool)
75+
g = graph(state)
76+
v = vops(state)
77+
a = qubit
78+
79+
# Cₐ -> Cₐ Z^res
80+
if res
81+
_apply_vop_right!(v, sZ(a))
82+
end
83+
84+
nghbrs_a = copy(inneighbors(g, a))
85+
if !isempty(nghbrs_a)
86+
b = nghbrs_a[1]
87+
nghbrs_b = copy(inneighbors(g, b))
88+
nghbr_a_and_b = intersect(nghbrs_a, nghbrs_b)
89+
90+
# tweak the VOPs first
91+
# C_b -> C_b (sInvSQRTY)(†) where † is applied only if res is true
92+
if res
93+
_apply_vop_right!(v, sSQRTY(b))
94+
else
95+
_apply_vop_right!(v, sInvSQRTY(b))
96+
end
97+
98+
if res
99+
# nghbr(b) \ nghbr(a) \ {a}
100+
for c in setdiff(nghbrs_b, nghbrs_a, a)
101+
_apply_vop_right!(v, sZ(c))
102+
end
103+
else
104+
# nghbr(a) \ nghbr(b) \ {b}
105+
for c in setdiff(nghbrs_a, nghbrs_b, b)
106+
_apply_vop_right!(v, sZ(c))
107+
end
108+
end
109+
110+
# complementation on edges {{c, d} | c ∈ nghbr(a), d ∈ nghbr(b)}
111+
for (c, d) in Iterators.product(nghbrs_a, nghbrs_b)
112+
if (c nghbrs_b) && (d nghbrs_a)
113+
if c < d
114+
toggle_edge!(g, c, d)
115+
end
116+
else
117+
toggle_edge!(g, c, d)
118+
end
119+
end
120+
121+
# complementation on edges {{c, d} | c, d ∈ nghbr(a) ∩ nghbr(b)}
122+
for c in eachindex(nghbr_a_and_b)
123+
for d in c+1:length(nghbr_a_and_b)
124+
toggle_edge!(g, nghbr_a_and_b[c], nghbr_a_and_b[d])
125+
end
126+
end
127+
128+
# complement on edges {{b, d} | d ∈ nghbr(a) \ {b}}
129+
for d in nghbrs_a[2:end]
130+
toggle_edge!(g, b, d)
131+
end
132+
133+
remove_neighbors!(g, a)
134+
end
135+
return state
136+
end
137+
138+
function _project_graph!(state::GraphState, qubit::Int, op::Stabilizer, res::Bool)
139+
# Cₐ† opₐ Cₐ
140+
op_prime = apply!(op, inv(vops(state)[qubit]))
141+
142+
ispositive = (phases(op_prime)[1]) != 0x02
143+
# normalize the phase to match later
144+
phases(op_prime)[1] = 0x00
145+
res_tilde = ispositive ? res : !res
146+
147+
if op_prime == S"Z"
148+
_projectZ_pure_graph!(state, qubit, res_tilde)
149+
elseif op_prime == S"X"
150+
_projectX_pure_graph!(state, qubit, res_tilde)
151+
elseif op_prime == S"Y"
152+
_projectY_pure_graph!(state, qubit, res_tilde)
153+
end
154+
155+
return state
156+
end

test/test_graphs_project.jl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
@testitem "Graph states single-qubit Pauli measurements" begin
2+
using Random
3+
import QuantumClifford.GraphSim: _project_graph!, GraphState
4+
5+
test_sizes = [2,10,63,64,65,127,128,129]
6+
iteration = 1000
7+
8+
@testset "X measurement" begin
9+
for n in test_sizes
10+
s = random_stabilizer(n)
11+
for _ in 1:iteration
12+
g = GraphState(s)
13+
q = rand(1:n)
14+
# println("state: ", s)
15+
# println("Graph state: ", Stabilizer(g))
16+
# println("qubit: ", q)
17+
_, res = projectXrand!(s, q)
18+
# println("res: ", res == 0x02)
19+
_project_graph!(g, q, S"X", res == 0x02)
20+
@test canonicalize!(Stabilizer(g)) == canonicalize!(copy(s))
21+
end
22+
end
23+
end
24+
end

0 commit comments

Comments
 (0)