diff --git a/docs/src/gates.md b/docs/src/gates.md index 4edc03a..b5f59e0 100644 --- a/docs/src/gates.md +++ b/docs/src/gates.md @@ -26,7 +26,7 @@ BraketSimulator.Si BraketSimulator.U BraketSimulator.Unitary BraketSimulator.PhaseShift -BraketSimulator.MultiQubitPhaseShift +BraketSimulator.GPhase BraketSimulator.PRx BraketSimulator.GPi BraketSimulator.GPi2 diff --git a/src/BraketSimulator.jl b/src/BraketSimulator.jl index d574e90..949090a 100644 --- a/src/BraketSimulator.jl +++ b/src/BraketSimulator.jl @@ -60,20 +60,21 @@ complex_matrix_to_ir(m) = m include("raw_schema.jl") include("qubit_set.jl") include("operators.jl") -include("gates.jl") -include("noises.jl") -include("schemas.jl") include("observables.jl") using .Observables include("results.jl") +include("Quasar.jl") +using .Quasar +include("pragmas.jl") +include("gates.jl") +include("noises.jl") +include("schemas.jl") include("circuit.jl") include("validation.jl") include("custom_gates.jl") include("pow_gates.jl") include("gate_kernels.jl") include("noise_kernels.jl") -include("Quasar.jl") -using .Quasar const LOG2_CHUNK_SIZE = 10 const CHUNK_SIZE = 2^LOG2_CHUNK_SIZE @@ -201,7 +202,7 @@ function _prepare_program(circuit_ir::OpenQasmProgram, inputs::Dict{String, <:An ir_inputs = isnothing(circuit_ir.inputs) ? Dict{String, Float64}() : circuit_ir.inputs merged_inputs = merge(ir_inputs, inputs) src = circuit_ir.source::String - circuit = Quasar.to_circuit(src, merged_inputs) + circuit = to_circuit(src, merged_inputs) n_qubits = qubit_count(circuit) if shots > 0 _verify_openqasm_shots_observables(circuit, n_qubits) @@ -820,8 +821,8 @@ include("dm_simulator.jl") sv_oq3_program = OpenQasmProgram(braketSchemaHeader("braket.ir.openqasm.program", "1"), sv_exact_results_qasm, nothing) dm_oq3_program = OpenQasmProgram(braketSchemaHeader("braket.ir.openqasm.program", "1"), dm_exact_results_qasm, nothing) simulate(sv_simulator, sv_oq3_program, 0) - simulate("braket_sv_v2", sv_exact_results_qasm, "{}", 0) simulate(dm_simulator, dm_oq3_program, 0) + simulate("braket_sv_v2", sv_exact_results_qasm, "{}", 0) simulate("braket_dm_v2", dm_exact_results_qasm, "{}", 0) oq3_program = OpenQasmProgram(braketSchemaHeader("braket.ir.openqasm.program", "1"), shots_results_qasm, nothing) simulate(sv_simulator, oq3_program, 10) diff --git a/src/Quasar.jl b/src/Quasar.jl index 11493f5..8019dbe 100644 --- a/src/Quasar.jl +++ b/src/Quasar.jl @@ -1,11 +1,9 @@ module Quasar -using ..BraketSimulator using Automa, AbstractTrees, DataStructures, Dates using DataStructures: Stack -using BraketSimulator: Control, Instruction, Result, bind_value!, remap, qubit_count, Circuit -export parse_qasm, QasmProgramVisitor, Circuit +export parse_qasm, QasmProgramVisitor struct QasmParseError <: Exception message::String @@ -27,8 +25,6 @@ const first_letter = re"[A-Za-z_]" | unicode const general_letter = first_letter | re"[0-9]" const prefloat = re"[-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)" - - const integer = re"[-+]?[0-9]+" const float = prefloat | ((prefloat | re"[-+]?[0-9]+") * re"[eE][-+]?[0-9]+") @@ -119,6 +115,8 @@ const dt_type = Ref{DataType}() function __init__() dt_type[] = Nanosecond end +function parse_pragma end +function visit_pragma end @eval @enum Token error $(first.(qasm_tokens)...) make_tokenizer((error, @@ -539,23 +537,23 @@ function parse_list_expression(tokens::Vector{Tuple{Int64, Int32, Token}}, stack end function parse_literal(tokens::Vector{Tuple{Int64, Int32, Token}}, stack, start, qasm) - tokens[1][end] == duration_literal && return parse_duration_literal(popfirst!(tokens), qasm) - tokens[1][end] == string_token && return parse_string_literal(popfirst!(tokens), qasm) - tokens[1][end] == hex && return parse_hex_literal(popfirst!(tokens), qasm) - tokens[1][end] == oct && return parse_oct_literal(popfirst!(tokens), qasm) - tokens[1][end] == bin && return parse_bin_literal(popfirst!(tokens), qasm) - tokens[1][end] == irrational && return parse_irrational_literal(popfirst!(tokens), qasm) - tokens[1][end] == boolean && return parse_boolean_literal(popfirst!(tokens), qasm) - tokens[1][end] == integer_token && length(tokens) == 1 && return parse_integer_literal(popfirst!(tokens), qasm) - tokens[1][end] == float_token && length(tokens) == 1 && return parse_float_literal(popfirst!(tokens), qasm) + tokens[1][end] == duration_literal && return parse_duration_literal(popfirst!(tokens), qasm) + tokens[1][end] == string_token && return parse_string_literal(popfirst!(tokens), qasm) + tokens[1][end] == hex && return parse_hex_literal(popfirst!(tokens), qasm) + tokens[1][end] == oct && return parse_oct_literal(popfirst!(tokens), qasm) + tokens[1][end] == bin && return parse_bin_literal(popfirst!(tokens), qasm) + tokens[1][end] == irrational && return parse_irrational_literal(popfirst!(tokens), qasm) + tokens[1][end] == boolean && return parse_boolean_literal(popfirst!(tokens), qasm) + tokens[1][end] == integer_token && length(tokens) == 1 && return parse_integer_literal(popfirst!(tokens), qasm) + tokens[1][end] == float_token && length(tokens) == 1 && return parse_float_literal(popfirst!(tokens), qasm) - is_float = tokens[1][end] == float_token - is_complex = false - is_operator = tokens[2][end] == operator + is_float = tokens[1][end] == float_token + is_complex = false + is_operator = tokens[2][end] == operator is_plusminus = is_operator && parse_identifier(tokens[2], qasm).args[1] ∈ ("+","-") is_terminal = (tokens[2][end] == semicolon || tokens[2][end] == comma || (is_operator && !is_plusminus)) tokens[1][end] == integer_token && is_terminal && return parse_integer_literal(popfirst!(tokens), qasm) - tokens[1][end] == float_token && is_terminal && return parse_float_literal(popfirst!(tokens), qasm) + tokens[1][end] == float_token && is_terminal && return parse_float_literal(popfirst!(tokens), qasm) splice_end = 1 if tokens[2][end] == im_token is_complex = true @@ -797,61 +795,6 @@ function parse_matrix(tokens::Vector{Tuple{Int64, Int32, Token}}, stack, start, return matrix end -function parse_pragma_observables(tokens::Vector{Tuple{Int64, Int32, Token}}, stack, start, qasm) - observables_list = QasmExpression[] - obs_targets = QasmExpression[] - while !isempty(tokens) - observable_token = popfirst!(tokens) - observable_id = parse_identifier(observable_token, qasm) - if observable_id.args[1] == "hermitian" - matrix_tokens = extract_parensed(tokens, stack, start, qasm) - # next token is targets - h_mat = parse_matrix(matrix_tokens, stack, start, qasm) - # next token is targets - next_at = findfirst(triplet->triplet[end] == at, tokens) - final_token = isnothing(next_at) ? length(tokens) : next_at-1 - target_tokens = splice!(tokens, 1:final_token) - if !(isempty(target_tokens) || first(target_tokens)[end] == all_token) - push!(target_tokens, (-1, Int32(-1), semicolon)) - while !isempty(target_tokens) && first(target_tokens)[end] != semicolon - target_tokens[1][end] == comma && popfirst!(target_tokens) - target_expr = parse_expression(target_tokens, stack, target_tokens[1][1], qasm) - push!(obs_targets, target_expr) - end - end - push!(observables_list, QasmExpression(:hermitian, h_mat)) - elseif observable_id.args[1] == "all" - break - else - if !isempty(tokens) && first(tokens)[end] == lparen - arg_tokens = extract_parensed(tokens, stack, start, qasm) - push!(arg_tokens, (-1, Int32(-1), semicolon)) - target_expr = parse_expression(arg_tokens, stack, start, qasm) - push!(obs_targets, target_expr) - end - push!(observables_list, observable_id) - end - !isempty(tokens) && first(tokens)[end] == at && popfirst!(tokens) - end - if length(observables_list) == 1 && length(obs_targets) == 1 - return QasmExpression(:observable, only(observables_list)), QasmExpression(:qubit_targets, only(obs_targets)) - elseif length(observables_list) == 1 && length(obs_targets) == 0 - return QasmExpression(:observable, only(observables_list)), QasmExpression(:qubit_targets) - elseif length(observables_list) == 1 && length(obs_targets) > 1 - return QasmExpression(:observable, only(observables_list)), QasmExpression(:qubit_targets, QasmExpression(:array_literal, obs_targets...)) - else - return QasmExpression(:observable, QasmExpression(:array_literal, observables_list...)), QasmExpression(:qubit_targets, QasmExpression(:array_literal, obs_targets...)) - end -end - -function parse_pragma_targets(tokens::Vector{Tuple{Int64, Int32, Token}}, stack, start, qasm) - target_expr = QasmExpression(:qubit_targets) - (isempty(tokens) || first(tokens)[end] == all_token) && return target_expr - push!(tokens, (-1, Int32(-1), semicolon)) - push!(target_expr, parse_list_expression(tokens, stack, start, qasm)) - return target_expr -end - function parse_qasm(clean_tokens::Vector{Tuple{Int64, Int32, Token}}, qasm::String, root=QasmExpression(:program)) stack = Stack{QasmExpression}() push!(stack, root) @@ -871,67 +814,7 @@ function parse_qasm(clean_tokens::Vector{Tuple{Int64, Int32, Token}}, qasm::Stri closing = findfirst(triplet->triplet[end] == newline, clean_tokens) isnothing(closing) && throw(QasmParseError("missing final newline for #pragma", stack, start, qasm)) pragma_tokens = splice!(clean_tokens, 1:closing-1) - prefix = popfirst!(pragma_tokens) - prefix_id = parse_identifier(prefix, qasm) - prefix_id.args[1] == "braket" || throw(QasmParseError("pragma expression must begin with `#pragma braket`", stack, start, qasm)) - expr = QasmExpression(:pragma) - pragma_type = parse_identifier(popfirst!(pragma_tokens), qasm).args[1] - if pragma_type == "result" - push!(expr, :result) - result_type = parse_identifier(popfirst!(pragma_tokens), qasm).args[1] - if result_type == "state_vector" - push!(expr, :state_vector) - elseif result_type == "probability" - target_expr = parse_pragma_targets(pragma_tokens, stack, start, qasm) - push!(expr, :probability, target_expr) - elseif result_type == "density_matrix" - target_expr = parse_pragma_targets(pragma_tokens, stack, start, qasm) - push!(expr, :density_matrix, target_expr) - elseif result_type == "amplitude" - push!(pragma_tokens, (-1, Int32(-1), semicolon)) - states = parse_list_expression(pragma_tokens, stack, start, qasm) - push!(expr, :amplitude, states) - elseif result_type ∈ ("expectation", "variance", "sample") - obs, targets = parse_pragma_observables(pragma_tokens, stack, start, qasm) - push!(expr, Symbol(result_type), obs, targets) - elseif result_type == "adjoint_gradient" - push!(expr, :adjoint_gradient) - end - elseif pragma_type == "unitary" - push!(expr, :unitary) - matrix_tokens = extract_parensed(pragma_tokens, stack, start, qasm) - unitary_matrix = parse_matrix(matrix_tokens, stack, start, qasm) - push!(expr, unitary_matrix) - target_expr = parse_pragma_targets(pragma_tokens, stack, start, qasm) - push!(expr, target_expr) - elseif pragma_type == "noise" - push!(expr, :noise) - noise_type = parse_identifier(popfirst!(pragma_tokens), qasm)::QasmExpression - if noise_type.args[1] == "kraus" - matrix_tokens = extract_parensed(pragma_tokens, stack, start, qasm) - all(triplet->triplet[end] == lbracket, matrix_tokens[1:3]) && (matrix_tokens = extract_braced_block(matrix_tokens, stack, start, qasm)) - mats = Matrix{QasmExpression}[] - while !isempty(matrix_tokens) - push!(mats, parse_matrix(matrix_tokens, stack, start, qasm)) - isempty(matrix_tokens) && break - next_token = first(matrix_tokens) - next_token[end] == comma && popfirst!(matrix_tokens) - next_token[end] == semicolon && break - end - noise_args = QasmExpression(:arguments, QasmExpression(:array_literal, mats)) - else - noise_args = parse_arguments_list(pragma_tokens, stack, start, qasm) - end - push!(expr, noise_type, noise_args) - target_expr = parse_pragma_targets(pragma_tokens, stack, start, qasm) - push!(expr, target_expr) - elseif pragma_type == "verbatim" - # check that the next non-newline is a box token - push!(expr, :verbatim) - else - throw(QasmParseError("invalid type $pragma_type for pragma", stack, start, qasm)) - end - push!(stack, expr) + push!(stack, parse_pragma(pragma_tokens, stack, start, qasm)) elseif token == include_token closing = findfirst(triplet->triplet[end] == semicolon, clean_tokens) isnothing(closing) && throw(QasmParseError("missing final semicolon for include", stack, start, qasm)) @@ -1100,15 +983,27 @@ struct Qubit size::Int end -struct GateDefinition +const InstructionArgument = Union{Symbol, Dates.Period, Real, Matrix{ComplexF64}} +const CircuitInstruction = @NamedTuple begin type::String; arguments::Vector{InstructionArgument}; targets::Vector{Int}; controls::Vector{Pair{Int, Int}}; exponent::Float64 end +const CircuitResult = @NamedTuple begin type::Symbol; operator::Vector{Union{String, Matrix{ComplexF64}}}; targets::Vector{Int}; states::Vector{String}; end + +abstract type AbstractGateDefinition end + +struct GateDefinition <: AbstractGateDefinition + name::String + arguments::Vector{String} + qubit_targets::Vector{String} # keep this as string to support splatting + body::QasmExpression +end + +struct BuiltinGateDefinition <: AbstractGateDefinition name::String arguments::Vector{String} qubit_targets::Vector{String} # keep this as string to support splatting - body::Vector{Instruction} + body::CircuitInstruction end -GateDefinition(name::String, arguments::Vector{String}, qubit_targets::Vector{String}, @nospecialize(body::Instruction)) = GateDefinition(name, arguments, qubit_targets, [body]) -struct FunctionDefinition +struct FunctionDefinition name::String arguments::QasmExpression body::Vector{QasmExpression} @@ -1134,12 +1029,12 @@ mutable struct QasmProgramVisitor <: AbstractVisitor inputs::Dict{String, Any} classical_defs::Dict{String, ClassicalVariable} function_defs::Dict{String, FunctionDefinition} - gate_defs::Dict{String, GateDefinition} + gate_defs::Dict{String, AbstractGateDefinition} qubit_defs::Dict{String, Qubit} qubit_mapping::Dict{String, Vector{Int}} qubit_count::Int - instructions::Vector{Instruction} - results::Vector{Result} + instructions::Vector{CircuitInstruction} + results::Vector{CircuitResult} function QasmProgramVisitor(inputs::Dict{String, <:Any} = Dict{String, Any}()) new(inputs, Dict{String, ClassicalVariable}(), @@ -1148,19 +1043,37 @@ mutable struct QasmProgramVisitor <: AbstractVisitor Dict{String, Qubit}(), Dict{String, Vector{Int}}(), 0, - Instruction[], - Result[], + CircuitInstruction[], + CircuitResult[], ) end end mutable struct QasmGateDefVisitor <: AbstractVisitor parent::AbstractVisitor - params::Dict{String, BraketSimulator.FreeParameter} + classical_defs::Dict{String, ClassicalVariable} qubit_defs::Dict{String, Qubit} qubit_mapping::Dict{String, Vector{Int}} qubit_count::Int - instructions::Vector{Instruction} + instructions::Vector{CircuitInstruction} + function QasmGateDefVisitor(parent::AbstractVisitor, declared_arguments::Vector{String}, provided_arguments::QasmExpression, gate_qubits::Vector{String}) + qubit_defs = Dict(q=>Qubit(q, 1) for q in gate_qubits) + qubit_mapping = Dict(gate_qubits[ix+1]=>[ix] for ix in 0:length(gate_qubits)-1) + for ix in 0:length(gate_qubits)-1 + qubit_mapping[gate_qubits[ix+1] * "[0]"] = [ix] + end + v = new(parent, + deepcopy(classical_defs(parent)), + qubit_defs, + qubit_mapping, + length(gate_qubits), + CircuitInstruction[], + ) + for (arg_name, arg_value) in zip(declared_arguments, parent(provided_arguments)) + classical_defs(v)[arg_name] = ClassicalVariable(arg_name, Real, arg_value, true) + end + return v + end end mutable struct QasmForLoopVisitor <: AbstractVisitor @@ -1180,20 +1093,19 @@ mutable struct QasmFunctionVisitor <: AbstractVisitor qubit_defs::Dict{String, Qubit} qubit_mapping::Dict{String, Vector{Int}} qubit_count::Int - instructions::Vector{Instruction} - # nosemgrep - function QasmFunctionVisitor(@nospecialize(parent::AbstractVisitor), declared_arguments::Vector{QasmExpression}, provided_arguments::Vector{QasmExpression}) + instructions::Vector{CircuitInstruction} + function QasmFunctionVisitor(parent::AbstractVisitor, declared_arguments::Vector{QasmExpression}, provided_arguments::Vector{QasmExpression}) v = new(parent, classical_defs(parent), deepcopy(parent.qubit_defs), deepcopy(parent.qubit_mapping), qubit_count(parent), - Instruction[], + CircuitInstruction[], ) arg_map = Dict(zip(declared_arguments, provided_arguments)) for arg in declared_arguments if head(arg) ∈ (:const_declaration, :classical_declaration) - new_val = evaluate(parent, arg_map[arg]) + new_val = parent(arg_map[arg]) if head(arg.args[2]) != :classical_assignment arg_id = pop!(arg) push!(arg, QasmExpression(:classical_assignment, QasmExpression(:binary_op, Symbol("="), arg_id, new_val))) @@ -1235,40 +1147,31 @@ qubit_mapping(v::QasmProgramVisitor) = v.qubit_mapping qubit_mapping(v::QasmFunctionVisitor) = v.qubit_mapping qubit_mapping(v::QasmGateDefVisitor) = v.qubit_mapping -BraketSimulator.qubit_count(v::AbstractVisitor) = qubit_count(parent(v)) -BraketSimulator.qubit_count(v::QasmProgramVisitor) = v.qubit_count -BraketSimulator.qubit_count(v::QasmFunctionVisitor) = v.qubit_count -BraketSimulator.qubit_count(v::QasmGateDefVisitor) = v.qubit_count +qubit_count(o::String) = length(o) +qubit_count(o::Matrix) = Int(log2(size(o, 1))) +qubit_count(v::AbstractVisitor) = qubit_count(parent(v)) +qubit_count(v::QasmProgramVisitor) = v.qubit_count +qubit_count(v::QasmFunctionVisitor) = v.qubit_count +qubit_count(v::QasmGateDefVisitor) = v.qubit_count classical_defs(v::AbstractVisitor) = classical_defs(parent(v)) classical_defs(v::QasmProgramVisitor) = v.classical_defs +classical_defs(v::QasmGateDefVisitor) = v.classical_defs classical_defs(v::QasmFunctionVisitor) = v.classical_defs -Base.push!(v::AbstractVisitor, ixs::Vector{<:Instruction}) = push!(parent(v), ixs) -Base.push!(v::QasmProgramVisitor, ixs::Vector{<:Instruction}) = append!(v.instructions, ixs) -Base.push!(v::QasmGateDefVisitor, ixs::Vector{<:Instruction}) = append!(v.instructions, ixs) -Base.push!(v::QasmFunctionVisitor, ixs::Vector{<:Instruction}) = append!(v.instructions, ixs) -Base.push!(v::AbstractVisitor, @nospecialize(ix::Instruction)) = push!(parent(v), ix) -Base.push!(v::QasmProgramVisitor, @nospecialize(ix::Instruction)) = push!(v.instructions, ix) -Base.push!(v::QasmGateDefVisitor, @nospecialize(ix::Instruction)) = push!(v.instructions, ix) -Base.push!(v::QasmFunctionVisitor, @nospecialize(ix::Instruction)) = push!(v.instructions, ix) -Base.push!(v::AbstractVisitor, rts::Vector{<:Result}) = push!(parent(v), rts) -Base.push!(v::QasmProgramVisitor, rts::Vector{<:Result}) = append!(v.results, rts) -Base.push!(v::AbstractVisitor, @nospecialize(rt::Result)) = push!(parent(v), rt) -Base.push!(v::QasmProgramVisitor, @nospecialize(rt::Result)) = push!(v.results, rt) +instructions(v::AbstractVisitor) = instructions(parent(v)) +instructions(v::QasmProgramVisitor) = v.instructions +instructions(v::QasmGateDefVisitor) = v.instructions +instructions(v::QasmFunctionVisitor) = v.instructions -# nosemgrep -function generate_gate_body(@nospecialize(v::AbstractVisitor), argument_names::Vector{String}, qubits::Vector{String}, raw_expressions::QasmExpression) - params = Dict{String, BraketSimulator.FreeParameter}(arg=>BraketSimulator.FreeParameter(arg) for arg in argument_names) - qubit_defs = Dict(q=>Qubit(q, 1) for q in qubits) - qubit_mapping = Dict(qubits[ix+1]=>[ix] for ix in 0:length(qubits)-1) - for ix in 0:length(qubits)-1 - qubit_mapping[qubits[ix+1] * "[0]"] = [ix] - end - gate_def_visitor = QasmGateDefVisitor(v, params, qubit_defs, qubit_mapping, length(qubits), Instruction[]) - gate_def_visitor(raw_expressions) - return gate_def_visitor.instructions -end +results(v::AbstractVisitor) = results(parent(v)) +results(v::QasmProgramVisitor) = v.results + +Base.push!(v::AbstractVisitor, ixs::Vector{CircuitInstruction}) = append!(instructions(v), ixs) +Base.push!(v::AbstractVisitor, ix::CircuitInstruction) = push!(instructions(v), ix) + +Base.push!(v::AbstractVisitor, rts::Vector{CircuitResult}) = append!(results(v), rts) +Base.push!(v::AbstractVisitor, rt::CircuitResult) = push!(results(v), rt) function evaluate_unary_op(op::Symbol, arg) op == :! && return !arg @@ -1319,75 +1222,16 @@ function name(expr::QasmExpression)::String throw(QasmVisitorError("name not defined for expressions of type $(head(expr))")) end -evaluate(@nospecialize(v), i::Number) = i -evaluate(@nospecialize(v), i::String) = i -evaluate(@nospecialize(v), i::BitVector) = i -evaluate(@nospecialize(v), i::NTuple{N,<:Number}) where {N} = i -evaluate(@nospecialize(v), i::Vector{<:Number}) = i -function evaluate(v::V, expr::QasmExpression) where {V<:AbstractVisitor} - if head(expr) == :identifier - id_name = name(expr) - haskey(classical_defs(v), id_name) && return classical_defs(v)[id_name].val - haskey(qubit_mapping(v), id_name) && return evaluate_qubits(v, expr) - elseif head(expr) == :indexed_identifier - identifier_name = name(expr) - if haskey(classical_defs(v), identifier_name) - var = classical_defs(v)[identifier_name] - ix = evaluate(v, expr.args[2]::QasmExpression) - if ix isa StepRange && ix.step > 0 && ix.stop < ix.start # -1 in place of end - new_stop = var.type isa SizedNumber || var.type isa SizedBitVector ? evaluate(v, var.type.size) : length(var.val) - ix = StepRange(ix.start, ix.step, new_stop-1) - end - flat_ix = mapreduce(ix_ -> ix_ .+ 1, vcat, ix) - if var.type isa SizedInt || var.type isa SizedUInt - n_bits::Int = evaluate(v, var.type.size)::Int - int_val = convert(Int, var.val)::Int - values = Int[(int_val >> (n_bits - index)) & 1 for index in flat_ix] - return length(flat_ix) == 1 ? values[1] : values - else - return length(flat_ix) == 1 ? var.val[only(flat_ix)] : var.val[flat_ix] - end - elseif haskey(qubit_mapping(v), identifier_name) - return evaluate_qubits(v, expr) - end - elseif head(expr) == :range - raw_start, raw_step, raw_stop = convert(Vector{QasmExpression}, expr.args) - start::Int = evaluate(v, raw_start) - step::Int = evaluate(v, raw_step) - stop::Int = evaluate(v, raw_stop) - return StepRange(start, step, stop) - elseif head(expr) ∈ (:integer_literal, :float_literal, :string_literal, :complex_literal, :irrational_literal, :boolean_literal, :duration_literal) - return expr.args[1] - elseif head(expr) == :array_literal - return [evaluate(v, arg) for arg in convert(Vector{QasmExpression}, expr.args)] - elseif head(expr) == :observable - raw_obs = expr.args[1]::QasmExpression - if head(raw_obs) == :array_literal - new_obs = map(arg->evaluate(v, QasmExpression(:observable, arg)), convert(Vector{QasmExpression}, raw_obs.args)) - return BraketSimulator.Observables.TensorProduct(convert(Vector{BraketSimulator.Observables.Observable}, new_obs)) - elseif head(raw_obs) == :identifier - obs_name = raw_obs.args[1]::String - obs_name == "x" && return BraketSimulator.Observables.X() - obs_name == "y" && return BraketSimulator.Observables.Y() - obs_name == "z" && return BraketSimulator.Observables.Z() - obs_name == "i" && return BraketSimulator.Observables.I() - obs_name == "h" && return BraketSimulator.Observables.H() - elseif head(raw_obs) == :hermitian - h_mat = similar(raw_obs.args[1], ComplexF64)::Matrix{ComplexF64} - for ii in eachindex(h_mat) - h_mat[ii] = convert(ComplexF64, evaluate(v, raw_obs.args[1][ii]))::ComplexF64 - end - return BraketSimulator.Observables.HermitianObservable(h_mat) - end - elseif head(expr) == :power_mod - pow_expr = QasmExpression(:pow, evaluate(v, expr.args[1]::QasmExpression)) +function evaluate_modifiers(v::V, expr::QasmExpression) where {V<:AbstractVisitor} + if head(expr) == :power_mod + pow_expr = QasmExpression(:pow, v(expr.args[1]::QasmExpression)) return (pow_expr, expr.args[2]) elseif head(expr) == :inverse_mod return (QasmExpression(:inv), expr.args[1]::QasmExpression) elseif head(expr) ∈ (:control_mod, :negctrl_mod) has_argument = length(expr.args) > 1 if has_argument - arg_val::Int = evaluate(v, first(expr.args)::QasmExpression)::Int + arg_val::Int = v(first(expr.args)::QasmExpression)::Int isinteger(arg_val) || throw(QasmVisitorError("cannot apply non-integer ($arg_val) number of controls or negcontrols.")) true_inner = expr.args[2]::QasmExpression inner = QasmExpression(head(expr), true_inner) @@ -1400,97 +1244,8 @@ function evaluate(v::V, expr::QasmExpression) where {V<:AbstractVisitor} end new_head = head(expr) == :control_mod ? :ctrl : :negctrl return (QasmExpression(new_head), inner) - elseif head(expr) == :binary_op - op = expr.args[1]::Symbol - lhs = evaluate(v, expr.args[2]) - rhs = evaluate(v, expr.args[3]) - val = evaluate_binary_op(op, lhs, rhs) - return val - elseif head(expr) == :unary_op - op = expr.args[1]::Symbol - arg = evaluate(v, expr.args[2]) - return evaluate_unary_op(op, arg) - elseif head(expr) == :hw_qubit - return tryparse(Int, replace(expr.args[1], "\$"=>"")) - elseif head(expr) == :cast - casting_to = expr.args[1].args[1] - value = evaluate(v, expr.args[2]) - if casting_to == Bool - return value > 0 - # TODO - else - throw(QasmVisitorError("unable to evaluate cast expression $expr")) - end - elseif head(expr) == :measure - qubits_to_measure = evaluate_qubits(v, expr.args[1]) - push!(v, [Instruction(BraketSimulator.Measure(), q) for q in qubits_to_measure]) - return false - elseif head(expr) == :function_call - function_name = name(expr) - if haskey(builtin_functions, function_name) - concrete_arguments = evaluate(v, convert(Vector{QasmExpression}, expr.args[2].args)) - if function_name != "sizeof" - return_val = builtin_functions[function_name](Iterators.flatten(concrete_arguments)...) - else - return_val = builtin_functions[function_name](concrete_arguments...) - end - return return_val[1] - else - hasfunction(v, function_name) || throw(QasmVisitorError("function $function_name not defined!")) - function_def = function_defs(v)[function_name] - function_body = function_def.body::Vector{QasmExpression} - declared_args = only(function_def.arguments.args)::QasmExpression - provided_args = only(expr.args[2].args)::QasmExpression - function_v = QasmFunctionVisitor(v, declared_args, provided_args) - return_val = nothing - body_exprs::Vector{QasmExpression} = head(function_body[1]) == :scope ? function_body[1].args : function_body - for f_expr in body_exprs - if head(f_expr) == :return - return_val = evaluate(function_v, f_expr.args[1]) - else - function_v(f_expr) - end - end - # remap qubits and classical variables - function_args = if head(declared_args) == :array_literal - convert(Vector{QasmExpression}, declared_args.args)::Vector{QasmExpression} - else - declared_args - end - called_args = if head(provided_args) == :array_literal - convert(Vector{QasmExpression}, provided_args.args)::Vector{QasmExpression} - else - provided_args - end - arguments_map = Dict{QasmExpression, QasmExpression}(zip(function_args, called_args)) - reverse_arguments_map = Dict{QasmExpression, QasmExpression}(zip(called_args, function_args)) - reverse_qubits_map = Dict{Int, Int}() - for variable in keys(reverse_arguments_map) - if head(variable) ∈ (:identifier, :indexed_identifier) - variable_name = name(variable) - if haskey(classical_defs(v), variable_name) && classical_defs(v)[variable_name].type isa SizedArray - if head(reverse_arguments_map[variable]) != :const_declaration - inner_variable_name = name(reverse_arguments_map[variable]) - new_val = classical_defs(function_v)[inner_variable_name].val - back_assignment = QasmExpression(:classical_assignment, QasmExpression(:binary_op, Symbol("="), variable, new_val)) - v(back_assignment) - end - elseif haskey(qubit_defs(v), variable_name) - outer_context_map = only(evaluate_qubits(v, variable)) - inner_context_map = only(evaluate_qubits(function_v, reverse_arguments_map[variable].args[1])) - reverse_qubits_map[inner_context_map] = outer_context_map - end - end - end - remapped = isempty(reverse_qubits_map) ? function_v.instructions : Instruction[remap(ix, reverse_qubits_map) for ix in function_v.instructions] - push!(v, remapped) - return return_val - end - else - throw(QasmVisitorError("unable to evaluate expression $expr")) end end -evaluate(v::AbstractVisitor, exprs::Vector{QasmExpression}) = [evaluate(v, expr) for expr in exprs] # nosemgrep function _evaluate_qubits(::Val{:identifier}, v, qubit_expr::QasmExpression)::Vector{Int} @@ -1505,7 +1260,7 @@ function _evaluate_qubits(::Val{:indexed_identifier}, v, qubit_expr::QasmExpress qubit_name = name(qubit_expr) mapping = qubit_mapping(v)::Dict{String, Vector{Int}} haskey(mapping, qubit_name) || throw(QasmVisitorError("Missing input variable '$qubit_name'.", "NameError")) - qubit_ix = evaluate(v, qubit_expr.args[2]::QasmExpression) + qubit_ix = v(qubit_expr.args[2]::QasmExpression) qubits = Iterators.flatmap(qubit_ix) do rq haskey(mapping, qubit_name * "[$rq]") || throw(QasmVisitorError("Invalid qubit index '$rq' in '$qubit_name'.", "IndexError")) return mapping[qubit_name * "[$rq]"] @@ -1513,49 +1268,56 @@ function _evaluate_qubits(::Val{:indexed_identifier}, v, qubit_expr::QasmExpress return collect(qubits) end _evaluate_qubits(::Val{:array_literal}, v, qubit_expr::QasmExpression)::Vector{Int} = collect(Iterators.flatmap(expr->_evaluate_qubits(Val(head(expr)), v, expr), qubit_expr.args)) -_evaluate_qubits(::Val{:hw_qubit}, v, qubit_expr::QasmExpression)::Vector{Int} = [evaluate(v, qubit_expr)] +_evaluate_qubits(::Val{:hw_qubit}, v, qubit_expr::QasmExpression)::Vector{Int} = Int[v(qubit_expr)::Int] _evaluate_qubits(val, v, qubit_expr) = throw(QasmVisitorError("unable to evaluate qubits for expression $qubit_expr.")) -function evaluate_qubits(v::AbstractVisitor, qubit_targets::Vector{QasmExpression})::Vector{Int} +function evaluate_qubits(v::AbstractVisitor, qubit_targets::Vector)::Vector{Int} final_qubits = map(qubit_expr->_evaluate_qubits(Val(head(qubit_expr)), v, qubit_expr), qubit_targets) return vcat(final_qubits...) end evaluate_qubits(v::AbstractVisitor, qubit_targets::QasmExpression) = evaluate_qubits(v::AbstractVisitor, [qubit_targets]) -# semgrep rules can't handle this macro properly yet -# nosemgrep -function process_gate_arguments(v::AbstractVisitor, gate_name::String, defined_arguments::Vector{String}, called_arguments::Vector{QasmExpression}, @nospecialize(gate_body::Vector{Instruction})) +function remap(ix, target_mapper::Dict{Int, Int}) + mapped_targets = map(t->getindex(target_mapper, t), ix.targets) + mapped_controls = map(c->getindex(target_mapper, c[1])=>c[2], ix.controls) + return (type=ix.type, arguments=ix.arguments, targets=mapped_targets, controls=mapped_controls, exponent=ix.exponent) +end +function bind_arguments!(ix::CircuitInstruction, argument_values::Dict{Symbol, <:Real}) + new_arguments = InstructionArgument[get(argument_values, arg, arg) for arg in ix.arguments] + return (type=ix.type, arguments=new_arguments, targets=ix.targets, controls=ix.controls, exponent=ix.exponent) +end + +function process_gate_arguments(v::AbstractVisitor, gate_name::String, defined_arguments::Vector{String}, called_arguments::QasmExpression, gate_body::Vector{CircuitInstruction}) def_has_arguments = !isempty(defined_arguments) - call_has_arguments = !isempty(called_arguments) + call_has_arguments = !isempty(v(called_arguments)) if def_has_arguments ⊻ call_has_arguments def_has_arguments && throw(QasmVisitorError("gate $gate_name requires arguments but none were provided.")) call_has_arguments && throw(QasmVisitorError("gate $gate_name does not accept arguments but arguments were provided.")) end if def_has_arguments - evaled_args = only(evaluate(v, called_arguments)) + evaled_args = v(called_arguments) argument_values = Dict{Symbol, Real}(Symbol(arg_name)=>argument for (arg_name, argument) in zip(defined_arguments, evaled_args)) - return [bind_value!(ix, argument_values) for ix in gate_body] + return map(ix->bind_arguments!(ix, argument_values), gate_body) else return deepcopy(gate_body) end end -# nosemgrep -function handle_gate_modifiers(@nospecialize(ixs::Vector{<:Instruction}), mods::Vector{QasmExpression}, control_qubits::Vector{Int}, is_gphase::Bool) +function handle_gate_modifiers(ixs, mods::Vector{QasmExpression}, control_qubits::Vector{Int}, is_gphase::Bool) for mod in Iterators.reverse(mods) - control_qubit = (head(mod) ∈ (:negctrl, :ctrl) && !is_gphase) ? pop!(control_qubits) : -1 + control_qubit = head(mod) ∈ (:negctrl, :ctrl) ? pop!(control_qubits) : -1 for (ii, ix) in enumerate(ixs) if head(mod) == :pow - ixs[ii].operator.pow_exponent *= mod.args[1] + ixs[ii] = (type=ix.type, arguments=ix.arguments, targets=ix.targets, controls=ix.controls, exponent=ix.exponent*mod.args[1]) elseif head(mod) == :inv - ixs[ii].operator.pow_exponent *= -1 + ixs[ii] = (type=ix.type, arguments=ix.arguments, targets=ix.targets, controls=ix.controls, exponent=-ix.exponent) # need to handle "extra" target elseif head(mod) ∈ (:negctrl, :ctrl) - bit = head(mod) == :ctrl ? (1,) : (0,) + bit = head(mod) == :ctrl ? 1 : 0 if is_gphase - ixs[ii] = Instruction(Control(ix.operator, bit), ix.target) + ixs[ii] = (type=ix.type, arguments=ix.arguments, targets=ix.targets, controls=pushfirst!(ix.controls, control_qubit=>bit), exponent=ix.exponent) else - ixs[ii] = Instruction(Control(ix.operator, bit), BraketSimulator.QubitSet(control_qubit::Int, ix.target...)) + ixs[ii] = (type=ix.type, arguments=ix.arguments, targets=pushfirst!(ix.targets, control_qubit), controls=pushfirst!(ix.controls, control_qubit=>bit), exponent=ix.exponent) end end end @@ -1587,14 +1349,12 @@ function visit_gphase_call(v::AbstractVisitor, program_expr::QasmExpression) n_defined_with::Int = n_called_with gate_targets::Vector{Int} = collect(0:n_called_with-1) provided_arg::QasmExpression = only(program_expr.args[2].args) - evaled_arg = Float64(evaluate(v, provided_arg)) - applied_arguments = Instruction[Instruction(BraketSimulator.MultiQubitPhaseShift{n_called_with}(evaled_arg), gate_targets)] + evaled_arg = Float64(v(provided_arg)) + applied_arguments = CircuitInstruction[(type="gphase", arguments=[evaled_arg], targets=gate_targets, controls=Pair{Int,Int}[], exponent=1.0)] mods::Vector{QasmExpression} = length(program_expr.args) == 4 ? program_expr.args[4].args : QasmExpression[] - applied_arguments = handle_gate_modifiers(applied_arguments, mods, Int[], true) - target_mapper = Dict{Int, Int}(g_ix=>gate_targets[g_ix+1][1] for g_ix in 0:n_called_with-1) - for (ii, ix) in enumerate(applied_arguments) - push!(v, remap(ix, target_mapper)) - end + applied_arguments = handle_gate_modifiers(applied_arguments, mods, deepcopy(gate_targets), true) + target_mapper = Dict{Int, Int}(g_ix=>gate_targets[g_ix+1][1] for g_ix in 0:n_called_with-1) + push!(v, map(ix->remap(ix, target_mapper), applied_arguments)) return end @@ -1602,19 +1362,22 @@ function visit_gate_call(v::AbstractVisitor, program_expr::QasmExpression) gate_name = name(program_expr)::String raw_call_targets = program_expr.args[3]::QasmExpression call_targets::Vector{QasmExpression} = convert(Vector{QasmExpression}, head(raw_call_targets.args[1]) == :array_literal ? raw_call_targets.args[1].args : raw_call_targets.args)::Vector{QasmExpression} - provided_args::Vector{QasmExpression} = convert(Vector{QasmExpression}, program_expr.args[2].args)::Vector{QasmExpression} - has_modifiers = length(program_expr.args) == 4 - gate_targets = Vector{Int}[evaluate_qubits(v, call_target)::Vector{Int} for call_target in call_targets] - n_called_with = length(gate_targets) + provided_args = isempty(program_expr.args[2].args) ? QasmExpression(:empty) : only(program_expr.args[2].args)::QasmExpression + has_modifiers = length(program_expr.args) == 4 hasgate(v, gate_name) || throw(QasmVisitorError("gate $gate_name not defined!")) - gate_def = gate_defs(v)[gate_name] - n_defined_with = length(gate_def.qubit_targets) + gate_def = gate_defs(v)[gate_name] + gate_def_v = QasmGateDefVisitor(v, gate_def.arguments, provided_args, gate_def.qubit_targets) + gate_def_v(deepcopy(gate_def.body)) + gate_ixs = instructions(gate_def_v) + gate_targets = Vector{Int}[evaluate_qubits(v, call_target)::Vector{Int} for call_target in call_targets] + n_called_with = length(gate_targets) + n_defined_with = length(gate_def.qubit_targets) # cases like `ccnot qs`; if n_called_with < n_defined_with && length(gate_targets[1]) == n_defined_with n_called_with = length(gate_targets[1]) - gate_targets = Vector{Int}[[gt] for gt in gate_targets[1]] + gate_targets = Vector{Int}[[gt] for gt in gate_targets[1]] end - applied_arguments = process_gate_arguments(v, gate_name, gate_def.arguments, provided_args, gate_def.body) + applied_arguments = process_gate_arguments(v, gate_name, gate_def.arguments, provided_args, gate_ixs) control_qubits::Vector{Int} = collect(0:(n_called_with-n_defined_with)-1) mods::Vector{QasmExpression} = length(program_expr.args) == 4 ? convert(Vector{QasmExpression}, program_expr.args[4].args) : QasmExpression[] if !isempty(control_qubits) @@ -1623,25 +1386,22 @@ function visit_gate_call(v::AbstractVisitor, program_expr::QasmExpression) applied_arguments[ii] = remap(applied_arguments[ii], modifier_remap) end end - applied_arguments = handle_gate_modifiers(applied_arguments, mods, control_qubits, false) + applied_arguments = handle_gate_modifiers(applied_arguments, mods, control_qubits, false) longest, gate_targets = splat_gate_targets(gate_targets) for splatted_ix in 1:longest target_mapper = Dict{Int, Int}(g_ix=>gate_targets[g_ix+1][splatted_ix] for g_ix in 0:n_called_with-1) - for ii in 1:length(applied_arguments) - push!(v, remap(applied_arguments[ii], target_mapper)) - end + push!(v, map(ix->remap(ix, target_mapper), applied_arguments)) end return end -function _check_observable_targets(observable::BraketSimulator.Observables.HermitianObservable, targets) - qubit_count(observable) == 1 && (isempty(targets) || length(targets) == 1) && return - qubit_count(observable) == length(targets) && return - matrix_ir = BraketSimulator.complex_matrix_to_ir(observable.matrix) - throw(QasmVisitorError("Invalid observable specified: $matrix_ir, targets: $targets", "ValueError")) -end -_check_observable_targets(observable, targets) = nothing - +(v::AbstractVisitor)(i::Number) = i +(v::AbstractVisitor)(i::String) = i +(v::AbstractVisitor)(i::BitVector) = i +(v::AbstractVisitor)(i::NTuple{N,<:Number}) where {N} = i +(v::AbstractVisitor)(i::Vector{<:Number}) = i +(v::AbstractVisitor)(program_exprs::Vector) = map(v, program_exprs) +(v::QasmGateDefVisitor)(ix::CircuitInstruction) = push!(v, ix) function (v::AbstractVisitor)(program_expr::QasmExpression) var_name::String = "" if head(program_expr) == :program @@ -1660,20 +1420,23 @@ function (v::AbstractVisitor)(program_expr::QasmExpression) return v elseif head(program_expr) == :reset targets = program_expr.args[1]::QasmExpression - target_qubits = evaluate(v, targets) - push!(v, [BraketSimulator.Instruction(BraketSimulator.Reset(), t) for t in target_qubits]) + target_qubits = evaluate_qubits(v, targets) + ixs = [(type="reset", arguments=InstructionArgument[], targets=[t], controls=Pair{Int, Int}[], exponent=1.0) for t in target_qubits] + push!(v, ixs) return v elseif head(program_expr) == :barrier targets = program_expr.args[1]::QasmExpression - target_qubits = evaluate(v, targets) - push!(v, [BraketSimulator.Instruction(BraketSimulator.Barrier(), t) for t in target_qubits]) + target_qubits = evaluate_qubits(v, targets) + ixs = [(type="barrier", arguments=InstructionArgument[], targets=[t], controls=Pair{Int, Int}[], exponent=1.0) for t in target_qubits] + push!(v, ixs) return v elseif head(program_expr) == :delay duration_expr = program_expr.args[1].args[1]::QasmExpression targets = program_expr.args[2].args[1]::QasmExpression - target_qubits = evaluate(v, targets) - duration = evaluate(v, duration_expr) - push!(v, [BraketSimulator.Instruction(BraketSimulator.Delay(duration), t) for t in target_qubits]) + target_qubits = evaluate_qubits(v, targets) + duration = v(duration_expr) + ixs = [(type="delay", arguments=InstructionArgument[duration], targets=[t], controls=Pair{Int, Int}[], exponent=1.0) for t in target_qubits] + push!(v, ixs) return v elseif head(program_expr) == :stretch return v @@ -1694,7 +1457,7 @@ function (v::AbstractVisitor)(program_expr::QasmExpression) for_loop = convert(Vector{QasmExpression}, program_expr.args) loop_variable_type = for_loop[1].args[1] loop_variable_name = for_loop[2].args[1]::String - loop_variable_values = evaluate(for_v, for_loop[3]) + loop_variable_values = for_v(for_loop[3]) loop_body = for_loop[4]::QasmExpression for loop_value in loop_variable_values loop_variable = ClassicalVariable(loop_variable_name, loop_variable_type, loop_value, false) @@ -1714,12 +1477,12 @@ function (v::AbstractVisitor)(program_expr::QasmExpression) end delete!(classical_defs(v), loop_variable_name) elseif head(program_expr) == :switch - case_val = evaluate(v, program_expr.args[1]) + case_val = v(program_expr.args[1]) all_cases = convert(Vector{QasmExpression}, program_expr.args[2:end]) default = findfirst(expr->head(expr) == :default, all_cases) case_found = false for case in all_cases - if head(case) == :case && case_val ∈ evaluate(v, case.args[1]) + if head(case) == :case && case_val ∈ v(case.args[1]) case_found = true foreach(v, convert(Vector{QasmExpression}, case.args[2:end])) break @@ -1729,8 +1492,36 @@ function (v::AbstractVisitor)(program_expr::QasmExpression) isnothing(default) && throw(QasmVisitorError("no case matched and no default defined.")) foreach(v, convert(Vector{QasmExpression}, all_cases[default].args)) end + elseif head(program_expr) == :identifier + id_name = name(program_expr) + haskey(classical_defs(v), id_name) && return classical_defs(v)[id_name].val + haskey(qubit_mapping(v), id_name) && return evaluate_qubits(v, program_expr) + throw(QasmVisitorError("no identifier $id_name defined.")) + elseif head(program_expr) == :indexed_identifier + identifier_name = name(program_expr) + if haskey(classical_defs(v), identifier_name) + var = classical_defs(v)[identifier_name] + ix = v(program_expr.args[2]::QasmExpression) + if ix isa StepRange && ix.step > 0 && ix.stop < ix.start # -1 in place of end + new_stop = var.type isa SizedNumber || var.type isa SizedBitVector ? v(var.type.size) : length(var.val) + ix = StepRange(ix.start, ix.step, new_stop-1) + end + flat_ix = mapreduce(ix_ -> ix_ .+ 1, vcat, ix) + if var.type isa SizedInt || var.type isa SizedUInt + n_bits::Int = v(var.type.size)::Int + int_val = convert(Int, var.val)::Int + values = Int[(int_val >> (n_bits - index)) & 1 for index in flat_ix] + return length(flat_ix) == 1 ? values[1] : values + else + return length(flat_ix) == 1 ? var.val[only(flat_ix)] : var.val[flat_ix] + end + elseif haskey(qubit_mapping(v), identifier_name) + return evaluate_qubits(v, program_expr) + else + throw(QasmVisitorError("no identifier $identifier_name defined.")) + end elseif head(program_expr) == :if - condition_value = evaluate(v, program_expr.args[1]) > 0 + condition_value = v(program_expr.args[1]) > 0 has_else = findfirst(expr->head(expr) == :else, convert(Vector{QasmExpression}, program_expr.args)) last_expr = !isnothing(has_else) ? length(program_expr.args) - 1 : length(program_expr.args) if condition_value @@ -1748,7 +1539,7 @@ function (v::AbstractVisitor)(program_expr::QasmExpression) end elseif head(program_expr) == :while while_v = QasmWhileLoopVisitor(v) - condition_value = evaluate(v, program_expr.args[1]) > 0 + condition_value = v(program_expr.args[1]) > 0 loop_body = program_expr.args[2] while condition_value if head(loop_body) == :scope @@ -1760,15 +1551,15 @@ function (v::AbstractVisitor)(program_expr::QasmExpression) else while_v(loop_body) end - condition_value = evaluate(while_v, program_expr.args[1]) + condition_value = while_v(program_expr.args[1]) end elseif head(program_expr) == :classical_assignment op = program_expr.args[1].args[1]::Symbol left_hand_side = program_expr.args[1].args[2]::QasmExpression right_hand_side = program_expr.args[1].args[3] var_name = name(left_hand_side)::String - right_val = evaluate(v, right_hand_side) - left_val = evaluate(v, left_hand_side) + right_val = v(right_hand_side) + left_val = v(left_hand_side) classical_defs(v)[var_name].is_const && throw(QasmVisitorError("cannot reassign value of const variable!")) if head(left_hand_side) == :identifier var = classical_defs(v)[var_name] @@ -1782,11 +1573,11 @@ function (v::AbstractVisitor)(program_expr::QasmExpression) end var.val = new_val elseif head(left_hand_side) == :indexed_identifier - inds = evaluate(v, left_hand_side.args[2]) + inds = v(left_hand_side.args[2]) var = classical_defs(v)[var_name] var_type = var.type if inds isa StepRange && inds.step > 0 && inds.stop < inds.start # -1 in place of end - new_stop = var.type isa SizedNumber ? evaluate(v, var.type.size) - 1 : length(var.val) - 1 + new_stop = var.type isa SizedNumber ? v(var.type.size) - 1 : length(var.val) - 1 inds = StepRange(inds.start, inds.step, new_stop) end inds = inds .+ 1 @@ -1808,9 +1599,9 @@ function (v::AbstractVisitor)(program_expr::QasmExpression) init = if var_type isa SizedNumber undef elseif var_type isa SizedArray - fill(undef, evaluate(v, var_type.size)) + fill(undef, v(var_type.size)) elseif var_type isa SizedBitVector - falses(max(0, evaluate(v, var_type.size))) + falses(max(0, v(var_type.size))) end # no initial value if head(program_expr.args[2]) == :identifier @@ -1828,9 +1619,9 @@ function (v::AbstractVisitor)(program_expr::QasmExpression) init = if var_type isa SizedNumber undef elseif var_type isa SizedArray - fill(undef, evaluate(v, var_type.size)) + fill(undef, v(var_type.size)) elseif var_type isa SizedBitVector - falses(max(0, evaluate(v, var_type.size))) + falses(max(0, v(var_type.size))) end op, left_hand_side, right_hand_side = program_expr.args[2].args[1].args var_name = name(left_hand_side) @@ -1839,7 +1630,7 @@ function (v::AbstractVisitor)(program_expr::QasmExpression) v.classical_defs[var_name] = ClassicalVariable(var_name, var_type, v.classical_defs[var_name].val, true) elseif head(program_expr) == :qubit_declaration qubit_name::String = name(program_expr) - qubit_size::Int = evaluate(v, program_expr.args[2]) + qubit_size::Int = v(program_expr.args[2])::Int qubit_defs(v)[qubit_name] = Qubit(qubit_name, qubit_size) qubit_mapping(v)[qubit_name] = collect(qubit_count(v) : qubit_count(v) + qubit_size - 1) for qubit_i in 0:qubit_size-1 @@ -1848,10 +1639,10 @@ function (v::AbstractVisitor)(program_expr::QasmExpression) v.qubit_count += qubit_size elseif head(program_expr) ∈ (:power_mod, :inverse_mod, :control_mod, :negctrl_mod) mods = QasmExpression(:modifiers) - mod_expr, inner = evaluate(v, program_expr) + mod_expr, inner = evaluate_modifiers(v, program_expr) push!(mods, mod_expr) while head(inner) != :gate_call # done - mod_expr, inner = evaluate(v, inner) + mod_expr, inner = evaluate_modifiers(v, inner) push!(mods, mod_expr) end push!(inner, mods) @@ -1875,13 +1666,70 @@ function (v::AbstractVisitor)(program_expr::QasmExpression) single_argument = !isempty(gate_arguments.args) && head(gate_arguments.args[1]) == :array_literal argument_exprs = single_argument ? gate_arguments.args[1].args::Vector{Any} : gate_arguments.args::Vector{Any} argument_names = String[arg.args[1] for arg::QasmExpression in argument_exprs] - single_target = head(gate_def_targets.args[1]) == :array_literal - qubit_targets = single_target ? map(name, gate_def_targets.args[1].args)::Vector{String} : map(name, gate_def_targets.args)::Vector{String} - body_ixs = generate_gate_body(v, argument_names, qubit_targets, gate_body) - gate = GateDefinition(gate_name, argument_names, qubit_targets, body_ixs) - v.gate_defs[gate_name] = gate + single_target = head(gate_def_targets.args[1]) == :array_literal + qubit_targets = single_target ? map(name, gate_def_targets.args[1].args)::Vector{String} : map(name, gate_def_targets.args)::Vector{String} + v.gate_defs[gate_name] = GateDefinition(gate_name, argument_names, qubit_targets, gate_body) elseif head(program_expr) == :function_call - evaluate(v, program_expr) + function_name = name(program_expr) + if haskey(builtin_functions, function_name) + concrete_arguments = v(convert(Vector{QasmExpression}, program_expr.args[2].args)) + if function_name != "sizeof" + return_val = builtin_functions[function_name](Iterators.flatten(concrete_arguments)...) + else + return_val = builtin_functions[function_name](concrete_arguments...) + end + return return_val[1] + else + hasfunction(v, function_name) || throw(QasmVisitorError("function $function_name not defined!")) + function_def = function_defs(v)[function_name] + function_body = function_def.body::Vector{QasmExpression} + declared_args = only(function_def.arguments.args)::QasmExpression + provided_args = only(program_expr.args[2].args)::QasmExpression + function_v = QasmFunctionVisitor(v, declared_args, provided_args) + return_val = nothing + body_exprs::Vector{QasmExpression} = head(function_body[1]) == :scope ? function_body[1].args : function_body + for f_expr in body_exprs + if head(f_expr) == :return + return_val = function_v(f_expr.args[1]) + else + function_v(f_expr) + end + end + # remap qubits and classical variables + function_args = if head(declared_args) == :array_literal + convert(Vector{QasmExpression}, declared_args.args)::Vector{QasmExpression} + else + declared_args + end + called_args = if head(provided_args) == :array_literal + convert(Vector{QasmExpression}, provided_args.args)::Vector{QasmExpression} + else + provided_args + end + arguments_map = Dict{QasmExpression, QasmExpression}(zip(function_args, called_args)) + reverse_arguments_map = Dict{QasmExpression, QasmExpression}(zip(called_args, function_args)) + reverse_qubits_map = Dict{Int, Int}() + for variable in keys(reverse_arguments_map) + if head(variable) ∈ (:identifier, :indexed_identifier) + variable_name = name(variable) + if haskey(classical_defs(v), variable_name) && classical_defs(v)[variable_name].type isa SizedArray + if head(reverse_arguments_map[variable]) != :const_declaration + inner_variable_name = name(reverse_arguments_map[variable]) + new_val = classical_defs(function_v)[inner_variable_name].val + back_assignment = QasmExpression(:classical_assignment, QasmExpression(:binary_op, Symbol("="), variable, new_val)) + v(back_assignment) + end + elseif haskey(qubit_defs(v), variable_name) + outer_context_map = only(evaluate_qubits(v, variable)) + inner_context_map = only(evaluate_qubits(function_v, reverse_arguments_map[variable].args[1])) + reverse_qubits_map[inner_context_map] = outer_context_map + end + end + end + mapper = isempty(reverse_qubits_map) ? identity : ix->remap(ix, reverse_qubits_map) + push!(v, map(mapper, function_v.instructions)) + return return_val + end elseif head(program_expr) == :function_definition function_def = program_expr.args function_name = function_def[1].args[1]::String @@ -1891,124 +1739,58 @@ function (v::AbstractVisitor)(program_expr::QasmExpression) full_function_def = FunctionDefinition(function_name, function_arguments, function_body, function_return_type) v.function_defs[function_name] = full_function_def elseif head(program_expr) == :pragma - pragma_type::Symbol = program_expr.args[1] - if pragma_type == :result - result_type = program_expr.args[2] - if result_type == :state_vector - push!(v, BraketSimulator.StateVector()) - elseif result_type == :probability - has_targets = !isempty(program_expr.args[3].args) - targets = has_targets ? evaluate_qubits(v, program_expr.args[3].args[1]) : BraketSimulator.QubitSet() - push!(v, BraketSimulator.Probability(targets)) - elseif result_type == :density_matrix - has_targets = !isempty(program_expr.args[3].args) - targets = has_targets ? evaluate_qubits(v, program_expr.args[3].args[1]) : BraketSimulator.QubitSet() - push!(v, BraketSimulator.DensityMatrix(targets)) - elseif result_type == :amplitude - states = head(program_expr.args[3]) == :array_literal ? program_expr.args[3].args : program_expr.args[3] - clean_states = map(states) do state - return replace(state.args[1], "\""=>"", "\'"=>"") - end - push!(v, BraketSimulator.Amplitude(clean_states)) - elseif result_type == :expectation - raw_obs = program_expr.args[3]::QasmExpression - raw_targets = program_expr.args[end]::QasmExpression - has_targets = !isempty(raw_targets.args) - targets = has_targets ? evaluate_qubits(v, raw_targets.args[1]) : BraketSimulator.QubitSet() - observable = evaluate(v, raw_obs) - if observable isa BraketSimulator.Observables.StandardObservable && length(targets) > 1 - throw(QasmVisitorError("Standard observable target must be exactly 1 qubit.", "ValueError")) - end - _check_observable_targets(observable, targets) - push!(v, BraketSimulator.Expectation(observable, targets)) - elseif result_type == :variance - raw_obs = program_expr.args[3]::QasmExpression - raw_targets = program_expr.args[end]::QasmExpression - has_targets = !isempty(raw_targets.args) - targets = has_targets ? evaluate_qubits(v, raw_targets.args[1]) : BraketSimulator.QubitSet() - observable = evaluate(v, raw_obs) - if observable isa BraketSimulator.Observables.StandardObservable && length(targets) > 1 - throw(QasmVisitorError("Standard observable target must be exactly 1 qubit.", "ValueError")) - end - _check_observable_targets(observable, targets) - push!(v, BraketSimulator.Variance(observable, targets)) - elseif result_type == :sample - raw_obs = program_expr.args[3]::QasmExpression - raw_targets = program_expr.args[end]::QasmExpression - has_targets = !isempty(raw_targets.args) - targets = has_targets ? evaluate_qubits(v, raw_targets.args[1]) : BraketSimulator.QubitSet() - observable = evaluate(v, raw_obs) - if observable isa BraketSimulator.Observables.StandardObservable && length(targets) > 1 - throw(QasmVisitorError("Standard observable target must be exactly 1 qubit.", "ValueError")) - end - _check_observable_targets(observable, targets) - push!(v, BraketSimulator.Sample(observable, targets)) - elseif result_type == :adjoint_gradient - throw(QasmVisitorError("Result type adjoint_gradient is not supported.", "TypeError")) - end - elseif pragma_type == :unitary - raw_mat = program_expr.args[2]::Matrix{QasmExpression} - unitary_matrix = similar(raw_mat, ComplexF64)::Matrix{ComplexF64} - for ii in eachindex(unitary_matrix) - unitary_matrix[ii] = evaluate(v, raw_mat[ii]) - end - targets = evaluate_qubits(v, program_expr.args[end].args[1]) - push!(v, Instruction(BraketSimulator.Unitary(unitary_matrix), targets)) - elseif pragma_type == :noise - noise_type::String = program_expr.args[2].args[1]::String - raw_args::QasmExpression = program_expr.args[3].args[1]::QasmExpression - raw_targets::QasmExpression = program_expr.args[4]::QasmExpression - targets = evaluate_qubits(v, raw_targets.args[1])::Vector{Int} - if noise_type == "kraus" - raw_mats = raw_args.args - kraus_matrices = map(raw_mats) do raw_mat - return broadcast(expr->convert(ComplexF64, evaluate(v, expr)), raw_mat)::Matrix{ComplexF64} - end - push!(v, Instruction(BraketSimulator.Kraus(kraus_matrices), targets)) - else - braket_noise_type = noise_types[noise_type] - args = map(Float64, evaluate(v, raw_args)) - push!(v, Instruction(braket_noise_type(args...), targets)) - end - elseif pragma_type == :verbatim - end + visit_pragma(v, program_expr) + elseif head(program_expr) ∈ (:integer_literal, :float_literal, :string_literal, :complex_literal, :irrational_literal, :boolean_literal, :duration_literal) + return program_expr.args[1] + elseif head(program_expr) == :array_literal + return map(v, program_expr.args) + elseif head(program_expr) == :range + start::Int, step::Int, stop::Int = v(program_expr.args) + return StepRange(start, step, stop) + elseif head(program_expr) == :empty + return () elseif head(program_expr) == :measure - evaluate(v, program_expr) + qubits_to_measure = evaluate_qubits(v, program_expr.args[1]) + push!(v, CircuitInstruction[(type="measure", arguments=InstructionArgument[], targets=[q], controls=Pair{Int,Int}[], exponent=1.0) for q in qubits_to_measure]) + return false + elseif head(program_expr) == :hw_qubit + return tryparse(Int, name(program_expr)) elseif head(program_expr) == :output throw(QasmVisitorError("Output not supported.")) + elseif head(program_expr) == :binary_op + op = program_expr.args[1]::Symbol + lhs = v(program_expr.args[2]) + rhs = v(program_expr.args[3]) + val = evaluate_binary_op(op, lhs, rhs) + return val + elseif head(program_expr) == :unary_op + op = program_expr.args[1]::Symbol + arg = v(program_expr.args[2]) + return evaluate_unary_op(op, arg) + elseif head(program_expr) == :cast + casting_to = program_expr.args[1].args[1] + value = v(program_expr.args[2]) + if casting_to == Bool && !(value isa Period) + return value isa BitVector ? any(value) : value > 0 + elseif casting_to isa SizedBitVector && !(value isa Period || value isa AbstractFloat) + new_size = v(casting_to.size) + return value isa BitVector ? value[1:new_size] : BitVector(reverse(digits(value, base=2, pad=new_size))) + elseif casting_to isa SizedNumber && !(value isa Period) + num_value = value isa BitVector ? sum(reverse(value)[k]*2^(k-1) for k=1:length(value)) : value + if casting_to isa SizedInt + return Int(num_value) + elseif casting_to isa SizedUInt + return UInt(num_value) + elseif casting_to isa SizedFloat + return Float64(num_value) + end + else + throw(QasmVisitorError("unable to evaluate cast expression $program_expr")) + end else throw(QasmVisitorError("cannot visit expression $program_expr.")) end return v end -function to_circuit(v::QasmProgramVisitor) - c = BraketSimulator.Circuit() - foreach(ix->BraketSimulator.add_instruction!(c, ix), v.instructions) - for rt in v.results - obs = BraketSimulator.extract_observable(rt) - if !isnothing(obs) && c.observables_simultaneously_measureable && !(rt isa BraketSimulator.AdjointGradient) - BraketSimulator.add_to_qubit_observable_mapping!(c, obs, rt.targets) - end - BraketSimulator.add_to_qubit_observable_set!(c, rt) - push!(c.result_types, rt) - end - return c -end - -# semgrep rules can't handle this macro properly yet -function to_circuit(qasm_source::String, inputs) - input_qasm = if endswith(qasm_source, ".qasm") && isfile(qasm_source) - read(qasm_source, String) - else - qasm_source - end - endswith(input_qasm, "\n") || (input_qasm *= "\n") - parsed = parse_qasm(input_qasm) - visitor = QasmProgramVisitor(inputs) - visitor(parsed) - return to_circuit(visitor) -end -to_circuit(qasm_source::String) = to_circuit(qasm_source, Dict{String, Float64}()) - end # module Quasar diff --git a/src/builtin_gates.jl b/src/builtin_gates.jl index 195d887..f4d18e9 100644 --- a/src/builtin_gates.jl +++ b/src/builtin_gates.jl @@ -1,92 +1,79 @@ # OpenQASM 3 Braket Standard Gates -builtin_gates() = Dict{String, GateDefinition}( +builtin_gates() = Dict{String, BuiltinGateDefinition}( # identity gate - "i"=>GateDefinition("i", String[], ["a"], Instruction(BraketSimulator.I(), 0)), + "i"=>BuiltinGateDefinition("i", String[], ["a"], (type="i", arguments=InstructionArgument[], targets=[0], controls=Pair{Int,Int}[], exponent=1.0)), # phase gate - "phaseshift"=>GateDefinition("phaseshift", ["λ"], ["a"], Instruction(BraketSimulator.PhaseShift(BraketSimulator.FreeParameter(:λ)), 0)), + "phaseshift"=>BuiltinGateDefinition("phaseshift", ["λ"], ["a"], (type="phaseshift", arguments=InstructionArgument[:λ], targets=[0], controls=Pair{Int,Int}[], exponent=1.0)), # pauli X gate - "x"=>GateDefinition("x", String[], ["a"], Instruction(BraketSimulator.X(), 0)), + "x"=>BuiltinGateDefinition("x", String[], ["a"], (type="x", arguments=InstructionArgument[], targets=[0], controls=Pair{Int,Int}[], exponent=1.0)), # pauli Y gate - "y"=>GateDefinition("y", String[], ["a"], Instruction(BraketSimulator.Y(), 0)), + "y"=>BuiltinGateDefinition("y", String[], ["a"], (type="y", arguments=InstructionArgument[], targets=[0], controls=Pair{Int,Int}[], exponent=1.0)), # pauli Z gate - "z"=>GateDefinition("z", String[], ["a"], Instruction(BraketSimulator.Z(), 0)), + "z"=>BuiltinGateDefinition("z", String[], ["a"], (type="z", arguments=InstructionArgument[], targets=[0], controls=Pair{Int,Int}[], exponent=1.0)), # Hadamard gate - "h"=>GateDefinition("h", String[], ["a"], Instruction(BraketSimulator.H(), 0)), + "h"=>BuiltinGateDefinition("h", String[], ["a"], (type="h", arguments=InstructionArgument[], targets=[0], controls=Pair{Int,Int}[], exponent=1.0)), # S gate - "s"=>GateDefinition("s", String[], ["a"], Instruction(BraketSimulator.S(), 0)), + "s"=>BuiltinGateDefinition("s", String[], ["a"], (type="s", arguments=InstructionArgument[], targets=[0], controls=Pair{Int,Int}[], exponent=1.0)), # Si gate - "si"=>GateDefinition("si", String[], ["a"], Instruction(BraketSimulator.Si(), 0)), + "si"=>BuiltinGateDefinition("si", String[], ["a"], (type="si", arguments=InstructionArgument[], targets=[0], controls=Pair{Int,Int}[], exponent=1.0)), # T gate - "t"=>GateDefinition("t", String[], ["a"], Instruction(BraketSimulator.T(), 0)), + "t"=>BuiltinGateDefinition("t", String[], ["a"], (type="t", arguments=InstructionArgument[], targets=[0], controls=Pair{Int,Int}[], exponent=1.0)), # Ti gate - "ti"=>GateDefinition("ti", String[], ["a"], Instruction(BraketSimulator.Ti(), 0)), + "ti"=>BuiltinGateDefinition("ti", String[], ["a"], (type="ti", arguments=InstructionArgument[], targets=[0], controls=Pair{Int,Int}[], exponent=1.0)), # V gate - "v"=>GateDefinition("v", String[], ["a"], Instruction(BraketSimulator.V(), 0)), + "v"=>BuiltinGateDefinition("v", String[], ["a"], (type="v", arguments=InstructionArgument[], targets=[0], controls=Pair{Int,Int}[], exponent=1.0)), # Vi gate - "vi"=>GateDefinition("vi", String[], ["a"], Instruction(BraketSimulator.Vi(), 0)), + "vi"=>BuiltinGateDefinition("vi", String[], ["a"], (type="vi", arguments=InstructionArgument[], targets=[0], controls=Pair{Int,Int}[], exponent=1.0)), # RotX gate - "rx"=>GateDefinition("rx", ["θ"], ["a"], Instruction(BraketSimulator.Rx(BraketSimulator.FreeParameter(:θ)), 0)), + "rx"=>BuiltinGateDefinition("rx", ["θ"], ["a"], (type="rx", arguments=InstructionArgument[:θ], targets=[0], controls=Pair{Int,Int}[], exponent=1.0)), # RotY gate - "ry"=>GateDefinition("ry", ["θ"], ["a"], Instruction(BraketSimulator.Ry(BraketSimulator.FreeParameter(:θ)), 0)), + "ry"=>BuiltinGateDefinition("ry", ["θ"], ["a"], (type="ry", arguments=InstructionArgument[:θ], targets=[0], controls=Pair{Int,Int}[], exponent=1.0)), # RotZ gate - "rz"=>GateDefinition("rz", ["θ"], ["a"], Instruction(BraketSimulator.Rz(BraketSimulator.FreeParameter(:θ)), 0)), + "rz"=>BuiltinGateDefinition("rz", ["θ"], ["a"], (type="rz", arguments=InstructionArgument[:θ], targets=[0], controls=Pair{Int,Int}[], exponent=1.0)), # CNot gate - "cnot"=>GateDefinition("cnot", String[], ["a", "b"], Instruction(BraketSimulator.CNot(), BraketSimulator.QubitSet(0, 1))), + "cnot"=>BuiltinGateDefinition("cnot", String[], ["a", "b"], (type="cnot", arguments=InstructionArgument[], targets=[0, 1], controls=Pair{Int,Int}[], exponent=1.0)), # CY gate - "cy"=>GateDefinition("cy", String[], ["a", "b"], Instruction(BraketSimulator.CY(), BraketSimulator.QubitSet(0, 1))), + "cy"=>BuiltinGateDefinition("cy", String[], ["a", "b"], (type="cy", arguments=InstructionArgument[], targets=[0, 1], controls=Pair{Int,Int}[], exponent=1.0)), # CZ gate - "cz"=>GateDefinition("cz", String[], ["a", "b"], Instruction(BraketSimulator.CZ(), BraketSimulator.QubitSet(0, 1))), + "cz"=>BuiltinGateDefinition("cz", String[], ["a", "b"], (type="cz", arguments=InstructionArgument[], targets=[0, 1], controls=Pair{Int,Int}[], exponent=1.0)), # CV gate - "cv"=>GateDefinition("cv", String[], ["a", "b"], Instruction(BraketSimulator.CV(), BraketSimulator.QubitSet(0, 1))), + "cv"=>BuiltinGateDefinition("cv", String[], ["a", "b"], (type="cv", arguments=InstructionArgument[], targets=[0, 1], controls=Pair{Int,Int}[], exponent=1.0)), # controlled-phase - "cphaseshift"=>GateDefinition("cphaseshift", ["λ"], ["a", "b"], Instruction(BraketSimulator.CPhaseShift(BraketSimulator.FreeParameter(:λ)), BraketSimulator.QubitSet(0, 1))), + "cphaseshift"=>BuiltinGateDefinition("cphaseshift", ["λ"], ["a", "b"], (type="cphaseshift", arguments=InstructionArgument[:λ], targets=[0, 1], controls=Pair{Int,Int}[], exponent=1.0)), # controlled-phase-00 - "cphaseshift00"=>GateDefinition("cphaseshift00", ["λ"], ["a", "b"], Instruction(BraketSimulator.CPhaseShift00(BraketSimulator.FreeParameter(:λ)), BraketSimulator.QubitSet(0, 1))), + "cphaseshift00"=>BuiltinGateDefinition("cphaseshift00", ["λ"], ["a", "b"], (type="cphaseshift00", arguments=InstructionArgument[:λ], targets=[0, 1], controls=Pair{Int,Int}[], exponent=1.0)), # controlled-phase-01 - "cphaseshift01"=>GateDefinition("cphaseshift01", ["λ"], ["a", "b"], Instruction(BraketSimulator.CPhaseShift01(BraketSimulator.FreeParameter(:λ)), BraketSimulator.QubitSet(0, 1))), + "cphaseshift01"=>BuiltinGateDefinition("cphaseshift01", ["λ"], ["a", "b"], (type="cphaseshift01", arguments=InstructionArgument[:λ], targets=[0, 1], controls=Pair{Int,Int}[], exponent=1.0)), # controlled-phase-10 - "cphaseshift10"=>GateDefinition("cphaseshift10", ["λ"], ["a", "b"], Instruction(BraketSimulator.CPhaseShift10(BraketSimulator.FreeParameter(:λ)), BraketSimulator.QubitSet(0, 1))), + "cphaseshift10"=>BuiltinGateDefinition("cphaseshift10", ["λ"], ["a", "b"], (type="cphaseshift10", arguments=InstructionArgument[:λ], targets=[0, 1], controls=Pair{Int,Int}[], exponent=1.0)), # Swap gate - "swap"=>GateDefinition("swap", String[], ["a", "b"], Instruction(BraketSimulator.Swap(), BraketSimulator.QubitSet(0, 1))), + "swap"=>BuiltinGateDefinition("swap", String[], ["a", "b"], (type="swap", arguments=InstructionArgument[], targets=[0, 1], controls=Pair{Int,Int}[], exponent=1.0)), # ISwap gate - "iswap"=>GateDefinition("iswap", String[], ["a", "b"], Instruction(BraketSimulator.ISwap(), BraketSimulator.QubitSet(0, 1))), + "iswap"=>BuiltinGateDefinition("iswap", String[], ["a", "b"], (type="iswap", arguments=InstructionArgument[], targets=[0, 1], controls=Pair{Int,Int}[], exponent=1.0)), # ISwap gate - "pswap"=>GateDefinition("pswap", ["θ"], ["a", "b"], Instruction(BraketSimulator.PSwap(BraketSimulator.FreeParameter(:θ)), BraketSimulator.QubitSet(0, 1))), + "pswap"=>BuiltinGateDefinition("pswap", ["θ"], ["a", "b"], (type="pswap", arguments=InstructionArgument[:θ], targets=[0, 1], controls=Pair{Int,Int}[], exponent=1.0)), # controlled-swap gate - "cswap"=>GateDefinition("cswap", String[], ["a", "b", "c"], Instruction(BraketSimulator.CSwap(), BraketSimulator.QubitSet(0, 1, 2))), + "cswap"=>BuiltinGateDefinition("cswap", String[], ["a", "b", "c"], (type="cswap", arguments=InstructionArgument[], targets=[0, 1, 2], controls=Pair{Int,Int}[], exponent=1.0)), # ccnot/Toffoli gate - "ccnot"=>GateDefinition("ccnot", String[], ["a", "b", "c"], Instruction(BraketSimulator.CCNot(), BraketSimulator.QubitSet(0, 1, 2))), + "ccnot"=>BuiltinGateDefinition("ccnot", String[], ["a", "b", "c"], (type="ccnot", arguments=InstructionArgument[], targets=[0, 1, 2], controls=Pair{Int,Int}[], exponent=1.0)), # XX gate - "xx"=>GateDefinition("xx", ["θ"], ["a", "b"], Instruction(BraketSimulator.XX(BraketSimulator.FreeParameter(:θ)), BraketSimulator.QubitSet(0, 1))), + "xx"=>BuiltinGateDefinition("xx", ["θ"], ["a", "b"], (type="xx", arguments=InstructionArgument[:θ], targets=[0, 1], controls=Pair{Int,Int}[], exponent=1.0)), # XY gate - "xy"=>GateDefinition("xy", ["θ"], ["a", "b"], Instruction(BraketSimulator.XY(BraketSimulator.FreeParameter(:θ)), BraketSimulator.QubitSet(0, 1))), + "xy"=>BuiltinGateDefinition("xy", ["θ"], ["a", "b"], (type="xy", arguments=InstructionArgument[:θ], targets=[0, 1], controls=Pair{Int,Int}[], exponent=1.0)), # YY gate - "yy"=>GateDefinition("yy", ["θ"], ["a", "b"], Instruction(BraketSimulator.YY(BraketSimulator.FreeParameter(:θ)), BraketSimulator.QubitSet(0, 1))), + "yy"=>BuiltinGateDefinition("yy", ["θ"], ["a", "b"], (type="yy", arguments=InstructionArgument[:θ], targets=[0, 1], controls=Pair{Int,Int}[], exponent=1.0)), # ZZ gate - "zz"=>GateDefinition("zz", ["θ"], ["a", "b"], Instruction(BraketSimulator.ZZ(BraketSimulator.FreeParameter(:θ)), BraketSimulator.QubitSet(0, 1))), + "zz"=>BuiltinGateDefinition("zz", ["θ"], ["a", "b"], (type="zz", arguments=InstructionArgument[:θ], targets=[0, 1], controls=Pair{Int,Int}[], exponent=1.0)), # ECR gate - "ecr"=>GateDefinition("ecr", String[], ["a", "b"], Instruction(BraketSimulator.ECR(), BraketSimulator.QubitSet(0, 1))), + "ecr"=>BuiltinGateDefinition("ecr", String[], ["a", "b"], (type="ecr", arguments=InstructionArgument[], targets=[0, 1], controls=Pair{Int,Int}[], exponent=1.0)), # MS gate - "ms"=>GateDefinition("ms", ["ϕ", "θ", "λ"], ["a", "b"], Instruction(BraketSimulator.MS(BraketSimulator.FreeParameter(:ϕ), BraketSimulator.FreeParameter(:θ), BraketSimulator.FreeParameter(:λ)), BraketSimulator.QubitSet(0, 1))), + "ms"=>BuiltinGateDefinition("ms", ["ϕ", "θ", "λ"], ["a", "b"], (type="ms", arguments=InstructionArgument[:ϕ, :θ, :λ], targets=[0, 1], controls=Pair{Int,Int}[], exponent=1.0)), # GPi gate - "gpi"=>GateDefinition("gpi", ["θ"], ["a"], Instruction(BraketSimulator.GPi(BraketSimulator.FreeParameter(:θ)), 0)), + "gpi"=>BuiltinGateDefinition("gpi", ["θ"], ["a"], (type="gpi", arguments=InstructionArgument[:θ], targets=[0], controls=Pair{Int,Int}[], exponent=1.0)), # GPi2 gate - "gpi2"=>GateDefinition("gpi2", ["θ"], ["a"], Instruction(BraketSimulator.GPi2(BraketSimulator.FreeParameter(:θ)), 0)), + "gpi2"=>BuiltinGateDefinition("gpi2", ["θ"], ["a"], (type="gpi2", arguments=InstructionArgument[:θ], targets=[0], controls=Pair{Int,Int}[], exponent=1.0)), # PRx gate - "prx"=>GateDefinition("prx", ["θ", "ϕ"], ["a"], Instruction(BraketSimulator.PRx(BraketSimulator.FreeParameter(:θ), BraketSimulator.FreeParameter(:ϕ)), 0)), + "prx"=>BuiltinGateDefinition("prx", ["θ", "ϕ"], ["a"], (type="prx", arguments=InstructionArgument[:θ, :ϕ], targets=[0], controls=Pair{Int,Int}[], exponent=1.0)), # 3-angle U gate - "U"=>GateDefinition("U", ["θ", "ϕ", "λ"], ["a"], Instruction(BraketSimulator.U(BraketSimulator.FreeParameter(:θ), BraketSimulator.FreeParameter(:ϕ), BraketSimulator.FreeParameter(:λ)), 0)), + "U"=>BuiltinGateDefinition("U", ["θ", "ϕ", "λ"], ["a"], (type="u", arguments=InstructionArgument[:θ, :ϕ, :λ], targets=[0], controls=Pair{Int,Int}[], exponent=1.0)), ) - -const noise_types = Dict{String, Type}( - "bit_flip"=>BraketSimulator.BitFlip, - "phase_flip"=>BraketSimulator.PhaseFlip, - "pauli_channel"=>BraketSimulator.PauliChannel, - "depolarizing"=>BraketSimulator.Depolarizing, - "two_qubit_depolarizing"=>BraketSimulator.TwoQubitDepolarizing, - "two_qubit_dephasing"=>BraketSimulator.TwoQubitDephasing, - "amplitude_damping"=>BraketSimulator.AmplitudeDamping, - "generalized_amplitude_damping"=>BraketSimulator.GeneralizedAmplitudeDamping, - "phase_damping"=>BraketSimulator.PhaseDamping, - "kraus"=>BraketSimulator.Kraus, - ) diff --git a/src/circuit.jl b/src/circuit.jl index 9c17dce..3d7dd53 100644 --- a/src/circuit.jl +++ b/src/circuit.jl @@ -259,3 +259,38 @@ function add_instruction!(c::Circuit, ix::Instruction{O}) where {O<:Operator} push!(c.instructions, ix) return c end + +function to_circuit(v::Quasar.QasmProgramVisitor) + c = Circuit() + foreach(v.instructions) do ix + sim_op = StructTypes.constructfrom(QuantumOperator, ix) + op = isempty(ix.controls) ? sim_op : Control(sim_op, tuple(map(c->getindex(c, 2), ix.controls)...)) + sim_ix = Instruction(op, ix.targets) + add_instruction!(c, sim_ix) + end + for rt in v.results + sim_rt = StructTypes.constructfrom(Result, rt) + obs = extract_observable(sim_rt) + if !isnothing(obs) && c.observables_simultaneously_measureable && !(rt isa AdjointGradient) + add_to_qubit_observable_mapping!(c, obs, sim_rt.targets) + end + add_to_qubit_observable_set!(c, sim_rt) + push!(c.result_types, sim_rt) + end + return c +end + +# semgrep rules can't handle this macro properly yet +function to_circuit(qasm_source::String, inputs) + input_qasm = if endswith(qasm_source, ".qasm") && isfile(qasm_source) + read(qasm_source, String) + else + qasm_source + end + endswith(input_qasm, "\n") || (input_qasm *= "\n") + parsed = parse_qasm(input_qasm) + visitor = QasmProgramVisitor(inputs) + visitor(parsed) + return to_circuit(visitor) +end +to_circuit(qasm_source::String) = to_circuit(qasm_source, Dict{String, Float64}()) diff --git a/src/custom_gates.jl b/src/custom_gates.jl index 33ef87d..aa102bd 100644 --- a/src/custom_gates.jl +++ b/src/custom_gates.jl @@ -37,21 +37,22 @@ end qubit_count(g::MultiRZ) = 1 """ - MultiQubitPhaseShift{N}(angle) + GPhase{N}(angle) Global phase shift on `N` qubits. Equivalent to the OpenQASM3 built-in [`gphase` gate](https://openqasm.com/language/gates.html#gphase). Controls/negative controls applied to this gate control -which states are rotated, so that `Control(g::MultiQubitPhaseShift{2})` will apply the rotation +which states are rotated, so that `Control(g::GPhase{2})` will apply the rotation to the `|11>` state. """ -mutable struct MultiQubitPhaseShift{N} <: AngledGate{1} +mutable struct GPhase{N} <: AngledGate{1} angle::NTuple{1,Union{Real,FreeParameter}} pow_exponent::Float64 - MultiQubitPhaseShift{N}(angle::T, pow_exponent::Float64=1.0) where {N, T<:NTuple{1,Union{Real,FreeParameter}}} = new(angle, pow_exponent) + GPhase{N}(angle::T, pow_exponent::Float64=1.0) where {N, T<:NTuple{1,Union{Real,FreeParameter}}} = new(angle, pow_exponent) end -matrix_rep_raw(g::MultiQubitPhaseShift{N}) where {N} = Diagonal(SVector{2^N, ComplexF64}(exp(im*g.angle[1]) for _ in 1:2^N)) -qubit_count(g::MultiQubitPhaseShift{N}) where {N} = N +matrix_rep_raw(g::GPhase{N}) where {N} = Diagonal(SVector{2^N, ComplexF64}(exp(im*g.angle[1]) for _ in 1:2^N)) +qubit_count(g::GPhase{N}) where {N} = N +StructTypes.constructfrom(::Type{GPhase}, nt::Quasar.CircuitInstruction) = GPhase{length(nt.targets)}(only(nt.arguments), nt.exponent) function apply_gate!( factor::ComplexF64, @@ -75,7 +76,7 @@ for (V, f) in ((true, :conj), (false, :identity)) apply_gate!(::Val{$V}, g::MultiRZ, state_vec::AbstractStateVector{T}, t::Int) where {T<:Complex} = apply_gate!(Val($V), Rz(g.angle, g.pow_exponent), state_vec, t) apply_gate!(::Val{$V}, g::MultiRZ, state_vec::AbstractStateVector{T}, t1::Int,t2::Int,) where {T<:Complex} = apply_gate!(Val($V), ZZ(g.angle, g.pow_exponent), state_vec, t1, t2) apply_gate!(::Val{$V}, g::MultiRZ, state_vec::AbstractStateVector{T}, ts::Vararg{Int,N}) where {T<:Complex,N} = apply_gate!($f(-im * g.angle[1] / 2.0), PauliEigenvalues(Val(N)), state_vec, ts...) - apply_gate!(::Val{$V}, g::MultiQubitPhaseShift{N}, state_vec::AbstractStateVector{T}, ts::Vararg{Int,N}) where {T<:Complex, N} = apply_gate!($f(exp(im*g.angle[1]*g.pow_exponent)), state_vec, ts...) + apply_gate!(::Val{$V}, g::GPhase{N}, state_vec::AbstractStateVector{T}, ts::Vararg{Int,N}) where {T<:Complex, N} = apply_gate!($f(exp(im*g.angle[1]*g.pow_exponent)), state_vec, ts...) end end @@ -119,7 +120,7 @@ Control(g::Control{G, BC}, bitvals::NTuple{0, Int}, pow_exponent::Float64=1.0) w Control(g::G, bitvals::NTuple{0, Int}, pow_exponent::Float64=1.0) where {G<:Gate} = g Base.copy(c::Control{G, B}) where {G<:Gate, B} = Control(copy(c.g), c.bitvals, c.pow_exponent) qubit_count(c::Control{G, B}) where {G<:Gate, B} = qubit_count(c.g) + B -qubit_count(c::Control{MultiQubitPhaseShift{N}, B}) where {N, B} = N +qubit_count(c::Control{GPhase{N}, B}) where {N, B} = N function matrix_rep_raw(c::Control{G, B}) where {G<:Gate, B} inner_mat = matrix_rep(c.g) inner_qc = qubit_count(c.g) @@ -134,7 +135,7 @@ function matrix_rep_raw(c::Control{G, B}) where {G<:Gate, B} end return full_mat end -function matrix_rep_raw(c::Control{MultiQubitPhaseShift{N}, B}) where {N, B} +function matrix_rep_raw(c::Control{GPhase{N}, B}) where {N, B} g_mat = matrix_rep(c.g) qc = N ctrl_ix = 0 diff --git a/src/gate_kernels.jl b/src/gate_kernels.jl index 7bd86a6..45c45bc 100644 --- a/src/gate_kernels.jl +++ b/src/gate_kernels.jl @@ -468,7 +468,7 @@ end for (V, f) in ((true, :conj), (false, :identity)) @eval begin apply_gate!(::Val{$V}, gate::Control{G, B}, state_vec::AbstractStateVector{T}, qubits::Int...) where {T<:Complex, G<:Gate, B} = apply_controlled_gate!(Val($V), Val(B), gate, gate.g ^ gate.pow_exponent, state_vec, gate.bitvals, qubits...) - apply_gate!(::Val{$V}, gate::Control{MultiQubitPhaseShift{N}, B}, state_vec::AbstractStateVector{T}, qubits::Int...) where {T<:Complex, N, B} = apply_gate!($f(im), gate, state_vec, qubits...) + apply_gate!(::Val{$V}, gate::Control{GPhase{N}, B}, state_vec::AbstractStateVector{T}, qubits::Int...) where {T<:Complex, N, B} = apply_gate!($f(im), gate, state_vec, qubits...) apply_gate!(::Val{$V}, gate::Unitary, state_vec::AbstractStateVector{T}, targets::Vararg{Int,NQ}) where {T<:Complex,NQ} = apply_gate!($f(SMatrix{2^NQ, 2^NQ, ComplexF64}(matrix_rep(gate))), state_vec, targets...) apply_gate!(::Val{$V}, g::G, state_vec::AbstractStateVector{T}, qubits::Int...) where {G<:Gate,T<:Complex} = (mat = $f(matrix_rep(g)); apply_gate!(mat, state_vec, qubits...)) apply_controlled_gate!( @@ -526,7 +526,7 @@ end function apply_gate!( factor::Complex, - gate::Control{MultiQubitPhaseShift{N}, B}, + gate::Control{GPhase{N}, B}, state_vec::AbstractStateVector{T}, qubits::Int..., ) where {T<:Complex, N, B} diff --git a/src/gates.jl b/src/gates.jl index bd86c17..3e720e1 100644 --- a/src/gates.jl +++ b/src/gates.jl @@ -49,6 +49,7 @@ for gate_def in ( $G(angle::T, pow_exponent::Float64=1.0) where {T<:NTuple{$n_angle, Union{Real, FreeParameter}}} = new(angle, pow_exponent) end qubit_count(::Type{$G}) = $qc + StructTypes.constructfrom(::Type{$G}, nt::Quasar.CircuitInstruction) = $G(tuple(nt.arguments...), nt.exponent) end end Base.:(==)(g1::G, g2::G) where {G<:AngledGate} = g1.pow_exponent == g2.pow_exponent && g1.angle == g2.angle @@ -88,6 +89,7 @@ for gate_def in ( $G(pow_exponent::Float64=1.0) = new(pow_exponent) end qubit_count(::Type{$G}) = $qc + StructTypes.constructfrom(::Type{$G}, nt::Quasar.CircuitInstruction) = $G(nt.exponent) end end (::Type{G})(angle::T, pow_exponent=1.0) where {G<:AngledGate{1}, T<:Union{Real, FreeParameter}} = G((angle,), Float64(pow_exponent)) @@ -111,6 +113,7 @@ mutable struct Unitary <: Gate end Base.:(==)(u1::Unitary, u2::Unitary) = u1.matrix == u2.matrix && u1.pow_exponent == u2.pow_exponent qubit_count(g::Unitary) = convert(Int, log2(size(g.matrix, 1))) +StructTypes.constructfrom(::Type{Unitary}, nt::Quasar.CircuitInstruction) = Unitary(only(nt.arguments), nt.exponent) Parametrizable(g::AngledGate) = Parametrized() Parametrizable(g::Gate) = NonParametrized() diff --git a/src/noises.jl b/src/noises.jl index 1d5a75b..588ea7e 100644 --- a/src/noises.jl +++ b/src/noises.jl @@ -15,6 +15,7 @@ struct Kraus <: Noise end Base.:(==)(k1::Kraus, k2::Kraus) = k1.matrices == k2.matrices qubit_count(g::Kraus) = convert(Int, log2(size(g.matrices[1], 1))) +StructTypes.constructfrom(::Type{Kraus}, nt::Quasar.CircuitInstruction) = Kraus(nt.arguments) """ BitFlip <: Noise @@ -26,6 +27,7 @@ struct BitFlip <: Noise end Parametrizable(g::BitFlip) = Parametrized() qubit_count(g::BitFlip) = 1 +StructTypes.constructfrom(::Type{BitFlip}, nt::Quasar.CircuitInstruction) = BitFlip(only(nt.arguments)) """ PhaseFlip <: Noise @@ -37,6 +39,7 @@ struct PhaseFlip <: Noise end Parametrizable(g::PhaseFlip) = Parametrized() qubit_count(g::PhaseFlip) = 1 +StructTypes.constructfrom(::Type{PhaseFlip}, nt::Quasar.CircuitInstruction) = PhaseFlip(only(nt.arguments)) """ PauliChannel <: Noise @@ -50,6 +53,7 @@ struct PauliChannel <: Noise end Parametrizable(g::PauliChannel) = Parametrized() qubit_count(g::PauliChannel) = 1 +StructTypes.constructfrom(::Type{PauliChannel}, nt::Quasar.CircuitInstruction) = PauliChannel(nt.arguments...) """ AmplitudeDamping <: Noise @@ -61,6 +65,7 @@ struct AmplitudeDamping <: Noise end Parametrizable(g::AmplitudeDamping) = Parametrized() qubit_count(g::AmplitudeDamping) = 1 +StructTypes.constructfrom(::Type{AmplitudeDamping}, nt::Quasar.CircuitInstruction) = AmplitudeDamping(only(nt.arguments)) """ PhaseDamping <: Noise @@ -72,6 +77,7 @@ struct PhaseDamping <: Noise end Parametrizable(g::PhaseDamping) = Parametrized() qubit_count(g::PhaseDamping) = 1 +StructTypes.constructfrom(::Type{PhaseDamping}, nt::Quasar.CircuitInstruction) = PhaseDamping(only(nt.arguments)) """ Depolarizing <: Noise @@ -83,6 +89,7 @@ struct Depolarizing <: Noise end Parametrizable(g::Depolarizing) = Parametrized() qubit_count(g::Depolarizing) = 1 +StructTypes.constructfrom(::Type{Depolarizing}, nt::Quasar.CircuitInstruction) = Depolarizing(only(nt.arguments)) """ TwoQubitDephasing <: Noise @@ -94,6 +101,7 @@ struct TwoQubitDephasing <: Noise end Parametrizable(g::TwoQubitDephasing) = Parametrized() qubit_count(g::TwoQubitDephasing) = 2 +StructTypes.constructfrom(::Type{TwoQubitDephasing}, nt::Quasar.CircuitInstruction) = TwoQubitDephasing(only(nt.arguments)) """ TwoQubitDepolarizing <: Noise @@ -105,6 +113,7 @@ struct TwoQubitDepolarizing <: Noise end Parametrizable(g::TwoQubitDepolarizing) = Parametrized() qubit_count(g::TwoQubitDepolarizing) = 2 +StructTypes.constructfrom(::Type{TwoQubitDepolarizing}, nt::Quasar.CircuitInstruction) = TwoQubitDepolarizing(only(nt.arguments)) """ GeneralizedAmplitudeDamping <: Noise @@ -117,6 +126,7 @@ struct GeneralizedAmplitudeDamping <: Noise end Parametrizable(g::GeneralizedAmplitudeDamping) = Parametrized() qubit_count(g::GeneralizedAmplitudeDamping) = 1 +StructTypes.constructfrom(::Type{GeneralizedAmplitudeDamping}, nt::Quasar.CircuitInstruction) = GeneralizedAmplitudeDamping(nt.arguments...) """ MultiQubitPauliChannel{N} <: Noise diff --git a/src/observables.jl b/src/observables.jl index ed5bccf..3ae693f 100644 --- a/src/observables.jl +++ b/src/observables.jl @@ -211,6 +211,7 @@ function StructTypes.constructfrom(::Type{Observable}, obj::String) (obj == "h" || obj == "H") && return H() throw(ArgumentError("Observable of type \"$obj\" provided, only \"i\", \"x\", \"y\", \"z\", and \"h\" are valid.")) end +StructTypes.constructfrom(::Type{Observable}, obj::Matrix{ComplexF64}) = HermitianObservable(obj) StructTypes.constructfrom(::Type{Observable}, obj::Vector{Vector{Vector{Float64}}}) = HermitianObservable(obj) StructTypes.constructfrom(::Type{Observable}, obj::Vector{T}) where {T} = length(obj) == 1 ? StructTypes.constructfrom(Observable, obj[1]) : TensorProduct([StructTypes.constructfrom(Observable, o) for o in obj]) diff --git a/src/operators.jl b/src/operators.jl index 72157bb..e4eee7c 100644 --- a/src/operators.jl +++ b/src/operators.jl @@ -13,6 +13,9 @@ Abstract type representing *quantum* operations that can be applied to a [`Circu Subtypes include [`Gate`](@ref) and [`Noise`](@ref). """ abstract type QuantumOperator <: Operator end +StructTypes.StructType(::Type{QuantumOperator}) = StructTypes.AbstractType() +StructTypes.subtypes(::Type{QuantumOperator}) = (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, prx=PRx, u=U, gphase=GPhase, kraus=Kraus, bit_flip=BitFlip, phase_flip=PhaseFlip, pauli_channel=PauliChannel, amplitude_damping=AmplitudeDamping, phase_damping=PhaseDamping, depolarizing=Depolarizing, two_qubit_dephasing=TwoQubitDephasing, two_qubit_depolarizing=TwoQubitDepolarizing, generalized_amplitude_damping=GeneralizedAmplitudeDamping, multi_qubit_pauli_channel=MultiQubitPauliChannel, measure=Measure, reset=Reset, barrier=Barrier, delay=Delay) + abstract type Parametrizable end struct Parametrized end @@ -68,6 +71,7 @@ Measure() = Measure(-1) Parametrizable(m::Measure) = NonParametrized() qubit_count(::Type{Measure}) = 1 qubit_count(m::Measure) = qubit_count(Measure) +StructTypes.constructfrom(::Type{Measure}, nt) = Measure() """ Reset(index) <: QuantumOperator @@ -82,6 +86,7 @@ Reset() = Reset(-1) Parametrizable(m::Reset) = NonParametrized() qubit_count(::Type{Reset}) = 1 qubit_count(m::Reset) = qubit_count(Reset) +StructTypes.constructfrom(::Type{Reset}, nt) = Reset() """ Barrier(index) <: QuantumOperator @@ -96,6 +101,7 @@ Barrier() = Barrier(-1) Parametrizable(m::Barrier) = NonParametrized() qubit_count(::Type{Barrier}) = 1 qubit_count(m::Barrier) = qubit_count(Barrier) +StructTypes.constructfrom(::Type{Barrier}, nt) = Barrier() """ Delay(index, duration::Time) <: QuantumOperator @@ -112,3 +118,4 @@ Delay(duration::Dates.Period) = Delay(-1, duration) Parametrizable(m::Delay) = NonParametrized() qubit_count(::Type{Delay}) = 1 qubit_count(m::Delay) = qubit_count(Delay) +StructTypes.constructfrom(::Type{Delay}, nt) = Delay(only(nt.arguments)) diff --git a/src/pragmas.jl b/src/pragmas.jl new file mode 100644 index 0000000..eb8078e --- /dev/null +++ b/src/pragmas.jl @@ -0,0 +1,212 @@ +function _observable_targets_error(observable::Matrix{ComplexF64}, targets) + mat = Vector{Vector{Vector{Float64}}}(undef, size(observable, 1)) + for row in 1:size(observable, 1) + mat[row] = Vector{Vector{Float64}}(undef, size(observable, 2)) + for col in 1:size(observable, 2) + mat[row][col] = [real(observable[row, col]), imag(observable[row, col])] + end + end + throw(Quasar.QasmVisitorError("Invalid observable specified: $mat, targets: $targets", "ValueError")) +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 == 1 && (isempty(targets) || length(targets) == 1) && return + qc == length(targets) && return + _observable_targets_error(observable, targets) +end +function _check_observable_targets(observable::Vector{Union{String, Matrix{ComplexF64}}}, targets) + if length(observable) == 1 + _check_observable_targets(only(observable), targets) + else + return nothing + end +end + +function visit_observable(v, expr) + raw_obs = expr.args[1]::Quasar.QasmExpression + if Quasar.head(raw_obs) == :array_literal + return mapreduce(arg->visit_observable(v, Quasar.QasmExpression(:observable, arg)), vcat, convert(Vector{Quasar.QasmExpression}, raw_obs.args)) + elseif Quasar.head(raw_obs) == :identifier + return Union{String, Matrix{ComplexF64}}[raw_obs.args[1]] + elseif Quasar.head(raw_obs) == :hermitian + h_mat = similar(raw_obs.args[1], ComplexF64)::Matrix{ComplexF64} + for ii in eachindex(h_mat) + h_mat[ii] = convert(ComplexF64, v(raw_obs.args[1][ii]))::ComplexF64 + end + return Union{String, Matrix{ComplexF64}}[h_mat] + end +end + +function Quasar.visit_pragma(v, program_expr) + pragma_type::Symbol = program_expr.args[1] + if pragma_type == :result + result_type = program_expr.args[2] + if result_type == :state_vector + push!(v, (type=:state_vector, operator=Union{String, Matrix{ComplexF64}}[], targets=Int[], states=String[])) + elseif result_type ∈ (:probability, :density_matrix) + has_targets = !isempty(program_expr.args[3].args) + targets = has_targets ? Quasar.evaluate_qubits(v, program_expr.args[3].args[1]) : Int[] + rt = (type=result_type, operator=Union{String, Matrix{ComplexF64}}[], targets=targets, states=String[]) + push!(v, rt) + elseif result_type == :amplitude + states = Quasar.head(program_expr.args[3]) == :array_literal ? program_expr.args[3].args : program_expr.args[3] + clean_states = map(state->replace(state.args[1], "\""=>"", "\'"=>""), states) + push!(v, (type=:amplitude, operator=Union{String, Matrix{ComplexF64}}[], targets=Int[], states=clean_states)) + elseif result_type ∈ (:expectation, :variance, :sample) + raw_obs = program_expr.args[3]::Quasar.QasmExpression + raw_targets = program_expr.args[end]::Quasar.QasmExpression + has_targets = !isempty(raw_targets.args) + targets = has_targets ? Quasar.evaluate_qubits(v, raw_targets.args[1]) : Int[] + observable = visit_observable(v, raw_obs) + _check_observable_targets(observable, targets) + push!(v, (type=result_type, operator=observable, targets=targets, states=String[])) + elseif result_type == :adjoint_gradient + throw(Quasar.QasmVisitorError("Result type adjoint_gradient is not supported.", "TypeError")) + end + elseif pragma_type == :unitary + raw_mat = program_expr.args[2]::Matrix{Quasar.QasmExpression} + unitary_matrix = similar(raw_mat, ComplexF64)::Matrix{ComplexF64} + for ii in eachindex(unitary_matrix) + unitary_matrix[ii] = v(raw_mat[ii]) + end + targets = Quasar.evaluate_qubits(v, program_expr.args[end].args[1]) + push!(v, (type="unitary", arguments=Quasar.InstructionArgument[unitary_matrix], targets=targets, controls=Pair{Int,Int}[], exponent=1.0)) + elseif pragma_type == :noise + noise_type::String = program_expr.args[2].args[1]::String + raw_args::Quasar.QasmExpression = program_expr.args[3].args[1]::Quasar.QasmExpression + raw_targets::Quasar.QasmExpression = program_expr.args[4]::Quasar.QasmExpression + targets = Quasar.evaluate_qubits(v, raw_targets.args[1])::Vector{Int} + if noise_type == "kraus" + raw_mats = raw_args.args + kraus_matrices = Quasar.InstructionArgument[broadcast(expr->convert(ComplexF64, v(expr)), raw_mat)::Matrix{ComplexF64} for raw_mat in raw_mats] + push!(v, (type="kraus", arguments=kraus_matrices, targets=targets, controls=Pair{Int,Int}[], exponent=1.0)) + else + args = map(Float64, v(raw_args)) + push!(v, (type=noise_type, arguments=Quasar.InstructionArgument[args...], targets=targets, controls=Pair{Int,Int}[], exponent=1.0)) + end + # elseif pragma_type == :verbatim # a no-op for now + end + return v +end + +function parse_pragma_observables(tokens::Vector{Tuple{Int64, Int32, Quasar.Token}}, stack, start, qasm) + observables_list = Quasar.QasmExpression[] + obs_targets = Quasar.QasmExpression[] + while !isempty(tokens) + 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) + # next token is targets + h_mat = Quasar.parse_matrix(matrix_tokens, stack, start, qasm) + # next token is targets + next_at = findfirst(triplet->triplet[end] == Quasar.at, tokens) + final_token = isnothing(next_at) ? length(tokens) : next_at-1 + target_tokens = splice!(tokens, 1:final_token) + if !(isempty(target_tokens) || first(target_tokens)[end] == Quasar.all_token) + push!(target_tokens, (-1, Int32(-1), Quasar.semicolon)) + while !isempty(target_tokens) && first(target_tokens)[end] != Quasar.semicolon + target_tokens[1][end] == Quasar.comma && popfirst!(target_tokens) + target_expr = Quasar.parse_expression(target_tokens, stack, target_tokens[1][1], qasm) + push!(obs_targets, target_expr) + end + end + push!(observables_list, Quasar.QasmExpression(:hermitian, h_mat)) + elseif observable_id.args[1] == "all" + break + else + if !isempty(tokens) && first(tokens)[end] == Quasar.lparen + arg_tokens = Quasar.extract_parensed(tokens, 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) + end + push!(observables_list, observable_id) + end + !isempty(tokens) && first(tokens)[end] == Quasar.at && popfirst!(tokens) + end + if length(observables_list) == 1 && length(obs_targets) == 1 + return Quasar.QasmExpression(:observable, only(observables_list)), Quasar.QasmExpression(:qubit_targets, only(obs_targets)) + elseif length(observables_list) == 1 && length(obs_targets) == 0 + return Quasar.QasmExpression(:observable, only(observables_list)), Quasar.QasmExpression(:qubit_targets) + elseif length(observables_list) == 1 && length(obs_targets) > 1 + return Quasar.QasmExpression(:observable, only(observables_list)), Quasar.QasmExpression(:qubit_targets, Quasar.QasmExpression(:array_literal, obs_targets...)) + else + return Quasar.QasmExpression(:observable, Quasar.QasmExpression(:array_literal, observables_list...)), Quasar.QasmExpression(:qubit_targets, Quasar.QasmExpression(:array_literal, obs_targets...)) + end +end + +function parse_pragma_targets(tokens::Vector{Tuple{Int64, Int32, Quasar.Token}}, stack, start, qasm) + target_expr = Quasar.QasmExpression(:qubit_targets) + (isempty(tokens) || first(tokens)[end] == Quasar.all_token) && return target_expr + push!(tokens, (-1, Int32(-1), Quasar.semicolon)) + push!(target_expr, Quasar.parse_list_expression(tokens, stack, start, qasm)) + return target_expr +end + + +function Quasar.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)) + expr = Quasar.QasmExpression(:pragma) + pragma_type = Quasar.parse_identifier(popfirst!(tokens), qasm).args[1] + if pragma_type == "result" + push!(expr, :result) + result_type = Quasar.parse_identifier(popfirst!(tokens), qasm).args[1] + if result_type == "state_vector" + push!(expr, :state_vector) + elseif result_type == "probability" + target_expr = parse_pragma_targets(tokens, stack, start, qasm) + push!(expr, :probability, target_expr) + elseif result_type == "density_matrix" + target_expr = parse_pragma_targets(tokens, stack, start, qasm) + push!(expr, :density_matrix, target_expr) + elseif result_type == "amplitude" + push!(tokens, (-1, Int32(-1), Quasar.semicolon)) + states = Quasar.parse_list_expression(tokens, stack, start, qasm) + push!(expr, :amplitude, states) + elseif result_type ∈ ("expectation", "variance", "sample") + obs, targets = parse_pragma_observables(tokens, stack, start, qasm) + push!(expr, Symbol(result_type), obs, targets) + elseif result_type == "adjoint_gradient" + push!(expr, :adjoint_gradient) + end + elseif pragma_type == "unitary" + push!(expr, :unitary) + matrix_tokens = Quasar.extract_parensed(tokens, stack, start, qasm) + unitary_matrix = Quasar.parse_matrix(matrix_tokens, stack, start, qasm) + push!(expr, unitary_matrix) + target_expr = parse_pragma_targets(tokens, stack, start, qasm) + push!(expr, target_expr) + elseif pragma_type == "noise" + 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)) + mats = Matrix{Quasar.QasmExpression}[] + while !isempty(matrix_tokens) + push!(mats, Quasar.parse_matrix(matrix_tokens, stack, start, qasm)) + isempty(matrix_tokens) && break + next_token = first(matrix_tokens) + next_token[end] == Quasar.comma && popfirst!(matrix_tokens) + next_token[end] == Quasar.semicolon && break + end + noise_args = Quasar.QasmExpression(:arguments, Quasar.QasmExpression(:array_literal, mats)) + else + noise_args = Quasar.parse_arguments_list(tokens, stack, start, qasm) + end + push!(expr, noise_type, noise_args) + target_expr = parse_pragma_targets(tokens, stack, start, qasm) + push!(expr, target_expr) + elseif pragma_type == "verbatim" + # check that the next non-newline is a box token + push!(expr, :verbatim) + else + throw(Quasar.QasmParseError("invalid type $pragma_type for pragma", stack, start, qasm)) + end + return expr +end diff --git a/src/results.jl b/src/results.jl index 68cb111..83e2f32 100644 --- a/src/results.jl +++ b/src/results.jl @@ -9,6 +9,8 @@ See also: [`Expectation`](@ref), [`Variance`](@ref), [`DensityMatrix`](@ref), and [`Amplitude`](@ref). """ abstract type Result end +StructTypes.StructType(::Type{Result}) = StructTypes.AbstractType() +StructTypes.subtypes(::Type{Result}) = (amplitude=Amplitude, expectation=Expectation, variance=Variance, sample=Sample, state_vector=StateVector, density_matrix=DensityMatrix, probability=Probability) for (typ, ir_typ, label) in ((:Expectation, :(IR.Expectation), "expectation"), (:Variance, :(IR.Variance), "variance"), (:Sample, :(IR.Sample), "sample")) @eval begin @@ -40,6 +42,7 @@ for (typ, ir_typ, label) in ((:Expectation, :(IR.Expectation), "expectation"), ( end label(::$typ) = $label StructTypes.lower(x::$typ) = $ir_typ(StructTypes.lower(x.observable), (isempty(x.targets) ? nothing : Int.(x.targets)), $label) + StructTypes.constructfrom(::Type{$typ}, nt) = $typ(StructTypes.constructfrom(Observables.Observable, nt.operator), nt.targets) end end @@ -72,6 +75,7 @@ for (typ, ir_typ, label) in ((:Probability, :(IR.Probability), "probability"), ( $typ(targets::Vararg{IntOrQubit}) = $typ(QubitSet(targets...)) label(::$typ) = $label StructTypes.lower(x::$typ) = $ir_typ(isempty(x.targets) ? nothing : Int.(x.targets), $label) + StructTypes.constructfrom(::Type{$typ}, nt) = $typ(nt.targets) end end @@ -176,6 +180,7 @@ All elements of `states` must be `'0'` or `'1'`. Amplitude(s::String) = Amplitude([s]) Base.:(==)(a1::Amplitude, a2::Amplitude) = (a1.states == a2.states) label(::Amplitude) = "amplitude" +StructTypes.constructfrom(::Type{Amplitude}, nt) = Amplitude(nt.states) """ StateVector <: Result @@ -185,6 +190,7 @@ Struct which represents a state vector measurement on a [`Circuit`](@ref). struct StateVector <: Result end Base.:(==)(sv1::StateVector, sv2::StateVector) = true label(::StateVector) = "statevector" +StructTypes.constructfrom(::Type{StateVector}, nt) = StateVector() const ObservableResult = Union{Expectation, Variance, Sample} const ObservableParameterResult = Union{AdjointGradient,} diff --git a/test/test_custom_gates.jl b/test/test_custom_gates.jl index e82f3e4..7163d48 100644 --- a/test/test_custom_gates.jl +++ b/test/test_custom_gates.jl @@ -86,14 +86,14 @@ using Test, Logging, BraketSimulator, DataStructures @test BraketSimulator.qubit_count(BraketSimulator.U) == 1 @test inv(BraketSimulator.U(θ, ϕ, λ)) == BraketSimulator.U(θ, ϕ, λ, -1) end - @testset "MultiQubitPhaseShift" begin + @testset "GPhase" begin ϕ = 2.12 @testset "Simulator $sim, n_qubits $nq" for sim in (StateVectorSimulator, DensityMatrixSimulator), nq in 1:4 - instructions = vcat([BraketSimulator.Instruction(BraketSimulator.H(), [q]) for q in 0:nq-1], [BraketSimulator.Instruction(BraketSimulator.MultiQubitPhaseShift{nq}(ϕ), collect(0:nq-1))]) - u_instructions = vcat([BraketSimulator.Instruction(BraketSimulator.H(), [q]) for q in 0:nq-1], [BraketSimulator.Instruction(BraketSimulator.Unitary(Matrix(BraketSimulator.matrix_rep(BraketSimulator.MultiQubitPhaseShift{nq}(ϕ)))), collect(0:nq-1))]) + instructions = vcat([BraketSimulator.Instruction(BraketSimulator.H(), [q]) for q in 0:nq-1], [BraketSimulator.Instruction(BraketSimulator.GPhase{nq}(ϕ), collect(0:nq-1))]) + u_instructions = vcat([BraketSimulator.Instruction(BraketSimulator.H(), [q]) for q in 0:nq-1], [BraketSimulator.Instruction(BraketSimulator.Unitary(Matrix(BraketSimulator.matrix_rep(BraketSimulator.GPhase{nq}(ϕ)))), collect(0:nq-1))]) state_vector = exp(im*ϕ)/√(2^nq) * ones(2^nq) probability_amplitudes = 1/2^nq * ones(2^nq) - @test BraketSimulator.qubit_count(BraketSimulator.MultiQubitPhaseShift{nq}(ϕ)) == nq + @test BraketSimulator.qubit_count(BraketSimulator.GPhase{nq}(ϕ)) == nq @testset "BraketSimulator.Instruction set $ix_label" for (ix_label, ixs) in (("raw", instructions), ("unitary", u_instructions)) simulation = sim(nq, 0) simulation = BraketSimulator.evolve!(simulation, ixs) @@ -104,9 +104,9 @@ using Test, Logging, BraketSimulator, DataStructures collect(BraketSimulator.probabilities(simulation)) end end - @test BraketSimulator.qubit_count(BraketSimulator.MultiQubitPhaseShift{2}(ϕ)) == 2 - @test BraketSimulator.qubit_count(BraketSimulator.MultiQubitPhaseShift{3}(ϕ)) == 3 - @test inv(BraketSimulator.MultiQubitPhaseShift{2}(ϕ)) == BraketSimulator.MultiQubitPhaseShift{2}(ϕ, -1.0) + @test BraketSimulator.qubit_count(BraketSimulator.GPhase{2}(ϕ)) == 2 + @test BraketSimulator.qubit_count(BraketSimulator.GPhase{3}(ϕ)) == 3 + @test inv(BraketSimulator.GPhase{2}(ϕ)) == BraketSimulator.GPhase{2}(ϕ, -1.0) end @testset "MultiRz" begin ϕ = 2.12 @@ -158,9 +158,9 @@ using Test, Logging, BraketSimulator, DataStructures end @testset for nq in 1:2 ϕ = 2.12 - @test BraketSimulator.qubit_count(BraketSimulator.Control(BraketSimulator.MultiQubitPhaseShift{nq}(ϕ), ntuple(i->0, nq))) == nq - instructions = vcat([BraketSimulator.Instruction(BraketSimulator.H(), [q]) for q in 0:nq-1], [BraketSimulator.Instruction(BraketSimulator.Control(BraketSimulator.MultiQubitPhaseShift{nq}(ϕ), ntuple(i->0, nq)), collect(0:nq-1))]) - u = Matrix(BraketSimulator.matrix_rep(BraketSimulator.Control(BraketSimulator.MultiQubitPhaseShift{nq}(ϕ), ntuple(i->0, nq)))) + @test BraketSimulator.qubit_count(BraketSimulator.Control(BraketSimulator.GPhase{nq}(ϕ), ntuple(i->0, nq))) == nq + instructions = vcat([BraketSimulator.Instruction(BraketSimulator.H(), [q]) for q in 0:nq-1], [BraketSimulator.Instruction(BraketSimulator.Control(BraketSimulator.GPhase{nq}(ϕ), ntuple(i->0, nq)), collect(0:nq-1))]) + u = Matrix(BraketSimulator.matrix_rep(BraketSimulator.Control(BraketSimulator.GPhase{nq}(ϕ), ntuple(i->0, nq)))) u_instructions = vcat([BraketSimulator.Instruction(BraketSimulator.H(), [q]) for q in 0:nq-1], [BraketSimulator.Instruction(BraketSimulator.Unitary(u), collect(0:nq-1))]) state_vector = 1/√(2^nq) * ones(ComplexF64, 2^nq) state_vector[1] *= exp(im*ϕ) diff --git a/test/test_gate_operations.jl b/test/test_gate_operations.jl index c836961..1b96246 100644 --- a/test/test_gate_operations.jl +++ b/test/test_gate_operations.jl @@ -27,7 +27,7 @@ using Test, LinearAlgebra, Logging, BraketSimulator, DataStructures BraketSimulator.Vi(), BraketSimulator.U(θ, ϕ, λ), BraketSimulator.Unitary(q1_mat), - BraketSimulator.MultiQubitPhaseShift{1}(ϕ), + BraketSimulator.GPhase{1}(ϕ), ] @test inv(BraketSimulator.matrix_rep(g)) ≈ BraketSimulator.matrix_rep(inv(g)) @test BraketSimulator.matrix_rep(inv(g)) * BraketSimulator.matrix_rep(g) ≈ Diagonal(ones(2)) @@ -57,8 +57,8 @@ using Test, LinearAlgebra, Logging, BraketSimulator, DataStructures BraketSimulator.CY(), BraketSimulator.CZ(), BraketSimulator.CV(), - BraketSimulator.MultiQubitPhaseShift{2}(ϕ), - BraketSimulator.Control(BraketSimulator.MultiQubitPhaseShift{2}(ϕ), (0,0)), + BraketSimulator.GPhase{2}(ϕ), + BraketSimulator.Control(BraketSimulator.GPhase{2}(ϕ), (0,0)), BraketSimulator.Control(BraketSimulator.X(), (1,)), BraketSimulator.Control(BraketSimulator.U(θ, ϕ, λ), (1,))] @test inv(BraketSimulator.matrix_rep(g)) ≈ BraketSimulator.matrix_rep(inv(g)) @@ -101,7 +101,7 @@ using Test, LinearAlgebra, Logging, BraketSimulator, DataStructures BraketSimulator.Vi(), BraketSimulator.U(θ, ϕ, λ), BraketSimulator.Unitary(q1_mat), - BraketSimulator.MultiQubitPhaseShift{1}(ϕ) + BraketSimulator.GPhase{1}(ϕ) ] sim = StateVectorSimulator(nq, 0) new_sim = StateVectorSimulator(nq, 0) @@ -127,8 +127,8 @@ using Test, LinearAlgebra, Logging, BraketSimulator, DataStructures BraketSimulator.CPhaseShift10(ϕ), BraketSimulator.SingleExcitation(ϕ), BraketSimulator.Unitary(q2_mat), - BraketSimulator.Control(BraketSimulator.MultiQubitPhaseShift{2}(ϕ), (0,0)), - BraketSimulator.MultiQubitPhaseShift{2}(ϕ),] + BraketSimulator.Control(BraketSimulator.GPhase{2}(ϕ), (0,0)), + BraketSimulator.GPhase{2}(ϕ),] sim = StateVectorSimulator(nq, 0) new_sim = StateVectorSimulator(nq, 0) new_ixs = [BraketSimulator.Instruction(BraketSimulator.Unitary(Matrix(BraketSimulator.matrix_rep(g)^pow)), [0, 1])] diff --git a/test/test_openqasm3.jl b/test/test_openqasm3.jl index 0df44d6..4e02dbd 100644 --- a/test/test_openqasm3.jl +++ b/test/test_openqasm3.jl @@ -38,11 +38,10 @@ get_tol(shots::Int) = return ( visitor = Quasar.QasmProgramVisitor() bad_expression = Quasar.QasmExpression(:invalid_expression, Quasar.QasmExpression(:error)) @test_throws Quasar.QasmVisitorError("cannot visit expression $bad_expression.") visitor(bad_expression) - @test_throws Quasar.QasmVisitorError("unable to evaluate expression $bad_expression") Quasar.evaluate(visitor, bad_expression) bad_expression = Quasar.QasmExpression(:invalid_expression, Quasar.QasmExpression(:error)) @test_throws Quasar.QasmVisitorError("unable to evaluate qubits for expression $bad_expression.") Quasar.evaluate_qubits(visitor, bad_expression) cast_expression = Quasar.QasmExpression(:cast, Quasar.QasmExpression(:type_expr, Int32), Quasar.QasmExpression(:float_literal, 2.0)) - @test_throws Quasar.QasmVisitorError("unable to evaluate cast expression $cast_expression") Quasar.evaluate(visitor, cast_expression) + @test_throws Quasar.QasmVisitorError("unable to evaluate cast expression $cast_expression") visitor(cast_expression) end @testset "Visitors and parents" begin qasm = """ @@ -57,21 +56,21 @@ get_tol(shots::Int) = return ( visitor(parsed) for_visitor = Quasar.QasmForLoopVisitor(visitor) @test Quasar.function_defs(for_visitor) == Quasar.function_defs(visitor) - @test Quasar.qubit_defs(for_visitor) == Quasar.qubit_defs(visitor) - @test BraketSimulator.qubit_count(for_visitor) == Quasar.qubit_count(visitor) - push!(for_visitor, BraketSimulator.Amplitude("00")) - @test visitor.results[1] == BraketSimulator.Amplitude("00") - push!(for_visitor, [BraketSimulator.StateVector(), BraketSimulator.Probability()]) - @test visitor.results[2] == BraketSimulator.StateVector() - @test visitor.results[3] == BraketSimulator.Probability() - push!(for_visitor, [BraketSimulator.Instruction(BraketSimulator.X(), 0), BraketSimulator.Instruction(BraketSimulator.Y(), 1)]) - @test visitor.instructions == [BraketSimulator.Instruction(BraketSimulator.X(), 0), BraketSimulator.Instruction(BraketSimulator.X(), 0), BraketSimulator.Instruction(BraketSimulator.Y(), 1)] - gate_visitor = Quasar.QasmGateDefVisitor(visitor, Dict{String, FreeParameter}(), Dict{String, Quasar.Qubit}(), Dict{String, Vector{Int}}(), 0, BraketSimulator.Instruction[]) - push!(gate_visitor, [BraketSimulator.Instruction(BraketSimulator.X(), 0), BraketSimulator.Instruction(BraketSimulator.Y(), 1)]) - @test gate_visitor.instructions == [BraketSimulator.Instruction(BraketSimulator.X(), 0), BraketSimulator.Instruction(BraketSimulator.Y(), 1)] + @test Quasar.qubit_defs(for_visitor) == Quasar.qubit_defs(visitor) + @test Quasar.qubit_count(for_visitor) == Quasar.qubit_count(visitor) + push!(for_visitor, (type=:amplitude, operator=Union{String,Matrix{ComplexF64}}[], targets=Int[], states=["00"])) + @test visitor.results[1] == (type=:amplitude, operator=Union{String,Matrix{ComplexF64}}[], targets=Int[], states=["00"]) + push!(for_visitor, [(type=:state_vector, operator=Union{String,Matrix{ComplexF64}}[], targets=Int[], states=String[]), (type=:probability, operator=Union{String,Matrix{ComplexF64}}[], targets=Int[], states=String[])]) + @test visitor.results[2] == (type=:state_vector, operator=Union{String,Matrix{ComplexF64}}[], targets=Int[], states=String[]) + @test visitor.results[3] == (type=:probability, operator=Union{String,Matrix{ComplexF64}}[], targets=Int[], states=String[]) + push!(for_visitor, [(type="x", arguments=Union{Symbol,Dates.Period,Real,Matrix{ComplexF64}}[], targets=[0], controls=Pair{Int,Int}[], exponent=1.0), (type="y", arguments=Union{Symbol,Dates.Period,Real,Matrix{ComplexF64}}[], targets=[1], controls=Pair{Int,Int}[], exponent=1.0)]) + @test visitor.instructions == [(type="x", arguments=Union{Symbol,Dates.Period,Real,Matrix{ComplexF64}}[], targets=[0], controls=Pair{Int,Int}[], exponent=1.0), (type="x", arguments=Union{Symbol,Dates.Period,Real,Matrix{ComplexF64}}[], targets=[0], controls=Pair{Int,Int}[], exponent=1.0), (type="y", arguments=Union{Symbol,Dates.Period,Real,Matrix{ComplexF64}}[], targets=[1], controls=Pair{Int,Int}[], exponent=1.0)] + gate_visitor = Quasar.QasmGateDefVisitor(visitor, String[], Quasar.QasmExpression(:empty), String[]) + push!(gate_visitor, [(type="x", arguments=Union{Symbol,Dates.Period,Real,Matrix{ComplexF64}}[], targets=[0], controls=Pair{Int,Int}[], exponent=1.0), (type="y", arguments=Union{Symbol,Dates.Period,Real,Matrix{ComplexF64}}[], targets=[1], controls=Pair{Int,Int}[], exponent=1.0)]) + @test gate_visitor.instructions == [(type="x", arguments=Union{Symbol,Dates.Period,Real,Matrix{ComplexF64}}[], targets=[0], controls=Pair{Int,Int}[], exponent=1.0), (type="y", arguments=Union{Symbol,Dates.Period,Real,Matrix{ComplexF64}}[], targets=[1], controls=Pair{Int,Int}[], exponent=1.0)] function_visitor = Quasar.QasmFunctionVisitor(visitor, Quasar.QasmExpression[], Quasar.QasmExpression[]) - push!(function_visitor, [BraketSimulator.Instruction(BraketSimulator.X(), 0), BraketSimulator.Instruction(BraketSimulator.Y(), 1)]) - @test function_visitor.instructions == [BraketSimulator.Instruction(BraketSimulator.X(), 0), BraketSimulator.Instruction(BraketSimulator.Y(), 1)] + push!(function_visitor, [(type="x", arguments=Union{Symbol,Dates.Period,Real,Matrix{ComplexF64}}[], targets=[0], controls=Pair{Int,Int}[], exponent=1.0), (type="y", arguments=Union{Symbol,Dates.Period,Real,Matrix{ComplexF64}}[], targets=[1], controls=Pair{Int,Int}[], exponent=1.0)]) + @test function_visitor.instructions == [(type="x", arguments=Union{Symbol,Dates.Period,Real,Matrix{ComplexF64}}[], targets=[0], controls=Pair{Int,Int}[], exponent=1.0), (type="y", arguments=Union{Symbol,Dates.Period,Real,Matrix{ComplexF64}}[], targets=[1], controls=Pair{Int,Int}[], exponent=1.0)] end @testset "Sized types" begin for (t_string, T) in (("SizedBitVector", Quasar.SizedBitVector), @@ -138,6 +137,7 @@ get_tol(shots::Int) = return ( parsed = parse_qasm(sv_adder_qasm) visitor = QasmProgramVisitor(Dict("a_in"=>3, "b_in"=>7)) visitor(parsed) + circ = BraketSimulator.to_circuit(visitor) @test visitor.qubit_count == 10 correct_instructions = [ BraketSimulator.Instruction(BraketSimulator.X(), BraketSimulator.QubitSet(6)) @@ -171,12 +171,12 @@ get_tol(shots::Int) = return ( BraketSimulator.Instruction(BraketSimulator.CNot(), BraketSimulator.QubitSet(4, 0)) BraketSimulator.Instruction(BraketSimulator.CNot(), BraketSimulator.QubitSet(0, 8)) ] - for (ix, c_ix) in zip(visitor.instructions, correct_instructions) + for (ix, c_ix) in zip(circ.instructions, correct_instructions) @test ix == c_ix end - @test visitor.results == BraketSimulator.Result[BraketSimulator.Probability(BraketSimulator.QubitSet(9, 5, 6, 7, 8)), - BraketSimulator.Probability(BraketSimulator.QubitSet(9)), - BraketSimulator.Probability(BraketSimulator.QubitSet(5, 6, 7, 8))] + @test circ.result_types == BraketSimulator.Result[BraketSimulator.Probability(BraketSimulator.QubitSet(9, 5, 6, 7, 8)), + BraketSimulator.Probability(BraketSimulator.QubitSet(9)), + BraketSimulator.Probability(BraketSimulator.QubitSet(5, 6, 7, 8))] end @testset "Randomized Benchmarking" begin qasm = """ @@ -192,7 +192,7 @@ get_tol(shots::Int) = return ( h q[0]; measure q -> c; """ - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) @test circuit.instructions == [BraketSimulator.Instruction(BraketSimulator.H(), 0), BraketSimulator.Instruction(BraketSimulator.CZ(), [0, 1]), BraketSimulator.Instruction(BraketSimulator.S(), 0), @@ -208,7 +208,7 @@ get_tol(shots::Int) = return ( qubit[2] q; measure q; """ - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) @test circuit.instructions == [BraketSimulator.Instruction(BraketSimulator.Measure(), 0), BraketSimulator.Instruction(BraketSimulator.Measure(), 1)] @test [qubit_count(ix.operator) for ix in circuit.instructions] == [1, 1] @@ -225,7 +225,7 @@ get_tol(shots::Int) = return ( measure qs; measure q; """ - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) simulation = BraketSimulator.DensityMatrixSimulator(4, 0) BraketSimulator.evolve!(simulation, circuit.instructions) @test BraketSimulator.probabilities(simulation) ≈ (1/2)*[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0] @@ -254,13 +254,57 @@ get_tol(shots::Int) = return ( negctrl @ ctrl @ gphase(2 * π) qs[0], qs[1]; #pragma braket result amplitude '00', '01', '10', '11' """ - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) simulation = BraketSimulator.StateVectorSimulator(2, 0) BraketSimulator.evolve!(simulation, circuit.instructions) sv = 1/√2 * [-1; 0; 0; 1] @test simulation.state_vector ≈ sv @test circuit.result_types == [BraketSimulator.Amplitude(["00", "01", "10", "11"])] end + @testset "Casting" begin + @testset "Casting to $to_type from $from_type" for (to_type, to_value) in (("bool", true),), (from_type, from_value) in (("int[32]", "32",), + ("uint[16]", "1",), + ("float", "2.5",), + ("bool", "true",), + ("bit", "\"1\"",), + ) + qasm = """ + $from_type a = $from_value; + $to_type b = $to_type(a); + """ + parsed = parse_qasm(qasm) + visitor = QasmProgramVisitor() + visitor(parsed) + @test visitor.classical_defs["b"].val == to_value + end + @testset "Casting to $to_type from $from_type" for (to_type, to_value) in (("uint[32]", 32), ("int[16]", 32), ("float", 32.0)), (from_type, from_value) in (("int[32]", "32",), + ("uint[16]", "32",), + ("float", "32.0",), + ("bit[6]", "\"100000\"",), + ) + qasm = """ + $from_type a = $from_value; + $to_type b = $to_type(a); + """ + parsed = parse_qasm(qasm) + visitor = QasmProgramVisitor() + visitor(parsed) + @test visitor.classical_defs["b"].val == to_value + end + @testset "Casting to $to_type from $from_type" for (to_type, to_value) in (("bit[6]", BitVector([1,0,0,0,0,0])),), (from_type, from_value) in (("int[32]", "32",), + ("uint[16]", "32",), + ("bit[6]", "\"100000\"",), + ) + qasm = """ + $from_type a = $from_value; + $to_type b = $to_type(a); + """ + parsed = parse_qasm(qasm) + visitor = QasmProgramVisitor() + visitor(parsed) + @test visitor.classical_defs["b"].val == to_value + end + end @testset "Numbers $qasm_str" for (qasm_str, var_name, output_val) in (("float[32] a = 1.24e-3;", "a", 1.24e-3), ("complex[float] b = 1-0.23im;", "b", 1-0.23im), ("const bit c = \"0\";", "c", falses(1)), @@ -281,6 +325,30 @@ get_tol(shots::Int) = return ( visitor(parsed) @test visitor.classical_defs[var_name].val == output_val end + @testset "Qubit identifiers" begin + qasm = """ + qubit[2] q; + """ + parsed = parse_qasm(qasm) + visitor = QasmProgramVisitor() + visitor(parsed) + @test visitor(Quasar.QasmExpression(:identifier, "q")) == [0, 1] + @test visitor(Quasar.QasmExpression(:indexed_identifier, Quasar.QasmExpression(:identifier, "q"), Quasar.QasmExpression(:integer_literal, 1))) == [1] + @test_throws Quasar.QasmVisitorError("no identifier p defined.") visitor(Quasar.QasmExpression(:identifier, "p")) + @test_throws Quasar.QasmVisitorError("no identifier p defined.") visitor(Quasar.QasmExpression(:indexed_identifier, Quasar.QasmExpression(:identifier, "p"), Quasar.QasmExpression(:integer_literal, 1))) + end + @testset "Forbidden keywords" begin + qasm = """ + extern b; + """ + @test_throws Quasar.QasmParseError parse_qasm(qasm) + try + parse_qasm(qasm) + catch e + msg = sprint(showerror, e) + @test startswith(msg, "QasmParseError: keyword extern not supported.") + end + end @testset "Integers next to irrationals" begin qasm = """ float[64] h = 2π; @@ -293,6 +361,19 @@ get_tol(shots::Int) = return ( @test startswith(msg, "QasmParseError: expressions of form 2π are not supported -- you must separate the terms with a * operator.") end end + @testset "Gate def with argument manipulation" begin + qasm = """ + qubit[2] __qubits__; + gate u3(θ, ϕ, λ) q { + gphase(-(ϕ+λ)/2); + U(θ, ϕ, λ) q; + } + u3(0.1, 0.2, 0.3) __qubits__[0]; + """ + circ = BraketSimulator.to_circuit(qasm) + canonical_ixs = [BraketSimulator.Instruction(BraketSimulator.GPhase{1}(-(0.2 + 0.3)/2), 0), BraketSimulator.Instruction(BraketSimulator.U(0.1, 0.2, 0.3), 0)] + @test circ.instructions == canonical_ixs + end @testset "Bad classical type" begin qasm = """ angle[64] h = 2π; @@ -307,7 +388,7 @@ get_tol(shots::Int) = return ( parsed = parse_qasm(qasm) visitor = QasmProgramVisitor() visitor(parsed) - circ = Quasar.to_circuit(visitor) + circ = BraketSimulator.to_circuit(visitor) @test BraketSimulator.qubit_count(circ) == 2 @test circ.instructions == [BraketSimulator.Instruction(BraketSimulator.H(), 0), BraketSimulator.Instruction(BraketSimulator.CNot(), [0, 1])] end @@ -334,7 +415,7 @@ get_tol(shots::Int) = return ( __bit_0__[3] = measure __qubits__[3]; """ inputs = Dict("theta"=>0.2) - parsed_circ = Quasar.to_circuit(qasm_str, inputs) + parsed_circ = BraketSimulator.to_circuit(qasm_str, inputs) deleteat!(parsed_circ.instructions, length(parsed_circ.instructions)-3:length(parsed_circ.instructions)) @test parsed_circ.instructions == [BraketSimulator.Instruction(BraketSimulator.H(), 0), BraketSimulator.Instruction(BraketSimulator.CNot(), [0, 1]), @@ -524,7 +605,7 @@ get_tol(shots::Int) = return ( x = 1.0 y = 2.0 inputs = Dict("x"=>x, "y"=>y) - parsed_circ = Quasar.to_circuit(qasm, inputs) + parsed_circ = BraketSimulator.to_circuit(qasm, inputs) ixs = [BraketSimulator.Instruction(BraketSimulator.Rx(x), 0), BraketSimulator.Instruction(BraketSimulator.Rx(acos(x)), 0), BraketSimulator.Instruction(BraketSimulator.Rx(asin(x)), 0), @@ -572,7 +653,7 @@ get_tol(shots::Int) = return ( delay[$duration] q[0], q[1]; """ @test_warn "delay expression encountered -- currently `delay` is a no-op" parse_qasm(qasm) - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) @test circuit.instructions == [BraketSimulator.Instruction(BraketSimulator.X(), 0), BraketSimulator.Instruction(BraketSimulator.Delay(ix), 0), BraketSimulator.Instruction(BraketSimulator.Delay(ix), 1), @@ -596,7 +677,7 @@ get_tol(shots::Int) = return ( #pragma braket result state_vector """ @test_warn "barrier expression encountered -- currently `barrier` is a no-op" parse_qasm(qasm) - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) @test circuit.instructions == [BraketSimulator.Instruction(BraketSimulator.X(), 0), BraketSimulator.Instruction(BraketSimulator.Barrier(), 0)] simulation = BraketSimulator.StateVectorSimulator(4, 0) ref_circ = BraketSimulator.Circuit() @@ -614,12 +695,16 @@ get_tol(shots::Int) = return ( stretch a; """ @test_warn "stretch expression encountered -- currently `stretch` is a no-op" parse_qasm(qasm) + visitor = Quasar.QasmProgramVisitor() + @test visitor(Quasar.QasmExpression(:stretch)) === visitor end @testset "Duration" begin qasm = """ duration a = 10μs; """ @test_warn "duration expression encountered -- currently `duration` is a no-op" parse_qasm(qasm) + visitor = Quasar.QasmProgramVisitor() + @test visitor(Quasar.QasmExpression(:duration)) === visitor end @testset "Reset" begin qasm = """ @@ -628,7 +713,7 @@ get_tol(shots::Int) = return ( reset q[0]; """ @test_warn "reset expression encountered -- currently `reset` is a no-op" parse_qasm(qasm) - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) @test circuit.instructions == [BraketSimulator.Instruction(BraketSimulator.X(), 0), BraketSimulator.Instruction(BraketSimulator.Reset(), 0)] simulation = BraketSimulator.StateVectorSimulator(4, 0) ref_circ = BraketSimulator.Circuit() @@ -766,7 +851,7 @@ get_tol(shots::Int) = return ( y \$2; h \$3; """ - circ = Quasar.to_circuit(qasm) + circ = BraketSimulator.to_circuit(qasm) @test circ.instructions == [BraketSimulator.Instruction(BraketSimulator.Z(), 0), BraketSimulator.Instruction(BraketSimulator.X(), 1), BraketSimulator.Instruction(BraketSimulator.H(), 2), @@ -780,35 +865,35 @@ get_tol(shots::Int) = return ( default { z \$0; } } """ - circ = Quasar.to_circuit(qasm, Dict("x"=> -1)) + circ = BraketSimulator.to_circuit(qasm, Dict("x"=> -1)) @test isempty(circ.instructions) - circ = Quasar.to_circuit(qasm, Dict("x"=> 0)) + circ = BraketSimulator.to_circuit(qasm, Dict("x"=> 0)) @test circ.instructions == [BraketSimulator.Instruction(BraketSimulator.Z(), 0)] qasm = """ input int[8] x; switch (x) { case 0 {} case 1, 2 { z \$0; } } """ - circ = Quasar.to_circuit(qasm, Dict("x"=> 0)) + circ = BraketSimulator.to_circuit(qasm, Dict("x"=> 0)) @test isempty(circ.instructions) - circ = Quasar.to_circuit(qasm, Dict("x"=> 1)) + circ = BraketSimulator.to_circuit(qasm, Dict("x"=> 1)) @test circ.instructions == [BraketSimulator.Instruction(BraketSimulator.Z(), 0)] - circ = Quasar.to_circuit(qasm, Dict("x"=> 2)) + circ = BraketSimulator.to_circuit(qasm, Dict("x"=> 2)) @test circ.instructions == [BraketSimulator.Instruction(BraketSimulator.Z(), 0)] qasm = """ input int[8] x; switch (x) { case 0 {} default { z \$0; } default { x \$0; } } """ - @test_throws Quasar.QasmParseError Quasar.to_circuit(qasm, Dict("x"=>0)) + @test_throws Quasar.QasmParseError BraketSimulator.to_circuit(qasm, Dict("x"=>0)) qasm = """ input int[8] x; switch (x) { default { z \$0; } case 0 {} } """ - @test_throws Quasar.QasmParseError Quasar.to_circuit(qasm, Dict("x"=>0)) + @test_throws Quasar.QasmParseError BraketSimulator.to_circuit(qasm, Dict("x"=>0)) qasm = """ input int[8] x; switch (x) { case 0 { z \$0; } true {} } """ - @test_throws Quasar.QasmParseError Quasar.to_circuit(qasm, Dict("x"=>0)) + @test_throws Quasar.QasmParseError BraketSimulator.to_circuit(qasm, Dict("x"=>0)) end @testset "If/Else" begin qasm = """ @@ -860,7 +945,7 @@ get_tol(shots::Int) = return ( } """ for (flag, which_gate) in ((true, BraketSimulator.X()), (false, BraketSimulator.Y())) - circuit = Quasar.to_circuit(qasm, Dict("which_gate"=>flag)) + circuit = BraketSimulator.to_circuit(qasm, Dict("which_gate"=>flag)) @test circuit.instructions == [BraketSimulator.Instruction(which_gate, 0)] end end @@ -873,7 +958,7 @@ get_tol(shots::Int) = return ( h q2; ctrl @ s q1, q2; """ - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) simulation = BraketSimulator.StateVectorSimulator(2, 0) BraketSimulator.evolve!(simulation, circuit.instructions) @test simulation.state_vector ≈ [0.5, 0.5, 0.5, 0.5im] @@ -903,7 +988,7 @@ get_tol(shots::Int) = return ( canonical_simulation = BraketSimulator.StateVectorSimulator(2, 0) BraketSimulator.evolve!(canonical_simulation, canonical_ixs) @testset "$title" for (title, qasm) in (("standard", standard_qasm), ("custom", custom_qasm)) - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) simulation = BraketSimulator.StateVectorSimulator(2, 0) BraketSimulator.evolve!(simulation, circuit.instructions) @test simulation.state_vector ≈ canonical_simulation.state_vector @@ -979,7 +1064,7 @@ get_tol(shots::Int) = return ( @testset "$title" for (title, qasm) in (("standard", standard_qasm), ("custom", custom_qasm) ) - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) simulation = BraketSimulator.StateVectorSimulator(5, 0) BraketSimulator.evolve!(simulation, circuit.instructions) sv = zeros(32) @@ -1025,7 +1110,22 @@ get_tol(shots::Int) = return ( ccx_2 q1, q2, q4; ccx_2 q1, q2, q5; """ - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) + expected_ixs = [ + BraketSimulator.Instruction(BraketSimulator.Control(BraketSimulator.U(π, 0, π), (1,)), [0, 1]), + BraketSimulator.Instruction(BraketSimulator.U(π, 0, π), 0), + BraketSimulator.Instruction(BraketSimulator.Control(BraketSimulator.U(π, 0, π), (1,)), [0, 1]), + BraketSimulator.Instruction(BraketSimulator.Control(BraketSimulator.U(π, 0, π), (1,1)), [0, 3, 2]), + BraketSimulator.Instruction(BraketSimulator.Control(BraketSimulator.U(π, 0, π), (1,1)), [0, 2, 3]), + BraketSimulator.Instruction(BraketSimulator.Control(BraketSimulator.U(π, 0, π), (1,1)), [0, 2, 4]), + BraketSimulator.Instruction(BraketSimulator.Control(BraketSimulator.U(π, 0, π), (1,1)), [0, 1, 2]), + BraketSimulator.Instruction(BraketSimulator.Control(BraketSimulator.U(π, 0, π), (1,1)), [0, 1, 3]), + BraketSimulator.Instruction(BraketSimulator.Control(BraketSimulator.U(π, 0, π), (1,1)), [0, 1, 4]), + ] + @test length(circuit.instructions) == length(expected_ixs) + @testset for ix in 1:length(expected_ixs) + @test circuit.instructions[ix] == expected_ixs[ix] + end simulation = BraketSimulator.StateVectorSimulator(5, 0) sv = zeros(ComplexF64, 32) sv[end] = 1.0 @@ -1075,7 +1175,7 @@ get_tol(shots::Int) = return ( cccx q1, q2, q5, q3; ncccx q4, q2, q5, q3; """ - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) @test circuit.instructions == [BraketSimulator.Instruction(BraketSimulator.H(), 0), BraketSimulator.Instruction(BraketSimulator.H(), 1), BraketSimulator.Instruction(BraketSimulator.H(), 2), @@ -1135,7 +1235,7 @@ get_tol(shots::Int) = return ( t q; inv @ t q; """ - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) collapsed = prod(BraketSimulator.matrix_rep(ix.operator) for ix in circuit.instructions) @test collapsed ≈ diagm(ones(ComplexF64, 2^BraketSimulator.qubit_count(circuit))) end @@ -1159,7 +1259,7 @@ get_tol(shots::Int) = return ( #pragma braket noise $noise($arg) $qubits """ - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) @test only(circuit.instructions) == ix end @testset "StandardObservable/target mismatch" begin @@ -1187,7 +1287,7 @@ get_tol(shots::Int) = return ( #pragma braket result variance x(q[0]) @ y(q[1]) #pragma braket result sample x(q[0]) """ - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) BraketSimulator.basis_rotation_instructions!(circuit) c_bris = [circuit.basis_rotation_instructions[1], BraketSimulator.Instruction(BraketSimulator.Unitary(Matrix(mapreduce(ix->BraketSimulator.matrix_rep(ix.operator), *, circuit.basis_rotation_instructions[2:end]))), [1])] bris = vcat(BraketSimulator.Instruction(BraketSimulator.H(), [0]), BraketSimulator.diagonalizing_gates(BraketSimulator.Observables.Y(), [1])) @@ -1205,7 +1305,7 @@ get_tol(shots::Int) = return ( #pragma braket result variance x(q[0]) @ y(q[1]) #pragma braket result sample i(q[0]) """ - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) BraketSimulator.basis_rotation_instructions!(circuit) c_bris = [circuit.basis_rotation_instructions[1], BraketSimulator.Instruction(BraketSimulator.Unitary(Matrix(mapreduce(ix->BraketSimulator.matrix_rep(ix.operator), *, circuit.basis_rotation_instructions[2:end]))), [1])] bris = vcat(BraketSimulator.Instruction(BraketSimulator.H(), [0]), BraketSimulator.diagonalizing_gates(BraketSimulator.Observables.Y(), [1])) @@ -1224,7 +1324,7 @@ get_tol(shots::Int) = return ( // # noqa: E501 #pragma braket result expectation x(q[2]) @ hermitian([[-6+0im, 2+1im, -3+0im, -5+2im], [2-1im, 0im, 2-1im, -5+4im], [-3+0im, 2+1im, 0im, -4+3im], [-5-2im, -5-4im, -4-3im, -6+0im]]) q[0:1] """ - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) BraketSimulator.basis_rotation_instructions!(circuit) arr = [-6 2+1im -3 -5+2im; 2-1im 0 2-1im -5+4im; @@ -1271,8 +1371,8 @@ get_tol(shots::Int) = return ( // unitary pragma for ccnot gate #pragma braket unitary([[1.0, 0, 0, 0, 0, 0, 0, 0], [0, 1.0, 0, 0, 0, 0, 0, 0], [0, 0, 1.0, 0, 0, 0, 0, 0], [0, 0, 0, 1.0, 0, 0, 0, 0], [0, 0, 0, 0, 1.0, 0, 0, 0], [0, 0, 0, 0, 0, 1.0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1.0], [0, 0, 0, 0, 0, 0, 1.0, 0]]) q """ - circuit = Quasar.to_circuit(unitary_qasm) - ref_circuit = Quasar.to_circuit(standard_qasm) + circuit = BraketSimulator.to_circuit(unitary_qasm) + ref_circuit = BraketSimulator.to_circuit(standard_qasm) simulation = BraketSimulator.StateVectorSimulator(3, 0) ref_sim = BraketSimulator.StateVectorSimulator(3, 0) BraketSimulator.evolve!(simulation, circuit.instructions) @@ -1316,7 +1416,7 @@ get_tol(shots::Int) = return ( phaseshift(-2) q; ccnot qs; """ - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) @test circuit.instructions[1] == BraketSimulator.Instruction(BraketSimulator.X(), 0) @test circuit.instructions[2] == BraketSimulator.Instruction(BraketSimulator.X(), 2) @test circuit.instructions[3] == BraketSimulator.Instruction(BraketSimulator.H(), 3) @@ -1336,7 +1436,7 @@ get_tol(shots::Int) = return ( cphaseshift(1) qs, q; phaseshift(-2) q; """ - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) simulation = BraketSimulator.StateVectorSimulator(4, 0) BraketSimulator.evolve!(simulation, circuit.instructions) @test simulation.state_vector ≈ (1/√2)*[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0] @@ -1355,7 +1455,7 @@ get_tol(shots::Int) = return ( b[0] = measure q[0]; b[1] = measure q[1]; """ - circuit = Quasar.to_circuit(with_verbatim) + circuit = BraketSimulator.to_circuit(with_verbatim) sim_w_verbatim = BraketSimulator.StateVectorSimulator(2, 0) BraketSimulator.evolve!(sim_w_verbatim, circuit.instructions) @@ -1371,7 +1471,7 @@ get_tol(shots::Int) = return ( b[0] = measure q[0]; b[1] = measure q[1]; """ - circuit = Quasar.to_circuit(without_verbatim) + circuit = BraketSimulator.to_circuit(without_verbatim) sim_wo_verbatim = BraketSimulator.StateVectorSimulator(2, 0) BraketSimulator.evolve!(sim_wo_verbatim, circuit.instructions) @@ -1404,7 +1504,7 @@ get_tol(shots::Int) = return ( qubit[2] qs; flip(qs[0]); """ - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) simulation = BraketSimulator.StateVectorSimulator(2, 0) BraketSimulator.evolve!(simulation, circuit.instructions) @test simulation.state_vector ≈ [0, 0, 1, 0] @@ -1515,7 +1615,7 @@ get_tol(shots::Int) = return ( $operation #pragma braket result state_vector """ - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) simulation = BraketSimulator.StateVectorSimulator(1, 0) BraketSimulator.evolve!(simulation, circuit.instructions) @test simulation.state_vector ≈ state_vector @@ -1528,7 +1628,7 @@ get_tol(shots::Int) = return ( h q[0]; ctrl @ x q[0], q[1]; """ - circuit = Quasar.to_circuit(qasm) + circuit = BraketSimulator.to_circuit(qasm) program = BraketSimulator.Program(circuit) simulation = BraketSimulator.StateVectorSimulator(2, 0) @test_throws BraketSimulator.ValidationError BraketSimulator.simulate(simulation, program, 0) @@ -1608,11 +1708,11 @@ get_tol(shots::Int) = return ( i q; } """ - circ = Quasar.to_circuit(qasm_string, Dict("x"=>"1011")) + circ = BraketSimulator.to_circuit(qasm_string, Dict("x"=>"1011")) end @testset "GRCS 16" begin simulator = BraketSimulator.StateVectorSimulator(16, 0) - circuit = Quasar.to_circuit(joinpath(@__DIR__, "grcs_16.qasm")) + circuit = BraketSimulator.to_circuit(joinpath(@__DIR__, "grcs_16.qasm")) BraketSimulator.evolve!(simulator, circuit.instructions) probs = BraketSimulator.probabilities(simulator) @test first(probs) ≈ 0.0000062 atol=1e-7 diff --git a/test/test_sv_simulator.jl b/test/test_sv_simulator.jl index 0f31e14..a96dea2 100644 --- a/test/test_sv_simulator.jl +++ b/test/test_sv_simulator.jl @@ -157,7 +157,7 @@ LARGE_TESTS = get(ENV, "BRAKET_SIM_LARGE_TESTS", "false") == "true" [0.44285171, 0.55714829], ), ( - [BraketSimulator.Instruction(BraketSimulator.H(), [0]), BraketSimulator.Instruction(BraketSimulator.MultiQubitPhaseShift{1}(0.15), [0])], + [BraketSimulator.Instruction(BraketSimulator.H(), [0]), BraketSimulator.Instruction(BraketSimulator.GPhase{1}(0.15), [0])], 1, [0.69916673 + 0.10566872im, 0.69916673 + 0.10566872im], [0.5, 0.5], diff --git a/test/test_validation.jl b/test/test_validation.jl index 939a206..255f167 100644 --- a/test/test_validation.jl +++ b/test/test_validation.jl @@ -731,7 +731,7 @@ using Test, BraketSimulator #pragma braket result expectation z(q[3]) @ z(q[4]) @ z(q[5]) @ z(q[6]) @ z(q[7]) #pragma braket result expectation z(q[0]) @ z(q[3]) @ z(q[4]) @ z(q[5]) @ z(q[6]) @ z(q[7]) """ - circ = BraketSimulator.Quasar.to_circuit(source, inputs) + circ = BraketSimulator.to_circuit(source, inputs) @test isnothing(BraketSimulator._verify_openqasm_shots_observables(circ, 8)) # differs from source above by change of one Z to X in observables list bad_source = """ @@ -1399,7 +1399,7 @@ using Test, BraketSimulator #pragma braket result expectation z(q[3]) @ z(q[4]) @ z(q[5]) @ z(q[6]) @ z(q[7]) #pragma braket result expectation z(q[0]) @ z(q[3]) @ z(q[4]) @ z(q[5]) @ x(q[6]) @ z(q[7]) """ - circ = BraketSimulator.Quasar.to_circuit(bad_source, inputs) + circ = BraketSimulator.to_circuit(bad_source, inputs) @test_throws ValidationError BraketSimulator._verify_openqasm_shots_observables(circ, 8) end end