QuantumBFS / Yao.jl

Extensible, Efficient Quantum Algorithm Design for Humans.

Home Page:https://yaoquantum.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Calculating Jacobian using Zygote

radras opened this issue · comments

I'm trying to calculate the Jacobian for an example that is very similar to simple_example.jl from QuAlgorithmZoo.jl.
If I modify simple_example.jl such that the return value is a single element array then calculating Jacobian like below
returns (nothing, ) while calculating the gradient on a scalar output works.
I suspect this has something to do with the way jacobian is implemented (this is written in the Zygote doc "For arguments of any type except Number & AbstractArray, the result is nothing."), however, I don't know what to do about it.

using Zygote
include("chainrules_patch.jl")

import YaoExtensions, Random

c = YaoExtensions.variational_circuit(5)
dispatch!(c, :random)

function loss(reg::AbstractRegister, circuit::AbstractBlock{N}) where N
    reg = apply(copy(reg), circuit)
    st = state(reg)
    [sum(real(st.*st))]
end

reg0 = zero_state(5)
print(jacobian(c->loss(reg0, c), c))

Thanks for reporting the issue. You do not need chainrules_patch.jl anymore in the latest Yao. Now you should define the loss function in a different (more natural) way

julia> function loss(reg::AbstractRegister, circuit, params)
           reg = apply(copy(reg), dispatch(circuit, params))
           st = state(reg)
           [sum(real(st.*st))]
       end
loss (generic function with 3 methods)

julia> Zygote.jacobian(p->loss(reg0, c, p), parameters(c))
([-0.07283422404949928 -0.12449066973901673  0.25057711019332585 -0.30987561522442586],)

If you have an output with many outputs, you should use the ForwardDiff, it works good.

julia> ForwardDiff.jacobian(p->loss(ArrayReg(Complex{eltype(p)}.(reg0.state)), c, p), parameters(c))
1×50 Matrix{Float64}:
 -0.0728342  -0.124491  0.17673  -0.160745  0.0651408  -0.19335    0.08197  -0.162243  -0.017159  -0.171337  0.250577  -0.309876

In practise, you just compare their performance and pick a better one. The larger the output dimension, the worse the Zygote works.