Skip to content

Commit

Permalink
Merge pull request #18 from Sov-trotter/better-recursive-parse
Browse files Browse the repository at this point in the history
Better recursive parse
  • Loading branch information
Sov-trotter authored Oct 26, 2020
2 parents 5e5f5c1 + f78eb1c commit 77e1549
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 76 deletions.
6 changes: 3 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ version = "0.1.0"
[deps]
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
SimpleMock = "a896ed2c-15a5-4479-b61d-a0e88e2a1d25"
Yao = "5872b779-8223-5990-8dd0-5abbb0748c8c"
YaoBlocks = "418bc28f-b43b-5e0b-a6e7-61bbc1a2c1df"
YaoExtensions = "7a06699c-c960-11e9-3c98-9f78548b5f0f"

[compat]
HTTP = "0.8"
Expand All @@ -19,7 +17,9 @@ YaoBlocks = "0.11"
julia = "1"

[extras]
SimpleMock = "a896ed2c-15a5-4479-b61d-a0e88e2a1d25"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
YaoExtensions = "7a06699c-c960-11e9-3c98-9f78548b5f0f"

[targets]
test = ["Test"]
test = ["Test", "SimpleMock", "YaoExtensions"]
6 changes: 3 additions & 3 deletions src/extensions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ export U1, U2, U3
# Single qubit unitary gates
# https://github.com/Qiskit/openqasm/blob/master/examples/generic/qelib1.inc
# 1-parameter 0-pulse single qubit gate
mutable struct U1{T<:Number} <: PrimitiveBlock{2}
mutable struct U1{T<:Number} <: PrimitiveBlock{1}
lambda::T
end

# 2-parameter 1-pulse single qubit gate
mutable struct U2{T<:Number} <: PrimitiveBlock{2}
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{3}
mutable struct U3{T<:Number} <: PrimitiveBlock{1}
theta::T
phi::T
lambda::T
Expand Down
151 changes: 92 additions & 59 deletions src/qobj.jl
Original file line number Diff line number Diff line change
@@ -1,90 +1,123 @@
#todo variational gate correction stuff :Done
# add methods for h and rz conversions
# cleanup multiple dispatch
function yaotoqobj(qc::Array{ChainBlock{N}}, device::String; nshots=1024) where N
nslots = 1
export generate_inst, inst2qbir

function yaotoqobj(qc::Array{<:AbstractBlock{N}}, device::String; nshots=1024, nslots=1) where N
main_header = Dict("description"=>"Set of Experiments 1", "backend_name" => "$(device)")
main_config = Dict("shots"=>nshots, "memory_slots"=>nslots, "init_qubits"=> true)
experiments = collect(generate_experiment(i) for i in qc)
data = Dict("qobj_id" => "foo", "schema_version"=>"1.0.0", "type"=>"QASM", "header"=>main_header, "config"=>main_config, "experiments"=>experiments)
Qobj(data)
end

function generate_experiment(qc::ChainBlock)
qc_simpl = basicstyle(qc)
n_qubits = nqubits(qc_simpl)
n_classical_reg = 2
nslots=1
function generate_experiment(qc::AbstractBlock{N}, n_classical_reg=2, nslots=1) where N
c_label = [["c", i] for i in 0:n_classical_reg-1]
q_label = [["q", i] for i in 0:n_qubits-1]
exp_inst = generate_inst(qc_simpl)
exp_header = Dict("memory_slots"=>nslots, "n_qubits"=>n_qubits, "clbit_labels"=>c_label, "qubit_labels"=>q_label)
q_label = [["q", i] for i in 0:N-1]
exp_inst = generate_inst(qc)
exp_header = Dict("memory_slots"=>nslots, "n_qubits"=>N, "clbit_labels"=>c_label, "qubit_labels"=>q_label)
experiment = Dict("header"=>exp_header, "config"=>Dict(), "instructions"=>exp_inst)
return experiment
end

function generate_inst(qc_simpl::ChainBlock)
function generate_inst(qc_simpl::AbstractBlock{N}) where N
inst = []
generate_inst!(inst, basicstyle(qc_simpl), [0:N-1...], Int[])
return inst
end

function generate_inst!(inst, qc_simpl::ChainBlock, locs, controls)
for block in subblocks(qc_simpl)
i = generate_inst(block)
if block isa ChainBlock
if i isa Array{Array} # for nested chains
push!(inst, Iterators.flatten(i)...) #generalize this to loop till we get a Array{Dict}
else
append!(inst, i)
end
else
if i isa Array # for nested chains
append!(inst, i)
else
push!(inst, i)
end
end
generate_inst!(inst, block, locs, controls)
end
return inst
end

