"Compiling" HIR produced by `ZXCalculus.jl`

I would like to explore the affects of ZX calculus on the synthesis of certain quantum circuits. Thankfully, I saw that Yao has a native Julia implementation in ZXCalculus.jl! However, it seems to me its documentation might be outdated, and/or the package itself is out of lockstep with Yao.jl. My use case is as follows:

using ZXCalculus: ZX
zxd = ZXDiagram(N) # N number of qubits
# push gates onto the circuit, e.g. for some gate list
map(push_gate!(zxd, Val(:CNOT), g[1], g[2]), gates)
circuit = circuit_extraction(clifford_simplification(zxd))

The type produces is typeof(circuit) == YaoHIR.Chain, which I am not sure how to manipulate (YaoHIR.jl documentation link is broken). How can I proceed further with this output? In particular, I’d like to find the depth of the circuit specificed by this IR (do I need to pick some compilation backend), and preferably also visualize the circuit.

Hi, thanks for trying out. The package YaoHIR is not maintained in a good state. We cannot convert it to YaoBlocks because there is insufficient support for the ZX calculus-generated circuit. I’m (slowly) updating the IR to the next-gen with a refactor of Yao, but this is not done yet.

2 Likes

Thanks for your response. I am dealing with a rather small “gateset”, so the outputs I receive in circuit, as far as I can read it, contain only very few gate types (Zs, Xs, Hadamards, CNOTs). Is there a way for me to hack together a solution that manually translates this into some supported IR / circuit specification that I could then compile?

YaoHIR itself is only a straightforward IR definition (if you are willing to look at the implementation)

YaoCompiler infras should still work with 1.6 and they are tested, and you can find a lot of examples in the tests. I’m just not developing it, e.g you can start with a circuit defined via @device in Julia

using MLStyle
using YaoLocations
using YaoCompiler
using YaoCompiler.Intrinsics
using OpenQASM
using YaoTargetQASM
using YaoTargetQASM.QASM

@device function test_cir()
    5 => H
    @ctrl 4 5 => X
    5 => shift(7 / 4 * π)
    @ctrl 1 5 => X
    5 => T
    @ctrl 4 5 => X
    5 => shift(7 / 4 * π)
    @ctrl 1 5 => X
    4 => T
    5 => T
    @ctrl 1 4 => X
    4 => shift(7 / 4 * π)
    1 => T
    @ctrl 1 4 => X
    @ctrl 4 5 => X
    5 => shift(7 / 4 * π)
    @ctrl 3 5 => X
    5 => T
    @ctrl 4 5 => X
    5 => shift(7 / 4 * π)
    @ctrl 3 5 => X
    4 => T
    5 => T
    @ctrl 3 4 => X
    4 => shift(7 / 4 * π)
    5 => H
    3 => T
    @ctrl 3 4 => X
    @ctrl 4 5 => X
    5 => H
    @ctrl 3 5 => X
    5 => shift(7 / 4 * π)
    @ctrl 2 5 => X
    5 => T
    @ctrl 3 5 => X
    5 => shift(7 / 4 * π)
    @ctrl 2 5 => X
    3 => T
    5 => T
    @ctrl 2 3 => X
    3 => shift(7 / 4 * π)
    5 => H
    2 => T
    @ctrl 2 3 => X
    @ctrl 3 5 => X
    5 => H
    @ctrl 2 5 => X
    5 => shift(7 / 4 * π)
    @ctrl 1 5 => X
    5 => T
    @ctrl 2 5 => X
    5 => shift(7 / 4 * π)
    @ctrl 1 5 => X
    2 => T
    5 => T
    @ctrl 1 2 => X
    2 => shift(7 / 4 * π)
    5 => H
    1 => T
    @ctrl 1 2 => X
    @ctrl 2 5 => X
    @ctrl 1 5 => X
end


qasm = YaoCompiler.compile(OpenQASMTarget(), Intrinsics.apply, Tuple{AnyReg,Operation{typeof(test_cir), Tuple{}}}, HardwareFreeOptions(clifford_simplification=true))

which outputs the QASM 2.0 AST defined by OpenQASM, and you can copy that as a string and output it somewhere else. It supports all the basic control flows thus it can understand things like if else and thus can generate things like conditional measurement.

It has been a lot of maintenance burden to keep up with Julia compiler development (which is a good thing for Julia) for me, so YaoCompiler is broken in later Julia versions, I don’t gain much from reusing Julia internals. So I decide not to rely on Julia’s Core.Compiler.

1 Like

This is what the output should work like to save you some time

Of course! An extra-special thank you for maintaining YaoCompiler.

From your answer: Can I infer that clifford_simplification = true in the arguments to compile uses ZXCalculus.jl to do the simplification?

I am a bit foreign to the whole quantum description languages scene, so I’m not sure if a QASM representation is what I need. So I’ll describe my complete use case:

  1. I generate some random quantum circuits programmatically.
  2. I want to use ZX calculus to simplify them. (So far, I used the push_gate! method from my original post to do this; I’m not sure I know how to generalize the macro-based example you gave to receive a list of gates as parameters)
  3. I want to compile the resulting IR, given a specific gate set. In particular, I want to enforce qubit nearest-neighbor connectivity only.
  4. See what the resulting circuit depth is, and compare the depth to the case of no ZX simplification.

If it helps: I have no wish to actually execute any circuit on simulated or real hardware. I only care about the circuit schematic that is produced, assuming it is compliant with the connectivity constraint.