Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/graphs/cphase.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Perform local complementation on pure graph
"""
function local_comp!(g, id::Int)
# inneighbors or outneighbors shouldn't really matter because the graph is undirected
# NOTE: Here we don't need to copy nghbr even though it returns a reference because local complementation guarantees not changing it
nghbr = inneighbors(g, id)
for i1 in eachindex(nghbr)
for i2 in i1+1:length(nghbr)
Expand Down
5 changes: 3 additions & 2 deletions src/graphs/graphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export graphstate, graphstate!, graph_gatesequence, graph_gate
import Graphs: Graphs, nv, AbstractGraph
using QuantumClifford: AbstractStabilizer, AbstractSingleQubitOperator,
sId1, sInvPhase, sSQRTX, CliffordOperator, SingleQubitOperator,
Stabilizer, sZ, sPhase, sX, sY, sHadamard, sInvSQRTX, @S_str, stabilizerview,
Stabilizer, sZ, sPhase, sX, sY, sHadamard, sInvSQRTX, sInvSQRTY, sSQRTY, @S_str, stabilizerview,
canonicalize_gott!, phases, sCPHASE, affectedqubits, canonicalize!, tab
import QuantumClifford: nqubits, apply!

Expand Down Expand Up @@ -282,5 +282,6 @@ end

include("./two_qubits_table.jl")
include("./cphase.jl")
include("./project.jl")

end #module
end #module
156 changes: 156 additions & 0 deletions src/graphs/project.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# Some documentation for myself:
# In line with the existing definition
# - `projectrand!(gs::GraphState, m::sMX)` doesn't care about the register/classical bit
# - `apply!(state::Register, m::sMX)` does, and it depends on `projectrand!`
# And these should be the only public facing APIs for now

function remove_neighbors!(g, qubit::Int)
for nghbr in copy(inneighbors(g, qubit))
rem_edge!(g, qubit, nghbr)
end
end

"""
Project a pure graph state (all VOPs are identity) onto eigenspaces of the Z observable.
"""
function _projectZ_pure_graph!(state::GraphState, qubit::Int, res::Bool)
# For a = qubit to measure, b ∈ nghbrs(a)
# Cₐ -> Cₐ * Xₐ^res * Hₐ
# Cᵦ -> Cᵦ * Zᵦ^res
# And remove all edeges between a and its neighbors
g = graph(state)
v = vops(state)

# Tweak VOPs
if res
_apply_vop_right!(v, sX(qubit))
end
_apply_vop_right!(v, sHadamard(qubit))
for nghbr in inneighbors(g, qubit)
if res
_apply_vop_right!(v, sZ(nghbr))
end
end

# Modify graph
remove_neighbors!(g, qubit)

return state
end

"""
Project a pure graph state onto eigenspaces of the Y observable.
"""
function _projectY_pure_graph!(state::GraphState, qubit::Int, res::Bool)
g = graph(state)
v = vops(state)

function tweak_vop(q)
if res
_apply_vop_right!(v, sInvPhase(q))
else
_apply_vop_right!(v, sPhase(q))
end
end

# Tweak VOPs of target qubit and its neighbors
tweak_vop(qubit)
for b in inneighbors(g, qubit)
tweak_vop(b)
end

# perform local complementation on edge
local_comp!(g, qubit)

# remove the neighbors of the target qubit, this step is missing in the paper
remove_neighbors!(g, qubit)

return state
end

"""
Project a pure graph state onto eigenspaces of X observable.
"""
function _projectX_pure_graph!(state::GraphState, qubit::Int, res::Bool)
g = graph(state)
v = vops(state)
a = qubit

# Cₐ -> Cₐ Z^res
if res
_apply_vop_right!(v, sZ(a))
end

nghbrs_a = copy(inneighbors(g, a))
if !isempty(nghbrs_a)
b = nghbrs_a[1]
nghbrs_b = copy(inneighbors(g, b))
nghbr_a_and_b = intersect(nghbrs_a, nghbrs_b)

# tweak the VOPs first
# C_b -> C_b (sInvSQRTY)(†) where † is applied only if res is true
if res
_apply_vop_right!(v, sSQRTY(b))
else
_apply_vop_right!(v, sInvSQRTY(b))
end

if res
# nghbr(b) \ nghbr(a) \ {a}
for c in setdiff(nghbrs_b, nghbrs_a, a)
_apply_vop_right!(v, sZ(c))
end
else
# nghbr(a) \ nghbr(b) \ {b}
for c in setdiff(nghbrs_a, nghbrs_b, b)
_apply_vop_right!(v, sZ(c))
end
end

# complementation on edges {{c, d} | c ∈ nghbr(a), d ∈ nghbr(b)}
for (c, d) in Iterators.product(nghbrs_a, nghbrs_b)
if (c ∈ nghbrs_b) && (d ∈ nghbrs_a)
if c < d
toggle_edge!(g, c, d)
end
else
toggle_edge!(g, c, d)
end
end

# complementation on edges {{c, d} | c, d ∈ nghbr(a) ∩ nghbr(b)}
for c in eachindex(nghbr_a_and_b)
for d in c+1:length(nghbr_a_and_b)
toggle_edge!(g, nghbr_a_and_b[c], nghbr_a_and_b[d])
end
end

# complement on edges {{b, d} | d ∈ nghbr(a) \ {b}}
for d in nghbrs_a[2:end]
toggle_edge!(g, b, d)
end

remove_neighbors!(g, a)
end
return state
end

function _project_graph!(state::GraphState, qubit::Int, op::Stabilizer, res::Bool)
# Cₐ† opₐ Cₐ
op_prime = apply!(op, inv(vops(state)[qubit]))

ispositive = (phases(op_prime)[1]) != 0x02
# normalize the phase to match later
phases(op_prime)[1] = 0x00
res_tilde = ispositive ? res : !res

if op_prime == S"Z"
_projectZ_pure_graph!(state, qubit, res_tilde)
elseif op_prime == S"X"
_projectX_pure_graph!(state, qubit, res_tilde)
elseif op_prime == S"Y"
_projectY_pure_graph!(state, qubit, res_tilde)
end

return state
end
24 changes: 24 additions & 0 deletions test/test_graphs_project.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@testitem "Graph states single-qubit Pauli measurements" begin
using Random
import QuantumClifford.GraphSim: _project_graph!, GraphState

test_sizes = [2,10,63,64,65,127,128,129]
iteration = 1000

@testset "X measurement" begin
for n in test_sizes
s = random_stabilizer(n)
for _ in 1:iteration
g = GraphState(s)
q = rand(1:n)
# println("state: ", s)
# println("Graph state: ", Stabilizer(g))
# println("qubit: ", q)
_, res = projectXrand!(s, q)
# println("res: ", res == 0x02)
_project_graph!(g, q, S"X", res == 0x02)
@test canonicalize!(Stabilizer(g)) == canonicalize!(copy(s))
end
end
end
end
Loading