# How to run noisy circuits in \`Yao.jl\`

**URL:** <https://discourse.julialang.org/t/how-to-run-noisy-circuits-in-yao-jl/81775>\
**Category:** Quantum\
**Created:** [May 27, 2022, 10:36am UTC](https://discourse.julialang.org/t/how-to-run-noisy-circuits-in-yao-jl/81775 "2022-05-27T10:36:17Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)\
**Post date:** [May 27, 2022, 10:36am UTC](https://discourse.julialang.org/t/how-to-run-noisy-circuits-in-yao-jl/81775/1 "2022-05-27T10:36:17Z")

</div>

I want to run simulations of noisy circuits (e.g. with a random Pauli error `[I, X, Y, Z]` with weights `[1-3p, p, p, p]` after each gate) in Yao.jl.

My first thought was to use full density matrix simulations, but it seems that there is no support for time evolution of `DensityMatrix`es.

My second thought was running a `BatchedArrayReg` through a `UnitaryChannel`, i.e. like this

```julia
# initialize the computational zero state on all states in the register
state = zeros(ComplexF32, 2^2, 3)
state[1,:] .= 1.
reg = BatchedArrayReg(state, 3)

# create a circuit corresponding to the depolarizing channel
noisechannel = UnitaryChannel([igate(1), X, Y, Z], [0.4, 0.2, 0.2, 0.2])
circuit = put(2, 1=>noisechannel)

# and apply the circuit to the register
apply!(reg, circuit)

```

But it seems that `apply!()` samples one of the gates in the `noisechannel` and then applies the same gate to all states in the batch. Is there a way, that a different gate gets applied to all states in the register?

And tangentially related: What is the use of the `BatchedArrayRegister` when all states in the batch evolve under exactly the same circuit?

---

<div class="post-metadata">

**Author:** ![1115](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/1115/32/4465_2.png) [@1115](https://discourse.julialang.org/u/1115)\
**Post date:** [May 29, 2022, 6:27am UTC](https://discourse.julialang.org/t/how-to-run-noisy-circuits-in-yao-jl/81775/2 "2022-05-29T06:27:55Z")

</div>

We have not yet released the density matrix backend yet. Please check this pr: [Density matrix implementation by GiggleLiu · Pull Request #394 · QuantumBFS/Yao.jl · GitHub](https://github.com/QuantumBFS/Yao.jl/pull/394) . We can tag a new version at this weekend.

The batched register is for a batch of pure states, rather than the noisy circuit simulation.

---

<div class="post-metadata">

**Author:** ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)\
**Post date:** [May 30, 2022, 9:53am UTC](https://discourse.julialang.org/t/how-to-run-noisy-circuits-in-yao-jl/81775/3 "2022-05-30T09:53:53Z")

</div>

Thanks for pointing me to that PR!

Out of curiosity: So the point of a batched register is to run multiple, different states through the same circuit, but not to run one state through a probabilistic circuit?

---

<div class="post-metadata">

**Author:** ![1115](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/1115/32/4465_2.png) [@1115](https://discourse.julialang.org/u/1115)\
**Post date:** [May 30, 2022, 3:22pm UTC](https://discourse.julialang.org/t/how-to-run-noisy-circuits-in-yao-jl/81775/4 "2022-05-30T15:22:04Z")

</div>

Yes, batch is for improving the performance of GPU simulation in certain applications, like some variational algorithms.

---

<div class="post-metadata">

**Author:** ![1115](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/1115/32/4465_2.png) [@1115](https://discourse.julialang.org/u/1115)\
**Post date:** [June 1, 2022, 6:16am UTC](https://discourse.julialang.org/t/how-to-run-noisy-circuits-in-yao-jl/81775/5 "2022-06-01T06:16:29Z")

</div>

Hey, please check the newly released Yao@0.8. We have limited support to density matrix now.

```julia
julia> using Yao

julia> noisechannel = UnitaryChannel([put(2, 1=>igate(1)), put(2, 1=>X), put(2, 1=>Y), put(2, 1=>Z)], [0.4, 0.2, 0.2, 0.2])
nqubits: 2
unitary_channel
├─ [0.4] put on (1)
│ └─ igate(1)
├─ [0.2] put on (1)
│ └─ X
├─ [0.2] put on (1)
│ └─ Y
└─ [0.2] put on (1)
   └─ Z

julia> apply(density_matrix(zero_state(2)), noisechannel)
DensityMatrix{2, ComplexF64, Matrix{ComplexF64}}(ComplexF64[0.6000000000000001 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 0.4 + 0.0im 0.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im])

julia> measure(r; nshots=3)
3-element Vector{DitStr{2, 2, Int64}}:
 01 ₍₂₎
 00 ₍₂₎
 01 ₍₂₎

julia> expect(EasyBuild.heisenberg(2), r)
0.40000000000000013 + 0.0im

julia> fidelity(r, r)
1.0

```

NOTE:  
Since a `UnitaryChannel` block does not have a matrix representation, it is not composible with other blocks. It must be the outer most block. For example

```julia
julia> g = put(2, 1=>UnitaryChannel([igate(1), X, Y, Z], [0.4, 0.2, 0.2, 0.2]))
nqubits: 2
put on (1)
└─ unitary_channel
   ├─ [0.4] igate(1)
   ├─ [0.2] X
   ├─ [0.2] Y
   └─ [0.2] Z

julia> mat(g)
ERROR: `UnitaryChannel` does not have a matrix representation!
Stacktrace:
 [1] error(s::String)
   @ Base ./error.jl:35
 [2] mat(#unused#::Type{ComplexF64}, x::UnitaryChannel{2, Vector{Float64}})
   @ YaoBlocks ~/.julia/dev/Yao/lib/YaoBlocks/src/composite/unitary_channel.jl:64
 [3] mat(#unused#::Type{ComplexF64}, pb::PutBlock{2, 1, UnitaryChannel{2, Vector{Float64}}})
   @ YaoBlocks ~/.julia/dev/Yao/lib/YaoBlocks/src/composite/put_block.jl:86
 [4] mat(x::PutBlock{2, 1, UnitaryChannel{2, Vector{Float64}}})
   @ YaoBlocks ~/.julia/dev/Yao/lib/YaoBlocks/src/abstract_block.jl:120
 [5] top-level scope
   @ REPL[22]:1

```

Let me know if you want more features!

---

<div class="post-metadata">

**Author:** ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)\
**Post date:** [June 1, 2022, 10:02am UTC](https://discourse.julialang.org/t/how-to-run-noisy-circuits-in-yao-jl/81775/6 "2022-06-01T10:02:58Z")

</div>

Nice! For now it seems this does pretty much everything I need.

I was also going to ask for gradient support, but then I realized that quantum backpropagation as implemented in Yao.jl only works because evolution with unitary gates is invertible. But evolution with a unitary channel is not trivially invertible, so I guess gradients are not that easily possible

---

<div class="post-metadata">

**Author:** ![1115](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/1115/32/4465_2.png) [@1115](https://discourse.julialang.org/u/1115)\
**Post date:** [June 1, 2022, 4:53pm UTC](https://discourse.julialang.org/t/how-to-run-noisy-circuits-in-yao-jl/81775/7 "2022-06-01T16:53:04Z")

</div>

That is true. We can add the AD rule to ChainRules if you really need it. Or if you already know how to differentiate, this is where you can contribute:

> <https://github.com/QuantumBFS/Yao.jl/blob/master/lib/YaoBlocks/src/autodiff/chainrules_patch.jl>

Just specify an rrule for the the channel apply function. Then you can use it in AD engines like Zygote.

(NOTE: Zygote’s memory management and stability can be an issue though.)
