GithubHelp home page GithubHelp logo

quantumbfs / yaoblocks.jl Goto Github PK

View Code? Open in Web Editor NEW
26.0 6.0 11.0 615 KB

Standard basic quantum circuit simulator building blocks. (archived, for it is moved to Yao.jl)

License: Apache License 2.0

Julia 100.00%
julia quantum-computing intermediate-representation automatic-differentiation

yaoblocks.jl's Introduction

YaoBlocks

CI codecov

This package has been moved into the Yao repo

Standard basic quantum circuit simulator building blocks.

This is a component package for Yao.jl. It contains the abstract definitions and basic implementation of Yao's circuit building blocks.

yaoblocks.jl's People

Contributors

giggleliu avatar github-actions[bot] avatar jaipeg avatar juliatagbot avatar philipvinc avatar roger-luo avatar yihong-zhang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

yaoblocks.jl's Issues

Name changes

  • usedbits -> occupied_locations: directly follows the documentation:

occupied locations of a given block.

I tried to make this shorter, but it has to follow the convention locations, or occupied_locs. used is not accurate, it sounds like this location was used before, but still available which is in contrast to what this should mean.

  • block/parent -> content: name conflict in local scope, use content instead. This method is for all blocks with a block inside, no matter it is a primitive or composite.

  • chblock -> chsubblocks(blk, subblk): make use of multiple dispatch, when the 2nd arg is a block, just treat it as chblock, since this make sense for all composite blocks ( when chain, kron, etc. have a single content block and containers only have one content block)

  • addrs is removed, it's never used.

  • move constant gates to module ConstGate (not done yet, need to discuss what kind of common gates still need to be exported out of YaoBlockTree, e.g X, Y, Z)

  • matrixgate -> matgate: shorter name for convenience

  • measure is used to construct Measure block (no conflict with YaoBase, but is a type pirate, which is fine)

  • AddBlock -> Sum to make use of Prod's code, they are both binary operators, shares a lot common functionalities.

  • ctrl_qubits etc. -> ctrl_locs, ctrl_val/val etc. -> ctrl_config

updating...

do not export `factor`?

I hit this while implementing Shor algorithm.

This function conflicts with the factor function in Primes.jl, I think we don't need to export it by default for Scale, in most cases you can just use dot. instead of factor, after 1.0, there's no benefit to define an extra method for members anyway (since there are inspect functions like getproperty etc.)

`ControlBlock` should be primitive

  • it does not call apply! of its block member.
  • in gate counting, it's behavior is different from other composite blocks.
  • RotationGate is primitive, for consistency, control block should also be primitive.

Weird Performance of SMatrix and Matrix on instruct!

MWE:

function instruct2!(state, U, loc)
    a, c, b, d = U
    step = 1 << (loc - 1)
    step_2 = 1 << loc
    for j in 0:step_2:size(state, 1)-step
       @inbounds for i in j+1:j+step
            YaoArrayRegister.u1rows!(state, i, i+step, a, b, c, d)
       end
    end
    return state
end

The performance of instruct is quite different on my machine with Julia 1.1 with SMatrix and Matrix, unexpectedly, SMatrix is even slower. This is causing current QCBM circuit slower than before.

`mat_back!` for custom blocks

I defined my own PrimitiveBlock via

mutable struct MyBlock{T<:Real} <: YaoBlocks.PrimitiveBlock{2}
    theta::T
end

and added the neccessary mat, niparams, getiparams, setiparams, Base.adjoint and cache_key functions to it. I can use the block just fine in my circuits and

expect(hamiltonian::AbstractBlock, register::AbstractRegister => circuit::ChainBlock)

works also, where circuit contains MyBlock instances. However when I try to compute gradients via expect'(hamiltonian, register => circuit) I get the error message

MethodError: no method matching mat_back!(::Type{Complex{Float64}}, ::MyBlock{Float64}, ::Array{Complex{Float64},2}, ::Array{Any,1})
Closest candidates are:
  mat_back!(::Type{T}, ::AbstractBlock, ::Any, ::Any) where T at /home/janlukas/.julia/packages/YaoBlocks/a3uN9/src/autodiff/mat_back.jl:38
  mat_back!(::Type{T}, !Matched::RotationGate{N,RT,GT} where GT<:AbstractBlock{N}, ::Any, ::Any) where {T, N, RT} at /home/janlukas/.julia/packages/YaoBlocks/a3uN9/src/autodiff/mat_back.jl:10
  mat_back!(::Type{T}, !Matched::TimeEvolution{N,Tt,HT} where HT<:AbstractBlock{N} where Tt, ::Any, ::Any) where {N, T} at /home/janlukas/.julia/packages/YaoBlocks/a3uN9/src/autodiff/mat_back.jl:14
  ...

I tried writing my own mat_back! function for the new type like so:

import YaoBlocks.AD.projection
import YaoBlocks.mat_back!
function YaoBlocks.mat_back!(::Type{Complex{T}}, rb::HEVBlock{T}, adjy, collector) where{T}
    pushfirst!(collector, projection(rb.theta, adjy .* MyGrad(T, rb)))
end

But the error message persists.

How can I add automatic differentiation functionality to my own parametric blocks?

Scale printing is not correct

MWE:

g = kron(2, 1=>(im * Z))

got

julia> t
nqubits: 2, datatype: Complex{Float64}
kron
└─ 1=>Z gate

should be

julia> t
nqubits: 2, datatype: Complex{Float64}
kron
└─ 1=> [+im] Z gate

unintuitive error behaviour of dispatch!

the error behaviour is a bit unintuitive, it is safer to leave parameters unchanged instead of

julia> g = chain(Rx(0.1), Ry(0.2))
nqubits: 1, datatype: Complex{Float64}
chain
├─ rot(X gate, 0.1)
└─ rot(Y gate, 0.2)

julia> dispatch!(g, (0.2, ))
ERROR: BoundsError: attempt to access (0.2,)
  at index [2]
Stacktrace:
 [1] getindex at ./tuple.jl:24 [inlined]
 [2] getindex at ./range.jl:292 [inlined]
 [3] consume! at /Users/roger/.julia/dev/YaoBlocks/src/abstract_block.jl:211 [inlined]
 [4] dispatch!(::Nothing, ::RotationGate{1,Float64,YGate{Complex{Float64}}}, ::YaoBlocks.Dispatcher{Tuple{Float64}}) at /Users/roger/.julia/dev/YaoBlocks/src/abstract_block.jl:225
 [5] dispatch!(::Nothing, ::ChainBlock{1,Complex{Float64}}, ::YaoBlocks.Dispatcher{Tuple{Float64}}) at /Users/roger/.julia/dev/YaoBlocks/src/abstract_block.jl:227
 [6] dispatch!(::Nothing, ::ChainBlock{1,Complex{Float64}}, ::Tuple{Float64}) at /Users/roger/.julia/dev/YaoBlocks/src/abstract_block.jl:234
 [7] dispatch!(::ChainBlock{1,Complex{Float64}}, ::Tuple{Float64}) at /Users/roger/.julia/dev/YaoBlocks/src/abstract_block.jl:239
 [8] top-level scope at none:0

julia> g
nqubits: 1, datatype: Complex{Float64}
chain
├─ rot(X gate, 0.2)
└─ rot(Y gate, 0.2)

XRef: #31

make `RotationBlock` composite block

It looks strange that:

julia> rot(chain(X, X), 0.1)
rot(nqubits: 1, datatype: Complex{Float64}
chain
├─ X gate
└─ X gate, 0.1)

it would be much better if it is just a container

julia> rot(chain(X, X), 0.1)
nqubits: 1, datatype: Complex{Float64}
rot(0.1)
└─ chain
          ├─ X gate
         └─ X gate, 0.1

since composite should simply means the composition of other blocks.

Issues related to Benchmarks

  • In primitive block benchmark, we should use put rather than repeat?
  • Do not use rand in this benchmark, please specify a deterministic position for benchmark.

remove `T` in block parameters

abstract type AbstractBlock{N, T} end

T is mainly for matrices, which is not useful in most cases, and will causing the following things to work/doesn't work, which completely make no sense since blocks are only representations of quantum operators, and do not have specific dependency on concrete types.

  • 32bit register can use 64bit circuit
  • 32bit register on CUDA RAM can use 64bit circuit on CPU RAM (since we actually do not specify the type or device in T)

This should be fixed before we become stable (v1.0), and will results in the following un-breaking changes:

  • since T is removed, and is only needed for mat and methods use mat, we could add an extra optional arg to mat as mat([T=ComplexF64], x)
  • currently, mat will try to return the matrix we think the most efficient for the block, but it is quite confusing and not convenient when we need the matrix, it would be nice to have some common matrices type constructor to work on blocks. and define some convert functions as well.

this will break some extension, which subtyping our AbstractBlock, but should be very easy to fix (by simply deleting all Ts) for downstream

StaticArrays warning

if you try to run the test, we get this now, seems to be

 Warning: `a::StaticArray + b::Number` is deprecated, use `a .+ b` instead.
│   caller = (::getfield(YaoBlocks, Symbol("##16#17")){StaticArrays.SArray{Tuple{2,2},Basic,2,4},StaticArrays.SArray{Tuple{2},Int64,1,2},SparseArrays.SparseMatrixCSC{Basic,Int64}})(::Int64) at routines.jl:201
└ @ YaoBlocks ~/.julia/packages/YaoBlocks/RfMRn/src/routines.jl:201

Sum does not work for SubArray

MWE:

r = ArrayReg(view(rand(ComplexF64, 16, 2), :, 1))
g = put(4, 1=>X) + put(4, 2=>X)
r |> g

This cause the Lancoz process in time evolution block fails.