function generate_inst(blk::PutBlock{N,M}) where {N,M}
locs = [blk.locs...]
generate_inst(blk.content, locs)
function generate_inst!(inst, blk::PutBlock{N,M}, locs, controls) where {N,M}
generate_inst!(inst, blk.content, sublocs(blk.locs, locs), controls)
end

function generate_inst(blk::ControlBlock{N,GT,C}) where {N,GT,C}
findfirst(!=(0),blk.ctrl_config) == nothing && error("Inverse Control used in Control gate context")
generate_inst(blk.content, blk.ctrl_locs, blk.locs)
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)...])
end

function generate_inst(blk::ChainBlock, locs::Array)
ins = []
for sub_blk in subblocks(blk)
push!(ins, generate_inst(sub_blk, locs))
end
return ins
function generate_inst!(inst, m::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")
(m.postprocess isa NoPostProcess) || error("postprocessing is not supported")
(length(controls) == 0) || error("controlled measure is not supported")
push!(inst, Dict("name"=>"measure", "qubits"=>mlocs, "memory"=>zeros(length(mlocs))))
end

# IBMQ Chip only supports ["id", "u1", "u2", "u3", "cx"]
# Conversions implemented for H, RX, RY, RZ
generate_inst(::HGate, locs) = Dict("name"=>"u2", "qubits"=>locs, "params"=>[0, π])
generate_inst(::I2Gate, locs) = Dict("name"=>"id", "qubits"=>locs)
generate_inst(::TGate, locs) = Dict("name"=>"t", "qubits"=>locs)
generate_inst(::SWAPGate, locs) = Dict("name"=>"swap", "qubits"=>locs)
generate_inst(::Measure, locs) = Dict("name"=>"measure", "qubits"=>locs, "memory"=>[0])# memory: List of memory slots in which to store the measurement results (mustbe the same length as qubits).

generate_inst(b::ShiftGate, ctrl_locs, locs) = Dict("name"=>"cu1", "qubits"=>[locs..., ctrl_locs...], "params"=>[b.theta])

generate_inst(::XGate, locs::Array) = Dict("name"=>"x", "qubits"=>locs)
generate_inst(::YGate, locs::Array) = Dict("name"=>"y", "qubits"=>locs)
generate_inst(::ZGate, locs::Array) = Dict("name"=>"z", "qubits"=>locs)
# 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)]
@eval function generate_inst!(inst, ::$GT, locs, controls)
if length(controls) <= $MAXC
push!(inst, Dict("name"=>"c"^(length(controls))*$NAME, "qubits"=>[controls..., locs...]))
else
error("too many control bits!")
end
end
end

generate_inst(::XGate, ctrl_locs::Tuple, locs::Tuple) = Dict("name"=>"cx", "qubits"=>[ctrl_locs..., locs...])
generate_inst(::YGate, ctrl_locs::Tuple, locs::Tuple) = Dict("name"=>"cy", "qubits"=>[ctrl_locs..., locs...])
generate_inst(::ZGate, ctrl_locs::Tuple, locs::Tuple) = Dict("name"=>"cz", "qubits"=>[ctrl_locs..., locs...])
# 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),
]
@eval function generate_inst!(inst, b::$GT, locs, controls)
if length(controls) <= $MAXC
push!(inst, Dict("name"=>"c"^(length(controls))*$NAME, "qubits"=>[controls..., locs...], "params"=>$PARAMS))
else
error("too many control bits! got $controls (length > $($(MAXC)))")
end
end
end

generate_inst(b::RotationGate{1, T, XGate}, locs) where T = Dict("name"=>"u3", "qubits"=>locs, "params"=>[b.theta, -π/2, π/2])
generate_inst(b::RotationGate{1, T, YGate}, locs) where T = Dict("name"=>"u2", "qubits"=>locs, "params"=>[b.theta, 0, 0])
generate_inst(b::RotationGate{1, T, ZGate}, locs) where T = Dict("name"=>"u1", "qubits"=>locs, "params"=>[b.theta])
sublocs(subs, locs) = [locs[i] for i in subs]

function basicstyle(blk::AbstractBlock)
YaoBlocks.Optimise.simplify(blk, rules=[YaoBlocks.Optimise.to_basictypes])
end

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<length(name)
nc += 1
end
if nc > 0
control(n, locs[1:nc], locs[nc+1:end]=>name_index(name[nc+1:end], get(x, "params", nothing)))
else
put(n, locs=>name_index(name, get(x, "params", nothing)))
end
end)
end

