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 ``` 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 000000000..de9beeb30 Binary files /dev/null and b/docs/src/assets/figures/digit2.png differ diff --git a/docs/src/assets/figures/digits012.png b/docs/src/assets/figures/digits012.png new file mode 100644 index 000000000..6b4b2c0a1 Binary files /dev/null and b/docs/src/assets/figures/digits012.png differ diff --git a/docs/src/tutorial/Grover.md b/docs/src/tutorial/Grover.md index 12573a6d1..a92842e6d 100644 --- a/docs/src/tutorial/Grover.md +++ b/docs/src/tutorial/Grover.md @@ -1,162 +1,172 @@ -# Grover Search and Inference +# Grover Search and Quantum Inference ## Grover Search ![grover](../assets/figures/grover.png) +First, we construct the reflection block ``R(|\psi\rangle) = 2|\psi\rangle\langle\psi|-1``, given we know how to construct ``|\psi\rangle=A|0\rangle``. +Then it equivalent to construct $R(|\psi\rangle) = A(2|0\rangle\langle 0|-1)A^\dagger$ ```@example Grover using Yao +using Yao.Blocks +using Compat +using Compat.Test +using StatsBase + +""" +A way to construct oracle, e.g. inference_oracle([1,2,-3,5]) will +invert the sign when a qubit configuration matches: 1=>1, 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