define my custom gate but cannot use mat function

Hi there, I use Yao.jl for my quantum computation project, where I should define a new gate with some parameters.

After the definition of my gate and its mat function, I can compute the matrix of mygate.i.e: mat(ComplexF64, mygate) works well.

However, I find error MethodError: no method matching mat(::Type{ComplexF64}, ::mygate), when I use Matrix(chain(n, mygate)) and operator_fidelity(block, mygate). Did I do something incorrect?

[Feature Request] AncillasBlock

This is just a reminder to add an AncillasBlock to this package which add several ancilla qubits and then measure them out.

Batched block

We have already got batched register for solving the sampling problem.

Also, I find pretty useful to have batched blocks, especially in simulating kernel quantum algorithms in

Schuld, Maria, and Nathan Killoran. "Quantum machine learning in feature Hilbert spaces." Physical review letters 122.4 (2019): 040504.

A possible prototype

using Yao

struct Batched{N, T, BT<:AbstractBlock{N, T}} <: CompositeBlock{N, T}
    blocks::Vector{<:BT}
end

Base.broadcastable(bb::Batched) = bb.blocks

function Yao.apply!(reg::AbstractRegister{B}, bb::Batched) where B
    B == length(bb.blocks) || error()
    Yao.apply!.(reg, bb)
    reg
end

zero_state(4, nbatch=10) |> bb |> state

The problem is how do we dispatch a Batched block? Or just disallow dispatch! a scalar for such a block?

Better printing

Measure, show locs, collapseto and operator information
Control, controlled qubits

Make measurement result change inplace

Change mb.result = measure(...) to

res = measure(...)
if typeof(res) == typeof(mb) && size(res) == size(mb)
    mb.result[:] .= res
else
    isdefined(res, :result) && warn("Measure result output stream changed! Giveup allocated memory and use new one.")
    mb.result = res
end

For two reasons,

  • It is easier to port QASM, where classical memory creg can be mapped to a constant address. (or can we use measure block itself as creg? sounds a bit strange)
  • When porting real device, mb.result can act as a shared memory between quantum program and classical program.

return the expectation and its gridient in the same function exp'

can we put the function exp' in YaoBlocks/src/autodiff/specializes.jl to support return the expection and its gridients?
I try this by adding the energy = copy(outδ)'*copy(out), but I feel the parameters or energy dose not change in the iteration.
Is there is something wrong due to the parameters is changed somehow?
iteration and Loss function energy is : 1 -6.783714426018552 + 0.0im
iteration and Loss function energy is : 2 -6.783714426018552 + 0.0im
iteration and Loss function energy is : 3 -6.783714426018552 + 0.0im
iteration and Loss function energy is : 4 -6.783714426018552 + 0.0im
iteration and Loss function energy is : 5 -6.783714426018552 + 0.0im
iteration and Loss function energy is : 6 -6.783714426018552 + 0.0im
iteration and Loss function energy is : 7 -6.783714426018552 + 0.0im
iteration and Loss function energy is : 8 -6.783714426018552 + 0.0im
iteration and Loss function energy is : 9 -6.783714426018552 + 0.0im
iteration and Loss function energy is : 10 -6.783714426018552 + 0.0im
iteration and Loss function energy is : 11 -6.783714426018552 + 0.0im
iteration and Loss function energy is : 12 -6.783714426018552 + 0.0im

I use the optimizier lbfgs. previously before I change the function expect' seems everything ok.

function lbfgs_optimize(circuit::AbstractBlock{N}, hc, niter::Int) where N
iteration = 0
function f(params, grad)
# reg = zero_state(N) |> dispatch!(circuit, params)
_, grad1 = expect'(hc,zero_state(N)=>circuit)
print("grad1 is ",grad1)
for i= 1: length(grad1)
grad[i] = grad1[i]
end
loss = expect(hc, reg) |> real
println("Loss function is", loss)
iteration += 1
loss
end
opt = Opt(:LD_LBFGS, nparameters(circuit))
min_objective!(opt, f)
maxeval!(opt, niter)

print("here is ok")

ftol_rel!(opt,1e-7) # default this value

cost, params, info = optimize(opt, parameters(circuit))

pl = zero_state(N) |> circuit |> probs

print("info is", info)

print("iteration time is ", iteration)

cost, params, info, iteration

end

This is the origin expect' function(except the comment)

function (::Adjoint{Any,typeof(expect)})(op::AbstractBlock, circuit::Pair{<:ArrayReg,<:AbstractBlock})
reg, c = circuit
#println("try to modify")
out = copy(reg) |> c
outδ = copy(out) |> op
#energy = copy(outδ)'copy(out)
#println("energy is ", energy)
(in, inδ), paramsδ = apply_back((out, outδ), c)
return inδ => paramsδ .
2
end

setiparams! for complex array

setiparams! is not defined for complex arrays?

Also, the error message for is wrong,
ERROR: LoadError: setparams!(x, θ...) is not implemented
should be setiparams!(x, θ...) like the function name


