Skip to content

Commit 3abf98e

Browse files
GiggleLiuRogerluo
authored andcommitted
upgrade (#8)
* upgrade * new QSVD * polish QSVD
1 parent 803f30f commit 3abf98e

20 files changed

+145
-58
lines changed

examples/QAOA.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ using NLopt
44
include("maxcut_gw.jl")
55

66
HB(nbit::Int) = sum([put(nbit, i=>X) for i=1:nbit])
7-
tb = TimeEvolution(HB(3), 0.1)
7+
tb = TimeEvolution(HB(3) |> cache, 0.1; check_hermicity=false)
88
function HC(W::AbstractMatrix)
99
nbit = size(W, 1)
1010
ab = Any[]
@@ -22,11 +22,11 @@ function qaoa_circuit(W::AbstractMatrix, depth::Int; use_cache::Bool=false)
2222
hc = HC(W)
2323
use_cache && (hb = hb |> cache; hc = hc |> cache)
2424
c = chain(nbit, [repeat(nbit, H, 1:nbit)])
25-
append!(c, [chain(nbit, [time_evolve(hc, 0.0, tol=1e-5), time_evolve(hb, 0.0, tol=1e-5)]) for i=1:depth])
25+
append!(c, [chain(nbit, [time_evolve(hc, 0.0, tol=1e-5, check_hermicity=false), time_evolve(hb, 0.0, tol=1e-5, check_hermicity=false)]) for i=1:depth])
2626
end
2727

2828

29-
function cobyla_optimize(circuit::MatrixBlock{N}, hc::MatrixBlock; niter::Int) where N
29+
function cobyla_optimize(circuit::AbstractBlock{N}, hc::AbstractBlock; niter::Int) where N
3030
function f(params, grad)
3131
reg = zero_state(N) |> dispatch!(circuit, params)
3232
loss = expect(hc, reg) |> real
@@ -60,7 +60,7 @@ using Random, Test
6060
@test -expect(hc, product_state(5, bmask(sets[1]...)))+sum(W)/4 exact_cost
6161

6262
# build a QAOA circuit and start training.
63-
qc = qaoa_circuit(W, 20; use_cache=false)
63+
qc = qaoa_circuit(W, 20; use_cache=true)
6464
opt_pl = nothing
6565
opt_cost = Inf
6666
for i = 1:10

examples/VQE.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ using Yao
22
using QuAlgorithmZoo
33
using KrylovKit
44

5-
function ed_groundstate(h::MatrixBlock)
5+
function ed_groundstate(h::AbstractBlock)
66
E, V = eigsolve(h |> mat, 1, :SR, ishermitian=true)
77
println("Ground State Energy is $(E[1])")
88
ArrayReg(V[1])

src/CircuitBuild.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ function rand_single_gate()
115115
end
116116

117117
"""
118-
rand_gate(nbit::Int, mbit::Int, [ngate::Int]) -> MatrixBlock
118+
rand_gate(nbit::Int, mbit::Int, [ngate::Int]) -> AbstractBlock
119119
120120
random nbit gate.
121121
"""

src/Diff.jl

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,40 @@ import Yao: expect, content, chcontent, mat, apply!
33
using StatsBase
44

55
############# General Rotor ############
6-
const Rotor{N, T} = Union{RotationGate{N, T}, PutBlock{N, <:Any, <:RotationGate, <:Complex{T}}}
6+
const Rotor{N, T} = Union{RotationGate{N, T}, PutBlock{N, <:Any, <:RotationGate{<:Any, T}}}
77
const CphaseGate{N, T} = ControlBlock{N,<:ShiftGate{T},<:Any}
88
const DiffBlock{N, T} = Union{Rotor{N, T}, CphaseGate{N, T}}
99
"""
10-
generator(rot::Rotor) -> MatrixBlock
10+
generator(rot::Rotor) -> AbstractBlock
1111
1212
Return the generator of rotation block.
1313
"""
1414
generator(rot::RotationGate) = rot.block
1515
generator(rot::PutBlock{N, C, GT}) where {N, C, GT<:RotationGate} = PutBlock{N}(generator(rot|>content), rot |> occupied_locs)
1616
generator(c::CphaseGate{N}) where N = ControlBlock(N, c.ctrol_locs, ctrl_config, control(2,1,2=>Z), c.locs)
1717

18-
abstract type AbstractDiff{GT, N, T} <: TagBlock{GT, N, T} end
18+
abstract type AbstractDiff{GT, N, T} <: TagBlock{GT, N} end
1919
Base.adjoint(df::AbstractDiff) = Daggered(df)
2020

2121
istraitkeeper(::AbstractDiff) = Val(true)
2222

2323
#################### The Basic Diff #################
2424
"""
25-
QDiff{GT, N, T} <: AbstractDiff{GT, N, Complex{T}}
25+
QDiff{GT, N} <: AbstractDiff{GT, N, T}
2626
QDiff(block) -> QDiff
2727
2828
Mark a block as quantum differentiable.
2929
"""
30-
mutable struct QDiff{GT, N, T} <: AbstractDiff{GT, N, Complex{T}}
30+
mutable struct QDiff{GT, N, T} <: AbstractDiff{GT, N, T}
3131
block::GT
3232
grad::T
3333
QDiff(block::DiffBlock{N, T}) where {N, T} = new{typeof(block), N, T}(block, T(0))
3434
end
3535
content(cb::QDiff) = cb.block
3636
chcontent(cb::QDiff, blk::DiffBlock) = QDiff(blk)
3737

38-
@forward QDiff.block mat, apply!
38+
@forward QDiff.block apply!
39+
mat(::Type{T}, df::QDiff) where T = mat(T, df.block)
3940
Base.adjoint(df::QDiff) = QDiff(content(df)')
4041

4142
function YaoBlocks.print_annotation(io::IO, df::QDiff)
@@ -52,19 +53,19 @@ Mark a block as differentiable, here `GT`, `PT` is gate type, parameter type.
5253
Warning:
5354
please don't use the `adjoint` after `BPDiff`! `adjoint` is reserved for special purpose! (back propagation)
5455
"""
55-
mutable struct BPDiff{GT, N, T, PT} <: AbstractDiff{GT, N, T}
56+
mutable struct BPDiff{GT, N, T} <: AbstractDiff{GT, N, T}
5657
block::GT
57-
grad::PT
58+
grad::T
5859
input::AbstractRegister
59-
BPDiff(block::MatrixBlock{N, T}, grad::PT) where {N, T, PT} = new{typeof(block), N, T, typeof(grad)}(block, grad)
60+
BPDiff(block::AbstractBlock{N}, grad::T) where {N, T} = new{typeof(block), N, T}(block, grad)
6061
end
61-
BPDiff(block::MatrixBlock) = BPDiff(block, zeros(parameters_eltype(block), nparameters(block)))
62+
BPDiff(block::AbstractBlock) = BPDiff(block, zeros(parameters_eltype(block), nparameters(block)))
6263
BPDiff(block::DiffBlock{N, T}) where {N, T} = BPDiff(block, T(0))
6364

6465
content(cb::BPDiff) = cb.block
65-
chcontent(cb::BPDiff, blk::MatrixBlock) = BPDiff(blk)
66+
chcontent(cb::BPDiff, blk::AbstractBlock) = BPDiff(blk)
6667

67-
@forward BPDiff.block mat
68+
mat(::Type{T}, df::BPDiff) where T = mat(T, df.block)
6869
function apply!(reg::AbstractRegister, df::BPDiff)
6970
if isdefined(df, :input)
7071
copyto!(df.input, reg)
@@ -105,7 +106,7 @@ autodiff(mode::Symbol, block::AbstractBlock) = autodiff(Val(mode), block)
105106
autodiff(::Val{:BP}, block::DiffBlock) = BPDiff(block)
106107
autodiff(::Val{:BP}, block::AbstractBlock) = block
107108
# Sequential, Roller and ChainBlock can propagate.
108-
function autodiff(mode::Val{:BP}, blk::Union{ChainBlock, Roller, Sequential})
109+
function autodiff(mode::Val{:BP}, blk::Union{ChainBlock, Sequential})
109110
chsubblocks(blk, autodiff.(mode, subblocks(blk)))
110111
end
111112

@@ -148,11 +149,11 @@ Numeric differentiation.
148149
end
149150

150151
"""
151-
opdiff(psifunc, diffblock::AbstractDiff, op::MatrixBlock)
152+
opdiff(psifunc, diffblock::AbstractDiff, op::AbstractBlock)
152153
153154
Operator differentiation.
154155
"""
155-
@inline function opdiff(psifunc, diffblock::AbstractDiff, op::MatrixBlock)
156+
@inline function opdiff(psifunc, diffblock::AbstractDiff, op::AbstractBlock)
156157
r1, r2 = _perturb(()->expect(op, psifunc()) |> real, diffblock, π/2)
157158
diffblock.grad = (r2 - r1)/2
158159
end
@@ -215,14 +216,14 @@ end
215216
end
216217

217218
"""
218-
backward!(δ::AbstractRegister, circuit::MatrixBlock) -> AbstractRegister
219+
backward!(δ::AbstractRegister, circuit::AbstractBlock) -> AbstractRegister
219220
220221
back propagate and calculate the gradient ∂f/∂θ = 2*Re(∂f/∂ψ*⋅∂ψ*/∂θ), given ∂f/∂ψ*.
221222
222223
Note:
223224
Here, the input circuit should be a matrix block, otherwise the back propagate may not apply (like Measure operations).
224225
"""
225-
backward!::AbstractRegister, circuit::MatrixBlock) = apply!(δ, circuit')
226+
backward!::AbstractRegister, circuit::AbstractBlock) = apply!(δ, circuit')
226227

227228
"""
228229
gradient(circuit::AbstractBlock, mode::Symbol=:ANY) -> Vector

src/Grover.jl

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,21 @@ num_grover_step(psi::ArrayReg, oracle) = _num_grover_step(prob_match_oracle(psi,
3838
_num_grover_step(prob::Real) = Int(round(pi/4/sqrt(prob)))-1
3939

4040
"""
41-
GroverIter{N, T}
41+
GroverIter{N}
4242
43-
GroverIter(oracle, ref::ReflectBlock{N, T}, psi::ArrayReg, niter::Int)
43+
GroverIter(oracle, ref::ReflectBlock{N}, psi::ArrayReg, niter::Int)
4444
4545
an iterator that perform Grover operations step by step.
4646
An Grover operation consists of applying oracle and Reflection.
4747
"""
48-
struct GroverIter{N, T}
48+
struct GroverIter{N}
4949
psi::ArrayReg
5050
oracle
51-
ref::ReflectBlock{N, T}
51+
ref::ReflectBlock{N}
5252
niter::Int
5353
end
5454

55-
groveriter(psi::ArrayReg, oracle, ref::ReflectBlock{N, T}, niter::Int) where {N, T} = GroverIter{N, T}(psi, oracle, ref, niter)
55+
groveriter(psi::ArrayReg, oracle, ref::ReflectBlock{N}, niter::Int) where {N} = GroverIter{N}(psi, oracle, ref, niter)
5656
groveriter(psi::ArrayReg, oracle, niter::Int) = groveriter(psi, oracle, ReflectBlock(psi |> copy), niter)
5757
groveriter(psi::ArrayReg, oracle) = groveriter(psi, oracle, ReflectBlock(psi |> copy), num_grover_step(psi, oracle))
5858

@@ -68,17 +68,17 @@ end
6868
Base.length(it::GroverIter) = it.niter
6969

7070
"""
71-
groverblock(oracle, ref::ReflectBlock{N, T}, niter::Int=-1)
71+
groverblock(oracle, ref::ReflectBlock{N}, niter::Int=-1)
7272
groverblock(oracle, psi::ArrayReg, niter::Int=-1)
7373
7474
Return a ChainBlock/Sequential as Grover Iteration, the default `niter` will stop at the first optimal step.
7575
"""
76-
function groverblock(oracle::MatrixBlock{N, T}, ref::ReflectBlock{N, T}, niter::Int=-1) where {N, T}
76+
function groverblock(oracle::AbstractBlock{N}, ref::ReflectBlock{N}, niter::Int=-1) where {N}
7777
if niter == -1 niter = num_grover_step(ref.psi, oracle) end
7878
chain(N, chain(oracle, ref) for i = 1:niter)
7979
end
8080

81-
function groverblock(oracle, ref::ReflectBlock{N, T}, niter::Int=-1) where {N, T}
81+
function groverblock(oracle, ref::ReflectBlock{N}, niter::Int=-1) where {N}
8282
if niter == -1 niter = num_grover_step(ref.psi, oracle) end
8383
sequence(sequence(oracle, ref) for i = 1:niter)
8484
end

src/HHL.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
export hhlcircuit, hhlproject!, hhlsolve, HHLCRot
22

33
"""
4-
HHLCRot{N, NC, T} <: PrimitiveBlock{N, Complex{T}}
4+
HHLCRot{N, NC} <: PrimitiveBlock{N}
55
66
Controlled rotation gate used in HHL algorithm, applied on N qubits.
77
88
* cbits: control bits.
99
* ibit:: the ancilla bit.
1010
* C_value:: the value of constant "C", should be smaller than the spectrum "gap".
1111
"""
12-
struct HHLCRot{N, NC, T} <: PrimitiveBlock{N, Complex{T}}
12+
struct HHLCRot{N, NC, T} <: PrimitiveBlock{N}
1313
cbits::Vector{Int}
1414
ibit::Int
1515
C_value::T

src/HadamardTest.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ export hadamard_test, hadamard_test_circuit, swap_test_circuit
33
"""
44
see WiKi.
55
"""
6-
function hadamard_test_circuit(U::MatrixBlock{N}, ϕ::Real) where N
6+
function hadamard_test_circuit(U::AbstractBlock{N}, ϕ::Real) where N
77
chain(N+1, put(N+1, 1=>H),
88
put(N+1, 1=>Rz(ϕ)),
99
control(N+1, 1, 2:N+1=>U), # get matrix first, very inefficient
1010
put(N+1, 1=>H)
1111
)
1212
end
1313

14-
function hadamard_test(U::MatrixBlock{N}, reg::AbstractRegister, ϕ::Real) where N
14+
function hadamard_test(U::AbstractBlock{N}, reg::AbstractRegister, ϕ::Real) where N
1515
c = hadamard_test_circuit(U, ϕ::Real)
1616
reg = join(reg, zero_state(1))
1717
expect(put(N+1, 1=>Z), reg |> c)

src/Miscellaneous.jl

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,12 @@ function inverselines(nbit::Int; n_reg::Int=nbit)
1616
c
1717
end
1818

19-
function singlet_block(::Type{T}, nbit::Int, i::Int, j::Int) where T
19+
function singlet_block(nbit::Int, i::Int, j::Int)
2020
unit = chain(nbit)
21-
push!(unit, put(nbit, i=>chain(XGate{T}(), HGate{T}())))
22-
push!(unit, control(nbit, -i, j=>XGate{T}()))
21+
push!(unit, put(nbit, i=>chain(X, H)))
22+
push!(unit, control(nbit, -i, j=>X))
2323
end
2424

25-
singlet_block(nbit::Int, i::Int, j::Int) = singlet_block(ComplexF64, nbit, i, j)
2625
singlet_block() = singlet_block(2,1,2)
2726

2827
Yao.mat::DensityMatrix{1}) = dropdims(state(ρ), dims=3)

src/QFT.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ CRk(i::Int, j::Int, k::Int) = control([i, ], j=>shift(2π/(1<<k)))
88
CRot(n::Int, i::Int) = chain(n, i==j ? kron(i=>H) : CRk(j, i, j-i+1) for j = i:n)
99
QFTCircuit(n::Int) = chain(n, CRot(n, i) for i = 1:n)
1010

11-
struct QFTBlock{N} <: PrimitiveBlock{N,ComplexF64} end
12-
mat(q::QFTBlock{N}) where N = applymatrix(q)
11+
struct QFTBlock{N} <: PrimitiveBlock{N} end
12+
mat(::Type{T}, q::QFTBlock{N}) where {T, N} = T.(applymatrix(q))
1313

1414
apply!(reg::DefaultRegister{B}, ::QFTBlock) where B = (reg.state = ifft!(invorder_firstdim(reg |> state), 1)*sqrt(1<<nactive(reg)); reg)
15-
apply!(reg::DefaultRegister{B}, ::Daggered{N, T, <:QFTBlock}) where {B,N,T} = (reg.state = invorder_firstdim(fft!(reg|>state, 1)/sqrt(1<<nactive(reg))); reg)
15+
apply!(reg::DefaultRegister{B}, ::Daggered{N, <:QFTBlock}) where {B,N} = (reg.state = invorder_firstdim(fft!(reg|>state, 1)/sqrt(1<<nactive(reg))); reg)
1616

1717
# traits
1818
ishermitian(q::QFTBlock{N}) where N = N==1
@@ -26,7 +26,7 @@ function print_block(io::IO, pb::QFTBlock{N}) where N
2626
printstyled(io, "QFT(1-$N)"; bold=true, color=:blue)
2727
end
2828

29-
function print_block(io::IO, pb::Daggered{N, T,<:QFTBlock}) where {N, T}
29+
function print_block(io::IO, pb::Daggered{N,<:QFTBlock}) where {N, T}
3030
printstyled(io, "IQFT(1-$N)"; bold=true, color=:blue)
3131
end
3232

src/QSVD.jl

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Quantum SVD
2+
# Reference: https://arxiv.org/abs/1905.01353
3+
export QuantumSVD, circuit_qsvd, train_qsvd!, readout_qsvd
4+
5+
"""
6+
Quantum singular value decomposition algorithm.
7+
8+
* `reg`, input register (A, B) as the target matrix to decompose,
9+
* `circuit_a`, U matrix applied on register A,
10+
* `circuit_b`, V matrix applied on register B,
11+
* `optimizer`, the optimizer, normally we use `Adam(lr=0.1)`,
12+
* `Nc`, log2 number of singular values kept,
13+
* `maxiter`, the maximum number of iterations.
14+
"""
15+
function train_qsvd!(reg, circuit_a::AbstractBlock{Na}, circuit_b::AbstractBlock{Nb}, optimizer; Nc::Int=min(Na, Nb), maxiter::Int=100) where {Na, Nb}
16+
nbit = Na+Nb
17+
c = circuit_qsvd(circuit_a, circuit_b, Nc) |> autodiff(:QC) # construct a differentiable circuit for training
18+
19+
obs = -mapreduce(i->put(nbit, i=>Z), +, (1:Na..., Na+Nc+1:Na+Nb...))
20+
params = parameters(c)
21+
for i = 1:maxiter
22+
grad = opdiff.(() -> copy(reg) |> c, collect_blocks(AbstractDiff, c), Ref(obs))
23+
QuAlgorithmZoo.update!(params, grad, optimizer)
24+
println("Iter $i, Loss = $(Na+expect(obs, copy(reg) |> c))")
25+
dispatch!(c, params)
26+
end
27+
end
28+
29+
"""build the circuit for quantum SVD training."""
30+
function circuit_qsvd(circuit_a::AbstractBlock{Na}, circuit_b::AbstractBlock{Nb}, Nc::Int) where {Na, Nb}
31+
nbit = Na+Nb
32+
cnots = chain(control(nbit, i+Na, i=>X) for i=1:Nc)
33+
c = chain(concentrate(nbit, circuit_a, 1:Na), concentrate(nbit, circuit_b, Na+1:nbit), cnots)
34+
end
35+
36+
"""read QSVD results"""
37+
function readout_qsvd(reg::AbstractRegister, circuit_a::AbstractBlock{Na}, circuit_b::AbstractBlock{Nb}, Nc::Int) where {Na, Nb}
38+
reg = copy(reg) |> concentrate(Na+Nb, circuit_a, 1:Na) |> concentrate(Na+Nb, circuit_b, Na+1:Na+Nb)
39+
_S = [select(reg, b|b<<Na).state[] for b in basis(Nc)]
40+
S = abs.(_S)
41+
order = sortperm(S, rev=true)
42+
S, _S = S[order], _S[order]
43+
mat(circuit_a)[order,:]'.*transpose(_S./S), S, transpose(mat(circuit_b)[order,:])
44+
end
45+
46+
"""
47+
QuantumSVD(M; kwargs...)
48+
Quantum SVD.
49+
* `M`, the matrix to decompose, size should be (2^Na × 2^Nb), the sum of squared spectrum must be 1.
50+
kwargs includes
51+
* `Nc`, log2 number of singular values kept,
52+
* `circuit_a` and `circuit_b`, the circuit ansatz for `U` and `V` matrices,
53+
* `maxiter`, maximum number of iterations,
54+
* `optimizer`, default is `Adam(lr=0.1)`.
55+
"""
56+
function QuantumSVD(M::AbstractMatrix; Nc::Int=log2i(min(size(M)...)),
57+
circuit_a=random_diff_circuit(log2i(size(M, 1)), 5, pair_ring(log2i(size(M, 1)))),
58+
circuit_b=random_diff_circuit(log2i(size(M, 2)), 5, pair_ring(log2i(size(M, 2)))),
59+
maxiter=200, optimizer=Adam(lr=0.1))
60+
61+
dispatch!(circuit_a, :random)
62+
dispatch!(circuit_b, :random)
63+
reg = ArrayReg(vec(M))
64+
train_qsvd!(reg, circuit_a, circuit_b, optimizer, Nc=Nc, maxiter=maxiter)
65+
readout_qsvd(reg, circuit_a, circuit_b, Nc)
66+
end

0 commit comments

Comments
 (0)