From 00299c7d936c0961ee0d1ef261b7ad017b59db1a Mon Sep 17 00:00:00 2001 From: Leo Date: Tue, 3 Jul 2018 02:28:59 +0800 Subject: [PATCH 1/2] polish grover and add examples (#111) * polish grover and add examples * new grover search doc * fix a test --- docs/make.jl | 1 + docs/src/assets/figures/digit2.png | Bin 0 -> 4395 bytes docs/src/assets/figures/digits012.png | Bin 0 -> 8716 bytes docs/src/tutorial/Grover.md | 262 +++++++++++++------------- examples/Grover.jl | 10 +- src/Blocks/ReflectBlock.jl | 15 +- src/Interfaces/Sequential.jl | 3 - src/Zoo/Differential.jl | 2 +- src/Zoo/Grover.jl | 65 ++++--- test/Blocks/ReflectBlock.jl | 4 +- test/Interfaces/Interfaces.jl | 7 +- test/Zoo/Differential.jl | 4 +- test/Zoo/Grover.jl | 21 ++- test/Zoo/Zoo.jl | 4 + 14 files changed, 216 insertions(+), 182 deletions(-) create mode 100644 docs/src/assets/figures/digit2.png create mode 100644 docs/src/assets/figures/digits012.png diff --git a/docs/make.jl b/docs/make.jl index dfe8c0be2..1608eb071 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -18,6 +18,7 @@ makedocs( "Tutorial" => Any[ "tutorial/GHZ.md", "tutorial/QFT.md", + "tutorial/Grover.md", "tutorial/QCBM.md", ], "Manual" => Any[ diff --git a/docs/src/assets/figures/digit2.png b/docs/src/assets/figures/digit2.png new file mode 100644 index 0000000000000000000000000000000000000000..de9beeb30c61df7c1c00f23c0fddfaadd8d1a426 GIT binary patch literal 4395 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, 2=>1, 3=>0, 5=>1. +""" +function inference_oracle(locs::Vector{Int}) + control(locs[1:end-1], abs(locs[end]) => (locs[end]>0 ? Z : chain(phase(π), Z))) +end -# Control-R(k) gate in block-A -A(i::Int, j::Int, k::Int) = control([i, ], j=>shift(2π/(1<H) : A(j, i, j-i+1) for j = i:n) -QFT(n::Int) = chain(n, B(n, i) for i = 1:n) - -# define QFT and IQFT block. -num_bit = 5 -qft = QFT(num_bit) -iqft = adjoint(qft) -``` - -The basic building block - controled phase shift gate is defined as - -```math -R(k)=\begin{bmatrix} -1 & 0\\ -0 & \exp\left(\frac{2\pi i}{2^k}\right) -\end{bmatrix} -``` -In Yao, factory methods for blocks will be loaded lazily. For example, if you missed the total -number of qubits of `chain`, then it will return a function that requires an input of an integer. -So the following two statements are equivalent -```@example QFT -control([4, ], 1=>shift(-2π/(1<<4)))(5) == control(5, [4, ], 1=>shift(-2π/(1<<4))) -``` -Both of then will return a `ControlBlock` instance. If you missed the total number of qubits. It is OK. Just go on, it will be filled when its possible. - -Once you have construct a block, you can inspect its matrix using `mat` function. -Let's construct the circuit in dashed box A, and see the matrix of ``R_4`` gate -```julia -julia> a = A(4, 1, 4)(5) -Total: 5, DataType: Complex{Float64} -control(4) -└─ 1=>Phase Shift Gate:-0.39269908169872414 +function reflectblock(A::MatrixBlock{N}) where N + chain(N, A |> adjoint, inference_oracle(-collect(1:N)), A) +end +nbit = 12 +A = repeat(nbit, H) +ref = reflectblock(A) -julia> mat(a.block) -2×2 Diagonal{Complex{Float64}}: - 1.0+0.0im ⋅ - ⋅ 0.92388-0.382683im +@testset "test reflect" begin + reg = rand_state(nbit) + ref_vec = apply!(zero_state(nbit), A) |> statevec + v0 = reg |> statevec + @test -2*(ref_vec'*v0)*ref_vec + v0 ≈ apply!(copy(reg), ref) |> statevec +end ``` +Then we define the oracle and target state -Similarly, you can use `put` and `chain` to construct `PutBlock` (basic placement of a single gate) and `ChainBlock` (sequential application of `MatrixBlock`s) instances. `Yao.jl` view every component in a circuit as an `AbstractBlock`, these blocks can be integrated to perform higher level functionality. - -You can check the result using classical `fft` -```@example QFT -# if you're using lastest julia, you need to add the fft package. -@static if VERSION >= v"0.7-" - using FFTW +```@example Grover +# first, construct the oracle with desired state in the range 100-105. +oracle!(reg::DefaultRegister) = (reg.state[100:105,:]*=-1; reg) + +# transform it into a function block, so it can be put inside a `Sequential`. +fb_oracle = FunctionBlock{:Oracle}(reg->oracle!(reg)) + +""" +ratio of components in a wavefunction that flip sign under oracle. +""" +function prob_match_oracle(psi::DefaultRegister, oracle) + fliped_reg = apply!(register(ones(Complex128, 1< statevec |> real .< 0 + norm(statevec(psi)[match_mask])^2 end -using Compat.Test -@test chain(num_bit, qft, iqft) |> mat ≈ eye(2^num_bit) +# uniform state as initial state +psi0 = apply!(zero_state(nbit), A) -# define a register and get its vector representation -reg = rand_state(num_bit) -rv = reg |> statevec |> copy +# the number of grover steps that can make it reach first maximum overlap. +num_grover_step(prob::Real) = Int(round(pi/4/sqrt(prob)))-1 +niter = num_grover_step(prob_match_oracle(psi0, fb_oracle)) -# test fft -reg_qft = apply!(copy(reg) |>invorder!, qft) -kv = ifft(rv)*sqrt(length(rv)) -@test reg_qft |> statevec ≈ kv - -# test ifft -reg_iqft = apply!(copy(reg), iqft) -kv = fft(rv)/sqrt(length(rv)) -@test reg_iqft |> statevec ≈ kv |> invorder +# construct the whole circuit +gb = sequence(sequence(fb_oracle, ref) for i = 1:niter); ``` -QFT and IQFT are different from FFT and IFFT in three ways, +Now, let's start training +```@example Grover +for (i, blk) in enumerate(gb) + apply!(psi0, blk) + overlap = prob_match_oracle(psi0, fb_oracle) + println("step $i, overlap = $overlap") +end +``` -1. they are different by a factor of ``\sqrt{2^n}`` with ``n`` the number of qubits. -2. the little end and big end will exchange after applying QFT or IQFT. -3. due to the convention, QFT is more related to IFFT rather than FFT. +The above is the standard Grover Search algorithm, it can find target state in $O(\sqrt N)$ time, with $N$ the size of an unordered database. +Similar algorithm can be used in more useful applications, like inference, i.e. get conditional probability distribution $p(x|y)$ given $p(x, y)$. +```@example Grover +function rand_circuit(nbit::Int, ngate::Int) + circuit = chain(nbit) + gate_list = [X, H, Ry(0.3), CNOT] + for i = 1:ngate + gate = rand(gate_list) + push!(circuit, put(nbit, (sample(1:nbit, nqubits(gate),replace=false)...,)=>gate)) + end + circuit +end +A = rand_circuit(nbit, 200) +psi0 = apply!(zero_state(nbit), A) + +# now we want to search the subspace with [1,3,5,8,9,11,12] +# fixed to 1 and [4,6] fixed to 0. +evidense = [1, 3, -4, 5, -6, 8, 9, 11, 12] + +""" +Doing Inference, psi is the initial state, +the target is to search target space with specific evidense. +e.g. evidense [1, -3, 6] means the [1, 3, 6]-th bits take value [1, 0, 1]. +""" +oracle_infer = inference_oracle(evidense)(nqubits(psi0)) + +niter = num_grover_step(prob_match_oracle(psi0, oracle_infer)) +gb_infer = chain(nbit, chain(oracle_infer, reflectblock(A)) for i = 1:niter); +``` -## Phase Estimation -Since we have QFT and IQFT blocks we can then use them to realize phase estimation circuit, what we want to realize is the following circuit -![phase estimation](../assets/figures/phaseest.png) +Now, let's start training +```@example Grover +for (i, blk) in enumerate(gb_infer) + apply!(psi0, blk) + p_target = prob_match_oracle(psi0, oracle_infer) + println("step $i, overlap^2 = $p_target") +end +``` -In the following simulation, we use equivalent `QFTBlock` in the Yao.`Zoo` module rather than the above chain block, -it is faster than the above construction because it hides all the simulation details (yes, we are cheating :D) and get the equivalent output. +Here is an application, suppose we have constructed some digits and stored it in a wave vector. -```@example PhaseEstimation -using Yao -using Yao.Zoo -using Yao.Blocks +```@example Grover using Yao.Intrinsics -function phase_estimation(reg1::DefaultRegister, reg2::DefaultRegister, U::GeneralMatrixGate{N}, nshot::Int=1) where {N} - M = nqubits(reg1) - iqft = QFTBlock{M}() |> adjoint - HGates = rollrepeat(M, H) - - control_circuit = chain(M+N) - for i = 1:M - push!(control_circuit, control(M+N, (i,), (M+1:M+N...,)=>U)) - if i != M - U = matrixgate(mat(U) * mat(U)) - end - end +x1 = [0 1 0; 0 1 0; 0 1 0; 0 1 0; 0 1 0] +x2 = [1 1 1; 0 0 1; 1 1 1; 1 0 0; 1 1 1] +x0 = [1 1 1; 1 0 1; 1 0 1; 1 0 1; 1 1 1] - # calculation - # step1 apply hadamard gates. - apply!(reg1, HGates) - # join two registers - reg = join(reg1, reg2) - # using iqft to read out the phase - apply!(reg, sequence(control_circuit, focus(1:M...), iqft)) - # measure the register (on focused bits), if the phase can be exactly represented by M qubits, only a single shot is needed. - res = measure(reg, nshot) - # inverse the bits in result due to the exchange of big and little ends, so that we can get the correct phase. - breflect.(M, res)./(1< vec |> BitArray |> packbits)+1] = sqrt(p) end ``` -Here, `reg1` (``Q_{1-5}``) is used as the output space to store phase ϕ, and `reg2` (``Q_{6-8}``) is the input state which corresponds to an eigenvector of oracle matrix `U`. -The algorithm detials can be found [here](https://en.wikipedia.org/wiki/Quantum_phase_estimation_algorithm). +Plot them, you will see these digits -In this function, `HGates` corresponds to circuit block in dashed box `A`, `control_circuit` corresponds to block in dashed box `B`. -`matrixgate` is a factory function for `GeneralMatrixGate`. +![digits](../assets/figures/digits012.png) -Here, the only difficult concept is `focus`, `focus` returns a `FunctionBlock`, that will make focused bits the active bits. -An operator sees only active bits, and operating active space is more efficient, most importantly, it becomes much easier to integrate blocks. -However, it has the potential ability to change line orders, for safety consideration, you may also need safer [`Concentrator`](@ref). +Then we construct the inference circuit. +Here, we choose to use `reflect` to construct a [`ReflectBlock`](@ref), +instead of constructing it explicitly. +```@example Grover +rb = reflect(copy(v)) +psi0 = register(v) -```@example PhaseEstimation -r = rand_state(6) -apply!(r, focus(4,1,2)) # or equivalently using focus!(r, [4,1,2]) -nactive(r) -``` +# we want to find the digits with the first 5 qubits [1, 0, 1, 1, 1]. +evidense = [1, -2, 3, 4, 5] +oracle_infer = inference_oracle(evidense)(nbit) -Then we will have a check to above function +niter = num_grover_step(prob_match_oracle(psi0, oracle_infer)) +gb_infer = chain(nbit, chain(oracle_infer, rb) for i = 1:niter) +``` -```@example PhaseEstimation -rand_unitary(N::Int) = qr(randn(N, N))[1] +Now, let's start training +```@example Grover +for (i, blk) in enumerate(gb_infer) + apply!(psi0, blk) + p_target = prob_match_oracle(psi0, oracle_infer) + println("step $i, overlap^2 = $p_target") +end +``` -M = 5 -N = 3 +The result is +```@example Grover +pl = psi0 |> probs +config = findn(pl.>0.5)[] - 1 |> bitarray(nbit) +res = reshape(config, 5,3) +``` -# prepair oracle matrix U -V = rand_unitary(1< state + r.state[:,:] .= 2 .* (v'*r.state) .* v - r.state r end -==(A::ReflectBlock, B::ReflectBlock) = A.state == B.state -copy(r::ReflectBlock) = ReflectBlock(r.state) +==(A::ReflectBlock, B::ReflectBlock) = A.psi == B.psi +copy(r::ReflectBlock) = ReflectBlock(r.psi) -mat(r::ReflectBlock) = 2*r.state*r.state' - IMatrix(length(r.state)) +mat(r::ReflectBlock) = (v = r.psi |> statevec; 2*v*v' - IMatrix(length(v))) isreflexive(::ReflectBlock) = true ishermitian(::ReflectBlock) = true isunitary(::ReflectBlock) = true diff --git a/src/Interfaces/Sequential.jl b/src/Interfaces/Sequential.jl index 91d8001fa..dbc1201e3 100644 --- a/src/Interfaces/Sequential.jl +++ b/src/Interfaces/Sequential.jl @@ -12,6 +12,3 @@ function sequence end sequence() = Sequential([]) sequence(blocks::AbstractBlock...) = Sequential(blocks...) sequence(blocks) = sequence(blocks...) - -# lazy constructors -sequence(blocks...) = n->sequence([parse_block(n, each) for each in blocks]) diff --git a/src/Zoo/Differential.jl b/src/Zoo/Differential.jl index 3d69fdf04..ac2502769 100644 --- a/src/Zoo/Differential.jl +++ b/src/Zoo/Differential.jl @@ -1,4 +1,4 @@ -export diff_circuit, num_gradient, rotter, cnot_entangler, opgrad, collect_rotblocks +export diff_circuit, num_gradient, rotter, cnot_entangler, opgrad, collect_rotblocks, perturb """ rotter(noleading::Bool=false, notrailing::Bool=false) -> ChainBlock{1, ComplexF64} diff --git a/src/Zoo/Grover.jl b/src/Zoo/Grover.jl index 1dd80079b..d026d6886 100644 --- a/src/Zoo/Grover.jl +++ b/src/Zoo/Grover.jl @@ -1,11 +1,12 @@ -export num_grover_step, inference_oracle, prob_match, GroverIter +export num_grover_step, inference_oracle, GroverIter, groverblock, groveriter!, prob_match_oracle """ - inference_oracle(locs::Vector{Int}) -> ControlBlock + inference_oracle([nbit::Int,] locs::Vector{Int}) -> ControlBlock A simple inference oracle, e.g. inference([-1, -8, 5]) is a control block that flip the bit if values of bits on position [1, 8, 5] match [0, 0, 1]. """ inference_oracle(locs::Vector{Int}) = control(locs[1:end-1], abs(locs[end]) => (locs[end]>0 ? Z : chain(phase(π), Z))) +inference_oracle(nbit::Int, locs::Vector{Int}) = inference_oracle(locs)(nbit) """ target_space(oracle) -> Vector{Bool} @@ -13,49 +14,61 @@ inference_oracle(locs::Vector{Int}) = control(locs[1:end-1], abs(locs[end]) => ( Return a mask, that disired subspace of an oracle are masked true. """ target_space(num_bit::Int, oracle) = (register(ones(Complex128, 1< oracle |> statevec |> real) .< 0 -prob_inspace(psi::AbstractRegister, ts) = norm(statevec(psi)[ts])^2 +prob_inspace(psi::DefaultRegister, ts) = norm(statevec(psi)[ts])^2 """ prob_match_oracle(psi, oracle) -> Float64 Return the probability that `psi` matches oracle. """ -prob_match_oracle(psi::AbstractRegister, oracle) = prob_inspace(psi, target_space(nqubits(psi), oracle)) +prob_match_oracle(psi::DefaultRegister, oracle) = prob_inspace(psi, target_space(nqubits(psi), oracle)) """ - num_grover_step(prob::Real) -> Int + num_grover_step(psi::DefaultRegister, oracle) -> Int -Return number of grover steps to obtain the maximum overlap with target state. - -Input parameter `prob` is the overlap between `target state space` and initial state ``|\\psi\\ranlge``, -which means the probability of obtaining `true` on initial state. +Return number of grover steps needed to match the oracle. """ -num_grover_step(prob::Real) = Int(round(pi/4/sqrt(prob)))-1 +num_grover_step(psi::DefaultRegister, oracle) = _num_grover_step(prob_match_oracle(psi, oracle)) + +_num_grover_step(prob::Real) = Int(round(pi/4/sqrt(prob)))-1 """ - GroverIter{AUTOSTOP, N, T} + GroverIter{N, T} - GroverIter{AUTOSTOP}(oracle, ref::ReflectBlock{N, T}, psi::AbstractRegister) -> GroverIter{N, T} + GroverIter(oracle, ref::ReflectBlock{N, T}, psi::DefaultRegister, niter::Int) -Return an iterator that perform Grover operations step by step. +an iterator that perform Grover operations step by step. An Grover operation consists of applying oracle and Reflection. - -If `AUTOSTOP` is true, it will stop when the first time the state reaches the sweet spot. """ -struct GroverIter{AUTOSTOP, N, T} +struct GroverIter{N, T} + psi::DefaultRegister oracle ref::ReflectBlock{N, T} - psi::AbstractRegister niter::Int end -GroverIter{AUTOSTOP}(oracle, ref::ReflectBlock{N, T}, psi::AbstractRegister) where {AUTOSTOP, N, T} = GroverIter{AUTOSTOP, N, T}(oracle, ref, psi, AUTOSTOP ? num_grover_step(prob_match_oracle(psi, oracle)) : -1) -GroverIter{AUTOSTOP}(oracle, psi::AbstractRegister{B, T}) where {AUTOSTOP, B, T} = GroverIter{AUTOSTOP}(oracle, ReflectBlock(psi |> statevec |> copy), psi) -GroverIter(oracle, psi::AbstractRegister) = GroverIter{true}(oracle, psi) +groveriter!(psi::DefaultRegister, oracle, ref::ReflectBlock{N, T}, niter::Int) where {N, T} = GroverIter{N, T}(psi, oracle, ref, niter) +groveriter!(psi::DefaultRegister, oracle, niter::Int) = groveriter!(psi, oracle, ReflectBlock(psi |> copy), niter) +groveriter!(psi::DefaultRegister, oracle) = groveriter!(psi, oracle, ReflectBlock(psi |> copy), num_grover_step(psi, oracle)) -Base.next(iter::GroverIter, state::Int) = (state, apply!(iter.psi |> iter.oracle, iter.ref)), state+1 +Base.next(iter::GroverIter, state::Int) = apply!(iter.psi |> iter.oracle, iter.ref), state+1 Base.start(iter::GroverIter) = 1 -Base.done(iter::GroverIter{false}, state::Int) = false -Base.done(iter::GroverIter{true}, state::Int) = iter.niter+1 == state -Base.iteratorsize(::Type{GroverIter{AUTOSTOP}}) where AUTOSTOP = AUTOSTOP ? Base.HasLength() : Base.IsInfite() -Base.length(iter::GroverIter{true}) = iter.niter -Base.length(iter::GroverIter{false}) = Inf +Base.done(iter::GroverIter, state::Int) = iter.niter+1 == state +Base.length(iter::GroverIter) = iter.niter + +""" + groverblock(oracle, ref::ReflectBlock{N, T}, niter::Int=-1) + groverblock(oracle, psi::DefaultRegister, niter::Int=-1) + +Return a ChainBlock/Sequential as Grover Iteration, the default `niter` will stop at the first optimal step. +""" +function groverblock(oracle::MatrixBlock{N, T}, ref::ReflectBlock{N, T}, niter::Int=-1) where {N, T} + if niter == -1 niter = num_grover_step(ref.psi, oracle) end + chain(N, chain(oracle, ref) for i = 1:niter) +end + +function groverblock(oracle, ref::ReflectBlock{N, T}, niter::Int=-1) where {N, T} + if niter == -1 niter = num_grover_step(ref.psi, oracle) end + sequence(sequence(oracle, ref) for i = 1:niter) +end + +groverblock(oracle, psi::DefaultRegister, niter::Int=-1) = groverblock(oracle, ReflectBlock(psi |> copy), niter) diff --git a/test/Blocks/ReflectBlock.jl b/test/Blocks/ReflectBlock.jl index 17e99eeb5..08ce1fc7b 100644 --- a/test/Blocks/ReflectBlock.jl +++ b/test/Blocks/ReflectBlock.jl @@ -15,6 +15,6 @@ import Yao.Blocks: ReflectBlock v0, v1 = vec(reg.state), vec(reg0.state) @test mat(rf)*(reg0|>statevec) ≈ apply!(copy(reg0), rf) |> statevec - @test rf.state'*v0 ≈ rf.state'*v1 - @test v0-rf.state'*v0*rf.state ≈ -(v1-rf.state'*v1*rf.state) + @test (rf.psi |> statevec)'*v0 ≈ (rf.psi |> statevec)'*v1 + @test v0-(rf.psi |> statevec)'*v0*(rf.psi |> statevec) ≈ -(v1-(rf.psi |> statevec)'*v1*(rf.psi |> statevec)) end diff --git a/test/Interfaces/Interfaces.jl b/test/Interfaces/Interfaces.jl index 6309f8f74..d0c6eb003 100644 --- a/test/Interfaces/Interfaces.jl +++ b/test/Interfaces/Interfaces.jl @@ -140,11 +140,8 @@ end end @testset "sequence" begin - sq = sequence(kron(3=>X), addbit(3), MEASURE) - @test sq isa Function - sqs = sq(5) - @test sqs isa Sequential - @test sqs == sequence(kron(5, 3=>X), addbit(3), MEASURE) == sequence((kron(5, 3=>X), addbit(3), MEASURE)) + sqs = sequence(kron(5, 3=>X), addbit(3), MEASURE) + @test sqs == sequence((kron(5, 3=>X), addbit(3), MEASURE)) insert!(sqs, 3, kron(8, 8=>X)) push!(sqs, Reset) reg = register(bit"11111") |> sqs diff --git a/test/Zoo/Differential.jl b/test/Zoo/Differential.jl index ae7a3affd..aeec080d4 100644 --- a/test/Zoo/Differential.jl +++ b/test/Zoo/Differential.jl @@ -10,8 +10,8 @@ using Compat.Test @test length(rots) == nparameters(c) == 40 obs = kron(nqubits(c), 2=>X) - @test mean(opgrad(()->expect(obs, apply!(zero_state(4), c))|>real, rots) .|> abs) > 3e-3 - @test isapprox(opgrad(()->expect(obs, apply!(zero_state(4), c))|>real, rots), num_gradient(()->expect(obs, apply!(zero_state(4),c))|>real, rots), atol=1e-3) + @test mean(opgrad(()->expect(obs, apply!(zero_state(4), c))|>real, rots) .|> abs) > 3e-4 + @test isapprox(opgrad(()->expect(obs, apply!(zero_state(4), c))|>real, rots), num_gradient(()->expect(obs, apply!(zero_state(4),c))|>real, rots), atol=1e-4) @test rotter(true, true) == Rx(0) @test rotter(false, false) == rotter() == chain(Rz(0), Rx(0), Rz(0)) diff --git a/test/Zoo/Grover.jl b/test/Zoo/Grover.jl index 7e250f5ae..38ba4ea00 100644 --- a/test/Zoo/Grover.jl +++ b/test/Zoo/Grover.jl @@ -1,20 +1,21 @@ using Yao using Yao.Zoo +import Yao.Zoo: _num_grover_step using Yao.Blocks using Yao.Intrinsics using Compat using Compat.Test function GroverSearch(oracle, num_bit::Int; psi::DefaultRegister = uniform_state(num_bit)) - it = GroverIter(oracle, psi) - for (i, psi) in it end + it = groveriter!(psi, oracle) + for psi in it end return (it.niter, psi) end function inference(psi::DefaultRegister, evidense::Vector{Int}, num_iter::Int) oracle = inference_oracle(evidense)(nqubits(psi)) - it = GroverIter(oracle, psi) - for (i, psi) in it end + it = groveriter!(psi, oracle) + for psi in it end it.niter, psi end @@ -39,6 +40,16 @@ end @test isapprox(abs(statevec(psi)'*target_state), 1, atol=1e-3) end +@testset "groverblock" begin + psi = uniform_state(5) + or = inference_oracle(5, [-1,2,5,4,3]) + func_or = FunctionBlock{:Oracle}(reg->apply!(reg, or)) + gb = groverblock(or, psi) + gb2 = groverblock(func_or, psi) + @test apply!(copy(psi), gb) == (for psi in groveriter!(copy(psi), func_or) end; psi) + @test apply!(copy(psi), gb) == apply!(copy(psi), gb2) +end + @testset "test inference" begin num_bit = 12 psi0 = rand_state(num_bit) @@ -55,7 +66,7 @@ end v_desired[:] ./= sqrt(p) # search the subspace - num_iter = num_grover_step(p) + num_iter = _num_grover_step(p) niter, psi = inference(psi0, evidense, num_iter) @test isapprox((psi.state[subinds+1]'*v_desired) |> abs2, 1, atol=1e-2) end diff --git a/test/Zoo/Zoo.jl b/test/Zoo/Zoo.jl index 683dae575..29591ecae 100644 --- a/test/Zoo/Zoo.jl +++ b/test/Zoo/Zoo.jl @@ -17,3 +17,7 @@ end @testset "RotBasis" begin include("RotBasis.jl") end + +@testset "Grover" begin + include("Grover.jl") +end From 9687b6f22e58804e5fc442305b46c8ea4330a488 Mon Sep 17 00:00:00 2001 From: Rogerluo Date: Tue, 3 Jul 2018 16:01:06 +0800 Subject: [PATCH 2/2] Fix typo --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 83c80ef8c..1f72113f5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -3,5 +3,5 @@ use the following command to build locally. ```sh -> julia make.jl loacl +> julia make.jl local ```