N = 4

circuit = chain(N)
append!(circuit, [TimeEvolution(put(N, 1=>X),  0.5im)])

dispatch!(circuit, rand(1))

dispatch!(circuit, 1im*rand(1))

Stacktrace:
 [1] error(::String) at ./error.jl:33
 [2] setiparams!(::TimeEvolution{4,Float64,Complex{Float64},PutBlock{4,1,XGate}}, ::Complex{Float64}) at /home/meach/.julia/packages/YaoBlocks/lIpWc/src/abstract_block.jl:136
 [3] setiparams! at /home/meach/.julia/packages/YaoBlocks/lIpWc/src/abstract_block.jl:135 [inlined]
 [4] setiparams! at /home/meach/.julia/packages/YaoBlocks/lIpWc/src/abstract_block.jl:145 [inlined]
 [5] dispatch!(::Nothing, ::TimeEvolution{4,Float64,Complex{Float64},PutBlock{4,1,XGate}}, ::YaoBlocks.Dispatcher{Array{Complex{Float64},1}}) at /home/meach/.julia/packages/YaoBlocks/lIpWc/src/abstract_block.jl:233
 [6] dispatch!(::Nothing, ::ChainBlock{4}, ::YaoBlocks.Dispatcher{Array{Complex{Float64},1}}) at /home/meach/.julia/packages/YaoBlocks/lIpWc/src/abstract_block.jl:235
 [7] dispatch!(::Nothing, ::ChainBlock{4}, ::Array{Complex{Float64},1}) at /home/meach/.julia/packages/YaoBlocks/lIpWc/src/abstract_block.jl:251
 [8] dispatch!(::ChainBlock{4}, ::Array{Complex{Float64},1}) at /home/meach/.julia/packages/YaoBlocks/lIpWc/src/abstract_block.jl:256
 [9] top-level scope at /home/meach/git_projects/QuAlgorithmZoo.jl/mwe.jl:10
 [10] include at ./boot.jl:328 [inlined]
 [11] include_relative(::Module, ::String) at ./loading.jl:1094
 [12] include(::Module, ::String) at ./Base.jl:31
 [13] include(::String) at ./client.jl:432
 [14] top-level scope at REPL[2]:1
in expression starting at /home/meach/git_projects/QuAlgorithmZoo.jl/mwe.jl:10

long type parameters in Static Types

@Roger-luo
I tried to construct a hamiltonian, but got the following printing information in error message. I worried about the performance of having the following long type parameters. It is possible?
I wonder why static type parameter would be useful in optimization.
It is pretty heavy in printing.

