Core

Qecsim

QecsimModule

Package for simulating quantum error correction using stabilizer codes.

source
Qecsim.QecsimErrorType
QecsimError <: Exception

QecsimError(msg)

Construct an exception indicating an internal (core or models) error.

source

App

Qecsim.AppModule

Functions to run quantum error correction simulations and merge/read/write output data.

source
Qecsim.App.qec_run_onceFunction
qec_run_once(
    code, error_model, decoder, p::Real, rng::AbstractRNG=GLOBAL_RNG
) -> RunResult

Execute a stabilizer code error-decode-recovery (ideal) simulation and return run result.

The parameters code, error_model and decoder should be concrete subtypes or duck-typed implementations of StabilizerCode, ErrorModel and Decoder, respectively.

The simulation algorithm is as follows:

  1. $S ←$ stabilizers(code)
  2. $L ←$ logicals(code)
  3. $e ←$ generate(error_model, code, p, rng)
  4. $y ← S ⊙ e$
  5. decode_result $←$ decode(decoder, code, $y$; kwargs...)
  6. $r ←$ decode_result.recovery
  7. sanity check: $S ⊙ (r ⊕ e) = 0$
  8. logical_commutations $← L ⊙ (r ⊕ e)$
  9. success $← L ⊙ (r ⊕ e) = 0$

where $⊕$ denotes element-wise exclusive-or, and $⊙$ is defined in PauliTools.bsp.

The kwargs passed to decode include error_model, p and error; most decoders will ignore these parameters. The decode method returns a DecodeResult. If decode_result.success and/or decode_result.logical_commutations are specified, they override the values of success and logical_commutations, irrespective of whether decode_result.recovery is specified or not. The value decode_result.custom_values is passed through in the run result.

See also RunResult.

Examples

julia> using Qecsim.BasicModels, Qecsim.GenericModels, Random

julia> rng = MersenneTwister(6);  # use random seed for reproducible result

julia> qec_run_once(FiveQubitCode(), DepolarizingErrorModel(), NaiveDecoder(), 0.2, rng)
RunResult{Nothing}(false, 2, Bool[1, 0], nothing)
source
Qecsim.App.RunResultType
RunResult(
    success::Bool,
    error_weight::Int,
    logical_commutations::Union{Nothing,BitVector}
    custom_values::Union{Nothing,Vector}
)

Construct a run result as returned by qec_run_once.

Examples

julia> r = RunResult(false, 2, BitVector([0, 1]), [1.2, 3.1])
RunResult{Vector{Float64}}(false, 2, Bool[0, 1], [1.2, 3.1])

julia> r.success, r.error_weight, r.logical_commutations, r.custom_values
(false, 2, Bool[0, 1], [1.2, 3.1])
source
Qecsim.App.qec_runFunction
qec_run(
    code, error_model, decoder, p::Real, random_seed=nothing;
    max_runs::Union{Integer,Nothing}=nothing,
    max_failures::Union{Integer,Nothing}=nothing
) -> Dict

Execute stabilizer code error-decode-recovery (ideal) simulations many times and return aggregated run data, see qec_run_once for details of a single run.

The parameters code, error_model and decoder should be concrete subtypes or duck-typed implementations of StabilizerCode, ErrorModel and Decoder, respectively.

Simulations are run one or more times as determined by max_runs and max_failures. If max_runs and/or max_failures are specified, stop after max_runs runs or max_failures failures, whichever happens first. If neither is specified, stop after one run.

The returned aggregated data has the following format:

Dict(
    :code => "5-qubit"              # label(code)
    :n_k_d => (5, 1, 3)             # nkd(code)
    :time_steps => 1                # always 1 for ideal simulations
    :error_model => "Depolarizing"  # label(error_model)
    :decoder => "Naive"             # label(decoder)
    :error_probability => 0.1       # p
    :measurement_error_probability => 0.0   # always 0.0 for ideal simulations
    :n_run => 100                   # count of runs
    :n_success => 92                # count of successful recoveries
    :n_fail => 8                    # count of failed recoveries
    :n_logical_commutations => [5, 6]   # count of logical_commutations
    :custom_totals => nothing       # sum of custom_values
    :error_weight_total => 55       # sum of error_weight over n_run runs
    :error_weight_pvar => 0.4075    # pvariance of error_weight over n_run runs
    :logical_failure_rate => 0.08   # n_fail / n_run
    :physical_error_rate => 0.11    # error_weight_total / n_k_d[1] / time_steps / n_run
    :wall_time => 0.00253906        # wall-time for run in fractional seconds
)

Examples

julia> using Qecsim.BasicModels, Qecsim.GenericModels

julia> seed = 7;

julia> data = qec_run(FiveQubitCode(), DepolarizingErrorModel(), NaiveDecoder(), 0.1, seed;
           max_runs=100);
