-
Notifications
You must be signed in to change notification settings - Fork 4
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
fix: Improvements to precompilation and adjustments to new Quasar api #61
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,9 +78,11 @@ include("noise_kernels.jl") | |
|
||
function __init__() | ||
Quasar.builtin_gates[] = builtin_gates | ||
Quasar.parse_pragma[] = parse_pragma | ||
Quasar.visit_pragma[] = visit_pragma | ||
end | ||
|
||
const LOG2_CHUNK_SIZE = 10 | ||
const LOG2_CHUNK_SIZE = 16 | ||
const CHUNK_SIZE = 2^LOG2_CHUNK_SIZE | ||
|
||
function _index_to_endian_bits(ix::Int, qubit_count::Int) | ||
|
@@ -203,7 +205,7 @@ basis rotation instructions if running with non-zero shots. Return the `Program` | |
parsing and the qubit count of the circuit. | ||
""" | ||
function _prepare_program(circuit_ir::OpenQasmProgram, inputs::Dict{String, <:Any}, shots::Int) | ||
ir_inputs = isnothing(circuit_ir.inputs) ? Dict{String, Float64}() : circuit_ir.inputs | ||
ir_inputs = isnothing(circuit_ir.inputs) ? Dict{String, Float64}() : circuit_ir.inputs | ||
merged_inputs = merge(ir_inputs, inputs) | ||
src = circuit_ir.source::String | ||
circuit = to_circuit(src, merged_inputs) | ||
|
@@ -704,11 +706,14 @@ include("dm_simulator.jl") | |
bit[3] b; | ||
qubit[3] q; | ||
rx(0.1) q[0]; | ||
rx(1) q[0]; | ||
prx(0.1, 0.2) q[0]; | ||
x q[0]; | ||
ry(0.1) q[0]; | ||
ry(1) q[0]; | ||
y q[0]; | ||
rz(0.1) q[0]; | ||
rz(1) q[0]; | ||
z q[0]; | ||
h q[0]; | ||
i q[0]; | ||
|
@@ -758,6 +763,11 @@ include("dm_simulator.jl") | |
#pragma braket result density_matrix | ||
#pragma braket result probability | ||
#pragma braket result expectation x(q[0]) | ||
#pragma braket result expectation x(q[0]) @ x(q[1]) | ||
#pragma braket result expectation z(q[0]) @ z(q[1]) | ||
#pragma braket result expectation y(q[0]) @ y(q[1]) | ||
#pragma braket result expectation h(q[0]) @ h(q[1]) | ||
#pragma braket result expectation i(q[0]) @ i(q[1]) | ||
Comment on lines
+766
to
+770
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to handle expectations of tensor products specifically? I see we're already handling tensor products for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because the tensor product type is parametrized by the underlying element type |
||
#pragma braket result variance x(q[0]) @ y(q[1]) | ||
""" | ||
dm_exact_results_qasm = """ | ||
|
@@ -772,16 +782,22 @@ include("dm_simulator.jl") | |
""" | ||
shots_results_qasm = """ | ||
OPENQASM 3.0; | ||
qubit[2] q; | ||
qubit[10] q; | ||
h q; | ||
#pragma braket result probability | ||
#pragma braket result expectation x(q[0]) | ||
#pragma braket result variance x(q[0]) @ y(q[1]) | ||
#pragma braket result sample x(q[0]) @ y(q[1]) | ||
#pragma braket result expectation z(q[2]) @ z(q[3]) | ||
#pragma braket result expectation x(q[4]) @ x(q[5]) | ||
#pragma braket result expectation y(q[6]) @ y(q[7]) | ||
#pragma braket result expectation h(q[8]) @ h(q[9]) | ||
""" | ||
@compile_workload begin | ||
using BraketSimulator, Quasar | ||
Quasar.builtin_gates[] = BraketSimulator.builtin_gates | ||
Quasar.parse_pragma[] = BraketSimulator.parse_pragma | ||
Quasar.visit_pragma[] = BraketSimulator.visit_pragma | ||
simulator = StateVectorSimulator(5, 0) | ||
oq3_program = OpenQasmProgram(braketSchemaHeader("braket.ir.openqasm.program", "1"), custom_qasm, nothing) | ||
simulate(simulator, oq3_program, 100) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -96,13 +96,14 @@ function matrix_rep(g::PRx) | |
end | ||
|
||
for G in (:Rx, :Ry, :Rz, :PhaseShift) | ||
@eval function matrix_rep(g::$G) | ||
@eval function matrix_rep(g::$G)::SMatrix{2,2,ComplexF64} | ||
n = g.pow_exponent::Float64 | ||
θ = @inbounds g.angle[1] | ||
iszero(n) && return matrix_rep_raw(I())::SMatrix{2,2,ComplexF64} | ||
isone(n) && return matrix_rep_raw(g, θ)::SMatrix{2,2,ComplexF64} | ||
isinteger(n) && return matrix_rep_raw(g, θ*n)::SMatrix{2,2,ComplexF64} | ||
return SMatrix{2,2,ComplexF64}(matrix_rep_raw(g, θ) ^ n) | ||
one_mat = matrix_rep_raw(g, θ) | ||
iszero(n) && return matrix_rep_raw(I()) | ||
isone(n) && return one_mat | ||
isinteger(n) && return matrix_rep_raw(g, θ*n) | ||
return SMatrix{2,2,ComplexF64}(one_mat ^ n) | ||
end | ||
end | ||
|
||
|
@@ -211,9 +212,9 @@ matrix_rep_raw(g::PRx) = SMatrix{2,2}( | |
-im*exp(im*g.angle[2])*sin(g.angle[1]/2.0) cos(g.angle[1] / 2.0) | ||
], | ||
) | ||
matrix_rep_raw(g::Rz, ϕ) = (θ = ϕ/2.0; return SMatrix{2,2}(exp(-im*θ), 0.0, 0.0, exp(im*θ))) | ||
matrix_rep_raw(g::Rx, ϕ) = ((sθ, cθ) = sincos(ϕ/2.0); return SMatrix{2,2}(cθ, -im*sθ, -im*sθ, cθ)) | ||
matrix_rep_raw(g::Ry, ϕ) = ((sθ, cθ) = sincos(ϕ/2.0); return SMatrix{2,2}(complex(cθ), complex(sθ), -complex(sθ), complex(cθ))) | ||
matrix_rep_raw(g::Rz, ϕ)::SMatrix{2,2,ComplexF64} = ((sθ, cθ) = sincos(ϕ/2.0); return SMatrix{2,2}(cθ - im*sθ, 0.0, 0.0, cθ + im*sθ)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why switch to the trig expression? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mostly for consistency with the above methods and also |
||
matrix_rep_raw(g::Rx, ϕ)::SMatrix{2,2,ComplexF64} = ((sθ, cθ) = sincos(ϕ/2.0); return SMatrix{2,2}(cθ, -im*sθ, -im*sθ, cθ)) | ||
matrix_rep_raw(g::Ry, ϕ)::SMatrix{2,2,ComplexF64} = ((sθ, cθ) = sincos(ϕ/2.0); return SMatrix{2,2}(complex(cθ), complex(sθ), -complex(sθ), complex(cθ))) | ||
matrix_rep_raw(g::GPi) = | ||
SMatrix{2,2}(complex([0 exp(-im * g.angle[1]); exp(im * g.angle[1]) 0])) | ||
|
||
|
@@ -311,14 +312,11 @@ function apply_gate!( | |
g_00, g_10, g_01, g_11 = g_matrix | ||
Threads.@threads for chunk_index = 0:n_chunks-1 | ||
# my_amps is the group of amplitude generators which this `Task` will process | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need new explanation here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep will add in a future "lots o' explanatory comments" PR |
||
my_amps = if n_chunks > 1 | ||
chunk_index*CHUNK_SIZE:((chunk_index+1)*CHUNK_SIZE-1) | ||
else | ||
0:n_tasks-1 | ||
end | ||
lower_ix = pad_bit(my_amps[1], endian_qubit) + 1 | ||
first_amp = n_chunks > 1 ? chunk_index*CHUNK_SIZE : 0 | ||
amp_block = n_chunks > 1 ? CHUNK_SIZE : n_tasks | ||
lower_ix = pad_bit(first_amp, endian_qubit) + 1 | ||
higher_ix = lower_ix + flipper | ||
for task_amp = 0:length(my_amps)-1 | ||
for task_amp = 0:amp_block-1 | ||
if is_small_target && div(task_amp, flipper) > 0 && mod(task_amp, flipper) == 0 | ||
lower_ix = higher_ix | ||
higher_ix = lower_ix + flipper | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,4 @@ | ||
Quasar.qubit_count(o::String) = length(o) | ||
Quasar.qubit_count(o::Matrix) = Int(log2(size(o, 1))) | ||
qubit_count(o::String) = length(o) | ||
|
||
function _observable_targets_error(observable::Matrix{ComplexF64}, targets) | ||
mat = Vector{Vector{Vector{Float64}}}(undef, size(observable, 1)) | ||
|
@@ -14,7 +13,7 @@ end | |
_observable_targets_error(::String, targets) = throw(Quasar.QasmVisitorError("Standard observable target must be exactly 1 qubit.", "ValueError")) | ||
|
||
function _check_observable_targets(observable::Union{Matrix{ComplexF64}, String}, targets) | ||
qc = Quasar.qubit_count(observable) | ||
qc = qubit_count(observable) | ||
qc == 1 && (isempty(targets) || length(targets) == 1) && return | ||
qc == length(targets) && return | ||
_observable_targets_error(observable, targets) | ||
|
@@ -42,7 +41,7 @@ function visit_observable(v, expr) | |
end | ||
end | ||
|
||
function Quasar.visit_pragma(v, program_expr) | ||
function visit_pragma(v, program_expr) | ||
pragma_type::Symbol = program_expr.args[1] | ||
if pragma_type == :result | ||
result_type = program_expr.args[2] | ||
|
@@ -95,12 +94,12 @@ function Quasar.visit_pragma(v, program_expr) | |
end | ||
|
||
function parse_matrix(tokens::Vector{Tuple{Int64, Int32, Quasar.Token}}, stack, start, qasm) | ||
inner = Quasar.extract_braced_block(tokens, stack, start, qasm) | ||
inner = Quasar.extract_expression(tokens, Quasar.lbracket, Quasar.rbracket, stack, start, qasm) | ||
n_rows = count(triplet->triplet[end] == Quasar.lbracket, inner) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why the switch from the more specific to the more general function? Are the delimiter-specific methods going away in the new Quasar.jl? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because |
||
matrix = Matrix{Quasar.QasmExpression}(undef, n_rows, n_rows) | ||
row = 1 | ||
while !isempty(inner) | ||
row_tokens = Quasar.extract_braced_block(inner, stack, start, qasm) | ||
row_tokens = Quasar.extract_expression(inner, Quasar.lbracket, Quasar.rbracket, stack, start, qasm) | ||
push!(row_tokens, (-1, Int32(-1), Quasar.semicolon)) | ||
col = 1 | ||
while !isempty(row_tokens) | ||
|
@@ -126,7 +125,7 @@ function parse_pragma_observables(tokens::Vector{Tuple{Int64, Int32, Quasar.Toke | |
observable_token = popfirst!(tokens) | ||
observable_id = Quasar.parse_identifier(observable_token, qasm) | ||
if observable_id.args[1] == "hermitian" | ||
matrix_tokens = Quasar.extract_parensed(tokens, stack, start, qasm) | ||
matrix_tokens = Quasar.extract_expression(tokens, Quasar.lparen, Quasar.rparen, stack, start, qasm) | ||
# next token is targets | ||
h_mat = parse_matrix(matrix_tokens, stack, start, qasm) | ||
# next token is targets | ||
|
@@ -146,7 +145,7 @@ function parse_pragma_observables(tokens::Vector{Tuple{Int64, Int32, Quasar.Toke | |
break | ||
else | ||
if !isempty(tokens) && first(tokens)[end] == Quasar.lparen | ||
arg_tokens = Quasar.extract_parensed(tokens, stack, start, qasm) | ||
arg_tokens = Quasar.extract_expression(tokens, Quasar.lparen, Quasar.rparen, stack, start, qasm) | ||
push!(arg_tokens, (-1, Int32(-1), Quasar.semicolon)) | ||
target_expr = Quasar.parse_expression(arg_tokens, stack, start, qasm) | ||
push!(obs_targets, target_expr) | ||
|
@@ -175,7 +174,7 @@ function parse_pragma_targets(tokens::Vector{Tuple{Int64, Int32, Quasar.Token}}, | |
end | ||
|
||
|
||
function Quasar.parse_pragma(tokens, stack, start, qasm) | ||
function parse_pragma(tokens, stack, start, qasm) | ||
prefix = popfirst!(tokens) | ||
prefix_id = Quasar.parse_identifier(prefix, qasm) | ||
prefix_id.args[1] == "braket" || throw(Quasar.QasmParseError("pragma expression must begin with `#pragma braket`", stack, start, qasm)) | ||
|
@@ -204,7 +203,7 @@ function Quasar.parse_pragma(tokens, stack, start, qasm) | |
end | ||
elseif pragma_type == "unitary" | ||
push!(expr, :unitary) | ||
matrix_tokens = Quasar.extract_parensed(tokens, stack, start, qasm) | ||
matrix_tokens = Quasar.extract_expression(tokens, Quasar.lparen, Quasar.rparen, stack, start, qasm) | ||
unitary_matrix = parse_matrix(matrix_tokens, stack, start, qasm) | ||
push!(expr, unitary_matrix) | ||
target_expr = parse_pragma_targets(tokens, stack, start, qasm) | ||
|
@@ -213,8 +212,8 @@ function Quasar.parse_pragma(tokens, stack, start, qasm) | |
push!(expr, :noise) | ||
noise_type = Quasar.parse_identifier(popfirst!(tokens), qasm)::Quasar.QasmExpression | ||
if noise_type.args[1] == "kraus" | ||
matrix_tokens = Quasar.extract_parensed(tokens, stack, start, qasm) | ||
all(triplet->triplet[end] == Quasar.lbracket, matrix_tokens[1:3]) && (matrix_tokens = Quasar.extract_braced_block(matrix_tokens, stack, start, qasm)) | ||
matrix_tokens = Quasar.extract_expression(tokens, Quasar.lparen, Quasar.rparen, stack, start, qasm) | ||
all(triplet->triplet[end] == Quasar.lbracket, matrix_tokens[1:3]) && (matrix_tokens = Quasar.extract_expression(matrix_tokens, Quasar.lbracket, Quasar.rbracket, stack, start, qasm)) | ||
mats = Matrix{Quasar.QasmExpression}[] | ||
while !isempty(matrix_tokens) | ||
push!(mats, parse_matrix(matrix_tokens, stack, start, qasm)) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why add these specific values?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So that the precompilation for integer-typed argument values gets triggered