solving hamiltonian: Error During Test at /home/leo/.julia/dev/QuAlgorithmZoo/test/hamiltonian_solvers.jl:6
  Got exception outside of a @test
  MethodError: no method matching TimeEvolution(::CachedBlock{CacheServers.DefaultServer{AbstractBlock,CacheFragment},
Sum{8,Complex{Float64},Tuple{Prod{8,Complex{Float64},Tuple{PutBlock{8,1,1,Complex{Float64
},XGate{Complex{Float64}}},PutBlock{8,1,1,Complex{Float64},XGate{Complex{Float64}}}}},Prod{
8,Complex{Float64},Tuple{PutBlock{8,1,1,Complex{Float64},YGate{Complex{Float64}}},PutBlock
{8,1,1,Complex{Float64},YGate{Complex{Float64}}}}},Prod{8,Complex{Float64},Tuple{PutBlock{8
,1,1,Complex{Float64},ZGate{Complex{Float64}}},PutBlock{8,1,1,Complex{Float64},ZGate{Comp
lex{Float64}}}}},Prod{8,Complex{Float64},Tuple{PutBlock{8,1,1,Complex{Float64},XGate{Comple
x{Float64}}},PutBlock{8,1,1,Complex{Float64},XGate{Complex{Float64}}}}},Prod{8,Complex{Float
64},Tuple{PutBlock{8,1,1,Complex{Float64},YGate{Complex{Float64}}},PutBlock{8,1,1,Complex{
Float64},YGate{Complex{Float64}}}}},Prod{8,Complex{Float64},Tuple{PutBlock{8,1,1,Complex{Fl
oat64},ZGate{Complex{Float64}}},PutBlock{8,1,1,Complex{Float64},ZGate{Complex{Float64}}}}},
Prod{8,Complex{Float64},Tuple{PutBlock{8,1,1,Complex{Float64},XGate{Complex{Float64}}},Put
Block{8,1,1,Complex{Float64},XGate{Complex{Float64}}}}},Prod{8,Complex{Float64},Tuple{PutBl
ock{8,1,1,Complex{Float64},YGate{Complex{Float64}}},PutBlock{8,1,1,Complex{Float64},YGate{
Complex{Float64}}}}},Prod{8,Complex{Float64},Tuple{PutBlock{8,1,1,Complex{Float64},ZGate{C
omplex{Float64}}},PutBlock{8,1,1,Complex{Float64},ZGate{Complex{Float64}}}}},Prod{8,Comple
x{Float64},Tuple{PutBlock{8,1,1,Complex{Float64},XGate{Complex{Float64}}},PutBlock{8,1,1,Co
mplex{Float64},XGate{Complex{Float64}}}}},Prod{8,Complex{Float64},Tuple{PutBlock{8,1,1,Com
plex{Float64},YGate{Complex{Float64}}},PutBlock{8,1,1,Complex{Float64},YGate{Complex{Float
64}}}}},Prod{8,Complex{Float64},Tuple{PutBlock{8,1,1,Complex{Float64},ZGate{Complex{Float6
4}}},PutBlock{8,1,1,Complex{Float64},ZGate{Complex{Float64}}}}},Prod{8,Complex{Float64},Tupl
e{PutBlock{8,1,1,Complex{Float64},XGate{Complex{Float64}}},PutBlock{8,1,1,Complex{Float64},
XGate{Complex{Float64}}}}},Prod{8,Complex{Float64},Tuple{PutBlock{8,1,1,Complex{Float64},Y
Gate{Complex{Float64}}},PutBlock{8,1,1,Complex{Float64},YGate{Complex{Float64}}}}},Prod{8,
Complex{Float64},Tuple{PutBlock{8,1,1,Complex{Float64},ZGate{Complex{Float64}}},PutBlock{8
,1,1,Complex{Float64},ZGate{Complex{Float64}}}}},Prod{8,Complex{Float64},Tuple{PutBlock{8,1
,1,Complex{Float64},XGate{Complex{Float64}}},PutBlock{8,1,1,Complex{Float64},XGate{Comple
x{Float64}}}}},Prod{8,Complex{Float64},Tuple{PutBlock{8,1,1,Complex{Float64},YGate{Complex{
Float64}}},PutBlock{8,1,1,Complex{Float64},YGate{Complex{Float64}}}}},Prod{8,Complex{Float6
4},Tuple{PutBlock{8,1,1,Complex{Float64},ZGate{Complex{Float64}}},PutBlock{8,1,1,Complex{Fl
oat64},ZGate{Complex{Float64}}}}},Prod{8,Complex{Float64},Tuple{PutBlock{8,1,1,Complex{Flo
at64},XGate{Complex{Float64}}},PutBlock{8,1,1,Complex{Float64},XGate{Complex{Float64}}}}},P
rod{8,Complex{Float64},Tuple{PutBlock{8,1,1,Complex{Float64},YGate{Complex{Float64}}},PutB
lock{8,1,1,Complex{Float64},YGate{Complex{Float64}}}}},Prod{8,Complex{Float64},Tuple{PutBlo
ck{8,1,1,Complex{Float64},ZGate{Complex{Float64}}},PutBlock{8,1,1,Complex{Float64},ZGate{C
omplex{Float64}}}}},Prod{8,Complex{Float64},Tuple{PutBlock{8,1,1,Complex{Float64},XGate{Co
mplex{Float64}}},PutBlock{8,1,1,Complex{Float64},XGate{Complex{Float64}}}}},Prod{8,Complex{
Float64},Tuple{PutBlock{8,1,1,Complex{Float64},YGate{Complex{Float64}}},PutBlock{8,1,1,Com
plex{Float64},YGate{Complex{Float64}}}}},Prod{8,Complex{Float64},Tuple{PutBlock{8,1,1,Compl
ex{Float64},ZGate{Complex{Float64}}},PutBlock{8,1,1,Complex{Float64},ZGate{Complex{Float6
4}}}}}}},8,Complex{Float64}}, ::Complex{Int64})

how can I implement two blocks sharing same parameters?

Hi there. I am going to implement a quantum circuit using Yao.jl, which contains two blocks sharing same parameters. Let's say U=u(a)s(b)v(a) and a b are variational parameters in this circuit.

  1. Specifically I can implement this circuit manually and compute their gradients using some custom methods, which is also my current method. However, if the structures of u(a) and v(a) are more complex, manual implementation becomes somehow tedious and boring.

  2. I noticed that the same block instance has sharing parameters, but potentially I have different blocks in u(a) and v(a) (a specific circumstance is u=dagger(v) which is like VFF in arXiv:1910.04292 ).

  3. I also tried Daggered method, but it looks like it returns a new block with no sharing parameters.

  4. I also thought a custom block, but in order to obtain the AD function, I have to implement mat_back! method, which is as complex as manual implementation.

So, is it possible to do this thing directly in Yao.jl and also obtain the AD function?

thanks your time!

Zygote: transposing a block yields DimensionMismatch (or missing adjoint)

For certain kinds of WF overlap gradients, one would need to conjugate-transpose a unitary that has appeared before, so one needs both the regular and daggered version to appear in the expression (cost or loss function). However, I get errors when using the daggered version.

