diff --git a/Project.toml b/Project.toml index 79df658..f19b08d 100644 --- a/Project.toml +++ b/Project.toml @@ -8,14 +8,21 @@ Expronicon = "6b7a57c9-7cc1-4fdf-b7f5-e857abae3636" MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078" YaoLocations = "66df03fb-d475-48f7-b449-3d9064bf085b" +[weakdeps] +OpenQASM = "a8821629-a4c0-4df7-9e00-12969ff383a7" + +[extensions] +YaoHIRExt = ["OpenQASM"] + [compat] Expronicon = "0.10" MLStyle = "0.4" YaoLocations = "0.1" -julia = "1.6" +julia = "1.9" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +OpenQASM = "a8821629-a4c0-4df7-9e00-12969ff383a7" [targets] -test = ["Test"] +test = ["Test", "OpenQASM"] diff --git a/ext/YaoHIRExt.jl b/ext/YaoHIRExt.jl new file mode 100644 index 0000000..e28eaee --- /dev/null +++ b/ext/YaoHIRExt.jl @@ -0,0 +1,80 @@ +module YaoHIRExt + +using YaoHIR, YaoLocations +using YaoHIR.IntrinsicOperation +using MLStyle +using OpenQASM +using OpenQASM.Types: Instruction, CXGate, Measure, Reset, MainProgram, Include, RegDecl, ASTNode +using Core.Compiler: IRCode + +function YaoHIR.BlockIR(ast::MainProgram) + convert_to_blockir(ast::MainProgram) +end +function YaoHIR.BlockIR(qasm::String) + convert_to_blockir(OpenQASM.parse(qasm)::MainProgram) +end + +qarg_address(i::Instruction) = Locations(parse(Int, i.qargs[1].address.str) + 1) +qarg_address(i::CXGate) = Locations(parse(Int, i.qarg.address.str) + 1) +#qarg_address(i::CZGate) = Locations(parse(Int, i.qarg.address.str) + 1) +ctrl_address(i::ASTNode) = Locations(parse(Int, i.ctrl.address.str) + 1) + + +push_prog!(circ::Chain, prog::Any) = prog !== nothing && push!(circ.args, prog) + +function convert_to_blockir(ast::MainProgram) + qubits = sum([parse(Int, m.size.str) for m in ast.prog if m isa RegDecl && m.type.str == "qreg"]) + ir = IRCode() + chain = Chain() + [push_prog!(chain, prog_to_gate(prog)) for prog in ast.prog] + BlockIR(ir, qubits, chain) +end + + +function instruction_to_gate(i::Instruction) + @switch i.name begin + @case "z" + Gate(Z, qarg_address(i)) + @case "x" + Gate(X, qarg_address(i)) + @case "h" + Gate(H, qarg_address(i)) + @case "s" + Gate(S, qarg_address(i)) + @case "sdg" + Gate(AdjointOperation(S), qarg_address(i)) + @case "t" + Gate(S, qarg_address(i)) + @case "tdg" + Gate(AdjointOperation(T), qarg_address(i)) + @case "rx" + error("Gate $i not yet implemented") + @case "rx" + error("Gate $i not yet implemented") + @case "shift" + error("Gate $i not yet implemented") + @case "id" + nothing + @case _ + error("Gate $i not supported") + end +end + +function prog_to_gate(a::Any) + @match a begin + i::Include => nothing + r::RegDecl => nothing + inst::Instruction => instruction_to_gate(inst) + cx::CXGate => Ctrl(Gate(X, + Locations(qarg_address(cx))), + CtrlLocations(ctrl_address(cx))) + +# cz::CZGate => Ctrl(Gate(Z, +# Locations(qarg_address(cz))), +# CtrlLocations(ctrl_address(cz))) + m::Measure => nothing + r::Reset => error("only unitaries are supported, please use the function reconstruct_unitaries from the package QuantumCircuitEquivalence.jl") + end +end + +end diff --git a/src/YaoHIR.jl b/src/YaoHIR.jl index 42e4cf0..afd3fcb 100644 --- a/src/YaoHIR.jl +++ b/src/YaoHIR.jl @@ -1,11 +1,11 @@ module YaoHIR export GenericRoutine, Routine, - IntrinsicRoutine, - Operation, - AdjointOperation, - Chain, Gate, Ctrl, - BlockIR + IntrinsicRoutine, + Operation, + AdjointOperation, + Chain, Gate, Ctrl, + BlockIR using MLStyle using Expronicon diff --git a/src/types.jl b/src/types.jl index 1d8d83d..8cb44f7 100644 --- a/src/types.jl +++ b/src/types.jl @@ -114,6 +114,10 @@ struct BlockIR circuit::Chain end +function BlockIR(_::Any) + error("it is required to load OpenQASM for conversion .qasm. Run 'using OpenQASM'") +end + @as_record Chain @as_record Gate @as_record Ctrl diff --git a/test/hir.jl b/test/hir.jl new file mode 100644 index 0000000..4177b7f --- /dev/null +++ b/test/hir.jl @@ -0,0 +1,53 @@ + +@testset "types" begin +@test YaoHIR.routine_name(TestIntrinsic.X) == :X +@test YaoHIR.routine_name(TestIntrinsic.R(1.0)) == :R +@test TestIntrinsic.R(1.0).theta == 1.0 + +display(X) +display(Rx(1.0)) +display(YaoHIR.Operation(X, 2.0)) + +circ = Chain( + Gate(X, Locations(1)), + Core.SSAValue(1), + Ctrl(Gate(Core.SSAValue(1), Locations(3)), CtrlLocations(2)) +) + +print(circ) + +@test YaoHIR.leaves(circ) == [Gate(X, Locations(1)), + Core.SSAValue(1), + Ctrl(Gate(Core.SSAValue(1), Locations(3)), CtrlLocations(2)) +] + +end + + +@testset "test match" begin + gate = Gate(X, Locations(2)) + + @match gate begin + Gate(op, locs) => begin + @test op == X + @test locs == Locations(2) + end + end + + ctrl = Ctrl(Gate(X, Locations(2)), CtrlLocations(3)) + + @match ctrl begin + Ctrl(Gate(op, locs), ctrl) => begin + @test op == X + @test locs == Locations(2) + @test ctrl == CtrlLocations(3) + end + end +end + +@testset "isequal" begin + circuit1 = Chain(Gate(H, Locations((1, ))), Gate(H, Locations((1, )))) + circuit2 = Chain(Gate(H, Locations((1, ))), Gate(H, Locations((1, )))) + @test circuit1 == circuit2 +end + diff --git a/test/instrinsic.jl b/test/instrinsic.jl new file mode 100644 index 0000000..3988896 --- /dev/null +++ b/test/instrinsic.jl @@ -0,0 +1,16 @@ +module TestIntrinsic +using YaoHIR: @intrinsic + +@intrinsic X +@intrinsic R(theta::T) where {T <: Real} +@intrinsic SWAP + +end + +@testset "instrinic" begin + + @test YaoHIR.routine_name(TestIntrinsic.X) == :X + @test YaoHIR.routine_name(TestIntrinsic.R(1.0)) == :R + @test TestIntrinsic.R(1.0).theta == 1.0 + +end diff --git a/test/qasm.jl b/test/qasm.jl new file mode 100644 index 0000000..1a03550 --- /dev/null +++ b/test/qasm.jl @@ -0,0 +1,167 @@ +using YaoHIR +using YaoHIR.IntrinsicOperation +using OpenQASM + +@testset "convert qasm" begin + + bir = BlockIR(""" + OPENQASM 2.0; + include "qelib1.inc"; + qreg q0[3]; + creg c0[2]; + h q0[0]; + h q0[1]; + x q0[2]; + h q0[2]; + CX q0[0], q0[2]; + h q0[0]; + measure q0[0] -> c0[0]; + CX q0[1], q0[2]; + h q0[1]; + measure q0[1] -> c0[1]; + """) + + @testset "correct parsing" begin + bir !== nothing + end + + chain = Chain( + Gate(H, Locations(1)), + Gate(H, Locations(2)), + Gate(X, Locations(3)), + Gate(H, Locations(3)), + Ctrl(Gate(X, Locations(3)), CtrlLocations(1)), + Gate(H, Locations(1)), + Ctrl(Gate(X, Locations(3)), CtrlLocations(2)), + Gate(H, Locations(2)) + ) + + @testset "conversion" begin + @test chain == bir.circuit + end + + +@testset "parse DJ-NAND" begin + bv = BlockIR(""" + OPENQASM 2.0; + include "qelib1.inc"; + qreg q[3]; + creg c[2]; + h q[0]; + h q[1]; + x q[2]; + h q[2]; + x q[2]; + h q[2]; + cx q[1],q[2]; + tdg q[2]; + cx q[0],q[2]; + t q[2]; + cx q[1],q[2]; + t q[1]; + tdg q[2]; + cx q[0],q[2]; + cx q[0],q[1]; + t q[0]; + tdg q[1]; + cx q[0],q[1]; + h q[0]; + h q[1]; + t q[2]; + h q[2]; + measure q[0] -> c[0]; + measure q[1] -> c[1]; + """) + + + bv_re = BlockIR(""" + OPENQASM 2.0; + include "qelib1.inc"; + qreg q[3]; + creg c1[1]; + creg c2[1]; + h q[1]; + t q[1]; + x q[2]; + h q[2]; + x q[2]; + h q[2]; + t q[2]; + CX q[1], q[2]; + tdg q[2]; + CX q[1], q[2]; + h q[1]; + h q[2]; + h q[2]; + t q[2]; + h q[0]; + t q[0]; + CX q[0], q[2]; + tdg q[2]; + CX q[0], q[2]; + CX q[1], q[0]; + tdg q[0]; + tdg q[2]; + CX q[0], q[2]; + t q[2]; + CX q[0], q[2]; + CX q[1], q[0]; + h q[0]; + h q[2]; + measure q[0] -> c2[0]; + measure q[1] -> c1[0];""") + + @testset "correct parsing" begin + @test bv !== nothing + end + + @testset "convert into BlockIR" begin + @test bv_re !== nothing + + end + +end + + @testset "dj 3" begin + dj3 = BlockIR(""" + OPENQASM 2.0; + include "qelib1.inc"; + + qreg q[3]; + creg c[2]; + h q[0]; + h q[1]; + x q[2]; + x q[0]; + x q[1]; + h q[2]; + cx q[0],q[2]; + x q[0]; + cx q[1],q[2]; + h q[0]; + x q[1]; + h q[1]; + """) + + chain = Chain(Gate(H, Locations(1)), + Gate(H, Locations(2)), + Gate(X, Locations(3)), + Gate(X, Locations(1)), + Gate(X, Locations(2)), + Gate(H, Locations(3)), + Ctrl(Gate(X, Locations(3)), + CtrlLocations(1)), + Gate(X, Locations(1)), + Ctrl(Gate(X, Locations(3)), + CtrlLocations(2)), + Gate(H, Locations(1)), + Gate(X, Locations(2)), + Gate(H, Locations(2))) + +@test dj3.circuit == chain +# FIXME should an empty IR print '1 ─ return nothing' ? +# print(dj3) + +end + +end diff --git a/test/runtests.jl b/test/runtests.jl index a1c7e2b..f37a452 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,60 +4,7 @@ using YaoHIR using YaoHIR.IntrinsicOperation using Test -module TestIntrinsic -using YaoHIR: @intrinsic -@intrinsic X -@intrinsic R(theta::T) where {T <: Real} -@intrinsic SWAP - -end - -@test YaoHIR.routine_name(TestIntrinsic.X) == :X -@test YaoHIR.routine_name(TestIntrinsic.R(1.0)) == :R -@test TestIntrinsic.R(1.0).theta == 1.0 - -display(X) -display(Rx(1.0)) -display(YaoHIR.Operation(X, 2.0)) - -circ = Chain( - Gate(X, Locations(1)), - Core.SSAValue(1), - Ctrl(Gate(Core.SSAValue(1), Locations(3)), CtrlLocations(2)) -) - -print(circ) - -@test YaoHIR.leaves(circ) == [Gate(X, Locations(1)), - Core.SSAValue(1), - Ctrl(Gate(Core.SSAValue(1), Locations(3)), CtrlLocations(2)) -] - - -@testset "test match" begin - gate = Gate(X, Locations(2)) - - @match gate begin - Gate(op, locs) => begin - @test op == X - @test locs == Locations(2) - end - end - - ctrl = Ctrl(Gate(X, Locations(2)), CtrlLocations(3)) - - @match ctrl begin - Ctrl(Gate(op, locs), ctrl) => begin - @test op == X - @test locs == Locations(2) - @test ctrl == CtrlLocations(3) - end - end -end - -@testset "isequal" begin - circuit1 = Chain(Gate(H, Locations((1, ))), Gate(H, Locations((1, )))) - circuit2 = Chain(Gate(H, Locations((1, ))), Gate(H, Locations((1, )))) - @test circuit1 == circuit2 -end +include("instrinsic.jl") +include("hir.jl") +include("qasm.jl")