Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better handling of optional arguments #13

Merged
merged 12 commits into from
May 23, 2021
3 changes: 2 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ authors = ["Arsh Sharma <[email protected]> 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"
YaoBlocks = "418bc28f-b43b-5e0b-a6e7-61bbc1a2c1df"

[compat]
Expand Down
25 changes: 10 additions & 15 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,16 @@ indigo = DocThemeIndigo.install(YaoBlocksQobj)


makedocs(;
modules=[YaoBlocksQobj],
authors="Arsh Sharma <[email protected]> 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 <[email protected]> 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")
3 changes: 2 additions & 1 deletion src/YaoBlocksQobj.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
module YaoBlocksQobj

export create_qobj
export convert_to_qobj

using Configurations
using IBMQClient.Schema
using YaoBlocks

Expand Down
195 changes: 118 additions & 77 deletions src/qobj.jl
Original file line number Diff line number Diff line change
@@ -1,86 +1,102 @@
using Random
using Configurations, UUIDs

@option struct ExpOptions
id::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
Expand All @@ -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")
Expand All @@ -112,27 +133,47 @@ 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
end
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
Expand All @@ -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
Loading