A minimal reproducing example with even just 1 block, which for now is the non-daggered version, U:

using Zygote
using Yao
using YaoBlocks

N=2
psi_0 = zero_state(N)
U0 = chain(N, put(1=>Rx(0.0)), put(2=>Ry(0.0)))
C = sum([chain(N, put(k=>Z)) for k=1:N])

function loss(theta)
    U = dispatch(U0, theta)
    psi0 = copy(psi_0)
    psi1 = apply(psi0, U)
    psi2 = apply(psi1, C)
    result = real(sum(conj(state(psi1)) .* state(psi2)))
    return result
end

theta = [1.1,2.2]
println(expect'(C, copy(psi_0) => dispatch(U0, theta))[2])
grad = Zygote.gradient(theta->loss(theta), theta)[1]
println(grad)

In this case, the above loss function computes effectively an expectation value equivalent to expect(C, psi_0 => U). Therefore, expect' and zygote.gradient yield the same result [-0.8912073600614354, -0.8084964038195902], as expected.

However, if we instead select the conjugate transpose, daggered version, of U, the expect' version correctly returns the gradient as

julia> expect'(C, copy(psi_0) => dispatch(U0, theta)')[2]
2-element Vector{Float64}:
 0.8084964038195901
 0.8912073600614354

But when I attempt the same in Zygote by setting psi1 = apply(psi0, U') we get an error message:

ERROR: LoadError: DimensionMismatch("variable with size(x) == (2,) cannot have a gradient with size(dx) == (1, 2)")
Stacktrace:
 [1] (::ChainRulesCore.ProjectTo{AbstractArray, NamedTuple{(:element, :axes), Tuple{ChainRulesCore.ProjectTo{Float64, NamedTuple{(), Tuple{}}}, Tuple{Base.OneTo{Int64}}}}})(dx::Matrix{Float64})
   @ ChainRulesCore ~/.julia/packages/ChainRulesCore/7ZiwT/src/projection.jl:226
 [2] ProjectTo
   @ ~/.julia/packages/ChainRulesCore/7ZiwT/src/projection.jl:247 [inlined]
 [3] _project
   @ ~/.julia/packages/Zygote/AlLTp/src/compiler/chainrules.jl:182 [inlined]
 [4] map(f::typeof(Zygote._project), t::Tuple{Vector{Float64}}, s::Tuple{LinearAlgebra.Adjoint{Float64, Vector{Float64}}})
   @ Base ./tuple.jl:232
 [5] gradient(f::Function, args::Vector{Float64})
   @ Zygote ~/.julia/packages/Zygote/AlLTp/src/compiler/interface.jl:77
 [6] top-level scope
   @ ~/zygote_bug_reproducer.jl:21
 [7] include(fname::String)
   @ Base.MainInclude ./client.jl:444
 [8] top-level scope
   @ REPL[6]:1
in expression starting at /zygote_bug_reproducer.jl:21

I have also tried copying U first by setting psi1 = apply(psi0, copy(U)') or psi1 = apply(psi0, copy(U')), (because the 'lazy' dagger operation might cause trouble?) but then I think Zygote gets lost tracking parameters across the copied block, because I get another kind of error even if I don't perform the dagger operation but simply psi1 = apply(psi0, copy(U)):

ERROR: LoadError: Need an adjoint for constructor ChainBlock{2}. Gradient is of type LinearAlgebra.Adjoint{Float64, Vector{Float64}}

any thoughts on how to enable daggered blocks with the Zygote patch? Am I taking the wrong approach, or is it simply defining the correct adjoint/rule like @GiggleLiu did for the other blocks like Add? is it an issue with double-daggered definitions? Thanks as always!

TagBot trigger issue

This issue is used to trigger TagBot; feel free to unsubscribe.

If you haven't already, you should update your TagBot.yml to include issue comment triggers.
Please see this post on Discourse for instructions and more details.

Zygote patch not working when involving non-unitary blocks

Thank you @GiggleLiu for #166 . I could run your example successfully with 0.11.7, but I ran into the following issue that I'm trying to solve.
When I run the following slightly modified code

using Yao
using Random
using Zygote
using YaoArrayRegister
using ForwardDiff
using Test

H = chain(5, put(1=>Z))
# H = sum([chain(5, put(k=>Z)) for k=1:5])

c = chain(put(5, 2=>chain(Rx(0.4), Rx(0.5))), cnot(5, 3, 1), put(5, 3=>Rx(-0.5)))
dispatch!(c, :random)
function loss(reg::AbstractRegister, circuit::AbstractBlock{N}) where N
        reg = apply(copy(reg), circuit)
        st = state(reg)
        reg2 = apply(copy(reg), H)
        st2 = state(reg2)
        sum(real(st.*st2))
end