function name_index(name, params=nothing)
if name == "u1"
U1(params...)
elseif name == "u2"
U2(params...)
elseif name == "u3"
U3(params...)
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
20 changes: 19 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ using Test
# todo add tests wrt standard circuits(qft etc) to confirm that qobj is correct
# verify the results
qc = chain(1, put(1=>I2))
test_token = "4b108e35df658648486a3a66c3ccf1e66cd3005c97ab2016c9b1ceeec64802c7052325952dd02a01b73ca6e28613c36b7e87c0a854ebae1dc434a07fcfcc7c7b"
test_token = "8e87a83bbe4f5ad0aa953094fb8df853b07b2a86dadf010261eef2a65cd524df29e5bc38bbe2f8064155226bdfb6dcaecc8e3a6a029e402c8c7389ec0cef3574"
user = authenticate(test_token)

mock(readline => Mock(() -> "1")) do _
Expand All @@ -27,4 +27,22 @@ end
@testset "Qobj" begin
include("testqobj.jl")
end

@testset "Single Quibit Unitary Gates" begin
@test isunitary(U1(0.5))
@test isunitary(U2(0.5, 0.6))
@test isunitary(U3(0.5, 0.6, 0.4))
u1 = U1/2)
u2 = U2/2, π/6)
u3 = U3/2, π/6, π/4)

for fs in [u1, u2, u3]
@test eval(YaoBlocks.parse_ex(dump_gate(fs), 1)) == fs
@test Yao.iparams_eltype(fs) == Float64
end
@test Yao.getiparams(u1) ==/2)
@test Yao.getiparams(u2) ==/2, π/6)
@test Yao.getiparams(u3) ==/2, π/6, π/4)

end
end
2 changes: 1 addition & 1 deletion test/testapi.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ if stat == "COMPLETED"
@test res[1]["status"] == "DONE"
@test res[1]["success"] == true
end
end
end
46 changes: 37 additions & 9 deletions test/testqobj.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# https://tutorials.yaoquantum.org/dev/
using YaoExtensions
using YaoExtensions, IBMQJulia, Yao, Test

@testset "QFT" begin
for i in 1:10
for i in [2, 4, 5, 7, 8]
q = qft_circuit(i)
qobj = IBMQJulia.yaotoqobj([q], "foo_device")
exp_1 = qobj.data["experiments"]
Expand All @@ -14,11 +14,12 @@ using YaoExtensions
@test length(ins) == i*(i+1)/2
for j in ins
@test j isa Dict{String, Any}
# test individual blocks here
# use the YaoBlocks.gate count method for verification/a method synonymous to it can be added in this package also

end
end
c = qft_circuit(4)
inst = generate_inst(c)
c2 = inst |> inst2qbir
@test operator_fidelity(c, c2) 1
end

@testset "Quantum Circuit Born Machine" begin
Expand Down Expand Up @@ -50,12 +51,15 @@ end
@test ins isa Array{Any,1}
for j in ins
@test j isa Dict{String,Any}
# test individual blocks here
end
c = build_circuit(4, 1, [1=>2, 2=>3, 3=>4])
inst = generate_inst(c)
c2 = inst |> inst2qbir
@test operator_fidelity(c, c2) 1
end

@testset "Variational Quantum Eigen Solver" begin
for n in 3:10
for n in [3, 6, 7, 8, 9]
circuit = dispatch!(variational_circuit(n, n+1),:random)
qobj = IBMQJulia.yaotoqobj([circuit], "foo_device")
exp_1 = qobj.data["experiments"]
Expand All @@ -66,9 +70,33 @@ end
@test ins isa Array{Any,1}
for j in ins
@test j isa Dict{String,Any}
# test individual blocks here
end
# gatecount(circuit)
end
c = dispatch!(variational_circuit(4, 5), :random)
inst = generate_inst(c)
c2 = inst |> inst2qbir
@test operator_fidelity(c, c2) 1
end

@testset "Misc" 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))
qobj = IBMQJulia.yaotoqobj([qc], "foo_device")
exp_1 = qobj.data["experiments"]
ins = exp_1[1]["instructions"]
@test qobj isa IBMQJulia.Qobj
@test qobj.data isa Dict{String,Any}
@test exp_1 isa Array{Dict{String,Any},1}
@test ins isa Array{Any,1}
for j in ins
@test j isa Dict{String,Any}
end

c = 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))
inst = generate_inst(c)
c2 = inst |> inst2qbir
@test operator_fidelity(c, c2) 1
end

0 comments on commit 77e1549

Please sign in to comment.