Skip to content

Commit 10433da

Browse files
committed
new shor
1 parent cb1d5ab commit 10433da

File tree

1 file changed

+282
-0
lines changed

1 file changed

+282
-0
lines changed

examples/order_finding.jl

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
using Yao, YaoBlocks, BitBasis, LuxurySparse, LinearAlgebra
2+
3+
function YaoBlocks.cunmat(nbit::Int, cbits::NTuple{C, Int}, cvals::NTuple{C, Int}, U0::Adjoint, locs::NTuple{M, Int}) where {C, M}
4+
YaoBlocks.cunmat(nbit, cbits, cvals, copy(U0), locs)
5+
end
6+
7+
"""x^Nz%N"""
8+
function powermod(x::Int, k::Int, N::Int)
9+
rem = 1
10+
for i=1:k
11+
rem = mod(rem*x, N)
12+
end
13+
rem
14+
end
15+
16+
Z_star(N::Int) = filter(i->gcd(i, N)==1, 0:N-1)
17+
Eulerφ(N) = length(Z_star(N))
18+
19+
"""obtain `s` and `r` from `ϕ` that satisfies `|s/r - ϕ| ≦ 1/2r²`"""
20+
continued_fraction(ϕ, niter::Int) = niter==0 ? floor(Int, ϕ) : floor(Int, ϕ) + 1//continued_fraction(1/mod(ϕ, 1), niter-1)
21+
continued_fraction::Rational, niter::Int) = niter==0 || ϕ.den==1 ? floor(Int, ϕ) : floor(Int, ϕ) + 1//continued_fraction(1/mod(ϕ, 1), niter-1)
22+
23+
"""
24+
Return `y` that `(x*y)%N == 1`, notice the `(x*y)%N` operations in Z* forms a group.
25+
"""
26+
function mod_inverse(x::Int, N::Int)
27+
for i=1:N
28+
(x*i)%N == 1 && return i
29+
end
30+
throw(ArgumentError("Can not find the inverse, $x is probably not in Z*($N)!"))
31+
end
32+
33+
is_order(r, x, N) = powermod(x, r, N) == 1
34+
35+
"""get the order, the classical approach."""
36+
function get_order(::Val{:classical}, x::Int, N::Int)
37+
findfirst(r->is_order(r, x, N), 1:N)
38+
end
39+
40+
function rand_primeto(L)
41+
while true
42+
x = rand(2:L-1)
43+
d = gcd(x, L)
44+
if d == 1
45+
return x
46+
end
47+
end
48+
end
49+
50+
function shor(L, ver=Val(:quantum); maxiter=100)
51+
L%2 == 0 && return 2
52+
# some classical method to accelerate the solution finding
53+
for i in 1:maxiter
54+
x = rand_primeto(L)
55+
r = get_order(ver, x, L)
56+
# if `x^(r/2)` is non-trivil, go on.
57+
# Here, we do not condsier `powermod(x, r÷2, L) == 1`, since in this case the order should be `r/2`
58+
if r%2 == 0 && powermod(x, r÷2, L) != L-1
59+
f1, f2 = gcd(powermod(x, r÷2, L)-1, L), gcd(powermod(x, r÷2, L)+1, L)
60+
if f1!=1
61+
return f1
62+
elseif f2!=1
63+
return f2
64+
else
65+
error("Algorithm Fail!")
66+
end
67+
end
68+
end
69+
end
70+
71+
"""
72+
Mod{N} <: PrimitiveBlock{N}
73+
74+
calculates `mod(a*x, L)`, notice `gcd(a, L)` should be 1.
75+
"""
76+
struct Mod{N} <: PrimitiveBlock{N}
77+
a::Int
78+
L::Int
79+
function Mod{N}(a, L) where N
80+
@assert gcd(a, L) == 1 && L<=1<<N
81+
new{N}(a, L)
82+
end
83+
end
84+
85+
function Yao.apply!(reg::ArrayReg{B}, m::Mod{N}) where {B, N}
86+
YaoBlocks._check_size(reg, m)
87+
nstate = zero(reg.state)
88+
for i in basis(reg)
89+
_i = i >= m.L ? i+1 : mod(i*m.a, m.L)+1
90+
for j in 1:B
91+
@inbounds nstate[_i,j] = reg.state[i+1,j]
92+
end
93+
end
94+
reg.state = nstate
95+
reg
96+
end
97+
98+
function Yao.mat(::Type{T}, m::Mod{N}) where {T, N}
99+
perm = Vector{Int}(undef, 1<<N)
100+
for i in basis(N)
101+
@inbounds perm[i >= m.L ? i+1 : mod(i*m.a, m.L)+1] = i+1
102+
end
103+
PermMatrix(perm, ones(T, 1<<N))
104+
end
105+
106+
Base.adjoint(m::Mod{N}) where N = Mod{N}(mod_inverse(m.a, m.L), m.L)
107+
Yao.print_block(io::IO, m::Mod{N}) where N = print(io, "Mod{$N}: $(m.a)*x % $(m.L)")
108+
109+
Yao.isunitary(::Mod) = true
110+
# Yao.ishermitian(::Mod) = true # this is not true for L = 1.
111+
# Yao.isreflexive(::Mod) = true # this is not true for L = 1.
112+
113+
"""
114+
KMod{N, K} <: PrimitiveBlock{N}
115+
116+
The first `K` qubits are exponent `k`, and the rest `N-K` are base `a`,
117+
it calculates `mod(a^k*x, L)`, notice `gcd(a, L)` should be 1.
118+
"""
119+
struct KMod{N, K} <: PrimitiveBlock{N}
120+
a::Int
121+
L::Int
122+
function KMod{N, K}(a, L) where {N, K}
123+
@assert gcd(a, L) == 1 && L<=1<<(N-K)
124+
new{N, K}(a, L)
125+
end
126+
end
127+
128+
nqubits_data(m::KMod{N, K}) where {N, K} = N-K
129+
nqubits_control(m::KMod{N, K}) where {N, K} = K
130+
131+
function bint2_reader(T, k::Int)
132+
mask = bmask(T, 1:k)
133+
return b -> (b&mask, b>>k)
134+
end
135+
136+
function Yao.apply!(reg::ArrayReg{B}, m::KMod{N, K}) where {B, N, K}
137+
YaoBlocks._check_size(reg, m)
138+
nstate = zero(reg.state)
139+
140+
reader = bint2_reader(Int, K)
141+
for b in basis(reg)
142+
k, i = reader(b)
143+
_i = i >= m.L ? i : mod(i*powermod(m.a, k, m.L), m.L)
144+
_b = k + _i<<K + 1
145+
for j in 1:B
146+
@inbounds nstate[_b,j] = reg.state[b+1,j]
147+
end
148+
end
149+
reg.state = nstate
150+
reg
151+
end
152+
153+
function Yao.mat(::Type{T}, m::KMod{N, K}) where {T, N, K}
154+
perm = Vector{Int}(undef, 1<<N)
155+
reader = bint2_reader(Int, K)
156+
for b in basis(N)
157+
k, i = reader(b)
158+
_i = i >= m.L ? i : mod(i*powermod(m.a, k, m.L), m.L)
159+
_b = k + _i<<K + 1
160+
@inbounds perm[_b] = b+1
161+
end
162+
PermMatrix(perm, ones(T, 1<<N))
163+
end
164+
165+
Base.adjoint(m::KMod{N, K}) where {N, K} = KMod{N, K}(mod_inverse(m.a, m.L), m.L)
166+
Yao.print_block(io::IO, m::KMod{N, K}) where {N, K} = print(io, "Mod{$N, $K}: $(m.a)^k*x % $(m.L)")
167+
168+
Yao.isunitary(::KMod) = true
169+
# Yao.ishermitian(::Mod) = true # this is not true for L = 1.
170+
# Yao.isreflexive(::Mod) = true # this is not true for L = 1.
171+
172+
estimate_K(nbit::Int, ϵ::Real) = 2*nbit + 1 + ceil(Int,log2(2+1/2ϵ))
173+
174+
using QuAlgorithmZoo: QFTBlock
175+
function order_finding_circuit(x::Int, L::Int; nbit::Int=bit_length(L-1), K::Int=estimate_K(nbit, 0.25))
176+
N = nbit+K
177+
chain(N, repeat(N, H, 1:K), KMod{N, K}(x, L), concentrate(N, QFTBlock{K}()', 1:K))
178+
end
179+
180+
function shor(L::Int; nshots=10)
181+
x = rand_primeto(L)
182+
end
183+
184+
function get_order(::Val{:quantum}, x::Int, L::Int; nshots=10)
185+
c = order_finding_circuit(x, L)
186+
n = nqubits_data(c[2])
187+
K = nqubits_control(c[2])
188+
reg = join(product_state(n, 1), zero_state(K))
189+
190+
res = measure(copy(reg) |> c; nshots=nshots)
191+
reader = bint2_reader(Int, K)
192+
for r in res
193+
k, i = reader(r)
194+
# get s/r
195+
ϕ = bfloat(k) #
196+
ϕ == 0 && continue
197+
198+
order = order_from_float(ϕ, x, L)
199+
if order === nothing
200+
continue
201+
else
202+
return order
203+
end
204+
end
205+
return nothing
206+
end
207+
208+
function order_from_float(ϕ, x, L)
209+
k = 1
210+
rnum = continued_fraction(ϕ, k)
211+
while rnum.den < L
212+
r = rnum.den
213+
@show r
214+
if is_order(r, x, L)
215+
return r
216+
end
217+
k += 1
218+
rnum = continued_fraction(ϕ, k)
219+
end
220+
return nothing
221+
end
222+
223+
using Test
224+
function check_Euler_theorem(N::Int)
225+
Z = Z_star(N)
226+
Nz = length(Z) # Eulerφ
227+
for x in Z
228+
@test powermod(x,Nz,N) == 1 # the order is a devisor of Eulerφ
229+
end
230+
end
231+
232+
@testset "Euler" begin
233+
check_Euler_theorem(150)
234+
end
235+
236+
@testset "Mod" begin
237+
@test_throws AssertionError Mod{4}(4,10)
238+
@test_throws AssertionError Mod{2}(3,10)
239+
m = Mod{4}(3,10)
240+
@test mat(m) applymatrix(m)
241+
@test isunitary(m)
242+
@test isunitary(mat(m))
243+
@test m' == Mod{4}(7,10)
244+
end
245+
246+
@testset "KMod" begin
247+
@test_throws AssertionError KMod{6, 2}(4,10)
248+
@test_throws AssertionError KMod{4, 2}(3,10)
249+
m = KMod{6, 2}(3,10)
250+
@test mat(m) applymatrix(m)
251+
@test isunitary(m)
252+
@test isunitary(mat(m))
253+
@test m' == KMod{6, 2}(7,10)
254+
end
255+
256+
using Random
257+
@testset "shor_classical" begin
258+
Random.seed!(129)
259+
L = 35
260+
f = shor(L, Val(:classical))
261+
@test f == 5 || f == 7
262+
263+
L = 25
264+
f = shor(L, Val(:classical))
265+
@test_broken f == 5
266+
267+
L = 7*11
268+
f = shor(L, Val(:classical))
269+
@test f == 7 || f == 11
270+
271+
L = 14
272+
f = shor(L, Val(:classical))
273+
@test f == 2 || f == 7
274+
end
275+
276+
using Random
277+
@testset "shor quantum" begin
278+
Random.seed!(129)
279+
L = 15
280+
f = shor(L, Val(:quantum))
281+
@test f == 5 || f == 3
282+
end

0 commit comments

Comments
 (0)