Full API
Autogenerated API list
QuantumSavory.W — Constant
QuantumSavory.AbstractBackground — Type
An abstract type for the various background processes that might be inflicted upon a Register slot, e.g. decay, dephasing, etc.
QuantumSavory.AmplitudeDamping — Type
A depolarization background.
QuantumSavory.ConstantHamiltonianEvolution — Type
Represents a Hamiltonian being applied for the given duration. See also NonInstantGate.
QuantumSavory.Depolarization — Type
A depolarization background.
The τ parameter specifies the average time between depolarization events (assuming a Poisson point process). I.e. after time t the probability for an depolarization event is 1-exp(-t/τ).
QuantumSavory.MessageBuffer — Type
A a buffer for classical messages. Usually a part of a Register structure.
See also: channel, messagebuffer
QuantumSavory.NonInstantGate — Type
Represents an gate applied instantaneously followed by a waiting period. See also ConstantHamiltonianEvolution.
QuantumSavory.PauliNoise — Type
A Pauli noise background.
QuantumSavory.QuantumChannel — Type
Quantum channel for transmitting quantum states from one register to another.
Delay and background noise processes are supported.
The function put! is used to take the contents of a RegRef and put it in the channel. That state can can then be received by a register (after a delay) using the take! method.
julia> using QuantumSavory, ResumableFunctions, ConcurrentSim
julia> regA = Register(1); regB = Register(1);
julia> initialize!(regA[1], Z1);
julia> sim = Simulation();
julia> qc = QuantumChannel(sim, 10.0) # a delay of 10 units
QuantumChannel{Qubit}(Qubit(), DelayQueue{Register}(ConcurrentSim.QueueStore{Register, Int64}, 10.0), nothing)
julia> @resumable function alice_node(env, qc)
println("Putting Alice's qubit in the channel at ", now(env))
put!(qc, regA[1])
end
alice_node (generic function with 1 method)
julia> @resumable function bob_node(env, qc)
@yield take!(qc, regB[1])
println("Taking the qubit from alice at ", now(env))
end
bob_node (generic function with 1 method)
julia> @process alice_node(sim, qc); @process bob_node(sim, qc);
julia> run(sim)
Putting Alice's qubit in the channel at 0.0
Taking the qubit from alice at 10.0
julia> regA
Register with 1 slots: [ Qubit ]
Slots:
nothingQuantumSavory.QuantumStateTrait — Type
An abstract type for the various types of states that can be given to Register slots, e.g. qubit, harmonic oscillator, etc.
QuantumSavory.Qubit — Type
Specifies that a given register slot contains qubits.
QuantumSavory.Qumode — Type
Specifies that a given register slot contains qumodes.
QuantumSavory.RegRef — Type
A reference to a Register slot, convenient for use with functions like apply!, etc.
julia> r = Register(2)
initialize!(r[1], X₁)
observable(r[1], X)
0.9999999999999998 + 0.0imQuantumSavory.Register — Type
The main data structure in QuantumSavory, used to represent a quantum register in an arbitrary formalism.
QuantumSavory.RegisterNet — Type
A network of Registers with convenient graph API as well.
QuantumSavory.RegisterNet — Method
Construct a RegisterNet from a given list of Registers and a graph.
julia> graph = grid([2,2]) # from Graphs.jl
{4, 4} undirected simple Int64 graph
julia> registers = [Register(1), Register(2), Register(1), Register(2)]
4-element Vector{Register}:
Register with 1 slots: [ Qubit ]
Slots:
nothing
Register with 2 slots: [ Qubit | Qubit ]
Slots:
nothing
nothing
Register with 1 slots: [ Qubit ]
Slots:
nothing
Register with 2 slots: [ Qubit | Qubit ]
Slots:
nothing
nothing
julia> net = RegisterNet(graph, registers)
A network of 4 registers in a graph of 4 edges
julia> neighbors(net, 1) # from Graphs.jl
2-element Vector{Int64}:
2
3QuantumSavory.RegisterNet — Method
Construct a RegisterNet from a given list of Registers, defaulting to a chain topology.
julia> net = RegisterNet([Register(2), Register(4), Register(2)])
A network of 3 registers in a graph of 2 edges
julia> neighbors(net,2) # from Graphs.jl
2-element Vector{Int64}:
1
3QuantumSavory.T1Decay — Type
A background describing the T₁ decay of a two-level system.
QuantumSavory.T2Dephasing — Type
A background describing the T₂ dephasing of a two-level system.
QuantumSavory.Tag — Type
Tags are used to represent classical metadata describing the state (or even history) of nodes and their registers. The library allows the construction of custom tags using the Tag constructor. Currently tags are implemented as instances of a sum type and have fairly constrained structure. Most of them are constrained to contain only Symbol instances and integers.
Here is an example of such a generic tag:
julia> Tag(:sometagdescriptor, 1, 2, -3)
SymbolIntIntInt(:sometagdescriptor, 1, 2, -3)::TagA tag can have a custom DataType as first argument, in which case additional customizability in printing is available. E.g. consider the [EntanglementHistory] tag used to track how pairs were entangled before a swap happened.
julia> using QuantumSavory.ProtocolZoo: EntanglementHistory
julia> Tag(EntanglementHistory, 1, 2, 3, 4, 5)
Was entangled to 1.2, but swapped with .5 which was entangled to 3.4QuantumInterface.apply! — Method
Apply a given operation on the given set of register slots.
apply!([regA, regB], [slot1, slot2], Gates.CNOT) would apply a CNOT gate on the content of the given registers at the given slots. The appropriate representation of the gate is used, depending on the formalism under which a quantum state is stored in the given registers. The Hilbert spaces of the registers are automatically joined if necessary.
QuantumInterface.traceout! — Method
Delete the given slot of the given register.
traceout!(reg, slot) would reset (perform a partial trace) over the given subsystem. The Hilbert space of the register gets automatically shrunk.
QuantumSavory.channel — Method
Get a handle to a classical channel between two registers.
Usually used for sending classical messages between registers. It can be used for receiving as well, but a more convenient choice is messagebuffer, which is a message buffer listening to all channels sending to a given destination register.
julia> net = RegisterNet([Register(2), Register(2), Register(2)]) # defaults to a chain topology
A network of 3 registers in a graph of 2 edges
julia> channel(net, 1=>2)
ConcurrentSim.DelayQueue{Tag}(ConcurrentSim.QueueStore{Tag, Int64}, 0.0)
julia> channel(net, 1=>2)
ConcurrentSim.DelayQueue{Tag}(ConcurrentSim.QueueStore{Tag, Int64}, 0.0)
julia> channel(net, 1=>2) === channel(net, net[1]=>net[2])
trueSee also: qchannel, messagebuffer
QuantumSavory.findfreeslot — Method
Find an empty unlocked slot in a given Register.
julia> reg = Register(3); initialize!(reg[1], X); lock(reg[2]);
julia> findfreeslot(reg) == reg[3]
true
julia> lock(findfreeslot(reg));
julia> findfreeslot(reg) |> isnothing
trueQuantumSavory.generate_map — Function
Generates a default map with country and state boundaries and returns a GeoAxis. The returned GeoAxis can be used as an input for registernetplot_axis.
The Tyler package must be installed and imported.
QuantumSavory.initialize! — Method
Set the state of a given set of registers.
initialize!([regA,regB], [slot1,slot2], state) would set the state of the given slots in the given registers to state. state can be any supported state representation, e.g., kets or density matrices from QuantumOptics.jl or tableaux from QuantumClifford.jl.
QuantumSavory.krausops — Function
For a given background noise type, provide the corresponding Kraus operators, in a QuantumOptics.jl representation.
See also: paulinoise, lindbladop
QuantumSavory.krausops — Method
The Kraus operators for depolarization are √(1-3p/4) I, √p/2 * X, √p/2 * Y, √p/2 Z
QuantumSavory.krausops — Method
The Kraus operators for a T₁ process
A₁ = |0⟩⟨0| + √(1-γ) |1⟩⟨1|A₂ = √γ |0⟩⟨1|λ = 1 - exp(-Δt/T₁)
QuantumSavory.krausops — Method
The Kraus operators for a T₂ process
One option is the following (more popular in the literature):
P₁ = |0⟩⟨0| + √(1-λ) |1⟩⟨1|P₂ = √λ |1⟩⟨1|λ = 1 - exp(-2Δt/T₂)
An equivalent option is (more convenient when converting to a Pauli error channel):
P₁′ = √(1-p/2) IP₂′ = √(p/2) Zp = 1 - exp(-Δt/T₂)
These two options are equivalent under a unitary transformation. We implement the second one.
QuantumSavory.lindbladop — Function
For a given background noise type, provide the corresponding Lindblad collapse operator, in a QuantumOptics.jl representation.
See also: paulinoise, krausops
QuantumSavory.lindbladop — Method
1/√τ â
QuantumSavory.lindbladop — Method
1/√T₁ |0⟩⟨1|
QuantumSavory.lindbladop — Method
1/√(2T₂) Z
QuantumSavory.messagebuffer — Method
messagebuffer(
net::RegisterNet,
dst::Int64
) -> MessageBuffer{Tag}
Get a handle to a classical message buffer corresponding to all channels sending to a given destination register.
See also: channel
QuantumSavory.messagebuffer — Method
messagebuffer(
ref::Union{RegRef, Register}
) -> MessageBuffer{Tag}
Get a handle to a classical message buffer corresponding to all channels sending to a given destination register.
See also: channel
QuantumSavory.observable — Method
Calculate the expectation value of a quantum observable on the given register and slot.
observable([regA, regB], [slot1, slot2], obs) would calculate the expectation value of the obs observable (using the appropriate formalism, depending on the state representation in the given registers).
QuantumSavory.onchange — Function
Wait for changes to occur on a MessageBuffer or Register. By specifying a second argument, you can filter what type of events are waited on. E.g. onchange(r, Tag) will wait only on changes to tags and metadata.
QuantumSavory.paulinoise — Function
For a given background noise type, provide the corresponding (potentially twirled) Pauli operators and the probabilities for the operators to act, in a QuantumClifford.jl representation.
See also: krausops, lindbladop
QuantumSavory.paulinoise — Method
The Pauli operator and probability of its application for a Depolarization process.
((p/4, X), (p/4, Y), (p/4, Z)) for p = 1-exp(-Δt/τ)
QuantumSavory.paulinoise — Method
The Pauli operator and probability of its application for a T₂ process.
(1-exp(-Δt/T₂)) / 2 and Z
QuantumSavory.project_traceout! — Function
Perform a projective measurement on the given slot of the given register.
project_traceout!(reg, slot, [stateA, stateB]) performs a projective measurement, projecting on either stateA or stateB, returning the index of the subspace on which the projection happened. It assumes the list of possible states forms a basis for the Hilbert space. The Hilbert space of the register is automatically shrunk.
A basis object can be specified on its own as well, e.g. project_traceout!(reg, slot, basis).
QuantumSavory.qchannel — Method
Get a handle to a quantum channel between two registers.
julia> net = RegisterNet([Register(2), Register(2), Register(2)]) # defaults to a chain topology
A network of 3 registers in a graph of 2 edges
julia> qchannel(net, 1=>2)
QuantumChannel{Qubit}(Qubit(), ConcurrentSim.DelayQueue{Register}(ConcurrentSim.QueueStore{Register, Int64}, 0.0), nothing)
julia> qchannel(net, 1=>2) === qchannel(net, net[1]=>net[2])
trueSee also: channel
QuantumSavory.query — Method
query(
mb::MessageBuffer,
queryargs::Union{QuantumSavory.Wildcard, Int64, DataType, Function, Symbol}...
) -> Union{Nothing, NamedTuple{(:depth, :src, :tag), <:Tuple{Int64, Any, Any}}}
You are advised to actually use querydelete!, not query when working with classical message buffers.
QuantumSavory.query — Method
query(
reg::Union{RegRef, Register},
queryargs::Union{QuantumSavory.Wildcard, Int64, DataType, Function, Symbol}...;
locked,
assigned,
filo
) -> Any
A query function searching for the first slot in a register that has a given tag.
Wildcards are supported (instances of Wildcard also available as the constants W or the emoji ❓ which can be entered as \:question: in the REPL). Predicate functions are also supported (they have to be Int↦Bool functions). The order of query lookup can be specified in terms of FIFO or FILO and defaults to FILO if not specified. The keyword arguments locked and assigned can be used to check, respectively, whether the given slot is locked or whether it contains a quantum state. The keyword argument filo can be used to specify whether the search should be done in a FIFO or FILO order, defaulting to filo=true (i.e. a stack-like behavior).
julia> r = Register(10);
tag!(r[1], :symbol, 2, 3);
tag!(r[2], :symbol, 4, 5);
julia> query(r, :symbol, 4, 5)
(slot = Slot 2, id = 4, tag = SymbolIntInt(:symbol, 4, 5)::Tag)
julia> lock(r[1]);
julia> query(r, :symbol, 4, 5; locked=false) |> isnothing
false
julia> query(r, :symbol, ❓, 3)
(slot = Slot 1, id = 3, tag = SymbolIntInt(:symbol, 2, 3)::Tag)
julia> query(r, :symbol, ❓, 3; assigned=true) |> isnothing
true
julia> query(r, :othersym, ❓, ❓) |> isnothing
true
julia> tag!(r[5], Int, 4, 5);
julia> query(r, Float64, 4, 5) |> isnothing
true
julia> query(r, Int, 4, >(7)) |> isnothing
true
julia> query(r, Int, 4, <(7))
(slot = Slot 5, id = 5, tag = TypeIntInt(Int64, 4, 5)::Tag)A query can be on on a single slot of a register:
julia> r = Register(5);
julia> tag!(r[2], :symbol, 2, 3);
julia> query(r[2], :symbol, 2, 3)
(slot = Slot 2, id = 6, tag = SymbolIntInt(:symbol, 2, 3)::Tag)
julia> query(r[3], :symbol, 2, 3) === nothing
true
julia> queryall(r[2], :symbol, 2, 3)
1-element Vector{@NamedTuple{slot::RegRef, id::Int128, tag::Tag}}:
(slot = Slot 2, id = 6, tag = SymbolIntInt(:symbol, 2, 3)::Tag)QuantumSavory.queryall — Method
queryall(
reg::Union{RegRef, Register},
queryargs::Union{QuantumSavory.Wildcard, Int64, DataType, Function, Symbol}...;
filo,
kwargs...
) -> Any
A query function that returns all slots of a register that have a given tag, with support for predicates and wildcards.
julia> r = Register(10);
tag!(r[1], :symbol, 2, 3);
tag!(r[2], :symbol, 4, 5);
julia> queryall(r, :symbol, ❓, ❓)
2-element Vector{@NamedTuple{slot::RegRef, id::Int128, tag::Tag}}:
(slot = Slot 2, id = 2, tag = SymbolIntInt(:symbol, 4, 5)::Tag)
(slot = Slot 1, id = 1, tag = SymbolIntInt(:symbol, 2, 3)::Tag)
julia> queryall(r, :symbol, ❓, >(4))
1-element Vector{@NamedTuple{slot::RegRef, id::Int128, tag::Tag}}:
(slot = Slot 2, id = 2, tag = SymbolIntInt(:symbol, 4, 5)::Tag)
julia> queryall(r, :symbol, ❓, >(5))
@NamedTuple{slot::RegRef, id::Int128, tag::Tag}[]QuantumSavory.querydelete! — Method
querydelete!(
mb::MessageBuffer,
args...
) -> Union{Nothing, @NamedTuple{src::Union{Nothing, Int64}, tag::T} where T}
A query for classical message buffers that also deletes the message out of the buffer.
julia> net = RegisterNet([Register(3), Register(2)])
A network of 2 registers in a graph of 1 edges
julia> put!(channel(net, 1=>2), Tag(:my_tag));
julia> put!(channel(net, 1=>2), Tag(:another_tag, 123, 456));
julia> query(messagebuffer(net, 2), :my_tag)
julia> run(get_time_tracker(net))
julia> query(messagebuffer(net, 2), :my_tag)
(depth = 1, src = 1, tag = Symbol(:my_tag)::Tag)
julia> querydelete!(messagebuffer(net, 2), :my_tag)
@NamedTuple{src::Union{Nothing, Int64}, tag::Tag}((1, Symbol(:my_tag)::Tag))
julia> querydelete!(messagebuffer(net, 2), :my_tag) === nothing
true
julia> querydelete!(messagebuffer(net, 2), :another_tag, ❓, ❓)
@NamedTuple{src::Union{Nothing, Int64}, tag::Tag}((1, SymbolIntInt(:another_tag, 123, 456)::Tag))
julia> querydelete!(messagebuffer(net, 2), :another_tag, ❓, ❓) === nothing
trueYou can also wait on a message buffer for a message to arrive before running a query:
julia> using ResumableFunctions; using ConcurrentSim;
julia> net = RegisterNet([Register(3), Register(2), Register(3)])
A network of 3 registers in a graph of 2 edges
julia> env = get_time_tracker(net);
julia> @resumable function receive_tags(env)
while true
mb = messagebuffer(net, 2)
@yield onchange(mb)
msg = querydelete!(mb, :second_tag, ❓, ❓)
print("t=$(now(env)): query returns ")
if isnothing(msg)
println("nothing")
else
println("$(msg.tag) received from node $(msg.src)")
end
end
end
receive_tags (generic function with 1 method)
julia> @resumable function send_tags(env)
@yield timeout(env, 1.0)
put!(channel(net, 1=>2), Tag(:my_tag))
@yield timeout(env, 2.0)
put!(channel(net, 3=>2), Tag(:second_tag, 123, 456))
end
send_tags (generic function with 1 method)
julia> @process send_tags(env);
julia> @process receive_tags(env);
julia> run(env, 10)
t=1.0: query returns nothing
t=3.0: query returns SymbolIntInt(:second_tag, 123, 456)::Tag received from node 3QuantumSavory.querydelete! — Method
querydelete!(
reg::Union{RegRef, Register},
args...;
kwa...
) -> Any
A query for Register or a register slot (i.e. a RegRef) that also deletes the tag.
julia> reg = Register(3)
tag!(reg[1], :tagA, 1, 2, 3)
tag!(reg[2], :tagA, 10, 20, 30)
tag!(reg[2], :tagB, 6, 7, 8);
julia> queryall(reg, :tagA, ❓, ❓, ❓)
2-element Vector{@NamedTuple{slot::RegRef, id::Int128, tag::Tag}}:
(slot = Slot 2, id = 4, tag = SymbolIntIntInt(:tagA, 10, 20, 30)::Tag)
(slot = Slot 1, id = 3, tag = SymbolIntIntInt(:tagA, 1, 2, 3)::Tag)
julia> querydelete!(reg, :tagA, ❓, ❓, ❓)
(slot = Slot 2, id = 4, tag = SymbolIntIntInt(:tagA, 10, 20, 30)::Tag)
julia> queryall(reg, :tagA, ❓, ❓, ❓)
1-element Vector{@NamedTuple{slot::RegRef, id::Int128, tag::Tag}}:
(slot = Slot 1, id = 3, tag = SymbolIntIntInt(:tagA, 1, 2, 3)::Tag)QuantumSavory.registernetplot — Function
Draw the given register network.
Requires a Makie backend be already imported.
QuantumSavory.registernetplot! — Function
Draw the given register network on a given Makie axis.
Requires a Makie backend be already imported.
QuantumSavory.registernetplot_axis — Function
Draw the given register network on a given Makie axis or subfigure and modify the axis with numerous visualization enhancements.
Requires a Makie backend be already imported.
QuantumSavory.resourceplot_axis — Function
Draw the various resources and locks stored in the given meta-graph on a given Makie axis.
Requires a Makie backend be already imported.
QuantumSavory.subsystemcompose — Method
Ensure that the all slots of the given registers are represented by one single state object, i.e. that all the register slots are tracked in the same Hilbert space.
QuantumSavory.tag! — Method
QuantumSavory.untag! — Method
untag!(
ref::Union{RegRef, Register},
id::Integer
) -> @NamedTuple{tag::Tag, slot::Int64, time::Float64}
Remove the tag with the given id from a RegRef or a Register.
To remove a tag based on a query, use querydelete! instead.
See also: querydelete!, query, tag!
QuantumSavory.uptotime! — Function
Evolve all the states in a register to a given time, according to the various backgrounds that they might have.
julia> reg = Register(2, T1Decay(1.0))
Register with 2 slots: [ Qubit | Qubit ]
Slots:
nothing
nothing
julia> initialize!(reg[1], X₁)
observable(reg[1], σᶻ)
0.0 + 0.0im
julia> uptotime!(reg[1], 10)
observable(reg[1], Z)
0.9999546000702374 + 0.0im