diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 8611805a..ac15c2a7 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -170,5 +170,5 @@ jobs: run: | using Documenter: DocMeta, doctest using Braket - DocMeta.setdocmeta!(Braket, :DocTestSetup, :(using Braket); recursive=true) + DocMeta.setdocmeta!(Braket, :DocTestSetup, :(using Braket, Braket.Dates); recursive=true) doctest(Braket) diff --git a/Project.toml b/Project.toml index f95d974d..2d7576d3 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Braket" uuid = "19504a0f-b47d-4348-9127-acc6cc69ef67" authors = ["Katharine Hyatt "] -version = "0.9.4" +version = "0.9.5" [deps] AWS = "fbe9abb3-538b-5e4e-ba9e-bc94f4f92ebc" @@ -33,7 +33,7 @@ Tar = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" [compat] -AWS = "=1.91.0" +AWS = "=1.92.0" AWSS3 = "=0.11.2" Aqua = "=0.8" AxisArrays = "=0.4.7" @@ -50,12 +50,12 @@ Downloads = "1" Graphs = "=1.11.2" HTTP = "=1.10.8" InteractiveUtils = "1.6" -JLD2 = "=0.4.51" +JLD2 = "=0.5.4" JSON3 = "=1.14.0" LinearAlgebra = "1.6" Logging = "1.6" Markdown = "=0.7.5" -Mocking = "=0.7.9" +Mocking = "=0.8.1" NamedTupleTools = "=0.14.3" OrderedCollections = "=1.6.3" Pkg = "1.6" @@ -63,7 +63,7 @@ Random = "1.6" SparseArrays = "1.6" StaticArrays = "=1.9.7" Statistics = "1.6" -StructTypes = "=1.10.0" +StructTypes = "=1.11.0" Tar = "1.9.3" Test = "1.6" UUIDs = "1.6" diff --git a/PyBraket/Project.toml b/PyBraket/Project.toml index df115738..0aafbf02 100644 --- a/PyBraket/Project.toml +++ b/PyBraket/Project.toml @@ -1,7 +1,7 @@ name = "PyBraket" uuid = "e85266a6-1825-490b-a80e-9b9469c53660" authors = ["Katharine Hyatt "] -version = "0.9.4" +version = "0.9.5" [deps] Braket = "19504a0f-b47d-4348-9127-acc6cc69ef67" @@ -14,13 +14,13 @@ StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" [compat] Aqua = "=0.8" -Braket = "=0.9.4" +Braket = "=0.9.5" CondaPkg = "=0.2.23" DataStructures = "=0.18.20" LinearAlgebra = "1.6" PythonCall = "=0.9.22" Statistics = "1" -StructTypes = "=1.10.0" +StructTypes = "=1.11.0" Test = "1.6" julia = "1.6" diff --git a/docs/make.jl b/docs/make.jl index e1ebc472..28c188fe 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,6 +1,6 @@ push!(LOAD_PATH,"../src/") -using Documenter, Braket +using Documenter, Braket, Braket.Dates makedocs(sitename="Braket.jl") diff --git a/docs/src/circuits.md b/docs/src/circuits.md index 79d7719a..de4718af 100644 --- a/docs/src/circuits.md +++ b/docs/src/circuits.md @@ -44,6 +44,12 @@ qubits qubit_count measure Measure +barrier +Barrier +reset(::Circuit, ::Any) +Reset +delay +Delay ``` ## Output to IR diff --git a/src/Braket.jl b/src/Braket.jl index ec4b5206..0ee1ef4b 100644 --- a/src/Braket.jl +++ b/src/Braket.jl @@ -8,7 +8,7 @@ export provider_name, properties, type export apply_gate_noise!, apply export logs, log_metric, metrics, @hybrid_job export depth, qubit_count, qubits, ir, IRType, OpenQASMSerializationProperties -export OpenQasmProgram, Measure, measure +export OpenQasmProgram, Measure, Reset, Barrier, Delay, measure, reset, barrier, delay export simulate export QueueDepthInfo, QueueType, Normal, Priority, queue_depth, queue_position diff --git a/src/circuit.jl b/src/circuit.jl index 2b363d09..30859829 100644 --- a/src/circuit.jl +++ b/src/circuit.jl @@ -354,6 +354,71 @@ function measure(c::Circuit, target_qubits) return _add_measure!(c, target_qubits) end +""" + reset(c::Circuit, target_qubits) -> Circuit + +Add a [`Reset`](@ref) operator to `c`, performing an active reset to the `|0>` state on the targeted qubits. +A `Reset` operation can be applied after a [`Measure`](@ref) to re-initialize the qubit and allow it to be reused +after mid-circuit measurement. + +# Examples +```jldoctest +julia> circ = Circuit([(H, 0), (CNot, 0, 1)]); + +julia> circ = reset(circ, 0); + +julia> circ.instructions +3-element Vector{Braket.Instruction}: + Braket.Instruction{H}(H(), QubitSet(0)) + Braket.Instruction{CNot}(CNot(), QubitSet(0, 1)) + Braket.Instruction{Reset}(Reset(), QubitSet(0)) +``` +""" +Base.reset(c::Circuit, target_qubits) = (foreach(t->add_instruction!(c, Instruction(Reset(), t)), target_qubits); return c) + +""" + delay(c::Circuit, duration::Dates.Period, target_qubits) -> Circuit + +Add a [`Delay`](@ref) operator to `c`, which forces all targeted qubits to wait for `duration` +before any new operations may be applied to any of them. + +# Examples +```jldoctest +julia> circ = Circuit([(H, 0), (CNot, 0, 1)]); + +julia> circ = delay(circ, Nanosecond(10), [0, 1]); + +julia> circ.instructions +3-element Vector{Braket.Instruction}: + Braket.Instruction{H}(H(), QubitSet(0)) + Braket.Instruction{CNot}(CNot(), QubitSet(0, 1)) + Braket.Instruction{Delay}(Delay(Nanosecond(10)), QubitSet(0, 1)) +``` +""" +delay(c::Circuit, duration::Dates.Period, target_qubits) = (add_instruction!(c, Instruction(Delay(duration), target_qubits)); return c) +# TODO enforce this in `Moments` as well + +""" + barrier(c::Circuit, target_qubits) -> Circuit + +Add a [`Barrier`](@ref) operator to `c`, which forces all targeted qubits to reach the barrier before any new +operations may be applied to any of them. + +# Examples +```jldoctest +julia> circ = Circuit([(H, 0), (CNot, 0, 1)]); + +julia> circ = barrier(circ, [0, 1]); + +julia> circ.instructions +3-element Vector{Braket.Instruction}: + Braket.Instruction{H}(H(), QubitSet(0)) + Braket.Instruction{CNot}(CNot(), QubitSet(0, 1)) + Braket.Instruction{Barrier}(Barrier(), QubitSet(0, 1)) +``` +""" +barrier(c::Circuit, target_qubits) = (add_instruction!(c, Instruction(Barrier(), target_qubits)); return c) + function openqasm_header(c::Circuit, sps::SerializationProperties=OpenQASMSerializationProperties()) ir_instructions = ["OPENQASM 3.0;"] for p in sort(string.(c.parameters)) diff --git a/src/operators.jl b/src/operators.jl index a1d93c51..835ada2d 100644 --- a/src/operators.jl +++ b/src/operators.jl @@ -30,9 +30,7 @@ struct PauliEigenvalues{N} end PauliEigenvalues(::Val{N}, coeff::Float64=1.0) where {N} = PauliEigenvalues{N}(coeff) Base.length(p::PauliEigenvalues{N}) where {N} = 2^N -function Base.iterate(p::PauliEigenvalues{N}, ix::Int=1) where {N} - return ix <= length(p) ? (p[ix], ix+1) : nothing -end +Base.iterate(p::PauliEigenvalues{N}, ix::Int=1) where {N} = ix <= length(p) ? (p[ix], ix+1) : nothing Base.getindex(p::PauliEigenvalues{1}, i::Int)::Float64 = getindex((p.coeff, -p.coeff), i) function Base.getindex(p::PauliEigenvalues{N}, i::Int)::Float64 where N @@ -71,8 +69,9 @@ end Measure() = Measure(-1) Parametrizable(m::Measure) = NonParametrized() chars(::Type{Measure}) = ("M",) -chars(m::Measure) = ("M",) +chars(::Measure) = chars(Measure) qubit_count(::Type{Measure}) = 1 +qubit_count(::Measure) = qubit_count(Measure) ir(m::Measure, target::QubitSet, ::Val{:JAQCD}; kwargs...) = error("measure instructions are not supported with JAQCD.") function ir(m::Measure, target::QubitSet, ::Val{:OpenQASM}; serialization_properties=OpenQASMSerializationProperties()) instructions = Vector{String}(undef, length(target)) @@ -83,3 +82,52 @@ function ir(m::Measure, target::QubitSet, ::Val{:OpenQASM}; serialization_proper end return join(instructions, "\n") end + +""" + Reset() <: QuantumOperator + +Represents an active reset operation on targeted qubit. +""" +struct Reset <: QuantumOperator end +Parametrizable(m::Reset) = NonParametrized() +chars(::Type{Reset}) = ("Reset",) +chars(::Reset) = chars(Reset) +label(::Reset) = "reset" +qubit_count(::Type{Reset}) = 1 +qubit_count(r::Reset) = qubit_count(Reset) + +""" + Barrier() <: QuantumOperator + +Represents a barrier operation on targeted qubit. +""" +struct Barrier <: QuantumOperator end +Parametrizable(b::Barrier) = NonParametrized() +chars(::Type{Barrier}) = ("Barrier",) +chars(r::Barrier) = chars(Barrier) +label(::Barrier) = "barrier" +qubit_count(::Type{Barrier}) = 1 +qubit_count(b::Barrier) = qubit_count(Barrier) + +""" + Delay(duration::Period) <: QuantumOperator + +Represents a delay operation for `duration` on targeted qubit. +""" +struct Delay <: QuantumOperator + duration::Dates.Period +end +Parametrizable(m::Delay) = NonParametrized() +chars(d::Delay) = ("Delay($(label(d.duration)))",) +label(d::Delay) = "delay[$(label(d.duration))]" +qubit_count(::Type{Delay}) = 1 +qubit_count(d::Delay) = qubit_count(Delay) +Base.:(==)(d1::Delay, d2::Delay) = d1.duration == d2.duration +label(d::Microsecond) = "$(d.value)ms" +label(d::Nanosecond) = "$(d.value)ns" +label(d::Second) = "$(d.value)s" + +ir(ix::Union{Reset, Barrier, Delay}, target::QubitSet, ::Val{:JAQCD}; kwargs...) = error("$(label(ix)) instructions are not supported with JAQCD.") +function ir(ix::Union{Reset, Barrier, Delay}, target::QubitSet, v::Val{:OpenQASM}; serialization_properties=OpenQASMSerializationProperties()) + return join(("$(label(ix)) $(format_qubits(qubit, serialization_properties));" for qubit in target), "\n") +end diff --git a/test/circuit_timing.jl b/test/circuit_timing.jl new file mode 100644 index 00000000..1c9f8fc0 --- /dev/null +++ b/test/circuit_timing.jl @@ -0,0 +1,53 @@ +using Braket, Braket.Dates, Test +using Braket: Instruction, VIRTUAL, PHYSICAL, OpenQASMSerializationProperties + +@testset "Barrier, reset, and delay operators" begin + @test Barrier() isa Braket.QuantumOperator + @test Reset() isa Braket.QuantumOperator + @test Braket.Parametrizable(Reset()) == Braket.NonParametrized() + @test Braket.Parametrizable(Barrier()) == Braket.NonParametrized() + @test Braket.Parametrizable(Delay(Microsecond(200))) == Braket.NonParametrized() + @test qubit_count(Reset()) == 1 + @test qubit_count(Barrier()) == 1 + circ = Circuit([(H, 0), (CNot, 0, 1)]) + circ = barrier(circ, 0) + @test circ.instructions == [Instruction{H}(H(), QubitSet(0)), Instruction{CNot}(CNot(), QubitSet(0, 1)), Instruction{Barrier}(Barrier(), QubitSet(0))] + circ = Circuit([(H, 0), (CNot, 0, 1)]) + circ = reset(circ, 0) + @test circ.instructions == [Instruction{H}(H(), QubitSet(0)), Instruction{CNot}(CNot(), QubitSet(0, 1)), Instruction{Reset}(Reset(), QubitSet(0))] + circ = Circuit([(H, 0), (CNot, 0, 1)]) + circ = delay(circ, Nanosecond(10), 0) + @test circ.instructions == [Instruction{H}(H(), QubitSet(0)), Instruction{CNot}(CNot(), QubitSet(0, 1)), Instruction{Delay}(Delay(Nanosecond(10)), QubitSet(0))] + @test qubit_count(Delay(Microsecond(200))) == 1 + @test Delay(Microsecond(200)) isa Braket.QuantumOperator + @testset "Equality" for t in (Barrier, Reset) + t1 = t() + t2 = t() + non_t = Measure() + @test t1 == t2 + @test t1 != non_t + end + @test Delay(Nanosecond(10)) != Delay(Microsecond(10)) + @test Delay(Nanosecond(10_000)) == Delay(Microsecond(10)) + @test Braket.chars(Barrier()) == ("Barrier",) + @test Braket.chars(Reset()) == ("Reset",) + @test Braket.chars(Delay(Nanosecond(4))) == ("Delay(4ns)",) + @test Braket.chars(Delay(Microsecond(4))) == ("Delay(4ms)",) + @testset "To IR" for (t, str) in ((Barrier(), "barrier"), + (Reset(), "reset"), + (Delay(Second(1)), "delay[1s]"), + ) + @testset "Invalid ir_type $ir_type" for (ir_type, message) in ((:JAQCD, "$str instructions are not supported with JAQCD."), + ) + @test_throws ErrorException(message) ir(t, QubitSet([0]), Val(ir_type)) + end + @testset "OpenQASM, target $target, serialization properties $sps" for (target, sps, expected_ir) in ( + ([0], OpenQASMSerializationProperties(qubit_reference_type=VIRTUAL), "$str q[0];"), + ([4], OpenQASMSerializationProperties(qubit_reference_type=PHYSICAL), "$str \$4;"), + ) + + + @test ir(Instruction(t, target), Val(:OpenQASM); serialization_properties=sps) == expected_ir + end + end +end diff --git a/test/measure.jl b/test/measure.jl index 6de55a18..2a252d27 100644 --- a/test/measure.jl +++ b/test/measure.jl @@ -3,6 +3,8 @@ using Braket: Instruction, VIRTUAL, PHYSICAL, OpenQASMSerializationProperties @testset "Measure operator" begin @test Measure() isa Braket.QuantumOperator + @test Braket.Parametrizable(Measure()) == Braket.NonParametrized() + @test qubit_count(Measure()) == 1 @testset "Equality" begin measure1 = Measure() measure2 = Measure() @@ -11,6 +13,9 @@ using Braket: Instruction, VIRTUAL, PHYSICAL, OpenQASMSerializationProperties @test measure1 != non_measure end @test Braket.chars(Measure()) == ("M",) + circ = Circuit([(H, 0), (CNot, 0, 1)]) + circ = measure(circ, 0) + @test circ.instructions == [Instruction{H}(H(), QubitSet(0)), Instruction{CNot}(CNot(), QubitSet(0, 1)), Instruction{Measure}(Measure(0), QubitSet(0))] @testset "To IR" begin @testset "Invalid ir_type $ir_type" for (ir_type, message) in ((:JAQCD, "measure instructions are not supported with JAQCD."), diff --git a/test/observables.jl b/test/observables.jl index 5dfd3a0a..39c42460 100644 --- a/test/observables.jl +++ b/test/observables.jl @@ -6,8 +6,12 @@ using LinearAlgebra: eigvals Braket.IRType[] = :JAQCD @testset "pauli eigenvalues" begin z = [1.0 0.0; 0.0 -1.0] + @test PauliEigenvalues(Val(1))[1] == 1.0 + @test PauliEigenvalues(Val(1))[2] == -1.0 + @test collect(PauliEigenvalues(Val(1))) == [1.0, -1.0] for n in 2:6 pe = PauliEigenvalues(Val(n)) + @test length(pe) == 2^n mat = kron(ntuple(i->diag(z), n)...) for ix in 1:2^n @test pe[ix] == mat[ix] diff --git a/test/runtests.jl b/test/runtests.jl index 91807f74..d3ccfc90 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -51,6 +51,7 @@ for group in groups include("device.jl") include("circuits.jl") include("measure.jl") + include("circuit_timing.jl") include("free_parameter.jl") include("gates.jl") include("observables.jl")