Register Interface
A rather diverse set of simulation libraries is used under the hood. Long term the Julia Quantum Science community might be able to converge to a common interface that would slightly simplify work between the libraries, but in the interim the Julia multimethod paradigm is sufficient. Below we describe the interface that enables us to operate with many distinct underlying simulators.
initialize!
Initialize the state of a register to a known state.
QuantumSavory.initialize! — FunctionSet 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.
initialize!(refs::Vector{RegRef}, state; time)
Store a state in the given register slots.
refs can also be Tuple{Vararg{RegRef, N}} or a single RegRef.
initialize!(r::Vector{Register}, i::Vector{Int64}, state; time)
r can also be a single Register.
The accesstimes attributes of the slots are reset to the given time.
If state<:Symbolic, then consistent_representation is used to choose an appropriate representation based on the AbstractRepresentation properties of the register slots. Then an express call is made to transform the symbolic object into the appropriate representation.
initialize!(r::RegRef; time) and initialize!(reg::Register, i::Int64; time)
When a state is not provided, a default one is calculated from newstate, depending on the register slot's QuantumStateTrait (e.g. qubit vs qumode) and AbstractRepresentation (e.g. ket vs tableaux).
Interface Overview
apply!
Apply a quantum operation to a register.
QuantumInterface.apply! — FunctionApply 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.
apply!(refs::Vector{RegRef}, operation; time)
Applying an operation to the qubits referred to by the sequence of RegRefs at a specified time.
refs can also be Tuple{Vararg{RegRef, N}} or a single RegRef.
apply!(regs::Vector{Register}, indices, operation; time)
indices refers to the slots inside of the given regs.
Calls uptotime! in order to update any AbstractBackground properties.
Calls subsystemcompose in order to make one big state. Then goes to apply!(state, subsystem_indices, operation; time).
apply!(state, subsystem_indices, operation; time)
subsystem_indices refers to subsystems in state.
If operation<:Symbolic, then express(operation, repr, ::UseAsOperation) is used to convert the symbolic operation into something workable for the given state type. repr is chosen by dispatch on state.
Currently, the decision of how to convert a symbolic operation is based only on the state on which the operation would act. It can not be modified by the AbstractRepresentation properties of the Registers containing the state.
Interface Overview
As mentioned above, converting from symbolic to explicit representation for the operation is dependent only on the type of state, i.e. by the time the conversion is done, no knowledge of the register and its properties are kept (in particular its preferred representation is not considered).
You can add a custom dispatch that skips the express functionality by defining a method apply!(state::YourStateType, indices, operation<:Symbolic{AbstractOperator}). This would preemt the default apply!(state, indices, operation<:Symbolic{AbstractOperator}) containing the express logic. The drawback is that this would also skip the memoization employed by express.
observable
Measure a quantum observable. The dispatch down the call three is very similar to the one for apply!.
QuantumSavory.observable — FunctionCalculate 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).
observable(refs::Tuple{Vararg{RegRef, N}}, obs; something=nothing, time=nothing)
Calculate the value of an observable on the state in the sequence of RegRefs at a specified time. If these registers are not instantiated, return something.
refs can also be Tuple{Vararg{RegRef, N}} or a single RegRef.
observable(regs::Vector{Register}, indices, obs; something=nothing, time=nothing)
indices refers to the slots inside of the given regs.
Calls uptotime! in order to update any AbstractBackground properties.
Calls subsystemcompose in order to make one big state. Then goes to observable(state, subsystem_indices, obs; time).
observable(state, subsystem_indices, obs; time)
subsystem_indicesrefers to subsystems instate`.
If operation<:Symbolic, then an express(obs, repr, ::UseAsObservable) call is used to convert the symbolic obs into something workable for the given state type. repr is chosen by dispatch on state.
Interface Overview
Similarly to the case with apply!, you can skips the express functionality by defining a method observable(state::YourStateType, indices, obs<:Symbolic{AbstractOperator}).
project_traceout!
QuantumSavory.project_traceout! — FunctionPerform 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).
project_traceout!(r::RegRef, basis; time)
Project the state in RegRef on basis at a specified time. basis can be a Vector or Tuple of basis states, or it can be a Matrix like Z or X.
project_traceout(reg::Register, i::Int, basis; time)
Project the state in the slot in index i of Register on basis at a specified time. basis can be a Vector or Tuple of basis states, or it can be a Matrix like Z or X.
project_traceout!(f, r::RegRef, basis; time)
Project the state in RegRef on basis at a specified time and apply function f on the projected basis state. basis can be a Vector or Tuple of basis states, or it can be a Matrix like Z or X.
project_traceout!(f, reg::Register, i::Int, basis; time)
Project the state in the slot in index i of Register on basis at a specified time and apply function f on the projected basis state. basis can be a Vector or Tuple of basis states, or it can be a Matrix like Z or X. Lowers the representation from registers to states.
project_traceout!(state, stateindex, basis::Symbolic{AbstractOperator}) and basis::AbstractVecOrTuple{<:Symbolic{AbstractKet}}
Backend implementations. If basis is an operator, call eigvecs to convert it into a matrix whose columns are the eigenvectors of the operator. If basis is a Vector or Tuple of Symbolic basis states, call express to convert it to the necessary representation.
Interface Overview
traceout!
QuantumInterface.traceout! — FunctionDelete 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.
Perform a partial trace over a part of the system (i.e. discard a part of the system).
traceout!(r::RegRef)
Partial trace over a particular register reference.
traceout!(r::Register, i::Int)
Partial trace over slot i of register r. Calls down to the state reference stored in that particular register.
traceout!(s::StateRef, i::Int)
Partial trace over subsystem i of state referenced by s.
Interface Overview
uptotime!
QuantumSavory.uptotime! — FunctionEvolve 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.0imuptotime!(ref::RegRef, now)
Evolve the state in a RegRef upto a given time now
uptotime!(refs::Base.AbstractVecOrTuple{RegRef}, now)
Evolve the state represented by the given RegRefs upto a time now
uptotime!(registers, indices::Base.AbstractVecOrTuple{Int}, now)
Evolve the state of all the given registers at the slots represented by indices upto a time now
uptotime!(stateref::StateRef, idx::Int, background, Δt)
Evolve a StateRef at index idx with given background and Δt
uptotime!(state, indices::Base.AbstractVecOrTuple{Int}, backgrounds, Δt)
Evolve state at indices given backgrounds and Δt
uptotime!(state::QuantumClifford.MixedDestabilizer, idx::Int, background, Δt)
Low level implementation to compute the result of uptotime! for states using Clifford representation
uptotime!(state::StateVector, idx::Int, background, Δt)
Low level implementation to compute the result of uptotime! for states using Ket representation. The state in ket representation is converted to a density matrix before calling the uptotime! for final computation.
uptotime!(state::Operator, idx::Int, background, Δt)
Low level implementation to compute the result of uptotime! for Operator
Interface Overview
swap!
swap!(r1::RegRef, r2::RegRef)
Swap the state of the given RegRefs
swap!(reg1::Register, reg2::Register, i1::Int, i2::Int)
Swap the state stored in the two Registers at slots i1 and i2 respectively
Interface Overview
overwritetime!
overwritetime!(refs::Base.AbstractVecOrTuple{RegRef}, now)
Overwrite the time of the simulation for the given references to now
overwritetime!(registers, indices, now)
Overwrite the time of the simulation for the given registers at indices to now