┌ Info: qec_run: starting
│   code = Qecsim.BasicModels.BasicCode(["XZZXI", "IXZZX", "XIXZZ", "ZXIXZ"], ["XXXXX"], ["ZZZZZ"], (5, 1, 3), "5-qubit")
│   error_model = Qecsim.GenericModels.DepolarizingErrorModel()
│   decoder = Qecsim.GenericModels.NaiveDecoder(10)
│   p = 0.1
│   random_seed = 7
│   max_runs = 100
└   max_failures = nothing
[ Info: qec_run: rng=MersenneTwister(7)
[ Info: qec_run: complete: data=Dict{Symbol, Any}(:error_weight_pvar => 0.4075000000000001, :time_steps => 1, :n_logical_commutations => [5, 6], :error_weight_total => 55, :wall_time => 0.002539058, :n_k_d => (5, 1, 3), :error_model => "Depolarizing", :physical_error_rate => 0.11, :measurement_error_probability => 0.0, :error_probability => 0.1, :n_success => 92, :logical_failure_rate => 0.08, :custom_totals => nothing, :code => "5-qubit", :decoder => "Naive", :n_fail => 8, :n_run => 100)

julia> data
Dict{Symbol, Any} with 17 entries:
  :error_weight_pvar             => 0.4075
  :time_steps                    => 1
  :n_logical_commutations        => [5, 6]
  :error_weight_total            => 55
  :wall_time                     => 0.00253906
  :n_k_d                         => (5, 1, 3)
  :error_model                   => "Depolarizing"
  :physical_error_rate           => 0.11
  :measurement_error_probability => 0.0
  :error_probability             => 0.1
  :n_success                     => 92
  :logical_failure_rate          => 0.08
  :custom_totals                 => nothing
  :code                          => "5-qubit"
  :decoder                       => "Naive"
  :n_fail                        => 8
  :n_run                         => 100
source
Qecsim.App.qec_mergeFunction
qec_merge(data...) -> Vector{Dict}

Merge simulation run data.

Run data is expected in the format specified by qec_run. Merged data is grouped by: (:code, :n_k_d, :error_model, :decoder, :error_probability, :time_steps, :measurement_error_probability). The scalar values: :n_run, :n_success, :n_fail, :error_weight_total and :wall_time are summed. The vector value: :n_logical_commutations is summed element-wise. The vector value: :custom_totals is summed element-wise for scalar elements and concatenated along dimension 1 (vcat) for array elements. The values: :logical_failure_rate and :physical_error_rate are recalculated. The value :error_weight_pvar is not currently recalculated and is therefore omitted.

Examples

julia> using Qecsim.BasicModels, Qecsim.GenericModels, Logging

julia> code, error_model, decoder = FiveQubitCode(), BitFlipErrorModel(), NaiveDecoder();

julia> data = Dict[];

julia> with_logger(NullLogger()) do # disable logging for brevity in doctest
           push!(data, qec_run(code, error_model, decoder, 0.08, 19; max_runs=100))
           push!(data, qec_run(code, error_model, decoder, 0.08, 23; max_runs=100))
       end;

julia> qec_merge(data...)
1-element Vector{Dict{Symbol, Any}}:
 Dict(:measurement_error_probability => 0.0, :error_probability => 0.08, :time_steps => 1, :error_weight_total => 83, :n_logical_commutations => [11, 3], :wall_time => 0.0028351720000000004, :n_k_d => (5, 1, 3), :error_model => "Bit-flip", :n_success => 189, :logical_failure_rate => 0.055…)
source
Qecsim.App.qec_readFunction
qec_read(io::IO) -> Vector{Dict{Symbol,Any}}
qec_read(filename::AbstractString) -> Vector{Dict{Symbol,Any}}

Read simulation run data from the given I/O stream or file.

Run data is expected in the format written by qec_write (i.e. a JSON array of objects using default JSON encoding of the format specified by qec_run). The read data is converted to the specified return type as follows: dictionary keys are converted to symbols, :n_k_d entries are converted to tuples, and vector types are inferred from their elements. If this conversion fails, an exception is thrown.

The JSON format is compatible with the format used by the Python package qecsim.

See also qec_write.

source
Qecsim.App.qec_writeFunction
qec_write(io::IO, data...)
qec_write(filename::AbstractString, data...)

Write simulation run data to the given I/O stream or file.

Run data is expected in the format specified by qec_run and written as a JSON array of objects using default JSON encoding. No checking of the format of the given data is performed. The file version of this method will refuse to overwrite an existing file, instead attempting to log the unwritten data and throwing an exception.

The JSON format is compatible with the format used by the Python package qecsim.

See also qec_read.

source

Model

Qecsim.ModelModule

Abstract types and methods for codes, error models and decoders.

source

Model.AbstractModel

Qecsim.Model.labelFunction
label(model) -> String

Return a label suitable for use in plots and for grouping results.

Abstract method

This method should be implemented for concrete subtypes or duck-typed implementations of AbstractModel.

source

Model.StabilizerCode

Qecsim.Model.logical_xsFunction
logical_xs(code) -> BitMatrix

Return the logical X operators in binary symplectic form.

Each row is an operator. The order should match that of logical_zs.

Abstract method

This method should be implemented for concrete subtypes or duck-typed implementations of StabilizerCode.

source
Qecsim.Model.logical_zsFunction
logical_zs(code) -> BitMatrix

Return the logical Z operators in binary symplectic form.

Each row is an operator. The order should match that of logical_xs.

Abstract method

This method should be implemented for concrete subtypes or duck-typed implementations of StabilizerCode.

source
Qecsim.Model.logicalsFunction
logicals(code) -> BitMatrix

Return the logical operators in binary symplectic form.

Each row is an operator. X operators are stacked above Z operators in the order given by logical_xs and logical_zs.

source
Qecsim.Model.nkdFunction
nkd(code) -> Tuple{Int,Int,Union{Int,Missing}}

Return a descriptor in the format (n, k, d), where n is the number of physical qubits, k is the number of logical qubits, and d is the distance of the code (or missing if unknown).

Abstract method

This method should be implemented for concrete subtypes or duck-typed implementations of StabilizerCode.

source
Qecsim.Model.stabilizersFunction
stabilizers(code) -> BitMatrix

Return the stabilizers in binary symplectic form.

Each row is a stabilizer generator. An overcomplete set of generators can be included to simplify decoding.

Abstract method

This method should be implemented for concrete subtypes or duck-typed implementations of StabilizerCode.

source

Model.ErrorModel

Qecsim.Model.generateFunction
generate(error_model, code, p::Real, [rng::AbstractRNG=GLOBAL_RNG]) -> BitVector

Generate a new error in binary symplectic form according to the error_model and code, where p is typically the probability of an error on a single qubit.

Abstract method

This method should be implemented for concrete subtypes or duck-typed implementations of ErrorModel.

source
Qecsim.Model.probability_distributionFunction
probability_distribution(error_model, p::Real) -> NTuple{4,Real}

Return the single-qubit probability distribution amongst Pauli I, X, Y and Z, where p is the overall probability of an error on a single qubit.

Abstract method [optional]

This method is not invoked by any core modules. Since it is often useful for decoders, it is provided as a template and concrete subtypes or duck-typed implementations of ErrorModel are encouraged to implement it when appropriate, particularly for IID error models.

source

Model.Decoder

Qecsim.Model.decodeFunction
decode(decoder, code, syndrome::AbstractVector{Bool}; kwargs...) -> DecodeResult

Resolve a recovery operation for the given code and syndrome, or evaluate the success of decoding, as encapsulated in the decode result.

The syndrome has length equal to the number of code stabilizers, and element values of 0 or 1 (false or true) indicate whether the corresponding stabilizer does or does not commute with the error, respectively.

Keyword parameters kwargs may be provided by the client, e.g. App, with context values such as error_model, error_probability and error. Most implementations will ignore such parameters; however, if they are used, implementations should declare them explicitly and treat them as optional.

See also DecodeResult.

Abstract method

This method should be implemented for concrete subtypes or duck-typed implementations of Decoder.

source
Qecsim.Model.DecodeResultType
DecodeResult(
    success::Union{Nothing,Bool},
    recovery::Union{Nothing,AbstractVector{Bool}},
    logical_commutations::Union{Nothing,AbstractVector{Bool}}
    custom_values::Union{Nothing,AbstractVector}
)
DecodeResult(;
    success::Union{Nothing,Bool}=nothing,
    recovery::Union{Nothing,AbstractVector{Bool}}=nothing,
    logical_commutations::Union{Nothing,AbstractVector{Bool}}=nothing
    custom_values::Union{Nothing,AbstractVector}=nothing
)

Construct a decoding result as returned by decode.

Typically decoders will provide a recovery operation and delegate the evaluation of success and logical_commutations to the client, e.g. App. Optionally, success and/or logical_commutations may be provided as overrides. At least one of recovery or success must be specified to allow a success value to be resolved. Additionally custom_values may be specified. If logical_commutations and/or custom_values are provided then they should be of consistent type and size over identically parameterized simulation runs. For example, App will sum logical_commutations across runs and, similarly, if custom_values are numbers they will be summed across runs, and if they are arrays they will be concatenated using vcat.

See also decode.

Examples

julia> DecodeResult(; recovery=BitVector([1, 0, 1, 0, 1, 1]))  # typical use-case
DecodeResult{Nothing}(nothing, Bool[1, 0, 1, 0, 1, 1], nothing, nothing)

julia> DecodeResult(; success=true)  # override success
DecodeResult{Nothing}(true, nothing, nothing, nothing)

julia> DecodeResult(; success=false, logical_commutations=BitVector([1, 0]))  # override all
DecodeResult{Nothing}(false, nothing, Bool[1, 0], nothing)

julia> DecodeResult(; success=true, custom_values=[2.3, 4.1])  # custom values (numbers)
DecodeResult{Vector{Float64}}(true, nothing, nothing, [2.3, 4.1])

julia> DecodeResult(; success=true, custom_values=[[2.3], [4.1]])  # custom values (arrays)
DecodeResult{Vector{Vector{Float64}}}(true, nothing, nothing, [[2.3], [4.1]])

julia> DecodeResult(true, nothing, nothing, [2.3, 4.1])  # positional parameters
DecodeResult{Vector{Float64}}(true, nothing, nothing, [2.3, 4.1])

julia> DecodeResult(; success=nothing, recovery=nothing)  # too few specified parameters
ERROR: QecsimError: at least one of 'success' or 'recovery' must be specified
Stacktrace:
[...]
source

PauliTools

Qecsim.PauliTools.bspFunction
bsp(
    A::AbstractVecOrMat{Bool}, B::AbstractVecOrMat{Bool}
) -> Union{Bool,BitVector,BitMatrix}

Return the binary symplectic product of A with B, given in binary symplectic form.

The binary symplectic product $⊙$ is defined as $A ⊙ B ≡ A Λ B \bmod 2$ where $Λ = \left[\begin{smallmatrix} 0 & I \\ I & 0 \end{smallmatrix}\right]$.

Examples

julia> a = BitVector([1, 0, 0, 0]);  # XI

julia> b = BitVector([0, 0, 1, 0]);  # ZI

julia> bsp(a', b)
true
julia> stabilizers = BitMatrix(  # 5-qubit stabilizers
       [1 0 0 1 0 0 1 1 0 0     # XZZXI
        0 1 0 0 1 0 0 1 1 0     # IXZZX
        1 0 1 0 0 0 0 0 1 1     # XIXZZ
        0 1 0 1 0 1 0 0 0 1]);  # ZXIXZ

julia> error = BitVector([0, 0, 1, 1, 0, 0, 1, 0, 1, 0]);  # IZXYI

julia> bsp(stabilizers, error)
4-element BitVector:
 0
 1
 1
 0
source
Qecsim.PauliTools.packFunction
pack(bsf::AbstractVector{Bool}) -> Tuple{String,Int}

Pack a binary vector into a concise representation, typically for log output. See also unpack.

Examples

julia> a = BitVector([1, 0, 1, 0, 1, 1]);  # XZY

julia> b = pack(a)  # (hex_value, length)
("2b", 6)
julia> unpack(b) == a
true
source
Qecsim.PauliTools.to_bsfFunction
to_bsf(
    pauli::Union{AbstractString,AbstractVector{<:AbstractString}}
) -> Union{BitVector,BitMatrix}

Convert the Pauli string operator(s) to binary symplectic form.

A single Pauli string is converted to a vector. A vector of Pauli strings is converted to a matrix where each row corresponds to a Pauli.

Examples

julia> to_bsf("XIZIY")
10-element BitVector:
 1
 0
 0
 0
 1
 0
 0
 1
 0
 1
julia> to_bsf(["XIZIY", "IXZYI"])
2×10 BitMatrix:
 1  0  0  0  1  0  0  1  0  1
 0  1  0  1  0  0  0  1  1  0
source
Qecsim.PauliTools.to_pauliFunction
to_pauli(bsf::AbstractVecOrMat{Bool}) -> Union{String,Vector{String}}

Convert the binary symplectic form to Pauli string operator(s).

A vector is converted to a single Pauli string. A matrix is converted row-by-row to a collection of Pauli strings.

Examples

julia> to_pauli(BitVector([1, 0, 0, 0, 1, 0, 0, 1, 0, 1]))
"XIZIY"
julia> to_pauli(BitMatrix([1 0 0 0 1 0 0 1 0 1; 0 1 0 1 0 0 0 1 1 0]))
2-element Vector{String}:
 "XIZIY"
 "IXZYI"
source
Qecsim.PauliTools.unpackFunction
unpack(packed_bsf::Tuple{String,Int}) -> BitVector

Unpack a binary vector from a concise representation, typically from log output. See also pack.

Examples

julia> a = ("2b", 6);  # (hex_value, length)

julia> b = unpack(a)  # XZY
6-element BitVector:
 1
 0
 1
 0
 1
 1
julia> pack(b) == a
true
source
Qecsim.PauliTools.weightFunction
weight(bsf::AbstractVecOrMat{Bool}) -> Int

Return the weight of the binary symplectic form.

Examples

julia> weight(BitVector([1, 0, 0, 0, 1, 0, 0, 1, 0, 1]))
3
julia> weight(BitMatrix([1 0 0 0 1 0 0 1 0 1; 1 1 1 1 1 0 0 0 0 0]))
8
source
weight(pauli::Union{AbstractString,AbstractVector{<:AbstractString}}) -> Int

Return the weight of the Pauli string operator(s).

Examples

julia> weight("XIZIY")
3
julia> weight(["XIZIY", "XXXXX"])
8
source