Skip to content

Commit e3e84d8

Browse files
committed
initial commit
1 parent 3a5f82a commit e3e84d8

File tree

3 files changed

+159
-0
lines changed

3 files changed

+159
-0
lines changed

src/QuantumClifford.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ export
9191
mctrajectory!, mctrajectories, applywstatus!,
9292
# petrajectories
9393
petrajectories, applybranches,
94+
# backtrajectory
95+
backtrajectory,
9496
# nonclifford
9597
GeneralizedStabilizer, UnitaryPauliChannel, PauliChannel, pcT, pcPhase,
9698
# makie plotting -- defined only when extension is loaded
@@ -1435,6 +1437,7 @@ include("linalg.jl")
14351437
include("operator_traits.jl")
14361438
include("mctrajectory.jl")
14371439
include("petrajectory.jl")
1440+
include("backtrajectory.jl")
14381441
include("misc_ops.jl")
14391442
include("classical_register.jl")
14401443
include("noise.jl")

src/backtrajectory.jl

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
"""
2+
Simulates measurement results of a Clifford circuit acting on an `n`-qubit |0⟩^⊗n state using the stabilizer tableau backtracking method,
3+
as described by Gidney (2021).
4+
5+
This method incrementally folds operations into an identity tableau by prepending inverses of Clifford gates. Pauli-Z measurements are
6+
resolved by transforming their observables to the initial state; deterministic measurements are directly computed from tableau signs,
7+
while random measurements are simplified and simulated with randomized gate insertions.
8+
9+
Reference:
10+
Gidney, C. (2021). Stim: A fast stabilizer circuit simulator. *Quantum*, 5, 497. https://doi.org/10.22331/q-2021-07-06-497
11+
"""
12+
function backtrajectory(circuit::Vector{<:AbstractOperation}, n::Int)
13+
T = one(CliffordOperator, n)
14+
results = Int8[]
15+
16+
for op in circuit
17+
if op isa AbstractCliffordOperator
18+
apply_right!(T, op)
19+
elseif op isa sMX
20+
push!(results, do_MX!(T, op))
21+
# elseif op isa sMY
22+
# push!(results, do_MY!(T, op))
23+
elseif op isa sMZ
24+
push!(results, do_MZ!(T, op))
25+
else
26+
error("Unsupported operation: $(typeof(op))")
27+
end
28+
end
29+
30+
return results
31+
end
32+
33+
34+
# function do_MY!(T, op::sMY)
35+
# collapse_y!(T, op.qubit)
36+
# return eval_y_obs(T, q)
37+
# end
38+
39+
# function collapse_y!(T, q::Int)
40+
# if is_deterministic_y(T, q)
41+
# return
42+
# end
43+
44+
# apply!(T, sHadamardYZ(q); phases=true)
45+
# collapse_z!(T, q)
46+
# apply!(T, sHadamardYZ(q); phases=true)
47+
# end
48+
49+
# function eval_y_obs(T, q::Int)
50+
# result = T[q]
51+
# log_i = mul_right!(result, T[nqubits(T)+q])
52+
# log_i += 1
53+
# @assert log_i & 1 == 0
54+
# if log_i & 2
55+
# result.phase[]
56+
# end
57+
# return result
58+
# end
59+
60+
function do_MX!(T, op::sMX)
61+
collapse_x!(T, op.qubit)
62+
return phases(tab(T))[op.qubit] == 0x00 ? 1 : -1
63+
end
64+
65+
# function do_MRX!(T, op::sMRX)
66+
# collapse_x!(T, op.qubit)
67+
# result = phases(tab(T))[op.qubit] == 0x00 ? 1 : -1
68+
# # change the signs to zero
69+
# return result
70+
# end
71+
72+
function collapse_x!(T, q::Int)
73+
if is_deterministic_x(T, q)
74+
return
75+
end
76+
77+
apply!(T, sHadamard(q); phases=true)
78+
collapse_z!(T, q)
79+
apply!(T, sHadamard(q); phases=true)
80+
end
81+
82+
function do_MZ!(T, op::sMZ)
83+
collapse_z!(T, op.qubit)
84+
return phases(tab(T))[op.qubit+nqubits(T)] == 0x00 ? 1 : -1
85+
end
86+
87+
function collapse_z!(T, q::Int)
88+
if is_deterministic_z(T, q)
89+
return
90+
end
91+
92+
n = nqubits(T)
93+
t = tab(T)
94+
95+
# Search for any stabilizer generator that anti-commutes with the measurement observable.
96+
pivot = 1
97+
while pivot <= n && getxbit(t, n+q, pivot) == 0
98+
pivot += 1
99+
end
100+
if pivot == n+1
101+
# No anti-commuting stabilizer generator. Measurement is deterministic.
102+
return -1
103+
end
104+
105+
# Perform partial Gaussian elimination over the stabilizer generators that anti-commute with the measurement.
106+
# Do this by introducing no-effect-because-control-is-zero CNOTs at the beginning of time.
107+
for k in pivot+1:n
108+
if getxbit(t, n+q, k) > 0
109+
apply!(T, sCNOT(pivot, k); phases=true)
110+
end
111+
end
112+
113+
# Swap the now-isolated anti-commuting stabilizer generator for one that commutes with the measurement.
114+
if getzbit(t, n+q, pivot) == 0
115+
apply!(T, sHadamard(pivot); phases=true)
116+
else
117+
apply!(T, sHadamardYZ(pivot); phases=true)
118+
end
119+
120+
return pivot
121+
end
122+
123+
@inline is_deterministic_x(T, q::Int) = all(getxbytes(T, q) .== 0)
124+
@inline is_deterministic_y(T, q::Int) = all(getxbytes(T, q) .== getxbytes(T, nqubits(T)+q))
125+
@inline is_deterministic_z(T, q::Int) = all(getxbytes(T, nqubits(T)+q) .== 0)
126+
127+
@inline getxbytes(T, r) = tab(T).xzs[1:2:end,r]
128+
@inline getzbytes(T, r) = tab(T).xzs[2:2:end,r]
129+
130+
131+
# function backtrajectory(circuit0::Vector{AbstractOperation}, state::AbstractStabilizer)
132+
# # TODO - Figure out if to use Reset or Gates
133+
# pushfirst!(circuit0, Reset(state, 1:nqubits(state)))
134+
# return backtrajectory(circuit0, nqubits(state))
135+
# end
136+
137+
function backtrajectory(circuit::Vector{AbstractOperation})
138+
n = 0
139+
for op in circuit
140+
if op isa AbstractSingleQubitOperator
141+
n = max(n, op.q)
142+
elseif op isa AbstractTwoQubitOperator
143+
n = max(n, op.q1, op.q2)
144+
elseif op isa AbstractMeasurement
145+
n = max(n, op.qubit)
146+
else
147+
error("Unsupported operation: $(typeof(op))")
148+
end
149+
end
150+
151+
return backtrajectory(circuit, n)
152+
end

test/test_backtraj.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@testitem "test backtrajectory" begin
2+
@testset "test backtrajectory" begin
3+
end
4+
end

0 commit comments

Comments
 (0)