reg0 = zero_state(5)
params = rand!(parameters(c))
paramsδ = Zygote.gradient(params->loss(reg0, dispatch(c, params)), params)[1]
fparamsδ = ForwardDiff.gradient(params->loss(ArrayReg(Matrix{Complex{eltype(params)}}(reg0.state)), dispatch(c, params)), params)
@test fparamsδ ≈ paramsδ

Tests pass. Note that H is not the 'circuit' like in your example, but another unitary operator.
However, when I switch H to a sum of unitaries (like a Hamiltonian, H = sum([chain(5, put(k=>X)) for k=1:5])) I get the following error:

ERROR: LoadError: UndefKeywordError: keyword argument in not assigned
Stacktrace:
  [1] apply_back!(st::Tuple{ArrayReg{1, ComplexF64, Matrix{ComplexF64}}, ArrayReg{1, ComplexF64, Matrix{ComplexF64}}}, circuit::Add{5}, collector::Vector{Any})
    @ YaoBlocks.AD ~/.julia/packages/YaoBlocks/xRlSK/src/autodiff/apply_back.jl:112
  [2] apply_back(st::Tuple{ArrayReg{1, ComplexF64, Matrix{ComplexF64}}, ArrayReg{1, ComplexF64, Matrix{ComplexF64}}}, block::Add{5}; kwargs::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ YaoBlocks.AD ~/.julia/packages/YaoBlocks/xRlSK/src/autodiff/apply_back.jl:151
  [3] apply_back(st::Tuple{ArrayReg{1, ComplexF64, Matrix{ComplexF64}}, ArrayReg{1, ComplexF64, Matrix{ComplexF64}}}, block::Add{5})
    @ YaoBlocks.AD ~/.julia/packages/YaoBlocks/xRlSK/src/autodiff/apply_back.jl:150
  [4] (::YaoBlocks.AD.var"#21#22"{Add{5}, ArrayReg{1, ComplexF64, Matrix{ComplexF64}}})(outδ::ArrayReg{1, ComplexF64, Matrix{ComplexF64}})
    @ YaoBlocks.AD ~/.julia/packages/YaoBlocks/xRlSK/src/autodiff/chainrules_patch.jl:6
  [5] ZBack
    @ ~/.julia/packages/Zygote/AlLTp/src/compiler/chainrules.jl:204 [inlined]
  [6] Pullback
    @ ~/test.jl:16 [inlined]
  [7] (::typeof(∂(loss)))(Δ::Float64)
    @ Zygote ~/.julia/packages/Zygote/AlLTp/src/compiler/interface2.jl:0
  [8] Pullback
    @ ~/test.jl:23 [inlined]
  [9] (::typeof(∂(#9)))(Δ::Float64)
    @ Zygote ~/.julia/packages/Zygote/AlLTp/src/compiler/interface2.jl:0
 [10] (::Zygote.var"#55#56"{typeof(∂(#9))})(Δ::Float64)
    @ Zygote ~/.julia/packages/Zygote/AlLTp/src/compiler/interface.jl:41
 [11] gradient(f::Function, args::Vector{Float64})
    @ Zygote ~/.julia/packages/Zygote/AlLTp/src/compiler/interface.jl:76
 [12] top-level scope
    @ ~/test.jl:23
 [13] include(fname::String)
    @ Base.MainInclude ./client.jl:444
 [14] top-level scope
    @ REPL[1]:1

I am not sure if I am doing something wrong here, or whether it is working as intended? Thanks beforehand!

[Feature Request] Use Clifford to replace Pauli String

Clifford group can be stored by a tableau and be calculated in poly time (on stabilizer states) with either the tableau based algorithm or graph states. A block for Cliffords is more useful when construct/transform/calculate things like graph state, error correction codes.

But since this is deeply related to graph states, this might should go into another package with graph state together.

Related work: Cliffords.jl
FYI: https://arxiv.org/pdf/quant-ph/0602096.pdf

Return type for mat()

mat returns ComplexF64, by default. For GeneralMatrixBlock , it should return the element type of the matrix.

julia> M = rand(Float32,2,2)
2×2 Array{Float32,2}:
 0.0412188  0.879695
 0.251251   0.767503

julia> MB = matblock(M)
matblock(...)

julia> typeof(MB)
GeneralMatrixBlock{1,1,Complex{Float32},Array{Complex{Float32},2}}

julia> mat(MB)
┌ Warning: converting Float32 to eltype Complex{Float64}, consider create another matblock with eltype Complex{Float64}
└ @ YaoBlocks ~/.julia/packages/YaoBlocks/Q5IBc/src/primitive/general_matrix_gate.jl:52
2×2 Array{Complex{Float64},2}:
 0.0412188+0.0im  0.879695+0.0im
  0.251251+0.0im  0.767503+0.0im

missing single function constructor chain

MWE:

chain(4, n->kron(n, 1=>H))

causing error:

CRk(i::Int, j::Int, k::Int) = control([i, ], j=>shift(2π/(1<<k)))
CRot(n::Int, i::Int) = chain(i==j ? kron(i=>H) : CRk(j, i, j-i+1) for j = i:n)
QFTCircuit(n::Int) = chain(n, CRot(n, i) for i = 1:n)

[Feature Request] Classical Control Block

So sometimes it's useful to embed a classical control inside the circuit, it controls a part of the circuit by measuring some qubits out.

The desired behavior can be measure and remove or measure and collapse to some qubits config.

incorrect dispatch!

julia> dispatch!(chain(Rx(0.1), Rx(0.2)), [0.3, 0])
ERROR: AssertionError: expect 1 parameters, got 2
Stacktrace:
 [1] dispatch!(::RotationGate{1,Float64,XGate{Complex{Float64}}}, ::Base.Iterators.Drop{Array{Float64,1}}) at /home/leo/.julia/dev/YaoBlocks/src/abstract_block.jl:224
 [2] dispatch!(::ChainBlock{1,Complex{Float64},AbstractBlock{1,Complex{Float64}}}, ::Array{Float64,1}) at /home/leo/.julia/dev/YaoBlocks/src/abstract_block.jl:228
 [3] top-level scope at none:0

What does `adjcunmat` do?

I am trying to get quantum backpropagation with GPU acceleration working for MyCustomBlock <: YaoBlocks.PrimitiveBlock{2}. It has only one real parameter (like e.g. a rotation gate) and in my circuits always occurs inside a PuttBlock. Hence the easiest solution seemed to be to write my own

mat_back!(::Type{T}, rb::PutBlock{N,C,MyCustomBlock}, adjy, collector)

to replace the version in YaoBlocks/autodiff/mat_back.jl:43. But I don't exactly understand
what adjcunmat in line 45 does and what I should do instead

Use SimpleTraits for block properties like ishermitian etc.

Currently the traits e.g PreserveAll is implemented manually, we should make use of SimpleTraits to make this more elegant and powerful.

e.g with trait IsHermitian, we could dispatch time evolution's expv based on this trait, and the circuit simplification should be easier to make use of multiple dispatch.

The different behaviors between `expect` and `expect'` when inputting a pair of register and circuit

I was reading the documentation of Yao and found that in the description of function expect', the Note (blue box) says that expect' is a slight modification of expect for the cases of batched registers when we input a pair of register and circuit, which I assume implies that expect can also return gradients.

However, it is also stated that "expect" only returns the expectation value of the observable, which is contradictory to the Note (blue box).

From my test

julia> expect(h, zero_state(4) => c) == expect(h, zero_state(4) |> c)
true
julia> expect'(h, zero_state(4)|>c)
ArrayReg{1, Complex{Float64}, Array...}
    active qubits: 4/4

So it seems that expect just returns the expectation value in either case and expect' doesn't return expectation value even when we don't input the pair of register and circuit.

Do you intend to differ expect and expect' by just the functionality of accumulating the outputs for multiple-batch cases? Or do you want to specify expect' as the function to get gradients of a differentiable circuit? So far I find the logic and documentation about those two functions are a bit confusing.

Also, I think it would be better if you could specify the requirement of the circuit inside the input pair. Does it have to be the variational_circuit from YaoExtension.jl or can it be any parameterized circuit but marked with differentiable blocks?

Thank you very much!

incorrect error msg

julia> kron(2, (1, 2)=>X)
ERROR: location of sparse distributed blocks must be explicit declared with pair (e.g 2=>X)

We need better error msg for types as well instead of suggesting pairs.

Better error information for blocks with undefined setiparams!

Now is

julia> setiparams!(chain(5), (0.3,))
ERROR: StackOverflowError:
Stacktrace:
 [1] setiparams!(::ChainBlock{5,Complex{Float64},AbstractBlock{5,Complex{Float64}}}, ::Float64) at /home/leo/.julia/dev/YaoBlocks/src/abstract_block.jl:132 (repeats 8122 times)
 [2] top-level scope at none:0

Should throw a Number of parameter mismatch error.

This line of code

setiparams!(x::AbstractBlock, it) = setiparams!(x, it...)

should be written in a more restrictive way.

merge Pauli gates for composite blocks

Currently only single qubit pauli gate in chain and prod supports merging pauli gates.

Composite blocks like kron and PauliString with pure Pauli gates can be merged by when inside chain/prod as well.

I'll leave this to next release, so we can have some time play with this new optimization pass see what needs to improve.

Preserve `chain` type signature when simplifying the circuit

Current, for simplicity in implementation, I just make use of the Prod's simplification rules for chain, but this will cause chain become a prod after simplification.

This is not a big deal, since:

  1. these two blocks are equivalent mathematically, (the only difference is that prod uses the order of production and is static, and chain follows the order of application with a dynamic list).
  2. the process of simplify itself will and has to change the type, so I think it is generally fine.

But it might have some inconvenience in some cases, since prod is immutable while chain is mutable. I'll leave this issue here, to see if there's any use cases on this in the future then we shall consider change this behaviour.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.