Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
with a stabilizer-rank strategy. Without a mask, exactly `shots`
rows are returned. `sample` warns when a masked detector is a constant 1,
which discards every shot. (#252)
- The public API is `tsim.Circuit`, `tsim.CompiledMeasurementSampler` and
`tsim.CompiledDetectorSampler`, which follow semantic versioning. Every other
module is internal and may change without notice, and the API reference
documents only the public and experimental objects. `CompiledStateProbs`,
`SteaneEncoder` and `ColorEncoder5` are importable from `tsim.experimental`,
which may change in any release. The stabilizer rank strategies (`"cat5"`,
`"bss"`, `"cutting"`) are provisional: they may be removed in a minor
release, after at least one minor release in which they emit a
`DeprecationWarning`. A seed reproduces its samples only with the same
versions of Tsim and its dependencies and the same backend (CPU or GPU), and,
with a stabilizer-rank strategy, the same explicit `batch_size`.

### Added
- `CompiledMeasurementSampler.sample(bit_packed=True)`, as in Stim.
Expand Down
4 changes: 2 additions & 2 deletions docs/demos/magic_state_distillation.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"import numpy as np\n",
"from typing import Literal\n",
"import matplotlib.pyplot as plt\n",
"from tsim.utils.encoder import ColorEncoder5"
"from tsim.experimental import ColorEncoder5"
]
},
{
Expand Down Expand Up @@ -338,7 +338,7 @@
"# Encoding the distillation circuit\n",
"\n",
"To address this issue, we need to make our distillation circuit resilient to noise.\n",
"We do this by encoding it using a quantum error correction code, specifically the [17,1,5] color code. The helper class `ColorEncoder5` provides a convenient way to encode the distillation circuit. Each qubit will be encoded into 17 physical qubits using an encoding circuit. After that, each gate of the distillation circuit will be applied transversally to the encoded qubits."
"We do this by encoding it using a quantum error correction code, specifically the [17,1,5] color code. The experimental helper class `tsim.experimental.ColorEncoder5` provides a convenient way to encode the distillation circuit. Each qubit will be encoded into 17 physical qubits using an encoding circuit. After that, each gate of the distillation circuit will be applied transversally to the encoded qubits."
]
},
{
Expand Down
4 changes: 2 additions & 2 deletions docs/demos/overview.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,7 @@
"id": "54",
"metadata": {},
"source": [
"Finally, Tsim allows to compute probability values for target states via the `CompiledStateProbs` sampler."
"Finally, Tsim allows to compute probability values for target states via the experimental `tsim.experimental.CompiledStateProbs` sampler, which may change in any release."
]
},
{
Expand All @@ -645,7 +645,7 @@
"outputs": [],
"source": [
"import numpy as np\n",
"from tsim.sampler import CompiledStateProbs\n",
"from tsim.experimental import CompiledStateProbs\n",
"\n",
"sampler = CompiledStateProbs(c)"
]
Expand Down
8 changes: 8 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ Choose a strategy with the `strategy` argument of `compile_sampler` and `compile
sampler = c.compile_detector_sampler(strategy="cat5")
```

The stabilizer rank strategies are provisional; see [API stability](#api-stability).

The [Tsim 1.0 blog post](https://bloqade.quera.com/blog/introducing-tsim-1-0-0/) introduces the active state vector strategy and compares its performance with other simulators.

## Architecture
Expand Down Expand Up @@ -170,6 +172,12 @@ the noiseless reference sample, and otherwise when their raw parities are 0. A
masked detector that is a constant 1, such as one of a noiseless circuit whose
measurements have parity 1, discards every shot; `sample` warns about it.

## API stability

Tsim follows [semantic versioning](https://semver.org/) for its public API: `tsim.Circuit`, `tsim.CompiledMeasurementSampler` and `tsim.CompiledDetectorSampler`. `tsim.experimental` holds helpers that may change in any release. Every other module is internal, even where it is importable. The stabilizer rank [sampling strategies](#sampling-strategies) (`"cat5"`, `"bss"`, `"cutting"`) are provisional: they may be removed in a minor release, after at least one minor release in which they emit a `DeprecationWarning`. See the [API reference](reference/index.md).

A seed reproduces its samples only with the same versions of Tsim and its dependencies (such as NumPy and JAX) and the same backend (CPU or GPU). With a stabilizer-rank strategy, pass the same explicit `batch_size` as well, since the default batch size depends on the free memory.

## Citing Tsim
If you use Tsim, please consider citing the paper describing the core simulation approach:
```
Expand Down
82 changes: 36 additions & 46 deletions docs/scripts/gen_ref_nav.py
Original file line number Diff line number Diff line change
@@ -1,55 +1,45 @@
# type: ignore
"""Generate the code reference pages and navigation."""

from pathlib import Path
"""Generate the API reference pages for Tsim's public and experimental API."""

import mkdocs_gen_files

SRC_PATH = "src"

skip_keywords = []
PAGES = {
"index.md": (
"Tsim API",
"The public API, which follows semantic versioning. Other modules are "
"internal and may change without notice.",
[
"tsim.Circuit",
"tsim.CompiledMeasurementSampler",
"tsim.CompiledDetectorSampler",
],
),
"experimental.md": (
"Experimental",
"Helpers outside of the stable API. They may change or be removed in any "
"release.",
[
"tsim.experimental.CompiledStateProbs",
"tsim.experimental.SteaneEncoder",
"tsim.experimental.ColorEncoder5",
],
),
}

# NOTE: the encoders' methods are inherited from an internal base class, and the
# bases would name internal classes.
OPTIONS = """ options:
inherited_members: true
show_bases: false
show_inheritance_diagram: false
"""

nav = mkdocs_gen_files.Nav()
for path in sorted(Path(SRC_PATH).rglob("*.py")):
module_path = path.relative_to(SRC_PATH).with_suffix("")
doc_path = path.relative_to(SRC_PATH).with_suffix(".md")
full_doc_path = Path("reference", doc_path)

iskip = False

# Skip anything in the external folder
if "external" in str(doc_path):
iskip = True

for kwrd in skip_keywords:
if kwrd in str(doc_path):
iskip = True
break
if iskip:
print("[Ignore]", str(doc_path))
continue

print("[>]", str(doc_path))

parts = tuple(module_path.parts)

if parts[-1] == "__init__":
parts = parts[:-1]
doc_path = doc_path.with_name("index.md")
full_doc_path = full_doc_path.with_name("index.md")

# [note] if selectionally ignore some files
# elif parts[-1].startswith("_wrapper"):
# pass
# else:
# continue

nav[parts] = doc_path.as_posix()
with mkdocs_gen_files.open(full_doc_path, "w") as fd:
ident = ".".join(parts)
fd.write(f"::: {ident}")

mkdocs_gen_files.set_edit_path(full_doc_path, ".." / path)
for doc_path, (title, intro, objects) in PAGES.items():
nav[title] = doc_path
with mkdocs_gen_files.open(f"reference/{doc_path}", "w") as fd:
fd.write(f"# {title}\n\n{intro}\n\n")
fd.writelines(f"::: {obj}\n{OPTIONS}\n" for obj in objects)

with mkdocs_gen_files.open("reference/SUMMARY.txt", "w") as nav_file:
nav_file.writelines(nav.build_literate_nav())
14 changes: 5 additions & 9 deletions src/tsim/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,9 @@
It exposes `Circuit`, `CompiledDetectorSampler`, and `CompiledMeasurementSampler`
objects, which follow the Stim API.

The package is organized as follows:

- `circuit.py`: The thin `Circuit` wrapper around `stim.Circuit`.
- `sampler.py`: Orchestrates compilation and evaluation to sample measurements or detectors.
- `core/`: Builds quantum/classical ZX graphs with zxparam; parsing and data types.
- `compile/`: Compiles zxparam's stabilizer-rank decompositions to JAX.
- `statevector/`: Samples non-direct components as Pauli rotations and measurements on a dense state vector (`strategy="active_statevector"`).
- `noise/`: Pauli noise channels and detector error models.
- `utils/`: Utility functions for visualization and linear algebra.
These three objects are Tsim's public API, which follows semantic versioning.
`tsim.experimental` holds helpers that may change in any release. Every other
module is internal and may change without notice, even where it is importable.
"""

__version__ = "0.1.5"
Expand All @@ -22,3 +16,5 @@
CompiledDetectorSampler as CompiledDetectorSampler,
CompiledMeasurementSampler as CompiledMeasurementSampler,
)

__all__ = ["Circuit", "CompiledDetectorSampler", "CompiledMeasurementSampler"]
29 changes: 18 additions & 11 deletions src/tsim/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -953,12 +953,16 @@ def compile_sampler(
non-direct component on a dense state vector over its active
qubits, or a stabilizer rank decomposition strategy ("cat5",
"bss", "cutting"). "active_statevector" samples on the CPU, on
``numba.get_num_threads()`` threads.
seed: Random seed for the sampler. If None, a random seed is drawn. With
"active_statevector", and under any strategy for a circuit without
``numba.get_num_threads()`` threads. The stabilizer rank
strategies are provisional.
seed: Random seed for the sampler. If None, a random seed is drawn. A seed
reproduces its samples only with the same versions of Tsim and its
dependencies (such as NumPy and JAX) and the same backend (CPU or GPU).
With "active_statevector", and under any strategy for a circuit without
non-direct components, the samples of a seed depend on neither the batch
size nor the thread count; with a stabilizer-rank strategy they are only
deterministic for a fixed batch size.
size nor the thread count. With a stabilizer-rank strategy they also
need the same explicit ``batch_size``, since the default batch size
depends on the free memory.

Returns:
A CompiledMeasurementSampler that can be used to sample measurements.
Expand Down Expand Up @@ -986,13 +990,16 @@ def compile_detector_sampler(
non-direct component on a dense state vector over its active
qubits, or a stabilizer rank decomposition strategy ("cat5",
"bss", "cutting"). "active_statevector" samples on the CPU, on
``numba.get_num_threads()`` threads.
seed: Random seed for the sampler. If None, a random seed is drawn. With
"active_statevector", and under any strategy for a circuit without
``numba.get_num_threads()`` threads. The stabilizer rank
strategies are provisional.
seed: Random seed for the sampler. If None, a random seed is drawn. A seed
reproduces its samples only with the same versions of Tsim and its
dependencies (such as NumPy and JAX) and the same backend (CPU or GPU).
With "active_statevector", and under any strategy for a circuit without
non-direct components, the samples of a seed depend on neither the batch
size nor the thread count; with a stabilizer-rank strategy they are only
deterministic for a fixed batch size and fixed reference sample
settings.
size nor the thread count. With a stabilizer-rank strategy they also
need the same explicit ``batch_size`` and the same reference sample
settings, since the default batch size depends on the free memory.

Returns:
A CompiledDetectorSampler that can be used to sample detectors and observables.
Expand Down
18 changes: 18 additions & 0 deletions src/tsim/experimental.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""Experimental helpers, outside of Tsim's stable API.

The objects here may change or be removed in any release, including patch
releases.

- `CompiledStateProbs` computes the probabilities of given measurement or
detector outcomes with a stabilizer rank decomposition.
- `SteaneEncoder` and `ColorEncoder5` encode a circuit transversally into the
[[7,1,3]] Steane code and the [[17,1,5]] color code.
"""

from tsim.sampler import CompiledStateProbs as CompiledStateProbs
from tsim.utils.encoder import (
ColorEncoder5 as ColorEncoder5,
SteaneEncoder as SteaneEncoder,
)

__all__ = ["ColorEncoder5", "CompiledStateProbs", "SteaneEncoder"]
49 changes: 31 additions & 18 deletions src/tsim/sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,12 +313,16 @@ def __init__(
non-direct component on a dense state vector over its active
qubits, or a stabilizer rank decomposition strategy ("cat5",
"bss", "cutting"). "active_statevector" samples on the CPU, on
``numba.get_num_threads()`` threads.
seed: Random seed for the sampler. If None, a random seed is drawn. With
"active_statevector", and under any strategy for a circuit without
``numba.get_num_threads()`` threads. The stabilizer rank
strategies are provisional.
seed: Random seed for the sampler. If None, a random seed is drawn. A seed
reproduces its samples only with the same versions of Tsim and its
dependencies (such as NumPy and JAX) and the same backend (CPU or GPU).
With "active_statevector", and under any strategy for a circuit without
non-direct components, the samples of a seed depend on neither the batch
size nor the thread count; with a stabilizer-rank strategy they are only
deterministic for a fixed batch size.
size nor the thread count. With a stabilizer-rank strategy they also
need the same explicit ``batch_size``, since the default batch size
depends on the free memory.

"""
if seed is None:
Expand Down Expand Up @@ -767,12 +771,16 @@ def __init__(
Args:
circuit: The quantum circuit to compile.
strategy: "active_statevector" (the default), or a stabilizer rank
decomposition strategy ("cat5", "bss", "cutting").
seed: Random seed for the sampler. If None, a random seed is drawn. With
"active_statevector", and under any strategy for a circuit without
decomposition strategy ("cat5", "bss", "cutting"). The
stabilizer rank strategies are provisional.
seed: Random seed for the sampler. If None, a random seed is drawn. A seed
reproduces its samples only with the same versions of Tsim and its
dependencies (such as NumPy and JAX) and the same backend (CPU or GPU).
With "active_statevector", and under any strategy for a circuit without
non-direct components, the samples of a seed depend on neither the batch
size nor the thread count; with a stabilizer-rank strategy they are only
deterministic for a fixed batch size.
size nor the thread count. With a stabilizer-rank strategy they also
need the same explicit ``batch_size``, since the default batch size
depends on the free memory.

"""
super().__init__(
Expand Down Expand Up @@ -863,13 +871,16 @@ def __init__(
Args:
circuit: The quantum circuit to compile.
strategy: "active_statevector" (the default), or a stabilizer rank
decomposition strategy ("cat5", "bss", "cutting").
seed: Random seed for the sampler. If None, a random seed is drawn. With
"active_statevector", and under any strategy for a circuit without
decomposition strategy ("cat5", "bss", "cutting"). The
stabilizer rank strategies are provisional.
seed: Random seed for the sampler. If None, a random seed is drawn. A seed
reproduces its samples only with the same versions of Tsim and its
dependencies (such as NumPy and JAX) and the same backend (CPU or GPU).
With "active_statevector", and under any strategy for a circuit without
non-direct components, the samples of a seed depend on neither the batch
size nor the thread count; with a stabilizer-rank strategy they are only
deterministic for a fixed batch size and fixed reference sample
settings.
size nor the thread count. With a stabilizer-rank strategy they also
need the same explicit ``batch_size`` and the same reference sample
settings, since the default batch size depends on the free memory.

"""
super().__init__(
Expand Down Expand Up @@ -1062,8 +1073,10 @@ def __init__(
sample_detectors: If True, compute detector/observable probabilities.
strategy: Stabilizer rank decomposition strategy ("cat5", "bss",
"cutting"); "active_statevector" cannot compute probabilities.
seed: Random seed. If None, a random seed is generated. Note that
deterministic results are only guaranteed for a fixed batch size.
seed: Random seed. If None, a random seed is generated. Results are
only deterministic for a fixed batch size, the same versions of
Tsim and its dependencies (such as NumPy and JAX) and the same
backend (CPU or GPU).

"""
super().__init__(
Expand Down
24 changes: 24 additions & 0 deletions test/unit/test_public_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import tsim
import tsim.experimental
from tsim.sampler import CompiledStateProbs
from tsim.utils.encoder import ColorEncoder5, SteaneEncoder


def test_public_api():
assert sorted(tsim.__all__) == [
"Circuit",
"CompiledDetectorSampler",
"CompiledMeasurementSampler",
]
assert all(hasattr(tsim, name) for name in tsim.__all__)


def test_experimental_api():
assert tsim.experimental.CompiledStateProbs is CompiledStateProbs
assert tsim.experimental.ColorEncoder5 is ColorEncoder5
assert tsim.experimental.SteaneEncoder is SteaneEncoder
assert sorted(tsim.experimental.__all__) == [
"ColorEncoder5",
"CompiledStateProbs",
"SteaneEncoder",
]
Loading