Skip to content

Commit

Permalink
move Noise out of Experimental
Browse files Browse the repository at this point in the history
Signed-off-by: Stefan Krastanov <stefan@krastanov.org>
  • Loading branch information
Krastanov committed Dec 27, 2022
1 parent ca6e9f3 commit da80a22
Show file tree
Hide file tree
Showing 12 changed files with 283 additions and 133 deletions.
10 changes: 8 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ Planned for v1.0.0:

## v0.6.7

- Test coverage increase.
- Fixes to inference failures (detected by JET).
- **(fix)** Fixed bug in `CliffordOperator(AbstractTwoQubitOperator)`.
- Fixes to inference failures (detected by JET).
- Significant test coverage increase.
- Stabilizing a few features, moving out of `Experimental.NoisyCircuits`
- `applynoise!`
- `affectedqubits`
- `NoisyGate`
- `NoiseOp` and `NoiseOpAll`
- `UnbiasedUncorrelatedNoise`

## v0.6.6 - 2022-12-24

Expand Down
3 changes: 2 additions & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ pages = [
"Mixed States" => "mixed.md",
"Graph States" => "graphs.md",
"Datastructure Choice" => "datastructures.md",
"Useful States and Operators" => "commonstates.md",
"Useful States" => "commonstates.md",
"All Gates" => "allops.md",
"Plotting" => "plotting.md",
"API" => "API.md",
"Tutorials and Publications" => "tutandpub.md",
Expand Down
46 changes: 7 additions & 39 deletions docs/src/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,48 +17,16 @@ Moreover, a [`MixedDestabilizer`](@ref) can be stored inside a [`Register`](@ref

There are [convenience constructors for common types of states and operators](@ref Useful-States-and-Operators).

## [Operations](@id all-operations)
## Operations

All of these can be applied on a state with the [`apply!`](@ref) function. Whether they are deterministic and their computational complexity is listed in the table below. A list of lower-level functions for more control over how an operation is performed is also given.
Acting on quantum states can be performed either:

```@raw html
<style>
td > code {
white-space: pre;
}
</style>
```
- In a "linear algebra" language, where the operators are [Clifford operators](@ref CliffordOperator) represented as tableaux. This is an explicitly deterministic lower-level interface, which provides a great deal of control over how tableaux are manipulated.
- Or in a "circuit" language, where the operators (and measurements and noise) are represented as circuit gates. This is a higher-level interface in which the outcome of an operation can be stochastic. Particularly useful for [Monte Carlo simulations](@ref noisycircuits_mc) and [Perturbative Expansion Symbolic Results](@ref noisycircuits_perturb).

See the [manual](@ref Manual) as a primer on these approaches.

| Type | Deterministic | 𝒪(nˣ) | Low-level functions
|:--|:-:|---|---|
|`AbstractOperation `| | | |
|` ├─ AbstractCliffordOperator `| | | |
|` │ ├─ AbstractSymbolicOperator `| | | |
|` │ │ ├─ AbstractSingleQubitOperator`| | | |
|` │ │ │ ├─ SingleQubitOperator `|✔️ | n | |
|` │ │ │ ├─ sHadamard `|✔️ | n | |
|` │ │ │ ├─ sId1 `|✔️ | n | |
|` │ │ │ ├─ sInvPhase `|✔️ | n | |
|` │ │ │ ├─ sPhase `|✔️ | n | |
|` │ │ │ ├─ sX `|✔️ | n | |
|` │ │ │ ├─ sY `|✔️ | n | |
|` │ │ │ └─ sZ `|✔️ | n | |
|` │ │ └─ AbstractTwoQubitOperator `| | | |
|` │ │ ├─ sCNOT `|✔️ | n | |
|` │ │ ├─ sCPHASE `|✔️ | n | |
|` │ │ └─ sSWAP `|✔️ | n | |
|` │ │ `| | | |
|` │ ├─ CliffordOperator `|✔️ || |
|` │ ├─ PauliOperator `|✔️ || |
|` │ └─ SparseGate `|✔️ |kn²| |
|` ├─ AbstractMeasurement `| | | |
|` │ ├─ PauliMeasurement `||| [`project!`](@ref), [`projectrand!`](@ref) |
|` │ ├─ sMX `||| [`projectX!`](@ref) |
|` │ ├─ sMY `||| [`projectY!`](@ref) |
|` │ └─ sMZ `||| [`projectZ!`](@ref) |
|``| | | |
|` ├─ BellMeasurement `||| |
|` └─ Reset `|✔️ |kn²| [`reset_qubits!`](@ref)|
See the [full list of operations](@ref all-operations) for a list of implemented operations.

## Autogenerated API list

Expand Down
146 changes: 146 additions & 0 deletions docs/src/allops.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# [Operations - Gates, Measurements, and More](@id all-operations)

```@meta
DocTestSetup = quote
using QuantumClifford
using StableRNGs
rng = StableRNG(42)
end
```

## [Operations](@id all-operations)

Acting on quantum states can be performed either:

- In a "linear algebra" language, where the operators are [Clifford operators](@ref CliffordOperator) represented as tableaux. This is an explicitly deterministic lower-level interface, which provides a great deal of control over how tableaux are manipulated.
- Or in a "circuit" language, where the operators (and measurements and noise) are represented as circuit gates. This is a higher-level interface in which the outcome of an operation can be stochastic. Particularly useful for [Monte Carlo simulations](@ref noisycircuits_mc) and [Perturbative Expansion Symbolic Results](@ref noisycircuits_perturb)

In the circuit language, all operations can be applied on a state with the [`apply!`](@ref) function. Whether they are deterministic and their computational complexity is listed in the table below. A list of lower-level "linear algebra style" functions for more control over how an operation is performed is also given.

```@raw html
<style>
td > code {
white-space: pre;
}
</style>
```

| Type | Deterministic | 𝒪(nˣ) | Low-level functions
|:--|:-:|---|---|
|`AbstractOperation `| | | |
|` ├─ AbstractCliffordOperator `| | | |
|` │ ├─ AbstractSymbolicOperator `| | | |
|` │ │ ├─ AbstractSingleQubitOperator`| | | |
|` │ │ │ ├─ SingleQubitOperator `|✔️ | n | |
|` │ │ │ ├─ sHadamard `|✔️ | n | |
|` │ │ │ ├─ sId1 `|✔️ | n | |
|` │ │ │ ├─ sInvPhase `|✔️ | n | |
|` │ │ │ ├─ sPhase `|✔️ | n | |
|` │ │ │ ├─ sX `|✔️ | n | |
|` │ │ │ ├─ sY `|✔️ | n | |
|` │ │ │ └─ sZ `|✔️ | n | |
|` │ │ └─ AbstractTwoQubitOperator `| | | |
|` │ │ ├─ sCNOT `|✔️ | n | |
|` │ │ ├─ sCPHASE `|✔️ | n | |
|` │ │ └─ sSWAP `|✔️ | n | |
|` │ │ `| | | |
|` │ ├─ CliffordOperator `|✔️ || |
|` │ ├─ PauliOperator `|✔️ || |
|` │ └─ SparseGate `|✔️ |kn²| |
|` ├─ AbstractMeasurement `| | | |
|` │ ├─ PauliMeasurement `||| [`project!`](@ref), [`projectrand!`](@ref) |
|` │ ├─ sMX `||| [`projectX!`](@ref) |
|` │ ├─ sMY `||| [`projectY!`](@ref) |
|` │ └─ sMZ `||| [`projectZ!`](@ref) |
|``| | | |
|` ├─ BellMeasurement `||| |
|` ├─ NoiseOp `|| ?| [`applynoise!`](@ref) |
|` ├─ NoiseOpAll `|| ?| [`applynoise!`](@ref) |
|` ├─ NoisyGate `|| ?| [`applynoise!`](@ref) |
|` └─ Reset `|✔️ |kn²| [`reset_qubits!`](@ref)|

## Details of Operations Supported by [`apply!`](@ref)

### Unitary Gates

We distinguish between symbolic gates like [`sCNOT`](@ref) that have specialized (fast) `apply!` methods (usually just for single and two qubit gates) and general tableau representation of gates like [`CliffordOperator`](@ref) that can represent any multi-qubit gate.

Predefined unitary gates are available, like [`sCNOT`](@ref), [`sHadamard`](@ref), etc.

```@example 1
using QuantumClifford # hide
using QuantumClifford.Experimental.NoisyCircuits # hide
using QuantumCliffordPlots # hide
[sCNOT(2,4),sHadamard(2),sCPHASE(1,3),sSWAP(2,4)]
```

Any arbitrary tableaux can be used as a gate too.

They can be specified by giving a Clifford operator tableaux and the indices on which it acts
(particularly useful for gates acting on a small part of a circuit):

```@example 1
using QuantumClifford # hide
using QuantumClifford.Experimental.NoisyCircuits # hide
using QuantumCliffordPlots # hide
SparseGate(tCNOT, [2,4])
```

The Clifford operator tableaux can be completely arbitrary.
```@example 1
SparseGate(random_clifford(3), [2,4,5])
```

If the Clifford operator acts on all qubits, we do not need to specify indices, just use the operator.

### Noisy Gates

Each gate can be followed by noise applied to the qubits on which it has acted.
This is done by wrapping the given gate into a [`NoisyGate`](@ref)

```@example 1
ε = 0.03 # X/Y/Z error probability
noise = UnbiasedUncorrelatedNoise(ε)
noisy_gate = NoisyGate(SparseGate(tCNOT, [2,4]), noise)
```

In circuit diagrams the noise is not depicted, but after each application of the gate defined in `noisy_gate`, a noise operator will also be applied. The example above is of Pauli Depolarization implemented by [`UnbiasedUncorrelatedNoise`](@ref).

One can also apply only the noise operator by using [`NoiseOp`](@ref) which acts only on specified qubits. Or alternatively, one can use [`NoiseOpAll`](@ref) in order to apply noise to all qubits.

```@example 1
[NoiseOp(noise, [4,5]), NoiseOpAll(noise)]
```

The machinery behind noise processes and different types of noise is detailed in [the section on noise](@ref noise)

### Coincidence Measurements

Global parity measurements involving single-qubit projections and classical communication are implemented with [`BellMeasurement`](@ref). One needs to specify the axes of measurement and the qubits being measured. If the parity is trivial, the circuit continues, if the parity is non-trivial, the circuit ends and reports a detected failure.
This operator is frequently used in the simulation of entanglement purification.

```@example 1
BellMeasurement([sMX(1), sMY(3), sMZ(4)])
```

There is also [`NoisyBellMeasurement`](@ref) that takes the bit-flip probability of a single-qubit measurement as a third argument.

### Stabilizer Measurements

A measurement over one or more qubits can also be performed, e.g., a direct stabilizer measurement on multiple qubits without the use of ancillary qubits. When applied to multiple qubits, this differs from `BellMeasurement` as it performs a single projection, unlike `BellMeasurement` which performs a separate projection for every single qubit involved. This measurement is implemented in [`PauliMeasurement`](@ref) which requires a Pauli operator on which to project and the index of the classical bit in which to store the result. Alternatively, there are [`sMX`](@ref), [`sMZ`](@ref), [`sMY`](@ref) if you are measuring a single qubit.

```@example 1
[PauliMeasurement(P"XYZ", 1), sMZ(2, 2)]
```

### Reset Operations

The [`Reset`](@ref) operations lets you trace out the specified qubits and set their state to a specific tableau.

```@example 1
new_state = random_stabilizer(3)
qubit_indices = [1,2,3]
Reset(new_state, qubit_indices)
```

It can be done anywhere in a circuit, not just at the beginning.
9 changes: 7 additions & 2 deletions docs/src/commonstates.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@ DocTestSetup = quote
rng = StableRNG(42)
end
```
## States

There are numerous frequently used stabilizer states already implemented in this
library.
Stabilizer states can be represented with the [`Stabilizer`](@ref), [`Destabilizer`](@ref), [`MixedStabilizer`](@ref), and [`MixedDestabilizer`](@ref) tableau data structures. You probably want to use [`MixedDestabilizer`](@ref) which supports the widest set of operations.

Moreover, a [`MixedDestabilizer`](@ref) can be stored inside a [`Register`](@ref) together with a set of classical bits in which measurement results can be written.

Below are convenience constructors for common types of states and operators,
already implemented in this library.

## Pauli Operators

Expand Down
23 changes: 23 additions & 0 deletions docs/src/noise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# [Noise Processes](@id noise)

```@meta
DocTestSetup = quote
using QuantumClifford
using StableRNGs
rng = StableRNG(42)
end
```

As seen in the list of [possible gates](@id all-operations),
the simulator is capable of modeling different types of noise.
If that is your goal, please consider using the available
[Monte Carlo simulator](@ref noisycircuits_md) or the
[Symbolic Perturbative Expansion system](@ref noisycircuit_perturb).

The implemented types of noise include:

- [`UnbiasedUncorrelatedNoise`](@ref)

The low-level functionality to work with noise is `applynoise!`,
but most of the time you would probably just want to use
[`NoisyGate`](@ref) and [`NoisyOpAll`](@ref).
5 changes: 5 additions & 0 deletions src/QuantumClifford.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export
stabilizerview, destabilizerview, logicalxview, logicalzview, phases,
bitview, quantumstate, tab,
BadDataStructure,
affectedqubits,
# GF2
stab_to_gf2, gf2_gausselim!, gf2_isinvertible, gf2_invert, gf2_H_to_G,
# Canonicalization
Expand Down Expand Up @@ -57,6 +58,8 @@ export
enumerate_cliffords, symplecticGS, clifford_cardinality, enumerate_phases,
random_invertible_gf2,
random_pauli, random_stabilizer, random_destabilizer, random_clifford,
# Noise
applynoise!, UnbiasedUncorrelatedNoise, NoiseOp, NoiseOpAll, NoisyGate,
# Useful States
bell, ghz,
single_z, single_x, single_y,
Expand Down Expand Up @@ -1387,6 +1390,8 @@ include("classical_register.jl")
include("enumeration.jl")
include("randoms.jl")
include("useful_states.jl")
include("noise.jl")
include("affectedqubits.jl")
include("experimental/Experimental.jl")
include("graphs.jl")
include("entanglement.jl")
Expand Down
11 changes: 11 additions & 0 deletions src/affectedqubits.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""A method giving the qubits acted upon by a given operation. Part of the Noise interface."""
function affectedqubits end
affectedqubits(g::AbstractSingleQubitOperator) = [g.q,]
affectedqubits(g::AbstractTwoQubitOperator) = [g.q1, g.q2]
affectedqubits(g::NoisyGate) = affectedqubits(g.gate)
affectedqubits(g::SparseGate) = g.indices
affectedqubits(b::BellMeasurement) = [m.qubit for m in b.measurements]
affectedqubits(r::Reset) = r.indices
affectedqubits(n::NoiseOp) = n.indices
affectedqubits(g::PauliMeasurement) = 1:length(g.pauli)
affectedqubits(m::AbstractMeasurement) = [m.qubit]
1 change: 1 addition & 0 deletions src/classical_register.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Register(s,bits) = Register(MixedDestabilizer(s), bits)
Register(s) = Register(s, Bool[])

Base.copy(r::Register) = Register(copy(r.stab),copy(r.bits))
Base.:(==)(l::Register,r::Register) = l.stab==r.stab && l.bits==r.bits

stabilizerview(r::Register) = stabilizerview(quantumstate(r))
destabilizerview(r::Register) = destabilizerview(quantumstate(r))
Expand Down
Loading

0 comments on commit da80a22

Please sign in to comment.