diff --git a/doc/python_api_reference_vDev.md b/doc/python_api_reference_vDev.md
index 145d04ac..4ba62593 100644
--- a/doc/python_api_reference_vDev.md
+++ b/doc/python_api_reference_vDev.md
@@ -186,6 +186,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi
- [`stim.FlipSimulator.__init__`](#stim.FlipSimulator.__init__)
- [`stim.FlipSimulator.batch_size`](#stim.FlipSimulator.batch_size)
- [`stim.FlipSimulator.broadcast_pauli_errors`](#stim.FlipSimulator.broadcast_pauli_errors)
+ - [`stim.FlipSimulator.copy`](#stim.FlipSimulator.copy)
- [`stim.FlipSimulator.do`](#stim.FlipSimulator.do)
- [`stim.FlipSimulator.get_detector_flips`](#stim.FlipSimulator.get_detector_flips)
- [`stim.FlipSimulator.get_measurement_flips`](#stim.FlipSimulator.get_measurement_flips)
@@ -195,6 +196,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi
- [`stim.FlipSimulator.num_observables`](#stim.FlipSimulator.num_observables)
- [`stim.FlipSimulator.num_qubits`](#stim.FlipSimulator.num_qubits)
- [`stim.FlipSimulator.peek_pauli_flips`](#stim.FlipSimulator.peek_pauli_flips)
+ - [`stim.FlipSimulator.reset`](#stim.FlipSimulator.reset)
- [`stim.FlipSimulator.set_pauli_flip`](#stim.FlipSimulator.set_pauli_flip)
- [`stim.FlippedMeasurement`](#stim.FlippedMeasurement)
- [`stim.FlippedMeasurement.__init__`](#stim.FlippedMeasurement.__init__)
@@ -1680,11 +1682,19 @@ def diagram(
Passing `range(A, B)` for a time slice will show the
operations between tick A and tick B.
- filter_coords: A set of acceptable coordinate prefixes, or
- desired stim.DemTargets. For detector slice diagrams, only
- detectors match one of the filters are included. If no filter
- is specified, all detectors are included (but no observables).
- To include an observable, add it as one of the filters.
+ rows: In diagrams that have multiple separate pieces, such as timeslice
+ diagrams and detslice diagrams, this controls how many rows of
+ pieces there will be. If not specified, a number of rows that creates
+ a roughly square layout will be chosen.
+ filter_coords: A list of things to include in the diagram. Different
+ effects depending on the diagram.
+
+ For detslice diagrams, the filter defaults to showing all detectors
+ and no observables. When specified, each list entry can be a collection
+ of floats (detectors whose coordinates start with the same numbers will
+ be included), a stim.DemTarget (specifying a detector or observable
+ to include), a string like "D5" or "L0" specifying a detector or
+ observable to include.
Returns:
An object whose `__str__` method returns the diagram, so that
@@ -5304,7 +5314,24 @@ def __str__(
def args_copy(
self,
) -> List[float]:
- """Returns a copy of the list of numbers parameterizing the instruction (e.g. the probability of an error).
+ """Returns a copy of the list of numbers parameterizing the instruction.
+
+ For example, this would be coordinates of a detector instruction or the
+ probability of an error instruction. The result is a copy, meaning that
+ editing it won't change the instruction's targets or future copies.
+
+ Examples:
+ >>> import stim
+ >>> instruction = stim.DetectorErrorModel('''
+ ... error(0.125) D0
+ ... ''')[0]
+ >>> instruction.args_copy()
+ [0.125]
+
+ >>> instruction.args_copy() == instruction.args_copy()
+ True
+ >>> instruction.args_copy() is instruction.args_copy()
+ False
"""
```
@@ -5318,8 +5345,21 @@ def targets_copy(
) -> List[Union[int, stim.DemTarget]]:
"""Returns a copy of the instruction's targets.
- (Making a copy is enforced to make it clear that editing the result won't change
- the instruction's targets.)
+ The result is a copy, meaning that editing it won't change the instruction's
+ targets or future copies.
+
+ Examples:
+ >>> import stim
+ >>> instruction = stim.DetectorErrorModel('''
+ ... error(0.125) D0 L2
+ ... ''')[0]
+ >>> instruction.targets_copy()
+ [stim.DemTarget('D0'), stim.DemTarget('L2')]
+
+ >>> instruction.targets_copy() == instruction.targets_copy()
+ True
+ >>> instruction.targets_copy() is instruction.targets_copy()
+ False
"""
```
@@ -7177,6 +7217,83 @@ def broadcast_pauli_errors(
"""
```
+
+```python
+# stim.FlipSimulator.copy
+
+# (in class stim.FlipSimulator)
+def copy(
+ self,
+ *,
+ copy_rng: bool = False,
+ seed: Optional[int] = None,
+) -> stim.FlipSimulator:
+ """Returns a simulator with the same internal state, except perhaps its prng.
+
+ Args:
+ copy_rng: Defaults to False. When False, the copy's pseudo random number
+ generator is reinitialized with a random seed instead of being a copy
+ of the original simulator's pseudo random number generator. This
+ causes the copy and the original to sample independent randomness,
+ instead of identical randomness, for future random operations. When set
+ to true, the copy will have the exact same pseudo random number
+ generator state as the original, and so will produce identical results
+ if told to do the same noisy operations. This argument is incompatible
+ with the `seed` argument.
+
+ seed: PARTIALLY determines simulation results by deterministically seeding
+ the random number generator.
+
+ Must be None or an integer in range(2**64).
+
+ Defaults to None. When None, the prng state is either copied from the
+ original simulator or reseeded from system entropy, depending on the
+ copy_rng argument.
+
+ When set to an integer, making the exact same series calls on the exact
+ same machine with the exact same version of Stim will produce the exact
+ same simulation results.
+
+ CAUTION: simulation results *WILL NOT* be consistent between versions of
+ Stim. This restriction is present to make it possible to have future
+ optimizations to the random sampling, and is enforced by introducing
+ intentional differences in the seeding strategy from version to version.
+
+ CAUTION: simulation results *MAY NOT* be consistent across machines that
+ differ in the width of supported SIMD instructions. For example, using
+ the same seed on a machine that supports AVX instructions and one that
+ only supports SSE instructions may produce different simulation results.
+
+ CAUTION: simulation results *MAY NOT* be consistent if you vary how the
+ circuit is executed. For example, reordering whether a reset on one
+ qubit happens before or after a reset on another qubit can result in
+ different measurement results being observed starting from the same
+ seed.
+
+ Returns:
+ The copy of the simulator.
+
+ Examples:
+ >>> import stim
+ >>> import numpy as np
+
+ >>> s1 = stim.FlipSimulator(batch_size=256)
+ >>> s1.set_pauli_flip('X', qubit_index=2, instance_index=3)
+ >>> s2 = s1.copy()
+ >>> s2 is s1
+ False
+ >>> s2.peek_pauli_flips() == s1.peek_pauli_flips()
+ True
+
+ >>> s1 = stim.FlipSimulator(batch_size=256)
+ >>> s2 = s1.copy(copy_rng=True)
+ >>> s1.do(stim.Circuit("X_ERROR(0.25) 0 \n M 0"))
+ >>> s2.do(stim.Circuit("X_ERROR(0.25) 0 \n M 0"))
+ >>> np.array_equal(s1.get_measurement_flips(), s2.get_measurement_flips())
+ True
+ """
+```
+
```python
# stim.FlipSimulator.do
@@ -7613,6 +7730,38 @@ def peek_pauli_flips(
"""
```
+
+```python
+# stim.FlipSimulator.reset
+
+# (in class stim.FlipSimulator)
+def reset(
+ self,
+) -> None:
+ """Resets the simulator's state, so it can be reused for another simulation.
+
+ This empties the measurement flip history, empties the detector flip history,
+ and zeroes the observable flip state. It also resets all qubits to |0>. If
+ stabilizer randomization is disabled, this zeros all pauli flips data. Otherwise
+ it randomizes all pauli flips to be I or Z with equal probability.
+
+ Examples:
+ >>> import stim
+ >>> sim = stim.FlipSimulator(batch_size=256)
+ >>> sim.do(stim.Circuit("M(0.1) 9"))
+ >>> sim.num_qubits
+ 10
+ >>> sim.get_measurement_flips().shape
+ (1, 256)
+
+ >>> sim.reset()
+ >>> sim.num_qubits
+ 10
+ >>> sim.get_measurement_flips().shape
+ (0, 256)
+ """
+```
+
```python
# stim.FlipSimulator.set_pauli_flip
@@ -9533,6 +9682,13 @@ def __len__(
self,
) -> int:
"""Returns the length the pauli string; the number of qubits it operates on.
+
+ Examples:
+ >>> import stim
+ >>> len(stim.PauliString("XY_ZZ"))
+ 5
+ >>> len(stim.PauliString("X0*Z99"))
+ 100
"""
```
@@ -10663,6 +10819,12 @@ def __len__(
self,
) -> int:
"""Returns the number of qubits operated on by the tableau.
+
+ Examples:
+ >>> import stim
+ >>> t = stim.Tableau.from_named_gate("CNOT")
+ >>> len(t)
+ 2
"""
```
diff --git a/doc/stim.pyi b/doc/stim.pyi
index bbd39402..cd232b6a 100644
--- a/doc/stim.pyi
+++ b/doc/stim.pyi
@@ -1084,11 +1084,19 @@ class Circuit:
Passing `range(A, B)` for a time slice will show the
operations between tick A and tick B.
- filter_coords: A set of acceptable coordinate prefixes, or
- desired stim.DemTargets. For detector slice diagrams, only
- detectors match one of the filters are included. If no filter
- is specified, all detectors are included (but no observables).
- To include an observable, add it as one of the filters.
+ rows: In diagrams that have multiple separate pieces, such as timeslice
+ diagrams and detslice diagrams, this controls how many rows of
+ pieces there will be. If not specified, a number of rows that creates
+ a roughly square layout will be chosen.
+ filter_coords: A list of things to include in the diagram. Different
+ effects depending on the diagram.
+
+ For detslice diagrams, the filter defaults to showing all detectors
+ and no observables. When specified, each list entry can be a collection
+ of floats (detectors whose coordinates start with the same numbers will
+ be included), a stim.DemTarget (specifying a detector or observable
+ to include), a string like "D5" or "L0" specifying a detector or
+ observable to include.
Returns:
An object whose `__str__` method returns the diagram, so that
@@ -4145,15 +4153,45 @@ class DemInstruction:
def args_copy(
self,
) -> List[float]:
- """Returns a copy of the list of numbers parameterizing the instruction (e.g. the probability of an error).
+ """Returns a copy of the list of numbers parameterizing the instruction.
+
+ For example, this would be coordinates of a detector instruction or the
+ probability of an error instruction. The result is a copy, meaning that
+ editing it won't change the instruction's targets or future copies.
+
+ Examples:
+ >>> import stim
+ >>> instruction = stim.DetectorErrorModel('''
+ ... error(0.125) D0
+ ... ''')[0]
+ >>> instruction.args_copy()
+ [0.125]
+
+ >>> instruction.args_copy() == instruction.args_copy()
+ True
+ >>> instruction.args_copy() is instruction.args_copy()
+ False
"""
def targets_copy(
self,
) -> List[Union[int, stim.DemTarget]]:
"""Returns a copy of the instruction's targets.
- (Making a copy is enforced to make it clear that editing the result won't change
- the instruction's targets.)
+ The result is a copy, meaning that editing it won't change the instruction's
+ targets or future copies.
+
+ Examples:
+ >>> import stim
+ >>> instruction = stim.DetectorErrorModel('''
+ ... error(0.125) D0 L2
+ ... ''')[0]
+ >>> instruction.targets_copy()
+ [stim.DemTarget('D0'), stim.DemTarget('L2')]
+
+ >>> instruction.targets_copy() == instruction.targets_copy()
+ True
+ >>> instruction.targets_copy() is instruction.targets_copy()
+ False
"""
@property
def type(
@@ -5575,6 +5613,76 @@ class FlipSimulator:
>>> sim.peek_pauli_flips()
[stim.PauliString("+X_Y"), stim.PauliString("+Z_Y")]
"""
+ def copy(
+ self,
+ *,
+ copy_rng: bool = False,
+ seed: Optional[int] = None,
+ ) -> stim.FlipSimulator:
+ """Returns a simulator with the same internal state, except perhaps its prng.
+
+ Args:
+ copy_rng: Defaults to False. When False, the copy's pseudo random number
+ generator is reinitialized with a random seed instead of being a copy
+ of the original simulator's pseudo random number generator. This
+ causes the copy and the original to sample independent randomness,
+ instead of identical randomness, for future random operations. When set
+ to true, the copy will have the exact same pseudo random number
+ generator state as the original, and so will produce identical results
+ if told to do the same noisy operations. This argument is incompatible
+ with the `seed` argument.
+
+ seed: PARTIALLY determines simulation results by deterministically seeding
+ the random number generator.
+
+ Must be None or an integer in range(2**64).
+
+ Defaults to None. When None, the prng state is either copied from the
+ original simulator or reseeded from system entropy, depending on the
+ copy_rng argument.
+
+ When set to an integer, making the exact same series calls on the exact
+ same machine with the exact same version of Stim will produce the exact
+ same simulation results.
+
+ CAUTION: simulation results *WILL NOT* be consistent between versions of
+ Stim. This restriction is present to make it possible to have future
+ optimizations to the random sampling, and is enforced by introducing
+ intentional differences in the seeding strategy from version to version.
+
+ CAUTION: simulation results *MAY NOT* be consistent across machines that
+ differ in the width of supported SIMD instructions. For example, using
+ the same seed on a machine that supports AVX instructions and one that
+ only supports SSE instructions may produce different simulation results.
+
+ CAUTION: simulation results *MAY NOT* be consistent if you vary how the
+ circuit is executed. For example, reordering whether a reset on one
+ qubit happens before or after a reset on another qubit can result in
+ different measurement results being observed starting from the same
+ seed.
+
+ Returns:
+ The copy of the simulator.
+
+ Examples:
+ >>> import stim
+ >>> import numpy as np
+
+ >>> s1 = stim.FlipSimulator(batch_size=256)
+ >>> s1.set_pauli_flip('X', qubit_index=2, instance_index=3)
+ >>> s2 = s1.copy()
+ >>> s2 is s1
+ False
+ >>> s2.peek_pauli_flips() == s1.peek_pauli_flips()
+ True
+
+ >>> s1 = stim.FlipSimulator(batch_size=256)
+ >>> s2 = s1.copy(copy_rng=True)
+ >>> s1.do(stim.Circuit("X_ERROR(0.25) 0 \n M 0"))
+ >>> s2.do(stim.Circuit("X_ERROR(0.25) 0 \n M 0"))
+ >>> np.array_equal(s1.get_measurement_flips(), s2.get_measurement_flips())
+ True
+ """
def do(
self,
obj: Union[stim.Circuit, stim.CircuitInstruction, stim.CircuitRepeatBlock],
@@ -5948,6 +6056,31 @@ class FlipSimulator:
>>> sorted(set(str(flips))) # Should have Zs from stabilizer randomization
['+', 'Z', '_']
"""
+ def reset(
+ self,
+ ) -> None:
+ """Resets the simulator's state, so it can be reused for another simulation.
+
+ This empties the measurement flip history, empties the detector flip history,
+ and zeroes the observable flip state. It also resets all qubits to |0>. If
+ stabilizer randomization is disabled, this zeros all pauli flips data. Otherwise
+ it randomizes all pauli flips to be I or Z with equal probability.
+
+ Examples:
+ >>> import stim
+ >>> sim = stim.FlipSimulator(batch_size=256)
+ >>> sim.do(stim.Circuit("M(0.1) 9"))
+ >>> sim.num_qubits
+ 10
+ >>> sim.get_measurement_flips().shape
+ (1, 256)
+
+ >>> sim.reset()
+ >>> sim.num_qubits
+ 10
+ >>> sim.get_measurement_flips().shape
+ (0, 256)
+ """
def set_pauli_flip(
self,
pauli: Union[str, int],
@@ -7401,6 +7534,13 @@ class PauliString:
self,
) -> int:
"""Returns the length the pauli string; the number of qubits it operates on.
+
+ Examples:
+ >>> import stim
+ >>> len(stim.PauliString("XY_ZZ"))
+ 5
+ >>> len(stim.PauliString("X0*Z99"))
+ 100
"""
def __mul__(
self,
@@ -8306,6 +8446,12 @@ class Tableau:
self,
) -> int:
"""Returns the number of qubits operated on by the tableau.
+
+ Examples:
+ >>> import stim
+ >>> t = stim.Tableau.from_named_gate("CNOT")
+ >>> len(t)
+ 2
"""
def __mul__(
self,
diff --git a/glue/python/src/stim/__init__.pyi b/glue/python/src/stim/__init__.pyi
index bbd39402..cd232b6a 100644
--- a/glue/python/src/stim/__init__.pyi
+++ b/glue/python/src/stim/__init__.pyi
@@ -1084,11 +1084,19 @@ class Circuit:
Passing `range(A, B)` for a time slice will show the
operations between tick A and tick B.
- filter_coords: A set of acceptable coordinate prefixes, or
- desired stim.DemTargets. For detector slice diagrams, only
- detectors match one of the filters are included. If no filter
- is specified, all detectors are included (but no observables).
- To include an observable, add it as one of the filters.
+ rows: In diagrams that have multiple separate pieces, such as timeslice
+ diagrams and detslice diagrams, this controls how many rows of
+ pieces there will be. If not specified, a number of rows that creates
+ a roughly square layout will be chosen.
+ filter_coords: A list of things to include in the diagram. Different
+ effects depending on the diagram.
+
+ For detslice diagrams, the filter defaults to showing all detectors
+ and no observables. When specified, each list entry can be a collection
+ of floats (detectors whose coordinates start with the same numbers will
+ be included), a stim.DemTarget (specifying a detector or observable
+ to include), a string like "D5" or "L0" specifying a detector or
+ observable to include.
Returns:
An object whose `__str__` method returns the diagram, so that
@@ -4145,15 +4153,45 @@ class DemInstruction:
def args_copy(
self,
) -> List[float]:
- """Returns a copy of the list of numbers parameterizing the instruction (e.g. the probability of an error).
+ """Returns a copy of the list of numbers parameterizing the instruction.
+
+ For example, this would be coordinates of a detector instruction or the
+ probability of an error instruction. The result is a copy, meaning that
+ editing it won't change the instruction's targets or future copies.
+
+ Examples:
+ >>> import stim
+ >>> instruction = stim.DetectorErrorModel('''
+ ... error(0.125) D0
+ ... ''')[0]
+ >>> instruction.args_copy()
+ [0.125]
+
+ >>> instruction.args_copy() == instruction.args_copy()
+ True
+ >>> instruction.args_copy() is instruction.args_copy()
+ False
"""
def targets_copy(
self,
) -> List[Union[int, stim.DemTarget]]:
"""Returns a copy of the instruction's targets.
- (Making a copy is enforced to make it clear that editing the result won't change
- the instruction's targets.)
+ The result is a copy, meaning that editing it won't change the instruction's
+ targets or future copies.
+
+ Examples:
+ >>> import stim
+ >>> instruction = stim.DetectorErrorModel('''
+ ... error(0.125) D0 L2
+ ... ''')[0]
+ >>> instruction.targets_copy()
+ [stim.DemTarget('D0'), stim.DemTarget('L2')]
+
+ >>> instruction.targets_copy() == instruction.targets_copy()
+ True
+ >>> instruction.targets_copy() is instruction.targets_copy()
+ False
"""
@property
def type(
@@ -5575,6 +5613,76 @@ class FlipSimulator:
>>> sim.peek_pauli_flips()
[stim.PauliString("+X_Y"), stim.PauliString("+Z_Y")]
"""
+ def copy(
+ self,
+ *,
+ copy_rng: bool = False,
+ seed: Optional[int] = None,
+ ) -> stim.FlipSimulator:
+ """Returns a simulator with the same internal state, except perhaps its prng.
+
+ Args:
+ copy_rng: Defaults to False. When False, the copy's pseudo random number
+ generator is reinitialized with a random seed instead of being a copy
+ of the original simulator's pseudo random number generator. This
+ causes the copy and the original to sample independent randomness,
+ instead of identical randomness, for future random operations. When set
+ to true, the copy will have the exact same pseudo random number
+ generator state as the original, and so will produce identical results
+ if told to do the same noisy operations. This argument is incompatible
+ with the `seed` argument.
+
+ seed: PARTIALLY determines simulation results by deterministically seeding
+ the random number generator.
+
+ Must be None or an integer in range(2**64).
+
+ Defaults to None. When None, the prng state is either copied from the
+ original simulator or reseeded from system entropy, depending on the
+ copy_rng argument.
+
+ When set to an integer, making the exact same series calls on the exact
+ same machine with the exact same version of Stim will produce the exact
+ same simulation results.
+
+ CAUTION: simulation results *WILL NOT* be consistent between versions of
+ Stim. This restriction is present to make it possible to have future
+ optimizations to the random sampling, and is enforced by introducing
+ intentional differences in the seeding strategy from version to version.
+
+ CAUTION: simulation results *MAY NOT* be consistent across machines that
+ differ in the width of supported SIMD instructions. For example, using
+ the same seed on a machine that supports AVX instructions and one that
+ only supports SSE instructions may produce different simulation results.
+
+ CAUTION: simulation results *MAY NOT* be consistent if you vary how the
+ circuit is executed. For example, reordering whether a reset on one
+ qubit happens before or after a reset on another qubit can result in
+ different measurement results being observed starting from the same
+ seed.
+
+ Returns:
+ The copy of the simulator.
+
+ Examples:
+ >>> import stim
+ >>> import numpy as np
+
+ >>> s1 = stim.FlipSimulator(batch_size=256)
+ >>> s1.set_pauli_flip('X', qubit_index=2, instance_index=3)
+ >>> s2 = s1.copy()
+ >>> s2 is s1
+ False
+ >>> s2.peek_pauli_flips() == s1.peek_pauli_flips()
+ True
+
+ >>> s1 = stim.FlipSimulator(batch_size=256)
+ >>> s2 = s1.copy(copy_rng=True)
+ >>> s1.do(stim.Circuit("X_ERROR(0.25) 0 \n M 0"))
+ >>> s2.do(stim.Circuit("X_ERROR(0.25) 0 \n M 0"))
+ >>> np.array_equal(s1.get_measurement_flips(), s2.get_measurement_flips())
+ True
+ """
def do(
self,
obj: Union[stim.Circuit, stim.CircuitInstruction, stim.CircuitRepeatBlock],
@@ -5948,6 +6056,31 @@ class FlipSimulator:
>>> sorted(set(str(flips))) # Should have Zs from stabilizer randomization
['+', 'Z', '_']
"""
+ def reset(
+ self,
+ ) -> None:
+ """Resets the simulator's state, so it can be reused for another simulation.
+
+ This empties the measurement flip history, empties the detector flip history,
+ and zeroes the observable flip state. It also resets all qubits to |0>. If
+ stabilizer randomization is disabled, this zeros all pauli flips data. Otherwise
+ it randomizes all pauli flips to be I or Z with equal probability.
+
+ Examples:
+ >>> import stim
+ >>> sim = stim.FlipSimulator(batch_size=256)
+ >>> sim.do(stim.Circuit("M(0.1) 9"))
+ >>> sim.num_qubits
+ 10
+ >>> sim.get_measurement_flips().shape
+ (1, 256)
+
+ >>> sim.reset()
+ >>> sim.num_qubits
+ 10
+ >>> sim.get_measurement_flips().shape
+ (0, 256)
+ """
def set_pauli_flip(
self,
pauli: Union[str, int],
@@ -7401,6 +7534,13 @@ class PauliString:
self,
) -> int:
"""Returns the length the pauli string; the number of qubits it operates on.
+
+ Examples:
+ >>> import stim
+ >>> len(stim.PauliString("XY_ZZ"))
+ 5
+ >>> len(stim.PauliString("X0*Z99"))
+ 100
"""
def __mul__(
self,
@@ -8306,6 +8446,12 @@ class Tableau:
self,
) -> int:
"""Returns the number of qubits operated on by the tableau.
+
+ Examples:
+ >>> import stim
+ >>> t = stim.Tableau.from_named_gate("CNOT")
+ >>> len(t)
+ 2
"""
def __mul__(
self,
diff --git a/src/stim/circuit/circuit.pybind.cc b/src/stim/circuit/circuit.pybind.cc
index 4c870e61..8c801e5c 100644
--- a/src/stim/circuit/circuit.pybind.cc
+++ b/src/stim/circuit/circuit.pybind.cc
@@ -3267,6 +3267,7 @@ void stim_pybind::pybind_circuit_methods(pybind11::module &, pybind11::class_ 'stim._DiagramHelper':
@@ -3343,11 +3344,19 @@ void stim_pybind::pybind_circuit_methods(pybind11::module &, pybind11::class_ filter_coords;
try {
@@ -221,6 +222,11 @@ DiagramHelper stim_pybind::circuit_diagram(
throw std::invalid_argument("filter_coords wasn't an Iterable[stim.DemTarget | Iterable[float]].");
}
+ size_t num_rows = 0;
+ if (!rows.is_none()) {
+ num_rows = pybind11::cast(rows);
+ }
+
uint64_t tick_min;
uint64_t num_ticks;
if (tick.is_none()) {
@@ -262,7 +268,7 @@ DiagramHelper stim_pybind::circuit_diagram(
type == "timeslice" || type == "time-slice") {
std::stringstream out;
DiagramTimelineSvgDrawer::make_diagram_write_to(
- circuit, out, tick_min, num_ticks, DiagramTimelineSvgDrawerMode::SVG_MODE_TIME_SLICE, filter_coords);
+ circuit, out, tick_min, num_ticks, DiagramTimelineSvgDrawerMode::SVG_MODE_TIME_SLICE, filter_coords, num_rows);
DiagramType d_type =
type.find("html") != std::string::npos ? DiagramType::DIAGRAM_TYPE_SVG_HTML : DiagramType::DIAGRAM_TYPE_SVG;
return DiagramHelper{d_type, out.str()};
@@ -270,7 +276,7 @@ DiagramHelper stim_pybind::circuit_diagram(
type == "detslice-svg" || type == "detslice" || type == "detslice-html" || type == "detslice-svg-html" ||
type == "detector-slice-svg" || type == "detector-slice") {
std::stringstream out;
- DetectorSliceSet::from_circuit_ticks(circuit, tick_min, num_ticks, filter_coords).write_svg_diagram_to(out);
+ DetectorSliceSet::from_circuit_ticks(circuit, tick_min, num_ticks, filter_coords).write_svg_diagram_to(out, num_rows);
DiagramType d_type =
type.find("html") != std::string::npos ? DiagramType::DIAGRAM_TYPE_SVG_HTML : DiagramType::DIAGRAM_TYPE_SVG;
return DiagramHelper{d_type, out.str()};
@@ -284,7 +290,8 @@ DiagramHelper stim_pybind::circuit_diagram(
tick_min,
num_ticks,
DiagramTimelineSvgDrawerMode::SVG_MODE_TIME_DETECTOR_SLICE,
- filter_coords);
+ filter_coords,
+ num_rows);
DiagramType d_type =
type.find("html") != std::string::npos ? DiagramType::DIAGRAM_TYPE_SVG_HTML : DiagramType::DIAGRAM_TYPE_SVG;
return DiagramHelper{d_type, out.str()};
diff --git a/src/stim/cmd/command_diagram.pybind.h b/src/stim/cmd/command_diagram.pybind.h
index 9e38276f..7c25a628 100644
--- a/src/stim/cmd/command_diagram.pybind.h
+++ b/src/stim/cmd/command_diagram.pybind.h
@@ -42,6 +42,7 @@ DiagramHelper circuit_diagram(
const stim::Circuit &circuit,
std::string_view type,
const pybind11::object &tick,
+ const pybind11::object &rows,
const pybind11::object &filter_coords_obj);
} // namespace stim_pybind
diff --git a/src/stim/dem/detector_error_model_instruction.pybind.cc b/src/stim/dem/detector_error_model_instruction.pybind.cc
index dfd98350..a9f0a5ca 100644
--- a/src/stim/dem/detector_error_model_instruction.pybind.cc
+++ b/src/stim/dem/detector_error_model_instruction.pybind.cc
@@ -14,7 +14,6 @@
#include "stim/dem/detector_error_model_instruction.pybind.h"
-#include "stim/dem/detector_error_model.pybind.h"
#include "stim/dem/detector_error_model_target.pybind.h"
#include "stim/py/base.pybind.h"
#include "stim/util_bot/str_util.h"
@@ -183,7 +182,29 @@ void stim_pybind::pybind_detector_error_model_instruction_methods(
c.def(
"args_copy",
&ExposedDemInstruction::args_copy,
- "Returns a copy of the list of numbers parameterizing the instruction (e.g. the probability of an error).");
+ clean_doc_string(R"DOC(
+ @signature def args_copy(self) -> List[float]:
+ Returns a copy of the list of numbers parameterizing the instruction.
+
+ For example, this would be coordinates of a detector instruction or the
+ probability of an error instruction. The result is a copy, meaning that
+ editing it won't change the instruction's targets or future copies.
+
+ Examples:
+ >>> import stim
+ >>> instruction = stim.DetectorErrorModel('''
+ ... error(0.125) D0
+ ... ''')[0]
+ >>> instruction.args_copy()
+ [0.125]
+
+ >>> instruction.args_copy() == instruction.args_copy()
+ True
+ >>> instruction.args_copy() is instruction.args_copy()
+ False
+ )DOC")
+ .data());
+
c.def(
"targets_copy",
&ExposedDemInstruction::targets_copy,
@@ -191,8 +212,21 @@ void stim_pybind::pybind_detector_error_model_instruction_methods(
@signature def targets_copy(self) -> List[Union[int, stim.DemTarget]]:
Returns a copy of the instruction's targets.
- (Making a copy is enforced to make it clear that editing the result won't change
- the instruction's targets.)
+ The result is a copy, meaning that editing it won't change the instruction's
+ targets or future copies.
+
+ Examples:
+ >>> import stim
+ >>> instruction = stim.DetectorErrorModel('''
+ ... error(0.125) D0 L2
+ ... ''')[0]
+ >>> instruction.targets_copy()
+ [stim.DemTarget('D0'), stim.DemTarget('L2')]
+
+ >>> instruction.targets_copy() == instruction.targets_copy()
+ True
+ >>> instruction.targets_copy() is instruction.targets_copy()
+ False
)DOC")
.data());
c.def_property_readonly(
diff --git a/src/stim/diagram/detector_slice/detector_slice_set.cc b/src/stim/diagram/detector_slice/detector_slice_set.cc
index abf26b06..ad90760c 100644
--- a/src/stim/diagram/detector_slice/detector_slice_set.cc
+++ b/src/stim/diagram/detector_slice/detector_slice_set.cc
@@ -741,8 +741,7 @@ void _start_two_body_svg_path(
std::ostream &out,
const std::function(uint64_t tick, uint32_t qubit)> &coords,
uint64_t tick,
- SpanRef terms,
- std::vector> &pts_workspace) {
+ SpanRef terms) {
auto a = coords(tick, terms[0].qubit_value());
auto b = coords(tick, terms[1].qubit_value());
auto dif = b - a;
@@ -774,7 +773,6 @@ void _start_one_body_svg_path(
const std::function(uint64_t tick, uint32_t qubit)> &coords,
uint64_t tick,
SpanRef terms,
- std::vector> &pts_workspace,
size_t scale) {
auto c = coords(tick, terms[0].qubit_value());
out << " 2) {
_start_many_body_svg_path(out, coords, tick, terms, pts_workspace);
} else if (terms.size() == 2) {
- _start_two_body_svg_path(out, coords, tick, terms, pts_workspace);
+ _start_two_body_svg_path(out, coords, tick, terms);
} else if (terms.size() == 1) {
- _start_one_body_svg_path(out, coords, tick, terms, pts_workspace, scale);
+ _start_one_body_svg_path(out, coords, tick, terms, scale);
}
}
-void DetectorSliceSet::write_svg_diagram_to(std::ostream &out) const {
- size_t num_cols = (uint64_t)ceil(sqrt((double)num_ticks));
- size_t num_rows = num_ticks / num_cols;
- while (num_cols * num_rows < num_ticks) {
- num_rows++;
- }
- while (num_cols * num_rows >= num_ticks + num_rows) {
- num_cols--;
+void DetectorSliceSet::write_svg_diagram_to(std::ostream &out, size_t num_rows) const {
+ size_t num_cols;
+ if (num_rows == 0) {
+ num_cols = (uint64_t)ceil(sqrt((double)num_ticks));
+ num_rows = num_ticks / num_cols;
+ while (num_cols * num_rows < num_ticks) {
+ num_rows++;
+ }
+ while (num_cols * num_rows >= num_ticks + num_rows) {
+ num_cols--;
+ }
+ } else {
+ num_cols = (num_ticks + num_rows - 1) / num_rows;
}
auto coordsys = FlattenedCoords::from(*this, 32);
diff --git a/src/stim/diagram/detector_slice/detector_slice_set.h b/src/stim/diagram/detector_slice/detector_slice_set.h
index 76314eba..1fdeffa9 100644
--- a/src/stim/diagram/detector_slice/detector_slice_set.h
+++ b/src/stim/diagram/detector_slice/detector_slice_set.h
@@ -65,7 +65,7 @@ struct DetectorSliceSet {
std::string str() const;
void write_text_diagram_to(std::ostream &out) const;
- void write_svg_diagram_to(std::ostream &out) const;
+ void write_svg_diagram_to(std::ostream &out, size_t num_rows = 0) const;
void write_svg_contents_to(
std::ostream &out,
const std::function(uint32_t qubit)> &unscaled_coords,
diff --git a/src/stim/diagram/timeline/timeline_svg_drawer.cc b/src/stim/diagram/timeline/timeline_svg_drawer.cc
index 59a3ab02..67621fa4 100644
--- a/src/stim/diagram/timeline/timeline_svg_drawer.cc
+++ b/src/stim/diagram/timeline/timeline_svg_drawer.cc
@@ -808,7 +808,8 @@ void DiagramTimelineSvgDrawer::make_diagram_write_to(
uint64_t tick_slice_start,
uint64_t tick_slice_num,
DiagramTimelineSvgDrawerMode mode,
- SpanRef filter) {
+ SpanRef filter,
+ size_t num_rows) {
uint64_t circuit_num_ticks = circuit.count_ticks();
auto circuit_has_ticks = circuit_num_ticks > 0;
auto num_qubits = circuit.count_qubits();
@@ -825,8 +826,13 @@ void DiagramTimelineSvgDrawer::make_diagram_write_to(
obj.coord_sys = FlattenedCoords::from(obj.detector_slice_set, GATE_PITCH);
obj.coord_sys.size.xyz[0] += TIME_SLICE_PADDING * 2;
obj.coord_sys.size.xyz[1] += TIME_SLICE_PADDING * 2;
- obj.num_cols = (uint64_t)ceil(sqrt((double)tick_slice_num));
- obj.num_rows = tick_slice_num / obj.num_cols;
+ if (num_rows == 0) {
+ obj.num_cols = (uint64_t)ceil(sqrt((double)tick_slice_num));
+ obj.num_rows = tick_slice_num / obj.num_cols;
+ } else {
+ obj.num_rows = num_rows;
+ obj.num_cols = (tick_slice_num + num_rows - 1) / num_rows;
+ }
while (obj.num_cols * obj.num_rows < tick_slice_num) {
obj.num_rows++;
}
@@ -855,9 +861,9 @@ void DiagramTimelineSvgDrawer::make_diagram_write_to(
auto w = obj.m2x(obj.cur_moment) - GATE_PITCH * 0.5f;
svg_out << R"SVG(
\ No newline at end of file
diff --git a/testdata/bezier_time_slice.svg b/testdata/bezier_time_slice.svg
index 1f132c86..47291d78 100644
--- a/testdata/bezier_time_slice.svg
+++ b/testdata/bezier_time_slice.svg
@@ -26,7 +26,9 @@
+Tick 0
-
+Tick 1
+
\ No newline at end of file
diff --git a/testdata/circuit_all_ops_detslice.svg b/testdata/circuit_all_ops_detslice.svg
index a917974c..6a654cdf 100644
--- a/testdata/circuit_all_ops_detslice.svg
+++ b/testdata/circuit_all_ops_detslice.svg
@@ -561,19 +561,33 @@
Zrec
+Tick 0
-
-
-
-
-
-
-
-
-
-
-
-
-
+Tick 1
+
+Tick 2
+
+Tick 3
+
+Tick 4
+
+Tick 5
+
+Tick 6
+
+Tick 7
+
+Tick 8
+
+Tick 9
+
+Tick 10
+
+Tick 11
+
+Tick 12
+
+Tick 13
+
\ No newline at end of file
diff --git a/testdata/circuit_all_ops_timeslice.svg b/testdata/circuit_all_ops_timeslice.svg
index a917974c..6a654cdf 100644
--- a/testdata/circuit_all_ops_timeslice.svg
+++ b/testdata/circuit_all_ops_timeslice.svg
@@ -561,19 +561,33 @@
Zrec
+Tick 0
-
-
-
-
-
-
-
-
-
-
-
-
-
+Tick 1
+
+Tick 2
+
+Tick 3
+
+Tick 4
+
+Tick 5
+
+Tick 6
+
+Tick 7
+
+Tick 8
+
+Tick 9
+
+Tick 10
+
+Tick 11
+
+Tick 12
+
+Tick 13
+
\ No newline at end of file
diff --git a/testdata/circuit_diagram_timeline_svg_chained_loops.svg b/testdata/circuit_diagram_timeline_svg_chained_loops.svg
index cd3bbbd0..3ef5e37f 100644
--- a/testdata/circuit_diagram_timeline_svg_chained_loops.svg
+++ b/testdata/circuit_diagram_timeline_svg_chained_loops.svg
@@ -46,16 +46,27 @@
Z
+Tick 0
-
-
-
-
-
-
-
-
-
-
+Tick 1
+
+Tick 2
+
+Tick 3
+
+Tick 4
+
+Tick 5
+
+Tick 6
+
+Tick 7
+
+Tick 8
+
+Tick 9
+
+Tick 10
+
\ No newline at end of file
diff --git a/testdata/detslice-with-ops_surface_code.svg b/testdata/detslice-with-ops_surface_code.svg
index e79153bd..6463895f 100644
--- a/testdata/detslice-with-ops_surface_code.svg
+++ b/testdata/detslice-with-ops_surface_code.svg
@@ -1271,19 +1271,33 @@
H
+Tick 0
-
-
-
-
-
-
-
-
-
-
-
-
-
+Tick 1
+
+Tick 2
+
+Tick 3
+
+Tick 4
+
+Tick 5
+
+Tick 6
+
+Tick 7
+
+Tick 8
+
+Tick 9
+
+Tick 10
+
+Tick 11
+
+Tick 12
+
+Tick 13
+
\ No newline at end of file
diff --git a/testdata/observable_slices.svg b/testdata/observable_slices.svg
index 6bff67cb..6d4aa7be 100644
--- a/testdata/observable_slices.svg
+++ b/testdata/observable_slices.svg
@@ -145,18 +145,31 @@
M
+Tick 0
-
-
-
-
-
-
-
-
-
-
-
-
+Tick 1
+
+Tick 2
+
+Tick 3
+
+Tick 4
+
+Tick 5
+
+Tick 6
+
+Tick 7
+
+Tick 8
+
+Tick 9
+
+Tick 10
+
+Tick 11
+
+Tick 12
+
\ No newline at end of file
diff --git a/testdata/surface_code_full_time_detector_slice.svg b/testdata/surface_code_full_time_detector_slice.svg
index ace2226d..7e7fdae6 100644
--- a/testdata/surface_code_full_time_detector_slice.svg
+++ b/testdata/surface_code_full_time_detector_slice.svg
@@ -2823,41 +2823,77 @@
M
+Tick 0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+Tick 1
+
+Tick 2
+
+Tick 3
+
+Tick 4
+
+Tick 5
+
+Tick 6
+
+Tick 7
+
+Tick 8
+
+Tick 9
+
+Tick 10
+
+Tick 11
+
+Tick 12
+
+Tick 13
+
+Tick 14
+
+Tick 15
+
+Tick 16
+
+Tick 17
+
+Tick 18
+
+Tick 19
+
+Tick 20
+
+Tick 21
+
+Tick 22
+
+Tick 23
+
+Tick 24
+
+Tick 25
+
+Tick 26
+
+Tick 27
+
+Tick 28
+
+Tick 29
+
+Tick 30
+
+Tick 31
+
+Tick 32
+
+Tick 33
+
+Tick 34
+
+Tick 35
+
\ No newline at end of file
diff --git a/testdata/surface_code_time_detector_slice.svg b/testdata/surface_code_time_detector_slice.svg
index 01fbf938..3bf551e1 100644
--- a/testdata/surface_code_time_detector_slice.svg
+++ b/testdata/surface_code_time_detector_slice.svg
@@ -749,16 +749,27 @@
H
+Tick 5
-
-
-
-
-
-
-
-
-
-
+Tick 6
+
+Tick 7
+
+Tick 8
+
+Tick 9
+
+Tick 10
+
+Tick 11
+
+Tick 12
+
+Tick 13
+
+Tick 14
+
+Tick 15
+
\ No newline at end of file
diff --git a/testdata/surface_code_time_slice.svg b/testdata/surface_code_time_slice.svg
index 38ede396..1606fe7f 100644
--- a/testdata/surface_code_time_slice.svg
+++ b/testdata/surface_code_time_slice.svg
@@ -373,16 +373,27 @@
H
+Tick 5
-
-
-
-
-
-
-
-
-
-
+Tick 6
+
+Tick 7
+
+Tick 8
+
+Tick 9
+
+Tick 10
+
+Tick 11
+
+Tick 12
+
+Tick 13
+
+Tick 14
+
+Tick 15
+
\ No newline at end of file