diff --git a/src/QuantumSavory.jl b/src/QuantumSavory.jl index b48fe54d..3219f191 100644 --- a/src/QuantumSavory.jl +++ b/src/QuantumSavory.jl @@ -16,7 +16,7 @@ export project_traceout! #TODO should move to QuantumInterface import ConcurrentSim using ResumableFunctions -import SumTypes: @sum_type +import SumTypes: @sum_type, isvariant import Combinatorics: powerset @reexport using QuantumSymbolics @@ -34,7 +34,7 @@ export Qubit, Qumode, QuantumStateTrait, UseAsState, UseAsObservable, UseAsOperation, AbstractBackground export QuantumChannel -export tag!, tag_types +export tag!, tag_types, W, ❓, query #TODO you can not assume you can always in-place modify a state. Have all these functions work on stateref, not stateref[] @@ -302,13 +302,17 @@ function Base.show(io::IO, net::RegisterNet) end function Base.show(io::IO, r::RegRef) - print(io, "Slot $(r.idx)/$(length(r.reg.traits)) of Register $(objectid(r.reg))") # TODO make this length call prettier - print(io, "\nContent:") - i,s = r.reg.stateindices[r.idx], r.reg.staterefs[r.idx] - if isnothing(s) - print(io, "\n nothing") + if get(io, :compact, false) | haskey(io, :typeinfo) + print(io, "Slot $(r.idx)") else - print(io, "\n $(i) @ $(typeof(s.state[]).name.module).$(typeof(s.state[]).name.name) $(objectid(s.state[]))") + print(io, "Slot $(r.idx)/$(length(r.reg.traits)) of Register $(objectid(r.reg))") # TODO make this length call prettier + print(io, "\nContent:") + i,s = r.reg.stateindices[r.idx], r.reg.staterefs[r.idx] + if isnothing(s) + print(io, "\n nothing") + else + print(io, "\n $(i) @ $(typeof(s.state[]).name.module).$(typeof(s.state[]).name.name) $(objectid(s.state[]))") + end end end diff --git a/src/queries.jl b/src/queries.jl index 19711e20..196b22f7 100644 --- a/src/queries.jl +++ b/src/queries.jl @@ -1,3 +1,6 @@ +"""Assign a tag to a slot in a register. + +See also: [`query`](@ref), [`tag_types`](@ref)""" function tag!(ref::RegRef, tag::Tag) push!(ref.reg.tags[ref.idx], tag) end @@ -9,18 +12,46 @@ struct Wildcard end """A wilcard instance for use with the tag querying functionality. -See also: [`query`](@ref), [`tag!`](@ref), [`tag_types`](@ref)""" +See also: [`query`](@ref), [`tag!`](@ref), [`tag_types`](@ref), [`Wildcard`](@ref)""" const W = Wildcard() -const ❓ +"""A wilcard instance for use with the tag querying functionality. + +See also: [`query`](@ref), [`tag!`](@ref), [`tag_types`](@ref), [`Wildcard`](@ref)""" +const ❓ = W + +""" A query function checking for the first slot in a register that has a given tag. + +It supports wildcards (instances of `Wildcard` also available as the constants [`W`](@ref) or [`❓`](@ref) which can be entered as `\\:question:` in the REPL). + +```jldoctest +julia> r = Register(10); + tag!(r[1], :symbol, 2, 3); + tag!(r[2], :symbol, 4, 5); + tag!(r[5], Int, 4, 5); + +julia> query(r, :symbol, 4, 5) +(Slot 2, SymbolIntInt(:symbol, 4, 5)::QuantumSavory.Tag) + +julia> query(r, :symbol, ❓, 3) +(Slot 1, SymbolIntInt(:symbol, 2, 3)::QuantumSavory.Tag) + +julia> query(r, :othersym, ❓, ❓) |> isnothing +true + +julia> query(r, Float64, 4, 5) |> isnothing +true +``` +""" function query(reg::Register, tag::Tag) i = findfirst(set -> tag ∈ set, reg.tags) - isnothing(i) ? nothing : (reg.refs[i], tag) + isnothing(i) ? nothing : (reg[i], tag) end -_query_and() = true -_query_and(a::Bool) = a -_query_and(a::Bool, b::Bool) = a && b +_query_all() = true +_query_all(a::Bool) = a +_query_all(a::Bool, b::Bool) = a && b +_query_all(a::Bool, b::Bool, c::Bool) = a && b && c # Create a query function for each combination of tag arguments and/or wildcard arguments for (tagsymbol, tagvariant) in pairs(tag_types) @@ -39,17 +70,18 @@ for (tagsymbol, tagvariant) in pairs(tag_types) int_idx_all = [i for (i,s) in enumerate(sig) if s == Int] int_idx_combs = powerset(int_idx_all, 1) for idx in int_idx_combs - complement_idx = tuple(setdiff(int_idx_all, idx)...) + complement_idx = tuple(setdiff(1:length(sig), idx)...) sig_wild = collect(sig) sig_wild[idx] .= Wildcard argssig_wild = [:($a::$t) for (a,t) in zip(args, sig_wild)] - nonwild_checks = [:(tag.data[i]==$(args[i])) for i in complement_idx] + nonwild_checks = [:(tag.data[$i]==$(args[i])) for i in complement_idx] eval(quote function query(reg::Register, $(argssig_wild...)) for (reg_idx, tags) in enumerate(reg.tags) for tag in tags - if isvariant(tag, $(tagsymbol)) - if _query_all($(nonwild_checks...)) end - return (reg_idx, tag) + if isvariant(tag, ($(tagsymbol,))[1]) # a weird workaround for interpolating a symbol as a symbol + if _query_all($(nonwild_checks...)) + return (reg[reg_idx], tag) + end end end end diff --git a/test/runtests.jl b/test/runtests.jl index 278c015d..b493e30d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -30,6 +30,8 @@ println("Starting tests with $(Threads.nthreads()) threads out of `Sys.CPU_THREA @doset "noninstant_and_backgrounds_qubit" @doset "noninstant_and_backgrounds_qumode" +@doset "tags_and_queries" + @doset "circuitzoo_api" @doset "circuitzoo_purification" @doset "circuitzoo_superdense" diff --git a/test/test_tags_and_queries.jl b/test/test_tags_and_queries.jl new file mode 100644 index 00000000..1cc81b35 --- /dev/null +++ b/test/test_tags_and_queries.jl @@ -0,0 +1,14 @@ +using QuantumSavory +using Test + +r = Register(10) +tag!(r[1], :symbol1, 2, 3) +tag!(r[2], :symbol1, 4, 5) +tag!(r[5], Int, 4, 5) + +@test query(r, :symbol1, 4, ❓) == (r[2], tag_types.SymbolIntInt(:symbol1, 4, 5)) +@test query(r, :symbol1, 4, 5) == (r[2], tag_types.SymbolIntInt(:symbol1, 4, 5)) +@test query(r, :symbol1, ❓, ❓) == (r[1], tag_types.SymbolIntInt(:symbol1, 2, 3)) +@test query(r, :symbol2, ❓, ❓) == nothing +@test query(r, Int, 4, 5) == (r[5], tag_types.TypeIntInt(Int, 4, 5)) +@test query(r, Float32, 4, 5) == nothing