diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 79c390b9..e3b49fff 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -35,7 +35,7 @@ jobs: - name: "Set up Julia" uses: julia-actions/setup-julia@v1 with: - version: 1.8 + version: 1.9 arch: ${{ matrix.arch }} - uses: julia-actions/julia-buildpkg@latest - run: | diff --git a/PyBraket/Project.toml b/PyBraket/Project.toml index d381b70d..39e9bb01 100644 --- a/PyBraket/Project.toml +++ b/PyBraket/Project.toml @@ -17,7 +17,7 @@ Aqua = "=0.6" Braket = "=0.7.5" CondaPkg = "=0.2.18" DataStructures = "=0.18.14" -PythonCall = "=0.9.12" +PythonCall = "=0.9.14" StructTypes = "=1.10.0" julia = "1.6" diff --git a/PyBraket/src/PyBraket.jl b/PyBraket/src/PyBraket.jl index 47c6dba6..301e2895 100644 --- a/PyBraket/src/PyBraket.jl +++ b/PyBraket/src/PyBraket.jl @@ -52,6 +52,7 @@ module PyBraket args = map(fns) do fn val = getproperty(x, fn) isnothing(val) && return fn=>pybuiltins.None + typeof(val) <: NTuple{1} && return fn=>Py(val[1]) typeof(val) <: Vector{<:Number} && return fn=>pylist(Py(val)) typeof(val) == Vector{String} && return fn=>pylist(pystr.(val)) typeof(val) == Vector{Braket.IR.PhysicalField} && return fn=>pylist(Py.(val)) diff --git a/PyBraket/src/pygates.jl b/PyBraket/src/pygates.jl index ff777e63..c220b2d3 100644 --- a/PyBraket/src/pygates.jl +++ b/PyBraket/src/pygates.jl @@ -1,340 +1,84 @@ -function Py(x::IR.Rx) - fns = fieldnames(IR.Rx) - args = arg_gen(x, fns) - return pyjaqcd.Rx(; args...) -end -function Py(x::Rx) - fns = fieldnames(Rx) - args = arg_gen(x, fns) - return pygates.Rx(; args...) -end -function Py(x::IR.Ry) - fns = fieldnames(IR.Ry) - args = arg_gen(x, fns) - return pyjaqcd.Ry(; args...) -end -function Py(x::Ry) - fns = fieldnames(Ry) - args = arg_gen(x, fns) - return pygates.Ry(; args...) -end -function Py(x::IR.Rz) - fns = fieldnames(IR.Rz) - args = arg_gen(x, fns) - return pyjaqcd.Rz(; args...) -end -function Py(x::Rz) - fns = fieldnames(Rz) - args = arg_gen(x, fns) - return pygates.Rz(; args...) -end -function Py(x::IR.PhaseShift) - fns = fieldnames(IR.PhaseShift) - args = arg_gen(x, fns) - return pyjaqcd.PhaseShift(; args...) -end -function Py(x::PhaseShift) - fns = fieldnames(PhaseShift) - args = arg_gen(x, fns) - return pygates.PhaseShift(; args...) -end -function Py(x::IR.PSwap) - fns = fieldnames(IR.PSwap) - args = arg_gen(x, fns) - return pyjaqcd.PSwap(; args...) -end -function Py(x::PSwap) - fns = fieldnames(PSwap) - args = arg_gen(x, fns) - return pygates.PSwap(; args...) -end -function Py(x::IR.XY) - fns = fieldnames(IR.XY) - args = arg_gen(x, fns) - return pyjaqcd.XY(; args...) -end -function Py(x::XY) - fns = fieldnames(XY) - args = arg_gen(x, fns) - return pygates.XY(; args...) -end -function Py(x::IR.CPhaseShift) - fns = fieldnames(IR.CPhaseShift) - args = arg_gen(x, fns) - return pyjaqcd.CPhaseShift(; args...) -end -function Py(x::CPhaseShift) - fns = fieldnames(CPhaseShift) - args = arg_gen(x, fns) - return pygates.CPhaseShift(; args...) -end -function Py(x::IR.CPhaseShift00) - fns = fieldnames(IR.CPhaseShift00) - args = arg_gen(x, fns) - return pyjaqcd.CPhaseShift00(; args...) -end -function Py(x::CPhaseShift00) - fns = fieldnames(CPhaseShift00) - args = arg_gen(x, fns) - return pygates.CPhaseShift00(; args...) -end -function Py(x::IR.CPhaseShift01) - fns = fieldnames(IR.CPhaseShift01) - args = arg_gen(x, fns) - return pyjaqcd.CPhaseShift01(; args...) -end -function Py(x::CPhaseShift01) - fns = fieldnames(CPhaseShift01) - args = arg_gen(x, fns) - return pygates.CPhaseShift01(; args...) -end -function Py(x::IR.CPhaseShift10) - fns = fieldnames(IR.CPhaseShift10) - args = arg_gen(x, fns) - return pyjaqcd.CPhaseShift10(; args...) -end -function Py(x::CPhaseShift10) - fns = fieldnames(CPhaseShift10) - args = arg_gen(x, fns) - return pygates.CPhaseShift10(; args...) -end -function Py(x::IR.XX) - fns = fieldnames(IR.XX) - args = arg_gen(x, fns) - return pyjaqcd.XX(; args...) -end -function Py(x::XX) - fns = fieldnames(XX) - args = arg_gen(x, fns) - return pygates.XX(; args...) -end -function Py(x::IR.YY) - fns = fieldnames(IR.YY) - args = arg_gen(x, fns) - return pyjaqcd.YY(; args...) -end -function Py(x::YY) - fns = fieldnames(YY) - args = arg_gen(x, fns) - return pygates.YY(; args...) -end -function Py(x::IR.ZZ) - fns = fieldnames(IR.ZZ) - args = arg_gen(x, fns) - return pyjaqcd.ZZ(; args...) -end -function Py(x::ZZ) - fns = fieldnames(ZZ) - args = arg_gen(x, fns) - return pygates.ZZ(; args...) -end -function Py(x::IR.H) - fns = fieldnames(IR.H) - args = arg_gen(x, fns) - return pyjaqcd.H(; args...) -end -function Py(x::H) - fns = fieldnames(H) - args = arg_gen(x, fns) - return pygates.H(; args...) -end -function Py(x::IR.I) - fns = fieldnames(IR.I) - args = arg_gen(x, fns) - return pyjaqcd.I(; args...) -end -function Py(x::I) - fns = fieldnames(I) - args = arg_gen(x, fns) - return pygates.I(; args...) -end -function Py(x::IR.X) - fns = fieldnames(IR.X) - args = arg_gen(x, fns) - return pyjaqcd.X(; args...) -end -function Py(x::X) - fns = fieldnames(X) - args = arg_gen(x, fns) - return pygates.X(; args...) -end -function Py(x::IR.Y) - fns = fieldnames(IR.Y) - args = arg_gen(x, fns) - return pyjaqcd.Y(; args...) -end -function Py(x::Y) - fns = fieldnames(Y) - args = arg_gen(x, fns) - return pygates.Y(; args...) -end -function Py(x::IR.Z) - fns = fieldnames(IR.Z) - args = arg_gen(x, fns) - return pyjaqcd.Z(; args...) -end -function Py(x::Z) - fns = fieldnames(Z) - args = arg_gen(x, fns) - return pygates.Z(; args...) -end -function Py(x::IR.S) - fns = fieldnames(IR.S) - args = arg_gen(x, fns) - return pyjaqcd.S(; args...) -end -function Py(x::S) - fns = fieldnames(S) - args = arg_gen(x, fns) - return pygates.S(; args...) -end -function Py(x::IR.Si) - fns = fieldnames(IR.Si) - args = arg_gen(x, fns) - return pyjaqcd.Si(; args...) -end -function Py(x::Si) - fns = fieldnames(Si) - args = arg_gen(x, fns) - return pygates.Si(; args...) -end -function Py(x::IR.T) - fns = fieldnames(IR.T) - args = arg_gen(x, fns) - return pyjaqcd.T(; args...) -end -function Py(x::T) - fns = fieldnames(T) - args = arg_gen(x, fns) - return pygates.T(; args...) -end -function Py(x::IR.Ti) - fns = fieldnames(IR.Ti) - args = arg_gen(x, fns) - return pyjaqcd.Ti(; args...) -end -function Py(x::Ti) - fns = fieldnames(Ti) - args = arg_gen(x, fns) - return pygates.Ti(; args...) -end -function Py(x::IR.V) - fns = fieldnames(IR.V) - args = arg_gen(x, fns) - return pyjaqcd.V(; args...) -end -function Py(x::V) - fns = fieldnames(V) - args = arg_gen(x, fns) - return pygates.V(; args...) -end -function Py(x::IR.Vi) - fns = fieldnames(IR.Vi) - args = arg_gen(x, fns) - return pyjaqcd.Vi(; args...) -end -function Py(x::Vi) - fns = fieldnames(Vi) - args = arg_gen(x, fns) - return pygates.Vi(; args...) -end -function Py(x::IR.CNot) - fns = fieldnames(IR.CNot) - args = arg_gen(x, fns) - return pyjaqcd.CNot(; args...) -end -function Py(x::CNot) - fns = fieldnames(CNot) - args = arg_gen(x, fns) - return pygates.CNot(; args...) -end -function Py(x::IR.Swap) - fns = fieldnames(IR.Swap) - args = arg_gen(x, fns) - return pyjaqcd.Swap(; args...) -end -function Py(x::Swap) - fns = fieldnames(Swap) - args = arg_gen(x, fns) - return pygates.Swap(; args...) -end -function Py(x::IR.ISwap) - fns = fieldnames(IR.ISwap) - args = arg_gen(x, fns) - return pyjaqcd.ISwap(; args...) -end -function Py(x::ISwap) - fns = fieldnames(ISwap) - args = arg_gen(x, fns) - return pygates.ISwap(; args...) -end -function Py(x::IR.CV) - fns = fieldnames(IR.CV) - args = arg_gen(x, fns) - return pyjaqcd.CV(; args...) -end -function Py(x::CV) - fns = fieldnames(CV) - args = arg_gen(x, fns) - return pygates.CV(; args...) -end -function Py(x::IR.CY) - fns = fieldnames(IR.CY) - args = arg_gen(x, fns) - return pyjaqcd.CY(; args...) -end -function Py(x::CY) - fns = fieldnames(CY) - args = arg_gen(x, fns) - return pygates.CY(; args...) -end -function Py(x::IR.CZ) - fns = fieldnames(IR.CZ) - args = arg_gen(x, fns) - return pyjaqcd.CZ(; args...) -end -function Py(x::CZ) - fns = fieldnames(CZ) - args = arg_gen(x, fns) - return pygates.CZ(; args...) -end -function Py(x::IR.ECR) - fns = fieldnames(IR.ECR) - args = arg_gen(x, fns) - return pyjaqcd.ECR(; args...) -end -function Py(x::ECR) - fns = fieldnames(ECR) - args = arg_gen(x, fns) - return pygates.ECR(; args...) -end -function Py(x::IR.CCNot) - fns = fieldnames(IR.CCNot) - args = arg_gen(x, fns) - return pyjaqcd.CCNot(; args...) -end -function Py(x::CCNot) - fns = fieldnames(CCNot) - args = arg_gen(x, fns) - return pygates.CCNot(; args...) -end -function Py(x::IR.CSwap) - fns = fieldnames(IR.CSwap) - args = arg_gen(x, fns) - return pyjaqcd.CSwap(; args...) -end -function Py(x::CSwap) - fns = fieldnames(CSwap) - args = arg_gen(x, fns) - return pygates.CSwap(; args...) -end -function Py(x::IR.Unitary) - fns = fieldnames(IR.Unitary) - args = arg_gen(x, fns) - return pyjaqcd.Unitary(; args...) -end -function Py(x::Unitary) - fns = fieldnames(Unitary) - args = arg_gen(x, fns) - return pygates.Unitary(; args...) +Py(x::IR.H) = pyjaqcd.H(; arg_gen(x, fieldnames(IR.H))...) +Py(x::H) = pygates.H(; arg_gen(x, fieldnames(H))...) +Py(x::IR.I) = pyjaqcd.I(; arg_gen(x, fieldnames(IR.I))...) +Py(x::I) = pygates.I(; arg_gen(x, fieldnames(I))...) +Py(x::IR.X) = pyjaqcd.X(; arg_gen(x, fieldnames(IR.X))...) +Py(x::X) = pygates.X(; arg_gen(x, fieldnames(X))...) +Py(x::IR.Y) = pyjaqcd.Y(; arg_gen(x, fieldnames(IR.Y))...) +Py(x::Y) = pygates.Y(; arg_gen(x, fieldnames(Y))...) +Py(x::IR.Z) = pyjaqcd.Z(; arg_gen(x, fieldnames(IR.Z))...) +Py(x::Z) = pygates.Z(; arg_gen(x, fieldnames(Z))...) +Py(x::IR.S) = pyjaqcd.S(; arg_gen(x, fieldnames(IR.S))...) +Py(x::S) = pygates.S(; arg_gen(x, fieldnames(S))...) +Py(x::IR.Si) = pyjaqcd.Si(; arg_gen(x, fieldnames(IR.Si))...) +Py(x::Si) = pygates.Si(; arg_gen(x, fieldnames(Si))...) +Py(x::IR.T) = pyjaqcd.T(; arg_gen(x, fieldnames(IR.T))...) +Py(x::T) = pygates.T(; arg_gen(x, fieldnames(T))...) +Py(x::IR.Ti) = pyjaqcd.Ti(; arg_gen(x, fieldnames(IR.Ti))...) +Py(x::Ti) = pygates.Ti(; arg_gen(x, fieldnames(Ti))...) +Py(x::IR.V) = pyjaqcd.V(; arg_gen(x, fieldnames(IR.V))...) +Py(x::V) = pygates.V(; arg_gen(x, fieldnames(V))...) +Py(x::IR.Vi) = pyjaqcd.Vi(; arg_gen(x, fieldnames(IR.Vi))...) +Py(x::Vi) = pygates.Vi(; arg_gen(x, fieldnames(Vi))...) +Py(x::IR.CNot) = pyjaqcd.CNot(; arg_gen(x, fieldnames(IR.CNot))...) +Py(x::CNot) = pygates.CNot(; arg_gen(x, fieldnames(CNot))...) +Py(x::IR.Swap) = pyjaqcd.Swap(; arg_gen(x, fieldnames(IR.Swap))...) +Py(x::Swap) = pygates.Swap(; arg_gen(x, fieldnames(Swap))...) +Py(x::IR.ISwap) = pyjaqcd.ISwap(; arg_gen(x, fieldnames(IR.ISwap))...) +Py(x::ISwap) = pygates.ISwap(; arg_gen(x, fieldnames(ISwap))...) +Py(x::IR.CV) = pyjaqcd.CV(; arg_gen(x, fieldnames(IR.CV))...) +Py(x::CV) = pygates.CV(; arg_gen(x, fieldnames(CV))...) +Py(x::IR.CY) = pyjaqcd.CY(; arg_gen(x, fieldnames(IR.CY))...) +Py(x::CY) = pygates.CY(; arg_gen(x, fieldnames(CY))...) +Py(x::IR.CZ) = pyjaqcd.CZ(; arg_gen(x, fieldnames(IR.CZ))...) +Py(x::CZ) = pygates.CZ(; arg_gen(x, fieldnames(CZ))...) +Py(x::IR.ECR) = pyjaqcd.ECR(; arg_gen(x, fieldnames(IR.ECR))...) +Py(x::ECR) = pygates.ECR(; arg_gen(x, fieldnames(ECR))...) +Py(x::IR.CCNot) = pyjaqcd.CCNot(; arg_gen(x, fieldnames(IR.CCNot))...) +Py(x::CCNot) = pygates.CCNot(; arg_gen(x, fieldnames(CCNot))...) +Py(x::IR.CSwap) = pyjaqcd.CSwap(; arg_gen(x, fieldnames(IR.CSwap))...) +Py(x::CSwap) = pygates.CSwap(; arg_gen(x, fieldnames(CSwap))...) +Py(x::IR.Unitary) = pyjaqcd.Unitary(; arg_gen(x, fieldnames(IR.Unitary))...) +Py(x::Unitary) = pygates.Unitary(; arg_gen(x, fieldnames(Unitary))...) +Py(x::IR.Rx) = pyjaqcd.Rx(; arg_gen(x, fieldnames(IR.Rx))...) +Py(x::Rx) = pygates.Rx(; arg_gen(x, fieldnames(Rx))...) +Py(x::IR.Ry) = pyjaqcd.Ry(; arg_gen(x, fieldnames(IR.Ry))...) +Py(x::Ry) = pygates.Ry(; arg_gen(x, fieldnames(Ry))...) +Py(x::IR.Rz) = pyjaqcd.Rz(; arg_gen(x, fieldnames(IR.Rz))...) +Py(x::Rz) = pygates.Rz(; arg_gen(x, fieldnames(Rz))...) +Py(x::IR.PhaseShift) = pyjaqcd.PhaseShift(; arg_gen(x, fieldnames(IR.PhaseShift))...) +Py(x::PhaseShift) = pygates.PhaseShift(; arg_gen(x, fieldnames(PhaseShift))...) +Py(x::IR.PSwap) = pyjaqcd.PSwap(; arg_gen(x, fieldnames(IR.PSwap))...) +Py(x::PSwap) = pygates.PSwap(; arg_gen(x, fieldnames(PSwap))...) +Py(x::IR.XY) = pyjaqcd.XY(; arg_gen(x, fieldnames(IR.XY))...) +Py(x::XY) = pygates.XY(; arg_gen(x, fieldnames(XY))...) +Py(x::IR.CPhaseShift) = pyjaqcd.CPhaseShift(; arg_gen(x, fieldnames(IR.CPhaseShift))...) +Py(x::CPhaseShift) = pygates.CPhaseShift(; arg_gen(x, fieldnames(CPhaseShift))...) +Py(x::IR.CPhaseShift00) = pyjaqcd.CPhaseShift00(; arg_gen(x, fieldnames(IR.CPhaseShift00))...) +Py(x::CPhaseShift00) = pygates.CPhaseShift00(; arg_gen(x, fieldnames(CPhaseShift00))...) +Py(x::IR.CPhaseShift01) = pyjaqcd.CPhaseShift01(; arg_gen(x, fieldnames(IR.CPhaseShift01))...) +Py(x::CPhaseShift01) = pygates.CPhaseShift01(; arg_gen(x, fieldnames(CPhaseShift01))...) +Py(x::IR.CPhaseShift10) = pyjaqcd.CPhaseShift10(; arg_gen(x, fieldnames(IR.CPhaseShift10))...) +Py(x::CPhaseShift10) = pygates.CPhaseShift10(; arg_gen(x, fieldnames(CPhaseShift10))...) +Py(x::IR.XX) = pyjaqcd.XX(; arg_gen(x, fieldnames(IR.XX))...) +Py(x::XX) = pygates.XX(; arg_gen(x, fieldnames(XX))...) +Py(x::IR.YY) = pyjaqcd.YY(; arg_gen(x, fieldnames(IR.YY))...) +Py(x::YY) = pygates.YY(; arg_gen(x, fieldnames(YY))...) +Py(x::IR.ZZ) = pyjaqcd.ZZ(; arg_gen(x, fieldnames(IR.ZZ))...) +Py(x::ZZ) = pygates.ZZ(; arg_gen(x, fieldnames(ZZ))...) +Py(x::IR.GPi) = pyjaqcd.GPi(; arg_gen(x, fieldnames(IR.GPi))...) +Py(x::GPi) = pygates.GPi(; arg_gen(x, fieldnames(GPi))...) +Py(x::IR.GPi2) = pyjaqcd.GPi2(; arg_gen(x, fieldnames(IR.GPi2))...) +Py(x::GPi2) = pygates.GPi2(; arg_gen(x, fieldnames(GPi2))...) +function Py(x::IR.MS) + fns = fieldnames(IR.MS) + args = Dict(arg_gen(x, fns)...) + mapped_args = Dict(zip([:angle_1, :angle_2, :angle_3], args[:angle])) + return pyjaqcd.MS(; mapped_args...) +end +function Py(x::MS) + fns = fieldnames(MS) + args = Dict(arg_gen(x, fns)...) + mapped_args = Dict(zip([:angle_1, :angle_2, :angle_3], args[:angle])) + return pygates.MS(; mapped_args...) end diff --git a/PyBraket/test/gates.jl b/PyBraket/test/gates.jl index 76bb6667..392e5644 100644 --- a/PyBraket/test/gates.jl +++ b/PyBraket/test/gates.jl @@ -1,10 +1,12 @@ using Test, PyBraket, Braket, Braket.IR, PythonCall using PythonCall: Py, pyconvert, pyisTrue +using Braket: I + @testset "Gates" begin Braket.IRType[] = :JAQCD @testset for (gate, ir_gate) in ((H, IR.H), - (Braket.I, IR.I), + (I, IR.I), (X, IR.X), (Y, IR.Y), (Z, IR.Z), @@ -15,7 +17,7 @@ using PythonCall: Py, pyconvert, pyisTrue (V, IR.V), (Vi, IR.Vi) ) - g = gate() + g = gate() ir_g = Braket.ir(g, 0) py_g = Py(g) @test pyisTrue(py_g.to_ir([0]) == Py(ir_g)) @@ -27,9 +29,9 @@ using PythonCall: Py, pyconvert, pyisTrue (Rz, IR.Rz), ) angle = rand() - g = gate(angle) - ir_g = Braket.ir(g, 0) - py_g = Py(g) + g = gate(angle) + ir_g = Braket.ir(g, 0) + py_g = Py(g) @test pyisTrue(py_g.to_ir([0]) == Py(ir_g)) @test pyconvert(ir_gate, Py(ir_g)) == ir_g end @@ -41,7 +43,7 @@ using PythonCall: Py, pyconvert, pyisTrue (CV, IR.CV), (ECR, IR.ECR), ) - g = gate() + g = gate() ir_g = Braket.ir(g, [0, 1]) py_g = Py(g) @test pyisTrue(py_g.to_ir([0, 1]) == Py(ir_g)) @@ -57,22 +59,22 @@ using PythonCall: Py, pyconvert, pyisTrue (PSwap, IR.PSwap) ) angle = rand() - g = gate(angle) - ir_g = Braket.ir(g, [0, 1]) - py_g = Py(g) + g = gate(angle) + ir_g = Braket.ir(g, [0, 1]) + py_g = Py(g) @test pyisTrue(py_g.to_ir([0, 1]) == Py(ir_g)) @test pyconvert(ir_gate, Py(ir_g)) == ir_g end @testset for (gate, ir_gate) in ((CCNot, IR.CCNot), (CSwap, IR.CSwap)) - g = gate() + g = gate() ir_g = Braket.ir(g, [0, 1, 2]) py_g = Py(g) @test pyisTrue(py_g.to_ir([0, 1, 2]) == Py(ir_g)) @test pyconvert(ir_gate, Py(ir_g)) == ir_g end @testset "(gate, ir_gate) = (Unitary, IR.Unitary)" begin - mat = complex([0. 1.; 1. 0.]) - n = Unitary(mat) + mat = complex([0. 1.; 1. 0.]) + n = Unitary(mat) ir_n = Braket.ir(n, 0) py_n = Py(n) @test pyisTrue(py_n.to_ir([0]) == Py(ir_n)) diff --git a/docs/src/gates.md b/docs/src/gates.md index ecfa4e3e..9ad46e11 100644 --- a/docs/src/gates.md +++ b/docs/src/gates.md @@ -1,5 +1,28 @@ # Gates +The gates are used to represent various gate operations on qubits. +Gates should implement the following functions: + +```julia +label +qubit_count +n_angles +angles +chars +ir_typ +ir_str +targets_and_controls +``` + +- `label` is used to generate the gate's representation in OpenQASM3. +- `chars` is used when pretty-printing the gate as part of a `Circuit`. +- `n_angles` is the number of angles present in the gate and `angles` is an accessor method for those angles -- gates without angles should return an empty tuple `()`. +- `ir_typ` is a mapping to the gate's sibling in the `IR` submodule. +- `ir_str` is the gate's full OpenQASM3 representation. +- `targets_and_controls` should accept the gate and a `QubitSet` of qubits to apply it to, and generate from this the list of control qubits and target qubits. + +New gates with angle parameters should be subtypes of `AngledGate{N}`, where `N` is the number of angle parameters. They should have one member, `angle::NTuple{N, Union{Float64, FreeParameter}}`. + ```@docs Gate AngledGate diff --git a/src/circuit.jl b/src/circuit.jl index 9f80feb6..61042703 100644 --- a/src/circuit.jl +++ b/src/circuit.jl @@ -385,7 +385,7 @@ basis_rotation_gates(o::Observables.I) = () basis_rotation_gates(o::Observables.Z) = () basis_rotation_gates(o::Observables.Y) = (Z(), S(), H()) basis_rotation_gates(o::Observables.TensorProduct) = tuple(reduce(vcat, basis_rotation_gates.(o.factors))...) -basis_rotation_gates(o::Observables.HermitianObservable) = (Unitary(adjoint(eigvecs(o.matrix))),) +basis_rotation_gates(o::Observables.HermitianObservable) = (Unitary(Matrix(adjoint(eigvecs(o.matrix)))),) _observable_to_instruction(observable::Observables.Observable, target_list)::Vector{Instruction} = [Instruction(gate, target_list) for gate in basis_rotation_gates(observable)] diff --git a/src/gate_applicators.jl b/src/gate_applicators.jl index 70e0c1c3..d14af113 100644 --- a/src/gate_applicators.jl +++ b/src/gate_applicators.jl @@ -1,63 +1,28 @@ -for (G, IRG) in zip((:H,:I,:X,:Y,:Z,:S,:Si,:T,:Ti,:V,:Vi,:CNot,:Swap,:ISwap,:CV,:CY,:CZ,:ECR,:CCNot,:CSwap,:Unitary,:Rx,:Ry,:Rz,:PhaseShift,:PSwap,:XY,:CPhaseShift,:CPhaseShift00,:CPhaseShift01,:CPhaseShift10,:XX,:YY,:ZZ,:GPi,:GPi2,:MS), (:(IR.H), :(IR.I), :(IR.X), :(IR.Y), :(IR.Z), :(IR.S), :(IR.Si), :(IR.T), :(IR.Ti), :(IR.V), :(IR.Vi), :(IR.CNot), :(IR.Swap), :(IR.ISwap), :(IR.CV), :(IR.CY), :(IR.CZ), :(IR.ECR), :(IR.CCNot), :(IR.CSwap), :(IR.Unitary), :(IR.Rx), :(IR.Ry), :(IR.Rz), :(IR.PhaseShift), :(IR.PSwap), :(IR.XY), :(IR.CPhaseShift), :(IR.CPhaseShift00), :(IR.CPhaseShift01), :(IR.CPhaseShift10), :(IR.XX), :(IR.YY), :(IR.ZZ), :(IR.GPi), :(IR.GPi2), :(IR.MS))) - @eval begin - $G(c::Circuit, args...) = apply_gate!(IR.Angle($IRG), IR.Control($IRG), IR.Target($IRG), $G, c, args...) - apply_gate!(::Type{$G}, c::Circuit, args...) = apply_gate!(IR.Angle($IRG), IR.Control($IRG), IR.Target($IRG), $G, c, args...) - end -end -apply_gate!(::IR.NonAngled, ::IR.NoControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, arg::IntOrQubit) where {G<:Gate} = add_instruction!(c, Instruction(G(), arg)) -apply_gate!(::IR.Angled, ::IR.NoControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, arg::IntOrQubit, angle) where {G<:Gate} = add_instruction!(c, Instruction(G(angle), arg)) -#apply_gate!(::IR.DoubleAngled, ::IR.NoControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, arg::IntOrQubit, angle1, angle2) where {G<:Gate} = add_instruction!(c, Instruction(G(angle1, angle2), arg)) -apply_gate!(::IR.TripleAngled, ::IR.NoControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, arg::IntOrQubit, angle1, angle2, angle3) where {G<:Gate} = add_instruction!(c, Instruction(G(angle1, angle2, angle3), arg)) - -apply_gate!(::IR.NonAngled, ::IR.NoControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, arg::VecOrQubitSet) where {G<:Gate} = (foreach(i->add_instruction!(c, Instruction(G(), i)), arg); return c) -apply_gate!(::IR.Angled, ::IR.NoControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, v::VecOrQubitSet, angle) where {G<:Gate} = (foreach(i->add_instruction!(c, Instruction(G(angle), i)), v); return c) -#apply_gate!(::IR.DoubleAngled, ::IR.NoControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, v::VecOrQubitSet, angle1, angle2) where {G<:Gate} = (foreach(i->add_instruction!(c, Instruction(G(angle1, angle2), i)), v); return c) -apply_gate!(::IR.TripleAngled, ::IR.NoControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, v::VecOrQubitSet, angle1, angle2, angle3) where {G<:Gate} = (foreach(i->add_instruction!(c, Instruction(G(angle1, angle2, angle3), i)), v); return c) - -apply_gate!(::IR.NonAngled, ::IR.NoControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args...) where {G<:Gate} = (foreach(i->add_instruction!(c, Instruction(G(), i)), args); return c) -apply_gate!(::IR.Angled, ::IR.NoControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args...) where {G<:Gate} = (foreach(i->add_instruction!(c, Instruction(G(args[end]), i)), args[1:end-1]); return c) -#apply_gate!(::IR.DoubleAngled, ::IR.NoControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args...) where {G<:Gate} = (foreach(i->add_instruction!(c, Instruction(G(args[end-1], args[end]), i)), args[1:end-2]); return c) -apply_gate!(::IR.TripleAngled, ::IR.NoControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args...) where {G<:Gate} = (foreach(i->add_instruction!(c, Instruction(G(args[end-2], args[end-1], args[end]), i)), args[1:end-2]); return c) - -apply_gate!(::IR.NonAngled, ::IR.NoControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, args::VecOrQubitSet) where {G<:Gate} = add_instruction!(c, Instruction(G(), args[1:2])) -apply_gate!(::IR.Angled, ::IR.NoControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, args::VecOrQubitSet, angle) where {G<:Gate} = add_instruction!(c, Instruction(G(angle), args[1:2])) -#apply_gate!(::IR.DoubleAngled, ::IR.NoControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, args::VecOrQubitSet, angle1, angle2) where {G<:Gate} = add_instruction!(c, Instruction(G(angle1, angle2), args[1:2])) -apply_gate!(::IR.TripleAngled, ::IR.NoControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, args::VecOrQubitSet, angle1, angle2, angle3) where {G<:Gate} = add_instruction!(c, Instruction(G(angle1, angle2, angle3), args[1:3])) - -apply_gate!(::IR.NonAngled, ::IR.NoControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, args...) where {G<:Gate} = add_instruction!(c, Instruction(G(), [args[1:2]...])) -apply_gate!(::IR.Angled, ::IR.NoControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, args...) where {G<:Gate} = add_instruction!(c, Instruction(G(args[end]), [args[1:2]...])) -#apply_gate!(::IR.DoubleAngled, ::IR.NoControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, args...) where {G<:Gate} = add_instruction!(c, Instruction(G(args[end-1], args[end]), [args[1:2]...])) -apply_gate!(::IR.TripleAngled, ::IR.NoControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, args...) where {G<:Gate} = add_instruction!(c, Instruction(G(args[end-2], args[end-1], args[end]), [args[1:2]...])) - -apply_gate!(::IR.NonAngled, ::IR.SingleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args::VecOrQubitSet) where {G<:Gate} = add_instruction!(c, Instruction(G(), args[1:2])) -apply_gate!(::IR.Angled, ::IR.SingleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args::VecOrQubitSet, angle) where {G<:Gate} = add_instruction!(c, Instruction(G(angle), args[1:2])) -#apply_gate!(::IR.DoubleAngled, ::IR.SingleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args::VecOrQubitSet, angle1, angle2) where {G<:Gate} = add_instruction!(c, Instruction(G(angle1, angle2), args[1:2])) -apply_gate!(::IR.TripleAngled, ::IR.SingleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args::VecOrQubitSet, angle1, angle2, angle3) where {G<:Gate} = add_instruction!(c, Instruction(G(angle1, angle2, angle3), args[1:2])) - -apply_gate!(::IR.NonAngled, ::IR.SingleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args...) where {G<:Gate} = add_instruction!(c, Instruction(G(), [args[1:2]...])) -apply_gate!(::IR.Angled, ::IR.SingleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args...) where {G<:Gate} = add_instruction!(c, Instruction(G(args[end]), [args[1:2]...])) -#apply_gate!(::IR.DoubleAngled, ::IR.SingleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args...) where {G<:Gate} = add_instruction!(c, Instruction(G(args[end-1], args[end]), [args[1:2]...])) -apply_gate!(::IR.TripleAngled, ::IR.SingleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args...) where {G<:Gate} = add_instruction!(c, Instruction(G(args[end-2], args[end-1], args[end]), [args[1:2]...])) - -apply_gate!(::IR.NonAngled, ::IR.SingleControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, args::VecOrQubitSet) where {G<:Gate} = add_instruction!(c, Instruction(G(), args[1:3])) -apply_gate!(::IR.Angled, ::IR.SingleControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, args::VecOrQubitSet, angle) where {G<:Gate} = add_instruction!(c, Instruction(G(angle), args[1:3])) -#apply_gate!(::IR.DoubleAngled, ::IR.SingleControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, args::VecOrQubitSet, angle1, angle2) where {G<:Gate} = add_instruction!(c, Instruction(G(angle1, angle2), args[1:3])) -apply_gate!(::IR.TripleAngled, ::IR.SingleControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, args::VecOrQubitSet, angle1, angle2, angle3) where {G<:Gate} = add_instruction!(c, Instruction(G(angle1, angle2, angle3), args[1:3])) - -apply_gate!(::IR.NonAngled, ::IR.SingleControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, args...) where {G<:Gate} = add_instruction!(c, Instruction(G(), [args[1:3]...])) -apply_gate!(::IR.Angled, ::IR.SingleControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, args...) where {G<:Gate} = add_instruction!(c, Instruction(G(args[end]), [args[1:3]...])) -#apply_gate!(::IR.DoubleAngled, ::IR.SingleControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, args...) where {G<:Gate} = add_instruction!(c, Instruction(G(args[end-1], args[end]), [args[1:3]...])) -apply_gate!(::IR.TripleAngled, ::IR.SingleControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, args...) where {G<:Gate} = add_instruction!(c, Instruction(G(args[end-2], args[end-1], args[end]), [args[1:3]...])) - -apply_gate!(::IR.NonAngled, ::IR.DoubleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args::VecOrQubitSet) where {G<:Gate} = add_instruction!(c, Instruction(G(), args[1:3])) -apply_gate!(::IR.Angled, ::IR.DoubleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args::VecOrQubitSet, angle) where {G<:Gate} = add_instruction!(c, Instruction(G(angle), args[1:3])) -#apply_gate!(::IR.DoubleAngled, ::IR.DoubleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args::VecOrQubitSet, angle1, angle2) where {G<:Gate} = add_instruction!(c, Instruction(G(angle1, angle2), args[1:3])) -apply_gate!(::IR.TripleAngled, ::IR.DoubleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args::VecOrQubitSet, angle1, angle2, angle3) where {G<:Gate} = add_instruction!(c, Instruction(G(angle1, angle2, angle3), args[1:3])) - -apply_gate!(::IR.NonAngled, ::IR.DoubleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args...) where {G<:Gate} = add_instruction!(c, Instruction(G(), [args[1:3]...])) -apply_gate!(::IR.Angled, ::IR.DoubleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args...) where {G<:Gate} = add_instruction!(c, Instruction(G(args[end]), [args[1:3]...])) -#apply_gate!(::IR.DoubleAngled, ::IR.DoubleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args...) where {G<:Gate} = add_instruction!(c, Instruction(G(args[end-1], args[end]), [args[1:3]...])) -apply_gate!(::IR.TripleAngled, ::IR.DoubleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args...) where {G<:Gate} = add_instruction!(c, Instruction(G(args[end-2], args[end-1], args[end]), [args[1:3]...])) - -apply_gate!(::IR.NonAngled, ::IR.NoControl, ::IR.MultiTarget, ::Type{Unitary}, c::Circuit, v::VecOrQubitSet, m::Matrix{ComplexF64}) = add_instruction!(c, Instruction(Unitary(m), v)) -apply_gate!(::IR.NonAngled, ::IR.NoControl, ::IR.MultiTarget, ::Type{Unitary}, c::Circuit, args...) = add_instruction!(c, Instruction(Unitary(args[end]), [args[1:end-1]...])) +(::Type{G})(c::Circuit) where {G<:Gate} = throw(ArgumentError("gate applied to a circuit must have targets.")) +(::Type{G})(c::Circuit, args...) where {G<:Gate} = apply_gate!(Val(n_angles(G)), IR.Control(ir_typ(G)), IR.Target(ir_typ(G)), G, c, args...) +apply_gate!(::Type{G}, c::Circuit, args...) where {G<:Gate} = apply_gate!(Val(n_angles(G)), IR.Control(ir_typ(G)), IR.Target(ir_typ(G)), G, c, args...) + +apply_gate!(::Val{N}, ::IR.NoControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, arg::IntOrQubit, angle::Vararg{<:Union{Float64, FreeParameter}}) where {G<:Gate, N} = add_instruction!(c, Instruction(G(angle), arg)) +apply_gate!(::Val{N}, ::IR.NoControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, v::VecOrQubitSet, angle::Vararg{<:Union{Float64, FreeParameter}}) where {G<:Gate, N} = (foreach(i->add_instruction!(c, Instruction(G(angle), i)), v); return c) +apply_gate!(::Val{N}, ::IR.NoControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, v::NTuple{Nq, Ti}, angle::Vararg{<:Union{Float64, FreeParameter}}) where {G<:Gate, N, Nq, Ti} = (foreach(i->add_instruction!(c, Instruction(G(angle), i)), v); return c) +apply_gate!(::Val{N}, ::IR.NoControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args...) where {G<:Gate, N} = (foreach(i->add_instruction!(c, Instruction(G(args[end-(N-1):end]), i)), args[1:end-N]); return c) +apply_gate!(::Val{0}, ::IR.NoControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, qs::Vararg{IntOrQubit}) where {G<:Gate} = (foreach(i->add_instruction!(c, Instruction(G(), i)), qs); return c) + +apply_gate!(::Val{N}, ::IR.NoControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, t1::IntOrQubit, t2::IntOrQubit, angle::Vararg{<:Union{Float64, FreeParameter}}) where {G<:Gate, N} = add_instruction!(c, Instruction(G(angle), [t1, t2])) +apply_gate!(::Val{N}, ::IR.NoControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, args::VecOrQubitSet, angle::Vararg{<:Union{Float64, FreeParameter}}) where {G<:Gate, N} = add_instruction!(c, Instruction(G(angle), args[1:2])) +apply_gate!(::Val{N}, ::IR.NoControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, args::NTuple{2, Ti}, angle::Vararg{<:Union{Float64, FreeParameter}}) where {G<:Gate, N, Ti} = add_instruction!(c, Instruction(G(angle), args[1:2])) + +apply_gate!(::Val{N}, ::IR.SingleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, ci::IntOrQubit, ti::IntOrQubit, angles::Vararg{<:Union{Float64, FreeParameter}}) where {G<:Gate, N} = add_instruction!(c, Instruction(G(angles), [ci, ti])) +apply_gate!(::Val{N}, ::IR.SingleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args::VecOrQubitSet, angles::Vararg{<:Union{Float64, FreeParameter}}) where {G<:Gate, N} = add_instruction!(c, Instruction(G(angles), args[1:2])) +apply_gate!(::Val{N}, ::IR.SingleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args::NTuple{2, Ti}, angles::Vararg{<:Union{Float64, FreeParameter}}) where {G<:Gate, N, Ti} = add_instruction!(c, Instruction(G(angles), [args...])) + +apply_gate!(::Val{N}, ::IR.SingleControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, ci::IntOrQubit, t1::IntOrQubit, t2::IntOrQubit, angle::Vararg{<:Union{Float64, FreeParameter}}) where {G<:Gate, N} = add_instruction!(c, Instruction(G(angle), [ci, t1, t2])) +apply_gate!(::Val{N}, ::IR.SingleControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, args::VecOrQubitSet, angle::Vararg{<:Union{Float64, FreeParameter}}) where {G<:Gate, N} = add_instruction!(c, Instruction(G(angle), args[1:3])) +apply_gate!(::Val{N}, ::IR.SingleControl, ::IR.DoubleTarget, ::Type{G}, c::Circuit, args::NTuple{3, Ti}, angle::Vararg{<:Union{Float64, FreeParameter}}) where {G<:Gate, N, Ti} = add_instruction!(c, Instruction(G(angle), [args...])) + +apply_gate!(::Val{N}, ::IR.DoubleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, c1::IntOrQubit, c2::IntOrQubit, ti::IntOrQubit, angle::Vararg{<:Union{Float64, FreeParameter}}) where {G<:Gate, N} = add_instruction!(c, Instruction(G(angle), [c1, c2, ti])) +apply_gate!(::Val{N}, ::IR.DoubleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args::VecOrQubitSet, angle::Vararg{<:Union{Float64, FreeParameter}}) where {G<:Gate, N} = add_instruction!(c, Instruction(G(angle), args[1:3])) +apply_gate!(::Val{N}, ::IR.DoubleControl, ::IR.SingleTarget, ::Type{G}, c::Circuit, args::NTuple{3, Ti}, angle::Vararg{<:Union{Float64, FreeParameter}}) where {G<:Gate, N, Ti} = add_instruction!(c, Instruction(G(angle), [args...])) + +apply_gate!(::Val{0}, ::IR.NoControl, ::IR.MultiTarget, ::Type{Unitary}, c::Circuit, v::VecOrQubitSet, m::Matrix{ComplexF64}) = add_instruction!(c, Instruction(Unitary(m), v)) +apply_gate!(::Val{0}, ::IR.NoControl, ::IR.MultiTarget, ::Type{Unitary}, c::Circuit, args...) = add_instruction!(c, Instruction(Unitary(args[end]), [args[1:end-1]...])) diff --git a/src/gates.jl b/src/gates.jl index 72392bbe..8ea1b7fe 100644 --- a/src/gates.jl +++ b/src/gates.jl @@ -1,4 +1,4 @@ -export Gate,AngledGate,TripleAngledGate,H,I,X,Y,Z,S,Si,T,Ti,V,Vi,CNot,Swap,ISwap,CV,CY,CZ,ECR,CCNot,CSwap,Unitary,Rx,Ry,Rz,PhaseShift,PSwap,XY,CPhaseShift,CPhaseShift00,CPhaseShift01,CPhaseShift10,XX,YY,ZZ,GPi,GPi2,MS +export Gate, AngledGate, H, I, X, Y, Z, S, Si, T, Ti, V, Vi, CNot, Swap, ISwap, CV, CY, CZ, ECR, CCNot, CSwap, Unitary, Rx, Ry, Rz, PhaseShift, PSwap, XY, CPhaseShift, CPhaseShift00, CPhaseShift01, CPhaseShift10, XX, YY, ZZ, GPi, GPi2, MS """ Gate <: QuantumOperator @@ -6,79 +6,76 @@ Abstract type representing a quantum gate. """ abstract type Gate <: QuantumOperator end StructTypes.StructType(::Type{Gate}) = StructTypes.AbstractType() -StructTypes.subtypes(::Type{Gate}) = (angledgate=AngledGate, tripleangledgate=TripleAngledGate, h=H, i=I, x=X, y=Y, z=Z, s=S, si=Si, t=T, ti=Ti, v=V, vi=Vi, cnot=CNot, swap=Swap, iswap=ISwap, cv=CV, cy=CY, cz=CZ, ecr=ECR, ccnot=CCNot, cswap=CSwap, unitary=Unitary, rx=Rx, ry=Ry, rz=Rz, phaseshift=PhaseShift, pswap=PSwap, xy=XY, cphaseshift=CPhaseShift, cphaseshift00=CPhaseShift00, cphaseshift01=CPhaseShift01, cphaseshift10=CPhaseShift10, xx=XX, yy=YY, zz=ZZ, gpi=GPi, gpi2=GPi2, ms=MS) +StructTypes.subtypes(::Type{Gate}) = (angledgate=AngledGate, h=H, i=I, x=X, y=Y, z=Z, s=S, si=Si, t=T, ti=Ti, v=V, vi=Vi, cnot=CNot, swap=Swap, iswap=ISwap, cv=CV, cy=CY, cz=CZ, ecr=ECR, ccnot=CCNot, cswap=CSwap, unitary=Unitary, rx=Rx, ry=Ry, rz=Rz, phaseshift=PhaseShift, pswap=PSwap, xy=XY, cphaseshift=CPhaseShift, cphaseshift00=CPhaseShift00, cphaseshift01=CPhaseShift01, cphaseshift10=CPhaseShift10, xx=XX, yy=YY, zz=ZZ, gpi=GPi, gpi2=GPi2, ms=MS) """ - AngledGate <: Gate + AngledGate{NA} <: Gate -Abstract type representing a quantum gate with an `angle` parameter. +Parametric type representing a quantum gate with `NA` `angle` parameters. """ -abstract type AngledGate <: Gate end +abstract type AngledGate{NA} <: Gate end StructTypes.StructType(::Type{AngledGate}) = StructTypes.AbstractType() -StructTypes.subtypes(::Type{AngledGate}) = (rx=Rx, ry=Ry, rz=Rz, phaseshift=PhaseShift, pswap=PSwap, xy=XY, cphaseshift=CPhaseShift, cphaseshift00=CPhaseShift00, cphaseshift01=CPhaseShift01, cphaseshift10=CPhaseShift10, xx=XX, yy=YY, zz=ZZ, gpi=GPi, gpi2=GPi2) - -""" - TripleAngledGate <: Gate - -Abstract type representing a quantum gate with three `angle` parameters. -""" -abstract type TripleAngledGate <: Gate end -StructTypes.StructType(::Type{TripleAngledGate}) = StructTypes.AbstractType() -StructTypes.subtypes(::Type{TripleAngledGate}) = (ms=MS) - -for (G, IRG, label, qc, c) in zip((:Rx, :Ry, :Rz, :PhaseShift, :PSwap, :XY, :CPhaseShift, :CPhaseShift00, :CPhaseShift01, :CPhaseShift10, :XX, :YY, :ZZ, :GPi, :GPi2), (:(IR.Rx), :(IR.Ry), :(IR.Rz), :(IR.PhaseShift), :(IR.PSwap), :(IR.XY), :(IR.CPhaseShift), :(IR.CPhaseShift00), :(IR.CPhaseShift01), :(IR.CPhaseShift10), :(IR.XX), :(IR.YY), :(IR.ZZ), :(IR.GPi), :(IR.GPi2)), ("rx", "ry", "rz", "phaseshift", "pswap", "xy", "cphaseshift", "cphaseshift00", "cphaseshift01", "cphaseshift10", "xx", "yy", "zz", "gpi", "gpi2"), (:1, :1, :1, :1, :2, :2, :2, :2, :2, :2, :2, :2, :2, :1, :1), (["Rx(ang)"], ["Ry(ang)"], ["Rz(ang)"], ["PHASE(ang)"], ["PSWAP(ang)", "PSWAP(ang)"], ["XY(ang)", "XY(ang)"], ["C", "PHASE(ang)"], ["C", "PHASE00(ang)"], ["C", "PHASE01(ang)"], ["C", "PHASE10(ang)"], ["XX(ang)", "XX(ang)"], ["YY(ang)", "YY(ang)"], ["ZZ(ang)", "ZZ(ang)"], ["GPi(ang)"], ["GPi2(ang)"])) - @eval begin - @doc """ - $($G) <: AngledGate - $($G)(angle::Union{Float64, FreeParameter}) -> $($G) - - $($G) gate. - """ - struct $G <: AngledGate - angle::Union{Float64, FreeParameter} - end - chars(g::$G) = map(char->replace(string(char), "ang"=>string(g.angle)), $c) - qubit_count(g::$G) = $qc - qubit_count(::Type{$G}) = $qc - function ir(g::$G, target::QubitSet, ::Val{:JAQCD}; kwargs...) - t_c = IR._generate_control_and_target(IR.ControlAndTarget($IRG)..., target) - return $IRG(g.angle, t_c..., $label) - end - function ir(g::$G, target::QubitSet, ::Val{:OpenQASM}; serialization_properties=OpenQASMSerializationProperties()) - t_c = IR._generate_control_and_target(IR.ControlAndTarget($IRG)..., target) - t = format_qubits(t_c, serialization_properties) - return $label*"($(g.angle)) $t;" - end - end -end -for (G, IRG, label, qc, c) in zip((:MS,), (:(IR.MS),), ("ms",), (:2,), (["MS(ang1, ang2, ang3)", "MS(ang1, ang2, ang3)"],)) +n_angles(::Type{<:Gate}) = 0 +n_angles(::Type{<:AngledGate{N}}) where {N} = N +n_angles(g::G) where {G<:Gate} = n_angles(G) +for gate_def in ( + (:Rx, :1, :1, ("Rx(ang)",)), + (:Ry, :1, :1, ("Ry(ang)",)), + (:Rz, :1, :1, ("Rz(ang)",)), + (:PhaseShift, :1, :1, ("PHASE(ang)",)), + (:PSwap, :1, :2, ("PSWAP(ang)", "PSWAP(ang)")), + (:XY, :1, :2, ("XY(ang)", "XY(ang)")), + (:CPhaseShift, :1, :2, ("C", "PHASE(ang)")), + (:CPhaseShift00, :1, :2, ("C", "PHASE00(ang)")), + (:CPhaseShift01, :1, :2, ("C", "PHASE01(ang)")), + (:CPhaseShift10, :1, :2, ("C", "PHASE10(ang)")), + (:XX, :1, :2, ("XX(ang)", "XX(ang)")), + (:YY, :1, :2, ("YY(ang)", "YY(ang)")), + (:ZZ, :1, :2, ("ZZ(ang)", "ZZ(ang)")), + (:GPi, :1, :1, ("GPi(ang)",)), + (:GPi2, :1, :1, ("GPi2(ang)",)), + (:MS, :3, :2, ("MS(ang)", "MS(ang)"))) + G, n_angle, qc, c = gate_def @eval begin @doc """ - $($G) <: TripleAngledGate - $($G)(angle1::Union{Float64, FreeParameter}, angle2::Union{Float64, FreeParameter}, angle3::Union{Float64, FreeParameter}) -> $($G) + $($G) <: AngledGate{$($n_angle)} + $($G)(angles) -> $($G) $($G) gate. """ - struct $G <: TripleAngledGate - angle1::Union{Float64, FreeParameter} - angle2::Union{Float64, FreeParameter} - angle3::Union{Float64, FreeParameter} + struct $G <: AngledGate{$n_angle} + angle::NTuple{$n_angle, Union{Float64, FreeParameter}} + $G(angle::NTuple{$n_angle, Union{Float64, FreeParameter}}) = new(angle) end - chars(g::$G) = map(char->replace(string(char), "ang1"=>string(g.angle1), "ang2"=>string(g.angle2), "ang3"=>string(g.angle3)), $c) - qubit_count(g::$G) = $qc + $G(angles::Vararg{Union{Float64, FreeParameter}}) = $G(tuple(angles...)) + chars(::Type{$G}) = $c qubit_count(::Type{$G}) = $qc - function ir(g::$G, target::QubitSet, ::Val{:JAQCD}; kwargs...) - t_c = IR._generate_control_and_target(IR.ControlAndTarget($IRG)..., target) - return $IRG(g.angle1, g.angle2, g.angle3, collect(t_c)..., $label) - end - function ir(g::$G, target::QubitSet, ::Val{:OpenQASM}; serialization_properties=OpenQASMSerializationProperties()) - t_c = IR._generate_control_and_target(IR.ControlAndTarget($IRG)..., target) - t = format_qubits(t_c, serialization_properties) - return $label*"($(g.angle1), $(g.angle2), $(g.angle3)) $t;" - end end end -for (G, IRG, label, qc, c) in zip((:H, :I, :X, :Y, :Z, :S, :Si, :T, :Ti, :V, :Vi, :CNot, :Swap, :ISwap, :CV, :CY, :CZ, :ECR, :CCNot, :CSwap), (:(IR.H), :(IR.I), :(IR.X), :(IR.Y), :(IR.Z), :(IR.S), :(IR.Si), :(IR.T), :(IR.Ti), :(IR.V), :(IR.Vi), :(IR.CNot), :(IR.Swap), :(IR.ISwap), :(IR.CV), :(IR.CY), :(IR.CZ), :(IR.ECR), :(IR.CCNot), :(IR.CSwap)), ("h", "i", "x", "y", "z", "s", "si", "t", "ti", "v", "vi", "cnot", "swap", "iswap", "cv", "cy", "cz", "ecr", "ccnot", "cswap"), (:1, :1, :1, :1, :1, :1, :1, :1, :1, :1, :1, :2, :2, :2, :2, :2, :2, :2, :3, :3), (["H"], ["I"], ["X"], ["Y"], ["Z"], ["S"], ["Si"], ["T"], ["Ti"], ["V"], ["Vi"], ["C", "X"], ["SWAP", "SWAP"], ["ISWAP", "ISWAP"], ["C", "V"], ["C", "Y"], ["C", "Z"], ["ECR", "ECR"], ["C", "C", "X"], ["C", "SWAP", "SWAP"])) +(::Type{G})(angle::Union{Float64, FreeParameter}) where {G<:AngledGate} = G((angles,)) + +for gate_def in ( + (:H, :1, ("H",)), + (:I, :1, ("I",)), + (:X, :1, ("X",)), + (:Y, :1, ("Y",)), + (:Z, :1, ("Z",)), + (:S, :1, ("S",)), + (:Si, :1, ("Si",)), + (:T, :1, ("T",)), + (:Ti, :1, ("Ti",)), + (:V, :1, ("V",)), + (:Vi, :1, ("Vi",)), + (:CNot, :2, ("C", "X")), + (:Swap, :2, ("SWAP", "SWAP")), + (:ISwap, :2, ("ISWAP", "ISWAP")), + (:CV, :2, ("C", "V")), + (:CY, :2, ("C", "Y")), + (:CZ, :2, ("C", "Z")), + (:ECR, :2, ("ECR", "ECR")), + (:CCNot, :3, ("C", "C", "X")), + (:CSwap, :3, ("C", "SWAP", "SWAP"))) + G, qc, c = gate_def @eval begin @doc """ $($G) <: Gate @@ -87,18 +84,35 @@ for (G, IRG, label, qc, c) in zip((:H, :I, :X, :Y, :Z, :S, :Si, :T, :Ti, :V, :Vi $($G) gate. """ struct $G <: Gate end - chars(g::$G) = $c - qubit_count(g::$G) = $qc + chars(::Type{$G}) = $c qubit_count(::Type{$G}) = $qc - function ir(g::$G, target::QubitSet, ::Val{:JAQCD}; kwargs...) - t_c = IR._generate_control_and_target(IR.ControlAndTarget($IRG)..., target) - return $IRG(t_c..., $label) - end - function ir(g::$G, target::QubitSet, ::Val{:OpenQASM}; serialization_properties=OpenQASMSerializationProperties()) - t_c = IR._generate_control_and_target(IR.ControlAndTarget($IRG)..., target) - t = format_qubits(t_c, serialization_properties) - return $label*" $t;" - end + end +end +label(::Type{G}) where {G<:Gate} = lowercase(string(G)) +(::Type{G})(x::Tuple{}) where {G<:Gate} = G() +(::Type{G})(x::Tuple{}) where {G<:AngledGate} = throw(ArgumentError("angled gate must be constructed with at least one angle.")) +(::Type{G})(x::AbstractVector) where {G<:AngledGate} = G(x...) +qubit_count(g::G) where {G<:Gate} = qubit_count(G) +angles(g::G) where {G<:Gate} = () +angles(g::AngledGate{N}) where {N} = g.angle +chars(g::G) where {G<:Gate} = map(char->replace(string(char), "ang"=>join(string.(angles(g)), ", ")), chars(G)) +ir_typ(::Type{G}) where {G<:Gate} = getproperty(IR, Symbol(G)) +ir_typ(g::G) where {G<:Gate} = ir_typ(G) +label(g::G) where {G<:Gate} = label(G) +ir_str(g::G) where {G<:AngledGate} = label(g) * "(" * join(string.(angles(g)), ", ") * ")" +ir_str(g::G) where {G<:Gate} = label(g) +targets_and_controls(g::G, target::QubitSet) where {G<:Gate} = IR._generate_control_and_target(IR.ControlAndTarget(ir_typ(g))..., target) +function ir(g::G, target::QubitSet, ::Val{:JAQCD}; kwargs...) where {G<:Gate} + t_c = targets_and_controls(g, target) + return ir_typ(g)(angles(g)..., t_c..., label(g)) +end +function ir(g::G, target::QubitSet, ::Val{:OpenQASM}; serialization_properties=OpenQASMSerializationProperties()) where {G<:Gate} + t = format_qubits(targets_and_controls(g, target), serialization_properties) + ir_string = ir_str(g) * " " * t + if occursin("#pragma", ir_string) + return ir_string + else + return ir_string * ";" end end """ @@ -109,49 +123,38 @@ Arbitrary unitary gate. """ struct Unitary <: Gate matrix::Matrix{ComplexF64} + Unitary(matrix::Matrix{<:Number}) = new(ComplexF64.(matrix)) end Unitary(mat::Vector{Vector{Vector{Float64}}}) = Unitary(complex_matrix_from_ir(mat)) Base.:(==)(u1::Unitary, u2::Unitary) = u1.matrix == u2.matrix qubit_count(g::Unitary) = convert(Int, log2(size(g.matrix, 1))) -chars(g::Unitary) = ntuple(i->"U", qubit_count(g)) +chars(g::Unitary) = ntuple(i->"U", qubit_count(g)) + +targets_and_controls(g::Unitary, target::QubitSet) = target[1:convert(Int, log2(size(g.matrix, 1)))] +ir_str(g::Unitary) = "#pragma braket unitary(" * format_matrix(g.matrix) * ")" function ir(g::Unitary, target::QubitSet, ::Val{:JAQCD}; kwargs...) + t_c = targets_and_controls(g, target) mat = complex_matrix_to_ir(g.matrix) - t_c = target[1:convert(Int, log2(size(g.matrix, 1)))] return IR.Unitary(t_c, mat, "unitary") end -function ir(g::Unitary, target::QubitSet, ::Val{:OpenQASM}; serialization_properties=OpenQASMSerializationProperties()) - t_c = target[1:convert(Int, log2(size(g.matrix, 1)))] - m = format_matrix(g.matrix) - t = format_qubits(t_c, serialization_properties) - return "#pragma braket unitary($m) $t" -end StructTypes.StructType(::Type{<:Gate}) = StructTypes.Struct() abstract type Parametrizable end struct Parametrized end struct NonParametrized end -Parametrizable(g::TripleAngledGate) = Parametrized() Parametrizable(g::AngledGate) = Parametrized() -Parametrizable(g::Gate) = NonParametrized() - -parameters(g::TripleAngledGate) = filter(a->a isa FreeParameter, [g.angle1, g.angle2, g.angle3]) -parameters(g::AngledGate) = g.angle isa FreeParameter ? [g.angle] : FreeParameter[] -parameters(g::Gate) = FreeParameter[] +Parametrizable(g::Gate) = NonParametrized() +parameters(g::AngledGate) = collect(filter(a->a isa FreeParameter, angles(g))) +parameters(g::Gate) = FreeParameter[] bind_value!(g::G, params::Dict{Symbol, Number}) where {G<:Gate} = bind_value!(Parametrizable(g), g, params) bind_value!(::NonParametrized, g::G, params::Dict{Symbol, Number}) where {G<:Gate} = g - -function bind_value!(::Parametrized, g::G, params::Dict{Symbol, Number}) where {G<:TripleAngledGate} - new_angles = map([g.angle1, g.angle2, g.angle3]) do angle +function bind_value!(::Parametrized, g::G, params::Dict{Symbol, Number}) where {G<:AngledGate} + new_angles = map(angles(g)) do angle angle isa FreeParameter || return angle return get(params, angle.name, angle) end return G(new_angles...) end - -function bind_value!(::Parametrized, g::G, params::Dict{Symbol, Number}) where {G<:AngledGate} - g.angle isa FreeParameter || return G(g.angle) - new_angle = get(params, g.angle.name, g.angle) - return G(new_angle) -end ir(g::Gate, target::Int, args...) = ir(g, QubitSet(target), args...) Base.copy(g::G) where {G<:Gate} = G((copy(getproperty(g, fn)) for fn in fieldnames(G))...) +Base.copy(g::G) where {G<:AngledGate} = G(angles(g)) diff --git a/src/schemas.jl b/src/schemas.jl index fe60a463..949b2e5b 100644 --- a/src/schemas.jl +++ b/src/schemas.jl @@ -33,17 +33,27 @@ ir(x::Instruction, ::Val{:OpenQASM}; kwargs...) = isempty(x.target) ? ir(x.opera conc_types = filter(Base.isconcretetype, vcat(subtypes(AbstractIR), subtypes(CompilerDirective))) nt_dict = merge([Dict(zip(fieldnames(t), (Union{Nothing, x} for x in fieldtypes(t)))) for t in conc_types]...) -ks = tuple(keys(nt_dict)...) -vs = Tuple{values(nt_dict)...} +ks = tuple(:angles, keys(nt_dict)...) +vs = Tuple{Union{Nothing, Vector{Float64}}, values(nt_dict)...} inst_typ = NamedTuple{ks, vs} StructTypes.lowertype(::Type{Instruction}) = inst_typ function Instruction(x) sts = merge(StructTypes.subtypes(Gate), StructTypes.subtypes(Noise), StructTypes.subtypes(CompilerDirective)) o_type = sts[Symbol(x[:type])] (o_type <: CompilerDirective) && return Instruction(o_type(), Int[]) - o_fns = fieldnames(o_type) - args = [x[fn] for fn in o_fns] - op = o_type(args...) + if o_type <: AngledGate{1} + o_fns = (:angles, :angle) + args = (x[:angle],) + op = o_type(args) + elseif o_type <: AngledGate{3} + o_fns = (:angles, :angle1, :angle2, :angle3) + args = tuple([x[fn] for fn in [Symbol("angle$i") for i in 1:n_angles(o_type)]]...) + op = o_type(args) + else + o_fns = fieldnames(o_type) + args = [x[fn] for fn in o_fns] + op = o_type(args...) + end raw_target = Int[] target_keys = collect(setdiff(keys(x), vcat(o_fns..., :type))) for k in sort(target_keys, by=(x->occursin("target", string(x)))) diff --git a/test/circuits.jl b/test/circuits.jl index 8d7a06f9..4d26cfc2 100644 --- a/test/circuits.jl +++ b/test/circuits.jl @@ -809,7 +809,8 @@ using Braket: Instruction, Result, VIRTUAL, PHYSICAL, OpenQASMSerializationPrope ] c, sps, expected_ir = ir_bolus if !isnothing(sps) - @test ir(c, Val(:OpenQASM), serialization_properties=sps) == expected_ir + generated_ir = ir(c, Val(:OpenQASM), serialization_properties=sps) + @test generated_ir == expected_ir else @test ir(c, Val(:OpenQASM)) == expected_ir end @@ -820,7 +821,8 @@ using Braket: Instruction, Result, VIRTUAL, PHYSICAL, OpenQASMSerializationPrope c = Circuit([(H, [0, 1, 2]), (Rx, 0, a), (Ry, 1, b)]) props = OpenQASMSerializationProperties(PHYSICAL) expected_ir = OpenQasmProgram(join([ "OPENQASM 3.0;", "input float a;", "input float b;", "bit[3] b;", "h \$0;", "h \$1;", "h \$2;", "rx(a) \$0;", "ry(b) \$1;", "b[0] = measure \$0;", "b[1] = measure \$1;", "b[2] = measure \$2;"], "\n")) - @test ir(c, Val(:OpenQASM), serialization_properties=props) == expected_ir + generated_ir = ir(c, Val(:OpenQASM), serialization_properties=props) + @test generated_ir == expected_ir end end @testset "pretty-printing" begin diff --git a/test/gates.jl b/test/gates.jl index f8bbeea6..825d78b4 100644 --- a/test/gates.jl +++ b/test/gates.jl @@ -149,25 +149,27 @@ T_mat = round.(reduce(hcat, [[1.0, 0], [0, 0.70710678 + 0.70710678im]]), digits= end @testset "Angled 3 qubit gates" begin # build some "fake" (for now) 3 qubit gates to test gate applicators - struct CCPhaseShift <: Braket.AngledGate - angle::Union{Float64, Braket.FreeParameter} + struct CCPhaseShift <: Braket.AngledGate{1} + angle::NTuple{1, Union{Float64, Braket.FreeParameter}} end - struct CXX <: Braket.AngledGate - angle::Union{Float64, Braket.FreeParameter} + CCPhaseShift(angles::Vararg{Union{Float64, FreeParameter}}) = CCPhaseShift(tuple(angles...)) + struct CXX <: Braket.AngledGate{1} + angle::NTuple{1, Union{Float64, Braket.FreeParameter}} end + CXX(angles::Vararg{Union{Float64, FreeParameter}}) = CXX(tuple(angles...)) angle = rand() c = Circuit() - c = Braket.apply_gate!(Braket.IR.Angled(), Braket.IR.DoubleControl(), Braket.IR.SingleTarget(), CCPhaseShift, c, 0, 1, 2, angle) - @test c.instructions == [Instruction(CCPhaseShift(angle), [0, 1, 2])] + c = Braket.apply_gate!(Val(1), Braket.IR.DoubleControl(), Braket.IR.SingleTarget(), CCPhaseShift, c, 0, 1, 2, angle) + @test c.instructions == [Instruction(CCPhaseShift((angle,)), [0, 1, 2])] c = Circuit() - c = Braket.apply_gate!(Braket.IR.Angled(), Braket.IR.DoubleControl(), Braket.IR.SingleTarget(), CCPhaseShift, c, [0, 1, 2], angle) - @test c.instructions == [Instruction(CCPhaseShift(angle), [0, 1, 2])] + c = Braket.apply_gate!(Val(1), Braket.IR.DoubleControl(), Braket.IR.SingleTarget(), CCPhaseShift, c, [0, 1, 2], angle) + @test c.instructions == [Instruction(CCPhaseShift((angle,)), [0, 1, 2])] c = Circuit() - c = Braket.apply_gate!(Braket.IR.Angled(), Braket.IR.SingleControl(), Braket.IR.DoubleTarget(), CXX, c, 0, 1, 2, angle) - @test c.instructions == [Instruction(CXX(angle), [0, 1, 2])] + c = Braket.apply_gate!(Val(1), Braket.IR.SingleControl(), Braket.IR.DoubleTarget(), CXX, c, 0, 1, 2, angle) + @test c.instructions == [Instruction(CXX((angle,)), [0, 1, 2])] c = Circuit() - c = Braket.apply_gate!(Braket.IR.Angled(), Braket.IR.SingleControl(), Braket.IR.DoubleTarget(), CXX, c, [0, 1, 2], angle) - @test c.instructions == [Instruction(CXX(angle), [0, 1, 2])] + c = Braket.apply_gate!(Val(1), Braket.IR.SingleControl(), Braket.IR.DoubleTarget(), CXX, c, [0, 1, 2], angle) + @test c.instructions == [Instruction(CXX((angle,)), [0, 1, 2])] end @testset "OpenQASM IR" begin fp = FreeParameter(:alpha) @@ -302,7 +304,8 @@ T_mat = round.(reduce(hcat, [[1.0, 0], [0, 0.70710678 + 0.70710678im]]), digits= ) ] gate, target, s_props, expected_ir = ir_bolus - @test ir(gate, target, Val(:OpenQASM); serialization_properties=s_props) == expected_ir + generated_ir = ir(gate, target, Val(:OpenQASM); serialization_properties=s_props) + @test generated_ir == expected_ir end end @test StructTypes.StructType(X) == StructTypes.Struct() diff --git a/test/integ_tests/simulator_quantum_task.jl b/test/integ_tests/simulator_quantum_task.jl index 6527f759..6e359e58 100644 --- a/test/integ_tests/simulator_quantum_task.jl +++ b/test/integ_tests/simulator_quantum_task.jl @@ -19,7 +19,7 @@ function many_layers(n_qubits::Int, n_layers::Int) for qubit in qubits angle = rand(d) gate = rand([Rx, Ry, Rz, H]) - if gate isa AngledGate + if gate <: AngledGate{1} circuit(gate, qubit, angle) else circuit(gate, qubit) diff --git a/test/runtests.jl b/test/runtests.jl index 5dea316e..875a0868 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -68,6 +68,7 @@ for group in groups elseif pkg_name == "Examples" Pkg.activate(joinpath(@__DIR__, "..", "examples")) Pkg.instantiate() + develop_subpackage("PyBraket") if test_type == "unit" # test example notebooks that don't need AWS devices @testset "Local Examples" begin