diff --git a/Project.toml b/Project.toml index 9f84a41..1bc11da 100644 --- a/Project.toml +++ b/Project.toml @@ -4,8 +4,10 @@ authors = ["Arsh Sharma and contributors"] version = "0.1.0" [deps] +Configurations = "5218b696-f38b-4ac9-8b61-a12ec717816d" IBMQClient = "02860bad-daa6-4506-8736-4a3616c18085" -Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" +Yao = "5872b779-8223-5990-8dd0-5abbb0748c8c" YaoBlocks = "418bc28f-b43b-5e0b-a6e7-61bbc1a2c1df" [compat] @@ -15,7 +17,6 @@ julia = "1.6" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -Yao = "5872b779-8223-5990-8dd0-5abbb0748c8c" [targets] -test = ["Test", "Yao"] +test = ["Test"] diff --git a/docs/make.jl b/docs/make.jl index b1aa1c0..f1cf725 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -4,21 +4,16 @@ indigo = DocThemeIndigo.install(YaoBlocksQobj) makedocs(; - modules=[YaoBlocksQobj], - authors="Arsh Sharma and contributors", - repo="https://github.com/QuantumBFS/YaoBlocksQobj.jl/blob/{commit}{path}#{line}", - sitename="YaoBlocksQobj.jl", - format=Documenter.HTML(; - prettyurls=get(ENV, "CI", "false") == "true", - canonical="https://QuantumBFS.github.io/YaoBlocksQobj.jl", - assets=String[indigo, "assets/default.css"], + modules = [YaoBlocksQobj], + authors = "Arsh Sharma and contributors", + repo = "https://github.com/QuantumBFS/YaoBlocksQobj.jl/blob/{commit}{path}#{line}", + sitename = "YaoBlocksQobj.jl", + format = Documenter.HTML(; + prettyurls = get(ENV, "CI", "false") == "true", + canonical = "https://QuantumBFS.github.io/YaoBlocksQobj.jl", + assets = String[indigo, "assets/default.css"], ), - pages=[ - "Quickstart" => "index.md", - "API References" => "refs.md", - ], + pages = ["Quickstart" => "index.md", "API References" => "refs.md"], ) -deploydocs(; - repo="github.com/QuantumBFS/YaoBlocksQobj.jl",devbranch = "main", -) +deploydocs(; repo = "github.com/QuantumBFS/YaoBlocksQobj.jl", devbranch = "main") diff --git a/src/YaoBlocksQobj.jl b/src/YaoBlocksQobj.jl index a336e81..51c8f59 100644 --- a/src/YaoBlocksQobj.jl +++ b/src/YaoBlocksQobj.jl @@ -1,10 +1,12 @@ module YaoBlocksQobj -export create_qobj +export convert_to_qobj, convert_to_qbir +using Configurations using IBMQClient.Schema using YaoBlocks include("qobj.jl") +include("qbir.jl") end diff --git a/src/qbir.jl b/src/qbir.jl new file mode 100644 index 0000000..cec744d --- /dev/null +++ b/src/qbir.jl @@ -0,0 +1,152 @@ +using Yao + +mutable struct U1{T<:Number} <: PrimitiveBlock{1} + lambda::T +end + +# 2-parameter 1-pulse single qubit gate +mutable struct U2{T<:Number} <: PrimitiveBlock{1} + phi::T + lambda::T +end + +# 3-parameter 2-pulse single qubit gate +mutable struct U3{T<:Number} <: PrimitiveBlock{1} + theta::T + phi::T + lambda::T +end + +Base.:(==)(u1_1::U1, u1_2::U1) = u1_1.lambda == u1_2.lambda +Base.:(==)(u2_1::U2, u2_2::U2) = u2_1.phi == u2_2.phi && u2_1.lambda == u2_2.lambda +Base.:(==)(u3_1::U3, u3_2::U3) = + u3_1.theta == u3_2.theta && u3_1.phi == u3_2.phi && u3_1.lambda == u3_2.lambda + +function Yao.mat(::Type{T}, u1::U1) where {T} + λ = u1.lambda + + T[ + 1 0 + 0 exp(im * λ) + ] +end + +function Yao.mat(::Type{T}, u2::U2) where {T} + ϕ, λ = u2.phi, u2.lambda + + T[ + 1/√2 (-exp(im * λ))/√2 + (exp(im * ϕ))/√2 (exp(im * (ϕ + λ)))/√2 + ] +end + +function Yao.mat(::Type{T}, u3::U3) where {T} + θ, ϕ, λ = u3.theta, u3.phi, u3.lambda + + T[ + cos(θ / 2) -sin(θ / 2)*exp(im * λ) + sin(θ / 2)*exp(im * ϕ) cos(θ / 2)*exp(im * (ϕ + λ)) + ] +end + +Yao.iparams_eltype(::U1{T}) where {T} = T +Yao.iparams_eltype(::U2{T}) where {T} = T +Yao.iparams_eltype(::U3{T}) where {T} = T + +Yao.getiparams(u1::U1{T}) where {T} = (u1.lambda) +Yao.getiparams(u2::U2{T}) where {T} = (u2.phi, u2.lambda) +Yao.getiparams(u3::U3{T}) where {T} = (u3.theta, u3.phi, u3.lambda) + +function Yao.setiparams!(u1::U1{T}, λ) where {T} + u1.lambda = λ + return u1 +end + +function Yao.setiparams!(u2::U2{T}, ϕ, λ) where {T} + u2.phi = ϕ + u2.lambda = λ + return u2 +end + +function Yao.setiparams!(u3::U3{T}, θ, ϕ, λ) where {T} + u3.theta = θ + u3.phi = ϕ + u3.lambda = λ + return u3 +end + +YaoBlocks.@dumpload_fallback U1 U1 +YaoBlocks.@dumpload_fallback U2 U2 +YaoBlocks.@dumpload_fallback U3 U3 + +""" + convert_to_qbir(inst) + +Converts Qobj based instructions back to YaoIR. + +- `inst`: The Qobj based instructions. + +For Example: +```julia +q = convert_to_qobj(chain(1, put(1 => H))) +ir = q.experiments[1].instructions |> convert_to_qbir +``` +""" +function convert_to_qbir(inst) + n = maximum(x -> maximum(x.qubits), inst) + 1 + chain( + n, + map(inst) do x + name, locs = x.name, x.qubits .+ 1 + nc = 0 + while name[nc+1] == 'c' && nc < length(name) + nc += 1 + end + if nc > 0 + control( + n, + locs[1:nc], + locs[nc+1:end] => name_index(name[nc+1:end], x.params), + ) + elseif name == "measure" + put(n, locs => Yao.Measure(locs...)) + else + put(n, locs => name_index(name, x.params)) + end + end, + ) +end + +function name_index(name, params = nothing) + if name == "u1" + U1(params...) + elseif name == "u2" + if isapprox(params, [0, π]) + H + else + U2(params...) + end + elseif name == "u3" + if isapprox(params[2], -π / 2) && isapprox(params[3], π / 2) + Rx(params[1]) + elseif params[2] == 0 && params[3] == 0 + Ry(params[1]) + else + U3(params...) + end + elseif name == "id" + I2 + elseif name == "x" + X + elseif name == "y" + Y + elseif name == "z" + Z + elseif name == "t" + T + elseif name == "swap" + SWAP + else + error("gate type `$name` not defined!") + end +end diff --git a/src/qobj.jl b/src/qobj.jl index da514df..ea5e046 100644 --- a/src/qobj.jl +++ b/src/qobj.jl @@ -1,86 +1,102 @@ -using Random +using Configurations, UUIDs + +@option struct ExpOptions + id::String = string(uuid1()) + header::Maybe{Dict{String,Any}} = nothing + nshots::Int = 1024 + exp_header::Maybe{Vector{Dict{String,Any}}} = nothing + exp_config::Maybe{Vector{Dict{String,Any}}} = nothing +end """ - create_qobj(qc, id, header, nshots, exp_header, exp_config) + convert_to_qobj(qc, id, header, nshots, exp_header, exp_config) - Creates a `Qobj` based on the IBMQClient schema. +Creates a `Qobj` based on the IBMQClient schema. - - `qc`: An `Array` of `ChainBlock`(circuits that are to be run). - - `id`(optional): User generated run identifier. - - `header` (optional): User-defined structure that contains metadata on the job and is not used. - - `nshots`: Number of times to repeat the experiment (for some simulators this may - be limited to 1, e.g., a unitary simulator). - Each unitary gate has an efficient representation in this basis. - - `exp_header`(optional): Array of User-defined structure that contains metadata on each experiment and - is not used by the backend. The header will be passed through to the result data structure unchanged. - For example, this may contain a fitting parameters for the experiment. In addition, this header can - contain a mapping of backend memory and backend qubits to OpenQASM registers. - This is because an OpenQASM circuit may contain multiple classical and quantum registers, - but Qobj flattens them into a single memory and single set of qubits. - - `exp_config` (optional): An Array of Configuration structure for user settings that can be different in each - experiment. These will override the configuration settings of the whole job. +- `qc`: An `Array` of `ChainBlock`(circuits that are to be run). +- `id`(optional): User generated run identifier. +- `header` (optional): User-defined structure that contains metadata on the job and is not used. +- `nshots`: Number of times to repeat the experiment (for some simulators this may +be limited to 1, e.g., a unitary simulator). +Each unitary gate has an efficient representation in this basis. +- `exp_header`(optional): Array of User-defined structure that contains metadata on each experiment and +is not used by the backend. The header will be passed through to the result data structure unchanged. +For example, this may contain a fitting parameters for the experiment. In addition, this header can +contain a mapping of backend memory and backend qubits to OpenQASM registers. +This is because an OpenQASM circuit may contain multiple classical and quantum registers, +but Qobj flattens them into a single memory and single set of qubits. +- `exp_config` (optional): An Array of Configuration structure for user settings that can be different in each +experiment. These will override the configuration settings of the whole job. """ -function create_qobj(qc::Array{<:AbstractBlock{N}}; id::String = randstring(), header = nothing, nshots::Int = 1024, exp_header = nothing, exp_config = nothing) where N - experiments = create_experiment(qc, exp_header, exp_config) - config = ExpConfig(shots = nshots, memory_slots = length(experiments)) - Qobj(;qobj_id = id, type = "QASM", schema_version = v"1", header, experiments = experiments, config = config) + +convert_to_qobj(qc::Vector{<:AbstractBlock}; kw...) = + convert_to_qobj(qc, ExpOptions(; kw...)) + +function convert_to_qobj(qc::Vector{<:AbstractBlock{N}}, options::ExpOptions) where {N} + experiments = create_experiment(qc, options.exp_header, options.exp_config) + config = ExpConfig(shots = options.nshots, memory_slots = length(experiments)) #from the schema + Qobj(; + qobj_id = options.id, + type = "QASM", + schema_version = v"1", + options.header, + experiments, + config, + ) end """ create_experiment(qc, exp_header, exp_config) - Returns and experiment type that consits of instructions. - - - `qc`: An `Array` of `ChainBlock`(circuits that are to be run). - - `exp_header`(optional): Array of User-defined structure that contains metadata on each experiment and - is not used by the backend. The header will be passed through to the result data structure unchanged. - For example, this may contain a fitting parameters for the experiment. In addition, this header can - contain a mapping of backend memory and backend qubits to OpenQASM registers. - This is because an OpenQASM circuit may contain multiple classical and quantum registers, - but Qobj flattens them into a single memory and single set of qubits. - - `exp_config` (optional): An Array of Configuration structure for user settings that can be different in each - experiment. These will override the configuration settings of the whole job. +Returns and experiment type that consits of instructions. + +- `qc`: An `Array` of `ChainBlock`(circuits that are to be run). +- `exp_header`(optional): Array of User-defined structure that contains metadata on each experiment and +is not used by the backend. The header will be passed through to the result data structure unchanged. +For example, this may contain a fitting parameters for the experiment. In addition, this header can +contain a mapping of backend memory and backend qubits to OpenQASM registers. +This is because an OpenQASM circuit may contain multiple classical and quantum registers, +but Qobj flattens them into a single memory and single set of qubits. +- `exp_config` (optional): An Array of Configuration structure for user settings that can be different in each +experiment. These will override the configuration settings of the whole job. """ -function create_experiment(qc::Array{<:AbstractBlock{N}}, exp_header = nothing, exp_config = nothing) where N - experiments = Experiment[] - head = false - config = false - if exp_header !== nothing - head = true - elseif exp_config !== nothing - config = true - end - - for i in 1:length(qc) - if head && config - exp = create_experiment!(qc[i], exp_header[i], exp_config[i]) - elseif head && !config - exp = create_experiment!(qc[i], exp_header[i], exp_config) - elseif !head && config - exp = create_experiment!(qc[i], exp_header, exp_config[i]) - else - exp = create_experiment!(qc[i], exp_header, exp_config) - end - - push!(experiments, exp) - end - return experiments -end - -function create_experiment!(qc::AbstractBlock{N}, exp_header = nothing, exp_config = nothing) where N +create_experiment( + qc::Vector{<:AbstractBlock{N}}, + exp_header::Nothing, + exp_config::Nothing, +) where {N} = collect(create_experiment(qc[i], nothing, nothing) for i = 1:length(qc)) +create_experiment( + qc::Vector{<:AbstractBlock{N}}, + exp_header::Array, + exp_config::Array, +) where {N} = + collect(create_experiment(qc[i], exp_header[i], exp_config[i]) for i = 1:length(qc)) +create_experiment( + qc::Vector{<:AbstractBlock{N}}, + exp_header::Nothing, + exp_config::Array, +) where {N} = collect(create_experiment(qc[i], nothing, exp_config[i]) for i = 1:length(qc)) +create_experiment( + qc::Vector{<:AbstractBlock{N}}, + exp_header::Array, + exp_config::Nothing, +) where {N} = collect(create_experiment(qc[i], exp_header[i], nothing) for i = 1:length(qc)) + +function create_experiment(qc::AbstractBlock{N}, exp_header, exp_config) where {N} exp_inst = generate_inst(qc) - experiment = Experiment(;header = exp_header, config = exp_config, instructions = exp_inst) + experiment = + Experiment(; header = exp_header, config = exp_config, instructions = exp_inst) return experiment end """ generate_inst(qc) - Parses the YaoIR into a list of IBMQ supported instructions +Parses the YaoIR into a list of IBMQ supported instructions - - `qc`: A `ChainBlock`(circuit that is to be run). +- `qc`: A `ChainBlock`(circuit that is to be run). """ -function generate_inst(qc::AbstractBlock{N}) where N +function generate_inst(qc::AbstractBlock{N}) where {N} inst = Instruction[] generate_inst!(inst, basicstyle(qc), [0:N-1...], Int[]) return inst @@ -97,11 +113,16 @@ function generate_inst!(inst, blk::PutBlock{N,M}, locs, controls) where {N,M} end function generate_inst!(inst, blk::ControlBlock{N,GT,C}, locs, controls) where {N,GT,C} - any(==(0),blk.ctrl_config) && error("Inverse Control used in Control gate context") - generate_inst!(inst, blk.content, sublocs(blk.locs, locs), [controls..., sublocs(blk.ctrl_locs, locs)...]) + any(==(0), blk.ctrl_config) && error("Inverse Control used in Control gate context") + generate_inst!( + inst, + blk.content, + sublocs(blk.locs, locs), + [controls..., sublocs(blk.ctrl_locs, locs)...], + ) end -function generate_inst!(inst, m::YaoBlocks.Measure{N}, locs, controls) where N +function generate_inst!(inst, m::YaoBlocks.Measure{N}, locs, controls) where {N} # memory: List of memory slots in which to store the measurement results (mustbe the same length as qubits). mlocs = sublocs(m.locations isa AllLocs ? [1:N...] : [m.locations...], locs) (m.operator isa ComputationalBasis) || error("measuring an operator is not supported") @@ -112,11 +133,23 @@ end # IBMQ Chip only supports ["id", "u1", "u2", "u3", "cx"] # x, y, z and control x, y, z, id, t, swap and other primitive gates -for (GT, NAME, MAXC) in [(:XGate, "x", 2), (:YGate, "y", 2), (:ZGate, "z", 2), - (:I2Gate, "id", 0), (:TGate, "t", 0), (:SWAPGate, "swap", 0)] +for (GT, NAME, MAXC) in [ + (:XGate, "x", 2), + (:YGate, "y", 2), + (:ZGate, "z", 2), + (:I2Gate, "id", 0), + (:TGate, "t", 0), + (:SWAPGate, "swap", 0), +] @eval function generate_inst!(inst, ::$GT, locs, controls) if length(controls) <= $MAXC - push!(inst, Gate(name = "c"^(length(controls))*$NAME, qubits = [controls..., locs...])) + push!( + inst, + Gate( + name = "c"^(length(controls)) * $NAME, + qubits = [controls..., locs...], + ), + ) else error("too many control bits!") end @@ -124,15 +157,23 @@ for (GT, NAME, MAXC) in [(:XGate, "x", 2), (:YGate, "y", 2), (:ZGate, "z", 2), end # rotation gates -for (GT, NAME, PARAMS, MAXC) in [(:(RotationGate{1, T, XGate} where T), "u3", :([b.theta, -π/2, π/2]), 0), - (:(RotationGate{1, T, YGate} where T), "u3", :([b.theta, 0, 0]), 0), - (:(RotationGate{1, T, ZGate} where T), "u1", :([b.theta]), 0), - (:(ShiftGate), "u1", :([b.theta]), 1), - (:(HGate), "u2", :([0, π]), 0), - ] +for (GT, NAME, PARAMS, MAXC) in [ + (:(RotationGate{1,T,XGate} where {T}), "u3", :([b.theta, -π / 2, π / 2]), 0), + (:(RotationGate{1,T,YGate} where {T}), "u3", :([b.theta, 0, 0]), 0), + (:(RotationGate{1,T,ZGate} where {T}), "u1", :([b.theta]), 0), + (:(ShiftGate), "u1", :([b.theta]), 1), + (:(HGate), "u2", :([0, π]), 0), +] @eval function generate_inst!(inst, b::$GT, locs, controls) if length(controls) <= $MAXC - push!(inst, Gate(name = "c"^(length(controls))*$NAME, qubits = [controls..., locs...], params = $PARAMS)) + push!( + inst, + Gate( + name = "c"^(length(controls)) * $NAME, + qubits = [controls..., locs...], + params = $PARAMS, + ), + ) else error("too many control bits! got $controls (length > $($(MAXC)))") end @@ -142,5 +183,5 @@ end sublocs(subs, locs) = [locs[i] for i in subs] function basicstyle(blk::AbstractBlock) - YaoBlocks.Optimise.simplify(blk, rules=[YaoBlocks.Optimise.to_basictypes]) + YaoBlocks.Optimise.simplify(blk, rules = [YaoBlocks.Optimise.to_basictypes]) end diff --git a/test/qobjtoqbir.jl b/test/qobjtoqbir.jl deleted file mode 100644 index cece470..0000000 --- a/test/qobjtoqbir.jl +++ /dev/null @@ -1,121 +0,0 @@ -mutable struct U1{T<:Number} <: PrimitiveBlock{1} - lambda::T -end - -# 2-parameter 1-pulse single qubit gate -mutable struct U2{T<:Number} <: PrimitiveBlock{1} - phi::T - lambda::T -end - -# 3-parameter 2-pulse single qubit gate -mutable struct U3{T<:Number} <: PrimitiveBlock{1} - theta::T - phi::T - lambda::T -end - -Base.:(==)(u1_1::U1, u1_2::U1) = u1_1.lambda == u1_2.lambda -Base.:(==)(u2_1::U2, u2_2::U2) = u2_1.phi == u2_2.phi && u2_1.lambda == u2_2.lambda -Base.:(==)(u3_1::U3, u3_2::U3) = u3_1.theta == u3_2.theta && u3_1.phi == u3_2.phi && u3_1.lambda == u3_2.lambda - -function Yao.mat(::Type{T}, u1::U1) where T - λ = u1.lambda - - T[1 0; - 0 exp(im*λ)] -end - -function Yao.mat(::Type{T}, u2::U2) where T - ϕ, λ = u2.phi, u2.lambda - - T[1/√2 (-exp(im*λ))/√2; - (exp(im*ϕ))/√2 (exp(im*(ϕ+λ)))/√2] -end - -function Yao.mat(::Type{T}, u3::U3) where T - θ, ϕ, λ = u3.theta, u3.phi, u3.lambda - - T[cos(θ/2) -sin(θ/2)*exp(im*λ) ; - sin(θ/2)*exp(im*ϕ) cos(θ/2)*exp(im*(ϕ+λ))] -end - -Yao.iparams_eltype(::U1{T}) where T = T -Yao.iparams_eltype(::U2{T}) where T = T -Yao.iparams_eltype(::U3{T}) where T = T - -Yao.getiparams(u1::U1{T}) where T = (u1.lambda) -Yao.getiparams(u2::U2{T}) where T = (u2.phi, u2.lambda) -Yao.getiparams(u3::U3{T}) where T = (u3.theta, u3.phi, u3.lambda) - -function Yao.setiparams!(u1::U1{T}, λ) where T - u1.lambda = λ - return u1 -end - -function Yao.setiparams!(u2::U2{T}, ϕ, λ) where T - u2.phi = ϕ - u2.lambda = λ - return u2 -end - -function Yao.setiparams!(u3::U3{T}, θ, ϕ, λ) where T - u3.theta = θ - u3.phi = ϕ - u3.lambda = λ - return u3 -end - -YaoBlocks.@dumpload_fallback U1 U1 -YaoBlocks.@dumpload_fallback U2 U2 -YaoBlocks.@dumpload_fallback U3 U3 - -function inst2qbir(inst) - n = maximum(x->maximum(x.qubits), inst) + 1 - chain(n, map(inst) do x - name, locs = x.name, x.qubits .+ 1 - nc = 0 - while name[nc+1] == 'c' && nc 0 - control(n, locs[1:nc], locs[nc+1:end]=>name_index(name[nc+1:end], x.params)) - else - put(n, locs=>name_index(name, x.params)) - end - end) -end - -function name_index(name, params=nothing) - if name == "u1" - U1(params...) - elseif name == "u2" - if isapprox(params, [0, π]) - H - else - U2(params...) - end - elseif name == "u3" - if isapprox(params[2], -π/2) && isapprox(params[3], π/2) - Rx(params[1]) - elseif params[2] == 0 && params[3] == 0 - Ry(params[1]) - else - U3(params...) - end - elseif name == "id" - I2 - elseif name == "x" - X - elseif name == "y" - Y - elseif name == "z" - Z - elseif name == "t" - T - elseif name == "swap" - SWAP - else - error("gate type `$name` not defined!") - end -end diff --git a/test/runtests.jl b/test/runtests.jl index a2373fa..ccd38ee 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,30 +1,87 @@ -using Yao, YaoBlocksQobj +using IBMQClient, Yao, YaoBlocksQobj using Test -include("qobjtoqbir.jl") - @testset "YaoBlocksQobj.jl" begin - qc = chain(3, put(1=>X), put(2=>Y) ,put(3=>Z), - put(2=>T), swap(1,2), put(3=>Ry(0.7)), - control(2, 1=>Y), control(3, 2=>Z)) - qc1 = chain(3, put(3=>Rx(0.7)), - control(2, 1=>Y), control(3, 2=>Z)) - - header = Dict("description"=>"test circuits") - exp_header = [Dict("description"=>"1"), Dict("description"=>"2")] - + qc = chain( + 3, + put(1 => X), + put(2 => Y), + put(3 => Z), + put(2 => T), + put(2 => I2), + swap(1, 2), + put(3 => Ry(0.7)), + control(2, 1 => Y), + control(3, 2 => Z), + ) + qc1 = chain( + 3, + put(1 => H), + put(3 => Rx(0.7)), + control(2, 1 => Y), + control(3, 2 => Z), + put(1 => Yao.Measure(1)), + ) + + header = Dict("description" => "test circuits") + exp_header = [Dict("description" => "1"), Dict("description" => "2")] circuits = [qc, qc1] - q = create_qobj(circuits, id = "test", header = header, exp_header = exp_header) - - experiments = q.experiments - for i in 1:length(experiments) - exp = experiments[i] - @test exp isa YaoBlocksQobj.Schema.Experiment - @test exp.header == Dict("description" => "$i") - @test exp.config === nothing - inst = exp.instructions - ir = inst2qbir(inst) - @test ir == circuits[i] + + q = convert_to_qobj(circuits, id = "test", header = header, exp_header = exp_header) + q1 = convert_to_qobj(circuits) + q2 = convert_to_qobj(circuits, exp_header = exp_header) + + set = [q, q1, q2] + for i in set + experiments = i.experiments + for i = 1:length(experiments) + exp = experiments[i] + @test exp isa YaoBlocksQobj.Schema.Experiment + @test exp.header == Dict("description" => "$i") || exp.header === nothing + @test exp.config === nothing + inst = exp.instructions + ir = convert_to_qbir(inst) + @test ir == circuits[i] + end + + qc_inst = chain( + 1, + put(1 => YaoBlocksQobj.U1{Float64}(2)), + put(1 => YaoBlocksQobj.U2{Float64}(1, 0.7)), + put(1 => YaoBlocksQobj.U3{Float64}(0, 1, 0.7)), + ) + + inst = [ + IBMQClient.Schema.Gate("u1", [0], [2], nothing, nothing), + IBMQClient.Schema.Gate("u2", [0], [1, 0.7], nothing, nothing), + IBMQClient.Schema.Gate("u3", [0], [0, 1, 0.7], nothing, nothing), + ] + + @test qc_inst == convert_to_qbir(inst) end + @testset "qbir misc." begin + @test YaoBlocksQobj.mat(Number, YaoBlocksQobj.U1(0.7)) ≈ Number[ + 1 0 + 0 exp(im * 0.7) + ] + + @test YaoBlocksQobj.mat(Number, YaoBlocksQobj.U2(0.7, 0.5)) ≈ Number[ + 1/√2 (-exp(im * 0.5))/√2 + (exp(im * 0.7))/√2 (exp(im * (1.2)))/√2 + ] + + @test YaoBlocksQobj.mat(Number, YaoBlocksQobj.U3(0.7, 0.5, 0.2)) ≈ Number[ + cos(0.7 / 2) -sin(0.7 / 2)*exp(im * 0.2) + sin(0.7 / 2)*exp(im * 0.5) cos(0.7 / 2)*exp(im * (0.7)) + ] + + @test YaoBlocksQobj.iparams_eltype(YaoBlocksQobj.U1(0.7)) == Float64 + @test YaoBlocksQobj.iparams_eltype(YaoBlocksQobj.U2(0.7, 0.5)) == Float64 + @test YaoBlocksQobj.iparams_eltype(YaoBlocksQobj.U3(0.7, 0.5, 0.2)) == Float64 + + @test YaoBlocksQobj.getiparams(YaoBlocksQobj.U1(0.7)) == (0.7) + @test YaoBlocksQobj.getiparams(YaoBlocksQobj.U2(0.7, 0.5)) == (0.7, 0.5) + @test YaoBlocksQobj.getiparams(YaoBlocksQobj.U3(0.7, 0.5, 0.2)) == (0.7, 0.5, 0.2) + end end